From 9eb93ce70255316eda723120a6ed2147fd1753d5 Mon Sep 17 00:00:00 2001 From: Corentin Thomasset Date: Sun, 8 Sep 2024 10:05:22 +0200 Subject: [PATCH] feat(note): share file assets --- .gitignore | 3 +- packages/app-client/package.json | 2 + .../src/modules/files/files.models.test.ts | 82 ++ .../src/modules/files/files.models.ts | 42 + .../notes/components/file-uploader.tsx | 105 +++ .../src/modules/notes/notes.services.ts | 18 +- .../src/modules/notes/notes.usecases.ts | 6 +- .../modules/notes/pages/create-note.page.tsx | 39 +- .../modules/notes/pages/view-note.page.tsx | 136 ++- .../src/modules/shared/utils/safely.test.ts | 132 --- .../src/modules/shared/utils/safely.ts | 24 - packages/app-client/uno.config.ts | 5 + packages/app-server/package.json | 1 + .../modules/app/config/config.test-utils.ts | 2 +- .../src/modules/app/config/config.ts | 2 +- .../src/modules/notes/notes.repository.ts | 8 +- .../src/modules/notes/notes.routes.ts | 5 +- .../shared/injection/injection.test.ts | 25 - .../src/modules/shared/injection/injection.ts | 17 - .../app-server/src/modules/shared/types.ts | 13 - packages/lib/package.json | 4 + packages/lib/src/crypto/crypto.constants.ts | 12 + packages/lib/src/crypto/crypto.types.ts | 6 + .../lib/src/crypto/crypto.usecases.test.ts | 22 +- packages/lib/src/crypto/crypto.usecases.ts | 58 +- .../encryption-algorithms.models.ts | 9 + .../encryption-algorithms.registry.ts | 40 + .../encryption-algorithms.types.ts | 3 + .../crypto/node/crypto.node.usecases.test.ts | 14 - .../src/crypto/node/crypto.node.usecases.ts | 45 +- .../crypto.node.aes-256-gcm.ts | 42 + .../encryption-algorithms.registry.ts | 13 + .../cbor-array.serialization.test.ts | 9 + .../cbor-array/cbor-array.serialization.ts | 20 + .../serialization/serialization.models.ts | 11 + .../serialization/serialization.registry.ts | 36 + .../serialization/serialization.test-utils.ts | 87 ++ .../serialization/serialization.types.ts | 5 + .../src/crypto/web/crypto.web.models.test.ts | 13 + .../lib/src/crypto/web/crypto.web.models.ts | 9 +- .../crypto/web/crypto.web.usecases.test.ts | 14 - .../lib/src/crypto/web/crypto.web.usecases.ts | 37 +- .../crypto.web.aes-256-gcm.ts | 40 + .../encryption-algorithms.registry.ts | 13 + packages/lib/src/files/files.models.ts | 36 + packages/lib/src/index.node.ts | 27 +- packages/lib/src/index.web.ts | 26 +- packages/lib/src/notes/notes.services.ts | 3 + packages/lib/src/notes/notes.types.ts | 25 + packages/lib/src/notes/notes.usecases.ts | 30 +- pnpm-lock.yaml | 877 +++++++++++++----- 51 files changed, 1640 insertions(+), 613 deletions(-) create mode 100644 packages/app-client/src/modules/files/files.models.test.ts create mode 100644 packages/app-client/src/modules/files/files.models.ts create mode 100644 packages/app-client/src/modules/notes/components/file-uploader.tsx delete mode 100644 packages/app-client/src/modules/shared/utils/safely.test.ts delete mode 100644 packages/app-client/src/modules/shared/utils/safely.ts delete mode 100644 packages/app-server/src/modules/shared/injection/injection.test.ts delete mode 100644 packages/app-server/src/modules/shared/injection/injection.ts delete mode 100644 packages/app-server/src/modules/shared/types.ts create mode 100644 packages/lib/src/crypto/crypto.constants.ts create mode 100644 packages/lib/src/crypto/encryption-algorithms/encryption-algorithms.models.ts create mode 100644 packages/lib/src/crypto/encryption-algorithms/encryption-algorithms.registry.ts create mode 100644 packages/lib/src/crypto/encryption-algorithms/encryption-algorithms.types.ts delete mode 100644 packages/lib/src/crypto/node/crypto.node.usecases.test.ts create mode 100644 packages/lib/src/crypto/node/encryption-algorithms/crypto.node.aes-256-gcm.ts create mode 100644 packages/lib/src/crypto/node/encryption-algorithms/encryption-algorithms.registry.ts create mode 100644 packages/lib/src/crypto/serialization/cbor-array/cbor-array.serialization.test.ts create mode 100644 packages/lib/src/crypto/serialization/cbor-array/cbor-array.serialization.ts create mode 100644 packages/lib/src/crypto/serialization/serialization.models.ts create mode 100644 packages/lib/src/crypto/serialization/serialization.registry.ts create mode 100644 packages/lib/src/crypto/serialization/serialization.test-utils.ts create mode 100644 packages/lib/src/crypto/serialization/serialization.types.ts delete mode 100644 packages/lib/src/crypto/web/crypto.web.usecases.test.ts create mode 100644 packages/lib/src/crypto/web/encryption-algorithms/crypto.web.aes-256-gcm.ts create mode 100644 packages/lib/src/crypto/web/encryption-algorithms/encryption-algorithms.registry.ts create mode 100644 packages/lib/src/files/files.models.ts create mode 100644 packages/lib/src/notes/notes.types.ts diff --git a/.gitignore b/.gitignore index afdd4882..ed3cd98b 100644 --- a/.gitignore +++ b/.gitignore @@ -28,4 +28,5 @@ logs .wrangler coverage -cache \ No newline at end of file +cache +.zed diff --git a/packages/app-client/package.json b/packages/app-client/package.json index a8a72bb1..74475a70 100644 --- a/packages/app-client/package.json +++ b/packages/app-client/package.json @@ -26,12 +26,14 @@ "typecheck": "tsc --noEmit" }, "dependencies": { + "@corentinth/chisels": "^1.0.2", "@enclosed/lib": "workspace:*", "@kobalte/core": "^0.13.4", "@solidjs/router": "^0.14.3", "@unocss/reset": "^0.62.2", "class-variance-authority": "^0.7.0", "clsx": "^2.1.1", + "jszip": "^3.10.1", "lodash-es": "^4.17.21", "solid-js": "^1.8.11", "tailwind-merge": "^2.5.2", diff --git a/packages/app-client/src/modules/files/files.models.test.ts b/packages/app-client/src/modules/files/files.models.test.ts new file mode 100644 index 00000000..d140f62f --- /dev/null +++ b/packages/app-client/src/modules/files/files.models.test.ts @@ -0,0 +1,82 @@ +import { values } from 'lodash-es'; +import { describe, expect, test } from 'vitest'; +import { icons as tablerIconSet } from '@iconify-json/tabler'; +import { getFileIcon, iconByFileType } from './files.models'; + +describe('files models', () => { + describe('iconByFileType', () => { + const icons = values(iconByFileType); + + test('they must at least have the default icon', () => { + expect(iconByFileType['*']).toBeDefined(); + }); + + test('all the icons should be from tabler icon set', () => { + for (const icon of icons) { + expect(icon).to.match(/^i-tabler-/, `Icon ${icon} is not from tabler icon set`); + } + }); + + test('icons should not contain any spaces', () => { + for (const icon of icons) { + expect(icon).not.to.match(/\s/, `Icon ${icon} contains spaces`); + } + }); + + test('the icons used for showing file types should exists with current iconify configuration', () => { + for (const icon of icons) { + const iconName = icon.replace('i-tabler-', ''); + const iconData = tablerIconSet.icons[iconName] ?? tablerIconSet.aliases?.[iconName]; + + expect(iconData).to.not.eql(undefined, `Icon ${icon} does not exist in tabler icon set`); + } + }); + }); + + describe('getFileIcon', () => { + test('a file icon is selected based on the file type', () => { + const file = new File([''], 'test.txt', { type: 'text/plain' }); + const iconsMap = { + '*': 'i-tabler-file', + 'text/plain': 'i-tabler-file-text', + }; + const icon = getFileIcon({ file, iconsMap }); + + expect(icon).to.eql('i-tabler-file-text'); + }); + + test('if a file type is not associated with an icon, the default icon is used', () => { + const file = new File([''], 'test.txt', { type: 'text/html' }); + const iconsMap = { + '*': 'i-tabler-file', + 'text/plain': 'i-tabler-file-text', + }; + const icon = getFileIcon({ file, iconsMap }); + + expect(icon).to.eql('i-tabler-file'); + }); + + test('a file icon can be selected based on the file type group', () => { + const file = new File([''], 'test.html', { type: 'text/html' }); + const iconsMap = { + '*': 'i-tabler-file', + 'text': 'i-tabler-file-text', + }; + const icon = getFileIcon({ file, iconsMap }); + + expect(icon).to.eql('i-tabler-file-text'); + }); + + test('when an icon is defined for both the whole type and the group type, the file type icon is used', () => { + const file = new File([''], 'test.html', { type: 'text/html' }); + const iconsMap = { + '*': 'i-tabler-file', + 'text': 'i-tabler-file-text', + 'text/html': 'i-tabler-file-type-html', + }; + const icon = getFileIcon({ file, iconsMap }); + + expect(icon).to.eql('i-tabler-file-type-html'); + }); + }); +}); diff --git a/packages/app-client/src/modules/files/files.models.ts b/packages/app-client/src/modules/files/files.models.ts new file mode 100644 index 00000000..514aa09b --- /dev/null +++ b/packages/app-client/src/modules/files/files.models.ts @@ -0,0 +1,42 @@ +export { getFileIcon }; + +// Available icons : +// i-tabler-file-3d i-tabler-file-ai i-tabler-file-alert i-tabler-file-analytics i-tabler-file-arrow-left i-tabler-file-arrow-right i-tabler-file-barcode i-tabler-file-bitcoin i-tabler-file-broken i-tabler-file-certificate i-tabler-file-chart i-tabler-file-check i-tabler-file-code i-tabler-file-code-2 i-tabler-file-cv i-tabler-file-database i-tabler-file-delta i-tabler-file-description i-tabler-file-diff i-tabler-file-digit i-tabler-file-dislike i-tabler-file-dollar i-tabler-file-dots i-tabler-file-download i-tabler-file-euro i-tabler-file-excel i-tabler-file-export i-tabler-file-filled i-tabler-file-function i-tabler-file-horizontal i-tabler-file-import i-tabler-file-infinity i-tabler-file-info i-tabler-file-invoice i-tabler-file-isr i-tabler-file-lambda i-tabler-file-like i-tabler-file-minus i-tabler-file-music i-tabler-file-neutral i-tabler-file-off i-tabler-file-orientation i-tabler-file-pencil i-tabler-file-percent i-tabler-file-phone i-tabler-file-plus i-tabler-file-power i-tabler-file-report i-tabler-file-rss i-tabler-file-sad i-tabler-file-scissors i-tabler-file-search i-tabler-file-settings i-tabler-file-shredder i-tabler-file-signal i-tabler-file-smile i-tabler-file-spreadsheet i-tabler-file-stack i-tabler-file-star i-tabler-file-symlink i-tabler-file-text i-tabler-file-text-ai i-tabler-file-time i-tabler-file-type-bmp i-tabler-file-type-css i-tabler-file-type-csv i-tabler-file-type-doc i-tabler-file-type-docx i-tabler-file-type-html i-tabler-file-type-jpg i-tabler-file-type-js i-tabler-file-type-jsx i-tabler-file-type-pdf i-tabler-file-type-php i-tabler-file-type-png i-tabler-file-type-ppt i-tabler-file-type-rs i-tabler-file-type-sql i-tabler-file-type-svg i-tabler-file-type-ts i-tabler-file-type-tsx i-tabler-file-type-txt i-tabler-file-type-vue i-tabler-file-type-xls i-tabler-file-type-xml i-tabler-file-type-zip i-tabler-file-typography i-tabler-file-unknown i-tabler-file-upload i-tabler-file-vector i-tabler-file-word i-tabler-file-x i-tabler-file-x-filled i-tabler-file-zip + +export const iconByFileType = { + '*': 'i-tabler-file', + 'image': 'i-tabler-photo', + 'video': 'i-tabler-video', + 'audio': 'i-tabler-file-music', + 'application': 'i-tabler-file-code', + 'application/pdf': 'i-tabler-file-type-pdf', + 'application/zip': 'i-tabler-file-zip', + 'application/vnd.ms-excel': 'i-tabler-file-excel', + 'application/vnd.openxmlformats-officedocument.spreadsheetml.sheet': 'i-tabler-file-excel', + 'application/msword': 'i-tabler-file-word', + 'application/vnd.openxmlformats-officedocument.wordprocessingml.document': 'i-tabler-file-word', + 'application/json': 'i-tabler-file-code', + 'application/xml': 'i-tabler-file-code', + 'application/javascript': 'i-tabler-file-type-js', + 'application/typescript': 'i-tabler-file-type-ts', + 'application/vnd.ms-powerpoint': 'i-tabler-file-type-ppt', + 'application/vnd.openxmlformats-officedocument.presentationml.presentation': 'i-tabler-file-type-ppt', + 'text/plain': 'i-tabler-file-text', + 'text/html': 'i-tabler-file-type-html', + 'text/css': 'i-tabler-file-type-css', + 'text/csv': 'i-tabler-file-type-csv', + 'text/xml': 'i-tabler-file-type-xml', + 'text/javascript': 'i-tabler-file-type-js', + 'text/typescript': 'i-tabler-file-type-ts', +}; + +type FileTypes = keyof typeof iconByFileType; + +function getFileIcon({ file, iconsMap = iconByFileType }: { file: File; iconsMap?: Record & { '*': string } }): string { + const fileType = file.type; + const fileTypeGroup = fileType?.split('/')[0]; + + const icon = iconsMap[fileType as FileTypes] ?? iconsMap[fileTypeGroup as FileTypes] ?? iconsMap['*']; + + return icon; +} diff --git a/packages/app-client/src/modules/notes/components/file-uploader.tsx b/packages/app-client/src/modules/notes/components/file-uploader.tsx new file mode 100644 index 00000000..cf990075 --- /dev/null +++ b/packages/app-client/src/modules/notes/components/file-uploader.tsx @@ -0,0 +1,105 @@ +import { type Component, type ComponentProps, type JSX, type ParentComponent, createSignal, onCleanup, splitProps } from 'solid-js'; +import { Button } from '@/modules/ui/components/button'; +import { cn } from '@/modules/shared/style/cn'; +import { Card, CardContent } from '@/modules/ui/components/card'; + +const DropArea: Component<{ onFilesDrop?: (args: { files: File[] }) => void }> = (props) => { + const [isDragging, setIsDragging] = createSignal(false); + + const handleDragOver = (e: DragEvent) => { + e.preventDefault(); + setIsDragging(true); + }; + + const handleDragLeave = (e: DragEvent) => { + if (e.relatedTarget === null) { + setIsDragging(false); + } + }; + + const handleDrop = (e: DragEvent) => { + e.preventDefault(); + setIsDragging(false); + const files = Array.from(e.dataTransfer?.files ?? []); + + if (files.length === 0) { + return; + } + + props.onFilesDrop?.({ files }); + }; + + // Adding global event listeners for drag and drop + document.addEventListener('dragover', handleDragOver); + document.addEventListener('dragleave', handleDragLeave); + document.addEventListener('drop', handleDrop); + + // Cleanup listeners when component unmounts + onCleanup(() => { + document.removeEventListener('dragover', handleDragOver); + document.removeEventListener('dragleave', handleDragLeave); + document.removeEventListener('drop', handleDrop); + }); + + return ( +
+
+
+
Drop files here
+
+ Drag and drop files here to attach them to the note +
+
+
+ ); +}; + +export const FileUploaderButton: ParentComponent<{ + onFileUpload?: (args: { file: File }) => void; + onFilesUpload?: (args: { files: File[] }) => void; + multiple?: boolean; +} & ComponentProps> = (props) => { + const [fileInputRef, setFileInputRef] = createSignal(null); + const [local, rest] = splitProps(props, ['onFileUpload', 'multiple', 'onFilesUpload']); + + const uploadFiles = ({ files }: { files: File[] }) => { + local.onFilesUpload?.({ files }); + + for (const file of files) { + local.onFileUpload?.({ file }); + } + }; + + const onFileChange = (e: Event) => { + const target = e.target as HTMLInputElement; + if (!target.files) { + return; + } + + const files = Array.from(target.files); + + uploadFiles({ files }); + }; + + const onButtonClick = () => { + fileInputRef()?.click(); + }; + + return ( + <> + + + + + ); +}; diff --git a/packages/app-client/src/modules/notes/notes.services.ts b/packages/app-client/src/modules/notes/notes.services.ts index 517643cf..52500088 100644 --- a/packages/app-client/src/modules/notes/notes.services.ts +++ b/packages/app-client/src/modules/notes/notes.services.ts @@ -2,7 +2,17 @@ import { apiClient } from '../shared/http/http-client'; export { storeNote, fetchNoteById }; -async function storeNote({ content, isPasswordProtected, ttlInSeconds, deleteAfterReading }: { content: string; isPasswordProtected: boolean; ttlInSeconds: number; deleteAfterReading: boolean }) { +async function storeNote({ + content, + isPasswordProtected, + ttlInSeconds, + deleteAfterReading, +}: { + content: string; + isPasswordProtected: boolean; + ttlInSeconds: number; + deleteAfterReading: boolean; +}) { const { noteId } = await apiClient<{ noteId: string }>({ path: '/api/notes', method: 'POST', @@ -18,7 +28,11 @@ async function storeNote({ content, isPasswordProtected, ttlInSeconds, deleteAft } async function fetchNoteById({ noteId }: { noteId: string }) { - const { note } = await apiClient<{ note: { content: string; isPasswordProtected: boolean } }>({ + const { note } = await apiClient<{ note: { + content: string; + isPasswordProtected: boolean; + assets: string[]; + }; }>({ path: `/api/notes/${noteId}`, method: 'GET', }); diff --git a/packages/app-client/src/modules/notes/notes.usecases.ts b/packages/app-client/src/modules/notes/notes.usecases.ts index b95f544e..d53aba82 100644 --- a/packages/app-client/src/modules/notes/notes.usecases.ts +++ b/packages/app-client/src/modules/notes/notes.usecases.ts @@ -1,4 +1,4 @@ -import { createNote } from '@enclosed/lib'; +import { createNote, filesToNoteAssets } from '@enclosed/lib'; import { storeNote } from './notes.services'; export { encryptAndCreateNote }; @@ -8,10 +8,14 @@ async function encryptAndCreateNote(args: { password?: string; ttlInSeconds: number; deleteAfterReading: boolean; + fileAssets: File[]; }) { return createNote({ ...args, storeNote, clientBaseUrl: window.location.origin, + assets: [ + ...await filesToNoteAssets({ files: args.fileAssets }), + ], }); } diff --git a/packages/app-client/src/modules/notes/pages/create-note.page.tsx b/packages/app-client/src/modules/notes/pages/create-note.page.tsx index 43767aba..9a9b4ac0 100644 --- a/packages/app-client/src/modules/notes/pages/create-note.page.tsx +++ b/packages/app-client/src/modules/notes/pages/create-note.page.tsx @@ -2,6 +2,7 @@ import { type Component, Match, Show, Switch, createSignal, onCleanup, onMount } import { encryptAndCreateNote } from '../notes.usecases'; import { useNoteContext } from '../notes.context'; import { NotePasswordField } from '../components/note-password-field'; +import { FileUploaderButton } from '../components/file-uploader'; import { TextArea } from '@/modules/ui/components/textarea'; import { TextField, TextFieldLabel, TextFieldRoot } from '@/modules/ui/components/textfield'; import { Button } from '@/modules/ui/components/button'; @@ -10,6 +11,8 @@ import { SwitchControl, SwitchLabel, SwitchThumb, Switch as SwitchUiComponent } import { Alert, AlertDescription } from '@/modules/ui/components/alert'; import { CopyButton } from '@/modules/shared/utils/copy'; import { isHttpErrorWithCode, isRateLimitError } from '@/modules/shared/http/http-errors'; +import { cn } from '@/modules/shared/style/cn'; +import { getFileIcon } from '@/modules/files/files.models'; export const CreateNotePage: Component = () => { const [getContent, setContent] = createSignal(''); @@ -19,6 +22,7 @@ export const CreateNotePage: Component = () => { const [getIsNoteCreated, setIsNoteCreated] = createSignal(false); const [getTtlInSeconds, setTtlInSeconds] = createSignal(3600); const [getDeleteAfterReading, setDeleteAfterReading] = createSignal(false); + const [getUploadedFiles, setUploadedFiles] = createSignal([]); const { onResetNoteForm, removeResetNoteFormHandler } = useNoteContext(); @@ -30,6 +34,7 @@ export const CreateNotePage: Component = () => { setIsNoteCreated(false); setTtlInSeconds(3600); setDeleteAfterReading(false); + setUploadedFiles([]); } onMount(() => { @@ -41,8 +46,8 @@ export const CreateNotePage: Component = () => { }); const createNote = async () => { - if (!getContent()) { - setErrorMessage('Please enter a note content.'); + if (!getContent() && getUploadedFiles().length === 0) { + setErrorMessage('Please enter a note content or attach a file.'); return; } @@ -52,6 +57,7 @@ export const CreateNotePage: Component = () => { password: getPassword(), ttlInSeconds: getTtlInSeconds(), deleteAfterReading: getDeleteAfterReading(), + fileAssets: getUploadedFiles(), }); setNoteUrl(noteUrl); @@ -137,9 +143,32 @@ export const CreateNotePage: Component = () => { - +
+ setUploadedFiles(prevFiles => [...prevFiles, ...files])}> +
+ Attach files +
+ + +
+ +
+ {getUploadedFiles().map(file => ( +
+
+
+ {file.name} +
+ {/*
{(file.size)}
*/} + + +
+ ))} +
{getMessage => ( diff --git a/packages/app-client/src/modules/notes/pages/view-note.page.tsx b/packages/app-client/src/modules/notes/pages/view-note.page.tsx index d21ddfd2..a52c87fb 100644 --- a/packages/app-client/src/modules/notes/pages/view-note.page.tsx +++ b/packages/app-client/src/modules/notes/pages/view-note.page.tsx @@ -1,14 +1,17 @@ import { useLocation, useParams } from '@solidjs/router'; import { type Component, Match, Show, Switch, createSignal, onMount } from 'solid-js'; -import { decryptNote } from '@enclosed/lib'; +import { decryptNote, noteAssetsToFiles } from '@enclosed/lib'; +import JSZip from 'jszip'; +import { formatBytes, safely } from '@corentinth/chisels'; import { fetchNoteById } from '../notes.services'; import { TextField, TextFieldLabel, TextFieldRoot } from '@/modules/ui/components/textfield'; import { Card, CardContent, CardDescription, CardHeader } from '@/modules/ui/components/card'; import { Button } from '@/modules/ui/components/button'; import { isHttpErrorWithCode, isRateLimitError } from '@/modules/shared/http/http-errors'; -import { safely } from '@/modules/shared/utils/safely'; import { Alert, AlertDescription } from '@/modules/ui/components/alert'; import { CopyButton } from '@/modules/shared/utils/copy'; +import { getFileIcon } from '@/modules/files/files.models'; +import { cn } from '@/modules/shared/style/cn'; const RequestPasswordForm: Component<{ onPasswordEntered: (args: { password: string }) => void; getIsPasswordInvalid: () => boolean; setIsPasswordInvalid: (value: boolean) => void }> = (props) => { const [getPassword, setPassword] = createSignal(''); @@ -68,6 +71,8 @@ export const ViewNotePage: Component = () => { const [getNote, setNote] = createSignal<{ content: string; isPasswordProtected: boolean } | null>(null); const [getDecryptedNote, setDecryptedNote] = createSignal(null); const [getIsPasswordInvalid, setIsPasswordInvalid] = createSignal(false); + const [fileAssets, setFileAssets] = createSignal([]); + const [isDownloadingAllLoading, setIsDownloadingAllLoading] = createSignal(false); const getEncryptionKey = () => location.hash.slice(1); @@ -113,8 +118,8 @@ export const ViewNotePage: Component = () => { return; } - const [decryptedNote, decryptionError] = await safely(decryptNote({ - encryptedContent: note.content, + const [decryptedNoteResult, decryptionError] = await safely(decryptNote({ + encryptedPayload: note.content, encryptionKey: getEncryptionKey(), })); @@ -126,14 +131,18 @@ export const ViewNotePage: Component = () => { return; } - setDecryptedNote(decryptedNote.decryptedContent); + const { note: decryptedNote } = decryptedNoteResult; + + const files = await noteAssetsToFiles({ noteAssets: decryptedNote.assets }); + setFileAssets(files); + setDecryptedNote(decryptedNote.content); }); const onPasswordEntered = async ({ password }: { password: string }) => { const { content } = getNote()!; const [decryptionResult, decryptionError] = await safely(decryptNote({ - encryptedContent: content, + encryptedPayload: content, encryptionKey: getEncryptionKey(), password, })); @@ -143,12 +152,38 @@ export const ViewNotePage: Component = () => { return; } - const { decryptedContent } = decryptionResult; + const { note } = decryptionResult; - setDecryptedNote(decryptedContent); + setDecryptedNote(note.content); setIsPasswordEntered(true); }; + const downloadFile = async ({ file }: { file: File }) => { + const url = URL.createObjectURL(file); + const a = document.createElement('a'); + a.href = url; + a.download = file.name; + a.click(); + URL.revokeObjectURL(url); + }; + + const downloadAllFiles = async () => { + setIsDownloadingAllLoading(true); + const zipFile = new JSZip(); + fileAssets().forEach((file) => { + zipFile.file(file.name, file); + }); + + const blob = await zipFile.generateAsync({ type: 'blob' }); + const url = URL.createObjectURL(blob); + const a = document.createElement('a'); + a.href = url; + a.download = 'note-files.zip'; + a.click(); + URL.revokeObjectURL(url); + setIsDownloadingAllLoading(false); + }; + return (
@@ -172,24 +207,83 @@ export const ViewNotePage: Component = () => { - - {getNoteContent => ( -
- - -
{getNoteContent()}
-
-
+ 0}> + +
+ {getDecryptedNote() && ( +
+
+
+ Note content +
+ +
-
+ + +
{getDecryptedNote()}
+
+
-
-
- )} + )} + + {fileAssets().length > 0 && ( +
+
+
+ {`${fileAssets().length} file${fileAssets().length > 1 ? 's' : ''} attached to this note`} +
+ + {fileAssets().length > 1 && ( + + )} +
+ +
+ { + fileAssets().map(file => ( + + +
+
+ +
+ {formatBytes({ bytes: file.size })} +
+
+
+ +
+ + + )) + } +
+ +
+ + )} + +
-
); }; diff --git a/packages/app-client/src/modules/shared/utils/safely.test.ts b/packages/app-client/src/modules/shared/utils/safely.test.ts deleted file mode 100644 index 48a361ee..00000000 --- a/packages/app-client/src/modules/shared/utils/safely.test.ts +++ /dev/null @@ -1,132 +0,0 @@ -import { describe, expect, test } from 'vitest'; -import { safely, safelySync } from './safely'; - -describe('safely', () => { - describe('safelySync', () => { - test('when the provided function does not throw an error, it returns a tuple with the result and a null error', () => { - expect(safelySync(() => 1)).to.eql([1, null]); - }); - - test('when the provided function throws an error, it returns a tuple with a null result and the error', () => { - expect( - safelySync(() => { - throw new Error('An error occurred'); - }), - ).to.eql([null, new Error('An error occurred')]); - }); - - test('if the thrown error is not an instance of Error, it is serialized and cast to an Error instance', () => { - expect( - safelySync(() => { - // eslint-disable-next-line no-throw-literal - throw 'An error occurred'; - }), - ).to.eql([null, new Error('An error occurred')]); - - expect( - safelySync(() => { - // eslint-disable-next-line no-throw-literal - throw 1; - }), - ).to.eql([null, new Error('1')]); - }); - }); - - describe('safely', () => { - describe('when provided a synchronous function', () => { - test('when the provided function does not throw an error, it returns a tuple with the result and a null error', async () => { - expect(await safely(() => 1)).to.eql([1, null]); - }); - - test('when the provided function throws an error, it returns a tuple with a null result and the error', async () => { - expect( - await safely(() => { - throw new Error('An error occurred'); - }), - ).to.eql([null, new Error('An error occurred')]); - }); - - test('if the thrown error is not an instance of Error, it is serialized and cast to an Error instance', async () => { - expect( - await safely(() => { - // eslint-disable-next-line no-throw-literal - throw 'An error occurred'; - }), - ).to.eql([null, new Error('An error occurred')]); - - expect( - await safely(() => { - // eslint-disable-next-line no-throw-literal - throw 1; - }), - ).to.eql([null, new Error('1')]); - }); - }); - - describe('when provided an asynchronous function', () => { - test('when the provided function does not throw an error, it returns a tuple with the result and a null error', async () => { - expect(await safely(async () => 1)).to.eql([1, null]); - }); - - test('when the provided function throws an error, it returns a tuple with a null result and the error', async () => { - expect( - await safely(async () => { - throw new Error('An error occurred'); - }), - ).to.eql([null, new Error('An error occurred')]); - }); - - test('if the thrown error is not an instance of Error, it is serialized and cast to an Error instance', async () => { - expect( - await safely(async () => { - // eslint-disable-next-line no-throw-literal - throw 'An error occurred'; - }), - ).to.eql([null, new Error('An error occurred')]); - - expect( - await safely(async () => { - // eslint-disable-next-line no-throw-literal - throw 1; - }), - ).to.eql([null, new Error('1')]); - }); - }); - - describe('when provided a promise', () => { - test('when the promise resolves, it returns a tuple with the result and a null error', async () => { - expect(await safely(Promise.resolve(1))).to.eql([1, null]); - }); - - test('when the promise rejects, it returns a tuple with a null result and the error', async () => { - expect( - await safely( - new Promise((_, reject) => { - reject(new Error('An error occurred')); - }), - ), - ).to.eql([null, new Error('An error occurred')]); - }); - - test('if the rejected error is not an instance of Error, it is serialized and cast to an Error instance', async () => { - expect( - await safely( - new Promise((_, reject) => { - // eslint-disable-next-line prefer-promise-reject-errors - reject('An error occurred'); - }), - ), - ).to.eql([null, new Error('An error occurred')]); - - expect( - await safely( - new Promise((_, reject) => { - // eslint-disable-next-line prefer-promise-reject-errors - reject(1); - }), - ), - ).to.eql([null, new Error('1')]); - }); - }); - }); -}); diff --git a/packages/app-client/src/modules/shared/utils/safely.ts b/packages/app-client/src/modules/shared/utils/safely.ts deleted file mode 100644 index edd73997..00000000 --- a/packages/app-client/src/modules/shared/utils/safely.ts +++ /dev/null @@ -1,24 +0,0 @@ -import { isError, isFunction } from 'lodash-es'; - -export { safelySync, safely }; - -function castError(error: unknown): Error { - return isError(error) ? error : new Error(String(error)); -} - -function safelySync(fn: () => T): [T, null] | [null, Error] { - try { - return [fn(), null]; - } catch (error) { - return [null, castError(error)]; - } -} - -async function safely(fn: (() => Promise | T) | Promise): Promise<[T, null] | [null, Error]> { - try { - const result = isFunction(fn) ? await fn() : await fn; - return [result, null]; - } catch (error) { - return [null, castError(error)]; - } -} diff --git a/packages/app-client/uno.config.ts b/packages/app-client/uno.config.ts index 1bc13e04..3069d867 100644 --- a/packages/app-client/uno.config.ts +++ b/packages/app-client/uno.config.ts @@ -7,6 +7,8 @@ import { transformerVariantGroup, } from 'unocss'; import presetAnimations from 'unocss-preset-animations'; +import { chain } from 'lodash-es'; +import { iconByFileType } from '@/modules/files/files.models'; export default defineConfig({ presets: [ @@ -98,4 +100,7 @@ export default defineConfig({ }, }, }, + safelist: [ + ...chain(iconByFileType).values().uniq().value(), + ], }); diff --git a/packages/app-server/package.json b/packages/app-server/package.json index 1ca6b7b2..78175f6d 100644 --- a/packages/app-server/package.json +++ b/packages/app-server/package.json @@ -28,6 +28,7 @@ "script:generate-config-table": "tsx -r dotenv/config src/scripts/generate-config-table.script.ts" }, "dependencies": { + "@corentinth/chisels": "^1.0.4", "@hono/node-server": "^1.12.1", "date-fns": "^3.6.0", "figue": "^2.0.0", diff --git a/packages/app-server/src/modules/app/config/config.test-utils.ts b/packages/app-server/src/modules/app/config/config.test-utils.ts index ee4c745f..60e64896 100644 --- a/packages/app-server/src/modules/app/config/config.test-utils.ts +++ b/packages/app-server/src/modules/app/config/config.test-utils.ts @@ -1,5 +1,5 @@ import { merge } from 'lodash-es'; -import type { DeepPartial } from '../../shared/types'; +import type { DeepPartial } from '@corentinth/chisels'; import { getConfig } from './config'; import type { Config } from './config.types'; diff --git a/packages/app-server/src/modules/app/config/config.ts b/packages/app-server/src/modules/app/config/config.ts index 6f3a8d1c..179726e1 100644 --- a/packages/app-server/src/modules/app/config/config.ts +++ b/packages/app-server/src/modules/app/config/config.ts @@ -35,7 +35,7 @@ export const configDefinition = { maxEncryptedContentLength: { doc: 'The maximum length of the encrypted content of a note allowed by the api', schema: z.coerce.number().int().positive().min(1), - default: 1024 * 1024 * 5, // 5MB + default: 1024 * 1024 * 50, // 50MB env: 'NOTES_MAX_ENCRYPTED_CONTENT_LENGTH', }, }, diff --git a/packages/app-server/src/modules/notes/notes.repository.ts b/packages/app-server/src/modules/notes/notes.repository.ts index 51ab04a4..dc8f7616 100644 --- a/packages/app-server/src/modules/notes/notes.repository.ts +++ b/packages/app-server/src/modules/notes/notes.repository.ts @@ -1,8 +1,9 @@ +import { injectArguments } from '@corentinth/chisels'; import type { Storage } from '../storage/storage.types'; -import { injectArguments } from '../shared/injection/injection'; import { generateId } from '../shared/utils/random'; import { createNoteNotFoundError } from './notes.errors'; import { getNoteExpirationDate } from './notes.models'; +import type { StoredNote } from './notes.types'; export { createNoteRepository }; @@ -33,6 +34,7 @@ async function saveNote( ttlInSeconds, deleteAfterReading, storage, + assets = [], generateNoteId = generateId, now = new Date(), }: @@ -42,6 +44,7 @@ async function saveNote( ttlInSeconds: number; deleteAfterReading: boolean; storage: Storage; + assets?: string[]; generateNoteId?: () => string; now?: Date; }, @@ -56,6 +59,7 @@ async function saveNote( isPasswordProtected, expirationDate: expirationDate.toISOString(), deleteAfterReading, + assets, }, { // Some storage drivers have a different API for setting TTLs @@ -69,7 +73,7 @@ async function saveNote( } async function getNoteById({ noteId, storage }: { noteId: string; storage: Storage }) { - const note = await storage.getItem<{ content: string; isPasswordProtected: boolean; expirationDate: string; deleteAfterReading: boolean }>(noteId); + const note = await storage.getItem(noteId); if (!note) { throw createNoteNotFoundError(); diff --git a/packages/app-server/src/modules/notes/notes.routes.ts b/packages/app-server/src/modules/notes/notes.routes.ts index d7b09613..51dba28a 100644 --- a/packages/app-server/src/modules/notes/notes.routes.ts +++ b/packages/app-server/src/modules/notes/notes.routes.ts @@ -41,6 +41,7 @@ function setupCreateNoteRoute({ app }: { app: ServerInstance }) { ttlInSeconds: z.number() .min(TEN_MINUTES_IN_SECONDS) .max(ONE_MONTH_IN_SECONDS), + assets: z.array(z.string()).optional(), }), ), @@ -56,12 +57,12 @@ function setupCreateNoteRoute({ app }: { app: ServerInstance }) { }, async (context) => { - const { content, isPasswordProtected, ttlInSeconds, deleteAfterReading } = context.req.valid('json'); + const { content, isPasswordProtected, ttlInSeconds, deleteAfterReading, assets } = context.req.valid('json'); const storage = context.get('storage'); const notesRepository = createNoteRepository({ storage }); - const { noteId } = await notesRepository.saveNote({ content, isPasswordProtected, ttlInSeconds, deleteAfterReading }); + const { noteId } = await notesRepository.saveNote({ content, isPasswordProtected, ttlInSeconds, deleteAfterReading, assets }); return context.json({ noteId }); }, diff --git a/packages/app-server/src/modules/shared/injection/injection.test.ts b/packages/app-server/src/modules/shared/injection/injection.test.ts deleted file mode 100644 index 227c2638..00000000 --- a/packages/app-server/src/modules/shared/injection/injection.test.ts +++ /dev/null @@ -1,25 +0,0 @@ -import { describe, expect, test } from 'vitest'; -import { injectArguments } from './injection'; - -describe('injection', () => { - describe('injectArguments', () => { - test('the injected dependencies are added to each function arguments', () => { - const functions = injectArguments( - { - foo: ({ a, b }: { a: number; b: number }) => a + b, - bar: ({ a, c }: { a: number; c: number }) => a + c, - baz: ({ f }: { f: number }) => f, - }, - { a: 1, b: 3 }, - ); - - expect(Object.keys(functions)).toEqual(['foo', 'bar', 'baz']); - - const { foo, bar, baz } = functions; - - expect(foo()).toBe(4); - expect(bar({ c: 2 })).toBe(3); - expect(baz({ f: 5 })).toBe(5); - }); - }); -}); diff --git a/packages/app-server/src/modules/shared/injection/injection.ts b/packages/app-server/src/modules/shared/injection/injection.ts deleted file mode 100644 index 09319a8a..00000000 --- a/packages/app-server/src/modules/shared/injection/injection.ts +++ /dev/null @@ -1,17 +0,0 @@ -import { mapValues } from 'lodash-es'; -import type { Dictionary, Expand, Subtract } from '../types'; - -export { injectArguments }; - -function injectArguments any>, InjectedArgs>(functions: Functions, injectedArgs: InjectedArgs) { - return mapValues(functions, (fn) => { - return (args: any) => fn({ ...args, ...injectedArgs }); - }) as { - [K in keyof Functions]: Expand[0], InjectedArgs>> extends infer Args - // eslint-disable-next-line ts/no-empty-object-type - ? {} extends Args - ? () => ReturnType - : (args: Args) => ReturnType - : never; - }; -} diff --git a/packages/app-server/src/modules/shared/types.ts b/packages/app-server/src/modules/shared/types.ts deleted file mode 100644 index f25af53c..00000000 --- a/packages/app-server/src/modules/shared/types.ts +++ /dev/null @@ -1,13 +0,0 @@ -export type PartialBy = Omit & Partial>; - -export type Expand = T extends infer O ? { [K in keyof O]: O[K] } : never; - -export type Dictionary = Record; - -export type DeepPartial = T extends object - ? { - [P in keyof T]?: DeepPartial; - } - : T; - -export type Subtract = Pick>; diff --git a/packages/lib/package.json b/packages/lib/package.json index 0c028d7b..469df8e1 100644 --- a/packages/lib/package.json +++ b/packages/lib/package.json @@ -77,15 +77,19 @@ "prepublishOnly": "pnpm run build" }, "dependencies": { + "cbor-x": "^1.6.0", "lodash-es": "^4.17.21", + "msgpackr": "^1.11.0", "ofetch": "^1.3.4" }, "devDependencies": { "@antfu/eslint-config": "^2.27.0", "@types/lodash-es": "^4.17.12", + "@types/node": "^22.5.4", "@vitest/coverage-v8": "^2.0.5", "dotenv": "^16.4.5", "eslint": "^9.9.0", + "tsx": "^4.17.0", "typescript": "^5.5.4", "unbuild": "^2.0.0", "vitest": "^2.0.5" diff --git a/packages/lib/src/crypto/crypto.constants.ts b/packages/lib/src/crypto/crypto.constants.ts new file mode 100644 index 00000000..24d9d64c --- /dev/null +++ b/packages/lib/src/crypto/crypto.constants.ts @@ -0,0 +1,12 @@ +export const ENCRYPTION_ALGORITHM_AES_256_GCM = 'aes-256-gcm'; +export const ENCRYPTION_ALGORITHMS = [ENCRYPTION_ALGORITHM_AES_256_GCM] as const; + +export const KEY_DERIVATION_ALGORITHM_PBKDF2_BASE_KEY_SALTED = 'pbkdf2-base-key-salted'; +export const KEY_DERIVATION_ALGORITHMS = [KEY_DERIVATION_ALGORITHM_PBKDF2_BASE_KEY_SALTED] as const; + +export const COMPRESSION_ALGORITHM_BROTLI = 'brotli'; +export const COMPRESSION_ALGORITHM_NONE = 'none'; +export const COMPRESSION_ALGORITHMS = [COMPRESSION_ALGORITHM_BROTLI, COMPRESSION_ALGORITHM_NONE] as const; + +export const SERIALIZATION_FORMAT_CBOR_ARRAY = 'cbor-array'; +export const SERIALIZATION_FORMATS = [SERIALIZATION_FORMAT_CBOR_ARRAY] as const; diff --git a/packages/lib/src/crypto/crypto.types.ts b/packages/lib/src/crypto/crypto.types.ts index dd6d76e4..219c004e 100644 --- a/packages/lib/src/crypto/crypto.types.ts +++ b/packages/lib/src/crypto/crypto.types.ts @@ -1,3 +1,9 @@ +import type { COMPRESSION_ALGORITHMS, ENCRYPTION_ALGORITHMS, KEY_DERIVATION_ALGORITHMS } from './crypto.constants'; + +export type EncryptionAlgorithm = typeof ENCRYPTION_ALGORITHMS[number]; +export type KeyDerivationAlgorithm = typeof KEY_DERIVATION_ALGORITHMS[number]; +export type CompressionAlgorithm = typeof COMPRESSION_ALGORITHMS[number]; + export type CryptoServices = { encryptNote: (args: { content: string; password?: string }) => Promise<{ encryptedContent: string; encryptionKey: string }>; }; diff --git a/packages/lib/src/crypto/crypto.usecases.test.ts b/packages/lib/src/crypto/crypto.usecases.test.ts index a306b9f6..b7742e3a 100644 --- a/packages/lib/src/crypto/crypto.usecases.test.ts +++ b/packages/lib/src/crypto/crypto.usecases.test.ts @@ -213,18 +213,21 @@ describe('cross-environment encryption and decryption', () => { const { encryptNote } = createEncryptUsecase(webCryptoLib); const { decryptNote } = createDecryptUsecase(nodeCryptoLib); - const { encryptedContent, encryptionKey } = await encryptNote({ + const { encryptedPayload, encryptionKey } = await encryptNote({ content, password, }); - const { decryptedContent } = await decryptNote({ - encryptedContent, + const { note } = await decryptNote({ + encryptedPayload, encryptionKey, password, }); - expect(decryptedContent).toBe(content); + expect(note).to.eql({ + content: 'Hello, world!', + assets: [], + }); }); test('a note encrypted in the node environment can be decrypted in the web environment', async () => { @@ -234,17 +237,20 @@ describe('cross-environment encryption and decryption', () => { const { encryptNote } = createEncryptUsecase(nodeCryptoLib); const { decryptNote } = createDecryptUsecase(webCryptoLib); - const { encryptedContent, encryptionKey } = await encryptNote({ + const { encryptedPayload, encryptionKey } = await encryptNote({ content, password, }); - const { decryptedContent } = await decryptNote({ - encryptedContent, + const { note } = await decryptNote({ + encryptedPayload, encryptionKey, password, }); - expect(decryptedContent).toBe(content); + expect(note).to.eql({ + content: 'Hello, world!', + assets: [], + }); }); }); diff --git a/packages/lib/src/crypto/crypto.usecases.ts b/packages/lib/src/crypto/crypto.usecases.ts index 3a94e877..c8e935b3 100644 --- a/packages/lib/src/crypto/crypto.usecases.ts +++ b/packages/lib/src/crypto/crypto.usecases.ts @@ -1,3 +1,7 @@ +import type { NoteAsset } from '../notes/notes.types'; +import type { EncryptionAlgorithm } from './crypto.types'; +import { getParsingMethod, getSerializationMethod } from './serialization/serialization.registry'; +import type { SerializationFormat } from './serialization/serialization.types'; import { base64UrlToBuffer, bufferToBase64Url } from './web/crypto.web.models'; export { createEncryptUsecase, createDecryptUsecase }; @@ -5,43 +9,77 @@ export { createEncryptUsecase, createDecryptUsecase }; function createEncryptUsecase({ generateBaseKey, deriveMasterKey, - encryptNoteContent, + getEncryptionMethod, }: { generateBaseKey: () => { baseKey: Uint8Array }; deriveMasterKey: ({ baseKey, password }: { baseKey: Uint8Array; password?: string }) => Promise<{ masterKey: Uint8Array }>; - encryptNoteContent: ({ content, masterKey }: { content: string; masterKey: Uint8Array }) => Promise<{ encryptedContent: string }>; + getEncryptionMethod: (args: { encryptionAlgorithm: string }) => { encryptBuffer: (args: { buffer: Uint8Array; encryptionKey: Uint8Array }) => Promise<{ encryptedString: string }> }; }) { return { - encryptNote: async ({ content, password }: { content: string; password?: string }) => { + encryptNote: async ({ + content, + password, + assets = [], + encryptionAlgorithm = 'aes-256-gcm', + serializationFormat = 'cbor-array', + }: { + content: string; + password?: string; + assets?: NoteAsset[]; + encryptionAlgorithm?: EncryptionAlgorithm; + serializationFormat?: SerializationFormat; + }) => { + const { serializeNote } = getSerializationMethod({ serializationFormat }); + const { encryptBuffer } = getEncryptionMethod({ encryptionAlgorithm }); + const { baseKey } = generateBaseKey(); const { masterKey } = await deriveMasterKey({ baseKey, password }); - const { encryptedContent } = await encryptNoteContent({ content, masterKey }); + const { noteBuffer } = await serializeNote({ note: { content, assets } }); + + const { encryptedString: encryptedPayload } = await encryptBuffer({ buffer: noteBuffer, encryptionKey: masterKey }); const encryptionKey = bufferToBase64Url({ buffer: baseKey }); - return { encryptedContent, encryptionKey }; + return { encryptedPayload, encryptionKey }; }, }; } function createDecryptUsecase({ deriveMasterKey, - decryptNoteContent, + getDecryptionMethod, }: { deriveMasterKey: ({ baseKey, password }: { baseKey: Uint8Array; password?: string }) => Promise<{ masterKey: Uint8Array }>; - decryptNoteContent: ({ encryptedContent, masterKey }: { encryptedContent: string; masterKey: Uint8Array }) => Promise<{ decryptedContent: string }>; + getDecryptionMethod: (args: { encryptionAlgorithm: string }) => { decryptString: (args: { encryptedString: string;encryptionKey: Uint8Array }) => Promise<{ decryptedBuffer: Uint8Array }> }; }) { return { - decryptNote: async ({ encryptedContent, password, encryptionKey }: { encryptedContent: string; password?: string; encryptionKey: string }) => { + decryptNote: async ({ + encryptedPayload, + password, + encryptionKey, + serializationFormat = 'cbor-array', + encryptionAlgorithm = 'aes-256-gcm', + }: { + encryptedPayload: string; + password?: string; + encryptionKey: string; + serializationFormat?: SerializationFormat; + encryptionAlgorithm?: EncryptionAlgorithm; + }) => { + const { parseNote } = getParsingMethod({ serializationFormat }); + const { decryptString } = getDecryptionMethod({ encryptionAlgorithm }); + const baseKey = base64UrlToBuffer({ base64Url: encryptionKey }); const { masterKey } = await deriveMasterKey({ baseKey, password }); - const { decryptedContent } = await decryptNoteContent({ encryptedContent, masterKey }); + const { decryptedBuffer } = await decryptString({ encryptedString: encryptedPayload, encryptionKey: masterKey }); + + const { note } = await parseNote({ noteBuffer: decryptedBuffer }); - return { decryptedContent }; + return { note }; }, }; } diff --git a/packages/lib/src/crypto/encryption-algorithms/encryption-algorithms.models.ts b/packages/lib/src/crypto/encryption-algorithms/encryption-algorithms.models.ts new file mode 100644 index 00000000..33a437d2 --- /dev/null +++ b/packages/lib/src/crypto/encryption-algorithms/encryption-algorithms.models.ts @@ -0,0 +1,9 @@ +export { defineEncryptionMethods }; + +function defineEncryptionMethods(args: { + name: Name; + encryptBuffer: (args: { buffer: Uint8Array; encryptionKey: Uint8Array }) => Promise<{ encryptedString: string }>; + decryptString: (args: { encryptedString: string; encryptionKey: Uint8Array }) => Promise<{ decryptedBuffer: Uint8Array }>; +}) { + return args; +} diff --git a/packages/lib/src/crypto/encryption-algorithms/encryption-algorithms.registry.ts b/packages/lib/src/crypto/encryption-algorithms/encryption-algorithms.registry.ts new file mode 100644 index 00000000..ea02516c --- /dev/null +++ b/packages/lib/src/crypto/encryption-algorithms/encryption-algorithms.registry.ts @@ -0,0 +1,40 @@ +import { keyBy } from 'lodash-es'; +import type { EncryptionMethodsDefinition } from './encryption-algorithms.types'; + +export { createEncryptionAlgorithmsRegistry }; + +function createEncryptionAlgorithmsRegistry({ encryptionMethodDefinitions }: { encryptionMethodDefinitions: EncryptionMethodsDefinition[] }) { + const encryptionMethodDefinitionsByName: Record = keyBy(encryptionMethodDefinitions, 'name'); + const encryptionAlgorithms = encryptionMethodDefinitions.map(({ name }) => name); + + return { + encryptionMethodDefinitions, + encryptionMethodDefinitionsByName, + encryptionAlgorithms, + + getEncryptionMethod: ({ encryptionAlgorithm }: { encryptionAlgorithm: string }) => { + const encryptionMethods = encryptionMethodDefinitionsByName[encryptionAlgorithm]; + + if (!encryptionMethods) { + throw new Error(`Encryption algorithm "${encryptionAlgorithm}" not found`); + } + + const { encryptBuffer } = encryptionMethods; + + return { encryptBuffer }; + }, + + getDecryptionMethod: ({ encryptionAlgorithm }: { encryptionAlgorithm: string }) => { + const encryptionMethods = encryptionMethodDefinitionsByName[encryptionAlgorithm]; + + if (!encryptionMethods) { + throw new Error(`Encryption algorithm "${encryptionAlgorithm}" not found`); + } + + const { decryptString } = encryptionMethods; + + return { decryptString }; + }, + + }; +}; diff --git a/packages/lib/src/crypto/encryption-algorithms/encryption-algorithms.types.ts b/packages/lib/src/crypto/encryption-algorithms/encryption-algorithms.types.ts new file mode 100644 index 00000000..adb6bfdc --- /dev/null +++ b/packages/lib/src/crypto/encryption-algorithms/encryption-algorithms.types.ts @@ -0,0 +1,3 @@ +import type { defineEncryptionMethods } from './encryption-algorithms.models'; + +export type EncryptionMethodsDefinition = ReturnType; diff --git a/packages/lib/src/crypto/node/crypto.node.usecases.test.ts b/packages/lib/src/crypto/node/crypto.node.usecases.test.ts deleted file mode 100644 index c08e397f..00000000 --- a/packages/lib/src/crypto/node/crypto.node.usecases.test.ts +++ /dev/null @@ -1,14 +0,0 @@ -import { describe } from 'vitest'; -import { runCommonCryptoUsecasesTests } from '../crypto.usecases.test'; -import { createDecryptUsecase, createEncryptUsecase } from '../crypto.usecases'; - -import * as nodeCryptoLib from './crypto.node.usecases'; - -describe('crypto usecases', () => { - describe('node', () => { - runCommonCryptoUsecasesTests({ - ...createEncryptUsecase({ ...nodeCryptoLib }), - ...createDecryptUsecase({ ...nodeCryptoLib }), - }); - }); -}); diff --git a/packages/lib/src/crypto/node/crypto.node.usecases.ts b/packages/lib/src/crypto/node/crypto.node.usecases.ts index a991c3e6..5ef02e1c 100644 --- a/packages/lib/src/crypto/node/crypto.node.usecases.ts +++ b/packages/lib/src/crypto/node/crypto.node.usecases.ts @@ -1,8 +1,9 @@ -import { createCipheriv, createDecipheriv, pbkdf2, randomBytes } from 'node:crypto'; +import { pbkdf2, randomBytes } from 'node:crypto'; import { TextEncoder, promisify } from 'node:util'; -import { base64UrlToBuffer, bufferToBase64Url } from './crypto.node.models'; -export { generateBaseKey, deriveMasterKey, encryptNoteContent, decryptNoteContent }; +export { getEncryptionMethod, getDecryptionMethod } from './encryption-algorithms/encryption-algorithms.registry'; + +export { generateBaseKey, deriveMasterKey, createRandomBuffer }; const deriveWithPbkdf2 = promisify(pbkdf2); @@ -26,41 +27,3 @@ async function deriveMasterKey({ baseKey, password = '' }: { baseKey: Uint8Array masterKey, }; } - -async function encryptNoteContent({ content, masterKey }: { content: string; masterKey: Uint8Array }) { - const iv = createRandomBuffer({ length: 12 }); - - const cipher = createCipheriv('aes-256-gcm', masterKey, iv); - - const encryptedBuffer = new Uint8Array([...cipher.update(content), ...cipher.final(), ...cipher.getAuthTag()]); - const encrypted = bufferToBase64Url({ buffer: encryptedBuffer }); - - return { - encryptedContent: `${bufferToBase64Url({ buffer: iv })}:${encrypted}`, - }; -} - -async function decryptNoteContent({ encryptedContent, masterKey }: { encryptedContent: string; masterKey: Uint8Array }) { - const [ivString, encryptedStringWithAuthTag] = encryptedContent.split(':').map(part => part.trim()); - - if (!ivString || !encryptedStringWithAuthTag) { - throw new Error('Invalid encrypted content'); - } - - const iv = base64UrlToBuffer({ base64Url: ivString }); - const encryptedContentAndTagBuffer = base64UrlToBuffer({ base64Url: encryptedStringWithAuthTag }); - - const encryptedBuffer = encryptedContentAndTagBuffer.slice(0, -16); - const authTag = encryptedContentAndTagBuffer.slice(-16); - - const decipher = createDecipheriv('aes-256-gcm', masterKey, iv); - decipher.setAuthTag(authTag); - - const decryptedBuffer = new Uint8Array([...decipher.update(encryptedBuffer), ...decipher.final()]); - - const decryptedContent = new TextDecoder().decode(decryptedBuffer); - - return { - decryptedContent, - }; -} diff --git a/packages/lib/src/crypto/node/encryption-algorithms/crypto.node.aes-256-gcm.ts b/packages/lib/src/crypto/node/encryption-algorithms/crypto.node.aes-256-gcm.ts new file mode 100644 index 00000000..6d64cf42 --- /dev/null +++ b/packages/lib/src/crypto/node/encryption-algorithms/crypto.node.aes-256-gcm.ts @@ -0,0 +1,42 @@ +import { createCipheriv, createDecipheriv } from 'node:crypto'; +import { defineEncryptionMethods } from '../../encryption-algorithms/encryption-algorithms.models'; +import { base64UrlToBuffer, bufferToBase64Url } from '../crypto.node.models'; +import { createRandomBuffer } from '../crypto.node.usecases'; + +export const aes256GcmEncryptionAlgorithmDefinition = defineEncryptionMethods({ + name: 'aes-256-gcm', + + encryptBuffer: async ({ buffer, encryptionKey }) => { + const iv = createRandomBuffer({ length: 12 }); + + const cipher = createCipheriv('aes-256-gcm', encryptionKey, iv); + + const encryptedBuffer = new Uint8Array([...cipher.update(buffer), ...cipher.final(), ...cipher.getAuthTag()]); + const encrypted = bufferToBase64Url({ buffer: encryptedBuffer }); + + return { + encryptedString: `${bufferToBase64Url({ buffer: iv })}:${encrypted}`, + }; + }, + + decryptString: async ({ encryptedString, encryptionKey }) => { + const [ivString, encryptedStringWithAuthTag] = encryptedString.split(':').map(part => part.trim()); + + if (!ivString || !encryptedStringWithAuthTag) { + throw new Error('Invalid encrypted content'); + } + + const iv = base64UrlToBuffer({ base64Url: ivString }); + const encryptedContentAndTagBuffer = base64UrlToBuffer({ base64Url: encryptedStringWithAuthTag }); + + const encryptedBuffer = encryptedContentAndTagBuffer.slice(0, -16); + const authTag = encryptedContentAndTagBuffer.slice(-16); + + const decipher = createDecipheriv('aes-256-gcm', encryptionKey, iv); + decipher.setAuthTag(authTag); + + const decryptedBuffer = new Uint8Array([...decipher.update(encryptedBuffer), ...decipher.final()]); + + return { decryptedBuffer }; + }, +}); diff --git a/packages/lib/src/crypto/node/encryption-algorithms/encryption-algorithms.registry.ts b/packages/lib/src/crypto/node/encryption-algorithms/encryption-algorithms.registry.ts new file mode 100644 index 00000000..14422475 --- /dev/null +++ b/packages/lib/src/crypto/node/encryption-algorithms/encryption-algorithms.registry.ts @@ -0,0 +1,13 @@ +import { createEncryptionAlgorithmsRegistry } from '../../encryption-algorithms/encryption-algorithms.registry'; +import { aes256GcmEncryptionAlgorithmDefinition } from './crypto.node.aes-256-gcm'; + +const encryptionMethodDefinitions = [ + aes256GcmEncryptionAlgorithmDefinition, +]; + +export const { + encryptionAlgorithms, + encryptionMethodDefinitionsByName, + getDecryptionMethod, + getEncryptionMethod, +} = createEncryptionAlgorithmsRegistry({ encryptionMethodDefinitions }); diff --git a/packages/lib/src/crypto/serialization/cbor-array/cbor-array.serialization.test.ts b/packages/lib/src/crypto/serialization/cbor-array/cbor-array.serialization.test.ts new file mode 100644 index 00000000..7b25bb9b --- /dev/null +++ b/packages/lib/src/crypto/serialization/cbor-array/cbor-array.serialization.test.ts @@ -0,0 +1,9 @@ +import { describe } from 'vitest'; +import { runCommonSerializationTests } from '../serialization.test-utils'; +import { cborArraySerializationDefinition } from './cbor-array.serialization'; + +describe('cbor-array serialization', () => { + describe('cborArraySerializationDefinition', () => { + runCommonSerializationTests(cborArraySerializationDefinition); + }); +}); diff --git a/packages/lib/src/crypto/serialization/cbor-array/cbor-array.serialization.ts b/packages/lib/src/crypto/serialization/cbor-array/cbor-array.serialization.ts new file mode 100644 index 00000000..a0aa8779 --- /dev/null +++ b/packages/lib/src/crypto/serialization/cbor-array/cbor-array.serialization.ts @@ -0,0 +1,20 @@ +import { decode, encode } from 'cbor-x'; +import type { Note } from '../../../notes/notes.types'; +import { defineSerializationMethods } from '../serialization.models'; + +export const cborArraySerializationDefinition = defineSerializationMethods({ + name: 'cbor-array', + + serializeNote: async ({ note }) => { + const noteBuffer = encode([note.content, note.assets.map(({ content, metadata }) => ([metadata, content]))]); + return { noteBuffer }; + }, + + parseNote: async ({ noteBuffer }) => { + const [content, assets] = decode(noteBuffer) as [string, [Record, Uint8Array][]]; + + const note = { content, assets: assets.map(([metadata, content]) => ({ metadata, content })) } as Note; + + return { note }; + }, +}); diff --git a/packages/lib/src/crypto/serialization/serialization.models.ts b/packages/lib/src/crypto/serialization/serialization.models.ts new file mode 100644 index 00000000..415c9379 --- /dev/null +++ b/packages/lib/src/crypto/serialization/serialization.models.ts @@ -0,0 +1,11 @@ +import type { Note } from '../../notes/notes.types'; + +export { defineSerializationMethods }; + +function defineSerializationMethods(args: { + name: Name; + serializeNote: (args: { note: Note }) => Promise<{ noteBuffer: Uint8Array }>; + parseNote: (args: { noteBuffer: Uint8Array }) => Promise<{ note: Note }>; +}) { + return args; +} diff --git a/packages/lib/src/crypto/serialization/serialization.registry.ts b/packages/lib/src/crypto/serialization/serialization.registry.ts new file mode 100644 index 00000000..0986e980 --- /dev/null +++ b/packages/lib/src/crypto/serialization/serialization.registry.ts @@ -0,0 +1,36 @@ +import { keyBy, map } from 'lodash-es'; +import { cborArraySerializationDefinition } from './cbor-array/cbor-array.serialization'; +import type { SerializationFormat, SerializationMethodsDefinition } from './serialization.types'; + +export { getSerializationMethod, getParsingMethod }; + +export const serializationMethodDefinitions = [ + cborArraySerializationDefinition, +] as const; + +export const serializationMethodDefinitionsByName: Record = keyBy(serializationMethodDefinitions, 'name'); +export const serializationFormat = map(serializationMethodDefinitions, 'name'); + +function getSerializationMethod({ serializationFormat }: { serializationFormat: SerializationFormat }) { + const serializationMethods = serializationMethodDefinitionsByName[serializationFormat]; + + if (!serializationMethods) { + throw new Error(`Serialization format "${serializationFormat}" not found`); + } + + const { serializeNote } = serializationMethods; + + return { serializeNote }; +} + +function getParsingMethod({ serializationFormat }: { serializationFormat: SerializationFormat }) { + const serializationMethods = serializationMethodDefinitionsByName[serializationFormat]; + + if (!serializationMethods) { + throw new Error(`Serialization format "${serializationFormat}" not found`); + } + + const { parseNote } = serializationMethods; + + return { parseNote }; +} diff --git a/packages/lib/src/crypto/serialization/serialization.test-utils.ts b/packages/lib/src/crypto/serialization/serialization.test-utils.ts new file mode 100644 index 00000000..2891f7ca --- /dev/null +++ b/packages/lib/src/crypto/serialization/serialization.test-utils.ts @@ -0,0 +1,87 @@ +import { describe, expect, test } from 'vitest'; +import type { Note } from '../../notes/notes.types'; + +export { runCommonSerializationTests }; + +function runCommonSerializationTests({ + serializeNote, + parseNote, +}: { + serializeNote: (args: { note: Note }) => Promise<{ noteBuffer: Uint8Array }>; + parseNote: (args: { noteBuffer: Uint8Array }) => Promise<{ note: Note }>; +}) { + describe('a note serialized and parsed should stay the same', async () => { + test('a note with content and no assets', async () => { + const note = { + content: 'Hello, world!', + assets: [], + }; + + const { noteBuffer } = await serializeNote({ note }); + const { note: parsedNote } = await parseNote({ noteBuffer }); + + expect(parsedNote).to.eql({ + content: 'Hello, world!', + assets: [], + }); + }); + + test('a note with content and assets', async () => { + const note = { + content: 'Hello, world!', + assets: [ + { + metadata: { type: 'file', fileType: 'image/png', width: 1920, height: 1080 }, + content: new Uint8Array([0, 1, 2, 3]), + }, + ], + }; + + const { noteBuffer } = await serializeNote({ note }); + const { note: parsedNote } = await parseNote({ noteBuffer }); + + expect(parsedNote).to.eql({ + content: 'Hello, world!', + assets: [ + { + metadata: { type: 'file', fileType: 'image/png', width: 1920, height: 1080 }, + content: new Uint8Array([0, 1, 2, 3]), + }, + ], + }); + }); + + test('a note with content and multiple assets', async () => { + const note = { + content: 'Hello, world!', + assets: [ + { + metadata: { type: 'file', fileType: 'image/png', width: 1920, height: 1080 }, + content: new Uint8Array([0, 1, 2, 3]), + }, + { + metadata: { type: 'file', fileType: 'image/png', width: 1920, height: 1080 }, + content: new Uint8Array([4, 5, 6, 7]), + }, + ], + }; + + const { noteBuffer } = await serializeNote({ note }); + const { note: parsedNote } = await parseNote({ noteBuffer }); + + expect(parsedNote).to.eql({ + content: 'Hello, world!', + assets: [ + { + metadata: { type: 'file', fileType: 'image/png', width: 1920, height: 1080 }, + content: new Uint8Array([0, 1, 2, 3]), + }, + { + metadata: { type: 'file', fileType: 'image/png', width: 1920, height: 1080 }, + content: new Uint8Array([4, 5, 6, 7]), + }, + ], + }); + }); + }); +} diff --git a/packages/lib/src/crypto/serialization/serialization.types.ts b/packages/lib/src/crypto/serialization/serialization.types.ts new file mode 100644 index 00000000..344a29a0 --- /dev/null +++ b/packages/lib/src/crypto/serialization/serialization.types.ts @@ -0,0 +1,5 @@ +import type { defineSerializationMethods } from './serialization.models'; +import type { serializationMethodDefinitions } from './serialization.registry'; + +export type SerializationMethodsDefinition = ReturnType; +export type SerializationFormat = typeof serializationMethodDefinitions[number]['name']; diff --git a/packages/lib/src/crypto/web/crypto.web.models.test.ts b/packages/lib/src/crypto/web/crypto.web.models.test.ts index b1665e98..d78b1ab2 100644 --- a/packages/lib/src/crypto/web/crypto.web.models.test.ts +++ b/packages/lib/src/crypto/web/crypto.web.models.test.ts @@ -26,6 +26,19 @@ describe('crypto models', () => { expect(base64Url).toBe('An1aThIn_OeGWQUn-e4o2nEXvdtEagY2lJxCQN1SgKc'); }); + + test('it can stringify a large buffer', () => { + const length = 10_000_000; + const buffer = new Uint8Array(length); + + for (let i = 0; i < length; i++) { + buffer[i] = i % 256; + } + + const base64Url = bufferToBase64Url({ buffer }); + + expect(base64Url).toBeDefined(); + }); }); describe('base64UrlToBuffer', () => { diff --git a/packages/lib/src/crypto/web/crypto.web.models.ts b/packages/lib/src/crypto/web/crypto.web.models.ts index 5886c2aa..4ce1753b 100644 --- a/packages/lib/src/crypto/web/crypto.web.models.ts +++ b/packages/lib/src/crypto/web/crypto.web.models.ts @@ -1,7 +1,14 @@ export { bufferToBase64Url, base64UrlToBuffer }; function bufferToBase64Url({ buffer }: { buffer: Uint8Array }): string { - const base64 = btoa(String.fromCharCode(...buffer)); + let binaryString = ''; + const chunkSize = 0x8000; // 32KB chunks to avoid stack overflow + for (let i = 0; i < buffer.length; i += chunkSize) { + const chunk = buffer.subarray(i, i + chunkSize); + binaryString += String.fromCharCode(...chunk); + } + + const base64 = btoa(binaryString); const base64Url = base64 .replace(/\+/g, '-') .replace(/\//g, '_') diff --git a/packages/lib/src/crypto/web/crypto.web.usecases.test.ts b/packages/lib/src/crypto/web/crypto.web.usecases.test.ts deleted file mode 100644 index 51660954..00000000 --- a/packages/lib/src/crypto/web/crypto.web.usecases.test.ts +++ /dev/null @@ -1,14 +0,0 @@ -import { describe } from 'vitest'; -import { runCommonCryptoUsecasesTests } from '../crypto.usecases.test'; -import { createDecryptUsecase, createEncryptUsecase } from '../crypto.usecases'; - -import * as webCryptoLib from './crypto.web.usecases'; - -describe('crypto usecases', () => { - describe('web', () => { - runCommonCryptoUsecasesTests({ - ...createEncryptUsecase({ ...webCryptoLib }), - ...createDecryptUsecase({ ...webCryptoLib }), - }); - }); -}); diff --git a/packages/lib/src/crypto/web/crypto.web.usecases.ts b/packages/lib/src/crypto/web/crypto.web.usecases.ts index 552633d1..97e0cf93 100644 --- a/packages/lib/src/crypto/web/crypto.web.usecases.ts +++ b/packages/lib/src/crypto/web/crypto.web.usecases.ts @@ -1,6 +1,6 @@ -import { base64UrlToBuffer, bufferToBase64Url } from './crypto.web.models'; +export { generateBaseKey, deriveMasterKey, createRandomBuffer }; -export { generateBaseKey, deriveMasterKey, encryptNoteContent, decryptNoteContent }; +export { getEncryptionMethod, getDecryptionMethod } from './encryption-algorithms/encryption-algorithms.registry'; function createRandomBuffer({ length = 16 }: { length?: number } = {}): Uint8Array { const randomValues = new Uint8Array(length); @@ -38,36 +38,3 @@ async function deriveMasterKey({ baseKey, password = '' }: { baseKey: Uint8Array masterKey: new Uint8Array(exportedKey), }; } - -async function encryptNoteContent({ content, masterKey }: { content: string; masterKey: Uint8Array }) { - const contentBuffer = new TextEncoder().encode(content); - const iv = createRandomBuffer({ length: 12 }); - - const key = await crypto.subtle.importKey('raw', masterKey, 'AES-GCM', false, ['encrypt']); - const encrypted = await crypto.subtle.encrypt({ name: 'AES-GCM', iv }, key, contentBuffer); - - const ivString = bufferToBase64Url({ buffer: iv }); - const encryptedString = bufferToBase64Url({ buffer: new Uint8Array(encrypted) }); - - return { - encryptedContent: `${ivString}:${encryptedString}`, - }; -} - -async function decryptNoteContent({ encryptedContent, masterKey }: { encryptedContent: string; masterKey: Uint8Array }) { - const [ivString, encryptedString] = encryptedContent.split(':').map(part => part.trim()); - - if (!ivString || !encryptedString) { - throw new Error('Invalid data'); - } - - const iv = base64UrlToBuffer({ base64Url: ivString }); - const encrypted = base64UrlToBuffer({ base64Url: encryptedString }); - - const key = await crypto.subtle.importKey('raw', masterKey, 'AES-GCM', false, ['decrypt']); - const decrypted = await crypto.subtle.decrypt({ name: 'AES-GCM', iv }, key, encrypted); - - const decryptedContent = new TextDecoder().decode(decrypted); - - return { decryptedContent }; -} diff --git a/packages/lib/src/crypto/web/encryption-algorithms/crypto.web.aes-256-gcm.ts b/packages/lib/src/crypto/web/encryption-algorithms/crypto.web.aes-256-gcm.ts new file mode 100644 index 00000000..eb6f8a46 --- /dev/null +++ b/packages/lib/src/crypto/web/encryption-algorithms/crypto.web.aes-256-gcm.ts @@ -0,0 +1,40 @@ +import { defineEncryptionMethods } from '../../encryption-algorithms/encryption-algorithms.models'; +import { base64UrlToBuffer, bufferToBase64Url } from '../crypto.web.models'; +import { createRandomBuffer } from '../crypto.web.usecases'; + +export const aes256GcmEncryptionAlgorithmDefinition = defineEncryptionMethods({ + name: 'aes-256-gcm', + + encryptBuffer: async ({ buffer, encryptionKey }) => { + const iv = createRandomBuffer({ length: 12 }); + + const key = await crypto.subtle.importKey('raw', encryptionKey, 'AES-GCM', false, ['encrypt']); + const encrypted = await crypto.subtle.encrypt({ name: 'AES-GCM', iv }, key, buffer); + const encryptedBuffer = new Uint8Array(encrypted); + + const ivString = bufferToBase64Url({ buffer: iv }); + const payloadString = bufferToBase64Url({ buffer: encryptedBuffer }); + const encryptedString = `${ivString}:${payloadString}`; + + return { + encryptedString, + }; + }, + + decryptString: async ({ encryptedString, encryptionKey }) => { + const [ivString, encryptedContentString] = encryptedString.split(':').map(part => part.trim()); + + if (!ivString || !encryptedContentString) { + throw new Error('Invalid data'); + } + + const iv = base64UrlToBuffer({ base64Url: ivString }); + const encrypted = base64UrlToBuffer({ base64Url: encryptedContentString }); + + const key = await crypto.subtle.importKey('raw', encryptionKey, 'AES-GCM', false, ['decrypt']); + const decryptedCryptoBuffer = await crypto.subtle.decrypt({ name: 'AES-GCM', iv }, key, encrypted); + const decryptedBuffer = new Uint8Array(decryptedCryptoBuffer); + + return { decryptedBuffer }; + }, +}); diff --git a/packages/lib/src/crypto/web/encryption-algorithms/encryption-algorithms.registry.ts b/packages/lib/src/crypto/web/encryption-algorithms/encryption-algorithms.registry.ts new file mode 100644 index 00000000..d6563003 --- /dev/null +++ b/packages/lib/src/crypto/web/encryption-algorithms/encryption-algorithms.registry.ts @@ -0,0 +1,13 @@ +import { createEncryptionAlgorithmsRegistry } from '../../encryption-algorithms/encryption-algorithms.registry'; +import { aes256GcmEncryptionAlgorithmDefinition } from './crypto.web.aes-256-gcm'; + +const encryptionMethodDefinitions = [ + aes256GcmEncryptionAlgorithmDefinition, +]; + +export const { + encryptionAlgorithms, + encryptionMethodDefinitionsByName, + getDecryptionMethod, + getEncryptionMethod, +} = createEncryptionAlgorithmsRegistry({ encryptionMethodDefinitions }); diff --git a/packages/lib/src/files/files.models.ts b/packages/lib/src/files/files.models.ts new file mode 100644 index 00000000..90f5521b --- /dev/null +++ b/packages/lib/src/files/files.models.ts @@ -0,0 +1,36 @@ +import { get } from 'lodash-es'; +import type { NoteAsset } from '../notes/notes.types'; + +export { fileToNoteAsset, filesToNoteAssets, noteAssetToFile, noteAssetsToFiles }; + +async function fileToNoteAsset({ file }: { file: File }): Promise { + const content = new Uint8Array(await file.arrayBuffer()); + + return { + metadata: { + type: 'file', + fileType: file.type, + name: file.name, + size: file.size, + }, + content, + }; +} + +async function filesToNoteAssets({ files }: { files: File[] }): Promise { + return Promise.all(files.map(file => fileToNoteAsset({ file }))); +} + +async function noteAssetToFile({ noteAsset }: { noteAsset: NoteAsset }): Promise { + if (noteAsset.metadata.type !== 'file') { + throw new Error('Asset is not a file'); + } + + const fileName = get(noteAsset, 'metadata.name', 'file') as string; + const fileType = get(noteAsset, 'metadata.fileType', 'application/octet-stream') as string; + return new File([noteAsset.content], fileName, { type: fileType }); +} + +async function noteAssetsToFiles({ noteAssets }: { noteAssets: NoteAsset[] }): Promise { + return Promise.all(noteAssets.map(noteAsset => noteAssetToFile({ noteAsset }))); +} diff --git a/packages/lib/src/index.node.ts b/packages/lib/src/index.node.ts index 2476cfd6..aebe71a7 100644 --- a/packages/lib/src/index.node.ts +++ b/packages/lib/src/index.node.ts @@ -1,12 +1,29 @@ import { createEnclosedLib } from './notes/notes.usecases'; -import { decryptNoteContent, deriveMasterKey, encryptNoteContent, generateBaseKey } from './crypto/node/crypto.node.usecases'; +import { deriveMasterKey, generateBaseKey } from './crypto/node/crypto.node.usecases'; import { fetchNote, storeNote } from './notes/notes.services'; import { createDecryptUsecase, createEncryptUsecase } from './crypto/crypto.usecases'; import { isApiClientErrorWithCode, isApiClientErrorWithStatusCode } from './api/api.models'; +import { fileToNoteAsset, filesToNoteAssets, noteAssetToFile, noteAssetsToFiles } from './files/files.models'; +import { getDecryptionMethod, getEncryptionMethod } from './crypto/node/encryption-algorithms/encryption-algorithms.registry'; -export const { encryptNote } = createEncryptUsecase({ generateBaseKey, deriveMasterKey, encryptNoteContent }); -export const { decryptNote } = createDecryptUsecase({ deriveMasterKey, decryptNoteContent }); +const { encryptNote } = createEncryptUsecase({ generateBaseKey, deriveMasterKey, getEncryptionMethod }); +const { decryptNote } = createDecryptUsecase({ deriveMasterKey, getDecryptionMethod }); -export const { createNote, createNoteUrl, parseNoteUrl } = createEnclosedLib({ encryptNote, storeNote }); +const { createNote, createNoteUrl, parseNoteUrl } = createEnclosedLib({ encryptNote, storeNote }); -export { fetchNote, storeNote, isApiClientErrorWithStatusCode, isApiClientErrorWithCode }; +export { + fetchNote, + storeNote, + isApiClientErrorWithStatusCode, + isApiClientErrorWithCode, + fileToNoteAsset, + filesToNoteAssets, + noteAssetToFile, + noteAssetsToFiles, + encryptNote, + decryptNote, + createNote, + createNoteUrl, + parseNoteUrl, + +}; diff --git a/packages/lib/src/index.web.ts b/packages/lib/src/index.web.ts index ea0ce969..0658cc38 100644 --- a/packages/lib/src/index.web.ts +++ b/packages/lib/src/index.web.ts @@ -1,12 +1,28 @@ import { createEnclosedLib } from './notes/notes.usecases'; -import { decryptNoteContent, deriveMasterKey, encryptNoteContent, generateBaseKey } from './crypto/web/crypto.web.usecases'; +import { deriveMasterKey, generateBaseKey } from './crypto/web/crypto.web.usecases'; import { fetchNote, storeNote } from './notes/notes.services'; import { createDecryptUsecase, createEncryptUsecase } from './crypto/crypto.usecases'; import { isApiClientErrorWithCode, isApiClientErrorWithStatusCode } from './api/api.models'; +import { fileToNoteAsset, filesToNoteAssets, noteAssetToFile, noteAssetsToFiles } from './files/files.models'; +import { getDecryptionMethod, getEncryptionMethod } from './crypto/web/encryption-algorithms/encryption-algorithms.registry'; -export const { encryptNote } = createEncryptUsecase({ generateBaseKey, deriveMasterKey, encryptNoteContent }); -export const { decryptNote } = createDecryptUsecase({ deriveMasterKey, decryptNoteContent }); +const { encryptNote } = createEncryptUsecase({ generateBaseKey, deriveMasterKey, getEncryptionMethod }); +const { decryptNote } = createDecryptUsecase({ deriveMasterKey, getDecryptionMethod }); -export const { createNote, createNoteUrl, parseNoteUrl } = createEnclosedLib({ encryptNote, storeNote }); +const { createNote, createNoteUrl, parseNoteUrl } = createEnclosedLib({ encryptNote, storeNote }); -export { fetchNote, storeNote, isApiClientErrorWithStatusCode, isApiClientErrorWithCode }; +export { + fetchNote, + storeNote, + isApiClientErrorWithStatusCode, + isApiClientErrorWithCode, + fileToNoteAsset, + filesToNoteAssets, + noteAssetToFile, + noteAssetsToFiles, + encryptNote, + decryptNote, + createNote, + createNoteUrl, + parseNoteUrl, +}; diff --git a/packages/lib/src/notes/notes.services.ts b/packages/lib/src/notes/notes.services.ts index 486a5b35..fd0ca492 100644 --- a/packages/lib/src/notes/notes.services.ts +++ b/packages/lib/src/notes/notes.services.ts @@ -8,12 +8,14 @@ async function storeNote({ ttlInSeconds, deleteAfterReading, apiBaseUrl, + assets, }: { content: string; isPasswordProtected: boolean; ttlInSeconds: number; deleteAfterReading: boolean; apiBaseUrl?: string; + assets: string[]; }): Promise<{ noteId: string }> { const { noteId } = await apiClient<{ noteId: string }>({ path: 'api/notes', @@ -24,6 +26,7 @@ async function storeNote({ isPasswordProtected, ttlInSeconds, deleteAfterReading, + assets, }, }); diff --git a/packages/lib/src/notes/notes.types.ts b/packages/lib/src/notes/notes.types.ts new file mode 100644 index 00000000..6d2536d8 --- /dev/null +++ b/packages/lib/src/notes/notes.types.ts @@ -0,0 +1,25 @@ +import type { CompressionAlgorithm, EncryptionAlgorithm, KeyDerivationAlgorithm } from '../crypto/crypto.types'; +import type { SerializationFormat } from '../crypto/serialization/serialization.types'; + +export type NoteAsset = { + metadata: { + type: string; + [key: string]: unknown ; + }; + content: Uint8Array; +}; + +export type Note = { + content: string; + assets: NoteAsset[]; +}; + +export type EncryptedNote = { + payload: string; + encryptionAlgorithm: EncryptionAlgorithm; + serializationFormat: SerializationFormat; + keyDerivationAlgorithm: KeyDerivationAlgorithm; + compressionAlgorithm: CompressionAlgorithm; + ttlInSeconds: number; + deleteAfterReading: boolean; +}; diff --git a/packages/lib/src/notes/notes.usecases.ts b/packages/lib/src/notes/notes.usecases.ts index 2273c1e0..5c38f507 100644 --- a/packages/lib/src/notes/notes.usecases.ts +++ b/packages/lib/src/notes/notes.usecases.ts @@ -1,4 +1,7 @@ +import type { EncryptionAlgorithm } from '../crypto/crypto.types'; +import type { SerializationFormat } from '../crypto/serialization/serialization.types'; import { createNoteUrl as createNoteUrlImpl, parseNoteUrl } from './notes.models'; +import type { NoteAsset } from './notes.types'; export { createEnclosedLib }; @@ -11,9 +14,24 @@ function createEnclosedLib({ storeNote: storeNoteImpl, // fetchNote: fetchNoteImpl, }: { - encryptNote: (args: { content: string; password?: string }) => Promise<{ encryptedContent: string; encryptionKey: string }>; + encryptNote: (args: { + content: string; + password?: string; + assets?: NoteAsset[]; + encryptionAlgorithm?: EncryptionAlgorithm; + serializationFormat?: SerializationFormat; + }) => Promise<{ + encryptedPayload: string; + encryptionKey: string; + }>; // decryptNote: (args: { encryptedContent: string; encryptionKey: string }) => Promise<{ content: string }>; - storeNote: (params: { content: string; isPasswordProtected: boolean; ttlInSeconds: number; deleteAfterReading: boolean; apiBaseUrl?: string }) => Promise<{ noteId: string }>; + storeNote: (params: { + content: string; + isPasswordProtected: boolean; + ttlInSeconds: number; + deleteAfterReading: boolean; + apiBaseUrl?: string; + }) => Promise<{ noteId: string }>; // fetchNote: (params: { noteId: string; apiBaseUrl?: string }) => Promise<{ content: string; isPasswordProtected: boolean }>; }) { return { @@ -29,6 +47,7 @@ function createEnclosedLib({ apiBaseUrl = clientBaseUrl, createNoteUrl = createNoteUrlImpl, storeNote = params => storeNoteImpl({ ...params, apiBaseUrl }), + assets = [], }: { content: string; password?: string; @@ -36,17 +55,18 @@ function createEnclosedLib({ deleteAfterReading?: boolean; clientBaseUrl?: string; apiBaseUrl?: string; + assets?: NoteAsset[]; createNoteUrl?: (args: { noteId: string; encryptionKey: string; clientBaseUrl: string }) => { noteUrl: string }; storeNote?: (params: { content: string; isPasswordProtected: boolean; ttlInSeconds: number; deleteAfterReading: boolean }) => Promise<{ noteId: string }>; }) => { - const { encryptedContent, encryptionKey } = await encryptNote({ content, password }); + const { encryptedPayload, encryptionKey } = await encryptNote({ content, password, assets }); - const { noteId } = await storeNote({ content: encryptedContent, isPasswordProtected: Boolean(password), ttlInSeconds, deleteAfterReading }); + const { noteId } = await storeNote({ content: encryptedPayload, isPasswordProtected: Boolean(password), ttlInSeconds, deleteAfterReading }); const { noteUrl } = createNoteUrl({ noteId, encryptionKey, clientBaseUrl }); return { - encryptedContent, + encryptedPayload, encryptionKey, noteId, noteUrl, diff --git a/pnpm-lock.yaml b/pnpm-lock.yaml index 3ea30b4e..e7a4f820 100644 --- a/pnpm-lock.yaml +++ b/pnpm-lock.yaml @@ -10,6 +10,9 @@ importers: packages/app-client: dependencies: + '@corentinth/chisels': + specifier: ^1.0.2 + version: 1.0.2 '@enclosed/lib': specifier: workspace:* version: link:../lib @@ -28,6 +31,9 @@ importers: clsx: specifier: ^2.1.1 version: 2.1.1 + jszip: + specifier: ^3.10.1 + version: 3.10.1 lodash-es: specifier: ^4.17.21 version: 4.17.21 @@ -39,11 +45,11 @@ importers: version: 2.5.2 unocss-preset-animations: specifier: ^1.1.0 - version: 1.1.0(@unocss/preset-wind@0.62.3)(unocss@0.62.3(postcss@8.4.44)(rollup@4.21.2)(vite@5.4.3(@types/node@22.5.4)(terser@5.31.6))) + version: 1.1.0(@unocss/preset-wind@0.62.3)(unocss@0.62.3(postcss@8.4.45)(rollup@4.21.2)(vite@5.4.3(@types/node@22.5.4)(terser@5.31.6))) devDependencies: '@antfu/eslint-config': specifier: ^2.27.0 - version: 2.27.3(@typescript-eslint/utils@8.3.0(eslint@9.10.0(jiti@1.21.6))(typescript@5.5.4))(@vue/compiler-sfc@3.4.38)(eslint@9.10.0(jiti@1.21.6))(typescript@5.5.4)(vitest@2.0.5(@types/node@22.5.4)(jsdom@25.0.0)(terser@5.31.6)) + version: 2.27.3(@typescript-eslint/utils@8.4.0(eslint@9.9.1(jiti@1.21.6))(typescript@5.5.4))(@vue/compiler-sfc@3.5.3)(eslint@9.9.1(jiti@1.21.6))(typescript@5.5.4)(vitest@2.0.5(@types/node@22.5.4)(jsdom@25.0.0)(terser@5.31.6)) '@iconify-json/tabler': specifier: ^1.1.120 version: 1.2.0 @@ -52,7 +58,7 @@ importers: version: 4.17.12 eslint: specifier: ^9.9.0 - version: 9.10.0(jiti@1.21.6) + version: 9.9.1(jiti@1.21.6) jsdom: specifier: ^25.0.0 version: 25.0.0 @@ -61,7 +67,7 @@ importers: version: 5.5.4 unocss: specifier: ^0.62.2 - version: 0.62.3(postcss@8.4.44)(rollup@4.21.2)(vite@5.4.3(@types/node@22.5.4)(terser@5.31.6)) + version: 0.62.3(postcss@8.4.45)(rollup@4.21.2)(vite@5.4.3(@types/node@22.5.4)(terser@5.31.6)) vite: specifier: ^5.0.11 version: 5.4.3(@types/node@22.5.4)(terser@5.31.6) @@ -74,6 +80,9 @@ importers: packages/app-server: dependencies: + '@corentinth/chisels': + specifier: ^1.0.4 + version: 1.0.4 '@hono/node-server': specifier: ^1.12.1 version: 1.12.2(hono@4.5.11) @@ -100,14 +109,14 @@ importers: version: 2.1.0 unstorage: specifier: ^1.10.2 - version: 1.12.0 + version: 1.11.1 zod: specifier: ^3.23.8 version: 3.23.8 devDependencies: '@antfu/eslint-config': specifier: ^2.27.0 - version: 2.27.3(@typescript-eslint/utils@8.3.0(eslint@9.10.0(jiti@1.21.6))(typescript@5.5.4))(@vue/compiler-sfc@3.4.38)(eslint@9.10.0(jiti@1.21.6))(typescript@5.5.4)(vitest@2.0.5(@types/node@22.5.4)(jsdom@25.0.0)(terser@5.31.6)) + version: 2.27.3(@typescript-eslint/utils@8.4.0(eslint@9.9.1(jiti@1.21.6))(typescript@5.5.4))(@vue/compiler-sfc@3.5.3)(eslint@9.9.1(jiti@1.21.6))(typescript@5.5.4)(vitest@2.0.5(@types/node@22.5.4)(jsdom@25.0.0)(terser@5.31.6)) '@cloudflare/workers-types': specifier: ^4.20240821.1 version: 4.20240903.0 @@ -131,7 +140,7 @@ importers: version: 0.23.1 eslint: specifier: ^9.9.0 - version: 9.10.0(jiti@1.21.6) + version: 9.9.1(jiti@1.21.6) pino-pretty: specifier: ^11.2.2 version: 11.2.2 @@ -146,7 +155,7 @@ importers: version: 2.0.5(@types/node@22.5.4)(jsdom@25.0.0)(terser@5.31.6) wrangler: specifier: ^3.72.1 - version: 3.75.0(@cloudflare/workers-types@4.20240903.0) + version: 3.74.0(@cloudflare/workers-types@4.20240903.0) zx: specifier: ^8.1.4 version: 8.1.5 @@ -158,7 +167,7 @@ importers: version: link:../lib '@inquirer/prompts': specifier: ^5.3.8 - version: 5.5.0 + version: 5.4.0 citty: specifier: ^0.1.6 version: 0.1.6 @@ -183,7 +192,7 @@ importers: devDependencies: '@antfu/eslint-config': specifier: ^2.27.0 - version: 2.27.3(@typescript-eslint/utils@8.3.0(eslint@9.10.0(jiti@1.21.6))(typescript@5.5.4))(@vue/compiler-sfc@3.4.38)(eslint@9.10.0(jiti@1.21.6))(typescript@5.5.4)(vitest@2.0.5(@types/node@22.5.4)(jsdom@25.0.0)(terser@5.31.6)) + version: 2.27.3(@typescript-eslint/utils@8.4.0(eslint@9.9.1(jiti@1.21.6))(typescript@5.5.4))(@vue/compiler-sfc@3.5.3)(eslint@9.9.1(jiti@1.21.6))(typescript@5.5.4)(vitest@2.0.5(@types/node@22.5.4)(jsdom@25.0.0)(terser@5.31.6)) '@types/lodash-es': specifier: ^4.17.12 version: 4.17.12 @@ -195,7 +204,7 @@ importers: version: 16.4.5 eslint: specifier: ^9.9.0 - version: 9.10.0(jiti@1.21.6) + version: 9.9.1(jiti@1.21.6) typescript: specifier: ^5.5.4 version: 5.5.4 @@ -212,23 +221,32 @@ importers: devDependencies: vitepress: specifier: ^1.3.4 - version: 1.3.4(@algolia/client-search@4.24.0)(@types/node@22.5.4)(postcss@8.4.44)(search-insights@2.17.0)(terser@5.31.6)(typescript@5.5.4) + version: 1.3.4(@algolia/client-search@5.3.0)(@types/node@22.5.4)(postcss@8.4.45)(search-insights@2.17.1)(terser@5.31.6)(typescript@5.5.4) packages/lib: dependencies: + cbor-x: + specifier: ^1.6.0 + version: 1.6.0 lodash-es: specifier: ^4.17.21 version: 4.17.21 + msgpackr: + specifier: ^1.11.0 + version: 1.11.0 ofetch: specifier: ^1.3.4 version: 1.3.4 devDependencies: '@antfu/eslint-config': specifier: ^2.27.0 - version: 2.27.3(@typescript-eslint/utils@8.3.0(eslint@9.10.0(jiti@1.21.6))(typescript@5.5.4))(@vue/compiler-sfc@3.4.38)(eslint@9.10.0(jiti@1.21.6))(typescript@5.5.4)(vitest@2.0.5(@types/node@22.5.4)(jsdom@25.0.0)(terser@5.31.6)) + version: 2.27.3(@typescript-eslint/utils@8.4.0(eslint@9.9.1(jiti@1.21.6))(typescript@5.5.4))(@vue/compiler-sfc@3.5.3)(eslint@9.9.1(jiti@1.21.6))(typescript@5.5.4)(vitest@2.0.5(@types/node@22.5.4)(jsdom@25.0.0)(terser@5.31.6)) '@types/lodash-es': specifier: ^4.17.12 version: 4.17.12 + '@types/node': + specifier: ^22.5.4 + version: 22.5.4 '@vitest/coverage-v8': specifier: ^2.0.5 version: 2.0.5(vitest@2.0.5(@types/node@22.5.4)(jsdom@25.0.0)(terser@5.31.6)) @@ -237,7 +255,10 @@ importers: version: 16.4.5 eslint: specifier: ^9.9.0 - version: 9.10.0(jiti@1.21.6) + version: 9.9.1(jiti@1.21.6) + tsx: + specifier: ^4.17.0 + version: 4.19.0 typescript: specifier: ^5.5.4 version: 5.5.4 @@ -288,12 +309,20 @@ packages: '@algolia/client-common@4.24.0': resolution: {integrity: sha512-bc2ROsNL6w6rqpl5jj/UywlIYC21TwSSoFHKl01lYirGMW+9Eek6r02Tocg4gZ8HAw3iBvu6XQiM3BEbmEMoiA==} + '@algolia/client-common@5.3.0': + resolution: {integrity: sha512-iGx2c9aI8ZiGD512WUIu36hrG0XtJOBWseI+w7DyQpfDcG9u9Go9/9jkJZRXWzfrCqlMWyXMQ8z5N4vKTAhZ6g==} + engines: {node: '>= 14.0.0'} + '@algolia/client-personalization@4.24.0': resolution: {integrity: sha512-l5FRFm/yngztweU0HdUzz1rC4yoWCFo3IF+dVIVTfEPg906eZg5BOd1k0K6rZx5JzyyoP4LdmOikfkfGsKVE9w==} '@algolia/client-search@4.24.0': resolution: {integrity: sha512-uRW6EpNapmLAD0mW47OXqTP8eiIx5F6qN9/x/7HHO6owL3N1IXqydGwW5nhDFBrV+ldouro2W1VX3XlcUXEFCA==} + '@algolia/client-search@5.3.0': + resolution: {integrity: sha512-k4MWhi6j2YhvwKqyLmk6AKr1Vts/HDmbfjmzyd2/j72ftRHQ/nHWwsvoSyrTBu39yv3loNduBAXu58vw0JFJsQ==} + engines: {node: '>= 14.0.0'} + '@algolia/logger-common@4.24.0': resolution: {integrity: sha512-LLUNjkahj9KtKYrQhFKCzMx0BY3RnNP4FEtO+sBybCjJ73E8jNdaKJ/Dd8A/VA4imVHP5tADZ8pn5B8Ga/wTMA==} @@ -306,12 +335,20 @@ packages: '@algolia/requester-browser-xhr@4.24.0': resolution: {integrity: sha512-Z2NxZMb6+nVXSjF13YpjYTdvV3032YTBSGm2vnYvYPA6mMxzM3v5rsCiSspndn9rzIW4Qp1lPHBvuoKJV6jnAA==} + '@algolia/requester-browser-xhr@5.3.0': + resolution: {integrity: sha512-+lPsyrV8xmwyN3O4jFDvyiXph4S6Xa3r0DSzT0GLKnHGm6vHa81f5URruk6wYh1OVCn1H6MDMrawjKF4fY11qA==} + engines: {node: '>= 14.0.0'} + '@algolia/requester-common@4.24.0': resolution: {integrity: sha512-k3CXJ2OVnvgE3HMwcojpvY6d9kgKMPRxs/kVohrwF5WMr2fnqojnycZkxPoEg+bXm8fi5BBfFmOqgYztRtHsQA==} '@algolia/requester-node-http@4.24.0': resolution: {integrity: sha512-JF18yTjNOVYvU/L3UosRcvbPMGT9B+/GQWNWnenIImglzNVGpyzChkXLnrSf6uxwVNO6ESGu6oN8MqcGQcjQJw==} + '@algolia/requester-node-http@5.3.0': + resolution: {integrity: sha512-e9aWqwOJAnIS366iq0NFut3RCJutaqs8Gb0z9MQIgg6M/zg38jE0+Xgz6RscN0wPazRODpvPjkpKgsDHjftyUw==} + engines: {node: '>= 14.0.0'} + '@algolia/transporter@4.24.0': resolution: {integrity: sha512-86nI7w6NzWxd1Zp9q3413dRshDqAzSbsQjhcDhPIatEFiZrL1/TjnHL8S7jVKFePlIMzDsZWXAXwXzcok9c5oA==} @@ -524,6 +561,36 @@ packages: '@bcoe/v8-coverage@0.2.3': resolution: {integrity: sha512-0hYQ8SB4Db5zvZB4axdMHGwEaQjkZzFjQiN9LVYvIFB2nSUHW9tYpxWriPrWDASIxiaXax83REcLxuSdnGPZtw==} + '@cbor-extract/cbor-extract-darwin-arm64@2.2.0': + resolution: {integrity: sha512-P7swiOAdF7aSi0H+tHtHtr6zrpF3aAq/W9FXx5HektRvLTM2O89xCyXF3pk7pLc7QpaY7AoaE8UowVf9QBdh3w==} + cpu: [arm64] + os: [darwin] + + '@cbor-extract/cbor-extract-darwin-x64@2.2.0': + resolution: {integrity: sha512-1liF6fgowph0JxBbYnAS7ZlqNYLf000Qnj4KjqPNW4GViKrEql2MgZnAsExhY9LSy8dnvA4C0qHEBgPrll0z0w==} + cpu: [x64] + os: [darwin] + + '@cbor-extract/cbor-extract-linux-arm64@2.2.0': + resolution: {integrity: sha512-rQvhNmDuhjTVXSPFLolmQ47/ydGOFXtbR7+wgkSY0bdOxCFept1hvg59uiLPT2fVDuJFuEy16EImo5tE2x3RsQ==} + cpu: [arm64] + os: [linux] + + '@cbor-extract/cbor-extract-linux-arm@2.2.0': + resolution: {integrity: sha512-QeBcBXk964zOytiedMPQNZr7sg0TNavZeuUCD6ON4vEOU/25+pLhNN6EDIKJ9VLTKaZ7K7EaAriyYQ1NQ05s/Q==} + cpu: [arm] + os: [linux] + + '@cbor-extract/cbor-extract-linux-x64@2.2.0': + resolution: {integrity: sha512-cWLAWtT3kNLHSvP4RKDzSTX9o0wvQEEAj4SKvhWuOVZxiDAeQazr9A+PSiRILK1VYMLeDml89ohxCnUNQNQNCw==} + cpu: [x64] + os: [linux] + + '@cbor-extract/cbor-extract-win32-x64@2.2.0': + resolution: {integrity: sha512-l2M+Z8DO2vbvADOBNLbbh9y5ST1RY5sqkWOg/58GkUPBYou/cuNZ68SGQ644f1CvZ8kcOxyZtw06+dxWHIoN/w==} + cpu: [x64] + os: [win32] + '@clack/core@0.3.4': resolution: {integrity: sha512-H4hxZDXgHtWTwV3RAVenqcC4VbJZNegbBjlPvzOzCouXtS2y3sDvlO3IsbrPNWuLWPPlYVYPghQdSF64683Ldw==} @@ -573,6 +640,12 @@ packages: '@cloudflare/workers-types@4.20240903.0': resolution: {integrity: sha512-a4mqgtVsPWg3JNNlQdLRE0Z6/mHr/uXa1ANDw6Zd7in438UCbeb+j7Z954Sf93G24jExpAn9VZ8kUUml0RwZbQ==} + '@corentinth/chisels@1.0.2': + resolution: {integrity: sha512-q36uwefVyaakKwpMZeBmTRUg9pvoksF1cTRAVEIeKlQK9jEiSxa8yok0lN10CyoSI00TeCBIngCKTArDN/uEHQ==} + + '@corentinth/chisels@1.0.4': + resolution: {integrity: sha512-xFIh20+OC5u/GqLNtMfXdI+ZU7cu7E3kieys56OJC+SS6KfNAmF5nTQmi9axwYlCnc94cTlztGQsujGV5s1QqQ==} + '@corvu/utils@0.4.1': resolution: {integrity: sha512-swd/2DUJiXTo9PM5GVbBv3jNlEOwmKWvVOJG2l8Cwu1cokoXknKrDkBSZRz+yf0rFXdbhWhRQMWDPMPwuBnWNg==} peerDependencies: @@ -1199,18 +1272,14 @@ packages: resolution: {integrity: sha512-4Bfj15dVJdoy3RfZmmo86RK1Fwzn6SstsvK9JS+BaVKqC6QQQQyXekNaC+g+LKNgkQ+2VhGAzm6hO40AhMR3zQ==} engines: {node: ^18.18.0 || ^20.9.0 || >=21.1.0} - '@eslint/js@9.10.0': - resolution: {integrity: sha512-fuXtbiP5GWIn8Fz+LWoOMVf/Jxm+aajZYkhi6CuEm4SxymFM+eUWzbO9qXT+L0iCkL5+KGYMCSGxo686H19S1g==} + '@eslint/js@9.9.1': + resolution: {integrity: sha512-xIDQRsfg5hNBqHz04H1R3scSVwmI+KUbqjsQKHKQ1DAUSaUjYPReZZmS/5PNiKu1fUvzDd6H7DEDKACSEhu+TQ==} engines: {node: ^18.18.0 || ^20.9.0 || >=21.1.0} '@eslint/object-schema@2.1.4': resolution: {integrity: sha512-BsWiH1yFGjXXS2yvrf5LyuoSIIbPrGUWob917o+BTKuZ7qJdxX8aJLRxs1fS9n6r7vESrq1OUqb68dANcFXuQQ==} engines: {node: ^18.18.0 || ^20.9.0 || >=21.1.0} - '@eslint/plugin-kit@0.1.0': - resolution: {integrity: sha512-autAXT203ixhqei9xt+qkYOvY8l6LAFIdT2UXc/RPNeUVfqRF1BV94GTJyVPFKT8nFM6MyVJhjLj9E8JWvf5zQ==} - engines: {node: ^18.18.0 || ^20.9.0 || >=21.1.0} - '@fastify/busboy@2.1.1': resolution: {integrity: sha512-vBZP4NlzfOlerQTnba4aqZoMhE/a9HY7HRqoOPaETQcSQuWEIyZMHGfVu6w9wGtGK5fED5qRs2DteVCjOH60sA==} engines: {node: '>=14'} @@ -1263,8 +1332,8 @@ packages: resolution: {integrity: sha512-9KHOpJ+dIL5SZli8lJ6xdaYLPPzB8xB9GZItg39MBybzhxA16vxmszmQFrRwbOA918WA2rvu8xhDEg/p6LXKbw==} engines: {node: '>=18'} - '@inquirer/expand@2.3.0': - resolution: {integrity: sha512-qnJsUcOGCSG1e5DTOErmv2BPQqrtT6uzqn1vI/aYGiPKq+FgslGZmtdnXbhuI7IlT7OByDoEEqdnhUnVR2hhLw==} + '@inquirer/expand@2.2.0': + resolution: {integrity: sha512-PD0z1dTRTIlpcnXRMRvdVPfBe10jBf4i7YLBU8tNWDkf3HxqmdymVvqnT8XG+hxQSvqfpJCe13Jv2Iv1eB3bIg==} engines: {node: '>=18'} '@inquirer/figures@1.0.5': @@ -1283,8 +1352,8 @@ packages: resolution: {integrity: sha512-5otqIpgsPYIshqhgtEwSspBQE40etouR8VIxzpJkv9i0dVHIpyhiivbkH9/dGiMLdyamT54YRdGJLfl8TFnLHg==} engines: {node: '>=18'} - '@inquirer/prompts@5.5.0': - resolution: {integrity: sha512-BHDeL0catgHdcHbSFFUddNzvx/imzJMft+tWDPwTm3hfu8/tApk1HrooNngB2Mb4qY+KaRWF+iZqoVUPeslEog==} + '@inquirer/prompts@5.4.0': + resolution: {integrity: sha512-HIQGd7JOX6WXf7zg7WGs+1m+e3eRFyL4mDtWRlV01AXqZido9W3BSoku2BR4E1lK/NCXok6jg6tTcLw4I0thfg==} engines: {node: '>=18'} '@inquirer/rawlist@2.3.0': @@ -1351,6 +1420,36 @@ packages: peerDependencies: solid-js: ^1.8.8 + '@msgpackr-extract/msgpackr-extract-darwin-arm64@3.0.3': + resolution: {integrity: sha512-QZHtlVgbAdy2zAqNA9Gu1UpIuI8Xvsd1v8ic6B2pZmeFnFcMWiPLfWXh7TVw4eGEZ/C9TH281KwhVoeQUKbyjw==} + cpu: [arm64] + os: [darwin] + + '@msgpackr-extract/msgpackr-extract-darwin-x64@3.0.3': + resolution: {integrity: sha512-mdzd3AVzYKuUmiWOQ8GNhl64/IoFGol569zNRdkLReh6LRLHOXxU4U8eq0JwaD8iFHdVGqSy4IjFL4reoWCDFw==} + cpu: [x64] + os: [darwin] + + '@msgpackr-extract/msgpackr-extract-linux-arm64@3.0.3': + resolution: {integrity: sha512-YxQL+ax0XqBJDZiKimS2XQaf+2wDGVa1enVRGzEvLLVFeqa5kx2bWbtcSXgsxjQB7nRqqIGFIcLteF/sHeVtQg==} + cpu: [arm64] + os: [linux] + + '@msgpackr-extract/msgpackr-extract-linux-arm@3.0.3': + resolution: {integrity: sha512-fg0uy/dG/nZEXfYilKoRe7yALaNmHoYeIoJuJ7KJ+YyU2bvY8vPv27f7UKhGRpY6euFYqEVhxCFZgAUNQBM3nw==} + cpu: [arm] + os: [linux] + + '@msgpackr-extract/msgpackr-extract-linux-x64@3.0.3': + resolution: {integrity: sha512-cvwNfbP07pKUfq1uH+S6KJ7dT9K8WOE4ZiAcsrSes+UY55E/0jLYc+vq+DO7jlmqRb5zAggExKm0H7O/CBaesg==} + cpu: [x64] + os: [linux] + + '@msgpackr-extract/msgpackr-extract-win32-x64@3.0.3': + resolution: {integrity: sha512-x0fWaQtYp4E6sktbsdAqnehxDgEc/VwM7uLsRCYWaiGu0ykYdZPiS8zCWdnjHwyiumousxfBm4SO31eXqwEZhQ==} + cpu: [x64] + os: [win32] + '@nodelib/fs.scandir@2.1.5': resolution: {integrity: sha512-vq24Bq3ym5HEQm2NKCr3yXDwjc7vTsEThRDnkp2DK9p1uqLR+DHurm/NOTo0KG7HYHU7eppKZj3MyqYuMBf62g==} engines: {node: '>= 8'} @@ -1758,8 +1857,8 @@ packages: '@types/node-forge@1.3.11': resolution: {integrity: sha512-FQx220y22OKNTqaByeBGqHWYz4cl94tpcxeFdvBo3wjG6XPBuZ0BNgNZRV5J5TFmmcsJ4IzsLkmGRiQbnYsBEQ==} - '@types/node@22.5.1': - resolution: {integrity: sha512-KkHsxej0j9IW1KKOOAA/XBA0z08UFSrRQHErzEfA3Vgq57eXIMYboIlHJuYIfd+lwCQjtKqUu3UnmKbtUc9yRw==} + '@types/node@22.5.2': + resolution: {integrity: sha512-acJsPTEqYqulZS/Yp/S3GgeE6GZ0qYODUR8aVr/DkhHQ8l9nd4j5x1/ZJy9/gHrRlFMqkO6i0I3E27Alu4jjPg==} '@types/node@22.5.4': resolution: {integrity: sha512-FDuKUJQm/ju9fT/SeX/6+gBzoPzlVCzfzmGkwKvRHQVxi4BntVbyIwf6a4Xn62mrvndLiml6z/UBXIdEVjQLXg==} @@ -1804,6 +1903,10 @@ packages: resolution: {integrity: sha512-mz2X8WcN2nVu5Hodku+IR8GgCOl4C0G/Z1ruaWN4dgec64kDBabuXyPAr+/RgJtumv8EEkqIzf3X2U5DUKB2eg==} engines: {node: ^18.18.0 || ^20.9.0 || >=21.1.0} + '@typescript-eslint/scope-manager@8.4.0': + resolution: {integrity: sha512-n2jFxLeY0JmKfUqy3P70rs6vdoPjHK8P/w+zJcV3fk0b0BwRXC/zxRTEnAsgYT7MwdQDt/ZEbtdzdVC+hcpF0A==} + engines: {node: ^18.18.0 || ^20.9.0 || >=21.1.0} + '@typescript-eslint/type-utils@8.3.0': resolution: {integrity: sha512-wrV6qh//nLbfXZQoj32EXKmwHf4b7L+xXLrP3FZ0GOUU72gSvLjeWUl5J5Ue5IwRxIV1TfF73j/eaBapxx99Lg==} engines: {node: ^18.18.0 || ^20.9.0 || >=21.1.0} @@ -1821,6 +1924,10 @@ packages: resolution: {integrity: sha512-y6sSEeK+facMaAyixM36dQ5NVXTnKWunfD1Ft4xraYqxP0lC0POJmIaL/mw72CUMqjY9qfyVfXafMeaUj0noWw==} engines: {node: ^18.18.0 || ^20.9.0 || >=21.1.0} + '@typescript-eslint/types@8.4.0': + resolution: {integrity: sha512-T1RB3KQdskh9t3v/qv7niK6P8yvn7ja1mS7QK7XfRVL6wtZ8/mFs/FHf4fKvTA0rKnqnYxl/uHFNbnEt0phgbw==} + engines: {node: ^18.18.0 || ^20.9.0 || >=21.1.0} + '@typescript-eslint/typescript-estree@8.3.0': resolution: {integrity: sha512-Mq7FTHl0R36EmWlCJWojIC1qn/ZWo2YiWYc1XVtasJ7FIgjo0MVv9rZWXEE7IK2CGrtwe1dVOxWwqXUdNgfRCA==} engines: {node: ^18.18.0 || ^20.9.0 || >=21.1.0} @@ -1830,16 +1937,35 @@ packages: typescript: optional: true + '@typescript-eslint/typescript-estree@8.4.0': + resolution: {integrity: sha512-kJ2OIP4dQw5gdI4uXsaxUZHRwWAGpREJ9Zq6D5L0BweyOrWsL6Sz0YcAZGWhvKnH7fm1J5YFE1JrQL0c9dd53A==} + engines: {node: ^18.18.0 || ^20.9.0 || >=21.1.0} + peerDependencies: + typescript: '*' + peerDependenciesMeta: + typescript: + optional: true + '@typescript-eslint/utils@8.3.0': resolution: {integrity: sha512-F77WwqxIi/qGkIGOGXNBLV7nykwfjLsdauRB/DOFPdv6LTF3BHHkBpq81/b5iMPSF055oO2BiivDJV4ChvNtXA==} engines: {node: ^18.18.0 || ^20.9.0 || >=21.1.0} peerDependencies: eslint: ^8.57.0 || ^9.0.0 + '@typescript-eslint/utils@8.4.0': + resolution: {integrity: sha512-swULW8n1IKLjRAgciCkTCafyTHHfwVQFt8DovmaF69sKbOxTSFMmIZaSHjqO9i/RV0wIblaawhzvtva8Nmm7lQ==} + engines: {node: ^18.18.0 || ^20.9.0 || >=21.1.0} + peerDependencies: + eslint: ^8.57.0 || ^9.0.0 + '@typescript-eslint/visitor-keys@8.3.0': resolution: {integrity: sha512-RmZwrTbQ9QveF15m/Cl28n0LXD6ea2CjkhH5rQ55ewz3H24w+AMCJHPVYaZ8/0HoG8Z3cLLFFycRXxeO2tz9FA==} engines: {node: ^18.18.0 || ^20.9.0 || >=21.1.0} + '@typescript-eslint/visitor-keys@8.4.0': + resolution: {integrity: sha512-zTQD6WLNTre1hj5wp09nBIDiOc2U5r/qmzo7wxPn4ZgAjHql09EofqhF9WF+fZHzL5aCyaIpPcT2hyxl73kr9A==} + engines: {node: ^18.18.0 || ^20.9.0 || >=21.1.0} + '@unocss/astro@0.62.3': resolution: {integrity: sha512-C6ZdyLbLDS0LebwmgwVItLNAOSkL/tvVWNRd1i3Jy5uj1vPxlrw+3lIYiHjEofn0GFpBiwlv5+OCvO1Xpq5MqA==} peerDependencies: @@ -1974,15 +2100,27 @@ packages: '@vue/compiler-core@3.4.38': resolution: {integrity: sha512-8IQOTCWnLFqfHzOGm9+P8OPSEDukgg3Huc92qSG49if/xI2SAwLHQO2qaPQbjCWPBcQoO1WYfXfTACUrWV3c5A==} + '@vue/compiler-core@3.5.3': + resolution: {integrity: sha512-adAfy9boPkP233NTyvLbGEqVuIfK/R0ZsBsIOW4BZNfb4BRpRW41Do1u+ozJpsb+mdoy80O20IzAsHaihRb5qA==} + '@vue/compiler-dom@3.4.38': resolution: {integrity: sha512-Osc/c7ABsHXTsETLgykcOwIxFktHfGSUDkb05V61rocEfsFDcjDLH/IHJSNJP+/Sv9KeN2Lx1V6McZzlSb9EhQ==} + '@vue/compiler-dom@3.5.3': + resolution: {integrity: sha512-wnzFArg9zpvk/811CDOZOadJRugf1Bgl/TQ3RfV4nKfSPok4hi0w10ziYUQR6LnnBAUlEXYLUfZ71Oj9ds/+QA==} + '@vue/compiler-sfc@3.4.38': resolution: {integrity: sha512-s5QfZ+9PzPh3T5H4hsQDJtI8x7zdJaew/dCGgqZ2630XdzaZ3AD8xGZfBqpT8oaD/p2eedd+pL8tD5vvt5ZYJQ==} + '@vue/compiler-sfc@3.5.3': + resolution: {integrity: sha512-P3uATLny2tfyvMB04OQFe7Sczteno7SLFxwrOA/dw01pBWQHB5HL15a8PosoNX2aG/EAMGqnXTu+1LnmzFhpTQ==} + '@vue/compiler-ssr@3.4.38': resolution: {integrity: sha512-YXznKFQ8dxYpAz9zLuVvfcXhc31FSPFDcqr0kyujbOwNhlmaNvL2QfIy+RZeJgSn5Fk54CWoEUeW+NVBAogGaw==} + '@vue/compiler-ssr@3.5.3': + resolution: {integrity: sha512-F/5f+r2WzL/2YAPl7UlKcJWHrvoZN8XwEBLnT7S4BXwncH25iDOabhO2M2DWioyTguJAGavDOawejkFXj8EM1w==} + '@vue/devtools-api@7.3.9': resolution: {integrity: sha512-D+GTYtFg68bqSu66EugQUydsOqaDlPLNmYw5oYk8k81uBu9/bVTUrqlAJrAA9Am7MXhKz2gWdDkopY6sOBf/Bg==} @@ -2009,6 +2147,9 @@ packages: '@vue/shared@3.4.38': resolution: {integrity: sha512-q0xCiLkuWWQLzVrecPb0RMsNWyxICOjPrcrwxTUEHb1fsnvni4dcuyG7RT/Ie7VPTvnjzIaWzRMUBsrqNj/hhw==} + '@vue/shared@3.5.3': + resolution: {integrity: sha512-Jp2v8nylKBT+PlOUjun2Wp/f++TfJVFjshLzNtJDdmFJabJa7noGMncqXRM1vXGX+Yo2V7WykQFNxusSim8SCA==} + '@vueuse/core@11.0.3': resolution: {integrity: sha512-RENlh64+SYA9XMExmmH1a3TPqeIuJBNNB/63GT35MZI+zpru3oMRUA6cEFr9HmGqEgUisurwGwnIieF6qu3aXw==} @@ -2234,6 +2375,13 @@ packages: capnp-ts@0.7.0: resolution: {integrity: sha512-XKxXAC3HVPv7r674zP0VC3RTXz+/JKhfyw94ljvF80yynK6VkTnqE3jMuN8b3dUVmmc43TjyxjW4KTsmB3c86g==} + cbor-extract@2.2.0: + resolution: {integrity: sha512-Ig1zM66BjLfTXpNgKpvBePq271BPOvu8MR0Jl080yG7Jsl+wAZunfrwiwA+9ruzm/WEdIV5QF/bjDZTqyAIVHA==} + hasBin: true + + cbor-x@1.6.0: + resolution: {integrity: sha512-0kareyRwHSkL6ws5VXHEf8uY1liitysCVJjlmhaLG+IXLqhSaOO+t63coaso7yjwEzWZzLy8fJo06gZDVQM9Qg==} + chai@5.1.1: resolution: {integrity: sha512-pT1ZgP8rPNqUgieVaEY+ryQr6Q4HXNg8Ei9UnLUrjN4IA7dvQC5JB+/kxVcPNDHyBcc/26CXPkbNzq3qwrOEKA==} engines: {node: '>=12'} @@ -2380,6 +2528,9 @@ packages: core-js-compat@3.38.1: resolution: {integrity: sha512-JRH6gfXxGmrzF3tZ57lFx97YARxCXPaMzPo6jELZhv88pBH5VXpQ+y0znKGlFnzuaihqhLbefxSJxWJMPtfDzw==} + core-util-is@1.0.3: + resolution: {integrity: sha512-ZQBvi1DcpJ4GDqanjucZ2Hj3wEO5pZDS89BWbkcrvdxksJorwUDDZamX9ldFkp9aw2lmBDLgkObEA4DWNJ9FYQ==} + cross-spawn@7.0.3: resolution: {integrity: sha512-iRDPJKUPVEND7dHPO8rkbOnPpyDygcDFtWjpeWNCgy8WP2rXcxXL8TskReQl6OrB2G7+UJrags1q15Fudc7G6w==} engines: {node: '>= 8'} @@ -2519,6 +2670,10 @@ packages: engines: {node: '>=0.10'} hasBin: true + detect-libc@2.0.3: + resolution: {integrity: sha512-bwy0MGW55bG41VqxxypOsdSdGqLwXPI/focwgTYCFMbdUiBAxLg9CFzG08sz2aqzknwiX7Hkl0bQENjg8iLByw==} + engines: {node: '>=8'} + dir-glob@3.0.1: resolution: {integrity: sha512-WkrWp9GR4KXfKGYzOLmTuGVi1UWFfws377n9cc55/tb6DuqyF6pcQ5AbiHEshaDpY9v6oaSr2XCDidGmMwdzIA==} engines: {node: '>=8'} @@ -2769,8 +2924,8 @@ packages: resolution: {integrity: sha512-OtIRv/2GyiF6o/d8K7MYKKbXrOUBIK6SfkIRM4Z0dY3w+LiQ0vy3F57m0Z71bjbyeiWFiHJ8brqnmE6H6/jEuw==} engines: {node: ^18.18.0 || ^20.9.0 || >=21.1.0} - eslint@9.10.0: - resolution: {integrity: sha512-Y4D0IgtBZfOcOUAIQTSXBKoNGfY0REGqHJG6+Q81vNippW5YlKjHFj4soMxamKK1NXHUWuBZTLdU3Km+L/pcHw==} + eslint@9.9.1: + resolution: {integrity: sha512-dHvhrbfr4xFQ9/dq+jcVneZMyRYLjggWjk6RVsIiHsP8Rz6yZ8LvZ//iU4TrZF+SXWG+JkNF2OyiZRvzgRDqMg==} engines: {node: ^18.18.0 || ^20.9.0 || >=21.1.0} hasBin: true peerDependencies: @@ -2970,7 +3125,6 @@ packages: glob@8.1.0: resolution: {integrity: sha512-r8hpEjiQEYlF2QU0df3dS+nxxSIreXQS1qRhMJM0Q5NDdR386C7jb7Hwwod8Fgiuex+k0GFjgft18yvxm5XoCQ==} engines: {node: '>=12'} - deprecated: Glob versions prior to v9 are no longer supported globals@11.12.0: resolution: {integrity: sha512-WOBp/EEGUiIsJSp7wcv/y6MO+lV9UoncWqxuFfm8eBwzWNgyfBd6Gz+IeKQ9jCmyhoH99g15M3T+QaVHFjizVA==} @@ -3071,6 +3225,9 @@ packages: resolution: {integrity: sha512-hsBTNUqQTDwkWtcdYI2i06Y/nUBEsNEDJKjWdigLvegy8kDuJAS8uRlpkkcQpyEXL0Z/pjDy5HBmMjRCJ2gq+g==} engines: {node: '>= 4'} + immediate@3.0.6: + resolution: {integrity: sha512-XXOFtyqDjNDAQxVfYxuF7g9Il/IbWmmlQg2MYKOH8ExIT1qg6xc4zyS3HaEEATgs1btfzxq15ciUiY7gjSXRGQ==} + import-fresh@3.3.0: resolution: {integrity: sha512-veYYhQa+D1QBKznvhUHxb8faxlrwUnxseDAbAp457E0wLNio2bOSKnjYDhMj+YiAq61xrMGhQk9iXVk5FzgQMw==} engines: {node: '>=6'} @@ -3088,7 +3245,6 @@ packages: inflight@1.0.6: resolution: {integrity: sha512-k92I/b08q4wvFscXCLvqfsHCrjrF7yiXsQuIVvVE7N82W3+aqpzuUdBbfhWcy/FZR3/4IgflMgKLOsvPDrGCJA==} - deprecated: This module is not supported, and leaks memory. Do not use it. Check out lru-cache if you want a good and tested way to coalesce async requests by a key value, which is much more comprehensive and powerful. inherits@2.0.4: resolution: {integrity: sha512-k/vGaX4/Yla3WzyMCvTQOXYeIHvqOKtnqBduzTHpzpQZzAskKMhZ2K+EnBiSM9zGSoIFeMpXKxa4dYeZIQqewQ==} @@ -3190,6 +3346,9 @@ packages: resolution: {integrity: sha512-jv+8jaWCl0g2lSBkNSVXdzfBA0npK1HGC2KtWM9FumFRoGS94g3NbCCLVnCYHLjp4GrW2KZeeSTMo5ddtznmGw==} engines: {node: '>=18'} + isarray@1.0.0: + resolution: {integrity: sha512-VLghIWNM6ELQzo7zwmcg0NmTVyWKYjvIeM83yjp0wRDTmUnrM678fQbcKBo6n2CJEF0szoG//ytg+TKla89ALQ==} + isexe@2.0.0: resolution: {integrity: sha512-RHxMLp9lnKHGHRng9QFhRCMbYAcVpn69smSGcq3f36xjgVVWThj4qqLbTLlq7Ssj8B+fIQ1EuCEGI2lKsyQeIw==} @@ -3289,6 +3448,9 @@ packages: resolution: {integrity: sha512-WYDyuc/uFcGp6YtM2H0uKmUwieOuzeE/5YocFJLnLfclZ4inf3mRn8ZVy1s7Hxji7Jxm6Ss8gqpexD/GlKoGgg==} engines: {node: ^12.22.0 || ^14.17.0 || >=16.0.0} + jszip@3.10.1: + resolution: {integrity: sha512-xXDvecyTpGLrqFrvkrUSoxxfJI5AH7U8zxxtVclpsUtMCq4JQ290LY8AW5c7Ggnr/Y/oK+bQMbqK2qmtk3pN4g==} + keyv@4.5.4: resolution: {integrity: sha512-oxVHkHR/EJf2CNXnWxRLW6mg7JyCCUcG0DtEGmL2ctUo1PNTin1PUil+r/+4r5MpVgC/fn1kjsx7mjSujKqIpw==} @@ -3299,6 +3461,9 @@ packages: resolution: {integrity: sha512-+bT2uH4E5LGE7h/n3evcS/sQlJXCpIp6ym8OWJ5eV6+67Dsql/LaaT7qJBAt2rzfoa/5QBGBhxDix1dMt2kQKQ==} engines: {node: '>= 0.8.0'} + lie@3.3.0: + resolution: {integrity: sha512-UaiMJzeWRlEujzAuw5LokY1L5ecNQYZKfmyZ9L7wDHb/p5etKaxXhohBcrw0EYby+G/NA52vRSN4N39dxHAIwQ==} + lilconfig@3.1.2: resolution: {integrity: sha512-eop+wDAvpItUys0FWkHIKeC9ybYrTGbU41U5K7+bttZZeohvnY7M9dZ5kB21GNWiFT2q1OoPTvncPCgSOVO5ow==} engines: {node: '>=14'} @@ -3490,6 +3655,13 @@ packages: ms@2.1.3: resolution: {integrity: sha512-6FlzubTLZG3J2a/NVCAleEhjzq5oxgHyaCU9yYXvcLsvoVaHJq/s5xXI6/XXP6tz7R9xAOtHnSO/tXtF3WRTlA==} + msgpackr-extract@3.0.3: + resolution: {integrity: sha512-P0efT1C9jIdVRefqjzOQ9Xml57zpOXnIuS+csaB4MdZbTdmGDLo8XhzBG1N7aO11gKDDkJvBLULeFTo46wwreA==} + hasBin: true + + msgpackr@1.11.0: + resolution: {integrity: sha512-I8qXuuALqJe5laEBYoFykChhSXLikZmUhccjGsPuSJ/7uPip2TJ7lwdIQwWSAi0jGZDXv4WOP8Qg65QZRuXxXw==} + mustache@4.2.0: resolution: {integrity: sha512-71ippSywq5Yb7/tVYyGbkBggbU8H3u5Rz56fH60jGFgr8uHwxs+aSKeqmluIVzM0m0kB7xQjKS6qPfd0b2ZoqQ==} hasBin: true @@ -3523,6 +3695,14 @@ packages: resolution: {integrity: sha512-dPEtOeMvF9VMcYV/1Wb8CPoVAXtp6MKMlcbAt4ddqmGqUJ6fQZFXkNZNkNlfevtNkGtaSoXf/vNNNSvgrdXwtA==} engines: {node: '>= 6.13.0'} + node-gyp-build-optional-packages@5.1.1: + resolution: {integrity: sha512-+P72GAjVAbTxjjwUmwjVrqrdZROD4nf8KgpBoDxqXXTiYZZt/ud60dE5yvCSr9lRO8e8yv6kgJIC0K0PfZFVQw==} + hasBin: true + + node-gyp-build-optional-packages@5.2.2: + resolution: {integrity: sha512-s+w+rBWnpTMwSFbaE0UXsRlg7hU4FjekKU4eyAih5T8nJuNZT1nNsskXpxmeqSK9UzkBl6UgRlnKc8hz8IEqOw==} + hasBin: true + node-releases@2.0.18: resolution: {integrity: sha512-d9VeXT4SJ7ZeOqGX6R5EM022wpL+eWPooLI+5UpWn2jCT1aosUQEhQP214x33Wkwx3JQMvIm+tIoVOdodFS40g==} @@ -3606,6 +3786,9 @@ packages: package-manager-detector@0.2.0: resolution: {integrity: sha512-E385OSk9qDcXhcM9LNSe4sdhx8a9mAPrZ4sMLW+tmxl5ZuGtPUcdFu+MPP2jbgiWAZ6Pfe5soGFMd+0Db5Vrog==} + pako@1.0.11: + resolution: {integrity: sha512-4hLB8Py4zZce5s4yd9XzopqwVv/yGNhV1Bl8NTmCq1763HeK2+EwVTv+leGeL13Dnh2wfbqowVPXCIO0z4taYw==} + parent-module@1.0.1: resolution: {integrity: sha512-GQ2EWRpQV8/o+Aw8YqtfZZPfNRWZYkbidE9k5rpl/hC3vtHHBfGm2Ifi6qWV+coDGkrUKZAxE3Lot5kcsRlh+g==} engines: {node: '>=6'} @@ -3878,6 +4061,10 @@ packages: resolution: {integrity: sha512-Aweb9unOEpQ3ezu4Q00DPvvM2ZTUitJdNKeP/+uQgr1IBIqu574IaZoURId7BKtWMREwzKa9OgzPzezWGPWFQw==} engines: {node: ^10 || ^12 || >=14} + postcss@8.4.45: + resolution: {integrity: sha512-7KTLTdzdZZYscUc65XmjFiB73vBhBfbPztCYdUNvlaso9PrzjzcmjqBPR0lNGkcVlcO4BjiO5rK/qNz+XAen1Q==} + engines: {node: ^10 || ^12 || >=14} + preact@10.23.2: resolution: {integrity: sha512-kKYfePf9rzKnxOAKDpsWhg/ysrHPqT+yQ7UW4JjdnqjFIeNUnNcEJvhuA8fDenxAGWzUqtd51DfVg7xp/8T9NA==} @@ -3892,6 +4079,9 @@ packages: printable-characters@1.0.42: resolution: {integrity: sha512-dKp+C4iXWK4vVYZmYSd0KBH5F/h1HoZRsbJ82AVKRO3PEo8L4lBS/vLwhVtpwwuYcoIsVY+1JYKR268yn480uQ==} + process-nextick-args@2.0.1: + resolution: {integrity: sha512-3ouUOpQhtgrbOa17J7+uxOTpITYWaGP7/AhoR3+A+/1e9skrzelGi/dXzEYyvbxubEF6Wn2ypscTKiKJFFn1ag==} + process-warning@4.0.0: resolution: {integrity: sha512-/MyYDxttz7DfGMMHiysAsFE4qF+pQYAA8ziO/3NcRVrQ5fSk+Mns4QZA/oRPFzvcqNoVJXQNWNAsdwBXLUkQKw==} @@ -3929,6 +4119,9 @@ packages: resolution: {integrity: sha512-Ug69mNOpfvKDAc2Q8DRpMjjzdtrnv9HcSMX+4VsZxD1aZ6ZzrIE7rlzXBtWTyhULSMKg076AW6WR5iZpD0JiOg==} engines: {node: '>=8'} + readable-stream@2.3.8: + resolution: {integrity: sha512-8p0AUk4XODgIewSi0l8Epjs+EVnWiK7NoDIEGU0HhE7+ZyY8D1IMY7odu5lRrFXGg71L15KG8QrPmum45RTtdA==} + readable-stream@4.5.2: resolution: {integrity: sha512-yjavECdqeZ3GLXNgRXgeQEdz9fvDDkNKyHnbHRFtOr7/LcfgBcmct7t/ET+HaCTqfh06OzoAxrkN/IfjJBVe+g==} engines: {node: ^12.22.0 || ^14.17.0 || >=16.0.0} @@ -4003,7 +4196,6 @@ packages: rollup-plugin-inject@3.0.2: resolution: {integrity: sha512-ptg9PQwzs3orn4jkgXJ74bfs5vYz1NCZlSQMBUA0wKcGp5i5pA1AO3fOUEte8enhGUC+iapTCzEWw2jEFFUO/w==} - deprecated: This package has been deprecated and is no longer maintained. Please use @rollup/plugin-inject. rollup-plugin-node-polyfills@0.2.1: resolution: {integrity: sha512-4kCrKPTJ6sK4/gLL/U5QzVT8cxJcofO0OU74tnB19F40cmuAKSzH5/siithxlofFEjwvw1YAhPmbvGNA6jEroA==} @@ -4030,6 +4222,9 @@ packages: run-parallel@1.2.0: resolution: {integrity: sha512-5l4VyZR86LZ/lDxZTR6jqL8AFE2S0IFLMP26AbjsLVADxHdhB/c0GUsH+y39UfCi3dzz8OlQuPmnaJOMoDHQBA==} + safe-buffer@5.1.2: + resolution: {integrity: sha512-Gd2UZBJDkXlY7GbJxfsE8/nvKkUEU1G38c1siN6QP6a9PT9MmHB8GnpscSmMJSoF8LOIrt8ud/wPtojys4G6+g==} + safe-buffer@5.2.1: resolution: {integrity: sha512-rp3So07KcdmmKbGvgaNxQSJr7bGVSVk5S9Eq1F+ppbRo70+YeaDxkw5Dd8NPN+GD6bjnYm2VuPuCXmpuYvmCXQ==} @@ -4051,8 +4246,8 @@ packages: scule@1.3.0: resolution: {integrity: sha512-6FtHJEvt+pVMIB9IBY+IcCJ6Z5f1iQnytgyfKMhDKgmzYG+TeH/wx1y3l27rshSbLiSanrR9ffZDrEsmjlQF2g==} - search-insights@2.17.0: - resolution: {integrity: sha512-AskayU3QNsXQzSL6v4LTYST7NNfs2HWyHHB+sdORP9chsytAhro5XRfToAMI/LAVYgNbzowVZTMfBRodgbUHKg==} + search-insights@2.17.1: + resolution: {integrity: sha512-HHFjYH/0AqXacETlIbe9EYc3UNlQYGNNTY0fZ/sWl6SweX+GDxq9NB5+RVoPLgEFuOtCz7M9dhYxqDnhbbF0eQ==} secure-json-parse@2.7.0: resolution: {integrity: sha512-6aU+Rwsezw7VR8/nyvKTx8QpWH9FrcYiXXlqC4z5d5XQBDRqtbfsRjnwGyqbi3gddNtWHuEk9OANUotL26qKUw==} @@ -4084,6 +4279,9 @@ packages: resolution: {integrity: sha512-rqEO6FZk8mv7Hyv4UCj3FD3b6Waqft605TLfsCe/BiaylRpyyMC0b+uA5TJKawX3KzMrdi3wsLbCaLplrQmBvQ==} engines: {node: '>=10'} + setimmediate@1.0.5: + resolution: {integrity: sha512-MATJdZp8sLqDl/68LfQmbP8zKPLQNV6BIZoIgrscFDQ+RsvK/BxeDQOgyxKKoh0y/8h3BqVFnCqQ/gd+reiIXA==} + shebang-command@2.0.0: resolution: {integrity: sha512-kHxr2zZpYtdmrN1qDjrrX/Z1rR1kG8Dx+gkpK1G4eXmvXswmcE1hTWBWYUzlraYw1/yZp6YuDY77YtvbN0dmDA==} engines: {node: '>=8'} @@ -4153,7 +4351,6 @@ packages: sourcemap-codec@1.4.8: resolution: {integrity: sha512-9NykojV5Uih4lgo5So5dtw+f0JgJX30KCNI8gwhz2J9A15wD0Ml6tjHKwf6fTSa6fAdVBdZeNOs9eJ71qCk8vA==} - deprecated: Please use @jridgewell/sourcemap-codec instead spdx-correct@3.2.0: resolution: {integrity: sha512-kN9dJbvnySHULIluDHy32WHRUu3Og7B9sbY7tsFLctQkIqnMh3hErYgdMjTYuqmcXX+lK5T1lnUt3G7zNswmZA==} @@ -4210,6 +4407,9 @@ packages: resolution: {integrity: sha512-tsaTIkKW9b4N+AEj+SVA+WhJzV7/zMhcSu78mLKWSk7cXMOSHsBKFWUs0fWwq8QyK3MgJBQRX6Gbi4kYbdvGkQ==} engines: {node: '>=18'} + string_decoder@1.1.1: + resolution: {integrity: sha512-n/ShnvDi6FHbbVfviro+WojiFzv+s8MPMHBczVePfUpDJLwoLT0ht1l4YwBCbi8pJAveEEdnkHyPyTP/mzRfwg==} + string_decoder@1.3.0: resolution: {integrity: sha512-hkRX8U1WjJFd8LsDJ2yQ/wWWxaopEsABU1XfkM8A+j0+85JAGppt16cr1Whg6KIbb4okU6Mql6BOj+uup/wKeA==} @@ -4459,19 +4659,19 @@ packages: vite: optional: true - unstorage@1.12.0: - resolution: {integrity: sha512-ARZYTXiC+e8z3lRM7/qY9oyaOkaozCeNd2xoz7sYK9fv7OLGhVsf+BZbmASqiK/HTZ7T6eAlnVq9JynZppyk3w==} + unstorage@1.11.1: + resolution: {integrity: sha512-3NVszU4MGlO21WWnkSq0xnPVMHnMyB5DdJQyGRAg/DUZVeQjWRinLOia89iw5KGpllRtoA5+N+xnq75MAsPAOA==} peerDependencies: - '@azure/app-configuration': ^1.7.0 - '@azure/cosmos': ^4.1.1 + '@azure/app-configuration': ^1.6.0 + '@azure/cosmos': ^4.0.0 '@azure/data-tables': ^13.2.2 - '@azure/identity': ^4.4.1 + '@azure/identity': ^4.2.0 '@azure/keyvault-secrets': ^4.8.0 - '@azure/storage-blob': ^12.24.0 - '@capacitor/preferences': ^6.0.2 + '@azure/storage-blob': ^12.18.0 + '@capacitor/preferences': ^6.0.0 '@netlify/blobs': ^6.5.0 || ^7.0.0 - '@planetscale/database': ^1.19.0 - '@upstash/redis': ^1.34.0 + '@planetscale/database': ^1.18.0 + '@upstash/redis': ^1.31.3 '@vercel/kv': ^1.0.1 idb-keyval: ^6.2.1 ioredis: ^5.4.1 @@ -4697,8 +4897,8 @@ packages: engines: {node: '>=16'} hasBin: true - wrangler@3.75.0: - resolution: {integrity: sha512-CitNuNj0O1z6qbonUXmpUbxeWpU3nx28Kc4ZT33tMdeooQssb063Ie7+ZCdfS3kPhRHSwGdtOV22xFYytHON8w==} + wrangler@3.74.0: + resolution: {integrity: sha512-wmtb+tQrgb61yN+Wa2JM98G1Gt4tKFRYPw6xwuyzUcA74L+Dum1A13w22/manl9Gq1jA3dPn+7UzT5sYEVHRog==} engines: {node: '>=16.17.0'} hasBin: true peerDependencies: @@ -4793,32 +4993,32 @@ packages: snapshots: - '@algolia/autocomplete-core@1.9.3(@algolia/client-search@4.24.0)(algoliasearch@4.24.0)(search-insights@2.17.0)': + '@algolia/autocomplete-core@1.9.3(@algolia/client-search@5.3.0)(algoliasearch@4.24.0)(search-insights@2.17.1)': dependencies: - '@algolia/autocomplete-plugin-algolia-insights': 1.9.3(@algolia/client-search@4.24.0)(algoliasearch@4.24.0)(search-insights@2.17.0) - '@algolia/autocomplete-shared': 1.9.3(@algolia/client-search@4.24.0)(algoliasearch@4.24.0) + '@algolia/autocomplete-plugin-algolia-insights': 1.9.3(@algolia/client-search@5.3.0)(algoliasearch@4.24.0)(search-insights@2.17.1) + '@algolia/autocomplete-shared': 1.9.3(@algolia/client-search@5.3.0)(algoliasearch@4.24.0) transitivePeerDependencies: - '@algolia/client-search' - algoliasearch - search-insights - '@algolia/autocomplete-plugin-algolia-insights@1.9.3(@algolia/client-search@4.24.0)(algoliasearch@4.24.0)(search-insights@2.17.0)': + '@algolia/autocomplete-plugin-algolia-insights@1.9.3(@algolia/client-search@5.3.0)(algoliasearch@4.24.0)(search-insights@2.17.1)': dependencies: - '@algolia/autocomplete-shared': 1.9.3(@algolia/client-search@4.24.0)(algoliasearch@4.24.0) - search-insights: 2.17.0 + '@algolia/autocomplete-shared': 1.9.3(@algolia/client-search@5.3.0)(algoliasearch@4.24.0) + search-insights: 2.17.1 transitivePeerDependencies: - '@algolia/client-search' - algoliasearch - '@algolia/autocomplete-preset-algolia@1.9.3(@algolia/client-search@4.24.0)(algoliasearch@4.24.0)': + '@algolia/autocomplete-preset-algolia@1.9.3(@algolia/client-search@5.3.0)(algoliasearch@4.24.0)': dependencies: - '@algolia/autocomplete-shared': 1.9.3(@algolia/client-search@4.24.0)(algoliasearch@4.24.0) - '@algolia/client-search': 4.24.0 + '@algolia/autocomplete-shared': 1.9.3(@algolia/client-search@5.3.0)(algoliasearch@4.24.0) + '@algolia/client-search': 5.3.0 algoliasearch: 4.24.0 - '@algolia/autocomplete-shared@1.9.3(@algolia/client-search@4.24.0)(algoliasearch@4.24.0)': + '@algolia/autocomplete-shared@1.9.3(@algolia/client-search@5.3.0)(algoliasearch@4.24.0)': dependencies: - '@algolia/client-search': 4.24.0 + '@algolia/client-search': 5.3.0 algoliasearch: 4.24.0 '@algolia/cache-browser-local-storage@4.24.0': @@ -4849,6 +5049,8 @@ snapshots: '@algolia/requester-common': 4.24.0 '@algolia/transporter': 4.24.0 + '@algolia/client-common@5.3.0': {} + '@algolia/client-personalization@4.24.0': dependencies: '@algolia/client-common': 4.24.0 @@ -4861,6 +5063,12 @@ snapshots: '@algolia/requester-common': 4.24.0 '@algolia/transporter': 4.24.0 + '@algolia/client-search@5.3.0': + dependencies: + '@algolia/client-common': 5.3.0 + '@algolia/requester-browser-xhr': 5.3.0 + '@algolia/requester-node-http': 5.3.0 + '@algolia/logger-common@4.24.0': {} '@algolia/logger-console@4.24.0': @@ -4885,12 +5093,20 @@ snapshots: dependencies: '@algolia/requester-common': 4.24.0 + '@algolia/requester-browser-xhr@5.3.0': + dependencies: + '@algolia/client-common': 5.3.0 + '@algolia/requester-common@4.24.0': {} '@algolia/requester-node-http@4.24.0': dependencies: '@algolia/requester-common': 4.24.0 + '@algolia/requester-node-http@5.3.0': + dependencies: + '@algolia/client-common': 5.3.0 + '@algolia/transporter@4.24.0': dependencies: '@algolia/cache-common': 4.24.0 @@ -4902,42 +5118,42 @@ snapshots: '@jridgewell/gen-mapping': 0.3.5 '@jridgewell/trace-mapping': 0.3.25 - '@antfu/eslint-config@2.27.3(@typescript-eslint/utils@8.3.0(eslint@9.10.0(jiti@1.21.6))(typescript@5.5.4))(@vue/compiler-sfc@3.4.38)(eslint@9.10.0(jiti@1.21.6))(typescript@5.5.4)(vitest@2.0.5(@types/node@22.5.4)(jsdom@25.0.0)(terser@5.31.6))': + '@antfu/eslint-config@2.27.3(@typescript-eslint/utils@8.4.0(eslint@9.9.1(jiti@1.21.6))(typescript@5.5.4))(@vue/compiler-sfc@3.5.3)(eslint@9.9.1(jiti@1.21.6))(typescript@5.5.4)(vitest@2.0.5(@types/node@22.5.4)(jsdom@25.0.0)(terser@5.31.6))': dependencies: '@antfu/install-pkg': 0.4.1 '@clack/prompts': 0.7.0 - '@eslint-community/eslint-plugin-eslint-comments': 4.4.0(eslint@9.10.0(jiti@1.21.6)) - '@stylistic/eslint-plugin': 2.6.4(eslint@9.10.0(jiti@1.21.6))(typescript@5.5.4) - '@typescript-eslint/eslint-plugin': 8.3.0(@typescript-eslint/parser@8.3.0(eslint@9.10.0(jiti@1.21.6))(typescript@5.5.4))(eslint@9.10.0(jiti@1.21.6))(typescript@5.5.4) - '@typescript-eslint/parser': 8.3.0(eslint@9.10.0(jiti@1.21.6))(typescript@5.5.4) - '@vitest/eslint-plugin': 1.0.5(@typescript-eslint/utils@8.3.0(eslint@9.10.0(jiti@1.21.6))(typescript@5.5.4))(eslint@9.10.0(jiti@1.21.6))(typescript@5.5.4)(vitest@2.0.5(@types/node@22.5.4)(jsdom@25.0.0)(terser@5.31.6)) - eslint: 9.10.0(jiti@1.21.6) + '@eslint-community/eslint-plugin-eslint-comments': 4.4.0(eslint@9.9.1(jiti@1.21.6)) + '@stylistic/eslint-plugin': 2.6.4(eslint@9.9.1(jiti@1.21.6))(typescript@5.5.4) + '@typescript-eslint/eslint-plugin': 8.3.0(@typescript-eslint/parser@8.3.0(eslint@9.9.1(jiti@1.21.6))(typescript@5.5.4))(eslint@9.9.1(jiti@1.21.6))(typescript@5.5.4) + '@typescript-eslint/parser': 8.3.0(eslint@9.9.1(jiti@1.21.6))(typescript@5.5.4) + '@vitest/eslint-plugin': 1.0.5(@typescript-eslint/utils@8.4.0(eslint@9.9.1(jiti@1.21.6))(typescript@5.5.4))(eslint@9.9.1(jiti@1.21.6))(typescript@5.5.4)(vitest@2.0.5(@types/node@22.5.4)(jsdom@25.0.0)(terser@5.31.6)) + eslint: 9.9.1(jiti@1.21.6) eslint-config-flat-gitignore: 0.1.8 eslint-flat-config-utils: 0.3.1 - eslint-merge-processors: 0.1.0(eslint@9.10.0(jiti@1.21.6)) - eslint-plugin-antfu: 2.3.6(eslint@9.10.0(jiti@1.21.6)) - eslint-plugin-command: 0.2.3(eslint@9.10.0(jiti@1.21.6)) - eslint-plugin-import-x: 4.1.0(eslint@9.10.0(jiti@1.21.6))(typescript@5.5.4) - eslint-plugin-jsdoc: 50.2.2(eslint@9.10.0(jiti@1.21.6)) - eslint-plugin-jsonc: 2.16.0(eslint@9.10.0(jiti@1.21.6)) - eslint-plugin-markdown: 5.1.0(eslint@9.10.0(jiti@1.21.6)) - eslint-plugin-n: 17.10.2(eslint@9.10.0(jiti@1.21.6)) + eslint-merge-processors: 0.1.0(eslint@9.9.1(jiti@1.21.6)) + eslint-plugin-antfu: 2.3.6(eslint@9.9.1(jiti@1.21.6)) + eslint-plugin-command: 0.2.3(eslint@9.9.1(jiti@1.21.6)) + eslint-plugin-import-x: 4.1.0(eslint@9.9.1(jiti@1.21.6))(typescript@5.5.4) + eslint-plugin-jsdoc: 50.2.2(eslint@9.9.1(jiti@1.21.6)) + eslint-plugin-jsonc: 2.16.0(eslint@9.9.1(jiti@1.21.6)) + eslint-plugin-markdown: 5.1.0(eslint@9.9.1(jiti@1.21.6)) + eslint-plugin-n: 17.10.2(eslint@9.9.1(jiti@1.21.6)) eslint-plugin-no-only-tests: 3.3.0 - eslint-plugin-perfectionist: 3.3.0(eslint@9.10.0(jiti@1.21.6))(typescript@5.5.4)(vue-eslint-parser@9.4.3(eslint@9.10.0(jiti@1.21.6))) - eslint-plugin-regexp: 2.6.0(eslint@9.10.0(jiti@1.21.6)) - eslint-plugin-toml: 0.11.1(eslint@9.10.0(jiti@1.21.6)) - eslint-plugin-unicorn: 55.0.0(eslint@9.10.0(jiti@1.21.6)) - eslint-plugin-unused-imports: 4.1.3(@typescript-eslint/eslint-plugin@8.3.0(@typescript-eslint/parser@8.3.0(eslint@9.10.0(jiti@1.21.6))(typescript@5.5.4))(eslint@9.10.0(jiti@1.21.6))(typescript@5.5.4))(eslint@9.10.0(jiti@1.21.6)) - eslint-plugin-vue: 9.27.0(eslint@9.10.0(jiti@1.21.6)) - eslint-plugin-yml: 1.14.0(eslint@9.10.0(jiti@1.21.6)) - eslint-processor-vue-blocks: 0.1.2(@vue/compiler-sfc@3.4.38)(eslint@9.10.0(jiti@1.21.6)) + eslint-plugin-perfectionist: 3.3.0(eslint@9.9.1(jiti@1.21.6))(typescript@5.5.4)(vue-eslint-parser@9.4.3(eslint@9.9.1(jiti@1.21.6))) + eslint-plugin-regexp: 2.6.0(eslint@9.9.1(jiti@1.21.6)) + eslint-plugin-toml: 0.11.1(eslint@9.9.1(jiti@1.21.6)) + eslint-plugin-unicorn: 55.0.0(eslint@9.9.1(jiti@1.21.6)) + eslint-plugin-unused-imports: 4.1.3(@typescript-eslint/eslint-plugin@8.3.0(@typescript-eslint/parser@8.3.0(eslint@9.9.1(jiti@1.21.6))(typescript@5.5.4))(eslint@9.9.1(jiti@1.21.6))(typescript@5.5.4))(eslint@9.9.1(jiti@1.21.6)) + eslint-plugin-vue: 9.27.0(eslint@9.9.1(jiti@1.21.6)) + eslint-plugin-yml: 1.14.0(eslint@9.9.1(jiti@1.21.6)) + eslint-processor-vue-blocks: 0.1.2(@vue/compiler-sfc@3.5.3)(eslint@9.9.1(jiti@1.21.6)) globals: 15.9.0 jsonc-eslint-parser: 2.4.0 local-pkg: 0.5.0 parse-gitignore: 2.0.0 picocolors: 1.0.1 toml-eslint-parser: 0.10.0 - vue-eslint-parser: 9.4.3(eslint@9.10.0(jiti@1.21.6)) + vue-eslint-parser: 9.4.3(eslint@9.9.1(jiti@1.21.6)) yaml-eslint-parser: 1.2.3 yargs: 17.7.2 transitivePeerDependencies: @@ -4975,7 +5191,7 @@ snapshots: '@babel/traverse': 7.25.4 '@babel/types': 7.25.4 convert-source-map: 2.0.0 - debug: 4.3.7 + debug: 4.3.6 gensync: 1.0.0-beta.2 json5: 2.2.3 semver: 6.3.1 @@ -5153,7 +5369,7 @@ snapshots: '@babel/parser': 7.25.4 '@babel/template': 7.25.0 '@babel/types': 7.25.4 - debug: 4.3.7 + debug: 4.3.6 globals: 11.12.0 transitivePeerDependencies: - supports-color @@ -5172,6 +5388,24 @@ snapshots: '@bcoe/v8-coverage@0.2.3': {} + '@cbor-extract/cbor-extract-darwin-arm64@2.2.0': + optional: true + + '@cbor-extract/cbor-extract-darwin-x64@2.2.0': + optional: true + + '@cbor-extract/cbor-extract-linux-arm64@2.2.0': + optional: true + + '@cbor-extract/cbor-extract-linux-arm@2.2.0': + optional: true + + '@cbor-extract/cbor-extract-linux-x64@2.2.0': + optional: true + + '@cbor-extract/cbor-extract-win32-x64@2.2.0': + optional: true + '@clack/core@0.3.4': dependencies: picocolors: 1.0.1 @@ -5206,6 +5440,14 @@ snapshots: '@cloudflare/workers-types@4.20240903.0': {} + '@corentinth/chisels@1.0.2': + dependencies: + lodash-es: 4.17.21 + + '@corentinth/chisels@1.0.4': + dependencies: + lodash-es: 4.17.21 + '@corvu/utils@0.4.1(solid-js@1.8.22)': dependencies: '@floating-ui/dom': 1.6.10 @@ -5217,9 +5459,9 @@ snapshots: '@docsearch/css@3.6.1': {} - '@docsearch/js@3.6.1(@algolia/client-search@4.24.0)(search-insights@2.17.0)': + '@docsearch/js@3.6.1(@algolia/client-search@5.3.0)(search-insights@2.17.1)': dependencies: - '@docsearch/react': 3.6.1(@algolia/client-search@4.24.0)(search-insights@2.17.0) + '@docsearch/react': 3.6.1(@algolia/client-search@5.3.0)(search-insights@2.17.1) preact: 10.23.2 transitivePeerDependencies: - '@algolia/client-search' @@ -5228,14 +5470,14 @@ snapshots: - react-dom - search-insights - '@docsearch/react@3.6.1(@algolia/client-search@4.24.0)(search-insights@2.17.0)': + '@docsearch/react@3.6.1(@algolia/client-search@5.3.0)(search-insights@2.17.1)': dependencies: - '@algolia/autocomplete-core': 1.9.3(@algolia/client-search@4.24.0)(algoliasearch@4.24.0)(search-insights@2.17.0) - '@algolia/autocomplete-preset-algolia': 1.9.3(@algolia/client-search@4.24.0)(algoliasearch@4.24.0) + '@algolia/autocomplete-core': 1.9.3(@algolia/client-search@5.3.0)(algoliasearch@4.24.0)(search-insights@2.17.1) + '@algolia/autocomplete-preset-algolia': 1.9.3(@algolia/client-search@5.3.0)(algoliasearch@4.24.0) '@docsearch/css': 3.6.1 algoliasearch: 4.24.0 optionalDependencies: - search-insights: 2.17.0 + search-insights: 2.17.1 transitivePeerDependencies: - '@algolia/client-search' @@ -5540,15 +5782,15 @@ snapshots: '@esbuild/win32-x64@0.23.1': optional: true - '@eslint-community/eslint-plugin-eslint-comments@4.4.0(eslint@9.10.0(jiti@1.21.6))': + '@eslint-community/eslint-plugin-eslint-comments@4.4.0(eslint@9.9.1(jiti@1.21.6))': dependencies: escape-string-regexp: 4.0.0 - eslint: 9.10.0(jiti@1.21.6) + eslint: 9.9.1(jiti@1.21.6) ignore: 5.3.2 - '@eslint-community/eslint-utils@4.4.0(eslint@9.10.0(jiti@1.21.6))': + '@eslint-community/eslint-utils@4.4.0(eslint@9.9.1(jiti@1.21.6))': dependencies: - eslint: 9.10.0(jiti@1.21.6) + eslint: 9.9.1(jiti@1.21.6) eslint-visitor-keys: 3.4.3 '@eslint-community/regexpp@4.11.0': {} @@ -5556,7 +5798,7 @@ snapshots: '@eslint/config-array@0.18.0': dependencies: '@eslint/object-schema': 2.1.4 - debug: 4.3.7 + debug: 4.3.6 minimatch: 3.1.2 transitivePeerDependencies: - supports-color @@ -5564,7 +5806,7 @@ snapshots: '@eslint/eslintrc@3.1.0': dependencies: ajv: 6.12.6 - debug: 4.3.7 + debug: 4.3.6 espree: 10.1.0 globals: 14.0.0 ignore: 5.3.2 @@ -5575,14 +5817,10 @@ snapshots: transitivePeerDependencies: - supports-color - '@eslint/js@9.10.0': {} + '@eslint/js@9.9.1': {} '@eslint/object-schema@2.1.4': {} - '@eslint/plugin-kit@0.1.0': - dependencies: - levn: 0.4.1 - '@fastify/busboy@2.1.1': {} '@floating-ui/core@1.6.7': @@ -5615,7 +5853,7 @@ snapshots: '@antfu/install-pkg': 0.4.1 '@antfu/utils': 0.7.10 '@iconify/types': 2.0.0 - debug: 4.3.7 + debug: 4.3.6 kolorist: 1.8.0 local-pkg: 0.5.0 mlly: 1.7.1 @@ -5640,7 +5878,7 @@ snapshots: '@inquirer/figures': 1.0.5 '@inquirer/type': 1.5.3 '@types/mute-stream': 0.0.4 - '@types/node': 22.5.4 + '@types/node': 22.5.2 '@types/wrap-ansi': 3.0.0 ansi-escapes: 4.3.2 cli-spinners: 2.9.2 @@ -5657,7 +5895,7 @@ snapshots: '@inquirer/type': 1.5.3 external-editor: 3.1.0 - '@inquirer/expand@2.3.0': + '@inquirer/expand@2.2.0': dependencies: '@inquirer/core': 9.1.0 '@inquirer/type': 1.5.3 @@ -5681,12 +5919,12 @@ snapshots: '@inquirer/type': 1.5.3 ansi-escapes: 4.3.2 - '@inquirer/prompts@5.5.0': + '@inquirer/prompts@5.4.0': dependencies: '@inquirer/checkbox': 2.5.0 '@inquirer/confirm': 3.2.0 '@inquirer/editor': 2.2.0 - '@inquirer/expand': 2.3.0 + '@inquirer/expand': 2.2.0 '@inquirer/input': 2.3.0 '@inquirer/number': 1.1.0 '@inquirer/password': 2.2.0 @@ -5789,6 +6027,24 @@ snapshots: '@solid-primitives/utils': 6.2.3(solid-js@1.8.22) solid-js: 1.8.22 + '@msgpackr-extract/msgpackr-extract-darwin-arm64@3.0.3': + optional: true + + '@msgpackr-extract/msgpackr-extract-darwin-x64@3.0.3': + optional: true + + '@msgpackr-extract/msgpackr-extract-linux-arm64@3.0.3': + optional: true + + '@msgpackr-extract/msgpackr-extract-linux-arm@3.0.3': + optional: true + + '@msgpackr-extract/msgpackr-extract-linux-x64@3.0.3': + optional: true + + '@msgpackr-extract/msgpackr-extract-win32-x64@3.0.3': + optional: true + '@nodelib/fs.scandir@2.1.5': dependencies: '@nodelib/fs.stat': 2.0.5 @@ -6048,47 +6304,47 @@ snapshots: dependencies: solid-js: 1.8.22 - '@stylistic/eslint-plugin-js@2.6.4(eslint@9.10.0(jiti@1.21.6))': + '@stylistic/eslint-plugin-js@2.6.4(eslint@9.9.1(jiti@1.21.6))': dependencies: '@types/eslint': 9.6.1 acorn: 8.12.1 - eslint: 9.10.0(jiti@1.21.6) + eslint: 9.9.1(jiti@1.21.6) eslint-visitor-keys: 4.0.0 espree: 10.1.0 - '@stylistic/eslint-plugin-jsx@2.6.4(eslint@9.10.0(jiti@1.21.6))': + '@stylistic/eslint-plugin-jsx@2.6.4(eslint@9.9.1(jiti@1.21.6))': dependencies: - '@stylistic/eslint-plugin-js': 2.6.4(eslint@9.10.0(jiti@1.21.6)) + '@stylistic/eslint-plugin-js': 2.6.4(eslint@9.9.1(jiti@1.21.6)) '@types/eslint': 9.6.1 - eslint: 9.10.0(jiti@1.21.6) + eslint: 9.9.1(jiti@1.21.6) eslint-visitor-keys: 4.0.0 espree: 10.1.0 estraverse: 5.3.0 picomatch: 4.0.2 - '@stylistic/eslint-plugin-plus@2.6.4(eslint@9.10.0(jiti@1.21.6))': + '@stylistic/eslint-plugin-plus@2.6.4(eslint@9.9.1(jiti@1.21.6))': dependencies: '@types/eslint': 9.6.1 - eslint: 9.10.0(jiti@1.21.6) + eslint: 9.9.1(jiti@1.21.6) - '@stylistic/eslint-plugin-ts@2.6.4(eslint@9.10.0(jiti@1.21.6))(typescript@5.5.4)': + '@stylistic/eslint-plugin-ts@2.6.4(eslint@9.9.1(jiti@1.21.6))(typescript@5.5.4)': dependencies: - '@stylistic/eslint-plugin-js': 2.6.4(eslint@9.10.0(jiti@1.21.6)) + '@stylistic/eslint-plugin-js': 2.6.4(eslint@9.9.1(jiti@1.21.6)) '@types/eslint': 9.6.1 - '@typescript-eslint/utils': 8.3.0(eslint@9.10.0(jiti@1.21.6))(typescript@5.5.4) - eslint: 9.10.0(jiti@1.21.6) + '@typescript-eslint/utils': 8.3.0(eslint@9.9.1(jiti@1.21.6))(typescript@5.5.4) + eslint: 9.9.1(jiti@1.21.6) transitivePeerDependencies: - supports-color - typescript - '@stylistic/eslint-plugin@2.6.4(eslint@9.10.0(jiti@1.21.6))(typescript@5.5.4)': + '@stylistic/eslint-plugin@2.6.4(eslint@9.9.1(jiti@1.21.6))(typescript@5.5.4)': dependencies: - '@stylistic/eslint-plugin-js': 2.6.4(eslint@9.10.0(jiti@1.21.6)) - '@stylistic/eslint-plugin-jsx': 2.6.4(eslint@9.10.0(jiti@1.21.6)) - '@stylistic/eslint-plugin-plus': 2.6.4(eslint@9.10.0(jiti@1.21.6)) - '@stylistic/eslint-plugin-ts': 2.6.4(eslint@9.10.0(jiti@1.21.6))(typescript@5.5.4) + '@stylistic/eslint-plugin-js': 2.6.4(eslint@9.9.1(jiti@1.21.6)) + '@stylistic/eslint-plugin-jsx': 2.6.4(eslint@9.9.1(jiti@1.21.6)) + '@stylistic/eslint-plugin-plus': 2.6.4(eslint@9.9.1(jiti@1.21.6)) + '@stylistic/eslint-plugin-ts': 2.6.4(eslint@9.9.1(jiti@1.21.6))(typescript@5.5.4) '@types/eslint': 9.6.1 - eslint: 9.10.0(jiti@1.21.6) + eslint: 9.9.1(jiti@1.21.6) transitivePeerDependencies: - supports-color - typescript @@ -6137,7 +6393,7 @@ snapshots: '@types/fs-extra@11.0.4': dependencies: '@types/jsonfile': 6.1.4 - '@types/node': 22.5.1 + '@types/node': 22.5.2 optional: true '@types/hast@3.0.4': @@ -6148,7 +6404,7 @@ snapshots: '@types/jsonfile@6.1.4': dependencies: - '@types/node': 22.5.1 + '@types/node': 22.5.2 optional: true '@types/linkify-it@5.0.0': {} @@ -6172,18 +6428,17 @@ snapshots: '@types/mute-stream@0.0.4': dependencies: - '@types/node': 22.5.4 + '@types/node': 22.5.2 '@types/node-cron@3.0.11': {} '@types/node-forge@1.3.11': dependencies: - '@types/node': 22.5.4 + '@types/node': 22.5.2 - '@types/node@22.5.1': + '@types/node@22.5.2': dependencies: undici-types: 6.19.8 - optional: true '@types/node@22.5.4': dependencies: @@ -6199,15 +6454,15 @@ snapshots: '@types/wrap-ansi@3.0.0': {} - '@typescript-eslint/eslint-plugin@8.3.0(@typescript-eslint/parser@8.3.0(eslint@9.10.0(jiti@1.21.6))(typescript@5.5.4))(eslint@9.10.0(jiti@1.21.6))(typescript@5.5.4)': + '@typescript-eslint/eslint-plugin@8.3.0(@typescript-eslint/parser@8.3.0(eslint@9.9.1(jiti@1.21.6))(typescript@5.5.4))(eslint@9.9.1(jiti@1.21.6))(typescript@5.5.4)': dependencies: '@eslint-community/regexpp': 4.11.0 - '@typescript-eslint/parser': 8.3.0(eslint@9.10.0(jiti@1.21.6))(typescript@5.5.4) + '@typescript-eslint/parser': 8.3.0(eslint@9.9.1(jiti@1.21.6))(typescript@5.5.4) '@typescript-eslint/scope-manager': 8.3.0 - '@typescript-eslint/type-utils': 8.3.0(eslint@9.10.0(jiti@1.21.6))(typescript@5.5.4) - '@typescript-eslint/utils': 8.3.0(eslint@9.10.0(jiti@1.21.6))(typescript@5.5.4) + '@typescript-eslint/type-utils': 8.3.0(eslint@9.9.1(jiti@1.21.6))(typescript@5.5.4) + '@typescript-eslint/utils': 8.3.0(eslint@9.9.1(jiti@1.21.6))(typescript@5.5.4) '@typescript-eslint/visitor-keys': 8.3.0 - eslint: 9.10.0(jiti@1.21.6) + eslint: 9.9.1(jiti@1.21.6) graphemer: 1.4.0 ignore: 5.3.2 natural-compare: 1.4.0 @@ -6217,14 +6472,14 @@ snapshots: transitivePeerDependencies: - supports-color - '@typescript-eslint/parser@8.3.0(eslint@9.10.0(jiti@1.21.6))(typescript@5.5.4)': + '@typescript-eslint/parser@8.3.0(eslint@9.9.1(jiti@1.21.6))(typescript@5.5.4)': dependencies: '@typescript-eslint/scope-manager': 8.3.0 '@typescript-eslint/types': 8.3.0 '@typescript-eslint/typescript-estree': 8.3.0(typescript@5.5.4) '@typescript-eslint/visitor-keys': 8.3.0 - debug: 4.3.7 - eslint: 9.10.0(jiti@1.21.6) + debug: 4.3.6 + eslint: 9.9.1(jiti@1.21.6) optionalDependencies: typescript: 5.5.4 transitivePeerDependencies: @@ -6235,11 +6490,17 @@ snapshots: '@typescript-eslint/types': 8.3.0 '@typescript-eslint/visitor-keys': 8.3.0 - '@typescript-eslint/type-utils@8.3.0(eslint@9.10.0(jiti@1.21.6))(typescript@5.5.4)': + '@typescript-eslint/scope-manager@8.4.0': + dependencies: + '@typescript-eslint/types': 8.4.0 + '@typescript-eslint/visitor-keys': 8.4.0 + optional: true + + '@typescript-eslint/type-utils@8.3.0(eslint@9.9.1(jiti@1.21.6))(typescript@5.5.4)': dependencies: '@typescript-eslint/typescript-estree': 8.3.0(typescript@5.5.4) - '@typescript-eslint/utils': 8.3.0(eslint@9.10.0(jiti@1.21.6))(typescript@5.5.4) - debug: 4.3.7 + '@typescript-eslint/utils': 8.3.0(eslint@9.9.1(jiti@1.21.6))(typescript@5.5.4) + debug: 4.3.6 ts-api-utils: 1.3.0(typescript@5.5.4) optionalDependencies: typescript: 5.5.4 @@ -6251,10 +6512,28 @@ snapshots: '@typescript-eslint/types@8.3.0': {} + '@typescript-eslint/types@8.4.0': + optional: true + '@typescript-eslint/typescript-estree@8.3.0(typescript@5.5.4)': dependencies: '@typescript-eslint/types': 8.3.0 '@typescript-eslint/visitor-keys': 8.3.0 + debug: 4.3.6 + fast-glob: 3.3.2 + is-glob: 4.0.3 + minimatch: 9.0.5 + semver: 7.6.3 + ts-api-utils: 1.3.0(typescript@5.5.4) + optionalDependencies: + typescript: 5.5.4 + transitivePeerDependencies: + - supports-color + + '@typescript-eslint/typescript-estree@8.4.0(typescript@5.5.4)': + dependencies: + '@typescript-eslint/types': 8.4.0 + '@typescript-eslint/visitor-keys': 8.4.0 debug: 4.3.7 fast-glob: 3.3.2 is-glob: 4.0.3 @@ -6265,23 +6544,42 @@ snapshots: typescript: 5.5.4 transitivePeerDependencies: - supports-color + optional: true - '@typescript-eslint/utils@8.3.0(eslint@9.10.0(jiti@1.21.6))(typescript@5.5.4)': + '@typescript-eslint/utils@8.3.0(eslint@9.9.1(jiti@1.21.6))(typescript@5.5.4)': dependencies: - '@eslint-community/eslint-utils': 4.4.0(eslint@9.10.0(jiti@1.21.6)) + '@eslint-community/eslint-utils': 4.4.0(eslint@9.9.1(jiti@1.21.6)) '@typescript-eslint/scope-manager': 8.3.0 '@typescript-eslint/types': 8.3.0 '@typescript-eslint/typescript-estree': 8.3.0(typescript@5.5.4) - eslint: 9.10.0(jiti@1.21.6) + eslint: 9.9.1(jiti@1.21.6) transitivePeerDependencies: - supports-color - typescript + '@typescript-eslint/utils@8.4.0(eslint@9.9.1(jiti@1.21.6))(typescript@5.5.4)': + dependencies: + '@eslint-community/eslint-utils': 4.4.0(eslint@9.9.1(jiti@1.21.6)) + '@typescript-eslint/scope-manager': 8.4.0 + '@typescript-eslint/types': 8.4.0 + '@typescript-eslint/typescript-estree': 8.4.0(typescript@5.5.4) + eslint: 9.9.1(jiti@1.21.6) + transitivePeerDependencies: + - supports-color + - typescript + optional: true + '@typescript-eslint/visitor-keys@8.3.0': dependencies: '@typescript-eslint/types': 8.3.0 eslint-visitor-keys: 3.4.3 + '@typescript-eslint/visitor-keys@8.4.0': + dependencies: + '@typescript-eslint/types': 8.4.0 + eslint-visitor-keys: 3.4.3 + optional: true + '@unocss/astro@0.62.3(rollup@4.21.2)(vite@5.4.3(@types/node@22.5.4)(terser@5.31.6))': dependencies: '@unocss/core': 0.62.3 @@ -6332,14 +6630,14 @@ snapshots: gzip-size: 6.0.0 sirv: 2.0.4 - '@unocss/postcss@0.62.3(postcss@8.4.44)': + '@unocss/postcss@0.62.3(postcss@8.4.45)': dependencies: '@unocss/config': 0.62.3 '@unocss/core': 0.62.3 '@unocss/rule-utils': 0.62.3 css-tree: 2.3.1 magic-string: 0.30.11 - postcss: 8.4.44 + postcss: 8.4.45 tinyglobby: 0.2.5 transitivePeerDependencies: - supports-color @@ -6465,11 +6763,11 @@ snapshots: transitivePeerDependencies: - supports-color - '@vitest/eslint-plugin@1.0.5(@typescript-eslint/utils@8.3.0(eslint@9.10.0(jiti@1.21.6))(typescript@5.5.4))(eslint@9.10.0(jiti@1.21.6))(typescript@5.5.4)(vitest@2.0.5(@types/node@22.5.4)(jsdom@25.0.0)(terser@5.31.6))': + '@vitest/eslint-plugin@1.0.5(@typescript-eslint/utils@8.4.0(eslint@9.9.1(jiti@1.21.6))(typescript@5.5.4))(eslint@9.9.1(jiti@1.21.6))(typescript@5.5.4)(vitest@2.0.5(@types/node@22.5.4)(jsdom@25.0.0)(terser@5.31.6))': dependencies: - eslint: 9.10.0(jiti@1.21.6) + eslint: 9.9.1(jiti@1.21.6) optionalDependencies: - '@typescript-eslint/utils': 8.3.0(eslint@9.10.0(jiti@1.21.6))(typescript@5.5.4) + '@typescript-eslint/utils': 8.4.0(eslint@9.9.1(jiti@1.21.6))(typescript@5.5.4) typescript: 5.5.4 vitest: 2.0.5(@types/node@22.5.4)(jsdom@25.0.0)(terser@5.31.6) @@ -6514,11 +6812,24 @@ snapshots: estree-walker: 2.0.2 source-map-js: 1.2.0 + '@vue/compiler-core@3.5.3': + dependencies: + '@babel/parser': 7.25.6 + '@vue/shared': 3.5.3 + entities: 4.5.0 + estree-walker: 2.0.2 + source-map-js: 1.2.0 + '@vue/compiler-dom@3.4.38': dependencies: '@vue/compiler-core': 3.4.38 '@vue/shared': 3.4.38 + '@vue/compiler-dom@3.5.3': + dependencies: + '@vue/compiler-core': 3.5.3 + '@vue/shared': 3.5.3 + '@vue/compiler-sfc@3.4.38': dependencies: '@babel/parser': 7.25.6 @@ -6531,11 +6842,28 @@ snapshots: postcss: 8.4.44 source-map-js: 1.2.0 + '@vue/compiler-sfc@3.5.3': + dependencies: + '@babel/parser': 7.25.6 + '@vue/compiler-core': 3.5.3 + '@vue/compiler-dom': 3.5.3 + '@vue/compiler-ssr': 3.5.3 + '@vue/shared': 3.5.3 + estree-walker: 2.0.2 + magic-string: 0.30.11 + postcss: 8.4.45 + source-map-js: 1.2.0 + '@vue/compiler-ssr@3.4.38': dependencies: '@vue/compiler-dom': 3.4.38 '@vue/shared': 3.4.38 + '@vue/compiler-ssr@3.5.3': + dependencies: + '@vue/compiler-dom': 3.5.3 + '@vue/shared': 3.5.3 + '@vue/devtools-api@7.3.9': dependencies: '@vue/devtools-kit': 7.3.9 @@ -6578,6 +6906,8 @@ snapshots: '@vue/shared@3.4.38': {} + '@vue/shared@3.5.3': {} + '@vueuse/core@11.0.3(vue@3.4.38(typescript@5.5.4))': dependencies: '@types/web-bluetooth': 0.0.20 @@ -6624,7 +6954,7 @@ snapshots: agent-base@7.1.1: dependencies: - debug: 4.3.7 + debug: 4.3.6 transitivePeerDependencies: - supports-color @@ -6792,11 +7122,27 @@ snapshots: capnp-ts@0.7.0: dependencies: - debug: 4.3.7 + debug: 4.3.6 tslib: 2.7.0 transitivePeerDependencies: - supports-color + cbor-extract@2.2.0: + dependencies: + node-gyp-build-optional-packages: 5.1.1 + optionalDependencies: + '@cbor-extract/cbor-extract-darwin-arm64': 2.2.0 + '@cbor-extract/cbor-extract-darwin-x64': 2.2.0 + '@cbor-extract/cbor-extract-linux-arm': 2.2.0 + '@cbor-extract/cbor-extract-linux-arm64': 2.2.0 + '@cbor-extract/cbor-extract-linux-x64': 2.2.0 + '@cbor-extract/cbor-extract-win32-x64': 2.2.0 + optional: true + + cbor-x@1.6.0: + optionalDependencies: + cbor-extract: 2.2.0 + chai@5.1.1: dependencies: assertion-error: 2.0.1 @@ -6939,6 +7285,8 @@ snapshots: dependencies: browserslist: 4.23.3 + core-util-is@1.0.3: {} + cross-spawn@7.0.3: dependencies: path-key: 3.1.1 @@ -7053,6 +7401,7 @@ snapshots: debug@4.3.7: dependencies: ms: 2.1.3 + optional: true decimal.js@10.4.3: {} @@ -7070,6 +7419,9 @@ snapshots: detect-libc@1.0.3: {} + detect-libc@2.0.3: + optional: true + dir-glob@3.0.1: dependencies: path-type: 4.0.0 @@ -7243,9 +7595,9 @@ snapshots: escape-string-regexp@4.0.0: {} - eslint-compat-utils@0.5.1(eslint@9.10.0(jiti@1.21.6)): + eslint-compat-utils@0.5.1(eslint@9.9.1(jiti@1.21.6)): dependencies: - eslint: 9.10.0(jiti@1.21.6) + eslint: 9.9.1(jiti@1.21.6) semver: 7.6.3 eslint-config-flat-gitignore@0.1.8: @@ -7266,34 +7618,34 @@ snapshots: transitivePeerDependencies: - supports-color - eslint-merge-processors@0.1.0(eslint@9.10.0(jiti@1.21.6)): + eslint-merge-processors@0.1.0(eslint@9.9.1(jiti@1.21.6)): dependencies: - eslint: 9.10.0(jiti@1.21.6) + eslint: 9.9.1(jiti@1.21.6) - eslint-plugin-antfu@2.3.6(eslint@9.10.0(jiti@1.21.6)): + eslint-plugin-antfu@2.3.6(eslint@9.9.1(jiti@1.21.6)): dependencies: '@antfu/utils': 0.7.10 - eslint: 9.10.0(jiti@1.21.6) + eslint: 9.9.1(jiti@1.21.6) - eslint-plugin-command@0.2.3(eslint@9.10.0(jiti@1.21.6)): + eslint-plugin-command@0.2.3(eslint@9.9.1(jiti@1.21.6)): dependencies: '@es-joy/jsdoccomment': 0.43.1 - eslint: 9.10.0(jiti@1.21.6) + eslint: 9.9.1(jiti@1.21.6) - eslint-plugin-es-x@7.8.0(eslint@9.10.0(jiti@1.21.6)): + eslint-plugin-es-x@7.8.0(eslint@9.9.1(jiti@1.21.6)): dependencies: - '@eslint-community/eslint-utils': 4.4.0(eslint@9.10.0(jiti@1.21.6)) + '@eslint-community/eslint-utils': 4.4.0(eslint@9.9.1(jiti@1.21.6)) '@eslint-community/regexpp': 4.11.0 - eslint: 9.10.0(jiti@1.21.6) - eslint-compat-utils: 0.5.1(eslint@9.10.0(jiti@1.21.6)) + eslint: 9.9.1(jiti@1.21.6) + eslint-compat-utils: 0.5.1(eslint@9.9.1(jiti@1.21.6)) - eslint-plugin-import-x@4.1.0(eslint@9.10.0(jiti@1.21.6))(typescript@5.5.4): + eslint-plugin-import-x@4.1.0(eslint@9.9.1(jiti@1.21.6))(typescript@5.5.4): dependencies: '@typescript-eslint/typescript-estree': 8.3.0(typescript@5.5.4) - '@typescript-eslint/utils': 8.3.0(eslint@9.10.0(jiti@1.21.6))(typescript@5.5.4) - debug: 4.3.7 + '@typescript-eslint/utils': 8.3.0(eslint@9.9.1(jiti@1.21.6))(typescript@5.5.4) + debug: 4.3.6 doctrine: 3.0.0 - eslint: 9.10.0(jiti@1.21.6) + eslint: 9.9.1(jiti@1.21.6) eslint-import-resolver-node: 0.3.9 get-tsconfig: 4.7.6 is-glob: 4.0.3 @@ -7305,14 +7657,14 @@ snapshots: - supports-color - typescript - eslint-plugin-jsdoc@50.2.2(eslint@9.10.0(jiti@1.21.6)): + eslint-plugin-jsdoc@50.2.2(eslint@9.9.1(jiti@1.21.6)): dependencies: '@es-joy/jsdoccomment': 0.48.0 are-docs-informative: 0.0.2 comment-parser: 1.4.1 - debug: 4.3.7 + debug: 4.3.6 escape-string-regexp: 4.0.0 - eslint: 9.10.0(jiti@1.21.6) + eslint: 9.9.1(jiti@1.21.6) espree: 10.1.0 esquery: 1.6.0 parse-imports: 2.1.1 @@ -7322,30 +7674,30 @@ snapshots: transitivePeerDependencies: - supports-color - eslint-plugin-jsonc@2.16.0(eslint@9.10.0(jiti@1.21.6)): + eslint-plugin-jsonc@2.16.0(eslint@9.9.1(jiti@1.21.6)): dependencies: - '@eslint-community/eslint-utils': 4.4.0(eslint@9.10.0(jiti@1.21.6)) - eslint: 9.10.0(jiti@1.21.6) - eslint-compat-utils: 0.5.1(eslint@9.10.0(jiti@1.21.6)) + '@eslint-community/eslint-utils': 4.4.0(eslint@9.9.1(jiti@1.21.6)) + eslint: 9.9.1(jiti@1.21.6) + eslint-compat-utils: 0.5.1(eslint@9.9.1(jiti@1.21.6)) espree: 9.6.1 graphemer: 1.4.0 jsonc-eslint-parser: 2.4.0 natural-compare: 1.4.0 synckit: 0.6.2 - eslint-plugin-markdown@5.1.0(eslint@9.10.0(jiti@1.21.6)): + eslint-plugin-markdown@5.1.0(eslint@9.9.1(jiti@1.21.6)): dependencies: - eslint: 9.10.0(jiti@1.21.6) + eslint: 9.9.1(jiti@1.21.6) mdast-util-from-markdown: 0.8.5 transitivePeerDependencies: - supports-color - eslint-plugin-n@17.10.2(eslint@9.10.0(jiti@1.21.6)): + eslint-plugin-n@17.10.2(eslint@9.9.1(jiti@1.21.6)): dependencies: - '@eslint-community/eslint-utils': 4.4.0(eslint@9.10.0(jiti@1.21.6)) + '@eslint-community/eslint-utils': 4.4.0(eslint@9.9.1(jiti@1.21.6)) enhanced-resolve: 5.17.1 - eslint: 9.10.0(jiti@1.21.6) - eslint-plugin-es-x: 7.8.0(eslint@9.10.0(jiti@1.21.6)) + eslint: 9.9.1(jiti@1.21.6) + eslint-plugin-es-x: 7.8.0(eslint@9.9.1(jiti@1.21.6)) get-tsconfig: 4.7.6 globals: 15.9.0 ignore: 5.3.2 @@ -7354,48 +7706,48 @@ snapshots: eslint-plugin-no-only-tests@3.3.0: {} - eslint-plugin-perfectionist@3.3.0(eslint@9.10.0(jiti@1.21.6))(typescript@5.5.4)(vue-eslint-parser@9.4.3(eslint@9.10.0(jiti@1.21.6))): + eslint-plugin-perfectionist@3.3.0(eslint@9.9.1(jiti@1.21.6))(typescript@5.5.4)(vue-eslint-parser@9.4.3(eslint@9.9.1(jiti@1.21.6))): dependencies: '@typescript-eslint/types': 8.3.0 - '@typescript-eslint/utils': 8.3.0(eslint@9.10.0(jiti@1.21.6))(typescript@5.5.4) - eslint: 9.10.0(jiti@1.21.6) + '@typescript-eslint/utils': 8.3.0(eslint@9.9.1(jiti@1.21.6))(typescript@5.5.4) + eslint: 9.9.1(jiti@1.21.6) minimatch: 10.0.1 natural-compare-lite: 1.4.0 optionalDependencies: - vue-eslint-parser: 9.4.3(eslint@9.10.0(jiti@1.21.6)) + vue-eslint-parser: 9.4.3(eslint@9.9.1(jiti@1.21.6)) transitivePeerDependencies: - supports-color - typescript - eslint-plugin-regexp@2.6.0(eslint@9.10.0(jiti@1.21.6)): + eslint-plugin-regexp@2.6.0(eslint@9.9.1(jiti@1.21.6)): dependencies: - '@eslint-community/eslint-utils': 4.4.0(eslint@9.10.0(jiti@1.21.6)) + '@eslint-community/eslint-utils': 4.4.0(eslint@9.9.1(jiti@1.21.6)) '@eslint-community/regexpp': 4.11.0 comment-parser: 1.4.1 - eslint: 9.10.0(jiti@1.21.6) + eslint: 9.9.1(jiti@1.21.6) jsdoc-type-pratt-parser: 4.1.0 refa: 0.12.1 regexp-ast-analysis: 0.7.1 scslre: 0.3.0 - eslint-plugin-toml@0.11.1(eslint@9.10.0(jiti@1.21.6)): + eslint-plugin-toml@0.11.1(eslint@9.9.1(jiti@1.21.6)): dependencies: - debug: 4.3.7 - eslint: 9.10.0(jiti@1.21.6) - eslint-compat-utils: 0.5.1(eslint@9.10.0(jiti@1.21.6)) + debug: 4.3.6 + eslint: 9.9.1(jiti@1.21.6) + eslint-compat-utils: 0.5.1(eslint@9.9.1(jiti@1.21.6)) lodash: 4.17.21 toml-eslint-parser: 0.10.0 transitivePeerDependencies: - supports-color - eslint-plugin-unicorn@55.0.0(eslint@9.10.0(jiti@1.21.6)): + eslint-plugin-unicorn@55.0.0(eslint@9.9.1(jiti@1.21.6)): dependencies: '@babel/helper-validator-identifier': 7.24.7 - '@eslint-community/eslint-utils': 4.4.0(eslint@9.10.0(jiti@1.21.6)) + '@eslint-community/eslint-utils': 4.4.0(eslint@9.9.1(jiti@1.21.6)) ci-info: 4.0.0 clean-regexp: 1.0.0 core-js-compat: 3.38.1 - eslint: 9.10.0(jiti@1.21.6) + eslint: 9.9.1(jiti@1.21.6) esquery: 1.6.0 globals: 15.9.0 indent-string: 4.0.0 @@ -7408,41 +7760,41 @@ snapshots: semver: 7.6.3 strip-indent: 3.0.0 - eslint-plugin-unused-imports@4.1.3(@typescript-eslint/eslint-plugin@8.3.0(@typescript-eslint/parser@8.3.0(eslint@9.10.0(jiti@1.21.6))(typescript@5.5.4))(eslint@9.10.0(jiti@1.21.6))(typescript@5.5.4))(eslint@9.10.0(jiti@1.21.6)): + eslint-plugin-unused-imports@4.1.3(@typescript-eslint/eslint-plugin@8.3.0(@typescript-eslint/parser@8.3.0(eslint@9.9.1(jiti@1.21.6))(typescript@5.5.4))(eslint@9.9.1(jiti@1.21.6))(typescript@5.5.4))(eslint@9.9.1(jiti@1.21.6)): dependencies: - eslint: 9.10.0(jiti@1.21.6) + eslint: 9.9.1(jiti@1.21.6) optionalDependencies: - '@typescript-eslint/eslint-plugin': 8.3.0(@typescript-eslint/parser@8.3.0(eslint@9.10.0(jiti@1.21.6))(typescript@5.5.4))(eslint@9.10.0(jiti@1.21.6))(typescript@5.5.4) + '@typescript-eslint/eslint-plugin': 8.3.0(@typescript-eslint/parser@8.3.0(eslint@9.9.1(jiti@1.21.6))(typescript@5.5.4))(eslint@9.9.1(jiti@1.21.6))(typescript@5.5.4) - eslint-plugin-vue@9.27.0(eslint@9.10.0(jiti@1.21.6)): + eslint-plugin-vue@9.27.0(eslint@9.9.1(jiti@1.21.6)): dependencies: - '@eslint-community/eslint-utils': 4.4.0(eslint@9.10.0(jiti@1.21.6)) - eslint: 9.10.0(jiti@1.21.6) + '@eslint-community/eslint-utils': 4.4.0(eslint@9.9.1(jiti@1.21.6)) + eslint: 9.9.1(jiti@1.21.6) globals: 13.24.0 natural-compare: 1.4.0 nth-check: 2.1.1 postcss-selector-parser: 6.1.2 semver: 7.6.3 - vue-eslint-parser: 9.4.3(eslint@9.10.0(jiti@1.21.6)) + vue-eslint-parser: 9.4.3(eslint@9.9.1(jiti@1.21.6)) xml-name-validator: 4.0.0 transitivePeerDependencies: - supports-color - eslint-plugin-yml@1.14.0(eslint@9.10.0(jiti@1.21.6)): + eslint-plugin-yml@1.14.0(eslint@9.9.1(jiti@1.21.6)): dependencies: - debug: 4.3.7 - eslint: 9.10.0(jiti@1.21.6) - eslint-compat-utils: 0.5.1(eslint@9.10.0(jiti@1.21.6)) + debug: 4.3.6 + eslint: 9.9.1(jiti@1.21.6) + eslint-compat-utils: 0.5.1(eslint@9.9.1(jiti@1.21.6)) lodash: 4.17.21 natural-compare: 1.4.0 yaml-eslint-parser: 1.2.3 transitivePeerDependencies: - supports-color - eslint-processor-vue-blocks@0.1.2(@vue/compiler-sfc@3.4.38)(eslint@9.10.0(jiti@1.21.6)): + eslint-processor-vue-blocks@0.1.2(@vue/compiler-sfc@3.5.3)(eslint@9.9.1(jiti@1.21.6)): dependencies: - '@vue/compiler-sfc': 3.4.38 - eslint: 9.10.0(jiti@1.21.6) + '@vue/compiler-sfc': 3.5.3 + eslint: 9.9.1(jiti@1.21.6) eslint-scope@7.2.2: dependencies: @@ -7458,21 +7810,20 @@ snapshots: eslint-visitor-keys@4.0.0: {} - eslint@9.10.0(jiti@1.21.6): + eslint@9.9.1(jiti@1.21.6): dependencies: - '@eslint-community/eslint-utils': 4.4.0(eslint@9.10.0(jiti@1.21.6)) + '@eslint-community/eslint-utils': 4.4.0(eslint@9.9.1(jiti@1.21.6)) '@eslint-community/regexpp': 4.11.0 '@eslint/config-array': 0.18.0 '@eslint/eslintrc': 3.1.0 - '@eslint/js': 9.10.0 - '@eslint/plugin-kit': 0.1.0 + '@eslint/js': 9.9.1 '@humanwhocodes/module-importer': 1.0.1 '@humanwhocodes/retry': 0.3.0 '@nodelib/fs.walk': 1.2.8 ajv: 6.12.6 chalk: 4.1.2 cross-spawn: 7.0.3 - debug: 4.3.7 + debug: 4.3.6 escape-string-regexp: 4.0.0 eslint-scope: 8.0.2 eslint-visitor-keys: 4.0.0 @@ -7488,6 +7839,7 @@ snapshots: is-glob: 4.0.3 is-path-inside: 3.0.3 json-stable-stringify-without-jsonify: 1.0.1 + levn: 0.4.1 lodash.merge: 4.6.2 minimatch: 3.1.2 natural-compare: 1.4.0 @@ -7756,7 +8108,7 @@ snapshots: http-proxy-agent@7.0.2: dependencies: agent-base: 7.1.1 - debug: 4.3.7 + debug: 4.3.6 transitivePeerDependencies: - supports-color @@ -7765,7 +8117,7 @@ snapshots: https-proxy-agent@7.0.5: dependencies: agent-base: 7.1.1 - debug: 4.3.7 + debug: 4.3.6 transitivePeerDependencies: - supports-color @@ -7783,6 +8135,8 @@ snapshots: ignore@5.3.2: {} + immediate@3.0.6: {} + import-fresh@3.3.0: dependencies: parent-module: 1.0.1 @@ -7791,7 +8145,7 @@ snapshots: importx@0.4.3: dependencies: bundle-require: 5.0.0(esbuild@0.23.1) - debug: 4.3.7 + debug: 4.3.6 esbuild: 0.23.1 jiti: 2.0.0-beta.2 jiti-v1: jiti@1.21.6 @@ -7883,6 +8237,8 @@ snapshots: dependencies: system-architecture: 0.1.0 + isarray@1.0.0: {} + isexe@2.0.0: {} istanbul-lib-coverage@3.2.2: {} @@ -7983,6 +8339,13 @@ snapshots: espree: 9.6.1 semver: 7.6.3 + jszip@3.10.1: + dependencies: + lie: 3.3.0 + pako: 1.0.11 + readable-stream: 2.3.8 + setimmediate: 1.0.5 + keyv@4.5.4: dependencies: json-buffer: 3.0.1 @@ -7994,6 +8357,10 @@ snapshots: prelude-ls: 1.2.1 type-check: 0.4.0 + lie@3.3.0: + dependencies: + immediate: 3.0.6 + lilconfig@3.1.2: {} lines-and-columns@1.2.4: {} @@ -8107,7 +8474,7 @@ snapshots: micromark@2.11.4: dependencies: - debug: 4.3.7 + debug: 4.3.6 parse-entities: 2.0.0 transitivePeerDependencies: - supports-color @@ -8207,6 +8574,22 @@ snapshots: ms@2.1.3: {} + msgpackr-extract@3.0.3: + dependencies: + node-gyp-build-optional-packages: 5.2.2 + optionalDependencies: + '@msgpackr-extract/msgpackr-extract-darwin-arm64': 3.0.3 + '@msgpackr-extract/msgpackr-extract-darwin-x64': 3.0.3 + '@msgpackr-extract/msgpackr-extract-linux-arm': 3.0.3 + '@msgpackr-extract/msgpackr-extract-linux-arm64': 3.0.3 + '@msgpackr-extract/msgpackr-extract-linux-x64': 3.0.3 + '@msgpackr-extract/msgpackr-extract-win32-x64': 3.0.3 + optional: true + + msgpackr@1.11.0: + optionalDependencies: + msgpackr-extract: 3.0.3 + mustache@4.2.0: {} mute-stream@1.0.0: {} @@ -8227,6 +8610,16 @@ snapshots: node-forge@1.3.1: {} + node-gyp-build-optional-packages@5.1.1: + dependencies: + detect-libc: 2.0.3 + optional: true + + node-gyp-build-optional-packages@5.2.2: + dependencies: + detect-libc: 2.0.3 + optional: true + node-releases@2.0.18: {} normalize-package-data@2.5.0: @@ -8317,6 +8710,8 @@ snapshots: package-manager-detector@0.2.0: {} + pako@1.0.11: {} + parent-module@1.0.1: dependencies: callsites: 3.1.0 @@ -8592,6 +8987,12 @@ snapshots: picocolors: 1.1.0 source-map-js: 1.2.0 + postcss@8.4.45: + dependencies: + nanoid: 3.3.7 + picocolors: 1.1.0 + source-map-js: 1.2.0 + preact@10.23.2: {} prelude-ls@1.2.1: {} @@ -8600,6 +9001,8 @@ snapshots: printable-characters@1.0.42: {} + process-nextick-args@2.0.1: {} + process-warning@4.0.0: {} process@0.11.10: {} @@ -8634,6 +9037,16 @@ snapshots: parse-json: 5.2.0 type-fest: 0.6.0 + readable-stream@2.3.8: + dependencies: + core-util-is: 1.0.3 + inherits: 2.0.4 + isarray: 1.0.0 + process-nextick-args: 2.0.1 + safe-buffer: 5.1.2 + string_decoder: 1.1.1 + util-deprecate: 1.0.2 + readable-stream@4.5.2: dependencies: abort-controller: 3.0.0 @@ -8746,6 +9159,8 @@ snapshots: dependencies: queue-microtask: 1.2.3 + safe-buffer@5.1.2: {} + safe-buffer@5.2.1: {} safe-stable-stringify@2.5.0: {} @@ -8764,7 +9179,7 @@ snapshots: scule@1.3.0: {} - search-insights@2.17.0: {} + search-insights@2.17.1: {} secure-json-parse@2.7.0: {} @@ -8785,6 +9200,8 @@ snapshots: seroval@1.1.1: {} + setimmediate@1.0.5: {} + shebang-command@2.0.0: dependencies: shebang-regex: 3.0.0 @@ -8914,6 +9331,10 @@ snapshots: get-east-asian-width: 1.2.0 strip-ansi: 7.1.0 + string_decoder@1.1.1: + dependencies: + safe-buffer: 5.1.2 + string_decoder@1.3.0: dependencies: safe-buffer: 5.2.1 @@ -9152,18 +9573,18 @@ snapshots: universalify@0.2.0: {} - unocss-preset-animations@1.1.0(@unocss/preset-wind@0.62.3)(unocss@0.62.3(postcss@8.4.44)(rollup@4.21.2)(vite@5.4.3(@types/node@22.5.4)(terser@5.31.6))): + unocss-preset-animations@1.1.0(@unocss/preset-wind@0.62.3)(unocss@0.62.3(postcss@8.4.45)(rollup@4.21.2)(vite@5.4.3(@types/node@22.5.4)(terser@5.31.6))): dependencies: '@unocss/preset-wind': 0.62.3 - unocss: 0.62.3(postcss@8.4.44)(rollup@4.21.2)(vite@5.4.3(@types/node@22.5.4)(terser@5.31.6)) + unocss: 0.62.3(postcss@8.4.45)(rollup@4.21.2)(vite@5.4.3(@types/node@22.5.4)(terser@5.31.6)) - unocss@0.62.3(postcss@8.4.44)(rollup@4.21.2)(vite@5.4.3(@types/node@22.5.4)(terser@5.31.6)): + unocss@0.62.3(postcss@8.4.45)(rollup@4.21.2)(vite@5.4.3(@types/node@22.5.4)(terser@5.31.6)): dependencies: '@unocss/astro': 0.62.3(rollup@4.21.2)(vite@5.4.3(@types/node@22.5.4)(terser@5.31.6)) '@unocss/cli': 0.62.3(rollup@4.21.2) '@unocss/core': 0.62.3 '@unocss/extractor-arbitrary-variants': 0.62.3 - '@unocss/postcss': 0.62.3(postcss@8.4.44) + '@unocss/postcss': 0.62.3(postcss@8.4.45) '@unocss/preset-attributify': 0.62.3 '@unocss/preset-icons': 0.62.3 '@unocss/preset-mini': 0.62.3 @@ -9186,7 +9607,7 @@ snapshots: - rollup - supports-color - unstorage@1.12.0: + unstorage@1.11.1: dependencies: anymatch: 3.1.3 chokidar: 3.6.0 @@ -9292,10 +9713,10 @@ snapshots: optionalDependencies: vite: 5.4.3(@types/node@22.5.4)(terser@5.31.6) - vitepress@1.3.4(@algolia/client-search@4.24.0)(@types/node@22.5.4)(postcss@8.4.44)(search-insights@2.17.0)(terser@5.31.6)(typescript@5.5.4): + vitepress@1.3.4(@algolia/client-search@5.3.0)(@types/node@22.5.4)(postcss@8.4.45)(search-insights@2.17.1)(terser@5.31.6)(typescript@5.5.4): dependencies: '@docsearch/css': 3.6.1 - '@docsearch/js': 3.6.1(@algolia/client-search@4.24.0)(search-insights@2.17.0) + '@docsearch/js': 3.6.1(@algolia/client-search@5.3.0)(search-insights@2.17.1) '@shikijs/core': 1.16.1 '@shikijs/transformers': 1.16.1 '@types/markdown-it': 14.1.2 @@ -9311,7 +9732,7 @@ snapshots: vite: 5.4.3(@types/node@22.5.4)(terser@5.31.6) vue: 3.4.38(typescript@5.5.4) optionalDependencies: - postcss: 8.4.44 + postcss: 8.4.45 transitivePeerDependencies: - '@algolia/client-search' - '@types/node' @@ -9378,10 +9799,10 @@ snapshots: dependencies: vue: 3.4.38(typescript@5.5.4) - vue-eslint-parser@9.4.3(eslint@9.10.0(jiti@1.21.6)): + vue-eslint-parser@9.4.3(eslint@9.9.1(jiti@1.21.6)): dependencies: - debug: 4.3.7 - eslint: 9.10.0(jiti@1.21.6) + debug: 4.3.6 + eslint: 9.9.1(jiti@1.21.6) eslint-scope: 7.2.2 eslint-visitor-keys: 3.4.3 espree: 9.6.1 @@ -9439,7 +9860,7 @@ snapshots: '@cloudflare/workerd-linux-arm64': 1.20240821.1 '@cloudflare/workerd-windows-64': 1.20240821.1 - wrangler@3.75.0(@cloudflare/workers-types@4.20240903.0): + wrangler@3.74.0(@cloudflare/workers-types@4.20240903.0): dependencies: '@cloudflare/kv-asset-handler': 0.3.4 '@cloudflare/workers-shared': 0.4.1 @@ -9536,4 +9957,4 @@ snapshots: zx@8.1.5: optionalDependencies: '@types/fs-extra': 11.0.4 - '@types/node': 22.5.1 + '@types/node': 22.5.2