Compare commits

..

5 Commits

Author SHA1 Message Date
cupcakearmy ea561f635a Merge remote-tracking branch 'origin/main' into msgpack 2026-06-28 18:51:40 +02:00
cupcakearmy 1f180a2e53 Merge pull request #213 from cupcakearmy/feat/redis-key-prefix
feat: add REDIS_PREFIX env var for sharing a Redis instance
2026-06-25 22:30:59 +02:00
cupcakearmy 6b29c6f069 feat: add REDIS_PREFIX env var for shared Redis instance namespace 2026-06-25 21:21:17 +01:00
cupcakearmy 781231e414 msgpack 2026-06-07 10:46:15 +02:00
cupcakearmy bb422fdd8d msgpack 2026-06-07 10:46:11 +02:00
29 changed files with 880 additions and 9 deletions
+4 -2
View File
@@ -80,15 +80,17 @@ of the notes even if it tried to.
| `ALLOW_ADVANCED` | `true` | Allow custom configuration. If set to `false` all notes will be one view only. |
| `ALLOW_FILES` | `true` | Allow uploading files. If set to `false`, users will only be allowed to create text notes. |
| `ID_LENGTH` | `32` | Set the size of the note `id` in bytes. By default this is `32` bytes. This is useful for reducing link size. _This setting does not affect encryption strength_. |
| `REDIS_PREFIX` | `""` | Optional prefix for all Redis keys. Useful when sharing a Redis instance with other apps via ACL namespaces. |
| `VERBOSITY` | `warn` | Verbosity level for the backend. [Possible values](https://docs.rs/env_logger/latest/env_logger/#enabling-logging) are: `error`, `warn`, `info`, `debug`, `trace` |
| `THEME_IMAGE` | `""` | Custom image for replacing the logo. Must be publicly reachable |
| `THEME_TEXT` | `""` | Custom text for replacing the description below the logo |
| `THEME_PAGE_TITLE` | `""` | Custom text the page title |
| `THEME_FAVICON` | `""` | Custom url for the favicon. Must be publicly reachable |
| `THEME_NEW_NOTE_NOTICE` | `true` | Show the message about how notes are stored in the memory and may be evicted after creating a new note. Defaults to `true`. |
| `THEME_HOME_LINK` | `true` | Show the `/home` link in the footer. Defaults to `true`. |
| `THEME_HOME_LINK` | `true` | Show the `/home` link in the footer. Defaults to `true`. |
| `IMPRINT_URL` | `""` | Custom url for an Imprint hosted somewhere else. Must be publicly reachable. Takes precedence above `IMPRINT_HTML`. |
| `IMPRINT_HTML` | `""` | Alternative to `IMPRINT_URL`, this can be used to specify the HTML code to show on `/imprint`. Only `IMPRINT_HTML` or `IMPRINT_URL` should be specified, not both. |
## Deployment
> ️ `https` is required otherwise browsers will not support the cryptographic functions.
@@ -102,7 +104,7 @@ Docker is the easiest way. There is the [official image here](https://hub.docker
```yaml
# docker-compose.yml
version: '3.8'
version: "3.8"
services:
redis:
+20
View File
@@ -261,6 +261,7 @@ dependencies = [
"lazy_static",
"redis",
"ring",
"rmp-serde",
"serde",
"serde_json",
"tokio",
@@ -1004,6 +1005,25 @@ dependencies = [
"windows-sys 0.52.0",
]
[[package]]
name = "rmp"
version = "0.8.15"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "4ba8be72d372b2c9b35542551678538b562e7cf86c3315773cae48dfbfe7790c"
dependencies = [
"num-traits",
]
[[package]]
name = "rmp-serde"
version = "1.3.1"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "72f81bee8c8ef9b577d1681a70ebbc962c232461e397b22c208c43c04b67a155"
dependencies = [
"rmp",
"serde",
]
[[package]]
name = "rustix"
version = "1.1.4"
+1
View File
@@ -25,3 +25,4 @@ ring = "0.17"
bs62 = "0.1"
byte-unit = "4"
dotenv = "0.15"
rmp-serde = "1.3.1"
+4
View File
@@ -34,6 +34,10 @@ pub static ref ID_LENGTH: u32 = std::env::var("ID_LENGTH")
.unwrap_or("32".to_string())
.parse()
.unwrap();
pub static ref REDIS_PREFIX: String = std::env::var("REDIS_PREFIX")
.unwrap_or("".to_string())
.parse()
.unwrap();
pub static ref ALLOW_FILES: bool = std::env::var("ALLOW_FILES")
.unwrap_or("true".to_string())
.parse()
+3 -1
View File
@@ -23,6 +23,7 @@ mod csp;
mod health;
mod lock;
mod note;
mod note_v2;
mod status;
mod store;
@@ -42,7 +43,8 @@ async fn main() {
let notes_routes = Router::new()
.route("/", post(note::create))
.route("/{id}", delete(note::delete))
.route("/{id}", get(note::preview));
.route("/{id}", get(note::preview))
.route("/v2/", post(note_v2::create));
let health_routes = Router::new().route("/live", get(health::report_health));
let status_routes = Router::new().route("/status", get(status::get_status));
let api_routes = Router::new()
+5
View File
@@ -0,0 +1,5 @@
mod model;
mod routes;
pub use model::*;
pub use routes::*;
+41
View File
@@ -0,0 +1,41 @@
use bs62;
use ring::rand::SecureRandom;
use serde::{Deserialize, Serialize};
use crate::config;
#[derive(Serialize, Deserialize, Clone)]
pub struct NoteV2Metadata {
pub views: Option<u32>,
pub expiration: Option<u32>,
}
#[derive(Serialize, Deserialize, Clone)]
pub struct NoteV2 {
pub metadata: NoteV2Metadata,
pub public: String,
pub content: String,
}
#[derive(Serialize)]
pub struct NoteInfoV2 {
pub public: String,
}
#[derive(Serialize)]
pub struct NotePublicV2 {
pub public: String,
pub content: String,
}
pub fn generate_id_v2() -> String {
let mut result = "".to_owned();
let mut id: [u8; 1] = [0; 1];
let sr = ring::rand::SystemRandom::new();
for _ in 0..*config::ID_LENGTH {
let _ = sr.fill(&mut id);
result.push_str(&bs62::encode_data(&id));
}
return result;
}
+126
View File
@@ -0,0 +1,126 @@
use axum::{
Json,
body::Bytes,
extract::Path,
http::StatusCode,
response::{IntoResponse, Response},
};
use serde::{Deserialize, Serialize};
use std::{sync::Arc, time::SystemTime};
use crate::note_v2::{NoteV2, generate_id_v2};
use crate::store;
use crate::{config, lock::SharedState};
use super::NotePublicV2;
pub fn now() -> u32 {
SystemTime::now()
.duration_since(SystemTime::UNIX_EPOCH)
.unwrap()
.as_secs() as u32
}
// #[derive(Deserialize)]
// pub struct OneNoteParams {
// id: String,
// }
// pub async fn preview(Path(OneNoteParams { id }): Path<OneNoteParams>) -> Response {
// let note = store::get(&id);
// match note {
// Ok(Some(n)) => (StatusCode::OK, Json(NoteInfoV2 { meta: n.meta })).into_response(),
// Ok(None) => (StatusCode::NOT_FOUND).into_response(),
// Err(e) => (StatusCode::INTERNAL_SERVER_ERROR, e.to_string()).into_response(),
// }
// }
#[derive(Serialize, Deserialize)]
struct CreateResponse {
id: String,
}
pub async fn create(body: Bytes) -> Response {
let id = generate_id_v2();
println!("{}", body.len());
(StatusCode::OK, Json(CreateResponse { id })).into_response()
// match store::set(&id.clone(), &n.clone()) {
// Ok(_) => (StatusCode::OK, Json(CreateResponse { id })).into_response(),
// Err(e) => (StatusCode::INTERNAL_SERVER_ERROR, e.to_string()).into_response(),
// }
}
// pub async fn delete(
// Path(OneNoteParams { id }): Path<OneNoteParams>,
// state: axum::extract::State<SharedState>,
// ) -> Response {
// let mut locks_map = state.locks.lock().await;
// let lock = locks_map
// .entry(id.clone())
// .or_insert_with(|| Arc::new(Mutex::new(())))
// .clone();
// drop(locks_map);
// let _guard = lock.lock().await;
// let note = store::get(&id);
// match note {
// Err(e) => (StatusCode::INTERNAL_SERVER_ERROR, e.to_string()).into_response(),
// Ok(None) => (StatusCode::NOT_FOUND).into_response(),
// Ok(Some(note)) => {
// let mut changed = note.clone();
// if changed.views == None && changed.expiration == None {
// return (StatusCode::BAD_REQUEST).into_response();
// }
// match changed.views {
// Some(v) => {
// changed.views = Some(v - 1);
// let id = id.clone();
// if v <= 1 {
// match store::del(&id) {
// Err(e) => {
// return (StatusCode::INTERNAL_SERVER_ERROR, e.to_string())
// .into_response();
// }
// _ => {}
// }
// } else {
// match store::set(&id, &changed.clone()) {
// Err(e) => {
// return (StatusCode::INTERNAL_SERVER_ERROR, e.to_string())
// .into_response();
// }
// _ => {}
// }
// }
// }
// _ => {}
// }
// let n = now();
// match changed.expiration {
// Some(e) => {
// if e < n {
// match store::del(&id.clone()) {
// Ok(_) => return (StatusCode::BAD_REQUEST).into_response(),
// Err(e) => {
// return (StatusCode::INTERNAL_SERVER_ERROR, e.to_string())
// .into_response();
// }
// }
// }
// }
// _ => {}
// }
// return (
// StatusCode::OK,
// Json(NotePublicV2 {
// content: changed.contents,
// public: changed.meta,
// }),
// )
// .into_response();
// }
// }
// }
+12 -4
View File
@@ -1,6 +1,7 @@
use redis;
use redis::Commands;
use crate::config;
use crate::note::now;
use crate::note::Note;
@@ -11,6 +12,10 @@ lazy_static! {
.unwrap();
}
fn prefixed(id: &String) -> String {
format!("{}{}", config::REDIS_PREFIX.as_str(), id)
}
fn get_connection() -> Result<redis::Connection, &'static str> {
let client =
redis::Client::open(REDIS_CLIENT.to_string()).map_err(|_| "Unable to connect to redis")?;
@@ -28,15 +33,16 @@ pub fn can_reach_redis() -> bool {
}
pub fn set(id: &String, note: &Note) -> Result<(), &'static str> {
let key = prefixed(id);
let serialized = serde_json::to_string(&note.clone()).unwrap();
let mut conn = get_connection()?;
conn.set::<_, _, ()>(id, serialized)
conn.set::<_, _, ()>(key.as_str(), serialized)
.map_err(|_| "Unable to set note in redis")?;
match note.expiration {
Some(e) => {
let seconds = e - now();
conn.expire::<_, ()>(id, seconds as i64)
conn.expire::<_, ()>(key.as_str(), seconds as i64)
.map_err(|_| "Unable to set expiration on note")?
}
None => {}
@@ -45,8 +51,9 @@ pub fn set(id: &String, note: &Note) -> Result<(), &'static str> {
}
pub fn get(id: &String) -> Result<Option<Note>, &'static str> {
let key = prefixed(id);
let mut conn = get_connection()?;
let value: Option<String> = conn.get(id).map_err(|_| "Could not load note in redis")?;
let value: Option<String> = conn.get(key.as_str()).map_err(|_| "Could not load note in redis")?;
match value {
None => return Ok(None),
Some(s) => {
@@ -57,7 +64,8 @@ pub fn get(id: &String) -> Result<Option<Note>, &'static str> {
}
pub fn del(id: &String) -> Result<(), &'static str> {
let key = prefixed(id);
let mut conn = get_connection()?;
conn.del::<_, ()>(id).map_err(|_| "Unable to delete note in redis")?;
conn.del::<_, ()>(key.as_str()).map_err(|_| "Unable to delete note in redis")?;
Ok(())
}
Binary file not shown.

After

Width:  |  Height:  |  Size: 5.6 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 5.6 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 5.6 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 5.6 KiB

+24
View File
@@ -0,0 +1,24 @@
{
"name": "@cryptgeon/shared",
"private": true,
"type": "module",
"exports": {
".": "./src/index.ts"
},
"dependencies": {
"@msgpack/msgpack": "^3.1.3",
"@noble/ciphers": "^2.2.0",
"@noble/hashes": "^2.2.0",
"ky": "^2.0.2",
"zod": "^4.4.3"
},
"devDependencies": {
"@tsconfig/strictest": "catalog:",
"@vitest/browser-playwright": "catalog:",
"typescript": "catalog:",
"vitest": "catalog:"
},
"scripts": {
"test:browser": "vitest"
}
}
Binary file not shown.

After

Width:  |  Height:  |  Size: 5.6 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 5.6 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 5.6 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 5.6 KiB

@@ -0,0 +1,38 @@
// Vitest Snapshot v1, https://vitest.dev/guide/snapshot.html
exports[`encryption > stable derive key 1`] = `
Uint8Array [
107,
115,
150,
59,
243,
162,
229,
9,
221,
235,
124,
184,
124,
51,
96,
32,
183,
240,
114,
43,
221,
208,
248,
142,
16,
45,
163,
137,
102,
240,
245,
198,
]
`;
+14
View File
@@ -0,0 +1,14 @@
import { describe, expect, it } from "vitest";
import { upload } from "./actions";
import { deriveKey } from "./encryption";
describe("actions", () => {
describe("upload", () => {
it("upload", async () => {
const key = deriveKey("abc");
await expect(
upload({ public: "foo", metadata: { views: 1 }, files: [] }, key),
).resolves.toBe({});
});
});
});
+39
View File
@@ -0,0 +1,39 @@
import { encode } from "@msgpack/msgpack";
import { encrypt } from "./encryption";
import { ClientNote, ServerNote } from "./types";
import ky from "ky";
import z from "zod";
const client = ky.extend({ baseUrl: "http://localhost:8000/api" });
export async function upload(
note: ClientNote,
key: Uint8Array,
): Promise<string> {
const data = encode(note.files);
const encrypted = encrypt(data, key);
const serverNote: ServerNote = {
metadata: structuredClone(note.metadata),
public: "",
data: encrypted,
};
const payload = encode(serverNote);
const response = await client
.post("/notes/v2", {
headers: {
"content-type": "application/msgpack",
},
body: new Blob([payload]).stream(),
})
.json(
z.object({
id: z.string().nonempty(),
}),
);
console.info("created note", response);
throw new Error("not implemented");
}
export function view(noteId: string): Promise<ClientNote> {
throw new Error("not implemented");
}
+42
View File
@@ -0,0 +1,42 @@
import { describe, expect, it } from "vitest";
import {
decrypt,
deriveKey,
encrypt,
generateKey,
utf8ToBytes,
} from "./encryption";
describe("encryption", () => {
it("chacha20 custom key", () => {
const password = "abc";
const payload = utf8ToBytes("libero iste qui");
const key = deriveKey(password);
const encrypted = encrypt(payload, key);
const decrypted = decrypt(encrypted, key);
expect(decrypted).toEqual(payload);
});
it("chacha20 auto key", () => {
const payload = utf8ToBytes(
"Earum id inventore debitis rerum minima necessitatibus consequuntur.",
);
const key = generateKey();
const encrypted = encrypt(payload, key);
const decrypted = decrypt(encrypted, key);
expect(decrypted).toEqual(payload);
});
it("stable derive key", () => {
const password = "alias laboriosam porro";
expect(deriveKey(password)).toMatchSnapshot();
});
it("derived key has same length as generated", () => {
const derived = deriveKey("ipsam esse asperiores");
const generated = generateKey();
expect(derived.length).toEqual(generated.length);
});
});
+40
View File
@@ -0,0 +1,40 @@
import { xchacha20poly1305 } from "@noble/ciphers/chacha.js";
import {
managedNonce,
randomBytes,
utf8ToBytes,
} from "@noble/ciphers/utils.js";
import { scrypt } from "@noble/hashes/scrypt.js";
export { bytesToUtf8, utf8ToBytes } from "@noble/ciphers/utils.js";
const APP_SPECIFIC_SECRET = "758ac0b9d5f04efca13f57909d3d0fc0";
const SECURITY_LEVEL = 2 ** 15;
const KEY_SIZE = 32;
export function deriveKey(password: string) {
const key = scrypt(password, APP_SPECIFIC_SECRET, {
N: SECURITY_LEVEL,
r: 8,
p: 1,
dkLen: KEY_SIZE,
});
return key;
}
export function generateKey(): Uint8Array {
return randomBytes(KEY_SIZE);
}
export function encrypt(payload: Uint8Array, key: Uint8Array): Uint8Array {
const chacha = managedNonce(xchacha20poly1305)(key);
const data = payload instanceof Uint8Array ? payload : utf8ToBytes(payload);
const ciphertext = chacha.encrypt(data);
return ciphertext;
}
export function decrypt(ciphertext: Uint8Array, key: Uint8Array): Uint8Array {
const chacha = managedNonce(xchacha20poly1305)(key);
const decrypted = chacha.decrypt(ciphertext, key);
return decrypted;
}
+1
View File
@@ -0,0 +1 @@
export * as encryption from "./encryption";
+39
View File
@@ -0,0 +1,39 @@
/**
* `metadata` is public, and NOT encrypted
*/
export type NoteMetadata = {
expiration?: number;
views?: number;
};
export type ClientNote = {
metadata: NoteMetadata;
/**
* public information
*/
public: string;
/**
* `files` are encrypted
*/
files: {
name: string;
type: string;
content: Uint8Array;
}[];
};
export type ServerNote = {
metadata: NoteMetadata;
/**
* Publicly available information
*/
public: string;
/**
* Encrypted binary blob
*/
data: Uint8Array;
};
+8
View File
@@ -0,0 +1,8 @@
{
"extends": ["@tsconfig/strictest"],
"compilerOptions": {
"target": "ESNext",
"module": "ESNext",
"moduleResolution": "bundler",
},
}
+26
View File
@@ -0,0 +1,26 @@
import { defineConfig } from "vitest/config";
import { playwright } from "@vitest/browser-playwright";
export default defineConfig({
test: {
projects: [
{ test: { name: "node", environment: "node" } },
{
test: {
name: "Browser",
browser: {
enabled: true,
provider: playwright(),
headless: true,
// https://vitest.dev/config/browser/playwright
instances: [
{ browser: "chromium" },
{ browser: "firefox" },
{ browser: "webkit" },
],
},
},
},
],
},
});
+387 -2
View File
@@ -4,6 +4,21 @@ settings:
autoInstallPeers: true
excludeLinksFromLockfile: false
catalogs:
default:
'@tsconfig/strictest':
specifier: ^2.0.8
version: 2.0.8
'@vitest/browser-playwright':
specifier: ^4.1.7
version: 4.1.7
typescript:
specifier: ^6.0.3
version: 6.0.3
vitest:
specifier: ^4.1.7
version: 4.1.7
importers:
.:
@@ -120,6 +135,37 @@ importers:
specifier: ^8.0.14
version: 8.0.14(@types/node@24.12.4)(esbuild@0.27.7)
packages/shared:
dependencies:
'@msgpack/msgpack':
specifier: ^3.1.3
version: 3.1.3
'@noble/ciphers':
specifier: ^2.2.0
version: 2.2.0
'@noble/hashes':
specifier: ^2.2.0
version: 2.2.0
ky:
specifier: ^2.0.2
version: 2.0.2
zod:
specifier: ^4.4.3
version: 4.4.3
devDependencies:
'@tsconfig/strictest':
specifier: 'catalog:'
version: 2.0.8
'@vitest/browser-playwright':
specifier: 'catalog:'
version: 4.1.7(playwright@1.60.0)(vite@8.0.14(@types/node@24.12.4)(esbuild@0.27.7))(vitest@4.1.7)
typescript:
specifier: 'catalog:'
version: 6.0.3
vitest:
specifier: 'catalog:'
version: 4.1.7(@types/node@24.12.4)(@vitest/browser-playwright@4.1.7)(vite@8.0.14(@types/node@24.12.4)(esbuild@0.27.7))
packages:
'@ampproject/remapping@2.3.0':
@@ -193,6 +239,9 @@ packages:
resolution: {integrity: sha512-Y3IR1cRnOxOCDvMmNiym7XpXQ93iGDDPHx+Zj+NM+rg0fBaShfQLkg+hKPaZCEvg5N/LeCo4+Rj/i3FuJsIQaw==}
engines: {node: '>=6.9.0'}
'@blazediff/core@1.9.1':
resolution: {integrity: sha512-ehg3jIkYKulZh+8om/O25vkvSsXXwC+skXmyA87FFx6A/45eqOkZsBltMw/TVteb0mloiGT8oGRTcjRAz66zaA==}
'@commander-js/extra-typings@12.1.0':
resolution: {integrity: sha512-wf/lwQvWAA0goIghcb91dQYpkLBcyhOhQNqG/VgWhnKzgt+UOMvra7EX/2fv70arm5RW+PUHoQHHDa6/p77Eqg==}
peerDependencies:
@@ -437,12 +486,24 @@ packages:
resolution: {integrity: sha512-p5KAAp6qe16lVYyh+pgvgY7FvnlJ44ICa/jNLMB442F1BsTfaITa4zX1EhwWGqc0NDRxjBvNZPF0Hw6Rc2EClg==}
engines: {node: '>=18'}
'@msgpack/msgpack@3.1.3':
resolution: {integrity: sha512-47XIizs9XZXvuJgoaJUIE2lFoID8ugvc0jzSHP+Ptfk8nTbnR8g788wv48N03Kx0UkAv559HWRQ3yzOgzlRNUA==}
engines: {node: '>= 18'}
'@napi-rs/wasm-runtime@1.1.4':
resolution: {integrity: sha512-3NQNNgA1YSlJb/kMH1ildASP9HW7/7kYnRI2szWJaofaS1hWmbGI4H+d3+22aGzXXN9IJ+n+GiFVcGipJP18ow==}
peerDependencies:
'@emnapi/core': ^1.7.1
'@emnapi/runtime': ^1.7.1
'@noble/ciphers@2.2.0':
resolution: {integrity: sha512-Z6pjIZ/8IJcCGzb2S/0Px5J81yij85xASuk1teLNeg75bfT07MV3a/O2Mtn1I2se43k3lkVEcFaR10N4cgQcZA==}
engines: {node: '>= 20.19.0'}
'@noble/hashes@2.2.0':
resolution: {integrity: sha512-IYqDGiTXab6FniAgnSdZwgWbomxpy9FtYvLKs7wCUs2a8RkITG+DFGO1DM9cr+E3/RgADRpFjrKVaJ1z6sjtEg==}
engines: {node: '>= 20.19.0'}
'@npmcli/agent@4.0.2':
resolution: {integrity: sha512-EUEuWAxnL07Sp5/iC/1X6Xj+XThUvnbei9zfRWZdEXa7lss9RTHMhAHBeg+MZ5To9s/gGaSI+UwZTPdYMvKSeg==}
engines: {node: ^20.17.0 || >=22.9.0}
@@ -779,6 +840,9 @@ packages:
svelte: ^5.46.4
vite: ^8.0.0-beta.7 || ^8.0.0
'@tsconfig/strictest@2.0.8':
resolution: {integrity: sha512-XnQ7vNz5HRN0r88GYf1J9JJjqtZPiHt2woGJOo2dYqyHGGcd6OLGqSlBB6p1j9mpzja6Oe5BoPqWmeDx6X9rLw==}
'@tufjs/canonical-json@2.0.0':
resolution: {integrity: sha512-yVtV8zsdo8qFHe+/3kw81dSLyF7D576A5cCFCi4X7B39tWT7SekaEFUnvnWJHz+9qO7qJTah1JbrDjWKqFtdWA==}
engines: {node: ^16.14.0 || >=18.0.0}
@@ -790,12 +854,21 @@ packages:
'@tybys/wasm-util@0.10.2':
resolution: {integrity: sha512-RoBvJ2X0wuKlWFIjrwffGw1IqZHKQqzIchKaadZZfnNpsAYp2mM0h36JtPCjNDAHGgYez/15uMBpfGwchhiMgg==}
'@types/chai@5.2.3':
resolution: {integrity: sha512-Mw558oeA9fFbv65/y4mHtXDs9bPnFMZAL/jxdPFUpOHHIXX91mcgEHbS5Lahr+pwZFR8A7GQleRWeI6cGFC2UA==}
'@types/cookie@0.6.0':
resolution: {integrity: sha512-4Kh9a6B2bQciAhf7FSuMRRkUWecJgJu9nPnx3yzpsfXX/c50REIqpHY4C82bXP90qrLtXtkDxTZosYO3UpOwlA==}
'@types/deep-eql@4.0.2':
resolution: {integrity: sha512-c9h9dVVMigMPc4bwTvC5dxqtqJZwQPePsWjPlpSOnojbor6pGqdk541lfA7AqFQr5pB1BRdq0juY9db81BwyFw==}
'@types/estree@1.0.6':
resolution: {integrity: sha512-AYnb1nQyY49te+VRAVgmzfcgjYS91mY5P0TKUDCLEM+gNnA+3T6rWITXRLYCpahpqSQbN5cE+gHpnPyXjHWxcw==}
'@types/estree@1.0.9':
resolution: {integrity: sha512-GhdPgy1el4/ImP05X05Uw4cw2/M93BCUmnEvWZNStlCzEKME4Fkk+YpoA5OiHNQmoS7Cafb8Xa3Pya8m1Qrzeg==}
'@types/inquirer@9.0.9':
resolution: {integrity: sha512-/mWx5136gts2Z2e5izdoRCo46lPp5TMs9R15GTSsgg/XnZyxDWVqoVU3R9lWnccKpqwsJLvRoxbCjoJtZB7DSw==}
@@ -818,6 +891,46 @@ packages:
'@types/trusted-types@2.0.7':
resolution: {integrity: sha512-ScaPdn1dQczgbl0QFTeTOmVHFULt394XJgOQNoyVhZ6r2vLnMLJfBPd53SB52T/3G36VI1/g2MZaX0cwDuXsfw==}
'@vitest/browser-playwright@4.1.7':
resolution: {integrity: sha512-OlTlJej7YN6VwV7zJJoNeaCsctF+JXpzpZ4oBHUbrQFfIq+0KW2f07rprCLh9N/zRIZ0v4Mchn1QDDmWMUhPKw==}
peerDependencies:
playwright: '*'
vitest: 4.1.7
'@vitest/browser@4.1.7':
resolution: {integrity: sha512-N2JFGfXoEGVAut+kHeru9dD4BUMq/q5xDvBARNl0tUsly3m5KglLOu8VO/6MkDfOlgxXTycojkt6gBKsuyR+IQ==}
peerDependencies:
vitest: 4.1.7
'@vitest/expect@4.1.7':
resolution: {integrity: sha512-1R+tw0ortHEbZDGMymm+pN7/AFQ/RkFFdtd7EN+VBpynKmLbP8A3rpEXdshBJ7+8hQ9zBJh/i1s0yKNtxAnU7w==}
'@vitest/mocker@4.1.7':
resolution: {integrity: sha512-vY7nuamKgfvpA1Koa3oYIw/k7D6kZnpGyNMZW8loow2bsBYla1TFdqTaXncWdRn4pgwNs+90RhnXhJScDwQeJA==}
peerDependencies:
msw: ^2.4.9
vite: ^6.0.0 || ^7.0.0 || ^8.0.0
peerDependenciesMeta:
msw:
optional: true
vite:
optional: true
'@vitest/pretty-format@4.1.7':
resolution: {integrity: sha512-umgCarTOYQWIaDMvGDRZij+6b9oVeLIyJzfN+AS88e0ZOU3QTgNNSTtjQOpcvWr3np1N0j4WgZj+sb3oYBDscw==}
'@vitest/runner@4.1.7':
resolution: {integrity: sha512-BapjmAQ2aI78WdMEfeUWivnfVzB+VPGwWRQcJE0OUq7qEeEcBsCSf+0T5iREBNE5nBb4wA5Ya0W6IA+sghdEFw==}
'@vitest/snapshot@4.1.7':
resolution: {integrity: sha512-ZacLzja+TmJeZ1h14xW2FB/WpeimUD3haBXQPyJqxvo8jQTmfeA8zv58mtjN2C7EHXZDYVcVYdYmAxjkWVvKCw==}
'@vitest/spy@4.1.7':
resolution: {integrity: sha512-kbkI5LMWakyuTIvs6fUJ5qdIVb1XVKsYJAT4OJ938cHMROYMSfmoQdZy0aaAnjbbc8F61vkoTqz/Az+/HiIu5Q==}
'@vitest/utils@4.1.7':
resolution: {integrity: sha512-T532WBu791cBxJlCl6SO+J14l81DQx6uQHm1bQbmCDY7nqlEIgkza/UFnSBNaUtSf41unldDFjdOBYEQC4b5Hw==}
'@zerodevx/svelte-toast@0.9.6':
resolution: {integrity: sha512-nHlTrCjverlPK9yukK6fqbG3e/R+f10ldrc4nJHOe2qNDScuPTuYVSFEk2dDDtzWAwTN5pmdEXgA3M2RbT8jiw==}
peerDependencies:
@@ -895,6 +1008,10 @@ packages:
resolution: {integrity: sha512-bMxMKAjg13EBSVscxTaYA4mRc5t1UAXa2kXiGTNfZ079HIWXEkKmkgFrh/nJqamaLSrXO5H4WFFkPEaLJWbs3A==}
engines: {node: '>= 0.4'}
assertion-error@2.0.1:
resolution: {integrity: sha512-Izi8RQcffqCeNVgFigKli1ssklIbpHnCYc6AknXGYoB6grJqyeby7jv12JUQgmTAnIDnbck1uxksT4dzN3PWBA==}
engines: {node: '>=12'}
available-typed-arrays@1.0.7:
resolution: {integrity: sha512-wvUjBtSGN7+7SjNpq/9M2Tg350UZD3q62IFZLbRAR1bSMlCo1ZaeW+BJ+D090e4hIIZLBcTDWe4Mh4jvUDajzQ==}
engines: {node: '>= 0.4'}
@@ -964,6 +1081,10 @@ packages:
caniuse-lite@1.0.30001701:
resolution: {integrity: sha512-faRs/AW3jA9nTwmJBSO1PQ6L/EOgsB5HMQQq4iCu5zhPgVVgO/pZRHlmatwijZKetFw8/Pr4q6dEN8sJuq8qTw==}
chai@6.2.2:
resolution: {integrity: sha512-NUPRluOfOiTKBKvWPtSD4PhFvWCqOi0BGStNWs57X9js7XGTprSmFoz5F0tWhR4WPjNeR9jXqdC7/UpSJTnlRg==}
engines: {node: '>=18'}
chalk@2.4.2:
resolution: {integrity: sha512-Mti+f9lpJNcwF4tWV8/OrTTtF1gZi+f8FqlyAdouralcFWFQWF2+NgCHShjkCb+IFBLq9buZwE1xckQU4peSuQ==}
engines: {node: '>=4'}
@@ -1131,6 +1252,9 @@ packages:
resolution: {integrity: sha512-Zf5H2Kxt2xjTvbJvP2ZWLEICxA6j+hAmMzIlypy4xcBg1vKVnx89Wy0GbS+kf5cwCVFFzdCFh2XSCFNULS6csw==}
engines: {node: '>= 0.4'}
es-module-lexer@2.1.0:
resolution: {integrity: sha512-n27zTYMjYu1aj4MjCWzSP7G9r75utsaoc8m61weK+W8JMBGGQybd43GstCXZ3WNmSFtGT9wi59qQTW6mhTR5LQ==}
es-set-tostringtag@2.0.3:
resolution: {integrity: sha512-3T8uNMC3OQTHkFUsFq8r/BwAXLHvU/9O9mE0fBc/MY5iq/8H7ncvO947LmYA6ldWw9Uh8Yhf25zu6n7nML5QWQ==}
engines: {node: '>= 0.4'}
@@ -1163,6 +1287,13 @@ packages:
'@typescript-eslint/types':
optional: true
estree-walker@3.0.3:
resolution: {integrity: sha512-7RUKfXgSMMkzt6ZuXmqapOurLGPPfgj6l9uRZ7lRGolvk0y2yocc35LdcxKC5PQZdn2DMqioAQ2NoWcrTKmm6g==}
expect-type@1.3.0:
resolution: {integrity: sha512-knvyeauYhqjOYvQ66MznSMs83wmHrCycNEN6Ao+2AeYEfxUIkuiVxdEa1qlGEPK+We3n0THiDciYSsCcgW/DoA==}
engines: {node: '>=12.0.0'}
exponential-backoff@3.1.3:
resolution: {integrity: sha512-ZgEeZXj30q+I0EN+CbSSpIyPaJ5HVQD18Z1m+u1FXbAeT94mr1zw50q4q6jiiC447Nl/YTcIYSAftiGqetwXCA==}
@@ -1488,6 +1619,10 @@ packages:
resolution: {integrity: sha512-o+NO+8WrRiQEE4/7nwRJhN1HWpVmJm511pBHUxPLtp0BUISzlBplORYSmTclCnJvQq2tKu/sgl3xVpkc7ZWuQQ==}
engines: {node: '>=6'}
ky@2.0.2:
resolution: {integrity: sha512-/GmXpo9F9W+f8n4Ivr2iH+7h7wL7jLbLKWkMlpflcCRb6kGjBfTlASEXaZ9qUgNTn4VgS0P2pwxxzQ4EM6Ulgg==}
engines: {node: '>=22'}
license-checker-rseidelsohn@5.0.1:
resolution: {integrity: sha512-9X+ikKxt9Hy3zOrOZzW1dXL4St5akoYjLt63Am9JZVzU6aTdN+xfDvqySpnJT+gF/h5RmtMk2waW6TDNNCKbqQ==}
engines: {node: '>=24', npm: '>=11'}
@@ -1889,6 +2024,10 @@ packages:
engines: {node: '>=18'}
hasBin: true
pngjs@7.0.0:
resolution: {integrity: sha512-LKWqWJRhstyYo9pGvgor/ivk2w94eSjE3RGVuzLGlr3NmD8bf7RcYGze1mNdEHRP6TRP6rMuDHk5t44hnTRyow==}
engines: {node: '>=14.19.0'}
possible-typed-array-names@1.0.0:
resolution: {integrity: sha512-d7Uw+eZoloe0EHDIYoe+bQ5WXnGMOpmiZFTuMWCwpjzzkL2nTjcKiAk4hh8TjnGye2TwWOk3UXucZ+3rbmBa8Q==}
engines: {node: '>= 0.4'}
@@ -2069,6 +2208,9 @@ packages:
resolution: {integrity: sha512-fDW/EZ6Q9RiO8eFG8Hj+7u/oW+XrPTIChwCOM2+th2A6OblDtYYIpve9m+KvI9Z4C9qSEXlaGR6bTEYHReuglA==}
engines: {node: '>= 0.4'}
siginfo@2.0.0:
resolution: {integrity: sha512-ybx0WO1/8bSBLEWXZvEd7gMW3Sn3JFlW3TvX1nREbDLRNQNaeNN8WK0meBwPdAaOI7TtRRRJn/Es1zhrrCHu7g==}
signal-exit@3.0.7:
resolution: {integrity: sha512-wnD2ZE+l+SPC/uoS0vXeE9L1+0wuaMqKlfz9AMUo38JsyLSBWSFcHR1Rri62LZc12vLr1gb3jl7iwQhgwpAbGQ==}
@@ -2084,6 +2226,10 @@ packages:
resolution: {integrity: sha512-FoqMu0NCGBLCcAkS1qA+XJIQTR6/JHfQXl+uGteNCQ76T91DMUjPa9xfmeqMY3z80nLSg9yQmNjK0Px6RWsH/A==}
engines: {node: '>=18'}
sirv@3.0.2:
resolution: {integrity: sha512-2wcC/oGxHis/BoHkkPwldgiPSYcpZK3JU28WoMVv55yHJgcZ8rlXvuG9iZggz+sU1d4bRgIGASwyWqjxu3FM0g==}
engines: {node: '>=18'}
smart-buffer@4.2.0:
resolution: {integrity: sha512-94hK0Hh8rPqQl2xXc3HsaBoOXKV20MToPkcXvwbISWLEs+64sBq5kFgn2kJDHb1Pry9yrP0dxrCI9RRci7RXKg==}
engines: {node: '>= 6.0.0', npm: '>= 3.0.0'}
@@ -2132,6 +2278,12 @@ packages:
resolution: {integrity: sha512-QUiRf1+u9wPTL/76GTYlKttDEBWV1ga9ZXW8BG6kfdeyyM8LGPix9gROyg9V2+P0xNyF3X2Go526xKFdMZrHSQ==}
engines: {node: ^20.17.0 || >=22.9.0}
stackback@0.0.2:
resolution: {integrity: sha512-1XMJE5fQo1jGH6Y/7ebnwPOBEkIEnT4QF32d5R1+VXdXveM0IBMJt8zfaxX1P3QhVwrYe+576+jkANtSS2mBbw==}
std-env@4.1.0:
resolution: {integrity: sha512-Rq7ybcX2RuC55r9oaPVEW7/xu3tj8u4GeBYHBWCychFtzMIr86A7e3PPEBPT37sHStKX3+TiX/Fr/ACmJLVlLQ==}
string-width@4.2.3:
resolution: {integrity: sha512-wKyQRQpjJ0sIp62ErSZdGsjMJWsap5oRNihHhu6G7JVO/9jIB6UyevL+tXuOqrng8j/cxKTWyWUwvSTriiZz/g==}
engines: {node: '>=8'}
@@ -2216,13 +2368,24 @@ packages:
thenify@3.3.1:
resolution: {integrity: sha512-RVZSIV5IG10Hk3enotrhvz0T9em6cyHBLkH/YAZuKqd8hRkKhSfCGIcP2KUY0EPxndzANBmNllzWPwak+bheSw==}
tinybench@2.9.0:
resolution: {integrity: sha512-0+DUvqWMValLmha6lr4kD8iAMK1HzV0/aKnCtWb9v9641TnP/MFb7Pc2bxoxQjTXAErryXVgUOfv2YqNllqGeg==}
tinyexec@0.3.2:
resolution: {integrity: sha512-KQQR9yN7R5+OSwaK0XQoj22pwHoTlgYqmUscPYoknOoWCWfj/5/ABTMRi69FrKU5ffPVh5QcFikpWJI/P1ocHA==}
tinyexec@1.2.2:
resolution: {integrity: sha512-M/Q0B2cp4K7kynaT/vnED1j8TlLY+Pp7C6Wl2bl/7u/F0mUVwdyOpwomQb8JpYLitHUssAJRmLZdMCGsrx7i+g==}
engines: {node: '>=18'}
tinyglobby@0.2.16:
resolution: {integrity: sha512-pn99VhoACYR8nFHhxqix+uvsbXineAasWm5ojXoN8xEwK5Kd3/TrhNn1wByuD52UxWRLy8pu+kRMniEi6Eq9Zg==}
engines: {node: '>=12.0.0'}
tinyrainbow@3.1.0:
resolution: {integrity: sha512-Bf+ILmBgretUrdJxzXM0SgXLZ3XfiaUuOj/IKQHuTXip+05Xn+uyEYdVg0kYDipTBcLrCVyUzAPz7QmArb0mmw==}
engines: {node: '>=14.0.0'}
totalist@3.0.1:
resolution: {integrity: sha512-sf4i37nQ2LBx4m3wB74y+ubopq6W/dIzXg0FDGjsYnZHVa1Da8FH853wlL2gtUhg+xJXjfk3kUZS3BRoQeoQBQ==}
engines: {node: '>=6'}
@@ -2387,6 +2550,47 @@ packages:
vite:
optional: true
vitest@4.1.7:
resolution: {integrity: sha512-flYyaFd2CgoCoU+0UKt3pxksgC+S02iTDN0n3LtqaMeXsI9SBcdNujc2k0DeFLzUn/0k538yNjOSdwgCqcrwJA==}
engines: {node: ^20.0.0 || ^22.0.0 || >=24.0.0}
hasBin: true
peerDependencies:
'@edge-runtime/vm': '*'
'@opentelemetry/api': ^1.9.0
'@types/node': ^20.0.0 || ^22.0.0 || >=24.0.0
'@vitest/browser-playwright': 4.1.7
'@vitest/browser-preview': 4.1.7
'@vitest/browser-webdriverio': 4.1.7
'@vitest/coverage-istanbul': 4.1.7
'@vitest/coverage-v8': 4.1.7
'@vitest/ui': 4.1.7
happy-dom: '*'
jsdom: '*'
vite: ^6.0.0 || ^7.0.0 || ^8.0.0
peerDependenciesMeta:
'@edge-runtime/vm':
optional: true
'@opentelemetry/api':
optional: true
'@types/node':
optional: true
'@vitest/browser-playwright':
optional: true
'@vitest/browser-preview':
optional: true
'@vitest/browser-webdriverio':
optional: true
'@vitest/coverage-istanbul':
optional: true
'@vitest/coverage-v8':
optional: true
'@vitest/ui':
optional: true
happy-dom:
optional: true
jsdom:
optional: true
walk-up-path@4.0.0:
resolution: {integrity: sha512-3hu+tD8YzSLGuFYtPRb48vdhKMi0KQV5sn+uWr8+7dMEq/2G/dtLrdDinkLjqq5TIbIBjYJ4Ax/n3YiaW7QM8A==}
engines: {node: 20 || >=22}
@@ -2415,6 +2619,11 @@ packages:
engines: {node: ^20.17.0 || >=22.9.0}
hasBin: true
why-is-node-running@2.3.0:
resolution: {integrity: sha512-hUrmaWBdVDcxvYqnyh09zunKzROWjbZTiNy8dBEjkS7ehEDQibXJ7XvlmtbwuTclUiIyN+CyXQD4Vmko8fNm8w==}
engines: {node: '>=8'}
hasBin: true
wrap-ansi@6.2.0:
resolution: {integrity: sha512-r6lPcBGxZXlIcymEu7InxDMhdW0KDxpLgoFLcguasxCaJ/SOIZwINatK9KY/tf+ZrlywOKU0UDj3ATXUBfxJXA==}
engines: {node: '>=8'}
@@ -2434,6 +2643,18 @@ packages:
resolution: {integrity: sha512-OTIk8iR8/aCRWBqvxrzxR0hgxWpnYBblY1S5hDWBQfk/VFmJwzmJgQFN3WsoUKHISv2eAwe+PpbUzyL1CKTLXg==}
engines: {node: ^20.17.0 || >=22.9.0}
ws@8.21.0:
resolution: {integrity: sha512-Vsp28b7DRcimFQvrqu2Wek3z1iYxDCWqHYB8Qsnk/S4RfaCQzPGPyBNuVjJV3cd6UiKtUtp6sNM77gWvzcCH+g==}
engines: {node: '>=10.0.0'}
peerDependencies:
bufferutil: ^4.0.1
utf-8-validate: '>=5.0.2'
peerDependenciesMeta:
bufferutil:
optional: true
utf-8-validate:
optional: true
yallist@3.1.1:
resolution: {integrity: sha512-a4UGQaWPH59mOXUYnAG2ewncQS4i4F43Tv3JoAM+s2VDAmS9NsK8GpDMLrCHPksFT7h3K6TOoUNn2pb7RoXx4g==}
@@ -2451,6 +2672,9 @@ packages:
zimmerframe@1.1.2:
resolution: {integrity: sha512-rAbqEGa8ovJy4pyBxZM70hg4pE6gDgaQ0Sl9M3enG3I0d6H4XSAM3GeNGLKnsBpuijUow064sf7ww1nutC5/3w==}
zod@4.4.3:
resolution: {integrity: sha512-ytENFjIJFl2UwYglde2jchW2Hwm4GJFLDiSXWdTrJQBIN9Fcyp7n4DhxJEiWNAJMV1/BqWfW/kkg71UDcHJyTQ==}
snapshots:
'@ampproject/remapping@2.3.0':
@@ -2558,6 +2782,8 @@ snapshots:
'@babel/helper-string-parser': 7.25.9
'@babel/helper-validator-identifier': 7.25.9
'@blazediff/core@1.9.1': {}
'@commander-js/extra-typings@12.1.0(commander@12.1.0)':
dependencies:
commander: 12.1.0
@@ -2736,6 +2962,8 @@ snapshots:
'@lokalise/node-api@13.2.1': {}
'@msgpack/msgpack@3.1.3': {}
'@napi-rs/wasm-runtime@1.1.4(@emnapi/core@1.10.0)(@emnapi/runtime@1.10.0)':
dependencies:
'@emnapi/core': 1.10.0
@@ -2743,6 +2971,10 @@ snapshots:
'@tybys/wasm-util': 0.10.2
optional: true
'@noble/ciphers@2.2.0': {}
'@noble/hashes@2.2.0': {}
'@npmcli/agent@4.0.2':
dependencies:
agent-base: 7.1.4
@@ -3055,6 +3287,8 @@ snapshots:
vite: 8.0.14(@types/node@24.12.4)(esbuild@0.27.7)
vitefu: 1.1.3(vite@8.0.14(@types/node@24.12.4)(esbuild@0.27.7))
'@tsconfig/strictest@2.0.8': {}
'@tufjs/canonical-json@2.0.0': {}
'@tufjs/models@4.1.0':
@@ -3067,10 +3301,19 @@ snapshots:
tslib: 2.8.1
optional: true
'@types/chai@5.2.3':
dependencies:
'@types/deep-eql': 4.0.2
assertion-error: 2.0.1
'@types/cookie@0.6.0': {}
'@types/deep-eql@4.0.2': {}
'@types/estree@1.0.6': {}
'@types/estree@1.0.9': {}
'@types/inquirer@9.0.9':
dependencies:
'@types/through': 0.0.33
@@ -3098,6 +3341,77 @@ snapshots:
'@types/trusted-types@2.0.7': {}
'@vitest/browser-playwright@4.1.7(playwright@1.60.0)(vite@8.0.14(@types/node@24.12.4)(esbuild@0.27.7))(vitest@4.1.7)':
dependencies:
'@vitest/browser': 4.1.7(vite@8.0.14(@types/node@24.12.4)(esbuild@0.27.7))(vitest@4.1.7)
'@vitest/mocker': 4.1.7(vite@8.0.14(@types/node@24.12.4)(esbuild@0.27.7))
playwright: 1.60.0
tinyrainbow: 3.1.0
vitest: 4.1.7(@types/node@24.12.4)(@vitest/browser-playwright@4.1.7)(vite@8.0.14(@types/node@24.12.4)(esbuild@0.27.7))
transitivePeerDependencies:
- bufferutil
- msw
- utf-8-validate
- vite
'@vitest/browser@4.1.7(vite@8.0.14(@types/node@24.12.4)(esbuild@0.27.7))(vitest@4.1.7)':
dependencies:
'@blazediff/core': 1.9.1
'@vitest/mocker': 4.1.7(vite@8.0.14(@types/node@24.12.4)(esbuild@0.27.7))
'@vitest/utils': 4.1.7
magic-string: 0.30.21
pngjs: 7.0.0
sirv: 3.0.2
tinyrainbow: 3.1.0
vitest: 4.1.7(@types/node@24.12.4)(@vitest/browser-playwright@4.1.7)(vite@8.0.14(@types/node@24.12.4)(esbuild@0.27.7))
ws: 8.21.0
transitivePeerDependencies:
- bufferutil
- msw
- utf-8-validate
- vite
'@vitest/expect@4.1.7':
dependencies:
'@standard-schema/spec': 1.1.0
'@types/chai': 5.2.3
'@vitest/spy': 4.1.7
'@vitest/utils': 4.1.7
chai: 6.2.2
tinyrainbow: 3.1.0
'@vitest/mocker@4.1.7(vite@8.0.14(@types/node@24.12.4)(esbuild@0.27.7))':
dependencies:
'@vitest/spy': 4.1.7
estree-walker: 3.0.3
magic-string: 0.30.21
optionalDependencies:
vite: 8.0.14(@types/node@24.12.4)(esbuild@0.27.7)
'@vitest/pretty-format@4.1.7':
dependencies:
tinyrainbow: 3.1.0
'@vitest/runner@4.1.7':
dependencies:
'@vitest/utils': 4.1.7
pathe: 2.0.3
'@vitest/snapshot@4.1.7':
dependencies:
'@vitest/pretty-format': 4.1.7
'@vitest/utils': 4.1.7
magic-string: 0.30.21
pathe: 2.0.3
'@vitest/spy@4.1.7': {}
'@vitest/utils@4.1.7':
dependencies:
'@vitest/pretty-format': 4.1.7
convert-source-map: 2.0.0
tinyrainbow: 3.1.0
'@zerodevx/svelte-toast@0.9.6(svelte@5.55.9)':
dependencies:
svelte: 5.55.9
@@ -3156,6 +3470,8 @@ snapshots:
is-array-buffer: 3.0.4
is-shared-array-buffer: 1.0.3
assertion-error@2.0.1: {}
available-typed-arrays@1.0.7:
dependencies:
possible-typed-array-names: 1.0.0
@@ -3243,6 +3559,8 @@ snapshots:
caniuse-lite@1.0.30001701: {}
chai@6.2.2: {}
chalk@2.4.2:
dependencies:
ansi-styles: 3.2.1
@@ -3414,6 +3732,8 @@ snapshots:
es-errors@1.3.0: {}
es-module-lexer@2.1.0: {}
es-set-tostringtag@2.0.3:
dependencies:
get-intrinsic: 1.2.4
@@ -3465,6 +3785,12 @@ snapshots:
dependencies:
'@jridgewell/sourcemap-codec': 1.5.0
estree-walker@3.0.3:
dependencies:
'@types/estree': 1.0.9
expect-type@1.3.0: {}
exponential-backoff@3.1.3: {}
fdir@6.4.3(picomatch@4.0.4):
@@ -3477,7 +3803,7 @@ snapshots:
fix-dts-default-cjs-exports@1.0.1:
dependencies:
magic-string: 0.30.17
magic-string: 0.30.21
mlly: 1.8.2
rollup: 4.34.8
@@ -3709,7 +4035,7 @@ snapshots:
is-reference@3.0.3:
dependencies:
'@types/estree': 1.0.6
'@types/estree': 1.0.9
is-regex@1.1.4:
dependencies:
@@ -3776,6 +4102,8 @@ snapshots:
kleur@4.1.5: {}
ky@2.0.2: {}
license-checker-rseidelsohn@5.0.1:
dependencies:
'@npmcli/arborist': 9.6.0
@@ -4190,6 +4518,8 @@ snapshots:
optionalDependencies:
fsevents: 2.3.2
pngjs@7.0.0: {}
possible-typed-array-names@1.0.0: {}
postcss-load-config@6.0.1(postcss@8.5.15):
@@ -4389,6 +4719,8 @@ snapshots:
get-intrinsic: 1.2.4
object-inspect: 1.13.1
siginfo@2.0.0: {}
signal-exit@3.0.7: {}
signal-exit@4.1.0: {}
@@ -4410,6 +4742,12 @@ snapshots:
mrmime: 2.0.1
totalist: 3.0.1
sirv@3.0.2:
dependencies:
'@polka/url': 1.0.0-next.28
mrmime: 2.0.1
totalist: 3.0.1
smart-buffer@4.2.0: {}
socks-proxy-agent@8.0.5:
@@ -4466,6 +4804,10 @@ snapshots:
dependencies:
minipass: 7.1.3
stackback@0.0.2: {}
std-env@4.1.0: {}
string-width@4.2.3:
dependencies:
emoji-regex: 8.0.0
@@ -4599,13 +4941,19 @@ snapshots:
dependencies:
any-promise: 1.3.0
tinybench@2.9.0: {}
tinyexec@0.3.2: {}
tinyexec@1.2.2: {}
tinyglobby@0.2.16:
dependencies:
fdir: 6.5.0(picomatch@4.0.4)
picomatch: 4.0.4
tinyrainbow@3.1.0: {}
totalist@3.0.1: {}
tree-kill@1.2.2: {}
@@ -4742,6 +5090,34 @@ snapshots:
optionalDependencies:
vite: 8.0.14(@types/node@24.12.4)(esbuild@0.27.7)
vitest@4.1.7(@types/node@24.12.4)(@vitest/browser-playwright@4.1.7)(vite@8.0.14(@types/node@24.12.4)(esbuild@0.27.7)):
dependencies:
'@vitest/expect': 4.1.7
'@vitest/mocker': 4.1.7(vite@8.0.14(@types/node@24.12.4)(esbuild@0.27.7))
'@vitest/pretty-format': 4.1.7
'@vitest/runner': 4.1.7
'@vitest/snapshot': 4.1.7
'@vitest/spy': 4.1.7
'@vitest/utils': 4.1.7
es-module-lexer: 2.1.0
expect-type: 1.3.0
magic-string: 0.30.21
obug: 2.1.1
pathe: 2.0.3
picomatch: 4.0.4
std-env: 4.1.0
tinybench: 2.9.0
tinyexec: 1.2.2
tinyglobby: 0.2.16
tinyrainbow: 3.1.0
vite: 8.0.14(@types/node@24.12.4)(esbuild@0.27.7)
why-is-node-running: 2.3.0
optionalDependencies:
'@types/node': 24.12.4
'@vitest/browser-playwright': 4.1.7(playwright@1.60.0)(vite@8.0.14(@types/node@24.12.4)(esbuild@0.27.7))(vitest@4.1.7)
transitivePeerDependencies:
- msw
walk-up-path@4.0.0: {}
wcwidth@1.0.1:
@@ -4776,6 +5152,11 @@ snapshots:
dependencies:
isexe: 4.0.0
why-is-node-running@2.3.0:
dependencies:
siginfo: 2.0.0
stackback: 0.0.2
wrap-ansi@6.2.0:
dependencies:
ansi-styles: 4.3.0
@@ -4800,6 +5181,8 @@ snapshots:
dependencies:
signal-exit: 4.1.0
ws@8.21.0: {}
yallist@3.1.1: {}
yallist@4.0.0: {}
@@ -4809,3 +5192,5 @@ snapshots:
yoctocolors-cjs@2.1.2: {}
zimmerframe@1.1.2: {}
zod@4.4.3: {}
+6
View File
@@ -5,3 +5,9 @@ allowBuilds:
esbuild: true
minimumReleaseAge: 10080 # One week
catalog:
"@tsconfig/strictest": "^2.0.8"
"typescript": "^6.0.3"
"vitest": "^4.1.7"
"@vitest/browser-playwright": "^4.1.7"