From d136864ef16138695ec581ecee305502105ee1f4 Mon Sep 17 00:00:00 2001 From: Philipinho <16838612+Philipinho@users.noreply.github.com> Date: Sun, 16 Aug 2026 11:46:50 +0100 Subject: [PATCH 01/14] sync --- apps/server/src/ee | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/apps/server/src/ee b/apps/server/src/ee index b38dc5ecd..22a5f200d 160000 --- a/apps/server/src/ee +++ b/apps/server/src/ee @@ -1 +1 @@ -Subproject commit b38dc5ecd10118b687e6cb073cf7202e8c2fe914 +Subproject commit 22a5f200dfb7d042e88c6a1397568da5bdf9477b From 911c1057d632716f79eba7d3bdcf4df940c02229 Mon Sep 17 00:00:00 2001 From: Philip Okugbe <16838612+Philipinho@users.noreply.github.com> Date: Sun, 16 Aug 2026 12:35:37 +0100 Subject: [PATCH 02/14] feat(server): add global encryption module (AES-256-GCM) (#2400) Provides an injectable EncryptionService that encrypts/decrypts strings with AES-256-GCM using a key derived from APP_SECRET with domain separation. --- apps/server/src/app.module.ts | 2 + .../encryption/encryption.errors.ts | 13 ++ .../encryption/encryption.module.ts | 9 + .../encryption/encryption.service.spec.ts | 184 ++++++++++++++++++ .../encryption/encryption.service.ts | 108 ++++++++++ 5 files changed, 316 insertions(+) create mode 100644 apps/server/src/integrations/encryption/encryption.errors.ts create mode 100644 apps/server/src/integrations/encryption/encryption.module.ts create mode 100644 apps/server/src/integrations/encryption/encryption.service.spec.ts create mode 100644 apps/server/src/integrations/encryption/encryption.service.ts diff --git a/apps/server/src/app.module.ts b/apps/server/src/app.module.ts index b8cfc5877..4f5411eca 100644 --- a/apps/server/src/app.module.ts +++ b/apps/server/src/app.module.ts @@ -27,6 +27,7 @@ import { LoggerModule } from './common/logger/logger.module'; import { ClsModule } from 'nestjs-cls'; import { NoopAuditModule } from './integrations/audit/audit.module'; import { ThrottleModule } from './integrations/throttle/throttle.module'; +import { EncryptionModule } from './integrations/encryption/encryption.module'; const enterpriseModules = []; try { @@ -53,6 +54,7 @@ try { CoreModule, DatabaseModule, EnvironmentModule, + EncryptionModule, RedisModule.forRootAsync({ useClass: RedisConfigService, }), diff --git a/apps/server/src/integrations/encryption/encryption.errors.ts b/apps/server/src/integrations/encryption/encryption.errors.ts new file mode 100644 index 000000000..06b5cca81 --- /dev/null +++ b/apps/server/src/integrations/encryption/encryption.errors.ts @@ -0,0 +1,13 @@ +export class UnableToInitialize extends Error { + constructor(message: string) { + super(`Unable to initialize the encryption service: ${message}`); + this.name = 'UnableToInitialize'; + } +} + +export class UnableToDecrypt extends Error { + constructor(reason: string) { + super(`Unable to decrypt the ciphertext: ${reason}`); + this.name = 'UnableToDecrypt'; + } +} diff --git a/apps/server/src/integrations/encryption/encryption.module.ts b/apps/server/src/integrations/encryption/encryption.module.ts new file mode 100644 index 000000000..75022234e --- /dev/null +++ b/apps/server/src/integrations/encryption/encryption.module.ts @@ -0,0 +1,9 @@ +import { Global, Module } from '@nestjs/common'; +import { EncryptionService } from './encryption.service'; + +@Global() +@Module({ + providers: [EncryptionService], + exports: [EncryptionService], +}) +export class EncryptionModule {} diff --git a/apps/server/src/integrations/encryption/encryption.service.spec.ts b/apps/server/src/integrations/encryption/encryption.service.spec.ts new file mode 100644 index 000000000..64b1a8e00 --- /dev/null +++ b/apps/server/src/integrations/encryption/encryption.service.spec.ts @@ -0,0 +1,184 @@ +import { Test, TestingModule } from '@nestjs/testing'; +import { EncryptionService } from './encryption.service'; +import { UnableToDecrypt, UnableToInitialize } from './encryption.errors'; +import { EnvironmentService } from '../environment/environment.service'; + +const APP_SECRET = 'test-app-secret-with-plenty-of-entropy-1234567890'; + +const buildService = (appSecret: string | undefined) => { + const env = { getAppSecret: () => appSecret } as EnvironmentService; + return new EncryptionService(env); +}; + +const decodeEnvelope = (encrypted: string) => + JSON.parse(Buffer.from(encrypted, 'base64').toString()) as { + iv: string; + authTag: string; + cipherText: string; + }; + +const encodeEnvelope = (envelope: { + iv: string; + authTag: string; + cipherText: string; +}) => Buffer.from(JSON.stringify(envelope)).toString('base64'); + +describe('EncryptionService', () => { + let service: EncryptionService; + + beforeEach(async () => { + const module: TestingModule = await Test.createTestingModule({ + providers: [ + EncryptionService, + { + provide: EnvironmentService, + useValue: { getAppSecret: () => APP_SECRET }, + }, + ], + }).compile(); + + service = module.get(EncryptionService); + }); + + describe('initialization', () => { + it('compiles via Nest DI', () => { + expect(service).toBeDefined(); + }); + + it('throws UnableToInitialize when APP_SECRET is missing', () => { + expect(() => buildService(undefined)).toThrow(UnableToInitialize); + expect(() => buildService('')).toThrow(UnableToInitialize); + }); + }); + + describe('encrypt + decrypt round-trip', () => { + it('decrypts back to the original plaintext', () => { + const plaintext = 'hello world'; + const encrypted = service.encrypt(plaintext); + expect(service.decrypt(encrypted)).toBe(plaintext); + }); + + it('handles empty string', () => { + const encrypted = service.encrypt(''); + expect(service.decrypt(encrypted)).toBe(''); + }); + + it('handles unicode (multi-byte UTF-8)', () => { + const plaintext = 'héllo 🔐 世界'; + const encrypted = service.encrypt(plaintext); + expect(service.decrypt(encrypted)).toBe(plaintext); + }); + + it('handles long plaintext (>1 block)', () => { + const plaintext = 'a'.repeat(10_000); + const encrypted = service.encrypt(plaintext); + expect(service.decrypt(encrypted)).toBe(plaintext); + }); + + it('produces distinct ciphertexts for the same plaintext (random IV)', () => { + const plaintext = 'same input'; + const a = service.encrypt(plaintext); + const b = service.encrypt(plaintext); + expect(a).not.toBe(b); + expect(service.decrypt(a)).toBe(plaintext); + expect(service.decrypt(b)).toBe(plaintext); + }); + }); + + describe('cross-key isolation', () => { + it('cannot decrypt ciphertext produced under a different APP_SECRET', () => { + const other = buildService('totally-different-secret-value-9876543210'); + const encrypted = service.encrypt('secret'); + expect(() => other.decrypt(encrypted)).toThrow(UnableToDecrypt); + }); + }); + + describe('tamper detection', () => { + it('rejects modified ciphertext', () => { + const encrypted = service.encrypt('hello'); + const env = decodeEnvelope(encrypted); + const tamperedCipher = Buffer.from(env.cipherText, 'base64'); + tamperedCipher[0] ^= 0x01; + const tampered = encodeEnvelope({ + ...env, + cipherText: tamperedCipher.toString('base64'), + }); + expect(() => service.decrypt(tampered)).toThrow(UnableToDecrypt); + }); + + it('rejects modified auth tag', () => { + const encrypted = service.encrypt('hello'); + const env = decodeEnvelope(encrypted); + const tamperedTag = Buffer.from(env.authTag, 'base64'); + tamperedTag[0] ^= 0x01; + const tampered = encodeEnvelope({ + ...env, + authTag: tamperedTag.toString('base64'), + }); + expect(() => service.decrypt(tampered)).toThrow(UnableToDecrypt); + }); + + it('rejects modified IV', () => { + const encrypted = service.encrypt('hello'); + const env = decodeEnvelope(encrypted); + const tamperedIV = Buffer.from(env.iv, 'base64'); + tamperedIV[0] ^= 0x01; + const tampered = encodeEnvelope({ + ...env, + iv: tamperedIV.toString('base64'), + }); + expect(() => service.decrypt(tampered)).toThrow(UnableToDecrypt); + }); + }); + + describe('malformed payloads', () => { + it('rejects non-base64 garbage', () => { + expect(() => service.decrypt('!!!not-valid-base64!!!')).toThrow( + UnableToDecrypt, + ); + }); + + it('rejects base64 of non-JSON', () => { + const garbage = Buffer.from('not json at all').toString('base64'); + expect(() => service.decrypt(garbage)).toThrow(UnableToDecrypt); + }); + + it('rejects JSON missing required fields', () => { + const partial = encodeEnvelope({ + iv: Buffer.alloc(12).toString('base64'), + authTag: Buffer.alloc(16).toString('base64'), + } as never); + expect(() => service.decrypt(partial)).toThrow(UnableToDecrypt); + }); + + it('rejects wrong-length IV', () => { + const encrypted = service.encrypt('hello'); + const env = decodeEnvelope(encrypted); + const bad = encodeEnvelope({ + ...env, + iv: Buffer.alloc(8).toString('base64'), + }); + expect(() => service.decrypt(bad)).toThrow(UnableToDecrypt); + }); + + it('rejects wrong-length auth tag', () => { + const encrypted = service.encrypt('hello'); + const env = decodeEnvelope(encrypted); + const bad = encodeEnvelope({ + ...env, + authTag: Buffer.alloc(8).toString('base64'), + }); + expect(() => service.decrypt(bad)).toThrow(UnableToDecrypt); + }); + }); + + describe('envelope format', () => { + it('returns base64 of JSON envelope with iv (12B), authTag (16B), cipherText', () => { + const encrypted = service.encrypt('hello'); + const env = decodeEnvelope(encrypted); + expect(Buffer.from(env.iv, 'base64')).toHaveLength(12); + expect(Buffer.from(env.authTag, 'base64')).toHaveLength(16); + expect(Buffer.from(env.cipherText, 'base64').length).toBeGreaterThan(0); + }); + }); +}); diff --git a/apps/server/src/integrations/encryption/encryption.service.ts b/apps/server/src/integrations/encryption/encryption.service.ts new file mode 100644 index 000000000..63dc4ce99 --- /dev/null +++ b/apps/server/src/integrations/encryption/encryption.service.ts @@ -0,0 +1,108 @@ +// https://github.com/nhedger/nestjs-encryption - MIT +import { Injectable } from '@nestjs/common'; +import { + createCipheriv, + createDecipheriv, + createHash, + randomBytes, +} from 'node:crypto'; +import { UnableToDecrypt, UnableToInitialize } from './encryption.errors'; +import { EnvironmentService } from '../environment/environment.service'; + +const ALGORITHM = 'aes-256-gcm'; +const KEY_DOMAIN = 'docmost:encryption:v1'; +const IV_LENGTH = 12; +const AUTH_TAG_LENGTH = 16; + +type AEADPayload = { + iv: TFormat; + authTag: TFormat; + cipherText: TFormat; +}; + +@Injectable() +export class EncryptionService { + private readonly key: Buffer; + + constructor(environmentService: EnvironmentService) { + const appSecret = environmentService.getAppSecret(); + if (!appSecret) { + throw new UnableToInitialize('APP_SECRET is not set.'); + } + this.key = createHash('sha256') + .update(KEY_DOMAIN) + .update(appSecret) + .digest(); + } + + public encrypt(plaintext: string): string { + const iv = randomBytes(IV_LENGTH); + const cipher = createCipheriv(ALGORITHM, this.key, iv); + const cipherText = Buffer.concat([ + cipher.update(plaintext, 'utf8'), + cipher.final(), + ]); + const authTag = cipher.getAuthTag(); + + const aead: AEADPayload = { + iv: iv.toString('base64'), + authTag: authTag.toString('base64'), + cipherText: cipherText.toString('base64'), + }; + + return Buffer.from(JSON.stringify(aead)).toString('base64'); + } + + public decrypt(encrypted: string): string { + try { + const { iv, authTag, cipherText } = this.decodeAEADPayload(encrypted); + const decipher = createDecipheriv(ALGORITHM, this.key, iv); + decipher.setAuthTag(authTag); + const decrypted = Buffer.concat([ + decipher.update(cipherText), + decipher.final(), + ]); + return decrypted.toString('utf8'); + } catch (e: unknown) { + throw new UnableToDecrypt((e as Error).message); + } + } + + private decodeAEADPayload(encodedPayload: string): AEADPayload { + const payload = Buffer.from(encodedPayload, 'base64'); + + let deserializedPkg: Record; + try { + deserializedPkg = JSON.parse(payload.toString()); + } catch { + throw new Error('The decoded AEAD payload is not a valid JSON string.'); + } + + for (const field of ['iv', 'authTag', 'cipherText']) { + if (!Object.prototype.hasOwnProperty.call(deserializedPkg, field)) { + throw new Error(`The AEAD payload is missing the ${field} field.`); + } + } + + const iv = Buffer.from(deserializedPkg.iv as string, 'base64'); + if (iv.length !== IV_LENGTH) { + throw new Error( + `The decoded IV is not the correct length. Expected ${IV_LENGTH} bytes, got ${iv.length} bytes.`, + ); + } + + const authTag = Buffer.from(deserializedPkg.authTag as string, 'base64'); + if (authTag.length !== AUTH_TAG_LENGTH) { + throw new Error( + `The decoded auth tag is not the correct length. Expected ${AUTH_TAG_LENGTH} bytes, got ${authTag.length} bytes.`, + ); + } + + const cipherText = Buffer.from( + deserializedPkg.cipherText as string, + 'base64', + ); + + return { iv, authTag, cipherText }; + } +} From 232beda471a97938d961e1a7b7fed1453dcaf82e Mon Sep 17 00:00:00 2001 From: Philip Okugbe <16838612+Philipinho@users.noreply.github.com> Date: Mon, 17 Aug 2026 21:18:16 +0100 Subject: [PATCH 03/14] feat(ee): turbopuffer ai vector search driver (#2402) * feat(ai): add AI_VECTOR_DRIVER and turbopuffer configuration * feat(ai): carry workspace and target space ids on vector lifecycle events * feat(ai): add vector driver interface and turbopuffer request helpers * feat(ai): add pgvector driver behind the vector driver interface * refactor(ai): route vector reads and writes through the vector driver * feat(ai): add turbopuffer vector driver * feat(ai): rebuild turbopuffer namespaces when the embedding model changes * feat(ai): warm the vector namespace cache on session start * fix(ai): harden turbopuffer misconfiguration and reset failure paths * fix(ai): collapse blank-line runs in extracted page text * fix(ai): skip full re-embed when ai search is re-enabled within the delete grace window * sync * fix(ai): store real embedding dimensions instead of serialized vector length * fix(ai): filter search hits by the page's current space at query time * fix(ai): retry the page moved-to-space vector patch job * sync * feat(ai): pre-warm the vector namespace * fix(ai): pass AI_VECTOR_DRIVER through the client build config --- .../src/ee/ai/services/ai-search-service.ts | 8 +++++ .../search/components/search-spotlight.tsx | 15 ++++++++ apps/client/src/lib/config.ts | 4 +++ apps/client/vite.config.ts | 2 ++ apps/server/package.json | 1 + .../src/collaboration/collaboration.util.ts | 3 +- apps/server/src/common/helpers/index.ts | 1 + .../src/common/helpers/text.utils.spec.ts | 14 ++++++++ apps/server/src/common/helpers/text.utils.ts | 3 ++ .../src/core/page/services/page.service.ts | 19 +++++++--- .../src/database/listeners/space.listener.ts | 5 +-- .../src/database/repos/space/space.repo.ts | 1 + .../repos/workspace/workspace.repo.ts | 18 ++++++++++ apps/server/src/ee | 2 +- .../environment/environment.service.ts | 25 +++++++++++++ .../environment/environment.validation.ts | 36 +++++++++++++++++++ .../queue/constants/queue.constants.ts | 1 + .../src/integrations/static/static.module.ts | 4 +++ pnpm-lock.yaml | 17 +++++++++ 19 files changed, 171 insertions(+), 8 deletions(-) create mode 100644 apps/server/src/common/helpers/text.utils.spec.ts create mode 100644 apps/server/src/common/helpers/text.utils.ts diff --git a/apps/client/src/ee/ai/services/ai-search-service.ts b/apps/client/src/ee/ai/services/ai-search-service.ts index 8c2af64a8..d580a2090 100644 --- a/apps/client/src/ee/ai/services/ai-search-service.ts +++ b/apps/client/src/ee/ai/services/ai-search-service.ts @@ -15,6 +15,14 @@ export interface IAiSearchResponse { }>; } +export async function hintVectorCache(): Promise { + try { + await api.post("/ai/vector-cache-hint"); + } catch { + // best-effort cache hint + } +} + export async function aiAnswers( params: IPageSearchParams, onChunk?: (chunk: { content?: string; sources?: any[] }) => void, diff --git a/apps/client/src/features/search/components/search-spotlight.tsx b/apps/client/src/features/search/components/search-spotlight.tsx index 4c5269f15..3ada7ab1f 100644 --- a/apps/client/src/features/search/components/search-spotlight.tsx +++ b/apps/client/src/features/search/components/search-spotlight.tsx @@ -13,11 +13,16 @@ import { SearchResultItem } from "./search-result-item.tsx"; import { AiSearchResult } from "../../../ee/ai/components/ai-search-result.tsx"; import { useHasFeature } from "@/ee/hooks/use-feature"; import { Feature } from "@/ee/features"; +import { useAtomValue } from "jotai"; +import { workspaceAtom } from "@/features/user/atoms/current-user-atom.ts"; +import { hintVectorCache } from "@/ee/ai/services/ai-search-service.ts"; +import { getAiVectorDriver } from "@/lib/config.ts"; interface SearchSpotlightProps { spaceId?: string; } export function SearchSpotlight({ spaceId }: SearchSpotlightProps) { + const workspace = useAtomValue(workspaceAtom); const { t } = useTranslation(); const hasAiFeature = useHasFeature(Feature.AI); const hasAttachmentIndexing = useHasFeature(Feature.ATTACHMENT_INDEXING); @@ -96,6 +101,15 @@ export function SearchSpotlight({ spaceId }: SearchSpotlightProps) { /> )); + const handleSpotlightOpen = () => { + if ( + workspace?.settings?.ai?.search === true && + getAiVectorDriver() === "turbopuffer" + ) { + hintVectorCache(); + } + }; + const handleFiltersChange = (newFilters: any) => { setFilters(newFilters); }; @@ -115,6 +129,7 @@ export function SearchSpotlight({ spaceId }: SearchSpotlightProps) { { BILLING_TRIAL_DAYS, POSTHOG_HOST, POSTHOG_KEY, + AI_VECTOR_DRIVER, } = loadEnv(mode, envPath, ""); return { @@ -31,6 +32,7 @@ export default defineConfig(({ mode }) => { BILLING_TRIAL_DAYS, POSTHOG_HOST, POSTHOG_KEY, + AI_VECTOR_DRIVER, }, APP_VERSION: JSON.stringify(process.env.npm_package_version), }, diff --git a/apps/server/package.json b/apps/server/package.json index cb32657a7..3e5ae9592 100644 --- a/apps/server/package.json +++ b/apps/server/package.json @@ -66,6 +66,7 @@ "@nestjs/websockets": "11.1.28", "@node-saml/passport-saml": "5.1.0", "@socket.io/redis-adapter": "8.3.0", + "@turbopuffer/turbopuffer": "^2.8.0", "ai": "6.0.134", "ai-sdk-ollama": "3.8.1", "bcrypt": "6.0.0", diff --git a/apps/server/src/collaboration/collaboration.util.ts b/apps/server/src/collaboration/collaboration.util.ts index ed182cf7f..e5c55c77a 100644 --- a/apps/server/src/collaboration/collaboration.util.ts +++ b/apps/server/src/collaboration/collaboration.util.ts @@ -57,6 +57,7 @@ import { JSONContent, } from '@tiptap/core'; import { generateHTML, generateJSON } from '../common/helpers/prosemirror/html'; +import { collapseBlankLines } from '../common/helpers'; // @tiptap/html library works best for generating prosemirror json state but not HTML // see: https://github.com/ueberdosis/tiptap/issues/5352 // see:https://github.com/ueberdosis/tiptap/issues/4089 @@ -146,7 +147,7 @@ export function htmlToJson(html: string) { } export function jsonToText(tiptapJson: JSONContent) { - return generateText(tiptapJson, tiptapExtensions); + return collapseBlankLines(generateText(tiptapJson, tiptapExtensions)); } export function jsonToNode(tiptapJson: JSONContent) { diff --git a/apps/server/src/common/helpers/index.ts b/apps/server/src/common/helpers/index.ts index 80e9a9028..c9fac94c5 100644 --- a/apps/server/src/common/helpers/index.ts +++ b/apps/server/src/common/helpers/index.ts @@ -1,4 +1,5 @@ export * from './utils'; +export * from './text.utils'; export * from './nanoid.utils'; export * from './file.helper'; export * from './constants'; diff --git a/apps/server/src/common/helpers/text.utils.spec.ts b/apps/server/src/common/helpers/text.utils.spec.ts new file mode 100644 index 000000000..6d049a541 --- /dev/null +++ b/apps/server/src/common/helpers/text.utils.spec.ts @@ -0,0 +1,14 @@ +import { collapseBlankLines } from './text.utils'; + +describe('collapseBlankLines', () => { + it.each([ + ['a\n\n\n\nb', 'a\n\nb'], + ['a\n\nb', 'a\n\nb'], + ['a\nb', 'a\nb'], + ['\n\n\n\na\n\n\n', '\n\na\n\n'], + ['no newlines', 'no newlines'], + ['', ''], + ])('collapses %j to %j', (input, expected) => { + expect(collapseBlankLines(input)).toBe(expected); + }); +}); diff --git a/apps/server/src/common/helpers/text.utils.ts b/apps/server/src/common/helpers/text.utils.ts new file mode 100644 index 000000000..8a9cd1a27 --- /dev/null +++ b/apps/server/src/common/helpers/text.utils.ts @@ -0,0 +1,3 @@ +export function collapseBlankLines(text: string): string { + return text.replace(/\n{2,}/g, '\n\n'); +} diff --git a/apps/server/src/core/page/services/page.service.ts b/apps/server/src/core/page/services/page.service.ts index 387abc5cc..cabfb0c74 100644 --- a/apps/server/src/core/page/services/page.service.ts +++ b/apps/server/src/core/page/services/page.service.ts @@ -496,10 +496,21 @@ export class PageService { }, ); - await this.aiQueue.add(QueueJob.PAGE_MOVED_TO_SPACE, { - pageIds: pageIdsToMove, - workspaceId: rootPage.workspaceId, - }); + await this.aiQueue.add( + QueueJob.PAGE_MOVED_TO_SPACE, + { + pageIds: pageIdsToMove, + spaceId, + workspaceId: rootPage.workspaceId, + }, + { + attempts: 2, + backoff: { + type: 'fixed', + delay: 2 * 60 * 1000, + }, + }, + ); } }); diff --git a/apps/server/src/database/listeners/space.listener.ts b/apps/server/src/database/listeners/space.listener.ts index af6e3a222..3a766b6ef 100644 --- a/apps/server/src/database/listeners/space.listener.ts +++ b/apps/server/src/database/listeners/space.listener.ts @@ -8,6 +8,7 @@ import { EnvironmentService } from '../../integrations/environment/environment.s export class SpaceEvent { spaceId: string; + workspaceId: string; } @Injectable() @@ -22,12 +23,12 @@ export class SpaceListener { @OnEvent(EventName.SPACE_DELETED) async handleSpaceDeleted(event: SpaceEvent) { - const { spaceId } = event; + const { spaceId, workspaceId } = event; if (this.isTypesense()) { await this.searchQueue.add(QueueJob.SPACE_DELETED, { spaceId }); } - await this.aiQueue.add(QueueJob.SPACE_DELETED, { spaceId }); + await this.aiQueue.add(QueueJob.SPACE_DELETED, { spaceId, workspaceId }); } isTypesense(): boolean { diff --git a/apps/server/src/database/repos/space/space.repo.ts b/apps/server/src/database/repos/space/space.repo.ts index 905d2fac6..82fc170bd 100644 --- a/apps/server/src/database/repos/space/space.repo.ts +++ b/apps/server/src/database/repos/space/space.repo.ts @@ -230,6 +230,7 @@ export class SpaceRepo { this.eventEmitter.emit(EventName.SPACE_DELETED, { spaceId, + workspaceId, }); } } diff --git a/apps/server/src/database/repos/workspace/workspace.repo.ts b/apps/server/src/database/repos/workspace/workspace.repo.ts index d0f9b5cee..a8323d7ce 100644 --- a/apps/server/src/database/repos/workspace/workspace.repo.ts +++ b/apps/server/src/database/repos/workspace/workspace.repo.ts @@ -211,6 +211,24 @@ export class WorkspaceRepo { .executeTakeFirst(); } + async updateAiEmbeddingFingerprint( + workspaceId: string, + fingerprint: { driver: string; model: string; dimensions: number }, + trx?: KyselyTransaction, + ) { + const db = dbOrTx(this.db, trx); + return db + .updateTable('workspaces') + .set({ + settings: sql`COALESCE(settings, '{}'::jsonb) + || jsonb_build_object('ai', COALESCE(settings->'ai', '{}'::jsonb) + || jsonb_build_object('embedding', ${JSON.stringify(fingerprint)}::text::jsonb))`, + updatedAt: new Date(), + }) + .where('id', '=', workspaceId) + .execute(); + } + async updateSharingSettings( workspaceId: string, prefKey: string, diff --git a/apps/server/src/ee b/apps/server/src/ee index 22a5f200d..660418ac2 160000 --- a/apps/server/src/ee +++ b/apps/server/src/ee @@ -1 +1 @@ -Subproject commit 22a5f200dfb7d042e88c6a1397568da5bdf9477b +Subproject commit 660418ac2c175b37b2491ab3cd2560c2fd406412 diff --git a/apps/server/src/integrations/environment/environment.service.ts b/apps/server/src/integrations/environment/environment.service.ts index 5667bf5a6..c483df626 100644 --- a/apps/server/src/integrations/environment/environment.service.ts +++ b/apps/server/src/integrations/environment/environment.service.ts @@ -310,6 +310,31 @@ export class EnvironmentService { return val === 'true'; } + getAiVectorDriver(): string { + return this.configService + .get('AI_VECTOR_DRIVER', 'pgvector') + .toLowerCase(); + } + + getTurbopufferApiKey(): string { + return this.configService.get('TURBOPUFFER_API_KEY'); + } + + getTurbopufferRegion(): string { + return this.configService.get('TURBOPUFFER_REGION'); + } + + getTurbopufferBaseUrl(): string { + return this.configService.get('TURBOPUFFER_BASE_URL'); + } + + getTurbopufferNamespacePrefix(): string { + return this.configService.get( + 'TURBOPUFFER_NAMESPACE_PREFIX', + 'docmost', + ); + } + getOpenAiApiKey(): string { return this.configService.get('OPENAI_API_KEY'); } diff --git a/apps/server/src/integrations/environment/environment.validation.ts b/apps/server/src/integrations/environment/environment.validation.ts index ef3c420cf..d431d7e3a 100644 --- a/apps/server/src/integrations/environment/environment.validation.ts +++ b/apps/server/src/integrations/environment/environment.validation.ts @@ -5,6 +5,7 @@ import { IsOptional, IsString, IsUrl, + Matches, MinLength, ValidateIf, validateSync, @@ -108,6 +109,41 @@ export class EnvironmentVariables { @IsString() AI_DRIVER: string; + @IsOptional() + @ValidateIf((obj) => obj.AI_VECTOR_DRIVER) + @IsIn(['pgvector', 'turbopuffer']) + @IsString() + AI_VECTOR_DRIVER: string; + + @ValidateIf((obj) => obj.AI_VECTOR_DRIVER === 'turbopuffer') + @IsNotEmpty() + @IsString() + TURBOPUFFER_API_KEY: string; + + @ValidateIf( + (obj) => + obj.AI_VECTOR_DRIVER === 'turbopuffer' && !obj.TURBOPUFFER_BASE_URL, + ) + @IsNotEmpty({ + message: + 'TURBOPUFFER_REGION is required when AI_VECTOR_DRIVER is turbopuffer, unless TURBOPUFFER_BASE_URL is set', + }) + @IsString() + TURBOPUFFER_REGION: string; + + @IsOptional() + @ValidateIf((obj) => obj.TURBOPUFFER_BASE_URL != '' && obj.TURBOPUFFER_BASE_URL != null) + @IsUrl({ protocols: ['http', 'https'], require_tld: false }) + TURBOPUFFER_BASE_URL: string; + + @IsOptional() + @IsString() + @Matches(/^[A-Za-z0-9\-_.]{1,90}$/, { + message: + 'TURBOPUFFER_NAMESPACE_PREFIX may only contain letters, digits, dot, dash, underscore (max 90 chars)', + }) + TURBOPUFFER_NAMESPACE_PREFIX: string; + @IsOptional() @IsString() AI_EMBEDDING_MODEL: string; diff --git a/apps/server/src/integrations/queue/constants/queue.constants.ts b/apps/server/src/integrations/queue/constants/queue.constants.ts index 546553438..8b7c03a1f 100644 --- a/apps/server/src/integrations/queue/constants/queue.constants.ts +++ b/apps/server/src/integrations/queue/constants/queue.constants.ts @@ -61,6 +61,7 @@ export enum QueueJob { WORKSPACE_DELETED = 'workspace-deleted', WORKSPACE_CREATE_EMBEDDINGS = 'workspace-create-embeddings', WORKSPACE_DELETE_EMBEDDINGS = 'workspace-delete-embeddings', + WORKSPACE_RESET_EMBEDDINGS = 'workspace-reset-embeddings', GENERATE_PAGE_EMBEDDINGS = 'generate-page-embeddings', DELETE_PAGE_EMBEDDINGS = 'delete-page-embeddings', diff --git a/apps/server/src/integrations/static/static.module.ts b/apps/server/src/integrations/static/static.module.ts index f0b7e8319..8805804f3 100644 --- a/apps/server/src/integrations/static/static.module.ts +++ b/apps/server/src/integrations/static/static.module.ts @@ -49,6 +49,10 @@ export class StaticModule implements OnModuleInit { : undefined, POSTHOG_HOST: this.environmentService.getPostHogHost(), POSTHOG_KEY: this.environmentService.getPostHogKey(), + AI_VECTOR_DRIVER: + this.environmentService.getAiVectorDriver() === 'turbopuffer' + ? 'turbopuffer' + : undefined, }; const windowScriptContent = ``; diff --git a/pnpm-lock.yaml b/pnpm-lock.yaml index e26d0a45d..3a6ca01a7 100644 --- a/pnpm-lock.yaml +++ b/pnpm-lock.yaml @@ -593,6 +593,9 @@ importers: '@socket.io/redis-adapter': specifier: 8.3.0 version: 8.3.0(socket.io-adapter@2.5.4(supports-color@10.2.2))(supports-color@10.2.2) + '@turbopuffer/turbopuffer': + specifier: ^2.8.0 + version: 2.8.0 ai: specifier: 6.0.134 version: 6.0.134(zod@4.3.6) @@ -4382,6 +4385,9 @@ packages: '@tsconfig/node16@1.0.4': resolution: {integrity: sha512-vxhUy4J8lyeyinH7Azl1pdd43GJhZH/tP2weN8TntQblOY+A0XbT8DJk1/oCPuOOyg/Ja757rG0CgHcWC8OfMA==} + '@turbopuffer/turbopuffer@2.8.0': + resolution: {integrity: sha512-lurmVByLynuWtPtVl7SBEJ0bNakb8bs4KsafKzeLWnXA6BsQiGS9obT/7ExQVb9/yecyRn/grj8VgwUzi2xMyg==} + '@tybys/wasm-util@0.10.2': resolution: {integrity: sha512-RoBvJ2X0wuKlWFIjrwffGw1IqZHKQqzIchKaadZZfnNpsAYp2mM0h36JtPCjNDAHGgYez/15uMBpfGwchhiMgg==} @@ -5698,6 +5704,7 @@ packages: cron-parser@4.9.0: resolution: {integrity: sha512-p0SaNjrHOnQeR8/VnfGbmg9te2kfyYSQ7Sc/j/6DtPL3JQvKxmjO9TSjNFpujqV3vEYYBvNNvXSxzyksBWAx1Q==} engines: {node: '>=12.0.0'} + deprecated: v4 is no longer maintained, upgrade to v5 cron@4.4.0: resolution: {integrity: sha512-fkdfq+b+AHI4cKdhZlppHveI/mgz2qpiYxcm+t5E5TsxX7QrLS1VE0+7GENEk9z0EeGPcpSciGv6ez24duWhwQ==} @@ -8168,6 +8175,9 @@ packages: pako@2.0.3: resolution: {integrity: sha512-WjR1hOeg+kki3ZIOjaf4b5WVcay1jaliKSYiEaB1XzwhMQZJxRdQRv0V31EKBYlxb4T7SK3hjfc/jxyU64BoSw==} + pako@2.2.0: + resolution: {integrity: sha512-zJq6RP/5q+TO2OpFV3FHzlPnFjmkb7Nc99a5SNjJE+uu/PkpChs+NIZSSzbBoD+6kjiISXjfYdwj1ZRQ81dz/w==} + parent-module@1.0.1: resolution: {integrity: sha512-GQ2EWRpQV8/o+Aw8YqtfZZPfNRWZYkbidE9k5rpl/hC3vtHHBfGm2Ifi6qWV+coDGkrUKZAxE3Lot5kcsRlh+g==} engines: {node: '>=6'} @@ -14341,6 +14351,11 @@ snapshots: '@tsconfig/node16@1.0.4': {} + '@turbopuffer/turbopuffer@2.8.0': + dependencies: + pako: 2.2.0 + undici: 7.29.0 + '@tybys/wasm-util@0.10.2': dependencies: tslib: 2.8.1 @@ -18749,6 +18764,8 @@ snapshots: pako@2.0.3: {} + pako@2.2.0: {} + parent-module@1.0.1: dependencies: callsites: 3.1.0 From 8913d20aa0435e7c1f86146c87d4a42384ae52c4 Mon Sep 17 00:00:00 2001 From: Philip Okugbe <16838612+Philipinho@users.noreply.github.com> Date: Wed, 19 Aug 2026 19:59:00 +0100 Subject: [PATCH 04/14] chore: package updates (#2412) --- apps/client/package.json | 2 +- apps/server/package.json | 20 +- apps/server/src/ee | 2 +- pnpm-lock.yaml | 468 +++++++++++++++------------------------ pnpm-workspace.yaml | 4 +- 5 files changed, 197 insertions(+), 299 deletions(-) diff --git a/apps/client/package.json b/apps/client/package.json index e5bb5ad5a..cca0ef4be 100644 --- a/apps/client/package.json +++ b/apps/client/package.json @@ -52,7 +52,7 @@ "mantine-form-zod-resolver": "1.3.0", "mermaid": "11.16.1", "mitt": "3.0.1", - "nanoid": "3.3.17", + "nanoid": "3.3.18", "posthog-js": "1.391.2", "react": "19.2.7", "react-clear-modal": "^2.0.18", diff --git a/apps/server/package.json b/apps/server/package.json index 3e5ae9592..94463aacc 100644 --- a/apps/server/package.json +++ b/apps/server/package.json @@ -40,30 +40,30 @@ "@clickhouse/client": "1.18.2", "@docmost/base-formula": "workspace:*", "@docmost/pdf-inspector": "1.9.6", - "@fastify/cookie": "11.0.2", - "@fastify/multipart": "10.0.0", - "@fastify/static": "10.1.2", + "@fastify/cookie": "11.1.2", + "@fastify/multipart": "10.1.1", + "@fastify/static": "10.1.3", "@keyv/redis": "5.1.6", "@langchain/core": "1.1.46", "@langchain/textsplitters": "1.0.1", "@modelcontextprotocol/sdk": "1.30.0", "@nest-lab/throttler-storage-redis": "1.2.0", "@nestjs-labs/nestjs-ioredis": "11.0.4", - "@nestjs/bullmq": "11.0.4", + "@nestjs/bullmq": "11.0.5", "@nestjs/cache-manager": "3.1.3", - "@nestjs/common": "11.1.28", + "@nestjs/common": "11.2.1", "@nestjs/config": "4.0.4", - "@nestjs/core": "11.1.27", + "@nestjs/core": "11.2.1", "@nestjs/event-emitter": "3.1.0", "@nestjs/jwt": "11.0.2", "@nestjs/mapped-types": "2.1.1", "@nestjs/passport": "11.0.5", - "@nestjs/platform-fastify": "11.1.28", - "@nestjs/platform-socket.io": "11.1.28", + "@nestjs/platform-fastify": "11.2.1", + "@nestjs/platform-socket.io": "11.2.1", "@nestjs/schedule": "6.1.3", "@nestjs/terminus": "11.1.1", "@nestjs/throttler": "6.5.0", - "@nestjs/websockets": "11.1.28", + "@nestjs/websockets": "11.2.1", "@node-saml/passport-saml": "5.1.0", "@socket.io/redis-adapter": "8.3.0", "@turbopuffer/turbopuffer": "^2.8.0", @@ -120,7 +120,7 @@ "tmp-promise": "3.0.3", "typesense": "3.0.5", "undici": "7.29.0", - "ws": "8.21.0", + "ws": "8.21.3", "yauzl": "3.4.0", "zod": "4.3.6" }, diff --git a/apps/server/src/ee b/apps/server/src/ee index 660418ac2..e13af0ce0 160000 --- a/apps/server/src/ee +++ b/apps/server/src/ee @@ -1 +1 @@ -Subproject commit 660418ac2c175b37b2491ab3cd2560c2fd406412 +Subproject commit e13af0ce055cdc83962533f0a7865ad60a11e55e diff --git a/pnpm-lock.yaml b/pnpm-lock.yaml index 3a6ca01a7..a4dffce5b 100644 --- a/pnpm-lock.yaml +++ b/pnpm-lock.yaml @@ -7,12 +7,12 @@ settings: overrides: prosemirror-changeset: 2.4.0 glob: 13.0.6 - ws: 8.21.0 + ws: 8.21.3 dompurify: 3.4.13 mermaid: 11.16.1 undici: 7.29.0 tmp: 0.2.7 - nanoid@^3: 3.3.17 + nanoid@^3: 3.3.18 lodash-es: 4.18.1 express-rate-limit: 8.2.2 flatted: 3.4.2 @@ -362,8 +362,8 @@ importers: specifier: 3.0.1 version: 3.0.1 nanoid: - specifier: 3.3.17 - version: 3.3.17 + specifier: 3.3.18 + version: 3.3.18 posthog-js: specifier: 1.391.2 version: 1.391.2 @@ -436,7 +436,7 @@ importers: version: 19.2.3(@types/react@19.2.17) '@vitejs/plugin-react': specifier: 6.0.1 - version: 6.0.1(vite@8.0.16(@types/node@22.19.1)(esbuild@0.28.1)(jiti@2.4.2)(less@4.2.0)(sugarss@5.0.1(postcss@8.5.25))(terser@5.39.0)(tsx@4.21.0)(yaml@2.8.3)) + version: 6.0.1(vite@8.0.16(@types/node@22.19.1)(esbuild@0.28.1)(jiti@2.4.2)(sugarss@5.0.1(postcss@8.5.25))(terser@5.39.0)(tsx@4.21.0)(yaml@2.8.3)) eslint: specifier: 9.28.0 version: 9.28.0(jiti@2.4.2)(supports-color@10.2.2) @@ -478,10 +478,10 @@ importers: version: 8.57.1(eslint@9.28.0(jiti@2.4.2)(supports-color@10.2.2))(supports-color@10.2.2)(typescript@5.9.3) vite: specifier: 8.0.16 - version: 8.0.16(@types/node@22.19.1)(esbuild@0.28.1)(jiti@2.4.2)(less@4.2.0)(sugarss@5.0.1(postcss@8.5.25))(terser@5.39.0)(tsx@4.21.0)(yaml@2.8.3) + version: 8.0.16(@types/node@22.19.1)(esbuild@0.28.1)(jiti@2.4.2)(sugarss@5.0.1(postcss@8.5.25))(terser@5.39.0)(tsx@4.21.0)(yaml@2.8.3) vitest: specifier: 4.1.6 - version: 4.1.6(@opentelemetry/api@1.9.0)(@types/node@22.19.1)(happy-dom@20.8.9)(jsdom@25.0.0(supports-color@10.2.2))(vite@8.0.16(@types/node@22.19.1)(esbuild@0.28.1)(jiti@2.4.2)(less@4.2.0)(sugarss@5.0.1(postcss@8.5.25))(terser@5.39.0)(tsx@4.21.0)(yaml@2.8.3)) + version: 4.1.6(@opentelemetry/api@1.9.0)(@types/node@22.19.1)(happy-dom@20.8.9)(jsdom@25.0.0(supports-color@10.2.2))(vite@8.0.16(@types/node@22.19.1)(esbuild@0.28.1)(jiti@2.4.2)(sugarss@5.0.1(postcss@8.5.25))(terser@5.39.0)(tsx@4.21.0)(yaml@2.8.3)) apps/server: dependencies: @@ -516,77 +516,77 @@ importers: specifier: 1.9.6 version: 1.9.6 '@fastify/cookie': - specifier: 11.0.2 - version: 11.0.2 + specifier: 11.1.2 + version: 11.1.2 '@fastify/multipart': - specifier: 10.0.0 - version: 10.0.0 + specifier: 10.1.1 + version: 10.1.1 '@fastify/static': - specifier: 10.1.2 - version: 10.1.2 + specifier: 10.1.3 + version: 10.1.3 '@keyv/redis': specifier: 5.1.6 version: 5.1.6(keyv@5.6.0) '@langchain/core': specifier: 1.1.46 - version: 1.1.46(@opentelemetry/api@1.9.0)(@opentelemetry/sdk-trace-base@2.2.0(@opentelemetry/api@1.9.0))(openai@6.2.0(ws@8.21.0)(zod@4.3.6))(ws@8.21.0) + version: 1.1.46(@opentelemetry/api@1.9.0)(@opentelemetry/sdk-trace-base@2.2.0(@opentelemetry/api@1.9.0))(openai@6.2.0(ws@8.21.3)(zod@4.3.6))(ws@8.21.3) '@langchain/textsplitters': specifier: 1.0.1 - version: 1.0.1(@langchain/core@1.1.46(@opentelemetry/api@1.9.0)(@opentelemetry/sdk-trace-base@2.2.0(@opentelemetry/api@1.9.0))(openai@6.2.0(ws@8.21.0)(zod@4.3.6))(ws@8.21.0)) + version: 1.0.1(@langchain/core@1.1.46(@opentelemetry/api@1.9.0)(@opentelemetry/sdk-trace-base@2.2.0(@opentelemetry/api@1.9.0))(openai@6.2.0(ws@8.21.3)(zod@4.3.6))(ws@8.21.3)) '@modelcontextprotocol/sdk': specifier: 1.30.0 version: 1.30.0(@cfworker/json-schema@4.1.1)(supports-color@10.2.2)(zod@4.3.6) '@nest-lab/throttler-storage-redis': specifier: 1.2.0 - version: 1.2.0(@nestjs/common@11.1.28(class-transformer@0.5.1)(class-validator@0.15.1)(reflect-metadata@0.2.2)(rxjs@7.8.2)(supports-color@10.2.2))(@nestjs/core@11.1.27)(@nestjs/throttler@6.5.0(@nestjs/common@11.1.28(class-transformer@0.5.1)(class-validator@0.15.1)(reflect-metadata@0.2.2)(rxjs@7.8.2)(supports-color@10.2.2))(@nestjs/core@11.1.27)(reflect-metadata@0.2.2))(ioredis@5.10.1(supports-color@10.2.2))(reflect-metadata@0.2.2) + version: 1.2.0(@nestjs/common@11.2.1(class-transformer@0.5.1)(class-validator@0.15.1)(reflect-metadata@0.2.2)(rxjs@7.8.2)(supports-color@10.2.2))(@nestjs/core@11.2.1)(@nestjs/throttler@6.5.0(@nestjs/common@11.2.1(class-transformer@0.5.1)(class-validator@0.15.1)(reflect-metadata@0.2.2)(rxjs@7.8.2)(supports-color@10.2.2))(@nestjs/core@11.2.1)(reflect-metadata@0.2.2))(ioredis@5.10.1(supports-color@10.2.2))(reflect-metadata@0.2.2) '@nestjs-labs/nestjs-ioredis': specifier: 11.0.4 - version: 11.0.4(@nestjs/common@11.1.28(class-transformer@0.5.1)(class-validator@0.15.1)(reflect-metadata@0.2.2)(rxjs@7.8.2)(supports-color@10.2.2))(@nestjs/core@11.1.27)(ioredis@5.10.1(supports-color@10.2.2)) + version: 11.0.4(@nestjs/common@11.2.1(class-transformer@0.5.1)(class-validator@0.15.1)(reflect-metadata@0.2.2)(rxjs@7.8.2)(supports-color@10.2.2))(@nestjs/core@11.2.1)(ioredis@5.10.1(supports-color@10.2.2)) '@nestjs/bullmq': - specifier: 11.0.4 - version: 11.0.4(@nestjs/common@11.1.28(class-transformer@0.5.1)(class-validator@0.15.1)(reflect-metadata@0.2.2)(rxjs@7.8.2)(supports-color@10.2.2))(@nestjs/core@11.1.27)(bullmq@5.79.0(supports-color@10.2.2)) + specifier: 11.0.5 + version: 11.0.5(@nestjs/common@11.2.1(class-transformer@0.5.1)(class-validator@0.15.1)(reflect-metadata@0.2.2)(rxjs@7.8.2)(supports-color@10.2.2))(@nestjs/core@11.2.1)(bullmq@5.79.0(supports-color@10.2.2)) '@nestjs/cache-manager': specifier: 3.1.3 - version: 3.1.3(@nestjs/common@11.1.28(class-transformer@0.5.1)(class-validator@0.15.1)(reflect-metadata@0.2.2)(rxjs@7.8.2)(supports-color@10.2.2))(@nestjs/core@11.1.27)(cache-manager@7.2.8)(keyv@5.6.0)(rxjs@7.8.2) + version: 3.1.3(@nestjs/common@11.2.1(class-transformer@0.5.1)(class-validator@0.15.1)(reflect-metadata@0.2.2)(rxjs@7.8.2)(supports-color@10.2.2))(@nestjs/core@11.2.1)(cache-manager@7.2.8)(keyv@5.6.0)(rxjs@7.8.2) '@nestjs/common': - specifier: 11.1.28 - version: 11.1.28(class-transformer@0.5.1)(class-validator@0.15.1)(reflect-metadata@0.2.2)(rxjs@7.8.2)(supports-color@10.2.2) + specifier: 11.2.1 + version: 11.2.1(class-transformer@0.5.1)(class-validator@0.15.1)(reflect-metadata@0.2.2)(rxjs@7.8.2)(supports-color@10.2.2) '@nestjs/config': specifier: 4.0.4 - version: 4.0.4(@nestjs/common@11.1.28(class-transformer@0.5.1)(class-validator@0.15.1)(reflect-metadata@0.2.2)(rxjs@7.8.2)(supports-color@10.2.2))(rxjs@7.8.2) + version: 4.0.4(@nestjs/common@11.2.1(class-transformer@0.5.1)(class-validator@0.15.1)(reflect-metadata@0.2.2)(rxjs@7.8.2)(supports-color@10.2.2))(rxjs@7.8.2) '@nestjs/core': - specifier: 11.1.27 - version: 11.1.27(@nestjs/common@11.1.28(class-transformer@0.5.1)(class-validator@0.15.1)(reflect-metadata@0.2.2)(rxjs@7.8.2)(supports-color@10.2.2))(@nestjs/websockets@11.1.28)(reflect-metadata@0.2.2)(rxjs@7.8.2) + specifier: 11.2.1 + version: 11.2.1(@nestjs/common@11.2.1(class-transformer@0.5.1)(class-validator@0.15.1)(reflect-metadata@0.2.2)(rxjs@7.8.2)(supports-color@10.2.2))(@nestjs/websockets@11.2.1)(reflect-metadata@0.2.2)(rxjs@7.8.2) '@nestjs/event-emitter': specifier: 3.1.0 - version: 3.1.0(@nestjs/common@11.1.28(class-transformer@0.5.1)(class-validator@0.15.1)(reflect-metadata@0.2.2)(rxjs@7.8.2)(supports-color@10.2.2))(@nestjs/core@11.1.27) + version: 3.1.0(@nestjs/common@11.2.1(class-transformer@0.5.1)(class-validator@0.15.1)(reflect-metadata@0.2.2)(rxjs@7.8.2)(supports-color@10.2.2))(@nestjs/core@11.2.1) '@nestjs/jwt': specifier: 11.0.2 - version: 11.0.2(@nestjs/common@11.1.28(class-transformer@0.5.1)(class-validator@0.15.1)(reflect-metadata@0.2.2)(rxjs@7.8.2)(supports-color@10.2.2)) + version: 11.0.2(@nestjs/common@11.2.1(class-transformer@0.5.1)(class-validator@0.15.1)(reflect-metadata@0.2.2)(rxjs@7.8.2)(supports-color@10.2.2)) '@nestjs/mapped-types': specifier: 2.1.1 - version: 2.1.1(@nestjs/common@11.1.28(class-transformer@0.5.1)(class-validator@0.15.1)(reflect-metadata@0.2.2)(rxjs@7.8.2)(supports-color@10.2.2))(class-transformer@0.5.1)(class-validator@0.15.1)(reflect-metadata@0.2.2) + version: 2.1.1(@nestjs/common@11.2.1(class-transformer@0.5.1)(class-validator@0.15.1)(reflect-metadata@0.2.2)(rxjs@7.8.2)(supports-color@10.2.2))(class-transformer@0.5.1)(class-validator@0.15.1)(reflect-metadata@0.2.2) '@nestjs/passport': specifier: 11.0.5 - version: 11.0.5(@nestjs/common@11.1.28(class-transformer@0.5.1)(class-validator@0.15.1)(reflect-metadata@0.2.2)(rxjs@7.8.2)(supports-color@10.2.2))(passport@0.7.0) + version: 11.0.5(@nestjs/common@11.2.1(class-transformer@0.5.1)(class-validator@0.15.1)(reflect-metadata@0.2.2)(rxjs@7.8.2)(supports-color@10.2.2))(passport@0.7.0) '@nestjs/platform-fastify': - specifier: 11.1.28 - version: 11.1.28(@fastify/static@10.1.2)(@nestjs/common@11.1.28(class-transformer@0.5.1)(class-validator@0.15.1)(reflect-metadata@0.2.2)(rxjs@7.8.2)(supports-color@10.2.2))(@nestjs/core@11.1.27) + specifier: 11.2.1 + version: 11.2.1(@fastify/static@10.1.3)(@nestjs/common@11.2.1(class-transformer@0.5.1)(class-validator@0.15.1)(reflect-metadata@0.2.2)(rxjs@7.8.2)(supports-color@10.2.2))(@nestjs/core@11.2.1) '@nestjs/platform-socket.io': - specifier: 11.1.28 - version: 11.1.28(@nestjs/common@11.1.28(class-transformer@0.5.1)(class-validator@0.15.1)(reflect-metadata@0.2.2)(rxjs@7.8.2)(supports-color@10.2.2))(@nestjs/websockets@11.1.28)(rxjs@7.8.2)(supports-color@10.2.2) + specifier: 11.2.1 + version: 11.2.1(@nestjs/common@11.2.1(class-transformer@0.5.1)(class-validator@0.15.1)(reflect-metadata@0.2.2)(rxjs@7.8.2)(supports-color@10.2.2))(@nestjs/websockets@11.2.1)(rxjs@7.8.2)(supports-color@10.2.2) '@nestjs/schedule': specifier: 6.1.3 - version: 6.1.3(@nestjs/common@11.1.28(class-transformer@0.5.1)(class-validator@0.15.1)(reflect-metadata@0.2.2)(rxjs@7.8.2)(supports-color@10.2.2))(@nestjs/core@11.1.27) + version: 6.1.3(@nestjs/common@11.2.1(class-transformer@0.5.1)(class-validator@0.15.1)(reflect-metadata@0.2.2)(rxjs@7.8.2)(supports-color@10.2.2))(@nestjs/core@11.2.1) '@nestjs/terminus': specifier: 11.1.1 - version: 11.1.1(@nestjs/common@11.1.28(class-transformer@0.5.1)(class-validator@0.15.1)(reflect-metadata@0.2.2)(rxjs@7.8.2)(supports-color@10.2.2))(@nestjs/core@11.1.27)(reflect-metadata@0.2.2)(rxjs@7.8.2) + version: 11.1.1(@nestjs/common@11.2.1(class-transformer@0.5.1)(class-validator@0.15.1)(reflect-metadata@0.2.2)(rxjs@7.8.2)(supports-color@10.2.2))(@nestjs/core@11.2.1)(reflect-metadata@0.2.2)(rxjs@7.8.2) '@nestjs/throttler': specifier: 6.5.0 - version: 6.5.0(@nestjs/common@11.1.28(class-transformer@0.5.1)(class-validator@0.15.1)(reflect-metadata@0.2.2)(rxjs@7.8.2)(supports-color@10.2.2))(@nestjs/core@11.1.27)(reflect-metadata@0.2.2) + version: 6.5.0(@nestjs/common@11.2.1(class-transformer@0.5.1)(class-validator@0.15.1)(reflect-metadata@0.2.2)(rxjs@7.8.2)(supports-color@10.2.2))(@nestjs/core@11.2.1)(reflect-metadata@0.2.2) '@nestjs/websockets': - specifier: 11.1.28 - version: 11.1.28(@nestjs/common@11.1.28(class-transformer@0.5.1)(class-validator@0.15.1)(reflect-metadata@0.2.2)(rxjs@7.8.2)(supports-color@10.2.2))(@nestjs/core@11.1.27)(@nestjs/platform-socket.io@11.1.28)(reflect-metadata@0.2.2)(rxjs@7.8.2) + specifier: 11.2.1 + version: 11.2.1(@nestjs/common@11.2.1(class-transformer@0.5.1)(class-validator@0.15.1)(reflect-metadata@0.2.2)(rxjs@7.8.2)(supports-color@10.2.2))(@nestjs/core@11.2.1)(@nestjs/platform-socket.io@11.2.1)(reflect-metadata@0.2.2)(rxjs@7.8.2) '@node-saml/passport-saml': specifier: 5.1.0 version: 5.1.0(supports-color@10.2.2) @@ -676,13 +676,13 @@ importers: version: 5.1.16 nestjs-cls: specifier: 6.2.0 - version: 6.2.0(@nestjs/common@11.1.28(class-transformer@0.5.1)(class-validator@0.15.1)(reflect-metadata@0.2.2)(rxjs@7.8.2)(supports-color@10.2.2))(@nestjs/core@11.1.27)(reflect-metadata@0.2.2)(rxjs@7.8.2) + version: 6.2.0(@nestjs/common@11.2.1(class-transformer@0.5.1)(class-validator@0.15.1)(reflect-metadata@0.2.2)(rxjs@7.8.2)(supports-color@10.2.2))(@nestjs/core@11.2.1)(reflect-metadata@0.2.2)(rxjs@7.8.2) nestjs-kysely: specifier: 3.1.2 - version: 3.1.2(@nestjs/common@11.1.28(class-transformer@0.5.1)(class-validator@0.15.1)(reflect-metadata@0.2.2)(rxjs@7.8.2)(supports-color@10.2.2))(@nestjs/core@11.1.27)(kysely@0.28.17)(reflect-metadata@0.2.2) + version: 3.1.2(@nestjs/common@11.2.1(class-transformer@0.5.1)(class-validator@0.15.1)(reflect-metadata@0.2.2)(rxjs@7.8.2)(supports-color@10.2.2))(@nestjs/core@11.2.1)(kysely@0.28.17)(reflect-metadata@0.2.2) nestjs-pino: specifier: 4.6.1 - version: 4.6.1(@nestjs/common@11.1.28(class-transformer@0.5.1)(class-validator@0.15.1)(reflect-metadata@0.2.2)(rxjs@7.8.2)(supports-color@10.2.2))(pino-http@11.0.0)(pino@10.1.0)(rxjs@7.8.2) + version: 4.6.1(@nestjs/common@11.2.1(class-transformer@0.5.1)(class-validator@0.15.1)(reflect-metadata@0.2.2)(rxjs@7.8.2)(supports-color@10.2.2))(pino-http@11.0.0)(pino@10.1.0)(rxjs@7.8.2) nodemailer: specifier: 9.0.1 version: 9.0.1 @@ -756,8 +756,8 @@ importers: specifier: 7.29.0 version: 7.29.0 ws: - specifier: 8.21.0 - version: 8.21.0 + specifier: 8.21.3 + version: 8.21.3 yauzl: specifier: 3.4.0 version: 3.4.0 @@ -776,7 +776,7 @@ importers: version: 11.0.10(chokidar@4.0.3)(typescript@5.9.3) '@nestjs/testing': specifier: ^11.1.19 - version: 11.1.19(@nestjs/common@11.1.28(class-transformer@0.5.1)(class-validator@0.15.1)(reflect-metadata@0.2.2)(rxjs@7.8.2)(supports-color@10.2.2))(@nestjs/core@11.1.27) + version: 11.1.19(@nestjs/common@11.2.1(class-transformer@0.5.1)(class-validator@0.15.1)(reflect-metadata@0.2.2)(rxjs@7.8.2)(supports-color@10.2.2))(@nestjs/core@11.2.1) '@types/bcrypt': specifier: ^6.0.0 version: 6.0.0 @@ -2159,11 +2159,11 @@ packages: '@fastify/busboy@3.1.1': resolution: {integrity: sha512-5DGmA8FTdB2XbDeEwc/5ZXBl6UbBAyBOOLlPuBnZ/N1SwdH9Ii+cOX3tBROlDgcTXxjOYnLMVoKk9+FXAw0CJw==} - '@fastify/cookie@11.0.2': - resolution: {integrity: sha512-GWdwdGlgJxyvNv+QcKiGNevSspMQXncjMZ1J8IvuDQk0jvkzgWWZFNC2En3s+nHndZBGV8IbLwOI/sxCZw/mzA==} + '@fastify/cookie@11.1.2': + resolution: {integrity: sha512-Dtrpk/YOGUsbRMvP/8ZqPpwnMRv0qSqodFdoQ2B589Obc7jw4s4Qla+cV72Bsm7WsZJnqlYFX/i7uSBq0xzg6g==} - '@fastify/cors@11.2.0': - resolution: {integrity: sha512-LbLHBuSAdGdSFZYTLVA3+Ch2t+sA6nq3Ejc6XLAKiQ6ViS2qFnvicpj0htsx03FyYeLs04HfRNBsz/a8SvbcUw==} + '@fastify/cors@11.3.0': + resolution: {integrity: sha512-ggQGua+xHv1MvePbPr0v//xLYEsCXbWspquXCJS9Ot5YoRXq8J8ZWzHnxDBVnbtXosvistXo6LtNzOJswf64Fw==} '@fastify/deepmerge@3.1.0': resolution: {integrity: sha512-lCVONBQINyNhM6LLezB6+2afusgEYR4G8xenMsfe+AT+iZ7Ca6upM5Ha8UkZuYSnuMw3GWl/BiPXnLMi/gSxuQ==} @@ -2183,8 +2183,8 @@ packages: '@fastify/merge-json-schemas@0.2.1': resolution: {integrity: sha512-OA3KGBCy6KtIvLf8DINC5880o5iBlDX4SxzLQS8HorJAbqluzLRn80UXU0bxZn7UOFhFgpRJDasfwn9nG4FG4A==} - '@fastify/multipart@10.0.0': - resolution: {integrity: sha512-pUx3Z1QStY7E7kwvDTIvB6P+rF5lzP+iqPgZyJyG3yBJVPvQaZxzDHYbQD89rbY0ciXrMOyGi8ezHDVexLvJDA==} + '@fastify/multipart@10.1.1': + resolution: {integrity: sha512-jyRHgnFVdchZRKjJRf6kGPEiDp3Bg4MLo4d6/krt8Z4RutLrqL5IYWihx8a4tnv7Tu7JfuHcyC+dU9zPzTxiSg==} '@fastify/proxy-addr@5.0.0': resolution: {integrity: sha512-37qVVA1qZ5sgH7KpHkkC4z9SK6StIsIcOmpjvMPXNb3vx2GQxhZocogVYbr2PbbeLCQxYIPDok307xEvRZOzGA==} @@ -2192,8 +2192,8 @@ packages: '@fastify/send@4.1.0': resolution: {integrity: sha512-TMYeQLCBSy2TOFmV95hQWkiTYgC/SEx7vMdV+wnZVX4tt8VBLKzmH8vV9OzJehV0+XBfg+WxPMt5wp+JBUKsVw==} - '@fastify/static@10.1.2': - resolution: {integrity: sha512-G/g18cG9tLutT/OVyN1AIsHIl9L1UwmJ+S3dkyhVpplIx0nEMicd7RGQ+uJLyhKKF4a3tTcQydccn3Mop1fX+Q==} + '@fastify/static@10.1.3': + resolution: {integrity: sha512-W6jqajYS974XjPjB5hQWoxPM8NKM4+p8YmQT6G5IbCa4uhdWSVadZUv75siy1wEA/3ty8RYdpBydfWeu9AqAqQ==} '@floating-ui/core@1.7.5': resolution: {integrity: sha512-1Ih4WTWyw0+lKyFMcBHGbb5U5FtuHJuujoyyr5zTaWS5EYMeT6Jb2AuDeftsCsEuchO+mM2ij5+q9crhydzLhQ==} @@ -2710,18 +2710,18 @@ packages: '@nestjs/core': ^10.0.0 || ^11.0.0 ioredis: ^5.0.0 - '@nestjs/bull-shared@11.0.4': - resolution: {integrity: sha512-VBJcDHSAzxQnpcDfA0kt9MTGUD1XZzfByV70su0W0eDCQ9aqIEBlzWRW21tv9FG9dIut22ysgDidshdjlnczLw==} + '@nestjs/bull-shared@11.0.5': + resolution: {integrity: sha512-QI7GHPk4oePBwszAZn/Kz5hgB79KijB1RDoo+E/BrSt33sHUrvRZXVyO0+knZleyloRYy0LT2VwsqSP/aQvg4Q==} peerDependencies: '@nestjs/common': ^10.0.0 || ^11.0.0 '@nestjs/core': ^10.0.0 || ^11.0.0 - '@nestjs/bullmq@11.0.4': - resolution: {integrity: sha512-wBzK9raAVG0/6NTMdvLGM4/FQ1lsB35/pYS8L6a0SDgkTiLpd7mAjQ8R692oMx5s7IjvgntaZOuTUrKYLNfIkA==} + '@nestjs/bullmq@11.0.5': + resolution: {integrity: sha512-u0VGVc5aER8A+4jEYXDin2rtqIHR8sFL68RUgNDgZslvjdeb1QL4CF+Sr0v4zMXJEfaLSVD606Oq0cryNNCxHg==} peerDependencies: '@nestjs/common': ^10.0.0 || ^11.0.0 '@nestjs/core': ^10.0.0 || ^11.0.0 - bullmq: ^3.0.0 || ^4.0.0 || ^5.0.0 + bullmq: ^3.0.0 || ^4.0.0 || ^5.0.0 || ^6.0.0 '@nestjs/cache-manager@3.1.3': resolution: {integrity: sha512-HMtiOfHz75NZX7mJn1VnZGLSachVI04TnUc5wvEogIaKwk5BDQHgtP5htxreizjv7oxKalJbuTyxtiF6bE+bgQ==} @@ -2745,8 +2745,8 @@ packages: '@swc/core': optional: true - '@nestjs/common@11.1.28': - resolution: {integrity: sha512-bRImsxibie+AM7xjdwcrm/gr5YeacI65kSBNzTufa1Ib5iwziaY/lqMtRh9THq6pbV4e1HP9aI2ZxGUumnmaoQ==} + '@nestjs/common@11.2.1': + resolution: {integrity: sha512-SEgtP+M9DqNhQkgJIlJ3oTp3gemo/8owySovzMGmJj2kcfIH1G6QP45AAb8dE4a3IpVicpUvdDAy7Syk7ebjBw==} peerDependencies: class-transformer: '>=0.4.1' class-validator: '>=0.13.2' @@ -2764,8 +2764,8 @@ packages: '@nestjs/common': ^10.0.0 || ^11.0.0 rxjs: ^7.1.0 - '@nestjs/core@11.1.27': - resolution: {integrity: sha512-K6DX7hcqmZdeXkv7tsPakKBRCgqL19a4mtbX4FluY0hWtFdtPKp6lbe+lb8gWPfvLdbOWr/CPScn7BSjBX+Ecg==} + '@nestjs/core@11.2.1': + resolution: {integrity: sha512-M5PWFU8NdRTgX9Po49d7TQKg7f5t8GAVUa/Esy4tmaMWcKugTzg3ZzpJfD3LEPMuRHjfs6+8pQYgtuP2uz3rDw==} engines: {node: '>= 20'} peerDependencies: '@nestjs/common': ^11.0.0 @@ -2812,10 +2812,10 @@ packages: '@nestjs/common': ^10.0.0 || ^11.0.0 passport: ^0.5.0 || ^0.6.0 || ^0.7.0 - '@nestjs/platform-fastify@11.1.28': - resolution: {integrity: sha512-utUfyxRzZsoFxz1GU3Z0OthHpijB605iqtxwFnk9yKowLrU1t1ZkxMUuad/csemImY6AsuaTpTjCecKbmfl+pQ==} + '@nestjs/platform-fastify@11.2.1': + resolution: {integrity: sha512-dX00kr+yAA7Eh8u/LsYxaxdUizmhvaeUT4pbpNTixNDLFBP73XVuatSrgBdmq7HVAsk0mozI/zL/g7R0MSVUrA==} peerDependencies: - '@fastify/static': ^8.0.0 || ^9.0.0 + '@fastify/static': ^10.1.2 '@fastify/view': ^10.0.0 || ^11.0.0 || ^12.0.0 '@nestjs/common': ^11.0.0 '@nestjs/core': ^11.0.0 @@ -2825,8 +2825,8 @@ packages: '@fastify/view': optional: true - '@nestjs/platform-socket.io@11.1.28': - resolution: {integrity: sha512-vY+GmU2jBcymvgm5rEnftUx4qNxK8cDJmXjl1/1NcpITTNJo0vg07xYR43MwXHcMqe7b0jwqt5+UCTzxqQFIqA==} + '@nestjs/platform-socket.io@11.2.1': + resolution: {integrity: sha512-BZuk8PpZCN5Dp1nXRuzRIMP9GwOH7YkP10nXQDtKnJfJnMhEfi5ANTjHrk/j0qHB/dhxy6WgFzib7ncSg7wMDw==} peerDependencies: '@nestjs/common': ^11.0.0 '@nestjs/websockets': ^11.0.0 @@ -2911,8 +2911,8 @@ packages: '@nestjs/core': ^7.0.0 || ^8.0.0 || ^9.0.0 || ^10.0.0 || ^11.0.0 reflect-metadata: ^0.1.13 || ^0.2.0 - '@nestjs/websockets@11.1.28': - resolution: {integrity: sha512-jeyclAURCJTN8S8lctDhfLdiJeDKjZmYWWLav653Fb9hl9c+zx5jPhavI8Xk5++R8u+lX9qzaRxtsjEoxTtjyw==} + '@nestjs/websockets@11.2.1': + resolution: {integrity: sha512-HWBSJl4kciGJW+PfSgVUKa2tsrM9IqmjsovGQOla44x6VnE/N57y/dJmEJISaoHUJm+bDm4whO5ggXxQHhLwMw==} peerDependencies: '@nestjs/common': ^11.0.0 '@nestjs/core': ^11.0.0 @@ -5647,12 +5647,13 @@ packages: resolution: {integrity: sha512-ei8Aos7ja0weRpFzJnEA9UHJ/7XQmqglbRwnf2ATjcB9Wq874VKH9kfjjirM6UhU2/E5fFYadylyhFldcqSidQ==} engines: {node: '>=18'} + cookie@2.0.1: + resolution: {integrity: sha512-yuToqVvRrj6pfDXREyQAAv8SkAEk/8GS3jQRTiUMm66TVtBYmqQeoEjL2Lmq8Rpo6271vH76InTChTitEAm65w==} + engines: {node: '>=22'} + cookiejar@2.1.4: resolution: {integrity: sha512-LDx6oHrK+PhzLKJU9j5S7/Y3jM/mUHvD/DeI1WQmJn652iPC5Y4TBzC9l+5OMOXlyTTA+SmVUPm0HQUwpD5Jqw==} - copy-anything@2.0.6: - resolution: {integrity: sha512-1j20GZTsvKNkc4BY3NpMOM8tt///wY3FpIzozTOFO2ffuZcV61nojHXVKIy3WM+7ADCy5FVhdZYHYDdgTU0yJw==} - core-js-compat@3.35.0: resolution: {integrity: sha512-5blwFAddknKeNgsjBzilkdQ0+YK8L1PfqPYq40NOYMYFSS38qj+hpTcLLWwpIwA2A5bje/x5jmVn2tzUMg9IVw==} @@ -6200,10 +6201,6 @@ packages: resolution: {integrity: sha512-dtJUTepzMW3Lm/NPxRf3wP4642UWhjL2sQxc+ym2YMj1m/H2zDNQOlezafzkHwn6sMstjHTwG6iQQsctDW/b1A==} engines: {node: ^12.20.0 || ^14.13.1 || >=16.0.0} - errno@0.1.8: - resolution: {integrity: sha512-dJ6oBr5SQ1VSd9qkk7ByRgb/1SH4JZjCHSW/mr63/QcXO9zLVxvJ6Oy13nio03rxpSnVDDjFor75SjVeZWPW/A==} - hasBin: true - error-ex@1.3.2: resolution: {integrity: sha512-7dFHNmqeFSEt2ZBsCriorKnn3Z2pj+fd9kmI6QoWw4//DL+icEBfc0U7qJCisqrTsKTjw4fNFy2pW9OqStD84g==} @@ -6466,8 +6463,8 @@ packages: fastify-plugin@6.0.0: resolution: {integrity: sha512-fZOty7z3O7vOliF6d8bHE3wiEh1KcNnKEQensSgTk9C1DvN6nRLS++XVd86v33Hw/8u9Un8A1zDrQ8ujcQDHEg==} - fastify@5.10.0: - resolution: {integrity: sha512-A9L0ziuWGQHgEEVgF3davQ9vbD93IuX+lo2IsxapQmu5b/Y/ynn9m9K5JHt9dvyJXOFc5iN0Zk5GHEOqnzhWjg==} + fastify@5.11.3: + resolution: {integrity: sha512-W6hzDP8s0iSeL7LGwY6Oc/ZxuXWOvFEMs6p2L0Si415YRo27W5pBKdOTXxhemBDeSTAcpYf5evRA9onF2OYhPA==} fastq@1.17.1: resolution: {integrity: sha512-sRVD3lWVIXWg6By68ZN7vho9a1pQcN/WBFaAAsDDFzlJjvoGx0P8z7V1t72grFJfJhu3YPZBuu25f7Kaw2jN1w==} @@ -6842,11 +6839,6 @@ packages: engines: {node: '>=18'} hasBin: true - image-size@0.5.5: - resolution: {integrity: sha512-6TDAlDPZxUFCv+fuOkIoXT/V/f3Qbq8e37p+YOiYrUv3v9cc3/6x78VdfPgFVaB9dZYeLUfKgHRebpkm/oP2VQ==} - engines: {node: '>=0.10.0'} - hasBin: true - immediate@3.0.6: resolution: {integrity: sha512-XXOFtyqDjNDAQxVfYxuF7g9Il/IbWmmlQg2MYKOH8ExIT1qg6xc4zyS3HaEEATgs1btfzxq15ciUiY7gjSXRGQ==} @@ -7049,9 +7041,6 @@ packages: resolution: {integrity: sha512-LvIm3/KWzS9oRFHugab7d+M/GcBXuXX5xZkzPmN+NxihdQlZUQ4dWuSV1xR/sq6upL1TJEDrfBgRepHFdBtSNQ==} engines: {node: '>= 0.4'} - is-what@3.14.1: - resolution: {integrity: sha512-sNxgpk9793nzSs7bA6JQJGeIuRBQhAaNGG77kzYQgMkrID+lS6SlK07K5LaptscDlSaIgH+GPFzf+d75FVxozA==} - is-wsl@3.1.0: resolution: {integrity: sha512-UcVfVfaK4Sc4m7X3dUSoHoozQGBEFeDC+zVo06t98xe8CzHSZZBekNXH+tu0NalHolcJ/QAGqS46Hef7QXBIMw==} engines: {node: '>=16'} @@ -7482,7 +7471,7 @@ packages: '@opentelemetry/exporter-trace-otlp-proto': '*' '@opentelemetry/sdk-trace-base': '*' openai: '*' - ws: 8.21.0 + ws: 8.21.3 peerDependenciesMeta: '@opentelemetry/api': optional: true @@ -7508,11 +7497,6 @@ packages: leac@0.6.0: resolution: {integrity: sha512-y+SqErxb8h7nE/fiEX07jsbuhrpO9lL8eca7/Y1nuWV2moNlXhyd59iDGcRf6moVyDMbmTNzL40SUyrFU/yDpg==} - less@4.2.0: - resolution: {integrity: sha512-P3b3HJDBtSzsXUl0im2L7gTO5Ubg8mEN6G8qoTS77iXxXX4Hvu4Qj540PZDvQ8V6DmX6iXo98k7Md0Cm1PrLaA==} - engines: {node: '>=6'} - hasBin: true - leven@3.1.0: resolution: {integrity: sha512-qsda+H8jTaUaN/x5vzW2rzc+8Rw4TAQ/4KjB46IwK5VH+IlVeeeje/EoZRpiXvIqjFgK84QffqPztGI3VBLG1A==} engines: {node: '>=6'} @@ -7729,10 +7713,6 @@ packages: magic-string@0.30.21: resolution: {integrity: sha512-vd2F4YUyEXKGcLHoq+TEyCjxueSeHnFxyyjNp80yg0XV4vUhnDer/lvvlqM/arB5bXQN5K2/3oinyCRyx8T2CQ==} - make-dir@2.1.0: - resolution: {integrity: sha512-LS9X+dc8KLxXCb8dni79fLIIUA5VyZoyjSMCwTluaXA0o27cCK0bhXkpgw+sTXVpPy/lSO57ilRixqk0vDmtRA==} - engines: {node: '>=6'} - make-dir@4.0.0: resolution: {integrity: sha512-hXdUTZYIVOt1Ex//jAQi+wTZZpUpwBj/0QsOzqegb3rGMMeJiSEu5xLHnYfBrRV4RH2+OCSOO95Is/7x1WJ4bw==} engines: {node: '>=10'} @@ -7819,11 +7799,6 @@ packages: resolution: {integrity: sha512-Lbgzdk0h4juoQ9fCKXW4by0UJqj+nOOrI9MJ1sSj4nI8aI2eo1qmvQEie4VD1glsS250n15LsWsYtCugiStS5A==} engines: {node: '>=18'} - mime@1.6.0: - resolution: {integrity: sha512-x0Vn8spI+wuJ1O6S7gnbaQg8Pxh4NNHb7KSINmEWKiPE4RKOplvijn+NkmYmmRgP68mc70j2EbeTFRsrswaQeg==} - engines: {node: '>=4'} - hasBin: true - mime@2.6.0: resolution: {integrity: sha512-USPkMeET31rOMiarsBNIHZKLGgvKc/LrjofAnBlOttf5ajRvqiRA8QsenbcooctK6d6Ts6aqZXBA+XbkKthiQg==} engines: {node: '>=4.0.0'} @@ -7897,8 +7872,8 @@ packages: resolution: {integrity: sha512-WWdIxpyjEn+FhQJQQv9aQAYlHoNVdzIzUySNV1gHUPDSdZJ3yZn7pAAbQcV7B56Mvu881q9FZV+0Vx2xC44VWA==} engines: {node: ^18.17.0 || >=20.5.0} - nanoid@3.3.17: - resolution: {integrity: sha512-xQLf0A3HOMlgHq0n247/LRuAOYmB7dXJ/DvAxGvsSBij45XtBSmQycu+F8ODbHwns/XyFZagyL1+J0Offw1E0g==} + nanoid@3.3.18: + resolution: {integrity: sha512-DTg4MJbGMWkfi6VZFdNt2/caMbQy4Ou+Op/hJQvGEWcnVfoA1QA+xzRKAzw9jD6+GVOOeYr/mIcuDSdug6F6+w==} engines: {node: ^10 || ^12 || ^13.7 || ^14 || >=15.0.1} hasBin: true @@ -7915,11 +7890,6 @@ packages: natural-compare@1.4.0: resolution: {integrity: sha512-OWND8ei3VtNC9h7V60qff3SVobHr996CTwgxubgyQYEpg290h9J0buyECNNJexkFm5sOajh5G116RYA1c8ZMSw==} - needle@3.5.0: - resolution: {integrity: sha512-jaQyPKKk2YokHrEg+vFDYxXIHTCBgiZwSHOoVx/8V3GIBS8/VN6NdVRmg8q1ERtPkMvmOvebsgga4sAj5hls/w==} - engines: {node: '>= 4.4.x'} - hasBin: true - negotiator@0.6.3: resolution: {integrity: sha512-+EUsqGPLsM+j/zdChZjsnX51g4XrHFOIXwfnCVPGlQk/k5giakcKsuxCObBRu6DSm9opw/O6slWbJdghQM4bBg==} engines: {node: '>= 0.6'} @@ -8095,7 +8065,7 @@ packages: resolution: {integrity: sha512-qqjzHls7F5xkXNGy9P1Ei1rorI5LWupUUFWP66zPU8FlZbiITX8SFcHMKNZg/NATJ0LpIZcMUFxSwQmdeQPwSw==} hasBin: true peerDependencies: - ws: 8.21.0 + ws: 8.21.3 zod: ^3.25 || ^4.0 peerDependenciesMeta: ws: @@ -8186,10 +8156,6 @@ packages: resolution: {integrity: sha512-ayCKvm/phCGxOkYRSCM82iDwct8/EonSEgCSxWxD7ve6jHggsFl4fZVQBPRNgQoKiuV/odhFrGzQXZwbifC8Rg==} engines: {node: '>=8'} - parse-node-version@1.0.1: - resolution: {integrity: sha512-3YHlOa/JgH6Mnpr05jP9eDG254US9ek25LyIxZlDItp2iJtwyaXQb57lBYLdT3MowkUFYEV2XXNAYIPlESvJlA==} - engines: {node: '>= 0.10'} - parse5-htmlparser2-tree-adapter@7.1.0: resolution: {integrity: sha512-ruw5xyKs6lrpo9x9rCZqZZnIUntICjQAd0Wsmp396Ul9lN/h+ifgVV1x1gZHi8euej6wTfpqX8j+BFQxF0NS/g==} @@ -8333,10 +8299,6 @@ packages: resolution: {integrity: sha512-lGA1TNsmy2bxvRsTI2cV01kfTwKzZjnZSDmF9llYNyMHMrU4sP87lQ5taiIKm88L3cbswjl008nwyGc3WpNvzg==} engines: {node: '>=18.0.0'} - pify@4.0.1: - resolution: {integrity: sha512-uB80kBFb/tfd68bVleG9T5GGsGPjJrLAUpR5PZIrhBnIaRTQRjqdJSsIKkOP6OAIFbj7GOrcudc5pNjZ+geV2g==} - engines: {node: '>=6'} - pino-abstract-transport@2.0.0: resolution: {integrity: sha512-F63x5tizV6WCh4R6RHyi2Ml+M70DNRXt/+HANowMflpgGFMAym/VKm6G7ZOQRjqN7XbGxK1Lg9t6ZrtzOaivMw==} @@ -8555,9 +8517,6 @@ packages: resolution: {integrity: sha512-cJ+oHTW1VAEa8cJslgmUZrc+sjRKgAKl3Zyse6+PV38hZe/V6Z14TbCuXcan9F9ghlz4QrFr2c92TNF82UkYHA==} engines: {node: '>=10'} - prr@1.0.1: - resolution: {integrity: sha512-yPw4Sng1gWghHQWj0B3ZggWUm4qVbPwPFcRG8KyxiU7J2OHFSoEHKS+EZ3fv5l1t9CyCiop6l/ZYeWbrgoQejw==} - psl@1.15.0: resolution: {integrity: sha512-JZd3gMVBAVQkSs6HdNZo9Sdo0LNcQeMNP3CozBJb3JYC/QUYZTnKxP+f8oWRX4rHP5EurWxqAHTSwUCjlNKa1w==} @@ -8969,10 +8928,6 @@ packages: selderee@0.11.0: resolution: {integrity: sha512-5TF+l7p4+OsnP8BCCvSyZiSPc4x4//p5uPwK8TCnVPJYRmU2aYKMpOXvw8zM5a5JvuuCGN1jmsMwuU2W02ukfA==} - semver@5.7.2: - resolution: {integrity: sha512-cBznnQ9KjJqU67B52RMC65CMarK2600WFnbkcaiwWq3xy/5haFJlshgnpjovMVJ+Hff49d8GEn0b87C5pDQ10g==} - hasBin: true - semver@6.3.1: resolution: {integrity: sha512-BR7VvDCVHO+q2xBEWskxS6DJE1qRnb7DxzUrogb71CWoSficBxYsiAGd+Kl0mmq/MprG9yArRkyrQxTO6XjMzA==} hasBin: true @@ -9931,8 +9886,8 @@ packages: resolution: {integrity: sha512-+QU2zd6OTD8XWIJCbffaiQeH9U73qIqafo1x6V1snCWYGJf6cVE0cDR4D8xRzcEnfI21IFrUPzPGtcPf8AC+Rw==} engines: {node: ^14.17.0 || ^16.13.0 || >=18.0.0} - ws@8.21.0: - resolution: {integrity: sha512-Vsp28b7DRcimFQvrqu2Wek3z1iYxDCWqHYB8Qsnk/S4RfaCQzPGPyBNuVjJV3cd6UiKtUtp6sNM77gWvzcCH+g==} + ws@8.21.3: + resolution: {integrity: sha512-201TZ/kPWxoPr/OKWjquZR1SWKXcvxdH+e1xrx89b3YbmzLMFCLfnaG1HFIgWzJOEWZ7MvpK++odZufgYR50Rw==} engines: {node: '>=10.0.0'} peerDependencies: bufferutil: ^4.0.1 @@ -11908,7 +11863,7 @@ snapshots: jotai-scope: 0.7.2(jotai@2.11.0(@types/react@19.2.17)(react@19.2.7))(react@19.2.7) lodash.debounce: 4.0.8 lodash.throttle: 4.1.1 - nanoid: 3.3.17 + nanoid: 3.3.18 pako: 2.0.3 perfect-freehand: 1.2.0 pica: 7.1.1 @@ -11955,14 +11910,14 @@ snapshots: '@fastify/busboy@3.1.1': {} - '@fastify/cookie@11.0.2': + '@fastify/cookie@11.1.2': dependencies: - cookie: 1.1.1 - fastify-plugin: 5.1.0 + cookie: 2.0.1 + fastify-plugin: 6.0.0 - '@fastify/cors@11.2.0': + '@fastify/cors@11.3.0': dependencies: - fastify-plugin: 5.1.0 + fastify-plugin: 6.0.0 toad-cache: 3.7.0 '@fastify/deepmerge@3.1.0': {} @@ -11984,12 +11939,12 @@ snapshots: dependencies: dequal: 2.0.3 - '@fastify/multipart@10.0.0': + '@fastify/multipart@10.1.1': dependencies: '@fastify/busboy': 3.1.1 '@fastify/deepmerge': 3.1.0 '@fastify/error': 4.0.0 - fastify-plugin: 5.1.0 + fastify-plugin: 6.0.0 secure-json-parse: 4.0.0 '@fastify/proxy-addr@5.0.0': @@ -12005,7 +11960,7 @@ snapshots: http-errors: 2.0.1 mime: 3.0.0 - '@fastify/static@10.1.2': + '@fastify/static@10.1.3': dependencies: '@fastify/accept-negotiator': 2.0.1 '@fastify/error': 4.0.0 @@ -12492,12 +12447,12 @@ snapshots: '@keyv/serialize@1.1.1': {} - '@langchain/core@1.1.46(@opentelemetry/api@1.9.0)(@opentelemetry/sdk-trace-base@2.2.0(@opentelemetry/api@1.9.0))(openai@6.2.0(ws@8.21.0)(zod@4.3.6))(ws@8.21.0)': + '@langchain/core@1.1.46(@opentelemetry/api@1.9.0)(@opentelemetry/sdk-trace-base@2.2.0(@opentelemetry/api@1.9.0))(openai@6.2.0(ws@8.21.3)(zod@4.3.6))(ws@8.21.3)': dependencies: '@cfworker/json-schema': 4.1.1 '@standard-schema/spec': 1.1.0 js-tiktoken: 1.0.21 - langsmith: 0.8.9(@opentelemetry/api@1.9.0)(@opentelemetry/sdk-trace-base@2.2.0(@opentelemetry/api@1.9.0))(openai@6.2.0(ws@8.21.0)(zod@4.3.6))(ws@8.21.0) + langsmith: 0.8.9(@opentelemetry/api@1.9.0)(@opentelemetry/sdk-trace-base@2.2.0(@opentelemetry/api@1.9.0))(openai@6.2.0(ws@8.21.3)(zod@4.3.6))(ws@8.21.3) mustache: 4.2.0 p-queue: 6.6.2 zod: 4.3.6 @@ -12508,9 +12463,9 @@ snapshots: - openai - ws - '@langchain/textsplitters@1.0.1(@langchain/core@1.1.46(@opentelemetry/api@1.9.0)(@opentelemetry/sdk-trace-base@2.2.0(@opentelemetry/api@1.9.0))(openai@6.2.0(ws@8.21.0)(zod@4.3.6))(ws@8.21.0))': + '@langchain/textsplitters@1.0.1(@langchain/core@1.1.46(@opentelemetry/api@1.9.0)(@opentelemetry/sdk-trace-base@2.2.0(@opentelemetry/api@1.9.0))(openai@6.2.0(ws@8.21.3)(zod@4.3.6))(ws@8.21.3))': dependencies: - '@langchain/core': 1.1.46(@opentelemetry/api@1.9.0)(@opentelemetry/sdk-trace-base@2.2.0(@opentelemetry/api@1.9.0))(openai@6.2.0(ws@8.21.0)(zod@4.3.6))(ws@8.21.0) + '@langchain/core': 1.1.46(@opentelemetry/api@1.9.0)(@opentelemetry/sdk-trace-base@2.2.0(@opentelemetry/api@1.9.0))(openai@6.2.0(ws@8.21.3)(zod@4.3.6))(ws@8.21.3) js-tiktoken: 1.0.21 '@lifeomic/attempt@3.1.0': {} @@ -12650,40 +12605,40 @@ snapshots: '@tybys/wasm-util': 0.10.2 optional: true - '@nest-lab/throttler-storage-redis@1.2.0(@nestjs/common@11.1.28(class-transformer@0.5.1)(class-validator@0.15.1)(reflect-metadata@0.2.2)(rxjs@7.8.2)(supports-color@10.2.2))(@nestjs/core@11.1.27)(@nestjs/throttler@6.5.0(@nestjs/common@11.1.28(class-transformer@0.5.1)(class-validator@0.15.1)(reflect-metadata@0.2.2)(rxjs@7.8.2)(supports-color@10.2.2))(@nestjs/core@11.1.27)(reflect-metadata@0.2.2))(ioredis@5.10.1(supports-color@10.2.2))(reflect-metadata@0.2.2)': + '@nest-lab/throttler-storage-redis@1.2.0(@nestjs/common@11.2.1(class-transformer@0.5.1)(class-validator@0.15.1)(reflect-metadata@0.2.2)(rxjs@7.8.2)(supports-color@10.2.2))(@nestjs/core@11.2.1)(@nestjs/throttler@6.5.0(@nestjs/common@11.2.1(class-transformer@0.5.1)(class-validator@0.15.1)(reflect-metadata@0.2.2)(rxjs@7.8.2)(supports-color@10.2.2))(@nestjs/core@11.2.1)(reflect-metadata@0.2.2))(ioredis@5.10.1(supports-color@10.2.2))(reflect-metadata@0.2.2)': dependencies: - '@nestjs/common': 11.1.28(class-transformer@0.5.1)(class-validator@0.15.1)(reflect-metadata@0.2.2)(rxjs@7.8.2)(supports-color@10.2.2) - '@nestjs/core': 11.1.27(@nestjs/common@11.1.28(class-transformer@0.5.1)(class-validator@0.15.1)(reflect-metadata@0.2.2)(rxjs@7.8.2)(supports-color@10.2.2))(@nestjs/websockets@11.1.28)(reflect-metadata@0.2.2)(rxjs@7.8.2) - '@nestjs/throttler': 6.5.0(@nestjs/common@11.1.28(class-transformer@0.5.1)(class-validator@0.15.1)(reflect-metadata@0.2.2)(rxjs@7.8.2)(supports-color@10.2.2))(@nestjs/core@11.1.27)(reflect-metadata@0.2.2) + '@nestjs/common': 11.2.1(class-transformer@0.5.1)(class-validator@0.15.1)(reflect-metadata@0.2.2)(rxjs@7.8.2)(supports-color@10.2.2) + '@nestjs/core': 11.2.1(@nestjs/common@11.2.1(class-transformer@0.5.1)(class-validator@0.15.1)(reflect-metadata@0.2.2)(rxjs@7.8.2)(supports-color@10.2.2))(@nestjs/websockets@11.2.1)(reflect-metadata@0.2.2)(rxjs@7.8.2) + '@nestjs/throttler': 6.5.0(@nestjs/common@11.2.1(class-transformer@0.5.1)(class-validator@0.15.1)(reflect-metadata@0.2.2)(rxjs@7.8.2)(supports-color@10.2.2))(@nestjs/core@11.2.1)(reflect-metadata@0.2.2) ioredis: 5.10.1(supports-color@10.2.2) reflect-metadata: 0.2.2 tslib: 2.8.1 - '@nestjs-labs/nestjs-ioredis@11.0.4(@nestjs/common@11.1.28(class-transformer@0.5.1)(class-validator@0.15.1)(reflect-metadata@0.2.2)(rxjs@7.8.2)(supports-color@10.2.2))(@nestjs/core@11.1.27)(ioredis@5.10.1(supports-color@10.2.2))': + '@nestjs-labs/nestjs-ioredis@11.0.4(@nestjs/common@11.2.1(class-transformer@0.5.1)(class-validator@0.15.1)(reflect-metadata@0.2.2)(rxjs@7.8.2)(supports-color@10.2.2))(@nestjs/core@11.2.1)(ioredis@5.10.1(supports-color@10.2.2))': dependencies: - '@nestjs/common': 11.1.28(class-transformer@0.5.1)(class-validator@0.15.1)(reflect-metadata@0.2.2)(rxjs@7.8.2)(supports-color@10.2.2) - '@nestjs/core': 11.1.27(@nestjs/common@11.1.28(class-transformer@0.5.1)(class-validator@0.15.1)(reflect-metadata@0.2.2)(rxjs@7.8.2)(supports-color@10.2.2))(@nestjs/websockets@11.1.28)(reflect-metadata@0.2.2)(rxjs@7.8.2) + '@nestjs/common': 11.2.1(class-transformer@0.5.1)(class-validator@0.15.1)(reflect-metadata@0.2.2)(rxjs@7.8.2)(supports-color@10.2.2) + '@nestjs/core': 11.2.1(@nestjs/common@11.2.1(class-transformer@0.5.1)(class-validator@0.15.1)(reflect-metadata@0.2.2)(rxjs@7.8.2)(supports-color@10.2.2))(@nestjs/websockets@11.2.1)(reflect-metadata@0.2.2)(rxjs@7.8.2) ioredis: 5.10.1(supports-color@10.2.2) tslib: 2.8.1 - '@nestjs/bull-shared@11.0.4(@nestjs/common@11.1.28(class-transformer@0.5.1)(class-validator@0.15.1)(reflect-metadata@0.2.2)(rxjs@7.8.2)(supports-color@10.2.2))(@nestjs/core@11.1.27)': + '@nestjs/bull-shared@11.0.5(@nestjs/common@11.2.1(class-transformer@0.5.1)(class-validator@0.15.1)(reflect-metadata@0.2.2)(rxjs@7.8.2)(supports-color@10.2.2))(@nestjs/core@11.2.1)': dependencies: - '@nestjs/common': 11.1.28(class-transformer@0.5.1)(class-validator@0.15.1)(reflect-metadata@0.2.2)(rxjs@7.8.2)(supports-color@10.2.2) - '@nestjs/core': 11.1.27(@nestjs/common@11.1.28(class-transformer@0.5.1)(class-validator@0.15.1)(reflect-metadata@0.2.2)(rxjs@7.8.2)(supports-color@10.2.2))(@nestjs/websockets@11.1.28)(reflect-metadata@0.2.2)(rxjs@7.8.2) + '@nestjs/common': 11.2.1(class-transformer@0.5.1)(class-validator@0.15.1)(reflect-metadata@0.2.2)(rxjs@7.8.2)(supports-color@10.2.2) + '@nestjs/core': 11.2.1(@nestjs/common@11.2.1(class-transformer@0.5.1)(class-validator@0.15.1)(reflect-metadata@0.2.2)(rxjs@7.8.2)(supports-color@10.2.2))(@nestjs/websockets@11.2.1)(reflect-metadata@0.2.2)(rxjs@7.8.2) tslib: 2.8.1 - '@nestjs/bullmq@11.0.4(@nestjs/common@11.1.28(class-transformer@0.5.1)(class-validator@0.15.1)(reflect-metadata@0.2.2)(rxjs@7.8.2)(supports-color@10.2.2))(@nestjs/core@11.1.27)(bullmq@5.79.0(supports-color@10.2.2))': + '@nestjs/bullmq@11.0.5(@nestjs/common@11.2.1(class-transformer@0.5.1)(class-validator@0.15.1)(reflect-metadata@0.2.2)(rxjs@7.8.2)(supports-color@10.2.2))(@nestjs/core@11.2.1)(bullmq@5.79.0(supports-color@10.2.2))': dependencies: - '@nestjs/bull-shared': 11.0.4(@nestjs/common@11.1.28(class-transformer@0.5.1)(class-validator@0.15.1)(reflect-metadata@0.2.2)(rxjs@7.8.2)(supports-color@10.2.2))(@nestjs/core@11.1.27) - '@nestjs/common': 11.1.28(class-transformer@0.5.1)(class-validator@0.15.1)(reflect-metadata@0.2.2)(rxjs@7.8.2)(supports-color@10.2.2) - '@nestjs/core': 11.1.27(@nestjs/common@11.1.28(class-transformer@0.5.1)(class-validator@0.15.1)(reflect-metadata@0.2.2)(rxjs@7.8.2)(supports-color@10.2.2))(@nestjs/websockets@11.1.28)(reflect-metadata@0.2.2)(rxjs@7.8.2) + '@nestjs/bull-shared': 11.0.5(@nestjs/common@11.2.1(class-transformer@0.5.1)(class-validator@0.15.1)(reflect-metadata@0.2.2)(rxjs@7.8.2)(supports-color@10.2.2))(@nestjs/core@11.2.1) + '@nestjs/common': 11.2.1(class-transformer@0.5.1)(class-validator@0.15.1)(reflect-metadata@0.2.2)(rxjs@7.8.2)(supports-color@10.2.2) + '@nestjs/core': 11.2.1(@nestjs/common@11.2.1(class-transformer@0.5.1)(class-validator@0.15.1)(reflect-metadata@0.2.2)(rxjs@7.8.2)(supports-color@10.2.2))(@nestjs/websockets@11.2.1)(reflect-metadata@0.2.2)(rxjs@7.8.2) bullmq: 5.79.0(supports-color@10.2.2) tslib: 2.8.1 - '@nestjs/cache-manager@3.1.3(@nestjs/common@11.1.28(class-transformer@0.5.1)(class-validator@0.15.1)(reflect-metadata@0.2.2)(rxjs@7.8.2)(supports-color@10.2.2))(@nestjs/core@11.1.27)(cache-manager@7.2.8)(keyv@5.6.0)(rxjs@7.8.2)': + '@nestjs/cache-manager@3.1.3(@nestjs/common@11.2.1(class-transformer@0.5.1)(class-validator@0.15.1)(reflect-metadata@0.2.2)(rxjs@7.8.2)(supports-color@10.2.2))(@nestjs/core@11.2.1)(cache-manager@7.2.8)(keyv@5.6.0)(rxjs@7.8.2)': dependencies: - '@nestjs/common': 11.1.28(class-transformer@0.5.1)(class-validator@0.15.1)(reflect-metadata@0.2.2)(rxjs@7.8.2)(supports-color@10.2.2) - '@nestjs/core': 11.1.27(@nestjs/common@11.1.28(class-transformer@0.5.1)(class-validator@0.15.1)(reflect-metadata@0.2.2)(rxjs@7.8.2)(supports-color@10.2.2))(@nestjs/websockets@11.1.28)(reflect-metadata@0.2.2)(rxjs@7.8.2) + '@nestjs/common': 11.2.1(class-transformer@0.5.1)(class-validator@0.15.1)(reflect-metadata@0.2.2)(rxjs@7.8.2)(supports-color@10.2.2) + '@nestjs/core': 11.2.1(@nestjs/common@11.2.1(class-transformer@0.5.1)(class-validator@0.15.1)(reflect-metadata@0.2.2)(rxjs@7.8.2)(supports-color@10.2.2))(@nestjs/websockets@11.2.1)(reflect-metadata@0.2.2)(rxjs@7.8.2) cache-manager: 7.2.8 keyv: 5.6.0 rxjs: 7.8.2 @@ -12716,7 +12671,7 @@ snapshots: - uglify-js - webpack-cli - '@nestjs/common@11.1.28(class-transformer@0.5.1)(class-validator@0.15.1)(reflect-metadata@0.2.2)(rxjs@7.8.2)(supports-color@10.2.2)': + '@nestjs/common@11.2.1(class-transformer@0.5.1)(class-validator@0.15.1)(reflect-metadata@0.2.2)(rxjs@7.8.2)(supports-color@10.2.2)': dependencies: file-type: 21.3.4(supports-color@10.2.2) iterare: 1.2.1 @@ -12731,17 +12686,17 @@ snapshots: transitivePeerDependencies: - supports-color - '@nestjs/config@4.0.4(@nestjs/common@11.1.28(class-transformer@0.5.1)(class-validator@0.15.1)(reflect-metadata@0.2.2)(rxjs@7.8.2)(supports-color@10.2.2))(rxjs@7.8.2)': + '@nestjs/config@4.0.4(@nestjs/common@11.2.1(class-transformer@0.5.1)(class-validator@0.15.1)(reflect-metadata@0.2.2)(rxjs@7.8.2)(supports-color@10.2.2))(rxjs@7.8.2)': dependencies: - '@nestjs/common': 11.1.28(class-transformer@0.5.1)(class-validator@0.15.1)(reflect-metadata@0.2.2)(rxjs@7.8.2)(supports-color@10.2.2) + '@nestjs/common': 11.2.1(class-transformer@0.5.1)(class-validator@0.15.1)(reflect-metadata@0.2.2)(rxjs@7.8.2)(supports-color@10.2.2) dotenv: 17.4.1 dotenv-expand: 12.0.3 lodash: 4.18.1 rxjs: 7.8.2 - '@nestjs/core@11.1.27(@nestjs/common@11.1.28(class-transformer@0.5.1)(class-validator@0.15.1)(reflect-metadata@0.2.2)(rxjs@7.8.2)(supports-color@10.2.2))(@nestjs/websockets@11.1.28)(reflect-metadata@0.2.2)(rxjs@7.8.2)': + '@nestjs/core@11.2.1(@nestjs/common@11.2.1(class-transformer@0.5.1)(class-validator@0.15.1)(reflect-metadata@0.2.2)(rxjs@7.8.2)(supports-color@10.2.2))(@nestjs/websockets@11.2.1)(reflect-metadata@0.2.2)(rxjs@7.8.2)': dependencies: - '@nestjs/common': 11.1.28(class-transformer@0.5.1)(class-validator@0.15.1)(reflect-metadata@0.2.2)(rxjs@7.8.2)(supports-color@10.2.2) + '@nestjs/common': 11.2.1(class-transformer@0.5.1)(class-validator@0.15.1)(reflect-metadata@0.2.2)(rxjs@7.8.2)(supports-color@10.2.2) fast-safe-stringify: 2.1.1 iterare: 1.2.1 path-to-regexp: 8.4.2 @@ -12750,41 +12705,41 @@ snapshots: tslib: 2.8.1 uid: 2.0.2 optionalDependencies: - '@nestjs/websockets': 11.1.28(@nestjs/common@11.1.28(class-transformer@0.5.1)(class-validator@0.15.1)(reflect-metadata@0.2.2)(rxjs@7.8.2)(supports-color@10.2.2))(@nestjs/core@11.1.27)(@nestjs/platform-socket.io@11.1.28)(reflect-metadata@0.2.2)(rxjs@7.8.2) + '@nestjs/websockets': 11.2.1(@nestjs/common@11.2.1(class-transformer@0.5.1)(class-validator@0.15.1)(reflect-metadata@0.2.2)(rxjs@7.8.2)(supports-color@10.2.2))(@nestjs/core@11.2.1)(@nestjs/platform-socket.io@11.2.1)(reflect-metadata@0.2.2)(rxjs@7.8.2) - '@nestjs/event-emitter@3.1.0(@nestjs/common@11.1.28(class-transformer@0.5.1)(class-validator@0.15.1)(reflect-metadata@0.2.2)(rxjs@7.8.2)(supports-color@10.2.2))(@nestjs/core@11.1.27)': + '@nestjs/event-emitter@3.1.0(@nestjs/common@11.2.1(class-transformer@0.5.1)(class-validator@0.15.1)(reflect-metadata@0.2.2)(rxjs@7.8.2)(supports-color@10.2.2))(@nestjs/core@11.2.1)': dependencies: - '@nestjs/common': 11.1.28(class-transformer@0.5.1)(class-validator@0.15.1)(reflect-metadata@0.2.2)(rxjs@7.8.2)(supports-color@10.2.2) - '@nestjs/core': 11.1.27(@nestjs/common@11.1.28(class-transformer@0.5.1)(class-validator@0.15.1)(reflect-metadata@0.2.2)(rxjs@7.8.2)(supports-color@10.2.2))(@nestjs/websockets@11.1.28)(reflect-metadata@0.2.2)(rxjs@7.8.2) + '@nestjs/common': 11.2.1(class-transformer@0.5.1)(class-validator@0.15.1)(reflect-metadata@0.2.2)(rxjs@7.8.2)(supports-color@10.2.2) + '@nestjs/core': 11.2.1(@nestjs/common@11.2.1(class-transformer@0.5.1)(class-validator@0.15.1)(reflect-metadata@0.2.2)(rxjs@7.8.2)(supports-color@10.2.2))(@nestjs/websockets@11.2.1)(reflect-metadata@0.2.2)(rxjs@7.8.2) eventemitter2: 6.4.9 - '@nestjs/jwt@11.0.2(@nestjs/common@11.1.28(class-transformer@0.5.1)(class-validator@0.15.1)(reflect-metadata@0.2.2)(rxjs@7.8.2)(supports-color@10.2.2))': + '@nestjs/jwt@11.0.2(@nestjs/common@11.2.1(class-transformer@0.5.1)(class-validator@0.15.1)(reflect-metadata@0.2.2)(rxjs@7.8.2)(supports-color@10.2.2))': dependencies: - '@nestjs/common': 11.1.28(class-transformer@0.5.1)(class-validator@0.15.1)(reflect-metadata@0.2.2)(rxjs@7.8.2)(supports-color@10.2.2) + '@nestjs/common': 11.2.1(class-transformer@0.5.1)(class-validator@0.15.1)(reflect-metadata@0.2.2)(rxjs@7.8.2)(supports-color@10.2.2) '@types/jsonwebtoken': 9.0.10 jsonwebtoken: 9.0.3 - '@nestjs/mapped-types@2.1.1(@nestjs/common@11.1.28(class-transformer@0.5.1)(class-validator@0.15.1)(reflect-metadata@0.2.2)(rxjs@7.8.2)(supports-color@10.2.2))(class-transformer@0.5.1)(class-validator@0.15.1)(reflect-metadata@0.2.2)': + '@nestjs/mapped-types@2.1.1(@nestjs/common@11.2.1(class-transformer@0.5.1)(class-validator@0.15.1)(reflect-metadata@0.2.2)(rxjs@7.8.2)(supports-color@10.2.2))(class-transformer@0.5.1)(class-validator@0.15.1)(reflect-metadata@0.2.2)': dependencies: - '@nestjs/common': 11.1.28(class-transformer@0.5.1)(class-validator@0.15.1)(reflect-metadata@0.2.2)(rxjs@7.8.2)(supports-color@10.2.2) + '@nestjs/common': 11.2.1(class-transformer@0.5.1)(class-validator@0.15.1)(reflect-metadata@0.2.2)(rxjs@7.8.2)(supports-color@10.2.2) reflect-metadata: 0.2.2 optionalDependencies: class-transformer: 0.5.1 class-validator: 0.15.1 - '@nestjs/passport@11.0.5(@nestjs/common@11.1.28(class-transformer@0.5.1)(class-validator@0.15.1)(reflect-metadata@0.2.2)(rxjs@7.8.2)(supports-color@10.2.2))(passport@0.7.0)': + '@nestjs/passport@11.0.5(@nestjs/common@11.2.1(class-transformer@0.5.1)(class-validator@0.15.1)(reflect-metadata@0.2.2)(rxjs@7.8.2)(supports-color@10.2.2))(passport@0.7.0)': dependencies: - '@nestjs/common': 11.1.28(class-transformer@0.5.1)(class-validator@0.15.1)(reflect-metadata@0.2.2)(rxjs@7.8.2)(supports-color@10.2.2) + '@nestjs/common': 11.2.1(class-transformer@0.5.1)(class-validator@0.15.1)(reflect-metadata@0.2.2)(rxjs@7.8.2)(supports-color@10.2.2) passport: 0.7.0 - '@nestjs/platform-fastify@11.1.28(@fastify/static@10.1.2)(@nestjs/common@11.1.28(class-transformer@0.5.1)(class-validator@0.15.1)(reflect-metadata@0.2.2)(rxjs@7.8.2)(supports-color@10.2.2))(@nestjs/core@11.1.27)': + '@nestjs/platform-fastify@11.2.1(@fastify/static@10.1.3)(@nestjs/common@11.2.1(class-transformer@0.5.1)(class-validator@0.15.1)(reflect-metadata@0.2.2)(rxjs@7.8.2)(supports-color@10.2.2))(@nestjs/core@11.2.1)': dependencies: - '@fastify/cors': 11.2.0 + '@fastify/cors': 11.3.0 '@fastify/formbody': 8.0.2 - '@nestjs/common': 11.1.28(class-transformer@0.5.1)(class-validator@0.15.1)(reflect-metadata@0.2.2)(rxjs@7.8.2)(supports-color@10.2.2) - '@nestjs/core': 11.1.27(@nestjs/common@11.1.28(class-transformer@0.5.1)(class-validator@0.15.1)(reflect-metadata@0.2.2)(rxjs@7.8.2)(supports-color@10.2.2))(@nestjs/websockets@11.1.28)(reflect-metadata@0.2.2)(rxjs@7.8.2) + '@nestjs/common': 11.2.1(class-transformer@0.5.1)(class-validator@0.15.1)(reflect-metadata@0.2.2)(rxjs@7.8.2)(supports-color@10.2.2) + '@nestjs/core': 11.2.1(@nestjs/common@11.2.1(class-transformer@0.5.1)(class-validator@0.15.1)(reflect-metadata@0.2.2)(rxjs@7.8.2)(supports-color@10.2.2))(@nestjs/websockets@11.2.1)(reflect-metadata@0.2.2)(rxjs@7.8.2) fast-querystring: 1.1.2 - fastify: 5.10.0 + fastify: 5.11.3 fastify-plugin: 6.0.0 find-my-way: 9.7.0 light-my-request: 6.6.0 @@ -12792,12 +12747,12 @@ snapshots: reusify: 1.1.0 tslib: 2.8.1 optionalDependencies: - '@fastify/static': 10.1.2 + '@fastify/static': 10.1.3 - '@nestjs/platform-socket.io@11.1.28(@nestjs/common@11.1.28(class-transformer@0.5.1)(class-validator@0.15.1)(reflect-metadata@0.2.2)(rxjs@7.8.2)(supports-color@10.2.2))(@nestjs/websockets@11.1.28)(rxjs@7.8.2)(supports-color@10.2.2)': + '@nestjs/platform-socket.io@11.2.1(@nestjs/common@11.2.1(class-transformer@0.5.1)(class-validator@0.15.1)(reflect-metadata@0.2.2)(rxjs@7.8.2)(supports-color@10.2.2))(@nestjs/websockets@11.2.1)(rxjs@7.8.2)(supports-color@10.2.2)': dependencies: - '@nestjs/common': 11.1.28(class-transformer@0.5.1)(class-validator@0.15.1)(reflect-metadata@0.2.2)(rxjs@7.8.2)(supports-color@10.2.2) - '@nestjs/websockets': 11.1.28(@nestjs/common@11.1.28(class-transformer@0.5.1)(class-validator@0.15.1)(reflect-metadata@0.2.2)(rxjs@7.8.2)(supports-color@10.2.2))(@nestjs/core@11.1.27)(@nestjs/platform-socket.io@11.1.28)(reflect-metadata@0.2.2)(rxjs@7.8.2) + '@nestjs/common': 11.2.1(class-transformer@0.5.1)(class-validator@0.15.1)(reflect-metadata@0.2.2)(rxjs@7.8.2)(supports-color@10.2.2) + '@nestjs/websockets': 11.2.1(@nestjs/common@11.2.1(class-transformer@0.5.1)(class-validator@0.15.1)(reflect-metadata@0.2.2)(rxjs@7.8.2)(supports-color@10.2.2))(@nestjs/core@11.2.1)(@nestjs/platform-socket.io@11.2.1)(reflect-metadata@0.2.2)(rxjs@7.8.2) rxjs: 7.8.2 socket.io: 4.8.3(supports-color@10.2.2) tslib: 2.8.1 @@ -12806,10 +12761,10 @@ snapshots: - supports-color - utf-8-validate - '@nestjs/schedule@6.1.3(@nestjs/common@11.1.28(class-transformer@0.5.1)(class-validator@0.15.1)(reflect-metadata@0.2.2)(rxjs@7.8.2)(supports-color@10.2.2))(@nestjs/core@11.1.27)': + '@nestjs/schedule@6.1.3(@nestjs/common@11.2.1(class-transformer@0.5.1)(class-validator@0.15.1)(reflect-metadata@0.2.2)(rxjs@7.8.2)(supports-color@10.2.2))(@nestjs/core@11.2.1)': dependencies: - '@nestjs/common': 11.1.28(class-transformer@0.5.1)(class-validator@0.15.1)(reflect-metadata@0.2.2)(rxjs@7.8.2)(supports-color@10.2.2) - '@nestjs/core': 11.1.27(@nestjs/common@11.1.28(class-transformer@0.5.1)(class-validator@0.15.1)(reflect-metadata@0.2.2)(rxjs@7.8.2)(supports-color@10.2.2))(@nestjs/websockets@11.1.28)(reflect-metadata@0.2.2)(rxjs@7.8.2) + '@nestjs/common': 11.2.1(class-transformer@0.5.1)(class-validator@0.15.1)(reflect-metadata@0.2.2)(rxjs@7.8.2)(supports-color@10.2.2) + '@nestjs/core': 11.2.1(@nestjs/common@11.2.1(class-transformer@0.5.1)(class-validator@0.15.1)(reflect-metadata@0.2.2)(rxjs@7.8.2)(supports-color@10.2.2))(@nestjs/websockets@11.2.1)(reflect-metadata@0.2.2)(rxjs@7.8.2) cron: 4.4.0 '@nestjs/schematics@11.0.10(chokidar@4.0.3)(typescript@5.9.3)': @@ -12823,38 +12778,38 @@ snapshots: transitivePeerDependencies: - chokidar - '@nestjs/terminus@11.1.1(@nestjs/common@11.1.28(class-transformer@0.5.1)(class-validator@0.15.1)(reflect-metadata@0.2.2)(rxjs@7.8.2)(supports-color@10.2.2))(@nestjs/core@11.1.27)(reflect-metadata@0.2.2)(rxjs@7.8.2)': + '@nestjs/terminus@11.1.1(@nestjs/common@11.2.1(class-transformer@0.5.1)(class-validator@0.15.1)(reflect-metadata@0.2.2)(rxjs@7.8.2)(supports-color@10.2.2))(@nestjs/core@11.2.1)(reflect-metadata@0.2.2)(rxjs@7.8.2)': dependencies: - '@nestjs/common': 11.1.28(class-transformer@0.5.1)(class-validator@0.15.1)(reflect-metadata@0.2.2)(rxjs@7.8.2)(supports-color@10.2.2) - '@nestjs/core': 11.1.27(@nestjs/common@11.1.28(class-transformer@0.5.1)(class-validator@0.15.1)(reflect-metadata@0.2.2)(rxjs@7.8.2)(supports-color@10.2.2))(@nestjs/websockets@11.1.28)(reflect-metadata@0.2.2)(rxjs@7.8.2) + '@nestjs/common': 11.2.1(class-transformer@0.5.1)(class-validator@0.15.1)(reflect-metadata@0.2.2)(rxjs@7.8.2)(supports-color@10.2.2) + '@nestjs/core': 11.2.1(@nestjs/common@11.2.1(class-transformer@0.5.1)(class-validator@0.15.1)(reflect-metadata@0.2.2)(rxjs@7.8.2)(supports-color@10.2.2))(@nestjs/websockets@11.2.1)(reflect-metadata@0.2.2)(rxjs@7.8.2) boxen: 5.1.2 check-disk-space: 3.4.0 reflect-metadata: 0.2.2 rxjs: 7.8.2 - '@nestjs/testing@11.1.19(@nestjs/common@11.1.28(class-transformer@0.5.1)(class-validator@0.15.1)(reflect-metadata@0.2.2)(rxjs@7.8.2)(supports-color@10.2.2))(@nestjs/core@11.1.27)': + '@nestjs/testing@11.1.19(@nestjs/common@11.2.1(class-transformer@0.5.1)(class-validator@0.15.1)(reflect-metadata@0.2.2)(rxjs@7.8.2)(supports-color@10.2.2))(@nestjs/core@11.2.1)': dependencies: - '@nestjs/common': 11.1.28(class-transformer@0.5.1)(class-validator@0.15.1)(reflect-metadata@0.2.2)(rxjs@7.8.2)(supports-color@10.2.2) - '@nestjs/core': 11.1.27(@nestjs/common@11.1.28(class-transformer@0.5.1)(class-validator@0.15.1)(reflect-metadata@0.2.2)(rxjs@7.8.2)(supports-color@10.2.2))(@nestjs/websockets@11.1.28)(reflect-metadata@0.2.2)(rxjs@7.8.2) + '@nestjs/common': 11.2.1(class-transformer@0.5.1)(class-validator@0.15.1)(reflect-metadata@0.2.2)(rxjs@7.8.2)(supports-color@10.2.2) + '@nestjs/core': 11.2.1(@nestjs/common@11.2.1(class-transformer@0.5.1)(class-validator@0.15.1)(reflect-metadata@0.2.2)(rxjs@7.8.2)(supports-color@10.2.2))(@nestjs/websockets@11.2.1)(reflect-metadata@0.2.2)(rxjs@7.8.2) tslib: 2.8.1 - '@nestjs/throttler@6.5.0(@nestjs/common@11.1.28(class-transformer@0.5.1)(class-validator@0.15.1)(reflect-metadata@0.2.2)(rxjs@7.8.2)(supports-color@10.2.2))(@nestjs/core@11.1.27)(reflect-metadata@0.2.2)': + '@nestjs/throttler@6.5.0(@nestjs/common@11.2.1(class-transformer@0.5.1)(class-validator@0.15.1)(reflect-metadata@0.2.2)(rxjs@7.8.2)(supports-color@10.2.2))(@nestjs/core@11.2.1)(reflect-metadata@0.2.2)': dependencies: - '@nestjs/common': 11.1.28(class-transformer@0.5.1)(class-validator@0.15.1)(reflect-metadata@0.2.2)(rxjs@7.8.2)(supports-color@10.2.2) - '@nestjs/core': 11.1.27(@nestjs/common@11.1.28(class-transformer@0.5.1)(class-validator@0.15.1)(reflect-metadata@0.2.2)(rxjs@7.8.2)(supports-color@10.2.2))(@nestjs/websockets@11.1.28)(reflect-metadata@0.2.2)(rxjs@7.8.2) + '@nestjs/common': 11.2.1(class-transformer@0.5.1)(class-validator@0.15.1)(reflect-metadata@0.2.2)(rxjs@7.8.2)(supports-color@10.2.2) + '@nestjs/core': 11.2.1(@nestjs/common@11.2.1(class-transformer@0.5.1)(class-validator@0.15.1)(reflect-metadata@0.2.2)(rxjs@7.8.2)(supports-color@10.2.2))(@nestjs/websockets@11.2.1)(reflect-metadata@0.2.2)(rxjs@7.8.2) reflect-metadata: 0.2.2 - '@nestjs/websockets@11.1.28(@nestjs/common@11.1.28(class-transformer@0.5.1)(class-validator@0.15.1)(reflect-metadata@0.2.2)(rxjs@7.8.2)(supports-color@10.2.2))(@nestjs/core@11.1.27)(@nestjs/platform-socket.io@11.1.28)(reflect-metadata@0.2.2)(rxjs@7.8.2)': + '@nestjs/websockets@11.2.1(@nestjs/common@11.2.1(class-transformer@0.5.1)(class-validator@0.15.1)(reflect-metadata@0.2.2)(rxjs@7.8.2)(supports-color@10.2.2))(@nestjs/core@11.2.1)(@nestjs/platform-socket.io@11.2.1)(reflect-metadata@0.2.2)(rxjs@7.8.2)': dependencies: - '@nestjs/common': 11.1.28(class-transformer@0.5.1)(class-validator@0.15.1)(reflect-metadata@0.2.2)(rxjs@7.8.2)(supports-color@10.2.2) - '@nestjs/core': 11.1.27(@nestjs/common@11.1.28(class-transformer@0.5.1)(class-validator@0.15.1)(reflect-metadata@0.2.2)(rxjs@7.8.2)(supports-color@10.2.2))(@nestjs/websockets@11.1.28)(reflect-metadata@0.2.2)(rxjs@7.8.2) + '@nestjs/common': 11.2.1(class-transformer@0.5.1)(class-validator@0.15.1)(reflect-metadata@0.2.2)(rxjs@7.8.2)(supports-color@10.2.2) + '@nestjs/core': 11.2.1(@nestjs/common@11.2.1(class-transformer@0.5.1)(class-validator@0.15.1)(reflect-metadata@0.2.2)(rxjs@7.8.2)(supports-color@10.2.2))(@nestjs/websockets@11.2.1)(reflect-metadata@0.2.2)(rxjs@7.8.2) iterare: 1.2.1 object-hash: 3.0.0 reflect-metadata: 0.2.2 rxjs: 7.8.2 tslib: 2.8.1 optionalDependencies: - '@nestjs/platform-socket.io': 11.1.28(@nestjs/common@11.1.28(class-transformer@0.5.1)(class-validator@0.15.1)(reflect-metadata@0.2.2)(rxjs@7.8.2)(supports-color@10.2.2))(@nestjs/websockets@11.1.28)(rxjs@7.8.2)(supports-color@10.2.2) + '@nestjs/platform-socket.io': 11.2.1(@nestjs/common@11.2.1(class-transformer@0.5.1)(class-validator@0.15.1)(reflect-metadata@0.2.2)(rxjs@7.8.2)(supports-color@10.2.2))(@nestjs/websockets@11.2.1)(rxjs@7.8.2)(supports-color@10.2.2) '@noble/hashes@1.8.0': {} @@ -14926,10 +14881,10 @@ snapshots: '@vercel/oidc@3.1.0': {} - '@vitejs/plugin-react@6.0.1(vite@8.0.16(@types/node@22.19.1)(esbuild@0.28.1)(jiti@2.4.2)(less@4.2.0)(sugarss@5.0.1(postcss@8.5.25))(terser@5.39.0)(tsx@4.21.0)(yaml@2.8.3))': + '@vitejs/plugin-react@6.0.1(vite@8.0.16(@types/node@22.19.1)(esbuild@0.28.1)(jiti@2.4.2)(sugarss@5.0.1(postcss@8.5.25))(terser@5.39.0)(tsx@4.21.0)(yaml@2.8.3))': dependencies: '@rolldown/pluginutils': 1.0.0-rc.7 - vite: 8.0.16(@types/node@22.19.1)(esbuild@0.28.1)(jiti@2.4.2)(less@4.2.0)(sugarss@5.0.1(postcss@8.5.25))(terser@5.39.0)(tsx@4.21.0)(yaml@2.8.3) + vite: 8.0.16(@types/node@22.19.1)(esbuild@0.28.1)(jiti@2.4.2)(sugarss@5.0.1(postcss@8.5.25))(terser@5.39.0)(tsx@4.21.0)(yaml@2.8.3) '@vitest/expect@4.1.6': dependencies: @@ -14940,13 +14895,13 @@ snapshots: chai: 6.2.2 tinyrainbow: 3.1.0 - '@vitest/mocker@4.1.6(vite@8.0.16(@types/node@22.19.1)(esbuild@0.28.1)(jiti@2.4.2)(less@4.2.0)(sugarss@5.0.1(postcss@8.5.25))(terser@5.39.0)(tsx@4.21.0)(yaml@2.8.3))': + '@vitest/mocker@4.1.6(vite@8.0.16(@types/node@22.19.1)(esbuild@0.28.1)(jiti@2.4.2)(sugarss@5.0.1(postcss@8.5.25))(terser@5.39.0)(tsx@4.21.0)(yaml@2.8.3))': dependencies: '@vitest/spy': 4.1.6 estree-walker: 3.0.3 magic-string: 0.30.21 optionalDependencies: - vite: 8.0.16(@types/node@22.19.1)(esbuild@0.28.1)(jiti@2.4.2)(less@4.2.0)(sugarss@5.0.1(postcss@8.5.25))(terser@5.39.0)(tsx@4.21.0)(yaml@2.8.3) + vite: 8.0.16(@types/node@22.19.1)(esbuild@0.28.1)(jiti@2.4.2)(sugarss@5.0.1(postcss@8.5.25))(terser@5.39.0)(tsx@4.21.0)(yaml@2.8.3) '@vitest/pretty-format@4.1.6': dependencies: @@ -15783,12 +15738,9 @@ snapshots: cookie@1.1.1: {} - cookiejar@2.1.4: {} + cookie@2.0.1: {} - copy-anything@2.0.6: - dependencies: - is-what: 3.14.1 - optional: true + cookiejar@2.1.4: {} core-js-compat@3.35.0: dependencies: @@ -16312,7 +16264,7 @@ snapshots: '@socket.io/component-emitter': 3.1.0 debug: 4.3.7(supports-color@10.2.2) engine.io-parser: 5.2.2 - ws: 8.21.0 + ws: 8.21.3 xmlhttprequest-ssl: 2.1.2 transitivePeerDependencies: - bufferutil @@ -16332,7 +16284,7 @@ snapshots: cors: 2.8.5 debug: 4.4.3(supports-color@10.2.2) engine.io-parser: 5.2.2 - ws: 8.21.0 + ws: 8.21.3 transitivePeerDependencies: - bufferutil - supports-color @@ -16357,11 +16309,6 @@ snapshots: env-paths@3.0.0: {} - errno@0.1.8: - dependencies: - prr: 1.0.1 - optional: true - error-ex@1.3.2: dependencies: is-arrayish: 0.2.1 @@ -16780,7 +16727,7 @@ snapshots: fastify-plugin@6.0.0: {} - fastify@5.10.0: + fastify@5.11.3: dependencies: '@fastify/ajv-compiler': 4.0.5 '@fastify/error': 4.0.0 @@ -17049,7 +16996,7 @@ snapshots: '@types/ws': 8.18.1 entities: 7.0.1 whatwg-mimetype: 3.0.0 - ws: 8.21.0 + ws: 8.21.3 transitivePeerDependencies: - bufferutil - utf-8-validate @@ -17219,9 +17166,6 @@ snapshots: image-dimensions@2.5.0: {} - image-size@0.5.5: - optional: true - immediate@3.0.6: {} immutable@4.3.9: {} @@ -17404,9 +17348,6 @@ snapshots: call-bind: 1.0.8 get-intrinsic: 1.3.0 - is-what@3.14.1: - optional: true - is-wsl@3.1.0: dependencies: is-inside-container: 1.0.0 @@ -17844,7 +17785,7 @@ snapshots: whatwg-encoding: 3.1.1 whatwg-mimetype: 4.0.0 whatwg-url: 14.2.0 - ws: 8.21.0 + ws: 8.21.3 xml-name-validator: 5.0.0 transitivePeerDependencies: - bufferutil @@ -17871,7 +17812,7 @@ snapshots: whatwg-encoding: 3.1.1 whatwg-mimetype: 4.0.0 whatwg-url: 14.2.0 - ws: 8.21.0 + ws: 8.21.3 xml-name-validator: 5.0.0 transitivePeerDependencies: - bufferutil @@ -18018,14 +17959,14 @@ snapshots: vscode-languageserver-textdocument: 1.0.12 vscode-uri: 3.0.8 - langsmith@0.8.9(@opentelemetry/api@1.9.0)(@opentelemetry/sdk-trace-base@2.2.0(@opentelemetry/api@1.9.0))(openai@6.2.0(ws@8.21.0)(zod@4.3.6))(ws@8.21.0): + langsmith@0.8.9(@opentelemetry/api@1.9.0)(@opentelemetry/sdk-trace-base@2.2.0(@opentelemetry/api@1.9.0))(openai@6.2.0(ws@8.21.3)(zod@4.3.6))(ws@8.21.3): dependencies: p-queue: 6.6.2 optionalDependencies: '@opentelemetry/api': 1.9.0 '@opentelemetry/sdk-trace-base': 2.2.0(@opentelemetry/api@1.9.0) - openai: 6.2.0(ws@8.21.0)(zod@4.3.6) - ws: 8.21.0 + openai: 6.2.0(ws@8.21.3)(zod@4.3.6) + ws: 8.21.3 layout-base@1.0.2: {} @@ -18037,21 +17978,6 @@ snapshots: leac@0.6.0: {} - less@4.2.0: - dependencies: - copy-anything: 2.0.6 - parse-node-version: 1.0.1 - tslib: 2.8.1 - optionalDependencies: - errno: 0.1.8 - graceful-fs: 4.2.11 - image-size: 0.5.5 - make-dir: 2.1.0 - mime: 1.6.0 - needle: 3.5.0 - source-map: 0.6.1 - optional: true - leven@3.1.0: {} levn@0.4.1: @@ -18222,12 +18148,6 @@ snapshots: dependencies: '@jridgewell/sourcemap-codec': 1.5.5 - make-dir@2.1.0: - dependencies: - pify: 4.0.1 - semver: 5.7.2 - optional: true - make-dir@4.0.0: dependencies: semver: 7.7.4 @@ -18319,9 +18239,6 @@ snapshots: dependencies: mime-db: 1.54.0 - mime@1.6.0: - optional: true - mime@2.6.0: {} mime@3.0.0: {} @@ -18388,7 +18305,7 @@ snapshots: mute-stream@2.0.0: {} - nanoid@3.3.17: {} + nanoid@3.3.18: {} nanoid@5.1.16: {} @@ -18396,35 +18313,29 @@ snapshots: natural-compare@1.4.0: {} - needle@3.5.0: - dependencies: - iconv-lite: 0.6.3 - sax: 1.6.0 - optional: true - negotiator@0.6.3: {} negotiator@1.0.0: {} neo-async@2.6.2: {} - nestjs-cls@6.2.0(@nestjs/common@11.1.28(class-transformer@0.5.1)(class-validator@0.15.1)(reflect-metadata@0.2.2)(rxjs@7.8.2)(supports-color@10.2.2))(@nestjs/core@11.1.27)(reflect-metadata@0.2.2)(rxjs@7.8.2): + nestjs-cls@6.2.0(@nestjs/common@11.2.1(class-transformer@0.5.1)(class-validator@0.15.1)(reflect-metadata@0.2.2)(rxjs@7.8.2)(supports-color@10.2.2))(@nestjs/core@11.2.1)(reflect-metadata@0.2.2)(rxjs@7.8.2): dependencies: - '@nestjs/common': 11.1.28(class-transformer@0.5.1)(class-validator@0.15.1)(reflect-metadata@0.2.2)(rxjs@7.8.2)(supports-color@10.2.2) - '@nestjs/core': 11.1.27(@nestjs/common@11.1.28(class-transformer@0.5.1)(class-validator@0.15.1)(reflect-metadata@0.2.2)(rxjs@7.8.2)(supports-color@10.2.2))(@nestjs/websockets@11.1.28)(reflect-metadata@0.2.2)(rxjs@7.8.2) + '@nestjs/common': 11.2.1(class-transformer@0.5.1)(class-validator@0.15.1)(reflect-metadata@0.2.2)(rxjs@7.8.2)(supports-color@10.2.2) + '@nestjs/core': 11.2.1(@nestjs/common@11.2.1(class-transformer@0.5.1)(class-validator@0.15.1)(reflect-metadata@0.2.2)(rxjs@7.8.2)(supports-color@10.2.2))(@nestjs/websockets@11.2.1)(reflect-metadata@0.2.2)(rxjs@7.8.2) reflect-metadata: 0.2.2 rxjs: 7.8.2 - nestjs-kysely@3.1.2(@nestjs/common@11.1.28(class-transformer@0.5.1)(class-validator@0.15.1)(reflect-metadata@0.2.2)(rxjs@7.8.2)(supports-color@10.2.2))(@nestjs/core@11.1.27)(kysely@0.28.17)(reflect-metadata@0.2.2): + nestjs-kysely@3.1.2(@nestjs/common@11.2.1(class-transformer@0.5.1)(class-validator@0.15.1)(reflect-metadata@0.2.2)(rxjs@7.8.2)(supports-color@10.2.2))(@nestjs/core@11.2.1)(kysely@0.28.17)(reflect-metadata@0.2.2): dependencies: - '@nestjs/common': 11.1.28(class-transformer@0.5.1)(class-validator@0.15.1)(reflect-metadata@0.2.2)(rxjs@7.8.2)(supports-color@10.2.2) - '@nestjs/core': 11.1.27(@nestjs/common@11.1.28(class-transformer@0.5.1)(class-validator@0.15.1)(reflect-metadata@0.2.2)(rxjs@7.8.2)(supports-color@10.2.2))(@nestjs/websockets@11.1.28)(reflect-metadata@0.2.2)(rxjs@7.8.2) + '@nestjs/common': 11.2.1(class-transformer@0.5.1)(class-validator@0.15.1)(reflect-metadata@0.2.2)(rxjs@7.8.2)(supports-color@10.2.2) + '@nestjs/core': 11.2.1(@nestjs/common@11.2.1(class-transformer@0.5.1)(class-validator@0.15.1)(reflect-metadata@0.2.2)(rxjs@7.8.2)(supports-color@10.2.2))(@nestjs/websockets@11.2.1)(reflect-metadata@0.2.2)(rxjs@7.8.2) kysely: 0.28.17 reflect-metadata: 0.2.2 - nestjs-pino@4.6.1(@nestjs/common@11.1.28(class-transformer@0.5.1)(class-validator@0.15.1)(reflect-metadata@0.2.2)(rxjs@7.8.2)(supports-color@10.2.2))(pino-http@11.0.0)(pino@10.1.0)(rxjs@7.8.2): + nestjs-pino@4.6.1(@nestjs/common@11.2.1(class-transformer@0.5.1)(class-validator@0.15.1)(reflect-metadata@0.2.2)(rxjs@7.8.2)(supports-color@10.2.2))(pino-http@11.0.0)(pino@10.1.0)(rxjs@7.8.2): dependencies: - '@nestjs/common': 11.1.28(class-transformer@0.5.1)(class-validator@0.15.1)(reflect-metadata@0.2.2)(rxjs@7.8.2)(supports-color@10.2.2) + '@nestjs/common': 11.2.1(class-transformer@0.5.1)(class-validator@0.15.1)(reflect-metadata@0.2.2)(rxjs@7.8.2)(supports-color@10.2.2) pino: 10.1.0 pino-http: 11.0.0 rxjs: 7.8.2 @@ -18677,9 +18588,9 @@ snapshots: is-inside-container: 1.0.0 is-wsl: 3.1.0 - openai@6.2.0(ws@8.21.0)(zod@4.3.6): + openai@6.2.0(ws@8.21.3)(zod@4.3.6): optionalDependencies: - ws: 8.21.0 + ws: 8.21.3 zod: 4.3.6 optional: true @@ -18777,9 +18688,6 @@ snapshots: json-parse-even-better-errors: 2.3.1 lines-and-columns: 1.2.4 - parse-node-version@1.0.1: - optional: true - parse5-htmlparser2-tree-adapter@7.1.0: dependencies: domhandler: 5.0.3 @@ -18918,9 +18826,6 @@ snapshots: picospinner@3.0.0: {} - pify@4.0.1: - optional: true - pino-abstract-transport@2.0.0: dependencies: split2: 4.2.0 @@ -19045,7 +18950,7 @@ snapshots: postcss@8.5.25: dependencies: - nanoid: 3.3.17 + nanoid: 3.3.18 picocolors: 1.1.1 source-map-js: 1.2.1 @@ -19201,9 +19106,6 @@ snapshots: proxy-from-env@2.1.0: {} - prr@1.0.1: - optional: true - psl@1.15.0: dependencies: punycode: 2.3.1 @@ -19704,9 +19606,6 @@ snapshots: dependencies: parseley: 0.12.1 - semver@5.7.2: - optional: true - semver@6.3.1: {} semver@7.7.4: {} @@ -19825,7 +19724,7 @@ snapshots: socket.io-adapter@2.5.4(supports-color@10.2.2): dependencies: debug: 4.3.7(supports-color@10.2.2) - ws: 8.21.0 + ws: 8.21.3 transitivePeerDependencies: - bufferutil - supports-color @@ -20481,7 +20380,7 @@ snapshots: vary@1.1.2: {} - vite@8.0.16(@types/node@22.19.1)(esbuild@0.28.1)(jiti@2.4.2)(less@4.2.0)(sugarss@5.0.1(postcss@8.5.25))(terser@5.39.0)(tsx@4.21.0)(yaml@2.8.3): + vite@8.0.16(@types/node@22.19.1)(esbuild@0.28.1)(jiti@2.4.2)(sugarss@5.0.1(postcss@8.5.25))(terser@5.39.0)(tsx@4.21.0)(yaml@2.8.3): dependencies: lightningcss: 1.32.0 picomatch: 4.0.4 @@ -20493,16 +20392,15 @@ snapshots: esbuild: 0.28.1 fsevents: 2.3.3 jiti: 2.4.2 - less: 4.2.0 sugarss: 5.0.1(postcss@8.5.25) terser: 5.39.0 tsx: 4.21.0 yaml: 2.8.3 - vitest@4.1.6(@opentelemetry/api@1.9.0)(@types/node@22.19.1)(happy-dom@20.8.9)(jsdom@25.0.0(supports-color@10.2.2))(vite@8.0.16(@types/node@22.19.1)(esbuild@0.28.1)(jiti@2.4.2)(less@4.2.0)(sugarss@5.0.1(postcss@8.5.25))(terser@5.39.0)(tsx@4.21.0)(yaml@2.8.3)): + vitest@4.1.6(@opentelemetry/api@1.9.0)(@types/node@22.19.1)(happy-dom@20.8.9)(jsdom@25.0.0(supports-color@10.2.2))(vite@8.0.16(@types/node@22.19.1)(esbuild@0.28.1)(jiti@2.4.2)(sugarss@5.0.1(postcss@8.5.25))(terser@5.39.0)(tsx@4.21.0)(yaml@2.8.3)): dependencies: '@vitest/expect': 4.1.6 - '@vitest/mocker': 4.1.6(vite@8.0.16(@types/node@22.19.1)(esbuild@0.28.1)(jiti@2.4.2)(less@4.2.0)(sugarss@5.0.1(postcss@8.5.25))(terser@5.39.0)(tsx@4.21.0)(yaml@2.8.3)) + '@vitest/mocker': 4.1.6(vite@8.0.16(@types/node@22.19.1)(esbuild@0.28.1)(jiti@2.4.2)(sugarss@5.0.1(postcss@8.5.25))(terser@5.39.0)(tsx@4.21.0)(yaml@2.8.3)) '@vitest/pretty-format': 4.1.6 '@vitest/runner': 4.1.6 '@vitest/snapshot': 4.1.6 @@ -20519,7 +20417,7 @@ snapshots: tinyexec: 1.1.2 tinyglobby: 0.2.17 tinyrainbow: 3.1.0 - vite: 8.0.16(@types/node@22.19.1)(esbuild@0.28.1)(jiti@2.4.2)(less@4.2.0)(sugarss@5.0.1(postcss@8.5.25))(terser@5.39.0)(tsx@4.21.0)(yaml@2.8.3) + vite: 8.0.16(@types/node@22.19.1)(esbuild@0.28.1)(jiti@2.4.2)(sugarss@5.0.1(postcss@8.5.25))(terser@5.39.0)(tsx@4.21.0)(yaml@2.8.3) why-is-node-running: 2.3.0 optionalDependencies: '@opentelemetry/api': 1.9.0 @@ -20720,7 +20618,7 @@ snapshots: imurmurhash: 0.1.4 signal-exit: 4.1.0 - ws@8.21.0: {} + ws@8.21.3: {} xml-crypto@6.1.2: dependencies: diff --git a/pnpm-workspace.yaml b/pnpm-workspace.yaml index ba6f1af39..817e408d5 100644 --- a/pnpm-workspace.yaml +++ b/pnpm-workspace.yaml @@ -6,12 +6,12 @@ patchedDependencies: overrides: prosemirror-changeset: 2.4.0 glob: 13.0.6 - ws: 8.21.0 + ws: 8.21.3 dompurify: 3.4.13 mermaid: 11.16.1 undici: 7.29.0 tmp: 0.2.7 - nanoid@^3: 3.3.17 + nanoid@^3: 3.3.18 lodash-es: 4.18.1 express-rate-limit: 8.2.2 flatted: 3.4.2 From 8c2c49ea6dfd4c995695e8ba43869e9fd099721f Mon Sep 17 00:00:00 2001 From: Michael Lohr Date: Wed, 19 Aug 2026 22:05:44 +0200 Subject: [PATCH 05/14] fix: handle empty password, missing port, and TLS in Redis URL parsing (#2021) * fix: handle empty password, missing port, and TLS in Redis URL parsing --------- Co-authored-by: Philipinho <16838612+Philipinho@users.noreply.github.com> --- apps/server/src/app.module.ts | 15 +++++++++++++-- .../src/collaboration/collaboration.gateway.ts | 1 + apps/server/src/common/helpers/utils.ts | 16 ++++++++++++---- .../src/integrations/health/redis.health.ts | 5 ++++- .../src/integrations/queue/queue.module.ts | 1 + .../integrations/redis/redis-config.service.ts | 1 + apps/server/src/ws/adapter/ws-redis.adapter.ts | 1 + 7 files changed, 33 insertions(+), 7 deletions(-) diff --git a/apps/server/src/app.module.ts b/apps/server/src/app.module.ts index 4f5411eca..2de94a662 100644 --- a/apps/server/src/app.module.ts +++ b/apps/server/src/app.module.ts @@ -22,7 +22,8 @@ import { TelemetryModule } from './integrations/telemetry/telemetry.module'; import { RedisModule } from '@nestjs-labs/nestjs-ioredis'; import { RedisConfigService } from './integrations/redis/redis-config.service'; import { CacheModule } from '@nestjs/cache-manager'; -import KeyvRedis from '@keyv/redis'; +import KeyvRedis, { defaultReconnectStrategy } from '@keyv/redis'; +import { parseRedisUrl } from './common/helpers'; import { LoggerModule } from './common/logger/logger.module'; import { ClsModule } from 'nestjs-cls'; import { NoopAuditModule } from './integrations/audit/audit.module'; @@ -62,10 +63,20 @@ try { isGlobal: true, useFactory: async (environmentService: EnvironmentService) => { const redisUrl = environmentService.getRedisUrl(); + const { family, tls } = parseRedisUrl(redisUrl); return { ttl: 5 * 1000, - stores: [new KeyvRedis(redisUrl)], + stores: [ + new KeyvRedis({ + url: redisUrl, + socket: { + family, + reconnectStrategy: defaultReconnectStrategy, + ...tls, + }, + }), + ], }; }, inject: [EnvironmentService], diff --git a/apps/server/src/collaboration/collaboration.gateway.ts b/apps/server/src/collaboration/collaboration.gateway.ts index b799ce366..05536664e 100644 --- a/apps/server/src/collaboration/collaboration.gateway.ts +++ b/apps/server/src/collaboration/collaboration.gateway.ts @@ -66,6 +66,7 @@ export class CollaborationGateway { password: this.redisConfig.password, db: this.redisConfig.db, family: this.redisConfig.family, + tls: this.redisConfig.tls, retryStrategy: createRetryStrategy(), }), serverId: `collab-${os?.hostname()}-${nanoid(10)}`, diff --git a/apps/server/src/common/helpers/utils.ts b/apps/server/src/common/helpers/utils.ts index 21475c143..100d55d92 100644 --- a/apps/server/src/common/helpers/utils.ts +++ b/apps/server/src/common/helpers/utils.ts @@ -30,13 +30,14 @@ export type RedisConfig = { db: number; password?: string; family?: number; + tls?: { rejectUnauthorized?: boolean }; }; export function parseRedisUrl(redisUrl: string): RedisConfig { - // format - redis[s]://[[username][:password]@][host][:port][/db-number][?family=4|6] + // format - redis[s]://[[username][:password]@][host][:port][/db-number][?family=4|6][&rejectUnauthorized=false] const url = new URL(redisUrl); - const { hostname, port, password, pathname, searchParams } = url; - const portInt = parseInt(port, 10); + const { hostname, port, password, pathname, protocol, searchParams } = url; + const portInt = port ? parseInt(port, 10) : 6379; let db: number = 0; // extract db value if present @@ -54,7 +55,14 @@ export function parseRedisUrl(redisUrl: string): RedisConfig { family = parseInt(familyParam, 10); } - return { host: hostname, port: portInt, password, db, family }; + const tls = + protocol === 'rediss:' + ? searchParams.get('rejectUnauthorized') === 'false' + ? { rejectUnauthorized: false } + : {} + : undefined; + + return { host: hostname, port: portInt, password: password || undefined, db, family, tls }; } export function createRetryStrategy() { diff --git a/apps/server/src/integrations/health/redis.health.ts b/apps/server/src/integrations/health/redis.health.ts index 96ed53b6b..1c9f43611 100644 --- a/apps/server/src/integrations/health/redis.health.ts +++ b/apps/server/src/integrations/health/redis.health.ts @@ -5,6 +5,7 @@ import { import { Injectable, Logger } from '@nestjs/common'; import { EnvironmentService } from '../environment/environment.service'; import { Redis } from 'ioredis'; +import { parseRedisUrl } from '../../common/helpers'; @Injectable() export class RedisHealthIndicator { @@ -19,8 +20,10 @@ export class RedisHealthIndicator { const indicator = this.healthIndicatorService.check(key); try { - const redis = new Redis(this.environmentService.getRedisUrl(), { + const redisUrl = this.environmentService.getRedisUrl(); + const redis = new Redis(redisUrl, { maxRetriesPerRequest: 15, + tls: parseRedisUrl(redisUrl).tls, }); await redis.ping(); diff --git a/apps/server/src/integrations/queue/queue.module.ts b/apps/server/src/integrations/queue/queue.module.ts index eeb74cbb3..fcb317dbf 100644 --- a/apps/server/src/integrations/queue/queue.module.ts +++ b/apps/server/src/integrations/queue/queue.module.ts @@ -18,6 +18,7 @@ import { GeneralQueueProcessor } from './processors/general-queue.processor'; password: redisConfig.password, db: redisConfig.db, family: redisConfig.family, + tls: redisConfig.tls, retryStrategy: createRetryStrategy(), }, defaultJobOptions: { diff --git a/apps/server/src/integrations/redis/redis-config.service.ts b/apps/server/src/integrations/redis/redis-config.service.ts index 719f89f12..7f3e90174 100644 --- a/apps/server/src/integrations/redis/redis-config.service.ts +++ b/apps/server/src/integrations/redis/redis-config.service.ts @@ -19,6 +19,7 @@ export class RedisConfigService implements RedisOptionsFactory { password: redisConfig.password, db: redisConfig.db, family: redisConfig.family, + tls: redisConfig.tls, retryStrategy: createRetryStrategy(), }, }; diff --git a/apps/server/src/ws/adapter/ws-redis.adapter.ts b/apps/server/src/ws/adapter/ws-redis.adapter.ts index a221c84a3..ee1be320c 100644 --- a/apps/server/src/ws/adapter/ws-redis.adapter.ts +++ b/apps/server/src/ws/adapter/ws-redis.adapter.ts @@ -17,6 +17,7 @@ export class WsRedisIoAdapter extends IoAdapter { const options: RedisOptions = { family: this.redisConfig.family, + tls: this.redisConfig.tls, retryStrategy: createRetryStrategy(), }; From ab43031375c05a2142480dacfddc049eeaf751b4 Mon Sep 17 00:00:00 2001 From: Philipinho <16838612+Philipinho@users.noreply.github.com> Date: Wed, 19 Aug 2026 21:46:01 +0100 Subject: [PATCH 06/14] fix race issue --- .../space/services/space-member.service.ts | 32 +++++++++++++------ .../database/repos/space/space-member.repo.ts | 13 ++++++-- 2 files changed, 33 insertions(+), 12 deletions(-) diff --git a/apps/server/src/core/space/services/space-member.service.ts b/apps/server/src/core/space/services/space-member.service.ts index bc280f252..e9b836f1f 100644 --- a/apps/server/src/core/space/services/space-member.service.ts +++ b/apps/server/src/core/space/services/space-member.service.ts @@ -339,15 +339,25 @@ export class SpaceMemberService { return; } - if (spaceMember.role === SpaceRole.ADMIN) { - await this.validateLastAdmin(dto.spaceId); - } + await executeTx(this.db, async (trx) => { + await trx + .selectFrom('spaces') + .select('id') + .where('id', '=', dto.spaceId) + .forUpdate() + .executeTakeFirst(); - await this.spaceMemberRepo.updateSpaceMember( - { role: dto.role }, - spaceMember.id, - dto.spaceId, - ); + if (spaceMember.role === SpaceRole.ADMIN) { + await this.validateLastAdmin(dto.spaceId, trx); + } + + await this.spaceMemberRepo.updateSpaceMember( + { role: dto.role }, + spaceMember.id, + dto.spaceId, + trx, + ); + }); this.auditService.log({ event: AuditEvent.SPACE_MEMBER_ROLE_CHANGED, @@ -368,10 +378,14 @@ export class SpaceMemberService { }); } - async validateLastAdmin(spaceId: string): Promise { + async validateLastAdmin( + spaceId: string, + trx?: KyselyTransaction, + ): Promise { const spaceOwnerCount = await this.spaceMemberRepo.roleCountBySpaceId( SpaceRole.ADMIN, spaceId, + trx, ); if (spaceOwnerCount === 1) { throw new BadRequestException( diff --git a/apps/server/src/database/repos/space/space-member.repo.ts b/apps/server/src/database/repos/space/space-member.repo.ts index 6711c30c7..d6172e282 100644 --- a/apps/server/src/database/repos/space/space-member.repo.ts +++ b/apps/server/src/database/repos/space/space-member.repo.ts @@ -46,8 +46,10 @@ export class SpaceMemberRepo { updatableSpaceMember: UpdatableSpaceMember, spaceMemberId: string, spaceId: string, + trx?: KyselyTransaction, ): Promise { - await this.db + const db = dbOrTx(this.db, trx); + await db .updateTable('spaceMembers') .set(updatableSpaceMember) .where('id', '=', spaceMemberId) @@ -92,8 +94,13 @@ export class SpaceMemberRepo { .execute(); } - async roleCountBySpaceId(role: string, spaceId: string): Promise { - const { count } = await this.db + async roleCountBySpaceId( + role: string, + spaceId: string, + trx?: KyselyTransaction, + ): Promise { + const db = dbOrTx(this.db, trx); + const { count } = await db .selectFrom('spaceMembers') .select((eb) => eb.fn.count('role').as('count')) .where('role', '=', role) From 66b424a3b88397b11a0c8bcb3cde6e2e6b681634 Mon Sep 17 00:00:00 2001 From: Philipinho <16838612+Philipinho@users.noreply.github.com> Date: Thu, 20 Aug 2026 22:25:45 +0100 Subject: [PATCH 07/14] fix: preserve hash in vimeo embed url --- packages/editor-ext/src/lib/embed-provider.ts | 10 +++++++--- 1 file changed, 7 insertions(+), 3 deletions(-) diff --git a/packages/editor-ext/src/lib/embed-provider.ts b/packages/editor-ext/src/lib/embed-provider.ts index 4c286a8b8..4c775532a 100644 --- a/packages/editor-ext/src/lib/embed-provider.ts +++ b/packages/editor-ext/src/lib/embed-provider.ts @@ -73,9 +73,13 @@ export const embedProviders: IEmbedProvider[] = [ id: "vimeo", name: "Vimeo", regex: - /^(https:)?\/\/(?:www\.|player\.)?vimeo.com\/(?:channels\/(?:\w+\/)?|groups\/([^/]*)\/videos\/|album\/(\d+)\/video\/|video\/|)(\d+)/, - getEmbedUrl: (match) => { - return `https://player.vimeo.com/video/${match[4]}`; + /^(https:)?\/\/(?:www\.|player\.)?vimeo.com\/(?:channels\/(?:\w+\/)?|groups\/([^/]*)\/videos\/|album\/(\d+)\/video\/|video\/|)(\d+)(?:\/([\da-zA-Z]+))?/, + getEmbedUrl: (match, url: string) => { + // preserve ?h= hash for unlisted videos + const hash = + match[5] ?? new URL(url, "https://vimeo.com").searchParams.get("h"); + const base = `https://player.vimeo.com/video/${match[4]}`; + return hash ? `${base}?h=${hash}` : base; }, }, { From 3b858746e3144e3ce06263ab165fe864dae5b513 Mon Sep 17 00:00:00 2001 From: Salihu Date: Fri, 21 Aug 2026 19:21:14 +0100 Subject: [PATCH 08/14] fix: checkbox filtering --- apps/server/src/ee | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/apps/server/src/ee b/apps/server/src/ee index e13af0ce0..6dfbcb924 160000 --- a/apps/server/src/ee +++ b/apps/server/src/ee @@ -1 +1 @@ -Subproject commit e13af0ce055cdc83962533f0a7865ad60a11e55e +Subproject commit 6dfbcb92412008e612c89800125d27c58da2bae5 From b86abd3d40b8767b36e66dc8a0486b0246653328 Mon Sep 17 00:00:00 2001 From: Salihu Date: Fri, 21 Aug 2026 19:23:48 +0100 Subject: [PATCH 09/14] Revert "fix: checkbox filtering" This reverts commit 3b858746e3144e3ce06263ab165fe864dae5b513. --- apps/server/src/ee | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/apps/server/src/ee b/apps/server/src/ee index 6dfbcb924..e13af0ce0 160000 --- a/apps/server/src/ee +++ b/apps/server/src/ee @@ -1 +1 @@ -Subproject commit 6dfbcb92412008e612c89800125d27c58da2bae5 +Subproject commit e13af0ce055cdc83962533f0a7865ad60a11e55e From b814bd0f12df5912a3d8b62eb57e92a3aa9b2fc5 Mon Sep 17 00:00:00 2001 From: Salihu Date: Fri, 21 Aug 2026 19:29:10 +0100 Subject: [PATCH 10/14] fix: checkbox filtering --- apps/server/src/ee | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/apps/server/src/ee b/apps/server/src/ee index e13af0ce0..6dfbcb924 160000 --- a/apps/server/src/ee +++ b/apps/server/src/ee @@ -1 +1 @@ -Subproject commit e13af0ce055cdc83962533f0a7865ad60a11e55e +Subproject commit 6dfbcb92412008e612c89800125d27c58da2bae5 From e14f499f3d8c9ad69e096b8ca20e50b98c46478f Mon Sep 17 00:00:00 2001 From: Philip Okugbe <16838612+Philipinho@users.noreply.github.com> Date: Sat, 22 Aug 2026 03:46:00 +0100 Subject: [PATCH 11/14] fix: pass tls to redis in throttle module (#2415) --- apps/server/src/integrations/throttle/throttle.module.ts | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/apps/server/src/integrations/throttle/throttle.module.ts b/apps/server/src/integrations/throttle/throttle.module.ts index 42dd0ec4a..827f671c1 100644 --- a/apps/server/src/integrations/throttle/throttle.module.ts +++ b/apps/server/src/integrations/throttle/throttle.module.ts @@ -3,7 +3,7 @@ import { ThrottlerModule } from '@nestjs/throttler'; import { ThrottlerStorageRedisService } from '@nest-lab/throttler-storage-redis'; import { EnvironmentService } from '../environment/environment.service'; import { EnvironmentModule } from '../environment/environment.module'; -import { parseRedisUrl } from '../../common/helpers'; +import { createRetryStrategy, parseRedisUrl } from '../../common/helpers'; import { AUTH_THROTTLER, AI_CHAT_THROTTLER } from './throttler-names'; import Redis from 'ioredis'; @@ -27,6 +27,8 @@ import Redis from 'ioredis'; password: redisConfig.password, db: redisConfig.db, family: redisConfig.family, + tls: redisConfig.tls, + retryStrategy: createRetryStrategy(), keyPrefix: 'throttle:', }), ), From 549cf7c0053bb4f4c3c4e08d588b1f0c69297daf Mon Sep 17 00:00:00 2001 From: Philip Okugbe <16838612+Philipinho@users.noreply.github.com> Date: Sat, 22 Aug 2026 13:20:35 +0100 Subject: [PATCH 12/14] fix: skip pgvector table check when enabling AI search on turbopuffer (#2416) --- apps/server/src/core/workspace/services/workspace.service.ts | 5 ++++- 1 file changed, 4 insertions(+), 1 deletion(-) diff --git a/apps/server/src/core/workspace/services/workspace.service.ts b/apps/server/src/core/workspace/services/workspace.service.ts index 77ccb90c9..68dfd271a 100644 --- a/apps/server/src/core/workspace/services/workspace.service.ts +++ b/apps/server/src/core/workspace/services/workspace.service.ts @@ -396,7 +396,10 @@ export class WorkspaceService { } } - if (updateWorkspaceDto.aiSearch) { + if ( + updateWorkspaceDto.aiSearch && + this.environmentService.getAiVectorDriver() !== 'turbopuffer' + ) { const tableExists = await isPageEmbeddingsTableExists(this.db); if (!tableExists) { throw new BadRequestException( From b7f0b063dc576015482e7aed20357ee014626afa Mon Sep 17 00:00:00 2001 From: Philip Okugbe <16838612+Philipinho@users.noreply.github.com> Date: Mon, 24 Aug 2026 20:29:21 +0100 Subject: [PATCH 13/14] feat(ee): add enterprise ai chat controls (#2421) * feat(ee): add enterprise ai chat controls * feat: add comment listing tool to ai chat * fix: scope comment endpoints to workspace and skip trashed pages --- .../public/locales/de-DE/translation.json | 2 +- .../public/locales/en-US/translation.json | 10 ++- .../public/locales/es-ES/translation.json | 2 +- .../public/locales/fr-FR/translation.json | 2 +- .../public/locales/it-IT/translation.json | 2 +- .../public/locales/ja-JP/translation.json | 2 +- .../public/locales/ko-KR/translation.json | 2 +- .../public/locales/nl-NL/translation.json | 2 +- .../public/locales/pt-BR/translation.json | 2 +- .../public/locales/ru-RU/translation.json | 2 +- .../public/locales/uk-UA/translation.json | 2 +- .../public/locales/zh-CN/translation.json | 2 +- .../ai-chat/components/ai-chat-read-only.tsx | 74 +++++++++++++++++++ .../ai-chat-workspace-knowledge-only.tsx | 74 +++++++++++++++++++ .../ai-chat/components/chat-empty-state.tsx | 17 ++++- .../src/ee/ai-chat/components/chat-input.tsx | 35 ++++++++- apps/client/src/ee/ai/pages/ai-settings.tsx | 24 +++++- apps/client/src/ee/features.ts | 1 + .../workspace/types/workspace.types.ts | 4 + apps/server/src/common/features.ts | 1 + .../src/core/comment/comment.controller.ts | 17 +++-- .../workspace/dto/update-workspace.dto.ts | 8 ++ .../workspace/services/workspace.service.ts | 49 +++++++++++- apps/server/src/ee | 2 +- 24 files changed, 308 insertions(+), 30 deletions(-) create mode 100644 apps/client/src/ee/ai-chat/components/ai-chat-read-only.tsx create mode 100644 apps/client/src/ee/ai-chat/components/ai-chat-workspace-knowledge-only.tsx diff --git a/apps/client/public/locales/de-DE/translation.json b/apps/client/public/locales/de-DE/translation.json index f067ecc27..084c92ef5 100644 --- a/apps/client/public/locales/de-DE/translation.json +++ b/apps/client/public/locales/de-DE/translation.json @@ -696,7 +696,7 @@ "Upgrade your plan": "Upgrade Ihres Plans", "Available with a paid license": "Verfügbar mit einer kostenpflichtigen Lizenz", "Upgrade your license tier.": "Stufen Sie Ihre Lizenz hoch.", - "AI is only available in the Docmost enterprise edition. Contact sales@docmost.com.": "KI ist nur in der Docmost Enterprise-Edition verfügbar. Kontaktieren Sie sales@docmost.com.", + "AI is available in the Docmost paid editions. Contact sales@docmost.com.": "KI ist in den kostenpflichtigen Docmost-Editionen verfügbar. Kontaktieren Sie sales@docmost.com.", "AI & MCP": "KI & MCP", "AI": "KI", "MCP": "MCP", diff --git a/apps/client/public/locales/en-US/translation.json b/apps/client/public/locales/en-US/translation.json index 03e10c53e..e5768c7e4 100644 --- a/apps/client/public/locales/en-US/translation.json +++ b/apps/client/public/locales/en-US/translation.json @@ -698,7 +698,7 @@ "Upgrade your plan": "Upgrade your plan", "Available with a paid license": "Available with a paid license", "Upgrade your license tier.": "Upgrade your license tier.", - "AI is only available in the Docmost enterprise edition. Contact sales@docmost.com.": "AI is only available in the Docmost enterprise edition. Contact sales@docmost.com.", + "AI is available in the Docmost paid editions. Contact sales@docmost.com.": "AI is available in the Docmost paid editions. Contact sales@docmost.com.", "AI & MCP": "AI & MCP", "AI": "AI", "MCP": "MCP", @@ -1302,5 +1302,11 @@ "Error loading attachments.": "Error loading attachments.", "No attachments on this page yet.": "No attachments on this page yet.", "Uploaded by {{name}}": "Uploaded by {{name}}", - "Download {{name}}": "Download {{name}}" + "Download {{name}}": "Download {{name}}", + "Workspace knowledge only": "Workspace knowledge only", + "Restrict AI Chat to answering from your workspace pages and uploaded files only. It will not use outside knowledge.": "Restrict AI Chat to answering from your workspace pages and uploaded files only. It will not use outside knowledge.", + "Toggle workspace knowledge only": "Toggle workspace knowledge only", + "Read-only mode": "Read-only mode", + "AI Chat can search and read workspace content, but cannot create or edit pages.": "AI Chat can search and read workspace content, but cannot create or edit pages.", + "Toggle AI Chat read-only mode": "Toggle AI Chat read-only mode" } diff --git a/apps/client/public/locales/es-ES/translation.json b/apps/client/public/locales/es-ES/translation.json index fd834a2c6..fb5364725 100644 --- a/apps/client/public/locales/es-ES/translation.json +++ b/apps/client/public/locales/es-ES/translation.json @@ -696,7 +696,7 @@ "Upgrade your plan": "Mejora tu plan", "Available with a paid license": "Disponible con una licencia de pago", "Upgrade your license tier.": "Mejora el nivel de tu licencia.", - "AI is only available in the Docmost enterprise edition. Contact sales@docmost.com.": "La IA solo está disponible en la edición empresarial de Docmost. Contacte con sales@docmost.com.", + "AI is available in the Docmost paid editions. Contact sales@docmost.com.": "La IA está disponible en las ediciones de pago de Docmost. Contacte con sales@docmost.com.", "AI & MCP": "IA y MCP", "AI": "IA", "MCP": "MCP", diff --git a/apps/client/public/locales/fr-FR/translation.json b/apps/client/public/locales/fr-FR/translation.json index 04ebbc6ad..e9fbeed06 100644 --- a/apps/client/public/locales/fr-FR/translation.json +++ b/apps/client/public/locales/fr-FR/translation.json @@ -696,7 +696,7 @@ "Upgrade your plan": "Mettez à niveau votre forfait", "Available with a paid license": "Disponible avec une licence payante", "Upgrade your license tier.": "Mettez à niveau votre niveau de licence.", - "AI is only available in the Docmost enterprise edition. Contact sales@docmost.com.": "L'IA n'est disponible que dans l'édition Entreprise de Docmost. Contactez sales@docmost.com.", + "AI is available in the Docmost paid editions. Contact sales@docmost.com.": "L'IA est disponible dans les éditions payantes de Docmost. Contactez sales@docmost.com.", "AI & MCP": "IA & MCP", "AI": "IA", "MCP": "MCP", diff --git a/apps/client/public/locales/it-IT/translation.json b/apps/client/public/locales/it-IT/translation.json index e3aaa82ce..81eb1727f 100644 --- a/apps/client/public/locales/it-IT/translation.json +++ b/apps/client/public/locales/it-IT/translation.json @@ -696,7 +696,7 @@ "Upgrade your plan": "Aggiorna il tuo piano", "Available with a paid license": "Disponibile con una licenza a pagamento", "Upgrade your license tier.": "Aggiorna il livello della tua licenza.", - "AI is only available in the Docmost enterprise edition. Contact sales@docmost.com.": "L'IA è disponibile solo nell'edizione Enterprise di Docmost. Contatta sales@docmost.com.", + "AI is available in the Docmost paid editions. Contact sales@docmost.com.": "L'IA è disponibile nelle edizioni a pagamento di Docmost. Contatta sales@docmost.com.", "AI & MCP": "IA e MCP", "AI": "IA", "MCP": "MCP", diff --git a/apps/client/public/locales/ja-JP/translation.json b/apps/client/public/locales/ja-JP/translation.json index e408bfda8..c1bfa1652 100644 --- a/apps/client/public/locales/ja-JP/translation.json +++ b/apps/client/public/locales/ja-JP/translation.json @@ -696,7 +696,7 @@ "Upgrade your plan": "プランをアップグレードする", "Available with a paid license": "有料ライセンスで利用可能", "Upgrade your license tier.": "ライセンスタイアをアップグレードしてください。", - "AI is only available in the Docmost enterprise edition. Contact sales@docmost.com.": "AI は Docmost のエンタープライズ版でのみ利用可能です。sales@docmost.com までお問い合わせください。", + "AI is available in the Docmost paid editions. Contact sales@docmost.com.": "AI は Docmost の有料版で利用可能です。sales@docmost.com までお問い合わせください。", "AI & MCP": "AI と MCP", "AI": "AI", "MCP": "MCP", diff --git a/apps/client/public/locales/ko-KR/translation.json b/apps/client/public/locales/ko-KR/translation.json index fb8e9884a..a7d795454 100644 --- a/apps/client/public/locales/ko-KR/translation.json +++ b/apps/client/public/locales/ko-KR/translation.json @@ -696,7 +696,7 @@ "Upgrade your plan": "요금제를 업그레이드하세요", "Available with a paid license": "유료 라이선스에서만 사용 가능합니다", "Upgrade your license tier.": "라이선스 등급을 업그레이드하세요.", - "AI is only available in the Docmost enterprise edition. Contact sales@docmost.com.": "AI는 Docmost 엔터프라이즈 에디션에서만 제공됩니다. sales@docmost.com으로 문의하세요.", + "AI is available in the Docmost paid editions. Contact sales@docmost.com.": "AI는 Docmost 유료 에디션에서 제공됩니다. sales@docmost.com으로 문의하세요.", "AI & MCP": "AI 및 MCP", "AI": "AI", "MCP": "MCP", diff --git a/apps/client/public/locales/nl-NL/translation.json b/apps/client/public/locales/nl-NL/translation.json index 59c1d7a73..1f1465ab1 100644 --- a/apps/client/public/locales/nl-NL/translation.json +++ b/apps/client/public/locales/nl-NL/translation.json @@ -696,7 +696,7 @@ "Upgrade your plan": "Upgrade je abonnement", "Available with a paid license": "Beschikbaar met een betaalde licentie", "Upgrade your license tier.": "Upgrade je licentieniveau.", - "AI is only available in the Docmost enterprise edition. Contact sales@docmost.com.": "AI is alleen beschikbaar in de Docmost Enterprise-editie. Neem contact op met sales@docmost.com.", + "AI is available in the Docmost paid editions. Contact sales@docmost.com.": "AI is beschikbaar in de betaalde edities van Docmost. Neem contact op met sales@docmost.com.", "AI & MCP": "AI & MCP", "AI": "AI", "MCP": "MCP", diff --git a/apps/client/public/locales/pt-BR/translation.json b/apps/client/public/locales/pt-BR/translation.json index 2e4cd89ef..82f213703 100644 --- a/apps/client/public/locales/pt-BR/translation.json +++ b/apps/client/public/locales/pt-BR/translation.json @@ -696,7 +696,7 @@ "Upgrade your plan": "Faça upgrade do seu plano", "Available with a paid license": "Disponível com uma licença paga", "Upgrade your license tier.": "Faça upgrade do seu nível de licença.", - "AI is only available in the Docmost enterprise edition. Contact sales@docmost.com.": "A IA está disponível apenas na edição empresarial do Docmost. Contate sales@docmost.com.", + "AI is available in the Docmost paid editions. Contact sales@docmost.com.": "A IA está disponível nas edições pagas do Docmost. Contate sales@docmost.com.", "AI & MCP": "IA e MCP", "AI": "IA", "MCP": "MCP", diff --git a/apps/client/public/locales/ru-RU/translation.json b/apps/client/public/locales/ru-RU/translation.json index bae44d133..9d4ddc8a6 100644 --- a/apps/client/public/locales/ru-RU/translation.json +++ b/apps/client/public/locales/ru-RU/translation.json @@ -696,7 +696,7 @@ "Upgrade your plan": "Обновите свой тарифный план", "Available with a paid license": "Доступно с платной лицензией", "Upgrade your license tier.": "Обновите уровень вашей лицензии.", - "AI is only available in the Docmost enterprise edition. Contact sales@docmost.com.": "ИИ доступен только в корпоративной версии Docmost. Свяжитесь по адресу sales@docmost.com.", + "AI is available in the Docmost paid editions. Contact sales@docmost.com.": "ИИ доступен в платных версиях Docmost. Свяжитесь по адресу sales@docmost.com.", "AI & MCP": "ИИ и MCP", "AI": "ИИ", "MCP": "MCP", diff --git a/apps/client/public/locales/uk-UA/translation.json b/apps/client/public/locales/uk-UA/translation.json index d3dd687ea..be09a646f 100644 --- a/apps/client/public/locales/uk-UA/translation.json +++ b/apps/client/public/locales/uk-UA/translation.json @@ -696,7 +696,7 @@ "Upgrade your plan": "Оновіть свій тарифний план", "Available with a paid license": "Доступно за платною ліцензією", "Upgrade your license tier.": "Оновіть рівень своєї ліцензії.", - "AI is only available in the Docmost enterprise edition. Contact sales@docmost.com.": "ШІ доступний лише в корпоративній редакції Docmost. Зверніться до sales@docmost.com.", + "AI is available in the Docmost paid editions. Contact sales@docmost.com.": "ШІ доступний у платних редакціях Docmost. Зверніться до sales@docmost.com.", "AI & MCP": "ШІ та MCP", "AI": "ШІ", "MCP": "MCP", diff --git a/apps/client/public/locales/zh-CN/translation.json b/apps/client/public/locales/zh-CN/translation.json index 97ff9ce85..1b273e71c 100644 --- a/apps/client/public/locales/zh-CN/translation.json +++ b/apps/client/public/locales/zh-CN/translation.json @@ -696,7 +696,7 @@ "Upgrade your plan": "升级您的方案", "Available with a paid license": "需付费许可才可用", "Upgrade your license tier.": "升级您的许可等级。", - "AI is only available in the Docmost enterprise edition. Contact sales@docmost.com.": "AI 仅在 Docmost 企业版中提供。请联系 sales@docmost.com。", + "AI is available in the Docmost paid editions. Contact sales@docmost.com.": "AI 在 Docmost 付费版中提供。请联系 sales@docmost.com。", "AI & MCP": "AI 与 MCP", "AI": "AI", "MCP": "MCP", diff --git a/apps/client/src/ee/ai-chat/components/ai-chat-read-only.tsx b/apps/client/src/ee/ai-chat/components/ai-chat-read-only.tsx new file mode 100644 index 000000000..732800299 --- /dev/null +++ b/apps/client/src/ee/ai-chat/components/ai-chat-read-only.tsx @@ -0,0 +1,74 @@ +import { Badge, Group, Text, Switch, Tooltip } from "@mantine/core"; +import { useAtom } from "jotai"; +import { workspaceAtom } from "@/features/user/atoms/current-user-atom.ts"; +import { useState } from "react"; +import { useTranslation } from "react-i18next"; +import { updateWorkspace } from "@/features/workspace/services/workspace-service.ts"; +import { notifications } from "@mantine/notifications"; +import { useHasFeature } from "@/ee/hooks/use-feature"; +import { Feature } from "@/ee/features"; +import { useUpgradeLabel } from "@/ee/hooks/use-upgrade-label"; + +export default function AiChatReadOnly() { + const { t } = useTranslation(); + const hasAccess = useHasFeature(Feature.AI_CONTROLS); + + return ( + +
+ + {t("Read-only mode")} + {!hasAccess && ( + + {t("Enterprise")} + + )} + + + {t( + "AI Chat can search and read workspace content, but cannot create or edit pages.", + )} + +
+ + +
+ ); +} + +function AiChatReadOnlyToggle() { + const { t } = useTranslation(); + const [workspace, setWorkspace] = useAtom(workspaceAtom); + const [checked, setChecked] = useState( + workspace?.settings?.ai?.chatReadOnly, + ); + const hasAccess = useHasFeature(Feature.AI_CONTROLS); + const upgradeLabel = useUpgradeLabel(); + + const handleChange = async (event: React.ChangeEvent) => { + const value = event.currentTarget.checked; + try { + const updatedWorkspace = await updateWorkspace({ + aiChatReadOnly: value, + }); + setChecked(value); + setWorkspace(updatedWorkspace); + } catch (err: any) { + notifications.show({ + message: err?.response?.data?.message, + color: "red", + }); + } + }; + + return ( + + + + ); +} diff --git a/apps/client/src/ee/ai-chat/components/ai-chat-workspace-knowledge-only.tsx b/apps/client/src/ee/ai-chat/components/ai-chat-workspace-knowledge-only.tsx new file mode 100644 index 000000000..497a337b4 --- /dev/null +++ b/apps/client/src/ee/ai-chat/components/ai-chat-workspace-knowledge-only.tsx @@ -0,0 +1,74 @@ +import { Badge, Group, Text, Switch, Tooltip } from "@mantine/core"; +import { useAtom } from "jotai"; +import { workspaceAtom } from "@/features/user/atoms/current-user-atom.ts"; +import { useState } from "react"; +import { useTranslation } from "react-i18next"; +import { updateWorkspace } from "@/features/workspace/services/workspace-service.ts"; +import { notifications } from "@mantine/notifications"; +import { useHasFeature } from "@/ee/hooks/use-feature"; +import { Feature } from "@/ee/features"; +import { useUpgradeLabel } from "@/ee/hooks/use-upgrade-label"; + +export default function AiChatWorkspaceKnowledgeOnly() { + const { t } = useTranslation(); + const hasAccess = useHasFeature(Feature.AI_CONTROLS); + + return ( + +
+ + {t("Workspace knowledge only")} + {!hasAccess && ( + + {t("Enterprise")} + + )} + + + {t( + "Restrict AI Chat to answering from your workspace pages and uploaded files only. It will not use outside knowledge.", + )} + +
+ + +
+ ); +} + +function AiChatWorkspaceKnowledgeOnlyToggle() { + const { t } = useTranslation(); + const [workspace, setWorkspace] = useAtom(workspaceAtom); + const [checked, setChecked] = useState( + workspace?.settings?.ai?.chatWorkspaceKnowledgeOnly, + ); + const hasAccess = useHasFeature(Feature.AI_CONTROLS); + const upgradeLabel = useUpgradeLabel(); + + const handleChange = async (event: React.ChangeEvent) => { + const value = event.currentTarget.checked; + try { + const updatedWorkspace = await updateWorkspace({ + aiChatWorkspaceKnowledgeOnly: value, + }); + setChecked(value); + setWorkspace(updatedWorkspace); + } catch (err: any) { + notifications.show({ + message: err?.response?.data?.message, + color: "red", + }); + } + }; + + return ( + + + + ); +} diff --git a/apps/client/src/ee/ai-chat/components/chat-empty-state.tsx b/apps/client/src/ee/ai-chat/components/chat-empty-state.tsx index 9583a2f03..423557c53 100644 --- a/apps/client/src/ee/ai-chat/components/chat-empty-state.tsx +++ b/apps/client/src/ee/ai-chat/components/chat-empty-state.tsx @@ -6,7 +6,10 @@ import { IconFileText, } from "@tabler/icons-react"; import { useTranslation } from "react-i18next"; -import ChatInput from "./chat-input"; +import { useAtomValue } from "jotai"; +import { workspaceAtom } from "@/features/user/atoms/current-user-atom.ts"; +import { useRef } from "react"; +import ChatInput, { ChatInputHandle } from "./chat-input"; import type { ChatAttachment, PageMention } from "../types/ai-chat.types"; import classes from "../styles/ai-chat.module.css"; @@ -14,6 +17,7 @@ type Suggestion = { icon: React.ReactNode; text: string; prompt: string; + write?: boolean; }; const SUGGESTIONS: Suggestion[] = [ @@ -26,6 +30,7 @@ const SUGGESTIONS: Suggestion[] = [ icon: , text: "Create a new page", prompt: "Create a new page titled ", + write: true, }, { icon: , @@ -36,6 +41,7 @@ const SUGGESTIONS: Suggestion[] = [ icon: , text: "Update page content", prompt: "Update the page @", + write: true, }, ]; @@ -47,9 +53,13 @@ type Props = { export default function ChatEmptyState({ isStreaming, onSend, onStop }: Props) { const { t } = useTranslation(); + const workspace = useAtomValue(workspaceAtom); + const writesDisabled = workspace?.settings?.ai?.chatReadOnly === true; + + const inputRef = useRef(null); const handleSuggestionClick = (prompt: string) => { - onSend(prompt, [], []); + inputRef.current?.prefill(prompt); }; return ( @@ -62,6 +72,7 @@ export default function ChatEmptyState({ isStreaming, onSend, onStop }: Props) {

{t("Get started")}

- {SUGGESTIONS.map((s) => ( + {SUGGESTIONS.filter((s) => !writesDisabled || !s.write).map((s) => (