From 71b00dcec1e6a08538f69a6df84451837eea2a39 Mon Sep 17 00:00:00 2001 From: Max Date: Tue, 14 Jul 2026 09:29:51 +0200 Subject: [PATCH 01/17] fix(autosave): every time the typing stops Debounce will delay the execution of the function every time it is called. So autosave was waiting until no updates occured for 30 seconds. That's a long time and does not feel responsive. Try to autosave every time the user stops typing for at least one second. Signed-off-by: Max --- src/services/SaveService.ts | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) diff --git a/src/services/SaveService.ts b/src/services/SaveService.ts index d2053e61e2f..98f6e041e48 100644 --- a/src/services/SaveService.ts +++ b/src/services/SaveService.ts @@ -17,7 +17,7 @@ import { ERROR_TYPE, type SyncService } from './SyncService' * * @type {number} time in ms */ -const AUTOSAVE_INTERVAL = 30000 +const AUTOSAVE_DEBOUNCE = 1000 class SaveService { connection: ShallowRef @@ -41,7 +41,7 @@ class SaveService { this.syncService = syncService this.serialize = serialize this.getDocumentState = getDocumentState - this.autosave = debounce(this._autosave.bind(this), AUTOSAVE_INTERVAL) + this.autosave = debounce(this._autosave.bind(this), AUTOSAVE_DEBOUNCE) this.syncService.bus.on('close', () => { this.autosave.clear() }) @@ -115,6 +115,7 @@ class SaveService { } _autosave() { + logger.debug('_autosave') return this.save({ manualSave: false }).catch((error) => { logger.error('Failed to autosave document.', { error }) }) From 2da1fe73d9b35b0f26609076eef5bdccff34c2ee Mon Sep 17 00:00:00 2001 From: Max Date: Sat, 25 Jul 2026 22:06:33 +0200 Subject: [PATCH 02/17] chore(refactor): document tracking into provideSyncService Also removed the document attribute from the `sync` event load. It is already included in the `change` event load. Signed-off-by: Max --- src/components/Editor.vue | 16 +++++--------- src/composables/useSyncService.ts | 35 ++++++++++++++++++++++++++----- src/services/PollingBackend.ts | 12 +++++------ src/services/SyncService.ts | 5 +---- 4 files changed, 42 insertions(+), 26 deletions(-) diff --git a/src/components/Editor.vue b/src/components/Editor.vue index 57ead19c0b5..3024025fb63 100644 --- a/src/components/Editor.vue +++ b/src/components/Editor.vue @@ -243,7 +243,7 @@ export default defineComponent({ props, ) const { connection, openConnection } = provideConnection(props) - const { syncService } = provideSyncService(connection, openConnection) + const { document, syncService } = provideSyncService(connection, openConnection) const extensions = [ Autofocus.configure({ fileId: props.fileId }), Collaboration.configure({ document: ydoc }), @@ -290,6 +290,7 @@ export default defineComponent({ return { awareness, connection, + document, editor, el, hasConnectionIssue, @@ -314,7 +315,6 @@ export default defineComponent({ return { IDLE_TIMEOUT, - document: null, fileNode: null, idle: false, @@ -501,8 +501,7 @@ export default defineComponent({ this.idle = false }, - onOpened({ document, session, content, documentState, readOnly }) { - this.document = document + onOpened({ session, content, documentState, readOnly }) { this.readOnly = readOnly this.editMode = !readOnly && !this.openReadOnlyEnabled this.hasConnectionIssue = false @@ -541,9 +540,7 @@ export default defineComponent({ this.updateUser(session) }, - onChange({ document }) { - this.document = document - + onChange() { this.syncError = null this.setEditable(this.editMode && !this.requireReconnect) }, @@ -563,7 +560,7 @@ export default defineComponent({ }) }, - onSync({ steps, document }) { + onSync({ steps }) { this.hasConnectionIssue = this.syncService.backend.fetcher === 0 || !this.syncProvider?.wsconnected @@ -575,9 +572,6 @@ export default defineComponent({ this.$nextTick(() => { this.emit('sync-service:sync') }) - if (document) { - this.document = document - } }, onError({ type, data }) { diff --git a/src/composables/useSyncService.ts b/src/composables/useSyncService.ts index 08b4483d1b3..9d3016575ed 100644 --- a/src/composables/useSyncService.ts +++ b/src/composables/useSyncService.ts @@ -3,14 +3,18 @@ * SPDX-License-Identifier: AGPL-3.0-or-later */ -import { type InjectionKey, type ShallowRef, inject, provide } from 'vue' -import { SyncService } from '../services/SyncService' -import type { Connection, InitialData } from './useConnection' +import type { InjectionKey, ShallowRef } from 'vue' +import type { Document } from '../services/SyncService.ts' +import type { Connection, InitialData } from './useConnection.ts' + +import { inject, onUnmounted, provide, ref } from 'vue' +import { SyncService } from '../services/SyncService.ts' const syncServiceKey = Symbol('text:sync') as InjectionKey /** * Define a sync service and provide it to child components + * * @param connection Connection to the text api. * @param openConnection Function to open the connection. */ @@ -23,10 +27,31 @@ export function provideSyncService( openConnection, }) provide(syncServiceKey, syncService) - return { syncService } + + const document = ref() + /** + * Update the document ref based on the event provided + * + * @param event that triggered the update + * @param event.document latest state of the document + */ + function updateDocument({ document: current }: { document: Document }) { + document.value = current + } + syncService.bus.on('opened', updateDocument) + syncService.bus.on('change', updateDocument) + onUnmounted(() => { + syncService.bus.off('opened', updateDocument) + syncService.bus.off('change', updateDocument) + }) + + return { document, syncService } } -export const useSyncService = () => { +/** + * + */ +export function useSyncService() { const syncService = inject(syncServiceKey) as SyncService return { syncService } } diff --git a/src/services/PollingBackend.ts b/src/services/PollingBackend.ts index 6bbf93e2196..e69d4656d51 100644 --- a/src/services/PollingBackend.ts +++ b/src/services/PollingBackend.ts @@ -143,23 +143,23 @@ class PollingBackend { } _handleResponse({ data }: { data: PollData }) { - const { document, sessions } = data + const { document, readOnly, sessions, steps } = data this.#fetchRetryCounter = 0 - if (data.readOnly !== undefined && data.readOnly !== this.#readOnly) { - this.#readOnly = data.readOnly + if (readOnly !== undefined && readOnly !== this.#readOnly) { + this.#readOnly = readOnly this.#syncService.bus.emit('permissionChange', { readOnly: this.#readOnly, }) - if (data.readOnly) { + if (readOnly) { this.maximumReadOnlyTimer() } } this.#syncService.bus.emit('change', { document, sessions }) - this.#syncService.receiveSteps(data) + this.#syncService.receiveSteps({ sessions, steps }) - if (data.steps.length === 0) { + if (steps.length === 0) { if (!this.#initialLoadingFinished) { this.#initialLoadingFinished = true } diff --git a/src/services/SyncService.ts b/src/services/SyncService.ts index d15da90a9e6..888b03a7ab9 100644 --- a/src/services/SyncService.ts +++ b/src/services/SyncService.ts @@ -110,7 +110,7 @@ export declare type EventTypes = { opened: OpenData /* received new steps */ - sync: { document?: object; steps: Step[] } + sync: { steps: Step[] } /* state changed (dirty) */ stateChange: { initialLoading?: boolean; dirty?: boolean } @@ -304,17 +304,14 @@ class SyncService { receiveSteps({ steps, - document, sessions = [], }: { steps: Step[] - document?: object sessions?: Session[] }) { const versionAfter = Math.max(this.version, ...steps.map((s) => s.version)) this.bus.emit('sync', { steps: [...awarenessSteps(sessions), ...steps], - document, }) if (this.version < versionAfter) { // Steps up to version where emitted but it looks like they were not processed. From 9558bab988ef116ef21203da0c355987c4a40f13 Mon Sep 17 00:00:00 2001 From: Max Date: Sat, 25 Jul 2026 22:45:23 +0200 Subject: [PATCH 03/17] fix(autosave): only save when server is ready The server will only accept autosaves every 10 seconds. `document` contains the last saved timestamp. Compute the time to autosave next. Add a small random delay (up to 3 seconds) to avoid all connected clients from saving at the same time. Signed-off-by: Max --- src/apis/save.ts | 2 +- src/composables/useSaveService.ts | 29 +++++++++++++----- src/composables/useSyncService.ts | 2 ++ src/services/SaveService.ts | 51 ++++++++++++++++++++----------- src/services/SyncService.ts | 2 +- 5 files changed, 60 insertions(+), 26 deletions(-) diff --git a/src/apis/save.ts b/src/apis/save.ts index 45e87f910f7..399726da38f 100644 --- a/src/apis/save.ts +++ b/src/apis/save.ts @@ -19,7 +19,7 @@ interface SaveData { } interface SaveResponse { - data: Document + data: { document: Document } } /** diff --git a/src/composables/useSaveService.ts b/src/composables/useSaveService.ts index 8ef431d8244..fd6816caae7 100644 --- a/src/composables/useSaveService.ts +++ b/src/composables/useSaveService.ts @@ -3,23 +3,35 @@ * SPDX-License-Identifier: AGPL-3.0-or-later */ -import { type InjectionKey, type ShallowRef, inject, provide } from 'vue' +import type { InjectionKey, Ref, ShallowRef } from 'vue' import type { Doc } from 'yjs' -import { getDocumentState } from '../helpers/yjs' -import { SaveService } from '../services/SaveService' -import type { SyncService } from '../services/SyncService.ts' +import type { Document, SyncService } from '../services/SyncService.ts' import type { Connection } from './useConnection.ts' +import { inject, provide } from 'vue' +import { getDocumentState } from '../helpers/yjs.ts' +import { SaveService } from '../services/SaveService.ts' + const saveServiceKey = Symbol('text:save') as InjectionKey -export const provideSaveService = ( +/** + * + * @param connection to the api + * @param document as tracked by the sync service + * @param syncService mostly used for the event bus and events + * @param serialize to extract the document markdown content + * @param ydoc to extract the document state from + */ +export function provideSaveService( connection: ShallowRef, + document: Ref, syncService: SyncService, serialize: () => string, ydoc: Doc, -) => { +) { const saveService = new SaveService({ connection, + document, syncService, serialize, getDocumentState: () => getDocumentState(ydoc), @@ -28,7 +40,10 @@ export const provideSaveService = ( return { saveService } } -export const useSaveService = () => { +/** + * + */ +export function useSaveService() { const saveService = inject(saveServiceKey) as SaveService return { saveService } } diff --git a/src/composables/useSyncService.ts b/src/composables/useSyncService.ts index 9d3016575ed..e69ca06ed73 100644 --- a/src/composables/useSyncService.ts +++ b/src/composables/useSyncService.ts @@ -40,9 +40,11 @@ export function provideSyncService( } syncService.bus.on('opened', updateDocument) syncService.bus.on('change', updateDocument) + syncService.bus.on('save', updateDocument) onUnmounted(() => { syncService.bus.off('opened', updateDocument) syncService.bus.off('change', updateDocument) + syncService.bus.off('save', updateDocument) }) return { document, syncService } diff --git a/src/services/SaveService.ts b/src/services/SaveService.ts index 98f6e041e48..e7e097798f2 100644 --- a/src/services/SaveService.ts +++ b/src/services/SaveService.ts @@ -3,24 +3,27 @@ * SPDX-License-Identifier: AGPL-3.0-or-later */ +import type { Ref, ShallowRef } from 'vue' +import type { Connection } from '../composables/useConnection.ts' +import type { Document, SyncService } from './SyncService.ts' + import { showError } from '@nextcloud/dialogs' import debounce from 'debounce' - -import type { ShallowRef } from 'vue' -import { save, saveViaSendBeacon } from '../apis/save' -import type { Connection } from '../composables/useConnection.ts' +import { save, saveViaSendBeacon } from '../apis/save.ts' import { logger } from '../helpers/logger.js' -import { ERROR_TYPE, type SyncService } from './SyncService' +import { ERROR_TYPE } from './SyncService.ts' -/** - * Interval to save the serialized document and the document state - * - * @type {number} time in ms - */ -const AUTOSAVE_DEBOUNCE = 1000 +// Time constants in seconds: +// Only autosave after 1 second typing breaks +const AUTOSAVE_DEBOUNCE = 1 +// Server only accepts auutosaves every 10 seconds +const SERVER_AUTOSAVE_INTERVAL = 10 +// Randomize save times to prevent all clients saving at the same time. +const MAX_RANDOM_AUTOSAVE_DELAY = 3 class SaveService { connection: ShallowRef + document: Ref syncService serialize getDocumentState @@ -28,20 +31,23 @@ class SaveService { constructor({ connection, + document, syncService, serialize, getDocumentState, }: { connection: ShallowRef + document: Ref syncService: SyncService serialize: () => string getDocumentState: () => string }) { this.connection = connection + this.document = document this.syncService = syncService this.serialize = serialize this.getDocumentState = getDocumentState - this.autosave = debounce(this._autosave.bind(this), AUTOSAVE_DEBOUNCE) + this.autosave = debounce(this._autosave.bind(this), AUTOSAVE_DEBOUNCE * 1000) this.syncService.bus.on('close', () => { this.autosave.clear() }) @@ -73,14 +79,13 @@ class SaveService { force, manualSave, }) - this.emit('stateChange', { dirty: false }) logger.debug('[SaveService] saved', { response }) this.emit('save', response.data) this.autosave.clear() } catch (e) { logger.error('Failed to save document.', { error: e }) const response = ( - e as { response?: { status?: number; data?: { error?: string } } } + e as { response?: { status?: number, data?: { error?: string } } } ).response if (response?.status === 403) { // Document is now read-only; permissionChange from sync will update the UI @@ -103,11 +108,14 @@ class SaveService { if (!this.connection.value) { return } - saveViaSendBeacon(this.connection.value, { + const success = saveViaSendBeacon(this.connection.value, { version: this.version, autosaveContent: this._getContent(), documentState: this.getDocumentState(), - }) && logger.debug('[SaveService] saved using sendBeacon') + }) + if (success) { + logger.debug('[SaveService] saved using sendBeacon') + } } forceSave() { @@ -115,7 +123,16 @@ class SaveService { } _autosave() { - logger.debug('_autosave') + const lastSave = this.document.value?.lastSavedVersionTime ?? 0 + const now = Date.now() / 1000 + // Server won't accept autosaves yet + if (now < lastSave + SERVER_AUTOSAVE_INTERVAL) { + logger.debug('Not autosaving as last save is recent', { lastSave, now }) + const nextSave = lastSave + SERVER_AUTOSAVE_INTERVAL + Math.random() * MAX_RANDOM_AUTOSAVE_DELAY + setTimeout(() => this.autosave(), (nextSave - now) * 1000) + return + } + logger.debug('Autosaving') return this.save({ manualSave: false }).catch((error) => { logger.error('Failed to autosave document.', { error }) }) diff --git a/src/services/SyncService.ts b/src/services/SyncService.ts index 888b03a7ab9..ba2d6966737 100644 --- a/src/services/SyncService.ts +++ b/src/services/SyncService.ts @@ -122,7 +122,7 @@ export declare type EventTypes = { change: { sessions: Session[]; document: Document } /* Emitted after successful save */ - save: object + save: { document: Document } /* Emitted once a document becomes idle */ idle: void From 164ccd9aeffca18f9ad0cfead9b5c22d60acf312 Mon Sep 17 00:00:00 2001 From: Max Date: Sun, 26 Jul 2026 19:01:16 +0200 Subject: [PATCH 04/17] fix(autosave): better dirty tracking with versions Keep track of the version that has our changes and compare it to the last saved version on the server. This allows fixing two scenarios: * Server response happily to save but does not actually save. The server will only save new versions every 10 seconds. If the latest save just happened it will still respond with 200 but list the outdated version in the response. Comparing the versions shows that our changes have not been saved yet. * Other user already saved the file. our changes. So far dirty would stay true until WE save our changes. Comparing the versions also shows the file was saved when it was saved by someone else. Signed-off-by: Max --- src/components/Editor.vue | 25 ++++++++++++++----------- src/composables/useSyncService.ts | 28 ++++++++++++++++++++++++---- src/services/SyncService.ts | 13 +++++++++---- 3 files changed, 47 insertions(+), 19 deletions(-) diff --git a/src/components/Editor.vue b/src/components/Editor.vue index 3024025fb63..55cdd1f3c35 100644 --- a/src/components/Editor.vue +++ b/src/components/Editor.vue @@ -235,6 +235,10 @@ export default defineComponent({ const ydoc = new Doc() const awareness = new Awareness(ydoc) const hasConnectionIssue = ref(false) + const dirty = ref(false) + const setDirty = (val) => { + dirty.value = val + } const { delayed: requireReconnect } = useDelayedFlag(hasConnectionIssue) const { isPublic, isRichEditor, isRichWorkspace, useTableOfContents } = provideEditorFlags(props) @@ -243,7 +247,7 @@ export default defineComponent({ props, ) const { connection, openConnection } = provideConnection(props) - const { document, syncService } = provideSyncService(connection, openConnection) + const { document, syncService } = provideSyncService(connection, openConnection, setDirty) const extensions = [ Autofocus.configure({ fileId: props.fileId }), Collaboration.configure({ document: ydoc }), @@ -280,6 +284,7 @@ export default defineComponent({ const { saveService } = provideSaveService( connection, + document, syncService, serialize, ydoc, @@ -290,6 +295,7 @@ export default defineComponent({ return { awareness, connection, + dirty, document, editor, el, @@ -302,6 +308,7 @@ export default defineComponent({ requireReconnect, saveService, serialize, + setDirty, setEditable, syncProvider, syncService, @@ -318,7 +325,6 @@ export default defineComponent({ fileNode: null, idle: false, - dirty: false, contentLoaded: false, syncError: null, readOnly: true, @@ -478,6 +484,7 @@ export default defineComponent({ bus.on('idle', this.onIdle) bus.on('save', this.onSave) bus.on('permissionChange', this.onPermissionChange) + bus.on('changesPushed', this.onChangesPushed) }, unlistenSyncServiceEvents() { @@ -490,6 +497,7 @@ export default defineComponent({ bus.off('idle', this.onIdle) bus.off('save', this.onSave) bus.off('permissionChange', this.onPermissionChange) + bus.off('changesPushed', this.onChangesPushed) }, reconnect() { @@ -627,15 +635,6 @@ export default defineComponent({ } this.emit('ready') } - if (Object.prototype.hasOwnProperty.call(state, 'dirty')) { - // ignore initial loading and other automated changes before first user change - if (this.editor.can().undo() || this.editor.can().redo()) { - this.dirty = state.dirty - if (this.dirty) { - this.saveService.autosave() - } - } - } }, onIdle() { @@ -679,6 +678,10 @@ export default defineComponent({ } }, + onChangesPushed() { + this.saveService.autosave() + }, + onFocus() { this.emit('focus') }, diff --git a/src/composables/useSyncService.ts b/src/composables/useSyncService.ts index e69ca06ed73..b759b068bdc 100644 --- a/src/composables/useSyncService.ts +++ b/src/composables/useSyncService.ts @@ -7,7 +7,7 @@ import type { InjectionKey, ShallowRef } from 'vue' import type { Document } from '../services/SyncService.ts' import type { Connection, InitialData } from './useConnection.ts' -import { inject, onUnmounted, provide, ref } from 'vue' +import { computed, inject, onUnmounted, provide, ref, watch } from 'vue' import { SyncService } from '../services/SyncService.ts' const syncServiceKey = Symbol('text:sync') as InjectionKey @@ -17,10 +17,12 @@ const syncServiceKey = Symbol('text:sync') as InjectionKey * * @param connection Connection to the text api. * @param openConnection Function to open the connection. + * @param setDirty to udpate the dirty state. */ export function provideSyncService( connection: ShallowRef, openConnection: () => Promise, + setDirty: (val: boolean) => Promise, ) { const syncService = new SyncService({ connection, @@ -35,8 +37,8 @@ export function provideSyncService( * @param event that triggered the update * @param event.document latest state of the document */ - function updateDocument({ document: current }: { document: Document }) { - document.value = current + function updateDocument(event: { document: Document }) { + document.value = event.document } syncService.bus.on('opened', updateDocument) syncService.bus.on('change', updateDocument) @@ -47,7 +49,25 @@ export function provideSyncService( syncService.bus.off('save', updateDocument) }) - return { document, syncService } + const versionWithChanges = ref(0) + /** + * Update the tracked version based on the one in the event + * + * @param event that triggered the update + * @param event.version with changes pushed to the server + */ + function updateVersionWithChanges(event: { version: number }) { + versionWithChanges.value = Math.max(event.version, versionWithChanges.value) + } + syncService.bus.on('changesPushed', updateVersionWithChanges) + onUnmounted(() => { + syncService.bus.off('changesPushed', updateVersionWithChanges) + }) + + const dirty = computed(() => (document.value?.lastSavedVersion ?? 0) < versionWithChanges.value) + watch(dirty, setDirty) + + return { dirty, document, syncService } } /** diff --git a/src/services/SyncService.ts b/src/services/SyncService.ts index ba2d6966737..6ce8227a2af 100644 --- a/src/services/SyncService.ts +++ b/src/services/SyncService.ts @@ -113,7 +113,10 @@ export declare type EventTypes = { sync: { steps: Step[] } /* state changed (dirty) */ - stateChange: { initialLoading?: boolean; dirty?: boolean } + stateChange: { initialLoading?: boolean } + + /* local yjs changes have been pushed to the server */ + changesPushed: { version: number } /* error */ error: { type: ErrorType; data?: object } @@ -241,9 +244,7 @@ class SyncService { this.#sending = true clearInterval(this.#sendIntervalId) this.#sendIntervalId = undefined - if (this.#outbox.hasUpdate) { - this.bus.emit('stateChange', { dirty: true }) - } + const hadUpdate = this.#outbox.hasUpdate if (!this.hasActiveConnection()) { this.#sending = false return @@ -267,6 +268,10 @@ class SyncService { this.#sending = false if (steps?.length > 0) { this.receiveSteps({ steps }) + if (hadUpdate) { + // this.version has been increased in receiveSteps + this.bus.emit('changesPushed', { version: this.version }) + } } }) .catch((err) => { From 96e39a2f1f2b29e0cbcd7cc372005c4a27a3e719 Mon Sep 17 00:00:00 2001 From: Max Date: Mon, 27 Jul 2026 06:50:17 +0200 Subject: [PATCH 05/17] chore(refactor): Save service with its own bus Signed-off-by: Max --- src/components/Editor.vue | 18 ++++------- src/composables/useSaveService.ts | 53 ++++++++++++++++++++++++++++--- src/composables/useSyncService.ts | 44 ++----------------------- src/services/SaveService.ts | 30 ++++++++++------- src/services/SyncService.ts | 3 -- 5 files changed, 75 insertions(+), 73 deletions(-) diff --git a/src/components/Editor.vue b/src/components/Editor.vue index 55cdd1f3c35..62f243fc04c 100644 --- a/src/components/Editor.vue +++ b/src/components/Editor.vue @@ -247,7 +247,7 @@ export default defineComponent({ props, ) const { connection, openConnection } = provideConnection(props) - const { document, syncService } = provideSyncService(connection, openConnection, setDirty) + const { syncService } = provideSyncService(connection, openConnection) const extensions = [ Autofocus.configure({ fileId: props.fileId }), Collaboration.configure({ document: ydoc }), @@ -282,12 +282,12 @@ export default defineComponent({ ) : () => serializePlainText(editor.state.doc) - const { saveService } = provideSaveService( + const { document, saveService } = provideSaveService( connection, - document, syncService, serialize, ydoc, + setDirty, ) const syncProvider = shallowRef(null) @@ -482,9 +482,9 @@ export default defineComponent({ bus.on('error', this.onError) bus.on('stateChange', this.onStateChange) bus.on('idle', this.onIdle) - bus.on('save', this.onSave) bus.on('permissionChange', this.onPermissionChange) - bus.on('changesPushed', this.onChangesPushed) + this.saveService.bus.on('error', this.onError) + this.saveService.bus.on('save', this.onSave) }, unlistenSyncServiceEvents() { @@ -495,9 +495,9 @@ export default defineComponent({ bus.off('error', this.onError) bus.off('stateChange', this.onStateChange) bus.off('idle', this.onIdle) - bus.off('save', this.onSave) bus.off('permissionChange', this.onPermissionChange) - bus.off('changesPushed', this.onChangesPushed) + this.saveService.bus.off('error', this.onError) + this.saveService.bus.off('save', this.onSave) }, reconnect() { @@ -678,10 +678,6 @@ export default defineComponent({ } }, - onChangesPushed() { - this.saveService.autosave() - }, - onFocus() { this.emit('focus') }, diff --git a/src/composables/useSaveService.ts b/src/composables/useSaveService.ts index fd6816caae7..dd825f60c18 100644 --- a/src/composables/useSaveService.ts +++ b/src/composables/useSaveService.ts @@ -3,12 +3,12 @@ * SPDX-License-Identifier: AGPL-3.0-or-later */ -import type { InjectionKey, Ref, ShallowRef } from 'vue' +import type { InjectionKey, ShallowRef } from 'vue' import type { Doc } from 'yjs' import type { Document, SyncService } from '../services/SyncService.ts' import type { Connection } from './useConnection.ts' -import { inject, provide } from 'vue' +import { computed, inject, onUnmounted, provide, ref, watch } from 'vue' import { getDocumentState } from '../helpers/yjs.ts' import { SaveService } from '../services/SaveService.ts' @@ -17,18 +17,19 @@ const saveServiceKey = Symbol('text:save') as InjectionKey /** * * @param connection to the api - * @param document as tracked by the sync service * @param syncService mostly used for the event bus and events * @param serialize to extract the document markdown content * @param ydoc to extract the document state from + * @param setDirty set the dirty state for the editor */ export function provideSaveService( connection: ShallowRef, - document: Ref, syncService: SyncService, serialize: () => string, ydoc: Doc, + setDirty: (val: boolean) => Promise, ) { + const document = ref() const saveService = new SaveService({ connection, document, @@ -36,8 +37,50 @@ export function provideSaveService( serialize, getDocumentState: () => getDocumentState(ydoc), }) + + syncService.bus.on('changesPushed', saveService.autosave) + onUnmounted(() => { + syncService.bus.off('changesPushed', saveService.autosave) + }) + + /** + * Update the document ref based on the event provided + * + * @param event that triggered the update + * @param event.document latest state of the document + */ + function updateDocument(event: { document: Document }) { + document.value = event.document + } + syncService.bus.on('opened', updateDocument) + syncService.bus.on('change', updateDocument) + saveService.bus.on('save', updateDocument) + onUnmounted(() => { + syncService.bus.off('opened', updateDocument) + syncService.bus.off('change', updateDocument) + saveService.bus.off('save', updateDocument) + }) + + const versionWithChanges = ref(0) + /** + * Update the tracked version based on the one in the event + * + * @param event that triggered the update + * @param event.version with changes pushed to the server + */ + function updateVersionWithChanges(event: { version: number }) { + versionWithChanges.value = Math.max(event.version, versionWithChanges.value) + } + syncService.bus.on('changesPushed', updateVersionWithChanges) + onUnmounted(() => { + syncService.bus.off('changesPushed', updateVersionWithChanges) + }) + + const dirty = computed(() => (document.value?.lastSavedVersion ?? 0) < versionWithChanges.value) + watch(dirty, setDirty) + provide(saveServiceKey, saveService) - return { saveService } + return { document, saveService } } /** diff --git a/src/composables/useSyncService.ts b/src/composables/useSyncService.ts index b759b068bdc..3a612f08c46 100644 --- a/src/composables/useSyncService.ts +++ b/src/composables/useSyncService.ts @@ -4,10 +4,9 @@ */ import type { InjectionKey, ShallowRef } from 'vue' -import type { Document } from '../services/SyncService.ts' import type { Connection, InitialData } from './useConnection.ts' -import { computed, inject, onUnmounted, provide, ref, watch } from 'vue' +import { inject, provide } from 'vue' import { SyncService } from '../services/SyncService.ts' const syncServiceKey = Symbol('text:sync') as InjectionKey @@ -17,12 +16,10 @@ const syncServiceKey = Symbol('text:sync') as InjectionKey * * @param connection Connection to the text api. * @param openConnection Function to open the connection. - * @param setDirty to udpate the dirty state. */ export function provideSyncService( connection: ShallowRef, openConnection: () => Promise, - setDirty: (val: boolean) => Promise, ) { const syncService = new SyncService({ connection, @@ -30,44 +27,7 @@ export function provideSyncService( }) provide(syncServiceKey, syncService) - const document = ref() - /** - * Update the document ref based on the event provided - * - * @param event that triggered the update - * @param event.document latest state of the document - */ - function updateDocument(event: { document: Document }) { - document.value = event.document - } - syncService.bus.on('opened', updateDocument) - syncService.bus.on('change', updateDocument) - syncService.bus.on('save', updateDocument) - onUnmounted(() => { - syncService.bus.off('opened', updateDocument) - syncService.bus.off('change', updateDocument) - syncService.bus.off('save', updateDocument) - }) - - const versionWithChanges = ref(0) - /** - * Update the tracked version based on the one in the event - * - * @param event that triggered the update - * @param event.version with changes pushed to the server - */ - function updateVersionWithChanges(event: { version: number }) { - versionWithChanges.value = Math.max(event.version, versionWithChanges.value) - } - syncService.bus.on('changesPushed', updateVersionWithChanges) - onUnmounted(() => { - syncService.bus.off('changesPushed', updateVersionWithChanges) - }) - - const dirty = computed(() => (document.value?.lastSavedVersion ?? 0) < versionWithChanges.value) - watch(dirty, setDirty) - - return { dirty, document, syncService } + return { syncService } } /** diff --git a/src/services/SaveService.ts b/src/services/SaveService.ts index e7e097798f2..e23d2930af4 100644 --- a/src/services/SaveService.ts +++ b/src/services/SaveService.ts @@ -9,6 +9,7 @@ import type { Document, SyncService } from './SyncService.ts' import { showError } from '@nextcloud/dialogs' import debounce from 'debounce' +import mitt from 'mitt' import { save, saveViaSendBeacon } from '../apis/save.ts' import { logger } from '../helpers/logger.js' import { ERROR_TYPE } from './SyncService.ts' @@ -21,7 +22,18 @@ const SERVER_AUTOSAVE_INTERVAL = 10 // Randomize save times to prevent all clients saving at the same time. const MAX_RANDOM_AUTOSAVE_DELAY = 3 +type ErrorType = (typeof ERROR_TYPE)[keyof typeof ERROR_TYPE] + +export declare type EventTypes = { + /* error */ + error: { type: ErrorType, data?: object } + + /* Emitted after successful save */ + save: { document: Document } +} + class SaveService { + bus = mitt() connection: ShallowRef document: Ref syncService @@ -57,14 +69,6 @@ class SaveService { return this.syncService.version } - get emit() { - return this.syncService.bus.emit - } - - _getContent() { - return this.serialize() - } - async save({ force = false, manualSave = true } = {}) { logger.debug('[SaveService] saving', { force, manualSave }) if (!this.connection.value) { @@ -74,13 +78,13 @@ class SaveService { try { const response = await save(this.connection.value, { version: this.version, - autosaveContent: this._getContent(), + autosaveContent: this.serialize(), documentState: this.getDocumentState(), force, manualSave, }) logger.debug('[SaveService] saved', { response }) - this.emit('save', response.data) + this.bus.emit('save', response.data) this.autosave.clear() } catch (e) { logger.error('Failed to save document.', { error: e }) @@ -92,7 +96,7 @@ class SaveService { return } if (response?.status === 412) { - this.emit('error', { + this.bus.emit('error', { type: ERROR_TYPE.LOAD_ERROR, data: response, }) @@ -110,7 +114,7 @@ class SaveService { } const success = saveViaSendBeacon(this.connection.value, { version: this.version, - autosaveContent: this._getContent(), + autosaveContent: this.serialize(), documentState: this.getDocumentState(), }) if (success) { @@ -135,6 +139,8 @@ class SaveService { logger.debug('Autosaving') return this.save({ manualSave: false }).catch((error) => { logger.error('Failed to autosave document.', { error }) + // retry in 30 seconds + this.autosave() }) } } diff --git a/src/services/SyncService.ts b/src/services/SyncService.ts index 6ce8227a2af..6f0590d9d39 100644 --- a/src/services/SyncService.ts +++ b/src/services/SyncService.ts @@ -124,9 +124,6 @@ export declare type EventTypes = { /* Events for session and document meta data */ change: { sessions: Session[]; document: Document } - /* Emitted after successful save */ - save: { document: Document } - /* Emitted once a document becomes idle */ idle: void From 1bfaf79395177e56d40aaac4b8c4741943977218 Mon Sep 17 00:00:00 2001 From: Max Date: Mon, 27 Jul 2026 07:15:29 +0200 Subject: [PATCH 06/17] chore(refactor): separate SaveService from SyncService and ydoc * `getSaveData` now prepares the data to send to the server. * `provideSaveService` now handles all the sync service events calling functions on `saveService` where needed. Signed-off-by: Max --- src/apis/save.ts | 9 +++++--- src/composables/useSaveService.ts | 19 +++++++++++++--- src/services/SaveService.ts | 38 ++++++++----------------------- 3 files changed, 32 insertions(+), 34 deletions(-) diff --git a/src/apis/save.ts b/src/apis/save.ts index 399726da38f..6b9cd4944ee 100644 --- a/src/apis/save.ts +++ b/src/apis/save.ts @@ -10,10 +10,13 @@ import { unref, type ShallowRef } from 'vue' import type { Connection } from '../composables/useConnection' import type { Document } from '../services/SyncService' -interface SaveData { +export interface SaveData { version: number autosaveContent: string documentState: string +} + +export interface SaveOptions { force: boolean manualSave: boolean } @@ -29,7 +32,7 @@ interface SaveResponse { */ export function save( connection: ShallowRef | Connection, - data: SaveData, + data: SaveData & SaveOptions, ): Promise { const con = unref(connection) const pub = con.shareToken ? '/public' : '' @@ -57,7 +60,7 @@ export function save( */ export function saveViaSendBeacon( connection: Connection, - data: Omit, + data: SaveData, ): boolean { const con = unref(connection) const pub = con.shareToken ? '/public' : '' diff --git a/src/composables/useSaveService.ts b/src/composables/useSaveService.ts index dd825f60c18..6c0aeddbb9a 100644 --- a/src/composables/useSaveService.ts +++ b/src/composables/useSaveService.ts @@ -5,6 +5,7 @@ import type { InjectionKey, ShallowRef } from 'vue' import type { Doc } from 'yjs' +import type { SaveData } from '../apis/save.ts' import type { Document, SyncService } from '../services/SyncService.ts' import type { Connection } from './useConnection.ts' @@ -30,17 +31,29 @@ export function provideSaveService( setDirty: (val: boolean) => Promise, ) { const document = ref() + + /** + * Get the data for the save request + */ + function getSaveData(): SaveData { + return { + version: syncService.version, + autosaveContent: serialize(), + documentState: getDocumentState(ydoc), + } + } + const saveService = new SaveService({ connection, document, - syncService, - serialize, - getDocumentState: () => getDocumentState(ydoc), + getSaveData, }) syncService.bus.on('changesPushed', saveService.autosave) + syncService.bus.on('close', saveService.clear) onUnmounted(() => { syncService.bus.off('changesPushed', saveService.autosave) + syncService.bus.off('close', saveService.clear) }) /** diff --git a/src/services/SaveService.ts b/src/services/SaveService.ts index e23d2930af4..d3b3d2734d0 100644 --- a/src/services/SaveService.ts +++ b/src/services/SaveService.ts @@ -4,8 +4,9 @@ */ import type { Ref, ShallowRef } from 'vue' +import type { SaveData } from '../apis/save.ts' import type { Connection } from '../composables/useConnection.ts' -import type { Document, SyncService } from './SyncService.ts' +import type { Document } from './SyncService.ts' import { showError } from '@nextcloud/dialogs' import debounce from 'debounce' @@ -36,37 +37,24 @@ class SaveService { bus = mitt() connection: ShallowRef document: Ref - syncService - serialize - getDocumentState + getSaveData autosave + clear constructor({ connection, document, - syncService, - serialize, - getDocumentState, + getSaveData, }: { connection: ShallowRef document: Ref - syncService: SyncService - serialize: () => string - getDocumentState: () => string + getSaveData: () => SaveData }) { this.connection = connection this.document = document - this.syncService = syncService - this.serialize = serialize - this.getDocumentState = getDocumentState + this.getSaveData = getSaveData this.autosave = debounce(this._autosave.bind(this), AUTOSAVE_DEBOUNCE * 1000) - this.syncService.bus.on('close', () => { - this.autosave.clear() - }) - } - - get version() { - return this.syncService.version + this.clear = this.autosave.clear.bind(this.autosave) } async save({ force = false, manualSave = true } = {}) { @@ -77,9 +65,7 @@ class SaveService { } try { const response = await save(this.connection.value, { - version: this.version, - autosaveContent: this.serialize(), - documentState: this.getDocumentState(), + ...this.getSaveData(), force, manualSave, }) @@ -112,11 +98,7 @@ class SaveService { if (!this.connection.value) { return } - const success = saveViaSendBeacon(this.connection.value, { - version: this.version, - autosaveContent: this.serialize(), - documentState: this.getDocumentState(), - }) + const success = saveViaSendBeacon(this.connection.value, this.getSaveData()) if (success) { logger.debug('[SaveService] saved using sendBeacon') } From c4715488c69529710747e70a04b304fdc21fe5b7 Mon Sep 17 00:00:00 2001 From: Max Date: Mon, 27 Jul 2026 07:55:07 +0200 Subject: [PATCH 07/17] fix(autosave): exponential delay for retries on error Signed-off-by: Max --- src/services/SaveService.ts | 21 +++++++++++++++------ 1 file changed, 15 insertions(+), 6 deletions(-) diff --git a/src/services/SaveService.ts b/src/services/SaveService.ts index d3b3d2734d0..953103d1139 100644 --- a/src/services/SaveService.ts +++ b/src/services/SaveService.ts @@ -22,6 +22,8 @@ const AUTOSAVE_DEBOUNCE = 1 const SERVER_AUTOSAVE_INTERVAL = 10 // Randomize save times to prevent all clients saving at the same time. const MAX_RANDOM_AUTOSAVE_DELAY = 3 +// First retry on error - interval will double with every retry. +const RETRY_TIMEOUT = SERVER_AUTOSAVE_INTERVAL type ErrorType = (typeof ERROR_TYPE)[keyof typeof ERROR_TYPE] @@ -37,6 +39,7 @@ class SaveService { bus = mitt() connection: ShallowRef document: Ref + autosaveErrorCount = 0 getSaveData autosave clear @@ -115,15 +118,21 @@ class SaveService { if (now < lastSave + SERVER_AUTOSAVE_INTERVAL) { logger.debug('Not autosaving as last save is recent', { lastSave, now }) const nextSave = lastSave + SERVER_AUTOSAVE_INTERVAL + Math.random() * MAX_RANDOM_AUTOSAVE_DELAY - setTimeout(() => this.autosave(), (nextSave - now) * 1000) + setTimeout(this.autosave, (nextSave - now) * 1000) return } logger.debug('Autosaving') - return this.save({ manualSave: false }).catch((error) => { - logger.error('Failed to autosave document.', { error }) - // retry in 30 seconds - this.autosave() - }) + return this.save({ manualSave: false }) + .then(() => { + this.autosaveErrorCount = 0 + }) + .catch((error) => { + logger.error('Failed to autosave document.', { error }) + // double the delay on every failed attempt + const delay = Math.pow(2, this.autosaveErrorCount) * RETRY_TIMEOUT + setTimeout(this.autosave, delay * 1000) + this.autosaveErrorCount++ + }) } } From 9eb25ab63bc31c318afad18f1aeed0bcb5187243 Mon Sep 17 00:00:00 2001 From: Max Date: Mon, 27 Jul 2026 08:11:45 +0200 Subject: [PATCH 08/17] fix(autosave): retry if server throttled save request The server will only perform one actual save every 10 seconds. Trigger another autosave if the save attempt was throttled. `document` is udpated by throttled save attempts. Rely on its `lastSavedVersionTime` for the retry. Signed-off-by: Max --- src/services/SaveService.ts | 28 ++++++++++++++++++++++++---- 1 file changed, 24 insertions(+), 4 deletions(-) diff --git a/src/services/SaveService.ts b/src/services/SaveService.ts index 953103d1139..7536e8a989f 100644 --- a/src/services/SaveService.ts +++ b/src/services/SaveService.ts @@ -60,21 +60,36 @@ class SaveService { this.clear = this.autosave.clear.bind(this.autosave) } + /** + * Save the current state + * + * @param options for saving + * @param options.force force save for handling conflicts + * @param options.manualSave user initiated the saving - not autosave + * @return true on success, false if autosave was throttled by the server + */ async save({ force = false, manualSave = true } = {}) { logger.debug('[SaveService] saving', { force, manualSave }) if (!this.connection.value) { logger.warn('Could not save due to missing connection') return } + const data = this.getSaveData() try { const response = await save(this.connection.value, { - ...this.getSaveData(), + ...data, force, manualSave, }) - logger.debug('[SaveService] saved', { response }) + // update the document - even if the save was throttled this.bus.emit('save', response.data) + if (response.data.document.lastSavedVersion < data.version) { + logger.debug('[SaveService] Server throttled save request.', { response }) + return false + } + logger.debug('[SaveService] saved', { response }) this.autosave.clear() + return true } catch (e) { logger.error('Failed to save document.', { error: e }) const response = ( @@ -122,9 +137,14 @@ class SaveService { return } logger.debug('Autosaving') - return this.save({ manualSave: false }) - .then(() => { + this.save({ manualSave: false }) + .then((saved) => { this.autosaveErrorCount = 0 + // server did not save due to throttling + if (saved === false) { + // document has been updated - let autosave handle the delay. + this.autosave() + } }) .catch((error) => { logger.error('Failed to autosave document.', { error }) From a6b811b1c60a22e8a1bcfe6621b8238215c43b0f Mon Sep 17 00:00:00 2001 From: Max Date: Mon, 27 Jul 2026 08:21:34 +0200 Subject: [PATCH 09/17] fix(autosave): handle out of sync clocks * If the server claims to have saved the doc in the future use the current timestamp instead. Autosave happens at least ten seconds after `lastSavedVersionTime`. So if that was far into the future it would never happen. * If the server is living in the past delay the autosave retries without relying on `lastSavedVersionTime`. Signed-off-by: Max --- src/composables/useSaveService.ts | 10 +++++++++- src/services/SaveService.ts | 4 ++-- 2 files changed, 11 insertions(+), 3 deletions(-) diff --git a/src/composables/useSaveService.ts b/src/composables/useSaveService.ts index 6c0aeddbb9a..7d1b3bfa9d0 100644 --- a/src/composables/useSaveService.ts +++ b/src/composables/useSaveService.ts @@ -63,7 +63,15 @@ export function provideSaveService( * @param event.document latest state of the document */ function updateDocument(event: { document: Document }) { - document.value = event.document + // Limit lastSavedVersionTime to now. No saving from the future. + const lastSavedVersionTime = Math.min( + event.document.lastSavedVersionTime, + Math.ceil(Date.now() / 1000), + ) + document.value = { + ...event.document, + lastSavedVersionTime, + } } syncService.bus.on('opened', updateDocument) syncService.bus.on('change', updateDocument) diff --git a/src/services/SaveService.ts b/src/services/SaveService.ts index 7536e8a989f..7202e555e61 100644 --- a/src/services/SaveService.ts +++ b/src/services/SaveService.ts @@ -142,8 +142,8 @@ class SaveService { this.autosaveErrorCount = 0 // server did not save due to throttling if (saved === false) { - // document has been updated - let autosave handle the delay. - this.autosave() + // Make sure to not hammer the server if clocks are out of sync. + setTimeout(this.autosave, SERVER_AUTOSAVE_INTERVAL * 1000) } }) .catch((error) => { From 906515c9beed728137a6ed7a0af587789ac3ccd4 Mon Sep 17 00:00:00 2001 From: Max Date: Tue, 28 Jul 2026 10:10:30 +0200 Subject: [PATCH 10/17] chore(test): always retry autosave after 10 seconds Adding a random offset makes determenistic testing harder. Now we also do not depend on in sync clocks. Signed-off-by: Max --- playwright/e2e/autosave.spec.ts | 85 ++++++++++++++++++++++ playwright/support/fixtures/upload-file.ts | 18 +++-- 2 files changed, 95 insertions(+), 8 deletions(-) create mode 100644 playwright/e2e/autosave.spec.ts diff --git a/playwright/e2e/autosave.spec.ts b/playwright/e2e/autosave.spec.ts new file mode 100644 index 00000000000..c9e39433333 --- /dev/null +++ b/playwright/e2e/autosave.spec.ts @@ -0,0 +1,85 @@ +/** + * SPDX-FileCopyrightText: 2025 Nextcloud GmbH and Nextcloud contributors + * SPDX-License-Identifier: AGPL-3.0-or-later + */ + +import { expect, mergeTests } from '@playwright/test' +import { test as editorTest } from '../support/fixtures/editor' +import { test as offlineTest } from '../support/fixtures/offline' +import { test as uploadFileTest } from '../support/fixtures/upload-file' + +const test = mergeTests(editorTest, offlineTest, uploadFileTest) + +// As we switch on and off the network +// we cannot run tests in parallel. +test.describe.configure({ mode: 'serial' }) + +// Files were created 10 seconds ago so there's no throttling to begin with. +test.use({ mtime: Date.now() / 1000 - 10 }) + +test.beforeEach(async ({ open }) => { + await open() +}) + +test('saves after 1 second', async ({ editor }) => { + await expect(editor.el).toBeVisible() + await editor.typeHeading('Hello world') + await expect(editor.saveIndicator).toHaveAccessibleName(/Unsaved changes/) + await expect(editor.saveIndicator).not.toHaveAccessibleName(/Unsaved changes/) + // TODO: Why does this not work? await expect(await file.getContent()).toBe('## Hello world') +}) + +/* + * 1 second autosave debounce + * 10 seconds waiting for server to be ready again + * 1 second for the save request + */ +test('saves again within 12 seconds', async ({ editor }) => { + test.slow() + await expect(editor.el).toBeVisible() + await editor.typeHeading('Hello') + await expect(editor.saveIndicator).toHaveAccessibleName(/Unsaved changes/) + await expect(editor.saveIndicator).not.toHaveAccessibleName(/Unsaved changes/) + await editor.type(' again') + await expect(editor.saveIndicator).toHaveAccessibleName(/Unsaved changes/) + await expect(editor.saveIndicator).not.toHaveAccessibleName(/Unsaved changes/, { timeout: 12_000 }) +}) + +test('saves after being disconnected for 5 sec.', async ({ + editor, + setOffline, + setOnline, +}) => { + await expect(editor.el).toBeVisible() + await editor.typeHeading('Hello') + await expect(editor.saveIndicator).not.toHaveAccessibleName(/Unsaved changes/) + await editor.type(' again') + await expect(editor.saveIndicator).toHaveAccessibleName(/Unsaved changes/) + await setOffline() + await new Promise((resolve) => setTimeout(resolve, 5_000)) + await setOnline() + await expect(editor.saveIndicator).toHaveAccessibleName(/Unsaved changes/) + await expect(editor.saveIndicator).not.toHaveAccessibleName(/Unsaved changes/, { timeout: 10_000 }) +}) + +test('saves after being disconnected for 2 minutes.', async ({ + editor, + page, + setOffline, + setOnline, +}) => { + await page.clock.install() + await expect(editor.el).toBeVisible() + await editor.typeHeading('Hello') + await expect(editor.saveIndicator).not.toHaveAccessibleName(/Unsaved changes/) + await editor.type(' again') + await expect(editor.saveIndicator).toHaveAccessibleName(/Unsaved changes/) + await setOffline() + await new Promise((resolve) => setTimeout(resolve, 5_000)) + await page.clock.fastForward(110_000) + await setOnline() + await new Promise((resolve) => setTimeout(resolve, 5_000)) + await page.clock.fastForward(5_000) + await expect(editor.saveIndicator).toHaveAccessibleName(/Unsaved changes/) + await expect(editor.saveIndicator).not.toHaveAccessibleName(/Unsaved changes/) +}) diff --git a/playwright/support/fixtures/upload-file.ts b/playwright/support/fixtures/upload-file.ts index 578b5283e79..a9d3694f923 100644 --- a/playwright/support/fixtures/upload-file.ts +++ b/playwright/support/fixtures/upload-file.ts @@ -3,14 +3,16 @@ * SPDX-License-Identifier: AGPL-3.0-or-later */ -import { test as base } from './random-user' -import { Node } from './Node' -import { ViewerSection } from '../sections/ViewerSection' +import type { Node } from './Node.ts' + +import { ViewerSection } from '../sections/ViewerSection.ts' +import { test as base } from './random-user.ts' export interface UploadFileFixture { file: Node fileName: string fileContent: string + mtime: number oldVersions: { content?: string, mtime: number }[] open: () => Promise close: () => Promise @@ -24,16 +26,16 @@ export interface UploadFileFixture { export const test = base.extend({ fileContent: ['', { option: true }], fileName: ['empty.md', { option: true }], + mtime: [undefined, { option: true }], oldVersions: [[], { option: true }], - file: async ({ fileContent, fileName, oldVersions, user }, use) => { - const uploadVersion = - (opts: { content?: string, mtime?: number }) => - user.uploadFile({ name: fileName, ...opts }) + file: async ({ fileContent, fileName, oldVersions, mtime, user }, use) => { + const uploadVersion + = (opts: { content?: string, mtime?: number }) => user.uploadFile({ name: fileName, ...opts }) for (const version of oldVersions) { await uploadVersion(version) } - const file = await uploadVersion({ content: fileContent }) + const file = await uploadVersion({ content: fileContent, mtime }) await use(file) }, From 458712b1e9c76c36d27ed85017fd9588c1680dad Mon Sep 17 00:00:00 2001 From: Max Date: Tue, 28 Jul 2026 10:12:17 +0200 Subject: [PATCH 11/17] fix(indexed-db): rely on autosave when recovering Autosave is triggered by the push of the steps. Saving before that delays the autosave because of server throttling. Signed-off-by: Max --- src/services/SaveService.ts | 9 +++------ 1 file changed, 3 insertions(+), 6 deletions(-) diff --git a/src/services/SaveService.ts b/src/services/SaveService.ts index 7202e555e61..6ba2fdfc50d 100644 --- a/src/services/SaveService.ts +++ b/src/services/SaveService.ts @@ -20,8 +20,6 @@ import { ERROR_TYPE } from './SyncService.ts' const AUTOSAVE_DEBOUNCE = 1 // Server only accepts auutosaves every 10 seconds const SERVER_AUTOSAVE_INTERVAL = 10 -// Randomize save times to prevent all clients saving at the same time. -const MAX_RANDOM_AUTOSAVE_DELAY = 3 // First retry on error - interval will double with every retry. const RETRY_TIMEOUT = SERVER_AUTOSAVE_INTERVAL @@ -131,9 +129,8 @@ class SaveService { const now = Date.now() / 1000 // Server won't accept autosaves yet if (now < lastSave + SERVER_AUTOSAVE_INTERVAL) { - logger.debug('Not autosaving as last save is recent', { lastSave, now }) - const nextSave = lastSave + SERVER_AUTOSAVE_INTERVAL + Math.random() * MAX_RANDOM_AUTOSAVE_DELAY - setTimeout(this.autosave, (nextSave - now) * 1000) + logger.debug('Just saved, will try again in 10 seconds.', { lastSave, now }) + setTimeout(this.autosave, (SERVER_AUTOSAVE_INTERVAL - AUTOSAVE_DEBOUNCE) * 1000) return } logger.debug('Autosaving') @@ -143,7 +140,7 @@ class SaveService { // server did not save due to throttling if (saved === false) { // Make sure to not hammer the server if clocks are out of sync. - setTimeout(this.autosave, SERVER_AUTOSAVE_INTERVAL * 1000) + setTimeout(this.autosave, (SERVER_AUTOSAVE_INTERVAL - AUTOSAVE_DEBOUNCE) * 1000) } }) .catch((error) => { From 1425a71167d2a377317defee5b8ab2fdc529e809 Mon Sep 17 00:00:00 2001 From: Max Date: Tue, 28 Jul 2026 10:35:25 +0200 Subject: [PATCH 12/17] chore(CI): playwright test timeout 45 seconds Some of our runners are slow. Give them more time to finish the runs. Also separate local and CI config in the config file. Signed-off-by: Max --- playwright.config.ts | 53 +++++++++++++++++++++++++++++--------------- 1 file changed, 35 insertions(+), 18 deletions(-) diff --git a/playwright.config.ts b/playwright.config.ts index e28e2c34e03..d16c75500fb 100644 --- a/playwright.config.ts +++ b/playwright.config.ts @@ -3,30 +3,44 @@ * SPDX-License-Identifier: AGPL-3.0-or-later */ +import type { ReporterDescription } from '@playwright/test' + import { defineConfig, devices } from '@playwright/test' +/** + * Used locally - i.e. if `CI` is not set as an environment variable. + */ +const LOCAL_CONFIG = { + // Just the html report with the traces + reporter: 'list', +} as const + +/** + * Used on CI - i.e. if `CI` is set as an environment variable. + */ +const CI_CONFIG = { + // ensure no `test.only` is left in the code causing false positives + forbidOnly: true, + // blob (so we can merge reports and download them for inspection), + // dot (so we have a quick overview in the logs while the tests are running) + // github (to have annotations in the PR) + reporter: [['blob'], ['line'], ['github']] as ReporterDescription[], + retries: 1, + timeout: 45_000, + // we shard to speed up the tests so no parallelism in workers + workers: 1, +} as const + /** * See https://playwright.dev/docs/test-configuration. */ export default defineConfig({ testDir: './playwright', - - /* Run tests in files in parallel */ - fullyParallel: true, - /* Fail the build on CI if you accidentally left test.only in the source code. */ - forbidOnly: !!process.env.CI, - /* Retry on CI only */ - retries: process.env.CI ? 2 : 0, - /* Opt out of parallel tests on CI. */ - workers: process.env.CI ? 1 : undefined, - /* Reporter to use. See https://playwright.dev/docs/test-reporters */ - reporter: process.env.CI ? 'github' : 'list', - /* Shared settings for all the projects below. See https://playwright.dev/docs/api/class-testoptions. */ + ...(process.env.CI ? CI_CONFIG : LOCAL_CONFIG), use: { - /* Base URL to use in actions like `await page.goto('./')`. */ + // Base URL to use in actions like `await page.goto('./')`. baseURL: process.env.baseURL ?? 'http://localhost:8089/index.php/', - - /* Collect trace when retrying the failed test. See https://playwright.dev/docs/trace-viewer */ + // record traces but only keep them when the test fails trace: 'on-first-retry', }, @@ -40,6 +54,8 @@ export default defineConfig({ ], webServer: { + // Don't set `url` as it would take precedence over `wait.stdout` and tests start too early + // url: 'http://127.0.0.1:8089', // Starts the Nextcloud docker container command: 'npm run start:nextcloud', // we use sigterm to notify the script to stop the container @@ -48,11 +64,12 @@ export default defineConfig({ signal: 'SIGTERM', timeout: 10000, }, - reuseExistingServer: !process.env.CI, + // `start-nextcloud-server.mjs` only starts the server if not reachable yet. + reuseExistingServer: false, stderr: 'pipe', stdout: 'pipe', - url: 'http://127.0.0.1:8089', - timeout: 5 * 60 * 1000, // max. 5 minutes for creating the container + // max. 5 minutes for creating the container + timeout: 5 * 60 * 1000, wait: { // we wait for this line to appear in the output of the webserver until consider it done stdout: /Nextcloud is now ready to use/, From c4264d5080d68da50ffb78497489a5af7c26c288 Mon Sep 17 00:00:00 2001 From: Max Date: Tue, 28 Jul 2026 10:42:16 +0200 Subject: [PATCH 13/17] chore(test): mtime is optional in upload file fixture Signed-off-by: Max --- playwright/support/fixtures/upload-file.ts | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/playwright/support/fixtures/upload-file.ts b/playwright/support/fixtures/upload-file.ts index a9d3694f923..53a5c3c301e 100644 --- a/playwright/support/fixtures/upload-file.ts +++ b/playwright/support/fixtures/upload-file.ts @@ -12,7 +12,7 @@ export interface UploadFileFixture { file: Node fileName: string fileContent: string - mtime: number + mtime?: number oldVersions: { content?: string, mtime: number }[] open: () => Promise close: () => Promise From b4d9550873238e75ab19f18a11833922c122c7e8 Mon Sep 17 00:00:00 2001 From: Max Date: Wed, 29 Jul 2026 08:08:00 +0200 Subject: [PATCH 14/17] fix(autosave): throttle to SERVER_AUTOSAVE_INTERVAL Only consider the local clock. Avoid problems if clocks are out of sync or if the server is unresponsive and `lastSavedVersionTime` does not get updated Signed-off-by: Max --- src/services/SaveService.ts | 40 +++++++++++++++++++++---------------- 1 file changed, 23 insertions(+), 17 deletions(-) diff --git a/src/services/SaveService.ts b/src/services/SaveService.ts index 6ba2fdfc50d..0992ea12d71 100644 --- a/src/services/SaveService.ts +++ b/src/services/SaveService.ts @@ -20,8 +20,6 @@ import { ERROR_TYPE } from './SyncService.ts' const AUTOSAVE_DEBOUNCE = 1 // Server only accepts auutosaves every 10 seconds const SERVER_AUTOSAVE_INTERVAL = 10 -// First retry on error - interval will double with every retry. -const RETRY_TIMEOUT = SERVER_AUTOSAVE_INTERVAL type ErrorType = (typeof ERROR_TYPE)[keyof typeof ERROR_TYPE] @@ -37,7 +35,8 @@ class SaveService { bus = mitt() connection: ShallowRef document: Ref - autosaveErrorCount = 0 + lastSaveAttempt = 0 + pendingAutosave = 0 getSaveData autosave clear @@ -55,7 +54,7 @@ class SaveService { this.document = document this.getSaveData = getSaveData this.autosave = debounce(this._autosave.bind(this), AUTOSAVE_DEBOUNCE * 1000) - this.clear = this.autosave.clear.bind(this.autosave) + this.clear = this.clearAutosave.bind(this) } /** @@ -74,6 +73,7 @@ class SaveService { } const data = this.getSaveData() try { + this.lastSaveAttempt = Date.now() const response = await save(this.connection.value, { ...data, force, @@ -86,7 +86,7 @@ class SaveService { return false } logger.debug('[SaveService] saved', { response }) - this.autosave.clear() + this.clearAutosave() return true } catch (e) { logger.error('Failed to save document.', { error: e }) @@ -125,32 +125,38 @@ class SaveService { } _autosave() { - const lastSave = this.document.value?.lastSavedVersionTime ?? 0 - const now = Date.now() / 1000 + const now = Date.now() + const nextSaveAttempt = this.lastSaveAttempt + SERVER_AUTOSAVE_INTERVAL * 1000 // Server won't accept autosaves yet - if (now < lastSave + SERVER_AUTOSAVE_INTERVAL) { - logger.debug('Just saved, will try again in 10 seconds.', { lastSave, now }) - setTimeout(this.autosave, (SERVER_AUTOSAVE_INTERVAL - AUTOSAVE_DEBOUNCE) * 1000) + if (now < nextSaveAttempt) { + if (!this.pendingAutosave) { + const wait = nextSaveAttempt - now + logger.debug(`Just saved, will try again in ${Math.ceil(wait)} seconds.`) + this.pendingAutosave = window.setTimeout(this.autosave, wait) + } return } logger.debug('Autosaving') this.save({ manualSave: false }) .then((saved) => { - this.autosaveErrorCount = 0 // server did not save due to throttling if (saved === false) { - // Make sure to not hammer the server if clocks are out of sync. - setTimeout(this.autosave, (SERVER_AUTOSAVE_INTERVAL - AUTOSAVE_DEBOUNCE) * 1000) + this.autosave() } }) .catch((error) => { logger.error('Failed to autosave document.', { error }) - // double the delay on every failed attempt - const delay = Math.pow(2, this.autosaveErrorCount) * RETRY_TIMEOUT - setTimeout(this.autosave, delay * 1000) - this.autosaveErrorCount++ + this.autosave() }) } + + clearAutosave() { + this.autosave.clear() + if (this.pendingAutosave) { + window.clearTimeout(this.pendingAutosave) + this.pendingAutosave = 0 + } + } } export { SaveService } From e3fc23603c19bcde35694ad0385629e04c1dda5b Mon Sep 17 00:00:00 2001 From: Max Date: Wed, 29 Jul 2026 10:11:40 +0200 Subject: [PATCH 15/17] chore(tests): fix autosave test with new timing Signed-off-by: Max --- playwright/e2e/autosave.spec.ts | 9 ++++++--- src/services/SyncService.ts | 1 + 2 files changed, 7 insertions(+), 3 deletions(-) diff --git a/playwright/e2e/autosave.spec.ts b/playwright/e2e/autosave.spec.ts index c9e39433333..0d44ebe69d8 100644 --- a/playwright/e2e/autosave.spec.ts +++ b/playwright/e2e/autosave.spec.ts @@ -71,15 +71,18 @@ test('saves after being disconnected for 2 minutes.', async ({ await page.clock.install() await expect(editor.el).toBeVisible() await editor.typeHeading('Hello') + await expect(editor.saveIndicator).toHaveAccessibleName(/Unsaved changes/) await expect(editor.saveIndicator).not.toHaveAccessibleName(/Unsaved changes/) await editor.type(' again') await expect(editor.saveIndicator).toHaveAccessibleName(/Unsaved changes/) await setOffline() - await new Promise((resolve) => setTimeout(resolve, 5_000)) + // Wait long enough for the server throttling to be over. + await new Promise((resolve) => setTimeout(resolve, 10_000)) await page.clock.fastForward(110_000) await setOnline() - await new Promise((resolve) => setTimeout(resolve, 5_000)) - await page.clock.fastForward(5_000) + await expect(editor.offlineState).not.toBeVisible() await expect(editor.saveIndicator).toHaveAccessibleName(/Unsaved changes/) + // Be sure to trigger at least one autosave + await page.clock.fastForward(15_000) await expect(editor.saveIndicator).not.toHaveAccessibleName(/Unsaved changes/) }) diff --git a/src/services/SyncService.ts b/src/services/SyncService.ts index 6f0590d9d39..9c4d14fb0b3 100644 --- a/src/services/SyncService.ts +++ b/src/services/SyncService.ts @@ -268,6 +268,7 @@ class SyncService { if (hadUpdate) { // this.version has been increased in receiveSteps this.bus.emit('changesPushed', { version: this.version }) + logger.debug('changesPushed', { version: this.version }) } } }) From d44512b46e008d1dabfee1b8df2612a7f0f0a59e Mon Sep 17 00:00:00 2001 From: Max Date: Tue, 4 Aug 2026 16:26:44 +0200 Subject: [PATCH 16/17] chore(lint): fix tsc, prettier and linter after backport Allow importing with ts extensions. This way we can backport changes to the import statements more easily. Signed-off-by: Max --- playwright/e2e/autosave.spec.ts | 8 ++++++-- src/apis/save.ts | 5 +---- src/components/Editor.vue | 2 +- src/composables/useSaveService.ts | 4 +++- src/services/SaveService.ts | 15 ++++++++++----- src/services/SyncService.ts | 8 +------- tsconfig.json | 1 + 7 files changed, 23 insertions(+), 20 deletions(-) diff --git a/playwright/e2e/autosave.spec.ts b/playwright/e2e/autosave.spec.ts index 0d44ebe69d8..d0c7d229aa6 100644 --- a/playwright/e2e/autosave.spec.ts +++ b/playwright/e2e/autosave.spec.ts @@ -42,7 +42,9 @@ test('saves again within 12 seconds', async ({ editor }) => { await expect(editor.saveIndicator).not.toHaveAccessibleName(/Unsaved changes/) await editor.type(' again') await expect(editor.saveIndicator).toHaveAccessibleName(/Unsaved changes/) - await expect(editor.saveIndicator).not.toHaveAccessibleName(/Unsaved changes/, { timeout: 12_000 }) + await expect(editor.saveIndicator).not.toHaveAccessibleName(/Unsaved changes/, { + timeout: 12_000, + }) }) test('saves after being disconnected for 5 sec.', async ({ @@ -59,7 +61,9 @@ test('saves after being disconnected for 5 sec.', async ({ await new Promise((resolve) => setTimeout(resolve, 5_000)) await setOnline() await expect(editor.saveIndicator).toHaveAccessibleName(/Unsaved changes/) - await expect(editor.saveIndicator).not.toHaveAccessibleName(/Unsaved changes/, { timeout: 10_000 }) + await expect(editor.saveIndicator).not.toHaveAccessibleName(/Unsaved changes/, { + timeout: 10_000, + }) }) test('saves after being disconnected for 2 minutes.', async ({ diff --git a/src/apis/save.ts b/src/apis/save.ts index 6b9cd4944ee..6d6f8502018 100644 --- a/src/apis/save.ts +++ b/src/apis/save.ts @@ -58,10 +58,7 @@ export function save( * @param connection the active connection * @param data data to save */ -export function saveViaSendBeacon( - connection: Connection, - data: SaveData, -): boolean { +export function saveViaSendBeacon(connection: Connection, data: SaveData): boolean { const con = unref(connection) const pub = con.shareToken ? '/public' : '' const url = generateUrl(`apps/text${pub}/session/${con.documentId}/save`) diff --git a/src/components/Editor.vue b/src/components/Editor.vue index 62f243fc04c..d23df30ec99 100644 --- a/src/components/Editor.vue +++ b/src/components/Editor.vue @@ -568,7 +568,7 @@ export default defineComponent({ }) }, - onSync({ steps }) { + onSync() { this.hasConnectionIssue = this.syncService.backend.fetcher === 0 || !this.syncProvider?.wsconnected diff --git a/src/composables/useSaveService.ts b/src/composables/useSaveService.ts index 7d1b3bfa9d0..10015c6577e 100644 --- a/src/composables/useSaveService.ts +++ b/src/composables/useSaveService.ts @@ -97,7 +97,9 @@ export function provideSaveService( syncService.bus.off('changesPushed', updateVersionWithChanges) }) - const dirty = computed(() => (document.value?.lastSavedVersion ?? 0) < versionWithChanges.value) + const dirty = computed( + () => (document.value?.lastSavedVersion ?? 0) < versionWithChanges.value, + ) watch(dirty, setDirty) provide(saveServiceKey, saveService) diff --git a/src/services/SaveService.ts b/src/services/SaveService.ts index 0992ea12d71..8795da2197b 100644 --- a/src/services/SaveService.ts +++ b/src/services/SaveService.ts @@ -25,7 +25,7 @@ type ErrorType = (typeof ERROR_TYPE)[keyof typeof ERROR_TYPE] export declare type EventTypes = { /* error */ - error: { type: ErrorType, data?: object } + error: { type: ErrorType; data?: object } /* Emitted after successful save */ save: { document: Document } @@ -82,7 +82,9 @@ class SaveService { // update the document - even if the save was throttled this.bus.emit('save', response.data) if (response.data.document.lastSavedVersion < data.version) { - logger.debug('[SaveService] Server throttled save request.', { response }) + logger.debug('[SaveService] Server throttled save request.', { + response, + }) return false } logger.debug('[SaveService] saved', { response }) @@ -91,7 +93,7 @@ class SaveService { } catch (e) { logger.error('Failed to save document.', { error: e }) const response = ( - e as { response?: { status?: number, data?: { error?: string } } } + e as { response?: { status?: number; data?: { error?: string } } } ).response if (response?.status === 403) { // Document is now read-only; permissionChange from sync will update the UI @@ -126,12 +128,15 @@ class SaveService { _autosave() { const now = Date.now() - const nextSaveAttempt = this.lastSaveAttempt + SERVER_AUTOSAVE_INTERVAL * 1000 + const nextSaveAttempt = + this.lastSaveAttempt + SERVER_AUTOSAVE_INTERVAL * 1000 // Server won't accept autosaves yet if (now < nextSaveAttempt) { if (!this.pendingAutosave) { const wait = nextSaveAttempt - now - logger.debug(`Just saved, will try again in ${Math.ceil(wait)} seconds.`) + logger.debug( + `Just saved, will try again in ${Math.ceil(wait)} seconds.`, + ) this.pendingAutosave = window.setTimeout(this.autosave, wait) } return diff --git a/src/services/SyncService.ts b/src/services/SyncService.ts index 9c4d14fb0b3..0194f45f72d 100644 --- a/src/services/SyncService.ts +++ b/src/services/SyncService.ts @@ -305,13 +305,7 @@ class SyncService { }) } - receiveSteps({ - steps, - sessions = [], - }: { - steps: Step[] - sessions?: Session[] - }) { + receiveSteps({ steps, sessions = [] }: { steps: Step[]; sessions?: Session[] }) { const versionAfter = Math.max(this.version, ...steps.map((s) => s.version)) this.bus.emit('sync', { steps: [...awarenessSteps(sessions), ...steps], diff --git a/tsconfig.json b/tsconfig.json index a35e901aa1c..140c30a3bb5 100644 --- a/tsconfig.json +++ b/tsconfig.json @@ -1,6 +1,7 @@ { "extends": "@vue/tsconfig", "compilerOptions": { + "allowImportingTsExtensions": true, "allowSyntheticDefaultImports": true, "allowJs": true, "declaration": true, From e011a8e72f54a1c940830dea41734c29841a026d Mon Sep 17 00:00:00 2001 From: Jonas Date: Wed, 5 Aug 2026 11:11:43 +0200 Subject: [PATCH 17/17] chore(deps): update dependency cypress to ^15.19.0 Signed-off-by: Jonas --- package-lock.json | 533 +++++++++------------------------------------- package.json | 3 +- 2 files changed, 105 insertions(+), 431 deletions(-) diff --git a/package-lock.json b/package-lock.json index fc28a39cb94..740b5798a9b 100644 --- a/package-lock.json +++ b/package-lock.json @@ -94,12 +94,13 @@ "@nextcloud/vite-config": "^1.7.2", "@playwright/test": "^1.60.0", "@types/markdown-it": "^14.1.2", + "@types/node": "^26.1.2", "@vitejs/plugin-vue2": "^2.3.4", "@vitest/coverage-v8": "^4.1.10", "@vue/test-utils": "^1.3.0 <2", "@vue/tsconfig": "^0.5.1", "@vueuse/core": "^11.3.0", - "cypress": "^15.12.0", + "cypress": "^15.19.0", "cypress-split": "^1.24.31", "cypress-vite": "^1.8.0", "eslint-config-prettier": "^10.1.8", @@ -255,7 +256,6 @@ "integrity": "sha512-Aup7aUOfpbAUg2ROOJN6Iw5f9DMBlzu0mIkm/malLQFN/YQgO48wCj0Kxa3sEHJvPVFg7siR+qRInwXd2qhQKw==", "dev": true, "license": "MIT", - "peer": true, "dependencies": { "@babel/helper-validator-identifier": "^7.29.7", "js-tokens": "^4.0.0", @@ -271,7 +271,6 @@ "integrity": "sha512-locTkQyKvwIEgBzVrn8693ebc97F2U8ZHjbXwDXJ5Fn2TCpNwTlKcaKLkdHop5c/icOFE7qt7Q9JC5hnKNa6Gg==", "dev": true, "license": "MIT", - "peer": true, "engines": { "node": ">=6.9.0" } @@ -334,7 +333,6 @@ "integrity": "sha512-DkXD5OJQaAQIdZ1bt3UZdEnHAn9Imd3IVBdX03UFe+ony9Ojw5pzr9YVKGDY1jt+Gcn/FnGkNf8r+Vj5NOJWtQ==", "dev": true, "license": "MIT", - "peer": true, "dependencies": { "@babel/parser": "^7.29.7", "@babel/types": "^7.29.7", @@ -352,7 +350,6 @@ "integrity": "sha512-fXSwMQqitTGeHLBC08Eq5yXz2m37E4pJX1qAU1+2cNedz/ifv/bVXft90VeSav5nFO61EcNgwr0aJxbyPaWBPg==", "dev": true, "license": "MIT", - "peer": true, "dependencies": { "@babel/types": "^7.27.3" }, @@ -366,7 +363,6 @@ "integrity": "sha512-wem6WaBj4NaVYVdNhLPPVacES6ZJ+KBBfSkTMD3YZxbP3rm3Di85tJU5ljaUNhaOynt+Aj0xruhYuzQBt8n71g==", "dev": true, "license": "MIT", - "peer": true, "dependencies": { "@babel/compat-data": "^7.29.7", "@babel/helper-validator-option": "^7.29.7", @@ -384,7 +380,6 @@ "integrity": "sha512-q3WC4JfdODypvxArsJQROfupPBq9+lMwjKq7C33GhbFYJsufD0yd/ziwD+hJucLeWsnFPWZjsU2DNFqBPE7jwQ==", "dev": true, "license": "MIT", - "peer": true, "dependencies": { "@babel/helper-annotate-as-pure": "^7.27.3", "@babel/helper-member-expression-to-functions": "^7.28.5", @@ -407,7 +402,6 @@ "integrity": "sha512-N1EhvLtHzOvj7QQOUCCS3NrPJP8c5W6ZXCHDn7Yialuy1iu4r5EmIYkXlKNqT99Ciw+W0mDqWoR6HWMZlFP3hw==", "dev": true, "license": "MIT", - "peer": true, "dependencies": { "@babel/helper-annotate-as-pure": "^7.27.3", "regexpu-core": "^6.3.1", @@ -426,7 +420,6 @@ "integrity": "sha512-uJnGFcPsWQK8fvjgGP5LZUZZsYGIoPeRjSF5PGwrelYgq7Q15/Ft9NGFp1zglwgIv//W0uG4BevRuSJRyylZPg==", "dev": true, "license": "MIT", - "peer": true, "dependencies": { "@babel/helper-compilation-targets": "^7.27.2", "@babel/helper-plugin-utils": "^7.27.1", @@ -444,7 +437,6 @@ "integrity": "sha512-3nQVUAtvkKH9zahfWgw96Jc/uFOmjACE1kQz82E2lqWmHBgjzbNlsC22nuQTfahmWeQtTq5nQ/4Nnd2A1wj4zA==", "dev": true, "license": "MIT", - "peer": true, "engines": { "node": ">=6.9.0" } @@ -455,7 +447,6 @@ "integrity": "sha512-cwM7SBRZcPCLgl8a7cY0soT1SptSzAlMH39vwiRpOQkJlh53r5hdHwLSCZpQdVLT39sZt+CRpNwYG4Y2v77atg==", "dev": true, "license": "MIT", - "peer": true, "dependencies": { "@babel/traverse": "^7.28.5", "@babel/types": "^7.28.5" @@ -470,7 +461,6 @@ "integrity": "sha512-ejHwrQQYcm9xnTivShn2IDOlIzInN34AXskvq9QicvCtEzq1Vzclu/tKF8Jq1Cg8JG2GL6/EmjgsCT7lXepE3g==", "dev": true, "license": "MIT", - "peer": true, "dependencies": { "@babel/traverse": "^7.29.7", "@babel/types": "^7.29.7" @@ -485,7 +475,6 @@ "integrity": "sha512-UPUVSyXbOh627KiCIGQSgwWzGeBKLkaJ9PJEdrngIwMSzxLR4jS4+f1f1jb7VzBbg8nFLaYotvVPFCTqdrmTAg==", "dev": true, "license": "MIT", - "peer": true, "dependencies": { "@babel/helper-module-imports": "^7.29.7", "@babel/helper-validator-identifier": "^7.29.7", @@ -504,7 +493,6 @@ "integrity": "sha512-URMGH08NzYFhubNSGJrpUEphGKQwMQYBySzat5cAByY1/YgIRkULnIy3tAMeszlL/so2HbeilYloUmSpd7GdVw==", "dev": true, "license": "MIT", - "peer": true, "dependencies": { "@babel/types": "^7.27.1" }, @@ -528,7 +516,6 @@ "integrity": "sha512-7fiA521aVw8lSPeI4ZOD3vRFkoqkJcS+z4hFo82bFSH/2tNd6eJ5qCVMS5OzDmZh/kaHQeBaeyxK6wljcPtveA==", "dev": true, "license": "MIT", - "peer": true, "dependencies": { "@babel/helper-annotate-as-pure": "^7.27.1", "@babel/helper-wrap-function": "^7.27.1", @@ -547,7 +534,6 @@ "integrity": "sha512-7EHz6qDZc8RYS5ElPoShMheWvEgERonFCs7IAonWLLUTXW59DP14bCZt89/GKyreYn8g3S83m21FelHKbeDCKA==", "dev": true, "license": "MIT", - "peer": true, "dependencies": { "@babel/helper-member-expression-to-functions": "^7.27.1", "@babel/helper-optimise-call-expression": "^7.27.1", @@ -566,7 +552,6 @@ "integrity": "sha512-Tub4ZKEXqbPjXgWLl2+3JpQAYBJ8+ikpQ2Ocj/q/r0LwE3UhENh7EUabyHjz2kCEsrRY83ew2DQdHluuiDQFzg==", "dev": true, "license": "MIT", - "peer": true, "dependencies": { "@babel/traverse": "^7.27.1", "@babel/types": "^7.27.1" @@ -599,7 +584,6 @@ "integrity": "sha512-N9ZErrD+yW5geCDtBqnOoxmR8+tNKiGuxKlDpuJxfsqpa2dFcexaziGAE/qoHLiDDreVNMupxGmSoNlyvsA3gw==", "dev": true, "license": "MIT", - "peer": true, "engines": { "node": ">=6.9.0" } @@ -610,7 +594,6 @@ "integrity": "sha512-zdf983tNfLZFletc0RRXYrHrucBEg95NIFMkn6K9dbeMYnsgHaSBGcQqdsCSStG2PYwRre0Qc2NNSCXbG+xc6g==", "dev": true, "license": "MIT", - "peer": true, "dependencies": { "@babel/template": "^7.27.2", "@babel/traverse": "^7.28.3", @@ -626,7 +609,6 @@ "integrity": "sha512-1k2lAGRMfHTcwuNYcCNUmaUffmQv8KWMfh2iJUUeRlwlwH4FdNG7mfPI10NPfLHJFThE4Tyr4mv7kTNZOiPuBg==", "dev": true, "license": "MIT", - "peer": true, "dependencies": { "@babel/template": "^7.29.7", "@babel/types": "^7.29.7" @@ -656,7 +638,6 @@ "integrity": "sha512-87GDMS3tsmMSi/3bWOte1UblL+YUTFMV8SZPZ2eSEL17s74Cw/l63rR6NmGVKMYW2GYi85nE+/d6Hw5N0bEk2Q==", "dev": true, "license": "MIT", - "peer": true, "dependencies": { "@babel/helper-plugin-utils": "^7.27.1", "@babel/traverse": "^7.28.5" @@ -674,7 +655,6 @@ "integrity": "sha512-qNeq3bCKnGgLkEXUuFry6dPlGfCdQNZbn7yUAPCInwAJHMU7THJfrBSozkcWq5sNM6RcF3S8XyQL2A52KNR9IA==", "dev": true, "license": "MIT", - "peer": true, "dependencies": { "@babel/helper-plugin-utils": "^7.27.1" }, @@ -691,7 +671,6 @@ "integrity": "sha512-g4L7OYun04N1WyqMNjldFwlfPCLVkgB54A/YCXICZYBsvJJE3kByKv9c9+R/nAfmIfjl2rKYLNyMHboYbZaWaA==", "dev": true, "license": "MIT", - "peer": true, "dependencies": { "@babel/helper-plugin-utils": "^7.27.1" }, @@ -708,7 +687,6 @@ "integrity": "sha512-oO02gcONcD5O1iTLi/6frMJBIwWEHceWGSGqrpCmEL8nogiS6J9PBlE48CaK20/Jx1LuRml9aDftLgdjXT8+Cw==", "dev": true, "license": "MIT", - "peer": true, "dependencies": { "@babel/helper-plugin-utils": "^7.27.1", "@babel/helper-skip-transparent-expression-wrappers": "^7.27.1", @@ -727,7 +705,6 @@ "integrity": "sha512-b6YTX108evsvE4YgWyQ921ZAFFQm3Bn+CA3+ZXlNVnPhx+UfsVURoPjfGAPCjBgrqo30yX/C2nZGX96DxvR9Iw==", "dev": true, "license": "MIT", - "peer": true, "dependencies": { "@babel/helper-plugin-utils": "^7.27.1", "@babel/traverse": "^7.28.3" @@ -745,7 +722,6 @@ "integrity": "sha512-SOSkfJDddaM7mak6cPEpswyTRnuRltl429hMraQEglW+OkovnCzsiszTmsrlY//qLFjCpQDFRvjdm2wA5pPm9w==", "dev": true, "license": "MIT", - "peer": true, "engines": { "node": ">=6.9.0" }, @@ -773,7 +749,6 @@ "integrity": "sha512-UT/Jrhw57xg4ILHLFnzFpPDlMbcdEicaAtjPQpbj9wa8T4r5KVWCimHcL/460g8Ht0DMxDyjsLgiWSkVjnwPFg==", "dev": true, "license": "MIT", - "peer": true, "dependencies": { "@babel/helper-plugin-utils": "^7.27.1" }, @@ -790,7 +765,6 @@ "integrity": "sha512-oFT0FrKHgF53f4vOsZGi2Hh3I35PfSmVs4IBFLFj4dnafP+hIWDLg3VyKmUHfLoLHlyxY4C7DGtmHuJgn+IGww==", "dev": true, "license": "MIT", - "peer": true, "dependencies": { "@babel/helper-plugin-utils": "^7.27.1" }, @@ -823,7 +797,6 @@ "integrity": "sha512-727YkEAPwSIQTv5im8QHz3upqp92JTWhidIC81Tdx4VJYIte/VndKf1qKrfnnhPLiPghStWfvC/iFaMCQu7Nqg==", "dev": true, "license": "MIT", - "peer": true, "dependencies": { "@babel/helper-create-regexp-features-plugin": "^7.18.6", "@babel/helper-plugin-utils": "^7.18.6" @@ -841,7 +814,6 @@ "integrity": "sha512-8Z4TGic6xW70FKThA5HYEKKyBpOOsucTOD1DjU3fZxDg+K3zBJcXMFnt/4yQiZnf5+MiOMSXQ9PaEK/Ilh1DeA==", "dev": true, "license": "MIT", - "peer": true, "dependencies": { "@babel/helper-plugin-utils": "^7.27.1" }, @@ -858,7 +830,6 @@ "integrity": "sha512-BEOdvX4+M765icNPZeidyADIvQ1m1gmunXufXxvRESy/jNNyfovIqUyE7MVgGBjWktCoJlzvFA1To2O4ymIO3Q==", "dev": true, "license": "MIT", - "peer": true, "dependencies": { "@babel/helper-plugin-utils": "^7.27.1", "@babel/helper-remap-async-to-generator": "^7.27.1", @@ -877,7 +848,6 @@ "integrity": "sha512-NREkZsZVJS4xmTr8qzE5y8AfIPqsdQfRuUiLRTEzb7Qii8iFWCyDKaUV2c0rCuh4ljDZ98ALHP/PetiBV2nddA==", "dev": true, "license": "MIT", - "peer": true, "dependencies": { "@babel/helper-module-imports": "^7.27.1", "@babel/helper-plugin-utils": "^7.27.1", @@ -896,7 +866,6 @@ "integrity": "sha512-cnqkuOtZLapWYZUYM5rVIdv1nXYuFVIltZ6ZJ7nIj585QsjKM5dhL2Fu/lICXZ1OyIAFc7Qy+bvDAtTXqGrlhg==", "dev": true, "license": "MIT", - "peer": true, "dependencies": { "@babel/helper-plugin-utils": "^7.27.1" }, @@ -913,7 +882,6 @@ "integrity": "sha512-45DmULpySVvmq9Pj3X9B+62Xe+DJGov27QravQJU1LLcapR6/10i+gYVAucGGJpHBp5mYxIMK4nDAT/QDLr47g==", "dev": true, "license": "MIT", - "peer": true, "dependencies": { "@babel/helper-plugin-utils": "^7.27.1" }, @@ -930,7 +898,6 @@ "integrity": "sha512-D0VcalChDMtuRvJIu3U/fwWjf8ZMykz5iZsg77Nuj821vCKI3zCyRLwRdWbsuJ/uRwZhZ002QtCqIkwC/ZkvbA==", "dev": true, "license": "MIT", - "peer": true, "dependencies": { "@babel/helper-create-class-features-plugin": "^7.27.1", "@babel/helper-plugin-utils": "^7.27.1" @@ -948,7 +915,6 @@ "integrity": "sha512-LtPXlBbRoc4Njl/oh1CeD/3jC+atytbnf/UqLoqTDcEYGUPj022+rvfkbDYieUrSj3CaV4yHDByPE+T2HwfsJg==", "dev": true, "license": "MIT", - "peer": true, "dependencies": { "@babel/helper-create-class-features-plugin": "^7.28.3", "@babel/helper-plugin-utils": "^7.27.1" @@ -966,7 +932,6 @@ "integrity": "sha512-cFOlhIYPBv/iBoc+KS3M6et2XPtbT2HiCRfBXWtfpc9OAyostldxIf9YAYB6ypURBBbx+Qv6nyrLzASfJe+hBA==", "dev": true, "license": "MIT", - "peer": true, "dependencies": { "@babel/helper-annotate-as-pure": "^7.27.3", "@babel/helper-compilation-targets": "^7.27.2", @@ -988,7 +953,6 @@ "integrity": "sha512-lj9PGWvMTVksbWiDT2tW68zGS/cyo4AkZ/QTp0sQT0mjPopCmrSkzxeXkznjqBxzDI6TclZhOJbBmbBLjuOZUw==", "dev": true, "license": "MIT", - "peer": true, "dependencies": { "@babel/helper-plugin-utils": "^7.27.1", "@babel/template": "^7.27.1" @@ -1006,7 +970,6 @@ "integrity": "sha512-Kl9Bc6D0zTUcFUvkNuQh4eGXPKKNDOJQXVyyM4ZAQPMveniJdxi8XMJwLo+xSoW3MIq81bD33lcUe9kZpl0MCw==", "dev": true, "license": "MIT", - "peer": true, "dependencies": { "@babel/helper-plugin-utils": "^7.27.1", "@babel/traverse": "^7.28.5" @@ -1024,7 +987,6 @@ "integrity": "sha512-gEbkDVGRvjj7+T1ivxrfgygpT7GUd4vmODtYpbs0gZATdkX8/iSnOtZSxiZnsgm1YjTgjI6VKBGSJJevkrclzw==", "dev": true, "license": "MIT", - "peer": true, "dependencies": { "@babel/helper-create-regexp-features-plugin": "^7.27.1", "@babel/helper-plugin-utils": "^7.27.1" @@ -1042,7 +1004,6 @@ "integrity": "sha512-MTyJk98sHvSs+cvZ4nOauwTTG1JeonDjSGvGGUNHreGQns+Mpt6WX/dVzWBHgg+dYZhkC4X+zTDfkTU+Vy9y7Q==", "dev": true, "license": "MIT", - "peer": true, "dependencies": { "@babel/helper-plugin-utils": "^7.27.1" }, @@ -1059,7 +1020,6 @@ "integrity": "sha512-hkGcueTEzuhB30B3eJCbCYeCaaEQOmQR0AdvzpD4LoN0GXMWzzGSuRrxR2xTnCrvNbVwK9N6/jQ92GSLfiZWoQ==", "dev": true, "license": "MIT", - "peer": true, "dependencies": { "@babel/helper-create-regexp-features-plugin": "^7.27.1", "@babel/helper-plugin-utils": "^7.27.1" @@ -1077,7 +1037,6 @@ "integrity": "sha512-MHzkWQcEmjzzVW9j2q8LGjwGWpG2mjwaaB0BNQwst3FIjqsg8Ct/mIZlvSPJvfi9y2AC8mi/ktxbFVL9pZ1I4A==", "dev": true, "license": "MIT", - "peer": true, "dependencies": { "@babel/helper-plugin-utils": "^7.27.1" }, @@ -1094,7 +1053,6 @@ "integrity": "sha512-K8nhUcn3f6iB+P3gwCv/no7OdzOZQcKchW6N389V6PD8NUWKZHzndOd9sPDVbMoBsbmjMqlB4L9fm+fEFNVlwQ==", "dev": true, "license": "MIT", - "peer": true, "dependencies": { "@babel/helper-plugin-utils": "^7.27.1", "@babel/plugin-transform-destructuring": "^7.28.0" @@ -1112,7 +1070,6 @@ "integrity": "sha512-D4WIMaFtwa2NizOp+dnoFjRez/ClKiC2BqqImwKd1X28nqBtZEyCYJ2ozQrrzlxAFrcrjxo39S6khe9RNDlGzw==", "dev": true, "license": "MIT", - "peer": true, "dependencies": { "@babel/helper-plugin-utils": "^7.27.1" }, @@ -1129,7 +1086,6 @@ "integrity": "sha512-tQvHWSZ3/jH2xuq/vZDy0jNn+ZdXJeM8gHvX4lnJmsc3+50yPlWdZXIc5ay+umX+2/tJIqHqiEqcJvxlmIvRvQ==", "dev": true, "license": "MIT", - "peer": true, "dependencies": { "@babel/helper-plugin-utils": "^7.27.1" }, @@ -1146,7 +1102,6 @@ "integrity": "sha512-BfbWFFEJFQzLCQ5N8VocnCtA8J1CLkNTe2Ms2wocj75dd6VpiqS5Z5quTYcUoo4Yq+DN0rtikODccuv7RU81sw==", "dev": true, "license": "MIT", - "peer": true, "dependencies": { "@babel/helper-plugin-utils": "^7.27.1", "@babel/helper-skip-transparent-expression-wrappers": "^7.27.1" @@ -1164,7 +1119,6 @@ "integrity": "sha512-1bQeydJF9Nr1eBCMMbC+hdwmRlsv5XYOMu03YSWFwNs0HsAmtSxxF1fyuYPqemVldVyFmlCU7w8UE14LupUSZQ==", "dev": true, "license": "MIT", - "peer": true, "dependencies": { "@babel/helper-compilation-targets": "^7.27.1", "@babel/helper-plugin-utils": "^7.27.1", @@ -1183,7 +1137,6 @@ "integrity": "sha512-6WVLVJiTjqcQauBhn1LkICsR2H+zm62I3h9faTDKt1qP4jn2o72tSvqMwtGFKGTpojce0gJs+76eZ2uCHRZh0Q==", "dev": true, "license": "MIT", - "peer": true, "dependencies": { "@babel/helper-plugin-utils": "^7.27.1" }, @@ -1200,7 +1153,6 @@ "integrity": "sha512-0HCFSepIpLTkLcsi86GG3mTUzxV5jpmbv97hTETW3yzrAij8aqlD36toB1D0daVFJM8NK6GvKO0gslVQmm+zZA==", "dev": true, "license": "MIT", - "peer": true, "dependencies": { "@babel/helper-plugin-utils": "^7.27.1" }, @@ -1217,7 +1169,6 @@ "integrity": "sha512-axUuqnUTBuXyHGcJEVVh9pORaN6wC5bYfE7FGzPiaWa3syib9m7g+/IT/4VgCOe2Upef43PHzeAvcrVek6QuuA==", "dev": true, "license": "MIT", - "peer": true, "dependencies": { "@babel/helper-plugin-utils": "^7.27.1" }, @@ -1234,7 +1185,6 @@ "integrity": "sha512-hqoBX4dcZ1I33jCSWcXrP+1Ku7kdqXf1oeah7ooKOIiAdKQ+uqftgCFNOSzA5AMS2XIHEYeGFg4cKRCdpxzVOQ==", "dev": true, "license": "MIT", - "peer": true, "dependencies": { "@babel/helper-plugin-utils": "^7.27.1" }, @@ -1251,7 +1201,6 @@ "integrity": "sha512-iCsytMg/N9/oFq6n+gFTvUYDZQOMK5kEdeYxmxt91fcJGycfxVP9CnrxoliM0oumFERba2i8ZtwRUCMhvP1LnA==", "dev": true, "license": "MIT", - "peer": true, "dependencies": { "@babel/helper-module-transforms": "^7.27.1", "@babel/helper-plugin-utils": "^7.27.1" @@ -1269,7 +1218,6 @@ "integrity": "sha512-OJguuwlTYlN0gBZFRPqwOGNWssZjfIUdS7HMYtN8c1KmwpwHFBwTeFZrg9XZa+DFTitWOW5iTAG7tyCUPsCCyw==", "dev": true, "license": "MIT", - "peer": true, "dependencies": { "@babel/helper-module-transforms": "^7.27.1", "@babel/helper-plugin-utils": "^7.27.1" @@ -1287,7 +1235,6 @@ "integrity": "sha512-N7QmZ0xRZfjHOfZeQLJjwgX2zS9pdGHSVl/cjSGlo4dXMqvurfxXDMKY4RqEKzPozV78VMcd0lxyG13mlbKc4w==", "dev": true, "license": "MIT", - "peer": true, "dependencies": { "@babel/helper-module-transforms": "^7.28.6", "@babel/helper-plugin-utils": "^7.28.6", @@ -1307,7 +1254,6 @@ "integrity": "sha512-iQBE/xC5BV1OxJbp6WG7jq9IWiD+xxlZhLrdwpPkTX3ydmXdvoCpyfJN7acaIBZaOqTfr76pgzqBJflNbeRK+w==", "dev": true, "license": "MIT", - "peer": true, "dependencies": { "@babel/helper-module-transforms": "^7.27.1", "@babel/helper-plugin-utils": "^7.27.1" @@ -1325,7 +1271,6 @@ "integrity": "sha512-SstR5JYy8ddZvD6MhV0tM/j16Qds4mIpJTOd1Yu9J9pJjH93bxHECF7pgtc28XvkzTD6Pxcm/0Z73Hvk7kb3Ng==", "dev": true, "license": "MIT", - "peer": true, "dependencies": { "@babel/helper-create-regexp-features-plugin": "^7.27.1", "@babel/helper-plugin-utils": "^7.27.1" @@ -1343,7 +1288,6 @@ "integrity": "sha512-f6PiYeqXQ05lYq3TIfIDu/MtliKUbNwkGApPUvyo6+tc7uaR4cPjPe7DFPr15Uyycg2lZU6btZ575CuQoYh7MQ==", "dev": true, "license": "MIT", - "peer": true, "dependencies": { "@babel/helper-plugin-utils": "^7.27.1" }, @@ -1360,7 +1304,6 @@ "integrity": "sha512-aGZh6xMo6q9vq1JGcw58lZ1Z0+i0xB2x0XaauNIUXd6O1xXc3RwoWEBlsTQrY4KQ9Jf0s5rgD6SiNkaUdJegTA==", "dev": true, "license": "MIT", - "peer": true, "dependencies": { "@babel/helper-plugin-utils": "^7.27.1" }, @@ -1377,7 +1320,6 @@ "integrity": "sha512-fdPKAcujuvEChxDBJ5c+0BTaS6revLV7CJL08e4m3de8qJfNIuCc2nc7XJYOjBoTMJeqSmwXJ0ypE14RCjLwaw==", "dev": true, "license": "MIT", - "peer": true, "dependencies": { "@babel/helper-plugin-utils": "^7.27.1" }, @@ -1394,7 +1336,6 @@ "integrity": "sha512-373KA2HQzKhQCYiRVIRr+3MjpCObqzDlyrM6u4I201wL8Mp2wHf7uB8GhDwis03k2ti8Zr65Zyyqs1xOxUF/Ew==", "dev": true, "license": "MIT", - "peer": true, "dependencies": { "@babel/helper-compilation-targets": "^7.27.2", "@babel/helper-plugin-utils": "^7.27.1", @@ -1415,7 +1356,6 @@ "integrity": "sha512-SFy8S9plRPbIcxlJ8A6mT/CxFdJx/c04JEctz4jf8YZaVS2px34j7NXRrlGlHkN/M2gnpL37ZpGRGVFLd3l8Ng==", "dev": true, "license": "MIT", - "peer": true, "dependencies": { "@babel/helper-plugin-utils": "^7.27.1", "@babel/helper-replace-supers": "^7.27.1" @@ -1433,7 +1373,6 @@ "integrity": "sha512-txEAEKzYrHEX4xSZN4kJ+OfKXFVSWKB2ZxM9dpcE3wT7smwkNmXo5ORRlVzMVdJbD+Q8ILTgSD7959uj+3Dm3Q==", "dev": true, "license": "MIT", - "peer": true, "dependencies": { "@babel/helper-plugin-utils": "^7.27.1" }, @@ -1450,7 +1389,6 @@ "integrity": "sha512-N6fut9IZlPnjPwgiQkXNhb+cT8wQKFlJNqcZkWlcTqkcqx6/kU4ynGmLFoa4LViBSirn05YAwk+sQBbPfxtYzQ==", "dev": true, "license": "MIT", - "peer": true, "dependencies": { "@babel/helper-plugin-utils": "^7.27.1", "@babel/helper-skip-transparent-expression-wrappers": "^7.27.1" @@ -1468,7 +1406,6 @@ "integrity": "sha512-qBkYTYCb76RRxUM6CcZA5KRu8K4SM8ajzVeUgVdMVO9NN9uI/GaVmBg/WKJJGnNokV9SY8FxNOVWGXzqzUidBg==", "dev": true, "license": "MIT", - "peer": true, "dependencies": { "@babel/helper-plugin-utils": "^7.27.1" }, @@ -1485,7 +1422,6 @@ "integrity": "sha512-10FVt+X55AjRAYI9BrdISN9/AQWHqldOeZDUoLyif1Kn05a56xVBXb8ZouL8pZ9jem8QpXaOt8TS7RHUIS+GPA==", "dev": true, "license": "MIT", - "peer": true, "dependencies": { "@babel/helper-create-class-features-plugin": "^7.27.1", "@babel/helper-plugin-utils": "^7.27.1" @@ -1503,7 +1439,6 @@ "integrity": "sha512-5J+IhqTi1XPa0DXF83jYOaARrX+41gOewWbkPyjMNRDqgOCqdffGh8L3f/Ek5utaEBZExjSAzcyjmV9SSAWObQ==", "dev": true, "license": "MIT", - "peer": true, "dependencies": { "@babel/helper-annotate-as-pure": "^7.27.1", "@babel/helper-create-class-features-plugin": "^7.27.1", @@ -1522,7 +1457,6 @@ "integrity": "sha512-oThy3BCuCha8kDZ8ZkgOg2exvPYUlprMukKQXI1r1pJ47NCvxfkEy8vK+r/hT9nF0Aa4H1WUPZZjHTFtAhGfmQ==", "dev": true, "license": "MIT", - "peer": true, "dependencies": { "@babel/helper-plugin-utils": "^7.27.1" }, @@ -1539,7 +1473,6 @@ "integrity": "sha512-+ZEdQlBoRg9m2NnzvEeLgtvBMO4tkFBw5SQIUgLICgTrumLoU7lr+Oghi6km2PFj+dbUt2u1oby2w3BDO9YQnA==", "dev": true, "license": "MIT", - "peer": true, "dependencies": { "@babel/helper-plugin-utils": "^7.27.1" }, @@ -1556,7 +1489,6 @@ "integrity": "sha512-TtEciroaiODtXvLZv4rmfMhkCv8jx3wgKpL68PuiPh2M4fvz5jhsA7697N1gMvkvr/JTF13DrFYyEbY9U7cVPA==", "dev": true, "license": "MIT", - "peer": true, "dependencies": { "@babel/helper-create-regexp-features-plugin": "^7.27.1", "@babel/helper-plugin-utils": "^7.27.1" @@ -1574,7 +1506,6 @@ "integrity": "sha512-V2ABPHIJX4kC7HegLkYoDpfg9PVmuWy/i6vUM5eGK22bx4YVFD3M5F0QQnWQoDs6AGsUWTVOopBiMFQgHaSkVw==", "dev": true, "license": "MIT", - "peer": true, "dependencies": { "@babel/helper-plugin-utils": "^7.27.1" }, @@ -1591,7 +1522,6 @@ "integrity": "sha512-N/wH1vcn4oYawbJ13Y/FxcQrWk63jhfNa7jef0ih7PHSIHX2LB7GWE1rkPrOnka9kwMxb6hMl19p7lidA+EHmQ==", "dev": true, "license": "MIT", - "peer": true, "dependencies": { "@babel/helper-plugin-utils": "^7.27.1" }, @@ -1608,7 +1538,6 @@ "integrity": "sha512-kpb3HUqaILBJcRFVhFUs6Trdd4mkrzcGXss+6/mxUd273PfbWqSDHRzMT2234gIg2QYfAjvXLSquP1xECSg09Q==", "dev": true, "license": "MIT", - "peer": true, "dependencies": { "@babel/helper-plugin-utils": "^7.27.1", "@babel/helper-skip-transparent-expression-wrappers": "^7.27.1" @@ -1626,7 +1555,6 @@ "integrity": "sha512-lhInBO5bi/Kowe2/aLdBAawijx+q1pQzicSgnkB6dUPc1+RC8QmJHKf2OjvU+NZWitguJHEaEmbV6VWEouT58g==", "dev": true, "license": "MIT", - "peer": true, "dependencies": { "@babel/helper-plugin-utils": "^7.27.1" }, @@ -1643,7 +1571,6 @@ "integrity": "sha512-fBJKiV7F2DxZUkg5EtHKXQdbsbURW3DZKQUWphDum0uRP6eHGGa/He9mc0mypL680pb+e/lDIthRohlv8NCHkg==", "dev": true, "license": "MIT", - "peer": true, "dependencies": { "@babel/helper-plugin-utils": "^7.27.1" }, @@ -1660,7 +1587,6 @@ "integrity": "sha512-RiSILC+nRJM7FY5srIyc4/fGIwUhyDuuBSdWn4y6yT6gm652DpCHZjIipgn6B7MQ1ITOUnAKWixEUjQRIBIcLw==", "dev": true, "license": "MIT", - "peer": true, "dependencies": { "@babel/helper-plugin-utils": "^7.27.1" }, @@ -1677,7 +1603,6 @@ "integrity": "sha512-Ysg4v6AmF26k9vpfFuTZg8HRfVWzsh1kVfowA23y9j/Gu6dOuahdUVhkLqpObp3JIv27MLSii6noRnuKN8H0Mg==", "dev": true, "license": "MIT", - "peer": true, "dependencies": { "@babel/helper-plugin-utils": "^7.27.1" }, @@ -1694,7 +1619,6 @@ "integrity": "sha512-uW20S39PnaTImxp39O5qFlHLS9LJEmANjMG7SxIhap8rCHqu0Ik+tLEPX5DKmHn6CsWQ7j3lix2tFOa5YtL12Q==", "dev": true, "license": "MIT", - "peer": true, "dependencies": { "@babel/helper-create-regexp-features-plugin": "^7.27.1", "@babel/helper-plugin-utils": "^7.27.1" @@ -1712,7 +1636,6 @@ "integrity": "sha512-xvINq24TRojDuyt6JGtHmkVkrfVV3FPT16uytxImLeBZqW3/H52yN+kM1MGuyPkIQxrzKwPHs5U/MP3qKyzkGw==", "dev": true, "license": "MIT", - "peer": true, "dependencies": { "@babel/helper-create-regexp-features-plugin": "^7.27.1", "@babel/helper-plugin-utils": "^7.27.1" @@ -1730,7 +1653,6 @@ "integrity": "sha512-EtkOujbc4cgvb0mlpQefi4NTPBzhSIevblFevACNLUspmrALgmEBdL/XfnyyITfd8fKBZrZys92zOWcik7j9Tw==", "dev": true, "license": "MIT", - "peer": true, "dependencies": { "@babel/helper-create-regexp-features-plugin": "^7.27.1", "@babel/helper-plugin-utils": "^7.27.1" @@ -1834,7 +1756,6 @@ "integrity": "sha512-HrcgcIESLm9aIR842yhJ5RWan/gebQUJ6E/E5+rf0y9o6oj7w0Br+sWuL6kEQ/o/AdfvR1Je9jG18/gnpwjEyA==", "dev": true, "license": "MIT", - "peer": true, "dependencies": { "@babel/helper-plugin-utils": "^7.0.0", "@babel/types": "^7.4.4", @@ -1859,7 +1780,6 @@ "integrity": "sha512-puq+Gf35oI24FeN11LkoUQFqv9uwNeWpxXZi/Ji3rRIoKAzKnxRaZ+Gkj0vKS9ZCiTESfng1N9LyOyXvo+m+Gg==", "dev": true, "license": "MIT", - "peer": true, "dependencies": { "@babel/code-frame": "^7.29.7", "@babel/parser": "^7.29.7", @@ -1875,7 +1795,6 @@ "integrity": "sha512-EhlfNQtZ+NK22w5BM61ciuiq1m58ed33Wr1Xan//ZRTy6hgjnwyCffRYwzsGXdASJSUJ1guZILsErh1eQcl+zw==", "dev": true, "license": "MIT", - "peer": true, "dependencies": { "@babel/code-frame": "^7.29.7", "@babel/generator": "^7.29.7", @@ -2028,6 +1947,7 @@ } ], "license": "MIT", + "peer": true, "engines": { "node": ">=18" }, @@ -2074,6 +1994,7 @@ } ], "license": "MIT", + "peer": true, "engines": { "node": ">=18" } @@ -2149,7 +2070,6 @@ "dev": true, "license": "MIT", "optional": true, - "peer": true, "dependencies": { "@emnapi/wasi-threads": "1.2.0", "tslib": "^2.4.0" @@ -2162,7 +2082,6 @@ "dev": true, "license": "MIT", "optional": true, - "peer": true, "dependencies": { "tslib": "^2.4.0" } @@ -2174,7 +2093,6 @@ "dev": true, "license": "MIT", "optional": true, - "peer": true, "dependencies": { "tslib": "^2.4.0" } @@ -2185,7 +2103,6 @@ "integrity": "sha512-aKUhyn1QI5Ksbqcr3fFJj16p99QdjUxXAEuFst1Z47DRyoiMwivIH9MV/ARcJOCXVjPfjITciej8ZD2O/6qUmw==", "dev": true, "license": "MIT", - "peer": true, "dependencies": { "comment-parser": "1.4.1", "esquery": "^1.5.0", @@ -2643,7 +2560,6 @@ "integrity": "sha512-ayVFHdtZ+hsq1t2Dy24wCmGXGe4q9Gu3smhLYALJrr473ZH27MsnSL+LKUlimp4BWJqMDMLmPpx/Q9R3OAlL4g==", "dev": true, "license": "MIT", - "peer": true, "dependencies": { "eslint-visitor-keys": "^3.4.3" }, @@ -2663,7 +2579,6 @@ "integrity": "sha512-wpc+LXeiyiisxPlEkUzU6svyS1frIO3Mgxj1fdy7Pm8Ygzguax2N3Fa/D/ag1WqbOprdI+uY6wMUl8/a2G+iag==", "dev": true, "license": "Apache-2.0", - "peer": true, "engines": { "node": "^12.22.0 || ^14.17.0 || >=16.0.0" }, @@ -2677,7 +2592,6 @@ "integrity": "sha512-EriSTlt5OC9/7SXkRSCAhfSxxoSUgBm33OH+IkwbdpgoqsSsUg7y3uh+IICI/Qg4BBWr3U2i39RpmycbxMq4ew==", "dev": true, "license": "MIT", - "peer": true, "engines": { "node": "^12.0.0 || ^14.0.0 || >=16.0.0" } @@ -2688,7 +2602,6 @@ "integrity": "sha512-269Z39MS6wVJtsoUl10L60WdkhJVdPG24Q4eZTH3nnF6lpvSShEK3wQjDX9JRWAUPvPh7COouPpU9IrqaZFvtQ==", "dev": true, "license": "MIT", - "peer": true, "dependencies": { "ajv": "^6.12.4", "debug": "^4.3.2", @@ -2713,7 +2626,6 @@ "integrity": "sha512-Edep/X9fGqVNmzKBVsDYIOtD+z1tuezV70LBjdCst9Tqu76lsnvRiZ6oTic1n+/BIwX6QDGAO94PN4N2SADvtw==", "dev": true, "license": "MIT", - "peer": true, "dependencies": { "balanced-match": "^1.0.0", "concat-map": "0.0.1" @@ -2725,7 +2637,6 @@ "integrity": "sha512-VgjWUsnnT6n+NUk6eZq77zeFdpW2LWDzP6zFGrCbHXiYNul5Dzqk2HHQ5uFH2DNW5Xbp8+jVzaeNt94ssEEl4w==", "dev": true, "license": "ISC", - "peer": true, "dependencies": { "brace-expansion": "^1.1.7" }, @@ -2739,7 +2650,6 @@ "integrity": "sha512-d9zaMRSTIKDLhctzH12MtXvJKSSUhaHcjV+2Z+GK+EEY7XKpP5yR4x+N3TAcHTcu963nIr+TMcCb4DBCYX1z6Q==", "dev": true, "license": "MIT", - "peer": true, "engines": { "node": "^12.22.0 || ^14.17.0 || >=16.0.0" } @@ -2786,6 +2696,7 @@ "resolved": "https://registry.npmjs.org/@floating-ui/dom/-/dom-1.7.6.tgz", "integrity": "sha512-9gZSAI5XM36880PPMm//9dfiEngYoC6Am2izES1FF406YFsjvyBMmeJ2g4SAju3xWwtuynNRFL2s9hgxpLI5SQ==", "license": "MIT", + "peer": true, "dependencies": { "@floating-ui/core": "^1.7.5", "@floating-ui/utils": "^0.2.11" @@ -2857,7 +2768,6 @@ "integrity": "sha512-DZLEEqFWQFiyK6h5YIeynKx7JlvCYWL0cImfSRXZ9l4Sg2efkFGTuFf6vzXjK1cq6IYkU+Eg/JizXw+TD2vRNw==", "dev": true, "license": "Apache-2.0", - "peer": true, "dependencies": { "@humanwhocodes/object-schema": "^2.0.3", "debug": "^4.3.1", @@ -2873,7 +2783,6 @@ "integrity": "sha512-Edep/X9fGqVNmzKBVsDYIOtD+z1tuezV70LBjdCst9Tqu76lsnvRiZ6oTic1n+/BIwX6QDGAO94PN4N2SADvtw==", "dev": true, "license": "MIT", - "peer": true, "dependencies": { "balanced-match": "^1.0.0", "concat-map": "0.0.1" @@ -2885,7 +2794,6 @@ "integrity": "sha512-VgjWUsnnT6n+NUk6eZq77zeFdpW2LWDzP6zFGrCbHXiYNul5Dzqk2HHQ5uFH2DNW5Xbp8+jVzaeNt94ssEEl4w==", "dev": true, "license": "ISC", - "peer": true, "dependencies": { "brace-expansion": "^1.1.7" }, @@ -2899,7 +2807,6 @@ "integrity": "sha512-bxveV4V8v5Yb4ncFTT3rPSgZBOpCkjfK0y4oVVVJwIuDVBRMDXrPyXRL988i5ap9m9bnyEEjWfm5WkBmtffLfA==", "dev": true, "license": "Apache-2.0", - "peer": true, "engines": { "node": ">=12.22" }, @@ -2913,8 +2820,7 @@ "resolved": "https://registry.npmjs.org/@humanwhocodes/object-schema/-/object-schema-2.0.3.tgz", "integrity": "sha512-93zYdMES/c1D69yZiKDBj0V24vqNzB/koF26KPaagAfd3P/4gUlh3Dys5ogAK+Exi9QyzlD8x/08Zt7wIKcDcA==", "dev": true, - "license": "BSD-3-Clause", - "peer": true + "license": "BSD-3-Clause" }, "node_modules/@iconify/types": { "version": "2.0.0", @@ -3059,7 +2965,6 @@ "integrity": "sha512-2kkt/7niJ6MgEPxF0bYdQ6etZaA+fQvDcLKckhy1yIQOzaoKjBBjSj63/aLVjYE3qhRt5dvM+uUyfCg6UKCBbA==", "dev": true, "license": "MIT", - "peer": true, "dependencies": { "@jridgewell/sourcemap-codec": "^1.5.0", "@jridgewell/trace-mapping": "^0.3.24" @@ -3071,7 +2976,6 @@ "integrity": "sha512-LI9u/+laYG4Ds1TDKSJW2YPrIlcVYOwi2fUC6xB43lueCjgxV4lffOCZCtYFiH6TNOX+tQKXx97T4IKHbhyHEQ==", "dev": true, "license": "MIT", - "peer": true, "dependencies": { "@jridgewell/gen-mapping": "^0.3.5", "@jridgewell/trace-mapping": "^0.3.24" @@ -3358,7 +3262,6 @@ "dev": true, "license": "MIT", "optional": true, - "peer": true, "dependencies": { "@emnapi/core": "^1.4.3", "@emnapi/runtime": "^1.4.3", @@ -3571,7 +3474,6 @@ "integrity": "sha512-RX+0FxpL1h2EzjNLeW0VSGTkbyWIq7WgV7QAjtyUmDbSGwf1ds9Zy5OcRkgXRHRIu/W0gB0DhS2iz9qXHphCzA==", "dev": true, "license": "ISC", - "peer": true, "dependencies": { "fast-xml-parser": "^4.2.5", "requireindex": "^1.2.0", @@ -3597,7 +3499,6 @@ } ], "license": "MIT", - "peer": true, "dependencies": { "strnum": "^1.0.5" }, @@ -3611,7 +3512,6 @@ "integrity": "sha512-SdsKMrI9TdgjdweUSR9MweHA4EJ8YxHn8DFaDisvhVlUOe4BF1tLD7GAj0lIqWVl+dPb/rExr0Btby5loQm20Q==", "dev": true, "license": "ISC", - "peer": true, "bin": { "semver": "bin/semver.js" }, @@ -3630,8 +3530,7 @@ "url": "https://github.com/sponsors/NaturalIntelligence" } ], - "license": "MIT", - "peer": true + "license": "MIT" }, "node_modules/@nextcloud/event-bus": { "version": "3.3.3", @@ -3887,6 +3786,7 @@ "resolved": "https://registry.npmjs.org/@nextcloud/vue/-/vue-8.40.0.tgz", "integrity": "sha512-jc00yzNPXGVgEwnP0b9DVIXPt/JOQDLmCxLHUwuTnuDXfppB92LBzxPOJ/L0rkHYqKRsk+gg1cHKZp9zZFDQww==", "license": "AGPL-3.0-or-later", + "peer": true, "dependencies": { "@floating-ui/dom": "^1.7.6", "@linusborg/vue-simple-portal": "^0.1.5", @@ -4008,7 +3908,6 @@ "integrity": "sha512-54/JRvkLIzzDWshCWfuhadfrfZVPiElY8Fcgmg1HroEly/EDSszzhBAsarCux+D/kOslTRquNzuyGSmUSTTHGg==", "dev": true, "license": "MIT", - "peer": true, "dependencies": { "eslint-scope": "5.1.1" } @@ -4069,7 +3968,6 @@ "integrity": "sha512-nn5ozdjYQpUCZlWGuxcJY/KpxkWQs4DcbMCmKojjyrYDEAGy4Ce19NN4v5MduafTwJlbKc99UA8YhSVqq9yPZA==", "dev": true, "license": "MIT", - "peer": true, "engines": { "node": ">=12.4.0" } @@ -4089,7 +3987,6 @@ "hasInstallScript": true, "license": "MIT", "optional": true, - "peer": true, "dependencies": { "detect-libc": "^1.0.3", "is-glob": "^4.0.3", @@ -4132,7 +4029,6 @@ "os": [ "android" ], - "peer": true, "engines": { "node": ">= 10.0.0" }, @@ -4154,7 +4050,6 @@ "os": [ "darwin" ], - "peer": true, "engines": { "node": ">= 10.0.0" }, @@ -4176,7 +4071,6 @@ "os": [ "darwin" ], - "peer": true, "engines": { "node": ">= 10.0.0" }, @@ -4198,7 +4092,6 @@ "os": [ "freebsd" ], - "peer": true, "engines": { "node": ">= 10.0.0" }, @@ -4220,7 +4113,6 @@ "os": [ "linux" ], - "peer": true, "engines": { "node": ">= 10.0.0" }, @@ -4242,7 +4134,6 @@ "os": [ "linux" ], - "peer": true, "engines": { "node": ">= 10.0.0" }, @@ -4264,7 +4155,6 @@ "os": [ "linux" ], - "peer": true, "engines": { "node": ">= 10.0.0" }, @@ -4286,7 +4176,6 @@ "os": [ "linux" ], - "peer": true, "engines": { "node": ">= 10.0.0" }, @@ -4308,7 +4197,6 @@ "os": [ "linux" ], - "peer": true, "engines": { "node": ">= 10.0.0" }, @@ -4330,7 +4218,6 @@ "os": [ "linux" ], - "peer": true, "engines": { "node": ">= 10.0.0" }, @@ -4352,7 +4239,6 @@ "os": [ "win32" ], - "peer": true, "engines": { "node": ">= 10.0.0" }, @@ -4374,7 +4260,6 @@ "os": [ "win32" ], - "peer": true, "engines": { "node": ">= 10.0.0" }, @@ -4396,7 +4281,6 @@ "os": [ "win32" ], - "peer": true, "engines": { "node": ">= 10.0.0" }, @@ -4886,8 +4770,7 @@ "resolved": "https://registry.npmjs.org/@rtsao/scc/-/scc-1.1.0.tgz", "integrity": "sha512-zt6OdqaDoOnJ1ZYsCYGt9YmWzDXl4vQdKTyJev62gFhRGKdx7mcT54V9KIjg+d2wi9EXsPvAPKe7i7WjfVWB8g==", "dev": true, - "license": "MIT", - "peer": true + "license": "MIT" }, "node_modules/@rushstack/node-core-library": { "version": "5.21.0", @@ -4920,6 +4803,7 @@ "integrity": "sha512-PlXPeEWMXMZ7sPYOHqmDyCJzcfNrUr3fGNKtezX14ykXOEIvyK81d+qydx89KY5O71FKMPaQ2vBfBFI5NHR63A==", "dev": true, "license": "MIT", + "peer": true, "dependencies": { "fast-deep-equal": "^3.1.3", "fast-uri": "^3.0.1", @@ -5085,6 +4969,7 @@ "resolved": "https://registry.npmjs.org/@tiptap/core/-/core-3.22.4.tgz", "integrity": "sha512-vGIGm/HpqLg8EAAQXQ+koV+/S828OEpzocfWcPOwo1u2QUVf9dQG47Yy6JJ8zFFaJwfv4dBcOXli+7BrJwsxDQ==", "license": "MIT", + "peer": true, "funding": { "type": "github", "url": "https://github.com/sponsors/ueberdosis" @@ -5155,6 +5040,7 @@ "resolved": "https://registry.npmjs.org/@tiptap/extension-code-block/-/extension-code-block-3.15.3.tgz", "integrity": "sha512-q1UB9icNfdJppTqMIUWfoRKkx5SSdWIpwZoL2NeOI5Ah3E20/dQKVttIgLhsE521chyvxCYCRaHD5tMNGKfhyw==", "license": "MIT", + "peer": true, "funding": { "type": "github", "url": "https://github.com/sponsors/ueberdosis" @@ -5186,6 +5072,7 @@ "resolved": "https://registry.npmjs.org/@tiptap/extension-collaboration/-/extension-collaboration-3.22.4.tgz", "integrity": "sha512-4g4DdpvXZyYYCcWs3cO4DwtzhukqI13waYUKfwOcNnQwZ4YOCR83ET/kgqMk/xMAfbymKghbyAZRCc33zD5xvw==", "license": "MIT", + "peer": true, "funding": { "type": "github", "url": "https://github.com/sponsors/ueberdosis" @@ -5230,6 +5117,7 @@ "resolved": "https://registry.npmjs.org/@tiptap/extension-drag-handle/-/extension-drag-handle-3.22.4.tgz", "integrity": "sha512-VCheiqy0OGrcrxOYt7Kp7MXWMZoEaZp517HnRrdrvcCCO0j2hcrT5mbo1mMPzP/WvTUHkXCmqcLv+uIk3Bdo/g==", "license": "MIT", + "peer": true, "dependencies": { "@floating-ui/dom": "^1.6.13" }, @@ -5516,6 +5404,7 @@ "resolved": "https://registry.npmjs.org/@tiptap/extensions/-/extensions-3.15.3.tgz", "integrity": "sha512-ycx/BgxR4rc9tf3ZyTdI98Z19yKLFfqM3UN+v42ChuIwkzyr9zyp7kG8dB9xN2lNqrD+5y/HyJobz/VJ7T90gA==", "license": "MIT", + "peer": true, "funding": { "type": "github", "url": "https://github.com/sponsors/ueberdosis" @@ -5530,6 +5419,7 @@ "resolved": "https://registry.npmjs.org/@tiptap/pm/-/pm-3.22.4.tgz", "integrity": "sha512-hj8Qka6WcHRllHUdeSjDnq2XaisUo4KsoGJc1WcFpoa1Yd+OeD861zUMnV7DFVGdZRy45Obht0CUYJpXQ4yA4w==", "license": "MIT", + "peer": true, "dependencies": { "prosemirror-changeset": "^2.3.0", "prosemirror-commands": "^1.6.2", @@ -5554,6 +5444,7 @@ "resolved": "https://registry.npmjs.org/@tiptap/suggestion/-/suggestion-3.15.3.tgz", "integrity": "sha512-+CbaHhPfKUe+fNpUIQaOPhh6xI+xL5jbK1zw++U+CZIRrVAAmHRhO+D0O2HdiE1RK7596y8bRqMiB2CRHF7emA==", "license": "MIT", + "peer": true, "funding": { "type": "github", "url": "https://github.com/sponsors/ueberdosis" @@ -5568,6 +5459,7 @@ "resolved": "https://registry.npmjs.org/@tiptap/vue-2/-/vue-2-3.22.4.tgz", "integrity": "sha512-mf3aDiFYkjuZqpOFmQTjRxCjCX+N5a4/w18XiD4eNiI41ycWgdxg9QpTuQLX63aX6D6rCMsU8V3f8FiJf4LSFw==", "license": "MIT", + "peer": true, "dependencies": { "vue-ts-types": "1.6.2" }, @@ -5619,7 +5511,6 @@ "dev": true, "license": "MIT", "optional": true, - "peer": true, "dependencies": { "tslib": "^2.4.0" } @@ -5963,8 +5854,7 @@ "resolved": "https://registry.npmjs.org/@types/json5/-/json5-0.0.29.tgz", "integrity": "sha512-dRLjCWHYg4oaA77cxO64oO+7JwCwnIzkZPdrrC71jQmQtlhM556pwKo5bUzqvZndkVbeFLIIi+9TC40JNF5hNQ==", "dev": true, - "license": "MIT", - "peer": true + "license": "MIT" }, "node_modules/@types/linkify-it": { "version": "5.0.0", @@ -6004,14 +5894,14 @@ "license": "MIT" }, "node_modules/@types/node": { - "version": "24.10.1", - "resolved": "https://registry.npmjs.org/@types/node/-/node-24.10.1.tgz", - "integrity": "sha512-GNWcUTRBgIRJD5zj+Tq0fKOJ5XZajIiBroOF0yvj2bSU1WvNdYS/dn9UxwsujGW4JX06dnHyjV2y9rRaybH0iQ==", + "version": "26.1.2", + "resolved": "https://registry.npmjs.org/@types/node/-/node-26.1.2.tgz", + "integrity": "sha512-Vu4a5UFA9rIIFJ7rB/Vaafh9lrCQszopTCx6KjFboXTGQbPNasehVR5TEiithSDGyd1DEiUByggTZsg8jukeIg==", "dev": true, "license": "MIT", - "optional": true, + "peer": true, "dependencies": { - "undici-types": "~7.16.0" + "undici-types": "~8.3.0" } }, "node_modules/@types/react": { @@ -6075,24 +5965,12 @@ "integrity": "sha512-g9gZnnXVq7gM7v3tJCWV/qw7w+KeOlSHAhgF9RytFyifW6AF61hdT2ucrYhPq9hLs5JIryeupHV3qGk95dH9ow==", "license": "MIT" }, - "node_modules/@types/yauzl": { - "version": "2.10.3", - "resolved": "https://registry.npmjs.org/@types/yauzl/-/yauzl-2.10.3.tgz", - "integrity": "sha512-oJoftv0LSuaDZE3Le4DbKX+KS9G36NzOeSap90UIK0yMA/NhKJhqlSGtNDORNRaIbQfzjXDrQa0ytJ6mNRGz/Q==", - "dev": true, - "license": "MIT", - "optional": true, - "dependencies": { - "@types/node": "*" - } - }, "node_modules/@typescript-eslint/eslint-plugin": { "version": "7.18.0", "resolved": "https://registry.npmjs.org/@typescript-eslint/eslint-plugin/-/eslint-plugin-7.18.0.tgz", "integrity": "sha512-94EQTWZ40mzBc42ATNIBimBEDltSJ9RQHCC8vc/PDbxi4k8dVwUAv4o98dk50M1zB+JGFxp43FP7f8+FP8R6Sw==", "dev": true, "license": "MIT", - "peer": true, "dependencies": { "@eslint-community/regexpp": "^4.10.0", "@typescript-eslint/scope-manager": "7.18.0", @@ -6193,7 +6071,6 @@ "integrity": "sha512-jjhdIE/FPF2B7Z1uzc6i3oWKbGcHb87Qw7AWj6jmEqNOfDFbJWtjt/XfwCpvNkpGWlcJaog5vTR+VV8+w9JflA==", "dev": true, "license": "MIT", - "peer": true, "dependencies": { "@typescript-eslint/types": "7.18.0", "@typescript-eslint/visitor-keys": "7.18.0" @@ -6229,7 +6106,6 @@ "integrity": "sha512-XL0FJXuCLaDuX2sYqZUUSOJ2sG5/i1AAze+axqmLnSkNEVMVYLF+cbwlB2w8D1tinFuSikHmFta+P+HOofrLeA==", "dev": true, "license": "MIT", - "peer": true, "dependencies": { "@typescript-eslint/typescript-estree": "7.18.0", "@typescript-eslint/utils": "7.18.0", @@ -6258,7 +6134,6 @@ "integrity": "sha512-iZqi+Ds1y4EDYUtlOOC+aUmxnE9xS/yCigkjA7XpTKV6nCBd3Hp/PRGGmdwnfkV2ThMyYldP1wRpm/id99spTQ==", "dev": true, "license": "MIT", - "peer": true, "engines": { "node": "^18.18.0 || >=20.0.0" }, @@ -6273,7 +6148,6 @@ "integrity": "sha512-aP1v/BSPnnyhMHts8cf1qQ6Q1IFwwRvAQGRvBFkWlo3/lH29OXA3Pts+c10nxRxIBrDnoMqzhgdwVe5f2D6OzA==", "dev": true, "license": "BSD-2-Clause", - "peer": true, "dependencies": { "@typescript-eslint/types": "7.18.0", "@typescript-eslint/visitor-keys": "7.18.0", @@ -6303,7 +6177,6 @@ "integrity": "sha512-SdsKMrI9TdgjdweUSR9MweHA4EJ8YxHn8DFaDisvhVlUOe4BF1tLD7GAj0lIqWVl+dPb/rExr0Btby5loQm20Q==", "dev": true, "license": "ISC", - "peer": true, "bin": { "semver": "bin/semver.js" }, @@ -6317,7 +6190,6 @@ "integrity": "sha512-kK0/rNa2j74XuHVcoCZxdFBMF+aq/vH83CXAOHieC+2Gis4mF8jJXT5eAfyD3K0sAxtPuwxaIOIOvhwzVDt/kw==", "dev": true, "license": "MIT", - "peer": true, "dependencies": { "@eslint-community/eslint-utils": "^4.4.0", "@typescript-eslint/scope-manager": "7.18.0", @@ -6341,7 +6213,6 @@ "integrity": "sha512-cDF0/Gf81QpY3xYyJKDV14Zwdmid5+uuENhjH2EqFaF0ni+yAyq/LzMaIJdhNJXZI7uLzwIlA+V7oWoyn6Curg==", "dev": true, "license": "MIT", - "peer": true, "dependencies": { "@typescript-eslint/types": "7.18.0", "eslint-visitor-keys": "^3.4.3" @@ -6360,7 +6231,6 @@ "integrity": "sha512-wpc+LXeiyiisxPlEkUzU6svyS1frIO3Mgxj1fdy7Pm8Ygzguax2N3Fa/D/ag1WqbOprdI+uY6wMUl8/a2G+iag==", "dev": true, "license": "Apache-2.0", - "peer": true, "engines": { "node": "^12.22.0 || ^14.17.0 || >=16.0.0" }, @@ -6386,8 +6256,7 @@ "optional": true, "os": [ "android" - ], - "peer": true + ] }, "node_modules/@unrs/resolver-binding-android-arm64": { "version": "1.11.1", @@ -6401,8 +6270,7 @@ "optional": true, "os": [ "android" - ], - "peer": true + ] }, "node_modules/@unrs/resolver-binding-darwin-arm64": { "version": "1.11.1", @@ -6416,8 +6284,7 @@ "optional": true, "os": [ "darwin" - ], - "peer": true + ] }, "node_modules/@unrs/resolver-binding-darwin-x64": { "version": "1.11.1", @@ -6431,8 +6298,7 @@ "optional": true, "os": [ "darwin" - ], - "peer": true + ] }, "node_modules/@unrs/resolver-binding-freebsd-x64": { "version": "1.11.1", @@ -6446,8 +6312,7 @@ "optional": true, "os": [ "freebsd" - ], - "peer": true + ] }, "node_modules/@unrs/resolver-binding-linux-arm-gnueabihf": { "version": "1.11.1", @@ -6461,8 +6326,7 @@ "optional": true, "os": [ "linux" - ], - "peer": true + ] }, "node_modules/@unrs/resolver-binding-linux-arm-musleabihf": { "version": "1.11.1", @@ -6476,8 +6340,7 @@ "optional": true, "os": [ "linux" - ], - "peer": true + ] }, "node_modules/@unrs/resolver-binding-linux-arm64-gnu": { "version": "1.11.1", @@ -6491,8 +6354,7 @@ "optional": true, "os": [ "linux" - ], - "peer": true + ] }, "node_modules/@unrs/resolver-binding-linux-arm64-musl": { "version": "1.11.1", @@ -6506,8 +6368,7 @@ "optional": true, "os": [ "linux" - ], - "peer": true + ] }, "node_modules/@unrs/resolver-binding-linux-ppc64-gnu": { "version": "1.11.1", @@ -6521,8 +6382,7 @@ "optional": true, "os": [ "linux" - ], - "peer": true + ] }, "node_modules/@unrs/resolver-binding-linux-riscv64-gnu": { "version": "1.11.1", @@ -6536,8 +6396,7 @@ "optional": true, "os": [ "linux" - ], - "peer": true + ] }, "node_modules/@unrs/resolver-binding-linux-riscv64-musl": { "version": "1.11.1", @@ -6551,8 +6410,7 @@ "optional": true, "os": [ "linux" - ], - "peer": true + ] }, "node_modules/@unrs/resolver-binding-linux-s390x-gnu": { "version": "1.11.1", @@ -6566,8 +6424,7 @@ "optional": true, "os": [ "linux" - ], - "peer": true + ] }, "node_modules/@unrs/resolver-binding-linux-x64-gnu": { "version": "1.11.1", @@ -6581,8 +6438,7 @@ "optional": true, "os": [ "linux" - ], - "peer": true + ] }, "node_modules/@unrs/resolver-binding-linux-x64-musl": { "version": "1.11.1", @@ -6596,8 +6452,7 @@ "optional": true, "os": [ "linux" - ], - "peer": true + ] }, "node_modules/@unrs/resolver-binding-wasm32-wasi": { "version": "1.11.1", @@ -6609,7 +6464,6 @@ "dev": true, "license": "MIT", "optional": true, - "peer": true, "dependencies": { "@napi-rs/wasm-runtime": "^0.2.11" }, @@ -6629,8 +6483,7 @@ "optional": true, "os": [ "win32" - ], - "peer": true + ] }, "node_modules/@unrs/resolver-binding-win32-ia32-msvc": { "version": "1.11.1", @@ -6644,8 +6497,7 @@ "optional": true, "os": [ "win32" - ], - "peer": true + ] }, "node_modules/@unrs/resolver-binding-win32-x64-msvc": { "version": "1.11.1", @@ -6659,8 +6511,7 @@ "optional": true, "os": [ "win32" - ], - "peer": true + ] }, "node_modules/@upsetjs/venn.js": { "version": "2.0.0", @@ -6692,6 +6543,7 @@ "integrity": "sha512-IM49HmthevbgAO4anp1hwtoT9wYe59w0LR00gr+eagHE+ZJ5lK4sLPeO0ubgoJcwLk6dehU3R24N+FbEEKDc8g==", "dev": true, "license": "MIT", + "peer": true, "dependencies": { "@bcoe/v8-coverage": "^1.0.2", "@vitest/utils": "4.1.10", @@ -7090,6 +6942,7 @@ "resolved": "https://registry.npmjs.org/acorn/-/acorn-8.15.0.tgz", "integrity": "sha512-NZyJarBfL7nWwIq+FDL6Zp/yHEhePMNnnJ0y3qfieCrmNvYct8uvtiV41UvlSe6apAfk0fY1FbWx+NwfmpvtTg==", "license": "MIT", + "peer": true, "bin": { "acorn": "bin/acorn" }, @@ -7103,7 +6956,6 @@ "integrity": "sha512-rq9s+JNhf0IChjtDXxllJ7g41oZk5SlXtp0LHwyA5cejwn7vKmKp4pPri6YEePv2PU65sAsegbXtIinmDFDXgQ==", "dev": true, "license": "MIT", - "peer": true, "peerDependencies": { "acorn": "^6.0.0 || ^7.0.0 || ^8.0.0" } @@ -7137,7 +6989,6 @@ "integrity": "sha512-IWrosm/yrn43eiKqkfkHis7QioDleaXQHdDVPKg0FSwwd/DuvyX79TZnFOnYpB7dcsFAMmtFztZuXPDvSePkFw==", "dev": true, "license": "MIT", - "peer": true, "dependencies": { "fast-deep-equal": "^3.1.1", "fast-json-stable-stringify": "^2.0.0", @@ -7301,7 +7152,6 @@ "integrity": "sha512-ixiS0nLNNG5jNQzgZJNoUpBKdo9yTYZMGJ+QgT2jmjR7G7+QHRCc4v6LQ3NgE7EBJq+o0ams3waJwkrlBom8Ig==", "dev": true, "license": "MIT", - "peer": true, "engines": { "node": ">=14" } @@ -7352,7 +7202,6 @@ "integrity": "sha512-FmeCCAenzH0KH381SPT5FZmiA/TmpndpcaShhfgEN9eCVjnFBqq3l1xrI42y8+PPLI6hypzou4GXw00WHmPBLQ==", "dev": true, "license": "MIT", - "peer": true, "dependencies": { "call-bind": "^1.0.8", "call-bound": "^1.0.4", @@ -7386,7 +7235,6 @@ "integrity": "sha512-F/TKATkzseUExPlfvmwQKGITM3DGTK+vkAsCZoDc5daVygbJBnjEUCbgkAvVFsgfXfX4YIqZ/27G3k3tdXrTxQ==", "dev": true, "license": "MIT", - "peer": true, "dependencies": { "call-bind": "^1.0.8", "call-bound": "^1.0.4", @@ -7409,7 +7257,6 @@ "integrity": "sha512-rwG/ja1neyLqCuGZ5YYrznA62D4mZXg0i1cIskIUKSiqF3Cje9/wXAls9B9s1Wa2fomMsIv8czB8jZcPmxCXFg==", "dev": true, "license": "MIT", - "peer": true, "dependencies": { "call-bind": "^1.0.8", "define-properties": "^1.2.1", @@ -7429,7 +7276,6 @@ "integrity": "sha512-Y7Wt51eKJSyi80hFrJCePGGNo5ktJCslFuboqJsbf57CCPcm5zztluPlc4/aD8sWsKvlwatezpV4U1efk8kpjg==", "dev": true, "license": "MIT", - "peer": true, "dependencies": { "call-bind": "^1.0.8", "define-properties": "^1.2.1", @@ -7449,7 +7295,6 @@ "integrity": "sha512-BNoCY6SXXPQ7gF2opIP4GBE+Xw7U+pHMYKuzjgCN3GwiaIR09UUeKfheyIry77QtrCBlC0KK0q5/TER/tYh3PQ==", "dev": true, "license": "MIT", - "peer": true, "dependencies": { "array-buffer-byte-length": "^1.0.1", "call-bind": "^1.0.8", @@ -7574,7 +7419,6 @@ "integrity": "sha512-hsU18Ae8CDTR6Kgu9DYf0EbCr/a5iGL0rytQDobUcdpYOKokk8LEjVphnXkDkgpi0wYVsqrXuP0bZxJaTqdgoA==", "dev": true, "license": "MIT", - "peer": true, "engines": { "node": ">= 0.4" } @@ -7671,7 +7515,6 @@ "integrity": "sha512-Co2Y9wX854ts6U8gAAPXfn0GmAyctHuK8n0Yhfjd6t30g7yvKjspvvOo9yG+z52PZRgFErt7Ka2pYnXCjLKEpg==", "dev": true, "license": "MIT", - "peer": true, "dependencies": { "@babel/compat-data": "^7.27.7", "@babel/helper-define-polyfill-provider": "^0.6.5", @@ -7687,7 +7530,6 @@ "integrity": "sha512-U+GNwMdSFgzVmfhNm8GJUX88AadB3uo9KpJqS3FaqNIPKgySuvMb+bHPsOmmuWyIcuqZj/pzt1RUIUZns4y2+A==", "dev": true, "license": "MIT", - "peer": true, "dependencies": { "@babel/helper-define-polyfill-provider": "^0.6.5", "core-js-compat": "^3.43.0" @@ -7702,7 +7544,6 @@ "integrity": "sha512-ISqQ2frbiNU9vIJkzg7dlPpznPZ4jOiUQ1uSmB0fEHeowtN3COYRsXr/xexn64NpU13P06jc/L5TgiJXOgrbEg==", "dev": true, "license": "MIT", - "peer": true, "dependencies": { "@babel/helper-define-polyfill-provider": "^0.6.5" }, @@ -7840,8 +7681,7 @@ "resolved": "https://registry.npmjs.org/boolbase/-/boolbase-1.0.0.tgz", "integrity": "sha512-JZOSA7Mo9sNGB8+UjSgzdLtokWAky1zbztM3WRLCbZ70/3cTANmQmOdR7y2g+J0e2WXywy1yS468tY+IruqEww==", "dev": true, - "license": "ISC", - "peer": true + "license": "ISC" }, "node_modules/brace-expansion": { "version": "2.1.4", @@ -8035,6 +7875,7 @@ } ], "license": "MIT", + "peer": true, "dependencies": { "baseline-browser-mapping": "^2.8.25", "caniuse-lite": "^1.0.30001754", @@ -8093,16 +7934,6 @@ "ieee754": "^1.1.13" } }, - "node_modules/buffer-crc32": { - "version": "0.2.13", - "resolved": "https://registry.npmjs.org/buffer-crc32/-/buffer-crc32-0.2.13.tgz", - "integrity": "sha512-VO9Ht/+p3SN7SKWqcrgEzjGbRSJYTx+Q1pTQC0wrWqHx0vpJraQ6GtHx8tvcg1rlK1byhU5gccxgOgj7B0TDkQ==", - "dev": true, - "license": "MIT", - "engines": { - "node": "*" - } - }, "node_modules/buffer-xor": { "version": "1.0.3", "resolved": "https://registry.npmjs.org/buffer-xor/-/buffer-xor-1.0.3.tgz", @@ -8126,7 +7957,6 @@ "integrity": "sha512-zhaCDicdLuWN5UbN5IMnFqNMhNfo919sH85y2/ea+5Yg9TsTkeZxpL+JLbp6cgYFS4sRLp3YV4S6yDuqVWHYOw==", "dev": true, "license": "MIT", - "peer": true, "engines": { "node": ">=6" }, @@ -8147,7 +7977,6 @@ "integrity": "sha512-SW9lzGTLvWTP1AY8xeAMZimqDrIaSdLQUcVr9DMef51niJ022Ri87SwRRKYm4A6iHfkPaiVUu/Duw2Wc4J7kKg==", "dev": true, "license": "MIT", - "peer": true, "dependencies": { "semver": "^7.0.0" } @@ -8158,7 +7987,6 @@ "integrity": "sha512-SdsKMrI9TdgjdweUSR9MweHA4EJ8YxHn8DFaDisvhVlUOe4BF1tLD7GAj0lIqWVl+dPb/rExr0Btby5loQm20Q==", "dev": true, "license": "ISC", - "peer": true, "bin": { "semver": "bin/semver.js" }, @@ -8237,7 +8065,6 @@ "integrity": "sha512-P8BjAsXvZS+VIDUI11hHCQEv74YT67YUi5JJFNWIqL235sBmjX4+qx9Muvls5ivyNENctx46xQLQ3aTuE7ssaQ==", "dev": true, "license": "MIT", - "peer": true, "engines": { "node": ">=6" } @@ -8593,7 +8420,6 @@ "integrity": "sha512-buhp5kePrmda3vhc5B9t7pUQXAb2Tnd0qgpkIhPhkHXxJpiPJ11H0ZEU0oBpJ2QztSbzG/ZxMj/CHsYJqRHmyg==", "dev": true, "license": "MIT", - "peer": true, "engines": { "node": ">= 12.0.0" } @@ -8627,8 +8453,7 @@ "resolved": "https://registry.npmjs.org/concat-map/-/concat-map-0.0.1.tgz", "integrity": "sha512-/Srv4dswyQNBfohGpz9o6Yb3Gz3SrUDqBH5rTuhGR7ahtlbYKnVxw2bCFMRljaA7EXHaXZ8wsHdodFvbkhKmqg==", "dev": true, - "license": "MIT", - "peer": true + "license": "MIT" }, "node_modules/condense-newlines": { "version": "0.2.1", @@ -8883,7 +8708,6 @@ "integrity": "sha512-/Tb/JcjK111nNScGob5MNtsntNM1aCNUDipB/TkwZFhyDrrE47SOx/18wF2bbjgc3ZzCSKW1T5nt5EbFoAz/Vg==", "dev": true, "license": "MIT", - "peer": true, "bin": { "cssesc": "bin/cssesc" }, @@ -8913,9 +8737,9 @@ "license": "MIT" }, "node_modules/cypress": { - "version": "15.15.0", - "resolved": "https://registry.npmjs.org/cypress/-/cypress-15.15.0.tgz", - "integrity": "sha512-N8qBv3AUYn6xfIG73O5O58kTClUBSZ7a3C08IQFkSGTUdEauJ3BqwTFb/f9KPZgadftoZjllC0XSwD7xNNolbA==", + "version": "15.19.0", + "resolved": "https://registry.npmjs.org/cypress/-/cypress-15.19.0.tgz", + "integrity": "sha512-kjy1u3SWlMRWS5qffH3U+bXx1PqPP7qwhIzEY3UtERcW2r/8zy5uk6uSwa75pYXJyYFVP1SO8mAn/avZUvUbfg==", "dev": true, "hasInstallScript": true, "license": "MIT", @@ -8940,7 +8764,6 @@ "eventemitter2": "6.4.7", "execa": "4.1.0", "executable": "^4.1.1", - "extract-zip": "2.0.1", "fs-extra": "^9.1.0", "hasha": "5.2.2", "is-installed-globally": "~0.4.0", @@ -8959,7 +8782,7 @@ "tree-kill": "1.2.2", "tslib": "1.14.1", "untildify": "^4.0.0", - "yauzl": "^2.10.0" + "yauzl": "^3.3.1" }, "bin": { "cypress": "bin/cypress" @@ -9022,6 +8845,7 @@ "resolved": "https://registry.npmjs.org/cytoscape/-/cytoscape-3.33.1.tgz", "integrity": "sha512-iJc4TwyANnOGR1OmWhsS9ayRS3s+XQ185FmuHObThD+5AeJCakAAbWv8KimMTt08xCCLNgneQwFp+JRJOr9qGQ==", "license": "MIT", + "peer": true, "engines": { "node": ">=0.10" } @@ -9431,6 +9255,7 @@ "resolved": "https://registry.npmjs.org/d3-selection/-/d3-selection-3.0.0.tgz", "integrity": "sha512-fmTRWbNMmsmWq6xJV8D19U/gw/bwrHfNXxrIN+HfZgnzqTHp9jOmKMhsTUjXOJnZOdZY9Q28y4yebKzqDKlxlQ==", "license": "ISC", + "peer": true, "engines": { "node": ">=12" } @@ -9567,7 +9392,6 @@ "integrity": "sha512-EmKO5V3OLXh1rtK2wgXRansaK1/mtVdTUEiEI0W8RkvgT05kfxaH29PliLnpLP73yYO6142Q72QNa8Wx/A5CqQ==", "dev": true, "license": "MIT", - "peer": true, "dependencies": { "call-bound": "^1.0.3", "es-errors": "^1.3.0", @@ -9586,7 +9410,6 @@ "integrity": "sha512-tuhGbE6CfTM9+5ANGf+oQb72Ky/0+s3xKUpHvShfiz2RxMFgFPjsXuRLBVMtvMs15awe45SRb83D6wH4ew6wlQ==", "dev": true, "license": "MIT", - "peer": true, "dependencies": { "call-bound": "^1.0.3", "es-errors": "^1.3.0", @@ -9605,7 +9428,6 @@ "integrity": "sha512-BS8PfmtDGnrgYdOonGZQdLZslWIeCGFP9tpan0hi1Co2Zr2NKADsvGYA8XxuG/4UWgJ6Cjtv+YJnB6MM69QGlQ==", "dev": true, "license": "MIT", - "peer": true, "dependencies": { "call-bound": "^1.0.2", "es-errors": "^1.3.0", @@ -9724,8 +9546,7 @@ "resolved": "https://registry.npmjs.org/deep-is/-/deep-is-0.1.4.tgz", "integrity": "sha512-oIPzksmTg4/MriiaYGO+okXDT7ztn/w3Eptv/+gSIdMdKsJo0u4CfYNFJPy+4SKMuCqGw2wxnA+URMg3t8a/bQ==", "dev": true, - "license": "MIT", - "peer": true + "license": "MIT" }, "node_modules/defaults": { "version": "1.0.4", @@ -9875,7 +9696,6 @@ "dev": true, "license": "Apache-2.0", "optional": true, - "peer": true, "bin": { "detect-libc": "bin/detect-libc.js" }, @@ -10267,7 +10087,6 @@ "integrity": "sha512-yS+Q5i3hBf7GBkd4KG8a7eBNNWNGLTaEwwYWUijIYM7zrlYDM0BFXHjjPWlWZ1Rg7UaddZeIDmi9jF3HmqiQ2w==", "dev": true, "license": "Apache-2.0", - "peer": true, "dependencies": { "esutils": "^2.0.2" }, @@ -10493,7 +10312,6 @@ "integrity": "sha512-WSzPgsdLtTcQwm4CROfS5ju2Wa1QQcVeT37jFjYzdFz1r9ahadC8B8/a4qxJxM+09F18iumCdRmlr96ZYkQvEg==", "dev": true, "license": "MIT", - "peer": true, "dependencies": { "array-buffer-byte-length": "^1.0.2", "arraybuffer.prototype.slice": "^1.0.4", @@ -10636,7 +10454,6 @@ "integrity": "sha512-d9T8ucsEhh8Bi1woXCf+TIKDIROLG5WCkxg8geBCbvk22kzwC5G2OnXVMO6FUsvQlgUUXQ2itephWDLqDzbeCw==", "dev": true, "license": "MIT", - "peer": true, "dependencies": { "hasown": "^2.0.2" }, @@ -10650,7 +10467,6 @@ "integrity": "sha512-w+5mJ3GuFL+NjVtJlvydShqE1eN3h3PbI7/5LAsYJP/2qtuMXjfL2LpHSRqo4b4eSF5K/DH1JXKUAHSB2UW50g==", "dev": true, "license": "MIT", - "peer": true, "dependencies": { "is-callable": "^1.2.7", "is-date-object": "^1.0.5", @@ -10737,7 +10553,6 @@ "integrity": "sha512-TtpcNJ3XAzx3Gq8sWRzJaVajRs0uVxA2YAkdb1jm2YkPz4G6egUFAyA3n5vtEIZefPk5Wa4UXbKuS5fKkJWdgA==", "dev": true, "license": "MIT", - "peer": true, "engines": { "node": ">=10" }, @@ -10840,7 +10655,6 @@ "integrity": "sha512-3z3vFexKIEnjHE3zCMRo6fn/e44U7T1khUjg+Hp0ZQMCigh28rALD0nPFBcGZuiLC5rLZa2ubQHDRln09JfU2Q==", "dev": true, "license": "MIT", - "peer": true, "dependencies": { "semver": "^7.5.4" }, @@ -10857,7 +10671,6 @@ "integrity": "sha512-SdsKMrI9TdgjdweUSR9MweHA4EJ8YxHn8DFaDisvhVlUOe4BF1tLD7GAj0lIqWVl+dPb/rExr0Btby5loQm20Q==", "dev": true, "license": "ISC", - "peer": true, "bin": { "semver": "bin/semver.js" }, @@ -10933,7 +10746,6 @@ "integrity": "sha512-WFj2isz22JahUv+B788TlO3N6zL3nNJGU8CcZbPZvVEkBPaJdCV4vy5wyghty5ROFbCRnm132v8BScu5/1BQ8g==", "dev": true, "license": "MIT", - "peer": true, "dependencies": { "debug": "^3.2.7", "is-core-module": "^2.13.0", @@ -10946,7 +10758,6 @@ "integrity": "sha512-CFjzYYAi4ThfiQvizrFQevTTXHtnCqWfe7x1AhgEscTz6ZbLbfoLRLPugTQyBth6f8ZERVUSyWHFD/7Wu4t1XQ==", "dev": true, "license": "MIT", - "peer": true, "dependencies": { "ms": "^2.1.1" } @@ -10993,7 +10804,6 @@ "integrity": "sha512-L8jSWTze7K2mTg0vos/RuLRS5soomksDPoJLXIslC7c8Wmut3bx7CPpJijDcBZtxQ5lrbUdM+s0OlNbz0DCDNw==", "dev": true, "license": "MIT", - "peer": true, "dependencies": { "debug": "^3.2.7" }, @@ -11012,7 +10822,6 @@ "integrity": "sha512-CFjzYYAi4ThfiQvizrFQevTTXHtnCqWfe7x1AhgEscTz6ZbLbfoLRLPugTQyBth6f8ZERVUSyWHFD/7Wu4t1XQ==", "dev": true, "license": "MIT", - "peer": true, "dependencies": { "ms": "^2.1.1" } @@ -11040,7 +10849,6 @@ "https://opencollective.com/eslint" ], "license": "MIT", - "peer": true, "dependencies": { "@eslint-community/eslint-utils": "^4.1.2", "@eslint-community/regexpp": "^4.11.0", @@ -11094,7 +10902,6 @@ "integrity": "sha512-Edep/X9fGqVNmzKBVsDYIOtD+z1tuezV70LBjdCst9Tqu76lsnvRiZ6oTic1n+/BIwX6QDGAO94PN4N2SADvtw==", "dev": true, "license": "MIT", - "peer": true, "dependencies": { "balanced-match": "^1.0.0", "concat-map": "0.0.1" @@ -11106,7 +10913,6 @@ "integrity": "sha512-CFjzYYAi4ThfiQvizrFQevTTXHtnCqWfe7x1AhgEscTz6ZbLbfoLRLPugTQyBth6f8ZERVUSyWHFD/7Wu4t1XQ==", "dev": true, "license": "MIT", - "peer": true, "dependencies": { "ms": "^2.1.1" } @@ -11117,7 +10923,6 @@ "integrity": "sha512-35mSku4ZXK0vfCuHEDAwt55dg2jNajHZ1odvF+8SSr82EsZY4QmXfuWso8oEd8zRhVObSN18aM0CjSdoBX7zIw==", "dev": true, "license": "Apache-2.0", - "peer": true, "dependencies": { "esutils": "^2.0.2" }, @@ -11131,7 +10936,6 @@ "integrity": "sha512-VgjWUsnnT6n+NUk6eZq77zeFdpW2LWDzP6zFGrCbHXiYNul5Dzqk2HHQ5uFH2DNW5Xbp8+jVzaeNt94ssEEl4w==", "dev": true, "license": "ISC", - "peer": true, "dependencies": { "brace-expansion": "^1.1.7" }, @@ -11145,7 +10949,6 @@ "integrity": "sha512-x8wxIpv00Y50NyweDUpa+58ffgSAI5sqe+zcZh33xphD0AVh+1kqr1ombaTRb7Fhpove1zfUuujlX9DWWBP5ag==", "dev": true, "license": "BSD-3-Clause", - "peer": true, "dependencies": { "@es-joy/jsdoccomment": "~0.41.0", "are-docs-informative": "^0.0.2", @@ -11170,7 +10973,6 @@ "integrity": "sha512-SdsKMrI9TdgjdweUSR9MweHA4EJ8YxHn8DFaDisvhVlUOe4BF1tLD7GAj0lIqWVl+dPb/rExr0Btby5loQm20Q==", "dev": true, "license": "ISC", - "peer": true, "bin": { "semver": "bin/semver.js" }, @@ -11214,7 +11016,6 @@ "integrity": "sha512-Edep/X9fGqVNmzKBVsDYIOtD+z1tuezV70LBjdCst9Tqu76lsnvRiZ6oTic1n+/BIwX6QDGAO94PN4N2SADvtw==", "dev": true, "license": "MIT", - "peer": true, "dependencies": { "balanced-match": "^1.0.0", "concat-map": "0.0.1" @@ -11226,7 +11027,6 @@ "integrity": "sha512-VgjWUsnnT6n+NUk6eZq77zeFdpW2LWDzP6zFGrCbHXiYNul5Dzqk2HHQ5uFH2DNW5Xbp8+jVzaeNt94ssEEl4w==", "dev": true, "license": "ISC", - "peer": true, "dependencies": { "brace-expansion": "^1.1.7" }, @@ -11240,7 +11040,6 @@ "integrity": "sha512-SdsKMrI9TdgjdweUSR9MweHA4EJ8YxHn8DFaDisvhVlUOe4BF1tLD7GAj0lIqWVl+dPb/rExr0Btby5loQm20Q==", "dev": true, "license": "ISC", - "peer": true, "bin": { "semver": "bin/semver.js" }, @@ -11295,7 +11094,6 @@ "integrity": "sha512-SdsKMrI9TdgjdweUSR9MweHA4EJ8YxHn8DFaDisvhVlUOe4BF1tLD7GAj0lIqWVl+dPb/rExr0Btby5loQm20Q==", "dev": true, "license": "ISC", - "peer": true, "bin": { "semver": "bin/semver.js" }, @@ -11309,7 +11107,6 @@ "integrity": "sha512-2NxwbF/hZ0KpepYN0cNbo+FN6XoK7GaHlQhgx/hIZl6Va0bF45RQOOwhLIy8lQDbuCiadSLCBnH2CFYquit5bw==", "dev": true, "license": "BSD-2-Clause", - "peer": true, "dependencies": { "esrecurse": "^4.3.0", "estraverse": "^4.1.1" @@ -11324,7 +11121,6 @@ "integrity": "sha512-0rSmRBzXgDzIsD6mGdJgevzgezI534Cer5L/vyMX0kHzT/jiB43jRhd9YUlMGYLQy2zprNmoT8qasCGtY+QaKw==", "dev": true, "license": "Apache-2.0", - "peer": true, "engines": { "node": ">=10" } @@ -11335,7 +11131,6 @@ "integrity": "sha512-Edep/X9fGqVNmzKBVsDYIOtD+z1tuezV70LBjdCst9Tqu76lsnvRiZ6oTic1n+/BIwX6QDGAO94PN4N2SADvtw==", "dev": true, "license": "MIT", - "peer": true, "dependencies": { "balanced-match": "^1.0.0", "concat-map": "0.0.1" @@ -11347,7 +11142,6 @@ "integrity": "sha512-dOt21O7lTMhDM+X9mB4GX+DZrZtCUJPL/wlcTqxyrx5IvO0IYtILdtrQGQp+8n5S0gwSVmOf9NQrjMOgfQZlIg==", "dev": true, "license": "BSD-2-Clause", - "peer": true, "dependencies": { "esrecurse": "^4.3.0", "estraverse": "^5.2.0" @@ -11365,7 +11159,6 @@ "integrity": "sha512-wpc+LXeiyiisxPlEkUzU6svyS1frIO3Mgxj1fdy7Pm8Ygzguax2N3Fa/D/ag1WqbOprdI+uY6wMUl8/a2G+iag==", "dev": true, "license": "Apache-2.0", - "peer": true, "engines": { "node": "^12.22.0 || ^14.17.0 || >=16.0.0" }, @@ -11379,7 +11172,6 @@ "integrity": "sha512-MMdARuVEQziNTeJD8DgMqmhwR11BRQ/cBP+pLtYdSTnf3MIO8fFeiINEbX36ZdNlfU/7A9f3gUw49B3oQsvwBA==", "dev": true, "license": "BSD-2-Clause", - "peer": true, "engines": { "node": ">=4.0" } @@ -11390,7 +11182,6 @@ "integrity": "sha512-XxwI8EOhVQgWp6iDL+3b0r86f4d6AX6zSU55HfB4ydCEuXLXc5FcYeOu+nnGftS4TEju/11rt4KJPTMgbfmv4A==", "dev": true, "license": "ISC", - "peer": true, "dependencies": { "is-glob": "^4.0.3" }, @@ -11404,7 +11195,6 @@ "integrity": "sha512-VgjWUsnnT6n+NUk6eZq77zeFdpW2LWDzP6zFGrCbHXiYNul5Dzqk2HHQ5uFH2DNW5Xbp8+jVzaeNt94ssEEl4w==", "dev": true, "license": "ISC", - "peer": true, "dependencies": { "brace-expansion": "^1.1.7" }, @@ -11418,7 +11208,6 @@ "integrity": "sha512-oruZaFkjorTpF32kDSI5/75ViwGeZginGGy2NoOSg3Q9bnwlnmDm4HLnkl0RE3n+njDXR037aY1+x58Z/zFdwQ==", "dev": true, "license": "BSD-2-Clause", - "peer": true, "dependencies": { "acorn": "^8.9.0", "acorn-jsx": "^5.3.2", @@ -11437,7 +11226,6 @@ "integrity": "sha512-wpc+LXeiyiisxPlEkUzU6svyS1frIO3Mgxj1fdy7Pm8Ygzguax2N3Fa/D/ag1WqbOprdI+uY6wMUl8/a2G+iag==", "dev": true, "license": "Apache-2.0", - "peer": true, "engines": { "node": "^12.22.0 || ^14.17.0 || >=16.0.0" }, @@ -11465,7 +11253,6 @@ "integrity": "sha512-ca9pw9fomFcKPvFLXhBKUK90ZvGibiGOvRJNbjljY7s7uq/5YO4BOzcYtJqExdx99rF6aAcnRxHmcUHcz6sQsg==", "dev": true, "license": "BSD-3-Clause", - "peer": true, "dependencies": { "estraverse": "^5.1.0" }, @@ -11479,7 +11266,6 @@ "integrity": "sha512-MMdARuVEQziNTeJD8DgMqmhwR11BRQ/cBP+pLtYdSTnf3MIO8fFeiINEbX36ZdNlfU/7A9f3gUw49B3oQsvwBA==", "dev": true, "license": "BSD-2-Clause", - "peer": true, "engines": { "node": ">=4.0" } @@ -11490,7 +11276,6 @@ "integrity": "sha512-KmfKL3b6G+RXvP8N1vr3Tq1kL/oCFgn2NYXEtqP8/L3pKapUA4G8cFVaoF3SU323CD4XypR/ffioHmkti6/Tag==", "dev": true, "license": "BSD-2-Clause", - "peer": true, "dependencies": { "estraverse": "^5.2.0" }, @@ -11504,7 +11289,6 @@ "integrity": "sha512-MMdARuVEQziNTeJD8DgMqmhwR11BRQ/cBP+pLtYdSTnf3MIO8fFeiINEbX36ZdNlfU/7A9f3gUw49B3oQsvwBA==", "dev": true, "license": "BSD-2-Clause", - "peer": true, "engines": { "node": ">=4.0" } @@ -11515,7 +11299,6 @@ "integrity": "sha512-39nnKffWz8xN1BU/2c79n9nB9HDzo0niYUqx6xyqUnyoAnQyyWpOTdZEeiCch8BBu515t4wp9ZmgVfVhn9EBpw==", "dev": true, "license": "BSD-2-Clause", - "peer": true, "engines": { "node": ">=4.0" } @@ -11654,27 +11437,6 @@ "node": ">=0.10.0" } }, - "node_modules/extract-zip": { - "version": "2.0.1", - "resolved": "https://registry.npmjs.org/extract-zip/-/extract-zip-2.0.1.tgz", - "integrity": "sha512-GDhU9ntwuKyGXdZBUgTIe+vXnWj0fppUEtMDL0+idd5Sta8TGpHssn/eusA9mrPr9qNDym6SxAYZjNvCn/9RBg==", - "dev": true, - "license": "BSD-2-Clause", - "dependencies": { - "debug": "^4.1.1", - "get-stream": "^5.1.0", - "yauzl": "^2.10.0" - }, - "bin": { - "extract-zip": "cli.js" - }, - "engines": { - "node": ">= 10.17.0" - }, - "optionalDependencies": { - "@types/yauzl": "^2.9.1" - } - }, "node_modules/extsprintf": { "version": "1.3.0", "resolved": "https://registry.npmjs.org/extsprintf/-/extsprintf-1.3.0.tgz", @@ -11714,16 +11476,14 @@ "resolved": "https://registry.npmjs.org/fast-json-stable-stringify/-/fast-json-stable-stringify-2.1.0.tgz", "integrity": "sha512-lhd/wF+Lk98HZoTCtlVraHtfh5XYijIjalXck7saUtuanSDyLMxnHhSXEDJqHxD7msR8D0uCmqlkwjCV8xvwHw==", "dev": true, - "license": "MIT", - "peer": true + "license": "MIT" }, "node_modules/fast-levenshtein": { "version": "2.0.6", "resolved": "https://registry.npmjs.org/fast-levenshtein/-/fast-levenshtein-2.0.6.tgz", "integrity": "sha512-DCXu6Ifhqcks7TZKY3Hxp3y6qphY5SJZmrWMDrKcERSOXWQdMhU9Ig/PYrzyw/ul9jOIyh0N4M0tbC5hodg8dw==", "dev": true, - "license": "MIT", - "peer": true + "license": "MIT" }, "node_modules/fast-shuffle": { "version": "6.1.1", @@ -11799,16 +11559,6 @@ "reusify": "^1.0.4" } }, - "node_modules/fd-slicer": { - "version": "1.1.0", - "resolved": "https://registry.npmjs.org/fd-slicer/-/fd-slicer-1.1.0.tgz", - "integrity": "sha512-cE1qsB/VwyQozZ+q1dGxR8LBYNZeofhEdUNGSMbQD3Gw2lAzX9Zb3uIU6Ebc/Fmyjo9AWWfnn0AUCHqtevs/8g==", - "dev": true, - "license": "MIT", - "dependencies": { - "pend": "~1.2.0" - } - }, "node_modules/fdir": { "version": "6.5.0", "resolved": "https://registry.npmjs.org/fdir/-/fdir-6.5.0.tgz", @@ -11856,7 +11606,6 @@ "integrity": "sha512-7Gps/XWymbLk2QLYK4NzpMOrYjMhdIxXuIvy2QBsLE6ljuodKvdkWs/cpyJJ3CVIVpH0Oi1Hvg1ovbMzLdFBBg==", "dev": true, "license": "MIT", - "peer": true, "dependencies": { "flat-cache": "^3.0.4" }, @@ -12037,7 +11786,6 @@ "integrity": "sha512-CYcENa+FtcUKLmhhqyctpclsq7QF38pKjZHsGNiSQF5r4FtoKDWabFDl3hzaEQMvT1LHEysw5twgLvpYYb4vbw==", "dev": true, "license": "MIT", - "peer": true, "dependencies": { "flatted": "^3.2.9", "keyv": "^4.5.3", @@ -12052,8 +11800,7 @@ "resolved": "https://registry.npmjs.org/flatted/-/flatted-3.4.2.tgz", "integrity": "sha512-PjDse7RzhcPkIJwy5t7KPWQSZ9cAbzQXcafsetQoD7sOJRQlGikNbx7yZp2OotDnJyrDcbyRq3Ttb18iYOqkxA==", "dev": true, - "license": "ISC", - "peer": true + "license": "ISC" }, "node_modules/floating-vue": { "version": "1.0.0-beta.19", @@ -12224,8 +11971,7 @@ "resolved": "https://registry.npmjs.org/fs.realpath/-/fs.realpath-1.0.0.tgz", "integrity": "sha512-OO0pH2lK6a0hZnAdau5ItzHPI6pUlvI7jMVnxUQRtw4owF2wk8lOSabtGDCTP4Ggrg2MbGnWO9X8K1t4+fGMDw==", "dev": true, - "license": "ISC", - "peer": true + "license": "ISC" }, "node_modules/fsevents": { "version": "2.3.3", @@ -12257,7 +12003,6 @@ "integrity": "sha512-e5iwyodOHhbMr/yNrc7fDYG4qlbIvI5gajyzPnb5TCwyhjApznQh1BMFou9b30SevY43gCJKXycoCBjMbsuW0Q==", "dev": true, "license": "MIT", - "peer": true, "dependencies": { "call-bind": "^1.0.8", "call-bound": "^1.0.3", @@ -12299,7 +12044,6 @@ "integrity": "sha512-3hN7NaskYvMDLQY55gnW3NQ+mesEAepTqlg+VEbj7zzqEMBVNhzcGYYeqFo/TlYz6eQiFcp1HcsCZO+nGgS8zg==", "dev": true, "license": "MIT", - "peer": true, "engines": { "node": ">=6.9.0" } @@ -12397,7 +12141,6 @@ "integrity": "sha512-w9UMqWwJxHNOvoNzSJ2oPF5wvYcvP7jUvYzhp67yEhTi17ZDBBC1z9pTdGuzjD+EFIqLSYRweZjqfiPzQ06Ebg==", "dev": true, "license": "MIT", - "peer": true, "dependencies": { "call-bound": "^1.0.3", "es-errors": "^1.3.0", @@ -12416,7 +12159,6 @@ "integrity": "sha512-1VKTZJCwBrvbd+Wn3AOgQP/2Av+TfTCOlE4AcRJE72W1ksZXbAx8PPBR9RzgTeSPzlPMHrbANMH3LbltH73wxQ==", "dev": true, "license": "MIT", - "peer": true, "dependencies": { "resolve-pkg-maps": "^1.0.0" }, @@ -12516,7 +12258,6 @@ "integrity": "sha512-DpLKbNU4WylpxJykQujfCcwYWiV/Jhm50Goo0wrVILAv5jOr9d+H+UR3PhSCD2rCCEIg0uc+G+muBTwD54JhDQ==", "dev": true, "license": "MIT", - "peer": true, "dependencies": { "define-properties": "^1.2.1", "gopd": "^1.0.1" @@ -12589,8 +12330,7 @@ "resolved": "https://registry.npmjs.org/graphemer/-/graphemer-1.4.0.tgz", "integrity": "sha512-EtKwoO6kxCL9WO5xipiHTZlSzBm7WLT627TqC/uVRd0HKmq8NXyebnNYxDoBi7wt8eTWrUrKXCOVaFq9x1kgag==", "dev": true, - "license": "MIT", - "peer": true + "license": "MIT" }, "node_modules/hachure-fill": { "version": "0.5.2", @@ -12647,7 +12387,6 @@ "integrity": "sha512-KIL7eQPfHQRC8+XluaIw7BHUwwqL19bQn4hzNgdr+1wXoU0KKj6rufu47lhY7KbJR2C6T6+PfyN0Ea7wkSS+qQ==", "dev": true, "license": "MIT", - "peer": true, "dependencies": { "dunder-proto": "^1.0.0" }, @@ -12827,6 +12566,7 @@ "resolved": "https://registry.npmjs.org/highlight.js/-/highlight.js-11.11.1.tgz", "integrity": "sha512-Xwwo44whKBVCYoliBQwaPvtd/2tYFkRQtXDWj1nackaV2JPXx3L0+Jvd8/qCJ2p+ML0/XVkJ2q+Mr+UVdpJK5w==", "license": "BSD-3-Clause", + "peer": true, "engines": { "node": ">=12.0.0" } @@ -13000,8 +12740,7 @@ "resolved": "https://registry.npmjs.org/immutable/-/immutable-5.1.9.tgz", "integrity": "sha512-m8nVez3rwrgmWxtLMt1ZYXB2Lv7OKYn/disyxAlSDYAlKSlFoPPfIAmAM/M5xqL4m4C/wAPw7S2/CNaUii1Hxg==", "dev": true, - "license": "MIT", - "peer": true + "license": "MIT" }, "node_modules/import-fresh": { "version": "3.3.1", @@ -13009,7 +12748,6 @@ "integrity": "sha512-TR3KfrTZTYLPB6jUjfx6MF9WcWrHL9su5TObK4ZkYgBdWKPOFoSoQIdEuTuR82pmtxH2spWG9h6etwfr1pLBqQ==", "dev": true, "license": "MIT", - "peer": true, "dependencies": { "parent-module": "^1.0.0", "resolve-from": "^4.0.0" @@ -13037,7 +12775,6 @@ "integrity": "sha512-JmXMZ6wuvDmLiHEml9ykzqO6lwFbof0GG4IkcGaENdCRDDmMVnny7s5HsIgHCbaq0w2MyPhDqkhTUgS2LU2PHA==", "dev": true, "license": "MIT", - "peer": true, "engines": { "node": ">=0.8.19" } @@ -13048,7 +12785,6 @@ "integrity": "sha512-k92I/b08q4wvFscXCLvqfsHCrjrF7yiXsQuIVvVE7N82W3+aqpzuUdBbfhWcy/FZR3/4IgflMgKLOsvPDrGCJA==", "dev": true, "license": "ISC", - "peer": true, "dependencies": { "once": "^1.3.0", "wrappy": "1" @@ -13154,7 +12890,6 @@ "integrity": "sha512-9dgM/cZBnNvjzaMYHVoxxfPj2QXt22Ev7SuuPrs+xav0ukGB0S6d4ydZdEiM48kLx5kDV+QBPrpVnFyefL8kkQ==", "dev": true, "license": "MIT", - "peer": true, "dependencies": { "async-function": "^1.0.0", "call-bound": "^1.0.3", @@ -13227,7 +12962,6 @@ "integrity": "sha512-BSLE3HnV2syZ0FK0iMA/yUGplUeMmNz4AW5fnTunbCIqZi4vG3WjJT9FHMy5D69xmAYBHXQhJdALdpwVxV501A==", "dev": true, "license": "MIT", - "peer": true, "dependencies": { "builtin-modules": "^3.3.0" }, @@ -13244,7 +12978,6 @@ "integrity": "sha512-gNCGbnnnnFAUGKeZ9PdbyeGYJqewpmc2aKHUEMO5nQPWU9lOmv7jcmQIv+qHD8fXW6W7qfuCwX4rY9LNRjXrkQ==", "dev": true, "license": "MIT", - "peer": true, "dependencies": { "semver": "^7.7.1" } @@ -13255,7 +12988,6 @@ "integrity": "sha512-SdsKMrI9TdgjdweUSR9MweHA4EJ8YxHn8DFaDisvhVlUOe4BF1tLD7GAj0lIqWVl+dPb/rExr0Btby5loQm20Q==", "dev": true, "license": "ISC", - "peer": true, "bin": { "semver": "bin/semver.js" }, @@ -13298,7 +13030,6 @@ "integrity": "sha512-RKtWF8pGmS87i2D6gqQu/l7EYRlVdfzemCJN/P3UOs//x1QE7mfhvzHIApBTRf7axvT6DMGwSwBXYCT0nfB9xw==", "dev": true, "license": "MIT", - "peer": true, "dependencies": { "call-bound": "^1.0.2", "get-intrinsic": "^1.2.6", @@ -13354,7 +13085,6 @@ "integrity": "sha512-1pC6N8qWJbWoPtEjgcL2xyhQOP491EQjeUo3qTKcmV8YSDDJrOepfG8pcC7h/QgnQHYSv0mJ3Z/ZWxmatVrysg==", "dev": true, "license": "MIT", - "peer": true, "dependencies": { "call-bound": "^1.0.3" }, @@ -13461,7 +13191,6 @@ "integrity": "sha512-5KoIu2Ngpyek75jXodFvnafB6DJgr3u8uuK0LEZJjrU19DrMD3EVERaR8sjz8CCGgpZvxPl9SuE1GMVPFHx1mw==", "dev": true, "license": "MIT", - "peer": true, "engines": { "node": ">= 0.4" }, @@ -13731,7 +13460,6 @@ "integrity": "sha512-6i9mGWSlqzNMEqpCp93KwRS1uUOodk2OJ6b+sq7ZPDSy2WuI5NFIxp/254TytR8ftefexkWn5xNiHUNpPOfSew==", "dev": true, "license": "MIT", - "peer": true, "dependencies": { "call-bound": "^1.0.3" }, @@ -13941,8 +13669,7 @@ "resolved": "https://registry.npmjs.org/js-tokens/-/js-tokens-4.0.0.tgz", "integrity": "sha512-RdJUflcE3cUzKiMqQgsCu06FPu9UdIJO0beYbPhHN4k6apgJtifcoCtT9bcxOpYBtpD2kCM6Sbzg4CausW/PKQ==", "dev": true, - "license": "MIT", - "peer": true + "license": "MIT" }, "node_modules/js-yaml": { "version": "4.3.0", @@ -13960,7 +13687,6 @@ } ], "license": "MIT", - "peer": true, "dependencies": { "argparse": "^2.0.1" }, @@ -13981,7 +13707,6 @@ "integrity": "sha512-YtOli5Cmzy3q4dP26GraSOeAhqecewG04hoO8DY56CH4KJ9Fvv5qKWUCCo3HZob7esJQHCv6/+bnTy72xZZaVQ==", "dev": true, "license": "MIT", - "peer": true, "engines": { "node": ">=12.0.0" } @@ -14075,7 +13800,6 @@ "integrity": "sha512-/sM3dO2FOzXjKQhJuo0Q173wf2KOo8t4I8vHy6lF9poUp7bKT0/NHE8fPX23PwfhnykfqnC2xRxOnVw5XuGIaA==", "dev": true, "license": "MIT", - "peer": true, "bin": { "jsesc": "bin/jsesc" }, @@ -14088,8 +13812,7 @@ "resolved": "https://registry.npmjs.org/json-buffer/-/json-buffer-3.0.1.tgz", "integrity": "sha512-4bV5BfR2mqfQTJm+V5tPPdf+ZpuhiIvTuAB5g8kcrXOZpTT/QwwVRWBywX1ozr6lEuPdbHxwaJlm9G6mI2sfSQ==", "dev": true, - "license": "MIT", - "peer": true + "license": "MIT" }, "node_modules/json-schema": { "version": "0.4.0", @@ -14103,16 +13826,14 @@ "resolved": "https://registry.npmjs.org/json-schema-traverse/-/json-schema-traverse-0.4.1.tgz", "integrity": "sha512-xbbCH5dCYU5T8LcEhhuh7HJ88HXuW3qsI3Y0zOZFKfZEHcpWiHU/Jxzk629Brsab/mMiHQti9wMP+845RPe3Vg==", "dev": true, - "license": "MIT", - "peer": true + "license": "MIT" }, "node_modules/json-stable-stringify-without-jsonify": { "version": "1.0.1", "resolved": "https://registry.npmjs.org/json-stable-stringify-without-jsonify/-/json-stable-stringify-without-jsonify-1.0.1.tgz", "integrity": "sha512-Bdboy+l7tA3OGW6FjyFHWkP5LuByj1Tk33Ljyq0axyzdk9//JSi2u3fP1QSmd1KNwq6VOKYGlAu87CisVir6Pw==", "dev": true, - "license": "MIT", - "peer": true + "license": "MIT" }, "node_modules/json-stringify-safe": { "version": "5.0.1", @@ -14172,6 +13893,7 @@ "https://github.com/sponsors/katex" ], "license": "MIT", + "peer": true, "dependencies": { "commander": "^8.3.0" }, @@ -14194,7 +13916,6 @@ "integrity": "sha512-oxVHkHR/EJf2CNXnWxRLW6mg7JyCCUcG0DtEGmL2ctUo1PNTin1PUil+r/+4r5MpVgC/fn1kjsx7mjSujKqIpw==", "dev": true, "license": "MIT", - "peer": true, "dependencies": { "json-buffer": "3.0.1" } @@ -14251,7 +13972,6 @@ "integrity": "sha512-+bT2uH4E5LGE7h/n3evcS/sQlJXCpIp6ym8OWJ5eV6+67Dsql/LaaT7qJBAt2rzfoa/5QBGBhxDix1dMt2kQKQ==", "dev": true, "license": "MIT", - "peer": true, "dependencies": { "prelude-ls": "^1.2.1", "type-check": "~0.4.0" @@ -14313,7 +14033,8 @@ "version": "4.3.3", "resolved": "https://registry.npmjs.org/linkifyjs/-/linkifyjs-4.3.3.tgz", "integrity": "sha512-P8aEP5U/D1/IlTY2OeYsErdwh9bGuLE30NcXtKEjgdHcahveQoQwM2yZNsioQHsWFz0P7KKudisbrzCgR0sDHg==", - "license": "MIT" + "license": "MIT", + "peer": true }, "node_modules/listr2": { "version": "9.0.5", @@ -14469,16 +14190,14 @@ "resolved": "https://registry.npmjs.org/lodash.debounce/-/lodash.debounce-4.0.8.tgz", "integrity": "sha512-FT1yDzDYEoYWhnSGnpE/4Kj1fLZkDFyqRb7fNt6FdYOSxlUWAtp42Eh6Wb0rGIv/m9Bgo7x4GhQbm5Ys4SG5ow==", "dev": true, - "license": "MIT", - "peer": true + "license": "MIT" }, "node_modules/lodash.merge": { "version": "4.6.2", "resolved": "https://registry.npmjs.org/lodash.merge/-/lodash.merge-4.6.2.tgz", "integrity": "sha512-0KpjqXRVvrYyCsX1swR/XTK0va6VQkQM6MNo7PqW77ByjAhoARA8EfrP1N4+KlKj8YS0ZUCtRT/YUuhyYDujIQ==", "dev": true, - "license": "MIT", - "peer": true + "license": "MIT" }, "node_modules/lodash.once": { "version": "4.1.1", @@ -14653,6 +14372,7 @@ "resolved": "https://registry.npmjs.org/lowlight/-/lowlight-3.3.0.tgz", "integrity": "sha512-0JNhgFoPvP6U6lE/UdVsSq99tn6DhjjpAj5MxG49ewd2mOBVtwWYIT8ClyABhq198aXXODMU6Ox8DrGy/CpTZQ==", "license": "MIT", + "peer": true, "dependencies": { "@types/hast": "^3.0.0", "devlop": "^1.0.0", @@ -14669,7 +14389,6 @@ "integrity": "sha512-KpNARQA3Iwv+jTA0utUVVbrh+Jlrr1Fv0e56GGzAFOXN7dk/FviaDW8LHmK52DlcH4WP2n6gI8vN1aesBFgo9w==", "dev": true, "license": "ISC", - "peer": true, "dependencies": { "yallist": "^3.0.2" } @@ -14740,6 +14459,7 @@ } ], "license": "MIT", + "peer": true, "dependencies": { "argparse": "^2.0.1", "entities": "^4.4.0", @@ -15742,7 +15462,6 @@ "integrity": "sha512-PHI5f1O0EP5xJ9gQmFGMS6IZcrVvTjpXjz7Na41gTE7eE2hK11lg04CECCYEEjdc17EV4DO+fkGEtt7TpTaTiQ==", "dev": true, "license": "MIT", - "peer": true, "bin": { "napi-postinstall": "lib/cli.js" }, @@ -15758,8 +15477,7 @@ "resolved": "https://registry.npmjs.org/natural-compare/-/natural-compare-1.4.0.tgz", "integrity": "sha512-OWND8ei3VtNC9h7V60qff3SVobHr996CTwgxubgyQYEpg290h9J0buyECNNJexkFm5sOajh5G116RYA1c8ZMSw==", "dev": true, - "license": "MIT", - "peer": true + "license": "MIT" }, "node_modules/nested-property": { "version": "4.0.0", @@ -15773,8 +15491,7 @@ "integrity": "sha512-5m3bsyrjFWE1xf7nz7YXdN4udnVtXK6/Yfgn5qnahL6bCkf2yKt4k3nuTKAtT4r3IG8JNR2ncsIMdZuAzJjHQQ==", "dev": true, "license": "MIT", - "optional": true, - "peer": true + "optional": true }, "node_modules/node-domexception": { "version": "1.0.0", @@ -15924,7 +15641,6 @@ "integrity": "sha512-lqjrjmaOoAnWfMmBPL+XNnynZh2+swxiX3WUE0s4yEHI6m+AwrK2UZOimIRl3X/4QctVqS8AiZjFqyOGrMXb/w==", "dev": true, "license": "BSD-2-Clause", - "peer": true, "dependencies": { "boolbase": "^1.0.0" }, @@ -15999,7 +15715,6 @@ "integrity": "sha512-k6E21FzySsSK5a21KRADBd/NGneRegFO5pLHfdQLpRDETUNJueLXs3WCzyQ3tFRDYgbq3KHGXfTbi2bs8WQ6rQ==", "dev": true, "license": "MIT", - "peer": true, "dependencies": { "call-bind": "^1.0.7", "define-properties": "^1.2.1", @@ -16019,7 +15734,6 @@ "integrity": "sha512-+Lhy3TQTuzXI5hevh8sBGqbmurHbbIjAi0Z4S63nthVLmLxfbj4T54a4CfZrXIrt9iP4mVAPYMo/v99taj3wjQ==", "dev": true, "license": "MIT", - "peer": true, "dependencies": { "call-bind": "^1.0.7", "define-properties": "^1.2.1", @@ -16035,7 +15749,6 @@ "integrity": "sha512-gXah6aZrcUxjWg2zR2MwouP2eHlCBzdV4pygudehaKXSGW4v2AsRQUK+lwwXhii6KFZcunEnmSUoYp5CXibxtA==", "dev": true, "license": "MIT", - "peer": true, "dependencies": { "call-bind": "^1.0.8", "call-bound": "^1.0.3", @@ -16092,7 +15805,6 @@ "integrity": "sha512-6IpQ7mKUxRcZNLIObR0hz7lxsapSSIYNZJwXPGeF0mTVqGKFIXj1DQcMoT22S3ROcLyY/rz0PWaWZ9ayWmad9g==", "dev": true, "license": "MIT", - "peer": true, "dependencies": { "deep-is": "^0.1.3", "fast-levenshtein": "^2.0.6", @@ -16131,7 +15843,6 @@ "integrity": "sha512-qFOyK5PjiWZd+QQIh+1jhdb9LpxTF0qs7Pm8o5QHYZ0M3vKqSqzsZaEB6oWlxZ+q2sJBMI/Ktgd2N5ZwQoRHfg==", "dev": true, "license": "MIT", - "peer": true, "dependencies": { "get-intrinsic": "^1.2.6", "object-keys": "^1.1.1", @@ -16243,7 +15954,6 @@ "integrity": "sha512-GQ2EWRpQV8/o+Aw8YqtfZZPfNRWZYkbidE9k5rpl/hC3vtHHBfGm2Ifi6qWV+coDGkrUKZAxE3Lot5kcsRlh+g==", "dev": true, "license": "MIT", - "peer": true, "dependencies": { "callsites": "^3.0.0" }, @@ -16338,7 +16048,6 @@ "integrity": "sha512-AVbw3UJ2e9bq64vSaS9Am0fje1Pa8pbGqTTsmXfaIiMpnr5DlDhfJOuLj9Sf95ZPVDAUerDfEk88MPmPe7UCQg==", "dev": true, "license": "MIT", - "peer": true, "engines": { "node": ">=0.10.0" } @@ -16620,6 +16329,7 @@ } ], "license": "MIT", + "peer": true, "dependencies": { "nanoid": "^3.3.16", "picocolors": "^1.1.1", @@ -16635,7 +16345,6 @@ "integrity": "sha512-Q8qQfPiZ+THO/3ZrOrO0cJJKfpYCagtMUkXbnEfmgUjwXg6z/WBeOyS9APBBPCTSiDV+s4SwQGu8yFsiMRIudg==", "dev": true, "license": "MIT", - "peer": true, "dependencies": { "cssesc": "^3.0.0", "util-deprecate": "^1.0.2" @@ -16708,7 +16417,6 @@ "integrity": "sha512-vkcDPrRZo1QZLbn5RLGPpg/WmIQ65qoWWhcGKf/b5eplkkarX0m9z8ppCat4mlOqUsWpyNuYgO3VRyrYHSzX5g==", "dev": true, "license": "MIT", - "peer": true, "engines": { "node": ">= 0.8.0" } @@ -16902,6 +16610,7 @@ "resolved": "https://registry.npmjs.org/prosemirror-model/-/prosemirror-model-1.25.4.tgz", "integrity": "sha512-PIM7E43PBxKce8OQeezAs9j4TP+5yDpZVbuurd1h5phUxEKIu+G2a+EUZzIC5nS1mJktDJWzbqS23n1tsAf5QA==", "license": "MIT", + "peer": true, "dependencies": { "orderedmap": "^2.0.0" } @@ -16932,6 +16641,7 @@ "resolved": "https://registry.npmjs.org/prosemirror-state/-/prosemirror-state-1.4.4.tgz", "integrity": "sha512-6jiYHH2CIGbCfnxdHbXZ12gySFY/fz/ulZE333G6bPqIZ4F+TXo9ifiR86nAHpWnfoNjOb3o5ESi7J8Uz1jXHw==", "license": "MIT", + "peer": true, "dependencies": { "prosemirror-model": "^1.0.0", "prosemirror-transform": "^1.0.0", @@ -16977,6 +16687,7 @@ "resolved": "https://registry.npmjs.org/prosemirror-view/-/prosemirror-view-1.41.3.tgz", "integrity": "sha512-SqMiYMUQNNBP9kfPhLO8WXEk/fon47vc52FQsUiJzTBuyjKgEcoAwMyF04eQ4WZ2ArMn7+ReypYL60aKngbACQ==", "license": "MIT", + "peer": true, "dependencies": { "prosemirror-model": "^1.20.0", "prosemirror-state": "^1.0.0", @@ -17211,7 +16922,6 @@ "integrity": "sha512-00o4I+DVrefhv+nX0ulyi3biSHCPDe+yLv5o/p6d/UVlirijB8E16FtfwSAi4g3tcqrQ4lRAqQSoFEZJehYEcw==", "dev": true, "license": "MIT", - "peer": true, "dependencies": { "call-bind": "^1.0.8", "define-properties": "^1.2.1", @@ -17234,8 +16944,7 @@ "resolved": "https://registry.npmjs.org/regenerate/-/regenerate-1.4.2.tgz", "integrity": "sha512-zrceR/XhGYU/d/opr2EKO7aRHUeiBI8qjtfHqADTwZd6Szfy16la6kqD0MIUs5z5hx6AaKa+PixpPrR289+I0A==", "dev": true, - "license": "MIT", - "peer": true + "license": "MIT" }, "node_modules/regenerate-unicode-properties": { "version": "10.2.2", @@ -17243,7 +16952,6 @@ "integrity": "sha512-m03P+zhBeQd1RGnYxrGyDAPpWX/epKirLrp8e3qevZdVkKtnCrjjWczIbYc8+xd6vcTStVlqfycTx1KR4LOr0g==", "dev": true, "license": "MIT", - "peer": true, "dependencies": { "regenerate": "^1.4.2" }, @@ -17278,7 +16986,6 @@ "integrity": "sha512-0ghuzq67LI9bLXpOX/ISfve/Mq33a4aFRzoQYhnnok1JOFpmE/A2TBGkNVenOGEeSBCjIiWcc6MVOG5HEQv0sA==", "dev": true, "license": "MIT", - "peer": true, "dependencies": { "regenerate": "^1.4.2", "regenerate-unicode-properties": "^10.2.2", @@ -17296,8 +17003,7 @@ "resolved": "https://registry.npmjs.org/regjsgen/-/regjsgen-0.8.0.tgz", "integrity": "sha512-RvwtGe3d7LvWiDQXeQw8p5asZUmfU1G/l6WbUXeHta7Y2PEIvBTwH6E2EfmYUK8pxcxEdEmaomqyp0vZZ7C+3Q==", "dev": true, - "license": "MIT", - "peer": true + "license": "MIT" }, "node_modules/regjsparser": { "version": "0.13.0", @@ -17305,7 +17011,6 @@ "integrity": "sha512-NZQZdC5wOE/H3UT28fVGL+ikOZcEzfMGk/c3iN9UGxzWHMa1op7274oyiUVrAG4B2EuFhus8SvkaYnhvW92p9Q==", "dev": true, "license": "BSD-2-Clause", - "peer": true, "dependencies": { "jsesc": "~3.1.0" }, @@ -17591,7 +17296,6 @@ "integrity": "sha512-L9jEkOi3ASd9PYit2cwRfyppc9NoABujTP8/5gFcbERmo5jUoAKovIC3fsF17pkTnGsrByysqX+Kxd2OTNI1ww==", "dev": true, "license": "MIT", - "peer": true, "engines": { "node": ">=0.10.5" } @@ -17667,7 +17371,6 @@ "integrity": "sha512-pb/MYmXstAkysRFx8piNI1tGFNQIFA3vkE3Gq4EuA1dF6gHp/+vgZqsCGJapvy8N3Q+4o7FwvquPJcnZ7RYy4g==", "dev": true, "license": "MIT", - "peer": true, "engines": { "node": ">=4" } @@ -17678,7 +17381,6 @@ "integrity": "sha512-seS2Tj26TBVOC2NIc2rOe2y2ZO7efxITtLZcGSOnHHNOQ7CkiUBfw0Iw2ck6xkIhPwLhKNLS8BO+hEpngQlqzw==", "dev": true, "license": "MIT", - "peer": true, "funding": { "url": "https://github.com/privatenumber/resolve-pkg-maps?sponsor=1" } @@ -17689,7 +17391,6 @@ "integrity": "sha512-OcXjMsGdhL4XnbShKpAcSqPMzQoYkYyhbEaeSko47MjRP9NfEQMhZkXL1DoFlt9LWQn4YttrdnV6X2OiyzBi+A==", "dev": true, "license": "MIT", - "peer": true, "engines": { "node": ">=10" } @@ -17764,7 +17465,6 @@ "integrity": "sha512-JZkJMZkAGFFPP2YqXZXPbMlMBgsxzE8ILs4lMIX/2o0L9UBw9O/Y3o6wFw/i9YLapcUJWwqbi3kdxIPdC62TIA==", "dev": true, "license": "ISC", - "peer": true, "dependencies": { "glob": "^7.1.3" }, @@ -17781,7 +17481,6 @@ "integrity": "sha512-Edep/X9fGqVNmzKBVsDYIOtD+z1tuezV70LBjdCst9Tqu76lsnvRiZ6oTic1n+/BIwX6QDGAO94PN4N2SADvtw==", "dev": true, "license": "MIT", - "peer": true, "dependencies": { "balanced-match": "^1.0.0", "concat-map": "0.0.1" @@ -17793,7 +17492,6 @@ "integrity": "sha512-nFR0zLpU2YCaRxwoCJvL6UvCH2JFyFVIvwTLsIf21AuHlMskA1hhTdk+LlYJtOlYt9v6dvszD2BGRqBL+iQK9Q==", "dev": true, "license": "ISC", - "peer": true, "dependencies": { "fs.realpath": "^1.0.0", "inflight": "^1.0.4", @@ -17815,7 +17513,6 @@ "integrity": "sha512-VgjWUsnnT6n+NUk6eZq77zeFdpW2LWDzP6zFGrCbHXiYNul5Dzqk2HHQ5uFH2DNW5Xbp8+jVzaeNt94ssEEl4w==", "dev": true, "license": "ISC", - "peer": true, "dependencies": { "brace-expansion": "^1.1.7" }, @@ -17912,6 +17609,7 @@ "integrity": "sha512-2oMpl67a3zCH9H79LeMcbDhXW/UmWG/y2zuqnF2jQq5uq9TbM9TVyXvA4+t+ne2IIkBdrLpAaRQAvo7YI/Yyeg==", "dev": true, "license": "MIT", + "peer": true, "dependencies": { "@types/estree": "1.0.8" }, @@ -18152,7 +17850,6 @@ "integrity": "sha512-AURm5f0jYEOydBj7VQlVvDrjeFgthDdEF5H1dP+6mNpoXOMo1quQqJ4wvJDyRZ9+pO3kGWoOdmV08cSv2aJV6Q==", "dev": true, "license": "MIT", - "peer": true, "dependencies": { "call-bind": "^1.0.8", "call-bound": "^1.0.2", @@ -18194,7 +17891,6 @@ "integrity": "sha512-iKE9w/Z7xCzUMIZqdBsp6pEQvwuEebH4vdpjcDWnyzaI6yl6O9FHvVpmGelvEHNsoY6wGblkxR6Zty/h00WiSA==", "dev": true, "license": "MIT", - "peer": true, "dependencies": { "es-errors": "^1.3.0", "isarray": "^2.0.5" @@ -18285,7 +17981,6 @@ "integrity": "sha512-Qgzu8kfBvo+cA4962jnP1KkS6Dop5NS6g7R5LFYJr4b8Ub94PPQXUksCw9PvXoeXPRRddRNC5C1JQUR2SMGtnA==", "dev": true, "license": "MIT", - "peer": true, "dependencies": { "readdirp": "^4.0.1" }, @@ -18302,7 +17997,6 @@ "integrity": "sha512-GDhwkLfywWL2s6vEjyhri+eXmfH6j1L7JE27WhqLeYzoh/A3DBaYGEj2H/HFZCn/kMfim73FXxEJTw06WtxQwg==", "dev": true, "license": "MIT", - "peer": true, "engines": { "node": ">= 14.18.0" }, @@ -18336,7 +18030,6 @@ "integrity": "sha512-BR7VvDCVHO+q2xBEWskxS6DJE1qRnb7DxzUrogb71CWoSficBxYsiAGd+Kl0mmq/MprG9yArRkyrQxTO6XjMzA==", "dev": true, "license": "ISC", - "peer": true, "bin": { "semver": "bin/semver.js" } @@ -18381,7 +18074,6 @@ "integrity": "sha512-RJRdvCo6IAnPdsvP/7m6bsQqNnn1FCBX5ZNtFL98MmFF/4xAIJTIg1YbHW5DC2W5SKZanrC6i4HsJqlajw/dZw==", "dev": true, "license": "MIT", - "peer": true, "dependencies": { "dunder-proto": "^1.0.1", "es-errors": "^1.3.0", @@ -18924,8 +18616,7 @@ "resolved": "https://registry.npmjs.org/stable-hash/-/stable-hash-0.0.5.tgz", "integrity": "sha512-+L3ccpzibovGXFK+Ap/f8LOS0ahMrHTf3xu7mMLSpEGU0EO9ucaysSylKo9eRDFNhWve/y275iPmIZ4z39a9iA==", "dev": true, - "license": "MIT", - "peer": true + "license": "MIT" }, "node_modules/stackback": { "version": "0.0.2", @@ -19079,7 +18770,6 @@ "integrity": "sha512-Rs66F0P/1kedk5lyYyH9uBzuiI/kNRmwJAR9quK6VOtIpZ2G+hMZd+HQbbv25MgCA6gEffoMZYxlTod4WcdrKA==", "dev": true, "license": "MIT", - "peer": true, "dependencies": { "call-bind": "^1.0.8", "call-bound": "^1.0.2", @@ -19102,7 +18792,6 @@ "integrity": "sha512-G7Ok5C6E/j4SGfyLCloXTrngQIQU3PWtXGst3yM7Bea9FRURf1S42ZHlZZtsNque2FN2PoUhfZXYLNWwEr4dLQ==", "dev": true, "license": "MIT", - "peer": true, "dependencies": { "call-bind": "^1.0.8", "call-bound": "^1.0.2", @@ -19122,7 +18811,6 @@ "integrity": "sha512-UXSH262CSZY1tfu3G3Secr6uGLCFVPMhIqHjlgCUtCCcgihYc/xKs9djMTMUOb2j1mVSeU8EU6NWc/iQKU6Gfg==", "dev": true, "license": "MIT", - "peer": true, "dependencies": { "call-bind": "^1.0.7", "define-properties": "^1.2.1", @@ -19419,8 +19107,7 @@ "resolved": "https://registry.npmjs.org/text-table/-/text-table-0.2.0.tgz", "integrity": "sha512-N+8UisAXDGk8PFXP4HAzVR9nbfmVJ3zYLAWiTIoqC5v5isinhr+r5uaO8+7r3BMfuNIufIsA7RdpVgacC2cSpw==", "dev": true, - "license": "MIT", - "peer": true + "license": "MIT" }, "node_modules/throttleit": { "version": "1.0.1", @@ -19635,7 +19322,6 @@ "integrity": "sha512-i3eMG77UTMD0hZhgRS562pv83RC6ukSAC2GMNWc+9dieh/+jDM5u5YG+NHX6VNDRHQcHwmsTHctP9LhbC3WxVw==", "dev": true, "license": "MIT", - "peer": true, "engines": { "node": ">=16" }, @@ -19658,7 +19344,6 @@ "integrity": "sha512-2Ac2RgzDe/cn48GvOe3M+o82pEFewD3UPbyoUHHdKasHwJKjds4fLXWf/Ux5kATBKN20oaFGu+jbElp1pos0mg==", "dev": true, "license": "MIT", - "peer": true, "dependencies": { "@types/json5": "^0.0.29", "json5": "^1.0.2", @@ -19672,7 +19357,6 @@ "integrity": "sha512-g1MWMLBiz8FKi1e4w0UyVL3w+iJceWAFBAaBnnGKOpNa5f8TLktkbre1+s6oICydWAm+HRUGTmI+//xv2hvXYA==", "dev": true, "license": "MIT", - "peer": true, "dependencies": { "minimist": "^1.2.0" }, @@ -20233,7 +19917,6 @@ "integrity": "sha512-XleUoc9uwGXqjWwXaUTZAmzMcFZ5858QA2vvx1Ur5xIcixXIP+8LnFDgRplU30us6teqdlskFfu+ae4K79Ooew==", "dev": true, "license": "MIT", - "peer": true, "dependencies": { "prelude-ls": "^1.2.1" }, @@ -20275,7 +19958,6 @@ "integrity": "sha512-BaXgOuIxz8n8pIq3e7Atg/7s+DpiYrxn4vdot3w9KbnBhcRQq6o3xemQdIfynqSeXeDrF32x+WvfzmOjPiY9lg==", "dev": true, "license": "MIT", - "peer": true, "dependencies": { "call-bind": "^1.0.8", "for-each": "^0.3.3", @@ -20296,7 +19978,6 @@ "integrity": "sha512-bTlAFB/FBYMcuX81gbL4OcpH5PmlFHqlCCpAl8AlEzMz5k53oNDvN8p1PNOWLEmI2x4orp3raOFB51tv9X+MFQ==", "dev": true, "license": "MIT", - "peer": true, "dependencies": { "available-typed-arrays": "^1.0.7", "call-bind": "^1.0.8", @@ -20319,7 +20000,6 @@ "integrity": "sha512-3KS2b+kL7fsuk/eJZ7EQdnEmQoaho/r6KUef7hxvltNA5DR8NAUM+8wJMbJyZ4G9/7i3v5zPBIMN5aybAh2/Jg==", "dev": true, "license": "MIT", - "peer": true, "dependencies": { "call-bind": "^1.0.7", "for-each": "^0.3.3", @@ -20341,6 +20021,7 @@ "integrity": "sha512-jl1vZzPDinLr9eUt3J/t7V6FgNEw9QjvBPdysz9KfQDD41fQrC2Y4vKQdiaUpFT4bXlb1RHhLpp8wtm6M5TgSw==", "dev": true, "license": "Apache-2.0", + "peer": true, "bin": { "tsc": "bin/tsc", "tsserver": "bin/tsserver" @@ -20373,7 +20054,6 @@ "integrity": "sha512-nWJ91DjeOkej/TA8pXQ3myruKpKEYgqvpw9lz4OPHj/NWFNluYrjbz9j01CJ8yKQd2g4jFoOkINCTW2I5LEEyw==", "dev": true, "license": "MIT", - "peer": true, "dependencies": { "call-bound": "^1.0.3", "has-bigints": "^1.0.2", @@ -20398,12 +20078,11 @@ } }, "node_modules/undici-types": { - "version": "7.16.0", - "resolved": "https://registry.npmjs.org/undici-types/-/undici-types-7.16.0.tgz", - "integrity": "sha512-Zz+aZWSj8LE6zoxD+xrjh4VfkIG8Ya6LvYkZqtUQGJPZjYl53ypCaUwWqo7eI0x66KBGeRo+mlBEkMSeSZ38Nw==", + "version": "8.3.0", + "resolved": "https://registry.npmjs.org/undici-types/-/undici-types-8.3.0.tgz", + "integrity": "sha512-j375ScV60dom+YkPFIfTLcOiPxkN/buHz5GobjLhixFuANaNs3C9l4GmrWqejgXWJ7BbJcFYpTEUkS1Ge8bpZQ==", "dev": true, - "license": "MIT", - "optional": true + "license": "MIT" }, "node_modules/unicode-canonical-property-names-ecmascript": { "version": "2.0.1", @@ -20411,7 +20090,6 @@ "integrity": "sha512-dA8WbNeb2a6oQzAQ55YlT5vQAWGV9WXOsi3SskE3bcCdM0P4SDd+24zS/OCacdRq5BkdsRj9q3Pg6YyQoxIGqg==", "dev": true, "license": "MIT", - "peer": true, "engines": { "node": ">=4" } @@ -20422,7 +20100,6 @@ "integrity": "sha512-5kaZCrbp5mmbz5ulBkDkbY0SsPOjKqVS35VpL9ulMPfSl0J0Xsm+9Evphv9CoIZFwre7aJoa94AY6seMKGVN5Q==", "dev": true, "license": "MIT", - "peer": true, "dependencies": { "unicode-canonical-property-names-ecmascript": "^2.0.0", "unicode-property-aliases-ecmascript": "^2.0.0" @@ -20437,7 +20114,6 @@ "integrity": "sha512-JQ84qTuMg4nVkx8ga4A16a1epI9H6uTXAknqxkGF/aFfRLw1xC/Bp24HNLaZhHSkWd3+84t8iXnp1J0kYcZHhg==", "dev": true, "license": "MIT", - "peer": true, "engines": { "node": ">=4" } @@ -20448,7 +20124,6 @@ "integrity": "sha512-hpbDzxUY9BFwX+UeBnxv3Sh1q7HFxj48DTmXchNgRa46lO8uj3/1iEn3MiNUYTg1g9ctIqXCCERn8gYZhHC5lQ==", "dev": true, "license": "MIT", - "peer": true, "engines": { "node": ">=4" } @@ -20584,7 +20259,6 @@ "dev": true, "hasInstallScript": true, "license": "MIT", - "peer": true, "dependencies": { "napi-postinstall": "^0.3.0" }, @@ -20660,7 +20334,6 @@ "integrity": "sha512-7rKUyy33Q1yc98pQ1DAmLtwX109F7TIfWlW1Ydo8Wl1ii1SeHieeh0HHfPeL2fMXK6z0s8ecKs9frCuLJvndBg==", "dev": true, "license": "BSD-2-Clause", - "peer": true, "dependencies": { "punycode": "^2.1.0" } @@ -20788,6 +20461,7 @@ "integrity": "sha512-4XP60spRGjSZFf1qYH+dJIkK2znL3zQfl9KkOV9MkkRR/3Dls0dxaBsQPTloEc5BLXWPL9vsOxopxyKoMmDueg==", "dev": true, "license": "MIT", + "peer": true, "dependencies": { "esbuild": "^0.27.0 || ^0.28.0", "fdir": "^6.5.0", @@ -21426,6 +21100,7 @@ "integrity": "sha512-R9jUTe5S4Qb0HCd4TNqpC7oGcrMssMRGXLW80ubjWsW9VH5GF8y1Y0SFLY9AbqSk6nt0PnOx4H4WNJYZ13GUPw==", "dev": true, "license": "MIT", + "peer": true, "dependencies": { "@vitest/expect": "4.1.10", "@vitest/mocker": "4.1.10", @@ -21536,6 +21211,7 @@ "resolved": "https://registry.npmjs.org/vue/-/vue-2.7.16.tgz", "integrity": "sha512-4gCtFXaAA3zYZdTp5s4Hl2sozuySsgz4jy1EnpBHNfpMa9dK1ZCG7viqBPCwXtmgc8nHqUsAu3G4gtmXkkY3Sw==", "license": "MIT", + "peer": true, "dependencies": { "@vue/compiler-sfc": "2.7.16", "csstype": "^3.1.0" @@ -21591,7 +21267,6 @@ "integrity": "sha512-2rYRLWlIpaiN8xbPiDyXZXRgLGOtWxERV7ND5fFAv5qo1D2N9Fu9MNajBNc6o13lZ+24DAWCkQCvj4klgmcITg==", "dev": true, "license": "MIT", - "peer": true, "dependencies": { "debug": "^4.3.4", "eslint-scope": "^7.1.1", @@ -21617,7 +21292,6 @@ "integrity": "sha512-dOt21O7lTMhDM+X9mB4GX+DZrZtCUJPL/wlcTqxyrx5IvO0IYtILdtrQGQp+8n5S0gwSVmOf9NQrjMOgfQZlIg==", "dev": true, "license": "BSD-2-Clause", - "peer": true, "dependencies": { "esrecurse": "^4.3.0", "estraverse": "^5.2.0" @@ -21635,7 +21309,6 @@ "integrity": "sha512-wpc+LXeiyiisxPlEkUzU6svyS1frIO3Mgxj1fdy7Pm8Ygzguax2N3Fa/D/ag1WqbOprdI+uY6wMUl8/a2G+iag==", "dev": true, "license": "Apache-2.0", - "peer": true, "engines": { "node": "^12.22.0 || ^14.17.0 || >=16.0.0" }, @@ -21649,7 +21322,6 @@ "integrity": "sha512-MMdARuVEQziNTeJD8DgMqmhwR11BRQ/cBP+pLtYdSTnf3MIO8fFeiINEbX36ZdNlfU/7A9f3gUw49B3oQsvwBA==", "dev": true, "license": "BSD-2-Clause", - "peer": true, "engines": { "node": ">=4.0" } @@ -21660,7 +21332,6 @@ "integrity": "sha512-SdsKMrI9TdgjdweUSR9MweHA4EJ8YxHn8DFaDisvhVlUOe4BF1tLD7GAj0lIqWVl+dPb/rExr0Btby5loQm20Q==", "dev": true, "license": "ISC", - "peer": true, "bin": { "semver": "bin/semver.js" }, @@ -21710,6 +21381,7 @@ "integrity": "sha512-AYbUWAJHLGGQM7+cNTELw+KsOG9nl2CnSv467WobS5Cv9uk3wFcnr1Etsz2sEIHEZvw1U+o9mRlEO6QbZvUPGQ==", "dev": true, "license": "MIT", + "peer": true, "dependencies": { "de-indent": "^1.0.2", "he": "^1.2.0" @@ -21730,6 +21402,7 @@ "integrity": "sha512-P7OP77b2h/Pmk+lZdJ0YWs+5tJ6J2+uOQPo7tlBnY44QqQSPYvS0qVT4wqDJgwrZaLe47etJLLQRFia71GYITw==", "dev": true, "license": "MIT", + "peer": true, "dependencies": { "@volar/typescript": "2.4.15", "@vue/language-core": "2.2.12" @@ -22035,7 +21708,6 @@ "integrity": "sha512-6iBczoX+kDQ7a3+YJBnh3T+KZRxM/iYNPXicqk66/Qfm1b93iu+yOImkg0zHbj5LNOcNv1TEADiZ0xa34B4q6Q==", "dev": true, "license": "MIT", - "peer": true, "dependencies": { "call-bound": "^1.0.2", "function.prototype.name": "^1.1.6", @@ -22122,7 +21794,6 @@ "integrity": "sha512-BN22B5eaMMI9UMtjrGd5g5eCYPpCPDUy0FJXbYsaT5zYxjFOckS53SQDE3pWkVoWpHXVb3BrYcEN4Twa55B5cA==", "dev": true, "license": "MIT", - "peer": true, "engines": { "node": ">=0.10.0" } @@ -22181,7 +21852,6 @@ "integrity": "sha512-ICP2e+jsHvAj2E2lIHxa5tjXRlKDJo4IdvPvCXbXQGdzSfmSpNVyIKMvoZHjDY9DP0zV17iI85o90vRFXNccRw==", "dev": true, "license": "Apache-2.0", - "peer": true, "engines": { "node": ">=12" } @@ -22247,6 +21917,7 @@ "resolved": "https://registry.npmjs.org/y-protocols/-/y-protocols-1.0.7.tgz", "integrity": "sha512-YSVsLoXxO67J6eE/nV4AtFtT3QEotZf5sK5BHxFBXso7VDUT3Tx07IfA6hsu5Q5OmBdMkQVmFZ9QOA7fikWvnw==", "license": "MIT", + "peer": true, "dependencies": { "lib0": "^0.2.85" }, @@ -22267,18 +21938,19 @@ "resolved": "https://registry.npmjs.org/yallist/-/yallist-3.1.1.tgz", "integrity": "sha512-a4UGQaWPH59mOXUYnAG2ewncQS4i4F43Tv3JoAM+s2VDAmS9NsK8GpDMLrCHPksFT7h3K6TOoUNn2pb7RoXx4g==", "dev": true, - "license": "ISC", - "peer": true + "license": "ISC" }, "node_modules/yauzl": { - "version": "2.10.0", - "resolved": "https://registry.npmjs.org/yauzl/-/yauzl-2.10.0.tgz", - "integrity": "sha512-p4a9I6X6nu6IhoGmBqAcbJy1mlC4j27vEPZX9F4L4/vZT3Lyq1VkFHw/V/PUcB9Buo+DG3iHkT0x3Qya58zc3g==", + "version": "3.4.0", + "resolved": "https://registry.npmjs.org/yauzl/-/yauzl-3.4.0.tgz", + "integrity": "sha512-jIH9yLR9wqr0wOS0TpBvo/g/2UgZH5qePVbjgRliiF0BYvOZyaBknKsF+x9Iht0O6sqgnB93rCICdOZFecJuDw==", "dev": true, "license": "MIT", "dependencies": { - "buffer-crc32": "~0.2.3", - "fd-slicer": "~1.1.0" + "pend": "~1.2.0" + }, + "engines": { + "node": ">=12" } }, "node_modules/yjs": { @@ -22286,6 +21958,7 @@ "resolved": "https://registry.npmjs.org/yjs/-/yjs-13.6.31.tgz", "integrity": "sha512-Eq+5BRfbeGyqGVrTJL3bEcr8gKkxPuyuoHmAwpk52fDb8kOVMrfVSTRPd6yiGgX5Fskb96qCRjzjbRjrL4YEnw==", "license": "MIT", + "peer": true, "dependencies": { "lib0": "^0.2.99" }, diff --git a/package.json b/package.json index fcc8f21356a..82cce96d969 100644 --- a/package.json +++ b/package.json @@ -114,12 +114,13 @@ "@nextcloud/vite-config": "^1.7.2", "@playwright/test": "^1.60.0", "@types/markdown-it": "^14.1.2", + "@types/node": "^26.1.2", "@vitejs/plugin-vue2": "^2.3.4", "@vitest/coverage-v8": "^4.1.10", "@vue/test-utils": "^1.3.0 <2", "@vue/tsconfig": "^0.5.1", "@vueuse/core": "^11.3.0", - "cypress": "^15.12.0", + "cypress": "^15.19.0", "cypress-split": "^1.24.31", "cypress-vite": "^1.8.0", "eslint-config-prettier": "^10.1.8",