From 3308752db8ca8ef02f27b90816d3b4f99480b609 Mon Sep 17 00:00:00 2001 From: Denis Aleksanov Date: Sun, 9 Nov 2025 15:54:32 +0100 Subject: [PATCH 01/51] issue-66 playwright dependency and npm scripts --- package-lock.json | 66 ++++++++++++++++++++++++++++++++++++++++++++++- package.json | 7 ++++- 2 files changed, 71 insertions(+), 2 deletions(-) diff --git a/package-lock.json b/package-lock.json index 99e12712..ac0892b5 100644 --- a/package-lock.json +++ b/package-lock.json @@ -15,6 +15,7 @@ "@babel/core": "^7.16.12", "@babel/preset-env": "^7.28.0", "@babel/preset-typescript": "^7.27.1", + "@playwright/test": "^1.56.1", "@rollup/plugin-node-resolve": "^16.0.1", "@rollup/plugin-terser": "^0.4.4", "@types/jest": "^30.0.0", @@ -2859,6 +2860,22 @@ "url": "https://opencollective.com/pkgr" } }, + "node_modules/@playwright/test": { + "version": "1.56.1", + "resolved": "https://registry.npmjs.org/@playwright/test/-/test-1.56.1.tgz", + "integrity": "sha512-vSMYtL/zOcFpvJCW71Q/OEGQb7KYBPAdKh35WNSkaZA75JlAO8ED8UN6GUNTm3drWomcbcqRPFqQbLae8yBTdg==", + "dev": true, + "license": "Apache-2.0", + "dependencies": { + "playwright": "1.56.1" + }, + "bin": { + "playwright": "cli.js" + }, + "engines": { + "node": ">=18" + } + }, "node_modules/@rollup/plugin-node-resolve": { "version": "16.0.1", "resolved": "https://registry.npmjs.org/@rollup/plugin-node-resolve/-/plugin-node-resolve-16.0.1.tgz", @@ -7377,6 +7394,53 @@ "node": ">=8" } }, + "node_modules/playwright": { + "version": "1.56.1", + "resolved": "https://registry.npmjs.org/playwright/-/playwright-1.56.1.tgz", + "integrity": "sha512-aFi5B0WovBHTEvpM3DzXTUaeN6eN0qWnTkKx4NQaH4Wvcmc153PdaY2UBdSYKaGYw+UyWXSVyxDUg5DoPEttjw==", + "dev": true, + "license": "Apache-2.0", + "dependencies": { + "playwright-core": "1.56.1" + }, + "bin": { + "playwright": "cli.js" + }, + "engines": { + "node": ">=18" + }, + "optionalDependencies": { + "fsevents": "2.3.2" + } + }, + "node_modules/playwright-core": { + "version": "1.56.1", + "resolved": "https://registry.npmjs.org/playwright-core/-/playwright-core-1.56.1.tgz", + "integrity": "sha512-hutraynyn31F+Bifme+Ps9Vq59hKuUCz7H1kDOcBs+2oGguKkWTU50bBWrtz34OUWmIwpBTWDxaRPXrIXkgvmQ==", + "dev": true, + "license": "Apache-2.0", + "bin": { + "playwright-core": "cli.js" + }, + "engines": { + "node": ">=18" + } + }, + "node_modules/playwright/node_modules/fsevents": { + "version": "2.3.2", + "resolved": "https://registry.npmjs.org/fsevents/-/fsevents-2.3.2.tgz", + "integrity": "sha512-xiqMQR4xAeHTuB9uWm+fFRcIOgKBMiOBP+eXiyT7jsgVCq1bkVygt00oASowB7EdtpOHaaPgKt812P9ab+DDKA==", + "dev": true, + "hasInstallScript": true, + "license": "MIT", + "optional": true, + "os": [ + "darwin" + ], + "engines": { + "node": "^8.16.0 || ^10.6.0 || >=11.0.0" + } + }, "node_modules/prelude-ls": { "version": "1.2.1", "resolved": "https://registry.npmjs.org/prelude-ls/-/prelude-ls-1.2.1.tgz", @@ -8748,4 +8812,4 @@ } } } -} \ No newline at end of file +} diff --git a/package.json b/package.json index 2f29f6e9..aca5e279 100644 --- a/package.json +++ b/package.json @@ -29,6 +29,10 @@ "watch": "jest --watch", "build": "node build", "test": "npm run lint && npm run jest", + "test:e2e": "playwright test --config=e2e/playwright.config.ts", + "test:e2e:ui": "playwright test --config=e2e/playwright.config.ts --ui", + "test:e2e:debug": "playwright test --config=e2e/playwright.config.ts --debug", + "test:all": "npm run test && npm run test:e2e", "prepack": "npm run build", "checkGit": "sh ./uncommited.sh", "prepublishOnly": "npm run checkGit && npm run test && npm run build && npm run checkGit" @@ -40,6 +44,7 @@ "@babel/core": "^7.16.12", "@babel/preset-env": "^7.28.0", "@babel/preset-typescript": "^7.27.1", + "@playwright/test": "^1.56.1", "@rollup/plugin-node-resolve": "^16.0.1", "@rollup/plugin-terser": "^0.4.4", "@types/jest": "^30.0.0", @@ -67,4 +72,4 @@ "javascript", "typescript" ] -} +} \ No newline at end of file From a499e20dbe9aacb17b4f94b46f427cfccfc8bfbb Mon Sep 17 00:00:00 2001 From: Denis Aleksanov Date: Sun, 9 Nov 2025 15:55:06 +0100 Subject: [PATCH 02/51] issue-66 playwright configuration --- .gitignore | 2 ++ e2e/playwright.config.ts | 28 ++++++++++++++++++++++++++++ e2e/tsconfig.json | 19 +++++++++++++++++++ 3 files changed, 49 insertions(+) create mode 100644 e2e/playwright.config.ts create mode 100644 e2e/tsconfig.json diff --git a/.gitignore b/.gitignore index 9bb67705..073413fb 100644 --- a/.gitignore +++ b/.gitignore @@ -3,3 +3,5 @@ node_modules .vscode *.tgz .DS_Store +e2e/playwright-report +e2e/test-results diff --git a/e2e/playwright.config.ts b/e2e/playwright.config.ts new file mode 100644 index 00000000..58827681 --- /dev/null +++ b/e2e/playwright.config.ts @@ -0,0 +1,28 @@ +import { defineConfig, devices } from '@playwright/test'; + +export default defineConfig({ + testDir: './specs', + outputDir: './test-results', + fullyParallel: true, + forbidOnly: !!process.env.CI, + retries: process.env.CI ? 2 : 0, + workers: process.env.CI ? 1 : undefined, + reporter: [['html', { outputFolder: './playwright-report' }]], + // timeout: 10 * 60 * 1000, // 10 minutes per test + + use: { + baseURL: 'about:blank', + trace: 'on-first-retry', + screenshot: 'only-on-failure', + // headless: false, // Show browser window + // slowMo: 500, // Slow down actions by 500ms for visibility + }, + + projects: [ + { + name: 'chromium', + use: { ...devices['Desktop Chrome'] }, + }, + ], +}); + diff --git a/e2e/tsconfig.json b/e2e/tsconfig.json new file mode 100644 index 00000000..8d5720af --- /dev/null +++ b/e2e/tsconfig.json @@ -0,0 +1,19 @@ +{ + "compilerOptions": { + "baseUrl": ".", + "outDir": "./dist", + "types": ["node", "@playwright/test"], + "typeRoots": ["./types", "../node_modules/@types"], + "module": "ESNext", + "moduleResolution": "bundler", + "esModuleInterop": true, + "skipLibCheck": true, + "target": "ESNext", + "lib": ["ESNext", "DOM"], + "strict": false, + "allowJs": true, + "resolveJsonModule": true + }, + "include": ["**/*.ts", "types/**/*.d.ts"], + "exclude": ["node_modules", "dist"] +} From 748363eb6e7185f6af089608b408c6bbb1182e5d Mon Sep 17 00:00:00 2001 From: Denis Aleksanov Date: Sun, 9 Nov 2025 15:55:38 +0100 Subject: [PATCH 03/51] issue-66 basic fixture entities --- e2e/fixture/Adapter.ts | 234 +++++++++++++++++++++ e2e/fixture/Scroller.ts | 102 ++++++++++ e2e/fixture/VScrollFixture.ts | 368 ++++++++++++++++++++++++++++++++++ 3 files changed, 704 insertions(+) create mode 100644 e2e/fixture/Adapter.ts create mode 100644 e2e/fixture/Scroller.ts create mode 100644 e2e/fixture/VScrollFixture.ts diff --git a/e2e/fixture/Adapter.ts b/e2e/fixture/Adapter.ts new file mode 100644 index 00000000..fb7b6adc --- /dev/null +++ b/e2e/fixture/Adapter.ts @@ -0,0 +1,234 @@ +import { Page } from '@playwright/test'; + +/** + * Adapter accessor class - provides access to adapter methods in browser context + */ +export class Adapter { + constructor(private page: Page) { } + + /** + * Wait for the scroller to relax (no pending operations) + */ + async relax() { + return await this.page.evaluate(() => { + const ds = window.__vscroll__.datasource; + if (!ds.adapter) { + throw new Error('Adapter not available. Set useAdapter: true in config.'); + } + return ds.adapter.relax(); + }); + } + + /** + * Append items to the end of the dataset + */ + async append(options) { + return await this.page.evaluate((opts) => { + const ds = window.__vscroll__.datasource; + if (!ds.adapter) { + throw new Error('Adapter not available. Set useAdapter: true in config.'); + } + return ds.adapter.append(opts); + }, options); + } + + /** + * Prepend items to the beginning of the dataset + */ + async prepend(options) { + return await this.page.evaluate((opts) => { + const ds = window.__vscroll__.datasource; + if (!ds.adapter) { + throw new Error('Adapter not available. Set useAdapter: true in config.'); + } + return ds.adapter.prepend(opts); + }, options); + } + + /** + * Reload the dataset + */ + async reload(reloadIndex) { + return await this.page.evaluate((index) => { + const ds = window.__vscroll__.datasource; + if (!ds.adapter) { + throw new Error('Adapter not available. Set useAdapter: true in config.'); + } + return ds.adapter.reload(index); + }, reloadIndex); + } + + /** + * Reset the scroller to initial state + */ + async reset(options) { + return await this.page.evaluate((opts) => { + const ds = window.__vscroll__.datasource; + if (!ds.adapter) { + throw new Error('Adapter not available. Set useAdapter: true in config.'); + } + return ds.adapter.reset(opts); + }, options); + } + + /** + * Remove items from the dataset + */ + async remove(options) { + const predicateStr = options.predicate.toString(); + return await this.page.evaluate(({ predicateStr, increase }) => { + const ds = window.__vscroll__.datasource; + if (!ds.adapter) { + throw new Error('Adapter not available. Set useAdapter: true in config.'); + } + const predicate = eval(`(${predicateStr})`); + return ds.adapter.remove({ predicate, increase }); + }, { predicateStr, increase: options.increase }); + } + + /** + * Insert items at a specific position + */ + async insert(options) { + return await this.page.evaluate((opts) => { + const ds = window.__vscroll__.datasource; + if (!ds.adapter) { + throw new Error('Adapter not available. Set useAdapter: true in config.'); + } + return ds.adapter.insert(opts); + }, options); + } + + /** + * Replace items in the dataset + */ + async replace(options) { + const predicateStr = options.predicate.toString(); + return await this.page.evaluate(({ predicateStr, items }) => { + const ds = window.__vscroll__.datasource; + if (!ds.adapter) { + throw new Error('Adapter not available. Set useAdapter: true in config.'); + } + const predicate = eval(`(${predicateStr})`); + return ds.adapter.replace({ predicate, items }); + }, { predicateStr, items: options.items }); + } + + /** + * Update items in the dataset + */ + async update(options) { + const predicateStr = options.predicate.toString(); + return await this.page.evaluate((predicateStr) => { + const ds = window.__vscroll__.datasource; + if (!ds.adapter) { + throw new Error('Adapter not available. Set useAdapter: true in config.'); + } + const predicate = eval(`(${predicateStr})`); + return ds.adapter.update({ predicate }); + }, predicateStr); + } + + /** + * Clip the dataset (remove items outside visible range) + */ + async clip(options) { + return await this.page.evaluate((opts) => { + const ds = window.__vscroll__.datasource; + if (!ds.adapter) { + throw new Error('Adapter not available. Set useAdapter: true in config.'); + } + return ds.adapter.clip(opts); + }, options); + } + + /** + * Check the dataset consistency + */ + async check() { + return await this.page.evaluate(() => { + const ds = window.__vscroll__.datasource; + if (!ds.adapter) { + throw new Error('Adapter not available. Set useAdapter: true in config.'); + } + return ds.adapter.check(); + }); + } + + /** + * Fix scroll position + */ + async fix(options) { + return await this.page.evaluate((opts) => { + const ds = window.__vscroll__.datasource; + if (!ds.adapter) { + throw new Error('Adapter not available. Set useAdapter: true in config.'); + } + return ds.adapter.fix(opts); + }, options); + } + + /** + * Get adapter properties (as getters to match IAdapter interface) + */ + get isLoading(): Promise { + return this.page.evaluate(() => { + const ds = window.__vscroll__.datasource; + if (!ds.adapter) { + throw new Error('Adapter not available. Set useAdapter: true in config.'); + } + return ds.adapter.isLoading; + }); + } + + get bof(): Promise { + return this.page.evaluate(() => { + const ds = window.__vscroll__.datasource; + if (!ds.adapter) { + throw new Error('Adapter not available. Set useAdapter: true in config.'); + } + return ds.adapter.bof; + }); + } + + get eof(): Promise { + return this.page.evaluate(() => { + const ds = window.__vscroll__.datasource; + if (!ds.adapter) { + throw new Error('Adapter not available. Set useAdapter: true in config.'); + } + return ds.adapter.eof; + }); + } + + get itemsCount(): Promise { + return this.page.evaluate(() => { + const ds = window.__vscroll__.datasource; + if (!ds.adapter) { + throw new Error('Adapter not available. Set useAdapter: true in config.'); + } + return ds.adapter.itemsCount; + }); + } + + get firstVisible(): Promise { + return this.page.evaluate(() => { + const ds = window.__vscroll__.datasource; + if (!ds.adapter) { + throw new Error('Adapter not available. Set useAdapter: true in config.'); + } + return ds.adapter.firstVisible; + }); + } + + get lastVisible(): Promise { + return this.page.evaluate(() => { + const ds = window.__vscroll__.datasource; + if (!ds.adapter) { + throw new Error('Adapter not available. Set useAdapter: true in config.'); + } + return ds.adapter.lastVisible; + }); + } +} + diff --git a/e2e/fixture/Scroller.ts b/e2e/fixture/Scroller.ts new file mode 100644 index 00000000..a4af8a28 --- /dev/null +++ b/e2e/fixture/Scroller.ts @@ -0,0 +1,102 @@ +import { Page } from '@playwright/test'; +import { Direction as DirectionEnum } from '../../src/inputs/common'; + +/** + * Direction enum (inline, no import from vscroll source) + * This avoids TypeScript compilation issues when loading e2e tests + */ +export const Direction = { + forward: 'forward', + backward: 'backward' +} as const; + +export type DirectionType = typeof Direction[keyof typeof Direction]; + +/** + * Scroller accessor class - provides access to scroller properties in browser context + */ +export class Scroller { + constructor(private page: Page) { } + + get settings() { + const page = this.page; + return { + get startIndex(): Promise { + return page.evaluate(() => { + return window.__vscroll__.workflow.scroller.settings.startIndex; + }); + }, + get bufferSize(): Promise { + return page.evaluate(() => { + return window.__vscroll__.workflow.scroller.settings.bufferSize; + }); + }, + get padding(): Promise { + return page.evaluate(() => { + return window.__vscroll__.workflow.scroller.settings.padding; + }); + }, + get horizontal(): Promise { + return page.evaluate(() => { + return window.__vscroll__.workflow.scroller.settings.horizontal; + }); + } + }; + } + + get buffer() { + const page = this.page; + return { + get defaultSize(): Promise { + return page.evaluate(() => { + return window.__vscroll__.workflow.scroller.buffer.defaultSize; + }); + }, + getEdgeVisibleItem: async (direction: DirectionType) => { + return await page.evaluate((dir) => { + const workflow = window.__vscroll__.workflow; + const item = workflow.scroller.buffer.getEdgeVisibleItem(dir as DirectionEnum); + return item ? { $index: item.$index, data: item.data } : null; + }, direction); + } + }; + } + + get viewport() { + const page = this.page; + return { + getSize: async () => { + return await page.evaluate(() => { + return window.__vscroll__.workflow.scroller.viewport.getSize(); + }); + }, + getScrollableSize: async () => { + return await page.evaluate(() => { + return window.__vscroll__.workflow.scroller.viewport.getScrollableSize(); + }); + }, + get scrollPosition(): Promise { + return page.evaluate(() => { + return window.__vscroll__.workflow.scroller.viewport.scrollPosition; + }); + }, + paddings: { + [Direction.forward]: { + get size(): Promise { + return page.evaluate(() => { + return window.__vscroll__.workflow.scroller.viewport.paddings.forward.size; + }); + } + }, + [Direction.backward]: { + get size(): Promise { + return page.evaluate(() => { + return window.__vscroll__.workflow.scroller.viewport.paddings.backward.size; + }); + } + } + } + }; + } +} + diff --git a/e2e/fixture/VScrollFixture.ts b/e2e/fixture/VScrollFixture.ts new file mode 100644 index 00000000..22775c05 --- /dev/null +++ b/e2e/fixture/VScrollFixture.ts @@ -0,0 +1,368 @@ +import { Page } from '@playwright/test'; +import * as path from 'path'; +import type { IDatasource, Item } from '../../src/index'; +import { Scroller, Direction, type DirectionType } from './Scroller.js'; +import { Adapter } from './Adapter.js'; + +export { Direction, type DirectionType }; +export interface VScrollFixtureConfig { + datasource: IDatasource; + useAdapter?: boolean; // If true, creates Datasource instance with adapter support + templateSettings?: { + viewportHeight?: number; + viewportWidth?: number; + itemHeight?: number; + itemWidth?: number; + horizontal?: boolean; + }; + templateFn?: (item: unknown) => string; +} + +export class VScrollFixture { + private page: Page; + private config: VScrollFixtureConfig; + readonly scroller: Scroller; + readonly adapter: Adapter; + + constructor(page: Page, config: VScrollFixtureConfig) { + this.page = page; + this.config = config; + this.scroller = new Scroller(page); + this.adapter = new Adapter(page); + } + + static async create(page: Page, config: VScrollFixtureConfig): Promise { + const fixture = new VScrollFixture(page, config); + await fixture.mount(); + return fixture; + } + + /** + * Mount vscroll in browser (POC pattern) + */ + async mount(): Promise { + const page = this.page; + + // 1. Go to blank page + await page.goto('about:blank'); + + // 2. Load vscroll UMD bundle + const vscrollPath = path.join(process.cwd(), 'dist', 'bundles', 'vscroll.umd.js'); + await page.addScriptTag({ path: vscrollPath }); + + // 3. Add global styles FIRST (separate evaluate call to ensure styles are processed) + const templateSettings = this.config.templateSettings || {}; + if (templateSettings.horizontal || templateSettings.itemHeight) { + await page.addStyleTag({ + content: templateSettings.horizontal + ? ` + #viewport { + white-space: nowrap; + } + #viewport div { + display: inline-block; + } + #vscroll [data-sid] { + width: ${templateSettings.itemWidth || 100}px; + overflow-x: hidden; + } + ` + : ` + #vscroll [data-sid] { + height: ${templateSettings.itemHeight}px; + overflow-y: hidden; + } + ` + }); + } + + // 4. Create viewport and vscroll element (after styles are loaded) + await page.evaluate((settings) => { + const viewport = document.createElement('div'); + viewport.id = 'viewport'; + + if (settings.horizontal) { + viewport.style.width = `${settings.viewportWidth || 300}px`; + viewport.style.height = '100%'; + viewport.style.overflowX = 'scroll'; // Always scroll, not auto + viewport.style.overflowY = 'hidden'; + } else { + viewport.style.height = `${settings.viewportHeight || 200}px`; + viewport.style.width = '100%'; + viewport.style.overflowY = 'scroll'; // Always scroll, not auto + viewport.style.overflowX = 'hidden'; + } + + document.body.appendChild(viewport); + + // Create vscroll element with padding elements + const vscrollEl = document.createElement('div'); + vscrollEl.id = 'vscroll'; + + const backwardPadding = document.createElement('div'); + backwardPadding.setAttribute('data-padding-backward', ''); + vscrollEl.appendChild(backwardPadding); + + const forwardPadding = document.createElement('div'); + forwardPadding.setAttribute('data-padding-forward', ''); + vscrollEl.appendChild(forwardPadding); + + viewport.appendChild(vscrollEl); + }, templateSettings); + + // 5. Initialize vscroll workflow + await this.initializeWorkflow(); + } + + /** + * Initialize vscroll workflow in browser + * Supports two patterns: + * 1. Plain object datasource (simple tests) + * 2. Datasource class with adapter (adapter tests) + */ + private async initializeWorkflow(): Promise { + const { datasource, templateFn, useAdapter } = this.config; + + // Serialize functions and config + const datasourceGetStr = datasource.get.toString(); + const datasourceSettings = datasource.settings || {}; + const datasourceDevSettings = datasource.devSettings || {}; // Include devSettings + const templateFnStr = templateFn + ? templateFn.toString() + : '(item) => `
${item.$index}: ${item.data.text || ""}
`'; + + await this.page.evaluate( + ({ + datasourceGetStr, + datasourceSettings, + datasourceDevSettings, + templateFnStr, + useAdapter + }) => { + const VScroll = window.VScroll; + + // Parse configs + const datasourceGet = eval(`(${datasourceGetStr})`); + const templateFn = eval(`(${templateFnStr})`); + + // Old items storage + let oldItems: Item[] = []; + + // Renderer (from POC) + const processItems = (newItems: Item[], oldItems: Item[]) => { + // Remove elements not present + oldItems + .filter(item => !newItems.includes(item)) + .forEach(item => item.element.remove()); + + // Create and insert new elements + const elements = []; + const vscrollEl = document.getElementById('vscroll')!; + let beforeElement = vscrollEl.querySelector('[data-padding-forward]'); + + for (let i = newItems.length - 1; i >= 0; i--) { + if (oldItems.includes(newItems[i])) { + if (!elements.length) { + beforeElement = newItems[i].element; + continue; + } else { + break; + } + } + + // Create element + const item = newItems[i]; + const element = document.createElement('div'); + element.setAttribute('data-sid', String(item.$index)); + + if (item.invisible) { + element.style.position = 'fixed'; + element.style.top = '-99px'; + } + + element.innerHTML = templateFn(item); + + item.element = element; + elements.unshift(element); + } + + elements.forEach(elt => + beforeElement!.insertAdjacentElement('beforebegin', elt) + ); + }; + + // Create datasource (plain object or Datasource class) + let datasourceInstance; + + if (useAdapter) { + // Pattern 2: Create Datasource class with adapter support + const Datasource = VScroll.makeDatasource(); + datasourceInstance = new Datasource({ + get: datasourceGet, + settings: datasourceSettings, + devSettings: datasourceDevSettings + }); + } else { + // Pattern 1: Plain object (no adapter) + datasourceInstance = { + get: datasourceGet, + settings: datasourceSettings, + devSettings: datasourceDevSettings + }; + } + + // Create workflow + const workflow = new VScroll.Workflow({ + consumer: { name: 'vscroll-e2e', version: '1.0.0' }, + element: document.getElementById('vscroll'), + datasource: datasourceInstance, + run: (newItems: unknown[]) => { + const items = newItems as Item[]; + if (!items.length && !oldItems.length) { + return; + } + processItems(items, oldItems); + oldItems = items; + } + }); + + // Expose for testing + window.__vscroll__ = { + workflow, + datasource: datasourceInstance, + oldItems, + Direction: { forward: 'forward', backward: 'backward' } + }; + }, + { + datasourceGetStr, + datasourceSettings, + datasourceDevSettings, + templateFnStr, + useAdapter: !!useAdapter + } + ); + } + + /** + * Access to workflow in browser + */ + get workflow() { + return { + // Proxy methods to browser + cyclesDone: async () => { + return await this.page.evaluate(() => { + return window.__vscroll__.workflow.cyclesDone; + }); + } + }; + } + + + + /** + * Scroll to a specific position + * Waits for the workflow to complete and the adapter to relax if the scroll position changed + */ + async scrollTo(position: number): Promise { + const positionBefore = await this.scroller.viewport.scrollPosition; + await this.adapter.fix({ scrollPosition: position }); + const positionAfter = await this.scroller.viewport.scrollPosition; + if (positionBefore !== positionAfter) { + await this.relaxNext(); + } + } + + /** + * Scroll to max position + */ + async scrollMax(): Promise { + await this.scrollTo(Infinity); + } + + /** + * Scroll to min position + */ + async scrollMin(): Promise { + await this.scrollTo(0); + } + + /** + * Get all rendered elements + */ + async getElements(): Promise { + return await this.page.$$('[data-sid]'); + } + + /** + * Get element index + */ + async getElementIndex(element: HTMLElement): Promise { + return await this.page.evaluate((el) => { + return parseInt(el.getAttribute('data-sid') || '0', 10); + }, element); + } + + /** + * Check element content by index + */ + async checkElementContentByIndex(index: number): Promise { + return await this.page.evaluate((idx) => { + const elements = Array.from(document.querySelectorAll('[data-sid]')); + const element = elements.find(el => el.getAttribute('data-sid') === String(idx)); + return element ? element.textContent?.includes(String(idx)) || false : false; + }, index); + } + + /** + * Wait for workflow to start loading (a new cycle to begin) + * This ensures the workflow has started processing before we proceed + */ + async waitForLoadingStart(): Promise { + await this.page.evaluate(() => { + return new Promise((resolve) => { + const workflow = window.__vscroll__.workflow; + const ds = window.__vscroll__.datasource; + const initialCycles = workflow.cyclesDone; + + // If already loading, we're good + if (ds.adapter && ds.adapter.isLoading) { + resolve(); + return; + } + + // Otherwise wait for a new cycle to start OR loading to begin + const check = () => { + if (workflow.cyclesDone > initialCycles || (ds.adapter && ds.adapter.isLoading)) { + resolve(); + } else { + requestAnimationFrame(check); + } + }; + check(); + }); + }); + } + + /** + * Wait for the next workflow cycle to start and complete + * Equivalent to: waitForLoadingStart() + adapter.relax() + * This is the recommended way to wait for workflow operations to complete + */ + async relaxNext(): Promise { + await this.waitForLoadingStart(); + await this.adapter.relax(); + } + + + /** + * Cleanup + */ + async cleanup(): Promise { + await this.page.evaluate(() => { + window.__vscroll__?.workflow?.dispose(); + document.querySelector('#viewport')?.remove(); + }); + } +} + From 17a5f62aa0545dc293ffe637a0fd3bbf9621ad46 Mon Sep 17 00:00:00 2001 From: Denis Aleksanov Date: Sun, 9 Nov 2025 15:56:07 +0100 Subject: [PATCH 04/51] issue-66 global.d.ts for e2e --- e2e/types/global.d.ts | 35 +++++++++++++++++++++++++++++++++++ 1 file changed, 35 insertions(+) create mode 100644 e2e/types/global.d.ts diff --git a/e2e/types/global.d.ts b/e2e/types/global.d.ts new file mode 100644 index 00000000..ec56fed3 --- /dev/null +++ b/e2e/types/global.d.ts @@ -0,0 +1,35 @@ +/** + * Global type definitions for e2e tests + * Extends Window with vscroll-specific properties + */ + +import type { IAdapter } from '../../src/interfaces/adapter'; +import type { Item } from '../../src/interfaces/item'; +import type { Workflow } from '../../src/workflow'; + +interface VScrollGlobal { + workflow: Workflow | Record; + datasource: { + adapter?: IAdapter; + }; + oldItems: Item[]; + Direction: { + forward: string; + backward: string; + }; +} + +interface VScrollConstructor { + makeDatasource: () => new (config: unknown) => unknown; + Workflow: new (config: unknown) => Workflow; +} + +declare global { + interface Window { + VScroll: VScrollConstructor; + __vscroll__: VScrollGlobal; + } +} + +export { }; + From 7e74a7be337c07774f317676c63b0a2857a44a96 Mon Sep 17 00:00:00 2001 From: Denis Aleksanov Date: Sun, 9 Nov 2025 15:56:30 +0100 Subject: [PATCH 05/51] issue-66 scroll-basic.spec --- e2e/helpers/itemsCounter.ts | 58 +++++ e2e/specs/scroll-basic.spec.ts | 440 +++++++++++++++++++++++++++++++++ 2 files changed, 498 insertions(+) create mode 100644 e2e/helpers/itemsCounter.ts create mode 100644 e2e/specs/scroll-basic.spec.ts diff --git a/e2e/helpers/itemsCounter.ts b/e2e/helpers/itemsCounter.ts new file mode 100644 index 00000000..ff9565f5 --- /dev/null +++ b/e2e/helpers/itemsCounter.ts @@ -0,0 +1,58 @@ +import { Direction, type DirectionType } from '../fixture/VScrollFixture.js'; + +/** + * Helper class to track item counts in forward and backward directions + */ +export class ItemsDirCounter { + count: number; + index: number; + padding: number; + paddingShift?: number; + size: number; + + constructor(count = 0, padding = 0) { + this.count = count; + this.padding = padding; + this.paddingShift = 0; + this.index = NaN; + this.size = NaN; + } +} + +/** + * Helper class for tracking expected buffer state during scroll tests + */ +export class ItemsCounter { + direction: DirectionType | null; + backward: ItemsDirCounter; + forward: ItemsDirCounter; + average: number; + + get total(): number { + return this.forward.index - this.backward.index + 1; + } + + get paddings(): number { + return this.forward.padding + this.backward.padding; + } + + constructor(direction?: DirectionType) { + this.direction = direction || null; + this.forward = new ItemsDirCounter(); + this.backward = new ItemsDirCounter(); + this.average = NaN; + } + + get(token: DirectionType): ItemsDirCounter { + return token === Direction.backward ? this.backward : this.forward; + } + + set(token: DirectionType, value: Partial): void { + if (token === Direction.backward) { + Object.assign(this.backward, value); + } else { + Object.assign(this.forward, value); + } + } +} + diff --git a/e2e/specs/scroll-basic.spec.ts b/e2e/specs/scroll-basic.spec.ts new file mode 100644 index 00000000..38617129 --- /dev/null +++ b/e2e/specs/scroll-basic.spec.ts @@ -0,0 +1,440 @@ +import { test, expect } from '@playwright/test'; +import type { Page } from '@playwright/test'; +import { VScrollFixture, Direction, type DirectionType } from '../fixture/VScrollFixture.js'; +import { ItemsCounter } from '../helpers/itemsCounter.js'; +import type { IDatasource } from '../../src/index.js'; +import type { Settings, DevSettings } from '../../src/interfaces/settings.js'; + +interface ICustom { + direction: DirectionType; + count: number; + bouncing?: boolean; + mass?: boolean; +} + +interface ITemplateSettings { + viewportHeight?: number; + viewportWidth?: number; + itemHeight?: number; + itemWidth?: number; + horizontal?: boolean; +} + +interface ITestConfig { + datasourceSettings: Settings; + datasourceDevSettings?: DevSettings; // Optional dev settings for debugging + templateSettings: ITemplateSettings; + custom: ICustom; +} + +// Test configurations (same as original) +const configList: ITestConfig[] = [ + { + datasourceSettings: { + startIndex: 100, + bufferSize: 4, + padding: 0.22, + itemSize: 20 + }, + datasourceDevSettings: { debug: true, immediateLog: true }, + templateSettings: { viewportHeight: 71, itemHeight: 20 }, + custom: { direction: Direction.forward, count: 1 } + }, + { + datasourceSettings: { + startIndex: 1, + bufferSize: 5, + padding: 0.2, + itemSize: 20 + }, + datasourceDevSettings: { debug: true, immediateLog: true }, + templateSettings: { viewportHeight: 100, itemHeight: 20 }, + custom: { direction: Direction.forward, count: 1 } + }, + { + datasourceSettings: { + startIndex: -15, + bufferSize: 12, + padding: 0.98, + itemSize: 20 + }, + datasourceDevSettings: { debug: true, immediateLog: true }, + templateSettings: { viewportHeight: 66, itemHeight: 20 }, + custom: { direction: Direction.forward, count: 1 } + }, + { + datasourceSettings: { + startIndex: 1, + bufferSize: 5, + padding: 1, + horizontal: true, + itemSize: 100 + }, + datasourceDevSettings: { debug: true, immediateLog: true }, + templateSettings: { viewportWidth: 450, itemWidth: 100, horizontal: true }, + custom: { direction: Direction.forward, count: 1 } + }, + { + datasourceSettings: { + startIndex: -74, + bufferSize: 4, + padding: 0.72, + horizontal: true, + itemSize: 75 + }, + datasourceDevSettings: { debug: true, immediateLog: true }, + templateSettings: { viewportWidth: 300, itemWidth: 75, horizontal: true }, + custom: { direction: Direction.forward, count: 1 } + } +]; + +// Helper functions (ported from original) +const treatIndex = (index: number) => (index <= 3 ? index : 3 * 2 - index); + +const doScrollMax = async (config: ITestConfig, fixture: VScrollFixture) => { + if (config.custom.direction === Direction.forward) { + await fixture.scrollMax(); + } else { + await fixture.scrollMin(); + } +}; + +const invertDirection = (config: ITestConfig) => { + const _forward = config.custom.direction === Direction.forward; + config.custom.direction = _forward ? Direction.backward : Direction.forward; +}; + +const getInitialItemsCounter = async ( + fixture: VScrollFixture, + config: ITestConfig +): Promise => { + const edgeItem = await fixture.scroller.buffer.getEdgeVisibleItem(Direction.forward); + const oppositeItem = await fixture.scroller.buffer.getEdgeVisibleItem(Direction.backward); + const result = new ItemsCounter(); + + if (!edgeItem || !oppositeItem) { + return result; + } + + const startIndex = config.datasourceSettings.startIndex; + + result.set(Direction.forward, { + count: edgeItem.$index - startIndex + 1, + index: edgeItem.$index, + padding: 0, + size: 0 + }); + result.set(Direction.backward, { + count: startIndex - oppositeItem.$index, + index: oppositeItem.$index, + padding: 0, + size: NaN + }); + + return result; +}; + +const getFullHouseDiff = ( + viewportSize: number, + paddingDelta: number, + itemSize: number, + bufferSize: number +): number => { + const sizeToFill = viewportSize + 2 * paddingDelta; + const itemsToFillNotRounded = sizeToFill / itemSize; + const itemsToFillRounded = Math.ceil(sizeToFill / itemSize); + const itemsToFill = + itemsToFillRounded + (itemsToFillNotRounded === itemsToFillRounded ? 0 : 1); + const bufferSizeDiff = bufferSize - itemsToFill; + return Math.max(0, bufferSizeDiff); +}; + +const getCurrentItemsCounter = async ( + fixture: VScrollFixture, + direction: DirectionType, + previous: ItemsCounter, + config: ITestConfig +): Promise => { + const bufferSize = config.datasourceSettings.bufferSize; + const padding = config.datasourceSettings.padding; + const viewportSize = await fixture.scroller.viewport.getSize(); + const itemSize = await fixture.scroller.buffer.defaultSize; + const fwd = direction === Direction.forward; + const opposite = fwd ? Direction.backward : Direction.forward; + const delta = viewportSize * padding; + + // handle direction (fetch) + const fullHouseDiff = getFullHouseDiff(viewportSize, delta, itemSize, bufferSize); + const _singleFetchCount = Math.ceil(delta / itemSize); + const singleFetchCount = Math.max(bufferSize, _singleFetchCount); + const itemsToFetch = + previous.direction && previous.direction !== direction + ? _singleFetchCount + fullHouseDiff + : singleFetchCount; + const newDirIndex = + previous.get(direction).index + + (fwd ? 1 : -1) * (previous.get(direction).padding / itemSize) + + (fwd ? 1 : -1) * itemsToFetch; + + // handle opposite (clip) + const oppPadding = previous.get(opposite).padding; + const previousTotalSize = previous.total * itemSize + previous.paddings; + const sizeToClip = previousTotalSize - oppPadding - viewportSize - delta; + const itemsToClip = Math.floor(sizeToClip / itemSize); + const newOppIndex = + previous.get(opposite).index + (fwd ? 1 : -1) * itemsToClip; + const newOppPadding = itemsToClip * itemSize + oppPadding; + + const result = new ItemsCounter(direction); + result.set(direction, { + index: newDirIndex, + padding: 0, + count: NaN, + size: NaN + }); + result.set(opposite, { + index: newOppIndex, + padding: newOppPadding, + count: NaN, + size: NaN + }); + return result; +}; + +// Create test fixture from config +const createFixture = async (page: Page, config: ITestConfig): Promise => { + const { datasourceSettings, datasourceDevSettings, templateSettings } = config; + + // // Capture browser console logs + // page.on('console', (msg: any) => { + // console.log(`[BROWSER] ${msg.text()}`); + // }); + + // Enable adapter to access relax() method + const datasource: IDatasource = { + get: (index, count, success) => { + const data = []; + for (let i = index; i < index + count; i++) { + data.push({ id: i, text: `item #${i}` }); + } + setTimeout(() => success(data), 25); + }, + settings: datasourceSettings, + devSettings: datasourceDevSettings + }; + + const fixture = await VScrollFixture.create(page, { + datasource, + useAdapter: true, + templateSettings, + templateFn: (item: { $index: number, data: { id: number, text: string } }) => + `
${item.$index}: ${item.data.text}
` + }); + + // Wait for initial workflow cycle to complete + await fixture.relaxNext(); + + // // Debug: log actual element dimensions + // const debugInfo = await page.evaluate(() => { + // const viewport = document.getElementById('viewport')!; + // const vscroll = document.getElementById('vscroll')!; + // const items = Array.from(vscroll.querySelectorAll('[data-sid]')); + // const bwdPadding = vscroll.querySelector('[data-padding-backward]') as HTMLElement; + // const fwdPadding = vscroll.querySelector('[data-padding-forward]') as HTMLElement; + // const horizontal = (window as any).__vscroll__.workflow.scroller.settings.horizontal; + // return { + // viewportDimensions: { + // clientWidth: viewport.clientWidth, + // clientHeight: viewport.clientHeight, + // scrollWidth: viewport.scrollWidth, + // scrollHeight: viewport.scrollHeight + // }, + // vscrollDimensions: { + // clientWidth: vscroll.clientWidth, + // clientHeight: vscroll.clientHeight, + // scrollWidth: vscroll.scrollWidth, + // scrollHeight: vscroll.scrollHeight + // }, + // itemCount: items.length, + // firstItemDimensions: items[0] ? { + // width: (items[0] as HTMLElement).clientWidth, + // height: (items[0] as HTMLElement).clientHeight, + // display: getComputedStyle(items[0] as HTMLElement).display, + // whiteSpace: getComputedStyle(vscroll).whiteSpace + // } : null, + // bwdPaddingDimensions: { + // width: bwdPadding.clientWidth, + // height: bwdPadding.clientHeight, + // display: getComputedStyle(bwdPadding).display, + // computedWidth: getComputedStyle(bwdPadding).width + // }, + // fwdPaddingDimensions: { + // width: fwdPadding.clientWidth, + // height: fwdPadding.clientHeight, + // display: getComputedStyle(fwdPadding).display, + // computedWidth: getComputedStyle(fwdPadding).width + // }, + // horizontal + // }; + // }); + // console.log('[DEBUG] Initial dimensions:', JSON.stringify(debugInfo, null, 2)); + + return fixture; +}; + +// Main test function +const shouldScroll = async (fixture: VScrollFixture, config: ITestConfig) => { + const custom = config.custom; + const wfCount = custom.count + 1; + const wfCountMiddle = Math.ceil(wfCount / 2); + let itemsCounter: ItemsCounter; + let cycles = 1; + + // Initial state + itemsCounter = await getInitialItemsCounter(fixture, config); + + // Perform scrolls + while (cycles < wfCount) { + cycles++; + + if (custom.bouncing) { + invertDirection(config); + } else if (custom.mass) { + if (cycles === wfCountMiddle) { + invertDirection(config); + } + } + + await doScrollMax(config, fixture); + + itemsCounter = await getCurrentItemsCounter(fixture, custom.direction, itemsCounter, config); + } + + // Final expectations + const direction: DirectionType = custom.direction; + const opposite: DirectionType = + direction === Direction.forward ? Direction.backward : Direction.forward; + + const edgeItem = await fixture.scroller.buffer.getEdgeVisibleItem(direction); + const oppositeItem = await fixture.scroller.buffer.getEdgeVisibleItem(opposite); + const edgeItemIndex = itemsCounter.get(direction).index; + const oppositeItemIndex = itemsCounter.get(opposite).index; + + // await new Promise(resolve => setTimeout(resolve, 1000000)); + expect(edgeItem && edgeItem.$index).toEqual(edgeItemIndex); + expect(oppositeItem && oppositeItem.$index).toEqual(oppositeItemIndex); + expect(await fixture.scroller.viewport.paddings[direction].size).toEqual( + itemsCounter.get(direction).padding + ); + expect(await fixture.scroller.viewport.paddings[opposite].size).toEqual( + itemsCounter.get(opposite).padding + ); + expect(await fixture.checkElementContentByIndex(edgeItemIndex)).toEqual(true); + expect(await fixture.checkElementContentByIndex(oppositeItemIndex)).toEqual(true); +}; + +const makeTest = (title: string, config: ITestConfig) => { + test(title, async ({ page }) => { + const fixture = await createFixture(page, config); + await shouldScroll(fixture, config); + await fixture.cleanup(); + }); +}; + +// Test suites +test.describe('Basic Scroll Spec', () => { + test.describe('Single max fwd scroll event', () => { + configList.forEach((config, index) => { + makeTest(`should process 1 forward max scroll (config ${index})`, config); + }); + }); + + test.describe('Single max bwd scroll event', () => + configList.map(config => ({ + ...config, + custom: { + ...config.custom, + direction: Direction.backward + } + })).forEach((config, index) => + makeTest(`should process 1 backward max scroll (config ${index})`, config) + ) + ); + + test.describe('Mass max fwd scroll events', () => + configList.map((config, index) => ({ + ...config, + custom: { + direction: Direction.forward, + count: 3 + treatIndex(index) + } + })).forEach((config, index) => + makeTest(`should process ${config.custom.count} forward scrolls (config ${index})`, config) + ) + ); + + test.describe('Mass max bwd scroll events', () => + configList.map((config, index) => ({ + ...config, + custom: { + direction: Direction.backward, + count: 3 + treatIndex(index) + } + })).forEach((config, index) => + makeTest(`should process ${config.custom.count} backward scrolls (config ${index})`, config) + ) + ); + + test.describe('Bouncing max two-directional scroll events (fwd started)', () => + configList.map((config, index) => ({ + ...config, + custom: { + direction: Direction.forward, + count: (3 + treatIndex(index)) * 2, + bouncing: true + } + })).forEach((config, index) => + makeTest(`should process ${config.custom.count} bouncing scrolls (config ${index})`, config) + ) + ); + + test.describe('Bouncing max two-directional scroll events (bwd started)', () => + configList.map((config, index) => ({ + ...config, + custom: { + direction: Direction.backward, + count: (3 + treatIndex(index)) * 2, + bouncing: true + } + })).forEach((config, index) => + makeTest(`should process ${config.custom.count} bouncing scrolls (config ${index})`, config) + ) + ); + + test.describe('Mass two-directional scroll events (first half fwd, second half bwd)', () => + configList.map((config, index) => ({ + ...config, + custom: { + direction: Direction.forward, + count: (3 + treatIndex(index)) * 2, + mass: true + } + })).forEach((config, index) => + makeTest(`should process ${config.custom.count} two-directional scrolls (config ${index})`, config) + ) + ); + + test.describe('Mass two-directional scroll events (first half bwd, second half fwd)', () => + configList.map((config, index) => ({ + ...config, + custom: { + direction: Direction.backward, + count: (3 + treatIndex(index)) * 2, + mass: true + } + })).forEach((config, index) => + makeTest(`should process ${config.custom.count} two-directional scrolls (config ${index})`, config) + ) + ); +}); + From 454683ab56cb3799782006f299e38f07741e3e24 Mon Sep 17 00:00:00 2001 From: Denis Aleksanov Date: Sun, 9 Nov 2025 16:03:14 +0100 Subject: [PATCH 06/51] issue-66 new CI workflow --- .github/workflows/build.yml | 2 +- .github/workflows/general.yml | 52 +++++++++++++++++++++++++++++++++++ package.json | 4 +-- 3 files changed, 55 insertions(+), 3 deletions(-) create mode 100644 .github/workflows/general.yml diff --git a/.github/workflows/build.yml b/.github/workflows/build.yml index 3ed7c975..e93beead 100644 --- a/.github/workflows/build.yml +++ b/.github/workflows/build.yml @@ -36,4 +36,4 @@ jobs: - run: npm ci - run: npm run build - - run: npm test + - run: npm run test:unit diff --git a/.github/workflows/general.yml b/.github/workflows/general.yml new file mode 100644 index 00000000..09b6e9c1 --- /dev/null +++ b/.github/workflows/general.yml @@ -0,0 +1,52 @@ +name: vscroll build and tests + +on: + pull_request: + workflow_dispatch: + inputs: + cause: + description: "Reason" + required: true + default: "Manual triggering" + +jobs: + build_and_tests: + runs-on: ubuntu-latest + steps: + - name: Dispatched? + if: ${{ github.event_name == 'workflow_dispatch' }} + run: | + echo "This is dispatched" + echo "Test reason: ${{ github.event.inputs.cause }}" + + - name: Checkout + uses: actions/checkout@v4 + + - name: Use Node.js 22.x + uses: actions/setup-node@v4 + with: + node-version: 22.x + + - name: Install dependencies + run: npm ci + + - name: Build + run: npm run build + + - name: Run unit tests + run: npm run test:unit + + - name: Install Playwright Browsers + run: npx playwright install --with-deps chromium + + - name: Run e2e tests + run: npm run test:e2e + + - name: Upload Playwright Report + uses: actions/upload-artifact@v4 + if: ${{ !cancelled() }} + with: + name: playwright-report + path: vscroll/e2e/playwright-report/ + retention-days: 30 + diff --git a/package.json b/package.json index aca5e279..5dc7dbcf 100644 --- a/package.json +++ b/package.json @@ -28,11 +28,11 @@ "jest": "jest", "watch": "jest --watch", "build": "node build", - "test": "npm run lint && npm run jest", + "test:unit": "npm run lint && npm run jest", "test:e2e": "playwright test --config=e2e/playwright.config.ts", "test:e2e:ui": "playwright test --config=e2e/playwright.config.ts --ui", "test:e2e:debug": "playwright test --config=e2e/playwright.config.ts --debug", - "test:all": "npm run test && npm run test:e2e", + "test": "npm run test:unit && npm run test:e2e", "prepack": "npm run build", "checkGit": "sh ./uncommited.sh", "prepublishOnly": "npm run checkGit && npm run test && npm run build && npm run checkGit" From 42c1c0e83808603aacad68c4dcf610b9281ac62d Mon Sep 17 00:00:00 2001 From: Denis Aleksanov Date: Sun, 9 Nov 2025 16:09:12 +0100 Subject: [PATCH 07/51] issue-66 fix unit tests --- jest.config.js | 11 +++++++++++ 1 file changed, 11 insertions(+) create mode 100644 jest.config.js diff --git a/jest.config.js b/jest.config.js new file mode 100644 index 00000000..675fbf9d --- /dev/null +++ b/jest.config.js @@ -0,0 +1,11 @@ +export default { + testMatch: ['**/tests/**/*.spec.ts'], + testPathIgnorePatterns: ['/node_modules/', '/e2e/'], + transform: { + '^.+\\.ts$': 'babel-jest' + }, + moduleFileExtensions: ['ts', 'js'], + collectCoverageFrom: ['src/**/*.ts'], + coveragePathIgnorePatterns: ['/node_modules/', '/tests/'] +}; + From 6f2e0ab6152437a70fa043afcfc3ce9fa118b815 Mon Sep 17 00:00:00 2001 From: Denis Aleksanov Date: Sun, 9 Nov 2025 18:43:56 +0100 Subject: [PATCH 08/51] issue-66 scroll-basic.spec | config type --- e2e/specs/scroll-basic.spec.ts | 47 ++++++++++++---------------------- e2e/types/index.ts | 17 ++++++++++++ 2 files changed, 33 insertions(+), 31 deletions(-) create mode 100644 e2e/types/index.ts diff --git a/e2e/specs/scroll-basic.spec.ts b/e2e/specs/scroll-basic.spec.ts index 38617129..47356392 100644 --- a/e2e/specs/scroll-basic.spec.ts +++ b/e2e/specs/scroll-basic.spec.ts @@ -3,32 +3,17 @@ import type { Page } from '@playwright/test'; import { VScrollFixture, Direction, type DirectionType } from '../fixture/VScrollFixture.js'; import { ItemsCounter } from '../helpers/itemsCounter.js'; import type { IDatasource } from '../../src/index.js'; -import type { Settings, DevSettings } from '../../src/interfaces/settings.js'; +import { ITestConfig } from 'types/index.js'; -interface ICustom { +type IConfig = ITestConfig<{ direction: DirectionType; count: number; bouncing?: boolean; mass?: boolean; -} - -interface ITemplateSettings { - viewportHeight?: number; - viewportWidth?: number; - itemHeight?: number; - itemWidth?: number; - horizontal?: boolean; -} - -interface ITestConfig { - datasourceSettings: Settings; - datasourceDevSettings?: DevSettings; // Optional dev settings for debugging - templateSettings: ITemplateSettings; - custom: ICustom; -} +}>; // Test configurations (same as original) -const configList: ITestConfig[] = [ +const configList: IConfig[] = [ { datasourceSettings: { startIndex: 100, @@ -91,7 +76,7 @@ const configList: ITestConfig[] = [ // Helper functions (ported from original) const treatIndex = (index: number) => (index <= 3 ? index : 3 * 2 - index); -const doScrollMax = async (config: ITestConfig, fixture: VScrollFixture) => { +const doScrollMax = async (config: IConfig, fixture: VScrollFixture) => { if (config.custom.direction === Direction.forward) { await fixture.scrollMax(); } else { @@ -99,14 +84,14 @@ const doScrollMax = async (config: ITestConfig, fixture: VScrollFixture) => { } }; -const invertDirection = (config: ITestConfig) => { +const invertDirection = (config: IConfig) => { const _forward = config.custom.direction === Direction.forward; config.custom.direction = _forward ? Direction.backward : Direction.forward; }; const getInitialItemsCounter = async ( fixture: VScrollFixture, - config: ITestConfig + config: IConfig ): Promise => { const edgeItem = await fixture.scroller.buffer.getEdgeVisibleItem(Direction.forward); const oppositeItem = await fixture.scroller.buffer.getEdgeVisibleItem(Direction.backward); @@ -153,7 +138,7 @@ const getCurrentItemsCounter = async ( fixture: VScrollFixture, direction: DirectionType, previous: ItemsCounter, - config: ITestConfig + config: IConfig ): Promise => { const bufferSize = config.datasourceSettings.bufferSize; const padding = config.datasourceSettings.padding; @@ -202,7 +187,7 @@ const getCurrentItemsCounter = async ( }; // Create test fixture from config -const createFixture = async (page: Page, config: ITestConfig): Promise => { +const createFixture = async (page: Page, config: IConfig): Promise => { const { datasourceSettings, datasourceDevSettings, templateSettings } = config; // // Capture browser console logs @@ -283,7 +268,7 @@ const createFixture = async (page: Page, config: ITestConfig): Promise { +const shouldScroll = async (fixture: VScrollFixture, config: IConfig) => { const custom = config.custom; const wfCount = custom.count + 1; const wfCountMiddle = Math.ceil(wfCount / 2); @@ -333,7 +318,7 @@ const shouldScroll = async (fixture: VScrollFixture, config: ITestConfig) => { expect(await fixture.checkElementContentByIndex(oppositeItemIndex)).toEqual(true); }; -const makeTest = (title: string, config: ITestConfig) => { +const makeTest = (title: string, config: IConfig) => { test(title, async ({ page }) => { const fixture = await createFixture(page, config); await shouldScroll(fixture, config); @@ -343,11 +328,11 @@ const makeTest = (title: string, config: ITestConfig) => { // Test suites test.describe('Basic Scroll Spec', () => { - test.describe('Single max fwd scroll event', () => { - configList.forEach((config, index) => { - makeTest(`should process 1 forward max scroll (config ${index})`, config); - }); - }); + test.describe('Single max fwd scroll event', () => + configList.forEach((config, index) => + makeTest(`should process 1 forward max scroll (config ${index})`, config) + ) + ); test.describe('Single max bwd scroll event', () => configList.map(config => ({ diff --git a/e2e/types/index.ts b/e2e/types/index.ts new file mode 100644 index 00000000..848d2ea9 --- /dev/null +++ b/e2e/types/index.ts @@ -0,0 +1,17 @@ + +import type { Settings, DevSettings } from '../../src/interfaces/settings.js'; + +interface ITemplateSettings { + viewportHeight?: number; + viewportWidth?: number; + itemHeight?: number; + itemWidth?: number; + horizontal?: boolean; +} + +export interface ITestConfig { + datasourceSettings: Settings; + datasourceDevSettings?: DevSettings; + templateSettings: ITemplateSettings; + custom?: Custom; +} \ No newline at end of file From eef5633c90e5674a9e71ed241a431811ca45ba42 Mon Sep 17 00:00:00 2001 From: Denis Aleksanov Date: Sun, 9 Nov 2025 20:07:02 +0100 Subject: [PATCH 09/51] issue-66 Workflow.cyclesDone$ --- src/workflow.ts | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/src/workflow.ts b/src/workflow.ts index c3c05615..f7e4bd5a 100644 --- a/src/workflow.ts +++ b/src/workflow.ts @@ -23,6 +23,7 @@ export class Workflow { initTimer: ReturnType | null; adapterRun$: Reactive; cyclesDone: number; + cyclesDone$: Reactive; interruptionCount: number; errors: WorkflowError[]; @@ -45,6 +46,7 @@ export class Workflow { this.initTimer = null; this.adapterRun$ = new Reactive(); this.cyclesDone = 0; + this.cyclesDone$ = new Reactive(0); this.interruptionCount = 0; this.errors = []; this.offScroll = () => null; @@ -196,6 +198,7 @@ export class Workflow { done(): void { const { state, logger } = this.scroller; this.cyclesDone++; + this.cyclesDone$.set(this.cyclesDone); logger.logCycle(false); state.endWorkflowCycle(this.cyclesDone + 1); this.finalize(); @@ -208,6 +211,7 @@ export class Workflow { } this.offScroll(); this.adapterRun$.dispose(); + this.cyclesDone$.dispose(); this.scroller.dispose(true); Object.getOwnPropertyNames(this).forEach(prop => { delete (this as Record)[prop]; From c96f1c9b86de65599591b4024405a62a1b12675e Mon Sep 17 00:00:00 2001 From: Denis Aleksanov Date: Sun, 9 Nov 2025 20:07:19 +0100 Subject: [PATCH 10/51] issue-66 scroll-delay.spec --- e2e/fixture/VScrollFixture.ts | 22 +++-- e2e/specs/scroll-delay.spec.ts | 174 +++++++++++++++++++++++++++++++++ 2 files changed, 186 insertions(+), 10 deletions(-) create mode 100644 e2e/specs/scroll-delay.spec.ts diff --git a/e2e/fixture/VScrollFixture.ts b/e2e/fixture/VScrollFixture.ts index 22775c05..4923ff20 100644 --- a/e2e/fixture/VScrollFixture.ts +++ b/e2e/fixture/VScrollFixture.ts @@ -248,10 +248,11 @@ export class VScrollFixture { * Access to workflow in browser */ get workflow() { + const page = this.page; return { // Proxy methods to browser - cyclesDone: async () => { - return await this.page.evaluate(() => { + get cyclesDone(): Promise { + return page.evaluate(() => { return window.__vscroll__.workflow.cyclesDone; }); } @@ -261,14 +262,15 @@ export class VScrollFixture { /** - * Scroll to a specific position - * Waits for the workflow to complete and the adapter to relax if the scroll position changed + * Scroll to a specific position. Waits for the scroller to relax if the scroll position changed. + * @param position - The position to scroll to + * @param options.noRelax - If true, the adapter will not be relaxed even if the scroll position changed */ - async scrollTo(position: number): Promise { + async scrollTo(position: number, options: { noRelax?: boolean } = {}): Promise { const positionBefore = await this.scroller.viewport.scrollPosition; await this.adapter.fix({ scrollPosition: position }); const positionAfter = await this.scroller.viewport.scrollPosition; - if (positionBefore !== positionAfter) { + if (!options?.noRelax && positionBefore !== positionAfter) { await this.relaxNext(); } } @@ -276,15 +278,15 @@ export class VScrollFixture { /** * Scroll to max position */ - async scrollMax(): Promise { - await this.scrollTo(Infinity); + async scrollMax(options?: { noRelax?: boolean }): Promise { + return this.scrollTo(Infinity, options); } /** * Scroll to min position */ - async scrollMin(): Promise { - await this.scrollTo(0); + async scrollMin(options?: { noRelax?: boolean }): Promise { + return this.scrollTo(0, options); } /** diff --git a/e2e/specs/scroll-delay.spec.ts b/e2e/specs/scroll-delay.spec.ts new file mode 100644 index 00000000..6b3a35b9 --- /dev/null +++ b/e2e/specs/scroll-delay.spec.ts @@ -0,0 +1,174 @@ +import { test, expect } from '@playwright/test'; +import type { Page } from '@playwright/test'; +import { VScrollFixture } from '../fixture/VScrollFixture.js'; +import type { IDatasource } from '../../src/index.js'; +import { ITestConfig } from 'types/index.js'; + +/** + * Create fixture with datasource delay + */ +const createFixture = async (page: Page, config: ITestConfig): Promise => { + const { datasourceSettings, datasourceDevSettings, templateSettings } = config; + + // Note: closure variables not available in browser context + const datasource: IDatasource = { + get: (index, count, success) => { + const data = []; + for (let i = index; i < index + count; i++) { + data.push({ id: i, text: `item #${i}` }); + } + setTimeout(() => success(data), 150); // 150ms delay for slow fetch test + }, + settings: datasourceSettings, + devSettings: datasourceDevSettings + }; + + const fixture = await VScrollFixture.create(page, { + datasource, + useAdapter: true, + templateSettings, + templateFn: (item: { $index: number, data: { id: number, text: string } }) => + `
${item.$index}: ${item.data.text}
` + }); + + // Wait for initial workflow cycle to complete + await fixture.relaxNext(); + + return fixture; +}; + +/** + * Test: Throttled scroll event handling + * + * Rapidly triggers multiple scroll operations and verifies that the workflow + * throttles them into fewer cycles than the number of scroll events. + */ +test.describe('Delay Scroll Spec', () => { + test('should work with throttled scroll event handling', async ({ page }) => { + const config: ITestConfig = { + datasourceSettings: { + startIndex: 1, + bufferSize: 5, + padding: 0.5, + itemSize: 20, + adapter: true + }, + datasourceDevSettings: { + throttle: 500 + }, + templateSettings: { + viewportHeight: 200, + itemHeight: 20 + } + }; + + const fixture = await createFixture(page, config); + const initCyclesCount = await fixture.workflow.cyclesDone; + expect(initCyclesCount).toBe(1); + + // Start rapid scrolling and monitor cycles in browser context + const count = await page.evaluate(() => + new Promise(resolve => { + const COUNT = 10; + let scrollCount = 0; + let timer: ReturnType; + const workflow = window.__vscroll__.workflow; + const adapter = window.__vscroll__.datasource.adapter; + + // Subscribe to cyclesDone$ - resolve when cycle 3 completes + workflow.cyclesDone$.on(cycles => { + if (cycles === 3) { + resolve(scrollCount); + } + }); + + // Start the scrolling timer after cycle 1 completes (already completed) + timer = setInterval(() => { + scrollCount++; + if (scrollCount % 2 === 0) { + adapter.fix({ scrollPosition: 0 }); // scrollMin + } else { + adapter.fix({ scrollPosition: Infinity }); // scrollMax + } + if (scrollCount === COUNT) { + clearInterval(timer); + } + }, 10); + }) + ); + + // Verify all scrolls were executed + expect(count).toBe(10); + + await fixture.cleanup(); + }); + + test('should handle additional scrolling during slow fetch', async ({ page }) => { + const config: ITestConfig = { + datasourceSettings: { + startIndex: 1, + bufferSize: 5, + padding: 0.5, + itemSize: 20, + adapter: true + }, + templateSettings: { + viewportHeight: 200, + itemHeight: 20 + } + }; + + const fixture = await createFixture(page, config); + const initCyclesCount = await fixture.workflow.cyclesDone; + expect(initCyclesCount).toBe(1); + + const startPosition = await fixture.scroller.viewport.scrollPosition; + + // Start rapid scrolling and monitor cycles in browser context + const result = await page.evaluate(({ start }) => + new Promise<{ endPos: number, position: number }>((resolve) => { + const COUNT = 10; + let scrollCount = 0; + let endPos: number; + let timer: ReturnType; + const workflow = window.__vscroll__.workflow; + const adapter = window.__vscroll__.datasource.adapter; + + // Subscribe to cyclesDone$ - stop timer when the nearest cycle completes (cycle 2) + workflow.cyclesDone$.on(count => { + if (count === 2) { + clearInterval(timer); + const position = workflow.scroller.viewport.scrollPosition; + resolve({ endPos, position }); + } + }); + + // Start the scrolling timer + timer = setInterval( + () => + requestAnimationFrame(() => { + scrollCount++; + endPos = start + scrollCount * 5; + + // Trigger scroll via adapter.fix + adapter.fix({ scrollPosition: endPos }); + + // Stop timer if all scrolls done + if (scrollCount === COUNT) { + clearInterval(timer); + } + }), + 25 + ); + }) + , { start: startPosition }); + + + // Verify final position + expect(result.position).toBe(result.endPos); + expect(result.endPos).toBeGreaterThan(startPosition); + + await fixture.cleanup(); + }); +}); + From 3c8ec1c33dd819b4a1634252861f7161ce11e903 Mon Sep 17 00:00:00 2001 From: Denis Aleksanov Date: Tue, 11 Nov 2025 15:01:46 +0100 Subject: [PATCH 11/51] issue-66 devSettings.logColor --- src/classes/logger.ts | 50 ++++++++++++++++++++++++++------------ src/classes/settings.ts | 8 ++++++ src/inputs/settings.ts | 5 ++++ src/interfaces/settings.ts | 1 + src/workflow.ts | 16 ++++++------ 5 files changed, 57 insertions(+), 23 deletions(-) diff --git a/src/classes/logger.ts b/src/classes/logger.ts index a94abf24..41bae2f3 100644 --- a/src/classes/logger.ts +++ b/src/classes/logger.ts @@ -9,6 +9,7 @@ export class Logger { readonly debug: boolean; readonly immediateLog: boolean; readonly logTime: boolean; + readonly logColor: boolean; readonly getTime: () => string; readonly getStat: () => string; readonly getFetchRange: () => string; @@ -23,6 +24,7 @@ export class Logger { this.debug = settings.debug; this.immediateLog = settings.immediateLog; this.logTime = settings.logTime; + this.logColor = settings.logColor; this.getTime = (): string => scroller.state && ` // time: ${scroller.state.time}`; this.getStat = (): string => { @@ -91,19 +93,27 @@ export class Logger { stat(str?: string): void { if (this.debug) { - const logStyles = [ - 'color: #888; border: dashed #888 0; border-bottom-width: 0px', - 'color: #000; border-width: 0' - ]; - this.log(() => ['%cstat' + (str ? ` ${str}` : '') + ',%c ' + this.getStat(), ...logStyles]); + if (this.logColor) { + const logStyles = [ + 'color: #888; border: dashed #888 0; border-bottom-width: 0px', + 'color: #000; border-width: 0' + ]; + this.log(() => ['%cstat' + (str ? ` ${str}` : '') + ',%c ' + this.getStat(), ...logStyles]); + } else { + this.log(() => ['stat' + (str ? ` ${str}` : '') + ', ' + this.getStat()]); + } } } fetch(str?: string): void { if (this.debug) { const _text = 'fetch interval' + (str ? ` ${str}` : ''); - const logStyles = ['color: #888', 'color: #000']; - this.log(() => [`%c${_text}: %c${this.getFetchRange()}`, ...logStyles]); + if (this.logColor) { + const logStyles = ['color: #888', 'color: #000']; + this.log(() => [`%c${_text}: %c${this.getFetchRange()}`, ...logStyles]); + } else { + this.log(() => [`${_text}: ${this.getFetchRange()}`]); + } } } @@ -124,32 +134,42 @@ export class Logger { if ( process === CommonProcess.init && status === Status.next ) { - loopLog.push(`%c---=== loop ${this.getLoopIdNext()} start`); + const loopStart = `---=== loop ${this.getLoopIdNext()} start`; + loopLog.push(this.logColor ? `%c${loopStart}` : loopStart); } else if ( process === CommonProcess.end ) { - loopLog.push(`%c---=== loop ${this.getLoopId()} done`); + const loopDone = `---=== loop ${this.getLoopId()} done`; + loopLog.push(this.logColor ? `%c${loopDone}` : loopDone); const parent = payload && payload.process; if (status === Status.next && (parent !== AdapterProcess.reset && parent !== AdapterProcess.reload)) { loopLog[0] += `, loop ${this.getLoopIdNext()} start`; } } if (loopLog.length) { - this.log(() => [...loopLog, 'color: #006600;']); + this.log(() => this.logColor ? [...loopLog, 'color: #006600;'] : loopLog); } } logCycle(start = true): void { const logData = this.getWorkflowCycleData(); - const border = start ? '1px 0 0 1px' : '0 0 1px 1px'; - const logStyles = `color: #0000aa; border: solid #555 1px; border-width: ${border}; margin-left: -2px`; - this.log(() => [`%c ~~~ WF Cycle ${logData} ${start ? 'STARTED' : 'FINALIZED'} ~~~ `, logStyles]); + if (this.logColor) { + const border = start ? '1px 0 0 1px' : '0 0 1px 1px'; + const logStyles = `color: #0000aa; border: solid #555 1px; border-width: ${border}; margin-left: -2px`; + this.log(() => [`%c ~~~ WF Cycle ${logData} ${start ? 'STARTED' : 'FINALIZED'} ~~~ `, logStyles]); + } else { + this.log(() => [` ~~~ WF Cycle ${logData} ${start ? 'STARTED' : 'FINALIZED'} ~~~ `]); + } } logError(str: string): void { if (this.debug) { - const logStyles = ['color: #a00;', 'color: #000']; - this.log(() => ['error:%c' + (str ? ` ${str}` : '') + `%c (loop ${this.getLoopIdNext()})`, ...logStyles]); + if (this.logColor) { + const logStyles = ['color: #a00;', 'color: #000']; + this.log(() => ['error:%c' + (str ? ` ${str}` : '') + `%c (loop ${this.getLoopIdNext()})`, ...logStyles]); + } else { + this.log(() => ['error:' + (str ? ` ${str}` : '') + ` (loop ${this.getLoopIdNext()})`]); + } } } diff --git a/src/classes/settings.ts b/src/classes/settings.ts index 34fdaced..4c67a24a 100644 --- a/src/classes/settings.ts +++ b/src/classes/settings.ts @@ -51,6 +51,14 @@ export class Settings implements ISettings, IDevSettings { */ logProcessRun: boolean; + /** + * Development setting. + * If true, console color formatting will be used in logs. + * Default value: true. + * @type {boolean} + */ + logColor: boolean; + /** * Development setting. * If set, scroll event handling is throttled (ms). diff --git a/src/inputs/settings.ts b/src/inputs/settings.ts index 8e821253..9ace0a03 100644 --- a/src/inputs/settings.ts +++ b/src/inputs/settings.ts @@ -26,6 +26,7 @@ enum DevSettings { immediateLog = 'immediateLog', logProcessRun = 'logProcessRun', logTime = 'logTime', + logColor = 'logColor', throttle = 'throttle', initDelay = 'initDelay', initWindowDelay = 'initWindowDelay', @@ -120,6 +121,10 @@ export const DEV_SETTINGS: ICommonProps = { validators: [BOOLEAN], defaultValue: false }, + [DevSettings.logColor]: { + validators: [BOOLEAN], + defaultValue: true + }, [DevSettings.throttle]: { validators: [INTEGER, MORE_OR_EQUAL(MIN[DevSettings.throttle], true)], defaultValue: 40 diff --git a/src/interfaces/settings.ts b/src/interfaces/settings.ts index d83b866a..da966322 100644 --- a/src/interfaces/settings.ts +++ b/src/interfaces/settings.ts @@ -23,6 +23,7 @@ export interface DevSettings { immediateLog?: boolean; logProcessRun?: boolean; logTime?: boolean; + logColor?: boolean; throttle?: number; initDelay?: number; initWindowDelay?: number; diff --git a/src/workflow.ts b/src/workflow.ts index f7e4bd5a..ac720efc 100644 --- a/src/workflow.ts +++ b/src/workflow.ts @@ -125,10 +125,10 @@ export class Workflow { process(data: ProcessSubject): void { const { status, process, payload } = data; if (this.scroller.settings.logProcessRun) { - this.scroller.logger.log(() => [ - '%cfire%c', ...['color: #cc7777;', 'color: #000000;'], - process, `"${status}"`, ...(payload !== void 0 ? [payload] : []) - ]); + const _fire = this.scroller.settings.logColor + ? ['%cfire%c', 'color: #cc7777;', 'color: #000000;'] + : ['fire']; + this.scroller.logger.log(() => [..._fire, process, `"${status}"`, ...(payload !== void 0 ? [payload] : [])]); } this.scroller.logger.logProcess(data); @@ -145,10 +145,10 @@ export class Workflow { return ({ run, process, name }: ProcessClass) => (...args: unknown[]): void => { if (this.scroller.settings.logProcessRun) { - this.scroller.logger.log(() => [ - '%crun%c', ...['color: #333399;', 'color: #000000;'], - process || name, ...args - ]); + const _run = this.scroller.settings.logColor + ? ['%crun%c', 'color: #333399;', 'color: #000000;'] + : ['run']; + this.scroller.logger.log(() => [..._run, process || name, ...args]); } run(this.scroller as Scroller, ...args); }; From ec23b704732ba19d6e199c4b69939e653510a1f4 Mon Sep 17 00:00:00 2001 From: Denis Aleksanov Date: Tue, 11 Nov 2025 15:02:20 +0100 Subject: [PATCH 12/51] issue-66 detailed console output --- e2e/playwright.config.ts | 5 ++++- 1 file changed, 4 insertions(+), 1 deletion(-) diff --git a/e2e/playwright.config.ts b/e2e/playwright.config.ts index 58827681..585e83cc 100644 --- a/e2e/playwright.config.ts +++ b/e2e/playwright.config.ts @@ -7,7 +7,10 @@ export default defineConfig({ forbidOnly: !!process.env.CI, retries: process.env.CI ? 2 : 0, workers: process.env.CI ? 1 : undefined, - reporter: [['html', { outputFolder: './playwright-report' }]], + reporter: [ + ['list'], // Detailed console output + ['html', { outputFolder: './playwright-report' }] + ], // timeout: 10 * 60 * 1000, // 10 minutes per test use: { From ad0a25bba3a669db7a6293762ccef8ba626181f6 Mon Sep 17 00:00:00 2001 From: Denis Aleksanov Date: Tue, 11 Nov 2025 15:07:19 +0100 Subject: [PATCH 13/51] issue-66 add browser dev log for all tests --- e2e/fixture/VScrollFixture.ts | 9 ++++++++- e2e/specs/scroll-basic.spec.ts | 6 ------ 2 files changed, 8 insertions(+), 7 deletions(-) diff --git a/e2e/fixture/VScrollFixture.ts b/e2e/fixture/VScrollFixture.ts index 4923ff20..5cc122b4 100644 --- a/e2e/fixture/VScrollFixture.ts +++ b/e2e/fixture/VScrollFixture.ts @@ -126,7 +126,14 @@ export class VScrollFixture { // Serialize functions and config const datasourceGetStr = datasource.get.toString(); const datasourceSettings = datasource.settings || {}; - const datasourceDevSettings = datasource.devSettings || {}; // Include devSettings + // Add default devSettings for all e2e tests: debug enabled, colors disabled + const datasourceDevSettings = { + debug: true, + logProcessRun: true, + immediateLog: true, + logColor: false, + ...(datasource.devSettings || {}) + }; const templateFnStr = templateFn ? templateFn.toString() : '(item) => `
${item.$index}: ${item.data.text || ""}
`'; diff --git a/e2e/specs/scroll-basic.spec.ts b/e2e/specs/scroll-basic.spec.ts index 47356392..75ce1411 100644 --- a/e2e/specs/scroll-basic.spec.ts +++ b/e2e/specs/scroll-basic.spec.ts @@ -12,7 +12,6 @@ type IConfig = ITestConfig<{ mass?: boolean; }>; -// Test configurations (same as original) const configList: IConfig[] = [ { datasourceSettings: { @@ -21,7 +20,6 @@ const configList: IConfig[] = [ padding: 0.22, itemSize: 20 }, - datasourceDevSettings: { debug: true, immediateLog: true }, templateSettings: { viewportHeight: 71, itemHeight: 20 }, custom: { direction: Direction.forward, count: 1 } }, @@ -32,7 +30,6 @@ const configList: IConfig[] = [ padding: 0.2, itemSize: 20 }, - datasourceDevSettings: { debug: true, immediateLog: true }, templateSettings: { viewportHeight: 100, itemHeight: 20 }, custom: { direction: Direction.forward, count: 1 } }, @@ -43,7 +40,6 @@ const configList: IConfig[] = [ padding: 0.98, itemSize: 20 }, - datasourceDevSettings: { debug: true, immediateLog: true }, templateSettings: { viewportHeight: 66, itemHeight: 20 }, custom: { direction: Direction.forward, count: 1 } }, @@ -55,7 +51,6 @@ const configList: IConfig[] = [ horizontal: true, itemSize: 100 }, - datasourceDevSettings: { debug: true, immediateLog: true }, templateSettings: { viewportWidth: 450, itemWidth: 100, horizontal: true }, custom: { direction: Direction.forward, count: 1 } }, @@ -67,7 +62,6 @@ const configList: IConfig[] = [ horizontal: true, itemSize: 75 }, - datasourceDevSettings: { debug: true, immediateLog: true }, templateSettings: { viewportWidth: 300, itemWidth: 75, horizontal: true }, custom: { direction: Direction.forward, count: 1 } } From e01efb686b422c1e076df14ffae1e8bc6c79c784 Mon Sep 17 00:00:00 2001 From: Denis Aleksanov Date: Tue, 11 Nov 2025 15:44:50 +0100 Subject: [PATCH 14/51] issue-66 Logger.getLogs() --- src/classes/logger.ts | 6 +++++- 1 file changed, 5 insertions(+), 1 deletion(-) diff --git a/src/classes/logger.ts b/src/classes/logger.ts index 41bae2f3..9099d485 100644 --- a/src/classes/logger.ts +++ b/src/classes/logger.ts @@ -17,7 +17,7 @@ export class Logger { readonly getLoopId: () => string; readonly getLoopIdNext: () => string; readonly getScrollPosition: () => number; - private logs: unknown[][] = []; + private logs: LogType[] = []; constructor(scroller: Scroller, packageInfo: IPackages, adapter?: { id: number }) { const { settings } = scroller; @@ -238,4 +238,8 @@ export class Logger { } } } + + getLogs(): LogType[] { + return this.logs; + } } From d1b3b2c918f5b9e7ad87211eb8e38347fa656e78 Mon Sep 17 00:00:00 2001 From: Denis Aleksanov Date: Tue, 11 Nov 2025 16:04:31 +0100 Subject: [PATCH 15/51] issue-66 types --- e2e/specs/scroll-basic.spec.ts | 9 +++------ e2e/specs/scroll-delay.spec.ts | 4 +--- e2e/types/index.ts | 4 ++++ 3 files changed, 8 insertions(+), 9 deletions(-) diff --git a/e2e/specs/scroll-basic.spec.ts b/e2e/specs/scroll-basic.spec.ts index 75ce1411..97e1d887 100644 --- a/e2e/specs/scroll-basic.spec.ts +++ b/e2e/specs/scroll-basic.spec.ts @@ -1,9 +1,7 @@ import { test, expect } from '@playwright/test'; -import type { Page } from '@playwright/test'; import { VScrollFixture, Direction, type DirectionType } from '../fixture/VScrollFixture.js'; import { ItemsCounter } from '../helpers/itemsCounter.js'; -import type { IDatasource } from '../../src/index.js'; -import { ITestConfig } from 'types/index.js'; +import { ITestConfig, Page, IDatasource } from 'types/index.js'; type IConfig = ITestConfig<{ direction: DirectionType; @@ -182,7 +180,7 @@ const getCurrentItemsCounter = async ( // Create test fixture from config const createFixture = async (page: Page, config: IConfig): Promise => { - const { datasourceSettings, datasourceDevSettings, templateSettings } = config; + const { datasourceSettings, templateSettings } = config; // // Capture browser console logs // page.on('console', (msg: any) => { @@ -198,8 +196,7 @@ const createFixture = async (page: Page, config: IConfig): Promise success(data), 25); }, - settings: datasourceSettings, - devSettings: datasourceDevSettings + settings: datasourceSettings }; const fixture = await VScrollFixture.create(page, { diff --git a/e2e/specs/scroll-delay.spec.ts b/e2e/specs/scroll-delay.spec.ts index 6b3a35b9..512dd626 100644 --- a/e2e/specs/scroll-delay.spec.ts +++ b/e2e/specs/scroll-delay.spec.ts @@ -1,8 +1,6 @@ import { test, expect } from '@playwright/test'; -import type { Page } from '@playwright/test'; import { VScrollFixture } from '../fixture/VScrollFixture.js'; -import type { IDatasource } from '../../src/index.js'; -import { ITestConfig } from 'types/index.js'; +import { ITestConfig, Page, IDatasource } from 'types/index.js'; /** * Create fixture with datasource delay diff --git a/e2e/types/index.ts b/e2e/types/index.ts index 848d2ea9..ba086f6d 100644 --- a/e2e/types/index.ts +++ b/e2e/types/index.ts @@ -1,6 +1,10 @@ +import type { Page } from '@playwright/test'; +import type { IDatasource } from '../../src/index.js'; import type { Settings, DevSettings } from '../../src/interfaces/settings.js'; +export type { Page }; +export type { IDatasource }; interface ITemplateSettings { viewportHeight?: number; viewportWidth?: number; From b28ea5d342efc0364a3cb0baa62b35fa63f70ee2 Mon Sep 17 00:00:00 2001 From: Denis Aleksanov Date: Tue, 11 Nov 2025 16:04:57 +0100 Subject: [PATCH 16/51] issue-66 test.afterEach(afterEachLogs); --- e2e/fixture/VScrollFixture.ts | 6 ++-- e2e/fixture/after-each-logs.ts | 55 ++++++++++++++++++++++++++++++++++ e2e/specs/scroll-basic.spec.ts | 3 ++ e2e/specs/scroll-delay.spec.ts | 3 ++ 4 files changed, 64 insertions(+), 3 deletions(-) create mode 100644 e2e/fixture/after-each-logs.ts diff --git a/e2e/fixture/VScrollFixture.ts b/e2e/fixture/VScrollFixture.ts index 5cc122b4..80b3f0f7 100644 --- a/e2e/fixture/VScrollFixture.ts +++ b/e2e/fixture/VScrollFixture.ts @@ -126,11 +126,11 @@ export class VScrollFixture { // Serialize functions and config const datasourceGetStr = datasource.get.toString(); const datasourceSettings = datasource.settings || {}; - // Add default devSettings for all e2e tests: debug enabled, colors disabled + // Add default devSettings for all e2e tests: + // debug enabled, colors disabled, immediateLog disabled (to store logs in logger for retrieval on test failure) const datasourceDevSettings = { debug: true, - logProcessRun: true, - immediateLog: true, + immediateLog: false, logColor: false, ...(datasource.devSettings || {}) }; diff --git a/e2e/fixture/after-each-logs.ts b/e2e/fixture/after-each-logs.ts new file mode 100644 index 00000000..bd4b842d --- /dev/null +++ b/e2e/fixture/after-each-logs.ts @@ -0,0 +1,55 @@ +import type { Page, TestInfo } from '@playwright/test'; + +/** + * afterEachLogs handler that retrieves and prints vscroll debug logs when tests fail. + * + * With immediateLog: false, logs are stored in workflow.scroller.logger + * and can be retrieved from the browser context. + * + * Usage: + * ```typescript + * import { afterEachLogs } from '../fixture/after-each-logs.js'; + * + * test.afterEach(afterEachLogs); + * ``` + */ +export async function afterEachLogs({ page }: { page: Page }, testInfo: TestInfo): Promise { + // Only process if test failed + if (testInfo.status !== testInfo.expectedStatus) { + // Get logs from vscroll logger + const logs = await page.evaluate(() => { + const vscroll = window.__vscroll__; + if (!vscroll?.workflow?.scroller?.logger) { + return []; + } + + const logger = vscroll.workflow.scroller.logger; + // Access private logs array (stored when immediateLog: false) + const logArray = logger.getLogs() || []; + + // Format logs similar to console output + return logArray.map(args => { + return args.map(arg => { + if (typeof arg === 'object' && arg !== null) { + return JSON.stringify(arg); + } + return String(arg); + }).join(' '); + }); + }).catch(() => [] as string[]); + + if (logs.length > 0) { + console.log('\n' + '='.repeat(80)); + console.log('VSCROLL DEBUG LOGS (test failed):'); + console.log('='.repeat(80)); + logs.forEach(log => console.log(log)); + console.log('='.repeat(80) + '\n'); + + await testInfo.attach('vscroll-debug-logs', { + body: logs.join('\n'), + contentType: 'text/plain' + }); + } + } +} + diff --git a/e2e/specs/scroll-basic.spec.ts b/e2e/specs/scroll-basic.spec.ts index 97e1d887..ccabc736 100644 --- a/e2e/specs/scroll-basic.spec.ts +++ b/e2e/specs/scroll-basic.spec.ts @@ -1,8 +1,11 @@ import { test, expect } from '@playwright/test'; +import { afterEachLogs } from '../fixture/after-each-logs.js'; import { VScrollFixture, Direction, type DirectionType } from '../fixture/VScrollFixture.js'; import { ItemsCounter } from '../helpers/itemsCounter.js'; import { ITestConfig, Page, IDatasource } from 'types/index.js'; +test.afterEach(afterEachLogs); + type IConfig = ITestConfig<{ direction: DirectionType; count: number; diff --git a/e2e/specs/scroll-delay.spec.ts b/e2e/specs/scroll-delay.spec.ts index 512dd626..26989b67 100644 --- a/e2e/specs/scroll-delay.spec.ts +++ b/e2e/specs/scroll-delay.spec.ts @@ -1,7 +1,10 @@ import { test, expect } from '@playwright/test'; +import { afterEachLogs } from '../fixture/after-each-logs.js'; import { VScrollFixture } from '../fixture/VScrollFixture.js'; import { ITestConfig, Page, IDatasource } from 'types/index.js'; +test.afterEach(afterEachLogs); + /** * Create fixture with datasource delay */ From 1a6eae7e9d1a45715846897373a5cfa357ed2532 Mon Sep 17 00:00:00 2001 From: Denis Aleksanov Date: Tue, 11 Nov 2025 16:09:17 +0100 Subject: [PATCH 17/51] issue-66 test.afterEach(afterEachLogs) | spec name in the log --- e2e/fixture/after-each-logs.ts | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/e2e/fixture/after-each-logs.ts b/e2e/fixture/after-each-logs.ts index bd4b842d..e8527a0b 100644 --- a/e2e/fixture/after-each-logs.ts +++ b/e2e/fixture/after-each-logs.ts @@ -39,8 +39,9 @@ export async function afterEachLogs({ page }: { page: Page }, testInfo: TestInfo }).catch(() => [] as string[]); if (logs.length > 0) { + const testName = testInfo.titlePath.join(' › '); console.log('\n' + '='.repeat(80)); - console.log('VSCROLL DEBUG LOGS (test failed):'); + console.log(`VSCROLL DEBUG LOGS (test failed): ${testName}`); console.log('='.repeat(80)); logs.forEach(log => console.log(log)); console.log('='.repeat(80) + '\n'); From ecbb7804901c79d5092d878de04d354c603fe06c Mon Sep 17 00:00:00 2001 From: Denis Aleksanov Date: Tue, 11 Nov 2025 16:19:05 +0100 Subject: [PATCH 18/51] issue-66 tests/unit, tests/e2e --- .gitignore | 4 ++-- jest.config.js | 3 +-- package.json | 6 +++--- {e2e => tests/e2e}/fixture/Adapter.ts | 0 {e2e => tests/e2e}/fixture/Scroller.ts | 0 {e2e => tests/e2e}/fixture/VScrollFixture.ts | 0 {e2e => tests/e2e}/fixture/after-each-logs.ts | 0 {e2e => tests/e2e}/helpers/itemsCounter.ts | 0 {e2e => tests/e2e}/playwright.config.ts | 0 {e2e => tests/e2e}/specs/scroll-basic.spec.ts | 0 {e2e => tests/e2e}/specs/scroll-delay.spec.ts | 0 {e2e => tests/e2e}/tsconfig.json | 0 {e2e => tests/e2e}/types/global.d.ts | 0 {e2e => tests/e2e}/types/index.ts | 0 tests/{ => unit}/adapter.spec.ts | 10 +++++----- tests/{ => unit}/buffer.spec.ts | 4 ++-- tests/{ => unit}/cache.spec.ts | 6 +++--- tests/{ => unit}/datasource-bad.spec.ts | 2 +- tests/{ => unit}/misc/items.ts | 4 ++-- tests/{ => unit}/misc/types.ts | 4 ++-- tests/{ => unit}/reactive.spec.ts | 2 +- tests/{ => unit}/routines.spec.ts | 4 ++-- tests/{ => unit}/validation.spec.ts | 4 ++-- 23 files changed, 26 insertions(+), 27 deletions(-) rename {e2e => tests/e2e}/fixture/Adapter.ts (100%) rename {e2e => tests/e2e}/fixture/Scroller.ts (100%) rename {e2e => tests/e2e}/fixture/VScrollFixture.ts (100%) rename {e2e => tests/e2e}/fixture/after-each-logs.ts (100%) rename {e2e => tests/e2e}/helpers/itemsCounter.ts (100%) rename {e2e => tests/e2e}/playwright.config.ts (100%) rename {e2e => tests/e2e}/specs/scroll-basic.spec.ts (100%) rename {e2e => tests/e2e}/specs/scroll-delay.spec.ts (100%) rename {e2e => tests/e2e}/tsconfig.json (100%) rename {e2e => tests/e2e}/types/global.d.ts (100%) rename {e2e => tests/e2e}/types/index.ts (100%) rename tests/{ => unit}/adapter.spec.ts (95%) rename tests/{ => unit}/buffer.spec.ts (99%) rename tests/{ => unit}/cache.spec.ts (99%) rename tests/{ => unit}/datasource-bad.spec.ts (92%) rename tests/{ => unit}/misc/items.ts (90%) rename tests/{ => unit}/misc/types.ts (91%) rename tests/{ => unit}/reactive.spec.ts (98%) rename tests/{ => unit}/routines.spec.ts (95%) rename tests/{ => unit}/validation.spec.ts (99%) diff --git a/.gitignore b/.gitignore index 073413fb..e0afc50e 100644 --- a/.gitignore +++ b/.gitignore @@ -3,5 +3,5 @@ node_modules .vscode *.tgz .DS_Store -e2e/playwright-report -e2e/test-results +tests/e2e/playwright-report +tests/e2e/test-results diff --git a/jest.config.js b/jest.config.js index 675fbf9d..bba98f53 100644 --- a/jest.config.js +++ b/jest.config.js @@ -1,6 +1,5 @@ export default { - testMatch: ['**/tests/**/*.spec.ts'], - testPathIgnorePatterns: ['/node_modules/', '/e2e/'], + testMatch: ['**/tests/unit/**/*.spec.ts'], transform: { '^.+\\.ts$': 'babel-jest' }, diff --git a/package.json b/package.json index 5dc7dbcf..b5963ec1 100644 --- a/package.json +++ b/package.json @@ -29,9 +29,9 @@ "watch": "jest --watch", "build": "node build", "test:unit": "npm run lint && npm run jest", - "test:e2e": "playwright test --config=e2e/playwright.config.ts", - "test:e2e:ui": "playwright test --config=e2e/playwright.config.ts --ui", - "test:e2e:debug": "playwright test --config=e2e/playwright.config.ts --debug", + "test:e2e": "playwright test --config=tests/e2e/playwright.config.ts", + "test:e2e:ui": "playwright test --config=tests/e2e/playwright.config.ts --ui", + "test:e2e:debug": "playwright test --config=tests/e2e/playwright.config.ts --debug", "test": "npm run test:unit && npm run test:e2e", "prepack": "npm run build", "checkGit": "sh ./uncommited.sh", diff --git a/e2e/fixture/Adapter.ts b/tests/e2e/fixture/Adapter.ts similarity index 100% rename from e2e/fixture/Adapter.ts rename to tests/e2e/fixture/Adapter.ts diff --git a/e2e/fixture/Scroller.ts b/tests/e2e/fixture/Scroller.ts similarity index 100% rename from e2e/fixture/Scroller.ts rename to tests/e2e/fixture/Scroller.ts diff --git a/e2e/fixture/VScrollFixture.ts b/tests/e2e/fixture/VScrollFixture.ts similarity index 100% rename from e2e/fixture/VScrollFixture.ts rename to tests/e2e/fixture/VScrollFixture.ts diff --git a/e2e/fixture/after-each-logs.ts b/tests/e2e/fixture/after-each-logs.ts similarity index 100% rename from e2e/fixture/after-each-logs.ts rename to tests/e2e/fixture/after-each-logs.ts diff --git a/e2e/helpers/itemsCounter.ts b/tests/e2e/helpers/itemsCounter.ts similarity index 100% rename from e2e/helpers/itemsCounter.ts rename to tests/e2e/helpers/itemsCounter.ts diff --git a/e2e/playwright.config.ts b/tests/e2e/playwright.config.ts similarity index 100% rename from e2e/playwright.config.ts rename to tests/e2e/playwright.config.ts diff --git a/e2e/specs/scroll-basic.spec.ts b/tests/e2e/specs/scroll-basic.spec.ts similarity index 100% rename from e2e/specs/scroll-basic.spec.ts rename to tests/e2e/specs/scroll-basic.spec.ts diff --git a/e2e/specs/scroll-delay.spec.ts b/tests/e2e/specs/scroll-delay.spec.ts similarity index 100% rename from e2e/specs/scroll-delay.spec.ts rename to tests/e2e/specs/scroll-delay.spec.ts diff --git a/e2e/tsconfig.json b/tests/e2e/tsconfig.json similarity index 100% rename from e2e/tsconfig.json rename to tests/e2e/tsconfig.json diff --git a/e2e/types/global.d.ts b/tests/e2e/types/global.d.ts similarity index 100% rename from e2e/types/global.d.ts rename to tests/e2e/types/global.d.ts diff --git a/e2e/types/index.ts b/tests/e2e/types/index.ts similarity index 100% rename from e2e/types/index.ts rename to tests/e2e/types/index.ts diff --git a/tests/adapter.spec.ts b/tests/unit/adapter.spec.ts similarity index 95% rename from tests/adapter.spec.ts rename to tests/unit/adapter.spec.ts index 249e2aec..0a622e5c 100644 --- a/tests/adapter.spec.ts +++ b/tests/unit/adapter.spec.ts @@ -1,8 +1,8 @@ -import { Scroller } from '../src/scroller'; -import { AdapterPropName } from '../src/classes/adapter/props'; -import { Datasource } from '../src/classes/datasource'; -import { wantedUtils } from '../src/classes/adapter/wanted'; -import version from '../src/version'; +import { Scroller } from '../../src/scroller'; +import { AdapterPropName } from '../../src/classes/adapter/props'; +import { Datasource } from '../../src/classes/datasource'; +import { wantedUtils } from '../../src/classes/adapter/wanted'; +import version from '../../src/version'; const MOCK = { datasource: { diff --git a/tests/buffer.spec.ts b/tests/unit/buffer.spec.ts similarity index 99% rename from tests/buffer.spec.ts rename to tests/unit/buffer.spec.ts index 5b4a91e5..b937cccd 100644 --- a/tests/buffer.spec.ts +++ b/tests/unit/buffer.spec.ts @@ -1,6 +1,6 @@ -import { Buffer } from '../src/classes/buffer'; +import { Buffer } from '../../src/classes/buffer'; -import { Direction } from '../src/inputs'; +import { Direction } from '../../src/inputs'; import { generateItem as makeItem, generateBufferItem as cb, generateBufferItems, generateItem } from './misc/items'; import { Data, BufferParams, BufferUpdateConfig, BufferUpdateTrackConfig, BufferInsertConfig diff --git a/tests/cache.spec.ts b/tests/unit/cache.spec.ts similarity index 99% rename from tests/cache.spec.ts rename to tests/unit/cache.spec.ts index 4a2485f2..9688e09d 100644 --- a/tests/cache.spec.ts +++ b/tests/unit/cache.spec.ts @@ -1,9 +1,9 @@ -import { Cache } from '../src/classes/buffer/cache'; -import { Item } from '../src/classes/item'; +import { Cache } from '../../src/classes/buffer/cache'; +import { Item } from '../../src/classes/item'; import { Data, Id, IndexIdList, IndexSizeList } from './misc/types'; import { generateBufferItem, generateBufferItems, generateItem } from './misc/items'; -import { Direction, SizeStrategy } from '../src/inputs'; +import { Direction, SizeStrategy } from '../../src/inputs'; interface ActionCacheConfig { title: string; diff --git a/tests/datasource-bad.spec.ts b/tests/unit/datasource-bad.spec.ts similarity index 92% rename from tests/datasource-bad.spec.ts rename to tests/unit/datasource-bad.spec.ts index f6a157d4..09bb9cdd 100644 --- a/tests/datasource-bad.spec.ts +++ b/tests/unit/datasource-bad.spec.ts @@ -1,4 +1,4 @@ -import { INVALID_DATASOURCE_PREFIX, Scroller } from '../src/scroller'; +import { INVALID_DATASOURCE_PREFIX, Scroller } from '../../src/scroller'; describe('Wrong Datasource on Scroller instantiation', () => { diff --git a/tests/misc/items.ts b/tests/unit/misc/items.ts similarity index 90% rename from tests/misc/items.ts rename to tests/unit/misc/items.ts index 55b526ce..7060d638 100644 --- a/tests/misc/items.ts +++ b/tests/unit/misc/items.ts @@ -1,5 +1,5 @@ -import { Item } from '../../src/classes/item'; -import { ItemCache } from '../../src/classes/buffer/cache'; +import { Item } from '../../../src/classes/item'; +import { ItemCache } from '../../../src/classes/buffer/cache'; import { Id, Data } from './types'; const MIN_SIZE = 1; diff --git a/tests/misc/types.ts b/tests/unit/misc/types.ts similarity index 91% rename from tests/misc/types.ts rename to tests/unit/misc/types.ts index d4a74b81..e05cd4eb 100644 --- a/tests/misc/types.ts +++ b/tests/unit/misc/types.ts @@ -1,5 +1,5 @@ -import { Direction } from '../../src/inputs'; -import { BufferUpdater } from '../../src/interfaces'; +import { Direction } from '../../../src/inputs'; +import { BufferUpdater } from '../../../src/interfaces'; export type Id = number | string; diff --git a/tests/reactive.spec.ts b/tests/unit/reactive.spec.ts similarity index 98% rename from tests/reactive.spec.ts rename to tests/unit/reactive.spec.ts index 9cab3a3f..ce7f3281 100644 --- a/tests/reactive.spec.ts +++ b/tests/unit/reactive.spec.ts @@ -1,4 +1,4 @@ -import { Reactive } from '../src/classes/reactive'; +import { Reactive } from '../../src/classes/reactive'; describe('Reactive', () => { diff --git a/tests/routines.spec.ts b/tests/unit/routines.spec.ts similarity index 95% rename from tests/routines.spec.ts rename to tests/unit/routines.spec.ts index 1c88a8c9..07c7d09f 100644 --- a/tests/routines.spec.ts +++ b/tests/unit/routines.spec.ts @@ -1,5 +1,5 @@ -import { Routines } from '../src/classes/domRoutines'; -import { Settings } from '../src/classes/settings'; +import { Routines } from '../../src/classes/domRoutines'; +import { Settings } from '../../src/classes/settings'; const settings = new Settings(void 0, void 0, 0); const element = { parentElement: { style: {} } } as unknown as HTMLElement; diff --git a/tests/validation.spec.ts b/tests/unit/validation.spec.ts similarity index 99% rename from tests/validation.spec.ts rename to tests/unit/validation.spec.ts index c39de210..99bc59e7 100644 --- a/tests/validation.spec.ts +++ b/tests/unit/validation.spec.ts @@ -1,5 +1,5 @@ -import { ValidatorType, VALIDATORS, validateOne, validate } from '../src/inputs'; -import { IValidator } from '../src/interfaces'; +import { ValidatorType, VALIDATORS, validateOne, validate } from '../../src/inputs'; +import { IValidator } from '../../src/interfaces'; const { INTEGER, From 4bca5d013a9426796c18284195277e43938cce28 Mon Sep 17 00:00:00 2001 From: Denis Aleksanov Date: Tue, 11 Nov 2025 16:31:22 +0100 Subject: [PATCH 19/51] issue-66 own package.json for tests --- .github/workflows/build.yml | 17 +- .github/workflows/general.yml | 12 +- package-lock.json | 8695 +++--------------- package.json | 19 +- babel.config.json => tests/babel.config.json | 0 tests/e2e/fixture/Scroller.ts | 2 +- tests/e2e/fixture/VScrollFixture.ts | 4 +- tests/jest.config.js | 10 + tests/package-lock.json | 6047 ++++++++++++ tests/package.json | 25 + 10 files changed, 7447 insertions(+), 7384 deletions(-) rename babel.config.json => tests/babel.config.json (100%) create mode 100644 tests/jest.config.js create mode 100644 tests/package-lock.json create mode 100644 tests/package.json diff --git a/.github/workflows/build.yml b/.github/workflows/build.yml index e93beead..c4a6a937 100644 --- a/.github/workflows/build.yml +++ b/.github/workflows/build.yml @@ -34,6 +34,17 @@ jobs: with: node-version: ${{ matrix.node-version }} - - run: npm ci - - run: npm run build - - run: npm run test:unit + - name: Install root dependencies + run: npm ci + + - name: Lint + run: npm run lint + + - name: Build + run: npm run build + + - name: Install test dependencies + run: cd tests && npm ci + + - name: Run unit tests + run: npm run test:unit diff --git a/.github/workflows/general.yml b/.github/workflows/general.yml index 09b6e9c1..597e26ad 100644 --- a/.github/workflows/general.yml +++ b/.github/workflows/general.yml @@ -27,17 +27,23 @@ jobs: with: node-version: 22.x - - name: Install dependencies + - name: Install root dependencies run: npm ci + - name: Lint + run: npm run lint + - name: Build run: npm run build + - name: Install test dependencies + run: cd tests && npm ci + - name: Run unit tests run: npm run test:unit - name: Install Playwright Browsers - run: npx playwright install --with-deps chromium + run: cd tests && npx playwright install --with-deps chromium - name: Run e2e tests run: npm run test:e2e @@ -47,6 +53,6 @@ jobs: if: ${{ !cancelled() }} with: name: playwright-report - path: vscroll/e2e/playwright-report/ + path: tests/e2e/playwright-report/ retention-days: 30 diff --git a/package-lock.json b/package-lock.json index ac0892b5..81aa738b 100644 --- a/package-lock.json +++ b/package-lock.json @@ -12,20 +12,13 @@ "tslib": "^2.3.1" }, "devDependencies": { - "@babel/core": "^7.16.12", - "@babel/preset-env": "^7.28.0", - "@babel/preset-typescript": "^7.27.1", - "@playwright/test": "^1.56.1", "@rollup/plugin-node-resolve": "^16.0.1", "@rollup/plugin-terser": "^0.4.4", - "@types/jest": "^30.0.0", "@typescript-eslint/eslint-plugin": "^8.38.0", "@typescript-eslint/parser": "^8.38.0", - "babel-jest": "^30.0.5", "chalk": "^5.0.0", "eslint": "^9.32.0", "globals": "^16.3.0", - "jest": "^30.0.5", "rollup": "^4.45.1", "rollup-plugin-license": "^3.6.0", "rollup-plugin-sourcemaps2": "^0.5.3", @@ -33,5832 +26,1024 @@ "typescript": "^5.8.3" } }, - "node_modules/@ampproject/remapping": { - "version": "2.3.0", - "resolved": "https://registry.npmjs.org/@ampproject/remapping/-/remapping-2.3.0.tgz", - "integrity": "sha512-30iZtAPgz+LTIYoeivqYo853f02jBYSd5uGnGpkFV0M3xOt9aN73erkgYAmZU43x4VfqcnLxW9Kpg3R5LC4YYw==", - "dev": true, - "license": "Apache-2.0", - "dependencies": { - "@jridgewell/gen-mapping": "^0.3.5", - "@jridgewell/trace-mapping": "^0.3.24" - }, - "engines": { - "node": ">=6.0.0" - } - }, - "node_modules/@babel/code-frame": { - "version": "7.27.1", - "resolved": "https://registry.npmjs.org/@babel/code-frame/-/code-frame-7.27.1.tgz", - "integrity": "sha512-cjQ7ZlQ0Mv3b47hABuTevyTuYN4i+loJKGeV9flcCgIK37cCXRh+L1bd3iBHlynerhQ7BhCkn2BPbQUL+rGqFg==", + "node_modules/@eslint-community/eslint-utils": { + "version": "4.9.0", + "resolved": "https://registry.npmjs.org/@eslint-community/eslint-utils/-/eslint-utils-4.9.0.tgz", + "integrity": "sha512-ayVFHdtZ+hsq1t2Dy24wCmGXGe4q9Gu3smhLYALJrr473ZH27MsnSL+LKUlimp4BWJqMDMLmPpx/Q9R3OAlL4g==", "dev": true, "license": "MIT", "dependencies": { - "@babel/helper-validator-identifier": "^7.27.1", - "js-tokens": "^4.0.0", - "picocolors": "^1.1.1" + "eslint-visitor-keys": "^3.4.3" }, "engines": { - "node": ">=6.9.0" + "node": "^12.22.0 || ^14.17.0 || >=16.0.0" + }, + "funding": { + "url": "https://opencollective.com/eslint" + }, + "peerDependencies": { + "eslint": "^6.0.0 || ^7.0.0 || >=8.0.0" } }, - "node_modules/@babel/compat-data": { - "version": "7.28.0", - "resolved": "https://registry.npmjs.org/@babel/compat-data/-/compat-data-7.28.0.tgz", - "integrity": "sha512-60X7qkglvrap8mn1lh2ebxXdZYtUcpd7gsmy9kLaBJ4i/WdY8PqTSdxyA8qraikqKQK5C1KRBKXqznrVapyNaw==", + "node_modules/@eslint-community/regexpp": { + "version": "4.12.2", + "resolved": "https://registry.npmjs.org/@eslint-community/regexpp/-/regexpp-4.12.2.tgz", + "integrity": "sha512-EriSTlt5OC9/7SXkRSCAhfSxxoSUgBm33OH+IkwbdpgoqsSsUg7y3uh+IICI/Qg4BBWr3U2i39RpmycbxMq4ew==", "dev": true, "license": "MIT", "engines": { - "node": ">=6.9.0" + "node": "^12.0.0 || ^14.0.0 || >=16.0.0" } }, - "node_modules/@babel/core": { - "version": "7.28.0", - "resolved": "https://registry.npmjs.org/@babel/core/-/core-7.28.0.tgz", - "integrity": "sha512-UlLAnTPrFdNGoFtbSXwcGFQBtQZJCNjaN6hQNP3UPvuNXT1i82N26KL3dZeIpNalWywr9IuQuncaAfUaS1g6sQ==", + "node_modules/@eslint/config-array": { + "version": "0.21.1", + "resolved": "https://registry.npmjs.org/@eslint/config-array/-/config-array-0.21.1.tgz", + "integrity": "sha512-aw1gNayWpdI/jSYVgzN5pL0cfzU02GT3NBpeT/DXbx1/1x7ZKxFPd9bwrzygx/qiwIQiJ1sw/zD8qY/kRvlGHA==", "dev": true, - "license": "MIT", + "license": "Apache-2.0", "dependencies": { - "@ampproject/remapping": "^2.2.0", - "@babel/code-frame": "^7.27.1", - "@babel/generator": "^7.28.0", - "@babel/helper-compilation-targets": "^7.27.2", - "@babel/helper-module-transforms": "^7.27.3", - "@babel/helpers": "^7.27.6", - "@babel/parser": "^7.28.0", - "@babel/template": "^7.27.2", - "@babel/traverse": "^7.28.0", - "@babel/types": "^7.28.0", - "convert-source-map": "^2.0.0", - "debug": "^4.1.0", - "gensync": "^1.0.0-beta.2", - "json5": "^2.2.3", - "semver": "^6.3.1" + "@eslint/object-schema": "^2.1.7", + "debug": "^4.3.1", + "minimatch": "^3.1.2" }, "engines": { - "node": ">=6.9.0" - }, - "funding": { - "type": "opencollective", - "url": "https://opencollective.com/babel" + "node": "^18.18.0 || ^20.9.0 || >=21.1.0" } }, - "node_modules/@babel/generator": { - "version": "7.28.0", - "resolved": "https://registry.npmjs.org/@babel/generator/-/generator-7.28.0.tgz", - "integrity": "sha512-lJjzvrbEeWrhB4P3QBsH7tey117PjLZnDbLiQEKjQ/fNJTjuq4HSqgFA+UNSwZT8D7dxxbnuSBMsa1lrWzKlQg==", + "node_modules/@eslint/config-array/node_modules/brace-expansion": { + "version": "1.1.12", + "resolved": "https://registry.npmjs.org/brace-expansion/-/brace-expansion-1.1.12.tgz", + "integrity": "sha512-9T9UjW3r0UW5c1Q7GTwllptXwhvYmEzFhzMfZ9H7FQWt+uZePjZPjBP/W1ZEyZ1twGWom5/56TF4lPcqjnDHcg==", "dev": true, "license": "MIT", "dependencies": { - "@babel/parser": "^7.28.0", - "@babel/types": "^7.28.0", - "@jridgewell/gen-mapping": "^0.3.12", - "@jridgewell/trace-mapping": "^0.3.28", - "jsesc": "^3.0.2" - }, - "engines": { - "node": ">=6.9.0" + "balanced-match": "^1.0.0", + "concat-map": "0.0.1" } }, - "node_modules/@babel/helper-annotate-as-pure": { - "version": "7.27.3", - "resolved": "https://registry.npmjs.org/@babel/helper-annotate-as-pure/-/helper-annotate-as-pure-7.27.3.tgz", - "integrity": "sha512-fXSwMQqitTGeHLBC08Eq5yXz2m37E4pJX1qAU1+2cNedz/ifv/bVXft90VeSav5nFO61EcNgwr0aJxbyPaWBPg==", + "node_modules/@eslint/config-array/node_modules/minimatch": { + "version": "3.1.2", + "resolved": "https://registry.npmjs.org/minimatch/-/minimatch-3.1.2.tgz", + "integrity": "sha512-J7p63hRiAjw1NDEww1W7i37+ByIrOWO5XQQAzZ3VOcL0PNybwpfmV/N05zFAzwQ9USyEcX6t3UO+K5aqBQOIHw==", "dev": true, - "license": "MIT", + "license": "ISC", "dependencies": { - "@babel/types": "^7.27.3" + "brace-expansion": "^1.1.7" }, "engines": { - "node": ">=6.9.0" + "node": "*" } }, - "node_modules/@babel/helper-compilation-targets": { - "version": "7.27.2", - "resolved": "https://registry.npmjs.org/@babel/helper-compilation-targets/-/helper-compilation-targets-7.27.2.tgz", - "integrity": "sha512-2+1thGUUWWjLTYTHZWK1n8Yga0ijBz1XAhUXcKy81rd5g6yh7hGqMp45v7cadSbEHc9G3OTv45SyneRN3ps4DQ==", + "node_modules/@eslint/config-helpers": { + "version": "0.4.2", + "resolved": "https://registry.npmjs.org/@eslint/config-helpers/-/config-helpers-0.4.2.tgz", + "integrity": "sha512-gBrxN88gOIf3R7ja5K9slwNayVcZgK6SOUORm2uBzTeIEfeVaIhOpCtTox3P6R7o2jLFwLFTLnC7kU/RGcYEgw==", "dev": true, - "license": "MIT", + "license": "Apache-2.0", "dependencies": { - "@babel/compat-data": "^7.27.2", - "@babel/helper-validator-option": "^7.27.1", - "browserslist": "^4.24.0", - "lru-cache": "^5.1.1", - "semver": "^6.3.1" + "@eslint/core": "^0.17.0" }, "engines": { - "node": ">=6.9.0" + "node": "^18.18.0 || ^20.9.0 || >=21.1.0" } }, - "node_modules/@babel/helper-create-class-features-plugin": { - "version": "7.27.1", - "resolved": "https://registry.npmjs.org/@babel/helper-create-class-features-plugin/-/helper-create-class-features-plugin-7.27.1.tgz", - "integrity": "sha512-QwGAmuvM17btKU5VqXfb+Giw4JcN0hjuufz3DYnpeVDvZLAObloM77bhMXiqry3Iio+Ai4phVRDwl6WU10+r5A==", + "node_modules/@eslint/core": { + "version": "0.17.0", + "resolved": "https://registry.npmjs.org/@eslint/core/-/core-0.17.0.tgz", + "integrity": "sha512-yL/sLrpmtDaFEiUj1osRP4TI2MDz1AddJL+jZ7KSqvBuliN4xqYY54IfdN8qD8Toa6g1iloph1fxQNkjOxrrpQ==", "dev": true, - "license": "MIT", + "license": "Apache-2.0", "dependencies": { - "@babel/helper-annotate-as-pure": "^7.27.1", - "@babel/helper-member-expression-to-functions": "^7.27.1", - "@babel/helper-optimise-call-expression": "^7.27.1", - "@babel/helper-replace-supers": "^7.27.1", - "@babel/helper-skip-transparent-expression-wrappers": "^7.27.1", - "@babel/traverse": "^7.27.1", - "semver": "^6.3.1" + "@types/json-schema": "^7.0.15" }, "engines": { - "node": ">=6.9.0" - }, - "peerDependencies": { - "@babel/core": "^7.0.0" + "node": "^18.18.0 || ^20.9.0 || >=21.1.0" } }, - "node_modules/@babel/helper-create-regexp-features-plugin": { - "version": "7.27.1", - "resolved": "https://registry.npmjs.org/@babel/helper-create-regexp-features-plugin/-/helper-create-regexp-features-plugin-7.27.1.tgz", - "integrity": "sha512-uVDC72XVf8UbrH5qQTc18Agb8emwjTiZrQE11Nv3CuBEZmVvTwwE9CBUEvHku06gQCAyYf8Nv6ja1IN+6LMbxQ==", + "node_modules/@eslint/eslintrc": { + "version": "3.3.1", + "resolved": "https://registry.npmjs.org/@eslint/eslintrc/-/eslintrc-3.3.1.tgz", + "integrity": "sha512-gtF186CXhIl1p4pJNGZw8Yc6RlshoePRvE0X91oPGb3vZ8pM3qOS9W9NGPat9LziaBV7XrJWGylNQXkGcnM3IQ==", "dev": true, "license": "MIT", "dependencies": { - "@babel/helper-annotate-as-pure": "^7.27.1", - "regexpu-core": "^6.2.0", - "semver": "^6.3.1" + "ajv": "^6.12.4", + "debug": "^4.3.2", + "espree": "^10.0.1", + "globals": "^14.0.0", + "ignore": "^5.2.0", + "import-fresh": "^3.2.1", + "js-yaml": "^4.1.0", + "minimatch": "^3.1.2", + "strip-json-comments": "^3.1.1" }, "engines": { - "node": ">=6.9.0" + "node": "^18.18.0 || ^20.9.0 || >=21.1.0" }, - "peerDependencies": { - "@babel/core": "^7.0.0" + "funding": { + "url": "https://opencollective.com/eslint" } }, - "node_modules/@babel/helper-define-polyfill-provider": { - "version": "0.6.5", - "resolved": "https://registry.npmjs.org/@babel/helper-define-polyfill-provider/-/helper-define-polyfill-provider-0.6.5.tgz", - "integrity": "sha512-uJnGFcPsWQK8fvjgGP5LZUZZsYGIoPeRjSF5PGwrelYgq7Q15/Ft9NGFp1zglwgIv//W0uG4BevRuSJRyylZPg==", + "node_modules/@eslint/eslintrc/node_modules/brace-expansion": { + "version": "1.1.12", + "resolved": "https://registry.npmjs.org/brace-expansion/-/brace-expansion-1.1.12.tgz", + "integrity": "sha512-9T9UjW3r0UW5c1Q7GTwllptXwhvYmEzFhzMfZ9H7FQWt+uZePjZPjBP/W1ZEyZ1twGWom5/56TF4lPcqjnDHcg==", "dev": true, "license": "MIT", "dependencies": { - "@babel/helper-compilation-targets": "^7.27.2", - "@babel/helper-plugin-utils": "^7.27.1", - "debug": "^4.4.1", - "lodash.debounce": "^4.0.8", - "resolve": "^1.22.10" - }, - "peerDependencies": { - "@babel/core": "^7.4.0 || ^8.0.0-0 <8.0.0" + "balanced-match": "^1.0.0", + "concat-map": "0.0.1" } }, - "node_modules/@babel/helper-globals": { - "version": "7.28.0", - "resolved": "https://registry.npmjs.org/@babel/helper-globals/-/helper-globals-7.28.0.tgz", - "integrity": "sha512-+W6cISkXFa1jXsDEdYA8HeevQT/FULhxzR99pxphltZcVaugps53THCeiWA8SguxxpSp3gKPiuYfSWopkLQ4hw==", + "node_modules/@eslint/eslintrc/node_modules/globals": { + "version": "14.0.0", + "resolved": "https://registry.npmjs.org/globals/-/globals-14.0.0.tgz", + "integrity": "sha512-oahGvuMGQlPw/ivIYBjVSrWAfWLBeku5tpPE2fOPLi+WHffIWbuh2tCjhyQhTBPMf5E9jDEH4FOmTYgYwbKwtQ==", "dev": true, "license": "MIT", "engines": { - "node": ">=6.9.0" + "node": ">=18" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" } }, - "node_modules/@babel/helper-member-expression-to-functions": { - "version": "7.27.1", - "resolved": "https://registry.npmjs.org/@babel/helper-member-expression-to-functions/-/helper-member-expression-to-functions-7.27.1.tgz", - "integrity": "sha512-E5chM8eWjTp/aNoVpcbfM7mLxu9XGLWYise2eBKGQomAk/Mb4XoxyqXTZbuTohbsl8EKqdlMhnDI2CCLfcs9wA==", + "node_modules/@eslint/eslintrc/node_modules/ignore": { + "version": "5.3.2", + "resolved": "https://registry.npmjs.org/ignore/-/ignore-5.3.2.tgz", + "integrity": "sha512-hsBTNUqQTDwkWtcdYI2i06Y/nUBEsNEDJKjWdigLvegy8kDuJAS8uRlpkkcQpyEXL0Z/pjDy5HBmMjRCJ2gq+g==", "dev": true, "license": "MIT", - "dependencies": { - "@babel/traverse": "^7.27.1", - "@babel/types": "^7.27.1" - }, "engines": { - "node": ">=6.9.0" + "node": ">= 4" } }, - "node_modules/@babel/helper-module-imports": { - "version": "7.27.1", - "resolved": "https://registry.npmjs.org/@babel/helper-module-imports/-/helper-module-imports-7.27.1.tgz", - "integrity": "sha512-0gSFWUPNXNopqtIPQvlD5WgXYI5GY2kP2cCvoT8kczjbfcfuIljTbcWrulD1CIPIX2gt1wghbDy08yE1p+/r3w==", + "node_modules/@eslint/eslintrc/node_modules/minimatch": { + "version": "3.1.2", + "resolved": "https://registry.npmjs.org/minimatch/-/minimatch-3.1.2.tgz", + "integrity": "sha512-J7p63hRiAjw1NDEww1W7i37+ByIrOWO5XQQAzZ3VOcL0PNybwpfmV/N05zFAzwQ9USyEcX6t3UO+K5aqBQOIHw==", "dev": true, - "license": "MIT", + "license": "ISC", "dependencies": { - "@babel/traverse": "^7.27.1", - "@babel/types": "^7.27.1" + "brace-expansion": "^1.1.7" }, "engines": { - "node": ">=6.9.0" + "node": "*" } }, - "node_modules/@babel/helper-module-transforms": { - "version": "7.27.3", - "resolved": "https://registry.npmjs.org/@babel/helper-module-transforms/-/helper-module-transforms-7.27.3.tgz", - "integrity": "sha512-dSOvYwvyLsWBeIRyOeHXp5vPj5l1I011r52FM1+r1jCERv+aFXYk4whgQccYEGYxK2H3ZAIA8nuPkQ0HaUo3qg==", + "node_modules/@eslint/js": { + "version": "9.39.1", + "resolved": "https://registry.npmjs.org/@eslint/js/-/js-9.39.1.tgz", + "integrity": "sha512-S26Stp4zCy88tH94QbBv3XCuzRQiZ9yXofEILmglYTh/Ug/a9/umqvgFtYBAo3Lp0nsI/5/qH1CCrbdK3AP1Tw==", "dev": true, "license": "MIT", - "dependencies": { - "@babel/helper-module-imports": "^7.27.1", - "@babel/helper-validator-identifier": "^7.27.1", - "@babel/traverse": "^7.27.3" - }, "engines": { - "node": ">=6.9.0" + "node": "^18.18.0 || ^20.9.0 || >=21.1.0" }, - "peerDependencies": { - "@babel/core": "^7.0.0" + "funding": { + "url": "https://eslint.org/donate" } }, - "node_modules/@babel/helper-optimise-call-expression": { - "version": "7.27.1", - "resolved": "https://registry.npmjs.org/@babel/helper-optimise-call-expression/-/helper-optimise-call-expression-7.27.1.tgz", - "integrity": "sha512-URMGH08NzYFhubNSGJrpUEphGKQwMQYBySzat5cAByY1/YgIRkULnIy3tAMeszlL/so2HbeilYloUmSpd7GdVw==", + "node_modules/@eslint/object-schema": { + "version": "2.1.7", + "resolved": "https://registry.npmjs.org/@eslint/object-schema/-/object-schema-2.1.7.tgz", + "integrity": "sha512-VtAOaymWVfZcmZbp6E2mympDIHvyjXs/12LqWYjVw6qjrfF+VK+fyG33kChz3nnK+SU5/NeHOqrTEHS8sXO3OA==", "dev": true, - "license": "MIT", - "dependencies": { - "@babel/types": "^7.27.1" - }, + "license": "Apache-2.0", "engines": { - "node": ">=6.9.0" + "node": "^18.18.0 || ^20.9.0 || >=21.1.0" } }, - "node_modules/@babel/helper-plugin-utils": { - "version": "7.27.1", - "resolved": "https://registry.npmjs.org/@babel/helper-plugin-utils/-/helper-plugin-utils-7.27.1.tgz", - "integrity": "sha512-1gn1Up5YXka3YYAHGKpbideQ5Yjf1tDa9qYcgysz+cNCXukyLl6DjPXhD3VRwSb8c0J9tA4b2+rHEZtc6R0tlw==", + "node_modules/@eslint/plugin-kit": { + "version": "0.4.1", + "resolved": "https://registry.npmjs.org/@eslint/plugin-kit/-/plugin-kit-0.4.1.tgz", + "integrity": "sha512-43/qtrDUokr7LJqoF2c3+RInu/t4zfrpYdoSDfYyhg52rwLV6TnOvdG4fXm7IkSB3wErkcmJS9iEhjVtOSEjjA==", "dev": true, - "license": "MIT", + "license": "Apache-2.0", + "dependencies": { + "@eslint/core": "^0.17.0", + "levn": "^0.4.1" + }, "engines": { - "node": ">=6.9.0" + "node": "^18.18.0 || ^20.9.0 || >=21.1.0" } }, - "node_modules/@babel/helper-remap-async-to-generator": { - "version": "7.27.1", - "resolved": "https://registry.npmjs.org/@babel/helper-remap-async-to-generator/-/helper-remap-async-to-generator-7.27.1.tgz", - "integrity": "sha512-7fiA521aVw8lSPeI4ZOD3vRFkoqkJcS+z4hFo82bFSH/2tNd6eJ5qCVMS5OzDmZh/kaHQeBaeyxK6wljcPtveA==", + "node_modules/@humanfs/core": { + "version": "0.19.1", + "resolved": "https://registry.npmjs.org/@humanfs/core/-/core-0.19.1.tgz", + "integrity": "sha512-5DyQ4+1JEUzejeK1JGICcideyfUbGixgS9jNgex5nqkW+cY7WZhxBigmieN5Qnw9ZosSNVC9KQKyb+GUaGyKUA==", "dev": true, - "license": "MIT", - "dependencies": { - "@babel/helper-annotate-as-pure": "^7.27.1", - "@babel/helper-wrap-function": "^7.27.1", - "@babel/traverse": "^7.27.1" - }, + "license": "Apache-2.0", "engines": { - "node": ">=6.9.0" - }, - "peerDependencies": { - "@babel/core": "^7.0.0" + "node": ">=18.18.0" } }, - "node_modules/@babel/helper-replace-supers": { - "version": "7.27.1", - "resolved": "https://registry.npmjs.org/@babel/helper-replace-supers/-/helper-replace-supers-7.27.1.tgz", - "integrity": "sha512-7EHz6qDZc8RYS5ElPoShMheWvEgERonFCs7IAonWLLUTXW59DP14bCZt89/GKyreYn8g3S83m21FelHKbeDCKA==", + "node_modules/@humanfs/node": { + "version": "0.16.7", + "resolved": "https://registry.npmjs.org/@humanfs/node/-/node-0.16.7.tgz", + "integrity": "sha512-/zUx+yOsIrG4Y43Eh2peDeKCxlRt/gET6aHfaKpuq267qXdYDFViVHfMaLyygZOnl0kGWxFIgsBy8QFuTLUXEQ==", "dev": true, - "license": "MIT", + "license": "Apache-2.0", "dependencies": { - "@babel/helper-member-expression-to-functions": "^7.27.1", - "@babel/helper-optimise-call-expression": "^7.27.1", - "@babel/traverse": "^7.27.1" + "@humanfs/core": "^0.19.1", + "@humanwhocodes/retry": "^0.4.0" }, "engines": { - "node": ">=6.9.0" - }, - "peerDependencies": { - "@babel/core": "^7.0.0" + "node": ">=18.18.0" } }, - "node_modules/@babel/helper-skip-transparent-expression-wrappers": { - "version": "7.27.1", - "resolved": "https://registry.npmjs.org/@babel/helper-skip-transparent-expression-wrappers/-/helper-skip-transparent-expression-wrappers-7.27.1.tgz", - "integrity": "sha512-Tub4ZKEXqbPjXgWLl2+3JpQAYBJ8+ikpQ2Ocj/q/r0LwE3UhENh7EUabyHjz2kCEsrRY83ew2DQdHluuiDQFzg==", + "node_modules/@humanwhocodes/module-importer": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/@humanwhocodes/module-importer/-/module-importer-1.0.1.tgz", + "integrity": "sha512-bxveV4V8v5Yb4ncFTT3rPSgZBOpCkjfK0y4oVVVJwIuDVBRMDXrPyXRL988i5ap9m9bnyEEjWfm5WkBmtffLfA==", "dev": true, - "license": "MIT", - "dependencies": { - "@babel/traverse": "^7.27.1", - "@babel/types": "^7.27.1" - }, + "license": "Apache-2.0", "engines": { - "node": ">=6.9.0" + "node": ">=12.22" + }, + "funding": { + "type": "github", + "url": "https://github.com/sponsors/nzakas" } }, - "node_modules/@babel/helper-string-parser": { - "version": "7.27.1", - "resolved": "https://registry.npmjs.org/@babel/helper-string-parser/-/helper-string-parser-7.27.1.tgz", - "integrity": "sha512-qMlSxKbpRlAridDExk92nSobyDdpPijUq2DW6oDnUqd0iOGxmQjyqhMIihI9+zv4LPyZdRje2cavWPbCbWm3eA==", + "node_modules/@humanwhocodes/retry": { + "version": "0.4.3", + "resolved": "https://registry.npmjs.org/@humanwhocodes/retry/-/retry-0.4.3.tgz", + "integrity": "sha512-bV0Tgo9K4hfPCek+aMAn81RppFKv2ySDQeMoSZuvTASywNTnVJCArCZE2FWqpvIatKu7VMRLWlR1EazvVhDyhQ==", "dev": true, - "license": "MIT", + "license": "Apache-2.0", "engines": { - "node": ">=6.9.0" + "node": ">=18.18" + }, + "funding": { + "type": "github", + "url": "https://github.com/sponsors/nzakas" } }, - "node_modules/@babel/helper-validator-identifier": { - "version": "7.27.1", - "resolved": "https://registry.npmjs.org/@babel/helper-validator-identifier/-/helper-validator-identifier-7.27.1.tgz", - "integrity": "sha512-D2hP9eA+Sqx1kBZgzxZh0y1trbuU+JoDkiEwqhQ36nodYqJwyEIhPSdMNd7lOm/4io72luTPWH20Yda0xOuUow==", + "node_modules/@jridgewell/gen-mapping": { + "version": "0.3.13", + "resolved": "https://registry.npmjs.org/@jridgewell/gen-mapping/-/gen-mapping-0.3.13.tgz", + "integrity": "sha512-2kkt/7niJ6MgEPxF0bYdQ6etZaA+fQvDcLKckhy1yIQOzaoKjBBjSj63/aLVjYE3qhRt5dvM+uUyfCg6UKCBbA==", "dev": true, "license": "MIT", - "engines": { - "node": ">=6.9.0" + "dependencies": { + "@jridgewell/sourcemap-codec": "^1.5.0", + "@jridgewell/trace-mapping": "^0.3.24" } }, - "node_modules/@babel/helper-validator-option": { - "version": "7.27.1", - "resolved": "https://registry.npmjs.org/@babel/helper-validator-option/-/helper-validator-option-7.27.1.tgz", - "integrity": "sha512-YvjJow9FxbhFFKDSuFnVCe2WxXk1zWc22fFePVNEaWJEu8IrZVlda6N0uHwzZrUM1il7NC9Mlp4MaJYbYd9JSg==", + "node_modules/@jridgewell/resolve-uri": { + "version": "3.1.2", + "resolved": "https://registry.npmjs.org/@jridgewell/resolve-uri/-/resolve-uri-3.1.2.tgz", + "integrity": "sha512-bRISgCIjP20/tbWSPWMEi54QVPRZExkuD9lJL+UIxUKtwVJA8wW1Trb1jMs1RFXo1CBTNZ/5hpC9QvmKWdopKw==", "dev": true, "license": "MIT", "engines": { - "node": ">=6.9.0" + "node": ">=6.0.0" } }, - "node_modules/@babel/helper-wrap-function": { - "version": "7.27.1", - "resolved": "https://registry.npmjs.org/@babel/helper-wrap-function/-/helper-wrap-function-7.27.1.tgz", - "integrity": "sha512-NFJK2sHUvrjo8wAU/nQTWU890/zB2jj0qBcCbZbbf+005cAsv6tMjXz31fBign6M5ov1o0Bllu+9nbqkfsjjJQ==", + "node_modules/@jridgewell/source-map": { + "version": "0.3.11", + "resolved": "https://registry.npmjs.org/@jridgewell/source-map/-/source-map-0.3.11.tgz", + "integrity": "sha512-ZMp1V8ZFcPG5dIWnQLr3NSI1MiCU7UETdS/A0G8V/XWHvJv3ZsFqutJn1Y5RPmAPX6F3BiE397OqveU/9NCuIA==", "dev": true, "license": "MIT", "dependencies": { - "@babel/template": "^7.27.1", - "@babel/traverse": "^7.27.1", - "@babel/types": "^7.27.1" - }, - "engines": { - "node": ">=6.9.0" + "@jridgewell/gen-mapping": "^0.3.5", + "@jridgewell/trace-mapping": "^0.3.25" } }, - "node_modules/@babel/helpers": { - "version": "7.28.2", - "resolved": "https://registry.npmjs.org/@babel/helpers/-/helpers-7.28.2.tgz", - "integrity": "sha512-/V9771t+EgXz62aCcyofnQhGM8DQACbRhvzKFsXKC9QM+5MadF8ZmIm0crDMaz3+o0h0zXfJnd4EhbYbxsrcFw==", + "node_modules/@jridgewell/sourcemap-codec": { + "version": "1.5.5", + "resolved": "https://registry.npmjs.org/@jridgewell/sourcemap-codec/-/sourcemap-codec-1.5.5.tgz", + "integrity": "sha512-cYQ9310grqxueWbl+WuIUIaiUaDcj7WOq5fVhEljNVgRfOUhY9fy2zTvfoqWsnebh8Sl70VScFbICvJnLKB0Og==", + "dev": true, + "license": "MIT" + }, + "node_modules/@jridgewell/trace-mapping": { + "version": "0.3.31", + "resolved": "https://registry.npmjs.org/@jridgewell/trace-mapping/-/trace-mapping-0.3.31.tgz", + "integrity": "sha512-zzNR+SdQSDJzc8joaeP8QQoCQr8NuYx2dIIytl1QeBEZHJ9uW6hebsrYgbz8hJwUQao3TWCMtmfV8Nu1twOLAw==", "dev": true, "license": "MIT", "dependencies": { - "@babel/template": "^7.27.2", - "@babel/types": "^7.28.2" - }, - "engines": { - "node": ">=6.9.0" + "@jridgewell/resolve-uri": "^3.1.0", + "@jridgewell/sourcemap-codec": "^1.4.14" } }, - "node_modules/@babel/parser": { - "version": "7.28.0", - "resolved": "https://registry.npmjs.org/@babel/parser/-/parser-7.28.0.tgz", - "integrity": "sha512-jVZGvOxOuNSsuQuLRTh13nU0AogFlw32w/MT+LV6D3sP5WdbW61E77RnkbaO2dUvmPAYrBDJXGn5gGS6tH4j8g==", + "node_modules/@nodelib/fs.scandir": { + "version": "2.1.5", + "resolved": "https://registry.npmjs.org/@nodelib/fs.scandir/-/fs.scandir-2.1.5.tgz", + "integrity": "sha512-vq24Bq3ym5HEQm2NKCr3yXDwjc7vTsEThRDnkp2DK9p1uqLR+DHurm/NOTo0KG7HYHU7eppKZj3MyqYuMBf62g==", "dev": true, "license": "MIT", "dependencies": { - "@babel/types": "^7.28.0" - }, - "bin": { - "parser": "bin/babel-parser.js" + "@nodelib/fs.stat": "2.0.5", + "run-parallel": "^1.1.9" }, "engines": { - "node": ">=6.0.0" + "node": ">= 8" } }, - "node_modules/@babel/plugin-bugfix-firefox-class-in-computed-class-key": { - "version": "7.27.1", - "resolved": "https://registry.npmjs.org/@babel/plugin-bugfix-firefox-class-in-computed-class-key/-/plugin-bugfix-firefox-class-in-computed-class-key-7.27.1.tgz", - "integrity": "sha512-QPG3C9cCVRQLxAVwmefEmwdTanECuUBMQZ/ym5kiw3XKCGA7qkuQLcjWWHcrD/GKbn/WmJwaezfuuAOcyKlRPA==", + "node_modules/@nodelib/fs.stat": { + "version": "2.0.5", + "resolved": "https://registry.npmjs.org/@nodelib/fs.stat/-/fs.stat-2.0.5.tgz", + "integrity": "sha512-RkhPPp2zrqDAQA/2jNhnztcPAlv64XdhIp7a7454A5ovI7Bukxgt7MX7udwAu3zg1DcpPU0rz3VV1SeaqvY4+A==", "dev": true, "license": "MIT", - "dependencies": { - "@babel/helper-plugin-utils": "^7.27.1", - "@babel/traverse": "^7.27.1" - }, "engines": { - "node": ">=6.9.0" - }, - "peerDependencies": { - "@babel/core": "^7.0.0" + "node": ">= 8" } }, - "node_modules/@babel/plugin-bugfix-safari-class-field-initializer-scope": { - "version": "7.27.1", - "resolved": "https://registry.npmjs.org/@babel/plugin-bugfix-safari-class-field-initializer-scope/-/plugin-bugfix-safari-class-field-initializer-scope-7.27.1.tgz", - "integrity": "sha512-qNeq3bCKnGgLkEXUuFry6dPlGfCdQNZbn7yUAPCInwAJHMU7THJfrBSozkcWq5sNM6RcF3S8XyQL2A52KNR9IA==", + "node_modules/@nodelib/fs.walk": { + "version": "1.2.8", + "resolved": "https://registry.npmjs.org/@nodelib/fs.walk/-/fs.walk-1.2.8.tgz", + "integrity": "sha512-oGB+UxlgWcgQkgwo8GcEGwemoTFt3FIO9ababBmaGwXIoBKZ+GTy0pP185beGg7Llih/NSHSV2XAs1lnznocSg==", "dev": true, "license": "MIT", "dependencies": { - "@babel/helper-plugin-utils": "^7.27.1" + "@nodelib/fs.scandir": "2.1.5", + "fastq": "^1.6.0" }, "engines": { - "node": ">=6.9.0" - }, - "peerDependencies": { - "@babel/core": "^7.0.0" + "node": ">= 8" } }, - "node_modules/@babel/plugin-bugfix-safari-id-destructuring-collision-in-function-expression": { - "version": "7.27.1", - "resolved": "https://registry.npmjs.org/@babel/plugin-bugfix-safari-id-destructuring-collision-in-function-expression/-/plugin-bugfix-safari-id-destructuring-collision-in-function-expression-7.27.1.tgz", - "integrity": "sha512-g4L7OYun04N1WyqMNjldFwlfPCLVkgB54A/YCXICZYBsvJJE3kByKv9c9+R/nAfmIfjl2rKYLNyMHboYbZaWaA==", + "node_modules/@rollup/plugin-node-resolve": { + "version": "16.0.3", + "resolved": "https://registry.npmjs.org/@rollup/plugin-node-resolve/-/plugin-node-resolve-16.0.3.tgz", + "integrity": "sha512-lUYM3UBGuM93CnMPG1YocWu7X802BrNF3jW2zny5gQyLQgRFJhV1Sq0Zi74+dh/6NBx1DxFC4b4GXg9wUCG5Qg==", "dev": true, "license": "MIT", "dependencies": { - "@babel/helper-plugin-utils": "^7.27.1" + "@rollup/pluginutils": "^5.0.1", + "@types/resolve": "1.20.2", + "deepmerge": "^4.2.2", + "is-module": "^1.0.0", + "resolve": "^1.22.1" }, "engines": { - "node": ">=6.9.0" + "node": ">=14.0.0" }, "peerDependencies": { - "@babel/core": "^7.0.0" + "rollup": "^2.78.0||^3.0.0||^4.0.0" + }, + "peerDependenciesMeta": { + "rollup": { + "optional": true + } } }, - "node_modules/@babel/plugin-bugfix-v8-spread-parameters-in-optional-chaining": { - "version": "7.27.1", - "resolved": "https://registry.npmjs.org/@babel/plugin-bugfix-v8-spread-parameters-in-optional-chaining/-/plugin-bugfix-v8-spread-parameters-in-optional-chaining-7.27.1.tgz", - "integrity": "sha512-oO02gcONcD5O1iTLi/6frMJBIwWEHceWGSGqrpCmEL8nogiS6J9PBlE48CaK20/Jx1LuRml9aDftLgdjXT8+Cw==", + "node_modules/@rollup/plugin-terser": { + "version": "0.4.4", + "resolved": "https://registry.npmjs.org/@rollup/plugin-terser/-/plugin-terser-0.4.4.tgz", + "integrity": "sha512-XHeJC5Bgvs8LfukDwWZp7yeqin6ns8RTl2B9avbejt6tZqsqvVoWI7ZTQrcNsfKEDWBTnTxM8nMDkO2IFFbd0A==", "dev": true, "license": "MIT", "dependencies": { - "@babel/helper-plugin-utils": "^7.27.1", - "@babel/helper-skip-transparent-expression-wrappers": "^7.27.1", - "@babel/plugin-transform-optional-chaining": "^7.27.1" + "serialize-javascript": "^6.0.1", + "smob": "^1.0.0", + "terser": "^5.17.4" }, "engines": { - "node": ">=6.9.0" + "node": ">=14.0.0" }, "peerDependencies": { - "@babel/core": "^7.13.0" + "rollup": "^2.0.0||^3.0.0||^4.0.0" + }, + "peerDependenciesMeta": { + "rollup": { + "optional": true + } } }, - "node_modules/@babel/plugin-bugfix-v8-static-class-fields-redefine-readonly": { - "version": "7.27.1", - "resolved": "https://registry.npmjs.org/@babel/plugin-bugfix-v8-static-class-fields-redefine-readonly/-/plugin-bugfix-v8-static-class-fields-redefine-readonly-7.27.1.tgz", - "integrity": "sha512-6BpaYGDavZqkI6yT+KSPdpZFfpnd68UKXbcjI9pJ13pvHhPrCKWOOLp+ysvMeA+DxnhuPpgIaRpxRxo5A9t5jw==", + "node_modules/@rollup/pluginutils": { + "version": "5.3.0", + "resolved": "https://registry.npmjs.org/@rollup/pluginutils/-/pluginutils-5.3.0.tgz", + "integrity": "sha512-5EdhGZtnu3V88ces7s53hhfK5KSASnJZv8Lulpc04cWO3REESroJXg73DFsOmgbU2BhwV0E20bu2IDZb3VKW4Q==", "dev": true, "license": "MIT", "dependencies": { - "@babel/helper-plugin-utils": "^7.27.1", - "@babel/traverse": "^7.27.1" + "@types/estree": "^1.0.0", + "estree-walker": "^2.0.2", + "picomatch": "^4.0.2" }, "engines": { - "node": ">=6.9.0" + "node": ">=14.0.0" }, "peerDependencies": { - "@babel/core": "^7.0.0" - } - }, - "node_modules/@babel/plugin-proposal-private-property-in-object": { - "version": "7.21.0-placeholder-for-preset-env.2", - "resolved": "https://registry.npmjs.org/@babel/plugin-proposal-private-property-in-object/-/plugin-proposal-private-property-in-object-7.21.0-placeholder-for-preset-env.2.tgz", - "integrity": "sha512-SOSkfJDddaM7mak6cPEpswyTRnuRltl429hMraQEglW+OkovnCzsiszTmsrlY//qLFjCpQDFRvjdm2wA5pPm9w==", - "dev": true, - "license": "MIT", - "engines": { - "node": ">=6.9.0" + "rollup": "^1.20.0||^2.0.0||^3.0.0||^4.0.0" }, - "peerDependencies": { - "@babel/core": "^7.0.0-0" + "peerDependenciesMeta": { + "rollup": { + "optional": true + } } }, - "node_modules/@babel/plugin-syntax-async-generators": { - "version": "7.8.4", - "resolved": "https://registry.npmjs.org/@babel/plugin-syntax-async-generators/-/plugin-syntax-async-generators-7.8.4.tgz", - "integrity": "sha512-tycmZxkGfZaxhMRbXlPXuVFpdWlXpir2W4AMhSJgRKzk/eDlIXOhb2LHWoLpDF7TEHylV5zNhykX6KAgHJmTNw==", + "node_modules/@rollup/rollup-android-arm-eabi": { + "version": "4.53.2", + "resolved": "https://registry.npmjs.org/@rollup/rollup-android-arm-eabi/-/rollup-android-arm-eabi-4.53.2.tgz", + "integrity": "sha512-yDPzwsgiFO26RJA4nZo8I+xqzh7sJTZIWQOxn+/XOdPE31lAvLIYCKqjV+lNH/vxE2L2iH3plKxDCRK6i+CwhA==", + "cpu": [ + "arm" + ], "dev": true, "license": "MIT", - "dependencies": { - "@babel/helper-plugin-utils": "^7.8.0" - }, - "peerDependencies": { - "@babel/core": "^7.0.0-0" - } + "optional": true, + "os": [ + "android" + ] }, - "node_modules/@babel/plugin-syntax-bigint": { - "version": "7.8.3", - "resolved": "https://registry.npmjs.org/@babel/plugin-syntax-bigint/-/plugin-syntax-bigint-7.8.3.tgz", - "integrity": "sha512-wnTnFlG+YxQm3vDxpGE57Pj0srRU4sHE/mDkt1qv2YJJSeUAec2ma4WLUnUPeKjyrfntVwe/N6dCXpU+zL3Npg==", + "node_modules/@rollup/rollup-android-arm64": { + "version": "4.53.2", + "resolved": "https://registry.npmjs.org/@rollup/rollup-android-arm64/-/rollup-android-arm64-4.53.2.tgz", + "integrity": "sha512-k8FontTxIE7b0/OGKeSN5B6j25EuppBcWM33Z19JoVT7UTXFSo3D9CdU39wGTeb29NO3XxpMNauh09B+Ibw+9g==", + "cpu": [ + "arm64" + ], "dev": true, "license": "MIT", - "dependencies": { - "@babel/helper-plugin-utils": "^7.8.0" - }, - "peerDependencies": { - "@babel/core": "^7.0.0-0" - } + "optional": true, + "os": [ + "android" + ] }, - "node_modules/@babel/plugin-syntax-class-properties": { - "version": "7.12.13", - "resolved": "https://registry.npmjs.org/@babel/plugin-syntax-class-properties/-/plugin-syntax-class-properties-7.12.13.tgz", - "integrity": "sha512-fm4idjKla0YahUNgFNLCB0qySdsoPiZP3iQE3rky0mBUtMZ23yDJ9SJdg6dXTSDnulOVqiF3Hgr9nbXvXTQZYA==", + "node_modules/@rollup/rollup-darwin-arm64": { + "version": "4.53.2", + "resolved": "https://registry.npmjs.org/@rollup/rollup-darwin-arm64/-/rollup-darwin-arm64-4.53.2.tgz", + "integrity": "sha512-A6s4gJpomNBtJ2yioj8bflM2oogDwzUiMl2yNJ2v9E7++sHrSrsQ29fOfn5DM/iCzpWcebNYEdXpaK4tr2RhfQ==", + "cpu": [ + "arm64" + ], "dev": true, "license": "MIT", - "dependencies": { - "@babel/helper-plugin-utils": "^7.12.13" - }, - "peerDependencies": { - "@babel/core": "^7.0.0-0" - } + "optional": true, + "os": [ + "darwin" + ] }, - "node_modules/@babel/plugin-syntax-class-static-block": { - "version": "7.14.5", - "resolved": "https://registry.npmjs.org/@babel/plugin-syntax-class-static-block/-/plugin-syntax-class-static-block-7.14.5.tgz", - "integrity": "sha512-b+YyPmr6ldyNnM6sqYeMWE+bgJcJpO6yS4QD7ymxgH34GBPNDM/THBh8iunyvKIZztiwLH4CJZ0RxTk9emgpjw==", + "node_modules/@rollup/rollup-darwin-x64": { + "version": "4.53.2", + "resolved": "https://registry.npmjs.org/@rollup/rollup-darwin-x64/-/rollup-darwin-x64-4.53.2.tgz", + "integrity": "sha512-e6XqVmXlHrBlG56obu9gDRPW3O3hLxpwHpLsBJvuI8qqnsrtSZ9ERoWUXtPOkY8c78WghyPHZdmPhHLWNdAGEw==", + "cpu": [ + "x64" + ], "dev": true, "license": "MIT", - "dependencies": { - "@babel/helper-plugin-utils": "^7.14.5" - }, - "engines": { - "node": ">=6.9.0" - }, - "peerDependencies": { - "@babel/core": "^7.0.0-0" - } + "optional": true, + "os": [ + "darwin" + ] }, - "node_modules/@babel/plugin-syntax-import-assertions": { - "version": "7.27.1", - "resolved": "https://registry.npmjs.org/@babel/plugin-syntax-import-assertions/-/plugin-syntax-import-assertions-7.27.1.tgz", - "integrity": "sha512-UT/Jrhw57xg4ILHLFnzFpPDlMbcdEicaAtjPQpbj9wa8T4r5KVWCimHcL/460g8Ht0DMxDyjsLgiWSkVjnwPFg==", + "node_modules/@rollup/rollup-freebsd-arm64": { + "version": "4.53.2", + "resolved": "https://registry.npmjs.org/@rollup/rollup-freebsd-arm64/-/rollup-freebsd-arm64-4.53.2.tgz", + "integrity": "sha512-v0E9lJW8VsrwPux5Qe5CwmH/CF/2mQs6xU1MF3nmUxmZUCHazCjLgYvToOk+YuuUqLQBio1qkkREhxhc656ViA==", + "cpu": [ + "arm64" + ], "dev": true, "license": "MIT", - "dependencies": { - "@babel/helper-plugin-utils": "^7.27.1" - }, - "engines": { - "node": ">=6.9.0" - }, - "peerDependencies": { - "@babel/core": "^7.0.0-0" - } + "optional": true, + "os": [ + "freebsd" + ] }, - "node_modules/@babel/plugin-syntax-import-attributes": { - "version": "7.27.1", - "resolved": "https://registry.npmjs.org/@babel/plugin-syntax-import-attributes/-/plugin-syntax-import-attributes-7.27.1.tgz", - "integrity": "sha512-oFT0FrKHgF53f4vOsZGi2Hh3I35PfSmVs4IBFLFj4dnafP+hIWDLg3VyKmUHfLoLHlyxY4C7DGtmHuJgn+IGww==", + "node_modules/@rollup/rollup-freebsd-x64": { + "version": "4.53.2", + "resolved": "https://registry.npmjs.org/@rollup/rollup-freebsd-x64/-/rollup-freebsd-x64-4.53.2.tgz", + "integrity": "sha512-ClAmAPx3ZCHtp6ysl4XEhWU69GUB1D+s7G9YjHGhIGCSrsg00nEGRRZHmINYxkdoJehde8VIsDC5t9C0gb6yqA==", + "cpu": [ + "x64" + ], "dev": true, "license": "MIT", - "dependencies": { - "@babel/helper-plugin-utils": "^7.27.1" - }, - "engines": { - "node": ">=6.9.0" - }, - "peerDependencies": { - "@babel/core": "^7.0.0-0" - } + "optional": true, + "os": [ + "freebsd" + ] }, - "node_modules/@babel/plugin-syntax-import-meta": { - "version": "7.10.4", - "resolved": "https://registry.npmjs.org/@babel/plugin-syntax-import-meta/-/plugin-syntax-import-meta-7.10.4.tgz", - "integrity": "sha512-Yqfm+XDx0+Prh3VSeEQCPU81yC+JWZ2pDPFSS4ZdpfZhp4MkFMaDC1UqseovEKwSUpnIL7+vK+Clp7bfh0iD7g==", + "node_modules/@rollup/rollup-linux-arm-gnueabihf": { + "version": "4.53.2", + "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-arm-gnueabihf/-/rollup-linux-arm-gnueabihf-4.53.2.tgz", + "integrity": "sha512-EPlb95nUsz6Dd9Qy13fI5kUPXNSljaG9FiJ4YUGU1O/Q77i5DYFW5KR8g1OzTcdZUqQQ1KdDqsTohdFVwCwjqg==", + "cpu": [ + "arm" + ], "dev": true, "license": "MIT", - "dependencies": { - "@babel/helper-plugin-utils": "^7.10.4" - }, - "peerDependencies": { - "@babel/core": "^7.0.0-0" - } + "optional": true, + "os": [ + "linux" + ] }, - "node_modules/@babel/plugin-syntax-json-strings": { - "version": "7.8.3", - "resolved": "https://registry.npmjs.org/@babel/plugin-syntax-json-strings/-/plugin-syntax-json-strings-7.8.3.tgz", - "integrity": "sha512-lY6kdGpWHvjoe2vk4WrAapEuBR69EMxZl+RoGRhrFGNYVK8mOPAW8VfbT/ZgrFbXlDNiiaxQnAtgVCZ6jv30EA==", + "node_modules/@rollup/rollup-linux-arm-musleabihf": { + "version": "4.53.2", + "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-arm-musleabihf/-/rollup-linux-arm-musleabihf-4.53.2.tgz", + "integrity": "sha512-BOmnVW+khAUX+YZvNfa0tGTEMVVEerOxN0pDk2E6N6DsEIa2Ctj48FOMfNDdrwinocKaC7YXUZ1pHlKpnkja/Q==", + "cpu": [ + "arm" + ], "dev": true, "license": "MIT", - "dependencies": { - "@babel/helper-plugin-utils": "^7.8.0" - }, - "peerDependencies": { - "@babel/core": "^7.0.0-0" - } + "optional": true, + "os": [ + "linux" + ] }, - "node_modules/@babel/plugin-syntax-jsx": { - "version": "7.27.1", - "resolved": "https://registry.npmjs.org/@babel/plugin-syntax-jsx/-/plugin-syntax-jsx-7.27.1.tgz", - "integrity": "sha512-y8YTNIeKoyhGd9O0Jiyzyyqk8gdjnumGTQPsz0xOZOQ2RmkVJeZ1vmmfIvFEKqucBG6axJGBZDE/7iI5suUI/w==", + "node_modules/@rollup/rollup-linux-arm64-gnu": { + "version": "4.53.2", + "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-arm64-gnu/-/rollup-linux-arm64-gnu-4.53.2.tgz", + "integrity": "sha512-Xt2byDZ+6OVNuREgBXr4+CZDJtrVso5woFtpKdGPhpTPHcNG7D8YXeQzpNbFRxzTVqJf7kvPMCub/pcGUWgBjA==", + "cpu": [ + "arm64" + ], "dev": true, "license": "MIT", - "dependencies": { - "@babel/helper-plugin-utils": "^7.27.1" - }, - "engines": { - "node": ">=6.9.0" - }, - "peerDependencies": { - "@babel/core": "^7.0.0-0" - } + "optional": true, + "os": [ + "linux" + ] }, - "node_modules/@babel/plugin-syntax-logical-assignment-operators": { - "version": "7.10.4", - "resolved": "https://registry.npmjs.org/@babel/plugin-syntax-logical-assignment-operators/-/plugin-syntax-logical-assignment-operators-7.10.4.tgz", - "integrity": "sha512-d8waShlpFDinQ5MtvGU9xDAOzKH47+FFoney2baFIoMr952hKOLp1HR7VszoZvOsV/4+RRszNY7D17ba0te0ig==", + "node_modules/@rollup/rollup-linux-arm64-musl": { + "version": "4.53.2", + "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-arm64-musl/-/rollup-linux-arm64-musl-4.53.2.tgz", + "integrity": "sha512-+LdZSldy/I9N8+klim/Y1HsKbJ3BbInHav5qE9Iy77dtHC/pibw1SR/fXlWyAk0ThnpRKoODwnAuSjqxFRDHUQ==", + "cpu": [ + "arm64" + ], "dev": true, "license": "MIT", - "dependencies": { - "@babel/helper-plugin-utils": "^7.10.4" - }, - "peerDependencies": { - "@babel/core": "^7.0.0-0" - } + "optional": true, + "os": [ + "linux" + ] }, - "node_modules/@babel/plugin-syntax-nullish-coalescing-operator": { - "version": "7.8.3", - "resolved": "https://registry.npmjs.org/@babel/plugin-syntax-nullish-coalescing-operator/-/plugin-syntax-nullish-coalescing-operator-7.8.3.tgz", - "integrity": "sha512-aSff4zPII1u2QD7y+F8oDsz19ew4IGEJg9SVW+bqwpwtfFleiQDMdzA/R+UlWDzfnHFCxxleFT0PMIrR36XLNQ==", + "node_modules/@rollup/rollup-linux-loong64-gnu": { + "version": "4.53.2", + "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-loong64-gnu/-/rollup-linux-loong64-gnu-4.53.2.tgz", + "integrity": "sha512-8ms8sjmyc1jWJS6WdNSA23rEfdjWB30LH8Wqj0Cqvv7qSHnvw6kgMMXRdop6hkmGPlyYBdRPkjJnj3KCUHV/uQ==", + "cpu": [ + "loong64" + ], "dev": true, "license": "MIT", - "dependencies": { - "@babel/helper-plugin-utils": "^7.8.0" - }, - "peerDependencies": { - "@babel/core": "^7.0.0-0" - } + "optional": true, + "os": [ + "linux" + ] }, - "node_modules/@babel/plugin-syntax-numeric-separator": { - "version": "7.10.4", - "resolved": "https://registry.npmjs.org/@babel/plugin-syntax-numeric-separator/-/plugin-syntax-numeric-separator-7.10.4.tgz", - "integrity": "sha512-9H6YdfkcK/uOnY/K7/aA2xpzaAgkQn37yzWUMRK7OaPOqOpGS1+n0H5hxT9AUw9EsSjPW8SVyMJwYRtWs3X3ug==", + "node_modules/@rollup/rollup-linux-ppc64-gnu": { + "version": "4.53.2", + "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-ppc64-gnu/-/rollup-linux-ppc64-gnu-4.53.2.tgz", + "integrity": "sha512-3HRQLUQbpBDMmzoxPJYd3W6vrVHOo2cVW8RUo87Xz0JPJcBLBr5kZ1pGcQAhdZgX9VV7NbGNipah1omKKe23/g==", + "cpu": [ + "ppc64" + ], "dev": true, "license": "MIT", - "dependencies": { - "@babel/helper-plugin-utils": "^7.10.4" - }, - "peerDependencies": { - "@babel/core": "^7.0.0-0" - } + "optional": true, + "os": [ + "linux" + ] }, - "node_modules/@babel/plugin-syntax-object-rest-spread": { - "version": "7.8.3", - "resolved": "https://registry.npmjs.org/@babel/plugin-syntax-object-rest-spread/-/plugin-syntax-object-rest-spread-7.8.3.tgz", - "integrity": "sha512-XoqMijGZb9y3y2XskN+P1wUGiVwWZ5JmoDRwx5+3GmEplNyVM2s2Dg8ILFQm8rWM48orGy5YpI5Bl8U1y7ydlA==", + "node_modules/@rollup/rollup-linux-riscv64-gnu": { + "version": "4.53.2", + "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-riscv64-gnu/-/rollup-linux-riscv64-gnu-4.53.2.tgz", + "integrity": "sha512-fMjKi+ojnmIvhk34gZP94vjogXNNUKMEYs+EDaB/5TG/wUkoeua7p7VCHnE6T2Tx+iaghAqQX8teQzcvrYpaQA==", + "cpu": [ + "riscv64" + ], "dev": true, "license": "MIT", - "dependencies": { - "@babel/helper-plugin-utils": "^7.8.0" - }, - "peerDependencies": { - "@babel/core": "^7.0.0-0" - } + "optional": true, + "os": [ + "linux" + ] }, - "node_modules/@babel/plugin-syntax-optional-catch-binding": { - "version": "7.8.3", - "resolved": "https://registry.npmjs.org/@babel/plugin-syntax-optional-catch-binding/-/plugin-syntax-optional-catch-binding-7.8.3.tgz", - "integrity": "sha512-6VPD0Pc1lpTqw0aKoeRTMiB+kWhAoT24PA+ksWSBrFtl5SIRVpZlwN3NNPQjehA2E/91FV3RjLWoVTglWcSV3Q==", + "node_modules/@rollup/rollup-linux-riscv64-musl": { + "version": "4.53.2", + "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-riscv64-musl/-/rollup-linux-riscv64-musl-4.53.2.tgz", + "integrity": "sha512-XuGFGU+VwUUV5kLvoAdi0Wz5Xbh2SrjIxCtZj6Wq8MDp4bflb/+ThZsVxokM7n0pcbkEr2h5/pzqzDYI7cCgLQ==", + "cpu": [ + "riscv64" + ], "dev": true, "license": "MIT", - "dependencies": { - "@babel/helper-plugin-utils": "^7.8.0" - }, - "peerDependencies": { - "@babel/core": "^7.0.0-0" - } + "optional": true, + "os": [ + "linux" + ] }, - "node_modules/@babel/plugin-syntax-optional-chaining": { - "version": "7.8.3", - "resolved": "https://registry.npmjs.org/@babel/plugin-syntax-optional-chaining/-/plugin-syntax-optional-chaining-7.8.3.tgz", - "integrity": "sha512-KoK9ErH1MBlCPxV0VANkXW2/dw4vlbGDrFgz8bmUsBGYkFRcbRwMh6cIJubdPrkxRwuGdtCk0v/wPTKbQgBjkg==", + "node_modules/@rollup/rollup-linux-s390x-gnu": { + "version": "4.53.2", + "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-s390x-gnu/-/rollup-linux-s390x-gnu-4.53.2.tgz", + "integrity": "sha512-w6yjZF0P+NGzWR3AXWX9zc0DNEGdtvykB03uhonSHMRa+oWA6novflo2WaJr6JZakG2ucsyb+rvhrKac6NIy+w==", + "cpu": [ + "s390x" + ], "dev": true, "license": "MIT", - "dependencies": { - "@babel/helper-plugin-utils": "^7.8.0" - }, - "peerDependencies": { - "@babel/core": "^7.0.0-0" - } + "optional": true, + "os": [ + "linux" + ] }, - "node_modules/@babel/plugin-syntax-private-property-in-object": { - "version": "7.14.5", - "resolved": "https://registry.npmjs.org/@babel/plugin-syntax-private-property-in-object/-/plugin-syntax-private-property-in-object-7.14.5.tgz", - "integrity": "sha512-0wVnp9dxJ72ZUJDV27ZfbSj6iHLoytYZmh3rFcxNnvsJF3ktkzLDZPy/mA17HGsaQT3/DQsWYX1f1QGWkCoVUg==", + "node_modules/@rollup/rollup-linux-x64-gnu": { + "version": "4.53.2", + "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-x64-gnu/-/rollup-linux-x64-gnu-4.53.2.tgz", + "integrity": "sha512-yo8d6tdfdeBArzC7T/PnHd7OypfI9cbuZzPnzLJIyKYFhAQ8SvlkKtKBMbXDxe1h03Rcr7u++nFS7tqXz87Gtw==", + "cpu": [ + "x64" + ], "dev": true, "license": "MIT", - "dependencies": { - "@babel/helper-plugin-utils": "^7.14.5" - }, - "engines": { - "node": ">=6.9.0" - }, - "peerDependencies": { - "@babel/core": "^7.0.0-0" - } + "optional": true, + "os": [ + "linux" + ] }, - "node_modules/@babel/plugin-syntax-top-level-await": { - "version": "7.14.5", - "resolved": "https://registry.npmjs.org/@babel/plugin-syntax-top-level-await/-/plugin-syntax-top-level-await-7.14.5.tgz", - "integrity": "sha512-hx++upLv5U1rgYfwe1xBQUhRmU41NEvpUvrp8jkrSCdvGSnM5/qdRMtylJ6PG5OFkBaHkbTAKTnd3/YyESRHFw==", + "node_modules/@rollup/rollup-linux-x64-musl": { + "version": "4.53.2", + "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-x64-musl/-/rollup-linux-x64-musl-4.53.2.tgz", + "integrity": "sha512-ah59c1YkCxKExPP8O9PwOvs+XRLKwh/mV+3YdKqQ5AMQ0r4M4ZDuOrpWkUaqO7fzAHdINzV9tEVu8vNw48z0lA==", + "cpu": [ + "x64" + ], "dev": true, "license": "MIT", - "dependencies": { - "@babel/helper-plugin-utils": "^7.14.5" - }, - "engines": { - "node": ">=6.9.0" - }, - "peerDependencies": { - "@babel/core": "^7.0.0-0" - } + "optional": true, + "os": [ + "linux" + ] }, - "node_modules/@babel/plugin-syntax-typescript": { - "version": "7.27.1", - "resolved": "https://registry.npmjs.org/@babel/plugin-syntax-typescript/-/plugin-syntax-typescript-7.27.1.tgz", - "integrity": "sha512-xfYCBMxveHrRMnAWl1ZlPXOZjzkN82THFvLhQhFXFt81Z5HnN+EtUkZhv/zcKpmT3fzmWZB0ywiBrbC3vogbwQ==", + "node_modules/@rollup/rollup-openharmony-arm64": { + "version": "4.53.2", + "resolved": "https://registry.npmjs.org/@rollup/rollup-openharmony-arm64/-/rollup-openharmony-arm64-4.53.2.tgz", + "integrity": "sha512-4VEd19Wmhr+Zy7hbUsFZ6YXEiP48hE//KPLCSVNY5RMGX2/7HZ+QkN55a3atM1C/BZCGIgqN+xrVgtdak2S9+A==", + "cpu": [ + "arm64" + ], "dev": true, "license": "MIT", - "dependencies": { - "@babel/helper-plugin-utils": "^7.27.1" - }, - "engines": { - "node": ">=6.9.0" - }, - "peerDependencies": { - "@babel/core": "^7.0.0-0" - } + "optional": true, + "os": [ + "openharmony" + ] }, - "node_modules/@babel/plugin-syntax-unicode-sets-regex": { - "version": "7.18.6", - "resolved": "https://registry.npmjs.org/@babel/plugin-syntax-unicode-sets-regex/-/plugin-syntax-unicode-sets-regex-7.18.6.tgz", - "integrity": "sha512-727YkEAPwSIQTv5im8QHz3upqp92JTWhidIC81Tdx4VJYIte/VndKf1qKrfnnhPLiPghStWfvC/iFaMCQu7Nqg==", + "node_modules/@rollup/rollup-win32-arm64-msvc": { + "version": "4.53.2", + "resolved": "https://registry.npmjs.org/@rollup/rollup-win32-arm64-msvc/-/rollup-win32-arm64-msvc-4.53.2.tgz", + "integrity": "sha512-IlbHFYc/pQCgew/d5fslcy1KEaYVCJ44G8pajugd8VoOEI8ODhtb/j8XMhLpwHCMB3yk2J07ctup10gpw2nyMA==", + "cpu": [ + "arm64" + ], "dev": true, "license": "MIT", - "dependencies": { - "@babel/helper-create-regexp-features-plugin": "^7.18.6", - "@babel/helper-plugin-utils": "^7.18.6" - }, - "engines": { - "node": ">=6.9.0" - }, - "peerDependencies": { - "@babel/core": "^7.0.0" - } + "optional": true, + "os": [ + "win32" + ] }, - "node_modules/@babel/plugin-transform-arrow-functions": { - "version": "7.27.1", - "resolved": "https://registry.npmjs.org/@babel/plugin-transform-arrow-functions/-/plugin-transform-arrow-functions-7.27.1.tgz", - "integrity": "sha512-8Z4TGic6xW70FKThA5HYEKKyBpOOsucTOD1DjU3fZxDg+K3zBJcXMFnt/4yQiZnf5+MiOMSXQ9PaEK/Ilh1DeA==", + "node_modules/@rollup/rollup-win32-ia32-msvc": { + "version": "4.53.2", + "resolved": "https://registry.npmjs.org/@rollup/rollup-win32-ia32-msvc/-/rollup-win32-ia32-msvc-4.53.2.tgz", + "integrity": "sha512-lNlPEGgdUfSzdCWU176ku/dQRnA7W+Gp8d+cWv73jYrb8uT7HTVVxq62DUYxjbaByuf1Yk0RIIAbDzp+CnOTFg==", + "cpu": [ + "ia32" + ], "dev": true, "license": "MIT", - "dependencies": { - "@babel/helper-plugin-utils": "^7.27.1" - }, - "engines": { - "node": ">=6.9.0" - }, - "peerDependencies": { - "@babel/core": "^7.0.0-0" - } - }, - "node_modules/@babel/plugin-transform-async-generator-functions": { - "version": "7.28.0", - "resolved": "https://registry.npmjs.org/@babel/plugin-transform-async-generator-functions/-/plugin-transform-async-generator-functions-7.28.0.tgz", - "integrity": "sha512-BEOdvX4+M765icNPZeidyADIvQ1m1gmunXufXxvRESy/jNNyfovIqUyE7MVgGBjWktCoJlzvFA1To2O4ymIO3Q==", - "dev": true, - "license": "MIT", - "dependencies": { - "@babel/helper-plugin-utils": "^7.27.1", - "@babel/helper-remap-async-to-generator": "^7.27.1", - "@babel/traverse": "^7.28.0" - }, - "engines": { - "node": ">=6.9.0" - }, - "peerDependencies": { - "@babel/core": "^7.0.0-0" - } - }, - "node_modules/@babel/plugin-transform-async-to-generator": { - "version": "7.27.1", - "resolved": "https://registry.npmjs.org/@babel/plugin-transform-async-to-generator/-/plugin-transform-async-to-generator-7.27.1.tgz", - "integrity": "sha512-NREkZsZVJS4xmTr8qzE5y8AfIPqsdQfRuUiLRTEzb7Qii8iFWCyDKaUV2c0rCuh4ljDZ98ALHP/PetiBV2nddA==", - "dev": true, - "license": "MIT", - "dependencies": { - "@babel/helper-module-imports": "^7.27.1", - "@babel/helper-plugin-utils": "^7.27.1", - "@babel/helper-remap-async-to-generator": "^7.27.1" - }, - "engines": { - "node": ">=6.9.0" - }, - "peerDependencies": { - "@babel/core": "^7.0.0-0" - } - }, - "node_modules/@babel/plugin-transform-block-scoped-functions": { - "version": "7.27.1", - "resolved": "https://registry.npmjs.org/@babel/plugin-transform-block-scoped-functions/-/plugin-transform-block-scoped-functions-7.27.1.tgz", - "integrity": "sha512-cnqkuOtZLapWYZUYM5rVIdv1nXYuFVIltZ6ZJ7nIj585QsjKM5dhL2Fu/lICXZ1OyIAFc7Qy+bvDAtTXqGrlhg==", - "dev": true, - "license": "MIT", - "dependencies": { - "@babel/helper-plugin-utils": "^7.27.1" - }, - "engines": { - "node": ">=6.9.0" - }, - "peerDependencies": { - "@babel/core": "^7.0.0-0" - } - }, - "node_modules/@babel/plugin-transform-block-scoping": { - "version": "7.28.0", - "resolved": "https://registry.npmjs.org/@babel/plugin-transform-block-scoping/-/plugin-transform-block-scoping-7.28.0.tgz", - "integrity": "sha512-gKKnwjpdx5sER/wl0WN0efUBFzF/56YZO0RJrSYP4CljXnP31ByY7fol89AzomdlLNzI36AvOTmYHsnZTCkq8Q==", - "dev": true, - "license": "MIT", - "dependencies": { - "@babel/helper-plugin-utils": "^7.27.1" - }, - "engines": { - "node": ">=6.9.0" - }, - "peerDependencies": { - "@babel/core": "^7.0.0-0" - } - }, - "node_modules/@babel/plugin-transform-class-properties": { - "version": "7.27.1", - "resolved": "https://registry.npmjs.org/@babel/plugin-transform-class-properties/-/plugin-transform-class-properties-7.27.1.tgz", - "integrity": "sha512-D0VcalChDMtuRvJIu3U/fwWjf8ZMykz5iZsg77Nuj821vCKI3zCyRLwRdWbsuJ/uRwZhZ002QtCqIkwC/ZkvbA==", - "dev": true, - "license": "MIT", - "dependencies": { - "@babel/helper-create-class-features-plugin": "^7.27.1", - "@babel/helper-plugin-utils": "^7.27.1" - }, - "engines": { - "node": ">=6.9.0" - }, - "peerDependencies": { - "@babel/core": "^7.0.0-0" - } - }, - "node_modules/@babel/plugin-transform-class-static-block": { - "version": "7.27.1", - "resolved": "https://registry.npmjs.org/@babel/plugin-transform-class-static-block/-/plugin-transform-class-static-block-7.27.1.tgz", - "integrity": "sha512-s734HmYU78MVzZ++joYM+NkJusItbdRcbm+AGRgJCt3iA+yux0QpD9cBVdz3tKyrjVYWRl7j0mHSmv4lhV0aoA==", - "dev": true, - "license": "MIT", - "dependencies": { - "@babel/helper-create-class-features-plugin": "^7.27.1", - "@babel/helper-plugin-utils": "^7.27.1" - }, - "engines": { - "node": ">=6.9.0" - }, - "peerDependencies": { - "@babel/core": "^7.12.0" - } - }, - "node_modules/@babel/plugin-transform-classes": { - "version": "7.28.0", - "resolved": "https://registry.npmjs.org/@babel/plugin-transform-classes/-/plugin-transform-classes-7.28.0.tgz", - "integrity": "sha512-IjM1IoJNw72AZFlj33Cu8X0q2XK/6AaVC3jQu+cgQ5lThWD5ajnuUAml80dqRmOhmPkTH8uAwnpMu9Rvj0LTRA==", - "dev": true, - "license": "MIT", - "dependencies": { - "@babel/helper-annotate-as-pure": "^7.27.3", - "@babel/helper-compilation-targets": "^7.27.2", - "@babel/helper-globals": "^7.28.0", - "@babel/helper-plugin-utils": "^7.27.1", - "@babel/helper-replace-supers": "^7.27.1", - "@babel/traverse": "^7.28.0" - }, - "engines": { - "node": ">=6.9.0" - }, - "peerDependencies": { - "@babel/core": "^7.0.0-0" - } - }, - "node_modules/@babel/plugin-transform-computed-properties": { - "version": "7.27.1", - "resolved": "https://registry.npmjs.org/@babel/plugin-transform-computed-properties/-/plugin-transform-computed-properties-7.27.1.tgz", - "integrity": "sha512-lj9PGWvMTVksbWiDT2tW68zGS/cyo4AkZ/QTp0sQT0mjPopCmrSkzxeXkznjqBxzDI6TclZhOJbBmbBLjuOZUw==", - "dev": true, - "license": "MIT", - "dependencies": { - "@babel/helper-plugin-utils": "^7.27.1", - "@babel/template": "^7.27.1" - }, - "engines": { - "node": ">=6.9.0" - }, - "peerDependencies": { - "@babel/core": "^7.0.0-0" - } - }, - "node_modules/@babel/plugin-transform-destructuring": { - "version": "7.28.0", - "resolved": "https://registry.npmjs.org/@babel/plugin-transform-destructuring/-/plugin-transform-destructuring-7.28.0.tgz", - "integrity": "sha512-v1nrSMBiKcodhsyJ4Gf+Z0U/yawmJDBOTpEB3mcQY52r9RIyPneGyAS/yM6seP/8I+mWI3elOMtT5dB8GJVs+A==", - "dev": true, - "license": "MIT", - "dependencies": { - "@babel/helper-plugin-utils": "^7.27.1", - "@babel/traverse": "^7.28.0" - }, - "engines": { - "node": ">=6.9.0" - }, - "peerDependencies": { - "@babel/core": "^7.0.0-0" - } - }, - "node_modules/@babel/plugin-transform-dotall-regex": { - "version": "7.27.1", - "resolved": "https://registry.npmjs.org/@babel/plugin-transform-dotall-regex/-/plugin-transform-dotall-regex-7.27.1.tgz", - "integrity": "sha512-gEbkDVGRvjj7+T1ivxrfgygpT7GUd4vmODtYpbs0gZATdkX8/iSnOtZSxiZnsgm1YjTgjI6VKBGSJJevkrclzw==", - "dev": true, - "license": "MIT", - "dependencies": { - "@babel/helper-create-regexp-features-plugin": "^7.27.1", - "@babel/helper-plugin-utils": "^7.27.1" - }, - "engines": { - "node": ">=6.9.0" - }, - "peerDependencies": { - "@babel/core": "^7.0.0-0" - } - }, - "node_modules/@babel/plugin-transform-duplicate-keys": { - "version": "7.27.1", - "resolved": "https://registry.npmjs.org/@babel/plugin-transform-duplicate-keys/-/plugin-transform-duplicate-keys-7.27.1.tgz", - "integrity": "sha512-MTyJk98sHvSs+cvZ4nOauwTTG1JeonDjSGvGGUNHreGQns+Mpt6WX/dVzWBHgg+dYZhkC4X+zTDfkTU+Vy9y7Q==", - "dev": true, - "license": "MIT", - "dependencies": { - "@babel/helper-plugin-utils": "^7.27.1" - }, - "engines": { - "node": ">=6.9.0" - }, - "peerDependencies": { - "@babel/core": "^7.0.0-0" - } - }, - "node_modules/@babel/plugin-transform-duplicate-named-capturing-groups-regex": { - "version": "7.27.1", - "resolved": "https://registry.npmjs.org/@babel/plugin-transform-duplicate-named-capturing-groups-regex/-/plugin-transform-duplicate-named-capturing-groups-regex-7.27.1.tgz", - "integrity": "sha512-hkGcueTEzuhB30B3eJCbCYeCaaEQOmQR0AdvzpD4LoN0GXMWzzGSuRrxR2xTnCrvNbVwK9N6/jQ92GSLfiZWoQ==", - "dev": true, - "license": "MIT", - "dependencies": { - "@babel/helper-create-regexp-features-plugin": "^7.27.1", - "@babel/helper-plugin-utils": "^7.27.1" - }, - "engines": { - "node": ">=6.9.0" - }, - "peerDependencies": { - "@babel/core": "^7.0.0" - } - }, - "node_modules/@babel/plugin-transform-dynamic-import": { - "version": "7.27.1", - "resolved": "https://registry.npmjs.org/@babel/plugin-transform-dynamic-import/-/plugin-transform-dynamic-import-7.27.1.tgz", - "integrity": "sha512-MHzkWQcEmjzzVW9j2q8LGjwGWpG2mjwaaB0BNQwst3FIjqsg8Ct/mIZlvSPJvfi9y2AC8mi/ktxbFVL9pZ1I4A==", - "dev": true, - "license": "MIT", - "dependencies": { - "@babel/helper-plugin-utils": "^7.27.1" - }, - "engines": { - "node": ">=6.9.0" - }, - "peerDependencies": { - "@babel/core": "^7.0.0-0" - } - }, - "node_modules/@babel/plugin-transform-explicit-resource-management": { - "version": "7.28.0", - "resolved": "https://registry.npmjs.org/@babel/plugin-transform-explicit-resource-management/-/plugin-transform-explicit-resource-management-7.28.0.tgz", - "integrity": "sha512-K8nhUcn3f6iB+P3gwCv/no7OdzOZQcKchW6N389V6PD8NUWKZHzndOd9sPDVbMoBsbmjMqlB4L9fm+fEFNVlwQ==", - "dev": true, - "license": "MIT", - "dependencies": { - "@babel/helper-plugin-utils": "^7.27.1", - "@babel/plugin-transform-destructuring": "^7.28.0" - }, - "engines": { - "node": ">=6.9.0" - }, - "peerDependencies": { - "@babel/core": "^7.0.0-0" - } - }, - "node_modules/@babel/plugin-transform-exponentiation-operator": { - "version": "7.27.1", - "resolved": "https://registry.npmjs.org/@babel/plugin-transform-exponentiation-operator/-/plugin-transform-exponentiation-operator-7.27.1.tgz", - "integrity": "sha512-uspvXnhHvGKf2r4VVtBpeFnuDWsJLQ6MF6lGJLC89jBR1uoVeqM416AZtTuhTezOfgHicpJQmoD5YUakO/YmXQ==", - "dev": true, - "license": "MIT", - "dependencies": { - "@babel/helper-plugin-utils": "^7.27.1" - }, - "engines": { - "node": ">=6.9.0" - }, - "peerDependencies": { - "@babel/core": "^7.0.0-0" - } - }, - "node_modules/@babel/plugin-transform-export-namespace-from": { - "version": "7.27.1", - "resolved": "https://registry.npmjs.org/@babel/plugin-transform-export-namespace-from/-/plugin-transform-export-namespace-from-7.27.1.tgz", - "integrity": "sha512-tQvHWSZ3/jH2xuq/vZDy0jNn+ZdXJeM8gHvX4lnJmsc3+50yPlWdZXIc5ay+umX+2/tJIqHqiEqcJvxlmIvRvQ==", - "dev": true, - "license": "MIT", - "dependencies": { - "@babel/helper-plugin-utils": "^7.27.1" - }, - "engines": { - "node": ">=6.9.0" - }, - "peerDependencies": { - "@babel/core": "^7.0.0-0" - } - }, - "node_modules/@babel/plugin-transform-for-of": { - "version": "7.27.1", - "resolved": "https://registry.npmjs.org/@babel/plugin-transform-for-of/-/plugin-transform-for-of-7.27.1.tgz", - "integrity": "sha512-BfbWFFEJFQzLCQ5N8VocnCtA8J1CLkNTe2Ms2wocj75dd6VpiqS5Z5quTYcUoo4Yq+DN0rtikODccuv7RU81sw==", - "dev": true, - "license": "MIT", - "dependencies": { - "@babel/helper-plugin-utils": "^7.27.1", - "@babel/helper-skip-transparent-expression-wrappers": "^7.27.1" - }, - "engines": { - "node": ">=6.9.0" - }, - "peerDependencies": { - "@babel/core": "^7.0.0-0" - } - }, - "node_modules/@babel/plugin-transform-function-name": { - "version": "7.27.1", - "resolved": "https://registry.npmjs.org/@babel/plugin-transform-function-name/-/plugin-transform-function-name-7.27.1.tgz", - "integrity": "sha512-1bQeydJF9Nr1eBCMMbC+hdwmRlsv5XYOMu03YSWFwNs0HsAmtSxxF1fyuYPqemVldVyFmlCU7w8UE14LupUSZQ==", - "dev": true, - "license": "MIT", - "dependencies": { - "@babel/helper-compilation-targets": "^7.27.1", - "@babel/helper-plugin-utils": "^7.27.1", - "@babel/traverse": "^7.27.1" - }, - "engines": { - "node": ">=6.9.0" - }, - "peerDependencies": { - "@babel/core": "^7.0.0-0" - } - }, - "node_modules/@babel/plugin-transform-json-strings": { - "version": "7.27.1", - "resolved": "https://registry.npmjs.org/@babel/plugin-transform-json-strings/-/plugin-transform-json-strings-7.27.1.tgz", - "integrity": "sha512-6WVLVJiTjqcQauBhn1LkICsR2H+zm62I3h9faTDKt1qP4jn2o72tSvqMwtGFKGTpojce0gJs+76eZ2uCHRZh0Q==", - "dev": true, - "license": "MIT", - "dependencies": { - "@babel/helper-plugin-utils": "^7.27.1" - }, - "engines": { - "node": ">=6.9.0" - }, - "peerDependencies": { - "@babel/core": "^7.0.0-0" - } - }, - "node_modules/@babel/plugin-transform-literals": { - "version": "7.27.1", - "resolved": "https://registry.npmjs.org/@babel/plugin-transform-literals/-/plugin-transform-literals-7.27.1.tgz", - "integrity": "sha512-0HCFSepIpLTkLcsi86GG3mTUzxV5jpmbv97hTETW3yzrAij8aqlD36toB1D0daVFJM8NK6GvKO0gslVQmm+zZA==", - "dev": true, - "license": "MIT", - "dependencies": { - "@babel/helper-plugin-utils": "^7.27.1" - }, - "engines": { - "node": ">=6.9.0" - }, - "peerDependencies": { - "@babel/core": "^7.0.0-0" - } - }, - "node_modules/@babel/plugin-transform-logical-assignment-operators": { - "version": "7.27.1", - "resolved": "https://registry.npmjs.org/@babel/plugin-transform-logical-assignment-operators/-/plugin-transform-logical-assignment-operators-7.27.1.tgz", - "integrity": "sha512-SJvDs5dXxiae4FbSL1aBJlG4wvl594N6YEVVn9e3JGulwioy6z3oPjx/sQBO3Y4NwUu5HNix6KJ3wBZoewcdbw==", - "dev": true, - "license": "MIT", - "dependencies": { - "@babel/helper-plugin-utils": "^7.27.1" - }, - "engines": { - "node": ">=6.9.0" - }, - "peerDependencies": { - "@babel/core": "^7.0.0-0" - } - }, - "node_modules/@babel/plugin-transform-member-expression-literals": { - "version": "7.27.1", - "resolved": "https://registry.npmjs.org/@babel/plugin-transform-member-expression-literals/-/plugin-transform-member-expression-literals-7.27.1.tgz", - "integrity": "sha512-hqoBX4dcZ1I33jCSWcXrP+1Ku7kdqXf1oeah7ooKOIiAdKQ+uqftgCFNOSzA5AMS2XIHEYeGFg4cKRCdpxzVOQ==", - "dev": true, - "license": "MIT", - "dependencies": { - "@babel/helper-plugin-utils": "^7.27.1" - }, - "engines": { - "node": ">=6.9.0" - }, - "peerDependencies": { - "@babel/core": "^7.0.0-0" - } - }, - "node_modules/@babel/plugin-transform-modules-amd": { - "version": "7.27.1", - "resolved": "https://registry.npmjs.org/@babel/plugin-transform-modules-amd/-/plugin-transform-modules-amd-7.27.1.tgz", - "integrity": "sha512-iCsytMg/N9/oFq6n+gFTvUYDZQOMK5kEdeYxmxt91fcJGycfxVP9CnrxoliM0oumFERba2i8ZtwRUCMhvP1LnA==", - "dev": true, - "license": "MIT", - "dependencies": { - "@babel/helper-module-transforms": "^7.27.1", - "@babel/helper-plugin-utils": "^7.27.1" - }, - "engines": { - "node": ">=6.9.0" - }, - "peerDependencies": { - "@babel/core": "^7.0.0-0" - } - }, - "node_modules/@babel/plugin-transform-modules-commonjs": { - "version": "7.27.1", - "resolved": "https://registry.npmjs.org/@babel/plugin-transform-modules-commonjs/-/plugin-transform-modules-commonjs-7.27.1.tgz", - "integrity": "sha512-OJguuwlTYlN0gBZFRPqwOGNWssZjfIUdS7HMYtN8c1KmwpwHFBwTeFZrg9XZa+DFTitWOW5iTAG7tyCUPsCCyw==", - "dev": true, - "license": "MIT", - "dependencies": { - "@babel/helper-module-transforms": "^7.27.1", - "@babel/helper-plugin-utils": "^7.27.1" - }, - "engines": { - "node": ">=6.9.0" - }, - "peerDependencies": { - "@babel/core": "^7.0.0-0" - } - }, - "node_modules/@babel/plugin-transform-modules-systemjs": { - "version": "7.27.1", - "resolved": "https://registry.npmjs.org/@babel/plugin-transform-modules-systemjs/-/plugin-transform-modules-systemjs-7.27.1.tgz", - "integrity": "sha512-w5N1XzsRbc0PQStASMksmUeqECuzKuTJer7kFagK8AXgpCMkeDMO5S+aaFb7A51ZYDF7XI34qsTX+fkHiIm5yA==", - "dev": true, - "license": "MIT", - "dependencies": { - "@babel/helper-module-transforms": "^7.27.1", - "@babel/helper-plugin-utils": "^7.27.1", - "@babel/helper-validator-identifier": "^7.27.1", - "@babel/traverse": "^7.27.1" - }, - "engines": { - "node": ">=6.9.0" - }, - "peerDependencies": { - "@babel/core": "^7.0.0-0" - } - }, - "node_modules/@babel/plugin-transform-modules-umd": { - "version": "7.27.1", - "resolved": "https://registry.npmjs.org/@babel/plugin-transform-modules-umd/-/plugin-transform-modules-umd-7.27.1.tgz", - "integrity": "sha512-iQBE/xC5BV1OxJbp6WG7jq9IWiD+xxlZhLrdwpPkTX3ydmXdvoCpyfJN7acaIBZaOqTfr76pgzqBJflNbeRK+w==", - "dev": true, - "license": "MIT", - "dependencies": { - "@babel/helper-module-transforms": "^7.27.1", - "@babel/helper-plugin-utils": "^7.27.1" - }, - "engines": { - "node": ">=6.9.0" - }, - "peerDependencies": { - "@babel/core": "^7.0.0-0" - } - }, - "node_modules/@babel/plugin-transform-named-capturing-groups-regex": { - "version": "7.27.1", - "resolved": "https://registry.npmjs.org/@babel/plugin-transform-named-capturing-groups-regex/-/plugin-transform-named-capturing-groups-regex-7.27.1.tgz", - "integrity": "sha512-SstR5JYy8ddZvD6MhV0tM/j16Qds4mIpJTOd1Yu9J9pJjH93bxHECF7pgtc28XvkzTD6Pxcm/0Z73Hvk7kb3Ng==", - "dev": true, - "license": "MIT", - "dependencies": { - "@babel/helper-create-regexp-features-plugin": "^7.27.1", - "@babel/helper-plugin-utils": "^7.27.1" - }, - "engines": { - "node": ">=6.9.0" - }, - "peerDependencies": { - "@babel/core": "^7.0.0" - } - }, - "node_modules/@babel/plugin-transform-new-target": { - "version": "7.27.1", - "resolved": "https://registry.npmjs.org/@babel/plugin-transform-new-target/-/plugin-transform-new-target-7.27.1.tgz", - "integrity": "sha512-f6PiYeqXQ05lYq3TIfIDu/MtliKUbNwkGApPUvyo6+tc7uaR4cPjPe7DFPr15Uyycg2lZU6btZ575CuQoYh7MQ==", - "dev": true, - "license": "MIT", - "dependencies": { - "@babel/helper-plugin-utils": "^7.27.1" - }, - "engines": { - "node": ">=6.9.0" - }, - "peerDependencies": { - "@babel/core": "^7.0.0-0" - } - }, - "node_modules/@babel/plugin-transform-nullish-coalescing-operator": { - "version": "7.27.1", - "resolved": "https://registry.npmjs.org/@babel/plugin-transform-nullish-coalescing-operator/-/plugin-transform-nullish-coalescing-operator-7.27.1.tgz", - "integrity": "sha512-aGZh6xMo6q9vq1JGcw58lZ1Z0+i0xB2x0XaauNIUXd6O1xXc3RwoWEBlsTQrY4KQ9Jf0s5rgD6SiNkaUdJegTA==", - "dev": true, - "license": "MIT", - "dependencies": { - "@babel/helper-plugin-utils": "^7.27.1" - }, - "engines": { - "node": ">=6.9.0" - }, - "peerDependencies": { - "@babel/core": "^7.0.0-0" - } - }, - "node_modules/@babel/plugin-transform-numeric-separator": { - "version": "7.27.1", - "resolved": "https://registry.npmjs.org/@babel/plugin-transform-numeric-separator/-/plugin-transform-numeric-separator-7.27.1.tgz", - "integrity": "sha512-fdPKAcujuvEChxDBJ5c+0BTaS6revLV7CJL08e4m3de8qJfNIuCc2nc7XJYOjBoTMJeqSmwXJ0ypE14RCjLwaw==", - "dev": true, - "license": "MIT", - "dependencies": { - "@babel/helper-plugin-utils": "^7.27.1" - }, - "engines": { - "node": ">=6.9.0" - }, - "peerDependencies": { - "@babel/core": "^7.0.0-0" - } - }, - "node_modules/@babel/plugin-transform-object-rest-spread": { - "version": "7.28.0", - "resolved": "https://registry.npmjs.org/@babel/plugin-transform-object-rest-spread/-/plugin-transform-object-rest-spread-7.28.0.tgz", - "integrity": "sha512-9VNGikXxzu5eCiQjdE4IZn8sb9q7Xsk5EXLDBKUYg1e/Tve8/05+KJEtcxGxAgCY5t/BpKQM+JEL/yT4tvgiUA==", - "dev": true, - "license": "MIT", - "dependencies": { - "@babel/helper-compilation-targets": "^7.27.2", - "@babel/helper-plugin-utils": "^7.27.1", - "@babel/plugin-transform-destructuring": "^7.28.0", - "@babel/plugin-transform-parameters": "^7.27.7", - "@babel/traverse": "^7.28.0" - }, - "engines": { - "node": ">=6.9.0" - }, - "peerDependencies": { - "@babel/core": "^7.0.0-0" - } - }, - "node_modules/@babel/plugin-transform-object-super": { - "version": "7.27.1", - "resolved": "https://registry.npmjs.org/@babel/plugin-transform-object-super/-/plugin-transform-object-super-7.27.1.tgz", - "integrity": "sha512-SFy8S9plRPbIcxlJ8A6mT/CxFdJx/c04JEctz4jf8YZaVS2px34j7NXRrlGlHkN/M2gnpL37ZpGRGVFLd3l8Ng==", - "dev": true, - "license": "MIT", - "dependencies": { - "@babel/helper-plugin-utils": "^7.27.1", - "@babel/helper-replace-supers": "^7.27.1" - }, - "engines": { - "node": ">=6.9.0" - }, - "peerDependencies": { - "@babel/core": "^7.0.0-0" - } - }, - "node_modules/@babel/plugin-transform-optional-catch-binding": { - "version": "7.27.1", - "resolved": "https://registry.npmjs.org/@babel/plugin-transform-optional-catch-binding/-/plugin-transform-optional-catch-binding-7.27.1.tgz", - "integrity": "sha512-txEAEKzYrHEX4xSZN4kJ+OfKXFVSWKB2ZxM9dpcE3wT7smwkNmXo5ORRlVzMVdJbD+Q8ILTgSD7959uj+3Dm3Q==", - "dev": true, - "license": "MIT", - "dependencies": { - "@babel/helper-plugin-utils": "^7.27.1" - }, - "engines": { - "node": ">=6.9.0" - }, - "peerDependencies": { - "@babel/core": "^7.0.0-0" - } - }, - "node_modules/@babel/plugin-transform-optional-chaining": { - "version": "7.27.1", - "resolved": "https://registry.npmjs.org/@babel/plugin-transform-optional-chaining/-/plugin-transform-optional-chaining-7.27.1.tgz", - "integrity": "sha512-BQmKPPIuc8EkZgNKsv0X4bPmOoayeu4F1YCwx2/CfmDSXDbp7GnzlUH+/ul5VGfRg1AoFPsrIThlEBj2xb4CAg==", - "dev": true, - "license": "MIT", - "dependencies": { - "@babel/helper-plugin-utils": "^7.27.1", - "@babel/helper-skip-transparent-expression-wrappers": "^7.27.1" - }, - "engines": { - "node": ">=6.9.0" - }, - "peerDependencies": { - "@babel/core": "^7.0.0-0" - } - }, - "node_modules/@babel/plugin-transform-parameters": { - "version": "7.27.7", - "resolved": "https://registry.npmjs.org/@babel/plugin-transform-parameters/-/plugin-transform-parameters-7.27.7.tgz", - "integrity": "sha512-qBkYTYCb76RRxUM6CcZA5KRu8K4SM8ajzVeUgVdMVO9NN9uI/GaVmBg/WKJJGnNokV9SY8FxNOVWGXzqzUidBg==", - "dev": true, - "license": "MIT", - "dependencies": { - "@babel/helper-plugin-utils": "^7.27.1" - }, - "engines": { - "node": ">=6.9.0" - }, - "peerDependencies": { - "@babel/core": "^7.0.0-0" - } - }, - "node_modules/@babel/plugin-transform-private-methods": { - "version": "7.27.1", - "resolved": "https://registry.npmjs.org/@babel/plugin-transform-private-methods/-/plugin-transform-private-methods-7.27.1.tgz", - "integrity": "sha512-10FVt+X55AjRAYI9BrdISN9/AQWHqldOeZDUoLyif1Kn05a56xVBXb8ZouL8pZ9jem8QpXaOt8TS7RHUIS+GPA==", - "dev": true, - "license": "MIT", - "dependencies": { - "@babel/helper-create-class-features-plugin": "^7.27.1", - "@babel/helper-plugin-utils": "^7.27.1" - }, - "engines": { - "node": ">=6.9.0" - }, - "peerDependencies": { - "@babel/core": "^7.0.0-0" - } - }, - "node_modules/@babel/plugin-transform-private-property-in-object": { - "version": "7.27.1", - "resolved": "https://registry.npmjs.org/@babel/plugin-transform-private-property-in-object/-/plugin-transform-private-property-in-object-7.27.1.tgz", - "integrity": "sha512-5J+IhqTi1XPa0DXF83jYOaARrX+41gOewWbkPyjMNRDqgOCqdffGh8L3f/Ek5utaEBZExjSAzcyjmV9SSAWObQ==", - "dev": true, - "license": "MIT", - "dependencies": { - "@babel/helper-annotate-as-pure": "^7.27.1", - "@babel/helper-create-class-features-plugin": "^7.27.1", - "@babel/helper-plugin-utils": "^7.27.1" - }, - "engines": { - "node": ">=6.9.0" - }, - "peerDependencies": { - "@babel/core": "^7.0.0-0" - } - }, - "node_modules/@babel/plugin-transform-property-literals": { - "version": "7.27.1", - "resolved": "https://registry.npmjs.org/@babel/plugin-transform-property-literals/-/plugin-transform-property-literals-7.27.1.tgz", - "integrity": "sha512-oThy3BCuCha8kDZ8ZkgOg2exvPYUlprMukKQXI1r1pJ47NCvxfkEy8vK+r/hT9nF0Aa4H1WUPZZjHTFtAhGfmQ==", - "dev": true, - "license": "MIT", - "dependencies": { - "@babel/helper-plugin-utils": "^7.27.1" - }, - "engines": { - "node": ">=6.9.0" - }, - "peerDependencies": { - "@babel/core": "^7.0.0-0" - } - }, - "node_modules/@babel/plugin-transform-regenerator": { - "version": "7.28.1", - "resolved": "https://registry.npmjs.org/@babel/plugin-transform-regenerator/-/plugin-transform-regenerator-7.28.1.tgz", - "integrity": "sha512-P0QiV/taaa3kXpLY+sXla5zec4E+4t4Aqc9ggHlfZ7a2cp8/x/Gv08jfwEtn9gnnYIMvHx6aoOZ8XJL8eU71Dg==", - "dev": true, - "license": "MIT", - "dependencies": { - "@babel/helper-plugin-utils": "^7.27.1" - }, - "engines": { - "node": ">=6.9.0" - }, - "peerDependencies": { - "@babel/core": "^7.0.0-0" - } - }, - "node_modules/@babel/plugin-transform-regexp-modifiers": { - "version": "7.27.1", - "resolved": "https://registry.npmjs.org/@babel/plugin-transform-regexp-modifiers/-/plugin-transform-regexp-modifiers-7.27.1.tgz", - "integrity": "sha512-TtEciroaiODtXvLZv4rmfMhkCv8jx3wgKpL68PuiPh2M4fvz5jhsA7697N1gMvkvr/JTF13DrFYyEbY9U7cVPA==", - "dev": true, - "license": "MIT", - "dependencies": { - "@babel/helper-create-regexp-features-plugin": "^7.27.1", - "@babel/helper-plugin-utils": "^7.27.1" - }, - "engines": { - "node": ">=6.9.0" - }, - "peerDependencies": { - "@babel/core": "^7.0.0" - } - }, - "node_modules/@babel/plugin-transform-reserved-words": { - "version": "7.27.1", - "resolved": "https://registry.npmjs.org/@babel/plugin-transform-reserved-words/-/plugin-transform-reserved-words-7.27.1.tgz", - "integrity": "sha512-V2ABPHIJX4kC7HegLkYoDpfg9PVmuWy/i6vUM5eGK22bx4YVFD3M5F0QQnWQoDs6AGsUWTVOopBiMFQgHaSkVw==", - "dev": true, - "license": "MIT", - "dependencies": { - "@babel/helper-plugin-utils": "^7.27.1" - }, - "engines": { - "node": ">=6.9.0" - }, - "peerDependencies": { - "@babel/core": "^7.0.0-0" - } - }, - "node_modules/@babel/plugin-transform-shorthand-properties": { - "version": "7.27.1", - "resolved": "https://registry.npmjs.org/@babel/plugin-transform-shorthand-properties/-/plugin-transform-shorthand-properties-7.27.1.tgz", - "integrity": "sha512-N/wH1vcn4oYawbJ13Y/FxcQrWk63jhfNa7jef0ih7PHSIHX2LB7GWE1rkPrOnka9kwMxb6hMl19p7lidA+EHmQ==", - "dev": true, - "license": "MIT", - "dependencies": { - "@babel/helper-plugin-utils": "^7.27.1" - }, - "engines": { - "node": ">=6.9.0" - }, - "peerDependencies": { - "@babel/core": "^7.0.0-0" - } - }, - "node_modules/@babel/plugin-transform-spread": { - "version": "7.27.1", - "resolved": "https://registry.npmjs.org/@babel/plugin-transform-spread/-/plugin-transform-spread-7.27.1.tgz", - "integrity": "sha512-kpb3HUqaILBJcRFVhFUs6Trdd4mkrzcGXss+6/mxUd273PfbWqSDHRzMT2234gIg2QYfAjvXLSquP1xECSg09Q==", - "dev": true, - "license": "MIT", - "dependencies": { - "@babel/helper-plugin-utils": "^7.27.1", - "@babel/helper-skip-transparent-expression-wrappers": "^7.27.1" - }, - "engines": { - "node": ">=6.9.0" - }, - "peerDependencies": { - "@babel/core": "^7.0.0-0" - } - }, - "node_modules/@babel/plugin-transform-sticky-regex": { - "version": "7.27.1", - "resolved": "https://registry.npmjs.org/@babel/plugin-transform-sticky-regex/-/plugin-transform-sticky-regex-7.27.1.tgz", - "integrity": "sha512-lhInBO5bi/Kowe2/aLdBAawijx+q1pQzicSgnkB6dUPc1+RC8QmJHKf2OjvU+NZWitguJHEaEmbV6VWEouT58g==", - "dev": true, - "license": "MIT", - "dependencies": { - "@babel/helper-plugin-utils": "^7.27.1" - }, - "engines": { - "node": ">=6.9.0" - }, - "peerDependencies": { - "@babel/core": "^7.0.0-0" - } - }, - "node_modules/@babel/plugin-transform-template-literals": { - "version": "7.27.1", - "resolved": "https://registry.npmjs.org/@babel/plugin-transform-template-literals/-/plugin-transform-template-literals-7.27.1.tgz", - "integrity": "sha512-fBJKiV7F2DxZUkg5EtHKXQdbsbURW3DZKQUWphDum0uRP6eHGGa/He9mc0mypL680pb+e/lDIthRohlv8NCHkg==", - "dev": true, - "license": "MIT", - "dependencies": { - "@babel/helper-plugin-utils": "^7.27.1" - }, - "engines": { - "node": ">=6.9.0" - }, - "peerDependencies": { - "@babel/core": "^7.0.0-0" - } - }, - "node_modules/@babel/plugin-transform-typeof-symbol": { - "version": "7.27.1", - "resolved": "https://registry.npmjs.org/@babel/plugin-transform-typeof-symbol/-/plugin-transform-typeof-symbol-7.27.1.tgz", - "integrity": "sha512-RiSILC+nRJM7FY5srIyc4/fGIwUhyDuuBSdWn4y6yT6gm652DpCHZjIipgn6B7MQ1ITOUnAKWixEUjQRIBIcLw==", - "dev": true, - "license": "MIT", - "dependencies": { - "@babel/helper-plugin-utils": "^7.27.1" - }, - "engines": { - "node": ">=6.9.0" - }, - "peerDependencies": { - "@babel/core": "^7.0.0-0" - } - }, - "node_modules/@babel/plugin-transform-typescript": { - "version": "7.28.0", - "resolved": "https://registry.npmjs.org/@babel/plugin-transform-typescript/-/plugin-transform-typescript-7.28.0.tgz", - "integrity": "sha512-4AEiDEBPIZvLQaWlc9liCavE0xRM0dNca41WtBeM3jgFptfUOSG9z0uteLhq6+3rq+WB6jIvUwKDTpXEHPJ2Vg==", - "dev": true, - "license": "MIT", - "dependencies": { - "@babel/helper-annotate-as-pure": "^7.27.3", - "@babel/helper-create-class-features-plugin": "^7.27.1", - "@babel/helper-plugin-utils": "^7.27.1", - "@babel/helper-skip-transparent-expression-wrappers": "^7.27.1", - "@babel/plugin-syntax-typescript": "^7.27.1" - }, - "engines": { - "node": ">=6.9.0" - }, - "peerDependencies": { - "@babel/core": "^7.0.0-0" - } - }, - "node_modules/@babel/plugin-transform-unicode-escapes": { - "version": "7.27.1", - "resolved": "https://registry.npmjs.org/@babel/plugin-transform-unicode-escapes/-/plugin-transform-unicode-escapes-7.27.1.tgz", - "integrity": "sha512-Ysg4v6AmF26k9vpfFuTZg8HRfVWzsh1kVfowA23y9j/Gu6dOuahdUVhkLqpObp3JIv27MLSii6noRnuKN8H0Mg==", - "dev": true, - "license": "MIT", - "dependencies": { - "@babel/helper-plugin-utils": "^7.27.1" - }, - "engines": { - "node": ">=6.9.0" - }, - "peerDependencies": { - "@babel/core": "^7.0.0-0" - } - }, - "node_modules/@babel/plugin-transform-unicode-property-regex": { - "version": "7.27.1", - "resolved": "https://registry.npmjs.org/@babel/plugin-transform-unicode-property-regex/-/plugin-transform-unicode-property-regex-7.27.1.tgz", - "integrity": "sha512-uW20S39PnaTImxp39O5qFlHLS9LJEmANjMG7SxIhap8rCHqu0Ik+tLEPX5DKmHn6CsWQ7j3lix2tFOa5YtL12Q==", - "dev": true, - "license": "MIT", - "dependencies": { - "@babel/helper-create-regexp-features-plugin": "^7.27.1", - "@babel/helper-plugin-utils": "^7.27.1" - }, - "engines": { - "node": ">=6.9.0" - }, - "peerDependencies": { - "@babel/core": "^7.0.0-0" - } - }, - "node_modules/@babel/plugin-transform-unicode-regex": { - "version": "7.27.1", - "resolved": "https://registry.npmjs.org/@babel/plugin-transform-unicode-regex/-/plugin-transform-unicode-regex-7.27.1.tgz", - "integrity": "sha512-xvINq24TRojDuyt6JGtHmkVkrfVV3FPT16uytxImLeBZqW3/H52yN+kM1MGuyPkIQxrzKwPHs5U/MP3qKyzkGw==", - "dev": true, - "license": "MIT", - "dependencies": { - "@babel/helper-create-regexp-features-plugin": "^7.27.1", - "@babel/helper-plugin-utils": "^7.27.1" - }, - "engines": { - "node": ">=6.9.0" - }, - "peerDependencies": { - "@babel/core": "^7.0.0-0" - } - }, - "node_modules/@babel/plugin-transform-unicode-sets-regex": { - "version": "7.27.1", - "resolved": "https://registry.npmjs.org/@babel/plugin-transform-unicode-sets-regex/-/plugin-transform-unicode-sets-regex-7.27.1.tgz", - "integrity": "sha512-EtkOujbc4cgvb0mlpQefi4NTPBzhSIevblFevACNLUspmrALgmEBdL/XfnyyITfd8fKBZrZys92zOWcik7j9Tw==", - "dev": true, - "license": "MIT", - "dependencies": { - "@babel/helper-create-regexp-features-plugin": "^7.27.1", - "@babel/helper-plugin-utils": "^7.27.1" - }, - "engines": { - "node": ">=6.9.0" - }, - "peerDependencies": { - "@babel/core": "^7.0.0" - } - }, - "node_modules/@babel/preset-env": { - "version": "7.28.0", - "resolved": "https://registry.npmjs.org/@babel/preset-env/-/preset-env-7.28.0.tgz", - "integrity": "sha512-VmaxeGOwuDqzLl5JUkIRM1X2Qu2uKGxHEQWh+cvvbl7JuJRgKGJSfsEF/bUaxFhJl/XAyxBe7q7qSuTbKFuCyg==", - "dev": true, - "license": "MIT", - "dependencies": { - "@babel/compat-data": "^7.28.0", - "@babel/helper-compilation-targets": "^7.27.2", - "@babel/helper-plugin-utils": "^7.27.1", - "@babel/helper-validator-option": "^7.27.1", - "@babel/plugin-bugfix-firefox-class-in-computed-class-key": "^7.27.1", - "@babel/plugin-bugfix-safari-class-field-initializer-scope": "^7.27.1", - "@babel/plugin-bugfix-safari-id-destructuring-collision-in-function-expression": "^7.27.1", - "@babel/plugin-bugfix-v8-spread-parameters-in-optional-chaining": "^7.27.1", - "@babel/plugin-bugfix-v8-static-class-fields-redefine-readonly": "^7.27.1", - "@babel/plugin-proposal-private-property-in-object": "7.21.0-placeholder-for-preset-env.2", - "@babel/plugin-syntax-import-assertions": "^7.27.1", - "@babel/plugin-syntax-import-attributes": "^7.27.1", - "@babel/plugin-syntax-unicode-sets-regex": "^7.18.6", - "@babel/plugin-transform-arrow-functions": "^7.27.1", - "@babel/plugin-transform-async-generator-functions": "^7.28.0", - "@babel/plugin-transform-async-to-generator": "^7.27.1", - "@babel/plugin-transform-block-scoped-functions": "^7.27.1", - "@babel/plugin-transform-block-scoping": "^7.28.0", - "@babel/plugin-transform-class-properties": "^7.27.1", - "@babel/plugin-transform-class-static-block": "^7.27.1", - "@babel/plugin-transform-classes": "^7.28.0", - "@babel/plugin-transform-computed-properties": "^7.27.1", - "@babel/plugin-transform-destructuring": "^7.28.0", - "@babel/plugin-transform-dotall-regex": "^7.27.1", - "@babel/plugin-transform-duplicate-keys": "^7.27.1", - "@babel/plugin-transform-duplicate-named-capturing-groups-regex": "^7.27.1", - "@babel/plugin-transform-dynamic-import": "^7.27.1", - "@babel/plugin-transform-explicit-resource-management": "^7.28.0", - "@babel/plugin-transform-exponentiation-operator": "^7.27.1", - "@babel/plugin-transform-export-namespace-from": "^7.27.1", - "@babel/plugin-transform-for-of": "^7.27.1", - "@babel/plugin-transform-function-name": "^7.27.1", - "@babel/plugin-transform-json-strings": "^7.27.1", - "@babel/plugin-transform-literals": "^7.27.1", - "@babel/plugin-transform-logical-assignment-operators": "^7.27.1", - "@babel/plugin-transform-member-expression-literals": "^7.27.1", - "@babel/plugin-transform-modules-amd": "^7.27.1", - "@babel/plugin-transform-modules-commonjs": "^7.27.1", - "@babel/plugin-transform-modules-systemjs": "^7.27.1", - "@babel/plugin-transform-modules-umd": "^7.27.1", - "@babel/plugin-transform-named-capturing-groups-regex": "^7.27.1", - "@babel/plugin-transform-new-target": "^7.27.1", - "@babel/plugin-transform-nullish-coalescing-operator": "^7.27.1", - "@babel/plugin-transform-numeric-separator": "^7.27.1", - "@babel/plugin-transform-object-rest-spread": "^7.28.0", - "@babel/plugin-transform-object-super": "^7.27.1", - "@babel/plugin-transform-optional-catch-binding": "^7.27.1", - "@babel/plugin-transform-optional-chaining": "^7.27.1", - "@babel/plugin-transform-parameters": "^7.27.7", - "@babel/plugin-transform-private-methods": "^7.27.1", - "@babel/plugin-transform-private-property-in-object": "^7.27.1", - "@babel/plugin-transform-property-literals": "^7.27.1", - "@babel/plugin-transform-regenerator": "^7.28.0", - "@babel/plugin-transform-regexp-modifiers": "^7.27.1", - "@babel/plugin-transform-reserved-words": "^7.27.1", - "@babel/plugin-transform-shorthand-properties": "^7.27.1", - "@babel/plugin-transform-spread": "^7.27.1", - "@babel/plugin-transform-sticky-regex": "^7.27.1", - "@babel/plugin-transform-template-literals": "^7.27.1", - "@babel/plugin-transform-typeof-symbol": "^7.27.1", - "@babel/plugin-transform-unicode-escapes": "^7.27.1", - "@babel/plugin-transform-unicode-property-regex": "^7.27.1", - "@babel/plugin-transform-unicode-regex": "^7.27.1", - "@babel/plugin-transform-unicode-sets-regex": "^7.27.1", - "@babel/preset-modules": "0.1.6-no-external-plugins", - "babel-plugin-polyfill-corejs2": "^0.4.14", - "babel-plugin-polyfill-corejs3": "^0.13.0", - "babel-plugin-polyfill-regenerator": "^0.6.5", - "core-js-compat": "^3.43.0", - "semver": "^6.3.1" - }, - "engines": { - "node": ">=6.9.0" - }, - "peerDependencies": { - "@babel/core": "^7.0.0-0" - } - }, - "node_modules/@babel/preset-modules": { - "version": "0.1.6-no-external-plugins", - "resolved": "https://registry.npmjs.org/@babel/preset-modules/-/preset-modules-0.1.6-no-external-plugins.tgz", - "integrity": "sha512-HrcgcIESLm9aIR842yhJ5RWan/gebQUJ6E/E5+rf0y9o6oj7w0Br+sWuL6kEQ/o/AdfvR1Je9jG18/gnpwjEyA==", - "dev": true, - "license": "MIT", - "dependencies": { - "@babel/helper-plugin-utils": "^7.0.0", - "@babel/types": "^7.4.4", - "esutils": "^2.0.2" - }, - "peerDependencies": { - "@babel/core": "^7.0.0-0 || ^8.0.0-0 <8.0.0" - } - }, - "node_modules/@babel/preset-typescript": { - "version": "7.27.1", - "resolved": "https://registry.npmjs.org/@babel/preset-typescript/-/preset-typescript-7.27.1.tgz", - "integrity": "sha512-l7WfQfX0WK4M0v2RudjuQK4u99BS6yLHYEmdtVPP7lKV013zr9DygFuWNlnbvQ9LR+LS0Egz/XAvGx5U9MX0fQ==", - "dev": true, - "license": "MIT", - "dependencies": { - "@babel/helper-plugin-utils": "^7.27.1", - "@babel/helper-validator-option": "^7.27.1", - "@babel/plugin-syntax-jsx": "^7.27.1", - "@babel/plugin-transform-modules-commonjs": "^7.27.1", - "@babel/plugin-transform-typescript": "^7.27.1" - }, - "engines": { - "node": ">=6.9.0" - }, - "peerDependencies": { - "@babel/core": "^7.0.0-0" - } - }, - "node_modules/@babel/template": { - "version": "7.27.2", - "resolved": "https://registry.npmjs.org/@babel/template/-/template-7.27.2.tgz", - "integrity": "sha512-LPDZ85aEJyYSd18/DkjNh4/y1ntkE5KwUHWTiqgRxruuZL2F1yuHligVHLvcHY2vMHXttKFpJn6LwfI7cw7ODw==", - "dev": true, - "license": "MIT", - "dependencies": { - "@babel/code-frame": "^7.27.1", - "@babel/parser": "^7.27.2", - "@babel/types": "^7.27.1" - }, - "engines": { - "node": ">=6.9.0" - } - }, - "node_modules/@babel/traverse": { - "version": "7.28.0", - "resolved": "https://registry.npmjs.org/@babel/traverse/-/traverse-7.28.0.tgz", - "integrity": "sha512-mGe7UK5wWyh0bKRfupsUchrQGqvDbZDbKJw+kcRGSmdHVYrv+ltd0pnpDTVpiTqnaBru9iEvA8pz8W46v0Amwg==", - "dev": true, - "license": "MIT", - "dependencies": { - "@babel/code-frame": "^7.27.1", - "@babel/generator": "^7.28.0", - "@babel/helper-globals": "^7.28.0", - "@babel/parser": "^7.28.0", - "@babel/template": "^7.27.2", - "@babel/types": "^7.28.0", - "debug": "^4.3.1" - }, - "engines": { - "node": ">=6.9.0" - } - }, - "node_modules/@babel/types": { - "version": "7.28.2", - "resolved": "https://registry.npmjs.org/@babel/types/-/types-7.28.2.tgz", - "integrity": "sha512-ruv7Ae4J5dUYULmeXw1gmb7rYRz57OWCPM57pHojnLq/3Z1CK2lNSLTCVjxVk1F/TZHwOZZrOWi0ur95BbLxNQ==", - "dev": true, - "license": "MIT", - "dependencies": { - "@babel/helper-string-parser": "^7.27.1", - "@babel/helper-validator-identifier": "^7.27.1" - }, - "engines": { - "node": ">=6.9.0" - } - }, - "node_modules/@bcoe/v8-coverage": { - "version": "0.2.3", - "resolved": "https://registry.npmjs.org/@bcoe/v8-coverage/-/v8-coverage-0.2.3.tgz", - "integrity": "sha512-0hYQ8SB4Db5zvZB4axdMHGwEaQjkZzFjQiN9LVYvIFB2nSUHW9tYpxWriPrWDASIxiaXax83REcLxuSdnGPZtw==", - "dev": true, - "license": "MIT" - }, - "node_modules/@emnapi/core": { - "version": "1.4.5", - "resolved": "https://registry.npmjs.org/@emnapi/core/-/core-1.4.5.tgz", - "integrity": "sha512-XsLw1dEOpkSX/WucdqUhPWP7hDxSvZiY+fsUC14h+FtQ2Ifni4znbBt8punRX+Uj2JG/uDb8nEHVKvrVlvdZ5Q==", - "dev": true, - "license": "MIT", - "optional": true, - "dependencies": { - "@emnapi/wasi-threads": "1.0.4", - "tslib": "^2.4.0" - } - }, - "node_modules/@emnapi/runtime": { - "version": "1.4.5", - "resolved": "https://registry.npmjs.org/@emnapi/runtime/-/runtime-1.4.5.tgz", - "integrity": "sha512-++LApOtY0pEEz1zrd9vy1/zXVaVJJ/EbAF3u0fXIzPJEDtnITsBGbbK0EkM72amhl/R5b+5xx0Y/QhcVOpuulg==", - "dev": true, - "license": "MIT", - "optional": true, - "dependencies": { - "tslib": "^2.4.0" - } - }, - "node_modules/@emnapi/wasi-threads": { - "version": "1.0.4", - "resolved": "https://registry.npmjs.org/@emnapi/wasi-threads/-/wasi-threads-1.0.4.tgz", - "integrity": "sha512-PJR+bOmMOPH8AtcTGAyYNiuJ3/Fcoj2XN/gBEWzDIKh254XO+mM9XoXHk5GNEhodxeMznbg7BlRojVbKN+gC6g==", - "dev": true, - "license": "MIT", - "optional": true, - "dependencies": { - "tslib": "^2.4.0" - } - }, - "node_modules/@eslint-community/eslint-utils": { - "version": "4.7.0", - "resolved": "https://registry.npmjs.org/@eslint-community/eslint-utils/-/eslint-utils-4.7.0.tgz", - "integrity": "sha512-dyybb3AcajC7uha6CvhdVRJqaKyn7w2YKqKyAN37NKYgZT36w+iRb0Dymmc5qEJ549c/S31cMMSFd75bteCpCw==", - "dev": true, - "license": "MIT", - "dependencies": { - "eslint-visitor-keys": "^3.4.3" - }, - "engines": { - "node": "^12.22.0 || ^14.17.0 || >=16.0.0" - }, - "funding": { - "url": "https://opencollective.com/eslint" - }, - "peerDependencies": { - "eslint": "^6.0.0 || ^7.0.0 || >=8.0.0" - } - }, - "node_modules/@eslint-community/regexpp": { - "version": "4.12.1", - "resolved": "https://registry.npmjs.org/@eslint-community/regexpp/-/regexpp-4.12.1.tgz", - "integrity": "sha512-CCZCDJuduB9OUkFkY2IgppNZMi2lBQgD2qzwXkEia16cge2pijY/aXi96CJMquDMn3nJdlPV1A5KrJEXwfLNzQ==", - "dev": true, - "license": "MIT", - "engines": { - "node": "^12.0.0 || ^14.0.0 || >=16.0.0" - } - }, - "node_modules/@eslint/config-array": { - "version": "0.21.0", - "resolved": "https://registry.npmjs.org/@eslint/config-array/-/config-array-0.21.0.tgz", - "integrity": "sha512-ENIdc4iLu0d93HeYirvKmrzshzofPw6VkZRKQGe9Nv46ZnWUzcF1xV01dcvEg/1wXUR61OmmlSfyeyO7EvjLxQ==", - "dev": true, - "license": "Apache-2.0", - "dependencies": { - "@eslint/object-schema": "^2.1.6", - "debug": "^4.3.1", - "minimatch": "^3.1.2" - }, - "engines": { - "node": "^18.18.0 || ^20.9.0 || >=21.1.0" - } - }, - "node_modules/@eslint/config-array/node_modules/brace-expansion": { - "version": "1.1.12", - "resolved": "https://registry.npmjs.org/brace-expansion/-/brace-expansion-1.1.12.tgz", - "integrity": "sha512-9T9UjW3r0UW5c1Q7GTwllptXwhvYmEzFhzMfZ9H7FQWt+uZePjZPjBP/W1ZEyZ1twGWom5/56TF4lPcqjnDHcg==", - "dev": true, - "license": "MIT", - "dependencies": { - "balanced-match": "^1.0.0", - "concat-map": "0.0.1" - } - }, - "node_modules/@eslint/config-array/node_modules/minimatch": { - "version": "3.1.2", - "resolved": "https://registry.npmjs.org/minimatch/-/minimatch-3.1.2.tgz", - "integrity": "sha512-J7p63hRiAjw1NDEww1W7i37+ByIrOWO5XQQAzZ3VOcL0PNybwpfmV/N05zFAzwQ9USyEcX6t3UO+K5aqBQOIHw==", - "dev": true, - "license": "ISC", - "dependencies": { - "brace-expansion": "^1.1.7" - }, - "engines": { - "node": "*" - } - }, - "node_modules/@eslint/config-helpers": { - "version": "0.3.0", - "resolved": "https://registry.npmjs.org/@eslint/config-helpers/-/config-helpers-0.3.0.tgz", - "integrity": "sha512-ViuymvFmcJi04qdZeDc2whTHryouGcDlaxPqarTD0ZE10ISpxGUVZGZDx4w01upyIynL3iu6IXH2bS1NhclQMw==", - "dev": true, - "license": "Apache-2.0", - "engines": { - "node": "^18.18.0 || ^20.9.0 || >=21.1.0" - } - }, - "node_modules/@eslint/core": { - "version": "0.15.1", - "resolved": "https://registry.npmjs.org/@eslint/core/-/core-0.15.1.tgz", - "integrity": "sha512-bkOp+iumZCCbt1K1CmWf0R9pM5yKpDv+ZXtvSyQpudrI9kuFLp+bM2WOPXImuD/ceQuaa8f5pj93Y7zyECIGNA==", - "dev": true, - "license": "Apache-2.0", - "dependencies": { - "@types/json-schema": "^7.0.15" - }, - "engines": { - "node": "^18.18.0 || ^20.9.0 || >=21.1.0" - } - }, - "node_modules/@eslint/eslintrc": { - "version": "3.3.1", - "resolved": "https://registry.npmjs.org/@eslint/eslintrc/-/eslintrc-3.3.1.tgz", - "integrity": "sha512-gtF186CXhIl1p4pJNGZw8Yc6RlshoePRvE0X91oPGb3vZ8pM3qOS9W9NGPat9LziaBV7XrJWGylNQXkGcnM3IQ==", - "dev": true, - "license": "MIT", - "dependencies": { - "ajv": "^6.12.4", - "debug": "^4.3.2", - "espree": "^10.0.1", - "globals": "^14.0.0", - "ignore": "^5.2.0", - "import-fresh": "^3.2.1", - "js-yaml": "^4.1.0", - "minimatch": "^3.1.2", - "strip-json-comments": "^3.1.1" - }, - "engines": { - "node": "^18.18.0 || ^20.9.0 || >=21.1.0" - }, - "funding": { - "url": "https://opencollective.com/eslint" - } - }, - "node_modules/@eslint/eslintrc/node_modules/argparse": { - "version": "2.0.1", - "resolved": "https://registry.npmjs.org/argparse/-/argparse-2.0.1.tgz", - "integrity": "sha512-8+9WqebbFzpX9OR+Wa6O29asIogeRMzcGtAINdpMHHyAg10f05aSFVBbcEqGf/PXw1EjAZ+q2/bEBg3DvurK3Q==", - "dev": true, - "license": "Python-2.0" - }, - "node_modules/@eslint/eslintrc/node_modules/brace-expansion": { - "version": "1.1.12", - "resolved": "https://registry.npmjs.org/brace-expansion/-/brace-expansion-1.1.12.tgz", - "integrity": "sha512-9T9UjW3r0UW5c1Q7GTwllptXwhvYmEzFhzMfZ9H7FQWt+uZePjZPjBP/W1ZEyZ1twGWom5/56TF4lPcqjnDHcg==", - "dev": true, - "license": "MIT", - "dependencies": { - "balanced-match": "^1.0.0", - "concat-map": "0.0.1" - } - }, - "node_modules/@eslint/eslintrc/node_modules/globals": { - "version": "14.0.0", - "resolved": "https://registry.npmjs.org/globals/-/globals-14.0.0.tgz", - "integrity": "sha512-oahGvuMGQlPw/ivIYBjVSrWAfWLBeku5tpPE2fOPLi+WHffIWbuh2tCjhyQhTBPMf5E9jDEH4FOmTYgYwbKwtQ==", - "dev": true, - "license": "MIT", - "engines": { - "node": ">=18" - }, - "funding": { - "url": "https://github.com/sponsors/sindresorhus" - } - }, - "node_modules/@eslint/eslintrc/node_modules/ignore": { - "version": "5.3.2", - "resolved": "https://registry.npmjs.org/ignore/-/ignore-5.3.2.tgz", - "integrity": "sha512-hsBTNUqQTDwkWtcdYI2i06Y/nUBEsNEDJKjWdigLvegy8kDuJAS8uRlpkkcQpyEXL0Z/pjDy5HBmMjRCJ2gq+g==", - "dev": true, - "license": "MIT", - "engines": { - "node": ">= 4" - } - }, - "node_modules/@eslint/eslintrc/node_modules/js-yaml": { - "version": "4.1.0", - "resolved": "https://registry.npmjs.org/js-yaml/-/js-yaml-4.1.0.tgz", - "integrity": "sha512-wpxZs9NoxZaJESJGIZTyDEaYpl0FKSA+FB9aJiyemKhMwkxQg63h4T1KJgUGHpTqPDNRcmmYLugrRjJlBtWvRA==", - "dev": true, - "license": "MIT", - "dependencies": { - "argparse": "^2.0.1" - }, - "bin": { - "js-yaml": "bin/js-yaml.js" - } - }, - "node_modules/@eslint/eslintrc/node_modules/minimatch": { - "version": "3.1.2", - "resolved": "https://registry.npmjs.org/minimatch/-/minimatch-3.1.2.tgz", - "integrity": "sha512-J7p63hRiAjw1NDEww1W7i37+ByIrOWO5XQQAzZ3VOcL0PNybwpfmV/N05zFAzwQ9USyEcX6t3UO+K5aqBQOIHw==", - "dev": true, - "license": "ISC", - "dependencies": { - "brace-expansion": "^1.1.7" - }, - "engines": { - "node": "*" - } - }, - "node_modules/@eslint/js": { - "version": "9.32.0", - "resolved": "https://registry.npmjs.org/@eslint/js/-/js-9.32.0.tgz", - "integrity": "sha512-BBpRFZK3eX6uMLKz8WxFOBIFFcGFJ/g8XuwjTHCqHROSIsopI+ddn/d5Cfh36+7+e5edVS8dbSHnBNhrLEX0zg==", - "dev": true, - "license": "MIT", - "engines": { - "node": "^18.18.0 || ^20.9.0 || >=21.1.0" - }, - "funding": { - "url": "https://eslint.org/donate" - } - }, - "node_modules/@eslint/object-schema": { - "version": "2.1.6", - "resolved": "https://registry.npmjs.org/@eslint/object-schema/-/object-schema-2.1.6.tgz", - "integrity": "sha512-RBMg5FRL0I0gs51M/guSAj5/e14VQ4tpZnQNWwuDT66P14I43ItmPfIZRhO9fUVIPOAQXU47atlywZ/czoqFPA==", - "dev": true, - "license": "Apache-2.0", - "engines": { - "node": "^18.18.0 || ^20.9.0 || >=21.1.0" - } - }, - "node_modules/@eslint/plugin-kit": { - "version": "0.3.4", - "resolved": "https://registry.npmjs.org/@eslint/plugin-kit/-/plugin-kit-0.3.4.tgz", - "integrity": "sha512-Ul5l+lHEcw3L5+k8POx6r74mxEYKG5kOb6Xpy2gCRW6zweT6TEhAf8vhxGgjhqrd/VO/Dirhsb+1hNpD1ue9hw==", - "dev": true, - "license": "Apache-2.0", - "dependencies": { - "@eslint/core": "^0.15.1", - "levn": "^0.4.1" - }, - "engines": { - "node": "^18.18.0 || ^20.9.0 || >=21.1.0" - } - }, - "node_modules/@humanfs/core": { - "version": "0.19.1", - "resolved": "https://registry.npmjs.org/@humanfs/core/-/core-0.19.1.tgz", - "integrity": "sha512-5DyQ4+1JEUzejeK1JGICcideyfUbGixgS9jNgex5nqkW+cY7WZhxBigmieN5Qnw9ZosSNVC9KQKyb+GUaGyKUA==", - "dev": true, - "license": "Apache-2.0", - "engines": { - "node": ">=18.18.0" - } - }, - "node_modules/@humanfs/node": { - "version": "0.16.6", - "resolved": "https://registry.npmjs.org/@humanfs/node/-/node-0.16.6.tgz", - "integrity": "sha512-YuI2ZHQL78Q5HbhDiBA1X4LmYdXCKCMQIfw0pw7piHJwyREFebJUvrQN4cMssyES6x+vfUbx1CIpaQUKYdQZOw==", - "dev": true, - "license": "Apache-2.0", - "dependencies": { - "@humanfs/core": "^0.19.1", - "@humanwhocodes/retry": "^0.3.0" - }, - "engines": { - "node": ">=18.18.0" - } - }, - "node_modules/@humanfs/node/node_modules/@humanwhocodes/retry": { - "version": "0.3.1", - "resolved": "https://registry.npmjs.org/@humanwhocodes/retry/-/retry-0.3.1.tgz", - "integrity": "sha512-JBxkERygn7Bv/GbN5Rv8Ul6LVknS+5Bp6RgDC/O8gEBU/yeH5Ui5C/OlWrTb6qct7LjjfT6Re2NxB0ln0yYybA==", - "dev": true, - "license": "Apache-2.0", - "engines": { - "node": ">=18.18" - }, - "funding": { - "type": "github", - "url": "https://github.com/sponsors/nzakas" - } - }, - "node_modules/@humanwhocodes/module-importer": { - "version": "1.0.1", - "resolved": "https://registry.npmjs.org/@humanwhocodes/module-importer/-/module-importer-1.0.1.tgz", - "integrity": "sha512-bxveV4V8v5Yb4ncFTT3rPSgZBOpCkjfK0y4oVVVJwIuDVBRMDXrPyXRL988i5ap9m9bnyEEjWfm5WkBmtffLfA==", - "dev": true, - "license": "Apache-2.0", - "engines": { - "node": ">=12.22" - }, - "funding": { - "type": "github", - "url": "https://github.com/sponsors/nzakas" - } - }, - "node_modules/@humanwhocodes/retry": { - "version": "0.4.3", - "resolved": "https://registry.npmjs.org/@humanwhocodes/retry/-/retry-0.4.3.tgz", - "integrity": "sha512-bV0Tgo9K4hfPCek+aMAn81RppFKv2ySDQeMoSZuvTASywNTnVJCArCZE2FWqpvIatKu7VMRLWlR1EazvVhDyhQ==", - "dev": true, - "license": "Apache-2.0", - "engines": { - "node": ">=18.18" - }, - "funding": { - "type": "github", - "url": "https://github.com/sponsors/nzakas" - } - }, - "node_modules/@isaacs/cliui": { - "version": "8.0.2", - "resolved": "https://registry.npmjs.org/@isaacs/cliui/-/cliui-8.0.2.tgz", - "integrity": "sha512-O8jcjabXaleOG9DQ0+ARXWZBTfnP4WNAqzuiJK7ll44AmxGKv/J2M4TPjxjY3znBCfvBXFzucm1twdyFybFqEA==", - "dev": true, - "license": "ISC", - "dependencies": { - "string-width": "^5.1.2", - "string-width-cjs": "npm:string-width@^4.2.0", - "strip-ansi": "^7.0.1", - "strip-ansi-cjs": "npm:strip-ansi@^6.0.1", - "wrap-ansi": "^8.1.0", - "wrap-ansi-cjs": "npm:wrap-ansi@^7.0.0" - }, - "engines": { - "node": ">=12" - } - }, - "node_modules/@istanbuljs/load-nyc-config": { - "version": "1.1.0", - "resolved": "https://registry.npmjs.org/@istanbuljs/load-nyc-config/-/load-nyc-config-1.1.0.tgz", - "integrity": "sha512-VjeHSlIzpv/NyD3N0YuHfXOPDIixcA1q2ZV98wsMqcYlPmv2n3Yb2lYP9XMElnaFVXg5A7YLTeLu6V84uQDjmQ==", - "dev": true, - "license": "ISC", - "dependencies": { - "camelcase": "^5.3.1", - "find-up": "^4.1.0", - "get-package-type": "^0.1.0", - "js-yaml": "^3.13.1", - "resolve-from": "^5.0.0" - }, - "engines": { - "node": ">=8" - } - }, - "node_modules/@istanbuljs/schema": { - "version": "0.1.3", - "resolved": "https://registry.npmjs.org/@istanbuljs/schema/-/schema-0.1.3.tgz", - "integrity": "sha512-ZXRY4jNvVgSVQ8DL3LTcakaAtXwTVUxE81hslsyD2AtoXW/wVob10HkOJ1X/pAlcI7D+2YoZKg5do8G/w6RYgA==", - "dev": true, - "license": "MIT", - "engines": { - "node": ">=8" - } - }, - "node_modules/@jest/console": { - "version": "30.0.5", - "resolved": "https://registry.npmjs.org/@jest/console/-/console-30.0.5.tgz", - "integrity": "sha512-xY6b0XiL0Nav3ReresUarwl2oIz1gTnxGbGpho9/rbUWsLH0f1OD/VT84xs8c7VmH7MChnLb0pag6PhZhAdDiA==", - "dev": true, - "license": "MIT", - "dependencies": { - "@jest/types": "30.0.5", - "@types/node": "*", - "chalk": "^4.1.2", - "jest-message-util": "30.0.5", - "jest-util": "30.0.5", - "slash": "^3.0.0" - }, - "engines": { - "node": "^18.14.0 || ^20.0.0 || ^22.0.0 || >=24.0.0" - } - }, - "node_modules/@jest/console/node_modules/ansi-styles": { - "version": "4.3.0", - "resolved": "https://registry.npmjs.org/ansi-styles/-/ansi-styles-4.3.0.tgz", - "integrity": "sha512-zbB9rCJAT1rbjiVDb2hqKFHNYLxgtk8NURxZ3IZwD3F6NtxbXZQCnnSi1Lkx+IDohdPlFp222wVALIheZJQSEg==", - "dev": true, - "license": "MIT", - "dependencies": { - "color-convert": "^2.0.1" - }, - "engines": { - "node": ">=8" - }, - "funding": { - "url": "https://github.com/chalk/ansi-styles?sponsor=1" - } - }, - "node_modules/@jest/console/node_modules/chalk": { - "version": "4.1.2", - "resolved": "https://registry.npmjs.org/chalk/-/chalk-4.1.2.tgz", - "integrity": "sha512-oKnbhFyRIXpUuez8iBMmyEa4nbj4IOQyuhc/wy9kY7/WVPcwIO9VA668Pu8RkO7+0G76SLROeyw9CpQ061i4mA==", - "dev": true, - "license": "MIT", - "dependencies": { - "ansi-styles": "^4.1.0", - "supports-color": "^7.1.0" - }, - "engines": { - "node": ">=10" - }, - "funding": { - "url": "https://github.com/chalk/chalk?sponsor=1" - } - }, - "node_modules/@jest/core": { - "version": "30.0.5", - "resolved": "https://registry.npmjs.org/@jest/core/-/core-30.0.5.tgz", - "integrity": "sha512-fKD0OulvRsXF1hmaFgHhVJzczWzA1RXMMo9LTPuFXo9q/alDbME3JIyWYqovWsUBWSoBcsHaGPSLF9rz4l9Qeg==", - "dev": true, - "license": "MIT", - "dependencies": { - "@jest/console": "30.0.5", - "@jest/pattern": "30.0.1", - "@jest/reporters": "30.0.5", - "@jest/test-result": "30.0.5", - "@jest/transform": "30.0.5", - "@jest/types": "30.0.5", - "@types/node": "*", - "ansi-escapes": "^4.3.2", - "chalk": "^4.1.2", - "ci-info": "^4.2.0", - "exit-x": "^0.2.2", - "graceful-fs": "^4.2.11", - "jest-changed-files": "30.0.5", - "jest-config": "30.0.5", - "jest-haste-map": "30.0.5", - "jest-message-util": "30.0.5", - "jest-regex-util": "30.0.1", - "jest-resolve": "30.0.5", - "jest-resolve-dependencies": "30.0.5", - "jest-runner": "30.0.5", - "jest-runtime": "30.0.5", - "jest-snapshot": "30.0.5", - "jest-util": "30.0.5", - "jest-validate": "30.0.5", - "jest-watcher": "30.0.5", - "micromatch": "^4.0.8", - "pretty-format": "30.0.5", - "slash": "^3.0.0" - }, - "engines": { - "node": "^18.14.0 || ^20.0.0 || ^22.0.0 || >=24.0.0" - }, - "peerDependencies": { - "node-notifier": "^8.0.1 || ^9.0.0 || ^10.0.0" - }, - "peerDependenciesMeta": { - "node-notifier": { - "optional": true - } - } - }, - "node_modules/@jest/core/node_modules/ansi-styles": { - "version": "4.3.0", - "resolved": "https://registry.npmjs.org/ansi-styles/-/ansi-styles-4.3.0.tgz", - "integrity": "sha512-zbB9rCJAT1rbjiVDb2hqKFHNYLxgtk8NURxZ3IZwD3F6NtxbXZQCnnSi1Lkx+IDohdPlFp222wVALIheZJQSEg==", - "dev": true, - "license": "MIT", - "dependencies": { - "color-convert": "^2.0.1" - }, - "engines": { - "node": ">=8" - }, - "funding": { - "url": "https://github.com/chalk/ansi-styles?sponsor=1" - } - }, - "node_modules/@jest/core/node_modules/chalk": { - "version": "4.1.2", - "resolved": "https://registry.npmjs.org/chalk/-/chalk-4.1.2.tgz", - "integrity": "sha512-oKnbhFyRIXpUuez8iBMmyEa4nbj4IOQyuhc/wy9kY7/WVPcwIO9VA668Pu8RkO7+0G76SLROeyw9CpQ061i4mA==", - "dev": true, - "license": "MIT", - "dependencies": { - "ansi-styles": "^4.1.0", - "supports-color": "^7.1.0" - }, - "engines": { - "node": ">=10" - }, - "funding": { - "url": "https://github.com/chalk/chalk?sponsor=1" - } - }, - "node_modules/@jest/diff-sequences": { - "version": "30.0.1", - "resolved": "https://registry.npmjs.org/@jest/diff-sequences/-/diff-sequences-30.0.1.tgz", - "integrity": "sha512-n5H8QLDJ47QqbCNn5SuFjCRDrOLEZ0h8vAHCK5RL9Ls7Xa8AQLa/YxAc9UjFqoEDM48muwtBGjtMY5cr0PLDCw==", - "dev": true, - "license": "MIT", - "engines": { - "node": "^18.14.0 || ^20.0.0 || ^22.0.0 || >=24.0.0" - } - }, - "node_modules/@jest/environment": { - "version": "30.0.5", - "resolved": "https://registry.npmjs.org/@jest/environment/-/environment-30.0.5.tgz", - "integrity": "sha512-aRX7WoaWx1oaOkDQvCWImVQ8XNtdv5sEWgk4gxR6NXb7WBUnL5sRak4WRzIQRZ1VTWPvV4VI4mgGjNL9TeKMYA==", - "dev": true, - "license": "MIT", - "dependencies": { - "@jest/fake-timers": "30.0.5", - "@jest/types": "30.0.5", - "@types/node": "*", - "jest-mock": "30.0.5" - }, - "engines": { - "node": "^18.14.0 || ^20.0.0 || ^22.0.0 || >=24.0.0" - } - }, - "node_modules/@jest/expect": { - "version": "30.0.5", - "resolved": "https://registry.npmjs.org/@jest/expect/-/expect-30.0.5.tgz", - "integrity": "sha512-6udac8KKrtTtC+AXZ2iUN/R7dp7Ydry+Fo6FPFnDG54wjVMnb6vW/XNlf7Xj8UDjAE3aAVAsR4KFyKk3TCXmTA==", - "dev": true, - "license": "MIT", - "dependencies": { - "expect": "30.0.5", - "jest-snapshot": "30.0.5" - }, - "engines": { - "node": "^18.14.0 || ^20.0.0 || ^22.0.0 || >=24.0.0" - } - }, - "node_modules/@jest/expect-utils": { - "version": "30.0.5", - "resolved": "https://registry.npmjs.org/@jest/expect-utils/-/expect-utils-30.0.5.tgz", - "integrity": "sha512-F3lmTT7CXWYywoVUGTCmom0vXq3HTTkaZyTAzIy+bXSBizB7o5qzlC9VCtq0arOa8GqmNsbg/cE9C6HLn7Szew==", - "dev": true, - "license": "MIT", - "dependencies": { - "@jest/get-type": "30.0.1" - }, - "engines": { - "node": "^18.14.0 || ^20.0.0 || ^22.0.0 || >=24.0.0" - } - }, - "node_modules/@jest/fake-timers": { - "version": "30.0.5", - "resolved": "https://registry.npmjs.org/@jest/fake-timers/-/fake-timers-30.0.5.tgz", - "integrity": "sha512-ZO5DHfNV+kgEAeP3gK3XlpJLL4U3Sz6ebl/n68Uwt64qFFs5bv4bfEEjyRGK5uM0C90ewooNgFuKMdkbEoMEXw==", - "dev": true, - "license": "MIT", - "dependencies": { - "@jest/types": "30.0.5", - "@sinonjs/fake-timers": "^13.0.0", - "@types/node": "*", - "jest-message-util": "30.0.5", - "jest-mock": "30.0.5", - "jest-util": "30.0.5" - }, - "engines": { - "node": "^18.14.0 || ^20.0.0 || ^22.0.0 || >=24.0.0" - } - }, - "node_modules/@jest/get-type": { - "version": "30.0.1", - "resolved": "https://registry.npmjs.org/@jest/get-type/-/get-type-30.0.1.tgz", - "integrity": "sha512-AyYdemXCptSRFirI5EPazNxyPwAL0jXt3zceFjaj8NFiKP9pOi0bfXonf6qkf82z2t3QWPeLCWWw4stPBzctLw==", - "dev": true, - "license": "MIT", - "engines": { - "node": "^18.14.0 || ^20.0.0 || ^22.0.0 || >=24.0.0" - } - }, - "node_modules/@jest/globals": { - "version": "30.0.5", - "resolved": "https://registry.npmjs.org/@jest/globals/-/globals-30.0.5.tgz", - "integrity": "sha512-7oEJT19WW4oe6HR7oLRvHxwlJk2gev0U9px3ufs8sX9PoD1Eza68KF0/tlN7X0dq/WVsBScXQGgCldA1V9Y/jA==", - "dev": true, - "license": "MIT", - "dependencies": { - "@jest/environment": "30.0.5", - "@jest/expect": "30.0.5", - "@jest/types": "30.0.5", - "jest-mock": "30.0.5" - }, - "engines": { - "node": "^18.14.0 || ^20.0.0 || ^22.0.0 || >=24.0.0" - } - }, - "node_modules/@jest/pattern": { - "version": "30.0.1", - "resolved": "https://registry.npmjs.org/@jest/pattern/-/pattern-30.0.1.tgz", - "integrity": "sha512-gWp7NfQW27LaBQz3TITS8L7ZCQ0TLvtmI//4OwlQRx4rnWxcPNIYjxZpDcN4+UlGxgm3jS5QPz8IPTCkb59wZA==", - "dev": true, - "license": "MIT", - "dependencies": { - "@types/node": "*", - "jest-regex-util": "30.0.1" - }, - "engines": { - "node": "^18.14.0 || ^20.0.0 || ^22.0.0 || >=24.0.0" - } - }, - "node_modules/@jest/reporters": { - "version": "30.0.5", - "resolved": "https://registry.npmjs.org/@jest/reporters/-/reporters-30.0.5.tgz", - "integrity": "sha512-mafft7VBX4jzED1FwGC1o/9QUM2xebzavImZMeqnsklgcyxBto8mV4HzNSzUrryJ+8R9MFOM3HgYuDradWR+4g==", - "dev": true, - "license": "MIT", - "dependencies": { - "@bcoe/v8-coverage": "^0.2.3", - "@jest/console": "30.0.5", - "@jest/test-result": "30.0.5", - "@jest/transform": "30.0.5", - "@jest/types": "30.0.5", - "@jridgewell/trace-mapping": "^0.3.25", - "@types/node": "*", - "chalk": "^4.1.2", - "collect-v8-coverage": "^1.0.2", - "exit-x": "^0.2.2", - "glob": "^10.3.10", - "graceful-fs": "^4.2.11", - "istanbul-lib-coverage": "^3.0.0", - "istanbul-lib-instrument": "^6.0.0", - "istanbul-lib-report": "^3.0.0", - "istanbul-lib-source-maps": "^5.0.0", - "istanbul-reports": "^3.1.3", - "jest-message-util": "30.0.5", - "jest-util": "30.0.5", - "jest-worker": "30.0.5", - "slash": "^3.0.0", - "string-length": "^4.0.2", - "v8-to-istanbul": "^9.0.1" - }, - "engines": { - "node": "^18.14.0 || ^20.0.0 || ^22.0.0 || >=24.0.0" - }, - "peerDependencies": { - "node-notifier": "^8.0.1 || ^9.0.0 || ^10.0.0" - }, - "peerDependenciesMeta": { - "node-notifier": { - "optional": true - } - } - }, - "node_modules/@jest/reporters/node_modules/ansi-styles": { - "version": "4.3.0", - "resolved": "https://registry.npmjs.org/ansi-styles/-/ansi-styles-4.3.0.tgz", - "integrity": "sha512-zbB9rCJAT1rbjiVDb2hqKFHNYLxgtk8NURxZ3IZwD3F6NtxbXZQCnnSi1Lkx+IDohdPlFp222wVALIheZJQSEg==", - "dev": true, - "license": "MIT", - "dependencies": { - "color-convert": "^2.0.1" - }, - "engines": { - "node": ">=8" - }, - "funding": { - "url": "https://github.com/chalk/ansi-styles?sponsor=1" - } - }, - "node_modules/@jest/reporters/node_modules/chalk": { - "version": "4.1.2", - "resolved": "https://registry.npmjs.org/chalk/-/chalk-4.1.2.tgz", - "integrity": "sha512-oKnbhFyRIXpUuez8iBMmyEa4nbj4IOQyuhc/wy9kY7/WVPcwIO9VA668Pu8RkO7+0G76SLROeyw9CpQ061i4mA==", - "dev": true, - "license": "MIT", - "dependencies": { - "ansi-styles": "^4.1.0", - "supports-color": "^7.1.0" - }, - "engines": { - "node": ">=10" - }, - "funding": { - "url": "https://github.com/chalk/chalk?sponsor=1" - } - }, - "node_modules/@jest/schemas": { - "version": "30.0.5", - "resolved": "https://registry.npmjs.org/@jest/schemas/-/schemas-30.0.5.tgz", - "integrity": "sha512-DmdYgtezMkh3cpU8/1uyXakv3tJRcmcXxBOcO0tbaozPwpmh4YMsnWrQm9ZmZMfa5ocbxzbFk6O4bDPEc/iAnA==", - "dev": true, - "license": "MIT", - "dependencies": { - "@sinclair/typebox": "^0.34.0" - }, - "engines": { - "node": "^18.14.0 || ^20.0.0 || ^22.0.0 || >=24.0.0" - } - }, - "node_modules/@jest/snapshot-utils": { - "version": "30.0.5", - "resolved": "https://registry.npmjs.org/@jest/snapshot-utils/-/snapshot-utils-30.0.5.tgz", - "integrity": "sha512-XcCQ5qWHLvi29UUrowgDFvV4t7ETxX91CbDczMnoqXPOIcZOxyNdSjm6kV5XMc8+HkxfRegU/MUmnTbJRzGrUQ==", - "dev": true, - "license": "MIT", - "dependencies": { - "@jest/types": "30.0.5", - "chalk": "^4.1.2", - "graceful-fs": "^4.2.11", - "natural-compare": "^1.4.0" - }, - "engines": { - "node": "^18.14.0 || ^20.0.0 || ^22.0.0 || >=24.0.0" - } - }, - "node_modules/@jest/snapshot-utils/node_modules/ansi-styles": { - "version": "4.3.0", - "resolved": "https://registry.npmjs.org/ansi-styles/-/ansi-styles-4.3.0.tgz", - "integrity": "sha512-zbB9rCJAT1rbjiVDb2hqKFHNYLxgtk8NURxZ3IZwD3F6NtxbXZQCnnSi1Lkx+IDohdPlFp222wVALIheZJQSEg==", - "dev": true, - "license": "MIT", - "dependencies": { - "color-convert": "^2.0.1" - }, - "engines": { - "node": ">=8" - }, - "funding": { - "url": "https://github.com/chalk/ansi-styles?sponsor=1" - } - }, - "node_modules/@jest/snapshot-utils/node_modules/chalk": { - "version": "4.1.2", - "resolved": "https://registry.npmjs.org/chalk/-/chalk-4.1.2.tgz", - "integrity": "sha512-oKnbhFyRIXpUuez8iBMmyEa4nbj4IOQyuhc/wy9kY7/WVPcwIO9VA668Pu8RkO7+0G76SLROeyw9CpQ061i4mA==", - "dev": true, - "license": "MIT", - "dependencies": { - "ansi-styles": "^4.1.0", - "supports-color": "^7.1.0" - }, - "engines": { - "node": ">=10" - }, - "funding": { - "url": "https://github.com/chalk/chalk?sponsor=1" - } - }, - "node_modules/@jest/source-map": { - "version": "30.0.1", - "resolved": "https://registry.npmjs.org/@jest/source-map/-/source-map-30.0.1.tgz", - "integrity": "sha512-MIRWMUUR3sdbP36oyNyhbThLHyJ2eEDClPCiHVbrYAe5g3CHRArIVpBw7cdSB5fr+ofSfIb2Tnsw8iEHL0PYQg==", - "dev": true, - "license": "MIT", - "dependencies": { - "@jridgewell/trace-mapping": "^0.3.25", - "callsites": "^3.1.0", - "graceful-fs": "^4.2.11" - }, - "engines": { - "node": "^18.14.0 || ^20.0.0 || ^22.0.0 || >=24.0.0" - } - }, - "node_modules/@jest/test-result": { - "version": "30.0.5", - "resolved": "https://registry.npmjs.org/@jest/test-result/-/test-result-30.0.5.tgz", - "integrity": "sha512-wPyztnK0gbDMQAJZ43tdMro+qblDHH1Ru/ylzUo21TBKqt88ZqnKKK2m30LKmLLoKtR2lxdpCC/P3g1vfKcawQ==", - "dev": true, - "license": "MIT", - "dependencies": { - "@jest/console": "30.0.5", - "@jest/types": "30.0.5", - "@types/istanbul-lib-coverage": "^2.0.6", - "collect-v8-coverage": "^1.0.2" - }, - "engines": { - "node": "^18.14.0 || ^20.0.0 || ^22.0.0 || >=24.0.0" - } - }, - "node_modules/@jest/test-sequencer": { - "version": "30.0.5", - "resolved": "https://registry.npmjs.org/@jest/test-sequencer/-/test-sequencer-30.0.5.tgz", - "integrity": "sha512-Aea/G1egWoIIozmDD7PBXUOxkekXl7ueGzrsGGi1SbeKgQqCYCIf+wfbflEbf2LiPxL8j2JZGLyrzZagjvW4YQ==", - "dev": true, - "license": "MIT", - "dependencies": { - "@jest/test-result": "30.0.5", - "graceful-fs": "^4.2.11", - "jest-haste-map": "30.0.5", - "slash": "^3.0.0" - }, - "engines": { - "node": "^18.14.0 || ^20.0.0 || ^22.0.0 || >=24.0.0" - } - }, - "node_modules/@jest/transform": { - "version": "30.0.5", - "resolved": "https://registry.npmjs.org/@jest/transform/-/transform-30.0.5.tgz", - "integrity": "sha512-Vk8amLQCmuZyy6GbBht1Jfo9RSdBtg7Lks+B0PecnjI8J+PCLQPGh7uI8Q/2wwpW2gLdiAfiHNsmekKlywULqg==", - "dev": true, - "license": "MIT", - "dependencies": { - "@babel/core": "^7.27.4", - "@jest/types": "30.0.5", - "@jridgewell/trace-mapping": "^0.3.25", - "babel-plugin-istanbul": "^7.0.0", - "chalk": "^4.1.2", - "convert-source-map": "^2.0.0", - "fast-json-stable-stringify": "^2.1.0", - "graceful-fs": "^4.2.11", - "jest-haste-map": "30.0.5", - "jest-regex-util": "30.0.1", - "jest-util": "30.0.5", - "micromatch": "^4.0.8", - "pirates": "^4.0.7", - "slash": "^3.0.0", - "write-file-atomic": "^5.0.1" - }, - "engines": { - "node": "^18.14.0 || ^20.0.0 || ^22.0.0 || >=24.0.0" - } - }, - "node_modules/@jest/transform/node_modules/ansi-styles": { - "version": "4.3.0", - "resolved": "https://registry.npmjs.org/ansi-styles/-/ansi-styles-4.3.0.tgz", - "integrity": "sha512-zbB9rCJAT1rbjiVDb2hqKFHNYLxgtk8NURxZ3IZwD3F6NtxbXZQCnnSi1Lkx+IDohdPlFp222wVALIheZJQSEg==", - "dev": true, - "license": "MIT", - "dependencies": { - "color-convert": "^2.0.1" - }, - "engines": { - "node": ">=8" - }, - "funding": { - "url": "https://github.com/chalk/ansi-styles?sponsor=1" - } - }, - "node_modules/@jest/transform/node_modules/chalk": { - "version": "4.1.2", - "resolved": "https://registry.npmjs.org/chalk/-/chalk-4.1.2.tgz", - "integrity": "sha512-oKnbhFyRIXpUuez8iBMmyEa4nbj4IOQyuhc/wy9kY7/WVPcwIO9VA668Pu8RkO7+0G76SLROeyw9CpQ061i4mA==", - "dev": true, - "license": "MIT", - "dependencies": { - "ansi-styles": "^4.1.0", - "supports-color": "^7.1.0" - }, - "engines": { - "node": ">=10" - }, - "funding": { - "url": "https://github.com/chalk/chalk?sponsor=1" - } - }, - "node_modules/@jest/types": { - "version": "30.0.5", - "resolved": "https://registry.npmjs.org/@jest/types/-/types-30.0.5.tgz", - "integrity": "sha512-aREYa3aku9SSnea4aX6bhKn4bgv3AXkgijoQgbYV3yvbiGt6z+MQ85+6mIhx9DsKW2BuB/cLR/A+tcMThx+KLQ==", - "dev": true, - "license": "MIT", - "dependencies": { - "@jest/pattern": "30.0.1", - "@jest/schemas": "30.0.5", - "@types/istanbul-lib-coverage": "^2.0.6", - "@types/istanbul-reports": "^3.0.4", - "@types/node": "*", - "@types/yargs": "^17.0.33", - "chalk": "^4.1.2" - }, - "engines": { - "node": "^18.14.0 || ^20.0.0 || ^22.0.0 || >=24.0.0" - } - }, - "node_modules/@jest/types/node_modules/ansi-styles": { - "version": "4.3.0", - "resolved": "https://registry.npmjs.org/ansi-styles/-/ansi-styles-4.3.0.tgz", - "integrity": "sha512-zbB9rCJAT1rbjiVDb2hqKFHNYLxgtk8NURxZ3IZwD3F6NtxbXZQCnnSi1Lkx+IDohdPlFp222wVALIheZJQSEg==", - "dev": true, - "license": "MIT", - "dependencies": { - "color-convert": "^2.0.1" - }, - "engines": { - "node": ">=8" - }, - "funding": { - "url": "https://github.com/chalk/ansi-styles?sponsor=1" - } - }, - "node_modules/@jest/types/node_modules/chalk": { - "version": "4.1.2", - "resolved": "https://registry.npmjs.org/chalk/-/chalk-4.1.2.tgz", - "integrity": "sha512-oKnbhFyRIXpUuez8iBMmyEa4nbj4IOQyuhc/wy9kY7/WVPcwIO9VA668Pu8RkO7+0G76SLROeyw9CpQ061i4mA==", - "dev": true, - "license": "MIT", - "dependencies": { - "ansi-styles": "^4.1.0", - "supports-color": "^7.1.0" - }, - "engines": { - "node": ">=10" - }, - "funding": { - "url": "https://github.com/chalk/chalk?sponsor=1" - } - }, - "node_modules/@jridgewell/gen-mapping": { - "version": "0.3.12", - "resolved": "https://registry.npmjs.org/@jridgewell/gen-mapping/-/gen-mapping-0.3.12.tgz", - "integrity": "sha512-OuLGC46TjB5BbN1dH8JULVVZY4WTdkF7tV9Ys6wLL1rubZnCMstOhNHueU5bLCrnRuDhKPDM4g6sw4Bel5Gzqg==", - "dev": true, - "license": "MIT", - "dependencies": { - "@jridgewell/sourcemap-codec": "^1.5.0", - "@jridgewell/trace-mapping": "^0.3.24" - } - }, - "node_modules/@jridgewell/resolve-uri": { - "version": "3.1.2", - "resolved": "https://registry.npmjs.org/@jridgewell/resolve-uri/-/resolve-uri-3.1.2.tgz", - "integrity": "sha512-bRISgCIjP20/tbWSPWMEi54QVPRZExkuD9lJL+UIxUKtwVJA8wW1Trb1jMs1RFXo1CBTNZ/5hpC9QvmKWdopKw==", - "dev": true, - "license": "MIT", - "engines": { - "node": ">=6.0.0" - } - }, - "node_modules/@jridgewell/source-map": { - "version": "0.3.10", - "resolved": "https://registry.npmjs.org/@jridgewell/source-map/-/source-map-0.3.10.tgz", - "integrity": "sha512-0pPkgz9dY+bijgistcTTJ5mR+ocqRXLuhXHYdzoMmmoJ2C9S46RCm2GMUbatPEUK9Yjy26IrAy8D/M00lLkv+Q==", - "dev": true, - "license": "MIT", - "dependencies": { - "@jridgewell/gen-mapping": "^0.3.5", - "@jridgewell/trace-mapping": "^0.3.25" - } - }, - "node_modules/@jridgewell/sourcemap-codec": { - "version": "1.5.4", - "resolved": "https://registry.npmjs.org/@jridgewell/sourcemap-codec/-/sourcemap-codec-1.5.4.tgz", - "integrity": "sha512-VT2+G1VQs/9oz078bLrYbecdZKs912zQlkelYpuf+SXF+QvZDYJlbx/LSx+meSAwdDFnF8FVXW92AVjjkVmgFw==", - "dev": true, - "license": "MIT" - }, - "node_modules/@jridgewell/trace-mapping": { - "version": "0.3.29", - "resolved": "https://registry.npmjs.org/@jridgewell/trace-mapping/-/trace-mapping-0.3.29.tgz", - "integrity": "sha512-uw6guiW/gcAGPDhLmd77/6lW8QLeiV5RUTsAX46Db6oLhGaVj4lhnPwb184s1bkc8kdVg/+h988dro8GRDpmYQ==", - "dev": true, - "license": "MIT", - "dependencies": { - "@jridgewell/resolve-uri": "^3.1.0", - "@jridgewell/sourcemap-codec": "^1.4.14" - } - }, - "node_modules/@napi-rs/wasm-runtime": { - "version": "0.2.12", - "resolved": "https://registry.npmjs.org/@napi-rs/wasm-runtime/-/wasm-runtime-0.2.12.tgz", - "integrity": "sha512-ZVWUcfwY4E/yPitQJl481FjFo3K22D6qF0DuFH6Y/nbnE11GY5uguDxZMGXPQ8WQ0128MXQD7TnfHyK4oWoIJQ==", - "dev": true, - "license": "MIT", - "optional": true, - "dependencies": { - "@emnapi/core": "^1.4.3", - "@emnapi/runtime": "^1.4.3", - "@tybys/wasm-util": "^0.10.0" - } - }, - "node_modules/@nodelib/fs.scandir": { - "version": "2.1.5", - "resolved": "https://registry.npmjs.org/@nodelib/fs.scandir/-/fs.scandir-2.1.5.tgz", - "integrity": "sha512-vq24Bq3ym5HEQm2NKCr3yXDwjc7vTsEThRDnkp2DK9p1uqLR+DHurm/NOTo0KG7HYHU7eppKZj3MyqYuMBf62g==", - "dev": true, - "license": "MIT", - "dependencies": { - "@nodelib/fs.stat": "2.0.5", - "run-parallel": "^1.1.9" - }, - "engines": { - "node": ">= 8" - } - }, - "node_modules/@nodelib/fs.stat": { - "version": "2.0.5", - "resolved": "https://registry.npmjs.org/@nodelib/fs.stat/-/fs.stat-2.0.5.tgz", - "integrity": "sha512-RkhPPp2zrqDAQA/2jNhnztcPAlv64XdhIp7a7454A5ovI7Bukxgt7MX7udwAu3zg1DcpPU0rz3VV1SeaqvY4+A==", - "dev": true, - "license": "MIT", - "engines": { - "node": ">= 8" - } - }, - "node_modules/@nodelib/fs.walk": { - "version": "1.2.8", - "resolved": "https://registry.npmjs.org/@nodelib/fs.walk/-/fs.walk-1.2.8.tgz", - "integrity": "sha512-oGB+UxlgWcgQkgwo8GcEGwemoTFt3FIO9ababBmaGwXIoBKZ+GTy0pP185beGg7Llih/NSHSV2XAs1lnznocSg==", - "dev": true, - "license": "MIT", - "dependencies": { - "@nodelib/fs.scandir": "2.1.5", - "fastq": "^1.6.0" - }, - "engines": { - "node": ">= 8" - } - }, - "node_modules/@pkgjs/parseargs": { - "version": "0.11.0", - "resolved": "https://registry.npmjs.org/@pkgjs/parseargs/-/parseargs-0.11.0.tgz", - "integrity": "sha512-+1VkjdD0QBLPodGrJUeqarH8VAIvQODIbwh9XpP5Syisf7YoQgsJKPNFoqqLQlu+VQ/tVSshMR6loPMn8U+dPg==", - "dev": true, - "license": "MIT", - "optional": true, - "engines": { - "node": ">=14" - } - }, - "node_modules/@pkgr/core": { - "version": "0.2.9", - "resolved": "https://registry.npmjs.org/@pkgr/core/-/core-0.2.9.tgz", - "integrity": "sha512-QNqXyfVS2wm9hweSYD2O7F0G06uurj9kZ96TRQE5Y9hU7+tgdZwIkbAKc5Ocy1HxEY2kuDQa6cQ1WRs/O5LFKA==", - "dev": true, - "license": "MIT", - "engines": { - "node": "^12.20.0 || ^14.18.0 || >=16.0.0" - }, - "funding": { - "url": "https://opencollective.com/pkgr" - } - }, - "node_modules/@playwright/test": { - "version": "1.56.1", - "resolved": "https://registry.npmjs.org/@playwright/test/-/test-1.56.1.tgz", - "integrity": "sha512-vSMYtL/zOcFpvJCW71Q/OEGQb7KYBPAdKh35WNSkaZA75JlAO8ED8UN6GUNTm3drWomcbcqRPFqQbLae8yBTdg==", - "dev": true, - "license": "Apache-2.0", - "dependencies": { - "playwright": "1.56.1" - }, - "bin": { - "playwright": "cli.js" - }, - "engines": { - "node": ">=18" - } - }, - "node_modules/@rollup/plugin-node-resolve": { - "version": "16.0.1", - "resolved": "https://registry.npmjs.org/@rollup/plugin-node-resolve/-/plugin-node-resolve-16.0.1.tgz", - "integrity": "sha512-tk5YCxJWIG81umIvNkSod2qK5KyQW19qcBF/B78n1bjtOON6gzKoVeSzAE8yHCZEDmqkHKkxplExA8KzdJLJpA==", - "dev": true, - "license": "MIT", - "dependencies": { - "@rollup/pluginutils": "^5.0.1", - "@types/resolve": "1.20.2", - "deepmerge": "^4.2.2", - "is-module": "^1.0.0", - "resolve": "^1.22.1" - }, - "engines": { - "node": ">=14.0.0" - }, - "peerDependencies": { - "rollup": "^2.78.0||^3.0.0||^4.0.0" - }, - "peerDependenciesMeta": { - "rollup": { - "optional": true - } - } - }, - "node_modules/@rollup/plugin-terser": { - "version": "0.4.4", - "resolved": "https://registry.npmjs.org/@rollup/plugin-terser/-/plugin-terser-0.4.4.tgz", - "integrity": "sha512-XHeJC5Bgvs8LfukDwWZp7yeqin6ns8RTl2B9avbejt6tZqsqvVoWI7ZTQrcNsfKEDWBTnTxM8nMDkO2IFFbd0A==", - "dev": true, - "license": "MIT", - "dependencies": { - "serialize-javascript": "^6.0.1", - "smob": "^1.0.0", - "terser": "^5.17.4" - }, - "engines": { - "node": ">=14.0.0" - }, - "peerDependencies": { - "rollup": "^2.0.0||^3.0.0||^4.0.0" - }, - "peerDependenciesMeta": { - "rollup": { - "optional": true - } - } - }, - "node_modules/@rollup/pluginutils": { - "version": "5.2.0", - "resolved": "https://registry.npmjs.org/@rollup/pluginutils/-/pluginutils-5.2.0.tgz", - "integrity": "sha512-qWJ2ZTbmumwiLFomfzTyt5Kng4hwPi9rwCYN4SHb6eaRU1KNO4ccxINHr/VhH4GgPlt1XfSTLX2LBTme8ne4Zw==", - "dev": true, - "license": "MIT", - "dependencies": { - "@types/estree": "^1.0.0", - "estree-walker": "^2.0.2", - "picomatch": "^4.0.2" - }, - "engines": { - "node": ">=14.0.0" - }, - "peerDependencies": { - "rollup": "^1.20.0||^2.0.0||^3.0.0||^4.0.0" - }, - "peerDependenciesMeta": { - "rollup": { - "optional": true - } - } - }, - "node_modules/@rollup/rollup-android-arm-eabi": { - "version": "4.45.1", - "resolved": "https://registry.npmjs.org/@rollup/rollup-android-arm-eabi/-/rollup-android-arm-eabi-4.45.1.tgz", - "integrity": "sha512-NEySIFvMY0ZQO+utJkgoMiCAjMrGvnbDLHvcmlA33UXJpYBCvlBEbMMtV837uCkS+plG2umfhn0T5mMAxGrlRA==", - "cpu": [ - "arm" - ], - "dev": true, - "license": "MIT", - "optional": true, - "os": [ - "android" - ] - }, - "node_modules/@rollup/rollup-android-arm64": { - "version": "4.45.1", - "resolved": "https://registry.npmjs.org/@rollup/rollup-android-arm64/-/rollup-android-arm64-4.45.1.tgz", - "integrity": "sha512-ujQ+sMXJkg4LRJaYreaVx7Z/VMgBBd89wGS4qMrdtfUFZ+TSY5Rs9asgjitLwzeIbhwdEhyj29zhst3L1lKsRQ==", - "cpu": [ - "arm64" - ], - "dev": true, - "license": "MIT", - "optional": true, - "os": [ - "android" - ] - }, - "node_modules/@rollup/rollup-darwin-arm64": { - "version": "4.45.1", - "resolved": "https://registry.npmjs.org/@rollup/rollup-darwin-arm64/-/rollup-darwin-arm64-4.45.1.tgz", - "integrity": "sha512-FSncqHvqTm3lC6Y13xncsdOYfxGSLnP+73k815EfNmpewPs+EyM49haPS105Rh4aF5mJKywk9X0ogzLXZzN9lA==", - "cpu": [ - "arm64" - ], - "dev": true, - "license": "MIT", - "optional": true, - "os": [ - "darwin" - ] - }, - "node_modules/@rollup/rollup-darwin-x64": { - "version": "4.45.1", - "resolved": "https://registry.npmjs.org/@rollup/rollup-darwin-x64/-/rollup-darwin-x64-4.45.1.tgz", - "integrity": "sha512-2/vVn/husP5XI7Fsf/RlhDaQJ7x9zjvC81anIVbr4b/f0xtSmXQTFcGIQ/B1cXIYM6h2nAhJkdMHTnD7OtQ9Og==", - "cpu": [ - "x64" - ], - "dev": true, - "license": "MIT", - "optional": true, - "os": [ - "darwin" - ] - }, - "node_modules/@rollup/rollup-freebsd-arm64": { - "version": "4.45.1", - "resolved": "https://registry.npmjs.org/@rollup/rollup-freebsd-arm64/-/rollup-freebsd-arm64-4.45.1.tgz", - "integrity": "sha512-4g1kaDxQItZsrkVTdYQ0bxu4ZIQ32cotoQbmsAnW1jAE4XCMbcBPDirX5fyUzdhVCKgPcrwWuucI8yrVRBw2+g==", - "cpu": [ - "arm64" - ], - "dev": true, - "license": "MIT", - "optional": true, - "os": [ - "freebsd" - ] - }, - "node_modules/@rollup/rollup-freebsd-x64": { - "version": "4.45.1", - "resolved": "https://registry.npmjs.org/@rollup/rollup-freebsd-x64/-/rollup-freebsd-x64-4.45.1.tgz", - "integrity": "sha512-L/6JsfiL74i3uK1Ti2ZFSNsp5NMiM4/kbbGEcOCps99aZx3g8SJMO1/9Y0n/qKlWZfn6sScf98lEOUe2mBvW9A==", - "cpu": [ - "x64" - ], - "dev": true, - "license": "MIT", - "optional": true, - "os": [ - "freebsd" - ] - }, - "node_modules/@rollup/rollup-linux-arm-gnueabihf": { - "version": "4.45.1", - "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-arm-gnueabihf/-/rollup-linux-arm-gnueabihf-4.45.1.tgz", - "integrity": "sha512-RkdOTu2jK7brlu+ZwjMIZfdV2sSYHK2qR08FUWcIoqJC2eywHbXr0L8T/pONFwkGukQqERDheaGTeedG+rra6Q==", - "cpu": [ - "arm" - ], - "dev": true, - "license": "MIT", - "optional": true, - "os": [ - "linux" - ] - }, - "node_modules/@rollup/rollup-linux-arm-musleabihf": { - "version": "4.45.1", - "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-arm-musleabihf/-/rollup-linux-arm-musleabihf-4.45.1.tgz", - "integrity": "sha512-3kJ8pgfBt6CIIr1o+HQA7OZ9mp/zDk3ctekGl9qn/pRBgrRgfwiffaUmqioUGN9hv0OHv2gxmvdKOkARCtRb8Q==", - "cpu": [ - "arm" - ], - "dev": true, - "license": "MIT", - "optional": true, - "os": [ - "linux" - ] - }, - "node_modules/@rollup/rollup-linux-arm64-gnu": { - "version": "4.45.1", - "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-arm64-gnu/-/rollup-linux-arm64-gnu-4.45.1.tgz", - "integrity": "sha512-k3dOKCfIVixWjG7OXTCOmDfJj3vbdhN0QYEqB+OuGArOChek22hn7Uy5A/gTDNAcCy5v2YcXRJ/Qcnm4/ma1xw==", - "cpu": [ - "arm64" - ], - "dev": true, - "license": "MIT", - "optional": true, - "os": [ - "linux" - ] - }, - "node_modules/@rollup/rollup-linux-arm64-musl": { - "version": "4.45.1", - "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-arm64-musl/-/rollup-linux-arm64-musl-4.45.1.tgz", - "integrity": "sha512-PmI1vxQetnM58ZmDFl9/Uk2lpBBby6B6rF4muJc65uZbxCs0EA7hhKCk2PKlmZKuyVSHAyIw3+/SiuMLxKxWog==", - "cpu": [ - "arm64" - ], - "dev": true, - "license": "MIT", - "optional": true, - "os": [ - "linux" - ] - }, - "node_modules/@rollup/rollup-linux-loongarch64-gnu": { - "version": "4.45.1", - "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-loongarch64-gnu/-/rollup-linux-loongarch64-gnu-4.45.1.tgz", - "integrity": "sha512-9UmI0VzGmNJ28ibHW2GpE2nF0PBQqsyiS4kcJ5vK+wuwGnV5RlqdczVocDSUfGX/Na7/XINRVoUgJyFIgipoRg==", - "cpu": [ - "loong64" - ], - "dev": true, - "license": "MIT", - "optional": true, - "os": [ - "linux" - ] - }, - "node_modules/@rollup/rollup-linux-powerpc64le-gnu": { - "version": "4.45.1", - "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-powerpc64le-gnu/-/rollup-linux-powerpc64le-gnu-4.45.1.tgz", - "integrity": "sha512-7nR2KY8oEOUTD3pBAxIBBbZr0U7U+R9HDTPNy+5nVVHDXI4ikYniH1oxQz9VoB5PbBU1CZuDGHkLJkd3zLMWsg==", - "cpu": [ - "ppc64" - ], - "dev": true, - "license": "MIT", - "optional": true, - "os": [ - "linux" - ] - }, - "node_modules/@rollup/rollup-linux-riscv64-gnu": { - "version": "4.45.1", - "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-riscv64-gnu/-/rollup-linux-riscv64-gnu-4.45.1.tgz", - "integrity": "sha512-nlcl3jgUultKROfZijKjRQLUu9Ma0PeNv/VFHkZiKbXTBQXhpytS8CIj5/NfBeECZtY2FJQubm6ltIxm/ftxpw==", - "cpu": [ - "riscv64" - ], - "dev": true, - "license": "MIT", - "optional": true, - "os": [ - "linux" - ] - }, - "node_modules/@rollup/rollup-linux-riscv64-musl": { - "version": "4.45.1", - "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-riscv64-musl/-/rollup-linux-riscv64-musl-4.45.1.tgz", - "integrity": "sha512-HJV65KLS51rW0VY6rvZkiieiBnurSzpzore1bMKAhunQiECPuxsROvyeaot/tcK3A3aGnI+qTHqisrpSgQrpgA==", - "cpu": [ - "riscv64" - ], - "dev": true, - "license": "MIT", - "optional": true, - "os": [ - "linux" - ] - }, - "node_modules/@rollup/rollup-linux-s390x-gnu": { - "version": "4.45.1", - "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-s390x-gnu/-/rollup-linux-s390x-gnu-4.45.1.tgz", - "integrity": "sha512-NITBOCv3Qqc6hhwFt7jLV78VEO/il4YcBzoMGGNxznLgRQf43VQDae0aAzKiBeEPIxnDrACiMgbqjuihx08OOw==", - "cpu": [ - "s390x" - ], - "dev": true, - "license": "MIT", - "optional": true, - "os": [ - "linux" - ] - }, - "node_modules/@rollup/rollup-linux-x64-gnu": { - "version": "4.45.1", - "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-x64-gnu/-/rollup-linux-x64-gnu-4.45.1.tgz", - "integrity": "sha512-+E/lYl6qu1zqgPEnTrs4WysQtvc/Sh4fC2nByfFExqgYrqkKWp1tWIbe+ELhixnenSpBbLXNi6vbEEJ8M7fiHw==", - "cpu": [ - "x64" - ], - "dev": true, - "license": "MIT", - "optional": true, - "os": [ - "linux" - ] - }, - "node_modules/@rollup/rollup-linux-x64-musl": { - "version": "4.45.1", - "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-x64-musl/-/rollup-linux-x64-musl-4.45.1.tgz", - "integrity": "sha512-a6WIAp89p3kpNoYStITT9RbTbTnqarU7D8N8F2CV+4Cl9fwCOZraLVuVFvlpsW0SbIiYtEnhCZBPLoNdRkjQFw==", - "cpu": [ - "x64" - ], - "dev": true, - "license": "MIT", - "optional": true, - "os": [ - "linux" - ] - }, - "node_modules/@rollup/rollup-win32-arm64-msvc": { - "version": "4.45.1", - "resolved": "https://registry.npmjs.org/@rollup/rollup-win32-arm64-msvc/-/rollup-win32-arm64-msvc-4.45.1.tgz", - "integrity": "sha512-T5Bi/NS3fQiJeYdGvRpTAP5P02kqSOpqiopwhj0uaXB6nzs5JVi2XMJb18JUSKhCOX8+UE1UKQufyD6Or48dJg==", - "cpu": [ - "arm64" - ], - "dev": true, - "license": "MIT", - "optional": true, - "os": [ - "win32" - ] - }, - "node_modules/@rollup/rollup-win32-ia32-msvc": { - "version": "4.45.1", - "resolved": "https://registry.npmjs.org/@rollup/rollup-win32-ia32-msvc/-/rollup-win32-ia32-msvc-4.45.1.tgz", - "integrity": "sha512-lxV2Pako3ujjuUe9jiU3/s7KSrDfH6IgTSQOnDWr9aJ92YsFd7EurmClK0ly/t8dzMkDtd04g60WX6yl0sGfdw==", - "cpu": [ - "ia32" - ], - "dev": true, - "license": "MIT", - "optional": true, - "os": [ - "win32" - ] - }, - "node_modules/@rollup/rollup-win32-x64-msvc": { - "version": "4.45.1", - "resolved": "https://registry.npmjs.org/@rollup/rollup-win32-x64-msvc/-/rollup-win32-x64-msvc-4.45.1.tgz", - "integrity": "sha512-M/fKi4sasCdM8i0aWJjCSFm2qEnYRR8AMLG2kxp6wD13+tMGA4Z1tVAuHkNRjud5SW2EM3naLuK35w9twvf6aA==", - "cpu": [ - "x64" - ], - "dev": true, - "license": "MIT", - "optional": true, - "os": [ - "win32" - ] - }, - "node_modules/@sinclair/typebox": { - "version": "0.34.38", - "resolved": "https://registry.npmjs.org/@sinclair/typebox/-/typebox-0.34.38.tgz", - "integrity": "sha512-HpkxMmc2XmZKhvaKIZZThlHmx1L0I/V1hWK1NubtlFnr6ZqdiOpV72TKudZUNQjZNsyDBay72qFEhEvb+bcwcA==", - "dev": true, - "license": "MIT" - }, - "node_modules/@sinonjs/commons": { - "version": "3.0.1", - "resolved": "https://registry.npmjs.org/@sinonjs/commons/-/commons-3.0.1.tgz", - "integrity": "sha512-K3mCHKQ9sVh8o1C9cxkwxaOmXoAMlDxC1mYyHrjqOWEcBjYr76t96zL2zlj5dUGZ3HSw240X1qgH3Mjf1yJWpQ==", - "dev": true, - "license": "BSD-3-Clause", - "dependencies": { - "type-detect": "4.0.8" - } - }, - "node_modules/@sinonjs/fake-timers": { - "version": "13.0.5", - "resolved": "https://registry.npmjs.org/@sinonjs/fake-timers/-/fake-timers-13.0.5.tgz", - "integrity": "sha512-36/hTbH2uaWuGVERyC6da9YwGWnzUZXuPro/F2LfsdOsLnCojz/iSH8MxUt/FD2S5XBSVPhmArFUXcpCQ2Hkiw==", - "dev": true, - "license": "BSD-3-Clause", - "dependencies": { - "@sinonjs/commons": "^3.0.1" - } - }, - "node_modules/@tybys/wasm-util": { - "version": "0.10.0", - "resolved": "https://registry.npmjs.org/@tybys/wasm-util/-/wasm-util-0.10.0.tgz", - "integrity": "sha512-VyyPYFlOMNylG45GoAe0xDoLwWuowvf92F9kySqzYh8vmYm7D2u4iUJKa1tOUpS70Ku13ASrOkS4ScXFsTaCNQ==", - "dev": true, - "license": "MIT", - "optional": true, - "dependencies": { - "tslib": "^2.4.0" - } - }, - "node_modules/@types/babel__core": { - "version": "7.20.5", - "resolved": "https://registry.npmjs.org/@types/babel__core/-/babel__core-7.20.5.tgz", - "integrity": "sha512-qoQprZvz5wQFJwMDqeseRXWv3rqMvhgpbXFfVyWhbx9X47POIA6i/+dXefEmZKoAgOaTdaIgNSMqMIU61yRyzA==", - "dev": true, - "license": "MIT", - "dependencies": { - "@babel/parser": "^7.20.7", - "@babel/types": "^7.20.7", - "@types/babel__generator": "*", - "@types/babel__template": "*", - "@types/babel__traverse": "*" - } - }, - "node_modules/@types/babel__generator": { - "version": "7.27.0", - "resolved": "https://registry.npmjs.org/@types/babel__generator/-/babel__generator-7.27.0.tgz", - "integrity": "sha512-ufFd2Xi92OAVPYsy+P4n7/U7e68fex0+Ee8gSG9KX7eo084CWiQ4sdxktvdl0bOPupXtVJPY19zk6EwWqUQ8lg==", - "dev": true, - "license": "MIT", - "dependencies": { - "@babel/types": "^7.0.0" - } - }, - "node_modules/@types/babel__template": { - "version": "7.4.4", - "resolved": "https://registry.npmjs.org/@types/babel__template/-/babel__template-7.4.4.tgz", - "integrity": "sha512-h/NUaSyG5EyxBIp8YRxo4RMe2/qQgvyowRwVMzhYhBCONbW8PUsg4lkFMrhgZhUe5z3L3MiLDuvyJ/CaPa2A8A==", - "dev": true, - "license": "MIT", - "dependencies": { - "@babel/parser": "^7.1.0", - "@babel/types": "^7.0.0" - } - }, - "node_modules/@types/babel__traverse": { - "version": "7.20.7", - "resolved": "https://registry.npmjs.org/@types/babel__traverse/-/babel__traverse-7.20.7.tgz", - "integrity": "sha512-dkO5fhS7+/oos4ciWxyEyjWe48zmG6wbCheo/G2ZnHx4fs3EU6YC6UM8rk56gAjNJ9P3MTH2jo5jb92/K6wbng==", - "dev": true, - "license": "MIT", - "dependencies": { - "@babel/types": "^7.20.7" - } - }, - "node_modules/@types/estree": { - "version": "1.0.8", - "resolved": "https://registry.npmjs.org/@types/estree/-/estree-1.0.8.tgz", - "integrity": "sha512-dWHzHa2WqEXI/O1E9OjrocMTKJl2mSrEolh1Iomrv6U+JuNwaHXsXx9bLu5gG7BUWFIN0skIQJQ/L1rIex4X6w==", - "dev": true, - "license": "MIT" - }, - "node_modules/@types/istanbul-lib-coverage": { - "version": "2.0.6", - "resolved": "https://registry.npmjs.org/@types/istanbul-lib-coverage/-/istanbul-lib-coverage-2.0.6.tgz", - "integrity": "sha512-2QF/t/auWm0lsy8XtKVPG19v3sSOQlJe/YHZgfjb/KBBHOGSV+J2q/S671rcq9uTBrLAXmZpqJiaQbMT+zNU1w==", - "dev": true, - "license": "MIT" - }, - "node_modules/@types/istanbul-lib-report": { - "version": "3.0.3", - "resolved": "https://registry.npmjs.org/@types/istanbul-lib-report/-/istanbul-lib-report-3.0.3.tgz", - "integrity": "sha512-NQn7AHQnk/RSLOxrBbGyJM/aVQ+pjj5HCgasFxc0K/KhoATfQ/47AyUl15I2yBUpihjmas+a+VJBOqecrFH+uA==", - "dev": true, - "license": "MIT", - "dependencies": { - "@types/istanbul-lib-coverage": "*" - } - }, - "node_modules/@types/istanbul-reports": { - "version": "3.0.4", - "resolved": "https://registry.npmjs.org/@types/istanbul-reports/-/istanbul-reports-3.0.4.tgz", - "integrity": "sha512-pk2B1NWalF9toCRu6gjBzR69syFjP4Od8WRAX+0mmf9lAjCRicLOWc+ZrxZHx/0XRjotgkF9t6iaMJ+aXcOdZQ==", - "dev": true, - "license": "MIT", - "dependencies": { - "@types/istanbul-lib-report": "*" - } - }, - "node_modules/@types/jest": { - "version": "30.0.0", - "resolved": "https://registry.npmjs.org/@types/jest/-/jest-30.0.0.tgz", - "integrity": "sha512-XTYugzhuwqWjws0CVz8QpM36+T+Dz5mTEBKhNs/esGLnCIlGdRy+Dq78NRjd7ls7r8BC8ZRMOrKlkO1hU0JOwA==", - "dev": true, - "license": "MIT", - "dependencies": { - "expect": "^30.0.0", - "pretty-format": "^30.0.0" - } - }, - "node_modules/@types/json-schema": { - "version": "7.0.15", - "resolved": "https://registry.npmjs.org/@types/json-schema/-/json-schema-7.0.15.tgz", - "integrity": "sha512-5+fP8P8MFNC+AyZCDxrB2pkZFPGzqQWUzpSeuuVLvm8VMcorNYavBqoFcxK8bQz4Qsbn4oUEEem4wDLfcysGHA==", - "dev": true, - "license": "MIT" - }, - "node_modules/@types/node": { - "version": "24.1.0", - "resolved": "https://registry.npmjs.org/@types/node/-/node-24.1.0.tgz", - "integrity": "sha512-ut5FthK5moxFKH2T1CUOC6ctR67rQRvvHdFLCD2Ql6KXmMuCrjsSsRI9UsLCm9M18BMwClv4pn327UvB7eeO1w==", - "dev": true, - "license": "MIT", - "dependencies": { - "undici-types": "~7.8.0" - } - }, - "node_modules/@types/resolve": { - "version": "1.20.2", - "resolved": "https://registry.npmjs.org/@types/resolve/-/resolve-1.20.2.tgz", - "integrity": "sha512-60BCwRFOZCQhDncwQdxxeOEEkbc5dIMccYLwbxsS4TUNeVECQ/pBJ0j09mrHOl/JJvpRPGwO9SvE4nR2Nb/a4Q==", - "dev": true, - "license": "MIT" - }, - "node_modules/@types/stack-utils": { - "version": "2.0.3", - "resolved": "https://registry.npmjs.org/@types/stack-utils/-/stack-utils-2.0.3.tgz", - "integrity": "sha512-9aEbYZ3TbYMznPdcdr3SmIrLXwC/AKZXQeCf9Pgao5CKb8CyHuEX5jzWPTkvregvhRJHcpRO6BFoGW9ycaOkYw==", - "dev": true, - "license": "MIT" - }, - "node_modules/@types/yargs": { - "version": "17.0.33", - "resolved": "https://registry.npmjs.org/@types/yargs/-/yargs-17.0.33.tgz", - "integrity": "sha512-WpxBCKWPLr4xSsHgz511rFJAM+wS28w2zEO1QDNY5zM/S8ok70NNfztH0xwhqKyaK0OHCbN98LDAZuy1ctxDkA==", - "dev": true, - "license": "MIT", - "dependencies": { - "@types/yargs-parser": "*" - } - }, - "node_modules/@types/yargs-parser": { - "version": "21.0.3", - "resolved": "https://registry.npmjs.org/@types/yargs-parser/-/yargs-parser-21.0.3.tgz", - "integrity": "sha512-I4q9QU9MQv4oEOz4tAHJtNz1cwuLxn2F3xcc2iV5WdqLPpUnj30aUuxt1mAxYTG+oe8CZMV/+6rU4S4gRDzqtQ==", - "dev": true, - "license": "MIT" - }, - "node_modules/@typescript-eslint/eslint-plugin": { - "version": "8.38.0", - "resolved": "https://registry.npmjs.org/@typescript-eslint/eslint-plugin/-/eslint-plugin-8.38.0.tgz", - "integrity": "sha512-CPoznzpuAnIOl4nhj4tRr4gIPj5AfKgkiJmGQDaq+fQnRJTYlcBjbX3wbciGmpoPf8DREufuPRe1tNMZnGdanA==", - "dev": true, - "license": "MIT", - "dependencies": { - "@eslint-community/regexpp": "^4.10.0", - "@typescript-eslint/scope-manager": "8.38.0", - "@typescript-eslint/type-utils": "8.38.0", - "@typescript-eslint/utils": "8.38.0", - "@typescript-eslint/visitor-keys": "8.38.0", - "graphemer": "^1.4.0", - "ignore": "^7.0.0", - "natural-compare": "^1.4.0", - "ts-api-utils": "^2.1.0" - }, - "engines": { - "node": "^18.18.0 || ^20.9.0 || >=21.1.0" - }, - "funding": { - "type": "opencollective", - "url": "https://opencollective.com/typescript-eslint" - }, - "peerDependencies": { - "@typescript-eslint/parser": "^8.38.0", - "eslint": "^8.57.0 || ^9.0.0", - "typescript": ">=4.8.4 <5.9.0" - } - }, - "node_modules/@typescript-eslint/parser": { - "version": "8.38.0", - "resolved": "https://registry.npmjs.org/@typescript-eslint/parser/-/parser-8.38.0.tgz", - "integrity": "sha512-Zhy8HCvBUEfBECzIl1PKqF4p11+d0aUJS1GeUiuqK9WmOug8YCmC4h4bjyBvMyAMI9sbRczmrYL5lKg/YMbrcQ==", - "dev": true, - "license": "MIT", - "dependencies": { - "@typescript-eslint/scope-manager": "8.38.0", - "@typescript-eslint/types": "8.38.0", - "@typescript-eslint/typescript-estree": "8.38.0", - "@typescript-eslint/visitor-keys": "8.38.0", - "debug": "^4.3.4" - }, - "engines": { - "node": "^18.18.0 || ^20.9.0 || >=21.1.0" - }, - "funding": { - "type": "opencollective", - "url": "https://opencollective.com/typescript-eslint" - }, - "peerDependencies": { - "eslint": "^8.57.0 || ^9.0.0", - "typescript": ">=4.8.4 <5.9.0" - } - }, - "node_modules/@typescript-eslint/project-service": { - "version": "8.38.0", - "resolved": "https://registry.npmjs.org/@typescript-eslint/project-service/-/project-service-8.38.0.tgz", - "integrity": "sha512-dbK7Jvqcb8c9QfH01YB6pORpqX1mn5gDZc9n63Ak/+jD67oWXn3Gs0M6vddAN+eDXBCS5EmNWzbSxsn9SzFWWg==", - "dev": true, - "license": "MIT", - "dependencies": { - "@typescript-eslint/tsconfig-utils": "^8.38.0", - "@typescript-eslint/types": "^8.38.0", - "debug": "^4.3.4" - }, - "engines": { - "node": "^18.18.0 || ^20.9.0 || >=21.1.0" - }, - "funding": { - "type": "opencollective", - "url": "https://opencollective.com/typescript-eslint" - }, - "peerDependencies": { - "typescript": ">=4.8.4 <5.9.0" - } - }, - "node_modules/@typescript-eslint/scope-manager": { - "version": "8.38.0", - "resolved": "https://registry.npmjs.org/@typescript-eslint/scope-manager/-/scope-manager-8.38.0.tgz", - "integrity": "sha512-WJw3AVlFFcdT9Ri1xs/lg8LwDqgekWXWhH3iAF+1ZM+QPd7oxQ6jvtW/JPwzAScxitILUIFs0/AnQ/UWHzbATQ==", - "dev": true, - "license": "MIT", - "dependencies": { - "@typescript-eslint/types": "8.38.0", - "@typescript-eslint/visitor-keys": "8.38.0" - }, - "engines": { - "node": "^18.18.0 || ^20.9.0 || >=21.1.0" - }, - "funding": { - "type": "opencollective", - "url": "https://opencollective.com/typescript-eslint" - } - }, - "node_modules/@typescript-eslint/tsconfig-utils": { - "version": "8.38.0", - "resolved": "https://registry.npmjs.org/@typescript-eslint/tsconfig-utils/-/tsconfig-utils-8.38.0.tgz", - "integrity": "sha512-Lum9RtSE3EroKk/bYns+sPOodqb2Fv50XOl/gMviMKNvanETUuUcC9ObRbzrJ4VSd2JalPqgSAavwrPiPvnAiQ==", - "dev": true, - "license": "MIT", - "engines": { - "node": "^18.18.0 || ^20.9.0 || >=21.1.0" - }, - "funding": { - "type": "opencollective", - "url": "https://opencollective.com/typescript-eslint" - }, - "peerDependencies": { - "typescript": ">=4.8.4 <5.9.0" - } - }, - "node_modules/@typescript-eslint/type-utils": { - "version": "8.38.0", - "resolved": "https://registry.npmjs.org/@typescript-eslint/type-utils/-/type-utils-8.38.0.tgz", - "integrity": "sha512-c7jAvGEZVf0ao2z+nnz8BUaHZD09Agbh+DY7qvBQqLiz8uJzRgVPj5YvOh8I8uEiH8oIUGIfHzMwUcGVco/SJg==", - "dev": true, - "license": "MIT", - "dependencies": { - "@typescript-eslint/types": "8.38.0", - "@typescript-eslint/typescript-estree": "8.38.0", - "@typescript-eslint/utils": "8.38.0", - "debug": "^4.3.4", - "ts-api-utils": "^2.1.0" - }, - "engines": { - "node": "^18.18.0 || ^20.9.0 || >=21.1.0" - }, - "funding": { - "type": "opencollective", - "url": "https://opencollective.com/typescript-eslint" - }, - "peerDependencies": { - "eslint": "^8.57.0 || ^9.0.0", - "typescript": ">=4.8.4 <5.9.0" - } - }, - "node_modules/@typescript-eslint/types": { - "version": "8.38.0", - "resolved": "https://registry.npmjs.org/@typescript-eslint/types/-/types-8.38.0.tgz", - "integrity": "sha512-wzkUfX3plUqij4YwWaJyqhiPE5UCRVlFpKn1oCRn2O1bJ592XxWJj8ROQ3JD5MYXLORW84063z3tZTb/cs4Tyw==", - "dev": true, - "license": "MIT", - "engines": { - "node": "^18.18.0 || ^20.9.0 || >=21.1.0" - }, - "funding": { - "type": "opencollective", - "url": "https://opencollective.com/typescript-eslint" - } - }, - "node_modules/@typescript-eslint/typescript-estree": { - "version": "8.38.0", - "resolved": "https://registry.npmjs.org/@typescript-eslint/typescript-estree/-/typescript-estree-8.38.0.tgz", - "integrity": "sha512-fooELKcAKzxux6fA6pxOflpNS0jc+nOQEEOipXFNjSlBS6fqrJOVY/whSn70SScHrcJ2LDsxWrneFoWYSVfqhQ==", - "dev": true, - "license": "MIT", - "dependencies": { - "@typescript-eslint/project-service": "8.38.0", - "@typescript-eslint/tsconfig-utils": "8.38.0", - "@typescript-eslint/types": "8.38.0", - "@typescript-eslint/visitor-keys": "8.38.0", - "debug": "^4.3.4", - "fast-glob": "^3.3.2", - "is-glob": "^4.0.3", - "minimatch": "^9.0.4", - "semver": "^7.6.0", - "ts-api-utils": "^2.1.0" - }, - "engines": { - "node": "^18.18.0 || ^20.9.0 || >=21.1.0" - }, - "funding": { - "type": "opencollective", - "url": "https://opencollective.com/typescript-eslint" - }, - "peerDependencies": { - "typescript": ">=4.8.4 <5.9.0" - } - }, - "node_modules/@typescript-eslint/typescript-estree/node_modules/semver": { - "version": "7.7.2", - "resolved": "https://registry.npmjs.org/semver/-/semver-7.7.2.tgz", - "integrity": "sha512-RF0Fw+rO5AMf9MAyaRXI4AV0Ulj5lMHqVxxdSgiVbixSCXoEmmX/jk0CuJw4+3SqroYO9VoUh+HcuJivvtJemA==", - "dev": true, - "license": "ISC", - "bin": { - "semver": "bin/semver.js" - }, - "engines": { - "node": ">=10" - } - }, - "node_modules/@typescript-eslint/utils": { - "version": "8.38.0", - "resolved": "https://registry.npmjs.org/@typescript-eslint/utils/-/utils-8.38.0.tgz", - "integrity": "sha512-hHcMA86Hgt+ijJlrD8fX0j1j8w4C92zue/8LOPAFioIno+W0+L7KqE8QZKCcPGc/92Vs9x36w/4MPTJhqXdyvg==", - "dev": true, - "license": "MIT", - "dependencies": { - "@eslint-community/eslint-utils": "^4.7.0", - "@typescript-eslint/scope-manager": "8.38.0", - "@typescript-eslint/types": "8.38.0", - "@typescript-eslint/typescript-estree": "8.38.0" - }, - "engines": { - "node": "^18.18.0 || ^20.9.0 || >=21.1.0" - }, - "funding": { - "type": "opencollective", - "url": "https://opencollective.com/typescript-eslint" - }, - "peerDependencies": { - "eslint": "^8.57.0 || ^9.0.0", - "typescript": ">=4.8.4 <5.9.0" - } - }, - "node_modules/@typescript-eslint/visitor-keys": { - "version": "8.38.0", - "resolved": "https://registry.npmjs.org/@typescript-eslint/visitor-keys/-/visitor-keys-8.38.0.tgz", - "integrity": "sha512-pWrTcoFNWuwHlA9CvlfSsGWs14JxfN1TH25zM5L7o0pRLhsoZkDnTsXfQRJBEWJoV5DL0jf+Z+sxiud+K0mq1g==", - "dev": true, - "license": "MIT", - "dependencies": { - "@typescript-eslint/types": "8.38.0", - "eslint-visitor-keys": "^4.2.1" - }, - "engines": { - "node": "^18.18.0 || ^20.9.0 || >=21.1.0" - }, - "funding": { - "type": "opencollective", - "url": "https://opencollective.com/typescript-eslint" - } - }, - "node_modules/@typescript-eslint/visitor-keys/node_modules/eslint-visitor-keys": { - "version": "4.2.1", - "resolved": "https://registry.npmjs.org/eslint-visitor-keys/-/eslint-visitor-keys-4.2.1.tgz", - "integrity": "sha512-Uhdk5sfqcee/9H/rCOJikYz67o0a2Tw2hGRPOG2Y1R2dg7brRe1uG0yaNQDHu+TO/uQPF/5eCapvYSmHUjt7JQ==", - "dev": true, - "license": "Apache-2.0", - "engines": { - "node": "^18.18.0 || ^20.9.0 || >=21.1.0" - }, - "funding": { - "url": "https://opencollective.com/eslint" - } - }, - "node_modules/@ungap/structured-clone": { - "version": "1.3.0", - "resolved": "https://registry.npmjs.org/@ungap/structured-clone/-/structured-clone-1.3.0.tgz", - "integrity": "sha512-WmoN8qaIAo7WTYWbAZuG8PYEhn5fkz7dZrqTBZ7dtt//lL2Gwms1IcnQ5yHqjDfX8Ft5j4YzDM23f87zBfDe9g==", - "dev": true, - "license": "ISC" - }, - "node_modules/@unrs/resolver-binding-android-arm-eabi": { - "version": "1.11.1", - "resolved": "https://registry.npmjs.org/@unrs/resolver-binding-android-arm-eabi/-/resolver-binding-android-arm-eabi-1.11.1.tgz", - "integrity": "sha512-ppLRUgHVaGRWUx0R0Ut06Mjo9gBaBkg3v/8AxusGLhsIotbBLuRk51rAzqLC8gq6NyyAojEXglNjzf6R948DNw==", - "cpu": [ - "arm" - ], - "dev": true, - "license": "MIT", - "optional": true, - "os": [ - "android" - ] - }, - "node_modules/@unrs/resolver-binding-android-arm64": { - "version": "1.11.1", - "resolved": "https://registry.npmjs.org/@unrs/resolver-binding-android-arm64/-/resolver-binding-android-arm64-1.11.1.tgz", - "integrity": "sha512-lCxkVtb4wp1v+EoN+HjIG9cIIzPkX5OtM03pQYkG+U5O/wL53LC4QbIeazgiKqluGeVEeBlZahHalCaBvU1a2g==", - "cpu": [ - "arm64" - ], - "dev": true, - "license": "MIT", - "optional": true, - "os": [ - "android" - ] - }, - "node_modules/@unrs/resolver-binding-darwin-arm64": { - "version": "1.11.1", - "resolved": "https://registry.npmjs.org/@unrs/resolver-binding-darwin-arm64/-/resolver-binding-darwin-arm64-1.11.1.tgz", - "integrity": "sha512-gPVA1UjRu1Y/IsB/dQEsp2V1pm44Of6+LWvbLc9SDk1c2KhhDRDBUkQCYVWe6f26uJb3fOK8saWMgtX8IrMk3g==", - "cpu": [ - "arm64" - ], - "dev": true, - "license": "MIT", - "optional": true, - "os": [ - "darwin" - ] - }, - "node_modules/@unrs/resolver-binding-darwin-x64": { - "version": "1.11.1", - "resolved": "https://registry.npmjs.org/@unrs/resolver-binding-darwin-x64/-/resolver-binding-darwin-x64-1.11.1.tgz", - "integrity": "sha512-cFzP7rWKd3lZaCsDze07QX1SC24lO8mPty9vdP+YVa3MGdVgPmFc59317b2ioXtgCMKGiCLxJ4HQs62oz6GfRQ==", - "cpu": [ - "x64" - ], - "dev": true, - "license": "MIT", - "optional": true, - "os": [ - "darwin" - ] - }, - "node_modules/@unrs/resolver-binding-freebsd-x64": { - "version": "1.11.1", - "resolved": "https://registry.npmjs.org/@unrs/resolver-binding-freebsd-x64/-/resolver-binding-freebsd-x64-1.11.1.tgz", - "integrity": "sha512-fqtGgak3zX4DCB6PFpsH5+Kmt/8CIi4Bry4rb1ho6Av2QHTREM+47y282Uqiu3ZRF5IQioJQ5qWRV6jduA+iGw==", - "cpu": [ - "x64" - ], - "dev": true, - "license": "MIT", - "optional": true, - "os": [ - "freebsd" - ] - }, - "node_modules/@unrs/resolver-binding-linux-arm-gnueabihf": { - "version": "1.11.1", - "resolved": "https://registry.npmjs.org/@unrs/resolver-binding-linux-arm-gnueabihf/-/resolver-binding-linux-arm-gnueabihf-1.11.1.tgz", - "integrity": "sha512-u92mvlcYtp9MRKmP+ZvMmtPN34+/3lMHlyMj7wXJDeXxuM0Vgzz0+PPJNsro1m3IZPYChIkn944wW8TYgGKFHw==", - "cpu": [ - "arm" - ], - "dev": true, - "license": "MIT", - "optional": true, - "os": [ - "linux" - ] - }, - "node_modules/@unrs/resolver-binding-linux-arm-musleabihf": { - "version": "1.11.1", - "resolved": "https://registry.npmjs.org/@unrs/resolver-binding-linux-arm-musleabihf/-/resolver-binding-linux-arm-musleabihf-1.11.1.tgz", - "integrity": "sha512-cINaoY2z7LVCrfHkIcmvj7osTOtm6VVT16b5oQdS4beibX2SYBwgYLmqhBjA1t51CarSaBuX5YNsWLjsqfW5Cw==", - "cpu": [ - "arm" - ], - "dev": true, - "license": "MIT", - "optional": true, - "os": [ - "linux" - ] - }, - "node_modules/@unrs/resolver-binding-linux-arm64-gnu": { - "version": "1.11.1", - "resolved": "https://registry.npmjs.org/@unrs/resolver-binding-linux-arm64-gnu/-/resolver-binding-linux-arm64-gnu-1.11.1.tgz", - "integrity": "sha512-34gw7PjDGB9JgePJEmhEqBhWvCiiWCuXsL9hYphDF7crW7UgI05gyBAi6MF58uGcMOiOqSJ2ybEeCvHcq0BCmQ==", - "cpu": [ - "arm64" - ], - "dev": true, - "license": "MIT", - "optional": true, - "os": [ - "linux" - ] - }, - "node_modules/@unrs/resolver-binding-linux-arm64-musl": { - "version": "1.11.1", - "resolved": "https://registry.npmjs.org/@unrs/resolver-binding-linux-arm64-musl/-/resolver-binding-linux-arm64-musl-1.11.1.tgz", - "integrity": "sha512-RyMIx6Uf53hhOtJDIamSbTskA99sPHS96wxVE/bJtePJJtpdKGXO1wY90oRdXuYOGOTuqjT8ACccMc4K6QmT3w==", - "cpu": [ - "arm64" - ], - "dev": true, - "license": "MIT", - "optional": true, - "os": [ - "linux" - ] - }, - "node_modules/@unrs/resolver-binding-linux-ppc64-gnu": { - "version": "1.11.1", - "resolved": "https://registry.npmjs.org/@unrs/resolver-binding-linux-ppc64-gnu/-/resolver-binding-linux-ppc64-gnu-1.11.1.tgz", - "integrity": "sha512-D8Vae74A4/a+mZH0FbOkFJL9DSK2R6TFPC9M+jCWYia/q2einCubX10pecpDiTmkJVUH+y8K3BZClycD8nCShA==", - "cpu": [ - "ppc64" - ], - "dev": true, - "license": "MIT", - "optional": true, - "os": [ - "linux" - ] - }, - "node_modules/@unrs/resolver-binding-linux-riscv64-gnu": { - "version": "1.11.1", - "resolved": "https://registry.npmjs.org/@unrs/resolver-binding-linux-riscv64-gnu/-/resolver-binding-linux-riscv64-gnu-1.11.1.tgz", - "integrity": "sha512-frxL4OrzOWVVsOc96+V3aqTIQl1O2TjgExV4EKgRY09AJ9leZpEg8Ak9phadbuX0BA4k8U5qtvMSQQGGmaJqcQ==", - "cpu": [ - "riscv64" - ], - "dev": true, - "license": "MIT", - "optional": true, - "os": [ - "linux" - ] - }, - "node_modules/@unrs/resolver-binding-linux-riscv64-musl": { - "version": "1.11.1", - "resolved": "https://registry.npmjs.org/@unrs/resolver-binding-linux-riscv64-musl/-/resolver-binding-linux-riscv64-musl-1.11.1.tgz", - "integrity": "sha512-mJ5vuDaIZ+l/acv01sHoXfpnyrNKOk/3aDoEdLO/Xtn9HuZlDD6jKxHlkN8ZhWyLJsRBxfv9GYM2utQ1SChKew==", - "cpu": [ - "riscv64" - ], - "dev": true, - "license": "MIT", - "optional": true, - "os": [ - "linux" - ] - }, - "node_modules/@unrs/resolver-binding-linux-s390x-gnu": { - "version": "1.11.1", - "resolved": "https://registry.npmjs.org/@unrs/resolver-binding-linux-s390x-gnu/-/resolver-binding-linux-s390x-gnu-1.11.1.tgz", - "integrity": "sha512-kELo8ebBVtb9sA7rMe1Cph4QHreByhaZ2QEADd9NzIQsYNQpt9UkM9iqr2lhGr5afh885d/cB5QeTXSbZHTYPg==", - "cpu": [ - "s390x" - ], - "dev": true, - "license": "MIT", - "optional": true, - "os": [ - "linux" - ] - }, - "node_modules/@unrs/resolver-binding-linux-x64-gnu": { - "version": "1.11.1", - "resolved": "https://registry.npmjs.org/@unrs/resolver-binding-linux-x64-gnu/-/resolver-binding-linux-x64-gnu-1.11.1.tgz", - "integrity": "sha512-C3ZAHugKgovV5YvAMsxhq0gtXuwESUKc5MhEtjBpLoHPLYM+iuwSj3lflFwK3DPm68660rZ7G8BMcwSro7hD5w==", - "cpu": [ - "x64" - ], - "dev": true, - "license": "MIT", - "optional": true, - "os": [ - "linux" - ] - }, - "node_modules/@unrs/resolver-binding-linux-x64-musl": { - "version": "1.11.1", - "resolved": "https://registry.npmjs.org/@unrs/resolver-binding-linux-x64-musl/-/resolver-binding-linux-x64-musl-1.11.1.tgz", - "integrity": "sha512-rV0YSoyhK2nZ4vEswT/QwqzqQXw5I6CjoaYMOX0TqBlWhojUf8P94mvI7nuJTeaCkkds3QE4+zS8Ko+GdXuZtA==", - "cpu": [ - "x64" - ], - "dev": true, - "license": "MIT", - "optional": true, - "os": [ - "linux" - ] - }, - "node_modules/@unrs/resolver-binding-wasm32-wasi": { - "version": "1.11.1", - "resolved": "https://registry.npmjs.org/@unrs/resolver-binding-wasm32-wasi/-/resolver-binding-wasm32-wasi-1.11.1.tgz", - "integrity": "sha512-5u4RkfxJm+Ng7IWgkzi3qrFOvLvQYnPBmjmZQ8+szTK/b31fQCnleNl1GgEt7nIsZRIf5PLhPwT0WM+q45x/UQ==", - "cpu": [ - "wasm32" - ], - "dev": true, - "license": "MIT", - "optional": true, - "dependencies": { - "@napi-rs/wasm-runtime": "^0.2.11" - }, - "engines": { - "node": ">=14.0.0" - } - }, - "node_modules/@unrs/resolver-binding-win32-arm64-msvc": { - "version": "1.11.1", - "resolved": "https://registry.npmjs.org/@unrs/resolver-binding-win32-arm64-msvc/-/resolver-binding-win32-arm64-msvc-1.11.1.tgz", - "integrity": "sha512-nRcz5Il4ln0kMhfL8S3hLkxI85BXs3o8EYoattsJNdsX4YUU89iOkVn7g0VHSRxFuVMdM4Q1jEpIId1Ihim/Uw==", - "cpu": [ - "arm64" - ], - "dev": true, - "license": "MIT", - "optional": true, - "os": [ - "win32" - ] - }, - "node_modules/@unrs/resolver-binding-win32-ia32-msvc": { - "version": "1.11.1", - "resolved": "https://registry.npmjs.org/@unrs/resolver-binding-win32-ia32-msvc/-/resolver-binding-win32-ia32-msvc-1.11.1.tgz", - "integrity": "sha512-DCEI6t5i1NmAZp6pFonpD5m7i6aFrpofcp4LA2i8IIq60Jyo28hamKBxNrZcyOwVOZkgsRp9O2sXWBWP8MnvIQ==", - "cpu": [ - "ia32" - ], - "dev": true, - "license": "MIT", - "optional": true, - "os": [ - "win32" - ] - }, - "node_modules/@unrs/resolver-binding-win32-x64-msvc": { - "version": "1.11.1", - "resolved": "https://registry.npmjs.org/@unrs/resolver-binding-win32-x64-msvc/-/resolver-binding-win32-x64-msvc-1.11.1.tgz", - "integrity": "sha512-lrW200hZdbfRtztbygyaq/6jP6AKE8qQN2KvPcJ+x7wiD038YtnYtZ82IMNJ69GJibV7bwL3y9FgK+5w/pYt6g==", - "cpu": [ - "x64" - ], - "dev": true, - "license": "MIT", - "optional": true, - "os": [ - "win32" - ] - }, - "node_modules/acorn": { - "version": "8.15.0", - "resolved": "https://registry.npmjs.org/acorn/-/acorn-8.15.0.tgz", - "integrity": "sha512-NZyJarBfL7nWwIq+FDL6Zp/yHEhePMNnnJ0y3qfieCrmNvYct8uvtiV41UvlSe6apAfk0fY1FbWx+NwfmpvtTg==", - "dev": true, - "license": "MIT", - "bin": { - "acorn": "bin/acorn" - }, - "engines": { - "node": ">=0.4.0" - } - }, - "node_modules/acorn-jsx": { - "version": "5.3.2", - "resolved": "https://registry.npmjs.org/acorn-jsx/-/acorn-jsx-5.3.2.tgz", - "integrity": "sha512-rq9s+JNhf0IChjtDXxllJ7g41oZk5SlXtp0LHwyA5cejwn7vKmKp4pPri6YEePv2PU65sAsegbXtIinmDFDXgQ==", - "dev": true, - "license": "MIT", - "peerDependencies": { - "acorn": "^6.0.0 || ^7.0.0 || ^8.0.0" - } - }, - "node_modules/ajv": { - "version": "6.12.6", - "resolved": "https://registry.npmjs.org/ajv/-/ajv-6.12.6.tgz", - "integrity": "sha512-j3fVLgvTo527anyYyJOGTYJbG+vnnQYvE0m5mmkc1TK+nxAppkCLMIL0aZ4dblVCNoGShhm+kzE4ZUykBoMg4g==", - "dev": true, - "license": "MIT", - "dependencies": { - "fast-deep-equal": "^3.1.1", - "fast-json-stable-stringify": "^2.0.0", - "json-schema-traverse": "^0.4.1", - "uri-js": "^4.2.2" - }, - "funding": { - "type": "github", - "url": "https://github.com/sponsors/epoberezkin" - } - }, - "node_modules/ansi-escapes": { - "version": "4.3.2", - "resolved": "https://registry.npmjs.org/ansi-escapes/-/ansi-escapes-4.3.2.tgz", - "integrity": "sha512-gKXj5ALrKWQLsYG9jlTRmR/xKluxHV+Z9QEwNIgCfM1/uwPMCuzVVnh5mwTd+OuBZcwSIMbqssNWRm1lE51QaQ==", - "dev": true, - "license": "MIT", - "dependencies": { - "type-fest": "^0.21.3" - }, - "engines": { - "node": ">=8" - }, - "funding": { - "url": "https://github.com/sponsors/sindresorhus" - } - }, - "node_modules/ansi-regex": { - "version": "6.1.0", - "resolved": "https://registry.npmjs.org/ansi-regex/-/ansi-regex-6.1.0.tgz", - "integrity": "sha512-7HSX4QQb4CspciLpVFwyRe79O3xsIZDDLER21kERQ71oaPodF8jL725AgJMFAYbooIqolJoRLuM81SpeUkpkvA==", - "dev": true, - "license": "MIT", - "engines": { - "node": ">=12" - }, - "funding": { - "url": "https://github.com/chalk/ansi-regex?sponsor=1" - } - }, - "node_modules/ansi-styles": { - "version": "5.2.0", - "resolved": "https://registry.npmjs.org/ansi-styles/-/ansi-styles-5.2.0.tgz", - "integrity": "sha512-Cxwpt2SfTzTtXcfOlzGEee8O+c+MmUgGrNiBcXnuWxuFJHe6a5Hz7qwhwe5OgaSYI0IJvkLqWX1ASG+cJOkEiA==", - "dev": true, - "license": "MIT", - "engines": { - "node": ">=10" - }, - "funding": { - "url": "https://github.com/chalk/ansi-styles?sponsor=1" - } - }, - "node_modules/anymatch": { - "version": "3.1.3", - "resolved": "https://registry.npmjs.org/anymatch/-/anymatch-3.1.3.tgz", - "integrity": "sha512-KMReFUr0B4t+D+OBkjR3KYqvocp2XaSzO55UcB6mgQMd3KbcE+mWTyvVV7D/zsdEbNnV6acZUutkiHQXvTr1Rw==", - "dev": true, - "license": "ISC", - "dependencies": { - "normalize-path": "^3.0.0", - "picomatch": "^2.0.4" - }, - "engines": { - "node": ">= 8" - } - }, - "node_modules/anymatch/node_modules/picomatch": { - "version": "2.3.1", - "resolved": "https://registry.npmjs.org/picomatch/-/picomatch-2.3.1.tgz", - "integrity": "sha512-JU3teHTNjmE2VCGFzuY8EXzCDVwEqB2a8fsIvwaStHhAWJEeVd1o1QD80CU6+ZdEXXSLbSsuLwJjkCBWqRQUVA==", - "dev": true, - "license": "MIT", - "engines": { - "node": ">=8.6" - }, - "funding": { - "url": "https://github.com/sponsors/jonschlinkert" - } - }, - "node_modules/argparse": { - "version": "1.0.10", - "resolved": "https://registry.npmjs.org/argparse/-/argparse-1.0.10.tgz", - "integrity": "sha512-o5Roy6tNG4SL/FOkCAN6RzjiakZS25RLYFrcMttJqbdd8BWrnA+fGz57iN5Pb06pvBGvl5gQ0B48dJlslXvoTg==", - "dev": true, - "license": "MIT", - "dependencies": { - "sprintf-js": "~1.0.2" - } - }, - "node_modules/array-find-index": { - "version": "1.0.2", - "resolved": "https://registry.npmjs.org/array-find-index/-/array-find-index-1.0.2.tgz", - "integrity": "sha512-M1HQyIXcBGtVywBt8WVdim+lrNaK7VHp99Qt5pSNziXznKHViIBbXWtfRTpEFpF/c4FdfxNAsCCwPp5phBYJtw==", - "dev": true, - "license": "MIT", - "engines": { - "node": ">=0.10.0" - } - }, - "node_modules/babel-jest": { - "version": "30.0.5", - "resolved": "https://registry.npmjs.org/babel-jest/-/babel-jest-30.0.5.tgz", - "integrity": "sha512-mRijnKimhGDMsizTvBTWotwNpzrkHr+VvZUQBof2AufXKB8NXrL1W69TG20EvOz7aevx6FTJIaBuBkYxS8zolg==", - "dev": true, - "license": "MIT", - "dependencies": { - "@jest/transform": "30.0.5", - "@types/babel__core": "^7.20.5", - "babel-plugin-istanbul": "^7.0.0", - "babel-preset-jest": "30.0.1", - "chalk": "^4.1.2", - "graceful-fs": "^4.2.11", - "slash": "^3.0.0" - }, - "engines": { - "node": "^18.14.0 || ^20.0.0 || ^22.0.0 || >=24.0.0" - }, - "peerDependencies": { - "@babel/core": "^7.11.0" - } - }, - "node_modules/babel-jest/node_modules/ansi-styles": { - "version": "4.3.0", - "resolved": "https://registry.npmjs.org/ansi-styles/-/ansi-styles-4.3.0.tgz", - "integrity": "sha512-zbB9rCJAT1rbjiVDb2hqKFHNYLxgtk8NURxZ3IZwD3F6NtxbXZQCnnSi1Lkx+IDohdPlFp222wVALIheZJQSEg==", - "dev": true, - "license": "MIT", - "dependencies": { - "color-convert": "^2.0.1" - }, - "engines": { - "node": ">=8" - }, - "funding": { - "url": "https://github.com/chalk/ansi-styles?sponsor=1" - } - }, - "node_modules/babel-jest/node_modules/chalk": { - "version": "4.1.2", - "resolved": "https://registry.npmjs.org/chalk/-/chalk-4.1.2.tgz", - "integrity": "sha512-oKnbhFyRIXpUuez8iBMmyEa4nbj4IOQyuhc/wy9kY7/WVPcwIO9VA668Pu8RkO7+0G76SLROeyw9CpQ061i4mA==", - "dev": true, - "license": "MIT", - "dependencies": { - "ansi-styles": "^4.1.0", - "supports-color": "^7.1.0" - }, - "engines": { - "node": ">=10" - }, - "funding": { - "url": "https://github.com/chalk/chalk?sponsor=1" - } - }, - "node_modules/babel-plugin-istanbul": { - "version": "7.0.0", - "resolved": "https://registry.npmjs.org/babel-plugin-istanbul/-/babel-plugin-istanbul-7.0.0.tgz", - "integrity": "sha512-C5OzENSx/A+gt7t4VH1I2XsflxyPUmXRFPKBxt33xncdOmq7oROVM3bZv9Ysjjkv8OJYDMa+tKuKMvqU/H3xdw==", - "dev": true, - "license": "BSD-3-Clause", - "dependencies": { - "@babel/helper-plugin-utils": "^7.0.0", - "@istanbuljs/load-nyc-config": "^1.0.0", - "@istanbuljs/schema": "^0.1.3", - "istanbul-lib-instrument": "^6.0.2", - "test-exclude": "^6.0.0" - }, - "engines": { - "node": ">=12" - } - }, - "node_modules/babel-plugin-jest-hoist": { - "version": "30.0.1", - "resolved": "https://registry.npmjs.org/babel-plugin-jest-hoist/-/babel-plugin-jest-hoist-30.0.1.tgz", - "integrity": "sha512-zTPME3pI50NsFW8ZBaVIOeAxzEY7XHlmWeXXu9srI+9kNfzCUTy8MFan46xOGZY8NZThMqq+e3qZUKsvXbasnQ==", - "dev": true, - "license": "MIT", - "dependencies": { - "@babel/template": "^7.27.2", - "@babel/types": "^7.27.3", - "@types/babel__core": "^7.20.5" - }, - "engines": { - "node": "^18.14.0 || ^20.0.0 || ^22.0.0 || >=24.0.0" - } - }, - "node_modules/babel-plugin-polyfill-corejs2": { - "version": "0.4.14", - "resolved": "https://registry.npmjs.org/babel-plugin-polyfill-corejs2/-/babel-plugin-polyfill-corejs2-0.4.14.tgz", - "integrity": "sha512-Co2Y9wX854ts6U8gAAPXfn0GmAyctHuK8n0Yhfjd6t30g7yvKjspvvOo9yG+z52PZRgFErt7Ka2pYnXCjLKEpg==", - "dev": true, - "license": "MIT", - "dependencies": { - "@babel/compat-data": "^7.27.7", - "@babel/helper-define-polyfill-provider": "^0.6.5", - "semver": "^6.3.1" - }, - "peerDependencies": { - "@babel/core": "^7.4.0 || ^8.0.0-0 <8.0.0" - } - }, - "node_modules/babel-plugin-polyfill-corejs3": { - "version": "0.13.0", - "resolved": "https://registry.npmjs.org/babel-plugin-polyfill-corejs3/-/babel-plugin-polyfill-corejs3-0.13.0.tgz", - "integrity": "sha512-U+GNwMdSFgzVmfhNm8GJUX88AadB3uo9KpJqS3FaqNIPKgySuvMb+bHPsOmmuWyIcuqZj/pzt1RUIUZns4y2+A==", - "dev": true, - "license": "MIT", - "dependencies": { - "@babel/helper-define-polyfill-provider": "^0.6.5", - "core-js-compat": "^3.43.0" - }, - "peerDependencies": { - "@babel/core": "^7.4.0 || ^8.0.0-0 <8.0.0" - } - }, - "node_modules/babel-plugin-polyfill-regenerator": { - "version": "0.6.5", - "resolved": "https://registry.npmjs.org/babel-plugin-polyfill-regenerator/-/babel-plugin-polyfill-regenerator-0.6.5.tgz", - "integrity": "sha512-ISqQ2frbiNU9vIJkzg7dlPpznPZ4jOiUQ1uSmB0fEHeowtN3COYRsXr/xexn64NpU13P06jc/L5TgiJXOgrbEg==", - "dev": true, - "license": "MIT", - "dependencies": { - "@babel/helper-define-polyfill-provider": "^0.6.5" - }, - "peerDependencies": { - "@babel/core": "^7.4.0 || ^8.0.0-0 <8.0.0" - } - }, - "node_modules/babel-preset-current-node-syntax": { - "version": "1.1.0", - "resolved": "https://registry.npmjs.org/babel-preset-current-node-syntax/-/babel-preset-current-node-syntax-1.1.0.tgz", - "integrity": "sha512-ldYss8SbBlWva1bs28q78Ju5Zq1F+8BrqBZZ0VFhLBvhh6lCpC2o3gDJi/5DRLs9FgYZCnmPYIVFU4lRXCkyUw==", - "dev": true, - "license": "MIT", - "dependencies": { - "@babel/plugin-syntax-async-generators": "^7.8.4", - "@babel/plugin-syntax-bigint": "^7.8.3", - "@babel/plugin-syntax-class-properties": "^7.12.13", - "@babel/plugin-syntax-class-static-block": "^7.14.5", - "@babel/plugin-syntax-import-attributes": "^7.24.7", - "@babel/plugin-syntax-import-meta": "^7.10.4", - "@babel/plugin-syntax-json-strings": "^7.8.3", - "@babel/plugin-syntax-logical-assignment-operators": "^7.10.4", - "@babel/plugin-syntax-nullish-coalescing-operator": "^7.8.3", - "@babel/plugin-syntax-numeric-separator": "^7.10.4", - "@babel/plugin-syntax-object-rest-spread": "^7.8.3", - "@babel/plugin-syntax-optional-catch-binding": "^7.8.3", - "@babel/plugin-syntax-optional-chaining": "^7.8.3", - "@babel/plugin-syntax-private-property-in-object": "^7.14.5", - "@babel/plugin-syntax-top-level-await": "^7.14.5" - }, - "peerDependencies": { - "@babel/core": "^7.0.0" - } - }, - "node_modules/babel-preset-jest": { - "version": "30.0.1", - "resolved": "https://registry.npmjs.org/babel-preset-jest/-/babel-preset-jest-30.0.1.tgz", - "integrity": "sha512-+YHejD5iTWI46cZmcc/YtX4gaKBtdqCHCVfuVinizVpbmyjO3zYmeuyFdfA8duRqQZfgCAMlsfmkVbJ+e2MAJw==", - "dev": true, - "license": "MIT", - "dependencies": { - "babel-plugin-jest-hoist": "30.0.1", - "babel-preset-current-node-syntax": "^1.1.0" - }, - "engines": { - "node": "^18.14.0 || ^20.0.0 || ^22.0.0 || >=24.0.0" - }, - "peerDependencies": { - "@babel/core": "^7.11.0" - } - }, - "node_modules/balanced-match": { - "version": "1.0.2", - "resolved": "https://registry.npmjs.org/balanced-match/-/balanced-match-1.0.2.tgz", - "integrity": "sha512-3oSeUO0TMV67hN1AmbXsK4yaqU7tjiHlbxRDZOpH0KW9+CeX4bRAaX0Anxt0tx2MrpRpWwQaPwIlISEJhYU5Pw==", - "dev": true, - "license": "MIT" - }, - "node_modules/brace-expansion": { - "version": "2.0.2", - "resolved": "https://registry.npmjs.org/brace-expansion/-/brace-expansion-2.0.2.tgz", - "integrity": "sha512-Jt0vHyM+jmUBqojB7E1NIYadt0vI0Qxjxd2TErW94wDz+E2LAm5vKMXXwg6ZZBTHPuUlDgQHKXvjGBdfcF1ZDQ==", - "dev": true, - "license": "MIT", - "dependencies": { - "balanced-match": "^1.0.0" - } - }, - "node_modules/braces": { - "version": "3.0.3", - "resolved": "https://registry.npmjs.org/braces/-/braces-3.0.3.tgz", - "integrity": "sha512-yQbXgO/OSZVD2IsiLlro+7Hf6Q18EJrKSEsdoMzKePKXct3gvD8oLcOQdIzGupr5Fj+EDe8gO/lxc1BzfMpxvA==", - "dev": true, - "license": "MIT", - "dependencies": { - "fill-range": "^7.1.1" - }, - "engines": { - "node": ">=8" - } - }, - "node_modules/browserslist": { - "version": "4.25.1", - "resolved": "https://registry.npmjs.org/browserslist/-/browserslist-4.25.1.tgz", - "integrity": "sha512-KGj0KoOMXLpSNkkEI6Z6mShmQy0bc1I+T7K9N81k4WWMrfz+6fQ6es80B/YLAeRoKvjYE1YSHHOW1qe9xIVzHw==", - "dev": true, - "funding": [ - { - "type": "opencollective", - "url": "https://opencollective.com/browserslist" - }, - { - "type": "tidelift", - "url": "https://tidelift.com/funding/github/npm/browserslist" - }, - { - "type": "github", - "url": "https://github.com/sponsors/ai" - } - ], - "license": "MIT", - "dependencies": { - "caniuse-lite": "^1.0.30001726", - "electron-to-chromium": "^1.5.173", - "node-releases": "^2.0.19", - "update-browserslist-db": "^1.1.3" - }, - "bin": { - "browserslist": "cli.js" - }, - "engines": { - "node": "^6 || ^7 || ^8 || ^9 || ^10 || ^11 || ^12 || >=13.7" - } - }, - "node_modules/bser": { - "version": "2.1.1", - "resolved": "https://registry.npmjs.org/bser/-/bser-2.1.1.tgz", - "integrity": "sha512-gQxTNE/GAfIIrmHLUE3oJyp5FO6HRBfhjnw4/wMmA63ZGDJnWBmgY/lyQBpnDUkGmAhbSe39tx2d/iTOAfglwQ==", - "dev": true, - "license": "Apache-2.0", - "dependencies": { - "node-int64": "^0.4.0" - } - }, - "node_modules/buffer-from": { - "version": "1.1.2", - "resolved": "https://registry.npmjs.org/buffer-from/-/buffer-from-1.1.2.tgz", - "integrity": "sha512-E+XQCRwSbaaiChtv6k6Dwgc+bx+Bs6vuKJHHl5kox/BaKbhiXzqQOwK4cO22yElGp2OCmjwVhT3HmxgyPGnJfQ==", - "dev": true, - "license": "MIT" - }, - "node_modules/callsites": { - "version": "3.1.0", - "resolved": "https://registry.npmjs.org/callsites/-/callsites-3.1.0.tgz", - "integrity": "sha512-P8BjAsXvZS+VIDUI11hHCQEv74YT67YUi5JJFNWIqL235sBmjX4+qx9Muvls5ivyNENctx46xQLQ3aTuE7ssaQ==", - "dev": true, - "license": "MIT", - "engines": { - "node": ">=6" - } - }, - "node_modules/camelcase": { - "version": "5.3.1", - "resolved": "https://registry.npmjs.org/camelcase/-/camelcase-5.3.1.tgz", - "integrity": "sha512-L28STB170nwWS63UjtlEOE3dldQApaJXZkOI1uMFfzf3rRuPegHaHesyee+YxQ+W6SvRDQV6UrdOdRiR153wJg==", - "dev": true, - "license": "MIT", - "engines": { - "node": ">=6" - } - }, - "node_modules/caniuse-lite": { - "version": "1.0.30001727", - "resolved": "https://registry.npmjs.org/caniuse-lite/-/caniuse-lite-1.0.30001727.tgz", - "integrity": "sha512-pB68nIHmbN6L/4C6MH1DokyR3bYqFwjaSs/sWDHGj4CTcFtQUQMuJftVwWkXq7mNWOybD3KhUv3oWHoGxgP14Q==", - "dev": true, - "funding": [ - { - "type": "opencollective", - "url": "https://opencollective.com/browserslist" - }, - { - "type": "tidelift", - "url": "https://tidelift.com/funding/github/npm/caniuse-lite" - }, - { - "type": "github", - "url": "https://github.com/sponsors/ai" - } - ], - "license": "CC-BY-4.0" - }, - "node_modules/chalk": { - "version": "5.4.1", - "resolved": "https://registry.npmjs.org/chalk/-/chalk-5.4.1.tgz", - "integrity": "sha512-zgVZuo2WcZgfUEmsn6eO3kINexW8RAE4maiQ8QNs8CtpPCSyMiYsULR3HQYkm3w8FIA3SberyMJMSldGsW+U3w==", - "dev": true, - "license": "MIT", - "engines": { - "node": "^12.17.0 || ^14.13 || >=16.0.0" - }, - "funding": { - "url": "https://github.com/chalk/chalk?sponsor=1" - } - }, - "node_modules/char-regex": { - "version": "1.0.2", - "resolved": "https://registry.npmjs.org/char-regex/-/char-regex-1.0.2.tgz", - "integrity": "sha512-kWWXztvZ5SBQV+eRgKFeh8q5sLuZY2+8WUIzlxWVTg+oGwY14qylx1KbKzHd8P6ZYkAg0xyIDU9JMHhyJMZ1jw==", - "dev": true, - "license": "MIT", - "engines": { - "node": ">=10" - } - }, - "node_modules/ci-info": { - "version": "4.3.0", - "resolved": "https://registry.npmjs.org/ci-info/-/ci-info-4.3.0.tgz", - "integrity": "sha512-l+2bNRMiQgcfILUi33labAZYIWlH1kWDp+ecNo5iisRKrbm0xcRyCww71/YU0Fkw0mAFpz9bJayXPjey6vkmaQ==", - "dev": true, - "funding": [ - { - "type": "github", - "url": "https://github.com/sponsors/sibiraj-s" - } - ], - "license": "MIT", - "engines": { - "node": ">=8" - } - }, - "node_modules/cjs-module-lexer": { - "version": "2.1.0", - "resolved": "https://registry.npmjs.org/cjs-module-lexer/-/cjs-module-lexer-2.1.0.tgz", - "integrity": "sha512-UX0OwmYRYQQetfrLEZeewIFFI+wSTofC+pMBLNuH3RUuu/xzG1oz84UCEDOSoQlN3fZ4+AzmV50ZYvGqkMh9yA==", - "dev": true, - "license": "MIT" - }, - "node_modules/cliui": { - "version": "8.0.1", - "resolved": "https://registry.npmjs.org/cliui/-/cliui-8.0.1.tgz", - "integrity": "sha512-BSeNnyus75C4//NQ9gQt1/csTXyo/8Sb+afLAkzAptFuMsod9HFokGNudZpi/oQV73hnVK+sR+5PVRMd+Dr7YQ==", - "dev": true, - "license": "ISC", - "dependencies": { - "string-width": "^4.2.0", - "strip-ansi": "^6.0.1", - "wrap-ansi": "^7.0.0" - }, - "engines": { - "node": ">=12" - } - }, - "node_modules/cliui/node_modules/ansi-regex": { - "version": "5.0.1", - "resolved": "https://registry.npmjs.org/ansi-regex/-/ansi-regex-5.0.1.tgz", - "integrity": "sha512-quJQXlTSUGL2LH9SUXo8VwsY4soanhgo6LNSm84E1LBcE8s3O0wpdiRzyR9z/ZZJMlMWv37qOOb9pdJlMUEKFQ==", - "dev": true, - "license": "MIT", - "engines": { - "node": ">=8" - } - }, - "node_modules/cliui/node_modules/ansi-styles": { - "version": "4.3.0", - "resolved": "https://registry.npmjs.org/ansi-styles/-/ansi-styles-4.3.0.tgz", - "integrity": "sha512-zbB9rCJAT1rbjiVDb2hqKFHNYLxgtk8NURxZ3IZwD3F6NtxbXZQCnnSi1Lkx+IDohdPlFp222wVALIheZJQSEg==", - "dev": true, - "license": "MIT", - "dependencies": { - "color-convert": "^2.0.1" - }, - "engines": { - "node": ">=8" - }, - "funding": { - "url": "https://github.com/chalk/ansi-styles?sponsor=1" - } - }, - "node_modules/cliui/node_modules/emoji-regex": { - "version": "8.0.0", - "resolved": "https://registry.npmjs.org/emoji-regex/-/emoji-regex-8.0.0.tgz", - "integrity": "sha512-MSjYzcWNOA0ewAHpz0MxpYFvwg6yjy1NG3xteoqz644VCo/RPgnr1/GGt+ic3iJTzQ8Eu3TdM14SawnVUmGE6A==", - "dev": true, - "license": "MIT" - }, - "node_modules/cliui/node_modules/string-width": { - "version": "4.2.3", - "resolved": "https://registry.npmjs.org/string-width/-/string-width-4.2.3.tgz", - "integrity": "sha512-wKyQRQpjJ0sIp62ErSZdGsjMJWsap5oRNihHhu6G7JVO/9jIB6UyevL+tXuOqrng8j/cxKTWyWUwvSTriiZz/g==", - "dev": true, - "license": "MIT", - "dependencies": { - "emoji-regex": "^8.0.0", - "is-fullwidth-code-point": "^3.0.0", - "strip-ansi": "^6.0.1" - }, - "engines": { - "node": ">=8" - } - }, - "node_modules/cliui/node_modules/strip-ansi": { - "version": "6.0.1", - "resolved": "https://registry.npmjs.org/strip-ansi/-/strip-ansi-6.0.1.tgz", - "integrity": "sha512-Y38VPSHcqkFrCpFnQ9vuSXmquuv5oXOKpGeT6aGrr3o3Gc9AlVa6JBfUSOCnbxGGZF+/0ooI7KrPuUSztUdU5A==", - "dev": true, - "license": "MIT", - "dependencies": { - "ansi-regex": "^5.0.1" - }, - "engines": { - "node": ">=8" - } - }, - "node_modules/cliui/node_modules/wrap-ansi": { - "version": "7.0.0", - "resolved": "https://registry.npmjs.org/wrap-ansi/-/wrap-ansi-7.0.0.tgz", - "integrity": "sha512-YVGIj2kamLSTxw6NsZjoBxfSwsn0ycdesmc4p+Q21c5zPuZ1pl+NfxVdxPtdHvmNVOQ6XSYG4AUtyt/Fi7D16Q==", - "dev": true, - "license": "MIT", - "dependencies": { - "ansi-styles": "^4.0.0", - "string-width": "^4.1.0", - "strip-ansi": "^6.0.0" - }, - "engines": { - "node": ">=10" - }, - "funding": { - "url": "https://github.com/chalk/wrap-ansi?sponsor=1" - } - }, - "node_modules/co": { - "version": "4.6.0", - "resolved": "https://registry.npmjs.org/co/-/co-4.6.0.tgz", - "integrity": "sha512-QVb0dM5HvG+uaxitm8wONl7jltx8dqhfU33DcqtOZcLSVIKSDDLDi7+0LbAKiyI8hD9u42m2YxXSkMGWThaecQ==", - "dev": true, - "license": "MIT", - "engines": { - "iojs": ">= 1.0.0", - "node": ">= 0.12.0" - } - }, - "node_modules/collect-v8-coverage": { - "version": "1.0.2", - "resolved": "https://registry.npmjs.org/collect-v8-coverage/-/collect-v8-coverage-1.0.2.tgz", - "integrity": "sha512-lHl4d5/ONEbLlJvaJNtsF/Lz+WvB07u2ycqTYbdrq7UypDXailES4valYb2eWiJFxZlVmpGekfqoxQhzyFdT4Q==", - "dev": true, - "license": "MIT" - }, - "node_modules/color-convert": { - "version": "2.0.1", - "resolved": "https://registry.npmjs.org/color-convert/-/color-convert-2.0.1.tgz", - "integrity": "sha512-RRECPsj7iu/xb5oKYcsFHSppFNnsj/52OVTRKb4zP5onXwVF3zVmmToNcOfGC+CRDpfK/U584fMg38ZHCaElKQ==", - "dev": true, - "license": "MIT", - "dependencies": { - "color-name": "~1.1.4" - }, - "engines": { - "node": ">=7.0.0" - } - }, - "node_modules/color-name": { - "version": "1.1.4", - "resolved": "https://registry.npmjs.org/color-name/-/color-name-1.1.4.tgz", - "integrity": "sha512-dOy+3AuW3a2wNbZHIuMZpTcgjGuLU/uBL/ubcZF9OXbDo8ff4O8yVp5Bf0efS8uEoYo5q4Fx7dY9OgQGXgAsQA==", - "dev": true, - "license": "MIT" - }, - "node_modules/commander": { - "version": "2.20.3", - "resolved": "https://registry.npmjs.org/commander/-/commander-2.20.3.tgz", - "integrity": "sha512-GpVkmM8vF2vQUkj2LvZmD35JxeJOLCwJ9cUkugyk2nuhbv3+mJvpLYYt+0+USMxE+oj+ey/lJEnhZw75x/OMcQ==", - "dev": true, - "license": "MIT" - }, - "node_modules/commenting": { - "version": "1.1.0", - "resolved": "https://registry.npmjs.org/commenting/-/commenting-1.1.0.tgz", - "integrity": "sha512-YeNK4tavZwtH7jEgK1ZINXzLKm6DZdEMfsaaieOsCAN0S8vsY7UeuO3Q7d/M018EFgE+IeUAuBOKkFccBZsUZA==", - "dev": true, - "license": "MIT" - }, - "node_modules/concat-map": { - "version": "0.0.1", - "resolved": "https://registry.npmjs.org/concat-map/-/concat-map-0.0.1.tgz", - "integrity": "sha512-/Srv4dswyQNBfohGpz9o6Yb3Gz3SrUDqBH5rTuhGR7ahtlbYKnVxw2bCFMRljaA7EXHaXZ8wsHdodFvbkhKmqg==", - "dev": true, - "license": "MIT" - }, - "node_modules/convert-source-map": { - "version": "2.0.0", - "resolved": "https://registry.npmjs.org/convert-source-map/-/convert-source-map-2.0.0.tgz", - "integrity": "sha512-Kvp459HrV2FEJ1CAsi1Ku+MY3kasH19TFykTz2xWmMeq6bk2NU3XXvfJ+Q61m0xktWwt+1HSYf3JZsTms3aRJg==", - "dev": true, - "license": "MIT" - }, - "node_modules/core-js-compat": { - "version": "3.44.0", - "resolved": "https://registry.npmjs.org/core-js-compat/-/core-js-compat-3.44.0.tgz", - "integrity": "sha512-JepmAj2zfl6ogy34qfWtcE7nHKAJnKsQFRn++scjVS2bZFllwptzw61BZcZFYBPpUznLfAvh0LGhxKppk04ClA==", - "dev": true, - "license": "MIT", - "dependencies": { - "browserslist": "^4.25.1" - }, - "funding": { - "type": "opencollective", - "url": "https://opencollective.com/core-js" - } - }, - "node_modules/cross-spawn": { - "version": "7.0.6", - "resolved": "https://registry.npmjs.org/cross-spawn/-/cross-spawn-7.0.6.tgz", - "integrity": "sha512-uV2QOWP2nWzsy2aMp8aRibhi9dlzF5Hgh5SHaB9OiTGEyDTiJJyx0uy51QXdyWbtAHNua4XJzUKca3OzKUd3vA==", - "dev": true, - "license": "MIT", - "dependencies": { - "path-key": "^3.1.0", - "shebang-command": "^2.0.0", - "which": "^2.0.1" - }, - "engines": { - "node": ">= 8" - } - }, - "node_modules/debug": { - "version": "4.4.1", - "resolved": "https://registry.npmjs.org/debug/-/debug-4.4.1.tgz", - "integrity": "sha512-KcKCqiftBJcZr++7ykoDIEwSa3XWowTfNPo92BYxjXiyYEVrUQh2aLyhxBCwww+heortUFxEJYcRzosstTEBYQ==", - "dev": true, - "license": "MIT", - "dependencies": { - "ms": "^2.1.3" - }, - "engines": { - "node": ">=6.0" - }, - "peerDependenciesMeta": { - "supports-color": { - "optional": true - } - } - }, - "node_modules/dedent": { - "version": "1.6.0", - "resolved": "https://registry.npmjs.org/dedent/-/dedent-1.6.0.tgz", - "integrity": "sha512-F1Z+5UCFpmQUzJa11agbyPVMbpgT/qA3/SKyJ1jyBgm7dUcUEa8v9JwDkerSQXfakBwFljIxhOJqGkjUwZ9FSA==", - "dev": true, - "license": "MIT", - "peerDependencies": { - "babel-plugin-macros": "^3.1.0" - }, - "peerDependenciesMeta": { - "babel-plugin-macros": { - "optional": true - } - } - }, - "node_modules/deep-is": { - "version": "0.1.4", - "resolved": "https://registry.npmjs.org/deep-is/-/deep-is-0.1.4.tgz", - "integrity": "sha512-oIPzksmTg4/MriiaYGO+okXDT7ztn/w3Eptv/+gSIdMdKsJo0u4CfYNFJPy+4SKMuCqGw2wxnA+URMg3t8a/bQ==", - "dev": true, - "license": "MIT" - }, - "node_modules/deepmerge": { - "version": "4.3.1", - "resolved": "https://registry.npmjs.org/deepmerge/-/deepmerge-4.3.1.tgz", - "integrity": "sha512-3sUqbMEc77XqpdNO7FRyRog+eW3ph+GYCbj+rK+uYyRMuwsVy0rMiVtPn+QJlKFvWP/1PYpapqYn0Me2knFn+A==", - "dev": true, - "license": "MIT", - "engines": { - "node": ">=0.10.0" - } - }, - "node_modules/detect-newline": { - "version": "3.1.0", - "resolved": "https://registry.npmjs.org/detect-newline/-/detect-newline-3.1.0.tgz", - "integrity": "sha512-TLz+x/vEXm/Y7P7wn1EJFNLxYpUD4TgMosxY6fAVJUnJMbupHBOncxyWUG9OpTaH9EBD7uFI5LfEgmMOc54DsA==", - "dev": true, - "license": "MIT", - "engines": { - "node": ">=8" - } - }, - "node_modules/eastasianwidth": { - "version": "0.2.0", - "resolved": "https://registry.npmjs.org/eastasianwidth/-/eastasianwidth-0.2.0.tgz", - "integrity": "sha512-I88TYZWc9XiYHRQ4/3c5rjjfgkjhLyW2luGIheGERbNQ6OY7yTybanSpDXZa8y7VUP9YmDcYa+eyq4ca7iLqWA==", - "dev": true, - "license": "MIT" - }, - "node_modules/electron-to-chromium": { - "version": "1.5.191", - "resolved": "https://registry.npmjs.org/electron-to-chromium/-/electron-to-chromium-1.5.191.tgz", - "integrity": "sha512-xcwe9ELcuxYLUFqZZxL19Z6HVKcvNkIwhbHUz7L3us6u12yR+7uY89dSl570f/IqNthx8dAw3tojG7i4Ni4tDA==", - "dev": true, - "license": "ISC" - }, - "node_modules/emittery": { - "version": "0.13.1", - "resolved": "https://registry.npmjs.org/emittery/-/emittery-0.13.1.tgz", - "integrity": "sha512-DeWwawk6r5yR9jFgnDKYt4sLS0LmHJJi3ZOnb5/JdbYwj3nW+FxQnHIjhBKz8YLC7oRNPVM9NQ47I3CVx34eqQ==", - "dev": true, - "license": "MIT", - "engines": { - "node": ">=12" - }, - "funding": { - "url": "https://github.com/sindresorhus/emittery?sponsor=1" - } - }, - "node_modules/emoji-regex": { - "version": "9.2.2", - "resolved": "https://registry.npmjs.org/emoji-regex/-/emoji-regex-9.2.2.tgz", - "integrity": "sha512-L18DaJsXSUk2+42pv8mLs5jJT2hqFkFE4j21wOmgbUqsZ2hL72NsUU785g9RXgo3s0ZNgVl42TiHp3ZtOv/Vyg==", - "dev": true, - "license": "MIT" - }, - "node_modules/error-ex": { - "version": "1.3.2", - "resolved": "https://registry.npmjs.org/error-ex/-/error-ex-1.3.2.tgz", - "integrity": "sha512-7dFHNmqeFSEt2ZBsCriorKnn3Z2pj+fd9kmI6QoWw4//DL+icEBfc0U7qJCisqrTsKTjw4fNFy2pW9OqStD84g==", - "dev": true, - "license": "MIT", - "dependencies": { - "is-arrayish": "^0.2.1" - } - }, - "node_modules/escalade": { - "version": "3.2.0", - "resolved": "https://registry.npmjs.org/escalade/-/escalade-3.2.0.tgz", - "integrity": "sha512-WUj2qlxaQtO4g6Pq5c29GTcWGDyd8itL8zTlipgECz3JesAiiOKotd8JU6otB3PACgG6xkJUyVhboMS+bje/jA==", - "dev": true, - "license": "MIT", - "engines": { - "node": ">=6" - } - }, - "node_modules/escape-string-regexp": { - "version": "4.0.0", - "resolved": "https://registry.npmjs.org/escape-string-regexp/-/escape-string-regexp-4.0.0.tgz", - "integrity": "sha512-TtpcNJ3XAzx3Gq8sWRzJaVajRs0uVxA2YAkdb1jm2YkPz4G6egUFAyA3n5vtEIZefPk5Wa4UXbKuS5fKkJWdgA==", - "dev": true, - "license": "MIT", - "engines": { - "node": ">=10" - }, - "funding": { - "url": "https://github.com/sponsors/sindresorhus" - } - }, - "node_modules/eslint": { - "version": "9.32.0", - "resolved": "https://registry.npmjs.org/eslint/-/eslint-9.32.0.tgz", - "integrity": "sha512-LSehfdpgMeWcTZkWZVIJl+tkZ2nuSkyyB9C27MZqFWXuph7DvaowgcTvKqxvpLW1JZIk8PN7hFY3Rj9LQ7m7lg==", - "dev": true, - "license": "MIT", - "dependencies": { - "@eslint-community/eslint-utils": "^4.2.0", - "@eslint-community/regexpp": "^4.12.1", - "@eslint/config-array": "^0.21.0", - "@eslint/config-helpers": "^0.3.0", - "@eslint/core": "^0.15.0", - "@eslint/eslintrc": "^3.3.1", - "@eslint/js": "9.32.0", - "@eslint/plugin-kit": "^0.3.4", - "@humanfs/node": "^0.16.6", - "@humanwhocodes/module-importer": "^1.0.1", - "@humanwhocodes/retry": "^0.4.2", - "@types/estree": "^1.0.6", - "@types/json-schema": "^7.0.15", - "ajv": "^6.12.4", - "chalk": "^4.0.0", - "cross-spawn": "^7.0.6", - "debug": "^4.3.2", - "escape-string-regexp": "^4.0.0", - "eslint-scope": "^8.4.0", - "eslint-visitor-keys": "^4.2.1", - "espree": "^10.4.0", - "esquery": "^1.5.0", - "esutils": "^2.0.2", - "fast-deep-equal": "^3.1.3", - "file-entry-cache": "^8.0.0", - "find-up": "^5.0.0", - "glob-parent": "^6.0.2", - "ignore": "^5.2.0", - "imurmurhash": "^0.1.4", - "is-glob": "^4.0.0", - "json-stable-stringify-without-jsonify": "^1.0.1", - "lodash.merge": "^4.6.2", - "minimatch": "^3.1.2", - "natural-compare": "^1.4.0", - "optionator": "^0.9.3" - }, - "bin": { - "eslint": "bin/eslint.js" - }, - "engines": { - "node": "^18.18.0 || ^20.9.0 || >=21.1.0" - }, - "funding": { - "url": "https://eslint.org/donate" - }, - "peerDependencies": { - "jiti": "*" - }, - "peerDependenciesMeta": { - "jiti": { - "optional": true - } - } - }, - "node_modules/eslint-scope": { - "version": "8.4.0", - "resolved": "https://registry.npmjs.org/eslint-scope/-/eslint-scope-8.4.0.tgz", - "integrity": "sha512-sNXOfKCn74rt8RICKMvJS7XKV/Xk9kA7DyJr8mJik3S7Cwgy3qlkkmyS2uQB3jiJg6VNdZd/pDBJu0nvG2NlTg==", - "dev": true, - "license": "BSD-2-Clause", - "dependencies": { - "esrecurse": "^4.3.0", - "estraverse": "^5.2.0" - }, - "engines": { - "node": "^18.18.0 || ^20.9.0 || >=21.1.0" - }, - "funding": { - "url": "https://opencollective.com/eslint" - } - }, - "node_modules/eslint-visitor-keys": { - "version": "3.4.3", - "resolved": "https://registry.npmjs.org/eslint-visitor-keys/-/eslint-visitor-keys-3.4.3.tgz", - "integrity": "sha512-wpc+LXeiyiisxPlEkUzU6svyS1frIO3Mgxj1fdy7Pm8Ygzguax2N3Fa/D/ag1WqbOprdI+uY6wMUl8/a2G+iag==", - "dev": true, - "license": "Apache-2.0", - "engines": { - "node": "^12.22.0 || ^14.17.0 || >=16.0.0" - }, - "funding": { - "url": "https://opencollective.com/eslint" - } - }, - "node_modules/eslint/node_modules/ansi-styles": { - "version": "4.3.0", - "resolved": "https://registry.npmjs.org/ansi-styles/-/ansi-styles-4.3.0.tgz", - "integrity": "sha512-zbB9rCJAT1rbjiVDb2hqKFHNYLxgtk8NURxZ3IZwD3F6NtxbXZQCnnSi1Lkx+IDohdPlFp222wVALIheZJQSEg==", - "dev": true, - "license": "MIT", - "dependencies": { - "color-convert": "^2.0.1" - }, - "engines": { - "node": ">=8" - }, - "funding": { - "url": "https://github.com/chalk/ansi-styles?sponsor=1" - } - }, - "node_modules/eslint/node_modules/brace-expansion": { - "version": "1.1.12", - "resolved": "https://registry.npmjs.org/brace-expansion/-/brace-expansion-1.1.12.tgz", - "integrity": "sha512-9T9UjW3r0UW5c1Q7GTwllptXwhvYmEzFhzMfZ9H7FQWt+uZePjZPjBP/W1ZEyZ1twGWom5/56TF4lPcqjnDHcg==", - "dev": true, - "license": "MIT", - "dependencies": { - "balanced-match": "^1.0.0", - "concat-map": "0.0.1" - } - }, - "node_modules/eslint/node_modules/chalk": { - "version": "4.1.2", - "resolved": "https://registry.npmjs.org/chalk/-/chalk-4.1.2.tgz", - "integrity": "sha512-oKnbhFyRIXpUuez8iBMmyEa4nbj4IOQyuhc/wy9kY7/WVPcwIO9VA668Pu8RkO7+0G76SLROeyw9CpQ061i4mA==", - "dev": true, - "license": "MIT", - "dependencies": { - "ansi-styles": "^4.1.0", - "supports-color": "^7.1.0" - }, - "engines": { - "node": ">=10" - }, - "funding": { - "url": "https://github.com/chalk/chalk?sponsor=1" - } - }, - "node_modules/eslint/node_modules/eslint-visitor-keys": { - "version": "4.2.1", - "resolved": "https://registry.npmjs.org/eslint-visitor-keys/-/eslint-visitor-keys-4.2.1.tgz", - "integrity": "sha512-Uhdk5sfqcee/9H/rCOJikYz67o0a2Tw2hGRPOG2Y1R2dg7brRe1uG0yaNQDHu+TO/uQPF/5eCapvYSmHUjt7JQ==", - "dev": true, - "license": "Apache-2.0", - "engines": { - "node": "^18.18.0 || ^20.9.0 || >=21.1.0" - }, - "funding": { - "url": "https://opencollective.com/eslint" - } - }, - "node_modules/eslint/node_modules/find-up": { - "version": "5.0.0", - "resolved": "https://registry.npmjs.org/find-up/-/find-up-5.0.0.tgz", - "integrity": "sha512-78/PXT1wlLLDgTzDs7sjq9hzz0vXD+zn+7wypEe4fXQxCmdmqfGsEPQxmiCSQI3ajFV91bVSsvNtrJRiW6nGng==", - "dev": true, - "license": "MIT", - "dependencies": { - "locate-path": "^6.0.0", - "path-exists": "^4.0.0" - }, - "engines": { - "node": ">=10" - }, - "funding": { - "url": "https://github.com/sponsors/sindresorhus" - } - }, - "node_modules/eslint/node_modules/ignore": { - "version": "5.3.2", - "resolved": "https://registry.npmjs.org/ignore/-/ignore-5.3.2.tgz", - "integrity": "sha512-hsBTNUqQTDwkWtcdYI2i06Y/nUBEsNEDJKjWdigLvegy8kDuJAS8uRlpkkcQpyEXL0Z/pjDy5HBmMjRCJ2gq+g==", - "dev": true, - "license": "MIT", - "engines": { - "node": ">= 4" - } - }, - "node_modules/eslint/node_modules/locate-path": { - "version": "6.0.0", - "resolved": "https://registry.npmjs.org/locate-path/-/locate-path-6.0.0.tgz", - "integrity": "sha512-iPZK6eYjbxRu3uB4/WZ3EsEIMJFMqAoopl3R+zuq0UjcAm/MO6KCweDgPfP3elTztoKP3KtnVHxTn2NHBSDVUw==", - "dev": true, - "license": "MIT", - "dependencies": { - "p-locate": "^5.0.0" - }, - "engines": { - "node": ">=10" - }, - "funding": { - "url": "https://github.com/sponsors/sindresorhus" - } - }, - "node_modules/eslint/node_modules/minimatch": { - "version": "3.1.2", - "resolved": "https://registry.npmjs.org/minimatch/-/minimatch-3.1.2.tgz", - "integrity": "sha512-J7p63hRiAjw1NDEww1W7i37+ByIrOWO5XQQAzZ3VOcL0PNybwpfmV/N05zFAzwQ9USyEcX6t3UO+K5aqBQOIHw==", - "dev": true, - "license": "ISC", - "dependencies": { - "brace-expansion": "^1.1.7" - }, - "engines": { - "node": "*" - } - }, - "node_modules/eslint/node_modules/p-locate": { - "version": "5.0.0", - "resolved": "https://registry.npmjs.org/p-locate/-/p-locate-5.0.0.tgz", - "integrity": "sha512-LaNjtRWUBY++zB5nE/NwcaoMylSPk+S+ZHNB1TzdbMJMny6dynpAGt7X/tl/QYq3TIeE6nxHppbo2LGymrG5Pw==", - "dev": true, - "license": "MIT", - "dependencies": { - "p-limit": "^3.0.2" - }, - "engines": { - "node": ">=10" - }, - "funding": { - "url": "https://github.com/sponsors/sindresorhus" - } - }, - "node_modules/espree": { - "version": "10.4.0", - "resolved": "https://registry.npmjs.org/espree/-/espree-10.4.0.tgz", - "integrity": "sha512-j6PAQ2uUr79PZhBjP5C5fhl8e39FmRnOjsD5lGnWrFU8i2G776tBK7+nP8KuQUTTyAZUwfQqXAgrVH5MbH9CYQ==", - "dev": true, - "license": "BSD-2-Clause", - "dependencies": { - "acorn": "^8.15.0", - "acorn-jsx": "^5.3.2", - "eslint-visitor-keys": "^4.2.1" - }, - "engines": { - "node": "^18.18.0 || ^20.9.0 || >=21.1.0" - }, - "funding": { - "url": "https://opencollective.com/eslint" - } - }, - "node_modules/espree/node_modules/eslint-visitor-keys": { - "version": "4.2.1", - "resolved": "https://registry.npmjs.org/eslint-visitor-keys/-/eslint-visitor-keys-4.2.1.tgz", - "integrity": "sha512-Uhdk5sfqcee/9H/rCOJikYz67o0a2Tw2hGRPOG2Y1R2dg7brRe1uG0yaNQDHu+TO/uQPF/5eCapvYSmHUjt7JQ==", - "dev": true, - "license": "Apache-2.0", - "engines": { - "node": "^18.18.0 || ^20.9.0 || >=21.1.0" - }, - "funding": { - "url": "https://opencollective.com/eslint" - } - }, - "node_modules/esprima": { - "version": "4.0.1", - "resolved": "https://registry.npmjs.org/esprima/-/esprima-4.0.1.tgz", - "integrity": "sha512-eGuFFw7Upda+g4p+QHvnW0RyTX/SVeJBDM/gCtMARO0cLuT2HcEKnTPvhjV6aGeqrCB/sbNop0Kszm0jsaWU4A==", - "dev": true, - "license": "BSD-2-Clause", - "bin": { - "esparse": "bin/esparse.js", - "esvalidate": "bin/esvalidate.js" - }, - "engines": { - "node": ">=4" - } - }, - "node_modules/esquery": { - "version": "1.6.0", - "resolved": "https://registry.npmjs.org/esquery/-/esquery-1.6.0.tgz", - "integrity": "sha512-ca9pw9fomFcKPvFLXhBKUK90ZvGibiGOvRJNbjljY7s7uq/5YO4BOzcYtJqExdx99rF6aAcnRxHmcUHcz6sQsg==", - "dev": true, - "license": "BSD-3-Clause", - "dependencies": { - "estraverse": "^5.1.0" - }, - "engines": { - "node": ">=0.10" - } - }, - "node_modules/esrecurse": { - "version": "4.3.0", - "resolved": "https://registry.npmjs.org/esrecurse/-/esrecurse-4.3.0.tgz", - "integrity": "sha512-KmfKL3b6G+RXvP8N1vr3Tq1kL/oCFgn2NYXEtqP8/L3pKapUA4G8cFVaoF3SU323CD4XypR/ffioHmkti6/Tag==", - "dev": true, - "license": "BSD-2-Clause", - "dependencies": { - "estraverse": "^5.2.0" - }, - "engines": { - "node": ">=4.0" - } - }, - "node_modules/estraverse": { - "version": "5.3.0", - "resolved": "https://registry.npmjs.org/estraverse/-/estraverse-5.3.0.tgz", - "integrity": "sha512-MMdARuVEQziNTeJD8DgMqmhwR11BRQ/cBP+pLtYdSTnf3MIO8fFeiINEbX36ZdNlfU/7A9f3gUw49B3oQsvwBA==", - "dev": true, - "license": "BSD-2-Clause", - "engines": { - "node": ">=4.0" - } - }, - "node_modules/estree-walker": { - "version": "2.0.2", - "resolved": "https://registry.npmjs.org/estree-walker/-/estree-walker-2.0.2.tgz", - "integrity": "sha512-Rfkk/Mp/DL7JVje3u18FxFujQlTNR2q6QfMSMB7AvCBx91NGj/ba3kCfza0f6dVDbw7YlRf/nDrn7pQrCCyQ/w==", - "dev": true, - "license": "MIT" - }, - "node_modules/esutils": { - "version": "2.0.3", - "resolved": "https://registry.npmjs.org/esutils/-/esutils-2.0.3.tgz", - "integrity": "sha512-kVscqXk4OCp68SZ0dkgEKVi6/8ij300KBWTJq32P/dYeWTSwK41WyTxalN1eRmA5Z9UU/LX9D7FWSmV9SAYx6g==", - "dev": true, - "license": "BSD-2-Clause", - "engines": { - "node": ">=0.10.0" - } - }, - "node_modules/execa": { - "version": "5.1.1", - "resolved": "https://registry.npmjs.org/execa/-/execa-5.1.1.tgz", - "integrity": "sha512-8uSpZZocAZRBAPIEINJj3Lo9HyGitllczc27Eh5YYojjMFMn8yHMDMaUHE2Jqfq05D/wucwI4JGURyXt1vchyg==", - "dev": true, - "license": "MIT", - "dependencies": { - "cross-spawn": "^7.0.3", - "get-stream": "^6.0.0", - "human-signals": "^2.1.0", - "is-stream": "^2.0.0", - "merge-stream": "^2.0.0", - "npm-run-path": "^4.0.1", - "onetime": "^5.1.2", - "signal-exit": "^3.0.3", - "strip-final-newline": "^2.0.0" - }, - "engines": { - "node": ">=10" - }, - "funding": { - "url": "https://github.com/sindresorhus/execa?sponsor=1" - } - }, - "node_modules/execa/node_modules/signal-exit": { - "version": "3.0.7", - "resolved": "https://registry.npmjs.org/signal-exit/-/signal-exit-3.0.7.tgz", - "integrity": "sha512-wnD2ZE+l+SPC/uoS0vXeE9L1+0wuaMqKlfz9AMUo38JsyLSBWSFcHR1Rri62LZc12vLr1gb3jl7iwQhgwpAbGQ==", - "dev": true, - "license": "ISC" - }, - "node_modules/exit-x": { - "version": "0.2.2", - "resolved": "https://registry.npmjs.org/exit-x/-/exit-x-0.2.2.tgz", - "integrity": "sha512-+I6B/IkJc1o/2tiURyz/ivu/O0nKNEArIUB5O7zBrlDVJr22SCLH3xTeEry428LvFhRzIA1g8izguxJ/gbNcVQ==", - "dev": true, - "license": "MIT", - "engines": { - "node": ">= 0.8.0" - } - }, - "node_modules/expect": { - "version": "30.0.5", - "resolved": "https://registry.npmjs.org/expect/-/expect-30.0.5.tgz", - "integrity": "sha512-P0te2pt+hHI5qLJkIR+iMvS+lYUZml8rKKsohVHAGY+uClp9XVbdyYNJOIjSRpHVp8s8YqxJCiHUkSYZGr8rtQ==", - "dev": true, - "license": "MIT", - "dependencies": { - "@jest/expect-utils": "30.0.5", - "@jest/get-type": "30.0.1", - "jest-matcher-utils": "30.0.5", - "jest-message-util": "30.0.5", - "jest-mock": "30.0.5", - "jest-util": "30.0.5" - }, - "engines": { - "node": "^18.14.0 || ^20.0.0 || ^22.0.0 || >=24.0.0" - } - }, - "node_modules/fast-deep-equal": { - "version": "3.1.3", - "resolved": "https://registry.npmjs.org/fast-deep-equal/-/fast-deep-equal-3.1.3.tgz", - "integrity": "sha512-f3qQ9oQy9j2AhBe/H9VC91wLmKBCCU/gDOnKNAYG5hswO7BLKj09Hc5HYNz9cGI++xlpDCIgDaitVs03ATR84Q==", - "dev": true, - "license": "MIT" - }, - "node_modules/fast-glob": { - "version": "3.3.3", - "resolved": "https://registry.npmjs.org/fast-glob/-/fast-glob-3.3.3.tgz", - "integrity": "sha512-7MptL8U0cqcFdzIzwOTHoilX9x5BrNqye7Z/LuC7kCMRio1EMSyqRK3BEAUD7sXRq4iT4AzTVuZdhgQ2TCvYLg==", - "dev": true, - "license": "MIT", - "dependencies": { - "@nodelib/fs.stat": "^2.0.2", - "@nodelib/fs.walk": "^1.2.3", - "glob-parent": "^5.1.2", - "merge2": "^1.3.0", - "micromatch": "^4.0.8" - }, - "engines": { - "node": ">=8.6.0" - } - }, - "node_modules/fast-glob/node_modules/glob-parent": { - "version": "5.1.2", - "resolved": "https://registry.npmjs.org/glob-parent/-/glob-parent-5.1.2.tgz", - "integrity": "sha512-AOIgSQCepiJYwP3ARnGx+5VnTu2HBYdzbGP45eLw1vr3zB3vZLeyed1sC9hnbcOc9/SrMyM5RPQrkGz4aS9Zow==", - "dev": true, - "license": "ISC", - "dependencies": { - "is-glob": "^4.0.1" - }, - "engines": { - "node": ">= 6" - } - }, - "node_modules/fast-json-stable-stringify": { - "version": "2.1.0", - "resolved": "https://registry.npmjs.org/fast-json-stable-stringify/-/fast-json-stable-stringify-2.1.0.tgz", - "integrity": "sha512-lhd/wF+Lk98HZoTCtlVraHtfh5XYijIjalXck7saUtuanSDyLMxnHhSXEDJqHxD7msR8D0uCmqlkwjCV8xvwHw==", - "dev": true, - "license": "MIT" - }, - "node_modules/fast-levenshtein": { - "version": "2.0.6", - "resolved": "https://registry.npmjs.org/fast-levenshtein/-/fast-levenshtein-2.0.6.tgz", - "integrity": "sha512-DCXu6Ifhqcks7TZKY3Hxp3y6qphY5SJZmrWMDrKcERSOXWQdMhU9Ig/PYrzyw/ul9jOIyh0N4M0tbC5hodg8dw==", - "dev": true, - "license": "MIT" - }, - "node_modules/fastq": { - "version": "1.19.1", - "resolved": "https://registry.npmjs.org/fastq/-/fastq-1.19.1.tgz", - "integrity": "sha512-GwLTyxkCXjXbxqIhTsMI2Nui8huMPtnxg7krajPJAjnEG/iiOS7i+zCtWGZR9G0NBKbXKh6X9m9UIsYX/N6vvQ==", - "dev": true, - "license": "ISC", - "dependencies": { - "reusify": "^1.0.4" - } - }, - "node_modules/fb-watchman": { - "version": "2.0.2", - "resolved": "https://registry.npmjs.org/fb-watchman/-/fb-watchman-2.0.2.tgz", - "integrity": "sha512-p5161BqbuCaSnB8jIbzQHOlpgsPmK5rJVDfDKO91Axs5NC1uu3HRQm6wt9cd9/+GtQQIO53JdGXXoyDpTAsgYA==", - "dev": true, - "license": "Apache-2.0", - "dependencies": { - "bser": "2.1.1" - } - }, - "node_modules/fdir": { - "version": "6.4.6", - "resolved": "https://registry.npmjs.org/fdir/-/fdir-6.4.6.tgz", - "integrity": "sha512-hiFoqpyZcfNm1yc4u8oWCf9A2c4D3QjCrks3zmoVKVxpQRzmPNar1hUJcBG2RQHvEVGDN+Jm81ZheVLAQMK6+w==", - "dev": true, - "license": "MIT", - "peerDependencies": { - "picomatch": "^3 || ^4" - }, - "peerDependenciesMeta": { - "picomatch": { - "optional": true - } - } - }, - "node_modules/file-entry-cache": { - "version": "8.0.0", - "resolved": "https://registry.npmjs.org/file-entry-cache/-/file-entry-cache-8.0.0.tgz", - "integrity": "sha512-XXTUwCvisa5oacNGRP9SfNtYBNAMi+RPwBFmblZEF7N7swHYQS6/Zfk7SRwx4D5j3CH211YNRco1DEMNVfZCnQ==", - "dev": true, - "license": "MIT", - "dependencies": { - "flat-cache": "^4.0.0" - }, - "engines": { - "node": ">=16.0.0" - } - }, - "node_modules/fill-range": { - "version": "7.1.1", - "resolved": "https://registry.npmjs.org/fill-range/-/fill-range-7.1.1.tgz", - "integrity": "sha512-YsGpe3WHLK8ZYi4tWDg2Jy3ebRz2rXowDxnld4bkQB00cc/1Zw9AWnC0i9ztDJitivtQvaI9KaLyKrc+hBW0yg==", - "dev": true, - "license": "MIT", - "dependencies": { - "to-regex-range": "^5.0.1" - }, - "engines": { - "node": ">=8" - } - }, - "node_modules/find-up": { - "version": "4.1.0", - "resolved": "https://registry.npmjs.org/find-up/-/find-up-4.1.0.tgz", - "integrity": "sha512-PpOwAdQ/YlXQ2vj8a3h8IipDuYRi3wceVQQGYWxNINccq40Anw7BlsEXCMbt1Zt+OLA6Fq9suIpIWD0OsnISlw==", - "dev": true, - "license": "MIT", - "dependencies": { - "locate-path": "^5.0.0", - "path-exists": "^4.0.0" - }, - "engines": { - "node": ">=8" - } - }, - "node_modules/flat-cache": { - "version": "4.0.1", - "resolved": "https://registry.npmjs.org/flat-cache/-/flat-cache-4.0.1.tgz", - "integrity": "sha512-f7ccFPK3SXFHpx15UIGyRJ/FJQctuKZ0zVuN3frBo4HnK3cay9VEW0R6yPYFHC0AgqhukPzKjq22t5DmAyqGyw==", - "dev": true, - "license": "MIT", - "dependencies": { - "flatted": "^3.2.9", - "keyv": "^4.5.4" - }, - "engines": { - "node": ">=16" - } - }, - "node_modules/flatted": { - "version": "3.3.3", - "resolved": "https://registry.npmjs.org/flatted/-/flatted-3.3.3.tgz", - "integrity": "sha512-GX+ysw4PBCz0PzosHDepZGANEuFCMLrnRTiEy9McGjmkCQYwRq4A/X786G/fjM/+OjsWSU1ZrY5qyARZmO/uwg==", - "dev": true, - "license": "ISC" - }, - "node_modules/foreground-child": { - "version": "3.3.1", - "resolved": "https://registry.npmjs.org/foreground-child/-/foreground-child-3.3.1.tgz", - "integrity": "sha512-gIXjKqtFuWEgzFRJA9WCQeSJLZDjgJUOMCMzxtvFq/37KojM1BFGufqsCy0r4qSQmYLsZYMeyRqzIWOMup03sw==", - "dev": true, - "license": "ISC", - "dependencies": { - "cross-spawn": "^7.0.6", - "signal-exit": "^4.0.1" - }, - "engines": { - "node": ">=14" - }, - "funding": { - "url": "https://github.com/sponsors/isaacs" - } - }, - "node_modules/fs.realpath": { - "version": "1.0.0", - "resolved": "https://registry.npmjs.org/fs.realpath/-/fs.realpath-1.0.0.tgz", - "integrity": "sha512-OO0pH2lK6a0hZnAdau5ItzHPI6pUlvI7jMVnxUQRtw4owF2wk8lOSabtGDCTP4Ggrg2MbGnWO9X8K1t4+fGMDw==", - "dev": true, - "license": "ISC" - }, - "node_modules/fsevents": { - "version": "2.3.3", - "resolved": "https://registry.npmjs.org/fsevents/-/fsevents-2.3.3.tgz", - "integrity": "sha512-5xoDfX+fL7faATnagmWPpbFtwh/R77WmMMqqHGS65C3vvB0YHrgF+B1YmZ3441tMj5n63k0212XNoJwzlhffQw==", - "dev": true, - "hasInstallScript": true, - "license": "MIT", - "optional": true, - "os": [ - "darwin" - ], - "engines": { - "node": "^8.16.0 || ^10.6.0 || >=11.0.0" - } - }, - "node_modules/function-bind": { - "version": "1.1.2", - "resolved": "https://registry.npmjs.org/function-bind/-/function-bind-1.1.2.tgz", - "integrity": "sha512-7XHNxH7qX9xG5mIwxkhumTox/MIRNcOgDrxWsMt2pAr23WHp6MrRlN7FBSFpCpr+oVO0F744iUgR82nJMfG2SA==", - "dev": true, - "license": "MIT", - "funding": { - "url": "https://github.com/sponsors/ljharb" - } - }, - "node_modules/gensync": { - "version": "1.0.0-beta.2", - "resolved": "https://registry.npmjs.org/gensync/-/gensync-1.0.0-beta.2.tgz", - "integrity": "sha512-3hN7NaskYvMDLQY55gnW3NQ+mesEAepTqlg+VEbj7zzqEMBVNhzcGYYeqFo/TlYz6eQiFcp1HcsCZO+nGgS8zg==", - "dev": true, - "license": "MIT", - "engines": { - "node": ">=6.9.0" - } - }, - "node_modules/get-caller-file": { - "version": "2.0.5", - "resolved": "https://registry.npmjs.org/get-caller-file/-/get-caller-file-2.0.5.tgz", - "integrity": "sha512-DyFP3BM/3YHTQOCUL/w0OZHR0lpKeGrxotcHWcqNEdnltqFwXVfhEBQ94eIo34AfQpo0rGki4cyIiftY06h2Fg==", - "dev": true, - "license": "ISC", - "engines": { - "node": "6.* || 8.* || >= 10.*" - } - }, - "node_modules/get-package-type": { - "version": "0.1.0", - "resolved": "https://registry.npmjs.org/get-package-type/-/get-package-type-0.1.0.tgz", - "integrity": "sha512-pjzuKtY64GYfWizNAJ0fr9VqttZkNiK2iS430LtIHzjBEr6bX8Am2zm4sW4Ro5wjWW5cAlRL1qAMTcXbjNAO2Q==", - "dev": true, - "license": "MIT", - "engines": { - "node": ">=8.0.0" - } - }, - "node_modules/get-stream": { - "version": "6.0.1", - "resolved": "https://registry.npmjs.org/get-stream/-/get-stream-6.0.1.tgz", - "integrity": "sha512-ts6Wi+2j3jQjqi70w5AlN8DFnkSwC+MqmxEzdEALB2qXZYV3X/b1CTfgPLGJNMeAWxdPfU8FO1ms3NUfaHCPYg==", - "dev": true, - "license": "MIT", - "engines": { - "node": ">=10" - }, - "funding": { - "url": "https://github.com/sponsors/sindresorhus" - } - }, - "node_modules/glob": { - "version": "10.4.5", - "resolved": "https://registry.npmjs.org/glob/-/glob-10.4.5.tgz", - "integrity": "sha512-7Bv8RF0k6xjo7d4A/PxYLbUCfb6c+Vpd2/mB2yRDlew7Jb5hEXiCD9ibfO7wpk8i4sevK6DFny9h7EYbM3/sHg==", - "dev": true, - "license": "ISC", - "dependencies": { - "foreground-child": "^3.1.0", - "jackspeak": "^3.1.2", - "minimatch": "^9.0.4", - "minipass": "^7.1.2", - "package-json-from-dist": "^1.0.0", - "path-scurry": "^1.11.1" - }, - "bin": { - "glob": "dist/esm/bin.mjs" - }, - "funding": { - "url": "https://github.com/sponsors/isaacs" - } - }, - "node_modules/glob-parent": { - "version": "6.0.2", - "resolved": "https://registry.npmjs.org/glob-parent/-/glob-parent-6.0.2.tgz", - "integrity": "sha512-XxwI8EOhVQgWp6iDL+3b0r86f4d6AX6zSU55HfB4ydCEuXLXc5FcYeOu+nnGftS4TEju/11rt4KJPTMgbfmv4A==", - "dev": true, - "license": "ISC", - "dependencies": { - "is-glob": "^4.0.3" - }, - "engines": { - "node": ">=10.13.0" - } - }, - "node_modules/globals": { - "version": "16.3.0", - "resolved": "https://registry.npmjs.org/globals/-/globals-16.3.0.tgz", - "integrity": "sha512-bqWEnJ1Nt3neqx2q5SFfGS8r/ahumIakg3HcwtNlrVlwXIeNumWn/c7Pn/wKzGhf6SaW6H6uWXLqC30STCMchQ==", - "dev": true, - "license": "MIT", - "engines": { - "node": ">=18" - }, - "funding": { - "url": "https://github.com/sponsors/sindresorhus" - } - }, - "node_modules/graceful-fs": { - "version": "4.2.11", - "resolved": "https://registry.npmjs.org/graceful-fs/-/graceful-fs-4.2.11.tgz", - "integrity": "sha512-RbJ5/jmFcNNCcDV5o9eTnBLJ/HszWV0P73bc+Ff4nS/rJj+YaS6IGyiOL0VoBYX+l1Wrl3k63h/KrH+nhJ0XvQ==", - "dev": true, - "license": "ISC" - }, - "node_modules/graphemer": { - "version": "1.4.0", - "resolved": "https://registry.npmjs.org/graphemer/-/graphemer-1.4.0.tgz", - "integrity": "sha512-EtKwoO6kxCL9WO5xipiHTZlSzBm7WLT627TqC/uVRd0HKmq8NXyebnNYxDoBi7wt8eTWrUrKXCOVaFq9x1kgag==", - "dev": true, - "license": "MIT" - }, - "node_modules/has-flag": { - "version": "4.0.0", - "resolved": "https://registry.npmjs.org/has-flag/-/has-flag-4.0.0.tgz", - "integrity": "sha512-EykJT/Q1KjTWctppgIAgfSO0tKVuZUjhgMr17kqTumMl6Afv3EISleU7qZUzoXDFTAHTDC4NOoG/ZxU3EvlMPQ==", - "dev": true, - "license": "MIT", - "engines": { - "node": ">=8" - } - }, - "node_modules/hasown": { - "version": "2.0.2", - "resolved": "https://registry.npmjs.org/hasown/-/hasown-2.0.2.tgz", - "integrity": "sha512-0hJU9SCPvmMzIBdZFqNPXWa6dqh7WdH0cII9y+CyS8rG3nL48Bclra9HmKhVVUHyPWNH5Y7xDwAB7bfgSjkUMQ==", - "dev": true, - "license": "MIT", - "dependencies": { - "function-bind": "^1.1.2" - }, - "engines": { - "node": ">= 0.4" - } - }, - "node_modules/html-escaper": { - "version": "2.0.2", - "resolved": "https://registry.npmjs.org/html-escaper/-/html-escaper-2.0.2.tgz", - "integrity": "sha512-H2iMtd0I4Mt5eYiapRdIDjp+XzelXQ0tFE4JS7YFwFevXXMmOp9myNrUvCg0D6ws8iqkRPBfKHgbwig1SmlLfg==", - "dev": true, - "license": "MIT" - }, - "node_modules/human-signals": { - "version": "2.1.0", - "resolved": "https://registry.npmjs.org/human-signals/-/human-signals-2.1.0.tgz", - "integrity": "sha512-B4FFZ6q/T2jhhksgkbEW3HBvWIfDW85snkQgawt07S7J5QXTk6BkNV+0yAeZrM5QpMAdYlocGoljn0sJ/WQkFw==", - "dev": true, - "license": "Apache-2.0", - "engines": { - "node": ">=10.17.0" - } - }, - "node_modules/ignore": { - "version": "7.0.5", - "resolved": "https://registry.npmjs.org/ignore/-/ignore-7.0.5.tgz", - "integrity": "sha512-Hs59xBNfUIunMFgWAbGX5cq6893IbWg4KnrjbYwX3tx0ztorVgTDA6B2sxf8ejHJ4wz8BqGUMYlnzNBer5NvGg==", - "dev": true, - "license": "MIT", - "engines": { - "node": ">= 4" - } - }, - "node_modules/import-fresh": { - "version": "3.3.1", - "resolved": "https://registry.npmjs.org/import-fresh/-/import-fresh-3.3.1.tgz", - "integrity": "sha512-TR3KfrTZTYLPB6jUjfx6MF9WcWrHL9su5TObK4ZkYgBdWKPOFoSoQIdEuTuR82pmtxH2spWG9h6etwfr1pLBqQ==", - "dev": true, - "license": "MIT", - "dependencies": { - "parent-module": "^1.0.0", - "resolve-from": "^4.0.0" - }, - "engines": { - "node": ">=6" - }, - "funding": { - "url": "https://github.com/sponsors/sindresorhus" - } - }, - "node_modules/import-fresh/node_modules/resolve-from": { - "version": "4.0.0", - "resolved": "https://registry.npmjs.org/resolve-from/-/resolve-from-4.0.0.tgz", - "integrity": "sha512-pb/MYmXstAkysRFx8piNI1tGFNQIFA3vkE3Gq4EuA1dF6gHp/+vgZqsCGJapvy8N3Q+4o7FwvquPJcnZ7RYy4g==", - "dev": true, - "license": "MIT", - "engines": { - "node": ">=4" - } - }, - "node_modules/import-local": { - "version": "3.2.0", - "resolved": "https://registry.npmjs.org/import-local/-/import-local-3.2.0.tgz", - "integrity": "sha512-2SPlun1JUPWoM6t3F0dw0FkCF/jWY8kttcY4f599GLTSjh2OCuuhdTkJQsEcZzBqbXZGKMK2OqW1oZsjtf/gQA==", - "dev": true, - "license": "MIT", - "dependencies": { - "pkg-dir": "^4.2.0", - "resolve-cwd": "^3.0.0" - }, - "bin": { - "import-local-fixture": "fixtures/cli.js" - }, - "engines": { - "node": ">=8" - }, - "funding": { - "url": "https://github.com/sponsors/sindresorhus" - } - }, - "node_modules/imurmurhash": { - "version": "0.1.4", - "resolved": "https://registry.npmjs.org/imurmurhash/-/imurmurhash-0.1.4.tgz", - "integrity": "sha512-JmXMZ6wuvDmLiHEml9ykzqO6lwFbof0GG4IkcGaENdCRDDmMVnny7s5HsIgHCbaq0w2MyPhDqkhTUgS2LU2PHA==", - "dev": true, - "license": "MIT", - "engines": { - "node": ">=0.8.19" - } - }, - "node_modules/inflight": { - "version": "1.0.6", - "resolved": "https://registry.npmjs.org/inflight/-/inflight-1.0.6.tgz", - "integrity": "sha512-k92I/b08q4wvFscXCLvqfsHCrjrF7yiXsQuIVvVE7N82W3+aqpzuUdBbfhWcy/FZR3/4IgflMgKLOsvPDrGCJA==", - "deprecated": "This module is not supported, and leaks memory. Do not use it. Check out lru-cache if you want a good and tested way to coalesce async requests by a key value, which is much more comprehensive and powerful.", - "dev": true, - "license": "ISC", - "dependencies": { - "once": "^1.3.0", - "wrappy": "1" - } - }, - "node_modules/inherits": { - "version": "2.0.4", - "resolved": "https://registry.npmjs.org/inherits/-/inherits-2.0.4.tgz", - "integrity": "sha512-k/vGaX4/Yla3WzyMCvTQOXYeIHvqOKtnqBduzTHpzpQZzAskKMhZ2K+EnBiSM9zGSoIFeMpXKxa4dYeZIQqewQ==", - "dev": true, - "license": "ISC" - }, - "node_modules/is-arrayish": { - "version": "0.2.1", - "resolved": "https://registry.npmjs.org/is-arrayish/-/is-arrayish-0.2.1.tgz", - "integrity": "sha512-zz06S8t0ozoDXMG+ube26zeCTNXcKIPJZJi8hBrF4idCLms4CG9QtK7qBl1boi5ODzFpjswb5JPmHCbMpjaYzg==", - "dev": true, - "license": "MIT" - }, - "node_modules/is-core-module": { - "version": "2.16.1", - "resolved": "https://registry.npmjs.org/is-core-module/-/is-core-module-2.16.1.tgz", - "integrity": "sha512-UfoeMA6fIJ8wTYFEUjelnaGI67v6+N7qXJEvQuIGa99l4xsCruSYOVSQ0uPANn4dAzm8lkYPaKLrrijLq7x23w==", - "dev": true, - "license": "MIT", - "dependencies": { - "hasown": "^2.0.2" - }, - "engines": { - "node": ">= 0.4" - }, - "funding": { - "url": "https://github.com/sponsors/ljharb" - } - }, - "node_modules/is-extglob": { - "version": "2.1.1", - "resolved": "https://registry.npmjs.org/is-extglob/-/is-extglob-2.1.1.tgz", - "integrity": "sha512-SbKbANkN603Vi4jEZv49LeVJMn4yGwsbzZworEoyEiutsN3nJYdbO36zfhGJ6QEDpOZIFkDtnq5JRxmvl3jsoQ==", - "dev": true, - "license": "MIT", - "engines": { - "node": ">=0.10.0" - } - }, - "node_modules/is-fullwidth-code-point": { - "version": "3.0.0", - "resolved": "https://registry.npmjs.org/is-fullwidth-code-point/-/is-fullwidth-code-point-3.0.0.tgz", - "integrity": "sha512-zymm5+u+sCsSWyD9qNaejV3DFvhCKclKdizYaJUuHA83RLjb7nSuGnddCHGv0hk+KY7BMAlsWeK4Ueg6EV6XQg==", - "dev": true, - "license": "MIT", - "engines": { - "node": ">=8" - } - }, - "node_modules/is-generator-fn": { - "version": "2.1.0", - "resolved": "https://registry.npmjs.org/is-generator-fn/-/is-generator-fn-2.1.0.tgz", - "integrity": "sha512-cTIB4yPYL/Grw0EaSzASzg6bBy9gqCofvWN8okThAYIxKJZC+udlRAmGbM0XLeniEJSs8uEgHPGuHSe1XsOLSQ==", - "dev": true, - "license": "MIT", - "engines": { - "node": ">=6" - } - }, - "node_modules/is-glob": { - "version": "4.0.3", - "resolved": "https://registry.npmjs.org/is-glob/-/is-glob-4.0.3.tgz", - "integrity": "sha512-xelSayHH36ZgE7ZWhli7pW34hNbNl8Ojv5KVmkJD4hBdD3th8Tfk9vYasLM+mXWOZhFkgZfxhLSnrwRr4elSSg==", - "dev": true, - "license": "MIT", - "dependencies": { - "is-extglob": "^2.1.1" - }, - "engines": { - "node": ">=0.10.0" - } - }, - "node_modules/is-module": { - "version": "1.0.0", - "resolved": "https://registry.npmjs.org/is-module/-/is-module-1.0.0.tgz", - "integrity": "sha512-51ypPSPCoTEIN9dy5Oy+h4pShgJmPCygKfyRCISBI+JoWT/2oJvK8QPxmwv7b/p239jXrm9M1mlQbyKJ5A152g==", - "dev": true, - "license": "MIT" + "optional": true, + "os": [ + "win32" + ] }, - "node_modules/is-number": { - "version": "7.0.0", - "resolved": "https://registry.npmjs.org/is-number/-/is-number-7.0.0.tgz", - "integrity": "sha512-41Cifkg6e8TylSpdtTpeLVMqvSBEVzTttHvERD741+pnZ8ANv0004MRL43QKPDlK9cGvNp6NZWZUBlbGXYxxng==", + "node_modules/@rollup/rollup-win32-x64-gnu": { + "version": "4.53.2", + "resolved": "https://registry.npmjs.org/@rollup/rollup-win32-x64-gnu/-/rollup-win32-x64-gnu-4.53.2.tgz", + "integrity": "sha512-S6YojNVrHybQis2lYov1sd+uj7K0Q05NxHcGktuMMdIQ2VixGwAfbJ23NnlvvVV1bdpR2m5MsNBViHJKcA4ADw==", + "cpu": [ + "x64" + ], "dev": true, "license": "MIT", - "engines": { - "node": ">=0.12.0" - } + "optional": true, + "os": [ + "win32" + ] }, - "node_modules/is-stream": { - "version": "2.0.1", - "resolved": "https://registry.npmjs.org/is-stream/-/is-stream-2.0.1.tgz", - "integrity": "sha512-hFoiJiTl63nn+kstHGBtewWSKnQLpyb155KHheA1l39uvtO9nWIop1p3udqPcUd/xbF1VLMO4n7OI6p7RbngDg==", + "node_modules/@rollup/rollup-win32-x64-msvc": { + "version": "4.53.2", + "resolved": "https://registry.npmjs.org/@rollup/rollup-win32-x64-msvc/-/rollup-win32-x64-msvc-4.53.2.tgz", + "integrity": "sha512-k+/Rkcyx//P6fetPoLMb8pBeqJBNGx81uuf7iljX9++yNBVRDQgD04L+SVXmXmh5ZP4/WOp4mWF0kmi06PW2tA==", + "cpu": [ + "x64" + ], "dev": true, "license": "MIT", - "engines": { - "node": ">=8" - }, - "funding": { - "url": "https://github.com/sponsors/sindresorhus" - } + "optional": true, + "os": [ + "win32" + ] }, - "node_modules/isexe": { - "version": "2.0.0", - "resolved": "https://registry.npmjs.org/isexe/-/isexe-2.0.0.tgz", - "integrity": "sha512-RHxMLp9lnKHGHRng9QFhRCMbYAcVpn69smSGcq3f36xjgVVWThj4qqLbTLlq7Ssj8B+fIQ1EuCEGI2lKsyQeIw==", + "node_modules/@types/estree": { + "version": "1.0.8", + "resolved": "https://registry.npmjs.org/@types/estree/-/estree-1.0.8.tgz", + "integrity": "sha512-dWHzHa2WqEXI/O1E9OjrocMTKJl2mSrEolh1Iomrv6U+JuNwaHXsXx9bLu5gG7BUWFIN0skIQJQ/L1rIex4X6w==", "dev": true, - "license": "ISC" + "license": "MIT" }, - "node_modules/istanbul-lib-coverage": { - "version": "3.2.2", - "resolved": "https://registry.npmjs.org/istanbul-lib-coverage/-/istanbul-lib-coverage-3.2.2.tgz", - "integrity": "sha512-O8dpsF+r0WV/8MNRKfnmrtCWhuKjxrq2w+jpzBL5UZKTi2LeVWnWOmWRxFlesJONmc+wLAGvKQZEOanko0LFTg==", + "node_modules/@types/json-schema": { + "version": "7.0.15", + "resolved": "https://registry.npmjs.org/@types/json-schema/-/json-schema-7.0.15.tgz", + "integrity": "sha512-5+fP8P8MFNC+AyZCDxrB2pkZFPGzqQWUzpSeuuVLvm8VMcorNYavBqoFcxK8bQz4Qsbn4oUEEem4wDLfcysGHA==", "dev": true, - "license": "BSD-3-Clause", - "engines": { - "node": ">=8" - } + "license": "MIT" + }, + "node_modules/@types/resolve": { + "version": "1.20.2", + "resolved": "https://registry.npmjs.org/@types/resolve/-/resolve-1.20.2.tgz", + "integrity": "sha512-60BCwRFOZCQhDncwQdxxeOEEkbc5dIMccYLwbxsS4TUNeVECQ/pBJ0j09mrHOl/JJvpRPGwO9SvE4nR2Nb/a4Q==", + "dev": true, + "license": "MIT" }, - "node_modules/istanbul-lib-instrument": { - "version": "6.0.3", - "resolved": "https://registry.npmjs.org/istanbul-lib-instrument/-/istanbul-lib-instrument-6.0.3.tgz", - "integrity": "sha512-Vtgk7L/R2JHyyGW07spoFlB8/lpjiOLTjMdms6AFMraYt3BaJauod/NGrfnVG/y4Ix1JEuMRPDPEj2ua+zz1/Q==", + "node_modules/@typescript-eslint/eslint-plugin": { + "version": "8.46.4", + "resolved": "https://registry.npmjs.org/@typescript-eslint/eslint-plugin/-/eslint-plugin-8.46.4.tgz", + "integrity": "sha512-R48VhmTJqplNyDxCyqqVkFSZIx1qX6PzwqgcXn1olLrzxcSBDlOsbtcnQuQhNtnNiJ4Xe5gREI1foajYaYU2Vg==", "dev": true, - "license": "BSD-3-Clause", + "license": "MIT", "dependencies": { - "@babel/core": "^7.23.9", - "@babel/parser": "^7.23.9", - "@istanbuljs/schema": "^0.1.3", - "istanbul-lib-coverage": "^3.2.0", - "semver": "^7.5.4" + "@eslint-community/regexpp": "^4.10.0", + "@typescript-eslint/scope-manager": "8.46.4", + "@typescript-eslint/type-utils": "8.46.4", + "@typescript-eslint/utils": "8.46.4", + "@typescript-eslint/visitor-keys": "8.46.4", + "graphemer": "^1.4.0", + "ignore": "^7.0.0", + "natural-compare": "^1.4.0", + "ts-api-utils": "^2.1.0" }, "engines": { - "node": ">=10" - } - }, - "node_modules/istanbul-lib-instrument/node_modules/semver": { - "version": "7.7.2", - "resolved": "https://registry.npmjs.org/semver/-/semver-7.7.2.tgz", - "integrity": "sha512-RF0Fw+rO5AMf9MAyaRXI4AV0Ulj5lMHqVxxdSgiVbixSCXoEmmX/jk0CuJw4+3SqroYO9VoUh+HcuJivvtJemA==", - "dev": true, - "license": "ISC", - "bin": { - "semver": "bin/semver.js" + "node": "^18.18.0 || ^20.9.0 || >=21.1.0" }, - "engines": { - "node": ">=10" + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/typescript-eslint" + }, + "peerDependencies": { + "@typescript-eslint/parser": "^8.46.4", + "eslint": "^8.57.0 || ^9.0.0", + "typescript": ">=4.8.4 <6.0.0" } }, - "node_modules/istanbul-lib-report": { - "version": "3.0.1", - "resolved": "https://registry.npmjs.org/istanbul-lib-report/-/istanbul-lib-report-3.0.1.tgz", - "integrity": "sha512-GCfE1mtsHGOELCU8e/Z7YWzpmybrx/+dSTfLrvY8qRmaY6zXTKWn6WQIjaAFw069icm6GVMNkgu0NzI4iPZUNw==", + "node_modules/@typescript-eslint/parser": { + "version": "8.46.4", + "resolved": "https://registry.npmjs.org/@typescript-eslint/parser/-/parser-8.46.4.tgz", + "integrity": "sha512-tK3GPFWbirvNgsNKto+UmB/cRtn6TZfyw0D6IKrW55n6Vbs7KJoZtI//kpTKzE/DUmmnAFD8/Ca46s7Obs92/w==", "dev": true, - "license": "BSD-3-Clause", + "license": "MIT", "dependencies": { - "istanbul-lib-coverage": "^3.0.0", - "make-dir": "^4.0.0", - "supports-color": "^7.1.0" + "@typescript-eslint/scope-manager": "8.46.4", + "@typescript-eslint/types": "8.46.4", + "@typescript-eslint/typescript-estree": "8.46.4", + "@typescript-eslint/visitor-keys": "8.46.4", + "debug": "^4.3.4" }, "engines": { - "node": ">=10" + "node": "^18.18.0 || ^20.9.0 || >=21.1.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/typescript-eslint" + }, + "peerDependencies": { + "eslint": "^8.57.0 || ^9.0.0", + "typescript": ">=4.8.4 <6.0.0" } }, - "node_modules/istanbul-lib-source-maps": { - "version": "5.0.6", - "resolved": "https://registry.npmjs.org/istanbul-lib-source-maps/-/istanbul-lib-source-maps-5.0.6.tgz", - "integrity": "sha512-yg2d+Em4KizZC5niWhQaIomgf5WlL4vOOjZ5xGCmF8SnPE/mDWWXgvRExdcpCgh9lLRRa1/fSYp2ymmbJ1pI+A==", + "node_modules/@typescript-eslint/project-service": { + "version": "8.46.4", + "resolved": "https://registry.npmjs.org/@typescript-eslint/project-service/-/project-service-8.46.4.tgz", + "integrity": "sha512-nPiRSKuvtTN+no/2N1kt2tUh/HoFzeEgOm9fQ6XQk4/ApGqjx0zFIIaLJ6wooR1HIoozvj2j6vTi/1fgAz7UYQ==", "dev": true, - "license": "BSD-3-Clause", + "license": "MIT", "dependencies": { - "@jridgewell/trace-mapping": "^0.3.23", - "debug": "^4.1.1", - "istanbul-lib-coverage": "^3.0.0" + "@typescript-eslint/tsconfig-utils": "^8.46.4", + "@typescript-eslint/types": "^8.46.4", + "debug": "^4.3.4" }, "engines": { - "node": ">=10" + "node": "^18.18.0 || ^20.9.0 || >=21.1.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/typescript-eslint" + }, + "peerDependencies": { + "typescript": ">=4.8.4 <6.0.0" } }, - "node_modules/istanbul-reports": { - "version": "3.1.7", - "resolved": "https://registry.npmjs.org/istanbul-reports/-/istanbul-reports-3.1.7.tgz", - "integrity": "sha512-BewmUXImeuRk2YY0PVbxgKAysvhRPUQE0h5QRM++nVWyubKGV0l8qQ5op8+B2DOmwSe63Jivj0BjkPQVf8fP5g==", + "node_modules/@typescript-eslint/scope-manager": { + "version": "8.46.4", + "resolved": "https://registry.npmjs.org/@typescript-eslint/scope-manager/-/scope-manager-8.46.4.tgz", + "integrity": "sha512-tMDbLGXb1wC+McN1M6QeDx7P7c0UWO5z9CXqp7J8E+xGcJuUuevWKxuG8j41FoweS3+L41SkyKKkia16jpX7CA==", "dev": true, - "license": "BSD-3-Clause", + "license": "MIT", "dependencies": { - "html-escaper": "^2.0.0", - "istanbul-lib-report": "^3.0.0" + "@typescript-eslint/types": "8.46.4", + "@typescript-eslint/visitor-keys": "8.46.4" }, "engines": { - "node": ">=8" + "node": "^18.18.0 || ^20.9.0 || >=21.1.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/typescript-eslint" } }, - "node_modules/jackspeak": { - "version": "3.4.3", - "resolved": "https://registry.npmjs.org/jackspeak/-/jackspeak-3.4.3.tgz", - "integrity": "sha512-OGlZQpz2yfahA/Rd1Y8Cd9SIEsqvXkLVoSw/cgwhnhFMDbsQFeZYoJJ7bIZBS9BcamUW96asq/npPWugM+RQBw==", + "node_modules/@typescript-eslint/tsconfig-utils": { + "version": "8.46.4", + "resolved": "https://registry.npmjs.org/@typescript-eslint/tsconfig-utils/-/tsconfig-utils-8.46.4.tgz", + "integrity": "sha512-+/XqaZPIAk6Cjg7NWgSGe27X4zMGqrFqZ8atJsX3CWxH/jACqWnrWI68h7nHQld0y+k9eTTjb9r+KU4twLoo9A==", "dev": true, - "license": "BlueOak-1.0.0", - "dependencies": { - "@isaacs/cliui": "^8.0.2" + "license": "MIT", + "engines": { + "node": "^18.18.0 || ^20.9.0 || >=21.1.0" }, "funding": { - "url": "https://github.com/sponsors/isaacs" + "type": "opencollective", + "url": "https://opencollective.com/typescript-eslint" }, - "optionalDependencies": { - "@pkgjs/parseargs": "^0.11.0" + "peerDependencies": { + "typescript": ">=4.8.4 <6.0.0" } }, - "node_modules/jest": { - "version": "30.0.5", - "resolved": "https://registry.npmjs.org/jest/-/jest-30.0.5.tgz", - "integrity": "sha512-y2mfcJywuTUkvLm2Lp1/pFX8kTgMO5yyQGq/Sk/n2mN7XWYp4JsCZ/QXW34M8YScgk8bPZlREH04f6blPnoHnQ==", + "node_modules/@typescript-eslint/type-utils": { + "version": "8.46.4", + "resolved": "https://registry.npmjs.org/@typescript-eslint/type-utils/-/type-utils-8.46.4.tgz", + "integrity": "sha512-V4QC8h3fdT5Wro6vANk6eojqfbv5bpwHuMsBcJUJkqs2z5XnYhJzyz9Y02eUmF9u3PgXEUiOt4w4KHR3P+z0PQ==", "dev": true, "license": "MIT", "dependencies": { - "@jest/core": "30.0.5", - "@jest/types": "30.0.5", - "import-local": "^3.2.0", - "jest-cli": "30.0.5" - }, - "bin": { - "jest": "bin/jest.js" + "@typescript-eslint/types": "8.46.4", + "@typescript-eslint/typescript-estree": "8.46.4", + "@typescript-eslint/utils": "8.46.4", + "debug": "^4.3.4", + "ts-api-utils": "^2.1.0" }, "engines": { - "node": "^18.14.0 || ^20.0.0 || ^22.0.0 || >=24.0.0" + "node": "^18.18.0 || ^20.9.0 || >=21.1.0" }, - "peerDependencies": { - "node-notifier": "^8.0.1 || ^9.0.0 || ^10.0.0" + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/typescript-eslint" }, - "peerDependenciesMeta": { - "node-notifier": { - "optional": true - } + "peerDependencies": { + "eslint": "^8.57.0 || ^9.0.0", + "typescript": ">=4.8.4 <6.0.0" } }, - "node_modules/jest-changed-files": { - "version": "30.0.5", - "resolved": "https://registry.npmjs.org/jest-changed-files/-/jest-changed-files-30.0.5.tgz", - "integrity": "sha512-bGl2Ntdx0eAwXuGpdLdVYVr5YQHnSZlQ0y9HVDu565lCUAe9sj6JOtBbMmBBikGIegne9piDDIOeiLVoqTkz4A==", + "node_modules/@typescript-eslint/types": { + "version": "8.46.4", + "resolved": "https://registry.npmjs.org/@typescript-eslint/types/-/types-8.46.4.tgz", + "integrity": "sha512-USjyxm3gQEePdUwJBFjjGNG18xY9A2grDVGuk7/9AkjIF1L+ZrVnwR5VAU5JXtUnBL/Nwt3H31KlRDaksnM7/w==", "dev": true, "license": "MIT", - "dependencies": { - "execa": "^5.1.1", - "jest-util": "30.0.5", - "p-limit": "^3.1.0" - }, "engines": { - "node": "^18.14.0 || ^20.0.0 || ^22.0.0 || >=24.0.0" + "node": "^18.18.0 || ^20.9.0 || >=21.1.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/typescript-eslint" } }, - "node_modules/jest-circus": { - "version": "30.0.5", - "resolved": "https://registry.npmjs.org/jest-circus/-/jest-circus-30.0.5.tgz", - "integrity": "sha512-h/sjXEs4GS+NFFfqBDYT7y5Msfxh04EwWLhQi0F8kuWpe+J/7tICSlswU8qvBqumR3kFgHbfu7vU6qruWWBPug==", + "node_modules/@typescript-eslint/typescript-estree": { + "version": "8.46.4", + "resolved": "https://registry.npmjs.org/@typescript-eslint/typescript-estree/-/typescript-estree-8.46.4.tgz", + "integrity": "sha512-7oV2qEOr1d4NWNmpXLR35LvCfOkTNymY9oyW+lUHkmCno7aOmIf/hMaydnJBUTBMRCOGZh8YjkFOc8dadEoNGA==", "dev": true, "license": "MIT", "dependencies": { - "@jest/environment": "30.0.5", - "@jest/expect": "30.0.5", - "@jest/test-result": "30.0.5", - "@jest/types": "30.0.5", - "@types/node": "*", - "chalk": "^4.1.2", - "co": "^4.6.0", - "dedent": "^1.6.0", - "is-generator-fn": "^2.1.0", - "jest-each": "30.0.5", - "jest-matcher-utils": "30.0.5", - "jest-message-util": "30.0.5", - "jest-runtime": "30.0.5", - "jest-snapshot": "30.0.5", - "jest-util": "30.0.5", - "p-limit": "^3.1.0", - "pretty-format": "30.0.5", - "pure-rand": "^7.0.0", - "slash": "^3.0.0", - "stack-utils": "^2.0.6" + "@typescript-eslint/project-service": "8.46.4", + "@typescript-eslint/tsconfig-utils": "8.46.4", + "@typescript-eslint/types": "8.46.4", + "@typescript-eslint/visitor-keys": "8.46.4", + "debug": "^4.3.4", + "fast-glob": "^3.3.2", + "is-glob": "^4.0.3", + "minimatch": "^9.0.4", + "semver": "^7.6.0", + "ts-api-utils": "^2.1.0" }, "engines": { - "node": "^18.14.0 || ^20.0.0 || ^22.0.0 || >=24.0.0" + "node": "^18.18.0 || ^20.9.0 || >=21.1.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/typescript-eslint" + }, + "peerDependencies": { + "typescript": ">=4.8.4 <6.0.0" } }, - "node_modules/jest-circus/node_modules/ansi-styles": { - "version": "4.3.0", - "resolved": "https://registry.npmjs.org/ansi-styles/-/ansi-styles-4.3.0.tgz", - "integrity": "sha512-zbB9rCJAT1rbjiVDb2hqKFHNYLxgtk8NURxZ3IZwD3F6NtxbXZQCnnSi1Lkx+IDohdPlFp222wVALIheZJQSEg==", + "node_modules/@typescript-eslint/utils": { + "version": "8.46.4", + "resolved": "https://registry.npmjs.org/@typescript-eslint/utils/-/utils-8.46.4.tgz", + "integrity": "sha512-AbSv11fklGXV6T28dp2Me04Uw90R2iJ30g2bgLz529Koehrmkbs1r7paFqr1vPCZi7hHwYxYtxfyQMRC8QaVSg==", "dev": true, "license": "MIT", "dependencies": { - "color-convert": "^2.0.1" + "@eslint-community/eslint-utils": "^4.7.0", + "@typescript-eslint/scope-manager": "8.46.4", + "@typescript-eslint/types": "8.46.4", + "@typescript-eslint/typescript-estree": "8.46.4" }, "engines": { - "node": ">=8" + "node": "^18.18.0 || ^20.9.0 || >=21.1.0" }, "funding": { - "url": "https://github.com/chalk/ansi-styles?sponsor=1" + "type": "opencollective", + "url": "https://opencollective.com/typescript-eslint" + }, + "peerDependencies": { + "eslint": "^8.57.0 || ^9.0.0", + "typescript": ">=4.8.4 <6.0.0" } }, - "node_modules/jest-circus/node_modules/chalk": { - "version": "4.1.2", - "resolved": "https://registry.npmjs.org/chalk/-/chalk-4.1.2.tgz", - "integrity": "sha512-oKnbhFyRIXpUuez8iBMmyEa4nbj4IOQyuhc/wy9kY7/WVPcwIO9VA668Pu8RkO7+0G76SLROeyw9CpQ061i4mA==", + "node_modules/@typescript-eslint/visitor-keys": { + "version": "8.46.4", + "resolved": "https://registry.npmjs.org/@typescript-eslint/visitor-keys/-/visitor-keys-8.46.4.tgz", + "integrity": "sha512-/++5CYLQqsO9HFGLI7APrxBJYo+5OCMpViuhV8q5/Qa3o5mMrF//eQHks+PXcsAVaLdn817fMuS7zqoXNNZGaw==", "dev": true, "license": "MIT", "dependencies": { - "ansi-styles": "^4.1.0", - "supports-color": "^7.1.0" + "@typescript-eslint/types": "8.46.4", + "eslint-visitor-keys": "^4.2.1" }, "engines": { - "node": ">=10" + "node": "^18.18.0 || ^20.9.0 || >=21.1.0" }, "funding": { - "url": "https://github.com/chalk/chalk?sponsor=1" + "type": "opencollective", + "url": "https://opencollective.com/typescript-eslint" + } + }, + "node_modules/@typescript-eslint/visitor-keys/node_modules/eslint-visitor-keys": { + "version": "4.2.1", + "resolved": "https://registry.npmjs.org/eslint-visitor-keys/-/eslint-visitor-keys-4.2.1.tgz", + "integrity": "sha512-Uhdk5sfqcee/9H/rCOJikYz67o0a2Tw2hGRPOG2Y1R2dg7brRe1uG0yaNQDHu+TO/uQPF/5eCapvYSmHUjt7JQ==", + "dev": true, + "license": "Apache-2.0", + "engines": { + "node": "^18.18.0 || ^20.9.0 || >=21.1.0" + }, + "funding": { + "url": "https://opencollective.com/eslint" } }, - "node_modules/jest-cli": { - "version": "30.0.5", - "resolved": "https://registry.npmjs.org/jest-cli/-/jest-cli-30.0.5.tgz", - "integrity": "sha512-Sa45PGMkBZzF94HMrlX4kUyPOwUpdZasaliKN3mifvDmkhLYqLLg8HQTzn6gq7vJGahFYMQjXgyJWfYImKZzOw==", + "node_modules/acorn": { + "version": "8.15.0", + "resolved": "https://registry.npmjs.org/acorn/-/acorn-8.15.0.tgz", + "integrity": "sha512-NZyJarBfL7nWwIq+FDL6Zp/yHEhePMNnnJ0y3qfieCrmNvYct8uvtiV41UvlSe6apAfk0fY1FbWx+NwfmpvtTg==", "dev": true, "license": "MIT", - "dependencies": { - "@jest/core": "30.0.5", - "@jest/test-result": "30.0.5", - "@jest/types": "30.0.5", - "chalk": "^4.1.2", - "exit-x": "^0.2.2", - "import-local": "^3.2.0", - "jest-config": "30.0.5", - "jest-util": "30.0.5", - "jest-validate": "30.0.5", - "yargs": "^17.7.2" - }, "bin": { - "jest": "bin/jest.js" + "acorn": "bin/acorn" }, "engines": { - "node": "^18.14.0 || ^20.0.0 || ^22.0.0 || >=24.0.0" - }, + "node": ">=0.4.0" + } + }, + "node_modules/acorn-jsx": { + "version": "5.3.2", + "resolved": "https://registry.npmjs.org/acorn-jsx/-/acorn-jsx-5.3.2.tgz", + "integrity": "sha512-rq9s+JNhf0IChjtDXxllJ7g41oZk5SlXtp0LHwyA5cejwn7vKmKp4pPri6YEePv2PU65sAsegbXtIinmDFDXgQ==", + "dev": true, + "license": "MIT", "peerDependencies": { - "node-notifier": "^8.0.1 || ^9.0.0 || ^10.0.0" + "acorn": "^6.0.0 || ^7.0.0 || ^8.0.0" + } + }, + "node_modules/ajv": { + "version": "6.12.6", + "resolved": "https://registry.npmjs.org/ajv/-/ajv-6.12.6.tgz", + "integrity": "sha512-j3fVLgvTo527anyYyJOGTYJbG+vnnQYvE0m5mmkc1TK+nxAppkCLMIL0aZ4dblVCNoGShhm+kzE4ZUykBoMg4g==", + "dev": true, + "license": "MIT", + "dependencies": { + "fast-deep-equal": "^3.1.1", + "fast-json-stable-stringify": "^2.0.0", + "json-schema-traverse": "^0.4.1", + "uri-js": "^4.2.2" }, - "peerDependenciesMeta": { - "node-notifier": { - "optional": true - } + "funding": { + "type": "github", + "url": "https://github.com/sponsors/epoberezkin" } }, - "node_modules/jest-cli/node_modules/ansi-styles": { + "node_modules/ansi-styles": { "version": "4.3.0", "resolved": "https://registry.npmjs.org/ansi-styles/-/ansi-styles-4.3.0.tgz", "integrity": "sha512-zbB9rCJAT1rbjiVDb2hqKFHNYLxgtk8NURxZ3IZwD3F6NtxbXZQCnnSi1Lkx+IDohdPlFp222wVALIheZJQSEg==", @@ -5874,939 +1059,822 @@ "url": "https://github.com/chalk/ansi-styles?sponsor=1" } }, - "node_modules/jest-cli/node_modules/chalk": { - "version": "4.1.2", - "resolved": "https://registry.npmjs.org/chalk/-/chalk-4.1.2.tgz", - "integrity": "sha512-oKnbhFyRIXpUuez8iBMmyEa4nbj4IOQyuhc/wy9kY7/WVPcwIO9VA668Pu8RkO7+0G76SLROeyw9CpQ061i4mA==", + "node_modules/argparse": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/argparse/-/argparse-2.0.1.tgz", + "integrity": "sha512-8+9WqebbFzpX9OR+Wa6O29asIogeRMzcGtAINdpMHHyAg10f05aSFVBbcEqGf/PXw1EjAZ+q2/bEBg3DvurK3Q==", + "dev": true, + "license": "Python-2.0" + }, + "node_modules/array-find-index": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/array-find-index/-/array-find-index-1.0.2.tgz", + "integrity": "sha512-M1HQyIXcBGtVywBt8WVdim+lrNaK7VHp99Qt5pSNziXznKHViIBbXWtfRTpEFpF/c4FdfxNAsCCwPp5phBYJtw==", "dev": true, "license": "MIT", - "dependencies": { - "ansi-styles": "^4.1.0", - "supports-color": "^7.1.0" - }, "engines": { - "node": ">=10" - }, - "funding": { - "url": "https://github.com/chalk/chalk?sponsor=1" + "node": ">=0.10.0" + } + }, + "node_modules/balanced-match": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/balanced-match/-/balanced-match-1.0.2.tgz", + "integrity": "sha512-3oSeUO0TMV67hN1AmbXsK4yaqU7tjiHlbxRDZOpH0KW9+CeX4bRAaX0Anxt0tx2MrpRpWwQaPwIlISEJhYU5Pw==", + "dev": true, + "license": "MIT" + }, + "node_modules/brace-expansion": { + "version": "2.0.2", + "resolved": "https://registry.npmjs.org/brace-expansion/-/brace-expansion-2.0.2.tgz", + "integrity": "sha512-Jt0vHyM+jmUBqojB7E1NIYadt0vI0Qxjxd2TErW94wDz+E2LAm5vKMXXwg6ZZBTHPuUlDgQHKXvjGBdfcF1ZDQ==", + "dev": true, + "license": "MIT", + "dependencies": { + "balanced-match": "^1.0.0" } }, - "node_modules/jest-config": { - "version": "30.0.5", - "resolved": "https://registry.npmjs.org/jest-config/-/jest-config-30.0.5.tgz", - "integrity": "sha512-aIVh+JNOOpzUgzUnPn5FLtyVnqc3TQHVMupYtyeURSb//iLColiMIR8TxCIDKyx9ZgjKnXGucuW68hCxgbrwmA==", + "node_modules/braces": { + "version": "3.0.3", + "resolved": "https://registry.npmjs.org/braces/-/braces-3.0.3.tgz", + "integrity": "sha512-yQbXgO/OSZVD2IsiLlro+7Hf6Q18EJrKSEsdoMzKePKXct3gvD8oLcOQdIzGupr5Fj+EDe8gO/lxc1BzfMpxvA==", "dev": true, "license": "MIT", "dependencies": { - "@babel/core": "^7.27.4", - "@jest/get-type": "30.0.1", - "@jest/pattern": "30.0.1", - "@jest/test-sequencer": "30.0.5", - "@jest/types": "30.0.5", - "babel-jest": "30.0.5", - "chalk": "^4.1.2", - "ci-info": "^4.2.0", - "deepmerge": "^4.3.1", - "glob": "^10.3.10", - "graceful-fs": "^4.2.11", - "jest-circus": "30.0.5", - "jest-docblock": "30.0.1", - "jest-environment-node": "30.0.5", - "jest-regex-util": "30.0.1", - "jest-resolve": "30.0.5", - "jest-runner": "30.0.5", - "jest-util": "30.0.5", - "jest-validate": "30.0.5", - "micromatch": "^4.0.8", - "parse-json": "^5.2.0", - "pretty-format": "30.0.5", - "slash": "^3.0.0", - "strip-json-comments": "^3.1.1" + "fill-range": "^7.1.1" }, "engines": { - "node": "^18.14.0 || ^20.0.0 || ^22.0.0 || >=24.0.0" - }, - "peerDependencies": { - "@types/node": "*", - "esbuild-register": ">=3.4.0", - "ts-node": ">=9.0.0" - }, - "peerDependenciesMeta": { - "@types/node": { - "optional": true - }, - "esbuild-register": { - "optional": true - }, - "ts-node": { - "optional": true - } + "node": ">=8" } }, - "node_modules/jest-config/node_modules/ansi-styles": { - "version": "4.3.0", - "resolved": "https://registry.npmjs.org/ansi-styles/-/ansi-styles-4.3.0.tgz", - "integrity": "sha512-zbB9rCJAT1rbjiVDb2hqKFHNYLxgtk8NURxZ3IZwD3F6NtxbXZQCnnSi1Lkx+IDohdPlFp222wVALIheZJQSEg==", + "node_modules/buffer-from": { + "version": "1.1.2", + "resolved": "https://registry.npmjs.org/buffer-from/-/buffer-from-1.1.2.tgz", + "integrity": "sha512-E+XQCRwSbaaiChtv6k6Dwgc+bx+Bs6vuKJHHl5kox/BaKbhiXzqQOwK4cO22yElGp2OCmjwVhT3HmxgyPGnJfQ==", + "dev": true, + "license": "MIT" + }, + "node_modules/callsites": { + "version": "3.1.0", + "resolved": "https://registry.npmjs.org/callsites/-/callsites-3.1.0.tgz", + "integrity": "sha512-P8BjAsXvZS+VIDUI11hHCQEv74YT67YUi5JJFNWIqL235sBmjX4+qx9Muvls5ivyNENctx46xQLQ3aTuE7ssaQ==", "dev": true, "license": "MIT", - "dependencies": { - "color-convert": "^2.0.1" - }, "engines": { - "node": ">=8" - }, - "funding": { - "url": "https://github.com/chalk/ansi-styles?sponsor=1" + "node": ">=6" } }, - "node_modules/jest-config/node_modules/chalk": { - "version": "4.1.2", - "resolved": "https://registry.npmjs.org/chalk/-/chalk-4.1.2.tgz", - "integrity": "sha512-oKnbhFyRIXpUuez8iBMmyEa4nbj4IOQyuhc/wy9kY7/WVPcwIO9VA668Pu8RkO7+0G76SLROeyw9CpQ061i4mA==", + "node_modules/chalk": { + "version": "5.6.2", + "resolved": "https://registry.npmjs.org/chalk/-/chalk-5.6.2.tgz", + "integrity": "sha512-7NzBL0rN6fMUW+f7A6Io4h40qQlG+xGmtMxfbnH/K7TAtt8JQWVQK+6g0UXKMeVJoyV5EkkNsErQ8pVD3bLHbA==", "dev": true, "license": "MIT", - "dependencies": { - "ansi-styles": "^4.1.0", - "supports-color": "^7.1.0" - }, "engines": { - "node": ">=10" + "node": "^12.17.0 || ^14.13 || >=16.0.0" }, "funding": { "url": "https://github.com/chalk/chalk?sponsor=1" } }, - "node_modules/jest-diff": { - "version": "30.0.5", - "resolved": "https://registry.npmjs.org/jest-diff/-/jest-diff-30.0.5.tgz", - "integrity": "sha512-1UIqE9PoEKaHcIKvq2vbibrCog4Y8G0zmOxgQUVEiTqwR5hJVMCoDsN1vFvI5JvwD37hjueZ1C4l2FyGnfpE0A==", + "node_modules/color-convert": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/color-convert/-/color-convert-2.0.1.tgz", + "integrity": "sha512-RRECPsj7iu/xb5oKYcsFHSppFNnsj/52OVTRKb4zP5onXwVF3zVmmToNcOfGC+CRDpfK/U584fMg38ZHCaElKQ==", "dev": true, "license": "MIT", "dependencies": { - "@jest/diff-sequences": "30.0.1", - "@jest/get-type": "30.0.1", - "chalk": "^4.1.2", - "pretty-format": "30.0.5" + "color-name": "~1.1.4" }, "engines": { - "node": "^18.14.0 || ^20.0.0 || ^22.0.0 || >=24.0.0" + "node": ">=7.0.0" } }, - "node_modules/jest-diff/node_modules/ansi-styles": { - "version": "4.3.0", - "resolved": "https://registry.npmjs.org/ansi-styles/-/ansi-styles-4.3.0.tgz", - "integrity": "sha512-zbB9rCJAT1rbjiVDb2hqKFHNYLxgtk8NURxZ3IZwD3F6NtxbXZQCnnSi1Lkx+IDohdPlFp222wVALIheZJQSEg==", + "node_modules/color-name": { + "version": "1.1.4", + "resolved": "https://registry.npmjs.org/color-name/-/color-name-1.1.4.tgz", + "integrity": "sha512-dOy+3AuW3a2wNbZHIuMZpTcgjGuLU/uBL/ubcZF9OXbDo8ff4O8yVp5Bf0efS8uEoYo5q4Fx7dY9OgQGXgAsQA==", + "dev": true, + "license": "MIT" + }, + "node_modules/commander": { + "version": "2.20.3", + "resolved": "https://registry.npmjs.org/commander/-/commander-2.20.3.tgz", + "integrity": "sha512-GpVkmM8vF2vQUkj2LvZmD35JxeJOLCwJ9cUkugyk2nuhbv3+mJvpLYYt+0+USMxE+oj+ey/lJEnhZw75x/OMcQ==", + "dev": true, + "license": "MIT" + }, + "node_modules/commenting": { + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/commenting/-/commenting-1.1.0.tgz", + "integrity": "sha512-YeNK4tavZwtH7jEgK1ZINXzLKm6DZdEMfsaaieOsCAN0S8vsY7UeuO3Q7d/M018EFgE+IeUAuBOKkFccBZsUZA==", + "dev": true, + "license": "MIT" + }, + "node_modules/concat-map": { + "version": "0.0.1", + "resolved": "https://registry.npmjs.org/concat-map/-/concat-map-0.0.1.tgz", + "integrity": "sha512-/Srv4dswyQNBfohGpz9o6Yb3Gz3SrUDqBH5rTuhGR7ahtlbYKnVxw2bCFMRljaA7EXHaXZ8wsHdodFvbkhKmqg==", + "dev": true, + "license": "MIT" + }, + "node_modules/cross-spawn": { + "version": "7.0.6", + "resolved": "https://registry.npmjs.org/cross-spawn/-/cross-spawn-7.0.6.tgz", + "integrity": "sha512-uV2QOWP2nWzsy2aMp8aRibhi9dlzF5Hgh5SHaB9OiTGEyDTiJJyx0uy51QXdyWbtAHNua4XJzUKca3OzKUd3vA==", "dev": true, "license": "MIT", "dependencies": { - "color-convert": "^2.0.1" + "path-key": "^3.1.0", + "shebang-command": "^2.0.0", + "which": "^2.0.1" }, "engines": { - "node": ">=8" - }, - "funding": { - "url": "https://github.com/chalk/ansi-styles?sponsor=1" + "node": ">= 8" } }, - "node_modules/jest-diff/node_modules/chalk": { - "version": "4.1.2", - "resolved": "https://registry.npmjs.org/chalk/-/chalk-4.1.2.tgz", - "integrity": "sha512-oKnbhFyRIXpUuez8iBMmyEa4nbj4IOQyuhc/wy9kY7/WVPcwIO9VA668Pu8RkO7+0G76SLROeyw9CpQ061i4mA==", + "node_modules/debug": { + "version": "4.4.3", + "resolved": "https://registry.npmjs.org/debug/-/debug-4.4.3.tgz", + "integrity": "sha512-RGwwWnwQvkVfavKVt22FGLw+xYSdzARwm0ru6DhTVA3umU5hZc28V3kO4stgYryrTlLpuvgI9GiijltAjNbcqA==", "dev": true, "license": "MIT", "dependencies": { - "ansi-styles": "^4.1.0", - "supports-color": "^7.1.0" + "ms": "^2.1.3" }, "engines": { - "node": ">=10" + "node": ">=6.0" }, - "funding": { - "url": "https://github.com/chalk/chalk?sponsor=1" + "peerDependenciesMeta": { + "supports-color": { + "optional": true + } } }, - "node_modules/jest-docblock": { - "version": "30.0.1", - "resolved": "https://registry.npmjs.org/jest-docblock/-/jest-docblock-30.0.1.tgz", - "integrity": "sha512-/vF78qn3DYphAaIc3jy4gA7XSAz167n9Bm/wn/1XhTLW7tTBIzXtCJpb/vcmc73NIIeeohCbdL94JasyXUZsGA==", + "node_modules/deep-is": { + "version": "0.1.4", + "resolved": "https://registry.npmjs.org/deep-is/-/deep-is-0.1.4.tgz", + "integrity": "sha512-oIPzksmTg4/MriiaYGO+okXDT7ztn/w3Eptv/+gSIdMdKsJo0u4CfYNFJPy+4SKMuCqGw2wxnA+URMg3t8a/bQ==", "dev": true, - "license": "MIT", - "dependencies": { - "detect-newline": "^3.1.0" - }, - "engines": { - "node": "^18.14.0 || ^20.0.0 || ^22.0.0 || >=24.0.0" - } + "license": "MIT" }, - "node_modules/jest-each": { - "version": "30.0.5", - "resolved": "https://registry.npmjs.org/jest-each/-/jest-each-30.0.5.tgz", - "integrity": "sha512-dKjRsx1uZ96TVyejD3/aAWcNKy6ajMaN531CwWIsrazIqIoXI9TnnpPlkrEYku/8rkS3dh2rbH+kMOyiEIv0xQ==", + "node_modules/deepmerge": { + "version": "4.3.1", + "resolved": "https://registry.npmjs.org/deepmerge/-/deepmerge-4.3.1.tgz", + "integrity": "sha512-3sUqbMEc77XqpdNO7FRyRog+eW3ph+GYCbj+rK+uYyRMuwsVy0rMiVtPn+QJlKFvWP/1PYpapqYn0Me2knFn+A==", "dev": true, "license": "MIT", - "dependencies": { - "@jest/get-type": "30.0.1", - "@jest/types": "30.0.5", - "chalk": "^4.1.2", - "jest-util": "30.0.5", - "pretty-format": "30.0.5" - }, "engines": { - "node": "^18.14.0 || ^20.0.0 || ^22.0.0 || >=24.0.0" + "node": ">=0.10.0" } }, - "node_modules/jest-each/node_modules/ansi-styles": { - "version": "4.3.0", - "resolved": "https://registry.npmjs.org/ansi-styles/-/ansi-styles-4.3.0.tgz", - "integrity": "sha512-zbB9rCJAT1rbjiVDb2hqKFHNYLxgtk8NURxZ3IZwD3F6NtxbXZQCnnSi1Lkx+IDohdPlFp222wVALIheZJQSEg==", + "node_modules/escape-string-regexp": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/escape-string-regexp/-/escape-string-regexp-4.0.0.tgz", + "integrity": "sha512-TtpcNJ3XAzx3Gq8sWRzJaVajRs0uVxA2YAkdb1jm2YkPz4G6egUFAyA3n5vtEIZefPk5Wa4UXbKuS5fKkJWdgA==", "dev": true, "license": "MIT", - "dependencies": { - "color-convert": "^2.0.1" - }, "engines": { - "node": ">=8" + "node": ">=10" }, "funding": { - "url": "https://github.com/chalk/ansi-styles?sponsor=1" + "url": "https://github.com/sponsors/sindresorhus" } }, - "node_modules/jest-each/node_modules/chalk": { - "version": "4.1.2", - "resolved": "https://registry.npmjs.org/chalk/-/chalk-4.1.2.tgz", - "integrity": "sha512-oKnbhFyRIXpUuez8iBMmyEa4nbj4IOQyuhc/wy9kY7/WVPcwIO9VA668Pu8RkO7+0G76SLROeyw9CpQ061i4mA==", + "node_modules/eslint": { + "version": "9.39.1", + "resolved": "https://registry.npmjs.org/eslint/-/eslint-9.39.1.tgz", + "integrity": "sha512-BhHmn2yNOFA9H9JmmIVKJmd288g9hrVRDkdoIgRCRuSySRUHH7r/DI6aAXW9T1WwUuY3DFgrcaqB+deURBLR5g==", "dev": true, "license": "MIT", "dependencies": { - "ansi-styles": "^4.1.0", - "supports-color": "^7.1.0" + "@eslint-community/eslint-utils": "^4.8.0", + "@eslint-community/regexpp": "^4.12.1", + "@eslint/config-array": "^0.21.1", + "@eslint/config-helpers": "^0.4.2", + "@eslint/core": "^0.17.0", + "@eslint/eslintrc": "^3.3.1", + "@eslint/js": "9.39.1", + "@eslint/plugin-kit": "^0.4.1", + "@humanfs/node": "^0.16.6", + "@humanwhocodes/module-importer": "^1.0.1", + "@humanwhocodes/retry": "^0.4.2", + "@types/estree": "^1.0.6", + "ajv": "^6.12.4", + "chalk": "^4.0.0", + "cross-spawn": "^7.0.6", + "debug": "^4.3.2", + "escape-string-regexp": "^4.0.0", + "eslint-scope": "^8.4.0", + "eslint-visitor-keys": "^4.2.1", + "espree": "^10.4.0", + "esquery": "^1.5.0", + "esutils": "^2.0.2", + "fast-deep-equal": "^3.1.3", + "file-entry-cache": "^8.0.0", + "find-up": "^5.0.0", + "glob-parent": "^6.0.2", + "ignore": "^5.2.0", + "imurmurhash": "^0.1.4", + "is-glob": "^4.0.0", + "json-stable-stringify-without-jsonify": "^1.0.1", + "lodash.merge": "^4.6.2", + "minimatch": "^3.1.2", + "natural-compare": "^1.4.0", + "optionator": "^0.9.3" + }, + "bin": { + "eslint": "bin/eslint.js" }, "engines": { - "node": ">=10" + "node": "^18.18.0 || ^20.9.0 || >=21.1.0" }, "funding": { - "url": "https://github.com/chalk/chalk?sponsor=1" + "url": "https://eslint.org/donate" + }, + "peerDependencies": { + "jiti": "*" + }, + "peerDependenciesMeta": { + "jiti": { + "optional": true + } } }, - "node_modules/jest-environment-node": { - "version": "30.0.5", - "resolved": "https://registry.npmjs.org/jest-environment-node/-/jest-environment-node-30.0.5.tgz", - "integrity": "sha512-ppYizXdLMSvciGsRsMEnv/5EFpvOdXBaXRBzFUDPWrsfmog4kYrOGWXarLllz6AXan6ZAA/kYokgDWuos1IKDA==", + "node_modules/eslint-scope": { + "version": "8.4.0", + "resolved": "https://registry.npmjs.org/eslint-scope/-/eslint-scope-8.4.0.tgz", + "integrity": "sha512-sNXOfKCn74rt8RICKMvJS7XKV/Xk9kA7DyJr8mJik3S7Cwgy3qlkkmyS2uQB3jiJg6VNdZd/pDBJu0nvG2NlTg==", "dev": true, - "license": "MIT", + "license": "BSD-2-Clause", "dependencies": { - "@jest/environment": "30.0.5", - "@jest/fake-timers": "30.0.5", - "@jest/types": "30.0.5", - "@types/node": "*", - "jest-mock": "30.0.5", - "jest-util": "30.0.5", - "jest-validate": "30.0.5" + "esrecurse": "^4.3.0", + "estraverse": "^5.2.0" }, "engines": { - "node": "^18.14.0 || ^20.0.0 || ^22.0.0 || >=24.0.0" + "node": "^18.18.0 || ^20.9.0 || >=21.1.0" + }, + "funding": { + "url": "https://opencollective.com/eslint" } }, - "node_modules/jest-haste-map": { - "version": "30.0.5", - "resolved": "https://registry.npmjs.org/jest-haste-map/-/jest-haste-map-30.0.5.tgz", - "integrity": "sha512-dkmlWNlsTSR0nH3nRfW5BKbqHefLZv0/6LCccG0xFCTWcJu8TuEwG+5Cm75iBfjVoockmO6J35o5gxtFSn5xeg==", + "node_modules/eslint-visitor-keys": { + "version": "3.4.3", + "resolved": "https://registry.npmjs.org/eslint-visitor-keys/-/eslint-visitor-keys-3.4.3.tgz", + "integrity": "sha512-wpc+LXeiyiisxPlEkUzU6svyS1frIO3Mgxj1fdy7Pm8Ygzguax2N3Fa/D/ag1WqbOprdI+uY6wMUl8/a2G+iag==", "dev": true, - "license": "MIT", - "dependencies": { - "@jest/types": "30.0.5", - "@types/node": "*", - "anymatch": "^3.1.3", - "fb-watchman": "^2.0.2", - "graceful-fs": "^4.2.11", - "jest-regex-util": "30.0.1", - "jest-util": "30.0.5", - "jest-worker": "30.0.5", - "micromatch": "^4.0.8", - "walker": "^1.0.8" - }, + "license": "Apache-2.0", "engines": { - "node": "^18.14.0 || ^20.0.0 || ^22.0.0 || >=24.0.0" + "node": "^12.22.0 || ^14.17.0 || >=16.0.0" }, - "optionalDependencies": { - "fsevents": "^2.3.3" + "funding": { + "url": "https://opencollective.com/eslint" } }, - "node_modules/jest-leak-detector": { - "version": "30.0.5", - "resolved": "https://registry.npmjs.org/jest-leak-detector/-/jest-leak-detector-30.0.5.tgz", - "integrity": "sha512-3Uxr5uP8jmHMcsOtYMRB/zf1gXN3yUIc+iPorhNETG54gErFIiUhLvyY/OggYpSMOEYqsmRxmuU4ZOoX5jpRFg==", + "node_modules/eslint/node_modules/brace-expansion": { + "version": "1.1.12", + "resolved": "https://registry.npmjs.org/brace-expansion/-/brace-expansion-1.1.12.tgz", + "integrity": "sha512-9T9UjW3r0UW5c1Q7GTwllptXwhvYmEzFhzMfZ9H7FQWt+uZePjZPjBP/W1ZEyZ1twGWom5/56TF4lPcqjnDHcg==", "dev": true, "license": "MIT", "dependencies": { - "@jest/get-type": "30.0.1", - "pretty-format": "30.0.5" - }, - "engines": { - "node": "^18.14.0 || ^20.0.0 || ^22.0.0 || >=24.0.0" + "balanced-match": "^1.0.0", + "concat-map": "0.0.1" } }, - "node_modules/jest-matcher-utils": { - "version": "30.0.5", - "resolved": "https://registry.npmjs.org/jest-matcher-utils/-/jest-matcher-utils-30.0.5.tgz", - "integrity": "sha512-uQgGWt7GOrRLP1P7IwNWwK1WAQbq+m//ZY0yXygyfWp0rJlksMSLQAA4wYQC3b6wl3zfnchyTx+k3HZ5aPtCbQ==", + "node_modules/eslint/node_modules/chalk": { + "version": "4.1.2", + "resolved": "https://registry.npmjs.org/chalk/-/chalk-4.1.2.tgz", + "integrity": "sha512-oKnbhFyRIXpUuez8iBMmyEa4nbj4IOQyuhc/wy9kY7/WVPcwIO9VA668Pu8RkO7+0G76SLROeyw9CpQ061i4mA==", "dev": true, "license": "MIT", "dependencies": { - "@jest/get-type": "30.0.1", - "chalk": "^4.1.2", - "jest-diff": "30.0.5", - "pretty-format": "30.0.5" + "ansi-styles": "^4.1.0", + "supports-color": "^7.1.0" }, "engines": { - "node": "^18.14.0 || ^20.0.0 || ^22.0.0 || >=24.0.0" + "node": ">=10" + }, + "funding": { + "url": "https://github.com/chalk/chalk?sponsor=1" } }, - "node_modules/jest-matcher-utils/node_modules/ansi-styles": { - "version": "4.3.0", - "resolved": "https://registry.npmjs.org/ansi-styles/-/ansi-styles-4.3.0.tgz", - "integrity": "sha512-zbB9rCJAT1rbjiVDb2hqKFHNYLxgtk8NURxZ3IZwD3F6NtxbXZQCnnSi1Lkx+IDohdPlFp222wVALIheZJQSEg==", + "node_modules/eslint/node_modules/eslint-visitor-keys": { + "version": "4.2.1", + "resolved": "https://registry.npmjs.org/eslint-visitor-keys/-/eslint-visitor-keys-4.2.1.tgz", + "integrity": "sha512-Uhdk5sfqcee/9H/rCOJikYz67o0a2Tw2hGRPOG2Y1R2dg7brRe1uG0yaNQDHu+TO/uQPF/5eCapvYSmHUjt7JQ==", "dev": true, - "license": "MIT", - "dependencies": { - "color-convert": "^2.0.1" - }, + "license": "Apache-2.0", "engines": { - "node": ">=8" + "node": "^18.18.0 || ^20.9.0 || >=21.1.0" }, "funding": { - "url": "https://github.com/chalk/ansi-styles?sponsor=1" + "url": "https://opencollective.com/eslint" } }, - "node_modules/jest-matcher-utils/node_modules/chalk": { - "version": "4.1.2", - "resolved": "https://registry.npmjs.org/chalk/-/chalk-4.1.2.tgz", - "integrity": "sha512-oKnbhFyRIXpUuez8iBMmyEa4nbj4IOQyuhc/wy9kY7/WVPcwIO9VA668Pu8RkO7+0G76SLROeyw9CpQ061i4mA==", + "node_modules/eslint/node_modules/ignore": { + "version": "5.3.2", + "resolved": "https://registry.npmjs.org/ignore/-/ignore-5.3.2.tgz", + "integrity": "sha512-hsBTNUqQTDwkWtcdYI2i06Y/nUBEsNEDJKjWdigLvegy8kDuJAS8uRlpkkcQpyEXL0Z/pjDy5HBmMjRCJ2gq+g==", "dev": true, "license": "MIT", - "dependencies": { - "ansi-styles": "^4.1.0", - "supports-color": "^7.1.0" - }, "engines": { - "node": ">=10" - }, - "funding": { - "url": "https://github.com/chalk/chalk?sponsor=1" + "node": ">= 4" } }, - "node_modules/jest-message-util": { - "version": "30.0.5", - "resolved": "https://registry.npmjs.org/jest-message-util/-/jest-message-util-30.0.5.tgz", - "integrity": "sha512-NAiDOhsK3V7RU0Aa/HnrQo+E4JlbarbmI3q6Pi4KcxicdtjV82gcIUrejOtczChtVQR4kddu1E1EJlW6EN9IyA==", + "node_modules/eslint/node_modules/minimatch": { + "version": "3.1.2", + "resolved": "https://registry.npmjs.org/minimatch/-/minimatch-3.1.2.tgz", + "integrity": "sha512-J7p63hRiAjw1NDEww1W7i37+ByIrOWO5XQQAzZ3VOcL0PNybwpfmV/N05zFAzwQ9USyEcX6t3UO+K5aqBQOIHw==", "dev": true, - "license": "MIT", + "license": "ISC", "dependencies": { - "@babel/code-frame": "^7.27.1", - "@jest/types": "30.0.5", - "@types/stack-utils": "^2.0.3", - "chalk": "^4.1.2", - "graceful-fs": "^4.2.11", - "micromatch": "^4.0.8", - "pretty-format": "30.0.5", - "slash": "^3.0.0", - "stack-utils": "^2.0.6" + "brace-expansion": "^1.1.7" }, "engines": { - "node": "^18.14.0 || ^20.0.0 || ^22.0.0 || >=24.0.0" + "node": "*" } }, - "node_modules/jest-message-util/node_modules/ansi-styles": { - "version": "4.3.0", - "resolved": "https://registry.npmjs.org/ansi-styles/-/ansi-styles-4.3.0.tgz", - "integrity": "sha512-zbB9rCJAT1rbjiVDb2hqKFHNYLxgtk8NURxZ3IZwD3F6NtxbXZQCnnSi1Lkx+IDohdPlFp222wVALIheZJQSEg==", + "node_modules/espree": { + "version": "10.4.0", + "resolved": "https://registry.npmjs.org/espree/-/espree-10.4.0.tgz", + "integrity": "sha512-j6PAQ2uUr79PZhBjP5C5fhl8e39FmRnOjsD5lGnWrFU8i2G776tBK7+nP8KuQUTTyAZUwfQqXAgrVH5MbH9CYQ==", "dev": true, - "license": "MIT", + "license": "BSD-2-Clause", "dependencies": { - "color-convert": "^2.0.1" + "acorn": "^8.15.0", + "acorn-jsx": "^5.3.2", + "eslint-visitor-keys": "^4.2.1" }, "engines": { - "node": ">=8" + "node": "^18.18.0 || ^20.9.0 || >=21.1.0" }, "funding": { - "url": "https://github.com/chalk/ansi-styles?sponsor=1" + "url": "https://opencollective.com/eslint" } }, - "node_modules/jest-message-util/node_modules/chalk": { - "version": "4.1.2", - "resolved": "https://registry.npmjs.org/chalk/-/chalk-4.1.2.tgz", - "integrity": "sha512-oKnbhFyRIXpUuez8iBMmyEa4nbj4IOQyuhc/wy9kY7/WVPcwIO9VA668Pu8RkO7+0G76SLROeyw9CpQ061i4mA==", + "node_modules/espree/node_modules/eslint-visitor-keys": { + "version": "4.2.1", + "resolved": "https://registry.npmjs.org/eslint-visitor-keys/-/eslint-visitor-keys-4.2.1.tgz", + "integrity": "sha512-Uhdk5sfqcee/9H/rCOJikYz67o0a2Tw2hGRPOG2Y1R2dg7brRe1uG0yaNQDHu+TO/uQPF/5eCapvYSmHUjt7JQ==", "dev": true, - "license": "MIT", - "dependencies": { - "ansi-styles": "^4.1.0", - "supports-color": "^7.1.0" - }, + "license": "Apache-2.0", "engines": { - "node": ">=10" + "node": "^18.18.0 || ^20.9.0 || >=21.1.0" }, "funding": { - "url": "https://github.com/chalk/chalk?sponsor=1" + "url": "https://opencollective.com/eslint" } }, - "node_modules/jest-mock": { - "version": "30.0.5", - "resolved": "https://registry.npmjs.org/jest-mock/-/jest-mock-30.0.5.tgz", - "integrity": "sha512-Od7TyasAAQX/6S+QCbN6vZoWOMwlTtzzGuxJku1GhGanAjz9y+QsQkpScDmETvdc9aSXyJ/Op4rhpMYBWW91wQ==", + "node_modules/esquery": { + "version": "1.6.0", + "resolved": "https://registry.npmjs.org/esquery/-/esquery-1.6.0.tgz", + "integrity": "sha512-ca9pw9fomFcKPvFLXhBKUK90ZvGibiGOvRJNbjljY7s7uq/5YO4BOzcYtJqExdx99rF6aAcnRxHmcUHcz6sQsg==", "dev": true, - "license": "MIT", + "license": "BSD-3-Clause", "dependencies": { - "@jest/types": "30.0.5", - "@types/node": "*", - "jest-util": "30.0.5" + "estraverse": "^5.1.0" }, "engines": { - "node": "^18.14.0 || ^20.0.0 || ^22.0.0 || >=24.0.0" + "node": ">=0.10" } }, - "node_modules/jest-pnp-resolver": { - "version": "1.2.3", - "resolved": "https://registry.npmjs.org/jest-pnp-resolver/-/jest-pnp-resolver-1.2.3.tgz", - "integrity": "sha512-+3NpwQEnRoIBtx4fyhblQDPgJI0H1IEIkX7ShLUjPGA7TtUTvI1oiKi3SR4oBR0hQhQR80l4WAe5RrXBwWMA8w==", + "node_modules/esrecurse": { + "version": "4.3.0", + "resolved": "https://registry.npmjs.org/esrecurse/-/esrecurse-4.3.0.tgz", + "integrity": "sha512-KmfKL3b6G+RXvP8N1vr3Tq1kL/oCFgn2NYXEtqP8/L3pKapUA4G8cFVaoF3SU323CD4XypR/ffioHmkti6/Tag==", "dev": true, - "license": "MIT", - "engines": { - "node": ">=6" - }, - "peerDependencies": { - "jest-resolve": "*" + "license": "BSD-2-Clause", + "dependencies": { + "estraverse": "^5.2.0" }, - "peerDependenciesMeta": { - "jest-resolve": { - "optional": true - } + "engines": { + "node": ">=4.0" } }, - "node_modules/jest-regex-util": { - "version": "30.0.1", - "resolved": "https://registry.npmjs.org/jest-regex-util/-/jest-regex-util-30.0.1.tgz", - "integrity": "sha512-jHEQgBXAgc+Gh4g0p3bCevgRCVRkB4VB70zhoAE48gxeSr1hfUOsM/C2WoJgVL7Eyg//hudYENbm3Ne+/dRVVA==", + "node_modules/estraverse": { + "version": "5.3.0", + "resolved": "https://registry.npmjs.org/estraverse/-/estraverse-5.3.0.tgz", + "integrity": "sha512-MMdARuVEQziNTeJD8DgMqmhwR11BRQ/cBP+pLtYdSTnf3MIO8fFeiINEbX36ZdNlfU/7A9f3gUw49B3oQsvwBA==", "dev": true, - "license": "MIT", + "license": "BSD-2-Clause", "engines": { - "node": "^18.14.0 || ^20.0.0 || ^22.0.0 || >=24.0.0" + "node": ">=4.0" } }, - "node_modules/jest-resolve": { - "version": "30.0.5", - "resolved": "https://registry.npmjs.org/jest-resolve/-/jest-resolve-30.0.5.tgz", - "integrity": "sha512-d+DjBQ1tIhdz91B79mywH5yYu76bZuE96sSbxj8MkjWVx5WNdt1deEFRONVL4UkKLSrAbMkdhb24XN691yDRHg==", + "node_modules/estree-walker": { + "version": "2.0.2", + "resolved": "https://registry.npmjs.org/estree-walker/-/estree-walker-2.0.2.tgz", + "integrity": "sha512-Rfkk/Mp/DL7JVje3u18FxFujQlTNR2q6QfMSMB7AvCBx91NGj/ba3kCfza0f6dVDbw7YlRf/nDrn7pQrCCyQ/w==", "dev": true, - "license": "MIT", - "dependencies": { - "chalk": "^4.1.2", - "graceful-fs": "^4.2.11", - "jest-haste-map": "30.0.5", - "jest-pnp-resolver": "^1.2.3", - "jest-util": "30.0.5", - "jest-validate": "30.0.5", - "slash": "^3.0.0", - "unrs-resolver": "^1.7.11" - }, - "engines": { - "node": "^18.14.0 || ^20.0.0 || ^22.0.0 || >=24.0.0" - } + "license": "MIT" }, - "node_modules/jest-resolve-dependencies": { - "version": "30.0.5", - "resolved": "https://registry.npmjs.org/jest-resolve-dependencies/-/jest-resolve-dependencies-30.0.5.tgz", - "integrity": "sha512-/xMvBR4MpwkrHW4ikZIWRttBBRZgWK4d6xt3xW1iRDSKt4tXzYkMkyPfBnSCgv96cpkrctfXs6gexeqMYqdEpw==", + "node_modules/esutils": { + "version": "2.0.3", + "resolved": "https://registry.npmjs.org/esutils/-/esutils-2.0.3.tgz", + "integrity": "sha512-kVscqXk4OCp68SZ0dkgEKVi6/8ij300KBWTJq32P/dYeWTSwK41WyTxalN1eRmA5Z9UU/LX9D7FWSmV9SAYx6g==", "dev": true, - "license": "MIT", - "dependencies": { - "jest-regex-util": "30.0.1", - "jest-snapshot": "30.0.5" - }, + "license": "BSD-2-Clause", "engines": { - "node": "^18.14.0 || ^20.0.0 || ^22.0.0 || >=24.0.0" + "node": ">=0.10.0" } }, - "node_modules/jest-resolve/node_modules/ansi-styles": { - "version": "4.3.0", - "resolved": "https://registry.npmjs.org/ansi-styles/-/ansi-styles-4.3.0.tgz", - "integrity": "sha512-zbB9rCJAT1rbjiVDb2hqKFHNYLxgtk8NURxZ3IZwD3F6NtxbXZQCnnSi1Lkx+IDohdPlFp222wVALIheZJQSEg==", + "node_modules/execa": { + "version": "5.1.1", + "resolved": "https://registry.npmjs.org/execa/-/execa-5.1.1.tgz", + "integrity": "sha512-8uSpZZocAZRBAPIEINJj3Lo9HyGitllczc27Eh5YYojjMFMn8yHMDMaUHE2Jqfq05D/wucwI4JGURyXt1vchyg==", "dev": true, "license": "MIT", "dependencies": { - "color-convert": "^2.0.1" + "cross-spawn": "^7.0.3", + "get-stream": "^6.0.0", + "human-signals": "^2.1.0", + "is-stream": "^2.0.0", + "merge-stream": "^2.0.0", + "npm-run-path": "^4.0.1", + "onetime": "^5.1.2", + "signal-exit": "^3.0.3", + "strip-final-newline": "^2.0.0" }, "engines": { - "node": ">=8" + "node": ">=10" }, "funding": { - "url": "https://github.com/chalk/ansi-styles?sponsor=1" + "url": "https://github.com/sindresorhus/execa?sponsor=1" } }, - "node_modules/jest-resolve/node_modules/chalk": { - "version": "4.1.2", - "resolved": "https://registry.npmjs.org/chalk/-/chalk-4.1.2.tgz", - "integrity": "sha512-oKnbhFyRIXpUuez8iBMmyEa4nbj4IOQyuhc/wy9kY7/WVPcwIO9VA668Pu8RkO7+0G76SLROeyw9CpQ061i4mA==", + "node_modules/fast-deep-equal": { + "version": "3.1.3", + "resolved": "https://registry.npmjs.org/fast-deep-equal/-/fast-deep-equal-3.1.3.tgz", + "integrity": "sha512-f3qQ9oQy9j2AhBe/H9VC91wLmKBCCU/gDOnKNAYG5hswO7BLKj09Hc5HYNz9cGI++xlpDCIgDaitVs03ATR84Q==", + "dev": true, + "license": "MIT" + }, + "node_modules/fast-glob": { + "version": "3.3.3", + "resolved": "https://registry.npmjs.org/fast-glob/-/fast-glob-3.3.3.tgz", + "integrity": "sha512-7MptL8U0cqcFdzIzwOTHoilX9x5BrNqye7Z/LuC7kCMRio1EMSyqRK3BEAUD7sXRq4iT4AzTVuZdhgQ2TCvYLg==", "dev": true, "license": "MIT", "dependencies": { - "ansi-styles": "^4.1.0", - "supports-color": "^7.1.0" + "@nodelib/fs.stat": "^2.0.2", + "@nodelib/fs.walk": "^1.2.3", + "glob-parent": "^5.1.2", + "merge2": "^1.3.0", + "micromatch": "^4.0.8" }, "engines": { - "node": ">=10" - }, - "funding": { - "url": "https://github.com/chalk/chalk?sponsor=1" + "node": ">=8.6.0" } }, - "node_modules/jest-runner": { - "version": "30.0.5", - "resolved": "https://registry.npmjs.org/jest-runner/-/jest-runner-30.0.5.tgz", - "integrity": "sha512-JcCOucZmgp+YuGgLAXHNy7ualBx4wYSgJVWrYMRBnb79j9PD0Jxh0EHvR5Cx/r0Ce+ZBC4hCdz2AzFFLl9hCiw==", + "node_modules/fast-glob/node_modules/glob-parent": { + "version": "5.1.2", + "resolved": "https://registry.npmjs.org/glob-parent/-/glob-parent-5.1.2.tgz", + "integrity": "sha512-AOIgSQCepiJYwP3ARnGx+5VnTu2HBYdzbGP45eLw1vr3zB3vZLeyed1sC9hnbcOc9/SrMyM5RPQrkGz4aS9Zow==", "dev": true, - "license": "MIT", + "license": "ISC", "dependencies": { - "@jest/console": "30.0.5", - "@jest/environment": "30.0.5", - "@jest/test-result": "30.0.5", - "@jest/transform": "30.0.5", - "@jest/types": "30.0.5", - "@types/node": "*", - "chalk": "^4.1.2", - "emittery": "^0.13.1", - "exit-x": "^0.2.2", - "graceful-fs": "^4.2.11", - "jest-docblock": "30.0.1", - "jest-environment-node": "30.0.5", - "jest-haste-map": "30.0.5", - "jest-leak-detector": "30.0.5", - "jest-message-util": "30.0.5", - "jest-resolve": "30.0.5", - "jest-runtime": "30.0.5", - "jest-util": "30.0.5", - "jest-watcher": "30.0.5", - "jest-worker": "30.0.5", - "p-limit": "^3.1.0", - "source-map-support": "0.5.13" + "is-glob": "^4.0.1" }, "engines": { - "node": "^18.14.0 || ^20.0.0 || ^22.0.0 || >=24.0.0" + "node": ">= 6" } }, - "node_modules/jest-runner/node_modules/ansi-styles": { - "version": "4.3.0", - "resolved": "https://registry.npmjs.org/ansi-styles/-/ansi-styles-4.3.0.tgz", - "integrity": "sha512-zbB9rCJAT1rbjiVDb2hqKFHNYLxgtk8NURxZ3IZwD3F6NtxbXZQCnnSi1Lkx+IDohdPlFp222wVALIheZJQSEg==", + "node_modules/fast-json-stable-stringify": { + "version": "2.1.0", + "resolved": "https://registry.npmjs.org/fast-json-stable-stringify/-/fast-json-stable-stringify-2.1.0.tgz", + "integrity": "sha512-lhd/wF+Lk98HZoTCtlVraHtfh5XYijIjalXck7saUtuanSDyLMxnHhSXEDJqHxD7msR8D0uCmqlkwjCV8xvwHw==", "dev": true, - "license": "MIT", + "license": "MIT" + }, + "node_modules/fast-levenshtein": { + "version": "2.0.6", + "resolved": "https://registry.npmjs.org/fast-levenshtein/-/fast-levenshtein-2.0.6.tgz", + "integrity": "sha512-DCXu6Ifhqcks7TZKY3Hxp3y6qphY5SJZmrWMDrKcERSOXWQdMhU9Ig/PYrzyw/ul9jOIyh0N4M0tbC5hodg8dw==", + "dev": true, + "license": "MIT" + }, + "node_modules/fastq": { + "version": "1.19.1", + "resolved": "https://registry.npmjs.org/fastq/-/fastq-1.19.1.tgz", + "integrity": "sha512-GwLTyxkCXjXbxqIhTsMI2Nui8huMPtnxg7krajPJAjnEG/iiOS7i+zCtWGZR9G0NBKbXKh6X9m9UIsYX/N6vvQ==", + "dev": true, + "license": "ISC", "dependencies": { - "color-convert": "^2.0.1" - }, + "reusify": "^1.0.4" + } + }, + "node_modules/fdir": { + "version": "6.5.0", + "resolved": "https://registry.npmjs.org/fdir/-/fdir-6.5.0.tgz", + "integrity": "sha512-tIbYtZbucOs0BRGqPJkshJUYdL+SDH7dVM8gjy+ERp3WAUjLEFJE+02kanyHtwjWOnwrKYBiwAmM0p4kLJAnXg==", + "dev": true, + "license": "MIT", "engines": { - "node": ">=8" + "node": ">=12.0.0" }, - "funding": { - "url": "https://github.com/chalk/ansi-styles?sponsor=1" + "peerDependencies": { + "picomatch": "^3 || ^4" + }, + "peerDependenciesMeta": { + "picomatch": { + "optional": true + } } }, - "node_modules/jest-runner/node_modules/chalk": { - "version": "4.1.2", - "resolved": "https://registry.npmjs.org/chalk/-/chalk-4.1.2.tgz", - "integrity": "sha512-oKnbhFyRIXpUuez8iBMmyEa4nbj4IOQyuhc/wy9kY7/WVPcwIO9VA668Pu8RkO7+0G76SLROeyw9CpQ061i4mA==", + "node_modules/file-entry-cache": { + "version": "8.0.0", + "resolved": "https://registry.npmjs.org/file-entry-cache/-/file-entry-cache-8.0.0.tgz", + "integrity": "sha512-XXTUwCvisa5oacNGRP9SfNtYBNAMi+RPwBFmblZEF7N7swHYQS6/Zfk7SRwx4D5j3CH211YNRco1DEMNVfZCnQ==", "dev": true, "license": "MIT", "dependencies": { - "ansi-styles": "^4.1.0", - "supports-color": "^7.1.0" + "flat-cache": "^4.0.0" }, "engines": { - "node": ">=10" - }, - "funding": { - "url": "https://github.com/chalk/chalk?sponsor=1" + "node": ">=16.0.0" } }, - "node_modules/jest-runtime": { - "version": "30.0.5", - "resolved": "https://registry.npmjs.org/jest-runtime/-/jest-runtime-30.0.5.tgz", - "integrity": "sha512-7oySNDkqpe4xpX5PPiJTe5vEa+Ak/NnNz2bGYZrA1ftG3RL3EFlHaUkA1Cjx+R8IhK0Vg43RML5mJedGTPNz3A==", + "node_modules/fill-range": { + "version": "7.1.1", + "resolved": "https://registry.npmjs.org/fill-range/-/fill-range-7.1.1.tgz", + "integrity": "sha512-YsGpe3WHLK8ZYi4tWDg2Jy3ebRz2rXowDxnld4bkQB00cc/1Zw9AWnC0i9ztDJitivtQvaI9KaLyKrc+hBW0yg==", "dev": true, "license": "MIT", "dependencies": { - "@jest/environment": "30.0.5", - "@jest/fake-timers": "30.0.5", - "@jest/globals": "30.0.5", - "@jest/source-map": "30.0.1", - "@jest/test-result": "30.0.5", - "@jest/transform": "30.0.5", - "@jest/types": "30.0.5", - "@types/node": "*", - "chalk": "^4.1.2", - "cjs-module-lexer": "^2.1.0", - "collect-v8-coverage": "^1.0.2", - "glob": "^10.3.10", - "graceful-fs": "^4.2.11", - "jest-haste-map": "30.0.5", - "jest-message-util": "30.0.5", - "jest-mock": "30.0.5", - "jest-regex-util": "30.0.1", - "jest-resolve": "30.0.5", - "jest-snapshot": "30.0.5", - "jest-util": "30.0.5", - "slash": "^3.0.0", - "strip-bom": "^4.0.0" + "to-regex-range": "^5.0.1" }, "engines": { - "node": "^18.14.0 || ^20.0.0 || ^22.0.0 || >=24.0.0" + "node": ">=8" } }, - "node_modules/jest-runtime/node_modules/ansi-styles": { - "version": "4.3.0", - "resolved": "https://registry.npmjs.org/ansi-styles/-/ansi-styles-4.3.0.tgz", - "integrity": "sha512-zbB9rCJAT1rbjiVDb2hqKFHNYLxgtk8NURxZ3IZwD3F6NtxbXZQCnnSi1Lkx+IDohdPlFp222wVALIheZJQSEg==", + "node_modules/find-up": { + "version": "5.0.0", + "resolved": "https://registry.npmjs.org/find-up/-/find-up-5.0.0.tgz", + "integrity": "sha512-78/PXT1wlLLDgTzDs7sjq9hzz0vXD+zn+7wypEe4fXQxCmdmqfGsEPQxmiCSQI3ajFV91bVSsvNtrJRiW6nGng==", "dev": true, "license": "MIT", "dependencies": { - "color-convert": "^2.0.1" + "locate-path": "^6.0.0", + "path-exists": "^4.0.0" }, "engines": { - "node": ">=8" + "node": ">=10" }, "funding": { - "url": "https://github.com/chalk/ansi-styles?sponsor=1" + "url": "https://github.com/sponsors/sindresorhus" } }, - "node_modules/jest-runtime/node_modules/chalk": { - "version": "4.1.2", - "resolved": "https://registry.npmjs.org/chalk/-/chalk-4.1.2.tgz", - "integrity": "sha512-oKnbhFyRIXpUuez8iBMmyEa4nbj4IOQyuhc/wy9kY7/WVPcwIO9VA668Pu8RkO7+0G76SLROeyw9CpQ061i4mA==", + "node_modules/flat-cache": { + "version": "4.0.1", + "resolved": "https://registry.npmjs.org/flat-cache/-/flat-cache-4.0.1.tgz", + "integrity": "sha512-f7ccFPK3SXFHpx15UIGyRJ/FJQctuKZ0zVuN3frBo4HnK3cay9VEW0R6yPYFHC0AgqhukPzKjq22t5DmAyqGyw==", "dev": true, "license": "MIT", "dependencies": { - "ansi-styles": "^4.1.0", - "supports-color": "^7.1.0" + "flatted": "^3.2.9", + "keyv": "^4.5.4" }, "engines": { - "node": ">=10" - }, - "funding": { - "url": "https://github.com/chalk/chalk?sponsor=1" + "node": ">=16" } }, - "node_modules/jest-snapshot": { - "version": "30.0.5", - "resolved": "https://registry.npmjs.org/jest-snapshot/-/jest-snapshot-30.0.5.tgz", - "integrity": "sha512-T00dWU/Ek3LqTp4+DcW6PraVxjk28WY5Ua/s+3zUKSERZSNyxTqhDXCWKG5p2HAJ+crVQ3WJ2P9YVHpj1tkW+g==", + "node_modules/flatted": { + "version": "3.3.3", + "resolved": "https://registry.npmjs.org/flatted/-/flatted-3.3.3.tgz", + "integrity": "sha512-GX+ysw4PBCz0PzosHDepZGANEuFCMLrnRTiEy9McGjmkCQYwRq4A/X786G/fjM/+OjsWSU1ZrY5qyARZmO/uwg==", + "dev": true, + "license": "ISC" + }, + "node_modules/fsevents": { + "version": "2.3.3", + "resolved": "https://registry.npmjs.org/fsevents/-/fsevents-2.3.3.tgz", + "integrity": "sha512-5xoDfX+fL7faATnagmWPpbFtwh/R77WmMMqqHGS65C3vvB0YHrgF+B1YmZ3441tMj5n63k0212XNoJwzlhffQw==", "dev": true, + "hasInstallScript": true, "license": "MIT", - "dependencies": { - "@babel/core": "^7.27.4", - "@babel/generator": "^7.27.5", - "@babel/plugin-syntax-jsx": "^7.27.1", - "@babel/plugin-syntax-typescript": "^7.27.1", - "@babel/types": "^7.27.3", - "@jest/expect-utils": "30.0.5", - "@jest/get-type": "30.0.1", - "@jest/snapshot-utils": "30.0.5", - "@jest/transform": "30.0.5", - "@jest/types": "30.0.5", - "babel-preset-current-node-syntax": "^1.1.0", - "chalk": "^4.1.2", - "expect": "30.0.5", - "graceful-fs": "^4.2.11", - "jest-diff": "30.0.5", - "jest-matcher-utils": "30.0.5", - "jest-message-util": "30.0.5", - "jest-util": "30.0.5", - "pretty-format": "30.0.5", - "semver": "^7.7.2", - "synckit": "^0.11.8" - }, + "optional": true, + "os": [ + "darwin" + ], "engines": { - "node": "^18.14.0 || ^20.0.0 || ^22.0.0 || >=24.0.0" + "node": "^8.16.0 || ^10.6.0 || >=11.0.0" } }, - "node_modules/jest-snapshot/node_modules/ansi-styles": { - "version": "4.3.0", - "resolved": "https://registry.npmjs.org/ansi-styles/-/ansi-styles-4.3.0.tgz", - "integrity": "sha512-zbB9rCJAT1rbjiVDb2hqKFHNYLxgtk8NURxZ3IZwD3F6NtxbXZQCnnSi1Lkx+IDohdPlFp222wVALIheZJQSEg==", + "node_modules/function-bind": { + "version": "1.1.2", + "resolved": "https://registry.npmjs.org/function-bind/-/function-bind-1.1.2.tgz", + "integrity": "sha512-7XHNxH7qX9xG5mIwxkhumTox/MIRNcOgDrxWsMt2pAr23WHp6MrRlN7FBSFpCpr+oVO0F744iUgR82nJMfG2SA==", "dev": true, "license": "MIT", - "dependencies": { - "color-convert": "^2.0.1" - }, - "engines": { - "node": ">=8" - }, "funding": { - "url": "https://github.com/chalk/ansi-styles?sponsor=1" + "url": "https://github.com/sponsors/ljharb" } }, - "node_modules/jest-snapshot/node_modules/chalk": { - "version": "4.1.2", - "resolved": "https://registry.npmjs.org/chalk/-/chalk-4.1.2.tgz", - "integrity": "sha512-oKnbhFyRIXpUuez8iBMmyEa4nbj4IOQyuhc/wy9kY7/WVPcwIO9VA668Pu8RkO7+0G76SLROeyw9CpQ061i4mA==", + "node_modules/get-stream": { + "version": "6.0.1", + "resolved": "https://registry.npmjs.org/get-stream/-/get-stream-6.0.1.tgz", + "integrity": "sha512-ts6Wi+2j3jQjqi70w5AlN8DFnkSwC+MqmxEzdEALB2qXZYV3X/b1CTfgPLGJNMeAWxdPfU8FO1ms3NUfaHCPYg==", "dev": true, "license": "MIT", - "dependencies": { - "ansi-styles": "^4.1.0", - "supports-color": "^7.1.0" - }, "engines": { "node": ">=10" }, "funding": { - "url": "https://github.com/chalk/chalk?sponsor=1" + "url": "https://github.com/sponsors/sindresorhus" } }, - "node_modules/jest-snapshot/node_modules/semver": { - "version": "7.7.2", - "resolved": "https://registry.npmjs.org/semver/-/semver-7.7.2.tgz", - "integrity": "sha512-RF0Fw+rO5AMf9MAyaRXI4AV0Ulj5lMHqVxxdSgiVbixSCXoEmmX/jk0CuJw4+3SqroYO9VoUh+HcuJivvtJemA==", + "node_modules/glob-parent": { + "version": "6.0.2", + "resolved": "https://registry.npmjs.org/glob-parent/-/glob-parent-6.0.2.tgz", + "integrity": "sha512-XxwI8EOhVQgWp6iDL+3b0r86f4d6AX6zSU55HfB4ydCEuXLXc5FcYeOu+nnGftS4TEju/11rt4KJPTMgbfmv4A==", "dev": true, "license": "ISC", - "bin": { - "semver": "bin/semver.js" + "dependencies": { + "is-glob": "^4.0.3" }, "engines": { - "node": ">=10" + "node": ">=10.13.0" } }, - "node_modules/jest-util": { - "version": "30.0.5", - "resolved": "https://registry.npmjs.org/jest-util/-/jest-util-30.0.5.tgz", - "integrity": "sha512-pvyPWssDZR0FlfMxCBoc0tvM8iUEskaRFALUtGQYzVEAqisAztmy+R8LnU14KT4XA0H/a5HMVTXat1jLne010g==", + "node_modules/globals": { + "version": "16.5.0", + "resolved": "https://registry.npmjs.org/globals/-/globals-16.5.0.tgz", + "integrity": "sha512-c/c15i26VrJ4IRt5Z89DnIzCGDn9EcebibhAOjw5ibqEHsE1wLUgkPn9RDmNcUKyU87GeaL633nyJ+pplFR2ZQ==", "dev": true, "license": "MIT", - "dependencies": { - "@jest/types": "30.0.5", - "@types/node": "*", - "chalk": "^4.1.2", - "ci-info": "^4.2.0", - "graceful-fs": "^4.2.11", - "picomatch": "^4.0.2" - }, "engines": { - "node": "^18.14.0 || ^20.0.0 || ^22.0.0 || >=24.0.0" + "node": ">=18" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" } }, - "node_modules/jest-util/node_modules/ansi-styles": { - "version": "4.3.0", - "resolved": "https://registry.npmjs.org/ansi-styles/-/ansi-styles-4.3.0.tgz", - "integrity": "sha512-zbB9rCJAT1rbjiVDb2hqKFHNYLxgtk8NURxZ3IZwD3F6NtxbXZQCnnSi1Lkx+IDohdPlFp222wVALIheZJQSEg==", + "node_modules/graphemer": { + "version": "1.4.0", + "resolved": "https://registry.npmjs.org/graphemer/-/graphemer-1.4.0.tgz", + "integrity": "sha512-EtKwoO6kxCL9WO5xipiHTZlSzBm7WLT627TqC/uVRd0HKmq8NXyebnNYxDoBi7wt8eTWrUrKXCOVaFq9x1kgag==", + "dev": true, + "license": "MIT" + }, + "node_modules/has-flag": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/has-flag/-/has-flag-4.0.0.tgz", + "integrity": "sha512-EykJT/Q1KjTWctppgIAgfSO0tKVuZUjhgMr17kqTumMl6Afv3EISleU7qZUzoXDFTAHTDC4NOoG/ZxU3EvlMPQ==", "dev": true, "license": "MIT", - "dependencies": { - "color-convert": "^2.0.1" - }, "engines": { "node": ">=8" - }, - "funding": { - "url": "https://github.com/chalk/ansi-styles?sponsor=1" } }, - "node_modules/jest-util/node_modules/chalk": { - "version": "4.1.2", - "resolved": "https://registry.npmjs.org/chalk/-/chalk-4.1.2.tgz", - "integrity": "sha512-oKnbhFyRIXpUuez8iBMmyEa4nbj4IOQyuhc/wy9kY7/WVPcwIO9VA668Pu8RkO7+0G76SLROeyw9CpQ061i4mA==", + "node_modules/hasown": { + "version": "2.0.2", + "resolved": "https://registry.npmjs.org/hasown/-/hasown-2.0.2.tgz", + "integrity": "sha512-0hJU9SCPvmMzIBdZFqNPXWa6dqh7WdH0cII9y+CyS8rG3nL48Bclra9HmKhVVUHyPWNH5Y7xDwAB7bfgSjkUMQ==", "dev": true, "license": "MIT", "dependencies": { - "ansi-styles": "^4.1.0", - "supports-color": "^7.1.0" + "function-bind": "^1.1.2" }, "engines": { - "node": ">=10" - }, - "funding": { - "url": "https://github.com/chalk/chalk?sponsor=1" + "node": ">= 0.4" + } + }, + "node_modules/human-signals": { + "version": "2.1.0", + "resolved": "https://registry.npmjs.org/human-signals/-/human-signals-2.1.0.tgz", + "integrity": "sha512-B4FFZ6q/T2jhhksgkbEW3HBvWIfDW85snkQgawt07S7J5QXTk6BkNV+0yAeZrM5QpMAdYlocGoljn0sJ/WQkFw==", + "dev": true, + "license": "Apache-2.0", + "engines": { + "node": ">=10.17.0" } }, - "node_modules/jest-validate": { - "version": "30.0.5", - "resolved": "https://registry.npmjs.org/jest-validate/-/jest-validate-30.0.5.tgz", - "integrity": "sha512-ouTm6VFHaS2boyl+k4u+Qip4TSH7Uld5tyD8psQ8abGgt2uYYB8VwVfAHWHjHc0NWmGGbwO5h0sCPOGHHevefw==", + "node_modules/ignore": { + "version": "7.0.5", + "resolved": "https://registry.npmjs.org/ignore/-/ignore-7.0.5.tgz", + "integrity": "sha512-Hs59xBNfUIunMFgWAbGX5cq6893IbWg4KnrjbYwX3tx0ztorVgTDA6B2sxf8ejHJ4wz8BqGUMYlnzNBer5NvGg==", "dev": true, "license": "MIT", - "dependencies": { - "@jest/get-type": "30.0.1", - "@jest/types": "30.0.5", - "camelcase": "^6.3.0", - "chalk": "^4.1.2", - "leven": "^3.1.0", - "pretty-format": "30.0.5" - }, "engines": { - "node": "^18.14.0 || ^20.0.0 || ^22.0.0 || >=24.0.0" + "node": ">= 4" } }, - "node_modules/jest-validate/node_modules/ansi-styles": { - "version": "4.3.0", - "resolved": "https://registry.npmjs.org/ansi-styles/-/ansi-styles-4.3.0.tgz", - "integrity": "sha512-zbB9rCJAT1rbjiVDb2hqKFHNYLxgtk8NURxZ3IZwD3F6NtxbXZQCnnSi1Lkx+IDohdPlFp222wVALIheZJQSEg==", + "node_modules/import-fresh": { + "version": "3.3.1", + "resolved": "https://registry.npmjs.org/import-fresh/-/import-fresh-3.3.1.tgz", + "integrity": "sha512-TR3KfrTZTYLPB6jUjfx6MF9WcWrHL9su5TObK4ZkYgBdWKPOFoSoQIdEuTuR82pmtxH2spWG9h6etwfr1pLBqQ==", "dev": true, "license": "MIT", "dependencies": { - "color-convert": "^2.0.1" + "parent-module": "^1.0.0", + "resolve-from": "^4.0.0" }, "engines": { - "node": ">=8" + "node": ">=6" }, "funding": { - "url": "https://github.com/chalk/ansi-styles?sponsor=1" + "url": "https://github.com/sponsors/sindresorhus" } }, - "node_modules/jest-validate/node_modules/camelcase": { - "version": "6.3.0", - "resolved": "https://registry.npmjs.org/camelcase/-/camelcase-6.3.0.tgz", - "integrity": "sha512-Gmy6FhYlCY7uOElZUSbxo2UCDH8owEk996gkbrpsgGtrJLM3J7jGxl9Ic7Qwwj4ivOE5AWZWRMecDdF7hqGjFA==", + "node_modules/imurmurhash": { + "version": "0.1.4", + "resolved": "https://registry.npmjs.org/imurmurhash/-/imurmurhash-0.1.4.tgz", + "integrity": "sha512-JmXMZ6wuvDmLiHEml9ykzqO6lwFbof0GG4IkcGaENdCRDDmMVnny7s5HsIgHCbaq0w2MyPhDqkhTUgS2LU2PHA==", "dev": true, "license": "MIT", "engines": { - "node": ">=10" - }, - "funding": { - "url": "https://github.com/sponsors/sindresorhus" + "node": ">=0.8.19" } }, - "node_modules/jest-validate/node_modules/chalk": { - "version": "4.1.2", - "resolved": "https://registry.npmjs.org/chalk/-/chalk-4.1.2.tgz", - "integrity": "sha512-oKnbhFyRIXpUuez8iBMmyEa4nbj4IOQyuhc/wy9kY7/WVPcwIO9VA668Pu8RkO7+0G76SLROeyw9CpQ061i4mA==", + "node_modules/is-core-module": { + "version": "2.16.1", + "resolved": "https://registry.npmjs.org/is-core-module/-/is-core-module-2.16.1.tgz", + "integrity": "sha512-UfoeMA6fIJ8wTYFEUjelnaGI67v6+N7qXJEvQuIGa99l4xsCruSYOVSQ0uPANn4dAzm8lkYPaKLrrijLq7x23w==", "dev": true, "license": "MIT", "dependencies": { - "ansi-styles": "^4.1.0", - "supports-color": "^7.1.0" + "hasown": "^2.0.2" }, "engines": { - "node": ">=10" + "node": ">= 0.4" }, "funding": { - "url": "https://github.com/chalk/chalk?sponsor=1" + "url": "https://github.com/sponsors/ljharb" } }, - "node_modules/jest-watcher": { - "version": "30.0.5", - "resolved": "https://registry.npmjs.org/jest-watcher/-/jest-watcher-30.0.5.tgz", - "integrity": "sha512-z9slj/0vOwBDBjN3L4z4ZYaA+pG56d6p3kTUhFRYGvXbXMWhXmb/FIxREZCD06DYUwDKKnj2T80+Pb71CQ0KEg==", + "node_modules/is-extglob": { + "version": "2.1.1", + "resolved": "https://registry.npmjs.org/is-extglob/-/is-extglob-2.1.1.tgz", + "integrity": "sha512-SbKbANkN603Vi4jEZv49LeVJMn4yGwsbzZworEoyEiutsN3nJYdbO36zfhGJ6QEDpOZIFkDtnq5JRxmvl3jsoQ==", "dev": true, "license": "MIT", - "dependencies": { - "@jest/test-result": "30.0.5", - "@jest/types": "30.0.5", - "@types/node": "*", - "ansi-escapes": "^4.3.2", - "chalk": "^4.1.2", - "emittery": "^0.13.1", - "jest-util": "30.0.5", - "string-length": "^4.0.2" - }, "engines": { - "node": "^18.14.0 || ^20.0.0 || ^22.0.0 || >=24.0.0" + "node": ">=0.10.0" } }, - "node_modules/jest-watcher/node_modules/ansi-styles": { - "version": "4.3.0", - "resolved": "https://registry.npmjs.org/ansi-styles/-/ansi-styles-4.3.0.tgz", - "integrity": "sha512-zbB9rCJAT1rbjiVDb2hqKFHNYLxgtk8NURxZ3IZwD3F6NtxbXZQCnnSi1Lkx+IDohdPlFp222wVALIheZJQSEg==", + "node_modules/is-glob": { + "version": "4.0.3", + "resolved": "https://registry.npmjs.org/is-glob/-/is-glob-4.0.3.tgz", + "integrity": "sha512-xelSayHH36ZgE7ZWhli7pW34hNbNl8Ojv5KVmkJD4hBdD3th8Tfk9vYasLM+mXWOZhFkgZfxhLSnrwRr4elSSg==", "dev": true, "license": "MIT", "dependencies": { - "color-convert": "^2.0.1" + "is-extglob": "^2.1.1" }, "engines": { - "node": ">=8" - }, - "funding": { - "url": "https://github.com/chalk/ansi-styles?sponsor=1" + "node": ">=0.10.0" } }, - "node_modules/jest-watcher/node_modules/chalk": { - "version": "4.1.2", - "resolved": "https://registry.npmjs.org/chalk/-/chalk-4.1.2.tgz", - "integrity": "sha512-oKnbhFyRIXpUuez8iBMmyEa4nbj4IOQyuhc/wy9kY7/WVPcwIO9VA668Pu8RkO7+0G76SLROeyw9CpQ061i4mA==", + "node_modules/is-module": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/is-module/-/is-module-1.0.0.tgz", + "integrity": "sha512-51ypPSPCoTEIN9dy5Oy+h4pShgJmPCygKfyRCISBI+JoWT/2oJvK8QPxmwv7b/p239jXrm9M1mlQbyKJ5A152g==", "dev": true, - "license": "MIT", - "dependencies": { - "ansi-styles": "^4.1.0", - "supports-color": "^7.1.0" - }, - "engines": { - "node": ">=10" - }, - "funding": { - "url": "https://github.com/chalk/chalk?sponsor=1" - } + "license": "MIT" }, - "node_modules/jest-worker": { - "version": "30.0.5", - "resolved": "https://registry.npmjs.org/jest-worker/-/jest-worker-30.0.5.tgz", - "integrity": "sha512-ojRXsWzEP16NdUuBw/4H/zkZdHOa7MMYCk4E430l+8fELeLg/mqmMlRhjL7UNZvQrDmnovWZV4DxX03fZF48fQ==", + "node_modules/is-number": { + "version": "7.0.0", + "resolved": "https://registry.npmjs.org/is-number/-/is-number-7.0.0.tgz", + "integrity": "sha512-41Cifkg6e8TylSpdtTpeLVMqvSBEVzTttHvERD741+pnZ8ANv0004MRL43QKPDlK9cGvNp6NZWZUBlbGXYxxng==", "dev": true, "license": "MIT", - "dependencies": { - "@types/node": "*", - "@ungap/structured-clone": "^1.3.0", - "jest-util": "30.0.5", - "merge-stream": "^2.0.0", - "supports-color": "^8.1.1" - }, "engines": { - "node": "^18.14.0 || ^20.0.0 || ^22.0.0 || >=24.0.0" + "node": ">=0.12.0" } }, - "node_modules/jest-worker/node_modules/supports-color": { - "version": "8.1.1", - "resolved": "https://registry.npmjs.org/supports-color/-/supports-color-8.1.1.tgz", - "integrity": "sha512-MpUEN2OodtUzxvKQl72cUF7RQ5EiHsGvSsVG0ia9c5RbWGL2CI4C7EpPS8UTBIplnlzZiNuV56w+FuNxy3ty2Q==", + "node_modules/is-stream": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/is-stream/-/is-stream-2.0.1.tgz", + "integrity": "sha512-hFoiJiTl63nn+kstHGBtewWSKnQLpyb155KHheA1l39uvtO9nWIop1p3udqPcUd/xbF1VLMO4n7OI6p7RbngDg==", "dev": true, "license": "MIT", - "dependencies": { - "has-flag": "^4.0.0" - }, "engines": { - "node": ">=10" + "node": ">=8" }, "funding": { - "url": "https://github.com/chalk/supports-color?sponsor=1" + "url": "https://github.com/sponsors/sindresorhus" } }, - "node_modules/js-tokens": { - "version": "4.0.0", - "resolved": "https://registry.npmjs.org/js-tokens/-/js-tokens-4.0.0.tgz", - "integrity": "sha512-RdJUflcE3cUzKiMqQgsCu06FPu9UdIJO0beYbPhHN4k6apgJtifcoCtT9bcxOpYBtpD2kCM6Sbzg4CausW/PKQ==", + "node_modules/isexe": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/isexe/-/isexe-2.0.0.tgz", + "integrity": "sha512-RHxMLp9lnKHGHRng9QFhRCMbYAcVpn69smSGcq3f36xjgVVWThj4qqLbTLlq7Ssj8B+fIQ1EuCEGI2lKsyQeIw==", "dev": true, - "license": "MIT" + "license": "ISC" }, "node_modules/js-yaml": { - "version": "3.14.1", - "resolved": "https://registry.npmjs.org/js-yaml/-/js-yaml-3.14.1.tgz", - "integrity": "sha512-okMH7OXXJ7YrN9Ok3/SXrnu4iX9yOk+25nqX4imS2npuvTYDmo/QEZoqwZkYaIDk3jVvBOTOIEgEhaLOynBS9g==", + "version": "4.1.0", + "resolved": "https://registry.npmjs.org/js-yaml/-/js-yaml-4.1.0.tgz", + "integrity": "sha512-wpxZs9NoxZaJESJGIZTyDEaYpl0FKSA+FB9aJiyemKhMwkxQg63h4T1KJgUGHpTqPDNRcmmYLugrRjJlBtWvRA==", "dev": true, "license": "MIT", "dependencies": { - "argparse": "^1.0.7", - "esprima": "^4.0.0" + "argparse": "^2.0.1" }, "bin": { "js-yaml": "bin/js-yaml.js" } }, - "node_modules/jsesc": { - "version": "3.1.0", - "resolved": "https://registry.npmjs.org/jsesc/-/jsesc-3.1.0.tgz", - "integrity": "sha512-/sM3dO2FOzXjKQhJuo0Q173wf2KOo8t4I8vHy6lF9poUp7bKT0/NHE8fPX23PwfhnykfqnC2xRxOnVw5XuGIaA==", - "dev": true, - "license": "MIT", - "bin": { - "jsesc": "bin/jsesc" - }, - "engines": { - "node": ">=6" - } - }, "node_modules/json-buffer": { "version": "3.0.1", "resolved": "https://registry.npmjs.org/json-buffer/-/json-buffer-3.0.1.tgz", @@ -6814,13 +1882,6 @@ "dev": true, "license": "MIT" }, - "node_modules/json-parse-even-better-errors": { - "version": "2.3.1", - "resolved": "https://registry.npmjs.org/json-parse-even-better-errors/-/json-parse-even-better-errors-2.3.1.tgz", - "integrity": "sha512-xyFwyhro/JEof6Ghe2iz2NcXoj2sloNsWr/XsERDK/oiPCfaNhl5ONfp+jQdAZRQQ0IJWNzH9zIZF7li91kh2w==", - "dev": true, - "license": "MIT" - }, "node_modules/json-schema-traverse": { "version": "0.4.1", "resolved": "https://registry.npmjs.org/json-schema-traverse/-/json-schema-traverse-0.4.1.tgz", @@ -6835,19 +1896,6 @@ "dev": true, "license": "MIT" }, - "node_modules/json5": { - "version": "2.2.3", - "resolved": "https://registry.npmjs.org/json5/-/json5-2.2.3.tgz", - "integrity": "sha512-XmOWe7eyHYH14cLdVPoyg+GOH3rYX++KpzrylJwSW98t3Nk+U8XOl8FWKOgwtzdb8lXGf6zYwDUzeHMWfxasyg==", - "dev": true, - "license": "MIT", - "bin": { - "json5": "lib/cli.js" - }, - "engines": { - "node": ">=6" - } - }, "node_modules/keyv": { "version": "4.5.4", "resolved": "https://registry.npmjs.org/keyv/-/keyv-4.5.4.tgz", @@ -6858,16 +1906,6 @@ "json-buffer": "3.0.1" } }, - "node_modules/leven": { - "version": "3.1.0", - "resolved": "https://registry.npmjs.org/leven/-/leven-3.1.0.tgz", - "integrity": "sha512-qsda+H8jTaUaN/x5vzW2rzc+8Rw4TAQ/4KjB46IwK5VH+IlVeeeje/EoZRpiXvIqjFgK84QffqPztGI3VBLG1A==", - "dev": true, - "license": "MIT", - "engines": { - "node": ">=6" - } - }, "node_modules/levn": { "version": "0.4.1", "resolved": "https://registry.npmjs.org/levn/-/levn-0.4.1.tgz", @@ -6882,24 +1920,20 @@ "node": ">= 0.8.0" } }, - "node_modules/lines-and-columns": { - "version": "1.2.4", - "resolved": "https://registry.npmjs.org/lines-and-columns/-/lines-and-columns-1.2.4.tgz", - "integrity": "sha512-7ylylesZQ/PV29jhEDl3Ufjo6ZX7gCqJr5F7PKrqc93v7fzSymt1BpwEU8nAUXs8qzzvqhbjhK5QZg6Mt/HkBg==", - "dev": true, - "license": "MIT" - }, "node_modules/locate-path": { - "version": "5.0.0", - "resolved": "https://registry.npmjs.org/locate-path/-/locate-path-5.0.0.tgz", - "integrity": "sha512-t7hw9pI+WvuwNJXwk5zVHpyhIqzg2qTlklJOf0mVxGSbe3Fp2VieZcduNYjaLDoy6p9uGpQEGWG87WpMKlNq8g==", + "version": "6.0.0", + "resolved": "https://registry.npmjs.org/locate-path/-/locate-path-6.0.0.tgz", + "integrity": "sha512-iPZK6eYjbxRu3uB4/WZ3EsEIMJFMqAoopl3R+zuq0UjcAm/MO6KCweDgPfP3elTztoKP3KtnVHxTn2NHBSDVUw==", "dev": true, "license": "MIT", "dependencies": { - "p-locate": "^4.1.0" + "p-locate": "^5.0.0" }, "engines": { - "node": ">=8" + "node": ">=10" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" } }, "node_modules/lodash": { @@ -6909,13 +1943,6 @@ "dev": true, "license": "MIT" }, - "node_modules/lodash.debounce": { - "version": "4.0.8", - "resolved": "https://registry.npmjs.org/lodash.debounce/-/lodash.debounce-4.0.8.tgz", - "integrity": "sha512-FT1yDzDYEoYWhnSGnpE/4Kj1fLZkDFyqRb7fNt6FdYOSxlUWAtp42Eh6Wb0rGIv/m9Bgo7x4GhQbm5Ys4SG5ow==", - "dev": true, - "license": "MIT" - }, "node_modules/lodash.merge": { "version": "4.6.2", "resolved": "https://registry.npmjs.org/lodash.merge/-/lodash.merge-4.6.2.tgz", @@ -6923,63 +1950,14 @@ "dev": true, "license": "MIT" }, - "node_modules/lru-cache": { - "version": "5.1.1", - "resolved": "https://registry.npmjs.org/lru-cache/-/lru-cache-5.1.1.tgz", - "integrity": "sha512-KpNARQA3Iwv+jTA0utUVVbrh+Jlrr1Fv0e56GGzAFOXN7dk/FviaDW8LHmK52DlcH4WP2n6gI8vN1aesBFgo9w==", - "dev": true, - "license": "ISC", - "dependencies": { - "yallist": "^3.0.2" - } - }, "node_modules/magic-string": { - "version": "0.30.17", - "resolved": "https://registry.npmjs.org/magic-string/-/magic-string-0.30.17.tgz", - "integrity": "sha512-sNPKHvyjVf7gyjwS4xGTaW/mCnF8wnjtifKBEhxfZ7E/S8tQ0rssrwGNn6q8JH/ohItJfSQp9mBtQYuTlH5QnA==", - "dev": true, - "license": "MIT", - "dependencies": { - "@jridgewell/sourcemap-codec": "^1.5.0" - } - }, - "node_modules/make-dir": { - "version": "4.0.0", - "resolved": "https://registry.npmjs.org/make-dir/-/make-dir-4.0.0.tgz", - "integrity": "sha512-hXdUTZYIVOt1Ex//jAQi+wTZZpUpwBj/0QsOzqegb3rGMMeJiSEu5xLHnYfBrRV4RH2+OCSOO95Is/7x1WJ4bw==", + "version": "0.30.21", + "resolved": "https://registry.npmjs.org/magic-string/-/magic-string-0.30.21.tgz", + "integrity": "sha512-vd2F4YUyEXKGcLHoq+TEyCjxueSeHnFxyyjNp80yg0XV4vUhnDer/lvvlqM/arB5bXQN5K2/3oinyCRyx8T2CQ==", "dev": true, "license": "MIT", "dependencies": { - "semver": "^7.5.3" - }, - "engines": { - "node": ">=10" - }, - "funding": { - "url": "https://github.com/sponsors/sindresorhus" - } - }, - "node_modules/make-dir/node_modules/semver": { - "version": "7.7.2", - "resolved": "https://registry.npmjs.org/semver/-/semver-7.7.2.tgz", - "integrity": "sha512-RF0Fw+rO5AMf9MAyaRXI4AV0Ulj5lMHqVxxdSgiVbixSCXoEmmX/jk0CuJw4+3SqroYO9VoUh+HcuJivvtJemA==", - "dev": true, - "license": "ISC", - "bin": { - "semver": "bin/semver.js" - }, - "engines": { - "node": ">=10" - } - }, - "node_modules/makeerror": { - "version": "1.0.12", - "resolved": "https://registry.npmjs.org/makeerror/-/makeerror-1.0.12.tgz", - "integrity": "sha512-JmqCvUhmt43madlpFzG4BQzG2Z3m6tvQDNKdClZnO3VbIudJYmxsT0FNJMeiB2+JTSlTQTSbU8QdesVmwJcmLg==", - "dev": true, - "license": "BSD-3-Clause", - "dependencies": { - "tmpl": "1.0.5" + "@jridgewell/sourcemap-codec": "^1.5.5" } }, "node_modules/merge-stream": { @@ -7052,16 +2030,6 @@ "url": "https://github.com/sponsors/isaacs" } }, - "node_modules/minipass": { - "version": "7.1.2", - "resolved": "https://registry.npmjs.org/minipass/-/minipass-7.1.2.tgz", - "integrity": "sha512-qOOzS1cBTWYF4BH8fVePDBOO9iptMnGUEZwNc/cMWnTV2nVLZ7VoNWEPHkYczZA0pdoA7dl6e7FL659nX9S2aw==", - "dev": true, - "license": "ISC", - "engines": { - "node": ">=16 || 14 >=14.17" - } - }, "node_modules/moment": { "version": "2.30.1", "resolved": "https://registry.npmjs.org/moment/-/moment-2.30.1.tgz", @@ -7079,22 +2047,6 @@ "dev": true, "license": "MIT" }, - "node_modules/napi-postinstall": { - "version": "0.3.2", - "resolved": "https://registry.npmjs.org/napi-postinstall/-/napi-postinstall-0.3.2.tgz", - "integrity": "sha512-tWVJxJHmBWLy69PvO96TZMZDrzmw5KeiZBz3RHmiM2XZ9grBJ2WgMAFVVg25nqp3ZjTFUs2Ftw1JhscL3Teliw==", - "dev": true, - "license": "MIT", - "bin": { - "napi-postinstall": "lib/cli.js" - }, - "engines": { - "node": "^12.20.0 || ^14.18.0 || >=16.0.0" - }, - "funding": { - "url": "https://opencollective.com/napi-postinstall" - } - }, "node_modules/natural-compare": { "version": "1.4.0", "resolved": "https://registry.npmjs.org/natural-compare/-/natural-compare-1.4.0.tgz", @@ -7102,30 +2054,6 @@ "dev": true, "license": "MIT" }, - "node_modules/node-int64": { - "version": "0.4.0", - "resolved": "https://registry.npmjs.org/node-int64/-/node-int64-0.4.0.tgz", - "integrity": "sha512-O5lz91xSOeoXP6DulyHfllpq+Eg00MWitZIbtPfoSEvqIHdl5gfcY6hYzDWnj0qD5tz52PI08u9qUvSVeUBeHw==", - "dev": true, - "license": "MIT" - }, - "node_modules/node-releases": { - "version": "2.0.19", - "resolved": "https://registry.npmjs.org/node-releases/-/node-releases-2.0.19.tgz", - "integrity": "sha512-xxOWJsBKtzAq7DY0J+DTzuz58K8e7sJbdgwkbMWQe8UYB6ekmsQ45q0M/tJDsGaZmbC+l7n57UV8Hl5tHxO9uw==", - "dev": true, - "license": "MIT" - }, - "node_modules/normalize-path": { - "version": "3.0.0", - "resolved": "https://registry.npmjs.org/normalize-path/-/normalize-path-3.0.0.tgz", - "integrity": "sha512-6eZs5Ls3WtCisHWp9S2GUy8dqkpGi4BVSz3GaqiE6ezub0512ESztXUwUB6C6IKbQkY2Pnb/mD4WYojCRwcwLA==", - "dev": true, - "license": "MIT", - "engines": { - "node": ">=0.10.0" - } - }, "node_modules/npm-run-path": { "version": "4.0.1", "resolved": "https://registry.npmjs.org/npm-run-path/-/npm-run-path-4.0.1.tgz", @@ -7139,16 +2067,6 @@ "node": ">=8" } }, - "node_modules/once": { - "version": "1.4.0", - "resolved": "https://registry.npmjs.org/once/-/once-1.4.0.tgz", - "integrity": "sha512-lNaJgI+2Q5URQBkccEKHTQOPaXdUxnZZElQTZY0MFUAuaEqe1E+Nyvgdz/aIyNi6Z9MzO5dv1H8n58/GELp3+w==", - "dev": true, - "license": "ISC", - "dependencies": { - "wrappy": "1" - } - }, "node_modules/onetime": { "version": "5.1.2", "resolved": "https://registry.npmjs.org/onetime/-/onetime-5.1.2.tgz", @@ -7200,51 +2118,21 @@ } }, "node_modules/p-locate": { - "version": "4.1.0", - "resolved": "https://registry.npmjs.org/p-locate/-/p-locate-4.1.0.tgz", - "integrity": "sha512-R79ZZ/0wAxKGu3oYMlz8jy/kbhsNrS7SKZ7PxEHBgJ5+F2mtFW2fK2cOtBh1cHYkQsbzFV7I+EoRKe6Yt0oK7A==", - "dev": true, - "license": "MIT", - "dependencies": { - "p-limit": "^2.2.0" - }, - "engines": { - "node": ">=8" - } - }, - "node_modules/p-locate/node_modules/p-limit": { - "version": "2.3.0", - "resolved": "https://registry.npmjs.org/p-limit/-/p-limit-2.3.0.tgz", - "integrity": "sha512-//88mFWSJx8lxCzwdAABTJL2MyWB12+eIY7MDL2SqLmAkeKU9qxRvWuSyTjm3FUmpBEMuFfckAIqEaVGUDxb6w==", + "version": "5.0.0", + "resolved": "https://registry.npmjs.org/p-locate/-/p-locate-5.0.0.tgz", + "integrity": "sha512-LaNjtRWUBY++zB5nE/NwcaoMylSPk+S+ZHNB1TzdbMJMny6dynpAGt7X/tl/QYq3TIeE6nxHppbo2LGymrG5Pw==", "dev": true, "license": "MIT", "dependencies": { - "p-try": "^2.0.0" + "p-limit": "^3.0.2" }, "engines": { - "node": ">=6" + "node": ">=10" }, "funding": { "url": "https://github.com/sponsors/sindresorhus" } }, - "node_modules/p-try": { - "version": "2.2.0", - "resolved": "https://registry.npmjs.org/p-try/-/p-try-2.2.0.tgz", - "integrity": "sha512-R4nPAVTAU0B9D35/Gk3uJf/7XYbQcyohSKdvAxIRSNghFl4e71hVoGnBNQz9cWaXxO2I10KTC+3jMdvvoKw6dQ==", - "dev": true, - "license": "MIT", - "engines": { - "node": ">=6" - } - }, - "node_modules/package-json-from-dist": { - "version": "1.0.1", - "resolved": "https://registry.npmjs.org/package-json-from-dist/-/package-json-from-dist-1.0.1.tgz", - "integrity": "sha512-UEZIS3/by4OC8vL3P2dTXRETpebLI2NiI5vIrjaD/5UtrkFX/tNbwjTSRAGC/+7CAo2pIcBaRgWmcBBHcsaCIw==", - "dev": true, - "license": "BlueOak-1.0.0" - }, "node_modules/package-name-regex": { "version": "2.0.6", "resolved": "https://registry.npmjs.org/package-name-regex/-/package-name-regex-2.0.6.tgz", @@ -7271,25 +2159,6 @@ "node": ">=6" } }, - "node_modules/parse-json": { - "version": "5.2.0", - "resolved": "https://registry.npmjs.org/parse-json/-/parse-json-5.2.0.tgz", - "integrity": "sha512-ayCKvm/phCGxOkYRSCM82iDwct8/EonSEgCSxWxD7ve6jHggsFl4fZVQBPRNgQoKiuV/odhFrGzQXZwbifC8Rg==", - "dev": true, - "license": "MIT", - "dependencies": { - "@babel/code-frame": "^7.0.0", - "error-ex": "^1.3.1", - "json-parse-even-better-errors": "^2.3.0", - "lines-and-columns": "^1.1.6" - }, - "engines": { - "node": ">=8" - }, - "funding": { - "url": "https://github.com/sponsors/sindresorhus" - } - }, "node_modules/path-exists": { "version": "4.0.0", "resolved": "https://registry.npmjs.org/path-exists/-/path-exists-4.0.0.tgz", @@ -7300,16 +2169,6 @@ "node": ">=8" } }, - "node_modules/path-is-absolute": { - "version": "1.0.1", - "resolved": "https://registry.npmjs.org/path-is-absolute/-/path-is-absolute-1.0.1.tgz", - "integrity": "sha512-AVbw3UJ2e9bq64vSaS9Am0fje1Pa8pbGqTTsmXfaIiMpnr5DlDhfJOuLj9Sf95ZPVDAUerDfEk88MPmPe7UCQg==", - "dev": true, - "license": "MIT", - "engines": { - "node": ">=0.10.0" - } - }, "node_modules/path-key": { "version": "3.1.1", "resolved": "https://registry.npmjs.org/path-key/-/path-key-3.1.1.tgz", @@ -7327,37 +2186,6 @@ "dev": true, "license": "MIT" }, - "node_modules/path-scurry": { - "version": "1.11.1", - "resolved": "https://registry.npmjs.org/path-scurry/-/path-scurry-1.11.1.tgz", - "integrity": "sha512-Xa4Nw17FS9ApQFJ9umLiJS4orGjm7ZzwUrwamcGQuHSzDyth9boKDaycYdDcZDuqYATXw4HFXgaqWTctW/v1HA==", - "dev": true, - "license": "BlueOak-1.0.0", - "dependencies": { - "lru-cache": "^10.2.0", - "minipass": "^5.0.0 || ^6.0.2 || ^7.0.0" - }, - "engines": { - "node": ">=16 || 14 >=14.18" - }, - "funding": { - "url": "https://github.com/sponsors/isaacs" - } - }, - "node_modules/path-scurry/node_modules/lru-cache": { - "version": "10.4.3", - "resolved": "https://registry.npmjs.org/lru-cache/-/lru-cache-10.4.3.tgz", - "integrity": "sha512-JNAzZcXrCt42VGLuYz0zfAzDfAvJWW6AfYlDBQyDV5DClI2m5sAmK+OIO7s59XfsRsWHp02jAJrRadPRGTt6SQ==", - "dev": true, - "license": "ISC" - }, - "node_modules/picocolors": { - "version": "1.1.1", - "resolved": "https://registry.npmjs.org/picocolors/-/picocolors-1.1.1.tgz", - "integrity": "sha512-xceH2snhtb5M9liqDsmEw56le376mTZkEX/jEb/RxNFyegNul7eNslCXP9FDj/Lcu0X8KEyMceP2ntpaHrDEVA==", - "dev": true, - "license": "ISC" - }, "node_modules/picomatch": { "version": "4.0.3", "resolved": "https://registry.npmjs.org/picomatch/-/picomatch-4.0.3.tgz", @@ -7371,76 +2199,6 @@ "url": "https://github.com/sponsors/jonschlinkert" } }, - "node_modules/pirates": { - "version": "4.0.7", - "resolved": "https://registry.npmjs.org/pirates/-/pirates-4.0.7.tgz", - "integrity": "sha512-TfySrs/5nm8fQJDcBDuUng3VOUKsd7S+zqvbOTiGXHfxX4wK31ard+hoNuvkicM/2YFzlpDgABOevKSsB4G/FA==", - "dev": true, - "license": "MIT", - "engines": { - "node": ">= 6" - } - }, - "node_modules/pkg-dir": { - "version": "4.2.0", - "resolved": "https://registry.npmjs.org/pkg-dir/-/pkg-dir-4.2.0.tgz", - "integrity": "sha512-HRDzbaKjC+AOWVXxAU/x54COGeIv9eb+6CkDSQoNTt4XyWoIJvuPsXizxu/Fr23EiekbtZwmh1IcIG/l/a10GQ==", - "dev": true, - "license": "MIT", - "dependencies": { - "find-up": "^4.0.0" - }, - "engines": { - "node": ">=8" - } - }, - "node_modules/playwright": { - "version": "1.56.1", - "resolved": "https://registry.npmjs.org/playwright/-/playwright-1.56.1.tgz", - "integrity": "sha512-aFi5B0WovBHTEvpM3DzXTUaeN6eN0qWnTkKx4NQaH4Wvcmc153PdaY2UBdSYKaGYw+UyWXSVyxDUg5DoPEttjw==", - "dev": true, - "license": "Apache-2.0", - "dependencies": { - "playwright-core": "1.56.1" - }, - "bin": { - "playwright": "cli.js" - }, - "engines": { - "node": ">=18" - }, - "optionalDependencies": { - "fsevents": "2.3.2" - } - }, - "node_modules/playwright-core": { - "version": "1.56.1", - "resolved": "https://registry.npmjs.org/playwright-core/-/playwright-core-1.56.1.tgz", - "integrity": "sha512-hutraynyn31F+Bifme+Ps9Vq59hKuUCz7H1kDOcBs+2oGguKkWTU50bBWrtz34OUWmIwpBTWDxaRPXrIXkgvmQ==", - "dev": true, - "license": "Apache-2.0", - "bin": { - "playwright-core": "cli.js" - }, - "engines": { - "node": ">=18" - } - }, - "node_modules/playwright/node_modules/fsevents": { - "version": "2.3.2", - "resolved": "https://registry.npmjs.org/fsevents/-/fsevents-2.3.2.tgz", - "integrity": "sha512-xiqMQR4xAeHTuB9uWm+fFRcIOgKBMiOBP+eXiyT7jsgVCq1bkVygt00oASowB7EdtpOHaaPgKt812P9ab+DDKA==", - "dev": true, - "hasInstallScript": true, - "license": "MIT", - "optional": true, - "os": [ - "darwin" - ], - "engines": { - "node": "^8.16.0 || ^10.6.0 || >=11.0.0" - } - }, "node_modules/prelude-ls": { "version": "1.2.1", "resolved": "https://registry.npmjs.org/prelude-ls/-/prelude-ls-1.2.1.tgz", @@ -7451,21 +2209,6 @@ "node": ">= 0.8.0" } }, - "node_modules/pretty-format": { - "version": "30.0.5", - "resolved": "https://registry.npmjs.org/pretty-format/-/pretty-format-30.0.5.tgz", - "integrity": "sha512-D1tKtYvByrBkFLe2wHJl2bwMJIiT8rW+XA+TiataH79/FszLQMrpGEvzUVkzPau7OCO0Qnrhpe87PqtOAIB8Yw==", - "dev": true, - "license": "MIT", - "dependencies": { - "@jest/schemas": "30.0.5", - "ansi-styles": "^5.2.0", - "react-is": "^18.3.1" - }, - "engines": { - "node": "^18.14.0 || ^20.0.0 || ^22.0.0 || >=24.0.0" - } - }, "node_modules/punycode": { "version": "2.3.1", "resolved": "https://registry.npmjs.org/punycode/-/punycode-2.3.1.tgz", @@ -7476,23 +2219,6 @@ "node": ">=6" } }, - "node_modules/pure-rand": { - "version": "7.0.1", - "resolved": "https://registry.npmjs.org/pure-rand/-/pure-rand-7.0.1.tgz", - "integrity": "sha512-oTUZM/NAZS8p7ANR3SHh30kXB+zK2r2BPcEn/awJIbOvq82WoMN4p62AWWp3Hhw50G0xMsw1mhIBLqHw64EcNQ==", - "dev": true, - "funding": [ - { - "type": "individual", - "url": "https://github.com/sponsors/dubzzz" - }, - { - "type": "opencollective", - "url": "https://opencollective.com/fast-check" - } - ], - "license": "MIT" - }, "node_modules/queue-microtask": { "version": "1.2.3", "resolved": "https://registry.npmjs.org/queue-microtask/-/queue-microtask-1.2.3.tgz", @@ -7510,116 +2236,28 @@ { "type": "consulting", "url": "https://feross.org/support" - } - ], - "license": "MIT" - }, - "node_modules/randombytes": { - "version": "2.1.0", - "resolved": "https://registry.npmjs.org/randombytes/-/randombytes-2.1.0.tgz", - "integrity": "sha512-vYl3iOX+4CKUWuxGi9Ukhie6fsqXqS9FE2Zaic4tNFD2N2QQaXOMFbuKK4QmDHC0JO6B1Zp41J0LpT0oR68amQ==", - "dev": true, - "license": "MIT", - "dependencies": { - "safe-buffer": "^5.1.0" - } - }, - "node_modules/react-is": { - "version": "18.3.1", - "resolved": "https://registry.npmjs.org/react-is/-/react-is-18.3.1.tgz", - "integrity": "sha512-/LLMVyas0ljjAtoYiPqYiL8VWXzUUdThrmU5+n20DZv+a+ClRoevUzw5JxU+Ieh5/c87ytoTBV9G1FiKfNJdmg==", - "dev": true, - "license": "MIT" - }, - "node_modules/regenerate": { - "version": "1.4.2", - "resolved": "https://registry.npmjs.org/regenerate/-/regenerate-1.4.2.tgz", - "integrity": "sha512-zrceR/XhGYU/d/opr2EKO7aRHUeiBI8qjtfHqADTwZd6Szfy16la6kqD0MIUs5z5hx6AaKa+PixpPrR289+I0A==", - "dev": true, - "license": "MIT" - }, - "node_modules/regenerate-unicode-properties": { - "version": "10.2.0", - "resolved": "https://registry.npmjs.org/regenerate-unicode-properties/-/regenerate-unicode-properties-10.2.0.tgz", - "integrity": "sha512-DqHn3DwbmmPVzeKj9woBadqmXxLvQoQIwu7nopMc72ztvxVmVk2SBhSnx67zuye5TP+lJsb/TBQsjLKhnDf3MA==", - "dev": true, - "license": "MIT", - "dependencies": { - "regenerate": "^1.4.2" - }, - "engines": { - "node": ">=4" - } - }, - "node_modules/regexpu-core": { - "version": "6.2.0", - "resolved": "https://registry.npmjs.org/regexpu-core/-/regexpu-core-6.2.0.tgz", - "integrity": "sha512-H66BPQMrv+V16t8xtmq+UC0CBpiTBA60V8ibS1QVReIp8T1z8hwFxqcGzm9K6lgsN7sB5edVH8a+ze6Fqm4weA==", - "dev": true, - "license": "MIT", - "dependencies": { - "regenerate": "^1.4.2", - "regenerate-unicode-properties": "^10.2.0", - "regjsgen": "^0.8.0", - "regjsparser": "^0.12.0", - "unicode-match-property-ecmascript": "^2.0.0", - "unicode-match-property-value-ecmascript": "^2.1.0" - }, - "engines": { - "node": ">=4" - } - }, - "node_modules/regjsgen": { - "version": "0.8.0", - "resolved": "https://registry.npmjs.org/regjsgen/-/regjsgen-0.8.0.tgz", - "integrity": "sha512-RvwtGe3d7LvWiDQXeQw8p5asZUmfU1G/l6WbUXeHta7Y2PEIvBTwH6E2EfmYUK8pxcxEdEmaomqyp0vZZ7C+3Q==", - "dev": true, - "license": "MIT" - }, - "node_modules/regjsparser": { - "version": "0.12.0", - "resolved": "https://registry.npmjs.org/regjsparser/-/regjsparser-0.12.0.tgz", - "integrity": "sha512-cnE+y8bz4NhMjISKbgeVJtqNbtf5QpjZP+Bslo+UqkIt9QPnX9q095eiRRASJG1/tz6dlNr6Z5NsBiWYokp6EQ==", - "dev": true, - "license": "BSD-2-Clause", - "dependencies": { - "jsesc": "~3.0.2" - }, - "bin": { - "regjsparser": "bin/parser" - } - }, - "node_modules/regjsparser/node_modules/jsesc": { - "version": "3.0.2", - "resolved": "https://registry.npmjs.org/jsesc/-/jsesc-3.0.2.tgz", - "integrity": "sha512-xKqzzWXDttJuOcawBt4KnKHHIf5oQ/Cxax+0PWFG+DFDgHNAdi+TXECADI+RYiFUMmx8792xsMbbgXj4CwnP4g==", - "dev": true, - "license": "MIT", - "bin": { - "jsesc": "bin/jsesc" - }, - "engines": { - "node": ">=6" - } + } + ], + "license": "MIT" }, - "node_modules/require-directory": { - "version": "2.1.1", - "resolved": "https://registry.npmjs.org/require-directory/-/require-directory-2.1.1.tgz", - "integrity": "sha512-fGxEI7+wsG9xrvdjsrlmL22OMTTiHRwAMroiEeMgq8gzoLC/PQr7RsRDSTLUg/bZAZtF+TVIkHc6/4RIKrui+Q==", + "node_modules/randombytes": { + "version": "2.1.0", + "resolved": "https://registry.npmjs.org/randombytes/-/randombytes-2.1.0.tgz", + "integrity": "sha512-vYl3iOX+4CKUWuxGi9Ukhie6fsqXqS9FE2Zaic4tNFD2N2QQaXOMFbuKK4QmDHC0JO6B1Zp41J0LpT0oR68amQ==", "dev": true, "license": "MIT", - "engines": { - "node": ">=0.10.0" + "dependencies": { + "safe-buffer": "^5.1.0" } }, "node_modules/resolve": { - "version": "1.22.10", - "resolved": "https://registry.npmjs.org/resolve/-/resolve-1.22.10.tgz", - "integrity": "sha512-NPRy+/ncIMeDlTAsuqwKIiferiawhefFJtkNSW0qZJEqMEb+qBt/77B/jGeeek+F0uOeN05CDa6HXbbIgtVX4w==", + "version": "1.22.11", + "resolved": "https://registry.npmjs.org/resolve/-/resolve-1.22.11.tgz", + "integrity": "sha512-RfqAvLnMl313r7c9oclB1HhUEAezcpLjz95wFH4LVuhk9JF/r22qmVP9AMmOU4vMX7Q8pN8jwNg/CSpdFnMjTQ==", "dev": true, "license": "MIT", "dependencies": { - "is-core-module": "^2.16.0", + "is-core-module": "^2.16.1", "path-parse": "^1.0.7", "supports-preserve-symlinks-flag": "^1.0.0" }, @@ -7633,27 +2271,14 @@ "url": "https://github.com/sponsors/ljharb" } }, - "node_modules/resolve-cwd": { - "version": "3.0.0", - "resolved": "https://registry.npmjs.org/resolve-cwd/-/resolve-cwd-3.0.0.tgz", - "integrity": "sha512-OrZaX2Mb+rJCpH/6CpSqt9xFVpN++x01XnN2ie9g6P5/3xelLAkXWVADpdz1IHD/KFfEXyE6V0U01OQ3UO2rEg==", - "dev": true, - "license": "MIT", - "dependencies": { - "resolve-from": "^5.0.0" - }, - "engines": { - "node": ">=8" - } - }, "node_modules/resolve-from": { - "version": "5.0.0", - "resolved": "https://registry.npmjs.org/resolve-from/-/resolve-from-5.0.0.tgz", - "integrity": "sha512-qYg9KP24dD5qka9J47d0aVky0N+b4fTU89LN9iDnjB5waksiC49rvMB0PrUJQGoTmH50XPiqOvAjDfaijGxYZw==", + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/resolve-from/-/resolve-from-4.0.0.tgz", + "integrity": "sha512-pb/MYmXstAkysRFx8piNI1tGFNQIFA3vkE3Gq4EuA1dF6gHp/+vgZqsCGJapvy8N3Q+4o7FwvquPJcnZ7RYy4g==", "dev": true, "license": "MIT", "engines": { - "node": ">=8" + "node": ">=4" } }, "node_modules/reusify": { @@ -7668,9 +2293,9 @@ } }, "node_modules/rollup": { - "version": "4.45.1", - "resolved": "https://registry.npmjs.org/rollup/-/rollup-4.45.1.tgz", - "integrity": "sha512-4iya7Jb76fVpQyLoiVpzUrsjQ12r3dM7fIVz+4NwoYvZOShknRmiv+iu9CClZml5ZLGb0XMcYLutK6w9tgxHDw==", + "version": "4.53.2", + "resolved": "https://registry.npmjs.org/rollup/-/rollup-4.53.2.tgz", + "integrity": "sha512-MHngMYwGJVi6Fmnk6ISmnk7JAHRNF0UkuucA0CUW3N3a4KnONPEZz+vUanQP/ZC/iY1Qkf3bwPWzyY84wEks1g==", "dev": true, "license": "MIT", "dependencies": { @@ -7684,26 +2309,28 @@ "npm": ">=8.0.0" }, "optionalDependencies": { - "@rollup/rollup-android-arm-eabi": "4.45.1", - "@rollup/rollup-android-arm64": "4.45.1", - "@rollup/rollup-darwin-arm64": "4.45.1", - "@rollup/rollup-darwin-x64": "4.45.1", - "@rollup/rollup-freebsd-arm64": "4.45.1", - "@rollup/rollup-freebsd-x64": "4.45.1", - "@rollup/rollup-linux-arm-gnueabihf": "4.45.1", - "@rollup/rollup-linux-arm-musleabihf": "4.45.1", - "@rollup/rollup-linux-arm64-gnu": "4.45.1", - "@rollup/rollup-linux-arm64-musl": "4.45.1", - "@rollup/rollup-linux-loongarch64-gnu": "4.45.1", - "@rollup/rollup-linux-powerpc64le-gnu": "4.45.1", - "@rollup/rollup-linux-riscv64-gnu": "4.45.1", - "@rollup/rollup-linux-riscv64-musl": "4.45.1", - "@rollup/rollup-linux-s390x-gnu": "4.45.1", - "@rollup/rollup-linux-x64-gnu": "4.45.1", - "@rollup/rollup-linux-x64-musl": "4.45.1", - "@rollup/rollup-win32-arm64-msvc": "4.45.1", - "@rollup/rollup-win32-ia32-msvc": "4.45.1", - "@rollup/rollup-win32-x64-msvc": "4.45.1", + "@rollup/rollup-android-arm-eabi": "4.53.2", + "@rollup/rollup-android-arm64": "4.53.2", + "@rollup/rollup-darwin-arm64": "4.53.2", + "@rollup/rollup-darwin-x64": "4.53.2", + "@rollup/rollup-freebsd-arm64": "4.53.2", + "@rollup/rollup-freebsd-x64": "4.53.2", + "@rollup/rollup-linux-arm-gnueabihf": "4.53.2", + "@rollup/rollup-linux-arm-musleabihf": "4.53.2", + "@rollup/rollup-linux-arm64-gnu": "4.53.2", + "@rollup/rollup-linux-arm64-musl": "4.53.2", + "@rollup/rollup-linux-loong64-gnu": "4.53.2", + "@rollup/rollup-linux-ppc64-gnu": "4.53.2", + "@rollup/rollup-linux-riscv64-gnu": "4.53.2", + "@rollup/rollup-linux-riscv64-musl": "4.53.2", + "@rollup/rollup-linux-s390x-gnu": "4.53.2", + "@rollup/rollup-linux-x64-gnu": "4.53.2", + "@rollup/rollup-linux-x64-musl": "4.53.2", + "@rollup/rollup-openharmony-arm64": "4.53.2", + "@rollup/rollup-win32-arm64-msvc": "4.53.2", + "@rollup/rollup-win32-ia32-msvc": "4.53.2", + "@rollup/rollup-win32-x64-gnu": "4.53.2", + "@rollup/rollup-win32-x64-msvc": "4.53.2", "fsevents": "~2.3.2" } }, @@ -7731,9 +2358,9 @@ } }, "node_modules/rollup-plugin-sourcemaps2": { - "version": "0.5.3", - "resolved": "https://registry.npmjs.org/rollup-plugin-sourcemaps2/-/rollup-plugin-sourcemaps2-0.5.3.tgz", - "integrity": "sha512-KmD8A50Lvi/FVkmu8Xo1LXFfhE5tCK/CIAWcnN44cJlgjyGR1l8WqYrSpJ+SQ3e6KNSj7GBvwqUg/4nQt3Agow==", + "version": "0.5.4", + "resolved": "https://registry.npmjs.org/rollup-plugin-sourcemaps2/-/rollup-plugin-sourcemaps2-0.5.4.tgz", + "integrity": "sha512-XK6ITvEsKtUFN1GQbYKoqilwh1yKxTS9BLaFlVsm0IaYUYe3eVnhBWzKP4AHbkBO2BNOheGNlf407K7wCj6Rrw==", "dev": true, "license": "MIT", "dependencies": { @@ -7752,6 +2379,29 @@ } } }, + "node_modules/rollup-plugin-sourcemaps2/node_modules/@rollup/pluginutils": { + "version": "5.2.0", + "resolved": "https://registry.npmjs.org/@rollup/pluginutils/-/pluginutils-5.2.0.tgz", + "integrity": "sha512-qWJ2ZTbmumwiLFomfzTyt5Kng4hwPi9rwCYN4SHb6eaRU1KNO4ccxINHr/VhH4GgPlt1XfSTLX2LBTme8ne4Zw==", + "dev": true, + "license": "MIT", + "dependencies": { + "@types/estree": "^1.0.0", + "estree-walker": "^2.0.2", + "picomatch": "^4.0.2" + }, + "engines": { + "node": ">=14.0.0" + }, + "peerDependencies": { + "rollup": "^1.20.0||^2.0.0||^3.0.0||^4.0.0" + }, + "peerDependenciesMeta": { + "rollup": { + "optional": true + } + } + }, "node_modules/run-parallel": { "version": "1.2.0", "resolved": "https://registry.npmjs.org/run-parallel/-/run-parallel-1.2.0.tgz", @@ -7798,13 +2448,16 @@ "license": "MIT" }, "node_modules/semver": { - "version": "6.3.1", - "resolved": "https://registry.npmjs.org/semver/-/semver-6.3.1.tgz", - "integrity": "sha512-BR7VvDCVHO+q2xBEWskxS6DJE1qRnb7DxzUrogb71CWoSficBxYsiAGd+Kl0mmq/MprG9yArRkyrQxTO6XjMzA==", + "version": "7.7.3", + "resolved": "https://registry.npmjs.org/semver/-/semver-7.7.3.tgz", + "integrity": "sha512-SdsKMrI9TdgjdweUSR9MweHA4EJ8YxHn8DFaDisvhVlUOe4BF1tLD7GAj0lIqWVl+dPb/rExr0Btby5loQm20Q==", "dev": true, "license": "ISC", "bin": { "semver": "bin/semver.js" + }, + "engines": { + "node": ">=10" } }, "node_modules/serialize-javascript": { @@ -7855,27 +2508,11 @@ } }, "node_modules/signal-exit": { - "version": "4.1.0", - "resolved": "https://registry.npmjs.org/signal-exit/-/signal-exit-4.1.0.tgz", - "integrity": "sha512-bzyZ1e88w9O1iNJbKnOlvYTrWPDl46O1bG0D3XInv+9tkPrxrN8jUUTiFlDkkmKWgn1M6CfIA13SuGqOa9Korw==", - "dev": true, - "license": "ISC", - "engines": { - "node": ">=14" - }, - "funding": { - "url": "https://github.com/sponsors/isaacs" - } - }, - "node_modules/slash": { - "version": "3.0.0", - "resolved": "https://registry.npmjs.org/slash/-/slash-3.0.0.tgz", - "integrity": "sha512-g9Q1haeby36OSStwb4ntCGGGaKsaVSjQ68fBxoQcutl5fS1vuY18H3wSt3jFyFtrkx+Kz0V1G85A4MyAdDMi2Q==", + "version": "3.0.7", + "resolved": "https://registry.npmjs.org/signal-exit/-/signal-exit-3.0.7.tgz", + "integrity": "sha512-wnD2ZE+l+SPC/uoS0vXeE9L1+0wuaMqKlfz9AMUo38JsyLSBWSFcHR1Rri62LZc12vLr1gb3jl7iwQhgwpAbGQ==", "dev": true, - "license": "MIT", - "engines": { - "node": ">=8" - } + "license": "ISC" }, "node_modules/smob": { "version": "1.5.0", @@ -7895,9 +2532,9 @@ } }, "node_modules/source-map-support": { - "version": "0.5.13", - "resolved": "https://registry.npmjs.org/source-map-support/-/source-map-support-0.5.13.tgz", - "integrity": "sha512-SHSKFHadjVA5oR4PPqhtAVdcBWwRYVd6g6cAXnIbRiIwc2EhPrTuKUBdSLvlEKyIP3GCf89fltvcZiP9MMFA1w==", + "version": "0.5.21", + "resolved": "https://registry.npmjs.org/source-map-support/-/source-map-support-0.5.21.tgz", + "integrity": "sha512-uBHU3L3czsIyYXKX88fdrGovxdSCoTGDRZ6SYXtSRxLZUzHg5P/66Ht6uoUlHu9EZod+inXhKo3qQgwXUT/y1w==", "dev": true, "license": "MIT", "dependencies": { @@ -7946,9 +2583,9 @@ } }, "node_modules/spdx-license-ids": { - "version": "3.0.21", - "resolved": "https://registry.npmjs.org/spdx-license-ids/-/spdx-license-ids-3.0.21.tgz", - "integrity": "sha512-Bvg/8F5XephndSK3JffaRqdT+gyhfqIPwDHpX80tJrF8QQRYMo8sNMeaZ2Dp5+jhwKnUmIOyFFQfHRkjJm5nXg==", + "version": "3.0.22", + "resolved": "https://registry.npmjs.org/spdx-license-ids/-/spdx-license-ids-3.0.22.tgz", + "integrity": "sha512-4PRT4nh1EImPbt2jASOKHX7PB7I+e4IWNLvkKFDxNhJlfjbYlleYQh285Z/3mPTHSAK/AvdMmw5BNNuYH8ShgQ==", "dev": true, "license": "CC0-1.0" }, @@ -7971,187 +2608,6 @@ "spdx-ranges": "^2.0.0" } }, - "node_modules/sprintf-js": { - "version": "1.0.3", - "resolved": "https://registry.npmjs.org/sprintf-js/-/sprintf-js-1.0.3.tgz", - "integrity": "sha512-D9cPgkvLlV3t3IzL0D0YLvGA9Ahk4PcvVwUbN0dSGr1aP0Nrt4AEnTUbuGvquEC0mA64Gqt1fzirlRs5ibXx8g==", - "dev": true, - "license": "BSD-3-Clause" - }, - "node_modules/stack-utils": { - "version": "2.0.6", - "resolved": "https://registry.npmjs.org/stack-utils/-/stack-utils-2.0.6.tgz", - "integrity": "sha512-XlkWvfIm6RmsWtNJx+uqtKLS8eqFbxUg0ZzLXqY0caEy9l7hruX8IpiDnjsLavoBgqCCR71TqWO8MaXYheJ3RQ==", - "dev": true, - "license": "MIT", - "dependencies": { - "escape-string-regexp": "^2.0.0" - }, - "engines": { - "node": ">=10" - } - }, - "node_modules/stack-utils/node_modules/escape-string-regexp": { - "version": "2.0.0", - "resolved": "https://registry.npmjs.org/escape-string-regexp/-/escape-string-regexp-2.0.0.tgz", - "integrity": "sha512-UpzcLCXolUWcNu5HtVMHYdXJjArjsF9C0aNnquZYY4uW/Vu0miy5YoWvbV345HauVvcAUnpRuhMMcqTcGOY2+w==", - "dev": true, - "license": "MIT", - "engines": { - "node": ">=8" - } - }, - "node_modules/string-length": { - "version": "4.0.2", - "resolved": "https://registry.npmjs.org/string-length/-/string-length-4.0.2.tgz", - "integrity": "sha512-+l6rNN5fYHNhZZy41RXsYptCjA2Igmq4EG7kZAYFQI1E1VTXarr6ZPXBg6eq7Y6eK4FEhY6AJlyuFIb/v/S0VQ==", - "dev": true, - "license": "MIT", - "dependencies": { - "char-regex": "^1.0.2", - "strip-ansi": "^6.0.0" - }, - "engines": { - "node": ">=10" - } - }, - "node_modules/string-length/node_modules/ansi-regex": { - "version": "5.0.1", - "resolved": "https://registry.npmjs.org/ansi-regex/-/ansi-regex-5.0.1.tgz", - "integrity": "sha512-quJQXlTSUGL2LH9SUXo8VwsY4soanhgo6LNSm84E1LBcE8s3O0wpdiRzyR9z/ZZJMlMWv37qOOb9pdJlMUEKFQ==", - "dev": true, - "license": "MIT", - "engines": { - "node": ">=8" - } - }, - "node_modules/string-length/node_modules/strip-ansi": { - "version": "6.0.1", - "resolved": "https://registry.npmjs.org/strip-ansi/-/strip-ansi-6.0.1.tgz", - "integrity": "sha512-Y38VPSHcqkFrCpFnQ9vuSXmquuv5oXOKpGeT6aGrr3o3Gc9AlVa6JBfUSOCnbxGGZF+/0ooI7KrPuUSztUdU5A==", - "dev": true, - "license": "MIT", - "dependencies": { - "ansi-regex": "^5.0.1" - }, - "engines": { - "node": ">=8" - } - }, - "node_modules/string-width": { - "version": "5.1.2", - "resolved": "https://registry.npmjs.org/string-width/-/string-width-5.1.2.tgz", - "integrity": "sha512-HnLOCR3vjcY8beoNLtcjZ5/nxn2afmME6lhrDrebokqMap+XbeW8n9TXpPDOqdGK5qcI3oT0GKTW6wC7EMiVqA==", - "dev": true, - "license": "MIT", - "dependencies": { - "eastasianwidth": "^0.2.0", - "emoji-regex": "^9.2.2", - "strip-ansi": "^7.0.1" - }, - "engines": { - "node": ">=12" - }, - "funding": { - "url": "https://github.com/sponsors/sindresorhus" - } - }, - "node_modules/string-width-cjs": { - "name": "string-width", - "version": "4.2.3", - "resolved": "https://registry.npmjs.org/string-width/-/string-width-4.2.3.tgz", - "integrity": "sha512-wKyQRQpjJ0sIp62ErSZdGsjMJWsap5oRNihHhu6G7JVO/9jIB6UyevL+tXuOqrng8j/cxKTWyWUwvSTriiZz/g==", - "dev": true, - "license": "MIT", - "dependencies": { - "emoji-regex": "^8.0.0", - "is-fullwidth-code-point": "^3.0.0", - "strip-ansi": "^6.0.1" - }, - "engines": { - "node": ">=8" - } - }, - "node_modules/string-width-cjs/node_modules/ansi-regex": { - "version": "5.0.1", - "resolved": "https://registry.npmjs.org/ansi-regex/-/ansi-regex-5.0.1.tgz", - "integrity": "sha512-quJQXlTSUGL2LH9SUXo8VwsY4soanhgo6LNSm84E1LBcE8s3O0wpdiRzyR9z/ZZJMlMWv37qOOb9pdJlMUEKFQ==", - "dev": true, - "license": "MIT", - "engines": { - "node": ">=8" - } - }, - "node_modules/string-width-cjs/node_modules/emoji-regex": { - "version": "8.0.0", - "resolved": "https://registry.npmjs.org/emoji-regex/-/emoji-regex-8.0.0.tgz", - "integrity": "sha512-MSjYzcWNOA0ewAHpz0MxpYFvwg6yjy1NG3xteoqz644VCo/RPgnr1/GGt+ic3iJTzQ8Eu3TdM14SawnVUmGE6A==", - "dev": true, - "license": "MIT" - }, - "node_modules/string-width-cjs/node_modules/strip-ansi": { - "version": "6.0.1", - "resolved": "https://registry.npmjs.org/strip-ansi/-/strip-ansi-6.0.1.tgz", - "integrity": "sha512-Y38VPSHcqkFrCpFnQ9vuSXmquuv5oXOKpGeT6aGrr3o3Gc9AlVa6JBfUSOCnbxGGZF+/0ooI7KrPuUSztUdU5A==", - "dev": true, - "license": "MIT", - "dependencies": { - "ansi-regex": "^5.0.1" - }, - "engines": { - "node": ">=8" - } - }, - "node_modules/strip-ansi": { - "version": "7.1.0", - "resolved": "https://registry.npmjs.org/strip-ansi/-/strip-ansi-7.1.0.tgz", - "integrity": "sha512-iq6eVVI64nQQTRYq2KtEg2d2uU7LElhTJwsH4YzIHZshxlgZms/wIc4VoDQTlG/IvVIrBKG06CrZnp0qv7hkcQ==", - "dev": true, - "license": "MIT", - "dependencies": { - "ansi-regex": "^6.0.1" - }, - "engines": { - "node": ">=12" - }, - "funding": { - "url": "https://github.com/chalk/strip-ansi?sponsor=1" - } - }, - "node_modules/strip-ansi-cjs": { - "name": "strip-ansi", - "version": "6.0.1", - "resolved": "https://registry.npmjs.org/strip-ansi/-/strip-ansi-6.0.1.tgz", - "integrity": "sha512-Y38VPSHcqkFrCpFnQ9vuSXmquuv5oXOKpGeT6aGrr3o3Gc9AlVa6JBfUSOCnbxGGZF+/0ooI7KrPuUSztUdU5A==", - "dev": true, - "license": "MIT", - "dependencies": { - "ansi-regex": "^5.0.1" - }, - "engines": { - "node": ">=8" - } - }, - "node_modules/strip-ansi-cjs/node_modules/ansi-regex": { - "version": "5.0.1", - "resolved": "https://registry.npmjs.org/ansi-regex/-/ansi-regex-5.0.1.tgz", - "integrity": "sha512-quJQXlTSUGL2LH9SUXo8VwsY4soanhgo6LNSm84E1LBcE8s3O0wpdiRzyR9z/ZZJMlMWv37qOOb9pdJlMUEKFQ==", - "dev": true, - "license": "MIT", - "engines": { - "node": ">=8" - } - }, - "node_modules/strip-bom": { - "version": "4.0.0", - "resolved": "https://registry.npmjs.org/strip-bom/-/strip-bom-4.0.0.tgz", - "integrity": "sha512-3xurFv5tEgii33Zi8Jtp55wEIILR9eh34FAW00PZf+JnSsTmV/ioewSgQl97JHvgjoRGwPShsWm+IdrxB35d0w==", - "dev": true, - "license": "MIT", - "engines": { - "node": ">=8" - } - }, "node_modules/strip-final-newline": { "version": "2.0.0", "resolved": "https://registry.npmjs.org/strip-final-newline/-/strip-final-newline-2.0.0.tgz", @@ -8184,137 +2640,42 @@ "dependencies": { "has-flag": "^4.0.0" }, - "engines": { - "node": ">=8" - } - }, - "node_modules/supports-preserve-symlinks-flag": { - "version": "1.0.0", - "resolved": "https://registry.npmjs.org/supports-preserve-symlinks-flag/-/supports-preserve-symlinks-flag-1.0.0.tgz", - "integrity": "sha512-ot0WnXS9fgdkgIcePe6RHNk1WA8+muPa6cSjeR3V8K27q9BB1rTE3R1p7Hv0z1ZyAc8s6Vvv8DIyWf681MAt0w==", - "dev": true, - "license": "MIT", - "engines": { - "node": ">= 0.4" - }, - "funding": { - "url": "https://github.com/sponsors/ljharb" - } - }, - "node_modules/synckit": { - "version": "0.11.11", - "resolved": "https://registry.npmjs.org/synckit/-/synckit-0.11.11.tgz", - "integrity": "sha512-MeQTA1r0litLUf0Rp/iisCaL8761lKAZHaimlbGK4j0HysC4PLfqygQj9srcs0m2RdtDYnF8UuYyKpbjHYp7Jw==", - "dev": true, - "license": "MIT", - "dependencies": { - "@pkgr/core": "^0.2.9" - }, - "engines": { - "node": "^14.18.0 || >=16.0.0" - }, - "funding": { - "url": "https://opencollective.com/synckit" - } - }, - "node_modules/terser": { - "version": "5.43.1", - "resolved": "https://registry.npmjs.org/terser/-/terser-5.43.1.tgz", - "integrity": "sha512-+6erLbBm0+LROX2sPXlUYx/ux5PyE9K/a92Wrt6oA+WDAoFTdpHE5tCYCI5PNzq2y8df4rA+QgHLJuR4jNymsg==", - "dev": true, - "license": "BSD-2-Clause", - "dependencies": { - "@jridgewell/source-map": "^0.3.3", - "acorn": "^8.14.0", - "commander": "^2.20.0", - "source-map-support": "~0.5.20" - }, - "bin": { - "terser": "bin/terser" - }, - "engines": { - "node": ">=10" - } - }, - "node_modules/terser/node_modules/source-map-support": { - "version": "0.5.21", - "resolved": "https://registry.npmjs.org/source-map-support/-/source-map-support-0.5.21.tgz", - "integrity": "sha512-uBHU3L3czsIyYXKX88fdrGovxdSCoTGDRZ6SYXtSRxLZUzHg5P/66Ht6uoUlHu9EZod+inXhKo3qQgwXUT/y1w==", - "dev": true, - "license": "MIT", - "dependencies": { - "buffer-from": "^1.0.0", - "source-map": "^0.6.0" - } - }, - "node_modules/test-exclude": { - "version": "6.0.0", - "resolved": "https://registry.npmjs.org/test-exclude/-/test-exclude-6.0.0.tgz", - "integrity": "sha512-cAGWPIyOHU6zlmg88jwm7VRyXnMN7iV68OGAbYDk/Mh/xC/pzVPlQtY6ngoIH/5/tciuhGfvESU8GrHrcxD56w==", - "dev": true, - "license": "ISC", - "dependencies": { - "@istanbuljs/schema": "^0.1.2", - "glob": "^7.1.4", - "minimatch": "^3.0.4" - }, - "engines": { - "node": ">=8" - } - }, - "node_modules/test-exclude/node_modules/brace-expansion": { - "version": "1.1.12", - "resolved": "https://registry.npmjs.org/brace-expansion/-/brace-expansion-1.1.12.tgz", - "integrity": "sha512-9T9UjW3r0UW5c1Q7GTwllptXwhvYmEzFhzMfZ9H7FQWt+uZePjZPjBP/W1ZEyZ1twGWom5/56TF4lPcqjnDHcg==", - "dev": true, - "license": "MIT", - "dependencies": { - "balanced-match": "^1.0.0", - "concat-map": "0.0.1" + "engines": { + "node": ">=8" } }, - "node_modules/test-exclude/node_modules/glob": { - "version": "7.2.3", - "resolved": "https://registry.npmjs.org/glob/-/glob-7.2.3.tgz", - "integrity": "sha512-nFR0zLpU2YCaRxwoCJvL6UvCH2JFyFVIvwTLsIf21AuHlMskA1hhTdk+LlYJtOlYt9v6dvszD2BGRqBL+iQK9Q==", - "deprecated": "Glob versions prior to v9 are no longer supported", + "node_modules/supports-preserve-symlinks-flag": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/supports-preserve-symlinks-flag/-/supports-preserve-symlinks-flag-1.0.0.tgz", + "integrity": "sha512-ot0WnXS9fgdkgIcePe6RHNk1WA8+muPa6cSjeR3V8K27q9BB1rTE3R1p7Hv0z1ZyAc8s6Vvv8DIyWf681MAt0w==", "dev": true, - "license": "ISC", - "dependencies": { - "fs.realpath": "^1.0.0", - "inflight": "^1.0.4", - "inherits": "2", - "minimatch": "^3.1.1", - "once": "^1.3.0", - "path-is-absolute": "^1.0.0" - }, + "license": "MIT", "engines": { - "node": "*" + "node": ">= 0.4" }, "funding": { - "url": "https://github.com/sponsors/isaacs" + "url": "https://github.com/sponsors/ljharb" } }, - "node_modules/test-exclude/node_modules/minimatch": { - "version": "3.1.2", - "resolved": "https://registry.npmjs.org/minimatch/-/minimatch-3.1.2.tgz", - "integrity": "sha512-J7p63hRiAjw1NDEww1W7i37+ByIrOWO5XQQAzZ3VOcL0PNybwpfmV/N05zFAzwQ9USyEcX6t3UO+K5aqBQOIHw==", + "node_modules/terser": { + "version": "5.44.1", + "resolved": "https://registry.npmjs.org/terser/-/terser-5.44.1.tgz", + "integrity": "sha512-t/R3R/n0MSwnnazuPpPNVO60LX0SKL45pyl9YlvxIdkH0Of7D5qM2EVe+yASRIlY5pZ73nclYJfNANGWPwFDZw==", "dev": true, - "license": "ISC", + "license": "BSD-2-Clause", "dependencies": { - "brace-expansion": "^1.1.7" + "@jridgewell/source-map": "^0.3.3", + "acorn": "^8.15.0", + "commander": "^2.20.0", + "source-map-support": "~0.5.20" + }, + "bin": { + "terser": "bin/terser" }, "engines": { - "node": "*" + "node": ">=10" } }, - "node_modules/tmpl": { - "version": "1.0.5", - "resolved": "https://registry.npmjs.org/tmpl/-/tmpl-1.0.5.tgz", - "integrity": "sha512-3f0uOEAQwIqGuWW2MVzYg8fV/QNnc/IpuJNG837rLuczAaLVHslWHZQj4IGiEl5Hs3kkbhwL9Ab7Hrsmuj+Smw==", - "dev": true, - "license": "BSD-3-Clause" - }, "node_modules/to-regex-range": { "version": "5.0.1", "resolved": "https://registry.npmjs.org/to-regex-range/-/to-regex-range-5.0.1.tgz", @@ -8360,33 +2721,10 @@ "node": ">= 0.8.0" } }, - "node_modules/type-detect": { - "version": "4.0.8", - "resolved": "https://registry.npmjs.org/type-detect/-/type-detect-4.0.8.tgz", - "integrity": "sha512-0fr/mIH1dlO+x7TlcMy+bIDqKPsw/70tVyeHW787goQjhmqaZe10uwLujubK9q9Lg6Fiho1KUKDYz0Z7k7g5/g==", - "dev": true, - "license": "MIT", - "engines": { - "node": ">=4" - } - }, - "node_modules/type-fest": { - "version": "0.21.3", - "resolved": "https://registry.npmjs.org/type-fest/-/type-fest-0.21.3.tgz", - "integrity": "sha512-t0rzBq87m3fVcduHDUFhKmyyX+9eo6WQjZvf51Ea/M0Q7+T374Jp1aUiyUl0GKxp8M/OETVHSDvmkyPgvX+X2w==", - "dev": true, - "license": "(MIT OR CC0-1.0)", - "engines": { - "node": ">=10" - }, - "funding": { - "url": "https://github.com/sponsors/sindresorhus" - } - }, "node_modules/typescript": { - "version": "5.8.3", - "resolved": "https://registry.npmjs.org/typescript/-/typescript-5.8.3.tgz", - "integrity": "sha512-p1diW6TqL9L07nNxvRMM7hMMw4c5XOo/1ibL4aAIGmSAt9slTE1Xgw5KWuof2uTOvCg9BY7ZRi+GaF+7sfgPeQ==", + "version": "5.9.3", + "resolved": "https://registry.npmjs.org/typescript/-/typescript-5.9.3.tgz", + "integrity": "sha512-jl1vZzPDinLr9eUt3J/t7V6FgNEw9QjvBPdysz9KfQDD41fQrC2Y4vKQdiaUpFT4bXlb1RHhLpp8wtm6M5TgSw==", "dev": true, "license": "Apache-2.0", "bin": { @@ -8397,123 +2735,6 @@ "node": ">=14.17" } }, - "node_modules/undici-types": { - "version": "7.8.0", - "resolved": "https://registry.npmjs.org/undici-types/-/undici-types-7.8.0.tgz", - "integrity": "sha512-9UJ2xGDvQ43tYyVMpuHlsgApydB8ZKfVYTsLDhXkFL/6gfkp+U8xTGdh8pMJv1SpZna0zxG1DwsKZsreLbXBxw==", - "dev": true, - "license": "MIT" - }, - "node_modules/unicode-canonical-property-names-ecmascript": { - "version": "2.0.1", - "resolved": "https://registry.npmjs.org/unicode-canonical-property-names-ecmascript/-/unicode-canonical-property-names-ecmascript-2.0.1.tgz", - "integrity": "sha512-dA8WbNeb2a6oQzAQ55YlT5vQAWGV9WXOsi3SskE3bcCdM0P4SDd+24zS/OCacdRq5BkdsRj9q3Pg6YyQoxIGqg==", - "dev": true, - "license": "MIT", - "engines": { - "node": ">=4" - } - }, - "node_modules/unicode-match-property-ecmascript": { - "version": "2.0.0", - "resolved": "https://registry.npmjs.org/unicode-match-property-ecmascript/-/unicode-match-property-ecmascript-2.0.0.tgz", - "integrity": "sha512-5kaZCrbp5mmbz5ulBkDkbY0SsPOjKqVS35VpL9ulMPfSl0J0Xsm+9Evphv9CoIZFwre7aJoa94AY6seMKGVN5Q==", - "dev": true, - "license": "MIT", - "dependencies": { - "unicode-canonical-property-names-ecmascript": "^2.0.0", - "unicode-property-aliases-ecmascript": "^2.0.0" - }, - "engines": { - "node": ">=4" - } - }, - "node_modules/unicode-match-property-value-ecmascript": { - "version": "2.2.0", - "resolved": "https://registry.npmjs.org/unicode-match-property-value-ecmascript/-/unicode-match-property-value-ecmascript-2.2.0.tgz", - "integrity": "sha512-4IehN3V/+kkr5YeSSDDQG8QLqO26XpL2XP3GQtqwlT/QYSECAwFztxVHjlbh0+gjJ3XmNLS0zDsbgs9jWKExLg==", - "dev": true, - "license": "MIT", - "engines": { - "node": ">=4" - } - }, - "node_modules/unicode-property-aliases-ecmascript": { - "version": "2.1.0", - "resolved": "https://registry.npmjs.org/unicode-property-aliases-ecmascript/-/unicode-property-aliases-ecmascript-2.1.0.tgz", - "integrity": "sha512-6t3foTQI9qne+OZoVQB/8x8rk2k1eVy1gRXhV3oFQ5T6R1dqQ1xtin3XqSlx3+ATBkliTaR/hHyJBm+LVPNM8w==", - "dev": true, - "license": "MIT", - "engines": { - "node": ">=4" - } - }, - "node_modules/unrs-resolver": { - "version": "1.11.1", - "resolved": "https://registry.npmjs.org/unrs-resolver/-/unrs-resolver-1.11.1.tgz", - "integrity": "sha512-bSjt9pjaEBnNiGgc9rUiHGKv5l4/TGzDmYw3RhnkJGtLhbnnA/5qJj7x3dNDCRx/PJxu774LlH8lCOlB4hEfKg==", - "dev": true, - "hasInstallScript": true, - "license": "MIT", - "dependencies": { - "napi-postinstall": "^0.3.0" - }, - "funding": { - "url": "https://opencollective.com/unrs-resolver" - }, - "optionalDependencies": { - "@unrs/resolver-binding-android-arm-eabi": "1.11.1", - "@unrs/resolver-binding-android-arm64": "1.11.1", - "@unrs/resolver-binding-darwin-arm64": "1.11.1", - "@unrs/resolver-binding-darwin-x64": "1.11.1", - "@unrs/resolver-binding-freebsd-x64": "1.11.1", - "@unrs/resolver-binding-linux-arm-gnueabihf": "1.11.1", - "@unrs/resolver-binding-linux-arm-musleabihf": "1.11.1", - "@unrs/resolver-binding-linux-arm64-gnu": "1.11.1", - "@unrs/resolver-binding-linux-arm64-musl": "1.11.1", - "@unrs/resolver-binding-linux-ppc64-gnu": "1.11.1", - "@unrs/resolver-binding-linux-riscv64-gnu": "1.11.1", - "@unrs/resolver-binding-linux-riscv64-musl": "1.11.1", - "@unrs/resolver-binding-linux-s390x-gnu": "1.11.1", - "@unrs/resolver-binding-linux-x64-gnu": "1.11.1", - "@unrs/resolver-binding-linux-x64-musl": "1.11.1", - "@unrs/resolver-binding-wasm32-wasi": "1.11.1", - "@unrs/resolver-binding-win32-arm64-msvc": "1.11.1", - "@unrs/resolver-binding-win32-ia32-msvc": "1.11.1", - "@unrs/resolver-binding-win32-x64-msvc": "1.11.1" - } - }, - "node_modules/update-browserslist-db": { - "version": "1.1.3", - "resolved": "https://registry.npmjs.org/update-browserslist-db/-/update-browserslist-db-1.1.3.tgz", - "integrity": "sha512-UxhIZQ+QInVdunkDAaiazvvT/+fXL5Osr0JZlJulepYu6Jd7qJtDZjlur0emRlT71EN3ScPoE7gvsuIKKNavKw==", - "dev": true, - "funding": [ - { - "type": "opencollective", - "url": "https://opencollective.com/browserslist" - }, - { - "type": "tidelift", - "url": "https://tidelift.com/funding/github/npm/browserslist" - }, - { - "type": "github", - "url": "https://github.com/sponsors/ai" - } - ], - "license": "MIT", - "dependencies": { - "escalade": "^3.2.0", - "picocolors": "^1.1.1" - }, - "bin": { - "update-browserslist-db": "cli.js" - }, - "peerDependencies": { - "browserslist": ">= 4.21.0" - } - }, "node_modules/uri-js": { "version": "4.4.1", "resolved": "https://registry.npmjs.org/uri-js/-/uri-js-4.4.1.tgz", @@ -8524,31 +2745,6 @@ "punycode": "^2.1.0" } }, - "node_modules/v8-to-istanbul": { - "version": "9.3.0", - "resolved": "https://registry.npmjs.org/v8-to-istanbul/-/v8-to-istanbul-9.3.0.tgz", - "integrity": "sha512-kiGUalWN+rgBJ/1OHZsBtU4rXZOfj/7rKQxULKlIzwzQSvMJUUNgPwJEEh7gU6xEVxC0ahoOBvN2YI8GH6FNgA==", - "dev": true, - "license": "ISC", - "dependencies": { - "@jridgewell/trace-mapping": "^0.3.12", - "@types/istanbul-lib-coverage": "^2.0.1", - "convert-source-map": "^2.0.0" - }, - "engines": { - "node": ">=10.12.0" - } - }, - "node_modules/walker": { - "version": "1.0.8", - "resolved": "https://registry.npmjs.org/walker/-/walker-1.0.8.tgz", - "integrity": "sha512-ts/8E8l5b7kY0vlWLewOkDXMmPdLcVV4GmOQLyxuSswIJsweeFZtAsMF7k1Nszz+TYBQrlYRmzOnr398y1JemQ==", - "dev": true, - "license": "Apache-2.0", - "dependencies": { - "makeerror": "1.0.12" - } - }, "node_modules/which": { "version": "2.0.2", "resolved": "https://registry.npmjs.org/which/-/which-2.0.2.tgz", @@ -8575,229 +2771,6 @@ "node": ">=0.10.0" } }, - "node_modules/wrap-ansi": { - "version": "8.1.0", - "resolved": "https://registry.npmjs.org/wrap-ansi/-/wrap-ansi-8.1.0.tgz", - "integrity": "sha512-si7QWI6zUMq56bESFvagtmzMdGOtoxfR+Sez11Mobfc7tm+VkUckk9bW2UeffTGVUbOksxmSw0AA2gs8g71NCQ==", - "dev": true, - "license": "MIT", - "dependencies": { - "ansi-styles": "^6.1.0", - "string-width": "^5.0.1", - "strip-ansi": "^7.0.1" - }, - "engines": { - "node": ">=12" - }, - "funding": { - "url": "https://github.com/chalk/wrap-ansi?sponsor=1" - } - }, - "node_modules/wrap-ansi-cjs": { - "name": "wrap-ansi", - "version": "7.0.0", - "resolved": "https://registry.npmjs.org/wrap-ansi/-/wrap-ansi-7.0.0.tgz", - "integrity": "sha512-YVGIj2kamLSTxw6NsZjoBxfSwsn0ycdesmc4p+Q21c5zPuZ1pl+NfxVdxPtdHvmNVOQ6XSYG4AUtyt/Fi7D16Q==", - "dev": true, - "license": "MIT", - "dependencies": { - "ansi-styles": "^4.0.0", - "string-width": "^4.1.0", - "strip-ansi": "^6.0.0" - }, - "engines": { - "node": ">=10" - }, - "funding": { - "url": "https://github.com/chalk/wrap-ansi?sponsor=1" - } - }, - "node_modules/wrap-ansi-cjs/node_modules/ansi-regex": { - "version": "5.0.1", - "resolved": "https://registry.npmjs.org/ansi-regex/-/ansi-regex-5.0.1.tgz", - "integrity": "sha512-quJQXlTSUGL2LH9SUXo8VwsY4soanhgo6LNSm84E1LBcE8s3O0wpdiRzyR9z/ZZJMlMWv37qOOb9pdJlMUEKFQ==", - "dev": true, - "license": "MIT", - "engines": { - "node": ">=8" - } - }, - "node_modules/wrap-ansi-cjs/node_modules/ansi-styles": { - "version": "4.3.0", - "resolved": "https://registry.npmjs.org/ansi-styles/-/ansi-styles-4.3.0.tgz", - "integrity": "sha512-zbB9rCJAT1rbjiVDb2hqKFHNYLxgtk8NURxZ3IZwD3F6NtxbXZQCnnSi1Lkx+IDohdPlFp222wVALIheZJQSEg==", - "dev": true, - "license": "MIT", - "dependencies": { - "color-convert": "^2.0.1" - }, - "engines": { - "node": ">=8" - }, - "funding": { - "url": "https://github.com/chalk/ansi-styles?sponsor=1" - } - }, - "node_modules/wrap-ansi-cjs/node_modules/emoji-regex": { - "version": "8.0.0", - "resolved": "https://registry.npmjs.org/emoji-regex/-/emoji-regex-8.0.0.tgz", - "integrity": "sha512-MSjYzcWNOA0ewAHpz0MxpYFvwg6yjy1NG3xteoqz644VCo/RPgnr1/GGt+ic3iJTzQ8Eu3TdM14SawnVUmGE6A==", - "dev": true, - "license": "MIT" - }, - "node_modules/wrap-ansi-cjs/node_modules/string-width": { - "version": "4.2.3", - "resolved": "https://registry.npmjs.org/string-width/-/string-width-4.2.3.tgz", - "integrity": "sha512-wKyQRQpjJ0sIp62ErSZdGsjMJWsap5oRNihHhu6G7JVO/9jIB6UyevL+tXuOqrng8j/cxKTWyWUwvSTriiZz/g==", - "dev": true, - "license": "MIT", - "dependencies": { - "emoji-regex": "^8.0.0", - "is-fullwidth-code-point": "^3.0.0", - "strip-ansi": "^6.0.1" - }, - "engines": { - "node": ">=8" - } - }, - "node_modules/wrap-ansi-cjs/node_modules/strip-ansi": { - "version": "6.0.1", - "resolved": "https://registry.npmjs.org/strip-ansi/-/strip-ansi-6.0.1.tgz", - "integrity": "sha512-Y38VPSHcqkFrCpFnQ9vuSXmquuv5oXOKpGeT6aGrr3o3Gc9AlVa6JBfUSOCnbxGGZF+/0ooI7KrPuUSztUdU5A==", - "dev": true, - "license": "MIT", - "dependencies": { - "ansi-regex": "^5.0.1" - }, - "engines": { - "node": ">=8" - } - }, - "node_modules/wrap-ansi/node_modules/ansi-styles": { - "version": "6.2.1", - "resolved": "https://registry.npmjs.org/ansi-styles/-/ansi-styles-6.2.1.tgz", - "integrity": "sha512-bN798gFfQX+viw3R7yrGWRqnrN2oRkEkUjjl4JNn4E8GxxbjtG3FbrEIIY3l8/hrwUwIeCZvi4QuOTP4MErVug==", - "dev": true, - "license": "MIT", - "engines": { - "node": ">=12" - }, - "funding": { - "url": "https://github.com/chalk/ansi-styles?sponsor=1" - } - }, - "node_modules/wrappy": { - "version": "1.0.2", - "resolved": "https://registry.npmjs.org/wrappy/-/wrappy-1.0.2.tgz", - "integrity": "sha512-l4Sp/DRseor9wL6EvV2+TuQn63dMkPjZ/sp9XkghTEbV9KlPS1xUsZ3u7/IQO4wxtcFB4bgpQPRcR3QCvezPcQ==", - "dev": true, - "license": "ISC" - }, - "node_modules/write-file-atomic": { - "version": "5.0.1", - "resolved": "https://registry.npmjs.org/write-file-atomic/-/write-file-atomic-5.0.1.tgz", - "integrity": "sha512-+QU2zd6OTD8XWIJCbffaiQeH9U73qIqafo1x6V1snCWYGJf6cVE0cDR4D8xRzcEnfI21IFrUPzPGtcPf8AC+Rw==", - "dev": true, - "license": "ISC", - "dependencies": { - "imurmurhash": "^0.1.4", - "signal-exit": "^4.0.1" - }, - "engines": { - "node": "^14.17.0 || ^16.13.0 || >=18.0.0" - } - }, - "node_modules/y18n": { - "version": "5.0.8", - "resolved": "https://registry.npmjs.org/y18n/-/y18n-5.0.8.tgz", - "integrity": "sha512-0pfFzegeDWJHJIAmTLRP2DwHjdF5s7jo9tuztdQxAhINCdvS+3nGINqPd00AphqJR/0LhANUS6/+7SCb98YOfA==", - "dev": true, - "license": "ISC", - "engines": { - "node": ">=10" - } - }, - "node_modules/yallist": { - "version": "3.1.1", - "resolved": "https://registry.npmjs.org/yallist/-/yallist-3.1.1.tgz", - "integrity": "sha512-a4UGQaWPH59mOXUYnAG2ewncQS4i4F43Tv3JoAM+s2VDAmS9NsK8GpDMLrCHPksFT7h3K6TOoUNn2pb7RoXx4g==", - "dev": true, - "license": "ISC" - }, - "node_modules/yargs": { - "version": "17.7.2", - "resolved": "https://registry.npmjs.org/yargs/-/yargs-17.7.2.tgz", - "integrity": "sha512-7dSzzRQ++CKnNI/krKnYRV7JKKPUXMEh61soaHKg9mrWEhzFWhFnxPxGl+69cD1Ou63C13NUPCnmIcrvqCuM6w==", - "dev": true, - "license": "MIT", - "dependencies": { - "cliui": "^8.0.1", - "escalade": "^3.1.1", - "get-caller-file": "^2.0.5", - "require-directory": "^2.1.1", - "string-width": "^4.2.3", - "y18n": "^5.0.5", - "yargs-parser": "^21.1.1" - }, - "engines": { - "node": ">=12" - } - }, - "node_modules/yargs-parser": { - "version": "21.1.1", - "resolved": "https://registry.npmjs.org/yargs-parser/-/yargs-parser-21.1.1.tgz", - "integrity": "sha512-tVpsJW7DdjecAiFpbIB1e3qxIQsE6NoPc5/eTdrbbIC4h0LVsWhnoa3g+m2HclBIujHzsxZ4VJVA+GUuc2/LBw==", - "dev": true, - "license": "ISC", - "engines": { - "node": ">=12" - } - }, - "node_modules/yargs/node_modules/ansi-regex": { - "version": "5.0.1", - "resolved": "https://registry.npmjs.org/ansi-regex/-/ansi-regex-5.0.1.tgz", - "integrity": "sha512-quJQXlTSUGL2LH9SUXo8VwsY4soanhgo6LNSm84E1LBcE8s3O0wpdiRzyR9z/ZZJMlMWv37qOOb9pdJlMUEKFQ==", - "dev": true, - "license": "MIT", - "engines": { - "node": ">=8" - } - }, - "node_modules/yargs/node_modules/emoji-regex": { - "version": "8.0.0", - "resolved": "https://registry.npmjs.org/emoji-regex/-/emoji-regex-8.0.0.tgz", - "integrity": "sha512-MSjYzcWNOA0ewAHpz0MxpYFvwg6yjy1NG3xteoqz644VCo/RPgnr1/GGt+ic3iJTzQ8Eu3TdM14SawnVUmGE6A==", - "dev": true, - "license": "MIT" - }, - "node_modules/yargs/node_modules/string-width": { - "version": "4.2.3", - "resolved": "https://registry.npmjs.org/string-width/-/string-width-4.2.3.tgz", - "integrity": "sha512-wKyQRQpjJ0sIp62ErSZdGsjMJWsap5oRNihHhu6G7JVO/9jIB6UyevL+tXuOqrng8j/cxKTWyWUwvSTriiZz/g==", - "dev": true, - "license": "MIT", - "dependencies": { - "emoji-regex": "^8.0.0", - "is-fullwidth-code-point": "^3.0.0", - "strip-ansi": "^6.0.1" - }, - "engines": { - "node": ">=8" - } - }, - "node_modules/yargs/node_modules/strip-ansi": { - "version": "6.0.1", - "resolved": "https://registry.npmjs.org/strip-ansi/-/strip-ansi-6.0.1.tgz", - "integrity": "sha512-Y38VPSHcqkFrCpFnQ9vuSXmquuv5oXOKpGeT6aGrr3o3Gc9AlVa6JBfUSOCnbxGGZF+/0ooI7KrPuUSztUdU5A==", - "dev": true, - "license": "MIT", - "dependencies": { - "ansi-regex": "^5.0.1" - }, - "engines": { - "node": ">=8" - } - }, "node_modules/yocto-queue": { "version": "0.1.0", "resolved": "https://registry.npmjs.org/yocto-queue/-/yocto-queue-0.1.0.tgz", diff --git a/package.json b/package.json index b5963ec1..28edc709 100644 --- a/package.json +++ b/package.json @@ -25,14 +25,12 @@ "license": "MIT", "scripts": { "lint": "eslint .", - "jest": "jest", - "watch": "jest --watch", "build": "node build", - "test:unit": "npm run lint && npm run jest", - "test:e2e": "playwright test --config=tests/e2e/playwright.config.ts", - "test:e2e:ui": "playwright test --config=tests/e2e/playwright.config.ts --ui", - "test:e2e:debug": "playwright test --config=tests/e2e/playwright.config.ts --debug", - "test": "npm run test:unit && npm run test:e2e", + "test": "npm run lint && cd tests && npm test", + "test:unit": "cd tests && npm run test:unit", + "test:e2e": "cd tests && npm run test:e2e", + "test:e2e:ui": "cd tests && npm run test:e2e:ui", + "test:e2e:debug": "cd tests && npm run test:e2e:debug", "prepack": "npm run build", "checkGit": "sh ./uncommited.sh", "prepublishOnly": "npm run checkGit && npm run test && npm run build && npm run checkGit" @@ -41,20 +39,13 @@ "tslib": "^2.3.1" }, "devDependencies": { - "@babel/core": "^7.16.12", - "@babel/preset-env": "^7.28.0", - "@babel/preset-typescript": "^7.27.1", - "@playwright/test": "^1.56.1", "@rollup/plugin-node-resolve": "^16.0.1", "@rollup/plugin-terser": "^0.4.4", - "@types/jest": "^30.0.0", "@typescript-eslint/eslint-plugin": "^8.38.0", "@typescript-eslint/parser": "^8.38.0", - "babel-jest": "^30.0.5", "chalk": "^5.0.0", "eslint": "^9.32.0", "globals": "^16.3.0", - "jest": "^30.0.5", "rollup": "^4.45.1", "rollup-plugin-license": "^3.6.0", "rollup-plugin-sourcemaps2": "^0.5.3", diff --git a/babel.config.json b/tests/babel.config.json similarity index 100% rename from babel.config.json rename to tests/babel.config.json diff --git a/tests/e2e/fixture/Scroller.ts b/tests/e2e/fixture/Scroller.ts index a4af8a28..3a18fd26 100644 --- a/tests/e2e/fixture/Scroller.ts +++ b/tests/e2e/fixture/Scroller.ts @@ -1,5 +1,5 @@ import { Page } from '@playwright/test'; -import { Direction as DirectionEnum } from '../../src/inputs/common'; +import { Direction as DirectionEnum } from '../../../src/inputs/common'; /** * Direction enum (inline, no import from vscroll source) diff --git a/tests/e2e/fixture/VScrollFixture.ts b/tests/e2e/fixture/VScrollFixture.ts index 80b3f0f7..e84ee32f 100644 --- a/tests/e2e/fixture/VScrollFixture.ts +++ b/tests/e2e/fixture/VScrollFixture.ts @@ -1,6 +1,6 @@ import { Page } from '@playwright/test'; import * as path from 'path'; -import type { IDatasource, Item } from '../../src/index'; +import type { IDatasource, Item } from '../../../src/index'; import { Scroller, Direction, type DirectionType } from './Scroller.js'; import { Adapter } from './Adapter.js'; @@ -47,7 +47,7 @@ export class VScrollFixture { await page.goto('about:blank'); // 2. Load vscroll UMD bundle - const vscrollPath = path.join(process.cwd(), 'dist', 'bundles', 'vscroll.umd.js'); + const vscrollPath = path.join(process.cwd(), '..', 'dist', 'bundles', 'vscroll.umd.js'); await page.addScriptTag({ path: vscrollPath }); // 3. Add global styles FIRST (separate evaluate call to ensure styles are processed) diff --git a/tests/jest.config.js b/tests/jest.config.js new file mode 100644 index 00000000..9b3ac750 --- /dev/null +++ b/tests/jest.config.js @@ -0,0 +1,10 @@ +export default { + testMatch: ['**/unit/**/*.spec.ts'], + transform: { + '^.+\\.ts$': 'babel-jest' + }, + moduleFileExtensions: ['ts', 'js'], + collectCoverageFrom: ['../src/**/*.ts'], + coveragePathIgnorePatterns: ['/node_modules/'] +}; + diff --git a/tests/package-lock.json b/tests/package-lock.json new file mode 100644 index 00000000..1bc3eeac --- /dev/null +++ b/tests/package-lock.json @@ -0,0 +1,6047 @@ +{ + "name": "vscroll-tests", + "version": "1.7.0", + "lockfileVersion": 3, + "requires": true, + "packages": { + "": { + "name": "vscroll-tests", + "version": "1.7.0", + "devDependencies": { + "@babel/core": "^7.16.12", + "@babel/preset-env": "^7.28.0", + "@babel/preset-typescript": "^7.27.1", + "@playwright/test": "^1.56.1", + "@types/jest": "^30.0.0", + "babel-jest": "^30.0.5", + "jest": "^30.0.5", + "typescript": "^5.8.3" + } + }, + "node_modules/@babel/code-frame": { + "version": "7.27.1", + "resolved": "https://registry.npmjs.org/@babel/code-frame/-/code-frame-7.27.1.tgz", + "integrity": "sha512-cjQ7ZlQ0Mv3b47hABuTevyTuYN4i+loJKGeV9flcCgIK37cCXRh+L1bd3iBHlynerhQ7BhCkn2BPbQUL+rGqFg==", + "dev": true, + "license": "MIT", + "dependencies": { + "@babel/helper-validator-identifier": "^7.27.1", + "js-tokens": "^4.0.0", + "picocolors": "^1.1.1" + }, + "engines": { + "node": ">=6.9.0" + } + }, + "node_modules/@babel/compat-data": { + "version": "7.28.5", + "resolved": "https://registry.npmjs.org/@babel/compat-data/-/compat-data-7.28.5.tgz", + "integrity": "sha512-6uFXyCayocRbqhZOB+6XcuZbkMNimwfVGFji8CTZnCzOHVGvDqzvitu1re2AU5LROliz7eQPhB8CpAMvnx9EjA==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=6.9.0" + } + }, + "node_modules/@babel/core": { + "version": "7.28.5", + "resolved": "https://registry.npmjs.org/@babel/core/-/core-7.28.5.tgz", + "integrity": "sha512-e7jT4DxYvIDLk1ZHmU/m/mB19rex9sv0c2ftBtjSBv+kVM/902eh0fINUzD7UwLLNR+jU585GxUJ8/EBfAM5fw==", + "dev": true, + "license": "MIT", + "dependencies": { + "@babel/code-frame": "^7.27.1", + "@babel/generator": "^7.28.5", + "@babel/helper-compilation-targets": "^7.27.2", + "@babel/helper-module-transforms": "^7.28.3", + "@babel/helpers": "^7.28.4", + "@babel/parser": "^7.28.5", + "@babel/template": "^7.27.2", + "@babel/traverse": "^7.28.5", + "@babel/types": "^7.28.5", + "@jridgewell/remapping": "^2.3.5", + "convert-source-map": "^2.0.0", + "debug": "^4.1.0", + "gensync": "^1.0.0-beta.2", + "json5": "^2.2.3", + "semver": "^6.3.1" + }, + "engines": { + "node": ">=6.9.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/babel" + } + }, + "node_modules/@babel/generator": { + "version": "7.28.5", + "resolved": "https://registry.npmjs.org/@babel/generator/-/generator-7.28.5.tgz", + "integrity": "sha512-3EwLFhZ38J4VyIP6WNtt2kUdW9dokXA9Cr4IVIFHuCpZ3H8/YFOl5JjZHisrn1fATPBmKKqXzDFvh9fUwHz6CQ==", + "dev": true, + "license": "MIT", + "dependencies": { + "@babel/parser": "^7.28.5", + "@babel/types": "^7.28.5", + "@jridgewell/gen-mapping": "^0.3.12", + "@jridgewell/trace-mapping": "^0.3.28", + "jsesc": "^3.0.2" + }, + "engines": { + "node": ">=6.9.0" + } + }, + "node_modules/@babel/helper-annotate-as-pure": { + "version": "7.27.3", + "resolved": "https://registry.npmjs.org/@babel/helper-annotate-as-pure/-/helper-annotate-as-pure-7.27.3.tgz", + "integrity": "sha512-fXSwMQqitTGeHLBC08Eq5yXz2m37E4pJX1qAU1+2cNedz/ifv/bVXft90VeSav5nFO61EcNgwr0aJxbyPaWBPg==", + "dev": true, + "license": "MIT", + "dependencies": { + "@babel/types": "^7.27.3" + }, + "engines": { + "node": ">=6.9.0" + } + }, + "node_modules/@babel/helper-compilation-targets": { + "version": "7.27.2", + "resolved": "https://registry.npmjs.org/@babel/helper-compilation-targets/-/helper-compilation-targets-7.27.2.tgz", + "integrity": "sha512-2+1thGUUWWjLTYTHZWK1n8Yga0ijBz1XAhUXcKy81rd5g6yh7hGqMp45v7cadSbEHc9G3OTv45SyneRN3ps4DQ==", + "dev": true, + "license": "MIT", + "dependencies": { + "@babel/compat-data": "^7.27.2", + "@babel/helper-validator-option": "^7.27.1", + "browserslist": "^4.24.0", + "lru-cache": "^5.1.1", + "semver": "^6.3.1" + }, + "engines": { + "node": ">=6.9.0" + } + }, + "node_modules/@babel/helper-create-class-features-plugin": { + "version": "7.28.5", + "resolved": "https://registry.npmjs.org/@babel/helper-create-class-features-plugin/-/helper-create-class-features-plugin-7.28.5.tgz", + "integrity": "sha512-q3WC4JfdODypvxArsJQROfupPBq9+lMwjKq7C33GhbFYJsufD0yd/ziwD+hJucLeWsnFPWZjsU2DNFqBPE7jwQ==", + "dev": true, + "license": "MIT", + "dependencies": { + "@babel/helper-annotate-as-pure": "^7.27.3", + "@babel/helper-member-expression-to-functions": "^7.28.5", + "@babel/helper-optimise-call-expression": "^7.27.1", + "@babel/helper-replace-supers": "^7.27.1", + "@babel/helper-skip-transparent-expression-wrappers": "^7.27.1", + "@babel/traverse": "^7.28.5", + "semver": "^6.3.1" + }, + "engines": { + "node": ">=6.9.0" + }, + "peerDependencies": { + "@babel/core": "^7.0.0" + } + }, + "node_modules/@babel/helper-create-regexp-features-plugin": { + "version": "7.28.5", + "resolved": "https://registry.npmjs.org/@babel/helper-create-regexp-features-plugin/-/helper-create-regexp-features-plugin-7.28.5.tgz", + "integrity": "sha512-N1EhvLtHzOvj7QQOUCCS3NrPJP8c5W6ZXCHDn7Yialuy1iu4r5EmIYkXlKNqT99Ciw+W0mDqWoR6HWMZlFP3hw==", + "dev": true, + "license": "MIT", + "dependencies": { + "@babel/helper-annotate-as-pure": "^7.27.3", + "regexpu-core": "^6.3.1", + "semver": "^6.3.1" + }, + "engines": { + "node": ">=6.9.0" + }, + "peerDependencies": { + "@babel/core": "^7.0.0" + } + }, + "node_modules/@babel/helper-define-polyfill-provider": { + "version": "0.6.5", + "resolved": "https://registry.npmjs.org/@babel/helper-define-polyfill-provider/-/helper-define-polyfill-provider-0.6.5.tgz", + "integrity": "sha512-uJnGFcPsWQK8fvjgGP5LZUZZsYGIoPeRjSF5PGwrelYgq7Q15/Ft9NGFp1zglwgIv//W0uG4BevRuSJRyylZPg==", + "dev": true, + "license": "MIT", + "dependencies": { + "@babel/helper-compilation-targets": "^7.27.2", + "@babel/helper-plugin-utils": "^7.27.1", + "debug": "^4.4.1", + "lodash.debounce": "^4.0.8", + "resolve": "^1.22.10" + }, + "peerDependencies": { + "@babel/core": "^7.4.0 || ^8.0.0-0 <8.0.0" + } + }, + "node_modules/@babel/helper-globals": { + "version": "7.28.0", + "resolved": "https://registry.npmjs.org/@babel/helper-globals/-/helper-globals-7.28.0.tgz", + "integrity": "sha512-+W6cISkXFa1jXsDEdYA8HeevQT/FULhxzR99pxphltZcVaugps53THCeiWA8SguxxpSp3gKPiuYfSWopkLQ4hw==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=6.9.0" + } + }, + "node_modules/@babel/helper-member-expression-to-functions": { + "version": "7.28.5", + "resolved": "https://registry.npmjs.org/@babel/helper-member-expression-to-functions/-/helper-member-expression-to-functions-7.28.5.tgz", + "integrity": "sha512-cwM7SBRZcPCLgl8a7cY0soT1SptSzAlMH39vwiRpOQkJlh53r5hdHwLSCZpQdVLT39sZt+CRpNwYG4Y2v77atg==", + "dev": true, + "license": "MIT", + "dependencies": { + "@babel/traverse": "^7.28.5", + "@babel/types": "^7.28.5" + }, + "engines": { + "node": ">=6.9.0" + } + }, + "node_modules/@babel/helper-module-imports": { + "version": "7.27.1", + "resolved": "https://registry.npmjs.org/@babel/helper-module-imports/-/helper-module-imports-7.27.1.tgz", + "integrity": "sha512-0gSFWUPNXNopqtIPQvlD5WgXYI5GY2kP2cCvoT8kczjbfcfuIljTbcWrulD1CIPIX2gt1wghbDy08yE1p+/r3w==", + "dev": true, + "license": "MIT", + "dependencies": { + "@babel/traverse": "^7.27.1", + "@babel/types": "^7.27.1" + }, + "engines": { + "node": ">=6.9.0" + } + }, + "node_modules/@babel/helper-module-transforms": { + "version": "7.28.3", + "resolved": "https://registry.npmjs.org/@babel/helper-module-transforms/-/helper-module-transforms-7.28.3.tgz", + "integrity": "sha512-gytXUbs8k2sXS9PnQptz5o0QnpLL51SwASIORY6XaBKF88nsOT0Zw9szLqlSGQDP/4TljBAD5y98p2U1fqkdsw==", + "dev": true, + "license": "MIT", + "dependencies": { + "@babel/helper-module-imports": "^7.27.1", + "@babel/helper-validator-identifier": "^7.27.1", + "@babel/traverse": "^7.28.3" + }, + "engines": { + "node": ">=6.9.0" + }, + "peerDependencies": { + "@babel/core": "^7.0.0" + } + }, + "node_modules/@babel/helper-optimise-call-expression": { + "version": "7.27.1", + "resolved": "https://registry.npmjs.org/@babel/helper-optimise-call-expression/-/helper-optimise-call-expression-7.27.1.tgz", + "integrity": "sha512-URMGH08NzYFhubNSGJrpUEphGKQwMQYBySzat5cAByY1/YgIRkULnIy3tAMeszlL/so2HbeilYloUmSpd7GdVw==", + "dev": true, + "license": "MIT", + "dependencies": { + "@babel/types": "^7.27.1" + }, + "engines": { + "node": ">=6.9.0" + } + }, + "node_modules/@babel/helper-plugin-utils": { + "version": "7.27.1", + "resolved": "https://registry.npmjs.org/@babel/helper-plugin-utils/-/helper-plugin-utils-7.27.1.tgz", + "integrity": "sha512-1gn1Up5YXka3YYAHGKpbideQ5Yjf1tDa9qYcgysz+cNCXukyLl6DjPXhD3VRwSb8c0J9tA4b2+rHEZtc6R0tlw==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=6.9.0" + } + }, + "node_modules/@babel/helper-remap-async-to-generator": { + "version": "7.27.1", + "resolved": "https://registry.npmjs.org/@babel/helper-remap-async-to-generator/-/helper-remap-async-to-generator-7.27.1.tgz", + "integrity": "sha512-7fiA521aVw8lSPeI4ZOD3vRFkoqkJcS+z4hFo82bFSH/2tNd6eJ5qCVMS5OzDmZh/kaHQeBaeyxK6wljcPtveA==", + "dev": true, + "license": "MIT", + "dependencies": { + "@babel/helper-annotate-as-pure": "^7.27.1", + "@babel/helper-wrap-function": "^7.27.1", + "@babel/traverse": "^7.27.1" + }, + "engines": { + "node": ">=6.9.0" + }, + "peerDependencies": { + "@babel/core": "^7.0.0" + } + }, + "node_modules/@babel/helper-replace-supers": { + "version": "7.27.1", + "resolved": "https://registry.npmjs.org/@babel/helper-replace-supers/-/helper-replace-supers-7.27.1.tgz", + "integrity": "sha512-7EHz6qDZc8RYS5ElPoShMheWvEgERonFCs7IAonWLLUTXW59DP14bCZt89/GKyreYn8g3S83m21FelHKbeDCKA==", + "dev": true, + "license": "MIT", + "dependencies": { + "@babel/helper-member-expression-to-functions": "^7.27.1", + "@babel/helper-optimise-call-expression": "^7.27.1", + "@babel/traverse": "^7.27.1" + }, + "engines": { + "node": ">=6.9.0" + }, + "peerDependencies": { + "@babel/core": "^7.0.0" + } + }, + "node_modules/@babel/helper-skip-transparent-expression-wrappers": { + "version": "7.27.1", + "resolved": "https://registry.npmjs.org/@babel/helper-skip-transparent-expression-wrappers/-/helper-skip-transparent-expression-wrappers-7.27.1.tgz", + "integrity": "sha512-Tub4ZKEXqbPjXgWLl2+3JpQAYBJ8+ikpQ2Ocj/q/r0LwE3UhENh7EUabyHjz2kCEsrRY83ew2DQdHluuiDQFzg==", + "dev": true, + "license": "MIT", + "dependencies": { + "@babel/traverse": "^7.27.1", + "@babel/types": "^7.27.1" + }, + "engines": { + "node": ">=6.9.0" + } + }, + "node_modules/@babel/helper-string-parser": { + "version": "7.27.1", + "resolved": "https://registry.npmjs.org/@babel/helper-string-parser/-/helper-string-parser-7.27.1.tgz", + "integrity": "sha512-qMlSxKbpRlAridDExk92nSobyDdpPijUq2DW6oDnUqd0iOGxmQjyqhMIihI9+zv4LPyZdRje2cavWPbCbWm3eA==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=6.9.0" + } + }, + "node_modules/@babel/helper-validator-identifier": { + "version": "7.28.5", + "resolved": "https://registry.npmjs.org/@babel/helper-validator-identifier/-/helper-validator-identifier-7.28.5.tgz", + "integrity": "sha512-qSs4ifwzKJSV39ucNjsvc6WVHs6b7S03sOh2OcHF9UHfVPqWWALUsNUVzhSBiItjRZoLHx7nIarVjqKVusUZ1Q==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=6.9.0" + } + }, + "node_modules/@babel/helper-validator-option": { + "version": "7.27.1", + "resolved": "https://registry.npmjs.org/@babel/helper-validator-option/-/helper-validator-option-7.27.1.tgz", + "integrity": "sha512-YvjJow9FxbhFFKDSuFnVCe2WxXk1zWc22fFePVNEaWJEu8IrZVlda6N0uHwzZrUM1il7NC9Mlp4MaJYbYd9JSg==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=6.9.0" + } + }, + "node_modules/@babel/helper-wrap-function": { + "version": "7.28.3", + "resolved": "https://registry.npmjs.org/@babel/helper-wrap-function/-/helper-wrap-function-7.28.3.tgz", + "integrity": "sha512-zdf983tNfLZFletc0RRXYrHrucBEg95NIFMkn6K9dbeMYnsgHaSBGcQqdsCSStG2PYwRre0Qc2NNSCXbG+xc6g==", + "dev": true, + "license": "MIT", + "dependencies": { + "@babel/template": "^7.27.2", + "@babel/traverse": "^7.28.3", + "@babel/types": "^7.28.2" + }, + "engines": { + "node": ">=6.9.0" + } + }, + "node_modules/@babel/helpers": { + "version": "7.28.4", + "resolved": "https://registry.npmjs.org/@babel/helpers/-/helpers-7.28.4.tgz", + "integrity": "sha512-HFN59MmQXGHVyYadKLVumYsA9dBFun/ldYxipEjzA4196jpLZd8UjEEBLkbEkvfYreDqJhZxYAWFPtrfhNpj4w==", + "dev": true, + "license": "MIT", + "dependencies": { + "@babel/template": "^7.27.2", + "@babel/types": "^7.28.4" + }, + "engines": { + "node": ">=6.9.0" + } + }, + "node_modules/@babel/parser": { + "version": "7.28.5", + "resolved": "https://registry.npmjs.org/@babel/parser/-/parser-7.28.5.tgz", + "integrity": "sha512-KKBU1VGYR7ORr3At5HAtUQ+TV3SzRCXmA/8OdDZiLDBIZxVyzXuztPjfLd3BV1PRAQGCMWWSHYhL0F8d5uHBDQ==", + "dev": true, + "license": "MIT", + "dependencies": { + "@babel/types": "^7.28.5" + }, + "bin": { + "parser": "bin/babel-parser.js" + }, + "engines": { + "node": ">=6.0.0" + } + }, + "node_modules/@babel/plugin-bugfix-firefox-class-in-computed-class-key": { + "version": "7.28.5", + "resolved": "https://registry.npmjs.org/@babel/plugin-bugfix-firefox-class-in-computed-class-key/-/plugin-bugfix-firefox-class-in-computed-class-key-7.28.5.tgz", + "integrity": "sha512-87GDMS3tsmMSi/3bWOte1UblL+YUTFMV8SZPZ2eSEL17s74Cw/l63rR6NmGVKMYW2GYi85nE+/d6Hw5N0bEk2Q==", + "dev": true, + "license": "MIT", + "dependencies": { + "@babel/helper-plugin-utils": "^7.27.1", + "@babel/traverse": "^7.28.5" + }, + "engines": { + "node": ">=6.9.0" + }, + "peerDependencies": { + "@babel/core": "^7.0.0" + } + }, + "node_modules/@babel/plugin-bugfix-safari-class-field-initializer-scope": { + "version": "7.27.1", + "resolved": "https://registry.npmjs.org/@babel/plugin-bugfix-safari-class-field-initializer-scope/-/plugin-bugfix-safari-class-field-initializer-scope-7.27.1.tgz", + "integrity": "sha512-qNeq3bCKnGgLkEXUuFry6dPlGfCdQNZbn7yUAPCInwAJHMU7THJfrBSozkcWq5sNM6RcF3S8XyQL2A52KNR9IA==", + "dev": true, + "license": "MIT", + "dependencies": { + "@babel/helper-plugin-utils": "^7.27.1" + }, + "engines": { + "node": ">=6.9.0" + }, + "peerDependencies": { + "@babel/core": "^7.0.0" + } + }, + "node_modules/@babel/plugin-bugfix-safari-id-destructuring-collision-in-function-expression": { + "version": "7.27.1", + "resolved": "https://registry.npmjs.org/@babel/plugin-bugfix-safari-id-destructuring-collision-in-function-expression/-/plugin-bugfix-safari-id-destructuring-collision-in-function-expression-7.27.1.tgz", + "integrity": "sha512-g4L7OYun04N1WyqMNjldFwlfPCLVkgB54A/YCXICZYBsvJJE3kByKv9c9+R/nAfmIfjl2rKYLNyMHboYbZaWaA==", + "dev": true, + "license": "MIT", + "dependencies": { + "@babel/helper-plugin-utils": "^7.27.1" + }, + "engines": { + "node": ">=6.9.0" + }, + "peerDependencies": { + "@babel/core": "^7.0.0" + } + }, + "node_modules/@babel/plugin-bugfix-v8-spread-parameters-in-optional-chaining": { + "version": "7.27.1", + "resolved": "https://registry.npmjs.org/@babel/plugin-bugfix-v8-spread-parameters-in-optional-chaining/-/plugin-bugfix-v8-spread-parameters-in-optional-chaining-7.27.1.tgz", + "integrity": "sha512-oO02gcONcD5O1iTLi/6frMJBIwWEHceWGSGqrpCmEL8nogiS6J9PBlE48CaK20/Jx1LuRml9aDftLgdjXT8+Cw==", + "dev": true, + "license": "MIT", + "dependencies": { + "@babel/helper-plugin-utils": "^7.27.1", + "@babel/helper-skip-transparent-expression-wrappers": "^7.27.1", + "@babel/plugin-transform-optional-chaining": "^7.27.1" + }, + "engines": { + "node": ">=6.9.0" + }, + "peerDependencies": { + "@babel/core": "^7.13.0" + } + }, + "node_modules/@babel/plugin-bugfix-v8-static-class-fields-redefine-readonly": { + "version": "7.28.3", + "resolved": "https://registry.npmjs.org/@babel/plugin-bugfix-v8-static-class-fields-redefine-readonly/-/plugin-bugfix-v8-static-class-fields-redefine-readonly-7.28.3.tgz", + "integrity": "sha512-b6YTX108evsvE4YgWyQ921ZAFFQm3Bn+CA3+ZXlNVnPhx+UfsVURoPjfGAPCjBgrqo30yX/C2nZGX96DxvR9Iw==", + "dev": true, + "license": "MIT", + "dependencies": { + "@babel/helper-plugin-utils": "^7.27.1", + "@babel/traverse": "^7.28.3" + }, + "engines": { + "node": ">=6.9.0" + }, + "peerDependencies": { + "@babel/core": "^7.0.0" + } + }, + "node_modules/@babel/plugin-proposal-private-property-in-object": { + "version": "7.21.0-placeholder-for-preset-env.2", + "resolved": "https://registry.npmjs.org/@babel/plugin-proposal-private-property-in-object/-/plugin-proposal-private-property-in-object-7.21.0-placeholder-for-preset-env.2.tgz", + "integrity": "sha512-SOSkfJDddaM7mak6cPEpswyTRnuRltl429hMraQEglW+OkovnCzsiszTmsrlY//qLFjCpQDFRvjdm2wA5pPm9w==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=6.9.0" + }, + "peerDependencies": { + "@babel/core": "^7.0.0-0" + } + }, + "node_modules/@babel/plugin-syntax-async-generators": { + "version": "7.8.4", + "resolved": "https://registry.npmjs.org/@babel/plugin-syntax-async-generators/-/plugin-syntax-async-generators-7.8.4.tgz", + "integrity": "sha512-tycmZxkGfZaxhMRbXlPXuVFpdWlXpir2W4AMhSJgRKzk/eDlIXOhb2LHWoLpDF7TEHylV5zNhykX6KAgHJmTNw==", + "dev": true, + "license": "MIT", + "dependencies": { + "@babel/helper-plugin-utils": "^7.8.0" + }, + "peerDependencies": { + "@babel/core": "^7.0.0-0" + } + }, + "node_modules/@babel/plugin-syntax-bigint": { + "version": "7.8.3", + "resolved": "https://registry.npmjs.org/@babel/plugin-syntax-bigint/-/plugin-syntax-bigint-7.8.3.tgz", + "integrity": "sha512-wnTnFlG+YxQm3vDxpGE57Pj0srRU4sHE/mDkt1qv2YJJSeUAec2ma4WLUnUPeKjyrfntVwe/N6dCXpU+zL3Npg==", + "dev": true, + "license": "MIT", + "dependencies": { + "@babel/helper-plugin-utils": "^7.8.0" + }, + "peerDependencies": { + "@babel/core": "^7.0.0-0" + } + }, + "node_modules/@babel/plugin-syntax-class-properties": { + "version": "7.12.13", + "resolved": "https://registry.npmjs.org/@babel/plugin-syntax-class-properties/-/plugin-syntax-class-properties-7.12.13.tgz", + "integrity": "sha512-fm4idjKla0YahUNgFNLCB0qySdsoPiZP3iQE3rky0mBUtMZ23yDJ9SJdg6dXTSDnulOVqiF3Hgr9nbXvXTQZYA==", + "dev": true, + "license": "MIT", + "dependencies": { + "@babel/helper-plugin-utils": "^7.12.13" + }, + "peerDependencies": { + "@babel/core": "^7.0.0-0" + } + }, + "node_modules/@babel/plugin-syntax-class-static-block": { + "version": "7.14.5", + "resolved": "https://registry.npmjs.org/@babel/plugin-syntax-class-static-block/-/plugin-syntax-class-static-block-7.14.5.tgz", + "integrity": "sha512-b+YyPmr6ldyNnM6sqYeMWE+bgJcJpO6yS4QD7ymxgH34GBPNDM/THBh8iunyvKIZztiwLH4CJZ0RxTk9emgpjw==", + "dev": true, + "license": "MIT", + "dependencies": { + "@babel/helper-plugin-utils": "^7.14.5" + }, + "engines": { + "node": ">=6.9.0" + }, + "peerDependencies": { + "@babel/core": "^7.0.0-0" + } + }, + "node_modules/@babel/plugin-syntax-import-assertions": { + "version": "7.27.1", + "resolved": "https://registry.npmjs.org/@babel/plugin-syntax-import-assertions/-/plugin-syntax-import-assertions-7.27.1.tgz", + "integrity": "sha512-UT/Jrhw57xg4ILHLFnzFpPDlMbcdEicaAtjPQpbj9wa8T4r5KVWCimHcL/460g8Ht0DMxDyjsLgiWSkVjnwPFg==", + "dev": true, + "license": "MIT", + "dependencies": { + "@babel/helper-plugin-utils": "^7.27.1" + }, + "engines": { + "node": ">=6.9.0" + }, + "peerDependencies": { + "@babel/core": "^7.0.0-0" + } + }, + "node_modules/@babel/plugin-syntax-import-attributes": { + "version": "7.27.1", + "resolved": "https://registry.npmjs.org/@babel/plugin-syntax-import-attributes/-/plugin-syntax-import-attributes-7.27.1.tgz", + "integrity": "sha512-oFT0FrKHgF53f4vOsZGi2Hh3I35PfSmVs4IBFLFj4dnafP+hIWDLg3VyKmUHfLoLHlyxY4C7DGtmHuJgn+IGww==", + "dev": true, + "license": "MIT", + "dependencies": { + "@babel/helper-plugin-utils": "^7.27.1" + }, + "engines": { + "node": ">=6.9.0" + }, + "peerDependencies": { + "@babel/core": "^7.0.0-0" + } + }, + "node_modules/@babel/plugin-syntax-import-meta": { + "version": "7.10.4", + "resolved": "https://registry.npmjs.org/@babel/plugin-syntax-import-meta/-/plugin-syntax-import-meta-7.10.4.tgz", + "integrity": "sha512-Yqfm+XDx0+Prh3VSeEQCPU81yC+JWZ2pDPFSS4ZdpfZhp4MkFMaDC1UqseovEKwSUpnIL7+vK+Clp7bfh0iD7g==", + "dev": true, + "license": "MIT", + "dependencies": { + "@babel/helper-plugin-utils": "^7.10.4" + }, + "peerDependencies": { + "@babel/core": "^7.0.0-0" + } + }, + "node_modules/@babel/plugin-syntax-json-strings": { + "version": "7.8.3", + "resolved": "https://registry.npmjs.org/@babel/plugin-syntax-json-strings/-/plugin-syntax-json-strings-7.8.3.tgz", + "integrity": "sha512-lY6kdGpWHvjoe2vk4WrAapEuBR69EMxZl+RoGRhrFGNYVK8mOPAW8VfbT/ZgrFbXlDNiiaxQnAtgVCZ6jv30EA==", + "dev": true, + "license": "MIT", + "dependencies": { + "@babel/helper-plugin-utils": "^7.8.0" + }, + "peerDependencies": { + "@babel/core": "^7.0.0-0" + } + }, + "node_modules/@babel/plugin-syntax-jsx": { + "version": "7.27.1", + "resolved": "https://registry.npmjs.org/@babel/plugin-syntax-jsx/-/plugin-syntax-jsx-7.27.1.tgz", + "integrity": "sha512-y8YTNIeKoyhGd9O0Jiyzyyqk8gdjnumGTQPsz0xOZOQ2RmkVJeZ1vmmfIvFEKqucBG6axJGBZDE/7iI5suUI/w==", + "dev": true, + "license": "MIT", + "dependencies": { + "@babel/helper-plugin-utils": "^7.27.1" + }, + "engines": { + "node": ">=6.9.0" + }, + "peerDependencies": { + "@babel/core": "^7.0.0-0" + } + }, + "node_modules/@babel/plugin-syntax-logical-assignment-operators": { + "version": "7.10.4", + "resolved": "https://registry.npmjs.org/@babel/plugin-syntax-logical-assignment-operators/-/plugin-syntax-logical-assignment-operators-7.10.4.tgz", + "integrity": "sha512-d8waShlpFDinQ5MtvGU9xDAOzKH47+FFoney2baFIoMr952hKOLp1HR7VszoZvOsV/4+RRszNY7D17ba0te0ig==", + "dev": true, + "license": "MIT", + "dependencies": { + "@babel/helper-plugin-utils": "^7.10.4" + }, + "peerDependencies": { + "@babel/core": "^7.0.0-0" + } + }, + "node_modules/@babel/plugin-syntax-nullish-coalescing-operator": { + "version": "7.8.3", + "resolved": "https://registry.npmjs.org/@babel/plugin-syntax-nullish-coalescing-operator/-/plugin-syntax-nullish-coalescing-operator-7.8.3.tgz", + "integrity": "sha512-aSff4zPII1u2QD7y+F8oDsz19ew4IGEJg9SVW+bqwpwtfFleiQDMdzA/R+UlWDzfnHFCxxleFT0PMIrR36XLNQ==", + "dev": true, + "license": "MIT", + "dependencies": { + "@babel/helper-plugin-utils": "^7.8.0" + }, + "peerDependencies": { + "@babel/core": "^7.0.0-0" + } + }, + "node_modules/@babel/plugin-syntax-numeric-separator": { + "version": "7.10.4", + "resolved": "https://registry.npmjs.org/@babel/plugin-syntax-numeric-separator/-/plugin-syntax-numeric-separator-7.10.4.tgz", + "integrity": "sha512-9H6YdfkcK/uOnY/K7/aA2xpzaAgkQn37yzWUMRK7OaPOqOpGS1+n0H5hxT9AUw9EsSjPW8SVyMJwYRtWs3X3ug==", + "dev": true, + "license": "MIT", + "dependencies": { + "@babel/helper-plugin-utils": "^7.10.4" + }, + "peerDependencies": { + "@babel/core": "^7.0.0-0" + } + }, + "node_modules/@babel/plugin-syntax-object-rest-spread": { + "version": "7.8.3", + "resolved": "https://registry.npmjs.org/@babel/plugin-syntax-object-rest-spread/-/plugin-syntax-object-rest-spread-7.8.3.tgz", + "integrity": "sha512-XoqMijGZb9y3y2XskN+P1wUGiVwWZ5JmoDRwx5+3GmEplNyVM2s2Dg8ILFQm8rWM48orGy5YpI5Bl8U1y7ydlA==", + "dev": true, + "license": "MIT", + "dependencies": { + "@babel/helper-plugin-utils": "^7.8.0" + }, + "peerDependencies": { + "@babel/core": "^7.0.0-0" + } + }, + "node_modules/@babel/plugin-syntax-optional-catch-binding": { + "version": "7.8.3", + "resolved": "https://registry.npmjs.org/@babel/plugin-syntax-optional-catch-binding/-/plugin-syntax-optional-catch-binding-7.8.3.tgz", + "integrity": "sha512-6VPD0Pc1lpTqw0aKoeRTMiB+kWhAoT24PA+ksWSBrFtl5SIRVpZlwN3NNPQjehA2E/91FV3RjLWoVTglWcSV3Q==", + "dev": true, + "license": "MIT", + "dependencies": { + "@babel/helper-plugin-utils": "^7.8.0" + }, + "peerDependencies": { + "@babel/core": "^7.0.0-0" + } + }, + "node_modules/@babel/plugin-syntax-optional-chaining": { + "version": "7.8.3", + "resolved": "https://registry.npmjs.org/@babel/plugin-syntax-optional-chaining/-/plugin-syntax-optional-chaining-7.8.3.tgz", + "integrity": "sha512-KoK9ErH1MBlCPxV0VANkXW2/dw4vlbGDrFgz8bmUsBGYkFRcbRwMh6cIJubdPrkxRwuGdtCk0v/wPTKbQgBjkg==", + "dev": true, + "license": "MIT", + "dependencies": { + "@babel/helper-plugin-utils": "^7.8.0" + }, + "peerDependencies": { + "@babel/core": "^7.0.0-0" + } + }, + "node_modules/@babel/plugin-syntax-private-property-in-object": { + "version": "7.14.5", + "resolved": "https://registry.npmjs.org/@babel/plugin-syntax-private-property-in-object/-/plugin-syntax-private-property-in-object-7.14.5.tgz", + "integrity": "sha512-0wVnp9dxJ72ZUJDV27ZfbSj6iHLoytYZmh3rFcxNnvsJF3ktkzLDZPy/mA17HGsaQT3/DQsWYX1f1QGWkCoVUg==", + "dev": true, + "license": "MIT", + "dependencies": { + "@babel/helper-plugin-utils": "^7.14.5" + }, + "engines": { + "node": ">=6.9.0" + }, + "peerDependencies": { + "@babel/core": "^7.0.0-0" + } + }, + "node_modules/@babel/plugin-syntax-top-level-await": { + "version": "7.14.5", + "resolved": "https://registry.npmjs.org/@babel/plugin-syntax-top-level-await/-/plugin-syntax-top-level-await-7.14.5.tgz", + "integrity": "sha512-hx++upLv5U1rgYfwe1xBQUhRmU41NEvpUvrp8jkrSCdvGSnM5/qdRMtylJ6PG5OFkBaHkbTAKTnd3/YyESRHFw==", + "dev": true, + "license": "MIT", + "dependencies": { + "@babel/helper-plugin-utils": "^7.14.5" + }, + "engines": { + "node": ">=6.9.0" + }, + "peerDependencies": { + "@babel/core": "^7.0.0-0" + } + }, + "node_modules/@babel/plugin-syntax-typescript": { + "version": "7.27.1", + "resolved": "https://registry.npmjs.org/@babel/plugin-syntax-typescript/-/plugin-syntax-typescript-7.27.1.tgz", + "integrity": "sha512-xfYCBMxveHrRMnAWl1ZlPXOZjzkN82THFvLhQhFXFt81Z5HnN+EtUkZhv/zcKpmT3fzmWZB0ywiBrbC3vogbwQ==", + "dev": true, + "license": "MIT", + "dependencies": { + "@babel/helper-plugin-utils": "^7.27.1" + }, + "engines": { + "node": ">=6.9.0" + }, + "peerDependencies": { + "@babel/core": "^7.0.0-0" + } + }, + "node_modules/@babel/plugin-syntax-unicode-sets-regex": { + "version": "7.18.6", + "resolved": "https://registry.npmjs.org/@babel/plugin-syntax-unicode-sets-regex/-/plugin-syntax-unicode-sets-regex-7.18.6.tgz", + "integrity": "sha512-727YkEAPwSIQTv5im8QHz3upqp92JTWhidIC81Tdx4VJYIte/VndKf1qKrfnnhPLiPghStWfvC/iFaMCQu7Nqg==", + "dev": true, + "license": "MIT", + "dependencies": { + "@babel/helper-create-regexp-features-plugin": "^7.18.6", + "@babel/helper-plugin-utils": "^7.18.6" + }, + "engines": { + "node": ">=6.9.0" + }, + "peerDependencies": { + "@babel/core": "^7.0.0" + } + }, + "node_modules/@babel/plugin-transform-arrow-functions": { + "version": "7.27.1", + "resolved": "https://registry.npmjs.org/@babel/plugin-transform-arrow-functions/-/plugin-transform-arrow-functions-7.27.1.tgz", + "integrity": "sha512-8Z4TGic6xW70FKThA5HYEKKyBpOOsucTOD1DjU3fZxDg+K3zBJcXMFnt/4yQiZnf5+MiOMSXQ9PaEK/Ilh1DeA==", + "dev": true, + "license": "MIT", + "dependencies": { + "@babel/helper-plugin-utils": "^7.27.1" + }, + "engines": { + "node": ">=6.9.0" + }, + "peerDependencies": { + "@babel/core": "^7.0.0-0" + } + }, + "node_modules/@babel/plugin-transform-async-generator-functions": { + "version": "7.28.0", + "resolved": "https://registry.npmjs.org/@babel/plugin-transform-async-generator-functions/-/plugin-transform-async-generator-functions-7.28.0.tgz", + "integrity": "sha512-BEOdvX4+M765icNPZeidyADIvQ1m1gmunXufXxvRESy/jNNyfovIqUyE7MVgGBjWktCoJlzvFA1To2O4ymIO3Q==", + "dev": true, + "license": "MIT", + "dependencies": { + "@babel/helper-plugin-utils": "^7.27.1", + "@babel/helper-remap-async-to-generator": "^7.27.1", + "@babel/traverse": "^7.28.0" + }, + "engines": { + "node": ">=6.9.0" + }, + "peerDependencies": { + "@babel/core": "^7.0.0-0" + } + }, + "node_modules/@babel/plugin-transform-async-to-generator": { + "version": "7.27.1", + "resolved": "https://registry.npmjs.org/@babel/plugin-transform-async-to-generator/-/plugin-transform-async-to-generator-7.27.1.tgz", + "integrity": "sha512-NREkZsZVJS4xmTr8qzE5y8AfIPqsdQfRuUiLRTEzb7Qii8iFWCyDKaUV2c0rCuh4ljDZ98ALHP/PetiBV2nddA==", + "dev": true, + "license": "MIT", + "dependencies": { + "@babel/helper-module-imports": "^7.27.1", + "@babel/helper-plugin-utils": "^7.27.1", + "@babel/helper-remap-async-to-generator": "^7.27.1" + }, + "engines": { + "node": ">=6.9.0" + }, + "peerDependencies": { + "@babel/core": "^7.0.0-0" + } + }, + "node_modules/@babel/plugin-transform-block-scoped-functions": { + "version": "7.27.1", + "resolved": "https://registry.npmjs.org/@babel/plugin-transform-block-scoped-functions/-/plugin-transform-block-scoped-functions-7.27.1.tgz", + "integrity": "sha512-cnqkuOtZLapWYZUYM5rVIdv1nXYuFVIltZ6ZJ7nIj585QsjKM5dhL2Fu/lICXZ1OyIAFc7Qy+bvDAtTXqGrlhg==", + "dev": true, + "license": "MIT", + "dependencies": { + "@babel/helper-plugin-utils": "^7.27.1" + }, + "engines": { + "node": ">=6.9.0" + }, + "peerDependencies": { + "@babel/core": "^7.0.0-0" + } + }, + "node_modules/@babel/plugin-transform-block-scoping": { + "version": "7.28.5", + "resolved": "https://registry.npmjs.org/@babel/plugin-transform-block-scoping/-/plugin-transform-block-scoping-7.28.5.tgz", + "integrity": "sha512-45DmULpySVvmq9Pj3X9B+62Xe+DJGov27QravQJU1LLcapR6/10i+gYVAucGGJpHBp5mYxIMK4nDAT/QDLr47g==", + "dev": true, + "license": "MIT", + "dependencies": { + "@babel/helper-plugin-utils": "^7.27.1" + }, + "engines": { + "node": ">=6.9.0" + }, + "peerDependencies": { + "@babel/core": "^7.0.0-0" + } + }, + "node_modules/@babel/plugin-transform-class-properties": { + "version": "7.27.1", + "resolved": "https://registry.npmjs.org/@babel/plugin-transform-class-properties/-/plugin-transform-class-properties-7.27.1.tgz", + "integrity": "sha512-D0VcalChDMtuRvJIu3U/fwWjf8ZMykz5iZsg77Nuj821vCKI3zCyRLwRdWbsuJ/uRwZhZ002QtCqIkwC/ZkvbA==", + "dev": true, + "license": "MIT", + "dependencies": { + "@babel/helper-create-class-features-plugin": "^7.27.1", + "@babel/helper-plugin-utils": "^7.27.1" + }, + "engines": { + "node": ">=6.9.0" + }, + "peerDependencies": { + "@babel/core": "^7.0.0-0" + } + }, + "node_modules/@babel/plugin-transform-class-static-block": { + "version": "7.28.3", + "resolved": "https://registry.npmjs.org/@babel/plugin-transform-class-static-block/-/plugin-transform-class-static-block-7.28.3.tgz", + "integrity": "sha512-LtPXlBbRoc4Njl/oh1CeD/3jC+atytbnf/UqLoqTDcEYGUPj022+rvfkbDYieUrSj3CaV4yHDByPE+T2HwfsJg==", + "dev": true, + "license": "MIT", + "dependencies": { + "@babel/helper-create-class-features-plugin": "^7.28.3", + "@babel/helper-plugin-utils": "^7.27.1" + }, + "engines": { + "node": ">=6.9.0" + }, + "peerDependencies": { + "@babel/core": "^7.12.0" + } + }, + "node_modules/@babel/plugin-transform-classes": { + "version": "7.28.4", + "resolved": "https://registry.npmjs.org/@babel/plugin-transform-classes/-/plugin-transform-classes-7.28.4.tgz", + "integrity": "sha512-cFOlhIYPBv/iBoc+KS3M6et2XPtbT2HiCRfBXWtfpc9OAyostldxIf9YAYB6ypURBBbx+Qv6nyrLzASfJe+hBA==", + "dev": true, + "license": "MIT", + "dependencies": { + "@babel/helper-annotate-as-pure": "^7.27.3", + "@babel/helper-compilation-targets": "^7.27.2", + "@babel/helper-globals": "^7.28.0", + "@babel/helper-plugin-utils": "^7.27.1", + "@babel/helper-replace-supers": "^7.27.1", + "@babel/traverse": "^7.28.4" + }, + "engines": { + "node": ">=6.9.0" + }, + "peerDependencies": { + "@babel/core": "^7.0.0-0" + } + }, + "node_modules/@babel/plugin-transform-computed-properties": { + "version": "7.27.1", + "resolved": "https://registry.npmjs.org/@babel/plugin-transform-computed-properties/-/plugin-transform-computed-properties-7.27.1.tgz", + "integrity": "sha512-lj9PGWvMTVksbWiDT2tW68zGS/cyo4AkZ/QTp0sQT0mjPopCmrSkzxeXkznjqBxzDI6TclZhOJbBmbBLjuOZUw==", + "dev": true, + "license": "MIT", + "dependencies": { + "@babel/helper-plugin-utils": "^7.27.1", + "@babel/template": "^7.27.1" + }, + "engines": { + "node": ">=6.9.0" + }, + "peerDependencies": { + "@babel/core": "^7.0.0-0" + } + }, + "node_modules/@babel/plugin-transform-destructuring": { + "version": "7.28.5", + "resolved": "https://registry.npmjs.org/@babel/plugin-transform-destructuring/-/plugin-transform-destructuring-7.28.5.tgz", + "integrity": "sha512-Kl9Bc6D0zTUcFUvkNuQh4eGXPKKNDOJQXVyyM4ZAQPMveniJdxi8XMJwLo+xSoW3MIq81bD33lcUe9kZpl0MCw==", + "dev": true, + "license": "MIT", + "dependencies": { + "@babel/helper-plugin-utils": "^7.27.1", + "@babel/traverse": "^7.28.5" + }, + "engines": { + "node": ">=6.9.0" + }, + "peerDependencies": { + "@babel/core": "^7.0.0-0" + } + }, + "node_modules/@babel/plugin-transform-dotall-regex": { + "version": "7.27.1", + "resolved": "https://registry.npmjs.org/@babel/plugin-transform-dotall-regex/-/plugin-transform-dotall-regex-7.27.1.tgz", + "integrity": "sha512-gEbkDVGRvjj7+T1ivxrfgygpT7GUd4vmODtYpbs0gZATdkX8/iSnOtZSxiZnsgm1YjTgjI6VKBGSJJevkrclzw==", + "dev": true, + "license": "MIT", + "dependencies": { + "@babel/helper-create-regexp-features-plugin": "^7.27.1", + "@babel/helper-plugin-utils": "^7.27.1" + }, + "engines": { + "node": ">=6.9.0" + }, + "peerDependencies": { + "@babel/core": "^7.0.0-0" + } + }, + "node_modules/@babel/plugin-transform-duplicate-keys": { + "version": "7.27.1", + "resolved": "https://registry.npmjs.org/@babel/plugin-transform-duplicate-keys/-/plugin-transform-duplicate-keys-7.27.1.tgz", + "integrity": "sha512-MTyJk98sHvSs+cvZ4nOauwTTG1JeonDjSGvGGUNHreGQns+Mpt6WX/dVzWBHgg+dYZhkC4X+zTDfkTU+Vy9y7Q==", + "dev": true, + "license": "MIT", + "dependencies": { + "@babel/helper-plugin-utils": "^7.27.1" + }, + "engines": { + "node": ">=6.9.0" + }, + "peerDependencies": { + "@babel/core": "^7.0.0-0" + } + }, + "node_modules/@babel/plugin-transform-duplicate-named-capturing-groups-regex": { + "version": "7.27.1", + "resolved": "https://registry.npmjs.org/@babel/plugin-transform-duplicate-named-capturing-groups-regex/-/plugin-transform-duplicate-named-capturing-groups-regex-7.27.1.tgz", + "integrity": "sha512-hkGcueTEzuhB30B3eJCbCYeCaaEQOmQR0AdvzpD4LoN0GXMWzzGSuRrxR2xTnCrvNbVwK9N6/jQ92GSLfiZWoQ==", + "dev": true, + "license": "MIT", + "dependencies": { + "@babel/helper-create-regexp-features-plugin": "^7.27.1", + "@babel/helper-plugin-utils": "^7.27.1" + }, + "engines": { + "node": ">=6.9.0" + }, + "peerDependencies": { + "@babel/core": "^7.0.0" + } + }, + "node_modules/@babel/plugin-transform-dynamic-import": { + "version": "7.27.1", + "resolved": "https://registry.npmjs.org/@babel/plugin-transform-dynamic-import/-/plugin-transform-dynamic-import-7.27.1.tgz", + "integrity": "sha512-MHzkWQcEmjzzVW9j2q8LGjwGWpG2mjwaaB0BNQwst3FIjqsg8Ct/mIZlvSPJvfi9y2AC8mi/ktxbFVL9pZ1I4A==", + "dev": true, + "license": "MIT", + "dependencies": { + "@babel/helper-plugin-utils": "^7.27.1" + }, + "engines": { + "node": ">=6.9.0" + }, + "peerDependencies": { + "@babel/core": "^7.0.0-0" + } + }, + "node_modules/@babel/plugin-transform-explicit-resource-management": { + "version": "7.28.0", + "resolved": "https://registry.npmjs.org/@babel/plugin-transform-explicit-resource-management/-/plugin-transform-explicit-resource-management-7.28.0.tgz", + "integrity": "sha512-K8nhUcn3f6iB+P3gwCv/no7OdzOZQcKchW6N389V6PD8NUWKZHzndOd9sPDVbMoBsbmjMqlB4L9fm+fEFNVlwQ==", + "dev": true, + "license": "MIT", + "dependencies": { + "@babel/helper-plugin-utils": "^7.27.1", + "@babel/plugin-transform-destructuring": "^7.28.0" + }, + "engines": { + "node": ">=6.9.0" + }, + "peerDependencies": { + "@babel/core": "^7.0.0-0" + } + }, + "node_modules/@babel/plugin-transform-exponentiation-operator": { + "version": "7.28.5", + "resolved": "https://registry.npmjs.org/@babel/plugin-transform-exponentiation-operator/-/plugin-transform-exponentiation-operator-7.28.5.tgz", + "integrity": "sha512-D4WIMaFtwa2NizOp+dnoFjRez/ClKiC2BqqImwKd1X28nqBtZEyCYJ2ozQrrzlxAFrcrjxo39S6khe9RNDlGzw==", + "dev": true, + "license": "MIT", + "dependencies": { + "@babel/helper-plugin-utils": "^7.27.1" + }, + "engines": { + "node": ">=6.9.0" + }, + "peerDependencies": { + "@babel/core": "^7.0.0-0" + } + }, + "node_modules/@babel/plugin-transform-export-namespace-from": { + "version": "7.27.1", + "resolved": "https://registry.npmjs.org/@babel/plugin-transform-export-namespace-from/-/plugin-transform-export-namespace-from-7.27.1.tgz", + "integrity": "sha512-tQvHWSZ3/jH2xuq/vZDy0jNn+ZdXJeM8gHvX4lnJmsc3+50yPlWdZXIc5ay+umX+2/tJIqHqiEqcJvxlmIvRvQ==", + "dev": true, + "license": "MIT", + "dependencies": { + "@babel/helper-plugin-utils": "^7.27.1" + }, + "engines": { + "node": ">=6.9.0" + }, + "peerDependencies": { + "@babel/core": "^7.0.0-0" + } + }, + "node_modules/@babel/plugin-transform-for-of": { + "version": "7.27.1", + "resolved": "https://registry.npmjs.org/@babel/plugin-transform-for-of/-/plugin-transform-for-of-7.27.1.tgz", + "integrity": "sha512-BfbWFFEJFQzLCQ5N8VocnCtA8J1CLkNTe2Ms2wocj75dd6VpiqS5Z5quTYcUoo4Yq+DN0rtikODccuv7RU81sw==", + "dev": true, + "license": "MIT", + "dependencies": { + "@babel/helper-plugin-utils": "^7.27.1", + "@babel/helper-skip-transparent-expression-wrappers": "^7.27.1" + }, + "engines": { + "node": ">=6.9.0" + }, + "peerDependencies": { + "@babel/core": "^7.0.0-0" + } + }, + "node_modules/@babel/plugin-transform-function-name": { + "version": "7.27.1", + "resolved": "https://registry.npmjs.org/@babel/plugin-transform-function-name/-/plugin-transform-function-name-7.27.1.tgz", + "integrity": "sha512-1bQeydJF9Nr1eBCMMbC+hdwmRlsv5XYOMu03YSWFwNs0HsAmtSxxF1fyuYPqemVldVyFmlCU7w8UE14LupUSZQ==", + "dev": true, + "license": "MIT", + "dependencies": { + "@babel/helper-compilation-targets": "^7.27.1", + "@babel/helper-plugin-utils": "^7.27.1", + "@babel/traverse": "^7.27.1" + }, + "engines": { + "node": ">=6.9.0" + }, + "peerDependencies": { + "@babel/core": "^7.0.0-0" + } + }, + "node_modules/@babel/plugin-transform-json-strings": { + "version": "7.27.1", + "resolved": "https://registry.npmjs.org/@babel/plugin-transform-json-strings/-/plugin-transform-json-strings-7.27.1.tgz", + "integrity": "sha512-6WVLVJiTjqcQauBhn1LkICsR2H+zm62I3h9faTDKt1qP4jn2o72tSvqMwtGFKGTpojce0gJs+76eZ2uCHRZh0Q==", + "dev": true, + "license": "MIT", + "dependencies": { + "@babel/helper-plugin-utils": "^7.27.1" + }, + "engines": { + "node": ">=6.9.0" + }, + "peerDependencies": { + "@babel/core": "^7.0.0-0" + } + }, + "node_modules/@babel/plugin-transform-literals": { + "version": "7.27.1", + "resolved": "https://registry.npmjs.org/@babel/plugin-transform-literals/-/plugin-transform-literals-7.27.1.tgz", + "integrity": "sha512-0HCFSepIpLTkLcsi86GG3mTUzxV5jpmbv97hTETW3yzrAij8aqlD36toB1D0daVFJM8NK6GvKO0gslVQmm+zZA==", + "dev": true, + "license": "MIT", + "dependencies": { + "@babel/helper-plugin-utils": "^7.27.1" + }, + "engines": { + "node": ">=6.9.0" + }, + "peerDependencies": { + "@babel/core": "^7.0.0-0" + } + }, + "node_modules/@babel/plugin-transform-logical-assignment-operators": { + "version": "7.28.5", + "resolved": "https://registry.npmjs.org/@babel/plugin-transform-logical-assignment-operators/-/plugin-transform-logical-assignment-operators-7.28.5.tgz", + "integrity": "sha512-axUuqnUTBuXyHGcJEVVh9pORaN6wC5bYfE7FGzPiaWa3syib9m7g+/IT/4VgCOe2Upef43PHzeAvcrVek6QuuA==", + "dev": true, + "license": "MIT", + "dependencies": { + "@babel/helper-plugin-utils": "^7.27.1" + }, + "engines": { + "node": ">=6.9.0" + }, + "peerDependencies": { + "@babel/core": "^7.0.0-0" + } + }, + "node_modules/@babel/plugin-transform-member-expression-literals": { + "version": "7.27.1", + "resolved": "https://registry.npmjs.org/@babel/plugin-transform-member-expression-literals/-/plugin-transform-member-expression-literals-7.27.1.tgz", + "integrity": "sha512-hqoBX4dcZ1I33jCSWcXrP+1Ku7kdqXf1oeah7ooKOIiAdKQ+uqftgCFNOSzA5AMS2XIHEYeGFg4cKRCdpxzVOQ==", + "dev": true, + "license": "MIT", + "dependencies": { + "@babel/helper-plugin-utils": "^7.27.1" + }, + "engines": { + "node": ">=6.9.0" + }, + "peerDependencies": { + "@babel/core": "^7.0.0-0" + } + }, + "node_modules/@babel/plugin-transform-modules-amd": { + "version": "7.27.1", + "resolved": "https://registry.npmjs.org/@babel/plugin-transform-modules-amd/-/plugin-transform-modules-amd-7.27.1.tgz", + "integrity": "sha512-iCsytMg/N9/oFq6n+gFTvUYDZQOMK5kEdeYxmxt91fcJGycfxVP9CnrxoliM0oumFERba2i8ZtwRUCMhvP1LnA==", + "dev": true, + "license": "MIT", + "dependencies": { + "@babel/helper-module-transforms": "^7.27.1", + "@babel/helper-plugin-utils": "^7.27.1" + }, + "engines": { + "node": ">=6.9.0" + }, + "peerDependencies": { + "@babel/core": "^7.0.0-0" + } + }, + "node_modules/@babel/plugin-transform-modules-commonjs": { + "version": "7.27.1", + "resolved": "https://registry.npmjs.org/@babel/plugin-transform-modules-commonjs/-/plugin-transform-modules-commonjs-7.27.1.tgz", + "integrity": "sha512-OJguuwlTYlN0gBZFRPqwOGNWssZjfIUdS7HMYtN8c1KmwpwHFBwTeFZrg9XZa+DFTitWOW5iTAG7tyCUPsCCyw==", + "dev": true, + "license": "MIT", + "dependencies": { + "@babel/helper-module-transforms": "^7.27.1", + "@babel/helper-plugin-utils": "^7.27.1" + }, + "engines": { + "node": ">=6.9.0" + }, + "peerDependencies": { + "@babel/core": "^7.0.0-0" + } + }, + "node_modules/@babel/plugin-transform-modules-systemjs": { + "version": "7.28.5", + "resolved": "https://registry.npmjs.org/@babel/plugin-transform-modules-systemjs/-/plugin-transform-modules-systemjs-7.28.5.tgz", + "integrity": "sha512-vn5Jma98LCOeBy/KpeQhXcV2WZgaRUtjwQmjoBuLNlOmkg0fB5pdvYVeWRYI69wWKwK2cD1QbMiUQnoujWvrew==", + "dev": true, + "license": "MIT", + "dependencies": { + "@babel/helper-module-transforms": "^7.28.3", + "@babel/helper-plugin-utils": "^7.27.1", + "@babel/helper-validator-identifier": "^7.28.5", + "@babel/traverse": "^7.28.5" + }, + "engines": { + "node": ">=6.9.0" + }, + "peerDependencies": { + "@babel/core": "^7.0.0-0" + } + }, + "node_modules/@babel/plugin-transform-modules-umd": { + "version": "7.27.1", + "resolved": "https://registry.npmjs.org/@babel/plugin-transform-modules-umd/-/plugin-transform-modules-umd-7.27.1.tgz", + "integrity": "sha512-iQBE/xC5BV1OxJbp6WG7jq9IWiD+xxlZhLrdwpPkTX3ydmXdvoCpyfJN7acaIBZaOqTfr76pgzqBJflNbeRK+w==", + "dev": true, + "license": "MIT", + "dependencies": { + "@babel/helper-module-transforms": "^7.27.1", + "@babel/helper-plugin-utils": "^7.27.1" + }, + "engines": { + "node": ">=6.9.0" + }, + "peerDependencies": { + "@babel/core": "^7.0.0-0" + } + }, + "node_modules/@babel/plugin-transform-named-capturing-groups-regex": { + "version": "7.27.1", + "resolved": "https://registry.npmjs.org/@babel/plugin-transform-named-capturing-groups-regex/-/plugin-transform-named-capturing-groups-regex-7.27.1.tgz", + "integrity": "sha512-SstR5JYy8ddZvD6MhV0tM/j16Qds4mIpJTOd1Yu9J9pJjH93bxHECF7pgtc28XvkzTD6Pxcm/0Z73Hvk7kb3Ng==", + "dev": true, + "license": "MIT", + "dependencies": { + "@babel/helper-create-regexp-features-plugin": "^7.27.1", + "@babel/helper-plugin-utils": "^7.27.1" + }, + "engines": { + "node": ">=6.9.0" + }, + "peerDependencies": { + "@babel/core": "^7.0.0" + } + }, + "node_modules/@babel/plugin-transform-new-target": { + "version": "7.27.1", + "resolved": "https://registry.npmjs.org/@babel/plugin-transform-new-target/-/plugin-transform-new-target-7.27.1.tgz", + "integrity": "sha512-f6PiYeqXQ05lYq3TIfIDu/MtliKUbNwkGApPUvyo6+tc7uaR4cPjPe7DFPr15Uyycg2lZU6btZ575CuQoYh7MQ==", + "dev": true, + "license": "MIT", + "dependencies": { + "@babel/helper-plugin-utils": "^7.27.1" + }, + "engines": { + "node": ">=6.9.0" + }, + "peerDependencies": { + "@babel/core": "^7.0.0-0" + } + }, + "node_modules/@babel/plugin-transform-nullish-coalescing-operator": { + "version": "7.27.1", + "resolved": "https://registry.npmjs.org/@babel/plugin-transform-nullish-coalescing-operator/-/plugin-transform-nullish-coalescing-operator-7.27.1.tgz", + "integrity": "sha512-aGZh6xMo6q9vq1JGcw58lZ1Z0+i0xB2x0XaauNIUXd6O1xXc3RwoWEBlsTQrY4KQ9Jf0s5rgD6SiNkaUdJegTA==", + "dev": true, + "license": "MIT", + "dependencies": { + "@babel/helper-plugin-utils": "^7.27.1" + }, + "engines": { + "node": ">=6.9.0" + }, + "peerDependencies": { + "@babel/core": "^7.0.0-0" + } + }, + "node_modules/@babel/plugin-transform-numeric-separator": { + "version": "7.27.1", + "resolved": "https://registry.npmjs.org/@babel/plugin-transform-numeric-separator/-/plugin-transform-numeric-separator-7.27.1.tgz", + "integrity": "sha512-fdPKAcujuvEChxDBJ5c+0BTaS6revLV7CJL08e4m3de8qJfNIuCc2nc7XJYOjBoTMJeqSmwXJ0ypE14RCjLwaw==", + "dev": true, + "license": "MIT", + "dependencies": { + "@babel/helper-plugin-utils": "^7.27.1" + }, + "engines": { + "node": ">=6.9.0" + }, + "peerDependencies": { + "@babel/core": "^7.0.0-0" + } + }, + "node_modules/@babel/plugin-transform-object-rest-spread": { + "version": "7.28.4", + "resolved": "https://registry.npmjs.org/@babel/plugin-transform-object-rest-spread/-/plugin-transform-object-rest-spread-7.28.4.tgz", + "integrity": "sha512-373KA2HQzKhQCYiRVIRr+3MjpCObqzDlyrM6u4I201wL8Mp2wHf7uB8GhDwis03k2ti8Zr65Zyyqs1xOxUF/Ew==", + "dev": true, + "license": "MIT", + "dependencies": { + "@babel/helper-compilation-targets": "^7.27.2", + "@babel/helper-plugin-utils": "^7.27.1", + "@babel/plugin-transform-destructuring": "^7.28.0", + "@babel/plugin-transform-parameters": "^7.27.7", + "@babel/traverse": "^7.28.4" + }, + "engines": { + "node": ">=6.9.0" + }, + "peerDependencies": { + "@babel/core": "^7.0.0-0" + } + }, + "node_modules/@babel/plugin-transform-object-super": { + "version": "7.27.1", + "resolved": "https://registry.npmjs.org/@babel/plugin-transform-object-super/-/plugin-transform-object-super-7.27.1.tgz", + "integrity": "sha512-SFy8S9plRPbIcxlJ8A6mT/CxFdJx/c04JEctz4jf8YZaVS2px34j7NXRrlGlHkN/M2gnpL37ZpGRGVFLd3l8Ng==", + "dev": true, + "license": "MIT", + "dependencies": { + "@babel/helper-plugin-utils": "^7.27.1", + "@babel/helper-replace-supers": "^7.27.1" + }, + "engines": { + "node": ">=6.9.0" + }, + "peerDependencies": { + "@babel/core": "^7.0.0-0" + } + }, + "node_modules/@babel/plugin-transform-optional-catch-binding": { + "version": "7.27.1", + "resolved": "https://registry.npmjs.org/@babel/plugin-transform-optional-catch-binding/-/plugin-transform-optional-catch-binding-7.27.1.tgz", + "integrity": "sha512-txEAEKzYrHEX4xSZN4kJ+OfKXFVSWKB2ZxM9dpcE3wT7smwkNmXo5ORRlVzMVdJbD+Q8ILTgSD7959uj+3Dm3Q==", + "dev": true, + "license": "MIT", + "dependencies": { + "@babel/helper-plugin-utils": "^7.27.1" + }, + "engines": { + "node": ">=6.9.0" + }, + "peerDependencies": { + "@babel/core": "^7.0.0-0" + } + }, + "node_modules/@babel/plugin-transform-optional-chaining": { + "version": "7.28.5", + "resolved": "https://registry.npmjs.org/@babel/plugin-transform-optional-chaining/-/plugin-transform-optional-chaining-7.28.5.tgz", + "integrity": "sha512-N6fut9IZlPnjPwgiQkXNhb+cT8wQKFlJNqcZkWlcTqkcqx6/kU4ynGmLFoa4LViBSirn05YAwk+sQBbPfxtYzQ==", + "dev": true, + "license": "MIT", + "dependencies": { + "@babel/helper-plugin-utils": "^7.27.1", + "@babel/helper-skip-transparent-expression-wrappers": "^7.27.1" + }, + "engines": { + "node": ">=6.9.0" + }, + "peerDependencies": { + "@babel/core": "^7.0.0-0" + } + }, + "node_modules/@babel/plugin-transform-parameters": { + "version": "7.27.7", + "resolved": "https://registry.npmjs.org/@babel/plugin-transform-parameters/-/plugin-transform-parameters-7.27.7.tgz", + "integrity": "sha512-qBkYTYCb76RRxUM6CcZA5KRu8K4SM8ajzVeUgVdMVO9NN9uI/GaVmBg/WKJJGnNokV9SY8FxNOVWGXzqzUidBg==", + "dev": true, + "license": "MIT", + "dependencies": { + "@babel/helper-plugin-utils": "^7.27.1" + }, + "engines": { + "node": ">=6.9.0" + }, + "peerDependencies": { + "@babel/core": "^7.0.0-0" + } + }, + "node_modules/@babel/plugin-transform-private-methods": { + "version": "7.27.1", + "resolved": "https://registry.npmjs.org/@babel/plugin-transform-private-methods/-/plugin-transform-private-methods-7.27.1.tgz", + "integrity": "sha512-10FVt+X55AjRAYI9BrdISN9/AQWHqldOeZDUoLyif1Kn05a56xVBXb8ZouL8pZ9jem8QpXaOt8TS7RHUIS+GPA==", + "dev": true, + "license": "MIT", + "dependencies": { + "@babel/helper-create-class-features-plugin": "^7.27.1", + "@babel/helper-plugin-utils": "^7.27.1" + }, + "engines": { + "node": ">=6.9.0" + }, + "peerDependencies": { + "@babel/core": "^7.0.0-0" + } + }, + "node_modules/@babel/plugin-transform-private-property-in-object": { + "version": "7.27.1", + "resolved": "https://registry.npmjs.org/@babel/plugin-transform-private-property-in-object/-/plugin-transform-private-property-in-object-7.27.1.tgz", + "integrity": "sha512-5J+IhqTi1XPa0DXF83jYOaARrX+41gOewWbkPyjMNRDqgOCqdffGh8L3f/Ek5utaEBZExjSAzcyjmV9SSAWObQ==", + "dev": true, + "license": "MIT", + "dependencies": { + "@babel/helper-annotate-as-pure": "^7.27.1", + "@babel/helper-create-class-features-plugin": "^7.27.1", + "@babel/helper-plugin-utils": "^7.27.1" + }, + "engines": { + "node": ">=6.9.0" + }, + "peerDependencies": { + "@babel/core": "^7.0.0-0" + } + }, + "node_modules/@babel/plugin-transform-property-literals": { + "version": "7.27.1", + "resolved": "https://registry.npmjs.org/@babel/plugin-transform-property-literals/-/plugin-transform-property-literals-7.27.1.tgz", + "integrity": "sha512-oThy3BCuCha8kDZ8ZkgOg2exvPYUlprMukKQXI1r1pJ47NCvxfkEy8vK+r/hT9nF0Aa4H1WUPZZjHTFtAhGfmQ==", + "dev": true, + "license": "MIT", + "dependencies": { + "@babel/helper-plugin-utils": "^7.27.1" + }, + "engines": { + "node": ">=6.9.0" + }, + "peerDependencies": { + "@babel/core": "^7.0.0-0" + } + }, + "node_modules/@babel/plugin-transform-regenerator": { + "version": "7.28.4", + "resolved": "https://registry.npmjs.org/@babel/plugin-transform-regenerator/-/plugin-transform-regenerator-7.28.4.tgz", + "integrity": "sha512-+ZEdQlBoRg9m2NnzvEeLgtvBMO4tkFBw5SQIUgLICgTrumLoU7lr+Oghi6km2PFj+dbUt2u1oby2w3BDO9YQnA==", + "dev": true, + "license": "MIT", + "dependencies": { + "@babel/helper-plugin-utils": "^7.27.1" + }, + "engines": { + "node": ">=6.9.0" + }, + "peerDependencies": { + "@babel/core": "^7.0.0-0" + } + }, + "node_modules/@babel/plugin-transform-regexp-modifiers": { + "version": "7.27.1", + "resolved": "https://registry.npmjs.org/@babel/plugin-transform-regexp-modifiers/-/plugin-transform-regexp-modifiers-7.27.1.tgz", + "integrity": "sha512-TtEciroaiODtXvLZv4rmfMhkCv8jx3wgKpL68PuiPh2M4fvz5jhsA7697N1gMvkvr/JTF13DrFYyEbY9U7cVPA==", + "dev": true, + "license": "MIT", + "dependencies": { + "@babel/helper-create-regexp-features-plugin": "^7.27.1", + "@babel/helper-plugin-utils": "^7.27.1" + }, + "engines": { + "node": ">=6.9.0" + }, + "peerDependencies": { + "@babel/core": "^7.0.0" + } + }, + "node_modules/@babel/plugin-transform-reserved-words": { + "version": "7.27.1", + "resolved": "https://registry.npmjs.org/@babel/plugin-transform-reserved-words/-/plugin-transform-reserved-words-7.27.1.tgz", + "integrity": "sha512-V2ABPHIJX4kC7HegLkYoDpfg9PVmuWy/i6vUM5eGK22bx4YVFD3M5F0QQnWQoDs6AGsUWTVOopBiMFQgHaSkVw==", + "dev": true, + "license": "MIT", + "dependencies": { + "@babel/helper-plugin-utils": "^7.27.1" + }, + "engines": { + "node": ">=6.9.0" + }, + "peerDependencies": { + "@babel/core": "^7.0.0-0" + } + }, + "node_modules/@babel/plugin-transform-shorthand-properties": { + "version": "7.27.1", + "resolved": "https://registry.npmjs.org/@babel/plugin-transform-shorthand-properties/-/plugin-transform-shorthand-properties-7.27.1.tgz", + "integrity": "sha512-N/wH1vcn4oYawbJ13Y/FxcQrWk63jhfNa7jef0ih7PHSIHX2LB7GWE1rkPrOnka9kwMxb6hMl19p7lidA+EHmQ==", + "dev": true, + "license": "MIT", + "dependencies": { + "@babel/helper-plugin-utils": "^7.27.1" + }, + "engines": { + "node": ">=6.9.0" + }, + "peerDependencies": { + "@babel/core": "^7.0.0-0" + } + }, + "node_modules/@babel/plugin-transform-spread": { + "version": "7.27.1", + "resolved": "https://registry.npmjs.org/@babel/plugin-transform-spread/-/plugin-transform-spread-7.27.1.tgz", + "integrity": "sha512-kpb3HUqaILBJcRFVhFUs6Trdd4mkrzcGXss+6/mxUd273PfbWqSDHRzMT2234gIg2QYfAjvXLSquP1xECSg09Q==", + "dev": true, + "license": "MIT", + "dependencies": { + "@babel/helper-plugin-utils": "^7.27.1", + "@babel/helper-skip-transparent-expression-wrappers": "^7.27.1" + }, + "engines": { + "node": ">=6.9.0" + }, + "peerDependencies": { + "@babel/core": "^7.0.0-0" + } + }, + "node_modules/@babel/plugin-transform-sticky-regex": { + "version": "7.27.1", + "resolved": "https://registry.npmjs.org/@babel/plugin-transform-sticky-regex/-/plugin-transform-sticky-regex-7.27.1.tgz", + "integrity": "sha512-lhInBO5bi/Kowe2/aLdBAawijx+q1pQzicSgnkB6dUPc1+RC8QmJHKf2OjvU+NZWitguJHEaEmbV6VWEouT58g==", + "dev": true, + "license": "MIT", + "dependencies": { + "@babel/helper-plugin-utils": "^7.27.1" + }, + "engines": { + "node": ">=6.9.0" + }, + "peerDependencies": { + "@babel/core": "^7.0.0-0" + } + }, + "node_modules/@babel/plugin-transform-template-literals": { + "version": "7.27.1", + "resolved": "https://registry.npmjs.org/@babel/plugin-transform-template-literals/-/plugin-transform-template-literals-7.27.1.tgz", + "integrity": "sha512-fBJKiV7F2DxZUkg5EtHKXQdbsbURW3DZKQUWphDum0uRP6eHGGa/He9mc0mypL680pb+e/lDIthRohlv8NCHkg==", + "dev": true, + "license": "MIT", + "dependencies": { + "@babel/helper-plugin-utils": "^7.27.1" + }, + "engines": { + "node": ">=6.9.0" + }, + "peerDependencies": { + "@babel/core": "^7.0.0-0" + } + }, + "node_modules/@babel/plugin-transform-typeof-symbol": { + "version": "7.27.1", + "resolved": "https://registry.npmjs.org/@babel/plugin-transform-typeof-symbol/-/plugin-transform-typeof-symbol-7.27.1.tgz", + "integrity": "sha512-RiSILC+nRJM7FY5srIyc4/fGIwUhyDuuBSdWn4y6yT6gm652DpCHZjIipgn6B7MQ1ITOUnAKWixEUjQRIBIcLw==", + "dev": true, + "license": "MIT", + "dependencies": { + "@babel/helper-plugin-utils": "^7.27.1" + }, + "engines": { + "node": ">=6.9.0" + }, + "peerDependencies": { + "@babel/core": "^7.0.0-0" + } + }, + "node_modules/@babel/plugin-transform-typescript": { + "version": "7.28.5", + "resolved": "https://registry.npmjs.org/@babel/plugin-transform-typescript/-/plugin-transform-typescript-7.28.5.tgz", + "integrity": "sha512-x2Qa+v/CuEoX7Dr31iAfr0IhInrVOWZU/2vJMJ00FOR/2nM0BcBEclpaf9sWCDc+v5e9dMrhSH8/atq/kX7+bA==", + "dev": true, + "license": "MIT", + "dependencies": { + "@babel/helper-annotate-as-pure": "^7.27.3", + "@babel/helper-create-class-features-plugin": "^7.28.5", + "@babel/helper-plugin-utils": "^7.27.1", + "@babel/helper-skip-transparent-expression-wrappers": "^7.27.1", + "@babel/plugin-syntax-typescript": "^7.27.1" + }, + "engines": { + "node": ">=6.9.0" + }, + "peerDependencies": { + "@babel/core": "^7.0.0-0" + } + }, + "node_modules/@babel/plugin-transform-unicode-escapes": { + "version": "7.27.1", + "resolved": "https://registry.npmjs.org/@babel/plugin-transform-unicode-escapes/-/plugin-transform-unicode-escapes-7.27.1.tgz", + "integrity": "sha512-Ysg4v6AmF26k9vpfFuTZg8HRfVWzsh1kVfowA23y9j/Gu6dOuahdUVhkLqpObp3JIv27MLSii6noRnuKN8H0Mg==", + "dev": true, + "license": "MIT", + "dependencies": { + "@babel/helper-plugin-utils": "^7.27.1" + }, + "engines": { + "node": ">=6.9.0" + }, + "peerDependencies": { + "@babel/core": "^7.0.0-0" + } + }, + "node_modules/@babel/plugin-transform-unicode-property-regex": { + "version": "7.27.1", + "resolved": "https://registry.npmjs.org/@babel/plugin-transform-unicode-property-regex/-/plugin-transform-unicode-property-regex-7.27.1.tgz", + "integrity": "sha512-uW20S39PnaTImxp39O5qFlHLS9LJEmANjMG7SxIhap8rCHqu0Ik+tLEPX5DKmHn6CsWQ7j3lix2tFOa5YtL12Q==", + "dev": true, + "license": "MIT", + "dependencies": { + "@babel/helper-create-regexp-features-plugin": "^7.27.1", + "@babel/helper-plugin-utils": "^7.27.1" + }, + "engines": { + "node": ">=6.9.0" + }, + "peerDependencies": { + "@babel/core": "^7.0.0-0" + } + }, + "node_modules/@babel/plugin-transform-unicode-regex": { + "version": "7.27.1", + "resolved": "https://registry.npmjs.org/@babel/plugin-transform-unicode-regex/-/plugin-transform-unicode-regex-7.27.1.tgz", + "integrity": "sha512-xvINq24TRojDuyt6JGtHmkVkrfVV3FPT16uytxImLeBZqW3/H52yN+kM1MGuyPkIQxrzKwPHs5U/MP3qKyzkGw==", + "dev": true, + "license": "MIT", + "dependencies": { + "@babel/helper-create-regexp-features-plugin": "^7.27.1", + "@babel/helper-plugin-utils": "^7.27.1" + }, + "engines": { + "node": ">=6.9.0" + }, + "peerDependencies": { + "@babel/core": "^7.0.0-0" + } + }, + "node_modules/@babel/plugin-transform-unicode-sets-regex": { + "version": "7.27.1", + "resolved": "https://registry.npmjs.org/@babel/plugin-transform-unicode-sets-regex/-/plugin-transform-unicode-sets-regex-7.27.1.tgz", + "integrity": "sha512-EtkOujbc4cgvb0mlpQefi4NTPBzhSIevblFevACNLUspmrALgmEBdL/XfnyyITfd8fKBZrZys92zOWcik7j9Tw==", + "dev": true, + "license": "MIT", + "dependencies": { + "@babel/helper-create-regexp-features-plugin": "^7.27.1", + "@babel/helper-plugin-utils": "^7.27.1" + }, + "engines": { + "node": ">=6.9.0" + }, + "peerDependencies": { + "@babel/core": "^7.0.0" + } + }, + "node_modules/@babel/preset-env": { + "version": "7.28.5", + "resolved": "https://registry.npmjs.org/@babel/preset-env/-/preset-env-7.28.5.tgz", + "integrity": "sha512-S36mOoi1Sb6Fz98fBfE+UZSpYw5mJm0NUHtIKrOuNcqeFauy1J6dIvXm2KRVKobOSaGq4t/hBXdN4HGU3wL9Wg==", + "dev": true, + "license": "MIT", + "dependencies": { + "@babel/compat-data": "^7.28.5", + "@babel/helper-compilation-targets": "^7.27.2", + "@babel/helper-plugin-utils": "^7.27.1", + "@babel/helper-validator-option": "^7.27.1", + "@babel/plugin-bugfix-firefox-class-in-computed-class-key": "^7.28.5", + "@babel/plugin-bugfix-safari-class-field-initializer-scope": "^7.27.1", + "@babel/plugin-bugfix-safari-id-destructuring-collision-in-function-expression": "^7.27.1", + "@babel/plugin-bugfix-v8-spread-parameters-in-optional-chaining": "^7.27.1", + "@babel/plugin-bugfix-v8-static-class-fields-redefine-readonly": "^7.28.3", + "@babel/plugin-proposal-private-property-in-object": "7.21.0-placeholder-for-preset-env.2", + "@babel/plugin-syntax-import-assertions": "^7.27.1", + "@babel/plugin-syntax-import-attributes": "^7.27.1", + "@babel/plugin-syntax-unicode-sets-regex": "^7.18.6", + "@babel/plugin-transform-arrow-functions": "^7.27.1", + "@babel/plugin-transform-async-generator-functions": "^7.28.0", + "@babel/plugin-transform-async-to-generator": "^7.27.1", + "@babel/plugin-transform-block-scoped-functions": "^7.27.1", + "@babel/plugin-transform-block-scoping": "^7.28.5", + "@babel/plugin-transform-class-properties": "^7.27.1", + "@babel/plugin-transform-class-static-block": "^7.28.3", + "@babel/plugin-transform-classes": "^7.28.4", + "@babel/plugin-transform-computed-properties": "^7.27.1", + "@babel/plugin-transform-destructuring": "^7.28.5", + "@babel/plugin-transform-dotall-regex": "^7.27.1", + "@babel/plugin-transform-duplicate-keys": "^7.27.1", + "@babel/plugin-transform-duplicate-named-capturing-groups-regex": "^7.27.1", + "@babel/plugin-transform-dynamic-import": "^7.27.1", + "@babel/plugin-transform-explicit-resource-management": "^7.28.0", + "@babel/plugin-transform-exponentiation-operator": "^7.28.5", + "@babel/plugin-transform-export-namespace-from": "^7.27.1", + "@babel/plugin-transform-for-of": "^7.27.1", + "@babel/plugin-transform-function-name": "^7.27.1", + "@babel/plugin-transform-json-strings": "^7.27.1", + "@babel/plugin-transform-literals": "^7.27.1", + "@babel/plugin-transform-logical-assignment-operators": "^7.28.5", + "@babel/plugin-transform-member-expression-literals": "^7.27.1", + "@babel/plugin-transform-modules-amd": "^7.27.1", + "@babel/plugin-transform-modules-commonjs": "^7.27.1", + "@babel/plugin-transform-modules-systemjs": "^7.28.5", + "@babel/plugin-transform-modules-umd": "^7.27.1", + "@babel/plugin-transform-named-capturing-groups-regex": "^7.27.1", + "@babel/plugin-transform-new-target": "^7.27.1", + "@babel/plugin-transform-nullish-coalescing-operator": "^7.27.1", + "@babel/plugin-transform-numeric-separator": "^7.27.1", + "@babel/plugin-transform-object-rest-spread": "^7.28.4", + "@babel/plugin-transform-object-super": "^7.27.1", + "@babel/plugin-transform-optional-catch-binding": "^7.27.1", + "@babel/plugin-transform-optional-chaining": "^7.28.5", + "@babel/plugin-transform-parameters": "^7.27.7", + "@babel/plugin-transform-private-methods": "^7.27.1", + "@babel/plugin-transform-private-property-in-object": "^7.27.1", + "@babel/plugin-transform-property-literals": "^7.27.1", + "@babel/plugin-transform-regenerator": "^7.28.4", + "@babel/plugin-transform-regexp-modifiers": "^7.27.1", + "@babel/plugin-transform-reserved-words": "^7.27.1", + "@babel/plugin-transform-shorthand-properties": "^7.27.1", + "@babel/plugin-transform-spread": "^7.27.1", + "@babel/plugin-transform-sticky-regex": "^7.27.1", + "@babel/plugin-transform-template-literals": "^7.27.1", + "@babel/plugin-transform-typeof-symbol": "^7.27.1", + "@babel/plugin-transform-unicode-escapes": "^7.27.1", + "@babel/plugin-transform-unicode-property-regex": "^7.27.1", + "@babel/plugin-transform-unicode-regex": "^7.27.1", + "@babel/plugin-transform-unicode-sets-regex": "^7.27.1", + "@babel/preset-modules": "0.1.6-no-external-plugins", + "babel-plugin-polyfill-corejs2": "^0.4.14", + "babel-plugin-polyfill-corejs3": "^0.13.0", + "babel-plugin-polyfill-regenerator": "^0.6.5", + "core-js-compat": "^3.43.0", + "semver": "^6.3.1" + }, + "engines": { + "node": ">=6.9.0" + }, + "peerDependencies": { + "@babel/core": "^7.0.0-0" + } + }, + "node_modules/@babel/preset-modules": { + "version": "0.1.6-no-external-plugins", + "resolved": "https://registry.npmjs.org/@babel/preset-modules/-/preset-modules-0.1.6-no-external-plugins.tgz", + "integrity": "sha512-HrcgcIESLm9aIR842yhJ5RWan/gebQUJ6E/E5+rf0y9o6oj7w0Br+sWuL6kEQ/o/AdfvR1Je9jG18/gnpwjEyA==", + "dev": true, + "license": "MIT", + "dependencies": { + "@babel/helper-plugin-utils": "^7.0.0", + "@babel/types": "^7.4.4", + "esutils": "^2.0.2" + }, + "peerDependencies": { + "@babel/core": "^7.0.0-0 || ^8.0.0-0 <8.0.0" + } + }, + "node_modules/@babel/preset-typescript": { + "version": "7.28.5", + "resolved": "https://registry.npmjs.org/@babel/preset-typescript/-/preset-typescript-7.28.5.tgz", + "integrity": "sha512-+bQy5WOI2V6LJZpPVxY+yp66XdZ2yifu0Mc1aP5CQKgjn4QM5IN2i5fAZ4xKop47pr8rpVhiAeu+nDQa12C8+g==", + "dev": true, + "license": "MIT", + "dependencies": { + "@babel/helper-plugin-utils": "^7.27.1", + "@babel/helper-validator-option": "^7.27.1", + "@babel/plugin-syntax-jsx": "^7.27.1", + "@babel/plugin-transform-modules-commonjs": "^7.27.1", + "@babel/plugin-transform-typescript": "^7.28.5" + }, + "engines": { + "node": ">=6.9.0" + }, + "peerDependencies": { + "@babel/core": "^7.0.0-0" + } + }, + "node_modules/@babel/template": { + "version": "7.27.2", + "resolved": "https://registry.npmjs.org/@babel/template/-/template-7.27.2.tgz", + "integrity": "sha512-LPDZ85aEJyYSd18/DkjNh4/y1ntkE5KwUHWTiqgRxruuZL2F1yuHligVHLvcHY2vMHXttKFpJn6LwfI7cw7ODw==", + "dev": true, + "license": "MIT", + "dependencies": { + "@babel/code-frame": "^7.27.1", + "@babel/parser": "^7.27.2", + "@babel/types": "^7.27.1" + }, + "engines": { + "node": ">=6.9.0" + } + }, + "node_modules/@babel/traverse": { + "version": "7.28.5", + "resolved": "https://registry.npmjs.org/@babel/traverse/-/traverse-7.28.5.tgz", + "integrity": "sha512-TCCj4t55U90khlYkVV/0TfkJkAkUg3jZFA3Neb7unZT8CPok7iiRfaX0F+WnqWqt7OxhOn0uBKXCw4lbL8W0aQ==", + "dev": true, + "license": "MIT", + "dependencies": { + "@babel/code-frame": "^7.27.1", + "@babel/generator": "^7.28.5", + "@babel/helper-globals": "^7.28.0", + "@babel/parser": "^7.28.5", + "@babel/template": "^7.27.2", + "@babel/types": "^7.28.5", + "debug": "^4.3.1" + }, + "engines": { + "node": ">=6.9.0" + } + }, + "node_modules/@babel/types": { + "version": "7.28.5", + "resolved": "https://registry.npmjs.org/@babel/types/-/types-7.28.5.tgz", + "integrity": "sha512-qQ5m48eI/MFLQ5PxQj4PFaprjyCTLI37ElWMmNs0K8Lk3dVeOdNpB3ks8jc7yM5CDmVC73eMVk/trk3fgmrUpA==", + "dev": true, + "license": "MIT", + "dependencies": { + "@babel/helper-string-parser": "^7.27.1", + "@babel/helper-validator-identifier": "^7.28.5" + }, + "engines": { + "node": ">=6.9.0" + } + }, + "node_modules/@bcoe/v8-coverage": { + "version": "0.2.3", + "resolved": "https://registry.npmjs.org/@bcoe/v8-coverage/-/v8-coverage-0.2.3.tgz", + "integrity": "sha512-0hYQ8SB4Db5zvZB4axdMHGwEaQjkZzFjQiN9LVYvIFB2nSUHW9tYpxWriPrWDASIxiaXax83REcLxuSdnGPZtw==", + "dev": true, + "license": "MIT" + }, + "node_modules/@emnapi/core": { + "version": "1.7.0", + "resolved": "https://registry.npmjs.org/@emnapi/core/-/core-1.7.0.tgz", + "integrity": "sha512-pJdKGq/1iquWYtv1RRSljZklxHCOCAJFJrImO5ZLKPJVJlVUcs8yFwNQlqS0Lo8xT1VAXXTCZocF9n26FWEKsw==", + "dev": true, + "license": "MIT", + "optional": true, + "dependencies": { + "@emnapi/wasi-threads": "1.1.0", + "tslib": "^2.4.0" + } + }, + "node_modules/@emnapi/runtime": { + "version": "1.7.0", + "resolved": "https://registry.npmjs.org/@emnapi/runtime/-/runtime-1.7.0.tgz", + "integrity": "sha512-oAYoQnCYaQZKVS53Fq23ceWMRxq5EhQsE0x0RdQ55jT7wagMu5k+fS39v1fiSLrtrLQlXwVINenqhLMtTrV/1Q==", + "dev": true, + "license": "MIT", + "optional": true, + "dependencies": { + "tslib": "^2.4.0" + } + }, + "node_modules/@emnapi/wasi-threads": { + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/@emnapi/wasi-threads/-/wasi-threads-1.1.0.tgz", + "integrity": "sha512-WI0DdZ8xFSbgMjR1sFsKABJ/C5OnRrjT06JXbZKexJGrDuPTzZdDYfFlsgcCXCyf+suG5QU2e/y1Wo2V/OapLQ==", + "dev": true, + "license": "MIT", + "optional": true, + "dependencies": { + "tslib": "^2.4.0" + } + }, + "node_modules/@isaacs/cliui": { + "version": "8.0.2", + "resolved": "https://registry.npmjs.org/@isaacs/cliui/-/cliui-8.0.2.tgz", + "integrity": "sha512-O8jcjabXaleOG9DQ0+ARXWZBTfnP4WNAqzuiJK7ll44AmxGKv/J2M4TPjxjY3znBCfvBXFzucm1twdyFybFqEA==", + "dev": true, + "license": "ISC", + "dependencies": { + "string-width": "^5.1.2", + "string-width-cjs": "npm:string-width@^4.2.0", + "strip-ansi": "^7.0.1", + "strip-ansi-cjs": "npm:strip-ansi@^6.0.1", + "wrap-ansi": "^8.1.0", + "wrap-ansi-cjs": "npm:wrap-ansi@^7.0.0" + }, + "engines": { + "node": ">=12" + } + }, + "node_modules/@istanbuljs/load-nyc-config": { + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/@istanbuljs/load-nyc-config/-/load-nyc-config-1.1.0.tgz", + "integrity": "sha512-VjeHSlIzpv/NyD3N0YuHfXOPDIixcA1q2ZV98wsMqcYlPmv2n3Yb2lYP9XMElnaFVXg5A7YLTeLu6V84uQDjmQ==", + "dev": true, + "license": "ISC", + "dependencies": { + "camelcase": "^5.3.1", + "find-up": "^4.1.0", + "get-package-type": "^0.1.0", + "js-yaml": "^3.13.1", + "resolve-from": "^5.0.0" + }, + "engines": { + "node": ">=8" + } + }, + "node_modules/@istanbuljs/schema": { + "version": "0.1.3", + "resolved": "https://registry.npmjs.org/@istanbuljs/schema/-/schema-0.1.3.tgz", + "integrity": "sha512-ZXRY4jNvVgSVQ8DL3LTcakaAtXwTVUxE81hslsyD2AtoXW/wVob10HkOJ1X/pAlcI7D+2YoZKg5do8G/w6RYgA==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=8" + } + }, + "node_modules/@jest/console": { + "version": "30.2.0", + "resolved": "https://registry.npmjs.org/@jest/console/-/console-30.2.0.tgz", + "integrity": "sha512-+O1ifRjkvYIkBqASKWgLxrpEhQAAE7hY77ALLUufSk5717KfOShg6IbqLmdsLMPdUiFvA2kTs0R7YZy+l0IzZQ==", + "dev": true, + "license": "MIT", + "dependencies": { + "@jest/types": "30.2.0", + "@types/node": "*", + "chalk": "^4.1.2", + "jest-message-util": "30.2.0", + "jest-util": "30.2.0", + "slash": "^3.0.0" + }, + "engines": { + "node": "^18.14.0 || ^20.0.0 || ^22.0.0 || >=24.0.0" + } + }, + "node_modules/@jest/core": { + "version": "30.2.0", + "resolved": "https://registry.npmjs.org/@jest/core/-/core-30.2.0.tgz", + "integrity": "sha512-03W6IhuhjqTlpzh/ojut/pDB2LPRygyWX8ExpgHtQA8H/3K7+1vKmcINx5UzeOX1se6YEsBsOHQ1CRzf3fOwTQ==", + "dev": true, + "license": "MIT", + "dependencies": { + "@jest/console": "30.2.0", + "@jest/pattern": "30.0.1", + "@jest/reporters": "30.2.0", + "@jest/test-result": "30.2.0", + "@jest/transform": "30.2.0", + "@jest/types": "30.2.0", + "@types/node": "*", + "ansi-escapes": "^4.3.2", + "chalk": "^4.1.2", + "ci-info": "^4.2.0", + "exit-x": "^0.2.2", + "graceful-fs": "^4.2.11", + "jest-changed-files": "30.2.0", + "jest-config": "30.2.0", + "jest-haste-map": "30.2.0", + "jest-message-util": "30.2.0", + "jest-regex-util": "30.0.1", + "jest-resolve": "30.2.0", + "jest-resolve-dependencies": "30.2.0", + "jest-runner": "30.2.0", + "jest-runtime": "30.2.0", + "jest-snapshot": "30.2.0", + "jest-util": "30.2.0", + "jest-validate": "30.2.0", + "jest-watcher": "30.2.0", + "micromatch": "^4.0.8", + "pretty-format": "30.2.0", + "slash": "^3.0.0" + }, + "engines": { + "node": "^18.14.0 || ^20.0.0 || ^22.0.0 || >=24.0.0" + }, + "peerDependencies": { + "node-notifier": "^8.0.1 || ^9.0.0 || ^10.0.0" + }, + "peerDependenciesMeta": { + "node-notifier": { + "optional": true + } + } + }, + "node_modules/@jest/diff-sequences": { + "version": "30.0.1", + "resolved": "https://registry.npmjs.org/@jest/diff-sequences/-/diff-sequences-30.0.1.tgz", + "integrity": "sha512-n5H8QLDJ47QqbCNn5SuFjCRDrOLEZ0h8vAHCK5RL9Ls7Xa8AQLa/YxAc9UjFqoEDM48muwtBGjtMY5cr0PLDCw==", + "dev": true, + "license": "MIT", + "engines": { + "node": "^18.14.0 || ^20.0.0 || ^22.0.0 || >=24.0.0" + } + }, + "node_modules/@jest/environment": { + "version": "30.2.0", + "resolved": "https://registry.npmjs.org/@jest/environment/-/environment-30.2.0.tgz", + "integrity": "sha512-/QPTL7OBJQ5ac09UDRa3EQes4gt1FTEG/8jZ/4v5IVzx+Cv7dLxlVIvfvSVRiiX2drWyXeBjkMSR8hvOWSog5g==", + "dev": true, + "license": "MIT", + "dependencies": { + "@jest/fake-timers": "30.2.0", + "@jest/types": "30.2.0", + "@types/node": "*", + "jest-mock": "30.2.0" + }, + "engines": { + "node": "^18.14.0 || ^20.0.0 || ^22.0.0 || >=24.0.0" + } + }, + "node_modules/@jest/expect": { + "version": "30.2.0", + "resolved": "https://registry.npmjs.org/@jest/expect/-/expect-30.2.0.tgz", + "integrity": "sha512-V9yxQK5erfzx99Sf+7LbhBwNWEZ9eZay8qQ9+JSC0TrMR1pMDHLMY+BnVPacWU6Jamrh252/IKo4F1Xn/zfiqA==", + "dev": true, + "license": "MIT", + "dependencies": { + "expect": "30.2.0", + "jest-snapshot": "30.2.0" + }, + "engines": { + "node": "^18.14.0 || ^20.0.0 || ^22.0.0 || >=24.0.0" + } + }, + "node_modules/@jest/expect-utils": { + "version": "30.2.0", + "resolved": "https://registry.npmjs.org/@jest/expect-utils/-/expect-utils-30.2.0.tgz", + "integrity": "sha512-1JnRfhqpD8HGpOmQp180Fo9Zt69zNtC+9lR+kT7NVL05tNXIi+QC8Csz7lfidMoVLPD3FnOtcmp0CEFnxExGEA==", + "dev": true, + "license": "MIT", + "dependencies": { + "@jest/get-type": "30.1.0" + }, + "engines": { + "node": "^18.14.0 || ^20.0.0 || ^22.0.0 || >=24.0.0" + } + }, + "node_modules/@jest/fake-timers": { + "version": "30.2.0", + "resolved": "https://registry.npmjs.org/@jest/fake-timers/-/fake-timers-30.2.0.tgz", + "integrity": "sha512-HI3tRLjRxAbBy0VO8dqqm7Hb2mIa8d5bg/NJkyQcOk7V118ObQML8RC5luTF/Zsg4474a+gDvhce7eTnP4GhYw==", + "dev": true, + "license": "MIT", + "dependencies": { + "@jest/types": "30.2.0", + "@sinonjs/fake-timers": "^13.0.0", + "@types/node": "*", + "jest-message-util": "30.2.0", + "jest-mock": "30.2.0", + "jest-util": "30.2.0" + }, + "engines": { + "node": "^18.14.0 || ^20.0.0 || ^22.0.0 || >=24.0.0" + } + }, + "node_modules/@jest/get-type": { + "version": "30.1.0", + "resolved": "https://registry.npmjs.org/@jest/get-type/-/get-type-30.1.0.tgz", + "integrity": "sha512-eMbZE2hUnx1WV0pmURZY9XoXPkUYjpc55mb0CrhtdWLtzMQPFvu/rZkTLZFTsdaVQa+Tr4eWAteqcUzoawq/uA==", + "dev": true, + "license": "MIT", + "engines": { + "node": "^18.14.0 || ^20.0.0 || ^22.0.0 || >=24.0.0" + } + }, + "node_modules/@jest/globals": { + "version": "30.2.0", + "resolved": "https://registry.npmjs.org/@jest/globals/-/globals-30.2.0.tgz", + "integrity": "sha512-b63wmnKPaK+6ZZfpYhz9K61oybvbI1aMcIs80++JI1O1rR1vaxHUCNqo3ITu6NU0d4V34yZFoHMn/uoKr/Rwfw==", + "dev": true, + "license": "MIT", + "dependencies": { + "@jest/environment": "30.2.0", + "@jest/expect": "30.2.0", + "@jest/types": "30.2.0", + "jest-mock": "30.2.0" + }, + "engines": { + "node": "^18.14.0 || ^20.0.0 || ^22.0.0 || >=24.0.0" + } + }, + "node_modules/@jest/pattern": { + "version": "30.0.1", + "resolved": "https://registry.npmjs.org/@jest/pattern/-/pattern-30.0.1.tgz", + "integrity": "sha512-gWp7NfQW27LaBQz3TITS8L7ZCQ0TLvtmI//4OwlQRx4rnWxcPNIYjxZpDcN4+UlGxgm3jS5QPz8IPTCkb59wZA==", + "dev": true, + "license": "MIT", + "dependencies": { + "@types/node": "*", + "jest-regex-util": "30.0.1" + }, + "engines": { + "node": "^18.14.0 || ^20.0.0 || ^22.0.0 || >=24.0.0" + } + }, + "node_modules/@jest/reporters": { + "version": "30.2.0", + "resolved": "https://registry.npmjs.org/@jest/reporters/-/reporters-30.2.0.tgz", + "integrity": "sha512-DRyW6baWPqKMa9CzeiBjHwjd8XeAyco2Vt8XbcLFjiwCOEKOvy82GJ8QQnJE9ofsxCMPjH4MfH8fCWIHHDKpAQ==", + "dev": true, + "license": "MIT", + "dependencies": { + "@bcoe/v8-coverage": "^0.2.3", + "@jest/console": "30.2.0", + "@jest/test-result": "30.2.0", + "@jest/transform": "30.2.0", + "@jest/types": "30.2.0", + "@jridgewell/trace-mapping": "^0.3.25", + "@types/node": "*", + "chalk": "^4.1.2", + "collect-v8-coverage": "^1.0.2", + "exit-x": "^0.2.2", + "glob": "^10.3.10", + "graceful-fs": "^4.2.11", + "istanbul-lib-coverage": "^3.0.0", + "istanbul-lib-instrument": "^6.0.0", + "istanbul-lib-report": "^3.0.0", + "istanbul-lib-source-maps": "^5.0.0", + "istanbul-reports": "^3.1.3", + "jest-message-util": "30.2.0", + "jest-util": "30.2.0", + "jest-worker": "30.2.0", + "slash": "^3.0.0", + "string-length": "^4.0.2", + "v8-to-istanbul": "^9.0.1" + }, + "engines": { + "node": "^18.14.0 || ^20.0.0 || ^22.0.0 || >=24.0.0" + }, + "peerDependencies": { + "node-notifier": "^8.0.1 || ^9.0.0 || ^10.0.0" + }, + "peerDependenciesMeta": { + "node-notifier": { + "optional": true + } + } + }, + "node_modules/@jest/schemas": { + "version": "30.0.5", + "resolved": "https://registry.npmjs.org/@jest/schemas/-/schemas-30.0.5.tgz", + "integrity": "sha512-DmdYgtezMkh3cpU8/1uyXakv3tJRcmcXxBOcO0tbaozPwpmh4YMsnWrQm9ZmZMfa5ocbxzbFk6O4bDPEc/iAnA==", + "dev": true, + "license": "MIT", + "dependencies": { + "@sinclair/typebox": "^0.34.0" + }, + "engines": { + "node": "^18.14.0 || ^20.0.0 || ^22.0.0 || >=24.0.0" + } + }, + "node_modules/@jest/snapshot-utils": { + "version": "30.2.0", + "resolved": "https://registry.npmjs.org/@jest/snapshot-utils/-/snapshot-utils-30.2.0.tgz", + "integrity": "sha512-0aVxM3RH6DaiLcjj/b0KrIBZhSX1373Xci4l3cW5xiUWPctZ59zQ7jj4rqcJQ/Z8JuN/4wX3FpJSa3RssVvCug==", + "dev": true, + "license": "MIT", + "dependencies": { + "@jest/types": "30.2.0", + "chalk": "^4.1.2", + "graceful-fs": "^4.2.11", + "natural-compare": "^1.4.0" + }, + "engines": { + "node": "^18.14.0 || ^20.0.0 || ^22.0.0 || >=24.0.0" + } + }, + "node_modules/@jest/source-map": { + "version": "30.0.1", + "resolved": "https://registry.npmjs.org/@jest/source-map/-/source-map-30.0.1.tgz", + "integrity": "sha512-MIRWMUUR3sdbP36oyNyhbThLHyJ2eEDClPCiHVbrYAe5g3CHRArIVpBw7cdSB5fr+ofSfIb2Tnsw8iEHL0PYQg==", + "dev": true, + "license": "MIT", + "dependencies": { + "@jridgewell/trace-mapping": "^0.3.25", + "callsites": "^3.1.0", + "graceful-fs": "^4.2.11" + }, + "engines": { + "node": "^18.14.0 || ^20.0.0 || ^22.0.0 || >=24.0.0" + } + }, + "node_modules/@jest/test-result": { + "version": "30.2.0", + "resolved": "https://registry.npmjs.org/@jest/test-result/-/test-result-30.2.0.tgz", + "integrity": "sha512-RF+Z+0CCHkARz5HT9mcQCBulb1wgCP3FBvl9VFokMX27acKphwyQsNuWH3c+ojd1LeWBLoTYoxF0zm6S/66mjg==", + "dev": true, + "license": "MIT", + "dependencies": { + "@jest/console": "30.2.0", + "@jest/types": "30.2.0", + "@types/istanbul-lib-coverage": "^2.0.6", + "collect-v8-coverage": "^1.0.2" + }, + "engines": { + "node": "^18.14.0 || ^20.0.0 || ^22.0.0 || >=24.0.0" + } + }, + "node_modules/@jest/test-sequencer": { + "version": "30.2.0", + "resolved": "https://registry.npmjs.org/@jest/test-sequencer/-/test-sequencer-30.2.0.tgz", + "integrity": "sha512-wXKgU/lk8fKXMu/l5Hog1R61bL4q5GCdT6OJvdAFz1P+QrpoFuLU68eoKuVc4RbrTtNnTL5FByhWdLgOPSph+Q==", + "dev": true, + "license": "MIT", + "dependencies": { + "@jest/test-result": "30.2.0", + "graceful-fs": "^4.2.11", + "jest-haste-map": "30.2.0", + "slash": "^3.0.0" + }, + "engines": { + "node": "^18.14.0 || ^20.0.0 || ^22.0.0 || >=24.0.0" + } + }, + "node_modules/@jest/transform": { + "version": "30.2.0", + "resolved": "https://registry.npmjs.org/@jest/transform/-/transform-30.2.0.tgz", + "integrity": "sha512-XsauDV82o5qXbhalKxD7p4TZYYdwcaEXC77PPD2HixEFF+6YGppjrAAQurTl2ECWcEomHBMMNS9AH3kcCFx8jA==", + "dev": true, + "license": "MIT", + "dependencies": { + "@babel/core": "^7.27.4", + "@jest/types": "30.2.0", + "@jridgewell/trace-mapping": "^0.3.25", + "babel-plugin-istanbul": "^7.0.1", + "chalk": "^4.1.2", + "convert-source-map": "^2.0.0", + "fast-json-stable-stringify": "^2.1.0", + "graceful-fs": "^4.2.11", + "jest-haste-map": "30.2.0", + "jest-regex-util": "30.0.1", + "jest-util": "30.2.0", + "micromatch": "^4.0.8", + "pirates": "^4.0.7", + "slash": "^3.0.0", + "write-file-atomic": "^5.0.1" + }, + "engines": { + "node": "^18.14.0 || ^20.0.0 || ^22.0.0 || >=24.0.0" + } + }, + "node_modules/@jest/types": { + "version": "30.2.0", + "resolved": "https://registry.npmjs.org/@jest/types/-/types-30.2.0.tgz", + "integrity": "sha512-H9xg1/sfVvyfU7o3zMfBEjQ1gcsdeTMgqHoYdN79tuLqfTtuu7WckRA1R5whDwOzxaZAeMKTYWqP+WCAi0CHsg==", + "dev": true, + "license": "MIT", + "dependencies": { + "@jest/pattern": "30.0.1", + "@jest/schemas": "30.0.5", + "@types/istanbul-lib-coverage": "^2.0.6", + "@types/istanbul-reports": "^3.0.4", + "@types/node": "*", + "@types/yargs": "^17.0.33", + "chalk": "^4.1.2" + }, + "engines": { + "node": "^18.14.0 || ^20.0.0 || ^22.0.0 || >=24.0.0" + } + }, + "node_modules/@jridgewell/gen-mapping": { + "version": "0.3.13", + "resolved": "https://registry.npmjs.org/@jridgewell/gen-mapping/-/gen-mapping-0.3.13.tgz", + "integrity": "sha512-2kkt/7niJ6MgEPxF0bYdQ6etZaA+fQvDcLKckhy1yIQOzaoKjBBjSj63/aLVjYE3qhRt5dvM+uUyfCg6UKCBbA==", + "dev": true, + "license": "MIT", + "dependencies": { + "@jridgewell/sourcemap-codec": "^1.5.0", + "@jridgewell/trace-mapping": "^0.3.24" + } + }, + "node_modules/@jridgewell/remapping": { + "version": "2.3.5", + "resolved": "https://registry.npmjs.org/@jridgewell/remapping/-/remapping-2.3.5.tgz", + "integrity": "sha512-LI9u/+laYG4Ds1TDKSJW2YPrIlcVYOwi2fUC6xB43lueCjgxV4lffOCZCtYFiH6TNOX+tQKXx97T4IKHbhyHEQ==", + "dev": true, + "license": "MIT", + "dependencies": { + "@jridgewell/gen-mapping": "^0.3.5", + "@jridgewell/trace-mapping": "^0.3.24" + } + }, + "node_modules/@jridgewell/resolve-uri": { + "version": "3.1.2", + "resolved": "https://registry.npmjs.org/@jridgewell/resolve-uri/-/resolve-uri-3.1.2.tgz", + "integrity": "sha512-bRISgCIjP20/tbWSPWMEi54QVPRZExkuD9lJL+UIxUKtwVJA8wW1Trb1jMs1RFXo1CBTNZ/5hpC9QvmKWdopKw==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=6.0.0" + } + }, + "node_modules/@jridgewell/sourcemap-codec": { + "version": "1.5.5", + "resolved": "https://registry.npmjs.org/@jridgewell/sourcemap-codec/-/sourcemap-codec-1.5.5.tgz", + "integrity": "sha512-cYQ9310grqxueWbl+WuIUIaiUaDcj7WOq5fVhEljNVgRfOUhY9fy2zTvfoqWsnebh8Sl70VScFbICvJnLKB0Og==", + "dev": true, + "license": "MIT" + }, + "node_modules/@jridgewell/trace-mapping": { + "version": "0.3.31", + "resolved": "https://registry.npmjs.org/@jridgewell/trace-mapping/-/trace-mapping-0.3.31.tgz", + "integrity": "sha512-zzNR+SdQSDJzc8joaeP8QQoCQr8NuYx2dIIytl1QeBEZHJ9uW6hebsrYgbz8hJwUQao3TWCMtmfV8Nu1twOLAw==", + "dev": true, + "license": "MIT", + "dependencies": { + "@jridgewell/resolve-uri": "^3.1.0", + "@jridgewell/sourcemap-codec": "^1.4.14" + } + }, + "node_modules/@napi-rs/wasm-runtime": { + "version": "0.2.12", + "resolved": "https://registry.npmjs.org/@napi-rs/wasm-runtime/-/wasm-runtime-0.2.12.tgz", + "integrity": "sha512-ZVWUcfwY4E/yPitQJl481FjFo3K22D6qF0DuFH6Y/nbnE11GY5uguDxZMGXPQ8WQ0128MXQD7TnfHyK4oWoIJQ==", + "dev": true, + "license": "MIT", + "optional": true, + "dependencies": { + "@emnapi/core": "^1.4.3", + "@emnapi/runtime": "^1.4.3", + "@tybys/wasm-util": "^0.10.0" + } + }, + "node_modules/@pkgjs/parseargs": { + "version": "0.11.0", + "resolved": "https://registry.npmjs.org/@pkgjs/parseargs/-/parseargs-0.11.0.tgz", + "integrity": "sha512-+1VkjdD0QBLPodGrJUeqarH8VAIvQODIbwh9XpP5Syisf7YoQgsJKPNFoqqLQlu+VQ/tVSshMR6loPMn8U+dPg==", + "dev": true, + "license": "MIT", + "optional": true, + "engines": { + "node": ">=14" + } + }, + "node_modules/@pkgr/core": { + "version": "0.2.9", + "resolved": "https://registry.npmjs.org/@pkgr/core/-/core-0.2.9.tgz", + "integrity": "sha512-QNqXyfVS2wm9hweSYD2O7F0G06uurj9kZ96TRQE5Y9hU7+tgdZwIkbAKc5Ocy1HxEY2kuDQa6cQ1WRs/O5LFKA==", + "dev": true, + "license": "MIT", + "engines": { + "node": "^12.20.0 || ^14.18.0 || >=16.0.0" + }, + "funding": { + "url": "https://opencollective.com/pkgr" + } + }, + "node_modules/@playwright/test": { + "version": "1.56.1", + "resolved": "https://registry.npmjs.org/@playwright/test/-/test-1.56.1.tgz", + "integrity": "sha512-vSMYtL/zOcFpvJCW71Q/OEGQb7KYBPAdKh35WNSkaZA75JlAO8ED8UN6GUNTm3drWomcbcqRPFqQbLae8yBTdg==", + "dev": true, + "license": "Apache-2.0", + "dependencies": { + "playwright": "1.56.1" + }, + "bin": { + "playwright": "cli.js" + }, + "engines": { + "node": ">=18" + } + }, + "node_modules/@sinclair/typebox": { + "version": "0.34.41", + "resolved": "https://registry.npmjs.org/@sinclair/typebox/-/typebox-0.34.41.tgz", + "integrity": "sha512-6gS8pZzSXdyRHTIqoqSVknxolr1kzfy4/CeDnrzsVz8TTIWUbOBr6gnzOmTYJ3eXQNh4IYHIGi5aIL7sOZ2G/g==", + "dev": true, + "license": "MIT" + }, + "node_modules/@sinonjs/commons": { + "version": "3.0.1", + "resolved": "https://registry.npmjs.org/@sinonjs/commons/-/commons-3.0.1.tgz", + "integrity": "sha512-K3mCHKQ9sVh8o1C9cxkwxaOmXoAMlDxC1mYyHrjqOWEcBjYr76t96zL2zlj5dUGZ3HSw240X1qgH3Mjf1yJWpQ==", + "dev": true, + "license": "BSD-3-Clause", + "dependencies": { + "type-detect": "4.0.8" + } + }, + "node_modules/@sinonjs/fake-timers": { + "version": "13.0.5", + "resolved": "https://registry.npmjs.org/@sinonjs/fake-timers/-/fake-timers-13.0.5.tgz", + "integrity": "sha512-36/hTbH2uaWuGVERyC6da9YwGWnzUZXuPro/F2LfsdOsLnCojz/iSH8MxUt/FD2S5XBSVPhmArFUXcpCQ2Hkiw==", + "dev": true, + "license": "BSD-3-Clause", + "dependencies": { + "@sinonjs/commons": "^3.0.1" + } + }, + "node_modules/@tybys/wasm-util": { + "version": "0.10.1", + "resolved": "https://registry.npmjs.org/@tybys/wasm-util/-/wasm-util-0.10.1.tgz", + "integrity": "sha512-9tTaPJLSiejZKx+Bmog4uSubteqTvFrVrURwkmHixBo0G4seD0zUxp98E1DzUBJxLQ3NPwXrGKDiVjwx/DpPsg==", + "dev": true, + "license": "MIT", + "optional": true, + "dependencies": { + "tslib": "^2.4.0" + } + }, + "node_modules/@types/babel__core": { + "version": "7.20.5", + "resolved": "https://registry.npmjs.org/@types/babel__core/-/babel__core-7.20.5.tgz", + "integrity": "sha512-qoQprZvz5wQFJwMDqeseRXWv3rqMvhgpbXFfVyWhbx9X47POIA6i/+dXefEmZKoAgOaTdaIgNSMqMIU61yRyzA==", + "dev": true, + "license": "MIT", + "dependencies": { + "@babel/parser": "^7.20.7", + "@babel/types": "^7.20.7", + "@types/babel__generator": "*", + "@types/babel__template": "*", + "@types/babel__traverse": "*" + } + }, + "node_modules/@types/babel__generator": { + "version": "7.27.0", + "resolved": "https://registry.npmjs.org/@types/babel__generator/-/babel__generator-7.27.0.tgz", + "integrity": "sha512-ufFd2Xi92OAVPYsy+P4n7/U7e68fex0+Ee8gSG9KX7eo084CWiQ4sdxktvdl0bOPupXtVJPY19zk6EwWqUQ8lg==", + "dev": true, + "license": "MIT", + "dependencies": { + "@babel/types": "^7.0.0" + } + }, + "node_modules/@types/babel__template": { + "version": "7.4.4", + "resolved": "https://registry.npmjs.org/@types/babel__template/-/babel__template-7.4.4.tgz", + "integrity": "sha512-h/NUaSyG5EyxBIp8YRxo4RMe2/qQgvyowRwVMzhYhBCONbW8PUsg4lkFMrhgZhUe5z3L3MiLDuvyJ/CaPa2A8A==", + "dev": true, + "license": "MIT", + "dependencies": { + "@babel/parser": "^7.1.0", + "@babel/types": "^7.0.0" + } + }, + "node_modules/@types/babel__traverse": { + "version": "7.28.0", + "resolved": "https://registry.npmjs.org/@types/babel__traverse/-/babel__traverse-7.28.0.tgz", + "integrity": "sha512-8PvcXf70gTDZBgt9ptxJ8elBeBjcLOAcOtoO/mPJjtji1+CdGbHgm77om1GrsPxsiE+uXIpNSK64UYaIwQXd4Q==", + "dev": true, + "license": "MIT", + "dependencies": { + "@babel/types": "^7.28.2" + } + }, + "node_modules/@types/istanbul-lib-coverage": { + "version": "2.0.6", + "resolved": "https://registry.npmjs.org/@types/istanbul-lib-coverage/-/istanbul-lib-coverage-2.0.6.tgz", + "integrity": "sha512-2QF/t/auWm0lsy8XtKVPG19v3sSOQlJe/YHZgfjb/KBBHOGSV+J2q/S671rcq9uTBrLAXmZpqJiaQbMT+zNU1w==", + "dev": true, + "license": "MIT" + }, + "node_modules/@types/istanbul-lib-report": { + "version": "3.0.3", + "resolved": "https://registry.npmjs.org/@types/istanbul-lib-report/-/istanbul-lib-report-3.0.3.tgz", + "integrity": "sha512-NQn7AHQnk/RSLOxrBbGyJM/aVQ+pjj5HCgasFxc0K/KhoATfQ/47AyUl15I2yBUpihjmas+a+VJBOqecrFH+uA==", + "dev": true, + "license": "MIT", + "dependencies": { + "@types/istanbul-lib-coverage": "*" + } + }, + "node_modules/@types/istanbul-reports": { + "version": "3.0.4", + "resolved": "https://registry.npmjs.org/@types/istanbul-reports/-/istanbul-reports-3.0.4.tgz", + "integrity": "sha512-pk2B1NWalF9toCRu6gjBzR69syFjP4Od8WRAX+0mmf9lAjCRicLOWc+ZrxZHx/0XRjotgkF9t6iaMJ+aXcOdZQ==", + "dev": true, + "license": "MIT", + "dependencies": { + "@types/istanbul-lib-report": "*" + } + }, + "node_modules/@types/jest": { + "version": "30.0.0", + "resolved": "https://registry.npmjs.org/@types/jest/-/jest-30.0.0.tgz", + "integrity": "sha512-XTYugzhuwqWjws0CVz8QpM36+T+Dz5mTEBKhNs/esGLnCIlGdRy+Dq78NRjd7ls7r8BC8ZRMOrKlkO1hU0JOwA==", + "dev": true, + "license": "MIT", + "dependencies": { + "expect": "^30.0.0", + "pretty-format": "^30.0.0" + } + }, + "node_modules/@types/node": { + "version": "24.10.0", + "resolved": "https://registry.npmjs.org/@types/node/-/node-24.10.0.tgz", + "integrity": "sha512-qzQZRBqkFsYyaSWXuEHc2WR9c0a0CXwiE5FWUvn7ZM+vdy1uZLfCunD38UzhuB7YN/J11ndbDBcTmOdxJo9Q7A==", + "dev": true, + "license": "MIT", + "dependencies": { + "undici-types": "~7.16.0" + } + }, + "node_modules/@types/stack-utils": { + "version": "2.0.3", + "resolved": "https://registry.npmjs.org/@types/stack-utils/-/stack-utils-2.0.3.tgz", + "integrity": "sha512-9aEbYZ3TbYMznPdcdr3SmIrLXwC/AKZXQeCf9Pgao5CKb8CyHuEX5jzWPTkvregvhRJHcpRO6BFoGW9ycaOkYw==", + "dev": true, + "license": "MIT" + }, + "node_modules/@types/yargs": { + "version": "17.0.34", + "resolved": "https://registry.npmjs.org/@types/yargs/-/yargs-17.0.34.tgz", + "integrity": "sha512-KExbHVa92aJpw9WDQvzBaGVE2/Pz+pLZQloT2hjL8IqsZnV62rlPOYvNnLmf/L2dyllfVUOVBj64M0z/46eR2A==", + "dev": true, + "license": "MIT", + "dependencies": { + "@types/yargs-parser": "*" + } + }, + "node_modules/@types/yargs-parser": { + "version": "21.0.3", + "resolved": "https://registry.npmjs.org/@types/yargs-parser/-/yargs-parser-21.0.3.tgz", + "integrity": "sha512-I4q9QU9MQv4oEOz4tAHJtNz1cwuLxn2F3xcc2iV5WdqLPpUnj30aUuxt1mAxYTG+oe8CZMV/+6rU4S4gRDzqtQ==", + "dev": true, + "license": "MIT" + }, + "node_modules/@ungap/structured-clone": { + "version": "1.3.0", + "resolved": "https://registry.npmjs.org/@ungap/structured-clone/-/structured-clone-1.3.0.tgz", + "integrity": "sha512-WmoN8qaIAo7WTYWbAZuG8PYEhn5fkz7dZrqTBZ7dtt//lL2Gwms1IcnQ5yHqjDfX8Ft5j4YzDM23f87zBfDe9g==", + "dev": true, + "license": "ISC" + }, + "node_modules/@unrs/resolver-binding-android-arm-eabi": { + "version": "1.11.1", + "resolved": "https://registry.npmjs.org/@unrs/resolver-binding-android-arm-eabi/-/resolver-binding-android-arm-eabi-1.11.1.tgz", + "integrity": "sha512-ppLRUgHVaGRWUx0R0Ut06Mjo9gBaBkg3v/8AxusGLhsIotbBLuRk51rAzqLC8gq6NyyAojEXglNjzf6R948DNw==", + "cpu": [ + "arm" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "android" + ] + }, + "node_modules/@unrs/resolver-binding-android-arm64": { + "version": "1.11.1", + "resolved": "https://registry.npmjs.org/@unrs/resolver-binding-android-arm64/-/resolver-binding-android-arm64-1.11.1.tgz", + "integrity": "sha512-lCxkVtb4wp1v+EoN+HjIG9cIIzPkX5OtM03pQYkG+U5O/wL53LC4QbIeazgiKqluGeVEeBlZahHalCaBvU1a2g==", + "cpu": [ + "arm64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "android" + ] + }, + "node_modules/@unrs/resolver-binding-darwin-arm64": { + "version": "1.11.1", + "resolved": "https://registry.npmjs.org/@unrs/resolver-binding-darwin-arm64/-/resolver-binding-darwin-arm64-1.11.1.tgz", + "integrity": "sha512-gPVA1UjRu1Y/IsB/dQEsp2V1pm44Of6+LWvbLc9SDk1c2KhhDRDBUkQCYVWe6f26uJb3fOK8saWMgtX8IrMk3g==", + "cpu": [ + "arm64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "darwin" + ] + }, + "node_modules/@unrs/resolver-binding-darwin-x64": { + "version": "1.11.1", + "resolved": "https://registry.npmjs.org/@unrs/resolver-binding-darwin-x64/-/resolver-binding-darwin-x64-1.11.1.tgz", + "integrity": "sha512-cFzP7rWKd3lZaCsDze07QX1SC24lO8mPty9vdP+YVa3MGdVgPmFc59317b2ioXtgCMKGiCLxJ4HQs62oz6GfRQ==", + "cpu": [ + "x64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "darwin" + ] + }, + "node_modules/@unrs/resolver-binding-freebsd-x64": { + "version": "1.11.1", + "resolved": "https://registry.npmjs.org/@unrs/resolver-binding-freebsd-x64/-/resolver-binding-freebsd-x64-1.11.1.tgz", + "integrity": "sha512-fqtGgak3zX4DCB6PFpsH5+Kmt/8CIi4Bry4rb1ho6Av2QHTREM+47y282Uqiu3ZRF5IQioJQ5qWRV6jduA+iGw==", + "cpu": [ + "x64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "freebsd" + ] + }, + "node_modules/@unrs/resolver-binding-linux-arm-gnueabihf": { + "version": "1.11.1", + "resolved": "https://registry.npmjs.org/@unrs/resolver-binding-linux-arm-gnueabihf/-/resolver-binding-linux-arm-gnueabihf-1.11.1.tgz", + "integrity": "sha512-u92mvlcYtp9MRKmP+ZvMmtPN34+/3lMHlyMj7wXJDeXxuM0Vgzz0+PPJNsro1m3IZPYChIkn944wW8TYgGKFHw==", + "cpu": [ + "arm" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "linux" + ] + }, + "node_modules/@unrs/resolver-binding-linux-arm-musleabihf": { + "version": "1.11.1", + "resolved": "https://registry.npmjs.org/@unrs/resolver-binding-linux-arm-musleabihf/-/resolver-binding-linux-arm-musleabihf-1.11.1.tgz", + "integrity": "sha512-cINaoY2z7LVCrfHkIcmvj7osTOtm6VVT16b5oQdS4beibX2SYBwgYLmqhBjA1t51CarSaBuX5YNsWLjsqfW5Cw==", + "cpu": [ + "arm" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "linux" + ] + }, + "node_modules/@unrs/resolver-binding-linux-arm64-gnu": { + "version": "1.11.1", + "resolved": "https://registry.npmjs.org/@unrs/resolver-binding-linux-arm64-gnu/-/resolver-binding-linux-arm64-gnu-1.11.1.tgz", + "integrity": "sha512-34gw7PjDGB9JgePJEmhEqBhWvCiiWCuXsL9hYphDF7crW7UgI05gyBAi6MF58uGcMOiOqSJ2ybEeCvHcq0BCmQ==", + "cpu": [ + "arm64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "linux" + ] + }, + "node_modules/@unrs/resolver-binding-linux-arm64-musl": { + "version": "1.11.1", + "resolved": "https://registry.npmjs.org/@unrs/resolver-binding-linux-arm64-musl/-/resolver-binding-linux-arm64-musl-1.11.1.tgz", + "integrity": "sha512-RyMIx6Uf53hhOtJDIamSbTskA99sPHS96wxVE/bJtePJJtpdKGXO1wY90oRdXuYOGOTuqjT8ACccMc4K6QmT3w==", + "cpu": [ + "arm64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "linux" + ] + }, + "node_modules/@unrs/resolver-binding-linux-ppc64-gnu": { + "version": "1.11.1", + "resolved": "https://registry.npmjs.org/@unrs/resolver-binding-linux-ppc64-gnu/-/resolver-binding-linux-ppc64-gnu-1.11.1.tgz", + "integrity": "sha512-D8Vae74A4/a+mZH0FbOkFJL9DSK2R6TFPC9M+jCWYia/q2einCubX10pecpDiTmkJVUH+y8K3BZClycD8nCShA==", + "cpu": [ + "ppc64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "linux" + ] + }, + "node_modules/@unrs/resolver-binding-linux-riscv64-gnu": { + "version": "1.11.1", + "resolved": "https://registry.npmjs.org/@unrs/resolver-binding-linux-riscv64-gnu/-/resolver-binding-linux-riscv64-gnu-1.11.1.tgz", + "integrity": "sha512-frxL4OrzOWVVsOc96+V3aqTIQl1O2TjgExV4EKgRY09AJ9leZpEg8Ak9phadbuX0BA4k8U5qtvMSQQGGmaJqcQ==", + "cpu": [ + "riscv64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "linux" + ] + }, + "node_modules/@unrs/resolver-binding-linux-riscv64-musl": { + "version": "1.11.1", + "resolved": "https://registry.npmjs.org/@unrs/resolver-binding-linux-riscv64-musl/-/resolver-binding-linux-riscv64-musl-1.11.1.tgz", + "integrity": "sha512-mJ5vuDaIZ+l/acv01sHoXfpnyrNKOk/3aDoEdLO/Xtn9HuZlDD6jKxHlkN8ZhWyLJsRBxfv9GYM2utQ1SChKew==", + "cpu": [ + "riscv64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "linux" + ] + }, + "node_modules/@unrs/resolver-binding-linux-s390x-gnu": { + "version": "1.11.1", + "resolved": "https://registry.npmjs.org/@unrs/resolver-binding-linux-s390x-gnu/-/resolver-binding-linux-s390x-gnu-1.11.1.tgz", + "integrity": "sha512-kELo8ebBVtb9sA7rMe1Cph4QHreByhaZ2QEADd9NzIQsYNQpt9UkM9iqr2lhGr5afh885d/cB5QeTXSbZHTYPg==", + "cpu": [ + "s390x" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "linux" + ] + }, + "node_modules/@unrs/resolver-binding-linux-x64-gnu": { + "version": "1.11.1", + "resolved": "https://registry.npmjs.org/@unrs/resolver-binding-linux-x64-gnu/-/resolver-binding-linux-x64-gnu-1.11.1.tgz", + "integrity": "sha512-C3ZAHugKgovV5YvAMsxhq0gtXuwESUKc5MhEtjBpLoHPLYM+iuwSj3lflFwK3DPm68660rZ7G8BMcwSro7hD5w==", + "cpu": [ + "x64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "linux" + ] + }, + "node_modules/@unrs/resolver-binding-linux-x64-musl": { + "version": "1.11.1", + "resolved": "https://registry.npmjs.org/@unrs/resolver-binding-linux-x64-musl/-/resolver-binding-linux-x64-musl-1.11.1.tgz", + "integrity": "sha512-rV0YSoyhK2nZ4vEswT/QwqzqQXw5I6CjoaYMOX0TqBlWhojUf8P94mvI7nuJTeaCkkds3QE4+zS8Ko+GdXuZtA==", + "cpu": [ + "x64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "linux" + ] + }, + "node_modules/@unrs/resolver-binding-wasm32-wasi": { + "version": "1.11.1", + "resolved": "https://registry.npmjs.org/@unrs/resolver-binding-wasm32-wasi/-/resolver-binding-wasm32-wasi-1.11.1.tgz", + "integrity": "sha512-5u4RkfxJm+Ng7IWgkzi3qrFOvLvQYnPBmjmZQ8+szTK/b31fQCnleNl1GgEt7nIsZRIf5PLhPwT0WM+q45x/UQ==", + "cpu": [ + "wasm32" + ], + "dev": true, + "license": "MIT", + "optional": true, + "dependencies": { + "@napi-rs/wasm-runtime": "^0.2.11" + }, + "engines": { + "node": ">=14.0.0" + } + }, + "node_modules/@unrs/resolver-binding-win32-arm64-msvc": { + "version": "1.11.1", + "resolved": "https://registry.npmjs.org/@unrs/resolver-binding-win32-arm64-msvc/-/resolver-binding-win32-arm64-msvc-1.11.1.tgz", + "integrity": "sha512-nRcz5Il4ln0kMhfL8S3hLkxI85BXs3o8EYoattsJNdsX4YUU89iOkVn7g0VHSRxFuVMdM4Q1jEpIId1Ihim/Uw==", + "cpu": [ + "arm64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "win32" + ] + }, + "node_modules/@unrs/resolver-binding-win32-ia32-msvc": { + "version": "1.11.1", + "resolved": "https://registry.npmjs.org/@unrs/resolver-binding-win32-ia32-msvc/-/resolver-binding-win32-ia32-msvc-1.11.1.tgz", + "integrity": "sha512-DCEI6t5i1NmAZp6pFonpD5m7i6aFrpofcp4LA2i8IIq60Jyo28hamKBxNrZcyOwVOZkgsRp9O2sXWBWP8MnvIQ==", + "cpu": [ + "ia32" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "win32" + ] + }, + "node_modules/@unrs/resolver-binding-win32-x64-msvc": { + "version": "1.11.1", + "resolved": "https://registry.npmjs.org/@unrs/resolver-binding-win32-x64-msvc/-/resolver-binding-win32-x64-msvc-1.11.1.tgz", + "integrity": "sha512-lrW200hZdbfRtztbygyaq/6jP6AKE8qQN2KvPcJ+x7wiD038YtnYtZ82IMNJ69GJibV7bwL3y9FgK+5w/pYt6g==", + "cpu": [ + "x64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "win32" + ] + }, + "node_modules/ansi-escapes": { + "version": "4.3.2", + "resolved": "https://registry.npmjs.org/ansi-escapes/-/ansi-escapes-4.3.2.tgz", + "integrity": "sha512-gKXj5ALrKWQLsYG9jlTRmR/xKluxHV+Z9QEwNIgCfM1/uwPMCuzVVnh5mwTd+OuBZcwSIMbqssNWRm1lE51QaQ==", + "dev": true, + "license": "MIT", + "dependencies": { + "type-fest": "^0.21.3" + }, + "engines": { + "node": ">=8" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/ansi-regex": { + "version": "6.2.2", + "resolved": "https://registry.npmjs.org/ansi-regex/-/ansi-regex-6.2.2.tgz", + "integrity": "sha512-Bq3SmSpyFHaWjPk8If9yc6svM8c56dB5BAtW4Qbw5jHTwwXXcTLoRMkpDJp6VL0XzlWaCHTXrkFURMYmD0sLqg==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=12" + }, + "funding": { + "url": "https://github.com/chalk/ansi-regex?sponsor=1" + } + }, + "node_modules/ansi-styles": { + "version": "4.3.0", + "resolved": "https://registry.npmjs.org/ansi-styles/-/ansi-styles-4.3.0.tgz", + "integrity": "sha512-zbB9rCJAT1rbjiVDb2hqKFHNYLxgtk8NURxZ3IZwD3F6NtxbXZQCnnSi1Lkx+IDohdPlFp222wVALIheZJQSEg==", + "dev": true, + "license": "MIT", + "dependencies": { + "color-convert": "^2.0.1" + }, + "engines": { + "node": ">=8" + }, + "funding": { + "url": "https://github.com/chalk/ansi-styles?sponsor=1" + } + }, + "node_modules/anymatch": { + "version": "3.1.3", + "resolved": "https://registry.npmjs.org/anymatch/-/anymatch-3.1.3.tgz", + "integrity": "sha512-KMReFUr0B4t+D+OBkjR3KYqvocp2XaSzO55UcB6mgQMd3KbcE+mWTyvVV7D/zsdEbNnV6acZUutkiHQXvTr1Rw==", + "dev": true, + "license": "ISC", + "dependencies": { + "normalize-path": "^3.0.0", + "picomatch": "^2.0.4" + }, + "engines": { + "node": ">= 8" + } + }, + "node_modules/argparse": { + "version": "1.0.10", + "resolved": "https://registry.npmjs.org/argparse/-/argparse-1.0.10.tgz", + "integrity": "sha512-o5Roy6tNG4SL/FOkCAN6RzjiakZS25RLYFrcMttJqbdd8BWrnA+fGz57iN5Pb06pvBGvl5gQ0B48dJlslXvoTg==", + "dev": true, + "license": "MIT", + "dependencies": { + "sprintf-js": "~1.0.2" + } + }, + "node_modules/babel-jest": { + "version": "30.2.0", + "resolved": "https://registry.npmjs.org/babel-jest/-/babel-jest-30.2.0.tgz", + "integrity": "sha512-0YiBEOxWqKkSQWL9nNGGEgndoeL0ZpWrbLMNL5u/Kaxrli3Eaxlt3ZtIDktEvXt4L/R9r3ODr2zKwGM/2BjxVw==", + "dev": true, + "license": "MIT", + "dependencies": { + "@jest/transform": "30.2.0", + "@types/babel__core": "^7.20.5", + "babel-plugin-istanbul": "^7.0.1", + "babel-preset-jest": "30.2.0", + "chalk": "^4.1.2", + "graceful-fs": "^4.2.11", + "slash": "^3.0.0" + }, + "engines": { + "node": "^18.14.0 || ^20.0.0 || ^22.0.0 || >=24.0.0" + }, + "peerDependencies": { + "@babel/core": "^7.11.0 || ^8.0.0-0" + } + }, + "node_modules/babel-plugin-istanbul": { + "version": "7.0.1", + "resolved": "https://registry.npmjs.org/babel-plugin-istanbul/-/babel-plugin-istanbul-7.0.1.tgz", + "integrity": "sha512-D8Z6Qm8jCvVXtIRkBnqNHX0zJ37rQcFJ9u8WOS6tkYOsRdHBzypCstaxWiu5ZIlqQtviRYbgnRLSoCEvjqcqbA==", + "dev": true, + "license": "BSD-3-Clause", + "workspaces": [ + "test/babel-8" + ], + "dependencies": { + "@babel/helper-plugin-utils": "^7.0.0", + "@istanbuljs/load-nyc-config": "^1.0.0", + "@istanbuljs/schema": "^0.1.3", + "istanbul-lib-instrument": "^6.0.2", + "test-exclude": "^6.0.0" + }, + "engines": { + "node": ">=12" + } + }, + "node_modules/babel-plugin-jest-hoist": { + "version": "30.2.0", + "resolved": "https://registry.npmjs.org/babel-plugin-jest-hoist/-/babel-plugin-jest-hoist-30.2.0.tgz", + "integrity": "sha512-ftzhzSGMUnOzcCXd6WHdBGMyuwy15Wnn0iyyWGKgBDLxf9/s5ABuraCSpBX2uG0jUg4rqJnxsLc5+oYBqoxVaA==", + "dev": true, + "license": "MIT", + "dependencies": { + "@types/babel__core": "^7.20.5" + }, + "engines": { + "node": "^18.14.0 || ^20.0.0 || ^22.0.0 || >=24.0.0" + } + }, + "node_modules/babel-plugin-polyfill-corejs2": { + "version": "0.4.14", + "resolved": "https://registry.npmjs.org/babel-plugin-polyfill-corejs2/-/babel-plugin-polyfill-corejs2-0.4.14.tgz", + "integrity": "sha512-Co2Y9wX854ts6U8gAAPXfn0GmAyctHuK8n0Yhfjd6t30g7yvKjspvvOo9yG+z52PZRgFErt7Ka2pYnXCjLKEpg==", + "dev": true, + "license": "MIT", + "dependencies": { + "@babel/compat-data": "^7.27.7", + "@babel/helper-define-polyfill-provider": "^0.6.5", + "semver": "^6.3.1" + }, + "peerDependencies": { + "@babel/core": "^7.4.0 || ^8.0.0-0 <8.0.0" + } + }, + "node_modules/babel-plugin-polyfill-corejs3": { + "version": "0.13.0", + "resolved": "https://registry.npmjs.org/babel-plugin-polyfill-corejs3/-/babel-plugin-polyfill-corejs3-0.13.0.tgz", + "integrity": "sha512-U+GNwMdSFgzVmfhNm8GJUX88AadB3uo9KpJqS3FaqNIPKgySuvMb+bHPsOmmuWyIcuqZj/pzt1RUIUZns4y2+A==", + "dev": true, + "license": "MIT", + "dependencies": { + "@babel/helper-define-polyfill-provider": "^0.6.5", + "core-js-compat": "^3.43.0" + }, + "peerDependencies": { + "@babel/core": "^7.4.0 || ^8.0.0-0 <8.0.0" + } + }, + "node_modules/babel-plugin-polyfill-regenerator": { + "version": "0.6.5", + "resolved": "https://registry.npmjs.org/babel-plugin-polyfill-regenerator/-/babel-plugin-polyfill-regenerator-0.6.5.tgz", + "integrity": "sha512-ISqQ2frbiNU9vIJkzg7dlPpznPZ4jOiUQ1uSmB0fEHeowtN3COYRsXr/xexn64NpU13P06jc/L5TgiJXOgrbEg==", + "dev": true, + "license": "MIT", + "dependencies": { + "@babel/helper-define-polyfill-provider": "^0.6.5" + }, + "peerDependencies": { + "@babel/core": "^7.4.0 || ^8.0.0-0 <8.0.0" + } + }, + "node_modules/babel-preset-current-node-syntax": { + "version": "1.2.0", + "resolved": "https://registry.npmjs.org/babel-preset-current-node-syntax/-/babel-preset-current-node-syntax-1.2.0.tgz", + "integrity": "sha512-E/VlAEzRrsLEb2+dv8yp3bo4scof3l9nR4lrld+Iy5NyVqgVYUJnDAmunkhPMisRI32Qc4iRiz425d8vM++2fg==", + "dev": true, + "license": "MIT", + "dependencies": { + "@babel/plugin-syntax-async-generators": "^7.8.4", + "@babel/plugin-syntax-bigint": "^7.8.3", + "@babel/plugin-syntax-class-properties": "^7.12.13", + "@babel/plugin-syntax-class-static-block": "^7.14.5", + "@babel/plugin-syntax-import-attributes": "^7.24.7", + "@babel/plugin-syntax-import-meta": "^7.10.4", + "@babel/plugin-syntax-json-strings": "^7.8.3", + "@babel/plugin-syntax-logical-assignment-operators": "^7.10.4", + "@babel/plugin-syntax-nullish-coalescing-operator": "^7.8.3", + "@babel/plugin-syntax-numeric-separator": "^7.10.4", + "@babel/plugin-syntax-object-rest-spread": "^7.8.3", + "@babel/plugin-syntax-optional-catch-binding": "^7.8.3", + "@babel/plugin-syntax-optional-chaining": "^7.8.3", + "@babel/plugin-syntax-private-property-in-object": "^7.14.5", + "@babel/plugin-syntax-top-level-await": "^7.14.5" + }, + "peerDependencies": { + "@babel/core": "^7.0.0 || ^8.0.0-0" + } + }, + "node_modules/babel-preset-jest": { + "version": "30.2.0", + "resolved": "https://registry.npmjs.org/babel-preset-jest/-/babel-preset-jest-30.2.0.tgz", + "integrity": "sha512-US4Z3NOieAQumwFnYdUWKvUKh8+YSnS/gB3t6YBiz0bskpu7Pine8pPCheNxlPEW4wnUkma2a94YuW2q3guvCQ==", + "dev": true, + "license": "MIT", + "dependencies": { + "babel-plugin-jest-hoist": "30.2.0", + "babel-preset-current-node-syntax": "^1.2.0" + }, + "engines": { + "node": "^18.14.0 || ^20.0.0 || ^22.0.0 || >=24.0.0" + }, + "peerDependencies": { + "@babel/core": "^7.11.0 || ^8.0.0-beta.1" + } + }, + "node_modules/balanced-match": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/balanced-match/-/balanced-match-1.0.2.tgz", + "integrity": "sha512-3oSeUO0TMV67hN1AmbXsK4yaqU7tjiHlbxRDZOpH0KW9+CeX4bRAaX0Anxt0tx2MrpRpWwQaPwIlISEJhYU5Pw==", + "dev": true, + "license": "MIT" + }, + "node_modules/baseline-browser-mapping": { + "version": "2.8.26", + "resolved": "https://registry.npmjs.org/baseline-browser-mapping/-/baseline-browser-mapping-2.8.26.tgz", + "integrity": "sha512-73lC1ugzwoaWCLJ1LvOgrR5xsMLTqSKIEoMHVtL9E/HNk0PXtTM76ZIm84856/SF7Nv8mPZxKoBsgpm0tR1u1Q==", + "dev": true, + "license": "Apache-2.0", + "bin": { + "baseline-browser-mapping": "dist/cli.js" + } + }, + "node_modules/brace-expansion": { + "version": "2.0.2", + "resolved": "https://registry.npmjs.org/brace-expansion/-/brace-expansion-2.0.2.tgz", + "integrity": "sha512-Jt0vHyM+jmUBqojB7E1NIYadt0vI0Qxjxd2TErW94wDz+E2LAm5vKMXXwg6ZZBTHPuUlDgQHKXvjGBdfcF1ZDQ==", + "dev": true, + "license": "MIT", + "dependencies": { + "balanced-match": "^1.0.0" + } + }, + "node_modules/braces": { + "version": "3.0.3", + "resolved": "https://registry.npmjs.org/braces/-/braces-3.0.3.tgz", + "integrity": "sha512-yQbXgO/OSZVD2IsiLlro+7Hf6Q18EJrKSEsdoMzKePKXct3gvD8oLcOQdIzGupr5Fj+EDe8gO/lxc1BzfMpxvA==", + "dev": true, + "license": "MIT", + "dependencies": { + "fill-range": "^7.1.1" + }, + "engines": { + "node": ">=8" + } + }, + "node_modules/browserslist": { + "version": "4.28.0", + "resolved": "https://registry.npmjs.org/browserslist/-/browserslist-4.28.0.tgz", + "integrity": "sha512-tbydkR/CxfMwelN0vwdP/pLkDwyAASZ+VfWm4EOwlB6SWhx1sYnWLqo8N5j0rAzPfzfRaxt0mM/4wPU/Su84RQ==", + "dev": true, + "funding": [ + { + "type": "opencollective", + "url": "https://opencollective.com/browserslist" + }, + { + "type": "tidelift", + "url": "https://tidelift.com/funding/github/npm/browserslist" + }, + { + "type": "github", + "url": "https://github.com/sponsors/ai" + } + ], + "license": "MIT", + "dependencies": { + "baseline-browser-mapping": "^2.8.25", + "caniuse-lite": "^1.0.30001754", + "electron-to-chromium": "^1.5.249", + "node-releases": "^2.0.27", + "update-browserslist-db": "^1.1.4" + }, + "bin": { + "browserslist": "cli.js" + }, + "engines": { + "node": "^6 || ^7 || ^8 || ^9 || ^10 || ^11 || ^12 || >=13.7" + } + }, + "node_modules/bser": { + "version": "2.1.1", + "resolved": "https://registry.npmjs.org/bser/-/bser-2.1.1.tgz", + "integrity": "sha512-gQxTNE/GAfIIrmHLUE3oJyp5FO6HRBfhjnw4/wMmA63ZGDJnWBmgY/lyQBpnDUkGmAhbSe39tx2d/iTOAfglwQ==", + "dev": true, + "license": "Apache-2.0", + "dependencies": { + "node-int64": "^0.4.0" + } + }, + "node_modules/buffer-from": { + "version": "1.1.2", + "resolved": "https://registry.npmjs.org/buffer-from/-/buffer-from-1.1.2.tgz", + "integrity": "sha512-E+XQCRwSbaaiChtv6k6Dwgc+bx+Bs6vuKJHHl5kox/BaKbhiXzqQOwK4cO22yElGp2OCmjwVhT3HmxgyPGnJfQ==", + "dev": true, + "license": "MIT" + }, + "node_modules/callsites": { + "version": "3.1.0", + "resolved": "https://registry.npmjs.org/callsites/-/callsites-3.1.0.tgz", + "integrity": "sha512-P8BjAsXvZS+VIDUI11hHCQEv74YT67YUi5JJFNWIqL235sBmjX4+qx9Muvls5ivyNENctx46xQLQ3aTuE7ssaQ==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=6" + } + }, + "node_modules/camelcase": { + "version": "5.3.1", + "resolved": "https://registry.npmjs.org/camelcase/-/camelcase-5.3.1.tgz", + "integrity": "sha512-L28STB170nwWS63UjtlEOE3dldQApaJXZkOI1uMFfzf3rRuPegHaHesyee+YxQ+W6SvRDQV6UrdOdRiR153wJg==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=6" + } + }, + "node_modules/caniuse-lite": { + "version": "1.0.30001754", + "resolved": "https://registry.npmjs.org/caniuse-lite/-/caniuse-lite-1.0.30001754.tgz", + "integrity": "sha512-x6OeBXueoAceOmotzx3PO4Zpt4rzpeIFsSr6AAePTZxSkXiYDUmpypEl7e2+8NCd9bD7bXjqyef8CJYPC1jfxg==", + "dev": true, + "funding": [ + { + "type": "opencollective", + "url": "https://opencollective.com/browserslist" + }, + { + "type": "tidelift", + "url": "https://tidelift.com/funding/github/npm/caniuse-lite" + }, + { + "type": "github", + "url": "https://github.com/sponsors/ai" + } + ], + "license": "CC-BY-4.0" + }, + "node_modules/chalk": { + "version": "4.1.2", + "resolved": "https://registry.npmjs.org/chalk/-/chalk-4.1.2.tgz", + "integrity": "sha512-oKnbhFyRIXpUuez8iBMmyEa4nbj4IOQyuhc/wy9kY7/WVPcwIO9VA668Pu8RkO7+0G76SLROeyw9CpQ061i4mA==", + "dev": true, + "license": "MIT", + "dependencies": { + "ansi-styles": "^4.1.0", + "supports-color": "^7.1.0" + }, + "engines": { + "node": ">=10" + }, + "funding": { + "url": "https://github.com/chalk/chalk?sponsor=1" + } + }, + "node_modules/char-regex": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/char-regex/-/char-regex-1.0.2.tgz", + "integrity": "sha512-kWWXztvZ5SBQV+eRgKFeh8q5sLuZY2+8WUIzlxWVTg+oGwY14qylx1KbKzHd8P6ZYkAg0xyIDU9JMHhyJMZ1jw==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=10" + } + }, + "node_modules/ci-info": { + "version": "4.3.1", + "resolved": "https://registry.npmjs.org/ci-info/-/ci-info-4.3.1.tgz", + "integrity": "sha512-Wdy2Igu8OcBpI2pZePZ5oWjPC38tmDVx5WKUXKwlLYkA0ozo85sLsLvkBbBn/sZaSCMFOGZJ14fvW9t5/d7kdA==", + "dev": true, + "funding": [ + { + "type": "github", + "url": "https://github.com/sponsors/sibiraj-s" + } + ], + "license": "MIT", + "engines": { + "node": ">=8" + } + }, + "node_modules/cjs-module-lexer": { + "version": "2.1.1", + "resolved": "https://registry.npmjs.org/cjs-module-lexer/-/cjs-module-lexer-2.1.1.tgz", + "integrity": "sha512-+CmxIZ/L2vNcEfvNtLdU0ZQ6mbq3FZnwAP2PPTiKP+1QOoKwlKlPgb8UKV0Dds7QVaMnHm+FwSft2VB0s/SLjQ==", + "dev": true, + "license": "MIT" + }, + "node_modules/cliui": { + "version": "8.0.1", + "resolved": "https://registry.npmjs.org/cliui/-/cliui-8.0.1.tgz", + "integrity": "sha512-BSeNnyus75C4//NQ9gQt1/csTXyo/8Sb+afLAkzAptFuMsod9HFokGNudZpi/oQV73hnVK+sR+5PVRMd+Dr7YQ==", + "dev": true, + "license": "ISC", + "dependencies": { + "string-width": "^4.2.0", + "strip-ansi": "^6.0.1", + "wrap-ansi": "^7.0.0" + }, + "engines": { + "node": ">=12" + } + }, + "node_modules/cliui/node_modules/ansi-regex": { + "version": "5.0.1", + "resolved": "https://registry.npmjs.org/ansi-regex/-/ansi-regex-5.0.1.tgz", + "integrity": "sha512-quJQXlTSUGL2LH9SUXo8VwsY4soanhgo6LNSm84E1LBcE8s3O0wpdiRzyR9z/ZZJMlMWv37qOOb9pdJlMUEKFQ==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=8" + } + }, + "node_modules/cliui/node_modules/emoji-regex": { + "version": "8.0.0", + "resolved": "https://registry.npmjs.org/emoji-regex/-/emoji-regex-8.0.0.tgz", + "integrity": "sha512-MSjYzcWNOA0ewAHpz0MxpYFvwg6yjy1NG3xteoqz644VCo/RPgnr1/GGt+ic3iJTzQ8Eu3TdM14SawnVUmGE6A==", + "dev": true, + "license": "MIT" + }, + "node_modules/cliui/node_modules/string-width": { + "version": "4.2.3", + "resolved": "https://registry.npmjs.org/string-width/-/string-width-4.2.3.tgz", + "integrity": "sha512-wKyQRQpjJ0sIp62ErSZdGsjMJWsap5oRNihHhu6G7JVO/9jIB6UyevL+tXuOqrng8j/cxKTWyWUwvSTriiZz/g==", + "dev": true, + "license": "MIT", + "dependencies": { + "emoji-regex": "^8.0.0", + "is-fullwidth-code-point": "^3.0.0", + "strip-ansi": "^6.0.1" + }, + "engines": { + "node": ">=8" + } + }, + "node_modules/cliui/node_modules/strip-ansi": { + "version": "6.0.1", + "resolved": "https://registry.npmjs.org/strip-ansi/-/strip-ansi-6.0.1.tgz", + "integrity": "sha512-Y38VPSHcqkFrCpFnQ9vuSXmquuv5oXOKpGeT6aGrr3o3Gc9AlVa6JBfUSOCnbxGGZF+/0ooI7KrPuUSztUdU5A==", + "dev": true, + "license": "MIT", + "dependencies": { + "ansi-regex": "^5.0.1" + }, + "engines": { + "node": ">=8" + } + }, + "node_modules/cliui/node_modules/wrap-ansi": { + "version": "7.0.0", + "resolved": "https://registry.npmjs.org/wrap-ansi/-/wrap-ansi-7.0.0.tgz", + "integrity": "sha512-YVGIj2kamLSTxw6NsZjoBxfSwsn0ycdesmc4p+Q21c5zPuZ1pl+NfxVdxPtdHvmNVOQ6XSYG4AUtyt/Fi7D16Q==", + "dev": true, + "license": "MIT", + "dependencies": { + "ansi-styles": "^4.0.0", + "string-width": "^4.1.0", + "strip-ansi": "^6.0.0" + }, + "engines": { + "node": ">=10" + }, + "funding": { + "url": "https://github.com/chalk/wrap-ansi?sponsor=1" + } + }, + "node_modules/co": { + "version": "4.6.0", + "resolved": "https://registry.npmjs.org/co/-/co-4.6.0.tgz", + "integrity": "sha512-QVb0dM5HvG+uaxitm8wONl7jltx8dqhfU33DcqtOZcLSVIKSDDLDi7+0LbAKiyI8hD9u42m2YxXSkMGWThaecQ==", + "dev": true, + "license": "MIT", + "engines": { + "iojs": ">= 1.0.0", + "node": ">= 0.12.0" + } + }, + "node_modules/collect-v8-coverage": { + "version": "1.0.3", + "resolved": "https://registry.npmjs.org/collect-v8-coverage/-/collect-v8-coverage-1.0.3.tgz", + "integrity": "sha512-1L5aqIkwPfiodaMgQunkF1zRhNqifHBmtbbbxcr6yVxxBnliw4TDOW6NxpO8DJLgJ16OT+Y4ztZqP6p/FtXnAw==", + "dev": true, + "license": "MIT" + }, + "node_modules/color-convert": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/color-convert/-/color-convert-2.0.1.tgz", + "integrity": "sha512-RRECPsj7iu/xb5oKYcsFHSppFNnsj/52OVTRKb4zP5onXwVF3zVmmToNcOfGC+CRDpfK/U584fMg38ZHCaElKQ==", + "dev": true, + "license": "MIT", + "dependencies": { + "color-name": "~1.1.4" + }, + "engines": { + "node": ">=7.0.0" + } + }, + "node_modules/color-name": { + "version": "1.1.4", + "resolved": "https://registry.npmjs.org/color-name/-/color-name-1.1.4.tgz", + "integrity": "sha512-dOy+3AuW3a2wNbZHIuMZpTcgjGuLU/uBL/ubcZF9OXbDo8ff4O8yVp5Bf0efS8uEoYo5q4Fx7dY9OgQGXgAsQA==", + "dev": true, + "license": "MIT" + }, + "node_modules/concat-map": { + "version": "0.0.1", + "resolved": "https://registry.npmjs.org/concat-map/-/concat-map-0.0.1.tgz", + "integrity": "sha512-/Srv4dswyQNBfohGpz9o6Yb3Gz3SrUDqBH5rTuhGR7ahtlbYKnVxw2bCFMRljaA7EXHaXZ8wsHdodFvbkhKmqg==", + "dev": true, + "license": "MIT" + }, + "node_modules/convert-source-map": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/convert-source-map/-/convert-source-map-2.0.0.tgz", + "integrity": "sha512-Kvp459HrV2FEJ1CAsi1Ku+MY3kasH19TFykTz2xWmMeq6bk2NU3XXvfJ+Q61m0xktWwt+1HSYf3JZsTms3aRJg==", + "dev": true, + "license": "MIT" + }, + "node_modules/core-js-compat": { + "version": "3.46.0", + "resolved": "https://registry.npmjs.org/core-js-compat/-/core-js-compat-3.46.0.tgz", + "integrity": "sha512-p9hObIIEENxSV8xIu+V68JjSeARg6UVMG5mR+JEUguG3sI6MsiS1njz2jHmyJDvA+8jX/sytkBHup6kxhM9law==", + "dev": true, + "license": "MIT", + "dependencies": { + "browserslist": "^4.26.3" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/core-js" + } + }, + "node_modules/cross-spawn": { + "version": "7.0.6", + "resolved": "https://registry.npmjs.org/cross-spawn/-/cross-spawn-7.0.6.tgz", + "integrity": "sha512-uV2QOWP2nWzsy2aMp8aRibhi9dlzF5Hgh5SHaB9OiTGEyDTiJJyx0uy51QXdyWbtAHNua4XJzUKca3OzKUd3vA==", + "dev": true, + "license": "MIT", + "dependencies": { + "path-key": "^3.1.0", + "shebang-command": "^2.0.0", + "which": "^2.0.1" + }, + "engines": { + "node": ">= 8" + } + }, + "node_modules/debug": { + "version": "4.4.3", + "resolved": "https://registry.npmjs.org/debug/-/debug-4.4.3.tgz", + "integrity": "sha512-RGwwWnwQvkVfavKVt22FGLw+xYSdzARwm0ru6DhTVA3umU5hZc28V3kO4stgYryrTlLpuvgI9GiijltAjNbcqA==", + "dev": true, + "license": "MIT", + "dependencies": { + "ms": "^2.1.3" + }, + "engines": { + "node": ">=6.0" + }, + "peerDependenciesMeta": { + "supports-color": { + "optional": true + } + } + }, + "node_modules/dedent": { + "version": "1.7.0", + "resolved": "https://registry.npmjs.org/dedent/-/dedent-1.7.0.tgz", + "integrity": "sha512-HGFtf8yhuhGhqO07SV79tRp+br4MnbdjeVxotpn1QBl30pcLLCQjX5b2295ll0fv8RKDKsmWYrl05usHM9CewQ==", + "dev": true, + "license": "MIT", + "peerDependencies": { + "babel-plugin-macros": "^3.1.0" + }, + "peerDependenciesMeta": { + "babel-plugin-macros": { + "optional": true + } + } + }, + "node_modules/deepmerge": { + "version": "4.3.1", + "resolved": "https://registry.npmjs.org/deepmerge/-/deepmerge-4.3.1.tgz", + "integrity": "sha512-3sUqbMEc77XqpdNO7FRyRog+eW3ph+GYCbj+rK+uYyRMuwsVy0rMiVtPn+QJlKFvWP/1PYpapqYn0Me2knFn+A==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/detect-newline": { + "version": "3.1.0", + "resolved": "https://registry.npmjs.org/detect-newline/-/detect-newline-3.1.0.tgz", + "integrity": "sha512-TLz+x/vEXm/Y7P7wn1EJFNLxYpUD4TgMosxY6fAVJUnJMbupHBOncxyWUG9OpTaH9EBD7uFI5LfEgmMOc54DsA==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=8" + } + }, + "node_modules/eastasianwidth": { + "version": "0.2.0", + "resolved": "https://registry.npmjs.org/eastasianwidth/-/eastasianwidth-0.2.0.tgz", + "integrity": "sha512-I88TYZWc9XiYHRQ4/3c5rjjfgkjhLyW2luGIheGERbNQ6OY7yTybanSpDXZa8y7VUP9YmDcYa+eyq4ca7iLqWA==", + "dev": true, + "license": "MIT" + }, + "node_modules/electron-to-chromium": { + "version": "1.5.250", + "resolved": "https://registry.npmjs.org/electron-to-chromium/-/electron-to-chromium-1.5.250.tgz", + "integrity": "sha512-/5UMj9IiGDMOFBnN4i7/Ry5onJrAGSbOGo3s9FEKmwobGq6xw832ccET0CE3CkkMBZ8GJSlUIesZofpyurqDXw==", + "dev": true, + "license": "ISC" + }, + "node_modules/emittery": { + "version": "0.13.1", + "resolved": "https://registry.npmjs.org/emittery/-/emittery-0.13.1.tgz", + "integrity": "sha512-DeWwawk6r5yR9jFgnDKYt4sLS0LmHJJi3ZOnb5/JdbYwj3nW+FxQnHIjhBKz8YLC7oRNPVM9NQ47I3CVx34eqQ==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=12" + }, + "funding": { + "url": "https://github.com/sindresorhus/emittery?sponsor=1" + } + }, + "node_modules/emoji-regex": { + "version": "9.2.2", + "resolved": "https://registry.npmjs.org/emoji-regex/-/emoji-regex-9.2.2.tgz", + "integrity": "sha512-L18DaJsXSUk2+42pv8mLs5jJT2hqFkFE4j21wOmgbUqsZ2hL72NsUU785g9RXgo3s0ZNgVl42TiHp3ZtOv/Vyg==", + "dev": true, + "license": "MIT" + }, + "node_modules/error-ex": { + "version": "1.3.4", + "resolved": "https://registry.npmjs.org/error-ex/-/error-ex-1.3.4.tgz", + "integrity": "sha512-sqQamAnR14VgCr1A618A3sGrygcpK+HEbenA/HiEAkkUwcZIIB/tgWqHFxWgOyDh4nB4JCRimh79dR5Ywc9MDQ==", + "dev": true, + "license": "MIT", + "dependencies": { + "is-arrayish": "^0.2.1" + } + }, + "node_modules/escalade": { + "version": "3.2.0", + "resolved": "https://registry.npmjs.org/escalade/-/escalade-3.2.0.tgz", + "integrity": "sha512-WUj2qlxaQtO4g6Pq5c29GTcWGDyd8itL8zTlipgECz3JesAiiOKotd8JU6otB3PACgG6xkJUyVhboMS+bje/jA==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=6" + } + }, + "node_modules/escape-string-regexp": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/escape-string-regexp/-/escape-string-regexp-2.0.0.tgz", + "integrity": "sha512-UpzcLCXolUWcNu5HtVMHYdXJjArjsF9C0aNnquZYY4uW/Vu0miy5YoWvbV345HauVvcAUnpRuhMMcqTcGOY2+w==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=8" + } + }, + "node_modules/esprima": { + "version": "4.0.1", + "resolved": "https://registry.npmjs.org/esprima/-/esprima-4.0.1.tgz", + "integrity": "sha512-eGuFFw7Upda+g4p+QHvnW0RyTX/SVeJBDM/gCtMARO0cLuT2HcEKnTPvhjV6aGeqrCB/sbNop0Kszm0jsaWU4A==", + "dev": true, + "license": "BSD-2-Clause", + "bin": { + "esparse": "bin/esparse.js", + "esvalidate": "bin/esvalidate.js" + }, + "engines": { + "node": ">=4" + } + }, + "node_modules/esutils": { + "version": "2.0.3", + "resolved": "https://registry.npmjs.org/esutils/-/esutils-2.0.3.tgz", + "integrity": "sha512-kVscqXk4OCp68SZ0dkgEKVi6/8ij300KBWTJq32P/dYeWTSwK41WyTxalN1eRmA5Z9UU/LX9D7FWSmV9SAYx6g==", + "dev": true, + "license": "BSD-2-Clause", + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/execa": { + "version": "5.1.1", + "resolved": "https://registry.npmjs.org/execa/-/execa-5.1.1.tgz", + "integrity": "sha512-8uSpZZocAZRBAPIEINJj3Lo9HyGitllczc27Eh5YYojjMFMn8yHMDMaUHE2Jqfq05D/wucwI4JGURyXt1vchyg==", + "dev": true, + "license": "MIT", + "dependencies": { + "cross-spawn": "^7.0.3", + "get-stream": "^6.0.0", + "human-signals": "^2.1.0", + "is-stream": "^2.0.0", + "merge-stream": "^2.0.0", + "npm-run-path": "^4.0.1", + "onetime": "^5.1.2", + "signal-exit": "^3.0.3", + "strip-final-newline": "^2.0.0" + }, + "engines": { + "node": ">=10" + }, + "funding": { + "url": "https://github.com/sindresorhus/execa?sponsor=1" + } + }, + "node_modules/execa/node_modules/signal-exit": { + "version": "3.0.7", + "resolved": "https://registry.npmjs.org/signal-exit/-/signal-exit-3.0.7.tgz", + "integrity": "sha512-wnD2ZE+l+SPC/uoS0vXeE9L1+0wuaMqKlfz9AMUo38JsyLSBWSFcHR1Rri62LZc12vLr1gb3jl7iwQhgwpAbGQ==", + "dev": true, + "license": "ISC" + }, + "node_modules/exit-x": { + "version": "0.2.2", + "resolved": "https://registry.npmjs.org/exit-x/-/exit-x-0.2.2.tgz", + "integrity": "sha512-+I6B/IkJc1o/2tiURyz/ivu/O0nKNEArIUB5O7zBrlDVJr22SCLH3xTeEry428LvFhRzIA1g8izguxJ/gbNcVQ==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">= 0.8.0" + } + }, + "node_modules/expect": { + "version": "30.2.0", + "resolved": "https://registry.npmjs.org/expect/-/expect-30.2.0.tgz", + "integrity": "sha512-u/feCi0GPsI+988gU2FLcsHyAHTU0MX1Wg68NhAnN7z/+C5wqG+CY8J53N9ioe8RXgaoz0nBR/TYMf3AycUuPw==", + "dev": true, + "license": "MIT", + "dependencies": { + "@jest/expect-utils": "30.2.0", + "@jest/get-type": "30.1.0", + "jest-matcher-utils": "30.2.0", + "jest-message-util": "30.2.0", + "jest-mock": "30.2.0", + "jest-util": "30.2.0" + }, + "engines": { + "node": "^18.14.0 || ^20.0.0 || ^22.0.0 || >=24.0.0" + } + }, + "node_modules/fast-json-stable-stringify": { + "version": "2.1.0", + "resolved": "https://registry.npmjs.org/fast-json-stable-stringify/-/fast-json-stable-stringify-2.1.0.tgz", + "integrity": "sha512-lhd/wF+Lk98HZoTCtlVraHtfh5XYijIjalXck7saUtuanSDyLMxnHhSXEDJqHxD7msR8D0uCmqlkwjCV8xvwHw==", + "dev": true, + "license": "MIT" + }, + "node_modules/fb-watchman": { + "version": "2.0.2", + "resolved": "https://registry.npmjs.org/fb-watchman/-/fb-watchman-2.0.2.tgz", + "integrity": "sha512-p5161BqbuCaSnB8jIbzQHOlpgsPmK5rJVDfDKO91Axs5NC1uu3HRQm6wt9cd9/+GtQQIO53JdGXXoyDpTAsgYA==", + "dev": true, + "license": "Apache-2.0", + "dependencies": { + "bser": "2.1.1" + } + }, + "node_modules/fill-range": { + "version": "7.1.1", + "resolved": "https://registry.npmjs.org/fill-range/-/fill-range-7.1.1.tgz", + "integrity": "sha512-YsGpe3WHLK8ZYi4tWDg2Jy3ebRz2rXowDxnld4bkQB00cc/1Zw9AWnC0i9ztDJitivtQvaI9KaLyKrc+hBW0yg==", + "dev": true, + "license": "MIT", + "dependencies": { + "to-regex-range": "^5.0.1" + }, + "engines": { + "node": ">=8" + } + }, + "node_modules/find-up": { + "version": "4.1.0", + "resolved": "https://registry.npmjs.org/find-up/-/find-up-4.1.0.tgz", + "integrity": "sha512-PpOwAdQ/YlXQ2vj8a3h8IipDuYRi3wceVQQGYWxNINccq40Anw7BlsEXCMbt1Zt+OLA6Fq9suIpIWD0OsnISlw==", + "dev": true, + "license": "MIT", + "dependencies": { + "locate-path": "^5.0.0", + "path-exists": "^4.0.0" + }, + "engines": { + "node": ">=8" + } + }, + "node_modules/foreground-child": { + "version": "3.3.1", + "resolved": "https://registry.npmjs.org/foreground-child/-/foreground-child-3.3.1.tgz", + "integrity": "sha512-gIXjKqtFuWEgzFRJA9WCQeSJLZDjgJUOMCMzxtvFq/37KojM1BFGufqsCy0r4qSQmYLsZYMeyRqzIWOMup03sw==", + "dev": true, + "license": "ISC", + "dependencies": { + "cross-spawn": "^7.0.6", + "signal-exit": "^4.0.1" + }, + "engines": { + "node": ">=14" + }, + "funding": { + "url": "https://github.com/sponsors/isaacs" + } + }, + "node_modules/fs.realpath": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/fs.realpath/-/fs.realpath-1.0.0.tgz", + "integrity": "sha512-OO0pH2lK6a0hZnAdau5ItzHPI6pUlvI7jMVnxUQRtw4owF2wk8lOSabtGDCTP4Ggrg2MbGnWO9X8K1t4+fGMDw==", + "dev": true, + "license": "ISC" + }, + "node_modules/fsevents": { + "version": "2.3.3", + "resolved": "https://registry.npmjs.org/fsevents/-/fsevents-2.3.3.tgz", + "integrity": "sha512-5xoDfX+fL7faATnagmWPpbFtwh/R77WmMMqqHGS65C3vvB0YHrgF+B1YmZ3441tMj5n63k0212XNoJwzlhffQw==", + "dev": true, + "hasInstallScript": true, + "license": "MIT", + "optional": true, + "os": [ + "darwin" + ], + "engines": { + "node": "^8.16.0 || ^10.6.0 || >=11.0.0" + } + }, + "node_modules/function-bind": { + "version": "1.1.2", + "resolved": "https://registry.npmjs.org/function-bind/-/function-bind-1.1.2.tgz", + "integrity": "sha512-7XHNxH7qX9xG5mIwxkhumTox/MIRNcOgDrxWsMt2pAr23WHp6MrRlN7FBSFpCpr+oVO0F744iUgR82nJMfG2SA==", + "dev": true, + "license": "MIT", + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/gensync": { + "version": "1.0.0-beta.2", + "resolved": "https://registry.npmjs.org/gensync/-/gensync-1.0.0-beta.2.tgz", + "integrity": "sha512-3hN7NaskYvMDLQY55gnW3NQ+mesEAepTqlg+VEbj7zzqEMBVNhzcGYYeqFo/TlYz6eQiFcp1HcsCZO+nGgS8zg==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=6.9.0" + } + }, + "node_modules/get-caller-file": { + "version": "2.0.5", + "resolved": "https://registry.npmjs.org/get-caller-file/-/get-caller-file-2.0.5.tgz", + "integrity": "sha512-DyFP3BM/3YHTQOCUL/w0OZHR0lpKeGrxotcHWcqNEdnltqFwXVfhEBQ94eIo34AfQpo0rGki4cyIiftY06h2Fg==", + "dev": true, + "license": "ISC", + "engines": { + "node": "6.* || 8.* || >= 10.*" + } + }, + "node_modules/get-package-type": { + "version": "0.1.0", + "resolved": "https://registry.npmjs.org/get-package-type/-/get-package-type-0.1.0.tgz", + "integrity": "sha512-pjzuKtY64GYfWizNAJ0fr9VqttZkNiK2iS430LtIHzjBEr6bX8Am2zm4sW4Ro5wjWW5cAlRL1qAMTcXbjNAO2Q==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=8.0.0" + } + }, + "node_modules/get-stream": { + "version": "6.0.1", + "resolved": "https://registry.npmjs.org/get-stream/-/get-stream-6.0.1.tgz", + "integrity": "sha512-ts6Wi+2j3jQjqi70w5AlN8DFnkSwC+MqmxEzdEALB2qXZYV3X/b1CTfgPLGJNMeAWxdPfU8FO1ms3NUfaHCPYg==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=10" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/glob": { + "version": "10.4.5", + "resolved": "https://registry.npmjs.org/glob/-/glob-10.4.5.tgz", + "integrity": "sha512-7Bv8RF0k6xjo7d4A/PxYLbUCfb6c+Vpd2/mB2yRDlew7Jb5hEXiCD9ibfO7wpk8i4sevK6DFny9h7EYbM3/sHg==", + "dev": true, + "license": "ISC", + "dependencies": { + "foreground-child": "^3.1.0", + "jackspeak": "^3.1.2", + "minimatch": "^9.0.4", + "minipass": "^7.1.2", + "package-json-from-dist": "^1.0.0", + "path-scurry": "^1.11.1" + }, + "bin": { + "glob": "dist/esm/bin.mjs" + }, + "funding": { + "url": "https://github.com/sponsors/isaacs" + } + }, + "node_modules/graceful-fs": { + "version": "4.2.11", + "resolved": "https://registry.npmjs.org/graceful-fs/-/graceful-fs-4.2.11.tgz", + "integrity": "sha512-RbJ5/jmFcNNCcDV5o9eTnBLJ/HszWV0P73bc+Ff4nS/rJj+YaS6IGyiOL0VoBYX+l1Wrl3k63h/KrH+nhJ0XvQ==", + "dev": true, + "license": "ISC" + }, + "node_modules/has-flag": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/has-flag/-/has-flag-4.0.0.tgz", + "integrity": "sha512-EykJT/Q1KjTWctppgIAgfSO0tKVuZUjhgMr17kqTumMl6Afv3EISleU7qZUzoXDFTAHTDC4NOoG/ZxU3EvlMPQ==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=8" + } + }, + "node_modules/hasown": { + "version": "2.0.2", + "resolved": "https://registry.npmjs.org/hasown/-/hasown-2.0.2.tgz", + "integrity": "sha512-0hJU9SCPvmMzIBdZFqNPXWa6dqh7WdH0cII9y+CyS8rG3nL48Bclra9HmKhVVUHyPWNH5Y7xDwAB7bfgSjkUMQ==", + "dev": true, + "license": "MIT", + "dependencies": { + "function-bind": "^1.1.2" + }, + "engines": { + "node": ">= 0.4" + } + }, + "node_modules/html-escaper": { + "version": "2.0.2", + "resolved": "https://registry.npmjs.org/html-escaper/-/html-escaper-2.0.2.tgz", + "integrity": "sha512-H2iMtd0I4Mt5eYiapRdIDjp+XzelXQ0tFE4JS7YFwFevXXMmOp9myNrUvCg0D6ws8iqkRPBfKHgbwig1SmlLfg==", + "dev": true, + "license": "MIT" + }, + "node_modules/human-signals": { + "version": "2.1.0", + "resolved": "https://registry.npmjs.org/human-signals/-/human-signals-2.1.0.tgz", + "integrity": "sha512-B4FFZ6q/T2jhhksgkbEW3HBvWIfDW85snkQgawt07S7J5QXTk6BkNV+0yAeZrM5QpMAdYlocGoljn0sJ/WQkFw==", + "dev": true, + "license": "Apache-2.0", + "engines": { + "node": ">=10.17.0" + } + }, + "node_modules/import-local": { + "version": "3.2.0", + "resolved": "https://registry.npmjs.org/import-local/-/import-local-3.2.0.tgz", + "integrity": "sha512-2SPlun1JUPWoM6t3F0dw0FkCF/jWY8kttcY4f599GLTSjh2OCuuhdTkJQsEcZzBqbXZGKMK2OqW1oZsjtf/gQA==", + "dev": true, + "license": "MIT", + "dependencies": { + "pkg-dir": "^4.2.0", + "resolve-cwd": "^3.0.0" + }, + "bin": { + "import-local-fixture": "fixtures/cli.js" + }, + "engines": { + "node": ">=8" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/imurmurhash": { + "version": "0.1.4", + "resolved": "https://registry.npmjs.org/imurmurhash/-/imurmurhash-0.1.4.tgz", + "integrity": "sha512-JmXMZ6wuvDmLiHEml9ykzqO6lwFbof0GG4IkcGaENdCRDDmMVnny7s5HsIgHCbaq0w2MyPhDqkhTUgS2LU2PHA==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=0.8.19" + } + }, + "node_modules/inflight": { + "version": "1.0.6", + "resolved": "https://registry.npmjs.org/inflight/-/inflight-1.0.6.tgz", + "integrity": "sha512-k92I/b08q4wvFscXCLvqfsHCrjrF7yiXsQuIVvVE7N82W3+aqpzuUdBbfhWcy/FZR3/4IgflMgKLOsvPDrGCJA==", + "deprecated": "This module is not supported, and leaks memory. Do not use it. Check out lru-cache if you want a good and tested way to coalesce async requests by a key value, which is much more comprehensive and powerful.", + "dev": true, + "license": "ISC", + "dependencies": { + "once": "^1.3.0", + "wrappy": "1" + } + }, + "node_modules/inherits": { + "version": "2.0.4", + "resolved": "https://registry.npmjs.org/inherits/-/inherits-2.0.4.tgz", + "integrity": "sha512-k/vGaX4/Yla3WzyMCvTQOXYeIHvqOKtnqBduzTHpzpQZzAskKMhZ2K+EnBiSM9zGSoIFeMpXKxa4dYeZIQqewQ==", + "dev": true, + "license": "ISC" + }, + "node_modules/is-arrayish": { + "version": "0.2.1", + "resolved": "https://registry.npmjs.org/is-arrayish/-/is-arrayish-0.2.1.tgz", + "integrity": "sha512-zz06S8t0ozoDXMG+ube26zeCTNXcKIPJZJi8hBrF4idCLms4CG9QtK7qBl1boi5ODzFpjswb5JPmHCbMpjaYzg==", + "dev": true, + "license": "MIT" + }, + "node_modules/is-core-module": { + "version": "2.16.1", + "resolved": "https://registry.npmjs.org/is-core-module/-/is-core-module-2.16.1.tgz", + "integrity": "sha512-UfoeMA6fIJ8wTYFEUjelnaGI67v6+N7qXJEvQuIGa99l4xsCruSYOVSQ0uPANn4dAzm8lkYPaKLrrijLq7x23w==", + "dev": true, + "license": "MIT", + "dependencies": { + "hasown": "^2.0.2" + }, + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/is-fullwidth-code-point": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/is-fullwidth-code-point/-/is-fullwidth-code-point-3.0.0.tgz", + "integrity": "sha512-zymm5+u+sCsSWyD9qNaejV3DFvhCKclKdizYaJUuHA83RLjb7nSuGnddCHGv0hk+KY7BMAlsWeK4Ueg6EV6XQg==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=8" + } + }, + "node_modules/is-generator-fn": { + "version": "2.1.0", + "resolved": "https://registry.npmjs.org/is-generator-fn/-/is-generator-fn-2.1.0.tgz", + "integrity": "sha512-cTIB4yPYL/Grw0EaSzASzg6bBy9gqCofvWN8okThAYIxKJZC+udlRAmGbM0XLeniEJSs8uEgHPGuHSe1XsOLSQ==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=6" + } + }, + "node_modules/is-number": { + "version": "7.0.0", + "resolved": "https://registry.npmjs.org/is-number/-/is-number-7.0.0.tgz", + "integrity": "sha512-41Cifkg6e8TylSpdtTpeLVMqvSBEVzTttHvERD741+pnZ8ANv0004MRL43QKPDlK9cGvNp6NZWZUBlbGXYxxng==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=0.12.0" + } + }, + "node_modules/is-stream": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/is-stream/-/is-stream-2.0.1.tgz", + "integrity": "sha512-hFoiJiTl63nn+kstHGBtewWSKnQLpyb155KHheA1l39uvtO9nWIop1p3udqPcUd/xbF1VLMO4n7OI6p7RbngDg==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=8" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/isexe": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/isexe/-/isexe-2.0.0.tgz", + "integrity": "sha512-RHxMLp9lnKHGHRng9QFhRCMbYAcVpn69smSGcq3f36xjgVVWThj4qqLbTLlq7Ssj8B+fIQ1EuCEGI2lKsyQeIw==", + "dev": true, + "license": "ISC" + }, + "node_modules/istanbul-lib-coverage": { + "version": "3.2.2", + "resolved": "https://registry.npmjs.org/istanbul-lib-coverage/-/istanbul-lib-coverage-3.2.2.tgz", + "integrity": "sha512-O8dpsF+r0WV/8MNRKfnmrtCWhuKjxrq2w+jpzBL5UZKTi2LeVWnWOmWRxFlesJONmc+wLAGvKQZEOanko0LFTg==", + "dev": true, + "license": "BSD-3-Clause", + "engines": { + "node": ">=8" + } + }, + "node_modules/istanbul-lib-instrument": { + "version": "6.0.3", + "resolved": "https://registry.npmjs.org/istanbul-lib-instrument/-/istanbul-lib-instrument-6.0.3.tgz", + "integrity": "sha512-Vtgk7L/R2JHyyGW07spoFlB8/lpjiOLTjMdms6AFMraYt3BaJauod/NGrfnVG/y4Ix1JEuMRPDPEj2ua+zz1/Q==", + "dev": true, + "license": "BSD-3-Clause", + "dependencies": { + "@babel/core": "^7.23.9", + "@babel/parser": "^7.23.9", + "@istanbuljs/schema": "^0.1.3", + "istanbul-lib-coverage": "^3.2.0", + "semver": "^7.5.4" + }, + "engines": { + "node": ">=10" + } + }, + "node_modules/istanbul-lib-instrument/node_modules/semver": { + "version": "7.7.3", + "resolved": "https://registry.npmjs.org/semver/-/semver-7.7.3.tgz", + "integrity": "sha512-SdsKMrI9TdgjdweUSR9MweHA4EJ8YxHn8DFaDisvhVlUOe4BF1tLD7GAj0lIqWVl+dPb/rExr0Btby5loQm20Q==", + "dev": true, + "license": "ISC", + "bin": { + "semver": "bin/semver.js" + }, + "engines": { + "node": ">=10" + } + }, + "node_modules/istanbul-lib-report": { + "version": "3.0.1", + "resolved": "https://registry.npmjs.org/istanbul-lib-report/-/istanbul-lib-report-3.0.1.tgz", + "integrity": "sha512-GCfE1mtsHGOELCU8e/Z7YWzpmybrx/+dSTfLrvY8qRmaY6zXTKWn6WQIjaAFw069icm6GVMNkgu0NzI4iPZUNw==", + "dev": true, + "license": "BSD-3-Clause", + "dependencies": { + "istanbul-lib-coverage": "^3.0.0", + "make-dir": "^4.0.0", + "supports-color": "^7.1.0" + }, + "engines": { + "node": ">=10" + } + }, + "node_modules/istanbul-lib-source-maps": { + "version": "5.0.6", + "resolved": "https://registry.npmjs.org/istanbul-lib-source-maps/-/istanbul-lib-source-maps-5.0.6.tgz", + "integrity": "sha512-yg2d+Em4KizZC5niWhQaIomgf5WlL4vOOjZ5xGCmF8SnPE/mDWWXgvRExdcpCgh9lLRRa1/fSYp2ymmbJ1pI+A==", + "dev": true, + "license": "BSD-3-Clause", + "dependencies": { + "@jridgewell/trace-mapping": "^0.3.23", + "debug": "^4.1.1", + "istanbul-lib-coverage": "^3.0.0" + }, + "engines": { + "node": ">=10" + } + }, + "node_modules/istanbul-reports": { + "version": "3.2.0", + "resolved": "https://registry.npmjs.org/istanbul-reports/-/istanbul-reports-3.2.0.tgz", + "integrity": "sha512-HGYWWS/ehqTV3xN10i23tkPkpH46MLCIMFNCaaKNavAXTF1RkqxawEPtnjnGZ6XKSInBKkiOA5BKS+aZiY3AvA==", + "dev": true, + "license": "BSD-3-Clause", + "dependencies": { + "html-escaper": "^2.0.0", + "istanbul-lib-report": "^3.0.0" + }, + "engines": { + "node": ">=8" + } + }, + "node_modules/jackspeak": { + "version": "3.4.3", + "resolved": "https://registry.npmjs.org/jackspeak/-/jackspeak-3.4.3.tgz", + "integrity": "sha512-OGlZQpz2yfahA/Rd1Y8Cd9SIEsqvXkLVoSw/cgwhnhFMDbsQFeZYoJJ7bIZBS9BcamUW96asq/npPWugM+RQBw==", + "dev": true, + "license": "BlueOak-1.0.0", + "dependencies": { + "@isaacs/cliui": "^8.0.2" + }, + "funding": { + "url": "https://github.com/sponsors/isaacs" + }, + "optionalDependencies": { + "@pkgjs/parseargs": "^0.11.0" + } + }, + "node_modules/jest": { + "version": "30.2.0", + "resolved": "https://registry.npmjs.org/jest/-/jest-30.2.0.tgz", + "integrity": "sha512-F26gjC0yWN8uAA5m5Ss8ZQf5nDHWGlN/xWZIh8S5SRbsEKBovwZhxGd6LJlbZYxBgCYOtreSUyb8hpXyGC5O4A==", + "dev": true, + "license": "MIT", + "dependencies": { + "@jest/core": "30.2.0", + "@jest/types": "30.2.0", + "import-local": "^3.2.0", + "jest-cli": "30.2.0" + }, + "bin": { + "jest": "bin/jest.js" + }, + "engines": { + "node": "^18.14.0 || ^20.0.0 || ^22.0.0 || >=24.0.0" + }, + "peerDependencies": { + "node-notifier": "^8.0.1 || ^9.0.0 || ^10.0.0" + }, + "peerDependenciesMeta": { + "node-notifier": { + "optional": true + } + } + }, + "node_modules/jest-changed-files": { + "version": "30.2.0", + "resolved": "https://registry.npmjs.org/jest-changed-files/-/jest-changed-files-30.2.0.tgz", + "integrity": "sha512-L8lR1ChrRnSdfeOvTrwZMlnWV8G/LLjQ0nG9MBclwWZidA2N5FviRki0Bvh20WRMOX31/JYvzdqTJrk5oBdydQ==", + "dev": true, + "license": "MIT", + "dependencies": { + "execa": "^5.1.1", + "jest-util": "30.2.0", + "p-limit": "^3.1.0" + }, + "engines": { + "node": "^18.14.0 || ^20.0.0 || ^22.0.0 || >=24.0.0" + } + }, + "node_modules/jest-circus": { + "version": "30.2.0", + "resolved": "https://registry.npmjs.org/jest-circus/-/jest-circus-30.2.0.tgz", + "integrity": "sha512-Fh0096NC3ZkFx05EP2OXCxJAREVxj1BcW/i6EWqqymcgYKWjyyDpral3fMxVcHXg6oZM7iULer9wGRFvfpl+Tg==", + "dev": true, + "license": "MIT", + "dependencies": { + "@jest/environment": "30.2.0", + "@jest/expect": "30.2.0", + "@jest/test-result": "30.2.0", + "@jest/types": "30.2.0", + "@types/node": "*", + "chalk": "^4.1.2", + "co": "^4.6.0", + "dedent": "^1.6.0", + "is-generator-fn": "^2.1.0", + "jest-each": "30.2.0", + "jest-matcher-utils": "30.2.0", + "jest-message-util": "30.2.0", + "jest-runtime": "30.2.0", + "jest-snapshot": "30.2.0", + "jest-util": "30.2.0", + "p-limit": "^3.1.0", + "pretty-format": "30.2.0", + "pure-rand": "^7.0.0", + "slash": "^3.0.0", + "stack-utils": "^2.0.6" + }, + "engines": { + "node": "^18.14.0 || ^20.0.0 || ^22.0.0 || >=24.0.0" + } + }, + "node_modules/jest-cli": { + "version": "30.2.0", + "resolved": "https://registry.npmjs.org/jest-cli/-/jest-cli-30.2.0.tgz", + "integrity": "sha512-Os9ukIvADX/A9sLt6Zse3+nmHtHaE6hqOsjQtNiugFTbKRHYIYtZXNGNK9NChseXy7djFPjndX1tL0sCTlfpAA==", + "dev": true, + "license": "MIT", + "dependencies": { + "@jest/core": "30.2.0", + "@jest/test-result": "30.2.0", + "@jest/types": "30.2.0", + "chalk": "^4.1.2", + "exit-x": "^0.2.2", + "import-local": "^3.2.0", + "jest-config": "30.2.0", + "jest-util": "30.2.0", + "jest-validate": "30.2.0", + "yargs": "^17.7.2" + }, + "bin": { + "jest": "bin/jest.js" + }, + "engines": { + "node": "^18.14.0 || ^20.0.0 || ^22.0.0 || >=24.0.0" + }, + "peerDependencies": { + "node-notifier": "^8.0.1 || ^9.0.0 || ^10.0.0" + }, + "peerDependenciesMeta": { + "node-notifier": { + "optional": true + } + } + }, + "node_modules/jest-config": { + "version": "30.2.0", + "resolved": "https://registry.npmjs.org/jest-config/-/jest-config-30.2.0.tgz", + "integrity": "sha512-g4WkyzFQVWHtu6uqGmQR4CQxz/CH3yDSlhzXMWzNjDx843gYjReZnMRanjRCq5XZFuQrGDxgUaiYWE8BRfVckA==", + "dev": true, + "license": "MIT", + "dependencies": { + "@babel/core": "^7.27.4", + "@jest/get-type": "30.1.0", + "@jest/pattern": "30.0.1", + "@jest/test-sequencer": "30.2.0", + "@jest/types": "30.2.0", + "babel-jest": "30.2.0", + "chalk": "^4.1.2", + "ci-info": "^4.2.0", + "deepmerge": "^4.3.1", + "glob": "^10.3.10", + "graceful-fs": "^4.2.11", + "jest-circus": "30.2.0", + "jest-docblock": "30.2.0", + "jest-environment-node": "30.2.0", + "jest-regex-util": "30.0.1", + "jest-resolve": "30.2.0", + "jest-runner": "30.2.0", + "jest-util": "30.2.0", + "jest-validate": "30.2.0", + "micromatch": "^4.0.8", + "parse-json": "^5.2.0", + "pretty-format": "30.2.0", + "slash": "^3.0.0", + "strip-json-comments": "^3.1.1" + }, + "engines": { + "node": "^18.14.0 || ^20.0.0 || ^22.0.0 || >=24.0.0" + }, + "peerDependencies": { + "@types/node": "*", + "esbuild-register": ">=3.4.0", + "ts-node": ">=9.0.0" + }, + "peerDependenciesMeta": { + "@types/node": { + "optional": true + }, + "esbuild-register": { + "optional": true + }, + "ts-node": { + "optional": true + } + } + }, + "node_modules/jest-diff": { + "version": "30.2.0", + "resolved": "https://registry.npmjs.org/jest-diff/-/jest-diff-30.2.0.tgz", + "integrity": "sha512-dQHFo3Pt4/NLlG5z4PxZ/3yZTZ1C7s9hveiOj+GCN+uT109NC2QgsoVZsVOAvbJ3RgKkvyLGXZV9+piDpWbm6A==", + "dev": true, + "license": "MIT", + "dependencies": { + "@jest/diff-sequences": "30.0.1", + "@jest/get-type": "30.1.0", + "chalk": "^4.1.2", + "pretty-format": "30.2.0" + }, + "engines": { + "node": "^18.14.0 || ^20.0.0 || ^22.0.0 || >=24.0.0" + } + }, + "node_modules/jest-docblock": { + "version": "30.2.0", + "resolved": "https://registry.npmjs.org/jest-docblock/-/jest-docblock-30.2.0.tgz", + "integrity": "sha512-tR/FFgZKS1CXluOQzZvNH3+0z9jXr3ldGSD8bhyuxvlVUwbeLOGynkunvlTMxchC5urrKndYiwCFC0DLVjpOCA==", + "dev": true, + "license": "MIT", + "dependencies": { + "detect-newline": "^3.1.0" + }, + "engines": { + "node": "^18.14.0 || ^20.0.0 || ^22.0.0 || >=24.0.0" + } + }, + "node_modules/jest-each": { + "version": "30.2.0", + "resolved": "https://registry.npmjs.org/jest-each/-/jest-each-30.2.0.tgz", + "integrity": "sha512-lpWlJlM7bCUf1mfmuqTA8+j2lNURW9eNafOy99knBM01i5CQeY5UH1vZjgT9071nDJac1M4XsbyI44oNOdhlDQ==", + "dev": true, + "license": "MIT", + "dependencies": { + "@jest/get-type": "30.1.0", + "@jest/types": "30.2.0", + "chalk": "^4.1.2", + "jest-util": "30.2.0", + "pretty-format": "30.2.0" + }, + "engines": { + "node": "^18.14.0 || ^20.0.0 || ^22.0.0 || >=24.0.0" + } + }, + "node_modules/jest-environment-node": { + "version": "30.2.0", + "resolved": "https://registry.npmjs.org/jest-environment-node/-/jest-environment-node-30.2.0.tgz", + "integrity": "sha512-ElU8v92QJ9UrYsKrxDIKCxu6PfNj4Hdcktcn0JX12zqNdqWHB0N+hwOnnBBXvjLd2vApZtuLUGs1QSY+MsXoNA==", + "dev": true, + "license": "MIT", + "dependencies": { + "@jest/environment": "30.2.0", + "@jest/fake-timers": "30.2.0", + "@jest/types": "30.2.0", + "@types/node": "*", + "jest-mock": "30.2.0", + "jest-util": "30.2.0", + "jest-validate": "30.2.0" + }, + "engines": { + "node": "^18.14.0 || ^20.0.0 || ^22.0.0 || >=24.0.0" + } + }, + "node_modules/jest-haste-map": { + "version": "30.2.0", + "resolved": "https://registry.npmjs.org/jest-haste-map/-/jest-haste-map-30.2.0.tgz", + "integrity": "sha512-sQA/jCb9kNt+neM0anSj6eZhLZUIhQgwDt7cPGjumgLM4rXsfb9kpnlacmvZz3Q5tb80nS+oG/if+NBKrHC+Xw==", + "dev": true, + "license": "MIT", + "dependencies": { + "@jest/types": "30.2.0", + "@types/node": "*", + "anymatch": "^3.1.3", + "fb-watchman": "^2.0.2", + "graceful-fs": "^4.2.11", + "jest-regex-util": "30.0.1", + "jest-util": "30.2.0", + "jest-worker": "30.2.0", + "micromatch": "^4.0.8", + "walker": "^1.0.8" + }, + "engines": { + "node": "^18.14.0 || ^20.0.0 || ^22.0.0 || >=24.0.0" + }, + "optionalDependencies": { + "fsevents": "^2.3.3" + } + }, + "node_modules/jest-leak-detector": { + "version": "30.2.0", + "resolved": "https://registry.npmjs.org/jest-leak-detector/-/jest-leak-detector-30.2.0.tgz", + "integrity": "sha512-M6jKAjyzjHG0SrQgwhgZGy9hFazcudwCNovY/9HPIicmNSBuockPSedAP9vlPK6ONFJ1zfyH/M2/YYJxOz5cdQ==", + "dev": true, + "license": "MIT", + "dependencies": { + "@jest/get-type": "30.1.0", + "pretty-format": "30.2.0" + }, + "engines": { + "node": "^18.14.0 || ^20.0.0 || ^22.0.0 || >=24.0.0" + } + }, + "node_modules/jest-matcher-utils": { + "version": "30.2.0", + "resolved": "https://registry.npmjs.org/jest-matcher-utils/-/jest-matcher-utils-30.2.0.tgz", + "integrity": "sha512-dQ94Nq4dbzmUWkQ0ANAWS9tBRfqCrn0bV9AMYdOi/MHW726xn7eQmMeRTpX2ViC00bpNaWXq+7o4lIQ3AX13Hg==", + "dev": true, + "license": "MIT", + "dependencies": { + "@jest/get-type": "30.1.0", + "chalk": "^4.1.2", + "jest-diff": "30.2.0", + "pretty-format": "30.2.0" + }, + "engines": { + "node": "^18.14.0 || ^20.0.0 || ^22.0.0 || >=24.0.0" + } + }, + "node_modules/jest-message-util": { + "version": "30.2.0", + "resolved": "https://registry.npmjs.org/jest-message-util/-/jest-message-util-30.2.0.tgz", + "integrity": "sha512-y4DKFLZ2y6DxTWD4cDe07RglV88ZiNEdlRfGtqahfbIjfsw1nMCPx49Uev4IA/hWn3sDKyAnSPwoYSsAEdcimw==", + "dev": true, + "license": "MIT", + "dependencies": { + "@babel/code-frame": "^7.27.1", + "@jest/types": "30.2.0", + "@types/stack-utils": "^2.0.3", + "chalk": "^4.1.2", + "graceful-fs": "^4.2.11", + "micromatch": "^4.0.8", + "pretty-format": "30.2.0", + "slash": "^3.0.0", + "stack-utils": "^2.0.6" + }, + "engines": { + "node": "^18.14.0 || ^20.0.0 || ^22.0.0 || >=24.0.0" + } + }, + "node_modules/jest-mock": { + "version": "30.2.0", + "resolved": "https://registry.npmjs.org/jest-mock/-/jest-mock-30.2.0.tgz", + "integrity": "sha512-JNNNl2rj4b5ICpmAcq+WbLH83XswjPbjH4T7yvGzfAGCPh1rw+xVNbtk+FnRslvt9lkCcdn9i1oAoKUuFsOxRw==", + "dev": true, + "license": "MIT", + "dependencies": { + "@jest/types": "30.2.0", + "@types/node": "*", + "jest-util": "30.2.0" + }, + "engines": { + "node": "^18.14.0 || ^20.0.0 || ^22.0.0 || >=24.0.0" + } + }, + "node_modules/jest-pnp-resolver": { + "version": "1.2.3", + "resolved": "https://registry.npmjs.org/jest-pnp-resolver/-/jest-pnp-resolver-1.2.3.tgz", + "integrity": "sha512-+3NpwQEnRoIBtx4fyhblQDPgJI0H1IEIkX7ShLUjPGA7TtUTvI1oiKi3SR4oBR0hQhQR80l4WAe5RrXBwWMA8w==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=6" + }, + "peerDependencies": { + "jest-resolve": "*" + }, + "peerDependenciesMeta": { + "jest-resolve": { + "optional": true + } + } + }, + "node_modules/jest-regex-util": { + "version": "30.0.1", + "resolved": "https://registry.npmjs.org/jest-regex-util/-/jest-regex-util-30.0.1.tgz", + "integrity": "sha512-jHEQgBXAgc+Gh4g0p3bCevgRCVRkB4VB70zhoAE48gxeSr1hfUOsM/C2WoJgVL7Eyg//hudYENbm3Ne+/dRVVA==", + "dev": true, + "license": "MIT", + "engines": { + "node": "^18.14.0 || ^20.0.0 || ^22.0.0 || >=24.0.0" + } + }, + "node_modules/jest-resolve": { + "version": "30.2.0", + "resolved": "https://registry.npmjs.org/jest-resolve/-/jest-resolve-30.2.0.tgz", + "integrity": "sha512-TCrHSxPlx3tBY3hWNtRQKbtgLhsXa1WmbJEqBlTBrGafd5fiQFByy2GNCEoGR+Tns8d15GaL9cxEzKOO3GEb2A==", + "dev": true, + "license": "MIT", + "dependencies": { + "chalk": "^4.1.2", + "graceful-fs": "^4.2.11", + "jest-haste-map": "30.2.0", + "jest-pnp-resolver": "^1.2.3", + "jest-util": "30.2.0", + "jest-validate": "30.2.0", + "slash": "^3.0.0", + "unrs-resolver": "^1.7.11" + }, + "engines": { + "node": "^18.14.0 || ^20.0.0 || ^22.0.0 || >=24.0.0" + } + }, + "node_modules/jest-resolve-dependencies": { + "version": "30.2.0", + "resolved": "https://registry.npmjs.org/jest-resolve-dependencies/-/jest-resolve-dependencies-30.2.0.tgz", + "integrity": "sha512-xTOIGug/0RmIe3mmCqCT95yO0vj6JURrn1TKWlNbhiAefJRWINNPgwVkrVgt/YaerPzY3iItufd80v3lOrFJ2w==", + "dev": true, + "license": "MIT", + "dependencies": { + "jest-regex-util": "30.0.1", + "jest-snapshot": "30.2.0" + }, + "engines": { + "node": "^18.14.0 || ^20.0.0 || ^22.0.0 || >=24.0.0" + } + }, + "node_modules/jest-runner": { + "version": "30.2.0", + "resolved": "https://registry.npmjs.org/jest-runner/-/jest-runner-30.2.0.tgz", + "integrity": "sha512-PqvZ2B2XEyPEbclp+gV6KO/F1FIFSbIwewRgmROCMBo/aZ6J1w8Qypoj2pEOcg3G2HzLlaP6VUtvwCI8dM3oqQ==", + "dev": true, + "license": "MIT", + "dependencies": { + "@jest/console": "30.2.0", + "@jest/environment": "30.2.0", + "@jest/test-result": "30.2.0", + "@jest/transform": "30.2.0", + "@jest/types": "30.2.0", + "@types/node": "*", + "chalk": "^4.1.2", + "emittery": "^0.13.1", + "exit-x": "^0.2.2", + "graceful-fs": "^4.2.11", + "jest-docblock": "30.2.0", + "jest-environment-node": "30.2.0", + "jest-haste-map": "30.2.0", + "jest-leak-detector": "30.2.0", + "jest-message-util": "30.2.0", + "jest-resolve": "30.2.0", + "jest-runtime": "30.2.0", + "jest-util": "30.2.0", + "jest-watcher": "30.2.0", + "jest-worker": "30.2.0", + "p-limit": "^3.1.0", + "source-map-support": "0.5.13" + }, + "engines": { + "node": "^18.14.0 || ^20.0.0 || ^22.0.0 || >=24.0.0" + } + }, + "node_modules/jest-runtime": { + "version": "30.2.0", + "resolved": "https://registry.npmjs.org/jest-runtime/-/jest-runtime-30.2.0.tgz", + "integrity": "sha512-p1+GVX/PJqTucvsmERPMgCPvQJpFt4hFbM+VN3n8TMo47decMUcJbt+rgzwrEme0MQUA/R+1de2axftTHkKckg==", + "dev": true, + "license": "MIT", + "dependencies": { + "@jest/environment": "30.2.0", + "@jest/fake-timers": "30.2.0", + "@jest/globals": "30.2.0", + "@jest/source-map": "30.0.1", + "@jest/test-result": "30.2.0", + "@jest/transform": "30.2.0", + "@jest/types": "30.2.0", + "@types/node": "*", + "chalk": "^4.1.2", + "cjs-module-lexer": "^2.1.0", + "collect-v8-coverage": "^1.0.2", + "glob": "^10.3.10", + "graceful-fs": "^4.2.11", + "jest-haste-map": "30.2.0", + "jest-message-util": "30.2.0", + "jest-mock": "30.2.0", + "jest-regex-util": "30.0.1", + "jest-resolve": "30.2.0", + "jest-snapshot": "30.2.0", + "jest-util": "30.2.0", + "slash": "^3.0.0", + "strip-bom": "^4.0.0" + }, + "engines": { + "node": "^18.14.0 || ^20.0.0 || ^22.0.0 || >=24.0.0" + } + }, + "node_modules/jest-snapshot": { + "version": "30.2.0", + "resolved": "https://registry.npmjs.org/jest-snapshot/-/jest-snapshot-30.2.0.tgz", + "integrity": "sha512-5WEtTy2jXPFypadKNpbNkZ72puZCa6UjSr/7djeecHWOu7iYhSXSnHScT8wBz3Rn8Ena5d5RYRcsyKIeqG1IyA==", + "dev": true, + "license": "MIT", + "dependencies": { + "@babel/core": "^7.27.4", + "@babel/generator": "^7.27.5", + "@babel/plugin-syntax-jsx": "^7.27.1", + "@babel/plugin-syntax-typescript": "^7.27.1", + "@babel/types": "^7.27.3", + "@jest/expect-utils": "30.2.0", + "@jest/get-type": "30.1.0", + "@jest/snapshot-utils": "30.2.0", + "@jest/transform": "30.2.0", + "@jest/types": "30.2.0", + "babel-preset-current-node-syntax": "^1.2.0", + "chalk": "^4.1.2", + "expect": "30.2.0", + "graceful-fs": "^4.2.11", + "jest-diff": "30.2.0", + "jest-matcher-utils": "30.2.0", + "jest-message-util": "30.2.0", + "jest-util": "30.2.0", + "pretty-format": "30.2.0", + "semver": "^7.7.2", + "synckit": "^0.11.8" + }, + "engines": { + "node": "^18.14.0 || ^20.0.0 || ^22.0.0 || >=24.0.0" + } + }, + "node_modules/jest-snapshot/node_modules/semver": { + "version": "7.7.3", + "resolved": "https://registry.npmjs.org/semver/-/semver-7.7.3.tgz", + "integrity": "sha512-SdsKMrI9TdgjdweUSR9MweHA4EJ8YxHn8DFaDisvhVlUOe4BF1tLD7GAj0lIqWVl+dPb/rExr0Btby5loQm20Q==", + "dev": true, + "license": "ISC", + "bin": { + "semver": "bin/semver.js" + }, + "engines": { + "node": ">=10" + } + }, + "node_modules/jest-util": { + "version": "30.2.0", + "resolved": "https://registry.npmjs.org/jest-util/-/jest-util-30.2.0.tgz", + "integrity": "sha512-QKNsM0o3Xe6ISQU869e+DhG+4CK/48aHYdJZGlFQVTjnbvgpcKyxpzk29fGiO7i/J8VENZ+d2iGnSsvmuHywlA==", + "dev": true, + "license": "MIT", + "dependencies": { + "@jest/types": "30.2.0", + "@types/node": "*", + "chalk": "^4.1.2", + "ci-info": "^4.2.0", + "graceful-fs": "^4.2.11", + "picomatch": "^4.0.2" + }, + "engines": { + "node": "^18.14.0 || ^20.0.0 || ^22.0.0 || >=24.0.0" + } + }, + "node_modules/jest-util/node_modules/picomatch": { + "version": "4.0.3", + "resolved": "https://registry.npmjs.org/picomatch/-/picomatch-4.0.3.tgz", + "integrity": "sha512-5gTmgEY/sqK6gFXLIsQNH19lWb4ebPDLA4SdLP7dsWkIXHWlG66oPuVvXSGFPppYZz8ZDZq0dYYrbHfBCVUb1Q==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=12" + }, + "funding": { + "url": "https://github.com/sponsors/jonschlinkert" + } + }, + "node_modules/jest-validate": { + "version": "30.2.0", + "resolved": "https://registry.npmjs.org/jest-validate/-/jest-validate-30.2.0.tgz", + "integrity": "sha512-FBGWi7dP2hpdi8nBoWxSsLvBFewKAg0+uSQwBaof4Y4DPgBabXgpSYC5/lR7VmnIlSpASmCi/ntRWPbv7089Pw==", + "dev": true, + "license": "MIT", + "dependencies": { + "@jest/get-type": "30.1.0", + "@jest/types": "30.2.0", + "camelcase": "^6.3.0", + "chalk": "^4.1.2", + "leven": "^3.1.0", + "pretty-format": "30.2.0" + }, + "engines": { + "node": "^18.14.0 || ^20.0.0 || ^22.0.0 || >=24.0.0" + } + }, + "node_modules/jest-validate/node_modules/camelcase": { + "version": "6.3.0", + "resolved": "https://registry.npmjs.org/camelcase/-/camelcase-6.3.0.tgz", + "integrity": "sha512-Gmy6FhYlCY7uOElZUSbxo2UCDH8owEk996gkbrpsgGtrJLM3J7jGxl9Ic7Qwwj4ivOE5AWZWRMecDdF7hqGjFA==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=10" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/jest-watcher": { + "version": "30.2.0", + "resolved": "https://registry.npmjs.org/jest-watcher/-/jest-watcher-30.2.0.tgz", + "integrity": "sha512-PYxa28dxJ9g777pGm/7PrbnMeA0Jr7osHP9bS7eJy9DuAjMgdGtxgf0uKMyoIsTWAkIbUW5hSDdJ3urmgXBqxg==", + "dev": true, + "license": "MIT", + "dependencies": { + "@jest/test-result": "30.2.0", + "@jest/types": "30.2.0", + "@types/node": "*", + "ansi-escapes": "^4.3.2", + "chalk": "^4.1.2", + "emittery": "^0.13.1", + "jest-util": "30.2.0", + "string-length": "^4.0.2" + }, + "engines": { + "node": "^18.14.0 || ^20.0.0 || ^22.0.0 || >=24.0.0" + } + }, + "node_modules/jest-worker": { + "version": "30.2.0", + "resolved": "https://registry.npmjs.org/jest-worker/-/jest-worker-30.2.0.tgz", + "integrity": "sha512-0Q4Uk8WF7BUwqXHuAjc23vmopWJw5WH7w2tqBoUOZpOjW/ZnR44GXXd1r82RvnmI2GZge3ivrYXk/BE2+VtW2g==", + "dev": true, + "license": "MIT", + "dependencies": { + "@types/node": "*", + "@ungap/structured-clone": "^1.3.0", + "jest-util": "30.2.0", + "merge-stream": "^2.0.0", + "supports-color": "^8.1.1" + }, + "engines": { + "node": "^18.14.0 || ^20.0.0 || ^22.0.0 || >=24.0.0" + } + }, + "node_modules/jest-worker/node_modules/supports-color": { + "version": "8.1.1", + "resolved": "https://registry.npmjs.org/supports-color/-/supports-color-8.1.1.tgz", + "integrity": "sha512-MpUEN2OodtUzxvKQl72cUF7RQ5EiHsGvSsVG0ia9c5RbWGL2CI4C7EpPS8UTBIplnlzZiNuV56w+FuNxy3ty2Q==", + "dev": true, + "license": "MIT", + "dependencies": { + "has-flag": "^4.0.0" + }, + "engines": { + "node": ">=10" + }, + "funding": { + "url": "https://github.com/chalk/supports-color?sponsor=1" + } + }, + "node_modules/js-tokens": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/js-tokens/-/js-tokens-4.0.0.tgz", + "integrity": "sha512-RdJUflcE3cUzKiMqQgsCu06FPu9UdIJO0beYbPhHN4k6apgJtifcoCtT9bcxOpYBtpD2kCM6Sbzg4CausW/PKQ==", + "dev": true, + "license": "MIT" + }, + "node_modules/js-yaml": { + "version": "3.14.1", + "resolved": "https://registry.npmjs.org/js-yaml/-/js-yaml-3.14.1.tgz", + "integrity": "sha512-okMH7OXXJ7YrN9Ok3/SXrnu4iX9yOk+25nqX4imS2npuvTYDmo/QEZoqwZkYaIDk3jVvBOTOIEgEhaLOynBS9g==", + "dev": true, + "license": "MIT", + "dependencies": { + "argparse": "^1.0.7", + "esprima": "^4.0.0" + }, + "bin": { + "js-yaml": "bin/js-yaml.js" + } + }, + "node_modules/jsesc": { + "version": "3.1.0", + "resolved": "https://registry.npmjs.org/jsesc/-/jsesc-3.1.0.tgz", + "integrity": "sha512-/sM3dO2FOzXjKQhJuo0Q173wf2KOo8t4I8vHy6lF9poUp7bKT0/NHE8fPX23PwfhnykfqnC2xRxOnVw5XuGIaA==", + "dev": true, + "license": "MIT", + "bin": { + "jsesc": "bin/jsesc" + }, + "engines": { + "node": ">=6" + } + }, + "node_modules/json-parse-even-better-errors": { + "version": "2.3.1", + "resolved": "https://registry.npmjs.org/json-parse-even-better-errors/-/json-parse-even-better-errors-2.3.1.tgz", + "integrity": "sha512-xyFwyhro/JEof6Ghe2iz2NcXoj2sloNsWr/XsERDK/oiPCfaNhl5ONfp+jQdAZRQQ0IJWNzH9zIZF7li91kh2w==", + "dev": true, + "license": "MIT" + }, + "node_modules/json5": { + "version": "2.2.3", + "resolved": "https://registry.npmjs.org/json5/-/json5-2.2.3.tgz", + "integrity": "sha512-XmOWe7eyHYH14cLdVPoyg+GOH3rYX++KpzrylJwSW98t3Nk+U8XOl8FWKOgwtzdb8lXGf6zYwDUzeHMWfxasyg==", + "dev": true, + "license": "MIT", + "bin": { + "json5": "lib/cli.js" + }, + "engines": { + "node": ">=6" + } + }, + "node_modules/leven": { + "version": "3.1.0", + "resolved": "https://registry.npmjs.org/leven/-/leven-3.1.0.tgz", + "integrity": "sha512-qsda+H8jTaUaN/x5vzW2rzc+8Rw4TAQ/4KjB46IwK5VH+IlVeeeje/EoZRpiXvIqjFgK84QffqPztGI3VBLG1A==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=6" + } + }, + "node_modules/lines-and-columns": { + "version": "1.2.4", + "resolved": "https://registry.npmjs.org/lines-and-columns/-/lines-and-columns-1.2.4.tgz", + "integrity": "sha512-7ylylesZQ/PV29jhEDl3Ufjo6ZX7gCqJr5F7PKrqc93v7fzSymt1BpwEU8nAUXs8qzzvqhbjhK5QZg6Mt/HkBg==", + "dev": true, + "license": "MIT" + }, + "node_modules/locate-path": { + "version": "5.0.0", + "resolved": "https://registry.npmjs.org/locate-path/-/locate-path-5.0.0.tgz", + "integrity": "sha512-t7hw9pI+WvuwNJXwk5zVHpyhIqzg2qTlklJOf0mVxGSbe3Fp2VieZcduNYjaLDoy6p9uGpQEGWG87WpMKlNq8g==", + "dev": true, + "license": "MIT", + "dependencies": { + "p-locate": "^4.1.0" + }, + "engines": { + "node": ">=8" + } + }, + "node_modules/lodash.debounce": { + "version": "4.0.8", + "resolved": "https://registry.npmjs.org/lodash.debounce/-/lodash.debounce-4.0.8.tgz", + "integrity": "sha512-FT1yDzDYEoYWhnSGnpE/4Kj1fLZkDFyqRb7fNt6FdYOSxlUWAtp42Eh6Wb0rGIv/m9Bgo7x4GhQbm5Ys4SG5ow==", + "dev": true, + "license": "MIT" + }, + "node_modules/lru-cache": { + "version": "5.1.1", + "resolved": "https://registry.npmjs.org/lru-cache/-/lru-cache-5.1.1.tgz", + "integrity": "sha512-KpNARQA3Iwv+jTA0utUVVbrh+Jlrr1Fv0e56GGzAFOXN7dk/FviaDW8LHmK52DlcH4WP2n6gI8vN1aesBFgo9w==", + "dev": true, + "license": "ISC", + "dependencies": { + "yallist": "^3.0.2" + } + }, + "node_modules/make-dir": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/make-dir/-/make-dir-4.0.0.tgz", + "integrity": "sha512-hXdUTZYIVOt1Ex//jAQi+wTZZpUpwBj/0QsOzqegb3rGMMeJiSEu5xLHnYfBrRV4RH2+OCSOO95Is/7x1WJ4bw==", + "dev": true, + "license": "MIT", + "dependencies": { + "semver": "^7.5.3" + }, + "engines": { + "node": ">=10" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/make-dir/node_modules/semver": { + "version": "7.7.3", + "resolved": "https://registry.npmjs.org/semver/-/semver-7.7.3.tgz", + "integrity": "sha512-SdsKMrI9TdgjdweUSR9MweHA4EJ8YxHn8DFaDisvhVlUOe4BF1tLD7GAj0lIqWVl+dPb/rExr0Btby5loQm20Q==", + "dev": true, + "license": "ISC", + "bin": { + "semver": "bin/semver.js" + }, + "engines": { + "node": ">=10" + } + }, + "node_modules/makeerror": { + "version": "1.0.12", + "resolved": "https://registry.npmjs.org/makeerror/-/makeerror-1.0.12.tgz", + "integrity": "sha512-JmqCvUhmt43madlpFzG4BQzG2Z3m6tvQDNKdClZnO3VbIudJYmxsT0FNJMeiB2+JTSlTQTSbU8QdesVmwJcmLg==", + "dev": true, + "license": "BSD-3-Clause", + "dependencies": { + "tmpl": "1.0.5" + } + }, + "node_modules/merge-stream": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/merge-stream/-/merge-stream-2.0.0.tgz", + "integrity": "sha512-abv/qOcuPfk3URPfDzmZU1LKmuw8kT+0nIHvKrKgFrwifol/doWcdA4ZqsWQ8ENrFKkd67Mfpo/LovbIUsbt3w==", + "dev": true, + "license": "MIT" + }, + "node_modules/micromatch": { + "version": "4.0.8", + "resolved": "https://registry.npmjs.org/micromatch/-/micromatch-4.0.8.tgz", + "integrity": "sha512-PXwfBhYu0hBCPw8Dn0E+WDYb7af3dSLVWKi3HGv84IdF4TyFoC0ysxFd0Goxw7nSv4T/PzEJQxsYsEiFCKo2BA==", + "dev": true, + "license": "MIT", + "dependencies": { + "braces": "^3.0.3", + "picomatch": "^2.3.1" + }, + "engines": { + "node": ">=8.6" + } + }, + "node_modules/mimic-fn": { + "version": "2.1.0", + "resolved": "https://registry.npmjs.org/mimic-fn/-/mimic-fn-2.1.0.tgz", + "integrity": "sha512-OqbOk5oEQeAZ8WXWydlu9HJjz9WVdEIvamMCcXmuqUYjTknH/sqsWvhQ3vgwKFRR1HpjvNBKQ37nbJgYzGqGcg==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=6" + } + }, + "node_modules/minimatch": { + "version": "9.0.5", + "resolved": "https://registry.npmjs.org/minimatch/-/minimatch-9.0.5.tgz", + "integrity": "sha512-G6T0ZX48xgozx7587koeX9Ys2NYy6Gmv//P89sEte9V9whIapMNF4idKxnW2QtCcLiTWlb/wfCabAtAFWhhBow==", + "dev": true, + "license": "ISC", + "dependencies": { + "brace-expansion": "^2.0.1" + }, + "engines": { + "node": ">=16 || 14 >=14.17" + }, + "funding": { + "url": "https://github.com/sponsors/isaacs" + } + }, + "node_modules/minipass": { + "version": "7.1.2", + "resolved": "https://registry.npmjs.org/minipass/-/minipass-7.1.2.tgz", + "integrity": "sha512-qOOzS1cBTWYF4BH8fVePDBOO9iptMnGUEZwNc/cMWnTV2nVLZ7VoNWEPHkYczZA0pdoA7dl6e7FL659nX9S2aw==", + "dev": true, + "license": "ISC", + "engines": { + "node": ">=16 || 14 >=14.17" + } + }, + "node_modules/ms": { + "version": "2.1.3", + "resolved": "https://registry.npmjs.org/ms/-/ms-2.1.3.tgz", + "integrity": "sha512-6FlzubTLZG3J2a/NVCAleEhjzq5oxgHyaCU9yYXvcLsvoVaHJq/s5xXI6/XXP6tz7R9xAOtHnSO/tXtF3WRTlA==", + "dev": true, + "license": "MIT" + }, + "node_modules/napi-postinstall": { + "version": "0.3.4", + "resolved": "https://registry.npmjs.org/napi-postinstall/-/napi-postinstall-0.3.4.tgz", + "integrity": "sha512-PHI5f1O0EP5xJ9gQmFGMS6IZcrVvTjpXjz7Na41gTE7eE2hK11lg04CECCYEEjdc17EV4DO+fkGEtt7TpTaTiQ==", + "dev": true, + "license": "MIT", + "bin": { + "napi-postinstall": "lib/cli.js" + }, + "engines": { + "node": "^12.20.0 || ^14.18.0 || >=16.0.0" + }, + "funding": { + "url": "https://opencollective.com/napi-postinstall" + } + }, + "node_modules/natural-compare": { + "version": "1.4.0", + "resolved": "https://registry.npmjs.org/natural-compare/-/natural-compare-1.4.0.tgz", + "integrity": "sha512-OWND8ei3VtNC9h7V60qff3SVobHr996CTwgxubgyQYEpg290h9J0buyECNNJexkFm5sOajh5G116RYA1c8ZMSw==", + "dev": true, + "license": "MIT" + }, + "node_modules/node-int64": { + "version": "0.4.0", + "resolved": "https://registry.npmjs.org/node-int64/-/node-int64-0.4.0.tgz", + "integrity": "sha512-O5lz91xSOeoXP6DulyHfllpq+Eg00MWitZIbtPfoSEvqIHdl5gfcY6hYzDWnj0qD5tz52PI08u9qUvSVeUBeHw==", + "dev": true, + "license": "MIT" + }, + "node_modules/node-releases": { + "version": "2.0.27", + "resolved": "https://registry.npmjs.org/node-releases/-/node-releases-2.0.27.tgz", + "integrity": "sha512-nmh3lCkYZ3grZvqcCH+fjmQ7X+H0OeZgP40OierEaAptX4XofMh5kwNbWh7lBduUzCcV/8kZ+NDLCwm2iorIlA==", + "dev": true, + "license": "MIT" + }, + "node_modules/normalize-path": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/normalize-path/-/normalize-path-3.0.0.tgz", + "integrity": "sha512-6eZs5Ls3WtCisHWp9S2GUy8dqkpGi4BVSz3GaqiE6ezub0512ESztXUwUB6C6IKbQkY2Pnb/mD4WYojCRwcwLA==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/npm-run-path": { + "version": "4.0.1", + "resolved": "https://registry.npmjs.org/npm-run-path/-/npm-run-path-4.0.1.tgz", + "integrity": "sha512-S48WzZW777zhNIrn7gxOlISNAqi9ZC/uQFnRdbeIHhZhCA6UqpkOT8T1G7BvfdgP4Er8gF4sUbaS0i7QvIfCWw==", + "dev": true, + "license": "MIT", + "dependencies": { + "path-key": "^3.0.0" + }, + "engines": { + "node": ">=8" + } + }, + "node_modules/once": { + "version": "1.4.0", + "resolved": "https://registry.npmjs.org/once/-/once-1.4.0.tgz", + "integrity": "sha512-lNaJgI+2Q5URQBkccEKHTQOPaXdUxnZZElQTZY0MFUAuaEqe1E+Nyvgdz/aIyNi6Z9MzO5dv1H8n58/GELp3+w==", + "dev": true, + "license": "ISC", + "dependencies": { + "wrappy": "1" + } + }, + "node_modules/onetime": { + "version": "5.1.2", + "resolved": "https://registry.npmjs.org/onetime/-/onetime-5.1.2.tgz", + "integrity": "sha512-kbpaSSGJTWdAY5KPVeMOKXSrPtr8C8C7wodJbcsd51jRnmD+GZu8Y0VoU6Dm5Z4vWr0Ig/1NKuWRKf7j5aaYSg==", + "dev": true, + "license": "MIT", + "dependencies": { + "mimic-fn": "^2.1.0" + }, + "engines": { + "node": ">=6" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/p-limit": { + "version": "3.1.0", + "resolved": "https://registry.npmjs.org/p-limit/-/p-limit-3.1.0.tgz", + "integrity": "sha512-TYOanM3wGwNGsZN2cVTYPArw454xnXj5qmWF1bEoAc4+cU/ol7GVh7odevjp1FNHduHc3KZMcFduxU5Xc6uJRQ==", + "dev": true, + "license": "MIT", + "dependencies": { + "yocto-queue": "^0.1.0" + }, + "engines": { + "node": ">=10" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/p-locate": { + "version": "4.1.0", + "resolved": "https://registry.npmjs.org/p-locate/-/p-locate-4.1.0.tgz", + "integrity": "sha512-R79ZZ/0wAxKGu3oYMlz8jy/kbhsNrS7SKZ7PxEHBgJ5+F2mtFW2fK2cOtBh1cHYkQsbzFV7I+EoRKe6Yt0oK7A==", + "dev": true, + "license": "MIT", + "dependencies": { + "p-limit": "^2.2.0" + }, + "engines": { + "node": ">=8" + } + }, + "node_modules/p-locate/node_modules/p-limit": { + "version": "2.3.0", + "resolved": "https://registry.npmjs.org/p-limit/-/p-limit-2.3.0.tgz", + "integrity": "sha512-//88mFWSJx8lxCzwdAABTJL2MyWB12+eIY7MDL2SqLmAkeKU9qxRvWuSyTjm3FUmpBEMuFfckAIqEaVGUDxb6w==", + "dev": true, + "license": "MIT", + "dependencies": { + "p-try": "^2.0.0" + }, + "engines": { + "node": ">=6" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/p-try": { + "version": "2.2.0", + "resolved": "https://registry.npmjs.org/p-try/-/p-try-2.2.0.tgz", + "integrity": "sha512-R4nPAVTAU0B9D35/Gk3uJf/7XYbQcyohSKdvAxIRSNghFl4e71hVoGnBNQz9cWaXxO2I10KTC+3jMdvvoKw6dQ==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=6" + } + }, + "node_modules/package-json-from-dist": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/package-json-from-dist/-/package-json-from-dist-1.0.1.tgz", + "integrity": "sha512-UEZIS3/by4OC8vL3P2dTXRETpebLI2NiI5vIrjaD/5UtrkFX/tNbwjTSRAGC/+7CAo2pIcBaRgWmcBBHcsaCIw==", + "dev": true, + "license": "BlueOak-1.0.0" + }, + "node_modules/parse-json": { + "version": "5.2.0", + "resolved": "https://registry.npmjs.org/parse-json/-/parse-json-5.2.0.tgz", + "integrity": "sha512-ayCKvm/phCGxOkYRSCM82iDwct8/EonSEgCSxWxD7ve6jHggsFl4fZVQBPRNgQoKiuV/odhFrGzQXZwbifC8Rg==", + "dev": true, + "license": "MIT", + "dependencies": { + "@babel/code-frame": "^7.0.0", + "error-ex": "^1.3.1", + "json-parse-even-better-errors": "^2.3.0", + "lines-and-columns": "^1.1.6" + }, + "engines": { + "node": ">=8" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/path-exists": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/path-exists/-/path-exists-4.0.0.tgz", + "integrity": "sha512-ak9Qy5Q7jYb2Wwcey5Fpvg2KoAc/ZIhLSLOSBmRmygPsGwkVVt0fZa0qrtMz+m6tJTAHfZQ8FnmB4MG4LWy7/w==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=8" + } + }, + "node_modules/path-is-absolute": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/path-is-absolute/-/path-is-absolute-1.0.1.tgz", + "integrity": "sha512-AVbw3UJ2e9bq64vSaS9Am0fje1Pa8pbGqTTsmXfaIiMpnr5DlDhfJOuLj9Sf95ZPVDAUerDfEk88MPmPe7UCQg==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/path-key": { + "version": "3.1.1", + "resolved": "https://registry.npmjs.org/path-key/-/path-key-3.1.1.tgz", + "integrity": "sha512-ojmeN0qd+y0jszEtoY48r0Peq5dwMEkIlCOu6Q5f41lfkswXuKtYrhgoTpLnyIcHm24Uhqx+5Tqm2InSwLhE6Q==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=8" + } + }, + "node_modules/path-parse": { + "version": "1.0.7", + "resolved": "https://registry.npmjs.org/path-parse/-/path-parse-1.0.7.tgz", + "integrity": "sha512-LDJzPVEEEPR+y48z93A0Ed0yXb8pAByGWo/k5YYdYgpY2/2EsOsksJrq7lOHxryrVOn1ejG6oAp8ahvOIQD8sw==", + "dev": true, + "license": "MIT" + }, + "node_modules/path-scurry": { + "version": "1.11.1", + "resolved": "https://registry.npmjs.org/path-scurry/-/path-scurry-1.11.1.tgz", + "integrity": "sha512-Xa4Nw17FS9ApQFJ9umLiJS4orGjm7ZzwUrwamcGQuHSzDyth9boKDaycYdDcZDuqYATXw4HFXgaqWTctW/v1HA==", + "dev": true, + "license": "BlueOak-1.0.0", + "dependencies": { + "lru-cache": "^10.2.0", + "minipass": "^5.0.0 || ^6.0.2 || ^7.0.0" + }, + "engines": { + "node": ">=16 || 14 >=14.18" + }, + "funding": { + "url": "https://github.com/sponsors/isaacs" + } + }, + "node_modules/path-scurry/node_modules/lru-cache": { + "version": "10.4.3", + "resolved": "https://registry.npmjs.org/lru-cache/-/lru-cache-10.4.3.tgz", + "integrity": "sha512-JNAzZcXrCt42VGLuYz0zfAzDfAvJWW6AfYlDBQyDV5DClI2m5sAmK+OIO7s59XfsRsWHp02jAJrRadPRGTt6SQ==", + "dev": true, + "license": "ISC" + }, + "node_modules/picocolors": { + "version": "1.1.1", + "resolved": "https://registry.npmjs.org/picocolors/-/picocolors-1.1.1.tgz", + "integrity": "sha512-xceH2snhtb5M9liqDsmEw56le376mTZkEX/jEb/RxNFyegNul7eNslCXP9FDj/Lcu0X8KEyMceP2ntpaHrDEVA==", + "dev": true, + "license": "ISC" + }, + "node_modules/picomatch": { + "version": "2.3.1", + "resolved": "https://registry.npmjs.org/picomatch/-/picomatch-2.3.1.tgz", + "integrity": "sha512-JU3teHTNjmE2VCGFzuY8EXzCDVwEqB2a8fsIvwaStHhAWJEeVd1o1QD80CU6+ZdEXXSLbSsuLwJjkCBWqRQUVA==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=8.6" + }, + "funding": { + "url": "https://github.com/sponsors/jonschlinkert" + } + }, + "node_modules/pirates": { + "version": "4.0.7", + "resolved": "https://registry.npmjs.org/pirates/-/pirates-4.0.7.tgz", + "integrity": "sha512-TfySrs/5nm8fQJDcBDuUng3VOUKsd7S+zqvbOTiGXHfxX4wK31ard+hoNuvkicM/2YFzlpDgABOevKSsB4G/FA==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">= 6" + } + }, + "node_modules/pkg-dir": { + "version": "4.2.0", + "resolved": "https://registry.npmjs.org/pkg-dir/-/pkg-dir-4.2.0.tgz", + "integrity": "sha512-HRDzbaKjC+AOWVXxAU/x54COGeIv9eb+6CkDSQoNTt4XyWoIJvuPsXizxu/Fr23EiekbtZwmh1IcIG/l/a10GQ==", + "dev": true, + "license": "MIT", + "dependencies": { + "find-up": "^4.0.0" + }, + "engines": { + "node": ">=8" + } + }, + "node_modules/playwright": { + "version": "1.56.1", + "resolved": "https://registry.npmjs.org/playwright/-/playwright-1.56.1.tgz", + "integrity": "sha512-aFi5B0WovBHTEvpM3DzXTUaeN6eN0qWnTkKx4NQaH4Wvcmc153PdaY2UBdSYKaGYw+UyWXSVyxDUg5DoPEttjw==", + "dev": true, + "license": "Apache-2.0", + "dependencies": { + "playwright-core": "1.56.1" + }, + "bin": { + "playwright": "cli.js" + }, + "engines": { + "node": ">=18" + }, + "optionalDependencies": { + "fsevents": "2.3.2" + } + }, + "node_modules/playwright-core": { + "version": "1.56.1", + "resolved": "https://registry.npmjs.org/playwright-core/-/playwright-core-1.56.1.tgz", + "integrity": "sha512-hutraynyn31F+Bifme+Ps9Vq59hKuUCz7H1kDOcBs+2oGguKkWTU50bBWrtz34OUWmIwpBTWDxaRPXrIXkgvmQ==", + "dev": true, + "license": "Apache-2.0", + "bin": { + "playwright-core": "cli.js" + }, + "engines": { + "node": ">=18" + } + }, + "node_modules/playwright/node_modules/fsevents": { + "version": "2.3.2", + "resolved": "https://registry.npmjs.org/fsevents/-/fsevents-2.3.2.tgz", + "integrity": "sha512-xiqMQR4xAeHTuB9uWm+fFRcIOgKBMiOBP+eXiyT7jsgVCq1bkVygt00oASowB7EdtpOHaaPgKt812P9ab+DDKA==", + "dev": true, + "hasInstallScript": true, + "license": "MIT", + "optional": true, + "os": [ + "darwin" + ], + "engines": { + "node": "^8.16.0 || ^10.6.0 || >=11.0.0" + } + }, + "node_modules/pretty-format": { + "version": "30.2.0", + "resolved": "https://registry.npmjs.org/pretty-format/-/pretty-format-30.2.0.tgz", + "integrity": "sha512-9uBdv/B4EefsuAL+pWqueZyZS2Ba+LxfFeQ9DN14HU4bN8bhaxKdkpjpB6fs9+pSjIBu+FXQHImEg8j/Lw0+vA==", + "dev": true, + "license": "MIT", + "dependencies": { + "@jest/schemas": "30.0.5", + "ansi-styles": "^5.2.0", + "react-is": "^18.3.1" + }, + "engines": { + "node": "^18.14.0 || ^20.0.0 || ^22.0.0 || >=24.0.0" + } + }, + "node_modules/pretty-format/node_modules/ansi-styles": { + "version": "5.2.0", + "resolved": "https://registry.npmjs.org/ansi-styles/-/ansi-styles-5.2.0.tgz", + "integrity": "sha512-Cxwpt2SfTzTtXcfOlzGEee8O+c+MmUgGrNiBcXnuWxuFJHe6a5Hz7qwhwe5OgaSYI0IJvkLqWX1ASG+cJOkEiA==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=10" + }, + "funding": { + "url": "https://github.com/chalk/ansi-styles?sponsor=1" + } + }, + "node_modules/pure-rand": { + "version": "7.0.1", + "resolved": "https://registry.npmjs.org/pure-rand/-/pure-rand-7.0.1.tgz", + "integrity": "sha512-oTUZM/NAZS8p7ANR3SHh30kXB+zK2r2BPcEn/awJIbOvq82WoMN4p62AWWp3Hhw50G0xMsw1mhIBLqHw64EcNQ==", + "dev": true, + "funding": [ + { + "type": "individual", + "url": "https://github.com/sponsors/dubzzz" + }, + { + "type": "opencollective", + "url": "https://opencollective.com/fast-check" + } + ], + "license": "MIT" + }, + "node_modules/react-is": { + "version": "18.3.1", + "resolved": "https://registry.npmjs.org/react-is/-/react-is-18.3.1.tgz", + "integrity": "sha512-/LLMVyas0ljjAtoYiPqYiL8VWXzUUdThrmU5+n20DZv+a+ClRoevUzw5JxU+Ieh5/c87ytoTBV9G1FiKfNJdmg==", + "dev": true, + "license": "MIT" + }, + "node_modules/regenerate": { + "version": "1.4.2", + "resolved": "https://registry.npmjs.org/regenerate/-/regenerate-1.4.2.tgz", + "integrity": "sha512-zrceR/XhGYU/d/opr2EKO7aRHUeiBI8qjtfHqADTwZd6Szfy16la6kqD0MIUs5z5hx6AaKa+PixpPrR289+I0A==", + "dev": true, + "license": "MIT" + }, + "node_modules/regenerate-unicode-properties": { + "version": "10.2.2", + "resolved": "https://registry.npmjs.org/regenerate-unicode-properties/-/regenerate-unicode-properties-10.2.2.tgz", + "integrity": "sha512-m03P+zhBeQd1RGnYxrGyDAPpWX/epKirLrp8e3qevZdVkKtnCrjjWczIbYc8+xd6vcTStVlqfycTx1KR4LOr0g==", + "dev": true, + "license": "MIT", + "dependencies": { + "regenerate": "^1.4.2" + }, + "engines": { + "node": ">=4" + } + }, + "node_modules/regexpu-core": { + "version": "6.4.0", + "resolved": "https://registry.npmjs.org/regexpu-core/-/regexpu-core-6.4.0.tgz", + "integrity": "sha512-0ghuzq67LI9bLXpOX/ISfve/Mq33a4aFRzoQYhnnok1JOFpmE/A2TBGkNVenOGEeSBCjIiWcc6MVOG5HEQv0sA==", + "dev": true, + "license": "MIT", + "dependencies": { + "regenerate": "^1.4.2", + "regenerate-unicode-properties": "^10.2.2", + "regjsgen": "^0.8.0", + "regjsparser": "^0.13.0", + "unicode-match-property-ecmascript": "^2.0.0", + "unicode-match-property-value-ecmascript": "^2.2.1" + }, + "engines": { + "node": ">=4" + } + }, + "node_modules/regjsgen": { + "version": "0.8.0", + "resolved": "https://registry.npmjs.org/regjsgen/-/regjsgen-0.8.0.tgz", + "integrity": "sha512-RvwtGe3d7LvWiDQXeQw8p5asZUmfU1G/l6WbUXeHta7Y2PEIvBTwH6E2EfmYUK8pxcxEdEmaomqyp0vZZ7C+3Q==", + "dev": true, + "license": "MIT" + }, + "node_modules/regjsparser": { + "version": "0.13.0", + "resolved": "https://registry.npmjs.org/regjsparser/-/regjsparser-0.13.0.tgz", + "integrity": "sha512-NZQZdC5wOE/H3UT28fVGL+ikOZcEzfMGk/c3iN9UGxzWHMa1op7274oyiUVrAG4B2EuFhus8SvkaYnhvW92p9Q==", + "dev": true, + "license": "BSD-2-Clause", + "dependencies": { + "jsesc": "~3.1.0" + }, + "bin": { + "regjsparser": "bin/parser" + } + }, + "node_modules/require-directory": { + "version": "2.1.1", + "resolved": "https://registry.npmjs.org/require-directory/-/require-directory-2.1.1.tgz", + "integrity": "sha512-fGxEI7+wsG9xrvdjsrlmL22OMTTiHRwAMroiEeMgq8gzoLC/PQr7RsRDSTLUg/bZAZtF+TVIkHc6/4RIKrui+Q==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/resolve": { + "version": "1.22.11", + "resolved": "https://registry.npmjs.org/resolve/-/resolve-1.22.11.tgz", + "integrity": "sha512-RfqAvLnMl313r7c9oclB1HhUEAezcpLjz95wFH4LVuhk9JF/r22qmVP9AMmOU4vMX7Q8pN8jwNg/CSpdFnMjTQ==", + "dev": true, + "license": "MIT", + "dependencies": { + "is-core-module": "^2.16.1", + "path-parse": "^1.0.7", + "supports-preserve-symlinks-flag": "^1.0.0" + }, + "bin": { + "resolve": "bin/resolve" + }, + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/resolve-cwd": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/resolve-cwd/-/resolve-cwd-3.0.0.tgz", + "integrity": "sha512-OrZaX2Mb+rJCpH/6CpSqt9xFVpN++x01XnN2ie9g6P5/3xelLAkXWVADpdz1IHD/KFfEXyE6V0U01OQ3UO2rEg==", + "dev": true, + "license": "MIT", + "dependencies": { + "resolve-from": "^5.0.0" + }, + "engines": { + "node": ">=8" + } + }, + "node_modules/resolve-from": { + "version": "5.0.0", + "resolved": "https://registry.npmjs.org/resolve-from/-/resolve-from-5.0.0.tgz", + "integrity": "sha512-qYg9KP24dD5qka9J47d0aVky0N+b4fTU89LN9iDnjB5waksiC49rvMB0PrUJQGoTmH50XPiqOvAjDfaijGxYZw==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=8" + } + }, + "node_modules/semver": { + "version": "6.3.1", + "resolved": "https://registry.npmjs.org/semver/-/semver-6.3.1.tgz", + "integrity": "sha512-BR7VvDCVHO+q2xBEWskxS6DJE1qRnb7DxzUrogb71CWoSficBxYsiAGd+Kl0mmq/MprG9yArRkyrQxTO6XjMzA==", + "dev": true, + "license": "ISC", + "bin": { + "semver": "bin/semver.js" + } + }, + "node_modules/shebang-command": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/shebang-command/-/shebang-command-2.0.0.tgz", + "integrity": "sha512-kHxr2zZpYtdmrN1qDjrrX/Z1rR1kG8Dx+gkpK1G4eXmvXswmcE1hTWBWYUzlraYw1/yZp6YuDY77YtvbN0dmDA==", + "dev": true, + "license": "MIT", + "dependencies": { + "shebang-regex": "^3.0.0" + }, + "engines": { + "node": ">=8" + } + }, + "node_modules/shebang-regex": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/shebang-regex/-/shebang-regex-3.0.0.tgz", + "integrity": "sha512-7++dFhtcx3353uBaq8DDR4NuxBetBzC7ZQOhmTQInHEd6bSrXdiEyzCvG07Z44UYdLShWUyXt5M/yhz8ekcb1A==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=8" + } + }, + "node_modules/signal-exit": { + "version": "4.1.0", + "resolved": "https://registry.npmjs.org/signal-exit/-/signal-exit-4.1.0.tgz", + "integrity": "sha512-bzyZ1e88w9O1iNJbKnOlvYTrWPDl46O1bG0D3XInv+9tkPrxrN8jUUTiFlDkkmKWgn1M6CfIA13SuGqOa9Korw==", + "dev": true, + "license": "ISC", + "engines": { + "node": ">=14" + }, + "funding": { + "url": "https://github.com/sponsors/isaacs" + } + }, + "node_modules/slash": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/slash/-/slash-3.0.0.tgz", + "integrity": "sha512-g9Q1haeby36OSStwb4ntCGGGaKsaVSjQ68fBxoQcutl5fS1vuY18H3wSt3jFyFtrkx+Kz0V1G85A4MyAdDMi2Q==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=8" + } + }, + "node_modules/source-map": { + "version": "0.6.1", + "resolved": "https://registry.npmjs.org/source-map/-/source-map-0.6.1.tgz", + "integrity": "sha512-UjgapumWlbMhkBgzT7Ykc5YXUT46F0iKu8SGXq0bcwP5dz/h0Plj6enJqjz1Zbq2l5WaqYnrVbwWOWMyF3F47g==", + "dev": true, + "license": "BSD-3-Clause", + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/source-map-support": { + "version": "0.5.13", + "resolved": "https://registry.npmjs.org/source-map-support/-/source-map-support-0.5.13.tgz", + "integrity": "sha512-SHSKFHadjVA5oR4PPqhtAVdcBWwRYVd6g6cAXnIbRiIwc2EhPrTuKUBdSLvlEKyIP3GCf89fltvcZiP9MMFA1w==", + "dev": true, + "license": "MIT", + "dependencies": { + "buffer-from": "^1.0.0", + "source-map": "^0.6.0" + } + }, + "node_modules/sprintf-js": { + "version": "1.0.3", + "resolved": "https://registry.npmjs.org/sprintf-js/-/sprintf-js-1.0.3.tgz", + "integrity": "sha512-D9cPgkvLlV3t3IzL0D0YLvGA9Ahk4PcvVwUbN0dSGr1aP0Nrt4AEnTUbuGvquEC0mA64Gqt1fzirlRs5ibXx8g==", + "dev": true, + "license": "BSD-3-Clause" + }, + "node_modules/stack-utils": { + "version": "2.0.6", + "resolved": "https://registry.npmjs.org/stack-utils/-/stack-utils-2.0.6.tgz", + "integrity": "sha512-XlkWvfIm6RmsWtNJx+uqtKLS8eqFbxUg0ZzLXqY0caEy9l7hruX8IpiDnjsLavoBgqCCR71TqWO8MaXYheJ3RQ==", + "dev": true, + "license": "MIT", + "dependencies": { + "escape-string-regexp": "^2.0.0" + }, + "engines": { + "node": ">=10" + } + }, + "node_modules/string-length": { + "version": "4.0.2", + "resolved": "https://registry.npmjs.org/string-length/-/string-length-4.0.2.tgz", + "integrity": "sha512-+l6rNN5fYHNhZZy41RXsYptCjA2Igmq4EG7kZAYFQI1E1VTXarr6ZPXBg6eq7Y6eK4FEhY6AJlyuFIb/v/S0VQ==", + "dev": true, + "license": "MIT", + "dependencies": { + "char-regex": "^1.0.2", + "strip-ansi": "^6.0.0" + }, + "engines": { + "node": ">=10" + } + }, + "node_modules/string-length/node_modules/ansi-regex": { + "version": "5.0.1", + "resolved": "https://registry.npmjs.org/ansi-regex/-/ansi-regex-5.0.1.tgz", + "integrity": "sha512-quJQXlTSUGL2LH9SUXo8VwsY4soanhgo6LNSm84E1LBcE8s3O0wpdiRzyR9z/ZZJMlMWv37qOOb9pdJlMUEKFQ==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=8" + } + }, + "node_modules/string-length/node_modules/strip-ansi": { + "version": "6.0.1", + "resolved": "https://registry.npmjs.org/strip-ansi/-/strip-ansi-6.0.1.tgz", + "integrity": "sha512-Y38VPSHcqkFrCpFnQ9vuSXmquuv5oXOKpGeT6aGrr3o3Gc9AlVa6JBfUSOCnbxGGZF+/0ooI7KrPuUSztUdU5A==", + "dev": true, + "license": "MIT", + "dependencies": { + "ansi-regex": "^5.0.1" + }, + "engines": { + "node": ">=8" + } + }, + "node_modules/string-width": { + "version": "5.1.2", + "resolved": "https://registry.npmjs.org/string-width/-/string-width-5.1.2.tgz", + "integrity": "sha512-HnLOCR3vjcY8beoNLtcjZ5/nxn2afmME6lhrDrebokqMap+XbeW8n9TXpPDOqdGK5qcI3oT0GKTW6wC7EMiVqA==", + "dev": true, + "license": "MIT", + "dependencies": { + "eastasianwidth": "^0.2.0", + "emoji-regex": "^9.2.2", + "strip-ansi": "^7.0.1" + }, + "engines": { + "node": ">=12" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/string-width-cjs": { + "name": "string-width", + "version": "4.2.3", + "resolved": "https://registry.npmjs.org/string-width/-/string-width-4.2.3.tgz", + "integrity": "sha512-wKyQRQpjJ0sIp62ErSZdGsjMJWsap5oRNihHhu6G7JVO/9jIB6UyevL+tXuOqrng8j/cxKTWyWUwvSTriiZz/g==", + "dev": true, + "license": "MIT", + "dependencies": { + "emoji-regex": "^8.0.0", + "is-fullwidth-code-point": "^3.0.0", + "strip-ansi": "^6.0.1" + }, + "engines": { + "node": ">=8" + } + }, + "node_modules/string-width-cjs/node_modules/ansi-regex": { + "version": "5.0.1", + "resolved": "https://registry.npmjs.org/ansi-regex/-/ansi-regex-5.0.1.tgz", + "integrity": "sha512-quJQXlTSUGL2LH9SUXo8VwsY4soanhgo6LNSm84E1LBcE8s3O0wpdiRzyR9z/ZZJMlMWv37qOOb9pdJlMUEKFQ==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=8" + } + }, + "node_modules/string-width-cjs/node_modules/emoji-regex": { + "version": "8.0.0", + "resolved": "https://registry.npmjs.org/emoji-regex/-/emoji-regex-8.0.0.tgz", + "integrity": "sha512-MSjYzcWNOA0ewAHpz0MxpYFvwg6yjy1NG3xteoqz644VCo/RPgnr1/GGt+ic3iJTzQ8Eu3TdM14SawnVUmGE6A==", + "dev": true, + "license": "MIT" + }, + "node_modules/string-width-cjs/node_modules/strip-ansi": { + "version": "6.0.1", + "resolved": "https://registry.npmjs.org/strip-ansi/-/strip-ansi-6.0.1.tgz", + "integrity": "sha512-Y38VPSHcqkFrCpFnQ9vuSXmquuv5oXOKpGeT6aGrr3o3Gc9AlVa6JBfUSOCnbxGGZF+/0ooI7KrPuUSztUdU5A==", + "dev": true, + "license": "MIT", + "dependencies": { + "ansi-regex": "^5.0.1" + }, + "engines": { + "node": ">=8" + } + }, + "node_modules/strip-ansi": { + "version": "7.1.2", + "resolved": "https://registry.npmjs.org/strip-ansi/-/strip-ansi-7.1.2.tgz", + "integrity": "sha512-gmBGslpoQJtgnMAvOVqGZpEz9dyoKTCzy2nfz/n8aIFhN/jCE/rCmcxabB6jOOHV+0WNnylOxaxBQPSvcWklhA==", + "dev": true, + "license": "MIT", + "dependencies": { + "ansi-regex": "^6.0.1" + }, + "engines": { + "node": ">=12" + }, + "funding": { + "url": "https://github.com/chalk/strip-ansi?sponsor=1" + } + }, + "node_modules/strip-ansi-cjs": { + "name": "strip-ansi", + "version": "6.0.1", + "resolved": "https://registry.npmjs.org/strip-ansi/-/strip-ansi-6.0.1.tgz", + "integrity": "sha512-Y38VPSHcqkFrCpFnQ9vuSXmquuv5oXOKpGeT6aGrr3o3Gc9AlVa6JBfUSOCnbxGGZF+/0ooI7KrPuUSztUdU5A==", + "dev": true, + "license": "MIT", + "dependencies": { + "ansi-regex": "^5.0.1" + }, + "engines": { + "node": ">=8" + } + }, + "node_modules/strip-ansi-cjs/node_modules/ansi-regex": { + "version": "5.0.1", + "resolved": "https://registry.npmjs.org/ansi-regex/-/ansi-regex-5.0.1.tgz", + "integrity": "sha512-quJQXlTSUGL2LH9SUXo8VwsY4soanhgo6LNSm84E1LBcE8s3O0wpdiRzyR9z/ZZJMlMWv37qOOb9pdJlMUEKFQ==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=8" + } + }, + "node_modules/strip-bom": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/strip-bom/-/strip-bom-4.0.0.tgz", + "integrity": "sha512-3xurFv5tEgii33Zi8Jtp55wEIILR9eh34FAW00PZf+JnSsTmV/ioewSgQl97JHvgjoRGwPShsWm+IdrxB35d0w==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=8" + } + }, + "node_modules/strip-final-newline": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/strip-final-newline/-/strip-final-newline-2.0.0.tgz", + "integrity": "sha512-BrpvfNAE3dcvq7ll3xVumzjKjZQ5tI1sEUIKr3Uoks0XUl45St3FlatVqef9prk4jRDzhW6WZg+3bk93y6pLjA==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=6" + } + }, + "node_modules/strip-json-comments": { + "version": "3.1.1", + "resolved": "https://registry.npmjs.org/strip-json-comments/-/strip-json-comments-3.1.1.tgz", + "integrity": "sha512-6fPc+R4ihwqP6N/aIv2f1gMH8lOVtWQHoqC4yK6oSDVVocumAsfCqjkXnqiYMhmMwS/mEHLp7Vehlt3ql6lEig==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=8" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/supports-color": { + "version": "7.2.0", + "resolved": "https://registry.npmjs.org/supports-color/-/supports-color-7.2.0.tgz", + "integrity": "sha512-qpCAvRl9stuOHveKsn7HncJRvv501qIacKzQlO/+Lwxc9+0q2wLyv4Dfvt80/DPn2pqOBsJdDiogXGR9+OvwRw==", + "dev": true, + "license": "MIT", + "dependencies": { + "has-flag": "^4.0.0" + }, + "engines": { + "node": ">=8" + } + }, + "node_modules/supports-preserve-symlinks-flag": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/supports-preserve-symlinks-flag/-/supports-preserve-symlinks-flag-1.0.0.tgz", + "integrity": "sha512-ot0WnXS9fgdkgIcePe6RHNk1WA8+muPa6cSjeR3V8K27q9BB1rTE3R1p7Hv0z1ZyAc8s6Vvv8DIyWf681MAt0w==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/synckit": { + "version": "0.11.11", + "resolved": "https://registry.npmjs.org/synckit/-/synckit-0.11.11.tgz", + "integrity": "sha512-MeQTA1r0litLUf0Rp/iisCaL8761lKAZHaimlbGK4j0HysC4PLfqygQj9srcs0m2RdtDYnF8UuYyKpbjHYp7Jw==", + "dev": true, + "license": "MIT", + "dependencies": { + "@pkgr/core": "^0.2.9" + }, + "engines": { + "node": "^14.18.0 || >=16.0.0" + }, + "funding": { + "url": "https://opencollective.com/synckit" + } + }, + "node_modules/test-exclude": { + "version": "6.0.0", + "resolved": "https://registry.npmjs.org/test-exclude/-/test-exclude-6.0.0.tgz", + "integrity": "sha512-cAGWPIyOHU6zlmg88jwm7VRyXnMN7iV68OGAbYDk/Mh/xC/pzVPlQtY6ngoIH/5/tciuhGfvESU8GrHrcxD56w==", + "dev": true, + "license": "ISC", + "dependencies": { + "@istanbuljs/schema": "^0.1.2", + "glob": "^7.1.4", + "minimatch": "^3.0.4" + }, + "engines": { + "node": ">=8" + } + }, + "node_modules/test-exclude/node_modules/brace-expansion": { + "version": "1.1.12", + "resolved": "https://registry.npmjs.org/brace-expansion/-/brace-expansion-1.1.12.tgz", + "integrity": "sha512-9T9UjW3r0UW5c1Q7GTwllptXwhvYmEzFhzMfZ9H7FQWt+uZePjZPjBP/W1ZEyZ1twGWom5/56TF4lPcqjnDHcg==", + "dev": true, + "license": "MIT", + "dependencies": { + "balanced-match": "^1.0.0", + "concat-map": "0.0.1" + } + }, + "node_modules/test-exclude/node_modules/glob": { + "version": "7.2.3", + "resolved": "https://registry.npmjs.org/glob/-/glob-7.2.3.tgz", + "integrity": "sha512-nFR0zLpU2YCaRxwoCJvL6UvCH2JFyFVIvwTLsIf21AuHlMskA1hhTdk+LlYJtOlYt9v6dvszD2BGRqBL+iQK9Q==", + "deprecated": "Glob versions prior to v9 are no longer supported", + "dev": true, + "license": "ISC", + "dependencies": { + "fs.realpath": "^1.0.0", + "inflight": "^1.0.4", + "inherits": "2", + "minimatch": "^3.1.1", + "once": "^1.3.0", + "path-is-absolute": "^1.0.0" + }, + "engines": { + "node": "*" + }, + "funding": { + "url": "https://github.com/sponsors/isaacs" + } + }, + "node_modules/test-exclude/node_modules/minimatch": { + "version": "3.1.2", + "resolved": "https://registry.npmjs.org/minimatch/-/minimatch-3.1.2.tgz", + "integrity": "sha512-J7p63hRiAjw1NDEww1W7i37+ByIrOWO5XQQAzZ3VOcL0PNybwpfmV/N05zFAzwQ9USyEcX6t3UO+K5aqBQOIHw==", + "dev": true, + "license": "ISC", + "dependencies": { + "brace-expansion": "^1.1.7" + }, + "engines": { + "node": "*" + } + }, + "node_modules/tmpl": { + "version": "1.0.5", + "resolved": "https://registry.npmjs.org/tmpl/-/tmpl-1.0.5.tgz", + "integrity": "sha512-3f0uOEAQwIqGuWW2MVzYg8fV/QNnc/IpuJNG837rLuczAaLVHslWHZQj4IGiEl5Hs3kkbhwL9Ab7Hrsmuj+Smw==", + "dev": true, + "license": "BSD-3-Clause" + }, + "node_modules/to-regex-range": { + "version": "5.0.1", + "resolved": "https://registry.npmjs.org/to-regex-range/-/to-regex-range-5.0.1.tgz", + "integrity": "sha512-65P7iz6X5yEr1cwcgvQxbbIw7Uk3gOy5dIdtZ4rDveLqhrdJP+Li/Hx6tyK0NEb+2GCyneCMJiGqrADCSNk8sQ==", + "dev": true, + "license": "MIT", + "dependencies": { + "is-number": "^7.0.0" + }, + "engines": { + "node": ">=8.0" + } + }, + "node_modules/tslib": { + "version": "2.8.1", + "resolved": "https://registry.npmjs.org/tslib/-/tslib-2.8.1.tgz", + "integrity": "sha512-oJFu94HQb+KVduSUQL7wnpmqnfmLsOA/nAh6b6EH0wCEoK0/mPeXU6c3wKDV83MkOuHPRHtSXKKU99IBazS/2w==", + "dev": true, + "license": "0BSD", + "optional": true + }, + "node_modules/type-detect": { + "version": "4.0.8", + "resolved": "https://registry.npmjs.org/type-detect/-/type-detect-4.0.8.tgz", + "integrity": "sha512-0fr/mIH1dlO+x7TlcMy+bIDqKPsw/70tVyeHW787goQjhmqaZe10uwLujubK9q9Lg6Fiho1KUKDYz0Z7k7g5/g==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=4" + } + }, + "node_modules/type-fest": { + "version": "0.21.3", + "resolved": "https://registry.npmjs.org/type-fest/-/type-fest-0.21.3.tgz", + "integrity": "sha512-t0rzBq87m3fVcduHDUFhKmyyX+9eo6WQjZvf51Ea/M0Q7+T374Jp1aUiyUl0GKxp8M/OETVHSDvmkyPgvX+X2w==", + "dev": true, + "license": "(MIT OR CC0-1.0)", + "engines": { + "node": ">=10" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/typescript": { + "version": "5.9.3", + "resolved": "https://registry.npmjs.org/typescript/-/typescript-5.9.3.tgz", + "integrity": "sha512-jl1vZzPDinLr9eUt3J/t7V6FgNEw9QjvBPdysz9KfQDD41fQrC2Y4vKQdiaUpFT4bXlb1RHhLpp8wtm6M5TgSw==", + "dev": true, + "license": "Apache-2.0", + "bin": { + "tsc": "bin/tsc", + "tsserver": "bin/tsserver" + }, + "engines": { + "node": ">=14.17" + } + }, + "node_modules/undici-types": { + "version": "7.16.0", + "resolved": "https://registry.npmjs.org/undici-types/-/undici-types-7.16.0.tgz", + "integrity": "sha512-Zz+aZWSj8LE6zoxD+xrjh4VfkIG8Ya6LvYkZqtUQGJPZjYl53ypCaUwWqo7eI0x66KBGeRo+mlBEkMSeSZ38Nw==", + "dev": true, + "license": "MIT" + }, + "node_modules/unicode-canonical-property-names-ecmascript": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/unicode-canonical-property-names-ecmascript/-/unicode-canonical-property-names-ecmascript-2.0.1.tgz", + "integrity": "sha512-dA8WbNeb2a6oQzAQ55YlT5vQAWGV9WXOsi3SskE3bcCdM0P4SDd+24zS/OCacdRq5BkdsRj9q3Pg6YyQoxIGqg==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=4" + } + }, + "node_modules/unicode-match-property-ecmascript": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/unicode-match-property-ecmascript/-/unicode-match-property-ecmascript-2.0.0.tgz", + "integrity": "sha512-5kaZCrbp5mmbz5ulBkDkbY0SsPOjKqVS35VpL9ulMPfSl0J0Xsm+9Evphv9CoIZFwre7aJoa94AY6seMKGVN5Q==", + "dev": true, + "license": "MIT", + "dependencies": { + "unicode-canonical-property-names-ecmascript": "^2.0.0", + "unicode-property-aliases-ecmascript": "^2.0.0" + }, + "engines": { + "node": ">=4" + } + }, + "node_modules/unicode-match-property-value-ecmascript": { + "version": "2.2.1", + "resolved": "https://registry.npmjs.org/unicode-match-property-value-ecmascript/-/unicode-match-property-value-ecmascript-2.2.1.tgz", + "integrity": "sha512-JQ84qTuMg4nVkx8ga4A16a1epI9H6uTXAknqxkGF/aFfRLw1xC/Bp24HNLaZhHSkWd3+84t8iXnp1J0kYcZHhg==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=4" + } + }, + "node_modules/unicode-property-aliases-ecmascript": { + "version": "2.2.0", + "resolved": "https://registry.npmjs.org/unicode-property-aliases-ecmascript/-/unicode-property-aliases-ecmascript-2.2.0.tgz", + "integrity": "sha512-hpbDzxUY9BFwX+UeBnxv3Sh1q7HFxj48DTmXchNgRa46lO8uj3/1iEn3MiNUYTg1g9ctIqXCCERn8gYZhHC5lQ==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=4" + } + }, + "node_modules/unrs-resolver": { + "version": "1.11.1", + "resolved": "https://registry.npmjs.org/unrs-resolver/-/unrs-resolver-1.11.1.tgz", + "integrity": "sha512-bSjt9pjaEBnNiGgc9rUiHGKv5l4/TGzDmYw3RhnkJGtLhbnnA/5qJj7x3dNDCRx/PJxu774LlH8lCOlB4hEfKg==", + "dev": true, + "hasInstallScript": true, + "license": "MIT", + "dependencies": { + "napi-postinstall": "^0.3.0" + }, + "funding": { + "url": "https://opencollective.com/unrs-resolver" + }, + "optionalDependencies": { + "@unrs/resolver-binding-android-arm-eabi": "1.11.1", + "@unrs/resolver-binding-android-arm64": "1.11.1", + "@unrs/resolver-binding-darwin-arm64": "1.11.1", + "@unrs/resolver-binding-darwin-x64": "1.11.1", + "@unrs/resolver-binding-freebsd-x64": "1.11.1", + "@unrs/resolver-binding-linux-arm-gnueabihf": "1.11.1", + "@unrs/resolver-binding-linux-arm-musleabihf": "1.11.1", + "@unrs/resolver-binding-linux-arm64-gnu": "1.11.1", + "@unrs/resolver-binding-linux-arm64-musl": "1.11.1", + "@unrs/resolver-binding-linux-ppc64-gnu": "1.11.1", + "@unrs/resolver-binding-linux-riscv64-gnu": "1.11.1", + "@unrs/resolver-binding-linux-riscv64-musl": "1.11.1", + "@unrs/resolver-binding-linux-s390x-gnu": "1.11.1", + "@unrs/resolver-binding-linux-x64-gnu": "1.11.1", + "@unrs/resolver-binding-linux-x64-musl": "1.11.1", + "@unrs/resolver-binding-wasm32-wasi": "1.11.1", + "@unrs/resolver-binding-win32-arm64-msvc": "1.11.1", + "@unrs/resolver-binding-win32-ia32-msvc": "1.11.1", + "@unrs/resolver-binding-win32-x64-msvc": "1.11.1" + } + }, + "node_modules/update-browserslist-db": { + "version": "1.1.4", + "resolved": "https://registry.npmjs.org/update-browserslist-db/-/update-browserslist-db-1.1.4.tgz", + "integrity": "sha512-q0SPT4xyU84saUX+tomz1WLkxUbuaJnR1xWt17M7fJtEJigJeWUNGUqrauFXsHnqev9y9JTRGwk13tFBuKby4A==", + "dev": true, + "funding": [ + { + "type": "opencollective", + "url": "https://opencollective.com/browserslist" + }, + { + "type": "tidelift", + "url": "https://tidelift.com/funding/github/npm/browserslist" + }, + { + "type": "github", + "url": "https://github.com/sponsors/ai" + } + ], + "license": "MIT", + "dependencies": { + "escalade": "^3.2.0", + "picocolors": "^1.1.1" + }, + "bin": { + "update-browserslist-db": "cli.js" + }, + "peerDependencies": { + "browserslist": ">= 4.21.0" + } + }, + "node_modules/v8-to-istanbul": { + "version": "9.3.0", + "resolved": "https://registry.npmjs.org/v8-to-istanbul/-/v8-to-istanbul-9.3.0.tgz", + "integrity": "sha512-kiGUalWN+rgBJ/1OHZsBtU4rXZOfj/7rKQxULKlIzwzQSvMJUUNgPwJEEh7gU6xEVxC0ahoOBvN2YI8GH6FNgA==", + "dev": true, + "license": "ISC", + "dependencies": { + "@jridgewell/trace-mapping": "^0.3.12", + "@types/istanbul-lib-coverage": "^2.0.1", + "convert-source-map": "^2.0.0" + }, + "engines": { + "node": ">=10.12.0" + } + }, + "node_modules/walker": { + "version": "1.0.8", + "resolved": "https://registry.npmjs.org/walker/-/walker-1.0.8.tgz", + "integrity": "sha512-ts/8E8l5b7kY0vlWLewOkDXMmPdLcVV4GmOQLyxuSswIJsweeFZtAsMF7k1Nszz+TYBQrlYRmzOnr398y1JemQ==", + "dev": true, + "license": "Apache-2.0", + "dependencies": { + "makeerror": "1.0.12" + } + }, + "node_modules/which": { + "version": "2.0.2", + "resolved": "https://registry.npmjs.org/which/-/which-2.0.2.tgz", + "integrity": "sha512-BLI3Tl1TW3Pvl70l3yq3Y64i+awpwXqsGBYWkkqMtnbXgrMD+yj7rhW0kuEDxzJaYXGjEW5ogapKNMEKNMjibA==", + "dev": true, + "license": "ISC", + "dependencies": { + "isexe": "^2.0.0" + }, + "bin": { + "node-which": "bin/node-which" + }, + "engines": { + "node": ">= 8" + } + }, + "node_modules/wrap-ansi": { + "version": "8.1.0", + "resolved": "https://registry.npmjs.org/wrap-ansi/-/wrap-ansi-8.1.0.tgz", + "integrity": "sha512-si7QWI6zUMq56bESFvagtmzMdGOtoxfR+Sez11Mobfc7tm+VkUckk9bW2UeffTGVUbOksxmSw0AA2gs8g71NCQ==", + "dev": true, + "license": "MIT", + "dependencies": { + "ansi-styles": "^6.1.0", + "string-width": "^5.0.1", + "strip-ansi": "^7.0.1" + }, + "engines": { + "node": ">=12" + }, + "funding": { + "url": "https://github.com/chalk/wrap-ansi?sponsor=1" + } + }, + "node_modules/wrap-ansi-cjs": { + "name": "wrap-ansi", + "version": "7.0.0", + "resolved": "https://registry.npmjs.org/wrap-ansi/-/wrap-ansi-7.0.0.tgz", + "integrity": "sha512-YVGIj2kamLSTxw6NsZjoBxfSwsn0ycdesmc4p+Q21c5zPuZ1pl+NfxVdxPtdHvmNVOQ6XSYG4AUtyt/Fi7D16Q==", + "dev": true, + "license": "MIT", + "dependencies": { + "ansi-styles": "^4.0.0", + "string-width": "^4.1.0", + "strip-ansi": "^6.0.0" + }, + "engines": { + "node": ">=10" + }, + "funding": { + "url": "https://github.com/chalk/wrap-ansi?sponsor=1" + } + }, + "node_modules/wrap-ansi-cjs/node_modules/ansi-regex": { + "version": "5.0.1", + "resolved": "https://registry.npmjs.org/ansi-regex/-/ansi-regex-5.0.1.tgz", + "integrity": "sha512-quJQXlTSUGL2LH9SUXo8VwsY4soanhgo6LNSm84E1LBcE8s3O0wpdiRzyR9z/ZZJMlMWv37qOOb9pdJlMUEKFQ==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=8" + } + }, + "node_modules/wrap-ansi-cjs/node_modules/emoji-regex": { + "version": "8.0.0", + "resolved": "https://registry.npmjs.org/emoji-regex/-/emoji-regex-8.0.0.tgz", + "integrity": "sha512-MSjYzcWNOA0ewAHpz0MxpYFvwg6yjy1NG3xteoqz644VCo/RPgnr1/GGt+ic3iJTzQ8Eu3TdM14SawnVUmGE6A==", + "dev": true, + "license": "MIT" + }, + "node_modules/wrap-ansi-cjs/node_modules/string-width": { + "version": "4.2.3", + "resolved": "https://registry.npmjs.org/string-width/-/string-width-4.2.3.tgz", + "integrity": "sha512-wKyQRQpjJ0sIp62ErSZdGsjMJWsap5oRNihHhu6G7JVO/9jIB6UyevL+tXuOqrng8j/cxKTWyWUwvSTriiZz/g==", + "dev": true, + "license": "MIT", + "dependencies": { + "emoji-regex": "^8.0.0", + "is-fullwidth-code-point": "^3.0.0", + "strip-ansi": "^6.0.1" + }, + "engines": { + "node": ">=8" + } + }, + "node_modules/wrap-ansi-cjs/node_modules/strip-ansi": { + "version": "6.0.1", + "resolved": "https://registry.npmjs.org/strip-ansi/-/strip-ansi-6.0.1.tgz", + "integrity": "sha512-Y38VPSHcqkFrCpFnQ9vuSXmquuv5oXOKpGeT6aGrr3o3Gc9AlVa6JBfUSOCnbxGGZF+/0ooI7KrPuUSztUdU5A==", + "dev": true, + "license": "MIT", + "dependencies": { + "ansi-regex": "^5.0.1" + }, + "engines": { + "node": ">=8" + } + }, + "node_modules/wrap-ansi/node_modules/ansi-styles": { + "version": "6.2.3", + "resolved": "https://registry.npmjs.org/ansi-styles/-/ansi-styles-6.2.3.tgz", + "integrity": "sha512-4Dj6M28JB+oAH8kFkTLUo+a2jwOFkuqb3yucU0CANcRRUbxS0cP0nZYCGjcc3BNXwRIsUVmDGgzawme7zvJHvg==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=12" + }, + "funding": { + "url": "https://github.com/chalk/ansi-styles?sponsor=1" + } + }, + "node_modules/wrappy": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/wrappy/-/wrappy-1.0.2.tgz", + "integrity": "sha512-l4Sp/DRseor9wL6EvV2+TuQn63dMkPjZ/sp9XkghTEbV9KlPS1xUsZ3u7/IQO4wxtcFB4bgpQPRcR3QCvezPcQ==", + "dev": true, + "license": "ISC" + }, + "node_modules/write-file-atomic": { + "version": "5.0.1", + "resolved": "https://registry.npmjs.org/write-file-atomic/-/write-file-atomic-5.0.1.tgz", + "integrity": "sha512-+QU2zd6OTD8XWIJCbffaiQeH9U73qIqafo1x6V1snCWYGJf6cVE0cDR4D8xRzcEnfI21IFrUPzPGtcPf8AC+Rw==", + "dev": true, + "license": "ISC", + "dependencies": { + "imurmurhash": "^0.1.4", + "signal-exit": "^4.0.1" + }, + "engines": { + "node": "^14.17.0 || ^16.13.0 || >=18.0.0" + } + }, + "node_modules/y18n": { + "version": "5.0.8", + "resolved": "https://registry.npmjs.org/y18n/-/y18n-5.0.8.tgz", + "integrity": "sha512-0pfFzegeDWJHJIAmTLRP2DwHjdF5s7jo9tuztdQxAhINCdvS+3nGINqPd00AphqJR/0LhANUS6/+7SCb98YOfA==", + "dev": true, + "license": "ISC", + "engines": { + "node": ">=10" + } + }, + "node_modules/yallist": { + "version": "3.1.1", + "resolved": "https://registry.npmjs.org/yallist/-/yallist-3.1.1.tgz", + "integrity": "sha512-a4UGQaWPH59mOXUYnAG2ewncQS4i4F43Tv3JoAM+s2VDAmS9NsK8GpDMLrCHPksFT7h3K6TOoUNn2pb7RoXx4g==", + "dev": true, + "license": "ISC" + }, + "node_modules/yargs": { + "version": "17.7.2", + "resolved": "https://registry.npmjs.org/yargs/-/yargs-17.7.2.tgz", + "integrity": "sha512-7dSzzRQ++CKnNI/krKnYRV7JKKPUXMEh61soaHKg9mrWEhzFWhFnxPxGl+69cD1Ou63C13NUPCnmIcrvqCuM6w==", + "dev": true, + "license": "MIT", + "dependencies": { + "cliui": "^8.0.1", + "escalade": "^3.1.1", + "get-caller-file": "^2.0.5", + "require-directory": "^2.1.1", + "string-width": "^4.2.3", + "y18n": "^5.0.5", + "yargs-parser": "^21.1.1" + }, + "engines": { + "node": ">=12" + } + }, + "node_modules/yargs-parser": { + "version": "21.1.1", + "resolved": "https://registry.npmjs.org/yargs-parser/-/yargs-parser-21.1.1.tgz", + "integrity": "sha512-tVpsJW7DdjecAiFpbIB1e3qxIQsE6NoPc5/eTdrbbIC4h0LVsWhnoa3g+m2HclBIujHzsxZ4VJVA+GUuc2/LBw==", + "dev": true, + "license": "ISC", + "engines": { + "node": ">=12" + } + }, + "node_modules/yargs/node_modules/ansi-regex": { + "version": "5.0.1", + "resolved": "https://registry.npmjs.org/ansi-regex/-/ansi-regex-5.0.1.tgz", + "integrity": "sha512-quJQXlTSUGL2LH9SUXo8VwsY4soanhgo6LNSm84E1LBcE8s3O0wpdiRzyR9z/ZZJMlMWv37qOOb9pdJlMUEKFQ==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=8" + } + }, + "node_modules/yargs/node_modules/emoji-regex": { + "version": "8.0.0", + "resolved": "https://registry.npmjs.org/emoji-regex/-/emoji-regex-8.0.0.tgz", + "integrity": "sha512-MSjYzcWNOA0ewAHpz0MxpYFvwg6yjy1NG3xteoqz644VCo/RPgnr1/GGt+ic3iJTzQ8Eu3TdM14SawnVUmGE6A==", + "dev": true, + "license": "MIT" + }, + "node_modules/yargs/node_modules/string-width": { + "version": "4.2.3", + "resolved": "https://registry.npmjs.org/string-width/-/string-width-4.2.3.tgz", + "integrity": "sha512-wKyQRQpjJ0sIp62ErSZdGsjMJWsap5oRNihHhu6G7JVO/9jIB6UyevL+tXuOqrng8j/cxKTWyWUwvSTriiZz/g==", + "dev": true, + "license": "MIT", + "dependencies": { + "emoji-regex": "^8.0.0", + "is-fullwidth-code-point": "^3.0.0", + "strip-ansi": "^6.0.1" + }, + "engines": { + "node": ">=8" + } + }, + "node_modules/yargs/node_modules/strip-ansi": { + "version": "6.0.1", + "resolved": "https://registry.npmjs.org/strip-ansi/-/strip-ansi-6.0.1.tgz", + "integrity": "sha512-Y38VPSHcqkFrCpFnQ9vuSXmquuv5oXOKpGeT6aGrr3o3Gc9AlVa6JBfUSOCnbxGGZF+/0ooI7KrPuUSztUdU5A==", + "dev": true, + "license": "MIT", + "dependencies": { + "ansi-regex": "^5.0.1" + }, + "engines": { + "node": ">=8" + } + }, + "node_modules/yocto-queue": { + "version": "0.1.0", + "resolved": "https://registry.npmjs.org/yocto-queue/-/yocto-queue-0.1.0.tgz", + "integrity": "sha512-rVksvsnNCdJ/ohGc6xgPwyN8eheCxsiLM8mxuE/t/mOVqJewPuO1miLpTHQiRgTKCLexL4MeAFVagts7HmNZ2Q==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=10" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + } + } +} diff --git a/tests/package.json b/tests/package.json new file mode 100644 index 00000000..28ea38ce --- /dev/null +++ b/tests/package.json @@ -0,0 +1,25 @@ +{ + "name": "vscroll-tests", + "version": "1.7.0", + "private": true, + "description": "Test suite for vscroll", + "type": "module", + "scripts": { + "test:unit": "jest", + "test:e2e": "playwright test --config=e2e/playwright.config.ts", + "test:e2e:ui": "playwright test --config=e2e/playwright.config.ts --ui", + "test:e2e:debug": "playwright test --config=e2e/playwright.config.ts --debug", + "test": "npm run test:unit && npm run test:e2e", + "watch": "jest --watch" + }, + "devDependencies": { + "@babel/core": "^7.16.12", + "@babel/preset-env": "^7.28.0", + "@babel/preset-typescript": "^7.27.1", + "@playwright/test": "^1.56.1", + "@types/jest": "^30.0.0", + "babel-jest": "^30.0.5", + "jest": "^30.0.5", + "typescript": "^5.8.3" + } +} \ No newline at end of file From fac56ac4ca722be92a3ce2dcd571a7cfd5a00bb6 Mon Sep 17 00:00:00 2001 From: Denis Aleksanov Date: Tue, 11 Nov 2025 16:32:41 +0100 Subject: [PATCH 20/51] issue-66 remove old CI workflow --- .github/workflows/build.yml | 50 ------------------------------------- tests/e2e/tsconfig.json | 1 - 2 files changed, 51 deletions(-) delete mode 100644 .github/workflows/build.yml diff --git a/.github/workflows/build.yml b/.github/workflows/build.yml deleted file mode 100644 index c4a6a937..00000000 --- a/.github/workflows/build.yml +++ /dev/null @@ -1,50 +0,0 @@ -name: vscroll build - -on: - push: - branches: - - "**" - pull_request: - branches: [main] - workflow_dispatch: - inputs: - cause: - description: "Reason" - required: true - default: "Manual triggering" - -jobs: - build: - runs-on: ubuntu-latest - strategy: - matrix: - node-version: [20.x] - steps: - - name: Dispatched? - if: ${{ github.event_name == 'workflow_dispatch' }} - run: | - echo "This is dispatched" - echo "Build reason: ${{ github.event.inputs.cause }}" - - - name: Checkout - uses: actions/checkout@v4 - - - name: Use Node.js ${{ matrix.node-version }} - uses: actions/setup-node@v4 - with: - node-version: ${{ matrix.node-version }} - - - name: Install root dependencies - run: npm ci - - - name: Lint - run: npm run lint - - - name: Build - run: npm run build - - - name: Install test dependencies - run: cd tests && npm ci - - - name: Run unit tests - run: npm run test:unit diff --git a/tests/e2e/tsconfig.json b/tests/e2e/tsconfig.json index 8d5720af..3181a0c8 100644 --- a/tests/e2e/tsconfig.json +++ b/tests/e2e/tsconfig.json @@ -3,7 +3,6 @@ "baseUrl": ".", "outDir": "./dist", "types": ["node", "@playwright/test"], - "typeRoots": ["./types", "../node_modules/@types"], "module": "ESNext", "moduleResolution": "bundler", "esModuleInterop": true, From 211c1237bce9bfd1bf37c37d638ab56d0712e81c Mon Sep 17 00:00:00 2001 From: Denis Aleksanov Date: Tue, 11 Nov 2025 17:32:52 +0100 Subject: [PATCH 21/51] issue-66 createFixture helper --- tests/e2e/fixture/create-fixture.ts | 78 +++++++++++++++++++++++++ tests/e2e/specs/scroll-basic.spec.ts | 87 ++-------------------------- tests/e2e/specs/scroll-delay.spec.ts | 37 +----------- tests/e2e/types/index.ts | 6 +- 4 files changed, 87 insertions(+), 121 deletions(-) create mode 100644 tests/e2e/fixture/create-fixture.ts diff --git a/tests/e2e/fixture/create-fixture.ts b/tests/e2e/fixture/create-fixture.ts new file mode 100644 index 00000000..1a2f9fa4 --- /dev/null +++ b/tests/e2e/fixture/create-fixture.ts @@ -0,0 +1,78 @@ +import { VScrollFixture } from './VScrollFixture.js'; +import { Page, IDatasource, ITestConfig } from 'types/index.js'; + +// Create test fixture from config +export const createFixture = async (page: Page, config: ITestConfig): Promise => { + const { datasourceSettings, datasourceDevSettings, templateSettings } = config; + + // Enable adapter to access relax() method + const datasource: IDatasource = { + get: (index, count, success) => { + const data = []; + for (let i = index; i < index + count; i++) { + data.push({ id: i, text: `item #${i}` }); + } + setTimeout(() => success(data), 25); + }, + settings: datasourceSettings, + devSettings: datasourceDevSettings + }; + + const fixture = await VScrollFixture.create(page, { + datasource, + useAdapter: true, + templateSettings, + templateFn: (item: { $index: number, data: { id: number, text: string } }) => + `
${item.$index}: ${item.data.text}
` + }); + + // Wait for initial workflow cycle to complete + await fixture.relaxNext(); + + // // Debug: log actual element dimensions + // const debugInfo = await page.evaluate(() => { + // const viewport = document.getElementById('viewport')!; + // const vscroll = document.getElementById('vscroll')!; + // const items = Array.from(vscroll.querySelectorAll('[data-sid]')); + // const bwdPadding = vscroll.querySelector('[data-padding-backward]') as HTMLElement; + // const fwdPadding = vscroll.querySelector('[data-padding-forward]') as HTMLElement; + // const horizontal = (window as any).__vscroll__.workflow.scroller.settings.horizontal; + // return { + // viewportDimensions: { + // clientWidth: viewport.clientWidth, + // clientHeight: viewport.clientHeight, + // scrollWidth: viewport.scrollWidth, + // scrollHeight: viewport.scrollHeight + // }, + // vscrollDimensions: { + // clientWidth: vscroll.clientWidth, + // clientHeight: vscroll.clientHeight, + // scrollWidth: vscroll.scrollWidth, + // scrollHeight: vscroll.scrollHeight + // }, + // itemCount: items.length, + // firstItemDimensions: items[0] ? { + // width: (items[0] as HTMLElement).clientWidth, + // height: (items[0] as HTMLElement).clientHeight, + // display: getComputedStyle(items[0] as HTMLElement).display, + // whiteSpace: getComputedStyle(vscroll).whiteSpace + // } : null, + // bwdPaddingDimensions: { + // width: bwdPadding.clientWidth, + // height: bwdPadding.clientHeight, + // display: getComputedStyle(bwdPadding).display, + // computedWidth: getComputedStyle(bwdPadding).width + // }, + // fwdPaddingDimensions: { + // width: fwdPadding.clientWidth, + // height: fwdPadding.clientHeight, + // display: getComputedStyle(fwdPadding).display, + // computedWidth: getComputedStyle(fwdPadding).width + // }, + // horizontal + // }; + // }); + // console.log('[DEBUG] Initial dimensions:', JSON.stringify(debugInfo, null, 2)); + + return fixture; +}; \ No newline at end of file diff --git a/tests/e2e/specs/scroll-basic.spec.ts b/tests/e2e/specs/scroll-basic.spec.ts index ccabc736..eac89411 100644 --- a/tests/e2e/specs/scroll-basic.spec.ts +++ b/tests/e2e/specs/scroll-basic.spec.ts @@ -1,8 +1,9 @@ import { test, expect } from '@playwright/test'; -import { afterEachLogs } from '../fixture/after-each-logs.js'; import { VScrollFixture, Direction, type DirectionType } from '../fixture/VScrollFixture.js'; +import { afterEachLogs } from '../fixture/after-each-logs.js'; +import { createFixture } from '../fixture/create-fixture.js'; import { ItemsCounter } from '../helpers/itemsCounter.js'; -import { ITestConfig, Page, IDatasource } from 'types/index.js'; +import { ITestConfig } from 'types/index.js'; test.afterEach(afterEachLogs); @@ -181,86 +182,6 @@ const getCurrentItemsCounter = async ( return result; }; -// Create test fixture from config -const createFixture = async (page: Page, config: IConfig): Promise => { - const { datasourceSettings, templateSettings } = config; - - // // Capture browser console logs - // page.on('console', (msg: any) => { - // console.log(`[BROWSER] ${msg.text()}`); - // }); - - // Enable adapter to access relax() method - const datasource: IDatasource = { - get: (index, count, success) => { - const data = []; - for (let i = index; i < index + count; i++) { - data.push({ id: i, text: `item #${i}` }); - } - setTimeout(() => success(data), 25); - }, - settings: datasourceSettings - }; - - const fixture = await VScrollFixture.create(page, { - datasource, - useAdapter: true, - templateSettings, - templateFn: (item: { $index: number, data: { id: number, text: string } }) => - `
${item.$index}: ${item.data.text}
` - }); - - // Wait for initial workflow cycle to complete - await fixture.relaxNext(); - - // // Debug: log actual element dimensions - // const debugInfo = await page.evaluate(() => { - // const viewport = document.getElementById('viewport')!; - // const vscroll = document.getElementById('vscroll')!; - // const items = Array.from(vscroll.querySelectorAll('[data-sid]')); - // const bwdPadding = vscroll.querySelector('[data-padding-backward]') as HTMLElement; - // const fwdPadding = vscroll.querySelector('[data-padding-forward]') as HTMLElement; - // const horizontal = (window as any).__vscroll__.workflow.scroller.settings.horizontal; - // return { - // viewportDimensions: { - // clientWidth: viewport.clientWidth, - // clientHeight: viewport.clientHeight, - // scrollWidth: viewport.scrollWidth, - // scrollHeight: viewport.scrollHeight - // }, - // vscrollDimensions: { - // clientWidth: vscroll.clientWidth, - // clientHeight: vscroll.clientHeight, - // scrollWidth: vscroll.scrollWidth, - // scrollHeight: vscroll.scrollHeight - // }, - // itemCount: items.length, - // firstItemDimensions: items[0] ? { - // width: (items[0] as HTMLElement).clientWidth, - // height: (items[0] as HTMLElement).clientHeight, - // display: getComputedStyle(items[0] as HTMLElement).display, - // whiteSpace: getComputedStyle(vscroll).whiteSpace - // } : null, - // bwdPaddingDimensions: { - // width: bwdPadding.clientWidth, - // height: bwdPadding.clientHeight, - // display: getComputedStyle(bwdPadding).display, - // computedWidth: getComputedStyle(bwdPadding).width - // }, - // fwdPaddingDimensions: { - // width: fwdPadding.clientWidth, - // height: fwdPadding.clientHeight, - // display: getComputedStyle(fwdPadding).display, - // computedWidth: getComputedStyle(fwdPadding).width - // }, - // horizontal - // }; - // }); - // console.log('[DEBUG] Initial dimensions:', JSON.stringify(debugInfo, null, 2)); - - return fixture; -}; - // Main test function const shouldScroll = async (fixture: VScrollFixture, config: IConfig) => { const custom = config.custom; @@ -314,7 +235,7 @@ const shouldScroll = async (fixture: VScrollFixture, config: IConfig) => { const makeTest = (title: string, config: IConfig) => { test(title, async ({ page }) => { - const fixture = await createFixture(page, config); + const fixture = await createFixture(page, config as ITestConfig); await shouldScroll(fixture, config); await fixture.cleanup(); }); diff --git a/tests/e2e/specs/scroll-delay.spec.ts b/tests/e2e/specs/scroll-delay.spec.ts index 26989b67..2e1b8914 100644 --- a/tests/e2e/specs/scroll-delay.spec.ts +++ b/tests/e2e/specs/scroll-delay.spec.ts @@ -1,43 +1,10 @@ import { test, expect } from '@playwright/test'; import { afterEachLogs } from '../fixture/after-each-logs.js'; -import { VScrollFixture } from '../fixture/VScrollFixture.js'; -import { ITestConfig, Page, IDatasource } from 'types/index.js'; +import { createFixture } from '../fixture/create-fixture.js'; +import { ITestConfig } from 'types/index.js'; test.afterEach(afterEachLogs); -/** - * Create fixture with datasource delay - */ -const createFixture = async (page: Page, config: ITestConfig): Promise => { - const { datasourceSettings, datasourceDevSettings, templateSettings } = config; - - // Note: closure variables not available in browser context - const datasource: IDatasource = { - get: (index, count, success) => { - const data = []; - for (let i = index; i < index + count; i++) { - data.push({ id: i, text: `item #${i}` }); - } - setTimeout(() => success(data), 150); // 150ms delay for slow fetch test - }, - settings: datasourceSettings, - devSettings: datasourceDevSettings - }; - - const fixture = await VScrollFixture.create(page, { - datasource, - useAdapter: true, - templateSettings, - templateFn: (item: { $index: number, data: { id: number, text: string } }) => - `
${item.$index}: ${item.data.text}
` - }); - - // Wait for initial workflow cycle to complete - await fixture.relaxNext(); - - return fixture; -}; - /** * Test: Throttled scroll event handling * diff --git a/tests/e2e/types/index.ts b/tests/e2e/types/index.ts index ba086f6d..6edbb001 100644 --- a/tests/e2e/types/index.ts +++ b/tests/e2e/types/index.ts @@ -1,7 +1,7 @@ import type { Page } from '@playwright/test'; -import type { IDatasource } from '../../src/index.js'; -import type { Settings, DevSettings } from '../../src/interfaces/settings.js'; +import type { IDatasource } from '../../../src/index.js'; +import type { Settings, DevSettings } from '../../../src/interfaces/settings.js'; export type { Page }; export type { IDatasource }; @@ -13,7 +13,7 @@ interface ITemplateSettings { horizontal?: boolean; } -export interface ITestConfig { +export interface ITestConfig { datasourceSettings: Settings; datasourceDevSettings?: DevSettings; templateSettings: ITemplateSettings; From 174336fd7f0a0197f5ccdaa2b41c3f07eaffc7f8 Mon Sep 17 00:00:00 2001 From: Denis Aleksanov Date: Tue, 11 Nov 2025 18:30:13 +0100 Subject: [PATCH 22/51] issue-66 datasourceGet for createFixture --- tests/e2e/fixture/Scroller.ts | 2 +- tests/e2e/fixture/VScrollFixture.ts | 15 +-------------- tests/e2e/fixture/create-fixture.ts | 23 ++++++++++------------- tests/e2e/specs/scroll-basic.spec.ts | 9 ++++++++- tests/e2e/specs/scroll-delay.spec.ts | 14 ++++++++++++-- tests/e2e/types/index.ts | 18 ++++++++++++++++-- 6 files changed, 48 insertions(+), 33 deletions(-) diff --git a/tests/e2e/fixture/Scroller.ts b/tests/e2e/fixture/Scroller.ts index 3a18fd26..f30b202a 100644 --- a/tests/e2e/fixture/Scroller.ts +++ b/tests/e2e/fixture/Scroller.ts @@ -1,4 +1,4 @@ -import { Page } from '@playwright/test'; +import { Page } from '../types'; import { Direction as DirectionEnum } from '../../../src/inputs/common'; /** diff --git a/tests/e2e/fixture/VScrollFixture.ts b/tests/e2e/fixture/VScrollFixture.ts index e84ee32f..74563cc8 100644 --- a/tests/e2e/fixture/VScrollFixture.ts +++ b/tests/e2e/fixture/VScrollFixture.ts @@ -1,22 +1,9 @@ -import { Page } from '@playwright/test'; import * as path from 'path'; -import type { IDatasource, Item } from '../../../src/index'; import { Scroller, Direction, type DirectionType } from './Scroller.js'; import { Adapter } from './Adapter.js'; +import { VScrollFixtureConfig, Page, Item } from '../types'; export { Direction, type DirectionType }; -export interface VScrollFixtureConfig { - datasource: IDatasource; - useAdapter?: boolean; // If true, creates Datasource instance with adapter support - templateSettings?: { - viewportHeight?: number; - viewportWidth?: number; - itemHeight?: number; - itemWidth?: number; - horizontal?: boolean; - }; - templateFn?: (item: unknown) => string; -} export class VScrollFixture { private page: Page; diff --git a/tests/e2e/fixture/create-fixture.ts b/tests/e2e/fixture/create-fixture.ts index 1a2f9fa4..fad8d463 100644 --- a/tests/e2e/fixture/create-fixture.ts +++ b/tests/e2e/fixture/create-fixture.ts @@ -1,21 +1,18 @@ import { VScrollFixture } from './VScrollFixture.js'; import { Page, IDatasource, ITestConfig } from 'types/index.js'; -// Create test fixture from config -export const createFixture = async (page: Page, config: ITestConfig): Promise => { - const { datasourceSettings, datasourceDevSettings, templateSettings } = config; +type FixtureParams = { + page: Page; + config: ITestConfig; +} + +export const createFixture = async ({ page, config }: FixtureParams): Promise => { + const { templateSettings } = config; - // Enable adapter to access relax() method const datasource: IDatasource = { - get: (index, count, success) => { - const data = []; - for (let i = index; i < index + count; i++) { - data.push({ id: i, text: `item #${i}` }); - } - setTimeout(() => success(data), 25); - }, - settings: datasourceSettings, - devSettings: datasourceDevSettings + get: config.datasourceGet, + settings: config.datasourceSettings, + devSettings: config.datasourceDevSettings }; const fixture = await VScrollFixture.create(page, { diff --git a/tests/e2e/specs/scroll-basic.spec.ts b/tests/e2e/specs/scroll-basic.spec.ts index eac89411..7cdca92c 100644 --- a/tests/e2e/specs/scroll-basic.spec.ts +++ b/tests/e2e/specs/scroll-basic.spec.ts @@ -235,7 +235,14 @@ const shouldScroll = async (fixture: VScrollFixture, config: IConfig) => { const makeTest = (title: string, config: IConfig) => { test(title, async ({ page }) => { - const fixture = await createFixture(page, config as ITestConfig); + config.datasourceGet = (index, count, success) => { + const data = []; + for (let i = index; i < index + count; i++) { + data.push({ id: i, text: `item #${i}` }); + } + setTimeout(() => success(data), 25); + }; + const fixture = await createFixture({ page, config }); await shouldScroll(fixture, config); await fixture.cleanup(); }); diff --git a/tests/e2e/specs/scroll-delay.spec.ts b/tests/e2e/specs/scroll-delay.spec.ts index 2e1b8914..898d94b6 100644 --- a/tests/e2e/specs/scroll-delay.spec.ts +++ b/tests/e2e/specs/scroll-delay.spec.ts @@ -5,6 +5,14 @@ import { ITestConfig } from 'types/index.js'; test.afterEach(afterEachLogs); +const datasourceGet: ITestConfig['datasourceGet'] = (index, count, success) => { + const data = []; + for (let i = index; i < index + count; i++) { + data.push({ id: i, text: `item #${i}` }); + } + setTimeout(() => success(data), 150); +}; + /** * Test: Throttled scroll event handling * @@ -14,6 +22,7 @@ test.afterEach(afterEachLogs); test.describe('Delay Scroll Spec', () => { test('should work with throttled scroll event handling', async ({ page }) => { const config: ITestConfig = { + datasourceGet, datasourceSettings: { startIndex: 1, bufferSize: 5, @@ -30,7 +39,7 @@ test.describe('Delay Scroll Spec', () => { } }; - const fixture = await createFixture(page, config); + const fixture = await createFixture({ page, config }); const initCyclesCount = await fixture.workflow.cyclesDone; expect(initCyclesCount).toBe(1); @@ -73,6 +82,7 @@ test.describe('Delay Scroll Spec', () => { test('should handle additional scrolling during slow fetch', async ({ page }) => { const config: ITestConfig = { + datasourceGet, datasourceSettings: { startIndex: 1, bufferSize: 5, @@ -86,7 +96,7 @@ test.describe('Delay Scroll Spec', () => { } }; - const fixture = await createFixture(page, config); + const fixture = await createFixture({ page, config }); const initCyclesCount = await fixture.workflow.cyclesDone; expect(initCyclesCount).toBe(1); diff --git a/tests/e2e/types/index.ts b/tests/e2e/types/index.ts index 6edbb001..a60ebe60 100644 --- a/tests/e2e/types/index.ts +++ b/tests/e2e/types/index.ts @@ -1,10 +1,10 @@ import type { Page } from '@playwright/test'; -import type { IDatasource } from '../../../src/index.js'; +import type { IDatasource, Item } from '../../../src/index.js'; import type { Settings, DevSettings } from '../../../src/interfaces/settings.js'; export type { Page }; -export type { IDatasource }; +export type { IDatasource, Item }; interface ITemplateSettings { viewportHeight?: number; viewportWidth?: number; @@ -14,8 +14,22 @@ interface ITemplateSettings { } export interface ITestConfig { + datasourceGet?: IDatasource['get']; datasourceSettings: Settings; datasourceDevSettings?: DevSettings; templateSettings: ITemplateSettings; custom?: Custom; +} + +export interface VScrollFixtureConfig { + datasource: IDatasource; + useAdapter?: boolean; // If true, creates Datasource instance with adapter support + templateSettings?: { + viewportHeight?: number; + viewportWidth?: number; + itemHeight?: number; + itemWidth?: number; + horizontal?: boolean; + }; + templateFn?: (item: unknown) => string; } \ No newline at end of file From 88a364a70549e41d44c08d623b98edd4979b8ca9 Mon Sep 17 00:00:00 2001 From: Denis Aleksanov Date: Wed, 12 Nov 2025 04:08:06 +0100 Subject: [PATCH 23/51] issue-66 Routines.getWindowParams() fix --- src/classes/domRoutines.ts | 19 ++++++++++--------- 1 file changed, 10 insertions(+), 9 deletions(-) diff --git a/src/classes/domRoutines.ts b/src/classes/domRoutines.ts index 0548c033..9763e46b 100644 --- a/src/classes/domRoutines.ts +++ b/src/classes/domRoutines.ts @@ -93,16 +93,17 @@ export class Routines implements IRoutines { } getWindowParams(): DOMRect { - const { clientWidth, clientHeight, clientLeft, clientTop } = this.viewport; + const height = window.innerHeight; + const width = window.innerWidth; return { - 'height': clientHeight, - 'width': clientWidth, - 'top': clientTop, - 'bottom': clientTop + clientHeight, - 'left': clientLeft, - 'right': clientLeft + clientWidth, - 'x': clientLeft, - 'y': clientTop, + 'height': height, + 'width': width, + 'top': 0, + 'bottom': height, + 'left': 0, + 'right': width, + 'x': 0, + 'y': 0, 'toJSON': () => null, }; } From 52af1f5722212c846cbff6b8d676c87cc0f038f8 Mon Sep 17 00:00:00 2001 From: Denis Aleksanov Date: Wed, 12 Nov 2025 04:29:35 +0100 Subject: [PATCH 24/51] issue-66 scroll-fast.spec --- tests/e2e/fixture/Scroller.ts | 5 + tests/e2e/fixture/VScrollFixture.ts | 15 +- tests/e2e/specs/scroll-fast.spec.ts | 275 ++++++++++++++++++++++++++++ tests/e2e/types/global.d.ts | 8 +- tests/e2e/types/index.ts | 9 +- 5 files changed, 299 insertions(+), 13 deletions(-) create mode 100644 tests/e2e/specs/scroll-fast.spec.ts diff --git a/tests/e2e/fixture/Scroller.ts b/tests/e2e/fixture/Scroller.ts index f30b202a..e52805cb 100644 --- a/tests/e2e/fixture/Scroller.ts +++ b/tests/e2e/fixture/Scroller.ts @@ -47,6 +47,11 @@ export class Scroller { get buffer() { const page = this.page; return { + get size(): Promise { + return page.evaluate(() => { + return window.__vscroll__.workflow.scroller.buffer.size; + }); + }, get defaultSize(): Promise { return page.evaluate(() => { return window.__vscroll__.workflow.scroller.buffer.defaultSize; diff --git a/tests/e2e/fixture/VScrollFixture.ts b/tests/e2e/fixture/VScrollFixture.ts index 74563cc8..5d1e9849 100644 --- a/tests/e2e/fixture/VScrollFixture.ts +++ b/tests/e2e/fixture/VScrollFixture.ts @@ -6,7 +6,7 @@ import { VScrollFixtureConfig, Page, Item } from '../types'; export { Direction, type DirectionType }; export class VScrollFixture { - private page: Page; + page: Page; private config: VScrollFixtureConfig; readonly scroller: Scroller; readonly adapter: Adapter; @@ -68,7 +68,15 @@ export class VScrollFixture { const viewport = document.createElement('div'); viewport.id = 'viewport'; - if (settings.horizontal) { + // Handle windowViewport mode (use document.body as viewport) + const useWindowViewport = settings.noViewportClass && settings.viewportHeight === 0; + + if (useWindowViewport) { + // For windowViewport: use body as viewport, no fixed dimensions + document.body.style.margin = '0'; + document.body.style.padding = '0'; + document.body.style.overflow = settings.horizontal ? 'hidden' : 'auto'; + } else if (settings.horizontal) { viewport.style.width = `${settings.viewportWidth || 300}px`; viewport.style.height = '100%'; viewport.style.overflowX = 'scroll'; // Always scroll, not auto @@ -111,12 +119,13 @@ export class VScrollFixture { const { datasource, templateFn, useAdapter } = this.config; // Serialize functions and config - const datasourceGetStr = datasource.get.toString(); + const datasourceGetStr = datasource.get ? datasource.get.toString() : null; const datasourceSettings = datasource.settings || {}; // Add default devSettings for all e2e tests: // debug enabled, colors disabled, immediateLog disabled (to store logs in logger for retrieval on test failure) const datasourceDevSettings = { debug: true, + logProcessRun: true, immediateLog: false, logColor: false, ...(datasource.devSettings || {}) diff --git a/tests/e2e/specs/scroll-fast.spec.ts b/tests/e2e/specs/scroll-fast.spec.ts new file mode 100644 index 00000000..b9a5727b --- /dev/null +++ b/tests/e2e/specs/scroll-fast.spec.ts @@ -0,0 +1,275 @@ +import { test, expect } from '@playwright/test'; +import { afterEachLogs } from '../fixture/after-each-logs.js'; +import { createFixture } from '../fixture/create-fixture.js'; +import { Direction, VScrollFixture, type DirectionType } from '../fixture/VScrollFixture.js'; +import { ITestConfig } from 'types/index.js'; + +test.afterEach(afterEachLogs); + +interface ICustom { + items: number; + scrollCount: number; + start: DirectionType; +} +type IConfig = ITestConfig; + +// Datasource generators for limited ranges +// Note: We can't use closure variables because the function gets serialized. +const datasourceGet1to100 = (index: number, count: number, success: (data: unknown[]) => void): void => { + const data = []; + for (let i = index; i < index + count; i++) { + if (i >= 1 && i <= 100) { + data.push({ id: i, text: `item #${i}` }); + } + } + setTimeout(() => success(data), 25); +}; + +const datasourceGet51to200 = (index: number, count: number, success: (data: unknown[]) => void): void => { + const data = []; + for (let i = index; i < index + count; i++) { + if (i >= 51 && i <= 200) { + data.push({ id: i, text: `item #${i}` }); + } + } + setTimeout(() => success(data), 25); +}; + +const configList: IConfig[] = [ + { + datasourceGet: datasourceGet1to100, + datasourceSettings: { + startIndex: 1, + bufferSize: 5, + padding: 0.5, + minIndex: 1, + maxIndex: 100, + adapter: true + }, + templateSettings: { viewportHeight: 100, itemHeight: 20 }, + custom: { items: 100, scrollCount: 5, start: Direction.backward } + }, + { + datasourceGet: datasourceGet1to100, + datasourceSettings: { + startIndex: 1, + bufferSize: 3, + padding: 0.3, + minIndex: 1, + maxIndex: 100, + adapter: true + }, + templateSettings: { viewportHeight: 110, itemHeight: 20 }, + custom: { items: 100, scrollCount: 8, start: Direction.backward } + }, + { + datasourceGet: datasourceGet51to200, + datasourceSettings: { + startIndex: 51, + bufferSize: 7, + padding: 1.1, + minIndex: 51, + maxIndex: 200, + adapter: true + }, + templateSettings: { viewportHeight: 69, itemHeight: 20 }, + custom: { items: 150, scrollCount: 6, start: Direction.backward } + }, + { + datasourceGet: datasourceGet51to200, + datasourceSettings: { + startIndex: 51, + bufferSize: 20, + padding: 0.2, + windowViewport: true, + minIndex: 51, + maxIndex: 200, + adapter: true + }, + templateSettings: { noViewportClass: true, viewportHeight: 0, itemHeight: 20 }, + custom: { items: 150, scrollCount: 5, start: Direction.backward } + } +]; + +const configEofList: IConfig[] = configList.map(config => ({ + ...config, + custom: { + ...config.custom, + start: Direction.forward + } +})); + +/** + * Run fast alternating scrolls in browser context + */ +const runFastScroll = ( + fixture: VScrollFixture, + customConfig: ICustom +): Promise => fixture.page.evaluate( + ({ scrollCount, start }) => { + return new Promise((resolve) => { + const adapter = window.__vscroll__.datasource.adapter; + let iteration = 0; + + const doScroll = () => { + setTimeout(() => { + // scrollMax + adapter.fix({ scrollPosition: Infinity }); + + setTimeout(async () => { + // scrollMin (except on last iteration for EOF tests) + if (iteration < scrollCount || start === 'backward') { + adapter.fix({ scrollPosition: 0 }); + } + + iteration++; + if (iteration <= scrollCount) { + doScroll(); + } else { + resolve(); + } + }, 25); + }, 25); + }; + + doScroll(); + }); + }, + { scrollCount: customConfig.scrollCount, start: customConfig.start } +); + +/** + * Wait for expected edge element to be reached + * Simple feedback loop: Wait → Check → Correct if needed → Repeat + */ +const waitForExpectedEdge = async ( + fixture: VScrollFixture, + config: IConfig +): Promise => { + const startIdx = config.datasourceSettings.startIndex as number; + const itemsCount = config.custom.items; + const timeout = 10000; + const startTime = Date.now(); + + while (true) { + // Check timeout + if (Date.now() - startTime > timeout) { + throw new Error('Timeout waiting for expected edge element'); + } + + // Step 1: Wait for workflow to be idle + await fixture.adapter.relax(); + + // Step 2: Get all state in ONE call + const state = await fixture.page.evaluate(() => { + const vscroll = window.__vscroll__; + const { buffer, viewport } = vscroll.workflow.scroller; + const Direction = window.VScroll.Direction; + const position = viewport.scrollPosition; + const eof = position === 0; + const direction = eof ? Direction.backward : Direction.forward; + const edgeItem = buffer.getEdgeVisibleItem(direction); + + return { + bufferSize: buffer.size, + position, + eof, + edgeItem: edgeItem ? { $index: edgeItem.$index } : null + }; + }); + + // Step 3: Do calculations/checks in Node.js + if (!state.bufferSize || !state.edgeItem) { + continue; // Buffer or edge item not ready + } + + const expectedIndex = startIdx + (state.eof ? 0 : itemsCount - 1); + + // Step 4: Check if we're at target edge + if (state.edgeItem.$index === expectedIndex) { + break; // Success! + } + + // Step 5: Issue corrective scroll + await fixture.adapter.fix({ + scrollPosition: state.eof ? 0 : Infinity + }); + } +}; + +/** + * Verify final expectations after fast scrolling + */ +const verifyExpectations = async ( + fixture: VScrollFixture, + config: IConfig +): Promise => { + const startIndex = config.datasourceSettings.startIndex as number; + const position = await fixture.scroller.viewport.scrollPosition; + const itemHeight = config.templateSettings.itemHeight || 20; + + // Get buffer and padding info + const bufferSize = await fixture.scroller.buffer.size; + const backwardPadding = await fixture.scroller.viewport.paddings[Direction.backward].size; + const forwardPadding = await fixture.scroller.viewport.paddings[Direction.forward].size; + + const bufferHeight = bufferSize * itemHeight; + const totalSize = backwardPadding + forwardPadding + bufferHeight; + const totalItemsHeight = config.custom.items * itemHeight; + + // Verify total size matches expected + expect(totalSize).toBe(totalItemsHeight); + expect(bufferSize).toBeGreaterThan(0); + + if (bufferSize > 0) { + if (position === 0) { + // At BOF - check first element + const first = await fixture.scroller.buffer.getEdgeVisibleItem(Direction.backward); + expect(first.$index).toBe(startIndex); + } else { + // At EOF - check last element + const last = await fixture.scroller.buffer.getEdgeVisibleItem(Direction.forward); + expect(last.$index).toBe(startIndex + config.custom.items - 1); + } + } +}; + +/** + * Main test function + */ +const shouldReachEdge = async (fixture: VScrollFixture, config: IConfig) => { + // Wait for initial load + await fixture.adapter.relax(); + + // Run fast scrolling and wait for it to complete + await runFastScroll(fixture, config.custom); + + // Wait for expected edge element + await waitForExpectedEdge(fixture, config); + + // Verify final state + await verifyExpectations(fixture, config); +}; + +const makeTest = (title: string, config: IConfig) => { + test(title, async ({ page }) => { + const fixture = await createFixture({ page, config }); + await shouldReachEdge(fixture, config); + await fixture.cleanup(); + }); +}; + +test.describe('Fast Scroll Spec', () => { + test.describe('multi-scroll to the BOF', () => + configList.forEach((config, index) => + makeTest(`should reach BOF without gaps (config ${index})`, config) + ) + ); + + test.describe('multi-scroll to the EOF', () => + configEofList.forEach((config, index) => + makeTest(`should reach EOF without gaps (config ${index})`, config) + ) + ); +}); + diff --git a/tests/e2e/types/global.d.ts b/tests/e2e/types/global.d.ts index ec56fed3..83338591 100644 --- a/tests/e2e/types/global.d.ts +++ b/tests/e2e/types/global.d.ts @@ -3,9 +3,10 @@ * Extends Window with vscroll-specific properties */ -import type { IAdapter } from '../../src/interfaces/adapter'; -import type { Item } from '../../src/interfaces/item'; -import type { Workflow } from '../../src/workflow'; +import type { IAdapter } from '../../../src/interfaces/adapter'; +import type { Item } from '../../../src/interfaces/item'; +import type { Workflow } from '../../../src/workflow'; +import type { Direction } from '../../../src/inputs/common'; interface VScrollGlobal { workflow: Workflow | Record; @@ -22,6 +23,7 @@ interface VScrollGlobal { interface VScrollConstructor { makeDatasource: () => new (config: unknown) => unknown; Workflow: new (config: unknown) => Workflow; + Direction: typeof Direction; } declare global { diff --git a/tests/e2e/types/index.ts b/tests/e2e/types/index.ts index a60ebe60..ddb8c572 100644 --- a/tests/e2e/types/index.ts +++ b/tests/e2e/types/index.ts @@ -11,6 +11,7 @@ interface ITemplateSettings { itemHeight?: number; itemWidth?: number; horizontal?: boolean; + noViewportClass?: boolean; } export interface ITestConfig { @@ -24,12 +25,6 @@ export interface ITestConfig { export interface VScrollFixtureConfig { datasource: IDatasource; useAdapter?: boolean; // If true, creates Datasource instance with adapter support - templateSettings?: { - viewportHeight?: number; - viewportWidth?: number; - itemHeight?: number; - itemWidth?: number; - horizontal?: boolean; - }; + templateSettings?: ITemplateSettings; templateFn?: (item: unknown) => string; } \ No newline at end of file From 6893215d35330ffb93fb5f167c6889097ac6d8c3 Mon Sep 17 00:00:00 2001 From: Denis Aleksanov Date: Wed, 12 Nov 2025 23:22:47 +0100 Subject: [PATCH 25/51] issue-66 viewport.spec --- tests/e2e/fixture/Adapter.ts | 12 ++-- tests/e2e/fixture/VScrollFixture.ts | 16 ++++- tests/e2e/specs/viewport.spec.ts | 105 ++++++++++++++++++++++++++++ tests/e2e/types/index.ts | 1 + 4 files changed, 125 insertions(+), 9 deletions(-) create mode 100644 tests/e2e/specs/viewport.spec.ts diff --git a/tests/e2e/fixture/Adapter.ts b/tests/e2e/fixture/Adapter.ts index fb7b6adc..408b8b30 100644 --- a/tests/e2e/fixture/Adapter.ts +++ b/tests/e2e/fixture/Adapter.ts @@ -171,7 +171,7 @@ export class Adapter { /** * Get adapter properties (as getters to match IAdapter interface) */ - get isLoading(): Promise { + get isLoading() { return this.page.evaluate(() => { const ds = window.__vscroll__.datasource; if (!ds.adapter) { @@ -181,7 +181,7 @@ export class Adapter { }); } - get bof(): Promise { + get bof() { return this.page.evaluate(() => { const ds = window.__vscroll__.datasource; if (!ds.adapter) { @@ -191,7 +191,7 @@ export class Adapter { }); } - get eof(): Promise { + get eof() { return this.page.evaluate(() => { const ds = window.__vscroll__.datasource; if (!ds.adapter) { @@ -201,7 +201,7 @@ export class Adapter { }); } - get itemsCount(): Promise { + get itemsCount() { return this.page.evaluate(() => { const ds = window.__vscroll__.datasource; if (!ds.adapter) { @@ -211,7 +211,7 @@ export class Adapter { }); } - get firstVisible(): Promise { + get firstVisible() { return this.page.evaluate(() => { const ds = window.__vscroll__.datasource; if (!ds.adapter) { @@ -221,7 +221,7 @@ export class Adapter { }); } - get lastVisible(): Promise { + get lastVisible() { return this.page.evaluate(() => { const ds = window.__vscroll__.datasource; if (!ds.adapter) { diff --git a/tests/e2e/fixture/VScrollFixture.ts b/tests/e2e/fixture/VScrollFixture.ts index 5d1e9849..78c5f3e5 100644 --- a/tests/e2e/fixture/VScrollFixture.ts +++ b/tests/e2e/fixture/VScrollFixture.ts @@ -65,12 +65,22 @@ export class VScrollFixture { // 4. Create viewport and vscroll element (after styles are loaded) await page.evaluate((settings) => { - const viewport = document.createElement('div'); - viewport.id = 'viewport'; - // Handle windowViewport mode (use document.body as viewport) const useWindowViewport = settings.noViewportClass && settings.viewportHeight === 0; + // Create header if specified + if (settings.headerHeight) { + const header = document.createElement('div'); + header.id = 'header'; + header.style.height = `${settings.headerHeight}px`; + header.style.width = '100%'; + header.style.backgroundColor = 'yellow'; + document.body.appendChild(header); + } + + const viewport = document.createElement('div'); + viewport.id = 'viewport'; + if (useWindowViewport) { // For windowViewport: use body as viewport, no fixed dimensions document.body.style.margin = '0'; diff --git a/tests/e2e/specs/viewport.spec.ts b/tests/e2e/specs/viewport.spec.ts new file mode 100644 index 00000000..a7326c48 --- /dev/null +++ b/tests/e2e/specs/viewport.spec.ts @@ -0,0 +1,105 @@ +import { test, expect } from '@playwright/test'; +import { afterEachLogs } from '../fixture/after-each-logs.js'; +import { createFixture } from '../fixture/create-fixture.js'; +import { VScrollFixture } from '../fixture/VScrollFixture.js'; +import { ITestConfig } from 'types/index.js'; + +test.afterEach(afterEachLogs); + +interface ICustom { + scrollTo?: number; +} + +type IConfig = ITestConfig; + +// Datasource: limited callback (1-100) +const datasourceGet = (index: number, count: number, success: (data: unknown[]) => void): void => { + const data = []; + const start = Math.max(1, index); + const end = index + count - 1; + if (start > 100 || end < 1) { + success(data); + return; + } + const first = Math.max(1, start); + const last = Math.min(100, end); + for (let i = first; i <= last; i++) { + data.push({ id: i, text: `item #${i}` }); + } + success(data); +}; + +// Base config: windowViewport with 50px header +const windowWith50HeaderConfig: IConfig = { + datasourceGet, + datasourceSettings: { startIndex: 1, windowViewport: true, adapter: true }, + templateSettings: { itemHeight: 50, noViewportClass: true, viewportHeight: 0, headerHeight: 50 }, + custom: { scrollTo: undefined } +}; + +// Config with 500px header +const windowWith500HeaderConfig: IConfig = { + ...windowWith50HeaderConfig, + templateSettings: { + ...windowWith50HeaderConfig.templateSettings, + headerHeight: 500 + } +}; + +// All test configurations +const windowWithHeaderConfigList: IConfig[] = [ + windowWith50HeaderConfig, + windowWith500HeaderConfig, + { ...windowWith50HeaderConfig, custom: { scrollTo: 99999 } }, + { ...windowWith500HeaderConfig, custom: { scrollTo: 99999 } }, + { ...windowWith500HeaderConfig, custom: { scrollTo: 450 } }, + { ...windowWith500HeaderConfig, custom: { scrollTo: 50 } }, + { ...windowWith500HeaderConfig, custom: { scrollTo: 500 } } +]; + +// Test implementation +const shouldWorkOnWindowWithHeader = async ( + fixture: VScrollFixture, + config: IConfig +) => { + const itemHeight = config.templateSettings.itemHeight || 50; + const headerHeight = config.templateSettings.headerHeight || 0; + let position = 0; + let index = 1; + + // Wait for initial load + await fixture.adapter.relax(); + + if (config.custom?.scrollTo !== undefined) { + // Scroll (synchronous in browser) + await fixture.adapter.fix({ scrollPosition: config.custom.scrollTo }); + // Read position immediately after scroll (before workflow processes it) + position = await fixture.scroller.viewport.scrollPosition; + index = Math.max(1, Math.ceil((position - headerHeight) / itemHeight)); + // Now wait for workflow to process the scroll + await fixture.adapter.relax(); + } + + const actualPosition = await fixture.scroller.viewport.scrollPosition; + const firstVisible = await fixture.adapter.firstVisible; + + expect(actualPosition).toBe(position); + expect(firstVisible.$index).toBe(index); +}; + +test.describe('Viewport Spec', () => { + test.describe('Entire Window with Header', () => { + windowWithHeaderConfigList.forEach((config, idx) => { + const scrollToText = config.custom?.scrollTo !== undefined + ? ` scroll to ${config.custom.scrollTo}` + : ' not scroll'; + const headerText = `${config.templateSettings.headerHeight}-offset`; + + test(`should${scrollToText} with ${headerText}`, async ({ page }) => { + const fixture = await createFixture({ page, config }); + await shouldWorkOnWindowWithHeader(fixture, config); + await fixture.cleanup(); + }); + }); + }); +}); diff --git a/tests/e2e/types/index.ts b/tests/e2e/types/index.ts index ddb8c572..c3fad869 100644 --- a/tests/e2e/types/index.ts +++ b/tests/e2e/types/index.ts @@ -12,6 +12,7 @@ interface ITemplateSettings { itemWidth?: number; horizontal?: boolean; noViewportClass?: boolean; + headerHeight?: number; } export interface ITestConfig { From e6163a7a8ad4d79ff94991ace2b11339c54f96bc Mon Sep 17 00:00:00 2001 From: Denis Aleksanov Date: Sun, 16 Nov 2025 14:47:09 +0100 Subject: [PATCH 26/51] issue-66 dispose, noRelaxOnStart --- tests/e2e/fixture/VScrollFixture.ts | 10 ++++++---- tests/e2e/fixture/create-fixture.ts | 4 +++- tests/e2e/specs/scroll-basic.spec.ts | 2 +- tests/e2e/specs/scroll-delay.spec.ts | 4 ++-- tests/e2e/specs/scroll-fast.spec.ts | 2 +- tests/e2e/specs/viewport.spec.ts | 2 +- tests/e2e/types/global.d.ts | 13 +++++++------ tests/e2e/types/index.ts | 1 + 8 files changed, 22 insertions(+), 16 deletions(-) diff --git a/tests/e2e/fixture/VScrollFixture.ts b/tests/e2e/fixture/VScrollFixture.ts index 78c5f3e5..8b9aa114 100644 --- a/tests/e2e/fixture/VScrollFixture.ts +++ b/tests/e2e/fixture/VScrollFixture.ts @@ -225,7 +225,7 @@ export class VScrollFixture { } // Create workflow - const workflow = new VScroll.Workflow({ + const workflowParams = { consumer: { name: 'vscroll-e2e', version: '1.0.0' }, element: document.getElementById('vscroll'), datasource: datasourceInstance, @@ -237,10 +237,12 @@ export class VScrollFixture { processItems(items, oldItems); oldItems = items; } - }); + } + const workflow = new VScroll.Workflow(workflowParams); // Expose for testing window.__vscroll__ = { + workflowParams, workflow, datasource: datasourceInstance, oldItems, @@ -371,9 +373,9 @@ export class VScrollFixture { /** - * Cleanup + * Dispose */ - async cleanup(): Promise { + async dispose(): Promise { await this.page.evaluate(() => { window.__vscroll__?.workflow?.dispose(); document.querySelector('#viewport')?.remove(); diff --git a/tests/e2e/fixture/create-fixture.ts b/tests/e2e/fixture/create-fixture.ts index fad8d463..9e59d371 100644 --- a/tests/e2e/fixture/create-fixture.ts +++ b/tests/e2e/fixture/create-fixture.ts @@ -24,7 +24,9 @@ export const createFixture = async ({ page, config }: FixtureParams): Promise { diff --git a/tests/e2e/specs/scroll-basic.spec.ts b/tests/e2e/specs/scroll-basic.spec.ts index 7cdca92c..96f88624 100644 --- a/tests/e2e/specs/scroll-basic.spec.ts +++ b/tests/e2e/specs/scroll-basic.spec.ts @@ -244,7 +244,7 @@ const makeTest = (title: string, config: IConfig) => { }; const fixture = await createFixture({ page, config }); await shouldScroll(fixture, config); - await fixture.cleanup(); + await fixture.dispose(); }); }; diff --git a/tests/e2e/specs/scroll-delay.spec.ts b/tests/e2e/specs/scroll-delay.spec.ts index 898d94b6..f17d4263 100644 --- a/tests/e2e/specs/scroll-delay.spec.ts +++ b/tests/e2e/specs/scroll-delay.spec.ts @@ -77,7 +77,7 @@ test.describe('Delay Scroll Spec', () => { // Verify all scrolls were executed expect(count).toBe(10); - await fixture.cleanup(); + await fixture.dispose(); }); test('should handle additional scrolling during slow fetch', async ({ page }) => { @@ -146,7 +146,7 @@ test.describe('Delay Scroll Spec', () => { expect(result.position).toBe(result.endPos); expect(result.endPos).toBeGreaterThan(startPosition); - await fixture.cleanup(); + await fixture.dispose(); }); }); diff --git a/tests/e2e/specs/scroll-fast.spec.ts b/tests/e2e/specs/scroll-fast.spec.ts index b9a5727b..32945990 100644 --- a/tests/e2e/specs/scroll-fast.spec.ts +++ b/tests/e2e/specs/scroll-fast.spec.ts @@ -255,7 +255,7 @@ const makeTest = (title: string, config: IConfig) => { test(title, async ({ page }) => { const fixture = await createFixture({ page, config }); await shouldReachEdge(fixture, config); - await fixture.cleanup(); + await fixture.dispose(); }); }; diff --git a/tests/e2e/specs/viewport.spec.ts b/tests/e2e/specs/viewport.spec.ts index a7326c48..9c4d1e52 100644 --- a/tests/e2e/specs/viewport.spec.ts +++ b/tests/e2e/specs/viewport.spec.ts @@ -98,7 +98,7 @@ test.describe('Viewport Spec', () => { test(`should${scrollToText} with ${headerText}`, async ({ page }) => { const fixture = await createFixture({ page, config }); await shouldWorkOnWindowWithHeader(fixture, config); - await fixture.cleanup(); + await fixture.dispose(); }); }); }); diff --git a/tests/e2e/types/global.d.ts b/tests/e2e/types/global.d.ts index 83338591..6f6fd209 100644 --- a/tests/e2e/types/global.d.ts +++ b/tests/e2e/types/global.d.ts @@ -4,16 +4,17 @@ */ import type { IAdapter } from '../../../src/interfaces/adapter'; -import type { Item } from '../../../src/interfaces/item'; +import type { WorkflowParams, Item } from '../../../src/interfaces/index'; import type { Workflow } from '../../../src/workflow'; import type { Direction } from '../../../src/inputs/common'; -interface VScrollGlobal { - workflow: Workflow | Record; +interface VScrollTest { + workflowParams: WorkflowParams; + workflow: Workflow | Record; datasource: { - adapter?: IAdapter; + adapter?: IAdapter; }; - oldItems: Item[]; + oldItems: Item[]; Direction: { forward: string; backward: string; @@ -29,7 +30,7 @@ interface VScrollConstructor { declare global { interface Window { VScroll: VScrollConstructor; - __vscroll__: VScrollGlobal; + __vscroll__: VScrollTest; } } diff --git a/tests/e2e/types/index.ts b/tests/e2e/types/index.ts index c3fad869..08547c3e 100644 --- a/tests/e2e/types/index.ts +++ b/tests/e2e/types/index.ts @@ -20,6 +20,7 @@ export interface ITestConfig { datasourceSettings: Settings; datasourceDevSettings?: DevSettings; templateSettings: ITemplateSettings; + noRelaxOnStart?: boolean; custom?: Custom; } From 31aca48bb65a66cf5d214707407583cff6beb2a1 Mon Sep 17 00:00:00 2001 From: Denis Aleksanov Date: Sun, 16 Nov 2025 14:56:13 +0100 Subject: [PATCH 27/51] issue-66 recreation.spec (1) --- tests/e2e/fixture/VScrollFixture.ts | 4 +-- tests/e2e/specs/recreation.spec.ts | 56 +++++++++++++++++++++++++++++ 2 files changed, 58 insertions(+), 2 deletions(-) create mode 100644 tests/e2e/specs/recreation.spec.ts diff --git a/tests/e2e/fixture/VScrollFixture.ts b/tests/e2e/fixture/VScrollFixture.ts index 8b9aa114..17ed4663 100644 --- a/tests/e2e/fixture/VScrollFixture.ts +++ b/tests/e2e/fixture/VScrollFixture.ts @@ -161,7 +161,7 @@ export class VScrollFixture { // Old items storage let oldItems: Item[] = []; - // Renderer (from POC) + // Renderer const processItems = (newItems: Item[], oldItems: Item[]) => { // Remove elements not present oldItems @@ -237,7 +237,7 @@ export class VScrollFixture { processItems(items, oldItems); oldItems = items; } - } + }; const workflow = new VScroll.Workflow(workflowParams); // Expose for testing diff --git a/tests/e2e/specs/recreation.spec.ts b/tests/e2e/specs/recreation.spec.ts new file mode 100644 index 00000000..314eeb7e --- /dev/null +++ b/tests/e2e/specs/recreation.spec.ts @@ -0,0 +1,56 @@ +import { test, expect } from '@playwright/test'; +import { afterEachLogs } from '../fixture/after-each-logs.js'; +import { createFixture } from '../fixture/create-fixture.js'; +import { ITestConfig } from 'types/index.js'; + +test.afterEach(afterEachLogs); + +const datasourceGet = (index: number, count: number, success: (data: unknown[]) => void): void => { + const data = []; + for (let i = index; i < index + count; i++) { + data.push({ id: i, text: `item #${i}` }); + } + success(data); +}; + +test.describe('Recreation Spec', () => { + test.describe('Destroying (plain DS)', () => { + + test('should not reset Datasource on destroy', async ({ page }) => { + const config: ITestConfig = { + datasourceGet, + datasourceSettings: { startIndex: 1, bufferSize: 5, padding: 0.5 }, + templateSettings: { viewportHeight: 200, itemHeight: 20 }, + noRelaxOnStart: true + }; + + const fixture = await createFixture({ page, config }); + + // Store datasource-adapter id before cleanup + const before = await page.evaluate(() => ({ + id: window.__vscroll__.datasource.adapter.id, + init: window.__vscroll__.datasource.adapter.init, + disposed: window.__vscroll__.workflow.disposed + })); + + // Destroy the scroller + await fixture.dispose(); + + // Wait a bit to ensure no async errors + await page.waitForTimeout(25); + + // Verify datasource still exists with its adapter (wasn't reset/destroyed) + const after = await page.evaluate(() => ({ + id: window.__vscroll__.datasource.adapter.id, + init: window.__vscroll__.datasource.adapter.init, + disposed: window.__vscroll__.workflow.disposed + })); + + expect(before.id).toBe(after.id); + expect(before.init).toBe(!after.init); + expect(before.disposed).toBe(!after.disposed); + }); + + }); +}); + From e7407a8c9d136590cad2f104e20e02c1863b8ce3 Mon Sep 17 00:00:00 2001 From: Denis Aleksanov Date: Sun, 16 Nov 2025 19:18:34 +0100 Subject: [PATCH 28/51] issue-66 makeDatasource -- getDefaultAdapterConfig --- src/classes/datasource.ts | 22 +++++++++++++++++++--- 1 file changed, 19 insertions(+), 3 deletions(-) diff --git a/src/classes/datasource.ts b/src/classes/datasource.ts index 80bf52e6..63bcd23f 100644 --- a/src/classes/datasource.ts +++ b/src/classes/datasource.ts @@ -1,6 +1,7 @@ import { AdapterContext } from './adapter/context'; -import { reactiveConfigStorage } from './adapter/props'; +import { reactiveConfigStorage, AdapterPropType, getDefaultAdapterProps } from './adapter/props'; import { wantedStorage } from './adapter/wanted'; +import { Reactive } from './reactive'; import { IDatasourceParams, IDatasourceConstructed, @@ -9,8 +10,23 @@ import { DevSettings, IAdapter, IAdapterConfig, + IReactivePropConfig } from '../interfaces/index'; +const getDefaultAdapterConfig = (): IAdapterConfig => { + const reactive = getDefaultAdapterProps() + .filter(({ type }) => type === AdapterPropType.Reactive) + .reduce((acc, { name, value }) => { + acc[name] = { + source: value, + emit: (source, val) => (source as Reactive).set(val) + }; + return acc; + }, {} as Record); + + return { mock: false, reactive }; +}; + export class DatasourceGeneric implements IDatasourceConstructed { get: DatasourceGet; settings?: Settings; @@ -33,11 +49,11 @@ export class DatasourceGeneric implements IDatasourceConstructed { export const makeDatasource = ( - getConfig?: () => IAdapterConfig + getAdapterConfig?: () => IAdapterConfig ) => class extends DatasourceGeneric { constructor(datasource: IDatasourceParams) { - const config = typeof getConfig === 'function' ? getConfig() : void 0; + const config = typeof getAdapterConfig === 'function' ? getAdapterConfig() : getDefaultAdapterConfig(); super(datasource, config); } } as DSClassType; From 02217c6232ccb12d497a797f6554eb6ede925db7 Mon Sep 17 00:00:00 2001 From: Denis Aleksanov Date: Sun, 16 Nov 2025 23:57:54 +0100 Subject: [PATCH 29/51] issue-66 Workflow.isInitialized = false on dispose --- src/scroller.ts | 3 ++- src/workflow.ts | 1 + 2 files changed, 3 insertions(+), 1 deletion(-) diff --git a/src/scroller.ts b/src/scroller.ts index 40cb6f1b..a5357b3f 100644 --- a/src/scroller.ts +++ b/src/scroller.ts @@ -95,7 +95,8 @@ export class Scroller { buffer: this.buffer, state: this.state, viewport: this.viewport, - logger: this.logger, adapterRun$, + logger: this.logger, + adapterRun$, getWorkflow: () => this.workflow }); } diff --git a/src/workflow.ts b/src/workflow.ts index ac720efc..66fc770d 100644 --- a/src/workflow.ts +++ b/src/workflow.ts @@ -216,6 +216,7 @@ export class Workflow { Object.getOwnPropertyNames(this).forEach(prop => { delete (this as Record)[prop]; }); + this.isInitialized = false; this.disposed = true; } From 3ff6e9247e1118c4af05357f1ea96b6d2b112012 Mon Sep 17 00:00:00 2001 From: Denis Aleksanov Date: Mon, 17 Nov 2025 00:04:06 +0100 Subject: [PATCH 30/51] issue-66 no-adapter case refactoring --- tests/e2e/fixture/VScrollFixture.ts | 22 ++++++++--------- tests/e2e/fixture/create-fixture.ts | 4 +-- tests/e2e/specs/recreation.spec.ts | 38 +++++++++++++++++------------ tests/e2e/types/index.ts | 8 +++--- 4 files changed, 40 insertions(+), 32 deletions(-) diff --git a/tests/e2e/fixture/VScrollFixture.ts b/tests/e2e/fixture/VScrollFixture.ts index 17ed4663..dc450de1 100644 --- a/tests/e2e/fixture/VScrollFixture.ts +++ b/tests/e2e/fixture/VScrollFixture.ts @@ -126,7 +126,7 @@ export class VScrollFixture { * 2. Datasource class with adapter (adapter tests) */ private async initializeWorkflow(): Promise { - const { datasource, templateFn, useAdapter } = this.config; + const { datasource, templateFn, noAdapter } = this.config; // Serialize functions and config const datasourceGetStr = datasource.get ? datasource.get.toString() : null; @@ -150,7 +150,7 @@ export class VScrollFixture { datasourceSettings, datasourceDevSettings, templateFnStr, - useAdapter + noAdapter }) => { const VScroll = window.VScroll; @@ -207,21 +207,21 @@ export class VScrollFixture { // Create datasource (plain object or Datasource class) let datasourceInstance; - if (useAdapter) { - // Pattern 2: Create Datasource class with adapter support - const Datasource = VScroll.makeDatasource(); - datasourceInstance = new Datasource({ + if (noAdapter) { + // Pattern 1: Plain object (no adapter) + datasourceInstance = { get: datasourceGet, settings: datasourceSettings, devSettings: datasourceDevSettings - }); + }; } else { - // Pattern 1: Plain object (no adapter) - datasourceInstance = { + // Pattern 2: Create Datasource class with adapter support + const Datasource = VScroll.makeDatasource(); + datasourceInstance = new Datasource({ get: datasourceGet, settings: datasourceSettings, devSettings: datasourceDevSettings - }; + }); } // Create workflow @@ -254,7 +254,7 @@ export class VScrollFixture { datasourceSettings, datasourceDevSettings, templateFnStr, - useAdapter: !!useAdapter + noAdapter: !!noAdapter } ); } diff --git a/tests/e2e/fixture/create-fixture.ts b/tests/e2e/fixture/create-fixture.ts index 9e59d371..7e146523 100644 --- a/tests/e2e/fixture/create-fixture.ts +++ b/tests/e2e/fixture/create-fixture.ts @@ -17,10 +17,10 @@ export const createFixture = async ({ page, config }: FixtureParams): Promise - `
${item.$index}: ${item.data.text}
` + `
${item.$index}: ${item.data.text}
`, + noAdapter: config.noAdapter }); // Wait for initial workflow cycle to complete diff --git a/tests/e2e/specs/recreation.spec.ts b/tests/e2e/specs/recreation.spec.ts index 314eeb7e..21fb75fc 100644 --- a/tests/e2e/specs/recreation.spec.ts +++ b/tests/e2e/specs/recreation.spec.ts @@ -21,17 +21,20 @@ test.describe('Recreation Spec', () => { datasourceGet, datasourceSettings: { startIndex: 1, bufferSize: 5, padding: 0.5 }, templateSettings: { viewportHeight: 200, itemHeight: 20 }, - noRelaxOnStart: true + noAdapter: true // plain DS }; const fixture = await createFixture({ page, config }); - // Store datasource-adapter id before cleanup - const before = await page.evaluate(() => ({ - id: window.__vscroll__.datasource.adapter.id, - init: window.__vscroll__.datasource.adapter.init, - disposed: window.__vscroll__.workflow.disposed - })); + const getWorkflowData = () => + page.evaluate(() => ({ + workflowInit: window.__vscroll__.workflow.isInitialized, + internalAdapterInit: window.__vscroll__.workflow?.scroller?.adapter?.init, + disposed: window.__vscroll__.workflow.disposed + })); + + // Store workflow data before cleanup + const before = await getWorkflowData(); // Destroy the scroller await fixture.dispose(); @@ -39,18 +42,21 @@ test.describe('Recreation Spec', () => { // Wait a bit to ensure no async errors await page.waitForTimeout(25); - // Verify datasource still exists with its adapter (wasn't reset/destroyed) - const after = await page.evaluate(() => ({ - id: window.__vscroll__.datasource.adapter.id, - init: window.__vscroll__.datasource.adapter.init, - disposed: window.__vscroll__.workflow.disposed - })); + // Store workflow data after cleanup + const after = await getWorkflowData(); - expect(before.id).toBe(after.id); - expect(before.init).toBe(!after.init); - expect(before.disposed).toBe(!after.disposed); + expect(before.workflowInit).toBe(true); + expect(after.workflowInit).toBe(false); + expect(before.internalAdapterInit).toBe(true); + expect(after.internalAdapterInit).toBe(undefined); + expect(before.disposed).toBe(false); + expect(after.disposed).toBe(true); }); + // Note: Test 1.2 ('should not reset Datasource on destroy via ngIf') is deferred. + // It requires viewport removal detection feature (Routines.onViewportRemoved with MutationObserver) + // to automatically call workflow.dispose() when viewport element is removed from DOM. }); + }); diff --git a/tests/e2e/types/index.ts b/tests/e2e/types/index.ts index 08547c3e..036f16c6 100644 --- a/tests/e2e/types/index.ts +++ b/tests/e2e/types/index.ts @@ -1,10 +1,11 @@ import type { Page } from '@playwright/test'; -import type { IDatasource, Item } from '../../../src/index.js'; +import type { IDatasource, Item, Workflow } from '../../../src/index.js'; import type { Settings, DevSettings } from '../../../src/interfaces/settings.js'; export type { Page }; -export type { IDatasource, Item }; +export type { IDatasource, Item, Workflow }; + interface ITemplateSettings { viewportHeight?: number; viewportWidth?: number; @@ -20,13 +21,14 @@ export interface ITestConfig { datasourceSettings: Settings; datasourceDevSettings?: DevSettings; templateSettings: ITemplateSettings; + noAdapter?: boolean; noRelaxOnStart?: boolean; custom?: Custom; } export interface VScrollFixtureConfig { datasource: IDatasource; - useAdapter?: boolean; // If true, creates Datasource instance with adapter support + noAdapter?: boolean; templateSettings?: ITemplateSettings; templateFn?: (item: unknown) => string; } \ No newline at end of file From 5a09b485de040538a7a3629f11db0a9accac0ca1 Mon Sep 17 00:00:00 2001 From: Denis Aleksanov Date: Mon, 17 Nov 2025 00:05:08 +0100 Subject: [PATCH 31/51] issue-66 manualRun and makeScroller --- tests/e2e/fixture/VScrollFixture.ts | 68 +++++++++++++++++------------ tests/e2e/fixture/create-fixture.ts | 7 +-- tests/e2e/types/global.d.ts | 5 +-- tests/e2e/types/index.ts | 2 + 4 files changed, 49 insertions(+), 33 deletions(-) diff --git a/tests/e2e/fixture/VScrollFixture.ts b/tests/e2e/fixture/VScrollFixture.ts index dc450de1..8ebc23e4 100644 --- a/tests/e2e/fixture/VScrollFixture.ts +++ b/tests/e2e/fixture/VScrollFixture.ts @@ -126,7 +126,7 @@ export class VScrollFixture { * 2. Datasource class with adapter (adapter tests) */ private async initializeWorkflow(): Promise { - const { datasource, templateFn, noAdapter } = this.config; + const { datasource, templateFn, noAdapter, manualRun } = this.config; // Serialize functions and config const datasourceGetStr = datasource.get ? datasource.get.toString() : null; @@ -150,7 +150,8 @@ export class VScrollFixture { datasourceSettings, datasourceDevSettings, templateFnStr, - noAdapter + noAdapter, + manualRun }) => { const VScroll = window.VScroll; @@ -158,9 +159,6 @@ export class VScrollFixture { const datasourceGet = eval(`(${datasourceGetStr})`); const templateFn = eval(`(${templateFnStr})`); - // Old items storage - let oldItems: Item[] = []; - // Renderer const processItems = (newItems: Item[], oldItems: Item[]) => { // Remove elements not present @@ -224,37 +222,51 @@ export class VScrollFixture { }); } - // Create workflow - const workflowParams = { - consumer: { name: 'vscroll-e2e', version: '1.0.0' }, - element: document.getElementById('vscroll'), - datasource: datasourceInstance, - run: (newItems: unknown[]) => { - const items = newItems as Item[]; - if (!items.length && !oldItems.length) { - return; - } - processItems(items, oldItems); - oldItems = items; - } - }; - const workflow = new VScroll.Workflow(workflowParams); - // Expose for testing window.__vscroll__ = { - workflowParams, - workflow, + workflow: undefined, // Will be set by createWorkflowFactory() + makeScroller: undefined, datasource: datasourceInstance, - oldItems, Direction: { forward: 'forward', backward: 'backward' } }; + + // Factory to create workflow factory with fresh oldItems closure + const workflowFactory = () => { + let oldItems: Item[] = []; + + const workflowParams = { + consumer: { name: 'vscroll-e2e', version: '1.0.0' }, + element: document.getElementById('vscroll'), + datasource: datasourceInstance, + run: (newItems: unknown[]) => { + const items = newItems as Item[]; + if (!items.length && !oldItems.length) { + return; + } + processItems(items, oldItems); + oldItems = items; + } + }; + + return () => new VScroll.Workflow(workflowParams); + }; + + // Creates Workflow with fresh closure and updates window.__vscroll__.workflow + const makeScroller = () => window.__vscroll__.workflow = workflowFactory()(); + window.__vscroll__.makeScroller = makeScroller; + + // Create initial workflow (unless manualRun) + if (!manualRun) { + makeScroller(); + } }, { datasourceGetStr, datasourceSettings, datasourceDevSettings, templateFnStr, - noAdapter: !!noAdapter + noAdapter: !!noAdapter, + manualRun: !!manualRun } ); } @@ -339,7 +351,7 @@ export class VScrollFixture { await this.page.evaluate(() => { return new Promise((resolve) => { const workflow = window.__vscroll__.workflow; - const ds = window.__vscroll__.datasource; + const ds = workflow.scroller.datasource; const initialCycles = workflow.cyclesDone; // If already loading, we're good @@ -368,7 +380,9 @@ export class VScrollFixture { */ async relaxNext(): Promise { await this.waitForLoadingStart(); - await this.adapter.relax(); + await this.page.evaluate(() => + window.__vscroll__.workflow.scroller.datasource.adapter.relax() + ); } diff --git a/tests/e2e/fixture/create-fixture.ts b/tests/e2e/fixture/create-fixture.ts index 7e146523..71c5646f 100644 --- a/tests/e2e/fixture/create-fixture.ts +++ b/tests/e2e/fixture/create-fixture.ts @@ -20,11 +20,12 @@ export const createFixture = async ({ page, config }: FixtureParams): Promise `
${item.$index}: ${item.data.text}
`, - noAdapter: config.noAdapter + noAdapter: config.noAdapter, + manualRun: config.manualRun }); - // Wait for initial workflow cycle to complete - if (!config.noRelaxOnStart) { + if (!config.manualRun && !config.noRelaxOnStart) { + // Wait for initial workflow cycle to complete await fixture.relaxNext(); } diff --git a/tests/e2e/types/global.d.ts b/tests/e2e/types/global.d.ts index 6f6fd209..fb54198a 100644 --- a/tests/e2e/types/global.d.ts +++ b/tests/e2e/types/global.d.ts @@ -9,12 +9,11 @@ import type { Workflow } from '../../../src/workflow'; import type { Direction } from '../../../src/inputs/common'; interface VScrollTest { - workflowParams: WorkflowParams; - workflow: Workflow | Record; + workflow: Workflow; datasource: { adapter?: IAdapter; }; - oldItems: Item[]; + makeScroller?: () => Workflow; // Creates Workflow with fresh oldItems closure (when manualRun) Direction: { forward: string; backward: string; diff --git a/tests/e2e/types/index.ts b/tests/e2e/types/index.ts index 036f16c6..1f3ee15c 100644 --- a/tests/e2e/types/index.ts +++ b/tests/e2e/types/index.ts @@ -23,6 +23,7 @@ export interface ITestConfig { templateSettings: ITemplateSettings; noAdapter?: boolean; noRelaxOnStart?: boolean; + manualRun?: boolean; custom?: Custom; } @@ -31,4 +32,5 @@ export interface VScrollFixtureConfig { noAdapter?: boolean; templateSettings?: ITemplateSettings; templateFn?: (item: unknown) => string; + manualRun?: boolean; } \ No newline at end of file From ff9bbee7834211fbebd0754c77377b3ab91bbe3d Mon Sep 17 00:00:00 2001 From: Denis Aleksanov Date: Mon, 17 Nov 2025 00:05:32 +0100 Subject: [PATCH 32/51] issue-66 recreation.spec (2): should switch Adapter.init trice --- tests/e2e/specs/recreation.spec.ts | 51 ++++++++++++++++++++++++++++++ tests/e2e/types/global.d.ts | 1 - 2 files changed, 51 insertions(+), 1 deletion(-) diff --git a/tests/e2e/specs/recreation.spec.ts b/tests/e2e/specs/recreation.spec.ts index 21fb75fc..7f502691 100644 --- a/tests/e2e/specs/recreation.spec.ts +++ b/tests/e2e/specs/recreation.spec.ts @@ -58,5 +58,56 @@ test.describe('Recreation Spec', () => { // to automatically call workflow.dispose() when viewport element is removed from DOM. }); + test.describe('Recreation via ngIf (instance DS)', () => { + + test('should switch Adapter.init trice', async ({ page }) => { + const config: ITestConfig = { + datasourceGet, + datasourceSettings: { startIndex: 1, bufferSize: 5, padding: 0.5 }, + templateSettings: { viewportHeight: 200, itemHeight: 20 }, + manualRun: true + }; + + const fixture = await createFixture({ page, config }); + + // Perform dispose and recreate cycle + const count = await page.evaluate(() => new Promise((resolve) => { + const { datasource, makeScroller } = window.__vscroll__; + + let emissionCount = 0; + + // Subscribe to init$ BEFORE creating workflow + const off = datasource.adapter.init$.on(() => { + if (++emissionCount === 3) { + off(); + resolve(emissionCount); + } + }); + + // Create and run initial scroller workflow + makeScroller!(); + + const checkAndRecreate = () => { + if (!datasource.adapter.isLoading && datasource.adapter.init) { + window.__vscroll__.workflow.dispose(); + + setTimeout(() => { + // Create and run new scroller workflow + makeScroller!(); + }, 25); + } else { + setTimeout(checkAndRecreate, 10); + } + }; + checkAndRecreate(); + })); + + // Check emission count + expect(count).toBe(3); + + await fixture.dispose(); + }); + + }); }); diff --git a/tests/e2e/types/global.d.ts b/tests/e2e/types/global.d.ts index fb54198a..dc9aafaa 100644 --- a/tests/e2e/types/global.d.ts +++ b/tests/e2e/types/global.d.ts @@ -4,7 +4,6 @@ */ import type { IAdapter } from '../../../src/interfaces/adapter'; -import type { WorkflowParams, Item } from '../../../src/interfaces/index'; import type { Workflow } from '../../../src/workflow'; import type { Direction } from '../../../src/inputs/common'; From 715e6c033515e343cf5c89f51509e2732d95a3b7 Mon Sep 17 00:00:00 2001 From: Denis Aleksanov Date: Mon, 17 Nov 2025 02:45:30 +0100 Subject: [PATCH 33/51] issue-66 recreation.spec (2): should switch Adapter.init trice: wfInitOnFirstMake --- tests/e2e/specs/recreation.spec.ts | 19 +++++++++++++------ 1 file changed, 13 insertions(+), 6 deletions(-) diff --git a/tests/e2e/specs/recreation.spec.ts b/tests/e2e/specs/recreation.spec.ts index 7f502691..dbfacffe 100644 --- a/tests/e2e/specs/recreation.spec.ts +++ b/tests/e2e/specs/recreation.spec.ts @@ -71,22 +71,26 @@ test.describe('Recreation Spec', () => { const fixture = await createFixture({ page, config }); // Perform dispose and recreate cycle - const count = await page.evaluate(() => new Promise((resolve) => { + type Result = { adapterInitCount: number; wfInitOnFirstMake?: boolean; }; + const result = await page.evaluate(() => new Promise((resolve) => { const { datasource, makeScroller } = window.__vscroll__; - let emissionCount = 0; + const result: Result = { adapterInitCount: 0 }; // Subscribe to init$ BEFORE creating workflow const off = datasource.adapter.init$.on(() => { - if (++emissionCount === 3) { + if (++result.adapterInitCount === 3) { off(); - resolve(emissionCount); + resolve(result); } }); // Create and run initial scroller workflow makeScroller!(); + // To make sure the workflow is not yet initialized immediately after creation + result.wfInitOnFirstMake = window.__vscroll__.workflow.isInitialized; + const checkAndRecreate = () => { if (!datasource.adapter.isLoading && datasource.adapter.init) { window.__vscroll__.workflow.dispose(); @@ -102,8 +106,11 @@ test.describe('Recreation Spec', () => { checkAndRecreate(); })); - // Check emission count - expect(count).toBe(3); + // Workflow should not be initialized immediately after creation + expect(result.wfInitOnFirstMake).toBe(false); + + // Adapter should be initialized 3 times + expect(result.adapterInitCount).toBe(3); await fixture.dispose(); }); From 8c243bec271f168856cfb1bb6a4850aff5f4dd58 Mon Sep 17 00:00:00 2001 From: Denis Aleksanov Date: Fri, 21 Nov 2025 00:49:12 +0100 Subject: [PATCH 34/51] issue-66 recreation.spec (3): should re-render the viewport --- tests/e2e/fixture/VScrollFixture.ts | 17 ++++++++++- tests/e2e/specs/recreation.spec.ts | 44 +++++++++++++++++++++++++++-- 2 files changed, 58 insertions(+), 3 deletions(-) diff --git a/tests/e2e/fixture/VScrollFixture.ts b/tests/e2e/fixture/VScrollFixture.ts index 8ebc23e4..0c4cc1f1 100644 --- a/tests/e2e/fixture/VScrollFixture.ts +++ b/tests/e2e/fixture/VScrollFixture.ts @@ -385,7 +385,6 @@ export class VScrollFixture { ); } - /** * Dispose */ @@ -395,5 +394,21 @@ export class VScrollFixture { document.querySelector('#viewport')?.remove(); }); } + + /** + * Recreate the scroller + * Disposes the current workflow, resets the viewport, and creates a new workflow + */ + async recreateScroller(): Promise { + await this.page.evaluate(() => { + window.__vscroll__.workflow.dispose(); + + // Reset vscroll element to initial state + const vscroll = document.getElementById('vscroll') as HTMLElement; + vscroll.innerHTML = '
'; + + window.__vscroll__.makeScroller!(); + }); + } } diff --git a/tests/e2e/specs/recreation.spec.ts b/tests/e2e/specs/recreation.spec.ts index dbfacffe..ea2d4e71 100644 --- a/tests/e2e/specs/recreation.spec.ts +++ b/tests/e2e/specs/recreation.spec.ts @@ -10,7 +10,7 @@ const datasourceGet = (index: number, count: number, success: (data: unknown[]) for (let i = index; i < index + count; i++) { data.push({ id: i, text: `item #${i}` }); } - success(data); + setTimeout(() => success(data), 0); }; test.describe('Recreation Spec', () => { @@ -115,6 +115,46 @@ test.describe('Recreation Spec', () => { await fixture.dispose(); }); + test('should re-render the viewport', async ({ page }) => { + const config: ITestConfig = { + datasourceGet, + datasourceSettings: { startIndex: 1, bufferSize: 5, padding: 0.5 }, + templateSettings: { viewportHeight: 200, itemHeight: 20 }, + datasourceDevSettings: { debug: true, immediateLog: true } + }; + + const fixture = await createFixture({ page, config }); + + // Helper to capture state + const getState = () => + page.evaluate(() => ({ + workflowInit: window.__vscroll__.workflow.isInitialized, + scrollerId: window.__vscroll__.workflow.scroller.settings.instanceIndex, + internalAdapterId: window.__vscroll__.workflow.scroller.adapter.id, + adapterId: window.__vscroll__.datasource.adapter.id + }) + ); + + // Capture initial state + const before = await getState(); + const beforeElements = await fixture.getElements(); + + // Dispose and recreate + await fixture.recreateScroller(); + await fixture.adapter.relax(); + + // Capture state after recreation + const after = await getState(); + const afterElements = await fixture.getElements(); + + // Verify expectations + expect(after.workflowInit).toBe(true); + expect(after.scrollerId).toBe(before.scrollerId + 1); // New scroller instance + expect(after.internalAdapterId).toBe(before.adapterId); // Scroller uses same adapter + expect(after.adapterId).toBe(before.adapterId); // Same external adapter + expect(afterElements.length).toBe(beforeElements.length); // Same number of elements + + await fixture.dispose(); + }); }); }); - From d1f0d2d3655ba77c4d4a0750f8cf1bb7b5da11ab Mon Sep 17 00:00:00 2001 From: Denis Aleksanov Date: Fri, 21 Nov 2025 02:48:56 +0100 Subject: [PATCH 35/51] issue-66 recreation.spec (4): should scroll and take firstVisible --- tests/e2e/fixture/VScrollFixture.ts | 19 +++++++++---------- tests/e2e/specs/recreation.spec.ts | 29 +++++++++++++++++++++++++++-- 2 files changed, 36 insertions(+), 12 deletions(-) diff --git a/tests/e2e/fixture/VScrollFixture.ts b/tests/e2e/fixture/VScrollFixture.ts index 0c4cc1f1..f21395e5 100644 --- a/tests/e2e/fixture/VScrollFixture.ts +++ b/tests/e2e/fixture/VScrollFixture.ts @@ -286,8 +286,6 @@ export class VScrollFixture { }; } - - /** * Scroll to a specific position. Waits for the scroller to relax if the scroll position changed. * @param position - The position to scroll to @@ -347,30 +345,31 @@ export class VScrollFixture { * Wait for workflow to start loading (a new cycle to begin) * This ensures the workflow has started processing before we proceed */ - async waitForLoadingStart(): Promise { - await this.page.evaluate(() => { - return new Promise((resolve) => { + waitForLoadingStart(): Promise { + return this.page.evaluate(() => + new Promise((resolve) => { const workflow = window.__vscroll__.workflow; const ds = workflow.scroller.datasource; const initialCycles = workflow.cyclesDone; // If already loading, we're good - if (ds.adapter && ds.adapter.isLoading) { + if (ds?.adapter?.isLoading) { resolve(); return; } // Otherwise wait for a new cycle to start OR loading to begin - const check = () => { - if (workflow.cyclesDone > initialCycles || (ds.adapter && ds.adapter.isLoading)) { + const check = async () => { + if (workflow.cyclesDone > initialCycles && ds?.adapter) { + await ds.adapter.relax(); resolve(); } else { requestAnimationFrame(check); } }; check(); - }); - }); + }) + ); } /** diff --git a/tests/e2e/specs/recreation.spec.ts b/tests/e2e/specs/recreation.spec.ts index ea2d4e71..e0640fe5 100644 --- a/tests/e2e/specs/recreation.spec.ts +++ b/tests/e2e/specs/recreation.spec.ts @@ -119,8 +119,7 @@ test.describe('Recreation Spec', () => { const config: ITestConfig = { datasourceGet, datasourceSettings: { startIndex: 1, bufferSize: 5, padding: 0.5 }, - templateSettings: { viewportHeight: 200, itemHeight: 20 }, - datasourceDevSettings: { debug: true, immediateLog: true } + templateSettings: { viewportHeight: 200, itemHeight: 20 } }; const fixture = await createFixture({ page, config }); @@ -156,5 +155,31 @@ test.describe('Recreation Spec', () => { await fixture.dispose(); }); + + test('should scroll and take firstVisible', async ({ page }) => { + const config: ITestConfig = { + datasourceGet, + datasourceSettings: { startIndex: 1, bufferSize: 5, padding: 0.5 }, + templateSettings: { viewportHeight: 200, itemHeight: 20 } + }; + + const fixture = await createFixture({ page, config }); + + await fixture.recreateScroller(); + await page.waitForFunction(() => window.__vscroll__.datasource.adapter.init); + await fixture.adapter.relax(); + + const firstVisible = await fixture.adapter.firstVisible; + expect(firstVisible.$index).toBe(1); + + const cyclesDone = await page.evaluate(() => window.__vscroll__.workflow.cyclesDone); + await fixture.adapter.fix({ scrollPosition: 200 }); + await page.waitForFunction((prevCycles) => window.__vscroll__.workflow.cyclesDone > prevCycles, cyclesDone); + + const firstVisibleAfter = await fixture.adapter.firstVisible; + expect(firstVisibleAfter.$index).toBeGreaterThan(1); + + await fixture.dispose(); + }); }); }); From 92dc5899d1a50301e7cef58d062e068bd6075d87 Mon Sep 17 00:00:00 2001 From: Denis Aleksanov Date: Sat, 22 Nov 2025 00:44:34 +0100 Subject: [PATCH 36/51] issue-66 recreation.spec: scrollTo refactoring --- tests/e2e/fixture/VScrollFixture.ts | 27 +++++++++++++++++++-------- tests/e2e/specs/recreation.spec.ts | 4 +--- 2 files changed, 20 insertions(+), 11 deletions(-) diff --git a/tests/e2e/fixture/VScrollFixture.ts b/tests/e2e/fixture/VScrollFixture.ts index f21395e5..91e39109 100644 --- a/tests/e2e/fixture/VScrollFixture.ts +++ b/tests/e2e/fixture/VScrollFixture.ts @@ -287,17 +287,28 @@ export class VScrollFixture { } /** - * Scroll to a specific position. Waits for the scroller to relax if the scroll position changed. + * Scroll to a specific position and wait for Scroller to relax. * @param position - The position to scroll to - * @param options.noRelax - If true, the adapter will not be relaxed even if the scroll position changed + * @param options.noRelax - If true, no wait for relaxation */ async scrollTo(position: number, options: { noRelax?: boolean } = {}): Promise { - const positionBefore = await this.scroller.viewport.scrollPosition; - await this.adapter.fix({ scrollPosition: position }); - const positionAfter = await this.scroller.viewport.scrollPosition; - if (!options?.noRelax && positionBefore !== positionAfter) { - await this.relaxNext(); - } + return this.page.evaluate(async ({ position, options }) => { + const workflow = window.__vscroll__.workflow; + const viewport = workflow.scroller.viewport; + const adapter = workflow.scroller.datasource.adapter; + const cyclesDone = workflow.cyclesDone; + const positionBefore = viewport.scrollPosition; + await adapter.fix({ scrollPosition: position }); + if (options?.noRelax || viewport.scrollPosition === positionBefore) { + return; + } + if (!adapter.isLoading) { + // The new workflow cycle should run and complete + while (workflow.cyclesDone === cyclesDone) { + await new Promise(resolve => setTimeout(resolve)); + } + } + }, { position, options }); } /** diff --git a/tests/e2e/specs/recreation.spec.ts b/tests/e2e/specs/recreation.spec.ts index e0640fe5..75f082ca 100644 --- a/tests/e2e/specs/recreation.spec.ts +++ b/tests/e2e/specs/recreation.spec.ts @@ -172,9 +172,7 @@ test.describe('Recreation Spec', () => { const firstVisible = await fixture.adapter.firstVisible; expect(firstVisible.$index).toBe(1); - const cyclesDone = await page.evaluate(() => window.__vscroll__.workflow.cyclesDone); - await fixture.adapter.fix({ scrollPosition: 200 }); - await page.waitForFunction((prevCycles) => window.__vscroll__.workflow.cyclesDone > prevCycles, cyclesDone); + await fixture.scrollTo(200); const firstVisibleAfter = await fixture.adapter.firstVisible; expect(firstVisibleAfter.$index).toBeGreaterThan(1); From 1fdcae233d3dd94de58acd1f58b957ff8b319cef Mon Sep 17 00:00:00 2001 From: Denis Aleksanov Date: Sat, 22 Nov 2025 02:07:58 +0100 Subject: [PATCH 37/51] issue-66 min-max-indexes.spec --- tests/e2e/fixture/Adapter.ts | 10 + tests/e2e/fixture/VScrollFixture.ts | 6 +- tests/e2e/specs/min-max-indexes.spec.ts | 438 ++++++++++++++++++++++++ 3 files changed, 453 insertions(+), 1 deletion(-) create mode 100644 tests/e2e/specs/min-max-indexes.spec.ts diff --git a/tests/e2e/fixture/Adapter.ts b/tests/e2e/fixture/Adapter.ts index 408b8b30..bf5f0369 100644 --- a/tests/e2e/fixture/Adapter.ts +++ b/tests/e2e/fixture/Adapter.ts @@ -230,5 +230,15 @@ export class Adapter { return ds.adapter.lastVisible; }); } + + get bufferInfo() { + return this.page.evaluate(() => { + const ds = window.__vscroll__.datasource; + if (!ds.adapter) { + throw new Error('Adapter not available. Set useAdapter: true in config.'); + } + return ds.adapter.bufferInfo; + }); + } } diff --git a/tests/e2e/fixture/VScrollFixture.ts b/tests/e2e/fixture/VScrollFixture.ts index 91e39109..70a16557 100644 --- a/tests/e2e/fixture/VScrollFixture.ts +++ b/tests/e2e/fixture/VScrollFixture.ts @@ -277,11 +277,15 @@ export class VScrollFixture { get workflow() { const page = this.page; return { - // Proxy methods to browser get cyclesDone(): Promise { return page.evaluate(() => { return window.__vscroll__.workflow.cyclesDone; }); + }, + get innerLoopCount(): Promise { + return page.evaluate(() => { + return window.__vscroll__.workflow.scroller.state.cycle.innerLoop.total; + }); } }; } diff --git a/tests/e2e/specs/min-max-indexes.spec.ts b/tests/e2e/specs/min-max-indexes.spec.ts new file mode 100644 index 00000000..06d89fb8 --- /dev/null +++ b/tests/e2e/specs/min-max-indexes.spec.ts @@ -0,0 +1,438 @@ +import { test, expect } from '@playwright/test'; +import { VScrollFixture, Direction } from '../fixture/VScrollFixture.js'; +import { afterEachLogs } from '../fixture/after-each-logs.js'; +import { createFixture } from '../fixture/create-fixture.js'; +import { ITestConfig } from 'types/index.js'; + +test.afterEach(afterEachLogs); + +const configList: ITestConfig[] = [ + { + datasourceGet: (index, count, success) => { + const data = []; + for (let i = index; i < index + count; i++) { + if (i >= -49 && i <= 100) { + data.push({ id: i, text: `item #${i}` }); + } + } + setTimeout(() => success(data), 25); + }, + datasourceSettings: { + startIndex: 1, + padding: 0.5, + itemSize: 20, + minIndex: -49, + maxIndex: 100, + adapter: true + }, + templateSettings: { viewportHeight: 200, itemHeight: 20 } + }, + { + datasourceGet: (index, count, success) => { + const data = []; + for (let i = index; i < index + count; i++) { + if (i >= -69 && i <= 1300) { + data.push({ id: i, text: `item #${i}` }); + } + } + setTimeout(() => success(data), 25); + }, + datasourceSettings: { + startIndex: 600, + padding: 1.2, + itemSize: 40, + minIndex: -69, + maxIndex: 1300, + adapter: true + }, + templateSettings: { viewportHeight: 100, itemHeight: 40 } + }, + { + datasourceGet: (index, count, success) => { + const data = []; + for (let i = index; i < index + count; i++) { + if (i >= 169 && i <= 230) { + data.push({ id: i, text: `item #${i}` }); + } + } + setTimeout(() => success(data), 25); + }, + datasourceSettings: { + startIndex: 174, + padding: 0.7, + itemSize: 25, + minIndex: 169, + maxIndex: 230, + adapter: true + }, + templateSettings: { viewportHeight: 50, itemHeight: 25 } + }, + { + datasourceGet: (index, count, success) => { + const data = []; + for (let i = index; i < index + count; i++) { + if (i >= 20 && i <= 230) { + data.push({ id: i, text: `item #${i}` }); + } + } + setTimeout(() => success(data), 25); + }, + datasourceSettings: { + startIndex: 33, + padding: 0.62, + itemSize: 100, + minIndex: 20, + maxIndex: 230, + horizontal: true, + adapter: true + }, + templateSettings: { viewportWidth: 450, itemWidth: 100, horizontal: true } + }, + { + datasourceGet: (index, count, success) => { + const data = []; + for (let i = index; i < index + count; i++) { + if (i >= -40 && i <= 159) { + data.push({ id: i, text: `item #${i}` }); + } + } + setTimeout(() => success(data), 25); + }, + datasourceSettings: { + startIndex: 1, + padding: 0.25, + itemSize: 20, + minIndex: -40, + maxIndex: 159, + windowViewport: true, + adapter: true + }, + templateSettings: { + noViewportClass: true, + viewportHeight: 0, + itemHeight: 20 + } + } +]; + +const noItemSizeConfigList: ITestConfig[] = configList.map( + ({ + datasourceGet, + datasourceSettings: { itemSize: _, ...restDatasourceSettings }, + ...config + }) => ({ + ...config, + datasourceGet, + datasourceSettings: { ...restDatasourceSettings } + }) +); + +const commonConfig = configList[0]; // [-49, ..., 100] + +const startIndexAroundMinIndexConfigList: ITestConfig[] = [ + { + ...commonConfig, + datasourceSettings: { + ...commonConfig.datasourceSettings, + startIndex: -9999 + } + }, + { + ...commonConfig, + datasourceSettings: { ...commonConfig.datasourceSettings, startIndex: -50 } + }, + { + ...commonConfig, + datasourceSettings: { ...commonConfig.datasourceSettings, startIndex: -49 } + }, + { + ...commonConfig, + datasourceSettings: { ...commonConfig.datasourceSettings, startIndex: -48 } + } +]; + +const startIndexAroundMaxIndexConfigList: ITestConfig[] = [ + { + ...commonConfig, + datasourceSettings: { ...commonConfig.datasourceSettings, startIndex: 99 } + }, + { + ...commonConfig, + datasourceSettings: { ...commonConfig.datasourceSettings, startIndex: 100 } + }, + { + ...commonConfig, + datasourceSettings: { ...commonConfig.datasourceSettings, startIndex: 101 } + }, + { + ...commonConfig, + datasourceSettings: { ...commonConfig.datasourceSettings, startIndex: 999 } + } +]; + +const noMaxIndexConfigList: ITestConfig[] = configList.map( + ({ + datasourceGet, + datasourceSettings: { maxIndex: _, ...datasourceSettings }, + ...config + }) => ({ ...config, datasourceGet, datasourceSettings }) +); + +const noMinIndexConfigList: ITestConfig[] = configList.map( + ({ + datasourceGet, + datasourceSettings: { minIndex: _, ...datasourceSettings }, + ...config + }) => ({ ...config, datasourceGet, datasourceSettings }) +); + +const forwardGapConfigList: ITestConfig[] = startIndexAroundMaxIndexConfigList.map( + ({ + datasourceGet, + datasourceSettings: { minIndex: _, ...datasourceSettings }, + ...config + }) => ({ ...config, datasourceGet, datasourceSettings }) +); + +const checkMinMaxIndexes = async (fixture: VScrollFixture) => { + const elements = await fixture.getElements(); + const bufferInfo = await fixture.adapter.bufferInfo; + const { firstIndex, lastIndex, minIndex, maxIndex } = bufferInfo; + + expect(firstIndex).toEqual(minIndex); + expect(lastIndex).toEqual(maxIndex); + expect(minIndex).toEqual(await fixture.getElementIndex(elements[0] as HTMLElement)); + expect(maxIndex).toEqual(await fixture.getElementIndex(elements[elements.length - 1] as HTMLElement)); +}; + +const getParams = ({ + datasourceSettings: settings +}: ITestConfig): { + maxIndex: number; + minIndex: number; + itemSize: number; + startIndex: number; + padding: number; +} => ({ + maxIndex: settings.maxIndex as number, + minIndex: settings.minIndex as number, + itemSize: settings.itemSize as number, + startIndex: settings.startIndex as number, + padding: settings.padding as number +}); + +const testCommonCase = async (fixture: VScrollFixture, config: ITestConfig) => { + await fixture.adapter.relax(); + + const bufferSize = await fixture.scroller.settings.bufferSize; + const { + maxIndex, + minIndex, + itemSize: _itemSize, + startIndex, + padding + } = getParams(config); + const viewportSize = await fixture.scroller.viewport.getSize(); + const viewportSizeDelta = viewportSize * padding; + const itemSize = _itemSize || (await fixture.scroller.buffer.defaultSize); + const hasMinIndex = minIndex !== undefined; + const hasMaxIndex = maxIndex !== undefined; + let innerLoopCount = 3; + + const _negativeItemsAmount = Math.ceil(viewportSizeDelta / itemSize); + const negativeItemsAmount = Math.max(_negativeItemsAmount, bufferSize); + const _positiveItemsAmount = Math.ceil( + (viewportSize + viewportSizeDelta) / itemSize + ); + let positiveItemsAmount = Math.max(_positiveItemsAmount, bufferSize); + + if (!_itemSize) { + // if Settings.itemSize is not set, then there could be 1 more fetch + const positiveDiff = _positiveItemsAmount - bufferSize; + if (positiveDiff > 0) { + innerLoopCount = 4; + // if the additional fetch size is less than bufferSize + if (positiveDiff < bufferSize) { + positiveItemsAmount = 2 * bufferSize; + } + } + } + + const bufferInfo = await fixture.adapter.bufferInfo; + + if (hasMinIndex) { + const negativeSize = (startIndex - minIndex) * itemSize; + const negativeItemsSize = negativeItemsAmount * itemSize; + const bwdPaddingSize = negativeSize - negativeItemsSize; + expect(await fixture.scroller.viewport.paddings[Direction.backward].size).toEqual(bwdPaddingSize); + expect(bufferInfo.absMinIndex).toEqual(minIndex); + } else { + expect(bufferInfo.absMinIndex).toEqual(-Infinity); + } + + if (hasMaxIndex) { + const positiveSize = (maxIndex - startIndex + 1) * itemSize; + const positiveItemsSize = positiveItemsAmount * itemSize; + const fwdPaddingSize = positiveSize - positiveItemsSize; + expect(await fixture.scroller.viewport.paddings[Direction.forward].size).toEqual(fwdPaddingSize); + expect(bufferInfo.absMaxIndex).toEqual(maxIndex); + } else { + expect(bufferInfo.absMaxIndex).toEqual(Infinity); + } + + let totalSize = 0; + if (hasMinIndex && hasMaxIndex) { + totalSize = (maxIndex - minIndex + 1) * itemSize; + } else if (hasMinIndex) { + const knownSize = (startIndex - minIndex) * itemSize; + totalSize = knownSize + positiveItemsAmount * itemSize; + } else if (hasMaxIndex) { + const knownSize = (maxIndex - startIndex + 1) * itemSize; + totalSize = knownSize + negativeItemsAmount * itemSize; + } + + expect(await fixture.scroller.viewport.getScrollableSize()).toEqual(totalSize); + expect(await fixture.workflow.innerLoopCount).toEqual(innerLoopCount); + await checkMinMaxIndexes(fixture); +}; + +const testStartIndexEdgeCase = async (fixture: VScrollFixture, config: ITestConfig) => { + await fixture.adapter.relax(); + + const { maxIndex, minIndex, itemSize, startIndex, padding } = getParams(config); + const bufferInfo = await fixture.adapter.bufferInfo; + const viewportSize = await fixture.scroller.viewport.getSize(); + const totalSize = (maxIndex - minIndex + 1) * itemSize; + const viewportSizeDelta = viewportSize * padding; + let _startIndex = Math.max(minIndex, startIndex); // startIndex < minIndex + _startIndex = Math.min(maxIndex, _startIndex); // startIndex > maxIndex + + // visible part of the viewport must be filled + const viewportItemsAmount = Math.ceil(viewportSize / itemSize); + const diff = _startIndex + viewportItemsAmount - maxIndex - 1; + if (diff > 0) { + _startIndex -= diff; + } + + const negativeSize = (_startIndex - minIndex) * itemSize; + const negativeItemsAmount = Math.ceil(viewportSizeDelta / itemSize); + const negativeItemsSize = negativeItemsAmount * itemSize; + const bwdPaddingSize = Math.max(0, negativeSize - negativeItemsSize); + + const positiveSize = (maxIndex - _startIndex + 1) * itemSize; + const positiveItemsAmount = Math.ceil( + (viewportSize + viewportSizeDelta) / itemSize + ); + const positiveItemsSize = positiveItemsAmount * itemSize; + const fwdPaddingSize = Math.max(0, positiveSize - positiveItemsSize); + + expect(await fixture.scroller.viewport.getScrollableSize()).toEqual(totalSize); + expect(await fixture.scroller.viewport.paddings[Direction.backward].size).toEqual(bwdPaddingSize); + expect(await fixture.scroller.viewport.paddings[Direction.forward].size).toEqual(fwdPaddingSize); + + expect(bufferInfo.absMinIndex).toEqual(minIndex); + expect(bufferInfo.absMaxIndex).toEqual(maxIndex); + await checkMinMaxIndexes(fixture); +}; + +const testForwardGapCase = async (fixture: VScrollFixture) => { + await fixture.adapter.relax(); + + const gapSize = await fixture.page.evaluate(() => { + const viewportChildren = window.__vscroll__.workflow.scroller.routines.element.children; + const lastChild = viewportChildren[viewportChildren.length - 2]; + const lastChildBottom = lastChild.getBoundingClientRect().bottom; + const viewport = window.__vscroll__.workflow.scroller.viewport; + const viewportSize = viewport.getSize(); + let gap = viewportSize - lastChildBottom; + gap = Math.max(0, gap); + return gap; + }); + + expect(gapSize).toEqual(0); + expect(await fixture.scroller.viewport.paddings[Direction.forward].size).toEqual(0); +}; + +const makeTest = ( + title: string, + config: ITestConfig, + testFunc: (fixture: VScrollFixture, config: ITestConfig) => Promise +) => { + test(title, async ({ page }) => { + const fixture = await createFixture({ page, config }); + await testFunc(fixture, config); + await fixture.dispose(); + }); +}; + +test.describe('Min/max Indexes Spec', () => { + test.describe('Common cases', () => + configList.forEach((config, index) => + makeTest( + `should fill the viewport and the paddings (config ${index})`, + config, + testCommonCase + ) + ) + ); + + test.describe('No maxIndex cases', () => + noMaxIndexConfigList.forEach((config, index) => + makeTest( + `should fill the viewport and backward padding (config ${index})`, + config, + testCommonCase + ) + ) + ); + + test.describe('No minIndex cases', () => + noMinIndexConfigList.forEach((config, index) => + makeTest( + `should fill the viewport and forward padding (config ${index})`, + config, + testCommonCase + ) + ) + ); + + test.describe('No itemSize cases', () => + noItemSizeConfigList.forEach((config, index) => + makeTest( + `should fill the viewport and the paddings (config ${index})`, + config, + testCommonCase + ) + ) + ); + + test.describe('startIndex\'s around minIndex', () => + startIndexAroundMinIndexConfigList.forEach((config, index) => + makeTest( + `should reset backward padding (config ${index})`, + config, + testStartIndexEdgeCase + ) + ) + ); + + test.describe('startIndex\'s around maxIndex', () => + startIndexAroundMaxIndexConfigList.forEach((config, index) => + makeTest( + `should reset forward padding (config ${index})`, + config, + testStartIndexEdgeCase + ) + ) + ); + + test.describe('startIndex\'s around maxIndex and no minIndex', () => + forwardGapConfigList.forEach((config, index) => + makeTest( + `should fill forward padding gap (config ${index})`, + config, + testForwardGapCase + ) + ) + ); +}); From 54f1343ce42d4edbac23a3251466936525745440 Mon Sep 17 00:00:00 2001 From: Denis Aleksanov Date: Sat, 22 Nov 2025 23:05:40 +0100 Subject: [PATCH 38/51] prettier husky lint-staged --- .husky/pre-commit | 1 + .prettierrc | 6 + lint-staged.config.js | 4 + package-lock.json | 460 ++++++++++++++++++++++++++++++++++++++++++ package.json | 5 +- 5 files changed, 475 insertions(+), 1 deletion(-) create mode 100644 .husky/pre-commit create mode 100644 .prettierrc create mode 100644 lint-staged.config.js diff --git a/.husky/pre-commit b/.husky/pre-commit new file mode 100644 index 00000000..2312dc58 --- /dev/null +++ b/.husky/pre-commit @@ -0,0 +1 @@ +npx lint-staged diff --git a/.prettierrc b/.prettierrc new file mode 100644 index 00000000..e5067d01 --- /dev/null +++ b/.prettierrc @@ -0,0 +1,6 @@ +{ + "tabWidth": 2, + "singleQuote": true, + "arrowParens": "avoid", + "trailingComma": "none" +} diff --git a/lint-staged.config.js b/lint-staged.config.js new file mode 100644 index 00000000..3235f5c4 --- /dev/null +++ b/lint-staged.config.js @@ -0,0 +1,4 @@ +export default { + '*.ts': () => 'npm run lint', + '*.{ts,js,json}': 'prettier --write' +}; diff --git a/package-lock.json b/package-lock.json index 81aa738b..6efa1207 100644 --- a/package-lock.json +++ b/package-lock.json @@ -19,6 +19,9 @@ "chalk": "^5.0.0", "eslint": "^9.32.0", "globals": "^16.3.0", + "husky": "^9.1.7", + "lint-staged": "^16.2.7", + "prettier": "^3.6.2", "rollup": "^4.45.1", "rollup-plugin-license": "^3.6.0", "rollup-plugin-sourcemaps2": "^0.5.3", @@ -1043,6 +1046,35 @@ "url": "https://github.com/sponsors/epoberezkin" } }, + "node_modules/ansi-escapes": { + "version": "7.2.0", + "resolved": "https://registry.npmjs.org/ansi-escapes/-/ansi-escapes-7.2.0.tgz", + "integrity": "sha512-g6LhBsl+GBPRWGWsBtutpzBYuIIdBkLEvad5C/va/74Db018+5TZiyA26cZJAr3Rft5lprVqOIPxf5Vid6tqAw==", + "dev": true, + "license": "MIT", + "dependencies": { + "environment": "^1.0.0" + }, + "engines": { + "node": ">=18" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/ansi-regex": { + "version": "6.2.2", + "resolved": "https://registry.npmjs.org/ansi-regex/-/ansi-regex-6.2.2.tgz", + "integrity": "sha512-Bq3SmSpyFHaWjPk8If9yc6svM8c56dB5BAtW4Qbw5jHTwwXXcTLoRMkpDJp6VL0XzlWaCHTXrkFURMYmD0sLqg==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=12" + }, + "funding": { + "url": "https://github.com/chalk/ansi-regex?sponsor=1" + } + }, "node_modules/ansi-styles": { "version": "4.3.0", "resolved": "https://registry.npmjs.org/ansi-styles/-/ansi-styles-4.3.0.tgz", @@ -1136,6 +1168,39 @@ "url": "https://github.com/chalk/chalk?sponsor=1" } }, + "node_modules/cli-cursor": { + "version": "5.0.0", + "resolved": "https://registry.npmjs.org/cli-cursor/-/cli-cursor-5.0.0.tgz", + "integrity": "sha512-aCj4O5wKyszjMmDT4tZj93kxyydN/K5zPWSCe6/0AV/AA1pqe5ZBIw0a2ZfPQV7lL5/yb5HsUreJ6UFAF1tEQw==", + "dev": true, + "license": "MIT", + "dependencies": { + "restore-cursor": "^5.0.0" + }, + "engines": { + "node": ">=18" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/cli-truncate": { + "version": "5.1.1", + "resolved": "https://registry.npmjs.org/cli-truncate/-/cli-truncate-5.1.1.tgz", + "integrity": "sha512-SroPvNHxUnk+vIW/dOSfNqdy1sPEFkrTk6TUtqLCnBlo3N7TNYYkzzN7uSD6+jVjrdO4+p8nH7JzH6cIvUem6A==", + "dev": true, + "license": "MIT", + "dependencies": { + "slice-ansi": "^7.1.0", + "string-width": "^8.0.0" + }, + "engines": { + "node": ">=20" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, "node_modules/color-convert": { "version": "2.0.1", "resolved": "https://registry.npmjs.org/color-convert/-/color-convert-2.0.1.tgz", @@ -1156,6 +1221,13 @@ "dev": true, "license": "MIT" }, + "node_modules/colorette": { + "version": "2.0.20", + "resolved": "https://registry.npmjs.org/colorette/-/colorette-2.0.20.tgz", + "integrity": "sha512-IfEDxwoWIjkeXL1eXcDiow4UbKjhLdq6/EuSVR9GMN7KVH3r9gQ83e73hsz1Nd1T3ijd5xv1wcWRYO+D6kCI2w==", + "dev": true, + "license": "MIT" + }, "node_modules/commander": { "version": "2.20.3", "resolved": "https://registry.npmjs.org/commander/-/commander-2.20.3.tgz", @@ -1227,6 +1299,26 @@ "node": ">=0.10.0" } }, + "node_modules/emoji-regex": { + "version": "10.6.0", + "resolved": "https://registry.npmjs.org/emoji-regex/-/emoji-regex-10.6.0.tgz", + "integrity": "sha512-toUI84YS5YmxW219erniWD0CIVOo46xGKColeNQRgOzDorgBi1v4D71/OFzgD9GO2UGKIv1C3Sp8DAn0+j5w7A==", + "dev": true, + "license": "MIT" + }, + "node_modules/environment": { + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/environment/-/environment-1.1.0.tgz", + "integrity": "sha512-xUtoPkMggbz0MPyPiIWr1Kp4aeWJjDZ6SMvURhimjdZgsRuDplF5/s9hcgGhyXMhs+6vpnuoiZ2kFiu3FMnS8Q==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=18" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, "node_modules/escape-string-regexp": { "version": "4.0.0", "resolved": "https://registry.npmjs.org/escape-string-regexp/-/escape-string-regexp-4.0.0.tgz", @@ -1478,6 +1570,13 @@ "node": ">=0.10.0" } }, + "node_modules/eventemitter3": { + "version": "5.0.1", + "resolved": "https://registry.npmjs.org/eventemitter3/-/eventemitter3-5.0.1.tgz", + "integrity": "sha512-GWkBvjiSZK87ELrYOSESUYeVIc9mvLLf/nXalMOS5dYrgZq9o5OVkbZAVM06CVxYsCwH9BDZFPlQTlPA1j4ahA==", + "dev": true, + "license": "MIT" + }, "node_modules/execa": { "version": "5.1.1", "resolved": "https://registry.npmjs.org/execa/-/execa-5.1.1.tgz", @@ -1670,6 +1769,19 @@ "url": "https://github.com/sponsors/ljharb" } }, + "node_modules/get-east-asian-width": { + "version": "1.4.0", + "resolved": "https://registry.npmjs.org/get-east-asian-width/-/get-east-asian-width-1.4.0.tgz", + "integrity": "sha512-QZjmEOC+IT1uk6Rx0sX22V6uHWVwbdbxf1faPqJ1QhLdGgsRGCZoyaQBm/piRdJy/D2um6hM1UP7ZEeQ4EkP+Q==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=18" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, "node_modules/get-stream": { "version": "6.0.1", "resolved": "https://registry.npmjs.org/get-stream/-/get-stream-6.0.1.tgz", @@ -1749,6 +1861,22 @@ "node": ">=10.17.0" } }, + "node_modules/husky": { + "version": "9.1.7", + "resolved": "https://registry.npmjs.org/husky/-/husky-9.1.7.tgz", + "integrity": "sha512-5gs5ytaNjBrh5Ow3zrvdUUY+0VxIuWVL4i9irt6friV+BqdCfmV11CQTWMiBYWHbXhco+J1kHfTOUkePhCDvMA==", + "dev": true, + "license": "MIT", + "bin": { + "husky": "bin.js" + }, + "engines": { + "node": ">=18" + }, + "funding": { + "url": "https://github.com/sponsors/typicode" + } + }, "node_modules/ignore": { "version": "7.0.5", "resolved": "https://registry.npmjs.org/ignore/-/ignore-7.0.5.tgz", @@ -1812,6 +1940,22 @@ "node": ">=0.10.0" } }, + "node_modules/is-fullwidth-code-point": { + "version": "5.1.0", + "resolved": "https://registry.npmjs.org/is-fullwidth-code-point/-/is-fullwidth-code-point-5.1.0.tgz", + "integrity": "sha512-5XHYaSyiqADb4RnZ1Bdad6cPp8Toise4TzEjcOYDHZkTCbKgiUl7WTUCpNWHuxmDt91wnsZBc9xinNzopv3JMQ==", + "dev": true, + "license": "MIT", + "dependencies": { + "get-east-asian-width": "^1.3.1" + }, + "engines": { + "node": ">=18" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, "node_modules/is-glob": { "version": "4.0.3", "resolved": "https://registry.npmjs.org/is-glob/-/is-glob-4.0.3.tgz", @@ -1920,6 +2064,59 @@ "node": ">= 0.8.0" } }, + "node_modules/lint-staged": { + "version": "16.2.7", + "resolved": "https://registry.npmjs.org/lint-staged/-/lint-staged-16.2.7.tgz", + "integrity": "sha512-lDIj4RnYmK7/kXMya+qJsmkRFkGolciXjrsZ6PC25GdTfWOAWetR0ZbsNXRAj1EHHImRSalc+whZFg56F5DVow==", + "dev": true, + "license": "MIT", + "dependencies": { + "commander": "^14.0.2", + "listr2": "^9.0.5", + "micromatch": "^4.0.8", + "nano-spawn": "^2.0.0", + "pidtree": "^0.6.0", + "string-argv": "^0.3.2", + "yaml": "^2.8.1" + }, + "bin": { + "lint-staged": "bin/lint-staged.js" + }, + "engines": { + "node": ">=20.17" + }, + "funding": { + "url": "https://opencollective.com/lint-staged" + } + }, + "node_modules/lint-staged/node_modules/commander": { + "version": "14.0.2", + "resolved": "https://registry.npmjs.org/commander/-/commander-14.0.2.tgz", + "integrity": "sha512-TywoWNNRbhoD0BXs1P3ZEScW8W5iKrnbithIl0YH+uCmBd0QpPOA8yc82DS3BIE5Ma6FnBVUsJ7wVUDz4dvOWQ==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=20" + } + }, + "node_modules/listr2": { + "version": "9.0.5", + "resolved": "https://registry.npmjs.org/listr2/-/listr2-9.0.5.tgz", + "integrity": "sha512-ME4Fb83LgEgwNw96RKNvKV4VTLuXfoKudAmm2lP8Kk87KaMK0/Xrx/aAkMWmT8mDb+3MlFDspfbCs7adjRxA2g==", + "dev": true, + "license": "MIT", + "dependencies": { + "cli-truncate": "^5.0.0", + "colorette": "^2.0.20", + "eventemitter3": "^5.0.1", + "log-update": "^6.1.0", + "rfdc": "^1.4.1", + "wrap-ansi": "^9.0.0" + }, + "engines": { + "node": ">=20.0.0" + } + }, "node_modules/locate-path": { "version": "6.0.0", "resolved": "https://registry.npmjs.org/locate-path/-/locate-path-6.0.0.tgz", @@ -1950,6 +2147,26 @@ "dev": true, "license": "MIT" }, + "node_modules/log-update": { + "version": "6.1.0", + "resolved": "https://registry.npmjs.org/log-update/-/log-update-6.1.0.tgz", + "integrity": "sha512-9ie8ItPR6tjY5uYJh8K/Zrv/RMZ5VOlOWvtZdEHYSTFKZfIBPQa9tOAEeAWhd+AnIneLJ22w5fjOYtoutpWq5w==", + "dev": true, + "license": "MIT", + "dependencies": { + "ansi-escapes": "^7.0.0", + "cli-cursor": "^5.0.0", + "slice-ansi": "^7.1.0", + "strip-ansi": "^7.1.0", + "wrap-ansi": "^9.0.0" + }, + "engines": { + "node": ">=18" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, "node_modules/magic-string": { "version": "0.30.21", "resolved": "https://registry.npmjs.org/magic-string/-/magic-string-0.30.21.tgz", @@ -2014,6 +2231,19 @@ "node": ">=6" } }, + "node_modules/mimic-function": { + "version": "5.0.1", + "resolved": "https://registry.npmjs.org/mimic-function/-/mimic-function-5.0.1.tgz", + "integrity": "sha512-VP79XUPxV2CigYP3jWwAUFSku2aKqBH7uTAapFWCBqutsbmDo96KY5o8uh6U+/YSIn5OxJnXp73beVkpqMIGhA==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=18" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, "node_modules/minimatch": { "version": "9.0.5", "resolved": "https://registry.npmjs.org/minimatch/-/minimatch-9.0.5.tgz", @@ -2047,6 +2277,19 @@ "dev": true, "license": "MIT" }, + "node_modules/nano-spawn": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/nano-spawn/-/nano-spawn-2.0.0.tgz", + "integrity": "sha512-tacvGzUY5o2D8CBh2rrwxyNojUsZNU2zjNTzKQrkgGJQTbGAfArVWXSKMBokBeeg6C7OLRGUEyoFlYbfeWQIqw==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=20.17" + }, + "funding": { + "url": "https://github.com/sindresorhus/nano-spawn?sponsor=1" + } + }, "node_modules/natural-compare": { "version": "1.4.0", "resolved": "https://registry.npmjs.org/natural-compare/-/natural-compare-1.4.0.tgz", @@ -2199,6 +2442,19 @@ "url": "https://github.com/sponsors/jonschlinkert" } }, + "node_modules/pidtree": { + "version": "0.6.0", + "resolved": "https://registry.npmjs.org/pidtree/-/pidtree-0.6.0.tgz", + "integrity": "sha512-eG2dWTVw5bzqGRztnHExczNxt5VGsE6OwTeCG3fdUf9KBsZzO3R5OIIIzWR+iZA0NtZ+RDVdaoE2dK1cn6jH4g==", + "dev": true, + "license": "MIT", + "bin": { + "pidtree": "bin/pidtree.js" + }, + "engines": { + "node": ">=0.10" + } + }, "node_modules/prelude-ls": { "version": "1.2.1", "resolved": "https://registry.npmjs.org/prelude-ls/-/prelude-ls-1.2.1.tgz", @@ -2209,6 +2465,22 @@ "node": ">= 0.8.0" } }, + "node_modules/prettier": { + "version": "3.6.2", + "resolved": "https://registry.npmjs.org/prettier/-/prettier-3.6.2.tgz", + "integrity": "sha512-I7AIg5boAr5R0FFtJ6rCfD+LFsWHp81dolrFD8S79U9tb8Az2nGrJncnMSnys+bpQJfRUzqs9hnA81OAA3hCuQ==", + "dev": true, + "license": "MIT", + "bin": { + "prettier": "bin/prettier.cjs" + }, + "engines": { + "node": ">=14" + }, + "funding": { + "url": "https://github.com/prettier/prettier?sponsor=1" + } + }, "node_modules/punycode": { "version": "2.3.1", "resolved": "https://registry.npmjs.org/punycode/-/punycode-2.3.1.tgz", @@ -2281,6 +2553,52 @@ "node": ">=4" } }, + "node_modules/restore-cursor": { + "version": "5.1.0", + "resolved": "https://registry.npmjs.org/restore-cursor/-/restore-cursor-5.1.0.tgz", + "integrity": "sha512-oMA2dcrw6u0YfxJQXm342bFKX/E4sG9rbTzO9ptUcR/e8A33cHuvStiYOwH7fszkZlZ1z/ta9AAoPk2F4qIOHA==", + "dev": true, + "license": "MIT", + "dependencies": { + "onetime": "^7.0.0", + "signal-exit": "^4.1.0" + }, + "engines": { + "node": ">=18" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/restore-cursor/node_modules/onetime": { + "version": "7.0.0", + "resolved": "https://registry.npmjs.org/onetime/-/onetime-7.0.0.tgz", + "integrity": "sha512-VXJjc87FScF88uafS3JllDgvAm+c/Slfz06lorj2uAY34rlUu0Nt+v8wreiImcrgAjjIHp1rXpTDlLOGw29WwQ==", + "dev": true, + "license": "MIT", + "dependencies": { + "mimic-function": "^5.0.0" + }, + "engines": { + "node": ">=18" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/restore-cursor/node_modules/signal-exit": { + "version": "4.1.0", + "resolved": "https://registry.npmjs.org/signal-exit/-/signal-exit-4.1.0.tgz", + "integrity": "sha512-bzyZ1e88w9O1iNJbKnOlvYTrWPDl46O1bG0D3XInv+9tkPrxrN8jUUTiFlDkkmKWgn1M6CfIA13SuGqOa9Korw==", + "dev": true, + "license": "ISC", + "engines": { + "node": ">=14" + }, + "funding": { + "url": "https://github.com/sponsors/isaacs" + } + }, "node_modules/reusify": { "version": "1.1.0", "resolved": "https://registry.npmjs.org/reusify/-/reusify-1.1.0.tgz", @@ -2292,6 +2610,13 @@ "node": ">=0.10.0" } }, + "node_modules/rfdc": { + "version": "1.4.1", + "resolved": "https://registry.npmjs.org/rfdc/-/rfdc-1.4.1.tgz", + "integrity": "sha512-q1b3N5QkRUWUl7iyylaaj3kOpIT0N2i9MqIEQXP73GVsN9cw3fdx8X63cEmWhJGi2PPCF23Ijp7ktmd39rawIA==", + "dev": true, + "license": "MIT" + }, "node_modules/rollup": { "version": "4.53.2", "resolved": "https://registry.npmjs.org/rollup/-/rollup-4.53.2.tgz", @@ -2514,6 +2839,36 @@ "dev": true, "license": "ISC" }, + "node_modules/slice-ansi": { + "version": "7.1.2", + "resolved": "https://registry.npmjs.org/slice-ansi/-/slice-ansi-7.1.2.tgz", + "integrity": "sha512-iOBWFgUX7caIZiuutICxVgX1SdxwAVFFKwt1EvMYYec/NWO5meOJ6K5uQxhrYBdQJne4KxiqZc+KptFOWFSI9w==", + "dev": true, + "license": "MIT", + "dependencies": { + "ansi-styles": "^6.2.1", + "is-fullwidth-code-point": "^5.0.0" + }, + "engines": { + "node": ">=18" + }, + "funding": { + "url": "https://github.com/chalk/slice-ansi?sponsor=1" + } + }, + "node_modules/slice-ansi/node_modules/ansi-styles": { + "version": "6.2.3", + "resolved": "https://registry.npmjs.org/ansi-styles/-/ansi-styles-6.2.3.tgz", + "integrity": "sha512-4Dj6M28JB+oAH8kFkTLUo+a2jwOFkuqb3yucU0CANcRRUbxS0cP0nZYCGjcc3BNXwRIsUVmDGgzawme7zvJHvg==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=12" + }, + "funding": { + "url": "https://github.com/chalk/ansi-styles?sponsor=1" + } + }, "node_modules/smob": { "version": "1.5.0", "resolved": "https://registry.npmjs.org/smob/-/smob-1.5.0.tgz", @@ -2608,6 +2963,49 @@ "spdx-ranges": "^2.0.0" } }, + "node_modules/string-argv": { + "version": "0.3.2", + "resolved": "https://registry.npmjs.org/string-argv/-/string-argv-0.3.2.tgz", + "integrity": "sha512-aqD2Q0144Z+/RqG52NeHEkZauTAUWJO8c6yTftGJKO3Tja5tUgIfmIl6kExvhtxSDP7fXB6DvzkfMpCd/F3G+Q==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=0.6.19" + } + }, + "node_modules/string-width": { + "version": "8.1.0", + "resolved": "https://registry.npmjs.org/string-width/-/string-width-8.1.0.tgz", + "integrity": "sha512-Kxl3KJGb/gxkaUMOjRsQ8IrXiGW75O4E3RPjFIINOVH8AMl2SQ/yWdTzWwF3FevIX9LcMAjJW+GRwAlAbTSXdg==", + "dev": true, + "license": "MIT", + "dependencies": { + "get-east-asian-width": "^1.3.0", + "strip-ansi": "^7.1.0" + }, + "engines": { + "node": ">=20" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/strip-ansi": { + "version": "7.1.2", + "resolved": "https://registry.npmjs.org/strip-ansi/-/strip-ansi-7.1.2.tgz", + "integrity": "sha512-gmBGslpoQJtgnMAvOVqGZpEz9dyoKTCzy2nfz/n8aIFhN/jCE/rCmcxabB6jOOHV+0WNnylOxaxBQPSvcWklhA==", + "dev": true, + "license": "MIT", + "dependencies": { + "ansi-regex": "^6.0.1" + }, + "engines": { + "node": ">=12" + }, + "funding": { + "url": "https://github.com/chalk/strip-ansi?sponsor=1" + } + }, "node_modules/strip-final-newline": { "version": "2.0.0", "resolved": "https://registry.npmjs.org/strip-final-newline/-/strip-final-newline-2.0.0.tgz", @@ -2771,6 +3169,68 @@ "node": ">=0.10.0" } }, + "node_modules/wrap-ansi": { + "version": "9.0.2", + "resolved": "https://registry.npmjs.org/wrap-ansi/-/wrap-ansi-9.0.2.tgz", + "integrity": "sha512-42AtmgqjV+X1VpdOfyTGOYRi0/zsoLqtXQckTmqTeybT+BDIbM/Guxo7x3pE2vtpr1ok6xRqM9OpBe+Jyoqyww==", + "dev": true, + "license": "MIT", + "dependencies": { + "ansi-styles": "^6.2.1", + "string-width": "^7.0.0", + "strip-ansi": "^7.1.0" + }, + "engines": { + "node": ">=18" + }, + "funding": { + "url": "https://github.com/chalk/wrap-ansi?sponsor=1" + } + }, + "node_modules/wrap-ansi/node_modules/ansi-styles": { + "version": "6.2.3", + "resolved": "https://registry.npmjs.org/ansi-styles/-/ansi-styles-6.2.3.tgz", + "integrity": "sha512-4Dj6M28JB+oAH8kFkTLUo+a2jwOFkuqb3yucU0CANcRRUbxS0cP0nZYCGjcc3BNXwRIsUVmDGgzawme7zvJHvg==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=12" + }, + "funding": { + "url": "https://github.com/chalk/ansi-styles?sponsor=1" + } + }, + "node_modules/wrap-ansi/node_modules/string-width": { + "version": "7.2.0", + "resolved": "https://registry.npmjs.org/string-width/-/string-width-7.2.0.tgz", + "integrity": "sha512-tsaTIkKW9b4N+AEj+SVA+WhJzV7/zMhcSu78mLKWSk7cXMOSHsBKFWUs0fWwq8QyK3MgJBQRX6Gbi4kYbdvGkQ==", + "dev": true, + "license": "MIT", + "dependencies": { + "emoji-regex": "^10.3.0", + "get-east-asian-width": "^1.0.0", + "strip-ansi": "^7.1.0" + }, + "engines": { + "node": ">=18" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/yaml": { + "version": "2.8.1", + "resolved": "https://registry.npmjs.org/yaml/-/yaml-2.8.1.tgz", + "integrity": "sha512-lcYcMxX2PO9XMGvAJkJ3OsNMw+/7FKes7/hgerGUYWIoWu5j/+YQqcZr5JnPZWzOsEBgMbSbiSTn/dv/69Mkpw==", + "dev": true, + "license": "ISC", + "bin": { + "yaml": "bin.mjs" + }, + "engines": { + "node": ">= 14.6" + } + }, "node_modules/yocto-queue": { "version": "0.1.0", "resolved": "https://registry.npmjs.org/yocto-queue/-/yocto-queue-0.1.0.tgz", diff --git a/package.json b/package.json index 28edc709..c73ea911 100644 --- a/package.json +++ b/package.json @@ -46,6 +46,9 @@ "chalk": "^5.0.0", "eslint": "^9.32.0", "globals": "^16.3.0", + "husky": "^9.1.7", + "lint-staged": "^16.2.7", + "prettier": "^3.6.2", "rollup": "^4.45.1", "rollup-plugin-license": "^3.6.0", "rollup-plugin-sourcemaps2": "^0.5.3", @@ -63,4 +66,4 @@ "javascript", "typescript" ] -} \ No newline at end of file +} From 96b4ffa9f59fa34c0e4b6ceededdab086d905419 Mon Sep 17 00:00:00 2001 From: Denis Aleksanov Date: Sat, 22 Nov 2025 23:06:33 +0100 Subject: [PATCH 39/51] prettier husky lint-staged: configs are formatted --- build.js | 2 +- demo/window-test.html | 184 ++++++++++++++++++++++++++++++++++++++++++ eslint.config.js | 13 +-- jest.config.js | 1 - tests/jest.config.js | 1 - tests/package.json | 2 +- 6 files changed, 190 insertions(+), 13 deletions(-) create mode 100644 demo/window-test.html diff --git a/build.js b/build.js index 01dc8a8c..9c3c623a 100644 --- a/build.js +++ b/build.js @@ -10,7 +10,7 @@ const CONF_DIR = 'build'; const configJsonString = readFileSync('./package.json', { encoding: 'utf8' }); const config = JSON.parse(configJsonString); -// Package version +// Package version shell.echo('Setup package version'); const versionContent = `export default { name: '${config.name}', diff --git a/demo/window-test.html b/demo/window-test.html new file mode 100644 index 00000000..69c8ca3c --- /dev/null +++ b/demo/window-test.html @@ -0,0 +1,184 @@ + + + + + vscroll window viewport test + + + + + + +
+
Window Viewport Test
+
html.clientHeight: -
+
html.scrollHeight: -
+
window.innerHeight: -
+
Buffer size: -
+
Fetches: 0
+
+
+
+
+
+ + + + \ No newline at end of file diff --git a/eslint.config.js b/eslint.config.js index d814545d..bf8dda32 100644 --- a/eslint.config.js +++ b/eslint.config.js @@ -6,12 +6,7 @@ import globals from 'globals'; export default [ // Global ignores (replaces .eslintignore) { - ignores: [ - 'node_modules/**', - 'dist/**', - 'build/**', - 'demo/**' - ] + ignores: ['node_modules/**', 'dist/**', 'build/**', 'demo/**'] }, // Base configuration for TypeScript files @@ -37,8 +32,8 @@ export default [ // Custom rules from original config 'max-len': ['error', { code: 120 }], - 'quotes': ['error', 'single'], - 'semi': ['error', 'always'], + quotes: ['error', 'single'], + semi: ['error', 'always'], '@typescript-eslint/no-empty-function': 'off', '@typescript-eslint/no-explicit-any': ['error', { ignoreRestArgs: true }], @@ -76,4 +71,4 @@ export default [ '@typescript-eslint/no-var-requires': 'off' } } -]; \ No newline at end of file +]; diff --git a/jest.config.js b/jest.config.js index bba98f53..80a43a54 100644 --- a/jest.config.js +++ b/jest.config.js @@ -7,4 +7,3 @@ export default { collectCoverageFrom: ['src/**/*.ts'], coveragePathIgnorePatterns: ['/node_modules/', '/tests/'] }; - diff --git a/tests/jest.config.js b/tests/jest.config.js index 9b3ac750..6bc5f98b 100644 --- a/tests/jest.config.js +++ b/tests/jest.config.js @@ -7,4 +7,3 @@ export default { collectCoverageFrom: ['../src/**/*.ts'], coveragePathIgnorePatterns: ['/node_modules/'] }; - diff --git a/tests/package.json b/tests/package.json index 28ea38ce..3878e27d 100644 --- a/tests/package.json +++ b/tests/package.json @@ -22,4 +22,4 @@ "jest": "^30.0.5", "typescript": "^5.8.3" } -} \ No newline at end of file +} From f891c44dd2c8b4cad474c42d0af1a34030db5bce Mon Sep 17 00:00:00 2001 From: Denis Aleksanov Date: Sat, 22 Nov 2025 23:53:31 +0100 Subject: [PATCH 40/51] prettier husky lint-staged: e2e tests are formatted --- tests/e2e/fixture/Adapter.ts | 129 +++++++++----- tests/e2e/fixture/Scroller.ts | 17 +- tests/e2e/fixture/VScrollFixture.ts | 136 +++++++++------ tests/e2e/fixture/after-each-logs.ts | 52 +++--- tests/e2e/fixture/create-fixture.ts | 15 +- tests/e2e/helpers/itemsCounter.ts | 1 - tests/e2e/playwright.config.ts | 20 +-- tests/e2e/specs/min-max-indexes.spec.ts | 90 ++++++---- tests/e2e/specs/recreation.spec.ts | 93 +++++----- tests/e2e/specs/scroll-basic.spec.ts | 215 +++++++++++++++--------- tests/e2e/specs/scroll-delay.spec.ts | 92 +++++----- tests/e2e/specs/scroll-fast.spec.ts | 97 ++++++----- tests/e2e/specs/viewport.spec.ts | 16 +- tests/e2e/types/global.d.ts | 3 +- tests/e2e/types/index.ts | 8 +- 15 files changed, 579 insertions(+), 405 deletions(-) diff --git a/tests/e2e/fixture/Adapter.ts b/tests/e2e/fixture/Adapter.ts index bf5f0369..01c7ab6f 100644 --- a/tests/e2e/fixture/Adapter.ts +++ b/tests/e2e/fixture/Adapter.ts @@ -4,7 +4,7 @@ import { Page } from '@playwright/test'; * Adapter accessor class - provides access to adapter methods in browser context */ export class Adapter { - constructor(private page: Page) { } + constructor(private page: Page) {} /** * Wait for the scroller to relax (no pending operations) @@ -13,7 +13,9 @@ export class Adapter { return await this.page.evaluate(() => { const ds = window.__vscroll__.datasource; if (!ds.adapter) { - throw new Error('Adapter not available. Set useAdapter: true in config.'); + throw new Error( + 'Adapter not available. Set useAdapter: true in config.' + ); } return ds.adapter.relax(); }); @@ -23,10 +25,12 @@ export class Adapter { * Append items to the end of the dataset */ async append(options) { - return await this.page.evaluate((opts) => { + return await this.page.evaluate(opts => { const ds = window.__vscroll__.datasource; if (!ds.adapter) { - throw new Error('Adapter not available. Set useAdapter: true in config.'); + throw new Error( + 'Adapter not available. Set useAdapter: true in config.' + ); } return ds.adapter.append(opts); }, options); @@ -36,10 +40,12 @@ export class Adapter { * Prepend items to the beginning of the dataset */ async prepend(options) { - return await this.page.evaluate((opts) => { + return await this.page.evaluate(opts => { const ds = window.__vscroll__.datasource; if (!ds.adapter) { - throw new Error('Adapter not available. Set useAdapter: true in config.'); + throw new Error( + 'Adapter not available. Set useAdapter: true in config.' + ); } return ds.adapter.prepend(opts); }, options); @@ -49,10 +55,12 @@ export class Adapter { * Reload the dataset */ async reload(reloadIndex) { - return await this.page.evaluate((index) => { + return await this.page.evaluate(index => { const ds = window.__vscroll__.datasource; if (!ds.adapter) { - throw new Error('Adapter not available. Set useAdapter: true in config.'); + throw new Error( + 'Adapter not available. Set useAdapter: true in config.' + ); } return ds.adapter.reload(index); }, reloadIndex); @@ -62,10 +70,12 @@ export class Adapter { * Reset the scroller to initial state */ async reset(options) { - return await this.page.evaluate((opts) => { + return await this.page.evaluate(opts => { const ds = window.__vscroll__.datasource; if (!ds.adapter) { - throw new Error('Adapter not available. Set useAdapter: true in config.'); + throw new Error( + 'Adapter not available. Set useAdapter: true in config.' + ); } return ds.adapter.reset(opts); }, options); @@ -76,24 +86,31 @@ export class Adapter { */ async remove(options) { const predicateStr = options.predicate.toString(); - return await this.page.evaluate(({ predicateStr, increase }) => { - const ds = window.__vscroll__.datasource; - if (!ds.adapter) { - throw new Error('Adapter not available. Set useAdapter: true in config.'); - } - const predicate = eval(`(${predicateStr})`); - return ds.adapter.remove({ predicate, increase }); - }, { predicateStr, increase: options.increase }); + return await this.page.evaluate( + ({ predicateStr, increase }) => { + const ds = window.__vscroll__.datasource; + if (!ds.adapter) { + throw new Error( + 'Adapter not available. Set useAdapter: true in config.' + ); + } + const predicate = eval(`(${predicateStr})`); + return ds.adapter.remove({ predicate, increase }); + }, + { predicateStr, increase: options.increase } + ); } /** * Insert items at a specific position */ async insert(options) { - return await this.page.evaluate((opts) => { + return await this.page.evaluate(opts => { const ds = window.__vscroll__.datasource; if (!ds.adapter) { - throw new Error('Adapter not available. Set useAdapter: true in config.'); + throw new Error( + 'Adapter not available. Set useAdapter: true in config.' + ); } return ds.adapter.insert(opts); }, options); @@ -104,14 +121,19 @@ export class Adapter { */ async replace(options) { const predicateStr = options.predicate.toString(); - return await this.page.evaluate(({ predicateStr, items }) => { - const ds = window.__vscroll__.datasource; - if (!ds.adapter) { - throw new Error('Adapter not available. Set useAdapter: true in config.'); - } - const predicate = eval(`(${predicateStr})`); - return ds.adapter.replace({ predicate, items }); - }, { predicateStr, items: options.items }); + return await this.page.evaluate( + ({ predicateStr, items }) => { + const ds = window.__vscroll__.datasource; + if (!ds.adapter) { + throw new Error( + 'Adapter not available. Set useAdapter: true in config.' + ); + } + const predicate = eval(`(${predicateStr})`); + return ds.adapter.replace({ predicate, items }); + }, + { predicateStr, items: options.items } + ); } /** @@ -119,10 +141,12 @@ export class Adapter { */ async update(options) { const predicateStr = options.predicate.toString(); - return await this.page.evaluate((predicateStr) => { + return await this.page.evaluate(predicateStr => { const ds = window.__vscroll__.datasource; if (!ds.adapter) { - throw new Error('Adapter not available. Set useAdapter: true in config.'); + throw new Error( + 'Adapter not available. Set useAdapter: true in config.' + ); } const predicate = eval(`(${predicateStr})`); return ds.adapter.update({ predicate }); @@ -133,10 +157,12 @@ export class Adapter { * Clip the dataset (remove items outside visible range) */ async clip(options) { - return await this.page.evaluate((opts) => { + return await this.page.evaluate(opts => { const ds = window.__vscroll__.datasource; if (!ds.adapter) { - throw new Error('Adapter not available. Set useAdapter: true in config.'); + throw new Error( + 'Adapter not available. Set useAdapter: true in config.' + ); } return ds.adapter.clip(opts); }, options); @@ -149,7 +175,9 @@ export class Adapter { return await this.page.evaluate(() => { const ds = window.__vscroll__.datasource; if (!ds.adapter) { - throw new Error('Adapter not available. Set useAdapter: true in config.'); + throw new Error( + 'Adapter not available. Set useAdapter: true in config.' + ); } return ds.adapter.check(); }); @@ -159,10 +187,12 @@ export class Adapter { * Fix scroll position */ async fix(options) { - return await this.page.evaluate((opts) => { + return await this.page.evaluate(opts => { const ds = window.__vscroll__.datasource; if (!ds.adapter) { - throw new Error('Adapter not available. Set useAdapter: true in config.'); + throw new Error( + 'Adapter not available. Set useAdapter: true in config.' + ); } return ds.adapter.fix(opts); }, options); @@ -175,7 +205,9 @@ export class Adapter { return this.page.evaluate(() => { const ds = window.__vscroll__.datasource; if (!ds.adapter) { - throw new Error('Adapter not available. Set useAdapter: true in config.'); + throw new Error( + 'Adapter not available. Set useAdapter: true in config.' + ); } return ds.adapter.isLoading; }); @@ -185,7 +217,9 @@ export class Adapter { return this.page.evaluate(() => { const ds = window.__vscroll__.datasource; if (!ds.adapter) { - throw new Error('Adapter not available. Set useAdapter: true in config.'); + throw new Error( + 'Adapter not available. Set useAdapter: true in config.' + ); } return ds.adapter.bof; }); @@ -195,7 +229,9 @@ export class Adapter { return this.page.evaluate(() => { const ds = window.__vscroll__.datasource; if (!ds.adapter) { - throw new Error('Adapter not available. Set useAdapter: true in config.'); + throw new Error( + 'Adapter not available. Set useAdapter: true in config.' + ); } return ds.adapter.eof; }); @@ -205,7 +241,9 @@ export class Adapter { return this.page.evaluate(() => { const ds = window.__vscroll__.datasource; if (!ds.adapter) { - throw new Error('Adapter not available. Set useAdapter: true in config.'); + throw new Error( + 'Adapter not available. Set useAdapter: true in config.' + ); } return ds.adapter.itemsCount; }); @@ -215,7 +253,9 @@ export class Adapter { return this.page.evaluate(() => { const ds = window.__vscroll__.datasource; if (!ds.adapter) { - throw new Error('Adapter not available. Set useAdapter: true in config.'); + throw new Error( + 'Adapter not available. Set useAdapter: true in config.' + ); } return ds.adapter.firstVisible; }); @@ -225,7 +265,9 @@ export class Adapter { return this.page.evaluate(() => { const ds = window.__vscroll__.datasource; if (!ds.adapter) { - throw new Error('Adapter not available. Set useAdapter: true in config.'); + throw new Error( + 'Adapter not available. Set useAdapter: true in config.' + ); } return ds.adapter.lastVisible; }); @@ -235,10 +277,11 @@ export class Adapter { return this.page.evaluate(() => { const ds = window.__vscroll__.datasource; if (!ds.adapter) { - throw new Error('Adapter not available. Set useAdapter: true in config.'); + throw new Error( + 'Adapter not available. Set useAdapter: true in config.' + ); } return ds.adapter.bufferInfo; }); } } - diff --git a/tests/e2e/fixture/Scroller.ts b/tests/e2e/fixture/Scroller.ts index e52805cb..7fd61cdf 100644 --- a/tests/e2e/fixture/Scroller.ts +++ b/tests/e2e/fixture/Scroller.ts @@ -10,13 +10,13 @@ export const Direction = { backward: 'backward' } as const; -export type DirectionType = typeof Direction[keyof typeof Direction]; +export type DirectionType = (typeof Direction)[keyof typeof Direction]; /** * Scroller accessor class - provides access to scroller properties in browser context */ export class Scroller { - constructor(private page: Page) { } + constructor(private page: Page) {} get settings() { const page = this.page; @@ -58,9 +58,11 @@ export class Scroller { }); }, getEdgeVisibleItem: async (direction: DirectionType) => { - return await page.evaluate((dir) => { + return await page.evaluate(dir => { const workflow = window.__vscroll__.workflow; - const item = workflow.scroller.buffer.getEdgeVisibleItem(dir as DirectionEnum); + const item = workflow.scroller.buffer.getEdgeVisibleItem( + dir as DirectionEnum + ); return item ? { $index: item.$index, data: item.data } : null; }, direction); } @@ -89,14 +91,16 @@ export class Scroller { [Direction.forward]: { get size(): Promise { return page.evaluate(() => { - return window.__vscroll__.workflow.scroller.viewport.paddings.forward.size; + return window.__vscroll__.workflow.scroller.viewport.paddings + .forward.size; }); } }, [Direction.backward]: { get size(): Promise { return page.evaluate(() => { - return window.__vscroll__.workflow.scroller.viewport.paddings.backward.size; + return window.__vscroll__.workflow.scroller.viewport.paddings + .backward.size; }); } } @@ -104,4 +108,3 @@ export class Scroller { }; } } - diff --git a/tests/e2e/fixture/VScrollFixture.ts b/tests/e2e/fixture/VScrollFixture.ts index 70a16557..5fb97509 100644 --- a/tests/e2e/fixture/VScrollFixture.ts +++ b/tests/e2e/fixture/VScrollFixture.ts @@ -18,7 +18,10 @@ export class VScrollFixture { this.adapter = new Adapter(page); } - static async create(page: Page, config: VScrollFixtureConfig): Promise { + static async create( + page: Page, + config: VScrollFixtureConfig + ): Promise { const fixture = new VScrollFixture(page, config); await fixture.mount(); return fixture; @@ -34,7 +37,13 @@ export class VScrollFixture { await page.goto('about:blank'); // 2. Load vscroll UMD bundle - const vscrollPath = path.join(process.cwd(), '..', 'dist', 'bundles', 'vscroll.umd.js'); + const vscrollPath = path.join( + process.cwd(), + '..', + 'dist', + 'bundles', + 'vscroll.umd.js' + ); await page.addScriptTag({ path: vscrollPath }); // 3. Add global styles FIRST (separate evaluate call to ensure styles are processed) @@ -64,9 +73,10 @@ export class VScrollFixture { } // 4. Create viewport and vscroll element (after styles are loaded) - await page.evaluate((settings) => { + await page.evaluate(settings => { // Handle windowViewport mode (use document.body as viewport) - const useWindowViewport = settings.noViewportClass && settings.viewportHeight === 0; + const useWindowViewport = + settings.noViewportClass && settings.viewportHeight === 0; // Create header if specified if (settings.headerHeight) { @@ -89,12 +99,12 @@ export class VScrollFixture { } else if (settings.horizontal) { viewport.style.width = `${settings.viewportWidth || 300}px`; viewport.style.height = '100%'; - viewport.style.overflowX = 'scroll'; // Always scroll, not auto + viewport.style.overflowX = 'scroll'; // Always scroll, not auto viewport.style.overflowY = 'hidden'; } else { viewport.style.height = `${settings.viewportHeight || 200}px`; viewport.style.width = '100%'; - viewport.style.overflowY = 'scroll'; // Always scroll, not auto + viewport.style.overflowY = 'scroll'; // Always scroll, not auto viewport.style.overflowX = 'hidden'; } @@ -160,7 +170,10 @@ export class VScrollFixture { const templateFn = eval(`(${templateFnStr})`); // Renderer - const processItems = (newItems: Item[], oldItems: Item[]) => { + const processItems = ( + newItems: Item[], + oldItems: Item[] + ) => { // Remove elements not present oldItems .filter(item => !newItems.includes(item)) @@ -252,7 +265,8 @@ export class VScrollFixture { }; // Creates Workflow with fresh closure and updates window.__vscroll__.workflow - const makeScroller = () => window.__vscroll__.workflow = workflowFactory()(); + const makeScroller = () => + (window.__vscroll__.workflow = workflowFactory()()); window.__vscroll__.makeScroller = makeScroller; // Create initial workflow (unless manualRun) @@ -284,7 +298,8 @@ export class VScrollFixture { }, get innerLoopCount(): Promise { return page.evaluate(() => { - return window.__vscroll__.workflow.scroller.state.cycle.innerLoop.total; + return window.__vscroll__.workflow.scroller.state.cycle.innerLoop + .total; }); } }; @@ -295,24 +310,30 @@ export class VScrollFixture { * @param position - The position to scroll to * @param options.noRelax - If true, no wait for relaxation */ - async scrollTo(position: number, options: { noRelax?: boolean } = {}): Promise { - return this.page.evaluate(async ({ position, options }) => { - const workflow = window.__vscroll__.workflow; - const viewport = workflow.scroller.viewport; - const adapter = workflow.scroller.datasource.adapter; - const cyclesDone = workflow.cyclesDone; - const positionBefore = viewport.scrollPosition; - await adapter.fix({ scrollPosition: position }); - if (options?.noRelax || viewport.scrollPosition === positionBefore) { - return; - } - if (!adapter.isLoading) { - // The new workflow cycle should run and complete - while (workflow.cyclesDone === cyclesDone) { - await new Promise(resolve => setTimeout(resolve)); + async scrollTo( + position: number, + options: { noRelax?: boolean } = {} + ): Promise { + return this.page.evaluate( + async ({ position, options }) => { + const workflow = window.__vscroll__.workflow; + const viewport = workflow.scroller.viewport; + const adapter = workflow.scroller.datasource.adapter; + const cyclesDone = workflow.cyclesDone; + const positionBefore = viewport.scrollPosition; + await adapter.fix({ scrollPosition: position }); + if (options?.noRelax || viewport.scrollPosition === positionBefore) { + return; } - } - }, { position, options }); + if (!adapter.isLoading) { + // The new workflow cycle should run and complete + while (workflow.cyclesDone === cyclesDone) { + await new Promise(resolve => setTimeout(resolve)); + } + } + }, + { position, options } + ); } /** @@ -332,15 +353,15 @@ export class VScrollFixture { /** * Get all rendered elements */ - async getElements(): Promise { - return await this.page.$$('[data-sid]'); + async getElements(): Promise { + return (await this.page.$$('[data-sid]')) as unknown as HTMLElement[]; } /** * Get element index */ async getElementIndex(element: HTMLElement): Promise { - return await this.page.evaluate((el) => { + return await this.page.evaluate(el => { return parseInt(el.getAttribute('data-sid') || '0', 10); }, element); } @@ -349,10 +370,14 @@ export class VScrollFixture { * Check element content by index */ async checkElementContentByIndex(index: number): Promise { - return await this.page.evaluate((idx) => { + return await this.page.evaluate(idx => { const elements = Array.from(document.querySelectorAll('[data-sid]')); - const element = elements.find(el => el.getAttribute('data-sid') === String(idx)); - return element ? element.textContent?.includes(String(idx)) || false : false; + const element = elements.find( + el => el.getAttribute('data-sid') === String(idx) + ); + return element + ? element.textContent?.includes(String(idx)) || false + : false; }, index); } @@ -361,29 +386,30 @@ export class VScrollFixture { * This ensures the workflow has started processing before we proceed */ waitForLoadingStart(): Promise { - return this.page.evaluate(() => - new Promise((resolve) => { - const workflow = window.__vscroll__.workflow; - const ds = workflow.scroller.datasource; - const initialCycles = workflow.cyclesDone; - - // If already loading, we're good - if (ds?.adapter?.isLoading) { - resolve(); - return; - } - - // Otherwise wait for a new cycle to start OR loading to begin - const check = async () => { - if (workflow.cyclesDone > initialCycles && ds?.adapter) { - await ds.adapter.relax(); + return this.page.evaluate( + () => + new Promise(resolve => { + const workflow = window.__vscroll__.workflow; + const ds = workflow.scroller.datasource; + const initialCycles = workflow.cyclesDone; + + // If already loading, we're good + if (ds?.adapter?.isLoading) { resolve(); - } else { - requestAnimationFrame(check); + return; } - }; - check(); - }) + + // Otherwise wait for a new cycle to start OR loading to begin + const check = async () => { + if (workflow.cyclesDone > initialCycles && ds?.adapter) { + await ds.adapter.relax(); + resolve(); + } else { + requestAnimationFrame(check); + } + }; + check(); + }) ); } @@ -419,10 +445,10 @@ export class VScrollFixture { // Reset vscroll element to initial state const vscroll = document.getElementById('vscroll') as HTMLElement; - vscroll.innerHTML = '
'; + vscroll.innerHTML = + '
'; window.__vscroll__.makeScroller!(); }); } } - diff --git a/tests/e2e/fixture/after-each-logs.ts b/tests/e2e/fixture/after-each-logs.ts index e8527a0b..cd6bed1a 100644 --- a/tests/e2e/fixture/after-each-logs.ts +++ b/tests/e2e/fixture/after-each-logs.ts @@ -2,41 +2,48 @@ import type { Page, TestInfo } from '@playwright/test'; /** * afterEachLogs handler that retrieves and prints vscroll debug logs when tests fail. - * + * * With immediateLog: false, logs are stored in workflow.scroller.logger * and can be retrieved from the browser context. - * + * * Usage: * ```typescript * import { afterEachLogs } from '../fixture/after-each-logs.js'; - * + * * test.afterEach(afterEachLogs); * ``` */ -export async function afterEachLogs({ page }: { page: Page }, testInfo: TestInfo): Promise { +export async function afterEachLogs( + { page }: { page: Page }, + testInfo: TestInfo +): Promise { // Only process if test failed if (testInfo.status !== testInfo.expectedStatus) { // Get logs from vscroll logger - const logs = await page.evaluate(() => { - const vscroll = window.__vscroll__; - if (!vscroll?.workflow?.scroller?.logger) { - return []; - } + const logs = await page + .evaluate(() => { + const vscroll = window.__vscroll__; + if (!vscroll?.workflow?.scroller?.logger) { + return []; + } - const logger = vscroll.workflow.scroller.logger; - // Access private logs array (stored when immediateLog: false) - const logArray = logger.getLogs() || []; + const logger = vscroll.workflow.scroller.logger; + // Access private logs array (stored when immediateLog: false) + const logArray = logger.getLogs() || []; - // Format logs similar to console output - return logArray.map(args => { - return args.map(arg => { - if (typeof arg === 'object' && arg !== null) { - return JSON.stringify(arg); - } - return String(arg); - }).join(' '); - }); - }).catch(() => [] as string[]); + // Format logs similar to console output + return logArray.map(args => { + return args + .map(arg => { + if (typeof arg === 'object' && arg !== null) { + return JSON.stringify(arg); + } + return String(arg); + }) + .join(' '); + }); + }) + .catch(() => [] as string[]); if (logs.length > 0) { const testName = testInfo.titlePath.join(' › '); @@ -53,4 +60,3 @@ export async function afterEachLogs({ page }: { page: Page }, testInfo: TestInfo } } } - diff --git a/tests/e2e/fixture/create-fixture.ts b/tests/e2e/fixture/create-fixture.ts index 71c5646f..8ad824ad 100644 --- a/tests/e2e/fixture/create-fixture.ts +++ b/tests/e2e/fixture/create-fixture.ts @@ -4,9 +4,12 @@ import { Page, IDatasource, ITestConfig } from 'types/index.js'; type FixtureParams = { page: Page; config: ITestConfig; -} +}; -export const createFixture = async ({ page, config }: FixtureParams): Promise => { +export const createFixture = async ({ + page, + config +}: FixtureParams): Promise => { const { templateSettings } = config; const datasource: IDatasource = { @@ -18,8 +21,10 @@ export const createFixture = async ({ page, config }: FixtureParams): Promise - `
${item.$index}: ${item.data.text}
`, + templateFn: (item: { + $index: number; + data: { id: number; text: string }; + }) => `
${item.$index}: ${item.data.text}
`, noAdapter: config.noAdapter, manualRun: config.manualRun }); @@ -75,4 +80,4 @@ export const createFixture = async ({ page, config }: FixtureParams): Promise ({ ...config, datasourceGet, datasourceSettings }) ); -const forwardGapConfigList: ITestConfig[] = startIndexAroundMaxIndexConfigList.map( - ({ - datasourceGet, - datasourceSettings: { minIndex: _, ...datasourceSettings }, - ...config - }) => ({ ...config, datasourceGet, datasourceSettings }) -); +const forwardGapConfigList: ITestConfig[] = + startIndexAroundMaxIndexConfigList.map( + ({ + datasourceGet, + datasourceSettings: { minIndex: _, ...datasourceSettings }, + ...config + }) => ({ ...config, datasourceGet, datasourceSettings }) + ); const checkMinMaxIndexes = async (fixture: VScrollFixture) => { const elements = await fixture.getElements(); const bufferInfo = await fixture.adapter.bufferInfo; const { firstIndex, lastIndex, minIndex, maxIndex } = bufferInfo; + const _minIndex = await fixture.getElementIndex(elements[0]); + const _maxIndex = await fixture.getElementIndex( + elements[elements.length - 1] + ); expect(firstIndex).toEqual(minIndex); expect(lastIndex).toEqual(maxIndex); - expect(minIndex).toEqual(await fixture.getElementIndex(elements[0] as HTMLElement)); - expect(maxIndex).toEqual(await fixture.getElementIndex(elements[elements.length - 1] as HTMLElement)); + expect(minIndex).toEqual(_minIndex); + expect(maxIndex).toEqual(_maxIndex); }; const getParams = ({ @@ -264,7 +269,9 @@ const testCommonCase = async (fixture: VScrollFixture, config: ITestConfig) => { const negativeSize = (startIndex - minIndex) * itemSize; const negativeItemsSize = negativeItemsAmount * itemSize; const bwdPaddingSize = negativeSize - negativeItemsSize; - expect(await fixture.scroller.viewport.paddings[Direction.backward].size).toEqual(bwdPaddingSize); + const _bwdPaddingSize = + await fixture.scroller.viewport.paddings[Direction.backward].size; + expect(_bwdPaddingSize).toEqual(bwdPaddingSize); expect(bufferInfo.absMinIndex).toEqual(minIndex); } else { expect(bufferInfo.absMinIndex).toEqual(-Infinity); @@ -274,7 +281,9 @@ const testCommonCase = async (fixture: VScrollFixture, config: ITestConfig) => { const positiveSize = (maxIndex - startIndex + 1) * itemSize; const positiveItemsSize = positiveItemsAmount * itemSize; const fwdPaddingSize = positiveSize - positiveItemsSize; - expect(await fixture.scroller.viewport.paddings[Direction.forward].size).toEqual(fwdPaddingSize); + const _fwdPaddingSize = + await fixture.scroller.viewport.paddings[Direction.forward].size; + expect(_fwdPaddingSize).toEqual(fwdPaddingSize); expect(bufferInfo.absMaxIndex).toEqual(maxIndex); } else { expect(bufferInfo.absMaxIndex).toEqual(Infinity); @@ -291,15 +300,21 @@ const testCommonCase = async (fixture: VScrollFixture, config: ITestConfig) => { totalSize = knownSize + negativeItemsAmount * itemSize; } - expect(await fixture.scroller.viewport.getScrollableSize()).toEqual(totalSize); - expect(await fixture.workflow.innerLoopCount).toEqual(innerLoopCount); + const _totalSize = await fixture.scroller.viewport.getScrollableSize(); + const _innerLoopCount = await fixture.workflow.innerLoopCount; + expect(_totalSize).toEqual(totalSize); + expect(_innerLoopCount).toEqual(innerLoopCount); await checkMinMaxIndexes(fixture); }; -const testStartIndexEdgeCase = async (fixture: VScrollFixture, config: ITestConfig) => { +const testStartIndexEdgeCase = async ( + fixture: VScrollFixture, + config: ITestConfig +) => { await fixture.adapter.relax(); - const { maxIndex, minIndex, itemSize, startIndex, padding } = getParams(config); + const { maxIndex, minIndex, itemSize, startIndex, padding } = + getParams(config); const bufferInfo = await fixture.adapter.bufferInfo; const viewportSize = await fixture.scroller.viewport.getSize(); const totalSize = (maxIndex - minIndex + 1) * itemSize; @@ -326,9 +341,14 @@ const testStartIndexEdgeCase = async (fixture: VScrollFixture, config: ITestConf const positiveItemsSize = positiveItemsAmount * itemSize; const fwdPaddingSize = Math.max(0, positiveSize - positiveItemsSize); - expect(await fixture.scroller.viewport.getScrollableSize()).toEqual(totalSize); - expect(await fixture.scroller.viewport.paddings[Direction.backward].size).toEqual(bwdPaddingSize); - expect(await fixture.scroller.viewport.paddings[Direction.forward].size).toEqual(fwdPaddingSize); + const _totalSize = await fixture.scroller.viewport.getScrollableSize(); + const _bwdPaddingSize = + await fixture.scroller.viewport.paddings[Direction.backward].size; + const _fwdPaddingSize = + await fixture.scroller.viewport.paddings[Direction.forward].size; + expect(_totalSize).toEqual(totalSize); + expect(_bwdPaddingSize).toEqual(bwdPaddingSize); + expect(_fwdPaddingSize).toEqual(fwdPaddingSize); expect(bufferInfo.absMinIndex).toEqual(minIndex); expect(bufferInfo.absMaxIndex).toEqual(maxIndex); @@ -339,7 +359,8 @@ const testForwardGapCase = async (fixture: VScrollFixture) => { await fixture.adapter.relax(); const gapSize = await fixture.page.evaluate(() => { - const viewportChildren = window.__vscroll__.workflow.scroller.routines.element.children; + const viewportChildren = + window.__vscroll__.workflow.scroller.routines.element.children; const lastChild = viewportChildren[viewportChildren.length - 2]; const lastChildBottom = lastChild.getBoundingClientRect().bottom; const viewport = window.__vscroll__.workflow.scroller.viewport; @@ -350,7 +371,9 @@ const testForwardGapCase = async (fixture: VScrollFixture) => { }); expect(gapSize).toEqual(0); - expect(await fixture.scroller.viewport.paddings[Direction.forward].size).toEqual(0); + expect( + await fixture.scroller.viewport.paddings[Direction.forward].size + ).toEqual(0); }; const makeTest = ( @@ -373,8 +396,7 @@ test.describe('Min/max Indexes Spec', () => { config, testCommonCase ) - ) - ); + )); test.describe('No maxIndex cases', () => noMaxIndexConfigList.forEach((config, index) => @@ -383,8 +405,7 @@ test.describe('Min/max Indexes Spec', () => { config, testCommonCase ) - ) - ); + )); test.describe('No minIndex cases', () => noMinIndexConfigList.forEach((config, index) => @@ -393,8 +414,7 @@ test.describe('Min/max Indexes Spec', () => { config, testCommonCase ) - ) - ); + )); test.describe('No itemSize cases', () => noItemSizeConfigList.forEach((config, index) => @@ -403,36 +423,32 @@ test.describe('Min/max Indexes Spec', () => { config, testCommonCase ) - ) - ); + )); - test.describe('startIndex\'s around minIndex', () => + test.describe('startIndex around minIndex', () => startIndexAroundMinIndexConfigList.forEach((config, index) => makeTest( `should reset backward padding (config ${index})`, config, testStartIndexEdgeCase ) - ) - ); + )); - test.describe('startIndex\'s around maxIndex', () => + test.describe('startIndex around maxIndex', () => startIndexAroundMaxIndexConfigList.forEach((config, index) => makeTest( `should reset forward padding (config ${index})`, config, testStartIndexEdgeCase ) - ) - ); + )); - test.describe('startIndex\'s around maxIndex and no minIndex', () => + test.describe('startIndex around maxIndex and no minIndex', () => forwardGapConfigList.forEach((config, index) => makeTest( `should fill forward padding gap (config ${index})`, config, testForwardGapCase ) - ) - ); + )); }); diff --git a/tests/e2e/specs/recreation.spec.ts b/tests/e2e/specs/recreation.spec.ts index 75f082ca..35f000d4 100644 --- a/tests/e2e/specs/recreation.spec.ts +++ b/tests/e2e/specs/recreation.spec.ts @@ -5,7 +5,11 @@ import { ITestConfig } from 'types/index.js'; test.afterEach(afterEachLogs); -const datasourceGet = (index: number, count: number, success: (data: unknown[]) => void): void => { +const datasourceGet = ( + index: number, + count: number, + success: (data: unknown[]) => void +): void => { const data = []; for (let i = index; i < index + count; i++) { data.push({ id: i, text: `item #${i}` }); @@ -15,7 +19,6 @@ const datasourceGet = (index: number, count: number, success: (data: unknown[]) test.describe('Recreation Spec', () => { test.describe('Destroying (plain DS)', () => { - test('should not reset Datasource on destroy', async ({ page }) => { const config: ITestConfig = { datasourceGet, @@ -29,7 +32,8 @@ test.describe('Recreation Spec', () => { const getWorkflowData = () => page.evaluate(() => ({ workflowInit: window.__vscroll__.workflow.isInitialized, - internalAdapterInit: window.__vscroll__.workflow?.scroller?.adapter?.init, + internalAdapterInit: + window.__vscroll__.workflow?.scroller?.adapter?.init, disposed: window.__vscroll__.workflow.disposed })); @@ -59,7 +63,6 @@ test.describe('Recreation Spec', () => { }); test.describe('Recreation via ngIf (instance DS)', () => { - test('should switch Adapter.init trice', async ({ page }) => { const config: ITestConfig = { datasourceGet, @@ -71,40 +74,44 @@ test.describe('Recreation Spec', () => { const fixture = await createFixture({ page, config }); // Perform dispose and recreate cycle - type Result = { adapterInitCount: number; wfInitOnFirstMake?: boolean; }; - const result = await page.evaluate(() => new Promise((resolve) => { - const { datasource, makeScroller } = window.__vscroll__; - - const result: Result = { adapterInitCount: 0 }; - - // Subscribe to init$ BEFORE creating workflow - const off = datasource.adapter.init$.on(() => { - if (++result.adapterInitCount === 3) { - off(); - resolve(result); - } - }); - - // Create and run initial scroller workflow - makeScroller!(); - - // To make sure the workflow is not yet initialized immediately after creation - result.wfInitOnFirstMake = window.__vscroll__.workflow.isInitialized; - - const checkAndRecreate = () => { - if (!datasource.adapter.isLoading && datasource.adapter.init) { - window.__vscroll__.workflow.dispose(); - - setTimeout(() => { - // Create and run new scroller workflow - makeScroller!(); - }, 25); - } else { - setTimeout(checkAndRecreate, 10); - } - }; - checkAndRecreate(); - })); + type Result = { adapterInitCount: number; wfInitOnFirstMake?: boolean }; + const result = await page.evaluate( + () => + new Promise(resolve => { + const { datasource, makeScroller } = window.__vscroll__; + + const result: Result = { adapterInitCount: 0 }; + + // Subscribe to init$ BEFORE creating workflow + const off = datasource.adapter.init$.on(() => { + if (++result.adapterInitCount === 3) { + off(); + resolve(result); + } + }); + + // Create and run initial scroller workflow + makeScroller!(); + + // To make sure the workflow is not yet initialized immediately after creation + result.wfInitOnFirstMake = + window.__vscroll__.workflow.isInitialized; + + const checkAndRecreate = () => { + if (!datasource.adapter.isLoading && datasource.adapter.init) { + window.__vscroll__.workflow.dispose(); + + setTimeout(() => { + // Create and run new scroller workflow + makeScroller!(); + }, 25); + } else { + setTimeout(checkAndRecreate, 10); + } + }; + checkAndRecreate(); + }) + ); // Workflow should not be initialized immediately after creation expect(result.wfInitOnFirstMake).toBe(false); @@ -128,11 +135,11 @@ test.describe('Recreation Spec', () => { const getState = () => page.evaluate(() => ({ workflowInit: window.__vscroll__.workflow.isInitialized, - scrollerId: window.__vscroll__.workflow.scroller.settings.instanceIndex, + scrollerId: + window.__vscroll__.workflow.scroller.settings.instanceIndex, internalAdapterId: window.__vscroll__.workflow.scroller.adapter.id, adapterId: window.__vscroll__.datasource.adapter.id - }) - ); + })); // Capture initial state const before = await getState(); @@ -166,7 +173,9 @@ test.describe('Recreation Spec', () => { const fixture = await createFixture({ page, config }); await fixture.recreateScroller(); - await page.waitForFunction(() => window.__vscroll__.datasource.adapter.init); + await page.waitForFunction( + () => window.__vscroll__.datasource.adapter.init + ); await fixture.adapter.relax(); const firstVisible = await fixture.adapter.firstVisible; diff --git a/tests/e2e/specs/scroll-basic.spec.ts b/tests/e2e/specs/scroll-basic.spec.ts index 96f88624..35002d5e 100644 --- a/tests/e2e/specs/scroll-basic.spec.ts +++ b/tests/e2e/specs/scroll-basic.spec.ts @@ -1,5 +1,9 @@ import { test, expect } from '@playwright/test'; -import { VScrollFixture, Direction, type DirectionType } from '../fixture/VScrollFixture.js'; +import { + VScrollFixture, + Direction, + type DirectionType +} from '../fixture/VScrollFixture.js'; import { afterEachLogs } from '../fixture/after-each-logs.js'; import { createFixture } from '../fixture/create-fixture.js'; import { ItemsCounter } from '../helpers/itemsCounter.js'; @@ -89,8 +93,12 @@ const getInitialItemsCounter = async ( fixture: VScrollFixture, config: IConfig ): Promise => { - const edgeItem = await fixture.scroller.buffer.getEdgeVisibleItem(Direction.forward); - const oppositeItem = await fixture.scroller.buffer.getEdgeVisibleItem(Direction.backward); + const edgeItem = await fixture.scroller.buffer.getEdgeVisibleItem( + Direction.forward + ); + const oppositeItem = await fixture.scroller.buffer.getEdgeVisibleItem( + Direction.backward + ); const result = new ItemsCounter(); if (!edgeItem || !oppositeItem) { @@ -145,7 +153,12 @@ const getCurrentItemsCounter = async ( const delta = viewportSize * padding; // handle direction (fetch) - const fullHouseDiff = getFullHouseDiff(viewportSize, delta, itemSize, bufferSize); + const fullHouseDiff = getFullHouseDiff( + viewportSize, + delta, + itemSize, + bufferSize + ); const _singleFetchCount = Math.ceil(delta / itemSize); const singleFetchCount = Math.max(bufferSize, _singleFetchCount); const itemsToFetch = @@ -207,7 +220,12 @@ const shouldScroll = async (fixture: VScrollFixture, config: IConfig) => { await doScrollMax(config, fixture); - itemsCounter = await getCurrentItemsCounter(fixture, custom.direction, itemsCounter, config); + itemsCounter = await getCurrentItemsCounter( + fixture, + custom.direction, + itemsCounter, + config + ); } // Final expectations @@ -216,7 +234,8 @@ const shouldScroll = async (fixture: VScrollFixture, config: IConfig) => { direction === Direction.forward ? Direction.backward : Direction.forward; const edgeItem = await fixture.scroller.buffer.getEdgeVisibleItem(direction); - const oppositeItem = await fixture.scroller.buffer.getEdgeVisibleItem(opposite); + const oppositeItem = + await fixture.scroller.buffer.getEdgeVisibleItem(opposite); const edgeItemIndex = itemsCounter.get(direction).index; const oppositeItemIndex = itemsCounter.get(opposite).index; @@ -230,7 +249,9 @@ const shouldScroll = async (fixture: VScrollFixture, config: IConfig) => { itemsCounter.get(opposite).padding ); expect(await fixture.checkElementContentByIndex(edgeItemIndex)).toEqual(true); - expect(await fixture.checkElementContentByIndex(oppositeItemIndex)).toEqual(true); + expect(await fixture.checkElementContentByIndex(oppositeItemIndex)).toEqual( + true + ); }; const makeTest = (title: string, config: IConfig) => { @@ -253,95 +274,121 @@ test.describe('Basic Scroll Spec', () => { test.describe('Single max fwd scroll event', () => configList.forEach((config, index) => makeTest(`should process 1 forward max scroll (config ${index})`, config) - ) - ); + )); test.describe('Single max bwd scroll event', () => - configList.map(config => ({ - ...config, - custom: { - ...config.custom, - direction: Direction.backward - } - })).forEach((config, index) => - makeTest(`should process 1 backward max scroll (config ${index})`, config) - ) - ); + configList + .map(config => ({ + ...config, + custom: { + ...config.custom, + direction: Direction.backward + } + })) + .forEach((config, index) => + makeTest( + `should process 1 backward max scroll (config ${index})`, + config + ) + )); test.describe('Mass max fwd scroll events', () => - configList.map((config, index) => ({ - ...config, - custom: { - direction: Direction.forward, - count: 3 + treatIndex(index) - } - })).forEach((config, index) => - makeTest(`should process ${config.custom.count} forward scrolls (config ${index})`, config) - ) - ); + configList + .map((config, index) => ({ + ...config, + custom: { + direction: Direction.forward, + count: 3 + treatIndex(index) + } + })) + .forEach((config, index) => + makeTest( + `should process ${config.custom.count} forward scrolls (config ${index})`, + config + ) + )); test.describe('Mass max bwd scroll events', () => - configList.map((config, index) => ({ - ...config, - custom: { - direction: Direction.backward, - count: 3 + treatIndex(index) - } - })).forEach((config, index) => - makeTest(`should process ${config.custom.count} backward scrolls (config ${index})`, config) - ) - ); + configList + .map((config, index) => ({ + ...config, + custom: { + direction: Direction.backward, + count: 3 + treatIndex(index) + } + })) + .forEach((config, index) => + makeTest( + `should process ${config.custom.count} backward scrolls (config ${index})`, + config + ) + )); test.describe('Bouncing max two-directional scroll events (fwd started)', () => - configList.map((config, index) => ({ - ...config, - custom: { - direction: Direction.forward, - count: (3 + treatIndex(index)) * 2, - bouncing: true - } - })).forEach((config, index) => - makeTest(`should process ${config.custom.count} bouncing scrolls (config ${index})`, config) - ) - ); + configList + .map((config, index) => ({ + ...config, + custom: { + direction: Direction.forward, + count: (3 + treatIndex(index)) * 2, + bouncing: true + } + })) + .forEach((config, index) => + makeTest( + `should process ${config.custom.count} bouncing scrolls (config ${index})`, + config + ) + )); test.describe('Bouncing max two-directional scroll events (bwd started)', () => - configList.map((config, index) => ({ - ...config, - custom: { - direction: Direction.backward, - count: (3 + treatIndex(index)) * 2, - bouncing: true - } - })).forEach((config, index) => - makeTest(`should process ${config.custom.count} bouncing scrolls (config ${index})`, config) - ) - ); + configList + .map((config, index) => ({ + ...config, + custom: { + direction: Direction.backward, + count: (3 + treatIndex(index)) * 2, + bouncing: true + } + })) + .forEach((config, index) => + makeTest( + `should process ${config.custom.count} bouncing scrolls (config ${index})`, + config + ) + )); test.describe('Mass two-directional scroll events (first half fwd, second half bwd)', () => - configList.map((config, index) => ({ - ...config, - custom: { - direction: Direction.forward, - count: (3 + treatIndex(index)) * 2, - mass: true - } - })).forEach((config, index) => - makeTest(`should process ${config.custom.count} two-directional scrolls (config ${index})`, config) - ) - ); + configList + .map((config, index) => ({ + ...config, + custom: { + direction: Direction.forward, + count: (3 + treatIndex(index)) * 2, + mass: true + } + })) + .forEach((config, index) => + makeTest( + `should process ${config.custom.count} two-directional scrolls (config ${index})`, + config + ) + )); test.describe('Mass two-directional scroll events (first half bwd, second half fwd)', () => - configList.map((config, index) => ({ - ...config, - custom: { - direction: Direction.backward, - count: (3 + treatIndex(index)) * 2, - mass: true - } - })).forEach((config, index) => - makeTest(`should process ${config.custom.count} two-directional scrolls (config ${index})`, config) - ) - ); + configList + .map((config, index) => ({ + ...config, + custom: { + direction: Direction.backward, + count: (3 + treatIndex(index)) * 2, + mass: true + } + })) + .forEach((config, index) => + makeTest( + `should process ${config.custom.count} two-directional scrolls (config ${index})`, + config + ) + )); }); - diff --git a/tests/e2e/specs/scroll-delay.spec.ts b/tests/e2e/specs/scroll-delay.spec.ts index f17d4263..b15d4700 100644 --- a/tests/e2e/specs/scroll-delay.spec.ts +++ b/tests/e2e/specs/scroll-delay.spec.ts @@ -15,7 +15,7 @@ const datasourceGet: ITestConfig['datasourceGet'] = (index, count, success) => { /** * Test: Throttled scroll event handling - * + * * Rapidly triggers multiple scroll operations and verifies that the workflow * throttles them into fewer cycles than the number of scroll events. */ @@ -44,8 +44,8 @@ test.describe('Delay Scroll Spec', () => { expect(initCyclesCount).toBe(1); // Start rapid scrolling and monitor cycles in browser context - const count = await page.evaluate(() => - new Promise(resolve => { + const count = await page.evaluate(() => { + return new Promise(resolve => { const COUNT = 10; let scrollCount = 0; let timer: ReturnType; @@ -71,8 +71,8 @@ test.describe('Delay Scroll Spec', () => { clearInterval(timer); } }, 10); - }) - ); + }); + }); // Verify all scrolls were executed expect(count).toBe(10); @@ -80,7 +80,9 @@ test.describe('Delay Scroll Spec', () => { await fixture.dispose(); }); - test('should handle additional scrolling during slow fetch', async ({ page }) => { + test('should handle additional scrolling during slow fetch', async ({ + page + }) => { const config: ITestConfig = { datasourceGet, datasourceSettings: { @@ -103,44 +105,45 @@ test.describe('Delay Scroll Spec', () => { const startPosition = await fixture.scroller.viewport.scrollPosition; // Start rapid scrolling and monitor cycles in browser context - const result = await page.evaluate(({ start }) => - new Promise<{ endPos: number, position: number }>((resolve) => { - const COUNT = 10; - let scrollCount = 0; - let endPos: number; - let timer: ReturnType; - const workflow = window.__vscroll__.workflow; - const adapter = window.__vscroll__.datasource.adapter; - - // Subscribe to cyclesDone$ - stop timer when the nearest cycle completes (cycle 2) - workflow.cyclesDone$.on(count => { - if (count === 2) { - clearInterval(timer); - const position = workflow.scroller.viewport.scrollPosition; - resolve({ endPos, position }); - } - }); - - // Start the scrolling timer - timer = setInterval( - () => - requestAnimationFrame(() => { - scrollCount++; - endPos = start + scrollCount * 5; - - // Trigger scroll via adapter.fix - adapter.fix({ scrollPosition: endPos }); - - // Stop timer if all scrolls done - if (scrollCount === COUNT) { - clearInterval(timer); - } - }), - 25 - ); - }) - , { start: startPosition }); - + const result = await page.evaluate( + ({ start }) => + new Promise<{ endPos: number; position: number }>(resolve => { + const COUNT = 10; + let scrollCount = 0; + let endPos: number; + let timer: ReturnType; + const workflow = window.__vscroll__.workflow; + const adapter = window.__vscroll__.datasource.adapter; + + // Subscribe to cyclesDone$ - stop timer when the nearest cycle completes (cycle 2) + workflow.cyclesDone$.on(count => { + if (count === 2) { + clearInterval(timer); + const position = workflow.scroller.viewport.scrollPosition; + resolve({ endPos, position }); + } + }); + + // Start the scrolling timer + timer = setInterval( + () => + requestAnimationFrame(() => { + scrollCount++; + endPos = start + scrollCount * 5; + + // Trigger scroll via adapter.fix + adapter.fix({ scrollPosition: endPos }); + + // Stop timer if all scrolls done + if (scrollCount === COUNT) { + clearInterval(timer); + } + }), + 25 + ); + }), + { start: startPosition } + ); // Verify final position expect(result.position).toBe(result.endPos); @@ -149,4 +152,3 @@ test.describe('Delay Scroll Spec', () => { await fixture.dispose(); }); }); - diff --git a/tests/e2e/specs/scroll-fast.spec.ts b/tests/e2e/specs/scroll-fast.spec.ts index 32945990..5e2ee1fa 100644 --- a/tests/e2e/specs/scroll-fast.spec.ts +++ b/tests/e2e/specs/scroll-fast.spec.ts @@ -1,7 +1,11 @@ import { test, expect } from '@playwright/test'; import { afterEachLogs } from '../fixture/after-each-logs.js'; import { createFixture } from '../fixture/create-fixture.js'; -import { Direction, VScrollFixture, type DirectionType } from '../fixture/VScrollFixture.js'; +import { + Direction, + VScrollFixture, + type DirectionType +} from '../fixture/VScrollFixture.js'; import { ITestConfig } from 'types/index.js'; test.afterEach(afterEachLogs); @@ -15,7 +19,7 @@ type IConfig = ITestConfig; // Datasource generators for limited ranges // Note: We can't use closure variables because the function gets serialized. -const datasourceGet1to100 = (index: number, count: number, success: (data: unknown[]) => void): void => { +const datasourceGet1to100 = (index, count, success) => { const data = []; for (let i = index; i < index + count; i++) { if (i >= 1 && i <= 100) { @@ -25,7 +29,7 @@ const datasourceGet1to100 = (index: number, count: number, success: (data: unkno setTimeout(() => success(data), 25); }; -const datasourceGet51to200 = (index: number, count: number, success: (data: unknown[]) => void): void => { +const datasourceGet51to200 = (index, count, success) => { const data = []; for (let i = index; i < index + count; i++) { if (i >= 51 && i <= 200) { @@ -86,7 +90,11 @@ const configList: IConfig[] = [ maxIndex: 200, adapter: true }, - templateSettings: { noViewportClass: true, viewportHeight: 0, itemHeight: 20 }, + templateSettings: { + noViewportClass: true, + viewportHeight: 0, + itemHeight: 20 + }, custom: { items: 150, scrollCount: 5, start: Direction.backward } } ]; @@ -105,38 +113,38 @@ const configEofList: IConfig[] = configList.map(config => ({ const runFastScroll = ( fixture: VScrollFixture, customConfig: ICustom -): Promise => fixture.page.evaluate( - ({ scrollCount, start }) => { - return new Promise((resolve) => { - const adapter = window.__vscroll__.datasource.adapter; - let iteration = 0; - - const doScroll = () => { - setTimeout(() => { - // scrollMax - adapter.fix({ scrollPosition: Infinity }); - - setTimeout(async () => { - // scrollMin (except on last iteration for EOF tests) - if (iteration < scrollCount || start === 'backward') { - adapter.fix({ scrollPosition: 0 }); - } - - iteration++; - if (iteration <= scrollCount) { - doScroll(); - } else { - resolve(); - } +): Promise => + fixture.page.evaluate( + ({ scrollCount, start }) => + new Promise(resolve => { + const adapter = window.__vscroll__.datasource.adapter; + let iteration = 0; + + const doScroll = () => { + setTimeout(() => { + // scrollMax + adapter.fix({ scrollPosition: Infinity }); + + setTimeout(async () => { + // scrollMin (except on last iteration for EOF tests) + if (iteration < scrollCount || start === 'backward') { + adapter.fix({ scrollPosition: 0 }); + } + + iteration++; + if (iteration <= scrollCount) { + doScroll(); + } else { + resolve(); + } + }, 25); }, 25); - }, 25); - }; + }; - doScroll(); - }); - }, - { scrollCount: customConfig.scrollCount, start: customConfig.start } -); + doScroll(); + }), + { scrollCount: customConfig.scrollCount, start: customConfig.start } + ); /** * Wait for expected edge element to be reached @@ -210,8 +218,10 @@ const verifyExpectations = async ( // Get buffer and padding info const bufferSize = await fixture.scroller.buffer.size; - const backwardPadding = await fixture.scroller.viewport.paddings[Direction.backward].size; - const forwardPadding = await fixture.scroller.viewport.paddings[Direction.forward].size; + const backwardPadding = + await fixture.scroller.viewport.paddings[Direction.backward].size; + const forwardPadding = + await fixture.scroller.viewport.paddings[Direction.forward].size; const bufferHeight = bufferSize * itemHeight; const totalSize = backwardPadding + forwardPadding + bufferHeight; @@ -224,11 +234,15 @@ const verifyExpectations = async ( if (bufferSize > 0) { if (position === 0) { // At BOF - check first element - const first = await fixture.scroller.buffer.getEdgeVisibleItem(Direction.backward); + const first = await fixture.scroller.buffer.getEdgeVisibleItem( + Direction.backward + ); expect(first.$index).toBe(startIndex); } else { // At EOF - check last element - const last = await fixture.scroller.buffer.getEdgeVisibleItem(Direction.forward); + const last = await fixture.scroller.buffer.getEdgeVisibleItem( + Direction.forward + ); expect(last.$index).toBe(startIndex + config.custom.items - 1); } } @@ -263,13 +277,10 @@ test.describe('Fast Scroll Spec', () => { test.describe('multi-scroll to the BOF', () => configList.forEach((config, index) => makeTest(`should reach BOF without gaps (config ${index})`, config) - ) - ); + )); test.describe('multi-scroll to the EOF', () => configEofList.forEach((config, index) => makeTest(`should reach EOF without gaps (config ${index})`, config) - ) - ); + )); }); - diff --git a/tests/e2e/specs/viewport.spec.ts b/tests/e2e/specs/viewport.spec.ts index 9c4d1e52..57068d86 100644 --- a/tests/e2e/specs/viewport.spec.ts +++ b/tests/e2e/specs/viewport.spec.ts @@ -13,7 +13,7 @@ interface ICustom { type IConfig = ITestConfig; // Datasource: limited callback (1-100) -const datasourceGet = (index: number, count: number, success: (data: unknown[]) => void): void => { +const datasourceGet = (index, count, success) => { const data = []; const start = Math.max(1, index); const end = index + count - 1; @@ -33,7 +33,12 @@ const datasourceGet = (index: number, count: number, success: (data: unknown[]) const windowWith50HeaderConfig: IConfig = { datasourceGet, datasourceSettings: { startIndex: 1, windowViewport: true, adapter: true }, - templateSettings: { itemHeight: 50, noViewportClass: true, viewportHeight: 0, headerHeight: 50 }, + templateSettings: { + itemHeight: 50, + noViewportClass: true, + viewportHeight: 0, + headerHeight: 50 + }, custom: { scrollTo: undefined } }; @@ -90,9 +95,10 @@ const shouldWorkOnWindowWithHeader = async ( test.describe('Viewport Spec', () => { test.describe('Entire Window with Header', () => { windowWithHeaderConfigList.forEach((config, idx) => { - const scrollToText = config.custom?.scrollTo !== undefined - ? ` scroll to ${config.custom.scrollTo}` - : ' not scroll'; + const scrollToText = + config.custom?.scrollTo !== undefined + ? ` scroll to ${config.custom.scrollTo}` + : ' not scroll'; const headerText = `${config.templateSettings.headerHeight}-offset`; test(`should${scrollToText} with ${headerText}`, async ({ page }) => { diff --git a/tests/e2e/types/global.d.ts b/tests/e2e/types/global.d.ts index dc9aafaa..4ebaed39 100644 --- a/tests/e2e/types/global.d.ts +++ b/tests/e2e/types/global.d.ts @@ -32,5 +32,4 @@ declare global { } } -export { }; - +export {}; diff --git a/tests/e2e/types/index.ts b/tests/e2e/types/index.ts index 1f3ee15c..475e1395 100644 --- a/tests/e2e/types/index.ts +++ b/tests/e2e/types/index.ts @@ -1,7 +1,9 @@ - import type { Page } from '@playwright/test'; import type { IDatasource, Item, Workflow } from '../../../src/index.js'; -import type { Settings, DevSettings } from '../../../src/interfaces/settings.js'; +import type { + Settings, + DevSettings +} from '../../../src/interfaces/settings.js'; export type { Page }; export type { IDatasource, Item, Workflow }; @@ -33,4 +35,4 @@ export interface VScrollFixtureConfig { templateSettings?: ITemplateSettings; templateFn?: (item: unknown) => string; manualRun?: boolean; -} \ No newline at end of file +} From 5aad76a4728d28c24ad51642c009f7d0aaef6c91 Mon Sep 17 00:00:00 2001 From: Denis Aleksanov Date: Sun, 23 Nov 2025 00:50:01 +0100 Subject: [PATCH 41/51] prettier husky lint-staged: unit tests are formatted --- tests/unit/adapter.spec.ts | 117 +-- tests/unit/buffer.spec.ts | 1609 ++++++++++++++++++++--------- tests/unit/cache.spec.ts | 843 ++++++++++----- tests/unit/datasource-bad.spec.ts | 33 +- tests/unit/misc/items.ts | 15 +- tests/unit/reactive.spec.ts | 30 +- tests/unit/routines.spec.ts | 2 - tests/unit/validation.spec.ts | 258 ++--- 8 files changed, 1894 insertions(+), 1013 deletions(-) diff --git a/tests/unit/adapter.spec.ts b/tests/unit/adapter.spec.ts index 0a622e5c..98779c83 100644 --- a/tests/unit/adapter.spec.ts +++ b/tests/unit/adapter.spec.ts @@ -20,7 +20,6 @@ const MOCK = { }; describe('Adapter Init Spec', () => { - const ds = new Datasource(MOCK.datasource); it('adapter should be available after Datasource instantiation ', () => { @@ -32,7 +31,6 @@ describe('Adapter Init Spec', () => { it('version should match', () => { expect(ds.adapter.version).toBe(version.version); }); - }); describe('Adapter Wanted Spec', () => { @@ -110,65 +108,64 @@ describe('Adapter Wanted Spec', () => { expect(getWanted()).toBeFalsy(); }); - [AdapterPropName.firstVisible, AdapterPropName.firstVisible$].forEach((token) => - describe('Explicit access to ' + token, () => { - it('"wanted" should become true when accessing before the Adapter is instantiated', () => { - const ds = new Datasource(MOCK.datasource); - ds.adapter[token]; - const scr = new Scroller({ - datasource: ds, - element: MOCK.element, - workflow: MOCK.workflow + [AdapterPropName.firstVisible, AdapterPropName.firstVisible$].forEach( + token => + describe('Explicit access to ' + token, () => { + it('"wanted" should become true when accessing before the Adapter is instantiated', () => { + const ds = new Datasource(MOCK.datasource); + ds.adapter[token]; + const scr = new Scroller({ + datasource: ds, + element: MOCK.element, + workflow: MOCK.workflow + }); + expect(getWanted({ scr })).toBeTruthy(); + }); + + it('"wanted" should become true when accessing before init', () => { + datasource.adapter[token]; + expect(getWanted()).toBeTruthy(); + }); + + it('"wanted" should become true when accessing prop after init', () => { + scroller.viewport.reset = () => null; + scroller.init(); + datasource.adapter[token]; + expect(getWanted()).toBeTruthy(); + }); + + it('"wanted" should remain true after the Scroller reset without 1st and 2nd init', () => { + datasource.adapter[token]; + scroller = new Scroller({ datasource, scroller }); + expect(getWanted()).toBeTruthy(); + }); + + it('"wanted" should remain true after the Scroller reset with only 1st init', () => { + datasource.adapter[token]; + scroller.viewport.reset = () => null; + scroller.init(); + scroller = new Scroller({ datasource, scroller }); + expect(getWanted()).toBeTruthy(); + }); + + it('"wanted" should remain true after the Scroller reset with only 2nd init', () => { + datasource.adapter[token]; + scroller = new Scroller({ datasource, scroller }); + scroller.viewport.reset = () => null; + scroller.init(); + expect(getWanted()).toBeTruthy(); }); - expect(getWanted({ scr })).toBeTruthy(); - }); - - it('"wanted" should become true when accessing before init', () => { - datasource.adapter[token]; - expect(getWanted()).toBeTruthy(); - }); - - it('"wanted" should become true when accessing prop after init', () => { - scroller.viewport.reset = () => null; - scroller.init(); - datasource.adapter[token]; - expect(getWanted()).toBeTruthy(); - }); - - it('"wanted" should remain true after the Scroller reset without 1st and 2nd init', () => { - datasource.adapter[token]; - scroller = new Scroller({ datasource, scroller }); - expect(getWanted()).toBeTruthy(); - }); - - it('"wanted" should remain true after the Scroller reset with only 1st init', () => { - datasource.adapter[token]; - scroller.viewport.reset = () => null; - scroller.init(); - scroller = new Scroller({ datasource, scroller }); - expect(getWanted()).toBeTruthy(); - }); - - it('"wanted" should remain true after the Scroller reset with only 2nd init', () => { - datasource.adapter[token]; - scroller = new Scroller({ datasource, scroller }); - scroller.viewport.reset = () => null; - scroller.init(); - expect(getWanted()).toBeTruthy(); - }); - - it('"wanted" should remain true after the Scroller reset with both 1st and 2nd init', () => { - datasource.adapter[token]; - scroller.viewport.reset = () => null; - scroller.init(); - scroller = new Scroller({ datasource, scroller }); - scroller.viewport.reset = () => null; - scroller.init(); - expect(getWanted()).toBeTruthy(); - }); - }) - ); + it('"wanted" should remain true after the Scroller reset with both 1st and 2nd init', () => { + datasource.adapter[token]; + scroller.viewport.reset = () => null; + scroller.init(); + scroller = new Scroller({ datasource, scroller }); + scroller.viewport.reset = () => null; + scroller.init(); + expect(getWanted()).toBeTruthy(); + }); + }) + ); }); }); - diff --git a/tests/unit/buffer.spec.ts b/tests/unit/buffer.spec.ts index b937cccd..27e10b96 100644 --- a/tests/unit/buffer.spec.ts +++ b/tests/unit/buffer.spec.ts @@ -1,9 +1,18 @@ import { Buffer } from '../../src/classes/buffer'; import { Direction } from '../../src/inputs'; -import { generateItem as makeItem, generateBufferItem as cb, generateBufferItems, generateItem } from './misc/items'; import { - Data, BufferParams, BufferUpdateConfig, BufferUpdateTrackConfig, BufferInsertConfig + generateItem as makeItem, + generateBufferItem as cb, + generateBufferItems, + generateItem +} from './misc/items'; +import { + Data, + BufferParams, + BufferUpdateConfig, + BufferUpdateTrackConfig, + BufferInsertConfig } from './misc/types'; const loggerMock = { log: () => null }; @@ -14,50 +23,61 @@ const makeBuffer = (params: BufferParams): Buffer => { const _maxCache = (Number.isInteger(maxCache) ? maxCache : max) as number; const _absMin = Number.isInteger(absMin) ? absMin : _minCache; const _absMax = Number.isInteger(absMax) ? absMax : _maxCache; - const buffer = new Buffer({ - itemSize: NaN, - cacheData: false, - startIndex: Number.isInteger(start) ? start : 1, - minIndex: _absMin, - maxIndex: _absMax, - } as never, () => null, loggerMock as never); + const buffer = new Buffer( + { + itemSize: NaN, + cacheData: false, + startIndex: Number.isInteger(start) ? start : 1, + minIndex: _absMin, + maxIndex: _absMax + } as never, + () => null, + loggerMock as never + ); buffer.setItems(generateBufferItems(min, max - min + 1)); - generateBufferItems(_minCache, _maxCache - _minCache + 1).forEach(item => buffer.cacheItem(item)); + generateBufferItems(_minCache, _maxCache - _minCache + 1).forEach(item => + buffer.cacheItem(item) + ); return buffer; }; -const checkUpdate = ({ min, max, predicate, fixRight, list }: BufferUpdateConfig) => () => { - const buffer = makeBuffer({ min, max }); - buffer.updateItems(predicate, cb, NaN, fixRight); - // console.log(buffer.items.map(i => i.get())); - expect(buffer.size).toEqual(list.length); - list.forEach((current, index) => { - const $index = Number(Object.keys(current)[0]); - if (index === 0) { - expect(buffer.firstIndex).toEqual($index); - expect(buffer.minIndex).toEqual($index); - expect(buffer.absMinIndex).toEqual($index); - } - if (index === list.length - 1) { - expect(buffer.lastIndex).toEqual($index); - expect(buffer.maxIndex).toEqual($index); - expect(buffer.absMaxIndex).toEqual($index); - } - const id = current[$index]; - const item = buffer.items[index]; - expect(item.$index).toEqual($index); - expect(item.data.id).toEqual(id); - }); -}; +const checkUpdate = + ({ min, max, predicate, fixRight, list }: BufferUpdateConfig) => + () => { + const buffer = makeBuffer({ min, max }); + buffer.updateItems(predicate, cb, NaN, fixRight); + // console.log(buffer.items.map(i => i.get())); + expect(buffer.size).toEqual(list.length); + list.forEach((current, index) => { + const $index = Number(Object.keys(current)[0]); + if (index === 0) { + expect(buffer.firstIndex).toEqual($index); + expect(buffer.minIndex).toEqual($index); + expect(buffer.absMinIndex).toEqual($index); + } + if (index === list.length - 1) { + expect(buffer.lastIndex).toEqual($index); + expect(buffer.maxIndex).toEqual($index); + expect(buffer.absMaxIndex).toEqual($index); + } + const id = current[$index]; + const item = buffer.items[index]; + expect(item.$index).toEqual($index); + expect(item.data.id).toEqual(id); + }); + }; const checkIndexTrackingOnUpdate = (params: BufferUpdateTrackConfig) => () => { const { index, predicate, fixRight, result, debug = false } = params; const buffer = makeBuffer(params); const item = buffer.get(index); const _item = debug && item ? JSON.parse(JSON.stringify(item.get())) : null; - const dump = debug && JSON.parse(JSON.stringify(buffer.items.map(i => i.get()))); - const absMinBefore = debug && buffer.finiteAbsMinIndex, absMaxBefore = debug && buffer.finiteAbsMaxIndex; - const minBefore = debug && buffer.minIndex, maxBefore = debug && buffer.maxIndex; + const dump = + debug && JSON.parse(JSON.stringify(buffer.items.map(i => i.get()))); + const absMinBefore = debug && buffer.finiteAbsMinIndex, + absMaxBefore = debug && buffer.finiteAbsMaxIndex; + const minBefore = debug && buffer.minIndex, + maxBefore = debug && buffer.maxIndex; const { trackedIndex } = buffer.updateItems(predicate, cb, index, fixRight); @@ -81,496 +101,1069 @@ const checkInsert = (params: BufferInsertConfig) => () => { const buffer = makeBuffer(params); const { result } = params; const items = params.items.map(id => generateItem(id)); - buffer.insertVirtually(items, params.index, params.direction, params.fixRight); + buffer.insertVirtually( + items, + params.index, + params.direction, + params.fixRight + ); buffer.items.forEach((item, i) => { - const $index = result ? Number(Object.keys(result.list[i])[0]) : bufferBefore.items[i].$index; + const $index = result + ? Number(Object.keys(result.list[i])[0]) + : bufferBefore.items[i].$index; const id = result ? result.list[i][$index] : bufferBefore.items[i].data.id; expect(item.$index).toBe($index); expect(item.data.id).toBe(id); }); - expect(buffer.absMinIndex).toEqual(result ? result.absMin : bufferBefore.absMinIndex); - expect(buffer.absMaxIndex).toEqual(result ? result.absMax : bufferBefore.absMaxIndex); + expect(buffer.absMinIndex).toEqual( + result ? result.absMin : bufferBefore.absMinIndex + ); + expect(buffer.absMaxIndex).toEqual( + result ? result.absMax : bufferBefore.absMaxIndex + ); }; describe('Buffer Spec', () => { - - describe('Update', () => [ - { - title: 'should pass all (simple)', - min: 1, max: 3, fixRight: false, - predicate: () => true, - list: [{ 1: 1 }, { 2: 2 }, { 3: 3 }], - }, { - title: 'should pass all (truthy)', - min: 1, max: 10, fixRight: false, - predicate: ({ $index }) => { - switch ($index) { - case 1: return 1; - case 2: return 'test'; - case 3: return {}; - case 4: return () => null; - case 5: return class { }; - case 6: return new Map(); - case 7: return new Set(); - case 8: return Symbol(); - case 9: return new Date(); - case 10: return new RegExp(''); - } + describe('Update', () => + [ + { + title: 'should pass all (simple)', + min: 1, + max: 3, + fixRight: false, + predicate: () => true, + list: [{ 1: 1 }, { 2: 2 }, { 3: 3 }] }, - list: Array.from({ length: 10 }).map((j, i) => ({ [i + 1]: i + 1 })), - }, { - title: 'should remove all (simple)', - min: 1, max: 3, fixRight: false, - predicate: () => false, - list: [], - }, { - title: 'should remove all (empty array)', - min: 1, max: 10, fixRight: false, - predicate: () => [], - list: [], - }, { - title: 'should remove all (falsy)', - min: 1, max: 10, fixRight: false, - predicate: ({ $index }) => { - switch ($index) { - case 1: return 0; - case 2: return -0; - case 3: return ''; - case 4: return NaN; - case 5: return null; - case 6: return void 0; - } + { + title: 'should pass all (truthy)', + min: 1, + max: 10, + fixRight: false, + predicate: ({ $index }) => { + switch ($index) { + case 1: + return 1; + case 2: + return 'test'; + case 3: + return {}; + case 4: + return () => null; + case 5: + return class {}; + case 6: + return new Map(); + case 7: + return new Set(); + case 8: + return Symbol(); + case 9: + return new Date(); + case 10: + return new RegExp(''); + } + }, + list: Array.from({ length: 10 }).map((j, i) => ({ [i + 1]: i + 1 })) }, - list: [], - }, { - title: 'should remove left item', - min: 1, max: 3, fixRight: false, - predicate: ({ $index }) => $index !== 1, - list: [{ 1: 2 }, { 2: 3 }], - }, { - title: 'should remove left item (fixRight)', - min: 1, max: 3, fixRight: true, - predicate: ({ $index }) => $index !== 1, - list: [{ 2: 2 }, { 3: 3 }], - }, { - title: 'should remove right item', - min: 1, max: 3, fixRight: false, - predicate: ({ $index }) => $index !== 3, - list: [{ 1: 1 }, { 2: 2 }], - }, { - title: 'should remove right item (fixRight)', - min: 1, max: 3, fixRight: true, - predicate: ({ $index }) => $index !== 3, - list: [{ 2: 1 }, { 3: 2 }], - }, { - title: 'should remove some middle items', - min: 1, max: 5, fixRight: false, - predicate: ({ $index }) => $index !== 2 && $index !== 4, - list: [{ 1: 1 }, { 2: 3 }, { 3: 5 }], - }, { - title: 'should remove some middle items (fixRight)', - min: 1, max: 5, fixRight: true, - predicate: ({ $index }) => $index !== 2 && $index !== 4, - list: [{ 3: 1 }, { 4: 3 }, { 5: 5 }], - }, { - title: 'should prepend', - min: 1, max: 2, fixRight: false, - predicate: ({ $index, data }) => $index === 1 ? [makeItem(0), data] : true, - list: [{ 1: 0 }, { 2: 1 }, { 3: 2 }], - }, { - title: 'should prepend (fixRight)', - min: 1, max: 2, fixRight: true, - predicate: ({ $index, data }) => $index === 1 ? [makeItem(0), data] : true, - list: [{ 0: 0 }, { 1: 1 }, { 2: 2 }], - }, { - title: 'should append', - min: 1, max: 2, fixRight: false, - predicate: ({ $index, data }) => $index === 2 ? [data, makeItem(3)] : true, - list: [{ 1: 1 }, { 2: 2 }, { 3: 3 }], - }, { - title: 'should append (fixRight)', - min: 1, max: 2, fixRight: true, - predicate: ({ $index, data }) => $index === 2 ? [data, makeItem(3)] : true, - list: [{ 0: 1 }, { 1: 2 }, { 2: 3 }], - }, { - title: 'should insert in left-center', - min: 1, max: 3, fixRight: false, - predicate: ({ $index, data }) => $index === 2 ? [makeItem(99), data] : true, - list: [{ 1: 1 }, { 2: 99 }, { 3: 2 }, { 4: 3 }], - }, { - title: 'should insert in left-center (fixRight)', - min: 1, max: 3, fixRight: true, - predicate: ({ $index, data }) => $index === 2 ? [makeItem(99), data] : true, - list: [{ 0: 1 }, { 1: 99 }, { 2: 2 }, { 3: 3 }], - }, { - title: 'should insert in right-center', - min: 1, max: 3, fixRight: false, - predicate: ({ $index, data }) => $index === 2 ? [data, makeItem(99)] : true, - list: [{ 1: 1 }, { 2: 2 }, { 3: 99 }, { 4: 3 }], - }, { - title: 'should insert in right-center (fixRight)', - min: 1, max: 3, fixRight: true, - predicate: ({ $index, data }) => $index === 2 ? [data, makeItem(99)] : true, - list: [{ 0: 1 }, { 1: 2 }, { 2: 99 }, { 3: 3 }], - }, { - title: 'should replace middle item', - min: 1, max: 3, fixRight: false, - predicate: ({ $index }) => $index === 2 ? [makeItem(99)] : true, - list: [{ 1: 1 }, { 2: 99 }, { 3: 3 }], - }, { - title: 'should replace middle item (fixRight)', - min: 1, max: 3, fixRight: true, - predicate: ({ $index }) => $index === 2 ? [makeItem(99)] : true, - list: [{ 1: 1 }, { 2: 99 }, { 3: 3 }], - }, { - title: 'should replace left item', - min: 1, max: 3, fixRight: false, - predicate: ({ $index }) => $index === 1 ? [makeItem(99)] : true, - list: [{ 1: 99 }, { 2: 2 }, { 3: 3 }], - }, { - title: 'should replace left item (fixRight)', - min: 1, max: 3, fixRight: true, - predicate: ({ $index }) => $index === 1 ? [makeItem(99)] : true, - list: [{ 1: 99 }, { 2: 2 }, { 3: 3 }], - }, { - title: 'should replace and insert', - min: 1, max: 3, fixRight: false, - predicate: ({ $index }) => $index === 2 ? [makeItem(97), makeItem(98), makeItem(99)] : true, - list: [{ 1: 1 }, { 2: 97 }, { 3: 98 }, { 4: 99 }, { 5: 3 }], - }, { - title: 'should replace and insert (fixRight)', - min: 1, max: 3, fixRight: true, - predicate: ({ $index }) => $index === 2 ? [makeItem(97), makeItem(98), makeItem(99)] : true, - list: [{ '-1': 1 }, { 0: 97 }, { 1: 98 }, { 2: 99 }, { 3: 3 }], - }, { - title: 'should perform complex update', - min: 1, max: 5, fixRight: false, - predicate: ({ $index, data }) => { - switch ($index) { - case 1: return [makeItem(0), data]; - case 2: return []; - case 3: return [makeItem(2), makeItem(4)]; - case 4: return []; - case 5: return [data, makeItem(6)]; - } + { + title: 'should remove all (simple)', + min: 1, + max: 3, + fixRight: false, + predicate: () => false, + list: [] }, - list: [{ 1: 0 }, { 2: 1 }, { 3: 2 }, { 4: 4 }, { 5: 5 }, { 6: 6 }], - }, { - title: 'should perform complex update (fixRight)', - min: 1, max: 5, fixRight: true, - predicate: ({ $index, data }) => { - switch ($index) { - case 1: return [makeItem(0), data]; - case 2: return []; - case 3: return [makeItem(2), makeItem(4)]; - case 4: return []; - case 5: return [data, makeItem(6)]; - } + { + title: 'should remove all (empty array)', + min: 1, + max: 10, + fixRight: false, + predicate: () => [], + list: [] }, - list: [{ 0: 0 }, { 1: 1 }, { 2: 2 }, { 3: 4 }, { 4: 5 }, { 5: 6 }], - }] - .forEach(config => it(config.title, checkUpdate(config as BufferUpdateConfig))) - ); + { + title: 'should remove all (falsy)', + min: 1, + max: 10, + fixRight: false, + predicate: ({ $index }) => { + switch ($index) { + case 1: + return 0; + case 2: + return -0; + case 3: + return ''; + case 4: + return NaN; + case 5: + return null; + case 6: + return void 0; + } + }, + list: [] + }, + { + title: 'should remove left item', + min: 1, + max: 3, + fixRight: false, + predicate: ({ $index }) => $index !== 1, + list: [{ 1: 2 }, { 2: 3 }] + }, + { + title: 'should remove left item (fixRight)', + min: 1, + max: 3, + fixRight: true, + predicate: ({ $index }) => $index !== 1, + list: [{ 2: 2 }, { 3: 3 }] + }, + { + title: 'should remove right item', + min: 1, + max: 3, + fixRight: false, + predicate: ({ $index }) => $index !== 3, + list: [{ 1: 1 }, { 2: 2 }] + }, + { + title: 'should remove right item (fixRight)', + min: 1, + max: 3, + fixRight: true, + predicate: ({ $index }) => $index !== 3, + list: [{ 2: 1 }, { 3: 2 }] + }, + { + title: 'should remove some middle items', + min: 1, + max: 5, + fixRight: false, + predicate: ({ $index }) => $index !== 2 && $index !== 4, + list: [{ 1: 1 }, { 2: 3 }, { 3: 5 }] + }, + { + title: 'should remove some middle items (fixRight)', + min: 1, + max: 5, + fixRight: true, + predicate: ({ $index }) => $index !== 2 && $index !== 4, + list: [{ 3: 1 }, { 4: 3 }, { 5: 5 }] + }, + { + title: 'should prepend', + min: 1, + max: 2, + fixRight: false, + predicate: ({ $index, data }) => + $index === 1 ? [makeItem(0), data] : true, + list: [{ 1: 0 }, { 2: 1 }, { 3: 2 }] + }, + { + title: 'should prepend (fixRight)', + min: 1, + max: 2, + fixRight: true, + predicate: ({ $index, data }) => + $index === 1 ? [makeItem(0), data] : true, + list: [{ 0: 0 }, { 1: 1 }, { 2: 2 }] + }, + { + title: 'should append', + min: 1, + max: 2, + fixRight: false, + predicate: ({ $index, data }) => + $index === 2 ? [data, makeItem(3)] : true, + list: [{ 1: 1 }, { 2: 2 }, { 3: 3 }] + }, + { + title: 'should append (fixRight)', + min: 1, + max: 2, + fixRight: true, + predicate: ({ $index, data }) => + $index === 2 ? [data, makeItem(3)] : true, + list: [{ 0: 1 }, { 1: 2 }, { 2: 3 }] + }, + { + title: 'should insert in left-center', + min: 1, + max: 3, + fixRight: false, + predicate: ({ $index, data }) => + $index === 2 ? [makeItem(99), data] : true, + list: [{ 1: 1 }, { 2: 99 }, { 3: 2 }, { 4: 3 }] + }, + { + title: 'should insert in left-center (fixRight)', + min: 1, + max: 3, + fixRight: true, + predicate: ({ $index, data }) => + $index === 2 ? [makeItem(99), data] : true, + list: [{ 0: 1 }, { 1: 99 }, { 2: 2 }, { 3: 3 }] + }, + { + title: 'should insert in right-center', + min: 1, + max: 3, + fixRight: false, + predicate: ({ $index, data }) => + $index === 2 ? [data, makeItem(99)] : true, + list: [{ 1: 1 }, { 2: 2 }, { 3: 99 }, { 4: 3 }] + }, + { + title: 'should insert in right-center (fixRight)', + min: 1, + max: 3, + fixRight: true, + predicate: ({ $index, data }) => + $index === 2 ? [data, makeItem(99)] : true, + list: [{ 0: 1 }, { 1: 2 }, { 2: 99 }, { 3: 3 }] + }, + { + title: 'should replace middle item', + min: 1, + max: 3, + fixRight: false, + predicate: ({ $index }) => ($index === 2 ? [makeItem(99)] : true), + list: [{ 1: 1 }, { 2: 99 }, { 3: 3 }] + }, + { + title: 'should replace middle item (fixRight)', + min: 1, + max: 3, + fixRight: true, + predicate: ({ $index }) => ($index === 2 ? [makeItem(99)] : true), + list: [{ 1: 1 }, { 2: 99 }, { 3: 3 }] + }, + { + title: 'should replace left item', + min: 1, + max: 3, + fixRight: false, + predicate: ({ $index }) => ($index === 1 ? [makeItem(99)] : true), + list: [{ 1: 99 }, { 2: 2 }, { 3: 3 }] + }, + { + title: 'should replace left item (fixRight)', + min: 1, + max: 3, + fixRight: true, + predicate: ({ $index }) => ($index === 1 ? [makeItem(99)] : true), + list: [{ 1: 99 }, { 2: 2 }, { 3: 3 }] + }, + { + title: 'should replace and insert', + min: 1, + max: 3, + fixRight: false, + predicate: ({ $index }) => + $index === 2 ? [makeItem(97), makeItem(98), makeItem(99)] : true, + list: [{ 1: 1 }, { 2: 97 }, { 3: 98 }, { 4: 99 }, { 5: 3 }] + }, + { + title: 'should replace and insert (fixRight)', + min: 1, + max: 3, + fixRight: true, + predicate: ({ $index }) => + $index === 2 ? [makeItem(97), makeItem(98), makeItem(99)] : true, + list: [{ '-1': 1 }, { 0: 97 }, { 1: 98 }, { 2: 99 }, { 3: 3 }] + }, + { + title: 'should perform complex update', + min: 1, + max: 5, + fixRight: false, + predicate: ({ $index, data }) => { + switch ($index) { + case 1: + return [makeItem(0), data]; + case 2: + return []; + case 3: + return [makeItem(2), makeItem(4)]; + case 4: + return []; + case 5: + return [data, makeItem(6)]; + } + }, + list: [{ 1: 0 }, { 2: 1 }, { 3: 2 }, { 4: 4 }, { 5: 5 }, { 6: 6 }] + }, + { + title: 'should perform complex update (fixRight)', + min: 1, + max: 5, + fixRight: true, + predicate: ({ $index, data }) => { + switch ($index) { + case 1: + return [makeItem(0), data]; + case 2: + return []; + case 3: + return [makeItem(2), makeItem(4)]; + case 4: + return []; + case 5: + return [data, makeItem(6)]; + } + }, + list: [{ 0: 0 }, { 1: 1 }, { 2: 2 }, { 3: 4 }, { 4: 5 }, { 5: 6 }] + } + ].forEach(config => + it(config.title, checkUpdate(config as BufferUpdateConfig)) + )); - describe('Index tracking on Update', () => [ - { - title: 'remove 1 item before', - min: 1, max: 10, fixRight: false, index: 5, result: 4, - predicate: ({ $index }) => $index !== 3, - }, { - title: 'remove 3 items before', - min: 1, max: 10, fixRight: false, index: 5, result: 2, - predicate: ({ $index }) => $index < 2 || $index > 4, - }, { - title: 'remove 3 items before (fixRight)', - min: 1, max: 10, fixRight: true, index: 5, result: 5, - predicate: ({ $index }) => $index < 1 || $index > 3, - }, { - title: 'remove 1 item after', - min: 1, max: 10, fixRight: false, index: 5, result: 5, - predicate: ({ $index }) => $index !== 7, - }, { - title: 'remove 3 items after', - min: 1, max: 10, fixRight: false, index: 5, result: 5, - predicate: ({ $index }) => $index < 7 || $index > 9, - }, { - title: 'remove 3 items after (fixRight)', - min: 1, max: 10, fixRight: true, index: 5, result: 8, - predicate: ({ $index }) => $index < 7 || $index > 9, - }, { - title: 'remove 1 item before and 1 item after', - min: 1, max: 10, fixRight: false, index: 5, result: 4, - predicate: ({ $index }) => !($index === 4 || $index === 6), - }, { - title: 'remove 1 item before and 1 item after (fixRight)', - min: 1, max: 10, fixRight: true, index: 5, result: 6, - predicate: ({ $index }) => !($index === 4 || $index === 6), - }, { - title: 'insert 1 item before', - min: 1, max: 10, fixRight: false, index: 5, result: 6, - predicate: ({ $index, data }) => $index === 2 ? [data, makeItem('x')] : true, - }, { - title: 'insert 2 items before', - min: 1, max: 10, fixRight: false, index: 5, result: 7, - predicate: ({ $index, data }) => $index === 2 ? [data, makeItem('x'), makeItem('y')] : true, - }, { - title: 'insert 2 items before (fixRight)', - min: 1, max: 10, fixRight: true, index: 5, result: 5, - predicate: ({ $index, data }) => $index === 2 ? [data, makeItem('x'), makeItem('y')] : true, - }, { - title: 'insert 1 item after', - min: 1, max: 10, fixRight: false, index: 5, result: 5, - predicate: ({ $index, data }) => $index === 7 ? [data, makeItem('x')] : true, - }, { - title: 'insert 2 items after', - min: 1, max: 10, fixRight: false, index: 5, result: 5, - predicate: ({ $index, data }) => $index === 7 ? [data, makeItem('x'), makeItem('y')] : true, - }, { - title: 'insert 2 items after (fixRight)', - min: 1, max: 10, fixRight: true, index: 5, result: 3, - predicate: ({ $index, data }) => $index === 7 ? [data, makeItem('x'), makeItem('y')] : true, - }, { - title: 'insert 1 item before and 1 item after', - min: 1, max: 10, fixRight: false, index: 5, result: 6, - predicate: ({ $index, data }) => $index === 5 ? [makeItem('x'), data, makeItem('y')] : true, - }, { - title: 'insert 1 item before and 1 item after (fixRight)', - min: 1, max: 10, fixRight: true, index: 5, result: 4, - predicate: ({ $index, data }) => $index === 5 ? [makeItem('x'), data, makeItem('y')] : true, - }, { - title: 'replace this item', - min: 1, max: 10, fixRight: false, index: 5, result: 5, - predicate: ({ $index }) => $index === 5 ? [makeItem('x')] : true, - }, { - title: 'replace this item (fixRight)', - min: 1, max: 10, fixRight: true, index: 5, result: 5, - predicate: ({ $index }) => $index === 5 ? [makeItem('x')] : true, - }, { - title: 'replace this item, remove 1 before and 1 after', - min: 1, max: 10, fixRight: false, index: 5, result: 4, - predicate: ({ $index }) => $index < 4 || $index > 6 ? true : ($index === 5 ? [makeItem('x')] : []), - }, { - title: 'replace this item, remove 1 before and 1 after (fixRight)', - min: 1, max: 10, fixRight: true, index: 5, result: 6, - predicate: ({ $index }) => $index < 4 || $index > 6 ? true : ($index === 5 ? [makeItem('x')] : []), - }, { - title: 'replace this item, remove 3 after', - min: 1, max: 10, fixRight: false, index: 5, result: 5, - predicate: ({ $index }) => $index === 5 ? [makeItem('x')] : !($index > 5 && $index <= 8), - }, { - title: 'replace this item, remove 3 after (fixRight)', - min: 1, max: 10, fixRight: true, index: 5, result: 8, - predicate: ({ $index }) => $index === 5 ? [makeItem('x')] : !($index > 5 && $index <= 8), - }, { - title: 'replace this item, remove 3 before', - min: 1, max: 10, fixRight: false, index: 5, result: 2, - predicate: ({ $index }) => $index === 5 ? [makeItem('x')] : !($index < 5 && $index >= 2), - }, { - title: 'replace this item, remove 3 before (fixRight)', - min: 1, max: 10, fixRight: true, index: 5, result: 5, - predicate: ({ $index }) => $index === 5 ? [makeItem('x')] : !($index < 5 && $index >= 2), - }, { - title: 'remove this item', - min: 1, max: 10, fixRight: false, index: 5, result: 5, - predicate: ({ $index }) => $index !== 5, - }, { - title: 'remove this item (fixRight)', - min: 1, max: 10, fixRight: true, index: 5, result: 6, - predicate: ({ $index }) => $index !== 5, - }, { - title: 'remove this, 1 before and 1 after', - min: 1, max: 10, fixRight: false, index: 5, result: 4, - predicate: ({ $index }) => !($index >= 4 && $index <= 6), - }, { - title: 'remove this, 1 before and 1 after (fixRight)', - min: 1, max: 10, fixRight: true, index: 5, result: 7, - predicate: ({ $index }) => !($index >= 4 && $index <= 6), - }, { - title: 'remove this and all after', - min: 1, max: 10, fixRight: false, index: 5, result: 4, - predicate: ({ $index }) => !($index >= 5), - }, { - title: 'remove this and all after (fixRight)', - min: 1, max: 10, fixRight: true, index: 5, result: 10, - predicate: ({ $index }) => !($index >= 5), - }, { - title: 'remove this and all before', - min: 1, max: 10, fixRight: false, index: 5, result: 1, - predicate: ({ $index }) => !($index <= 5), - }, { - title: 'remove this and all before (fixRight)', - min: 1, max: 10, fixRight: true, index: 5, result: 6, - predicate: ({ $index }) => !($index <= 5), - }, { - title: 'remove all', - min: 1, max: 10, fixRight: false, index: 5, result: NaN, - predicate: () => [], - }] - .forEach(config => it(config.title, checkIndexTrackingOnUpdate(config))) - ); + describe('Index tracking on Update', () => + [ + { + title: 'remove 1 item before', + min: 1, + max: 10, + fixRight: false, + index: 5, + result: 4, + predicate: ({ $index }) => $index !== 3 + }, + { + title: 'remove 3 items before', + min: 1, + max: 10, + fixRight: false, + index: 5, + result: 2, + predicate: ({ $index }) => $index < 2 || $index > 4 + }, + { + title: 'remove 3 items before (fixRight)', + min: 1, + max: 10, + fixRight: true, + index: 5, + result: 5, + predicate: ({ $index }) => $index < 1 || $index > 3 + }, + { + title: 'remove 1 item after', + min: 1, + max: 10, + fixRight: false, + index: 5, + result: 5, + predicate: ({ $index }) => $index !== 7 + }, + { + title: 'remove 3 items after', + min: 1, + max: 10, + fixRight: false, + index: 5, + result: 5, + predicate: ({ $index }) => $index < 7 || $index > 9 + }, + { + title: 'remove 3 items after (fixRight)', + min: 1, + max: 10, + fixRight: true, + index: 5, + result: 8, + predicate: ({ $index }) => $index < 7 || $index > 9 + }, + { + title: 'remove 1 item before and 1 item after', + min: 1, + max: 10, + fixRight: false, + index: 5, + result: 4, + predicate: ({ $index }) => !($index === 4 || $index === 6) + }, + { + title: 'remove 1 item before and 1 item after (fixRight)', + min: 1, + max: 10, + fixRight: true, + index: 5, + result: 6, + predicate: ({ $index }) => !($index === 4 || $index === 6) + }, + { + title: 'insert 1 item before', + min: 1, + max: 10, + fixRight: false, + index: 5, + result: 6, + predicate: ({ $index, data }) => + $index === 2 ? [data, makeItem('x')] : true + }, + { + title: 'insert 2 items before', + min: 1, + max: 10, + fixRight: false, + index: 5, + result: 7, + predicate: ({ $index, data }) => + $index === 2 ? [data, makeItem('x'), makeItem('y')] : true + }, + { + title: 'insert 2 items before (fixRight)', + min: 1, + max: 10, + fixRight: true, + index: 5, + result: 5, + predicate: ({ $index, data }) => + $index === 2 ? [data, makeItem('x'), makeItem('y')] : true + }, + { + title: 'insert 1 item after', + min: 1, + max: 10, + fixRight: false, + index: 5, + result: 5, + predicate: ({ $index, data }) => + $index === 7 ? [data, makeItem('x')] : true + }, + { + title: 'insert 2 items after', + min: 1, + max: 10, + fixRight: false, + index: 5, + result: 5, + predicate: ({ $index, data }) => + $index === 7 ? [data, makeItem('x'), makeItem('y')] : true + }, + { + title: 'insert 2 items after (fixRight)', + min: 1, + max: 10, + fixRight: true, + index: 5, + result: 3, + predicate: ({ $index, data }) => + $index === 7 ? [data, makeItem('x'), makeItem('y')] : true + }, + { + title: 'insert 1 item before and 1 item after', + min: 1, + max: 10, + fixRight: false, + index: 5, + result: 6, + predicate: ({ $index, data }) => + $index === 5 ? [makeItem('x'), data, makeItem('y')] : true + }, + { + title: 'insert 1 item before and 1 item after (fixRight)', + min: 1, + max: 10, + fixRight: true, + index: 5, + result: 4, + predicate: ({ $index, data }) => + $index === 5 ? [makeItem('x'), data, makeItem('y')] : true + }, + { + title: 'replace this item', + min: 1, + max: 10, + fixRight: false, + index: 5, + result: 5, + predicate: ({ $index }) => ($index === 5 ? [makeItem('x')] : true) + }, + { + title: 'replace this item (fixRight)', + min: 1, + max: 10, + fixRight: true, + index: 5, + result: 5, + predicate: ({ $index }) => ($index === 5 ? [makeItem('x')] : true) + }, + { + title: 'replace this item, remove 1 before and 1 after', + min: 1, + max: 10, + fixRight: false, + index: 5, + result: 4, + predicate: ({ $index }) => + $index < 4 || $index > 6 ? true : $index === 5 ? [makeItem('x')] : [] + }, + { + title: 'replace this item, remove 1 before and 1 after (fixRight)', + min: 1, + max: 10, + fixRight: true, + index: 5, + result: 6, + predicate: ({ $index }) => + $index < 4 || $index > 6 ? true : $index === 5 ? [makeItem('x')] : [] + }, + { + title: 'replace this item, remove 3 after', + min: 1, + max: 10, + fixRight: false, + index: 5, + result: 5, + predicate: ({ $index }) => + $index === 5 ? [makeItem('x')] : !($index > 5 && $index <= 8) + }, + { + title: 'replace this item, remove 3 after (fixRight)', + min: 1, + max: 10, + fixRight: true, + index: 5, + result: 8, + predicate: ({ $index }) => + $index === 5 ? [makeItem('x')] : !($index > 5 && $index <= 8) + }, + { + title: 'replace this item, remove 3 before', + min: 1, + max: 10, + fixRight: false, + index: 5, + result: 2, + predicate: ({ $index }) => + $index === 5 ? [makeItem('x')] : !($index < 5 && $index >= 2) + }, + { + title: 'replace this item, remove 3 before (fixRight)', + min: 1, + max: 10, + fixRight: true, + index: 5, + result: 5, + predicate: ({ $index }) => + $index === 5 ? [makeItem('x')] : !($index < 5 && $index >= 2) + }, + { + title: 'remove this item', + min: 1, + max: 10, + fixRight: false, + index: 5, + result: 5, + predicate: ({ $index }) => $index !== 5 + }, + { + title: 'remove this item (fixRight)', + min: 1, + max: 10, + fixRight: true, + index: 5, + result: 6, + predicate: ({ $index }) => $index !== 5 + }, + { + title: 'remove this, 1 before and 1 after', + min: 1, + max: 10, + fixRight: false, + index: 5, + result: 4, + predicate: ({ $index }) => !($index >= 4 && $index <= 6) + }, + { + title: 'remove this, 1 before and 1 after (fixRight)', + min: 1, + max: 10, + fixRight: true, + index: 5, + result: 7, + predicate: ({ $index }) => !($index >= 4 && $index <= 6) + }, + { + title: 'remove this and all after', + min: 1, + max: 10, + fixRight: false, + index: 5, + result: 4, + predicate: ({ $index }) => !($index >= 5) + }, + { + title: 'remove this and all after (fixRight)', + min: 1, + max: 10, + fixRight: true, + index: 5, + result: 10, + predicate: ({ $index }) => !($index >= 5) + }, + { + title: 'remove this and all before', + min: 1, + max: 10, + fixRight: false, + index: 5, + result: 1, + predicate: ({ $index }) => !($index <= 5) + }, + { + title: 'remove this and all before (fixRight)', + min: 1, + max: 10, + fixRight: true, + index: 5, + result: 6, + predicate: ({ $index }) => !($index <= 5) + }, + { + title: 'remove all', + min: 1, + max: 10, + fixRight: false, + index: 5, + result: NaN, + predicate: () => [] + } + ].forEach(config => it(config.title, checkIndexTrackingOnUpdate(config)))); describe('Index tracking on Update when flushing the Buffer', () => { - describe('Cache is present', () => [ - { - title: 'cache matches buffer', - min: -9, max: 20, minCache: -9, maxCache: 20, fixRight: false, index: 5, result: NaN, - }, { - title: 'simple flush', - min: 1, max: 10, minCache: -9, maxCache: 20, fixRight: false, index: 5, result: 1, - }, { - title: 'simple flush (fixRight)', - min: 1, max: 10, minCache: -9, maxCache: 20, fixRight: true, index: 5, result: 11, - }, { - title: 'flush when eof', - min: 1, max: 20, minCache: -9, maxCache: 20, fixRight: false, index: 5, result: 0, - }, { - title: 'flush when eof (fixRight)', - min: 1, max: 20, minCache: -9, maxCache: 20, fixRight: true, index: 5, result: 20, - }, { - title: 'flush when bof', - min: -9, max: 10, minCache: -9, maxCache: 20, fixRight: false, index: 5, result: -9, - }, { - title: 'flush when bof (fixRight)', - min: -9, max: 10, minCache: -9, maxCache: 20, fixRight: true, index: 5, result: 11, - }] - .forEach(config => it(config.title, checkIndexTrackingOnUpdate({ - ...config, - predicate: () => [] - }))) - ); + describe('Cache is present', () => + [ + { + title: 'cache matches buffer', + min: -9, + max: 20, + minCache: -9, + maxCache: 20, + fixRight: false, + index: 5, + result: NaN + }, + { + title: 'simple flush', + min: 1, + max: 10, + minCache: -9, + maxCache: 20, + fixRight: false, + index: 5, + result: 1 + }, + { + title: 'simple flush (fixRight)', + min: 1, + max: 10, + minCache: -9, + maxCache: 20, + fixRight: true, + index: 5, + result: 11 + }, + { + title: 'flush when eof', + min: 1, + max: 20, + minCache: -9, + maxCache: 20, + fixRight: false, + index: 5, + result: 0 + }, + { + title: 'flush when eof (fixRight)', + min: 1, + max: 20, + minCache: -9, + maxCache: 20, + fixRight: true, + index: 5, + result: 20 + }, + { + title: 'flush when bof', + min: -9, + max: 10, + minCache: -9, + maxCache: 20, + fixRight: false, + index: 5, + result: -9 + }, + { + title: 'flush when bof (fixRight)', + min: -9, + max: 10, + minCache: -9, + maxCache: 20, + fixRight: true, + index: 5, + result: 11 + } + ].forEach(config => + it( + config.title, + checkIndexTrackingOnUpdate({ + ...config, + predicate: () => [] + }) + ) + )); - describe('No Cache behind, but absolute borders are present', () => [ - { - title: 'borders matches buffer', - min: -9, max: 20, absMin: -9, absMax: 20, fixRight: false, index: 5, result: NaN, - }, { - title: 'simple flush', - min: 1, max: 10, absMin: -9, absMax: 20, fixRight: false, index: 5, result: 1, - }, { - title: 'simple flush (fixRight)', - min: 1, max: 10, absMin: -9, absMax: 20, fixRight: true, index: 5, result: 11, - }, { - title: 'no right border', - min: 1, max: 10, absMin: -9, absMax: 10, fixRight: false, index: 5, result: 0, - }, { - title: 'no right border (fixRight)', - min: 1, max: 10, absMin: -9, absMax: 10, fixRight: true, index: 5, result: 10, - }, { - title: 'no left border', - min: 1, max: 10, absMin: 1, absMax: 20, fixRight: false, index: 5, result: 1, - }, { - title: 'no left border (fixRight)', - min: 1, max: 10, absMin: 1, absMax: 20, fixRight: true, index: 5, result: 11, - }] - .forEach(config => it(config.title, checkIndexTrackingOnUpdate({ - ...config, - predicate: () => [] - }))) - ); + describe('No Cache behind, but absolute borders are present', () => + [ + { + title: 'borders matches buffer', + min: -9, + max: 20, + absMin: -9, + absMax: 20, + fixRight: false, + index: 5, + result: NaN + }, + { + title: 'simple flush', + min: 1, + max: 10, + absMin: -9, + absMax: 20, + fixRight: false, + index: 5, + result: 1 + }, + { + title: 'simple flush (fixRight)', + min: 1, + max: 10, + absMin: -9, + absMax: 20, + fixRight: true, + index: 5, + result: 11 + }, + { + title: 'no right border', + min: 1, + max: 10, + absMin: -9, + absMax: 10, + fixRight: false, + index: 5, + result: 0 + }, + { + title: 'no right border (fixRight)', + min: 1, + max: 10, + absMin: -9, + absMax: 10, + fixRight: true, + index: 5, + result: 10 + }, + { + title: 'no left border', + min: 1, + max: 10, + absMin: 1, + absMax: 20, + fixRight: false, + index: 5, + result: 1 + }, + { + title: 'no left border (fixRight)', + min: 1, + max: 10, + absMin: 1, + absMax: 20, + fixRight: true, + index: 5, + result: 11 + } + ].forEach(config => + it( + config.title, + checkIndexTrackingOnUpdate({ + ...config, + predicate: () => [] + }) + ) + )); }); - describe('Insert', () => [ - { - title: '[skip] inside buffer, forward', - items: ['A', 'B'], index: -1, direction: Direction.forward, fixRight: false - }, { - title: '[skip] inside buffer, forward + fixRight', - items: ['A', 'B'], index: -1, direction: Direction.forward, fixRight: true - }, { - title: 'at the left border of buffer, backward', - items: ['A', 'B'], index: -1, direction: Direction.backward, fixRight: false, - result: { list: [{ 1: -1 }, { 2: 0 }, { 3: 1 }], absMin: -100, absMax: 102 } - }, { - title: '[skip] inside buffer, backward', - items: ['A', 'B'], index: 1, direction: Direction.backward, fixRight: false - }, { - title: '[skip] inside buffer, backward + fixRight', - items: ['A', 'B'], index: 1, direction: Direction.backward, fixRight: true - }, { - title: 'at the right border of buffer, forward', - items: ['A', 'B'], index: 1, direction: Direction.forward, fixRight: false, - result: { list: [{ '-1': -1 }, { 0: 0 }, { 1: 1 }], absMin: -100, absMax: 102 } - }, { - title: 'out of the left border, backward', - items: ['A', 'B'], index: -50, direction: Direction.backward, fixRight: false, - result: { list: [{ 1: -1 }, { 2: 0 }, { 3: 1 }], absMin: -100, absMax: 102 } - }, { - title: 'out of the left border, backward + fixRight', - items: ['A', 'B'], index: -50, direction: Direction.backward, fixRight: true, - result: { list: [{ '-1': -1 }, { 0: 0 }, { 1: 1 }], absMin: -102, absMax: 100 } - }, { - title: 'out of the left border, forward', - items: ['A', 'B'], index: -50, direction: Direction.forward, fixRight: false, - result: { list: [{ 1: -1 }, { 2: 0 }, { 3: 1 }], absMin: -100, absMax: 102 } - }, { - title: 'out of the left border, forward + fixRight', - items: ['A', 'B'], index: -50, direction: Direction.forward, fixRight: true, - result: { list: [{ '-1': -1 }, { 0: 0 }, { 1: 1 }], absMin: -102, absMax: 100 } - }, { - title: 'out of the right border, backward', - items: ['A', 'B'], index: 50, direction: Direction.backward, fixRight: false, - result: { list: [{ '-1': -1 }, { 0: 0 }, { 1: 1 }], absMin: -100, absMax: 102 } - }, { - title: 'out of the right border, backward + fixRight', - items: ['A', 'B'], index: 50, direction: Direction.backward, fixRight: true, - result: { list: [{ '-3': -1 }, { '-2': 0 }, { '-1': 1 }], absMin: -102, absMax: 100 } - }, { - title: 'out of the right border, forward', - items: ['A', 'B'], index: 50, direction: Direction.forward, fixRight: false, - result: { list: [{ '-1': -1 }, { 0: 0 }, { 1: 1 }], absMin: -100, absMax: 102 } - }, { - title: 'out of the right border, forward + fixRight', - items: ['A', 'B'], index: 50, direction: Direction.forward, fixRight: true, - result: { list: [{ '-3': -1 }, { '-2': 0 }, { '-1': 1 }], absMin: -102, absMax: 100 } - }, { - title: 'on the right border, forward + fixRight', - items: ['A', 'B'], index: 100, direction: Direction.forward, fixRight: true, - result: { list: [{ '-3': -1 }, { '-2': 0 }, { '-1': 1 }], absMin: -102, absMax: 100 } - }, { - title: 'on the left border, backward', - items: ['A', 'B'], index: -100, direction: Direction.backward, fixRight: false, - result: { list: [{ 1: -1 }, { 2: 0 }, { 3: 1 }], absMin: -100, absMax: 102 } - }, { - title: '[skip] out of the right abs border, forward', - items: ['A', 'B'], index: 150, direction: Direction.forward, fixRight: false - }, { - title: '[skip] on the right abs border, forward', - items: ['A', 'B'], index: 101, direction: Direction.forward, fixRight: false - }, { - title: '[skip] on the right abs border, backward', - items: ['A', 'B'], index: 101, direction: Direction.backward, fixRight: false - }, { - title: '[skip] out of the right abs border, forward + fixRight', - items: ['A', 'B'], index: 150, direction: Direction.forward, fixRight: true - }, { - title: '[skip] on the right abs border, forward + fixRight', - items: ['A', 'B'], index: 101, direction: Direction.forward, fixRight: true - }, { - title: '[skip] on the right abs border, backward + fixRight', - items: ['A', 'B'], index: 101, direction: Direction.backward, fixRight: true - }, { - title: '[skip] out of the left abs border, forward', - items: ['A', 'B'], index: -150, direction: Direction.forward, fixRight: false - }, { - title: '[skip] on the left abs border, forward', - items: ['A', 'B'], index: -101, direction: Direction.forward, fixRight: false - }, { - title: '[skip] on the left abs border, backward', - items: ['A', 'B'], index: -101, direction: Direction.backward, fixRight: false - }, { - title: '[skip] out of the left abs border, forward + fixRight', - items: ['A', 'B'], index: -150, direction: Direction.forward, fixRight: true - }, { - title: '[skip] on the left abs border, forward + fixRight', - items: ['A', 'B'], index: -101, direction: Direction.forward, fixRight: true - }, { - title: '[skip] on the left abs border, backward + fixRight', - items: ['A', 'B'], index: -101, direction: Direction.backward, fixRight: true - }] - .map(c => ({ ...c, min: -1, max: 1, absMin: -100, absMax: 100 })) - .forEach(config => it(config.title, checkInsert(config as BufferInsertConfig))) - ); - + describe('Insert', () => + [ + { + title: '[skip] inside buffer, forward', + items: ['A', 'B'], + index: -1, + direction: Direction.forward, + fixRight: false + }, + { + title: '[skip] inside buffer, forward + fixRight', + items: ['A', 'B'], + index: -1, + direction: Direction.forward, + fixRight: true + }, + { + title: 'at the left border of buffer, backward', + items: ['A', 'B'], + index: -1, + direction: Direction.backward, + fixRight: false, + result: { + list: [{ 1: -1 }, { 2: 0 }, { 3: 1 }], + absMin: -100, + absMax: 102 + } + }, + { + title: '[skip] inside buffer, backward', + items: ['A', 'B'], + index: 1, + direction: Direction.backward, + fixRight: false + }, + { + title: '[skip] inside buffer, backward + fixRight', + items: ['A', 'B'], + index: 1, + direction: Direction.backward, + fixRight: true + }, + { + title: 'at the right border of buffer, forward', + items: ['A', 'B'], + index: 1, + direction: Direction.forward, + fixRight: false, + result: { + list: [{ '-1': -1 }, { 0: 0 }, { 1: 1 }], + absMin: -100, + absMax: 102 + } + }, + { + title: 'out of the left border, backward', + items: ['A', 'B'], + index: -50, + direction: Direction.backward, + fixRight: false, + result: { + list: [{ 1: -1 }, { 2: 0 }, { 3: 1 }], + absMin: -100, + absMax: 102 + } + }, + { + title: 'out of the left border, backward + fixRight', + items: ['A', 'B'], + index: -50, + direction: Direction.backward, + fixRight: true, + result: { + list: [{ '-1': -1 }, { 0: 0 }, { 1: 1 }], + absMin: -102, + absMax: 100 + } + }, + { + title: 'out of the left border, forward', + items: ['A', 'B'], + index: -50, + direction: Direction.forward, + fixRight: false, + result: { + list: [{ 1: -1 }, { 2: 0 }, { 3: 1 }], + absMin: -100, + absMax: 102 + } + }, + { + title: 'out of the left border, forward + fixRight', + items: ['A', 'B'], + index: -50, + direction: Direction.forward, + fixRight: true, + result: { + list: [{ '-1': -1 }, { 0: 0 }, { 1: 1 }], + absMin: -102, + absMax: 100 + } + }, + { + title: 'out of the right border, backward', + items: ['A', 'B'], + index: 50, + direction: Direction.backward, + fixRight: false, + result: { + list: [{ '-1': -1 }, { 0: 0 }, { 1: 1 }], + absMin: -100, + absMax: 102 + } + }, + { + title: 'out of the right border, backward + fixRight', + items: ['A', 'B'], + index: 50, + direction: Direction.backward, + fixRight: true, + result: { + list: [{ '-3': -1 }, { '-2': 0 }, { '-1': 1 }], + absMin: -102, + absMax: 100 + } + }, + { + title: 'out of the right border, forward', + items: ['A', 'B'], + index: 50, + direction: Direction.forward, + fixRight: false, + result: { + list: [{ '-1': -1 }, { 0: 0 }, { 1: 1 }], + absMin: -100, + absMax: 102 + } + }, + { + title: 'out of the right border, forward + fixRight', + items: ['A', 'B'], + index: 50, + direction: Direction.forward, + fixRight: true, + result: { + list: [{ '-3': -1 }, { '-2': 0 }, { '-1': 1 }], + absMin: -102, + absMax: 100 + } + }, + { + title: 'on the right border, forward + fixRight', + items: ['A', 'B'], + index: 100, + direction: Direction.forward, + fixRight: true, + result: { + list: [{ '-3': -1 }, { '-2': 0 }, { '-1': 1 }], + absMin: -102, + absMax: 100 + } + }, + { + title: 'on the left border, backward', + items: ['A', 'B'], + index: -100, + direction: Direction.backward, + fixRight: false, + result: { + list: [{ 1: -1 }, { 2: 0 }, { 3: 1 }], + absMin: -100, + absMax: 102 + } + }, + { + title: '[skip] out of the right abs border, forward', + items: ['A', 'B'], + index: 150, + direction: Direction.forward, + fixRight: false + }, + { + title: '[skip] on the right abs border, forward', + items: ['A', 'B'], + index: 101, + direction: Direction.forward, + fixRight: false + }, + { + title: '[skip] on the right abs border, backward', + items: ['A', 'B'], + index: 101, + direction: Direction.backward, + fixRight: false + }, + { + title: '[skip] out of the right abs border, forward + fixRight', + items: ['A', 'B'], + index: 150, + direction: Direction.forward, + fixRight: true + }, + { + title: '[skip] on the right abs border, forward + fixRight', + items: ['A', 'B'], + index: 101, + direction: Direction.forward, + fixRight: true + }, + { + title: '[skip] on the right abs border, backward + fixRight', + items: ['A', 'B'], + index: 101, + direction: Direction.backward, + fixRight: true + }, + { + title: '[skip] out of the left abs border, forward', + items: ['A', 'B'], + index: -150, + direction: Direction.forward, + fixRight: false + }, + { + title: '[skip] on the left abs border, forward', + items: ['A', 'B'], + index: -101, + direction: Direction.forward, + fixRight: false + }, + { + title: '[skip] on the left abs border, backward', + items: ['A', 'B'], + index: -101, + direction: Direction.backward, + fixRight: false + }, + { + title: '[skip] out of the left abs border, forward + fixRight', + items: ['A', 'B'], + index: -150, + direction: Direction.forward, + fixRight: true + }, + { + title: '[skip] on the left abs border, forward + fixRight', + items: ['A', 'B'], + index: -101, + direction: Direction.forward, + fixRight: true + }, + { + title: '[skip] on the left abs border, backward + fixRight', + items: ['A', 'B'], + index: -101, + direction: Direction.backward, + fixRight: true + } + ] + .map(c => ({ ...c, min: -1, max: 1, absMin: -100, absMax: 100 })) + .forEach(config => + it(config.title, checkInsert(config as BufferInsertConfig)) + )); }); diff --git a/tests/unit/cache.spec.ts b/tests/unit/cache.spec.ts index 9688e09d..101a9ef0 100644 --- a/tests/unit/cache.spec.ts +++ b/tests/unit/cache.spec.ts @@ -2,7 +2,11 @@ import { Cache } from '../../src/classes/buffer/cache'; import { Item } from '../../src/classes/item'; import { Data, Id, IndexIdList, IndexSizeList } from './misc/types'; -import { generateBufferItem, generateBufferItems, generateItem } from './misc/items'; +import { + generateBufferItem, + generateBufferItems, + generateItem +} from './misc/items'; import { Direction, SizeStrategy } from '../../src/inputs'; interface ActionCacheConfig { @@ -30,8 +34,10 @@ const make = (list: IndexIdList): Item[] => const checkCache = (cache: Cache, config: ActionCacheConfig) => { if (config.debug) { - console.log(Array.from((cache as unknown as { items: Map> }).items).map( - ([_, item]) => item.$index + ': ' + item.data.id) + console.log( + Array.from( + (cache as unknown as { items: Map> }).items + ).map(([_, item]) => item.$index + ': ' + item.data.id) ); } const list = config.result; @@ -52,9 +58,9 @@ const checkCache = (cache: Cache, config: ActionCacheConfig) => { }; describe('Cache Spec', () => { - describe('Remove', () => { - const MIN = 1, COUNT = 5; + const MIN = 1, + COUNT = 5; const items = generateBufferItems(MIN, COUNT); let cache: Cache; @@ -127,7 +133,8 @@ describe('Cache Spec', () => { }); describe('Update', () => { - const MIN = 1, COUNT = 7; + const MIN = 1, + COUNT = 7; const items = generateBufferItems(MIN, COUNT); const subset = items.slice(2, 5); // [3, 4, 5] let cache: Cache; @@ -137,110 +144,222 @@ describe('Cache Spec', () => { items.forEach(item => cache.add(item)); }); - const updateConfigList: ActionCacheConfig[] = [{ - title: 'should not change', - action: () => { - const after: IndexIdList = [{ 3: 3 }, { 4: 4 }, { 5: 5 }]; - cache.updateSubset(subset, make(after)); - }, - result: [{ 1: 1 }, { 2: 2 }, { 3: 3 }, { 4: 4 }, { 5: 5 }, { 6: 6 }, { 7: 7 }] - }, { - title: 'should clear', - action: () => { - cache.updateSubset(subset, []); + const updateConfigList: ActionCacheConfig[] = [ + { + title: 'should not change', + action: () => { + const after: IndexIdList = [{ 3: 3 }, { 4: 4 }, { 5: 5 }]; + cache.updateSubset(subset, make(after)); + }, + result: [ + { 1: 1 }, + { 2: 2 }, + { 3: 3 }, + { 4: 4 }, + { 5: 5 }, + { 6: 6 }, + { 7: 7 } + ] }, - result: [{ 1: 1 }, { 2: 2 }, { 3: 6 }, { 4: 7 }] - }, { - title: 'should clear (fixRight)', - action: () => { - cache.updateSubset(subset, [], true); + { + title: 'should clear', + action: () => { + cache.updateSubset(subset, []); + }, + result: [{ 1: 1 }, { 2: 2 }, { 3: 6 }, { 4: 7 }] }, - result: [{ 4: 1 }, { 5: 2 }, { 6: 6 }, { 7: 7 }] - }, { - title: 'should remove left', - action: () => { - const after: IndexIdList = [{ 3: 4 }, { 4: 5 }]; - cache.updateSubset(subset, make(after)); + { + title: 'should clear (fixRight)', + action: () => { + cache.updateSubset(subset, [], true); + }, + result: [{ 4: 1 }, { 5: 2 }, { 6: 6 }, { 7: 7 }] }, - result: [{ 1: 1 }, { 2: 2 }, { 3: 4 }, { 4: 5 }, { 5: 6 }, { 6: 7 }] - }, { - title: 'should remove left (fixRight)', - action: () => { - const after: IndexIdList = [{ 4: 4 }, { 5: 5 }]; - cache.updateSubset(subset, make(after)); + { + title: 'should remove left', + action: () => { + const after: IndexIdList = [{ 3: 4 }, { 4: 5 }]; + cache.updateSubset(subset, make(after)); + }, + result: [{ 1: 1 }, { 2: 2 }, { 3: 4 }, { 4: 5 }, { 5: 6 }, { 6: 7 }] }, - result: [{ 2: 1 }, { 3: 2 }, { 4: 4 }, { 5: 5 }, { 6: 6 }, { 7: 7 }] - }, { - title: 'should remove right', - action: () => { - const after: IndexIdList = [{ 3: 3 }, { 4: 4 }]; - cache.updateSubset(subset, make(after)); + { + title: 'should remove left (fixRight)', + action: () => { + const after: IndexIdList = [{ 4: 4 }, { 5: 5 }]; + cache.updateSubset(subset, make(after)); + }, + result: [{ 2: 1 }, { 3: 2 }, { 4: 4 }, { 5: 5 }, { 6: 6 }, { 7: 7 }] }, - result: [{ 1: 1 }, { 2: 2 }, { 3: 3 }, { 4: 4 }, { 5: 6 }, { 6: 7 }] - }, { - title: 'should remove right (fixRight)', - action: () => { - const after: IndexIdList = [{ 4: 3 }, { 5: 4 }]; - cache.updateSubset(subset, make(after)); + { + title: 'should remove right', + action: () => { + const after: IndexIdList = [{ 3: 3 }, { 4: 4 }]; + cache.updateSubset(subset, make(after)); + }, + result: [{ 1: 1 }, { 2: 2 }, { 3: 3 }, { 4: 4 }, { 5: 6 }, { 6: 7 }] }, - result: [{ 2: 1 }, { 3: 2 }, { 4: 3 }, { 5: 4 }, { 6: 6 }, { 7: 7 }] - }, { - title: 'should remove left & right', - action: () => { - const after = [{ 3: 4 }]; - cache.updateSubset(subset, make(after)); + { + title: 'should remove right (fixRight)', + action: () => { + const after: IndexIdList = [{ 4: 3 }, { 5: 4 }]; + cache.updateSubset(subset, make(after)); + }, + result: [{ 2: 1 }, { 3: 2 }, { 4: 3 }, { 5: 4 }, { 6: 6 }, { 7: 7 }] }, - result: [{ 1: 1 }, { 2: 2 }, { 3: 4 }, { 4: 6 }, { 5: 7 }] - }, { - title: 'should remove left & right (fixRight)', - action: () => { - const after = [{ 5: 4 }]; - cache.updateSubset(subset, make(after)); + { + title: 'should remove left & right', + action: () => { + const after = [{ 3: 4 }]; + cache.updateSubset(subset, make(after)); + }, + result: [{ 1: 1 }, { 2: 2 }, { 3: 4 }, { 4: 6 }, { 5: 7 }] }, - result: [{ 3: 1 }, { 4: 2 }, { 5: 4 }, { 6: 6 }, { 7: 7 }] - }, { - title: 'should insert left', - action: () => { - const after: IndexIdList = [{ 3: 'xxx' }, { 4: 3 }, { 5: 4 }, { 6: 5 }]; - cache.updateSubset(subset, make(after)); + { + title: 'should remove left & right (fixRight)', + action: () => { + const after = [{ 5: 4 }]; + cache.updateSubset(subset, make(after)); + }, + result: [{ 3: 1 }, { 4: 2 }, { 5: 4 }, { 6: 6 }, { 7: 7 }] }, - result: [{ 1: 1 }, { 2: 2 }, { 3: 'xxx' }, { 4: 3 }, { 5: 4 }, { 6: 5 }, { 7: 6 }, { 8: 7 }] - }, { - title: 'should insert left (fixRight)', - action: () => { - const after: IndexIdList = [{ 2: 'xxx' }, { 3: 3 }, { 4: 4 }, { 5: 5 }]; - cache.updateSubset(subset, make(after)); + { + title: 'should insert left', + action: () => { + const after: IndexIdList = [ + { 3: 'xxx' }, + { 4: 3 }, + { 5: 4 }, + { 6: 5 } + ]; + cache.updateSubset(subset, make(after)); + }, + result: [ + { 1: 1 }, + { 2: 2 }, + { 3: 'xxx' }, + { 4: 3 }, + { 5: 4 }, + { 6: 5 }, + { 7: 6 }, + { 8: 7 } + ] }, - result: [{ 0: 1 }, { 1: 2 }, { 2: 'xxx' }, { 3: 3 }, { 4: 4 }, { 5: 5 }, { 6: 6 }, { 7: 7 }] - }, { - title: 'should insert right', - action: () => { - const after: IndexIdList = [{ 3: 3 }, { 4: 4 }, { 5: 5 }, { 6: 'xxx' }]; - cache.updateSubset(subset, make(after)); + { + title: 'should insert left (fixRight)', + action: () => { + const after: IndexIdList = [ + { 2: 'xxx' }, + { 3: 3 }, + { 4: 4 }, + { 5: 5 } + ]; + cache.updateSubset(subset, make(after)); + }, + result: [ + { 0: 1 }, + { 1: 2 }, + { 2: 'xxx' }, + { 3: 3 }, + { 4: 4 }, + { 5: 5 }, + { 6: 6 }, + { 7: 7 } + ] }, - result: [{ 1: 1 }, { 2: 2 }, { 3: 3 }, { 4: 4 }, { 5: 5 }, { 6: 'xxx' }, { 7: 6 }, { 8: 7 }] - }, { - title: 'should insert right (fixRight)', - action: () => { - const after: IndexIdList = [{ 2: 3 }, { 3: 4 }, { 4: 5 }, { 5: 'xxx' }]; - cache.updateSubset(subset, make(after)); + { + title: 'should insert right', + action: () => { + const after: IndexIdList = [ + { 3: 3 }, + { 4: 4 }, + { 5: 5 }, + { 6: 'xxx' } + ]; + cache.updateSubset(subset, make(after)); + }, + result: [ + { 1: 1 }, + { 2: 2 }, + { 3: 3 }, + { 4: 4 }, + { 5: 5 }, + { 6: 'xxx' }, + { 7: 6 }, + { 8: 7 } + ] }, - result: [{ 0: 1 }, { 1: 2 }, { 2: 3 }, { 3: 4 }, { 4: 5 }, { 5: 'xxx' }, { 6: 6 }, { 7: 7 }] - }, { - title: 'should insert left & right', - action: () => { - const after: IndexIdList = [{ 3: 'xxx' }, { 4: 3 }, { 5: 4 }, { 6: 5 }, { 7: 'xxx' }]; - cache.updateSubset(subset, make(after)); + { + title: 'should insert right (fixRight)', + action: () => { + const after: IndexIdList = [ + { 2: 3 }, + { 3: 4 }, + { 4: 5 }, + { 5: 'xxx' } + ]; + cache.updateSubset(subset, make(after)); + }, + result: [ + { 0: 1 }, + { 1: 2 }, + { 2: 3 }, + { 3: 4 }, + { 4: 5 }, + { 5: 'xxx' }, + { 6: 6 }, + { 7: 7 } + ] }, - result: [{ 1: 1 }, { 2: 2 }, { 3: 'xxx' }, { 4: 3 }, { 5: 4 }, { 6: 5 }, { 7: 'xxx' }, { 8: 6 }, { 9: 7 }] - }, { - title: 'should insert left & right (fixRight)', - action: () => { - const after: IndexIdList = [{ 1: 'x' }, { 2: 3 }, { 3: 4 }, { 4: 5 }, { 5: 'y' }]; - cache.updateSubset(subset, make(after)); + { + title: 'should insert left & right', + action: () => { + const after: IndexIdList = [ + { 3: 'xxx' }, + { 4: 3 }, + { 5: 4 }, + { 6: 5 }, + { 7: 'xxx' } + ]; + cache.updateSubset(subset, make(after)); + }, + result: [ + { 1: 1 }, + { 2: 2 }, + { 3: 'xxx' }, + { 4: 3 }, + { 5: 4 }, + { 6: 5 }, + { 7: 'xxx' }, + { 8: 6 }, + { 9: 7 } + ] }, - result: [{ '-1': 1 }, { 0: 2 }, { 1: 'x' }, { 2: 3 }, { 3: 4 }, { 4: 5 }, { 5: 'y' }, { 6: 6 }, { 7: 7 }] - }]; + { + title: 'should insert left & right (fixRight)', + action: () => { + const after: IndexIdList = [ + { 1: 'x' }, + { 2: 3 }, + { 3: 4 }, + { 4: 5 }, + { 5: 'y' } + ]; + cache.updateSubset(subset, make(after)); + }, + result: [ + { '-1': 1 }, + { 0: 2 }, + { 1: 'x' }, + { 2: 3 }, + { 3: 4 }, + { 4: 5 }, + { 5: 'y' }, + { 6: 6 }, + { 7: 7 } + ] + } + ]; updateConfigList.forEach(config => it(config.title, () => { @@ -256,19 +375,34 @@ describe('Cache Spec', () => { sizeStrategy: SizeStrategy.Average }; - const prepareAverage = ( - { length, toRemove, toAdd }: { length: number, toRemove?: Id[], toAdd?: IndexSizeList } - ) => { - const beforeTotal = Array.from({ length }).reduce((acc: number, j, i) => acc + i + 1, 0); + const prepareAverage = ({ + length, + toRemove, + toAdd + }: { + length: number; + toRemove?: Id[]; + toAdd?: IndexSizeList; + }) => { + const beforeTotal = Array.from({ length }).reduce( + (acc: number, j, i) => acc + i + 1, + 0 + ); const before = Math.round(beforeTotal / length); let after; if (toRemove) { after = Math.round( Array.from({ length }).reduce( - (acc: number, j, i) => acc + (toRemove.includes(i + 1) ? 0 : (i + 1)), 0 - ) / (length - toRemove.length)); + (acc: number, j, i) => acc + (toRemove.includes(i + 1) ? 0 : i + 1), + 0 + ) / + (length - toRemove.length) + ); } else if (toAdd) { - let countNew = 0, sizeNew = 0, sizeOld = 0, sizeOldBefore = 0; + let countNew = 0, + sizeNew = 0, + sizeOld = 0, + sizeOldBefore = 0; toAdd.forEach(r => { const key = Number(Object.keys(r)[0]); const size = r[key]; @@ -280,9 +414,15 @@ describe('Cache Spec', () => { sizeOldBefore += key; } }); - after = Math.round((beforeTotal + sizeNew + sizeOld - sizeOldBefore) / (length + countNew)); + after = Math.round( + (beforeTotal + sizeNew + sizeOld - sizeOldBefore) / + (length + countNew) + ); } - const cache = new Cache(averageSizeSettings as never, loggerMock as never); + const cache = new Cache( + averageSizeSettings as never, + loggerMock as never + ); const items = generateBufferItems(1, length); items.forEach(item => { item.toRemove = !!toRemove?.includes(item.$index); @@ -336,9 +476,23 @@ describe('Cache Spec', () => { it('should maintain average size on remove via update', () => { const toRemove = [5, 8, 10]; - const { items, cache, average } = prepareAverage({ length: 10, toRemove }); - const listAfter: IndexIdList = [{ 1: 1 }, { 2: 2 }, { 3: 3 }, { 4: 4 }, { 5: 6 }, { 6: 7 }, { 7: 9 }]; - const subset = items.slice(toRemove[0], toRemove[toRemove.length - 1] + 1); + const { items, cache, average } = prepareAverage({ + length: 10, + toRemove + }); + const listAfter: IndexIdList = [ + { 1: 1 }, + { 2: 2 }, + { 3: 3 }, + { 4: 4 }, + { 5: 6 }, + { 6: 7 }, + { 7: 9 } + ]; + const subset = items.slice( + toRemove[0], + toRemove[toRemove.length - 1] + 1 + ); expect(cache.getDefaultSize()).toBe(average.before); @@ -349,26 +503,37 @@ describe('Cache Spec', () => { it('should maintain average size on remove via update (explicit)', () => { const INDEX_TO_REMOVE = 10; - const cache = new Cache(averageSizeSettings as never, loggerMock as never); + const cache = new Cache( + averageSizeSettings as never, + loggerMock as never + ); const items = generateBufferItems(1, 15); items.forEach(item => { - item.size = item.data.size = (item.$index === INDEX_TO_REMOVE ? 100 : 20); + item.size = item.data.size = + item.$index === INDEX_TO_REMOVE ? 100 : 20; item.toRemove = item.$index === INDEX_TO_REMOVE; cache.add(item); }); cache.recalculateDefaultSize(); expect(cache.getDefaultSize()).toBe(25); - const before = items.filter(({ $index }) => [10, 11, 12, 13, 14, 15].includes($index)); - const after = make([{ 10: 11 }, { 11: 12 }, { 12: 13 }, { 13: 14 }, { 14: 15 }]); - after.forEach(item => item.size = item.data.size = 20); + const before = items.filter(({ $index }) => + [10, 11, 12, 13, 14, 15].includes($index) + ); + const after = make([ + { 10: 11 }, + { 11: 12 }, + { 12: 13 }, + { 13: 14 }, + { 14: 15 } + ]); + after.forEach(item => (item.size = item.data.size = 20)); cache.updateSubset(before, after); cache.recalculateDefaultSize(); expect(cache.getDefaultSize()).toBe(20); }); }); - }); interface ICheckDefaultSize { @@ -382,13 +547,22 @@ describe('Cache Spec', () => { } const checkDefaultSize = ({ - sizeStrategy, cacheSize, setItemSize, updateCache, itemSize, before, after + sizeStrategy, + cacheSize, + setItemSize, + updateCache, + itemSize, + before, + after }: ICheckDefaultSize) => { - const cache = new Cache({ - ...settings, - itemSize, - sizeStrategy - } as never, loggerMock as never); + const cache = new Cache( + { + ...settings, + itemSize, + sizeStrategy + } as never, + loggerMock as never + ); const items = generateBufferItems(1, cacheSize); items.forEach(item => setItemSize(item, cache.itemSize) && cache.add(item)); cache.recalculateDefaultSize(); @@ -400,19 +574,23 @@ describe('Cache Spec', () => { }; describe('Frequent size', () => { - - const checkFrequentOnInit = (list: IndexSizeList, before: number, after?: number) => + const checkFrequentOnInit = ( + list: IndexSizeList, + before: number, + after?: number + ) => checkDefaultSize({ sizeStrategy: SizeStrategy.Frequent, cacheSize: list.length, itemSize: before, setItemSize: (item, defaultSize) => - item.size = list.reduce((a, i) => - item.$index === Number(Object.keys(i)[0]) - ? Object.values(i)[0] - : a, + (item.size = list.reduce( + (a, i) => + item.$index === Number(Object.keys(i)[0]) + ? Object.values(i)[0] + : a, defaultSize as number - ), + )), updateCache: () => null, before, after @@ -423,11 +601,12 @@ describe('Cache Spec', () => { sizeStrategy: SizeStrategy.Frequent, cacheSize: 10, itemSize: 12, - setItemSize: item => item.size = 12, - updateCache: cache => toAdd.forEach(r => { - const key = Number(Object.keys(r)[0]); - cache.add({ $index: key, size: r[key], data: {} } as Item); - }), + setItemSize: item => (item.size = 12), + updateCache: cache => + toAdd.forEach(r => { + const key = Number(Object.keys(r)[0]); + cache.add({ $index: key, size: r[key], data: {} } as Item); + }), after: newSize }); @@ -436,145 +615,180 @@ describe('Cache Spec', () => { sizeStrategy: SizeStrategy.Frequent, cacheSize: 10, itemSize: 12, - setItemSize: item => item.size = item.$index % 2 === 0 ? 5 : 12, // 1 - 12, 2 - 5, ... + setItemSize: item => (item.size = item.$index % 2 === 0 ? 5 : 12), // 1 - 12, 2 - 5, ... updateCache: cache => cache.removeItems(toRemove, false), after: newSize }); describe('On init', () => { it('should set frequent', () => - checkFrequentOnInit([{ 1: 15 }, { 2: 16 }, { 3: 15 }], 15) - ); + checkFrequentOnInit([{ 1: 15 }, { 2: 16 }, { 3: 15 }], 15)); it('should set first', () => - checkFrequentOnInit([{ 1: 15 }, { 2: 16 }, { 3: 17 }], 15) - ); + checkFrequentOnInit([{ 1: 15 }, { 2: 16 }, { 3: 17 }], 15)); it('should set first frequent', () => - checkFrequentOnInit([{ 1: 15 }, { 2: 16 }, { 3: 15 }, { 4: 16 }], 15) - ); + checkFrequentOnInit([{ 1: 15 }, { 2: 16 }, { 3: 15 }, { 4: 16 }], 15)); }); describe('On add', () => { it('should increase frequent size on add (existed)', () => - checkFrequentOnAdd([{ 1: 20 }, { 2: 20 }, { 3: 20 }, { 4: 20 }, { 5: 20 }, { 6: 20 }], 20) - ); + checkFrequentOnAdd( + [{ 1: 20 }, { 2: 20 }, { 3: 20 }, { 4: 20 }, { 5: 20 }, { 6: 20 }], + 20 + )); it('should decrease frequent size on add (existed)', () => - checkFrequentOnAdd([{ 1: 5 }, { 2: 5 }, { 3: 5 }, { 4: 5 }, { 5: 5 }, { 6: 5 }], 5) - ); + checkFrequentOnAdd( + [{ 1: 5 }, { 2: 5 }, { 3: 5 }, { 4: 5 }, { 5: 5 }, { 6: 5 }], + 5 + )); it('should not change frequent size on add (existed, smaller)', () => - checkFrequentOnAdd([{ 1: 5 }, { 2: 5 }, { 3: 5 }, { 4: 5 }]) - ); + checkFrequentOnAdd([{ 1: 5 }, { 2: 5 }, { 3: 5 }, { 4: 5 }])); it('should not change frequent size on add (existed, bigger)', () => - checkFrequentOnAdd([{ 1: 25 }, { 2: 25 }, { 3: 25 }, { 4: 25 }]) - ); + checkFrequentOnAdd([{ 1: 25 }, { 2: 25 }, { 3: 25 }, { 4: 25 }])); it('should not change frequent size on add (new, less)', () => - checkFrequentOnAdd(Array.from({ length: 9 }).map((j, i) => ({ [100 + i]: 20 }))) - ); + checkFrequentOnAdd( + Array.from({ length: 9 }).map((j, i) => ({ [100 + i]: 20 })) + )); it('should not change frequent size on add (new, equal)', () => - checkFrequentOnAdd(Array.from({ length: 10 }).map((j, i) => ({ [100 + i]: 20 }))) - ); + checkFrequentOnAdd( + Array.from({ length: 10 }).map((j, i) => ({ [100 + i]: 20 })) + )); it('should change frequent size on add (new, more)', () => - checkFrequentOnAdd(Array.from({ length: 11 }).map((j, i) => ({ [100 + i]: 20 })), 20) - ); + checkFrequentOnAdd( + Array.from({ length: 11 }).map((j, i) => ({ [100 + i]: 20 })), + 20 + )); it('should change frequent size on add (new, equal, double)', () => - checkFrequentOnAdd([ - ...Array.from({ length: 11 }).map((j, i) => ({ [100 + i]: 20 })), - ...Array.from({ length: 11 }).map((j, i) => ({ [200 + i]: 30 })), - ], 20) - ); + checkFrequentOnAdd( + [ + ...Array.from({ length: 11 }).map((j, i) => ({ [100 + i]: 20 })), + ...Array.from({ length: 11 }).map((j, i) => ({ [200 + i]: 30 })) + ], + 20 + )); }); describe('On remove', () => { it('should not change frequent size on remove (empty)', () => - checkFrequentOnRemove([]) - ); + checkFrequentOnRemove([])); it('should change frequent size on remove (many)', () => - checkFrequentOnRemove([1, 2, 3, 4, 5], 5) - ); + checkFrequentOnRemove([1, 2, 3, 4, 5], 5)); it('should change frequent size on remove (single)', () => - checkFrequentOnRemove([1], 5) - ); + checkFrequentOnRemove([1], 5)); it('should not change frequent size on remove (many)', () => - checkFrequentOnRemove([2, 4, 6, 8, 10]) - ); + checkFrequentOnRemove([2, 4, 6, 8, 10])); it('should not change frequent size on remove (single)', () => - checkFrequentOnRemove([2]) - ); + checkFrequentOnRemove([2])); it('should not change frequent size on remove (all)', () => - checkFrequentOnRemove([1, 2, 3, 4, 5, 6, 7, 8, 9, 10]) - ); + checkFrequentOnRemove([1, 2, 3, 4, 5, 6, 7, 8, 9, 10])); }); }); describe('Constant size', () => { - - const checkConstantOnInit = (list: IndexSizeList, updateCache: (c) => void, itemSize: number) => + const checkConstantOnInit = ( + list: IndexSizeList, + updateCache: (c) => void, + itemSize: number + ) => checkDefaultSize({ sizeStrategy: SizeStrategy.Constant, cacheSize: list.length, itemSize, setItemSize: (item, defaultSize) => - item.size = list.reduce((a, i) => - item.$index === Number(Object.keys(i)[0]) - ? Object.values(i)[0] - : a, + (item.size = list.reduce( + (a, i) => + item.$index === Number(Object.keys(i)[0]) + ? Object.values(i)[0] + : a, defaultSize as number - ), + )), updateCache, before: itemSize || Object.values(list[0])[0] }); - const checkConstantOnRemove = (list: IndexSizeList, toRemove: number[], itemSize: number) => - checkConstantOnInit(list, cache => cache.removeItems(toRemove, false), itemSize); + const checkConstantOnRemove = ( + list: IndexSizeList, + toRemove: number[], + itemSize: number + ) => + checkConstantOnInit( + list, + cache => cache.removeItems(toRemove, false), + itemSize + ); - const checkConstantOnAdd = (list: IndexSizeList, toAdd: IndexSizeList, itemSize: number) => - checkConstantOnInit(list, cache => toAdd.forEach(r => { - const key = Number(Object.keys(r)[0]); - cache.add({ $index: key, size: r[key], data: {} } as Item); - }), itemSize); + const checkConstantOnAdd = ( + list: IndexSizeList, + toAdd: IndexSizeList, + itemSize: number + ) => + checkConstantOnInit( + list, + cache => + toAdd.forEach(r => { + const key = Number(Object.keys(r)[0]); + cache.add({ $index: key, size: r[key], data: {} } as Item); + }), + itemSize + ); [12, NaN].forEach(itemSize => { it('should not change default size on init', () => - checkConstantOnInit([{ 1: 15 }, { 2: 16 }, { 3: 15 }], () => null, itemSize) - ); + checkConstantOnInit( + [{ 1: 15 }, { 2: 16 }, { 3: 15 }], + () => null, + itemSize + )); it('should not change default size on init (2)', () => - checkConstantOnInit([{ 1: 15 }, { 2: 16 }, { 3: 17 }], () => null, itemSize) - ); + checkConstantOnInit( + [{ 1: 15 }, { 2: 16 }, { 3: 17 }], + () => null, + itemSize + )); it('should not change default size on remove (1)', () => - checkConstantOnRemove([{ 1: 5 }, { 2: 6 }, { 3: 7 }], [], itemSize) - ); + checkConstantOnRemove([{ 1: 5 }, { 2: 6 }, { 3: 7 }], [], itemSize)); it('should not change default size on remove (2)', () => - checkConstantOnRemove([{ 1: 10 }, { 2: 11 }, { 3: 12 }], [1], itemSize) - ); + checkConstantOnRemove( + [{ 1: 10 }, { 2: 11 }, { 3: 12 }], + [1], + itemSize + )); it('should not change default size on add (1)', () => - checkConstantOnAdd([{ 1: 5 }, { 2: 6 }], [{ 1: 5 }, { 2: 5 }, { 3: 5 }], itemSize) - ); + checkConstantOnAdd( + [{ 1: 5 }, { 2: 6 }], + [{ 1: 5 }, { 2: 5 }, { 3: 5 }], + itemSize + )); it('should not change default size on add (2)', () => - checkConstantOnAdd([{ 1: 10 }, { 2: 10 }], [{ 1: 5 }, { 2: 5 }, { 3: 5 }], itemSize) - ); + checkConstantOnAdd( + [{ 1: 10 }, { 2: 10 }], + [{ 1: 5 }, { 2: 5 }, { 3: 5 }], + itemSize + )); }); }); describe('Insert', () => { - const MIN = 1, COUNT = 4; + const MIN = 1, + COUNT = 4; const items = generateBufferItems(MIN, COUNT); let cache: Cache; @@ -583,88 +797,157 @@ describe('Cache Spec', () => { items.forEach(item => cache.add(item)); }); - const make = (idList: Id[]): Data[] => - idList.map(id => generateItem(id)); - - const insertConfigList: ActionCacheConfig[] = [{ - title: 'not insert, empty case', - action: () => cache.insertItems( - make([]), 3, Direction.backward, false - ), - result: [{ 1: 1 }, { 2: 2 }, { 3: 3 }, { 4: 4 }] - }, { - title: 'insert in existed range, before 3', - action: () => cache.insertItems( - make(['A', 'B']), 3, Direction.backward, false - ), - result: [{ 1: 1 }, { 2: 2 }, { 3: 'A' }, { 4: 'B' }, { 5: 3 }, { 6: 4 }] - }, { - title: 'insert in existed range, before 3 (fixRight)', - action: () => cache.insertItems( - make(['A', 'B']), 3, Direction.backward, true - ), - result: [{ '-1': 1 }, { 0: 2 }, { 1: 'A' }, { 2: 'B' }, { 3: 3 }, { 4: 4 }] - }, { - title: 'insert in existed range, after 3', - action: () => cache.insertItems( - make(['A', 'B']), 3, Direction.forward, false - ), - result: [{ 1: 1 }, { 2: 2 }, { 3: 3 }, { 4: 'A' }, { 5: 'B' }, { 6: 4 }] - }, { - title: 'insert in existed range, after 3', - action: () => cache.insertItems( - make(['A', 'B']), 3, Direction.forward, true - ), - result: [{ '-1': 1 }, { 0: 2 }, { 1: 3 }, { 2: 'A' }, { 3: 'B' }, { 4: 4 }] - }, { - title: 'insert out of existed range, before 10', - action: () => cache.insertItems( - make(['A', 'B']), 10, Direction.backward, false - ), - result: [{ 1: 1 }, { 2: 2 }, { 3: 3 }, { 4: 4 }, { 10: 'A' }, { 11: 'B' }] - }, { - title: 'insert out of existed range, before 10 (fixRight)', - action: () => cache.insertItems( - make(['A', 'B']), 10, Direction.backward, true - ), - result: [{ '-1': 1 }, { 0: 2 }, { 1: 3 }, { 2: 4 }, { 8: 'A' }, { 9: 'B' }] - }, { - title: 'insert out of existed range, after 10', - action: () => cache.insertItems( - make(['A', 'B']), 10, Direction.forward, false - ), - result: [{ 1: 1 }, { 2: 2 }, { 3: 3 }, { 4: 4 }, { 11: 'A' }, { 12: 'B' }] - }, { - title: 'insert out of existed range, after 10 (fixRight)', - action: () => cache.insertItems( - make(['A', 'B']), 10, Direction.forward, true - ), - result: [{ '-1': 1 }, { 0: 2 }, { 1: 3 }, { 2: 4 }, { 9: 'A' }, { 10: 'B' }] - }, { - title: 'insert out of existed range, before -10', - action: () => cache.insertItems( - make(['A', 'B']), -10, Direction.backward, false - ), - result: [{ '-10': 'A' }, { '-9': 'B' }, { 3: 1 }, { 4: 2 }, { 5: 3 }, { 6: 4 }] - }, { - title: 'insert out of existed range, before -10 (fixRight)', - action: () => cache.insertItems( - make(['A', 'B']), -10, Direction.backward, true - ), - result: [{ '-12': 'A' }, { '-11': 'B' }, { 1: 1 }, { 2: 2 }, { 3: 3 }, { 4: 4 }] - }, { - title: 'insert out of existed range, after -10', - action: () => cache.insertItems( - make(['A', 'B']), -10, Direction.forward, false - ), - result: [{ '-9': 'A' }, { '-8': 'B' }, { 3: 1 }, { 4: 2 }, { 5: 3 }, { 6: 4 }] - }, { - title: 'insert out of existed range, after -10 (fixRight)', - action: () => cache.insertItems( - make(['A', 'B']), -10, Direction.forward, true - ), - result: [{ '-11': 'A' }, { '-10': 'B' }, { 1: 1 }, { 2: 2 }, { 3: 3 }, { 4: 4 }] - }]; + const make = (idList: Id[]): Data[] => idList.map(id => generateItem(id)); + + const insertConfigList: ActionCacheConfig[] = [ + { + title: 'not insert, empty case', + action: () => cache.insertItems(make([]), 3, Direction.backward, false), + result: [{ 1: 1 }, { 2: 2 }, { 3: 3 }, { 4: 4 }] + }, + { + title: 'insert in existed range, before 3', + action: () => + cache.insertItems(make(['A', 'B']), 3, Direction.backward, false), + result: [{ 1: 1 }, { 2: 2 }, { 3: 'A' }, { 4: 'B' }, { 5: 3 }, { 6: 4 }] + }, + { + title: 'insert in existed range, before 3 (fixRight)', + action: () => + cache.insertItems(make(['A', 'B']), 3, Direction.backward, true), + result: [ + { '-1': 1 }, + { 0: 2 }, + { 1: 'A' }, + { 2: 'B' }, + { 3: 3 }, + { 4: 4 } + ] + }, + { + title: 'insert in existed range, after 3', + action: () => + cache.insertItems(make(['A', 'B']), 3, Direction.forward, false), + result: [{ 1: 1 }, { 2: 2 }, { 3: 3 }, { 4: 'A' }, { 5: 'B' }, { 6: 4 }] + }, + { + title: 'insert in existed range, after 3', + action: () => + cache.insertItems(make(['A', 'B']), 3, Direction.forward, true), + result: [ + { '-1': 1 }, + { 0: 2 }, + { 1: 3 }, + { 2: 'A' }, + { 3: 'B' }, + { 4: 4 } + ] + }, + { + title: 'insert out of existed range, before 10', + action: () => + cache.insertItems(make(['A', 'B']), 10, Direction.backward, false), + result: [ + { 1: 1 }, + { 2: 2 }, + { 3: 3 }, + { 4: 4 }, + { 10: 'A' }, + { 11: 'B' } + ] + }, + { + title: 'insert out of existed range, before 10 (fixRight)', + action: () => + cache.insertItems(make(['A', 'B']), 10, Direction.backward, true), + result: [ + { '-1': 1 }, + { 0: 2 }, + { 1: 3 }, + { 2: 4 }, + { 8: 'A' }, + { 9: 'B' } + ] + }, + { + title: 'insert out of existed range, after 10', + action: () => + cache.insertItems(make(['A', 'B']), 10, Direction.forward, false), + result: [ + { 1: 1 }, + { 2: 2 }, + { 3: 3 }, + { 4: 4 }, + { 11: 'A' }, + { 12: 'B' } + ] + }, + { + title: 'insert out of existed range, after 10 (fixRight)', + action: () => + cache.insertItems(make(['A', 'B']), 10, Direction.forward, true), + result: [ + { '-1': 1 }, + { 0: 2 }, + { 1: 3 }, + { 2: 4 }, + { 9: 'A' }, + { 10: 'B' } + ] + }, + { + title: 'insert out of existed range, before -10', + action: () => + cache.insertItems(make(['A', 'B']), -10, Direction.backward, false), + result: [ + { '-10': 'A' }, + { '-9': 'B' }, + { 3: 1 }, + { 4: 2 }, + { 5: 3 }, + { 6: 4 } + ] + }, + { + title: 'insert out of existed range, before -10 (fixRight)', + action: () => + cache.insertItems(make(['A', 'B']), -10, Direction.backward, true), + result: [ + { '-12': 'A' }, + { '-11': 'B' }, + { 1: 1 }, + { 2: 2 }, + { 3: 3 }, + { 4: 4 } + ] + }, + { + title: 'insert out of existed range, after -10', + action: () => + cache.insertItems(make(['A', 'B']), -10, Direction.forward, false), + result: [ + { '-9': 'A' }, + { '-8': 'B' }, + { 3: 1 }, + { 4: 2 }, + { 5: 3 }, + { 6: 4 } + ] + }, + { + title: 'insert out of existed range, after -10 (fixRight)', + action: () => + cache.insertItems(make(['A', 'B']), -10, Direction.forward, true), + result: [ + { '-11': 'A' }, + { '-10': 'B' }, + { 1: 1 }, + { 2: 2 }, + { 3: 3 }, + { 4: 4 } + ] + } + ]; insertConfigList.forEach(config => it('should ' + config.title, () => { @@ -672,7 +955,5 @@ describe('Cache Spec', () => { checkCache(cache, config); }) ); - }); - }); diff --git a/tests/unit/datasource-bad.spec.ts b/tests/unit/datasource-bad.spec.ts index 09bb9cdd..8c54c7c5 100644 --- a/tests/unit/datasource-bad.spec.ts +++ b/tests/unit/datasource-bad.spec.ts @@ -1,7 +1,6 @@ import { INVALID_DATASOURCE_PREFIX, Scroller } from '../../src/scroller'; describe('Wrong Datasource on Scroller instantiation', () => { - const checkScrollerError = (datasource, error) => { try { new Scroller({ datasource }); @@ -12,31 +11,17 @@ describe('Wrong Datasource on Scroller instantiation', () => { }; it('should throw "get" must be set', () => - [ - void 0, - null, - 'wrong', - {}, - { get: void 0 } - ].forEach(ds => checkScrollerError(ds, '"get" must be set')) - ); + [void 0, null, 'wrong', {}, { get: void 0 }].forEach(ds => + checkScrollerError(ds, '"get" must be set') + )); it('should throw "get" must be a function', () => - [ - { get: 'get' }, - { get: {} }, - { get: 1 }, - { get: true } - ].forEach(ds => checkScrollerError(ds, '"get" must be a function')) - ); + [{ get: 'get' }, { get: {} }, { get: 1 }, { get: true }].forEach(ds => + checkScrollerError(ds, '"get" must be a function') + )); it('should throw "get" must have 2 argument(s)', () => - [ - { get: () => { } }, - { get: (a) => a }, - { get: (...a) => a }, - ].forEach(ds => checkScrollerError(ds, '"get" must have 2 argument(s)')) - ); - + [{ get: () => {} }, { get: a => a }, { get: (...a) => a }].forEach(ds => + checkScrollerError(ds, '"get" must have 2 argument(s)') + )); }); - diff --git a/tests/unit/misc/items.ts b/tests/unit/misc/items.ts index 7060d638..063c3d87 100644 --- a/tests/unit/misc/items.ts +++ b/tests/unit/misc/items.ts @@ -29,12 +29,19 @@ export const generateBufferItem = (index: number, data: Data): Item => { export const generateItems = (start: number, length: number): Data[] => Array.from({ length }).map((j, i) => generateItem(start + i)); -export const generateBufferItems = (start: number, length: number): Item[] => +export const generateBufferItems = ( + start: number, + length: number +): Item[] => generateItems(start, length).map((item, index) => generateBufferItem(start + index, item) ); -export const generateCacheItems = (start: number, length: number): ItemCache[] => - generateItems(start, length).map((item, index) => - new ItemCache(generateBufferItem(start + index, item), false) +export const generateCacheItems = ( + start: number, + length: number +): ItemCache[] => + generateItems(start, length).map( + (item, index) => + new ItemCache(generateBufferItem(start + index, item), false) ); diff --git a/tests/unit/reactive.spec.ts b/tests/unit/reactive.spec.ts index ce7f3281..1b15351d 100644 --- a/tests/unit/reactive.spec.ts +++ b/tests/unit/reactive.spec.ts @@ -1,7 +1,6 @@ import { Reactive } from '../../src/classes/reactive'; describe('Reactive', () => { - const VALUE = 'test'; const VALUES = ['test1', 'test2', 'test3', 'test4', 'test5']; @@ -22,7 +21,7 @@ describe('Reactive', () => { it('should subscribe and emit', () => { let call = 0; const $ = new Reactive(); - $.on((v) => { + $.on(v => { call++; expect(v).toEqual(VALUE); expect($.get()).toEqual(VALUE); @@ -34,7 +33,7 @@ describe('Reactive', () => { it('should emit multiple', () => { let call = 0; const $ = new Reactive(); - $.on((v) => expect(v).toEqual(VALUES[call++])); + $.on(v => expect(v).toEqual(VALUES[call++])); VALUES.forEach(V => $.set(V)); expect(call).toEqual(VALUES.length); expect($.get()).toEqual(VALUES[call - 1]); @@ -45,7 +44,7 @@ describe('Reactive', () => { it('should subscribe and emit', () => { let call = 0; const $ = new Reactive(); - $.once((v) => { + $.once(v => { call++; expect(v).toEqual(VALUE); expect($.get()).toEqual(VALUE); @@ -57,7 +56,7 @@ describe('Reactive', () => { it('should emit once, but set', () => { let call = 0; const $ = new Reactive(); - $.once((v) => { + $.once(v => { call++; expect(v).toEqual(VALUES[0]); }); @@ -71,7 +70,7 @@ describe('Reactive', () => { it('should emit on subscribe', () => { let call = 0; const $ = new Reactive(VALUE, { emitOnSubscribe: true }); - $.on((v) => { + $.on(v => { call++; expect(v).toEqual(VALUE); expect($.get()).toEqual(VALUE); @@ -89,7 +88,7 @@ describe('Reactive', () => { it('should continue emit', () => { let call = 0; const $ = new Reactive(VALUES[0], { emitOnSubscribe: true }); - $.on((v) => expect(v).toEqual(VALUES[call++])); + $.on(v => expect(v).toEqual(VALUES[call++])); VALUES.forEach((V, i) => i > 0 && $.set(V)); expect(call).toEqual(VALUES.length); expect($.get()).toEqual(VALUES[call - 1]); @@ -100,7 +99,7 @@ describe('Reactive', () => { it('should emit the same value', () => { let call = 0; const $ = new Reactive(VALUE, { emitEqual: true }); - $.on((v) => { + $.on(v => { call++; expect(v).toEqual(VALUE); }); @@ -119,16 +118,21 @@ describe('Reactive', () => { describe('Cancellation', () => { it('should stop emitting if the value becomes obsolete', () => { - let call1 = 0, call2 = 0, call3 = 0, value; + let call1 = 0, + call2 = 0, + call3 = 0, + value; const NEW_VALUE = VALUE + '*'; const $ = new Reactive(''); - $.on((v) => { // subscription 1 + $.on(v => { + // subscription 1 call1++; expect(v).toEqual(value); }); - $.on((v) => { // subscription 2 + $.on(v => { + // subscription 2 call2++; expect(v).toEqual(value); if (call2 === 1) { @@ -137,7 +141,8 @@ describe('Reactive', () => { } }); - $.on((v) => { // subscription 3 + $.on(v => { + // subscription 3 call3++; expect(v).toEqual(value); }); @@ -150,5 +155,4 @@ describe('Reactive', () => { expect(call3).toEqual(1); }); }); - }); diff --git a/tests/unit/routines.spec.ts b/tests/unit/routines.spec.ts index 07c7d09f..159d6d50 100644 --- a/tests/unit/routines.spec.ts +++ b/tests/unit/routines.spec.ts @@ -6,7 +6,6 @@ const element = { parentElement: { style: {} } } as unknown as HTMLElement; const elementBad = { parentElement: null } as unknown as HTMLElement; describe('Routines', () => { - describe('Standard instantiation', () => { it('should instantiate if element & settings are correct', () => { const routines = new Routines(element, settings); @@ -79,5 +78,4 @@ describe('Routines', () => { }); }); }); - }); diff --git a/tests/unit/validation.spec.ts b/tests/unit/validation.spec.ts index 99bc59e7..84cb8281 100644 --- a/tests/unit/validation.spec.ts +++ b/tests/unit/validation.spec.ts @@ -1,4 +1,9 @@ -import { ValidatorType, VALIDATORS, validateOne, validate } from '../../src/inputs'; +import { + ValidatorType, + VALIDATORS, + validateOne, + validate +} from '../../src/inputs'; import { IValidator } from '../../src/interfaces'; const { @@ -14,13 +19,11 @@ const { ONE_OF_CAN, ONE_OF_MUST, OR, - ENUM, + ENUM } = VALIDATORS; describe('Input Params Validation', () => { - describe('[Integer]', () => { - const integerPassInputs = [ { value: 23, parsed: 23 }, { value: '23', parsed: 23 }, @@ -30,7 +33,7 @@ describe('Input Params Validation', () => { { value: -23, parsed: -23 }, { value: '1e1', parsed: 10 }, { value: 1e1, parsed: 10 }, - { value: 1.1e1, parsed: 11 }, + { value: 1.1e1, parsed: 11 } ]; const integerBlockInputs = [ @@ -45,7 +48,7 @@ describe('Input Params Validation', () => { { value: () => null, parsed: NaN }, { value: {}, parsed: NaN }, // { value: void 0, parsed: NaN }, - { value: null, parsed: NaN }, + { value: null, parsed: NaN } ]; const intProp = { validators: [INTEGER] }; @@ -66,7 +69,7 @@ describe('Input Params Validation', () => { { value: Infinity, parsed: NaN }, { value: -Infinity, parsed: NaN }, { value: 'Infinity', parsed: NaN }, - { value: '-Infinity', parsed: NaN }, + { value: '-Infinity', parsed: NaN } ]; inputs.forEach(input => { const parsed = validateOne(input, 'value', intProp); @@ -86,7 +89,7 @@ describe('Input Params Validation', () => { { value: Infinity, parsed: Infinity }, { value: -Infinity, parsed: -Infinity }, { value: 'Infinity', parsed: Infinity }, - { value: '-Infinity', parsed: -Infinity }, + { value: '-Infinity', parsed: -Infinity } ]; inputs.forEach(input => { const parsed = validateOne(input, 'value', intUnlimitedProp); @@ -112,7 +115,14 @@ describe('Input Params Validation', () => { describe('[Iterator callback]', () => { it('should pass only one-argument function', done => { - const badInputs = [1, true, {}, 'test', () => null, (_a: never, _b: never) => null]; + const badInputs = [ + 1, + true, + {}, + 'test', + () => null, + (_a: never, _b: never) => null + ]; const funcProp = { validators: [FUNC_WITH_X_ARGUMENTS(1)] }; badInputs.forEach(input => expect( @@ -120,7 +130,8 @@ describe('Input Params Validation', () => { ).toEqual(false) ); expect( - validateOne({ value: (_item: never) => null }, 'value', funcProp).isValid + validateOne({ value: (_item: never) => null }, 'value', funcProp) + .isValid ).toEqual(true); done(); }); @@ -133,36 +144,44 @@ describe('Input Params Validation', () => { const getProp = (list: string[]) => ({ validators: [ONE_OF_CAN(list)] }); it('should pass only one of twos', done => { - expect(validateOne({ value }, 'value', getProp(['value'])).isValid).toEqual(false); - expect(validateOne({ value }, 'value', getProp(['test'])).isValid).toEqual(true); - expect(validateOne({ value }, 'test', getProp(['value'])).isValid).toEqual(true); - expect(validateOne({ value }, 'test', getProp(['test'])).isValid).toEqual(true); - expect(validateOne({ value, test }, 'value', getProp(['value'])).isValid).toEqual(false); - expect(validateOne({ value, test }, 'test', getProp(['value'])).isValid).toEqual(false); - expect(validateOne({ value, test }, 'value', getProp(['test'])).isValid).toEqual(false); - expect(validateOne({ value, test }, 'test', getProp(['test'])).isValid).toEqual(false); - expect(validateOne({ value, test }, 'test', getProp(['testX'])).isValid).toEqual(true); + [ + [{ value }, 'value', getProp(['value'])], + [{ value, test }, 'value', getProp(['value'])], + [{ value, test }, 'test', getProp(['value'])], + [{ value, test }, 'value', getProp(['test'])], + [{ value, test }, 'test', getProp(['test'])] + ].forEach(args => expect(validateOne(...args).isValid).toEqual(false)); + [ + [{ value }, 'value', getProp(['test'])], + [{ value }, 'test', getProp(['value'])], + [{ value }, 'value', getProp(['test'])], + [{ value }, 'test', getProp(['value'])] + ].forEach(args => expect(validateOne(...args).isValid).toEqual(true)); + done(); }); it('should pass only one of mnever', done => { - expect(validateOne({ value, test, add }, 'value', getProp(['test', 'add'])).isValid).toEqual(false); - expect(validateOne({ value, test, add }, 'value', getProp(['value', 'add'])).isValid).toEqual(false); - expect(validateOne({ value, test, add }, 'value', getProp(['test', 'value'])).isValid).toEqual(false); - expect(validateOne({ value, test, add }, 'value', getProp(['test', 'valueX'])).isValid).toEqual(false); - expect(validateOne({ value, test }, 'value', getProp(['test', 'add'])).isValid).toEqual(false); - expect(validateOne({ value, test }, 'value', getProp(['testX', 'addX'])).isValid).toEqual(true); - expect(validateOne({ value }, 'value', getProp(['test', 'add'])).isValid).toEqual(true); - expect(validateOne({ test }, 'value', getProp(['test', 'add'])).isValid).toEqual(true); - expect(validateOne({ test, add }, 'value', getProp(['test', 'add'])).isValid).toEqual(true); + [ + [{ value, test, add }, 'value', getProp(['test', 'add'])], + [{ value, test, add }, 'value', getProp(['value', 'add'])], + [{ value, test, add }, 'value', getProp(['test', 'value'])], + [{ value, test, add }, 'value', getProp(['test', 'valueX'])], + [{ value, test }, 'value', getProp(['test', 'add'])] + ].forEach(args => expect(validateOne(...args).isValid).toEqual(false)); + + [ + [{ value, test }, 'value', getProp(['testX', 'addX'])], + [{ value }, 'value', getProp(['test', 'add'])], + [{ test }, 'value', getProp(['test', 'add'])], + [{ test, add }, 'value', getProp(['test', 'add'])] + ].forEach(args => expect(validateOne(...args).isValid).toEqual(true)); done(); }); }); - }); describe('Validation', () => { - const token = 'test'; const run = (context: unknown, validators: IValidator[], mandatory = false) => validate(context, { @@ -199,47 +218,58 @@ describe('Validation', () => { const opt2 = 'opt2'; it('should not pass empty context or empty params', () => { - expect(validate({}, { - [opt1]: { validators: [ONE_OF_MUST([opt2])] }, - [opt2]: { validators: [ONE_OF_MUST([opt1])] } - }).isValid).toBe(false); - - expect(validate({}, { - [opt1]: { - validators: [ONE_OF_MUST([])] - }, - [opt2]: { - validators: [ONE_OF_MUST([])] - } - }).isValid).toBe(false); + expect( + validate( + {}, + { + [opt1]: { validators: [ONE_OF_MUST([opt2])] }, + [opt2]: { validators: [ONE_OF_MUST([opt1])] } + } + ).isValid + ).toBe(false); + + expect( + validate( + {}, + { + [opt1]: { validators: [ONE_OF_MUST([])] }, + [opt2]: { validators: [ONE_OF_MUST([])] } + } + ).isValid + ).toBe(false); }); it('should not pass if more than 1 param is present', () => { - expect(validate({ - [opt1]: 1, [opt2]: 2 - }, { - [opt1]: { validators: [ONE_OF_MUST([opt2])] }, - [opt2]: { validators: [ONE_OF_MUST([opt1])] } - }).isValid).toBe(false); - - const result = validate({ - [opt1]: 1, [opt2]: 2 - }, { - [opt1]: { validators: [ONE_OF_MUST([opt2, 'opt3'])] }, - [opt2]: { validators: [ONE_OF_MUST(['opt3'])] } - }); + expect( + validate( + { [opt1]: 1, [opt2]: 2 }, + { + [opt1]: { validators: [ONE_OF_MUST([opt2])] }, + [opt2]: { validators: [ONE_OF_MUST([opt1])] } + } + ).isValid + ).toBe(false); + + const result = validate( + { [opt1]: 1, [opt2]: 2 }, + { + [opt1]: { validators: [ONE_OF_MUST([opt2, 'opt3'])] }, + [opt2]: { validators: [ONE_OF_MUST(['opt3'])] } + } + ); expect(result.isValid).toBe(false); expect(result.params[opt1].isValid).toBe(false); expect(result.params[opt2].isValid).toBe(true); }); it('should pass if only 1 param is present', () => { - const result = validate({ - [opt1]: 1 - }, { - [opt1]: { validators: [ONE_OF_MUST([opt2])] }, - [opt2]: { validators: [ONE_OF_MUST([opt1])] } - }); + const result = validate( + { [opt1]: 1 }, + { + [opt1]: { validators: [ONE_OF_MUST([opt2])] }, + [opt2]: { validators: [ONE_OF_MUST([opt1])] } + } + ); expect(result.isValid).toBe(true); expect(result.params[opt1].isSet).toBe(true); expect(result.params[opt2].isSet).toBe(false); @@ -248,23 +278,16 @@ describe('Validation', () => { describe('[Item List]', () => { it('should not pass non-array', () => { - [ - null, - true, - 'test', - 123, - { test: true } - ].forEach(value => + [null, true, 'test', 123, { test: true }].forEach(value => expect(run({ [token]: value }, [ITEM_LIST])).toBe(false) ); }); it('should not pass empty array', () => { - expect(validate({ - [token]: [] - }, { - [token]: { validators: [ITEM_LIST] } - }).isValid).toBe(false); + expect( + validate({ [token]: [] }, { [token]: { validators: [ITEM_LIST] } }) + .isValid + ).toBe(false); }); it('should not pass array with items of different types', () => { @@ -273,11 +296,10 @@ describe('Validation', () => { ['1', '2', 3], [{ a: 1 }, { b: 2 }, 3] ].forEach(value => - expect(validate({ - [token]: value - }, { - [token]: { validators: [ITEM_LIST] } - }).isValid).toBe(false) + expect( + validate({ [token]: value }, { [token]: { validators: [ITEM_LIST] } }) + .isValid + ).toBe(false) ); }); @@ -288,11 +310,10 @@ describe('Validation', () => { ['1', '2', '3'], [{ a: 1 }, { b: 2 }, { a: true }] ].forEach(value => - expect(validate({ - [token]: value - }, { - [token]: { validators: [ITEM_LIST] } - }).isValid).toBe(true) + expect( + validate({ [token]: value }, { [token]: { validators: [ITEM_LIST] } }) + .isValid + ).toBe(true) ); }); }); @@ -324,21 +345,34 @@ describe('Validation', () => { it('should pass function with 2 arguments', () => { const validators = [FUNC_WITH_X_ARGUMENTS(2)]; passTwoArgumentsFunction(validators); - expect(run({ [token]: (_x: N, _y: N, _z: N) => null }, validators)).toBe(false); + expect(run({ [token]: (_x: N, _y: N, _z: N) => null }, validators)).toBe( + false + ); }); it('should pass function with 2 or more arguments', () => { const validators = [FUNC_WITH_X_AND_MORE_ARGUMENTS(2)]; passTwoArgumentsFunction(validators); - expect(run({ [token]: (_x: N, _y: N, _z: N) => null }, validators)).toBe(true); + expect(run({ [token]: (_x: N, _y: N, _z: N) => null }, validators)).toBe( + true + ); }); it('should pass function 2 to 4 arguments', () => { const validators = [FUNC_WITH_X_TO_Y_ARGUMENTS(2, 4)]; passTwoArgumentsFunction(validators); - expect(run({ [token]: (_x: N, _y: N, _z: N) => null }, validators)).toBe(true); - expect(run({ [token]: (_x: N, _y: N, _z: N, _a: N) => null }, validators)).toBe(true); - expect(run({ [token]: (_x: N, _y: N, _z: N, _a: N, _b: N) => null }, validators)).toBe(false); + expect(run({ [token]: (_x: N, _y: N, _z: N) => null }, validators)).toBe( + true + ); + expect( + run({ [token]: (_x: N, _y: N, _z: N, _a: N) => null }, validators) + ).toBe(true); + expect( + run( + { [token]: (_x: N, _y: N, _z: N, _a: N, _b: N) => null }, + validators + ) + ).toBe(false); }); }); @@ -351,18 +385,16 @@ describe('Validation', () => { true, '', () => null, - function () { }, + function () {}, [], null, - class { }, + class {}, new Map(), new Set(), Symbol(), new Date(), - new RegExp(''), - ].forEach(input => - expect(run({ [token]: input }, [OBJECT])).toBe(false) - ); + new RegExp('') + ].forEach(input => expect(run({ [token]: input }, [OBJECT])).toBe(false)); expect(run({ [token]: {} }, [OBJECT])).toBe(true); expect(run({ [token]: { x: 0 } }, [OBJECT])).toBe(true); }); @@ -373,12 +405,7 @@ describe('Validation', () => { const boolOrObj = [OR([BOOLEAN, OBJECT])]; expect(run({}, boolOrObj)).toBe(true); expect(run({}, boolOrObj, true)).toBe(false); - [ - 1, - null, - () => null, - 'test', - ].forEach(input => + [1, null, () => null, 'test'].forEach(input => expect(run({ [token]: input }, boolOrObj)).toBe(false) ); expect(run({ [token]: {} }, boolOrObj)).toBe(true); @@ -388,32 +415,21 @@ describe('Validation', () => { describe('[Enum]', () => { it('should pass an enumerated value', () => { - enum myEnum { a = 'a', b = 'b', c = 33, d } + enum myEnum { + a = 'a', + b = 'b', + c = 33, + d + } const enumValidators = [ENUM(myEnum)]; expect(run({}, enumValidators)).toBe(true); expect(run({}, enumValidators, true)).toBe(false); - [ - 0, - 1, - '33', - '34', - 35, - ].forEach(input => + [0, 1, '33', '34', 35].forEach(input => expect(run({ [token]: input }, enumValidators)).toBe(false) ); - [ - myEnum.a, - myEnum.b, - myEnum.c, - myEnum.d, - 'a', - 'b', - 33, - 34, - ].forEach(input => - expect(run({ [token]: input }, enumValidators)).toBe(true) + [myEnum.a, myEnum.b, myEnum.c, myEnum.d, 'a', 'b', 33, 34].forEach( + input => expect(run({ [token]: input }, enumValidators)).toBe(true) ); }); }); - }); From 14a942caef216be71c65ad02832d2e57a9181ccf Mon Sep 17 00:00:00 2001 From: Denis Aleksanov Date: Sun, 23 Nov 2025 13:34:27 +0100 Subject: [PATCH 42/51] issue-66 initial-load.spec: Fixed itemSize --- tests/e2e/playwright.config.ts | 6 +- tests/e2e/specs/initial-load.spec.ts | 238 +++++++++++++++++++++++++++ 2 files changed, 242 insertions(+), 2 deletions(-) create mode 100644 tests/e2e/specs/initial-load.spec.ts diff --git a/tests/e2e/playwright.config.ts b/tests/e2e/playwright.config.ts index da8c50fb..79bdd5d1 100644 --- a/tests/e2e/playwright.config.ts +++ b/tests/e2e/playwright.config.ts @@ -19,13 +19,15 @@ export default defineConfig({ screenshot: 'only-on-failure' // headless: true, // slowMo: 500, - // viewport: { width: 1024, height: 570 } }, projects: [ { name: 'chromium', - use: { ...devices['Desktop Chrome'] } + use: { + ...devices['Desktop Chrome'], + viewport: { width: 1024, height: 517 } + } } ] }); diff --git a/tests/e2e/specs/initial-load.spec.ts b/tests/e2e/specs/initial-load.spec.ts new file mode 100644 index 00000000..d43d3fcd --- /dev/null +++ b/tests/e2e/specs/initial-load.spec.ts @@ -0,0 +1,238 @@ +import { test, expect } from '@playwright/test'; +import { VScrollFixture, Direction } from '../fixture/VScrollFixture.js'; +import { afterEachLogs } from '../fixture/after-each-logs.js'; +import { createFixture } from '../fixture/create-fixture.js'; +import { ITestConfig } from 'types/index.js'; + +test.afterEach(afterEachLogs); + +// Basic unlimited datasource for all tests +const makeUnlimitedDatasource = + () => (index: number, count: number, success: (data: unknown[]) => void) => { + const data = []; + for (let i = index; i < index + count; i++) { + data.push({ id: i, text: `item #${i}` }); + } + setTimeout(() => success(data), 25); + }; + +const fixedItemSizeConfigList: ITestConfig[] = [ + { + datasourceGet: makeUnlimitedDatasource(), + datasourceSettings: { + startIndex: 1, + padding: 2, + itemSize: 15, + adapter: true + }, + templateSettings: { viewportHeight: 20, itemHeight: 15 } + }, + { + datasourceGet: makeUnlimitedDatasource(), + datasourceSettings: { + startIndex: 1, + padding: 0.5, + itemSize: 20, + adapter: true + }, + templateSettings: { viewportHeight: 120, itemHeight: 20 } + }, + { + datasourceGet: makeUnlimitedDatasource(), + datasourceSettings: { + startIndex: -99, + padding: 0.3, + itemSize: 25, + adapter: true + }, + templateSettings: { viewportHeight: 200, itemHeight: 25 } + }, + { + datasourceGet: makeUnlimitedDatasource(), + datasourceSettings: { + startIndex: -77, + padding: 0.62, + itemSize: 100, + horizontal: true, + adapter: true + }, + templateSettings: { viewportWidth: 450, itemWidth: 100, horizontal: true } + }, + { + datasourceGet: makeUnlimitedDatasource(), + datasourceSettings: { + startIndex: 1, + padding: 0.5, + itemSize: 20, + windowViewport: true, + adapter: true + }, + templateSettings: { + noViewportClass: true, + viewportHeight: 0, + itemHeight: 20 + } + } +]; + +const fixedItemSizeAndBigBufferSizeConfigList: ITestConfig[] = [ + { + datasourceGet: makeUnlimitedDatasource(), + datasourceSettings: { + startIndex: 100, + padding: 0.1, + itemSize: 20, + bufferSize: 20, + adapter: true + }, + templateSettings: { viewportHeight: 100, itemHeight: 20 } + }, + { + datasourceGet: makeUnlimitedDatasource(), + datasourceSettings: { + startIndex: -50, + padding: 0.1, + itemSize: 100, + bufferSize: 10, + horizontal: true, + adapter: true + }, + templateSettings: { viewportWidth: 200, itemWidth: 100, horizontal: true } + } +]; + +interface ItemsCounter { + backward: { count: number; index: number; padding: number }; + forward: { count: number; index: number; padding: number }; +} + +const getSetItemSizeCounter = async ( + config: ITestConfig, + fixture: VScrollFixture, + itemSize: number +): Promise => { + const bufferSize = await fixture.scroller.settings.bufferSize; + const startIndex = config.datasourceSettings.startIndex as number; + const padding = config.datasourceSettings.padding as number; + const viewportSize = await fixture.scroller.viewport.getSize(); + + const backwardLimit = viewportSize * padding; + const forwardLimit = viewportSize + backwardLimit; + const itemsCounter: ItemsCounter = { + backward: { count: 0, index: 0, padding: 0 }, + forward: { count: 0, index: 0, padding: 0 } + }; + + itemsCounter.backward.count = Math.ceil(backwardLimit / itemSize); + itemsCounter.forward.count = Math.ceil(forwardLimit / itemSize); + + // when bufferSize is big enough + const bwdDiff = bufferSize - itemsCounter.backward.count; + if (bwdDiff > 0) { + itemsCounter.backward.count += bwdDiff; + } + const fwdDiff = bufferSize - itemsCounter.forward.count; + if (fwdDiff > 0) { + itemsCounter.forward.count += fwdDiff; + } + + itemsCounter.backward.index = startIndex - itemsCounter.backward.count; + itemsCounter.forward.index = startIndex + itemsCounter.forward.count - 1; + return itemsCounter; +}; + +const testItemsCounter = async ( + startIndex: number, + fixture: VScrollFixture, + itemsCounter: ItemsCounter +) => { + const elements = await fixture.getElements(); + const firstIndex = await fixture.getElementIndex(elements[0] as HTMLElement); + const lastIndex = await fixture.getElementIndex( + elements[elements.length - 1] as HTMLElement + ); + + expect(firstIndex).toEqual(itemsCounter.backward.index); + expect(lastIndex).toEqual(itemsCounter.forward.index); + expect( + await fixture.scroller.viewport.paddings[Direction.backward].size + ).toEqual(itemsCounter.backward.padding); + expect( + await fixture.scroller.viewport.paddings[Direction.forward].size + ).toEqual(itemsCounter.forward.padding); + + const bufferInfo = await fixture.adapter.bufferInfo; + expect(bufferInfo.firstIndex).toEqual(itemsCounter.backward.index); + expect(bufferInfo.lastIndex).toEqual(itemsCounter.forward.index); + + const firstVisible = await fixture.adapter.firstVisible; + expect(firstVisible.$index).toEqual(startIndex); +}; + +const testSetItemSizeCase = async ( + fixture: VScrollFixture, + config: ITestConfig +) => { + // Wait for the workflow to complete + await fixture.adapter.relax(); + + const cyclesDone = await fixture.workflow.cyclesDone; + const fetchCount = await fixture.page.evaluate( + () => window.__vscroll__.workflow.scroller.state.fetch.callCount + ); + const innerLoopCount = await fixture.workflow.innerLoopCount; + const clipCount = await fixture.page.evaluate( + () => window.__vscroll__.workflow.scroller.state.clip.callCount + ); + + expect(cyclesDone).toEqual(1); + expect(fetchCount).toEqual(2); + expect(innerLoopCount).toEqual(3); + expect(clipCount).toEqual(0); + expect( + await fixture.scroller.viewport.paddings[Direction.backward].size + ).toEqual(0); + expect( + await fixture.scroller.viewport.paddings[Direction.forward].size + ).toEqual(0); + + const startIndex = config.datasourceSettings.startIndex as number; + const tplSettings = config.templateSettings || {}; + const horizontal = config.datasourceSettings.horizontal; + const itemSize = tplSettings[ + horizontal ? 'itemWidth' : 'itemHeight' + ] as number; + const itemsCounter = await getSetItemSizeCounter(config, fixture, itemSize); + await testItemsCounter(startIndex, fixture, itemsCounter); +}; + +const makeTest = ( + title: string, + config: ITestConfig, + testFunc: (fixture: VScrollFixture, config: ITestConfig) => Promise +) => { + test(title, async ({ page }) => { + const fixture = await createFixture({ page, config }); + await testFunc(fixture, config); + await fixture.dispose(); + }); +}; + +test.describe('Initial Load Spec', () => { + test.describe('Fixed itemSize', () => { + fixedItemSizeConfigList.forEach((config, i) => + makeTest( + `should make 2 fetches to satisfy padding limits (config ${i})`, + config, + testSetItemSizeCase + ) + ); + fixedItemSizeAndBigBufferSizeConfigList.forEach((config, i) => + makeTest( + `should make 2 fetches to overflow padding limits (bufferSize is big enough) (config ${i})`, + config, + testSetItemSizeCase + ) + ); + }); +}); From 78726a7b0fd49f17bd829b201fee74abd456d8d8 Mon Sep 17 00:00:00 2001 From: Denis Aleksanov Date: Sun, 23 Nov 2025 15:11:07 +0100 Subject: [PATCH 43/51] issue-66 initial-load.spec: Tuned itemSize (1) --- tests/e2e/specs/initial-load.spec.ts | 236 ++++++++++++++++++++++++++- 1 file changed, 235 insertions(+), 1 deletion(-) diff --git a/tests/e2e/specs/initial-load.spec.ts b/tests/e2e/specs/initial-load.spec.ts index d43d3fcd..9a7de8f5 100644 --- a/tests/e2e/specs/initial-load.spec.ts +++ b/tests/e2e/specs/initial-load.spec.ts @@ -101,9 +101,25 @@ const fixedItemSizeAndBigBufferSizeConfigList: ITestConfig[] = [ } ]; +const tunedItemSizeConfigList: ITestConfig[] = [ + { + datasourceGet: makeUnlimitedDatasource(), + datasourceSettings: { + startIndex: 1, + bufferSize: 1, + padding: 0.5, + itemSize: 40, + adapter: true + }, + noRelaxOnStart: true, + templateSettings: { viewportHeight: 100, itemHeight: 20 } + } +]; + interface ItemsCounter { backward: { count: number; index: number; padding: number }; forward: { count: number; index: number; padding: number }; + total: number; } const getSetItemSizeCounter = async ( @@ -120,7 +136,8 @@ const getSetItemSizeCounter = async ( const forwardLimit = viewportSize + backwardLimit; const itemsCounter: ItemsCounter = { backward: { count: 0, index: 0, padding: 0 }, - forward: { count: 0, index: 0, padding: 0 } + forward: { count: 0, index: 0, padding: 0 }, + total: 0 }; itemsCounter.backward.count = Math.ceil(backwardLimit / itemSize); @@ -138,6 +155,91 @@ const getSetItemSizeCounter = async ( itemsCounter.backward.index = startIndex - itemsCounter.backward.count; itemsCounter.forward.index = startIndex + itemsCounter.forward.count - 1; + itemsCounter.total = + itemsCounter.forward.index - itemsCounter.backward.index + 1; + return itemsCounter; +}; + +// eslint-disable-next-line @typescript-eslint/no-unused-vars +const getNotSetItemSizeCounter = async ( + config: ITestConfig, + fixture: VScrollFixture, + itemSize: number, + previous?: ItemsCounter +): Promise => { + const bufferSize = await fixture.scroller.settings.bufferSize; + const startIndex = config.datasourceSettings.startIndex as number; + const padding = config.datasourceSettings.padding as number; + const viewportSize = await fixture.scroller.viewport.getSize(); + const backwardLimit = viewportSize * padding; + const forwardLimit = viewportSize + backwardLimit; + const itemsCounter: ItemsCounter = { + backward: { count: 0, index: 0, padding: 0 }, + forward: { count: 0, index: 0, padding: 0 }, + total: 0 + }; + const countB = previous ? previous.backward.count : 0; + const countF = previous ? previous.forward.count : 0; + let bwd, fwd; + + // 1) fetch only in forward direction if this is the first fetch + // 2) fetch bufferSize items if Settings.itemSize value hasn't been set up + itemsCounter.backward.count = previous + ? itemSize + ? Math.ceil(backwardLimit / itemSize) + : bufferSize + : 0; + itemsCounter.forward.count = itemSize + ? Math.ceil(forwardLimit / itemSize) + : bufferSize; + if (previous) { + itemsCounter.backward.count = Math.max(itemsCounter.backward.count, countB); + itemsCounter.forward.count = Math.max(itemsCounter.forward.count, countF); + } + + // when bufferSize is big enough + bwd = itemsCounter.backward.count - (previous ? countB : 0); + fwd = itemsCounter.forward.count - (previous ? countF : 0); + const bwdDiff = bwd > 0 ? bufferSize - bwd : 0; + const fwdDiff = fwd > 0 ? bufferSize - fwd : 0; + if (bwdDiff > 0 && bwd > fwd) { + itemsCounter.backward.count += bwdDiff; + itemsCounter.forward.count = previous ? countF : itemsCounter.forward.count; + } + if (fwdDiff > 0 && fwd >= bwd) { + itemsCounter.forward.count += fwdDiff; + itemsCounter.backward.count = previous + ? countB + : itemsCounter.backward.count; + } + + if (previous) { + bwd = itemsCounter.backward.count - countB; + fwd = itemsCounter.forward.count - countF; + if (bwd > 0 && bwd > fwd) { + itemsCounter.backward.count = countB + bwd; + itemsCounter.forward.count = + fwd > 0 ? countF : itemsCounter.forward.count; + } + if (fwd > 0 && fwd >= bwd) { + itemsCounter.forward.count = countF + fwd; + itemsCounter.backward.count = + bwd > 0 ? countB : itemsCounter.backward.count; + } + } + + itemsCounter.backward.index = startIndex - itemsCounter.backward.count; + itemsCounter.forward.index = startIndex + itemsCounter.forward.count - 1; + + const defaultSize = await fixture.scroller.buffer.defaultSize; + itemsCounter.backward.padding = 0; + itemsCounter.forward.padding = Math.max( + 0, + viewportSize - itemsCounter.forward.count * defaultSize + ); + + itemsCounter.total = + itemsCounter.forward.index - itemsCounter.backward.index + 1; return itemsCounter; }; @@ -206,6 +308,121 @@ const testSetItemSizeCase = async ( await testItemsCounter(startIndex, fixture, itemsCounter); }; +type LoopData = { + loopCount: number; + viewportSize: number; + defaultSize: number; + bufferSize: number; + firstIndex: number; + lastIndex: number; + bwdPaddingSize: number; + fwdPaddingSize: number; + elementsLength: number; + firstElementIndex: number; + lastElementIndex: number; + bufferItemsLength: number; + bufferFirstIndex: number; + bufferLastIndex: number; + firstVisibleIndex: number; +}; + +const testNotSetItemSizeCase = async ( + fixture: VScrollFixture, + config: ITestConfig +) => { + const startIndex = config.datasourceSettings.startIndex as number; + const initialItemSize = config.datasourceSettings.itemSize as number; + const tplSettings = config.templateSettings || {}; + const horizontal = config.datasourceSettings.horizontal; + const itemSize = tplSettings[ + horizontal ? 'itemWidth' : 'itemHeight' + ] as number; + + // Capture data from all loops in browser context + const loopData = await fixture.page.evaluate(() => { + return new Promise<{ loops: LoopData[] }>(resolve => { + const workflow = window.__vscroll__.workflow; + const innerLoop = workflow.scroller.state.cycle.innerLoop; + const loops: LoopData[] = []; + + innerLoop.busy.on(loopPending => { + if (loopPending) { + return; + } + + const loopCount = innerLoop.total; + + // On loops 1, 2, 3: memorize complete state + if (loopCount >= 1 && loopCount <= 3) { + const buffer = workflow.scroller.buffer; + loops.push({ + loopCount: loopCount, + viewportSize: workflow.scroller.viewport.getScrollableSize(), + defaultSize: buffer.defaultSize, + bufferSize: buffer.size, + firstIndex: buffer.firstIndex, + lastIndex: buffer.lastIndex, + bwdPaddingSize: workflow.scroller.viewport.paddings.backward.size, + fwdPaddingSize: workflow.scroller.viewport.paddings.forward.size, + elementsLength: buffer.items.length, + firstElementIndex: buffer.items[0].$index, + lastElementIndex: buffer.items[buffer.items.length - 1].$index, + bufferItemsLength: buffer.items.length, + bufferFirstIndex: workflow.scroller.adapter.bufferInfo.firstIndex, + bufferLastIndex: workflow.scroller.adapter.bufferInfo.lastIndex, + firstVisibleIndex: workflow.scroller.adapter.firstVisible.$index + }); + } + + // On loop 4: resolve with all collected data + if (loopCount === 4) { + resolve({ loops }); + return; + } + }); + }); + }); + + // Wait for scroller to stop completely + await fixture.adapter.relax(); + + // Check final state in node context + const cyclesDone = await fixture.workflow.cyclesDone; + const { fetchCount, clipCount } = await fixture.page.evaluate(() => ({ + fetchCount: window.__vscroll__.workflow.scroller.state.fetch.callCount, + clipCount: window.__vscroll__.workflow.scroller.state.clip.callCount + })); + expect(cyclesDone).toEqual(1); + expect(fetchCount).toEqual(3); + expect(clipCount).toEqual(0); + + // Validate each loop's itemsCounter against captured snapshots + let previousCounter: ItemsCounter | undefined; + + for (const loop of loopData.loops) { + const sizeToUse = loop.loopCount === 1 ? initialItemSize : itemSize; + const itemsCounter = await getNotSetItemSizeCounter( + config, + fixture, + sizeToUse, + previousCounter + ); + + // Replicate all checks from original testItemsCounter using captured data + expect(loop.bwdPaddingSize).toEqual(itemsCounter.backward.padding); + expect(loop.fwdPaddingSize).toEqual(itemsCounter.forward.padding); + expect(loop.elementsLength).toEqual(itemsCounter.total); + expect(loop.bufferItemsLength).toEqual(itemsCounter.total); + expect(loop.firstElementIndex).toEqual(itemsCounter.backward.index); + expect(loop.lastElementIndex).toEqual(itemsCounter.forward.index); + expect(loop.bufferFirstIndex).toEqual(itemsCounter.backward.index); + expect(loop.bufferLastIndex).toEqual(itemsCounter.forward.index); + expect(loop.firstVisibleIndex).toEqual(startIndex); + + previousCounter = itemsCounter; + } +}; + const makeTest = ( title: string, config: ITestConfig, @@ -235,4 +452,21 @@ test.describe('Initial Load Spec', () => { ) ); }); + + test.describe('Tuned itemSize', () => { + tunedItemSizeConfigList.forEach((config, i) => + makeTest( + `should make 3 fetches to satisfy padding limits (config ${i})`, + config, + testNotSetItemSizeCase + ) + ); + // tunedItemSizeAndBigBufferSizeConfigList.forEach((config, i) => + // makeTest( + // `should make 3 fetches to overflow padding limits (bufferSize is big enough) (config ${i})`, + // config, + // testNotSetItemSizeCase + // ) + // ); + }); }); From b6580f8c9888705d418994c577cfe075d9cf6878 Mon Sep 17 00:00:00 2001 From: Denis Aleksanov Date: Sun, 23 Nov 2025 15:24:28 +0100 Subject: [PATCH 44/51] issue-66 initial-load.spec: Tuned itemSize (2) --- tests/e2e/specs/initial-load.spec.ts | 86 +++++++++++++++++++++++++--- 1 file changed, 79 insertions(+), 7 deletions(-) diff --git a/tests/e2e/specs/initial-load.spec.ts b/tests/e2e/specs/initial-load.spec.ts index 9a7de8f5..130de092 100644 --- a/tests/e2e/specs/initial-load.spec.ts +++ b/tests/e2e/specs/initial-load.spec.ts @@ -113,6 +113,78 @@ const tunedItemSizeConfigList: ITestConfig[] = [ }, noRelaxOnStart: true, templateSettings: { viewportHeight: 100, itemHeight: 20 } + }, + { + datasourceGet: makeUnlimitedDatasource(), + datasourceSettings: { + startIndex: -50, + bufferSize: 2, + padding: 0.5, + itemSize: 30, + adapter: true + }, + noRelaxOnStart: true, + templateSettings: { viewportHeight: 120, itemHeight: 20 } + }, + { + datasourceGet: makeUnlimitedDatasource(), + datasourceSettings: { + startIndex: -77, + padding: 0.82, + itemSize: 200, + horizontal: true, + adapter: true + }, + noRelaxOnStart: true, + templateSettings: { viewportWidth: 450, itemWidth: 100, horizontal: true } + }, + { + datasourceGet: makeUnlimitedDatasource(), + datasourceSettings: { + startIndex: -47, + padding: 0.3, + itemSize: 60, + windowViewport: true, + adapter: true + }, + noRelaxOnStart: true, + templateSettings: { + noViewportClass: true, + viewportHeight: 0, + itemHeight: 40 + } + } +]; + +const tunedItemSizeAndBigBufferSizeConfigList: ITestConfig[] = [ + { + datasourceGet: makeUnlimitedDatasource(), + datasourceSettings: { + startIndex: -50, + bufferSize: 7, + padding: 0.5, + itemSize: 30, + adapter: true + }, + noRelaxOnStart: true, + templateSettings: { viewportHeight: 120, itemHeight: 20 } + }, + { + datasourceGet: makeUnlimitedDatasource(), + datasourceSettings: { + startIndex: 50, + padding: 0.33, + itemSize: 35, + bufferSize: 20, + windowViewport: true, + adapter: true + }, + noRelaxOnStart: true, + templateSettings: { + noViewportClass: true, + viewportHeight: 0, + itemHeight: 20 + } } ]; @@ -461,12 +533,12 @@ test.describe('Initial Load Spec', () => { testNotSetItemSizeCase ) ); - // tunedItemSizeAndBigBufferSizeConfigList.forEach((config, i) => - // makeTest( - // `should make 3 fetches to overflow padding limits (bufferSize is big enough) (config ${i})`, - // config, - // testNotSetItemSizeCase - // ) - // ); + tunedItemSizeAndBigBufferSizeConfigList.forEach((config, i) => + makeTest( + `should make 3 fetches to overflow padding limits (bufferSize is big enough) (config ${i})`, + config, + testNotSetItemSizeCase + ) + ); }); }); From dce4e5dba8b3278bff4f46aa2f963ff579a51a81 Mon Sep 17 00:00:00 2001 From: Denis Aleksanov Date: Sun, 23 Nov 2025 18:14:15 +0100 Subject: [PATCH 45/51] issue-66 initial-load.spec: No itemSize --- tests/e2e/specs/initial-load.spec.ts | 42 ++++++++++++++++++++++++++++ 1 file changed, 42 insertions(+) diff --git a/tests/e2e/specs/initial-load.spec.ts b/tests/e2e/specs/initial-load.spec.ts index 130de092..e2aa79c1 100644 --- a/tests/e2e/specs/initial-load.spec.ts +++ b/tests/e2e/specs/initial-load.spec.ts @@ -188,6 +188,31 @@ const tunedItemSizeAndBigBufferSizeConfigList: ITestConfig[] = [ } ]; +const noItemSizeConfigList: ITestConfig[] = tunedItemSizeConfigList.map( + ({ + datasourceGet, + datasourceSettings: { itemSize: _, ...restDatasourceSettings }, + ...config + }) => ({ + ...config, + datasourceGet, + datasourceSettings: { ...restDatasourceSettings } + }) +); + +const noItemSizeAndBigBufferConfigList: ITestConfig[] = + tunedItemSizeAndBigBufferSizeConfigList.map( + ({ + datasourceGet, + datasourceSettings: { itemSize: _, ...restDatasourceSettings }, + ...config + }) => ({ + ...config, + datasourceGet, + datasourceSettings: { ...restDatasourceSettings } + }) + ); + interface ItemsCounter { backward: { count: number; index: number; padding: number }; forward: { count: number; index: number; padding: number }; @@ -541,4 +566,21 @@ test.describe('Initial Load Spec', () => { ) ); }); + + test.describe('No itemSize', () => { + noItemSizeConfigList.forEach((config, i) => + makeTest( + `should make 3 fetches to satisfy padding limits (config ${i})`, + config, + testNotSetItemSizeCase + ) + ); + noItemSizeAndBigBufferConfigList.forEach((config, i) => + makeTest( + `should make 3 fetches to overflow padding limits (bufferSize is big enough) (config ${i})`, + config, + testNotSetItemSizeCase + ) + ); + }); }); From 6b2e46e1ff0a7e6bee7719956da3a6dc6df110b1 Mon Sep 17 00:00:00 2001 From: Denis Aleksanov Date: Sun, 23 Nov 2025 22:22:59 +0100 Subject: [PATCH 46/51] issue-66 initial-load.spec: Lack of items after the 1st fetch --- tests/e2e/specs/initial-load.spec.ts | 159 +++++++++++++++++++++++++++ 1 file changed, 159 insertions(+) diff --git a/tests/e2e/specs/initial-load.spec.ts b/tests/e2e/specs/initial-load.spec.ts index e2aa79c1..79f63117 100644 --- a/tests/e2e/specs/initial-load.spec.ts +++ b/tests/e2e/specs/initial-load.spec.ts @@ -213,6 +213,111 @@ const noItemSizeAndBigBufferConfigList: ITestConfig[] = }) ); +const lackOfItemsOnFirstFetchConfigList: ITestConfig[] = [ + { + datasourceGet: ( + index: number, + count: number, + success: (data: unknown[]) => void + ) => { + const data = []; + for (let i = index; i < index + count; i++) { + if (i >= 1) { + data.push({ id: i, text: `item #${i}` }); + } + } + setTimeout(() => success(data), 25); + }, + datasourceSettings: { + startIndex: 100, + padding: 0.5, + bufferSize: 10, + minIndex: 1, + adapter: true + }, + noRelaxOnStart: true, + templateSettings: { viewportHeight: 300, itemHeight: 20 } + }, + { + datasourceGet: ( + index: number, + count: number, + success: (data: unknown[]) => void + ) => { + const data = []; + for (let i = index; i < index + count; i++) { + if (i >= -75) { + data.push({ id: i, text: `item #${i}` }); + } + } + setTimeout(() => success(data), 25); + }, + datasourceSettings: { + startIndex: -70, + padding: 0.5, + bufferSize: 2, + minIndex: -75, + adapter: true + }, + noRelaxOnStart: true, + templateSettings: { viewportHeight: 200, itemHeight: 20 } + }, + { + datasourceGet: ( + index: number, + count: number, + success: (data: unknown[]) => void + ) => { + const data = []; + for (let i = index; i < index + count; i++) { + if (i >= -9) { + data.push({ id: i, text: `item #${i}` }); + } + } + setTimeout(() => success(data), 25); + }, + datasourceSettings: { + startIndex: 1, + padding: 0.1, + bufferSize: 12, + minIndex: -9, + windowViewport: true, + adapter: true + }, + noRelaxOnStart: true, + templateSettings: { + noViewportClass: true, + viewportHeight: 0, + itemHeight: 20 + } + }, + { + datasourceGet: ( + index: number, + count: number, + success: (data: unknown[]) => void + ) => { + const data = []; + for (let i = index; i < index + count; i++) { + if (i >= -120) { + data.push({ id: i, text: `item #${i}` }); + } + } + setTimeout(() => success(data), 25); + }, + datasourceSettings: { + startIndex: -99, + padding: 0.3, + bufferSize: 4, + minIndex: -120, + horizontal: true, + adapter: true + }, + noRelaxOnStart: true, + templateSettings: { horizontal: true, viewportWidth: 300, itemWidth: 40 } + } +]; + interface ItemsCounter { backward: { count: number; index: number; padding: number }; forward: { count: number; index: number; padding: number }; @@ -520,6 +625,51 @@ const testNotSetItemSizeCase = async ( } }; +const testLackOfItemsOnFirstFetchCase = async ( + fixture: VScrollFixture, + config: ITestConfig +) => { + const startIndex = config.datasourceSettings.startIndex as number; + + // Capture state after the first inner loop completes + const result = await fixture.page.evaluate(() => { + return new Promise<{ + firstLoopScrollPosition: number; + firstLoopBwdPaddingSize: number; + firstLoopFirstVisibleIndex: number; + }>(resolve => { + const workflow = window.__vscroll__.workflow; + const innerLoop = workflow.scroller.state.cycle.innerLoop; + let resolved = false; + + innerLoop.busy.on((loopPending: boolean) => { + if (!loopPending && !resolved) { + // First inner loop just completed + resolved = true; + const viewport = workflow.scroller.viewport; + resolve({ + firstLoopScrollPosition: viewport.scrollPosition, + firstLoopBwdPaddingSize: viewport.paddings.backward.size, + firstLoopFirstVisibleIndex: + workflow.scroller.adapter.firstVisible.$index + }); + } + }); + }); + }); + + // Start workflow and wait for it to complete + await fixture.adapter.relax(); + + // Verify state after first inner loop + expect(result.firstLoopScrollPosition).toBe(result.firstLoopBwdPaddingSize); + expect(result.firstLoopFirstVisibleIndex).toEqual(startIndex); + + // Verify final state + const finalFirstVisibleIndex = (await fixture.adapter.firstVisible).$index; + expect(finalFirstVisibleIndex).toEqual(startIndex); +}; + const makeTest = ( title: string, config: ITestConfig, @@ -583,4 +733,13 @@ test.describe('Initial Load Spec', () => { ) ); }); + + test.describe('Lack of items after the 1st fetch', () => + lackOfItemsOnFirstFetchConfigList.forEach((config, i) => + makeTest( + `should stretch the forward padding element (${i})`, + config, + testLackOfItemsOnFirstFetchCase + ) + )); }); From c6d7dd73ecd4bab867d43b9b3c91f33b79d4c885 Mon Sep 17 00:00:00 2001 From: Denis Aleksanov Date: Sun, 23 Nov 2025 22:57:50 +0100 Subject: [PATCH 47/51] issue-66 initial-load.spec: finalization --- tests/e2e/specs/initial-load.spec.ts | 113 ++++++++++----------------- 1 file changed, 42 insertions(+), 71 deletions(-) diff --git a/tests/e2e/specs/initial-load.spec.ts b/tests/e2e/specs/initial-load.spec.ts index 79f63117..3d6751f6 100644 --- a/tests/e2e/specs/initial-load.spec.ts +++ b/tests/e2e/specs/initial-load.spec.ts @@ -3,22 +3,29 @@ import { VScrollFixture, Direction } from '../fixture/VScrollFixture.js'; import { afterEachLogs } from '../fixture/after-each-logs.js'; import { createFixture } from '../fixture/create-fixture.js'; import { ITestConfig } from 'types/index.js'; +import { ItemsCounter } from '../helpers/itemsCounter.js'; + +// // Capture console logs for comparison +// test.beforeEach(async ({ page }) => { +// page.on('console', msg => { +// console.log('[BROWSER]', msg.text()); +// }); +// }); test.afterEach(afterEachLogs); // Basic unlimited datasource for all tests -const makeUnlimitedDatasource = - () => (index: number, count: number, success: (data: unknown[]) => void) => { - const data = []; - for (let i = index; i < index + count; i++) { - data.push({ id: i, text: `item #${i}` }); - } - setTimeout(() => success(data), 25); - }; +const unlimitedDatasource = (index, count, success) => { + const data = []; + for (let i = index; i < index + count; i++) { + data.push({ id: i, text: `item #${i}` }); + } + setTimeout(() => success(data), 25); +}; const fixedItemSizeConfigList: ITestConfig[] = [ { - datasourceGet: makeUnlimitedDatasource(), + datasourceGet: unlimitedDatasource, datasourceSettings: { startIndex: 1, padding: 2, @@ -28,7 +35,7 @@ const fixedItemSizeConfigList: ITestConfig[] = [ templateSettings: { viewportHeight: 20, itemHeight: 15 } }, { - datasourceGet: makeUnlimitedDatasource(), + datasourceGet: unlimitedDatasource, datasourceSettings: { startIndex: 1, padding: 0.5, @@ -38,7 +45,7 @@ const fixedItemSizeConfigList: ITestConfig[] = [ templateSettings: { viewportHeight: 120, itemHeight: 20 } }, { - datasourceGet: makeUnlimitedDatasource(), + datasourceGet: unlimitedDatasource, datasourceSettings: { startIndex: -99, padding: 0.3, @@ -48,7 +55,7 @@ const fixedItemSizeConfigList: ITestConfig[] = [ templateSettings: { viewportHeight: 200, itemHeight: 25 } }, { - datasourceGet: makeUnlimitedDatasource(), + datasourceGet: unlimitedDatasource, datasourceSettings: { startIndex: -77, padding: 0.62, @@ -59,7 +66,7 @@ const fixedItemSizeConfigList: ITestConfig[] = [ templateSettings: { viewportWidth: 450, itemWidth: 100, horizontal: true } }, { - datasourceGet: makeUnlimitedDatasource(), + datasourceGet: unlimitedDatasource, datasourceSettings: { startIndex: 1, padding: 0.5, @@ -77,7 +84,7 @@ const fixedItemSizeConfigList: ITestConfig[] = [ const fixedItemSizeAndBigBufferSizeConfigList: ITestConfig[] = [ { - datasourceGet: makeUnlimitedDatasource(), + datasourceGet: unlimitedDatasource, datasourceSettings: { startIndex: 100, padding: 0.1, @@ -88,7 +95,7 @@ const fixedItemSizeAndBigBufferSizeConfigList: ITestConfig[] = [ templateSettings: { viewportHeight: 100, itemHeight: 20 } }, { - datasourceGet: makeUnlimitedDatasource(), + datasourceGet: unlimitedDatasource, datasourceSettings: { startIndex: -50, padding: 0.1, @@ -103,7 +110,7 @@ const fixedItemSizeAndBigBufferSizeConfigList: ITestConfig[] = [ const tunedItemSizeConfigList: ITestConfig[] = [ { - datasourceGet: makeUnlimitedDatasource(), + datasourceGet: unlimitedDatasource, datasourceSettings: { startIndex: 1, bufferSize: 1, @@ -115,7 +122,7 @@ const tunedItemSizeConfigList: ITestConfig[] = [ templateSettings: { viewportHeight: 100, itemHeight: 20 } }, { - datasourceGet: makeUnlimitedDatasource(), + datasourceGet: unlimitedDatasource, datasourceSettings: { startIndex: -50, bufferSize: 2, @@ -127,7 +134,7 @@ const tunedItemSizeConfigList: ITestConfig[] = [ templateSettings: { viewportHeight: 120, itemHeight: 20 } }, { - datasourceGet: makeUnlimitedDatasource(), + datasourceGet: unlimitedDatasource, datasourceSettings: { startIndex: -77, padding: 0.82, @@ -139,7 +146,7 @@ const tunedItemSizeConfigList: ITestConfig[] = [ templateSettings: { viewportWidth: 450, itemWidth: 100, horizontal: true } }, { - datasourceGet: makeUnlimitedDatasource(), + datasourceGet: unlimitedDatasource, datasourceSettings: { startIndex: -47, padding: 0.3, @@ -158,7 +165,7 @@ const tunedItemSizeConfigList: ITestConfig[] = [ const tunedItemSizeAndBigBufferSizeConfigList: ITestConfig[] = [ { - datasourceGet: makeUnlimitedDatasource(), + datasourceGet: unlimitedDatasource, datasourceSettings: { startIndex: -50, bufferSize: 7, @@ -170,7 +177,7 @@ const tunedItemSizeAndBigBufferSizeConfigList: ITestConfig[] = [ templateSettings: { viewportHeight: 120, itemHeight: 20 } }, { - datasourceGet: makeUnlimitedDatasource(), + datasourceGet: unlimitedDatasource, datasourceSettings: { startIndex: 50, padding: 0.33, @@ -215,11 +222,7 @@ const noItemSizeAndBigBufferConfigList: ITestConfig[] = const lackOfItemsOnFirstFetchConfigList: ITestConfig[] = [ { - datasourceGet: ( - index: number, - count: number, - success: (data: unknown[]) => void - ) => { + datasourceGet: (index, count, success) => { const data = []; for (let i = index; i < index + count; i++) { if (i >= 1) { @@ -239,11 +242,7 @@ const lackOfItemsOnFirstFetchConfigList: ITestConfig[] = [ templateSettings: { viewportHeight: 300, itemHeight: 20 } }, { - datasourceGet: ( - index: number, - count: number, - success: (data: unknown[]) => void - ) => { + datasourceGet: (index, count, success) => { const data = []; for (let i = index; i < index + count; i++) { if (i >= -75) { @@ -263,11 +262,7 @@ const lackOfItemsOnFirstFetchConfigList: ITestConfig[] = [ templateSettings: { viewportHeight: 200, itemHeight: 20 } }, { - datasourceGet: ( - index: number, - count: number, - success: (data: unknown[]) => void - ) => { + datasourceGet: (index, count, success) => { const data = []; for (let i = index; i < index + count; i++) { if (i >= -9) { @@ -292,11 +287,7 @@ const lackOfItemsOnFirstFetchConfigList: ITestConfig[] = [ } }, { - datasourceGet: ( - index: number, - count: number, - success: (data: unknown[]) => void - ) => { + datasourceGet: (index, count, success) => { const data = []; for (let i = index; i < index + count; i++) { if (i >= -120) { @@ -318,12 +309,6 @@ const lackOfItemsOnFirstFetchConfigList: ITestConfig[] = [ } ]; -interface ItemsCounter { - backward: { count: number; index: number; padding: number }; - forward: { count: number; index: number; padding: number }; - total: number; -} - const getSetItemSizeCounter = async ( config: ITestConfig, fixture: VScrollFixture, @@ -336,11 +321,7 @@ const getSetItemSizeCounter = async ( const backwardLimit = viewportSize * padding; const forwardLimit = viewportSize + backwardLimit; - const itemsCounter: ItemsCounter = { - backward: { count: 0, index: 0, padding: 0 }, - forward: { count: 0, index: 0, padding: 0 }, - total: 0 - }; + const itemsCounter = new ItemsCounter(); itemsCounter.backward.count = Math.ceil(backwardLimit / itemSize); itemsCounter.forward.count = Math.ceil(forwardLimit / itemSize); @@ -357,12 +338,9 @@ const getSetItemSizeCounter = async ( itemsCounter.backward.index = startIndex - itemsCounter.backward.count; itemsCounter.forward.index = startIndex + itemsCounter.forward.count - 1; - itemsCounter.total = - itemsCounter.forward.index - itemsCounter.backward.index + 1; return itemsCounter; }; -// eslint-disable-next-line @typescript-eslint/no-unused-vars const getNotSetItemSizeCounter = async ( config: ITestConfig, fixture: VScrollFixture, @@ -375,11 +353,7 @@ const getNotSetItemSizeCounter = async ( const viewportSize = await fixture.scroller.viewport.getSize(); const backwardLimit = viewportSize * padding; const forwardLimit = viewportSize + backwardLimit; - const itemsCounter: ItemsCounter = { - backward: { count: 0, index: 0, padding: 0 }, - forward: { count: 0, index: 0, padding: 0 }, - total: 0 - }; + const itemsCounter = new ItemsCounter(); const countB = previous ? previous.backward.count : 0; const countF = previous ? previous.forward.count : 0; let bwd, fwd; @@ -440,8 +414,6 @@ const getNotSetItemSizeCounter = async ( viewportSize - itemsCounter.forward.count * defaultSize ); - itemsCounter.total = - itemsCounter.forward.index - itemsCounter.backward.index + 1; return itemsCounter; }; @@ -634,9 +606,9 @@ const testLackOfItemsOnFirstFetchCase = async ( // Capture state after the first inner loop completes const result = await fixture.page.evaluate(() => { return new Promise<{ - firstLoopScrollPosition: number; - firstLoopBwdPaddingSize: number; - firstLoopFirstVisibleIndex: number; + scrollPosition: number; + bwdPaddingSize: number; + firstVisibleIndex: number; }>(resolve => { const workflow = window.__vscroll__.workflow; const innerLoop = workflow.scroller.state.cycle.innerLoop; @@ -648,10 +620,9 @@ const testLackOfItemsOnFirstFetchCase = async ( resolved = true; const viewport = workflow.scroller.viewport; resolve({ - firstLoopScrollPosition: viewport.scrollPosition, - firstLoopBwdPaddingSize: viewport.paddings.backward.size, - firstLoopFirstVisibleIndex: - workflow.scroller.adapter.firstVisible.$index + scrollPosition: viewport.scrollPosition, + bwdPaddingSize: viewport.paddings.backward.size, + firstVisibleIndex: workflow.scroller.adapter.firstVisible.$index }); } }); @@ -662,8 +633,8 @@ const testLackOfItemsOnFirstFetchCase = async ( await fixture.adapter.relax(); // Verify state after first inner loop - expect(result.firstLoopScrollPosition).toBe(result.firstLoopBwdPaddingSize); - expect(result.firstLoopFirstVisibleIndex).toEqual(startIndex); + expect(result.scrollPosition).toBe(result.bwdPaddingSize); + expect(result.firstVisibleIndex).toEqual(startIndex); // Verify final state const finalFirstVisibleIndex = (await fixture.adapter.firstVisible).$index; From 18ede93ffc73dced2e7fe81633ff7cfe8fd93925 Mon Sep 17 00:00:00 2001 From: Denis Aleksanov Date: Fri, 28 Nov 2025 00:05:50 +0100 Subject: [PATCH 48/51] prettier husky lint-staged: core sources are formatted --- .prettierrc | 3 +- eslint.config.js | 8 + src/classes/adapter.ts | 1229 +++++++++++++++-------------- src/classes/adapter/context.ts | 11 +- src/classes/adapter/props.ts | 13 +- src/classes/buffer.ts | 57 +- src/classes/buffer/cache.ts | 39 +- src/classes/buffer/checkCall.ts | 20 +- src/classes/buffer/defaultSize.ts | 15 +- src/classes/datasource.ts | 126 +-- src/classes/domRoutines.ts | 27 +- src/classes/logger.ts | 137 ++-- src/classes/paddings.ts | 187 ++--- src/classes/reactive.ts | 1 - src/classes/settings.ts | 29 +- src/classes/state.ts | 180 ++--- src/classes/state/clip.ts | 1 - src/classes/state/fetch.ts | 6 +- src/classes/viewport.ts | 16 +- src/index.ts | 6 +- src/inputs/adapter.ts | 88 ++- src/inputs/datasource.ts | 2 +- src/inputs/index.ts | 2 +- src/inputs/settings.ts | 13 +- src/inputs/validation.ts | 214 ++--- src/inputs/workflow.ts | 2 +- src/interfaces/datasource.ts | 7 +- src/interfaces/index.ts | 12 +- src/interfaces/process.ts | 93 +-- src/interfaces/routines.ts | 8 +- src/interfaces/settings.ts | 68 +- src/interfaces/state.ts | 52 +- src/interfaces/validation.ts | 4 +- src/interfaces/workflow.ts | 1 - src/processes/adapter/append.ts | 15 +- src/processes/adapter/check.ts | 19 +- src/processes/adapter/clip.ts | 2 - src/processes/adapter/fix.ts | 23 +- src/processes/adapter/insert.ts | 30 +- src/processes/adapter/pause.ts | 2 - src/processes/adapter/reload.ts | 2 - src/processes/adapter/remove.ts | 36 +- src/processes/adapter/replace.ts | 4 +- src/processes/adapter/reset.ts | 13 +- src/processes/adapter/update.ts | 32 +- src/processes/adjust.ts | 335 ++++---- src/processes/clip.ts | 122 ++- src/processes/end.ts | 5 +- src/processes/fetch.ts | 21 +- src/processes/index.ts | 83 +- src/processes/init.ts | 2 - src/processes/misc/base.ts | 26 +- src/processes/misc/enums.ts | 4 +- src/processes/postFetch.ts | 31 +- src/processes/preClip.ts | 23 +- src/processes/preFetch.ts | 51 +- src/processes/render.ts | 49 +- src/processes/scroll.ts | 35 +- src/processes/start.ts | 2 - src/scroller.ts | 46 +- src/workflow-transducer.ts | 5 +- src/workflow.ts | 53 +- 62 files changed, 1987 insertions(+), 1761 deletions(-) diff --git a/.prettierrc b/.prettierrc index e5067d01..ffe8ad35 100644 --- a/.prettierrc +++ b/.prettierrc @@ -2,5 +2,6 @@ "tabWidth": 2, "singleQuote": true, "arrowParens": "avoid", - "trailingComma": "none" + "trailingComma": "none", + "printWidth": 100 } diff --git a/eslint.config.js b/eslint.config.js index bf8dda32..d1e0489c 100644 --- a/eslint.config.js +++ b/eslint.config.js @@ -36,6 +36,14 @@ export default [ semi: ['error', 'always'], '@typescript-eslint/no-empty-function': 'off', '@typescript-eslint/no-explicit-any': ['error', { ignoreRestArgs: true }], + '@typescript-eslint/no-unused-vars': [ + 'error', + { + argsIgnorePattern: '^_', + varsIgnorePattern: '^_', + caughtErrorsIgnorePattern: '^_' + } + ], // Disable no-undef for TypeScript as TypeScript compiler handles this 'no-undef': 'off' diff --git a/src/classes/adapter.ts b/src/classes/adapter.ts index b6f99187..cbd262ff 100644 --- a/src/classes/adapter.ts +++ b/src/classes/adapter.ts @@ -1,606 +1,623 @@ -import { Logger } from './logger'; -import { Buffer } from './buffer'; -import { Reactive } from './reactive'; -import { - AdapterPropName, - AdapterPropType, - EMPTY_ITEM, - getDefaultAdapterProps, - methodPausedResult, - methodPreResult, - reactiveConfigStorage -} from './adapter/props'; -import { wantedUtils } from './adapter/wanted'; -import { Viewport } from './viewport'; -import { Direction } from '../inputs/index'; -import { AdapterProcess, ProcessStatus } from '../processes/index'; -import { - WorkflowGetter, - IAdapterProp, - AdapterMethodResult, - IAdapter, - ItemAdapter, - ItemsPredicate, - AdapterPrependOptions, - AdapterAppendOptions, - AdapterRemoveOptions, - AdapterClipOptions, - AdapterInsertOptions, - AdapterReplaceOptions, - AdapterUpdateOptions, - AdapterFixOptions, - ScrollerWorkflow, - IDatasourceOptional, - IPackages, - IBufferInfo, - State, - ProcessSubject, -} from '../interfaces/index'; - -type MethodResolver = (...args: unknown[]) => Promise; -type InitializationParams = { - buffer: Buffer, - state: State, - viewport: Viewport, - logger: Logger, - adapterRun$?: Reactive, - getWorkflow?: WorkflowGetter -} - -const ADAPTER_PROPS_STUB = getDefaultAdapterProps(); -const ALLOWED_METHODS_WHEN_PAUSED = ADAPTER_PROPS_STUB.filter(v => !!v.allowedWhenPaused).map(v => v.name); - -const _has = (obj: unknown, prop: string): boolean => - !!obj && typeof obj === 'object' && Object.prototype.hasOwnProperty.call(obj, prop); - -const convertAppendArgs = (prepend: boolean, options: unknown, eof?: boolean) => { - let result = options as AdapterAppendOptions & AdapterPrependOptions; - if (!_has(options, 'items')) { - const items = !Array.isArray(options) ? [options] : options; - result = prepend ? { items, bof: eof } : { items, eof: eof }; - } - return result; -}; - -const convertRemoveArgs = (options: AdapterRemoveOptions | ItemsPredicate) => { - if (!(_has(options, 'predicate') || _has(options, 'indexes'))) { - const predicate = options as ItemsPredicate; - options = { predicate }; - } - return options; -}; - -export class Adapter implements IAdapter { - private externalContext: IAdapter; - private logger: Logger; - private getWorkflow: WorkflowGetter; - private reloadCounter: number; - private source: { [key: string]: Reactive } = {}; // for Reactive props - private box: { [key: string]: unknown } = {}; // for Scalars over Reactive props - private demand: { [key: string]: unknown } = {}; // for Scalars on demand - private disposed: boolean; - - // eslint-disable-next-line @typescript-eslint/no-unused-vars - setFirstOrLastVisible = (_: { first?: boolean, last?: boolean, workflow?: ScrollerWorkflow }) => { }; - - get workflow(): ScrollerWorkflow { - return this.getWorkflow(); - } - get reloadCount(): number { - return this.reloadCounter; - } - get reloadId(): string { - return this.id + '.' + this.reloadCounter; - } - - id: number; - mock: boolean; - augmented: boolean; - version: string; - init: boolean; - init$: Reactive; - packageInfo: IPackages; - itemsCount: number; - bufferInfo: IBufferInfo; - isLoading: boolean; - isLoading$: Reactive; - loopPending: boolean; - loopPending$: Reactive; - firstVisible: ItemAdapter; - firstVisible$: Reactive>; - lastVisible: ItemAdapter; - lastVisible$: Reactive>; - bof: boolean; - bof$: Reactive; - eof: boolean; - eof$: Reactive; - paused: boolean; - paused$: Reactive; - - private relax$: Reactive | null; - private relaxRun: Promise | null; - - private getPromisifiedMethod(method: MethodResolver, args: unknown[]) { - return new Promise(resolve => { - if (this.relax$) { - this.relax$.once(value => resolve(value)); - } - method.apply(this, args); - }); - } - - private getWorkflowRunnerMethod(method: MethodResolver, name: AdapterPropName) { - return (...args: unknown[]): Promise => { - if (!this.relax$) { - this.logger?.log?.(() => 'scroller is not initialized: ' + name + ' method is ignored'); - return Promise.resolve(methodPreResult); - } - if (this.paused && !ALLOWED_METHODS_WHEN_PAUSED.includes(name)) { - this.logger?.log?.(() => 'scroller is paused: ' + name + ' method is ignored'); - return Promise.resolve(methodPausedResult); - - } - return this.getPromisifiedMethod(method, args); - }; - } - - constructor(context: IAdapter | null, getWorkflow: WorkflowGetter, logger: Logger) { - this.getWorkflow = getWorkflow; - this.logger = logger; - this.relax$ = null; - this.relaxRun = null; - this.reloadCounter = 0; - const contextId = context?.id || -1; - - // public context (if exists) should provide access to Reactive props config by id - const reactivePropsStore = context && reactiveConfigStorage.get(context.id) || {}; - - // the Adapter initialization should not trigger "wanted" props setting; - // after the initialization is completed, "wanted" functionality must be unblocked - wantedUtils.setBlock(true, contextId); - - // make array of the original values from public context if present - const adapterProps = context - ? ADAPTER_PROPS_STUB.map(prop => { - let value = context[prop.name]; - // if context is augmented, we need to replace external reactive props with inner ones - if (context.augmented) { - const reactiveProp = reactivePropsStore[prop.name]; - if (reactiveProp) { - value = reactiveProp.default as Reactive; // boolean doesn't matter here - } - } - return ({ ...prop, value }); - }) - : getDefaultAdapterProps(); - - // restore default reactive props if they were configured - Object.entries(reactivePropsStore).forEach(([key, value]) => { - const prop = adapterProps.find(({ name }) => name === key); - if (prop && value) { - prop.value = value.default; - } - }); - - // Scalar permanent props - adapterProps - .filter(({ type, permanent }) => type === AdapterPropType.Scalar && permanent) - .forEach(({ name, value }: IAdapterProp) => - Object.defineProperty(this, name, { - configurable: true, - get: () => value - }) - ); - - // Reactive props: store original values in "source" container, to avoid extra .get() calls on scalar twins set - adapterProps - .filter(prop => prop.type === AdapterPropType.Reactive) - .forEach(({ name, value }: IAdapterProp) => { - this.source[name] = value as Reactive; - Object.defineProperty(this, name, { - configurable: true, - get: () => this.source[name] - }); - }); - - // for "wanted" props that can be explicitly requested for the first time after the Adapter initialization, - // an implicit calculation of the initial value is required; - // so this method should be called when accessing the "wanted" props through one of the following getters - const processWanted = (prop: IAdapterProp) => { - if (wantedUtils.setBox(prop, contextId)) { - if ([AdapterPropName.firstVisible, AdapterPropName.firstVisible$].some(n => n === prop.name)) { - this.setFirstOrLastVisible({ first: true }); - } else if ([AdapterPropName.lastVisible, AdapterPropName.lastVisible$].some(n => n === prop.name)) { - this.setFirstOrLastVisible({ last: true }); - } - } - }; - - // Scalar props that have Reactive twins - // 1) reactive props (from "source") should be triggered on set - // 2) scalars should use "box" container on get - // 3) "wanted" scalars should also run wanted-related logic on get - adapterProps - .filter(prop => prop.type === AdapterPropType.Scalar && !!prop.reactive) - .forEach((prop: IAdapterProp) => { - const { name, value, reactive } = prop; - this.box[name] = value; - Object.defineProperty(this, name, { - configurable: true, - set: (newValue: unknown) => { - if (newValue !== this.box[name]) { - this.box[name] = newValue; - this.source[reactive as AdapterPropName].set(newValue); - // need to emit new value through the configured reactive prop if present - const reactiveProp = reactivePropsStore[reactive as AdapterPropName]; - if (reactiveProp) { - reactiveProp.emit(reactiveProp.source, newValue); - } - } - }, - get: () => { - processWanted(prop); - return this.box[name]; - } - }); - }); - - // Scalar props on-demand - // these scalars should use "demand" container - // setting defaults should be overridden on init() - adapterProps - .filter(prop => prop.type === AdapterPropType.Scalar && prop.onDemand) - .forEach(({ name, value }: IAdapterProp) => { - this.demand[name] = value; - Object.defineProperty(this, name, { - configurable: true, - get: () => this.demand[name] - }); - }); - - if (!context) { - return; - } - - // Adapter public context augmentation - adapterProps - .forEach((prop: IAdapterProp) => { - const { name, type, permanent } = prop; - let value = (this as IAdapter)[name]; - if (type === AdapterPropType.Function) { - value = (value as () => void).bind(this); - } else if (type === AdapterPropType.WorkflowRunner) { - value = this.getWorkflowRunnerMethod(value as MethodResolver, name); - } else if (type === AdapterPropType.Reactive && reactivePropsStore[name]) { - value = (context as IAdapter)[name]; - } else if (name === AdapterPropName.augmented) { - value = true; - } - const nonPermanentScalar = !permanent && type === AdapterPropType.Scalar; - Object.defineProperty(context, name, { - configurable: true, - get: () => { - processWanted(prop); // consider accessing "wanted" Reactive props - if (nonPermanentScalar) { - return (this as IAdapter)[name]; // non-permanent Scalars should be taken in runtime - } - return value; // other props (Reactive/Functions/WorkflowRunners) can be defined once - } - }); - }); - - this.externalContext = context; - wantedUtils.setBlock(false, contextId); - } - - initialize( - { buffer, state, viewport, logger, adapterRun$, getWorkflow }: InitializationParams - ): void { - // buffer - Object.defineProperty(this.demand, AdapterPropName.itemsCount, { - get: () => buffer.getVisibleItemsCount() - }); - Object.defineProperty(this.demand, AdapterPropName.bufferInfo, { - get: (): IBufferInfo => ({ - firstIndex: buffer.firstIndex, - lastIndex: buffer.lastIndex, - minIndex: buffer.minIndex, - maxIndex: buffer.maxIndex, - absMinIndex: buffer.absMinIndex, - absMaxIndex: buffer.absMaxIndex, - defaultSize: buffer.defaultSize, - }) - }); - this.bof = buffer.bof.get(); - buffer.bof.on(bof => this.bof = bof); - this.eof = buffer.eof.get(); - buffer.eof.on(eof => this.eof = eof); - - // state - Object.defineProperty(this.demand, AdapterPropName.packageInfo, { - get: () => state.packageInfo - }); - this.loopPending = state.cycle.innerLoop.busy.get(); - state.cycle.innerLoop.busy.on(busy => this.loopPending = busy); - this.isLoading = state.cycle.busy.get(); - state.cycle.busy.on(busy => this.isLoading = busy); - this.paused = state.paused.get(); - state.paused.on(paused => this.paused = paused); - - //viewport - this.setFirstOrLastVisible = ({ first, last, workflow }) => { - if ((!first && !last) || workflow?.call?.interrupted) { - return; - } - const token = first ? AdapterPropName.firstVisible : AdapterPropName.lastVisible; - if (!wantedUtils.getBox(this.externalContext?.id)?.[token]) { - return; - } - if (buffer.items.some(({ element }) => !element)) { - logger.log('skipping first/lastVisible set because not all buffered items are rendered at this moment'); - return; - } - const direction = first ? Direction.backward : Direction.forward; - const { item } = viewport.getEdgeVisibleItem(buffer.items, direction); - if (!item || item.element !== this[token].element) { - this[token] = (item ? item.get() : EMPTY_ITEM) as ItemAdapter; - } - }; - - // logger - this.logger = logger; - - // self-pending subscription; set up only on the very first init - if (adapterRun$) { - if (!this.relax$) { - this.relax$ = new Reactive(); - } - const relax$ = this.relax$; - adapterRun$.on(({ status, payload }) => { - let unSubRelax = () => { }; - if (status === ProcessStatus.start) { - unSubRelax = this.isLoading$.on(value => { - if (!value) { - unSubRelax(); - relax$.set({ success: true, immediate: false, details: null }); - } - }); - } else if (status === ProcessStatus.done || status === ProcessStatus.error) { - unSubRelax(); - relax$.set({ - success: status !== ProcessStatus.error, - immediate: true, - details: status === ProcessStatus.error && payload ? String(payload.error) : null - }); - } - }); - } - - // workflow getter - if (getWorkflow) { - this.getWorkflow = getWorkflow; - } - - // init - this.init = true; - } - - dispose(): void { - if (this.relax$) { - this.relax$.dispose(); - } - if (this.externalContext) { - this.resetContext(); - } - Object.getOwnPropertyNames(this).forEach(prop => { - delete (this as Record)[prop]; - }); - this.disposed = true; - } - - resetContext(): void { - const reactiveStore = reactiveConfigStorage.get(this.externalContext?.id); - ADAPTER_PROPS_STUB - .forEach(({ type, permanent, name, value }) => { - // assign initial values to non-reactive non-permanent props - if (type !== AdapterPropType.Reactive && !permanent) { - Object.defineProperty(this.externalContext, name, { - configurable: true, - get: () => value - }); - } - // reset reactive props - if (type === AdapterPropType.Reactive && reactiveStore) { - const property = reactiveStore[name]; - if (property) { - property.default.reset(); - property.emit(property.source, property.default.get()); - } - } - }); - } - - // eslint-disable-next-line @typescript-eslint/no-explicit-any - reset(options?: IDatasourceOptional): any { - this.reloadCounter++; - this.logger.logAdapterMethod('reset', options, ` of ${this.reloadId}`); - this.workflow.call({ - process: AdapterProcess.reset, - status: ProcessStatus.start, - payload: { options } - }); - } - - // eslint-disable-next-line @typescript-eslint/no-explicit-any - reload(options?: number | string): any { - this.reloadCounter++; - this.logger.logAdapterMethod('reload', options, ` of ${this.reloadId}`); - this.workflow.call({ - process: AdapterProcess.reload, - status: ProcessStatus.start, - payload: { options } - }); - } - - // eslint-disable-next-line @typescript-eslint/no-explicit-any - append(_options: AdapterAppendOptions | unknown, eof?: boolean): any { - const options = convertAppendArgs(false, _options, eof); // support old signature - this.logger.logAdapterMethod('append', [options.items, options.eof]); - this.workflow.call({ - process: AdapterProcess.append, - status: ProcessStatus.start, - payload: { options } - }); - } - - // eslint-disable-next-line @typescript-eslint/no-explicit-any - prepend(_options: AdapterPrependOptions | unknown, bof?: boolean): any { - const options = convertAppendArgs(true, _options, bof); // support old signature - this.logger.logAdapterMethod('prepend', [options.items, options.bof]); - this.workflow.call({ - process: AdapterProcess.prepend, - status: ProcessStatus.start, - payload: { options } - }); - } - - // eslint-disable-next-line @typescript-eslint/no-explicit-any - check(): any { - this.logger.logAdapterMethod('check'); - this.workflow.call({ - process: AdapterProcess.check, - status: ProcessStatus.start - }); - } - - // eslint-disable-next-line @typescript-eslint/no-explicit-any - remove(options: AdapterRemoveOptions | ItemsPredicate): any { - options = convertRemoveArgs(options); // support old signature - this.logger.logAdapterMethod('remove', options); - this.workflow.call({ - process: AdapterProcess.remove, - status: ProcessStatus.start, - payload: { options } - }); - } - - // eslint-disable-next-line @typescript-eslint/no-explicit-any - clip(options?: AdapterClipOptions): any { - this.logger.logAdapterMethod('clip', options); - this.workflow.call({ - process: AdapterProcess.clip, - status: ProcessStatus.start, - payload: { options } - }); - } - - // eslint-disable-next-line @typescript-eslint/no-explicit-any - insert(options: AdapterInsertOptions): any { - this.logger.logAdapterMethod('insert', options); - this.workflow.call({ - process: AdapterProcess.insert, - status: ProcessStatus.start, - payload: { options } - }); - } - - // eslint-disable-next-line @typescript-eslint/no-explicit-any - replace(options: AdapterReplaceOptions): any { - this.logger.logAdapterMethod('replace', options); - this.workflow.call({ - process: AdapterProcess.replace, - status: ProcessStatus.start, - payload: { options } - }); - } - - // eslint-disable-next-line @typescript-eslint/no-explicit-any - update(options: AdapterUpdateOptions): any { - this.logger.logAdapterMethod('update', options); - this.workflow.call({ - process: AdapterProcess.update, - status: ProcessStatus.start, - payload: { options } - }); - } - - // eslint-disable-next-line @typescript-eslint/no-explicit-any - pause(): any { - this.logger.logAdapterMethod('pause'); - this.workflow.call({ - process: AdapterProcess.pause, - status: ProcessStatus.start - }); - } - - // eslint-disable-next-line @typescript-eslint/no-explicit-any - resume(): any { - this.logger.logAdapterMethod('resume'); - this.workflow.call({ - process: AdapterProcess.pause, - status: ProcessStatus.start, - payload: { options: { resume: true } } - }); - } - - // eslint-disable-next-line @typescript-eslint/no-explicit-any - fix(options: AdapterFixOptions): any { - this.logger.logAdapterMethod('fix', options); - this.workflow.call({ - process: AdapterProcess.fix, - status: ProcessStatus.start, - payload: { options } - }); - } - - relaxUnchained(callback: (() => void) | undefined, reloadId: string): Promise { - const runCallback = () => typeof callback === 'function' && reloadId === this.reloadId && callback(); - if (!this.isLoading) { - runCallback(); - } - return new Promise(resolve => { - if (!this.isLoading) { - resolve(true); - return; - } - this.isLoading$.once(() => { - runCallback(); - resolve(false); - }); - }).then(immediate => { - if (this.disposed) { - return { - immediate, - success: false, - details: 'Adapter was disposed' - }; - } - const success = reloadId === this.reloadId; - this.logger?.log?.(() => !success ? `relax promise cancelled due to ${reloadId} != ${this.reloadId}` : void 0); - return { - immediate, - success, - details: !success ? 'Interrupted by reload or reset' : null - }; - }); - } - - relax(callback?: () => void): Promise { - const reloadId = this.reloadId; - this.logger.logAdapterMethod('relax', callback, ` of ${reloadId}`); - if (!this.init) { - return Promise.resolve(methodPreResult); - } - return this.relaxRun = this.relaxRun - ? this.relaxRun.then(() => this.relaxUnchained(callback, reloadId)) - : this.relaxUnchained(callback, reloadId).then((result) => { - this.relaxRun = null; - return result; - }); - } - - showLog(): void { - this.logger.logAdapterMethod('showLog'); - this.logger.logForce(); - } -} +import { Logger } from './logger'; +import { Buffer } from './buffer'; +import { Reactive } from './reactive'; +import { + AdapterPropName, + AdapterPropType, + EMPTY_ITEM, + getDefaultAdapterProps, + methodPausedResult, + methodPreResult, + reactiveConfigStorage +} from './adapter/props'; +import { wantedUtils } from './adapter/wanted'; +import { Viewport } from './viewport'; +import { Direction } from '../inputs/index'; +import { AdapterProcess, ProcessStatus } from '../processes/index'; +import { + WorkflowGetter, + IAdapterProp, + AdapterMethodResult, + IAdapter, + ItemAdapter, + ItemsPredicate, + AdapterPrependOptions, + AdapterAppendOptions, + AdapterRemoveOptions, + AdapterClipOptions, + AdapterInsertOptions, + AdapterReplaceOptions, + AdapterUpdateOptions, + AdapterFixOptions, + ScrollerWorkflow, + IDatasourceOptional, + IPackages, + IBufferInfo, + State, + ProcessSubject +} from '../interfaces/index'; + +type MethodResolver = (...args: unknown[]) => Promise; +type InitializationParams = { + buffer: Buffer; + state: State; + viewport: Viewport; + logger: Logger; + adapterRun$?: Reactive; + getWorkflow?: WorkflowGetter; +}; + +const ADAPTER_PROPS_STUB = getDefaultAdapterProps(); +const ALLOWED_METHODS_WHEN_PAUSED = ADAPTER_PROPS_STUB.filter(v => !!v.allowedWhenPaused).map( + v => v.name +); + +const _has = (obj: unknown, prop: string): boolean => + !!obj && typeof obj === 'object' && Object.prototype.hasOwnProperty.call(obj, prop); + +const convertAppendArgs = (prepend: boolean, options: unknown, eof?: boolean) => { + let result = options as AdapterAppendOptions & AdapterPrependOptions; + if (!_has(options, 'items')) { + const items = !Array.isArray(options) ? [options] : options; + result = prepend ? { items, bof: eof } : { items, eof: eof }; + } + return result; +}; + +const convertRemoveArgs = (options: AdapterRemoveOptions | ItemsPredicate) => { + if (!(_has(options, 'predicate') || _has(options, 'indexes'))) { + const predicate = options as ItemsPredicate; + options = { predicate }; + } + return options; +}; + +export class Adapter implements IAdapter { + private externalContext: IAdapter; + private logger: Logger; + private getWorkflow: WorkflowGetter; + private reloadCounter: number; + private source: { [key: string]: Reactive } = {}; // for Reactive props + private box: { [key: string]: unknown } = {}; // for Scalars over Reactive props + private demand: { [key: string]: unknown } = {}; // for Scalars on demand + private disposed: boolean; + + setFirstOrLastVisible = (_: { + first?: boolean; + last?: boolean; + workflow?: ScrollerWorkflow; + }) => {}; + + get workflow(): ScrollerWorkflow { + return this.getWorkflow(); + } + get reloadCount(): number { + return this.reloadCounter; + } + get reloadId(): string { + return this.id + '.' + this.reloadCounter; + } + + id: number; + mock: boolean; + augmented: boolean; + version: string; + init: boolean; + init$: Reactive; + packageInfo: IPackages; + itemsCount: number; + bufferInfo: IBufferInfo; + isLoading: boolean; + isLoading$: Reactive; + loopPending: boolean; + loopPending$: Reactive; + firstVisible: ItemAdapter; + firstVisible$: Reactive>; + lastVisible: ItemAdapter; + lastVisible$: Reactive>; + bof: boolean; + bof$: Reactive; + eof: boolean; + eof$: Reactive; + paused: boolean; + paused$: Reactive; + + private relax$: Reactive | null; + private relaxRun: Promise | null; + + private getPromisifiedMethod(method: MethodResolver, args: unknown[]) { + return new Promise(resolve => { + if (this.relax$) { + this.relax$.once(value => resolve(value)); + } + method.apply(this, args); + }); + } + + private getWorkflowRunnerMethod(method: MethodResolver, name: AdapterPropName) { + return (...args: unknown[]): Promise => { + if (!this.relax$) { + this.logger?.log?.(() => 'scroller is not initialized: ' + name + ' method is ignored'); + return Promise.resolve(methodPreResult); + } + if (this.paused && !ALLOWED_METHODS_WHEN_PAUSED.includes(name)) { + this.logger?.log?.(() => 'scroller is paused: ' + name + ' method is ignored'); + return Promise.resolve(methodPausedResult); + } + return this.getPromisifiedMethod(method, args); + }; + } + + constructor(context: IAdapter | null, getWorkflow: WorkflowGetter, logger: Logger) { + this.getWorkflow = getWorkflow; + this.logger = logger; + this.relax$ = null; + this.relaxRun = null; + this.reloadCounter = 0; + const contextId = context?.id || -1; + + // public context (if exists) should provide access to Reactive props config by id + const reactivePropsStore = (context && reactiveConfigStorage.get(context.id)) || {}; + + // the Adapter initialization should not trigger "wanted" props setting; + // after the initialization is completed, "wanted" functionality must be unblocked + wantedUtils.setBlock(true, contextId); + + // make array of the original values from public context if present + const adapterProps = context + ? ADAPTER_PROPS_STUB.map(prop => { + let value = context[prop.name]; + // if context is augmented, we need to replace external reactive props with inner ones + if (context.augmented) { + const reactiveProp = reactivePropsStore[prop.name]; + if (reactiveProp) { + value = reactiveProp.default as Reactive; // boolean doesn't matter here + } + } + return { ...prop, value }; + }) + : getDefaultAdapterProps(); + + // restore default reactive props if they were configured + Object.entries(reactivePropsStore).forEach(([key, value]) => { + const prop = adapterProps.find(({ name }) => name === key); + if (prop && value) { + prop.value = value.default; + } + }); + + // Scalar permanent props + adapterProps + .filter(({ type, permanent }) => type === AdapterPropType.Scalar && permanent) + .forEach(({ name, value }: IAdapterProp) => + Object.defineProperty(this, name, { + configurable: true, + get: () => value + }) + ); + + // Reactive props: store original values in "source" container, to avoid extra .get() calls on scalar twins set + adapterProps + .filter(prop => prop.type === AdapterPropType.Reactive) + .forEach(({ name, value }: IAdapterProp) => { + this.source[name] = value as Reactive; + Object.defineProperty(this, name, { + configurable: true, + get: () => this.source[name] + }); + }); + + // for "wanted" props that can be explicitly requested for the first time after the Adapter initialization, + // an implicit calculation of the initial value is required; + // so this method should be called when accessing the "wanted" props through one of the following getters + const processWanted = (prop: IAdapterProp) => { + if (wantedUtils.setBox(prop, contextId)) { + const firstPropList = [AdapterPropName.firstVisible, AdapterPropName.firstVisible$]; + const lastPropList = [AdapterPropName.lastVisible, AdapterPropName.lastVisible$]; + if (firstPropList.some(n => n === prop.name)) { + this.setFirstOrLastVisible({ first: true }); + } else if (lastPropList.some(n => n === prop.name)) { + this.setFirstOrLastVisible({ last: true }); + } + } + }; + + // Scalar props that have Reactive twins + // 1) reactive props (from "source") should be triggered on set + // 2) scalars should use "box" container on get + // 3) "wanted" scalars should also run wanted-related logic on get + adapterProps + .filter(prop => prop.type === AdapterPropType.Scalar && !!prop.reactive) + .forEach((prop: IAdapterProp) => { + const { name, value, reactive } = prop; + this.box[name] = value; + Object.defineProperty(this, name, { + configurable: true, + set: (newValue: unknown) => { + if (newValue !== this.box[name]) { + this.box[name] = newValue; + this.source[reactive as AdapterPropName].set(newValue); + // need to emit new value through the configured reactive prop if present + const reactiveProp = reactivePropsStore[reactive as AdapterPropName]; + if (reactiveProp) { + reactiveProp.emit(reactiveProp.source, newValue); + } + } + }, + get: () => { + processWanted(prop); + return this.box[name]; + } + }); + }); + + // Scalar props on-demand + // these scalars should use "demand" container + // setting defaults should be overridden on init() + adapterProps + .filter(prop => prop.type === AdapterPropType.Scalar && prop.onDemand) + .forEach(({ name, value }: IAdapterProp) => { + this.demand[name] = value; + Object.defineProperty(this, name, { + configurable: true, + get: () => this.demand[name] + }); + }); + + if (!context) { + return; + } + + // Adapter public context augmentation + adapterProps.forEach((prop: IAdapterProp) => { + const { name, type, permanent } = prop; + let value = (this as IAdapter)[name]; + if (type === AdapterPropType.Function) { + value = (value as () => void).bind(this); + } else if (type === AdapterPropType.WorkflowRunner) { + value = this.getWorkflowRunnerMethod(value as MethodResolver, name); + } else if (type === AdapterPropType.Reactive && reactivePropsStore[name]) { + value = (context as IAdapter)[name]; + } else if (name === AdapterPropName.augmented) { + value = true; + } + const nonPermanentScalar = !permanent && type === AdapterPropType.Scalar; + Object.defineProperty(context, name, { + configurable: true, + get: () => { + processWanted(prop); // consider accessing "wanted" Reactive props + if (nonPermanentScalar) { + return (this as IAdapter)[name]; // non-permanent Scalars should be taken in runtime + } + return value; // other props (Reactive/Functions/WorkflowRunners) can be defined once + } + }); + }); + + this.externalContext = context; + wantedUtils.setBlock(false, contextId); + } + + initialize({ + buffer, + state, + viewport, + logger, + adapterRun$, + getWorkflow + }: InitializationParams): void { + // buffer + Object.defineProperty(this.demand, AdapterPropName.itemsCount, { + get: () => buffer.getVisibleItemsCount() + }); + Object.defineProperty(this.demand, AdapterPropName.bufferInfo, { + get: (): IBufferInfo => ({ + firstIndex: buffer.firstIndex, + lastIndex: buffer.lastIndex, + minIndex: buffer.minIndex, + maxIndex: buffer.maxIndex, + absMinIndex: buffer.absMinIndex, + absMaxIndex: buffer.absMaxIndex, + defaultSize: buffer.defaultSize + }) + }); + this.bof = buffer.bof.get(); + buffer.bof.on(bof => (this.bof = bof)); + this.eof = buffer.eof.get(); + buffer.eof.on(eof => (this.eof = eof)); + + // state + Object.defineProperty(this.demand, AdapterPropName.packageInfo, { + get: () => state.packageInfo + }); + this.loopPending = state.cycle.innerLoop.busy.get(); + state.cycle.innerLoop.busy.on(busy => (this.loopPending = busy)); + this.isLoading = state.cycle.busy.get(); + state.cycle.busy.on(busy => (this.isLoading = busy)); + this.paused = state.paused.get(); + state.paused.on(paused => (this.paused = paused)); + + //viewport + this.setFirstOrLastVisible = ({ first, last, workflow }) => { + if ((!first && !last) || workflow?.call?.interrupted) { + return; + } + const token = first ? AdapterPropName.firstVisible : AdapterPropName.lastVisible; + if (!wantedUtils.getBox(this.externalContext?.id)?.[token]) { + return; + } + if (buffer.items.some(({ element }) => !element)) { + logger.log( + 'skipping first/lastVisible set because not all buffered items are rendered at this moment' + ); + return; + } + const direction = first ? Direction.backward : Direction.forward; + const { item } = viewport.getEdgeVisibleItem(buffer.items, direction); + if (!item || item.element !== this[token].element) { + this[token] = (item ? item.get() : EMPTY_ITEM) as ItemAdapter; + } + }; + + // logger + this.logger = logger; + + // self-pending subscription; set up only on the very first init + if (adapterRun$) { + if (!this.relax$) { + this.relax$ = new Reactive(); + } + const relax$ = this.relax$; + adapterRun$.on(({ status, payload }) => { + let unSubRelax = () => {}; + if (status === ProcessStatus.start) { + unSubRelax = this.isLoading$.on(value => { + if (!value) { + unSubRelax(); + relax$.set({ success: true, immediate: false, details: null }); + } + }); + } else if (status === ProcessStatus.done || status === ProcessStatus.error) { + unSubRelax(); + relax$.set({ + success: status !== ProcessStatus.error, + immediate: true, + details: status === ProcessStatus.error && payload ? String(payload.error) : null + }); + } + }); + } + + // workflow getter + if (getWorkflow) { + this.getWorkflow = getWorkflow; + } + + // init + this.init = true; + } + + dispose(): void { + if (this.relax$) { + this.relax$.dispose(); + } + if (this.externalContext) { + this.resetContext(); + } + Object.getOwnPropertyNames(this).forEach(prop => { + delete (this as Record)[prop]; + }); + this.disposed = true; + } + + resetContext(): void { + const reactiveStore = reactiveConfigStorage.get(this.externalContext?.id); + ADAPTER_PROPS_STUB.forEach(({ type, permanent, name, value }) => { + // assign initial values to non-reactive non-permanent props + if (type !== AdapterPropType.Reactive && !permanent) { + Object.defineProperty(this.externalContext, name, { + configurable: true, + get: () => value + }); + } + // reset reactive props + if (type === AdapterPropType.Reactive && reactiveStore) { + const property = reactiveStore[name]; + if (property) { + property.default.reset(); + property.emit(property.source, property.default.get()); + } + } + }); + } + + // eslint-disable-next-line @typescript-eslint/no-explicit-any + reset(options?: IDatasourceOptional): any { + this.reloadCounter++; + this.logger.logAdapterMethod('reset', options, ` of ${this.reloadId}`); + this.workflow.call({ + process: AdapterProcess.reset, + status: ProcessStatus.start, + payload: { options } + }); + } + + // eslint-disable-next-line @typescript-eslint/no-explicit-any + reload(options?: number | string): any { + this.reloadCounter++; + this.logger.logAdapterMethod('reload', options, ` of ${this.reloadId}`); + this.workflow.call({ + process: AdapterProcess.reload, + status: ProcessStatus.start, + payload: { options } + }); + } + + // eslint-disable-next-line @typescript-eslint/no-explicit-any + append(_options: AdapterAppendOptions | unknown, eof?: boolean): any { + const options = convertAppendArgs(false, _options, eof); // support old signature + this.logger.logAdapterMethod('append', [options.items, options.eof]); + this.workflow.call({ + process: AdapterProcess.append, + status: ProcessStatus.start, + payload: { options } + }); + } + + // eslint-disable-next-line @typescript-eslint/no-explicit-any + prepend(_options: AdapterPrependOptions | unknown, bof?: boolean): any { + const options = convertAppendArgs(true, _options, bof); // support old signature + this.logger.logAdapterMethod('prepend', [options.items, options.bof]); + this.workflow.call({ + process: AdapterProcess.prepend, + status: ProcessStatus.start, + payload: { options } + }); + } + + // eslint-disable-next-line @typescript-eslint/no-explicit-any + check(): any { + this.logger.logAdapterMethod('check'); + this.workflow.call({ + process: AdapterProcess.check, + status: ProcessStatus.start + }); + } + + // eslint-disable-next-line @typescript-eslint/no-explicit-any + remove(options: AdapterRemoveOptions | ItemsPredicate): any { + options = convertRemoveArgs(options); // support old signature + this.logger.logAdapterMethod('remove', options); + this.workflow.call({ + process: AdapterProcess.remove, + status: ProcessStatus.start, + payload: { options } + }); + } + + // eslint-disable-next-line @typescript-eslint/no-explicit-any + clip(options?: AdapterClipOptions): any { + this.logger.logAdapterMethod('clip', options); + this.workflow.call({ + process: AdapterProcess.clip, + status: ProcessStatus.start, + payload: { options } + }); + } + + // eslint-disable-next-line @typescript-eslint/no-explicit-any + insert(options: AdapterInsertOptions): any { + this.logger.logAdapterMethod('insert', options); + this.workflow.call({ + process: AdapterProcess.insert, + status: ProcessStatus.start, + payload: { options } + }); + } + + // eslint-disable-next-line @typescript-eslint/no-explicit-any + replace(options: AdapterReplaceOptions): any { + this.logger.logAdapterMethod('replace', options); + this.workflow.call({ + process: AdapterProcess.replace, + status: ProcessStatus.start, + payload: { options } + }); + } + + // eslint-disable-next-line @typescript-eslint/no-explicit-any + update(options: AdapterUpdateOptions): any { + this.logger.logAdapterMethod('update', options); + this.workflow.call({ + process: AdapterProcess.update, + status: ProcessStatus.start, + payload: { options } + }); + } + + // eslint-disable-next-line @typescript-eslint/no-explicit-any + pause(): any { + this.logger.logAdapterMethod('pause'); + this.workflow.call({ + process: AdapterProcess.pause, + status: ProcessStatus.start + }); + } + + // eslint-disable-next-line @typescript-eslint/no-explicit-any + resume(): any { + this.logger.logAdapterMethod('resume'); + this.workflow.call({ + process: AdapterProcess.pause, + status: ProcessStatus.start, + payload: { options: { resume: true } } + }); + } + + // eslint-disable-next-line @typescript-eslint/no-explicit-any + fix(options: AdapterFixOptions): any { + this.logger.logAdapterMethod('fix', options); + this.workflow.call({ + process: AdapterProcess.fix, + status: ProcessStatus.start, + payload: { options } + }); + } + + relaxUnchained( + callback: (() => void) | undefined, + reloadId: string + ): Promise { + const runCallback = () => + typeof callback === 'function' && reloadId === this.reloadId && callback(); + if (!this.isLoading) { + runCallback(); + } + return new Promise(resolve => { + if (!this.isLoading) { + resolve(true); + return; + } + this.isLoading$.once(() => { + runCallback(); + resolve(false); + }); + }).then(immediate => { + if (this.disposed) { + return { + immediate, + success: false, + details: 'Adapter was disposed' + }; + } + const success = reloadId === this.reloadId; + this.logger?.log?.(() => + !success ? `relax promise cancelled due to ${reloadId} != ${this.reloadId}` : void 0 + ); + return { + immediate, + success, + details: !success ? 'Interrupted by reload or reset' : null + }; + }); + } + + relax(callback?: () => void): Promise { + const reloadId = this.reloadId; + this.logger.logAdapterMethod('relax', callback, ` of ${reloadId}`); + if (!this.init) { + return Promise.resolve(methodPreResult); + } + return (this.relaxRun = this.relaxRun + ? this.relaxRun.then(() => this.relaxUnchained(callback, reloadId)) + : this.relaxUnchained(callback, reloadId).then(result => { + this.relaxRun = null; + return result; + })); + } + + showLog(): void { + this.logger.logAdapterMethod('showLog'); + this.logger.logForce(); + } +} diff --git a/src/classes/adapter/context.ts b/src/classes/adapter/context.ts index a8717924..8b4e4d78 100644 --- a/src/classes/adapter/context.ts +++ b/src/classes/adapter/context.ts @@ -1,4 +1,9 @@ -import { AdapterPropName, AdapterPropType, getDefaultAdapterProps, reactiveConfigStorage } from './props'; +import { + AdapterPropName, + AdapterPropType, + getDefaultAdapterProps, + reactiveConfigStorage +} from './props'; import core from '../../version'; import { Reactive } from '../reactive'; import { wantedStorage, wantedUtils } from './wanted'; @@ -7,7 +12,6 @@ import { IReactivePropsStore, IAdapterConfig } from '../../interfaces/index'; let instanceCount = 0; export class AdapterContext { - constructor(config: IAdapterConfig) { const { mock, reactive } = config; const id = ++instanceCount; @@ -51,7 +55,8 @@ export class AdapterContext { }); }); - if (reactive) { // save both configured and default reactive props in the store + if (reactive) { + // save both configured and default reactive props in the store reactiveConfigStorage.set(id, reactivePropsStore); } } diff --git a/src/classes/adapter/props.ts b/src/classes/adapter/props.ts index db888a6a..a5328ffa 100644 --- a/src/classes/adapter/props.ts +++ b/src/classes/adapter/props.ts @@ -1,6 +1,11 @@ import { Reactive } from '../reactive'; import { - IAdapterProp, IBufferInfo, ItemAdapter, IPackages, AdapterMethodResult, IReactivePropsStore + IAdapterProp, + IBufferInfo, + ItemAdapter, + IPackages, + AdapterMethodResult, + IReactivePropsStore } from '../../interfaces/index'; export enum AdapterPropName { @@ -41,14 +46,14 @@ export enum AdapterPropName { resume = 'resume', fix = 'fix', relax = 'relax', - showLog = 'showLog', + showLog = 'showLog' } export enum AdapterPropType { Scalar, Reactive, WorkflowRunner, - Function, + Function } const Name = AdapterPropName; @@ -88,7 +93,7 @@ const bufferInfoDefault: IBufferInfo = { maxIndex: NaN, absMinIndex: -Infinity, absMaxIndex: +Infinity, - defaultSize: NaN, + defaultSize: NaN }; export const EMPTY_ITEM = { diff --git a/src/classes/buffer.ts b/src/classes/buffer.ts index 850c0e31..58ec3521 100644 --- a/src/classes/buffer.ts +++ b/src/classes/buffer.ts @@ -8,7 +8,6 @@ import { Direction } from '../inputs/index'; import { OnDataChanged, BufferUpdater, ItemsPredicate } from '../interfaces/index'; export class Buffer { - private _items: Item[] = []; private _absMinIndex: number; private _absMaxIndex: number; @@ -69,11 +68,15 @@ export class Buffer { index = start; } if (index < min) { - this.logger.log(() => `setting startIndex to settings.minIndex (${min}) because ${index} < ${min}`); + this.logger.log( + () => `setting startIndex to settings.minIndex (${min}) because ${index} < ${min}` + ); index = min; } if (index > max) { - this.logger.log(() => `setting startIndex to settings.maxIndex (${max}) because ${index} > ${max}`); + this.logger.log( + () => `setting startIndex to settings.maxIndex (${max}) because ${index} > ${max}` + ); index = max; } this.startIndex = index; @@ -94,7 +97,8 @@ export class Buffer { set absMinIndex(value: number) { if (this._absMinIndex !== value) { - this._absMinIndex = Number.isFinite(this._absMaxIndex) && value > this._absMaxIndex ? this._absMaxIndex : value; + this._absMinIndex = + Number.isFinite(this._absMaxIndex) && value > this._absMaxIndex ? this._absMaxIndex : value; } if (!this.pristine) { this.checkBOF(); @@ -107,7 +111,8 @@ export class Buffer { set absMaxIndex(value: number) { if (this._absMaxIndex !== value) { - this._absMaxIndex = Number.isFinite(this._absMinIndex) && value < this._absMinIndex ? this._absMinIndex : value; + this._absMaxIndex = + Number.isFinite(this._absMinIndex) && value < this._absMinIndex ? this._absMinIndex : value; } if (!this.pristine) { this.checkEOF(); @@ -121,7 +126,7 @@ export class Buffer { private checkBOF() { // since bof has no setter, need to call checkBOF() on items and absMinIndex change const bof = this.items.length - ? (this.items[0].$index === this.absMinIndex) + ? this.items[0].$index === this.absMinIndex : isFinite(this.absMinIndex); this.bof.set(bof); } @@ -129,7 +134,7 @@ export class Buffer { private checkEOF() { // since eof has no setter, need to call checkEOF() on items and absMaxIndex change const eof = this.items.length - ? (this.items[this.items.length - 1].$index === this.absMaxIndex) + ? this.items[this.items.length - 1].$index === this.absMaxIndex : isFinite(this.absMaxIndex); this.eof.set(eof); } @@ -231,16 +236,13 @@ export class Buffer { removeVirtually(indexes: number[], fixRight: boolean): void { const length = this.items.length; let shifted = false; - for ( - let i = fixRight ? length - 1 : 0; - fixRight ? i >= 0 : i < length; - fixRight ? i-- : i++ - ) { + for (let i = fixRight ? length - 1 : 0; fixRight ? i >= 0 : i < length; fixRight ? i-- : i++) { const item = this.items[i]; - const diff = indexes.reduce((acc, index) => acc + (fixRight - ? (item.$index < index ? 1 : 0) - : (item.$index > index ? -1 : 0) - ), 0); + const diff = indexes.reduce( + (acc, index) => + acc + (fixRight ? (item.$index < index ? 1 : 0) : item.$index > index ? -1 : 0), + 0 + ); shifted = shifted || !!diff; item.updateIndex(item.$index + diff); } @@ -252,18 +254,19 @@ export class Buffer { } fillEmpty( - items: Data[], beforeIndex: number | undefined, afterIndex: number | undefined, fixRight: boolean, - generator: (index: number, data: Data) => Item, + items: Data[], + beforeIndex: number | undefined, + afterIndex: number | undefined, + fixRight: boolean, + generator: (index: number, data: Data) => Item ): boolean { if (!this.checkCall.fillEmpty(items, beforeIndex, afterIndex)) { return false; } const before = Number.isInteger(beforeIndex); const index = (before ? beforeIndex : afterIndex) as number; - const shift = (fixRight ? items.length : (before ? 1 : 0)); - this.items = items.map((data, i) => - generator(index + i + (!before ? 1 : 0) - shift, data) - ); + const shift = fixRight ? items.length : before ? 1 : 0; + this.items = items.map((data, i) => generator(index + i + (!before ? 1 : 0) - shift, data)); this._absMinIndex = this.items[0].$index; this._absMaxIndex = this.items[this.size - 1].$index; if (this.startIndex <= this.absMinIndex) { @@ -279,7 +282,7 @@ export class Buffer { generator: (index: number, data: Data) => Item, indexToTrack: number, fixRight: boolean - ): { trackedIndex: number, toRemove: Item[] } { + ): { trackedIndex: number; toRemove: Item[] } { if (!this.size || Number.isNaN(this.firstIndex)) { return { trackedIndex: NaN, toRemove: [] }; } @@ -299,7 +302,7 @@ export class Buffer { // if predicate result is falsy or empty array -> delete if (!result || (Array.isArray(result) && !result.length)) { item.toRemove = true; - trackedIndex += item.$index >= indexToTrack ? (fixRight ? 1 : 0) : (fixRight ? 0 : -1); + trackedIndex += item.$index >= indexToTrack ? (fixRight ? 1 : 0) : fixRight ? 0 : -1; this.shiftExtremum(-1, fixRight); continue; } @@ -394,8 +397,9 @@ export class Buffer { } getEdgeVisibleItem(direction: Direction, opposite?: boolean): Item | undefined { - return direction === (!opposite ? Direction.forward : Direction.backward) ? - this.getLastVisibleItem() : this.getFirstVisibleItem(); + return direction === (!opposite ? Direction.forward : Direction.backward) + ? this.getLastVisibleItem() + : this.getFirstVisibleItem(); } getVisibleItemsCount(): number { @@ -409,5 +413,4 @@ export class Buffer { checkDefaultSize(): boolean { return this.cache.recalculateDefaultSize(); } - } diff --git a/src/classes/buffer/cache.ts b/src/classes/buffer/cache.ts index cd5a78b9..d392fe2f 100644 --- a/src/classes/buffer/cache.ts +++ b/src/classes/buffer/cache.ts @@ -76,7 +76,7 @@ export class Cache { getSizeByIndex(index: number): number { const item = this.get(index); - return item && item.size || this.defaultSize.get(); + return (item && item.size) || this.defaultSize.get(); } getDefaultSize(): number { @@ -96,12 +96,12 @@ export class Cache { * Maintains min/max indexes and default item size. * * @param {Item} item A Buffer item to be cached, an objects with { $index, data, size } props. - * * @returns {ItemCache} Cached item. */ add(item: Item): ItemCache { let itemCache = this.get(item.$index); - if (itemCache) { // adding item is already cached + if (itemCache) { + // adding item is already cached if (this.saveData) { itemCache.data = item.data; } @@ -142,7 +142,8 @@ export class Cache { insertItems(toInsert: Data[], index: number, direction: Direction, fixRight: boolean): void { const items = new Map>(); const length = toInsert.length; - let min = Infinity, max = -Infinity; + let min = Infinity, + max = -Infinity; const set = (item: ItemCache) => { items.set(item.$index, item); min = item.$index < min ? item.$index : min; @@ -168,9 +169,11 @@ export class Cache { } set(item); }); - if (this.saveData) { // persist data with no sizes + if (this.saveData) { + // persist data with no sizes toInsert.forEach((data, i) => { - const $index = index + i - (fixRight ? length : 0) + (direction === Direction.forward ? 1 : 0); + const $index = + index + i - (fixRight ? length : 0) + (direction === Direction.forward ? 1 : 0); const item = new ItemCache({ $index, data }, this.saveData); set(item); }); @@ -192,7 +195,8 @@ export class Cache { */ removeItems(toRemove: number[], fixRight: boolean): void { const items = new Map>(); - let min = Infinity, max = -Infinity; + let min = Infinity, + max = -Infinity; this.items.forEach(item => { if (toRemove.some(index => index === item.$index)) { if (item.size) { @@ -229,10 +233,12 @@ export class Cache { if (!this.size || !before.length) { return; } - const minB = before[0].$index, maxB = before[before.length - 1].$index; + const minB = before[0].$index, + maxB = before[before.length - 1].$index; let leftDiff: number, rightDiff: number; if (after.length) { - const minA = after[0].$index, maxA = after[after.length - 1].$index; + const minA = after[0].$index, + maxA = after[after.length - 1].$index; leftDiff = minA - minB; rightDiff = maxA - maxB; } else { @@ -241,18 +247,22 @@ export class Cache { } const items = new Map>(); this.items.forEach(item => { - if (item.$index < minB) { // items to the left of the subset + if (item.$index < minB) { + // items to the left of the subset item.changeIndex(item.$index + leftDiff); items.set(item.$index, item); return; - } else if (item.$index > maxB) { // items to the right of the subset + } else if (item.$index > maxB) { + // items to the right of the subset item.changeIndex(item.$index + rightDiff); items.set(item.$index, item); return; } }); - after.forEach(item => // subset items - items.set(item.$index, new ItemCache(item, this.saveData)) + after.forEach( + ( + item // subset items + ) => items.set(item.$index, new ItemCache(item, this.saveData)) ); before // to maintain default size on remove .filter(item => item.toRemove) @@ -271,7 +281,8 @@ export class Cache { */ shiftIndexes(delta: number): void { const items = new Map>(); - let min = Infinity, max = -Infinity; + let min = Infinity, + max = -Infinity; this.items.forEach(item => { item.changeIndex(item.$index + delta); items.set(item.$index, item); diff --git a/src/classes/buffer/checkCall.ts b/src/classes/buffer/checkCall.ts index 695a3fc1..35ccbeed 100644 --- a/src/classes/buffer/checkCall.ts +++ b/src/classes/buffer/checkCall.ts @@ -26,13 +26,13 @@ export class CheckBufferCall { } insertInBuffer(predicate?: ItemsPredicate, before?: number, after?: number): number { - const index = Number.isInteger(before) ? before : (Number.isInteger(after) ? after : NaN); - const found = this.context.items.find(item => - (predicate && predicate(item.get())) || - (Number.isInteger(index) && index === item.$index) + const index = Number.isInteger(before) ? before : Number.isInteger(after) ? after : NaN; + const found = this.context.items.find( + item => + (predicate && predicate(item.get())) || (Number.isInteger(index) && index === item.$index) ); if (!found) { - this.logger.log('no items to insert in buffer; empty predicate\'s result'); + this.logger.log('no items to insert in buffer; empty predicate result'); return NaN; } return found.$index; @@ -45,15 +45,16 @@ export class CheckBufferCall { } const { firstIndex, lastIndex, finiteAbsMinIndex, finiteAbsMaxIndex } = this.context; if (index < finiteAbsMinIndex || index > finiteAbsMaxIndex) { - this.logger.log(() => - 'no items to insert virtually; ' + - `selected index (${index}) does not match virtual area [${finiteAbsMinIndex}..${finiteAbsMaxIndex}]` + this.logger.log( + () => + 'no items to insert virtually; ' + + `selected index (${index}) does not match virtual area [${finiteAbsMinIndex}..${finiteAbsMaxIndex}]` ); return false; } const before = direction === Direction.backward; if (!(index < firstIndex + (before ? 1 : 0) || index > lastIndex - (before ? 0 : 1))) { - this.logger.log(() => + this.logger.log( `no items to insert virtually; selected index (${index}) belongs Buffer [${firstIndex}..${lastIndex}]` ); return false; @@ -61,5 +62,4 @@ export class CheckBufferCall { this.logger.log(() => `going to insert ${items.length} item(s) virtually`); return true; } - } diff --git a/src/classes/buffer/defaultSize.ts b/src/classes/buffer/defaultSize.ts index 7c5f3c1c..3b9d74fe 100644 --- a/src/classes/buffer/defaultSize.ts +++ b/src/classes/buffer/defaultSize.ts @@ -77,7 +77,8 @@ export class DefaultSize { if (removed.length) { const removedSize = removed.reduce((acc, item) => acc + item.size, 0); const averageSize = this.averageSizeFloat || 0; - this.averageSizeFloat = ((cacheSize + removed.length) * averageSize - removedSize) / cacheSize; + this.averageSizeFloat = + ((cacheSize + removed.length) * averageSize - removedSize) / cacheSize; } this.averageSize = Math.round(this.averageSizeFloat); } @@ -89,11 +90,17 @@ export class DefaultSize { newItems.forEach(({ size }) => this.sizeMap.set(size, (this.sizeMap.get(size) || 0) + 1)); } if (oldItems.length) { - oldItems.forEach(({ size }) => this.sizeMap.set(size, Math.max((this.sizeMap.get(size) || 0) - 1, 0))); - oldItems.forEach(({ newSize: s }) => this.sizeMap.set(s as number, (this.sizeMap.get(s as number) || 0) + 1)); + oldItems.forEach(({ size }) => + this.sizeMap.set(size, Math.max((this.sizeMap.get(size) || 0) - 1, 0)) + ); + oldItems.forEach(({ newSize: s }) => + this.sizeMap.set(s as number, (this.sizeMap.get(s as number) || 0) + 1) + ); } if (removed.length) { - removed.forEach(({ size }) => this.sizeMap.set(size, Math.max((this.sizeMap.get(size) || 0) - 1, 0))); + removed.forEach(({ size }) => + this.sizeMap.set(size, Math.max((this.sizeMap.get(size) || 0) - 1, 0)) + ); } const sorted = [...this.sizeMap.entries()].sort((a, b) => b[1] - a[1]); const mostFrequentCount = sorted[0][1]; diff --git a/src/classes/datasource.ts b/src/classes/datasource.ts index 63bcd23f..e890b277 100644 --- a/src/classes/datasource.ts +++ b/src/classes/datasource.ts @@ -1,61 +1,65 @@ -import { AdapterContext } from './adapter/context'; -import { reactiveConfigStorage, AdapterPropType, getDefaultAdapterProps } from './adapter/props'; -import { wantedStorage } from './adapter/wanted'; -import { Reactive } from './reactive'; -import { - IDatasourceParams, - IDatasourceConstructed, - DatasourceGet, - Settings, - DevSettings, - IAdapter, - IAdapterConfig, - IReactivePropConfig -} from '../interfaces/index'; - -const getDefaultAdapterConfig = (): IAdapterConfig => { - const reactive = getDefaultAdapterProps() - .filter(({ type }) => type === AdapterPropType.Reactive) - .reduce((acc, { name, value }) => { - acc[name] = { - source: value, - emit: (source, val) => (source as Reactive).set(val) - }; - return acc; - }, {} as Record); - - return { mock: false, reactive }; -}; - -export class DatasourceGeneric implements IDatasourceConstructed { - get: DatasourceGet; - settings?: Settings; - devSettings?: DevSettings; - adapter: IAdapter; - - constructor(datasource: IDatasourceParams, config?: IAdapterConfig) { - this.get = datasource.get; - this.settings = datasource.settings; - this.devSettings = datasource.devSettings; - const adapterContext = new AdapterContext(config || { mock: false }); - this.adapter = adapterContext as unknown as IAdapter; - } - - dispose(): void { // todo: should it be published? - reactiveConfigStorage.delete(this.adapter.id); - wantedStorage.delete(this.adapter.id); - } -} - - -export const makeDatasource = ( - getAdapterConfig?: () => IAdapterConfig -) => - class extends DatasourceGeneric { - constructor(datasource: IDatasourceParams) { - const config = typeof getAdapterConfig === 'function' ? getAdapterConfig() : getDefaultAdapterConfig(); - super(datasource, config); - } - } as DSClassType; - -export const Datasource = makeDatasource(); +import { AdapterContext } from './adapter/context'; +import { reactiveConfigStorage, AdapterPropType, getDefaultAdapterProps } from './adapter/props'; +import { wantedStorage } from './adapter/wanted'; +import { Reactive } from './reactive'; +import { + IDatasourceParams, + IDatasourceConstructed, + DatasourceGet, + Settings, + DevSettings, + IAdapter, + IAdapterConfig, + IReactivePropConfig +} from '../interfaces/index'; + +const getDefaultAdapterConfig = (): IAdapterConfig => { + const reactive = getDefaultAdapterProps() + .filter(({ type }) => type === AdapterPropType.Reactive) + .reduce( + (acc, { name, value }) => { + acc[name] = { + source: value, + emit: (source, val) => (source as Reactive).set(val) + }; + return acc; + }, + {} as Record + ); + + return { mock: false, reactive }; +}; + +export class DatasourceGeneric implements IDatasourceConstructed { + get: DatasourceGet; + settings?: Settings; + devSettings?: DevSettings; + adapter: IAdapter; + + constructor(datasource: IDatasourceParams, config?: IAdapterConfig) { + this.get = datasource.get; + this.settings = datasource.settings; + this.devSettings = datasource.devSettings; + const adapterContext = new AdapterContext(config || { mock: false }); + this.adapter = adapterContext as unknown as IAdapter; + } + + // todo: should it be published? + dispose(): void { + reactiveConfigStorage.delete(this.adapter.id); + wantedStorage.delete(this.adapter.id); + } +} + +export const makeDatasource = ( + getAdapterConfig?: () => IAdapterConfig +) => + class extends DatasourceGeneric { + constructor(datasource: IDatasourceParams) { + const config = + typeof getAdapterConfig === 'function' ? getAdapterConfig() : getDefaultAdapterConfig(); + super(datasource, config); + } + } as DSClassType; + +export const Datasource = makeDatasource(); diff --git a/src/classes/domRoutines.ts b/src/classes/domRoutines.ts index 9763e46b..c8bf9a9f 100644 --- a/src/classes/domRoutines.ts +++ b/src/classes/domRoutines.ts @@ -3,7 +3,6 @@ import { Direction } from '../inputs/index'; import { IRoutines, ItemAdapter } from '../interfaces/index'; export class Routines implements IRoutines { - readonly settings: IRoutines['settings']; readonly element: HTMLElement; readonly viewport: HTMLElement; @@ -96,15 +95,15 @@ export class Routines implements IRoutines { const height = window.innerHeight; const width = window.innerWidth; return { - 'height': height, - 'width': width, - 'top': 0, - 'bottom': height, - 'left': 0, - 'right': width, - 'x': 0, - 'y': 0, - 'toJSON': () => null, + height: height, + width: width, + top: 0, + bottom: height, + left: 0, + right: width, + x: 0, + y: 0, + toJSON: () => null }; } @@ -139,7 +138,7 @@ export class Routines implements IRoutines { const { horizontal } = this.settings; const params = this.getElementParams(element); const isFwd = direction === Direction.forward; - return params[isFwd ? (horizontal ? 'right' : 'bottom') : (horizontal ? 'left' : 'top')]; + return params[isFwd ? (horizontal ? 'right' : 'bottom') : horizontal ? 'left' : 'top']; } getViewportEdge(direction: Direction): number { @@ -147,7 +146,7 @@ export class Routines implements IRoutines { if (window) { const params = this.getWindowParams(); const isFwd = direction === Direction.forward; - return params[isFwd ? (horizontal ? 'right' : 'bottom') : (horizontal ? 'left' : 'top')]; + return params[isFwd ? (horizontal ? 'right' : 'bottom') : horizontal ? 'left' : 'top']; } return this.getEdge(this.viewport, direction); } @@ -175,8 +174,7 @@ export class Routines implements IRoutines { element.scrollIntoView(argument); } - // eslint-disable-next-line @typescript-eslint/no-unused-vars - render(cb: () => void, params: { items: ItemAdapter[] }): () => void { + render(cb: () => void, _params: { items: ItemAdapter[] }): () => void { const timeoutId = setTimeout(() => cb()); return () => clearTimeout(timeoutId); } @@ -191,5 +189,4 @@ export class Routines implements IRoutines { eventReceiver.addEventListener('scroll', handler); return () => eventReceiver.removeEventListener('scroll', handler); } - } diff --git a/src/classes/logger.ts b/src/classes/logger.ts index 9099d485..e09b174c 100644 --- a/src/classes/logger.ts +++ b/src/classes/logger.ts @@ -5,7 +5,6 @@ import { IPackages, ProcessSubject } from '../interfaces/index'; type LogType = [unknown?, ...unknown[]]; export class Logger { - readonly debug: boolean; readonly immediateLog: boolean; readonly logTime: boolean; @@ -25,37 +24,53 @@ export class Logger { this.immediateLog = settings.immediateLog; this.logTime = settings.logTime; this.logColor = settings.logColor; - this.getTime = (): string => - scroller.state && ` // time: ${scroller.state.time}`; + this.getTime = (): string => scroller.state && ` // time: ${scroller.state.time}`; this.getStat = (): string => { const { buffer, viewport } = scroller; const first = buffer.getFirstVisibleItem(); const last = buffer.getLastVisibleItem(); - return 'pos: ' + viewport.scrollPosition + ', ' + - 'size: ' + viewport.getScrollableSize() + ', ' + - 'bwd_p: ' + viewport.paddings.backward.size + ', ' + - 'fwd_p: ' + viewport.paddings.forward.size + ', ' + - 'default: ' + (buffer.defaultSize || 'no') + ', ' + - 'items: ' + buffer.getVisibleItemsCount() + ', ' + - 'range: ' + (first && last ? `[${first.$index}..${last.$index}]` : 'no'); + return ( + 'pos: ' + + viewport.scrollPosition + + ', ' + + 'size: ' + + viewport.getScrollableSize() + + ', ' + + 'bwd_p: ' + + viewport.paddings.backward.size + + ', ' + + 'fwd_p: ' + + viewport.paddings.forward.size + + ', ' + + 'default: ' + + (buffer.defaultSize || 'no') + + ', ' + + 'items: ' + + buffer.getVisibleItemsCount() + + ', ' + + 'range: ' + + (first && last ? `[${first.$index}..${last.$index}]` : 'no') + ); }; this.getFetchRange = (): string => { - const { first: { index: first }, last: { index: last } } = scroller.state.fetch; - return !Number.isNaN(first) && !Number.isNaN(last) - ? `[${first}..${last}]` - : 'no'; + const { + first: { index: first }, + last: { index: last } + } = scroller.state.fetch; + return !Number.isNaN(first) && !Number.isNaN(last) ? `[${first}..${last}]` : 'no'; }; this.getLoopId = (): string => scroller.state.cycle.loopId; this.getLoopIdNext = (): string => scroller.state.cycle.loopIdNext; this.getWorkflowCycleData = (): string => `${settings.instanceIndex}-${scroller.state.cycle.count}`; this.getScrollPosition = () => scroller.routines.getScrollPosition(); - this.log(() => - 'vscroll Workflow has been started, ' + - `core: ${packageInfo.core.name} v${packageInfo.core.version}, ` + - `consumer: ${packageInfo.consumer.name} v${packageInfo.consumer.version}, ` + - `scroller instance: ${settings.instanceIndex}, adapter ` + - (!adapter ? 'is not instantiated' : `instance: ${adapter.id}`) + this.log( + () => + 'vscroll Workflow has been started, ' + + `core: ${packageInfo.core.name} v${packageInfo.core.version}, ` + + `consumer: ${packageInfo.consumer.name} v${packageInfo.consumer.version}, ` + + `scroller instance: ${settings.instanceIndex}, adapter ` + + (!adapter ? 'is not instantiated' : `instance: ${adapter.id}`) ); } @@ -63,30 +78,30 @@ export class Logger { this.log(() => [ str, stringify - ? JSON.stringify(obj, (k, v) => { - if (Number.isNaN(v)) { - return 'NaN'; - } - if (v === Infinity) { - return 'Infinity'; - } - if (v === -Infinity) { - return '-Infinity'; - } - if (v instanceof Element) { - return 'HTMLElement'; - } - if (v instanceof HTMLDocument) { - return 'HTMLDocument'; - } - if (typeof v === 'function') { - return 'Function'; - } - return v; - }) - .replace(/"/g, '') - .replace(/(\{|:|,)/g, '$1 ') - .replace(/(\})/g, ' $1') + ? JSON.stringify(obj, (_, v) => { + if (Number.isNaN(v)) { + return 'NaN'; + } + if (v === Infinity) { + return 'Infinity'; + } + if (v === -Infinity) { + return '-Infinity'; + } + if (v instanceof Element) { + return 'HTMLElement'; + } + if (v instanceof HTMLDocument) { + return 'HTMLDocument'; + } + if (typeof v === 'function') { + return 'Function'; + } + return v; + }) + .replace(/"/g, '') + .replace(/(\{|:|,)/g, '$1 ') + .replace(/(\})/g, ' $1') : obj ]); } @@ -118,9 +133,7 @@ export class Logger { } prepareForLog(data: unknown): unknown { - return data instanceof Event && data.target - ? this.getScrollPosition() - : data; + return data instanceof Event && data.target ? this.getScrollPosition() : data; } logProcess(data: ProcessSubject): void { @@ -131,23 +144,23 @@ export class Logger { // inner loop start-end log const loopLog: string[] = []; - if ( - process === CommonProcess.init && status === Status.next - ) { + if (process === CommonProcess.init && status === Status.next) { const loopStart = `---=== loop ${this.getLoopIdNext()} start`; loopLog.push(this.logColor ? `%c${loopStart}` : loopStart); - } else if ( - process === CommonProcess.end - ) { + } else if (process === CommonProcess.end) { const loopDone = `---=== loop ${this.getLoopId()} done`; loopLog.push(this.logColor ? `%c${loopDone}` : loopDone); const parent = payload && payload.process; - if (status === Status.next && (parent !== AdapterProcess.reset && parent !== AdapterProcess.reload)) { + if ( + status === Status.next && + parent !== AdapterProcess.reset && + parent !== AdapterProcess.reload + ) { loopLog[0] += `, loop ${this.getLoopIdNext()} start`; } } if (loopLog.length) { - this.log(() => this.logColor ? [...loopLog, 'color: #006600;'] : loopLog); + this.log(() => (this.logColor ? [...loopLog, 'color: #006600;'] : loopLog)); } } @@ -156,7 +169,10 @@ export class Logger { if (this.logColor) { const border = start ? '1px 0 0 1px' : '0 0 1px 1px'; const logStyles = `color: #0000aa; border: solid #555 1px; border-width: ${border}; margin-left: -2px`; - this.log(() => [`%c ~~~ WF Cycle ${logData} ${start ? 'STARTED' : 'FINALIZED'} ~~~ `, logStyles]); + this.log(() => [ + `%c ~~~ WF Cycle ${logData} ${start ? 'STARTED' : 'FINALIZED'} ~~~ `, + logStyles + ]); } else { this.log(() => [` ~~~ WF Cycle ${logData} ${start ? 'STARTED' : 'FINALIZED'} ~~~ `]); } @@ -166,7 +182,10 @@ export class Logger { if (this.debug) { if (this.logColor) { const logStyles = ['color: #a00;', 'color: #000']; - this.log(() => ['error:%c' + (str ? ` ${str}` : '') + `%c (loop ${this.getLoopIdNext()})`, ...logStyles]); + this.log(() => [ + 'error:%c' + (str ? ` ${str}` : '') + `%c (loop ${this.getLoopIdNext()})`, + ...logStyles + ]); } else { this.log(() => ['error:' + (str ? ` ${str}` : '') + ` (loop ${this.getLoopIdNext()})`]); } @@ -177,9 +196,7 @@ export class Logger { if (!this.debug) { return; } - const params = ( - args === void 0 ? [] : (Array.isArray(args) ? args : [args]) - ) + const params = (args === void 0 ? [] : Array.isArray(args) ? args : [args]) .map((arg: unknown) => { if (typeof arg === 'function') { return 'func'; diff --git a/src/classes/paddings.ts b/src/classes/paddings.ts index 380a3aa1..e087582c 100644 --- a/src/classes/paddings.ts +++ b/src/classes/paddings.ts @@ -1,93 +1,94 @@ -import { Routines } from './domRoutines'; -import { Settings } from './settings'; -import { Direction } from '../inputs/index'; - -export class Padding { - - element: HTMLElement; - direction: Direction; - routines: Routines; - - constructor(direction: Direction, routines: Routines) { - const found = routines.findPaddingElement(direction); - routines.checkElement(found as HTMLElement); - this.element = found as HTMLElement; - this.direction = direction; - this.routines = routines; - } - - reset(size?: number): void { - this.size = size || 0; - } - - get size(): number { - return this.routines.getSizeStyle(this.element); - } - - set size(value: number) { - this.routines.setSizeStyle(this.element, value); - } - -} - -export class Paddings { - settings: Settings; - forward: Padding; - backward: Padding; - - constructor(routines: Routines, settings: Settings) { - this.settings = settings; - this.forward = new Padding(Direction.forward, routines); - this.backward = new Padding(Direction.backward, routines); - } - - byDirection(direction: Direction, opposite?: boolean): Padding { - return direction === Direction.backward - ? (opposite ? this.forward : this.backward) - : (opposite ? this.backward : this.forward); - } - - reset(viewportSize: number, startIndex: number, offset: number): void { - const positive = this.getPositiveSize(startIndex, viewportSize, offset); - const negative = this.getNegativeSize(startIndex); - if (this.settings.inverse) { - this.forward.reset(negative); - this.backward.reset(positive); - const diff = viewportSize - this.backward.size - offset; - if (diff > 0) { - this.backward.size += diff; - this.forward.size -= diff; - } - } else { - this.forward.reset(positive); - this.backward.reset(negative); - const diff = viewportSize - this.forward.size - offset; - if (diff > 0) { - this.backward.size -= diff; - this.forward.size += diff; - } - } - - } - - getPositiveSize(startIndex: number, viewportSize: number, offset: number): number { - const { settings } = this; - let positiveSize = viewportSize; - if (isFinite(settings.maxIndex)) { - positiveSize = (settings.maxIndex - startIndex + 1) * settings.itemSize; - } - if (offset) { - positiveSize = Math.max(positiveSize - offset, 0); - } - return positiveSize; - } - - getNegativeSize(startIndex: number): number { - const { settings } = this; - let negativeSize = 0; - if (isFinite(settings.minIndex)) { - negativeSize = (startIndex - settings.minIndex) * settings.itemSize; - } - return negativeSize; - } -} +import { Routines } from './domRoutines'; +import { Settings } from './settings'; +import { Direction } from '../inputs/index'; + +export class Padding { + element: HTMLElement; + direction: Direction; + routines: Routines; + + constructor(direction: Direction, routines: Routines) { + const found = routines.findPaddingElement(direction); + routines.checkElement(found as HTMLElement); + this.element = found as HTMLElement; + this.direction = direction; + this.routines = routines; + } + + reset(size?: number): void { + this.size = size || 0; + } + + get size(): number { + return this.routines.getSizeStyle(this.element); + } + + set size(value: number) { + this.routines.setSizeStyle(this.element, value); + } +} + +export class Paddings { + settings: Settings; + forward: Padding; + backward: Padding; + + constructor(routines: Routines, settings: Settings) { + this.settings = settings; + this.forward = new Padding(Direction.forward, routines); + this.backward = new Padding(Direction.backward, routines); + } + + byDirection(direction: Direction, opposite?: boolean): Padding { + return direction === Direction.backward + ? opposite + ? this.forward + : this.backward + : opposite + ? this.backward + : this.forward; + } + + reset(viewportSize: number, startIndex: number, offset: number): void { + const positive = this.getPositiveSize(startIndex, viewportSize, offset); + const negative = this.getNegativeSize(startIndex); + if (this.settings.inverse) { + this.forward.reset(negative); + this.backward.reset(positive); + const diff = viewportSize - this.backward.size - offset; + if (diff > 0) { + this.backward.size += diff; + this.forward.size -= diff; + } + } else { + this.forward.reset(positive); + this.backward.reset(negative); + const diff = viewportSize - this.forward.size - offset; + if (diff > 0) { + this.backward.size -= diff; + this.forward.size += diff; + } + } + } + + getPositiveSize(startIndex: number, viewportSize: number, offset: number): number { + const { settings } = this; + let positiveSize = viewportSize; + if (isFinite(settings.maxIndex)) { + positiveSize = (settings.maxIndex - startIndex + 1) * settings.itemSize; + } + if (offset) { + positiveSize = Math.max(positiveSize - offset, 0); + } + return positiveSize; + } + + getNegativeSize(startIndex: number): number { + const { settings } = this; + let negativeSize = 0; + if (isFinite(settings.minIndex)) { + negativeSize = (startIndex - settings.minIndex) * settings.itemSize; + } + return negativeSize; + } +} diff --git a/src/classes/reactive.ts b/src/classes/reactive.ts index b2056b50..75a0c6ab 100644 --- a/src/classes/reactive.ts +++ b/src/classes/reactive.ts @@ -12,7 +12,6 @@ interface Options { } export class Reactive { - private initialValue: T; private value: T; private id: number; diff --git a/src/classes/settings.ts b/src/classes/settings.ts index 4c67a24a..d758b2f5 100644 --- a/src/classes/settings.ts +++ b/src/classes/settings.ts @@ -1,8 +1,20 @@ -import { SETTINGS, DEV_SETTINGS, validate, validateOne, VALIDATORS, SizeStrategy, Direction } from '../inputs/index'; -import { Settings as ISettings, DevSettings as IDevSettings, ICommonProps, ItemsProcessor } from '../interfaces/index'; +import { + SETTINGS, + DEV_SETTINGS, + validate, + validateOne, + VALIDATORS, + SizeStrategy, + Direction +} from '../inputs/index'; +import { + Settings as ISettings, + DevSettings as IDevSettings, + ICommonProps, + ItemsProcessor +} from '../interfaces/index'; export class Settings implements ISettings, IDevSettings { - // user settings adapter: boolean; startIndex: number; @@ -25,7 +37,7 @@ export class Settings implements ISettings, IDevSettings { * Default value: false. * @type {boolean} */ - debug: boolean; // if true, + debug: boolean; // if true, /** * Development setting. @@ -139,7 +151,9 @@ export class Settings implements ISettings, IDevSettings { viewport: HTMLElement | null; constructor( - settings: ISettings | undefined, devSettings: IDevSettings | undefined, instanceIndex: number + settings: ISettings | undefined, + devSettings: IDevSettings | undefined, + instanceIndex: number ) { this.parseInput(settings, SETTINGS); this.parseInput(devSettings, DEV_SETTINGS); @@ -149,7 +163,10 @@ export class Settings implements ISettings, IDevSettings { // todo: min/max indexes must be ignored if infinite mode is enabled ?? } - parseInput(input: ISettings | IDevSettings | undefined, props: ICommonProps): void { + parseInput( + input: ISettings | IDevSettings | undefined, + props: ICommonProps + ): void { const result = validate(input, props); if (!result.isValid) { throw new Error('Invalid settings'); diff --git a/src/classes/state.ts b/src/classes/state.ts index 5869b977..a609fc54 100644 --- a/src/classes/state.ts +++ b/src/classes/state.ts @@ -1,90 +1,90 @@ -import { Settings } from './settings'; -import { Reactive } from './reactive'; -import { WorkflowCycleModel } from './state/cycle'; -import { FetchModel } from './state/fetch'; -import { ClipModel } from './state/clip'; -import { RenderModel } from './state/render'; -import { ScrollModel } from './state/scroll'; -import { State as IState, IPackages, ProcessName } from '../interfaces/index'; - -export class State implements IState { - - readonly packageInfo: IPackages; - private settings: Settings; - initTime: number; - paused: Reactive; - - cycle: WorkflowCycleModel; - fetch: FetchModel; - clip: ClipModel; - render: RenderModel; - scroll: ScrollModel; - - get time(): number { - return Number(new Date()) - this.initTime; - } - - constructor(packageInfo: IPackages, settings: Settings, state?: IState) { - this.packageInfo = packageInfo; - this.settings = settings; - this.initTime = Number(new Date()); - this.paused = new Reactive(false); - - this.cycle = new WorkflowCycleModel(this.settings.instanceIndex, state ? state.cycle : void 0); - this.fetch = new FetchModel(settings.directionPriority); - this.clip = new ClipModel(); - this.render = new RenderModel(); - this.scroll = new ScrollModel(); - } - - startWorkflowCycle(isInitial: boolean, initiator: ProcessName): void { - this.cycle.start(isInitial, initiator); - } - - endWorkflowCycle(count: number): void { - this.cycle.end(count); - } - - startInnerLoop(): { process?: ProcessName, doRender?: boolean } { - const { cycle, scroll: scroll, fetch, render, clip } = this; - - cycle.innerLoop.start(); - scroll.positionBeforeAsync = null; - - if (!fetch.simulate) { - fetch.reset(); - } - clip.reset(clip.force); - render.reset(); - - return { - ...(cycle.innerLoop.first ? { - process: cycle.initiator, - doRender: fetch.simulate && fetch.items.length > 0 - } : {}) - }; - } - - endInnerLoop(): void { - const { fetch, clip, render, cycle } = this; - fetch.stopSimulate(); - clip.reset(true); - if (fetch.cancel) { - fetch.cancel(); - fetch.cancel = null; - } - if (render.cancel) { - render.cancel(); - render.cancel = null; - } - cycle.innerLoop.done(); - } - - dispose(): void { - this.scroll.stop(); - this.cycle.dispose(); - this.paused.dispose(); - this.endInnerLoop(); - } - -} +import { Settings } from './settings'; +import { Reactive } from './reactive'; +import { WorkflowCycleModel } from './state/cycle'; +import { FetchModel } from './state/fetch'; +import { ClipModel } from './state/clip'; +import { RenderModel } from './state/render'; +import { ScrollModel } from './state/scroll'; +import { State as IState, IPackages, ProcessName } from '../interfaces/index'; + +export class State implements IState { + readonly packageInfo: IPackages; + private settings: Settings; + initTime: number; + paused: Reactive; + + cycle: WorkflowCycleModel; + fetch: FetchModel; + clip: ClipModel; + render: RenderModel; + scroll: ScrollModel; + + get time(): number { + return Number(new Date()) - this.initTime; + } + + constructor(packageInfo: IPackages, settings: Settings, state?: IState) { + this.packageInfo = packageInfo; + this.settings = settings; + this.initTime = Number(new Date()); + this.paused = new Reactive(false); + + this.cycle = new WorkflowCycleModel(this.settings.instanceIndex, state ? state.cycle : void 0); + this.fetch = new FetchModel(settings.directionPriority); + this.clip = new ClipModel(); + this.render = new RenderModel(); + this.scroll = new ScrollModel(); + } + + startWorkflowCycle(isInitial: boolean, initiator: ProcessName): void { + this.cycle.start(isInitial, initiator); + } + + endWorkflowCycle(count: number): void { + this.cycle.end(count); + } + + startInnerLoop(): { process?: ProcessName; doRender?: boolean } { + const { cycle, scroll: scroll, fetch, render, clip } = this; + + cycle.innerLoop.start(); + scroll.positionBeforeAsync = null; + + if (!fetch.simulate) { + fetch.reset(); + } + clip.reset(clip.force); + render.reset(); + + return { + ...(cycle.innerLoop.first + ? { + process: cycle.initiator, + doRender: fetch.simulate && fetch.items.length > 0 + } + : {}) + }; + } + + endInnerLoop(): void { + const { fetch, clip, render, cycle } = this; + fetch.stopSimulate(); + clip.reset(true); + if (fetch.cancel) { + fetch.cancel(); + fetch.cancel = null; + } + if (render.cancel) { + render.cancel(); + render.cancel = null; + } + cycle.innerLoop.done(); + } + + dispose(): void { + this.scroll.stop(); + this.cycle.dispose(); + this.paused.dispose(); + this.endInnerLoop(); + } +} diff --git a/src/classes/state/clip.ts b/src/classes/state/clip.ts index f2fa558c..90b145a4 100644 --- a/src/classes/state/clip.ts +++ b/src/classes/state/clip.ts @@ -20,5 +20,4 @@ export class ClipModel { this.forceBackward = false; } } - } diff --git a/src/classes/state/fetch.ts b/src/classes/state/fetch.ts index bbbe04a7..fe52d4b2 100644 --- a/src/classes/state/fetch.ts +++ b/src/classes/state/fetch.ts @@ -122,7 +122,7 @@ export class FetchModel { } get hasNewItems(): boolean { - return !!((this._newItemsData && this._newItemsData.length)); + return !!(this._newItemsData && this._newItemsData.length); } get index(): number { @@ -130,7 +130,9 @@ export class FetchModel { } get count(): number { - return !isNaN(this.first.index) && !isNaN(this.last.index) ? this.last.index - this.first.index + 1 : 0; + return !isNaN(this.first.index) && !isNaN(this.last.index) + ? this.last.index - this.first.index + 1 + : 0; } shouldCheckPreSizeExpectation(lastBufferedIndex: number): boolean { diff --git a/src/classes/viewport.ts b/src/classes/viewport.ts index b9d75ad7..f2733d06 100644 --- a/src/classes/viewport.ts +++ b/src/classes/viewport.ts @@ -7,7 +7,6 @@ import { Logger } from './logger'; import { Direction } from '../inputs/index'; export class Viewport { - offset: number; paddings: Paddings; @@ -40,7 +39,9 @@ export class Viewport { this.routines.setScrollPosition(value); const position = this.scrollPosition; this.logger.log(() => [ - 'setting scroll position at', position, ...(position !== value ? [`(${value})`] : []) + 'setting scroll position at', + position, + ...(position !== value ? [`(${value})`] : []) ]); return position; } @@ -81,11 +82,15 @@ export class Viewport { return this.routines.findItemElement(id); } - getEdgeVisibleItem(items: Item[], direction: Direction): { item?: Item, index: number, diff: number } { + getEdgeVisibleItem( + items: Item[], + direction: Direction + ): { item?: Item; index: number; diff: number } { const bwd = direction === Direction.backward; const opposite = bwd ? Direction.forward : Direction.backward; const viewportEdge = this.getEdge(direction); - let item, diff = 0; + let item, + diff = 0; for ( let i = bwd ? 0 : items.length - 1; bwd ? i <= items.length - 1 : i >= 0; @@ -93,12 +98,11 @@ export class Viewport { ) { const itemEdge = this.routines.getEdge(items[i].element, opposite); diff = itemEdge - viewportEdge; - if (bwd && diff > 0 || !bwd && diff < 0) { + if ((bwd && diff > 0) || (!bwd && diff < 0)) { item = items[i]; break; } } return { item, index: item ? item.$index : NaN, diff }; } - } diff --git a/src/index.ts b/src/index.ts index 6506f441..01c4158a 100644 --- a/src/index.ts +++ b/src/index.ts @@ -14,7 +14,7 @@ import { IAdapterConfig, ItemAdapter, IAdapter, - Item, + Item } from './interfaces/index'; // export entities @@ -28,7 +28,7 @@ export { EMPTY_ITEM, getDefaultAdapterProps, Direction, - SizeStrategy, + SizeStrategy }; // export interfaces @@ -40,5 +40,5 @@ export { IAdapterConfig, ItemAdapter as IAdapterItem, IAdapter, - Item, + Item }; diff --git a/src/inputs/adapter.ts b/src/inputs/adapter.ts index 30df33fc..eecd6447 100644 --- a/src/inputs/adapter.ts +++ b/src/inputs/adapter.ts @@ -14,10 +14,10 @@ const { FUNC_WITH_X_TO_Y_ARGUMENTS, ONE_OF_MUST, ONE_OF_CAN, - OR, + OR } = VALIDATORS; -enum AdapterNoParams { } +enum AdapterNoParams {} const NO_METHOD_PARAMS: ICommonProps = {}; const RESET_METHOD_PARAMS: ICommonProps = { @@ -29,24 +29,24 @@ const RESET_METHOD_PARAMS: ICommonProps = { }, [AdapterResetParams.devSettings]: { validators: [OBJECT] - }, + } }; enum AdapterReloadParams { - reloadIndex = 'reloadIndex', + reloadIndex = 'reloadIndex' } const RELOAD_METHOD_PARAMS: ICommonProps = { [AdapterReloadParams.reloadIndex]: { validators: [INTEGER] - }, + } }; enum AdapterPrependParams { items = 'items', bof = 'bof', increase = 'increase', - virtualize = 'virtualize', + virtualize = 'virtualize' } const PREPEND_METHOD_PARAMS: ICommonProps = { @@ -65,14 +65,14 @@ const PREPEND_METHOD_PARAMS: ICommonProps = { [AdapterPrependParams.virtualize]: { validators: [BOOLEAN, ONE_OF_CAN([AdapterPrependParams.bof])], defaultValue: false - }, + } }; enum AdapterAppendParams { items = 'items', eof = 'eof', decrease = 'decrease', - virtualize = 'virtualize', + virtualize = 'virtualize' } const APPEND_METHOD_PARAMS: ICommonProps = { @@ -91,13 +91,13 @@ const APPEND_METHOD_PARAMS: ICommonProps = { [AdapterPrependParams.virtualize]: { validators: [BOOLEAN, ONE_OF_CAN([AdapterAppendParams.eof])], defaultValue: false - }, + } }; enum AdapterRemoveParams { predicate = 'predicate', indexes = 'indexes', - increase = 'increase', + increase = 'increase' } const REMOVE_METHOD_PARAMS: ICommonProps = { @@ -110,12 +110,12 @@ const REMOVE_METHOD_PARAMS: ICommonProps = { [AdapterRemoveParams.increase]: { validators: [BOOLEAN], defaultValue: false - }, + } }; enum AdapterClipParams { backwardOnly = 'backwardOnly', - forwardOnly = 'forwardOnly', + forwardOnly = 'forwardOnly' } const CLIP_METHOD_PARAMS: ICommonProps = { @@ -126,7 +126,7 @@ const CLIP_METHOD_PARAMS: ICommonProps = { [AdapterClipParams.forwardOnly]: { validators: [BOOLEAN, ONE_OF_CAN([AdapterClipParams.backwardOnly])], defaultValue: false - }, + } }; enum AdapterInsertParams { @@ -135,7 +135,7 @@ enum AdapterInsertParams { after = 'after', beforeIndex = 'beforeIndex', afterIndex = 'afterIndex', - decrease = 'decrease', + decrease = 'decrease' } const INSERT_METHOD_PARAMS: ICommonProps = { @@ -144,35 +144,55 @@ const INSERT_METHOD_PARAMS: ICommonProps = { mandatory: true }, [AdapterInsertParams.before]: { - validators: [FUNC_WITH_X_ARGUMENTS(1), ONE_OF_MUST([ - AdapterInsertParams.after, AdapterInsertParams.beforeIndex, AdapterInsertParams.afterIndex - ])] + validators: [ + FUNC_WITH_X_ARGUMENTS(1), + ONE_OF_MUST([ + AdapterInsertParams.after, + AdapterInsertParams.beforeIndex, + AdapterInsertParams.afterIndex + ]) + ] }, [AdapterInsertParams.after]: { - validators: [FUNC_WITH_X_ARGUMENTS(1), ONE_OF_MUST([ - AdapterInsertParams.before, AdapterInsertParams.beforeIndex, AdapterInsertParams.afterIndex - ])] + validators: [ + FUNC_WITH_X_ARGUMENTS(1), + ONE_OF_MUST([ + AdapterInsertParams.before, + AdapterInsertParams.beforeIndex, + AdapterInsertParams.afterIndex + ]) + ] }, [AdapterInsertParams.beforeIndex]: { - validators: [INTEGER, ONE_OF_MUST([ - AdapterInsertParams.before, AdapterInsertParams.after, AdapterInsertParams.afterIndex - ])] + validators: [ + INTEGER, + ONE_OF_MUST([ + AdapterInsertParams.before, + AdapterInsertParams.after, + AdapterInsertParams.afterIndex + ]) + ] }, [AdapterInsertParams.afterIndex]: { - validators: [INTEGER, ONE_OF_MUST([ - AdapterInsertParams.before, AdapterInsertParams.after, AdapterInsertParams.beforeIndex - ])] + validators: [ + INTEGER, + ONE_OF_MUST([ + AdapterInsertParams.before, + AdapterInsertParams.after, + AdapterInsertParams.beforeIndex + ]) + ] }, [AdapterInsertParams.decrease]: { validators: [BOOLEAN], defaultValue: false - }, + } }; enum AdapterReplaceParams { items = 'items', predicate = 'predicate', - fixRight = 'fixRight', + fixRight = 'fixRight' } const REPLACE_METHOD_PARAMS: ICommonProps = { @@ -192,7 +212,7 @@ const REPLACE_METHOD_PARAMS: ICommonProps = { enum AdapterUpdateParams { predicate = 'predicate', - fixRight = 'fixRight', + fixRight = 'fixRight' } const UPDATE_METHOD_PARAMS: ICommonProps = { @@ -203,7 +223,7 @@ const UPDATE_METHOD_PARAMS: ICommonProps = { [AdapterUpdateParams.fixRight]: { validators: [BOOLEAN], defaultValue: false - }, + } }; enum AdapterFixParams { @@ -212,7 +232,7 @@ enum AdapterFixParams { maxIndex = 'maxIndex', updater = 'updater', scrollToItem = 'scrollToItem', - scrollToItemOpt = 'scrollToItemOpt', + scrollToItemOpt = 'scrollToItemOpt' } const FIX_METHOD_PARAMS: ICommonProps = { @@ -233,7 +253,7 @@ const FIX_METHOD_PARAMS: ICommonProps = { }, [AdapterFixParams.scrollToItemOpt]: { validators: [OR([BOOLEAN, OBJECT])] - }, + } }; export const AdapterMethods: AdapterProcessMap<{ [key: string]: string }> = { @@ -248,7 +268,7 @@ export const AdapterMethods: AdapterProcessMap<{ [key: string]: string }> = { [Process.replace]: AdapterReplaceParams, [Process.update]: AdapterUpdateParams, [Process.pause]: AdapterNoParams, - [Process.fix]: AdapterFixParams, + [Process.fix]: AdapterFixParams }; export const ADAPTER_METHODS: AdapterProcessMap> = { @@ -263,5 +283,5 @@ export const ADAPTER_METHODS: AdapterProcessMap> = { [Process.replace]: REPLACE_METHOD_PARAMS, [Process.update]: UPDATE_METHOD_PARAMS, [Process.pause]: NO_METHOD_PARAMS, - [Process.fix]: FIX_METHOD_PARAMS, + [Process.fix]: FIX_METHOD_PARAMS }; diff --git a/src/inputs/datasource.ts b/src/inputs/datasource.ts index bf166cbe..8b406915 100644 --- a/src/inputs/datasource.ts +++ b/src/inputs/datasource.ts @@ -6,7 +6,7 @@ const { OBJECT, FUNC_WITH_X_AND_MORE_ARGUMENTS } = VALIDATORS; export enum DatasourceProps { get = 'get', settings = 'settings', - devSettings = 'devSettings', + devSettings = 'devSettings' } export const DATASOURCE: ICommonProps = { diff --git a/src/inputs/index.ts b/src/inputs/index.ts index 8ecf4445..c8321afa 100644 --- a/src/inputs/index.ts +++ b/src/inputs/index.ts @@ -18,5 +18,5 @@ export { DEV_SETTINGS, AdapterMethods, ADAPTER_METHODS, - WORKFLOW, + WORKFLOW }; diff --git a/src/inputs/settings.ts b/src/inputs/settings.ts index 9ace0a03..9cebcb4c 100644 --- a/src/inputs/settings.ts +++ b/src/inputs/settings.ts @@ -2,7 +2,8 @@ import { VALIDATORS } from './validation'; import { ICommonProps } from '../interfaces/index'; import { SizeStrategy, Direction } from './common'; -const { NUMBER, INTEGER, INTEGER_UNLIMITED, MORE_OR_EQUAL, BOOLEAN, ELEMENT, FUNC, OR, ENUM } = VALIDATORS; +const { NUMBER, INTEGER, INTEGER_UNLIMITED, MORE_OR_EQUAL, BOOLEAN, ELEMENT, FUNC, OR, ENUM } = + VALIDATORS; enum Settings { adapter = 'adapter', @@ -18,7 +19,7 @@ enum Settings { viewportElement = 'viewportElement', inverse = 'inverse', onBeforeClip = 'onBeforeClip', - sizeStrategy = 'sizeStrategy', + sizeStrategy = 'sizeStrategy' } enum DevSettings { @@ -33,7 +34,7 @@ enum DevSettings { cacheData = 'cacheData', cacheOnReload = 'cacheOnReload', dismissOverflowAnchor = 'dismissOverflowAnchor', - directionPriority = 'directionPriority', + directionPriority = 'directionPriority' } export const MIN = { @@ -42,7 +43,7 @@ export const MIN = { [Settings.padding]: 0.01, [DevSettings.throttle]: 0, [DevSettings.initDelay]: 0, - [DevSettings.initWindowDelay]: 0, + [DevSettings.initWindowDelay]: 0 }; export const SETTINGS: ICommonProps = { @@ -101,7 +102,7 @@ export const SETTINGS: ICommonProps = { [Settings.sizeStrategy]: { validators: [ENUM(SizeStrategy)], defaultValue: SizeStrategy.Average - }, + } }; export const DEV_SETTINGS: ICommonProps = { @@ -152,5 +153,5 @@ export const DEV_SETTINGS: ICommonProps = { [DevSettings.directionPriority]: { validators: [ENUM(Direction)], defaultValue: Direction.backward - }, + } }; diff --git a/src/inputs/validation.ts b/src/inputs/validation.ts index 58399edc..16d45a25 100644 --- a/src/inputs/validation.ts +++ b/src/inputs/validation.ts @@ -5,7 +5,7 @@ import { IValidatedData, IValidatedCommonProps, ICommonProps, - ICommonProp, + ICommonProp } from '../interfaces/index'; export enum ValidatorType { @@ -24,16 +24,14 @@ export enum ValidatorType { oneOfCan = 'can be present as only one item of {arg1} list', oneOfMust = 'must be present as only one item of {arg1} list', or = 'must satisfy at least 1 validator from {arg1} list', - enum = 'must belong to {arg1} list', + enum = 'must belong to {arg1} list' } const getError = (msg: ValidatorType, args?: string[]) => (args || ['']).reduce((acc, arg, index) => acc.replace(`{arg${index + 1}}`, arg), msg); const getNumber = (value: unknown): number => - typeof value === 'number' || (typeof value === 'string' && value !== '') - ? Number(value) - : NaN; + typeof value === 'number' || (typeof value === 'string' && value !== '') ? Number(value) : NaN; const onNumber = (value: unknown): ValidatedValue => { const parsedValue = getNumber(value); @@ -69,22 +67,24 @@ const onIntegerUnlimited = (value: unknown): ValidatedValue => { return { value: parsedValue, isSet: true, isValid: !errors.length, errors }; }; -const onMoreOrEqual = (limit: number, fallback?: boolean) => (value: unknown): ValidatedValue => { - const result = onNumber(value); - if (!result.isValid) { - return result; - } - let parsedValue = result.value as number; - const errors = []; - if (parsedValue < limit) { - if (!fallback) { - errors.push(getError(ValidatorType.moreOrEqual, [String(limit)])); - } else { - parsedValue = limit; +const onMoreOrEqual = + (limit: number, fallback?: boolean) => + (value: unknown): ValidatedValue => { + const result = onNumber(value); + if (!result.isValid) { + return result; } - } - return { value: parsedValue, isSet: true, isValid: !errors.length, errors }; -}; + let parsedValue = result.value as number; + const errors = []; + if (parsedValue < limit) { + if (!fallback) { + errors.push(getError(ValidatorType.moreOrEqual, [String(limit)])); + } else { + parsedValue = limit; + } + } + return { value: parsedValue, isSet: true, isValid: !errors.length, errors }; + }; const onBoolean = (value: unknown): ValidatedValue => { const errors = []; @@ -159,81 +159,93 @@ const onFunctionWithXArguments = (argsCount: number) => (value: unknown) => { return { value: value as Func, isSet: true, isValid: !errors.length, errors }; }; -const onFunctionWithXAndMoreArguments = (argsCount: number) => (value: unknown): ValidatedValue => { - const result = onFunction(value); - if (!result.isValid) { - return result; - } - value = result.value; - const errors = []; - if ((value as Func).length < argsCount) { - errors.push(getError(ValidatorType.funcOfxArguments, [String(argsCount)])); - } - return { value: value as Func, isSet: true, isValid: !errors.length, errors }; -}; +const onFunctionWithXAndMoreArguments = + (argsCount: number) => + (value: unknown): ValidatedValue => { + const result = onFunction(value); + if (!result.isValid) { + return result; + } + value = result.value; + const errors = []; + if ((value as Func).length < argsCount) { + errors.push(getError(ValidatorType.funcOfxArguments, [String(argsCount)])); + } + return { value: value as Func, isSet: true, isValid: !errors.length, errors }; + }; -const onFunctionWithXToYArguments = (from: number, to: number) => (value: unknown): ValidatedValue => { - const result = onFunction(value); - if (!result.isValid) { - return result; - } - value = result.value; - const errors = []; - if ((value as Func).length < from || (value as Func).length > to) { - errors.push(getError(ValidatorType.funcOfXToYArguments, [String(from), String(to)])); - } - return { value: value as Func, isSet: true, isValid: !errors.length, errors }; -}; +const onFunctionWithXToYArguments = + (from: number, to: number) => + (value: unknown): ValidatedValue => { + const result = onFunction(value); + if (!result.isValid) { + return result; + } + value = result.value; + const errors = []; + if ((value as Func).length < from || (value as Func).length > to) { + errors.push(getError(ValidatorType.funcOfXToYArguments, [String(from), String(to)])); + } + return { value: value as Func, isSet: true, isValid: !errors.length, errors }; + }; -const onOneOf = (tokens: string[], must: boolean) => (value: unknown, context?: IValidationContext): ValidatedValue => { - const errors = []; - const isSet = value !== void 0; - let noOneIsPresent = !isSet; - const err = must ? ValidatorType.oneOfMust : ValidatorType.oneOfCan; - if (!Array.isArray(tokens) || !tokens.length) { - errors.push(getError(err, ['undefined'])); - } else { - for (let i = tokens.length - 1; i >= 0; i--) { - const token = tokens[i]; - if (typeof token !== 'string') { - errors.push(getError(err, [tokens.join('", "')]) + ' (non-string token)'); - break; - } - const isAnotherPresent = context && Object.prototype.hasOwnProperty.call(context, token); - if (isSet && isAnotherPresent) { - errors.push(getError(err, [tokens.join('", "')]) + ` (${token} is present)`); - break; +const onOneOf = + (tokens: string[], must: boolean) => + (value: unknown, context?: IValidationContext): ValidatedValue => { + const errors = []; + const isSet = value !== void 0; + let noOneIsPresent = !isSet; + const err = must ? ValidatorType.oneOfMust : ValidatorType.oneOfCan; + if (!Array.isArray(tokens) || !tokens.length) { + errors.push(getError(err, ['undefined'])); + } else { + for (let i = tokens.length - 1; i >= 0; i--) { + const token = tokens[i]; + if (typeof token !== 'string') { + errors.push(getError(err, [tokens.join('", "')]) + ' (non-string token)'); + break; + } + const isAnotherPresent = context && Object.prototype.hasOwnProperty.call(context, token); + if (isSet && isAnotherPresent) { + errors.push(getError(err, [tokens.join('", "')]) + ` (${token} is present)`); + break; + } + if (noOneIsPresent && isAnotherPresent) { + noOneIsPresent = false; + } } - if (noOneIsPresent && isAnotherPresent) { - noOneIsPresent = false; + if (must && noOneIsPresent) { + errors.push(getError(err, [tokens.join('", "')])); } } - if (must && noOneIsPresent) { - errors.push(getError(err, [tokens.join('", "')])); - } - } - return { value, isSet, isValid: !errors.length, errors }; -}; + return { value, isSet, isValid: !errors.length, errors }; + }; -const onOr = (validators: IValidator[]) => (value: unknown): ValidatedValue => { - const errors = []; - if (validators.every(validator => !validator.method(value).isValid)) { - errors.push(validators.map(v => v.type).join(' OR ')); - } - return { value, isSet: true, isValid: !errors.length, errors }; -}; +const onOr = + (validators: IValidator[]) => + (value: unknown): ValidatedValue => { + const errors = []; + if (validators.every(validator => !validator.method(value).isValid)) { + errors.push(validators.map(v => v.type).join(' OR ')); + } + return { value, isSet: true, isValid: !errors.length, errors }; + }; type AbstractEnum = Record; type TEnum = AbstractEnum; -const onEnum = (list: TEnum) => (value: unknown): ValidatedValue => { - const errors = []; - const values = Object.keys(list).filter(k => isNaN(Number(k))).map(k => list[k as unknown as number]); - if (!values.some(item => item === value)) { - errors.push(getError(ValidatorType.enum, ['[' + values.join(',') + ']'])); - } - return { value, isSet: true, isValid: !errors.length, errors }; -}; +const onEnum = + (list: TEnum) => + (value: unknown): ValidatedValue => { + const errors = []; + const values = Object.keys(list) + .filter(k => isNaN(Number(k))) + .map(k => list[k as unknown as number]); + if (!values.some(item => item === value)) { + errors.push(getError(ValidatorType.enum, ['[' + values.join(',') + ']'])); + } + return { value, isSet: true, isValid: !errors.length, errors }; + }; export const VALIDATORS = { NUMBER: { @@ -303,7 +315,6 @@ export const VALIDATORS = { }; export class ValidatedData implements IValidatedData { - context: IValidationContext; isValidContext: boolean; isValid: boolean; @@ -331,9 +342,10 @@ export class ValidatedData implements IValidatedData { } private setValidity() { - this.errors = Object.keys(this.params).reduce((acc: string[], key: string) => [ - ...acc, ...this.params[key].errors - ], []); + this.errors = Object.keys(this.params).reduce( + (acc: string[], key: string) => [...acc, ...this.params[key].errors], + [] + ); this.isValid = !this.errors.length; } @@ -347,18 +359,14 @@ export class ValidatedData implements IValidatedData { if (!value.isValid) { value.errors = !value.isSet ? [`"${token}" must be set`] - : value.errors.map((err: string) => - `"${token}" ${err}` - ); + : value.errors.map((err: string) => `"${token}" ${err}`); } this.params[token] = value; this.setValidity(); } showErrors(): string { - return this.errors.length - ? 'validation failed: ' + this.errors.join(', ') - : ''; + return this.errors.length ? 'validation failed: ' + this.errors.join(', ') : ''; } } @@ -382,7 +390,7 @@ const getDefault = (value: unknown, prop: ICommonProp): ValidatedValue => { const empty = value === void 0; const auto = !prop.mandatory && prop.defaultValue !== void 0; return { - value: !empty ? value : (auto ? prop.defaultValue : void 0), + value: !empty ? value : auto ? prop.defaultValue : void 0, isSet: !empty || auto, isValid: !empty || !prop.mandatory, errors: [] @@ -390,7 +398,9 @@ const getDefault = (value: unknown, prop: ICommonProp): ValidatedValue => { }; export const validateOne = ( - context: IValidationContext, name: string, prop: ICommonProp + context: IValidationContext, + name: string, + prop: ICommonProp ): ValidatedValue => { const result = getDefault(context[name], prop); if (!result.isSet) { @@ -415,14 +425,12 @@ export const validateOne = ( return result; }; -export const validate = ( - context: unknown, params: ICommonProps -): IValidatedData => { +export const validate = (context: unknown, params: ICommonProps): IValidatedData => { const data = new ValidatedData(context); Object.entries(params).forEach(([key, prop]) => - data.setParam(key, data.isValidContext - ? validateOne(data.context, key, prop) - : getDefault(void 0, prop) + data.setParam( + key, + data.isValidContext ? validateOne(data.context, key, prop) : getDefault(void 0, prop) ) ); return data; diff --git a/src/inputs/workflow.ts b/src/inputs/workflow.ts index f1aacd65..1f2adb10 100644 --- a/src/inputs/workflow.ts +++ b/src/inputs/workflow.ts @@ -8,7 +8,7 @@ export enum WorkflowProps { element = 'element', datasource = 'datasource', run = 'run', - Routines = 'Routines', + Routines = 'Routines' } export const WORKFLOW: ICommonProps = { diff --git a/src/interfaces/datasource.ts b/src/interfaces/datasource.ts index b3ffa30c..1e86b8be 100644 --- a/src/interfaces/datasource.ts +++ b/src/interfaces/datasource.ts @@ -8,7 +8,12 @@ export interface ObservableLike { subscribe(next: SuccessFn, error: ErrorFn, complete: () => void): { unsubscribe: () => void }; } -type DSGetCallback = (index: number, count: number, success: SuccessFn, fail?: ErrorFn) => void; +type DSGetCallback = ( + index: number, + count: number, + success: SuccessFn, + fail?: ErrorFn +) => void; type DSGetObservable = (index: number, count: number) => ObservableLike; type DSGetPromise = (index: number, count: number) => PromiseLike; diff --git a/src/interfaces/index.ts b/src/interfaces/index.ts index d50f2320..13653b24 100644 --- a/src/interfaces/index.ts +++ b/src/interfaces/index.ts @@ -4,7 +4,7 @@ import { IDatasourceParams, IDatasourceOptional, IDatasource, - IDatasourceConstructed, + IDatasourceConstructed } from './datasource'; import { OnDataChanged, @@ -15,7 +15,7 @@ import { WorkflowError, InterruptParams, StateMachineMethods, - StateMachineParams, + StateMachineParams } from './workflow'; import { Item } from './item'; import { @@ -43,7 +43,7 @@ import { AdapterUpdateOptions, AdapterFixOptions, AdapterMethodResult, - IAdapter, + IAdapter } from './adapter'; import { Settings, DevSettings } from './settings'; import { IRoutines, RoutinesClassType } from './routines'; @@ -55,7 +55,7 @@ import { ProcessSubject, AdapterProcessMap, IBaseProcess, - IBaseAdapterProcess, + IBaseAdapterProcess } from './process'; import { ValidatedValue, @@ -63,7 +63,7 @@ import { ICommonProp, ICommonProps, IValidatedCommonProps, - IValidatedData, + IValidatedData } from './validation'; export { @@ -126,5 +126,5 @@ export { ICommonProp, ICommonProps, IValidatedCommonProps, - IValidatedData, + IValidatedData }; diff --git a/src/interfaces/process.ts b/src/interfaces/process.ts index d793b5c2..b66ddc22 100644 --- a/src/interfaces/process.ts +++ b/src/interfaces/process.ts @@ -1,45 +1,48 @@ -import { Scroller } from '../scroller'; -import { CommonProcess, AdapterProcess, ProcessStatus } from '../processes/index'; -import { IDatasourceConstructed } from './datasource'; -import { IAdapterInput } from './adapter'; - -export type ProcessName = CommonProcess | AdapterProcess; - -export interface ProcessClass { - process: ProcessName; - - run: (scroller: Scroller, ...args: any[]) => void; - name: string; -} - -export interface ProcessPayload { - process?: ProcessName; - options?: unknown; - event?: Event; - finalize?: boolean; - doClip?: boolean; - doRender?: boolean; - datasource?: IDatasourceConstructed; - error?: unknown; -} - -export interface ProcessSubject { - process: ProcessName; - status: ProcessStatus; - payload?: ProcessPayload; -} - -export type AdapterProcessMap = { - [key in AdapterProcess]: T; -}; - -export interface IBaseProcess { - new(): any; // eslint-disable-line @typescript-eslint/no-explicit-any - process: ProcessName; -} - -export interface IBaseAdapterProcess extends IBaseProcess { - parseInput: ( - scroller: Scroller, options: T, ignoreErrors?: boolean, process?: AdapterProcess - ) => IAdapterInput; -} +import { Scroller } from '../scroller'; +import { CommonProcess, AdapterProcess, ProcessStatus } from '../processes/index'; +import { IDatasourceConstructed } from './datasource'; +import { IAdapterInput } from './adapter'; + +export type ProcessName = CommonProcess | AdapterProcess; + +export interface ProcessClass { + process: ProcessName; + + run: (scroller: Scroller, ...args: any[]) => void; + name: string; +} + +export interface ProcessPayload { + process?: ProcessName; + options?: unknown; + event?: Event; + finalize?: boolean; + doClip?: boolean; + doRender?: boolean; + datasource?: IDatasourceConstructed; + error?: unknown; +} + +export interface ProcessSubject { + process: ProcessName; + status: ProcessStatus; + payload?: ProcessPayload; +} + +export type AdapterProcessMap = { + [key in AdapterProcess]: T; +}; + +export interface IBaseProcess { + new (): any; // eslint-disable-line @typescript-eslint/no-explicit-any + process: ProcessName; +} + +export interface IBaseAdapterProcess extends IBaseProcess { + parseInput: ( + scroller: Scroller, + options: T, + ignoreErrors?: boolean, + process?: AdapterProcess + ) => IAdapterInput; +} diff --git a/src/interfaces/routines.ts b/src/interfaces/routines.ts index b11321c8..c5fb665b 100644 --- a/src/interfaces/routines.ts +++ b/src/interfaces/routines.ts @@ -14,15 +14,14 @@ interface IRoutinesSettings { */ horizontal: boolean; - /** Determines wether the entire window is the scroller's viewport or not. + /** Determines wether the entire window is the scroller's viewport or not. * The value is equal to settings.window. */ window: boolean; } export interface IRoutines { - - /** Internal prop that is available after instantiation. + /** Internal prop that is available after instantiation. * Reduced version of the App settings object. */ readonly settings: IRoutinesSettings; @@ -47,7 +46,7 @@ export interface IRoutines { * "settings.viewport", "settings.window" and "element". * This method is being called during Routines instantiation * to determine the "viewport" prop: - * + * * this.viewport = this.getViewportElement(); * @returns {HTMLElement} HTML element. */ @@ -191,7 +190,6 @@ export interface IRoutines { * @returns {function} Callback to dismiss scroll event listener. */ onScroll: (handler: EventListener) => () => void; - } export type RoutinesClassType = new (...args: ConstructorParameters) => Routines; diff --git a/src/interfaces/settings.ts b/src/interfaces/settings.ts index da966322..12a27694 100644 --- a/src/interfaces/settings.ts +++ b/src/interfaces/settings.ts @@ -1,34 +1,34 @@ -import { ItemsProcessor } from './adapter'; -import { SizeStrategy } from '../inputs/index'; - -export interface Settings { - adapter?: boolean; - startIndex?: number; - minIndex?: number; - maxIndex?: number; - itemSize?: number; - bufferSize?: number; - padding?: number; - infinite?: boolean; - horizontal?: boolean; - windowViewport?: boolean; - viewportElement?: HTMLElement | (() => void) | null; - inverse?: boolean; - onBeforeClip?: ItemsProcessor | null; - sizeStrategy?: SizeStrategy; -} - -export interface DevSettings { - debug?: boolean; - immediateLog?: boolean; - logProcessRun?: boolean; - logTime?: boolean; - logColor?: boolean; - throttle?: number; - initDelay?: number; - initWindowDelay?: number; - cacheData?: boolean; - cacheOnReload?: boolean; - dismissOverflowAnchor?: boolean; - directionPriority?: 'backward' | 'forward'; -} +import { ItemsProcessor } from './adapter'; +import { SizeStrategy } from '../inputs/index'; + +export interface Settings { + adapter?: boolean; + startIndex?: number; + minIndex?: number; + maxIndex?: number; + itemSize?: number; + bufferSize?: number; + padding?: number; + infinite?: boolean; + horizontal?: boolean; + windowViewport?: boolean; + viewportElement?: HTMLElement | (() => void) | null; + inverse?: boolean; + onBeforeClip?: ItemsProcessor | null; + sizeStrategy?: SizeStrategy; +} + +export interface DevSettings { + debug?: boolean; + immediateLog?: boolean; + logProcessRun?: boolean; + logTime?: boolean; + logColor?: boolean; + throttle?: number; + initDelay?: number; + initWindowDelay?: number; + cacheData?: boolean; + cacheOnReload?: boolean; + dismissOverflowAnchor?: boolean; + directionPriority?: 'backward' | 'forward'; +} diff --git a/src/interfaces/state.ts b/src/interfaces/state.ts index 1962423a..7e9c4440 100644 --- a/src/interfaces/state.ts +++ b/src/interfaces/state.ts @@ -1,26 +1,26 @@ -import { Direction } from '../inputs/index'; -import { Reactive } from '../classes/reactive'; -import { WorkflowCycleModel } from '../classes/state/cycle'; -import { FetchModel } from '../classes/state/fetch'; -import { ClipModel } from '../classes/state/clip'; -import { RenderModel } from '../classes/state/render'; -import { ScrollModel } from '../classes/state/scroll'; -import { IPackages } from './adapter'; - -export interface ScrollEventData { - time: number; - position: number; - direction: Direction | null; -} - -export interface State { - packageInfo: IPackages; - initTime: number; - paused: Reactive; - cycle: WorkflowCycleModel; - fetch: FetchModel; - clip: ClipModel; - render: RenderModel; - scroll: ScrollModel; - time: number; -} +import { Direction } from '../inputs/index'; +import { Reactive } from '../classes/reactive'; +import { WorkflowCycleModel } from '../classes/state/cycle'; +import { FetchModel } from '../classes/state/fetch'; +import { ClipModel } from '../classes/state/clip'; +import { RenderModel } from '../classes/state/render'; +import { ScrollModel } from '../classes/state/scroll'; +import { IPackages } from './adapter'; + +export interface ScrollEventData { + time: number; + position: number; + direction: Direction | null; +} + +export interface State { + packageInfo: IPackages; + initTime: number; + paused: Reactive; + cycle: WorkflowCycleModel; + fetch: FetchModel; + clip: ClipModel; + render: RenderModel; + scroll: ScrollModel; + time: number; +} diff --git a/src/interfaces/validation.ts b/src/interfaces/validation.ts index 6e96e875..dfb692e7 100644 --- a/src/interfaces/validation.ts +++ b/src/interfaces/validation.ts @@ -1,6 +1,8 @@ import { ValidatorType } from '../inputs/validation'; -export interface IValidationContext { [key: string]: unknown; } +export interface IValidationContext { + [key: string]: unknown; +} type ValidatorMethod = (value: unknown, context?: IValidationContext) => ValidatedValue; diff --git a/src/interfaces/workflow.ts b/src/interfaces/workflow.ts index c060c938..16f3e30c 100644 --- a/src/interfaces/workflow.ts +++ b/src/interfaces/workflow.ts @@ -7,7 +7,6 @@ import { Scroller } from '../scroller'; export type OnDataChanged = (items: Item[]) => void; - export interface WorkflowParams { datasource: IDatasource; consumer: IPackage; diff --git a/src/processes/adapter/append.ts b/src/processes/adapter/append.ts index 18f27e3c..f576e07e 100644 --- a/src/processes/adapter/append.ts +++ b/src/processes/adapter/append.ts @@ -11,7 +11,6 @@ interface AppendRunOptions { } export default class Append extends BaseAdapterProcessFactory(AdapterProcess.append) { - static run(scroller: Scroller, { process, options }: AppendRunOptions): void { const { params } = Append.parseInput(scroller, options, false, process); if (!params) { @@ -26,17 +25,24 @@ export default class Append extends BaseAdapterProcessFactory(AdapterProcess.app }); } - static doAppend(scroller: Scroller, process: AdapterProcess, params: AdapterAppendPrependOptions): boolean { + static doAppend( + scroller: Scroller, + process: AdapterProcess, + params: AdapterAppendPrependOptions + ): boolean { const { bof, eof, increase, decrease } = params; const { buffer } = scroller; const prepend = process === AdapterProcess.prepend; const opposite = prepend ? !increase : decrease; - let beforeIndex, afterIndex, items = params.items; + let beforeIndex, + afterIndex, + items = params.items; if (prepend) { beforeIndex = (bof ? buffer.absMinIndex : buffer.minIndex) + (!buffer.size ? 1 : 0); items = [...items].reverse(); } else { - afterIndex = (eof ? buffer.absMaxIndex : buffer.maxIndex) - (!buffer.size && !opposite ? 1 : 0); + afterIndex = + (eof ? buffer.absMaxIndex : buffer.maxIndex) - (!buffer.size && !opposite ? 1 : 0); } return Insert.doInsert(scroller, { items, @@ -46,5 +52,4 @@ export default class Append extends BaseAdapterProcessFactory(AdapterProcess.app virtualize: params.virtualize }); } - } diff --git a/src/processes/adapter/check.ts b/src/processes/adapter/check.ts index 6ac99979..175ada97 100644 --- a/src/processes/adapter/check.ts +++ b/src/processes/adapter/check.ts @@ -3,10 +3,10 @@ import { BaseAdapterProcessFactory, AdapterProcess, ProcessStatus } from '../mis import { Direction } from '../../inputs/index'; export default class Check extends BaseAdapterProcessFactory(AdapterProcess.check) { - static run(scroller: Scroller): void { - const { workflow, buffer, state: { fetch }, viewport } = scroller; - let min = Infinity, max = -Infinity; + const { workflow, buffer, state, viewport } = scroller; + let min = Infinity, + max = -Infinity; buffer.items.forEach(item => { const size = item.size; @@ -19,16 +19,18 @@ export default class Check extends BaseAdapterProcessFactory(AdapterProcess.chec }); if (Number.isFinite(min)) { + const { fetch } = state; fetch.first.indexBuffer = buffer.firstIndex; fetch.last.indexBuffer = buffer.lastIndex; - const { index: firstIndex, diff } = viewport.getEdgeVisibleItem(buffer.items, Direction.backward); + const { index: firstIndex, diff } = viewport.getEdgeVisibleItem( + buffer.items, + Direction.backward + ); fetch.firstVisible.index = firstIndex; if (!isNaN(firstIndex)) { - fetch.firstVisible.delta = - buffer.getSizeByIndex(firstIndex) + diff; + fetch.firstVisible.delta = -buffer.getSizeByIndex(firstIndex) + diff; } - fetch.check( - buffer.items.filter(item => item.$index >= min && item.$index <= max) - ); + fetch.check(buffer.items.filter(item => item.$index >= min && item.$index <= max)); } scroller.logger.stat('check'); @@ -38,5 +40,4 @@ export default class Check extends BaseAdapterProcessFactory(AdapterProcess.chec status: Number.isFinite(min) ? ProcessStatus.next : ProcessStatus.done }); } - } diff --git a/src/processes/adapter/clip.ts b/src/processes/adapter/clip.ts index 31d9d3b0..8ba1adb7 100644 --- a/src/processes/adapter/clip.ts +++ b/src/processes/adapter/clip.ts @@ -3,7 +3,6 @@ import { BaseAdapterProcessFactory, AdapterProcess, ProcessStatus } from '../mis import { AdapterClipOptions } from '../../interfaces/index'; export default class UserClip extends BaseAdapterProcessFactory(AdapterProcess.clip) { - static run(scroller: Scroller, options?: AdapterClipOptions): void { const { params } = UserClip.parseInput(scroller, options); @@ -15,5 +14,4 @@ export default class UserClip extends BaseAdapterProcessFactory(AdapterProcess.c status: ProcessStatus.next }); } - } diff --git a/src/processes/adapter/fix.ts b/src/processes/adapter/fix.ts index 04c56cad..87a97e14 100644 --- a/src/processes/adapter/fix.ts +++ b/src/processes/adapter/fix.ts @@ -5,13 +5,12 @@ import { ItemsPredicate, ItemsUpdater, AdapterFixOptions, - IValidatedData, + IValidatedData } from '../../interfaces/index'; const { [AdapterProcess.fix]: FixParams } = AdapterMethods; export default class Fix extends BaseAdapterProcessFactory(AdapterProcess.fix) { - static run(scroller: Scroller, options: AdapterFixOptions): void { const { workflow } = scroller; @@ -32,7 +31,12 @@ export default class Fix extends BaseAdapterProcessFactory(AdapterProcess.fix) { }); } - static runByType(scroller: Scroller, token: string, value: unknown, methodData: IValidatedData): void { + static runByType( + scroller: Scroller, + token: string, + value: unknown, + methodData: IValidatedData + ): void { switch (token) { case FixParams.scrollPosition: return Fix.setScrollPosition(scroller, value as number); @@ -45,7 +49,9 @@ export default class Fix extends BaseAdapterProcessFactory(AdapterProcess.fix) { case FixParams.scrollToItem: if (methodData.params) { const scrollToItemOpt = methodData.params[FixParams.scrollToItemOpt]; - const options = scrollToItemOpt ? scrollToItemOpt.value as AdapterFixOptions['scrollToItemOpt'] : void 0; + const options = scrollToItemOpt + ? (scrollToItemOpt.value as AdapterFixOptions['scrollToItemOpt']) + : void 0; return Fix.scrollToItem(scroller, value as ItemsPredicate, options); } return; @@ -76,7 +82,7 @@ export default class Fix extends BaseAdapterProcessFactory(AdapterProcess.fix) { static updateItems({ buffer, logger }: Scroller, value: ItemsUpdater): void { let updateReference = false; - const updater = () => updateReference = true; + const updater = () => (updateReference = true); buffer.items.forEach(item => value(item.get(), updater)); if (updateReference) { logger.log(() => 'update Buffer.items reference'); @@ -84,7 +90,11 @@ export default class Fix extends BaseAdapterProcessFactory(AdapterProcess.fix) { } } - static scrollToItem(scroller: Scroller, value: ItemsPredicate, options?: boolean | ScrollIntoViewOptions): void { + static scrollToItem( + scroller: Scroller, + value: ItemsPredicate, + options?: boolean | ScrollIntoViewOptions + ): void { const found = scroller.buffer.items.find(item => value(item.get())); if (!found) { scroller.logger.log(() => 'scrollToItem cancelled, item not found'); @@ -92,5 +102,4 @@ export default class Fix extends BaseAdapterProcessFactory(AdapterProcess.fix) { } found.scrollTo(options); } - } diff --git a/src/processes/adapter/insert.ts b/src/processes/adapter/insert.ts index 4e07b23a..e7c54320 100644 --- a/src/processes/adapter/insert.ts +++ b/src/processes/adapter/insert.ts @@ -6,7 +6,6 @@ import { Direction } from '../../inputs/index'; import { AdapterInsertOptions, AdapterUpdateOptions } from '../../interfaces/index'; export default class Insert extends BaseAdapterProcessFactory(AdapterProcess.insert) { - static run(scroller: Scroller, options: AdapterInsertOptions): void { const { params } = Insert.parseInput(scroller, options); if (!params) { @@ -32,25 +31,34 @@ export default class Insert extends BaseAdapterProcessFactory(AdapterProcess.ins } static insertEmpty(scroller: Scroller, params: AdapterInsertOptions): boolean { - const { buffer, routines, state: { fetch } } = scroller; + const { buffer, routines, state } = scroller; if (buffer.size) { return false; } const { beforeIndex, afterIndex, items, decrease } = params; - if (!buffer.fillEmpty( - items, beforeIndex, afterIndex, !!decrease, - (index, data) => new Item(index, data, routines) - )) { + if ( + !buffer.fillEmpty( + items, + beforeIndex, + afterIndex, + !!decrease, + (index, data) => new Item(index, data, routines) + ) + ) { return false; } - fetch.fill(buffer.items, buffer.startIndex); + state.fetch.fill(buffer.items, buffer.startIndex); return true; } static insertInBuffer(scroller: Scroller, params: AdapterInsertOptions): boolean { const { before, after, beforeIndex, afterIndex, items, decrease } = params; - const indexToInsert = scroller.buffer.getIndexToInsert(before || after, beforeIndex, afterIndex); + const indexToInsert = scroller.buffer.getIndexToInsert( + before || after, + beforeIndex, + afterIndex + ); if (params.virtualize || isNaN(indexToInsert)) { return false; @@ -72,7 +80,7 @@ export default class Insert extends BaseAdapterProcessFactory(AdapterProcess.ins static insertVirtually(scroller: Scroller, params: AdapterInsertOptions): boolean { const { beforeIndex, afterIndex, items, decrease } = params; - const { buffer, state: { fetch }, viewport } = scroller; + const { buffer, state, viewport } = scroller; const direction = Number.isInteger(beforeIndex) ? Direction.backward : Direction.forward; const indexToInsert = (direction === Direction.backward ? beforeIndex : afterIndex) as number; @@ -80,14 +88,14 @@ export default class Insert extends BaseAdapterProcessFactory(AdapterProcess.ins return false; } + const { fetch } = state; const { index, diff } = viewport.getEdgeVisibleItem(buffer.items, Direction.backward); fetch.firstVisible.index = index; if (!isNaN(index)) { fetch.simulate = true; - fetch.firstVisible.delta = - buffer.getSizeByIndex(index) + diff; + fetch.firstVisible.delta = -buffer.getSizeByIndex(index) + diff; } return true; } - } diff --git a/src/processes/adapter/pause.ts b/src/processes/adapter/pause.ts index f37606fe..e304f95c 100644 --- a/src/processes/adapter/pause.ts +++ b/src/processes/adapter/pause.ts @@ -2,7 +2,6 @@ import { Scroller } from '../../scroller'; import { BaseAdapterProcessFactory, AdapterProcess, ProcessStatus } from '../misc/index'; export default class Pause extends BaseAdapterProcessFactory(AdapterProcess.pause) { - static run(scroller: Scroller, options?: { resume: boolean }): void { const resume = !!options?.resume; @@ -24,5 +23,4 @@ export default class Pause extends BaseAdapterProcessFactory(AdapterProcess.paus status: ProcessStatus.next }); } - } diff --git a/src/processes/adapter/reload.ts b/src/processes/adapter/reload.ts index beafa8b0..bfb6f7b9 100644 --- a/src/processes/adapter/reload.ts +++ b/src/processes/adapter/reload.ts @@ -3,7 +3,6 @@ import { BaseAdapterProcessFactory, AdapterProcess, ProcessStatus } from '../mis import { ProcessPayload } from '../../interfaces/index'; export default class Reload extends BaseAdapterProcessFactory(AdapterProcess.reload) { - static run(scroller: Scroller, reloadIndex: number): void { const { viewport, state, buffer } = scroller; @@ -24,5 +23,4 @@ export default class Reload extends BaseAdapterProcessFactory(AdapterProcess.rel payload }); } - } diff --git a/src/processes/adapter/remove.ts b/src/processes/adapter/remove.ts index 56083609..322ca69b 100644 --- a/src/processes/adapter/remove.ts +++ b/src/processes/adapter/remove.ts @@ -5,7 +5,6 @@ import { Direction } from '../../inputs/index'; import { AdapterRemoveOptions, AdapterUpdateOptions, ItemsPredicate } from '../../interfaces/index'; export default class Remove extends BaseAdapterProcessFactory(AdapterProcess.remove) { - static run(scroller: Scroller, options: AdapterRemoveOptions): void { const { params } = Remove.parseInput(scroller, options); if (!params) { @@ -33,8 +32,8 @@ export default class Remove extends BaseAdapterProcessFactory(AdapterProcess.rem if (params.indexes && params.indexes.length) { const diffLeft = (params.increase ? 1 : 0) * removed.length; const diffRight = (params.increase ? 0 : -1) * removed.length; - params.indexes = params.indexes.map(index => - index + (index < removed[0] ? diffLeft : diffRight) + params.indexes = params.indexes.map( + index => index + (index < removed[0] ? diffLeft : diffRight) ); } } @@ -52,11 +51,11 @@ export default class Remove extends BaseAdapterProcessFactory(AdapterProcess.rem return []; } const newPredicate: ItemsPredicate = item => - (predicate && predicate(item)) || - (!!indexes && indexes.includes(item.$index)); + (predicate && predicate(item)) || (!!indexes && indexes.includes(item.$index)); - const indexesToRemove: number[] = scroller.buffer.items.reduce((acc, item) => - newPredicate(item) ? [...acc, item.$index] : acc, [] as number[] + const indexesToRemove: number[] = scroller.buffer.items.reduce( + (acc, item) => (newPredicate(item) ? [...acc, item.$index] : acc), + [] as number[] ); const updateOptions: AdapterUpdateOptions = { predicate: item => !newPredicate(item), @@ -71,7 +70,7 @@ export default class Remove extends BaseAdapterProcessFactory(AdapterProcess.rem if (!indexes || !indexes.length) { return false; } - const { buffer, viewport, state: { fetch } } = scroller; + const { buffer, viewport, state } = scroller; // get items to remove const { finiteAbsMinIndex, firstIndex, finiteAbsMaxIndex, lastIndex } = buffer; @@ -92,11 +91,12 @@ export default class Remove extends BaseAdapterProcessFactory(AdapterProcess.rem } // what should be shown after remove; Buffer removal has priority + const { fetch } = state; if (isNaN(fetch.firstVisible.index)) { const { index, diff } = viewport.getEdgeVisibleItem(buffer.items, Direction.backward); fetch.firstVisible.index = index; if (!isNaN(index)) { - fetch.firstVisible.delta = - buffer.getSizeByIndex(index) + diff; + fetch.firstVisible.delta = -buffer.getSizeByIndex(index) + diff; } } @@ -109,15 +109,23 @@ export default class Remove extends BaseAdapterProcessFactory(AdapterProcess.rem return true; } - static shiftFirstVisibleIndex(scroller: Scroller, listToRemove: number[], increase: boolean): void { + static shiftFirstVisibleIndex( + scroller: Scroller, + listToRemove: number[], + increase: boolean + ): void { const { firstVisible } = scroller.state.fetch; if (isNaN(firstVisible.index)) { return; } - const shift = listToRemove.reduce((acc, index) => acc + ( - ((increase && index > firstVisible.index) || (!increase && index < firstVisible.index)) ? 1 : 0 - ), 0); + const shift = listToRemove.reduce( + (acc, index) => + acc + + ((increase && index > firstVisible.index) || (!increase && index < firstVisible.index) + ? 1 + : 0), + 0 + ); firstVisible.index = firstVisible.index + (increase ? shift : -shift); } - } diff --git a/src/processes/adapter/replace.ts b/src/processes/adapter/replace.ts index 5c9bd8a6..4d202447 100644 --- a/src/processes/adapter/replace.ts +++ b/src/processes/adapter/replace.ts @@ -4,7 +4,6 @@ import { BaseAdapterProcessFactory, AdapterProcess, ProcessStatus } from '../mis import { AdapterReplaceOptions, AdapterUpdateOptions } from '../../interfaces/index'; export default class Replace extends BaseAdapterProcessFactory(AdapterProcess.replace) { - static run(scroller: Scroller, options: AdapterReplaceOptions): void { const { params } = Replace.parseInput(scroller, options); if (!params) { @@ -14,7 +13,7 @@ export default class Replace extends BaseAdapterProcessFactory(AdapterProcess.re scroller.workflow.call({ process: Replace.process, - status: shouldReplace ? ProcessStatus.next : ProcessStatus.done, + status: shouldReplace ? ProcessStatus.next : ProcessStatus.done }); } @@ -45,5 +44,4 @@ export default class Replace extends BaseAdapterProcessFactory(AdapterProcess.re return Update.doUpdate(scroller, updateOptions); } - } diff --git a/src/processes/adapter/reset.ts b/src/processes/adapter/reset.ts index c5e0e618..77f2935d 100644 --- a/src/processes/adapter/reset.ts +++ b/src/processes/adapter/reset.ts @@ -5,10 +5,8 @@ import { BaseAdapterProcessFactory, AdapterProcess, ProcessStatus } from '../mis import { IDatasourceOptional, ProcessPayload } from '../../interfaces/index'; export default class Reset extends BaseAdapterProcessFactory(AdapterProcess.reset) { - static run(scroller: Scroller, options?: IDatasourceOptional): void { - const { datasource, buffer, viewport: { paddings }, state: { cycle } } = scroller; - + const { datasource, buffer, viewport, state } = scroller; if (options) { const { data } = Reset.parseInput(scroller, options); if (!data.isValid) { @@ -25,13 +23,13 @@ export default class Reset extends BaseAdapterProcessFactory(AdapterProcess.rese } buffer.reset(true); - paddings.backward.reset(); - paddings.forward.reset(); + viewport.paddings.backward.reset(); + viewport.paddings.forward.reset(); const payload: ProcessPayload = { datasource }; - if (cycle.busy.get()) { + if (state.cycle.busy.get()) { payload.finalize = true; - cycle.interrupter = Reset.process; + state.cycle.interrupter = Reset.process; } scroller.workflow.call({ @@ -40,5 +38,4 @@ export default class Reset extends BaseAdapterProcessFactory(AdapterProcess.rese payload }); } - } diff --git a/src/processes/adapter/update.ts b/src/processes/adapter/update.ts index b6fa7035..b4fc69b1 100644 --- a/src/processes/adapter/update.ts +++ b/src/processes/adapter/update.ts @@ -5,7 +5,6 @@ import { Direction } from '../../inputs/index'; import { AdapterUpdateOptions } from '../../interfaces/index'; export default class Update extends BaseAdapterProcessFactory(AdapterProcess.update) { - static run(scroller: Scroller, options: AdapterUpdateOptions): void { const { params } = Update.parseInput(scroller, options); if (!params) { @@ -21,13 +20,16 @@ export default class Update extends BaseAdapterProcessFactory(AdapterProcess.upd } static doUpdate(scroller: Scroller, params: AdapterUpdateOptions): boolean { - const { buffer, viewport, state: { fetch }, routines, logger } = scroller; + const { buffer, viewport, state, routines, logger } = scroller; if (!buffer.items) { logger.log(() => 'no items in Buffer'); return false; } - const { item: firstItem, index: firstIndex, diff: firstItemDiff } = - viewport.getEdgeVisibleItem(buffer.items, Direction.backward); + const { + item: firstItem, + index: firstIndex, + diff: firstItemDiff + } = viewport.getEdgeVisibleItem(buffer.items, Direction.backward); const { trackedIndex, toRemove } = buffer.updateItems( params.predicate, @@ -39,26 +41,28 @@ export default class Update extends BaseAdapterProcessFactory(AdapterProcess.upd let delta = 0; const trackedItem = buffer.get(trackedIndex); if (firstItem && firstItem === trackedItem) { - delta = - buffer.getSizeByIndex(trackedIndex) + firstItemDiff; + delta = -buffer.getSizeByIndex(trackedIndex) + firstItemDiff; } toRemove.forEach(item => item.hide()); - logger.log(() => toRemove.length - ? 'items to remove: [' + toRemove.map(({ $index }) => $index).join(',') + ']' - : 'no items to remove' + logger.log(() => + toRemove.length + ? 'items to remove: [' + toRemove.map(({ $index }) => $index).join(',') + ']' + : 'no items to remove' ); - if (toRemove.length) { // insertions will be processed on render + if (toRemove.length) { + // insertions will be processed on render buffer.checkDefaultSize(); } const toRender = buffer.items.filter(({ toInsert }) => toInsert); - logger.log(() => toRender.length - ? 'items to render: [' + toRender.map(({ $index }) => $index).join(',') + ']' - : 'no items to render' + logger.log(() => + toRender.length + ? 'items to render: [' + toRender.map(({ $index }) => $index).join(',') + ']' + : 'no items to render' ); - fetch.update(trackedIndex, delta, toRender, toRemove); + state.fetch.update(trackedIndex, delta, toRender, toRemove); return !!toRemove.length || !!toRender.length; } - } diff --git a/src/processes/adjust.ts b/src/processes/adjust.ts index 30001fcc..e8b063f2 100644 --- a/src/processes/adjust.ts +++ b/src/processes/adjust.ts @@ -1,164 +1,171 @@ -import { BaseProcessFactory, CommonProcess, ProcessStatus } from './misc/index'; -import { Scroller } from '../scroller'; -import End from './end'; - -export default class Adjust extends BaseProcessFactory(CommonProcess.adjust) { - - static run(scroller: Scroller): void { - const { workflow, viewport, state: { scroll } } = scroller; - - scroll.positionBeforeAdjust = viewport.scrollPosition; - Adjust.setPaddings(scroller); - scroll.positionAfterAdjust = viewport.scrollPosition; - - // scroll position adjustments - const position = Adjust.calculatePosition(scroller); - - // additional adjustment if the position can't be reached during the initial cycle - Adjust.setAdditionalForwardPadding(scroller, position); - - // set new position using animation frame - Adjust.setPosition(scroller, position, () => - workflow.call({ - process: Adjust.process, - status: ProcessStatus.done - }) - ); - } - - static setPaddings(scroller: Scroller): void { - const { viewport, buffer, settings: { inverse }, state: { fetch } } = scroller; - const firstItem = buffer.getFirstVisibleItem(); - const lastItem = buffer.getLastVisibleItem(); - let first, last; - if (firstItem && lastItem) { - first = firstItem.$index; - last = lastItem.$index; - } else { - first = !isNaN(fetch.firstVisible.index) ? fetch.firstVisible.index : buffer.startIndex; - last = first - 1; - } - const { forward, backward } = viewport.paddings; - let index, bwdSize = 0, fwdSize = 0; - - // new backward and forward paddings size - for (index = buffer.finiteAbsMinIndex; index < first; index++) { - bwdSize += buffer.getSizeByIndex(index); - } - for (index = last + 1; index <= buffer.finiteAbsMaxIndex; index++) { - fwdSize += buffer.getSizeByIndex(index); - } - - // lack of items case - const bufferSize = viewport.getScrollableSize() - forward.size - backward.size; - const scrollSize = bwdSize + bufferSize + fwdSize; - const viewportSizeDiff = viewport.getSize() - scrollSize; - if (viewportSizeDiff > 0) { - if (inverse) { - bwdSize += viewportSizeDiff; - } else { - fwdSize += viewportSizeDiff; - } - scroller.logger.log(() => - inverse ? 'backward' : 'forward' + ` padding will be increased by ${viewportSizeDiff} to fill the viewport` - ); - } - - backward.size = bwdSize; - forward.size = fwdSize; - - scroller.logger.stat('after paddings adjustments'); - } - - static calculatePosition(scroller: Scroller): number { - const { viewport, buffer, state: { fetch, render, scroll } } = scroller; - let position = viewport.paddings.backward.size; - - // increase the position to meet the expectation of the first visible item - if (!isNaN(fetch.firstVisible.index) && !isNaN(buffer.firstIndex)) { - scroller.logger.log(`first index = ${fetch.firstVisible.index}, delta = ${fetch.firstVisible.delta}`); - const shouldCheckPreSizeExpectation = fetch.shouldCheckPreSizeExpectation(buffer.lastIndex); - buffer.items.forEach(item => { - // 1) shift of the buffered items before the first visible item - if (item.$index < fetch.firstVisible.index) { - position += item.size; - return; - } - // 2) delta of the first visible item - if (item.$index === fetch.firstVisible.index && fetch.firstVisible.delta) { - position -= fetch.firstVisible.delta; - } - // 3) difference between expected and real sizes of fetched items after the first visible - if (shouldCheckPreSizeExpectation && item.preSize && fetch.items.includes(item)) { - position += item.size - item.preSize; - } - }); - } - - // slow fetch/render case - if (scroll.positionBeforeAsync !== null) { - const diff = render.positionBefore - scroll.positionBeforeAsync; - if (diff !== 0) { - scroller.logger.log(`shift position due to fetch-render difference (${diff})`); - position += diff; - } - } - - // increase the position due to viewport's offset - if (viewport.offset > 0 && (position || fetch.positions.before)) { - position += viewport.offset; - } - - return Math.round(position); - } - - static setAdditionalForwardPadding(scroller: Scroller, position: number): void { - const { viewport, buffer, state: { cycle } } = scroller; - if (!cycle.isInitial || !End.shouldContinueRun(scroller, null)) { - return; - } - const diff = position - viewport.getMaxScrollPosition(); - if (diff <= 0) { - return; - } - const last = buffer.getLastVisibleItem(); - if (!last) { - return; - } - let size = 0; - let index = last.$index + 1; - while (size <= diff && index <= buffer.absMaxIndex) { - size += buffer.getSizeByIndex(index++); - } - const shift = Math.min(size, diff); - if (shift) { - viewport.paddings.forward.size += shift; - scroller.logger.log(`increase fwd padding due to lack of items (${diff} -> ${shift})`); - } - } - - static setPosition(scroller: Scroller, position: number, done: () => void): void { - const { state: { scroll }, viewport, routines } = scroller; - if (!scroll.hasPositionChanged(position)) { - return done(); - } - scroll.syntheticPosition = position; - scroll.syntheticFulfill = false; - - scroll.cancelAnimation = routines.animate(() => { - scroll.cancelAnimation = null; - const inertiaDiff = (scroll.positionAfterAdjust as number) - viewport.scrollPosition; - let diffLog = ''; - if (inertiaDiff > 0) { - position -= inertiaDiff; - scroll.syntheticPosition = position; - diffLog = ` (-${inertiaDiff})`; - } - scroll.syntheticFulfill = true; - viewport.scrollPosition = position; - scroller.logger.stat('after scroll adjustment' + diffLog); - done(); - }); - } - -} +import { BaseProcessFactory, CommonProcess, ProcessStatus } from './misc/index'; +import { Scroller } from '../scroller'; +import End from './end'; + +export default class Adjust extends BaseProcessFactory(CommonProcess.adjust) { + static run(scroller: Scroller): void { + const { workflow, viewport, state } = scroller; + + state.scroll.positionBeforeAdjust = viewport.scrollPosition; + Adjust.setPaddings(scroller); + state.scroll.positionAfterAdjust = viewport.scrollPosition; + + // scroll position adjustments + const position = Adjust.calculatePosition(scroller); + + // additional adjustment if the position can't be reached during the initial cycle + Adjust.setAdditionalForwardPadding(scroller, position); + + // set new position using animation frame + Adjust.setPosition(scroller, position, () => + workflow.call({ + process: Adjust.process, + status: ProcessStatus.done + }) + ); + } + + static setPaddings(scroller: Scroller): void { + const { viewport, buffer, settings, state } = scroller; + const firstItem = buffer.getFirstVisibleItem(); + const lastItem = buffer.getLastVisibleItem(); + let first, last; + if (firstItem && lastItem) { + first = firstItem.$index; + last = lastItem.$index; + } else { + const { fetch } = state; + first = !isNaN(fetch.firstVisible.index) ? fetch.firstVisible.index : buffer.startIndex; + last = first - 1; + } + const { forward, backward } = viewport.paddings; + let index, + bwdSize = 0, + fwdSize = 0; + + // new backward and forward paddings size + for (index = buffer.finiteAbsMinIndex; index < first; index++) { + bwdSize += buffer.getSizeByIndex(index); + } + for (index = last + 1; index <= buffer.finiteAbsMaxIndex; index++) { + fwdSize += buffer.getSizeByIndex(index); + } + + // lack of items case + const bufferSize = viewport.getScrollableSize() - forward.size - backward.size; + const scrollSize = bwdSize + bufferSize + fwdSize; + const viewportSizeDiff = viewport.getSize() - scrollSize; + if (viewportSizeDiff > 0) { + if (settings.inverse) { + bwdSize += viewportSizeDiff; + } else { + fwdSize += viewportSizeDiff; + } + scroller.logger.log(() => + settings.inverse + ? 'backward' + : 'forward' + ` padding will be increased by ${viewportSizeDiff} to fill the viewport` + ); + } + + backward.size = bwdSize; + forward.size = fwdSize; + + scroller.logger.stat('after paddings adjustments'); + } + + static calculatePosition(scroller: Scroller): number { + const { viewport, buffer, state } = scroller; + const { fetch, render, scroll } = state; + let position = viewport.paddings.backward.size; + + // increase the position to meet the expectation of the first visible item + if (!isNaN(fetch.firstVisible.index) && !isNaN(buffer.firstIndex)) { + scroller.logger.log( + `first index = ${fetch.firstVisible.index}, delta = ${fetch.firstVisible.delta}` + ); + const shouldCheckPreSizeExpectation = fetch.shouldCheckPreSizeExpectation(buffer.lastIndex); + buffer.items.forEach(item => { + // 1) shift of the buffered items before the first visible item + if (item.$index < fetch.firstVisible.index) { + position += item.size; + return; + } + // 2) delta of the first visible item + if (item.$index === fetch.firstVisible.index && fetch.firstVisible.delta) { + position -= fetch.firstVisible.delta; + } + // 3) difference between expected and real sizes of fetched items after the first visible + if (shouldCheckPreSizeExpectation && item.preSize && fetch.items.includes(item)) { + position += item.size - item.preSize; + } + }); + } + + // slow fetch/render case + if (scroll.positionBeforeAsync !== null) { + const diff = render.positionBefore - scroll.positionBeforeAsync; + if (diff !== 0) { + scroller.logger.log(`shift position due to fetch-render difference (${diff})`); + position += diff; + } + } + + // increase the position due to viewport's offset + if (viewport.offset > 0 && (position || fetch.positions.before)) { + position += viewport.offset; + } + + return Math.round(position); + } + + static setAdditionalForwardPadding(scroller: Scroller, position: number): void { + const { viewport, buffer, state } = scroller; + if (!state.cycle.isInitial || !End.shouldContinueRun(scroller, null)) { + return; + } + const diff = position - viewport.getMaxScrollPosition(); + if (diff <= 0) { + return; + } + const last = buffer.getLastVisibleItem(); + if (!last) { + return; + } + let size = 0; + let index = last.$index + 1; + while (size <= diff && index <= buffer.absMaxIndex) { + size += buffer.getSizeByIndex(index++); + } + const shift = Math.min(size, diff); + if (shift) { + viewport.paddings.forward.size += shift; + scroller.logger.log(`increase fwd padding due to lack of items (${diff} -> ${shift})`); + } + } + + static setPosition(scroller: Scroller, position: number, done: () => void): void { + const { state, viewport, routines } = scroller; + const { scroll } = state; + if (!scroll.hasPositionChanged(position)) { + return done(); + } + scroll.syntheticPosition = position; + scroll.syntheticFulfill = false; + + scroll.cancelAnimation = routines.animate(() => { + scroll.cancelAnimation = null; + const inertiaDiff = (scroll.positionAfterAdjust as number) - viewport.scrollPosition; + let diffLog = ''; + if (inertiaDiff > 0) { + position -= inertiaDiff; + scroll.syntheticPosition = position; + diffLog = ` (-${inertiaDiff})`; + } + scroll.syntheticFulfill = true; + viewport.scrollPosition = position; + scroller.logger.stat('after scroll adjustment' + diffLog); + done(); + }); + } +} diff --git a/src/processes/clip.ts b/src/processes/clip.ts index d9877f75..0447f5c6 100644 --- a/src/processes/clip.ts +++ b/src/processes/clip.ts @@ -1,62 +1,60 @@ -import { BaseProcessFactory, CommonProcess, ProcessStatus } from './misc/index'; -import { Scroller } from '../scroller'; -import { Direction } from '../inputs/index'; - -export default class Clip extends BaseProcessFactory(CommonProcess.clip) { - - static run(scroller: Scroller): void { - const { workflow } = scroller; - - Clip.doClip(scroller); - - workflow.call({ - process: Clip.process, - status: ProcessStatus.next - }); - } - - static doClip(scroller: Scroller): void { - const { buffer, viewport: { paddings }, state: { clip }, logger } = scroller; - const size = { [Direction.backward]: 0, [Direction.forward]: 0 }; - - logger.stat(`before clip (${++clip.callCount})`); - - const itemsToRemove = buffer.items.filter(item => { - if (!item.toRemove) { - return false; - } - item.hide(); - size[item.removeDirection] += item.size; - return true; - }); - - if (itemsToRemove.length) { - if (size[Direction.backward]) { - paddings.byDirection(Direction.backward).size += size[Direction.backward]; - } - if (size[Direction.forward]) { - paddings.byDirection(Direction.forward).size += size[Direction.forward]; - } - if (scroller.settings.onBeforeClip) { - scroller.settings.onBeforeClip(itemsToRemove.map(item => item.get())); - } - } - - buffer.clip(); - - logger.log(() => { - const list = itemsToRemove.map(({ $index }) => $index); - return list.length - ? [ - `clipped ${list.length} item(s) from Buffer` + - (size.backward ? `, +${size.backward} fwd px` : '') + - (size.forward ? `, +${size.forward} bwd px` : '') + - `, range: [${list[0]}..${list[list.length - 1]}]` - ] - : 'clipped 0 items from Buffer'; - }); - - logger.stat('after clip'); - } - -} +import { BaseProcessFactory, CommonProcess, ProcessStatus } from './misc/index'; +import { Scroller } from '../scroller'; +import { Direction } from '../inputs/index'; + +export default class Clip extends BaseProcessFactory(CommonProcess.clip) { + static run(scroller: Scroller): void { + const { workflow } = scroller; + + Clip.doClip(scroller); + + workflow.call({ + process: Clip.process, + status: ProcessStatus.next + }); + } + + static doClip(scroller: Scroller): void { + const { buffer, viewport, state, logger } = scroller; + const size = { [Direction.backward]: 0, [Direction.forward]: 0 }; + + logger.stat(`before clip (${++state.clip.callCount})`); + + const itemsToRemove = buffer.items.filter(item => { + if (!item.toRemove) { + return false; + } + item.hide(); + size[item.removeDirection] += item.size; + return true; + }); + + if (itemsToRemove.length) { + if (size[Direction.backward]) { + viewport.paddings.byDirection(Direction.backward).size += size[Direction.backward]; + } + if (size[Direction.forward]) { + viewport.paddings.byDirection(Direction.forward).size += size[Direction.forward]; + } + if (scroller.settings.onBeforeClip) { + scroller.settings.onBeforeClip(itemsToRemove.map(item => item.get())); + } + } + + buffer.clip(); + + logger.log(() => { + const list = itemsToRemove.map(({ $index }) => $index); + return list.length + ? [ + `clipped ${list.length} item(s) from Buffer` + + (size.backward ? `, +${size.backward} fwd px` : '') + + (size.forward ? `, +${size.forward} bwd px` : '') + + `, range: [${list[0]}..${list[list.length - 1]}]` + ] + : 'clipped 0 items from Buffer'; + }); + + logger.stat('after clip'); + } +} diff --git a/src/processes/end.ts b/src/processes/end.ts index ff1a80d2..7bbc1152 100644 --- a/src/processes/end.ts +++ b/src/processes/end.ts @@ -5,9 +5,9 @@ import { ScrollerWorkflow } from '../interfaces/index'; const isInterrupted = ({ call }: ScrollerWorkflow): boolean => !!call.interrupted; export default class End extends BaseProcessFactory(CommonProcess.end) { - static run(scroller: Scroller, { error }: { error?: unknown } = {}): void { - const { workflow, state: { cycle: { interrupter } } } = scroller; + const { workflow, state } = scroller; + const { interrupter } = state.cycle; if (!error && !interrupter) { // set out params accessible via Adapter @@ -61,5 +61,4 @@ export default class End extends BaseProcessFactory(CommonProcess.end) { } return false; } - } diff --git a/src/processes/fetch.ts b/src/processes/fetch.ts index 9f1b6cd7..4171e661 100644 --- a/src/processes/fetch.ts +++ b/src/processes/fetch.ts @@ -16,15 +16,15 @@ interface FetchBox { } export default class Fetch extends BaseProcessFactory(CommonProcess.fetch) { - static run(scroller: Scroller): void { const { workflow } = scroller; const box = { success: (data: unknown[]) => { - scroller.logger.log(() => - `resolved ${data.length} items ` + - `(index = ${scroller.state.fetch.index}, count = ${scroller.state.fetch.count})` + scroller.logger.log( + () => + `resolved ${data.length} items ` + + `(index = ${scroller.state.fetch.index}, count = ${scroller.state.fetch.count})` ); scroller.state.fetch.newItemsData = data; workflow.call({ @@ -53,7 +53,8 @@ export default class Fetch extends BaseProcessFactory(CommonProcess.fetch) { box.fail(error); } } else { - const { state: { scroll, fetch }, viewport } = scroller; + const { state, viewport } = scroller; + const { scroll, fetch } = state; if (scroll.positionBeforeAsync === null) { scroll.positionBeforeAsync = viewport.scrollPosition; } @@ -62,8 +63,8 @@ export default class Fetch extends BaseProcessFactory(CommonProcess.fetch) { box.fail = () => null; }; (result as Promise).then( - (data) => box.success(data), - (error) => box.fail(error) + data => box.success(data), + error => box.fail(error) ); } } @@ -105,9 +106,10 @@ export default class Fetch extends BaseProcessFactory(CommonProcess.fetch) { } } - if (immediateData || immediateError) { // callback case or immediate observable + if (immediateData || immediateError) { + // callback case or immediate observable return { - data: immediateError ? null : (immediateData || []), + data: immediateError ? null : immediateData || [], error: immediateError, isError: !!immediateError }; @@ -118,5 +120,4 @@ export default class Fetch extends BaseProcessFactory(CommonProcess.fetch) { reject = _reject; }); } - } diff --git a/src/processes/index.ts b/src/processes/index.ts index 9bc0ebb3..f3dafed3 100644 --- a/src/processes/index.ts +++ b/src/processes/index.ts @@ -1,31 +1,52 @@ -import Init from './init'; -import Scroll from './scroll'; -import Reset from './adapter/reset'; -import Reload from './adapter/reload'; -import Append from './adapter/append'; -import Check from './adapter/check'; -import Remove from './adapter/remove'; -import UserClip from './adapter/clip'; -import Insert from './adapter/insert'; -import Replace from './adapter/replace'; -import Update from './adapter/update'; -import Pause from './adapter/pause'; -import Fix from './adapter/fix'; -import Start from './start'; -import PreFetch from './preFetch'; -import Fetch from './fetch'; -import PostFetch from './postFetch'; -import Render from './render'; -import Adjust from './adjust'; -import PreClip from './preClip'; -import Clip from './clip'; -import End from './end'; - -import { CommonProcess, AdapterProcess, ProcessStatus } from './misc/enums'; - -export { - Init, Scroll, - Reset, Reload, Append, Check, Remove, UserClip, Insert, Replace, Update, Pause, Fix, - Start, PreFetch, Fetch, PostFetch, Render, PreClip, Clip, Adjust, End, - CommonProcess, AdapterProcess, ProcessStatus, -}; +import Init from './init'; +import Scroll from './scroll'; +import Reset from './adapter/reset'; +import Reload from './adapter/reload'; +import Append from './adapter/append'; +import Check from './adapter/check'; +import Remove from './adapter/remove'; +import UserClip from './adapter/clip'; +import Insert from './adapter/insert'; +import Replace from './adapter/replace'; +import Update from './adapter/update'; +import Pause from './adapter/pause'; +import Fix from './adapter/fix'; +import Start from './start'; +import PreFetch from './preFetch'; +import Fetch from './fetch'; +import PostFetch from './postFetch'; +import Render from './render'; +import Adjust from './adjust'; +import PreClip from './preClip'; +import Clip from './clip'; +import End from './end'; + +import { CommonProcess, AdapterProcess, ProcessStatus } from './misc/enums'; + +export { + Init, + Scroll, + Reset, + Reload, + Append, + Check, + Remove, + UserClip, + Insert, + Replace, + Update, + Pause, + Fix, + Start, + PreFetch, + Fetch, + PostFetch, + Render, + PreClip, + Clip, + Adjust, + End, + CommonProcess, + AdapterProcess, + ProcessStatus +}; diff --git a/src/processes/init.ts b/src/processes/init.ts index e93a6750..313f73fa 100644 --- a/src/processes/init.ts +++ b/src/processes/init.ts @@ -5,7 +5,6 @@ import { ProcessName } from '../interfaces/index'; const initProcesses = [CommonProcess.init, AdapterProcess.reset, AdapterProcess.reload]; export default class Init extends BaseProcessFactory(CommonProcess.init) { - static run(scroller: Scroller, process: ProcessName): void { const { state, workflow } = scroller; const isInitial = initProcesses.includes(process); @@ -16,5 +15,4 @@ export default class Init extends BaseProcessFactory(CommonProcess.init) { status: ProcessStatus.next }); } - } diff --git a/src/processes/misc/base.ts b/src/processes/misc/base.ts index 3f96d679..d75fc176 100644 --- a/src/processes/misc/base.ts +++ b/src/processes/misc/base.ts @@ -1,35 +1,40 @@ import { AdapterProcess, ProcessStatus } from './enums'; import { Scroller } from '../../scroller'; import { ADAPTER_METHODS, validate } from '../../inputs/index'; -import { ProcessName, IBaseProcess, IBaseAdapterProcess, IAdapterInput } from '../../interfaces/index'; +import { + ProcessName, + IBaseProcess, + IBaseAdapterProcess, + IAdapterInput +} from '../../interfaces/index'; export const BaseProcessFactory = (process: ProcessName): IBaseProcess => - class BaseProcess { - static process: ProcessName = process; - }; export const BaseAdapterProcessFactory = (process: AdapterProcess): IBaseAdapterProcess => - class BaseAdapterProcess extends (BaseProcessFactory(process) as IBaseProcess) { - static process: AdapterProcess = process; static parseInput( - scroller: Scroller, options: T, ignoreErrors = false, _process?: AdapterProcess + scroller: Scroller, + options: T, + ignoreErrors = false, + _process?: AdapterProcess ): IAdapterInput { const result: IAdapterInput = { data: validate(options, ADAPTER_METHODS[_process || process]) }; if (result.data.isValid) { - result.params = Object.entries(result.data.params) - .reduce((acc, [key, { value }]) => ({ + result.params = Object.entries(result.data.params).reduce( + (acc, [key, { value }]) => ({ ...acc, [key]: value - }), {} as T); + }), + {} as T + ); } else { scroller.logger.log(() => result.data.showErrors()); if (!ignoreErrors) { @@ -43,5 +48,4 @@ export const BaseAdapterProcessFactory = (process: AdapterProcess): IBaseAdapter return result; } - }; diff --git a/src/processes/misc/enums.ts b/src/processes/misc/enums.ts index 35788187..429d8c41 100644 --- a/src/processes/misc/enums.ts +++ b/src/processes/misc/enums.ts @@ -9,7 +9,7 @@ export enum CommonProcess { preClip = 'preClip', clip = 'clip', adjust = 'adjust', - end = 'end', + end = 'end' } export enum AdapterProcess { @@ -24,7 +24,7 @@ export enum AdapterProcess { clip = 'adapter.clip', insert = 'adapter.insert', pause = 'adapter.pause', - fix = 'adapter.fix', + fix = 'adapter.fix' } export enum ProcessStatus { diff --git a/src/processes/postFetch.ts b/src/processes/postFetch.ts index 52e99c44..c6805029 100644 --- a/src/processes/postFetch.ts +++ b/src/processes/postFetch.ts @@ -3,51 +3,51 @@ import { Scroller } from '../scroller'; import { Item } from '../classes/item'; export default class PostFetch extends BaseProcessFactory(CommonProcess.postFetch) { - static run(scroller: Scroller): void { const { workflow } = scroller; if (PostFetch.setItems(scroller)) { PostFetch.setBufferLimits(scroller); workflow.call({ process: PostFetch.process, - status: scroller.state.fetch.hasNewItems - ? ProcessStatus.next - : ProcessStatus.done + status: scroller.state.fetch.hasNewItems ? ProcessStatus.next : ProcessStatus.done }); } else { workflow.call({ process: PostFetch.process, status: ProcessStatus.error, - payload: { error: 'Can\'t set buffer items' } + payload: { error: 'Can not set buffer items' } }); } } static setBufferLimits(scroller: Scroller): void { - const { buffer, state: { fetch, cycle: { innerLoop } } } = scroller; - const { items, first: { index: first }, last: { index: last } } = fetch; + const { buffer, state } = scroller; + const { fetch, cycle } = state; + const { items, first, last } = fetch; if (!items.length) { - if (last < buffer.minIndex || innerLoop.isInitial) { + if (last.index < buffer.minIndex || cycle.innerLoop.isInitial) { buffer.absMinIndex = buffer.minIndex; } - if (first > buffer.maxIndex || innerLoop.isInitial) { + if (first.index > buffer.maxIndex || cycle.innerLoop.isInitial) { buffer.absMaxIndex = buffer.maxIndex; } } else { const lastIndex = items.length - 1; - if (first < items[0].$index) { + if (first.index < items[0].$index) { buffer.absMinIndex = items[0].$index; } - if (last > items[lastIndex].$index) { + if (last.index > items[lastIndex].$index) { buffer.absMaxIndex = items[lastIndex].$index; } } } static setItems(scroller: Scroller): boolean { - const { buffer, state: { fetch, cycle } } = scroller; + const { buffer, state } = scroller; + const { fetch, cycle } = state; const items = fetch.newItemsData; - if (!items || !items.length) { // empty result + if (!items || !items.length) { + // empty result return true; } // eof/bof case, need to shift fetch index if bof @@ -61,10 +61,9 @@ export default class PostFetch extends BaseProcessFactory(CommonProcess.postFetc fetchIndex = buffer.firstIndex - items.length; } } - fetch.items = items.map((item, index: number) => - new Item(fetchIndex + index, item, scroller.routines) + fetch.items = items.map( + (item, index: number) => new Item(fetchIndex + index, item, scroller.routines) ); return buffer.setItems(fetch.items); } - } diff --git a/src/processes/preClip.ts b/src/processes/preClip.ts index 57e68d8e..f40c0cad 100644 --- a/src/processes/preClip.ts +++ b/src/processes/preClip.ts @@ -3,7 +3,6 @@ import { Scroller } from '../scroller'; import { Direction } from '../inputs/index'; export default class PreClip extends BaseProcessFactory(CommonProcess.preClip) { - static run(scroller: Scroller): void { PreClip.prepareClip(scroller); @@ -17,15 +16,17 @@ export default class PreClip extends BaseProcessFactory(CommonProcess.preClip) { } static prepareClip(scroller: Scroller): void { - const { state: { fetch, clip } } = scroller; + const { fetch, clip } = scroller.state; if (PreClip.shouldNotClip(scroller)) { return; } const firstIndex = fetch.first.indexBuffer; const lastIndex = fetch.last.indexBuffer; - scroller.logger.log(() => - `looking for ${fetch.direction ? 'anti-' + fetch.direction + ' ' : ''}items ` + - `that are out of [${firstIndex}..${lastIndex}] range`); + scroller.logger.log( + () => + `looking for ${fetch.direction ? 'anti-' + fetch.direction + ' ' : ''}items ` + + `that are out of [${firstIndex}..${lastIndex}] range` + ); if (PreClip.isBackward(scroller, firstIndex)) { PreClip.prepareClipByDirection(scroller, Direction.backward, firstIndex); } @@ -56,7 +57,8 @@ export default class PreClip extends BaseProcessFactory(CommonProcess.preClip) { } static isBackward(scroller: Scroller, firstIndex: number): boolean { - const { buffer, state: { clip, fetch } } = scroller; + const { buffer, state } = scroller; + const { clip, fetch } = state; if (clip.force) { return clip.forceBackward; } @@ -69,7 +71,8 @@ export default class PreClip extends BaseProcessFactory(CommonProcess.preClip) { } static isForward(scroller: Scroller, lastIndex: number): boolean { - const { buffer, state: { clip, fetch } } = scroller; + const { buffer, state } = scroller; + const { clip, fetch } = state; if (clip.force) { return clip.forceForward; } @@ -84,15 +87,11 @@ export default class PreClip extends BaseProcessFactory(CommonProcess.preClip) { static prepareClipByDirection(scroller: Scroller, direction: Direction, edgeIndex: number): void { const forward = direction === Direction.forward; scroller.buffer.items.forEach(item => { - if ( - (!forward && item.$index < edgeIndex) || - (forward && item.$index > edgeIndex) - ) { + if ((!forward && item.$index < edgeIndex) || (forward && item.$index > edgeIndex)) { item.toRemove = true; item.removeDirection = direction; scroller.state.clip.doClip = true; } }); } - } diff --git a/src/processes/preFetch.ts b/src/processes/preFetch.ts index d791b7b7..446bab88 100644 --- a/src/processes/preFetch.ts +++ b/src/processes/preFetch.ts @@ -3,9 +3,9 @@ import { Scroller } from '../scroller'; import { Direction } from '../inputs/index'; export default class PreFetch extends BaseProcessFactory(CommonProcess.preFetch) { - static run(scroller: Scroller): void { - const { workflow, buffer, state: { fetch, cycle } } = scroller; + const { workflow, buffer, state } = scroller; + const { fetch, cycle } = state; fetch.minIndex = buffer.minIndex; // set first and last indexes of items to fetch @@ -40,7 +40,8 @@ export default class PreFetch extends BaseProcessFactory(CommonProcess.preFetch) } static setPositions(scroller: Scroller): void { - const { state: { fetch: { positions } }, viewport } = scroller; + const { state, viewport } = scroller; + const { positions } = state.fetch; const paddingDelta = viewport.getBufferPadding(); positions.before = viewport.scrollPosition; positions.startDelta = PreFetch.getStartDelta(scroller); @@ -49,8 +50,10 @@ export default class PreFetch extends BaseProcessFactory(CommonProcess.preFetch) positions.end = positions.relative + viewport.getSize() + paddingDelta; } - static getStartDelta(scroller: Scroller): number { // calculate size before start index - const { buffer, viewport: { offset } } = scroller; + static getStartDelta(scroller: Scroller): number { + // calculate size before start index + const { buffer, viewport } = scroller; + const { offset } = viewport; let startDelta = 0; if (offset) { startDelta += offset; @@ -62,14 +65,16 @@ export default class PreFetch extends BaseProcessFactory(CommonProcess.preFetch) startDelta += buffer.getSizeByIndex(index); } scroller.logger.log(() => [ - `start delta is ${startDelta}`, ...(offset ? [` (+${offset} offset)`] : []) + `start delta is ${startDelta}`, + ...(offset ? [` (+${offset} offset)`] : []) ]); return startDelta; } static setFirstIndex(scroller: Scroller): void { const { state, buffer } = scroller; - const { positions: { start }, first } = state.fetch; + const { positions, first } = state.fetch; + const { start } = positions; let firstIndex = buffer.startIndex; let firstIndexPosition = 0; if (state.cycle.innerLoop.isInitial) { @@ -79,10 +84,10 @@ export default class PreFetch extends BaseProcessFactory(CommonProcess.preFetch) } else { let position = firstIndexPosition; let index = firstIndex; - while (1) { // eslint-disable-line no-constant-condition + while (true) { if (start >= 0) { const size = buffer.getSizeByIndex(index); - const diff = (position + size) - start; + const diff = position + size - start; if (diff > 0) { firstIndex = index; firstIndexPosition = position; @@ -114,8 +119,10 @@ export default class PreFetch extends BaseProcessFactory(CommonProcess.preFetch) } static setLastIndex(scroller: Scroller): void { - const { state: { fetch, cycle }, buffer, settings } = scroller; - const { firstVisible, positions: { relative, end }, first, last } = fetch; + const { state, buffer, settings } = scroller; + const { fetch, cycle } = state; + const { firstVisible, positions, first, last } = fetch; + const { relative, end } = positions; let lastIndex; if (!buffer.defaultSize) { // just to fetch forward bufferSize items if neither averageItemSize nor itemSize are present @@ -125,7 +132,7 @@ export default class PreFetch extends BaseProcessFactory(CommonProcess.preFetch) let index = first.indexBuffer; let position = first.position; lastIndex = index; - while (1) { // eslint-disable-line no-constant-condition + while (true) { lastIndex = index; const size = buffer.getSizeByIndex(index); position += size; @@ -180,7 +187,8 @@ export default class PreFetch extends BaseProcessFactory(CommonProcess.preFetch) } static checkBufferGaps(scroller: Scroller): void { - const { buffer, state: { fetch } } = scroller; + const { buffer, state } = scroller; + const { fetch } = state; if (!buffer.size) { return; } @@ -200,7 +208,8 @@ export default class PreFetch extends BaseProcessFactory(CommonProcess.preFetch) } static checkFetchPackSize(scroller: Scroller): void { - const { buffer, state: { fetch } } = scroller; + const { buffer, state } = scroller; + const { fetch } = state; if (!fetch.shouldFetch) { return; } @@ -210,7 +219,8 @@ export default class PreFetch extends BaseProcessFactory(CommonProcess.preFetch) if (diff <= 0) { return; } - if (!buffer.size || lastIndex > buffer.items[0].$index) { // forward + if (!buffer.size || lastIndex > buffer.items[0].$index) { + // forward const newLastIndex = Math.min(lastIndex + diff, buffer.absMaxIndex); if (newLastIndex > lastIndex) { fetch.last.index = fetch.last.indexBuffer = newLastIndex; @@ -228,11 +238,13 @@ export default class PreFetch extends BaseProcessFactory(CommonProcess.preFetch) } static setFetchDirection(scroller: Scroller): void { - const { buffer, state: { fetch } } = scroller; + const { buffer, state } = scroller; + const { fetch } = state; if (fetch.last.index) { let direction = Direction.forward; if (buffer.size) { - direction = fetch.last.index < buffer.items[0].$index ? Direction.backward : Direction.forward; + direction = + fetch.last.index < buffer.items[0].$index ? Direction.backward : Direction.forward; } fetch.direction = direction; scroller.logger.log(() => `fetch direction is "${direction}"`); @@ -246,10 +258,11 @@ export default class PreFetch extends BaseProcessFactory(CommonProcess.preFetch) return ProcessStatus.next; } if (fetch.shouldFetch) { - scroller.logger.log(() => `going to fetch ${fetch.count} items started from index ${fetch.index}`); + scroller.logger.log( + () => `going to fetch ${fetch.count} items started from index ${fetch.index}` + ); return ProcessStatus.next; } return ProcessStatus.done; } - } diff --git a/src/processes/render.ts b/src/processes/render.ts index 1ffa1a11..e17b5917 100644 --- a/src/processes/render.ts +++ b/src/processes/render.ts @@ -3,46 +3,48 @@ import { Scroller } from '../scroller'; import { Item } from '../classes/item'; export default class Render extends BaseProcessFactory(CommonProcess.render) { - static run(scroller: Scroller): void { - const { workflow, state: { cycle, render, scroll, fetch }, viewport, routines } = scroller; + const { workflow, state, viewport, routines } = scroller; + const { cycle, render, scroll, fetch } = state; scroller.logger.stat('before new items render'); if (scroll.positionBeforeAsync === null) { scroll.positionBeforeAsync = viewport.scrollPosition; } - render.cancel = routines.render(() => { - render.cancel = null; - if (Render.doRender(scroller)) { - workflow.call({ - process: Render.process, - status: render.noSize ? ProcessStatus.done : ProcessStatus.next, - payload: { process: cycle.initiator } - }); - } else { - workflow.call({ - process: Render.process, - status: ProcessStatus.error, - payload: { error: 'Can\'t associate item with element' } - }); - } - }, { items: fetch.items.map(i => i.get()) }); + render.cancel = routines.render( + () => { + render.cancel = null; + if (Render.doRender(scroller)) { + workflow.call({ + process: Render.process, + status: render.noSize ? ProcessStatus.done : ProcessStatus.next, + payload: { process: cycle.initiator } + }); + } else { + workflow.call({ + process: Render.process, + status: ProcessStatus.error, + payload: { error: 'Can not associate item with element' } + }); + } + }, + { items: fetch.items.map(i => i.get()) } + ); } static doRender(scroller: Scroller): boolean { - const { state: { fetch, render }, viewport, buffer, logger } = scroller; + const { state, viewport, buffer, logger } = scroller; + const { fetch, render } = state; render.positionBefore = viewport.scrollPosition; if (!fetch.isCheck) { render.sizeBefore = viewport.getScrollableSize(); - if (!fetch.items.every(item => - Render.processElement(scroller, item) - )) { + if (!fetch.items.every(item => Render.processElement(scroller, item))) { return false; } } buffer.checkDefaultSize(); render.sizeAfter = viewport.getScrollableSize(); logger.stat('after new items render'); - logger.log(() => render.noSize ? 'viewport size has not been changed' : void 0); + logger.log(() => (render.noSize ? 'viewport size has not been changed' : void 0)); return true; } @@ -58,5 +60,4 @@ export default class Render extends BaseProcessFactory(CommonProcess.render) { buffer.cacheItem(item); return true; } - } diff --git a/src/processes/scroll.ts b/src/processes/scroll.ts index f0a9ad74..cb558f67 100644 --- a/src/processes/scroll.ts +++ b/src/processes/scroll.ts @@ -4,9 +4,7 @@ import { Direction } from '../inputs/index'; import { ScrollEventData, ScrollerWorkflow } from '../interfaces/index'; export default class Scroll extends BaseProcessFactory(CommonProcess.scroll) { - - // eslint-disable-next-line @typescript-eslint/no-unused-vars - static run(scroller: Scroller, payload?: { event?: Event }): void { + static run(scroller: Scroller, _payload?: { event?: Event }): void { const { workflow, viewport } = scroller; const position = viewport.scrollPosition; @@ -14,9 +12,7 @@ export default class Scroll extends BaseProcessFactory(CommonProcess.scroll) { return; } - Scroll.onThrottle(scroller, position, () => - Scroll.onScroll(scroller, workflow) - ); + Scroll.onThrottle(scroller, position, () => Scroll.onScroll(scroller, workflow)); } static onSynthetic(scroller: Scroller, position: number): boolean { @@ -28,29 +24,35 @@ export default class Scroll extends BaseProcessFactory(CommonProcess.scroll) { } if (!scroll.syntheticFulfill || synthPos === position) { scroller.logger.log(() => [ - 'skipping scroll', position, `[${scroll.syntheticFulfill ? '' : 'pre-'}synthetic]` + 'skipping scroll', + position, + `[${scroll.syntheticFulfill ? '' : 'pre-'}synthetic]` ]); return true; } scroller.logger.log(() => [ - 'synthetic scroll has been fulfilled:', position, position < synthPos ? '<' : '>', synthPos + 'synthetic scroll has been fulfilled:', + position, + position < synthPos ? '<' : '>', + synthPos ]); } return false; } static onThrottle(scroller: Scroller, position: number, done: () => void): void { - const { state: { scroll }, settings: { throttle }, logger } = scroller; + const { state, settings, logger } = scroller; + const { scroll } = state; scroll.current = Scroll.getScrollEvent(position, scroll.previous); const { direction, time } = scroll.current; const timeDiff = scroll.previous ? time - scroll.previous.time : Infinity; - const delta = throttle - timeDiff; + const delta = settings.throttle - timeDiff; const shouldDelay = isFinite(delta) && delta > 0; const alreadyDelayed = !!scroll.scrollTimer; logger.log(() => [ direction === Direction.backward ? '\u2934' : '\u2935', position, - shouldDelay ? (timeDiff + 'ms') : '0ms', + shouldDelay ? timeDiff + 'ms' : '0ms', shouldDelay ? (alreadyDelayed ? 'delayed' : `/ ${delta}ms delay`) : '' ]); if (!shouldDelay) { @@ -68,7 +70,7 @@ export default class Scroll extends BaseProcessFactory(CommonProcess.scroll) { return [ curr.direction === Direction.backward ? '\u2934' : '\u2935', curr.position, - (curr.time - time) + 'ms', + curr.time - time + 'ms', 'triggered by timer set on', position ]; @@ -93,12 +95,16 @@ export default class Scroll extends BaseProcessFactory(CommonProcess.scroll) { } static onScroll(scroller: Scroller, workflow: ScrollerWorkflow): void { - const { state: { scroll, cycle } } = scroller; + const { scroll, cycle } = scroller.state; scroll.previous = { ...(scroll.current as ScrollEventData) }; scroll.current = null; if (cycle.busy.get()) { - scroller.logger.log(() => ['skipping scroll', (scroll.previous as ScrollEventData).position, '[pending]']); + scroller.logger.log(() => [ + 'skipping scroll', + (scroll.previous as ScrollEventData).position, + '[pending]' + ]); return; } @@ -107,5 +113,4 @@ export default class Scroll extends BaseProcessFactory(CommonProcess.scroll) { status: ProcessStatus.next }); } - } diff --git a/src/processes/start.ts b/src/processes/start.ts index ed63d9a1..db3e54a0 100644 --- a/src/processes/start.ts +++ b/src/processes/start.ts @@ -2,7 +2,6 @@ import { BaseProcessFactory, CommonProcess, ProcessStatus } from './misc/index'; import { Scroller } from '../scroller'; export default class Start extends BaseProcessFactory(CommonProcess.start) { - static run(scroller: Scroller): void { const payload = scroller.state.startInnerLoop(); @@ -12,5 +11,4 @@ export default class Start extends BaseProcessFactory(CommonProcess.start) { payload }); } - } diff --git a/src/scroller.ts b/src/scroller.ts index a5357b3f..be65a461 100644 --- a/src/scroller.ts +++ b/src/scroller.ts @@ -10,7 +10,12 @@ import { Reactive } from './classes/reactive'; import { validate, DATASOURCE } from './inputs/index'; import core from './version'; import { - ScrollerWorkflow, IDatasource, IDatasourceConstructed, ScrollerParams, IPackages, ProcessSubject + ScrollerWorkflow, + IDatasource, + IDatasourceConstructed, + ScrollerParams, + IPackages, + ProcessSubject } from './interfaces/index'; export const INVALID_DATASOURCE_PREFIX = 'Invalid datasource:'; @@ -30,9 +35,14 @@ export class Scroller { public adapter: Adapter; constructor({ - datasource, consumer, element, workflow, Routines: CustomRoutines, scroller + datasource, + consumer, + element, + workflow, + Routines: CustomRoutines, + scroller }: ScrollerParams) { - const { params: { get } } = validate(datasource, DATASOURCE); + const { get } = validate(datasource, DATASOURCE).params; if (!get.isValid) { throw new Error(`${INVALID_DATASOURCE_PREFIX} ${get.errors[0]}`); } @@ -44,17 +54,23 @@ export class Scroller { // In general, custom Routines must extend the original Routines. If not, we provide implicit extending. // This is undocumented feature. It should be removed in vscroll v2. if (CustomRoutines && !(CustomRoutines.prototype instanceof Routines)) { - class __Routines extends Routines { } + class __Routines extends Routines {} Object.getOwnPropertyNames(CustomRoutines.prototype) .filter(method => method !== 'constructor') - .forEach(method => - (__Routines.prototype as unknown as Record)[method] = CustomRoutines?.prototype[method] + .forEach( + method => + ((__Routines.prototype as unknown as Record)[method] = + CustomRoutines?.prototype[method]) ); CustomRoutines = __Routines; } this.workflow = workflow; - this.settings = new Settings(datasource.settings, datasource.devSettings, ++instanceCount); + this.settings = new Settings( + datasource.settings, + datasource.devSettings, + ++instanceCount + ); this.logger = new Logger(this as Scroller, packageInfo, datasource.adapter); this.routines = new (CustomRoutines || Routines)(element, this.settings); this.state = new State(packageInfo, this.settings, scroller ? scroller.state : void 0); @@ -66,7 +82,8 @@ export class Scroller { } initDatasource(datasource: IDatasource, scroller?: Scroller): void { - if (scroller) { // scroller re-instantiating case + if (scroller) { + // scroller re-instantiating case this.datasource = datasource as IDatasourceConstructed; this.adapter = scroller.adapter; // todo: what about (this.settings.adapter !== scroller.setting.adapter) case? @@ -75,9 +92,11 @@ export class Scroller { // scroller is being instantiated for the first time const constructed = datasource instanceof DatasourceGeneric; const mockAdapter = !constructed && !this.settings.adapter; - if (constructed) { // datasource is already instantiated + if (constructed) { + // datasource is already instantiated this.datasource = datasource as IDatasourceConstructed; - } else { // datasource as POJO + } else { + // datasource as POJO const DS = makeDatasource(() => ({ mock: mockAdapter })); this.datasource = new DS(datasource); if (this.settings.adapter) { @@ -103,14 +122,13 @@ export class Scroller { dispose(forever?: boolean): void { this.logger.log(() => 'disposing scroller' + (forever ? ' (forever)' : '')); - if (forever) { // Adapter is not re-instantiated on reset + if (forever) { + // Adapter is not re-instantiated on reset this.adapter.dispose(); } this.buffer.dispose(); this.state.dispose(); } - finalize(): void { - } - + finalize(): void {} } diff --git a/src/workflow-transducer.ts b/src/workflow-transducer.ts index d9c8cae0..f6339bd6 100644 --- a/src/workflow-transducer.ts +++ b/src/workflow-transducer.ts @@ -23,7 +23,7 @@ import { PreClip, Clip, Adjust, - End, + End } from './processes/index'; import { StateMachineParams } from './interfaces/index'; @@ -42,7 +42,8 @@ export const runStateMachine = ({ const { options } = payload; switch (process) { case CommonProcess.init: - if (status === Status.start) { // App start + if (status === Status.start) { + // App start run(Init)(process); } if (status === Status.next) { diff --git a/src/workflow.ts b/src/workflow.ts index 66fc770d..981ae1e4 100644 --- a/src/workflow.ts +++ b/src/workflow.ts @@ -2,7 +2,7 @@ import { Scroller } from './scroller'; import { runStateMachine } from './workflow-transducer'; import { Reactive } from './classes/reactive'; import { Item } from './classes/item'; -import { AdapterProcess, CommonProcess, ProcessStatus as Status, } from './processes/index'; +import { AdapterProcess, CommonProcess, ProcessStatus as Status } from './processes/index'; import { WORKFLOW, validate } from './inputs/index'; import { WorkflowParams, @@ -13,11 +13,10 @@ import { WorkflowError, InterruptParams, StateMachineMethods, - ScrollerWorkflow, + ScrollerWorkflow } from './interfaces/index'; export class Workflow { - isInitialized: boolean; disposed: boolean; initTimer: ReturnType | null; @@ -59,7 +58,11 @@ export class Workflow { }; this.scroller = new Scroller({ - element, datasource, consumer, workflow: this.getUpdater(), Routines + element, + datasource, + consumer, + workflow: this.getUpdater(), + Routines }); if (this.scroller.settings.initializeDelay) { @@ -77,8 +80,8 @@ export class Workflow { // set up scroll event listener const { routines } = this.scroller; - const onScrollHandler: EventListener = - event => this.callWorkflow({ + const onScrollHandler: EventListener = event => + this.callWorkflow({ process: CommonProcess.scroll, status: Status.start, payload: { event } @@ -118,17 +121,19 @@ export class Workflow { getUpdater(): ScrollerWorkflow { return { call: this.callWorkflow.bind(this), - onDataChanged: this.changeItems.bind(this), + onDataChanged: this.changeItems.bind(this) }; } process(data: ProcessSubject): void { const { status, process, payload } = data; if (this.scroller.settings.logProcessRun) { - const _fire = this.scroller.settings.logColor - ? ['%cfire%c', 'color: #cc7777;', 'color: #000000;'] - : ['fire']; - this.scroller.logger.log(() => [..._fire, process, `"${status}"`, ...(payload !== void 0 ? [payload] : [])]); + this.scroller.logger.log(() => { + const _fire = this.scroller.settings.logColor + ? ['%cfire%c', 'color: #cc7777;', 'color: #000000;'] + : ['fire']; + return [..._fire, process, `"${status}"`, ...(payload !== void 0 ? [payload] : [])]; + }); } this.scroller.logger.logProcess(data); @@ -145,17 +150,19 @@ export class Workflow { return ({ run, process, name }: ProcessClass) => (...args: unknown[]): void => { if (this.scroller.settings.logProcessRun) { - const _run = this.scroller.settings.logColor - ? ['%crun%c', 'color: #333399;', 'color: #000000;'] - : ['run']; - this.scroller.logger.log(() => [..._run, process || name, ...args]); + this.scroller.logger.log(() => { + const _run = this.scroller.settings.logColor + ? ['%crun%c', 'color: #333399;', 'color: #000000;'] + : ['run']; + return [..._run, process || name, ...args]; + }); } run(this.scroller as Scroller, ...args); }; } onError(process: ProcessName, payload?: ProcessPayload): void { - const message: string = payload && String(payload.error) || ''; + const message: string = (payload && String(payload.error)) || ''; const { time, cycle } = this.scroller.state; this.errors.push({ process, @@ -171,14 +178,16 @@ export class Workflow { const { workflow, logger } = this.scroller; // we are going to create a new reference for the scroller.workflow object // calling the old version of the scroller.workflow by any outstanding async processes will be skipped - workflow.call = (_: ProcessSubject) => // eslint-disable-line @typescript-eslint/no-unused-vars - logger.log('[skip wf call]'); + workflow.call = (_: ProcessSubject) => logger.log('[skip wf call]'); workflow.call.interrupted = true; this.scroller.workflow = this.getUpdater(); this.interruptionCount++; - logger.log(() => `workflow had been interrupted by the ${process} process (${this.interruptionCount})`); + logger.log( + () => `workflow had been interrupted by the ${process} process (${this.interruptionCount})` + ); } - if (datasource) { // Scroller re-initialization case + if (datasource) { + // Scroller re-initialization case const reInit = () => { this.scroller.logger.log('new Scroller instantiation'); const scroller = new Scroller({ datasource, scroller: this.scroller }); @@ -220,7 +229,5 @@ export class Workflow { this.disposed = true; } - finalize(): void { - } - + finalize(): void {} } From bb5316eebe766ba812b32b442971bfc13479fcc1 Mon Sep 17 00:00:00 2001 From: Denis Aleksanov Date: Fri, 28 Nov 2025 14:52:39 +0100 Subject: [PATCH 49/51] Update npm dependencies --- package-lock.json | 501 +++++++++++++++++++++++----------------------- package.json | 22 +- 2 files changed, 263 insertions(+), 260 deletions(-) diff --git a/package-lock.json b/package-lock.json index 6efa1207..d7338642 100644 --- a/package-lock.json +++ b/package-lock.json @@ -9,24 +9,24 @@ "version": "1.7.0", "license": "MIT", "dependencies": { - "tslib": "^2.3.1" + "tslib": "^2.8.1" }, "devDependencies": { - "@rollup/plugin-node-resolve": "^16.0.1", + "@rollup/plugin-node-resolve": "^16.0.3", "@rollup/plugin-terser": "^0.4.4", - "@typescript-eslint/eslint-plugin": "^8.38.0", - "@typescript-eslint/parser": "^8.38.0", - "chalk": "^5.0.0", - "eslint": "^9.32.0", - "globals": "^16.3.0", + "@typescript-eslint/eslint-plugin": "^8.48.0", + "@typescript-eslint/parser": "^8.48.0", + "chalk": "^5.6.2", + "eslint": "^9.39.1", + "globals": "^16.5.0", "husky": "^9.1.7", "lint-staged": "^16.2.7", - "prettier": "^3.6.2", - "rollup": "^4.45.1", + "prettier": "^3.7.1", + "rollup": "^4.53.3", "rollup-plugin-license": "^3.6.0", - "rollup-plugin-sourcemaps2": "^0.5.3", + "rollup-plugin-sourcemaps2": "^0.5.4", "shelljs": "^0.10.0", - "typescript": "^5.8.3" + "typescript": "^5.9.3" } }, "node_modules/@eslint-community/eslint-utils": { @@ -443,9 +443,9 @@ } }, "node_modules/@rollup/rollup-android-arm-eabi": { - "version": "4.53.2", - "resolved": "https://registry.npmjs.org/@rollup/rollup-android-arm-eabi/-/rollup-android-arm-eabi-4.53.2.tgz", - "integrity": "sha512-yDPzwsgiFO26RJA4nZo8I+xqzh7sJTZIWQOxn+/XOdPE31lAvLIYCKqjV+lNH/vxE2L2iH3plKxDCRK6i+CwhA==", + "version": "4.53.3", + "resolved": "https://registry.npmjs.org/@rollup/rollup-android-arm-eabi/-/rollup-android-arm-eabi-4.53.3.tgz", + "integrity": "sha512-mRSi+4cBjrRLoaal2PnqH82Wqyb+d3HsPUN/W+WslCXsZsyHa9ZeQQX/pQsZaVIWDkPcpV6jJ+3KLbTbgnwv8w==", "cpu": [ "arm" ], @@ -457,9 +457,9 @@ ] }, "node_modules/@rollup/rollup-android-arm64": { - "version": "4.53.2", - "resolved": "https://registry.npmjs.org/@rollup/rollup-android-arm64/-/rollup-android-arm64-4.53.2.tgz", - "integrity": "sha512-k8FontTxIE7b0/OGKeSN5B6j25EuppBcWM33Z19JoVT7UTXFSo3D9CdU39wGTeb29NO3XxpMNauh09B+Ibw+9g==", + "version": "4.53.3", + "resolved": "https://registry.npmjs.org/@rollup/rollup-android-arm64/-/rollup-android-arm64-4.53.3.tgz", + "integrity": "sha512-CbDGaMpdE9sh7sCmTrTUyllhrg65t6SwhjlMJsLr+J8YjFuPmCEjbBSx4Z/e4SmDyH3aB5hGaJUP2ltV/vcs4w==", "cpu": [ "arm64" ], @@ -471,9 +471,9 @@ ] }, "node_modules/@rollup/rollup-darwin-arm64": { - "version": "4.53.2", - "resolved": "https://registry.npmjs.org/@rollup/rollup-darwin-arm64/-/rollup-darwin-arm64-4.53.2.tgz", - "integrity": "sha512-A6s4gJpomNBtJ2yioj8bflM2oogDwzUiMl2yNJ2v9E7++sHrSrsQ29fOfn5DM/iCzpWcebNYEdXpaK4tr2RhfQ==", + "version": "4.53.3", + "resolved": "https://registry.npmjs.org/@rollup/rollup-darwin-arm64/-/rollup-darwin-arm64-4.53.3.tgz", + "integrity": "sha512-Nr7SlQeqIBpOV6BHHGZgYBuSdanCXuw09hon14MGOLGmXAFYjx1wNvquVPmpZnl0tLjg25dEdr4IQ6GgyToCUA==", "cpu": [ "arm64" ], @@ -485,9 +485,9 @@ ] }, "node_modules/@rollup/rollup-darwin-x64": { - "version": "4.53.2", - "resolved": "https://registry.npmjs.org/@rollup/rollup-darwin-x64/-/rollup-darwin-x64-4.53.2.tgz", - "integrity": "sha512-e6XqVmXlHrBlG56obu9gDRPW3O3hLxpwHpLsBJvuI8qqnsrtSZ9ERoWUXtPOkY8c78WghyPHZdmPhHLWNdAGEw==", + "version": "4.53.3", + "resolved": "https://registry.npmjs.org/@rollup/rollup-darwin-x64/-/rollup-darwin-x64-4.53.3.tgz", + "integrity": "sha512-DZ8N4CSNfl965CmPktJ8oBnfYr3F8dTTNBQkRlffnUarJ2ohudQD17sZBa097J8xhQ26AwhHJ5mvUyQW8ddTsQ==", "cpu": [ "x64" ], @@ -499,9 +499,9 @@ ] }, "node_modules/@rollup/rollup-freebsd-arm64": { - "version": "4.53.2", - "resolved": "https://registry.npmjs.org/@rollup/rollup-freebsd-arm64/-/rollup-freebsd-arm64-4.53.2.tgz", - "integrity": "sha512-v0E9lJW8VsrwPux5Qe5CwmH/CF/2mQs6xU1MF3nmUxmZUCHazCjLgYvToOk+YuuUqLQBio1qkkREhxhc656ViA==", + "version": "4.53.3", + "resolved": "https://registry.npmjs.org/@rollup/rollup-freebsd-arm64/-/rollup-freebsd-arm64-4.53.3.tgz", + "integrity": "sha512-yMTrCrK92aGyi7GuDNtGn2sNW+Gdb4vErx4t3Gv/Tr+1zRb8ax4z8GWVRfr3Jw8zJWvpGHNpss3vVlbF58DZ4w==", "cpu": [ "arm64" ], @@ -513,9 +513,9 @@ ] }, "node_modules/@rollup/rollup-freebsd-x64": { - "version": "4.53.2", - "resolved": "https://registry.npmjs.org/@rollup/rollup-freebsd-x64/-/rollup-freebsd-x64-4.53.2.tgz", - "integrity": "sha512-ClAmAPx3ZCHtp6ysl4XEhWU69GUB1D+s7G9YjHGhIGCSrsg00nEGRRZHmINYxkdoJehde8VIsDC5t9C0gb6yqA==", + "version": "4.53.3", + "resolved": "https://registry.npmjs.org/@rollup/rollup-freebsd-x64/-/rollup-freebsd-x64-4.53.3.tgz", + "integrity": "sha512-lMfF8X7QhdQzseM6XaX0vbno2m3hlyZFhwcndRMw8fbAGUGL3WFMBdK0hbUBIUYcEcMhVLr1SIamDeuLBnXS+Q==", "cpu": [ "x64" ], @@ -527,9 +527,9 @@ ] }, "node_modules/@rollup/rollup-linux-arm-gnueabihf": { - "version": "4.53.2", - "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-arm-gnueabihf/-/rollup-linux-arm-gnueabihf-4.53.2.tgz", - "integrity": "sha512-EPlb95nUsz6Dd9Qy13fI5kUPXNSljaG9FiJ4YUGU1O/Q77i5DYFW5KR8g1OzTcdZUqQQ1KdDqsTohdFVwCwjqg==", + "version": "4.53.3", + "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-arm-gnueabihf/-/rollup-linux-arm-gnueabihf-4.53.3.tgz", + "integrity": "sha512-k9oD15soC/Ln6d2Wv/JOFPzZXIAIFLp6B+i14KhxAfnq76ajt0EhYc5YPeX6W1xJkAdItcVT+JhKl1QZh44/qw==", "cpu": [ "arm" ], @@ -541,9 +541,9 @@ ] }, "node_modules/@rollup/rollup-linux-arm-musleabihf": { - "version": "4.53.2", - "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-arm-musleabihf/-/rollup-linux-arm-musleabihf-4.53.2.tgz", - "integrity": "sha512-BOmnVW+khAUX+YZvNfa0tGTEMVVEerOxN0pDk2E6N6DsEIa2Ctj48FOMfNDdrwinocKaC7YXUZ1pHlKpnkja/Q==", + "version": "4.53.3", + "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-arm-musleabihf/-/rollup-linux-arm-musleabihf-4.53.3.tgz", + "integrity": "sha512-vTNlKq+N6CK/8UktsrFuc+/7NlEYVxgaEgRXVUVK258Z5ymho29skzW1sutgYjqNnquGwVUObAaxae8rZ6YMhg==", "cpu": [ "arm" ], @@ -555,9 +555,9 @@ ] }, "node_modules/@rollup/rollup-linux-arm64-gnu": { - "version": "4.53.2", - "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-arm64-gnu/-/rollup-linux-arm64-gnu-4.53.2.tgz", - "integrity": "sha512-Xt2byDZ+6OVNuREgBXr4+CZDJtrVso5woFtpKdGPhpTPHcNG7D8YXeQzpNbFRxzTVqJf7kvPMCub/pcGUWgBjA==", + "version": "4.53.3", + "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-arm64-gnu/-/rollup-linux-arm64-gnu-4.53.3.tgz", + "integrity": "sha512-RGrFLWgMhSxRs/EWJMIFM1O5Mzuz3Xy3/mnxJp/5cVhZ2XoCAxJnmNsEyeMJtpK+wu0FJFWz+QF4mjCA7AUQ3w==", "cpu": [ "arm64" ], @@ -569,9 +569,9 @@ ] }, "node_modules/@rollup/rollup-linux-arm64-musl": { - "version": "4.53.2", - "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-arm64-musl/-/rollup-linux-arm64-musl-4.53.2.tgz", - "integrity": "sha512-+LdZSldy/I9N8+klim/Y1HsKbJ3BbInHav5qE9Iy77dtHC/pibw1SR/fXlWyAk0ThnpRKoODwnAuSjqxFRDHUQ==", + "version": "4.53.3", + "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-arm64-musl/-/rollup-linux-arm64-musl-4.53.3.tgz", + "integrity": "sha512-kASyvfBEWYPEwe0Qv4nfu6pNkITLTb32p4yTgzFCocHnJLAHs+9LjUu9ONIhvfT/5lv4YS5muBHyuV84epBo/A==", "cpu": [ "arm64" ], @@ -583,9 +583,9 @@ ] }, "node_modules/@rollup/rollup-linux-loong64-gnu": { - "version": "4.53.2", - "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-loong64-gnu/-/rollup-linux-loong64-gnu-4.53.2.tgz", - "integrity": "sha512-8ms8sjmyc1jWJS6WdNSA23rEfdjWB30LH8Wqj0Cqvv7qSHnvw6kgMMXRdop6hkmGPlyYBdRPkjJnj3KCUHV/uQ==", + "version": "4.53.3", + "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-loong64-gnu/-/rollup-linux-loong64-gnu-4.53.3.tgz", + "integrity": "sha512-JiuKcp2teLJwQ7vkJ95EwESWkNRFJD7TQgYmCnrPtlu50b4XvT5MOmurWNrCj3IFdyjBQ5p9vnrX4JM6I8OE7g==", "cpu": [ "loong64" ], @@ -597,9 +597,9 @@ ] }, "node_modules/@rollup/rollup-linux-ppc64-gnu": { - "version": "4.53.2", - "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-ppc64-gnu/-/rollup-linux-ppc64-gnu-4.53.2.tgz", - "integrity": "sha512-3HRQLUQbpBDMmzoxPJYd3W6vrVHOo2cVW8RUo87Xz0JPJcBLBr5kZ1pGcQAhdZgX9VV7NbGNipah1omKKe23/g==", + "version": "4.53.3", + "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-ppc64-gnu/-/rollup-linux-ppc64-gnu-4.53.3.tgz", + "integrity": "sha512-EoGSa8nd6d3T7zLuqdojxC20oBfNT8nexBbB/rkxgKj5T5vhpAQKKnD+h3UkoMuTyXkP5jTjK/ccNRmQrPNDuw==", "cpu": [ "ppc64" ], @@ -611,9 +611,9 @@ ] }, "node_modules/@rollup/rollup-linux-riscv64-gnu": { - "version": "4.53.2", - "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-riscv64-gnu/-/rollup-linux-riscv64-gnu-4.53.2.tgz", - "integrity": "sha512-fMjKi+ojnmIvhk34gZP94vjogXNNUKMEYs+EDaB/5TG/wUkoeua7p7VCHnE6T2Tx+iaghAqQX8teQzcvrYpaQA==", + "version": "4.53.3", + "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-riscv64-gnu/-/rollup-linux-riscv64-gnu-4.53.3.tgz", + "integrity": "sha512-4s+Wped2IHXHPnAEbIB0YWBv7SDohqxobiiPA1FIWZpX+w9o2i4LezzH/NkFUl8LRci/8udci6cLq+jJQlh+0g==", "cpu": [ "riscv64" ], @@ -625,9 +625,9 @@ ] }, "node_modules/@rollup/rollup-linux-riscv64-musl": { - "version": "4.53.2", - "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-riscv64-musl/-/rollup-linux-riscv64-musl-4.53.2.tgz", - "integrity": "sha512-XuGFGU+VwUUV5kLvoAdi0Wz5Xbh2SrjIxCtZj6Wq8MDp4bflb/+ThZsVxokM7n0pcbkEr2h5/pzqzDYI7cCgLQ==", + "version": "4.53.3", + "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-riscv64-musl/-/rollup-linux-riscv64-musl-4.53.3.tgz", + "integrity": "sha512-68k2g7+0vs2u9CxDt5ktXTngsxOQkSEV/xBbwlqYcUrAVh6P9EgMZvFsnHy4SEiUl46Xf0IObWVbMvPrr2gw8A==", "cpu": [ "riscv64" ], @@ -639,9 +639,9 @@ ] }, "node_modules/@rollup/rollup-linux-s390x-gnu": { - "version": "4.53.2", - "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-s390x-gnu/-/rollup-linux-s390x-gnu-4.53.2.tgz", - "integrity": "sha512-w6yjZF0P+NGzWR3AXWX9zc0DNEGdtvykB03uhonSHMRa+oWA6novflo2WaJr6JZakG2ucsyb+rvhrKac6NIy+w==", + "version": "4.53.3", + "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-s390x-gnu/-/rollup-linux-s390x-gnu-4.53.3.tgz", + "integrity": "sha512-VYsFMpULAz87ZW6BVYw3I6sWesGpsP9OPcyKe8ofdg9LHxSbRMd7zrVrr5xi/3kMZtpWL/wC+UIJWJYVX5uTKg==", "cpu": [ "s390x" ], @@ -653,9 +653,9 @@ ] }, "node_modules/@rollup/rollup-linux-x64-gnu": { - "version": "4.53.2", - "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-x64-gnu/-/rollup-linux-x64-gnu-4.53.2.tgz", - "integrity": "sha512-yo8d6tdfdeBArzC7T/PnHd7OypfI9cbuZzPnzLJIyKYFhAQ8SvlkKtKBMbXDxe1h03Rcr7u++nFS7tqXz87Gtw==", + "version": "4.53.3", + "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-x64-gnu/-/rollup-linux-x64-gnu-4.53.3.tgz", + "integrity": "sha512-3EhFi1FU6YL8HTUJZ51imGJWEX//ajQPfqWLI3BQq4TlvHy4X0MOr5q3D2Zof/ka0d5FNdPwZXm3Yyib/UEd+w==", "cpu": [ "x64" ], @@ -667,9 +667,9 @@ ] }, "node_modules/@rollup/rollup-linux-x64-musl": { - "version": "4.53.2", - "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-x64-musl/-/rollup-linux-x64-musl-4.53.2.tgz", - "integrity": "sha512-ah59c1YkCxKExPP8O9PwOvs+XRLKwh/mV+3YdKqQ5AMQ0r4M4ZDuOrpWkUaqO7fzAHdINzV9tEVu8vNw48z0lA==", + "version": "4.53.3", + "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-x64-musl/-/rollup-linux-x64-musl-4.53.3.tgz", + "integrity": "sha512-eoROhjcc6HbZCJr+tvVT8X4fW3/5g/WkGvvmwz/88sDtSJzO7r/blvoBDgISDiCjDRZmHpwud7h+6Q9JxFwq1Q==", "cpu": [ "x64" ], @@ -681,9 +681,9 @@ ] }, "node_modules/@rollup/rollup-openharmony-arm64": { - "version": "4.53.2", - "resolved": "https://registry.npmjs.org/@rollup/rollup-openharmony-arm64/-/rollup-openharmony-arm64-4.53.2.tgz", - "integrity": "sha512-4VEd19Wmhr+Zy7hbUsFZ6YXEiP48hE//KPLCSVNY5RMGX2/7HZ+QkN55a3atM1C/BZCGIgqN+xrVgtdak2S9+A==", + "version": "4.53.3", + "resolved": "https://registry.npmjs.org/@rollup/rollup-openharmony-arm64/-/rollup-openharmony-arm64-4.53.3.tgz", + "integrity": "sha512-OueLAWgrNSPGAdUdIjSWXw+u/02BRTcnfw9PN41D2vq/JSEPnJnVuBgw18VkN8wcd4fjUs+jFHVM4t9+kBSNLw==", "cpu": [ "arm64" ], @@ -695,9 +695,9 @@ ] }, "node_modules/@rollup/rollup-win32-arm64-msvc": { - "version": "4.53.2", - "resolved": "https://registry.npmjs.org/@rollup/rollup-win32-arm64-msvc/-/rollup-win32-arm64-msvc-4.53.2.tgz", - "integrity": "sha512-IlbHFYc/pQCgew/d5fslcy1KEaYVCJ44G8pajugd8VoOEI8ODhtb/j8XMhLpwHCMB3yk2J07ctup10gpw2nyMA==", + "version": "4.53.3", + "resolved": "https://registry.npmjs.org/@rollup/rollup-win32-arm64-msvc/-/rollup-win32-arm64-msvc-4.53.3.tgz", + "integrity": "sha512-GOFuKpsxR/whszbF/bzydebLiXIHSgsEUp6M0JI8dWvi+fFa1TD6YQa4aSZHtpmh2/uAlj/Dy+nmby3TJ3pkTw==", "cpu": [ "arm64" ], @@ -709,9 +709,9 @@ ] }, "node_modules/@rollup/rollup-win32-ia32-msvc": { - "version": "4.53.2", - "resolved": "https://registry.npmjs.org/@rollup/rollup-win32-ia32-msvc/-/rollup-win32-ia32-msvc-4.53.2.tgz", - "integrity": "sha512-lNlPEGgdUfSzdCWU176ku/dQRnA7W+Gp8d+cWv73jYrb8uT7HTVVxq62DUYxjbaByuf1Yk0RIIAbDzp+CnOTFg==", + "version": "4.53.3", + "resolved": "https://registry.npmjs.org/@rollup/rollup-win32-ia32-msvc/-/rollup-win32-ia32-msvc-4.53.3.tgz", + "integrity": "sha512-iah+THLcBJdpfZ1TstDFbKNznlzoxa8fmnFYK4V67HvmuNYkVdAywJSoteUszvBQ9/HqN2+9AZghbajMsFT+oA==", "cpu": [ "ia32" ], @@ -723,9 +723,9 @@ ] }, "node_modules/@rollup/rollup-win32-x64-gnu": { - "version": "4.53.2", - "resolved": "https://registry.npmjs.org/@rollup/rollup-win32-x64-gnu/-/rollup-win32-x64-gnu-4.53.2.tgz", - "integrity": "sha512-S6YojNVrHybQis2lYov1sd+uj7K0Q05NxHcGktuMMdIQ2VixGwAfbJ23NnlvvVV1bdpR2m5MsNBViHJKcA4ADw==", + "version": "4.53.3", + "resolved": "https://registry.npmjs.org/@rollup/rollup-win32-x64-gnu/-/rollup-win32-x64-gnu-4.53.3.tgz", + "integrity": "sha512-J9QDiOIZlZLdcot5NXEepDkstocktoVjkaKUtqzgzpt2yWjGlbYiKyp05rWwk4nypbYUNoFAztEgixoLaSETkg==", "cpu": [ "x64" ], @@ -737,9 +737,9 @@ ] }, "node_modules/@rollup/rollup-win32-x64-msvc": { - "version": "4.53.2", - "resolved": "https://registry.npmjs.org/@rollup/rollup-win32-x64-msvc/-/rollup-win32-x64-msvc-4.53.2.tgz", - "integrity": "sha512-k+/Rkcyx//P6fetPoLMb8pBeqJBNGx81uuf7iljX9++yNBVRDQgD04L+SVXmXmh5ZP4/WOp4mWF0kmi06PW2tA==", + "version": "4.53.3", + "resolved": "https://registry.npmjs.org/@rollup/rollup-win32-x64-msvc/-/rollup-win32-x64-msvc-4.53.3.tgz", + "integrity": "sha512-UhTd8u31dXadv0MopwGgNOBpUVROFKWVQgAg5N1ESyCz8AuBcMqm4AuTjrwgQKGDfoFuz02EuMRHQIw/frmYKQ==", "cpu": [ "x64" ], @@ -772,17 +772,17 @@ "license": "MIT" }, "node_modules/@typescript-eslint/eslint-plugin": { - "version": "8.46.4", - "resolved": "https://registry.npmjs.org/@typescript-eslint/eslint-plugin/-/eslint-plugin-8.46.4.tgz", - "integrity": "sha512-R48VhmTJqplNyDxCyqqVkFSZIx1qX6PzwqgcXn1olLrzxcSBDlOsbtcnQuQhNtnNiJ4Xe5gREI1foajYaYU2Vg==", + "version": "8.48.0", + "resolved": "https://registry.npmjs.org/@typescript-eslint/eslint-plugin/-/eslint-plugin-8.48.0.tgz", + "integrity": "sha512-XxXP5tL1txl13YFtrECECQYeZjBZad4fyd3cFV4a19LkAY/bIp9fev3US4S5fDVV2JaYFiKAZ/GRTOLer+mbyQ==", "dev": true, "license": "MIT", "dependencies": { "@eslint-community/regexpp": "^4.10.0", - "@typescript-eslint/scope-manager": "8.46.4", - "@typescript-eslint/type-utils": "8.46.4", - "@typescript-eslint/utils": "8.46.4", - "@typescript-eslint/visitor-keys": "8.46.4", + "@typescript-eslint/scope-manager": "8.48.0", + "@typescript-eslint/type-utils": "8.48.0", + "@typescript-eslint/utils": "8.48.0", + "@typescript-eslint/visitor-keys": "8.48.0", "graphemer": "^1.4.0", "ignore": "^7.0.0", "natural-compare": "^1.4.0", @@ -796,22 +796,22 @@ "url": "https://opencollective.com/typescript-eslint" }, "peerDependencies": { - "@typescript-eslint/parser": "^8.46.4", + "@typescript-eslint/parser": "^8.48.0", "eslint": "^8.57.0 || ^9.0.0", "typescript": ">=4.8.4 <6.0.0" } }, "node_modules/@typescript-eslint/parser": { - "version": "8.46.4", - "resolved": "https://registry.npmjs.org/@typescript-eslint/parser/-/parser-8.46.4.tgz", - "integrity": "sha512-tK3GPFWbirvNgsNKto+UmB/cRtn6TZfyw0D6IKrW55n6Vbs7KJoZtI//kpTKzE/DUmmnAFD8/Ca46s7Obs92/w==", + "version": "8.48.0", + "resolved": "https://registry.npmjs.org/@typescript-eslint/parser/-/parser-8.48.0.tgz", + "integrity": "sha512-jCzKdm/QK0Kg4V4IK/oMlRZlY+QOcdjv89U2NgKHZk1CYTj82/RVSx1mV/0gqCVMJ/DA+Zf/S4NBWNF8GQ+eqQ==", "dev": true, "license": "MIT", "dependencies": { - "@typescript-eslint/scope-manager": "8.46.4", - "@typescript-eslint/types": "8.46.4", - "@typescript-eslint/typescript-estree": "8.46.4", - "@typescript-eslint/visitor-keys": "8.46.4", + "@typescript-eslint/scope-manager": "8.48.0", + "@typescript-eslint/types": "8.48.0", + "@typescript-eslint/typescript-estree": "8.48.0", + "@typescript-eslint/visitor-keys": "8.48.0", "debug": "^4.3.4" }, "engines": { @@ -827,14 +827,14 @@ } }, "node_modules/@typescript-eslint/project-service": { - "version": "8.46.4", - "resolved": "https://registry.npmjs.org/@typescript-eslint/project-service/-/project-service-8.46.4.tgz", - "integrity": "sha512-nPiRSKuvtTN+no/2N1kt2tUh/HoFzeEgOm9fQ6XQk4/ApGqjx0zFIIaLJ6wooR1HIoozvj2j6vTi/1fgAz7UYQ==", + "version": "8.48.0", + "resolved": "https://registry.npmjs.org/@typescript-eslint/project-service/-/project-service-8.48.0.tgz", + "integrity": "sha512-Ne4CTZyRh1BecBf84siv42wv5vQvVmgtk8AuiEffKTUo3DrBaGYZueJSxxBZ8fjk/N3DrgChH4TOdIOwOwiqqw==", "dev": true, "license": "MIT", "dependencies": { - "@typescript-eslint/tsconfig-utils": "^8.46.4", - "@typescript-eslint/types": "^8.46.4", + "@typescript-eslint/tsconfig-utils": "^8.48.0", + "@typescript-eslint/types": "^8.48.0", "debug": "^4.3.4" }, "engines": { @@ -849,14 +849,14 @@ } }, "node_modules/@typescript-eslint/scope-manager": { - "version": "8.46.4", - "resolved": "https://registry.npmjs.org/@typescript-eslint/scope-manager/-/scope-manager-8.46.4.tgz", - "integrity": "sha512-tMDbLGXb1wC+McN1M6QeDx7P7c0UWO5z9CXqp7J8E+xGcJuUuevWKxuG8j41FoweS3+L41SkyKKkia16jpX7CA==", + "version": "8.48.0", + "resolved": "https://registry.npmjs.org/@typescript-eslint/scope-manager/-/scope-manager-8.48.0.tgz", + "integrity": "sha512-uGSSsbrtJrLduti0Q1Q9+BF1/iFKaxGoQwjWOIVNJv0o6omrdyR8ct37m4xIl5Zzpkp69Kkmvom7QFTtue89YQ==", "dev": true, "license": "MIT", "dependencies": { - "@typescript-eslint/types": "8.46.4", - "@typescript-eslint/visitor-keys": "8.46.4" + "@typescript-eslint/types": "8.48.0", + "@typescript-eslint/visitor-keys": "8.48.0" }, "engines": { "node": "^18.18.0 || ^20.9.0 || >=21.1.0" @@ -867,9 +867,9 @@ } }, "node_modules/@typescript-eslint/tsconfig-utils": { - "version": "8.46.4", - "resolved": "https://registry.npmjs.org/@typescript-eslint/tsconfig-utils/-/tsconfig-utils-8.46.4.tgz", - "integrity": "sha512-+/XqaZPIAk6Cjg7NWgSGe27X4zMGqrFqZ8atJsX3CWxH/jACqWnrWI68h7nHQld0y+k9eTTjb9r+KU4twLoo9A==", + "version": "8.48.0", + "resolved": "https://registry.npmjs.org/@typescript-eslint/tsconfig-utils/-/tsconfig-utils-8.48.0.tgz", + "integrity": "sha512-WNebjBdFdyu10sR1M4OXTt2OkMd5KWIL+LLfeH9KhgP+jzfDV/LI3eXzwJ1s9+Yc0Kzo2fQCdY/OpdusCMmh6w==", "dev": true, "license": "MIT", "engines": { @@ -884,15 +884,15 @@ } }, "node_modules/@typescript-eslint/type-utils": { - "version": "8.46.4", - "resolved": "https://registry.npmjs.org/@typescript-eslint/type-utils/-/type-utils-8.46.4.tgz", - "integrity": "sha512-V4QC8h3fdT5Wro6vANk6eojqfbv5bpwHuMsBcJUJkqs2z5XnYhJzyz9Y02eUmF9u3PgXEUiOt4w4KHR3P+z0PQ==", + "version": "8.48.0", + "resolved": "https://registry.npmjs.org/@typescript-eslint/type-utils/-/type-utils-8.48.0.tgz", + "integrity": "sha512-zbeVaVqeXhhab6QNEKfK96Xyc7UQuoFWERhEnj3mLVnUWrQnv15cJNseUni7f3g557gm0e46LZ6IJ4NJVOgOpw==", "dev": true, "license": "MIT", "dependencies": { - "@typescript-eslint/types": "8.46.4", - "@typescript-eslint/typescript-estree": "8.46.4", - "@typescript-eslint/utils": "8.46.4", + "@typescript-eslint/types": "8.48.0", + "@typescript-eslint/typescript-estree": "8.48.0", + "@typescript-eslint/utils": "8.48.0", "debug": "^4.3.4", "ts-api-utils": "^2.1.0" }, @@ -909,9 +909,9 @@ } }, "node_modules/@typescript-eslint/types": { - "version": "8.46.4", - "resolved": "https://registry.npmjs.org/@typescript-eslint/types/-/types-8.46.4.tgz", - "integrity": "sha512-USjyxm3gQEePdUwJBFjjGNG18xY9A2grDVGuk7/9AkjIF1L+ZrVnwR5VAU5JXtUnBL/Nwt3H31KlRDaksnM7/w==", + "version": "8.48.0", + "resolved": "https://registry.npmjs.org/@typescript-eslint/types/-/types-8.48.0.tgz", + "integrity": "sha512-cQMcGQQH7kwKoVswD1xdOytxQR60MWKM1di26xSUtxehaDs/32Zpqsu5WJlXTtTTqyAVK8R7hvsUnIXRS+bjvA==", "dev": true, "license": "MIT", "engines": { @@ -923,21 +923,20 @@ } }, "node_modules/@typescript-eslint/typescript-estree": { - "version": "8.46.4", - "resolved": "https://registry.npmjs.org/@typescript-eslint/typescript-estree/-/typescript-estree-8.46.4.tgz", - "integrity": "sha512-7oV2qEOr1d4NWNmpXLR35LvCfOkTNymY9oyW+lUHkmCno7aOmIf/hMaydnJBUTBMRCOGZh8YjkFOc8dadEoNGA==", + "version": "8.48.0", + "resolved": "https://registry.npmjs.org/@typescript-eslint/typescript-estree/-/typescript-estree-8.48.0.tgz", + "integrity": "sha512-ljHab1CSO4rGrQIAyizUS6UGHHCiAYhbfcIZ1zVJr5nMryxlXMVWS3duFPSKvSUbFPwkXMFk1k0EMIjub4sRRQ==", "dev": true, "license": "MIT", "dependencies": { - "@typescript-eslint/project-service": "8.46.4", - "@typescript-eslint/tsconfig-utils": "8.46.4", - "@typescript-eslint/types": "8.46.4", - "@typescript-eslint/visitor-keys": "8.46.4", + "@typescript-eslint/project-service": "8.48.0", + "@typescript-eslint/tsconfig-utils": "8.48.0", + "@typescript-eslint/types": "8.48.0", + "@typescript-eslint/visitor-keys": "8.48.0", "debug": "^4.3.4", - "fast-glob": "^3.3.2", - "is-glob": "^4.0.3", "minimatch": "^9.0.4", "semver": "^7.6.0", + "tinyglobby": "^0.2.15", "ts-api-utils": "^2.1.0" }, "engines": { @@ -952,16 +951,16 @@ } }, "node_modules/@typescript-eslint/utils": { - "version": "8.46.4", - "resolved": "https://registry.npmjs.org/@typescript-eslint/utils/-/utils-8.46.4.tgz", - "integrity": "sha512-AbSv11fklGXV6T28dp2Me04Uw90R2iJ30g2bgLz529Koehrmkbs1r7paFqr1vPCZi7hHwYxYtxfyQMRC8QaVSg==", + "version": "8.48.0", + "resolved": "https://registry.npmjs.org/@typescript-eslint/utils/-/utils-8.48.0.tgz", + "integrity": "sha512-yTJO1XuGxCsSfIVt1+1UrLHtue8xz16V8apzPYI06W0HbEbEWHxHXgZaAgavIkoh+GeV6hKKd5jm0sS6OYxWXQ==", "dev": true, "license": "MIT", "dependencies": { "@eslint-community/eslint-utils": "^4.7.0", - "@typescript-eslint/scope-manager": "8.46.4", - "@typescript-eslint/types": "8.46.4", - "@typescript-eslint/typescript-estree": "8.46.4" + "@typescript-eslint/scope-manager": "8.48.0", + "@typescript-eslint/types": "8.48.0", + "@typescript-eslint/typescript-estree": "8.48.0" }, "engines": { "node": "^18.18.0 || ^20.9.0 || >=21.1.0" @@ -976,13 +975,13 @@ } }, "node_modules/@typescript-eslint/visitor-keys": { - "version": "8.46.4", - "resolved": "https://registry.npmjs.org/@typescript-eslint/visitor-keys/-/visitor-keys-8.46.4.tgz", - "integrity": "sha512-/++5CYLQqsO9HFGLI7APrxBJYo+5OCMpViuhV8q5/Qa3o5mMrF//eQHks+PXcsAVaLdn817fMuS7zqoXNNZGaw==", + "version": "8.48.0", + "resolved": "https://registry.npmjs.org/@typescript-eslint/visitor-keys/-/visitor-keys-8.48.0.tgz", + "integrity": "sha512-T0XJMaRPOH3+LBbAfzR2jalckP1MSG/L9eUtY0DEzUyVaXJ/t6zN0nR7co5kz0Jko/nkSYCBRkz1djvjajVTTg==", "dev": true, "license": "MIT", "dependencies": { - "@typescript-eslint/types": "8.46.4", + "@typescript-eslint/types": "8.48.0", "eslint-visitor-keys": "^4.2.1" }, "engines": { @@ -1076,16 +1075,13 @@ } }, "node_modules/ansi-styles": { - "version": "4.3.0", - "resolved": "https://registry.npmjs.org/ansi-styles/-/ansi-styles-4.3.0.tgz", - "integrity": "sha512-zbB9rCJAT1rbjiVDb2hqKFHNYLxgtk8NURxZ3IZwD3F6NtxbXZQCnnSi1Lkx+IDohdPlFp222wVALIheZJQSEg==", + "version": "6.2.3", + "resolved": "https://registry.npmjs.org/ansi-styles/-/ansi-styles-6.2.3.tgz", + "integrity": "sha512-4Dj6M28JB+oAH8kFkTLUo+a2jwOFkuqb3yucU0CANcRRUbxS0cP0nZYCGjcc3BNXwRIsUVmDGgzawme7zvJHvg==", "dev": true, "license": "MIT", - "dependencies": { - "color-convert": "^2.0.1" - }, "engines": { - "node": ">=8" + "node": ">=12" }, "funding": { "url": "https://github.com/chalk/ansi-styles?sponsor=1" @@ -1229,11 +1225,14 @@ "license": "MIT" }, "node_modules/commander": { - "version": "2.20.3", - "resolved": "https://registry.npmjs.org/commander/-/commander-2.20.3.tgz", - "integrity": "sha512-GpVkmM8vF2vQUkj2LvZmD35JxeJOLCwJ9cUkugyk2nuhbv3+mJvpLYYt+0+USMxE+oj+ey/lJEnhZw75x/OMcQ==", + "version": "14.0.2", + "resolved": "https://registry.npmjs.org/commander/-/commander-14.0.2.tgz", + "integrity": "sha512-TywoWNNRbhoD0BXs1P3ZEScW8W5iKrnbithIl0YH+uCmBd0QpPOA8yc82DS3BIE5Ma6FnBVUsJ7wVUDz4dvOWQ==", "dev": true, - "license": "MIT" + "license": "MIT", + "engines": { + "node": ">=20" + } }, "node_modules/commenting": { "version": "1.1.0", @@ -1422,6 +1421,22 @@ "url": "https://opencollective.com/eslint" } }, + "node_modules/eslint/node_modules/ansi-styles": { + "version": "4.3.0", + "resolved": "https://registry.npmjs.org/ansi-styles/-/ansi-styles-4.3.0.tgz", + "integrity": "sha512-zbB9rCJAT1rbjiVDb2hqKFHNYLxgtk8NURxZ3IZwD3F6NtxbXZQCnnSi1Lkx+IDohdPlFp222wVALIheZJQSEg==", + "dev": true, + "license": "MIT", + "dependencies": { + "color-convert": "^2.0.1" + }, + "engines": { + "node": ">=8" + }, + "funding": { + "url": "https://github.com/chalk/ansi-styles?sponsor=1" + } + }, "node_modules/eslint/node_modules/brace-expansion": { "version": "1.1.12", "resolved": "https://registry.npmjs.org/brace-expansion/-/brace-expansion-1.1.12.tgz", @@ -1601,6 +1616,29 @@ "url": "https://github.com/sindresorhus/execa?sponsor=1" } }, + "node_modules/execa/node_modules/onetime": { + "version": "5.1.2", + "resolved": "https://registry.npmjs.org/onetime/-/onetime-5.1.2.tgz", + "integrity": "sha512-kbpaSSGJTWdAY5KPVeMOKXSrPtr8C8C7wodJbcsd51jRnmD+GZu8Y0VoU6Dm5Z4vWr0Ig/1NKuWRKf7j5aaYSg==", + "dev": true, + "license": "MIT", + "dependencies": { + "mimic-fn": "^2.1.0" + }, + "engines": { + "node": ">=6" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/execa/node_modules/signal-exit": { + "version": "3.0.7", + "resolved": "https://registry.npmjs.org/signal-exit/-/signal-exit-3.0.7.tgz", + "integrity": "sha512-wnD2ZE+l+SPC/uoS0vXeE9L1+0wuaMqKlfz9AMUo38JsyLSBWSFcHR1Rri62LZc12vLr1gb3jl7iwQhgwpAbGQ==", + "dev": true, + "license": "ISC" + }, "node_modules/fast-deep-equal": { "version": "3.1.3", "resolved": "https://registry.npmjs.org/fast-deep-equal/-/fast-deep-equal-3.1.3.tgz", @@ -2007,9 +2045,9 @@ "license": "ISC" }, "node_modules/js-yaml": { - "version": "4.1.0", - "resolved": "https://registry.npmjs.org/js-yaml/-/js-yaml-4.1.0.tgz", - "integrity": "sha512-wpxZs9NoxZaJESJGIZTyDEaYpl0FKSA+FB9aJiyemKhMwkxQg63h4T1KJgUGHpTqPDNRcmmYLugrRjJlBtWvRA==", + "version": "4.1.1", + "resolved": "https://registry.npmjs.org/js-yaml/-/js-yaml-4.1.1.tgz", + "integrity": "sha512-qQKT4zQxXl8lLwBtHMWwaTcGfFOZviOJet3Oy/xmGk2gZH677CJM9EvtfdSkgWcATZhj/55JZ0rmy3myCT5lsA==", "dev": true, "license": "MIT", "dependencies": { @@ -2089,16 +2127,6 @@ "url": "https://opencollective.com/lint-staged" } }, - "node_modules/lint-staged/node_modules/commander": { - "version": "14.0.2", - "resolved": "https://registry.npmjs.org/commander/-/commander-14.0.2.tgz", - "integrity": "sha512-TywoWNNRbhoD0BXs1P3ZEScW8W5iKrnbithIl0YH+uCmBd0QpPOA8yc82DS3BIE5Ma6FnBVUsJ7wVUDz4dvOWQ==", - "dev": true, - "license": "MIT", - "engines": { - "node": ">=20" - } - }, "node_modules/listr2": { "version": "9.0.5", "resolved": "https://registry.npmjs.org/listr2/-/listr2-9.0.5.tgz", @@ -2311,16 +2339,16 @@ } }, "node_modules/onetime": { - "version": "5.1.2", - "resolved": "https://registry.npmjs.org/onetime/-/onetime-5.1.2.tgz", - "integrity": "sha512-kbpaSSGJTWdAY5KPVeMOKXSrPtr8C8C7wodJbcsd51jRnmD+GZu8Y0VoU6Dm5Z4vWr0Ig/1NKuWRKf7j5aaYSg==", + "version": "7.0.0", + "resolved": "https://registry.npmjs.org/onetime/-/onetime-7.0.0.tgz", + "integrity": "sha512-VXJjc87FScF88uafS3JllDgvAm+c/Slfz06lorj2uAY34rlUu0Nt+v8wreiImcrgAjjIHp1rXpTDlLOGw29WwQ==", "dev": true, "license": "MIT", "dependencies": { - "mimic-fn": "^2.1.0" + "mimic-function": "^5.0.0" }, "engines": { - "node": ">=6" + "node": ">=18" }, "funding": { "url": "https://github.com/sponsors/sindresorhus" @@ -2466,9 +2494,9 @@ } }, "node_modules/prettier": { - "version": "3.6.2", - "resolved": "https://registry.npmjs.org/prettier/-/prettier-3.6.2.tgz", - "integrity": "sha512-I7AIg5boAr5R0FFtJ6rCfD+LFsWHp81dolrFD8S79U9tb8Az2nGrJncnMSnys+bpQJfRUzqs9hnA81OAA3hCuQ==", + "version": "3.7.1", + "resolved": "https://registry.npmjs.org/prettier/-/prettier-3.7.1.tgz", + "integrity": "sha512-RWKXE4qB3u5Z6yz7omJkjWwmTfLdcbv44jUVHC5NpfXwFGzvpQM798FGv/6WNK879tc+Cn0AAyherCl1KjbyZQ==", "dev": true, "license": "MIT", "bin": { @@ -2570,35 +2598,6 @@ "url": "https://github.com/sponsors/sindresorhus" } }, - "node_modules/restore-cursor/node_modules/onetime": { - "version": "7.0.0", - "resolved": "https://registry.npmjs.org/onetime/-/onetime-7.0.0.tgz", - "integrity": "sha512-VXJjc87FScF88uafS3JllDgvAm+c/Slfz06lorj2uAY34rlUu0Nt+v8wreiImcrgAjjIHp1rXpTDlLOGw29WwQ==", - "dev": true, - "license": "MIT", - "dependencies": { - "mimic-function": "^5.0.0" - }, - "engines": { - "node": ">=18" - }, - "funding": { - "url": "https://github.com/sponsors/sindresorhus" - } - }, - "node_modules/restore-cursor/node_modules/signal-exit": { - "version": "4.1.0", - "resolved": "https://registry.npmjs.org/signal-exit/-/signal-exit-4.1.0.tgz", - "integrity": "sha512-bzyZ1e88w9O1iNJbKnOlvYTrWPDl46O1bG0D3XInv+9tkPrxrN8jUUTiFlDkkmKWgn1M6CfIA13SuGqOa9Korw==", - "dev": true, - "license": "ISC", - "engines": { - "node": ">=14" - }, - "funding": { - "url": "https://github.com/sponsors/isaacs" - } - }, "node_modules/reusify": { "version": "1.1.0", "resolved": "https://registry.npmjs.org/reusify/-/reusify-1.1.0.tgz", @@ -2618,9 +2617,9 @@ "license": "MIT" }, "node_modules/rollup": { - "version": "4.53.2", - "resolved": "https://registry.npmjs.org/rollup/-/rollup-4.53.2.tgz", - "integrity": "sha512-MHngMYwGJVi6Fmnk6ISmnk7JAHRNF0UkuucA0CUW3N3a4KnONPEZz+vUanQP/ZC/iY1Qkf3bwPWzyY84wEks1g==", + "version": "4.53.3", + "resolved": "https://registry.npmjs.org/rollup/-/rollup-4.53.3.tgz", + "integrity": "sha512-w8GmOxZfBmKknvdXU1sdM9NHcoQejwF/4mNgj2JuEEdRaHwwF12K7e9eXn1nLZ07ad+du76mkVsyeb2rKGllsA==", "dev": true, "license": "MIT", "dependencies": { @@ -2634,28 +2633,28 @@ "npm": ">=8.0.0" }, "optionalDependencies": { - "@rollup/rollup-android-arm-eabi": "4.53.2", - "@rollup/rollup-android-arm64": "4.53.2", - "@rollup/rollup-darwin-arm64": "4.53.2", - "@rollup/rollup-darwin-x64": "4.53.2", - "@rollup/rollup-freebsd-arm64": "4.53.2", - "@rollup/rollup-freebsd-x64": "4.53.2", - "@rollup/rollup-linux-arm-gnueabihf": "4.53.2", - "@rollup/rollup-linux-arm-musleabihf": "4.53.2", - "@rollup/rollup-linux-arm64-gnu": "4.53.2", - "@rollup/rollup-linux-arm64-musl": "4.53.2", - "@rollup/rollup-linux-loong64-gnu": "4.53.2", - "@rollup/rollup-linux-ppc64-gnu": "4.53.2", - "@rollup/rollup-linux-riscv64-gnu": "4.53.2", - "@rollup/rollup-linux-riscv64-musl": "4.53.2", - "@rollup/rollup-linux-s390x-gnu": "4.53.2", - "@rollup/rollup-linux-x64-gnu": "4.53.2", - "@rollup/rollup-linux-x64-musl": "4.53.2", - "@rollup/rollup-openharmony-arm64": "4.53.2", - "@rollup/rollup-win32-arm64-msvc": "4.53.2", - "@rollup/rollup-win32-ia32-msvc": "4.53.2", - "@rollup/rollup-win32-x64-gnu": "4.53.2", - "@rollup/rollup-win32-x64-msvc": "4.53.2", + "@rollup/rollup-android-arm-eabi": "4.53.3", + "@rollup/rollup-android-arm64": "4.53.3", + "@rollup/rollup-darwin-arm64": "4.53.3", + "@rollup/rollup-darwin-x64": "4.53.3", + "@rollup/rollup-freebsd-arm64": "4.53.3", + "@rollup/rollup-freebsd-x64": "4.53.3", + "@rollup/rollup-linux-arm-gnueabihf": "4.53.3", + "@rollup/rollup-linux-arm-musleabihf": "4.53.3", + "@rollup/rollup-linux-arm64-gnu": "4.53.3", + "@rollup/rollup-linux-arm64-musl": "4.53.3", + "@rollup/rollup-linux-loong64-gnu": "4.53.3", + "@rollup/rollup-linux-ppc64-gnu": "4.53.3", + "@rollup/rollup-linux-riscv64-gnu": "4.53.3", + "@rollup/rollup-linux-riscv64-musl": "4.53.3", + "@rollup/rollup-linux-s390x-gnu": "4.53.3", + "@rollup/rollup-linux-x64-gnu": "4.53.3", + "@rollup/rollup-linux-x64-musl": "4.53.3", + "@rollup/rollup-openharmony-arm64": "4.53.3", + "@rollup/rollup-win32-arm64-msvc": "4.53.3", + "@rollup/rollup-win32-ia32-msvc": "4.53.3", + "@rollup/rollup-win32-x64-gnu": "4.53.3", + "@rollup/rollup-win32-x64-msvc": "4.53.3", "fsevents": "~2.3.2" } }, @@ -2833,11 +2832,17 @@ } }, "node_modules/signal-exit": { - "version": "3.0.7", - "resolved": "https://registry.npmjs.org/signal-exit/-/signal-exit-3.0.7.tgz", - "integrity": "sha512-wnD2ZE+l+SPC/uoS0vXeE9L1+0wuaMqKlfz9AMUo38JsyLSBWSFcHR1Rri62LZc12vLr1gb3jl7iwQhgwpAbGQ==", + "version": "4.1.0", + "resolved": "https://registry.npmjs.org/signal-exit/-/signal-exit-4.1.0.tgz", + "integrity": "sha512-bzyZ1e88w9O1iNJbKnOlvYTrWPDl46O1bG0D3XInv+9tkPrxrN8jUUTiFlDkkmKWgn1M6CfIA13SuGqOa9Korw==", "dev": true, - "license": "ISC" + "license": "ISC", + "engines": { + "node": ">=14" + }, + "funding": { + "url": "https://github.com/sponsors/isaacs" + } }, "node_modules/slice-ansi": { "version": "7.1.2", @@ -2856,19 +2861,6 @@ "url": "https://github.com/chalk/slice-ansi?sponsor=1" } }, - "node_modules/slice-ansi/node_modules/ansi-styles": { - "version": "6.2.3", - "resolved": "https://registry.npmjs.org/ansi-styles/-/ansi-styles-6.2.3.tgz", - "integrity": "sha512-4Dj6M28JB+oAH8kFkTLUo+a2jwOFkuqb3yucU0CANcRRUbxS0cP0nZYCGjcc3BNXwRIsUVmDGgzawme7zvJHvg==", - "dev": true, - "license": "MIT", - "engines": { - "node": ">=12" - }, - "funding": { - "url": "https://github.com/chalk/ansi-styles?sponsor=1" - } - }, "node_modules/smob": { "version": "1.5.0", "resolved": "https://registry.npmjs.org/smob/-/smob-1.5.0.tgz", @@ -3074,6 +3066,30 @@ "node": ">=10" } }, + "node_modules/terser/node_modules/commander": { + "version": "2.20.3", + "resolved": "https://registry.npmjs.org/commander/-/commander-2.20.3.tgz", + "integrity": "sha512-GpVkmM8vF2vQUkj2LvZmD35JxeJOLCwJ9cUkugyk2nuhbv3+mJvpLYYt+0+USMxE+oj+ey/lJEnhZw75x/OMcQ==", + "dev": true, + "license": "MIT" + }, + "node_modules/tinyglobby": { + "version": "0.2.15", + "resolved": "https://registry.npmjs.org/tinyglobby/-/tinyglobby-0.2.15.tgz", + "integrity": "sha512-j2Zq4NyQYG5XMST4cbs02Ak8iJUdxRM0XI5QyxXuZOzKOINmWurp3smXu3y5wDcJrptwpSjgXHzIQxR0omXljQ==", + "dev": true, + "license": "MIT", + "dependencies": { + "fdir": "^6.5.0", + "picomatch": "^4.0.3" + }, + "engines": { + "node": ">=12.0.0" + }, + "funding": { + "url": "https://github.com/sponsors/SuperchupuDev" + } + }, "node_modules/to-regex-range": { "version": "5.0.1", "resolved": "https://registry.npmjs.org/to-regex-range/-/to-regex-range-5.0.1.tgz", @@ -3187,19 +3203,6 @@ "url": "https://github.com/chalk/wrap-ansi?sponsor=1" } }, - "node_modules/wrap-ansi/node_modules/ansi-styles": { - "version": "6.2.3", - "resolved": "https://registry.npmjs.org/ansi-styles/-/ansi-styles-6.2.3.tgz", - "integrity": "sha512-4Dj6M28JB+oAH8kFkTLUo+a2jwOFkuqb3yucU0CANcRRUbxS0cP0nZYCGjcc3BNXwRIsUVmDGgzawme7zvJHvg==", - "dev": true, - "license": "MIT", - "engines": { - "node": ">=12" - }, - "funding": { - "url": "https://github.com/chalk/ansi-styles?sponsor=1" - } - }, "node_modules/wrap-ansi/node_modules/string-width": { "version": "7.2.0", "resolved": "https://registry.npmjs.org/string-width/-/string-width-7.2.0.tgz", diff --git a/package.json b/package.json index c73ea911..fb9989c8 100644 --- a/package.json +++ b/package.json @@ -36,24 +36,24 @@ "prepublishOnly": "npm run checkGit && npm run test && npm run build && npm run checkGit" }, "dependencies": { - "tslib": "^2.3.1" + "tslib": "^2.8.1" }, "devDependencies": { - "@rollup/plugin-node-resolve": "^16.0.1", + "@rollup/plugin-node-resolve": "^16.0.3", "@rollup/plugin-terser": "^0.4.4", - "@typescript-eslint/eslint-plugin": "^8.38.0", - "@typescript-eslint/parser": "^8.38.0", - "chalk": "^5.0.0", - "eslint": "^9.32.0", - "globals": "^16.3.0", + "@typescript-eslint/eslint-plugin": "^8.48.0", + "@typescript-eslint/parser": "^8.48.0", + "chalk": "^5.6.2", + "eslint": "^9.39.1", + "globals": "^16.5.0", "husky": "^9.1.7", "lint-staged": "^16.2.7", - "prettier": "^3.6.2", - "rollup": "^4.45.1", + "prettier": "^3.7.1", + "rollup": "^4.53.3", "rollup-plugin-license": "^3.6.0", - "rollup-plugin-sourcemaps2": "^0.5.3", + "rollup-plugin-sourcemaps2": "^0.5.4", "shelljs": "^0.10.0", - "typescript": "^5.8.3" + "typescript": "^5.9.3" }, "keywords": [ "vscroll", From 6898d028a922f0889deda13ac66e0f0196721720 Mon Sep 17 00:00:00 2001 From: Denis Aleksanov Date: Fri, 28 Nov 2025 14:55:31 +0100 Subject: [PATCH 50/51] Update tests npm dependencies --- tests/package-lock.json | 48 ++++++++++++++++++++--------------------- tests/package.json | 14 ++++++------ 2 files changed, 31 insertions(+), 31 deletions(-) diff --git a/tests/package-lock.json b/tests/package-lock.json index 1bc3eeac..f5685999 100644 --- a/tests/package-lock.json +++ b/tests/package-lock.json @@ -8,14 +8,14 @@ "name": "vscroll-tests", "version": "1.7.0", "devDependencies": { - "@babel/core": "^7.16.12", - "@babel/preset-env": "^7.28.0", - "@babel/preset-typescript": "^7.27.1", - "@playwright/test": "^1.56.1", + "@babel/core": "^7.28.5", + "@babel/preset-env": "^7.28.5", + "@babel/preset-typescript": "^7.28.5", + "@playwright/test": "^1.57.0", "@types/jest": "^30.0.0", - "babel-jest": "^30.0.5", - "jest": "^30.0.5", - "typescript": "^5.8.3" + "babel-jest": "^30.2.0", + "jest": "^30.2.0", + "typescript": "^5.9.3" } }, "node_modules/@babel/code-frame": { @@ -2311,13 +2311,13 @@ } }, "node_modules/@playwright/test": { - "version": "1.56.1", - "resolved": "https://registry.npmjs.org/@playwright/test/-/test-1.56.1.tgz", - "integrity": "sha512-vSMYtL/zOcFpvJCW71Q/OEGQb7KYBPAdKh35WNSkaZA75JlAO8ED8UN6GUNTm3drWomcbcqRPFqQbLae8yBTdg==", + "version": "1.57.0", + "resolved": "https://registry.npmjs.org/@playwright/test/-/test-1.57.0.tgz", + "integrity": "sha512-6TyEnHgd6SArQO8UO2OMTxshln3QMWBtPGrOCgs3wVEmQmwyuNtB10IZMfmYDE0riwNR1cu4q+pPcxMVtaG3TA==", "dev": true, "license": "Apache-2.0", "dependencies": { - "playwright": "1.56.1" + "playwright": "1.57.0" }, "bin": { "playwright": "cli.js" @@ -3645,9 +3645,9 @@ } }, "node_modules/glob": { - "version": "10.4.5", - "resolved": "https://registry.npmjs.org/glob/-/glob-10.4.5.tgz", - "integrity": "sha512-7Bv8RF0k6xjo7d4A/PxYLbUCfb6c+Vpd2/mB2yRDlew7Jb5hEXiCD9ibfO7wpk8i4sevK6DFny9h7EYbM3/sHg==", + "version": "10.5.0", + "resolved": "https://registry.npmjs.org/glob/-/glob-10.5.0.tgz", + "integrity": "sha512-DfXN8DfhJ7NH3Oe7cFmu3NCu1wKbkReJ8TorzSAFbSKrlNaQSKfIzqYqVY8zlbs2NLBbWpRiU52GX2PbaBVNkg==", "dev": true, "license": "ISC", "dependencies": { @@ -4548,9 +4548,9 @@ "license": "MIT" }, "node_modules/js-yaml": { - "version": "3.14.1", - "resolved": "https://registry.npmjs.org/js-yaml/-/js-yaml-3.14.1.tgz", - "integrity": "sha512-okMH7OXXJ7YrN9Ok3/SXrnu4iX9yOk+25nqX4imS2npuvTYDmo/QEZoqwZkYaIDk3jVvBOTOIEgEhaLOynBS9g==", + "version": "3.14.2", + "resolved": "https://registry.npmjs.org/js-yaml/-/js-yaml-3.14.2.tgz", + "integrity": "sha512-PMSmkqxr106Xa156c2M265Z+FTrPl+oxd/rgOQy2tijQeK5TxQ43psO1ZCwhVOSdnn+RzkzlRz/eY4BgJBYVpg==", "dev": true, "license": "MIT", "dependencies": { @@ -5016,13 +5016,13 @@ } }, "node_modules/playwright": { - "version": "1.56.1", - "resolved": "https://registry.npmjs.org/playwright/-/playwright-1.56.1.tgz", - "integrity": "sha512-aFi5B0WovBHTEvpM3DzXTUaeN6eN0qWnTkKx4NQaH4Wvcmc153PdaY2UBdSYKaGYw+UyWXSVyxDUg5DoPEttjw==", + "version": "1.57.0", + "resolved": "https://registry.npmjs.org/playwright/-/playwright-1.57.0.tgz", + "integrity": "sha512-ilYQj1s8sr2ppEJ2YVadYBN0Mb3mdo9J0wQ+UuDhzYqURwSoW4n1Xs5vs7ORwgDGmyEh33tRMeS8KhdkMoLXQw==", "dev": true, "license": "Apache-2.0", "dependencies": { - "playwright-core": "1.56.1" + "playwright-core": "1.57.0" }, "bin": { "playwright": "cli.js" @@ -5035,9 +5035,9 @@ } }, "node_modules/playwright-core": { - "version": "1.56.1", - "resolved": "https://registry.npmjs.org/playwright-core/-/playwright-core-1.56.1.tgz", - "integrity": "sha512-hutraynyn31F+Bifme+Ps9Vq59hKuUCz7H1kDOcBs+2oGguKkWTU50bBWrtz34OUWmIwpBTWDxaRPXrIXkgvmQ==", + "version": "1.57.0", + "resolved": "https://registry.npmjs.org/playwright-core/-/playwright-core-1.57.0.tgz", + "integrity": "sha512-agTcKlMw/mjBWOnD6kFZttAAGHgi/Nw0CZ2o6JqWSbMlI219lAFLZZCyqByTsvVAJq5XA5H8cA6PrvBRpBWEuQ==", "dev": true, "license": "Apache-2.0", "bin": { diff --git a/tests/package.json b/tests/package.json index 3878e27d..bc4bbc1e 100644 --- a/tests/package.json +++ b/tests/package.json @@ -13,13 +13,13 @@ "watch": "jest --watch" }, "devDependencies": { - "@babel/core": "^7.16.12", - "@babel/preset-env": "^7.28.0", - "@babel/preset-typescript": "^7.27.1", - "@playwright/test": "^1.56.1", + "@babel/core": "^7.28.5", + "@babel/preset-env": "^7.28.5", + "@babel/preset-typescript": "^7.28.5", + "@playwright/test": "^1.57.0", "@types/jest": "^30.0.0", - "babel-jest": "^30.0.5", - "jest": "^30.0.5", - "typescript": "^5.8.3" + "babel-jest": "^30.2.0", + "jest": "^30.2.0", + "typescript": "^5.9.3" } } From 7c36a9fd65353f10dddfc0da2c23085f54f353dd Mon Sep 17 00:00:00 2001 From: Denis Aleksanov Date: Fri, 28 Nov 2025 15:02:09 +0100 Subject: [PATCH 51/51] vscroll v1.8.0 --- package-lock.json | 4 ++-- package.json | 7 +++++-- src/version.ts | 2 +- tests/package-lock.json | 4 ++-- tests/package.json | 2 +- 5 files changed, 11 insertions(+), 8 deletions(-) diff --git a/package-lock.json b/package-lock.json index d7338642..a50fd4a9 100644 --- a/package-lock.json +++ b/package-lock.json @@ -1,12 +1,12 @@ { "name": "vscroll", - "version": "1.7.0", + "version": "1.8.0", "lockfileVersion": 3, "requires": true, "packages": { "": { "name": "vscroll", - "version": "1.7.0", + "version": "1.8.0", "license": "MIT", "dependencies": { "tslib": "^2.8.1" diff --git a/package.json b/package.json index fb9989c8..9d7e5430 100644 --- a/package.json +++ b/package.json @@ -1,6 +1,6 @@ { "name": "vscroll", - "version": "1.7.0", + "version": "1.8.0", "description": "Virtual scroll engine", "main": "dist/bundles/vscroll.umd.js", "module": "dist/bundles/vscroll.esm5.js", @@ -33,7 +33,7 @@ "test:e2e:debug": "cd tests && npm run test:e2e:debug", "prepack": "npm run build", "checkGit": "sh ./uncommited.sh", - "prepublishOnly": "npm run checkGit && npm run test && npm run build && npm run checkGit" + "prepublishOnly": "npm run checkGit && npm run build && npm run test && npm run checkGit" }, "dependencies": { "tslib": "^2.8.1" @@ -57,9 +57,12 @@ }, "keywords": [ "vscroll", + "lazy-loading", "virtual", "virtualizing", "virtualization", + "virtual-scroll", + "infinite-scroll", "scroll", "scrolling", "scroller", diff --git a/src/version.ts b/src/version.ts index 87ebc6cd..56150026 100644 --- a/src/version.ts +++ b/src/version.ts @@ -1,4 +1,4 @@ export default { name: 'vscroll', - version: '1.7.0' + version: '1.8.0' }; diff --git a/tests/package-lock.json b/tests/package-lock.json index f5685999..82f1659f 100644 --- a/tests/package-lock.json +++ b/tests/package-lock.json @@ -1,12 +1,12 @@ { "name": "vscroll-tests", - "version": "1.7.0", + "version": "1.8.0", "lockfileVersion": 3, "requires": true, "packages": { "": { "name": "vscroll-tests", - "version": "1.7.0", + "version": "1.8.0", "devDependencies": { "@babel/core": "^7.28.5", "@babel/preset-env": "^7.28.5", diff --git a/tests/package.json b/tests/package.json index bc4bbc1e..10ba0d4a 100644 --- a/tests/package.json +++ b/tests/package.json @@ -1,6 +1,6 @@ { "name": "vscroll-tests", - "version": "1.7.0", + "version": "1.8.0", "private": true, "description": "Test suite for vscroll", "type": "module",