diff --git a/cloudinary/src/getGenerateSignature.ts b/cloudinary/src/getGenerateSignature.ts index 1e9a8c3c..72975763 100644 --- a/cloudinary/src/getGenerateSignature.ts +++ b/cloudinary/src/getGenerateSignature.ts @@ -46,8 +46,12 @@ const allowedParams = new Set(['folder', 'public_id', 'timestamp']) const normalizeFolder = (value: string): string => { let start = 0 let end = value.length - while (start < end && value[start] === '/') start++ - while (end > start && value[end - 1] === '/') end-- + while (start < end && value[start] === '/') { + start++ + } + while (end > start && value[end - 1] === '/') { + end-- + } return value.slice(start, end) } diff --git a/content-translator/CHANGELOG.md b/content-translator/CHANGELOG.md index 4a0add98..fba0d7b8 100644 --- a/content-translator/CHANGELOG.md +++ b/content-translator/CHANGELOG.md @@ -2,6 +2,7 @@ ## Unreleased +- feat: add incremental richText translation ("Translate new & changed content") that translates only new or changed paragraphs, preserves existing translations and manual edits, and reports how many paragraphs need review when a source paragraph changed under a hand-edited translation - fix: translate rich text block-level elements as one unit using segment markers so inline formatting spans stay aligned and word order can change across languages - fix: reconstruct OpenAI translations by input index so a merged, dropped, or reordered entry no longer shifts later translations into the wrong fields; missing entries keep their original text - fix: abort a translation when the resolver returns a different number of texts than were sent, and guard against non-string values reaching `he.decode` diff --git a/content-translator/README.md b/content-translator/README.md index f42dbca5..f3307a45 100644 --- a/content-translator/README.md +++ b/content-translator/README.md @@ -42,6 +42,31 @@ export default buildConfig({ }) ``` +## Translation modes + +The translator modal offers three actions: + +- **Translate all fields** — retranslates every field, discarding existing target content. +- **Translate new & changed content** — incremental mode (see below). +- **Translate only empty fields** — fills target fields that have no value yet, leaving the rest untouched. + +### Incremental mode + +Incremental mode translates only what actually changed and preserves existing translations, which matters most for `richText`. For a lexical field it diffs the source against the existing translation at the **paragraph / block level**: + +- a paragraph whose source text is unchanged keeps its current translation (including any manual edits) and is not retranslated; +- a new or edited source paragraph is translated and placed in source order, so inserts and reorders land in the right position; +- a paragraph removed from the source is removed from the translation; +- if a source paragraph changed **and** its translation had been hand-edited, the human's version is left in place and counted — the success toast reports how many paragraphs need review, so machine accuracy never silently overwrites manual work. + +**Limitation:** change detection currently applies to lexical `richText` only. Every other field type (`text`, `textarea`, `number`, `array`, `blocks`, non-lexical `richText`) behaves like "translate only empty fields" in incremental mode — an empty target is filled, but a field whose source changed _after_ it was already translated is **not** retranslated. Detecting edits on those would require storing a source hash per field (plain fields have no inline NodeState slot like lexical nodes do). + +Paragraph identity is content-addressed: a hash of the source text and a hash of the machine output are stored inline on the translated node using Lexical's [NodeState](https://lexical.dev/docs/concepts/node-state) slot (`$`), under a single namespaced key — `"$": { "translator-plugin": { "srcHash": { "": … }, "outHash": … } }`. These pass through Payload saves and admin-editor edits untouched (covered by a regression test). Because identity comes from content rather than position, the diff survives inserts, deletes and reorders. + +The source language is whatever the editor selects in the modal (it defaults to your `defaultLocale`), so `srcHash` is keyed **by source locale**: translating a target from EN vs. DE are tracked independently, and a paragraph translated from one source isn't mistaken for content from another. The `outHash` is a single value — it hashes the target's own text, independent of which source produced it. + +The first incremental run on a field translated by an older version (no stored hashes) retranslates it once and then stamps the hashes; subsequent runs are incremental. If a future lexical/Payload release ever stopped preserving the `$` slot, the same merge can fall back to a sidecar field keyed by field path — the algorithm is identical, only the read/write of the hash changes. + ## Configuration ### Plugin Options diff --git a/content-translator/dev/src/collections/authors.ts b/content-translator/dev/src/collections/authors.ts index fbadab97..3d1ab29c 100644 --- a/content-translator/dev/src/collections/authors.ts +++ b/content-translator/dev/src/collections/authors.ts @@ -5,6 +5,7 @@ export const authorsSchema: CollectionConfig = { admin: { useAsTitle: 'name', }, + versions: true, fields: [ { name: 'name', diff --git a/content-translator/dev/src/collections/media.ts b/content-translator/dev/src/collections/media.ts index fa8973a8..ece4c773 100644 --- a/content-translator/dev/src/collections/media.ts +++ b/content-translator/dev/src/collections/media.ts @@ -7,6 +7,7 @@ const dirname = path.dirname(filename) export const mediaSchema: CollectionConfig = { slug: 'media', + versions: true, fields: [], upload: { staticDir: path.resolve(dirname, '../media'), diff --git a/content-translator/dev/src/collections/pages.ts b/content-translator/dev/src/collections/pages.ts index 604dcde1..56fed0d0 100644 --- a/content-translator/dev/src/collections/pages.ts +++ b/content-translator/dev/src/collections/pages.ts @@ -2,6 +2,7 @@ import type { CollectionConfig } from 'payload' export const pagesSchema: CollectionConfig = { slug: 'pages', + versions: true, fields: [ { name: 'title', diff --git a/content-translator/dev/src/collections/posts.ts b/content-translator/dev/src/collections/posts.ts index bfa6b295..7d49fe4d 100644 --- a/content-translator/dev/src/collections/posts.ts +++ b/content-translator/dev/src/collections/posts.ts @@ -2,6 +2,7 @@ import type { CollectionConfig, CollectionSlug } from 'payload' export const postsSchema: CollectionConfig = { slug: 'posts', + versions: true, fields: [ { name: 'title', diff --git a/content-translator/dev/src/seed.ts b/content-translator/dev/src/seed.ts index 5cf3f1c1..6b5bf508 100644 --- a/content-translator/dev/src/seed.ts +++ b/content-translator/dev/src/seed.ts @@ -8,11 +8,34 @@ interface AuthorSeedData { } interface PageSeedData { + content?: string[] keywords: string[] slug: string title: string } +/** Build a minimal lexical richText value from plain-text paragraphs. */ +const lexical = (paragraphs: string[]) => ({ + root: { + type: 'root', + children: paragraphs.map((text) => ({ + type: 'paragraph', + children: [ + { type: 'text', detail: 0, format: 0, mode: 'normal', style: '', text, version: 1 }, + ], + direction: 'ltr', + format: '', + indent: 0, + textFormat: 0, + version: 1, + })), + direction: 'ltr', + format: '', + indent: 0, + version: 1, + }, +}) + interface PostSeedData { slug: string title: string @@ -73,6 +96,18 @@ export const seed = async (payload: Payload) => { const pages: PageSeedData[] = [ { + // Source content for trying incremental richText translation. To see each + // case of the classification table: + // 1. open this page, switch to the German locale, "Translate all fields" + // 2. switch back to English, add / edit / reorder / delete a paragraph + // 3. switch to German, "Translate new & changed content" — only the + // changed paragraph is translated; the rest (incl. any manual edits + // you made to the German text) are preserved + content: [ + 'Welcome to our company. We build software that helps teams move faster.', + 'Our mission is to make complex workflows feel simple and reliable.', + 'Get in touch to learn how we can help your organisation.', + ], slug: 'home', keywords: ['welcome', 'home page', 'getting started'], title: 'Welcome to Our Website', @@ -95,15 +130,31 @@ export const seed = async (payload: Payload) => { ] for (const pageData of pages) { - const { totalDocs: existingPage } = await payload.count({ + const { docs } = await payload.find({ collection: 'pages' as CollectionSlug, + depth: 0, + limit: 1, where: { slug: { equals: pageData.slug } }, }) + const existingPage = docs[0] as { content?: unknown; id: number | string } | undefined + if (!existingPage) { await payload.create({ collection: 'pages' as CollectionSlug, - data: pageData, + data: { + slug: pageData.slug, + title: pageData.title, + keywords: pageData.keywords, + ...(pageData.content ? { content: lexical(pageData.content) } : {}), + } as Record, + }) + } else if (pageData.content && !existingPage.content) { + // Backfill demo content onto a page seeded before it had any. + await payload.update({ + collection: 'pages' as CollectionSlug, + id: existingPage.id, + data: { content: lexical(pageData.content) } as Record, }) } } diff --git a/content-translator/package.json b/content-translator/package.json index 53efad9b..1a95f134 100644 --- a/content-translator/package.json +++ b/content-translator/package.json @@ -43,7 +43,9 @@ "react-dom": "19.2.7" }, "devDependencies": { + "@lexical/headless": "^0.41.0", "@payloadcms/eslint-config": "^3.28.0", + "@payloadcms/richtext-lexical": "^3.84.1", "@swc/cli": "^0.8.1", "@swc/core": "^1.15.41", "@types/he": "^1.2.3", @@ -51,6 +53,7 @@ "@types/react-dom": "^19.2.3", "copyfiles": "^2.4.1", "eslint": "^9.39.4", + "lexical": "^0.41.0", "prettier": "^3.8.4", "rimraf": "^6.1.3", "tsx": "^4.22.4", diff --git a/content-translator/pnpm-lock.yaml b/content-translator/pnpm-lock.yaml index f17d30a4..383b34ec 100644 --- a/content-translator/pnpm-lock.yaml +++ b/content-translator/pnpm-lock.yaml @@ -57,15 +57,21 @@ importers: specifier: 19.2.7 version: 19.2.7(react@19.2.7) devDependencies: + '@lexical/headless': + specifier: ^0.41.0 + version: 0.41.0 '@payloadcms/eslint-config': specifier: ^3.28.0 - version: 3.28.0(@typescript-eslint/eslint-plugin@8.61.1(@typescript-eslint/parser@8.61.1(eslint@9.39.4)(typescript@6.0.3))(eslint@9.39.4)(typescript@6.0.3))(ts-api-utils@2.5.0(typescript@6.0.3)) + version: 3.28.0(@typescript-eslint/eslint-plugin@8.59.3(@typescript-eslint/parser@8.59.3(eslint@9.39.4)(typescript@6.0.3))(eslint@9.39.4)(typescript@6.0.3))(ts-api-utils@2.5.0(typescript@6.0.3)) + '@payloadcms/richtext-lexical': + specifier: ^3.84.1 + version: 3.84.1(@faceless-ui/modal@3.0.0(react-dom@19.2.7(react@19.2.7))(react@19.2.7))(@faceless-ui/scroll-info@2.0.0(react-dom@19.2.7(react@19.2.7))(react@19.2.7))(@payloadcms/next@3.84.1(@types/react@19.2.17)(graphql@16.12.0)(monaco-editor@0.55.1)(next@16.2.9(react-dom@19.2.7(react@19.2.7))(react@19.2.7)(sass@1.77.4))(payload@3.85.1(graphql@16.12.0)(typescript@6.0.3))(react-dom@19.2.7(react@19.2.7))(react@19.2.7)(typescript@6.0.3))(@types/react@19.2.17)(monaco-editor@0.55.1)(next@16.2.9(react-dom@19.2.7(react@19.2.7))(react@19.2.7)(sass@1.77.4))(payload@3.85.1(graphql@16.12.0)(typescript@6.0.3))(react-dom@19.2.7(react@19.2.7))(react@19.2.7)(typescript@6.0.3)(yjs@13.6.27) '@swc/cli': specifier: ^0.8.1 - version: 0.8.1(@swc/core@1.15.41) + version: 0.8.1(@swc/core@1.15.43) '@swc/core': specifier: ^1.15.41 - version: 1.15.41 + version: 1.15.43 '@types/he': specifier: ^1.2.3 version: 1.2.3 @@ -81,6 +87,9 @@ importers: eslint: specifier: ^9.39.4 version: 9.39.4 + lexical: + specifier: ^0.41.0 + version: 0.41.0 prettier: specifier: ^3.8.4 version: 3.8.4 @@ -175,8 +184,8 @@ packages: engines: {node: '>=6.0.0'} hasBin: true - '@babel/runtime@7.29.7': - resolution: {integrity: sha512-Nq8OhGWiZIZGV6hLHoyAKLLcJihP/xFeBMGJoUrxTX2psI8dCifzLhZISFb+VWS3wFMRDmCGw5R+dOySCqPLhw==} + '@babel/runtime@7.29.2': + resolution: {integrity: sha512-JiDShH45zKHWyGe4ZNVRrCjBz8Nh9TMmZG1kh4QTK8hCBTWBi8Da+i7s1fJw7/lYpM4ccepSNfqzZ/QvABBi5g==} engines: {node: '>=6.9.0'} '@babel/template@7.28.6': @@ -225,8 +234,8 @@ packages: peerDependencies: react: '>=16.8.0' - '@emnapi/runtime@1.11.1': - resolution: {integrity: sha512-vgj7R3y3Wgx24IQaGPA/R6YFXLHVMOZ0uVEyIQPaWs+rd1AzfEMXlAC22FYwO1XkKR6NPsq7mUandH8oIRdZFw==} + '@emnapi/runtime@1.10.0': + resolution: {integrity: sha512-ewvYlk86xUoGI0zQRNq/mC+16R1QeDlKQy21Ki3oSYXNgLb45GV1P6A0M+/s6nyCuNDqe5VpaY84BzXGwVbwFA==} '@emotion/babel-plugin@11.13.5': resolution: {integrity: sha512-pxHCpT2ex+0q+HH91/zsdHkw/lXd468DIN2zvfvLtPKLLMo6gQj7oLObq8PhkrxOZb/gGCq03S3Z7PDhS8pduQ==} @@ -1007,6 +1016,13 @@ packages: '@payloadcms/eslint-plugin@3.28.0': resolution: {integrity: sha512-oZhy8zd6WrxKwNygMfYE750yIRQEfdaoWEBR47UmKA6AMLb9p0kXygdkmnZLgNCFO4REZRUC2uHBWIh/dGqYrw==} + '@payloadcms/graphql@3.84.1': + resolution: {integrity: sha512-pVJdgihEEexpK+kANSozAMf3PQfd6p51/G3cK+rkPzSjghf7X+cQQ8NFOmAMDTpijM5YaowQYTuEje3KaLVXuQ==} + hasBin: true + peerDependencies: + graphql: ^16.8.1 + payload: 3.84.1 + '@payloadcms/graphql@3.85.1': resolution: {integrity: sha512-jMrSCiGYmsjOUGmZayX5U7Vh3i8IcO0XMmQ9x5jCIZuB+/sNDiBNeHJWArC+H2cfPYh0G+iv9t+RpPg6FemiMg==} hasBin: true @@ -1014,6 +1030,14 @@ packages: graphql: ^16.8.1 payload: 3.85.1 + '@payloadcms/next@3.84.1': + resolution: {integrity: sha512-T7f7Ja4l4Z6QM/oSM8MWJY63xyNQmWxUswDgssXl1/D0hQIVQASXMeZvXiXLQwg/g4f9s5anKTUNIx32dtHxWg==} + engines: {node: ^18.20.2 || >=20.9.0} + peerDependencies: + graphql: ^16.8.1 + next: 16.2.9 + payload: 3.84.1 + '@payloadcms/next@3.85.1': resolution: {integrity: sha512-k/2l2LQNrgfa5r5ibR0o4/nNhLAlQQXtLpwGrJq2/B/yws4Ir6otc+ozTMiprATlvVb6uXcNFT+m1XLYqqSFuQ==} engines: {node: ^18.20.2 || >=20.9.0} @@ -1022,6 +1046,17 @@ packages: next: 16.2.9 payload: 3.85.1 + '@payloadcms/richtext-lexical@3.84.1': + resolution: {integrity: sha512-KaNSz0RJFLnLc/hBRGg8Lgwk5FjCZSskA6KuufNWG5QdgUsgQe4Dx4Vr7G+VSR9zSZj+R4TVVSC0aVv4z7vL3g==} + engines: {node: ^18.20.2 || >=20.9.0} + peerDependencies: + '@faceless-ui/modal': 3.0.0 + '@faceless-ui/scroll-info': 2.0.0 + '@payloadcms/next': 3.84.1 + payload: 3.84.1 + react: ^19.0.1 || ^19.1.2 || ^19.2.1 + react-dom: ^19.0.1 || ^19.1.2 || ^19.2.1 + '@payloadcms/richtext-lexical@3.85.1': resolution: {integrity: sha512-58DXm5MotdtGCK1zStrnTdMlWYAJ2IqckEDjWv04xv2FbJc61FEKksqTjkAb5XgBxZKG2lxHNehgrDy97q+FWQ==} engines: {node: ^18.20.2 || >=20.9.0} @@ -1033,9 +1068,21 @@ packages: react: ^19.0.1 || ^19.1.2 || ^19.2.1 react-dom: ^19.0.1 || ^19.1.2 || ^19.2.1 + '@payloadcms/translations@3.84.1': + resolution: {integrity: sha512-1g4IsXvRfeQCn8YmPweaj8MMAFK9oSxYWKJG15j+Ah9Nra+f35/ZdOt/k80+vfoJmdKsOK/AnuzVdSTWy2GQpg==} + '@payloadcms/translations@3.85.1': resolution: {integrity: sha512-1DssESAfPjgkp8/f7Xq1ZOCnMjSXp0Xkpbo0I7Zt0DCaA+QY7Go+kZQuSYazKKRF+lgSGzKEHRJ5CWqzhpKh8w==} + '@payloadcms/ui@3.84.1': + resolution: {integrity: sha512-L9trD3/LcK1fiGg4AFexy9AQm4upMzRbicZFTNU/ac+x5o6Pwyr7FzrvbtfCr0l2t6QhawLBZmBCZJA8oBF2uA==} + engines: {node: ^18.20.2 || >=20.9.0} + peerDependencies: + next: 16.2.9 + payload: 3.84.1 + react: ^19.0.1 || ^19.1.2 || ^19.2.1 + react-dom: ^19.0.1 || ^19.1.2 || ^19.2.1 + '@payloadcms/ui@3.85.1': resolution: {integrity: sha512-WCJBEKAnwIvEi2Hiykl92dLVjGga6sPGsNlkbkssNMDqieEUh6qNAb3HewlJUa0IFrhxVFLoAEyJOzHr1UJXkQ==} engines: {node: ^18.20.2 || >=20.9.0} @@ -1048,8 +1095,8 @@ packages: '@pinojs/redact@0.4.0': resolution: {integrity: sha512-k2ENnmBugE/rzQfEcdWHcCY+/FM3VLzH9cYEsbdsoqrvzAKRhUZeRNhAZvB8OitQJ1TBed3yqWtdjzS6wJKBwg==} - '@preact/signals-core@1.14.3': - resolution: {integrity: sha512-m0K3vnbSLC5rHs2ZVfeAMvBtT1zIyq4mxx5OlNncSgMj5Iz6W5Rn3kPrDxAC+iIKmiVe0lSl6U37t5ZkEWoVAw==} + '@preact/signals-core@1.14.2': + resolution: {integrity: sha512-RZHdBj9ZF4n40Rp4jS052EHHjBWf96P9oNdXPfhQTovCuWY9iQn3Gq+gOTJSgBO9A/JBuPfMOWsSX/lIU9Pc/A==} '@sec-ant/readable-stream@0.4.1': resolution: {integrity: sha512-831qok9r2t8AlxLko40y2ebgSDhenenCatLVeW/uBtnHPyhHOvG0C7TvfgecV+wHzIm5KUICgzmVpWS+IMEAeg==} @@ -1073,86 +1120,86 @@ packages: chokidar: optional: true - '@swc/core-darwin-arm64@1.15.41': - resolution: {integrity: sha512-kREh6J5paQFvP3i7f/4FbqRNOJREutVFVOkder4GVyCBQ39YmER55cW/y1NNjwrchzFqgYswFn0mMDCqbqKzrw==} + '@swc/core-darwin-arm64@1.15.43': + resolution: {integrity: sha512-v1aVuvXdo/BHxJzco9V2xpHrvwWmhfS8t6gziY5wJxd+Z2h8AeJRnAwPD8itCDaGXVBwJ/CaKfxEzTkG0Va0OA==} engines: {node: '>=10'} cpu: [arm64] os: [darwin] - '@swc/core-darwin-x64@1.15.41': - resolution: {integrity: sha512-N8B56ESFazZAWZyIkecADSPCwlLEinW7QLMEeotCpv4J7VXwfH+OLkmRL8o96UZ+1355fwHxDTS6/wK7yucvkA==} + '@swc/core-darwin-x64@1.15.43': + resolution: {integrity: sha512-lp3d4Lamc8dt5huYdGLSR+9hLxmfr1jb0l+4XXG2zPqZwYWRN9R0U2qYoTrggiU2RWW0oV9VbWM3kBnqIc2kdQ==} engines: {node: '>=10'} cpu: [x64] os: [darwin] - '@swc/core-linux-arm-gnueabihf@1.15.41': - resolution: {integrity: sha512-6XrId2fyle0mS5xxON8rU84mPd2Cq1kDJRj+4BnQKTd7u+2kSA6Ww+JkOP0iTNqOqt9OXhPOEAjBHAuonWcdCg==} + '@swc/core-linux-arm-gnueabihf@1.15.43': + resolution: {integrity: sha512-JWTQQELtsG5GgphDrr/XqqmM2pDN3cZqbMS0Mrg+iTiXL3F74sn/S2IyYE/5u4h2KLkTf9qQ7dXyxsbx7YzkeA==} engines: {node: '>=10'} cpu: [arm] os: [linux] - '@swc/core-linux-arm64-gnu@1.15.41': - resolution: {integrity: sha512-ynLIarxlkVnqHn1D0fKOVht6mNU5ks6lrH+MY3kkS+XFaGGgDxFZVjWKJlkYTKm3RCvBTfA8Ng5fLufXheMRKQ==} + '@swc/core-linux-arm64-gnu@1.15.43': + resolution: {integrity: sha512-B4otJRdPWIsmiSBf0uG7Z/+vMWmkufjz5MmYxubwKuZazDW14Zd3symga1N62QR4RT+kEFeHEgsXfZGyn/w0hw==} engines: {node: '>=10'} cpu: [arm64] os: [linux] libc: [glibc] - '@swc/core-linux-arm64-musl@1.15.41': - resolution: {integrity: sha512-dXu/5vd4gh8symyhRF+4G7gOPkjmb4pONhh7sl+6GSiW0LOKZlfu5kXmyFbTz9smOT7jgr002qY9b1nujjXt2A==} + '@swc/core-linux-arm64-musl@1.15.43': + resolution: {integrity: sha512-6zB6OnpViBxYy4tgY3v2i6AZY9fwkcHZ032UOwtwUuW1d19sdT07qF0kZe6/3UR1tUaK6jjg2rmVcUIBCEYVjQ==} engines: {node: '>=10'} cpu: [arm64] os: [linux] libc: [musl] - '@swc/core-linux-ppc64-gnu@1.15.41': - resolution: {integrity: sha512-XGO6zVPXoPE0gf/XnI4jBbafNT13AYgoh6ns0JCSdOetI/kqVf0vhpz7NuNgAzZrMVCsmieqjPoTwViDgh4mOQ==} + '@swc/core-linux-ppc64-gnu@1.15.43': + resolution: {integrity: sha512-coxE1ZWdB3uSDVNoEtYNrRi/1epvckZx9cTJ8ICUxTMTxGk+yvQ/Twacp3ruZSaMPGCriUjP86C37VhaT6nyRg==} engines: {node: '>=10'} cpu: [ppc64] os: [linux] libc: [glibc] - '@swc/core-linux-s390x-gnu@1.15.41': - resolution: {integrity: sha512-0WUglRwyZtW+iMi7J3iFdrCxreZZIKf4egTwEQfIYRsqFax69A0OrFj+NIoFSE03xBT/IFRrg+S8K6f9Ky+4hA==} + '@swc/core-linux-s390x-gnu@1.15.43': + resolution: {integrity: sha512-lXfLhs+LpBsD5inuYx+YDH5WsPPBQ95KPUiy8P5wq9ob9xKDZFqwNfU2QW6bGO8NqRO/H9JQomTSt5Yyh+FGfA==} engines: {node: '>=10'} cpu: [s390x] os: [linux] libc: [glibc] - '@swc/core-linux-x64-gnu@1.15.41': - resolution: {integrity: sha512-VxkuQK59c0tHm6uJZCUrS3cyA2JhGGfdU6e41SZz0x/JS+4Sm7C1mIc97In14vkZJopEt7yXA2TouCqZDSygEA==} + '@swc/core-linux-x64-gnu@1.15.43': + resolution: {integrity: sha512-07XnKwTmKy8TGOZG3D9fRnLWGynxPjwQnZLVmBFbo6F+7vHYzBIOuwXEhemrChBWb6yDNZsVCcMWCPX6FDD2xg==} engines: {node: '>=10'} cpu: [x64] os: [linux] libc: [glibc] - '@swc/core-linux-x64-musl@1.15.41': - resolution: {integrity: sha512-/0qXIu1ZxggLuovLb22vFfKHq2AA4n6Whw5UwmVCHk4pkw7KWnPIQpMCEqUMPsNkFJig7PPp/TSYFu8ZEb2rtQ==} + '@swc/core-linux-x64-musl@1.15.43': + resolution: {integrity: sha512-TJc+bsSIaBh+hZvZ5GRtW/K1bw66TJ9vsUwvVIsZdiWxU5ObLwZvfcnZ3UpgVfMnFibRes9uriJrQNBHEEogRQ==} engines: {node: '>=10'} cpu: [x64] os: [linux] libc: [musl] - '@swc/core-win32-arm64-msvc@1.15.41': - resolution: {integrity: sha512-Y481sMNZM6rECh9VO4+y26N1lWEDAyxnBZskUf37fl90uHE946VHfmiVQWT0uMFOhyJJFovGTRuF4W82dwewUg==} + '@swc/core-win32-arm64-msvc@1.15.43': + resolution: {integrity: sha512-jfd7s2/bUQYkOHLs+LWQNKZdmDa8+sufKLllhpWAhVQ2GDCwsHe3vR/j+OSiItZNtkzFuaawa3+SAKz9y5gYfw==} engines: {node: '>=10'} cpu: [arm64] os: [win32] - '@swc/core-win32-ia32-msvc@1.15.41': - resolution: {integrity: sha512-BAchBD5qeUzy3hiPSLJtaaoSm4blCLyYffOF1bGE4ETcV+OisqjUAwDQMJj++4bTpvMCDzwC+Bj3PmQyBCtscw==} + '@swc/core-win32-ia32-msvc@1.15.43': + resolution: {integrity: sha512-rLAE8JvucqEW1ZGohxPQrQWPBQeJG4+ypKbWfdlU/qmKScvCkxf9/Jxnzki1dkUQCQ7P5Enp13RlvqOlvx/32g==} engines: {node: '>=10'} cpu: [ia32] os: [win32] - '@swc/core-win32-x64-msvc@1.15.41': - resolution: {integrity: sha512-WOkA+fJ/ViVBQDsSV9JC52NACTe5PhlurA6viASDZGb7HR3KS01ZG7RZ+Bg6SVQFIoq3gSbTsskQVe6EbHFAYw==} + '@swc/core-win32-x64-msvc@1.15.43': + resolution: {integrity: sha512-h8MLDHZcfIukwQWj03rIJZx1I0E81AYj2X7J/nGErG4nz+QAv6G1Z+peotvinL3lqpbo32tLYSMFo32/ySzxKg==} engines: {node: '>=10'} cpu: [x64] os: [win32] - '@swc/core@1.15.41': - resolution: {integrity: sha512-03nQq/082QRJJiOvp3FGbgxTGyyxMxohPTjhk/W9bD2J0tk4ukITI7goOhOO2WbaHn/lsPmo/zf8+DIXhwpgYQ==} + '@swc/core@1.15.43': + resolution: {integrity: sha512-1CuKjFkPxIgGdeHVuNbkxmBxkcbdc08u0aiI43pFq6yY1tTVKmXT9hFEooyyKs/sJ3xf1GPHyEwTtk9Xl8dvQw==} engines: {node: '>=10'} peerDependencies: '@swc/helpers': '>=0.5.17' @@ -1221,9 +1268,6 @@ packages: '@types/node@25.7.0': resolution: {integrity: sha512-z+pdZyxE+RTQE9AcboAZCb4otwcrvgHD+GlBpPgn0emDVt0ohrTMhAwlr2Wd9nZ+nihhYFxO2pThz3C5qSu2Eg==} - '@types/node@25.9.3': - resolution: {integrity: sha512-603BddQMv3pUcr4U2dhujk83N2tTDVr/34wII2B6bJy6g+8WD6yUb11jszNs0gdi4PesVWl7ABt8nYMVpnLUcg==} - '@types/parse-json@4.0.2': resolution: {integrity: sha512-dISoDXWWQwUquiKsyZ4Ng+HX2KsPL7LyHKHQwgGFEA3IaKac4Obd+h2a/a6waisAoepJlBcx9paWqjA8/HVjCw==} @@ -1261,103 +1305,103 @@ packages: '@types/ws@8.18.1': resolution: {integrity: sha512-ThVF6DCVhA8kUGy+aazFQ4kXQ7E1Ty7A3ypFOe0IcJV8O/M511G99AW24irKrW56Wt44yG9+ij8FaqoBGkuBXg==} - '@typescript-eslint/eslint-plugin@8.61.1': - resolution: {integrity: sha512-ZPlVl3PB3et/59Ne0fv/sci6ZXz4T4Hp4nTJ56i/Y0gR89ARb+KphojTq6j+56E5PIezmOIOOWyY+aWQFd+IkQ==} + '@typescript-eslint/eslint-plugin@8.59.3': + resolution: {integrity: sha512-PwFvSKsXGShKGW6n5bZOhGHEcCZXM8HofLK9fNsEwZXzFRjoY+XT1Vsf1zgyXdwTr0ZYz1/2tkZ0DBTT9jZjhw==} engines: {node: ^18.18.0 || ^20.9.0 || >=21.1.0} peerDependencies: '@typescript-eslint/parser': ^8.59.3 eslint: ^9.39.4 typescript: '>=4.8.4 <6.1.0' - '@typescript-eslint/parser@8.61.1': - resolution: {integrity: sha512-PJ5vePq5/ognBbrIcoC5+SHO5dfpeLPzP9FpLkzWrguoYQEeeSjlJpVwOpo1JRSTEi7dRcwNy4h4dzV70PqHcg==} + '@typescript-eslint/parser@8.59.3': + resolution: {integrity: sha512-HPwA+hVkfcriajbNvTmZv4VRauibay+cWArYUYq7u7W7PmGShMxbPxLvrwDme55a6d5alG3nrYfhyJ/G28XlLg==} engines: {node: ^18.18.0 || ^20.9.0 || >=21.1.0} peerDependencies: eslint: ^9.39.4 typescript: '>=4.8.4 <6.1.0' - '@typescript-eslint/project-service@8.61.1': - resolution: {integrity: sha512-PrC4JYGmR241lYnfhmKGTXkFqv8+ymbTFgSAY0fVXpY82/QkMw5TZPl+vGzuDDU2QYJk9fIDOBTntF+yDv9LEA==} + '@typescript-eslint/project-service@8.59.3': + resolution: {integrity: sha512-ECiUWa/KYRGDFUqTNehaRgzDshnJfkTABJxVemHk4ko22gcr0ukloKjWvyQ64g8YCV/UI47kN1dbmjf/GaQYng==} engines: {node: ^18.18.0 || ^20.9.0 || >=21.1.0} peerDependencies: typescript: '>=4.8.4 <6.1.0' - '@typescript-eslint/scope-manager@8.61.1': - resolution: {integrity: sha512-L2bdIeoQS8FlKAvONAr20w6OcLXeB+qiDKbAooS9A0Ben+iSIkBef0FxqwKWYqt5sa0i4KJtxVyVmhMylKzF5w==} + '@typescript-eslint/scope-manager@8.59.3': + resolution: {integrity: sha512-t2LvZnoEfzKtnPjgeEu41xw5gxq9mQVfYy4OoZ4Vlt0sk3JwxmhCca/AR7DwOiHrjWgjAj6as4AhRLKSDfvZIA==} engines: {node: ^18.18.0 || ^20.9.0 || >=21.1.0} - '@typescript-eslint/tsconfig-utils@8.61.1': - resolution: {integrity: sha512-UN/H4di+OO7EWx2ovME+8t31YO+KVnK0RRKEHR3kOt21/Ay8BOq3M1OMvWs5vNiqcFCYGYoxK3MXPZzmMUE+yg==} + '@typescript-eslint/tsconfig-utils@8.59.3': + resolution: {integrity: sha512-PcIJHjmaREXLgIAIzLnSY9VucEzz8FKXsRgFa1DmdGCK/5tJpW03TKJF01Q6VZd1lLdz2sIKPWaDUZN9dp//dw==} engines: {node: ^18.18.0 || ^20.9.0 || >=21.1.0} peerDependencies: typescript: '>=4.8.4 <6.1.0' - '@typescript-eslint/type-utils@8.61.1': - resolution: {integrity: sha512-GYRicKmVK0C4fsKgaACaknOUAq9Oa2kwsjnpFhFcS/5p4Ht5IP9OVLbgIgcK4SRk92nVHFluurg1lumD9dBcLw==} + '@typescript-eslint/type-utils@8.59.3': + resolution: {integrity: sha512-g71d8QD8UaiHGvrJwyIS1hCX5r63w6Jll+4VEYhEAHXTDIqX1JgxhTAbEHtKntL9kuc4jRo7/GWw5xfCepSccQ==} engines: {node: ^18.18.0 || ^20.9.0 || >=21.1.0} peerDependencies: eslint: ^9.39.4 typescript: '>=4.8.4 <6.1.0' - '@typescript-eslint/types@8.61.1': - resolution: {integrity: sha512-G+CRlPqLv7Bz1IZVs03x5K59F1veqL0EJUROAdGhKsEq8qOiRiZbI+HUojPq5l0fEGOKModD9br6lObhB8zkoA==} + '@typescript-eslint/types@8.59.3': + resolution: {integrity: sha512-ePFoH0g4ludssdRFqqDxQePCxU4WQyRa9+XVwjm7yLn0FKhMeoetC+qBEEI1Eyb1pGSDveTIT09Bvw2WhlGayg==} engines: {node: ^18.18.0 || ^20.9.0 || >=21.1.0} - '@typescript-eslint/typescript-estree@8.61.1': - resolution: {integrity: sha512-u+oQD3BqYWPc8YV9Zab4vaJElJuwOLPRc10Jm1o/qS+6Qwen14HCWwx0Seo4LnSn2wxea2Ik8DxPt2/FHmuhrg==} + '@typescript-eslint/typescript-estree@8.59.3': + resolution: {integrity: sha512-CbRjVRAf7Lr9Kr8RopKcbY45p2VfmmHrm0ygOCYFi7oU8q19m0Fs/6iHS7kNOmwpp+ob07ZVcAqlxUod9lYdmg==} engines: {node: ^18.18.0 || ^20.9.0 || >=21.1.0} peerDependencies: typescript: '>=4.8.4 <6.1.0' - '@typescript-eslint/utils@8.61.1': - resolution: {integrity: sha512-1+P/3Dj6jvtybE1q0HQ6yBt/gq+oKJyLdEv4HdnqasaEXRSYCAsD59mXEVQnM/ULNdQxbX77tdG4jPRjIS6knA==} + '@typescript-eslint/utils@8.59.3': + resolution: {integrity: sha512-JAvT14goBzRzzzZyqq3P9BLArIxTtQURUtFgQ/V7FO+eU+Gg6ES+5ymOPP1wRxXcxAYeivCk4uS3jCKWI1K8Zg==} engines: {node: ^18.18.0 || ^20.9.0 || >=21.1.0} peerDependencies: eslint: ^9.39.4 typescript: '>=4.8.4 <6.1.0' - '@typescript-eslint/visitor-keys@8.61.1': - resolution: {integrity: sha512-6fJ9MHWtK14C1DSkiMlHUSOmrVebL7150xZJBlJiL62jjhIA4JmOq6flwBgDxIdBKKdoiZRel+dfPD5MLfny3w==} + '@typescript-eslint/visitor-keys@8.59.3': + resolution: {integrity: sha512-f1UQF7ggd42YiwI5wGrRaPsa+P0CINBlrkLPmGfpq/u/I/oVtecoEIfFR9ag/oa1sLOsRNZ6xehf6qMZhQGBDg==} engines: {node: ^18.18.0 || ^20.9.0 || >=21.1.0} - '@xhmikosr/archive-type@8.1.0': - resolution: {integrity: sha512-EXOjEbnZFE5c/nFMf4FOrEURVanzHpnkPYmnmr78u02/8hAhE0FMq8p9TK1IM0/bFr5VcyBUY0gfLm8f7dKy+Q==} + '@xhmikosr/archive-type@8.0.1': + resolution: {integrity: sha512-toXuiWChyfOpEiCPsIw6HGHaNji5LVkvB6EREL548vGWr+hGaehwxG4LzN20vm9aGFXwnA/Jty8yW2/SmV+1zQ==} engines: {node: '>=20'} '@xhmikosr/bin-check@8.2.1': resolution: {integrity: sha512-DNruLq+kalxcE7JeDxtqrN9kyWjLW8VqsQPLRTwD1t9ck/1rF4qBL0mX5Fe2/xLOMjo5wPb67BNX2kSAhzfLjA==} engines: {node: '>=20'} - '@xhmikosr/bin-wrapper@14.4.0': - resolution: {integrity: sha512-Qp4YGNOIBnNEXoyM8nc0L92frBtrerzRpE/zZ4EMU9i37tGGfiuKyQTdHdVUcj7z1ZMfg2hAk+i5gMbWGc5cdw==} + '@xhmikosr/bin-wrapper@14.2.3': + resolution: {integrity: sha512-F8Sr2O2aqwYfoXTafemRNAYDG4xwBTaHJpAo9YVnnnRXHLP9gkb+HYDsFoCAsCneS3/J7BOfeYnxxlUCicLqjg==} engines: {node: '>=20'} '@xhmikosr/decompress-tar@9.0.1': resolution: {integrity: sha512-4AkVR1SoqTxYY22IRRYKDeLirPIDGqMqYsqgjKYuwhgRcBb+yDP4t5Xph33UCzL/nahK/aADmlMEjTNstbX7kw==} engines: {node: '>=20'} - '@xhmikosr/decompress-tarbz2@9.0.2': - resolution: {integrity: sha512-m0DvZhE7remCxtS8xY2iHSjivT4v+iyYDdfNoeuu8Nm+7g8xEXdLKSyDEicu4u1ImJLLGEfjMuTLera/F6UGWw==} + '@xhmikosr/decompress-tarbz2@9.0.1': + resolution: {integrity: sha512-aFONnsbqEOuXudvK7V7wB8dcEAKR389oUYQfZhrQZA8OtogJpDjrUAvEH3Qlc9yFqTU6r5/svTEcRwtXhoIJbQ==} engines: {node: '>=20'} '@xhmikosr/decompress-targz@9.0.1': resolution: {integrity: sha512-1JXu2b6yrpm5EuBoOzMU57B4qrHXJKWQQ7LlMynNEiz85mEjDciO3ayf//GXaTLLCEKiHjWlU3q3THjgf7uODA==} engines: {node: '>=20'} - '@xhmikosr/decompress-unzip@8.2.1': - resolution: {integrity: sha512-2MS94QnmXQwjkKN8WyFiu1sU7J3rcWJcMze4kRYsX7tN+CXpUGECgkh4YSOhujpkWPuVlFudIziJHO/TxOqkQQ==} + '@xhmikosr/decompress-unzip@8.1.1': + resolution: {integrity: sha512-/B+Z0qJflGn5UEtmMZ2qeKeXwexOycxaibYhMOyLcRPJriXs4IkoSngVUVZXLYViu9TdHyFWynC6NB4EWBg8cg==} engines: {node: '>=20'} - '@xhmikosr/decompress@11.1.3': - resolution: {integrity: sha512-NiyhJq6z7ERsYghcnXZUI6ooDXgZtoB+G9eUsYhfSM4VLp2rKx9UxhKI1NEf1PqosrNPxG3bnSsr2UBVbNurlg==} + '@xhmikosr/decompress@11.1.2': + resolution: {integrity: sha512-f2hlnMN1ChbifAfdzWns6mssojjr3lgJm6MtT4ttVAClx85QEIQAdGXN22bPqy/qKxbvDf93GdqbR6+xIO/a4A==} engines: {node: '>=20'} - '@xhmikosr/downloader@16.2.0': - resolution: {integrity: sha512-5vFLFTOFdDyuPJ6DDaqFPviMnMLrjWvpvbTp+go+JDoyzUBLd4dTjb+1PXIRiUMvJSvlxjpC8GIVN8mYVhQfhg==} + '@xhmikosr/downloader@16.1.2': + resolution: {integrity: sha512-31KQzQ6p4Rwnbo/gwTe4/Z+hVRcC8YoH/8f5xl+so1Oqqah5u1R3CGte8od+wOyNVfZ77DFijwy1umHk2NT6ZQ==} engines: {node: '>=20'} - '@xhmikosr/os-filter-obj@4.1.0': - resolution: {integrity: sha512-y5ArHvQ7BVule/+L9yE2nYMhceiJhgsqo58lOfnisQ7bg+Kjfmkgr7JBuVFiTkl+ErdShpp829QstZQyLugl8g==} + '@xhmikosr/os-filter-obj@4.0.0': + resolution: {integrity: sha512-CBJYipR5lrtQQZl9ylarWyh1qhcs/tMy9ydSHte/Hefn3ev8NMvS3ss+eqiXEoBr2wBVgKj2qjcViXO9P/8K4A==} engines: {node: '>=20'} acorn-jsx@5.3.2: @@ -1388,6 +1432,9 @@ packages: resolution: {integrity: sha512-KMReFUr0B4t+D+OBkjR3KYqvocp2XaSzO55UcB6mgQMd3KbcE+mWTyvVV7D/zsdEbNnV6acZUutkiHQXvTr1Rw==} engines: {node: '>= 8'} + arch@3.0.0: + resolution: {integrity: sha512-AmIAC+Wtm2AU8lGfTtHsw0Y9Qtftx2YXEEtiBP10xFUtMOA+sHHx6OAddyL52mUKh1vsXQ6/w1mVDptZCyUt4Q==} + argparse@2.0.1: resolution: {integrity: sha512-8+9WqebbFzpX9OR+Wa6O29asIogeRMzcGtAINdpMHHyAg10f05aSFVBbcEqGf/PXw1EjAZ+q2/bEBg3DvurK3Q==} @@ -1430,8 +1477,8 @@ packages: resolution: {integrity: sha512-wvUjBtSGN7+7SjNpq/9M2Tg350UZD3q62IFZLbRAR1bSMlCo1ZaeW+BJ+D090e4hIIZLBcTDWe4Mh4jvUDajzQ==} engines: {node: '>= 0.4'} - axe-core@4.12.1: - resolution: {integrity: sha512-s7iGf5GaVMxEG0ENN9x+xTr7GFZCb1ZP/1uATUpCEK2X78nDB3RwbtFCo9pGAf9ru+VwoQ464DkaLEeRM08wJA==} + axe-core@4.11.4: + resolution: {integrity: sha512-KunSNx+TVpkAw/6ULfhnx+HWRecjqZGTOyquAoWHYLRSdK1tB5Ihce1ZW+UY3fj33bYAFWPu7W/GRSmmrCGuxA==} engines: {node: '>=4'} axobject-query@4.1.0: @@ -1457,8 +1504,8 @@ packages: resolution: {integrity: sha512-BLrgEcRTwX2o6gGxGOCNyMvGSp35YofuYzw9h1IMTRmKqttAZZVU67bdb9Pr2vUHA8+j3i2tJfjO6C6+4myGTA==} engines: {node: 18 || 20 || >=22} - bare-events@2.9.1: - resolution: {integrity: sha512-Z0oHEHAFDZkffN8Qc39zNZjQlMDkPJRyyyZieU1VH7u8c5S+qHZ2S8ixdKIAxEjfHO7FJxXmJWgteOghVanIsg==} + bare-events@2.8.2: + resolution: {integrity: sha512-riJjyv1/mHLIPX4RwiK+oW9/4c3TEUeORHKefKAKnZ5kyslbN+HXowtbaVEqt4IMUB7OXlfixcs6gsFeo/jhiQ==} peerDependencies: bare-abort-controller: '*' peerDependenciesMeta: @@ -1468,8 +1515,8 @@ packages: base64-js@1.5.1: resolution: {integrity: sha512-AKpaYlHn8t4SVbOHCy+b5+KKgvR4vrsD8vbvrbiQJps7fKDTkjkDry6ji0rUJjC0kzbNePLwzxq8iypo41qeWA==} - baseline-browser-mapping@2.10.38: - resolution: {integrity: sha512-31/02mVB4yuQU6adKk5SlY6m+mxDwUq5KZkyYgnLrrKl7TEm1+3PyDtDBz2kOv/wxZz41GHsvV1A/u6RmiyBvw==} + baseline-browser-mapping@2.10.29: + resolution: {integrity: sha512-Asa2krT+XTPZINCS+2QcyS8WTkObE77RwkydwF7h6DmnKqbvlalz93m/dnphUyCa6SWSP51VgtEUf2FN+gelFQ==} engines: {node: '>=6.0.0'} hasBin: true @@ -1494,8 +1541,8 @@ packages: brace-expansion@1.1.14: resolution: {integrity: sha512-MWPGfDxnyzKU7rNOW9SP/c50vi3xrmrua/+6hfPbCS2ABNWfx24vPidzvC7krjU/RTo235sV776ymlsMtGKj8g==} - brace-expansion@2.1.1: - resolution: {integrity: sha512-WR1cURNjuvBLMZBMbqM0UoE+WAfdUcEV1ccD8PVBVOI+Z3ND4+SZbN8RsfT2bMuG1qwz5RFvPukSZm5fF2D5eA==} + brace-expansion@2.1.0: + resolution: {integrity: sha512-TN1kCZAgdgweJhWWpgKYrQaMNHcDULHkWwQIspdtjV4Y5aurRdZpjAqn6yX3FPqTA9ngHCc4hJxMAMgGfve85w==} brace-expansion@5.0.6: resolution: {integrity: sha512-kLpxurY4Z4r9sgMsyG0Z9uzsBlgiU/EFKhj/h91/8yHu0edo7XuixOIH3VcJ8kkxs6/jPzoI6U9Vj3WqbMQ94g==} @@ -1512,9 +1559,8 @@ packages: resolution: {integrity: sha512-WIsKqkSC0ABoBJuT1LEX+2HEvNmNKKgnTAyd0fL8qzK4SH2i9NXg+t08YtdZp/V9IZ33cxe3iV4yM0qg8lMQng==} engines: {node: '>=16.20.1'} - buffer-image-size@0.6.4: - resolution: {integrity: sha512-nEh+kZOPY1w+gcCMobZ6ETUp9WfibndnosbpwB1iJk/8Gt5ZF2bhS6+B6bPYz424KtwsR6Rflc3tCz1/ghX2dQ==} - engines: {node: '>=4.0'} + buffer-crc32@0.2.13: + resolution: {integrity: sha512-VO9Ht/+p3SN7SKWqcrgEzjGbRSJYTx+Q1pTQC0wrWqHx0vpJraQ6GtHx8tvcg1rlK1byhU5gccxgOgj7B0TDkQ==} buffer@5.7.1: resolution: {integrity: sha512-EHcyIPBQ4BSGlvjB16k5KgAJ27CIsHY/2JBmCRReo48y9rQ3MaUzWX3KVlBa4U7MyX02HdVj0K7C3WaB3ju7FQ==} @@ -1551,8 +1597,8 @@ packages: resolution: {integrity: sha512-P8BjAsXvZS+VIDUI11hHCQEv74YT67YUi5JJFNWIqL235sBmjX4+qx9Muvls5ivyNENctx46xQLQ3aTuE7ssaQ==} engines: {node: '>=6'} - caniuse-lite@1.0.30001799: - resolution: {integrity: sha512-hG1bReV+OUU+MOqK4t/ZWI0tZOyz3rqS9XuhOUz1cIcbwBKjOyJEJuw9ER5JuNyqxNk8u/JUVbGibBOL1yrjFw==} + caniuse-lite@1.0.30001792: + resolution: {integrity: sha512-hVLMUZFgR4JJ6ACt1uEESvQN1/dBVqPAKY0hgrV70eN3391K6juAfTjKZLKvOMsx8PxA7gsY1/tLMMTcfFLLpw==} ccount@2.0.1: resolution: {integrity: sha512-eyrF0jiFpY+3drT6383f1qhkbGsLSifNAjA61IUjZjmLCWjItY6LB9ft9YhoDgwfmclB2zhu51Lc7+95b8NRAg==} @@ -1615,8 +1661,8 @@ packages: resolution: {integrity: sha512-OkTL9umf+He2DZkUq8f8J9of7yL6RJKI24dVITBmNfZBmri9zYZQrKkuXiKhyfPSu8tUhnVBB1iKXevvnlR4Ww==} engines: {node: '>= 12'} - comment-parser@1.4.7: - resolution: {integrity: sha512-0h+uSNtQGW3D98eQt3jJ8L06Fves8hncB4V/PKdw/Qb8Hnk19VaKuTr55UNRYiSoVa7WwrFls+rh3ux9agmkeQ==} + comment-parser@1.4.6: + resolution: {integrity: sha512-ObxuY6vnbWTN6Od72xfwN9DbzC7Y2vv8u1Soi9ahRKL37gb6y1qk6/dgjs+3JWuXJHWvsg3BXIwzd/rkmAwavg==} engines: {node: '>= 12.0.0'} compare-versions@6.1.1: @@ -1628,8 +1674,8 @@ packages: console-table-printer@2.12.1: resolution: {integrity: sha512-wKGOQRRvdnd89pCeH96e2Fn4wkbenSP6LMHfjfyNLMbGuHEFbMqQNuxXqd0oXG9caIOQ1FTvc5Uijp9/4jujnQ==} - content-disposition@2.0.1: - resolution: {integrity: sha512-e+H0ZXHSWYrENhQzw1LPuP4oF5MzVKmDU6d3hxlvaPEYLLg62MxtQNPRx4SYSuYJSBUgnQIG4HIN2tEtNv7Dog==} + content-disposition@1.1.0: + resolution: {integrity: sha512-5jRCH9Z/+DRP7rkvY83B+yGIGX96OYdJmzngqnw2SBSxqCFPd0w2km3s5iawpGX8krnwSGmF0FW5Nhr0Hfai3g==} engines: {node: '>=18'} convert-hrtime@5.0.0: @@ -1759,8 +1805,8 @@ packages: dom-helpers@5.2.1: resolution: {integrity: sha512-nRCa7CK3VTrM2NmGkIy4cbK7IZlgBE/PYMn55rrXefr5xXDP0LdtfPnblFDoVdcAfslJ7or6iqAUnx0CCGIWQA==} - dompurify@3.4.11: - resolution: {integrity: sha512-zhlUV12GsaRzMsf9q5M254YhA4+VuF0fG+QFqu6aYpoGlKtz+w8//jBcGVYBgQkR5GHjUomejY84AV+/uPbWdw==} + dompurify@3.4.2: + resolution: {integrity: sha512-lHeS9SA/IKeIFFyYciHBr2n0v1VMPlSj843HdLOwjb2OxNwdq9Xykxqhk+FE42MzAdHvInbAolSE4mhahPpjXA==} dotenv@17.4.2: resolution: {integrity: sha512-nI4U3TottKAcAD9LLud4Cb7b2QztQMUEfHbvhTH09bqXTxnSie8WnjPALV/WMCrJZ6UV/qHJ6L03OqO3LcdYZw==} @@ -1779,8 +1825,8 @@ packages: end-of-stream@1.4.5: resolution: {integrity: sha512-ooEGc6HP26xXq/N+GCGOT0JKCLDGrq2bQUZrQ7gyrJiZANJ/8YDTxTpQBXGMn+WbIQXNVpyWymm7KYVICQnyOg==} - enhanced-resolve@5.24.0: - resolution: {integrity: sha512-SkE2t82KlkkxQRVMVLAGKxLfORGQfrkx5dkj+vlgXRVNEdPc4eZcR+J/Fvj8C+yKSFH5L0q3NFlyufOVQnCcYQ==} + enhanced-resolve@5.21.3: + resolution: {integrity: sha512-QyL119InA+XXEkNLNTPCXPugSvOfhwv0JOlGNzvxs0hZaiHLNvXSpudUWsOlsXGWJh8G6ckCScEkVHfX3kw/2Q==} engines: {node: '>=10.13.0'} entities@7.0.1: @@ -1790,10 +1836,6 @@ packages: error-ex@1.3.4: resolution: {integrity: sha512-sqQamAnR14VgCr1A618A3sGrygcpK+HEbenA/HiEAkkUwcZIIB/tgWqHFxWgOyDh4nB4JCRimh79dR5Ywc9MDQ==} - es-abstract-get@1.0.0: - resolution: {integrity: sha512-6PMWXpdhshVvFp+FoWYs1EvG1Nj0tvk0dZM+XcK0xMEM1czRVcP6ohqPWHy6qPagSpC8j4+p89WXlT+xXJs/fg==} - engines: {node: '>= 0.4'} - es-abstract@1.24.2: resolution: {integrity: sha512-2FpH9Q5i2RRwyEP1AylXe6nYLR5OhaJTZwmlcP0dL/+JCbgg7yyEo/sEK6HeGZRf3dFpWwThaRHVApXSkW3xeg==} engines: {node: '>= 0.4'} @@ -1806,8 +1848,8 @@ packages: resolution: {integrity: sha512-Zf5H2Kxt2xjTvbJvP2ZWLEICxA6j+hAmMzIlypy4xcBg1vKVnx89Wy0GbS+kf5cwCVFFzdCFh2XSCFNULS6csw==} engines: {node: '>= 0.4'} - es-object-atoms@1.1.2: - resolution: {integrity: sha512-HWcBoN6NileqtSydK2FqHbS/LoDd2pqrnQHLyJzBj4kOp/ky2MWMN694xOfkK8/SnUsW2DH7EfyVlydKCsm1Zw==} + es-object-atoms@1.1.1: + resolution: {integrity: sha512-FGgH2h8zKNim9ljj7dankFPcICIK9Cp5bm+c2gQSYePhpaG5+esrLODihIorn+Pe6FGJzWhXQotPv73jTaldXA==} engines: {node: '>= 0.4'} es-set-tostringtag@2.1.0: @@ -1818,8 +1860,8 @@ packages: resolution: {integrity: sha512-d9T8ucsEhh8Bi1woXCf+TIKDIROLG5WCkxg8geBCbvk22kzwC5G2OnXVMO6FUsvQlgUUXQ2itephWDLqDzbeCw==} engines: {node: '>= 0.4'} - es-to-primitive@1.3.1: - resolution: {integrity: sha512-CxN9N56HYfd2m/acc/NOFrZQsN9kU4eh+2kk6A707Kz1krH8tKmfrs5RnftB8WNX80T0NS7vSQsDOlg23diR2g==} + es-to-primitive@1.3.0: + resolution: {integrity: sha512-w+5mJ3GuFL+NjVtJlvydShqE1eN3h3PbI7/5LAsYJP/2qtuMXjfL2LpHSRqo4b4eSF5K/DH1JXKUAHSB2UW50g==} engines: {node: '>= 0.4'} esbuild@0.28.1: @@ -2093,8 +2135,8 @@ packages: resolution: {integrity: sha512-9ZT504KxEQDamsOogZImAWGEN24R1uFAxU3ZS4AZqn2ooidmN68Olh7n4/RcA4lLatZztjA0ZSuxeLHVoCc8JA==} engines: {node: '>=20'} - filenamify@7.0.2: - resolution: {integrity: sha512-fz10TUqSZ1lG7ftW1KnRotJzMD8YRb6kaAQKpZJBLvqXXfFgIEpuazy1w2lK3zhMiBSdH/uF9LFlv5smJ2Jl1w==} + filenamify@7.0.1: + resolution: {integrity: sha512-9b4rfnaX2MkJCgp27wypV6DAMvj4WMOSgJ+TdcpJIO84Dql+Cv6iJjdG4XDTLubOWkfNiBv3joO59sau/TXw+Q==} engines: {node: '>=20'} fill-range@7.1.1: @@ -2145,8 +2187,8 @@ packages: resolution: {integrity: sha512-939eZS4gJ3htTHAldmyyuzlrD58P03fHG49v2JfFXbV6OhvZKRC9j2yAtdHw/zrp2zXHuv05zMIy40F0ge7spA==} engines: {node: '>=18'} - function.prototype.name@1.2.0: - resolution: {integrity: sha512-jObKIik1P2QjPHP5nz5BaOtUlfgS0fWo8IUByNXkM+o+02sJOi94em77GwJKQSJ3gfPHdgzLNrHc1uokV4P/ew==} + function.prototype.name@1.1.8: + resolution: {integrity: sha512-e5iwyodOHhbMr/yNrc7fDYG4qlbIvI5gajyzPnb5TCwyhjApznQh1BMFou9b30SevY43gCJKXycoCBjMbsuW0Q==} engines: {node: '>= 0.4'} functions-have-names@1.2.3: @@ -2244,8 +2286,8 @@ packages: resolution: {integrity: sha512-DKKrynuQRne0PNpEbzuEdHlYOMksHSUI8Zc9Unei5gTsMNA2/vMpoMz/yKba50pejK56qj98qM0SjYxAKi13gQ==} engines: {node: ^12.22.0 || ^14.16.0 || ^16.0.0 || >=17.0.0} - happy-dom@20.10.6: - resolution: {integrity: sha512-6QD0ilzDDt93tX44y8tbmZdAcdTRYDhUP+Asgi6pC8Pp5IA3cvaZGyoVN/EGtlq9ziT65iPuBBn3ASLr6hCgVw==} + happy-dom@20.9.0: + resolution: {integrity: sha512-GZZ9mKe8r646NUAf/zemnGbjYh4Bt8/MqASJY+pSm5ZDtc3YQox+4gsLI7yi1hba6o+eCsGxpHn5+iEVn31/FQ==} engines: {node: '>=20.0.0'} has-bigints@1.1.0: @@ -2271,8 +2313,8 @@ packages: resolution: {integrity: sha512-NqADB8VjPFLM2V0VvHUewwwsw0ZWBaIdgo+ieHtK3hasLz4qeCRjYcqfB6AQrBggRKppKF8L52/VqdVsO47Dlw==} engines: {node: '>= 0.4'} - hasown@2.0.4: - resolution: {integrity: sha512-T2UbfbBEF32wiepXIsMlTW9+dDYC6wMh/t/vYA4tuOMKqWz/n3vr1NFSxQiyP+zk2mXsoMA/i/7qV6LKut1t1A==} + hasown@2.0.3: + resolution: {integrity: sha512-ej4AhfhfL2Q2zpMmLo7U1Uv9+PyhIZpgQLGT1F9miIGmiCJIoCgSmczFdrc97mWT4kVY72KA+WnnhJ5pghSvSg==} engines: {node: '>= 0.4'} he@1.2.0: @@ -2400,10 +2442,6 @@ packages: is-decimal@2.0.1: resolution: {integrity: sha512-AAB9hiomQs5DXWcRB1rqsxGUstbRroFOPPVAomNk/3XHR5JyEZChOyTWe2oayKnsSsr/kcGqF+z6yuH6HHpN0A==} - is-document.all@1.0.0: - resolution: {integrity: sha512-+XSoyS05OdBbhFuELhgTCpFNHkpBOJqtsZfUFFpe5QTw+9Sjbh8zitxhQkYAo6wV7e1Vb8cAPvpCk9jGam/82g==} - engines: {node: '>= 0.4'} - is-extglob@2.1.1: resolution: {integrity: sha512-SbKbANkN603Vi4jEZv49LeVJMn4yGwsbzZworEoyEiutsN3nJYdbO36zfhGJ6QEDpOZIFkDtnq5JRxmvl3jsoQ==} engines: {node: '>=0.10.0'} @@ -2630,8 +2668,8 @@ packages: resolution: {integrity: sha512-ozCC6gdQ+glXOQsveKD0YsDy8DSQFjDTz4zyzEHNV5+JP5D62LmfDZ6o1cycFx9ouG940M5dE8C8CTewdj2YWQ==} engines: {node: ^12.20.0 || ^14.13.1 || >=16.0.0} - lru-cache@11.5.1: - resolution: {integrity: sha512-RPimw/7aMdv2oqRrxKwvZXcPfwBrn/JZ2xYcY9Hus/6LaS3VOAKVWKWgNLCFSiOm1ESXinjsDlidVU7JlnCN2A==} + lru-cache@11.3.6: + resolution: {integrity: sha512-Gf/KoL3C/MlI7Bt0PGI9I+TeTC/I6r/csU58N4BSNc4lppLBeKsOdFYkK+dX0ABDUMJNfCHTyPpzwwO21Awd3A==} engines: {node: 20 || >=22} make-asynchronous@1.1.0: @@ -2824,8 +2862,8 @@ packages: resolution: {integrity: sha512-0LOsVEQmjrbJKVDi/IvFEhIezmuRjUE4loGgslv57j9nK/NMC+mbKT0QnaPSPpib4lByKVBcy3VbDa1TvlHZjA==} engines: {node: '>=4.0.0'} - mongoose@8.24.0: - resolution: {integrity: sha512-EEZwOibDPZ5uZN3bFapfnRskEbdljAf6sP9ln6u+P4e5IfkOAh6Tqw2g8/Tag++KHOAJ095WXT/c0uqRq4Vckg==} + mongoose@8.23.1: + resolution: {integrity: sha512-gHSPD8qEwRmiXapK17hEnFWZdcFENMegHTcw5XIIg2+7R8eXQvdwSiMpD/A2oG8tKzFLLHyRXd8/eaDPAVwZgQ==} engines: {node: '>=16.20.1'} mpath@0.8.4: @@ -2942,7 +2980,6 @@ packages: openai@6.44.0: resolution: {integrity: sha512-09/gH+8jH0RgUwsgWHAaxsKGRT5zVZ95IaJUnqAWj6XejIBmnFRwq2WUIF37VtDEsmGrtPmvCs5+yBSeZGWvkA==} - hasBin: true peerDependencies: ws: ^8.21.0 zod: ^3.25 || ^4.0 @@ -3063,8 +3100,8 @@ packages: resolution: {integrity: sha512-8OEwKp5juEvb/MjpIc4hjqfgCNysrS94RIOMXYvpYCdm/jglrKEiAYmiumbmGhCvs+IcInsphYDFwqrjr7398w==} hasBin: true - piscina@4.9.3: - resolution: {integrity: sha512-3e3ka9QCE8RJ5I9uszdAADZnkcYi21cqmF3gxox3u884N72qpFHCsIVhHt8cEQ9t3Auq/NqoiCEuhxlxxQuDWA==} + piscina@4.9.2: + resolution: {integrity: sha512-Fq0FERJWFEUpB4eSY59wSNwXD4RYqR+nR/WiEVcZW8IWfVBxJJafcgTEZDQo8k3w0sUarJ8RyVbbUF4GQ2LGbQ==} pluralize@8.0.0: resolution: {integrity: sha512-Nc3IT5yHzflTfbjgqWcCPpo7DaKy4FnpB0l/zCAW0Tc7jxAiuqSxHasntB3D7887LSrA93kDJ9IXovxJYxyLCA==} @@ -3074,14 +3111,19 @@ packages: resolution: {integrity: sha512-/+5VFTchJDoVj3bhoqi6UeymcD00DAwb1nJwamzPvHEszJ4FpF6SNNbUbOS8yI56qHzdV8eK0qEfOSiodkTdxg==} engines: {node: '>= 0.4'} - postcss@8.5.15: - resolution: {integrity: sha512-FfR8sjd4em2T6fb3I2MwAJU7HWVMr9zba+enmQeeWFfCbm+UOC/0X4DS8XtpUTMwWMGbjKYP7xjfNekzyGmB3A==} + postcss@8.5.14: + resolution: {integrity: sha512-SoSL4+OSEtR99LHFZQiJLkT59C5B1amGO1NzTwj7TT1qCUgUO6hxOvzkOYxD+vMrXBM3XJIKzokoERdqQq/Zmg==} engines: {node: ^10 || ^12 || >=14} prelude-ls@1.2.1: resolution: {integrity: sha512-vkcDPrRZo1QZLbn5RLGPpg/WmIQ65qoWWhcGKf/b5eplkkarX0m9z8ppCat4mlOqUsWpyNuYgO3VRyrYHSzX5g==} engines: {node: '>= 0.8.0'} + prettier@3.8.3: + resolution: {integrity: sha512-7igPTM53cGHMW8xWuVTydi2KO233VFiTNyF5hLJqpilHfmn8C8gPf+PS7dUT64YcXFbiMGZxS9pCSxL/Dxm/Jw==} + engines: {node: '>=14'} + hasBin: true + prettier@3.8.4: resolution: {integrity: sha512-N2MylSdi48+5N/6S5j+maeHbUSIzzZ5uOcX5Hm4QpV8Dkb1HFjfAKTKX6yNPJQD9AhcT3ifHNB66tWTTJDi11Q==} engines: {node: '>=14'} @@ -3146,8 +3188,8 @@ packages: peerDependencies: react: '>=16.13.1' - react-error-boundary@6.1.2: - resolution: {integrity: sha512-3DpCr5HVdZ0caUjYE/kIHBEJN0mNP3ZCgf16c48uJ5TbWjorKVp+YG8W3XqlJ7vJAVNw6wNIImyPXmFydwmyng==} + react-error-boundary@6.1.1: + resolution: {integrity: sha512-BrYwPOdXi5mqkk5lw+Uvt0ThHx32rCt3BkukS4X23A2AIWDPSGX6iaWTc0y9TU/mHDA/6qOSGel+B2ERkOvD1w==} peerDependencies: react: ^18.0.0 || ^19.0.0 @@ -3232,8 +3274,8 @@ packages: engines: {node: '>= 0.4'} hasBin: true - resolve@2.0.0-next.7: - resolution: {integrity: sha512-tqt+NBWwyaMgw3zDsnygx4CByWjQEJHOPMdslYhppaQSJUtL/D4JO9CcBBlhPoI8lz9oJIDXkwXfhF4aWqP8xQ==} + resolve@2.0.0-next.6: + resolution: {integrity: sha512-3JmVl5hMGtJ3kMmB3zi3DL25KfkCEyy3Tw7Gmw7z5w8M9WlwoPFnIvwChzu1+cF3iaK3sp18hhPz8ANeimdJfA==} engines: {node: '>= 0.4'} hasBin: true @@ -3302,8 +3344,8 @@ packages: resolution: {integrity: sha512-BR7VvDCVHO+q2xBEWskxS6DJE1qRnb7DxzUrogb71CWoSficBxYsiAGd+Kl0mmq/MprG9yArRkyrQxTO6XjMzA==} hasBin: true - semver@7.8.4: - resolution: {integrity: sha512-rUCObTnP32Q08R2uuIrt7r9PlEonuTmtuXYcW6s5kjdlj3xbnwe+21yXptAUYcMAABLkYYTtnmzb3w3EDZfueA==} + semver@7.8.0: + resolution: {integrity: sha512-AcM7dV/5ul4EekoQ29Agm5vri8JNqRyj39o0qpX6vDF2GZrtutZl5RwgD1XnZjiTAfncsJhMI48QQH3sN87YNA==} engines: {node: '>=10'} hasBin: true @@ -3343,8 +3385,8 @@ packages: resolution: {integrity: sha512-WPS/HvHQTYnHisLo9McqBHOJk2FkHO/tlpvldyrnem4aeQp4hai3gythswg6p01oSoTl58rcpiFAjF2br2Ak2A==} engines: {node: '>= 0.4'} - side-channel@1.1.1: - resolution: {integrity: sha512-6x6dK6zJdpTzF4sQeNYxwtvBzf6Eg4GtlesS94HOvTudUeyK2WXAaIfmDgsyslYrRBeFIlsi54AYsFGUuhmvrQ==} + side-channel@1.1.0: + resolution: {integrity: sha512-ZX99e6tRweoUXqR+VBrslhda51Nh5MTQwou5tnUDgbtyM0dBgmhEDtWGP/xbKn6hqfPRHujUNwz5fy/wbbhnpw==} engines: {node: '>= 0.4'} sift@17.1.3: @@ -3414,8 +3456,8 @@ packages: resolution: {integrity: sha512-Mcc5wHehp9aXz1ax6bZUyY5afg9u2rv5cqQI3mRrYkGC8rW2hM02jWuwjtL++LS5qinSyhj2QfLyNsuc+VsExg==} engines: {node: '>=10.0.0'} - streamx@2.28.0: - resolution: {integrity: sha512-1Yowhzjf0ivGMrTIkY9hav5TxobO9qIVqUE41fiCGMGgc3CLlf4MY+9AHmZqBWgDTue0fY9zWjYFVyf6Diuobw==} + streamx@2.25.0: + resolution: {integrity: sha512-0nQuG6jf1w+wddNEEXCF4nTg3LtufWINB5eFEN+5TNZW7KWJp6x87+JFL43vaAUPyCfH1wID+mNVyW6OHtFamg==} string-ts@2.3.1: resolution: {integrity: sha512-xSJq+BS52SaFFAVxuStmx6n5aYZU571uYUnUrPXkPFCfdHyZMMlbP2v2Wx5sNBnAVzq/2+0+mcBLBa3Xa5ubYw==} @@ -3428,12 +3470,12 @@ packages: resolution: {integrity: sha512-o7+c9bW6zpAdJHTtujeePODAhkuicdAryFsfVKwA+wGw89wJ4GTY484WTucM9hLtDEOpOvI+aHnzqnC5lHp4Rg==} engines: {node: '>= 0.4'} - string.prototype.trim@1.2.11: - resolution: {integrity: sha512-PwvK7BU+CMTJGYQCTZb5RWXIML92lftJLhQz1tBzgKiqGxJaMlBAa48POXaNAC2s4y8jr3EFqrkF9+44neS46w==} + string.prototype.trim@1.2.10: + resolution: {integrity: sha512-Rs66F0P/1kedk5lyYyH9uBzuiI/kNRmwJAR9quK6VOtIpZ2G+hMZd+HQbbv25MgCA6gEffoMZYxlTod4WcdrKA==} engines: {node: '>= 0.4'} - string.prototype.trimend@1.0.10: - resolution: {integrity: sha512-2+3aDAOmPTmuFwjDnmJG2ctEkQKVki7vOSqaxkv42Mowj1V6PnvuwFCRrR5lChUux1TBskPjfkeTOhqczDMxTw==} + string.prototype.trimend@1.0.9: + resolution: {integrity: sha512-G7Ok5C6E/j4SGfyLCloXTrngQIQU3PWtXGst3yM7Bea9FRURf1S42ZHlZZtsNque2FN2PoUhfZXYLNWwEr4dLQ==} engines: {node: '>= 0.4'} string.prototype.trimstart@1.0.8: @@ -3504,10 +3546,6 @@ packages: resolution: {integrity: sha512-ot0WnXS9fgdkgIcePe6RHNk1WA8+muPa6cSjeR3V8K27q9BB1rTE3R1p7Hv0z1ZyAc8s6Vvv8DIyWf681MAt0w==} engines: {node: '>= 0.4'} - system-architecture@1.0.0: - resolution: {integrity: sha512-0OJWD12D7XX3KUg1DYkMaTTjSTo2k/mhIYI3HlBlceXSMcJhW/1qO735fPKS5prcyjvn57Ub151vvASYXpQrEw==} - engines: {node: '>=18'} - tabbable@6.4.0: resolution: {integrity: sha512-05PUHKSNE8ou2dwIxTngl4EzcnsCDZGJ/iCLtDflR/SHB/ny14rXc+qU5P4mG9JkusiV7EivzY9Mhm55AzAvCg==} @@ -3534,8 +3572,8 @@ packages: resolution: {integrity: sha512-75voc/9G4rDIJleOo4jPvN4/YC4GRZrY8yy1uU4lwrB3XEQbWve8zXoO5No4eFrGcTAMYyoY67p8jRQdtA1HbA==} engines: {node: '>=12'} - tinyglobby@0.2.17: - resolution: {integrity: sha512-wXR/dYpcqKmfWpEdZjiKJOwCNFndD0DMnrW/cYjVGttEkBfVgcLFHoNrlj47mjOVic9yyNu65alsgF4NQyTa2g==} + tinyglobby@0.2.16: + resolution: {integrity: sha512-pn99VhoACYR8nFHhxqix+uvsbXineAasWm5ojXoN8xEwK5Kd3/TrhNn1wByuD52UxWRLy8pu+kRMniEi6Eq9Zg==} engines: {node: '>=12.0.0'} to-regex-range@5.0.1: @@ -3573,6 +3611,11 @@ packages: tslib@2.8.1: resolution: {integrity: sha512-oJFu94HQb+KVduSUQL7wnpmqnfmLsOA/nAh6b6EH0wCEoK0/mPeXU6c3wKDV83MkOuHPRHtSXKKU99IBazS/2w==} + tsx@4.21.0: + resolution: {integrity: sha512-5C1sg4USs1lfG0GFb2RLXsdpXqBSEhAaA/0kPL01wxzpMqLILNxIxIOKiILz+cdg/pLnOUxFYOR5yhHU666wbw==} + engines: {node: '>=18.0.0'} + hasBin: true + tsx@4.22.4: resolution: {integrity: sha512-X8EX+XV4QR5xCsrgxaED954zTDfY8KqlDtskKEL0cHhyS/P8b4IFOvGDQpsC9Q1XnLq915wEfwwY/zzskCtmhg==} engines: {node: '>=18.0.0'} @@ -3598,12 +3641,12 @@ packages: resolution: {integrity: sha512-bTlAFB/FBYMcuX81gbL4OcpH5PmlFHqlCCpAl8AlEzMz5k53oNDvN8p1PNOWLEmI2x4orp3raOFB51tv9X+MFQ==} engines: {node: '>= 0.4'} - typed-array-length@1.0.8: - resolution: {integrity: sha512-phPGCwqr2+Qo0fwniCE8e4pKnGu/yFb5nD5Y8bf0EEeiI5GklnACYA9GFy/DrAeRrKHXvHn+1SUsOWgJp6RO+g==} + typed-array-length@1.0.7: + resolution: {integrity: sha512-3KS2b+kL7fsuk/eJZ7EQdnEmQoaho/r6KUef7hxvltNA5DR8NAUM+8wJMbJyZ4G9/7i3v5zPBIMN5aybAh2/Jg==} engines: {node: '>= 0.4'} - typescript-eslint@8.61.1: - resolution: {integrity: sha512-V7PayAfJokV3pEHgN7/v03D1SpujhRfQtYLbLIiBfDDncdg4PAiRBfoS4cnCANK4jmAPncczi59QO3afiXUlNw==} + typescript-eslint@8.59.3: + resolution: {integrity: sha512-KgusgyDgG4LI8Ih/sWaCtZ06tckLAS5CvT5A4D1Q7bYVoAAyzwiZvE4BmwDHkhRVkvhRBepKeASoFzQetha7Fg==} engines: {node: ^18.18.0 || ^20.9.0 || >=21.1.0} peerDependencies: eslint: ^9.39.4 @@ -3633,9 +3676,6 @@ packages: undici-types@7.21.0: resolution: {integrity: sha512-w9IMgQrz4O0YN1LtB7K5P63vhlIOvC7opSmouCJ+ZywlPAlO9gIkJ+otk6LvGpAs2wg4econaCz3TvQ9xPoyuQ==} - undici-types@7.24.6: - resolution: {integrity: sha512-WRNW+sJgj5OBN4/0JpHFqtqzhpbnV0GuB+OozA9gCL7a993SmU+1JBZCzLNxYsbMfIeDL+lTsphD5jN5N+n0zg==} - undici@7.28.0: resolution: {integrity: sha512-cRZYrTDwWznlnRiPjggAGxZXanty6M8RV1ff8Wm4LWXBp7/IG8v5DnOm74DtUBp9OONpK75YlPnIjQqX0dBDtA==} engines: {node: '>=20.18.1'} @@ -3721,8 +3761,8 @@ packages: resolution: {integrity: sha512-K4jVyjnBdgvc86Y6BkaLZEN933SwYOuBFkdmBu9ZfkcAbdVbpITnDmjvZ/aQjRXQrv5EPkTnD1s39GiiqbngCw==} engines: {node: '>= 0.4'} - which-typed-array@1.1.22: - resolution: {integrity: sha512-fvO4ExWMFsqyhG3AiPAObMuY1lxaqgYcxbc49CNdWDDECOJNgQyvsOWVwbZc+qf3rzRtxojBK+CMEv0Ld5CYpw==} + which-typed-array@1.1.20: + resolution: {integrity: sha512-LYfpUkmqwl0h9A2HL09Mms427Q1RZWuOHsukfVcKRq9q95iQxdw0ix1JQrqbcDR9PH1QDwf5Qo8OZb5lksZ8Xg==} engines: {node: '>= 0.4'} which@2.0.2: @@ -3778,8 +3818,8 @@ packages: resolution: {integrity: sha512-D1mvvtDG0L5ft/jGWkLpG1+m0eQxOfaBvTNELraWj22wSVUMWxZUvYgJYcKh6jGGIkJFhH4IZPQhR4TKpc8mBw==} engines: {node: '>=10'} - yauzl@3.4.0: - resolution: {integrity: sha512-jIH9yLR9wqr0wOS0TpBvo/g/2UgZH5qePVbjgRliiF0BYvOZyaBknKsF+x9Iht0O6sqgnB93rCICdOZFecJuDw==} + yauzl@3.3.0: + resolution: {integrity: sha512-PtGEvEP30p7sbIBJKUBjUnqgTVOyMURc4dLo9iNyAJnNIEz9pm88cCXF21w94Kg3k6RXkeZh5DHOGS0qEONvNQ==} engines: {node: '>=12'} yjs@13.6.27: @@ -3836,7 +3876,7 @@ snapshots: dependencies: '@babel/types': 7.29.0 - '@babel/runtime@7.29.7': {} + '@babel/runtime@7.29.2': {} '@babel/template@7.28.6': dependencies: @@ -3897,7 +3937,7 @@ snapshots: react: 19.2.7 tslib: 2.8.1 - '@emnapi/runtime@1.11.1': + '@emnapi/runtime@1.10.0': dependencies: tslib: 2.8.1 optional: true @@ -3905,7 +3945,7 @@ snapshots: '@emotion/babel-plugin@11.13.5': dependencies: '@babel/helper-module-imports': 7.28.6 - '@babel/runtime': 7.29.7 + '@babel/runtime': 7.29.2 '@emotion/hash': 0.9.2 '@emotion/memoize': 0.9.0 '@emotion/serialize': 1.3.3 @@ -3932,7 +3972,7 @@ snapshots: '@emotion/react@11.14.0(@types/react@19.2.17)(react@19.2.7)': dependencies: - '@babel/runtime': 7.29.7 + '@babel/runtime': 7.29.2 '@emotion/babel-plugin': 11.13.5 '@emotion/cache': 11.14.0 '@emotion/serialize': 1.3.3 @@ -4056,9 +4096,9 @@ snapshots: '@eslint-react/ast@1.31.0(eslint@9.39.4)(typescript@5.7.3)': dependencies: '@eslint-react/eff': 1.31.0 - '@typescript-eslint/types': 8.61.1 - '@typescript-eslint/typescript-estree': 8.61.1(typescript@5.7.3) - '@typescript-eslint/utils': 8.61.1(eslint@9.39.4)(typescript@5.7.3) + '@typescript-eslint/types': 8.59.3 + '@typescript-eslint/typescript-estree': 8.59.3(typescript@5.7.3) + '@typescript-eslint/utils': 8.59.3(eslint@9.39.4)(typescript@5.7.3) string-ts: 2.3.1 ts-pattern: 5.9.0 transitivePeerDependencies: @@ -4073,10 +4113,10 @@ snapshots: '@eslint-react/jsx': 1.31.0(eslint@9.39.4)(typescript@5.7.3) '@eslint-react/shared': 1.31.0(eslint@9.39.4)(typescript@5.7.3) '@eslint-react/var': 1.31.0(eslint@9.39.4)(typescript@5.7.3) - '@typescript-eslint/scope-manager': 8.61.1 - '@typescript-eslint/type-utils': 8.61.1(eslint@9.39.4)(typescript@5.7.3) - '@typescript-eslint/types': 8.61.1 - '@typescript-eslint/utils': 8.61.1(eslint@9.39.4)(typescript@5.7.3) + '@typescript-eslint/scope-manager': 8.59.3 + '@typescript-eslint/type-utils': 8.59.3(eslint@9.39.4)(typescript@5.7.3) + '@typescript-eslint/types': 8.59.3 + '@typescript-eslint/utils': 8.59.3(eslint@9.39.4)(typescript@5.7.3) birecord: 0.1.1 ts-pattern: 5.9.0 transitivePeerDependencies: @@ -4090,10 +4130,10 @@ snapshots: dependencies: '@eslint-react/eff': 1.31.0 '@eslint-react/shared': 1.31.0(eslint@9.39.4)(typescript@5.7.3) - '@typescript-eslint/scope-manager': 8.61.1 - '@typescript-eslint/type-utils': 8.61.1(eslint@9.39.4)(typescript@5.7.3) - '@typescript-eslint/types': 8.61.1 - '@typescript-eslint/utils': 8.61.1(eslint@9.39.4)(typescript@5.7.3) + '@typescript-eslint/scope-manager': 8.59.3 + '@typescript-eslint/type-utils': 8.59.3(eslint@9.39.4)(typescript@5.7.3) + '@typescript-eslint/types': 8.59.3 + '@typescript-eslint/utils': 8.59.3(eslint@9.39.4)(typescript@5.7.3) eslint: 9.39.4 eslint-plugin-react-debug: 1.31.0(eslint@9.39.4)(typescript@5.7.3) eslint-plugin-react-dom: 1.31.0(eslint@9.39.4)(typescript@5.7.3) @@ -4112,9 +4152,9 @@ snapshots: '@eslint-react/ast': 1.31.0(eslint@9.39.4)(typescript@5.7.3) '@eslint-react/eff': 1.31.0 '@eslint-react/var': 1.31.0(eslint@9.39.4)(typescript@5.7.3) - '@typescript-eslint/scope-manager': 8.61.1 - '@typescript-eslint/types': 8.61.1 - '@typescript-eslint/utils': 8.61.1(eslint@9.39.4)(typescript@5.7.3) + '@typescript-eslint/scope-manager': 8.59.3 + '@typescript-eslint/types': 8.59.3 + '@typescript-eslint/utils': 8.59.3(eslint@9.39.4)(typescript@5.7.3) ts-pattern: 5.9.0 transitivePeerDependencies: - eslint @@ -4124,7 +4164,7 @@ snapshots: '@eslint-react/shared@1.31.0(eslint@9.39.4)(typescript@5.7.3)': dependencies: '@eslint-react/eff': 1.31.0 - '@typescript-eslint/utils': 8.61.1(eslint@9.39.4)(typescript@5.7.3) + '@typescript-eslint/utils': 8.59.3(eslint@9.39.4)(typescript@5.7.3) picomatch: 4.0.4 ts-pattern: 5.9.0 transitivePeerDependencies: @@ -4136,9 +4176,9 @@ snapshots: dependencies: '@eslint-react/ast': 1.31.0(eslint@9.39.4)(typescript@5.7.3) '@eslint-react/eff': 1.31.0 - '@typescript-eslint/scope-manager': 8.61.1 - '@typescript-eslint/types': 8.61.1 - '@typescript-eslint/utils': 8.61.1(eslint@9.39.4)(typescript@5.7.3) + '@typescript-eslint/scope-manager': 8.59.3 + '@typescript-eslint/types': 8.59.3 + '@typescript-eslint/utils': 8.59.3(eslint@9.39.4)(typescript@5.7.3) string-ts: 2.3.1 ts-pattern: 5.9.0 transitivePeerDependencies: @@ -4333,7 +4373,7 @@ snapshots: '@img/sharp-wasm32@0.34.5': dependencies: - '@emnapi/runtime': 1.11.1 + '@emnapi/runtime': 1.10.0 optional: true '@img/sharp-win32-arm64@0.34.5': @@ -4396,7 +4436,7 @@ snapshots: '@lexical/extension@0.41.0': dependencies: '@lexical/utils': 0.41.0 - '@preact/signals-core': 1.14.3 + '@preact/signals-core': 1.14.2 lexical: 0.41.0 '@lexical/hashtag@0.41.0': @@ -4407,7 +4447,7 @@ snapshots: '@lexical/headless@0.41.0': dependencies: - happy-dom: 20.10.6 + happy-dom: 20.9.0 lexical: 0.41.0 transitivePeerDependencies: - bufferutil @@ -4491,7 +4531,7 @@ snapshots: lexical: 0.41.0 react: 19.2.7 react-dom: 19.2.7(react@19.2.7) - react-error-boundary: 6.1.2(react@19.2.7) + react-error-boundary: 6.1.1(react@19.2.7) transitivePeerDependencies: - yjs @@ -4647,8 +4687,8 @@ snapshots: '@payloadcms/db-mongodb@3.85.1(payload@3.85.1(graphql@16.12.0)(typescript@6.0.3))': dependencies: - mongoose: 8.24.0 - mongoose-paginate-v2: 1.9.4(mongoose@8.24.0) + mongoose: 8.23.1 + mongoose-paginate-v2: 1.9.4(mongoose@8.23.1) payload: 3.85.1(graphql@16.12.0)(typescript@6.0.3) prompts: 2.4.2 uuid: 11.1.1 @@ -4662,17 +4702,17 @@ snapshots: - socks - supports-color - '@payloadcms/eslint-config@3.28.0(@typescript-eslint/eslint-plugin@8.61.1(@typescript-eslint/parser@8.61.1(eslint@9.39.4)(typescript@6.0.3))(eslint@9.39.4)(typescript@6.0.3))(ts-api-utils@2.5.0(typescript@6.0.3))': + '@payloadcms/eslint-config@3.28.0(@typescript-eslint/eslint-plugin@8.59.3(@typescript-eslint/parser@8.59.3(eslint@9.39.4)(typescript@6.0.3))(eslint@9.39.4)(typescript@6.0.3))(ts-api-utils@2.5.0(typescript@6.0.3))': dependencies: '@eslint-react/eslint-plugin': 1.31.0(eslint@9.39.4)(ts-api-utils@2.5.0(typescript@6.0.3))(typescript@5.7.3) '@eslint/js': 9.39.4 - '@payloadcms/eslint-plugin': 3.28.0(@typescript-eslint/eslint-plugin@8.61.1(@typescript-eslint/parser@8.61.1(eslint@9.39.4)(typescript@6.0.3))(eslint@9.39.4)(typescript@6.0.3))(ts-api-utils@2.5.0(typescript@6.0.3)) + '@payloadcms/eslint-plugin': 3.28.0(@typescript-eslint/eslint-plugin@8.59.3(@typescript-eslint/parser@8.59.3(eslint@9.39.4)(typescript@6.0.3))(eslint@9.39.4)(typescript@6.0.3))(ts-api-utils@2.5.0(typescript@6.0.3)) '@types/eslint': 9.6.1 - '@typescript-eslint/parser': 8.61.1(eslint@9.39.4)(typescript@5.7.3) + '@typescript-eslint/parser': 8.59.3(eslint@9.39.4)(typescript@5.7.3) eslint: 9.39.4 eslint-config-prettier: 10.1.1(eslint@9.39.4) eslint-plugin-import-x: 4.6.1(eslint@9.39.4)(typescript@5.7.3) - eslint-plugin-jest: 28.11.0(@typescript-eslint/eslint-plugin@8.61.1(@typescript-eslint/parser@8.61.1(eslint@9.39.4)(typescript@6.0.3))(eslint@9.39.4)(typescript@6.0.3))(eslint@9.39.4)(typescript@5.7.3) + eslint-plugin-jest: 28.11.0(@typescript-eslint/eslint-plugin@8.59.3(@typescript-eslint/parser@8.59.3(eslint@9.39.4)(typescript@6.0.3))(eslint@9.39.4)(typescript@6.0.3))(eslint@9.39.4)(typescript@5.7.3) eslint-plugin-jest-dom: 5.5.0(eslint@9.39.4) eslint-plugin-jsx-a11y: 6.10.2(eslint@9.39.4) eslint-plugin-perfectionist: 3.9.1(eslint@9.39.4)(typescript@5.7.3) @@ -4680,7 +4720,7 @@ snapshots: eslint-plugin-regexp: 2.7.0(eslint@9.39.4) globals: 16.0.0 typescript: 5.7.3 - typescript-eslint: 8.61.1(eslint@9.39.4)(typescript@5.7.3) + typescript-eslint: 8.59.3(eslint@9.39.4)(typescript@5.7.3) transitivePeerDependencies: - '@testing-library/dom' - '@typescript-eslint/eslint-plugin' @@ -4693,16 +4733,16 @@ snapshots: - ts-api-utils - vue-eslint-parser - '@payloadcms/eslint-plugin@3.28.0(@typescript-eslint/eslint-plugin@8.61.1(@typescript-eslint/parser@8.61.1(eslint@9.39.4)(typescript@6.0.3))(eslint@9.39.4)(typescript@6.0.3))(ts-api-utils@2.5.0(typescript@6.0.3))': + '@payloadcms/eslint-plugin@3.28.0(@typescript-eslint/eslint-plugin@8.59.3(@typescript-eslint/parser@8.59.3(eslint@9.39.4)(typescript@6.0.3))(eslint@9.39.4)(typescript@6.0.3))(ts-api-utils@2.5.0(typescript@6.0.3))': dependencies: '@eslint-react/eslint-plugin': 1.31.0(eslint@9.39.4)(ts-api-utils@2.5.0(typescript@6.0.3))(typescript@5.7.3) '@eslint/js': 9.39.4 '@types/eslint': 9.6.1 - '@typescript-eslint/parser': 8.61.1(eslint@9.39.4)(typescript@5.7.3) + '@typescript-eslint/parser': 8.59.3(eslint@9.39.4)(typescript@5.7.3) eslint: 9.39.4 eslint-config-prettier: 10.1.1(eslint@9.39.4) eslint-plugin-import-x: 4.6.1(eslint@9.39.4)(typescript@5.7.3) - eslint-plugin-jest: 28.11.0(@typescript-eslint/eslint-plugin@8.61.1(@typescript-eslint/parser@8.61.1(eslint@9.39.4)(typescript@6.0.3))(eslint@9.39.4)(typescript@6.0.3))(eslint@9.39.4)(typescript@5.7.3) + eslint-plugin-jest: 28.11.0(@typescript-eslint/eslint-plugin@8.59.3(@typescript-eslint/parser@8.59.3(eslint@9.39.4)(typescript@6.0.3))(eslint@9.39.4)(typescript@6.0.3))(eslint@9.39.4)(typescript@5.7.3) eslint-plugin-jest-dom: 5.5.0(eslint@9.39.4) eslint-plugin-jsx-a11y: 6.10.2(eslint@9.39.4) eslint-plugin-perfectionist: 3.9.1(eslint@9.39.4)(typescript@5.7.3) @@ -4710,7 +4750,7 @@ snapshots: eslint-plugin-regexp: 2.7.0(eslint@9.39.4) globals: 16.0.0 typescript: 5.7.3 - typescript-eslint: 8.61.1(eslint@9.39.4)(typescript@5.7.3) + typescript-eslint: 8.59.3(eslint@9.39.4)(typescript@5.7.3) transitivePeerDependencies: - '@testing-library/dom' - '@typescript-eslint/eslint-plugin' @@ -4723,6 +4763,17 @@ snapshots: - ts-api-utils - vue-eslint-parser + '@payloadcms/graphql@3.84.1(graphql@16.12.0)(payload@3.85.1(graphql@16.12.0)(typescript@6.0.3))(typescript@6.0.3)': + dependencies: + graphql: 16.12.0 + graphql-scalars: 1.22.2(graphql@16.12.0) + payload: 3.85.1(graphql@16.12.0)(typescript@6.0.3) + pluralize: 8.0.0 + ts-essentials: 10.0.3(typescript@6.0.3) + tsx: 4.21.0 + transitivePeerDependencies: + - typescript + '@payloadcms/graphql@3.85.1(graphql@16.12.0)(payload@3.85.1(graphql@16.12.0)(typescript@6.0.3))(typescript@6.0.3)': dependencies: graphql: 16.12.0 @@ -4734,6 +4785,35 @@ snapshots: transitivePeerDependencies: - typescript + '@payloadcms/next@3.84.1(@types/react@19.2.17)(graphql@16.12.0)(monaco-editor@0.55.1)(next@16.2.9(react-dom@19.2.7(react@19.2.7))(react@19.2.7)(sass@1.77.4))(payload@3.85.1(graphql@16.12.0)(typescript@6.0.3))(react-dom@19.2.7(react@19.2.7))(react@19.2.7)(typescript@6.0.3)': + dependencies: + '@dnd-kit/core': 6.3.1(react-dom@19.2.7(react@19.2.7))(react@19.2.7) + '@dnd-kit/modifiers': 9.0.0(@dnd-kit/core@6.3.1(react-dom@19.2.7(react@19.2.7))(react@19.2.7))(react@19.2.7) + '@dnd-kit/sortable': 10.0.0(@dnd-kit/core@6.3.1(react-dom@19.2.7(react@19.2.7))(react@19.2.7))(react@19.2.7) + '@payloadcms/graphql': 3.84.1(graphql@16.12.0)(payload@3.85.1(graphql@16.12.0)(typescript@6.0.3))(typescript@6.0.3) + '@payloadcms/translations': 3.84.1 + '@payloadcms/ui': 3.84.1(@types/react@19.2.17)(monaco-editor@0.55.1)(next@16.2.9(react-dom@19.2.7(react@19.2.7))(react@19.2.7)(sass@1.77.4))(payload@3.85.1(graphql@16.12.0)(typescript@6.0.3))(react-dom@19.2.7(react@19.2.7))(react@19.2.7)(typescript@6.0.3) + busboy: 1.6.0 + dequal: 2.0.3 + file-type: 21.3.4 + graphql: 16.12.0 + graphql-http: 1.22.4(graphql@16.12.0) + graphql-playground-html: 1.6.30 + http-status: 2.1.0 + next: 16.2.9(react-dom@19.2.7(react@19.2.7))(react@19.2.7)(sass@1.77.4) + path-to-regexp: 6.3.0 + payload: 3.85.1(graphql@16.12.0)(typescript@6.0.3) + qs-esm: 8.0.1 + sass: 1.77.4 + uuid: 11.1.1 + transitivePeerDependencies: + - '@types/react' + - monaco-editor + - react + - react-dom + - supports-color + - typescript + '@payloadcms/next@3.85.1(@types/react@19.2.17)(graphql@16.12.0)(monaco-editor@0.55.1)(next@16.2.9(react-dom@19.2.7(react@19.2.7))(react@19.2.7)(sass@1.77.4))(payload@3.85.1(graphql@16.12.0)(typescript@6.0.3))(react-dom@19.2.7(react@19.2.7))(react@19.2.7)(typescript@6.0.3)': dependencies: '@dnd-kit/core': 6.3.1(react-dom@19.2.7(react@19.2.7))(react@19.2.7) @@ -4763,6 +4843,51 @@ snapshots: - supports-color - typescript + '@payloadcms/richtext-lexical@3.84.1(@faceless-ui/modal@3.0.0(react-dom@19.2.7(react@19.2.7))(react@19.2.7))(@faceless-ui/scroll-info@2.0.0(react-dom@19.2.7(react@19.2.7))(react@19.2.7))(@payloadcms/next@3.84.1(@types/react@19.2.17)(graphql@16.12.0)(monaco-editor@0.55.1)(next@16.2.9(react-dom@19.2.7(react@19.2.7))(react@19.2.7)(sass@1.77.4))(payload@3.85.1(graphql@16.12.0)(typescript@6.0.3))(react-dom@19.2.7(react@19.2.7))(react@19.2.7)(typescript@6.0.3))(@types/react@19.2.17)(monaco-editor@0.55.1)(next@16.2.9(react-dom@19.2.7(react@19.2.7))(react@19.2.7)(sass@1.77.4))(payload@3.85.1(graphql@16.12.0)(typescript@6.0.3))(react-dom@19.2.7(react@19.2.7))(react@19.2.7)(typescript@6.0.3)(yjs@13.6.27)': + dependencies: + '@faceless-ui/modal': 3.0.0(react-dom@19.2.7(react@19.2.7))(react@19.2.7) + '@faceless-ui/scroll-info': 2.0.0(react-dom@19.2.7(react@19.2.7))(react@19.2.7) + '@lexical/clipboard': 0.41.0 + '@lexical/headless': 0.41.0 + '@lexical/html': 0.41.0 + '@lexical/link': 0.41.0 + '@lexical/list': 0.41.0 + '@lexical/mark': 0.41.0 + '@lexical/react': 0.41.0(react-dom@19.2.7(react@19.2.7))(react@19.2.7)(yjs@13.6.27) + '@lexical/rich-text': 0.41.0 + '@lexical/selection': 0.41.0 + '@lexical/table': 0.41.0 + '@lexical/utils': 0.41.0 + '@payloadcms/next': 3.84.1(@types/react@19.2.17)(graphql@16.12.0)(monaco-editor@0.55.1)(next@16.2.9(react-dom@19.2.7(react@19.2.7))(react@19.2.7)(sass@1.77.4))(payload@3.85.1(graphql@16.12.0)(typescript@6.0.3))(react-dom@19.2.7(react@19.2.7))(react@19.2.7)(typescript@6.0.3) + '@payloadcms/translations': 3.84.1 + '@payloadcms/ui': 3.84.1(@types/react@19.2.17)(monaco-editor@0.55.1)(next@16.2.9(react-dom@19.2.7(react@19.2.7))(react@19.2.7)(sass@1.77.4))(payload@3.85.1(graphql@16.12.0)(typescript@6.0.3))(react-dom@19.2.7(react@19.2.7))(react@19.2.7)(typescript@6.0.3) + acorn: 8.16.0 + bson-objectid: 2.0.4 + csstype: 3.1.3 + dequal: 2.0.3 + escape-html: 1.0.3 + jsox: 1.2.121 + lexical: 0.41.0 + mdast-util-from-markdown: 2.0.2 + mdast-util-mdx-jsx: 3.1.3 + micromark-extension-mdx-jsx: 3.0.1 + payload: 3.85.1(graphql@16.12.0)(typescript@6.0.3) + qs-esm: 8.0.1 + react: 19.2.7 + react-dom: 19.2.7(react@19.2.7) + react-error-boundary: 4.1.2(react@19.2.7) + ts-essentials: 10.0.3(typescript@6.0.3) + uuid: 11.1.1 + transitivePeerDependencies: + - '@types/react' + - bufferutil + - monaco-editor + - next + - supports-color + - typescript + - utf-8-validate + - yjs + '@payloadcms/richtext-lexical@3.85.1(@faceless-ui/modal@3.0.0(react-dom@19.2.7(react@19.2.7))(react@19.2.7))(@faceless-ui/scroll-info@2.0.0(react-dom@19.2.7(react@19.2.7))(react@19.2.7))(@payloadcms/next@3.85.1(@types/react@19.2.17)(graphql@16.12.0)(monaco-editor@0.55.1)(next@16.2.9(react-dom@19.2.7(react@19.2.7))(react@19.2.7)(sass@1.77.4))(payload@3.85.1(graphql@16.12.0)(typescript@6.0.3))(react-dom@19.2.7(react@19.2.7))(react@19.2.7)(typescript@6.0.3))(@types/react@19.2.17)(monaco-editor@0.55.1)(next@16.2.9(react-dom@19.2.7(react@19.2.7))(react@19.2.7)(sass@1.77.4))(payload@3.85.1(graphql@16.12.0)(typescript@6.0.3))(react-dom@19.2.7(react@19.2.7))(react@19.2.7)(typescript@6.0.3)(yjs@13.6.27)': dependencies: '@faceless-ui/modal': 3.0.0(react-dom@19.2.7(react@19.2.7))(react@19.2.7) @@ -4808,10 +4933,49 @@ snapshots: - utf-8-validate - yjs + '@payloadcms/translations@3.84.1': + dependencies: + date-fns: 4.1.0 + '@payloadcms/translations@3.85.1': dependencies: date-fns: 4.1.0 + '@payloadcms/ui@3.84.1(@types/react@19.2.17)(monaco-editor@0.55.1)(next@16.2.9(react-dom@19.2.7(react@19.2.7))(react@19.2.7)(sass@1.77.4))(payload@3.85.1(graphql@16.12.0)(typescript@6.0.3))(react-dom@19.2.7(react@19.2.7))(react@19.2.7)(typescript@6.0.3)': + dependencies: + '@date-fns/tz': 1.2.0 + '@dnd-kit/core': 6.3.1(react-dom@19.2.7(react@19.2.7))(react@19.2.7) + '@dnd-kit/sortable': 10.0.0(@dnd-kit/core@6.3.1(react-dom@19.2.7(react@19.2.7))(react@19.2.7))(react@19.2.7) + '@dnd-kit/utilities': 3.2.2(react@19.2.7) + '@faceless-ui/modal': 3.0.0(react-dom@19.2.7(react@19.2.7))(react@19.2.7) + '@faceless-ui/scroll-info': 2.0.0(react-dom@19.2.7(react@19.2.7))(react@19.2.7) + '@faceless-ui/window-info': 3.0.1(react-dom@19.2.7(react@19.2.7))(react@19.2.7) + '@monaco-editor/react': 4.7.0(monaco-editor@0.55.1)(react-dom@19.2.7(react@19.2.7))(react@19.2.7) + '@payloadcms/translations': 3.84.1 + bson-objectid: 2.0.4 + date-fns: 4.1.0 + dequal: 2.0.3 + md5: 2.3.0 + next: 16.2.9(react-dom@19.2.7(react@19.2.7))(react@19.2.7)(sass@1.77.4) + object-to-formdata: 4.5.1 + payload: 3.85.1(graphql@16.12.0)(typescript@6.0.3) + qs-esm: 8.0.1 + react: 19.2.7 + react-datepicker: 7.6.0(react-dom@19.2.7(react@19.2.7))(react@19.2.7) + react-dom: 19.2.7(react@19.2.7) + react-image-crop: 10.1.8(react@19.2.7) + react-select: 5.9.0(@types/react@19.2.17)(react-dom@19.2.7(react@19.2.7))(react@19.2.7) + scheduler: 0.25.0 + sonner: 1.7.4(react-dom@19.2.7(react@19.2.7))(react@19.2.7) + ts-essentials: 10.0.3(typescript@6.0.3) + use-context-selector: 2.0.0(react@19.2.7)(scheduler@0.25.0) + uuid: 11.1.1 + transitivePeerDependencies: + - '@types/react' + - monaco-editor + - supports-color + - typescript + '@payloadcms/ui@3.85.1(@types/react@19.2.17)(monaco-editor@0.55.1)(next@16.2.9(react-dom@19.2.7(react@19.2.7))(react@19.2.7)(sass@1.77.4))(payload@3.85.1(graphql@16.12.0)(typescript@6.0.3))(react-dom@19.2.7(react@19.2.7))(react@19.2.7)(typescript@6.0.3)': dependencies: '@date-fns/tz': 1.2.0 @@ -4849,7 +5013,7 @@ snapshots: '@pinojs/redact@0.4.0': {} - '@preact/signals-core@1.14.3': {} + '@preact/signals-core@1.14.2': {} '@sec-ant/readable-stream@0.4.1': {} @@ -4857,76 +5021,76 @@ snapshots: '@sindresorhus/merge-streams@4.0.0': {} - '@swc/cli@0.8.1(@swc/core@1.15.41)': + '@swc/cli@0.8.1(@swc/core@1.15.43)': dependencies: - '@swc/core': 1.15.41 + '@swc/core': 1.15.43 '@swc/counter': 0.1.3 - '@xhmikosr/bin-wrapper': 14.4.0 + '@xhmikosr/bin-wrapper': 14.2.3 commander: 8.3.0 minimatch: 9.0.9 - piscina: 4.9.3 - semver: 7.8.4 + piscina: 4.9.2 + semver: 7.8.0 slash: 3.0.0 source-map: 0.7.6 - tinyglobby: 0.2.17 + tinyglobby: 0.2.16 transitivePeerDependencies: - bare-abort-controller - react-native-b4a - supports-color - '@swc/core-darwin-arm64@1.15.41': + '@swc/core-darwin-arm64@1.15.43': optional: true - '@swc/core-darwin-x64@1.15.41': + '@swc/core-darwin-x64@1.15.43': optional: true - '@swc/core-linux-arm-gnueabihf@1.15.41': + '@swc/core-linux-arm-gnueabihf@1.15.43': optional: true - '@swc/core-linux-arm64-gnu@1.15.41': + '@swc/core-linux-arm64-gnu@1.15.43': optional: true - '@swc/core-linux-arm64-musl@1.15.41': + '@swc/core-linux-arm64-musl@1.15.43': optional: true - '@swc/core-linux-ppc64-gnu@1.15.41': + '@swc/core-linux-ppc64-gnu@1.15.43': optional: true - '@swc/core-linux-s390x-gnu@1.15.41': + '@swc/core-linux-s390x-gnu@1.15.43': optional: true - '@swc/core-linux-x64-gnu@1.15.41': + '@swc/core-linux-x64-gnu@1.15.43': optional: true - '@swc/core-linux-x64-musl@1.15.41': + '@swc/core-linux-x64-musl@1.15.43': optional: true - '@swc/core-win32-arm64-msvc@1.15.41': + '@swc/core-win32-arm64-msvc@1.15.43': optional: true - '@swc/core-win32-ia32-msvc@1.15.41': + '@swc/core-win32-ia32-msvc@1.15.43': optional: true - '@swc/core-win32-x64-msvc@1.15.41': + '@swc/core-win32-x64-msvc@1.15.43': optional: true - '@swc/core@1.15.41': + '@swc/core@1.15.43': dependencies: '@swc/counter': 0.1.3 '@swc/types': 0.1.27 optionalDependencies: - '@swc/core-darwin-arm64': 1.15.41 - '@swc/core-darwin-x64': 1.15.41 - '@swc/core-linux-arm-gnueabihf': 1.15.41 - '@swc/core-linux-arm64-gnu': 1.15.41 - '@swc/core-linux-arm64-musl': 1.15.41 - '@swc/core-linux-ppc64-gnu': 1.15.41 - '@swc/core-linux-s390x-gnu': 1.15.41 - '@swc/core-linux-x64-gnu': 1.15.41 - '@swc/core-linux-x64-musl': 1.15.41 - '@swc/core-win32-arm64-msvc': 1.15.41 - '@swc/core-win32-ia32-msvc': 1.15.41 - '@swc/core-win32-x64-msvc': 1.15.41 + '@swc/core-darwin-arm64': 1.15.43 + '@swc/core-darwin-x64': 1.15.43 + '@swc/core-linux-arm-gnueabihf': 1.15.43 + '@swc/core-linux-arm64-gnu': 1.15.43 + '@swc/core-linux-arm64-musl': 1.15.43 + '@swc/core-linux-ppc64-gnu': 1.15.43 + '@swc/core-linux-s390x-gnu': 1.15.43 + '@swc/core-linux-x64-gnu': 1.15.43 + '@swc/core-linux-x64-musl': 1.15.43 + '@swc/core-win32-arm64-msvc': 1.15.43 + '@swc/core-win32-ia32-msvc': 1.15.43 + '@swc/core-win32-x64-msvc': 1.15.43 '@swc/counter@0.1.3': {} @@ -4994,10 +5158,6 @@ snapshots: dependencies: undici-types: 7.21.0 - '@types/node@25.9.3': - dependencies: - undici-types: 7.24.6 - '@types/parse-json@4.0.2': {} '@types/react-dom@19.2.3(@types/react@19.2.17)': @@ -5029,16 +5189,16 @@ snapshots: '@types/ws@8.18.1': dependencies: - '@types/node': 25.9.3 + '@types/node': 25.7.0 - '@typescript-eslint/eslint-plugin@8.61.1(@typescript-eslint/parser@8.61.1(eslint@9.39.4)(typescript@5.7.3))(eslint@9.39.4)(typescript@5.7.3)': + '@typescript-eslint/eslint-plugin@8.59.3(@typescript-eslint/parser@8.59.3(eslint@9.39.4)(typescript@5.7.3))(eslint@9.39.4)(typescript@5.7.3)': dependencies: '@eslint-community/regexpp': 4.12.2 - '@typescript-eslint/parser': 8.61.1(eslint@9.39.4)(typescript@5.7.3) - '@typescript-eslint/scope-manager': 8.61.1 - '@typescript-eslint/type-utils': 8.61.1(eslint@9.39.4)(typescript@5.7.3) - '@typescript-eslint/utils': 8.61.1(eslint@9.39.4)(typescript@5.7.3) - '@typescript-eslint/visitor-keys': 8.61.1 + '@typescript-eslint/parser': 8.59.3(eslint@9.39.4)(typescript@5.7.3) + '@typescript-eslint/scope-manager': 8.59.3 + '@typescript-eslint/type-utils': 8.59.3(eslint@9.39.4)(typescript@5.7.3) + '@typescript-eslint/utils': 8.59.3(eslint@9.39.4)(typescript@5.7.3) + '@typescript-eslint/visitor-keys': 8.59.3 eslint: 9.39.4 ignore: 7.0.5 natural-compare: 1.4.0 @@ -5047,14 +5207,14 @@ snapshots: transitivePeerDependencies: - supports-color - '@typescript-eslint/eslint-plugin@8.61.1(@typescript-eslint/parser@8.61.1(eslint@9.39.4)(typescript@6.0.3))(eslint@9.39.4)(typescript@6.0.3)': + '@typescript-eslint/eslint-plugin@8.59.3(@typescript-eslint/parser@8.59.3(eslint@9.39.4)(typescript@6.0.3))(eslint@9.39.4)(typescript@6.0.3)': dependencies: '@eslint-community/regexpp': 4.12.2 - '@typescript-eslint/parser': 8.61.1(eslint@9.39.4)(typescript@5.7.3) - '@typescript-eslint/scope-manager': 8.61.1 - '@typescript-eslint/type-utils': 8.61.1(eslint@9.39.4)(typescript@6.0.3) - '@typescript-eslint/utils': 8.61.1(eslint@9.39.4)(typescript@6.0.3) - '@typescript-eslint/visitor-keys': 8.61.1 + '@typescript-eslint/parser': 8.59.3(eslint@9.39.4)(typescript@5.7.3) + '@typescript-eslint/scope-manager': 8.59.3 + '@typescript-eslint/type-utils': 8.59.3(eslint@9.39.4)(typescript@6.0.3) + '@typescript-eslint/utils': 8.59.3(eslint@9.39.4)(typescript@6.0.3) + '@typescript-eslint/visitor-keys': 8.59.3 eslint: 9.39.4 ignore: 7.0.5 natural-compare: 1.4.0 @@ -5064,56 +5224,56 @@ snapshots: - supports-color optional: true - '@typescript-eslint/parser@8.61.1(eslint@9.39.4)(typescript@5.7.3)': + '@typescript-eslint/parser@8.59.3(eslint@9.39.4)(typescript@5.7.3)': dependencies: - '@typescript-eslint/scope-manager': 8.61.1 - '@typescript-eslint/types': 8.61.1 - '@typescript-eslint/typescript-estree': 8.61.1(typescript@5.7.3) - '@typescript-eslint/visitor-keys': 8.61.1 + '@typescript-eslint/scope-manager': 8.59.3 + '@typescript-eslint/types': 8.59.3 + '@typescript-eslint/typescript-estree': 8.59.3(typescript@5.7.3) + '@typescript-eslint/visitor-keys': 8.59.3 debug: 4.4.3 eslint: 9.39.4 typescript: 5.7.3 transitivePeerDependencies: - supports-color - '@typescript-eslint/project-service@8.61.1(typescript@5.7.3)': + '@typescript-eslint/project-service@8.59.3(typescript@5.7.3)': dependencies: - '@typescript-eslint/tsconfig-utils': 8.61.1(typescript@5.7.3) - '@typescript-eslint/types': 8.61.1 + '@typescript-eslint/tsconfig-utils': 8.59.3(typescript@5.7.3) + '@typescript-eslint/types': 8.59.3 debug: 4.4.3 typescript: 5.7.3 transitivePeerDependencies: - supports-color - '@typescript-eslint/project-service@8.61.1(typescript@6.0.3)': + '@typescript-eslint/project-service@8.59.3(typescript@6.0.3)': dependencies: - '@typescript-eslint/tsconfig-utils': 8.61.1(typescript@6.0.3) - '@typescript-eslint/types': 8.61.1 + '@typescript-eslint/tsconfig-utils': 8.59.3(typescript@6.0.3) + '@typescript-eslint/types': 8.59.3 debug: 4.4.3 typescript: 6.0.3 transitivePeerDependencies: - supports-color optional: true - '@typescript-eslint/scope-manager@8.61.1': + '@typescript-eslint/scope-manager@8.59.3': dependencies: - '@typescript-eslint/types': 8.61.1 - '@typescript-eslint/visitor-keys': 8.61.1 + '@typescript-eslint/types': 8.59.3 + '@typescript-eslint/visitor-keys': 8.59.3 - '@typescript-eslint/tsconfig-utils@8.61.1(typescript@5.7.3)': + '@typescript-eslint/tsconfig-utils@8.59.3(typescript@5.7.3)': dependencies: typescript: 5.7.3 - '@typescript-eslint/tsconfig-utils@8.61.1(typescript@6.0.3)': + '@typescript-eslint/tsconfig-utils@8.59.3(typescript@6.0.3)': dependencies: typescript: 6.0.3 optional: true - '@typescript-eslint/type-utils@8.61.1(eslint@9.39.4)(typescript@5.7.3)': + '@typescript-eslint/type-utils@8.59.3(eslint@9.39.4)(typescript@5.7.3)': dependencies: - '@typescript-eslint/types': 8.61.1 - '@typescript-eslint/typescript-estree': 8.61.1(typescript@5.7.3) - '@typescript-eslint/utils': 8.61.1(eslint@9.39.4)(typescript@5.7.3) + '@typescript-eslint/types': 8.59.3 + '@typescript-eslint/typescript-estree': 8.59.3(typescript@5.7.3) + '@typescript-eslint/utils': 8.59.3(eslint@9.39.4)(typescript@5.7.3) debug: 4.4.3 eslint: 9.39.4 ts-api-utils: 2.5.0(typescript@5.7.3) @@ -5121,11 +5281,11 @@ snapshots: transitivePeerDependencies: - supports-color - '@typescript-eslint/type-utils@8.61.1(eslint@9.39.4)(typescript@6.0.3)': + '@typescript-eslint/type-utils@8.59.3(eslint@9.39.4)(typescript@6.0.3)': dependencies: - '@typescript-eslint/types': 8.61.1 - '@typescript-eslint/typescript-estree': 8.61.1(typescript@6.0.3) - '@typescript-eslint/utils': 8.61.1(eslint@9.39.4)(typescript@6.0.3) + '@typescript-eslint/types': 8.59.3 + '@typescript-eslint/typescript-estree': 8.59.3(typescript@6.0.3) + '@typescript-eslint/utils': 8.59.3(eslint@9.39.4)(typescript@6.0.3) debug: 4.4.3 eslint: 9.39.4 ts-api-utils: 2.5.0(typescript@6.0.3) @@ -5134,68 +5294,68 @@ snapshots: - supports-color optional: true - '@typescript-eslint/types@8.61.1': {} + '@typescript-eslint/types@8.59.3': {} - '@typescript-eslint/typescript-estree@8.61.1(typescript@5.7.3)': + '@typescript-eslint/typescript-estree@8.59.3(typescript@5.7.3)': dependencies: - '@typescript-eslint/project-service': 8.61.1(typescript@5.7.3) - '@typescript-eslint/tsconfig-utils': 8.61.1(typescript@5.7.3) - '@typescript-eslint/types': 8.61.1 - '@typescript-eslint/visitor-keys': 8.61.1 + '@typescript-eslint/project-service': 8.59.3(typescript@5.7.3) + '@typescript-eslint/tsconfig-utils': 8.59.3(typescript@5.7.3) + '@typescript-eslint/types': 8.59.3 + '@typescript-eslint/visitor-keys': 8.59.3 debug: 4.4.3 minimatch: 10.2.5 - semver: 7.8.4 - tinyglobby: 0.2.17 + semver: 7.8.0 + tinyglobby: 0.2.16 ts-api-utils: 2.5.0(typescript@5.7.3) typescript: 5.7.3 transitivePeerDependencies: - supports-color - '@typescript-eslint/typescript-estree@8.61.1(typescript@6.0.3)': + '@typescript-eslint/typescript-estree@8.59.3(typescript@6.0.3)': dependencies: - '@typescript-eslint/project-service': 8.61.1(typescript@6.0.3) - '@typescript-eslint/tsconfig-utils': 8.61.1(typescript@6.0.3) - '@typescript-eslint/types': 8.61.1 - '@typescript-eslint/visitor-keys': 8.61.1 + '@typescript-eslint/project-service': 8.59.3(typescript@6.0.3) + '@typescript-eslint/tsconfig-utils': 8.59.3(typescript@6.0.3) + '@typescript-eslint/types': 8.59.3 + '@typescript-eslint/visitor-keys': 8.59.3 debug: 4.4.3 minimatch: 10.2.5 - semver: 7.8.4 - tinyglobby: 0.2.17 + semver: 7.8.0 + tinyglobby: 0.2.16 ts-api-utils: 2.5.0(typescript@6.0.3) typescript: 6.0.3 transitivePeerDependencies: - supports-color optional: true - '@typescript-eslint/utils@8.61.1(eslint@9.39.4)(typescript@5.7.3)': + '@typescript-eslint/utils@8.59.3(eslint@9.39.4)(typescript@5.7.3)': dependencies: '@eslint-community/eslint-utils': 4.9.1(eslint@9.39.4) - '@typescript-eslint/scope-manager': 8.61.1 - '@typescript-eslint/types': 8.61.1 - '@typescript-eslint/typescript-estree': 8.61.1(typescript@5.7.3) + '@typescript-eslint/scope-manager': 8.59.3 + '@typescript-eslint/types': 8.59.3 + '@typescript-eslint/typescript-estree': 8.59.3(typescript@5.7.3) eslint: 9.39.4 typescript: 5.7.3 transitivePeerDependencies: - supports-color - '@typescript-eslint/utils@8.61.1(eslint@9.39.4)(typescript@6.0.3)': + '@typescript-eslint/utils@8.59.3(eslint@9.39.4)(typescript@6.0.3)': dependencies: '@eslint-community/eslint-utils': 4.9.1(eslint@9.39.4) - '@typescript-eslint/scope-manager': 8.61.1 - '@typescript-eslint/types': 8.61.1 - '@typescript-eslint/typescript-estree': 8.61.1(typescript@6.0.3) + '@typescript-eslint/scope-manager': 8.59.3 + '@typescript-eslint/types': 8.59.3 + '@typescript-eslint/typescript-estree': 8.59.3(typescript@6.0.3) eslint: 9.39.4 typescript: 6.0.3 transitivePeerDependencies: - supports-color optional: true - '@typescript-eslint/visitor-keys@8.61.1': + '@typescript-eslint/visitor-keys@8.59.3': dependencies: - '@typescript-eslint/types': 8.61.1 + '@typescript-eslint/types': 8.59.3 eslint-visitor-keys: 5.0.1 - '@xhmikosr/archive-type@8.1.0': + '@xhmikosr/archive-type@8.0.1': dependencies: file-type: 21.3.4 transitivePeerDependencies: @@ -5206,11 +5366,11 @@ snapshots: execa: 9.6.1 isexe: 4.0.0 - '@xhmikosr/bin-wrapper@14.4.0': + '@xhmikosr/bin-wrapper@14.2.3': dependencies: '@xhmikosr/bin-check': 8.2.1 - '@xhmikosr/downloader': 16.2.0 - '@xhmikosr/os-filter-obj': 4.1.0 + '@xhmikosr/downloader': 16.1.2 + '@xhmikosr/os-filter-obj': 4.0.0 binary-version-check: 6.1.0 transitivePeerDependencies: - bare-abort-controller @@ -5227,7 +5387,7 @@ snapshots: - react-native-b4a - supports-color - '@xhmikosr/decompress-tarbz2@9.0.2': + '@xhmikosr/decompress-tarbz2@9.0.1': dependencies: '@xhmikosr/decompress-tar': 9.0.1 file-type: 21.3.4 @@ -5249,20 +5409,20 @@ snapshots: - react-native-b4a - supports-color - '@xhmikosr/decompress-unzip@8.2.1': + '@xhmikosr/decompress-unzip@8.1.1': dependencies: file-type: 21.3.4 get-stream: 9.0.1 - yauzl: 3.4.0 + yauzl: 3.3.0 transitivePeerDependencies: - supports-color - '@xhmikosr/decompress@11.1.3': + '@xhmikosr/decompress@11.1.2': dependencies: '@xhmikosr/decompress-tar': 9.0.1 - '@xhmikosr/decompress-tarbz2': 9.0.2 + '@xhmikosr/decompress-tarbz2': 9.0.1 '@xhmikosr/decompress-targz': 9.0.1 - '@xhmikosr/decompress-unzip': 8.2.1 + '@xhmikosr/decompress-unzip': 8.1.1 graceful-fs: 4.2.11 strip-dirs: 3.0.0 transitivePeerDependencies: @@ -5270,23 +5430,24 @@ snapshots: - react-native-b4a - supports-color - '@xhmikosr/downloader@16.2.0': + '@xhmikosr/downloader@16.1.2': dependencies: - '@xhmikosr/archive-type': 8.1.0 - '@xhmikosr/decompress': 11.1.3 - content-disposition: 2.0.1 + '@xhmikosr/archive-type': 8.0.1 + '@xhmikosr/decompress': 11.1.2 + content-disposition: 1.1.0 ext-name: 5.0.0 file-type: 21.3.4 - filenamify: 7.0.2 + filenamify: 7.0.1 + get-stream: 9.0.1 got: 14.6.6 transitivePeerDependencies: - bare-abort-controller - react-native-b4a - supports-color - '@xhmikosr/os-filter-obj@4.1.0': + '@xhmikosr/os-filter-obj@4.0.0': dependencies: - system-architecture: 1.0.0 + arch: 3.0.0 acorn-jsx@5.3.2(acorn@8.16.0): dependencies: @@ -5319,6 +5480,8 @@ snapshots: normalize-path: 3.0.0 picomatch: 2.3.2 + arch@3.0.0: {} + argparse@2.0.1: {} aria-query@5.3.2: {} @@ -5334,7 +5497,7 @@ snapshots: call-bound: 1.0.4 define-properties: 1.2.1 es-abstract: 1.24.2 - es-object-atoms: 1.1.2 + es-object-atoms: 1.1.1 get-intrinsic: 1.3.0 is-string: 1.1.1 math-intrinsics: 1.1.0 @@ -5373,7 +5536,7 @@ snapshots: dependencies: possible-typed-array-names: 1.1.0 - axe-core@4.12.1: {} + axe-core@4.11.4: {} axobject-query@4.1.0: {} @@ -5381,7 +5544,7 @@ snapshots: babel-plugin-macros@3.1.0: dependencies: - '@babel/runtime': 7.29.7 + '@babel/runtime': 7.29.2 cosmiconfig: 7.1.0 resolve: 1.22.12 @@ -5389,18 +5552,18 @@ snapshots: balanced-match@4.0.4: {} - bare-events@2.9.1: {} + bare-events@2.8.2: {} base64-js@1.5.1: {} - baseline-browser-mapping@2.10.38: {} + baseline-browser-mapping@2.10.29: {} binary-extensions@2.3.0: {} binary-version-check@6.1.0: dependencies: binary-version: 7.1.0 - semver: 7.8.4 + semver: 7.8.0 semver-truncate: 3.0.0 binary-version@7.1.0: @@ -5417,7 +5580,7 @@ snapshots: balanced-match: 1.0.2 concat-map: 0.0.1 - brace-expansion@2.1.1: + brace-expansion@2.1.0: dependencies: balanced-match: 1.0.2 @@ -5433,9 +5596,7 @@ snapshots: bson@6.10.4: {} - buffer-image-size@0.6.4: - dependencies: - '@types/node': 25.9.3 + buffer-crc32@0.2.13: {} buffer@5.7.1: dependencies: @@ -5479,7 +5640,7 @@ snapshots: callsites@3.1.0: {} - caniuse-lite@1.0.30001799: {} + caniuse-lite@1.0.30001792: {} ccount@2.0.1: {} @@ -5536,7 +5697,7 @@ snapshots: commander@8.3.0: {} - comment-parser@1.4.7: {} + comment-parser@1.4.6: {} compare-versions@6.1.1: {} @@ -5546,7 +5707,7 @@ snapshots: dependencies: simple-wcswidth: 1.1.2 - content-disposition@2.0.1: {} + content-disposition@1.1.0: {} convert-hrtime@5.0.0: {} @@ -5668,10 +5829,10 @@ snapshots: dom-helpers@5.2.1: dependencies: - '@babel/runtime': 7.29.7 + '@babel/runtime': 7.29.2 csstype: 3.2.3 - dompurify@3.4.11: + dompurify@3.4.2: optionalDependencies: '@types/trusted-types': 2.0.7 @@ -5691,7 +5852,7 @@ snapshots: dependencies: once: 1.4.0 - enhanced-resolve@5.24.0: + enhanced-resolve@5.21.3: dependencies: graceful-fs: 4.2.11 tapable: 2.3.3 @@ -5702,13 +5863,6 @@ snapshots: dependencies: is-arrayish: 0.2.1 - es-abstract-get@1.0.0: - dependencies: - es-errors: 1.3.0 - es-object-atoms: 1.1.2 - is-callable: 1.2.7 - object-inspect: 1.13.4 - es-abstract@1.24.2: dependencies: array-buffer-byte-length: 1.0.2 @@ -5721,10 +5875,10 @@ snapshots: data-view-byte-offset: 1.0.1 es-define-property: 1.0.1 es-errors: 1.3.0 - es-object-atoms: 1.1.2 + es-object-atoms: 1.1.1 es-set-tostringtag: 2.1.0 - es-to-primitive: 1.3.1 - function.prototype.name: 1.2.0 + es-to-primitive: 1.3.0 + function.prototype.name: 1.1.8 get-intrinsic: 1.3.0 get-proto: 1.0.1 get-symbol-description: 1.1.0 @@ -5733,7 +5887,7 @@ snapshots: has-property-descriptors: 1.0.2 has-proto: 1.2.0 has-symbols: 1.1.0 - hasown: 2.0.4 + hasown: 2.0.3 internal-slot: 1.1.0 is-array-buffer: 3.0.5 is-callable: 1.2.7 @@ -5756,21 +5910,21 @@ snapshots: safe-regex-test: 1.1.0 set-proto: 1.0.0 stop-iteration-iterator: 1.1.0 - string.prototype.trim: 1.2.11 - string.prototype.trimend: 1.0.10 + string.prototype.trim: 1.2.10 + string.prototype.trimend: 1.0.9 string.prototype.trimstart: 1.0.8 typed-array-buffer: 1.0.3 typed-array-byte-length: 1.0.3 typed-array-byte-offset: 1.0.4 - typed-array-length: 1.0.8 + typed-array-length: 1.0.7 unbox-primitive: 1.1.0 - which-typed-array: 1.1.22 + which-typed-array: 1.1.20 es-define-property@1.0.1: {} es-errors@1.3.0: {} - es-object-atoms@1.1.2: + es-object-atoms@1.1.1: dependencies: es-errors: 1.3.0 @@ -5779,16 +5933,14 @@ snapshots: es-errors: 1.3.0 get-intrinsic: 1.3.0 has-tostringtag: 1.0.2 - hasown: 2.0.4 + hasown: 2.0.3 es-shim-unscopables@1.1.0: dependencies: - hasown: 2.0.4 + hasown: 2.0.3 - es-to-primitive@1.3.1: + es-to-primitive@1.3.0: dependencies: - es-abstract-get: 1.0.0 - es-errors: 1.3.0 is-callable: 1.2.7 is-date-object: 1.1.0 is-symbol: 1.1.1 @@ -5836,24 +5988,24 @@ snapshots: dependencies: debug: 3.2.7 is-core-module: 2.16.2 - resolve: 2.0.0-next.7 + resolve: 2.0.0-next.6 transitivePeerDependencies: - supports-color eslint-plugin-import-x@4.6.1(eslint@9.39.4)(typescript@5.7.3): dependencies: '@types/doctrine': 0.0.9 - '@typescript-eslint/scope-manager': 8.61.1 - '@typescript-eslint/utils': 8.61.1(eslint@9.39.4)(typescript@5.7.3) + '@typescript-eslint/scope-manager': 8.59.3 + '@typescript-eslint/utils': 8.59.3(eslint@9.39.4)(typescript@5.7.3) debug: 4.4.3 doctrine: 3.0.0 - enhanced-resolve: 5.24.0 + enhanced-resolve: 5.21.3 eslint: 9.39.4 eslint-import-resolver-node: 0.3.10 get-tsconfig: 4.14.0 is-glob: 4.0.3 minimatch: 9.0.9 - semver: 7.8.4 + semver: 7.8.0 stable-hash: 0.0.4 tslib: 2.8.1 transitivePeerDependencies: @@ -5862,16 +6014,16 @@ snapshots: eslint-plugin-jest-dom@5.5.0(eslint@9.39.4): dependencies: - '@babel/runtime': 7.29.7 + '@babel/runtime': 7.29.2 eslint: 9.39.4 requireindex: 1.2.0 - eslint-plugin-jest@28.11.0(@typescript-eslint/eslint-plugin@8.61.1(@typescript-eslint/parser@8.61.1(eslint@9.39.4)(typescript@6.0.3))(eslint@9.39.4)(typescript@6.0.3))(eslint@9.39.4)(typescript@5.7.3): + eslint-plugin-jest@28.11.0(@typescript-eslint/eslint-plugin@8.59.3(@typescript-eslint/parser@8.59.3(eslint@9.39.4)(typescript@6.0.3))(eslint@9.39.4)(typescript@6.0.3))(eslint@9.39.4)(typescript@5.7.3): dependencies: - '@typescript-eslint/utils': 8.61.1(eslint@9.39.4)(typescript@5.7.3) + '@typescript-eslint/utils': 8.59.3(eslint@9.39.4)(typescript@5.7.3) eslint: 9.39.4 optionalDependencies: - '@typescript-eslint/eslint-plugin': 8.61.1(@typescript-eslint/parser@8.61.1(eslint@9.39.4)(typescript@6.0.3))(eslint@9.39.4)(typescript@6.0.3) + '@typescript-eslint/eslint-plugin': 8.59.3(@typescript-eslint/parser@8.59.3(eslint@9.39.4)(typescript@6.0.3))(eslint@9.39.4)(typescript@6.0.3) transitivePeerDependencies: - supports-color - typescript @@ -5882,12 +6034,12 @@ snapshots: array-includes: 3.1.9 array.prototype.flatmap: 1.3.3 ast-types-flow: 0.0.8 - axe-core: 4.12.1 + axe-core: 4.11.4 axobject-query: 4.1.0 damerau-levenshtein: 1.0.8 emoji-regex: 9.2.2 eslint: 9.39.4 - hasown: 2.0.4 + hasown: 2.0.3 jsx-ast-utils: 3.3.5 language-tags: 1.0.9 minimatch: 3.1.5 @@ -5897,8 +6049,8 @@ snapshots: eslint-plugin-perfectionist@3.9.1(eslint@9.39.4)(typescript@5.7.3): dependencies: - '@typescript-eslint/types': 8.61.1 - '@typescript-eslint/utils': 8.61.1(eslint@9.39.4)(typescript@5.7.3) + '@typescript-eslint/types': 8.59.3 + '@typescript-eslint/utils': 8.59.3(eslint@9.39.4)(typescript@5.7.3) eslint: 9.39.4 minimatch: 9.0.9 natural-compare-lite: 1.4.0 @@ -5914,10 +6066,10 @@ snapshots: '@eslint-react/jsx': 1.31.0(eslint@9.39.4)(typescript@5.7.3) '@eslint-react/shared': 1.31.0(eslint@9.39.4)(typescript@5.7.3) '@eslint-react/var': 1.31.0(eslint@9.39.4)(typescript@5.7.3) - '@typescript-eslint/scope-manager': 8.61.1 - '@typescript-eslint/type-utils': 8.61.1(eslint@9.39.4)(typescript@5.7.3) - '@typescript-eslint/types': 8.61.1 - '@typescript-eslint/utils': 8.61.1(eslint@9.39.4)(typescript@5.7.3) + '@typescript-eslint/scope-manager': 8.59.3 + '@typescript-eslint/type-utils': 8.59.3(eslint@9.39.4)(typescript@5.7.3) + '@typescript-eslint/types': 8.59.3 + '@typescript-eslint/utils': 8.59.3(eslint@9.39.4)(typescript@5.7.3) eslint: 9.39.4 string-ts: 2.3.1 ts-pattern: 5.9.0 @@ -5934,9 +6086,9 @@ snapshots: '@eslint-react/jsx': 1.31.0(eslint@9.39.4)(typescript@5.7.3) '@eslint-react/shared': 1.31.0(eslint@9.39.4)(typescript@5.7.3) '@eslint-react/var': 1.31.0(eslint@9.39.4)(typescript@5.7.3) - '@typescript-eslint/scope-manager': 8.61.1 - '@typescript-eslint/types': 8.61.1 - '@typescript-eslint/utils': 8.61.1(eslint@9.39.4)(typescript@5.7.3) + '@typescript-eslint/scope-manager': 8.59.3 + '@typescript-eslint/types': 8.59.3 + '@typescript-eslint/utils': 8.59.3(eslint@9.39.4)(typescript@5.7.3) compare-versions: 6.1.1 eslint: 9.39.4 string-ts: 2.3.1 @@ -5954,10 +6106,10 @@ snapshots: '@eslint-react/jsx': 1.31.0(eslint@9.39.4)(typescript@5.7.3) '@eslint-react/shared': 1.31.0(eslint@9.39.4)(typescript@5.7.3) '@eslint-react/var': 1.31.0(eslint@9.39.4)(typescript@5.7.3) - '@typescript-eslint/scope-manager': 8.61.1 - '@typescript-eslint/type-utils': 8.61.1(eslint@9.39.4)(typescript@5.7.3) - '@typescript-eslint/types': 8.61.1 - '@typescript-eslint/utils': 8.61.1(eslint@9.39.4)(typescript@5.7.3) + '@typescript-eslint/scope-manager': 8.59.3 + '@typescript-eslint/type-utils': 8.59.3(eslint@9.39.4)(typescript@5.7.3) + '@typescript-eslint/types': 8.59.3 + '@typescript-eslint/utils': 8.59.3(eslint@9.39.4)(typescript@5.7.3) eslint: 9.39.4 string-ts: 2.3.1 ts-pattern: 5.9.0 @@ -5978,10 +6130,10 @@ snapshots: '@eslint-react/jsx': 1.31.0(eslint@9.39.4)(typescript@5.7.3) '@eslint-react/shared': 1.31.0(eslint@9.39.4)(typescript@5.7.3) '@eslint-react/var': 1.31.0(eslint@9.39.4)(typescript@5.7.3) - '@typescript-eslint/scope-manager': 8.61.1 - '@typescript-eslint/type-utils': 8.61.1(eslint@9.39.4)(typescript@5.7.3) - '@typescript-eslint/types': 8.61.1 - '@typescript-eslint/utils': 8.61.1(eslint@9.39.4)(typescript@5.7.3) + '@typescript-eslint/scope-manager': 8.59.3 + '@typescript-eslint/type-utils': 8.59.3(eslint@9.39.4)(typescript@5.7.3) + '@typescript-eslint/types': 8.59.3 + '@typescript-eslint/utils': 8.59.3(eslint@9.39.4)(typescript@5.7.3) eslint: 9.39.4 string-ts: 2.3.1 ts-pattern: 5.9.0 @@ -5998,9 +6150,9 @@ snapshots: '@eslint-react/jsx': 1.31.0(eslint@9.39.4)(typescript@5.7.3) '@eslint-react/shared': 1.31.0(eslint@9.39.4)(typescript@5.7.3) '@eslint-react/var': 1.31.0(eslint@9.39.4)(typescript@5.7.3) - '@typescript-eslint/scope-manager': 8.61.1 - '@typescript-eslint/types': 8.61.1 - '@typescript-eslint/utils': 8.61.1(eslint@9.39.4)(typescript@5.7.3) + '@typescript-eslint/scope-manager': 8.59.3 + '@typescript-eslint/types': 8.59.3 + '@typescript-eslint/utils': 8.59.3(eslint@9.39.4)(typescript@5.7.3) eslint: 9.39.4 string-ts: 2.3.1 ts-pattern: 5.9.0 @@ -6017,10 +6169,10 @@ snapshots: '@eslint-react/jsx': 1.31.0(eslint@9.39.4)(typescript@5.7.3) '@eslint-react/shared': 1.31.0(eslint@9.39.4)(typescript@5.7.3) '@eslint-react/var': 1.31.0(eslint@9.39.4)(typescript@5.7.3) - '@typescript-eslint/scope-manager': 8.61.1 - '@typescript-eslint/type-utils': 8.61.1(eslint@9.39.4)(typescript@5.7.3) - '@typescript-eslint/types': 8.61.1 - '@typescript-eslint/utils': 8.61.1(eslint@9.39.4)(typescript@5.7.3) + '@typescript-eslint/scope-manager': 8.59.3 + '@typescript-eslint/type-utils': 8.59.3(eslint@9.39.4)(typescript@5.7.3) + '@typescript-eslint/types': 8.59.3 + '@typescript-eslint/utils': 8.59.3(eslint@9.39.4)(typescript@5.7.3) compare-versions: 6.1.1 eslint: 9.39.4 string-ts: 2.3.1 @@ -6035,7 +6187,7 @@ snapshots: dependencies: '@eslint-community/eslint-utils': 4.9.1(eslint@9.39.4) '@eslint-community/regexpp': 4.12.2 - comment-parser: 1.4.7 + comment-parser: 1.4.6 eslint: 9.39.4 jsdoc-type-pratt-parser: 4.8.0 refa: 0.12.1 @@ -6119,7 +6271,7 @@ snapshots: events-universal@1.0.1: dependencies: - bare-events: 2.9.1 + bare-events: 2.8.2 transitivePeerDependencies: - bare-abort-controller @@ -6196,7 +6348,7 @@ snapshots: filename-reserved-regex@4.0.0: {} - filenamify@7.0.2: + filenamify@7.0.1: dependencies: filename-reserved-regex: 4.0.0 @@ -6242,17 +6394,14 @@ snapshots: function-timeout@1.0.2: {} - function.prototype.name@1.2.0: + function.prototype.name@1.1.8: dependencies: call-bind: 1.0.9 call-bound: 1.0.4 - es-define-property: 1.0.1 - es-errors: 1.3.0 + define-properties: 1.2.1 functions-have-names: 1.2.3 - has-property-descriptors: 1.0.2 - hasown: 2.0.4 + hasown: 2.0.3 is-callable: 1.2.7 - is-document.all: 1.0.0 functions-have-names@1.2.3: {} @@ -6265,18 +6414,18 @@ snapshots: call-bind-apply-helpers: 1.0.2 es-define-property: 1.0.1 es-errors: 1.3.0 - es-object-atoms: 1.1.2 + es-object-atoms: 1.1.1 function-bind: 1.1.2 get-proto: 1.0.1 gopd: 1.2.0 has-symbols: 1.1.0 - hasown: 2.0.4 + hasown: 2.0.3 math-intrinsics: 1.1.0 get-proto@1.0.1: dependencies: dunder-proto: 1.0.1 - es-object-atoms: 1.1.2 + es-object-atoms: 1.1.1 get-stream@8.0.1: {} @@ -6365,12 +6514,11 @@ snapshots: graphql@16.12.0: {} - happy-dom@20.10.6: + happy-dom@20.9.0: dependencies: - '@types/node': 25.9.3 + '@types/node': 25.7.0 '@types/whatwg-mimetype': 3.0.2 '@types/ws': 8.18.1 - buffer-image-size: 0.6.4 entities: 7.0.1 whatwg-mimetype: 3.0.0 ws: 8.21.0 @@ -6396,7 +6544,7 @@ snapshots: dependencies: has-symbols: 1.1.0 - hasown@2.0.4: + hasown@2.0.3: dependencies: function-bind: 1.1.2 @@ -6452,8 +6600,8 @@ snapshots: internal-slot@1.1.0: dependencies: es-errors: 1.3.0 - hasown: 2.0.4 - side-channel: 1.1.1 + hasown: 2.0.3 + side-channel: 1.1.0 ipaddr.js@2.2.0: {} @@ -6499,7 +6647,7 @@ snapshots: is-core-module@2.16.2: dependencies: - hasown: 2.0.4 + hasown: 2.0.3 is-data-view@1.0.2: dependencies: @@ -6514,10 +6662,6 @@ snapshots: is-decimal@2.0.1: {} - is-document.all@1.0.0: - dependencies: - call-bound: 1.0.4 - is-extglob@2.1.1: {} is-finalizationregistry@1.1.1: @@ -6560,7 +6704,7 @@ snapshots: call-bound: 1.0.4 gopd: 1.2.0 has-tostringtag: 1.0.2 - hasown: 2.0.4 + hasown: 2.0.3 is-set@2.0.3: {} @@ -6585,7 +6729,7 @@ snapshots: is-typed-array@1.1.15: dependencies: - which-typed-array: 1.1.22 + which-typed-array: 1.1.20 is-unicode-supported@2.1.0: {} @@ -6639,8 +6783,8 @@ snapshots: js-yaml: 4.2.0 lodash: 4.18.1 minimist: 1.2.8 - prettier: 3.8.4 - tinyglobby: 0.2.17 + prettier: 3.8.3 + tinyglobby: 0.2.16 json-schema-traverse@0.4.1: {} @@ -6706,7 +6850,7 @@ snapshots: lowercase-keys@3.0.0: {} - lru-cache@11.5.1: {} + lru-cache@11.3.6: {} make-asynchronous@1.1.0: dependencies: @@ -6970,7 +7114,7 @@ snapshots: minimatch@9.0.9: dependencies: - brace-expansion: 2.1.1 + brace-expansion: 2.1.0 minimist@1.2.8: {} @@ -6980,7 +7124,7 @@ snapshots: monaco-editor@0.55.1: dependencies: - dompurify: 3.4.11 + dompurify: 3.4.2 marked: 14.0.0 mongodb-connection-string-url@3.0.2: @@ -6994,18 +7138,18 @@ snapshots: bson: 6.10.4 mongodb-connection-string-url: 3.0.2 - mongoose-lean-virtuals@1.1.1(mongoose@8.24.0): + mongoose-lean-virtuals@1.1.1(mongoose@8.23.1): dependencies: - mongoose: 8.24.0 + mongoose: 8.23.1 mpath: 0.8.4 - mongoose-paginate-v2@1.9.4(mongoose@8.24.0): + mongoose-paginate-v2@1.9.4(mongoose@8.23.1): dependencies: - mongoose-lean-virtuals: 1.1.1(mongoose@8.24.0) + mongoose-lean-virtuals: 1.1.1(mongoose@8.23.1) transitivePeerDependencies: - mongoose - mongoose@8.24.0: + mongoose@8.23.1: dependencies: bson: 6.10.4 kareem: 2.6.3 @@ -7046,9 +7190,9 @@ snapshots: dependencies: '@next/env': 16.2.9 '@swc/helpers': 0.5.15 - baseline-browser-mapping: 2.10.38 - caniuse-lite: 1.0.30001799 - postcss: 8.5.15 + baseline-browser-mapping: 2.10.29 + caniuse-lite: 1.0.30001792 + postcss: 8.5.14 react: 19.2.7 react-dom: 19.2.7(react@19.2.7) styled-jsx: 5.1.6(react@19.2.7) @@ -7105,7 +7249,7 @@ snapshots: call-bind: 1.0.9 call-bound: 1.0.4 define-properties: 1.2.1 - es-object-atoms: 1.1.2 + es-object-atoms: 1.1.1 has-symbols: 1.1.0 object-keys: 1.1.1 @@ -7114,21 +7258,21 @@ snapshots: call-bind: 1.0.9 call-bound: 1.0.4 define-properties: 1.2.1 - es-object-atoms: 1.1.2 + es-object-atoms: 1.1.1 object.fromentries@2.0.8: dependencies: call-bind: 1.0.9 define-properties: 1.2.1 es-abstract: 1.24.2 - es-object-atoms: 1.1.2 + es-object-atoms: 1.1.1 object.values@1.2.1: dependencies: call-bind: 1.0.9 call-bound: 1.0.4 define-properties: 1.2.1 - es-object-atoms: 1.1.2 + es-object-atoms: 1.1.1 on-exit-leak-free@2.1.2: {} @@ -7212,7 +7356,7 @@ snapshots: path-scurry@2.0.2: dependencies: - lru-cache: 11.5.1 + lru-cache: 11.3.6 minipass: 7.1.3 path-to-regexp@6.3.0: {} @@ -7303,7 +7447,7 @@ snapshots: sonic-boom: 4.2.1 thread-stream: 3.1.0 - piscina@4.9.3: + piscina@4.9.2: optionalDependencies: '@napi-rs/nice': 1.1.1 @@ -7311,7 +7455,7 @@ snapshots: possible-typed-array-names@1.1.0: {} - postcss@8.5.15: + postcss@8.5.14: dependencies: nanoid: 3.3.12 picocolors: 1.1.1 @@ -7319,6 +7463,8 @@ snapshots: prelude-ls@1.2.1: {} + prettier@3.8.3: {} + prettier@3.8.4: {} pretty-ms@9.3.0: @@ -7372,10 +7518,10 @@ snapshots: react-error-boundary@4.1.2(react@19.2.7): dependencies: - '@babel/runtime': 7.29.7 + '@babel/runtime': 7.29.2 react: 19.2.7 - react-error-boundary@6.1.2(react@19.2.7): + react-error-boundary@6.1.1(react@19.2.7): dependencies: react: 19.2.7 @@ -7387,7 +7533,7 @@ snapshots: react-select@5.9.0(@types/react@19.2.17)(react-dom@19.2.7(react@19.2.7))(react@19.2.7): dependencies: - '@babel/runtime': 7.29.7 + '@babel/runtime': 7.29.2 '@emotion/cache': 11.14.0 '@emotion/react': 11.14.0(@types/react@19.2.17)(react@19.2.7) '@floating-ui/dom': 1.7.6 @@ -7404,7 +7550,7 @@ snapshots: react-transition-group@4.4.5(react-dom@19.2.7(react@19.2.7))(react@19.2.7): dependencies: - '@babel/runtime': 7.29.7 + '@babel/runtime': 7.29.2 dom-helpers: 5.2.1 loose-envify: 1.4.0 prop-types: 15.8.1 @@ -7446,7 +7592,7 @@ snapshots: define-properties: 1.2.1 es-abstract: 1.24.2 es-errors: 1.3.0 - es-object-atoms: 1.1.2 + es-object-atoms: 1.1.1 get-intrinsic: 1.3.0 get-proto: 1.0.1 which-builtin-type: 1.2.1 @@ -7484,7 +7630,7 @@ snapshots: path-parse: 1.0.7 supports-preserve-symlinks-flag: 1.0.0 - resolve@2.0.0-next.7: + resolve@2.0.0-next.6: dependencies: es-errors: 1.3.0 is-core-module: 2.16.2 @@ -7555,11 +7701,11 @@ snapshots: semver-truncate@3.0.0: dependencies: - semver: 7.8.4 + semver: 7.8.0 semver@6.3.1: {} - semver@7.8.4: {} + semver@7.8.0: {} set-function-length@1.2.2: dependencies: @@ -7581,13 +7727,13 @@ snapshots: dependencies: dunder-proto: 1.0.1 es-errors: 1.3.0 - es-object-atoms: 1.1.2 + es-object-atoms: 1.1.1 sharp@0.34.5: dependencies: '@img/colour': 1.1.0 detect-libc: 2.1.2 - semver: 7.8.4 + semver: 7.8.0 optionalDependencies: '@img/sharp-darwin-arm64': 0.34.5 '@img/sharp-darwin-x64': 0.34.5 @@ -7641,7 +7787,7 @@ snapshots: object-inspect: 1.13.4 side-channel-map: 1.0.1 - side-channel@1.1.1: + side-channel@1.1.0: dependencies: es-errors: 1.3.0 object-inspect: 1.13.4 @@ -7699,7 +7845,7 @@ snapshots: streamsearch@1.1.0: {} - streamx@2.28.0: + streamx@2.25.0: dependencies: events-universal: 1.0.1 fast-fifo: 1.3.2 @@ -7722,29 +7868,28 @@ snapshots: define-properties: 1.2.1 es-abstract: 1.24.2 - string.prototype.trim@1.2.11: + string.prototype.trim@1.2.10: dependencies: call-bind: 1.0.9 call-bound: 1.0.4 define-data-property: 1.1.4 define-properties: 1.2.1 es-abstract: 1.24.2 - es-object-atoms: 1.1.2 + es-object-atoms: 1.1.1 has-property-descriptors: 1.0.2 - safe-regex-test: 1.1.0 - string.prototype.trimend@1.0.10: + string.prototype.trimend@1.0.9: dependencies: call-bind: 1.0.9 call-bound: 1.0.4 define-properties: 1.2.1 - es-object-atoms: 1.1.2 + es-object-atoms: 1.1.1 string.prototype.trimstart@1.0.8: dependencies: call-bind: 1.0.9 define-properties: 1.2.1 - es-object-atoms: 1.1.2 + es-object-atoms: 1.1.1 string_decoder@0.10.31: {} @@ -7797,8 +7942,6 @@ snapshots: supports-preserve-symlinks-flag@1.0.0: {} - system-architecture@1.0.0: {} - tabbable@6.4.0: {} tapable@2.3.3: {} @@ -7807,7 +7950,7 @@ snapshots: dependencies: b4a: 1.8.1 fast-fifo: 1.3.2 - streamx: 2.28.0 + streamx: 2.25.0 transitivePeerDependencies: - bare-abort-controller - react-native-b4a @@ -7833,7 +7976,7 @@ snapshots: dependencies: convert-hrtime: 5.0.0 - tinyglobby@0.2.17: + tinyglobby@0.2.16: dependencies: fdir: 6.5.0(picomatch@4.0.4) picomatch: 4.0.4 @@ -7873,6 +8016,13 @@ snapshots: tslib@2.8.1: {} + tsx@4.21.0: + dependencies: + esbuild: 0.28.1 + get-tsconfig: 4.14.0 + optionalDependencies: + fsevents: 2.3.3 + tsx@4.22.4: dependencies: esbuild: 0.28.1 @@ -7909,7 +8059,7 @@ snapshots: is-typed-array: 1.1.15 reflect.getprototypeof: 1.0.10 - typed-array-length@1.0.8: + typed-array-length@1.0.7: dependencies: call-bind: 1.0.9 for-each: 0.3.5 @@ -7918,12 +8068,12 @@ snapshots: possible-typed-array-names: 1.1.0 reflect.getprototypeof: 1.0.10 - typescript-eslint@8.61.1(eslint@9.39.4)(typescript@5.7.3): + typescript-eslint@8.59.3(eslint@9.39.4)(typescript@5.7.3): dependencies: - '@typescript-eslint/eslint-plugin': 8.61.1(@typescript-eslint/parser@8.61.1(eslint@9.39.4)(typescript@5.7.3))(eslint@9.39.4)(typescript@5.7.3) - '@typescript-eslint/parser': 8.61.1(eslint@9.39.4)(typescript@5.7.3) - '@typescript-eslint/typescript-estree': 8.61.1(typescript@5.7.3) - '@typescript-eslint/utils': 8.61.1(eslint@9.39.4)(typescript@5.7.3) + '@typescript-eslint/eslint-plugin': 8.59.3(@typescript-eslint/parser@8.59.3(eslint@9.39.4)(typescript@5.7.3))(eslint@9.39.4)(typescript@5.7.3) + '@typescript-eslint/parser': 8.59.3(eslint@9.39.4)(typescript@5.7.3) + '@typescript-eslint/typescript-estree': 8.59.3(typescript@5.7.3) + '@typescript-eslint/utils': 8.59.3(eslint@9.39.4)(typescript@5.7.3) eslint: 9.39.4 typescript: 5.7.3 transitivePeerDependencies: @@ -7949,8 +8099,6 @@ snapshots: undici-types@7.21.0: {} - undici-types@7.24.6: {} - undici@7.28.0: {} unicorn-magic@0.3.0: {} @@ -8028,7 +8176,7 @@ snapshots: which-builtin-type@1.2.1: dependencies: call-bound: 1.0.4 - function.prototype.name: 1.2.0 + function.prototype.name: 1.1.8 has-tostringtag: 1.0.2 is-async-function: 2.1.1 is-date-object: 1.1.0 @@ -8039,7 +8187,7 @@ snapshots: isarray: 2.0.5 which-boxed-primitive: 1.1.1 which-collection: 1.0.2 - which-typed-array: 1.1.22 + which-typed-array: 1.1.20 which-collection@1.0.2: dependencies: @@ -8048,7 +8196,7 @@ snapshots: is-weakmap: 2.0.2 is-weakset: 2.0.4 - which-typed-array@1.1.22: + which-typed-array@1.1.20: dependencies: available-typed-arrays: 1.0.7 call-bind: 1.0.9 @@ -8097,8 +8245,9 @@ snapshots: y18n: 5.0.8 yargs-parser: 20.2.9 - yauzl@3.4.0: + yauzl@3.3.0: dependencies: + buffer-crc32: 0.2.13 pend: 1.2.0 yjs@13.6.27: diff --git a/content-translator/src/client/components/TranslatorModal/TranslatorModal.tsx b/content-translator/src/client/components/TranslatorModal/TranslatorModal.tsx index 53bb2aa1..a3b2193f 100644 --- a/content-translator/src/client/components/TranslatorModal/TranslatorModal.tsx +++ b/content-translator/src/client/components/TranslatorModal/TranslatorModal.tsx @@ -4,6 +4,8 @@ import { getTranslation } from '@payloadcms/translations' import { Button, LoadingOverlay, Modal, Popup, PopupList, useTranslation } from '@payloadcms/ui' import { useState } from 'react' +import type { TranslateMode } from '../../../translate/types.js' + import { useTranslator } from '../../providers/Translator/context.js' import { LocaleLabel } from '../LocaleLabel/LocaleLabel.js' @@ -25,9 +27,9 @@ export const TranslatorModal = () => { const [isTranslating, setIsTranslating] = useState(false) - async function onSubmit(emptyOnly: boolean) { + async function onSubmit(mode: TranslateMode) { setIsTranslating(true) - await submit({ emptyOnly }) + await submit({ mode }) setIsTranslating(false) } @@ -105,10 +107,13 @@ export const TranslatorModal = () => { ) : ( <> - - + diff --git a/content-translator/src/client/providers/Translator/TranslatorProvider.tsx b/content-translator/src/client/providers/Translator/TranslatorProvider.tsx index 33e6261a..1d1fdaf5 100644 --- a/content-translator/src/client/providers/Translator/TranslatorProvider.tsx +++ b/content-translator/src/client/providers/Translator/TranslatorProvider.tsx @@ -12,7 +12,7 @@ import { import { reduceFieldsToValues } from 'payload/shared' import { type ReactNode, useEffect, useState } from 'react' -import type { TranslateArgs } from '../../../translate/types.js' +import type { TranslateArgs, TranslateMode } from '../../../translate/types.js' import type { TranslatorClientConfig } from '../../../types.js' import { createClient } from '../../api/index.js' @@ -74,7 +74,7 @@ export const TranslatorProvider = ({ children }: { children: ReactNode }) => { const closeTranslator = () => modal.closeModal(modalSlug) - const submit = async ({ emptyOnly }: { emptyOnly: boolean }) => { + const submit = async ({ mode }: { mode: TranslateMode }) => { if (!resolver) { return } @@ -83,10 +83,10 @@ export const TranslatorProvider = ({ children }: { children: ReactNode }) => { id: id === null ? undefined : id, collectionSlug, data: reduceFieldsToValues(data, true), - emptyOnly, globalSlug, locale: locale.code, localeFrom: localeToTranslateFrom, + mode, } const result = await apiClient.translate(args) @@ -120,7 +120,16 @@ export const TranslatorProvider = ({ children }: { children: ReactNode }) => { }) setModified(true) - toast.success(translatorT('successMessage')) + + const reviewCount = result.reviewCount ?? 0 + toast.success( + reviewCount > 0 + ? `${translatorT('successMessage')} ${translatorT('reviewNeeded').replace( + '{{count}}', + String(reviewCount), + )}` + : translatorT('successMessage'), + ) } } catch (e) { console.error(e) diff --git a/content-translator/src/client/providers/Translator/context.ts b/content-translator/src/client/providers/Translator/context.ts index 9d0fc949..a62e9c79 100644 --- a/content-translator/src/client/providers/Translator/context.ts +++ b/content-translator/src/client/providers/Translator/context.ts @@ -2,6 +2,8 @@ import type { Locale } from 'payload' import { createContext, useContext } from 'react' +import type { TranslateMode } from '../../../translate/types.js' + export type TranslationKey = | 'buttonLabel' | 'errorMessage' @@ -9,8 +11,10 @@ export type TranslationKey = | 'modalSourceLanguage' | 'modalTitle' | 'modalTranslating' + | 'reviewNeeded' | 'submitButtonLabelEmpty' | 'submitButtonLabelFull' + | 'submitButtonLabelIncremental' | 'successMessage' type TranslatorContextData = { @@ -21,7 +25,7 @@ type TranslatorContextData = { openTranslator: () => void resolver: { key: string } | null setLocaleToTranslateFrom: (code: string) => void - submit: (args: { emptyOnly: boolean }) => Promise + submit: (args: { mode: TranslateMode }) => Promise translatorT: (key: TranslationKey) => string } diff --git a/content-translator/src/i18n/translations.ts b/content-translator/src/i18n/translations.ts index b2421c13..ea54b7ee 100644 --- a/content-translator/src/i18n/translations.ts +++ b/content-translator/src/i18n/translations.ts @@ -8,8 +8,10 @@ export const translations = { modalSourceLanguage: 'Quellsprache', modalTitle: 'Felder aus anderer Sprache übersetzen', modalTranslating: 'Felder werden übersetzt...', + reviewNeeded: '{{count}} Absätze müssen überprüft werden.', submitButtonLabelEmpty: 'Nur leere Felder übersetzen', submitButtonLabelFull: 'Alle Felder übersetzen', + submitButtonLabelIncremental: 'Neue & geänderte Inhalte übersetzen', successMessage: 'Erfolgreich übersetzt. Drücken Sie "Speichern", um die Änderungen anzuwenden.', }, @@ -23,8 +25,10 @@ export const translations = { modalSourceLanguage: 'Source language', modalTitle: 'Translate fields from another language', modalTranslating: 'Fields are being translated...', + reviewNeeded: '{{count}} paragraphs need review.', submitButtonLabelEmpty: 'Translate only empty fields', submitButtonLabelFull: 'Translate all fields', + submitButtonLabelIncremental: 'Translate new & changed content', successMessage: 'Successfully translated. Press "Save" to apply the changes.', }, }, diff --git a/content-translator/src/translate/endpoint.ts b/content-translator/src/translate/endpoint.ts index 066ddb13..6ada0c87 100644 --- a/content-translator/src/translate/endpoint.ts +++ b/content-translator/src/translate/endpoint.ts @@ -21,16 +21,16 @@ export const translateEndpoint = const args: TranslateEndpointArgs = await req.json() - const { id, collectionSlug, data, emptyOnly, globalSlug, locale, localeFrom } = args + const { id, collectionSlug, data, globalSlug, locale, localeFrom, mode } = args const result = await translateOperation({ id, collectionSlug, data, - emptyOnly, globalSlug, locale, localeFrom, + mode, overrideAccess: false, req, update: false, diff --git a/content-translator/src/translate/operation.ts b/content-translator/src/translate/operation.ts index 33e2bb1c..33cc271c 100644 --- a/content-translator/src/translate/operation.ts +++ b/content-translator/src/translate/operation.ts @@ -2,7 +2,12 @@ import he from 'he' import { APIError, type Payload, type PayloadRequest } from 'payload' import type { TranslatorCustomConfig } from '../types.js' -import type { TranslateArgs, TranslateResult, ValueToTranslate } from './types.js' +import type { + IncrementalAccumulator, + TranslateArgs, + TranslateResult, + ValueToTranslate, +} from './types.js' import { findEntityWithConfig } from './findEntityWithConfig.js' import { traverseFields } from './traverseFields.js' @@ -44,6 +49,7 @@ export const translateOperation = async (args: TranslateOperationArgs) => { } const valuesToTranslate: ValueToTranslate[] = [] + const incremental: IncrementalAccumulator = { conflictCount: 0, stamps: [] } let translatedData = args.data @@ -62,8 +68,10 @@ export const translateOperation = async (args: TranslateOperationArgs) => { traverseFields({ dataFrom, - emptyOnly: args.emptyOnly ?? false, fields: config.fields, + incremental, + localeFrom: args.localeFrom, + mode: args.mode ?? 'all', payloadConfig: req.payload.config, translatedData, valuesToTranslate, @@ -106,6 +114,11 @@ export const translateOperation = async (args: TranslateOperationArgs) => { valuesToTranslate[index].onTranslate(formattedValue) }) + // Stamp content-addressed hashes now that the translated text is in place. + for (const stamp of incremental.stamps) { + stamp() + } + if (args.update) { await updateEntity({ id, @@ -120,6 +133,7 @@ export const translateOperation = async (args: TranslateOperationArgs) => { } result = { + reviewCount: incremental.conflictCount, success: true, translatedData, } diff --git a/content-translator/src/translate/richtext/hashNode.ts b/content-translator/src/translate/richtext/hashNode.ts new file mode 100644 index 00000000..c7c31319 Binary files /dev/null and b/content-translator/src/translate/richtext/hashNode.ts differ diff --git a/content-translator/src/translate/richtext/nodeState.ts b/content-translator/src/translate/richtext/nodeState.ts new file mode 100644 index 00000000..f38e88d4 --- /dev/null +++ b/content-translator/src/translate/richtext/nodeState.ts @@ -0,0 +1,116 @@ +type LexicalNode = Record + +/** + * Both hashes live in Lexical's NodeState slot (`$`) under a single namespaced + * key so they travel with the node through copy/paste, reorder, history and + * admin-editor saves. Stored inline on every top-level node: + * + * "$": { "translator-plugin": { "srcHash": { "": }, "outHash": } } + * + * - `srcHash` — per source locale, the hash of the source text this node was + * translated from. Keyed by locale because the source language is chosen per + * run: translating the same target from EN vs. DE produces different source + * text, so each gets its own entry and switching sources does not falsely + * invalidate the other. + * - `outHash` — hash of the machine output written here (detects later manual + * edits). Single value: it hashes the target's own text, independent of which + * source produced it. + */ +const STATE_KEY = '$' +const NS_KEY = 'translator-plugin' +const SRC_KEY = 'srcHash' +const OUT_KEY = 'outHash' + +type NodeHashes = { + outHash?: string + srcHash?: string +} + +const readNamespace = (node: LexicalNode): Record | undefined => { + const state = node[STATE_KEY] + if (!state || typeof state !== 'object') { + return undefined + } + const ns = (state as Record)[NS_KEY] + return ns && typeof ns === 'object' ? (ns as Record) : undefined +} + +/** Read the hashes relevant to a run translating from `sourceLocale`. */ +export const getNodeHashes = (node: LexicalNode, sourceLocale: string): NodeHashes => { + const ns = readNamespace(node) + const srcMap = ns?.[SRC_KEY] + const srcHash = + srcMap && typeof srcMap === 'object' + ? (srcMap as Record)[sourceLocale] + : undefined + const outHash = ns?.[OUT_KEY] + + return { + outHash: typeof outHash === 'string' ? outHash : undefined, + srcHash: typeof srcHash === 'string' ? srcHash : undefined, + } +} + +/** + * Copy the per-locale `srcHash` map from one node onto another (without the + * `outHash`, which describes the target text and is set fresh on translate). + * Used when a paragraph is retranslated from a new source locale: the fresh + * clone inherits the source hashes of the locales it was previously translated + * from, so a later run from one of those locales can still reuse it instead of + * retranslating. + */ +export const inheritSrcHashes = (target: LexicalNode, source: LexicalNode): void => { + const srcMap = readNamespace(source)?.[SRC_KEY] + if (!srcMap || typeof srcMap !== 'object') { + return + } + + const state = + target[STATE_KEY] && typeof target[STATE_KEY] === 'object' + ? (target[STATE_KEY] as Record) + : {} + const existingNs = state[NS_KEY] + const ns = + existingNs && typeof existingNs === 'object' ? (existingNs as Record) : {} + + // Target's own per-locale hashes win over inherited ones. + const merged = { ...srcMap } + const existingMap = ns[SRC_KEY] + if (existingMap && typeof existingMap === 'object') { + Object.assign(merged, existingMap) + } + + ns[SRC_KEY] = merged + state[NS_KEY] = ns + target[STATE_KEY] = state +} + +export const setNodeHashes = ( + node: LexicalNode, + sourceLocale: string, + srcHash: string, + outHash: string, +): void => { + const state = + node[STATE_KEY] && typeof node[STATE_KEY] === 'object' + ? (node[STATE_KEY] as Record) + : {} + + const existingNs = state[NS_KEY] + const ns = + existingNs && typeof existingNs === 'object' ? (existingNs as Record) : {} + + const existingSrcMap = ns[SRC_KEY] + // Keep other locales' source hashes so a later run from a different source + // can still reuse an unchanged paragraph instead of retranslating it. + const srcMap = + existingSrcMap && typeof existingSrcMap === 'object' + ? (existingSrcMap as Record) + : {} + srcMap[sourceLocale] = srcHash + + ns[SRC_KEY] = srcMap + ns[OUT_KEY] = outHash + state[NS_KEY] = ns + node[STATE_KEY] = state +} diff --git a/content-translator/src/translate/richtext/reconcileIncremental.ts b/content-translator/src/translate/richtext/reconcileIncremental.ts new file mode 100644 index 00000000..1966d01a --- /dev/null +++ b/content-translator/src/translate/richtext/reconcileIncremental.ts @@ -0,0 +1,120 @@ +import { hashNode, hashText, nodePlainText } from './hashNode.js' +import { getNodeHashes, inheritSrcHashes, setNodeHashes } from './nodeState.js' + +type LexicalNode = Record + +export type ReconcileResult = { + /** The merged target children, in source order. */ + children: LexicalNode[] + /** Number of units left untouched because their source changed under a hand-edited translation. */ + conflictCount: number + /** Deferred hash stamps to run after the translation values have been applied. */ + stamps: Array<() => void> +} + +/** + * Merge a source lexical tree's top-level nodes into an existing target tree, + * translating only new or changed units and preserving everything else. + * + * Identity is content-addressed: each source unit hashes to `h`, and the target + * nodes are indexed by the `srcHash` they were translated from for this source + * locale. A match means the source is unchanged → the target node is reused + * as-is (manual edits preserved, no translation). A miss means new or changed: + * + * - if the paired prior target still holds untouched machine output → retranslate + * - if it was hand-edited → leave it in place and count it as needing review + * + * Target nodes whose `srcHash` no longer appears in the source are deletions and + * are dropped. The result follows source order, so inserts and reorders land in + * the right place. + */ +export const reconcileIncremental = ({ + collectUnitTexts, + localeFrom, + sourceChildren, + targetChildren, +}: { + /** Push the unit node's translatable text into valuesToTranslate (translated in place). */ + collectUnitTexts: (unitNode: LexicalNode) => void + /** Source locale of this run; selects which per-locale srcHash to join on. */ + localeFrom: string + sourceChildren: LexicalNode[] + targetChildren: LexicalNode[] +}): ReconcileResult => { + // Content-addressed index: stored srcHash (for this source locale) -> queue of + // target nodes (queued so duplicate-text units are consumed in order rather + // than colliding). + const targetsBySrcHash = new Map() + for (const targetNode of targetChildren) { + const { srcHash } = getNodeHashes(targetNode, localeFrom) + if (srcHash) { + const queue = targetsBySrcHash.get(srcHash) ?? [] + queue.push(targetNode) + targetsBySrcHash.set(srcHash, queue) + } + } + + // Pass 1: reuse content-matched units, set aside the rest as work. + const consumed = new Set() + const plan: Array< + { node: LexicalNode; type: 'reuse' } | { sourceNode: LexicalNode; type: 'work' } + > = [] + + for (const sourceNode of sourceChildren) { + const match = targetsBySrcHash.get(hashNode(sourceNode))?.shift() + if (match) { + consumed.add(match) + plan.push({ type: 'reuse', node: match }) + } else { + plan.push({ type: 'work', sourceNode }) + } + } + + // Unconsumed targets, in original order — paired positionally with changed + // source units to decide retranslate vs. review-conflict. + const leftoverTargets = targetChildren.filter((node) => !consumed.has(node)) + let leftoverIndex = 0 + + const stamps: Array<() => void> = [] + let conflictCount = 0 + const children: LexicalNode[] = [] + + for (const step of plan) { + if (step.type === 'reuse') { + children.push(step.node) + continue + } + + const prior = + leftoverIndex < leftoverTargets.length ? leftoverTargets[leftoverIndex++] : undefined + + if (prior) { + const { outHash } = getNodeHashes(prior, localeFrom) + const priorEdited = outHash !== undefined && outHash !== hashText(nodePlainText(prior)) + + if (priorEdited) { + // Source moved under a hand-tuned translation: keep the human's version. + conflictCount += 1 + children.push(prior) + continue + } + } + + // New unit, or changed source over untouched machine output: translate a + // fresh clone of the source so its text is overwritten and re-stamped. If it + // replaces a prior translation, inherit that node's per-locale source hashes + // so a later run from a different source locale can still reuse it. + const clone = structuredClone(step.sourceNode) + const srcHash = hashNode(clone) + collectUnitTexts(clone) + stamps.push(() => { + if (prior) { + inheritSrcHashes(clone, prior) + } + setNodeHashes(clone, localeFrom, srcHash, hashText(nodePlainText(clone))) + }) + children.push(clone) + } + + return { children, conflictCount, stamps } +} diff --git a/content-translator/src/translate/traverseFields.ts b/content-translator/src/translate/traverseFields.ts index 7d8e1746..94a35e17 100644 --- a/content-translator/src/translate/traverseFields.ts +++ b/content-translator/src/translate/traverseFields.ts @@ -5,19 +5,37 @@ import { tabHasName } from 'payload/shared' const ObjectID = typeof ObjectIDModule === 'function' ? ObjectIDModule : ObjectIDModule.default -import type { ValueToTranslate } from './types.js' +import type { IncrementalAccumulator, TranslateMode, ValueToTranslate } from './types.js' import { isEmpty } from '../utils/isEmpty.js' +import { hashNode, hashText, nodePlainText } from './richtext/hashNode.js' +import { setNodeHashes } from './richtext/nodeState.js' +import { reconcileIncremental } from './richtext/reconcileIncremental.js' import { traverseRichText } from './traverseRichText.js' const isUnsafeKey = (key: string): boolean => key === '__proto__' || key === 'constructor' || key === 'prototype' +/** + * Write to a dynamic, config-derived key while refusing prototype-polluting + * keys. The loop already skips unsafe field names up front, so this is + * defense-in-depth that also keeps the assignment provably safe at each call + * site (and quiet to static analysis). + */ +const assignSafely = (target: Record, key: string, value: unknown): void => { + if (isUnsafeKey(key)) { + return + } + target[key] = value +} + export const traverseFields = ({ dataFrom, - emptyOnly, fields, + incremental, + localeFrom, localizedParent, + mode, payloadConfig, siblingDataFrom, siblingDataTranslated, @@ -25,9 +43,12 @@ export const traverseFields = ({ valuesToTranslate, }: { dataFrom: Record - emptyOnly: boolean fields: Field[] + incremental?: IncrementalAccumulator + /** Source locale of this run; selects which per-locale srcHash to read/write. */ + localeFrom: string localizedParent?: boolean + mode: TranslateMode payloadConfig: SanitizedConfig siblingDataFrom?: Record siblingDataTranslated?: Record @@ -36,6 +57,19 @@ export const traverseFields = ({ }) => { siblingDataFrom = siblingDataFrom ?? dataFrom siblingDataTranslated = siblingDataTranslated ?? translatedData + incremental = incremental ?? { conflictCount: 0, stamps: [] } + + // LIMITATION: change detection only works for lexical richText. `incremental` + // does node-level diffing of lexical paragraphs/blocks (see the richText case + // below); for every other field type it falls back to empty-only here. So a + // text/textarea/number/array/blocks value whose SOURCE changed after it was + // already translated is NOT retranslated in incremental mode — only fields + // that are still empty get filled. Catching edits on those would need a hash + // of the source stored per field (plain fields have no NodeState slot to carry + // it inline, unlike lexical nodes), i.e. the sidecar approach — out of scope + // here. Despite the "new & changed" label, "changed" currently means lexical + // content only. + const fillEmptyOnly = mode !== 'all' for (const field of fields) { if ('virtual' in field && field.virtual) { @@ -60,7 +94,7 @@ export const traverseFields = ({ (siblingDataTranslated[field.name] as { id: number | string }[] | undefined) ?? [] if (field.localized || localizedParent) { - if (arrayDataTranslated.length > 0 && emptyOnly) { + if (arrayDataTranslated.length > 0 && fillEmptyOnly) { break } @@ -72,9 +106,11 @@ export const traverseFields = ({ arrayDataTranslated.forEach((item, index) => { traverseFields({ dataFrom, - emptyOnly, fields: field.fields, + incremental, + localeFrom, localizedParent: localizedParent ?? field.localized, + mode, payloadConfig, siblingDataFrom: arrayDataFrom[index], siblingDataTranslated: item, @@ -83,7 +119,7 @@ export const traverseFields = ({ }) }) - siblingDataTranslated[field.name] = arrayDataTranslated + assignSafely(siblingDataTranslated, field.name, arrayDataTranslated) break } @@ -104,7 +140,7 @@ export const traverseFields = ({ | undefined) ?? [] if (field.localized || localizedParent) { - if (blocksDataTranslated.length > 0 && emptyOnly) { + if (blocksDataTranslated.length > 0 && fillEmptyOnly) { break } @@ -140,9 +176,11 @@ export const traverseFields = ({ traverseFields({ dataFrom, - emptyOnly, fields: blockConfig.fields, + incremental, + localeFrom, localizedParent: localizedParent ?? field.localized, + mode, payloadConfig, siblingDataFrom: blocksDataFrom[index], siblingDataTranslated: item, @@ -151,7 +189,7 @@ export const traverseFields = ({ }) }) - siblingDataTranslated[field.name] = blocksDataTranslated + assignSafely(siblingDataTranslated, field.name, blocksDataTranslated) break } @@ -167,16 +205,18 @@ export const traverseFields = ({ case 'relationship': case 'select': case 'upload': - siblingDataTranslated[field.name] = siblingDataFrom[field.name] + assignSafely(siblingDataTranslated, field.name, siblingDataFrom[field.name]) break case 'collapsible': case 'row': traverseFields({ dataFrom, - emptyOnly, fields: field.fields, + incremental, + localeFrom, localizedParent, + mode, payloadConfig, siblingDataFrom, siblingDataTranslated, @@ -191,9 +231,11 @@ export const traverseFields = ({ // like row/collapsible, propagating the parent's localization context. traverseFields({ dataFrom, - emptyOnly, fields: field.fields, + incremental, + localeFrom, localizedParent, + mode, payloadConfig, siblingDataFrom, siblingDataTranslated, @@ -214,9 +256,11 @@ export const traverseFields = ({ traverseFields({ dataFrom, - emptyOnly, fields: field.fields, + incremental, + localeFrom, localizedParent: field.localized, + mode, payloadConfig, siblingDataFrom: groupDataFrom, siblingDataTranslated: groupDataTranslated, @@ -224,7 +268,7 @@ export const traverseFields = ({ valuesToTranslate, }) - siblingDataTranslated[field.name] = groupDataTranslated + assignSafely(siblingDataTranslated, field.name, groupDataTranslated) break } @@ -237,19 +281,59 @@ export const traverseFields = ({ break } - if (emptyOnly && !isEmpty(siblingDataTranslated[field.name])) { + const richTextDataFrom = siblingDataFrom[field.name] as Record + + if (!richTextDataFrom) { break } - const richTextDataFrom = siblingDataFrom[field.name] as object + const isLexical = 'root' in richTextDataFrom + const existingTarget = siblingDataTranslated[field.name] + + // Incremental: diff source against the existing translation at the + // paragraph/block level instead of skipping or wholesale-replacing. + if (mode === 'incremental' && isLexical && !isEmpty(existingTarget)) { + const sourceRoot = richTextDataFrom.root as Record + const targetRoot = (existingTarget as Record).root as Record< + string, + unknown + > + + const { children, conflictCount, stamps } = reconcileIncremental({ + collectUnitTexts: (unitNode) => { + traverseRichText({ + incremental, + localeFrom, + mode: 'all', + payloadConfig, + root: unitNode, + translatedData, + valuesToTranslate, + }) + }, + localeFrom, + sourceChildren: (sourceRoot?.children as Record[]) ?? [], + targetChildren: (targetRoot?.children as Record[]) ?? [], + }) - siblingDataTranslated[field.name] = richTextDataFrom + assignSafely(siblingDataTranslated, field.name, { + ...richTextDataFrom, + root: { ...sourceRoot, children }, + }) + incremental.stamps.push(...stamps) + incremental.conflictCount += conflictCount - if (!richTextDataFrom) { break } - const isLexical = 'root' in richTextDataFrom + // empty: leave an already-translated field untouched. + if (fillEmptyOnly && !isEmpty(existingTarget)) { + break + } + + // all (and incremental over an empty target, or non-lexical): copy the + // source tree and translate every text node. + assignSafely(siblingDataTranslated, field.name, richTextDataFrom) if (!isLexical) { break @@ -262,12 +346,26 @@ export const traverseFields = ({ if (root) { traverseRichText({ - emptyOnly, + incremental, + localeFrom, + mode, payloadConfig, root, translatedData, valuesToTranslate, }) + + // Stamp every top-level node so a later incremental run has the + // content-addressed hashes to join on. Capture srcHash now (before the + // deferred onTranslate mutates the text) and outHash after. + if (Array.isArray(root.children)) { + for (const child of root.children as Record[]) { + const srcHash = hashNode(child) + incremental.stamps.push(() => + setNodeHashes(child, localeFrom, srcHash, hashText(nodePlainText(child))), + ) + } + } } break @@ -295,9 +393,11 @@ export const traverseFields = ({ traverseFields({ dataFrom, - emptyOnly, fields: tab.fields, + incremental, + localeFrom, localizedParent: tab.localized, + mode, payloadConfig, siblingDataFrom: tabDataFrom, siblingDataTranslated: tabDataTranslated, @@ -306,7 +406,7 @@ export const traverseFields = ({ }) if (hasName) { - siblingDataTranslated[tab.name] = tabDataTranslated + assignSafely(siblingDataTranslated, tab.name, tabDataTranslated) } } @@ -320,7 +420,7 @@ export const traverseFields = ({ if (!(field.localized || localizedParent) || isEmpty(siblingDataFrom[field.name])) { break } - if (emptyOnly && siblingDataTranslated[field.name]) { + if (fillEmptyOnly && siblingDataTranslated[field.name]) { break } @@ -340,7 +440,7 @@ export const traverseFields = ({ // failed element keeps its original text. if (Array.isArray(fieldValue)) { const translatedArray = [...fieldValue] - siblingDataTranslated[field.name] = translatedArray + assignSafely(siblingDataTranslated, field.name, translatedArray) fieldValue.forEach((item, itemIndex) => { if (typeof item !== 'string' || isEmpty(item)) { @@ -359,8 +459,8 @@ export const traverseFields = ({ } valuesToTranslate.push({ - onTranslate: (translated) => { - siblingDataTranslated[field.name] = translated + onTranslate: (translated: string) => { + assignSafely(siblingDataTranslated, field.name, translated) }, value: fieldValue, }) diff --git a/content-translator/src/translate/traverseRichText.ts b/content-translator/src/translate/traverseRichText.ts index 1ce0051a..f45da732 100644 --- a/content-translator/src/translate/traverseRichText.ts +++ b/content-translator/src/translate/traverseRichText.ts @@ -1,6 +1,6 @@ import type { FlattenedBlock, SanitizedConfig } from 'payload' -import type { ValueToTranslate } from './types.js' +import type { IncrementalAccumulator, TranslateMode, ValueToTranslate } from './types.js' import { traverseFields } from './traverseFields.js' @@ -85,14 +85,18 @@ const pushTextRun = (run: Record[], valuesToTranslate: ValueToTrans } export const traverseRichText = ({ - emptyOnly, + incremental, + localeFrom, + mode, payloadConfig, root, siblingData, translatedData, valuesToTranslate, }: { - emptyOnly: boolean + incremental?: IncrementalAccumulator + localeFrom: string + mode: TranslateMode payloadConfig: SanitizedConfig root: Record siblingData?: Record @@ -119,9 +123,11 @@ export const traverseRichText = ({ // Traverse the fields of the block traverseFields({ dataFrom: root, - emptyOnly, fields: blockConfig.fields, + incremental, + localeFrom, localizedParent: false, + mode, payloadConfig, siblingDataFrom: blockData, siblingDataTranslated: blockData, @@ -159,7 +165,9 @@ export const traverseRichText = ({ pushTextRun(run, valuesToTranslate) } else { traverseRichText({ - emptyOnly, + incremental, + localeFrom, + mode, payloadConfig, root, siblingData: child, diff --git a/content-translator/src/translate/types.ts b/content-translator/src/translate/types.ts index 58c551ef..48a088fa 100644 --- a/content-translator/src/translate/types.ts +++ b/content-translator/src/translate/types.ts @@ -3,26 +3,44 @@ export type ValueToTranslate = { value: any } +/** + * - `all` — retranslate every field, discarding existing target content. + * - `empty` — only fill fields that have no target value yet. + * - `incremental` — for richText, translate only new or changed nodes and keep + * existing translations; other field types behave like `empty`. + */ +export type TranslateMode = 'all' | 'empty' | 'incremental' + +/** Mutable accumulator threaded through traverseFields for incremental richText. */ +export type IncrementalAccumulator = { + /** Units left untouched because their source changed under a hand-edited translation. */ + conflictCount: number + /** Deferred hash stamps, run after the translation values have been applied. */ + stamps: Array<() => void> +} + export type TranslateArgs = { collectionSlug?: string data?: Record - emptyOnly?: boolean globalSlug?: string id?: number | string /** active locale */ locale: string localeFrom: string + mode?: TranslateMode overrideAccess?: boolean update?: boolean } export type TranslateResult = | { - success: false - } - | { + /** Number of richText paragraphs flagged for review (incremental mode). */ + reviewCount?: number success: true translatedData: Record } + | { + success: false + } export type TranslateEndpointArgs = Omit diff --git a/content-translator/test/incrementalRichText.test.ts b/content-translator/test/incrementalRichText.test.ts new file mode 100644 index 00000000..5fd5f90e --- /dev/null +++ b/content-translator/test/incrementalRichText.test.ts @@ -0,0 +1,219 @@ +import type { Field, SanitizedConfig } from 'payload' + +import { createHeadlessEditor } from '@lexical/headless' +import { + defaultEditorConfig, + getEnabledNodes, + sanitizeServerEditorConfig, +} from '@payloadcms/richtext-lexical' +import assert from 'node:assert/strict' +import { describe, test } from 'node:test' + +import type { IncrementalAccumulator, ValueToTranslate } from '../src/translate/types.ts' + +import { traverseFields } from '../src/translate/traverseFields.ts' + +const payloadConfig = {} as SanitizedConfig + +const contentFields: Field[] = [{ name: 'content', type: 'richText', localized: true }] + +type LexNode = Record + +const lex = (children: LexNode[]) => ({ + root: { type: 'root', children, direction: 'ltr', format: '', indent: 0, version: 1 }, +}) + +const para = (text: null | string, extra: LexNode = {}): LexNode => ({ + type: 'paragraph', + children: + text === null + ? [] + : [{ type: 'text', detail: 0, format: 0, mode: 'normal', style: '', text, version: 1 }], + direction: 'ltr', + format: '', + indent: 0, + version: 1, + ...extra, +}) + +const paraText = (node: LexNode): string => + Array.isArray(node?.children) + ? node.children.map((c: LexNode) => (typeof c.text === 'string' ? c.text : '')).join('') + : '' + +/** + * Run a traverse pass, apply the mock translation to every collected value, + * then run the deferred hash stamps — exactly as the operation does. + */ +const runPass = ( + mode: 'all' | 'empty' | 'incremental', + dataFrom: Record, + translatedData: Record, + localeFrom = 'en', +) => { + const valuesToTranslate: ValueToTranslate[] = [] + const incremental: IncrementalAccumulator = { conflictCount: 0, stamps: [] } + + traverseFields({ + dataFrom, + fields: contentFields, + incremental, + localeFrom, + mode, + payloadConfig, + translatedData, + valuesToTranslate, + }) + + const translatedValues = valuesToTranslate.map((v) => v.value) + for (const v of valuesToTranslate) { + v.onTranslate(`TRANSLATED:${v.value}`) + } + for (const stamp of incremental.stamps) { + stamp() + } + + return { conflictCount: incremental.conflictCount, translatedData, translatedValues } +} + +/** Produce a fully translated + stamped target tree from a source tree (initial "all" run). */ +const initialTranslate = (sourceChildren: LexNode[], localeFrom = 'en') => { + const dataFrom = { content: lex(sourceChildren) } + const translatedData: Record = {} + runPass('all', dataFrom, translatedData, localeFrom) + return translatedData as { content: ReturnType } +} + +const targetChildren = (translatedData: { content: ReturnType }): LexNode[] => + translatedData.content.root.children + +describe('incremental richText translation', () => { + test('appended source paragraph is translated and inserted; existing paragraphs are not retranslated', () => { + const target = initialTranslate([para('Alpha'), para('Beta')]) + const before = targetChildren(target).map(paraText) + + const result = runPass( + 'incremental', + { content: lex([para('Alpha'), para('Beta'), para('Gamma')]) }, + target, + ) + + assert.deepEqual(result.translatedValues, ['Gamma']) + const after = targetChildren(target).map(paraText) + assert.deepEqual(after, [before[0], before[1], 'TRANSLATED:Gamma']) + }) + + test('a paragraph inserted in the middle lands in the correct position, not appended at the end', () => { + const target = initialTranslate([para('One'), para('Three')]) + + const result = runPass( + 'incremental', + { content: lex([para('One'), para('Two'), para('Three')]) }, + target, + ) + + assert.deepEqual(result.translatedValues, ['Two']) + assert.deepEqual(targetChildren(target).map(paraText), [ + 'TRANSLATED:One', + 'TRANSLATED:Two', + 'TRANSLATED:Three', + ]) + }) + + test('editing a source paragraph retranslates only that paragraph', () => { + const target = initialTranslate([para('One'), para('Two'), para('Three')]) + + const result = runPass( + 'incremental', + { content: lex([para('One'), para('Two changed'), para('Three')]) }, + target, + ) + + assert.deepEqual(result.translatedValues, ['Two changed']) + assert.deepEqual(targetChildren(target).map(paraText), [ + 'TRANSLATED:One', + 'TRANSLATED:Two changed', + 'TRANSLATED:Three', + ]) + }) + + test('a manually edited translation is preserved when its source is unchanged', () => { + const target = initialTranslate([para('Keep me')]) + // human edits the translation in the admin panel + targetChildren(target)[0].children[0].text = 'Hand tuned translation' + + const result = runPass('incremental', { content: lex([para('Keep me')]) }, target) + + assert.deepEqual(result.translatedValues, []) + assert.equal(paraText(targetChildren(target)[0]), 'Hand tuned translation') + }) + + test('when source changes under a hand-edited translation, the translation is left in place and counted as needing review', () => { + const target = initialTranslate([para('Original source')]) + targetChildren(target)[0].children[0].text = 'Hand tuned translation' + + const result = runPass('incremental', { content: lex([para('Edited source')]) }, target) + + assert.deepEqual(result.translatedValues, []) + assert.equal(result.conflictCount, 1) + assert.equal(paraText(targetChildren(target)[0]), 'Hand tuned translation') + }) + + test('a source paragraph deleted in the source is removed from the translation', () => { + const target = initialTranslate([para('Stay'), para('Go away')]) + + runPass('incremental', { content: lex([para('Stay')]) }, target) + + assert.deepEqual(targetChildren(target).map(paraText), ['TRANSLATED:Stay']) + }) + + test('a paragraph unchanged in one source locale is still reused after translating from a different source locale', () => { + // Target first translated from EN, stamping a per-EN source hash. + const target = initialTranslate([para('Hello world')], 'en') + + // Then translated from DE (different source text) — retranslates and stamps a per-DE hash. + runPass('incremental', { content: lex([para('Hallo Welt')]) }, target, 'de') + + // Re-running from EN with the EN source unchanged must reuse, not retranslate, + // because the per-locale srcHash for EN survived the DE run. + const result = runPass('incremental', { content: lex([para('Hello world')]) }, target, 'en') + + assert.deepEqual(result.translatedValues, []) + }) + + test('incremental from an empty target translates everything', () => { + const target: Record = {} + + const result = runPass('incremental', { content: lex([para('First'), para('Second')]) }, target) + + assert.deepEqual(result.translatedValues, ['First', 'Second']) + assert.deepEqual(targetChildren(target as any).map(paraText), [ + 'TRANSLATED:First', + 'TRANSLATED:Second', + ]) + }) +}) + +describe('incremental richText storage', () => { + // This is a regression guard on a third-party assumption: the incremental + // merge stores its hashes in Lexical's NodeState ($) slot, which only works + // because Payload's default lexical config round-trips unknown $ keys + // untouched. If a future @payloadcms/richtext-lexical drops them, this fails + // and the inline-storage strategy must be reconsidered (see the sidecar + // fallback in the README). + test('NodeState hashes survive a headless-editor round-trip with the default Payload lexical config', async () => { + const sanitized = await sanitizeServerEditorConfig(defaultEditorConfig, { + collections: [], + i18n: {}, + } as unknown as SanitizedConfig) + const nodes = getEnabledNodes({ editorConfig: sanitized as any }) + const editor = createHeadlessEditor({ nodes }) + + const nodeState = { 'translator-plugin': { outHash: 'def456', srcHash: { en: 'abc123' } } } + + const editorState = editor.parseEditorState(lex([para('Hallo Welt', { $: nodeState })]) as any) + const roundTripped: any = editorState.toJSON() + + assert.deepEqual(roundTripped.root.children[0].$, nodeState) + }) +}) diff --git a/content-translator/test/traverseFields.test.ts b/content-translator/test/traverseFields.test.ts index d4a552b8..016f33ff 100644 --- a/content-translator/test/traverseFields.test.ts +++ b/content-translator/test/traverseFields.test.ts @@ -15,8 +15,9 @@ const runTraverse = (fields: Field[], dataFrom: Record, emptyOn traverseFields({ dataFrom, - emptyOnly, fields, + localeFrom: 'en', + mode: emptyOnly ? 'empty' : 'all', payloadConfig, translatedData, valuesToTranslate, @@ -207,8 +208,9 @@ describe('traverseFields - emptyOnly with missing target sub-objects (#137)', () traverseFields({ dataFrom: { meta: { title: 'Hello', description: 'World' } }, - emptyOnly: true, fields, + localeFrom: 'en', + mode: 'empty', payloadConfig, translatedData, valuesToTranslate,