diff --git a/src/package.json b/src/package.json index ef75b66aa8..d0f0701277 100644 --- a/src/package.json +++ b/src/package.json @@ -18,11 +18,10 @@ "version-check": "pip3 install typing_extensions mypy || pip3 install --break-system-packages typing_extensions mypy && ./workspaces.py version-check && mypy scripts/check_npm_packages.py", "test-parallel": "unset DEBUG && pnpm run version-check && cd packages && pnpm run -r --parallel test", "test": "unset DEBUG && pnpm run depcheck && pnpm run version-check && ./workspaces.py test", - "test-github-ci": "unset DEBUG && pnpm run depcheck && pnpm run version-check && ./workspaces.py test --exclude=jupyter --retries=1", + "test-github-ci": "unset DEBUG && pnpm run depcheck && pnpm run version-check && ./workspaces.py test --exclude=jupyter,file-server --retries=1", "depcheck": "cd packages && pnpm run -r --parallel depcheck", "prettier-all": "cd packages/", "local-ci": "./scripts/ci.sh", - "conat-server": "cd packages/server && pnpm conat-server", "conat-connections": "cd packages/backend && pnpm conat-connections", "conat-watch": "cd packages/backend && pnpm conat-watch", "conat-inventory": "cd packages/backend && pnpm conat-inventory" diff --git a/src/packages/backend/get-listing.ts b/src/packages/backend/get-listing.ts index 0854ea82b1..02bcdbc835 100644 --- a/src/packages/backend/get-listing.ts +++ b/src/packages/backend/get-listing.ts @@ -4,7 +4,7 @@ */ /* -Server directory listing through the HTTP server and Websocket API. +This is used by backends to serve directory listings to clients: {files:[..., {size:?,name:?,mtime:?,isdir:?}]} diff --git a/src/packages/backend/logger.ts b/src/packages/backend/logger.ts index 3b89197d97..6d1dcea6a3 100644 --- a/src/packages/backend/logger.ts +++ b/src/packages/backend/logger.ts @@ -128,7 +128,7 @@ function initTransports() { // Similar as in debug source code, except I stuck a timestamp // at the beginning, which I like... except also aware of // non-printf formatting. - const line = `${new Date().toISOString()}: ${myFormat(...args)}\n`; + const line = `${new Date().toISOString()} (${process.pid}):${myFormat(...args)}\n`; if (transports.console) { // the console transport: diff --git a/src/packages/file-server/btrfs/filesystem.ts b/src/packages/file-server/btrfs/filesystem.ts new file mode 100644 index 0000000000..927fb23548 --- /dev/null +++ b/src/packages/file-server/btrfs/filesystem.ts @@ -0,0 +1,207 @@ +/* +BTRFS Filesystem + +DEVELOPMENT: + +Start node, then: + +DEBUG="cocalc:*file-server*" DEBUG_CONSOLE=yes node + +a = require('@cocalc/file-server/btrfs'); fs = await a.filesystem({device:'/tmp/btrfs.img', formatIfNeeded:true, mount:'/mnt/btrfs', uid:293597964}) + +*/ + +import refCache from "@cocalc/util/refcache"; +import { mkdirp, btrfs, sudo } from "./util"; +import { join } from "path"; +import { Subvolumes } from "./subvolumes"; +import { mkdir } from "fs/promises"; +import { exists } from "@cocalc/backend/misc/async-utils-node"; +import { executeCode } from "@cocalc/backend/execute-code"; + +// default size of btrfs filesystem if creating an image file. +const DEFAULT_FILESYSTEM_SIZE = "10G"; + +// default for newly created subvolumes +export const DEFAULT_SUBVOLUME_SIZE = "1G"; + +const MOUNT_ERROR = "wrong fs type, bad option, bad superblock"; + +export interface Options { + // the underlying block device. + // If this is a file (or filename) ending in .img, then it's a sparse file mounted as a loopback device. + // If this starts with "/dev" then it is a raw block device. + device: string; + // formatIfNeeded -- DANGEROUS! if true, format the device or image, + // if it doesn't mount with an error containing "wrong fs type, + // bad option, bad superblock". Never use this in production. Useful + // for testing and dev. + formatIfNeeded?: boolean; + // where the btrfs filesystem is mounted + mount: string; + + // default size of newly created subvolumes + defaultSize?: string | number; + defaultFilesystemSize?: string | number; +} + +export class Filesystem { + public readonly opts: Options; + public readonly bup: string; + public readonly subvolumes: Subvolumes; + + constructor(opts: Options) { + opts = { + defaultSize: DEFAULT_SUBVOLUME_SIZE, + defaultFilesystemSize: DEFAULT_FILESYSTEM_SIZE, + ...opts, + }; + this.opts = opts; + this.bup = join(this.opts.mount, "bup"); + this.subvolumes = new Subvolumes(this); + } + + init = async () => { + await mkdirp([this.opts.mount]); + await this.initDevice(); + await this.mountFilesystem(); + await btrfs({ + args: ["quota", "enable", "--simple", this.opts.mount], + }); + await this.initBup(); + }; + + unmount = async () => { + await sudo({ + command: "umount", + args: [this.opts.mount], + err_on_exit: true, + }); + }; + + close = () => {}; + + private initDevice = async () => { + if (!isImageFile(this.opts.device)) { + // raw block device -- nothing to do + return; + } + if (!(await exists(this.opts.device))) { + await sudo({ + command: "truncate", + args: ["-s", `${this.opts.defaultFilesystemSize}`, this.opts.device], + }); + } + }; + + info = async (): Promise<{ [field: string]: string }> => { + const { stdout } = await btrfs({ + args: ["subvolume", "show", this.opts.mount], + }); + const obj: { [field: string]: string } = {}; + for (const x of stdout.split("\n")) { + const i = x.indexOf(":"); + if (i == -1) continue; + obj[x.slice(0, i).trim()] = x.slice(i + 1).trim(); + } + return obj; + }; + + private mountFilesystem = async () => { + try { + await this.info(); + // already mounted + return; + } catch {} + const { stderr, exit_code } = await this._mountFilesystem(); + if (exit_code) { + if (stderr.includes(MOUNT_ERROR)) { + if (this.opts.formatIfNeeded) { + await this.formatDevice(); + const { stderr, exit_code } = await this._mountFilesystem(); + if (exit_code) { + throw Error(stderr); + } else { + return; + } + } + } + throw Error(stderr); + } + }; + + private formatDevice = async () => { + await sudo({ command: "mkfs.btrfs", args: [this.opts.device] }); + }; + + private _mountFilesystem = async () => { + const args: string[] = isImageFile(this.opts.device) ? ["-o", "loop"] : []; + args.push( + "-o", + "compress=zstd", + "-o", + "noatime", + "-o", + "space_cache=v2", + "-o", + "autodefrag", + this.opts.device, + "-t", + "btrfs", + this.opts.mount, + ); + { + const { stderr, exit_code } = await sudo({ + command: "mount", + args, + err_on_exit: false, + }); + if (exit_code) { + return { stderr, exit_code }; + } + } + const { stderr, exit_code } = await sudo({ + command: "chown", + args: [ + `${process.getuid?.() ?? 0}:${process.getgid?.() ?? 0}`, + this.opts.mount, + ], + err_on_exit: false, + }); + return { stderr, exit_code }; + }; + + private initBup = async () => { + if (!(await exists(this.bup))) { + await mkdir(this.bup); + } + await executeCode({ + command: "bup", + args: ["init"], + env: { BUP_DIR: this.bup }, + }); + }; +} + +function isImageFile(name: string) { + if (name.startsWith("/dev")) { + return false; + } + // TODO: could probably check os for a device with given name? + return name.endsWith(".img"); +} + +const cache = refCache({ + name: "btrfs-filesystems", + createObject: async (options: Options) => { + const filesystem = new Filesystem(options); + await filesystem.init(); + return filesystem; + }, +}); + +export async function filesystem( + options: Options & { noCache?: boolean }, +): Promise { + return await cache(options); +} diff --git a/src/packages/file-server/btrfs/index.ts b/src/packages/file-server/btrfs/index.ts new file mode 100644 index 0000000000..edfd27c3a9 --- /dev/null +++ b/src/packages/file-server/btrfs/index.ts @@ -0,0 +1 @@ +export { filesystem } from "./filesystem"; diff --git a/src/packages/file-server/btrfs/snapshots.ts b/src/packages/file-server/btrfs/snapshots.ts new file mode 100644 index 0000000000..daf8e09212 --- /dev/null +++ b/src/packages/file-server/btrfs/snapshots.ts @@ -0,0 +1,119 @@ +import { type SubvolumeSnapshots } from "./subvolume-snapshots"; +import getLogger from "@cocalc/backend/logger"; + +const logger = getLogger("file-server:btrfs:snapshots"); + +const DATE_REGEXP = /^\d{4}-\d{2}-\d{2}T\d{2}:\d{2}:\d{2}\.\d{3}Z$/; + +// Lengths of time in minutes to keep snapshots +// (code below assumes these are listed in ORDER from shortest to longest) +export const SNAPSHOT_INTERVALS_MS = { + frequent: 15 * 1000 * 60, + daily: 60 * 24 * 1000 * 60, + weekly: 60 * 24 * 7 * 1000 * 60, + monthly: 60 * 24 * 7 * 4 * 1000 * 60, +}; + +// How many of each type of snapshot to retain +export const DEFAULT_SNAPSHOT_COUNTS = { + frequent: 24, + daily: 14, + weekly: 7, + monthly: 4, +} as SnapshotCounts; + +export interface SnapshotCounts { + frequent: number; + daily: number; + weekly: number; + monthly: number; +} + +export async function updateRollingSnapshots({ + snapshots, + counts, +}: { + snapshots: SubvolumeSnapshots; + counts?: Partial; +}) { + counts = { ...DEFAULT_SNAPSHOT_COUNTS, ...counts }; + + const changed = await snapshots.hasUnsavedChanges(); + logger.debug("updateRollingSnapshots", { + name: snapshots.subvolume.name, + counts, + changed, + }); + if (!changed) { + // definitely no data written since most recent snapshot, so nothing to do + return; + } + + // get exactly the iso timestamp snapshot names: + const snapshotNames = (await snapshots.ls()) + .map((x) => x.name) + .filter((name) => DATE_REGEXP.test(name)); + snapshotNames.sort(); + if (snapshotNames.length > 0) { + const age = Date.now() - new Date(snapshotNames.slice(-1)[0]).valueOf(); + for (const key in SNAPSHOT_INTERVALS_MS) { + if (counts[key]) { + if (age < SNAPSHOT_INTERVALS_MS[key]) { + // no need to snapshot since there is already a sufficiently recent snapshot + logger.debug("updateRollingSnapshots: no need to snapshot", { + name: snapshots.subvolume.name, + }); + return; + } + // counts[key] nonzero and snapshot is old enough so we'll be making a snapshot + break; + } + } + } + + // make a new snapshot + const name = new Date().toISOString(); + await snapshots.create(name); + // delete extra snapshots + snapshotNames.push(name); + const toDelete = snapshotsToDelete({ counts, snapshots: snapshotNames }); + for (const expired of toDelete) { + try { + await snapshots.delete(expired); + } catch { + // some snapshots can't be deleted, e.g., they were used for the last send. + } + } +} + +function snapshotsToDelete({ counts, snapshots }): string[] { + if (snapshots.length == 0) { + // nothing to do + return []; + } + + // sorted from BIGGEST to smallest + const times = snapshots.map((x) => new Date(x).valueOf()); + times.reverse(); + const save = new Set(); + for (const type in counts) { + const count = counts[type]; + const length_ms = SNAPSHOT_INTERVALS_MS[type]; + + // Pick the first count newest snapshots at intervals of length + // length_ms milliseconds. + let n = 0, + i = 0, + last_tm = 0; + while (n < count && i < times.length) { + const tm = times[i]; + if (!last_tm || tm <= last_tm - length_ms) { + save.add(tm); + last_tm = tm; + n += 1; // found one more + } + i += 1; // move to next snapshot + } + } + return snapshots.filter((x) => !save.has(new Date(x).valueOf())); +} diff --git a/src/packages/file-server/btrfs/subvolume-bup.ts b/src/packages/file-server/btrfs/subvolume-bup.ts new file mode 100644 index 0000000000..21cbbff364 --- /dev/null +++ b/src/packages/file-server/btrfs/subvolume-bup.ts @@ -0,0 +1,192 @@ +/* + +BUP Architecture: + +There is a single global dedup'd backup archive stored in the btrfs filesystem. +Obviously, admins should rsync this regularly to a separate location as a genuine +backup strategy. + +NOTE: we use bup instead of btrfs send/recv ! + +Not used. Instead we will rely on bup (and snapshots of the underlying disk) for backups, since: + - much easier to check they are valid + - decoupled from any btrfs issues + - not tied to any specific filesystem at all + - easier to offsite via incremental rsync + - much more space efficient with *global* dedup and compression + - bup is really just git, which is much more proven than even btrfs + +The drawback is speed, but that can be managed. +*/ + +import { type DirectoryListingEntry } from "@cocalc/util/types"; +import { type Subvolume } from "./subvolume"; +import { sudo, parseBupTime } from "./util"; +import { join, normalize } from "path"; +import getLogger from "@cocalc/backend/logger"; + +const BUP_SNAPSHOT = "temp-bup-snapshot"; + +const logger = getLogger("file-server:btrfs:subvolume-bup"); + +export class SubvolumeBup { + constructor(private subvolume: Subvolume) {} + + // create a new bup backup + save = async ({ + // timeout used for bup index and bup save commands + timeout = 30 * 60 * 1000, + }: { timeout?: number } = {}) => { + if (await this.subvolume.snapshots.exists(BUP_SNAPSHOT)) { + logger.debug(`createBupBackup: deleting existing ${BUP_SNAPSHOT}`); + await this.subvolume.snapshots.delete(BUP_SNAPSHOT); + } + try { + logger.debug( + `createBackup: creating ${BUP_SNAPSHOT} to get a consistent backup`, + ); + await this.subvolume.snapshots.create(BUP_SNAPSHOT); + const target = this.subvolume.normalize( + this.subvolume.snapshots.path(BUP_SNAPSHOT), + ); + + logger.debug(`createBupBackup: indexing ${BUP_SNAPSHOT}`); + await sudo({ + command: "bup", + args: [ + "-d", + this.subvolume.filesystem.bup, + "index", + "--exclude", + join(target, ".snapshots"), + "-x", + target, + ], + timeout, + }); + + logger.debug(`createBackup: saving ${BUP_SNAPSHOT}`); + await sudo({ + command: "bup", + args: [ + "-d", + this.subvolume.filesystem.bup, + "save", + "--strip", + "-n", + this.subvolume.name, + target, + ], + timeout, + }); + } finally { + logger.debug(`createBupBackup: deleting temporary ${BUP_SNAPSHOT}`); + await this.subvolume.snapshots.delete(BUP_SNAPSHOT); + } + }; + + restore = async (path: string) => { + // path -- branch/revision/path/to/dir + if (path.startsWith("/")) { + path = path.slice(1); + } + path = normalize(path); + // ... but to avoid potential data loss, we make a snapshot before deleting it. + await this.subvolume.snapshots.create(); + const i = path.indexOf("/"); // remove the commit name + // remove the target we're about to restore + await this.subvolume.fs.rm(path.slice(i + 1), { recursive: true }); + await sudo({ + command: "bup", + args: [ + "-d", + this.subvolume.filesystem.bup, + "restore", + "-C", + this.subvolume.path, + join(`/${this.subvolume.name}`, path), + "--quiet", + ], + }); + }; + + ls = async (path: string = ""): Promise => { + if (!path) { + const { stdout } = await sudo({ + command: "bup", + args: ["-d", this.subvolume.filesystem.bup, "ls", this.subvolume.name], + }); + const v: DirectoryListingEntry[] = []; + let newest = 0; + for (const x of stdout.trim().split("\n")) { + const name = x.split(" ").slice(-1)[0]; + if (name == "latest") { + continue; + } + const mtime = parseBupTime(name).valueOf() / 1000; + newest = Math.max(mtime, newest); + v.push({ name, isdir: true, mtime }); + } + if (v.length > 0) { + v.push({ name: "latest", isdir: true, mtime: newest }); + } + return v; + } + + path = normalize(path); + const { stdout } = await sudo({ + command: "bup", + args: [ + "-d", + this.subvolume.filesystem.bup, + "ls", + "--almost-all", + "--file-type", + "-l", + join(`/${this.subvolume.name}`, path), + ], + }); + const v: DirectoryListingEntry[] = []; + for (const x of stdout.split("\n")) { + // [-rw-------","6b851643360e435eb87ef9a6ab64a8b1/6b851643360e435eb87ef9a6ab64a8b1","5","2025-07-15","06:12","a.txt"] + const w = x.split(/\s+/); + if (w.length >= 6) { + let isdir, name; + if (w[5].endsWith("@") || w[5].endsWith("=") || w[5].endsWith("|")) { + w[5] = w[5].slice(0, -1); + } + if (w[5].endsWith("/")) { + isdir = true; + name = w[5].slice(0, -1); + } else { + name = w[5]; + isdir = false; + } + const size = parseInt(w[2]); + const mtime = new Date(w[3] + "T" + w[4]).valueOf() / 1000; + v.push({ name, size, mtime, isdir }); + } + } + return v; + }; + + prune = async ({ + dailies = "1w", + monthlies = "4m", + all = "3d", + }: { dailies?: string; monthlies?: string; all?: string } = {}) => { + await sudo({ + command: "bup", + args: [ + "-d", + this.subvolume.filesystem.bup, + "prune-older", + `--keep-dailies-for=${dailies}`, + `--keep-monthlies-for=${monthlies}`, + `--keep-all-for=${all}`, + "--unsafe", + this.subvolume.name, + ], + }); + }; +} diff --git a/src/packages/file-server/btrfs/subvolume-fs.ts b/src/packages/file-server/btrfs/subvolume-fs.ts new file mode 100644 index 0000000000..f1f2dd3677 --- /dev/null +++ b/src/packages/file-server/btrfs/subvolume-fs.ts @@ -0,0 +1,150 @@ +import { + appendFile, + chmod, + cp, + copyFile, + link, + readFile, + realpath, + rename, + rm, + rmdir, + mkdir, + stat, + symlink, + truncate, + writeFile, + unlink, + utimes, + watch, +} from "node:fs/promises"; +import { exists } from "@cocalc/backend/misc/async-utils-node"; +import { type DirectoryListingEntry } from "@cocalc/util/types"; +import getListing from "@cocalc/backend/get-listing"; +import { type Subvolume } from "./subvolume"; +import { isdir, sudo } from "./util"; + +export class SubvolumeFilesystem { + constructor(private subvolume: Subvolume) {} + + private normalize = this.subvolume.normalize; + + ls = async ( + path: string, + { hidden, limit }: { hidden?: boolean; limit?: number } = {}, + ): Promise => { + return await getListing(this.normalize(path), hidden, { + limit, + home: "/", + }); + }; + + readFile = async (path: string, encoding?: any): Promise => { + return await readFile(this.normalize(path), encoding); + }; + + writeFile = async (path: string, data: string | Buffer) => { + return await writeFile(this.normalize(path), data); + }; + + appendFile = async (path: string, data: string | Buffer, encoding?) => { + return await appendFile(this.normalize(path), data, encoding); + }; + + unlink = async (path: string) => { + await unlink(this.normalize(path)); + }; + + stat = async (path: string) => { + return await stat(this.normalize(path)); + }; + + exists = async (path: string) => { + return await exists(this.normalize(path)); + }; + + // hard link + link = async (existingPath: string, newPath: string) => { + return await link(this.normalize(existingPath), this.normalize(newPath)); + }; + + symlink = async (target: string, path: string) => { + return await symlink(this.normalize(target), this.normalize(path)); + }; + + realpath = async (path: string) => { + const x = await realpath(this.normalize(path)); + return x.slice(this.subvolume.path.length + 1); + }; + + rename = async (oldPath: string, newPath: string) => { + await rename(this.normalize(oldPath), this.normalize(newPath)); + }; + + utimes = async ( + path: string, + atime: number | string | Date, + mtime: number | string | Date, + ) => { + await utimes(this.normalize(path), atime, mtime); + }; + + watch = (filename: string, options?) => { + return watch(this.normalize(filename), options); + }; + + truncate = async (path: string, len?: number) => { + await truncate(this.normalize(path), len); + }; + + copyFile = async (src: string, dest: string) => { + await copyFile(this.normalize(src), this.normalize(dest)); + }; + + cp = async (src: string, dest: string, options?) => { + await cp(this.normalize(src), this.normalize(dest), options); + }; + + chmod = async (path: string, mode: string | number) => { + await chmod(this.normalize(path), mode); + }; + + mkdir = async (path: string, options?) => { + await mkdir(this.normalize(path), options); + }; + + rsync = async ({ + src, + target, + args = ["-axH"], + timeout = 5 * 60 * 1000, + }: { + src: string; + target: string; + args?: string[]; + timeout?: number; + }): Promise<{ stdout: string; stderr: string; exit_code: number }> => { + let srcPath = this.normalize(src); + let targetPath = this.normalize(target); + if (!srcPath.endsWith("/") && (await isdir(srcPath))) { + srcPath += "/"; + if (!targetPath.endsWith("/")) { + targetPath += "/"; + } + } + return await sudo({ + command: "rsync", + args: [...args, srcPath, targetPath], + err_on_exit: false, + timeout: timeout / 1000, + }); + }; + + rmdir = async (path: string, options?) => { + await rmdir(this.normalize(path), options); + }; + + rm = async (path: string, options?) => { + await rm(this.normalize(path), options); + }; +} diff --git a/src/packages/file-server/btrfs/subvolume-quota.ts b/src/packages/file-server/btrfs/subvolume-quota.ts new file mode 100644 index 0000000000..b4288cfc57 --- /dev/null +++ b/src/packages/file-server/btrfs/subvolume-quota.ts @@ -0,0 +1,86 @@ +import { type Subvolume } from "./subvolume"; +import { btrfs } from "./util"; +import getLogger from "@cocalc/backend/logger"; + +const logger = getLogger("file-server:btrfs:subvolume-quota"); + +export class SubvolumeQuota { + constructor(public subvolume: Subvolume) {} + + private qgroup = async () => { + const { stdout } = await btrfs({ + verbose: false, + args: ["--format=json", "qgroup", "show", "-reF", this.subvolume.path], + }); + const x = JSON.parse(stdout); + return x["qgroup-show"][0]; + }; + + get = async (): Promise<{ + size: number; + used: number; + }> => { + let { max_referenced: size, referenced: used } = await this.qgroup(); + if (size == "none") { + size = null; + } + return { + used, + size, + }; + }; + + set = async (size: string | number) => { + if (!size) { + throw Error("size must be specified"); + } + logger.debug("setQuota ", this.subvolume.path, size); + await btrfs({ + args: ["qgroup", "limit", `${size}`, this.subvolume.path], + }); + }; + + du = async () => { + return await btrfs({ + args: ["filesystem", "du", "-s", this.subvolume.path], + }); + }; + + usage = async (): Promise<{ + // used and free in bytes + used: number; + free: number; + size: number; + }> => { + const { stdout } = await btrfs({ + args: ["filesystem", "usage", "-b", this.subvolume.path], + }); + let used: number = -1; + let free: number = -1; + let size: number = -1; + for (const x of stdout.split("\n")) { + if (used == -1) { + const i = x.indexOf("Used:"); + if (i != -1) { + used = parseInt(x.split(":")[1].trim()); + continue; + } + } + if (free == -1) { + const i = x.indexOf("Free (statfs, df):"); + if (i != -1) { + free = parseInt(x.split(":")[1].trim()); + continue; + } + } + if (size == -1) { + const i = x.indexOf("Device size:"); + if (i != -1) { + size = parseInt(x.split(":")[1].trim()); + continue; + } + } + } + return { used, free, size }; + }; +} diff --git a/src/packages/file-server/btrfs/subvolume-snapshots.ts b/src/packages/file-server/btrfs/subvolume-snapshots.ts new file mode 100644 index 0000000000..ffe71fe6fc --- /dev/null +++ b/src/packages/file-server/btrfs/subvolume-snapshots.ts @@ -0,0 +1,111 @@ +import { type Subvolume } from "./subvolume"; +import { btrfs } from "./util"; +import getLogger from "@cocalc/backend/logger"; +import { join } from "path"; +import { type DirectoryListingEntry } from "@cocalc/util/types"; +import { SnapshotCounts, updateRollingSnapshots } from "./snapshots"; + +export const SNAPSHOTS = ".snapshots"; +const logger = getLogger("file-server:btrfs:subvolume-snapshots"); + +export class SubvolumeSnapshots { + public readonly snapshotsDir: string; + + constructor(public subvolume: Subvolume) { + this.snapshotsDir = join(this.subvolume.path, SNAPSHOTS); + } + + path = (snapshot?: string, ...segments) => { + if (!snapshot) { + return SNAPSHOTS; + } + return join(SNAPSHOTS, snapshot, ...segments); + }; + + private makeSnapshotsDir = async () => { + if (await this.subvolume.fs.exists(SNAPSHOTS)) { + return; + } + await this.subvolume.fs.mkdir(SNAPSHOTS); + await this.subvolume.fs.chmod(SNAPSHOTS, "0550"); + }; + + create = async (name?: string) => { + if (name?.startsWith(".")) { + throw Error("snapshot name must not start with '.'"); + } + name ??= new Date().toISOString(); + logger.debug("create", { name, subvolume: this.subvolume.name }); + await this.makeSnapshotsDir(); + await btrfs({ + args: [ + "subvolume", + "snapshot", + "-r", + this.subvolume.path, + join(this.snapshotsDir, name), + ], + }); + }; + + ls = async (): Promise => { + await this.makeSnapshotsDir(); + return await this.subvolume.fs.ls(SNAPSHOTS, { hidden: false }); + }; + + lock = async (name: string) => { + if (await this.subvolume.fs.exists(this.path(name))) { + this.subvolume.fs.writeFile(this.path(`.${name}.lock`), ""); + } else { + throw Error(`snapshot ${name} does not exist`); + } + }; + + unlock = async (name: string) => { + await this.subvolume.fs.rm(this.path(`.${name}.lock`)); + }; + + exists = async (name: string) => { + return await this.subvolume.fs.exists(this.path(name)); + }; + + delete = async (name) => { + if (await this.subvolume.fs.exists(this.path(`.${name}.lock`))) { + throw Error(`snapshot ${name} is locked`); + } + await btrfs({ + args: ["subvolume", "delete", join(this.snapshotsDir, name)], + }); + }; + + // update the rolling snapshots schedule + update = async (counts?: Partial) => { + return await updateRollingSnapshots({ snapshots: this, counts }); + }; + + // has newly written changes since last snapshot + hasUnsavedChanges = async (): Promise => { + const s = await this.ls(); + if (s.length == 0) { + // more than just the SNAPSHOTS directory? + const v = await this.subvolume.fs.ls("", { hidden: true }); + if (v.length == 0 || (v.length == 1 && v[0].name == SNAPSHOTS)) { + return false; + } + return true; + } + const pathGen = await getGeneration(this.subvolume.path); + const snapGen = await getGeneration( + join(this.snapshotsDir, s[s.length - 1].name), + ); + return snapGen < pathGen; + }; +} + +async function getGeneration(path: string): Promise { + const { stdout } = await btrfs({ + args: ["subvolume", "show", path], + verbose: false, + }); + return parseInt(stdout.split("Generation:")[1].split("\n")[0].trim()); +} diff --git a/src/packages/file-server/btrfs/subvolume.ts b/src/packages/file-server/btrfs/subvolume.ts new file mode 100644 index 0000000000..3f77abd9c4 --- /dev/null +++ b/src/packages/file-server/btrfs/subvolume.ts @@ -0,0 +1,102 @@ +/* +A subvolume +*/ + +import { type Filesystem, DEFAULT_SUBVOLUME_SIZE } from "./filesystem"; +import refCache from "@cocalc/util/refcache"; +import { sudo } from "./util"; +import { join, normalize } from "path"; +import { SubvolumeFilesystem } from "./subvolume-fs"; +import { SubvolumeBup } from "./subvolume-bup"; +import { SubvolumeSnapshots } from "./subvolume-snapshots"; +import { SubvolumeQuota } from "./subvolume-quota"; +import { exists } from "@cocalc/backend/misc/async-utils-node"; + +import getLogger from "@cocalc/backend/logger"; + +const logger = getLogger("file-server:btrfs:subvolume"); + +interface Options { + filesystem: Filesystem; + name: string; +} + +export class Subvolume { + public readonly name: string; + + public readonly filesystem: Filesystem; + public readonly path: string; + public readonly fs: SubvolumeFilesystem; + public readonly bup: SubvolumeBup; + public readonly snapshots: SubvolumeSnapshots; + public readonly quota: SubvolumeQuota; + + constructor({ filesystem, name }: Options) { + this.filesystem = filesystem; + this.name = name; + this.path = join(filesystem.opts.mount, name); + this.fs = new SubvolumeFilesystem(this); + this.bup = new SubvolumeBup(this); + this.snapshots = new SubvolumeSnapshots(this); + this.quota = new SubvolumeQuota(this); + } + + init = async () => { + if (!(await exists(this.path))) { + logger.debug(`creating ${this.name} at ${this.path}`); + await sudo({ + command: "btrfs", + args: ["subvolume", "create", this.path], + }); + await this.chown(this.path); + await this.quota.set( + this.filesystem.opts.defaultSize ?? DEFAULT_SUBVOLUME_SIZE, + ); + } + }; + + close = () => { + // @ts-ignore + delete this.filesystem; + // @ts-ignore + delete this.name; + // @ts-ignore + delete this.path; + // @ts-ignore + delete this.snapshotsDir; + for (const sub of ["fs", "bup", "snapshots", "quota"]) { + this[sub].close?.(); + delete this[sub]; + } + }; + + private chown = async (path: string) => { + await sudo({ + command: "chown", + args: [`${process.getuid?.() ?? 0}:${process.getgid?.() ?? 0}`, path], + }); + }; + + // this should provide a path that is guaranteed to be + // inside this.path on the filesystem or throw error + // [ ] TODO: not sure if the code here is sufficient!! + normalize = (path: string) => { + return join(this.path, normalize(path)); + }; +} + +const cache = refCache({ + name: "btrfs-subvolumes", + createKey: ({ name }) => name, + createObject: async (options: Options) => { + const subvolume = new Subvolume(options); + await subvolume.init(); + return subvolume; + }, +}); + +export async function subvolume( + options: Options & { noCache?: boolean }, +): Promise { + return await cache(options); +} diff --git a/src/packages/file-server/btrfs/subvolumes.ts b/src/packages/file-server/btrfs/subvolumes.ts new file mode 100644 index 0000000000..0f8da1468f --- /dev/null +++ b/src/packages/file-server/btrfs/subvolumes.ts @@ -0,0 +1,115 @@ +import { type Filesystem } from "./filesystem"; +import { subvolume, type Subvolume } from "./subvolume"; +import getLogger from "@cocalc/backend/logger"; +import { SNAPSHOTS } from "./subvolume-snapshots"; +import { exists } from "@cocalc/backend/misc/async-utils-node"; +import { join, normalize } from "path"; +import { btrfs, isdir } from "./util"; +import { chmod, rename, rm } from "node:fs/promises"; +import { executeCode } from "@cocalc/backend/execute-code"; + +const RESERVED = new Set(["bup", SNAPSHOTS]); + +const logger = getLogger("file-server:btrfs:subvolumes"); + +export class Subvolumes { + constructor(public filesystem: Filesystem) {} + + get = async (name: string): Promise => { + if (RESERVED.has(name)) { + throw Error(`${name} is reserved`); + } + return await subvolume({ filesystem: this.filesystem, name }); + }; + + // create a subvolume by cloning an existing one. + clone = async (source: string, dest: string) => { + logger.debug("clone ", { source, dest }); + if (RESERVED.has(dest)) { + throw Error(`${dest} is reserved`); + } + if (!(await exists(join(this.filesystem.opts.mount, source)))) { + throw Error(`subvolume ${source} does not exist`); + } + if (await exists(join(this.filesystem.opts.mount, dest))) { + throw Error(`subvolume ${dest} already exists`); + } + await btrfs({ + args: [ + "subvolume", + "snapshot", + join(this.filesystem.opts.mount, source), + join(this.filesystem.opts.mount, source, dest), + ], + }); + await rename( + join(this.filesystem.opts.mount, source, dest), + join(this.filesystem.opts.mount, dest), + ); + const snapdir = join(this.filesystem.opts.mount, dest, SNAPSHOTS); + if (await exists(snapdir)) { + await chmod(snapdir, "0700"); + await rm(snapdir, { + recursive: true, + force: true, + }); + } + const src = await this.get(source); + const dst = await this.get(dest); + const { size } = await src.quota.get(); + if (size) { + await dst.quota.set(size); + } + return dst; + }; + + delete = async (name: string) => { + await btrfs({ + args: ["subvolume", "delete", join(this.filesystem.opts.mount, name)], + }); + }; + + list = async (): Promise => { + const { stdout } = await btrfs({ + args: ["subvolume", "list", this.filesystem.opts.mount], + }); + return stdout + .split("\n") + .map((x) => x.split(" ").slice(-1)[0]) + .filter((x) => x) + .sort(); + }; + + rsync = async ({ + src, + target, + args = ["-axH"], + timeout = 5 * 60 * 1000, + }: { + src: string; + target: string; + args?: string[]; + timeout?: number; + }): Promise<{ stdout: string; stderr: string; exit_code: number }> => { + let srcPath = normalize(join(this.filesystem.opts.mount, src)); + if (!srcPath.startsWith(this.filesystem.opts.mount)) { + throw Error("suspicious source"); + } + let targetPath = normalize(join(this.filesystem.opts.mount, target)); + if (!targetPath.startsWith(this.filesystem.opts.mount)) { + throw Error("suspicious target"); + } + if (!srcPath.endsWith("/") && (await isdir(srcPath))) { + srcPath += "/"; + if (!targetPath.endsWith("/")) { + targetPath += "/"; + } + } + return await executeCode({ + command: "rsync", + args: [...args, srcPath, targetPath], + err_on_exit: false, + timeout: timeout / 1000, + }); + }; +} diff --git a/src/packages/file-server/btrfs/test/filesystem-stress.test.ts b/src/packages/file-server/btrfs/test/filesystem-stress.test.ts new file mode 100644 index 0000000000..a2e1de9531 --- /dev/null +++ b/src/packages/file-server/btrfs/test/filesystem-stress.test.ts @@ -0,0 +1,95 @@ +import { before, after, fs } from "./setup"; + +beforeAll(before); + +const DEBUG = false; +const log = DEBUG ? console.log : (..._args) => {}; + +describe("stress operations with subvolumes", () => { + const count1 = 10; + it(`create ${count1} subvolumes in serial`, async () => { + const t = Date.now(); + for (let i = 0; i < count1; i++) { + await fs.subvolumes.get(`${i}`); + } + log( + `created ${Math.round((count1 / (Date.now() - t)) * 1000)} subvolumes per second serial`, + ); + }); + + it("list them and confirm", async () => { + const v = await fs.subvolumes.list(); + expect(v.length).toBe(count1); + }); + + let count2 = 10; + it(`create ${count2} subvolumes in parallel`, async () => { + const v: any[] = []; + const t = Date.now(); + for (let i = 0; i < count2; i++) { + v.push(fs.subvolumes.get(`p-${i}`)); + } + await Promise.all(v); + log( + `created ${Math.round((count2 / (Date.now() - t)) * 1000)} subvolumes per second in parallel`, + ); + }); + + it("list them and confirm", async () => { + const v = await fs.subvolumes.list(); + expect(v.length).toBe(count1 + count2); + }); + + it("write a file to each volume", async () => { + for (const name of await fs.subvolumes.list()) { + const vol = await fs.subvolumes.get(name); + await vol.fs.writeFile("a.txt", "hi"); + } + }); + + it("clone the first group in serial", async () => { + const t = Date.now(); + for (let i = 0; i < count1; i++) { + await fs.subvolumes.clone(`${i}`, `clone-of-${i}`); + } + log( + `cloned ${Math.round((count1 / (Date.now() - t)) * 1000)} subvolumes per second serial`, + ); + }); + + it("clone the second group in parallel", async () => { + const t = Date.now(); + const v: any[] = []; + for (let i = 0; i < count2; i++) { + v.push(fs.subvolumes.clone(`p-${i}`, `clone-of-p-${i}`)); + } + await Promise.all(v); + log( + `cloned ${Math.round((count2 / (Date.now() - t)) * 1000)} subvolumes per second parallel`, + ); + }); + + it("delete the first batch serial", async () => { + const t = Date.now(); + for (let i = 0; i < count1; i++) { + await fs.subvolumes.delete(`${i}`); + } + log( + `deleted ${Math.round((count1 / (Date.now() - t)) * 1000)} subvolumes per second serial`, + ); + }); + + it("delete the second batch in parallel", async () => { + const v: any[] = []; + const t = Date.now(); + for (let i = 0; i < count2; i++) { + v.push(fs.subvolumes.delete(`p-${i}`)); + } + await Promise.all(v); + log( + `deleted ${Math.round((count2 / (Date.now() - t)) * 1000)} subvolumes per second in parallel`, + ); + }); +}); + +afterAll(after); diff --git a/src/packages/file-server/btrfs/test/filesystem.test.ts b/src/packages/file-server/btrfs/test/filesystem.test.ts new file mode 100644 index 0000000000..673980af89 --- /dev/null +++ b/src/packages/file-server/btrfs/test/filesystem.test.ts @@ -0,0 +1,105 @@ +import { before, after, fs } from "./setup"; +import { isValidUUID } from "@cocalc/util/misc"; + +beforeAll(before); + +describe("some basic tests", () => { + it("gets basic info", async () => { + const info = await fs.info(); + expect(info).not.toEqual(null); + expect(info.Name).toBe(""); + expect(isValidUUID(info.UUID)).toBe(true); + const creation = new Date(info["Creation time"]); + expect(Math.abs(creation.valueOf() - Date.now())).toBeLessThan(15000); + expect(info["Snapshot(s)"]).toBe(""); + }); + + it("lists the subvolumes (there are none)", async () => { + expect(await fs.subvolumes.list()).toEqual([]); + }); +}); + +describe("operations with subvolumes", () => { + it("can't use a reserved subvolume name", async () => { + expect(async () => { + await fs.subvolumes.get("bup"); + }).rejects.toThrow("is reserved"); + }); + + it("creates a subvolume", async () => { + const vol = await fs.subvolumes.get("cocalc"); + expect(vol.name).toBe("cocalc"); + // it has no snapshots + expect(await vol.snapshots.ls()).toEqual([]); + }); + + it("our subvolume is in the list", async () => { + expect(await fs.subvolumes.list()).toEqual(["cocalc"]); + }); + + it("create another two subvolumes", async () => { + await fs.subvolumes.get("sagemath"); + await fs.subvolumes.get("a-math"); + // list is sorted: + expect(await fs.subvolumes.list()).toEqual([ + "a-math", + "cocalc", + "sagemath", + ]); + }); + + it("delete a subvolume", async () => { + await fs.subvolumes.delete("a-math"); + expect(await fs.subvolumes.list()).toEqual(["cocalc", "sagemath"]); + }); + + it("clone a subvolume", async () => { + await fs.subvolumes.clone("sagemath", "cython"); + expect(await fs.subvolumes.list()).toEqual([ + "cocalc", + "cython", + "sagemath", + ]); + }); + + it("rsync from one volume to another", async () => { + await fs.subvolumes.rsync({ src: "sagemath", target: "cython" }); + }); + + it("rsync an actual file", async () => { + const sagemath = await fs.subvolumes.get("sagemath"); + const cython = await fs.subvolumes.get("cython"); + await sagemath.fs.writeFile("README.md", "hi"); + await fs.subvolumes.rsync({ src: "sagemath", target: "cython" }); + const copy = await cython.fs.readFile("README.md", "utf8"); + expect(copy).toEqual("hi"); + }); + + it("clone a subvolume with contents", async () => { + await fs.subvolumes.clone("cython", "pyrex"); + const pyrex = await fs.subvolumes.get("pyrex"); + const clone = await pyrex.fs.readFile("README.md", "utf8"); + expect(clone).toEqual("hi"); + }); +}); + +describe("clone of a subvolume with snapshots should have no snapshots", () => { + it("creates a subvolume, a file, and a snapshot", async () => { + const x = await fs.subvolumes.get("my-volume"); + await x.fs.writeFile("abc.txt", "hi"); + await x.snapshots.create("my-snap"); + }); + + it("clones my-volume", async () => { + await fs.subvolumes.clone("my-volume", "my-clone"); + }); + + it("clone has no snapshots", async () => { + const clone = await fs.subvolumes.get("my-clone"); + expect(await clone.fs.readFile("abc.txt", "utf8")).toEqual("hi"); + expect(await clone.snapshots.ls()).toEqual([]); + await clone.snapshots.create("my-clone-snap"); + }); +}); + +afterAll(after); diff --git a/src/packages/file-server/btrfs/test/setup.ts b/src/packages/file-server/btrfs/test/setup.ts new file mode 100644 index 0000000000..c9736c8b79 --- /dev/null +++ b/src/packages/file-server/btrfs/test/setup.ts @@ -0,0 +1,68 @@ +import { + filesystem, + type Filesystem, +} from "@cocalc/file-server/btrfs/filesystem"; +import { chmod, mkdtemp, mkdir, rm, stat } from "node:fs/promises"; +import { tmpdir } from "node:os"; +import { join } from "path"; +import { until } from "@cocalc/util/async-utils"; +import { sudo } from "../util"; +export { sudo }; +export { delay } from "awaiting"; + +export let fs: Filesystem; +let tempDir; + +const TEMP_PREFIX = "cocalc-test-btrfs-"; + +async function ensureMoreLoops() { + // to run tests, this is helpful + //for i in $(seq 8 63); do sudo mknod -m660 /dev/loop$i b 7 $i; sudo chown root:disk /dev/loop$i; done + for (let i = 0; i < 64; i++) { + try { + await stat(`/dev/loop${i}`); + continue; + } catch {} + try { + // also try/catch this because ensureMoreLoops happens in parallel many times at once... + await sudo({ + command: "mknod", + args: ["-m660", `/dev/loop${i}`, "b", "7", `${i}`], + }); + } catch {} + await sudo({ command: "chown", args: ["root:disk", `/dev/loop${i}`] }); + } +} + +export async function before() { + try { + const command = `umount ${join(tmpdir(), TEMP_PREFIX)}*/mnt`; + // attempt to unmount any mounts left from previous runs. + // TODO: this could impact runs in parallel + await sudo({ command, bash: true }); + } catch {} + await ensureMoreLoops(); + tempDir = await mkdtemp(join(tmpdir(), TEMP_PREFIX)); + // Set world read/write/execute + await chmod(tempDir, 0o777); + const mount = join(tempDir, "mnt"); + await mkdir(mount); + await chmod(mount, 0o777); + fs = await filesystem({ + device: join(tempDir, "btrfs.img"), + formatIfNeeded: true, + mount: join(tempDir, "mnt"), + }); +} + +export async function after() { + await until(async () => { + try { + await fs.unmount(); + return true; + } catch { + return false; + } + }); + await rm(tempDir, { force: true, recursive: true }); +} diff --git a/src/packages/file-server/btrfs/test/subvolume-stress.test.ts b/src/packages/file-server/btrfs/test/subvolume-stress.test.ts new file mode 100644 index 0000000000..29bc048e69 --- /dev/null +++ b/src/packages/file-server/btrfs/test/subvolume-stress.test.ts @@ -0,0 +1,87 @@ +import { before, after, fs } from "./setup"; +import { mkdir, writeFile } from "fs/promises"; +import { join } from "path"; +import { type Subvolume } from "../subvolume"; + +const DEBUG = false; +const log = DEBUG ? console.log : (..._args) => {}; + +const numSnapshots = 25; +const numFiles = 1000; + +beforeAll(before); + +describe(`stress test creating ${numSnapshots} snapshots`, () => { + let vol: Subvolume; + it("creates a volume and write a file to it", async () => { + vol = await fs.subvolumes.get("stress"); + }); + + it(`create file and snapshot the volume ${numSnapshots} times`, async () => { + const snaps: string[] = []; + const start = Date.now(); + for (let i = 0; i < numSnapshots; i++) { + await writeFile(join(vol.path, `${i}.txt`), "world"); + await vol.snapshots.create(`snap${i}`); + snaps.push(`snap${i}`); + } + log( + `created ${Math.round((numSnapshots / (Date.now() - start)) * 1000)} snapshots per second in serial`, + ); + snaps.sort(); + expect((await vol.snapshots.ls()).map(({ name }) => name).sort()).toEqual( + snaps.sort(), + ); + }); + + it(`delete our ${numSnapshots} snapshots`, async () => { + for (let i = 0; i < numSnapshots; i++) { + await vol.snapshots.delete(`snap${i}`); + } + expect(await vol.snapshots.ls()).toEqual([]); + }); +}); + +describe(`create ${numFiles} files`, () => { + let vol: Subvolume; + it("creates a volume", async () => { + vol = await fs.subvolumes.get("many-files"); + }); + + it(`creates ${numFiles} files`, async () => { + const names: string[] = []; + const start = Date.now(); + for (let i = 0; i < numFiles; i++) { + await writeFile(join(vol.path, `${i}`), "world"); + names.push(`${i}`); + } + log( + `created ${Math.round((numFiles / (Date.now() - start)) * 1000)} files per second in serial`, + ); + const v = await vol.fs.ls(""); + const w = v.map(({ name }) => name); + expect(w.sort()).toEqual(names.sort()); + }); + + it(`creates ${numFiles} files in parallel`, async () => { + await mkdir(join(vol.path, "p")); + const names: string[] = []; + const start = Date.now(); + const z: any[] = []; + for (let i = 0; i < numFiles; i++) { + z.push(writeFile(join(vol.path, `p/${i}`), "world")); + names.push(`${i}`); + } + await Promise.all(z); + log( + `created ${Math.round((numFiles / (Date.now() - start)) * 1000)} files per second in parallel`, + ); + const t0 = Date.now(); + const v = await vol.fs.ls("p"); + log("get listing of files took", Date.now() - t0, "ms"); + const w = v.map(({ name }) => name); + expect(w.sort()).toEqual(names.sort()); + }); +}); + +afterAll(after); diff --git a/src/packages/file-server/btrfs/test/subvolume.test.ts b/src/packages/file-server/btrfs/test/subvolume.test.ts new file mode 100644 index 0000000000..e586cc656b --- /dev/null +++ b/src/packages/file-server/btrfs/test/subvolume.test.ts @@ -0,0 +1,296 @@ +import { before, after, fs, sudo } from "./setup"; +import { mkdir } from "fs/promises"; +import { join } from "path"; +import { wait } from "@cocalc/backend/conat/test/util"; +import { randomBytes } from "crypto"; +import { type Subvolume } from "../subvolume"; + +beforeAll(before); + +describe("setting and getting quota of a subvolume", () => { + let vol: Subvolume; + it("set the quota of a subvolume to 5 M", async () => { + vol = await fs.subvolumes.get("q"); + await vol.quota.set("5M"); + + const { size, used } = await vol.quota.get(); + expect(size).toBe(5 * 1024 * 1024); + expect(used).toBe(0); + }); + + it("get directory listing", async () => { + const v = await vol.fs.ls(""); + expect(v).toEqual([]); + }); + + it("write a file and check usage goes up", async () => { + const buf = randomBytes(4 * 1024 * 1024); + await vol.fs.writeFile("buf", buf); + await wait({ + until: async () => { + await sudo({ command: "sync" }); + const { used } = await vol.quota.usage(); + return used > 0; + }, + }); + const { used } = await vol.quota.usage(); + expect(used).toBeGreaterThan(0); + + const v = await vol.fs.ls(""); + // size is potentially random, reflecting compression + expect(v).toEqual([{ name: "buf", mtime: v[0].mtime, size: v[0].size }]); + }); + + it("fail to write a 50MB file (due to quota)", async () => { + const buf2 = randomBytes(50 * 1024 * 1024); + expect(async () => { + await vol.fs.writeFile("buf2", buf2); + }).rejects.toThrow("write"); + }); +}); + +describe("the filesystem operations", () => { + let vol: Subvolume; + + it("creates a volume and get empty listing", async () => { + vol = await fs.subvolumes.get("fs"); + expect(await vol.fs.ls("")).toEqual([]); + }); + + it("error listing non-existent path", async () => { + vol = await fs.subvolumes.get("fs"); + expect(async () => { + await vol.fs.ls("no-such-path"); + }).rejects.toThrow("ENOENT"); + }); + + it("creates a text file to it", async () => { + await vol.fs.writeFile("a.txt", "hello"); + const ls = await vol.fs.ls(""); + expect(ls).toEqual([{ name: "a.txt", mtime: ls[0].mtime, size: 5 }]); + }); + + it("read the file we just created as utf8", async () => { + expect(await vol.fs.readFile("a.txt", "utf8")).toEqual("hello"); + }); + + it("read the file we just created as a binary buffer", async () => { + expect(await vol.fs.readFile("a.txt")).toEqual(Buffer.from("hello")); + }); + + it("stat the file we just created", async () => { + const s = await vol.fs.stat("a.txt"); + expect(s.size).toBe(5); + expect(Math.abs(s.mtimeMs - Date.now())).toBeLessThan(60_000); + }); + + let origStat; + it("snapshot filesystem and see file is in snapshot", async () => { + await vol.snapshots.create("snap"); + const s = await vol.fs.ls(vol.snapshots.path("snap")); + expect(s).toEqual([{ name: "a.txt", mtime: s[0].mtime, size: 5 }]); + + const stat = await vol.fs.stat("a.txt"); + origStat = stat; + expect(stat.mtimeMs / 1000).toBeCloseTo(s[0].mtime ?? 0); + }); + + it("unlink (delete) our file", async () => { + await vol.fs.unlink("a.txt"); + expect(await vol.fs.ls("")).toEqual([]); + }); + + it("snapshot still exists", async () => { + expect(await vol.fs.exists(vol.snapshots.path("snap", "a.txt"))); + }); + + it("copy file from snapshot and note it has the same mode as before (so much nicer than what happens with zfs)", async () => { + await vol.fs.copyFile(vol.snapshots.path("snap", "a.txt"), "a.txt"); + const stat = await vol.fs.stat("a.txt"); + expect(stat.mode).toEqual(origStat.mode); + }); + + it("create and copy a folder", async () => { + await vol.fs.mkdir("my-folder"); + await vol.fs.writeFile("my-folder/foo.txt", "foo"); + await vol.fs.cp("my-folder", "folder2", { recursive: true }); + expect(await vol.fs.readFile("folder2/foo.txt", "utf8")).toEqual("foo"); + }); + + it("append to a file", async () => { + await vol.fs.writeFile("b.txt", "hell"); + await vol.fs.appendFile("b.txt", "-o"); + expect(await vol.fs.readFile("b.txt", "utf8")).toEqual("hell-o"); + }); + + it("make a file readonly, then change it back", async () => { + await vol.fs.writeFile("c.txt", "hi"); + await vol.fs.chmod("c.txt", "440"); + expect(async () => { + await vol.fs.appendFile("c.txt", " there"); + }).rejects.toThrow("EACCES"); + await vol.fs.chmod("c.txt", "660"); + await vol.fs.appendFile("c.txt", " there"); + }); + + it("realpath of a symlink", async () => { + await vol.fs.writeFile("real.txt", "i am real"); + await vol.fs.symlink("real.txt", "link.txt"); + expect(await vol.fs.realpath("link.txt")).toBe("real.txt"); + }); + + it("watch for changes", async () => { + await vol.fs.writeFile("w.txt", "hi"); + const ac = new AbortController(); + const { signal } = ac; + const watcher = vol.fs.watch("w.txt", { signal }); + vol.fs.appendFile("w.txt", " there"); + // @ts-ignore + const { value, done } = await watcher.next(); + expect(done).toBe(false); + expect(value).toEqual({ eventType: "change", filename: "w.txt" }); + ac.abort(); + + expect(async () => { + // @ts-ignore + await watcher.next(); + }).rejects.toThrow("aborted"); + }); + + it("rename a file", async () => { + await vol.fs.writeFile("old", "hi"); + await vol.fs.rename("old", "new"); + expect(await vol.fs.readFile("new", "utf8")).toEqual("hi"); + }); + + it("create and remove a directory", async () => { + await vol.fs.mkdir("path"); + await vol.fs.rmdir("path"); + }); + + it("create a directory recursively and remove", async () => { + await vol.fs.mkdir("path/to/stuff", { recursive: true }); + await vol.fs.rm("path", { recursive: true }); + }); +}); + +describe("test snapshots", () => { + let vol: Subvolume; + + it("creates a volume and write a file to it", async () => { + vol = await fs.subvolumes.get("snapper"); + expect(await vol.snapshots.hasUnsavedChanges()).toBe(false); + await vol.fs.writeFile("a.txt", "hello"); + expect(await vol.snapshots.hasUnsavedChanges()).toBe(true); + }); + + it("snapshot the volume", async () => { + expect(await vol.snapshots.ls()).toEqual([]); + await vol.snapshots.create("snap1"); + expect((await vol.snapshots.ls()).map((x) => x.name)).toEqual(["snap1"]); + expect(await vol.snapshots.hasUnsavedChanges()).toBe(false); + }); + + it("create a file see that we know there are unsaved changes", async () => { + await vol.fs.writeFile("b.txt", "world"); + await sudo({ command: "sync" }); + expect(await vol.snapshots.hasUnsavedChanges()).toBe(true); + }); + + it("delete our file, but then read it in a snapshot", async () => { + await vol.fs.unlink("a.txt"); + const b = await vol.fs.readFile( + vol.snapshots.path("snap1", "a.txt"), + "utf8", + ); + expect(b).toEqual("hello"); + }); + + it("verifies snapshot exists", async () => { + expect(await vol.snapshots.exists("snap1")).toBe(true); + expect(await vol.snapshots.exists("snap2")).toBe(false); + }); + + it("lock our snapshot and confirm it prevents deletion", async () => { + await vol.snapshots.lock("snap1"); + expect(async () => { + await vol.snapshots.delete("snap1"); + }).rejects.toThrow("locked"); + }); + + it("unlock our snapshot and delete it", async () => { + await vol.snapshots.unlock("snap1"); + await vol.snapshots.delete("snap1"); + expect(await vol.snapshots.exists("snap1")).toBe(false); + expect(await vol.snapshots.ls()).toEqual([]); + }); +}); + +describe.only("test bup backups", () => { + let vol: Subvolume; + it("creates a volume", async () => { + vol = await fs.subvolumes.get("bup-test"); + await vol.fs.writeFile("a.txt", "hello"); + }); + + it("create a bup backup", async () => { + await vol.bup.save(); + }); + + it("list bup backups of this vol -- there are 2, one for the date and 'latest'", async () => { + const v = await vol.bup.ls(); + expect(v.length).toBe(2); + const t = (v[0].mtime ?? 0) * 1000; + expect(Math.abs(t.valueOf() - Date.now())).toBeLessThan(10_000); + }); + + it("confirm a.txt is in our backup", async () => { + const x = await vol.bup.ls("latest"); + expect(x).toEqual([ + { name: "a.txt", size: 5, mtime: x[0].mtime, isdir: false }, + ]); + }); + + it("restore a.txt from our backup", async () => { + await vol.fs.writeFile("a.txt", "hello2"); + await vol.bup.restore("latest/a.txt"); + expect(await vol.fs.readFile("a.txt", "utf8")).toEqual("hello"); + }); + + it("prune bup backups does nothing since we have so few", async () => { + await vol.bup.prune(); + expect((await vol.bup.ls()).length).toBe(2); + }); + + it("add a directory and back up", async () => { + await mkdir(join(vol.path, "mydir")); + await vol.fs.writeFile(join("mydir", "file.txt"), "hello3"); + expect((await vol.fs.ls("mydir"))[0].name).toBe("file.txt"); + await vol.bup.save(); + const x = await vol.bup.ls("latest"); + expect(x).toEqual([ + { name: "a.txt", size: 5, mtime: x[0].mtime, isdir: false }, + { name: "mydir", size: 0, mtime: x[1].mtime, isdir: true }, + ]); + expect(Math.abs((x[0].mtime ?? 0) * 1000 - Date.now())).toBeLessThan( + 60_000, + ); + }); + + it("change file in the directory, then restore from backup whole dir", async () => { + await vol.fs.writeFile(join("mydir", "file.txt"), "changed"); + await vol.bup.restore("latest/mydir"); + expect(await vol.fs.readFile(join("mydir", "file.txt"), "utf8")).toEqual( + "hello3", + ); + }); + + it("most recent snapshot has a backup before the restore", async () => { + const s = await vol.snapshots.ls(); + const recent = s.slice(-1)[0].name; + const p = vol.snapshots.path(recent, "mydir", "file.txt"); + expect(await vol.fs.readFile(p, "utf8")).toEqual("changed"); + }); +}); + +afterAll(after); diff --git a/src/packages/file-server/btrfs/util.ts b/src/packages/file-server/btrfs/util.ts new file mode 100644 index 0000000000..b6408f8440 --- /dev/null +++ b/src/packages/file-server/btrfs/util.ts @@ -0,0 +1,65 @@ +import { + type ExecuteCodeOptions, + type ExecuteCodeOutput, +} from "@cocalc/util/types/execute-code"; +import { executeCode } from "@cocalc/backend/execute-code"; +import getLogger from "@cocalc/backend/logger"; +import { stat } from "node:fs/promises"; + +const logger = getLogger("file-server:storage:util"); + +const DEFAULT_EXEC_TIMEOUT_MS = 60 * 1000; + +export async function mkdirp(paths: string[]) { + if (paths.length == 0) return; + await sudo({ command: "mkdir", args: ["-p", ...paths] }); +} + +export async function sudo( + opts: ExecuteCodeOptions & { desc?: string }, +): Promise { + if (opts.verbose !== false && opts.desc) { + logger.debug("exec", opts.desc); + } + let command, args; + if (opts.bash) { + command = `sudo ${opts.command}`; + args = undefined; + } else { + command = "sudo"; + args = [opts.command, ...(opts.args ?? [])]; + } + return await executeCode({ + verbose: true, + timeout: DEFAULT_EXEC_TIMEOUT_MS / 1000, + ...opts, + command, + args, + }); +} + +export async function btrfs( + opts: Partial, +) { + return await sudo({ ...opts, command: "btrfs" }); +} + +export async function isdir(path: string) { + return (await stat(path)).isDirectory(); +} + +export function parseBupTime(s: string): Date { + const [year, month, day, time] = s.split("-"); + const hours = time.slice(0, 2); + const minutes = time.slice(2, 4); + const seconds = time.slice(4, 6); + + return new Date( + Number(year), + Number(month) - 1, // JS months are 0-based + Number(day), + Number(hours), + Number(minutes), + Number(seconds), + ); +} diff --git a/src/packages/file-server/package.json b/src/packages/file-server/package.json index 835280d7dc..58285c4bf6 100644 --- a/src/packages/file-server/package.json +++ b/src/packages/file-server/package.json @@ -3,32 +3,38 @@ "version": "1.0.0", "description": "CoCalc File Server", "exports": { - "./zfs": "./dist/zfs/index.js", - "./zfs/*": "./dist/zfs/*.js" + "./btrfs": "./dist/btrfs/index.js", + "./btrfs/*": "./dist/btrfs/*.js" }, "scripts": { "preinstall": "npx only-allow pnpm", "build": "pnpm exec tsc --build", "tsc": "pnpm exec tsc --watch --pretty --preserveWatchOutput", - "test": "pnpm exec jest --runInBand", - "depcheck": "pnpx depcheck" + "test": "pnpm exec jest", + "depcheck": "pnpx depcheck", + "clean": "rm -rf node_modules dist" }, - "files": ["dist/**", "README.md", "package.json"], + "files": [ + "dist/**", + "README.md", + "package.json" + ], "author": "SageMath, Inc.", - "keywords": ["utilities", "btrfs", "zfs", "cocalc"], + "keywords": [ + "utilities", + "btrfs", + "cocalc" + ], "license": "SEE LICENSE.md", "dependencies": { "@cocalc/backend": "workspace:*", - "@cocalc/conat": "workspace:*", "@cocalc/file-server": "workspace:*", "@cocalc/util": "workspace:*", - "awaiting": "^3.0.0", - "better-sqlite3": "^11.10.0", - "lodash": "^4.17.21" + "awaiting": "^3.0.0" }, "devDependencies": { - "@types/better-sqlite3": "^7.6.13", - "@types/lodash": "^4.14.202" + "@types/jest": "^29.5.14", + "@types/node": "^18.16.14" }, "repository": { "type": "git", diff --git a/src/packages/file-server/tsconfig.json b/src/packages/file-server/tsconfig.json index c2fcccc371..2e97efad86 100644 --- a/src/packages/file-server/tsconfig.json +++ b/src/packages/file-server/tsconfig.json @@ -1,9 +1,13 @@ { "extends": "../tsconfig.json", "compilerOptions": { + "types": ["node", "jest"], + "lib": ["es7"], "rootDir": "./", "outDir": "dist" }, "exclude": ["node_modules", "dist", "test"], - "references": [{ "path": "../util", "path": "../conat", "path": "../backend" }] + "references": [ + { "path": "../util", "path": "../conat", "path": "../backend" } + ] } diff --git a/src/packages/file-server/zfs/archive.ts b/src/packages/file-server/zfs/archive.ts deleted file mode 100644 index 70347e14c6..0000000000 --- a/src/packages/file-server/zfs/archive.ts +++ /dev/null @@ -1,236 +0,0 @@ -/* -Archiving and restore filesystems -*/ - -import { get, set } from "./db"; -import { createSnapshot, zfsGetSnapshots } from "./snapshots"; -import { - filesystemDataset, - filesystemArchivePath, - filesystemArchiveFilename, - filesystemDatasetTemp, - filesystemMountpoint, -} from "./names"; -import { exec } from "./util"; -import { - mountFilesystem, - unmountFilesystem, - zfsGetProperties, -} from "./properties"; -import { delay } from "awaiting"; -import { primaryKey, type PrimaryKey } from "./types"; -import { isEqual } from "lodash"; - -export async function dearchiveFilesystem( - opts: PrimaryKey & { - // called during dearchive with status updates: - progress?: (status: { - // a number between 0 and 100 indicating progress - progress: number; - // estimated number of seconds remaining - seconds_remaining?: number; - // how much of the total data we have de-archived - read?: number; - // total amount of data to de-archive - total?: number; - }) => void; - }, -) { - const start = Date.now(); - opts.progress?.({ progress: 0 }); - const pk = primaryKey(opts); - const filesystem = get(pk); - if (!filesystem.archived) { - throw Error("filesystem is not archived"); - } - const { used_by_dataset, used_by_snapshots } = filesystem; - const total = (used_by_dataset ?? 0) + (used_by_snapshots ?? 0); - const dataset = filesystemDataset(filesystem); - let done = false; - let progress = 0; - if (opts.progress && total > 0) { - (async () => { - const t0 = Date.now(); - let lastProgress = 0; - while (!done) { - await delay(750); - let x; - try { - x = await zfsGetProperties(dataset); - } catch { - // this is expected to fail, e.g., if filesystem doesn't exist yet. - } - if (done) { - return; - } - const read = x.used_by_dataset + x.used_by_snapshots; - progress = Math.min(100, Math.round((read * 100) / total)); - if (progress == lastProgress) { - continue; - } - lastProgress = progress; - let seconds_remaining: number | undefined = undefined; - if (progress > 0) { - const rate = (Date.now() - t0) / progress; - seconds_remaining = Math.ceil((rate * (100 - progress)) / 1000); - } - opts.progress?.({ progress, seconds_remaining, total, read }); - if (progress >= 100) { - break; - } - } - })(); - } - - // now we de-archive it: - const stream = filesystemArchiveFilename(filesystem); - await exec({ - verbose: true, - // have to use sudo sh -c because zfs recv only supports reading from stdin: - command: `sudo sh -c 'cat ${stream} | zfs recv ${dataset}'`, - what: { - ...pk, - desc: "de-archive a filesystem via zfs recv", - }, - }); - done = true; - if (progress < 100) { - opts.progress?.({ - progress: 100, - seconds_remaining: 0, - total, - read: total, - }); - } - await mountFilesystem(filesystem); - // mounting worked so remove the archive - await exec({ - command: "sudo", - args: ["rm", stream], - what: { - ...pk, - desc: "removing the stream during de-archive", - }, - }); - set({ ...pk, archived: false }); - return { milliseconds: Date.now() - start }; -} - -export async function archiveFilesystem(fs: PrimaryKey) { - const start = Date.now(); - const pk = primaryKey(fs); - const filesystem = get(pk); - if (filesystem.archived) { - throw Error("filesystem is already archived"); - } - // create or get most recent snapshot - const snapshot = await createSnapshot({ ...filesystem, ifChanged: true }); - // where archive of this filesystem goes: - const archive = filesystemArchivePath(filesystem); - const stream = filesystemArchiveFilename(filesystem); - await exec({ - command: "sudo", - args: ["mkdir", "-p", archive], - what: { ...pk, desc: "make archive target directory" }, - }); - - await mountFilesystem(filesystem); - const find = await hashFileTree({ - verbose: true, - path: filesystemMountpoint(filesystem), - what: { ...pk, desc: "getting sha1sum of file listing" }, - }); - // mountpoint will be used for test below, and also no point in archiving - // if we can't even unmount filesystem - await unmountFilesystem(filesystem); - - // make *full* zfs send - await exec({ - verbose: true, - // have to use sudo sh -c because zfs send only supports writing to stdout: - command: `sudo sh -c 'zfs send -e -c -R ${filesystemDataset(filesystem)}@${snapshot} > ${stream}'`, - what: { - ...pk, - desc: "zfs send of full filesystem dataset to archive it", - }, - }); - - // verify that the entire send stream is valid - const temp = filesystemDatasetTemp(filesystem); - try { - await exec({ - verbose: true, - // have to use sudo sh -c because zfs send only supports writing to stdout: - command: `sudo sh -c 'cat ${stream} | zfs recv ${temp}'`, - what: { - ...pk, - desc: "verify the archive zfs send is valid", - }, - }); - // inspect the list of all files, and verify that it is identical (has same sha1sum). - // I think this should be not necessary because the above read didn't fail, and there - // are supposed to be checksums. But I also think there are some ways to corrupt a - // stream so it reads in as empty (say), so this will definitely catch that. - const findtest = await hashFileTree({ - verbose: true, - path: filesystemMountpoint(filesystem), // same mountpoint due to being part of recv data - what: { ...pk, desc: "getting sha1sum of file listing" }, - }); - if (findtest != find) { - throw Error( - "files in archived filesystem do not match. Refusing to archive!", - ); - } - // Inspect list of snapshots, and verify they are identical as well. This is another - // good consistency check that the stream works. - const snapshots = await zfsGetSnapshots(temp); - if (!isEqual(snapshots, filesystem.snapshots)) { - throw Error( - "snapshots in archived filesystem do not match. Refusing to archive!", - ); - } - } finally { - // destroy the temporary filesystem - await exec({ - verbose: true, - command: "sudo", - args: ["zfs", "destroy", "-r", temp], - what: { - ...pk, - desc: "destroying temporary filesystem dataset used for testing archive stream", - }, - }); - } - - // destroy dataset - await exec({ - verbose: true, - command: "sudo", - args: ["zfs", "destroy", "-r", filesystemDataset(filesystem)], - what: { ...pk, desc: "destroying filesystem dataset" }, - }); - - // set as archived in database - set({ ...pk, archived: true }); - - return { snapshot, milliseconds: Date.now() - start }; -} - -// Returns a hash of the file tree. This uses the find command to get path names, but -// doesn't actually read the *contents* of any files, so it's reasonbly fast. -async function hashFileTree({ - path, - what, - verbose, -}: { - path: string; - what?; - verbose?; -}): Promise { - const { stdout } = await exec({ - verbose, - command: `sudo sh -c 'cd "${path}" && find . -xdev -printf "%p %s %TY-%Tm-%Td %TH:%TM\n" | sha1sum'`, - what, - }); - return stdout; -} diff --git a/src/packages/file-server/zfs/backup.ts b/src/packages/file-server/zfs/backup.ts deleted file mode 100644 index 5ded8ee4af..0000000000 --- a/src/packages/file-server/zfs/backup.ts +++ /dev/null @@ -1,176 +0,0 @@ -/* -Make backups using bup. -*/ - -import { bupFilesystemMountpoint, filesystemSnapshotMountpoint } from "./names"; -import { get, getRecent, set } from "./db"; -import { exec } from "./util"; -import getLogger from "@cocalc/backend/logger"; -import { exists } from "@cocalc/backend/misc/async-utils-node"; -import { join } from "path"; -import { mountFilesystem } from "./properties"; -import { split } from "@cocalc/util/misc"; -import { BUP_INTERVAL_MS } from "./config"; -import { primaryKey, type PrimaryKey } from "./types"; -import { createSnapshot } from "./snapshots"; - -const logger = getLogger("file-server:zfs:backup"); - -const EXCLUDES = [".conda", ".npm", "cache", ".julia", ".local/share/pnpm"]; - -export async function createBackup( - fs: PrimaryKey, -): Promise<{ BUP_DIR: string }> { - const pk = primaryKey(fs); - logger.debug("createBackup", pk); - const filesystem = get(pk); - await mountFilesystem(pk); - const snapshot = await createSnapshot({ ...filesystem, ifChanged: true }); - const mountpoint = filesystemSnapshotMountpoint({ ...filesystem, snapshot }); - const excludes: string[] = []; - for (const path of EXCLUDES) { - excludes.push("--exclude"); - excludes.push(join(mountpoint, path)); - } - logger.debug("createBackup: index", pk); - const BUP_DIR = bupFilesystemMountpoint(filesystem); - if (!(await exists(BUP_DIR))) { - await exec({ - verbose: true, - command: "sudo", - args: ["mkdir", "-p", BUP_DIR], - what: { ...pk, desc: "make bup repo" }, - }); - await exec({ - verbose: true, - command: "sudo", - args: ["bup", "-d", BUP_DIR, "init"], - what: { ...pk, desc: "bup init" }, - }); - } - await exec({ - verbose: true, - env: { BUP_DIR }, - command: "sudo", - args: [ - "--preserve-env", - "bup", - "index", - ...excludes, - "-x", - mountpoint, - "--no-check-device", - ], - what: { ...pk, desc: "creating bup index" }, - }); - logger.debug("createBackup: save", pk); - await exec({ - verbose: true, - env: { BUP_DIR }, - command: "sudo", - args: [ - "--preserve-env", - "bup", - "save", - "-q", - "--strip", - "-n", - "master", - mountpoint, - ], - what: { ...pk, desc: "save new bup snapshot" }, - }); - - const { stdout } = await exec({ - env: { BUP_DIR }, - command: "sudo", - args: ["--preserve-env", "bup", "ls", "master"], - what: { ...pk, desc: "getting name of backup" }, - }); - const v = split(stdout); - const last_bup_backup = v[v.length - 2]; - logger.debug("createBackup: created ", { last_bup_backup }); - set({ ...pk, last_bup_backup }); - - // prune-older --unsafe --keep-all-for 8d --keep-dailies-for 4w --keep-monthlies-for 6m --keep-yearlies-for 10y - logger.debug("createBackup: prune", pk); - await exec({ - verbose: true, - env: { BUP_DIR }, - command: "sudo", - args: [ - "--preserve-env", - "bup", - "prune-older", - "--unsafe", - "--keep-all-for", - "8d", - "--keep-dailies-for", - "4w", - "--keep-monthlies-for", - "6m", - "--keep-yearlies-for", - "5y", - ], - what: { ...pk, desc: "save new bup snapshot" }, - }); - - return { BUP_DIR }; -} - -// Go through ALL filesystems with last_edited >= cutoff and make a bup -// backup if they are due. -// cutoff = a Date (default = 1 week ago) -export async function maintainBackups(cutoff?: Date) { - logger.debug("backupActiveFilesystems: getting..."); - const v = getRecent({ cutoff }); - logger.debug( - `backupActiveFilesystems: considering ${v.length} filesystems`, - cutoff, - ); - let i = 0; - for (const { archived, last_edited, last_bup_backup, ...pk } of v) { - if (archived || !last_edited) { - continue; - } - const age = - new Date(last_edited).valueOf() - bupToDate(last_bup_backup).valueOf(); - if (age < BUP_INTERVAL_MS) { - // there's a new backup already - continue; - } - try { - await createBackup(pk); - } catch (err) { - logger.debug(`backupActiveFilesystems: error -- ${err}`); - } - i += 1; - if (i % 10 == 0) { - logger.debug(`backupActiveFilesystems: ${i}/${v.length}`); - } - } -} - -function bupToDate(dateString?: string): Date { - if (!dateString) { - return new Date(0); - } - // Extract components using regular expression - const match = dateString.match( - /^(\d{4})-(\d{2})-(\d{2})-(\d{2})(\d{2})(\d{2})$/, - ); - - if (match) { - const [_, year, month, day, hour, minute, second] = match; // Destructure components - return new Date( - parseInt(year), - parseInt(month) - 1, - parseInt(day), - parseInt(hour), - parseInt(minute), - parseInt(second), - ); - } else { - throw Error("Invalid bup date format"); - } -} diff --git a/src/packages/file-server/zfs/config.ts b/src/packages/file-server/zfs/config.ts deleted file mode 100644 index 96b0df9d74..0000000000 --- a/src/packages/file-server/zfs/config.ts +++ /dev/null @@ -1,98 +0,0 @@ -import { join } from "path"; -import { databaseFilename } from "./names"; - -// we ONLY put filesystems on pools whose name have this prefix. -// all other pools are ignored. We also mount everything in /{PREFIX} on the filesystem. -const PREFIX = process.env.COCALC_TEST_MODE ? "cocalcfs-test" : "cocalcfs"; - -const DATA = `/${PREFIX}`; - -const SQLITE3_DATABASE_FILE = databaseFilename(DATA); - -// Directory on server where filesystems get mounted (so NFS can serve them) -const FILESYSTEMS = join(DATA, "filesystems"); - -// Directory on server where zfs send streams (and tar?) are stored -const ARCHIVES = join(DATA, "archives"); - -// Directory to store data used in pulling as part of sync. -// E.g., this keeps around copies of the sqlite state database of each remote. -const PULL = join(DATA, "pull"); - -// Directory for bup -const BUP = join(DATA, "bup"); - -export const context = { - namespace: process.env.NAMESPACE ?? "default", - PREFIX, - DATA, - SQLITE3_DATABASE_FILE, - FILESYSTEMS, - ARCHIVES, - PULL, - BUP, -}; - -// WARNING: this "setContext" is global. It's very useful for **UNIT TESTING**, but -// for any other use, you want to set this at most once and never again!!! The reason -// is because with nodejs you could have async code running all over the place, and -// changing the context out from under it would lead to nonsense and corruption. -export function setContext({ - namespace, - prefix, -}: { - namespace?: string; - prefix?: string; -}) { - context.namespace = namespace ?? process.env.NAMESPACE ?? "default"; - context.PREFIX = prefix ?? PREFIX; - context.DATA = `/${context.PREFIX}`; - context.SQLITE3_DATABASE_FILE = databaseFilename(context.DATA); - context.FILESYSTEMS = join(context.DATA, "filesystems"); - context.ARCHIVES = join(context.DATA, "archives"); - context.PULL = join(context.DATA, "pull"); - context.BUP = join(context.DATA, "bup"); -} - -// Every filesystem has at least this much quota (?) -export const MIN_QUOTA = 1024 * 1024 * 1; // 1MB - -// We periodically do "zpool list" to find out what pools are available -// and how much space they have left. This info is cached for this long -// to avoid excessive calls: -export const POOLS_CACHE_MS = 15000; - -// two hour default for running any commands (e.g., zfs send/recv) -export const DEFAULT_EXEC_TIMEOUT_MS = 2 * 1000 * 60 * 60; - -// **all** user files for filesystems have this owner and group. -export const UID = 2001; -export const GID = 2001; - -// We make/update snapshots periodically, with this being the minimum interval. -export const SNAPSHOT_INTERVAL_MS = 60 * 30 * 1000; -//export const SNAPSHOT_INTERVAL_MS = 10 * 1000; - -// Lengths of time in minutes to keep these snapshots -export const SNAPSHOT_INTERVALS_MS = { - halfhourly: 30 * 1000 * 60, - daily: 60 * 24 * 1000 * 60, - weekly: 60 * 24 * 7 * 1000 * 60, - monthly: 60 * 24 * 7 * 4 * 1000 * 60, -}; - -// How many of each type of snapshot to retain -export const SNAPSHOT_COUNTS = { - halfhourly: 24, - daily: 14, - weekly: 7, - monthly: 4, -}; - -// Minimal interval for bup backups -export const BUP_INTERVAL_MS = 24 * 1000 * 60 * 60; - -// minimal interval for zfs streams -export const STREAM_INTERVAL_MS = 24 * 1000 * 60 * 60; -// when more than this many streams, we recompact down -export const MAX_STREAMS = 30; diff --git a/src/packages/file-server/zfs/copy.ts b/src/packages/file-server/zfs/copy.ts deleted file mode 100644 index 8248d3061f..0000000000 --- a/src/packages/file-server/zfs/copy.ts +++ /dev/null @@ -1,14 +0,0 @@ -/* -Copy between projects on this server -*/ - -export async function copy(opts: { - source_project_id: string; - target_project_id?: string; - source_path: string; - target_path: string; - rsyncOptions?: string; -}) { - console.log("copy", opts); - throw Error("copy: not implemented"); -} diff --git a/src/packages/file-server/zfs/create.ts b/src/packages/file-server/zfs/create.ts deleted file mode 100644 index 7be3005470..0000000000 --- a/src/packages/file-server/zfs/create.ts +++ /dev/null @@ -1,208 +0,0 @@ -import { create, get, getDb, deleteFromDb, filesystemExists } from "./db"; -import { exec } from "./util"; -import { - filesystemArchivePath, - bupFilesystemMountpoint, - filesystemDataset, - filesystemMountpoint, -} from "./names"; -import { getPools, initializePool } from "./pools"; -import { dearchiveFilesystem } from "./archive"; -import { UID, GID } from "./config"; -import { createSnapshot } from "./snapshots"; -import { type Filesystem, primaryKey, type PrimaryKey } from "./types"; - -export async function createFilesystem( - opts: PrimaryKey & { - affinity?: string; - clone?: PrimaryKey; - }, -): Promise { - if (filesystemExists(opts)) { - return get(opts); - } - const pk = primaryKey(opts); - const { namespace } = pk; - const { affinity, clone } = opts; - const source = clone ? get(clone) : undefined; - - const db = getDb(); - // select a pool: - let pool: undefined | string = undefined; - - if (source != null) { - // use same pool as source filesystem. (we could use zfs send/recv but that's much slower and not a clone) - pool = source.pool; - } else { - if (affinity) { - // if affinity is set, have preference to use same pool as other filesystems with this affinity. - const x = db - .prepare( - "SELECT pool, COUNT(pool) AS cnt FROM filesystems WHERE namespace=? AND affinity=? ORDER by cnt DESC", - ) - .get(namespace, affinity) as { pool: string; cnt: number } | undefined; - pool = x?.pool; - } - if (!pool) { - // assign one with *least* filesystems - const x = db - .prepare( - "SELECT pool, COUNT(pool) AS cnt FROM filesystems GROUP BY pool ORDER by cnt ASC", - ) - .all() as any; - const pools = await getPools(); - if (Object.keys(pools).length > x.length) { - // rare case: there exists a pool that isn't used yet, so not - // represented in above query at all; use it - const v = new Set(); - for (const { pool } of x) { - v.add(pool); - } - for (const name in pools) { - if (!v.has(name)) { - pool = name; - break; - } - } - } else { - if (x.length == 0) { - throw Error("cannot create filesystem -- no available pools"); - } - // just use the least crowded - pool = x[0].pool; - } - } - } - if (!pool) { - throw Error("bug -- unable to select a pool"); - } - - const { cnt } = db - .prepare( - "SELECT COUNT(pool) AS cnt FROM filesystems WHERE pool=? AND namespace=?", - ) - .get(pool, namespace) as { cnt: number }; - - if (cnt == 0) { - // initialize pool for use in this namespace: - await initializePool({ pool, namespace }); - } - - if (source == null) { - // create filesystem on the selected pool - const mountpoint = filesystemMountpoint(pk); - const dataset = filesystemDataset({ ...pk, pool }); - await exec({ - verbose: true, - command: "sudo", - args: [ - "zfs", - "create", - "-o", - `mountpoint=${mountpoint}`, - "-o", - "compression=lz4", - "-o", - "dedup=on", - dataset, - ], - what: { - ...pk, - desc: `create filesystem ${dataset} for filesystem on the selected pool mounted at ${mountpoint}`, - }, - }); - await exec({ - verbose: true, - command: "sudo", - args: ["chown", "-R", `${UID}:${GID}`, mountpoint], - whate: { - ...pk, - desc: `setting permissions of filesystem mounted at ${mountpoint}`, - }, - }); - } else { - // clone source - // First ensure filesystem isn't archived - // (we might alternatively de-archive to make the clone...?) - if (source.archived) { - await dearchiveFilesystem(source); - } - // Get newest snapshot, or make one if there are none - const snapshot = await createSnapshot({ ...source, ifChanged: true }); - if (!snapshot) { - throw Error("bug -- source should have snapshot"); - } - const source_snapshot = `${filesystemDataset(source)}@${snapshot}`; - await exec({ - verbose: true, - command: "sudo", - args: [ - "zfs", - "clone", - "-o", - `mountpoint=${filesystemMountpoint(pk)}`, - "-o", - "compression=lz4", - "-o", - "dedup=on", - source_snapshot, - filesystemDataset({ ...pk, pool }), - ], - what: { - ...pk, - desc: `clone filesystem from ${source_snapshot}`, - }, - }); - } - - // update database - create({ ...pk, pool, affinity }); - return get(pk); -} - -// delete -- This is very dangerous -- it deletes the filesystem, -// the archive, and any backups and removes knowledge the filesystem from the db. - -export async function deleteFilesystem(fs: PrimaryKey) { - const filesystem = get(fs); - const dataset = filesystemDataset(filesystem); - if (!filesystem.archived) { - await exec({ - verbose: true, - command: "sudo", - args: ["zfs", "destroy", "-r", dataset], - what: { - ...filesystem, - desc: `destroy dataset ${dataset} containing the filesystem`, - }, - }); - } - await exec({ - verbose: true, - command: "sudo", - args: ["rm", "-rf", filesystemMountpoint(filesystem)], - what: { - ...filesystem, - desc: `delete directory '${filesystemMountpoint(filesystem)}' where filesystem was stored`, - }, - }); - await exec({ - verbose: true, - command: "sudo", - args: ["rm", "-rf", bupFilesystemMountpoint(filesystem)], - what: { - ...filesystem, - desc: `delete directory '${bupFilesystemMountpoint(filesystem)}' where backups were stored`, - }, - }); - await exec({ - verbose: true, - command: "sudo", - args: ["rm", "-rf", filesystemArchivePath(filesystem)], - what: { - ...filesystem, - desc: `delete directory '${filesystemArchivePath(filesystem)}' where archives were stored`, - }, - }); - deleteFromDb(filesystem); -} diff --git a/src/packages/file-server/zfs/db.ts b/src/packages/file-server/zfs/db.ts deleted file mode 100644 index 9b33d202dc..0000000000 --- a/src/packages/file-server/zfs/db.ts +++ /dev/null @@ -1,277 +0,0 @@ -/* -Database -*/ - -import Database from "better-sqlite3"; -import { context } from "./config"; -import { - primaryKey, - type PrimaryKey, - type Filesystem, - type RawFilesystem, - type SetFilesystem, - OWNER_ID_FIELDS, -} from "./types"; -import { is_array, is_date } from "@cocalc/util/misc"; - -let db: { [file: string]: Database.Database } = {}; - -const tableName = "filesystems"; -const schema = { - // this uniquely defines the filesystem (it's the compound primary key) - owner_type: "TEXT", - owner_id: "TEXT", - namespace: "TEXT", - name: "TEXT", - - // data about the filesystem - pool: "TEXT", - archived: "INTEGER", - affinity: "TEXT", - nfs: "TEXT", - snapshots: "TEXT", - last_edited: "TEXT", - last_send_snapshot: "TEXT", - last_bup_backup: "TEXT", - error: "TEXT", - last_error: "TEXT", - used_by_dataset: "INTEGER", - used_by_snapshots: "INTEGER", - quota: "INTEGER", -}; - -const WHERE_PRIMARY_KEY = - "WHERE namespace=? AND owner_type=? AND owner_id=? AND name=?"; -function primaryKeyArgs(fs: PrimaryKey) { - const { namespace, owner_type, owner_id, name } = primaryKey(fs); - return [namespace, owner_type, owner_id, name]; -} - -export function getDb(databaseFile?): Database.Database { - const file = databaseFile ?? context.SQLITE3_DATABASE_FILE; - if (db[file] == null) { - db[file] = new Database(file); - initDb(db[file]); - } - return db[file]!; -} - -function initDb(db) { - const columnDefinitions = Object.entries(schema) - .map(([name, type]) => `${name} ${type}`) - .join(", "); - - // Create table if it doesn't exist - db.prepare( - `CREATE TABLE IF NOT EXISTS ${tableName} ( - ${columnDefinitions}, - PRIMARY KEY (namespace, owner_type, owner_id, name) - )`, - ).run(); - - // Check for missing columns and add them - const existingColumnsStmt = db.prepare(`PRAGMA table_info(${tableName})`); - const existingColumns = existingColumnsStmt.all().map((row) => row.name); - - for (const [name, type] of Object.entries(schema)) { - if (!existingColumns.includes(name)) { - db.prepare(`ALTER TABLE ${tableName} ADD COLUMN ${name} ${type}`).run(); - } - } -} - -// This is extremely dangerous and mainly used for unit testing: -export function resetDb() { - const db = new Database(context.SQLITE3_DATABASE_FILE); - db.prepare("DROP TABLE IF EXISTS filesystems").run(); - initDb(db); -} - -function convertToSqliteType({ value, getFilesystem }) { - if (is_array(value)) { - return value.join(","); - } else if (is_date(value)) { - return value.toISOString(); - } else if (typeof value == "boolean") { - return value ? 1 : 0; - } else if (typeof value == "function") { - const x = value(getFilesystem()); - if (typeof x == "function") { - throw Error("function must not return a function"); - } - // returned value needs to be converted - return convertToSqliteType({ value: x, getFilesystem }); - } - return value; -} - -export function set(obj: SetFilesystem) { - const pk = primaryKey(obj); - const fields: string[] = []; - const values: any[] = []; - let filesystem: null | Filesystem = null; - const getFilesystem = () => { - if (filesystem == null) { - filesystem = get(pk); - } - return filesystem; - }; - for (const field in obj) { - if (pk[field] !== undefined || OWNER_ID_FIELDS.includes(field)) { - continue; - } - fields.push(field); - values.push(convertToSqliteType({ value: obj[field], getFilesystem })); - } - let query = `UPDATE filesystems SET - ${fields.map((field) => `${field}=?`).join(", ")} - ${WHERE_PRIMARY_KEY} - `; - for (const x of primaryKeyArgs(pk)) { - values.push(x); - } - const db = getDb(); - db.prepare(query).run(...values); -} - -// Call this if something that should never happen, does in fact, happen. -// It will set the error state of the filesystem and throw the exception. -// Admins will be regularly notified of all filesystems in an error state. -export function fatalError( - obj: PrimaryKey & { - err: Error; - desc?: string; - }, -) { - set({ - ...primaryKey(obj), - error: `${obj.err}${obj.desc ? " - " + obj.desc : ""}`, - last_error: new Date(), - }); - throw obj.err; -} - -export function clearError(fs: PrimaryKey) { - set({ ...fs, error: null }); -} - -export function clearAllErrors() { - const db = getDb(); - db.prepare("UPDATE filesystems SET error=null").run(); -} - -export function getErrors() { - const db = getDb(); - return db - .prepare("SELECT * FROM filesystems WHERE error!=''") - .all() as RawFilesystem[]; -} - -export function touch(fs: PrimaryKey) { - set({ ...fs, last_edited: new Date() }); -} - -export function filesystemExists( - fs: PrimaryKey, - databaseFile?: string, -): boolean { - const db = getDb(databaseFile); - const x = db - .prepare("SELECT COUNT(*) AS count FROM filesystems " + WHERE_PRIMARY_KEY) - .get(...primaryKeyArgs(fs)); - return (x as any).count > 0; -} - -export function get(fs: PrimaryKey, databaseFile?: string): Filesystem { - const db = getDb(databaseFile); - const filesystem = db - .prepare("SELECT * FROM filesystems " + WHERE_PRIMARY_KEY) - .get(...primaryKeyArgs(fs)) as any; - if (filesystem == null) { - throw Error(`no filesystem ${JSON.stringify(fs)}`); - } - for (const key of ["nfs", "snapshots"]) { - filesystem[key] = sqliteStringToArray(filesystem[key]); - } - filesystem["archived"] = !!filesystem["archived"]; - if (filesystem.last_edited) { - filesystem.last_edited = new Date(filesystem.last_edited); - } - if (filesystem.last_error) { - filesystem.last_error = new Date(filesystem.last_error); - } - return filesystem as Filesystem; -} - -export function create( - obj: PrimaryKey & { - pool: string; - affinity?: string; - }, -) { - if (!obj.pool.startsWith(context.PREFIX)) { - throw Error(`pool must start with ${context.PREFIX} - ${obj.pool}`); - } - getDb() - .prepare( - "INSERT INTO filesystems(namespace, owner_type, owner_id, name, pool, affinity, last_edited) VALUES(?,?,?,?,?,?,?)", - ) - .run( - ...primaryKeyArgs(obj), - obj.pool, - obj.affinity, - new Date().toISOString(), - ); -} - -export function deleteFromDb(fs: PrimaryKey) { - getDb() - .prepare("DELETE FROM filesystems " + WHERE_PRIMARY_KEY) - .run(...primaryKeyArgs(fs)); -} - -export function getAll({ - namespace = context.namespace, -}: { namespace?: string } = {}): RawFilesystem[] { - const db = getDb(); - return db - .prepare("SELECT * FROM filesystems WHERE namespace=?") - .all(namespace) as RawFilesystem[]; -} - -export function getNamespacesAndPools(): { namespace: string; pool: string }[] { - const db = getDb(); - return db - .prepare("SELECT DISTINCT namespace, pool FROM filesystems") - .all() as any; -} - -export function getRecent({ - namespace, - cutoff, - databaseFile, -}: { - namespace?: string; - cutoff?: Date; - databaseFile?: string; -} = {}): RawFilesystem[] { - const db = getDb(databaseFile); - if (cutoff == null) { - cutoff = new Date(Date.now() - 1000 * 60 * 60 * 24 * 7); - } - const query = "SELECT * FROM filesystems WHERE last_edited>=?"; - if (namespace == null) { - return db.prepare(query).all(cutoff.toISOString()) as RawFilesystem[]; - } else { - return db - .prepare(`${query} AND namespace=?`) - .all(cutoff.toISOString(), namespace) as RawFilesystem[]; - } -} - -function sqliteStringToArray(s?: string): string[] { - if (!s) { - return []; - } - return s.split(","); -} diff --git a/src/packages/file-server/zfs/index.ts b/src/packages/file-server/zfs/index.ts deleted file mode 100644 index 8729ea2d04..0000000000 --- a/src/packages/file-server/zfs/index.ts +++ /dev/null @@ -1,29 +0,0 @@ -export { getPools, initializePool, initializeAllPools } from "./pools"; -export { - getModifiedFiles, - deleteSnapshot, - deleteExtraSnapshotsOfActiveFilesystems, - deleteExtraSnapshots, -} from "./snapshots"; -export { - getAll, - getRecent, - get, - set, - clearError, - getErrors, - clearAllErrors, -} from "./db"; -export { shareNFS, unshareNFS } from "./nfs"; -export { createFilesystem, deleteFilesystem } from "./create"; -export { createSnapshot, getSnapshots, maintainSnapshots } from "./snapshots"; -export { - mountFilesystem, - unmountFilesystem, - setQuota, - syncProperties, -} from "./properties"; -export { archiveFilesystem, dearchiveFilesystem } from "./archive"; -export { maintainBackups, createBackup } from "./backup"; -export { recv, send, recompact, maintainStreams } from "./streams"; -export { pull } from "./pull"; diff --git a/src/packages/file-server/zfs/names.ts b/src/packages/file-server/zfs/names.ts deleted file mode 100644 index a9f5ff7571..0000000000 --- a/src/packages/file-server/zfs/names.ts +++ /dev/null @@ -1,176 +0,0 @@ -import { join } from "path"; -import { context } from "./config"; -import { primaryKey, type PrimaryKey } from "./types"; -import { randomId } from "@cocalc/conat/names"; - -export function databaseFilename(data: string) { - return join(data, "database.sqlite3"); -} - -export function namespaceDataset({ - pool, - namespace, -}: { - pool: string; - namespace: string; -}) { - return `${pool}/${namespace}`; -} - -// Archives -// There is one single dataset for each namespace/pool pair: All the different -// archives across filesystems are stored in the *same* dataset, since there is no -// point in separating them. -export function archivesDataset({ - pool, - namespace, -}: { - pool: string; - namespace: string; -}) { - return `${namespaceDataset({ pool, namespace })}/archives`; -} - -export function archivesMountpoint({ - pool, - namespace, -}: { - pool: string; - namespace: string; -}) { - return join(context.ARCHIVES, namespace, pool); -} - -export function filesystemArchivePath({ - pool, - ...fs -}: PrimaryKey & { pool: string }) { - const pk = primaryKey(fs); - return join( - archivesMountpoint({ pool, namespace: pk.namespace }), - pk.owner_type, - pk.owner_id, - pk.name, - ); -} - -export function filesystemArchiveFilename(opts: PrimaryKey & { pool: string }) { - const { owner_type, owner_id, name } = primaryKey(opts); - return join( - filesystemArchivePath(opts), - `full-${owner_type}-${owner_id}-${name}.zfs`, - ); -} - -export function filesystemStreamsPath(opts: PrimaryKey & { pool: string }) { - return join(filesystemArchivePath(opts), "streams"); -} - -export function filesystemStreamsFilename({ - snapshot1, - snapshot2, - ...opts -}: PrimaryKey & { snapshot1: string; snapshot2: string; pool: string }) { - return join(filesystemStreamsPath(opts), `${snapshot1}-${snapshot2}.zfs`); -} - -// Bup -export function bupDataset({ - pool, - namespace, -}: { - pool: string; - namespace: string; -}) { - return `${namespaceDataset({ pool, namespace })}/bup`; -} - -export function bupMountpoint({ - pool, - namespace, -}: { - pool: string; - namespace: string; -}) { - return join(context.BUP, namespace, pool); -} - -export function bupFilesystemMountpoint({ - pool, - ...fs -}: PrimaryKey & { pool: string }) { - const pk = primaryKey(fs); - return join( - bupMountpoint({ ...pk, pool }), - pk.owner_type, - pk.owner_id, - pk.name, - ); -} - -// Filesystems - -export function filesystemsPath({ namespace }) { - return join(context.FILESYSTEMS, namespace); -} - -export function filesystemMountpoint(fs: PrimaryKey) { - const pk = primaryKey(fs); - return join(filesystemsPath(pk), pk.owner_type, pk.owner_id, pk.name); -} - -export function filesystemSnapshotMountpoint( - opts: PrimaryKey & { snapshot: string }, -) { - return join(filesystemMountpoint(opts), ".zfs", "snapshot", opts.snapshot); -} - -export function filesystemsDataset({ - pool, - namespace, -}: { - pool: string; - namespace: string; -}) { - return `${namespaceDataset({ pool, namespace })}/filesystems`; -} - -// There is one single dataset for each project_id/namespace/pool tripple since it -// is critical to separate each project to properly support snapshots, clones, -// backups, etc. -export function filesystemDataset({ - pool, - ...fs -}: PrimaryKey & { pool: string }) { - const { namespace, owner_type, owner_id, name } = primaryKey(fs); - // NOTE: we *could* use a heirarchy of datasets like this: - // ${owner_type}/${owner_id}/${name} - // However, that greatly increases the raw number of datasets, and there's a huge performance - // penalty. Since the owner_type is a fixed small list, owner_id is a uuid and the name is - // more general, there's no possible overlaps just concating them as below, and this way there's - // only one dataset, rather than three. (We also don't need to worry about deleting parents - // when there are no children...) - return `${filesystemsDataset({ pool, namespace: namespace })}/${owner_type}-${owner_id}-${name}`; -} - -export function tempDataset({ - pool, - namespace, -}: { - pool: string; - namespace: string; -}) { - return `${namespaceDataset({ pool, namespace })}/temp`; -} - -export function filesystemDatasetTemp({ - pool, - ...fs -}: PrimaryKey & { pool: string }) { - const { namespace, owner_type, owner_id, name } = primaryKey(fs); - return `${tempDataset({ pool, namespace })}/${owner_type}-${owner_id}-${name}-${randomId()}`; -} - -// NOTE: We use "join" for actual file paths and explicit -// strings with / for ZFS filesystem names, since in some whacky -// futuristic world maybe this server is running on MS Windows. diff --git a/src/packages/file-server/zfs/nfs.ts b/src/packages/file-server/zfs/nfs.ts deleted file mode 100644 index a3c6f87603..0000000000 --- a/src/packages/file-server/zfs/nfs.ts +++ /dev/null @@ -1,111 +0,0 @@ -import { get, set, touch } from "./db"; -import { exec } from "./util"; -import { filesystemDataset, filesystemMountpoint } from "./names"; -import { primaryKey, type PrimaryKey } from "./types"; - -// Ensure that this filesystem is mounted and setup so that export to the -// given client is allowed. -// Returns the remote that the client should use for NFS mounting, i.e., -// this return s, then type "mount s /mnt/..." to mount the filesystem. -// If client is not given, just sets the share at NFS level -// to what's specified in the database. -export async function shareNFS({ - client, - ...fs -}: PrimaryKey & { client?: string }): Promise { - client = client?.trim(); - const pk = primaryKey(fs); - const { pool, nfs } = get(pk); - let hostname; - if (client) { - hostname = await hostnameFor(client); - if (!nfs.includes(client)) { - nfs.push(client); - // update database which tracks what the share should be. - set({ ...pk, nfs: ({ nfs }) => [...nfs, client] }); - } - } - // actually ensure share is configured. - const name = filesystemDataset({ pool, ...pk }); - const sharenfs = - nfs.length > 0 - ? `${nfs.map((client) => `rw=${client}`).join(",")},no_root_squash,crossmnt,no_subtree_check` - : "off"; - await exec({ - verbose: true, - command: "sudo", - args: ["zfs", "set", `sharenfs=${sharenfs}`, name], - }); - if (nfs.length > 0) { - touch(pk); - } - if (client) { - return `${hostname}:${filesystemMountpoint(pk)}`; - } else { - return ""; - } -} - -// remove given client from nfs sharing -export async function unshareNFS({ - client, - ...fs -}: PrimaryKey & { client: string }) { - const pk = primaryKey(fs); - let { nfs } = get(pk); - if (!nfs.includes(client)) { - // nothing to do - return; - } - nfs = nfs.filter((x) => x != client); - // update database which tracks what the share should be. - set({ ...pk, nfs }); - // update zfs/nfs to no longer share to this client - await shareNFS(pk); -} - -let serverIps: null | string[] = null; -async function hostnameFor(client: string) { - if (serverIps == null) { - const ipRegex = /^(\d{1,3}\.){3}\d{1,3}$/; - const { stdout } = await exec({ - verbose: false, - command: "ifconfig", - }); - let i = stdout.indexOf("inet "); - const v: string[] = []; - while (i != -1) { - let j = stdout.indexOf("\n", i); - if (j == -1) { - break; - } - const x = stdout.slice(i, j).split(" "); - const ip = x[1]; - if (ipRegex.test(ip)) { - v.push(ip); - } - i = stdout.indexOf("inet ", j); - } - if (v.length == 0) { - throw Error("unable to determine server ip address"); - } - serverIps = v; - } - for (const ip of serverIps) { - if (subnetMatch(ip, client)) { - return ip; - } - } - throw Error("found no matching subdomain"); -} - -// a and b are ip addresses. Return true -// if the are on the same subnet, by which -// we mean that the first *TWO* segments match, -// since that's the size of our subnets usually. -// TODO: make configurable (?). -function subnetMatch(a, b) { - const v = a.split("."); - const w = b.split("."); - return v[0] == w[0] && v[1] == w[1]; -} diff --git a/src/packages/file-server/zfs/pools.ts b/src/packages/file-server/zfs/pools.ts deleted file mode 100644 index b71bdc45f2..0000000000 --- a/src/packages/file-server/zfs/pools.ts +++ /dev/null @@ -1,228 +0,0 @@ -/* -This code sets things up for each pool and namespace, e.g., defining datasets, creating directories, -etc. as defined in config and names. - -WARNING: For efficientcy and sanity, it assumes that once something is setup, it stays setup. -If there is a chaos monkey running around breaking things (e.g., screwing up -file permissions, deleting datasets, etc.,) then this code won't help at all. - -OPERATIONS: - -- To add a new pool, just create it using zfs with a name sthat starts with context.PREFIX. - It should automatically start getting used within POOLS_CACHE_MS by newly created filesystems. - -*/ - -import { reuseInFlight } from "@cocalc/util/reuse-in-flight"; -import { context, POOLS_CACHE_MS } from "./config"; -import { exec } from "./util"; -import { - archivesDataset, - archivesMountpoint, - namespaceDataset, - filesystemsDataset, - filesystemsPath, - bupDataset, - bupMountpoint, - tempDataset, -} from "./names"; -import { exists } from "@cocalc/backend/misc/async-utils-node"; -import { getNamespacesAndPools } from "./db"; - -// Make sure all pools and namespaces are initialized for all existing filesystems. -// This should be needed after booting up the server and importing the pools. -export async function initializeAllPools() { - // TODO: maybe import all here? - - for (const { namespace, pool } of getNamespacesAndPools()) { - await initializePool({ namespace, pool }); - } -} - -interface Pool { - name: string; - state: "ONLINE" | "OFFLINE"; - size: number; - allocated: number; - free: number; -} - -type Pools = { [name: string]: Pool }; -let poolsCache: { [prefix: string]: Pools } = {}; - -export const getPools = reuseInFlight( - async ({ noCache }: { noCache?: boolean } = {}): Promise => { - if (!noCache && poolsCache[context.PREFIX]) { - return poolsCache[context.PREFIX]; - } - const { stdout } = await exec({ - verbose: true, - command: "zpool", - args: ["list", "-j", "--json-int", "-o", "size,allocated,free"], - }); - const { pools } = JSON.parse(stdout); - const v: { [name: string]: Pool } = {}; - for (const name in pools) { - if (!name.startsWith(context.PREFIX)) { - continue; - } - const pool = pools[name]; - for (const key in pool.properties) { - pool.properties[key] = pool.properties[key].value; - } - v[name] = { name, state: pool.state, ...pool.properties }; - } - poolsCache[context.PREFIX] = v; - if (!process.env.COCALC_TEST_MODE) { - // only clear cache in non-test mode - setTimeout(() => { - delete poolsCache[context.PREFIX]; - }, POOLS_CACHE_MS); - } - return v; - }, -); - -// OK to call this again even if initialized already. -export const initializePool = reuseInFlight( - async ({ - namespace = context.namespace, - pool, - }: { - namespace?: string; - pool: string; - }) => { - if (!pool.startsWith(context.PREFIX)) { - throw Error( - `pool (="${pool}") must start with the prefix '${context.PREFIX}'`, - ); - } - // archives and filesystems for each namespace are in this dataset - await ensureDatasetExists({ - name: namespaceDataset({ namespace, pool }), - }); - - // Initialize archives dataset, used for archiving filesystems. - await ensureDatasetExists({ - name: archivesDataset({ pool, namespace }), - mountpoint: archivesMountpoint({ pool, namespace }), - }); - // This sets up the parent filesystem for all filesystems - // and enable compression and dedup. - await ensureDatasetExists({ - name: filesystemsDataset({ namespace, pool }), - }); - await ensureDatasetExists({ - name: tempDataset({ namespace, pool }), - dedup: "off", - }); - // Initialize bup dataset, used for backups. - await ensureDatasetExists({ - name: bupDataset({ pool, namespace }), - mountpoint: bupMountpoint({ pool, namespace }), - compression: "off", - dedup: "off", - }); - - const filesystems = filesystemsPath({ namespace }); - if (!(await exists(filesystems))) { - await exec({ - verbose: true, - command: "sudo", - args: ["mkdir", "-p", filesystems], - }); - await exec({ - verbose: true, - command: "sudo", - args: ["chmod", "a+rx", context.FILESYSTEMS], - }); - await exec({ - verbose: true, - command: "sudo", - args: ["chmod", "a+rx", filesystems], - }); - } - }, -); - -// If a dataset exists, it is assumed to exist henceforth for the life of this process. -// That's fine for *this* application here of initializing pools, since we never delete -// anything here. -const datasetExistsCache = new Set(); -async function datasetExists(name: string): Promise { - if (datasetExistsCache.has(name)) { - return true; - } - try { - await exec({ - verbose: true, - command: "zfs", - args: ["list", name], - }); - datasetExistsCache.add(name); - return true; - } catch { - return false; - } -} - -async function isMounted(dataset): Promise { - const { stdout } = await exec({ - command: "zfs", - args: ["get", "mounted", dataset, "-j"], - }); - const x = JSON.parse(stdout); - return x.datasets[dataset].properties.mounted.value == "yes"; -} - -async function ensureDatasetExists({ - name, - mountpoint, - compression = "lz4", - dedup = "on", -}: { - name: string; - mountpoint?: string; - compression?: "lz4" | "off"; - dedup?: "on" | "off"; -}) { - if (await datasetExists(name)) { - if (mountpoint && !(await isMounted(name))) { - // ensure mounted - await exec({ - verbose: true, - command: "sudo", - args: ["zfs", "mount", name], - }); - } - return; - } - await exec({ - verbose: true, - command: "sudo", - args: [ - "zfs", - "create", - "-o", - `mountpoint=${mountpoint ? mountpoint : "none"}`, - "-o", - `compression=${compression}`, - "-o", - `dedup=${dedup}`, - name, - ], - }); - // make sure it is very hard to accidentally delete the entire dataset - // see https://github.com/openzfs/zfs/issues/4134#issuecomment-2565724994 - const safety = `${name}@safety`; - await exec({ - verbose: true, - command: "sudo", - args: ["zfs", "snapshot", safety], - }); - await exec({ - verbose: true, - command: "sudo", - args: ["zfs", "hold", "safety", safety], - }); -} diff --git a/src/packages/file-server/zfs/properties.ts b/src/packages/file-server/zfs/properties.ts deleted file mode 100644 index 77c228a942..0000000000 --- a/src/packages/file-server/zfs/properties.ts +++ /dev/null @@ -1,127 +0,0 @@ -import { exec } from "./util"; -import { filesystemDataset } from "./names"; -import { get, set } from "./db"; -import { MIN_QUOTA } from "./config"; -import { primaryKey, type PrimaryKey } from "./types"; - -export async function setQuota({ - // quota in **number of bytes**. - // If quota is smaller than actual dataset, then the quota is set to what is - // actually used (plus 10 MB), hopefully allowing user to delete some data. - // The quota is never less than MIN_QUOTA. - // The value stored in database is *also* then set to this amount. - // So this is not some magic fire and forget setting, but something - // that cocalc should regularly call when starting the filesystem. - quota, - noSync, - ...fs -}: { - quota: number; - noSync?: boolean; -} & PrimaryKey) { - const pk = primaryKey(fs); - // this will update current usage in the database - await syncProperties(pk); - const { pool, used_by_dataset } = get(pk); - const used = (used_by_dataset ?? 0) + 10 * 1024; - if (quota < used) { - quota = used!; - } - quota = Math.max(MIN_QUOTA, quota); - try { - await exec({ - verbose: true, - command: "sudo", - args: [ - "zfs", - "set", - // refquota so snapshots don't count against the user - `refquota=${quota}`, - filesystemDataset({ pool, ...pk }), - ], - }); - } finally { - // this sets quota in database in bytes to whatever was just set above. - await syncProperties(pk); - } -} - -// Sync with ZFS the properties for the given filesystem by -// setting the database to what is in ZFS: -// - total space used by snapshots -// - total space used by dataset -// - the quota -export async function syncProperties(fs: PrimaryKey) { - const pk = primaryKey(fs); - const { pool, archived } = get(pk); - if (archived) { - // they can't have changed - return; - } - set({ - ...pk, - ...(await zfsGetProperties(filesystemDataset({ pool, ...pk }))), - }); -} - -export async function zfsGetProperties(dataset: string): Promise<{ - used_by_snapshots: number; - used_by_dataset: number; - quota: number | null; -}> { - const { stdout } = await exec({ - command: "zfs", - args: [ - "list", - dataset, - "-j", - "--json-int", - "-o", - "usedsnap,usedds,refquota", - ], - }); - const x = JSON.parse(stdout); - const { properties } = x.datasets[dataset]; - return { - used_by_snapshots: properties.usedbysnapshots.value, - used_by_dataset: properties.usedbydataset.value, - quota: properties.refquota.value ? properties.refquota.value : null, - }; -} - -export async function mountFilesystem(fs: PrimaryKey) { - const pk = primaryKey(fs); - const { pool } = get(pk); - try { - await exec({ - command: "sudo", - args: ["zfs", "mount", filesystemDataset({ pool, ...pk })], - what: { ...pk, desc: "mount filesystem" }, - }); - } catch (err) { - if (`${err}`.includes("already mounted")) { - // fine - return; - } - throw err; - } -} - -export async function unmountFilesystem(fs: PrimaryKey) { - const pk = primaryKey(fs); - const { pool } = get(pk); - try { - await exec({ - verbose: true, - command: "sudo", - args: ["zfs", "unmount", filesystemDataset({ pool, ...pk })], - what: { ...pk, desc: "unmount filesystem" }, - }); - } catch (err) { - if (`${err}`.includes("not currently mounted")) { - // fine - } else { - throw err; - } - } -} diff --git a/src/packages/file-server/zfs/pull.ts b/src/packages/file-server/zfs/pull.ts deleted file mode 100644 index a0a0236814..0000000000 --- a/src/packages/file-server/zfs/pull.ts +++ /dev/null @@ -1,301 +0,0 @@ -/* -Use zfs replication over ssh to pull recent filesystems from -one file-server to another one. - -This will be used for: - -- backup -- moving a filesystem from one region/cluster to another -*/ - -import { - type Filesystem, - type RawFilesystem, - primaryKey, - PrimaryKey, -} from "./types"; -import { exec } from "./util"; -import { - databaseFilename, - filesystemDataset, - filesystemMountpoint, -} from "./names"; -import { filesystemExists, getRecent, get, set } from "./db"; -import getLogger from "@cocalc/backend/logger"; -import { getSnapshots } from "./snapshots"; -import { createFilesystem, deleteFilesystem } from "./create"; -import { context } from "./config"; -import { archiveFilesystem, dearchiveFilesystem } from "./archive"; -import { deleteSnapshot } from "./snapshots"; -import { isEqual } from "lodash"; -import { join } from "path"; -import { readdir, unlink } from "fs/promises"; - -const logger = getLogger("file-server:zfs:pull"); - -// number of remote backups of db sqlite file to keep. -const NUM_DB_TO_KEEP = 10; - -// This is used for unit testing. It's what fields should match -// after doing a sync, except snapshots where local is a superset, -// unless you pull with deleteSnapshots set to true. -export const SYNCED_FIELDS = [ - // these four fields identify the filesystem, so they better get sync'd: - "namespace", - "owner_type", - "owner_id", - "name", - // snaphots -- reflects that we replicated properly. - "snapshots", - - // last_edited is useful for targetting sync work and making decisions, e.g.., should we delete - "last_edited", - // these just get directly sync'd. They aren't used unless somehow local were to actually server - // data directly. - "affinity", - "nfs", -]; - -interface Remote { - // remote = user@hostname that you can ssh to - remote: string; - // filesystem prefix of the remote server, so {prefix}/database.sqlite3 has the - // database that defines the state of the remote server. - prefix: string; -} - -// Copy from remote to here every filesystem that has changed since cutoff. -export async function pull({ - cutoff, - filesystem, - remote, - prefix, - deleteFilesystemCutoff, - deleteSnapshots, - dryRun, -}: Remote & { - // pulls everything that's changed with remote last_edited >= cutoff. - cutoff?: Date; - // alternatively -- if given, only pull this filesystem and nothing else: - filesystem?: PrimaryKey; - - // DANGER: if set, any local filesystem with - // cutoff <= last_edited <= deleteFilesystemCutoff - // gets actually deleted. This makes it possible, e.g., to delete every filesystem - // that was deleted on the main server in the last 6 months and deleted at least 1 - // month ago, so we have a bit of time before destroy backups. - deleteFilesystemCutoff?: Date; - // if true, delete local snapshots if they were deleted on the remote. - deleteSnapshots?: boolean; - // just say how much will happen, but don't do anything. - dryRun?: boolean; -}): Promise<{ - toUpdate: { remoteFs: Filesystem; localFs?: Filesystem }[]; - toDelete: RawFilesystem[]; -}> { - logger.debug("pull: from ", { remote, prefix, cutoff, filesystem }); - if (prefix.startsWith("/")) { - throw Error("prefix should not start with /"); - } - if (cutoff == null) { - cutoff = new Date(Date.now() - 1000 * 60 * 60 * 24 * 7); - } - logger.debug("pull: get the remote sqlite database"); - await exec({ command: "mkdir", args: ["-p", context.PULL] }); - const remoteDatabase = join( - context.PULL, - `${remote}:${prefix}---${new Date().toISOString()}.sqlite3`, - ); - // delete all but the most recent remote database files for this remote/prefix (?). - const oldDbFiles = (await readdir(context.PULL)) - .sort() - .filter((x) => x.startsWith(`${remote}:${prefix}---`)) - .slice(0, -NUM_DB_TO_KEEP); - for (const path of oldDbFiles) { - await unlink(join(context.PULL, path)); - } - - await exec({ - command: "scp", - args: [`${remote}:/${databaseFilename(prefix)}`, remoteDatabase], - }); - - logger.debug("pull: compare state"); - const recent = - filesystem != null - ? [get(filesystem, remoteDatabase)] - : getRecent({ cutoff, databaseFile: remoteDatabase }); - const toUpdate: { remoteFs: Filesystem; localFs?: Filesystem }[] = []; - for (const fs of recent) { - const remoteFs = get(fs, remoteDatabase); - if (!filesystemExists(fs)) { - toUpdate.push({ remoteFs }); - } else { - const localFs = get(fs); - if (remoteFs.archived != localFs.archived) { - // different archive state, so needs an update to resolve this (either way) - toUpdate.push({ remoteFs, localFs }); - continue; - } - if (deleteSnapshots) { - // sync if *any* snapshots differ - if (!isEqual(remoteFs.snapshots, localFs.snapshots)) { - toUpdate.push({ remoteFs, localFs }); - } - } else { - // only sync if newest snapshots are different - const newestRemoteSnapshot = - remoteFs.snapshots[remoteFs.snapshots.length - 1]; - if (!newestRemoteSnapshot) { - // no snapshots yet, so nothing to do. - continue; - } - const newestLocalSnapshot = - localFs.snapshots[localFs.snapshots.length - 1]; - if ( - !newestLocalSnapshot || - newestRemoteSnapshot > newestLocalSnapshot - ) { - toUpdate.push({ remoteFs, localFs }); - } - } - } - } - - logger.debug(`pull: toUpdate.length = ${toUpdate.length}`); - if (!dryRun) { - for (const x of toUpdate) { - logger.debug("pull: updating ", x); - await pullOne({ ...x, remote, deleteSnapshots }); - } - } - - const toDelete: RawFilesystem[] = []; - if (deleteFilesystemCutoff) { - for (const fs of getRecent({ cutoff })) { - if (!filesystemExists(fs, remoteDatabase)) { - if (new Date(fs.last_edited ?? 0) <= deleteFilesystemCutoff) { - // it's old enough to delete: - toDelete.push(fs); - } - } - } - } - logger.debug(`pull: toDelete.length = ${toDelete.length}`); - if (!dryRun) { - for (const fs of toDelete) { - logger.debug("pull: deleting", fs); - await deleteFilesystem(fs); - } - } - - return { toUpdate, toDelete }; -} - -async function pullOne({ - remoteFs, - localFs, - remote, - deleteSnapshots, -}: { - remoteFs: Filesystem; - localFs?: Filesystem; - remote?: string; - deleteSnapshots?: boolean; -}) { - logger.debug("pull:", { remoteFs, localFs, remote, deleteSnapshots }); - if (localFs == null) { - localFs = await createFilesystem(remoteFs); - } - - // sync last_edited, affinity and nfs fields in all cases - set({ - ...primaryKey(localFs), - last_edited: remoteFs.last_edited, - affinity: remoteFs.affinity, - nfs: remoteFs.nfs, - }); - - if (localFs.archived && !remoteFs.archived) { - // it's back in use: - await dearchiveFilesystem(localFs); - // don't return -- will then possibly sync more below, in case of new changes - } else if (!localFs.archived && remoteFs.archived) { - // we just archive ours. Note in theory there is a chance - // that our local version is not update-to-date with the remote - // version. However, the point of archiving is it should only happen - // many weeks after a filesystem stopped being used, and by that - // point we should have already pull'd the latest version. - // Don't bother worrying about deleting snapshots. - await archiveFilesystem(localFs); - return; - } - if (localFs.archived && remoteFs.archived) { - // nothing to do - // Also, don't bother worrying about deleting snapshots, since can't. - return; - } - const snapshot = newestCommonSnapshot(localFs.snapshots, remoteFs.snapshots); - const newest_snapshot = remoteFs.snapshots[remoteFs.snapshots.length - 1]; - if (!newest_snapshot || snapshot == newest_snapshot) { - logger.debug("pull: already have the newest snapshot locally"); - } else { - const mountpoint = filesystemMountpoint(localFs); - try { - if (!snapshot) { - // full replication with nothing local - await exec({ - verbose: true, - command: `ssh ${remote} "zfs send -e -c -R ${filesystemDataset(remoteFs)}@${newest_snapshot}" | sudo zfs recv -o mountpoint=${mountpoint} -F ${filesystemDataset(localFs)}`, - what: { - ...localFs, - desc: "pull: doing a full receive from remote", - }, - }); - } else { - // incremental based on the last common snapshot - const force = - localFs.snapshots[localFs.snapshots.length - 1] == snapshot - ? "" - : " -F "; - await exec({ - verbose: true, - command: `ssh ${remote} "zfs send -e -c -I @${snapshot} ${filesystemDataset(remoteFs)}@${newest_snapshot}" | sudo zfs recv -o mountpoint=${mountpoint} -F ${filesystemDataset(localFs)} ${force}`, - what: { - ...localFs, - desc: "pull: doing an incremental replication from remote", - }, - }); - } - } finally { - // even if there was an error, update local snapshots, since we likely have some new - // ones (e.g., even if there was a partial receive, interrupted by a network drop). - await getSnapshots(localFs); - } - } - - if (deleteSnapshots) { - // In general due to snapshot trimming, the - // list of snapshots on local might NOT match remote, but after replication - // local will always have a *supserset* of remote. We thus may have to - // trim some snapshots: - const remoteSnapshots = new Set(remoteFs.snapshots); - const localSnapshots = get(localFs).snapshots; - for (const snapshot of localSnapshots) { - if (!remoteSnapshots.has(snapshot)) { - await deleteSnapshot({ ...localFs, snapshot }); - } - } - } -} - -// s0 and s1 are sorted oldest-to-newest lists of names of snapshots. -// return largest that is in common between the two or undefined if nothing is in common -function newestCommonSnapshot(s0: string[], s1: string[]) { - const t1 = new Set(s1); - for (let i = s0.length - 1; i >= 0; i--) { - if (t1.has(s0[i])) { - return s0[i]; - } - } -} diff --git a/src/packages/file-server/zfs/snapshots.ts b/src/packages/file-server/zfs/snapshots.ts deleted file mode 100644 index c2d3ab4e7f..0000000000 --- a/src/packages/file-server/zfs/snapshots.ts +++ /dev/null @@ -1,315 +0,0 @@ -/* -Manage creating and deleting rolling snapshots of a filesystem. - -We keep track of all state in the sqlite database, so only have to touch -ZFS when we actually need to do something. Keep this in mind though since -if you try to mess with snapshots directly then the sqlite database won't -know you did that. -*/ - -import { exec } from "./util"; -import { get, getRecent, set } from "./db"; -import { filesystemDataset, filesystemMountpoint } from "./names"; -import { splitlines } from "@cocalc/util/misc"; -import getLogger from "@cocalc/backend/logger"; -import { - SNAPSHOT_INTERVAL_MS, - SNAPSHOT_INTERVALS_MS, - SNAPSHOT_COUNTS, -} from "./config"; -import { syncProperties } from "./properties"; -import { primaryKey, type PrimaryKey } from "./types"; -import { isEqual } from "lodash"; - -const logger = getLogger("file-server:zfs/snapshots"); - -export async function maintainSnapshots(cutoff?: Date) { - await deleteExtraSnapshotsOfActiveFilesystems(cutoff); - await snapshotActiveFilesystems(cutoff); -} - -// If there any changes to the filesystem since the last snapshot, -// and there are no snapshots since SNAPSHOT_INTERVAL_MS ms ago, -// make a new one. Always returns the most recent snapshot name. -// Error if filesystem is archived. -export async function createSnapshot({ - force, - ifChanged, - ...fs -}: PrimaryKey & { - force?: boolean; - // note -- ifChanged is VERY fast, but it's not instantaneous... - ifChanged?: boolean; -}): Promise { - logger.debug("createSnapshot: ", fs); - const pk = primaryKey(fs); - const { pool, archived, snapshots } = get(pk); - if (archived) { - throw Error("cannot snapshot an archived filesystem"); - } - if (!force && !ifChanged && snapshots.length > 0) { - // check for sufficiently recent snapshot - const last = new Date(snapshots[snapshots.length - 1]); - if (Date.now() - last.valueOf() < SNAPSHOT_INTERVAL_MS) { - // snapshot sufficiently recent - return snapshots[snapshots.length - 1]; - } - } - - // Check to see if nothing change on disk since last snapshot - if so, don't make a new one: - if (!force && snapshots.length > 0) { - const written = await getWritten(pk); - if (written == 0) { - // for sure definitely nothing written, so no possible - // need to make a snapshot - return snapshots[snapshots.length - 1]; - } - } - - const snapshot = new Date().toISOString(); - await exec({ - verbose: true, - command: "sudo", - args: [ - "zfs", - "snapshot", - `${filesystemDataset({ ...pk, pool })}@${snapshot}`, - ], - what: { ...pk, desc: "creating snapshot of project" }, - }); - set({ - ...pk, - snapshots: ({ snapshots }) => [...snapshots, snapshot], - }); - syncProperties(pk); - return snapshot; -} - -async function getWritten(fs: PrimaryKey) { - const pk = primaryKey(fs); - const { pool } = get(pk); - const { stdout } = await exec({ - verbose: true, - command: "zfs", - args: ["list", "-Hpo", "written", filesystemDataset({ ...pk, pool })], - what: { - ...pk, - desc: "getting amount of newly written data in project since last snapshot", - }, - }); - return parseInt(stdout); -} - -export async function zfsGetSnapshots(dataset: string) { - const { stdout } = await exec({ - command: "zfs", - args: ["list", "-j", "-o", "name", "-r", "-t", "snapshot", dataset], - }); - const snapshots = Object.keys(JSON.parse(stdout).datasets).map( - (name) => name.split("@")[1], - ); - return snapshots; -} - -// gets snapshots from disk via zfs *and* sets the list of snapshots -// in the database to match (and also updates sizes) -export async function getSnapshots(fs: PrimaryKey) { - const pk = primaryKey(fs); - const filesystem = get(fs); - const snapshots = await zfsGetSnapshots(filesystemDataset(filesystem)); - if (!isEqual(snapshots, filesystem.snapshots)) { - set({ ...pk, snapshots }); - syncProperties(fs); - } - return snapshots; -} - -export async function deleteSnapshot({ - snapshot, - ...fs -}: PrimaryKey & { snapshot: string }) { - const pk = primaryKey(fs); - logger.debug("deleteSnapshot: ", pk, snapshot); - const { pool, last_send_snapshot } = get(pk); - if (snapshot == last_send_snapshot) { - throw Error( - "can't delete snapshot since it is the last one used for a zfs send", - ); - } - await exec({ - verbose: true, - command: "sudo", - args: [ - "zfs", - "destroy", - `${filesystemDataset({ ...pk, pool })}@${snapshot}`, - ], - what: { ...pk, desc: "destroying a snapshot of a project" }, - }); - set({ - ...pk, - snapshots: ({ snapshots }) => snapshots.filter((x) => x != snapshot), - }); - syncProperties(pk); -} - -/* -Remove snapshots according to our retention policy, and -never delete last_stream if set. - -Returns names of deleted snapshots. -*/ -export async function deleteExtraSnapshots(fs: PrimaryKey): Promise { - const pk = primaryKey(fs); - logger.debug("deleteExtraSnapshots: ", pk); - const { last_send_snapshot, snapshots } = get(pk); - if (snapshots.length == 0) { - // nothing to do - return []; - } - - // sorted from BIGGEST to smallest - const times = snapshots.map((x) => new Date(x).valueOf()); - times.reverse(); - const save = new Set(); - if (last_send_snapshot) { - save.add(new Date(last_send_snapshot).valueOf()); - } - for (const type in SNAPSHOT_COUNTS) { - const count = SNAPSHOT_COUNTS[type]; - const length_ms = SNAPSHOT_INTERVALS_MS[type]; - - // Pick the first count newest snapshots at intervals of length - // length_ms milliseconds. - let n = 0, - i = 0, - last_tm = 0; - while (n < count && i < times.length) { - const tm = times[i]; - if (!last_tm || tm <= last_tm - length_ms) { - save.add(tm); - last_tm = tm; - n += 1; // found one more - } - i += 1; // move to next snapshot - } - } - const toDelete = snapshots.filter((x) => !save.has(new Date(x).valueOf())); - for (const snapshot of toDelete) { - await deleteSnapshot({ ...pk, snapshot }); - } - return toDelete; -} - -// Go through ALL projects with last_edited >= cutoff stored -// here and run trimActiveFilesystemSnapshots. -export async function deleteExtraSnapshotsOfActiveFilesystems(cutoff?: Date) { - const v = getRecent({ cutoff }); - logger.debug( - `deleteSnapshotsOfActiveFilesystems: considering ${v.length} filesystems`, - ); - let i = 0; - for (const fs of v) { - if (fs.archived) { - continue; - } - try { - await deleteExtraSnapshots(fs); - } catch (err) { - logger.debug(`deleteSnapshotsOfActiveFilesystems: error -- ${err}`); - } - i += 1; - if (i % 10 == 0) { - logger.debug(`deleteSnapshotsOfActiveFilesystems: ${i}/${v.length}`); - } - } -} - -// Go through ALL projects with last_edited >= cutoff and snapshot them -// if they are due a snapshot. -// cutoff = a Date (default = 1 week ago) -export async function snapshotActiveFilesystems(cutoff?: Date) { - logger.debug("snapshotActiveFilesystems: getting..."); - const v = getRecent({ cutoff }); - logger.debug( - `snapshotActiveFilesystems: considering ${v.length} projects`, - cutoff, - ); - let i = 0; - for (const fs of v) { - if (fs.archived) { - continue; - } - try { - await createSnapshot(fs); - } catch (err) { - // error is already logged in error field of database - logger.debug(`snapshotActiveFilesystems: error -- ${err}`); - } - i += 1; - if (i % 10 == 0) { - logger.debug(`snapshotActiveFilesystems: ${i}/${v.length}`); - } - } -} - -/* -Get list of files modified since given snapshot (or last snapshot if not given). - -**There's probably no good reason to ever use this code!** - -The reason is because it's really slow, e.g., I added the -cocalc src directory (5000) files and it takes about 6 seconds -to run this. In contrast. "time find .", which lists EVERYTHING -takes less than 0.074s. You could do that before and after, then -compare them, and it'll be a fraction of a second. -*/ -interface Mod { - time: number; - change: "-" | "+" | "M" | "R"; // remove/create/modify/rename - // see "man zfs diff": - type: "B" | "C" | "/" | ">" | "|" | "@" | "P" | "=" | "F"; - path: string; -} - -export async function getModifiedFiles({ - snapshot, - ...fs -}: PrimaryKey & { snapshot: string }) { - const pk = primaryKey(fs); - logger.debug(`getModifiedFiles: `, pk); - const { pool, snapshots } = get(pk); - if (snapshots.length == 0) { - return []; - } - if (snapshot == null) { - snapshot = snapshots[snapshots.length - 1]; - } - const { stdout } = await exec({ - verbose: true, - command: "sudo", - args: [ - "zfs", - "diff", - "-FHt", - `${filesystemDataset({ ...pk, pool })}@${snapshot}`, - ], - what: { ...pk, desc: "getting files modified since last snapshot" }, - }); - const mnt = filesystemMountpoint(pk) + "/"; - const files: Mod[] = []; - for (const line of splitlines(stdout)) { - const x = line.split(/\t/g); - let path = x[3]; - if (path.startsWith(mnt)) { - path = path.slice(mnt.length); - } - files.push({ - time: parseFloat(x[0]) * 1000, - change: x[1] as any, - type: x[2] as any, - path, - }); - } - return files; -} diff --git a/src/packages/file-server/zfs/streams.ts b/src/packages/file-server/zfs/streams.ts deleted file mode 100644 index 6720c21798..0000000000 --- a/src/packages/file-server/zfs/streams.ts +++ /dev/null @@ -1,253 +0,0 @@ -/* -Send/Receive incremental replication streams of a filesystem. -*/ - -import { type PrimaryKey } from "./types"; -import { get, getRecent, set } from "./db"; -import getLogger from "@cocalc/backend/logger"; -import { - filesystemStreamsPath, - filesystemStreamsFilename, - filesystemDataset, -} from "./names"; -import { exec } from "./util"; -import { split } from "@cocalc/util/misc"; -import { join } from "path"; -import { getSnapshots } from "./snapshots"; -import { STREAM_INTERVAL_MS, MAX_STREAMS } from "./config"; - -const logger = getLogger("file-server:zfs:send"); - -export async function send(fs: PrimaryKey) { - const filesystem = get(fs); - if (filesystem.archived) { - logger.debug("filesystem is archived, so nothing to do", fs); - return; - } - const { snapshots } = filesystem; - const newest_snapshot = snapshots[snapshots.length - 1]; - if (!newest_snapshot) { - logger.debug("no snapshots yet"); - return; - } - if (newest_snapshot == filesystem.last_send_snapshot) { - logger.debug("no new snapshots", fs); - // the most recent snapshot is the same as the last one we used to make - // an archive, so nothing to do. - return; - } - await exec({ - command: "sudo", - args: ["mkdir", "-p", filesystemStreamsPath(filesystem)], - what: { ...filesystem, desc: "make send target directory" }, - }); - - let stream; - if (!filesystem.last_send_snapshot) { - logger.debug("doing first ever send -- a full send"); - stream = filesystemStreamsFilename({ - ...filesystem, - snapshot1: new Date(0).toISOString(), - snapshot2: newest_snapshot, - }); - try { - await exec({ - verbose: true, - command: `sudo sh -c 'zfs send -e -c -R ${filesystemDataset(filesystem)}@${newest_snapshot} > ${stream}.temp'`, - what: { - ...filesystem, - desc: "send: zfs send of full filesystem dataset (first full send)", - }, - }); - } catch (err) { - await exec({ - verbose: true, - command: "sudo", - args: ["rm", `${stream}.temp`], - }); - throw err; - } - } else { - logger.debug("doing incremental send"); - const snapshot1 = filesystem.last_send_snapshot; - const snapshot2 = newest_snapshot; - stream = filesystemStreamsFilename({ - ...filesystem, - snapshot1, - snapshot2, - }); - try { - await exec({ - verbose: true, - command: `sudo sh -c 'zfs send -e -c -I @${snapshot1} ${filesystemDataset(filesystem)}@${snapshot2} > ${stream}.temp'`, - what: { - ...filesystem, - desc: "send: zfs incremental send", - }, - }); - } catch (err) { - await exec({ - verbose: true, - command: "sudo", - args: ["rm", `${stream}.temp`], - }); - throw err; - } - } - await exec({ - verbose: true, - command: "sudo", - args: ["mv", `${stream}.temp`, stream], - }); - set({ ...fs, last_send_snapshot: newest_snapshot }); -} - -async function getStreams(fs: PrimaryKey) { - const filesystem = get(fs); - const streamsPath = filesystemStreamsPath(filesystem); - const { stdout } = await exec({ - command: "sudo", - args: ["ls", streamsPath], - what: { ...filesystem, desc: "getting list of streams" }, - }); - return split(stdout.trim()).filter((path) => path.endsWith(".zfs")); -} - -export async function recv(fs: PrimaryKey) { - const filesystem = get(fs); - if (filesystem.archived) { - throw Error("filesystem must not be archived"); - } - const streams = await getStreams(filesystem); - if (streams.length == 0) { - logger.debug("no streams"); - return; - } - const { snapshots } = filesystem; - const newest_snapshot = snapshots[snapshots.length - 1] ?? ""; - const toRead = streams.filter((snapshot) => snapshot >= newest_snapshot); - if (toRead.length == 0) { - return; - } - const streamsPath = filesystemStreamsPath(filesystem); - try { - for (const stream of toRead) { - await exec({ - verbose: true, - command: `sudo sh -c 'cat ${join(streamsPath, stream)} | zfs recv ${filesystemDataset(filesystem)}'`, - what: { - ...filesystem, - desc: `send: zfs incremental receive`, - }, - }); - } - } finally { - // ensure snapshots and size info in our database is up to date: - await getSnapshots(fs); - } -} - -function getRange(streamName) { - const v = streamName.split("Z-"); - return { snapshot1: v + "Z", snapshot2: v[1].slice(0, -".zfs".length) }; -} - -// Replace older streams so that there are at most maxStreams total streams. -export async function recompact({ - maxStreams, - ...fs -}: PrimaryKey & { maxStreams: number }) { - const filesystem = get(fs); - const { snapshots } = filesystem; - const streams = await getStreams(filesystem); - if (streams.length <= maxStreams) { - // nothing to do - return; - } - if (maxStreams < 1) { - throw Error("maxStreams must be at least 1"); - } - // replace first n streams by one full replication stream - let n = streams.length - maxStreams + 1; - let snapshot2 = getRange(streams[n - 1]).snapshot2; - while (!snapshots.includes(snapshot2) && n < streams.length) { - snapshot2 = getRange(streams[n]).snapshot2; - if (snapshots.includes(snapshot2)) { - break; - } - n += 1; - } - if (!snapshots.includes(snapshot2)) { - throw Error( - "bug -- this can't happen because we never delete the last snapshot used for send", - ); - } - - const stream = filesystemStreamsFilename({ - ...filesystem, - snapshot1: new Date(0).toISOString(), - snapshot2, - }); - try { - await exec({ - verbose: true, - command: `sudo sh -c 'zfs send -e -c -R ${filesystemDataset(filesystem)}@${snapshot2} > ${stream}.temp'`, - what: { - ...filesystem, - desc: "send: zfs send of full filesystem dataset (first full send)", - }, - }); - // if this rm were to fail, then things would be left in a broken state, - // since ${stream}.temp also gets deleted in the catch. But it seems - // highly unlikely this rm of the old streams would ever fail. - const path = filesystemStreamsPath(filesystem); - await exec({ - verbose: true, - command: "sudo", - // full paths to the first n streams: - args: ["rm", "-f", ...streams.slice(0, n).map((x) => join(path, x))], - }); - await exec({ - verbose: true, - command: "sudo", - args: ["mv", `${stream}.temp`, stream], - }); - } catch (err) { - await exec({ - verbose: true, - command: "sudo", - args: ["rm", "-f", `${stream}.temp`], - }); - throw err; - } -} - -// Go through ALL filesystems with last_edited >= cutoff and send a stream if due, -// and also ensure number of streams isn't too large. -export async function maintainStreams(cutoff?: Date) { - logger.debug("backupActiveFilesystems: getting..."); - const v = getRecent({ cutoff }); - logger.debug(`maintainStreams: considering ${v.length} filesystems`, cutoff); - let i = 0; - for (const { archived, last_edited, last_send_snapshot, ...pk } of v) { - if (archived || !last_edited) { - continue; - } - const age = - new Date(last_edited).valueOf() - new Date(last_send_snapshot ?? 0).valueOf(); - if (age < STREAM_INTERVAL_MS) { - // there's a new enough stream already - continue; - } - try { - await send(pk); - await recompact({ ...pk, maxStreams: MAX_STREAMS }); - } catch (err) { - logger.debug(`maintainStreams: error -- ${err}`); - } - i += 1; - if (i % 10 == 0) { - logger.debug(`maintainStreams: ${i}/${v.length}`); - } - } -} diff --git a/src/packages/file-server/zfs/test/archive.test.ts b/src/packages/file-server/zfs/test/archive.test.ts deleted file mode 100644 index dfbb003bdb..0000000000 --- a/src/packages/file-server/zfs/test/archive.test.ts +++ /dev/null @@ -1,105 +0,0 @@ -/* -DEVELOPMENT: - -pnpm exec jest --watch archive.test.ts -*/ - -import { executeCode } from "@cocalc/backend/execute-code"; -import { createTestPools, deleteTestPools, init, describe } from "./util"; -import { - archiveFilesystem, - dearchiveFilesystem, - createFilesystem, - createSnapshot, - getSnapshots, - get, -} from "@cocalc/file-server/zfs"; -import { filesystemMountpoint } from "@cocalc/file-server/zfs/names"; -import { readFile, writeFile } from "fs/promises"; -import { join } from "path"; - -describe("create a project, put in some files/snapshot, archive the project, confirm gone, de-archive it, and confirm files are back as expected", () => { - jest.setTimeout(10000); - let x: any = null; - - beforeAll(async () => { - x = await createTestPools({ count: 1, size: "1G" }); - await init(); - }); - - afterAll(async () => { - if (x != null) { - await deleteTestPools(x); - } - }); - - const project_id = "00000000-0000-0000-0000-000000000001"; - const mnt = filesystemMountpoint({ project_id, namespace: "default" }); - const FILE_CONTENT = "hello"; - const FILENAME = "cocalc.txt"; - it("creates a project and write a file", async () => { - const filesystem = await createFilesystem({ - project_id, - }); - expect(filesystem.owner_type).toBe("project"); - expect(filesystem.owner_id).toBe(project_id); - const path = join(mnt, FILENAME); - await writeFile(path, FILE_CONTENT); - }); - - let snapshot1, snapshot2; - const FILE_CONTENT2 = "hello2"; - const FILENAME2 = "cocalc2.txt"; - - it("create a snapshot and write another file, so there is a nontrivial snapshot to be archived", async () => { - snapshot1 = await createSnapshot({ project_id }); - expect(!!snapshot1).toBe(true); - const path = join(mnt, FILENAME2); - await writeFile(path, FILE_CONTENT2); - snapshot2 = await createSnapshot({ project_id, force: true }); - expect(snapshot2).not.toEqual(snapshot1); - }); - - it("archive the project and checks project is no longer in zfs", async () => { - expect(get({ project_id }).archived).toBe(false); - await archiveFilesystem({ project_id }); - const { stdout } = await executeCode({ - command: "zfs", - args: ["list", x.pools[0]], - }); - expect(stdout).not.toContain(project_id); - expect(get({ project_id }).archived).toBe(true); - }); - - it("archiving an already archived project is an error", async () => { - await expect( - async () => await archiveFilesystem({ project_id }), - ).rejects.toThrow(); - }); - - it("dearchive project and verify zfs filesystem is back, along with files and snapshots", async () => { - let called = false; - await dearchiveFilesystem({ - project_id, - progress: () => { - called = true; - }, - }); - expect(called).toBe(true); - expect(get({ project_id }).archived).toBe(false); - - expect((await readFile(join(mnt, FILENAME))).toString()).toEqual( - FILE_CONTENT, - ); - expect((await readFile(join(mnt, FILENAME2))).toString()).toEqual( - FILE_CONTENT2, - ); - expect(await getSnapshots({ project_id })).toEqual([snapshot1, snapshot2]); - }); - - it("dearchiving an already de-archived project is an error", async () => { - await expect( - async () => await dearchiveFilesystem({ project_id }), - ).rejects.toThrow(); - }); -}); diff --git a/src/packages/file-server/zfs/test/create-types.test.ts b/src/packages/file-server/zfs/test/create-types.test.ts deleted file mode 100644 index 150adda7b5..0000000000 --- a/src/packages/file-server/zfs/test/create-types.test.ts +++ /dev/null @@ -1,78 +0,0 @@ -/* -DEVELOPMENT: - -pnpm exec jest --watch create-types.test.ts -*/ - -import { createTestPools, deleteTestPools, init, describe } from "./util"; -import { - createFilesystem, -} from "@cocalc/file-server/zfs"; -import type { Filesystem } from "../types"; - -describe("create some account and organization filesystems", () => { - let x: any = null; - - beforeAll(async () => { - x = await createTestPools({ count: 1, size: "1G" }); - await init(); - }); - - afterAll(async () => { - if (x != null) { - await deleteTestPools(x); - } - }); - - // Making these the same intentionally to ensure the filesystem properly - // does not distinguish types based on the owner_id. - const project_id = "00000000-0000-0000-0000-000000000001"; - const account_id = "00000000-0000-0000-0000-000000000001"; - const group_id = "00000000-0000-0000-0000-000000000001"; - const filesystems: Filesystem[] = []; - it("creates filesystems associated to the project, account and group", async () => { - const fs = await createFilesystem({ project_id }); - expect(fs.owner_id).toBe(project_id); - filesystems.push(fs); - const fs2 = await createFilesystem({ account_id, name: "cocalc" }); - expect(fs2.owner_id).toBe(account_id); - filesystems.push(fs2); - const fs3 = await createFilesystem({ group_id, name: "data" }); - expect(fs3.owner_id).toBe(group_id); - filesystems.push(fs3); - }); - - it("tries to create an account and group filesystem with empty name and gets an error", async () => { - expect(async () => { - await createFilesystem({ account_id }); - }).rejects.toThrow("name must be nonempty"); - expect(async () => { - await createFilesystem({ group_id }); - }).rejects.toThrow("name must be nonempty"); - }); - - it('for projects the name defaults to "home"', async () => { - expect(async () => { - await createFilesystem({ project_id, name: "" }); - }).rejects.toThrow("must be nonempty"); - expect(filesystems[0].name).toBe("home"); - }); - - it("name must be less than 64 characters", async () => { - let name = ""; - for (let i = 0; i < 63; i++) { - name += "x"; - } - await createFilesystem({ account_id, name }); - name += 1; - expect(async () => { - await createFilesystem({ account_id, name }); - }).rejects.toThrow("name must be at most 63 characters"); - }); - - it("name must not have 'funny characters'", async () => { - expect(async () => { - await createFilesystem({ account_id, name: "$%@!" }); - }).rejects.toThrow("name must only contain"); - }); -}); diff --git a/src/packages/file-server/zfs/test/create.test.ts b/src/packages/file-server/zfs/test/create.test.ts deleted file mode 100644 index 6204c4683e..0000000000 --- a/src/packages/file-server/zfs/test/create.test.ts +++ /dev/null @@ -1,256 +0,0 @@ -/* -DEVELOPMENT: - -pnpm exec jest --watch create.test.ts - - pnpm exec jest create.test.ts -b -*/ - -// application/typescript text -import { executeCode } from "@cocalc/backend/execute-code"; -import { createTestPools, deleteTestPools, init, describe, describe0 } from "./util"; -import { - createFilesystem, - createBackup, - deleteFilesystem, - getPools, -} from "@cocalc/file-server/zfs"; -import { filesystemMountpoint } from "@cocalc/file-server/zfs/names"; -import { readFile, writeFile } from "fs/promises"; -import { join } from "path"; -import { exists } from "@cocalc/backend/misc/async-utils-node"; -import { uuid } from "@cocalc/util/misc"; -import { map as asyncMap } from "awaiting"; - -describe0("test for zfs", () => { - it("checks for TEST_ZFS", () => { - if (!process.env.TEST_ZFS) { - // make sure people aren't silently overconfident... - console.log( - "WARNing: TEST_ZFS not set, so **SKIPPING ALL ZFS FILE SERVER TESTS!**", - ); - } - }); -}); - -describe("creates project, clone project, delete projects", () => { - let x: any = null; - - beforeAll(async () => { - x = await createTestPools({ count: 1, size: "1G" }); - await init(); - }); - - afterAll(async () => { - if (x != null) { - await deleteTestPools(x); - } - }); - - it("verifies there is a pool", async () => { - const { stdout } = await executeCode({ - command: "zpool", - args: ["list", x.pools[0]], - }); - expect(stdout).toContain(x.pools[0]); - expect(Object.keys(await getPools()).length).toBe(1); - }); - - const project_id = "00000000-0000-0000-0000-000000000001"; - it("creates a project", async () => { - const project = await createFilesystem({ - project_id, - }); - expect(project.owner_id).toBe(project_id); - }); - - it("verify project is in output of zfs list", async () => { - const { stdout } = await executeCode({ - command: "zfs", - args: ["list", "-r", x.pools[0]], - }); - expect(stdout).toContain(project_id); - }); - - const FILE_CONTENT = "hello"; - const FILENAME = "cocalc.txt"; - it("write a file to the project", async () => { - const path = join( - filesystemMountpoint({ project_id, namespace: "default" }), - FILENAME, - ); - await writeFile(path, FILE_CONTENT); - }); - - const project_id2 = "00000000-0000-0000-0000-000000000002"; - it("clones our project to make a second project", async () => { - const project2 = await createFilesystem({ - project_id: project_id2, - clone: { project_id }, - }); - expect(project2.owner_id).toBe(project_id2); - }); - - it("verify clone is in output of zfs list", async () => { - const { stdout } = await executeCode({ - command: "zfs", - args: ["list", "-r", x.pools[0]], - }); - expect(stdout).toContain(project_id2); - }); - - it("read file from the clone", async () => { - const path = join( - filesystemMountpoint({ project_id: project_id2, namespace: "default" }), - FILENAME, - ); - const content = (await readFile(path)).toString(); - expect(content).toEqual(FILE_CONTENT); - }); - - let BUP_DIR; - it("make a backup of project, so can see that it gets deleted below", async () => { - const x = await createBackup({ project_id }); - BUP_DIR = x.BUP_DIR; - expect(await exists(BUP_DIR)).toBe(true); - }); - - it("attempt to delete first project and get error", async () => { - try { - await deleteFilesystem({ project_id }); - throw Error("must throw"); - } catch (err) { - expect(`${err}`).toContain("filesystem has dependent clones"); - } - }); - - it("delete second project, then first project, works", async () => { - await deleteFilesystem({ project_id: project_id2 }); - await deleteFilesystem({ project_id }); - const { stdout } = await executeCode({ - command: "zfs", - args: ["list", "-r", x.pools[0]], - }); - expect(stdout).not.toContain(project_id); - expect(stdout).not.toContain(project_id2); - }); - - it("verifies bup backup is also gone", async () => { - expect(await exists(BUP_DIR)).toBe(false); - }); -}); - -describe("create two projects with the same project_id at the same time, but in different namespaces", () => { - let x: any = null; - - beforeAll(async () => { - x = await createTestPools({ count: 2, size: "1G" }); - await init(); - }); - - afterAll(async () => { - if (x != null) { - await deleteTestPools(x); - } - }); - - it("there are TWO pools this time", async () => { - expect(Object.keys(await getPools()).length).toBe(2); - }); - - const project_id = "00000000-0000-0000-0000-000000000001"; - it("creates two projects", async () => { - const project = await createFilesystem({ - project_id, - namespace: "default", - }); - expect(project.owner_id).toBe(project_id); - - const project2 = await createFilesystem({ - project_id, - namespace: "test", - }); - expect(project2.owner_id).toBe(project_id); - // they are on different pools - expect(project.pool).not.toEqual(project2.pool); - }); - - it("two different entries in zfs list", async () => { - const { stdout: stdout0 } = await executeCode({ - command: "zfs", - args: ["list", "-r", x.pools[0]], - }); - expect(stdout0).toContain(project_id); - const { stdout: stdout1 } = await executeCode({ - command: "zfs", - args: ["list", "-r", x.pools[1]], - }); - expect(stdout1).toContain(project_id); - }); -}); - -describe("test the affinity property when creating projects", () => { - let x: any = null; - - beforeAll(async () => { - x = await createTestPools({ count: 2, size: "1G" }); - await init(); - }); - - afterAll(async () => { - if (x != null) { - await deleteTestPools(x); - } - }); - - const project_id = "00000000-0000-0000-0000-000000000001"; - const project_id2 = "00000000-0000-0000-0000-000000000002"; - const affinity = "math100"; - it("creates two projects with same afinity", async () => { - const project = await createFilesystem({ - project_id, - affinity, - }); - expect(project.owner_id).toBe(project_id); - - const project2 = await createFilesystem({ - project_id: project_id2, - affinity, - }); - expect(project2.owner_id).toBe(project_id2); - // they are on SAME pools, because of affinity - expect(project.pool).toEqual(project2.pool); - }); -}); - -describe("do a stress/race condition test creating a larger number of projects on a larger number of pools", () => { - let x: any = null; - - const count = 3; - const nprojects = 25; - - beforeAll(async () => { - x = await createTestPools({ count, size: "1G" }); - await init(); - }); - - afterAll(async () => { - if (x != null) { - await deleteTestPools(x); - } - }); - - it(`creates ${nprojects} projects in parallel on ${count} pools`, async () => { - const f = async (project_id) => { - await createFilesystem({ project_id }); - }; - const v: string[] = []; - for (let n = 0; n < nprojects; n++) { - v.push(uuid()); - } - // doing these in parallel and having it work is an important stress test, - // since we will get a bid speedup doing this in production, and there we - // will really need it. - await asyncMap(v, nprojects, f); - }); -}); diff --git a/src/packages/file-server/zfs/test/nfs.test.ts b/src/packages/file-server/zfs/test/nfs.test.ts deleted file mode 100644 index 56729b2798..0000000000 --- a/src/packages/file-server/zfs/test/nfs.test.ts +++ /dev/null @@ -1,118 +0,0 @@ -/* -DEVELOPMENT: - -pnpm exec jest --watch nfs.test.ts -*/ - -import { executeCode } from "@cocalc/backend/execute-code"; -import { - createTestPools, - deleteTestPools, - restartNfsServer, - init, - describe, -} from "./util"; -import { - createFilesystem, - createSnapshot, - get, - shareNFS, - unshareNFS, -} from "@cocalc/file-server/zfs"; -import { filesystemMountpoint } from "@cocalc/file-server/zfs/names"; -import { readFile, writeFile } from "fs/promises"; -import { join } from "path"; - -describe("create a project, put in a files, snapshot, another file, then share via NFS, mount and verify it works", () => { - let x: any = null; - const project_id = "00000000-0000-0000-0000-000000000001"; - let nsfMnt = ""; - - beforeAll(async () => { - x = await createTestPools({ count: 1, size: "1G" }); - nsfMnt = join(x.tempDir, project_id); - await init(); - }); - - afterAll(async () => { - if (x != null) { - await deleteTestPools(x); - } - }); - - const mnt = filesystemMountpoint({ project_id, namespace: "default" }); - const FILE_CONTENT = "hello"; - const FILENAME = "cocalc.txt"; - it("creates a project and write a file", async () => { - const project = await createFilesystem({ - project_id, - }); - expect(project.owner_id).toBe(project_id); - const path = join(mnt, FILENAME); - await writeFile(path, FILE_CONTENT); - }); - - let snapshot1, snapshot2; - const FILE_CONTENT2 = "hello2"; - const FILENAME2 = "cocalc2.txt"; - - it("create a snapshot and write another file, so there is a nontrivial snapshot to view through NFS", async () => { - snapshot1 = await createSnapshot({ project_id }); - expect(!!snapshot1).toBe(true); - const path = join(mnt, FILENAME2); - await writeFile(path, FILE_CONTENT2); - snapshot2 = await createSnapshot({ project_id, force: true }); - expect(snapshot2).not.toEqual(snapshot1); - }); - - let host = ""; - const client = "127.0.0.1"; - - const mount = async () => { - await executeCode({ - command: "sudo", - args: ["mkdir", "-p", nsfMnt], - }); - await executeCode({ - command: "sudo", - args: ["mount", host, nsfMnt], - }); - }; - - it("shares the project via NFS, and mounts it", async () => { - host = await shareNFS({ project_id, client }); - const project = get({ project_id }); - expect(project.nfs).toEqual([client]); - await mount(); - }); - - it("confirms our files and snapshots are there as expected", async () => { - const { stdout } = await executeCode({ - command: "sudo", - args: ["ls", nsfMnt], - }); - expect(stdout).toContain(FILENAME); - expect(stdout).toContain(FILENAME2); - expect((await readFile(join(nsfMnt, FILENAME))).toString()).toEqual( - FILE_CONTENT, - ); - expect((await readFile(join(nsfMnt, FILENAME2))).toString()).toEqual( - FILE_CONTENT2, - ); - }); - - it("stop NFS share and confirms it no longers works", async () => { - await executeCode({ - command: "sudo", - args: ["umount", nsfMnt], - }); - await restartNfsServer(); - await unshareNFS({ project_id, client }); - try { - await mount(); - throw Error("bug -- mount should fail"); - } catch (err) { - expect(`${err}`).toMatch(/not permitted|denied/); - } - }); -}); diff --git a/src/packages/file-server/zfs/test/pull.test.ts b/src/packages/file-server/zfs/test/pull.test.ts deleted file mode 100644 index a2689d7270..0000000000 --- a/src/packages/file-server/zfs/test/pull.test.ts +++ /dev/null @@ -1,315 +0,0 @@ -/* -DEVELOPMENT: - -This tests pull replication by setting up two separate file-servers on disk locally -and doing pulls from one to the other over ssh. This involves password-less ssh -to root on localhost, and creating multiple pools, so use with caution and don't -expect this to work unless you really know what you're doing. -Also, these tests are going to take a while. - -Efficient powerful backup isn't trivial and is very valuable, so -its' worth the wait! - -pnpm exec jest --watch pull.test.ts -*/ - -import { join } from "path"; -import { createTestPools, deleteTestPools, init, describe } from "./util"; -import { - createFilesystem, - createSnapshot, - deleteSnapshot, - deleteFilesystem, - pull, - archiveFilesystem, - dearchiveFilesystem, -} from "@cocalc/file-server/zfs"; -import { context, setContext } from "@cocalc/file-server/zfs/config"; -import { filesystemMountpoint } from "@cocalc/file-server/zfs/names"; -import { readFile, writeFile } from "fs/promises"; -import { filesystemExists, get } from "@cocalc/file-server/zfs/db"; -import { SYNCED_FIELDS } from "../pull"; - -describe("create two separate file servers, then do pulls to sync one to the other under various conditions", () => { - let one: any = null, - two: any = null; - const prefix1 = context.PREFIX + ".1"; - const prefix2 = context.PREFIX + ".2"; - const remote = "root@localhost"; - - beforeAll(async () => { - one = await createTestPools({ count: 1, size: "1G", prefix: prefix1 }); - setContext({ prefix: prefix1 }); - await init(); - two = await createTestPools({ - count: 1, - size: "1G", - prefix: prefix2, - }); - setContext({ prefix: prefix2 }); - await init(); - }); - - afterAll(async () => { - await deleteTestPools(one); - await deleteTestPools(two); - }); - - it("creates a filesystem in pool one, writes a file and takes a snapshot", async () => { - setContext({ prefix: prefix1 }); - const fs = await createFilesystem({ - project_id: "00000000-0000-0000-0000-000000000001", - }); - await writeFile(join(filesystemMountpoint(fs), "a.txt"), "hello"); - await createSnapshot(fs); - expect(await filesystemExists(fs)).toEqual(true); - }); - - it("pulls filesystem one to filesystem two, and confirms the fs and file were indeed sync'd", async () => { - setContext({ prefix: prefix2 }); - expect( - await filesystemExists({ - project_id: "00000000-0000-0000-0000-000000000001", - }), - ).toEqual(false); - - // first dryRun - const { toUpdate, toDelete } = await pull({ - remote, - prefix: prefix1, - dryRun: true, - }); - expect(toDelete.length).toBe(0); - expect(toUpdate.length).toBe(1); - expect(toUpdate[0].remoteFs.owner_id).toEqual( - "00000000-0000-0000-0000-000000000001", - ); - expect(toUpdate[0].localFs).toBe(undefined); - - // now for real - const { toUpdate: toUpdate1, toDelete: toDelete1 } = await pull({ - remote, - prefix: prefix1, - }); - - expect(toDelete1).toEqual(toDelete); - expect(toUpdate1).toEqual(toUpdate); - const fs = { project_id: "00000000-0000-0000-0000-000000000001" }; - expect(await filesystemExists(fs)).toEqual(true); - expect( - (await readFile(join(filesystemMountpoint(fs), "a.txt"))).toString(), - ).toEqual("hello"); - - // nothing if we sync again: - const { toUpdate: toUpdate2, toDelete: toDelete2 } = await pull({ - remote, - prefix: prefix1, - }); - expect(toDelete2.length).toBe(0); - expect(toUpdate2.length).toBe(0); - }); - - it("creates another file in our filesystem, creates another snapshot, syncs again, and sees that the sync worked", async () => { - setContext({ prefix: prefix1 }); - const fs = { project_id: "00000000-0000-0000-0000-000000000001" }; - await writeFile(join(filesystemMountpoint(fs), "b.txt"), "cocalc"); - await createSnapshot({ ...fs, force: true }); - const { snapshots } = get(fs); - expect(snapshots.length).toBe(2); - - setContext({ prefix: prefix2 }); - await pull({ remote, prefix: prefix1 }); - - expect( - (await readFile(join(filesystemMountpoint(fs), "b.txt"))).toString(), - ).toEqual("cocalc"); - }); - - it("archives the project, does sync, and see the other one got archived", async () => { - const fs = { project_id: "00000000-0000-0000-0000-000000000001" }; - setContext({ prefix: prefix2 }); - const project2before = get(fs); - expect(project2before.archived).toBe(false); - - setContext({ prefix: prefix1 }); - await archiveFilesystem(fs); - const project1 = get(fs); - expect(project1.archived).toBe(true); - - setContext({ prefix: prefix2 }); - await pull({ remote, prefix: prefix1 }); - const project2 = get(fs); - expect(project2.archived).toBe(true); - expect(project1.last_edited).toEqual(project2.last_edited); - }); - - it("dearchives, does sync, then sees the other gets dearchived; this just tests that sync de-archives, but works even if there are no new snapshots", async () => { - const fs = { project_id: "00000000-0000-0000-0000-000000000001" }; - setContext({ prefix: prefix1 }); - await dearchiveFilesystem(fs); - const project1 = get(fs); - expect(project1.archived).toBe(false); - - setContext({ prefix: prefix2 }); - await pull({ remote, prefix: prefix1 }); - const project2 = get(fs); - expect(project2.archived).toBe(false); - }); - - it("archives project, does sync, de-archives project, adds another snapshot, then does sync, thus testing that sync both de-archives *and* pulls latest snapshot", async () => { - const fs = { project_id: "00000000-0000-0000-0000-000000000001" }; - setContext({ prefix: prefix1 }); - expect(get(fs).archived).toBe(false); - await archiveFilesystem(fs); - expect(get(fs).archived).toBe(true); - setContext({ prefix: prefix2 }); - await pull({ remote, prefix: prefix1 }); - expect(get(fs).archived).toBe(true); - - // now dearchive - setContext({ prefix: prefix1 }); - await dearchiveFilesystem(fs); - // write content - await writeFile(join(filesystemMountpoint(fs), "d.txt"), "hello"); - // snapshot - await createSnapshot({ ...fs, force: true }); - const project1 = get(fs); - - setContext({ prefix: prefix2 }); - await pull({ remote, prefix: prefix1 }); - const project2 = get(fs); - expect(project2.snapshots).toEqual(project1.snapshots); - expect(project2.archived).toBe(false); - }); - - it("deletes project, does sync, then sees the other does NOT gets deleted without passing the deleteFilesystemCutoff option, and also with deleteFilesystemCutoff an hour ago, but does get deleted with it now", async () => { - const fs = { project_id: "00000000-0000-0000-0000-000000000001" }; - setContext({ prefix: prefix1 }); - expect(await filesystemExists(fs)).toEqual(true); - await deleteFilesystem(fs); - expect(await filesystemExists(fs)).toEqual(false); - - setContext({ prefix: prefix2 }); - expect(await filesystemExists(fs)).toEqual(true); - await pull({ remote, prefix: prefix1 }); - expect(await filesystemExists(fs)).toEqual(true); - - await pull({ - remote, - prefix: prefix1, - deleteFilesystemCutoff: new Date(Date.now() - 1000 * 60 * 60), - }); - expect(await filesystemExists(fs)).toEqual(true); - - await pull({ - remote, - prefix: prefix1, - deleteFilesystemCutoff: new Date(), - }); - expect(await filesystemExists(fs)).toEqual(false); - }); - - const v = [ - { project_id: "00000000-0000-0000-0000-000000000001", affinity: "math" }, - { - account_id: "00000000-0000-0000-0000-000000000002", - name: "cocalc", - affinity: "math", - }, - { - group_id: "00000000-0000-0000-0000-000000000003", - namespace: "test", - name: "data", - affinity: "sage", - }, - ]; - it("creates 3 filesystems in 2 different namespaces, and confirms sync works", async () => { - setContext({ prefix: prefix1 }); - for (const fs of v) { - await createFilesystem(fs); - } - // write files to fs2 and fs3, so data will get sync'd too - await writeFile(join(filesystemMountpoint(v[1]), "a.txt"), "hello"); - await writeFile(join(filesystemMountpoint(v[2]), "b.txt"), "cocalc"); - // snapshot - await createSnapshot({ ...v[1], force: true }); - await createSnapshot({ ...v[2], force: true }); - const p = v.map((x) => get(x)); - - // do the sync - setContext({ prefix: prefix2 }); - await pull({ remote, prefix: prefix1 }); - - // verify that we have everything - for (const fs of v) { - expect(await filesystemExists(fs)).toEqual(true); - } - const p2 = v.map((x) => get(x)); - for (let i = 0; i < p.length; i++) { - // everything matches (even snapshots, since no trimming happened) - for (const field of SYNCED_FIELDS) { - expect({ i, field, value: p[i][field] }).toEqual({ - i, - field, - value: p2[i][field], - }); - } - } - }); - - it("edits some files on one of the above filesystems, snapshots, sync's, goes back and deletes a snapshot, edits more files, sync's, and notices that snapshots on sync target properly match snapshots on source.", async () => { - // edits some files on one of the above filesystems, snapshots: - setContext({ prefix: prefix1 }); - await writeFile(join(filesystemMountpoint(v[1]), "a2.txt"), "hello2"); - await createSnapshot({ ...v[1], force: true }); - - // sync's - setContext({ prefix: prefix2 }); - await pull({ remote, prefix: prefix1 }); - - // delete snapshot - setContext({ prefix: prefix1 }); - const fs1 = get(v[1]); - await deleteSnapshot({ ...v[1], snapshot: fs1.snapshots[0] }); - - // do more edits and make another snapshot - await writeFile(join(filesystemMountpoint(v[1]), "a3.txt"), "hello3"); - await createSnapshot({ ...v[1], force: true }); - const snapshots1 = get(v[1]).snapshots; - - // sync - setContext({ prefix: prefix2 }); - await pull({ remote, prefix: prefix1 }); - - // snapshots do NOT initially match, since we didn't enable snapshot deleting! - let snapshots2 = get(v[1]).snapshots; - expect(snapshots1).not.toEqual(snapshots2); - - await pull({ remote, prefix: prefix1, deleteSnapshots: true }); - // now snapshots should match exactly! - snapshots2 = get(v[1]).snapshots; - expect(snapshots1).toEqual(snapshots2); - }); - - it("test directly pulling one filesystem, rather than doing a full sync", async () => { - setContext({ prefix: prefix1 }); - await writeFile(join(filesystemMountpoint(v[1]), "a3.txt"), "hello2"); - await createSnapshot({ ...v[1], force: true }); - await writeFile(join(filesystemMountpoint(v[2]), "a4.txt"), "hello"); - await createSnapshot({ ...v[2], force: true }); - const p = v.map((x) => get(x)); - - setContext({ prefix: prefix2 }); - await pull({ remote, prefix: prefix1, filesystem: v[1] }); - const p2 = v.map((x) => get(x)); - - // now filesystem 1 should match, but not filesystem 2 - expect(p[1].snapshots).toEqual(p2[1].snapshots); - expect(p[2].snapshots).not.toEqual(p2[2].snapshots); - - // finally a full sync will get filesystem 2 - await pull({ remote, prefix: prefix1 }); - const p2b = v.map((x) => get(x)); - expect(p[2].snapshots).toEqual(p2b[2].snapshots); - }); -}); diff --git a/src/packages/file-server/zfs/test/util.ts b/src/packages/file-server/zfs/test/util.ts deleted file mode 100644 index 6d4b978297..0000000000 --- a/src/packages/file-server/zfs/test/util.ts +++ /dev/null @@ -1,111 +0,0 @@ -// application/typescript text -import { context, setContext } from "@cocalc/file-server/zfs/config"; -import { mkdtemp, rm } from "fs/promises"; -import { tmpdir } from "os"; -import { join } from "path"; -import { executeCode } from "@cocalc/backend/execute-code"; -import { initDataDir } from "@cocalc/file-server/zfs/util"; -import { resetDb } from "@cocalc/file-server/zfs/db"; -import { getPools } from "@cocalc/file-server/zfs/pools"; -import { map as asyncMap } from "awaiting"; - -// export "describe" from here that is a no-op unless TEST_ZFS is set - -const Describe = process.env.TEST_ZFS ? describe : describe.skip; -const describe0 = describe; -export { Describe as describe, describe0 }; - -export async function init() { - if (!context.PREFIX.includes("test")) { - throw Error("context.PREFIX must contain 'test'"); - } - await initDataDir(); - resetDb(); -} - -export async function createTestPools({ - size = "10G", - count = 1, - prefix, -}: { - size?: string; - count?: number; - prefix?: string; -}): Promise<{ tempDir: string; pools: string[]; prefix?: string }> { - setContext({ prefix }); - if (!context.PREFIX.includes("test")) { - throw Error(`context.PREFIX=${context.PREFIX} must contain 'test'`); - } - // Create temp directory - const tempDir = await mkdtemp(join(tmpdir(), "test-")); - const pools: string[] = []; - // in case pools left from a failing test: - for (const pool of Object.keys(await getPools())) { - try { - await executeCode({ - command: "sudo", - args: ["zpool", "destroy", pool], - }); - } catch {} - } - for (let n = 0; n < count; n++) { - const image = join(tempDir, `${n}`, "0.img"); - await executeCode({ - command: "mkdir", - args: [join(tempDir, `${n}`)], - }); - await executeCode({ - command: "truncate", - args: ["-s", size, image], - }); - const pool = `${context.PREFIX}-${n}`; - pools.push(pool); - await executeCode({ - command: "sudo", - args: ["zpool", "create", pool, image], - }); - } - // ensure pool cache is cleared: - await getPools({ noCache: true }); - return { tempDir, pools, prefix }; -} - -// Even after setting sharefnfs=off, it can be a while (a minute?) until NFS -// fully frees up the share so we can destroy the pool. This makes it instant, -// which is very useful for unit testing. -export async function restartNfsServer() { - await executeCode({ - command: "sudo", - args: ["service", "nfs-kernel-server", "restart"], - }); -} - -export async function deleteTestPools(x?: { - tempDir: string; - pools: string[]; - prefix?: string; -}) { - if (!x) { - return; - } - const { tempDir, pools, prefix } = x; - setContext({ prefix }); - if (!context.PREFIX.includes("test")) { - throw Error("context.PREFIX must contain 'test'"); - } - - const f = async (pool) => { - try { - await executeCode({ - command: "sudo", - args: ["zpool", "destroy", pool], - }); - } catch (err) { - // if (!`$err}`.includes("no such pool")) { - // console.log(err); - // } - } - }; - await asyncMap(pools, pools.length, f); - await rm(tempDir, { recursive: true }); -} diff --git a/src/packages/file-server/zfs/types.ts b/src/packages/file-server/zfs/types.ts deleted file mode 100644 index d4c5e44526..0000000000 --- a/src/packages/file-server/zfs/types.ts +++ /dev/null @@ -1,195 +0,0 @@ -import { context } from "./config"; -import { isValidUUID } from "@cocalc/util/misc"; - -export const OWNER_TYPES = ["account", "project", "group"] as const; - -export const OWNER_ID_FIELDS = OWNER_TYPES.map((x) => x + "_id"); - -export type OwnerType = (typeof OWNER_TYPES)[number]; - -export interface FilesystemPrimaryKey { - // The primary key (namespace, owner_type, owner_id, name): - - namespace: string; - // we support two types of filesystems: - // - 'project': owned by one project and can be used in various context associated with a single project; - // useful to all collaborators on a project. - // - 'account': owned by a user (a person) and be used in various ways on all projects they collaborator on. - // Other than the above distinction, the filesystems are treated identically by the server. - owner_type: OwnerType; - // owner_id is either a project_id or an account_id or an group_id. - owner_id: string; - // The name of the filesystem. - name: string; -} - -// This isn't exactly a FilesystemPrimaryKey, but it is convenient to -// work with and it uniquely *defines* one (or throws an error), after -// being fed through the primaryKey function below. -export interface PrimaryKey { - namespace?: string; - owner_type?: OwnerType; - owner_id?: string; - name?: string; - account_id?: string; - project_id?: string; - group_id?: string; -} - -const zfsSegmentRegex = /^[a-zA-Z0-9 _\-.:]+$/; - -export function primaryKey({ - namespace = context.namespace, - owner_type, - owner_id, - name, - account_id, - project_id, - group_id, -}: PrimaryKey): FilesystemPrimaryKey { - if ( - (account_id ? 1 : 0) + - (project_id ? 1 : 0) + - (group_id ? 1 : 0) + - (owner_type ? 1 : 0) != - 1 - ) { - throw Error( - `exactly one of account_id, project_id, group_id, or owner_type must be specified: ${JSON.stringify({ account_id, project_id, group_id, owner_type })}`, - ); - } - if ( - (account_id ? 1 : 0) + - (project_id ? 1 : 0) + - (group_id ? 1 : 0) + - (owner_id ? 1 : 0) != - 1 - ) { - throw Error( - `exactly one of account_id, project_id, group_id, or owner_type must be specified: ${JSON.stringify({ account_id, project_id, group_id, owner_id })}`, - ); - } - if (account_id) { - owner_type = "account"; - owner_id = account_id; - } else if (project_id) { - owner_type = "project"; - owner_id = project_id; - } else if (group_id) { - owner_type = "group"; - owner_id = group_id; - } - if (!owner_type || !OWNER_TYPES.includes(owner_type)) { - throw Error( - `unknown owner type '${owner_type}' -- must be one of ${JSON.stringify(OWNER_TYPES)}`, - ); - } - if (!name) { - if (owner_type == "project" && name == null) { - // the home directory of a project. - name = "home"; - } else { - throw Error("name must be nonempty"); - } - } - if (name.length >= 64) { - // this is only so mounting is reasonable on the filesystem... and could be lifted - throw Error("name must be at most 63 characters"); - } - if (!zfsSegmentRegex.test(name)) { - throw Error( - 'name must only contain alphanumeric characters, space, *, "-", "_", "." and ":"', - ); - } - - if (!isValidUUID(owner_id) || !owner_id) { - throw Error("owner_id must be a valid uuid"); - } - - return { namespace, owner_type, owner_id, name }; -} - -export interface Filesystem extends FilesystemPrimaryKey { - // Properties of the filesystem and its current state: - - // the pool is where the filesystem happened to get allocated. This can be influenced by affinity or usage. - pool: string; - // true if project is currently archived - archived: boolean; - // array of hosts (or range using CIDR notation) that we're - // granting NFS client access to. - nfs: string[]; - // list of snapshots as ISO timestamps from oldest to newest - snapshots: string[]; - // name of the most recent snapshot that was used for sending a stream - // (for incremental backups). This specified snapshot will never be - // deleted by the snapshot trimming process, until a newer send snapshot is made. - last_send_snapshot?: string; - // name of most recent bup backup - last_bup_backup?: string; - // Last_edited = last time this project was "edited" -- various - // operations cause this to get updated. - last_edited?: Date; - // optional arbitrary affinity string - we attempt if possible to put - // projects with the same affinity in the same pool, to improve chances of dedup. - affinity?: string; - // if this is set, then some sort of error that "should" never happen, - // has happened, and manual intervention is needed. - error?: string; - // when the last error actually happened - last_error?: Date; - - // Bytes used by the main project filesystem dataset, NOT counting snapshots (zfs "USED"). - // Obviously these used_by fields are NOT always up to date. They get updated on some - // standard operations, including making snapshots, so can be pretty useful for monitoring - // for issues, etc. - used_by_dataset?: number; - // Total amount of space used by snapshots (not the filesystem) - used_by_snapshots?: number; - - // Quota for dataset usage (in bytes), so used_by_dataset <= dataset_quota. This is the refquota property in ZFS. - quota?: number; -} - -// Used for set(...), main thing being each field can be FilesystemFieldFunction, -// which makes it very easy to *safely* mutate data (assuming only one process -// is using sqlite). -type FilesystemFieldFunction = (project: Filesystem) => any; -export interface SetFilesystem extends PrimaryKey { - pool?: string | FilesystemFieldFunction; - archived?: boolean | FilesystemFieldFunction; - nfs?: string[] | FilesystemFieldFunction; - snapshots?: string[] | FilesystemFieldFunction; - last_send_snapshot?: string | FilesystemFieldFunction; - last_bup_backup?: string | FilesystemFieldFunction; - last_edited?: Date | FilesystemFieldFunction; - affinity?: null | string | FilesystemFieldFunction; - error?: null | string | FilesystemFieldFunction; - last_error?: Date | FilesystemFieldFunction; - used_by_dataset?: null | number; - used_by_snapshots?: null | number; - quota?: null | number; -} - -// what is *actually* stored in sqlite -export interface RawFilesystem { - owner_type: OwnerType; - owner_id: string; - namespace: string; - pool: string; - // 0 or 1 - archived?: number; - // nfs and snasphots are v.join(',') - nfs?: string; - snapshots?: string; - last_send_snapshot?: string; - last_bup_backup?: string; - // new Date().ISOString() - last_edited?: string; - affinity?: string; - error?: string; - last_error?: string; - used_by_dataset?: number; - used_by_snapshots?: number; - quota?: number; -} diff --git a/src/packages/file-server/zfs/util.ts b/src/packages/file-server/zfs/util.ts deleted file mode 100644 index 6f60893fd6..0000000000 --- a/src/packages/file-server/zfs/util.ts +++ /dev/null @@ -1,29 +0,0 @@ -import { executeCode } from "@cocalc/backend/execute-code"; -import { context, DEFAULT_EXEC_TIMEOUT_MS } from "./config"; -import { fatalError } from "./db"; - -export async function exec(opts) { - try { - return await executeCode({ - ...opts, - timeout: DEFAULT_EXEC_TIMEOUT_MS / 1000, - }); - } catch (err) { - if (opts.what) { - fatalError({ - ...opts.what, - err, - desc: `${opts.desc ? opts.desc : ""} "${opts.command} ${opts.args?.join(" ") ?? ""}"`, - }); - } - throw err; - } -} - -export async function initDataDir() { - await executeCode({ command: "sudo", args: ["mkdir", "-p", context.DATA] }); - await executeCode({ - command: "sudo", - args: ["chmod", "a+rxw", context.DATA], - }); -} diff --git a/src/packages/package.json b/src/packages/package.json index b3ef6c6886..fac035d500 100644 --- a/src/packages/package.json +++ b/src/packages/package.json @@ -30,9 +30,10 @@ "tar-fs@3.0.8": "3.0.9" }, "onlyBuiltDependencies": [ - "better-sqlite3", "websocket-sftp", "websocketfs", + "zeromq", + "better-sqlite3", "zstd-napi" ] } diff --git a/src/packages/pnpm-lock.yaml b/src/packages/pnpm-lock.yaml index c794c4cb5e..1fe4499fe0 100644 --- a/src/packages/pnpm-lock.yaml +++ b/src/packages/pnpm-lock.yaml @@ -33,10 +33,10 @@ importers: version: 5.0.1 jest: specifier: ^29.7.0 - version: 29.7.0(@types/node@24.0.10) + version: 29.7.0(@types/node@18.19.118) ts-jest: specifier: ^29.2.3 - version: 29.4.0(@babel/core@7.26.9)(@jest/transform@29.7.0)(@jest/types@30.0.1)(babel-jest@29.7.0(@babel/core@7.26.9))(jest-util@30.0.2)(jest@29.7.0(@types/node@24.0.10))(typescript@5.8.3) + version: 29.4.0(@babel/core@7.28.0)(@jest/transform@29.7.0)(@jest/types@30.0.1)(babel-jest@29.7.0(@babel/core@7.28.0))(jest-util@30.0.2)(jest@29.7.0(@types/node@18.19.118))(typescript@5.8.3) typescript: specifier: ^5.7.3 version: 5.8.3 @@ -55,7 +55,7 @@ importers: devDependencies: '@types/node': specifier: ^18.16.14 - version: 18.19.115 + version: 18.19.118 assets: dependencies: @@ -64,7 +64,7 @@ importers: version: 3.7.1 url-loader: specifier: ^4.1.1 - version: 4.1.1(webpack@5.99.5(uglify-js@3.19.3)) + version: 4.1.1(webpack@5.100.1(uglify-js@3.19.3)) devDependencies: '@cocalc/backend': specifier: workspace:* @@ -132,7 +132,7 @@ importers: devDependencies: '@types/node': specifier: ^18.16.14 - version: 18.19.115 + version: 18.19.118 cdn: devDependencies: @@ -157,7 +157,7 @@ importers: devDependencies: '@types/node': specifier: ^18.16.14 - version: 18.19.115 + version: 18.19.118 conat: dependencies: @@ -221,7 +221,7 @@ importers: version: 4.17.20 '@types/node': specifier: ^18.16.14 - version: 18.19.115 + version: 18.19.118 database: dependencies: @@ -276,7 +276,7 @@ importers: version: 4.17.20 '@types/node': specifier: ^18.16.14 - version: 18.19.115 + version: 18.19.118 '@types/pg': specifier: ^8.6.1 version: 8.15.4 @@ -289,9 +289,6 @@ importers: '@cocalc/backend': specifier: workspace:* version: link:../backend - '@cocalc/conat': - specifier: workspace:* - version: link:../conat '@cocalc/file-server': specifier: workspace:* version: 'link:' @@ -301,19 +298,13 @@ importers: awaiting: specifier: ^3.0.0 version: 3.0.0 - better-sqlite3: - specifier: ^11.10.0 - version: 11.10.0 - lodash: - specifier: ^4.17.21 - version: 4.17.21 devDependencies: - '@types/better-sqlite3': - specifier: ^7.6.13 - version: 7.6.13 - '@types/lodash': - specifier: ^4.14.202 - version: 4.17.20 + '@types/jest': + specifier: ^29.5.14 + version: 29.5.14 + '@types/node': + specifier: ^18.16.14 + version: 18.19.118 frontend: dependencies: @@ -376,13 +367,13 @@ importers: version: 1.4.1 '@jupyter-widgets/base': specifier: ^4.1.1 - version: 4.1.7(crypto@1.0.1)(react@19.1.0) + version: 4.1.7(react@19.1.0) '@jupyter-widgets/controls': specifier: 5.0.0-rc.2 version: 5.0.0-rc.2(crypto@1.0.1)(react@19.1.0) '@jupyter-widgets/output': specifier: ^4.1.0 - version: 4.1.7(crypto@1.0.1)(react@19.1.0) + version: 4.1.7(react@19.1.0) '@microlink/react-json-view': specifier: ^1.23.3 version: 1.26.2(@types/react@19.1.8)(react-dom@19.1.0(react@19.1.0))(react@19.1.0) @@ -463,7 +454,7 @@ importers: version: 5.6.0 csv-stringify: specifier: ^6.3.0 - version: 6.5.2 + version: 6.6.0 d3: specifier: ^3.5.6 version: 3.5.17 @@ -574,7 +565,7 @@ importers: version: 4.10.38 plotly.js: specifier: ^2.29.1 - version: 2.35.3(@rspack/core@1.4.4(@swc/helpers@0.5.15))(mapbox-gl@1.13.3)(webpack@5.99.5) + version: 2.35.3(@rspack/core@1.4.6(@swc/helpers@0.5.15))(mapbox-gl@1.13.3)(webpack@5.100.1) project-name-generator: specifier: ^2.1.6 version: 2.1.9 @@ -610,7 +601,7 @@ importers: version: 7.1.11(react@19.1.0)(typescript@5.8.3) react-plotly.js: specifier: ^2.6.0 - version: 2.6.0(plotly.js@2.35.3(@rspack/core@1.4.4(@swc/helpers@0.5.15))(mapbox-gl@1.13.3)(webpack@5.99.5))(react@19.1.0) + version: 2.6.0(plotly.js@2.35.3(@rspack/core@1.4.6(@swc/helpers@0.5.15))(mapbox-gl@1.13.3)(webpack@5.100.1))(react@19.1.0) react-redux: specifier: ^9.2.0 version: 9.2.0(@types/react@19.1.8)(react@19.1.0) @@ -659,7 +650,7 @@ importers: devDependencies: '@cspell/dict-typescript': specifier: ^3.2.0 - version: 3.2.2 + version: 3.2.3 '@formatjs/cli': specifier: ^6.7.1 version: 6.7.2 @@ -785,7 +776,7 @@ importers: version: 3.5.4 http-proxy-3: specifier: ^1.20.8 - version: 1.20.8 + version: 1.20.10 lodash: specifier: ^4.17.21 version: 4.17.21 @@ -821,7 +812,7 @@ importers: version: 13.15.15 webpack-dev-middleware: specifier: ^7.4.2 - version: 7.4.2(webpack@5.99.5(uglify-js@3.19.3)) + version: 7.4.2(webpack@5.100.1(uglify-js@3.19.3)) webpack-hot-middleware: specifier: ^2.26.1 version: 2.26.1 @@ -831,7 +822,7 @@ importers: version: 4.17.23 '@types/node': specifier: ^18.16.14 - version: 18.19.115 + version: 18.19.118 coffeescript: specifier: ^2.5.1 version: 2.7.0 @@ -907,7 +898,7 @@ importers: version: 1.2.0 '@types/node': specifier: ^18.16.14 - version: 18.19.115 + version: 18.19.118 '@types/node-cleanup': specifier: ^2.1.2 version: 2.1.5 @@ -961,7 +952,7 @@ importers: version: 2.0.1 csv-stringify: specifier: ^6.3.0 - version: 6.5.2 + version: 6.6.0 dayjs: specifier: ^1.11.11 version: 1.11.13 @@ -979,13 +970,13 @@ importers: version: 2.1.2 next: specifier: 15.3.4 - version: 15.3.4(@babel/core@7.26.9)(@opentelemetry/api@1.9.0)(@playwright/test@1.51.1)(react-dom@19.1.0(react@19.1.0))(react@19.1.0)(sass@1.89.2) + version: 15.3.4(@babel/core@7.28.0)(@opentelemetry/api@1.9.0)(react-dom@19.1.0(react@19.1.0))(react@19.1.0)(sass@1.89.2) next-rest-framework: specifier: 6.0.0-beta.4 - version: 6.0.0-beta.4(zod@3.25.75) + version: 6.0.0-beta.4(zod@3.25.76) next-translate: specifier: ^2.6.2 - version: 2.6.2(next@15.3.4(@babel/core@7.26.9)(@opentelemetry/api@1.9.0)(@playwright/test@1.51.1)(react-dom@19.1.0(react@19.1.0))(react@19.1.0)(sass@1.89.2))(react@19.1.0) + version: 2.6.2(next@15.3.4(@babel/core@7.28.0)(@opentelemetry/api@1.9.0)(react-dom@19.1.0(react@19.1.0))(react@19.1.0)(sass@1.89.2))(react@19.1.0) password-hash: specifier: ^1.2.2 version: 1.2.2 @@ -1021,7 +1012,7 @@ importers: version: 3.1.1 zod: specifier: ^3.23.5 - version: 3.25.75 + version: 3.25.76 devDependencies: '@ant-design/cssinjs': specifier: ^1.23.0 @@ -1037,7 +1028,7 @@ importers: version: 4.17.23 '@types/node': specifier: ^18.16.14 - version: 18.19.115 + version: 18.19.118 '@types/react': specifier: ^19.1.8 version: 19.1.8 @@ -1049,7 +1040,7 @@ importers: version: 15.3.5(@swc/helpers@0.5.15)(webpack-hot-middleware@2.26.1) node-mocks-http: specifier: ^1.14.1 - version: 1.17.2(@types/express@4.17.23)(@types/node@18.19.115) + version: 1.17.2(@types/express@4.17.23)(@types/node@18.19.118) project: dependencies: @@ -1164,7 +1155,7 @@ importers: version: 4.17.20 '@types/node': specifier: ^18.16.14 - version: 18.19.115 + version: 18.19.118 '@types/primus': specifier: ^7.3.9 version: 7.3.9 @@ -1215,22 +1206,22 @@ importers: version: 1.4.1 '@langchain/anthropic': specifier: ^0.3.18 - version: 0.3.24(@langchain/core@0.3.62(openai@4.104.0(encoding@0.1.13)(ws@8.18.3)(zod@3.25.67))) + version: 0.3.24(@langchain/core@0.3.62(@opentelemetry/api@1.9.0)(openai@4.104.0(encoding@0.1.13)(ws@8.18.3)(zod@3.25.76))) '@langchain/core': specifier: ^0.3.46 - version: 0.3.62(openai@4.104.0(encoding@0.1.13)(ws@8.18.3)(zod@3.25.67)) + version: 0.3.62(@opentelemetry/api@1.9.0)(openai@4.104.0(encoding@0.1.13)(ws@8.18.3)(zod@3.25.76)) '@langchain/google-genai': specifier: ^0.2.4 - version: 0.2.14(@langchain/core@0.3.62(openai@4.104.0(encoding@0.1.13)(ws@8.18.3)(zod@3.25.67))) + version: 0.2.14(@langchain/core@0.3.62(@opentelemetry/api@1.9.0)(openai@4.104.0(encoding@0.1.13)(ws@8.18.3)(zod@3.25.76))) '@langchain/mistralai': specifier: ^0.2.0 - version: 0.2.1(@langchain/core@0.3.62(openai@4.104.0(encoding@0.1.13)(ws@8.18.3)(zod@3.25.67)))(zod@3.25.67) + version: 0.2.1(@langchain/core@0.3.62(@opentelemetry/api@1.9.0)(openai@4.104.0(encoding@0.1.13)(ws@8.18.3)(zod@3.25.76)))(zod@3.25.76) '@langchain/ollama': specifier: ^0.2.0 - version: 0.2.3(@langchain/core@0.3.62(openai@4.104.0(encoding@0.1.13)(ws@8.18.3)(zod@3.25.67))) + version: 0.2.3(@langchain/core@0.3.62(@opentelemetry/api@1.9.0)(openai@4.104.0(encoding@0.1.13)(ws@8.18.3)(zod@3.25.76))) '@langchain/openai': specifier: ^0.5.5 - version: 0.5.18(@langchain/core@0.3.62(openai@4.104.0(encoding@0.1.13)(ws@8.18.3)(zod@3.25.67)))(ws@8.18.3) + version: 0.5.18(@langchain/core@0.3.62(@opentelemetry/api@1.9.0)(openai@4.104.0(encoding@0.1.13)(ws@8.18.3)(zod@3.25.76)))(ws@8.18.3) '@node-saml/passport-saml': specifier: ^5.0.1 version: 5.0.1 @@ -1249,12 +1240,6 @@ importers: '@sendgrid/mail': specifier: ^8.1.4 version: 8.1.5 - '@socket.io/cluster-adapter': - specifier: ^0.2.2 - version: 0.2.2(socket.io-adapter@2.5.5) - '@socket.io/sticky': - specifier: ^1.0.4 - version: 1.0.4 '@zxcvbn-ts/core': specifier: ^3.0.4 version: 3.0.4 @@ -1347,7 +1332,7 @@ importers: version: 6.10.1 openai: specifier: ^4.95.1 - version: 4.104.0(encoding@0.1.13)(ws@8.18.3)(zod@3.25.67) + version: 4.104.0(encoding@0.1.13)(ws@8.18.3)(zod@3.25.76) parse-domain: specifier: ^5.0.0 version: 5.0.0(encoding@0.1.13) @@ -1429,7 +1414,7 @@ importers: version: 0.7.34 '@types/node': specifier: ^18.16.14 - version: 18.19.115 + version: 18.19.118 '@types/node-zendesk': specifier: ^2.0.15 version: 2.0.15 @@ -1469,10 +1454,10 @@ importers: devDependencies: '@rspack/cli': specifier: ^1.3.11 - version: 1.4.4(@rspack/core@1.4.4(@swc/helpers@0.5.15))(@types/express@4.17.23)(webpack@5.99.5) + version: 1.4.6(@rspack/core@1.4.6(@swc/helpers@0.5.15))(@types/express@4.17.23)(webpack@5.100.1) '@rspack/core': specifier: ^1.3.11 - version: 1.4.4(@swc/helpers@0.5.15) + version: 1.4.6(@swc/helpers@0.5.15) '@rspack/plugin-react-refresh': specifier: ^1.4.3 version: 1.4.3(react-refresh@0.17.0)(webpack-hot-middleware@2.26.1) @@ -1487,7 +1472,7 @@ importers: version: 3.5.32 '@types/node': specifier: ^18.16.14 - version: 18.19.115 + version: 18.19.118 '@types/react': specifier: ^19.1.8 version: 19.1.8 @@ -1523,19 +1508,19 @@ importers: version: 3.0.0 clean-webpack-plugin: specifier: ^4.0.0 - version: 4.0.0(webpack@5.99.5) + version: 4.0.0(webpack@5.100.1) coffee-cache: specifier: ^1.0.2 version: 1.0.2 coffee-loader: specifier: ^3.0.0 - version: 3.0.0(coffeescript@2.7.0)(webpack@5.99.5) + version: 3.0.0(coffeescript@2.7.0)(webpack@5.100.1) coffeescript: specifier: ^2.5.1 version: 2.7.0 css-loader: specifier: ^7.1.2 - version: 7.1.2(@rspack/core@1.4.4(@swc/helpers@0.5.15))(webpack@5.99.5) + version: 7.1.2(@rspack/core@1.4.6(@swc/helpers@0.5.15))(webpack@5.100.1) entities: specifier: ^2.2.0 version: 2.2.0 @@ -1562,16 +1547,16 @@ importers: version: 1.7.3(handlebars@4.7.8) html-loader: specifier: ^2.1.2 - version: 2.1.2(webpack@5.99.5) + version: 2.1.2(webpack@5.100.1) html-webpack-plugin: specifier: ^5.5.3 - version: 5.6.3(@rspack/core@1.4.4(@swc/helpers@0.5.15))(webpack@5.99.5) + version: 5.6.3(@rspack/core@1.4.6(@swc/helpers@0.5.15))(webpack@5.100.1) identity-obj-proxy: specifier: ^3.0.0 version: 3.0.0 imports-loader: specifier: ^3.0.0 - version: 3.1.1(webpack@5.99.5) + version: 3.1.1(webpack@5.100.1) jquery: specifier: ^3.6.0 version: 3.7.1 @@ -1595,13 +1580,13 @@ importers: version: 4.3.0 less-loader: specifier: ^11.0.0 - version: 11.1.4(less@4.3.0)(webpack@5.99.5) + version: 11.1.4(less@4.3.0)(webpack@5.100.1) path-browserify: specifier: ^1.0.1 version: 1.0.1 raw-loader: specifier: ^4.0.2 - version: 4.0.2(webpack@5.99.5) + version: 4.0.2(webpack@5.100.1) react: specifier: ^19.1.0 version: 19.1.0 @@ -1622,25 +1607,25 @@ importers: version: 1.89.2 sass-loader: specifier: ^16.0.5 - version: 16.0.5(@rspack/core@1.4.4(@swc/helpers@0.5.15))(sass@1.89.2)(webpack@5.99.5) + version: 16.0.5(@rspack/core@1.4.6(@swc/helpers@0.5.15))(sass@1.89.2)(webpack@5.100.1) script-loader: specifier: ^0.7.2 version: 0.7.2 source-map-loader: specifier: ^3.0.0 - version: 3.0.2(webpack@5.99.5) + version: 3.0.2(webpack@5.100.1) stream-browserify: specifier: ^3.0.0 version: 3.0.0 style-loader: specifier: ^2.0.0 - version: 2.0.0(webpack@5.99.5) + version: 2.0.0(webpack@5.100.1) timeago: specifier: ^1.6.3 version: 1.6.7 ts-jest: specifier: ^29.2.3 - version: 29.4.0(@babel/core@7.26.9)(@jest/transform@29.7.0)(@jest/types@30.0.1)(babel-jest@29.7.0(@babel/core@7.26.9))(jest-util@30.0.2)(jest@29.7.0(@types/node@18.19.115))(typescript@5.8.3) + version: 29.4.0(@babel/core@7.28.0)(@jest/transform@29.7.0)(@jest/types@30.0.1)(babel-jest@29.7.0(@babel/core@7.28.0))(jest-util@30.0.2)(jest@29.7.0(@types/node@18.19.118))(typescript@5.8.3) tsd: specifier: ^0.22.0 version: 0.22.0 @@ -1692,10 +1677,10 @@ importers: version: 4.17.20 '@types/node': specifier: ^18.16.14 - version: 18.19.115 + version: 18.19.118 ts-jest: specifier: ^29.2.3 - version: 29.4.0(@babel/core@7.26.9)(@jest/transform@29.7.0)(@jest/types@30.0.1)(babel-jest@29.7.0(@babel/core@7.26.9))(jest-util@30.0.2)(jest@29.7.0(@types/node@18.19.115))(typescript@5.8.3) + version: 29.4.0(@babel/core@7.28.0)(@jest/transform@29.7.0)(@jest/types@30.0.1)(babel-jest@29.7.0(@babel/core@7.28.0))(jest-util@30.0.2)(jest@29.7.0(@types/node@18.19.118))(typescript@5.8.3) sync-client: dependencies: @@ -1741,7 +1726,7 @@ importers: version: 29.5.14 '@types/node': specifier: ^18.16.14 - version: 18.19.115 + version: 18.19.118 '@types/primus': specifier: ^7.3.9 version: 7.3.9 @@ -1778,7 +1763,7 @@ importers: devDependencies: '@types/node': specifier: ^18.16.14 - version: 18.19.115 + version: 18.19.118 util: dependencies: @@ -1866,7 +1851,7 @@ importers: version: 4.17.20 '@types/node': specifier: ^18.16.14 - version: 18.19.115 + version: 18.19.118 '@types/seedrandom': specifier: ^3.0.8 version: 3.0.8 @@ -2052,36 +2037,36 @@ packages: '@asamuzakjp/css-color@3.2.0': resolution: {integrity: sha512-K1A6z8tS3XsmCMM86xoWdn7Fkdn9m6RSVtocUrJYIwZnFVkng/PvkEoWtOWmP+Scc6saYWHWZYbndEEXxl24jw==} - '@babel/code-frame@7.24.7': - resolution: {integrity: sha512-BcYH1CVJBO9tvyIZ2jVeXgSIMvGZ2FDRvDdOIVQyuklNKSsx+eppDEBq/g47Ayw+RqNFE+URvOShmf+f/qwAlA==} - engines: {node: '>=6.9.0'} - '@babel/code-frame@7.27.1': resolution: {integrity: sha512-cjQ7ZlQ0Mv3b47hABuTevyTuYN4i+loJKGeV9flcCgIK37cCXRh+L1bd3iBHlynerhQ7BhCkn2BPbQUL+rGqFg==} engines: {node: '>=6.9.0'} - '@babel/compat-data@7.26.8': - resolution: {integrity: sha512-oH5UPLMWR3L2wEFLnFJ1TZXqHufiTKAiLfqw5zkhS4dKXLJ10yVztfil/twG8EDTA4F/tvVNw9nOl4ZMslB8rQ==} + '@babel/compat-data@7.28.0': + resolution: {integrity: sha512-60X7qkglvrap8mn1lh2ebxXdZYtUcpd7gsmy9kLaBJ4i/WdY8PqTSdxyA8qraikqKQK5C1KRBKXqznrVapyNaw==} engines: {node: '>=6.9.0'} - '@babel/core@7.26.9': - resolution: {integrity: sha512-lWBYIrF7qK5+GjY5Uy+/hEgp8OJWOD/rpy74GplYRhEauvbHDeFB8t5hPOZxCZ0Oxf4Cc36tK51/l3ymJysrKw==} + '@babel/core@7.28.0': + resolution: {integrity: sha512-UlLAnTPrFdNGoFtbSXwcGFQBtQZJCNjaN6hQNP3UPvuNXT1i82N26KL3dZeIpNalWywr9IuQuncaAfUaS1g6sQ==} engines: {node: '>=6.9.0'} - '@babel/generator@7.27.1': - resolution: {integrity: sha512-UnJfnIpc/+JO0/+KRVQNGU+y5taA5vCbwN8+azkX6beii/ZF+enZJSOKo11ZSzGJjlNfJHfQtmQT8H+9TXPG2w==} + '@babel/generator@7.28.0': + resolution: {integrity: sha512-lJjzvrbEeWrhB4P3QBsH7tey117PjLZnDbLiQEKjQ/fNJTjuq4HSqgFA+UNSwZT8D7dxxbnuSBMsa1lrWzKlQg==} engines: {node: '>=6.9.0'} - '@babel/helper-compilation-targets@7.27.0': - resolution: {integrity: sha512-LVk7fbXml0H2xH34dFzKQ7TDZ2G4/rVTOrq9V+icbbadjbVxxeFeDsNHv2SrZeWoA+6ZiTyWYWtScEIW07EAcA==} + '@babel/helper-compilation-targets@7.27.2': + resolution: {integrity: sha512-2+1thGUUWWjLTYTHZWK1n8Yga0ijBz1XAhUXcKy81rd5g6yh7hGqMp45v7cadSbEHc9G3OTv45SyneRN3ps4DQ==} + engines: {node: '>=6.9.0'} + + '@babel/helper-globals@7.28.0': + resolution: {integrity: sha512-+W6cISkXFa1jXsDEdYA8HeevQT/FULhxzR99pxphltZcVaugps53THCeiWA8SguxxpSp3gKPiuYfSWopkLQ4hw==} engines: {node: '>=6.9.0'} '@babel/helper-module-imports@7.27.1': resolution: {integrity: sha512-0gSFWUPNXNopqtIPQvlD5WgXYI5GY2kP2cCvoT8kczjbfcfuIljTbcWrulD1CIPIX2gt1wghbDy08yE1p+/r3w==} engines: {node: '>=6.9.0'} - '@babel/helper-module-transforms@7.27.1': - resolution: {integrity: sha512-9yHn519/8KvTU5BjTVEEeIM3w9/2yXNKoD82JifINImhpKkARMJKPP59kLo+BafpdN5zgNeIcS4jsGDmd3l58g==} + '@babel/helper-module-transforms@7.27.3': + resolution: {integrity: sha512-dSOvYwvyLsWBeIRyOeHXp5vPj5l1I011r52FM1+r1jCERv+aFXYk4whgQccYEGYxK2H3ZAIA8nuPkQ0HaUo3qg==} engines: {node: '>=6.9.0'} peerDependencies: '@babel/core': ^7.0.0 @@ -2102,16 +2087,12 @@ packages: resolution: {integrity: sha512-YvjJow9FxbhFFKDSuFnVCe2WxXk1zWc22fFePVNEaWJEu8IrZVlda6N0uHwzZrUM1il7NC9Mlp4MaJYbYd9JSg==} engines: {node: '>=6.9.0'} - '@babel/helpers@7.27.0': - resolution: {integrity: sha512-U5eyP/CTFPuNE3qk+WZMxFkp/4zUzdceQlfzf7DdGdhp+Fezd7HD+i8Y24ZuTMKX3wQBld449jijbGq6OdGNQg==} - engines: {node: '>=6.9.0'} - - '@babel/highlight@7.25.7': - resolution: {integrity: sha512-iYyACpW3iW8Fw+ZybQK+drQre+ns/tKpXbNESfrhNnPLIklLbXr7MYJ6gPEd0iETGLOK+SxMjVvKb/ffmk+FEw==} + '@babel/helpers@7.27.6': + resolution: {integrity: sha512-muE8Tt8M22638HU31A3CgfSUciwz1fhATfoVai05aPXGor//CdWDCbnlY1yvBPo07njuVOCNGCSp/GTt12lIug==} engines: {node: '>=6.9.0'} - '@babel/parser@7.27.2': - resolution: {integrity: sha512-QYLs8299NA7WM/bZAdp+CviYYkVoYXlDW2rzliy3chxd1PQjej7JORuMJDJXJUb9g0TT+B99EwaVLKmX+sPXWw==} + '@babel/parser@7.28.0': + resolution: {integrity: sha512-jVZGvOxOuNSsuQuLRTh13nU0AogFlw32w/MT+LV6D3sP5WdbW61E77RnkbaO2dUvmPAYrBDJXGn5gGS6tH4j8g==} engines: {node: '>=6.0.0'} hasBin: true @@ -2130,6 +2111,18 @@ packages: peerDependencies: '@babel/core': ^7.0.0-0 + '@babel/plugin-syntax-class-static-block@7.14.5': + resolution: {integrity: sha512-b+YyPmr6ldyNnM6sqYeMWE+bgJcJpO6yS4QD7ymxgH34GBPNDM/THBh8iunyvKIZztiwLH4CJZ0RxTk9emgpjw==} + engines: {node: '>=6.9.0'} + peerDependencies: + '@babel/core': ^7.0.0-0 + + '@babel/plugin-syntax-import-attributes@7.27.1': + resolution: {integrity: sha512-oFT0FrKHgF53f4vOsZGi2Hh3I35PfSmVs4IBFLFj4dnafP+hIWDLg3VyKmUHfLoLHlyxY4C7DGtmHuJgn+IGww==} + engines: {node: '>=6.9.0'} + peerDependencies: + '@babel/core': ^7.0.0-0 + '@babel/plugin-syntax-import-meta@7.10.4': resolution: {integrity: sha512-Yqfm+XDx0+Prh3VSeEQCPU81yC+JWZ2pDPFSS4ZdpfZhp4MkFMaDC1UqseovEKwSUpnIL7+vK+Clp7bfh0iD7g==} peerDependencies: @@ -2176,6 +2169,12 @@ packages: peerDependencies: '@babel/core': ^7.0.0-0 + '@babel/plugin-syntax-private-property-in-object@7.14.5': + resolution: {integrity: sha512-0wVnp9dxJ72ZUJDV27ZfbSj6iHLoytYZmh3rFcxNnvsJF3ktkzLDZPy/mA17HGsaQT3/DQsWYX1f1QGWkCoVUg==} + engines: {node: '>=6.9.0'} + peerDependencies: + '@babel/core': ^7.0.0-0 + '@babel/plugin-syntax-top-level-await@7.14.5': resolution: {integrity: sha512-hx++upLv5U1rgYfwe1xBQUhRmU41NEvpUvrp8jkrSCdvGSnM5/qdRMtylJ6PG5OFkBaHkbTAKTnd3/YyESRHFw==} engines: {node: '>=6.9.0'} @@ -2188,18 +2187,6 @@ packages: peerDependencies: '@babel/core': ^7.0.0-0 - '@babel/runtime@7.25.6': - resolution: {integrity: sha512-VBj9MYyDb9tuLq7yzqjgzt6Q+IBQLrGZfdjOekyEirZPHxXWoTSGUTMrpsfi58Up73d13NfYLv8HT9vmznjzhQ==} - engines: {node: '>=6.9.0'} - - '@babel/runtime@7.27.0': - resolution: {integrity: sha512-VtPOkrdPHZsKc/clNqyi9WUA8TINkZ4cGk63UUE3u4pmB2k+ZMQRDuIOagv8UVd6j7k0T3+RRIb7beKTebNbcw==} - engines: {node: '>=6.9.0'} - - '@babel/runtime@7.27.1': - resolution: {integrity: sha512-1x3D2xEk2fRo3PAhwQwu5UubzgiVWSXTBfWpVd2Mx2AzRqJuDJCsgaDVZ7HB5iGzDW1Hl1sWN2mFyKjmR9uAog==} - engines: {node: '>=6.9.0'} - '@babel/runtime@7.27.6': resolution: {integrity: sha512-vbavdySgbTTrmFE+EsiqUTzlOr5bzlnJtUv9PynGCAKvfQqjIXbvFdumPM/GxMDfyuGMJaJAU6TO4zc1Jf1i8Q==} engines: {node: '>=6.9.0'} @@ -2208,12 +2195,12 @@ packages: resolution: {integrity: sha512-LPDZ85aEJyYSd18/DkjNh4/y1ntkE5KwUHWTiqgRxruuZL2F1yuHligVHLvcHY2vMHXttKFpJn6LwfI7cw7ODw==} engines: {node: '>=6.9.0'} - '@babel/traverse@7.27.1': - resolution: {integrity: sha512-ZCYtZciz1IWJB4U61UPu4KEaqyfj+r5T1Q5mqPo+IBpcG9kHv30Z0aD8LXPgC1trYa6rK0orRyAhqUgk4MjmEg==} + '@babel/traverse@7.28.0': + resolution: {integrity: sha512-mGe7UK5wWyh0bKRfupsUchrQGqvDbZDbKJw+kcRGSmdHVYrv+ltd0pnpDTVpiTqnaBru9iEvA8pz8W46v0Amwg==} engines: {node: '>=6.9.0'} - '@babel/types@7.27.1': - resolution: {integrity: sha512-+EzkxvLNfiUeKMgy/3luqfsCWFRXLb7U6wNQTk60tovuckwB15B191tJWvpp4HjiQWdJkCxO3Wbvc6jlk3Xb2Q==} + '@babel/types@7.28.1': + resolution: {integrity: sha512-x0LvFTekgSX+83TI28Y9wYPUfzrnl2aT5+5QLnO6v7mSJYtEEevuDRN0F0uSHRk1G1IWZC43o00Y0xDDrpBGPQ==} engines: {node: '>=6.9.0'} '@bcoe/v8-coverage@0.2.3': @@ -2285,184 +2272,184 @@ packages: resolution: {integrity: sha512-ekMWuNlFiVGfsKhfj4nmc8JCA+1ZltwJgxiKgDuwYtR09ie340RfXFF6YRd2VTW5zN7l4F1PfaAaPklVz6utSg==} engines: {node: '>=18'} - '@cspell/dict-ada@4.1.0': - resolution: {integrity: sha512-7SvmhmX170gyPd+uHXrfmqJBY5qLcCX8kTGURPVeGxmt8XNXT75uu9rnZO+jwrfuU2EimNoArdVy5GZRGljGNg==} + '@cspell/dict-ada@4.1.1': + resolution: {integrity: sha512-E+0YW9RhZod/9Qy2gxfNZiHJjCYFlCdI69br1eviQQWB8yOTJX0JHXLs79kOYhSW0kINPVUdvddEBe6Lu6CjGQ==} - '@cspell/dict-al@1.1.0': - resolution: {integrity: sha512-PtNI1KLmYkELYltbzuoztBxfi11jcE9HXBHCpID2lou/J4VMYKJPNqe4ZjVzSI9NYbMnMnyG3gkbhIdx66VSXg==} + '@cspell/dict-al@1.1.1': + resolution: {integrity: sha512-sD8GCaZetgQL4+MaJLXqbzWcRjfKVp8x+px3HuCaaiATAAtvjwUQ5/Iubiqwfd1boIh2Y1/3EgM3TLQ7Q8e0wQ==} - '@cspell/dict-aws@4.0.10': - resolution: {integrity: sha512-0qW4sI0GX8haELdhfakQNuw7a2pnWXz3VYQA2MpydH2xT2e6EN9DWFpKAi8DfcChm8MgDAogKkoHtIo075iYng==} + '@cspell/dict-aws@4.0.12': + resolution: {integrity: sha512-k1F48eYlX+LsCK2QjqpfHBrjNwNwRyut/XsGumyhUXZsm+j9NVuxQaFCjiEwXi81KE0YE3GBVdwSjqhuUOkpnQ==} - '@cspell/dict-bash@4.2.0': - resolution: {integrity: sha512-HOyOS+4AbCArZHs/wMxX/apRkjxg6NDWdt0jF9i9XkvJQUltMwEhyA2TWYjQ0kssBsnof+9amax2lhiZnh3kCg==} + '@cspell/dict-bash@4.2.1': + resolution: {integrity: sha512-SBnzfAyEAZLI9KFS7DUG6Xc1vDFuLllY3jz0WHvmxe8/4xV3ufFE3fGxalTikc1VVeZgZmxYiABw4iGxVldYEg==} - '@cspell/dict-companies@3.2.1': - resolution: {integrity: sha512-ryaeJ1KhTTKL4mtinMtKn8wxk6/tqD4vX5tFP+Hg89SiIXmbMk5vZZwVf+eyGUWJOyw5A1CVj9EIWecgoi+jYQ==} + '@cspell/dict-companies@3.2.2': + resolution: {integrity: sha512-iIuEBPfWzSQugIOn+OKOVsdfE9UloON5SKl57TbvC//D5mgIwPAMZGYT69yv20cjc5E6oMu353hCV3WFy9XO9A==} - '@cspell/dict-cpp@6.0.8': - resolution: {integrity: sha512-BzurRZilWqaJt32Gif6/yCCPi+FtrchjmnehVEIFzbWyeBd/VOUw77IwrEzehZsu5cRU91yPWuWp5fUsKfDAXA==} + '@cspell/dict-cpp@6.0.9': + resolution: {integrity: sha512-Xdq9MwGh0D5rsnbOqFW24NIClXXRhN11KJdySMibpcqYGeomxB2ODFBuhj1H7azO7kVGkGH0Okm4yQ2TRzBx0g==} - '@cspell/dict-cryptocurrencies@5.0.4': - resolution: {integrity: sha512-6iFu7Abu+4Mgqq08YhTKHfH59mpMpGTwdzDB2Y8bbgiwnGFCeoiSkVkgLn1Kel2++hYcZ8vsAW/MJS9oXxuMag==} + '@cspell/dict-cryptocurrencies@5.0.5': + resolution: {integrity: sha512-R68hYYF/rtlE6T/dsObStzN5QZw+0aQBinAXuWCVqwdS7YZo0X33vGMfChkHaiCo3Z2+bkegqHlqxZF4TD3rUA==} - '@cspell/dict-csharp@4.0.6': - resolution: {integrity: sha512-w/+YsqOknjQXmIlWDRmkW+BHBPJZ/XDrfJhZRQnp0wzpPOGml7W0q1iae65P2AFRtTdPKYmvSz7AL5ZRkCnSIw==} + '@cspell/dict-csharp@4.0.7': + resolution: {integrity: sha512-H16Hpu8O/1/lgijFt2lOk4/nnldFtQ4t8QHbyqphqZZVE5aS4J/zD/WvduqnLY21aKhZS6jo/xF5PX9jyqPKUA==} - '@cspell/dict-css@4.0.17': - resolution: {integrity: sha512-2EisRLHk6X/PdicybwlajLGKF5aJf4xnX2uuG5lexuYKt05xV/J/OiBADmi8q9obhxf1nesrMQbqAt+6CsHo/w==} + '@cspell/dict-css@4.0.18': + resolution: {integrity: sha512-EF77RqROHL+4LhMGW5NTeKqfUd/e4OOv6EDFQ/UQQiFyWuqkEKyEz0NDILxOFxWUEVdjT2GQ2cC7t12B6pESwg==} - '@cspell/dict-dart@2.3.0': - resolution: {integrity: sha512-1aY90lAicek8vYczGPDKr70pQSTQHwMFLbmWKTAI6iavmb1fisJBS1oTmMOKE4ximDf86MvVN6Ucwx3u/8HqLg==} + '@cspell/dict-dart@2.3.1': + resolution: {integrity: sha512-xoiGnULEcWdodXI6EwVyqpZmpOoh8RA2Xk9BNdR7DLamV/QMvEYn8KJ7NlRiTSauJKPNkHHQ5EVHRM6sTS7jdg==} - '@cspell/dict-data-science@2.0.8': - resolution: {integrity: sha512-uyAtT+32PfM29wRBeAkUSbkytqI8bNszNfAz2sGPtZBRmsZTYugKMEO9eDjAIE/pnT9CmbjNuoiXhk+Ss4fCOg==} + '@cspell/dict-data-science@2.0.9': + resolution: {integrity: sha512-wTOFMlxv06veIwKdXUwdGxrQcK44Zqs426m6JGgHIB/GqvieZQC5n0UI+tUm5OCxuNyo4OV6mylT4cRMjtKtWQ==} - '@cspell/dict-django@4.1.4': - resolution: {integrity: sha512-fX38eUoPvytZ/2GA+g4bbdUtCMGNFSLbdJJPKX2vbewIQGfgSFJKY56vvcHJKAvw7FopjvgyS/98Ta9WN1gckg==} + '@cspell/dict-django@4.1.5': + resolution: {integrity: sha512-AvTWu99doU3T8ifoMYOMLW2CXKvyKLukPh1auOPwFGHzueWYvBBN+OxF8wF7XwjTBMMeRleVdLh3aWCDEX/ZWg==} - '@cspell/dict-docker@1.1.14': - resolution: {integrity: sha512-p6Qz5mokvcosTpDlgSUREdSbZ10mBL3ndgCdEKMqjCSZJFdfxRdNdjrGER3lQ6LMq5jGr1r7nGXA0gvUJK80nw==} + '@cspell/dict-docker@1.1.15': + resolution: {integrity: sha512-wYthMAbEbqDBr9P90VC9aT3zjErrJbUtIr91pDmse7Y5WUvQtAwFhiJHgmNrtx2fZ2idII0eYvpMqoEO+FYFxw==} - '@cspell/dict-dotnet@5.0.9': - resolution: {integrity: sha512-JGD6RJW5sHtO5lfiJl11a5DpPN6eKSz5M1YBa1I76j4dDOIqgZB6rQexlDlK1DH9B06X4GdDQwdBfnpAB0r2uQ==} + '@cspell/dict-dotnet@5.0.10': + resolution: {integrity: sha512-ooar8BP/RBNP1gzYfJPStKEmpWy4uv/7JCq6FOnJLeD1yyfG3d/LFMVMwiJo+XWz025cxtkM3wuaikBWzCqkmg==} - '@cspell/dict-elixir@4.0.7': - resolution: {integrity: sha512-MAUqlMw73mgtSdxvbAvyRlvc3bYnrDqXQrx5K9SwW8F7fRYf9V4vWYFULh+UWwwkqkhX9w03ZqFYRTdkFku6uA==} + '@cspell/dict-elixir@4.0.8': + resolution: {integrity: sha512-CyfphrbMyl4Ms55Vzuj+mNmd693HjBFr9hvU+B2YbFEZprE5AG+EXLYTMRWrXbpds4AuZcvN3deM2XVB80BN/Q==} - '@cspell/dict-en-common-misspellings@2.0.11': - resolution: {integrity: sha512-xFQjeg0wFHh9sFhshpJ+5BzWR1m9Vu8pD0CGPkwZLK9oii8AD8RXNchabLKy/O5VTLwyqPOi9qpyp1cxm3US4Q==} + '@cspell/dict-en-common-misspellings@2.1.3': + resolution: {integrity: sha512-v1I97Hr1OrK+mwHsVzbY4vsPxx6mA5quhxzanF6XuRofz00wH4HPz8Q3llzRHxka5Wl/59gyan04UkUrvP4gdA==} '@cspell/dict-en-gb@1.1.33': resolution: {integrity: sha512-tKSSUf9BJEV+GJQAYGw5e+ouhEe2ZXE620S7BLKe3ZmpnjlNG9JqlnaBhkIMxKnNFkLY2BP/EARzw31AZnOv4g==} - '@cspell/dict-en_us@4.4.9': - resolution: {integrity: sha512-5gjqpUwhE+qP9A9wxD1+MGGJ3DNqTgSpiOsS10cGJfV4p/Z194XkDUZrUrJsnJA/3fsCZHAzcNWh8m0bw1v++A==} + '@cspell/dict-en_us@4.4.15': + resolution: {integrity: sha512-mhWaLui5aq0pgM4oIqAJNbOW/wQG3epe4nu22eqYTLQN+ztdzyn7aFpE+9yTtZTNvldK0xtN7jgk2mtHZ9w+1A==} - '@cspell/dict-filetypes@3.0.12': - resolution: {integrity: sha512-+ds5wgNdlUxuJvhg8A1TjuSpalDFGCh7SkANCWvIplg6QZPXL4j83lqxP7PgjHpx7PsBUS7vw0aiHPjZy9BItw==} + '@cspell/dict-filetypes@3.0.13': + resolution: {integrity: sha512-g6rnytIpQlMNKGJT1JKzWkC+b3xCliDKpQ3ANFSq++MnR4GaLiifaC4JkVON11Oh/UTplYOR1nY3BR4X30bswA==} - '@cspell/dict-flutter@1.1.0': - resolution: {integrity: sha512-3zDeS7zc2p8tr9YH9tfbOEYfopKY/srNsAa+kE3rfBTtQERAZeOhe5yxrnTPoufctXLyuUtcGMUTpxr3dO0iaA==} + '@cspell/dict-flutter@1.1.1': + resolution: {integrity: sha512-UlOzRcH2tNbFhZmHJN48Za/2/MEdRHl2BMkCWZBYs+30b91mWvBfzaN4IJQU7dUZtowKayVIF9FzvLZtZokc5A==} - '@cspell/dict-fonts@4.0.4': - resolution: {integrity: sha512-cHFho4hjojBcHl6qxidl9CvUb492IuSk7xIf2G2wJzcHwGaCFa2o3gRcxmIg1j62guetAeDDFELizDaJlVRIOg==} + '@cspell/dict-fonts@4.0.5': + resolution: {integrity: sha512-BbpkX10DUX/xzHs6lb7yzDf/LPjwYIBJHJlUXSBXDtK/1HaeS+Wqol4Mlm2+NAgZ7ikIE5DQMViTgBUY3ezNoQ==} - '@cspell/dict-fsharp@1.1.0': - resolution: {integrity: sha512-oguWmHhGzgbgbEIBKtgKPrFSVAFtvGHaQS0oj+vacZqMObwkapcTGu7iwf4V3Bc2T3caf0QE6f6rQfIJFIAVsw==} + '@cspell/dict-fsharp@1.1.1': + resolution: {integrity: sha512-imhs0u87wEA4/cYjgzS0tAyaJpwG7vwtC8UyMFbwpmtw+/bgss+osNfyqhYRyS/ehVCWL17Ewx2UPkexjKyaBA==} - '@cspell/dict-fullstack@3.2.6': - resolution: {integrity: sha512-cSaq9rz5RIU9j+0jcF2vnKPTQjxGXclntmoNp4XB7yFX2621PxJcekGjwf/lN5heJwVxGLL9toR0CBlGKwQBgA==} + '@cspell/dict-fullstack@3.2.7': + resolution: {integrity: sha512-IxEk2YAwAJKYCUEgEeOg3QvTL4XLlyArJElFuMQevU1dPgHgzWElFevN5lsTFnvMFA1riYsVinqJJX0BanCFEg==} - '@cspell/dict-gaming-terms@1.1.1': - resolution: {integrity: sha512-tb8GFxjTLDQstkJcJ90lDqF4rKKlMUKs5/ewePN9P+PYRSehqDpLI5S5meOfPit8LGszeOrjUdBQ4zXo7NpMyQ==} + '@cspell/dict-gaming-terms@1.1.2': + resolution: {integrity: sha512-9XnOvaoTBscq0xuD6KTEIkk9hhdfBkkvJAIsvw3JMcnp1214OCGW8+kako5RqQ2vTZR3Tnf3pc57o7VgkM0q1Q==} - '@cspell/dict-git@3.0.5': - resolution: {integrity: sha512-I7l86J2nOcpBY0OcwXLTGMbcXbEE7nxZme9DmYKrNgmt35fcLu+WKaiXW7P29V+lIXjJo/wKrEDY+wUEwVuABQ==} + '@cspell/dict-git@3.0.7': + resolution: {integrity: sha512-odOwVKgfxCQfiSb+nblQZc4ErXmnWEnv8XwkaI4sNJ7cNmojnvogYVeMqkXPjvfrgEcizEEA4URRD2Ms5PDk1w==} - '@cspell/dict-golang@6.0.21': - resolution: {integrity: sha512-D3wG1MWhFx54ySFJ00CS1MVjR4UiBVsOWGIjJ5Av+HamnguqEshxbF9mvy+BX0KqzdLVzwFkoLBs8QeOID56HA==} + '@cspell/dict-golang@6.0.23': + resolution: {integrity: sha512-oXqUh/9dDwcmVlfUF5bn3fYFqbUzC46lXFQmi5emB0vYsyQXdNWsqi6/yH3uE7bdRE21nP7Yo0mR1jjFNyLamg==} - '@cspell/dict-google@1.0.8': - resolution: {integrity: sha512-BnMHgcEeaLyloPmBs8phCqprI+4r2Jb8rni011A8hE+7FNk7FmLE3kiwxLFrcZnnb7eqM0agW4zUaNoB0P+z8A==} + '@cspell/dict-google@1.0.9': + resolution: {integrity: sha512-biL65POqialY0i4g6crj7pR6JnBkbsPovB2WDYkj3H4TuC/QXv7Pu5pdPxeUJA6TSCHI7T5twsO4VSVyRxD9CA==} - '@cspell/dict-haskell@4.0.5': - resolution: {integrity: sha512-s4BG/4tlj2pPM9Ha7IZYMhUujXDnI0Eq1+38UTTCpatYLbQqDwRFf2KNPLRqkroU+a44yTUAe0rkkKbwy4yRtQ==} + '@cspell/dict-haskell@4.0.6': + resolution: {integrity: sha512-ib8SA5qgftExpYNjWhpYIgvDsZ/0wvKKxSP+kuSkkak520iPvTJumEpIE+qPcmJQo4NzdKMN8nEfaeci4OcFAQ==} - '@cspell/dict-html-symbol-entities@4.0.3': - resolution: {integrity: sha512-aABXX7dMLNFdSE8aY844X4+hvfK7977sOWgZXo4MTGAmOzR8524fjbJPswIBK7GaD3+SgFZ2yP2o0CFvXDGF+A==} + '@cspell/dict-html-symbol-entities@4.0.4': + resolution: {integrity: sha512-afea+0rGPDeOV9gdO06UW183Qg6wRhWVkgCFwiO3bDupAoyXRuvupbb5nUyqSTsLXIKL8u8uXQlJ9pkz07oVXw==} - '@cspell/dict-html@4.0.11': - resolution: {integrity: sha512-QR3b/PB972SRQ2xICR1Nw/M44IJ6rjypwzA4jn+GH8ydjAX9acFNfc+hLZVyNe0FqsE90Gw3evLCOIF0vy1vQw==} + '@cspell/dict-html@4.0.12': + resolution: {integrity: sha512-JFffQ1dDVEyJq6tCDWv0r/RqkdSnV43P2F/3jJ9rwLgdsOIXwQbXrz6QDlvQLVvNSnORH9KjDtenFTGDyzfCaA==} - '@cspell/dict-java@5.0.11': - resolution: {integrity: sha512-T4t/1JqeH33Raa/QK/eQe26FE17eUCtWu+JsYcTLkQTci2dk1DfcIKo8YVHvZXBnuM43ATns9Xs0s+AlqDeH7w==} + '@cspell/dict-java@5.0.12': + resolution: {integrity: sha512-qPSNhTcl7LGJ5Qp6VN71H8zqvRQK04S08T67knMq9hTA8U7G1sTKzLmBaDOFhq17vNX/+rT+rbRYp+B5Nwza1A==} - '@cspell/dict-julia@1.1.0': - resolution: {integrity: sha512-CPUiesiXwy3HRoBR3joUseTZ9giFPCydSKu2rkh6I2nVjXnl5vFHzOMLXpbF4HQ1tH2CNfnDbUndxD+I+7eL9w==} + '@cspell/dict-julia@1.1.1': + resolution: {integrity: sha512-WylJR9TQ2cgwd5BWEOfdO3zvDB+L7kYFm0I9u0s9jKHWQ6yKmfKeMjU9oXxTBxIufhCXm92SKwwVNAC7gjv+yA==} - '@cspell/dict-k8s@1.0.10': - resolution: {integrity: sha512-313haTrX9prep1yWO7N6Xw4D6tvUJ0Xsx+YhCP+5YrrcIKoEw5Rtlg8R4PPzLqe6zibw6aJ+Eqq+y76Vx5BZkw==} + '@cspell/dict-k8s@1.0.12': + resolution: {integrity: sha512-2LcllTWgaTfYC7DmkMPOn9GsBWsA4DZdlun4po8s2ysTP7CPEnZc1ZfK6pZ2eI4TsZemlUQQ+NZxMe9/QutQxg==} - '@cspell/dict-kotlin@1.1.0': - resolution: {integrity: sha512-vySaVw6atY7LdwvstQowSbdxjXG6jDhjkWVWSjg1XsUckyzH1JRHXe9VahZz1i7dpoFEUOWQrhIe5B9482UyJQ==} + '@cspell/dict-kotlin@1.1.1': + resolution: {integrity: sha512-J3NzzfgmxRvEeOe3qUXnSJQCd38i/dpF9/t3quuWh6gXM+krsAXP75dY1CzDmS8mrJAlBdVBeAW5eAZTD8g86Q==} - '@cspell/dict-latex@4.0.3': - resolution: {integrity: sha512-2KXBt9fSpymYHxHfvhUpjUFyzrmN4c4P8mwIzweLyvqntBT3k0YGZJSriOdjfUjwSygrfEwiuPI1EMrvgrOMJw==} + '@cspell/dict-latex@4.0.4': + resolution: {integrity: sha512-YdTQhnTINEEm/LZgTzr9Voz4mzdOXH7YX+bSFs3hnkUHCUUtX/mhKgf1CFvZ0YNM2afjhQcmLaR9bDQVyYBvpA==} - '@cspell/dict-lorem-ipsum@4.0.4': - resolution: {integrity: sha512-+4f7vtY4dp2b9N5fn0za/UR0kwFq2zDtA62JCbWHbpjvO9wukkbl4rZg4YudHbBgkl73HRnXFgCiwNhdIA1JPw==} + '@cspell/dict-lorem-ipsum@4.0.5': + resolution: {integrity: sha512-9a4TJYRcPWPBKkQAJ/whCu4uCAEgv/O2xAaZEI0n4y1/l18Yyx8pBKoIX5QuVXjjmKEkK7hi5SxyIsH7pFEK9Q==} - '@cspell/dict-lua@4.0.7': - resolution: {integrity: sha512-Wbr7YSQw+cLHhTYTKV6cAljgMgcY+EUAxVIZW3ljKswEe4OLxnVJ7lPqZF5JKjlXdgCjbPSimsHqyAbC5pQN/Q==} + '@cspell/dict-lua@4.0.8': + resolution: {integrity: sha512-N4PkgNDMu9JVsRu7JBS/3E/dvfItRgk9w5ga2dKq+JupP2Y3lojNaAVFhXISh4Y0a6qXDn2clA6nvnavQ/jjLA==} - '@cspell/dict-makefile@1.0.4': - resolution: {integrity: sha512-E4hG/c0ekPqUBvlkrVvzSoAA+SsDA9bLi4xSV3AXHTVru7Y2bVVGMPtpfF+fI3zTkww/jwinprcU1LSohI3ylw==} + '@cspell/dict-makefile@1.0.5': + resolution: {integrity: sha512-4vrVt7bGiK8Rx98tfRbYo42Xo2IstJkAF4tLLDMNQLkQ86msDlYSKG1ZCk8Abg+EdNcFAjNhXIiNO+w4KflGAQ==} - '@cspell/dict-markdown@2.0.10': - resolution: {integrity: sha512-vtVa6L/84F9sTjclTYDkWJF/Vx2c5xzxBKkQp+CEFlxOF2SYgm+RSoEvAvg5vj4N5kuqR4350ZlY3zl2eA3MXw==} + '@cspell/dict-markdown@2.0.12': + resolution: {integrity: sha512-ufwoliPijAgWkD/ivAMC+A9QD895xKiJRF/fwwknQb7kt7NozTLKFAOBtXGPJAB4UjhGBpYEJVo2elQ0FCAH9A==} peerDependencies: - '@cspell/dict-css': ^4.0.17 - '@cspell/dict-html': ^4.0.11 - '@cspell/dict-html-symbol-entities': ^4.0.3 - '@cspell/dict-typescript': ^3.2.1 + '@cspell/dict-css': ^4.0.18 + '@cspell/dict-html': ^4.0.12 + '@cspell/dict-html-symbol-entities': ^4.0.4 + '@cspell/dict-typescript': ^3.2.3 - '@cspell/dict-monkeyc@1.0.10': - resolution: {integrity: sha512-7RTGyKsTIIVqzbvOtAu6Z/lwwxjGRtY5RkKPlXKHEoEAgIXwfDxb5EkVwzGQwQr8hF/D3HrdYbRT8MFBfsueZw==} + '@cspell/dict-monkeyc@1.0.11': + resolution: {integrity: sha512-7Q1Ncu0urALI6dPTrEbSTd//UK0qjRBeaxhnm8uY5fgYNFYAG+u4gtnTIo59S6Bw5P++4H3DiIDYoQdY/lha8w==} - '@cspell/dict-node@5.0.7': - resolution: {integrity: sha512-ZaPpBsHGQCqUyFPKLyCNUH2qzolDRm1/901IO8e7btk7bEDF56DN82VD43gPvD4HWz3yLs/WkcLa01KYAJpnOw==} + '@cspell/dict-node@5.0.8': + resolution: {integrity: sha512-AirZcN2i84ynev3p2/1NCPEhnNsHKMz9zciTngGoqpdItUb2bDt1nJBjwlsrFI78GZRph/VaqTVFwYikmncpXg==} - '@cspell/dict-npm@5.2.4': - resolution: {integrity: sha512-/hK5ii9OzSOQkmTjkzJlEYWz+PBnz2hRq5Xu7d4aDURaynO9xMAcK31JJlKNQulBkVbQHxFZLUrzjdzdAr/Opw==} + '@cspell/dict-npm@5.2.12': + resolution: {integrity: sha512-f5xcEl6+JZCFvDCOKJJuKv1ZMOzq9sBg/7y/iuqkBOgjeGDdB+PSrOJWk2jqu3PzXjjX39KJkt7mRUzv8Mrh1g==} - '@cspell/dict-php@4.0.14': - resolution: {integrity: sha512-7zur8pyncYZglxNmqsRycOZ6inpDoVd4yFfz1pQRe5xaRWMiK3Km4n0/X/1YMWhh3e3Sl/fQg5Axb2hlN68t1g==} + '@cspell/dict-php@4.0.15': + resolution: {integrity: sha512-iepGB2gtToMWSTvybesn4/lUp4LwXcEm0s8vasJLP76WWVkq1zYjmeS+WAIzNgsuURyZ/9mGqhS0CWMuo74ODw==} - '@cspell/dict-powershell@5.0.14': - resolution: {integrity: sha512-ktjjvtkIUIYmj/SoGBYbr3/+CsRGNXGpvVANrY0wlm/IoGlGywhoTUDYN0IsGwI2b8Vktx3DZmQkfb3Wo38jBA==} + '@cspell/dict-powershell@5.0.15': + resolution: {integrity: sha512-l4S5PAcvCFcVDMJShrYD0X6Huv9dcsQPlsVsBGbH38wvuN7gS7+GxZFAjTNxDmTY1wrNi1cCatSg6Pu2BW4rgg==} - '@cspell/dict-public-licenses@2.0.13': - resolution: {integrity: sha512-1Wdp/XH1ieim7CadXYE7YLnUlW0pULEjVl9WEeziZw3EKCAw8ZI8Ih44m4bEa5VNBLnuP5TfqC4iDautAleQzQ==} + '@cspell/dict-public-licenses@2.0.14': + resolution: {integrity: sha512-8NhNzQWALF6+NlLeKZKilSHbeW9MWeiD+NcrjehMAcovKFbsn8smmQG/bVxw+Ymtd6WEgNpLgswAqNsbSQQ4og==} - '@cspell/dict-python@4.2.18': - resolution: {integrity: sha512-hYczHVqZBsck7DzO5LumBLJM119a3F17aj8a7lApnPIS7cmEwnPc2eACNscAHDk7qAo2127oI7axUoFMe9/g1g==} + '@cspell/dict-python@4.2.19': + resolution: {integrity: sha512-9S2gTlgILp1eb6OJcVZeC8/Od83N8EqBSg5WHVpx97eMMJhifOzePkE0kDYjyHMtAFznCQTUu0iQEJohNQ5B0A==} - '@cspell/dict-r@2.1.0': - resolution: {integrity: sha512-k2512wgGG0lTpTYH9w5Wwco+lAMf3Vz7mhqV8+OnalIE7muA0RSuD9tWBjiqLcX8zPvEJr4LdgxVju8Gk3OKyA==} + '@cspell/dict-r@2.1.1': + resolution: {integrity: sha512-71Ka+yKfG4ZHEMEmDxc6+blFkeTTvgKbKAbwiwQAuKl3zpqs1Y0vUtwW2N4b3LgmSPhV3ODVY0y4m5ofqDuKMw==} - '@cspell/dict-ruby@5.0.8': - resolution: {integrity: sha512-ixuTneU0aH1cPQRbWJvtvOntMFfeQR2KxT8LuAv5jBKqQWIHSxzGlp+zX3SVyoeR0kOWiu64/O5Yn836A5yMcQ==} + '@cspell/dict-ruby@5.0.9': + resolution: {integrity: sha512-H2vMcERMcANvQshAdrVx0XoWaNX8zmmiQN11dZZTQAZaNJ0xatdJoSqY8C8uhEMW89bfgpN+NQgGuDXW2vmXEw==} - '@cspell/dict-rust@4.0.11': - resolution: {integrity: sha512-OGWDEEzm8HlkSmtD8fV3pEcO2XBpzG2XYjgMCJCRwb2gRKvR+XIm6Dlhs04N/K2kU+iH8bvrqNpM8fS/BFl0uw==} + '@cspell/dict-rust@4.0.12': + resolution: {integrity: sha512-z2QiH+q9UlNhobBJArvILRxV8Jz0pKIK7gqu4TgmEYyjiu1TvnGZ1tbYHeu9w3I/wOP6UMDoCBTty5AlYfW0mw==} - '@cspell/dict-scala@5.0.7': - resolution: {integrity: sha512-yatpSDW/GwulzO3t7hB5peoWwzo+Y3qTc0pO24Jf6f88jsEeKmDeKkfgPbYuCgbE4jisGR4vs4+jfQZDIYmXPA==} + '@cspell/dict-scala@5.0.8': + resolution: {integrity: sha512-YdftVmumv8IZq9zu1gn2U7A4bfM2yj9Vaupydotyjuc+EEZZSqAafTpvW/jKLWji2TgybM1L2IhmV0s/Iv9BTw==} - '@cspell/dict-shell@1.1.0': - resolution: {integrity: sha512-D/xHXX7T37BJxNRf5JJHsvziFDvh23IF/KvkZXNSh8VqcRdod3BAz9VGHZf6VDqcZXr1VRqIYR3mQ8DSvs3AVQ==} + '@cspell/dict-shell@1.1.1': + resolution: {integrity: sha512-T37oYxE7OV1x/1D4/13Y8JZGa1QgDCXV7AVt3HLXjn0Fe3TaNDvf5sU0fGnXKmBPqFFrHdpD3uutAQb1dlp15g==} - '@cspell/dict-software-terms@5.0.10': - resolution: {integrity: sha512-2nTcVKTYJKU5GzeviXGPtRRC9d23MtfpD4PM4pLSzl29/5nx5MxOUHkzPuJdyaw9mXIz8Rm9IlGeVAvQoTI8aw==} + '@cspell/dict-software-terms@5.1.4': + resolution: {integrity: sha512-zeinnVFfha+Snh8hMk4hRJTYWNLcRNaHRSvMMVe1DU8oljb1agfq6ouBt/uypIzwgGgAopPz9ArGyc/gVn9y8w==} - '@cspell/dict-sql@2.2.0': - resolution: {integrity: sha512-MUop+d1AHSzXpBvQgQkCiok8Ejzb+nrzyG16E8TvKL2MQeDwnIvMe3bv90eukP6E1HWb+V/MA/4pnq0pcJWKqQ==} + '@cspell/dict-sql@2.2.1': + resolution: {integrity: sha512-qDHF8MpAYCf4pWU8NKbnVGzkoxMNrFqBHyG/dgrlic5EQiKANCLELYtGlX5auIMDLmTf1inA0eNtv74tyRJ/vg==} - '@cspell/dict-svelte@1.0.6': - resolution: {integrity: sha512-8LAJHSBdwHCoKCSy72PXXzz7ulGROD0rP1CQ0StOqXOOlTUeSFaJJlxNYjlONgd2c62XBQiN2wgLhtPN+1Zv7Q==} + '@cspell/dict-svelte@1.0.7': + resolution: {integrity: sha512-hGZsGqP0WdzKkdpeVLBivRuSNzOTvN036EBmpOwxH+FTY2DuUH7ecW+cSaMwOgmq5JFSdTcbTNFlNC8HN8lhaQ==} - '@cspell/dict-swift@2.0.5': - resolution: {integrity: sha512-3lGzDCwUmnrfckv3Q4eVSW3sK3cHqqHlPprFJZD4nAqt23ot7fic5ALR7J4joHpvDz36nHX34TgcbZNNZOC/JA==} + '@cspell/dict-swift@2.0.6': + resolution: {integrity: sha512-PnpNbrIbex2aqU1kMgwEKvCzgbkHtj3dlFLPMqW1vSniop7YxaDTtvTUO4zA++ugYAEL+UK8vYrBwDPTjjvSnA==} - '@cspell/dict-terraform@1.1.1': - resolution: {integrity: sha512-07KFDwCU7EnKl4hOZLsLKlj6Zceq/IsQ3LRWUyIjvGFfZHdoGtFdCp3ZPVgnFaAcd/DKv+WVkrOzUBSYqHopQQ==} + '@cspell/dict-terraform@1.1.3': + resolution: {integrity: sha512-gr6wxCydwSFyyBKhBA2xkENXtVFToheqYYGFvlMZXWjviynXmh+NK/JTvTCk/VHk3+lzbO9EEQKee6VjrAUSbA==} - '@cspell/dict-typescript@3.2.2': - resolution: {integrity: sha512-H9Y+uUHsTIDFO/jdfUAcqmcd5osT+2DB5b0aRCHfLWN/twUbGn/1qq3b7YwEvttxKlYzWHU3uNFf+KfA93VY7w==} + '@cspell/dict-typescript@3.2.3': + resolution: {integrity: sha512-zXh1wYsNljQZfWWdSPYwQhpwiuW0KPW1dSd8idjMRvSD0aSvWWHoWlrMsmZeRl4qM4QCEAjua8+cjflm41cQBg==} - '@cspell/dict-vue@3.0.4': - resolution: {integrity: sha512-0dPtI0lwHcAgSiQFx8CzvqjdoXROcH+1LyqgROCpBgppommWpVhbQ0eubnKotFEXgpUCONVkeZJ6Ql8NbTEu+w==} + '@cspell/dict-vue@3.0.5': + resolution: {integrity: sha512-Mqutb8jbM+kIcywuPQCCaK5qQHTdaByoEO2J9LKFy3sqAdiBogNkrplqUK0HyyRFgCfbJUgjz3N85iCMcWH0JA==} '@cspell/dynamic-import@8.19.4': resolution: {integrity: sha512-0LLghC64+SiwQS20Sa0VfFUBPVia1rNyo0bYeIDoB34AA3qwguDBVJJkthkpmaP1R2JeR/VmxmJowuARc4ZUxA==} @@ -2544,14 +2531,14 @@ packages: peerDependencies: react: '>=16.8.0' - '@emnapi/core@1.4.3': - resolution: {integrity: sha512-4m62DuCE07lw01soJwPiBGC0nAww0Q+RY70VZ+n49yDIO13yyinhbWCeNnaob0lakDtWQzSdtNWzJeOJt2ma+g==} + '@emnapi/core@1.4.4': + resolution: {integrity: sha512-A9CnAbC6ARNMKcIcrQwq6HeHCjpcBZ5wSx4U01WXCqEKlrzB9F9315WDNHkrs2xbx7YjjSxbUYxuN6EQzpcY2g==} - '@emnapi/runtime@1.4.3': - resolution: {integrity: sha512-pBPWdu6MLKROBX05wSNKcNb++m5Er+KQ9QkB+WVM+pW2Kx9hoSrVTnu3BdkI5eBLZoKu/J6mW/B6i6bJB2ytXQ==} + '@emnapi/runtime@1.4.4': + resolution: {integrity: sha512-hHyapA4A3gPaDCNfiqyZUStTMqIkKRshqPIuDOXv1hcBnD4U3l8cP0T1HMCfGRxQ6V64TGCcoswChANyOAwbQg==} - '@emnapi/wasi-threads@1.0.2': - resolution: {integrity: sha512-5n3nTJblwRi8LlXkJ9eBzu+kZR8Yxcc7ubakyQTFzPMtIhFpUBRbsnc2Dv88IZDIbCDlBiWrknhB4Lsz7mg6BA==} + '@emnapi/wasi-threads@1.0.3': + resolution: {integrity: sha512-8K5IFFsQqF9wQNJptGbS6FNKgUTsSRYnTqNCG1vPP8jFdjSv18n2mQfJpkt2Oibo9iBEzcDnDxNwKTzC7svlJw==} '@emotion/hash@0.8.0': resolution: {integrity: sha512-kBJtf7PH6aWwZ6fka3zQ0p6SBYzx4fl1LoZXE2RrnYST9Xljm7WfKJrU4g/Xr3Beg72MLrp1AWNUmuYJTL7Cow==} @@ -2559,8 +2546,8 @@ packages: '@emotion/unitless@0.7.5': resolution: {integrity: sha512-OWORNpfjMsSSUBVrRBVGECkhWcULOAJz9ZW8uK9qgxD+87M7jHRcvh/A96XXNhXTLmKcoYSQtBEX7lHMO7YRwg==} - '@eslint-community/eslint-utils@4.5.1': - resolution: {integrity: sha512-soEIOALTfTK6EjmKMMoLugwaP0rzkad90iIWd1hMO9ARkSAyjfMfkRRhLvD5qH7vvM0Cg72pieUfR6yh6XxC4w==} + '@eslint-community/eslint-utils@4.7.0': + resolution: {integrity: sha512-dyybb3AcajC7uha6CvhdVRJqaKyn7w2YKqKyAN37NKYgZT36w+iRb0Dymmc5qEJ549c/S31cMMSFd75bteCpCw==} engines: {node: ^12.22.0 || ^14.17.0 || >=16.0.0} peerDependencies: eslint: ^6.0.0 || ^7.0.0 || >=8.0.0 @@ -2675,16 +2662,16 @@ packages: resolution: {integrity: sha512-pevEyZCb0Oc+dYNlSberW8oZBm4ofeTD5wN01TowQMhTwdAbGAnJMtQzoklh6Blq2AKsx8Ox6FWa44KioZLZiA==} engines: {node: '>=18.0.0'} - '@google/generative-ai@0.24.0': - resolution: {integrity: sha512-fnEITCGEB7NdX0BhoYZ/cq/7WPZ1QS5IzJJfC3Tg/OwkvBetMiVJciyaan297OvE4B9Jg1xvo0zIazX/9sGu1Q==} + '@google/generative-ai@0.24.1': + resolution: {integrity: sha512-MqO+MLfM6kjxcKoy0p1wRzG3b4ZZXtPI+z2IE26UogS2Cm/XHO+7gGRBh6gcJsOiIVoH93UwKvW4HdgiOZCy9Q==} engines: {node: '>=18.0.0'} - '@grpc/grpc-js@1.13.3': - resolution: {integrity: sha512-FTXHdOoPbZrBjlVLHuKbDZnsTxXv2BlHF57xw6LuThXacXvtkahEPED0CKMk6obZDf65Hv4k3z62eyPNpvinIg==} + '@grpc/grpc-js@1.13.4': + resolution: {integrity: sha512-GsFaMXCkMqkKIvwCQjCrwH+GHbPKBjhwo/8ZuUkWHqbI73Kky9I+pQltrlT0+MWpedCoosda53lgjYfyEPgxBg==} engines: {node: '>=12.10.0'} - '@grpc/proto-loader@0.7.13': - resolution: {integrity: sha512-AiXO/bfe9bmxBjxxtYxFAXGZvMaN5s8kO+jBHAJCON8rJoB5YS/D6X7ZNc6XQkuHNmyl4CYaMI1fJ/Gn27RGGw==} + '@grpc/proto-loader@0.7.15': + resolution: {integrity: sha512-tMXdRCfYVixjuFK+Hk0Q1s38gV9zDiDJfWL3h1rv4Qc39oILCu1TRTDt7+fGUI8K4G1Fj125Hx/ru3azECWTyQ==} engines: {node: '>=6'} hasBin: true @@ -2712,118 +2699,124 @@ packages: peerDependencies: react: '*' - '@img/sharp-darwin-arm64@0.34.2': - resolution: {integrity: sha512-OfXHZPppddivUJnqyKoi5YVeHRkkNE2zUFT2gbpKxp/JZCFYEYubnMg+gOp6lWfasPrTS+KPosKqdI+ELYVDtg==} + '@img/sharp-darwin-arm64@0.34.3': + resolution: {integrity: sha512-ryFMfvxxpQRsgZJqBd4wsttYQbCxsJksrv9Lw/v798JcQ8+w84mBWuXwl+TT0WJ/WrYOLaYpwQXi3sA9nTIaIg==} engines: {node: ^18.17.0 || ^20.3.0 || >=21.0.0} cpu: [arm64] os: [darwin] - '@img/sharp-darwin-x64@0.34.2': - resolution: {integrity: sha512-dYvWqmjU9VxqXmjEtjmvHnGqF8GrVjM2Epj9rJ6BUIXvk8slvNDJbhGFvIoXzkDhrJC2jUxNLz/GUjjvSzfw+g==} + '@img/sharp-darwin-x64@0.34.3': + resolution: {integrity: sha512-yHpJYynROAj12TA6qil58hmPmAwxKKC7reUqtGLzsOHfP7/rniNGTL8tjWX6L3CTV4+5P4ypcS7Pp+7OB+8ihA==} engines: {node: ^18.17.0 || ^20.3.0 || >=21.0.0} cpu: [x64] os: [darwin] - '@img/sharp-libvips-darwin-arm64@1.1.0': - resolution: {integrity: sha512-HZ/JUmPwrJSoM4DIQPv/BfNh9yrOA8tlBbqbLz4JZ5uew2+o22Ik+tHQJcih7QJuSa0zo5coHTfD5J8inqj9DA==} + '@img/sharp-libvips-darwin-arm64@1.2.0': + resolution: {integrity: sha512-sBZmpwmxqwlqG9ueWFXtockhsxefaV6O84BMOrhtg/YqbTaRdqDE7hxraVE3y6gVM4eExmfzW4a8el9ArLeEiQ==} cpu: [arm64] os: [darwin] - '@img/sharp-libvips-darwin-x64@1.1.0': - resolution: {integrity: sha512-Xzc2ToEmHN+hfvsl9wja0RlnXEgpKNmftriQp6XzY/RaSfwD9th+MSh0WQKzUreLKKINb3afirxW7A0fz2YWuQ==} + '@img/sharp-libvips-darwin-x64@1.2.0': + resolution: {integrity: sha512-M64XVuL94OgiNHa5/m2YvEQI5q2cl9d/wk0qFTDVXcYzi43lxuiFTftMR1tOnFQovVXNZJ5TURSDK2pNe9Yzqg==} cpu: [x64] os: [darwin] - '@img/sharp-libvips-linux-arm64@1.1.0': - resolution: {integrity: sha512-IVfGJa7gjChDET1dK9SekxFFdflarnUB8PwW8aGwEoF3oAsSDuNUTYS+SKDOyOJxQyDC1aPFMuRYLoDInyV9Ew==} + '@img/sharp-libvips-linux-arm64@1.2.0': + resolution: {integrity: sha512-RXwd0CgG+uPRX5YYrkzKyalt2OJYRiJQ8ED/fi1tq9WQW2jsQIn0tqrlR5l5dr/rjqq6AHAxURhj2DVjyQWSOA==} cpu: [arm64] os: [linux] - '@img/sharp-libvips-linux-arm@1.1.0': - resolution: {integrity: sha512-s8BAd0lwUIvYCJyRdFqvsj+BJIpDBSxs6ivrOPm/R7piTs5UIwY5OjXrP2bqXC9/moGsyRa37eYWYCOGVXxVrA==} + '@img/sharp-libvips-linux-arm@1.2.0': + resolution: {integrity: sha512-mWd2uWvDtL/nvIzThLq3fr2nnGfyr/XMXlq8ZJ9WMR6PXijHlC3ksp0IpuhK6bougvQrchUAfzRLnbsen0Cqvw==} cpu: [arm] os: [linux] - '@img/sharp-libvips-linux-ppc64@1.1.0': - resolution: {integrity: sha512-tiXxFZFbhnkWE2LA8oQj7KYR+bWBkiV2nilRldT7bqoEZ4HiDOcePr9wVDAZPi/Id5fT1oY9iGnDq20cwUz8lQ==} + '@img/sharp-libvips-linux-ppc64@1.2.0': + resolution: {integrity: sha512-Xod/7KaDDHkYu2phxxfeEPXfVXFKx70EAFZ0qyUdOjCcxbjqyJOEUpDe6RIyaunGxT34Anf9ue/wuWOqBW2WcQ==} cpu: [ppc64] os: [linux] - '@img/sharp-libvips-linux-s390x@1.1.0': - resolution: {integrity: sha512-xukSwvhguw7COyzvmjydRb3x/09+21HykyapcZchiCUkTThEQEOMtBj9UhkaBRLuBrgLFzQ2wbxdeCCJW/jgJA==} + '@img/sharp-libvips-linux-s390x@1.2.0': + resolution: {integrity: sha512-eMKfzDxLGT8mnmPJTNMcjfO33fLiTDsrMlUVcp6b96ETbnJmd4uvZxVJSKPQfS+odwfVaGifhsB07J1LynFehw==} cpu: [s390x] os: [linux] - '@img/sharp-libvips-linux-x64@1.1.0': - resolution: {integrity: sha512-yRj2+reB8iMg9W5sULM3S74jVS7zqSzHG3Ol/twnAAkAhnGQnpjj6e4ayUz7V+FpKypwgs82xbRdYtchTTUB+Q==} + '@img/sharp-libvips-linux-x64@1.2.0': + resolution: {integrity: sha512-ZW3FPWIc7K1sH9E3nxIGB3y3dZkpJlMnkk7z5tu1nSkBoCgw2nSRTFHI5pB/3CQaJM0pdzMF3paf9ckKMSE9Tg==} cpu: [x64] os: [linux] - '@img/sharp-libvips-linuxmusl-arm64@1.1.0': - resolution: {integrity: sha512-jYZdG+whg0MDK+q2COKbYidaqW/WTz0cc1E+tMAusiDygrM4ypmSCjOJPmFTvHHJ8j/6cAGyeDWZOsK06tP33w==} + '@img/sharp-libvips-linuxmusl-arm64@1.2.0': + resolution: {integrity: sha512-UG+LqQJbf5VJ8NWJ5Z3tdIe/HXjuIdo4JeVNADXBFuG7z9zjoegpzzGIyV5zQKi4zaJjnAd2+g2nna8TZvuW9Q==} cpu: [arm64] os: [linux] - '@img/sharp-libvips-linuxmusl-x64@1.1.0': - resolution: {integrity: sha512-wK7SBdwrAiycjXdkPnGCPLjYb9lD4l6Ze2gSdAGVZrEL05AOUJESWU2lhlC+Ffn5/G+VKuSm6zzbQSzFX/P65A==} + '@img/sharp-libvips-linuxmusl-x64@1.2.0': + resolution: {integrity: sha512-SRYOLR7CXPgNze8akZwjoGBoN1ThNZoqpOgfnOxmWsklTGVfJiGJoC/Lod7aNMGA1jSsKWM1+HRX43OP6p9+6Q==} cpu: [x64] os: [linux] - '@img/sharp-linux-arm64@0.34.2': - resolution: {integrity: sha512-D8n8wgWmPDakc83LORcfJepdOSN6MvWNzzz2ux0MnIbOqdieRZwVYY32zxVx+IFUT8er5KPcyU3XXsn+GzG/0Q==} + '@img/sharp-linux-arm64@0.34.3': + resolution: {integrity: sha512-QdrKe3EvQrqwkDrtuTIjI0bu6YEJHTgEeqdzI3uWJOH6G1O8Nl1iEeVYRGdj1h5I21CqxSvQp1Yv7xeU3ZewbA==} engines: {node: ^18.17.0 || ^20.3.0 || >=21.0.0} cpu: [arm64] os: [linux] - '@img/sharp-linux-arm@0.34.2': - resolution: {integrity: sha512-0DZzkvuEOqQUP9mo2kjjKNok5AmnOr1jB2XYjkaoNRwpAYMDzRmAqUIa1nRi58S2WswqSfPOWLNOr0FDT3H5RQ==} + '@img/sharp-linux-arm@0.34.3': + resolution: {integrity: sha512-oBK9l+h6KBN0i3dC8rYntLiVfW8D8wH+NPNT3O/WBHeW0OQWCjfWksLUaPidsrDKpJgXp3G3/hkmhptAW0I3+A==} engines: {node: ^18.17.0 || ^20.3.0 || >=21.0.0} cpu: [arm] os: [linux] - '@img/sharp-linux-s390x@0.34.2': - resolution: {integrity: sha512-EGZ1xwhBI7dNISwxjChqBGELCWMGDvmxZXKjQRuqMrakhO8QoMgqCrdjnAqJq/CScxfRn+Bb7suXBElKQpPDiw==} + '@img/sharp-linux-ppc64@0.34.3': + resolution: {integrity: sha512-GLtbLQMCNC5nxuImPR2+RgrviwKwVql28FWZIW1zWruy6zLgA5/x2ZXk3mxj58X/tszVF69KK0Is83V8YgWhLA==} + engines: {node: ^18.17.0 || ^20.3.0 || >=21.0.0} + cpu: [ppc64] + os: [linux] + + '@img/sharp-linux-s390x@0.34.3': + resolution: {integrity: sha512-3gahT+A6c4cdc2edhsLHmIOXMb17ltffJlxR0aC2VPZfwKoTGZec6u5GrFgdR7ciJSsHT27BD3TIuGcuRT0KmQ==} engines: {node: ^18.17.0 || ^20.3.0 || >=21.0.0} cpu: [s390x] os: [linux] - '@img/sharp-linux-x64@0.34.2': - resolution: {integrity: sha512-sD7J+h5nFLMMmOXYH4DD9UtSNBD05tWSSdWAcEyzqW8Cn5UxXvsHAxmxSesYUsTOBmUnjtxghKDl15EvfqLFbQ==} + '@img/sharp-linux-x64@0.34.3': + resolution: {integrity: sha512-8kYso8d806ypnSq3/Ly0QEw90V5ZoHh10yH0HnrzOCr6DKAPI6QVHvwleqMkVQ0m+fc7EH8ah0BB0QPuWY6zJQ==} engines: {node: ^18.17.0 || ^20.3.0 || >=21.0.0} cpu: [x64] os: [linux] - '@img/sharp-linuxmusl-arm64@0.34.2': - resolution: {integrity: sha512-NEE2vQ6wcxYav1/A22OOxoSOGiKnNmDzCYFOZ949xFmrWZOVII1Bp3NqVVpvj+3UeHMFyN5eP/V5hzViQ5CZNA==} + '@img/sharp-linuxmusl-arm64@0.34.3': + resolution: {integrity: sha512-vAjbHDlr4izEiXM1OTggpCcPg9tn4YriK5vAjowJsHwdBIdx0fYRsURkxLG2RLm9gyBq66gwtWI8Gx0/ov+JKQ==} engines: {node: ^18.17.0 || ^20.3.0 || >=21.0.0} cpu: [arm64] os: [linux] - '@img/sharp-linuxmusl-x64@0.34.2': - resolution: {integrity: sha512-DOYMrDm5E6/8bm/yQLCWyuDJwUnlevR8xtF8bs+gjZ7cyUNYXiSf/E8Kp0Ss5xasIaXSHzb888V1BE4i1hFhAA==} + '@img/sharp-linuxmusl-x64@0.34.3': + resolution: {integrity: sha512-gCWUn9547K5bwvOn9l5XGAEjVTTRji4aPTqLzGXHvIr6bIDZKNTA34seMPgM0WmSf+RYBH411VavCejp3PkOeQ==} engines: {node: ^18.17.0 || ^20.3.0 || >=21.0.0} cpu: [x64] os: [linux] - '@img/sharp-wasm32@0.34.2': - resolution: {integrity: sha512-/VI4mdlJ9zkaq53MbIG6rZY+QRN3MLbR6usYlgITEzi4Rpx5S6LFKsycOQjkOGmqTNmkIdLjEvooFKwww6OpdQ==} + '@img/sharp-wasm32@0.34.3': + resolution: {integrity: sha512-+CyRcpagHMGteySaWos8IbnXcHgfDn7pO2fiC2slJxvNq9gDipYBN42/RagzctVRKgxATmfqOSulgZv5e1RdMg==} engines: {node: ^18.17.0 || ^20.3.0 || >=21.0.0} cpu: [wasm32] - '@img/sharp-win32-arm64@0.34.2': - resolution: {integrity: sha512-cfP/r9FdS63VA5k0xiqaNaEoGxBg9k7uE+RQGzuK9fHt7jib4zAVVseR9LsE4gJcNWgT6APKMNnCcnyOtmSEUQ==} + '@img/sharp-win32-arm64@0.34.3': + resolution: {integrity: sha512-MjnHPnbqMXNC2UgeLJtX4XqoVHHlZNd+nPt1kRPmj63wURegwBhZlApELdtxM2OIZDRv/DFtLcNhVbd1z8GYXQ==} engines: {node: ^18.17.0 || ^20.3.0 || >=21.0.0} cpu: [arm64] os: [win32] - '@img/sharp-win32-ia32@0.34.2': - resolution: {integrity: sha512-QLjGGvAbj0X/FXl8n1WbtQ6iVBpWU7JO94u/P2M4a8CFYsvQi4GW2mRy/JqkRx0qpBzaOdKJKw8uc930EX2AHw==} + '@img/sharp-win32-ia32@0.34.3': + resolution: {integrity: sha512-xuCdhH44WxuXgOM714hn4amodJMZl3OEvf0GVTm0BEyMeA2to+8HEdRPShH0SLYptJY1uBw+SCFP9WVQi1Q/cw==} engines: {node: ^18.17.0 || ^20.3.0 || >=21.0.0} cpu: [ia32] os: [win32] - '@img/sharp-win32-x64@0.34.2': - resolution: {integrity: sha512-aUdT6zEYtDKCaxkofmmJDJYGCf0+pJg3eU9/oBuqvEeoB9dKI6ZLc/1iLJCTuJQDO4ptntAlkUmHgGjyuobZbw==} + '@img/sharp-win32-x64@0.34.3': + resolution: {integrity: sha512-OWwz05d++TxzLEv4VnsTz5CmZ6mI6S05sfQGEMrNrQcOEERbX46332IvE7pO/EUiw7jUrrS40z/M7kPyjfl04g==} engines: {node: ^18.17.0 || ^20.3.0 || >=21.0.0} cpu: [x64] os: [win32] @@ -2955,33 +2948,16 @@ packages: '@jridgewell/gen-mapping@0.3.12': resolution: {integrity: sha512-OuLGC46TjB5BbN1dH8JULVVZY4WTdkF7tV9Ys6wLL1rubZnCMstOhNHueU5bLCrnRuDhKPDM4g6sw4Bel5Gzqg==} - '@jridgewell/gen-mapping@0.3.8': - resolution: {integrity: sha512-imAbBGkb+ebQyxKgzv5Hu2nmROxoDOXHh80evxdoXNOrvAnVx7zimzc1Oo5h9RlfV4vPXaE2iM5pOFbvOCClWA==} - engines: {node: '>=6.0.0'} - '@jridgewell/resolve-uri@3.1.2': resolution: {integrity: sha512-bRISgCIjP20/tbWSPWMEi54QVPRZExkuD9lJL+UIxUKtwVJA8wW1Trb1jMs1RFXo1CBTNZ/5hpC9QvmKWdopKw==} engines: {node: '>=6.0.0'} - '@jridgewell/set-array@1.2.1': - resolution: {integrity: sha512-R8gLRTZeyp03ymzP/6Lil/28tGeGEzhx1q2k703KGWRAI1VdvPIXdG70VJc2pAMw3NA6JKL5hhFu1sJX0Mnn/A==} - engines: {node: '>=6.0.0'} - '@jridgewell/source-map@0.3.10': resolution: {integrity: sha512-0pPkgz9dY+bijgistcTTJ5mR+ocqRXLuhXHYdzoMmmoJ2C9S46RCm2GMUbatPEUK9Yjy26IrAy8D/M00lLkv+Q==} - '@jridgewell/source-map@0.3.6': - resolution: {integrity: sha512-1ZJTZebgqllO79ue2bm3rIGud/bOe0pP5BjSRCRxxYkEZS8STV7zN84UBbiYu7jy+eCKSnVIUgoWWE/tt+shMQ==} - - '@jridgewell/sourcemap-codec@1.5.0': - resolution: {integrity: sha512-gv3ZRaISU3fjPAgNsriBRqGWQL6quFx04YMPW/zD8XMLsU32mhCCbfbO6KZFLjvYpCZ8zyDEgqsgf+PwPaM7GQ==} - '@jridgewell/sourcemap-codec@1.5.4': resolution: {integrity: sha512-VT2+G1VQs/9oz078bLrYbecdZKs912zQlkelYpuf+SXF+QvZDYJlbx/LSx+meSAwdDFnF8FVXW92AVjjkVmgFw==} - '@jridgewell/trace-mapping@0.3.25': - resolution: {integrity: sha512-vNk6aEwybGtawWmy/PzwnGDOjCkLWSD2wqvjGGAgOAwCGWySYXfYoxt00IJkTF+8Lb57DwOb3Aa0o9CApepiYQ==} - '@jridgewell/trace-mapping@0.3.29': resolution: {integrity: sha512-uw6guiW/gcAGPDhLmd77/6lW8QLeiV5RUTsAX46Db6oLhGaVj4lhnPwb184s1bkc8kdVg/+h988dro8GRDpmYQ==} @@ -3000,8 +2976,8 @@ packages: peerDependencies: tslib: '2' - '@jsonjoy.com/util@1.5.0': - resolution: {integrity: sha512-ojoNsrIuPI9g6o8UxhraZQSyF2ByJanAY4cTFbc8Mf2AXEF4aQRGY1dJxyJpuyav8r9FGflEt/Ff3u5Nt6YMPA==} + '@jsonjoy.com/util@1.6.0': + resolution: {integrity: sha512-sw/RMbehRhN68WRtcKCpQOPfnH6lLP4GJfqzi3iYej8tnzpZUDr6UkZYJjcjjC0FWEJOJbyM3PTIwxucUmDG2A==} engines: {node: '>=10.0'} peerDependencies: tslib: '2' @@ -3021,25 +2997,25 @@ packages: '@jupyter-widgets/output@4.1.7': resolution: {integrity: sha512-S3cN9QlEpTl/vkBDT8p3BelL3kpvJrkGJv2EZEOV7EgYNGVqh+jiOopMio8T9JCKL0dvyp8FwVS3g0Fcw6eD+A==} - '@jupyter/ydoc@3.0.3': - resolution: {integrity: sha512-DYkLU7bsf5RT6IwrK6GW380dEZbK3q10lo7Ddt8smxKWDrBsQgbdYmQswBtavyihMMDtBlrczjx2OYPFz3Ku8Q==} + '@jupyter/ydoc@3.1.0': + resolution: {integrity: sha512-+hLNUBZr8Zz8NiuaoYKINDURDJHbjFGxg8EcSU52y+rBe412TsoFxfPSng4eP7w3cZFrVlm7D+8K0nAMHxj0ZQ==} - '@jupyterlab/coreutils@6.4.0': - resolution: {integrity: sha512-VXjcKLvXSoRjQWRedFOXPpsnhANg9o8Ao0IGo37X79dxrG1HtbZXe8vRLUBNKYsDxWcYEIIeXH9u+pyxTyBeXg==} + '@jupyterlab/coreutils@6.4.4': + resolution: {integrity: sha512-Isqwm/HbypOA9sV1wnE6sXiePbYNFjV9IEzIXnUww6PpTLbE79oYgPWkdcGbaXzsqhgpBYDYgmnnUIFgDC4JZQ==} - '@jupyterlab/nbformat@4.4.0': - resolution: {integrity: sha512-TDXQmsathEz9Ljbu1GDDzqgs2YS3/PX7rqp2shYWAYwvlzypDLTD8A2E7wyIp3L+9Kir1WuHxRkhl3yW2mkvfQ==} + '@jupyterlab/nbformat@4.4.4': + resolution: {integrity: sha512-xDCN95X7UB41TXoCwqCVLDGYlJRtkl5PMy9ACDgxxXonIwNoIpmPErg7AdPwn4ISKyI6LexqnV3HoVNy43ZRsA==} - '@jupyterlab/services@7.4.0': - resolution: {integrity: sha512-aSRDiy77zLFOVq+ERNJ/0bJwskzEwkdZQ445WS8ZcqSUZWywDX2ToIE1FIQharS9EGoG97uWx+pwOqG9RBxZWw==} + '@jupyterlab/services@7.4.4': + resolution: {integrity: sha512-yS9DEJx7Hhc6rB1qoiChDY3+DsDH6sLap+GnF3AAXtWnfcUoKeegTeoBkb8oaNwKZYclsjuQAuOYY/QASBDDRQ==} - '@jupyterlab/settingregistry@4.4.0': - resolution: {integrity: sha512-aBIEgoETF3zD2MrN1ntOzYAYJLTQT39oKyNGiOYDKsAJ3wbUajirCKjDk8dkNmbtN3VR/iNtugAVkLqTnnZQUw==} + '@jupyterlab/settingregistry@4.4.4': + resolution: {integrity: sha512-9ZbJzjyYNGa4Pj9FhdxWUtMaBCbro0k149pyx5RFnwziT09VxtOwjvt9pIxCoOdRr9H3iCXOkGUviYszADNNZw==} peerDependencies: react: '>=16' - '@jupyterlab/statedb@4.4.0': - resolution: {integrity: sha512-fjYfcJ08iq9DYRWg2qEuoq4gtIm+F9oU8x8tflKJ0UZZ+HpTQDRqChrWUCQ/nijymD8zs05BG3J63TUXlpTYnw==} + '@jupyterlab/statedb@4.4.4': + resolution: {integrity: sha512-F9OFm3EqLmBZ0Y2M6N/7s43/swKXFmcF6qkmoSiepsWI+bpXvH4e5S0v+hWyw0Sdbr050v47bx4YrAeB8E4PYA==} '@langchain/anthropic@0.3.24': resolution: {integrity: sha512-Gi1TwXu5vkCxUMToiXaiwTTWq9v3WMyU3ldB/VEWjzbkr3nKF5kcp+HLqhvV7WWOFVTTNgG+pzfq8JALecq5MA==} @@ -3123,6 +3099,9 @@ packages: '@lumino/dragdrop@1.14.5': resolution: {integrity: sha512-LC5xB82+xGF8hFyl716TMpV32OIMIMl+s3RU1PaqDkD6B7PkgiVk6NkJ4X9/GcEvl2igkvlGQt/3L7qxDAJNxw==} + '@lumino/dragdrop@2.1.6': + resolution: {integrity: sha512-N9aqdOYl5HTuTAIVvjTXxlPKK3e9JAj5yEtJ4nA/tNE7J8C9y3BRQ4fBAtk7O0z/8yx8tO8a0j5oSt2zAfvYuA==} + '@lumino/keyboard@1.8.2': resolution: {integrity: sha512-Dy+XqQ1wXbcnuYtjys5A0pAqf4SpAFl9NY6owyIhXAo0Va7w3LYp3jgiP1xAaBAwMuUppiUAfrbjrysZuZ625g==} @@ -3159,6 +3138,9 @@ packages: '@lumino/widgets@1.37.2': resolution: {integrity: sha512-NHKu1NBDo6ETBDoNrqSkornfUCwc8EFFzw6+LWBfYVxn2PIwciq2SdiJGEyNqL+0h/A9eVKb5ui5z4cwpRekmQ==} + '@lumino/widgets@2.7.1': + resolution: {integrity: sha512-gGq3zB1260gG1aK1m3SkqVoWZ/yfUxCKZvUOmRKLIcZPJUs33bgessm4P65oY5C1eAXalU4erLmKBiBaOn5gtw==} + '@lydell/node-pty-darwin-arm64@1.1.0': resolution: {integrity: sha512-7kFD+owAA61qmhJCtoMbqj3Uvff3YHDiU+4on5F2vQdcMI3MuwGi7dM6MkFG/yuzpw8LF2xULpL71tOPUfxs0w==} cpu: [arm64] @@ -3244,8 +3226,8 @@ packages: react: '>= 15' react-dom: '>= 15' - '@mistralai/mistralai@1.5.2': - resolution: {integrity: sha512-mBTIDQmuAX9RowMYteZFHJIYlEwDcHzzaxgXzrFtlvH9CkKXK7R1VnZ1sZSe+uLMg0dIXUVdPRUh1SwyFeSqXw==} + '@mistralai/mistralai@1.7.4': + resolution: {integrity: sha512-wty9hHEvIJ5RS8+75NY+a1zXtCCqYQgI26e8R2N7O9ZvD16ep2kF6ciJD2EiWOgS/K+iycRGsrI3nqgkPLG/Xw==} peerDependencies: zod: '>= 3' @@ -3289,75 +3271,75 @@ packages: resolution: {integrity: sha512-JEW4DEtBzfe8HvUYecLU9e6+XJnKDlUAIve8FvPzF3Kzs6Xo/KuZkZJsDH0wJXl/qEZbeeE7edxDNY3kMs39hQ==} engines: {node: '>= 18'} - '@napi-rs/canvas-android-arm64@0.1.69': - resolution: {integrity: sha512-4icWTByY8zPvM9SelfQKf3I6kwXw0aI5drBOVrwfER5kjwXJd78FPSDSZkxDHjvIo9Q86ljl18Yr963ehA4sHQ==} + '@napi-rs/canvas-android-arm64@0.1.73': + resolution: {integrity: sha512-s8dMhfYIHVv7gz8BXg3Nb6cFi950Y0xH5R/sotNZzUVvU9EVqHfkqiGJ4UIqu+15UhqguT6mI3Bv1mhpRkmMQw==} engines: {node: '>= 10'} cpu: [arm64] os: [android] - '@napi-rs/canvas-darwin-arm64@0.1.69': - resolution: {integrity: sha512-HOanhhYlHdukA+unjelT4Dg3ta7e820x87/AG2dKUMsUzH19jaeZs9bcYjzEy2vYi/dFWKz7cSv2yaIOudB8Yg==} + '@napi-rs/canvas-darwin-arm64@0.1.73': + resolution: {integrity: sha512-bLPCq8Yyq1vMdVdIpQAqmgf6VGUknk8e7NdSZXJJFOA9gxkJ1RGcHOwoXo7h0gzhHxSorg71hIxyxtwXpq10Rw==} engines: {node: '>= 10'} cpu: [arm64] os: [darwin] - '@napi-rs/canvas-darwin-x64@0.1.69': - resolution: {integrity: sha512-SIp7WfhxAPnSVK9bkFfJp+84rbATCIq9jMUzDwpCLhQ+v+OqtXe4pggX1oeV+62/HK6BT1t18qRmJfyqwJ9f3g==} + '@napi-rs/canvas-darwin-x64@0.1.73': + resolution: {integrity: sha512-GR1CcehDjdNYXN3bj8PIXcXfYLUUOQANjQpM+KNnmpRo7ojsuqPjT7ZVH+6zoG/aqRJWhiSo+ChQMRazZlRU9g==} engines: {node: '>= 10'} cpu: [x64] os: [darwin] - '@napi-rs/canvas-linux-arm-gnueabihf@0.1.69': - resolution: {integrity: sha512-Ls+KujCp6TGpkuMVFvrlx+CxtL+casdkrprFjqIuOAnB30Mct6bCEr+I83Tu29s3nNq4EzIGjdmA3fFAZG/Dtw==} + '@napi-rs/canvas-linux-arm-gnueabihf@0.1.73': + resolution: {integrity: sha512-cM7F0kBJVFio0+U2iKSW4fWSfYQ8CPg4/DRZodSum/GcIyfB8+UPJSRM1BvvlcWinKLfX1zUYOwonZX9IFRRcw==} engines: {node: '>= 10'} cpu: [arm] os: [linux] - '@napi-rs/canvas-linux-arm64-gnu@0.1.69': - resolution: {integrity: sha512-m8VcGmeSBNRbHZBd1srvdM1aq/ScS2y8KqGqmCCEgJlytYK4jdULzAo2K/BPKE1v3xvn8oUPZDLI/NBJbJkEoA==} + '@napi-rs/canvas-linux-arm64-gnu@0.1.73': + resolution: {integrity: sha512-PMWNrMON9uz9klz1B8ZY/RXepQSC5dxxHQTowfw93Tb3fLtWO5oNX2k9utw7OM4ypT9BUZUWJnDQ5bfuXc/EUQ==} engines: {node: '>= 10'} cpu: [arm64] os: [linux] - '@napi-rs/canvas-linux-arm64-musl@0.1.69': - resolution: {integrity: sha512-a3xjNRIeK2m2ZORGv2moBvv3vbkaFZG1QKMeiEv/BKij+rkztuEhTJGMar+buICFgS0fLgphXXsKNkUSJb7eRQ==} + '@napi-rs/canvas-linux-arm64-musl@0.1.73': + resolution: {integrity: sha512-lX0z2bNmnk1PGZ+0a9OZwI2lPPvWjRYzPqvEitXX7lspyLFrOzh2kcQiLL7bhyODN23QvfriqwYqp5GreSzVvA==} engines: {node: '>= 10'} cpu: [arm64] os: [linux] - '@napi-rs/canvas-linux-riscv64-gnu@0.1.69': - resolution: {integrity: sha512-pClUoJF5wdC9AvD0mc15G9JffL1Q85nuH1rLSQPRkGmGmQOtRjw5E9xNbanz7oFUiPbjH7xcAXUjVAcf7tdgPQ==} + '@napi-rs/canvas-linux-riscv64-gnu@0.1.73': + resolution: {integrity: sha512-QDQgMElwxAoADsSR3UYvdTTQk5XOyD9J5kq15Z8XpGwpZOZsSE0zZ/X1JaOtS2x+HEZL6z1S6MF/1uhZFZb5ig==} engines: {node: '>= 10'} cpu: [riscv64] os: [linux] - '@napi-rs/canvas-linux-x64-gnu@0.1.69': - resolution: {integrity: sha512-96X3bFAmzemfw84Ts6Jg/omL86uuynvK06MWGR/mp3JYNumY9RXofA14eF/kJIYelbYFWXcwpbcBR71lJ6G/YQ==} + '@napi-rs/canvas-linux-x64-gnu@0.1.73': + resolution: {integrity: sha512-wbzLJrTalQrpyrU1YRrO6w6pdr5vcebbJa+Aut5QfTaW9eEmMb1WFG6l1V+cCa5LdHmRr8bsvl0nJDU/IYDsmw==} engines: {node: '>= 10'} cpu: [x64] os: [linux] - '@napi-rs/canvas-linux-x64-musl@0.1.69': - resolution: {integrity: sha512-2QTsEFO72Kwkj53W9hc5y1FAUvdGx0V+pjJB+9oQF6Ys9+y989GyPIl5wZDzeh8nIJW6koZZ1eFa8pD+pA5BFQ==} + '@napi-rs/canvas-linux-x64-musl@0.1.73': + resolution: {integrity: sha512-xbfhYrUufoTAKvsEx2ZUN4jvACabIF0h1F5Ik1Rk4e/kQq6c+Dwa5QF0bGrfLhceLpzHT0pCMGMDeQKQrcUIyA==} engines: {node: '>= 10'} cpu: [x64] os: [linux] - '@napi-rs/canvas-win32-x64-msvc@0.1.69': - resolution: {integrity: sha512-Q4YA8kVnKarApBVLu7F8icGlIfSll5Glswo5hY6gPS4Is2dCI8+ig9OeDM8RlwYevUIxKq8lZBypN8Q1iLAQ7w==} + '@napi-rs/canvas-win32-x64-msvc@0.1.73': + resolution: {integrity: sha512-YQmHXBufFBdWqhx+ympeTPkMfs3RNxaOgWm59vyjpsub7Us07BwCcmu1N5kildhO8Fm0syoI2kHnzGkJBLSvsg==} engines: {node: '>= 10'} cpu: [x64] os: [win32] - '@napi-rs/canvas@0.1.69': - resolution: {integrity: sha512-ydvNeJMRm+l3T14yCoUKqjYQiEdXDq1isznI93LEBGYssXKfSaLNLHOkeM4z9Fnw9Pkt2EKOCAtW9cS4b00Zcg==} + '@napi-rs/canvas@0.1.73': + resolution: {integrity: sha512-9iwPZrNlCK4rG+vWyDvyvGeYjck9MoP0NVQP6N60gqJNFA1GsN0imG05pzNsqfCvFxUxgiTYlR8ff0HC1HXJiw==} engines: {node: '>= 10'} '@napi-rs/triples@1.2.0': resolution: {integrity: sha512-HAPjR3bnCsdXBsATpDIP5WCrw0JcACwhhrwIAQhiR46n+jm+a2F8kBsfseAuWtSyQ+H3Yebt2k43B5dy+04yMA==} - '@napi-rs/wasm-runtime@0.2.11': - resolution: {integrity: sha512-9DPkXtvHydrcOsopiYpUgPHpmj0HWZKMUnL2dZqpvC42lsratuBG06V5ipyno0fUek5VlFsNQ+AcFATSrJXgMA==} + '@napi-rs/wasm-runtime@0.2.12': + resolution: {integrity: sha512-ZVWUcfwY4E/yPitQJl481FjFo3K22D6qF0DuFH6Y/nbnE11GY5uguDxZMGXPQ8WQ0128MXQD7TnfHyK4oWoIJQ==} '@nestjs/axios@4.0.0': resolution: {integrity: sha512-1cB+Jyltu/uUPNQrpUimRHEQHrnQrpLzVj6dU3dgn6iDDDdahr10TgHFGTmw5VuJ9GzKZsCLDL78VSwJAs/9JQ==} @@ -3475,8 +3457,8 @@ packages: resolution: {integrity: sha512-oGB+UxlgWcgQkgwo8GcEGwemoTFt3FIO9ababBmaGwXIoBKZ+GTy0pP185beGg7Llih/NSHSV2XAs1lnznocSg==} engines: {node: '>= 8'} - '@nteract/commutable@7.5.0': - resolution: {integrity: sha512-rOUkWJdPif/1wbBEAJa4hHxLhhZRJ2/9xWt4RletIcWoAhPCJZkvUDYcS5ZIWUJ2nZoUyLiUhAD2qx5+PAS4CA==} + '@nteract/commutable@7.5.1': + resolution: {integrity: sha512-Z4dz6tUuQJGeCeB7zp2ZTYTnkyRxvGWWozS9n3QuoB9k18GeKdEgT7Rzkt7DbzBpKwmSrHaMVJETLiOVXj4bWA==} '@nteract/types@7.1.9': resolution: {integrity: sha512-a7lGMWdjfz2QGlZbAiFHifU9Nhk9ntwg/iKUTMIMRPY1Wfs5UreHSMt+vZ8OY5HGjxicfHozBatGDKXeKXFHMQ==} @@ -3637,11 +3619,6 @@ packages: resolution: {integrity: sha512-YLT9Zo3oNPJoBjBc4q8G2mjU4tqIbf5CEOORbUUr48dCD9q3umJ3IPlVqOqDakPfd2HuwccBaqlGhN4Gmr5OWg==} engines: {node: ^12.20.0 || ^14.18.0 || >=16.0.0} - '@playwright/test@1.51.1': - resolution: {integrity: sha512-nM+kEaTSAoVlXmMPH10017vn3FSiFqr/bh4fKg9vmAdMfd9SDqRZNvPSiAHADc/itWak+qPvMPZQOPwCBW7k7Q==} - engines: {node: '>=18'} - hasBin: true - '@plotly/d3-sankey-circular@0.33.1': resolution: {integrity: sha512-FgBV1HEvCr3DV7RHhDsPXyryknucxtfnLwPtCKKxdolKyTFYoLX/ibEfX39iFYIL7DYbVeRtP43dbFcrHNE+KQ==} @@ -3778,8 +3755,8 @@ packages: '@rinsuki/lz4-ts@1.0.1': resolution: {integrity: sha512-vkQP6c9GGEvsND/tY6eyb5g6bq+zzbI8VcAPozr6siisIZKb4tq1Jr6Z2+5GqkEjxHGLCfncrBWR1h86t8CEIg==} - '@rjsf/utils@5.24.8': - resolution: {integrity: sha512-Y/D1orNsTnIswp23QtWKq7QEme07/nSdnSJoK4gSsm2cDBkSCW+9KUsWSvmE7poqHwCn3zpbIaa3ZWEAQUJEEg==} + '@rjsf/utils@5.24.12': + resolution: {integrity: sha512-fDwQB0XkjZjpdFUz5UAnuZj8nnbxDbX5tp+jTOjjJKw2TMQ9gFFYCQ12lSpdhezA2YgEGZfxyYTGW0DKDL5Drg==} engines: {node: '>=14'} peerDependencies: react: ^16.14.0 || >=17 @@ -3789,8 +3766,8 @@ packages: cpu: [arm64] os: [darwin] - '@rspack/binding-darwin-arm64@1.4.4': - resolution: {integrity: sha512-r5Vr1DcKXemYfJhNabRTpRonZvzyRq8H7ggDJqxWpIR+SGmtZ62hErSic8DBFeEF5k8SZc5in6L4YXUZgYaibg==} + '@rspack/binding-darwin-arm64@1.4.6': + resolution: {integrity: sha512-K37H8e58eY7zBHGeMVtT7m0Z5XvlNQX7YDuaxlbiA4hZxqeRoS5BMX/YOcDiGdNbSuqv+iG5GSckJ99YUI67Cw==} cpu: [arm64] os: [darwin] @@ -3799,8 +3776,8 @@ packages: cpu: [x64] os: [darwin] - '@rspack/binding-darwin-x64@1.4.4': - resolution: {integrity: sha512-fyHjrug2xT3CU3nqzviL41I1PfJOv2/5T1+TdME8GzM5grWI1XFnCcXXocKhGuEpv6xHFdRZz9x7C9k7uQ4UCw==} + '@rspack/binding-darwin-x64@1.4.6': + resolution: {integrity: sha512-3p5u9q/Q9MMVe+5XFJ/WiFrzNrrxUjJFR19kB1k/KMcf8ox982xWjnfJuBkET/k7Hn0EZL7L06ym447uIfAVAg==} cpu: [x64] os: [darwin] @@ -3809,8 +3786,8 @@ packages: cpu: [arm64] os: [linux] - '@rspack/binding-linux-arm64-gnu@1.4.4': - resolution: {integrity: sha512-8UDmKFUgrt4w/sBHZtixBppAC+ObOwRbm3oSMhZMn+T3+BhBSDCUXhbGQDiKc7WG04cxyGhvwIocBckkdb1rSg==} + '@rspack/binding-linux-arm64-gnu@1.4.6': + resolution: {integrity: sha512-ZrrCn5b037ImZfZ3MShJrRw4d5M3Tq2rFJupr+SGMg7GTl2T6xEmo3ER/evHlT6e0ETi6tRWPxC9A1125jbSQA==} cpu: [arm64] os: [linux] @@ -3819,8 +3796,8 @@ packages: cpu: [arm64] os: [linux] - '@rspack/binding-linux-arm64-musl@1.4.4': - resolution: {integrity: sha512-Wtf9WR6VXYa1Y+uTa5uaTBcTX0eVzyM6d/0pLS6qJgwLYN4wOvi0VcOctLkUNaMpA72TsmGOnls8QBsDPLZRtg==} + '@rspack/binding-linux-arm64-musl@1.4.6': + resolution: {integrity: sha512-0a30oR6ZmZrqmsOHQYrbZPCxAgnqAiqlbFozdhHs+Yu2bS7SDiLpdjMg0PHwLZT2+siiMWsLodFZlXRJE54oAQ==} cpu: [arm64] os: [linux] @@ -3829,8 +3806,8 @@ packages: cpu: [x64] os: [linux] - '@rspack/binding-linux-x64-gnu@1.4.4': - resolution: {integrity: sha512-vc0e6ZkXJIVwHXDfkxFb62e/OpX0KuekjvD+rzs7A122Nt7R37YSilqGpZXWDlqlRjJlBxA73ORakJORsR3oww==} + '@rspack/binding-linux-x64-gnu@1.4.6': + resolution: {integrity: sha512-u6pq1aq7bX+NABVDDTOzH64KMj1KJn8fUWO+FaX7Kr7PBjhmxNRs4OaWZjbXEY6COhMYEJZ04h4DhY+lRzcKjA==} cpu: [x64] os: [linux] @@ -3839,13 +3816,13 @@ packages: cpu: [x64] os: [linux] - '@rspack/binding-linux-x64-musl@1.4.4': - resolution: {integrity: sha512-PL5iL2CbdDZwI6MBOfTQnryqT9esjPDZP6a2bxbT+IiyWyBoZjCXnjwYOB5dvIL4Hyrma8XJFwT5dAlFvGrzyQ==} + '@rspack/binding-linux-x64-musl@1.4.6': + resolution: {integrity: sha512-rjP/1dWKB828kzd4/QpDYNVasUAKDj0OeRJGh5L/RluSH3pEqhxm5FwvndpmFDv6m3iPekZ4IO26UrpGJmE9fw==} cpu: [x64] os: [linux] - '@rspack/binding-wasm32-wasi@1.4.4': - resolution: {integrity: sha512-/+uq1R+xzXknBDbcZWR0sbQwasZ2maPDSJ1rsnlBG6lQc447HbuwwZqjMpD8+TjpNunAS1E1mHuxql5IbL5UKg==} + '@rspack/binding-wasm32-wasi@1.4.6': + resolution: {integrity: sha512-5M0g7TaWgCFQJr4NKYW2bTLbQJuAQIgZL7WmiDwotgscBJDQWJVBayFEsnM6PYX1Inmu6RNhQ44BKIYwwoSyYw==} cpu: [wasm32] '@rspack/binding-win32-arm64-msvc@1.3.4': @@ -3853,8 +3830,8 @@ packages: cpu: [arm64] os: [win32] - '@rspack/binding-win32-arm64-msvc@1.4.4': - resolution: {integrity: sha512-8advF9WPaq4HndjeYIsUX7GNPMqJ8vTalZLdF1bJ0c1PXyp3igyG6ruJeJ4vsXT3/HmVy1AmK3FzHRmy7AT5Mw==} + '@rspack/binding-win32-arm64-msvc@1.4.6': + resolution: {integrity: sha512-thPCdbh4O+uEAJ8AvXBWZIOW0ZopJAN3CX2zlprso8Cnhi4wDseTtrIxFQh7cTo6pR3xSZAIv/zHd+MMF8TImA==} cpu: [arm64] os: [win32] @@ -3863,8 +3840,8 @@ packages: cpu: [ia32] os: [win32] - '@rspack/binding-win32-ia32-msvc@1.4.4': - resolution: {integrity: sha512-I3BqOEu8gHoMvxECdHS+a+fPMipzO3yrI+0uBjzeJY7UpkD9hjNH6MU2xTI8FxUDY2XYNbJv1EJkXd72VzSpaA==} + '@rspack/binding-win32-ia32-msvc@1.4.6': + resolution: {integrity: sha512-KQmm6c/ZfJKQ/TpzbY6J0pDvUB9kwTXzp+xl2FhGq2RXid8uyDS8ZqbeJA6LDxgttsmp4PRVJjMdLVYjZenfLw==} cpu: [ia32] os: [win32] @@ -3873,19 +3850,19 @@ packages: cpu: [x64] os: [win32] - '@rspack/binding-win32-x64-msvc@1.4.4': - resolution: {integrity: sha512-8Ju4ZSbBS6VLcgf53OTJcfMWYIR0pHSdHhfYZC16Vb5weWa89Hh0v3ClA0PqSt1hnAAFCMMOM9CcAPwk8P3gIg==} + '@rspack/binding-win32-x64-msvc@1.4.6': + resolution: {integrity: sha512-WRRhCsJ+xcOmvzo/r/b2UTejPLnDEbaD/te1yQwHe97sUaFGr3u1Njk6lVYRTV6mEvUopEChb8yAq/S4dvaGLg==} cpu: [x64] os: [win32] '@rspack/binding@1.3.4': resolution: {integrity: sha512-wDRqqNfrVXuHAEm25mPlhroKN+v4uwhihVnZF4duz0I0L5rbsUNCy7uEda0GrBXkj3jkKLfg60mSd9MCZD0JZw==} - '@rspack/binding@1.4.4': - resolution: {integrity: sha512-Z4Ir04eLbq5BwkSF74h/dBtkbTpcGrMtmi5b6YqMvFtGrT12R6K3P58hnXmrxqypKncbW4rI0JJOYkQa+gMteg==} + '@rspack/binding@1.4.6': + resolution: {integrity: sha512-rRc6sbKWxhomxxJeqi4QS3S/2T6pKf4JwC/VHXs7KXw7lHXHa3yxPynmn3xHstL0H6VLaM5xQj87Wh7lQYRAPg==} - '@rspack/cli@1.4.4': - resolution: {integrity: sha512-jH1YE82MGTq/zs4nKZ+jBEnLc2VpB7+Uf0A5UNPhe0RQyEcGFi3WWXCKgGEUsIcqy5uvT+bDJPGk+UgPrUiv3Q==} + '@rspack/cli@1.4.6': + resolution: {integrity: sha512-9ltEQMQ6P6FM/DSqFQMGn1ZpZ8sespD8+WyQZT+Vz5ICVAwvN/AbkLJ/yriLNPZS7vJHvnki+NKBpjotxI6BsA==} hasBin: true peerDependencies: '@rspack/core': ^1.0.0-alpha || ^1.x @@ -3902,8 +3879,8 @@ packages: '@swc/helpers': optional: true - '@rspack/core@1.4.4': - resolution: {integrity: sha512-TqEUHXbG5zNQ72djFfEg2A1/RoQF57QUhBU22ZLspbr3GcWmHou6noAa6i7lMn47RE4LWVnNyOCyMZyjXrrvYA==} + '@rspack/core@1.4.6': + resolution: {integrity: sha512-/OpJLv7dPEE7x/qCXGecRm9suNxz5w0Dheq2sh0TjTCUHodtMET3T+FlRWznBAlZeNuHLECDp0DWhchgS8BWuA==} engines: {node: '>=16.0.0'} peerDependencies: '@swc/helpers': '>=0.5.1' @@ -3970,18 +3947,9 @@ packages: '@sinonjs/fake-timers@13.0.5': resolution: {integrity: sha512-36/hTbH2uaWuGVERyC6da9YwGWnzUZXuPro/F2LfsdOsLnCojz/iSH8MxUt/FD2S5XBSVPhmArFUXcpCQ2Hkiw==} - '@socket.io/cluster-adapter@0.2.2': - resolution: {integrity: sha512-/tNcY6qQx0BOgjl4mFk3YxX6pjaPdEyeWhP88Ea9gTlISY4SfA7t8VxbryeAs5/9QgXzChlvSN/i37Gog3kWag==} - engines: {node: '>=10.0.0'} - peerDependencies: - socket.io-adapter: ^2.4.0 - '@socket.io/component-emitter@3.1.2': resolution: {integrity: sha512-9BCxFwvbGg/RsZK9tjXd8s4UcwR0MWeFQ1XEKIQVVvAGJyINdrqKMcTRyLoK8Rse1GjzLV9cwjWV1olXRWEXVA==} - '@socket.io/sticky@1.0.4': - resolution: {integrity: sha512-VuauT5CJLvzYtKIgouFSQ8rUaygseR+zRutnwh6ZA2QYcXx+8g52EoJ8V2SLxfo+Tfs3ELUDy08oEXxlWNrxaw==} - '@stripe/react-stripe-js@3.7.0': resolution: {integrity: sha512-PYls/2S9l0FF+2n0wHaEJsEU8x7CmBagiH7zYOsxbBlLIHEsqUIQ4MlIAbV9Zg6xwT8jlYdlRIyBTHmO3yM7kQ==} peerDependencies: @@ -4055,8 +4023,8 @@ packages: '@turf/meta@7.2.0': resolution: {integrity: sha512-igzTdHsQc8TV1RhPuOLVo74Px/hyPrVgVOTgjWQZzt3J9BVseCdpfY/0cJBdlSRI4S/yTmmHl7gAqjhpYH5Yaw==} - '@tybys/wasm-util@0.9.0': - resolution: {integrity: sha512-6+7nlbMVX/PVDCwaIQ8nTOPveOcFLSt8GcXdx8hD0bt39uWxYT88uXzqTd4fTvqta7oeUJqudepapKNt2DYJFw==} + '@tybys/wasm-util@0.10.0': + resolution: {integrity: sha512-VyyPYFlOMNylG45GoAe0xDoLwWuowvf92F9kySqzYh8vmYm7D2u4iUJKa1tOUpS70Ku13ASrOkS4ScXFsTaCNQ==} '@types/aria-query@5.0.4': resolution: {integrity: sha512-rfT93uj5s0PRL7EzccGMs3brplhcrghnDoV26NqKhCAS1hVo+WdNsPvE/yb6ilfr5hi2MEk6d5EWJTKdxg8jVw==} @@ -4067,14 +4035,14 @@ packages: '@types/babel__core@7.20.5': resolution: {integrity: sha512-qoQprZvz5wQFJwMDqeseRXWv3rqMvhgpbXFfVyWhbx9X47POIA6i/+dXefEmZKoAgOaTdaIgNSMqMIU61yRyzA==} - '@types/babel__generator@7.6.8': - resolution: {integrity: sha512-ASsj+tpEDsEiFr1arWrlN6V3mdfjRMZt6LtK/Vp/kreFLnr5QH5+DhvD5nINYZXzwJvXeGq+05iUXcAzVrqWtw==} + '@types/babel__generator@7.27.0': + resolution: {integrity: sha512-ufFd2Xi92OAVPYsy+P4n7/U7e68fex0+Ee8gSG9KX7eo084CWiQ4sdxktvdl0bOPupXtVJPY19zk6EwWqUQ8lg==} '@types/babel__template@7.4.4': resolution: {integrity: sha512-h/NUaSyG5EyxBIp8YRxo4RMe2/qQgvyowRwVMzhYhBCONbW8PUsg4lkFMrhgZhUe5z3L3MiLDuvyJ/CaPa2A8A==} - '@types/babel__traverse@7.20.5': - resolution: {integrity: sha512-WXCyOcRtH37HAUkpXhUduaxdm82b4GSlyTqajXviN4EfiuPgNYR109xMCKvpl6zPIpua0DGlMEDCq+g8EdoheQ==} + '@types/babel__traverse@7.20.7': + resolution: {integrity: sha512-dkO5fhS7+/oos4ciWxyEyjWe48zmG6wbCheo/G2ZnHx4fs3EU6YC6UM8rk56gAjNJ9P3MTH2jo5jb92/K6wbng==} '@types/backbone@1.4.14': resolution: {integrity: sha512-85ldQ99fiYTJFBlZuAJRaCdvTZKZ2p1fSs3fVf+6Ub6k1X0g0hNJ0qJ/2FOByyyAQYLtbEz3shX5taKQfBKBDw==} @@ -4088,9 +4056,6 @@ packages: '@types/better-sqlite3@7.6.13': resolution: {integrity: sha512-NMv9ASNARoKksWtsq/SHakpYAYnhBrQgGD8zkLYk/jaK8jUGn08CfEdTRgYhMypUQAfzSP8W6gNLe0q19/t4VA==} - '@types/body-parser@1.19.5': - resolution: {integrity: sha512-fB3Zu92ucau0iQ0JMCFQE7b/dv8Ot07NI3KaZIkIUNXq82k4eBAqUaneXfleGY9JWskeS9y+u0nXMyspcuQrCg==} - '@types/body-parser@1.19.6': resolution: {integrity: sha512-HLFeCYgz89uk22N5Qg3dvGvsv46B8GLvKKo1zKG4NybA8U2DiEO3w9lqGg29t/tfLRJpJ6iQxnVw4OnB7MoM9g==} @@ -4109,8 +4074,8 @@ packages: '@types/connect-history-api-fallback@1.5.4': resolution: {integrity: sha512-n6Cr2xS1h4uAulPRdlw6Jl6s1oG8KrVilPN2yUITEs+K48EzMJJ3W1xy8K5eWuFvjp3R74AOIGSmp2UfBJ8HFw==} - '@types/connect@3.4.35': - resolution: {integrity: sha512-cdeYyv4KWoEgpBISTxWvqYsVy444DOqehiF3fM3ne10AmJ62RSyNkUnxMJXHQWRQQX2eR94m5y1IZyDwBjV9FQ==} + '@types/connect@3.4.38': + resolution: {integrity: sha512-K6uROf1LD88uDQqJCktA4yzL1YYAK6NgfsI0v/mTgyPKWsX1CnJ0XPSDhViejru1GcRkLWb8RlzFYJRqGUbaug==} '@types/cookie@0.3.3': resolution: {integrity: sha512-LKVP3cgXBT9RYj+t+9FDKwS5tdI+rPBXaNSkma7hvqy35lc7mAokC2zsqWJH0LaqIt3B962nuYI77hsJoT1gow==} @@ -4118,8 +4083,8 @@ packages: '@types/cookie@0.6.0': resolution: {integrity: sha512-4Kh9a6B2bQciAhf7FSuMRRkUWecJgJu9nPnx3yzpsfXX/c50REIqpHY4C82bXP90qrLtXtkDxTZosYO3UpOwlA==} - '@types/cors@2.8.18': - resolution: {integrity: sha512-nX3d0sxJW41CqQvfOzVG1NCTXfFDrDWIghCZncpHeWlVFd81zxB/DLhg7avFg6eHLCRX7ckBmoIIcqa++upvJA==} + '@types/cors@2.8.19': + resolution: {integrity: sha512-mFNylyeyqN93lfe/9CSxOGREz8cpzAhH+E93xJ4xWQf62V8sQ/24reV2nyzUWM6H6Xji+GGHpkbLe7pVoUEskg==} '@types/d3-array@3.2.1': resolution: {integrity: sha512-Y2Jn2idRrLzUfAKV2LyRImR+y4oa2AntrgID95SHJxuMUrkNXmanDSed71sRNZysveJVt1hLLemQZIady0FpEg==} @@ -4229,15 +4194,9 @@ packages: '@types/eslint@9.6.1': resolution: {integrity: sha512-FXx2pKgId/WyYo2jXw63kk7/+TY7u7AziEJxJAnSFzHlqTAS3Ync6SvgYAN/k4/PQpnnVuzoMuVnByKK2qp0ag==} - '@types/estree@1.0.6': - resolution: {integrity: sha512-AYnb1nQyY49te+VRAVgmzfcgjYS91mY5P0TKUDCLEM+gNnA+3T6rWITXRLYCpahpqSQbN5cE+gHpnPyXjHWxcw==} - '@types/estree@1.0.8': resolution: {integrity: sha512-dWHzHa2WqEXI/O1E9OjrocMTKJl2mSrEolh1Iomrv6U+JuNwaHXsXx9bLu5gG7BUWFIN0skIQJQ/L1rIex4X6w==} - '@types/express-serve-static-core@4.19.0': - resolution: {integrity: sha512-bGyep3JqPCRry1wq+O5n7oiBgGWmeIJXPjXXCo8EK0u8duZGSYar7cGqd3ML2JUsLGeB7fmc06KYo9fLGWqPvQ==} - '@types/express-serve-static-core@4.19.6': resolution: {integrity: sha512-N4LZ2xG7DatVqhCZzOGb1Yi5lMbXSZcmdLDe9EzSndPV2HpWYWzRbaerl2n27irrm94EPpprqa8KpskPT085+A==} @@ -4274,9 +4233,6 @@ packages: '@types/html-minifier-terser@6.1.0': resolution: {integrity: sha512-oh/6byDPnL1zeNXFrDXFLyZjkr1MsBG667IM792caf1L2UPOOMf65NFzjUH/ltyfwjAGfs1rsX1eftK0jC/KIg==} - '@types/http-errors@2.0.4': - resolution: {integrity: sha512-D0CFMMtydbJAegzOyHjtiKPLlvnm3iTZyZRSZoLq2mRhDdmLfIWOCYPfQJ4cu2erKghU++QvjcUjp/5h7hESpA==} - '@types/http-errors@2.0.5': resolution: {integrity: sha512-r8Tayk8HJnX0FztbZN7oVqGccWgw98T/0neJphO91KkmOzug1KkofZURD4UaD5uH8AqcFLfdPErnBod0u71/qg==} @@ -4286,11 +4242,8 @@ packages: '@types/istanbul-lib-coverage@2.0.6': resolution: {integrity: sha512-2QF/t/auWm0lsy8XtKVPG19v3sSOQlJe/YHZgfjb/KBBHOGSV+J2q/S671rcq9uTBrLAXmZpqJiaQbMT+zNU1w==} - '@types/istanbul-lib-report@3.0.0': - resolution: {integrity: sha512-plGgXAPfVKFoYfa9NpYDAkseG+g6Jr294RqeqcqDixSbU34MZVJRi/P+7Y8GDpzkEwLaGZZOpKIEmeVZNtKsrg==} - - '@types/istanbul-reports@3.0.1': - resolution: {integrity: sha512-c3mAZEuK0lvBp8tmuL74XRKn1+y2dcwOUpH7x4WrF6gk1GIgiluDRgMYQtw2OFcBvAJWlt6ASU3tSqxp0Uu0Aw==} + '@types/istanbul-lib-report@3.0.3': + resolution: {integrity: sha512-NQn7AHQnk/RSLOxrBbGyJM/aVQ+pjj5HCgasFxc0K/KhoATfQ/47AyUl15I2yBUpihjmas+a+VJBOqecrFH+uA==} '@types/istanbul-reports@3.0.4': resolution: {integrity: sha512-pk2B1NWalF9toCRu6gjBzR69syFjP4Od8WRAX+0mmf9lAjCRicLOWc+ZrxZHx/0XRjotgkF9t6iaMJ+aXcOdZQ==} @@ -4350,14 +4303,12 @@ packages: '@types/mime@1.3.5': resolution: {integrity: sha512-/pyBZWSLD2n0dcHE3hq8s8ZvcETHtEuF+3E7XVt0Ig2nvsVQXdghHVcEkIWjy9A0wKfTn97a/PSDYohKIlnP/w==} - '@types/minimatch@5.1.2': - resolution: {integrity: sha512-K0VQKziLUWkVKiRVrx4a40iPaxTUefQmjtkQofBkYRcoaaL/8rhwDWww9qWbrgicNOgnpIsMxyNIUM4+n6dUIA==} - - '@types/minimist@1.2.2': - resolution: {integrity: sha512-jhuKLIRrhvCPLqwPcx6INqmKeiA5EWrsCOPhrlFSrbrmU4ZMPjj5Ul/oLCMDO98XRUIwVm78xICz4EPCektzeQ==} + '@types/minimatch@6.0.0': + resolution: {integrity: sha512-zmPitbQ8+6zNutpwgcQuLcsEpn/Cj54Kbn7L5pX0Os5kdWplB7xPgEh/g+SWOB/qmows2gpuCaPyduq8ZZRnxA==} + deprecated: This is a stub types definition. minimatch provides its own type definitions, so you do not need this installed. - '@types/ms@0.7.31': - resolution: {integrity: sha512-iiUgKzV9AuaEkZqkOLDIvlQiL6ltuZd9tGcW3gwpnX8JbuiuhFlEGmmFXEXkN50Cvq7Os88IY2v0dkDqXYWVgA==} + '@types/minimist@1.2.5': + resolution: {integrity: sha512-hov8bUuiLiyFPGyFPE1lwWhmzYbirOXQNNo40+y3zow8aFVTeyn3VWL0VFFfdNddA8S4Vf0Tc062rzyNr7Paag==} '@types/ms@0.7.34': resolution: {integrity: sha512-nG96G3Wp6acyAgJqGasjODb+acrI7KltPiRxzHPXnP3NgI28bpQDRv53olbqGXbfcgF5aiiHmO3xpwEpS5Ld9g==} @@ -4368,23 +4319,20 @@ packages: '@types/node-fetch@2.6.12': resolution: {integrity: sha512-8nneRWKCg3rMtF69nLQJnOYUcbafYeFSjqkw3jCRLsqkWFlHaoQrr5mXmofFGOx3DKn7UfmBMyov8ySvLRVldA==} - '@types/node-forge@1.3.11': - resolution: {integrity: sha512-FQx220y22OKNTqaByeBGqHWYz4cl94tpcxeFdvBo3wjG6XPBuZ0BNgNZRV5J5TFmmcsJ4IzsLkmGRiQbnYsBEQ==} + '@types/node-forge@1.3.13': + resolution: {integrity: sha512-zePQJSW5QkwSHKRApqWCVKeKoSOt4xvEnLENZPjyvm9Ezdf/EyDeJM7jqLzOwjVICQQzvLZ63T55MKdJB5H6ww==} '@types/node-zendesk@2.0.15': resolution: {integrity: sha512-8Kk7ceoSUiBst5+jX/121QBD8f69F5j9CqvLA1Ka+24vo+B6sPINnqPwfBJAs4/9jBpCLh7h2SH9hUbABiuZXg==} - '@types/node@18.19.115': - resolution: {integrity: sha512-kNrFiTgG4a9JAn1LMQeLOv3MvXIPokzXziohMrMsvpYgLpdEt/mMiVYc4sGKtDfyxM5gIDF4VgrPRyCw4fHOYg==} - - '@types/node@24.0.10': - resolution: {integrity: sha512-ENHwaH+JIRTDIEEbDK6QSQntAYGtbvdDXnMXnZaZ6k13Du1dPMmprkEHIL7ok2Wl2aZevetwTAb5S+7yIF+enA==} + '@types/node@18.19.118': + resolution: {integrity: sha512-hIPK0hSrrcaoAu/gJMzN3QClXE4QdCdFvaenJ0JsjIbExP1JFFVH+RHcBt25c9n8bx5dkIfqKE+uw6BmBns7ug==} '@types/nodemailer@6.4.17': resolution: {integrity: sha512-I9CCaIp6DTldEg7vyUTZi8+9Vo0hi1/T8gv3C89yk1rSAAzoKQ8H8ki/jBYJSFoH/BisgLP8tkZMlQ91CIquww==} - '@types/normalize-package-data@2.4.1': - resolution: {integrity: sha512-Gj7cI7z+98M282Tqmp2K5EIsoouUEzbBJhQQzDE3jSIRk6r9gsz0oUokqIUR4u1R3dMHo0pDHM7sNOHyhulypw==} + '@types/normalize-package-data@2.4.4': + resolution: {integrity: sha512-37i+OaWTh9qeK4LSHPsyRC7NahnGotNuZvjLSgcPzblpHB3rrCJxAOgI5gCdKm7coonsaX1Of0ILiTcnZjbfxA==} '@types/oauth@0.9.6': resolution: {integrity: sha512-H9TRCVKBNOhZZmyHLqFt9drPM9l+ShWiqqJijU1B8P3DX3ub84NjxDuy+Hjrz+fEca5Kwip3qPMKNyiLgNJtIA==} @@ -4392,8 +4340,8 @@ packages: '@types/passport-google-oauth20@2.0.16': resolution: {integrity: sha512-ayXK2CJ7uVieqhYOc6k/pIr5pcQxOLB6kBev+QUGS7oEZeTgIs1odDobXRqgfBPvXzl0wXCQHftV5220czZCPA==} - '@types/passport-oauth2@1.4.17': - resolution: {integrity: sha512-ODiAHvso6JcWJ6ZkHHroVp05EHGhqQN533PtFNBkg8Fy5mERDqsr030AX81M0D69ZcaMvhF92SRckEk2B0HYYg==} + '@types/passport-oauth2@1.8.0': + resolution: {integrity: sha512-6//z+4orIOy/g3zx17HyQ71GSRK4bs7Sb+zFasRoc2xzlv7ZCJ+vkDBYFci8U6HY+or6Zy7ajf4mz4rK7nsWJQ==} '@types/passport-strategy@0.2.38': resolution: {integrity: sha512-GC6eMqqojOooq993Tmnmp7AUTbbQSgilyvpCYQjT+H6JfG/g6RGc7nXEniZlp0zyKJ0WUdOiZWLBZft9Yug1uA==} @@ -4416,9 +4364,6 @@ packages: '@types/qs@6.14.0': resolution: {integrity: sha512-eOunJqu0K1923aExK6y8p6fsihYEn/BYuQ4g0CxAAgFc4b/ZLN4CrsRZ55srTdqoiLzU2B2evC+apEIxprEzkQ==} - '@types/qs@6.9.17': - resolution: {integrity: sha512-rX4/bPcfmvxHDv0XjfJELTTr+iB+tn032nPILqHm5wbthUUUuVtNGGqzhya9XUxjTP8Fpr0qYgSZZKxGY++svQ==} - '@types/range-parser@1.2.7': resolution: {integrity: sha512-hKormJbkJqzQGhziax5PItDUTMAM9uE2XXQmM37dyd4hVM+5aVl7oVxMVUiVQn2oCQFN/LKCZdvSM0pFRqbSmQ==} @@ -4454,23 +4399,17 @@ packages: '@types/semver@7.7.0': resolution: {integrity: sha512-k107IF4+Xr7UHjwDc7Cfd6PRQfbdkiRabXGRjo07b4WyPahFBZCZ1sE+BNxYIJPPg73UkfOsVOLwqVc/6ETrIA==} - '@types/send@0.17.4': - resolution: {integrity: sha512-x2EM6TJOybec7c52BX0ZspPodMsQUd5L6PRwOunVyVUhXiBSKf3AezDL8Dgvgt5o0UfKNfuA0eMLr2wLT4AiBA==} - '@types/send@0.17.5': resolution: {integrity: sha512-z6F2D3cOStZvuk2SaP6YrwkNO65iTZcwA2ZkSABegdkAh/lf+Aa/YQndZVfmEXT5vgAp6zv06VQ3ejSVjAny4w==} '@types/serve-index@1.9.4': resolution: {integrity: sha512-qLpGZ/c2fhSs5gnYsQxtDEq3Oy8SXPClIXkW5ghvAvsNuVSA8k+gCONcUCS/UjLEYvYps+e8uBtfgXgvhwfNug==} - '@types/serve-static@1.15.7': - resolution: {integrity: sha512-W8Ym+h8nhuRwaKPaDw34QUkwsGi6Rc4yYqvKFo5rm2FUEhCFbzVWrxXUxuKK8TASjWsysJY0nsmNCGhCOIsrOw==} - '@types/serve-static@1.15.8': resolution: {integrity: sha512-roei0UY3LhpOJvjbIP6ZZFngyLKl5dskOtDhxY5THRSpO+ZI+nzJ+m5yUMzGrp89YRa7lvknKkMYjqQFGwA7Sg==} - '@types/sizzle@2.3.3': - resolution: {integrity: sha512-JYM8x9EGF163bEyhdJBpR2QX1R5naCJHC8ucJylJ3w9/CVBaskdQ8WqBf8MmQrd1kRvp/a4TS8HJ+bxzR7ZJYQ==} + '@types/sizzle@2.3.9': + resolution: {integrity: sha512-xzLEyKB50yqCUPUJkIsrVvoWNfFUbIZI+RspLWt8u+tIW/BetMBZtgV2LY/2o+tYH8dRvQ+eoPf3NdhQCcLE2w==} '@types/sockjs@0.3.36': resolution: {integrity: sha512-MK9V6NzAS1+Ud7JV9lJLFqW85VbC9dq3LmwZCuBe4wBDgKC0Kj/jd8Xl+nSviU+Qc3+m7umHHyHg//2KSa0a0Q==} @@ -4517,8 +4456,8 @@ packages: '@types/xml2js@0.4.14': resolution: {integrity: sha512-4YnrRemBShWRO2QjvUin8ESA41rH+9nQGLUGZV/1IDhi3SL9OhdpNC/MrulTWuptXKwhx/aDxE7toV0f/ypIXQ==} - '@types/yargs-parser@21.0.0': - resolution: {integrity: sha512-iO9ZQHkZxHn4mSakYV0vFHAVDyEOIJQrV2uZ06HxEPcx+mt8swXoZHIbaaJ2crJYFfErySgktuTZ3BeLz+XmFA==} + '@types/yargs-parser@21.0.3': + resolution: {integrity: sha512-I4q9QU9MQv4oEOz4tAHJtNz1cwuLxn2F3xcc2iV5WdqLPpUnj30aUuxt1mAxYTG+oe8CZMV/+6rU4S4gRDzqtQ==} '@types/yargs@15.0.19': resolution: {integrity: sha512-2XUaGVmyQjgyAZldf0D0c14vvo/yv0MhQBSTJcejMMaitsn3nxCB6TmH4G0ZQf+uxROOa9mpanoSm8h6SG/1ZA==} @@ -4717,6 +4656,12 @@ packages: access-control@1.0.1: resolution: {integrity: sha512-H5aqjkogmFxfaOrfn/e42vyspHVXuJ8er63KuljJXpOyJ1ZO/U5CrHfO8BLKIy2w7mBM02L5quL0vbfQqrGQbA==} + acorn-import-phases@1.0.3: + resolution: {integrity: sha512-jtKLnfoOzm28PazuQ4dVBcE9Jeo6ha1GAJvq3N0LlNOszmTfx+wSycBehn+FN0RnyeR77IBxN/qVYMw0Rlj0Xw==} + engines: {node: '>=10.13.0'} + peerDependencies: + acorn: ^8.14.0 + acorn-jsx@5.3.2: resolution: {integrity: sha512-rq9s+JNhf0IChjtDXxllJ7g41oZk5SlXtp0LHwyA5cejwn7vKmKp4pPri6YEePv2PU65sAsegbXtIinmDFDXgQ==} peerDependencies: @@ -4731,11 +4676,6 @@ packages: engines: {node: '>=0.4.0'} hasBin: true - acorn@8.14.1: - resolution: {integrity: sha512-OvQ/2pUDKmgfCg++xsTX1wGxfTaszcHVcTctW4UJB4hibJx2HXxxO5UmVgyjMa+ZDsiaf5wWLXYpRWMmBI0QHg==} - engines: {node: '>=0.4.0'} - hasBin: true - acorn@8.15.0: resolution: {integrity: sha512-NZyJarBfL7nWwIq+FDL6Zp/yHEhePMNnnJ0y3qfieCrmNvYct8uvtiV41UvlSe6apAfk0fY1FbWx+NwfmpvtTg==} engines: {node: '>=0.4.0'} @@ -4753,8 +4693,8 @@ packages: resolution: {integrity: sha512-RZNwNclF7+MS/8bDg70amg32dyeZGZxiDuQmZxKLAlQjr3jGyLx+4Kkk58UO7D2QdgFIQCovuSuZESne6RG6XQ==} engines: {node: '>= 6.0.0'} - agent-base@7.1.3: - resolution: {integrity: sha512-jRR5wdylq8CkOe6hei19GGZnxM6rBGwFl3Bg0YItGDimvjGtAvdZk4Pu6Cl4u4Igsws4a1fd1Vq3ezrhn4KmFw==} + agent-base@7.1.4: + resolution: {integrity: sha512-MnA+YT8fwfJPgBx3m60MNqakm30XOkyIoH1y6huTQvC0PwZG7ki8NacLBcrPbNoo8vEZy7Jpuk7+jMO+CUovTQ==} engines: {node: '>= 14'} agentkeepalive@4.6.0: @@ -4808,10 +4748,6 @@ packages: resolution: {integrity: sha512-7HSX4QQb4CspciLpVFwyRe79O3xsIZDDLER21kERQ71oaPodF8jL725AgJMFAYbooIqolJoRLuM81SpeUkpkvA==} engines: {node: '>=12'} - ansi-styles@3.2.1: - resolution: {integrity: sha512-VT0ZI6kZRdTh8YyJw3SMbYm/u+NqfsAxEpWO0Pf9sq8/e94WxxOpPKx9FR1FlyCtOVDNOQ+8ntlqFxiRc+r5qA==} - engines: {node: '>=4'} - ansi-styles@4.3.0: resolution: {integrity: sha512-zbB9rCJAT1rbjiVDb2hqKFHNYLxgtk8NURxZ3IZwD3F6NtxbXZQCnnSi1Lkx+IDohdPlFp222wVALIheZJQSEg==} engines: {node: '>=8'} @@ -5018,8 +4954,8 @@ packages: resolution: {integrity: sha512-ESAc/RJvGTFEzRwOTT4+lNDk/GNHMkKbNzsvT0qKRfDyyYTskxB5rnU2njIDYVxXCBHHEI1c0YwHob3WaYujOg==} engines: {node: ^14.15.0 || ^16.10.0 || >=18.0.0} - babel-preset-current-node-syntax@1.0.1: - resolution: {integrity: sha512-M7LQ0bxarkxQoN+vz5aJPsLBn77n8QgTFmo8WK0/44auK2xlCXrYcUxHFxgU7qW5Yzw/CjmLRK2uJzaCd7LvqQ==} + babel-preset-current-node-syntax@1.1.0: + resolution: {integrity: sha512-ldYss8SbBlWva1bs28q78Ju5Zq1F+8BrqBZZ0VFhLBvhh6lCpC2o3gDJi/5DRLs9FgYZCnmPYIVFU4lRXCkyUw==} peerDependencies: '@babel/core': ^7.0.0 @@ -5096,11 +5032,11 @@ packages: big.js@6.2.2: resolution: {integrity: sha512-y/ie+Faknx7sZA5MfGA2xKlu0GDv8RWrXGsmlteyJQ2lvoKv9GBK/fpRMc2qlSoBAgNxrixICFCBefIq8WCQpQ==} - bignumber.js@9.2.1: - resolution: {integrity: sha512-+NzaKgOUvInq9TIUZ1+DRspzf/HApkCwD4btfuasFTdrfnOxqx853TgDpMolp+uv4RpRp7bPcEU2zKr9+fRmyw==} + bignumber.js@9.3.1: + resolution: {integrity: sha512-Ko0uX15oIUS7wJ3Rb30Fs6SkVbLmPBAKdlm7q9+ak9bbIeFf0MwuBsQV6z7+X768/cHsfg+WlysDWJcmthjsjQ==} - binary-extensions@2.2.0: - resolution: {integrity: sha512-jDctJ/IVQbZoJykoeHbhXpOlNBqGNcwXJKJog42E5HDPUwQTSdjCHdihjj0DlnheQ7blbT6dHOafNAiS8ooQKA==} + binary-extensions@2.3.0: + resolution: {integrity: sha512-Ceh+7ox5qe7LJuLHoY0feh3pHuUDHAcRUeyL2VYghZwfpkNIy/+8Ocg0a3UuSoYzavmylwuLWQOf3hl0jjMMIw==} engines: {node: '>=8'} binary-search-bounds@2.0.5: @@ -5207,10 +5143,6 @@ packages: resolution: {integrity: sha512-Sp1ablJ0ivDkSzjcaJdxEunN5/XvksFJ2sMBFfq6x0ryhQV/2b/KwFe21cMpmHtPOSij8K99/wSfoEuTObmuMQ==} engines: {node: '>= 0.4'} - call-bind@1.0.7: - resolution: {integrity: sha512-GHTSNSYICQ7scH7sZ+M2rFopRoLh8t2bLSW6BbgrtLsahOIB5iyAVJf9GjWK3cYTDaMj4XdBpM1cA6pIS0Kv2w==} - engines: {node: '>= 0.4'} - call-bind@1.0.8: resolution: {integrity: sha512-oKlSFMcMwpUg2ednkhQ454wfWiU/ul3CkJe/PEHcTKuiX6RpbehUiFMXu13HalGZxfUwCQzZG747YXBn1im9ww==} engines: {node: '>= 0.4'} @@ -5238,11 +5170,8 @@ packages: resolution: {integrity: sha512-Gmy6FhYlCY7uOElZUSbxo2UCDH8owEk996gkbrpsgGtrJLM3J7jGxl9Ic7Qwwj4ivOE5AWZWRMecDdF7hqGjFA==} engines: {node: '>=10'} - caniuse-lite@1.0.30001723: - resolution: {integrity: sha512-1R/elMjtehrFejxwmexeXAtae5UO9iSyFn6G/I806CYC/BLyyBk1EPhrKBkWhy6wM6Xnm47dSJQec+tLJ39WHw==} - - caniuse-lite@1.0.30001726: - resolution: {integrity: sha512-VQAUIUzBiZ/UnlM28fSp2CRF3ivUn1BWEvxMcVTNwpw91Py1pGbPIyIKtd+tzct9C3ouceCVdGAXxZOpZAsgdw==} + caniuse-lite@1.0.30001727: + resolution: {integrity: sha512-pB68nIHmbN6L/4C6MH1DokyR3bYqFwjaSs/sWDHGj4CTcFtQUQMuJftVwWkXq7mNWOybD3KhUv3oWHoGxgP14Q==} canvas-fit@1.5.0: resolution: {integrity: sha512-onIcjRpz69/Hx5bB5HGbYKUF2uC6QT6Gp+pfpGm3A7mPfcluSLV5v4Zu+oflDUwLdUw0rLIBhUbi0v8hM4FJQQ==} @@ -5263,10 +5192,6 @@ packages: resolution: {integrity: sha512-T2VJbcDuZQ0Tb2EWwSotMPJjgpy1/tGee1BTpUNsGZ/qgNjV2t7Mvu+d4600U564nbLesN1x2dPL+xii174Ekg==} engines: {node: '>=14.16'} - chalk@2.4.2: - resolution: {integrity: sha512-Mti+f9lpJNcwF4tWV8/OrTTtF1gZi+f8FqlyAdouralcFWFQWF2+NgCHShjkCb+IFBLq9buZwE1xckQU4peSuQ==} - engines: {node: '>=4'} - chalk@3.0.0: resolution: {integrity: sha512-4D3B6Wf41KOYRFdszmDqMCGq5VV/uMAB273JILmO+3jAlh8X4qDtdtgCR3fxtbLEMzSx22QdhnDcJvu2u1fVwg==} engines: {node: '>=8'} @@ -5347,12 +5272,12 @@ packages: resolution: {integrity: sha512-NIxF55hv4nSqQswkAeiOi1r83xy8JldOFDTWiug55KBu9Jnblncd2U6ViHmYgHf01TPZS77NJBhBMKdWj9HQMQ==} engines: {node: '>=8'} - ci-info@4.2.0: - resolution: {integrity: sha512-cYY9mypksY8NRqgDB1XD1RiJL338v/551niynFTGkZOO2LHuB2OmOYxDIe/ttN9AHwrqdum1360G3ald0W9kCg==} + ci-info@4.3.0: + resolution: {integrity: sha512-l+2bNRMiQgcfILUi33labAZYIWlH1kWDp+ecNo5iisRKrbm0xcRyCww71/YU0Fkw0mAFpz9bJayXPjey6vkmaQ==} engines: {node: '>=8'} - cjs-module-lexer@1.2.3: - resolution: {integrity: sha512-0TNiGstbQmCFwt4akjjBg5pLRTSyj/PkWQ1ZoO2zntmg9yLqSRxwEa4iCfQLGjqhiqBfOJa7W/E8wfGrTDmlZQ==} + cjs-module-lexer@1.4.3: + resolution: {integrity: sha512-9z8TZaGM1pfswYeXrUpzPrkx8UnWYdhJclsiYMm6x/w5+nN+8Tf/LnAgfLGQCm59qAOxU8WwHEq2vNwF6i4j+Q==} cjsx-loader@3.0.0: resolution: {integrity: sha512-bek+Ojam3A0QmqrT8GXvRUdo3ynHyPAjBHNZz2A2lSDccMU9GGZ1eNsTc7+cQ/n2H+UhDL8XW/H1wvG8cNLdZA==} @@ -5691,10 +5616,6 @@ packages: cross-fetch@4.1.0: resolution: {integrity: sha512-uKm5PU+MHTootlWEY+mZ4vvXoCn4fLQxT9dSc1sXVMSFkINTJVN8cAQROpwcKm8bJ/c7rgZVIBWzH5T78sNZZw==} - cross-spawn@7.0.3: - resolution: {integrity: sha512-iRDPJKUPVEND7dHPO8rkbOnPpyDygcDFtWjpeWNCgy8WP2rXcxXL8TskReQl6OrB2G7+UJrags1q15Fudc7G6w==} - engines: {node: '>= 8'} - cross-spawn@7.0.6: resolution: {integrity: sha512-uV2QOWP2nWzsy2aMp8aRibhi9dlzF5Hgh5SHaB9OiTGEyDTiJJyx0uy51QXdyWbtAHNua4XJzUKca3OzKUd3vA==} engines: {node: '>= 8'} @@ -5778,14 +5699,14 @@ packages: css-select@4.3.0: resolution: {integrity: sha512-wPpOYtnsVontu2mODhA19JrqWxNsfdatRKd64kmpRbQgh1KtItko5sTnEpPdpSaJszTOhEMlF/RPz28qj4HqhQ==} - css-select@5.1.0: - resolution: {integrity: sha512-nwoRF1rvRRnnCqqY7updORDsuqKzqYJ28+oSMaJMMgOauh3fvwHqMS7EZpIPqK8GL+g9mKxF1vP/ZjSeNjEVHg==} + css-select@5.2.2: + resolution: {integrity: sha512-TizTzUddG/xYLA3NXodFM0fSbNizXjOKhqiQQwvhlspadZokn1KDy0NZFS0wuEubIYAV5/c1/lAr0TaaFXEXzw==} css-system-font-keywords@1.0.0: resolution: {integrity: sha512-1umTtVd/fXS25ftfjB71eASCrYhilmEsvDEI6wG/QplnmlfmVM5HkZ/ZX46DT5K3eblFPgLUHt5BRCb0YXkSFA==} - css-what@6.1.0: - resolution: {integrity: sha512-HTUrgRJ7r4dsZKU6GjmpfRK1O76h97Z8MfS1G0FozR+oF2kG6Vfe8JE6zwrkbxigziPHinCJ+gCPjA9EaBDtRw==} + css-what@6.2.2: + resolution: {integrity: sha512-u/O3vwbptzhMs3L1fQE82ZSLHQQfto5gyZzwteVIEyeaY5Fc7R4dapF/BvRoSYFeqfBk4m0V1Vafq5Pjv25wvA==} engines: {node: '>= 6'} css.escape@1.5.1: @@ -5812,8 +5733,8 @@ packages: csv-parse@5.6.0: resolution: {integrity: sha512-l3nz3euub2QMg5ouu5U09Ew9Wf6/wQ8I++ch1loQ0ljmzhmfZYrH9fflS22i/PQEvsPvxCwxgz5q7UB8K1JO4Q==} - csv-stringify@6.5.2: - resolution: {integrity: sha512-RFPahj0sXcmUyjrObAK+DOWtMvMIFV328n4qZJhgX3x2RqkQgOTU2mCUmiFR0CzM6AzChlRSUErjiJeEt8BaQA==} + csv-stringify@6.6.0: + resolution: {integrity: sha512-YW32lKOmIBgbxtu3g5SaiqWNwa/9ISQt2EcgOq0+RAIFufFp9is6tqNnKahqE5kuKvrnYAzs28r+s6pXJR8Vcw==} cytoscape-cose-bilkent@4.1.0: resolution: {integrity: sha512-wgQlVIUJF13Quxiv5e1gstZ08rnZj2XaLHGoFMYXz7SkNfCDOOteKBE6SYRfA9WxxI/iBc3ajfDoc6hb/MRAHQ==} @@ -5825,8 +5746,8 @@ packages: peerDependencies: cytoscape: ^3.2.0 - cytoscape@3.32.0: - resolution: {integrity: sha512-5JHBC9n75kz5851jeklCPmZWcg3hUe6sjqJvyk3+hVqFaKcHwHgxsjeN1yLmggoUc6STbtm9/NQyabQehfjvWQ==} + cytoscape@3.32.1: + resolution: {integrity: sha512-dbeqFTLYEwlFg7UGtcZhCCG/2WayX72zK3Sq323CEX29CY81tYfVhw1MIdduCtpstB0cTOhJswWlM/OEB3Xp+Q==} engines: {node: '>=0.10'} d3-array@1.2.4: @@ -6086,8 +6007,8 @@ packages: supports-color: optional: true - decamelize-keys@1.1.0: - resolution: {integrity: sha512-ocLWuYzRPoS9bfiSdDd3cxvrzovVMZnRDVEzAs+hWIVXGDbHxWMECij2OBuyB/An0FFW/nLuq6Kv1i/YC5Qfzg==} + decamelize-keys@1.1.1: + resolution: {integrity: sha512-WiPxgEirIV0/eIOMcnFBA3/IJZAZqKnwAwWyvvdi4lsr1WCN22nhdf/3db3DoZcUjTV2SqfzIwNyp6y2xs3nmg==} engines: {node: '>=0.10.0'} decamelize@1.2.0: @@ -6097,18 +6018,18 @@ packages: decimal.js-light@2.5.1: resolution: {integrity: sha512-qIMFpTMZmny+MMIitAB6D7iVPEorVw6YQRWkvarTkT4tBeSLLiHzcwj6q0MmYSFCiVpiqPJTJEYIrpcPzVEIvg==} - decimal.js@10.5.0: - resolution: {integrity: sha512-8vDa8Qxvr/+d94hSh5P3IJwI5t8/c0KsMp+g8bNw9cY2icONa5aPfvKeieW1WlG0WQYwwhJ7mjui2xtiePQSXw==} + decimal.js@10.6.0: + resolution: {integrity: sha512-YpgQiITW3JXGntzdUmyUR1V812Hn8T1YVXhCu+wO3OpS4eU9l4YdD3qjyiKdV6mvV29zapkMeD390UVEf2lkUg==} - decode-named-character-reference@1.1.0: - resolution: {integrity: sha512-Wy+JTSbFThEOXQIR2L6mxJvEs+veIzpmqD7ynWxMXGpnk3smkHQOp6forLdHsKpAMW9iJpaBBIxz285t1n1C3w==} + decode-named-character-reference@1.2.0: + resolution: {integrity: sha512-c6fcElNV6ShtZXmsgNgFFV5tVX2PaV4g+MOAkb8eXHvn6sryJBrZa9r0zV6+dtTyoCKxtDy5tyQ5ZwQuidtd+Q==} decompress-response@6.0.0: resolution: {integrity: sha512-aW35yZM6Bb/4oJlZncMH2LCoZtJXTRxES17vE3hoRiowU2kWHaJKFkSBDnDR+cm9J+9QhXmREyIfv0pji9ejCQ==} engines: {node: '>=10'} - dedent@1.5.3: - resolution: {integrity: sha512-NHQtfOOW68WD8lgypbLA5oT+Bt0xXJhiYvoR6SmmNXZfpzOGXwdKWmcwG8N7PwVVWV3eF/68nmD9BaJSsTBhyQ==} + dedent@1.6.0: + resolution: {integrity: sha512-F1Z+5UCFpmQUzJa11agbyPVMbpgT/qA3/SKyJ1jyBgm7dUcUEa8v9JwDkerSQXfakBwFljIxhOJqGkjUwZ9FSA==} peerDependencies: babel-plugin-macros: ^3.1.0 peerDependenciesMeta: @@ -6191,10 +6112,6 @@ packages: engines: {node: '>=0.10'} hasBin: true - detect-libc@2.0.3: - resolution: {integrity: sha512-bwy0MGW55bG41VqxxypOsdSdGqLwXPI/focwgTYCFMbdUiBAxLg9CFzG08sz2aqzknwiX7Hkl0bQENjg8iLByw==} - engines: {node: '>=8'} - detect-libc@2.0.4: resolution: {integrity: sha512-3UDv+G9CsCKO1WKMGw9fwq/SWJYbI0c5Y7LU1AXYoDdbhE2AHQ6N6Nb34sG8Fj7T5APy8qXDCKuuIHd1BR0tVA==} engines: {node: '>=8'} @@ -6350,8 +6267,8 @@ packages: ecdsa-sig-formatter@1.0.11: resolution: {integrity: sha512-nagl3RYrbNv6kQkeJIpt6NJZy8twLB/2vtz6yN9Z4vRKHN4/QZJIEbqohALSgwKdnksuY3k5Addp5lg8sVoVcQ==} - edit-json-file@1.8.0: - resolution: {integrity: sha512-IBOpbe2aQufNl5oZ4jsr2AmNVUy5bO7jS5hk0cCyWhOLdH59Xv41B3XQObE/JB89Ae5qDY9hVsq13/hgGhFBZg==} + edit-json-file@1.8.1: + resolution: {integrity: sha512-x8L381+GwqxQejPipwrUZIyAg5gDQ9tLVwiETOspgXiaQztLsrOm7luBW5+Pe31aNezuzDY79YyzF+7viCRPXA==} ee-first@1.1.1: resolution: {integrity: sha512-WMwm9LhRUo+WUaRN+vRuETqG89IgZphVSNkdFgeb6sS/E4OrDIN7t48CAewSHXc6C8lefD8KKfr5vY61brQlow==} @@ -6361,8 +6278,8 @@ packages: engines: {node: '>=0.10.0'} hasBin: true - electron-to-chromium@1.5.175: - resolution: {integrity: sha512-Nqpef9mOVo7pZfl9NIUhj7tgtRTsMzCzRTJDP1ccim4Wb4YHOz3Le87uxeZq68OCNwau2iQ/X7UwdAZ3ReOkmg==} + electron-to-chromium@1.5.182: + resolution: {integrity: sha512-Lv65Btwv9W4J9pyODI6EWpdnhfvrve/us5h1WspW8B2Fb0366REPtY3hX7ounk1CkV/TBjWCEvCBBbYbmV0qCA==} element-size@1.1.1: resolution: {integrity: sha512-eaN+GMOq/Q+BIWy0ybsgpcYImjGIdNLyjLFJU4XsLHXYQao5jCNb36GyN6C2qwmDDYSfIBmKpPpr4VnBdLCsPQ==} @@ -6408,8 +6325,8 @@ packages: encoding@0.1.13: resolution: {integrity: sha512-ETBauow1T35Y/WZMkio9jiM0Z5xjHHmJ4XmjZOq1l/dXz3lr2sRn87nJy20RupqSh1F2m3HHPSp8ShIPQJrJ3A==} - end-of-stream@1.4.4: - resolution: {integrity: sha512-+uw1inIHVPQoaVuHzRyXd21icM+cnt4CzD5rW+NC1wjOUSTOs+Te7FOv7AhN7vS9x/oIyhLP5PR1H+phQAHu5Q==} + end-of-stream@1.4.5: + resolution: {integrity: sha512-ooEGc6HP26xXq/N+GCGOT0JKCLDGrq2bQUZrQ7gyrJiZANJ/8YDTxTpQBXGMn+WbIQXNVpyWymm7KYVICQnyOg==} engine.io-client@6.6.3: resolution: {integrity: sha512-T0iLjnyNWahNyv/lcjS2y4oE358tVS/SYQNxYXGAJ9/GLgH4VCvOQ/mhTjqU88mLZCQgiG8RIegFHYCdVC+j5w==} @@ -6458,10 +6375,6 @@ packages: error-stack-parser@2.1.4: resolution: {integrity: sha512-Sk5V6wVazPhq5MhpO+AUxJn5x7XSXGl1R93Vn7i+zS15KDVxQijejNCrz8340/2bgLBjR9GtEG8ZVKONDjcqGQ==} - es-abstract@1.23.9: - resolution: {integrity: sha512-py07lI0wjxAC/DcfK1S6G7iANonniZwTISvdPzk9hzeH0IZIshbuuFxLIU96OyF89Yb9hiqWn8M/bY83KY5vzA==} - engines: {node: '>= 0.4'} - es-abstract@1.24.0: resolution: {integrity: sha512-WSzPgsdLtTcQwm4CROfS5ju2Wa1QQcVeT37jFjYzdFz1r9ahadC8B8/a4qxJxM+09F18iumCdRmlr96ZYkQvEg==} engines: {node: '>= 0.4'} @@ -6469,10 +6382,6 @@ packages: es-class@2.1.1: resolution: {integrity: sha512-loFNtCIGY81XvaHMzsxPocOgwZW71p+d/iES+zDSWeK9D4JaxrR/AoO0sZnWbV39D/ESppKbHrApxMi+Vbl8rg==} - es-define-property@1.0.0: - resolution: {integrity: sha512-jxayLKShrEqqzJ0eumQbVhTYQM27CfT1T35+gCgDFoL82JLsXqTJ76zv6A0YLOgEnLUMvLzsDsGIrl8NFpT2gQ==} - engines: {node: '>= 0.4'} - es-define-property@1.0.1: resolution: {integrity: sha512-e3nRfgfUZ4rNGL232gUgX06QNyyez04KdjFrF+LTRoOXmrOgFKDg4BCdsjW8EnT69eqdYGmRpJwiPVYNrCaW3g==} engines: {node: '>= 0.4'} @@ -6731,8 +6640,8 @@ packages: resolution: {integrity: sha512-V7/RktU11J3I36Nwq2JnZEM7tNm17eBJz+u25qdxBZeCKiX6BkVSZQjwWIr+IobgnZy+ag73tTZgZi7tr0LrBw==} engines: {node: '>=6.0.0'} - fast-glob@3.3.2: - resolution: {integrity: sha512-oX2ruAFQwf/Orj8m737Y5adxDQO0LAB7/S5MnxCdTNDd4p6BsyIVsv9JQsATbTSq8KHRpLwIHbVlUNatxd+1Ow==} + fast-glob@3.3.3: + resolution: {integrity: sha512-7MptL8U0cqcFdzIzwOTHoilX9x5BrNqye7Z/LuC7kCMRio1EMSyqRK3BEAUD7sXRq4iT4AzTVuZdhgQ2TCvYLg==} engines: {node: '>=8.6.0'} fast-isnumeric@1.1.4: @@ -6747,8 +6656,8 @@ packages: fast-safe-stringify@2.1.1: resolution: {integrity: sha512-W+KJc2dmILlPplD/H4K9l9LcAHAfPtP6BY84uVLXQ6Evcz9Lcg33Y2z1IVblT6xdY54PXYVHEv+0Wpq8Io6zkA==} - fast-uri@3.0.2: - resolution: {integrity: sha512-GR6f0hD7XXyNJa25Tb9BuIdN0tdr+0BMi6/CJPH3wJO1JjNG3n/VsSw38AwRdKZABm8lGbPfakLRkYzx2V9row==} + fast-uri@3.0.6: + resolution: {integrity: sha512-Atfo14OibSv5wAp4VWNsFYE1AchQRTv9cBGWET4pZWHzYshFSS9NQI6I57rdKn9croWVMbYFbLhJ+yJvmZIIHw==} fast-xml-parser@4.5.3: resolution: {integrity: sha512-RKihhV+SHsIUGXObeVy9AXiBbFwkVk7Syp8XgwN5U3JV416+Gwp/GO9i0JYKmikykgz/UHRrrV4ROuZEo/T0ig==} @@ -6761,8 +6670,8 @@ packages: fastparse@1.1.2: resolution: {integrity: sha512-483XLLxTVIwWK3QTrMGRqUfUpoOs/0hbQrl2oz4J0pAcm3A3bu84wxTFqGqkJzewCLdME38xJLJAxBABfQT8sQ==} - fastq@1.13.0: - resolution: {integrity: sha512-YpkpUnK8od0o1hmeSc7UUs/eB/vIPWJYjKck2QKIzAf71Vm1AAQ3EbuZB3g2JIy+pg+ERD0vqI79KyZiB2e2Nw==} + fastq@1.19.1: + resolution: {integrity: sha512-GwLTyxkCXjXbxqIhTsMI2Nui8huMPtnxg7krajPJAjnEG/iiOS7i+zCtWGZR9G0NBKbXKh6X9m9UIsYX/N6vvQ==} faye-websocket@0.11.4: resolution: {integrity: sha512-CzbClwlXAuiRQAlUyfqPgvPoNKTckTPGfwZV4ZdAhVcP2lh9KUxJg2b5GkE7XbjKQ3YJnQ9z6D9ntLAlB+tP8g==} @@ -6771,8 +6680,8 @@ packages: fb-watchman@2.0.2: resolution: {integrity: sha512-p5161BqbuCaSnB8jIbzQHOlpgsPmK5rJVDfDKO91Axs5NC1uu3HRQm6wt9cd9/+GtQQIO53JdGXXoyDpTAsgYA==} - fdir@6.4.4: - resolution: {integrity: sha512-1NZP+GK4GfuAv3PqKvxQRDMjdSRZjnkq7KfhlNrCNNlZ0ygQFpebfrnfnq/W7fpUnAv9aGWmY1zKx7FYL3gwhg==} + fdir@6.4.6: + resolution: {integrity: sha512-hiFoqpyZcfNm1yc4u8oWCf9A2c4D3QjCrks3zmoVKVxpQRzmPNar1hUJcBG2RQHvEVGDN+Jm81ZheVLAQMK6+w==} peerDependencies: picomatch: ^3 || ^4 peerDependenciesMeta: @@ -6820,8 +6729,8 @@ packages: resolution: {integrity: sha512-78/PXT1wlLLDgTzDs7sjq9hzz0vXD+zn+7wypEe4fXQxCmdmqfGsEPQxmiCSQI3ajFV91bVSsvNtrJRiW6nGng==} engines: {node: '>=10'} - find-value@1.0.12: - resolution: {integrity: sha512-OCpo8LTk8eZ2sdDCwbU2Lc3ivYsdM6yod6jP2jHcNEFcjPhkgH0+POzTIol7xx1LZgtbI5rkO5jqxsG5MWtPjQ==} + find-value@1.0.13: + resolution: {integrity: sha512-epNL4mnl3HUYrwVQtZ8s0nxkE4ogAoSqO1V1fa670Ww1fXp8Yr74zNS9Aib/vLNf0rq0AF/4mboo7ev5XkikXQ==} flat-cache@3.2.0: resolution: {integrity: sha512-CYcENa+FtcUKLmhhqyctpclsq7QF38pKjZHsGNiSQF5r4FtoKDWabFDl3hzaEQMvT1LHEysw5twgLvpYYb4vbw==} @@ -6859,10 +6768,6 @@ packages: resolution: {integrity: sha512-dKx12eRCVIzqCxFGplyFKJMPvLEWgmNtUrpTiJIR5u97zEhRG8ySrtboPHZXx7daLxQVrl643cTzbab2tkQjxg==} engines: {node: '>= 0.4'} - foreground-child@3.3.0: - resolution: {integrity: sha512-Ld2g8rrAyMYFXBhEqMz8ZAHBi4J4uS1i/CxGMDnjyFWddMXLVcDp051DZfu+t7+ab7Wv6SMqpWmyFIj5UbfFvg==} - engines: {node: '>=14'} - foreground-child@3.3.1: resolution: {integrity: sha512-gIXjKqtFuWEgzFRJA9WCQeSJLZDjgJUOMCMzxtvFq/37KojM1BFGufqsCy0r4qSQmYLsZYMeyRqzIWOMup03sw==} engines: {node: '>=14'} @@ -6874,17 +6779,14 @@ packages: resolution: {integrity: sha512-XHIrMD0NpDrNM/Ckf7XJiBbLl57KEhT3+i3yY+eWm+cqYZJQTZrKo8Y8AWKnuV5GT4scfuUGt9LzNoIx3dU1nQ==} engines: {node: '>= 0.12'} - form-data@4.0.2: - resolution: {integrity: sha512-hGfm/slu0ZabnNt4oaRZ6uREyfCj6P4fT/n6A1rGV+Z0VdGXjfOhVUpkn6qVQONHGIFwmveGXyDs75+nr6FM8w==} + form-data@4.0.3: + resolution: {integrity: sha512-qsITQPfmvMOSAdeyZ+12I1c+CKSstAFAwu+97zrnWAbIr5u8wfsExUzCesVLC8NgHuRUqNN4Zy6UPWUTRGslcA==} engines: {node: '>= 6'} formdata-node@4.4.1: resolution: {integrity: sha512-0iirZp3uVDjVGt9p49aTaqjk84TrglENEDuqfdlZQ1roC9CWlPk6Avf8EEnZNcAqPonwkG35x4n3ww/1THYAeQ==} engines: {node: '>= 12.20'} - formidable@3.5.2: - resolution: {integrity: sha512-Jqc1btCy3QzRbJaICGwKcBfGWuLADRerLzDqi2NwSt/UkXLsHJw2TVResiaoBufHVHy9aSgClOHCeJsSsFLTbg==} - formidable@3.5.4: resolution: {integrity: sha512-YikH+7CUTOtP44ZTnUhR7Ic2UASBPOqmaRkRKxRbywPTe5VxF7RRCck4af9wutiZ/QKM5nME9Bie2fFaPz5Gug==} engines: {node: '>=14.0.0'} @@ -6913,11 +6815,6 @@ packages: fs.realpath@1.0.0: resolution: {integrity: sha512-OO0pH2lK6a0hZnAdau5ItzHPI6pUlvI7jMVnxUQRtw4owF2wk8lOSabtGDCTP4Ggrg2MbGnWO9X8K1t4+fGMDw==} - fsevents@2.3.2: - resolution: {integrity: sha512-xiqMQR4xAeHTuB9uWm+fFRcIOgKBMiOBP+eXiyT7jsgVCq1bkVygt00oASowB7EdtpOHaaPgKt812P9ab+DDKA==} - engines: {node: ^8.16.0 || ^10.6.0 || >=11.0.0} - os: [darwin] - fsevents@2.3.3: resolution: {integrity: sha512-5xoDfX+fL7faATnagmWPpbFtwh/R77WmMMqqHGS65C3vvB0YHrgF+B1YmZ3441tMj5n63k0212XNoJwzlhffQw==} engines: {node: ^8.16.0 || ^10.6.0 || >=11.0.0} @@ -6965,10 +6862,6 @@ packages: get-canvas-context@1.0.2: resolution: {integrity: sha512-LnpfLf/TNzr9zVOGiIY6aKCz8EKuXmlYNV7CM2pUjBa/B+c2I15tS7KLySep75+FuerJdmArvJLcsAXWEy2H0A==} - get-intrinsic@1.2.4: - resolution: {integrity: sha512-5uYhsJH8VJBTv7oslg4BznJYhDoRI6waYCxMmCdnTrcCrHA/fCFKoTFz2JKKE0HdDFUF7/oQuhzumXJK7paBRQ==} - engines: {node: '>= 0.4'} - get-intrinsic@1.3.0: resolution: {integrity: sha512-9fSjSaos/fRIVIp+xSJlE6lfwhES7LNtKaCBIamHsjr2na1BiABJPo0mOjjz8GJDURarmCPGqaiVg5mfjb98CQ==} engines: {node: '>= 0.4'} @@ -7001,8 +6894,8 @@ packages: resolution: {integrity: sha512-w9UMqWwJxHNOvoNzSJ2oPF5wvYcvP7jUvYzhp67yEhTi17ZDBBC1z9pTdGuzjD+EFIqLSYRweZjqfiPzQ06Ebg==} engines: {node: '>= 0.4'} - get-uri@6.0.4: - resolution: {integrity: sha512-E1b1lFFLvLgak2whF2xDBcOy6NLVGZBqqjJjsIhvopKfWWEi64pLVTWWehV8KlLerZkfNTA95sTe2OdJKm1OzQ==} + get-uri@6.0.5: + resolution: {integrity: sha512-b1O07XYq8eRuVzBNgJLstU6FYc1tS6wnMtF1I1D9lE8LxZSOGZ7LhxN54yPP6mGw5f2CkXY2BQUL9Fx41qvcIg==} engines: {node: '>= 14'} github-from-package@0.0.0: @@ -7059,10 +6952,6 @@ packages: global@4.4.0: resolution: {integrity: sha512-wv/LAoHdRE3BeTGz53FAamhGlPLhlssK45usmGFThIi4XqnBmjKQ16u+RNbP7WvigRZDxUsM0J3gcQ5yicaL0w==} - globals@11.12.0: - resolution: {integrity: sha512-WOBp/EEGUiIsJSp7wcv/y6MO+lV9UoncWqxuFfm8eBwzWNgyfBd6Gz+IeKQ9jCmyhoH99g15M3T+QaVHFjizVA==} - engines: {node: '>=4'} - globals@13.24.0: resolution: {integrity: sha512-AhO5QUcj8llrbG09iWhPU2B204J1xnPeL8kQmVorSsy+Sjj1sk8gIyh6cUocGmH4L0UuhAJy+hJMRA4mgA4mFQ==} engines: {node: '>=8'} @@ -7079,8 +6968,8 @@ packages: resolution: {integrity: sha512-jhIXaOzy1sb8IyocaruWSn1TjmnBVs8Ayhcy83rmxNJ8q2uWKCAj3CnJY+KpGSXCueAPc0i05kVvVKtP1t9S3g==} engines: {node: '>=10'} - globby@14.0.2: - resolution: {integrity: sha512-s3Fq41ZVh7vbbe2PN3nrW7yC7U7MFVc5c98/iTl9c2GawNMKx/J648KQRW6WKkuU8GIbbh2IXfIRQjOZnXcTnw==} + globby@14.1.0: + resolution: {integrity: sha512-0Ia46fDOaT7k4og1PDW4YbodWWr3scS2vAr2lTbsplOt2WkKp0vQbkI9wKis/T5LV/dqPjO3bpS/z6GTJB82LA==} engines: {node: '>=18'} globby@6.1.0: @@ -7137,8 +7026,8 @@ packages: resolution: {integrity: sha512-Jb6Z0+nvECVz+2lzSMt9u98UsoakXxA2HGHMCxh+so3n90XgYWkq5dur19JAJV7ONiJY22yBTyJB1TSkvPq9Ng==} engines: {node: '>=14'} - google-gax@4.4.1: - resolution: {integrity: sha512-Phyp9fMfA00J3sZbJxbbB4jC55b7DBjE3F6poyL3wKMEBVKA79q6BGuHcTiM28yOzVql0NDbRL8MLLh8Iwk9Dg==} + google-gax@4.6.1: + resolution: {integrity: sha512-V6eky/xz2mcKfAd1Ioxyd6nmA61gao3n01C+YeuIwu3vzM9EDR6wcVzMSIbLMDXWeoi9SHYctXuKYC5uJUT3eQ==} engines: {node: '>=14'} google-logging-utils@0.0.2: @@ -7209,10 +7098,6 @@ packages: resolution: {integrity: sha512-R3pbpkcIqv2Pm3dUwgjclDRVmWpTJW2DcMzcIhEXEx1oh/CEMObMm3KLmRJOdvhM7o4uQBnwr8pzRK2sJWIqfg==} engines: {node: '>= 0.4'} - has-flag@3.0.0: - resolution: {integrity: sha512-sKJf1+ceQBr4SMkvQnBDNDtf4TXpVhVGateu0t918bl30FnbE2m4vNLX+VWe/dpjlb+HugGYzW7uQXH98HPEYw==} - engines: {node: '>=4'} - has-flag@4.0.0: resolution: {integrity: sha512-EykJT/Q1KjTWctppgIAgfSO0tKVuZUjhgMr17kqTumMl6Afv3EISleU7qZUzoXDFTAHTDC4NOoG/ZxU3EvlMPQ==} engines: {node: '>=8'} @@ -7230,10 +7115,6 @@ packages: has-property-descriptors@1.0.2: resolution: {integrity: sha512-55JNKuIW+vq4Ke1BjOTjM2YctQIvCT7GFzHwmfZPGo5wnrgkid0YQtnAleFSqumZm4az3n2BS+erby5ipJdgrg==} - has-proto@1.0.3: - resolution: {integrity: sha512-SJ1amZAJUiZS+PhsVLf5tGydlaVB8EdFpaSO4gmiUKUOxk8qzn5AIy4ZeJUmh22znIdk/uMAUT2pl3FxzVUH+Q==} - engines: {node: '>= 0.4'} - has-proto@1.2.0: resolution: {integrity: sha512-KIL7eQPfHQRC8+XluaIw7BHUwwqL19bQn4hzNgdr+1wXoU0KKj6rufu47lhY7KbJR2C6T6+PfyN0Ea7wkSS+qQ==} engines: {node: '>= 0.4'} @@ -7281,10 +7162,6 @@ packages: resolution: {integrity: sha512-F/1DnUGPopORZi0ni+CvrCgHQ5FyEAHRLSApuYWMmrbSwoN2Mn/7k+Gl38gJnR7yyDZk6WLXwiGod1JOWNDKGw==} hasBin: true - hexoid@2.0.0: - resolution: {integrity: sha512-qlspKUK7IlSQv2o+5I7yhUd7TxlOG2Vr5LTa3ve2XSNVKAL/n/u/7KLvKmFNimomDIKvZFXWHv0T12mv7rT8Aw==} - engines: {node: '>=8'} - highlight-words-core@1.2.3: resolution: {integrity: sha512-m1O9HW3/GNHxzSIXWw1wCNXXsgLlxrP0OI6+ycGUhiUHkikqW3OrwVHz+lxeNBe5yqLESdIcj8PowHQ2zLvUvQ==} @@ -7383,8 +7260,8 @@ packages: http-parser-js@0.5.10: resolution: {integrity: sha512-Pysuw9XpUq5dVc/2SMHpuTY01RFl8fttgcyunjL7eEMhGM3cI4eOmiCycJDVCo/7O7ClfQD3SaI6ftDzqOXYMA==} - http-proxy-3@1.20.8: - resolution: {integrity: sha512-RNAa6Q8QswRZPzApka8e/4K5LOawFsGNLrWe1RbDol7nubErRgzyJJ9tpb5MEE/TvdC3VdPdmln/Rid8qkJxCA==} + http-proxy-3@1.20.10: + resolution: {integrity: sha512-jVCbIXd849IGpLyFSHuIFDE4HenkUJMaFB7P/pbRFAwfjTtOt1bkF+l7TQE/jsLADiconvSIBwF6eclEt9tjVA==} engines: {node: '>=18'} http-proxy-agent@5.0.0: @@ -7451,8 +7328,12 @@ packages: ieee754@1.2.1: resolution: {integrity: sha512-dcyqhDvX1C46lXZcVqCpK+FtMRQVdIMN6/Df5js2zouUsqG7I6sFxitIC+7KYK29KdXOLHdu9zL4sFnoVQnqaA==} - ignore@5.3.1: - resolution: {integrity: sha512-5Fytz/IraMjqpwfd34ke28PTVMjZjJG2MPn5t7OE4eUCUNf8BAa7b5WUS9/Qvr6mwOQS7Mk6vdsMno5he+T8Xw==} + ignore@5.3.2: + resolution: {integrity: sha512-hsBTNUqQTDwkWtcdYI2i06Y/nUBEsNEDJKjWdigLvegy8kDuJAS8uRlpkkcQpyEXL0Z/pjDy5HBmMjRCJ2gq+g==} + engines: {node: '>= 4'} + + ignore@7.0.5: + resolution: {integrity: sha512-Hs59xBNfUIunMFgWAbGX5cq6893IbWg4KnrjbYwX3tx0ztorVgTDA6B2sxf8ejHJ4wz8BqGUMYlnzNBer5NvGg==} engines: {node: '>= 4'} image-extensions@1.1.0: @@ -7470,8 +7351,8 @@ packages: immutable@4.3.7: resolution: {integrity: sha512-1hqclzwYwjRDFLjcFxOM5AYkkG0rpFPpr1RLPMEuGczoS7YA8gLhy8SWXYRAA/XwfEHpfo3cw5JGioS32fnMRw==} - immutable@5.1.1: - resolution: {integrity: sha512-3jatXi9ObIsPGr3N5hGw/vWWcTkq6hUYhpQz4k0wLC+owqWi/LiugIw9x0EdNZ2yGedKN/HzePiBvaJRXa0Ujg==} + immutable@5.1.3: + resolution: {integrity: sha512-+chQdDfvscSF1SJqv2gn4SRO2ZyS3xL3r7IW/wWEEzrzLisnOlKiQu5ytC/BVNcS15C39WT2Hg/bjKjDMcu+zg==} import-fresh@3.3.1: resolution: {integrity: sha512-TR3KfrTZTYLPB6jUjfx6MF9WcWrHL9su5TObK4ZkYgBdWKPOFoSoQIdEuTuR82pmtxH2spWG9h6etwfr1pLBqQ==} @@ -7561,8 +7442,8 @@ packages: resolution: {integrity: sha512-Ag3wB2o37wslZS19hZqorUnrnzSkpOVy+IiiDEiTqNubEYpYuHWIf6K4psgN2ZWKExS4xhVCrRVfb/wfW8fWJA==} engines: {node: '>= 10'} - irregular-plurals@3.3.0: - resolution: {integrity: sha512-MVBLKUTangM3EfRPFROhmWQQKRDsrgI83J8GS3jXy+OwYqiR2/aoWndYQ5416jLE3uaGgLH7ncme3X9y09gZ3g==} + irregular-plurals@3.5.0: + resolution: {integrity: sha512-1ANGLZ+Nkv1ptFb2pa8oG8Lem4krflKuX/gINiHJHjJUKaJHk/SXk5x6K3J+39/p0h1RQ2saROclJJ+QLvETCQ==} engines: {node: '>=8'} is-alphabetical@2.0.1: @@ -7887,8 +7768,8 @@ packages: resolution: {integrity: sha512-RKYVTCjAnRthyJes037NX/IiqeidgN1xc3j1RjFfECFp28A1GVwK9nA+i0rJPaHqSZwygLzRnFlzUuHFoWWy+Q==} engines: {node: '>=6'} - iterate-object@1.3.4: - resolution: {integrity: sha512-4dG1D1x/7g8PwHS9aK6QV5V94+ZvyP4+d19qDv43EzImmrndysIl4prmJ1hWWIGCqrZHyaHBm6BSEWHOLnpoNw==} + iterate-object@1.3.5: + resolution: {integrity: sha512-eL23u8oFooYTq6TtJKjp2RYjZnCkUYQvC0T/6fJfWykXJ3quvdDdzKZ3CEjy8b3JGOvLTjDYMEMIp5243R906A==} iterator.prototype@1.1.5: resolution: {integrity: sha512-H0dkQoCa3b2VEeKQBOxFph+JAbcrQdE7KC0UkqwpLmv2EC4P41QXP+rqo9wYodACiG5/WM5s9oDApTU8utwj9g==} @@ -8138,8 +8019,8 @@ packages: js-cookie@2.2.1: resolution: {integrity: sha512-HvdH2LzI/EAZcUwA8+0nKNtWHqS+ZmijLA30RwZA0bo7ToCckjK5MkGhjED9KoRcXO6BaGI3I9UIzSA1FKFPOQ==} - js-tiktoken@1.0.19: - resolution: {integrity: sha512-XC63YQeEcS47Y53gg950xiZ4IWmkfMe4p2V9OSaBt26q+p47WHn18izuXzSclCI73B7yGqtfRsT6jcZQI0y08g==} + js-tiktoken@1.0.20: + resolution: {integrity: sha512-Xlaqhhs8VfCd6Sh7a1cFkZHQbYTLCwVJJWiHVxBYzLPxW0XsoxBy1hitmjkdIjD3Aon5BXLHFwU5O8WUx6HH+A==} js-tokens@4.0.0: resolution: {integrity: sha512-RdJUflcE3cUzKiMqQgsCu06FPu9UdIJO0beYbPhHN4k6apgJtifcoCtT9bcxOpYBtpD2kCM6Sbzg4CausW/PKQ==} @@ -8247,11 +8128,11 @@ packages: jupyter-paths@2.0.4: resolution: {integrity: sha512-hS2osbroOqfcEsnkTHNkdDhNs0dwhR7/k57msC0iLo03pb6dqVMjpiBMxHhJ4/XNddhoc1SU3SdAk+pg2VuiRw==} - jwa@1.4.1: - resolution: {integrity: sha512-qiLX/xhEEFKUAJ6FiBMbes3w9ATzyk5W7Hvzpa/SLYdxNtng+gcurvrI7TbACjIXlsJyr05/S1oUhZrc63evQA==} + jwa@1.4.2: + resolution: {integrity: sha512-eeH5JO+21J78qMvTIDdBXidBd6nG2kZjg5Ohz/1fpa28Z4CcsWUzJ1ZZyFq/3z3N17aZy+ZuBoHljASbL1WfOw==} - jwa@2.0.0: - resolution: {integrity: sha512-jrZ2Qx916EA+fq9cEAeCROWPTfCwi1IVHqT2tapuqLEVVDKFDENFw1oL+MwrTvH6msKxsd1YTDVw6uKEcsrLEA==} + jwa@2.0.1: + resolution: {integrity: sha512-hRF04fqJIP8Abbkq5NKGN0Bbr3JxlQ+qhZufXVr0DvujKy93ZCbXZMHDL4EOtodSbCWxOqR8MS1tXA5hwqCXDg==} jws@3.2.2: resolution: {integrity: sha512-YHlZCB6lMTllWDtSPHz/ZXTsi8S00usEV6v1tjq8tOUZzw7DpSDWVXjXDre6ed1w/pd495ODpHZYSdkRTsa0HA==} @@ -8313,11 +8194,20 @@ packages: langs@2.0.0: resolution: {integrity: sha512-v4pxOBEQVN1WBTfB1crhTtxzNLZU9HPWgadlwzWKISJtt6Ku/CnpBrwVy+jFv8StjxsPfwPFzO0CMwdZLJ0/BA==} - langsmith@0.3.33: - resolution: {integrity: sha512-imNIaBL6+ElE5eMzNHYwFxo6W/6rHlqcaUjCYoIeGdCYWlARxE3CTGKul5DJnaUgGP2CTLFeNXyvRx5HWC/4KQ==} + langsmith@0.3.44: + resolution: {integrity: sha512-LMCZ7ULSzIpDmsrxGZKzCpp8exuempvCFCX1N0m+u517ZhikPDEtAtgnREObMjIISzB7eXkODkFq0Klxc9FODg==} peerDependencies: + '@opentelemetry/api': '*' + '@opentelemetry/exporter-trace-otlp-proto': '*' + '@opentelemetry/sdk-trace-base': '*' openai: '*' peerDependenciesMeta: + '@opentelemetry/api': + optional: true + '@opentelemetry/exporter-trace-otlp-proto': + optional: true + '@opentelemetry/sdk-trace-base': + optional: true openai: optional: true @@ -8359,8 +8249,8 @@ packages: resolution: {integrity: sha512-+bT2uH4E5LGE7h/n3evcS/sQlJXCpIp6ym8OWJ5eV6+67Dsql/LaaT7qJBAt2rzfoa/5QBGBhxDix1dMt2kQKQ==} engines: {node: '>= 0.8.0'} - lib0@0.2.102: - resolution: {integrity: sha512-g70kydI0I1sZU0ChO8mBbhw0oUW/8U0GHzygpvEIx8k+jgOpqnTSb/E+70toYVqHxBhrERD21TwD5QcZJQ40ZQ==} + lib0@0.2.109: + resolution: {integrity: sha512-jP0gbnyW0kwlx1Atc4dcHkBbrVAkdHjuyHxtClUPYla7qCmwIif1qZ6vQeJdR5FrOVdn26HvQT0ko01rgW7/Xw==} engines: {node: '>=16'} hasBin: true @@ -8465,8 +8355,8 @@ packages: resolution: {integrity: sha512-8XPvpAA8uyhfteu8pIvQxpJZ7SYYdpUivZpGy6sFsBuKRY/7rQGavedeB8aK+Zkyq6upMFVL/9AW6vOYzfRyLg==} engines: {node: '>=10'} - long@5.3.1: - resolution: {integrity: sha512-ka87Jz3gcx/I7Hal94xaN2tZEOPoUOEVftkQqZx2EeQRN7LGdfLlI3FvZ+7WDplm+vK2Urx9ULrvSowtdCieng==} + long@5.3.2: + resolution: {integrity: sha512-mNAgZ1GmyNhD7AuqnTG3/VQ26o760+ZYBPKjPvugO8+nLbYfX6TVpJPseBvopbdY+qpZ/lKUnmEc1LeZYS3QAA==} loose-envify@1.4.0: resolution: {integrity: sha512-lyuxPGr/Wfhrlem2CL/UcnUc1zcqKAImBDzukY7Y5F/yQiNdko6+fRLevlw1HgMySw7f611UIY408EtxRSoK3Q==} @@ -8580,8 +8470,8 @@ packages: resolution: {integrity: sha512-dq+qelQ9akHpcOl/gUVRTxVIOkAJ1wR3QAvb4RsVjS8oVoFjDGTc679wJYmUmknUF5HwMLOgb5O+a3KxfWapPQ==} engines: {node: '>= 0.6'} - memfs@4.17.0: - resolution: {integrity: sha512-4eirfZ7thblFmqFjywlTmuWVSvccHAJbn1r8qQLzmTO11qcqpohOjmY2mFce6x7x7WtskzRqApPD0hv+Oa74jg==} + memfs@4.17.2: + resolution: {integrity: sha512-NgYhCOWgovOXSzvYgUW0LQ7Qy72rWQMGGFJDoWg4G30RHd3z77VbYdtJ4fembJXBy8pMIUA31XNAupobOQlwdg==} engines: {node: '>= 4.0.0'} memoize-one@4.0.3: @@ -8645,6 +8535,10 @@ packages: resolution: {integrity: sha512-sPU4uV7dYlvtWJxwwxHD0PuihVNiE7TyAbQ5SWxDCB9mUYvOgroQOwYQQOKPJ8CIbE+1ETVlOoK1UC2nU3gYvg==} engines: {node: '>= 0.6'} + mime-db@1.54.0: + resolution: {integrity: sha512-aU5EJuIN2WDemCcAp2vFBfp/m4EAhWJnUNSSw0ixs7/kXbd6Pg64EmwJkNdFhB8aWt1sH2CTXrLxo/iAGV3oPQ==} + engines: {node: '>= 0.6'} + mime-types@2.1.35: resolution: {integrity: sha512-ZDY+bPm5zTTF+YpCrAU9nK0UgICYPT0QtT1NZWFv4s++TNkcgVaT0g6+4R2uI4MjQjzysHB1zxuWL50hzaeXiw==} engines: {node: '>= 0.6'} @@ -8775,19 +8669,14 @@ packages: resolution: {integrity: sha512-at/ZndSy3xEGJ8i0ygALh8ru9qy7gWW1cmkaqBN29JmMlIvM//MEO9y1sk/avxuwnPcfhkejkLsuPxH81BrkSg==} engines: {node: '>=0.8.0'} - nan@2.20.0: - resolution: {integrity: sha512-bk3gXBZDGILuuo/6sKtr0DQmSThYHLtNCdSdXk9YkxD/jK6X2vmCyyXBBxyqZ4XcnzTyYEAThfX3DCEnLf6igw==} + nan@2.23.0: + resolution: {integrity: sha512-1UxuyYGdoQHcGg87Lkqm3FzefucTa0NAiOcuRsDmysep3c1LVCRK2krrUDafMWtjSG04htvAmvg96+SDknOmgQ==} nanoid@3.3.11: resolution: {integrity: sha512-N8SpfPUnUp1bK+PMYW8qSWdl9U+wwNWI4QKxOYDy9JAro3WMX7p2OeVRF9v+347pnakNevPmiHhNmZ2HbFA76w==} engines: {node: ^10 || ^12 || ^13.7 || ^14 || >=15.0.1} hasBin: true - nanoid@3.3.8: - resolution: {integrity: sha512-WNLf5Sd8oZxOm+TzppcYk8gVOgP+l58xNy58D0nbUnOxOWRWvlcCV4kUF7ltmI6PsrLl/BgKEyS4mqsGChFN0w==} - engines: {node: ^10 || ^12 || ^13.7 || ^14 || >=15.0.1} - hasBin: true - napi-build-utils@2.0.0: resolution: {integrity: sha512-GEbrYkbfF7MoNaoh2iGG84Mnf/WZfB0GdGEsM8wz7Expx/LlWf5U8t9nvJKXSp3qr5IsEbK04cBGhol/KwOsWA==} @@ -8867,15 +8756,15 @@ packages: no-case@3.0.4: resolution: {integrity: sha512-fgAN3jGAh+RoxUGZHTSOLJIqUc2wmoBwGR4tbpNAKmmovFoWq0OdRkb0VkldReO2a2iBT/OEulG9XSUc10r3zg==} - node-abi@3.74.0: - resolution: {integrity: sha512-c5XK0MjkGBrQPGYG24GBADZud0NCbznxNx0ZkS+ebUTrmV1qTDxPxSL8zEAPURXSbLRWVexxmP4986BziahL5w==} + node-abi@3.75.0: + resolution: {integrity: sha512-OhYaY5sDsIka7H7AtijtI9jwGYLyl29eQn/W623DiN/MIv5sUqc4g7BIDThX+gb7di9f6xK02nkp8sdfFWZLTg==} engines: {node: '>=10'} node-addon-api@7.1.1: resolution: {integrity: sha512-5m3bsyrjFWE1xf7nz7YXdN4udnVtXK6/Yfgn5qnahL6bCkf2yKt4k3nuTKAtT4r3IG8JNR2ncsIMdZuAzJjHQQ==} - node-addon-api@8.3.1: - resolution: {integrity: sha512-lytcDEdxKjGJPTLEfW4mYMigRezMlyJY8W4wxJK8zE533Jlb8L8dRuObJFWg2P+AuOIxoCgKF+2Oq4d4Zd0OUA==} + node-addon-api@8.4.0: + resolution: {integrity: sha512-D9DI/gXHvVmjHS08SVch0Em8G5S1P+QWtU31appcKT/8wFSPRcdHadIFSAntdMMVM5zz+/DL+bL/gz3UDppqtg==} engines: {node: ^18 || ^20 || >= 21} node-cleanup@2.1.2: @@ -8884,16 +8773,6 @@ packages: node-domexception@1.0.0: resolution: {integrity: sha512-/jKZoMpw0F8GRwl4/eLROPA3cfcXtLApP0QzLmUT/HuPCZWyB7IY9ZrMeKw2O/nFIqPQB3PVM9aYm0F312AXDQ==} engines: {node: '>=10.5.0'} - deprecated: Use your platform's native DOMException instead - - node-fetch@2.6.7: - resolution: {integrity: sha512-ZjMPFEfVx5j+y2yF35Kzx5sF7kDzxuDj6ziH4FFbOp87zKDZNx8yExJIb05OGF4Nlt9IHFIMBkRl41VdvcNdbQ==} - engines: {node: 4.x || >=6.0.0} - peerDependencies: - encoding: ^0.1.0 - peerDependenciesMeta: - encoding: - optional: true node-fetch@2.7.0: resolution: {integrity: sha512-c4FRfUm/dbcWZ7U+1Wq0AwCyFL+3nt2bEw05wfxSz+DWpWsitgmSgYmy2dQdWyKC1694ELPqMs/YzUSNozLt8A==} @@ -9024,8 +8903,8 @@ packages: obuf@1.1.2: resolution: {integrity: sha512-PX1wu0AmAdPqOL1mWhqmlOd8kOIZQwGZw6rh7uby9fTc5lhaOWFLX3I6R1hrF9k3zUY40e6igsLGkDXK92LJNg==} - ollama@0.5.15: - resolution: {integrity: sha512-TSaZSJyP7MQJFjSmmNsoJiriwa3U+/UJRw6+M8aucs5dTsaWNZsBIGpDb5rXnW6nXxJBB/z79gZY8IaiIQgelQ==} + ollama@0.5.16: + resolution: {integrity: sha512-OEbxxOIUZtdZgOaTPAULo051F5y+Z1vosxEYOoABPnQKeW7i4O8tJNlxCB+xioyoorVqgjkdj+TA1f1Hy2ug/w==} on-finished@2.4.1: resolution: {integrity: sha512-oVlzkg3ENAhCk2zdv7IJwd/QUD4z2RxRwpkcGY8psCVcCYZNq4wYnVWALHM+brtuJjePWiYF/ClmuDr8Ch5+kg==} @@ -9065,8 +8944,8 @@ packages: zod: optional: true - openai@5.7.0: - resolution: {integrity: sha512-zXWawZl6J/P5Wz57/nKzVT3kJQZvogfuyuNVCdEp4/XU2UNrjL7SsuNpWAyLZbo6HVymwmnfno9toVzBhelygA==} + openai@5.9.0: + resolution: {integrity: sha512-cmLC0pfqLLhBGxE4aZPyRPjydgYCncppV2ClQkKmW79hNjCvmzkfhz8rN5/YVDmjVQlFV+UsF1JIuNjNgeagyQ==} hasBin: true peerDependencies: ws: ^8.18.0 @@ -9149,9 +9028,6 @@ packages: resolution: {integrity: sha512-5NPgf87AT2STgwa2ntRMr45jTKrYBGkVU36yT0ig/n/GMAa3oPqhZfIQ2kMEimReg0+t9kZViDVZ83qfVUlckg==} engines: {node: '>= 14'} - package-json-from-dist@1.0.0: - resolution: {integrity: sha512-dATvCeZN/8wQsGywez1mzHtTlP22H8OEfPrVMLNr4/eGa+ijtLn/6M5f0dY8UKNrC2O9UCU6SSoG3qRKnt7STw==} - package-json-from-dist@1.0.1: resolution: {integrity: sha512-UEZIS3/by4OC8vL3P2dTXRETpebLI2NiI5vIrjaD/5UtrkFX/tNbwjTSRAGC/+7CAo2pIcBaRgWmcBBHcsaCIw==} @@ -9214,9 +9090,6 @@ packages: parse5@6.0.1: resolution: {integrity: sha512-Ofn/CTFzRGTTxwpNEs9PP93gXShHcTq255nzRYSKe8AkVpZY7e1fpmTfOyoIvjP5HG7Z2ZM7VS9PPhQGW2pOpw==} - parse5@7.2.1: - resolution: {integrity: sha512-BuBYQYlv1ckiPdQi/ohiivi9Sagc9JG+Ozs0r7b/0iK3sKmrb0b9FdWdBbOdx6hBCM/F9Ir82ofnBhtZOjCRPQ==} - parse5@7.3.0: resolution: {integrity: sha512-IInvU7fabl34qmi9gY8XOVxhYyMyuH2xUNpb2q8/Y+7552KlejkRvqvD19nMoUW/uQGGbqNpA6Tufu5FL5BZgw==} @@ -9333,9 +9206,9 @@ packages: resolution: {integrity: sha512-gDKb8aZMDeD/tZWs9P6+q0J9Mwkdl6xMV8TjnGP3qJVJ06bdMgkbBlLU8IdfOsIsFz2BW1rNVT3XuNEl8zPAvw==} engines: {node: '>=8'} - path-type@5.0.0: - resolution: {integrity: sha512-5HviZNaZcfqP95rwpv+1HDgUamezbqdSYTyzjTvwtJSnIH+3vnbmWsItli8OFEndS984VT55M3jduxZbX351gg==} - engines: {node: '>=12'} + path-type@6.0.0: + resolution: {integrity: sha512-Vj7sf++t5pBD637NSfkxpHSMfWaeig5+DKWLhcqIYx6mWQz5hdJTGDVMQiJcw1ZYkhs7AazKDGpRVji1LJCZUQ==} + engines: {node: '>=18'} pathe@2.0.3: resolution: {integrity: sha512-WUjGcAqP1gQacoQe+OBJsFA7Ld4DyXuUIjZ5cc75cLHvJ7dtNsTugphxIADwspS+AraAUePCKrSVtPLFj/F88w==} @@ -9372,9 +9245,6 @@ packages: pg-protocol@1.10.3: resolution: {integrity: sha512-6DIBgBQaTKDJyxnXaLiLR8wBpQQcGWuAESkRBX/t6OwA8YsqP+iVSiond2EDy6Y/dsGk8rh/jtax3js5NeV7JQ==} - pg-protocol@1.8.0: - resolution: {integrity: sha512-jvuYlEkL03NRvOoyoRktBK7+qU5kOvlAwvmrH8sr3wbLrOdVWsRxQfz8mMy9sZFsqJ1hEWNfdWKI4SAmoL+j7g==} - pg-types@2.2.0: resolution: {integrity: sha512-qTAAlrEsl8s4OiEQY69wDvcMIdQN6wdz5ojQiOy6YRMuynxenON0O5oCpJI6lshc6scgAY8qvJ2On/p+CXY0GA==} engines: {node: '>=4'} @@ -9394,9 +9264,6 @@ packages: pick-by-alias@1.2.0: resolution: {integrity: sha512-ESj2+eBxhGrcA1azgHs7lARG5+5iLakc/6nlfbpjcLl00HuuUOIuORhYXN4D1HfvMSKuVtFQjAlnwi1JHEeDIw==} - picocolors@1.1.0: - resolution: {integrity: sha512-TQ92mBOW0l3LeMeyLV6mzy/kWr8lkd/hp3mTg7wYK7zJhuBStmGMBG0BdeDZS/dZx1IukaX6Bk11zcln25o1Aw==} - picocolors@1.1.1: resolution: {integrity: sha512-xceH2snhtb5M9liqDsmEw56le376mTZkEX/jEb/RxNFyegNul7eNslCXP9FDj/Lcu0X8KEyMceP2ntpaHrDEVA==} @@ -9424,8 +9291,8 @@ packages: resolution: {integrity: sha512-MnUuEycAemtSaeFSjXKW/aroV7akBbY+Sv+RkyqFjgAe73F+MR0TBWKBRDkmfWq/HiFmdavfZ1G7h4SPZXaCSg==} engines: {node: '>=0.10.0'} - pirates@4.0.6: - resolution: {integrity: sha512-saLsH7WeYYPiD25LDuLRRY/i+6HaPYr6G1OUlN39otzkSTxKnubR9RTxS3/Kk50s1g2JTgFwWQDQyplC5/SHZg==} + pirates@4.0.7: + resolution: {integrity: sha512-TfySrs/5nm8fQJDcBDuUng3VOUKsd7S+zqvbOTiGXHfxX4wK31ard+hoNuvkicM/2YFzlpDgABOevKSsB4G/FA==} engines: {node: '>= 6'} pkg-dir@4.2.0: @@ -9435,18 +9302,8 @@ packages: pkg-types@1.3.1: resolution: {integrity: sha512-/Jm5M4RvtBFVkKWRu2BLUTNP8/M2a+UwuAX+ae4770q1qVGtfjG+WTCupoZixokjmHiry8uI+dlY8KXYV5HVVQ==} - pkg-types@2.1.0: - resolution: {integrity: sha512-wmJwA+8ihJixSoHKxZJRBQG1oY8Yr9pGLzRmSsNms0iNWyHHAlZCa7mmKiFR10YPZuz/2k169JiS/inOjBCZ2A==} - - playwright-core@1.51.1: - resolution: {integrity: sha512-/crRMj8+j/Nq5s8QcvegseuyeZPxpQCZb6HNk3Sos3BlZyAknRjoyJPFWkpNn8v0+P3WiwqFF8P+zQo4eqiNuw==} - engines: {node: '>=18'} - hasBin: true - - playwright@1.51.1: - resolution: {integrity: sha512-kkx+MB2KQRkyxjYPc3a0wLZZoDczmppyGJIvQ43l+aZihkaVvmu/21kiyaHeHjiFxjxNNFnUncKmcGIyOojsaw==} - engines: {node: '>=18'} - hasBin: true + pkg-types@2.2.0: + resolution: {integrity: sha512-2SM/GZGAEkPp3KWORxQZns4M+WSeXbC2HEvmOIJe3Cmiv6ieAJvdVhDldtHqM5J1Y7MrR1XhkBT/rMlhh9FdqQ==} plotly.js@2.35.3: resolution: {integrity: sha512-7RaC6FxmCUhpD6H4MpD+QLUu3hCn76I11rotRefrh3m1iDvWqGnVqVk9dSaKmRAhFD3vsNsYea0OxnR1rc2IzQ==} @@ -9509,8 +9366,8 @@ packages: resolution: {integrity: sha512-PS08Iboia9mts/2ygV3eLpY5ghnUcfLV/EXTOW1E2qYxJKGGBUtNjN76FYHnMs36RmARn41bC0AZmn+rR0OVpQ==} engines: {node: ^10 || ^12 || >=14} - postcss@8.5.3: - resolution: {integrity: sha512-dle9A3yYxlBSrt8Fu+IpjGT8SY8hN0mlaA6GY8t0P5PjIOZemULz/E2Bnm/2dcUOena75OTNkHI76uZBNUUq3A==} + postcss@8.5.6: + resolution: {integrity: sha512-3Ybi1tAuwAP9s0r1UQ2J4n5Y0G05bJkpUIO0/bI9MhwmD70S5aTWbXGBwxHrelT+XM1k6dM0pk+SwNkpTRN7Pg==} engines: {node: ^10 || ^12 || >=14} postgres-array@2.0.0: @@ -9630,8 +9487,8 @@ packages: resolution: {integrity: sha512-SAzp/O4Yh02jGdRc+uIrGoe87dkN/XtwxfZ4ZyafJHymd79ozp5VG5nyZ7ygqPM5+cpLDjjGnYFUkngonyDPOQ==} engines: {node: '>=14.0.0'} - protobufjs@7.4.0: - resolution: {integrity: sha512-mRUWCc3KUU4w1jU8sGxICXH/gNS94DvI1gxqDvBzhj1JpcsimQkYiOJfwsPUykUI5ZaspFbSgmBLER8IrQ3tqw==} + protobufjs@7.5.3: + resolution: {integrity: sha512-sildjKwVqOI2kmFDiXQ6aEB0fjYTafpEvIBs8tOR8qI4spuL9OPROLVu2qZqi/xgCfsHIwVqlaF8JBjWFHnKbw==} engines: {node: '>=12.0.0'} protocol-buffers-schema@3.6.0: @@ -9651,8 +9508,8 @@ packages: prr@1.0.1: resolution: {integrity: sha512-yPw4Sng1gWghHQWj0B3ZggWUm4qVbPwPFcRG8KyxiU7J2OHFSoEHKS+EZ3fv5l1t9CyCiop6l/ZYeWbrgoQejw==} - pump@3.0.2: - resolution: {integrity: sha512-tUPXtzlGM8FE3P0ZL6DVs/3P58k9nk8/jZeQCurTJylQA8qFYzHFfhBJkuqyE0FifOsQ0uKWekiZ5g8wtr28cw==} + pump@3.0.3: + resolution: {integrity: sha512-todwxLMY7/heScKmntwQG8CXVkWUOdYxIvY2s0VWAAMh/nd8SoYiRaKjlr7+iCs984f2P8zvrfWcDDYVb73NfA==} punycode@2.3.1: resolution: {integrity: sha512-vYt7UD1U9Wg6138shLtLOvdAu+8DsC/ilFtEVHcH+wydcSpNE20AfSOduf6MkRFahL5FY7X1oU7nKVZFtfq8Fg==} @@ -9669,6 +9526,10 @@ packages: resolution: {integrity: sha512-+38qI9SOr8tfZ4QmJNplMUxqjbe7LKvvZgWdExBOmd+egZTtjLB67Gu0HRX3u/XOq7UU2Nx6nsjvS16Z9uwfpg==} engines: {node: '>=0.6'} + qs@6.14.0: + resolution: {integrity: sha512-YWWTjgABSKcvs/nWBi9PycY/JiPJqOD4JA6o9Sej2AtvSGarXxKC3OQSk4pAarbdQlKAh5D4FCQkJNkW+GAn3w==} + engines: {node: '>=0.6'} + quansync@0.2.10: resolution: {integrity: sha512-t41VRkMYbkHyCYmOvx/6URnN80H7k4X0lLdBMGsz+maAwrJQYB1djpV6vHrQIBE0WBSGqhtEHrK9U3DWWH8v7A==} @@ -9688,8 +9549,8 @@ packages: quickselect@3.0.0: resolution: {integrity: sha512-XdjUArbK4Bm5fLLvlm5KpTFOiOThgfWWI4axAZDWg4E/0mKdZyI9tNEfds27qCi1ze/vwTR16kvmmGhRra3c2g==} - r-json@1.3.0: - resolution: {integrity: sha512-xesd+RHCpymPCYd9DvDvUr1w1IieSChkqYF1EpuAYrvCfLXji9NP36DvyYZJZZB5soVDvZ0WUtBoZaU1g5Yt9A==} + r-json@1.3.1: + resolution: {integrity: sha512-5nhRFfjVMQdrwKUfUlRpDUCocdKtjSnYZ1R/86mpZDV3MfsZ3dYYNjSGuMX+mPBvFvQBhdzxSqxkuLPLv4uFGg==} raf@3.4.1: resolution: {integrity: sha512-Sq4CW4QhwOHE8ucn6J34MqtZCeWFP2aQSmrlroYgqAV1PjStIhJXxYuTgUIfkEk7zTLjmIjLmU5q+fbD1NnOJA==} @@ -9956,20 +9817,6 @@ packages: react: '>=16.9.0' react-dom: '>=16.9.0' - rc-virtual-list@3.18.5: - resolution: {integrity: sha512-1FuxVSxhzTj3y8k5xMPbhXCB0t2TOiI3Tq+qE2Bu+GGV7f+ECVuQl4OUg6lZ2qT5fordTW7CBpr9czdzXCI7Pg==} - engines: {node: '>=8.x'} - peerDependencies: - react: '>=16.9.0' - react-dom: '>=16.9.0' - - rc-virtual-list@3.18.6: - resolution: {integrity: sha512-TQ5SsutL3McvWmmxqQtMIbfeoE3dGjJrRSfKekgby7WQMpPIFvv4ghytp5Z0s3D8Nik9i9YNOCqHBfk86AwgAA==} - engines: {node: '>=8.x'} - peerDependencies: - react: '>=16.9.0' - react-dom: '>=16.9.0' - rc-virtual-list@3.19.1: resolution: {integrity: sha512-DCapO2oyPqmooGhxBuXHM4lFuX+sshQwWqqkuyFA+4rShLe//+GEPVwiDgO+jKtKHtbeYwZoNvetwfHdOf+iUQ==} engines: {node: '>=8.x'} @@ -10016,8 +9863,8 @@ packages: react: '>= 16.3.0' react-dom: '>= 16.3.0' - react-easy-crop@5.4.1: - resolution: {integrity: sha512-Djtsi7bWO75vkKYkVxNRrJWY69pXLahIAkUN0mmt9cXNnaq2tpG59ctSY6P7ipJgBc7COJDRMRuwb2lYwtACNQ==} + react-easy-crop@5.5.0: + resolution: {integrity: sha512-OZzU+yXMhe69vLkDex+5QxcfT94FdcgVCyW2dBUw35ZoC3Is42TUxUy04w8nH1mfMKaizVdC3rh/wUfNW1mK4w==} peerDependencies: react: '>=16.4.0' react-dom: '>=16.4.0' @@ -10189,9 +10036,6 @@ packages: regenerator-runtime@0.11.1: resolution: {integrity: sha512-MguG95oij0fC3QV3URf4V2SDYGJhJnJGqvIIgdECeODCT98wSWDAJ94SSuVpYQUoTcGUIL6L4yNB7j1DFFHSBg==} - regenerator-runtime@0.14.1: - resolution: {integrity: sha512-dYnhHh0nJoMfnkZs6GmmhFknAGRrLznOu5nc9ML+EJxGvrx6H7teuevqVqCuPcPK//3eDrrjQhehXVx9cnkGdw==} - regexp.prototype.flags@1.5.4: resolution: {integrity: sha512-dYqgNSZbDwkaJ2ceRd9ojCGjBq+mOm9LmtXnAnEGyHhN/5R7iDW2TRw3h+o/jCFxus3P2LfWIIiwowAjANm7IA==} engines: {node: '>= 0.4'} @@ -10266,8 +10110,8 @@ packages: resolve-protobuf-schema@2.1.0: resolution: {integrity: sha512-kI5ffTiZWmJaS/huM8wZfEMer1eRd7oJQhDuxeCLe3t7N7mX3z94CN0xPxBQxFYQTSNz9T0i+v6inKqSdK8xrQ==} - resolve.exports@2.0.2: - resolution: {integrity: sha512-X2UW6Nw3n/aMgDVy+0rSqgHlv39WZAlZrXCdnbyEiKm17DSqHX4MmQMaST3FbeWR5FTuRcUwYAziZajji0Y7mg==} + resolve.exports@2.0.3: + resolution: {integrity: sha512-OcXjMsGdhL4XnbShKpAcSqPMzQoYkYyhbEaeSko47MjRP9NfEQMhZkXL1DoFlt9LWQn4YttrdnV6X2OiyzBi+A==} engines: {node: '>=10'} resolve@0.6.3: @@ -10278,10 +10122,6 @@ packages: engines: {node: '>= 0.4'} hasBin: true - resolve@1.22.8: - resolution: {integrity: sha512-oKWePCxqpd6FlLvGV1VU0x7bkPmmCNolxzjMf4NczoDnQcIWrAF+cPtZn5i6n+RfD2d9i0tzpKnG6Yk168yIyw==} - hasBin: true - resolve@2.0.0-next.5: resolution: {integrity: sha512-U7WjGVG9sH8tvjW5SmGbQuui75FiyjAX72HX15DwBBwF9dNiQZRQAg9nnPhYy+TUnE0+VcrttuvNI8oSxZcocA==} hasBin: true @@ -10298,8 +10138,8 @@ packages: resolution: {integrity: sha512-XQBQ3I8W1Cge0Seh+6gjj03LbmRFWuoszgK9ooCpwYIrhhoO80pfq4cUkU5DkknwfOfFteRwlZ56PYOGYyFWdg==} engines: {node: '>= 4'} - reusify@1.0.4: - resolution: {integrity: sha512-U9nH88a3fc/ekCF1l0/UP1IosiuIjyTh7hBvXVMHYgVcfGvt897Xguj2UOLDeI5BG2m7/uwyaLVT6fbtCwTyzw==} + reusify@1.1.0: + resolution: {integrity: sha512-g6QUff04oZpHs0eG5p83rFLhHeV00ug/Yf9nZM6fLeUrPguBTkTQOdpAWWspMh55TZfVQDPaN3NQJfbVRAxdIw==} engines: {iojs: '>=1.0.0', node: '>=0.10.0'} right-now@1.0.0: @@ -10315,6 +10155,11 @@ packages: deprecated: Rimraf versions prior to v4 are no longer supported hasBin: true + rimraf@2.7.1: + resolution: {integrity: sha512-uWjbaKIK3T1OSVptzX7Nl6PvQ3qAGtKEtVRjRuazjfL3Bx5eI409VZSqgND+4UNnmzLVdPj9FqFJNPqBZFve4w==} + deprecated: Rimraf versions prior to v4 are no longer supported + hasBin: true + rimraf@3.0.2: resolution: {integrity: sha512-JZkJMZkAGFFPP2YqXZXPbMlMBgsxzE8ILs4lMIX/2o0L9UBw9O/Y3o6wFw/i9YLapcUJWwqbi3kdxIPdC62TIA==} deprecated: Rimraf versions prior to v4 are no longer supported @@ -10421,10 +10266,6 @@ packages: resolution: {integrity: sha512-pN/yOAvcC+5rQ5nERGuwrjLlYvLTbCibnZ1I7B1LaiAz9BRBlE9GMgE/eqV30P7aJQUf7Ddimy/RsbYO/GrVGg==} engines: {node: '>= 10.13.0'} - schema-utils@4.3.0: - resolution: {integrity: sha512-Gf9qqc58SpCA/xdziiHz35F4GNIWYWZrEshUc/G/r5BnLph6xpKuLeoJoQuj5WfBIx/eQLf+hmVPYHaxJu7V2g==} - engines: {node: '>= 10.13.0'} - schema-utils@4.3.2: resolution: {integrity: sha512-Gn/JaSk/Mt9gYubxTtSn/QCV4em9mpAPiR1rqy/Ocu19u/G9J5WWdNoUT4SiV6mFC3y6cxyFcFwdzPM3FgxGAQ==} engines: {node: '>= 10.13.0'} @@ -10453,16 +10294,6 @@ packages: resolution: {integrity: sha512-BR7VvDCVHO+q2xBEWskxS6DJE1qRnb7DxzUrogb71CWoSficBxYsiAGd+Kl0mmq/MprG9yArRkyrQxTO6XjMzA==} hasBin: true - semver@7.6.3: - resolution: {integrity: sha512-oVekP1cKtI+CTDvHWYFUcMtsK/00wmAEfyqKfNdARm8u1wNVhSgaX7A8d4UuIlUI5e84iEwOhs7ZPYRmzU9U6A==} - engines: {node: '>=10'} - hasBin: true - - semver@7.7.1: - resolution: {integrity: sha512-hlq8tAfn0m/61p4BVRcPzIGr6LKiMwo4VM6dGi6pt4qcRkmNzTcWq6eCEjEh+qXjkMDvPlOFFSGwQjoEa6gyMA==} - engines: {node: '>=10'} - hasBin: true - semver@7.7.2: resolution: {integrity: sha512-RF0Fw+rO5AMf9MAyaRXI4AV0Ulj5lMHqVxxdSgiVbixSCXoEmmX/jk0CuJw4+3SqroYO9VoUh+HcuJivvtJemA==} engines: {node: '>=10'} @@ -10517,8 +10348,8 @@ packages: shallowequal@1.1.0: resolution: {integrity: sha512-y0m1JoUZSlPAjXVtPPW70aZWfIL/dSP7AFkRnniLCrK/8MDKog3TySTBmckD+RObVxH0v4Tox67+F14PdED2oQ==} - sharp@0.34.2: - resolution: {integrity: sha512-lszvBmB9QURERtyKT2bNmsgxXK0ShJrL/fvqlonCo7e6xBF8nT8xU6pW+PMIbLsz0RxQk3rgH9kd8UmvOzlMJg==} + sharp@0.34.3: + resolution: {integrity: sha512-eX2IQ6nFohW4DbvHIOLRB3MHFpYqaqvXd3Tp5e/T/dSH83fxaNJQRvDMhASmkNTsNTVF2/OOopzRCt7xokgPfg==} engines: {node: ^18.17.0 || ^20.3.0 || >=21.0.0} shebang-command@2.0.0: @@ -10551,10 +10382,6 @@ packages: resolution: {integrity: sha512-WPS/HvHQTYnHisLo9McqBHOJk2FkHO/tlpvldyrnem4aeQp4hai3gythswg6p01oSoTl58rcpiFAjF2br2Ak2A==} engines: {node: '>= 0.4'} - side-channel@1.0.6: - resolution: {integrity: sha512-fDW/EZ6Q9RiO8eFG8Hj+7u/oW+XrPTIChwCOM2+th2A6OblDtYYIpve9m+KvI9Z4C9qSEXlaGR6bTEYHReuglA==} - engines: {node: '>= 0.4'} - side-channel@1.1.0: resolution: {integrity: sha512-ZX99e6tRweoUXqR+VBrslhda51Nh5MTQwou5tnUDgbtyM0dBgmhEDtWGP/xbKn6hqfPRHujUNwz5fy/wbbhnpw==} engines: {node: '>= 0.4'} @@ -10578,8 +10405,8 @@ packages: simple-swizzle@0.2.2: resolution: {integrity: sha512-JA//kQgZtbuY83m+xT+tXJkmJncGMTFT+C+g2h2R9uxkYIrE2yy9sgmcLhCnw57/WSD+Eh3J97FPEDFnbXnDUg==} - simple-wcswidth@1.1.1: - resolution: {integrity: sha512-R3q3/eoeNBp24CNTASEUrffXi0j9TwPIEvSStlvSrsFimM17sV5EHcMOc86j3K+UWZyLYvH0hRmYGCpCoaJ4vw==} + simple-wcswidth@1.1.2: + resolution: {integrity: sha512-j7piyCjAeTDSjzTSQ7DokZtMNwNlEAyxqSZeCS+CXH7fJ4jx3FuJ/mTW3mE+6JLs4VJBbcll0Kjn+KXI5t21Iw==} sirv@2.0.4: resolution: {integrity: sha512-94Bdh3cC2PKrbgSOUqTiGPWVZeSiXfKOVZNJniWoqrWrRkB1CJzBU3NEbiTsPcYy1lDsANA/THzS+9WBiy5nfQ==} @@ -10629,8 +10456,8 @@ packages: resolution: {integrity: sha512-HehCEsotFqbPW9sJ8WVYB6UbmIMv7kUUORIF2Nncq4VQvBfNBLibW9YZR5dlYCSUhwcD628pRllm7n+E+YTzJw==} engines: {node: '>= 14'} - socks@2.8.4: - resolution: {integrity: sha512-D3YaD0aRxR3mEcqnidIs7ReYJFVzWdd6fXJYUM8ixcQcJRGTka/b3saV0KflYhyVJXKhb947GndU35SxYNResQ==} + socks@2.8.6: + resolution: {integrity: sha512-pe4Y2yzru68lXCb38aAqRf5gvN8YdjP1lok5o0J7BOHljkyCGKVz7H3vpVIXKD27rj2giOJ7DwVyk/GWrPHDWA==} engines: {node: '>= 10.0.0', npm: '>= 3.0.0'} source-map-js@1.2.1: @@ -10659,17 +10486,17 @@ packages: spawn-command@0.0.2: resolution: {integrity: sha512-zC8zGoGkmc8J9ndvml8Xksr1Amk9qBujgbF0JAIWO7kXr43w0h/0GJNM/Vustixu+YE8N/MTrQ7N31FvHUACxQ==} - spdx-correct@3.1.1: - resolution: {integrity: sha512-cOYcUWwhCuHCXi49RhFRCyJEK3iPj1Ziz9DpViV3tbZOwXD49QzIN3MpOLJNxh2qwq2lJJZaKMVw9qNi4jTC0w==} + spdx-correct@3.2.0: + resolution: {integrity: sha512-kN9dJbvnySHULIluDHy32WHRUu3Og7B9sbY7tsFLctQkIqnMh3hErYgdMjTYuqmcXX+lK5T1lnUt3G7zNswmZA==} - spdx-exceptions@2.3.0: - resolution: {integrity: sha512-/tTrYOC7PPI1nUAgx34hUpqXuyJG+DTHJTnIULG4rDygi4xu/tfgmq1e1cIRwRzwZgo4NLySi+ricLkZkw4i5A==} + spdx-exceptions@2.5.0: + resolution: {integrity: sha512-PiU42r+xO4UbUS1buo3LPJkjlO7430Xn5SVAhdpzzsPHsjbYVflnnFdATgabnLude+Cqu25p6N+g2lw/PFsa4w==} spdx-expression-parse@3.0.1: resolution: {integrity: sha512-cbqHunsQWnJNE6KhVSMsMeH5H/L9EpymbzqTQ3uLwNCLZ1Q481oWaofqH7nO6V07xlXwY6PhQdQ2IedWx/ZK4Q==} - spdx-license-ids@3.0.12: - resolution: {integrity: sha512-rr+VVSXtRhO4OHbXUiAF7xW3Bo9DuuF6C5jH+q/x15j2jniycgKbxU09Hr0WqlSLUs4i4ltHGXqTe7VHclYWyA==} + spdx-license-ids@3.0.21: + resolution: {integrity: sha512-Bvg/8F5XephndSK3JffaRqdT+gyhfqIPwDHpX80tJrF8QQRYMo8sNMeaZ2Dp5+jhwKnUmIOyFFQfHRkjJm5nXg==} spdy-transport@3.0.0: resolution: {integrity: sha512-hsLVFE5SjA6TCisWeJXFKniGGOpBgMLmerfO2aCyCU5s7nJ/rpAepqmFifv/GCbSbueEeAJJnmSQ2rKC/g8Fcw==} @@ -10881,10 +10708,6 @@ packages: superscript-text@1.0.0: resolution: {integrity: sha512-gwu8l5MtRZ6koO0icVTlmN5pm7Dhh1+Xpe9O4x6ObMAsW+3jPbW14d1DsBq1F4wiI+WOFjXF35pslgec/G8yCQ==} - supports-color@5.5.0: - resolution: {integrity: sha512-QjVjwdXIt408MIiAqCX4oUKsgU2EqAGzs2Ppkm4aQYbjm+ZEWEcW4SfFNTr4uMNZma0ey4f5lgLrkB0aX0QMow==} - engines: {node: '>=4'} - supports-color@7.2.0: resolution: {integrity: sha512-qpCAvRl9stuOHveKsn7HncJRvv501qIacKzQlO/+Lwxc9+0q2wLyv4Dfvt80/DPn2pqOBsJdDiogXGR9+OvwRw==} engines: {node: '>=8'} @@ -10893,8 +10716,8 @@ packages: resolution: {integrity: sha512-MpUEN2OodtUzxvKQl72cUF7RQ5EiHsGvSsVG0ia9c5RbWGL2CI4C7EpPS8UTBIplnlzZiNuV56w+FuNxy3ty2Q==} engines: {node: '>=10'} - supports-hyperlinks@2.2.0: - resolution: {integrity: sha512-6sXEzV5+I5j8Bmq9/vUphGRM/RJNT9SCURJLjwfOg51heRtguGWDzcaBlgAzKhQa0EVNpPEKzQuBwZ8S8WaCeQ==} + supports-hyperlinks@2.3.0: + resolution: {integrity: sha512-RpsAZlpWcDwOPQA22aCH4J0t7L8JmAvsCxfOSEwm7cQs3LshN36QaTkwd70DnBOXDWGssw2eUoc8CaRWT0XunA==} engines: {node: '>=8'} supports-preserve-symlinks-flag@1.0.0: @@ -10964,11 +10787,6 @@ packages: engines: {node: '>=6.0.0'} hasBin: true - terser@5.39.0: - resolution: {integrity: sha512-LBAhFyLho16harJoWMg/nZsQYgTrg5jXOn2nCYjRUcZZEdE3qa2zb8QEDRUGVZBW4rlazf2fxkg8tztybTaqWw==} - engines: {node: '>=10'} - hasBin: true - terser@5.43.1: resolution: {integrity: sha512-+6erLbBm0+LROX2sPXlUYx/ux5PyE9K/a92Wrt6oA+WDAoFTdpHE5tCYCI5PNzq2y8df4rA+QgHLJuR4jNymsg==} engines: {node: '>=10'} @@ -11033,8 +10851,8 @@ packages: tinyexec@1.0.1: resolution: {integrity: sha512-5uC6DDlmeqiOwCPmK9jMSdOuZTh8bU39Ys6yidB+UTt5hfZUPGAypSgFRiEp+jbi9qH40BLDvy85jIU88wKSqw==} - tinyglobby@0.2.13: - resolution: {integrity: sha512-mEwzpUgrLySlveBwEVDMKk5B57bhLPYovRfPAXD5gA/98Opn0rCDj3GtLwFvCvH5RK9uPCExUROW5NjDwvqkxw==} + tinyglobby@0.2.14: + resolution: {integrity: sha512-tX5e7OM1HnYr2+a2C/4V0htOcSQcoSTH9KgJnVvNm5zm/cyEWKJ7j7YutsH9CxMdtOkkLFy2AHrMci9IM8IPZQ==} engines: {node: '>=12.0.0'} tinyqueue@2.0.3: @@ -11081,8 +10899,8 @@ packages: resolution: {integrity: sha512-o5sSPKEkg/DIQNmH43V0/uerLrpzVedkUh8tGNvaeXpfpuwjKenlSox/2O/BTlZUtEe+JG7s5YhEz608PlAHRA==} engines: {node: '>=0.6'} - token-types@6.0.0: - resolution: {integrity: sha512-lbDrTLVsHhOMljPscd0yitpozq7Ga2M5Cvez5AjGg8GASBjtt6iERCAJ93yommPmz62fb45oFIXHEZ3u9bfJEA==} + token-types@6.0.3: + resolution: {integrity: sha512-IKJ6EzuPPWtKtEIEPpIdXv9j5j2LGJEYk0CKY2efgKoYKLBiZdh6iQkLVBow/CB3phyWAWCyk+bZeaimJn6uRQ==} engines: {node: '>=14.16'} topojson-client@3.1.0: @@ -11104,8 +10922,8 @@ packages: resolution: {integrity: sha512-hdF5ZgjTqgAntKkklYw0R03MG2x/bSzTtkxmIRw/sTNV8YXsCJ1tfLAX23lhxhHJlEf3CRCOCGGWw3vI3GaSPw==} engines: {node: '>=18'} - tree-dump@1.0.2: - resolution: {integrity: sha512-dpev9ABuLWdEubk+cIaI9cHwRNNDjkBBLXTwI4UCUFdQ5xXKqNXoK4FEciw/vxf+NQ7Cb7sGUyeUtORvHIdRXQ==} + tree-dump@1.0.3: + resolution: {integrity: sha512-il+Cv80yVHFBwokQSfd4bldvr1Md951DpgAGfmhydt04L+YzHgubm2tQ7zueWDcGENKHq0ZvGFR/hjvNXilHEg==} engines: {node: '>=10.0'} peerDependencies: tslib: '2' @@ -11215,10 +11033,6 @@ packages: resolution: {integrity: sha512-tLq3bSNx+xSpwvAJnzrK0Ep5CLNWjvFTOp71URMaAEWBfRb9nnJiBoUe0tF8bI4ZFO3omgBR6NvnbzVUT3Ly4g==} engines: {node: '>=14.16'} - type-fest@4.32.0: - resolution: {integrity: sha512-rfgpoi08xagF3JSdtJlCwMq9DGNDE0IMh3Mkpc1wUypg9vPi786AiqeBBKcqvIkq42azsBM85N490fyZjeUftw==} - engines: {node: '>=16'} - type-fest@4.41.0: resolution: {integrity: sha512-TeTSQ6H5YHvpqVwBRcnLDCBnDOHWYu7IvGbHT6N8AOymcr9PJGjc1GTtiWZTYg0NCgYwvnYWEkVChQAr9bjfwA==} engines: {node: '>=16'} @@ -11296,11 +11110,8 @@ packages: undici-types@5.26.5: resolution: {integrity: sha512-JlCMO+ehdEIKqlFxk6IfVoAUVmgz7cU7zD/h9XZ0qzeosSHmUJVOzSQvvYSYWXkFXC+IfLKSIffhv0sVZup6pA==} - undici-types@7.8.0: - resolution: {integrity: sha512-9UJ2xGDvQ43tYyVMpuHlsgApydB8ZKfVYTsLDhXkFL/6gfkp+U8xTGdh8pMJv1SpZna0zxG1DwsKZsreLbXBxw==} - - unicorn-magic@0.1.0: - resolution: {integrity: sha512-lRfVq8fE8gz6QMBuDM6a+LO3IAzTi05H6gCVaUpir2E1Rwpo4ZUog45KpNXKC/Mn3Yb9UDuHumeFTo9iV/D9FQ==} + unicorn-magic@0.3.0: + resolution: {integrity: sha512-+QBBXBCvifc56fsbuxZQ6Sic3wqqc3WWaqxs58gvJrcOuN83HGTCwz3oS5phzU9LthRNE9VrJCFCLUgHeeFnfA==} engines: {node: '>=18'} unified@11.0.5: @@ -11343,8 +11154,8 @@ packages: universal-cookie@4.0.4: resolution: {integrity: sha512-lbRVHoOMtItjWbM7TwDLdl8wug7izB0tq3/YVKhT/ahB4VDvWMyvnADfnJI8y6fSvsjh51Ix7lTGC6Tn4rMPhw==} - universalify@2.0.0: - resolution: {integrity: sha512-hAZsKq7Yy11Zu1DE0OzWjw7nnLZmJZYTDZZyEFHZdUhV8FkH5MCfoU1XMaxXovpyW5nq5scPqq0ZDP9Zyl04oQ==} + universalify@2.0.1: + resolution: {integrity: sha512-gptHNQghINnc/vTGIk0SOFGFNXw7JVrlRUtConJRlvaw6DuX0wO5Jeko9sWrMBhh+PsYAZ7oXAiOnf/UKogyiw==} engines: {node: '>= 10.0.0'} unpipe@1.0.0: @@ -11414,8 +11225,8 @@ packages: peerDependencies: react: '>=16.8.0' - use-isomorphic-layout-effect@1.2.0: - resolution: {integrity: sha512-q6ayo8DWoPZT0VdG4u3D3uxcgONP3Mevx2i2b0434cwWBoL+aelL1DzkXI6w3PhTZzUeR2kaVlZn70iCiseP6w==} + use-isomorphic-layout-effect@1.2.1: + resolution: {integrity: sha512-tpZZ+EX0gaghDAiFR37hj5MgY6ZN55kLiPkJsKxBMZ6GZdOSPJXiOzPM984oPYZ5AnehYx5WQp1+ME8I/P/pRA==} peerDependencies: '@types/react': '*' react: ^16.8.0 || ^17.0.0 || ^18.0.0 || ^19.0.0 @@ -11476,8 +11287,8 @@ packages: resolution: {integrity: sha512-b+1eJOlsR9K8HJpow9Ok3fiWOWSIcIzXodvv0rQjVoOVNpWMpxf1wZNpt4y9h10odCNrqnYp1OBzRktckBe3sA==} hasBin: true - v8-to-istanbul@9.2.0: - resolution: {integrity: sha512-/EH/sDgxU2eGxajKdwLCDmQ4FWq+kpi3uCmBGpw1xJtnAxEjlD8j8PEiGWpCIMIs3ciNAgH0d3TTJiUkYzyZjA==} + v8-to-istanbul@9.3.0: + resolution: {integrity: sha512-kiGUalWN+rgBJ/1OHZsBtU4rXZOfj/7rKQxULKlIzwzQSvMJUUNgPwJEEh7gU6xEVxC0ahoOBvN2YI8GH6FNgA==} engines: {node: '>=10.12.0'} valid-url@1.0.9: @@ -11566,6 +11377,9 @@ packages: w-json@1.3.10: resolution: {integrity: sha512-XadVyw0xE+oZ5FGApXsdswv96rOhStzKqL53uSe5UaTadABGkWIg1+DTx8kiZ/VqTZTBneoL0l65RcPe4W3ecw==} + w-json@1.3.11: + resolution: {integrity: sha512-Xa8vTinB5XBIYZlcN8YyHpE625pBU6k+lvCetTQM+FKxRtLJxAY9zUVZbRqCqkMeEGbQpKvGUzwh4wZKGem+ag==} + w3c-xmlserializer@5.0.0: resolution: {integrity: sha512-o8qghlI8NZHU1lLPrpi2+Uq7abh4GGPpYANlalzWxyWteJOCsr/P+oPBA49TOLu5FTZO4d3F9MnWJfiMo4BkmA==} engines: {node: '>=18'} @@ -11643,8 +11457,8 @@ packages: resolution: {integrity: sha512-yd1RBzSGanHkitROoPFd6qsrxt+oFhg/129YzheDGqeustzX0vTZJZsSsQjVQC4yzBQ56K55XU8gaNCtIzOnTg==} engines: {node: '>=10.13.0'} - webpack@5.99.5: - resolution: {integrity: sha512-q+vHBa6H9qwBLUlHL4Y7L0L1/LlyBKZtS9FHNCQmtayxjI5RKC9yD8gpvLeqGv5lCQp1Re04yi0MF40pf30Pvg==} + webpack@5.100.1: + resolution: {integrity: sha512-YJB/ESPUe2Locd0NKXmw72Dx8fZQk1gTzI6rc9TAT4+Sypbnhl8jd8RywB1bDsDF9Dy1RUR7gn3q/ZJTd0OZZg==} engines: {node: '>=10.13.0'} hasBin: true peerDependencies: @@ -11716,8 +11530,8 @@ packages: wordwrap@1.0.0: resolution: {integrity: sha512-gvVzJFlPycKc5dZN4yPkP8w7Dc37BtP1yczEneOb4uq34pXZcvrtRTmWV8W+Ume+XCxKgbjM+nevkyFPMybd4Q==} - world-calendars@1.0.3: - resolution: {integrity: sha512-sAjLZkBnsbHkHWVhrsCU5Sa/EVuf9QqgvrN8zyJ2L/F9FR9Oc6CvVK0674+PGAtmmmYQMH98tCUSO4QLQv3/TQ==} + world-calendars@1.0.4: + resolution: {integrity: sha512-VGRnLJS+xJmGDPodgJRnGIDwGu0s+Cr9V2HB3EzlDZ5n0qb8h5SJtGUEkjrphZYAglEiXZ6kiXdmk0H/h/uu/w==} wrap-ansi@6.2.0: resolution: {integrity: sha512-r6lPcBGxZXlIcymEu7InxDMhdW0KDxpLgoFLcguasxCaJ/SOIZwINatK9KY/tf+ZrlywOKU0UDj3ATXUBfxJXA==} @@ -11778,8 +11592,8 @@ packages: resolution: {integrity: sha512-GCPAHLvrIH13+c0SuacwvRYj2SxJXQ4kaVTT5xgL3kPrz56XxkF21IGhjSE1+W0aw7gpBWRGXLCPnPby6lSpmQ==} engines: {node: '>=12'} - xml-crypto@6.1.0: - resolution: {integrity: sha512-0TYPBRPwXLnRGc2F0f9Zc/H076YcP7tkCa2US4jpguuPTEx7TWFqSysIfJ1hP4r2KF82IYzhnzepnsUEsOjlOw==} + xml-crypto@6.1.2: + resolution: {integrity: sha512-leBOVQdVi8FvPJrMYoum7Ici9qyxfE4kVi+AkpUoYCSXaQF4IlBm1cneTK9oAxR61LpYxTx7lNcsnBIeRpGW2w==} engines: {node: '>=16'} xml-encryption@3.1.0: @@ -11878,8 +11692,8 @@ packages: resolution: {integrity: sha512-7dSzzRQ++CKnNI/krKnYRV7JKKPUXMEh61soaHKg9mrWEhzFWhFnxPxGl+69cD1Ou63C13NUPCnmIcrvqCuM6w==} engines: {node: '>=12'} - yjs@13.6.24: - resolution: {integrity: sha512-xn/pYLTZa3uD1uDG8lpxfLRo5SR/rp0frdASOl2a71aYNvUXdWcLtVL91s2y7j+Q8ppmjZ9H3jsGVgoFMbT2VA==} + yjs@13.6.27: + resolution: {integrity: sha512-OIDwaflOaq4wC6YlPBy2L6ceKeKuF7DeTxx+jPzv1FHn9tCZ0ZwSRnUBxD05E3yed46fv/FWJbvR+Ud7x0L7zw==} engines: {node: '>=16.0.0', npm: '>=8.0.0'} yocto-queue@0.1.0: @@ -11898,16 +11712,13 @@ packages: peerDependencies: zod: ^3.21.4 - zod-to-json-schema@3.24.5: - resolution: {integrity: sha512-/AuWwMP+YqiPbsJx5D6TfgRTc4kTLjsh5SOcd4bLsfUg2RcEXrFMJl1DGgdHy2aCfsIA/cr/1JM0xcB2GZji8g==} + zod-to-json-schema@3.24.6: + resolution: {integrity: sha512-h/z3PKvcTcTetyjl1fkj79MHNEjm+HpD6NXheWjzOekY7kV+lwDYnHw+ivHkijnCSMz1yJaWBD9vu/Fcmk+vEg==} peerDependencies: zod: ^3.24.1 - zod@3.25.67: - resolution: {integrity: sha512-idA2YXwpCdqUSKRCACDE6ItZD9TZzy3OZMtpfLoh6oPR47lipysRrJfjzMqFxQ3uJuUPyUeWe1r9vLH33xO/Qw==} - - zod@3.25.75: - resolution: {integrity: sha512-OhpzAmVzabPOL6C3A3gpAifqr9MqihV/Msx3gor2b2kviCgcb+HM9SEOpMWwwNp9MRunWnhtAKUoo0AHhjyPPg==} + zod@3.25.76: + resolution: {integrity: sha512-gzUt/qt81nXsFGKIFcC3YnfEAx5NkunCfnDlvuBSSFS02bcXu4Lmea0AFIUwbLWxWPx3d9p8S5QoaujKcNQxcQ==} zstd-napi@0.0.10: resolution: {integrity: sha512-pwnG+auSiIrD2BNSIpPEUtcRSK33cfYmKo3sJPTohFiPqPci9F4SIRPR7gGeI45Maj4nFoyyxzT2YDxVXIIgzQ==} @@ -11922,8 +11733,8 @@ snapshots: '@ampproject/remapping@2.3.0': dependencies: - '@jridgewell/gen-mapping': 0.3.8 - '@jridgewell/trace-mapping': 0.3.25 + '@jridgewell/gen-mapping': 0.3.12 + '@jridgewell/trace-mapping': 0.3.29 '@ant-design/colors@6.0.0': dependencies: @@ -11965,7 +11776,7 @@ snapshots: '@ant-design/cssinjs@1.23.0(react-dom@19.1.0(react@19.1.0))(react@19.1.0)': dependencies: - '@babel/runtime': 7.27.1 + '@babel/runtime': 7.27.6 '@emotion/hash': 0.8.0 '@emotion/unitless': 0.7.5 classnames: 2.5.1 @@ -12073,31 +11884,26 @@ snapshots: '@csstools/css-tokenizer': 3.0.4 lru-cache: 10.4.3 - '@babel/code-frame@7.24.7': - dependencies: - '@babel/highlight': 7.25.7 - picocolors: 1.1.0 - '@babel/code-frame@7.27.1': dependencies: '@babel/helper-validator-identifier': 7.27.1 js-tokens: 4.0.0 picocolors: 1.1.1 - '@babel/compat-data@7.26.8': {} + '@babel/compat-data@7.28.0': {} - '@babel/core@7.26.9': + '@babel/core@7.28.0': dependencies: '@ampproject/remapping': 2.3.0 '@babel/code-frame': 7.27.1 - '@babel/generator': 7.27.1 - '@babel/helper-compilation-targets': 7.27.0 - '@babel/helper-module-transforms': 7.27.1(@babel/core@7.26.9) - '@babel/helpers': 7.27.0 - '@babel/parser': 7.27.2 + '@babel/generator': 7.28.0 + '@babel/helper-compilation-targets': 7.27.2 + '@babel/helper-module-transforms': 7.27.3(@babel/core@7.28.0) + '@babel/helpers': 7.27.6 + '@babel/parser': 7.28.0 '@babel/template': 7.27.2 - '@babel/traverse': 7.27.1 - '@babel/types': 7.27.1 + '@babel/traverse': 7.28.0 + '@babel/types': 7.28.1 convert-source-map: 2.0.0 debug: 4.4.1 gensync: 1.0.0-beta.2 @@ -12106,35 +11912,37 @@ snapshots: transitivePeerDependencies: - supports-color - '@babel/generator@7.27.1': + '@babel/generator@7.28.0': dependencies: - '@babel/parser': 7.27.2 - '@babel/types': 7.27.1 - '@jridgewell/gen-mapping': 0.3.8 - '@jridgewell/trace-mapping': 0.3.25 + '@babel/parser': 7.28.0 + '@babel/types': 7.28.1 + '@jridgewell/gen-mapping': 0.3.12 + '@jridgewell/trace-mapping': 0.3.29 jsesc: 3.1.0 - '@babel/helper-compilation-targets@7.27.0': + '@babel/helper-compilation-targets@7.27.2': dependencies: - '@babel/compat-data': 7.26.8 + '@babel/compat-data': 7.28.0 '@babel/helper-validator-option': 7.27.1 browserslist: 4.25.1 lru-cache: 5.1.1 semver: 6.3.1 + '@babel/helper-globals@7.28.0': {} + '@babel/helper-module-imports@7.27.1': dependencies: - '@babel/traverse': 7.27.1 - '@babel/types': 7.27.1 + '@babel/traverse': 7.28.0 + '@babel/types': 7.28.1 transitivePeerDependencies: - supports-color - '@babel/helper-module-transforms@7.27.1(@babel/core@7.26.9)': + '@babel/helper-module-transforms@7.27.3(@babel/core@7.28.0)': dependencies: - '@babel/core': 7.26.9 + '@babel/core': 7.28.0 '@babel/helper-module-imports': 7.27.1 '@babel/helper-validator-identifier': 7.27.1 - '@babel/traverse': 7.27.1 + '@babel/traverse': 7.28.0 transitivePeerDependencies: - supports-color @@ -12146,123 +11954,121 @@ snapshots: '@babel/helper-validator-option@7.27.1': {} - '@babel/helpers@7.27.0': + '@babel/helpers@7.27.6': dependencies: '@babel/template': 7.27.2 - '@babel/types': 7.27.1 + '@babel/types': 7.28.1 - '@babel/highlight@7.25.7': + '@babel/parser@7.28.0': dependencies: - '@babel/helper-validator-identifier': 7.27.1 - chalk: 2.4.2 - js-tokens: 4.0.0 - picocolors: 1.1.1 + '@babel/types': 7.28.1 - '@babel/parser@7.27.2': + '@babel/plugin-syntax-async-generators@7.8.4(@babel/core@7.28.0)': dependencies: - '@babel/types': 7.27.1 + '@babel/core': 7.28.0 + '@babel/helper-plugin-utils': 7.27.1 - '@babel/plugin-syntax-async-generators@7.8.4(@babel/core@7.26.9)': + '@babel/plugin-syntax-bigint@7.8.3(@babel/core@7.28.0)': dependencies: - '@babel/core': 7.26.9 + '@babel/core': 7.28.0 '@babel/helper-plugin-utils': 7.27.1 - '@babel/plugin-syntax-bigint@7.8.3(@babel/core@7.26.9)': + '@babel/plugin-syntax-class-properties@7.12.13(@babel/core@7.28.0)': dependencies: - '@babel/core': 7.26.9 + '@babel/core': 7.28.0 '@babel/helper-plugin-utils': 7.27.1 - '@babel/plugin-syntax-class-properties@7.12.13(@babel/core@7.26.9)': + '@babel/plugin-syntax-class-static-block@7.14.5(@babel/core@7.28.0)': dependencies: - '@babel/core': 7.26.9 + '@babel/core': 7.28.0 '@babel/helper-plugin-utils': 7.27.1 - '@babel/plugin-syntax-import-meta@7.10.4(@babel/core@7.26.9)': + '@babel/plugin-syntax-import-attributes@7.27.1(@babel/core@7.28.0)': dependencies: - '@babel/core': 7.26.9 + '@babel/core': 7.28.0 '@babel/helper-plugin-utils': 7.27.1 - '@babel/plugin-syntax-json-strings@7.8.3(@babel/core@7.26.9)': + '@babel/plugin-syntax-import-meta@7.10.4(@babel/core@7.28.0)': dependencies: - '@babel/core': 7.26.9 + '@babel/core': 7.28.0 '@babel/helper-plugin-utils': 7.27.1 - '@babel/plugin-syntax-jsx@7.27.1(@babel/core@7.26.9)': + '@babel/plugin-syntax-json-strings@7.8.3(@babel/core@7.28.0)': dependencies: - '@babel/core': 7.26.9 + '@babel/core': 7.28.0 '@babel/helper-plugin-utils': 7.27.1 - '@babel/plugin-syntax-logical-assignment-operators@7.10.4(@babel/core@7.26.9)': + '@babel/plugin-syntax-jsx@7.27.1(@babel/core@7.28.0)': dependencies: - '@babel/core': 7.26.9 + '@babel/core': 7.28.0 '@babel/helper-plugin-utils': 7.27.1 - '@babel/plugin-syntax-nullish-coalescing-operator@7.8.3(@babel/core@7.26.9)': + '@babel/plugin-syntax-logical-assignment-operators@7.10.4(@babel/core@7.28.0)': dependencies: - '@babel/core': 7.26.9 + '@babel/core': 7.28.0 '@babel/helper-plugin-utils': 7.27.1 - '@babel/plugin-syntax-numeric-separator@7.10.4(@babel/core@7.26.9)': + '@babel/plugin-syntax-nullish-coalescing-operator@7.8.3(@babel/core@7.28.0)': dependencies: - '@babel/core': 7.26.9 + '@babel/core': 7.28.0 '@babel/helper-plugin-utils': 7.27.1 - '@babel/plugin-syntax-object-rest-spread@7.8.3(@babel/core@7.26.9)': + '@babel/plugin-syntax-numeric-separator@7.10.4(@babel/core@7.28.0)': dependencies: - '@babel/core': 7.26.9 + '@babel/core': 7.28.0 '@babel/helper-plugin-utils': 7.27.1 - '@babel/plugin-syntax-optional-catch-binding@7.8.3(@babel/core@7.26.9)': + '@babel/plugin-syntax-object-rest-spread@7.8.3(@babel/core@7.28.0)': dependencies: - '@babel/core': 7.26.9 + '@babel/core': 7.28.0 '@babel/helper-plugin-utils': 7.27.1 - '@babel/plugin-syntax-optional-chaining@7.8.3(@babel/core@7.26.9)': + '@babel/plugin-syntax-optional-catch-binding@7.8.3(@babel/core@7.28.0)': dependencies: - '@babel/core': 7.26.9 + '@babel/core': 7.28.0 '@babel/helper-plugin-utils': 7.27.1 - '@babel/plugin-syntax-top-level-await@7.14.5(@babel/core@7.26.9)': + '@babel/plugin-syntax-optional-chaining@7.8.3(@babel/core@7.28.0)': dependencies: - '@babel/core': 7.26.9 + '@babel/core': 7.28.0 '@babel/helper-plugin-utils': 7.27.1 - '@babel/plugin-syntax-typescript@7.27.1(@babel/core@7.26.9)': + '@babel/plugin-syntax-private-property-in-object@7.14.5(@babel/core@7.28.0)': dependencies: - '@babel/core': 7.26.9 + '@babel/core': 7.28.0 '@babel/helper-plugin-utils': 7.27.1 - '@babel/runtime@7.25.6': + '@babel/plugin-syntax-top-level-await@7.14.5(@babel/core@7.28.0)': dependencies: - regenerator-runtime: 0.14.1 + '@babel/core': 7.28.0 + '@babel/helper-plugin-utils': 7.27.1 - '@babel/runtime@7.27.0': + '@babel/plugin-syntax-typescript@7.27.1(@babel/core@7.28.0)': dependencies: - regenerator-runtime: 0.14.1 - - '@babel/runtime@7.27.1': {} + '@babel/core': 7.28.0 + '@babel/helper-plugin-utils': 7.27.1 '@babel/runtime@7.27.6': {} '@babel/template@7.27.2': dependencies: '@babel/code-frame': 7.27.1 - '@babel/parser': 7.27.2 - '@babel/types': 7.27.1 + '@babel/parser': 7.28.0 + '@babel/types': 7.28.1 - '@babel/traverse@7.27.1': + '@babel/traverse@7.28.0': dependencies: '@babel/code-frame': 7.27.1 - '@babel/generator': 7.27.1 - '@babel/parser': 7.27.2 + '@babel/generator': 7.28.0 + '@babel/helper-globals': 7.28.0 + '@babel/parser': 7.28.0 '@babel/template': 7.27.2 - '@babel/types': 7.27.1 + '@babel/types': 7.28.1 debug: 4.4.1 - globals: 11.12.0 transitivePeerDependencies: - supports-color - '@babel/types@7.27.1': + '@babel/types@7.28.1': dependencies: '@babel/helper-string-parser': 7.27.1 '@babel/helper-validator-identifier': 7.27.1 @@ -12322,64 +12128,64 @@ snapshots: '@cspell/cspell-bundled-dicts@8.19.4': dependencies: - '@cspell/dict-ada': 4.1.0 - '@cspell/dict-al': 1.1.0 - '@cspell/dict-aws': 4.0.10 - '@cspell/dict-bash': 4.2.0 - '@cspell/dict-companies': 3.2.1 - '@cspell/dict-cpp': 6.0.8 - '@cspell/dict-cryptocurrencies': 5.0.4 - '@cspell/dict-csharp': 4.0.6 - '@cspell/dict-css': 4.0.17 - '@cspell/dict-dart': 2.3.0 - '@cspell/dict-data-science': 2.0.8 - '@cspell/dict-django': 4.1.4 - '@cspell/dict-docker': 1.1.14 - '@cspell/dict-dotnet': 5.0.9 - '@cspell/dict-elixir': 4.0.7 - '@cspell/dict-en-common-misspellings': 2.0.11 + '@cspell/dict-ada': 4.1.1 + '@cspell/dict-al': 1.1.1 + '@cspell/dict-aws': 4.0.12 + '@cspell/dict-bash': 4.2.1 + '@cspell/dict-companies': 3.2.2 + '@cspell/dict-cpp': 6.0.9 + '@cspell/dict-cryptocurrencies': 5.0.5 + '@cspell/dict-csharp': 4.0.7 + '@cspell/dict-css': 4.0.18 + '@cspell/dict-dart': 2.3.1 + '@cspell/dict-data-science': 2.0.9 + '@cspell/dict-django': 4.1.5 + '@cspell/dict-docker': 1.1.15 + '@cspell/dict-dotnet': 5.0.10 + '@cspell/dict-elixir': 4.0.8 + '@cspell/dict-en-common-misspellings': 2.1.3 '@cspell/dict-en-gb': 1.1.33 - '@cspell/dict-en_us': 4.4.9 - '@cspell/dict-filetypes': 3.0.12 - '@cspell/dict-flutter': 1.1.0 - '@cspell/dict-fonts': 4.0.4 - '@cspell/dict-fsharp': 1.1.0 - '@cspell/dict-fullstack': 3.2.6 - '@cspell/dict-gaming-terms': 1.1.1 - '@cspell/dict-git': 3.0.5 - '@cspell/dict-golang': 6.0.21 - '@cspell/dict-google': 1.0.8 - '@cspell/dict-haskell': 4.0.5 - '@cspell/dict-html': 4.0.11 - '@cspell/dict-html-symbol-entities': 4.0.3 - '@cspell/dict-java': 5.0.11 - '@cspell/dict-julia': 1.1.0 - '@cspell/dict-k8s': 1.0.10 - '@cspell/dict-kotlin': 1.1.0 - '@cspell/dict-latex': 4.0.3 - '@cspell/dict-lorem-ipsum': 4.0.4 - '@cspell/dict-lua': 4.0.7 - '@cspell/dict-makefile': 1.0.4 - '@cspell/dict-markdown': 2.0.10(@cspell/dict-css@4.0.17)(@cspell/dict-html-symbol-entities@4.0.3)(@cspell/dict-html@4.0.11)(@cspell/dict-typescript@3.2.2) - '@cspell/dict-monkeyc': 1.0.10 - '@cspell/dict-node': 5.0.7 - '@cspell/dict-npm': 5.2.4 - '@cspell/dict-php': 4.0.14 - '@cspell/dict-powershell': 5.0.14 - '@cspell/dict-public-licenses': 2.0.13 - '@cspell/dict-python': 4.2.18 - '@cspell/dict-r': 2.1.0 - '@cspell/dict-ruby': 5.0.8 - '@cspell/dict-rust': 4.0.11 - '@cspell/dict-scala': 5.0.7 - '@cspell/dict-shell': 1.1.0 - '@cspell/dict-software-terms': 5.0.10 - '@cspell/dict-sql': 2.2.0 - '@cspell/dict-svelte': 1.0.6 - '@cspell/dict-swift': 2.0.5 - '@cspell/dict-terraform': 1.1.1 - '@cspell/dict-typescript': 3.2.2 - '@cspell/dict-vue': 3.0.4 + '@cspell/dict-en_us': 4.4.15 + '@cspell/dict-filetypes': 3.0.13 + '@cspell/dict-flutter': 1.1.1 + '@cspell/dict-fonts': 4.0.5 + '@cspell/dict-fsharp': 1.1.1 + '@cspell/dict-fullstack': 3.2.7 + '@cspell/dict-gaming-terms': 1.1.2 + '@cspell/dict-git': 3.0.7 + '@cspell/dict-golang': 6.0.23 + '@cspell/dict-google': 1.0.9 + '@cspell/dict-haskell': 4.0.6 + '@cspell/dict-html': 4.0.12 + '@cspell/dict-html-symbol-entities': 4.0.4 + '@cspell/dict-java': 5.0.12 + '@cspell/dict-julia': 1.1.1 + '@cspell/dict-k8s': 1.0.12 + '@cspell/dict-kotlin': 1.1.1 + '@cspell/dict-latex': 4.0.4 + '@cspell/dict-lorem-ipsum': 4.0.5 + '@cspell/dict-lua': 4.0.8 + '@cspell/dict-makefile': 1.0.5 + '@cspell/dict-markdown': 2.0.12(@cspell/dict-css@4.0.18)(@cspell/dict-html-symbol-entities@4.0.4)(@cspell/dict-html@4.0.12)(@cspell/dict-typescript@3.2.3) + '@cspell/dict-monkeyc': 1.0.11 + '@cspell/dict-node': 5.0.8 + '@cspell/dict-npm': 5.2.12 + '@cspell/dict-php': 4.0.15 + '@cspell/dict-powershell': 5.0.15 + '@cspell/dict-public-licenses': 2.0.14 + '@cspell/dict-python': 4.2.19 + '@cspell/dict-r': 2.1.1 + '@cspell/dict-ruby': 5.0.9 + '@cspell/dict-rust': 4.0.12 + '@cspell/dict-scala': 5.0.8 + '@cspell/dict-shell': 1.1.1 + '@cspell/dict-software-terms': 5.1.4 + '@cspell/dict-sql': 2.2.1 + '@cspell/dict-svelte': 1.0.7 + '@cspell/dict-swift': 2.0.6 + '@cspell/dict-terraform': 1.1.3 + '@cspell/dict-typescript': 3.2.3 + '@cspell/dict-vue': 3.0.5 '@cspell/cspell-json-reporter@8.19.4': dependencies: @@ -12395,130 +12201,130 @@ snapshots: '@cspell/cspell-types@8.19.4': {} - '@cspell/dict-ada@4.1.0': {} + '@cspell/dict-ada@4.1.1': {} - '@cspell/dict-al@1.1.0': {} + '@cspell/dict-al@1.1.1': {} - '@cspell/dict-aws@4.0.10': {} + '@cspell/dict-aws@4.0.12': {} - '@cspell/dict-bash@4.2.0': + '@cspell/dict-bash@4.2.1': dependencies: - '@cspell/dict-shell': 1.1.0 + '@cspell/dict-shell': 1.1.1 - '@cspell/dict-companies@3.2.1': {} + '@cspell/dict-companies@3.2.2': {} - '@cspell/dict-cpp@6.0.8': {} + '@cspell/dict-cpp@6.0.9': {} - '@cspell/dict-cryptocurrencies@5.0.4': {} + '@cspell/dict-cryptocurrencies@5.0.5': {} - '@cspell/dict-csharp@4.0.6': {} + '@cspell/dict-csharp@4.0.7': {} - '@cspell/dict-css@4.0.17': {} + '@cspell/dict-css@4.0.18': {} - '@cspell/dict-dart@2.3.0': {} + '@cspell/dict-dart@2.3.1': {} - '@cspell/dict-data-science@2.0.8': {} + '@cspell/dict-data-science@2.0.9': {} - '@cspell/dict-django@4.1.4': {} + '@cspell/dict-django@4.1.5': {} - '@cspell/dict-docker@1.1.14': {} + '@cspell/dict-docker@1.1.15': {} - '@cspell/dict-dotnet@5.0.9': {} + '@cspell/dict-dotnet@5.0.10': {} - '@cspell/dict-elixir@4.0.7': {} + '@cspell/dict-elixir@4.0.8': {} - '@cspell/dict-en-common-misspellings@2.0.11': {} + '@cspell/dict-en-common-misspellings@2.1.3': {} '@cspell/dict-en-gb@1.1.33': {} - '@cspell/dict-en_us@4.4.9': {} + '@cspell/dict-en_us@4.4.15': {} - '@cspell/dict-filetypes@3.0.12': {} + '@cspell/dict-filetypes@3.0.13': {} - '@cspell/dict-flutter@1.1.0': {} + '@cspell/dict-flutter@1.1.1': {} - '@cspell/dict-fonts@4.0.4': {} + '@cspell/dict-fonts@4.0.5': {} - '@cspell/dict-fsharp@1.1.0': {} + '@cspell/dict-fsharp@1.1.1': {} - '@cspell/dict-fullstack@3.2.6': {} + '@cspell/dict-fullstack@3.2.7': {} - '@cspell/dict-gaming-terms@1.1.1': {} + '@cspell/dict-gaming-terms@1.1.2': {} - '@cspell/dict-git@3.0.5': {} + '@cspell/dict-git@3.0.7': {} - '@cspell/dict-golang@6.0.21': {} + '@cspell/dict-golang@6.0.23': {} - '@cspell/dict-google@1.0.8': {} + '@cspell/dict-google@1.0.9': {} - '@cspell/dict-haskell@4.0.5': {} + '@cspell/dict-haskell@4.0.6': {} - '@cspell/dict-html-symbol-entities@4.0.3': {} + '@cspell/dict-html-symbol-entities@4.0.4': {} - '@cspell/dict-html@4.0.11': {} + '@cspell/dict-html@4.0.12': {} - '@cspell/dict-java@5.0.11': {} + '@cspell/dict-java@5.0.12': {} - '@cspell/dict-julia@1.1.0': {} + '@cspell/dict-julia@1.1.1': {} - '@cspell/dict-k8s@1.0.10': {} + '@cspell/dict-k8s@1.0.12': {} - '@cspell/dict-kotlin@1.1.0': {} + '@cspell/dict-kotlin@1.1.1': {} - '@cspell/dict-latex@4.0.3': {} + '@cspell/dict-latex@4.0.4': {} - '@cspell/dict-lorem-ipsum@4.0.4': {} + '@cspell/dict-lorem-ipsum@4.0.5': {} - '@cspell/dict-lua@4.0.7': {} + '@cspell/dict-lua@4.0.8': {} - '@cspell/dict-makefile@1.0.4': {} + '@cspell/dict-makefile@1.0.5': {} - '@cspell/dict-markdown@2.0.10(@cspell/dict-css@4.0.17)(@cspell/dict-html-symbol-entities@4.0.3)(@cspell/dict-html@4.0.11)(@cspell/dict-typescript@3.2.2)': + '@cspell/dict-markdown@2.0.12(@cspell/dict-css@4.0.18)(@cspell/dict-html-symbol-entities@4.0.4)(@cspell/dict-html@4.0.12)(@cspell/dict-typescript@3.2.3)': dependencies: - '@cspell/dict-css': 4.0.17 - '@cspell/dict-html': 4.0.11 - '@cspell/dict-html-symbol-entities': 4.0.3 - '@cspell/dict-typescript': 3.2.2 + '@cspell/dict-css': 4.0.18 + '@cspell/dict-html': 4.0.12 + '@cspell/dict-html-symbol-entities': 4.0.4 + '@cspell/dict-typescript': 3.2.3 - '@cspell/dict-monkeyc@1.0.10': {} + '@cspell/dict-monkeyc@1.0.11': {} - '@cspell/dict-node@5.0.7': {} + '@cspell/dict-node@5.0.8': {} - '@cspell/dict-npm@5.2.4': {} + '@cspell/dict-npm@5.2.12': {} - '@cspell/dict-php@4.0.14': {} + '@cspell/dict-php@4.0.15': {} - '@cspell/dict-powershell@5.0.14': {} + '@cspell/dict-powershell@5.0.15': {} - '@cspell/dict-public-licenses@2.0.13': {} + '@cspell/dict-public-licenses@2.0.14': {} - '@cspell/dict-python@4.2.18': + '@cspell/dict-python@4.2.19': dependencies: - '@cspell/dict-data-science': 2.0.8 + '@cspell/dict-data-science': 2.0.9 - '@cspell/dict-r@2.1.0': {} + '@cspell/dict-r@2.1.1': {} - '@cspell/dict-ruby@5.0.8': {} + '@cspell/dict-ruby@5.0.9': {} - '@cspell/dict-rust@4.0.11': {} + '@cspell/dict-rust@4.0.12': {} - '@cspell/dict-scala@5.0.7': {} + '@cspell/dict-scala@5.0.8': {} - '@cspell/dict-shell@1.1.0': {} + '@cspell/dict-shell@1.1.1': {} - '@cspell/dict-software-terms@5.0.10': {} + '@cspell/dict-software-terms@5.1.4': {} - '@cspell/dict-sql@2.2.0': {} + '@cspell/dict-sql@2.2.1': {} - '@cspell/dict-svelte@1.0.6': {} + '@cspell/dict-svelte@1.0.7': {} - '@cspell/dict-swift@2.0.5': {} + '@cspell/dict-swift@2.0.6': {} - '@cspell/dict-terraform@1.1.1': {} + '@cspell/dict-terraform@1.1.3': {} - '@cspell/dict-typescript@3.2.2': {} + '@cspell/dict-typescript@3.2.3': {} - '@cspell/dict-vue@3.0.4': {} + '@cspell/dict-vue@3.0.5': {} '@cspell/dynamic-import@8.19.4': dependencies: @@ -12587,18 +12393,18 @@ snapshots: react: 19.1.0 tslib: 2.8.1 - '@emnapi/core@1.4.3': + '@emnapi/core@1.4.4': dependencies: - '@emnapi/wasi-threads': 1.0.2 + '@emnapi/wasi-threads': 1.0.3 tslib: 2.8.1 optional: true - '@emnapi/runtime@1.4.3': + '@emnapi/runtime@1.4.4': dependencies: tslib: 2.8.1 optional: true - '@emnapi/wasi-threads@1.0.2': + '@emnapi/wasi-threads@1.0.3': dependencies: tslib: 2.8.1 optional: true @@ -12607,7 +12413,7 @@ snapshots: '@emotion/unitless@0.7.5': {} - '@eslint-community/eslint-utils@4.5.1(eslint@8.57.1)': + '@eslint-community/eslint-utils@4.7.0(eslint@8.57.1)': dependencies: eslint: 8.57.1 eslint-visitor-keys: 3.4.3 @@ -12620,7 +12426,7 @@ snapshots: debug: 4.4.1 espree: 9.6.1 globals: 13.24.0 - ignore: 5.3.1 + ignore: 5.3.2 import-fresh: 3.3.1 js-yaml: 4.1.0 minimatch: 3.1.2 @@ -12636,7 +12442,7 @@ snapshots: dependencies: '@formatjs/fast-memoize': 2.2.7 '@formatjs/intl-localematcher': 0.6.1 - decimal.js: 10.5.0 + decimal.js: 10.6.0 tslib: 2.8.1 '@formatjs/fast-memoize@2.2.7': @@ -12702,14 +12508,14 @@ snapshots: '@google-cloud/compute@4.12.0(encoding@0.1.13)': dependencies: - google-gax: 4.4.1(encoding@0.1.13) + google-gax: 4.6.1(encoding@0.1.13) transitivePeerDependencies: - encoding - supports-color '@google-cloud/monitoring@4.1.0(encoding@0.1.13)': dependencies: - google-gax: 4.4.1(encoding@0.1.13) + google-gax: 4.6.1(encoding@0.1.13) transitivePeerDependencies: - encoding - supports-color @@ -12727,7 +12533,7 @@ snapshots: '@google-cloud/storage-transfer@3.6.1(encoding@0.1.13)': dependencies: - google-gax: 4.4.1(encoding@0.1.13) + google-gax: 4.6.1(encoding@0.1.13) transitivePeerDependencies: - encoding - supports-color @@ -12755,18 +12561,18 @@ snapshots: '@google/generative-ai@0.14.1': {} - '@google/generative-ai@0.24.0': {} + '@google/generative-ai@0.24.1': {} - '@grpc/grpc-js@1.13.3': + '@grpc/grpc-js@1.13.4': dependencies: - '@grpc/proto-loader': 0.7.13 + '@grpc/proto-loader': 0.7.15 '@js-sdsl/ordered-map': 4.4.2 - '@grpc/proto-loader@0.7.13': + '@grpc/proto-loader@0.7.15': dependencies: lodash.camelcase: 4.3.0 - long: 5.3.1 - protobufjs: 7.4.0 + long: 5.3.2 + protobufjs: 7.5.3 yargs: 17.7.2 '@humanwhocodes/config-array@0.13.0': @@ -12800,85 +12606,90 @@ snapshots: dependencies: react: 19.1.0 - '@img/sharp-darwin-arm64@0.34.2': + '@img/sharp-darwin-arm64@0.34.3': optionalDependencies: - '@img/sharp-libvips-darwin-arm64': 1.1.0 + '@img/sharp-libvips-darwin-arm64': 1.2.0 optional: true - '@img/sharp-darwin-x64@0.34.2': + '@img/sharp-darwin-x64@0.34.3': optionalDependencies: - '@img/sharp-libvips-darwin-x64': 1.1.0 + '@img/sharp-libvips-darwin-x64': 1.2.0 optional: true - '@img/sharp-libvips-darwin-arm64@1.1.0': + '@img/sharp-libvips-darwin-arm64@1.2.0': optional: true - '@img/sharp-libvips-darwin-x64@1.1.0': + '@img/sharp-libvips-darwin-x64@1.2.0': optional: true - '@img/sharp-libvips-linux-arm64@1.1.0': + '@img/sharp-libvips-linux-arm64@1.2.0': optional: true - '@img/sharp-libvips-linux-arm@1.1.0': + '@img/sharp-libvips-linux-arm@1.2.0': optional: true - '@img/sharp-libvips-linux-ppc64@1.1.0': + '@img/sharp-libvips-linux-ppc64@1.2.0': optional: true - '@img/sharp-libvips-linux-s390x@1.1.0': + '@img/sharp-libvips-linux-s390x@1.2.0': optional: true - '@img/sharp-libvips-linux-x64@1.1.0': + '@img/sharp-libvips-linux-x64@1.2.0': optional: true - '@img/sharp-libvips-linuxmusl-arm64@1.1.0': + '@img/sharp-libvips-linuxmusl-arm64@1.2.0': optional: true - '@img/sharp-libvips-linuxmusl-x64@1.1.0': + '@img/sharp-libvips-linuxmusl-x64@1.2.0': optional: true - '@img/sharp-linux-arm64@0.34.2': + '@img/sharp-linux-arm64@0.34.3': optionalDependencies: - '@img/sharp-libvips-linux-arm64': 1.1.0 + '@img/sharp-libvips-linux-arm64': 1.2.0 optional: true - '@img/sharp-linux-arm@0.34.2': + '@img/sharp-linux-arm@0.34.3': optionalDependencies: - '@img/sharp-libvips-linux-arm': 1.1.0 + '@img/sharp-libvips-linux-arm': 1.2.0 optional: true - '@img/sharp-linux-s390x@0.34.2': + '@img/sharp-linux-ppc64@0.34.3': optionalDependencies: - '@img/sharp-libvips-linux-s390x': 1.1.0 + '@img/sharp-libvips-linux-ppc64': 1.2.0 optional: true - '@img/sharp-linux-x64@0.34.2': + '@img/sharp-linux-s390x@0.34.3': optionalDependencies: - '@img/sharp-libvips-linux-x64': 1.1.0 + '@img/sharp-libvips-linux-s390x': 1.2.0 optional: true - '@img/sharp-linuxmusl-arm64@0.34.2': + '@img/sharp-linux-x64@0.34.3': optionalDependencies: - '@img/sharp-libvips-linuxmusl-arm64': 1.1.0 + '@img/sharp-libvips-linux-x64': 1.2.0 optional: true - '@img/sharp-linuxmusl-x64@0.34.2': + '@img/sharp-linuxmusl-arm64@0.34.3': optionalDependencies: - '@img/sharp-libvips-linuxmusl-x64': 1.1.0 + '@img/sharp-libvips-linuxmusl-arm64': 1.2.0 optional: true - '@img/sharp-wasm32@0.34.2': + '@img/sharp-linuxmusl-x64@0.34.3': + optionalDependencies: + '@img/sharp-libvips-linuxmusl-x64': 1.2.0 + optional: true + + '@img/sharp-wasm32@0.34.3': dependencies: - '@emnapi/runtime': 1.4.3 + '@emnapi/runtime': 1.4.4 optional: true - '@img/sharp-win32-arm64@0.34.2': + '@img/sharp-win32-arm64@0.34.3': optional: true - '@img/sharp-win32-ia32@0.34.2': + '@img/sharp-win32-ia32@0.34.3': optional: true - '@img/sharp-win32-x64@0.34.2': + '@img/sharp-win32-x64@0.34.3': optional: true '@isaacs/balanced-match@4.0.1': {} @@ -12911,7 +12722,7 @@ snapshots: '@jest/console@29.7.0': dependencies: '@jest/types': 29.6.3 - '@types/node': 24.0.10 + '@types/node': 18.19.118 chalk: 4.1.2 jest-message-util: 29.7.0 jest-util: 29.7.0 @@ -12924,14 +12735,14 @@ snapshots: '@jest/test-result': 29.7.0 '@jest/transform': 29.7.0 '@jest/types': 29.6.3 - '@types/node': 24.0.10 + '@types/node': 18.19.118 ansi-escapes: 4.3.2 chalk: 4.1.2 ci-info: 3.9.0 exit: 0.1.2 graceful-fs: 4.2.11 jest-changed-files: 29.7.0 - jest-config: 29.7.0(@types/node@24.0.10) + jest-config: 29.7.0(@types/node@18.19.118) jest-haste-map: 29.7.0 jest-message-util: 29.7.0 jest-regex-util: 29.6.3 @@ -12958,7 +12769,7 @@ snapshots: '@jest/fake-timers': 30.0.4 '@jest/types': 30.0.1 '@types/jsdom': 21.1.7 - '@types/node': 24.0.10 + '@types/node': 18.19.118 jest-mock: 30.0.2 jest-util: 30.0.2 jsdom: 26.1.0 @@ -12967,14 +12778,14 @@ snapshots: dependencies: '@jest/fake-timers': 29.7.0 '@jest/types': 29.6.3 - '@types/node': 24.0.10 + '@types/node': 18.19.118 jest-mock: 29.7.0 '@jest/environment@30.0.4': dependencies: '@jest/fake-timers': 30.0.4 '@jest/types': 30.0.1 - '@types/node': 24.0.10 + '@types/node': 18.19.118 jest-mock: 30.0.2 '@jest/expect-utils@29.7.0': @@ -12992,7 +12803,7 @@ snapshots: dependencies: '@jest/types': 29.6.3 '@sinonjs/fake-timers': 10.3.0 - '@types/node': 24.0.10 + '@types/node': 18.19.118 jest-message-util: 29.7.0 jest-mock: 29.7.0 jest-util: 29.7.0 @@ -13001,7 +12812,7 @@ snapshots: dependencies: '@jest/types': 30.0.1 '@sinonjs/fake-timers': 13.0.5 - '@types/node': 24.0.10 + '@types/node': 18.19.118 jest-message-util: 30.0.2 jest-mock: 30.0.2 jest-util: 30.0.2 @@ -13017,7 +12828,7 @@ snapshots: '@jest/pattern@30.0.1': dependencies: - '@types/node': 24.0.10 + '@types/node': 18.19.118 jest-regex-util: 30.0.1 '@jest/reporters@29.7.0': @@ -13027,8 +12838,8 @@ snapshots: '@jest/test-result': 29.7.0 '@jest/transform': 29.7.0 '@jest/types': 29.6.3 - '@jridgewell/trace-mapping': 0.3.25 - '@types/node': 24.0.10 + '@jridgewell/trace-mapping': 0.3.29 + '@types/node': 18.19.118 chalk: 4.1.2 collect-v8-coverage: 1.0.2 exit: 0.1.2 @@ -13045,7 +12856,7 @@ snapshots: slash: 3.0.0 string-length: 4.0.2 strip-ansi: 6.0.1 - v8-to-istanbul: 9.2.0 + v8-to-istanbul: 9.3.0 transitivePeerDependencies: - supports-color @@ -13059,7 +12870,7 @@ snapshots: '@jest/source-map@29.6.3': dependencies: - '@jridgewell/trace-mapping': 0.3.25 + '@jridgewell/trace-mapping': 0.3.29 callsites: 3.1.0 graceful-fs: 4.2.11 @@ -13079,9 +12890,9 @@ snapshots: '@jest/transform@29.7.0': dependencies: - '@babel/core': 7.26.9 + '@babel/core': 7.28.0 '@jest/types': 29.6.3 - '@jridgewell/trace-mapping': 0.3.25 + '@jridgewell/trace-mapping': 0.3.29 babel-plugin-istanbul: 6.1.1 chalk: 4.1.2 convert-source-map: 2.0.0 @@ -13091,7 +12902,7 @@ snapshots: jest-regex-util: 29.6.3 jest-util: 29.7.0 micromatch: 4.0.8 - pirates: 4.0.6 + pirates: 4.0.7 slash: 3.0.0 write-file-atomic: 4.0.2 transitivePeerDependencies: @@ -13100,8 +12911,8 @@ snapshots: '@jest/types@26.6.2': dependencies: '@types/istanbul-lib-coverage': 2.0.6 - '@types/istanbul-reports': 3.0.1 - '@types/node': 18.19.115 + '@types/istanbul-reports': 3.0.4 + '@types/node': 18.19.118 '@types/yargs': 15.0.19 chalk: 4.1.2 @@ -13110,7 +12921,7 @@ snapshots: '@jest/schemas': 29.6.3 '@types/istanbul-lib-coverage': 2.0.6 '@types/istanbul-reports': 3.0.4 - '@types/node': 24.0.10 + '@types/node': 18.19.118 '@types/yargs': 17.0.33 chalk: 4.1.2 @@ -13120,7 +12931,7 @@ snapshots: '@jest/schemas': 30.0.1 '@types/istanbul-lib-coverage': 2.0.6 '@types/istanbul-reports': 3.0.4 - '@types/node': 24.0.10 + '@types/node': 18.19.118 '@types/yargs': 17.0.33 chalk: 4.1.2 @@ -13129,35 +12940,15 @@ snapshots: '@jridgewell/sourcemap-codec': 1.5.4 '@jridgewell/trace-mapping': 0.3.29 - '@jridgewell/gen-mapping@0.3.8': - dependencies: - '@jridgewell/set-array': 1.2.1 - '@jridgewell/sourcemap-codec': 1.5.0 - '@jridgewell/trace-mapping': 0.3.25 - '@jridgewell/resolve-uri@3.1.2': {} - '@jridgewell/set-array@1.2.1': {} - '@jridgewell/source-map@0.3.10': dependencies: '@jridgewell/gen-mapping': 0.3.12 '@jridgewell/trace-mapping': 0.3.29 - '@jridgewell/source-map@0.3.6': - dependencies: - '@jridgewell/gen-mapping': 0.3.8 - '@jridgewell/trace-mapping': 0.3.25 - - '@jridgewell/sourcemap-codec@1.5.0': {} - '@jridgewell/sourcemap-codec@1.5.4': {} - '@jridgewell/trace-mapping@0.3.25': - dependencies: - '@jridgewell/resolve-uri': 3.1.2 - '@jridgewell/sourcemap-codec': 1.5.0 - '@jridgewell/trace-mapping@0.3.29': dependencies: '@jridgewell/resolve-uri': 3.1.2 @@ -13172,23 +12963,23 @@ snapshots: '@jsonjoy.com/json-pack@1.2.0(tslib@2.8.1)': dependencies: '@jsonjoy.com/base64': 1.1.2(tslib@2.8.1) - '@jsonjoy.com/util': 1.5.0(tslib@2.8.1) + '@jsonjoy.com/util': 1.6.0(tslib@2.8.1) hyperdyperid: 1.2.0 thingies: 1.21.0(tslib@2.8.1) tslib: 2.8.1 - '@jsonjoy.com/util@1.5.0(tslib@2.8.1)': + '@jsonjoy.com/util@1.6.0(tslib@2.8.1)': dependencies: tslib: 2.8.1 '@juggle/resize-observer@3.4.0': {} - '@jupyter-widgets/base@4.1.7(crypto@1.0.1)(react@19.1.0)': + '@jupyter-widgets/base@4.1.7(react@19.1.0)': dependencies: - '@jupyterlab/services': 7.4.0(react@19.1.0) + '@jupyterlab/services': 7.4.4(react@19.1.0) '@lumino/coreutils': 2.2.1 '@lumino/messaging': 2.0.3 - '@lumino/widgets': 1.37.2(crypto@1.0.1) + '@lumino/widgets': 2.7.1 '@types/backbone': 1.4.23 '@types/lodash': 4.17.20 backbone: 1.2.3 @@ -13197,13 +12988,12 @@ snapshots: lodash: 4.17.21 transitivePeerDependencies: - bufferutil - - crypto - react - utf-8-validate '@jupyter-widgets/base@6.0.11(crypto@1.0.1)(react@19.1.0)': dependencies: - '@jupyterlab/services': 7.4.0(react@19.1.0) + '@jupyterlab/services': 7.4.4(react@19.1.0) '@lumino/coreutils': 2.2.1 '@lumino/messaging': 1.10.3 '@lumino/widgets': 1.37.2(crypto@1.0.1) @@ -13236,25 +13026,24 @@ snapshots: - react - utf-8-validate - '@jupyter-widgets/output@4.1.7(crypto@1.0.1)(react@19.1.0)': + '@jupyter-widgets/output@4.1.7(react@19.1.0)': dependencies: - '@jupyter-widgets/base': 4.1.7(crypto@1.0.1)(react@19.1.0) + '@jupyter-widgets/base': 4.1.7(react@19.1.0) transitivePeerDependencies: - bufferutil - - crypto - react - utf-8-validate - '@jupyter/ydoc@3.0.3': + '@jupyter/ydoc@3.1.0': dependencies: - '@jupyterlab/nbformat': 4.4.0 + '@jupyterlab/nbformat': 4.4.4 '@lumino/coreutils': 2.2.1 '@lumino/disposable': 2.1.4 '@lumino/signaling': 2.1.4 - y-protocols: 1.0.6(yjs@13.6.24) - yjs: 13.6.24 + y-protocols: 1.0.6(yjs@13.6.27) + yjs: 13.6.27 - '@jupyterlab/coreutils@6.4.0': + '@jupyterlab/coreutils@6.4.4': dependencies: '@lumino/coreutils': 2.2.1 '@lumino/disposable': 2.1.4 @@ -13263,17 +13052,17 @@ snapshots: path-browserify: 1.0.1 url-parse: 1.5.10 - '@jupyterlab/nbformat@4.4.0': + '@jupyterlab/nbformat@4.4.4': dependencies: '@lumino/coreutils': 2.2.1 - '@jupyterlab/services@7.4.0(react@19.1.0)': + '@jupyterlab/services@7.4.4(react@19.1.0)': dependencies: - '@jupyter/ydoc': 3.0.3 - '@jupyterlab/coreutils': 6.4.0 - '@jupyterlab/nbformat': 4.4.0 - '@jupyterlab/settingregistry': 4.4.0(react@19.1.0) - '@jupyterlab/statedb': 4.4.0 + '@jupyter/ydoc': 3.1.0 + '@jupyterlab/coreutils': 6.4.4 + '@jupyterlab/nbformat': 4.4.4 + '@jupyterlab/settingregistry': 4.4.4(react@19.1.0) + '@jupyterlab/statedb': 4.4.4 '@lumino/coreutils': 2.2.1 '@lumino/disposable': 2.1.4 '@lumino/polling': 2.1.4 @@ -13285,20 +13074,20 @@ snapshots: - react - utf-8-validate - '@jupyterlab/settingregistry@4.4.0(react@19.1.0)': + '@jupyterlab/settingregistry@4.4.4(react@19.1.0)': dependencies: - '@jupyterlab/nbformat': 4.4.0 - '@jupyterlab/statedb': 4.4.0 + '@jupyterlab/nbformat': 4.4.4 + '@jupyterlab/statedb': 4.4.4 '@lumino/commands': 2.3.2 '@lumino/coreutils': 2.2.1 '@lumino/disposable': 2.1.4 '@lumino/signaling': 2.1.4 - '@rjsf/utils': 5.24.8(react@19.1.0) + '@rjsf/utils': 5.24.12(react@19.1.0) ajv: 8.17.1 json5: 2.2.3 react: 19.1.0 - '@jupyterlab/statedb@4.4.0': + '@jupyterlab/statedb@4.4.4': dependencies: '@lumino/commands': 2.3.2 '@lumino/coreutils': 2.2.1 @@ -13306,55 +13095,58 @@ snapshots: '@lumino/properties': 2.0.3 '@lumino/signaling': 2.1.4 - '@langchain/anthropic@0.3.24(@langchain/core@0.3.62(openai@4.104.0(encoding@0.1.13)(ws@8.18.3)(zod@3.25.67)))': + '@langchain/anthropic@0.3.24(@langchain/core@0.3.62(@opentelemetry/api@1.9.0)(openai@4.104.0(encoding@0.1.13)(ws@8.18.3)(zod@3.25.76)))': dependencies: '@anthropic-ai/sdk': 0.56.0 - '@langchain/core': 0.3.62(openai@4.104.0(encoding@0.1.13)(ws@8.18.3)(zod@3.25.67)) + '@langchain/core': 0.3.62(@opentelemetry/api@1.9.0)(openai@4.104.0(encoding@0.1.13)(ws@8.18.3)(zod@3.25.76)) fast-xml-parser: 4.5.3 - '@langchain/core@0.3.62(openai@4.104.0(encoding@0.1.13)(ws@8.18.3)(zod@3.25.67))': + '@langchain/core@0.3.62(@opentelemetry/api@1.9.0)(openai@4.104.0(encoding@0.1.13)(ws@8.18.3)(zod@3.25.76))': dependencies: '@cfworker/json-schema': 4.1.1 ansi-styles: 5.2.0 camelcase: 6.3.0 decamelize: 1.2.0 - js-tiktoken: 1.0.19 - langsmith: 0.3.33(openai@4.104.0(encoding@0.1.13)(ws@8.18.3)(zod@3.25.67)) + js-tiktoken: 1.0.20 + langsmith: 0.3.44(@opentelemetry/api@1.9.0)(openai@4.104.0(encoding@0.1.13)(ws@8.18.3)(zod@3.25.76)) mustache: 4.2.0 p-queue: 6.6.2 p-retry: 4.6.2 uuid: 10.0.0 - zod: 3.25.67 - zod-to-json-schema: 3.24.5(zod@3.25.67) + zod: 3.25.76 + zod-to-json-schema: 3.24.6(zod@3.25.76) transitivePeerDependencies: + - '@opentelemetry/api' + - '@opentelemetry/exporter-trace-otlp-proto' + - '@opentelemetry/sdk-trace-base' - openai - '@langchain/google-genai@0.2.14(@langchain/core@0.3.62(openai@4.104.0(encoding@0.1.13)(ws@8.18.3)(zod@3.25.67)))': + '@langchain/google-genai@0.2.14(@langchain/core@0.3.62(@opentelemetry/api@1.9.0)(openai@4.104.0(encoding@0.1.13)(ws@8.18.3)(zod@3.25.76)))': dependencies: - '@google/generative-ai': 0.24.0 - '@langchain/core': 0.3.62(openai@4.104.0(encoding@0.1.13)(ws@8.18.3)(zod@3.25.67)) + '@google/generative-ai': 0.24.1 + '@langchain/core': 0.3.62(@opentelemetry/api@1.9.0)(openai@4.104.0(encoding@0.1.13)(ws@8.18.3)(zod@3.25.76)) uuid: 11.1.0 - '@langchain/mistralai@0.2.1(@langchain/core@0.3.62(openai@4.104.0(encoding@0.1.13)(ws@8.18.3)(zod@3.25.67)))(zod@3.25.67)': + '@langchain/mistralai@0.2.1(@langchain/core@0.3.62(@opentelemetry/api@1.9.0)(openai@4.104.0(encoding@0.1.13)(ws@8.18.3)(zod@3.25.76)))(zod@3.25.76)': dependencies: - '@langchain/core': 0.3.62(openai@4.104.0(encoding@0.1.13)(ws@8.18.3)(zod@3.25.67)) - '@mistralai/mistralai': 1.5.2(zod@3.25.67) + '@langchain/core': 0.3.62(@opentelemetry/api@1.9.0)(openai@4.104.0(encoding@0.1.13)(ws@8.18.3)(zod@3.25.76)) + '@mistralai/mistralai': 1.7.4(zod@3.25.76) uuid: 10.0.0 transitivePeerDependencies: - zod - '@langchain/ollama@0.2.3(@langchain/core@0.3.62(openai@4.104.0(encoding@0.1.13)(ws@8.18.3)(zod@3.25.67)))': + '@langchain/ollama@0.2.3(@langchain/core@0.3.62(@opentelemetry/api@1.9.0)(openai@4.104.0(encoding@0.1.13)(ws@8.18.3)(zod@3.25.76)))': dependencies: - '@langchain/core': 0.3.62(openai@4.104.0(encoding@0.1.13)(ws@8.18.3)(zod@3.25.67)) - ollama: 0.5.15 + '@langchain/core': 0.3.62(@opentelemetry/api@1.9.0)(openai@4.104.0(encoding@0.1.13)(ws@8.18.3)(zod@3.25.76)) + ollama: 0.5.16 uuid: 10.0.0 - '@langchain/openai@0.5.18(@langchain/core@0.3.62(openai@4.104.0(encoding@0.1.13)(ws@8.18.3)(zod@3.25.67)))(ws@8.18.3)': + '@langchain/openai@0.5.18(@langchain/core@0.3.62(@opentelemetry/api@1.9.0)(openai@4.104.0(encoding@0.1.13)(ws@8.18.3)(zod@3.25.76)))(ws@8.18.3)': dependencies: - '@langchain/core': 0.3.62(openai@4.104.0(encoding@0.1.13)(ws@8.18.3)(zod@3.25.67)) - js-tiktoken: 1.0.19 - openai: 5.7.0(ws@8.18.3)(zod@3.25.67) - zod: 3.25.67 + '@langchain/core': 0.3.62(@opentelemetry/api@1.9.0)(openai@4.104.0(encoding@0.1.13)(ws@8.18.3)(zod@3.25.76)) + js-tiktoken: 1.0.20 + openai: 5.9.0(ws@8.18.3)(zod@3.25.76) + zod: 3.25.76 transitivePeerDependencies: - ws @@ -13424,6 +13216,11 @@ snapshots: transitivePeerDependencies: - crypto + '@lumino/dragdrop@2.1.6': + dependencies: + '@lumino/coreutils': 2.2.1 + '@lumino/disposable': 2.1.4 + '@lumino/keyboard@1.8.2': {} '@lumino/keyboard@2.0.3': {} @@ -13482,6 +13279,20 @@ snapshots: transitivePeerDependencies: - crypto + '@lumino/widgets@2.7.1': + dependencies: + '@lumino/algorithm': 2.0.3 + '@lumino/commands': 2.3.2 + '@lumino/coreutils': 2.2.1 + '@lumino/disposable': 2.1.4 + '@lumino/domutils': 2.0.3 + '@lumino/dragdrop': 2.1.6 + '@lumino/keyboard': 2.0.3 + '@lumino/messaging': 2.0.3 + '@lumino/properties': 2.0.3 + '@lumino/signaling': 2.1.4 + '@lumino/virtualdom': 2.0.3 + '@lydell/node-pty-darwin-arm64@1.1.0': optional: true @@ -13562,10 +13373,10 @@ snapshots: transitivePeerDependencies: - '@types/react' - '@mistralai/mistralai@1.5.2(zod@3.25.67)': + '@mistralai/mistralai@1.7.4(zod@3.25.76)': dependencies: - zod: 3.25.67 - zod-to-json-schema: 3.24.5(zod@3.25.67) + zod: 3.25.76 + zod-to-json-schema: 3.24.6(zod@3.25.76) '@module-federation/error-codes@0.11.2': {} @@ -13619,57 +13430,57 @@ snapshots: '@msgpack/msgpack@3.1.2': {} - '@napi-rs/canvas-android-arm64@0.1.69': + '@napi-rs/canvas-android-arm64@0.1.73': optional: true - '@napi-rs/canvas-darwin-arm64@0.1.69': + '@napi-rs/canvas-darwin-arm64@0.1.73': optional: true - '@napi-rs/canvas-darwin-x64@0.1.69': + '@napi-rs/canvas-darwin-x64@0.1.73': optional: true - '@napi-rs/canvas-linux-arm-gnueabihf@0.1.69': + '@napi-rs/canvas-linux-arm-gnueabihf@0.1.73': optional: true - '@napi-rs/canvas-linux-arm64-gnu@0.1.69': + '@napi-rs/canvas-linux-arm64-gnu@0.1.73': optional: true - '@napi-rs/canvas-linux-arm64-musl@0.1.69': + '@napi-rs/canvas-linux-arm64-musl@0.1.73': optional: true - '@napi-rs/canvas-linux-riscv64-gnu@0.1.69': + '@napi-rs/canvas-linux-riscv64-gnu@0.1.73': optional: true - '@napi-rs/canvas-linux-x64-gnu@0.1.69': + '@napi-rs/canvas-linux-x64-gnu@0.1.73': optional: true - '@napi-rs/canvas-linux-x64-musl@0.1.69': + '@napi-rs/canvas-linux-x64-musl@0.1.73': optional: true - '@napi-rs/canvas-win32-x64-msvc@0.1.69': + '@napi-rs/canvas-win32-x64-msvc@0.1.73': optional: true - '@napi-rs/canvas@0.1.69': + '@napi-rs/canvas@0.1.73': optionalDependencies: - '@napi-rs/canvas-android-arm64': 0.1.69 - '@napi-rs/canvas-darwin-arm64': 0.1.69 - '@napi-rs/canvas-darwin-x64': 0.1.69 - '@napi-rs/canvas-linux-arm-gnueabihf': 0.1.69 - '@napi-rs/canvas-linux-arm64-gnu': 0.1.69 - '@napi-rs/canvas-linux-arm64-musl': 0.1.69 - '@napi-rs/canvas-linux-riscv64-gnu': 0.1.69 - '@napi-rs/canvas-linux-x64-gnu': 0.1.69 - '@napi-rs/canvas-linux-x64-musl': 0.1.69 - '@napi-rs/canvas-win32-x64-msvc': 0.1.69 + '@napi-rs/canvas-android-arm64': 0.1.73 + '@napi-rs/canvas-darwin-arm64': 0.1.73 + '@napi-rs/canvas-darwin-x64': 0.1.73 + '@napi-rs/canvas-linux-arm-gnueabihf': 0.1.73 + '@napi-rs/canvas-linux-arm64-gnu': 0.1.73 + '@napi-rs/canvas-linux-arm64-musl': 0.1.73 + '@napi-rs/canvas-linux-riscv64-gnu': 0.1.73 + '@napi-rs/canvas-linux-x64-gnu': 0.1.73 + '@napi-rs/canvas-linux-x64-musl': 0.1.73 + '@napi-rs/canvas-win32-x64-msvc': 0.1.73 optional: true '@napi-rs/triples@1.2.0': {} - '@napi-rs/wasm-runtime@0.2.11': + '@napi-rs/wasm-runtime@0.2.12': dependencies: - '@emnapi/core': 1.4.3 - '@emnapi/runtime': 1.4.3 - '@tybys/wasm-util': 0.9.0 + '@emnapi/core': 1.4.4 + '@emnapi/runtime': 1.4.4 + '@tybys/wasm-util': 0.10.0 optional: true '@nestjs/axios@4.0.0(@nestjs/common@11.1.3(reflect-metadata@0.2.2)(rxjs@7.8.2))(axios@1.10.0)(rxjs@7.8.2)': @@ -13737,13 +13548,13 @@ snapshots: '@node-saml/node-saml@5.0.1': dependencies: '@types/debug': 4.1.12 - '@types/qs': 6.9.17 + '@types/qs': 6.14.0 '@types/xml-encryption': 1.2.4 '@types/xml2js': 0.4.14 '@xmldom/is-dom-node': 1.0.1 '@xmldom/xmldom': 0.8.10 debug: 4.4.1 - xml-crypto: 6.1.0 + xml-crypto: 6.1.2 xml-encryption: 3.1.0 xml2js: 0.6.2 xmlbuilder: 15.1.1 @@ -13772,9 +13583,9 @@ snapshots: '@nodelib/fs.walk@1.2.8': dependencies: '@nodelib/fs.scandir': 2.1.5 - fastq: 1.13.0 + fastq: 1.19.1 - '@nteract/commutable@7.5.0': + '@nteract/commutable@7.5.1': dependencies: immutable: 4.3.7 uuid: 8.3.2 @@ -13782,7 +13593,7 @@ snapshots: '@nteract/types@7.1.9': dependencies: - '@nteract/commutable': 7.5.0 + '@nteract/commutable': 7.5.1 immutable: 4.3.7 rxjs: 6.6.7 uuid: 8.3.2 @@ -13796,7 +13607,7 @@ snapshots: dependencies: chalk: 4.1.2 consola: 2.15.3 - node-fetch: 2.6.7(encoding@0.1.13) + node-fetch: 2.7.0(encoding@0.1.13) transitivePeerDependencies: - encoding @@ -13948,11 +13759,6 @@ snapshots: '@pkgr/core@0.2.7': {} - '@playwright/test@1.51.1': - dependencies: - playwright: 1.51.1 - optional: true - '@plotly/d3-sankey-circular@0.33.1': dependencies: d3-array: 1.2.4 @@ -14131,7 +13937,7 @@ snapshots: '@rinsuki/lz4-ts@1.0.1': {} - '@rjsf/utils@5.24.8(react@19.1.0)': + '@rjsf/utils@5.24.12(react@19.1.0)': dependencies: json-schema-merge-allof: 0.8.1 jsonpointer: 5.0.1 @@ -14143,60 +13949,60 @@ snapshots: '@rspack/binding-darwin-arm64@1.3.4': optional: true - '@rspack/binding-darwin-arm64@1.4.4': + '@rspack/binding-darwin-arm64@1.4.6': optional: true '@rspack/binding-darwin-x64@1.3.4': optional: true - '@rspack/binding-darwin-x64@1.4.4': + '@rspack/binding-darwin-x64@1.4.6': optional: true '@rspack/binding-linux-arm64-gnu@1.3.4': optional: true - '@rspack/binding-linux-arm64-gnu@1.4.4': + '@rspack/binding-linux-arm64-gnu@1.4.6': optional: true '@rspack/binding-linux-arm64-musl@1.3.4': optional: true - '@rspack/binding-linux-arm64-musl@1.4.4': + '@rspack/binding-linux-arm64-musl@1.4.6': optional: true '@rspack/binding-linux-x64-gnu@1.3.4': optional: true - '@rspack/binding-linux-x64-gnu@1.4.4': + '@rspack/binding-linux-x64-gnu@1.4.6': optional: true '@rspack/binding-linux-x64-musl@1.3.4': optional: true - '@rspack/binding-linux-x64-musl@1.4.4': + '@rspack/binding-linux-x64-musl@1.4.6': optional: true - '@rspack/binding-wasm32-wasi@1.4.4': + '@rspack/binding-wasm32-wasi@1.4.6': dependencies: - '@napi-rs/wasm-runtime': 0.2.11 + '@napi-rs/wasm-runtime': 0.2.12 optional: true '@rspack/binding-win32-arm64-msvc@1.3.4': optional: true - '@rspack/binding-win32-arm64-msvc@1.4.4': + '@rspack/binding-win32-arm64-msvc@1.4.6': optional: true '@rspack/binding-win32-ia32-msvc@1.3.4': optional: true - '@rspack/binding-win32-ia32-msvc@1.4.4': + '@rspack/binding-win32-ia32-msvc@1.4.6': optional: true '@rspack/binding-win32-x64-msvc@1.3.4': optional: true - '@rspack/binding-win32-x64-msvc@1.4.4': + '@rspack/binding-win32-x64-msvc@1.4.6': optional: true '@rspack/binding@1.3.4': @@ -14211,24 +14017,24 @@ snapshots: '@rspack/binding-win32-ia32-msvc': 1.3.4 '@rspack/binding-win32-x64-msvc': 1.3.4 - '@rspack/binding@1.4.4': + '@rspack/binding@1.4.6': optionalDependencies: - '@rspack/binding-darwin-arm64': 1.4.4 - '@rspack/binding-darwin-x64': 1.4.4 - '@rspack/binding-linux-arm64-gnu': 1.4.4 - '@rspack/binding-linux-arm64-musl': 1.4.4 - '@rspack/binding-linux-x64-gnu': 1.4.4 - '@rspack/binding-linux-x64-musl': 1.4.4 - '@rspack/binding-wasm32-wasi': 1.4.4 - '@rspack/binding-win32-arm64-msvc': 1.4.4 - '@rspack/binding-win32-ia32-msvc': 1.4.4 - '@rspack/binding-win32-x64-msvc': 1.4.4 - - '@rspack/cli@1.4.4(@rspack/core@1.4.4(@swc/helpers@0.5.15))(@types/express@4.17.23)(webpack@5.99.5)': + '@rspack/binding-darwin-arm64': 1.4.6 + '@rspack/binding-darwin-x64': 1.4.6 + '@rspack/binding-linux-arm64-gnu': 1.4.6 + '@rspack/binding-linux-arm64-musl': 1.4.6 + '@rspack/binding-linux-x64-gnu': 1.4.6 + '@rspack/binding-linux-x64-musl': 1.4.6 + '@rspack/binding-wasm32-wasi': 1.4.6 + '@rspack/binding-win32-arm64-msvc': 1.4.6 + '@rspack/binding-win32-ia32-msvc': 1.4.6 + '@rspack/binding-win32-x64-msvc': 1.4.6 + + '@rspack/cli@1.4.6(@rspack/core@1.4.6(@swc/helpers@0.5.15))(@types/express@4.17.23)(webpack@5.100.1)': dependencies: '@discoveryjs/json-ext': 0.5.7 - '@rspack/core': 1.4.4(@swc/helpers@0.5.15) - '@rspack/dev-server': 1.1.3(@rspack/core@1.4.4(@swc/helpers@0.5.15))(@types/express@4.17.23)(webpack@5.99.5) + '@rspack/core': 1.4.6(@swc/helpers@0.5.15) + '@rspack/dev-server': 1.1.3(@rspack/core@1.4.6(@swc/helpers@0.5.15))(@types/express@4.17.23)(webpack@5.100.1) colorette: 2.0.20 exit-hook: 4.0.0 interpret: 3.1.1 @@ -14249,25 +14055,25 @@ snapshots: '@module-federation/runtime-tools': 0.11.2 '@rspack/binding': 1.3.4 '@rspack/lite-tapable': 1.0.1 - caniuse-lite: 1.0.30001726 + caniuse-lite: 1.0.30001727 optionalDependencies: '@swc/helpers': 0.5.15 - '@rspack/core@1.4.4(@swc/helpers@0.5.15)': + '@rspack/core@1.4.6(@swc/helpers@0.5.15)': dependencies: '@module-federation/runtime-tools': 0.15.0 - '@rspack/binding': 1.4.4 + '@rspack/binding': 1.4.6 '@rspack/lite-tapable': 1.0.1 optionalDependencies: '@swc/helpers': 0.5.15 - '@rspack/dev-server@1.1.3(@rspack/core@1.4.4(@swc/helpers@0.5.15))(@types/express@4.17.23)(webpack@5.99.5)': + '@rspack/dev-server@1.1.3(@rspack/core@1.4.6(@swc/helpers@0.5.15))(@types/express@4.17.23)(webpack@5.100.1)': dependencies: - '@rspack/core': 1.4.4(@swc/helpers@0.5.15) + '@rspack/core': 1.4.6(@swc/helpers@0.5.15) chokidar: 3.6.0 http-proxy-middleware: 2.0.9(@types/express@4.17.23) p-retry: 6.2.1 - webpack-dev-server: 5.2.2(webpack@5.99.5) + webpack-dev-server: 5.2.2(webpack@5.100.1) ws: 8.18.3 transitivePeerDependencies: - '@types/express' @@ -14332,17 +14138,8 @@ snapshots: dependencies: '@sinonjs/commons': 3.0.1 - '@socket.io/cluster-adapter@0.2.2(socket.io-adapter@2.5.5)': - dependencies: - debug: 4.3.7 - socket.io-adapter: 2.5.5 - transitivePeerDependencies: - - supports-color - '@socket.io/component-emitter@3.1.2': {} - '@socket.io/sticky@1.0.4': {} - '@stripe/react-stripe-js@3.7.0(@stripe/stripe-js@5.10.0)(react-dom@19.1.0(react@19.1.0))(react@19.1.0)': dependencies: '@stripe/stripe-js': 5.10.0 @@ -14393,7 +14190,7 @@ snapshots: dependencies: debug: 4.4.1 fflate: 0.8.2 - token-types: 6.0.0 + token-types: 6.0.3 transitivePeerDependencies: - supports-color @@ -14436,7 +14233,7 @@ snapshots: '@turf/helpers': 7.2.0 '@types/geojson': 7946.0.16 - '@tybys/wasm-util@0.9.0': + '@tybys/wasm-util@0.10.0': dependencies: tslib: 2.8.1 optional: true @@ -14447,24 +14244,24 @@ snapshots: '@types/babel__core@7.20.5': dependencies: - '@babel/parser': 7.27.2 - '@babel/types': 7.27.1 - '@types/babel__generator': 7.6.8 + '@babel/parser': 7.28.0 + '@babel/types': 7.28.1 + '@types/babel__generator': 7.27.0 '@types/babel__template': 7.4.4 - '@types/babel__traverse': 7.20.5 + '@types/babel__traverse': 7.20.7 - '@types/babel__generator@7.6.8': + '@types/babel__generator@7.27.0': dependencies: - '@babel/types': 7.27.1 + '@babel/types': 7.28.1 '@types/babel__template@7.4.4': dependencies: - '@babel/parser': 7.27.2 - '@babel/types': 7.27.1 + '@babel/parser': 7.28.0 + '@babel/types': 7.28.1 - '@types/babel__traverse@7.20.5': + '@types/babel__traverse@7.20.7': dependencies: - '@babel/types': 7.27.1 + '@babel/types': 7.28.1 '@types/backbone@1.4.14': dependencies: @@ -14480,21 +14277,16 @@ snapshots: '@types/better-sqlite3@7.6.13': dependencies: - '@types/node': 18.19.115 - - '@types/body-parser@1.19.5': - dependencies: - '@types/connect': 3.4.35 - '@types/node': 18.19.115 + '@types/node': 18.19.118 '@types/body-parser@1.19.6': dependencies: - '@types/connect': 3.4.35 - '@types/node': 18.19.115 + '@types/connect': 3.4.38 + '@types/node': 18.19.118 '@types/bonjour@3.5.13': dependencies: - '@types/node': 18.19.115 + '@types/node': 18.19.118 '@types/caseless@0.12.5': {} @@ -14507,19 +14299,19 @@ snapshots: '@types/connect-history-api-fallback@1.5.4': dependencies: '@types/express-serve-static-core': 4.19.6 - '@types/node': 18.19.115 + '@types/node': 18.19.118 - '@types/connect@3.4.35': + '@types/connect@3.4.38': dependencies: - '@types/node': 18.19.115 + '@types/node': 18.19.118 '@types/cookie@0.3.3': {} '@types/cookie@0.6.0': {} - '@types/cors@2.8.18': + '@types/cors@2.8.19': dependencies: - '@types/node': 18.19.115 + '@types/node': 18.19.118 '@types/d3-array@3.2.1': {} @@ -14640,7 +14432,7 @@ snapshots: '@types/debug@4.1.12': dependencies: - '@types/ms': 0.7.31 + '@types/ms': 0.7.34 '@types/dot-object@2.1.6': {} @@ -14651,7 +14443,7 @@ snapshots: '@types/eslint@7.29.0': dependencies: - '@types/estree': 1.0.6 + '@types/estree': 1.0.8 '@types/json-schema': 7.0.15 '@types/eslint@9.6.1': @@ -14659,20 +14451,11 @@ snapshots: '@types/estree': 1.0.8 '@types/json-schema': 7.0.15 - '@types/estree@1.0.6': {} - '@types/estree@1.0.8': {} - '@types/express-serve-static-core@4.19.0': - dependencies: - '@types/node': 18.19.115 - '@types/qs': 6.9.17 - '@types/range-parser': 1.2.7 - '@types/send': 0.17.4 - '@types/express-serve-static-core@4.19.6': dependencies: - '@types/node': 18.19.115 + '@types/node': 18.19.118 '@types/qs': 6.14.0 '@types/range-parser': 1.2.7 '@types/send': 0.17.5 @@ -14683,14 +14466,14 @@ snapshots: '@types/express@4.17.23': dependencies: - '@types/body-parser': 1.19.5 - '@types/express-serve-static-core': 4.19.0 - '@types/qs': 6.9.17 - '@types/serve-static': 1.15.7 + '@types/body-parser': 1.19.6 + '@types/express-serve-static-core': 4.19.6 + '@types/qs': 6.14.0 + '@types/serve-static': 1.15.8 '@types/formidable@3.4.5': dependencies: - '@types/node': 18.19.115 + '@types/node': 18.19.118 '@types/geojson-vt@3.2.5': dependencies: @@ -14700,12 +14483,12 @@ snapshots: '@types/glob@7.2.0': dependencies: - '@types/minimatch': 5.1.2 - '@types/node': 18.19.115 + '@types/minimatch': 6.0.0 + '@types/node': 18.19.118 '@types/graceful-fs@4.1.9': dependencies: - '@types/node': 24.0.10 + '@types/node': 18.19.118 '@types/hast@2.3.10': dependencies: @@ -14722,27 +14505,21 @@ snapshots: '@types/html-minifier-terser@6.1.0': {} - '@types/http-errors@2.0.4': {} - '@types/http-errors@2.0.5': {} '@types/http-proxy@1.17.16': dependencies: - '@types/node': 18.19.115 + '@types/node': 18.19.118 '@types/istanbul-lib-coverage@2.0.6': {} - '@types/istanbul-lib-report@3.0.0': + '@types/istanbul-lib-report@3.0.3': dependencies: '@types/istanbul-lib-coverage': 2.0.6 - '@types/istanbul-reports@3.0.1': - dependencies: - '@types/istanbul-lib-report': 3.0.0 - '@types/istanbul-reports@3.0.4': dependencies: - '@types/istanbul-lib-report': 3.0.0 + '@types/istanbul-lib-report': 3.0.3 '@types/jest@29.5.14': dependencies: @@ -14751,13 +14528,13 @@ snapshots: '@types/jquery@3.5.32': dependencies: - '@types/sizzle': 2.3.3 + '@types/sizzle': 2.3.9 '@types/js-yaml@4.0.9': {} '@types/jsdom@21.1.7': dependencies: - '@types/node': 24.0.10 + '@types/node': 18.19.118 '@types/tough-cookie': 4.0.5 parse5: 7.3.0 @@ -14771,7 +14548,7 @@ snapshots: '@types/keyv@3.1.4': dependencies: - '@types/node': 18.19.115 + '@types/node': 18.19.118 '@types/linkify-it@5.0.0': {} @@ -14802,11 +14579,11 @@ snapshots: '@types/mime@1.3.5': {} - '@types/minimatch@5.1.2': {} - - '@types/minimist@1.2.2': {} + '@types/minimatch@6.0.0': + dependencies: + minimatch: 10.0.3 - '@types/ms@0.7.31': {} + '@types/minimist@1.2.5': {} '@types/ms@0.7.34': {} @@ -14814,42 +14591,38 @@ snapshots: '@types/node-fetch@2.6.12': dependencies: - '@types/node': 18.19.115 - form-data: 4.0.2 + '@types/node': 18.19.118 + form-data: 4.0.3 - '@types/node-forge@1.3.11': + '@types/node-forge@1.3.13': dependencies: - '@types/node': 18.19.115 + '@types/node': 18.19.118 '@types/node-zendesk@2.0.15': dependencies: - '@types/node': 18.19.115 + '@types/node': 18.19.118 - '@types/node@18.19.115': + '@types/node@18.19.118': dependencies: undici-types: 5.26.5 - '@types/node@24.0.10': - dependencies: - undici-types: 7.8.0 - '@types/nodemailer@6.4.17': dependencies: - '@types/node': 18.19.115 + '@types/node': 18.19.118 - '@types/normalize-package-data@2.4.1': {} + '@types/normalize-package-data@2.4.4': {} '@types/oauth@0.9.6': dependencies: - '@types/node': 18.19.115 + '@types/node': 18.19.118 '@types/passport-google-oauth20@2.0.16': dependencies: '@types/express': 4.17.23 '@types/passport': 1.0.17 - '@types/passport-oauth2': 1.4.17 + '@types/passport-oauth2': 1.8.0 - '@types/passport-oauth2@1.4.17': + '@types/passport-oauth2@1.8.0': dependencies: '@types/express': 4.17.23 '@types/oauth': 0.9.6 @@ -14868,20 +14641,18 @@ snapshots: '@types/pg@8.15.4': dependencies: - '@types/node': 18.19.115 - pg-protocol: 1.8.0 + '@types/node': 18.19.118 + pg-protocol: 1.10.3 pg-types: 2.2.0 '@types/primus@7.3.9': dependencies: - '@types/node': 18.19.115 + '@types/node': 18.19.118 '@types/prismjs@1.26.5': {} '@types/qs@6.14.0': {} - '@types/qs@6.9.17': {} - '@types/range-parser@1.2.7': {} '@types/react-dom@19.1.6(@types/react@19.1.8)': @@ -14902,13 +14673,13 @@ snapshots: '@types/request@2.48.12': dependencies: '@types/caseless': 0.12.5 - '@types/node': 18.19.115 + '@types/node': 18.19.118 '@types/tough-cookie': 4.0.5 form-data: 2.5.3 '@types/responselike@1.0.3': dependencies: - '@types/node': 18.19.115 + '@types/node': 18.19.118 '@types/retry@0.12.0': {} @@ -14922,37 +14693,26 @@ snapshots: '@types/semver@7.7.0': {} - '@types/send@0.17.4': - dependencies: - '@types/mime': 1.3.5 - '@types/node': 18.19.115 - '@types/send@0.17.5': dependencies: '@types/mime': 1.3.5 - '@types/node': 18.19.115 + '@types/node': 18.19.118 '@types/serve-index@1.9.4': dependencies: '@types/express': 4.17.23 - '@types/serve-static@1.15.7': - dependencies: - '@types/http-errors': 2.0.4 - '@types/node': 18.19.115 - '@types/send': 0.17.4 - '@types/serve-static@1.15.8': dependencies: '@types/http-errors': 2.0.5 - '@types/node': 18.19.115 + '@types/node': 18.19.118 '@types/send': 0.17.5 - '@types/sizzle@2.3.3': {} + '@types/sizzle@2.3.9': {} '@types/sockjs@0.3.36': dependencies: - '@types/node': 18.19.115 + '@types/node': 18.19.118 '@types/stack-utils@2.0.3': {} @@ -14962,7 +14722,7 @@ snapshots: '@types/tern@0.23.9': dependencies: - '@types/estree': 1.0.6 + '@types/estree': 1.0.8 '@types/tough-cookie@4.0.5': {} @@ -14983,25 +14743,25 @@ snapshots: '@types/ws@8.18.1': dependencies: - '@types/node': 18.19.115 + '@types/node': 18.19.118 '@types/xml-encryption@1.2.4': dependencies: - '@types/node': 18.19.115 + '@types/node': 18.19.118 '@types/xml2js@0.4.14': dependencies: - '@types/node': 18.19.115 + '@types/node': 18.19.118 - '@types/yargs-parser@21.0.0': {} + '@types/yargs-parser@21.0.3': {} '@types/yargs@15.0.19': dependencies: - '@types/yargs-parser': 21.0.0 + '@types/yargs-parser': 21.0.3 '@types/yargs@17.0.33': dependencies: - '@types/yargs-parser': 21.0.0 + '@types/yargs-parser': 21.0.3 '@typescript-eslint/eslint-plugin@6.21.0(@typescript-eslint/parser@6.21.0(eslint@8.57.1)(typescript@5.8.3))(eslint@8.57.1)(typescript@5.8.3)': dependencies: @@ -15014,9 +14774,9 @@ snapshots: debug: 4.4.1 eslint: 8.57.1 graphemer: 1.4.0 - ignore: 5.3.1 + ignore: 5.3.2 natural-compare: 1.4.0 - semver: 7.7.1 + semver: 7.7.2 ts-api-utils: 1.4.3(typescript@5.8.3) optionalDependencies: typescript: 5.8.3 @@ -15072,14 +14832,14 @@ snapshots: '@typescript-eslint/utils@6.21.0(eslint@8.57.1)(typescript@5.8.3)': dependencies: - '@eslint-community/eslint-utils': 4.5.1(eslint@8.57.1) + '@eslint-community/eslint-utils': 4.7.0(eslint@8.57.1) '@types/json-schema': 7.0.15 '@types/semver': 7.7.0 '@typescript-eslint/scope-manager': 6.21.0 '@typescript-eslint/types': 6.21.0 '@typescript-eslint/typescript-estree': 6.21.0(typescript@5.8.3) eslint: 8.57.1 - semver: 7.7.1 + semver: 7.7.2 transitivePeerDependencies: - supports-color - typescript @@ -15237,18 +14997,20 @@ snapshots: setheader: 1.0.2 vary: 1.1.2 - acorn-jsx@5.3.2(acorn@8.14.1): + acorn-import-phases@1.0.3(acorn@8.15.0): dependencies: - acorn: 8.14.1 + acorn: 8.15.0 + + acorn-jsx@5.3.2(acorn@8.15.0): + dependencies: + acorn: 8.15.0 acorn-walk@8.3.4: dependencies: - acorn: 8.14.1 + acorn: 8.15.0 acorn@7.4.1: {} - acorn@8.14.1: {} - acorn@8.15.0: {} activedirectory2@2.2.0: @@ -15268,7 +15030,7 @@ snapshots: transitivePeerDependencies: - supports-color - agent-base@7.1.3: {} + agent-base@7.1.4: {} agentkeepalive@4.6.0: dependencies: @@ -15297,7 +15059,7 @@ snapshots: ajv@8.17.1: dependencies: fast-deep-equal: 3.1.3 - fast-uri: 3.0.2 + fast-uri: 3.0.6 json-schema-traverse: 1.0.0 require-from-string: 2.0.2 @@ -15315,10 +15077,6 @@ snapshots: ansi-regex@6.1.0: {} - ansi-styles@3.2.1: - dependencies: - color-convert: 1.9.3 - ansi-styles@4.3.0: dependencies: color-convert: 2.0.1 @@ -15332,7 +15090,7 @@ snapshots: antd: 5.26.4(date-fns@2.30.0)(moment@2.30.1)(react-dom@19.1.0(react@19.1.0))(react@19.1.0) react: 19.1.0 react-dom: 19.1.0(react@19.1.0) - react-easy-crop: 5.4.1(react-dom@19.1.0(react@19.1.0))(react@19.1.0) + react-easy-crop: 5.5.0(react-dom@19.1.0(react@19.1.0))(react@19.1.0) tslib: 2.8.1 antd@5.26.4(date-fns@2.30.0)(moment@2.30.1)(react-dom@19.1.0(react@19.1.0))(react@19.1.0): @@ -15343,7 +15101,7 @@ snapshots: '@ant-design/fast-color': 2.0.6 '@ant-design/icons': 5.6.1(react-dom@19.1.0(react@19.1.0))(react@19.1.0) '@ant-design/react-slick': 1.1.2(react@19.1.0) - '@babel/runtime': 7.27.0 + '@babel/runtime': 7.27.6 '@rc-component/color-picker': 2.0.1(react-dom@19.1.0(react@19.1.0))(react@19.1.0) '@rc-component/mutate-observer': 1.1.0(react-dom@19.1.0(react@19.1.0))(react@19.1.0) '@rc-component/qrcode': 1.0.0(react-dom@19.1.0(react@19.1.0))(react@19.1.0) @@ -15465,7 +15223,7 @@ snapshots: dependencies: call-bind: 1.0.8 define-properties: 1.2.1 - es-abstract: 1.23.9 + es-abstract: 1.24.0 es-shim-unscopables: 1.1.0 array.prototype.flatmap@1.3.3: @@ -15511,7 +15269,7 @@ snapshots: assert@2.1.0: dependencies: - call-bind: 1.0.7 + call-bind: 1.0.8 is-nan: 1.3.2 object-is: 1.1.6 object.assign: 4.1.7 @@ -15563,19 +15321,19 @@ snapshots: axios@1.10.0: dependencies: - follow-redirects: 1.15.9(debug@4.4.1) - form-data: 4.0.2 + follow-redirects: 1.15.9 + form-data: 4.0.3 proxy-from-env: 1.1.0 transitivePeerDependencies: - debug - babel-jest@29.7.0(@babel/core@7.26.9): + babel-jest@29.7.0(@babel/core@7.28.0): dependencies: - '@babel/core': 7.26.9 + '@babel/core': 7.28.0 '@jest/transform': 29.7.0 '@types/babel__core': 7.20.5 babel-plugin-istanbul: 6.1.1 - babel-preset-jest: 29.6.3(@babel/core@7.26.9) + babel-preset-jest: 29.6.3(@babel/core@7.28.0) chalk: 4.1.2 graceful-fs: 4.2.11 slash: 3.0.0 @@ -15595,31 +15353,34 @@ snapshots: babel-plugin-jest-hoist@29.6.3: dependencies: '@babel/template': 7.27.2 - '@babel/types': 7.27.1 + '@babel/types': 7.28.1 '@types/babel__core': 7.20.5 - '@types/babel__traverse': 7.20.5 - - babel-preset-current-node-syntax@1.0.1(@babel/core@7.26.9): - dependencies: - '@babel/core': 7.26.9 - '@babel/plugin-syntax-async-generators': 7.8.4(@babel/core@7.26.9) - '@babel/plugin-syntax-bigint': 7.8.3(@babel/core@7.26.9) - '@babel/plugin-syntax-class-properties': 7.12.13(@babel/core@7.26.9) - '@babel/plugin-syntax-import-meta': 7.10.4(@babel/core@7.26.9) - '@babel/plugin-syntax-json-strings': 7.8.3(@babel/core@7.26.9) - '@babel/plugin-syntax-logical-assignment-operators': 7.10.4(@babel/core@7.26.9) - '@babel/plugin-syntax-nullish-coalescing-operator': 7.8.3(@babel/core@7.26.9) - '@babel/plugin-syntax-numeric-separator': 7.10.4(@babel/core@7.26.9) - '@babel/plugin-syntax-object-rest-spread': 7.8.3(@babel/core@7.26.9) - '@babel/plugin-syntax-optional-catch-binding': 7.8.3(@babel/core@7.26.9) - '@babel/plugin-syntax-optional-chaining': 7.8.3(@babel/core@7.26.9) - '@babel/plugin-syntax-top-level-await': 7.14.5(@babel/core@7.26.9) - - babel-preset-jest@29.6.3(@babel/core@7.26.9): - dependencies: - '@babel/core': 7.26.9 + '@types/babel__traverse': 7.20.7 + + babel-preset-current-node-syntax@1.1.0(@babel/core@7.28.0): + dependencies: + '@babel/core': 7.28.0 + '@babel/plugin-syntax-async-generators': 7.8.4(@babel/core@7.28.0) + '@babel/plugin-syntax-bigint': 7.8.3(@babel/core@7.28.0) + '@babel/plugin-syntax-class-properties': 7.12.13(@babel/core@7.28.0) + '@babel/plugin-syntax-class-static-block': 7.14.5(@babel/core@7.28.0) + '@babel/plugin-syntax-import-attributes': 7.27.1(@babel/core@7.28.0) + '@babel/plugin-syntax-import-meta': 7.10.4(@babel/core@7.28.0) + '@babel/plugin-syntax-json-strings': 7.8.3(@babel/core@7.28.0) + '@babel/plugin-syntax-logical-assignment-operators': 7.10.4(@babel/core@7.28.0) + '@babel/plugin-syntax-nullish-coalescing-operator': 7.8.3(@babel/core@7.28.0) + '@babel/plugin-syntax-numeric-separator': 7.10.4(@babel/core@7.28.0) + '@babel/plugin-syntax-object-rest-spread': 7.8.3(@babel/core@7.28.0) + '@babel/plugin-syntax-optional-catch-binding': 7.8.3(@babel/core@7.28.0) + '@babel/plugin-syntax-optional-chaining': 7.8.3(@babel/core@7.28.0) + '@babel/plugin-syntax-private-property-in-object': 7.14.5(@babel/core@7.28.0) + '@babel/plugin-syntax-top-level-await': 7.14.5(@babel/core@7.28.0) + + babel-preset-jest@29.6.3(@babel/core@7.28.0): + dependencies: + '@babel/core': 7.28.0 babel-plugin-jest-hoist: 29.6.3 - babel-preset-current-node-syntax: 1.0.1(@babel/core@7.26.9) + babel-preset-current-node-syntax: 1.1.0(@babel/core@7.28.0) babel-runtime@6.26.0: dependencies: @@ -15675,9 +15436,9 @@ snapshots: big.js@6.2.2: {} - bignumber.js@9.2.1: {} + bignumber.js@9.3.1: {} - binary-extensions@2.2.0: {} + binary-extensions@2.3.0: {} binary-search-bounds@2.0.5: {} @@ -15751,8 +15512,8 @@ snapshots: browserslist@4.25.1: dependencies: - caniuse-lite: 1.0.30001726 - electron-to-chromium: 1.5.175 + caniuse-lite: 1.0.30001727 + electron-to-chromium: 1.5.182 node-releases: 2.0.19 update-browserslist-db: 1.1.3(browserslist@4.25.1) @@ -15806,14 +15567,6 @@ snapshots: es-errors: 1.3.0 function-bind: 1.1.2 - call-bind@1.0.7: - dependencies: - es-define-property: 1.0.0 - es-errors: 1.3.0 - function-bind: 1.1.2 - get-intrinsic: 1.2.4 - set-function-length: 1.2.2 - call-bind@1.0.8: dependencies: call-bind-apply-helpers: 1.0.2 @@ -15843,9 +15596,7 @@ snapshots: camelcase@6.3.0: {} - caniuse-lite@1.0.30001723: {} - - caniuse-lite@1.0.30001726: {} + caniuse-lite@1.0.30001727: {} canvas-fit@1.5.0: dependencies: @@ -15864,12 +15615,6 @@ snapshots: dependencies: chalk: 5.4.1 - chalk@2.4.2: - dependencies: - ansi-styles: 3.2.1 - escape-string-regexp: 1.0.5 - supports-color: 5.5.0 - chalk@3.0.0: dependencies: ansi-styles: 4.3.0 @@ -15901,17 +15646,17 @@ snapshots: '@types/js-yaml': 4.0.9 chalk: 5.4.1 commander: 13.1.0 - edit-json-file: 1.8.0 - globby: 14.0.2 + edit-json-file: 1.8.1 + globby: 14.1.0 js-yaml: 4.1.0 - semver: 7.6.3 + semver: 7.7.2 table: 6.9.0 - type-fest: 4.32.0 + type-fest: 4.41.0 cheerio-select@1.6.0: dependencies: css-select: 4.3.0 - css-what: 6.1.0 + css-what: 6.2.2 domelementtype: 2.3.0 domhandler: 4.3.1 domutils: 2.8.0 @@ -15919,8 +15664,8 @@ snapshots: cheerio-select@2.1.0: dependencies: boolbase: 1.0.0 - css-select: 5.1.0 - css-what: 6.1.0 + css-select: 5.2.2 + css-what: 6.2.2 domelementtype: 2.3.0 domhandler: 5.0.3 domutils: 3.2.2 @@ -15942,7 +15687,7 @@ snapshots: domhandler: 5.0.3 domutils: 3.2.2 htmlparser2: 8.0.2 - parse5: 7.2.1 + parse5: 7.3.0 parse5-htmlparser2-tree-adapter: 7.1.0 chevrotain-allstar@0.3.1(chevrotain@11.0.3): @@ -15981,9 +15726,9 @@ snapshots: ci-info@3.9.0: {} - ci-info@4.2.0: {} + ci-info@4.3.0: {} - cjs-module-lexer@1.2.3: {} + cjs-module-lexer@1.4.3: {} cjsx-loader@3.0.0: dependencies: @@ -16002,10 +15747,10 @@ snapshots: dependencies: source-map: 0.6.1 - clean-webpack-plugin@4.0.0(webpack@5.99.5): + clean-webpack-plugin@4.0.0(webpack@5.100.1): dependencies: del: 4.1.1 - webpack: 5.99.5 + webpack: 5.100.1 clear-module@4.1.2: dependencies: @@ -16060,10 +15805,10 @@ snapshots: dependencies: mkpath: 0.1.0 - coffee-loader@3.0.0(coffeescript@2.7.0)(webpack@5.99.5): + coffee-loader@3.0.0(coffeescript@2.7.0)(webpack@5.100.1): dependencies: coffeescript: 2.7.0 - webpack: 5.99.5 + webpack: 5.100.1 coffee-react-transform@4.0.0: {} @@ -16175,7 +15920,7 @@ snapshots: compressible@2.0.18: dependencies: - mime-db: 1.52.0 + mime-db: 1.54.0 compression@1.8.0: dependencies: @@ -16240,7 +15985,7 @@ snapshots: console-table-printer@2.14.6: dependencies: - simple-wcswidth: 1.1.1 + simple-wcswidth: 1.1.2 console.table@0.10.0: dependencies: @@ -16309,28 +16054,13 @@ snapshots: dependencies: capture-stack-trace: 1.0.2 - create-jest@29.7.0(@types/node@18.19.115): - dependencies: - '@jest/types': 29.6.3 - chalk: 4.1.2 - exit: 0.1.2 - graceful-fs: 4.2.11 - jest-config: 29.7.0(@types/node@18.19.115) - jest-util: 29.7.0 - prompts: 2.4.2 - transitivePeerDependencies: - - '@types/node' - - babel-plugin-macros - - supports-color - - ts-node - - create-jest@29.7.0(@types/node@24.0.10): + create-jest@29.7.0(@types/node@18.19.118): dependencies: '@jest/types': 29.6.3 chalk: 4.1.2 exit: 0.1.2 graceful-fs: 4.2.11 - jest-config: 29.7.0(@types/node@24.0.10) + jest-config: 29.7.0(@types/node@18.19.118) jest-util: 29.7.0 prompts: 2.4.2 transitivePeerDependencies: @@ -16354,12 +16084,6 @@ snapshots: transitivePeerDependencies: - encoding - cross-spawn@7.0.3: - dependencies: - path-key: 3.1.1 - shebang-command: 2.0.0 - which: 2.0.2 - cross-spawn@7.0.6: dependencies: path-key: 3.1.1 @@ -16454,8 +16178,8 @@ snapshots: cspell-lib: 8.19.4 fast-json-stable-stringify: 2.1.0 file-entry-cache: 9.1.0 - semver: 7.7.1 - tinyglobby: 0.2.13 + semver: 7.7.2 + tinyglobby: 0.2.14 css-font-size-keywords@1.0.0: {} @@ -16479,39 +16203,39 @@ snapshots: css-global-keywords@1.0.1: {} - css-loader@7.1.2(@rspack/core@1.4.4(@swc/helpers@0.5.15))(webpack@5.99.5): + css-loader@7.1.2(@rspack/core@1.4.6(@swc/helpers@0.5.15))(webpack@5.100.1): dependencies: - icss-utils: 5.1.0(postcss@8.5.3) - postcss: 8.5.3 - postcss-modules-extract-imports: 3.1.0(postcss@8.5.3) - postcss-modules-local-by-default: 4.2.0(postcss@8.5.3) - postcss-modules-scope: 3.2.1(postcss@8.5.3) - postcss-modules-values: 4.0.0(postcss@8.5.3) + icss-utils: 5.1.0(postcss@8.5.6) + postcss: 8.5.6 + postcss-modules-extract-imports: 3.1.0(postcss@8.5.6) + postcss-modules-local-by-default: 4.2.0(postcss@8.5.6) + postcss-modules-scope: 3.2.1(postcss@8.5.6) + postcss-modules-values: 4.0.0(postcss@8.5.6) postcss-value-parser: 4.2.0 - semver: 7.7.1 + semver: 7.7.2 optionalDependencies: - '@rspack/core': 1.4.4(@swc/helpers@0.5.15) - webpack: 5.99.5 + '@rspack/core': 1.4.6(@swc/helpers@0.5.15) + webpack: 5.100.1 css-select@4.3.0: dependencies: boolbase: 1.0.0 - css-what: 6.1.0 + css-what: 6.2.2 domhandler: 4.3.1 domutils: 2.8.0 nth-check: 2.1.1 - css-select@5.1.0: + css-select@5.2.2: dependencies: boolbase: 1.0.0 - css-what: 6.1.0 + css-what: 6.2.2 domhandler: 5.0.3 domutils: 3.2.2 nth-check: 2.1.1 css-system-font-keywords@1.0.0: {} - css-what@6.1.0: {} + css-what@6.2.2: {} css.escape@1.5.1: {} @@ -16530,19 +16254,19 @@ snapshots: csv-parse@5.6.0: {} - csv-stringify@6.5.2: {} + csv-stringify@6.6.0: {} - cytoscape-cose-bilkent@4.1.0(cytoscape@3.32.0): + cytoscape-cose-bilkent@4.1.0(cytoscape@3.32.1): dependencies: cose-base: 1.0.3 - cytoscape: 3.32.0 + cytoscape: 3.32.1 - cytoscape-fcose@2.2.0(cytoscape@3.32.0): + cytoscape-fcose@2.2.0(cytoscape@3.32.1): dependencies: cose-base: 2.2.0 - cytoscape: 3.32.0 + cytoscape: 3.32.1 - cytoscape@3.32.0: {} + cytoscape@3.32.1: {} d3-array@1.2.4: {} @@ -16623,7 +16347,7 @@ snapshots: commander: 2.20.3 d3-array: 1.2.4 d3-geo: 1.12.1 - resolve: 1.22.8 + resolve: 1.22.10 d3-geo@1.12.1: dependencies: @@ -16816,7 +16540,7 @@ snapshots: dependencies: ms: 2.1.3 - decamelize-keys@1.1.0: + decamelize-keys@1.1.1: dependencies: decamelize: 1.2.0 map-obj: 1.0.1 @@ -16825,9 +16549,9 @@ snapshots: decimal.js-light@2.5.1: {} - decimal.js@10.5.0: {} + decimal.js@10.6.0: {} - decode-named-character-reference@1.1.0: + decode-named-character-reference@1.2.0: dependencies: character-entities: 2.0.2 @@ -16835,7 +16559,7 @@ snapshots: dependencies: mimic-response: 3.1.0 - dedent@1.5.3: {} + dedent@1.6.0: {} deep-extend@0.6.0: {} @@ -16884,7 +16608,7 @@ snapshots: is-path-in-cwd: 2.1.0 p-map: 2.1.0 pify: 4.0.1 - rimraf: 2.6.3 + rimraf: 2.7.1 delaunator@5.0.1: dependencies: @@ -16905,10 +16629,7 @@ snapshots: detect-libc@1.0.3: optional: true - detect-libc@2.0.3: {} - - detect-libc@2.0.4: - optional: true + detect-libc@2.0.4: {} detect-newline@3.1.0: {} @@ -16949,7 +16670,7 @@ snapshots: diskusage@1.2.0: dependencies: es6-promise: 4.2.8 - nan: 2.20.0 + nan: 2.23.0 dns-packet@5.6.1: dependencies: @@ -17038,7 +16759,7 @@ snapshots: dtrace-provider@0.8.8: dependencies: - nan: 2.20.0 + nan: 2.23.0 optional: true dtype@2.0.0: {} @@ -17057,14 +16778,14 @@ snapshots: duplexify@3.7.1: dependencies: - end-of-stream: 1.4.4 + end-of-stream: 1.4.5 inherits: 2.0.4 readable-stream: 2.3.8 stream-shift: 1.0.3 duplexify@4.1.3: dependencies: - end-of-stream: 1.4.4 + end-of-stream: 1.4.5 inherits: 2.0.4 readable-stream: 3.6.2 stream-shift: 1.0.3 @@ -17083,13 +16804,13 @@ snapshots: dependencies: safe-buffer: 5.2.1 - edit-json-file@1.8.0: + edit-json-file@1.8.1: dependencies: - find-value: 1.0.12 - iterate-object: 1.3.4 - r-json: 1.3.0 + find-value: 1.0.13 + iterate-object: 1.3.5 + r-json: 1.3.1 set-value: 4.1.0 - w-json: 1.3.10 + w-json: 1.3.11 ee-first@1.1.1: {} @@ -17097,7 +16818,7 @@ snapshots: dependencies: jake: 10.9.2 - electron-to-chromium@1.5.175: {} + electron-to-chromium@1.5.182: {} element-size@1.1.1: {} @@ -17131,7 +16852,7 @@ snapshots: dependencies: iconv-lite: 0.6.3 - end-of-stream@1.4.4: + end-of-stream@1.4.5: dependencies: once: 1.4.0 @@ -17151,8 +16872,8 @@ snapshots: engine.io@6.6.4: dependencies: - '@types/cors': 2.8.18 - '@types/node': 18.19.115 + '@types/cors': 2.8.19 + '@types/node': 18.19.118 accepts: 1.3.8 base64id: 2.0.0 cookie: 0.7.2 @@ -17195,60 +16916,6 @@ snapshots: dependencies: stackframe: 1.3.4 - es-abstract@1.23.9: - dependencies: - array-buffer-byte-length: 1.0.2 - arraybuffer.prototype.slice: 1.0.4 - available-typed-arrays: 1.0.7 - call-bind: 1.0.8 - call-bound: 1.0.4 - data-view-buffer: 1.0.2 - data-view-byte-length: 1.0.2 - data-view-byte-offset: 1.0.1 - es-define-property: 1.0.1 - es-errors: 1.3.0 - es-object-atoms: 1.1.1 - es-set-tostringtag: 2.1.0 - es-to-primitive: 1.3.0 - function.prototype.name: 1.1.8 - get-intrinsic: 1.3.0 - get-proto: 1.0.1 - get-symbol-description: 1.1.0 - globalthis: 1.0.4 - gopd: 1.2.0 - has-property-descriptors: 1.0.2 - has-proto: 1.2.0 - has-symbols: 1.1.0 - hasown: 2.0.2 - internal-slot: 1.1.0 - is-array-buffer: 3.0.5 - is-callable: 1.2.7 - is-data-view: 1.0.2 - is-regex: 1.2.1 - is-shared-array-buffer: 1.0.4 - is-string: 1.1.1 - is-typed-array: 1.1.15 - is-weakref: 1.1.1 - math-intrinsics: 1.1.0 - object-inspect: 1.13.4 - object-keys: 1.1.1 - object.assign: 4.1.7 - own-keys: 1.0.1 - regexp.prototype.flags: 1.5.4 - safe-array-concat: 1.1.3 - safe-push-apply: 1.0.0 - safe-regex-test: 1.1.0 - set-proto: 1.0.0 - string.prototype.trim: 1.2.10 - string.prototype.trimend: 1.0.9 - string.prototype.trimstart: 1.0.8 - typed-array-buffer: 1.0.3 - typed-array-byte-length: 1.0.3 - typed-array-byte-offset: 1.0.4 - typed-array-length: 1.0.7 - unbox-primitive: 1.1.0 - which-typed-array: 1.1.19 - es-abstract@1.24.0: dependencies: array-buffer-byte-length: 1.0.2 @@ -17308,10 +16975,6 @@ snapshots: es-class@2.1.1: {} - es-define-property@1.0.0: - dependencies: - get-intrinsic: 1.2.4 - es-define-property@1.0.1: {} es-errors@1.3.0: {} @@ -17418,7 +17081,7 @@ snapshots: log-symbols: 4.1.0 plur: 4.0.0 string-width: 4.2.3 - supports-hyperlinks: 2.2.0 + supports-hyperlinks: 2.3.0 eslint-plugin-prettier@5.5.1(@types/eslint@9.6.1)(eslint-config-prettier@9.1.0(eslint@8.57.1))(eslint@8.57.1)(prettier@3.6.2): dependencies: @@ -17472,7 +17135,7 @@ snapshots: eslint@8.57.1: dependencies: - '@eslint-community/eslint-utils': 4.5.1(eslint@8.57.1) + '@eslint-community/eslint-utils': 4.7.0(eslint@8.57.1) '@eslint-community/regexpp': 4.12.1 '@eslint/eslintrc': 2.1.4 '@eslint/js': 8.57.1 @@ -17482,7 +17145,7 @@ snapshots: '@ungap/structured-clone': 1.3.0 ajv: 6.12.6 chalk: 4.1.2 - cross-spawn: 7.0.3 + cross-spawn: 7.0.6 debug: 4.4.1 doctrine: 3.0.0 escape-string-regexp: 4.0.0 @@ -17497,7 +17160,7 @@ snapshots: glob-parent: 6.0.2 globals: 13.24.0 graphemer: 1.4.0 - ignore: 5.3.1 + ignore: 5.3.2 imurmurhash: 0.1.4 is-glob: 4.0.3 is-path-inside: 3.0.3 @@ -17522,8 +17185,8 @@ snapshots: espree@9.6.1: dependencies: - acorn: 8.14.1 - acorn-jsx: 5.3.2(acorn@8.14.1) + acorn: 8.15.0 + acorn-jsx: 5.3.2(acorn@8.15.0) eslint-visitor-keys: 3.4.3 esprima@4.0.1: {} @@ -17674,7 +17337,7 @@ snapshots: fast-equals@5.2.2: {} - fast-glob@3.3.2: + fast-glob@3.3.3: dependencies: '@nodelib/fs.stat': 2.0.5 '@nodelib/fs.walk': 1.2.8 @@ -17692,7 +17355,7 @@ snapshots: fast-safe-stringify@2.1.1: {} - fast-uri@3.0.2: {} + fast-uri@3.0.6: {} fast-xml-parser@4.5.3: dependencies: @@ -17702,9 +17365,9 @@ snapshots: fastparse@1.1.2: {} - fastq@1.13.0: + fastq@1.19.1: dependencies: - reusify: 1.0.4 + reusify: 1.1.0 faye-websocket@0.11.4: dependencies: @@ -17714,7 +17377,7 @@ snapshots: dependencies: bser: 2.1.1 - fdir@6.4.4(picomatch@4.0.2): + fdir@6.4.6(picomatch@4.0.2): optionalDependencies: picomatch: 4.0.2 @@ -17736,7 +17399,7 @@ snapshots: dependencies: '@tokenizer/inflate': 0.2.7 strtok3: 10.3.1 - token-types: 6.0.0 + token-types: 6.0.3 uint8array-extras: 1.4.0 transitivePeerDependencies: - supports-color @@ -17773,7 +17436,7 @@ snapshots: locate-path: 6.0.0 path-exists: 4.0.0 - find-value@1.0.12: {} + find-value@1.0.13: {} flat-cache@3.2.0: dependencies: @@ -17794,6 +17457,8 @@ snapshots: dependencies: dtype: 2.0.0 + follow-redirects@1.15.9: {} + follow-redirects@1.15.9(debug@4.4.1): optionalDependencies: debug: 4.4.1 @@ -17810,11 +17475,6 @@ snapshots: dependencies: is-callable: 1.2.7 - foreground-child@3.3.0: - dependencies: - cross-spawn: 7.0.3 - signal-exit: 4.1.0 - foreground-child@3.3.1: dependencies: cross-spawn: 7.0.6 @@ -17830,11 +17490,12 @@ snapshots: mime-types: 2.1.35 safe-buffer: 5.2.1 - form-data@4.0.2: + form-data@4.0.3: dependencies: asynckit: 0.4.0 combined-stream: 1.0.8 es-set-tostringtag: 2.1.0 + hasown: 2.0.2 mime-types: 2.1.35 formdata-node@4.4.1: @@ -17842,12 +17503,6 @@ snapshots: node-domexception: 1.0.0 web-streams-polyfill: 4.0.0-beta.3 - formidable@3.5.2: - dependencies: - dezalgo: 1.0.4 - hexoid: 2.0.0 - once: 1.4.0 - formidable@3.5.4: dependencies: '@paralleldrive/cuid2': 2.2.2 @@ -17871,13 +17526,10 @@ snapshots: dependencies: graceful-fs: 4.2.11 jsonfile: 6.1.0 - universalify: 2.0.0 + universalify: 2.0.1 fs.realpath@1.0.0: {} - fsevents@2.3.2: - optional: true - fsevents@2.3.3: optional: true @@ -17931,14 +17583,6 @@ snapshots: get-canvas-context@1.0.2: {} - get-intrinsic@1.2.4: - dependencies: - es-errors: 1.3.0 - function-bind: 1.1.2 - has-proto: 1.0.3 - has-symbols: 1.1.0 - hasown: 2.0.2 - get-intrinsic@1.3.0: dependencies: call-bind-apply-helpers: 1.0.2 @@ -17975,7 +17619,7 @@ snapshots: es-errors: 1.3.0 get-intrinsic: 1.3.0 - get-uri@6.0.4: + get-uri@6.0.5: dependencies: basic-ftp: 5.0.5 data-uri-to-buffer: 6.0.2 @@ -18031,11 +17675,11 @@ snapshots: glob@10.4.5: dependencies: - foreground-child: 3.3.0 + foreground-child: 3.3.1 jackspeak: 3.4.3 minimatch: 9.0.5 minipass: 7.1.2 - package-json-from-dist: 1.0.0 + package-json-from-dist: 1.0.1 path-scurry: 1.11.1 glob@11.0.3: @@ -18080,8 +17724,6 @@ snapshots: min-document: 2.19.0 process: 0.11.10 - globals@11.12.0: {} - globals@13.24.0: dependencies: type-fest: 0.20.2 @@ -18097,19 +17739,19 @@ snapshots: dependencies: array-union: 2.1.0 dir-glob: 3.0.1 - fast-glob: 3.3.2 - ignore: 5.3.1 + fast-glob: 3.3.3 + ignore: 5.3.2 merge2: 1.4.1 slash: 3.0.0 - globby@14.0.2: + globby@14.1.0: dependencies: '@sindresorhus/merge-streams': 2.3.0 - fast-glob: 3.3.2 - ignore: 5.3.1 - path-type: 5.0.0 + fast-glob: 3.3.3 + ignore: 7.0.5 + path-type: 6.0.0 slash: 5.1.0 - unicorn-magic: 0.1.0 + unicorn-magic: 0.3.0 globby@6.1.0: dependencies: @@ -18213,10 +17855,10 @@ snapshots: - encoding - supports-color - google-gax@4.4.1(encoding@0.1.13): + google-gax@4.6.1(encoding@0.1.13): dependencies: - '@grpc/grpc-js': 1.13.3 - '@grpc/proto-loader': 0.7.13 + '@grpc/grpc-js': 1.13.4 + '@grpc/proto-loader': 0.7.15 '@types/long': 4.0.2 abort-controller: 3.0.0 duplexify: 4.1.3 @@ -18224,7 +17866,7 @@ snapshots: node-fetch: 2.7.0(encoding@0.1.13) object-hash: 3.0.0 proto3-json-serializer: 2.0.2 - protobufjs: 7.4.0 + protobufjs: 7.5.3 retry-request: 7.0.2(encoding@0.1.13) uuid: 9.0.1 transitivePeerDependencies: @@ -18238,7 +17880,7 @@ snapshots: extend: 3.0.2 gaxios: 6.7.1(encoding@0.1.13) google-auth-library: 9.15.1(encoding@0.1.13) - qs: 6.13.0 + qs: 6.14.0 url-template: 2.0.8 uuid: 9.0.1 transitivePeerDependencies: @@ -18320,8 +17962,6 @@ snapshots: has-bigints@1.1.0: {} - has-flag@3.0.0: {} - has-flag@4.0.0: {} has-hover@1.0.1: @@ -18338,8 +17978,6 @@ snapshots: dependencies: es-define-property: 1.0.1 - has-proto@1.0.3: {} - has-proto@1.2.0: dependencies: dunder-proto: 1.0.1 @@ -18422,8 +18060,6 @@ snapshots: he@1.2.0: {} - hexoid@2.0.0: {} - highlight-words-core@1.2.3: {} hoist-non-react-statics@3.3.2: @@ -18460,11 +18096,11 @@ snapshots: html-escaper@2.0.2: {} - html-loader@2.1.2(webpack@5.99.5): + html-loader@2.1.2(webpack@5.100.1): dependencies: html-minifier-terser: 5.1.1 parse5: 6.0.1 - webpack: 5.99.5 + webpack: 5.100.1 html-minifier-terser@5.1.1: dependencies: @@ -18484,7 +18120,7 @@ snapshots: he: 1.2.0 param-case: 3.0.4 relateurl: 0.2.7 - terser: 5.39.0 + terser: 5.43.1 html-react-parser@5.2.5(@types/react@19.1.8)(react@19.1.0): dependencies: @@ -18498,7 +18134,7 @@ snapshots: html-void-elements@3.0.0: {} - html-webpack-plugin@5.6.3(@rspack/core@1.4.4(@swc/helpers@0.5.15))(webpack@5.99.5): + html-webpack-plugin@5.6.3(@rspack/core@1.4.6(@swc/helpers@0.5.15))(webpack@5.100.1): dependencies: '@types/html-minifier-terser': 6.1.0 html-minifier-terser: 6.1.0 @@ -18506,8 +18142,8 @@ snapshots: pretty-error: 4.0.0 tapable: 2.2.2 optionalDependencies: - '@rspack/core': 1.4.4(@swc/helpers@0.5.15) - webpack: 5.99.5 + '@rspack/core': 1.4.6(@swc/helpers@0.5.15) + webpack: 5.100.1 htmlparser2@10.0.0: dependencies: @@ -18549,7 +18185,7 @@ snapshots: http-parser-js@0.5.10: {} - http-proxy-3@1.20.8: + http-proxy-3@1.20.10: dependencies: debug: 4.4.1 follow-redirects: 1.15.9(debug@4.4.1) @@ -18566,7 +18202,7 @@ snapshots: http-proxy-agent@7.0.2: dependencies: - agent-base: 7.1.3 + agent-base: 7.1.4 debug: 4.4.1 transitivePeerDependencies: - supports-color @@ -18600,7 +18236,7 @@ snapshots: https-proxy-agent@7.0.6: dependencies: - agent-base: 7.1.3 + agent-base: 7.1.4 debug: 4.4.1 transitivePeerDependencies: - supports-color @@ -18623,9 +18259,9 @@ snapshots: dependencies: safer-buffer: 2.1.2 - icss-utils@5.1.0(postcss@8.5.3): + icss-utils@5.1.0(postcss@8.5.6): dependencies: - postcss: 8.5.3 + postcss: 8.5.6 identity-obj-proxy@3.0.0: dependencies: @@ -18633,7 +18269,9 @@ snapshots: ieee754@1.2.1: {} - ignore@5.3.1: {} + ignore@5.3.2: {} + + ignore@7.0.5: {} image-extensions@1.1.0: {} @@ -18644,7 +18282,7 @@ snapshots: immutable@4.3.7: {} - immutable@5.1.1: {} + immutable@5.1.3: {} import-fresh@3.3.1: dependencies: @@ -18658,11 +18296,11 @@ snapshots: import-meta-resolve@4.1.0: {} - imports-loader@3.1.1(webpack@5.99.5): + imports-loader@3.1.1(webpack@5.100.1): dependencies: source-map: 0.6.1 strip-comments: 2.0.1 - webpack: 5.99.5 + webpack: 5.100.1 imurmurhash@0.1.4: {} @@ -18733,7 +18371,7 @@ snapshots: ipaddr.js@2.2.0: {} - irregular-plurals@3.3.0: {} + irregular-plurals@3.5.0: {} is-alphabetical@2.0.1: {} @@ -18771,7 +18409,7 @@ snapshots: is-binary-path@2.1.0: dependencies: - binary-extensions: 2.2.0 + binary-extensions: 2.3.0 is-boolean-object@1.2.2: dependencies: @@ -18850,7 +18488,7 @@ snapshots: is-nan@1.3.2: dependencies: - call-bind: 1.0.7 + call-bind: 1.0.8 define-properties: 1.2.1 is-negative-zero@2.0.3: {} @@ -18975,8 +18613,8 @@ snapshots: istanbul-lib-instrument@5.2.1: dependencies: - '@babel/core': 7.26.9 - '@babel/parser': 7.27.2 + '@babel/core': 7.28.0 + '@babel/parser': 7.28.0 '@istanbuljs/schema': 0.1.3 istanbul-lib-coverage: 3.2.2 semver: 6.3.1 @@ -18985,8 +18623,8 @@ snapshots: istanbul-lib-instrument@6.0.3: dependencies: - '@babel/core': 7.26.9 - '@babel/parser': 7.27.2 + '@babel/core': 7.28.0 + '@babel/parser': 7.28.0 '@istanbuljs/schema': 0.1.3 istanbul-lib-coverage: 3.2.2 semver: 7.7.2 @@ -19014,7 +18652,7 @@ snapshots: iterare@1.2.1: {} - iterate-object@1.3.4: {} + iterate-object@1.3.5: {} iterator.prototype@1.1.5: dependencies: @@ -19054,10 +18692,10 @@ snapshots: '@jest/expect': 29.7.0 '@jest/test-result': 29.7.0 '@jest/types': 29.6.3 - '@types/node': 24.0.10 + '@types/node': 18.19.118 chalk: 4.1.2 co: 4.6.0 - dedent: 1.5.3 + dedent: 1.6.0 is-generator-fn: 2.1.0 jest-each: 29.7.0 jest-matcher-utils: 29.7.0 @@ -19074,35 +18712,16 @@ snapshots: - babel-plugin-macros - supports-color - jest-cli@29.7.0(@types/node@18.19.115): - dependencies: - '@jest/core': 29.7.0 - '@jest/test-result': 29.7.0 - '@jest/types': 29.6.3 - chalk: 4.1.2 - create-jest: 29.7.0(@types/node@18.19.115) - exit: 0.1.2 - import-local: 3.2.0 - jest-config: 29.7.0(@types/node@18.19.115) - jest-util: 29.7.0 - jest-validate: 29.7.0 - yargs: 17.7.2 - transitivePeerDependencies: - - '@types/node' - - babel-plugin-macros - - supports-color - - ts-node - - jest-cli@29.7.0(@types/node@24.0.10): + jest-cli@29.7.0(@types/node@18.19.118): dependencies: '@jest/core': 29.7.0 '@jest/test-result': 29.7.0 '@jest/types': 29.6.3 chalk: 4.1.2 - create-jest: 29.7.0(@types/node@24.0.10) + create-jest: 29.7.0(@types/node@18.19.118) exit: 0.1.2 import-local: 3.2.0 - jest-config: 29.7.0(@types/node@24.0.10) + jest-config: 29.7.0(@types/node@18.19.118) jest-util: 29.7.0 jest-validate: 29.7.0 yargs: 17.7.2 @@ -19112,12 +18731,12 @@ snapshots: - supports-color - ts-node - jest-config@29.7.0(@types/node@18.19.115): + jest-config@29.7.0(@types/node@18.19.118): dependencies: - '@babel/core': 7.26.9 + '@babel/core': 7.28.0 '@jest/test-sequencer': 29.7.0 '@jest/types': 29.6.3 - babel-jest: 29.7.0(@babel/core@7.26.9) + babel-jest: 29.7.0(@babel/core@7.28.0) chalk: 4.1.2 ci-info: 3.9.0 deepmerge: 4.3.1 @@ -19137,37 +18756,7 @@ snapshots: slash: 3.0.0 strip-json-comments: 3.1.1 optionalDependencies: - '@types/node': 18.19.115 - transitivePeerDependencies: - - babel-plugin-macros - - supports-color - - jest-config@29.7.0(@types/node@24.0.10): - dependencies: - '@babel/core': 7.26.9 - '@jest/test-sequencer': 29.7.0 - '@jest/types': 29.6.3 - babel-jest: 29.7.0(@babel/core@7.26.9) - chalk: 4.1.2 - ci-info: 3.9.0 - deepmerge: 4.3.1 - glob: 7.2.3 - graceful-fs: 4.2.11 - jest-circus: 29.7.0 - jest-environment-node: 29.7.0 - jest-get-type: 29.6.3 - jest-regex-util: 29.6.3 - jest-resolve: 29.7.0 - jest-runner: 29.7.0 - jest-util: 29.7.0 - jest-validate: 29.7.0 - micromatch: 4.0.8 - parse-json: 5.2.0 - pretty-format: 29.7.0 - slash: 3.0.0 - strip-json-comments: 3.1.1 - optionalDependencies: - '@types/node': 24.0.10 + '@types/node': 18.19.118 transitivePeerDependencies: - babel-plugin-macros - supports-color @@ -19203,7 +18792,7 @@ snapshots: '@jest/environment': 30.0.4 '@jest/environment-jsdom-abstract': 30.0.4(jsdom@26.1.0) '@types/jsdom': 21.1.7 - '@types/node': 24.0.10 + '@types/node': 18.19.118 jsdom: 26.1.0 transitivePeerDependencies: - bufferutil @@ -19215,7 +18804,7 @@ snapshots: '@jest/environment': 29.7.0 '@jest/fake-timers': 29.7.0 '@jest/types': 29.6.3 - '@types/node': 24.0.10 + '@types/node': 18.19.118 jest-mock: 29.7.0 jest-util: 29.7.0 @@ -19227,7 +18816,7 @@ snapshots: dependencies: '@jest/types': 29.6.3 '@types/graceful-fs': 4.1.9 - '@types/node': 24.0.10 + '@types/node': 18.19.118 anymatch: 3.1.3 fb-watchman: 2.0.2 graceful-fs: 4.2.11 @@ -19260,7 +18849,7 @@ snapshots: jest-message-util@26.6.2: dependencies: - '@babel/code-frame': 7.24.7 + '@babel/code-frame': 7.27.1 '@jest/types': 26.6.2 '@types/stack-utils': 2.0.3 chalk: 4.1.2 @@ -19297,13 +18886,13 @@ snapshots: jest-mock@29.7.0: dependencies: '@jest/types': 29.6.3 - '@types/node': 24.0.10 + '@types/node': 18.19.118 jest-util: 29.7.0 jest-mock@30.0.2: dependencies: '@jest/types': 30.0.1 - '@types/node': 24.0.10 + '@types/node': 18.19.118 jest-util: 30.0.2 jest-pnp-resolver@1.2.3(jest-resolve@29.7.0): @@ -19332,7 +18921,7 @@ snapshots: jest-util: 29.7.0 jest-validate: 29.7.0 resolve: 1.22.10 - resolve.exports: 2.0.2 + resolve.exports: 2.0.3 slash: 3.0.0 jest-runner@29.7.0: @@ -19342,7 +18931,7 @@ snapshots: '@jest/test-result': 29.7.0 '@jest/transform': 29.7.0 '@jest/types': 29.6.3 - '@types/node': 24.0.10 + '@types/node': 18.19.118 chalk: 4.1.2 emittery: 0.13.1 graceful-fs: 4.2.11 @@ -19370,9 +18959,9 @@ snapshots: '@jest/test-result': 29.7.0 '@jest/transform': 29.7.0 '@jest/types': 29.6.3 - '@types/node': 24.0.10 + '@types/node': 18.19.118 chalk: 4.1.2 - cjs-module-lexer: 1.2.3 + cjs-module-lexer: 1.4.3 collect-v8-coverage: 1.0.2 glob: 7.2.3 graceful-fs: 4.2.11 @@ -19390,15 +18979,15 @@ snapshots: jest-snapshot@29.7.0: dependencies: - '@babel/core': 7.26.9 - '@babel/generator': 7.27.1 - '@babel/plugin-syntax-jsx': 7.27.1(@babel/core@7.26.9) - '@babel/plugin-syntax-typescript': 7.27.1(@babel/core@7.26.9) - '@babel/types': 7.27.1 + '@babel/core': 7.28.0 + '@babel/generator': 7.28.0 + '@babel/plugin-syntax-jsx': 7.27.1(@babel/core@7.28.0) + '@babel/plugin-syntax-typescript': 7.27.1(@babel/core@7.28.0) + '@babel/types': 7.28.1 '@jest/expect-utils': 29.7.0 '@jest/transform': 29.7.0 '@jest/types': 29.6.3 - babel-preset-current-node-syntax: 1.0.1(@babel/core@7.26.9) + babel-preset-current-node-syntax: 1.1.0(@babel/core@7.28.0) chalk: 4.1.2 expect: 29.7.0 graceful-fs: 4.2.11 @@ -19416,7 +19005,7 @@ snapshots: jest-util@29.7.0: dependencies: '@jest/types': 29.6.3 - '@types/node': 18.19.115 + '@types/node': 18.19.118 chalk: 4.1.2 ci-info: 3.9.0 graceful-fs: 4.2.11 @@ -19425,9 +19014,9 @@ snapshots: jest-util@30.0.2: dependencies: '@jest/types': 30.0.1 - '@types/node': 24.0.10 + '@types/node': 18.19.118 chalk: 4.1.2 - ci-info: 4.2.0 + ci-info: 4.3.0 graceful-fs: 4.2.11 picomatch: 4.0.2 @@ -19444,7 +19033,7 @@ snapshots: dependencies: '@jest/test-result': 29.7.0 '@jest/types': 29.6.3 - '@types/node': 24.0.10 + '@types/node': 18.19.118 ansi-escapes: 4.3.2 chalk: 4.1.2 emittery: 0.13.1 @@ -19453,35 +19042,23 @@ snapshots: jest-worker@27.5.1: dependencies: - '@types/node': 18.19.115 + '@types/node': 18.19.118 merge-stream: 2.0.0 supports-color: 8.1.1 jest-worker@29.7.0: dependencies: - '@types/node': 24.0.10 + '@types/node': 18.19.118 jest-util: 29.7.0 merge-stream: 2.0.0 supports-color: 8.1.1 - jest@29.7.0(@types/node@18.19.115): + jest@29.7.0(@types/node@18.19.118): dependencies: '@jest/core': 29.7.0 '@jest/types': 29.6.3 import-local: 3.2.0 - jest-cli: 29.7.0(@types/node@18.19.115) - transitivePeerDependencies: - - '@types/node' - - babel-plugin-macros - - supports-color - - ts-node - - jest@29.7.0(@types/node@24.0.10): - dependencies: - '@jest/core': 29.7.0 - '@jest/types': 29.6.3 - import-local: 3.2.0 - jest-cli: 29.7.0(@types/node@24.0.10) + jest-cli: 29.7.0(@types/node@18.19.118) transitivePeerDependencies: - '@types/node' - babel-plugin-macros @@ -19534,7 +19111,7 @@ snapshots: js-cookie@2.2.1: {} - js-tiktoken@1.0.19: + js-tiktoken@1.0.20: dependencies: base64-js: 1.5.1 @@ -19555,7 +19132,7 @@ snapshots: dependencies: cssstyle: 4.6.0 data-urls: 5.0.0 - decimal.js: 10.5.0 + decimal.js: 10.6.0 html-encoding-sniffer: 4.0.0 http-proxy-agent: 7.0.2 https-proxy-agent: 7.0.6 @@ -19582,7 +19159,7 @@ snapshots: json-bigint@1.0.0: dependencies: - bignumber.js: 9.2.1 + bignumber.js: 9.3.1 json-buffer@3.0.1: {} @@ -19630,7 +19207,7 @@ snapshots: jsonfile@6.1.0: dependencies: - universalify: 2.0.0 + universalify: 2.0.1 optionalDependencies: graceful-fs: 4.2.11 @@ -19668,13 +19245,13 @@ snapshots: dependencies: home-dir: 1.0.0 - jwa@1.4.1: + jwa@1.4.2: dependencies: buffer-equal-constant-time: 1.0.1 ecdsa-sig-formatter: 1.0.11 safe-buffer: 5.2.1 - jwa@2.0.0: + jwa@2.0.1: dependencies: buffer-equal-constant-time: 1.0.1 ecdsa-sig-formatter: 1.0.11 @@ -19682,12 +19259,12 @@ snapshots: jws@3.2.2: dependencies: - jwa: 1.4.1 + jwa: 1.4.2 safe-buffer: 5.2.1 jws@4.0.0: dependencies: - jwa: 2.0.0 + jwa: 2.0.1 safe-buffer: 5.2.1 jwt-decode@3.1.2: {} @@ -19740,7 +19317,7 @@ snapshots: langs@2.0.0: {} - langsmith@0.3.33(openai@4.104.0(encoding@0.1.13)(ws@8.18.3)(zod@3.25.67)): + langsmith@0.3.44(@opentelemetry/api@1.9.0)(openai@4.104.0(encoding@0.1.13)(ws@8.18.3)(zod@3.25.76)): dependencies: '@types/uuid': 10.0.0 chalk: 4.1.2 @@ -19750,7 +19327,8 @@ snapshots: semver: 7.7.2 uuid: 10.0.0 optionalDependencies: - openai: 4.104.0(encoding@0.1.13)(ws@8.18.3)(zod@3.25.67) + '@opentelemetry/api': 1.9.0 + openai: 4.104.0(encoding@0.1.13)(ws@8.18.3)(zod@3.25.76) launch-editor@2.10.0: dependencies: @@ -19776,10 +19354,10 @@ snapshots: vasync: 2.2.1 verror: 1.10.1 - less-loader@11.1.4(less@4.3.0)(webpack@5.99.5): + less-loader@11.1.4(less@4.3.0)(webpack@5.100.1): dependencies: less: 4.3.0 - webpack: 5.99.5 + webpack: 5.100.1 less@4.3.0: dependencies: @@ -19802,7 +19380,7 @@ snapshots: prelude-ls: 1.2.1 type-check: 0.4.0 - lib0@0.2.102: + lib0@0.2.109: dependencies: isomorphic.js: 0.2.5 @@ -19848,7 +19426,7 @@ snapshots: local-pkg@1.1.1: dependencies: mlly: 1.7.4 - pkg-types: 2.1.0 + pkg-types: 2.2.0 quansync: 0.2.10 locate-path@5.0.0: @@ -19898,7 +19476,7 @@ snapshots: chalk: 4.1.2 is-unicode-supported: 0.1.0 - long@5.3.1: {} + long@5.3.2: {} loose-envify@1.4.0: dependencies: @@ -20066,11 +19644,11 @@ snapshots: media-typer@0.3.0: {} - memfs@4.17.0: + memfs@4.17.2: dependencies: '@jsonjoy.com/json-pack': 1.2.0(tslib@2.8.1) - '@jsonjoy.com/util': 1.5.0(tslib@2.8.1) - tree-dump: 1.0.2(tslib@2.8.1) + '@jsonjoy.com/util': 1.6.0(tslib@2.8.1) + tree-dump: 1.0.3(tslib@2.8.1) tslib: 2.8.1 memoize-one@4.0.3: {} @@ -20079,9 +19657,9 @@ snapshots: meow@6.1.1: dependencies: - '@types/minimist': 1.2.2 + '@types/minimist': 1.2.5 camelcase-keys: 6.2.2 - decamelize-keys: 1.1.0 + decamelize-keys: 1.1.1 hard-rejection: 2.1.0 minimist-options: 4.1.0 normalize-package-data: 2.5.0 @@ -20093,10 +19671,10 @@ snapshots: meow@9.0.0: dependencies: - '@types/minimist': 1.2.2 + '@types/minimist': 1.2.5 camelcase-keys: 6.2.2 decamelize: 1.2.0 - decamelize-keys: 1.1.0 + decamelize-keys: 1.1.1 hard-rejection: 2.1.0 minimist-options: 4.1.0 normalize-package-data: 3.0.3 @@ -20122,9 +19700,9 @@ snapshots: '@iconify/utils': 2.3.0 '@mermaid-js/parser': 0.6.1 '@types/d3': 7.4.3 - cytoscape: 3.32.0 - cytoscape-cose-bilkent: 4.1.0(cytoscape@3.32.0) - cytoscape-fcose: 2.2.0(cytoscape@3.32.0) + cytoscape: 3.32.1 + cytoscape-cose-bilkent: 4.1.0(cytoscape@3.32.1) + cytoscape-fcose: 2.2.0(cytoscape@3.32.1) d3: 7.9.0 d3-sankey: 0.12.3 dagre-d3-es: 7.0.11 @@ -20169,6 +19747,8 @@ snapshots: mime-db@1.52.0: {} + mime-db@1.54.0: {} + mime-types@2.1.35: dependencies: mime-db: 1.52.0 @@ -20283,12 +19863,10 @@ snapshots: rimraf: 2.4.5 optional: true - nan@2.20.0: {} + nan@2.23.0: {} nanoid@3.3.11: {} - nanoid@3.3.8: {} - napi-build-utils@2.0.0: {} native-promise-only@0.8.1: {} @@ -20320,15 +19898,15 @@ snapshots: netmask@2.0.2: {} - next-rest-framework@6.0.0-beta.4(zod@3.25.75): + next-rest-framework@6.0.0-beta.4(zod@3.25.76): dependencies: chalk: 4.1.2 commander: 10.0.1 - formidable: 3.5.2 + formidable: 3.5.4 lodash: 4.17.21 prettier: 3.0.2 qs: 6.11.2 - zod-to-json-schema: 3.21.4(zod@3.25.75) + zod-to-json-schema: 3.21.4(zod@3.25.76) transitivePeerDependencies: - zod @@ -20344,22 +19922,22 @@ snapshots: next-tick@1.1.0: {} - next-translate@2.6.2(next@15.3.4(@babel/core@7.26.9)(@opentelemetry/api@1.9.0)(@playwright/test@1.51.1)(react-dom@19.1.0(react@19.1.0))(react@19.1.0)(sass@1.89.2))(react@19.1.0): + next-translate@2.6.2(next@15.3.4(@babel/core@7.28.0)(@opentelemetry/api@1.9.0)(react-dom@19.1.0(react@19.1.0))(react@19.1.0)(sass@1.89.2))(react@19.1.0): dependencies: - next: 15.3.4(@babel/core@7.26.9)(@opentelemetry/api@1.9.0)(@playwright/test@1.51.1)(react-dom@19.1.0(react@19.1.0))(react@19.1.0)(sass@1.89.2) + next: 15.3.4(@babel/core@7.28.0)(@opentelemetry/api@1.9.0)(react-dom@19.1.0(react@19.1.0))(react@19.1.0)(sass@1.89.2) react: 19.1.0 - next@15.3.4(@babel/core@7.26.9)(@opentelemetry/api@1.9.0)(@playwright/test@1.51.1)(react-dom@19.1.0(react@19.1.0))(react@19.1.0)(sass@1.89.2): + next@15.3.4(@babel/core@7.28.0)(@opentelemetry/api@1.9.0)(react-dom@19.1.0(react@19.1.0))(react@19.1.0)(sass@1.89.2): dependencies: '@next/env': 15.3.4 '@swc/counter': 0.1.3 '@swc/helpers': 0.5.15 busboy: 1.6.0 - caniuse-lite: 1.0.30001723 + caniuse-lite: 1.0.30001727 postcss: 8.4.31 react: 19.1.0 react-dom: 19.1.0(react@19.1.0) - styled-jsx: 5.1.6(@babel/core@7.26.9)(react@19.1.0) + styled-jsx: 5.1.6(@babel/core@7.28.0)(react@19.1.0) optionalDependencies: '@next/swc-darwin-arm64': 15.3.4 '@next/swc-darwin-x64': 15.3.4 @@ -20370,9 +19948,8 @@ snapshots: '@next/swc-win32-arm64-msvc': 15.3.4 '@next/swc-win32-x64-msvc': 15.3.4 '@opentelemetry/api': 1.9.0 - '@playwright/test': 1.51.1 sass: 1.89.2 - sharp: 0.34.2 + sharp: 0.34.3 transitivePeerDependencies: - '@babel/core' - babel-plugin-macros @@ -20382,24 +19959,18 @@ snapshots: lower-case: 2.0.2 tslib: 2.8.1 - node-abi@3.74.0: + node-abi@3.75.0: dependencies: semver: 7.7.2 node-addon-api@7.1.1: {} - node-addon-api@8.3.1: {} + node-addon-api@8.4.0: {} node-cleanup@2.1.2: {} node-domexception@1.0.0: {} - node-fetch@2.6.7(encoding@0.1.13): - dependencies: - whatwg-url: 5.0.0 - optionalDependencies: - encoding: 0.1.13 - node-fetch@2.7.0(encoding@0.1.13): dependencies: whatwg-url: 5.0.0 @@ -20416,13 +19987,13 @@ snapshots: buffer: 6.0.3 es6-promise: 4.2.8 lodash: 4.17.21 - long: 5.3.1 + long: 5.3.2 node-forge: 1.3.1 pako: 2.1.0 process: 0.11.10 uuid: 9.0.1 - node-mocks-http@1.17.2(@types/express@4.17.23)(@types/node@18.19.115): + node-mocks-http@1.17.2(@types/express@4.17.23)(@types/node@18.19.118): dependencies: accepts: 1.3.8 content-disposition: 0.5.4 @@ -20436,7 +20007,7 @@ snapshots: type-is: 1.6.18 optionalDependencies: '@types/express': 4.17.23 - '@types/node': 18.19.115 + '@types/node': 18.19.118 node-releases@2.0.19: {} @@ -20539,7 +20110,7 @@ snapshots: obuf@1.1.2: {} - ollama@0.5.15: + ollama@0.5.16: dependencies: whatwg-fetch: 3.6.20 @@ -20570,25 +20141,25 @@ snapshots: is-inside-container: 1.0.0 is-wsl: 3.1.0 - openai@4.104.0(encoding@0.1.13)(ws@8.18.3)(zod@3.25.67): + openai@4.104.0(encoding@0.1.13)(ws@8.18.3)(zod@3.25.76): dependencies: - '@types/node': 18.19.115 + '@types/node': 18.19.118 '@types/node-fetch': 2.6.12 abort-controller: 3.0.0 agentkeepalive: 4.6.0 form-data-encoder: 1.7.2 formdata-node: 4.4.1 - node-fetch: 2.6.7(encoding@0.1.13) + node-fetch: 2.7.0(encoding@0.1.13) optionalDependencies: ws: 8.18.3 - zod: 3.25.67 + zod: 3.25.76 transitivePeerDependencies: - encoding - openai@5.7.0(ws@8.18.3)(zod@3.25.67): + openai@5.9.0(ws@8.18.3)(zod@3.25.76): optionalDependencies: ws: 8.18.3 - zod: 3.25.67 + zod: 3.25.76 opener@1.5.2: {} @@ -20666,9 +20237,9 @@ snapshots: pac-proxy-agent@7.2.0: dependencies: '@tootallnate/quickjs-emscripten': 0.23.0 - agent-base: 7.1.3 + agent-base: 7.1.4 debug: 4.4.1 - get-uri: 6.0.4 + get-uri: 6.0.5 http-proxy-agent: 7.0.2 https-proxy-agent: 7.0.6 pac-resolver: 7.0.1 @@ -20681,8 +20252,6 @@ snapshots: degenerator: 5.0.1 netmask: 2.0.2 - package-json-from-dist@1.0.0: {} - package-json-from-dist@1.0.1: {} package-manager-detector@1.3.0: {} @@ -20707,7 +20276,7 @@ snapshots: parse-domain@5.0.0(encoding@0.1.13): dependencies: is-ip: 3.1.0 - node-fetch: 2.6.7(encoding@0.1.13) + node-fetch: 2.7.0(encoding@0.1.13) punycode: 2.3.1 transitivePeerDependencies: - encoding @@ -20717,7 +20286,7 @@ snapshots: '@types/unist': 2.0.11 character-entities-legacy: 3.0.0 character-reference-invalid: 2.0.1 - decode-named-character-reference: 1.1.0 + decode-named-character-reference: 1.2.0 is-alphanumerical: 2.0.1 is-decimal: 2.0.1 is-hexadecimal: 2.0.1 @@ -20750,14 +20319,10 @@ snapshots: parse5-htmlparser2-tree-adapter@7.1.0: dependencies: domhandler: 5.0.3 - parse5: 7.2.1 + parse5: 7.3.0 parse5@6.0.1: {} - parse5@7.2.1: - dependencies: - entities: 4.5.0 - parse5@7.3.0: dependencies: entities: 6.0.1 @@ -20885,7 +20450,7 @@ snapshots: path-type@4.0.0: {} - path-type@5.0.0: {} + path-type@6.0.0: {} pathe@2.0.3: {} @@ -20898,7 +20463,7 @@ snapshots: pdfjs-dist@4.10.38: optionalDependencies: - '@napi-rs/canvas': 0.1.69 + '@napi-rs/canvas': 0.1.73 performance-now@2.1.0: {} @@ -20915,8 +20480,6 @@ snapshots: pg-protocol@1.10.3: {} - pg-protocol@1.8.0: {} - pg-types@2.2.0: dependencies: pg-int8: 1.0.1 @@ -20941,8 +20504,6 @@ snapshots: pick-by-alias@1.2.0: {} - picocolors@1.1.0: {} - picocolors@1.1.1: {} picomatch@2.3.1: {} @@ -20959,7 +20520,7 @@ snapshots: pinkie@2.0.4: {} - pirates@4.0.6: {} + pirates@4.0.7: {} pkg-dir@4.2.0: dependencies: @@ -20971,23 +20532,13 @@ snapshots: mlly: 1.7.4 pathe: 2.0.3 - pkg-types@2.1.0: + pkg-types@2.2.0: dependencies: confbox: 0.2.2 exsolve: 1.0.7 pathe: 2.0.3 - playwright-core@1.51.1: - optional: true - - playwright@1.51.1: - dependencies: - playwright-core: 1.51.1 - optionalDependencies: - fsevents: 2.3.2 - optional: true - - plotly.js@2.35.3(@rspack/core@1.4.4(@swc/helpers@0.5.15))(mapbox-gl@1.13.3)(webpack@5.99.5): + plotly.js@2.35.3(@rspack/core@1.4.6(@swc/helpers@0.5.15))(mapbox-gl@1.13.3)(webpack@5.100.1): dependencies: '@plotly/d3': 3.8.2 '@plotly/d3-sankey': 0.7.2 @@ -21003,7 +20554,7 @@ snapshots: color-parse: 2.0.0 color-rgba: 2.1.1 country-regex: 1.1.0 - css-loader: 7.1.2(@rspack/core@1.4.4(@swc/helpers@0.5.15))(webpack@5.99.5) + css-loader: 7.1.2(@rspack/core@1.4.6(@swc/helpers@0.5.15))(webpack@5.100.1) d3-force: 1.2.1 d3-format: 1.4.5 d3-geo: 1.12.1 @@ -21033,14 +20584,14 @@ snapshots: regl-scatter2d: 3.3.1 regl-splom: 1.0.14 strongly-connected-components: 1.0.1 - style-loader: 4.0.0(webpack@5.99.5) + style-loader: 4.0.0(webpack@5.100.1) superscript-text: 1.0.0 svg-path-sdf: 1.1.3 tinycolor2: 1.6.0 to-px: 1.0.1 topojson-client: 3.1.0 webgl-context: 2.2.0 - world-calendars: 1.0.3 + world-calendars: 1.0.4 transitivePeerDependencies: - '@rspack/core' - mapbox-gl @@ -21049,7 +20600,7 @@ snapshots: plur@4.0.0: dependencies: - irregular-plurals: 3.3.0 + irregular-plurals: 3.5.0 point-in-polygon@1.1.0: {} @@ -21066,26 +20617,26 @@ snapshots: possible-typed-array-names@1.1.0: {} - postcss-modules-extract-imports@3.1.0(postcss@8.5.3): + postcss-modules-extract-imports@3.1.0(postcss@8.5.6): dependencies: - postcss: 8.5.3 + postcss: 8.5.6 - postcss-modules-local-by-default@4.2.0(postcss@8.5.3): + postcss-modules-local-by-default@4.2.0(postcss@8.5.6): dependencies: - icss-utils: 5.1.0(postcss@8.5.3) - postcss: 8.5.3 + icss-utils: 5.1.0(postcss@8.5.6) + postcss: 8.5.6 postcss-selector-parser: 7.1.0 postcss-value-parser: 4.2.0 - postcss-modules-scope@3.2.1(postcss@8.5.3): + postcss-modules-scope@3.2.1(postcss@8.5.6): dependencies: - postcss: 8.5.3 + postcss: 8.5.6 postcss-selector-parser: 7.1.0 - postcss-modules-values@4.0.0(postcss@8.5.3): + postcss-modules-values@4.0.0(postcss@8.5.6): dependencies: - icss-utils: 5.1.0(postcss@8.5.3) - postcss: 8.5.3 + icss-utils: 5.1.0(postcss@8.5.6) + postcss: 8.5.6 postcss-selector-parser@7.1.0: dependencies: @@ -21100,7 +20651,7 @@ snapshots: picocolors: 1.1.1 source-map-js: 1.2.1 - postcss@8.5.3: + postcss@8.5.6: dependencies: nanoid: 3.3.11 picocolors: 1.1.1 @@ -21122,14 +20673,14 @@ snapshots: prebuild-install@7.1.3: dependencies: - detect-libc: 2.0.3 + detect-libc: 2.0.4 expand-template: 2.0.3 github-from-package: 0.0.0 minimist: 1.2.8 mkdirp-classic: 0.5.3 napi-build-utils: 2.0.0 - node-abi: 3.74.0 - pump: 3.0.2 + node-abi: 3.75.0 + pump: 3.0.3 rc: 1.2.8 simple-get: 4.0.1 tar-fs: 2.1.3 @@ -21192,7 +20743,7 @@ snapshots: eventemitter3: 5.0.1 forwarded-for: 1.1.0 fusing: 1.0.0 - nanoid: 3.3.8 + nanoid: 3.3.11 setheader: 1.0.2 ultron: 1.1.1 @@ -21237,9 +20788,9 @@ snapshots: proto3-json-serializer@2.0.2: dependencies: - protobufjs: 7.4.0 + protobufjs: 7.5.3 - protobufjs@7.4.0: + protobufjs@7.5.3: dependencies: '@protobufjs/aspromise': 1.1.2 '@protobufjs/base64': 1.1.2 @@ -21251,8 +20802,8 @@ snapshots: '@protobufjs/path': 1.1.2 '@protobufjs/pool': 1.1.0 '@protobufjs/utf8': 1.1.0 - '@types/node': 18.19.115 - long: 5.3.1 + '@types/node': 18.19.118 + long: 5.3.2 protocol-buffers-schema@3.6.0: {} @@ -21263,7 +20814,7 @@ snapshots: proxy-agent@6.5.0: dependencies: - agent-base: 7.1.3 + agent-base: 7.1.4 debug: 4.4.1 http-proxy-agent: 7.0.2 https-proxy-agent: 7.0.6 @@ -21279,9 +20830,9 @@ snapshots: prr@1.0.1: optional: true - pump@3.0.2: + pump@3.0.3: dependencies: - end-of-stream: 1.4.4 + end-of-stream: 1.4.5 once: 1.4.0 punycode@2.3.1: {} @@ -21290,12 +20841,16 @@ snapshots: qs@6.11.2: dependencies: - side-channel: 1.0.6 + side-channel: 1.1.0 qs@6.13.0: dependencies: side-channel: 1.1.0 + qs@6.14.0: + dependencies: + side-channel: 1.1.0 + quansync@0.2.10: {} querystringify@2.2.0: {} @@ -21308,7 +20863,7 @@ snapshots: quickselect@3.0.0: {} - r-json@1.3.0: + r-json@1.3.1: dependencies: w-json: 1.3.10 @@ -21335,11 +20890,11 @@ snapshots: raw-loader@0.5.1: {} - raw-loader@4.0.2(webpack@5.99.5): + raw-loader@4.0.2(webpack@5.100.1): dependencies: loader-utils: 2.0.4 schema-utils: 3.3.0 - webpack: 5.99.5 + webpack: 5.100.1 rc-animate@3.1.1(react-dom@19.1.0(react@19.1.0))(react@19.1.0): dependencies: @@ -21570,7 +21125,7 @@ snapshots: rc-motion: 2.9.5(react-dom@19.1.0(react@19.1.0))(react@19.1.0) rc-overflow: 1.4.1(react-dom@19.1.0(react@19.1.0))(react@19.1.0) rc-util: 5.44.4(react-dom@19.1.0(react@19.1.0))(react@19.1.0) - rc-virtual-list: 3.18.6(react-dom@19.1.0(react@19.1.0))(react@19.1.0) + rc-virtual-list: 3.19.1(react-dom@19.1.0(react@19.1.0))(react@19.1.0) react: 19.1.0 react-dom: 19.1.0(react@19.1.0) @@ -21656,7 +21211,7 @@ snapshots: classnames: 2.5.1 rc-motion: 2.9.5(react-dom@19.1.0(react@19.1.0))(react@19.1.0) rc-util: 5.44.4(react-dom@19.1.0(react@19.1.0))(react@19.1.0) - rc-virtual-list: 3.18.5(react-dom@19.1.0(react@19.1.0))(react@19.1.0) + rc-virtual-list: 3.19.1(react-dom@19.1.0(react@19.1.0))(react@19.1.0) react: 19.1.0 react-dom: 19.1.0(react@19.1.0) @@ -21683,24 +21238,6 @@ snapshots: react-dom: 19.1.0(react@19.1.0) react-is: 18.3.1 - rc-virtual-list@3.18.5(react-dom@19.1.0(react@19.1.0))(react@19.1.0): - dependencies: - '@babel/runtime': 7.27.6 - classnames: 2.5.1 - rc-resize-observer: 1.4.3(react-dom@19.1.0(react@19.1.0))(react@19.1.0) - rc-util: 5.44.4(react-dom@19.1.0(react@19.1.0))(react@19.1.0) - react: 19.1.0 - react-dom: 19.1.0(react@19.1.0) - - rc-virtual-list@3.18.6(react-dom@19.1.0(react@19.1.0))(react@19.1.0): - dependencies: - '@babel/runtime': 7.27.6 - classnames: 2.5.1 - rc-resize-observer: 1.4.3(react-dom@19.1.0(react@19.1.0))(react@19.1.0) - rc-util: 5.44.4(react-dom@19.1.0(react@19.1.0))(react@19.1.0) - react: 19.1.0 - react-dom: 19.1.0(react@19.1.0) - rc-virtual-list@3.19.1(react-dom@19.1.0(react@19.1.0))(react@19.1.0): dependencies: '@babel/runtime': 7.27.6 @@ -21767,7 +21304,7 @@ snapshots: react: 19.1.0 react-dom: 19.1.0(react@19.1.0) - react-easy-crop@5.4.1(react-dom@19.1.0(react@19.1.0))(react@19.1.0): + react-easy-crop@5.5.0(react-dom@19.1.0(react@19.1.0))(react@19.1.0): dependencies: normalize-wheel: 1.0.1 react: 19.1.0 @@ -21824,9 +21361,9 @@ snapshots: react-lifecycles-compat@3.0.4: {} - react-plotly.js@2.6.0(plotly.js@2.35.3(@rspack/core@1.4.4(@swc/helpers@0.5.15))(mapbox-gl@1.13.3)(webpack@5.99.5))(react@19.1.0): + react-plotly.js@2.6.0(plotly.js@2.35.3(@rspack/core@1.4.6(@swc/helpers@0.5.15))(mapbox-gl@1.13.3)(webpack@5.100.1))(react@19.1.0): dependencies: - plotly.js: 2.35.3(@rspack/core@1.4.4(@swc/helpers@0.5.15))(mapbox-gl@1.13.3)(webpack@5.99.5) + plotly.js: 2.35.3(@rspack/core@1.4.6(@swc/helpers@0.5.15))(mapbox-gl@1.13.3)(webpack@5.100.1) prop-types: 15.8.1 react: 19.1.0 @@ -21887,7 +21424,7 @@ snapshots: read-pkg@5.2.0: dependencies: - '@types/normalize-package-data': 2.4.1 + '@types/normalize-package-data': 2.4.4 normalize-package-data: 2.5.0 parse-json: 5.2.0 type-fest: 0.6.0 @@ -21936,7 +21473,7 @@ snapshots: redux@4.2.1: dependencies: - '@babel/runtime': 7.25.6 + '@babel/runtime': 7.27.6 reflect-metadata@0.2.2: {} @@ -21960,8 +21497,6 @@ snapshots: regenerator-runtime@0.11.1: {} - regenerator-runtime@0.14.1: {} - regexp.prototype.flags@1.5.4: dependencies: call-bind: 1.0.8 @@ -22088,7 +21623,7 @@ snapshots: dependencies: protocol-buffers-schema: 3.6.0 - resolve.exports@2.0.2: {} + resolve.exports@2.0.3: {} resolve@0.6.3: {} @@ -22098,12 +21633,6 @@ snapshots: path-parse: 1.0.7 supports-preserve-symlinks-flag: 1.0.0 - resolve@1.22.8: - dependencies: - is-core-module: 2.16.1 - path-parse: 1.0.7 - supports-preserve-symlinks-flag: 1.0.0 - resolve@2.0.0-next.5: dependencies: is-core-module: 2.16.1 @@ -22126,7 +21655,7 @@ snapshots: retry@0.13.1: {} - reusify@1.0.4: {} + reusify@1.1.0: {} right-now@1.0.0: {} @@ -22139,6 +21668,10 @@ snapshots: dependencies: glob: 7.2.3 + rimraf@2.7.1: + dependencies: + glob: 7.2.3 + rimraf@3.0.2: dependencies: glob: 7.2.3 @@ -22210,20 +21743,20 @@ snapshots: htmlparser2: 8.0.2 is-plain-object: 5.0.0 parse-srcset: 1.0.2 - postcss: 8.5.3 + postcss: 8.5.6 - sass-loader@16.0.5(@rspack/core@1.4.4(@swc/helpers@0.5.15))(sass@1.89.2)(webpack@5.99.5): + sass-loader@16.0.5(@rspack/core@1.4.6(@swc/helpers@0.5.15))(sass@1.89.2)(webpack@5.100.1): dependencies: neo-async: 2.6.2 optionalDependencies: - '@rspack/core': 1.4.4(@swc/helpers@0.5.15) + '@rspack/core': 1.4.6(@swc/helpers@0.5.15) sass: 1.89.2 - webpack: 5.99.5 + webpack: 5.100.1 sass@1.89.2: dependencies: chokidar: 4.0.3 - immutable: 5.1.1 + immutable: 5.1.3 source-map-js: 1.2.1 optionalDependencies: '@parcel/watcher': 2.5.1 @@ -22242,13 +21775,6 @@ snapshots: ajv: 6.12.6 ajv-keywords: 3.5.2(ajv@6.12.6) - schema-utils@4.3.0: - dependencies: - '@types/json-schema': 7.0.15 - ajv: 8.17.1 - ajv-formats: 2.1.1(ajv@8.17.1) - ajv-keywords: 5.1.0(ajv@8.17.1) - schema-utils@4.3.2: dependencies: '@types/json-schema': 7.0.15 @@ -22270,17 +21796,13 @@ snapshots: selfsigned@2.4.1: dependencies: - '@types/node-forge': 1.3.11 + '@types/node-forge': 1.3.13 node-forge: 1.3.1 semver@5.7.2: {} semver@6.3.1: {} - semver@7.6.3: {} - - semver@7.7.1: {} - semver@7.7.2: {} send@0.19.0: @@ -22370,33 +21892,34 @@ snapshots: shallowequal@1.1.0: {} - sharp@0.34.2: + sharp@0.34.3: dependencies: color: 4.2.3 detect-libc: 2.0.4 semver: 7.7.2 optionalDependencies: - '@img/sharp-darwin-arm64': 0.34.2 - '@img/sharp-darwin-x64': 0.34.2 - '@img/sharp-libvips-darwin-arm64': 1.1.0 - '@img/sharp-libvips-darwin-x64': 1.1.0 - '@img/sharp-libvips-linux-arm': 1.1.0 - '@img/sharp-libvips-linux-arm64': 1.1.0 - '@img/sharp-libvips-linux-ppc64': 1.1.0 - '@img/sharp-libvips-linux-s390x': 1.1.0 - '@img/sharp-libvips-linux-x64': 1.1.0 - '@img/sharp-libvips-linuxmusl-arm64': 1.1.0 - '@img/sharp-libvips-linuxmusl-x64': 1.1.0 - '@img/sharp-linux-arm': 0.34.2 - '@img/sharp-linux-arm64': 0.34.2 - '@img/sharp-linux-s390x': 0.34.2 - '@img/sharp-linux-x64': 0.34.2 - '@img/sharp-linuxmusl-arm64': 0.34.2 - '@img/sharp-linuxmusl-x64': 0.34.2 - '@img/sharp-wasm32': 0.34.2 - '@img/sharp-win32-arm64': 0.34.2 - '@img/sharp-win32-ia32': 0.34.2 - '@img/sharp-win32-x64': 0.34.2 + '@img/sharp-darwin-arm64': 0.34.3 + '@img/sharp-darwin-x64': 0.34.3 + '@img/sharp-libvips-darwin-arm64': 1.2.0 + '@img/sharp-libvips-darwin-x64': 1.2.0 + '@img/sharp-libvips-linux-arm': 1.2.0 + '@img/sharp-libvips-linux-arm64': 1.2.0 + '@img/sharp-libvips-linux-ppc64': 1.2.0 + '@img/sharp-libvips-linux-s390x': 1.2.0 + '@img/sharp-libvips-linux-x64': 1.2.0 + '@img/sharp-libvips-linuxmusl-arm64': 1.2.0 + '@img/sharp-libvips-linuxmusl-x64': 1.2.0 + '@img/sharp-linux-arm': 0.34.3 + '@img/sharp-linux-arm64': 0.34.3 + '@img/sharp-linux-ppc64': 0.34.3 + '@img/sharp-linux-s390x': 0.34.3 + '@img/sharp-linux-x64': 0.34.3 + '@img/sharp-linuxmusl-arm64': 0.34.3 + '@img/sharp-linuxmusl-x64': 0.34.3 + '@img/sharp-wasm32': 0.34.3 + '@img/sharp-win32-arm64': 0.34.3 + '@img/sharp-win32-ia32': 0.34.3 + '@img/sharp-win32-x64': 0.34.3 optional: true shebang-command@2.0.0: @@ -22431,13 +21954,6 @@ snapshots: object-inspect: 1.13.4 side-channel-map: 1.0.1 - side-channel@1.0.6: - dependencies: - call-bind: 1.0.8 - es-errors: 1.3.0 - get-intrinsic: 1.3.0 - object-inspect: 1.13.4 - side-channel@1.1.0: dependencies: es-errors: 1.3.0 @@ -22464,7 +21980,7 @@ snapshots: dependencies: is-arrayish: 0.3.2 - simple-wcswidth@1.1.1: {} + simple-wcswidth@1.1.2: {} sirv@2.0.4: dependencies: @@ -22541,25 +22057,25 @@ snapshots: socks-proxy-agent@8.0.5: dependencies: - agent-base: 7.1.3 + agent-base: 7.1.4 debug: 4.4.1 - socks: 2.8.4 + socks: 2.8.6 transitivePeerDependencies: - supports-color - socks@2.8.4: + socks@2.8.6: dependencies: ip-address: 9.0.5 smart-buffer: 4.2.0 source-map-js@1.2.1: {} - source-map-loader@3.0.2(webpack@5.99.5): + source-map-loader@3.0.2(webpack@5.100.1): dependencies: abab: 2.0.6 iconv-lite: 0.6.3 source-map-js: 1.2.1 - webpack: 5.99.5 + webpack: 5.100.1 source-map-support@0.5.13: dependencies: @@ -22577,19 +22093,19 @@ snapshots: spawn-command@0.0.2: {} - spdx-correct@3.1.1: + spdx-correct@3.2.0: dependencies: spdx-expression-parse: 3.0.1 - spdx-license-ids: 3.0.12 + spdx-license-ids: 3.0.21 - spdx-exceptions@2.3.0: {} + spdx-exceptions@2.5.0: {} spdx-expression-parse@3.0.1: dependencies: - spdx-exceptions: 2.3.0 - spdx-license-ids: 3.0.12 + spdx-exceptions: 2.5.0 + spdx-license-ids: 3.0.21 - spdx-license-ids@3.0.12: {} + spdx-license-ids@3.0.21: {} spdy-transport@3.0.0: dependencies: @@ -22715,7 +22231,7 @@ snapshots: call-bound: 1.0.4 define-data-property: 1.1.4 define-properties: 1.2.1 - es-abstract: 1.23.9 + es-abstract: 1.24.0 es-object-atoms: 1.1.1 has-property-descriptors: 1.0.2 @@ -22771,8 +22287,8 @@ snapshots: stripe@17.7.0: dependencies: - '@types/node': 18.19.115 - qs: 6.13.0 + '@types/node': 18.19.118 + qs: 6.14.0 strnum@1.1.2: {} @@ -22784,15 +22300,15 @@ snapshots: stubs@3.0.0: {} - style-loader@2.0.0(webpack@5.99.5): + style-loader@2.0.0(webpack@5.100.1): dependencies: loader-utils: 2.0.4 schema-utils: 3.3.0 - webpack: 5.99.5 + webpack: 5.100.1 - style-loader@4.0.0(webpack@5.99.5): + style-loader@4.0.0(webpack@5.100.1): dependencies: - webpack: 5.99.5 + webpack: 5.100.1 style-to-js@1.1.16: dependencies: @@ -22802,12 +22318,12 @@ snapshots: dependencies: inline-style-parser: 0.2.4 - styled-jsx@5.1.6(@babel/core@7.26.9)(react@19.1.0): + styled-jsx@5.1.6(@babel/core@7.28.0)(react@19.1.0): dependencies: client-only: 0.0.1 react: 19.1.0 optionalDependencies: - '@babel/core': 7.26.9 + '@babel/core': 7.28.0 stylis@4.3.6: {} @@ -22825,10 +22341,6 @@ snapshots: superscript-text@1.0.0: {} - supports-color@5.5.0: - dependencies: - has-flag: 3.0.0 - supports-color@7.2.0: dependencies: has-flag: 4.0.0 @@ -22837,7 +22349,7 @@ snapshots: dependencies: has-flag: 4.0.0 - supports-hyperlinks@2.2.0: + supports-hyperlinks@2.3.0: dependencies: has-flag: 4.0.0 supports-color: 7.2.0 @@ -22881,13 +22393,13 @@ snapshots: dependencies: chownr: 1.1.4 mkdirp-classic: 0.5.3 - pump: 3.0.2 + pump: 3.0.3 tar-stream: 2.2.0 tar-stream@2.2.0: dependencies: bl: 4.1.0 - end-of-stream: 1.4.4 + end-of-stream: 1.4.5 fs-constants: 1.0.0 inherits: 2.0.4 readable-stream: 3.6.2 @@ -22912,38 +22424,31 @@ snapshots: mkdirp: 0.5.6 rimraf: 2.6.3 - terser-webpack-plugin@5.3.14(uglify-js@3.19.3)(webpack@5.99.5(uglify-js@3.19.3)): + terser-webpack-plugin@5.3.14(uglify-js@3.19.3)(webpack@5.100.1(uglify-js@3.19.3)): dependencies: '@jridgewell/trace-mapping': 0.3.29 jest-worker: 27.5.1 schema-utils: 4.3.2 serialize-javascript: 6.0.2 terser: 5.43.1 - webpack: 5.99.5(uglify-js@3.19.3) + webpack: 5.100.1(uglify-js@3.19.3) optionalDependencies: uglify-js: 3.19.3 - terser-webpack-plugin@5.3.14(webpack@5.99.5): + terser-webpack-plugin@5.3.14(webpack@5.100.1): dependencies: '@jridgewell/trace-mapping': 0.3.29 jest-worker: 27.5.1 schema-utils: 4.3.2 serialize-javascript: 6.0.2 terser: 5.43.1 - webpack: 5.99.5 + webpack: 5.100.1 terser@4.8.1: dependencies: - acorn: 8.14.1 + acorn: 8.15.0 commander: 2.20.3 source-map: 0.6.1 - source-map-support: 0.5.13 - - terser@5.39.0: - dependencies: - '@jridgewell/source-map': 0.3.6 - acorn: 8.14.1 - commander: 2.20.3 source-map-support: 0.5.21 terser@5.43.1: @@ -23004,9 +22509,9 @@ snapshots: tinyexec@1.0.1: {} - tinyglobby@0.2.13: + tinyglobby@0.2.14: dependencies: - fdir: 6.4.4(picomatch@4.0.2) + fdir: 6.4.6(picomatch@4.0.2) picomatch: 4.0.2 tinyqueue@2.0.3: {} @@ -23045,7 +22550,7 @@ snapshots: toidentifier@1.0.1: {} - token-types@6.0.0: + token-types@6.0.3: dependencies: '@tokenizer/token': 0.3.0 ieee754: 1.2.1 @@ -23066,7 +22571,7 @@ snapshots: dependencies: punycode: 2.3.1 - tree-dump@1.0.2(tslib@2.8.1): + tree-dump@1.0.3(tslib@2.8.1): dependencies: tslib: 2.8.1 @@ -23084,32 +22589,12 @@ snapshots: ts-dedent@2.2.0: {} - ts-jest@29.4.0(@babel/core@7.26.9)(@jest/transform@29.7.0)(@jest/types@30.0.1)(babel-jest@29.7.0(@babel/core@7.26.9))(jest-util@30.0.2)(jest@29.7.0(@types/node@18.19.115))(typescript@5.8.3): - dependencies: - bs-logger: 0.2.6 - ejs: 3.1.10 - fast-json-stable-stringify: 2.1.0 - jest: 29.7.0(@types/node@18.19.115) - json5: 2.2.3 - lodash.memoize: 4.1.2 - make-error: 1.3.6 - semver: 7.7.2 - type-fest: 4.41.0 - typescript: 5.8.3 - yargs-parser: 21.1.1 - optionalDependencies: - '@babel/core': 7.26.9 - '@jest/transform': 29.7.0 - '@jest/types': 30.0.1 - babel-jest: 29.7.0(@babel/core@7.26.9) - jest-util: 30.0.2 - - ts-jest@29.4.0(@babel/core@7.26.9)(@jest/transform@29.7.0)(@jest/types@30.0.1)(babel-jest@29.7.0(@babel/core@7.26.9))(jest-util@30.0.2)(jest@29.7.0(@types/node@24.0.10))(typescript@5.8.3): + ts-jest@29.4.0(@babel/core@7.28.0)(@jest/transform@29.7.0)(@jest/types@30.0.1)(babel-jest@29.7.0(@babel/core@7.28.0))(jest-util@30.0.2)(jest@29.7.0(@types/node@18.19.118))(typescript@5.8.3): dependencies: bs-logger: 0.2.6 ejs: 3.1.10 fast-json-stable-stringify: 2.1.0 - jest: 29.7.0(@types/node@24.0.10) + jest: 29.7.0(@types/node@18.19.118) json5: 2.2.3 lodash.memoize: 4.1.2 make-error: 1.3.6 @@ -23118,10 +22603,10 @@ snapshots: typescript: 5.8.3 yargs-parser: 21.1.1 optionalDependencies: - '@babel/core': 7.26.9 + '@babel/core': 7.28.0 '@jest/transform': 29.7.0 '@jest/types': 30.0.1 - babel-jest: 29.7.0(@babel/core@7.26.9) + babel-jest: 29.7.0(@babel/core@7.28.0) jest-util: 30.0.2 tsd@0.22.0: @@ -23163,8 +22648,6 @@ snapshots: type-fest@3.13.1: {} - type-fest@4.32.0: {} - type-fest@4.41.0: {} type-is@1.6.18: @@ -23247,9 +22730,7 @@ snapshots: undici-types@5.26.5: {} - undici-types@7.8.0: {} - - unicorn-magic@0.1.0: {} + unicorn-magic@0.3.0: {} unified@11.0.5: dependencies: @@ -23307,7 +22788,7 @@ snapshots: '@types/cookie': 0.3.3 cookie: 0.4.2 - universalify@2.0.0: {} + universalify@2.0.1: {} unpipe@1.0.0: {} @@ -23327,12 +22808,12 @@ snapshots: dependencies: punycode: 2.3.1 - url-loader@4.1.1(webpack@5.99.5(uglify-js@3.19.3)): + url-loader@4.1.1(webpack@5.100.1(uglify-js@3.19.3)): dependencies: loader-utils: 2.0.4 mime-types: 2.1.35 schema-utils: 3.3.0 - webpack: 5.99.5(uglify-js@3.19.3) + webpack: 5.100.1(uglify-js@3.19.3) url-parse-lax@1.0.0: dependencies: @@ -23361,7 +22842,7 @@ snapshots: dependencies: react: 19.1.0 - use-isomorphic-layout-effect@1.2.0(@types/react@19.1.8)(react@19.1.0): + use-isomorphic-layout-effect@1.2.1(@types/react@19.1.8)(react@19.1.0): dependencies: react: 19.1.0 optionalDependencies: @@ -23370,7 +22851,7 @@ snapshots: use-latest@1.3.0(@types/react@19.1.8)(react@19.1.0): dependencies: react: 19.1.0 - use-isomorphic-layout-effect: 1.2.0(@types/react@19.1.8)(react@19.1.0) + use-isomorphic-layout-effect: 1.2.1(@types/react@19.1.8)(react@19.1.0) optionalDependencies: '@types/react': 19.1.8 @@ -23408,9 +22889,9 @@ snapshots: uuid@9.0.1: {} - v8-to-istanbul@9.2.0: + v8-to-istanbul@9.3.0: dependencies: - '@jridgewell/trace-mapping': 0.3.25 + '@jridgewell/trace-mapping': 0.3.29 '@types/istanbul-lib-coverage': 2.0.6 convert-source-map: 2.0.0 @@ -23418,7 +22899,7 @@ snapshots: validate-npm-package-license@3.0.4: dependencies: - spdx-correct: 3.1.1 + spdx-correct: 3.2.0 spdx-expression-parse: 3.0.1 validate.io-array@1.0.6: {} @@ -23502,6 +22983,8 @@ snapshots: w-json@1.3.10: {} + w-json@1.3.11: {} + w3c-xmlserializer@5.0.0: dependencies: xml-name-validator: 5.0.0 @@ -23544,7 +23027,7 @@ snapshots: webpack-bundle-analyzer@4.10.2: dependencies: '@discoveryjs/json-ext': 0.5.7 - acorn: 8.14.1 + acorn: 8.15.0 acorn-walk: 8.3.4 commander: 7.2.0 debounce: 1.2.1 @@ -23559,29 +23042,29 @@ snapshots: - bufferutil - utf-8-validate - webpack-dev-middleware@7.4.2(webpack@5.99.5(uglify-js@3.19.3)): + webpack-dev-middleware@7.4.2(webpack@5.100.1(uglify-js@3.19.3)): dependencies: colorette: 2.0.20 - memfs: 4.17.0 + memfs: 4.17.2 mime-types: 2.1.35 on-finished: 2.4.1 range-parser: 1.2.1 - schema-utils: 4.3.0 + schema-utils: 4.3.2 optionalDependencies: - webpack: 5.99.5(uglify-js@3.19.3) + webpack: 5.100.1(uglify-js@3.19.3) - webpack-dev-middleware@7.4.2(webpack@5.99.5): + webpack-dev-middleware@7.4.2(webpack@5.100.1): dependencies: colorette: 2.0.20 - memfs: 4.17.0 + memfs: 4.17.2 mime-types: 2.1.35 on-finished: 2.4.1 range-parser: 1.2.1 - schema-utils: 4.3.0 + schema-utils: 4.3.2 optionalDependencies: - webpack: 5.99.5 + webpack: 5.100.1 - webpack-dev-server@5.2.2(webpack@5.99.5): + webpack-dev-server@5.2.2(webpack@5.100.1): dependencies: '@types/bonjour': 3.5.13 '@types/connect-history-api-fallback': 1.5.4 @@ -23609,10 +23092,10 @@ snapshots: serve-index: 1.9.1 sockjs: 0.3.24 spdy: 4.0.2 - webpack-dev-middleware: 7.4.2(webpack@5.99.5) + webpack-dev-middleware: 7.4.2(webpack@5.100.1) ws: 8.18.3 optionalDependencies: - webpack: 5.99.5 + webpack: 5.100.1 transitivePeerDependencies: - bufferutil - debug @@ -23631,14 +23114,16 @@ snapshots: webpack-sources@3.3.3: {} - webpack@5.99.5: + webpack@5.100.1: dependencies: '@types/eslint-scope': 3.7.7 '@types/estree': 1.0.8 + '@types/json-schema': 7.0.15 '@webassemblyjs/ast': 1.14.1 '@webassemblyjs/wasm-edit': 1.14.1 '@webassemblyjs/wasm-parser': 1.14.1 acorn: 8.15.0 + acorn-import-phases: 1.0.3(acorn@8.15.0) browserslist: 4.25.1 chrome-trace-event: 1.0.4 enhanced-resolve: 5.18.2 @@ -23653,7 +23138,7 @@ snapshots: neo-async: 2.6.2 schema-utils: 4.3.2 tapable: 2.2.2 - terser-webpack-plugin: 5.3.14(webpack@5.99.5) + terser-webpack-plugin: 5.3.14(webpack@5.100.1) watchpack: 2.4.4 webpack-sources: 3.3.3 transitivePeerDependencies: @@ -23661,14 +23146,16 @@ snapshots: - esbuild - uglify-js - webpack@5.99.5(uglify-js@3.19.3): + webpack@5.100.1(uglify-js@3.19.3): dependencies: '@types/eslint-scope': 3.7.7 '@types/estree': 1.0.8 + '@types/json-schema': 7.0.15 '@webassemblyjs/ast': 1.14.1 '@webassemblyjs/wasm-edit': 1.14.1 '@webassemblyjs/wasm-parser': 1.14.1 acorn: 8.15.0 + acorn-import-phases: 1.0.3(acorn@8.15.0) browserslist: 4.25.1 chrome-trace-event: 1.0.4 enhanced-resolve: 5.18.2 @@ -23683,7 +23170,7 @@ snapshots: neo-async: 2.6.2 schema-utils: 4.3.2 tapable: 2.2.2 - terser-webpack-plugin: 5.3.14(uglify-js@3.19.3)(webpack@5.99.5(uglify-js@3.19.3)) + terser-webpack-plugin: 5.3.14(uglify-js@3.19.3)(webpack@5.100.1(uglify-js@3.19.3)) watchpack: 2.4.4 webpack-sources: 3.3.3 transitivePeerDependencies: @@ -23781,7 +23268,7 @@ snapshots: wordwrap@1.0.0: {} - world-calendars@1.0.3: + world-calendars@1.0.4: dependencies: object-assign: 4.1.1 @@ -23818,7 +23305,7 @@ snapshots: xdg-basedir@5.1.0: {} - xml-crypto@6.1.0: + xml-crypto@6.1.2: dependencies: '@xmldom/is-dom-node': 1.0.1 '@xmldom/xmldom': 0.8.10 @@ -23867,10 +23354,10 @@ snapshots: xtend@4.0.2: {} - y-protocols@1.0.6(yjs@13.6.24): + y-protocols@1.0.6(yjs@13.6.27): dependencies: - lib0: 0.2.102 - yjs: 13.6.24 + lib0: 0.2.109 + yjs: 13.6.27 y18n@5.0.8: {} @@ -23909,34 +23396,32 @@ snapshots: y18n: 5.0.8 yargs-parser: 21.1.1 - yjs@13.6.24: + yjs@13.6.27: dependencies: - lib0: 0.2.102 + lib0: 0.2.109 yocto-queue@0.1.0: {} zeromq@6.5.0: dependencies: cmake-ts: 1.0.2 - node-addon-api: 8.3.1 + node-addon-api: 8.4.0 zlibjs@0.3.1: {} - zod-to-json-schema@3.21.4(zod@3.25.75): + zod-to-json-schema@3.21.4(zod@3.25.76): dependencies: - zod: 3.25.75 + zod: 3.25.76 - zod-to-json-schema@3.24.5(zod@3.25.67): + zod-to-json-schema@3.24.6(zod@3.25.76): dependencies: - zod: 3.25.67 - - zod@3.25.67: {} + zod: 3.25.76 - zod@3.25.75: {} + zod@3.25.76: {} zstd-napi@0.0.10: dependencies: - '@types/node': 18.19.115 + '@types/node': 18.19.118 node-addon-api: 7.1.1 prebuild-install: 7.1.3 diff --git a/src/packages/server/conat/index.ts b/src/packages/server/conat/index.ts index d78fab9d5f..56e08e45b5 100644 --- a/src/packages/server/conat/index.ts +++ b/src/packages/server/conat/index.ts @@ -4,8 +4,8 @@ import { init as initChangefeedServer } from "@cocalc/database/conat/changefeed- import { init as initLLM } from "./llm"; import { loadConatConfiguration } from "./configuration"; import { createTimeService } from "@cocalc/conat/service/time"; -import { initPersistServer } from "@cocalc/backend/conat/persist"; -import { conatPersistCount, conatApiCount } from "@cocalc/backend/data"; +export { initConatPersist } from "./persist"; +import { conatApiCount } from "@cocalc/backend/data"; export { loadConatConfiguration }; @@ -19,16 +19,6 @@ export async function initConatChangefeedServer() { initChangefeedServer(); } -export async function initConatPersist() { - logger.debug("initPersistServer: sqlite3 stream persistence", { - conatPersistCount, - }); - await loadConatConfiguration(); - for (let i = 0; i < conatPersistCount; i++) { - initPersistServer(); - } -} - export async function initConatApi() { logger.debug("initConatApi: the central api services", { conatApiCount }); await loadConatConfiguration(); diff --git a/src/packages/server/conat/persist/index.ts b/src/packages/server/conat/persist/index.ts new file mode 100644 index 0000000000..2a31892ea9 --- /dev/null +++ b/src/packages/server/conat/persist/index.ts @@ -0,0 +1,36 @@ +import { loadConatConfiguration } from "../configuration"; +import { initPersistServer } from "@cocalc/backend/conat/persist"; +import { conatPersistCount } from "@cocalc/backend/data"; +import { createForkedPersistServer } from "./start-server"; + +import getLogger from "@cocalc/backend/logger"; + +const logger = getLogger("server:conat:persist"); + +export async function initConatPersist() { + logger.debug("initPersistServer: sqlite3 stream persistence", { + conatPersistCount, + }); + if (!conatPersistCount || conatPersistCount <= 1) { + // only 1, so no need to use separate processes + await loadConatConfiguration(); + initPersistServer(); + return; + } + + // more than 1 so no possible value to multiple servers if we don't + // use separate processes + createPersistCluster(); +} + +async function createPersistCluster() { + logger.debug( + "initPersistServer: creating cluster with", + conatPersistCount, + "nodes", + ); + for (let i = 0; i < conatPersistCount; i++) { + logger.debug("initPersistServer: starting node ", i); + createForkedPersistServer(); + } +} diff --git a/src/packages/server/conat/persist/start-persist-node.ts b/src/packages/server/conat/persist/start-persist-node.ts new file mode 100644 index 0000000000..57e27939f2 --- /dev/null +++ b/src/packages/server/conat/persist/start-persist-node.ts @@ -0,0 +1,16 @@ +import { loadConatConfiguration } from "../configuration"; +import { initPersistServer } from "@cocalc/backend/conat/persist"; +import { getLogger } from "@cocalc/backend/logger"; +import { addErrorListeners } from "@cocalc/server/metrics/error-listener"; + +const logger = getLogger("server:conat:persist:start-persist-node"); + +async function main() { + logger.debug("starting forked persist node in process", process.pid); + console.log("starting forked persist node in process", process.pid); + addErrorListeners(); + await loadConatConfiguration(); + await initPersistServer(); +} + +main(); diff --git a/src/packages/server/conat/socketio/cluster.ts b/src/packages/server/conat/socketio/cluster.ts deleted file mode 100644 index 7ada080fd6..0000000000 --- a/src/packages/server/conat/socketio/cluster.ts +++ /dev/null @@ -1,137 +0,0 @@ -/* - -To start this: - - pnpm conat-server - -Run this to be able to use all the cores, since nodejs is (mostly) single threaded. -*/ - -import { init as createConatServer } from "@cocalc/conat/core/server"; -import cluster from "node:cluster"; -import * as http from "http"; -import { availableParallelism } from "os"; -import { - setupMaster as setupPrimarySticky, - setupWorker, -} from "@socket.io/sticky"; -import { createAdapter, setupPrimary } from "@socket.io/cluster-adapter"; -import { getUser, isAllowed } from "./auth"; -import { secureRandomString } from "@cocalc/backend/misc"; -import basePath from "@cocalc/backend/base-path"; -import port from "@cocalc/backend/port"; -import { - conatSocketioCount, - conatClusterPort, - conatClusterHealthPort, -} from "@cocalc/backend/data"; -import { loadConatConfiguration } from "../configuration"; -import { join } from "path"; - -// ensure conat logging, credentials, etc. is setup -import "@cocalc/backend/conat"; - -console.log(`* CONAT Core Pub/Sub Server on port ${port} *`); - -async function primary() { - console.log(`Socketio Server Primary pid=${process.pid} is running`); - - await loadConatConfiguration(); - - const httpServer = http.createServer(); - setupPrimarySticky(httpServer, { - loadBalancingMethod: "least-connection", - }); - - setupPrimary(); - cluster.setupPrimary({ serialization: "advanced" }); - httpServer.listen(getPort()); - - if (conatClusterHealthPort) { - console.log( - `starting /health socketio server on port ${conatClusterHealthPort}`, - ); - const healthServer = http.createServer(); - healthServer.listen(conatClusterHealthPort); - healthServer.on("request", (req, res) => { - // unhealthy if >3 deaths in 1 min - handleHealth(req, res, recentDeaths.length <= 3, "Too many worker exits"); - }); - } - - const numWorkers = conatSocketioCount - ? conatSocketioCount - : availableParallelism(); - const systemAccountPassword = await secureRandomString(32); - for (let i = 0; i < numWorkers; i++) { - cluster.fork({ SYSTEM_ACCOUNT_PASSWORD: systemAccountPassword }); - } - console.log({ numWorkers, port, basePath }); - - const recentDeaths: number[] = []; - cluster.on("exit", (worker) => { - if (conatClusterHealthPort) { - recentDeaths.push(Date.now()); - // Remove entries older than X seconds (e.g. 60s) - while (recentDeaths.length && recentDeaths[0] < Date.now() - 60_000) { - recentDeaths.shift(); - } - } - - console.log(`Worker ${worker.process.pid} died, so making a new one`); - cluster.fork(); - }); -} - -async function worker() { - console.log("BASE_PATH=", process.env.BASE_PATH); - await loadConatConfiguration(); - - const path = join(basePath, "conat"); - console.log(`Socketio Worker pid=${process.pid} started with path=${path}`); - - const httpServer = http.createServer(); - const id = `${cluster.worker?.id ?? ""}`; - const systemAccountPassword = process.env.SYSTEM_ACCOUNT_PASSWORD; - delete process.env.SYSTEM_ACCOUNT_PASSWORD; - - const conatServer = createConatServer({ - path, - httpServer, - id, - getUser, - isAllowed, - systemAccountPassword, - // port -- server needs to know implicitly to make a clients - port: getPort(), - }); - conatServer.io.adapter(createAdapter()); - setupWorker(conatServer.io); -} - -function getPort() { - return conatClusterPort ? conatClusterPort : port; -} - -if (cluster.isPrimary) { - primary(); -} else { - worker(); -} - -function handleHealth( - req: http.IncomingMessage, - res: http.ServerResponse, - status: boolean, - msg?: string, -) { - if (req.method === "GET") { - if (status) { - res.statusCode = 200; - res.end("healthy"); - } else { - res.statusCode = 500; - res.end(msg || "Unhealthy"); - } - } -} diff --git a/src/packages/server/conat/socketio/start-cluster-node.ts b/src/packages/server/conat/socketio/start-cluster-node.ts index 92823a2fb7..54433fa497 100644 --- a/src/packages/server/conat/socketio/start-cluster-node.ts +++ b/src/packages/server/conat/socketio/start-cluster-node.ts @@ -1,3 +1,23 @@ +/* +The code here is run when conatSocketioCount > 1 (i.e., env var CONAT_SOCKETIO_COUNT). +This does NOT use the socketio cluster adapter or the nodejs cluster module. +Every worker process this spawns runs independently after it starts and there is +no single node coordinating communications like with the socketio cluster adapter, +and traffic across the cluster is minimal. This will thus *scale* much better, +though this is also just using normal TCP networking for communication instead of +IPC (like socketios cluster adapter). Also, the traffic between nodes is precisely +what is needed for Conat, so it's really solving a differnet problem than socketio's +cluster adapter, and under the hood what this actually does is much more sophisticated, +with each node maintaining and serving sqlite backed streams of data about their state. + +This code exists mainly for testing and potentially also for scaling cocalc to +more traffic when running on a single machine without Kubernetes. + +One cpu support several hundred simultaneous active connections -- if you want to +have 500 active users all using projects (that also means additional connections) -- +you will definitely need more than one node. +*/ + import "@cocalc/backend/conat/persist"; import { init, type Options } from "@cocalc/conat/core/server"; import { getUser, isAllowed } from "./auth"; @@ -6,7 +26,7 @@ import { loadConatConfiguration } from "../configuration"; import { getLogger } from "@cocalc/backend/logger"; async function main() { - console.log("main"); + console.log("conat server: starting a cluster node"); addErrorListeners(); const configDone = loadConatConfiguration(); diff --git a/src/packages/server/conat/socketio/start-cluster.ts b/src/packages/server/conat/socketio/start-cluster.ts deleted file mode 100644 index da6da241f8..0000000000 --- a/src/packages/server/conat/socketio/start-cluster.ts +++ /dev/null @@ -1,58 +0,0 @@ -import { spawn, ChildProcess } from "node:child_process"; -import { join } from "path"; -import { conatSocketioCount, conatClusterPort } from "@cocalc/backend/data"; -import basePath from "@cocalc/backend/base-path"; - -const servers: { close: Function }[] = []; - -export default function startCluster({ - port = conatClusterPort, - numWorkers = conatSocketioCount, -}: { port?: number; numWorkers?: number } = {}) { - const child: ChildProcess = spawn( - process.argv[0], - [join(__dirname, "cluster.js")], - { - stdio: "inherit", - detached: false, - cwd: __dirname, - env: { - ...process.env, - PORT: `${port}`, - CONAT_SOCKETIO_COUNT: `${numWorkers}`, - BASE_PATH: basePath, - }, - }, - ); - - let closed = false; - const close = () => { - if (closed) return; - closed = true; - if (!child?.pid) return; - try { - process.kill(child.pid, "SIGKILL"); - } catch { - // already dead or not found - } - }; - - const server = { - close, - }; - servers.push(server); - return server; -} - -process.once("exit", () => { - for (const { close } of servers) { - try { - close(); - } catch {} - } -}); -["SIGINT", "SIGTERM", "SIGQUIT"].forEach((sig) => { - process.once(sig, () => { - process.exit(); - }); -}); diff --git a/src/packages/server/package.json b/src/packages/server/package.json index 10173819ce..fd46d02793 100644 --- a/src/packages/server/package.json +++ b/src/packages/server/package.json @@ -37,8 +37,7 @@ "tsc": "../node_modules/.bin/tsc --watch --pretty --preserveWatchOutput ", "test": "TZ=UTC jest --forceExit --runInBand", "depcheck": "pnpx depcheck", - "prepublishOnly": "test", - "conat-server": "node ./dist/conat/socketio/cluster.js" + "prepublishOnly": "test" }, "author": "SageMath, Inc.", "license": "SEE LICENSE.md", @@ -68,8 +67,6 @@ "@passport-next/passport-oauth2": "^2.1.4", "@sendgrid/client": "^8.1.4", "@sendgrid/mail": "^8.1.4", - "@socket.io/cluster-adapter": "^0.2.2", - "@socket.io/sticky": "^1.0.4", "@zxcvbn-ts/core": "^3.0.4", "@zxcvbn-ts/language-common": "^3.0.4", "@zxcvbn-ts/language-en": "^3.0.2",