From 5093400ad80b2d7c26d1ecd012fea5e0c1a4ad76 Mon Sep 17 00:00:00 2001 From: Dominik Schmidt Date: Wed, 22 Oct 2025 15:31:23 +0200 Subject: [PATCH] feat(keepass): add work in progress keepass app --- dev/docker/csp.yaml | 4 + docker-compose.yml | 1 + packages/web-app-keepass/README.md | 3 + packages/web-app-keepass/l10n/.tx/config | 10 + .../web-app-keepass/l10n/translations.json | 1 + packages/web-app-keepass/package.json | 22 + packages/web-app-keepass/src/App.vue | 608 ++++++++++++++++++ .../src/components/GroupComponent.vue | 130 ++++ packages/web-app-keepass/src/index.ts | 54 ++ packages/web-app-keepass/src/kdbx-init.ts | 475 ++++++++++++++ packages/web-app-keepass/src/kdbx-logger.ts | 87 +++ .../web-app-keepass/tests/unit/App.spec.ts | 5 + packages/web-app-keepass/tsconfig.json | 3 + packages/web-app-keepass/vite.config.ts | 24 + pnpm-lock.yaml | 325 ++++++++++ 15 files changed, 1752 insertions(+) create mode 100644 packages/web-app-keepass/README.md create mode 100644 packages/web-app-keepass/l10n/.tx/config create mode 100644 packages/web-app-keepass/l10n/translations.json create mode 100644 packages/web-app-keepass/package.json create mode 100644 packages/web-app-keepass/src/App.vue create mode 100644 packages/web-app-keepass/src/components/GroupComponent.vue create mode 100644 packages/web-app-keepass/src/index.ts create mode 100644 packages/web-app-keepass/src/kdbx-init.ts create mode 100644 packages/web-app-keepass/src/kdbx-logger.ts create mode 100644 packages/web-app-keepass/tests/unit/App.spec.ts create mode 100644 packages/web-app-keepass/tsconfig.json create mode 100644 packages/web-app-keepass/vite.config.ts diff --git a/dev/docker/csp.yaml b/dev/docker/csp.yaml index 36657287..47222fd9 100644 --- a/dev/docker/csp.yaml +++ b/dev/docker/csp.yaml @@ -30,7 +30,11 @@ directives: script-src: - "'self'" - "'unsafe-inline'" + - "'unsafe-eval'" - 'https://www.gstatic.com/' style-src: - "'self'" - "'unsafe-inline'" + worker-src: + - "'self'" + - 'blob:' diff --git a/docker-compose.yml b/docker-compose.yml index bf3c5171..4cb52482 100644 --- a/docker-compose.yml +++ b/docker-compose.yml @@ -45,6 +45,7 @@ services: - ./packages/web-app-external-sites/dist:/web/apps/external-sites - ./packages/web-app-importer/dist:/web/apps/importer - ./packages/web-app-json-viewer/dist:/web/apps/json-viewer + - ./packages/web-app-keepass/dist:/web/apps/keepass - ./packages/web-app-maps/dist:/web/apps/maps - ./packages/web-app-progress-bars/dist:/web/apps/progress-bars - ./packages/web-app-unzip/dist:/web/apps/unzip diff --git a/packages/web-app-keepass/README.md b/packages/web-app-keepass/README.md new file mode 100644 index 00000000..7f9dfd54 --- /dev/null +++ b/packages/web-app-keepass/README.md @@ -0,0 +1,3 @@ +# web-app-keepass + +This application can be used for viewing (but not changing) `.kdbx` files from KeePass(XC). diff --git a/packages/web-app-keepass/l10n/.tx/config b/packages/web-app-keepass/l10n/.tx/config new file mode 100644 index 00000000..9ed325f4 --- /dev/null +++ b/packages/web-app-keepass/l10n/.tx/config @@ -0,0 +1,10 @@ +[main] +host = https://www.transifex.com + +[o:opencloud-eu:p:opencloud-eu:r:web-extensions-keepass] +file_filter = locale//app.po +minimum_perc = 0 +resource_name = web-extensions-keepass +source_file = template.pot +source_lang = en +type = PO diff --git a/packages/web-app-keepass/l10n/translations.json b/packages/web-app-keepass/l10n/translations.json new file mode 100644 index 00000000..0967ef42 --- /dev/null +++ b/packages/web-app-keepass/l10n/translations.json @@ -0,0 +1 @@ +{} diff --git a/packages/web-app-keepass/package.json b/packages/web-app-keepass/package.json new file mode 100644 index 00000000..5ef87552 --- /dev/null +++ b/packages/web-app-keepass/package.json @@ -0,0 +1,22 @@ +{ + "name": "keepass", + "version": "1.0.0", + "private": true, + "description": "OpenCloud Web Keepass", + "license": "AGPL-3.0", + "type": "module", + "scripts": { + "build": "pnpm vite build", + "build:w": "pnpm vite build --watch --mode development", + "check:types": "vue-tsc --noEmit", + "test:unit": "NODE_OPTIONS=--unhandled-rejections=throw vitest" + }, + "devDependencies": { + "@opencloud-eu/web-client": "^3.0.0", + "@opencloud-eu/web-pkg": "^3.0.0", + "argon2-browser": "^1.15.4", + "kdbxweb": "^2.1.1", + "vue": "^3.4.21", + "vue3-gettext": "^2.4.0" + } +} diff --git a/packages/web-app-keepass/src/App.vue b/packages/web-app-keepass/src/App.vue new file mode 100644 index 00000000..e5f9ecb6 --- /dev/null +++ b/packages/web-app-keepass/src/App.vue @@ -0,0 +1,608 @@ + + + + diff --git a/packages/web-app-keepass/src/components/GroupComponent.vue b/packages/web-app-keepass/src/components/GroupComponent.vue new file mode 100644 index 00000000..39c621dc --- /dev/null +++ b/packages/web-app-keepass/src/components/GroupComponent.vue @@ -0,0 +1,130 @@ + + + diff --git a/packages/web-app-keepass/src/index.ts b/packages/web-app-keepass/src/index.ts new file mode 100644 index 00000000..75a32993 --- /dev/null +++ b/packages/web-app-keepass/src/index.ts @@ -0,0 +1,54 @@ +import { AppWrapperRoute, defineWebApplication } from '@opencloud-eu/web-pkg' +import { useGettext } from 'vue3-gettext' +import App from './App.vue' +import translations from '../l10n/translations.json' + +// setup argon2 worker +import { KdbxwebInit } from './kdbx-init' +KdbxwebInit.init() + +const applicationId = 'keepass' +export default defineWebApplication({ + setup() { + const { $gettext } = useGettext() + + ;(async () => {})() + + const routes = [ + { + name: applicationId, + path: '/:driveAliasAndItem(.*)?', + component: AppWrapperRoute(App, { + applicationId, + fileContentOptions: { + responseType: 'arraybuffer' + } + }), + meta: { + authContext: 'hybrid', + title: $gettext('Keepass'), + patchCleanPath: true + } + } + ] + + const appInfo = { + name: $gettext('Keepass'), + id: applicationId, + icon: 'lock', + defaultExtension: 'kdbx', + extensions: [ + { + extension: 'kdbx', + routeName: 'keepass' + } + ] + } + + return { + appInfo, + routes, + translations + } + } +}) diff --git a/packages/web-app-keepass/src/kdbx-init.ts b/packages/web-app-keepass/src/kdbx-init.ts new file mode 100644 index 00000000..b277b3e0 --- /dev/null +++ b/packages/web-app-keepass/src/kdbx-init.ts @@ -0,0 +1,475 @@ +// based on https://github.com/keeweb/keeweb/blob/b4bb8e2ca617ad2b9d5a4d210552b6482e4ff503/app/scripts/util/kdbxweb/kdbxweb-init.js +// released under MIT license + +import * as kdbxweb from 'kdbxweb' +import { Logger } from './kdbx-logger' +import argon2Raw from 'argon2-browser/dist/argon2.js?raw' +import argon2WasmSIMD from 'argon2-browser/dist/argon2-simd.wasm?base64' +import argon2WasmRegular from 'argon2-browser/dist/argon2.wasm?base64' + +const logger = new (Logger as any)('argon2') + +const KdbxwebInit = { + runtimeModule: null, + init() { + kdbxweb.CryptoEngine.setArgon2Impl((...args) => this.argon2(...args)) + }, + + argon2(password, salt, memory, iterations, length, parallelism, type, version) { + const startTime = performance.now() + console.log( + `[ARGON2] Starting argon2 with params: memory=${memory}, iterations=${iterations}, length=${length}, parallelism=${parallelism}, type=${type}` + ) + const args = { password, salt, memory, iterations, length, parallelism, type, version } + return this.loadRuntime(memory).then((runtime) => { + const loadedTime = performance.now() + console.log(`[ARGON2] Runtime loaded in ${loadedTime - startTime}ms`) + const ts = logger.ts() + return runtime.hash(args).then((hash) => { + const completedTime = performance.now() + console.log( + `[ARGON2] Hash computed in ${completedTime - loadedTime}ms (total: ${completedTime - startTime}ms)` + ) + logger.debug('Hash computed', logger.ts(ts)) + return hash + }) + }) + }, + + loadRuntime(requiredMemory) { + const runtimeStartTime = performance.now() + if (this.runtimeModule) { + console.log(`[ARGON2] Using cached runtime module`) + return Promise.resolve(this.runtimeModule) + } + if (!globalThis.WebAssembly) { + return Promise.reject('WebAssembly is not supported') + } + + console.log(`[ARGON2] Creating new runtime for memory: ${requiredMemory}KB`) + console.log(`[ARGON2] SharedArrayBuffer supported:`, typeof SharedArrayBuffer !== 'undefined') + console.log(`[ARGON2] Atomics supported:`, typeof Atomics !== 'undefined') + console.log(`[ARGON2] crossOriginIsolated:`, self.crossOriginIsolated) + return new Promise((resolve, reject) => { + const loadTimeout = setTimeout(() => reject('timeout'), 5000) + try { + const ts = logger.ts() + const argon2LoaderCode = argon2Raw + + // Detect SIMD support using a simpler, more reliable method + const hasSIMDSupport = (() => { + try { + // Check if browser supports SIMD by looking for the feature in WebAssembly + return WebAssembly.validate( + new Uint8Array([ + 0x00, 0x61, 0x73, 0x6d, 0x01, 0x00, 0x00, 0x00, 0x01, 0x05, 0x01, 0x60, 0x01, 0x7b, + 0x00, 0x03, 0x02, 0x01, 0x00, 0x0a, 0x07, 0x01, 0x05, 0x00, 0x20, 0x00, 0x1a, 0x0b + ]) + ) + } catch { + return false + } + })() + + const wasmBinaryBase64 = hasSIMDSupport ? argon2WasmSIMD : argon2WasmRegular + console.log(`[ARGON2] SIMD support detected: ${hasSIMDSupport}`) + console.log(`[ARGON2] Using ${hasSIMDSupport ? 'SIMD' : 'regular'} WebAssembly binary`) + + console.log('memory', requiredMemory) + + const KB = 1024 + const MB = 1024 * KB + const GB = 1024 * MB + const WASM_PAGE_SIZE = 64 * 1024 + const totalMemory = (2 * GB - 64 * KB) / 1024 / WASM_PAGE_SIZE + const initialMemory = Math.min( + Math.max(Math.ceil((requiredMemory * 1024) / WASM_PAGE_SIZE), 256) + 256, + totalMemory + ) + + console.log( + `[ARGON2] Memory calculation: requiredMemory=${requiredMemory}KB, initialMemory=${initialMemory} pages (${(initialMemory * WASM_PAGE_SIZE) / 1024 / 1024}MB), totalMemory=${totalMemory} pages` + ) + + const memoryDecl = `var wasmMemory=new WebAssembly.Memory({initial:${initialMemory},maximum:${totalMemory}});` + const moduleDecl = + 'var Module={' + + 'wasmJSMethod: "native-wasm",' + + 'wasmBinary: Uint8Array.from(atob("' + + wasmBinaryBase64 + + '"), c => c.charCodeAt(0)),' + + 'print(...args) { postMessage({op:"log",args}) },' + + 'printErr(...args) { postMessage({op:"log",args}) },' + + 'postRun:' + + this.workerPostRun.toString() + + ',' + + 'calcHash:' + + this.calcHash.toString() + + ',' + + 'wasmMemory:wasmMemory,' + + 'buffer:wasmMemory.buffer,' + + 'TOTAL_MEMORY:' + + initialMemory * WASM_PAGE_SIZE + + '}' + const script = argon2LoaderCode.replace(/^var Module.*?}/, memoryDecl + moduleDecl) + console.log('worker script', script) + const blob = new Blob([script], { type: 'application/javascript' }) + const objectUrl = URL.createObjectURL(blob) + const workerCreateTime = performance.now() + console.log(`[ARGON2] Worker blob created in ${workerCreateTime - runtimeStartTime}ms`) + const worker = new Worker(objectUrl) + console.log(`[ARGON2] Worker instantiated in ${performance.now() - workerCreateTime}ms`) + const onMessage = (e) => { + switch (e.data.op) { + case 'log': + logger.debug(...e.data.args) + break + case 'postRun': + const postRunTime = performance.now() + console.log( + `[ARGON2] WebAssembly runtime loaded in ${postRunTime - runtimeStartTime}ms` + ) + logger.debug('WebAssembly runtime loaded (web worker)', logger.ts(ts)) + URL.revokeObjectURL(objectUrl) + clearTimeout(loadTimeout) + worker.removeEventListener('message', onMessage) + this.runtimeModule = { + hash(args) { + return new Promise((resolve, reject) => { + const hashStartTime = performance.now() + console.log(`[ARGON2] Starting hash computation`) + worker.postMessage(args) + const onHashMessage = (e) => { + const hashEndTime = performance.now() + console.log( + `[ARGON2] Hash message received in ${hashEndTime - hashStartTime}ms` + ) + worker.removeEventListener('message', onHashMessage) + // worker.terminate() // Don't terminate - keep worker alive for reuse + // KdbxwebInit.runtimeModule = null // Don't null - keep runtime module for reuse + if (!e.data || e.data.error || !e.data.hash) { + const ex = (e.data && e.data.error) || 'unexpected error' + logger.error('Worker error', ex) + reject(ex) + } else { + resolve(e.data.hash) + } + } + worker.addEventListener('message', onHashMessage) + }) + } + } + resolve(this.runtimeModule) + break + default: + logger.error('Unknown message', e.data) + URL.revokeObjectURL(objectUrl) + reject('Load error') + } + } + worker.addEventListener('message', onMessage) + } catch (err) { + reject(err) + } + }).catch((err) => { + logger.warn('WebAssembly error', err) + throw new Error('WebAssembly error') + }) + }, + + // eslint-disable-next-line object-shorthand + workerPostRun: function () { + self.postMessage({ op: 'postRun' }) + self.onmessage = (e) => { + try { + /* eslint-disable-next-line no-undef */ + const hash = Module.calcHash(Module, e.data) + self.postMessage({ hash }) + } catch (e) { + self.postMessage({ error: e.toString() }) + } + } + }, + + // eslint-disable-next-line object-shorthand + calcHash: function (Module, args) { + const startTime = performance.now() + console.log('[WORKER] Starting calcHash with params:', { + memory: args.memory, + iterations: args.iterations, + length: args.length, + parallelism: args.parallelism, + type: args.type + }) + + let { password, salt } = args + const { memory, iterations, length, parallelism, type, version } = args + const passwordLen = password.byteLength + + const allocStartTime = performance.now() + password = Module.allocate(new Uint8Array(password), 'i8', Module.ALLOC_NORMAL) + const saltLen = salt.byteLength + salt = Module.allocate(new Uint8Array(salt), 'i8', Module.ALLOC_NORMAL) + const hash = Module.allocate(new Array(length), 'i8', Module.ALLOC_NORMAL) + const encodedLen = 512 + const encoded = Module.allocate(new Array(encodedLen), 'i8', Module.ALLOC_NORMAL) + console.log('[WORKER] Memory allocation took:', performance.now() - allocStartTime, 'ms') + + const hashStartTime = performance.now() + console.log('[WORKER] Starting _argon2_hash call') + console.log('[WORKER] Module._argon2_hash type:', typeof Module._argon2_hash) + console.log( + '[WORKER] Module keys:', + Object.keys(Module).filter((k) => k.includes('argon')) + ) + const res = Module._argon2_hash( + iterations, + memory, + parallelism, + password, + passwordLen, + salt, + saltLen, + hash, + length, + encoded, + encodedLen, + type, + version + ) + console.log('[WORKER] _argon2_hash completed in:', performance.now() - hashStartTime, 'ms') + + if (res) { + throw new Error('Argon2 error ' + res) + } + + const copyStartTime = performance.now() + const hashArr = new Uint8Array(length) + for (let i = 0; i < length; i++) { + hashArr[i] = Module.HEAP8[hash + i] + } + console.log('[WORKER] Hash copy took:', performance.now() - copyStartTime, 'ms') + + Module._free(password) + Module._free(salt) + Module._free(hash) + Module._free(encoded) + + console.log('[WORKER] Total calcHash time:', performance.now() - startTime, 'ms') + return hashArr + } +} + +export { KdbxwebInit } + +import * as kdbxweb from 'kdbxweb' + +const ExpectedFieldRefChars = '{REF:0@I:00000000000000000000000000000000}'.split('') +const ExpectedFieldRefByteLength = ExpectedFieldRefChars.length + +kdbxweb.ProtectedValue.prototype.isProtected = true + +kdbxweb.ProtectedValue.prototype.forEachChar = function (fn) { + const value = this.value + const salt = this.salt + let b, b1, b2, b3 + for (let i = 0, len = value.length; i < len; i++) { + b = value[i] ^ salt[i] + if (b < 128) { + if (fn(b) === false) { + return + } + continue + } + i++ + b1 = value[i] ^ salt[i] + if (i === len) { + break + } + if (b >= 192 && b < 224) { + if (fn(((b & 0x1f) << 6) | (b1 & 0x3f)) === false) { + return + } + continue + } + i++ + b2 = value[i] ^ salt[i] + if (i === len) { + break + } + if (b >= 224 && b < 240) { + if (fn(((b & 0xf) << 12) | ((b1 & 0x3f) << 6) | (b2 & 0x3f)) === false) { + return + } + } + i++ + b3 = value[i] ^ salt[i] + if (i === len) { + break + } + if (b >= 240 && b < 248) { + let c = ((b & 7) << 18) | ((b1 & 0x3f) << 12) | ((b2 & 0x3f) << 6) | (b3 & 0x3f) + if (c <= 0xffff) { + if (fn(c) === false) { + return + } + } else { + c ^= 0x10000 + if (fn(0xd800 | (c >> 10)) === false) { + return + } + if (fn(0xdc00 | (c & 0x3ff)) === false) { + return + } + } + } + // skip error + } +} + +Object.defineProperty(kdbxweb.ProtectedValue.prototype, 'length', { + get() { + return this.textLength + } +}) + +Object.defineProperty(kdbxweb.ProtectedValue.prototype, 'textLength', { + get() { + let textLength = 0 + this.forEachChar(() => { + textLength++ + }) + return textLength + } +}) + +kdbxweb.ProtectedValue.prototype.includesLower = function (findLower) { + return this.indexOfLower(findLower) !== -1 +} + +kdbxweb.ProtectedValue.prototype.indexOfLower = function (findLower) { + let index = -1 + const foundSeqs = [] + const len = findLower.length + let chIndex = -1 + this.forEachChar((ch) => { + chIndex++ + ch = String.fromCharCode(ch).toLowerCase() + if (index !== -1) { + return + } + for (let i = 0; i < foundSeqs.length; i++) { + const seqIx = ++foundSeqs[i] + if (findLower[seqIx] !== ch) { + foundSeqs.splice(i, 1) + i-- + continue + } + if (seqIx === len - 1) { + index = chIndex - len + 1 + return + } + } + if (findLower[0] === ch) { + if (len === 1) { + index = chIndex - len + 1 + } else { + foundSeqs.push(0) + } + } + }) + return index +} + +kdbxweb.ProtectedValue.prototype.indexOfSelfInLower = function (targetLower) { + let firstCharIndex = -1 + let found = false + do { + let chIndex = -1 + this.forEachChar((ch) => { + chIndex++ + ch = String.fromCharCode(ch).toLowerCase() + if (chIndex === 0) { + firstCharIndex = targetLower.indexOf(ch, firstCharIndex + 1) + found = firstCharIndex !== -1 + return + } + if (!found) { + return + } + found = targetLower[firstCharIndex + chIndex] === ch + }) + } while (!found && firstCharIndex >= 0) + return firstCharIndex +} + +kdbxweb.ProtectedValue.prototype.equals = function (other) { + if (!other) { + return false + } + if (!other.isProtected) { + return this.textLength === other.length && this.includes(other) + } + if (other === this) { + return true + } + const len = this.byteLength + if (len !== other.byteLength) { + return false + } + for (let i = 0; i < len; i++) { + if ((this.value[i] ^ this.salt[i]) !== (other.value[i] ^ other.salt[i])) { + return false + } + } + return true +} + +kdbxweb.ProtectedValue.prototype.isFieldReference = function () { + if (this.byteLength !== ExpectedFieldRefByteLength) { + return false + } + let ix = 0 + this.forEachChar((ch) => { + const expected = ExpectedFieldRefChars[ix++] + if (expected !== '0' && ch !== expected) { + return false + } + }) + return true +} + +const RandomSalt = kdbxweb.CryptoEngine.random(128) + +kdbxweb.ProtectedValue.prototype.saltedValue = function () { + if (!this.byteLength) { + return 0 + } + const value = this.value + const salt = this.salt + let salted = '' + for (let i = 0, len = value.length; i < len; i++) { + const byte = value[i] ^ salt[i] + salted += String.fromCharCode(byte ^ RandomSalt[i % RandomSalt.length]) + } + return salted +} + +kdbxweb.ProtectedValue.prototype.dataAndSalt = function () { + return { + data: [...this.value], + salt: [...this.salt] + } +} + +kdbxweb.ProtectedValue.prototype.toBase64 = function () { + const binary = this.getBinary() + const base64 = kdbxweb.ByteUtils.bytesToBase64(binary) + kdbxweb.ByteUtils.zeroBuffer(binary) + return base64 +} + +kdbxweb.ProtectedValue.fromBase64 = function (base64) { + const bytes = kdbxweb.ByteUtils.base64ToBytes(base64) + return kdbxweb.ProtectedValue.fromBinary(bytes) +} diff --git a/packages/web-app-keepass/src/kdbx-logger.ts b/packages/web-app-keepass/src/kdbx-logger.ts new file mode 100644 index 00000000..231a69f3 --- /dev/null +++ b/packages/web-app-keepass/src/kdbx-logger.ts @@ -0,0 +1,87 @@ +// based on https://github.com/keeweb/keeweb/blob/b4bb8e2ca617ad2b9d5a4d210552b6482e4ff503/app/scripts/util/logger.js +// released under MIT license + +const Level = { + Off: 0, + Error: 1, + Warn: 2, + Info: 3, + Debug: 4, + All: 5 +} + +const MaxLogsToSave = 100 + +const lastLogs = [] + +const Logger = function (name, id, level = Level.All) { + this.prefix = name ? name + (id ? ':' + id : '') : 'default' + this.level = level +} + +Logger.prototype.ts = function (ts) { + if (ts) { + return Math.round(performance.now() - ts) + 'ms' + } else { + return performance.now() + } +} + +Logger.prototype.getPrefix = function () { + return new Date().toISOString() + ' [' + this.prefix + '] ' +} + +Logger.prototype.debug = function (...args) { + args[0] = this.getPrefix() + args[0] + if (this.level >= Level.Debug) { + Logger.saveLast('debug', args) + console.log(...args) // eslint-disable-line no-console + } +} + +Logger.prototype.info = function (...args) { + args[0] = this.getPrefix() + args[0] + if (this.level >= Level.Info) { + Logger.saveLast('info', args) + console.info(...args) // eslint-disable-line no-console + } +} + +Logger.prototype.warn = function (...args) { + args[0] = this.getPrefix() + args[0] + if (this.level >= Level.Warn) { + Logger.saveLast('warn', args) + console.warn(...args) // eslint-disable-line no-console + } +} + +Logger.prototype.error = function (...args) { + args[0] = this.getPrefix() + args[0] + if (this.level >= Level.Error) { + Logger.saveLast('error', args) + console.error(...args) // eslint-disable-line no-console + } +} + +Logger.prototype.setLevel = function (level) { + this.level = level +} + +Logger.prototype.getLevel = function () { + return this.level +} + +Logger.saveLast = function (level, args) { + lastLogs.push({ level, args: Array.prototype.slice.call(args) }) + if (lastLogs.length > MaxLogsToSave) { + lastLogs.shift() + } +} + +Logger.getLast = function () { + return lastLogs +} + +Logger.Level = Level + +export { Logger } diff --git a/packages/web-app-keepass/tests/unit/App.spec.ts b/packages/web-app-keepass/tests/unit/App.spec.ts new file mode 100644 index 00000000..cb04dbcb --- /dev/null +++ b/packages/web-app-keepass/tests/unit/App.spec.ts @@ -0,0 +1,5 @@ +describe('keepass', () => { + it('has a test stub', () => { + expect(true).toBeTruthy() + }) +}) diff --git a/packages/web-app-keepass/tsconfig.json b/packages/web-app-keepass/tsconfig.json new file mode 100644 index 00000000..4082f16a --- /dev/null +++ b/packages/web-app-keepass/tsconfig.json @@ -0,0 +1,3 @@ +{ + "extends": "../../tsconfig.json" +} diff --git a/packages/web-app-keepass/vite.config.ts b/packages/web-app-keepass/vite.config.ts new file mode 100644 index 00000000..746aba18 --- /dev/null +++ b/packages/web-app-keepass/vite.config.ts @@ -0,0 +1,24 @@ +import { defineConfig } from '@opencloud-eu/extension-sdk' +import { Plugin } from 'vite' +import { readFileSync } from 'fs' + +export default defineConfig({ + name: 'keepass', + plugins: [ + { + name: 'base64-loader', + transform(code: string, id: string) { + const [path, query] = id.split('?') + if (query != 'base64') return null + + const data = readFileSync(path) + const base64 = data.toString('base64') + + return `export default '${base64}';` + } + } as Plugin + ], + test: { + exclude: ['**/e2e/**'] + } +}) diff --git a/pnpm-lock.yaml b/pnpm-lock.yaml index 775a0b90..6ae5480a 100644 --- a/pnpm-lock.yaml +++ b/pnpm-lock.yaml @@ -196,6 +196,27 @@ importers: specifier: ^2.4.0 version: 2.4.0(@vue/compiler-sfc@3.5.22)(vue@3.5.22(typescript@5.9.3)) + packages/web-app-keepass: + devDependencies: + '@opencloud-eu/web-client': + specifier: ^3.0.0 + version: 3.2.0 + '@opencloud-eu/web-pkg': + specifier: ^3.0.0 + version: 3.2.0(@vue/compiler-sfc@3.5.22)(typescript@5.9.3)(vue@3.5.22(typescript@5.9.3)) + argon2-browser: + specifier: ^1.15.4 + version: 1.18.0 + kdbxweb: + specifier: ^2.1.1 + version: 2.1.1 + vue: + specifier: ^3.4.21 + version: 3.5.22(typescript@5.9.3) + vue3-gettext: + specifier: ^2.4.0 + version: 2.4.0(@vue/compiler-sfc@3.5.22)(vue@3.5.22(typescript@5.9.3)) + packages/web-app-maps: dependencies: '@opencloud-eu/web-client': @@ -785,6 +806,9 @@ packages: '@one-ini/wasm@0.1.1': resolution: {integrity: sha512-XuySG1E38YScSJoMlqovLru4KTUNSjgVTIjyh7qMX6aNN5HY5Ct5LhRJdxO79JtTzKfzV/bnWpz+zquYrISsvw==} + '@opencloud-eu/design-system@3.2.0': + resolution: {integrity: sha512-Wl+Q1QgC867K0c5WnJNnUxAGDBJMKc/XoaZ+Ic+BuT+hvwKJ/6/B/ZZx6SUQNcVHIZm5up1QstHKFAX5ux5SSw==} + '@opencloud-eu/design-system@4.0.0': resolution: {integrity: sha512-p6HFcQYQxBwtnRirPv7+eRQga0smg4jciQujfs8InQDNghx+dXvw9PnyBYPquixqIK25X9CGoKu6hlpd5rP1wQ==} @@ -806,9 +830,15 @@ packages: '@opencloud-eu/tsconfig@4.0.0': resolution: {integrity: sha512-INm1L3ATCiF9sUdH5uaF+JjfRx99fuYUd7Xu/a7wxVyu9j/VG9OntLu+tXNgc3AOaoHK0zHC4c51WJXF2L6r9g==} + '@opencloud-eu/web-client@3.2.0': + resolution: {integrity: sha512-hjesabA/NUMS6dJd4OxYxIlsz8LIIqpUKoAKaMyhszgsUC8y9klsf6Uc6Oo6ddOg2JR4znj6vL+AenEz3ALcOw==} + '@opencloud-eu/web-client@4.0.0': resolution: {integrity: sha512-gntHPAlSNn5z0Wz9u8kALMHax9d6g+0/0ZwYG7Kt3I8O4wXR4Wc58Y7e8T3HGII0lDx8Zif0+piFiTf8oZpHwg==} + '@opencloud-eu/web-pkg@3.2.0': + resolution: {integrity: sha512-AqFoMs+LJfKg+VmbrMPiitIeFW9578Bv1x1BqIsPu8mCGfwMfkj4txekSdAKtNYnhnnyZdZK+EOzP5Dvm8vzGA==} + '@opencloud-eu/web-pkg@4.0.0': resolution: {integrity: sha512-bFP8B1BaNNgR3rO9c31YPRpuuxx22+Ol6ethm/eyPp4QznvpiawDyQOo3AfwbaFS8DOoWaDaA7lTO3yFoQk5Xw==} @@ -1042,26 +1072,50 @@ packages: resolution: {integrity: sha512-32kM7Fs9x6d2GGiE3YaaKq2+qWwSaV2h0XhaIdvs/Ewkh9bJq81xlgEor7gjmJ6UpfxjPpU8rGST6hI1I5lcVg==} engines: {node: '>=18'} + '@sentry-internal/browser-utils@9.46.0': + resolution: {integrity: sha512-Q0CeHym9wysku8mYkORXmhtlBE0IrafAI+NiPSqxOBKXGOCWKVCvowHuAF56GwPFic2rSrRnub5fWYv7T1jfEQ==} + engines: {node: '>=18'} + '@sentry-internal/feedback@10.14.0': resolution: {integrity: sha512-Lj8VGq+VSdwfEu6/Oo7hhcLKQRaRmOs30CAvbcPFSYRKfoi/0xT+dsOm2/C7vgIM9tmXJJ3hMHjgTgnJ3PIZfw==} engines: {node: '>=18'} + '@sentry-internal/feedback@9.46.0': + resolution: {integrity: sha512-KLRy3OolDkGdPItQ3obtBU2RqDt9+KE8z7r7Gsu7c6A6A89m8ZVlrxee3hPQt6qp0YY0P8WazpedU3DYTtaT8w==} + engines: {node: '>=18'} + '@sentry-internal/replay-canvas@10.14.0': resolution: {integrity: sha512-z0DrutMZtxnCf8ZUxYttdbaFeUlnG6CQuDPJ9DQkQVTE2BOiVegZTMMRkj0cUDmIiRQ42Agf1hPbxVmqh2AUuQ==} engines: {node: '>=18'} + '@sentry-internal/replay-canvas@9.46.0': + resolution: {integrity: sha512-QcBjrdRWFJrrrjbmrr2bbrp2R9RYj1KMEbhHNT2Lm1XplIQw+tULEKOHxNtkUFSLR1RNje7JQbxhzM1j95FxVQ==} + engines: {node: '>=18'} + '@sentry-internal/replay@10.14.0': resolution: {integrity: sha512-C/DYUVTTlIxTLdgVlrPbilk2fYw/EPw4SfQgLC7tZXx/X7+Hh/Yi4ESrTlaKBsEUPhK/b82vdbS04+J1dZRyxA==} engines: {node: '>=18'} + '@sentry-internal/replay@9.46.0': + resolution: {integrity: sha512-+8JUblxSSnN0FXcmOewbN+wIc1dt6/zaSeAvt2xshrfrLooVullcGsuLAiPhY0d/e++Fk06q1SAl9g4V0V13gg==} + engines: {node: '>=18'} + '@sentry/browser@10.14.0': resolution: {integrity: sha512-bDtsrHX+wtyOK0J1CcZoSgSJm2U1ITHVceAQfnQeEwWNP9y9xPRsEZDHfE3DnVNl/jB8iA/IOl5I8p4cCQdtpQ==} engines: {node: '>=18'} + '@sentry/browser@9.46.0': + resolution: {integrity: sha512-NOnCTQCM0NFuwbyt4DYWDNO2zOTj1mCf43hJqGDFb1XM9F++7zAmSNnCx4UrEoBTiFOy40McJwBBk9D1blSktA==} + engines: {node: '>=18'} + '@sentry/core@10.14.0': resolution: {integrity: sha512-gyJB7/mW0OteM+vwEsAWaPcLd3fTaKRAc4LZM1aXRbl7juPRmhgwFftjqGg7AMMGNDE0JMs1Fb2W4xSVxH1ItQ==} engines: {node: '>=18'} + '@sentry/core@9.46.0': + resolution: {integrity: sha512-it7JMFqxVproAgEtbLgCVBYtQ9fIb+Bu0JD+cEplTN/Ukpe6GaolyYib5geZqslVxhp2sQgT+58aGvfd/k0N8Q==} + engines: {node: '>=18'} + '@sentry/vue@10.14.0': resolution: {integrity: sha512-gAVyu/Ai9G2bws8t9KaSebiwUiBL+jyGSBrVey60MZLGEcjIxS316do9CXOWXvI5kdaUk2ZkyZXdDfqRYcdWdw==} engines: {node: '>=18'} @@ -1072,6 +1126,16 @@ packages: pinia: optional: true + '@sentry/vue@9.46.0': + resolution: {integrity: sha512-xFeZevR2nG+4tdvZcVgO6U1YiTQJZJTtV8aKRsCEh4yYpBO3FrfLxbSMTUeipILfKxpFf2iu1lwmqNyQtEllkA==} + engines: {node: '>=18'} + peerDependencies: + pinia: 2.x || 3.x + vue: 2.x || 3.x + peerDependenciesMeta: + pinia: + optional: true + '@sphinxxxx/color-conversion@2.2.2': resolution: {integrity: sha512-XExJS3cLqgrmNBIP3bBw6+1oQ1ksGjFh0+oClDKFYpCCqx/hlqwWO5KO/S63fzUo67SxI9dMrF0y5T/Ey7h8Zw==} @@ -1322,11 +1386,19 @@ packages: '@ucast/mongo@2.4.3': resolution: {integrity: sha512-XcI8LclrHWP83H+7H2anGCEeDq0n+12FU2mXCTz6/Tva9/9ddK/iacvvhCyW6cijAAOILmt0tWplRyRhVyZLsA==} + '@uppy/companion-client@4.5.2': + resolution: {integrity: sha512-hfUsReHM5COhn+5d7CdZgZaG8BtDvtwj7vjXzg8qmgKI901mYUm/Zh420iOKT7eHiofKVTNoa7oijeGrqUEnyg==} + peerDependencies: + '@uppy/core': ^4.5.2 + '@uppy/companion-client@5.0.1': resolution: {integrity: sha512-Gy4NCoj5JXQqm6Gr0T6VTeKVhrf8OvnFP1Ddz35dHUsy4GkXuvcc1FMzzKpxKWONtXWz6iQZocnhslgVIZvZwQ==} peerDependencies: '@uppy/core': ^5.0.2 + '@uppy/core@4.5.3': + resolution: {integrity: sha512-52VLeBUY/j904h48lpPGykuWikkOOS4Lz/qkmalDiBQfNALb6iB1MOZs079IM3o/uMLYxzZRL80C3sKpkBUYcw==} + '@uppy/core@5.1.1': resolution: {integrity: sha512-a0EDB+KBENB1+jkeY3oeZLMJfLhMSMsA1EfeAr6XUtKIN2uu2YHFhut5psQlYfLNOL7qtRWmG0jAa03ew1TvEw==} @@ -1335,6 +1407,11 @@ packages: peerDependencies: '@uppy/core': ^5.1.1 + '@uppy/drop-target@3.2.2': + resolution: {integrity: sha512-Y6wPDqmRE5BaOqKOkEfhURtN4qzCGshRn9nBC7jWfsmEhtXvxW6s25GPcuNHMyQIrn659aKLdi28bW7gvQoobg==} + peerDependencies: + '@uppy/core': ^4.5.2 + '@uppy/google-drive@5.0.1': resolution: {integrity: sha512-UkX322Z6V8P2/F+NH+v2APzEAtJJNH5bMBgTzk4v2oyAMFTL1wxkOm+/K8HGhwEOLiU3ta5JvXwuvVlC2uU49A==} peerDependencies: @@ -1350,6 +1427,9 @@ packages: peerDependencies: '@uppy/core': ^5.1.1 + '@uppy/store-default@4.3.2': + resolution: {integrity: sha512-dnY9R2o8fwmO1bF89D0b5jijD7DGED2qVST5hI/j18JreLWzLKH7u6HuNmOvzok8msrQ/qWzQd5Gx4LDQKhBbw==} + '@uppy/store-default@5.0.0': resolution: {integrity: sha512-hQtCSQ1yGiaval/wVYUWquYGDJ+bpQ7e4FhUUAsRQz1x1K+o7NBtjfp63O9I4Ks1WRoKunpkarZ+as09l02cPw==} @@ -1358,11 +1438,19 @@ packages: peerDependencies: '@uppy/core': ^5.1.1 + '@uppy/tus@4.3.2': + resolution: {integrity: sha512-W9pXC/Xew6mM+XKbGafJI9flO3oQTFHxpd281SIy+hDFVTniAqW4VoNhcT15rDqlofQB+PufCXG1EJlX9pCIAw==} + peerDependencies: + '@uppy/core': ^4.5.2 + '@uppy/tus@5.0.1': resolution: {integrity: sha512-4BQA2UFjznXrJhmjsgT/m8WDThoSX2vxKZJH/aF1Xj4GmB46+a5kas6ttCsMDcPq918/sdRJsqCMGj1r4oXKLQ==} peerDependencies: '@uppy/core': ^5.0.2 + '@uppy/utils@6.2.2': + resolution: {integrity: sha512-9mYJtbcngv2HOJIECkyfmdXTI5dW/ObCyvWP1Iti3E5bKtsa4sMmbx5Yh/tGCj8k/lBNhfvWyZuYnvnjmzNLSQ==} + '@uppy/utils@7.1.1': resolution: {integrity: sha512-4jKH3WKmQs6SCOGHCbZkLHykiaJgQo3DZsET144K8mhaoaOxfVupCYohI/UKurTQll1FhCr9dKS95Qcm0T4w7g==} @@ -1371,6 +1459,11 @@ packages: peerDependencies: '@uppy/core': ^5.0.2 + '@uppy/xhr-upload@4.4.2': + resolution: {integrity: sha512-CU66aVn4yghGklEkepCqFPulc6uygznApy2DpD+jCMLNB5q6yT1RPSrQUgRgXsYhpW1YhutZJWsrEnHEDS+Tcw==} + peerDependencies: + '@uppy/core': ^4.5.2 + '@uppy/xhr-upload@5.0.1': resolution: {integrity: sha512-wG8M7WTVFi4widnwERxcZ73ap5tSSr8b8sGooGBEU8Kvhoo6P8l6tnJbuAFELRUAc/jkzZfL5WSYgsp0qTCUyQ==} peerDependencies: @@ -1495,6 +1588,11 @@ packages: peerDependencies: vue: ^3.5.0 + '@xmldom/xmldom@0.7.13': + resolution: {integrity: sha512-lm2GW5PkosIzccsaZIz7tp8cPADSIlIHWDFTR1N0SzfinhhYgeIQjFMz4rYzanCScr3DqQLeomUDArp6MWKm+g==} + engines: {node: '>=10.0.0'} + deprecated: this version is no longer supported, please update to at least 0.8.* + '@zip.js/zip.js@2.8.8': resolution: {integrity: sha512-v0KutehhSAuaoFAFGLp+V4+UiZ1mIxQ8vNOYMD7k9ZJaBbtQV49MYlg568oRLiuwWDg2Di58Iw3Q0ESNWR+5JA==} engines: {bun: '>=0.7.0', deno: '>=1.0.0', node: '>=18.0.0'} @@ -1545,6 +1643,9 @@ packages: arg@4.1.3: resolution: {integrity: sha512-58S9QDqG0Xx27YwPSt9fJxivjYl432YCwfDMfZ+71RAqUrZef7LrKQZ3LHLOwCS4FLNBplP533Zx895SeOCHvA==} + argon2-browser@1.18.0: + resolution: {integrity: sha512-ImVAGIItnFnvET1exhsQB7apRztcoC5TnlSqernMJDUjbc/DLq3UEYeXFrLPrlaIl8cVfwnXb6wX2KpFf2zxHw==} + argparse@2.0.1: resolution: {integrity: sha512-8+9WqebbFzpX9OR+Wa6O29asIogeRMzcGtAINdpMHHyAg10f05aSFVBbcEqGf/PXw1EjAZ+q2/bEBg3DvurK3Q==} @@ -2042,6 +2143,9 @@ packages: resolution: {integrity: sha512-7yAQpD2UMJzLi1Dqv7qFYnPbaPx7ZfFK6PiIxQ4PfkGPyNyl2Ugx+a/umUonmKqjhM4DnfbMvdX6otXq83soQQ==} engines: {node: ^12.20 || >= 14.13} + fflate@0.7.4: + resolution: {integrity: sha512-5u2V/CDW15QM1XbbgS+0DfPxVB+jUKhWEKuuFuHncbk3tEEqzmoXL+2KyOFuKGqOnmdIy0/davWF1CkuwtibCw==} + file-entry-cache@8.0.0: resolution: {integrity: sha512-XXTUwCvisa5oacNGRP9SfNtYBNAMi+RPwBFmblZEF7N7swHYQS6/Zfk7SRwx4D5j3CH211YNRco1DEMNVfZCnQ==} engines: {node: '>=16.0.0'} @@ -2356,6 +2460,9 @@ packages: resolution: {integrity: sha512-+KJGIyHgkGuIq3IEBNftfhW/LfWhXUIY6OmyVWjliu5KH1y0fw7VQ8YndE2O4qZdMSd9SqbnC8GOcZEy0Om7sA==} engines: {node: '>=18'} + kdbxweb@2.1.1: + resolution: {integrity: sha512-z+a2+BEzyK2kUh0xDekY7gwc9CkbzSetUyvc78NKVawxV06UG1KYbg9W9hZCJdQPYizIVePTvQsYUXIO1qtAhQ==} + keyv@4.5.4: resolution: {integrity: sha512-oxVHkHR/EJf2CNXnWxRLW6mg7JyCCUcG0DtEGmL2ctUo1PNTin1PUil+r/+4r5MpVgC/fn1kjsx7mjSujKqIpw==} @@ -2498,6 +2605,11 @@ packages: lru-cache@5.1.1: resolution: {integrity: sha512-KpNARQA3Iwv+jTA0utUVVbrh+Jlrr1Fv0e56GGzAFOXN7dk/FviaDW8LHmK52DlcH4WP2n6gI8vN1aesBFgo9w==} + lucide-vue-next@0.453.0: + resolution: {integrity: sha512-5zmv83vxAs9SVoe22veDBi8Dw0Fh2F+oTngWgKnKOkrZVbZjceXLQ3tescV2boB0zlaf9R2Sd9RuUP2766xvsQ==} + peerDependencies: + vue: '>=3.0.1' + lucide-vue-next@0.543.0: resolution: {integrity: sha512-Az5kpNm/koKAwSNIKjsZ4uHV2tVfmlQlcHwFBygQ8gc5/jFg7An9OrxgDy/aE5m+HLx7VfLYqDxLr8gWecZbQA==} peerDependencies: @@ -2536,6 +2648,11 @@ packages: resolution: {integrity: sha512-/IXtbwEk5HTPyEwyKX6hGkYXxM9nbj64B+ilVJnC/R6B0pH5G4V3b0pVbL7DBj4tkhBAppbQUlf6F6Xl9LHu1g==} engines: {node: '>= 0.4'} + md-editor-v3@5.8.5: + resolution: {integrity: sha512-NsqAmmAx/ykA1AcwxcHH4Hkn4VAPkqMX7Hd6Lv4FcwQoMQ70wWmJfs/mokyPGkqr4oYqqn8LRMBTqFNfoP0O0A==} + peerDependencies: + vue: ^3.5.3 + md-editor-v3@6.0.1: resolution: {integrity: sha512-ZZQpUsC6DkKYfFsDMck0kJabP/vYSDfNkB6Zmb6+v50orKOO7KG2SGgm6yGv78YWxh8ehqJL4IpZwhWneQzg1w==} peerDependencies: @@ -3123,6 +3240,10 @@ packages: util-deprecate@1.0.2: resolution: {integrity: sha512-EPD5q1uXyFxJpCrLnCc1nHnq3gOa6DZBocAIiI2TaSCA7VCJ1UJDMagCzIkXNsUYfD1daK//LTEQ8xiIbrHtcw==} + uuid@11.1.0: + resolution: {integrity: sha512-0/A9rDy9P7cJ+8w1c9WD9V//9Wj15Ce2MPz8Ri6032usz+NfePxx5AcN3bN+r6ZL6jEo066/yNYB3tn4pQEx+A==} + hasBin: true + uuid@13.0.0: resolution: {integrity: sha512-XQegIaBTVUjSHliKqcnFqYypAd4S+WCYt5NIeRs6w/UAry7z8Y9j5ZwRRL4kzq9U3sD6v+85er9FvkEaBpji2w==} hasBin: true @@ -4033,6 +4154,27 @@ snapshots: '@one-ini/wasm@0.1.1': {} + '@opencloud-eu/design-system@3.2.0(@vue/compiler-sfc@3.5.22)(vue@3.5.22(typescript@5.9.3))': + dependencies: + '@emoji-mart/data': 1.2.1 + '@popperjs/core': 2.11.8 + deepmerge: 4.3.1 + emoji-mart: 5.6.0 + focus-trap: 7.6.5 + focus-trap-vue: 4.0.3(focus-trap@7.6.5)(vue@3.5.22(typescript@5.9.3)) + fuse.js: 7.1.0 + lodash-es: 4.17.21 + luxon: 3.7.1 + portal-vue: 3.0.0(vue@3.5.22(typescript@5.9.3)) + tippy.js: 6.3.7 + vue-inline-svg: 4.0.1(vue@3.5.22(typescript@5.9.3)) + vue-router: 4.6.3(vue@3.5.22(typescript@5.9.3)) + vue-select: 4.0.0-beta.6(vue@3.5.22(typescript@5.9.3)) + vue3-gettext: 2.4.0(@vue/compiler-sfc@3.5.22)(vue@3.5.22(typescript@5.9.3)) + transitivePeerDependencies: + - '@vue/compiler-sfc' + - vue + '@opencloud-eu/design-system@4.0.0(@vue/compiler-sfc@3.5.22)(vue@3.5.22(typescript@5.9.3))': dependencies: '@emoji-mart/data': 1.2.1 @@ -4089,6 +4231,21 @@ snapshots: '@opencloud-eu/tsconfig@4.0.0': {} + '@opencloud-eu/web-client@3.2.0': + dependencies: + '@casl/ability': 6.7.3 + '@microsoft/fetch-event-source': 2.0.1 + axios: 1.10.0 + fast-xml-parser: 4.5.3 + lodash-es: 4.17.21 + luxon: 3.7.1 + uuid: 11.1.0 + webdav: 5.8.0 + xml-js: 1.6.11 + zod: 4.1.12 + transitivePeerDependencies: + - debug + '@opencloud-eu/web-client@4.0.0': dependencies: '@casl/ability': 6.7.3 @@ -4104,6 +4261,53 @@ snapshots: transitivePeerDependencies: - debug + '@opencloud-eu/web-pkg@3.2.0(@vue/compiler-sfc@3.5.22)(typescript@5.9.3)(vue@3.5.22(typescript@5.9.3))': + dependencies: + '@casl/ability': 6.7.3 + '@casl/vue': 2.2.2(@casl/ability@6.7.3)(vue@3.5.22(typescript@5.9.3)) + '@microsoft/fetch-event-source': 2.0.1 + '@opencloud-eu/design-system': 3.2.0(@vue/compiler-sfc@3.5.22)(vue@3.5.22(typescript@5.9.3)) + '@opencloud-eu/web-client': 3.2.0 + '@sentry/vue': 9.46.0(pinia@3.0.3(typescript@5.9.3)(vue@3.5.22(typescript@5.9.3)))(vue@3.5.22(typescript@5.9.3)) + '@uppy/core': 4.5.3 + '@uppy/drop-target': 3.2.2(@uppy/core@4.5.3) + '@uppy/tus': 4.3.2(@uppy/core@4.5.3) + '@uppy/utils': 6.2.2 + '@uppy/xhr-upload': 4.4.2(@uppy/core@4.5.3) + '@vavt/cm-extension': 1.10.1 + '@vue/shared': 3.5.22 + '@vueuse/core': 13.9.0(vue@3.5.22(typescript@5.9.3)) + axios: 1.10.0 + cropperjs: 1.6.2 + deepmerge: 4.3.1 + dompurify: 3.2.6 + filesize: 11.0.2 + fuse.js: 7.1.0 + js-generate-password: 1.0.0 + lodash-es: 4.17.21 + luxon: 3.7.1 + mark.js: 8.11.1 + md-editor-v3: 5.8.5(vue@3.5.22(typescript@5.9.3)) + oidc-client-ts: 3.3.0 + p-queue: 8.1.0 + password-sheriff: 1.1.1 + pinia: 3.0.3(typescript@5.9.3)(vue@3.5.22(typescript@5.9.3)) + portal-vue: 3.0.0(vue@3.5.22(typescript@5.9.3)) + prismjs: 1.30.0 + qs: 6.14.0 + screenfull: 6.0.2 + semver: 7.7.2 + uuid: 11.1.0 + vue-concurrency: 5.0.3(vue@3.5.22(typescript@5.9.3)) + vue-router: 4.6.3(vue@3.5.22(typescript@5.9.3)) + vue3-gettext: 2.4.0(@vue/compiler-sfc@3.5.22)(vue@3.5.22(typescript@5.9.3)) + zod: 4.1.12 + transitivePeerDependencies: + - '@vue/compiler-sfc' + - debug + - typescript + - vue + '@opencloud-eu/web-pkg@4.0.0(@vue/compiler-sfc@3.5.22)(typescript@5.9.3)(vue@3.5.22(typescript@5.9.3))': dependencies: '@casl/ability': 6.7.3 @@ -4323,20 +4527,38 @@ snapshots: dependencies: '@sentry/core': 10.14.0 + '@sentry-internal/browser-utils@9.46.0': + dependencies: + '@sentry/core': 9.46.0 + '@sentry-internal/feedback@10.14.0': dependencies: '@sentry/core': 10.14.0 + '@sentry-internal/feedback@9.46.0': + dependencies: + '@sentry/core': 9.46.0 + '@sentry-internal/replay-canvas@10.14.0': dependencies: '@sentry-internal/replay': 10.14.0 '@sentry/core': 10.14.0 + '@sentry-internal/replay-canvas@9.46.0': + dependencies: + '@sentry-internal/replay': 9.46.0 + '@sentry/core': 9.46.0 + '@sentry-internal/replay@10.14.0': dependencies: '@sentry-internal/browser-utils': 10.14.0 '@sentry/core': 10.14.0 + '@sentry-internal/replay@9.46.0': + dependencies: + '@sentry-internal/browser-utils': 9.46.0 + '@sentry/core': 9.46.0 + '@sentry/browser@10.14.0': dependencies: '@sentry-internal/browser-utils': 10.14.0 @@ -4345,8 +4567,18 @@ snapshots: '@sentry-internal/replay-canvas': 10.14.0 '@sentry/core': 10.14.0 + '@sentry/browser@9.46.0': + dependencies: + '@sentry-internal/browser-utils': 9.46.0 + '@sentry-internal/feedback': 9.46.0 + '@sentry-internal/replay': 9.46.0 + '@sentry-internal/replay-canvas': 9.46.0 + '@sentry/core': 9.46.0 + '@sentry/core@10.14.0': {} + '@sentry/core@9.46.0': {} + '@sentry/vue@10.14.0(pinia@3.0.3(typescript@5.9.3)(vue@3.5.22(typescript@5.9.3)))(vue@3.5.22(typescript@5.9.3))': dependencies: '@sentry/browser': 10.14.0 @@ -4355,6 +4587,14 @@ snapshots: optionalDependencies: pinia: 3.0.3(typescript@5.9.3)(vue@3.5.22(typescript@5.9.3)) + '@sentry/vue@9.46.0(pinia@3.0.3(typescript@5.9.3)(vue@3.5.22(typescript@5.9.3)))(vue@3.5.22(typescript@5.9.3))': + dependencies: + '@sentry/browser': 9.46.0 + '@sentry/core': 9.46.0 + vue: 3.5.22(typescript@5.9.3) + optionalDependencies: + pinia: 3.0.3(typescript@5.9.3)(vue@3.5.22(typescript@5.9.3)) + '@sphinxxxx/color-conversion@2.2.2': {} '@sveltejs/acorn-typescript@1.0.5(acorn@8.15.0)': @@ -4615,6 +4855,13 @@ snapshots: dependencies: '@ucast/core': 1.10.2 + '@uppy/companion-client@4.5.2(@uppy/core@4.5.3)': + dependencies: + '@uppy/core': 4.5.3 + '@uppy/utils': 6.2.2 + namespace-emitter: 2.0.1 + p-retry: 6.2.1 + '@uppy/companion-client@5.0.1(@uppy/core@5.1.1)': dependencies: '@uppy/core': 5.1.1 @@ -4622,6 +4869,17 @@ snapshots: namespace-emitter: 2.0.1 p-retry: 6.2.1 + '@uppy/core@4.5.3': + dependencies: + '@transloadit/prettier-bytes': 0.3.5 + '@uppy/store-default': 4.3.2 + '@uppy/utils': 6.2.2 + lodash: 4.17.21 + mime-match: 1.0.2 + namespace-emitter: 2.0.1 + nanoid: 5.1.6 + preact: 10.27.2 + '@uppy/core@5.1.1': dependencies: '@transloadit/prettier-bytes': 0.3.5 @@ -4646,6 +4904,11 @@ snapshots: preact: 10.27.2 shallow-equal: 3.1.0 + '@uppy/drop-target@3.2.2(@uppy/core@4.5.3)': + dependencies: + '@uppy/core': 4.5.3 + '@uppy/utils': 6.2.2 + '@uppy/google-drive@5.0.1(@uppy/core@5.1.1)': dependencies: '@uppy/companion-client': 5.0.1(@uppy/core@5.1.1) @@ -4671,6 +4934,8 @@ snapshots: p-queue: 8.1.0 preact: 10.27.2 + '@uppy/store-default@4.3.2': {} + '@uppy/store-default@5.0.0': {} '@uppy/thumbnail-generator@5.0.2(@uppy/core@5.1.1)': @@ -4679,6 +4944,13 @@ snapshots: '@uppy/utils': 7.1.1 exifr: 7.1.3 + '@uppy/tus@4.3.2(@uppy/core@4.5.3)': + dependencies: + '@uppy/companion-client': 4.5.2(@uppy/core@4.5.3) + '@uppy/core': 4.5.3 + '@uppy/utils': 6.2.2 + tus-js-client: 4.3.1 + '@uppy/tus@5.0.1(@uppy/core@5.1.1)': dependencies: '@uppy/companion-client': 5.0.1(@uppy/core@5.1.1) @@ -4686,6 +4958,11 @@ snapshots: '@uppy/utils': 7.1.1 tus-js-client: 4.3.1 + '@uppy/utils@6.2.2': + dependencies: + lodash: 4.17.21 + preact: 10.27.2 + '@uppy/utils@7.1.1': dependencies: lodash: 4.17.21 @@ -4699,6 +4976,12 @@ snapshots: '@uppy/utils': 7.1.1 preact: 10.27.2 + '@uppy/xhr-upload@4.4.2(@uppy/core@4.5.3)': + dependencies: + '@uppy/companion-client': 4.5.2(@uppy/core@4.5.3) + '@uppy/core': 4.5.3 + '@uppy/utils': 6.2.2 + '@uppy/xhr-upload@5.0.1(@uppy/core@5.1.1)': dependencies: '@uppy/companion-client': 5.0.1(@uppy/core@5.1.1) @@ -4875,6 +5158,8 @@ snapshots: dependencies: vue: 3.5.22(typescript@5.9.3) + '@xmldom/xmldom@0.7.13': {} + '@zip.js/zip.js@2.8.8': {} abbrev@2.0.0: {} @@ -4917,6 +5202,8 @@ snapshots: arg@4.1.3: {} + argon2-browser@1.18.0: {} + argparse@2.0.1: {} aria-query@5.3.2: {} @@ -5422,6 +5709,8 @@ snapshots: node-domexception: 1.0.0 web-streams-polyfill: 3.3.3 + fflate@0.7.4: {} + file-entry-cache@8.0.0: dependencies: flat-cache: 4.0.1 @@ -5693,6 +5982,11 @@ snapshots: jwt-decode@4.0.0: {} + kdbxweb@2.1.1: + dependencies: + '@xmldom/xmldom': 0.7.13 + fflate: 0.7.4 + keyv@4.5.4: dependencies: json-buffer: 3.0.1 @@ -5809,6 +6103,10 @@ snapshots: dependencies: yallist: 3.1.1 + lucide-vue-next@0.453.0(vue@3.5.22(typescript@5.9.3)): + dependencies: + vue: 3.5.22(typescript@5.9.3) + lucide-vue-next@0.543.0(vue@3.5.22(typescript@5.9.3)): dependencies: vue: 3.5.22(typescript@5.9.3) @@ -5842,6 +6140,31 @@ snapshots: math-intrinsics@1.1.0: {} + md-editor-v3@5.8.5(vue@3.5.22(typescript@5.9.3)): + dependencies: + '@codemirror/autocomplete': 6.19.0 + '@codemirror/commands': 6.8.1 + '@codemirror/lang-markdown': 6.3.4 + '@codemirror/language': 6.11.3 + '@codemirror/language-data': 6.5.1 + '@codemirror/search': 6.5.11 + '@codemirror/state': 6.5.2 + '@codemirror/view': 6.38.3 + '@lezer/highlight': 1.2.1 + '@types/markdown-it': 14.1.2 + '@vavt/copy2clipboard': 1.0.3 + '@vavt/util': 2.1.0 + codemirror: 6.0.2 + lru-cache: 11.2.2 + lucide-vue-next: 0.453.0(vue@3.5.22(typescript@5.9.3)) + markdown-it: 14.1.0 + markdown-it-image-figures: 2.1.1(markdown-it@14.1.0) + markdown-it-sub: 2.0.0 + markdown-it-sup: 2.0.0 + medium-zoom: 1.1.0 + vue: 3.5.22(typescript@5.9.3) + xss: 1.0.15 + md-editor-v3@6.0.1(vue@3.5.22(typescript@5.9.3)): dependencies: '@codemirror/autocomplete': 6.19.0 @@ -6410,6 +6733,8 @@ snapshots: util-deprecate@1.0.2: {} + uuid@11.1.0: {} + uuid@13.0.0: {} v8-compile-cache-lib@3.0.1: {}