diff --git a/eslint.config.mjs b/eslint.config.mjs index 99fb430ab..d97c94e0e 100644 --- a/eslint.config.mjs +++ b/eslint.config.mjs @@ -1,50 +1,43 @@ -import expensify from 'eslint-config-expensify'; -import tsPlugin from '@typescript-eslint/eslint-plugin'; -import tsParser from '@typescript-eslint/parser'; +import browserConfig from 'eslint-config-expensify/browser'; +import reactConfig from 'eslint-config-expensify/react'; +import scriptsConfig from 'eslint-config-expensify/scripts'; +import tsExpensifyConfig from 'eslint-config-expensify/typescript'; +import jestConfig from 'eslint-config-expensify/jest'; import prettierConfig from 'eslint-config-prettier'; +import seatbelt from 'eslint-seatbelt'; +import rulesdir from 'eslint-plugin-rulesdir'; +import {defineConfig, globalIgnores} from 'eslint/config'; +import globals from 'globals'; +import path from 'node:path'; +import {createRequire} from 'node:module'; +import {fileURLToPath} from 'node:url'; -export default [ - ...expensify, - prettierConfig, - { - ignores: ['dist/**', 'node_modules/**', '.github/**', '*.d.ts', '*.config.js', '*.config.cjs', 'tests/types/**/*.ts'], - }, +const dirname = path.dirname(fileURLToPath(import.meta.url)); +const require = createRequire(import.meta.url); +const expensifyConfigDirectory = path.dirname(require.resolve('eslint-config-expensify')); +rulesdir.RULES_DIR = path.resolve(expensifyConfigDirectory, 'eslint-plugin-expensify'); + +export default defineConfig([ + seatbelt.configs.enable, + globalIgnores(['dist/**', 'node_modules/**', '**/*.d.ts', '**/*.config.js', '**/*.config.cjs', 'tests/types/**/*.ts', 'cpp/**', 'bench-results/**', '.github/**']), + ...browserConfig, + ...reactConfig, + ...tsExpensifyConfig, + ...jestConfig, + ...scriptsConfig, { - // Overwriting this for now because web-e will conflict with this - files: ['**/*.js', '**/*.jsx'], + plugins: { + rulesdir, + }, settings: { - 'import/resolver': { - node: { - extensions: ['.js', '.website.js', '.desktop.js', '.native.js', '.ios.js', '.android.js', '.config.js', '.ts', '.tsx'], - }, + seatbelt: { + seatbeltFile: path.join(dirname, 'eslint.seatbelt.tsv'), + threadsafe: true, }, }, - rules: { - 'react/jsx-filename-extension': [1, {extensions: ['.js']}], - 'rulesdir/no-multiple-onyx-in-file': 'off', - 'import/extensions': [ - 'error', - 'ignorePackages', - { - js: 'never', - jsx: 'never', - ts: 'never', - tsx: 'never', - }, - ], - }, }, { - files: ['**/*.ts', '**/*.tsx'], - plugins: { - '@typescript-eslint': tsPlugin, - }, - languageOptions: { - parser: tsParser, - parserOptions: { - project: './tsconfig.json', - }, - }, + files: ['**/*.ts', '**/*.tsx', '**/*.js', '**/*.jsx'], settings: { 'import/resolver': { typescript: { @@ -54,88 +47,90 @@ export default [ }, }, rules: { - ...tsPlugin.configs.recommended.rules, - ...tsPlugin.configs.stylistic.rules, - 'rulesdir/prefer-underscore-method': 'off', - 'react/jsx-props-no-spreading': 'off', + 'react/prop-types': 'off', 'react/require-default-props': 'off', - 'react/jsx-filename-extension': ['error', {extensions: ['.tsx', '.jsx']}], - 'import/no-unresolved': 'error', - 'import/consistent-type-specifier-style': ['error', 'prefer-top-level'], - 'no-use-before-define': 'off', - '@typescript-eslint/no-use-before-define': 'off', - '@typescript-eslint/no-unused-vars': ['error', {argsIgnorePattern: '^_', caughtErrors: 'none'}], - '@typescript-eslint/consistent-type-imports': ['error', {prefer: 'type-imports'}], - '@typescript-eslint/consistent-type-exports': ['error', {fixMixedExportsWithInlineTypeSpecifier: false}], - '@typescript-eslint/no-non-null-assertion': 'off', - '@typescript-eslint/array-type': ['error', {default: 'array-simple'}], - '@typescript-eslint/consistent-type-definitions': 'off', - '@typescript-eslint/no-empty-object-type': 'off', - 'rulesdir/no-multiple-onyx-in-file': 'off', - 'valid-jsdoc': 'off', - 'rulesdir/prefer-import-module-contents': 'off', - 'es/no-optional-chaining': 'off', - 'es/no-nullish-coalescing-operators': 'off', - // Disable JSDoc type rules for TypeScript files (TypeScript provides the types) - 'jsdoc/require-param': 'off', - 'jsdoc/require-param-type': 'off', - 'jsdoc/check-param-names': 'off', - 'jsdoc/check-tag-names': 'off', - 'jsdoc/check-types': 'off', - 'no-func-assign': 'off', - 'no-loop-func': 'off', - 'no-redeclare': 'off', - '@typescript-eslint/no-redeclare': 'error', - 'import/extensions': [ - 'error', - 'ignorePackages', - { - js: 'never', - jsx: 'never', - ts: 'never', - tsx: 'never', - }, - ], - 'rulesdir/prefer-onyx-connect-in-libs': 'off', }, }, { - files: ['tests/**/*.{js,jsx,ts,tsx}', 'jestSetup.js', 'lib/**/__mocks__/**/*.{js,ts}'], + files: ['.prettierrc.cjs', 'jest-sequencer.js', 'jestSetup.js', 'jest-test-environment.ts', 'buildDocs.ts'], languageOptions: { - globals: { - jest: 'readonly', - describe: 'readonly', - it: 'readonly', - test: 'readonly', - expect: 'readonly', - beforeEach: 'readonly', - afterEach: 'readonly', - beforeAll: 'readonly', - afterAll: 'readonly', - fail: 'readonly', - }, + globals: globals.node, }, + }, + { + files: ['lib/OnyxUtils.ts'], rules: { - '@lwc/lwc/no-async-await': 'off', - 'no-await-in-loop': 'off', - 'no-restricted-syntax': ['error', 'ForInStatement', 'LabeledStatement', 'WithStatement'], - 'import/extensions': 'off', - '@typescript-eslint/no-require-imports': 'off', - 'no-restricted-imports': 'off', + '@typescript-eslint/no-use-before-define': 'off', + }, + }, + // Flat config replaces the whole naming-convention array, so this repeats eslint-config-expensify/typescript + // and adds objectLiteralProperty exceptions for Onyx key strings (underscores) and numeric collection IDs. + { + files: ['tests/**/*'], + rules: { + '@typescript-eslint/naming-convention': [ + 'error', + { + selector: ['variable', 'property'], + format: null, + filter: { + regex: '^__esModule$', + match: true, + }, + }, + { + selector: ['variable', 'property'], + format: ['camelCase', 'UPPER_CASE', 'PascalCase'], + filter: { + regex: '^private_[a-z][a-zA-Z0-9]*$', + match: false, + }, + }, + { + selector: 'objectLiteralProperty', + format: null, + filter: { + regex: '_', + match: true, + }, + }, + { + selector: 'objectLiteralProperty', + format: null, + filter: { + regex: '^[0-9]+$', + match: true, + }, + }, + { + selector: 'function', + format: ['camelCase', 'PascalCase'], + }, + { + selector: ['typeLike', 'enumMember'], + format: ['PascalCase'], + }, + { + selector: ['parameter', 'method'], + format: ['camelCase', 'PascalCase'], + leadingUnderscore: 'allow', + }, + ], }, }, { - files: ['**/*.native.ts', '**/*.native.tsx'], + files: ['tests/**/*', 'jestSetup.js', 'lib/**/__mocks__/**/*'], rules: { - '@typescript-eslint/no-require-imports': 'off', + 'import/extensions': 'off', }, }, { files: ['lib/storage/providers/MemoryOnlyProvider.ts'], languageOptions: { globals: { - jest: 'readonly', + ...globals.jest, }, }, }, -]; + prettierConfig, +]); diff --git a/eslint.seatbelt.tsv b/eslint.seatbelt.tsv new file mode 100644 index 000000000..782e9e40d --- /dev/null +++ b/eslint.seatbelt.tsv @@ -0,0 +1,107 @@ +# eslint-seatbelt temporarily allowed errors +# docs: https://github.com/justjake/eslint-seatbelt#readme + +"buildDocs.ts" "import/no-extraneous-dependencies" 1 +"jest-test-environment.ts" "import/no-extraneous-dependencies" 1 +"lib/DevTools/RealDevTools.ts" "@typescript-eslint/no-unsafe-assignment" 1 +"lib/DevTools/RealDevTools.ts" "@typescript-eslint/no-unsafe-member-access" 1 +"lib/DevTools/RealDevTools.ts" "@typescript-eslint/no-unsafe-type-assertion" 1 +"lib/Onyx.ts" "@typescript-eslint/no-unsafe-type-assertion" 6 +"lib/OnyxConnectionManager.ts" "@typescript-eslint/no-unsafe-type-assertion" 7 +"lib/OnyxConnectionManager.ts" "@typescript-eslint/non-nullable-type-assertion-style" 3 +"lib/OnyxKeys.ts" "@typescript-eslint/no-unsafe-type-assertion" 1 +"lib/OnyxMerge/index.native.ts" "@typescript-eslint/no-unsafe-type-assertion" 3 +"lib/OnyxMerge/index.ts" "@typescript-eslint/no-unsafe-type-assertion" 2 +"lib/OnyxSnapshotCache.ts" "@typescript-eslint/no-unsafe-type-assertion" 2 +"lib/OnyxUtils.ts" "@typescript-eslint/no-unsafe-argument" 6 +"lib/OnyxUtils.ts" "@typescript-eslint/no-unsafe-assignment" 6 +"lib/OnyxUtils.ts" "@typescript-eslint/no-unsafe-call" 2 +"lib/OnyxUtils.ts" "@typescript-eslint/no-unsafe-member-access" 5 +"lib/OnyxUtils.ts" "@typescript-eslint/no-unsafe-type-assertion" 31 +"lib/OnyxUtils.ts" "@typescript-eslint/non-nullable-type-assertion-style" 1 +"lib/Str.ts" "@typescript-eslint/no-unsafe-type-assertion" 1 +"lib/storage/providers/IDBKeyValProvider/index.ts" "@typescript-eslint/no-unsafe-return" 1 +"lib/storage/providers/IDBKeyValProvider/index.ts" "@typescript-eslint/no-unsafe-type-assertion" 3 +"lib/storage/providers/MemoryOnlyProvider.ts" "@typescript-eslint/no-unsafe-type-assertion" 5 +"lib/storage/providers/NoopProvider.ts" "@typescript-eslint/no-unsafe-type-assertion" 1 +"lib/storage/providers/SQLiteProvider.ts" "@typescript-eslint/no-unsafe-return" 3 +"lib/storage/providers/SQLiteProvider.ts" "@typescript-eslint/no-unsafe-type-assertion" 3 +# useLiveRef is deprecated but still used by useOnyx; refs assignment during render is intentional +"lib/useLiveRef.ts" "@typescript-eslint/no-deprecated" 1 +"lib/useLiveRef.ts" "react-hooks/refs" 1 +"lib/useOnyx.ts" "@typescript-eslint/no-deprecated" 1 +"lib/useOnyx.ts" "@typescript-eslint/no-unsafe-type-assertion" 2 +"lib/utils.ts" "@typescript-eslint/no-unsafe-type-assertion" 5 +"tests/perf-test/Onyx.perf-test.ts" "@typescript-eslint/no-unsafe-type-assertion" 1 +"tests/perf-test/OnyxCache.perf-test.ts" "@typescript-eslint/no-non-null-assertion" 1 +"tests/perf-test/OnyxCache.perf-test.ts" "@typescript-eslint/no-unsafe-assignment" 1 +"tests/perf-test/OnyxCache.perf-test.ts" "@typescript-eslint/no-unsafe-member-access" 1 +"tests/perf-test/OnyxConnectionManager.perf-test.ts" "@typescript-eslint/no-non-null-assertion" 1 +"tests/perf-test/OnyxSnapshotCache.perf-test.ts" "@typescript-eslint/no-unsafe-type-assertion" 4 +"tests/perf-test/OnyxUtils.perf-test.ts" "@typescript-eslint/no-misused-promises" 1 +"tests/perf-test/OnyxUtils.perf-test.ts" "@typescript-eslint/no-unsafe-type-assertion" 1 +"tests/perf-test/useOnyx.perf-test.tsx" "@typescript-eslint/no-unsafe-type-assertion" 2 +"tests/perf-test/utils.perf-test.ts" "@typescript-eslint/no-unsafe-call" 1 +"tests/perf-test/utils.perf-test.ts" "@typescript-eslint/no-unsafe-member-access" 3 +"tests/unit/DevToolsTest.ts" "@typescript-eslint/no-unsafe-assignment" 2 +"tests/unit/DevToolsTest.ts" "@typescript-eslint/no-unsafe-type-assertion" 9 +"tests/utils/onyxConnectionManagerTestHarness.ts" "@typescript-eslint/no-unsafe-type-assertion" 1 +"tests/unit/OnyxConnectionManagerTest.ts" "@typescript-eslint/no-unsafe-return" 1 +"tests/unit/OnyxConnectionManagerTest.ts" "@typescript-eslint/no-unsafe-type-assertion" 2 +"tests/unit/OnyxSnapshotCacheTest.ts" "@typescript-eslint/no-unsafe-type-assertion" 7 +"tests/unit/collectionHydrationTest.ts" "@typescript-eslint/no-unsafe-assignment" 3 +"tests/unit/collectionHydrationTest.ts" "@typescript-eslint/no-unsafe-member-access" 7 +"tests/unit/mocks/sqliteMock.ts" "@typescript-eslint/no-unsafe-type-assertion" 7 +"tests/unit/onyxCacheTest.tsx" "@typescript-eslint/no-non-null-assertion" 3 +"tests/unit/onyxCacheTest.tsx" "@typescript-eslint/no-unsafe-assignment" 7 +"tests/unit/onyxCacheTest.tsx" "@typescript-eslint/no-unsafe-member-access" 6 +"tests/unit/onyxCacheTest.tsx" "@typescript-eslint/no-unsafe-type-assertion" 2 +"tests/unit/onyxClearNativeStorageTest.ts" "@typescript-eslint/no-unsafe-assignment" 1 +"tests/unit/onyxClearNativeStorageTest.ts" "@typescript-eslint/no-unsafe-member-access" 1 +"tests/unit/onyxClearNativeStorageTest.ts" "@typescript-eslint/no-unsafe-type-assertion" 4 +"tests/unit/onyxClearWebStorageTest.ts" "@typescript-eslint/no-unsafe-assignment" 1 +"tests/unit/onyxClearWebStorageTest.ts" "@typescript-eslint/no-unsafe-member-access" 1 +"tests/unit/onyxMultiMergeWebStorageTest.ts" "@typescript-eslint/no-unsafe-assignment" 1 +"tests/unit/onyxMultiMergeWebStorageTest.ts" "@typescript-eslint/no-unsafe-member-access" 1 +"tests/unit/onyxMultiMergeWebStorageTest.ts" "@typescript-eslint/no-unsafe-type-assertion" 1 +"tests/unit/onyxTest.ts" "@typescript-eslint/no-non-null-assertion" 1 +"tests/unit/onyxTest.ts" "@typescript-eslint/no-unsafe-assignment" 9 +"tests/unit/onyxTest.ts" "@typescript-eslint/no-unsafe-call" 2 +"tests/unit/onyxTest.ts" "@typescript-eslint/no-unsafe-member-access" 31 +"tests/unit/onyxTest.ts" "@typescript-eslint/no-unsafe-return" 1 +"tests/unit/onyxTest.ts" "@typescript-eslint/no-unsafe-type-assertion" 5 +"tests/unit/onyxTest.ts" "import/no-extraneous-dependencies" 2 +"tests/unit/onyxUtilsTest.ts" "@typescript-eslint/await-thenable" 6 +"tests/unit/onyxUtilsTest.ts" "@typescript-eslint/no-unsafe-assignment" 10 +"tests/unit/onyxUtilsTest.ts" "@typescript-eslint/no-unsafe-call" 2 +"tests/unit/onyxUtilsTest.ts" "@typescript-eslint/no-unsafe-member-access" 20 +"tests/unit/onyxUtilsTest.ts" "@typescript-eslint/no-unsafe-return" 1 +"tests/unit/onyxUtilsTest.ts" "@typescript-eslint/no-unsafe-type-assertion" 3 +"tests/unit/storage/providers/IDBKeyvalProviderTest.ts" "@typescript-eslint/no-unsafe-member-access" 2 +"tests/unit/storage/providers/IDBKeyvalProviderTest.ts" "@typescript-eslint/no-unsafe-type-assertion" 4 +"tests/unit/storage/providers/SQLiteProviderTest.ts" "@typescript-eslint/no-unsafe-member-access" 2 +"tests/unit/storage/providers/SQLiteProviderTest.ts" "@typescript-eslint/no-unsafe-return" 1 +"tests/unit/storage/providers/SQLiteProviderTest.ts" "@typescript-eslint/no-unsafe-type-assertion" 4 +"tests/unit/storage/tryOrDegradePerformanceTest.ts" "@typescript-eslint/no-unsafe-assignment" 2 +"tests/unit/storage/tryOrDegradePerformanceTest.ts" "@typescript-eslint/no-unsafe-member-access" 1 +"tests/unit/useOnyxTest.ts" "@typescript-eslint/no-unsafe-type-assertion" 21 +"tests/unit/utilsTest.ts" "@typescript-eslint/no-unsafe-member-access" 3 +"lib/StateMachine.ts" "@typescript-eslint/no-unsafe-type-assertion" 2 +"lib/storage/__mocks__/index.ts" "@typescript-eslint/no-unsafe-argument" 1 +"lib/storage/index.ts" "no-nested-ternary" 1 +"lib/storage/providers/IDBKeyValProvider/index.ts" "rulesdir/prefer-at" 1 +"lib/storage/providers/NoopProvider.ts" "no-void" 1 +"lib/utils.ts" "@typescript-eslint/no-redundant-type-constituents" 1 +"tests/unit/OnyxConnectionManagerTest.ts" "@typescript-eslint/no-non-null-assertion" 1 +"tests/unit/onyxTest.ts" "@typescript-eslint/no-unnecessary-type-assertion" 4 +"tests/unit/onyxTest.ts" "rulesdir/prefer-at" 11 +"tests/unit/onyxUtilsTest.ts" "@typescript-eslint/no-non-null-assertion" 2 +"tests/unit/onyxUtilsTest.ts" "@typescript-eslint/no-unsafe-member-access" 21 +"tests/unit/onyxUtilsTest.ts" "@typescript-eslint/no-unsafe-type-assertion" 4 +"tests/unit/storage/providers/createStoreTest.ts" "@typescript-eslint/no-unsafe-assignment" 21 +"tests/unit/storage/providers/createStoreTest.ts" "@typescript-eslint/no-unsafe-type-assertion" 4 +"tests/unit/storage/providers/IDBKeyvalProviderTest.ts" "@typescript-eslint/no-unnecessary-type-assertion" 2 +"tests/unit/storage/providers/IDBKeyvalProviderTest.ts" "@typescript-eslint/unbound-method" 2 +"tests/unit/storage/providers/IDBKeyvalProviderTest.ts" "rulesdir/prefer-at" 4 +"tests/unit/storage/providers/SQLiteProviderTest.ts" "@typescript-eslint/no-unnecessary-type-assertion" 5 +"tests/unit/storage/providers/SQLiteProviderTest.ts" "rulesdir/prefer-at" 1 diff --git a/lib/CircuitBreaker/AbstractCircuitBreaker.ts b/lib/CircuitBreaker/AbstractCircuitBreaker.ts index 3ba6feeca..f1d4465ef 100644 --- a/lib/CircuitBreaker/AbstractCircuitBreaker.ts +++ b/lib/CircuitBreaker/AbstractCircuitBreaker.ts @@ -7,7 +7,7 @@ import type {CircuitBreakerOptions, CircuitBreakerState} from './types'; * * - **closed**: requests are allowed; failures are counted. * - **open**: requests are rejected until {@link resetTimeoutMs} elapses. - * - **half-open**: the recovery-probe state. After the open timeout, the breaker admits exactly ONE + * - **halfOpen**: the recovery-probe state. After the open timeout, the breaker admits exactly ONE * probe request: success means the dependency recovered, so the circuit closes. Failure means it's * still down, so the circuit reopens. This single-request probe prevents a "thundering herd" where * every caller fails loudly when the service hasn't recovered yet. @@ -69,10 +69,10 @@ abstract class AbstractCircuitBreaker { /** * Whether a request may proceed. * - * Returns `false` while open. In half-open, the FIRST caller is admitted as the recovery probe and + * Returns `false` while open. In halfOpen, the FIRST caller is admitted as the recovery probe and * `isProbeInFlight` is latched so every subsequent caller is rejected until that probe resolves * (via {@link recordSuccess} → close, or {@link recordFailure} → reopen). That single-probe gate is - * the whole point of half-open: it tests recovery with one request instead of letting a herd of + * the whole point of halfOpen: it tests recovery with one request instead of letting a herd of * waiting callers stampede a dependency that may still be down. */ isAllowed(): boolean { @@ -82,7 +82,7 @@ abstract class AbstractCircuitBreaker { return false; } - if (currentState === 'half-open') { + if (currentState === 'halfOpen') { if (this.isProbeInFlight) { return false; } @@ -93,7 +93,7 @@ abstract class AbstractCircuitBreaker { } /** - * Record a failed request. May open the circuit from closed or half-open. + * Record a failed request. May open the circuit from closed or halfOpen. * @returns `true` when the circuit is open after recording (the request must not proceed). */ recordFailure(): boolean { @@ -101,7 +101,7 @@ abstract class AbstractCircuitBreaker { return true; } - if (this.machine.state === 'half-open') { + if (this.machine.state === 'halfOpen') { this.trip(); return true; } @@ -115,9 +115,9 @@ abstract class AbstractCircuitBreaker { return false; } - /** Record a successful request. Closes the circuit from half-open and clears failure counts. */ + /** Record a successful request. Closes the circuit from halfOpen and clears failure counts. */ recordSuccess(): void { - if (this.machine.state === 'half-open') { + if (this.machine.state === 'halfOpen') { this.close(); return; } @@ -129,7 +129,7 @@ abstract class AbstractCircuitBreaker { /** * The current state WITHOUT advancing recovery — a pure query, safe to call without side effects. - * The open→half-open transition is applied only at the admission point ({@link isAllowed}); by the + * The open→halfOpen transition is applied only at the admission point ({@link isAllowed}); by the * time a caller queries state after being admitted, that transition has already happened. */ peekState(): CircuitBreakerState { @@ -166,7 +166,7 @@ abstract class AbstractCircuitBreaker { } private close(): void { - // close() only ever runs from half-open (see recordSuccess), and half-open → closed is the one + // close() only ever runs from halfOpen (see recordSuccess), and halfOpen → closed is the one // legal closing transition — so go through transition() to keep the illegal open → closed jump // an error rather than silently constructing a fresh closed machine. this.machine = this.machine.transition('closed'); @@ -177,9 +177,9 @@ abstract class AbstractCircuitBreaker { } /** - * Lazily advance open → half-open once the reset timeout has elapsed. This is checked on read + * Lazily advance open → halfOpen once the reset timeout has elapsed. This is checked on read * (via {@link getCurrentState}) rather than on a timer, so there's nothing to schedule or clean up: - * the transition simply becomes visible to the next caller after the window. Entering half-open + * the transition simply becomes visible to the next caller after the window. Entering halfOpen * clears `isProbeInFlight` so the next admitted request becomes the recovery probe. */ private maybeRecover(): void { @@ -191,7 +191,7 @@ abstract class AbstractCircuitBreaker { return; } - this.machine = this.machine.transition('half-open'); + this.machine = this.machine.transition('halfOpen'); this.isProbeInFlight = false; } } diff --git a/lib/CircuitBreaker/types.ts b/lib/CircuitBreaker/types.ts index d4076c684..6eb4e854c 100644 --- a/lib/CircuitBreaker/types.ts +++ b/lib/CircuitBreaker/types.ts @@ -5,7 +5,7 @@ * - **open**: tripped; requests are rejected outright so a known-bad dependency isn't hammered. * - **half-open**: a trial state entered after the open timeout — see {@link CIRCUIT_BREAKER_TRANSITIONS}. */ -type CircuitBreakerState = 'closed' | 'open' | 'half-open'; +type CircuitBreakerState = 'closed' | 'open' | 'halfOpen'; /** * Legal state transitions. The flow is closed → open → half-open → (closed | open). @@ -24,8 +24,8 @@ type CircuitBreakerState = 'closed' | 'open' | 'half-open'; */ const CIRCUIT_BREAKER_TRANSITIONS = { closed: ['open'], - open: ['half-open'], - 'half-open': ['closed', 'open'], + open: ['halfOpen'], + halfOpen: ['closed', 'open'], } as const satisfies Record; type CircuitBreakerOptions = { diff --git a/lib/Logger.ts b/lib/Logger.ts index 922b1dd0b..615853e25 100644 --- a/lib/Logger.ts +++ b/lib/Logger.ts @@ -7,7 +7,6 @@ type LogData = { }; type LoggerCallback = (data: LogData) => void; -// eslint-disable-next-line @typescript-eslint/no-empty-function let logger: LoggerCallback = () => {}; /** diff --git a/lib/Onyx.ts b/lib/Onyx.ts index 16a0f6ec6..383fa9161 100644 --- a/lib/Onyx.ts +++ b/lib/Onyx.ts @@ -18,7 +18,6 @@ import type { OnyxSetInput, OnyxUpdate, OnyxValue, - OnyxInput, OnyxMethodMap, SetOptions, } from './types'; @@ -64,7 +63,7 @@ function init({ // Setting isProcessingCollectionUpdate=true prevents triggering collection callbacks for each individual update const isKeyCollectionMember = OnyxKeys.isCollectionMember(key); - OnyxUtils.keyChanged(key, value as OnyxValue, undefined, isKeyCollectionMember); + OnyxUtils.keyChanged(key, value, undefined, isKeyCollectionMember); }); } @@ -237,7 +236,7 @@ function merge(key: TKey, changes: OnyxMergeInput): Logger.logAlert(logMessages.incompatibleUpdateAlert(key, 'merge', existingValueType, newValueType)); } return isCompatible; - }) as Array>; + }); // Clean up the write queue, so we don't apply these changes again. delete mergeQueue[key]; @@ -259,7 +258,7 @@ function merge(key: TKey, changes: OnyxMergeInput): OnyxUtils.sendActionToDevTools(OnyxUtils.METHOD.MERGE, key, changes, mergedValue); }); } catch (error) { - Logger.logAlert(`An error occurred while applying merge for key: ${key}, Error: ${error}`); + Logger.logAlert(`An error occurred while applying merge for key: ${key}, Error: ${error instanceof Error ? error.message : String(error)}`); return Promise.resolve(); } }); @@ -282,7 +281,13 @@ function merge(key: TKey, changes: OnyxMergeInput): * @param collection Object collection keyed by individual collection member keys and values */ function mergeCollection(collectionKey: TKey, collection: OnyxMergeCollectionInput): Promise { - return OnyxUtils.afterInit(() => OnyxUtils.mergeCollectionWithPatches({collectionKey, collection, isProcessingCollectionUpdate: true})); + return OnyxUtils.afterInit(() => + OnyxUtils.mergeCollectionWithPatches({ + collectionKey, + collection, + isProcessingCollectionUpdate: true, + }), + ); } /** @@ -321,7 +326,10 @@ function clear(keysToPreserve: OnyxKey[] = []): Promise { // because the notification process needs the old values in cache but at that point they will be already removed from it. const keyValuesToResetAsCollection: Record< OnyxKey, - {oldValues: Record; newValues: Record} + { + oldValues: Record; + newValues: Record; + } > = {}; const allKeys = new Set([...cachedKeys, ...initialKeys]); @@ -350,7 +358,10 @@ function clear(keysToPreserve: OnyxKey[] = []): Promise { if (collectionKey) { if (!keyValuesToResetAsCollection[collectionKey]) { - keyValuesToResetAsCollection[collectionKey] = {oldValues: {}, newValues: {}}; + keyValuesToResetAsCollection[collectionKey] = { + oldValues: {}, + newValues: {}, + }; } keyValuesToResetAsCollection[collectionKey].oldValues[key] = oldValue; keyValuesToResetAsCollection[collectionKey].newValues[key] = newValue ?? undefined; @@ -434,6 +445,37 @@ function update(data: Array>): Promise = Promise.resolve(); const onyxMethods = Object.values(OnyxUtils.METHOD); + const handlers: Record) => void> = { + [OnyxUtils.METHOD.SET]: enqueueSetOperation, + [OnyxUtils.METHOD.MERGE]: enqueueMergeOperation, + [OnyxUtils.METHOD.MERGE_COLLECTION]: (k, v) => { + const collection = v as OnyxMergeCollectionInput; + if (!OnyxUtils.isValidNonEmptyCollectionForMerge(collection)) { + Logger.logInfo('Invalid or empty value provided in Onyx mergeCollection. Skipping this operation.'); + return; + } + + // Confirm all the collection keys belong to the same parent + const collectionKeys = Object.keys(collection); + if (OnyxUtils.doAllCollectionItemsBelongToSameParent(k, collectionKeys)) { + const mergedCollection: OnyxInputKeyValueMapping = collection; + for (const collectionKey of collectionKeys) enqueueMergeOperation(collectionKey, mergedCollection[collectionKey]); + } + }, + [OnyxUtils.METHOD.SET_COLLECTION]: (k, v) => promises.push(() => setCollection(k as TKey, v as OnyxSetCollectionInput)), + [OnyxUtils.METHOD.MULTI_SET]: (k, v) => { + if (typeof v !== 'object' || Array.isArray(v) || typeof v === 'function') { + Logger.logInfo(`Invalid value provided in Onyx multiSet. Value must be of type object. Skipping this operation.`); + return; + } + + for (const [entryKey, entryValue] of Object.entries(v as Partial)) enqueueSetOperation(entryKey, entryValue); + }, + [OnyxUtils.METHOD.CLEAR]: () => { + clearPromise = clear(); + }, + }; + for (const {onyxMethod, key, value} of data) { if (!onyxMethods.includes(onyxMethod)) { Logger.logInfo(`Invalid onyxMethod ${onyxMethod} in Onyx update. Skipping this operation.`); @@ -444,37 +486,6 @@ function update(data: Array>): Promise void> = { - [OnyxUtils.METHOD.SET]: enqueueSetOperation, - [OnyxUtils.METHOD.MERGE]: enqueueMergeOperation, - [OnyxUtils.METHOD.MERGE_COLLECTION]: () => { - const collection = value as OnyxMergeCollectionInput; - if (!OnyxUtils.isValidNonEmptyCollectionForMerge(collection)) { - Logger.logInfo('Invalid or empty value provided in Onyx mergeCollection. Skipping this operation.'); - return; - } - - // Confirm all the collection keys belong to the same parent - const collectionKeys = Object.keys(collection); - if (OnyxUtils.doAllCollectionItemsBelongToSameParent(key, collectionKeys)) { - const mergedCollection: OnyxInputKeyValueMapping = collection; - for (const collectionKey of collectionKeys) enqueueMergeOperation(collectionKey, mergedCollection[collectionKey]); - } - }, - [OnyxUtils.METHOD.SET_COLLECTION]: (k, v) => promises.push(() => setCollection(k as TKey, v as OnyxSetCollectionInput)), - [OnyxUtils.METHOD.MULTI_SET]: (k, v) => { - if (typeof value !== 'object' || Array.isArray(value) || typeof value === 'function') { - Logger.logInfo(`Invalid value provided in Onyx multiSet. Value must be of type object. Skipping this operation.`); - return; - } - - for (const [entryKey, entryValue] of Object.entries(v as Partial)) enqueueSetOperation(entryKey, entryValue); - }, - [OnyxUtils.METHOD.CLEAR]: () => { - clearPromise = clear(); - }, - }; - handlers[onyxMethod](key, value); } @@ -497,7 +508,7 @@ function update(data: Array>): Promise(data: Array>): Promise OnyxUtils.partialSetCollection({collectionKey, collection: batchedCollectionUpdates.set as OnyxSetCollectionInput})); + promises.push(() => + OnyxUtils.partialSetCollection({ + collectionKey, + collection: batchedCollectionUpdates.set, + }), + ); } } for (const [key, operations] of Object.entries(updateQueue)) { - if (operations[0] === null) { + if (operations.at(0) === null) { const batchedChanges = OnyxUtils.mergeChanges(operations).result; promises.push(() => set(key, batchedChanges)); continue; diff --git a/lib/OnyxCache.ts b/lib/OnyxCache.ts index 23fae2130..d481e4df6 100644 --- a/lib/OnyxCache.ts +++ b/lib/OnyxCache.ts @@ -1,5 +1,5 @@ import {deepEqual} from 'fast-equals'; -import bindAll from 'lodash/bindAll'; +import bindAll from 'lodash.bindall'; import type {ValueOf} from 'type-fest'; import utils from './utils'; import type {CollectionKeyBase, KeyValueMapping, NonUndefined, OnyxCollection, OnyxKey, OnyxValue} from './types'; @@ -42,7 +42,7 @@ class OnyxCache { * Captured pending tasks for already running storage methods * Using a map yields better performance on operations such a delete */ - private pendingPromises: Map | OnyxKey[]>>; + private pendingPromises: Map>; /** List of keys that are safe to remove when we reach max storage */ private evictionAllowList: OnyxKey[] = []; @@ -272,7 +272,7 @@ class OnyxCache { * provided from this function * @param taskName - unique name given for the task */ - getTaskPromise(taskName: CacheTask): Promise | OnyxKey[]> | undefined { + getTaskPromise(taskName: CacheTask): Promise | undefined { return this.pendingPromises.get(taskName); } diff --git a/lib/OnyxConnectionManager.ts b/lib/OnyxConnectionManager.ts index 1cc245e32..ca5a5736e 100644 --- a/lib/OnyxConnectionManager.ts +++ b/lib/OnyxConnectionManager.ts @@ -1,4 +1,4 @@ -import bindAll from 'lodash/bindAll'; +import bindAll from 'lodash.bindall'; import * as Logger from './Logger'; import type {ConnectOptions} from './Onyx'; import OnyxUtils from './OnyxUtils'; @@ -138,7 +138,7 @@ class OnyxConnectionManager { (callback as DefaultConnectCallback)(connection.cachedCallbackValue, connection.cachedCallbackKey as OnyxKey); } } catch (error) { - Logger.logAlert(`[ConnectionManager] Subscriber callback threw an error for key '${connection.onyxKey}': ${error}`); + Logger.logAlert(`[ConnectionManager] Subscriber callback threw an error for key '${connection.onyxKey}': ${String(error)}`); } } } @@ -173,7 +173,7 @@ class OnyxConnectionManager { subscriptionID = OnyxUtils.subscribeToKey({ ...connectOptions, callback, - } as ConnectOptions); + }); connectionMetadata = { subscriptionID, diff --git a/lib/OnyxKeys.ts b/lib/OnyxKeys.ts index 748b717d9..275ff7b3d 100644 --- a/lib/OnyxKeys.ts +++ b/lib/OnyxKeys.ts @@ -178,7 +178,7 @@ function splitCollectionMemberKey | undefined, TChange extends OnyxInput | undefined>( - key: TKey, - existingValue: TValue, - validChanges: TChange[], -) => { +const applyMerge: ApplyMerge = , TChange extends OnyxInput>(key: TKey, existingValue: TValue, validChanges: TChange[]) => { // If any of the changes is null, we need to discard the existing value. const baseValue = validChanges.includes(null as TChange) ? undefined : existingValue; diff --git a/lib/OnyxMerge/index.ts b/lib/OnyxMerge/index.ts index dd6c86b5e..68be645fc 100644 --- a/lib/OnyxMerge/index.ts +++ b/lib/OnyxMerge/index.ts @@ -5,11 +5,7 @@ import Storage from '../storage'; import type {OnyxInput, OnyxKey, OnyxValue} from '../types'; import type {ApplyMerge} from './types'; -const applyMerge: ApplyMerge = | undefined, TChange extends OnyxInput | undefined>( - key: TKey, - existingValue: TValue, - validChanges: TChange[], -) => { +const applyMerge: ApplyMerge = , TChange extends OnyxInput>(key: TKey, existingValue: TValue, validChanges: TChange[]) => { const {result: mergedValue} = OnyxUtils.mergeChanges(validChanges, existingValue); // In cache, we don't want to remove the key if it's null to improve performance and speed up the next merge. diff --git a/lib/OnyxMerge/types.ts b/lib/OnyxMerge/types.ts index e53d8ff32..a755e3ce0 100644 --- a/lib/OnyxMerge/types.ts +++ b/lib/OnyxMerge/types.ts @@ -4,7 +4,7 @@ type ApplyMergeResult = { mergedValue: TValue; }; -type ApplyMerge = | undefined, TChange extends OnyxInput | null>( +type ApplyMerge = , TChange extends OnyxInput>( key: TKey, existingValue: TValue, validChanges: TChange[], diff --git a/lib/OnyxSnapshotCache.ts b/lib/OnyxSnapshotCache.ts index 92c7bba1e..70af8021e 100644 --- a/lib/OnyxSnapshotCache.ts +++ b/lib/OnyxSnapshotCache.ts @@ -43,11 +43,13 @@ class OnyxSnapshotCache { */ getSelectorID(selector: UseOnyxSelector): number { const typedSelector = selector as unknown as UseOnyxSelector; - if (!this.selectorIDMap.has(typedSelector)) { - const id = this.selectorIDCounter++; - this.selectorIDMap.set(typedSelector, id); + const existingID = this.selectorIDMap.get(typedSelector); + if (existingID !== undefined) { + return existingID; } - return this.selectorIDMap.get(typedSelector)!; + const id = this.selectorIDCounter++; + this.selectorIDMap.set(typedSelector, id); + return id; } /** @@ -67,7 +69,7 @@ class OnyxSnapshotCache { const cacheKey = `${key}_${selectorID}`; // Increment reference count for this cache key - const currentCount = this.cacheKeyRefCounts.get(cacheKey) || 0; + const currentCount = this.cacheKeyRefCounts.get(cacheKey) ?? 0; this.cacheKeyRefCounts.set(cacheKey, currentCount + 1); return cacheKey; @@ -78,7 +80,7 @@ class OnyxSnapshotCache { * Decrements reference counter and removes cache entry if no consumers remain. */ deregisterConsumer(key: OnyxKey, cacheKey: string): void { - const currentCount = this.cacheKeyRefCounts.get(cacheKey) || 0; + const currentCount = this.cacheKeyRefCounts.get(cacheKey) ?? 0; if (currentCount <= 1) { // Last consumer - remove from reference counter and cache @@ -111,10 +113,12 @@ class OnyxSnapshotCache { * Set cached snapshot result for a key and cache key combination */ setCachedResult>>(key: OnyxKey, cacheKey: string, result: TResult): void { - if (!this.snapshotCache.has(key)) { - this.snapshotCache.set(key, new Map()); + let keyCache = this.snapshotCache.get(key); + if (!keyCache) { + keyCache = new Map(); + this.snapshotCache.set(key, keyCache); } - this.snapshotCache.get(key)!.set(cacheKey, result); + keyCache.set(cacheKey, result); } /** diff --git a/lib/OnyxUtils.ts b/lib/OnyxUtils.ts index 4dc1ba2ce..48a9c7d98 100644 --- a/lib/OnyxUtils.ts +++ b/lib/OnyxUtils.ts @@ -54,6 +54,10 @@ const MAX_STORAGE_OPERATION_RETRY_ATTEMPTS = 5; type OnyxMethod = ValueOf; +function formatCaughtError(error: unknown): string { + return error instanceof Error ? error.message : String(error); +} + // Key/value store of Onyx key and arrays of values to merge let mergeQueue: Record>> = {}; let mergeQueuePromise: Record> = {}; @@ -212,13 +216,8 @@ function sendActionToDevTools( value: OnyxEntry, mergedValue?: OnyxEntry, ): void; -function sendActionToDevTools( - method: OnyxMethod, - key: OnyxKey | undefined, - value: OnyxCollection | OnyxEntry, - mergedValue: OnyxEntry = undefined, -): void { - DevTools.registerAction(utils.formatActionName(method, key), value, key ? {[key]: mergedValue || value} : (value as OnyxCollection)); +function sendActionToDevTools(method: OnyxMethod, key: OnyxKey | undefined, value: unknown, mergedValue: OnyxEntry = undefined): void { + DevTools.registerAction(utils.formatActionName(method, key), value, key ? {[key]: mergedValue !== undefined ? mergedValue : value} : (value as OnyxCollection)); } /** @@ -348,7 +347,10 @@ function multiGet(keys: CollectionKeyBase[]): Promise { for (const [index, value] of values.entries()) { - dataMap.set(pendingKeys[index], value); + const pendingKey = pendingKeys.at(index); + if (pendingKey !== undefined) { + dataMap.set(pendingKey, value); + } } return Promise.resolve(); @@ -390,7 +392,7 @@ function multiGet(keys: CollectionKeyBase[]): Promise); - temp[key] = value as OnyxValue; + temp[key] = value; } cache.merge(temp); return dataMap; @@ -493,7 +495,7 @@ function tryGetCachedValue(key: TKey): OnyxValue function getCachedCollection(collectionKey: TKey, collectionMemberKeys?: string[]): NonNullable> { // Use optimized collection data retrieval when cache is populated const collectionData = cache.getCollectionData(collectionKey); - const allKeys = collectionMemberKeys || cache.getAllKeys(); + const allKeys = collectionMemberKeys ?? cache.getAllKeys(); if (collectionData !== undefined && (Array.isArray(allKeys) ? allKeys.length > 0 : allKeys.size > 0)) { // If we have specific member keys, filter the collection if (collectionMemberKeys) { @@ -579,10 +581,13 @@ function keysChanged( } try { - lastConnectionCallbackData.set(subscriber.subscriptionID, {value: cachedCollection, matchedKey: subscriber.key}); + lastConnectionCallbackData.set(subscriber.subscriptionID, { + value: cachedCollection, + matchedKey: subscriber.key, + }); subscriber.callback(cachedCollection, subscriber.key); } catch (error) { - Logger.logAlert(`[OnyxUtils.keysChanged] Subscriber callback threw an error for key '${collectionKey}': ${error}`); + Logger.logAlert(`[OnyxUtils.keysChanged] Subscriber callback threw an error for key '${collectionKey}': ${formatCaughtError(error)}`); } } @@ -605,7 +610,7 @@ function keysChanged( matchedKey: subscriber.key, }); } catch (error) { - Logger.logAlert(`[OnyxUtils.keysChanged] Subscriber callback threw an error for key '${collectionKey}': ${error}`); + Logger.logAlert(`[OnyxUtils.keysChanged] Subscriber callback threw an error for key '${collectionKey}': ${formatCaughtError(error)}`); } } } @@ -674,7 +679,10 @@ function keyChanged( cachedCollection = getCachedCollection(subscriber.key); cachedCollections[subscriber.key] = cachedCollection; } - lastConnectionCallbackData.set(subscriber.subscriptionID, {value: cachedCollection, matchedKey: subscriber.key}); + lastConnectionCallbackData.set(subscriber.subscriptionID, { + value: cachedCollection, + matchedKey: subscriber.key, + }); subscriber.callback(cachedCollection, subscriber.key); continue; } @@ -688,7 +696,7 @@ function keyChanged( }); continue; } catch (error) { - Logger.logAlert(`[OnyxUtils.keyChanged] Subscriber callback threw an error for key '${key}': ${error}`); + Logger.logAlert(`[OnyxUtils.keyChanged] Subscriber callback threw an error for key '${key}': ${formatCaughtError(error)}`); } continue; @@ -927,7 +935,7 @@ function prepareKeyValuePairsForStorage( * @param changes Array of changes that should be merged * @param existingValue The existing value that should be merged with the changes */ -function mergeChanges | undefined, TChange extends OnyxInput | undefined>(changes: TChange[], existingValue?: TValue): FastMergeResult { +function mergeChanges, TChange extends OnyxInput>(changes: TChange[], existingValue?: TValue): FastMergeResult { return mergeInternal('merge', changes, existingValue); } @@ -938,10 +946,7 @@ function mergeChanges | undefined, TChange ext * @param changes Array of changes that should be merged * @param existingValue The existing value that should be merged with the changes */ -function mergeAndMarkChanges | undefined, TChange extends OnyxInput | undefined>( - changes: TChange[], - existingValue?: TValue, -): FastMergeResult { +function mergeAndMarkChanges, TChange extends OnyxInput>(changes: TChange[], existingValue?: TValue): FastMergeResult { return mergeInternal('mark', changes, existingValue); } @@ -951,11 +956,7 @@ function mergeAndMarkChanges | undefined, TCha * @param changes Array of changes that should be merged * @param existingValue The existing value that should be merged with the changes */ -function mergeInternal | undefined, TChange extends OnyxInput | undefined>( - mode: 'merge' | 'mark', - changes: TChange[], - existingValue?: TValue, -): FastMergeResult { +function mergeInternal, TChange extends OnyxInput>(mode: 'merge' | 'mark', changes: TChange[], existingValue?: TValue): FastMergeResult { const lastChange = changes?.at(-1); if (Array.isArray(lastChange)) { @@ -985,7 +986,10 @@ function mergeInternal | undefined, TChange ex // If we have anything else we can't merge it so we'll // simply return the last value that was queued - return {result: lastChange as TChange, replaceNullPatches: []}; + if (lastChange === undefined) { + return {result: (existingValue ?? {}) as TChange, replaceNullPatches: []}; + } + return {result: lastChange, replaceNullPatches: []}; } /** @@ -1226,7 +1230,7 @@ function updateSnapshots(data: Array>, me } if (Array.isArray(value) || Array.isArray(snapshotData[key])) { - updatedData[key] = value || []; + updatedData[key] = value ?? []; continue; } @@ -1235,7 +1239,7 @@ function updateSnapshots(data: Array>, me continue; } - const oldValue = updatedData[key] || {}; + const oldValue = updatedData[key] ?? {}; // Snapshot entries are stored as a "shape" of the last known data per key, so by default we only // merge fields that already exist in the snapshot to avoid unintentionally bloating snapshot data. @@ -1435,7 +1439,7 @@ function multiSetWithRetry(data: OnyxMultiSetInput, retryAttempt?: number): Prom // Skip on retry — already notified on attempt 0 (see same-reason comment above). if (!retryAttempt) { for (const [collectionKey, batch] of collectionBatches) { - keysChanged(collectionKey as CollectionKeyBase, batch.partial, batch.previous); + keysChanged(collectionKey, batch.partial, batch.previous); } } diff --git a/lib/StorageCircuitBreaker.ts b/lib/StorageCircuitBreaker.ts index b8cfef0c3..bb4a19b51 100644 --- a/lib/StorageCircuitBreaker.ts +++ b/lib/StorageCircuitBreaker.ts @@ -68,7 +68,7 @@ class StorageCircuitBreaker extends AbstractCircuitBreaker { // failure that triggered it must not re-trip the circuit. Let it proceed; the probe's outcome // is the verdict — recordWriteSuccess (retry landed) closes, recordProbeFailure (retry failed, // re-entering retryOperation) reopens. - if (this.peekState() === 'half-open') { + if (this.peekState() === 'halfOpen') { return false; } return this.recordFailure(); diff --git a/lib/storage/InstanceSync/index.ts b/lib/storage/InstanceSync/index.ts index af7424f6c..46511894a 100644 --- a/lib/storage/InstanceSync/index.ts +++ b/lib/storage/InstanceSync/index.ts @@ -1,4 +1,4 @@ -import NOOP from 'lodash/noop'; +const NOOP = (() => {}) as (...args: unknown[]) => void; /** * This is used to keep multiple browser tabs in sync, therefore only needed on web diff --git a/lib/storage/InstanceSync/index.web.ts b/lib/storage/InstanceSync/index.web.ts index cb1a3c5bb..4351e8e1d 100644 --- a/lib/storage/InstanceSync/index.web.ts +++ b/lib/storage/InstanceSync/index.web.ts @@ -12,7 +12,7 @@ const SYNC_ONYX = 'SYNC_ONYX'; /** * Raise an event through `localStorage` to let other tabs know a value changed - * @param {String} onyxKey + * @param onyxKey */ function raiseStorageSyncEvent(onyxKey: OnyxKey) { global.localStorage.setItem(SYNC_ONYX, onyxKey); @@ -30,7 +30,7 @@ let storage = NoopProvider; const InstanceSync = { shouldBeUsed: true, /** - * @param {Function} onStorageKeyChanged Storage synchronization mechanism keeping all opened tabs in sync + * @param onStorageKeyChanged Storage synchronization mechanism keeping all opened tabs in sync */ init: (onStorageKeyChanged: OnStorageKeyChanged, store: StorageProvider) => { storage = store; diff --git a/lib/storage/errors.ts b/lib/storage/errors.ts index 635f55428..23063ec74 100644 --- a/lib/storage/errors.ts +++ b/lib/storage/errors.ts @@ -30,9 +30,18 @@ const StorageErrorClass = { */ function getErrorParts(error: unknown): {name: string; message: string} { if (error instanceof Error || (typeof DOMException !== 'undefined' && error instanceof DOMException)) { - return {name: (error.name ?? '').toLowerCase(), message: (error.message ?? '').toLowerCase()}; + return { + name: (error.name ?? '').toLowerCase(), + message: (error.message ?? '').toLowerCase(), + }; } - return {name: '', message: String(error ?? '').toLowerCase()}; + if (typeof error === 'string') { + return {name: '', message: error.toLowerCase()}; + } + if (error === null || error === undefined) { + return {name: '', message: ''}; + } + return {name: '', message: JSON.stringify(error).toLowerCase()}; } export {StorageErrorClass, getErrorParts}; diff --git a/lib/storage/index.ts b/lib/storage/index.ts index 414a0a0ca..c35968ce3 100644 --- a/lib/storage/index.ts +++ b/lib/storage/index.ts @@ -20,7 +20,8 @@ type Storage = { * Degrade performance by removing the storage provider and only using cache */ function degradePerformance(error: Error) { - Logger.logHmmm(`Error while using ${provider.name}. Falling back to only using cache and dropping storage.\n Error: ${error.message}\n Stack: ${error.stack}\n Cause: ${error.cause}`); + const causeMessage = error.cause instanceof Error ? error.cause.message : typeof error.cause === 'string' ? error.cause : ''; + Logger.logHmmm(`Error while using ${provider.name}. Falling back to only using cache and dropping storage.\n Error: ${error.message}\n Stack: ${error.stack}\n Cause: ${causeMessage}`); console.error(error); provider = MemoryOnlyProvider; } diff --git a/lib/storage/providers/IDBKeyValProvider/index.ts b/lib/storage/providers/IDBKeyValProvider/index.ts index 09fcbb1f2..baaaecb66 100644 --- a/lib/storage/providers/IDBKeyValProvider/index.ts +++ b/lib/storage/providers/IDBKeyValProvider/index.ts @@ -85,7 +85,7 @@ const provider: StorageProvider = { if (value === null) { store.delete(key); } else { - const newValue = utils.fastMerge(values[index] as Record, value as Record, { + const newValue = utils.fastMerge(values.at(index) as Record, value as Record, { shouldRemoveNestedNulls: true, objectRemovalMode: 'replace', }).result; diff --git a/lib/storage/providers/NoopProvider.ts b/lib/storage/providers/NoopProvider.ts index 4f3aaea11..6ef440e55 100644 --- a/lib/storage/providers/NoopProvider.ts +++ b/lib/storage/providers/NoopProvider.ts @@ -1,4 +1,4 @@ -import type {OnyxValue} from '../../types'; +import type {OnyxKey, OnyxValue} from '../../types'; import {StorageErrorClass} from '../errors'; import type StorageProvider from './types'; @@ -25,8 +25,9 @@ const provider: StorageProvider = { /** * Get the value of a given key or return `null` if it's not available in memory */ - getItem(_key) { - return Promise.resolve(null as OnyxValue); + getItem(key: TKey) { + void key; + return Promise.resolve(null as OnyxValue); }, /** @@ -105,7 +106,10 @@ const provider: StorageProvider = { * `bytesRemaining` will always be `Number.POSITIVE_INFINITY` since we don't have a hard limit on memory. */ getDatabaseSize() { - return Promise.resolve({bytesRemaining: Number.POSITIVE_INFINITY, bytesUsed: 0}); + return Promise.resolve({ + bytesRemaining: Number.POSITIVE_INFINITY, + bytesUsed: 0, + }); }, }; diff --git a/lib/storage/providers/SQLiteProvider.ts b/lib/storage/providers/SQLiteProvider.ts index 56fadc588..7e19f54b0 100644 --- a/lib/storage/providers/SQLiteProvider.ts +++ b/lib/storage/providers/SQLiteProvider.ts @@ -41,7 +41,7 @@ const DB_NAME = 'OnyxDB'; * Prevents the stringifying of the object markers. */ function objectMarkRemover(key: string, value: unknown) { - if (key === utils.ONYX_INTERNALS__REPLACE_OBJECT_MARK) return undefined; + if (key === utils.ONYX_INTERNALS_REPLACE_OBJECT_MARK) return undefined; return value; } diff --git a/lib/types.ts b/lib/types.ts index f2b5b8014..1d71a12d3 100644 --- a/lib/types.ts +++ b/lib/types.ts @@ -88,6 +88,7 @@ type TypeOptions = Merge< * } * ``` */ +// eslint-disable-next-line @typescript-eslint/no-empty-object-type, @typescript-eslint/consistent-type-definitions -- must remain an interface so consumers can augment via declare module interface CustomTypeOptions {} /** @@ -189,12 +190,12 @@ type NonTransformableTypes = * * settings = applySavedSettings({textEditor: {fontWeight: 500, fontColor: null}}); */ -type NullishDeep = T extends NonTransformableTypes ? T : T extends object ? NullishObjectDeep : unknown; +type NullishDeep = T extends NonTransformableTypes ? T : T extends Record ? NullishObjectDeep : T; /** - * Same as `NullishDeep`, but accepts only `object`s as inputs. Internal helper for `NullishDeep`. + * Same as `NullishDeep`, but accepts only records as inputs. Internal helper for `NullishDeep`. */ -type NullishObjectDeep = { +type NullishObjectDeep> = { [KeyType in keyof ObjectType]?: NullishDeep | null; }; @@ -311,12 +312,36 @@ type ExpandOnyxKeys = TKey extends CollectionKeyBase ? NoI type OnyxUpdate = { // ⚠️ DO NOT CHANGE THIS TYPE, UNLESS YOU KNOW WHAT YOU ARE DOING. ⚠️ [K in TKey]: - | {onyxMethod: typeof OnyxUtils.METHOD.SET; key: ExpandOnyxKeys; value: OnyxSetInput} - | {onyxMethod: typeof OnyxUtils.METHOD.MULTI_SET; key: ExpandOnyxKeys; value: OnyxMultiSetInput} - | {onyxMethod: typeof OnyxUtils.METHOD.MERGE; key: ExpandOnyxKeys; value: OnyxMergeInput} - | {onyxMethod: typeof OnyxUtils.METHOD.CLEAR; key: ExpandOnyxKeys; value?: never} - | {onyxMethod: typeof OnyxUtils.METHOD.MERGE_COLLECTION; key: K; value: OnyxMergeCollectionInput} - | {onyxMethod: typeof OnyxUtils.METHOD.SET_COLLECTION; key: K; value: OnyxSetCollectionInput}; + | { + onyxMethod: typeof OnyxUtils.METHOD.SET; + key: ExpandOnyxKeys; + value: OnyxSetInput; + } + | { + onyxMethod: typeof OnyxUtils.METHOD.MULTI_SET; + key: ExpandOnyxKeys; + value: OnyxMultiSetInput; + } + | { + onyxMethod: typeof OnyxUtils.METHOD.MERGE; + key: ExpandOnyxKeys; + value: OnyxMergeInput; + } + | { + onyxMethod: typeof OnyxUtils.METHOD.CLEAR; + key: ExpandOnyxKeys; + value?: never; + } + | { + onyxMethod: typeof OnyxUtils.METHOD.MERGE_COLLECTION; + key: K; + value: OnyxMergeCollectionInput; + } + | { + onyxMethod: typeof OnyxUtils.METHOD.SET_COLLECTION; + key: K; + value: OnyxSetCollectionInput; + }; }[TKey]; /** diff --git a/lib/utils.ts b/lib/utils.ts index b469c1acd..2052cb495 100644 --- a/lib/utils.ts +++ b/lib/utils.ts @@ -37,7 +37,7 @@ type FastMergeResult = { replaceNullPatches: FastMergeReplaceNullPatch[]; }; -const ONYX_INTERNALS__REPLACE_OBJECT_MARK = 'ONYX_INTERNALS__REPLACE_OBJECT_MARK'; +const ONYX_INTERNALS_REPLACE_OBJECT_MARK = 'ONYX_INTERNALS__REPLACE_OBJECT_MARK'; /** * Merges two objects and removes null values if "shouldRemoveNestedNulls" is set to true @@ -66,7 +66,10 @@ function fastMerge(target: TValue, source: TValue, options?: FastMergeOp const mergedValue = mergeObject(target, source as Record, optionsWithDefaults, metadata, basePath) as TValue; - return {result: mergedValue, replaceNullPatches: metadata.replaceNullPatches}; + return { + result: mergedValue, + replaceNullPatches: metadata.replaceNullPatches, + }; } /** @@ -143,19 +146,19 @@ function mergeObject>( // When calling fastMerge again with "mark" removal mode, the marked objects will be removed. if (options.objectRemovalMode === 'mark' && targetProperty === null) { hasChanged = true; - targetProperty = {[ONYX_INTERNALS__REPLACE_OBJECT_MARK]: true}; + targetProperty = {[ONYX_INTERNALS_REPLACE_OBJECT_MARK]: true}; metadata.replaceNullPatches.push([[...basePath, key], {...sourceProperty}]); } // Later, when merging the batched changes with the Onyx value, if a nested object of the batched changes // has the internal flag set, we replace the entire destination object with the source one and remove // the flag. - if (options.objectRemovalMode === 'replace' && sourceProperty[ONYX_INTERNALS__REPLACE_OBJECT_MARK]) { + if (options.objectRemovalMode === 'replace' && sourceProperty[ONYX_INTERNALS_REPLACE_OBJECT_MARK]) { hasChanged = true; // We do a spread here in order to have a new object reference and allow us to delete the internal flag // of the merged object only. const sourcePropertyWithoutMark = {...sourceProperty}; - delete sourcePropertyWithoutMark.ONYX_INTERNALS__REPLACE_OBJECT_MARK; + delete sourcePropertyWithoutMark[ONYX_INTERNALS_REPLACE_OBJECT_MARK]; destination[key] = sourcePropertyWithoutMark; continue; } @@ -197,7 +200,7 @@ function isMergeableObject>(value: unkno } /** Deep removes the nested null values from the given value. Returns the original reference if no nulls were found. */ -function removeNestedNullValues | null>(value: TValue): TValue { +function removeNestedNullValues>(value: TValue): TValue { if (value === null || value === undefined || typeof value !== 'object' || Array.isArray(value)) { return value; } @@ -237,7 +240,12 @@ function formatActionName(method: string, key?: OnyxKey): string { function checkCompatibilityWithExistingValue( value: unknown, existingValue: unknown, -): {isCompatible: boolean; existingValueType?: string; newValueType?: string; isEmptyArrayCoercion?: boolean} { +): { + isCompatible: boolean; + existingValueType?: string; + newValueType?: string; + isEmptyArrayCoercion?: boolean; +} { if (!existingValue || !value) { return { isCompatible: true, @@ -330,6 +338,6 @@ export default { checkCompatibilityWithExistingValue, pick, omit, - ONYX_INTERNALS__REPLACE_OBJECT_MARK, + ONYX_INTERNALS_REPLACE_OBJECT_MARK, }; export type {FastMergeResult, FastMergeReplaceNullPatch, FastMergeOptions}; diff --git a/package-lock.json b/package-lock.json index 348a622e4..ab7f1272f 100644 --- a/package-lock.json +++ b/package-lock.json @@ -20,10 +20,9 @@ "devDependencies": { "@actions/core": "^1.10.1", "@actions/github": "^6.0.1", + "@eslint/eslintrc": "^3.3.1", "@jest/globals": "^29.7.0", - "@lwc/eslint-plugin-lwc": "^3.3.0", "@ngneat/falso": "^7.3.0", - "@react-native-community/eslint-config": "^3.2.0", "@react-native/babel-preset": "0.76.3", "@react-native/polyfills": "^2.0.0", "@testing-library/react-native": "^13.2.0", @@ -31,22 +30,19 @@ "@types/jest": "^29.5.14", "@types/jsdoc-to-markdown": "^7.0.6", "@types/lodash": "^4.14.202", + "@types/lodash.bindall": "^4.4.9", "@types/node": "^20.11.5", "@types/react": "^18.2.14", "@types/react-native": "^0.70.0", "@types/underscore": "^1.11.15", - "@typescript-eslint/eslint-plugin": "^8.51.0", - "@typescript-eslint/parser": "^8.51.0", "@vercel/ncc": "0.38.1", "better-sqlite3": "^12.10.0", "date-fns": "^4.1.0", "eslint": "^9.39.2", - "eslint-config-expensify": "2.0.108", - "eslint-config-prettier": "^9.1.0", - "eslint-import-resolver-typescript": "^4.4.4", - "eslint-plugin-import": "^2.31.0", - "eslint-plugin-jsx-a11y": "^6.10.2", - "eslint-plugin-react": "^7.37.5", + "eslint-config-expensify": "4.0.6", + "eslint-config-prettier": "^10.1.5", + "eslint-import-resolver-typescript": "^4.4.5", + "eslint-seatbelt": "^0.1.3", "fake-indexeddb": "^6.2.5", "idb-keyval": "^6.2.1", "jest": "^29.7.0", @@ -164,13 +160,13 @@ } }, "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==", + "version": "7.29.7", + "resolved": "https://registry.npmjs.org/@babel/code-frame/-/code-frame-7.29.7.tgz", + "integrity": "sha512-Aup7aUOfpbAUg2ROOJN6Iw5f9DMBlzu0mIkm/malLQFN/YQgO48wCj0Kxa3sEHJvPVFg7siR+qRInwXd2qhQKw==", "dev": true, "license": "MIT", "dependencies": { - "@babel/helper-validator-identifier": "^7.27.1", + "@babel/helper-validator-identifier": "^7.29.7", "js-tokens": "^4.0.0", "picocolors": "^1.1.1" }, @@ -179,9 +175,9 @@ } }, "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==", + "version": "7.29.7", + "resolved": "https://registry.npmjs.org/@babel/compat-data/-/compat-data-7.29.7.tgz", + "integrity": "sha512-locTkQyKvwIEgBzVrn8693ebc97F2U8ZHjbXwDXJ5Fn2TCpNwTlKcaKLkdHop5c/icOFE7qt7Q9JC5hnKNa6Gg==", "dev": true, "license": "MIT", "engines": { @@ -194,6 +190,7 @@ "integrity": "sha512-UlLAnTPrFdNGoFtbSXwcGFQBtQZJCNjaN6hQNP3UPvuNXT1i82N26KL3dZeIpNalWywr9IuQuncaAfUaS1g6sQ==", "dev": true, "license": "MIT", + "peer": true, "dependencies": { "@ampproject/remapping": "^2.2.0", "@babel/code-frame": "^7.27.1", @@ -239,14 +236,14 @@ } }, "node_modules/@babel/generator": { - "version": "7.28.5", - "resolved": "https://registry.npmjs.org/@babel/generator/-/generator-7.28.5.tgz", - "integrity": "sha512-3EwLFhZ38J4VyIP6WNtt2kUdW9dokXA9Cr4IVIFHuCpZ3H8/YFOl5JjZHisrn1fATPBmKKqXzDFvh9fUwHz6CQ==", + "version": "7.29.7", + "resolved": "https://registry.npmjs.org/@babel/generator/-/generator-7.29.7.tgz", + "integrity": "sha512-DkXD5OJQaAQIdZ1bt3UZdEnHAn9Imd3IVBdX03UFe+ony9Ojw5pzr9YVKGDY1jt+Gcn/FnGkNf8r+Vj5NOJWtQ==", "dev": true, "license": "MIT", "dependencies": { - "@babel/parser": "^7.28.5", - "@babel/types": "^7.28.5", + "@babel/parser": "^7.29.7", + "@babel/types": "^7.29.7", "@jridgewell/gen-mapping": "^0.3.12", "@jridgewell/trace-mapping": "^0.3.28", "jsesc": "^3.0.2" @@ -256,27 +253,27 @@ } }, "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==", + "version": "7.29.7", + "resolved": "https://registry.npmjs.org/@babel/helper-annotate-as-pure/-/helper-annotate-as-pure-7.29.7.tgz", + "integrity": "sha512-OoK6239jHPuSQOoS0kfTVKn0b/rVTk0seKq4Gd2UMLtmOVLjDC0ki3e+c90Trqv2gMfvJFqkiljrr568+qddiw==", "dev": true, "license": "MIT", "dependencies": { - "@babel/types": "^7.27.3" + "@babel/types": "^7.29.7" }, "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==", + "version": "7.29.7", + "resolved": "https://registry.npmjs.org/@babel/helper-compilation-targets/-/helper-compilation-targets-7.29.7.tgz", + "integrity": "sha512-wem6WaBj4NaVYVdNhLPPVacES6ZJ+KBBfSkTMD3YZxbP3rm3Di85tJU5ljaUNhaOynt+Aj0xruhYuzQBt8n71g==", "dev": true, "license": "MIT", "dependencies": { - "@babel/compat-data": "^7.27.2", - "@babel/helper-validator-option": "^7.27.1", + "@babel/compat-data": "^7.29.7", + "@babel/helper-validator-option": "^7.29.7", "browserslist": "^4.24.0", "lru-cache": "^5.1.1", "semver": "^6.3.1" @@ -286,18 +283,18 @@ } }, "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==", + "version": "7.29.7", + "resolved": "https://registry.npmjs.org/@babel/helper-create-class-features-plugin/-/helper-create-class-features-plugin-7.29.7.tgz", + "integrity": "sha512-IY3ZD9Tmooqr3TUhc3DUWxiuo8xx1DWLhd5M7hQ+ZWJamqM2BbalrBJb2MisSLoYorOj75U03qULCxQTY9r3hg==", "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", + "@babel/helper-annotate-as-pure": "^7.29.7", + "@babel/helper-member-expression-to-functions": "^7.29.7", + "@babel/helper-optimise-call-expression": "^7.29.7", + "@babel/helper-replace-supers": "^7.29.7", + "@babel/helper-skip-transparent-expression-wrappers": "^7.29.7", + "@babel/traverse": "^7.29.7", "semver": "^6.3.1" }, "engines": { @@ -308,14 +305,14 @@ } }, "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==", + "version": "7.29.7", + "resolved": "https://registry.npmjs.org/@babel/helper-create-regexp-features-plugin/-/helper-create-regexp-features-plugin-7.29.7.tgz", + "integrity": "sha512-907Uymvqgg1dwUA+7IGwFAOSYzQOuzPXKNJ1yxzwPffzkYFg2q2eHi1fIOs6sXkG9NbIUMunnUlkYsfRFNvomg==", "dev": true, "license": "MIT", "dependencies": { - "@babel/helper-annotate-as-pure": "^7.27.1", - "regexpu-core": "^6.2.0", + "@babel/helper-annotate-as-pure": "^7.29.7", + "regexpu-core": "^6.3.1", "semver": "^6.3.1" }, "engines": { @@ -326,26 +323,26 @@ } }, "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==", + "version": "0.6.8", + "resolved": "https://registry.npmjs.org/@babel/helper-define-polyfill-provider/-/helper-define-polyfill-provider-0.6.8.tgz", + "integrity": "sha512-47UwBLPpQi1NoWzLuHNjRoHlYXMwIJoBf7MFou6viC/sIHWYygpvr0B6IAyh5sBdA2nr2LPIRww8lfaUVQINBA==", "dev": true, "license": "MIT", "dependencies": { - "@babel/helper-compilation-targets": "^7.27.2", - "@babel/helper-plugin-utils": "^7.27.1", - "debug": "^4.4.1", + "@babel/helper-compilation-targets": "^7.28.6", + "@babel/helper-plugin-utils": "^7.28.6", + "debug": "^4.4.3", "lodash.debounce": "^4.0.8", - "resolve": "^1.22.10" + "resolve": "^1.22.11" }, "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==", + "version": "7.29.7", + "resolved": "https://registry.npmjs.org/@babel/helper-globals/-/helper-globals-7.29.7.tgz", + "integrity": "sha512-3nQVUAtvkKH9zahfWgw96Jc/uFOmjACE1kQz82E2lqWmHBgjzbNlsC22nuQTfahmWeQtTq5nQ/4Nnd2A1wj4zA==", "dev": true, "license": "MIT", "engines": { @@ -353,43 +350,43 @@ } }, "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==", + "version": "7.29.7", + "resolved": "https://registry.npmjs.org/@babel/helper-member-expression-to-functions/-/helper-member-expression-to-functions-7.29.7.tgz", + "integrity": "sha512-j+7JYmk1JYDtACIGj0QJqqWZjoUpMoEikQGADMaHgCMCSDqd2+P32rfcibUNrGOMWrlzK1WJBdxrB3JJQZwWtg==", "dev": true, "license": "MIT", "dependencies": { - "@babel/traverse": "^7.28.5", - "@babel/types": "^7.28.5" + "@babel/traverse": "^7.29.7", + "@babel/types": "^7.29.7" }, "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==", + "version": "7.29.7", + "resolved": "https://registry.npmjs.org/@babel/helper-module-imports/-/helper-module-imports-7.29.7.tgz", + "integrity": "sha512-ejHwrQQYcm9xnTivShn2IDOlIzInN34AXskvq9QicvCtEzq1Vzclu/tKF8Jq1Cg8JG2GL6/EmjgsCT7lXepE3g==", "dev": true, "license": "MIT", "dependencies": { - "@babel/traverse": "^7.27.1", - "@babel/types": "^7.27.1" + "@babel/traverse": "^7.29.7", + "@babel/types": "^7.29.7" }, "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==", + "version": "7.29.7", + "resolved": "https://registry.npmjs.org/@babel/helper-module-transforms/-/helper-module-transforms-7.29.7.tgz", + "integrity": "sha512-UPUVSyXbOh627KiCIGQSgwWzGeBKLkaJ9PJEdrngIwMSzxLR4jS4+f1f1jb7VzBbg8nFLaYotvVPFCTqdrmTAg==", "dev": true, "license": "MIT", "dependencies": { - "@babel/helper-module-imports": "^7.27.1", - "@babel/helper-validator-identifier": "^7.27.1", - "@babel/traverse": "^7.28.3" + "@babel/helper-module-imports": "^7.29.7", + "@babel/helper-validator-identifier": "^7.29.7", + "@babel/traverse": "^7.29.7" }, "engines": { "node": ">=6.9.0" @@ -399,22 +396,22 @@ } }, "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==", + "version": "7.29.7", + "resolved": "https://registry.npmjs.org/@babel/helper-optimise-call-expression/-/helper-optimise-call-expression-7.29.7.tgz", + "integrity": "sha512-+kmGVjcT9RGYzoDwdwEqEvGgKe3BYq+O1iGzjFubaNgZHwYHP6lsF2Yghf4kEuv9BV7tYDZ913aBW9am6YKong==", "dev": true, "license": "MIT", "dependencies": { - "@babel/types": "^7.27.1" + "@babel/types": "^7.29.7" }, "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==", + "version": "7.29.7", + "resolved": "https://registry.npmjs.org/@babel/helper-plugin-utils/-/helper-plugin-utils-7.29.7.tgz", + "integrity": "sha512-G7sHYigPY17oO5SYWnfD/0MTBwVR781S/JI643e/JhUYgVgWE/61SoW3NH9KWUKyKq5LVh3npif99Wkt6j86Jw==", "dev": true, "license": "MIT", "engines": { @@ -422,15 +419,15 @@ } }, "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==", + "version": "7.29.7", + "resolved": "https://registry.npmjs.org/@babel/helper-remap-async-to-generator/-/helper-remap-async-to-generator-7.29.7.tgz", + "integrity": "sha512-16AMiW26DbXWBbr3B8wNozKM0ydMLB892vaOaJW/fPJdnT8vJk5sdkQcU/isqUxyCE0cEoa8wZOcbgDuC4b6Og==", "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" + "@babel/helper-annotate-as-pure": "^7.29.7", + "@babel/helper-wrap-function": "^7.29.7", + "@babel/traverse": "^7.29.7" }, "engines": { "node": ">=6.9.0" @@ -440,15 +437,15 @@ } }, "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==", + "version": "7.29.7", + "resolved": "https://registry.npmjs.org/@babel/helper-replace-supers/-/helper-replace-supers-7.29.7.tgz", + "integrity": "sha512-atfGXWSeCiF4DnKZIfmJfQRkSw9b9gNNXR1kqKjbhG4pGYCOnkp8OcTB8E3NXjBu8NpheSnOeNKz8KT7UNFTmQ==", "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" + "@babel/helper-member-expression-to-functions": "^7.29.7", + "@babel/helper-optimise-call-expression": "^7.29.7", + "@babel/traverse": "^7.29.7" }, "engines": { "node": ">=6.9.0" @@ -458,23 +455,23 @@ } }, "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==", + "version": "7.29.7", + "resolved": "https://registry.npmjs.org/@babel/helper-skip-transparent-expression-wrappers/-/helper-skip-transparent-expression-wrappers-7.29.7.tgz", + "integrity": "sha512-brcMGQaVzIeUb+6/bs1Av0f8YuNNjKY2JyvfRCsFuFsdKccEQ5Ges2y74D74NZ1Rz8lKJ9ksJkfqwQFJ/iNEyQ==", "dev": true, "license": "MIT", "dependencies": { - "@babel/traverse": "^7.27.1", - "@babel/types": "^7.27.1" + "@babel/traverse": "^7.29.7", + "@babel/types": "^7.29.7" }, "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==", + "version": "7.29.7", + "resolved": "https://registry.npmjs.org/@babel/helper-string-parser/-/helper-string-parser-7.29.7.tgz", + "integrity": "sha512-Pb5ijPrZ89GDH8223L4UP8i6QApWxs04RbPQJTeWDV0/keR2E36MeKnyr6LYmUUvqRRI+Iv87SuF1W6ErINzYw==", "dev": true, "license": "MIT", "engines": { @@ -482,9 +479,9 @@ } }, "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==", + "version": "7.29.7", + "resolved": "https://registry.npmjs.org/@babel/helper-validator-identifier/-/helper-validator-identifier-7.29.7.tgz", + "integrity": "sha512-qehxGkRj55h/ff8EMaJ+cYhyaKlHIxqYDn682wQD7RNp9UujOQsHog2uS0r2vzr4pW+sXf90NeeayjcNaX3fFg==", "dev": true, "license": "MIT", "engines": { @@ -492,9 +489,9 @@ } }, "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==", + "version": "7.29.7", + "resolved": "https://registry.npmjs.org/@babel/helper-validator-option/-/helper-validator-option-7.29.7.tgz", + "integrity": "sha512-N9ZErrD+yW5geCDtBqnOoxmR8+tNKiGuxKlDpuJxfsqpa2dFcexaziGAE/qoHLiDDreVNMupxGmSoNlyvsA3gw==", "dev": true, "license": "MIT", "engines": { @@ -502,15 +499,15 @@ } }, "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==", + "version": "7.29.7", + "resolved": "https://registry.npmjs.org/@babel/helper-wrap-function/-/helper-wrap-function-7.29.7.tgz", + "integrity": "sha512-iES0Skag9ERIF68aXadpO6dbXa03mNWK3sEqJaMnLNs/eC3l0lkImdfoy6Y09/SfkpawdAB4RjQ7PVA7TcVGdw==", "dev": true, "license": "MIT", "dependencies": { - "@babel/template": "^7.27.1", - "@babel/traverse": "^7.27.1", - "@babel/types": "^7.27.1" + "@babel/template": "^7.29.7", + "@babel/traverse": "^7.29.7", + "@babel/types": "^7.29.7" }, "engines": { "node": ">=6.9.0" @@ -531,13 +528,13 @@ } }, "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==", + "version": "7.29.7", + "resolved": "https://registry.npmjs.org/@babel/parser/-/parser-7.29.7.tgz", + "integrity": "sha512-hnORnjP/1P/zFEndoeX+n+t1RwWRJiJpM/jO7FW32Kn9r5+sJB2JWOdYo4L6k78j15eCwY3Gm/7364B1EMwtNg==", "dev": true, "license": "MIT", "dependencies": { - "@babel/types": "^7.28.5" + "@babel/types": "^7.29.7" }, "bin": { "parser": "bin/babel-parser.js" @@ -547,15 +544,14 @@ } }, "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==", + "version": "7.29.7", + "resolved": "https://registry.npmjs.org/@babel/plugin-bugfix-firefox-class-in-computed-class-key/-/plugin-bugfix-firefox-class-in-computed-class-key-7.29.7.tgz", + "integrity": "sha512-j8SrR0zLZrRsC09DlszEx8FpMiwukKffYXMK0d5LmOglO7vGG6sz/BR/20yHqWH+Lnn31JTt2PE3hIWNgM2J6w==", "dev": true, "license": "MIT", - "peer": true, "dependencies": { - "@babel/helper-plugin-utils": "^7.27.1", - "@babel/traverse": "^7.28.5" + "@babel/helper-plugin-utils": "^7.29.7", + "@babel/traverse": "^7.29.7" }, "engines": { "node": ">=6.9.0" @@ -565,14 +561,13 @@ } }, "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==", + "version": "7.29.7", + "resolved": "https://registry.npmjs.org/@babel/plugin-bugfix-safari-class-field-initializer-scope/-/plugin-bugfix-safari-class-field-initializer-scope-7.29.7.tgz", + "integrity": "sha512-r8j8escF+U2FUHo0KOhPUdMzUO+jp9fInva6+ACVAF3Y97Ev+5iNZwiqTghmzNeWwDkOPlYuTcfb1vDaoZKmAQ==", "dev": true, "license": "MIT", - "peer": true, "dependencies": { - "@babel/helper-plugin-utils": "^7.27.1" + "@babel/helper-plugin-utils": "^7.29.7" }, "engines": { "node": ">=6.9.0" @@ -582,14 +577,30 @@ } }, "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==", + "version": "7.29.7", + "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.29.7.tgz", + "integrity": "sha512-GE1TFSiuFeGsCxmYXZl8HwoPrVlwe4rHPFE8weieGKZqnDORK+Ar3vgWMgW+AOxQ6/2TgLSKx9p6W7O4rC6qgQ==", "dev": true, "license": "MIT", - "peer": true, "dependencies": { - "@babel/helper-plugin-utils": "^7.27.1" + "@babel/helper-plugin-utils": "^7.29.7" + }, + "engines": { + "node": ">=6.9.0" + }, + "peerDependencies": { + "@babel/core": "^7.0.0" + } + }, + "node_modules/@babel/plugin-bugfix-safari-rest-destructuring-rhs-array": { + "version": "7.29.7", + "resolved": "https://registry.npmjs.org/@babel/plugin-bugfix-safari-rest-destructuring-rhs-array/-/plugin-bugfix-safari-rest-destructuring-rhs-array-7.29.7.tgz", + "integrity": "sha512-oBNVCvnO5tND+xSopWvV8WNGfpTfgP4Zr/YXXSj8zfmcPktp5Ku/aZlsIowgSD4fjmgHn6sGmB9APVsU5zOdhA==", + "dev": true, + "license": "MIT", + "dependencies": { + "@babel/helper-plugin-utils": "^7.29.7", + "@babel/helper-skip-transparent-expression-wrappers": "^7.29.7" }, "engines": { "node": ">=6.9.0" @@ -599,16 +610,15 @@ } }, "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==", + "version": "7.29.7", + "resolved": "https://registry.npmjs.org/@babel/plugin-bugfix-v8-spread-parameters-in-optional-chaining/-/plugin-bugfix-v8-spread-parameters-in-optional-chaining-7.29.7.tgz", + "integrity": "sha512-QQt9qKHZ2sg/kivaLr7lnQr8HVrQDdBNSfCsTjiDxRuX/K5ORyKq+Bu8Xr0cDE3Dfkv0cw28Ve0EKyKMvulkOw==", "dev": true, "license": "MIT", - "peer": true, "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" + "@babel/helper-plugin-utils": "^7.29.7", + "@babel/helper-skip-transparent-expression-wrappers": "^7.29.7", + "@babel/plugin-transform-optional-chaining": "^7.29.7" }, "engines": { "node": ">=6.9.0" @@ -618,15 +628,14 @@ } }, "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==", + "version": "7.29.7", + "resolved": "https://registry.npmjs.org/@babel/plugin-bugfix-v8-static-class-fields-redefine-readonly/-/plugin-bugfix-v8-static-class-fields-redefine-readonly-7.29.7.tgz", + "integrity": "sha512-pn6QacGLgvCcwc+syUhKE/qSjV2D1IHDB84RNxWYSt1mW3K/SCtjinZ2p0cETJxAWBjPy3K/1lHwG5BjjPxNlw==", "dev": true, "license": "MIT", - "peer": true, "dependencies": { - "@babel/helper-plugin-utils": "^7.27.1", - "@babel/traverse": "^7.28.3" + "@babel/helper-plugin-utils": "^7.29.7", + "@babel/traverse": "^7.29.7" }, "engines": { "node": ">=6.9.0" @@ -712,7 +721,6 @@ "integrity": "sha512-SOSkfJDddaM7mak6cPEpswyTRnuRltl429hMraQEglW+OkovnCzsiszTmsrlY//qLFjCpQDFRvjdm2wA5pPm9w==", "dev": true, "license": "MIT", - "peer": true, "engines": { "node": ">=6.9.0" }, @@ -821,14 +829,13 @@ } }, "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==", + "version": "7.29.7", + "resolved": "https://registry.npmjs.org/@babel/plugin-syntax-import-assertions/-/plugin-syntax-import-assertions-7.29.7.tgz", + "integrity": "sha512-/An1OCBN93thpBAGyfsK2pcf0jvju1SAtKkL2Ny++B5Sy6sqgzXDQH1cZxWbF96Wuk+bn41MDA9bLd4VVAw6rw==", "dev": true, "license": "MIT", - "peer": true, "dependencies": { - "@babel/helper-plugin-utils": "^7.27.1" + "@babel/helper-plugin-utils": "^7.29.7" }, "engines": { "node": ">=6.9.0" @@ -838,13 +845,13 @@ } }, "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==", + "version": "7.29.7", + "resolved": "https://registry.npmjs.org/@babel/plugin-syntax-import-attributes/-/plugin-syntax-import-attributes-7.29.7.tgz", + "integrity": "sha512-zGYcYfq/WmZ4V+kBIXQon9dSSc8ircGZqw9ZaNhhGj9nZkeBu1jHLBDQqYYi5WA9uawvA2sIMbry2nCFhf5Djg==", "dev": true, "license": "MIT", "dependencies": { - "@babel/helper-plugin-utils": "^7.27.1" + "@babel/helper-plugin-utils": "^7.29.7" }, "engines": { "node": ">=6.9.0" @@ -1027,7 +1034,6 @@ "integrity": "sha512-727YkEAPwSIQTv5im8QHz3upqp92JTWhidIC81Tdx4VJYIte/VndKf1qKrfnnhPLiPghStWfvC/iFaMCQu7Nqg==", "dev": true, "license": "MIT", - "peer": true, "dependencies": { "@babel/helper-create-regexp-features-plugin": "^7.18.6", "@babel/helper-plugin-utils": "^7.18.6" @@ -1040,13 +1046,13 @@ } }, "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==", + "version": "7.29.7", + "resolved": "https://registry.npmjs.org/@babel/plugin-transform-arrow-functions/-/plugin-transform-arrow-functions-7.29.7.tgz", + "integrity": "sha512-N7zArUXWzAMzm+/N0uPBeVB3Fam5lMxtUwMmDK5f/IBBS7a7p1qeUoxd/6CckXoxUdgsntq1Dh8xNW06maZbDQ==", "dev": true, "license": "MIT", "dependencies": { - "@babel/helper-plugin-utils": "^7.27.1" + "@babel/helper-plugin-utils": "^7.29.7" }, "engines": { "node": ">=6.9.0" @@ -1056,15 +1062,15 @@ } }, "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==", + "version": "7.29.7", + "resolved": "https://registry.npmjs.org/@babel/plugin-transform-async-generator-functions/-/plugin-transform-async-generator-functions-7.29.7.tgz", + "integrity": "sha512-d98gXZkgswvkyohMBABkhm3GeXhYj8psWfwQ2C7gtfrKGTykQa/iOIi+JJhwMjPlZ6Vm2XN+DCf3Es1EoG4ZLA==", "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" + "@babel/helper-plugin-utils": "^7.29.7", + "@babel/helper-remap-async-to-generator": "^7.29.7", + "@babel/traverse": "^7.29.7" }, "engines": { "node": ">=6.9.0" @@ -1074,15 +1080,15 @@ } }, "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==", + "version": "7.29.7", + "resolved": "https://registry.npmjs.org/@babel/plugin-transform-async-to-generator/-/plugin-transform-async-to-generator-7.29.7.tgz", + "integrity": "sha512-pcUb2SS+RMo9TWVBwKGI5ShtoG7R+zBsFmCKDa6fe8c+hPr3XJlZgoE5j6i8W7gDjhyvy+85vmYexanvXh3d1w==", "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" + "@babel/helper-module-imports": "^7.29.7", + "@babel/helper-plugin-utils": "^7.29.7", + "@babel/helper-remap-async-to-generator": "^7.29.7" }, "engines": { "node": ">=6.9.0" @@ -1092,14 +1098,13 @@ } }, "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==", + "version": "7.29.7", + "resolved": "https://registry.npmjs.org/@babel/plugin-transform-block-scoped-functions/-/plugin-transform-block-scoped-functions-7.29.7.tgz", + "integrity": "sha512-cUSmjh72N+rN4PrkFlN1dJwNCwjVp5d38/CQrEsFggkD10UiFlBFgdH3tv5dNsLuHY+3S8db2xCHjhZcv5WgvA==", "dev": true, "license": "MIT", - "peer": true, "dependencies": { - "@babel/helper-plugin-utils": "^7.27.1" + "@babel/helper-plugin-utils": "^7.29.7" }, "engines": { "node": ">=6.9.0" @@ -1109,13 +1114,13 @@ } }, "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==", + "version": "7.29.7", + "resolved": "https://registry.npmjs.org/@babel/plugin-transform-block-scoping/-/plugin-transform-block-scoping-7.29.7.tgz", + "integrity": "sha512-ONyr4+AZhKh8yKWInVxU9AXA9EbsyeLcL6V0dJy6M2/62vuvpGm29zzuymbTpdc451GEpDIdAyPLP3r+P61yKQ==", "dev": true, "license": "MIT", "dependencies": { - "@babel/helper-plugin-utils": "^7.27.1" + "@babel/helper-plugin-utils": "^7.29.7" }, "engines": { "node": ">=6.9.0" @@ -1125,14 +1130,14 @@ } }, "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==", + "version": "7.29.7", + "resolved": "https://registry.npmjs.org/@babel/plugin-transform-class-properties/-/plugin-transform-class-properties-7.29.7.tgz", + "integrity": "sha512-GtcpjFvanPfzNQi3eTitsCqtRRmmqzpy/A+yhTR1HaZo1Ly3EA8ZXxlPyHdR8/IuRMYc3E4wdGBewB2QKQjAaA==", "dev": true, "license": "MIT", "dependencies": { - "@babel/helper-create-class-features-plugin": "^7.27.1", - "@babel/helper-plugin-utils": "^7.27.1" + "@babel/helper-create-class-features-plugin": "^7.29.7", + "@babel/helper-plugin-utils": "^7.29.7" }, "engines": { "node": ">=6.9.0" @@ -1142,15 +1147,14 @@ } }, "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==", + "version": "7.29.7", + "resolved": "https://registry.npmjs.org/@babel/plugin-transform-class-static-block/-/plugin-transform-class-static-block-7.29.7.tgz", + "integrity": "sha512-kibJgmEdX2iMwsHY2tSZNDgj8PwIlCQz7FK9KuGKO8zsuoUwSEhoNnNVp/emKWrbY4HeO6kkXfdMqRKKKXBm2A==", "dev": true, "license": "MIT", - "peer": true, "dependencies": { - "@babel/helper-create-class-features-plugin": "^7.28.3", - "@babel/helper-plugin-utils": "^7.27.1" + "@babel/helper-create-class-features-plugin": "^7.29.7", + "@babel/helper-plugin-utils": "^7.29.7" }, "engines": { "node": ">=6.9.0" @@ -1160,18 +1164,18 @@ } }, "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==", + "version": "7.29.7", + "resolved": "https://registry.npmjs.org/@babel/plugin-transform-classes/-/plugin-transform-classes-7.29.7.tgz", + "integrity": "sha512-qV0OGGBVacduzQHE649JyCneOFI/maT+YKsO+K4Yi3xv2wTPNjM/W2o2gdzMwEAZz7fXNTHAe0NcSg30bIN69g==", "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" + "@babel/helper-annotate-as-pure": "^7.29.7", + "@babel/helper-compilation-targets": "^7.29.7", + "@babel/helper-globals": "^7.29.7", + "@babel/helper-plugin-utils": "^7.29.7", + "@babel/helper-replace-supers": "^7.29.7", + "@babel/traverse": "^7.29.7" }, "engines": { "node": ">=6.9.0" @@ -1181,14 +1185,14 @@ } }, "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==", + "version": "7.29.7", + "resolved": "https://registry.npmjs.org/@babel/plugin-transform-computed-properties/-/plugin-transform-computed-properties-7.29.7.tgz", + "integrity": "sha512-RK7/IyU5phpuCdBAuig5VkzG/EnbDaui5SQGdU9BFrHdV+mV4cUjLMQ9lJDjLNtWHsqtiefpGZUXQP2BiTYMsA==", "dev": true, "license": "MIT", "dependencies": { - "@babel/helper-plugin-utils": "^7.27.1", - "@babel/template": "^7.27.1" + "@babel/helper-plugin-utils": "^7.29.7", + "@babel/template": "^7.29.7" }, "engines": { "node": ">=6.9.0" @@ -1198,14 +1202,14 @@ } }, "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==", + "version": "7.29.7", + "resolved": "https://registry.npmjs.org/@babel/plugin-transform-destructuring/-/plugin-transform-destructuring-7.29.7.tgz", + "integrity": "sha512-iPX8aD6H9zV5s7ZsqTdNocPN/MGQ5sSMnElKrktxjJRMnB2jN/1p2+R7GkfD6CAYoVFqy5A4XnSIUeGgJzIWpg==", "dev": true, "license": "MIT", "dependencies": { - "@babel/helper-plugin-utils": "^7.27.1", - "@babel/traverse": "^7.28.5" + "@babel/helper-plugin-utils": "^7.29.7", + "@babel/traverse": "^7.29.7" }, "engines": { "node": ">=6.9.0" @@ -1215,15 +1219,14 @@ } }, "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==", + "version": "7.29.7", + "resolved": "https://registry.npmjs.org/@babel/plugin-transform-dotall-regex/-/plugin-transform-dotall-regex-7.29.7.tgz", + "integrity": "sha512-3qc18hsD2RdZiyJNDNc7HQpv6xbncwh8FYtxNFFzclSyh/trPD9KkVR9BDECUjDLvb7yJVF15GfYUuC+LMkkiQ==", "dev": true, "license": "MIT", - "peer": true, "dependencies": { - "@babel/helper-create-regexp-features-plugin": "^7.27.1", - "@babel/helper-plugin-utils": "^7.27.1" + "@babel/helper-create-regexp-features-plugin": "^7.29.7", + "@babel/helper-plugin-utils": "^7.29.7" }, "engines": { "node": ">=6.9.0" @@ -1233,14 +1236,13 @@ } }, "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==", + "version": "7.29.7", + "resolved": "https://registry.npmjs.org/@babel/plugin-transform-duplicate-keys/-/plugin-transform-duplicate-keys-7.29.7.tgz", + "integrity": "sha512-6IvRRriEMqnBwD6chtxdLpMYCHWEzN+oL5cyQtjykya19UgzbmKhxmhZgKC/LHxS2nYr9Q/qYPZ5Lr6jOL9+yQ==", "dev": true, "license": "MIT", - "peer": true, "dependencies": { - "@babel/helper-plugin-utils": "^7.27.1" + "@babel/helper-plugin-utils": "^7.29.7" }, "engines": { "node": ">=6.9.0" @@ -1250,15 +1252,14 @@ } }, "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==", + "version": "7.29.7", + "resolved": "https://registry.npmjs.org/@babel/plugin-transform-duplicate-named-capturing-groups-regex/-/plugin-transform-duplicate-named-capturing-groups-regex-7.29.7.tgz", + "integrity": "sha512-2wiIyo2BjtgU7HufSeDnL9L2O7zr8jmhFKuSr65VpRkUiRKRNpb0mdlk56+XPPKoIrfHqzbMuglDvZun0RISsA==", "dev": true, "license": "MIT", - "peer": true, "dependencies": { - "@babel/helper-create-regexp-features-plugin": "^7.27.1", - "@babel/helper-plugin-utils": "^7.27.1" + "@babel/helper-create-regexp-features-plugin": "^7.29.7", + "@babel/helper-plugin-utils": "^7.29.7" }, "engines": { "node": ">=6.9.0" @@ -1268,14 +1269,13 @@ } }, "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==", + "version": "7.29.7", + "resolved": "https://registry.npmjs.org/@babel/plugin-transform-dynamic-import/-/plugin-transform-dynamic-import-7.29.7.tgz", + "integrity": "sha512-giOlEm/EFjfjr+te9NsdjkUo2v4f8rS/SXPumRVHAtbNcyNlvtREkU1dZzaIDclNpnaVhlCqRdFKhJBjBikzLg==", "dev": true, "license": "MIT", - "peer": true, "dependencies": { - "@babel/helper-plugin-utils": "^7.27.1" + "@babel/helper-plugin-utils": "^7.29.7" }, "engines": { "node": ">=6.9.0" @@ -1285,15 +1285,14 @@ } }, "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==", + "version": "7.29.7", + "resolved": "https://registry.npmjs.org/@babel/plugin-transform-explicit-resource-management/-/plugin-transform-explicit-resource-management-7.29.7.tgz", + "integrity": "sha512-Rstj7coNz8sE+7Ju7ihpHLI564lsK5pUpNNlvptCIC/16E/S5hbl6n3kESPKdNRmqEWlpn5xpS5Q2dvXBsySLw==", "dev": true, "license": "MIT", - "peer": true, "dependencies": { - "@babel/helper-plugin-utils": "^7.27.1", - "@babel/plugin-transform-destructuring": "^7.28.0" + "@babel/helper-plugin-utils": "^7.29.7", + "@babel/plugin-transform-destructuring": "^7.29.7" }, "engines": { "node": ">=6.9.0" @@ -1303,14 +1302,13 @@ } }, "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==", + "version": "7.29.7", + "resolved": "https://registry.npmjs.org/@babel/plugin-transform-exponentiation-operator/-/plugin-transform-exponentiation-operator-7.29.7.tgz", + "integrity": "sha512-zFpMOTLZBdW5LfObqcSbL6kefg4R4eLdmvS0wbN9M6D5Mym/sKm9toOoWyVOa+xDjvCnuWcHls2YonXwHvH3CQ==", "dev": true, "license": "MIT", - "peer": true, "dependencies": { - "@babel/helper-plugin-utils": "^7.27.1" + "@babel/helper-plugin-utils": "^7.29.7" }, "engines": { "node": ">=6.9.0" @@ -1320,14 +1318,13 @@ } }, "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==", + "version": "7.29.7", + "resolved": "https://registry.npmjs.org/@babel/plugin-transform-export-namespace-from/-/plugin-transform-export-namespace-from-7.29.7.tgz", + "integrity": "sha512-24B2nOy2TeJSMheqwPD4DDQOV/elLSIlKxjZt4i05H5AgdPdWR3n18HnNrcJ+j76WJd9gbwb9jPjNYUy6RautA==", "dev": true, "license": "MIT", - "peer": true, "dependencies": { - "@babel/helper-plugin-utils": "^7.27.1" + "@babel/helper-plugin-utils": "^7.29.7" }, "engines": { "node": ">=6.9.0" @@ -1354,14 +1351,14 @@ } }, "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==", + "version": "7.29.7", + "resolved": "https://registry.npmjs.org/@babel/plugin-transform-for-of/-/plugin-transform-for-of-7.29.7.tgz", + "integrity": "sha512-zeSIHh0+E1Um1WJRXCFlHQYu2ieJNdivLLjlBEp+dIBu3S51n+SZZmIXjxnItw6pz56Cn+KvK68BIBVsxq2JiQ==", "dev": true, "license": "MIT", "dependencies": { - "@babel/helper-plugin-utils": "^7.27.1", - "@babel/helper-skip-transparent-expression-wrappers": "^7.27.1" + "@babel/helper-plugin-utils": "^7.29.7", + "@babel/helper-skip-transparent-expression-wrappers": "^7.29.7" }, "engines": { "node": ">=6.9.0" @@ -1371,15 +1368,15 @@ } }, "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==", + "version": "7.29.7", + "resolved": "https://registry.npmjs.org/@babel/plugin-transform-function-name/-/plugin-transform-function-name-7.29.7.tgz", + "integrity": "sha512-otRWaHXE6fbAGkePvaj/kvs3HsqXfPhlnzwSOlnFgbqCPMd975dW+4wZ00WFBt+/YlBGcJwNrARQTOJOb4ZrIg==", "dev": true, "license": "MIT", "dependencies": { - "@babel/helper-compilation-targets": "^7.27.1", - "@babel/helper-plugin-utils": "^7.27.1", - "@babel/traverse": "^7.27.1" + "@babel/helper-compilation-targets": "^7.29.7", + "@babel/helper-plugin-utils": "^7.29.7", + "@babel/traverse": "^7.29.7" }, "engines": { "node": ">=6.9.0" @@ -1389,14 +1386,13 @@ } }, "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==", + "version": "7.29.7", + "resolved": "https://registry.npmjs.org/@babel/plugin-transform-json-strings/-/plugin-transform-json-strings-7.29.7.tgz", + "integrity": "sha512-RRnE2+eon1rJAq8MnoF1b5kTpY1vU88twHcvcKMrsqP/jxIRqDVs9iJB5fqPuqyeFAW0wJo4MlUIPpQCq/aRsg==", "dev": true, "license": "MIT", - "peer": true, "dependencies": { - "@babel/helper-plugin-utils": "^7.27.1" + "@babel/helper-plugin-utils": "^7.29.7" }, "engines": { "node": ">=6.9.0" @@ -1406,13 +1402,13 @@ } }, "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==", + "version": "7.29.7", + "resolved": "https://registry.npmjs.org/@babel/plugin-transform-literals/-/plugin-transform-literals-7.29.7.tgz", + "integrity": "sha512-DZ/oLP21ZuWx1vKqnoNv6/tvEK48AQOBRai40CX9dTjGluvT/YZCyY3rryDtyUqCEoyNroy5KKPwX2iQCiRvyw==", "dev": true, "license": "MIT", "dependencies": { - "@babel/helper-plugin-utils": "^7.27.1" + "@babel/helper-plugin-utils": "^7.29.7" }, "engines": { "node": ">=6.9.0" @@ -1422,13 +1418,13 @@ } }, "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==", + "version": "7.29.7", + "resolved": "https://registry.npmjs.org/@babel/plugin-transform-logical-assignment-operators/-/plugin-transform-logical-assignment-operators-7.29.7.tgz", + "integrity": "sha512-A0H91hh6W8MFRkp5TqJmMr39jzGD1A1E1Ysiv2O06Sfbhkapm+XyIzxWCEh5kqwOZ1/8QZ0dY3SeQ7XBqfJd5Q==", "dev": true, "license": "MIT", "dependencies": { - "@babel/helper-plugin-utils": "^7.27.1" + "@babel/helper-plugin-utils": "^7.29.7" }, "engines": { "node": ">=6.9.0" @@ -1438,14 +1434,13 @@ } }, "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==", + "version": "7.29.7", + "resolved": "https://registry.npmjs.org/@babel/plugin-transform-member-expression-literals/-/plugin-transform-member-expression-literals-7.29.7.tgz", + "integrity": "sha512-hl1kwFZCCiDyfH25Xmco9jTrkPgnS9pmOzSG7W5I4SaGbLeqKv417hcU2RKmaxoPEgsoJh7ZPOrnPGq99bHoUg==", "dev": true, "license": "MIT", - "peer": true, "dependencies": { - "@babel/helper-plugin-utils": "^7.27.1" + "@babel/helper-plugin-utils": "^7.29.7" }, "engines": { "node": ">=6.9.0" @@ -1455,15 +1450,14 @@ } }, "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==", + "version": "7.29.7", + "resolved": "https://registry.npmjs.org/@babel/plugin-transform-modules-amd/-/plugin-transform-modules-amd-7.29.7.tgz", + "integrity": "sha512-fxtQoH3m5ywUSIfaH0FGCzWu4McsYon5bD3K4XnskC7f+OyQMj7rsOMi4NvvmJ83WwBAg4UCe+ov4VZlqEvyew==", "dev": true, "license": "MIT", - "peer": true, "dependencies": { - "@babel/helper-module-transforms": "^7.27.1", - "@babel/helper-plugin-utils": "^7.27.1" + "@babel/helper-module-transforms": "^7.29.7", + "@babel/helper-plugin-utils": "^7.29.7" }, "engines": { "node": ">=6.9.0" @@ -1473,14 +1467,14 @@ } }, "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==", + "version": "7.29.7", + "resolved": "https://registry.npmjs.org/@babel/plugin-transform-modules-commonjs/-/plugin-transform-modules-commonjs-7.29.7.tgz", + "integrity": "sha512-j0vCldybPC5b5dwCQOJ21uKtHzt7hxLygJTg9eF1ScfaikEDNfzn94XoW5Fi+seBR0nCyL23xaBFFkq7dTM8XQ==", "dev": true, "license": "MIT", "dependencies": { - "@babel/helper-module-transforms": "^7.27.1", - "@babel/helper-plugin-utils": "^7.27.1" + "@babel/helper-module-transforms": "^7.29.7", + "@babel/helper-plugin-utils": "^7.29.7" }, "engines": { "node": ">=6.9.0" @@ -1490,17 +1484,16 @@ } }, "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==", + "version": "7.29.7", + "resolved": "https://registry.npmjs.org/@babel/plugin-transform-modules-systemjs/-/plugin-transform-modules-systemjs-7.29.7.tgz", + "integrity": "sha512-TM2ZcQLoG2/y4HODiStCo10DibYhWhGWAwVv+EQKmG/7GFl0N+AAmUiXOMKM+aiJ9XBJ9AHVZBvTzMnJ2sM3cQ==", "dev": true, "license": "MIT", - "peer": true, "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" + "@babel/helper-module-transforms": "^7.29.7", + "@babel/helper-plugin-utils": "^7.29.7", + "@babel/helper-validator-identifier": "^7.29.7", + "@babel/traverse": "^7.29.7" }, "engines": { "node": ">=6.9.0" @@ -1510,15 +1503,14 @@ } }, "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==", + "version": "7.29.7", + "resolved": "https://registry.npmjs.org/@babel/plugin-transform-modules-umd/-/plugin-transform-modules-umd-7.29.7.tgz", + "integrity": "sha512-B4UkaTK3QpgCwJnrxKfMPKdo92CN7OKXAlpAAnM3UPu0Q0lCCk57ylA9AJbRy2v8dDKOPAAWcoR6CMyeoHwRCA==", "dev": true, "license": "MIT", - "peer": true, "dependencies": { - "@babel/helper-module-transforms": "^7.27.1", - "@babel/helper-plugin-utils": "^7.27.1" + "@babel/helper-module-transforms": "^7.29.7", + "@babel/helper-plugin-utils": "^7.29.7" }, "engines": { "node": ">=6.9.0" @@ -1528,14 +1520,14 @@ } }, "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==", + "version": "7.29.7", + "resolved": "https://registry.npmjs.org/@babel/plugin-transform-named-capturing-groups-regex/-/plugin-transform-named-capturing-groups-regex-7.29.7.tgz", + "integrity": "sha512-vuFoLwr4qnv2xbZ16SQd6uPcH5FNrLHhk/Jzo++0XJFcaDsr4gjJVg6j398oMHiC+83k/GiBzviwF5KBJkPUtQ==", "dev": true, "license": "MIT", "dependencies": { - "@babel/helper-create-regexp-features-plugin": "^7.27.1", - "@babel/helper-plugin-utils": "^7.27.1" + "@babel/helper-create-regexp-features-plugin": "^7.29.7", + "@babel/helper-plugin-utils": "^7.29.7" }, "engines": { "node": ">=6.9.0" @@ -1545,14 +1537,13 @@ } }, "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==", + "version": "7.29.7", + "resolved": "https://registry.npmjs.org/@babel/plugin-transform-new-target/-/plugin-transform-new-target-7.29.7.tgz", + "integrity": "sha512-fEo41GmsOUhOBlw8ioo6zvjX5Xc2Lqkzlyfqbpsk3eB6TReV18uhxZ0esfEokVbY2+PVJAQHNKxER6lGrzNd3A==", "dev": true, "license": "MIT", - "peer": true, "dependencies": { - "@babel/helper-plugin-utils": "^7.27.1" + "@babel/helper-plugin-utils": "^7.29.7" }, "engines": { "node": ">=6.9.0" @@ -1562,13 +1553,13 @@ } }, "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==", + "version": "7.29.7", + "resolved": "https://registry.npmjs.org/@babel/plugin-transform-nullish-coalescing-operator/-/plugin-transform-nullish-coalescing-operator-7.29.7.tgz", + "integrity": "sha512-idmp1dFaekP9GbcMvG24Kvw2BfhFZjHnNJCkV4WuIY4PskJzwI3f1N5OdgYke38T7rftO6ERulFRn2cFeZwRkg==", "dev": true, "license": "MIT", "dependencies": { - "@babel/helper-plugin-utils": "^7.27.1" + "@babel/helper-plugin-utils": "^7.29.7" }, "engines": { "node": ">=6.9.0" @@ -1578,13 +1569,13 @@ } }, "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==", + "version": "7.29.7", + "resolved": "https://registry.npmjs.org/@babel/plugin-transform-numeric-separator/-/plugin-transform-numeric-separator-7.29.7.tgz", + "integrity": "sha512-zR7fv/z14OjgHl4AgRtkDBvBMhIzCxqV/qN/2BCRC7LjFwvuzjYe7gDWxC4Wl/SNsLM6SE1IWvRPYMgSJaUvNw==", "dev": true, "license": "MIT", "dependencies": { - "@babel/helper-plugin-utils": "^7.27.1" + "@babel/helper-plugin-utils": "^7.29.7" }, "engines": { "node": ">=6.9.0" @@ -1594,17 +1585,17 @@ } }, "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==", + "version": "7.29.7", + "resolved": "https://registry.npmjs.org/@babel/plugin-transform-object-rest-spread/-/plugin-transform-object-rest-spread-7.29.7.tgz", + "integrity": "sha512-Ld98jn4c0smUywL57m7SgsHq3OpThOa6LqZJif3G6jYOovPleoFhVrBJ1WegRApSFB2wu4+RelAj9AC9G08Z4A==", "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" + "@babel/helper-compilation-targets": "^7.29.7", + "@babel/helper-plugin-utils": "^7.29.7", + "@babel/plugin-transform-destructuring": "^7.29.7", + "@babel/plugin-transform-parameters": "^7.29.7", + "@babel/traverse": "^7.29.7" }, "engines": { "node": ">=6.9.0" @@ -1614,15 +1605,14 @@ } }, "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==", + "version": "7.29.7", + "resolved": "https://registry.npmjs.org/@babel/plugin-transform-object-super/-/plugin-transform-object-super-7.29.7.tgz", + "integrity": "sha512-Ea/diGcw0twB5IlZPO5sgET6fJsLJqPABqTuFWIR+iMPGPZJkATEIWx0wa+aEQ5UY1CBQyP/gkAiLEqn1vBiQA==", "dev": true, "license": "MIT", - "peer": true, "dependencies": { - "@babel/helper-plugin-utils": "^7.27.1", - "@babel/helper-replace-supers": "^7.27.1" + "@babel/helper-plugin-utils": "^7.29.7", + "@babel/helper-replace-supers": "^7.29.7" }, "engines": { "node": ">=6.9.0" @@ -1632,13 +1622,13 @@ } }, "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==", + "version": "7.29.7", + "resolved": "https://registry.npmjs.org/@babel/plugin-transform-optional-catch-binding/-/plugin-transform-optional-catch-binding-7.29.7.tgz", + "integrity": "sha512-sLsyndxK2VwX6yNUOakMb7Sh553ZTe/vVM1XJ+9Z5aW1ytsc8xOIwmyk05NNjN60vkc5/KqoTH6hB4V41LJhng==", "dev": true, "license": "MIT", "dependencies": { - "@babel/helper-plugin-utils": "^7.27.1" + "@babel/helper-plugin-utils": "^7.29.7" }, "engines": { "node": ">=6.9.0" @@ -1648,14 +1638,14 @@ } }, "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==", + "version": "7.29.7", + "resolved": "https://registry.npmjs.org/@babel/plugin-transform-optional-chaining/-/plugin-transform-optional-chaining-7.29.7.tgz", + "integrity": "sha512-6GM1dhvK3gNODkXcEcMCOLEDCLSoZ/sBbro2Ax8HURyasQ4NshagQixkRFdh5niI6E4gmA/jYI/4aT7rRos3ZQ==", "dev": true, "license": "MIT", "dependencies": { - "@babel/helper-plugin-utils": "^7.27.1", - "@babel/helper-skip-transparent-expression-wrappers": "^7.27.1" + "@babel/helper-plugin-utils": "^7.29.7", + "@babel/helper-skip-transparent-expression-wrappers": "^7.29.7" }, "engines": { "node": ">=6.9.0" @@ -1665,13 +1655,13 @@ } }, "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==", + "version": "7.29.7", + "resolved": "https://registry.npmjs.org/@babel/plugin-transform-parameters/-/plugin-transform-parameters-7.29.7.tgz", + "integrity": "sha512-ZDOBqV/qLYJI0YElr8DcENEyARsFQeESqWXH6gZlghYXuPPjvweuDhP4VyEi4BlUBlLRFZVjxoZDMjxhLW766g==", "dev": true, "license": "MIT", "dependencies": { - "@babel/helper-plugin-utils": "^7.27.1" + "@babel/helper-plugin-utils": "^7.29.7" }, "engines": { "node": ">=6.9.0" @@ -1681,14 +1671,14 @@ } }, "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==", + "version": "7.29.7", + "resolved": "https://registry.npmjs.org/@babel/plugin-transform-private-methods/-/plugin-transform-private-methods-7.29.7.tgz", + "integrity": "sha512-/6Rz4DK1ETDEM/bWHsPHcaEe7ZaT1EqSXjtSP/L0DijOYuaUhiRiOKcwpZ8P7zR4xXEHc2ITdiCgBm9Tpyv9ug==", "dev": true, "license": "MIT", "dependencies": { - "@babel/helper-create-class-features-plugin": "^7.27.1", - "@babel/helper-plugin-utils": "^7.27.1" + "@babel/helper-create-class-features-plugin": "^7.29.7", + "@babel/helper-plugin-utils": "^7.29.7" }, "engines": { "node": ">=6.9.0" @@ -1698,15 +1688,15 @@ } }, "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==", + "version": "7.29.7", + "resolved": "https://registry.npmjs.org/@babel/plugin-transform-private-property-in-object/-/plugin-transform-private-property-in-object-7.29.7.tgz", + "integrity": "sha512-+BNo06dnrzdNNqCm1X6YUaVv0DKk8Q+JYcoZfOkLhYWNCXzlwTSRq8zGWayT1csjcpNXV9CQTBRRbmTLZac5cA==", "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" + "@babel/helper-annotate-as-pure": "^7.29.7", + "@babel/helper-create-class-features-plugin": "^7.29.7", + "@babel/helper-plugin-utils": "^7.29.7" }, "engines": { "node": ">=6.9.0" @@ -1716,14 +1706,13 @@ } }, "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==", + "version": "7.29.7", + "resolved": "https://registry.npmjs.org/@babel/plugin-transform-property-literals/-/plugin-transform-property-literals-7.29.7.tgz", + "integrity": "sha512-bOMRLQuI0A5ZqHq3OWJ89/rXpJ/NJrbVhXiP4zwPGMs6kpcVsuTUNjwoE30K0Qm3mf48a/TnRYYD6vPNqcg6jA==", "dev": true, "license": "MIT", - "peer": true, "dependencies": { - "@babel/helper-plugin-utils": "^7.27.1" + "@babel/helper-plugin-utils": "^7.29.7" }, "engines": { "node": ">=6.9.0" @@ -1801,13 +1790,13 @@ } }, "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==", + "version": "7.29.7", + "resolved": "https://registry.npmjs.org/@babel/plugin-transform-regenerator/-/plugin-transform-regenerator-7.29.7.tgz", + "integrity": "sha512-rNNFV0DBAJp988xW2DOntfDoYn1eR8GGF5AT5vYc+rjyfaQkM242c9tZUHHPe7KYaiJizXPWhQTzzdbXySyhBw==", "dev": true, "license": "MIT", "dependencies": { - "@babel/helper-plugin-utils": "^7.27.1" + "@babel/helper-plugin-utils": "^7.29.7" }, "engines": { "node": ">=6.9.0" @@ -1817,15 +1806,14 @@ } }, "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==", + "version": "7.29.7", + "resolved": "https://registry.npmjs.org/@babel/plugin-transform-regexp-modifiers/-/plugin-transform-regexp-modifiers-7.29.7.tgz", + "integrity": "sha512-mB5Fs0VWrJ42ZCmc8114v60qetdaUVNkj9PmSZRmanCZM3S9hm0CFRLjRmYIsuXav14l2jvZ+4T8iiCGnhj3nQ==", "dev": true, "license": "MIT", - "peer": true, "dependencies": { - "@babel/helper-create-regexp-features-plugin": "^7.27.1", - "@babel/helper-plugin-utils": "^7.27.1" + "@babel/helper-create-regexp-features-plugin": "^7.29.7", + "@babel/helper-plugin-utils": "^7.29.7" }, "engines": { "node": ">=6.9.0" @@ -1835,14 +1823,13 @@ } }, "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==", + "version": "7.29.7", + "resolved": "https://registry.npmjs.org/@babel/plugin-transform-reserved-words/-/plugin-transform-reserved-words-7.29.7.tgz", + "integrity": "sha512-5+YhdpVgmfSmwZyLMftfaiffLRMHjzIRHFHHLdibcSyJm2pasMrKHrO3Ptrt2DRshjvpgjEJJ1zVW14WPq/6QA==", "dev": true, "license": "MIT", - "peer": true, "dependencies": { - "@babel/helper-plugin-utils": "^7.27.1" + "@babel/helper-plugin-utils": "^7.29.7" }, "engines": { "node": ">=6.9.0" @@ -1873,13 +1860,13 @@ } }, "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==", + "version": "7.29.7", + "resolved": "https://registry.npmjs.org/@babel/plugin-transform-shorthand-properties/-/plugin-transform-shorthand-properties-7.29.7.tgz", + "integrity": "sha512-I+WYbGBAiCn7nA6xBrlgPH+MB7HWb4u8pv5S0Pv7OtwNvIFvCCb24YlttKEeUFVurfBCEaOTnuhlqsb7f0Z5Dg==", "dev": true, "license": "MIT", "dependencies": { - "@babel/helper-plugin-utils": "^7.27.1" + "@babel/helper-plugin-utils": "^7.29.7" }, "engines": { "node": ">=6.9.0" @@ -1889,14 +1876,14 @@ } }, "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==", + "version": "7.29.7", + "resolved": "https://registry.npmjs.org/@babel/plugin-transform-spread/-/plugin-transform-spread-7.29.7.tgz", + "integrity": "sha512-/u5K1QWada7tbYNqTjMh96718g9NTwh9tfPJMsSmVsQwGT447FskV+KcfeXkXq2GWki4EM/MuTdmBec+hOuVTQ==", "dev": true, "license": "MIT", "dependencies": { - "@babel/helper-plugin-utils": "^7.27.1", - "@babel/helper-skip-transparent-expression-wrappers": "^7.27.1" + "@babel/helper-plugin-utils": "^7.29.7", + "@babel/helper-skip-transparent-expression-wrappers": "^7.29.7" }, "engines": { "node": ">=6.9.0" @@ -1906,13 +1893,13 @@ } }, "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==", + "version": "7.29.7", + "resolved": "https://registry.npmjs.org/@babel/plugin-transform-sticky-regex/-/plugin-transform-sticky-regex-7.29.7.tgz", + "integrity": "sha512-BCHzNYJGe9l7EpwwDBN/ztlL2NYFFq8hp9ddjtUEM9f2O7S7kKV/lL6Fwo7IF7NSkYhPK2vO+86nIGltA90MsA==", "dev": true, "license": "MIT", "dependencies": { - "@babel/helper-plugin-utils": "^7.27.1" + "@babel/helper-plugin-utils": "^7.29.7" }, "engines": { "node": ">=6.9.0" @@ -1922,14 +1909,13 @@ } }, "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==", + "version": "7.29.7", + "resolved": "https://registry.npmjs.org/@babel/plugin-transform-template-literals/-/plugin-transform-template-literals-7.29.7.tgz", + "integrity": "sha512-NCSEJ4sLFU2gqAub45HYh4fus2yQ36rr6ei6vpU7NdoJqCpxvEG8E6eJpscGyXP3VHD2Ny+fSXr04k1hoUrFqA==", "dev": true, "license": "MIT", - "peer": true, "dependencies": { - "@babel/helper-plugin-utils": "^7.27.1" + "@babel/helper-plugin-utils": "^7.29.7" }, "engines": { "node": ">=6.9.0" @@ -1939,14 +1925,13 @@ } }, "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==", + "version": "7.29.7", + "resolved": "https://registry.npmjs.org/@babel/plugin-transform-typeof-symbol/-/plugin-transform-typeof-symbol-7.29.7.tgz", + "integrity": "sha512-223mNGoTkBiTEWFoK+Q6Go3tueMRclO8vxxxxquNCYuNI4jWOofFKJRRDu6SDrB8Sgo1UEGW9T4GAQ8ZyRso1A==", "dev": true, "license": "MIT", - "peer": true, "dependencies": { - "@babel/helper-plugin-utils": "^7.27.1" + "@babel/helper-plugin-utils": "^7.29.7" }, "engines": { "node": ">=6.9.0" @@ -1976,14 +1961,13 @@ } }, "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==", + "version": "7.29.7", + "resolved": "https://registry.npmjs.org/@babel/plugin-transform-unicode-escapes/-/plugin-transform-unicode-escapes-7.29.7.tgz", + "integrity": "sha512-jCfXxSjf94lf4E0hKE0AByxF6F3/pVFqRdUUNkDJhsY0m1ZKjnN6ZYyMeHNpzflxb/0q5b7t3p+BE+SLF1WOtA==", "dev": true, "license": "MIT", - "peer": true, "dependencies": { - "@babel/helper-plugin-utils": "^7.27.1" + "@babel/helper-plugin-utils": "^7.29.7" }, "engines": { "node": ">=6.9.0" @@ -1993,15 +1977,14 @@ } }, "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==", + "version": "7.29.7", + "resolved": "https://registry.npmjs.org/@babel/plugin-transform-unicode-property-regex/-/plugin-transform-unicode-property-regex-7.29.7.tgz", + "integrity": "sha512-OgZ+zoAJgZLUCunsTRQ5LAjOywDv5zzZ2/hQ5aMw1pGXyY2rtE8/chXYUmu3AlVHKpm10KEdG9aMwbI/K76ZGw==", "dev": true, "license": "MIT", - "peer": true, "dependencies": { - "@babel/helper-create-regexp-features-plugin": "^7.27.1", - "@babel/helper-plugin-utils": "^7.27.1" + "@babel/helper-create-regexp-features-plugin": "^7.29.7", + "@babel/helper-plugin-utils": "^7.29.7" }, "engines": { "node": ">=6.9.0" @@ -2011,14 +1994,14 @@ } }, "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==", + "version": "7.29.7", + "resolved": "https://registry.npmjs.org/@babel/plugin-transform-unicode-regex/-/plugin-transform-unicode-regex-7.29.7.tgz", + "integrity": "sha512-7D/x/23/d/3VqZ0QA+LGbZMlGwZjztBygSWWWsfTPoQ1oQ6Q1P6Mr3d0kk42XabyUVw+fha3LqdRsFqeKqvCyA==", "dev": true, "license": "MIT", "dependencies": { - "@babel/helper-create-regexp-features-plugin": "^7.27.1", - "@babel/helper-plugin-utils": "^7.27.1" + "@babel/helper-create-regexp-features-plugin": "^7.29.7", + "@babel/helper-plugin-utils": "^7.29.7" }, "engines": { "node": ">=6.9.0" @@ -2028,15 +2011,14 @@ } }, "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==", + "version": "7.29.7", + "resolved": "https://registry.npmjs.org/@babel/plugin-transform-unicode-sets-regex/-/plugin-transform-unicode-sets-regex-7.29.7.tgz", + "integrity": "sha512-BLOhLht9DOJwIxlmp91wHvkXv1lguuHS3/FwUO8HL1H0u8s4hR1gASVFyilu9iGtcTRYqjTZmlsFFeQletntEg==", "dev": true, "license": "MIT", - "peer": true, "dependencies": { - "@babel/helper-create-regexp-features-plugin": "^7.27.1", - "@babel/helper-plugin-utils": "^7.27.1" + "@babel/helper-create-regexp-features-plugin": "^7.29.7", + "@babel/helper-plugin-utils": "^7.29.7" }, "engines": { "node": ">=6.9.0" @@ -2046,82 +2028,82 @@ } }, "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", - "peer": true, - "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", + "version": "7.29.7", + "resolved": "https://registry.npmjs.org/@babel/preset-env/-/preset-env-7.29.7.tgz", + "integrity": "sha512-GYzX36n1nsciIb0uyH0GHwxwtNwPQIcpxSeiVLDtG/B7jB5xXgchnmL1f/jCX5o+pwnaDBtO60ONSJhEBJfxYA==", + "dev": true, + "license": "MIT", + "dependencies": { + "@babel/compat-data": "^7.29.7", + "@babel/helper-compilation-targets": "^7.29.7", + "@babel/helper-plugin-utils": "^7.29.7", + "@babel/helper-validator-option": "^7.29.7", + "@babel/plugin-bugfix-firefox-class-in-computed-class-key": "^7.29.7", + "@babel/plugin-bugfix-safari-class-field-initializer-scope": "^7.29.7", + "@babel/plugin-bugfix-safari-id-destructuring-collision-in-function-expression": "^7.29.7", + "@babel/plugin-bugfix-safari-rest-destructuring-rhs-array": "^7.29.7", + "@babel/plugin-bugfix-v8-spread-parameters-in-optional-chaining": "^7.29.7", + "@babel/plugin-bugfix-v8-static-class-fields-redefine-readonly": "^7.29.7", "@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-import-assertions": "^7.29.7", + "@babel/plugin-syntax-import-attributes": "^7.29.7", "@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/plugin-transform-arrow-functions": "^7.29.7", + "@babel/plugin-transform-async-generator-functions": "^7.29.7", + "@babel/plugin-transform-async-to-generator": "^7.29.7", + "@babel/plugin-transform-block-scoped-functions": "^7.29.7", + "@babel/plugin-transform-block-scoping": "^7.29.7", + "@babel/plugin-transform-class-properties": "^7.29.7", + "@babel/plugin-transform-class-static-block": "^7.29.7", + "@babel/plugin-transform-classes": "^7.29.7", + "@babel/plugin-transform-computed-properties": "^7.29.7", + "@babel/plugin-transform-destructuring": "^7.29.7", + "@babel/plugin-transform-dotall-regex": "^7.29.7", + "@babel/plugin-transform-duplicate-keys": "^7.29.7", + "@babel/plugin-transform-duplicate-named-capturing-groups-regex": "^7.29.7", + "@babel/plugin-transform-dynamic-import": "^7.29.7", + "@babel/plugin-transform-explicit-resource-management": "^7.29.7", + "@babel/plugin-transform-exponentiation-operator": "^7.29.7", + "@babel/plugin-transform-export-namespace-from": "^7.29.7", + "@babel/plugin-transform-for-of": "^7.29.7", + "@babel/plugin-transform-function-name": "^7.29.7", + "@babel/plugin-transform-json-strings": "^7.29.7", + "@babel/plugin-transform-literals": "^7.29.7", + "@babel/plugin-transform-logical-assignment-operators": "^7.29.7", + "@babel/plugin-transform-member-expression-literals": "^7.29.7", + "@babel/plugin-transform-modules-amd": "^7.29.7", + "@babel/plugin-transform-modules-commonjs": "^7.29.7", + "@babel/plugin-transform-modules-systemjs": "^7.29.7", + "@babel/plugin-transform-modules-umd": "^7.29.7", + "@babel/plugin-transform-named-capturing-groups-regex": "^7.29.7", + "@babel/plugin-transform-new-target": "^7.29.7", + "@babel/plugin-transform-nullish-coalescing-operator": "^7.29.7", + "@babel/plugin-transform-numeric-separator": "^7.29.7", + "@babel/plugin-transform-object-rest-spread": "^7.29.7", + "@babel/plugin-transform-object-super": "^7.29.7", + "@babel/plugin-transform-optional-catch-binding": "^7.29.7", + "@babel/plugin-transform-optional-chaining": "^7.29.7", + "@babel/plugin-transform-parameters": "^7.29.7", + "@babel/plugin-transform-private-methods": "^7.29.7", + "@babel/plugin-transform-private-property-in-object": "^7.29.7", + "@babel/plugin-transform-property-literals": "^7.29.7", + "@babel/plugin-transform-regenerator": "^7.29.7", + "@babel/plugin-transform-regexp-modifiers": "^7.29.7", + "@babel/plugin-transform-reserved-words": "^7.29.7", + "@babel/plugin-transform-shorthand-properties": "^7.29.7", + "@babel/plugin-transform-spread": "^7.29.7", + "@babel/plugin-transform-sticky-regex": "^7.29.7", + "@babel/plugin-transform-template-literals": "^7.29.7", + "@babel/plugin-transform-typeof-symbol": "^7.29.7", + "@babel/plugin-transform-unicode-escapes": "^7.29.7", + "@babel/plugin-transform-unicode-property-regex": "^7.29.7", + "@babel/plugin-transform-unicode-regex": "^7.29.7", + "@babel/plugin-transform-unicode-sets-regex": "^7.29.7", "@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", + "babel-plugin-polyfill-corejs2": "^0.4.15", + "babel-plugin-polyfill-corejs3": "^0.14.0", + "babel-plugin-polyfill-regenerator": "^0.6.6", + "core-js-compat": "^3.48.0", "semver": "^6.3.1" }, "engines": { @@ -2131,6 +2113,20 @@ "@babel/core": "^7.0.0-0" } }, + "node_modules/@babel/preset-env/node_modules/babel-plugin-polyfill-corejs3": { + "version": "0.14.2", + "resolved": "https://registry.npmjs.org/babel-plugin-polyfill-corejs3/-/babel-plugin-polyfill-corejs3-0.14.2.tgz", + "integrity": "sha512-coWpDLJ410R781Npmn/SIBZEsAetR4xVi0SxLMXPaMO4lSf1MwnkGYMtkFxew0Dn8B3/CpbpYxN0JCgg8mn67g==", + "dev": true, + "license": "MIT", + "dependencies": { + "@babel/helper-define-polyfill-provider": "^0.6.8", + "core-js-compat": "^3.48.0" + }, + "peerDependencies": { + "@babel/core": "^7.4.0 || ^8.0.0-0 <8.0.0" + } + }, "node_modules/@babel/preset-flow": { "version": "7.27.1", "resolved": "https://registry.npmjs.org/@babel/preset-flow/-/preset-flow-7.27.1.tgz", @@ -2155,7 +2151,6 @@ "integrity": "sha512-HrcgcIESLm9aIR842yhJ5RWan/gebQUJ6E/E5+rf0y9o6oj7w0Br+sWuL6kEQ/o/AdfvR1Je9jG18/gnpwjEyA==", "dev": true, "license": "MIT", - "peer": true, "dependencies": { "@babel/helper-plugin-utils": "^7.0.0", "@babel/types": "^7.4.4", @@ -2251,33 +2246,33 @@ } }, "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==", + "version": "7.29.7", + "resolved": "https://registry.npmjs.org/@babel/template/-/template-7.29.7.tgz", + "integrity": "sha512-puq+Gf35oI24FeN11LkoUQFqv9uwNeWpxXZi/Ji3rRIoKAzKnxRaZ+Gkj0vKS9ZCiTESfng1N9LyOyXvo+m+Gg==", "dev": true, "license": "MIT", "dependencies": { - "@babel/code-frame": "^7.27.1", - "@babel/parser": "^7.27.2", - "@babel/types": "^7.27.1" + "@babel/code-frame": "^7.29.7", + "@babel/parser": "^7.29.7", + "@babel/types": "^7.29.7" }, "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==", + "version": "7.29.7", + "resolved": "https://registry.npmjs.org/@babel/traverse/-/traverse-7.29.7.tgz", + "integrity": "sha512-EhlfNQtZ+NK22w5BM61ciuiq1m58ed33Wr1Xan//ZRTy6hgjnwyCffRYwzsGXdASJSUJ1guZILsErh1eQcl+zw==", "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", + "@babel/code-frame": "^7.29.7", + "@babel/generator": "^7.29.7", + "@babel/helper-globals": "^7.29.7", + "@babel/parser": "^7.29.7", + "@babel/template": "^7.29.7", + "@babel/types": "^7.29.7", "debug": "^4.3.1" }, "engines": { @@ -2305,14 +2300,14 @@ } }, "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==", + "version": "7.29.7", + "resolved": "https://registry.npmjs.org/@babel/types/-/types-7.29.7.tgz", + "integrity": "sha512-4zBIxpPzowiZpusoFkyGVwakdRJUyuH5PxQ/PrqghfdFWWasvnCdPfQXHrenDai+gyLARulZjZowCOj6fjT4pA==", "dev": true, "license": "MIT", "dependencies": { - "@babel/helper-string-parser": "^7.27.1", - "@babel/helper-validator-identifier": "^7.28.5" + "@babel/helper-string-parser": "^7.29.7", + "@babel/helper-validator-identifier": "^7.29.7" }, "engines": { "node": ">=6.9.0" @@ -2495,9 +2490,9 @@ } }, "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==", + "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": { @@ -3337,25 +3332,6 @@ "dev": true, "license": "MIT" }, - "node_modules/@lwc/eslint-plugin-lwc": { - "version": "3.3.0", - "resolved": "https://registry.npmjs.org/@lwc/eslint-plugin-lwc/-/eslint-plugin-lwc-3.3.0.tgz", - "integrity": "sha512-qTcRGGTdBAfuPlJTL2sTk65skQHqnthz8TRx/1IasFZb+aljSn+CamnaPyzq8s2xAzQsS+NZJQoWlrZjyzzCzw==", - "dev": true, - "license": "MIT", - "dependencies": { - "fast-xml-parser": "^4.5.1", - "globals": "~15.14.0", - "minimatch": "~9.0.4" - }, - "engines": { - "node": ">=10.0.0" - }, - "peerDependencies": { - "@babel/eslint-parser": "^7", - "eslint": "^9" - } - }, "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", @@ -3390,44 +3366,6 @@ "eslint-scope": "5.1.1" } }, - "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/@octokit/auth-token": { "version": "4.0.0", "resolved": "https://registry.npmjs.org/@octokit/auth-token/-/auth-token-4.0.0.tgz", @@ -3444,6 +3382,7 @@ "integrity": "sha512-/g2d4sW9nUDJOMz3mabVQvOGhVa4e/BN/Um7yca9Bb2XTzPPnfTWHWQg+IsEYO7M3Vx+EXvaM/I2pJWIMun1bg==", "dev": true, "license": "MIT", + "peer": true, "dependencies": { "@octokit/auth-token": "^4.0.0", "@octokit/graphql": "^7.1.0", @@ -3611,299 +3550,6 @@ "node": ">=14" } }, - "node_modules/@react-native-community/eslint-config": { - "version": "3.2.0", - "resolved": "https://registry.npmjs.org/@react-native-community/eslint-config/-/eslint-config-3.2.0.tgz", - "integrity": "sha512-ZjGvoeiBtCbd506hQqwjKmkWPgynGUoJspG8/MuV/EfKnkjCtBmeJvq2n+sWbWEvL9LWXDp2GJmPzmvU5RSvKQ==", - "dev": true, - "license": "MIT", - "dependencies": { - "@babel/core": "^7.14.0", - "@babel/eslint-parser": "^7.18.2", - "@react-native-community/eslint-plugin": "^1.1.0", - "@typescript-eslint/eslint-plugin": "^5.30.5", - "@typescript-eslint/parser": "^5.30.5", - "eslint-config-prettier": "^8.5.0", - "eslint-plugin-eslint-comments": "^3.2.0", - "eslint-plugin-ft-flow": "^2.0.1", - "eslint-plugin-jest": "^26.5.3", - "eslint-plugin-prettier": "^4.2.1", - "eslint-plugin-react": "^7.30.1", - "eslint-plugin-react-hooks": "^4.6.0", - "eslint-plugin-react-native": "^4.0.0" - }, - "peerDependencies": { - "eslint": ">=8", - "prettier": ">=2" - } - }, - "node_modules/@react-native-community/eslint-config/node_modules/@typescript-eslint/eslint-plugin": { - "version": "5.62.0", - "resolved": "https://registry.npmjs.org/@typescript-eslint/eslint-plugin/-/eslint-plugin-5.62.0.tgz", - "integrity": "sha512-TiZzBSJja/LbhNPvk6yc0JrX9XqhQ0hdh6M2svYfsHGejaKFIAGd9MQ+ERIMzLGlN/kZoYIgdxFV0PuljTKXag==", - "dev": true, - "license": "MIT", - "dependencies": { - "@eslint-community/regexpp": "^4.4.0", - "@typescript-eslint/scope-manager": "5.62.0", - "@typescript-eslint/type-utils": "5.62.0", - "@typescript-eslint/utils": "5.62.0", - "debug": "^4.3.4", - "graphemer": "^1.4.0", - "ignore": "^5.2.0", - "natural-compare-lite": "^1.4.0", - "semver": "^7.3.7", - "tsutils": "^3.21.0" - }, - "engines": { - "node": "^12.22.0 || ^14.17.0 || >=16.0.0" - }, - "funding": { - "type": "opencollective", - "url": "https://opencollective.com/typescript-eslint" - }, - "peerDependencies": { - "@typescript-eslint/parser": "^5.0.0", - "eslint": "^6.0.0 || ^7.0.0 || ^8.0.0" - }, - "peerDependenciesMeta": { - "typescript": { - "optional": true - } - } - }, - "node_modules/@react-native-community/eslint-config/node_modules/@typescript-eslint/parser": { - "version": "5.62.0", - "resolved": "https://registry.npmjs.org/@typescript-eslint/parser/-/parser-5.62.0.tgz", - "integrity": "sha512-VlJEV0fOQ7BExOsHYAGrgbEiZoi8D+Bl2+f6V2RrXerRSylnp+ZBHmPvaIa8cz0Ajx7WO7Z5RqfgYg7ED1nRhA==", - "dev": true, - "license": "BSD-2-Clause", - "dependencies": { - "@typescript-eslint/scope-manager": "5.62.0", - "@typescript-eslint/types": "5.62.0", - "@typescript-eslint/typescript-estree": "5.62.0", - "debug": "^4.3.4" - }, - "engines": { - "node": "^12.22.0 || ^14.17.0 || >=16.0.0" - }, - "funding": { - "type": "opencollective", - "url": "https://opencollective.com/typescript-eslint" - }, - "peerDependencies": { - "eslint": "^6.0.0 || ^7.0.0 || ^8.0.0" - }, - "peerDependenciesMeta": { - "typescript": { - "optional": true - } - } - }, - "node_modules/@react-native-community/eslint-config/node_modules/@typescript-eslint/scope-manager": { - "version": "5.62.0", - "resolved": "https://registry.npmjs.org/@typescript-eslint/scope-manager/-/scope-manager-5.62.0.tgz", - "integrity": "sha512-VXuvVvZeQCQb5Zgf4HAxc04q5j+WrNAtNh9OwCsCgpKqESMTu3tF/jhZ3xG6T4NZwWl65Bg8KuS2uEvhSfLl0w==", - "dev": true, - "license": "MIT", - "dependencies": { - "@typescript-eslint/types": "5.62.0", - "@typescript-eslint/visitor-keys": "5.62.0" - }, - "engines": { - "node": "^12.22.0 || ^14.17.0 || >=16.0.0" - }, - "funding": { - "type": "opencollective", - "url": "https://opencollective.com/typescript-eslint" - } - }, - "node_modules/@react-native-community/eslint-config/node_modules/@typescript-eslint/type-utils": { - "version": "5.62.0", - "resolved": "https://registry.npmjs.org/@typescript-eslint/type-utils/-/type-utils-5.62.0.tgz", - "integrity": "sha512-xsSQreu+VnfbqQpW5vnCJdq1Z3Q0U31qiWmRhr98ONQmcp/yhiPJFPq8MXiJVLiksmOKSjIldZzkebzHuCGzew==", - "dev": true, - "license": "MIT", - "dependencies": { - "@typescript-eslint/typescript-estree": "5.62.0", - "@typescript-eslint/utils": "5.62.0", - "debug": "^4.3.4", - "tsutils": "^3.21.0" - }, - "engines": { - "node": "^12.22.0 || ^14.17.0 || >=16.0.0" - }, - "funding": { - "type": "opencollective", - "url": "https://opencollective.com/typescript-eslint" - }, - "peerDependencies": { - "eslint": "*" - }, - "peerDependenciesMeta": { - "typescript": { - "optional": true - } - } - }, - "node_modules/@react-native-community/eslint-config/node_modules/@typescript-eslint/types": { - "version": "5.62.0", - "resolved": "https://registry.npmjs.org/@typescript-eslint/types/-/types-5.62.0.tgz", - "integrity": "sha512-87NVngcbVXUahrRTqIK27gD2t5Cu1yuCXxbLcFtCzZGlfyVWWh8mLHkoxzjsB6DDNnvdL+fW8MiwPEJyGJQDgQ==", - "dev": true, - "license": "MIT", - "engines": { - "node": "^12.22.0 || ^14.17.0 || >=16.0.0" - }, - "funding": { - "type": "opencollective", - "url": "https://opencollective.com/typescript-eslint" - } - }, - "node_modules/@react-native-community/eslint-config/node_modules/@typescript-eslint/typescript-estree": { - "version": "5.62.0", - "resolved": "https://registry.npmjs.org/@typescript-eslint/typescript-estree/-/typescript-estree-5.62.0.tgz", - "integrity": "sha512-CmcQ6uY7b9y694lKdRB8FEel7JbU/40iSAPomu++SjLMntB+2Leay2LO6i8VnJk58MtE9/nQSFIH6jpyRWyYzA==", - "dev": true, - "license": "BSD-2-Clause", - "dependencies": { - "@typescript-eslint/types": "5.62.0", - "@typescript-eslint/visitor-keys": "5.62.0", - "debug": "^4.3.4", - "globby": "^11.1.0", - "is-glob": "^4.0.3", - "semver": "^7.3.7", - "tsutils": "^3.21.0" - }, - "engines": { - "node": "^12.22.0 || ^14.17.0 || >=16.0.0" - }, - "funding": { - "type": "opencollective", - "url": "https://opencollective.com/typescript-eslint" - }, - "peerDependenciesMeta": { - "typescript": { - "optional": true - } - } - }, - "node_modules/@react-native-community/eslint-config/node_modules/@typescript-eslint/utils": { - "version": "5.62.0", - "resolved": "https://registry.npmjs.org/@typescript-eslint/utils/-/utils-5.62.0.tgz", - "integrity": "sha512-n8oxjeb5aIbPFEtmQxQYOLI0i9n5ySBEY/ZEHHZqKQSFnxio1rv6dthascc9dLuwrL0RC5mPCxB7vnAVGAYWAQ==", - "dev": true, - "license": "MIT", - "dependencies": { - "@eslint-community/eslint-utils": "^4.2.0", - "@types/json-schema": "^7.0.9", - "@types/semver": "^7.3.12", - "@typescript-eslint/scope-manager": "5.62.0", - "@typescript-eslint/types": "5.62.0", - "@typescript-eslint/typescript-estree": "5.62.0", - "eslint-scope": "^5.1.1", - "semver": "^7.3.7" - }, - "engines": { - "node": "^12.22.0 || ^14.17.0 || >=16.0.0" - }, - "funding": { - "type": "opencollective", - "url": "https://opencollective.com/typescript-eslint" - }, - "peerDependencies": { - "eslint": "^6.0.0 || ^7.0.0 || ^8.0.0" - } - }, - "node_modules/@react-native-community/eslint-config/node_modules/@typescript-eslint/visitor-keys": { - "version": "5.62.0", - "resolved": "https://registry.npmjs.org/@typescript-eslint/visitor-keys/-/visitor-keys-5.62.0.tgz", - "integrity": "sha512-07ny+LHRzQXepkGg6w0mFY41fVUNBrL2Roj/++7V1txKugfjm/Ci/qSND03r2RhlJhJYMcTn9AhhSSqQp0Ysyw==", - "dev": true, - "license": "MIT", - "dependencies": { - "@typescript-eslint/types": "5.62.0", - "eslint-visitor-keys": "^3.3.0" - }, - "engines": { - "node": "^12.22.0 || ^14.17.0 || >=16.0.0" - }, - "funding": { - "type": "opencollective", - "url": "https://opencollective.com/typescript-eslint" - } - }, - "node_modules/@react-native-community/eslint-config/node_modules/eslint-config-prettier": { - "version": "8.10.2", - "resolved": "https://registry.npmjs.org/eslint-config-prettier/-/eslint-config-prettier-8.10.2.tgz", - "integrity": "sha512-/IGJ6+Dka158JnP5n5YFMOszjDWrXggGz1LaK/guZq9vZTmniaKlHcsscvkAhn9y4U+BU3JuUdYvtAMcv30y4A==", - "dev": true, - "license": "MIT", - "bin": { - "eslint-config-prettier": "bin/cli.js" - }, - "peerDependencies": { - "eslint": ">=7.0.0" - } - }, - "node_modules/@react-native-community/eslint-config/node_modules/eslint-plugin-jest": { - "version": "26.9.0", - "resolved": "https://registry.npmjs.org/eslint-plugin-jest/-/eslint-plugin-jest-26.9.0.tgz", - "integrity": "sha512-TWJxWGp1J628gxh2KhaH1H1paEdgE2J61BBF1I59c6xWeL5+D1BzMxGDN/nXAfX+aSkR5u80K+XhskK6Gwq9ng==", - "dev": true, - "license": "MIT", - "dependencies": { - "@typescript-eslint/utils": "^5.10.0" - }, - "engines": { - "node": "^12.22.0 || ^14.17.0 || >=16.0.0" - }, - "peerDependencies": { - "@typescript-eslint/eslint-plugin": "^5.0.0", - "eslint": "^6.0.0 || ^7.0.0 || ^8.0.0" - }, - "peerDependenciesMeta": { - "@typescript-eslint/eslint-plugin": { - "optional": true - }, - "jest": { - "optional": true - } - } - }, - "node_modules/@react-native-community/eslint-config/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/@react-native-community/eslint-config/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/@react-native-community/eslint-plugin": { - "version": "1.3.0", - "resolved": "https://registry.npmjs.org/@react-native-community/eslint-plugin/-/eslint-plugin-1.3.0.tgz", - "integrity": "sha512-+zDZ20NUnSWghj7Ku5aFphMzuM9JulqCW+aPXT6IfIXFbb8tzYTTOSeRFOtuekJ99ibW2fUCSsjuKNlwDIbHFg==", - "dev": true, - "license": "MIT" - }, "node_modules/@react-native/assets-registry": { "version": "0.76.3", "resolved": "https://registry.npmjs.org/@react-native/assets-registry/-/assets-registry-0.76.3.tgz", @@ -4441,12 +4087,23 @@ "dev": true, "license": "MIT" }, + "node_modules/@types/lodash.bindall": { + "version": "4.4.9", + "resolved": "https://registry.npmjs.org/@types/lodash.bindall/-/lodash.bindall-4.4.9.tgz", + "integrity": "sha512-sG6V5+T3JcB39nXfORM+kHRH6DiCB+95DfGrH2YpPMXRpt6q3rn7VrW3dItiOLasJYeHq1jxc+tOW1ijT+d1wg==", + "dev": true, + "license": "MIT", + "dependencies": { + "@types/lodash": "*" + } + }, "node_modules/@types/markdown-it": { "version": "14.1.2", "resolved": "https://registry.npmjs.org/@types/markdown-it/-/markdown-it-14.1.2.tgz", "integrity": "sha512-promo4eFwuiW+TfGxhi+0x3czqTYJkG8qB17ZUJiVF10Xm7NLVRSLUsfRTU/6h1e24VvRnXCx+hG7li58lkzog==", "dev": true, "license": "MIT", + "peer": true, "dependencies": { "@types/linkify-it": "^5", "@types/mdurl": "^2" @@ -4465,6 +4122,7 @@ "integrity": "sha512-OP+We5WV8Xnbuvw0zC2m4qfB/BJvjyCwtNjhHdJxV1639SGSKrLmJkc3fMnp2Qy8nJyHp8RO6umxELN/dS1/EA==", "dev": true, "license": "MIT", + "peer": true, "dependencies": { "undici-types": "~6.21.0" } @@ -4507,13 +4165,6 @@ "@types/react": "*" } }, - "node_modules/@types/semver": { - "version": "7.7.0", - "resolved": "https://registry.npmjs.org/@types/semver/-/semver-7.7.0.tgz", - "integrity": "sha512-k107IF4+Xr7UHjwDc7Cfd6PRQfbdkiRabXGRjo07b4WyPahFBZCZ1sE+BNxYIJPPg73UkfOsVOLwqVc/6ETrIA==", - "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", @@ -4553,20 +4204,21 @@ "license": "MIT" }, "node_modules/@typescript-eslint/eslint-plugin": { - "version": "8.51.0", - "resolved": "https://registry.npmjs.org/@typescript-eslint/eslint-plugin/-/eslint-plugin-8.51.0.tgz", - "integrity": "sha512-XtssGWJvypyM2ytBnSnKtHYOGT+4ZwTnBVl36TA4nRO2f4PRNGz5/1OszHzcZCvcBMh+qb7I06uoCmLTRdR9og==", + "version": "8.61.1", + "resolved": "https://registry.npmjs.org/@typescript-eslint/eslint-plugin/-/eslint-plugin-8.61.1.tgz", + "integrity": "sha512-ZPlVl3PB3et/59Ne0fv/sci6ZXz4T4Hp4nTJ56i/Y0gR89ARb+KphojTq6j+56E5PIezmOIOOWyY+aWQFd+IkQ==", "dev": true, "license": "MIT", + "peer": true, "dependencies": { - "@eslint-community/regexpp": "^4.10.0", - "@typescript-eslint/scope-manager": "8.51.0", - "@typescript-eslint/type-utils": "8.51.0", - "@typescript-eslint/utils": "8.51.0", - "@typescript-eslint/visitor-keys": "8.51.0", - "ignore": "^7.0.0", + "@eslint-community/regexpp": "^4.12.2", + "@typescript-eslint/scope-manager": "8.61.1", + "@typescript-eslint/type-utils": "8.61.1", + "@typescript-eslint/utils": "8.61.1", + "@typescript-eslint/visitor-keys": "8.61.1", + "ignore": "^7.0.5", "natural-compare": "^1.4.0", - "ts-api-utils": "^2.2.0" + "ts-api-utils": "^2.5.0" }, "engines": { "node": "^18.18.0 || ^20.9.0 || >=21.1.0" @@ -4576,9 +4228,9 @@ "url": "https://opencollective.com/typescript-eslint" }, "peerDependencies": { - "@typescript-eslint/parser": "^8.51.0", - "eslint": "^8.57.0 || ^9.0.0", - "typescript": ">=4.8.4 <6.0.0" + "@typescript-eslint/parser": "^8.61.1", + "eslint": "^8.57.0 || ^9.0.0 || ^10.0.0", + "typescript": ">=4.8.4 <6.1.0" } }, "node_modules/@typescript-eslint/eslint-plugin/node_modules/ignore": { @@ -4592,17 +4244,18 @@ } }, "node_modules/@typescript-eslint/parser": { - "version": "8.51.0", - "resolved": "https://registry.npmjs.org/@typescript-eslint/parser/-/parser-8.51.0.tgz", - "integrity": "sha512-3xP4XzzDNQOIqBMWogftkwxhg5oMKApqY0BAflmLZiFYHqyhSOxv/cd/zPQLTcCXr4AkaKb25joocY0BD1WC6A==", + "version": "8.61.1", + "resolved": "https://registry.npmjs.org/@typescript-eslint/parser/-/parser-8.61.1.tgz", + "integrity": "sha512-PJ5vePq5/ognBbrIcoC5+SHO5dfpeLPzP9FpLkzWrguoYQEeeSjlJpVwOpo1JRSTEi7dRcwNy4h4dzV70PqHcg==", "dev": true, "license": "MIT", + "peer": true, "dependencies": { - "@typescript-eslint/scope-manager": "8.51.0", - "@typescript-eslint/types": "8.51.0", - "@typescript-eslint/typescript-estree": "8.51.0", - "@typescript-eslint/visitor-keys": "8.51.0", - "debug": "^4.3.4" + "@typescript-eslint/scope-manager": "8.61.1", + "@typescript-eslint/types": "8.61.1", + "@typescript-eslint/typescript-estree": "8.61.1", + "@typescript-eslint/visitor-keys": "8.61.1", + "debug": "^4.4.3" }, "engines": { "node": "^18.18.0 || ^20.9.0 || >=21.1.0" @@ -4612,20 +4265,20 @@ "url": "https://opencollective.com/typescript-eslint" }, "peerDependencies": { - "eslint": "^8.57.0 || ^9.0.0", - "typescript": ">=4.8.4 <6.0.0" + "eslint": "^8.57.0 || ^9.0.0 || ^10.0.0", + "typescript": ">=4.8.4 <6.1.0" } }, "node_modules/@typescript-eslint/project-service": { - "version": "8.51.0", - "resolved": "https://registry.npmjs.org/@typescript-eslint/project-service/-/project-service-8.51.0.tgz", - "integrity": "sha512-Luv/GafO07Z7HpiI7qeEW5NW8HUtZI/fo/kE0YbtQEFpJRUuR0ajcWfCE5bnMvL7QQFrmT/odMe8QZww8X2nfQ==", + "version": "8.61.1", + "resolved": "https://registry.npmjs.org/@typescript-eslint/project-service/-/project-service-8.61.1.tgz", + "integrity": "sha512-PrC4JYGmR241lYnfhmKGTXkFqv8+ymbTFgSAY0fVXpY82/QkMw5TZPl+vGzuDDU2QYJk9fIDOBTntF+yDv9LEA==", "dev": true, "license": "MIT", "dependencies": { - "@typescript-eslint/tsconfig-utils": "^8.51.0", - "@typescript-eslint/types": "^8.51.0", - "debug": "^4.3.4" + "@typescript-eslint/tsconfig-utils": "^8.61.1", + "@typescript-eslint/types": "^8.61.1", + "debug": "^4.4.3" }, "engines": { "node": "^18.18.0 || ^20.9.0 || >=21.1.0" @@ -4635,18 +4288,18 @@ "url": "https://opencollective.com/typescript-eslint" }, "peerDependencies": { - "typescript": ">=4.8.4 <6.0.0" + "typescript": ">=4.8.4 <6.1.0" } }, "node_modules/@typescript-eslint/scope-manager": { - "version": "8.51.0", - "resolved": "https://registry.npmjs.org/@typescript-eslint/scope-manager/-/scope-manager-8.51.0.tgz", - "integrity": "sha512-JhhJDVwsSx4hiOEQPeajGhCWgBMBwVkxC/Pet53EpBVs7zHHtayKefw1jtPaNRXpI9RA2uocdmpdfE7T+NrizA==", + "version": "8.61.1", + "resolved": "https://registry.npmjs.org/@typescript-eslint/scope-manager/-/scope-manager-8.61.1.tgz", + "integrity": "sha512-L2bdIeoQS8FlKAvONAr20w6OcLXeB+qiDKbAooS9A0Ben+iSIkBef0FxqwKWYqt5sa0i4KJtxVyVmhMylKzF5w==", "dev": true, "license": "MIT", "dependencies": { - "@typescript-eslint/types": "8.51.0", - "@typescript-eslint/visitor-keys": "8.51.0" + "@typescript-eslint/types": "8.61.1", + "@typescript-eslint/visitor-keys": "8.61.1" }, "engines": { "node": "^18.18.0 || ^20.9.0 || >=21.1.0" @@ -4657,9 +4310,9 @@ } }, "node_modules/@typescript-eslint/tsconfig-utils": { - "version": "8.51.0", - "resolved": "https://registry.npmjs.org/@typescript-eslint/tsconfig-utils/-/tsconfig-utils-8.51.0.tgz", - "integrity": "sha512-Qi5bSy/vuHeWyir2C8u/uqGMIlIDu8fuiYWv48ZGlZ/k+PRPHtaAu7erpc7p5bzw2WNNSniuxoMSO4Ar6V9OXw==", + "version": "8.61.1", + "resolved": "https://registry.npmjs.org/@typescript-eslint/tsconfig-utils/-/tsconfig-utils-8.61.1.tgz", + "integrity": "sha512-UN/H4di+OO7EWx2ovME+8t31YO+KVnK0RRKEHR3kOt21/Ay8BOq3M1OMvWs5vNiqcFCYGYoxK3MXPZzmMUE+yg==", "dev": true, "license": "MIT", "engines": { @@ -4670,21 +4323,21 @@ "url": "https://opencollective.com/typescript-eslint" }, "peerDependencies": { - "typescript": ">=4.8.4 <6.0.0" + "typescript": ">=4.8.4 <6.1.0" } }, "node_modules/@typescript-eslint/type-utils": { - "version": "8.51.0", - "resolved": "https://registry.npmjs.org/@typescript-eslint/type-utils/-/type-utils-8.51.0.tgz", - "integrity": "sha512-0XVtYzxnobc9K0VU7wRWg1yiUrw4oQzexCG2V2IDxxCxhqBMSMbjB+6o91A+Uc0GWtgjCa3Y8bi7hwI0Tu4n5Q==", + "version": "8.61.1", + "resolved": "https://registry.npmjs.org/@typescript-eslint/type-utils/-/type-utils-8.61.1.tgz", + "integrity": "sha512-GYRicKmVK0C4fsKgaACaknOUAq9Oa2kwsjnpFhFcS/5p4Ht5IP9OVLbgIgcK4SRk92nVHFluurg1lumD9dBcLw==", "dev": true, "license": "MIT", "dependencies": { - "@typescript-eslint/types": "8.51.0", - "@typescript-eslint/typescript-estree": "8.51.0", - "@typescript-eslint/utils": "8.51.0", - "debug": "^4.3.4", - "ts-api-utils": "^2.2.0" + "@typescript-eslint/types": "8.61.1", + "@typescript-eslint/typescript-estree": "8.61.1", + "@typescript-eslint/utils": "8.61.1", + "debug": "^4.4.3", + "ts-api-utils": "^2.5.0" }, "engines": { "node": "^18.18.0 || ^20.9.0 || >=21.1.0" @@ -4694,14 +4347,14 @@ "url": "https://opencollective.com/typescript-eslint" }, "peerDependencies": { - "eslint": "^8.57.0 || ^9.0.0", - "typescript": ">=4.8.4 <6.0.0" + "eslint": "^8.57.0 || ^9.0.0 || ^10.0.0", + "typescript": ">=4.8.4 <6.1.0" } }, "node_modules/@typescript-eslint/types": { - "version": "8.51.0", - "resolved": "https://registry.npmjs.org/@typescript-eslint/types/-/types-8.51.0.tgz", - "integrity": "sha512-TizAvWYFM6sSscmEakjY3sPqGwxZRSywSsPEiuZF6d5GmGD9Gvlsv0f6N8FvAAA0CD06l3rIcWNbsN1e5F/9Ag==", + "version": "8.61.1", + "resolved": "https://registry.npmjs.org/@typescript-eslint/types/-/types-8.61.1.tgz", + "integrity": "sha512-G+CRlPqLv7Bz1IZVs03x5K59F1veqL0EJUROAdGhKsEq8qOiRiZbI+HUojPq5l0fEGOKModD9br6lObhB8zkoA==", "dev": true, "license": "MIT", "engines": { @@ -4713,21 +4366,21 @@ } }, "node_modules/@typescript-eslint/typescript-estree": { - "version": "8.51.0", - "resolved": "https://registry.npmjs.org/@typescript-eslint/typescript-estree/-/typescript-estree-8.51.0.tgz", - "integrity": "sha512-1qNjGqFRmlq0VW5iVlcyHBbCjPB7y6SxpBkrbhNWMy/65ZoncXCEPJxkRZL8McrseNH6lFhaxCIaX+vBuFnRng==", + "version": "8.61.1", + "resolved": "https://registry.npmjs.org/@typescript-eslint/typescript-estree/-/typescript-estree-8.61.1.tgz", + "integrity": "sha512-u+oQD3BqYWPc8YV9Zab4vaJElJuwOLPRc10Jm1o/qS+6Qwen14HCWwx0Seo4LnSn2wxea2Ik8DxPt2/FHmuhrg==", "dev": true, "license": "MIT", "dependencies": { - "@typescript-eslint/project-service": "8.51.0", - "@typescript-eslint/tsconfig-utils": "8.51.0", - "@typescript-eslint/types": "8.51.0", - "@typescript-eslint/visitor-keys": "8.51.0", - "debug": "^4.3.4", - "minimatch": "^9.0.4", - "semver": "^7.6.0", + "@typescript-eslint/project-service": "8.61.1", + "@typescript-eslint/tsconfig-utils": "8.61.1", + "@typescript-eslint/types": "8.61.1", + "@typescript-eslint/visitor-keys": "8.61.1", + "debug": "^4.4.3", + "minimatch": "^10.2.2", + "semver": "^7.7.3", "tinyglobby": "^0.2.15", - "ts-api-utils": "^2.2.0" + "ts-api-utils": "^2.5.0" }, "engines": { "node": "^18.18.0 || ^20.9.0 || >=21.1.0" @@ -4737,13 +4390,52 @@ "url": "https://opencollective.com/typescript-eslint" }, "peerDependencies": { - "typescript": ">=4.8.4 <6.0.0" + "typescript": ">=4.8.4 <6.1.0" + } + }, + "node_modules/@typescript-eslint/typescript-estree/node_modules/balanced-match": { + "version": "4.0.4", + "resolved": "https://registry.npmjs.org/balanced-match/-/balanced-match-4.0.4.tgz", + "integrity": "sha512-BLrgEcRTwX2o6gGxGOCNyMvGSp35YofuYzw9h1IMTRmKqttAZZVU67bdb9Pr2vUHA8+j3i2tJfjO6C6+4myGTA==", + "dev": true, + "license": "MIT", + "engines": { + "node": "18 || 20 || >=22" + } + }, + "node_modules/@typescript-eslint/typescript-estree/node_modules/brace-expansion": { + "version": "5.0.6", + "resolved": "https://registry.npmjs.org/brace-expansion/-/brace-expansion-5.0.6.tgz", + "integrity": "sha512-kLpxurY4Z4r9sgMsyG0Z9uzsBlgiU/EFKhj/h91/8yHu0edo7XuixOIH3VcJ8kkxs6/jPzoI6U9Vj3WqbMQ94g==", + "dev": true, + "license": "MIT", + "dependencies": { + "balanced-match": "^4.0.2" + }, + "engines": { + "node": "18 || 20 || >=22" + } + }, + "node_modules/@typescript-eslint/typescript-estree/node_modules/minimatch": { + "version": "10.2.5", + "resolved": "https://registry.npmjs.org/minimatch/-/minimatch-10.2.5.tgz", + "integrity": "sha512-MULkVLfKGYDFYejP07QOurDLLQpcjk7Fw+7jXS2R2czRQzR56yHRveU5NDJEOviH+hETZKSkIk5c+T23GjFUMg==", + "dev": true, + "license": "BlueOak-1.0.0", + "dependencies": { + "brace-expansion": "^5.0.5" + }, + "engines": { + "node": "18 || 20 || >=22" + }, + "funding": { + "url": "https://github.com/sponsors/isaacs" } }, "node_modules/@typescript-eslint/typescript-estree/node_modules/semver": { - "version": "7.7.3", - "resolved": "https://registry.npmjs.org/semver/-/semver-7.7.3.tgz", - "integrity": "sha512-SdsKMrI9TdgjdweUSR9MweHA4EJ8YxHn8DFaDisvhVlUOe4BF1tLD7GAj0lIqWVl+dPb/rExr0Btby5loQm20Q==", + "version": "7.8.4", + "resolved": "https://registry.npmjs.org/semver/-/semver-7.8.4.tgz", + "integrity": "sha512-rUCObTnP32Q08R2uuIrt7r9PlEonuTmtuXYcW6s5kjdlj3xbnwe+21yXptAUYcMAABLkYYTtnmzb3w3EDZfueA==", "dev": true, "license": "ISC", "bin": { @@ -4754,16 +4446,16 @@ } }, "node_modules/@typescript-eslint/utils": { - "version": "8.51.0", - "resolved": "https://registry.npmjs.org/@typescript-eslint/utils/-/utils-8.51.0.tgz", - "integrity": "sha512-11rZYxSe0zabiKaCP2QAwRf/dnmgFgvTmeDTtZvUvXG3UuAdg/GU02NExmmIXzz3vLGgMdtrIosI84jITQOxUA==", + "version": "8.61.1", + "resolved": "https://registry.npmjs.org/@typescript-eslint/utils/-/utils-8.61.1.tgz", + "integrity": "sha512-1+P/3Dj6jvtybE1q0HQ6yBt/gq+oKJyLdEv4HdnqasaEXRSYCAsD59mXEVQnM/ULNdQxbX77tdG4jPRjIS6knA==", "dev": true, "license": "MIT", "dependencies": { - "@eslint-community/eslint-utils": "^4.7.0", - "@typescript-eslint/scope-manager": "8.51.0", - "@typescript-eslint/types": "8.51.0", - "@typescript-eslint/typescript-estree": "8.51.0" + "@eslint-community/eslint-utils": "^4.9.1", + "@typescript-eslint/scope-manager": "8.61.1", + "@typescript-eslint/types": "8.61.1", + "@typescript-eslint/typescript-estree": "8.61.1" }, "engines": { "node": "^18.18.0 || ^20.9.0 || >=21.1.0" @@ -4773,19 +4465,19 @@ "url": "https://opencollective.com/typescript-eslint" }, "peerDependencies": { - "eslint": "^8.57.0 || ^9.0.0", - "typescript": ">=4.8.4 <6.0.0" + "eslint": "^8.57.0 || ^9.0.0 || ^10.0.0", + "typescript": ">=4.8.4 <6.1.0" } }, "node_modules/@typescript-eslint/visitor-keys": { - "version": "8.51.0", - "resolved": "https://registry.npmjs.org/@typescript-eslint/visitor-keys/-/visitor-keys-8.51.0.tgz", - "integrity": "sha512-mM/JRQOzhVN1ykejrvwnBRV3+7yTKK8tVANVN3o1O0t0v7o+jqdVu9crPy5Y9dov15TJk/FTIgoUGHrTOVL3Zg==", + "version": "8.61.1", + "resolved": "https://registry.npmjs.org/@typescript-eslint/visitor-keys/-/visitor-keys-8.61.1.tgz", + "integrity": "sha512-6fJ9MHWtK14C1DSkiMlHUSOmrVebL7150xZJBlJiL62jjhIA4JmOq6flwBgDxIdBKKdoiZRel+dfPD5MLfny3w==", "dev": true, "license": "MIT", "dependencies": { - "@typescript-eslint/types": "8.51.0", - "eslint-visitor-keys": "^4.2.1" + "@typescript-eslint/types": "8.61.1", + "eslint-visitor-keys": "^5.0.0" }, "engines": { "node": "^18.18.0 || ^20.9.0 || >=21.1.0" @@ -4796,13 +4488,13 @@ } }, "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==", + "version": "5.0.1", + "resolved": "https://registry.npmjs.org/eslint-visitor-keys/-/eslint-visitor-keys-5.0.1.tgz", + "integrity": "sha512-tD40eHxA35h0PEIZNeIjkHoDR4YjjJp34biM0mDvplBe//mB+IHCqHDGV7pxF+7MklTvighcCPPZC7ynWyjdTA==", "dev": true, "license": "Apache-2.0", "engines": { - "node": "^18.18.0 || ^20.9.0 || >=21.1.0" + "node": "^20.19.0 || ^22.13.0 || >=24" }, "funding": { "url": "https://opencollective.com/eslint" @@ -5128,6 +4820,7 @@ "integrity": "sha512-NZyJarBfL7nWwIq+FDL6Zp/yHEhePMNnnJ0y3qfieCrmNvYct8uvtiV41UvlSe6apAfk0fY1FbWx+NwfmpvtTg==", "dev": true, "license": "MIT", + "peer": true, "bin": { "acorn": "bin/acorn" }, @@ -5415,16 +5108,6 @@ "url": "https://github.com/sponsors/ljharb" } }, - "node_modules/array-union": { - "version": "2.1.0", - "resolved": "https://registry.npmjs.org/array-union/-/array-union-2.1.0.tgz", - "integrity": "sha512-HGyxoOTYUyCM6stUe6EJgnd4EoewAI7zMdfqO+kGjnlZmBDz/cR5pf8r/cR4Wq60sL/p0IkcjUEEPwS3GFrIyw==", - "dev": true, - "license": "MIT", - "engines": { - "node": ">=8" - } - }, "node_modules/array.prototype.findlast": { "version": "1.2.5", "resolved": "https://registry.npmjs.org/array.prototype.findlast/-/array.prototype.findlast-1.2.5.tgz", @@ -5721,14 +5404,14 @@ } }, "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==", + "version": "0.4.17", + "resolved": "https://registry.npmjs.org/babel-plugin-polyfill-corejs2/-/babel-plugin-polyfill-corejs2-0.4.17.tgz", + "integrity": "sha512-aTyf30K/rqAsNwN76zYrdtx8obu0E4KoUME29B1xj+B3WxgvWkp943vYQ+z8Mv3lw9xHXMHpvSPOBxzAkIa94w==", "dev": true, "license": "MIT", "dependencies": { - "@babel/compat-data": "^7.27.7", - "@babel/helper-define-polyfill-provider": "^0.6.5", + "@babel/compat-data": "^7.28.6", + "@babel/helper-define-polyfill-provider": "^0.6.8", "semver": "^6.3.1" }, "peerDependencies": { @@ -5750,13 +5433,13 @@ } }, "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==", + "version": "0.6.8", + "resolved": "https://registry.npmjs.org/babel-plugin-polyfill-regenerator/-/babel-plugin-polyfill-regenerator-0.6.8.tgz", + "integrity": "sha512-M762rNHfSF1EV3SLtnCJXFoQbbIIz0OyRwnCmV0KPC7qosSfCO0QLTSuJX3ayAebubhE6oYBAYPrBA5ljowaZg==", "dev": true, "license": "MIT", "dependencies": { - "@babel/helper-define-polyfill-provider": "^0.6.5" + "@babel/helper-define-polyfill-provider": "^0.6.8" }, "peerDependencies": { "@babel/core": "^7.4.0 || ^8.0.0-0 <8.0.0" @@ -5871,6 +5554,19 @@ ], "license": "MIT" }, + "node_modules/baseline-browser-mapping": { + "version": "2.10.38", + "resolved": "https://registry.npmjs.org/baseline-browser-mapping/-/baseline-browser-mapping-2.10.38.tgz", + "integrity": "sha512-31/02mVB4yuQU6adKk5SlY6m+mxDwUq5KZkyYgnLrrKl7TEm1+3PyDtDBz2kOv/wxZz41GHsvV1A/u6RmiyBvw==", + "dev": true, + "license": "Apache-2.0", + "bin": { + "baseline-browser-mapping": "dist/cli.cjs" + }, + "engines": { + "node": ">=6.0.0" + } + }, "node_modules/before-after-hook": { "version": "2.2.3", "resolved": "https://registry.npmjs.org/before-after-hook/-/before-after-hook-2.2.3.tgz", @@ -5885,6 +5581,7 @@ "dev": true, "hasInstallScript": true, "license": "MIT", + "peer": true, "dependencies": { "bindings": "^1.5.0", "prebuild-install": "^7.1.1" @@ -5984,9 +5681,9 @@ } }, "node_modules/browserslist": { - "version": "4.25.1", - "resolved": "https://registry.npmjs.org/browserslist/-/browserslist-4.25.1.tgz", - "integrity": "sha512-KGj0KoOMXLpSNkkEI6Z6mShmQy0bc1I+T7K9N81k4WWMrfz+6fQ6es80B/YLAeRoKvjYE1YSHHOW1qe9xIVzHw==", + "version": "4.28.2", + "resolved": "https://registry.npmjs.org/browserslist/-/browserslist-4.28.2.tgz", + "integrity": "sha512-48xSriZYYg+8qXna9kwqjIVzuQxi+KYWp2+5nCYnYKPTr0LvD89Jqk2Or5ogxz0NUMfIjhh2lIUX/LyX9B4oIg==", "dev": true, "funding": [ { @@ -6003,11 +5700,13 @@ } ], "license": "MIT", + "peer": true, "dependencies": { - "caniuse-lite": "^1.0.30001726", - "electron-to-chromium": "^1.5.173", - "node-releases": "^2.0.19", - "update-browserslist-db": "^1.1.3" + "baseline-browser-mapping": "^2.10.12", + "caniuse-lite": "^1.0.30001782", + "electron-to-chromium": "^1.5.328", + "node-releases": "^2.0.36", + "update-browserslist-db": "^1.2.3" }, "bin": { "browserslist": "cli.js" @@ -6203,9 +5902,9 @@ } }, "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==", + "version": "1.0.30001799", + "resolved": "https://registry.npmjs.org/caniuse-lite/-/caniuse-lite-1.0.30001799.tgz", + "integrity": "sha512-hG1bReV+OUU+MOqK4t/ZWI0tZOyz3rqS9XuhOUz1cIcbwBKjOyJEJuw9ER5JuNyqxNk8u/JUVbGibBOL1yrjFw==", "dev": true, "funding": [ { @@ -6724,13 +6423,13 @@ "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==", + "version": "3.49.0", + "resolved": "https://registry.npmjs.org/core-js-compat/-/core-js-compat-3.49.0.tgz", + "integrity": "sha512-VQXt1jr9cBz03b331DFDCCP90b3fanciLkgiOoy8SBHy06gNf+vQ1A3WFLqG7I8TipYIKeYK9wxd0tUrvHcOZA==", "dev": true, "license": "MIT", "dependencies": { - "browserslist": "^4.25.1" + "browserslist": "^4.28.1" }, "funding": { "type": "opencollective", @@ -7184,19 +6883,6 @@ "node": "^14.15.0 || ^16.10.0 || >=18.0.0" } }, - "node_modules/dir-glob": { - "version": "3.0.1", - "resolved": "https://registry.npmjs.org/dir-glob/-/dir-glob-3.0.1.tgz", - "integrity": "sha512-WkrWp9GR4KXfKGYzOLmTuGVi1UWFfws377n9cc55/tb6DuqyF6pcQ5AbiHEshaDpY9v6oaSr2XCDidGmMwdzIA==", - "dev": true, - "license": "MIT", - "dependencies": { - "path-type": "^4.0.0" - }, - "engines": { - "node": ">=8" - } - }, "node_modules/dmd": { "version": "6.2.3", "resolved": "https://registry.npmjs.org/dmd/-/dmd-6.2.3.tgz", @@ -7278,9 +6964,9 @@ "license": "MIT" }, "node_modules/electron-to-chromium": { - "version": "1.5.180", - "resolved": "https://registry.npmjs.org/electron-to-chromium/-/electron-to-chromium-1.5.180.tgz", - "integrity": "sha512-ED+GEyEh3kYMwt2faNmgMB0b8O5qtATGgR4RmRsIp4T6p7B8vdMbIedYndnvZfsaXvSzegtpfqRMDNCjjiSduA==", + "version": "1.5.376", + "resolved": "https://registry.npmjs.org/electron-to-chromium/-/electron-to-chromium-1.5.376.tgz", + "integrity": "sha512-cUVA7/RvbFTEuw/i3obUwDTRIXojaxkResf+ibByPFxjc6XK3VNtcQXV0NSbAlJ0FMjcJGgftVVB4Qo184EXvA==", "dev": true, "license": "ISC" }, @@ -7599,6 +7285,7 @@ "integrity": "sha512-LEyamqS7W5HB3ujJyvi0HQK/dtVINZvd5mAAp9eT5S/ujByGjiZLCzPcHVzuXbpJDJF/cxwHlfceVUDZ2lnSTw==", "dev": true, "license": "MIT", + "peer": true, "dependencies": { "@eslint-community/eslint-utils": "^4.8.0", "@eslint-community/regexpp": "^4.12.1", @@ -7654,28 +7341,29 @@ } }, "node_modules/eslint-config-expensify": { - "version": "2.0.108", - "resolved": "https://registry.npmjs.org/eslint-config-expensify/-/eslint-config-expensify-2.0.108.tgz", - "integrity": "sha512-J+tW0s5rli+ewOdY3fyf8Hk3G8WJho4YPNlTeSyeS+zD4JsQcqpOFF9wKzQFr8Tl/2E9f/4gxcYo2xiHolONLA==", + "version": "4.0.6", + "resolved": "https://registry.npmjs.org/eslint-config-expensify/-/eslint-config-expensify-4.0.6.tgz", + "integrity": "sha512-pBRNfKhN5MAN4QFz0LPzaLO+bzd+Zbz/W3Xs7GxtwikS4lTmA8hp37Zd75N68XVtf7g8xUX91kET3mJNlQJWyg==", "dev": true, "license": "ISC", "dependencies": { "@babel/eslint-parser": "^7.28.4", - "@lwc/eslint-plugin-lwc": "^3.2.0", "@typescript-eslint/parser": "^8.44.1", "@typescript-eslint/utils": "^8.44.1", "confusing-browser-globals": "^1.0.11", "eslint": "^9.36.0", "eslint-plugin-es": "^4.1.0", "eslint-plugin-import": "^2.32.0", + "eslint-plugin-jest": "^29.0.1", "eslint-plugin-jsdoc": "^60.2.0", "eslint-plugin-jsx-a11y": "^6.10.2", "eslint-plugin-react": "^7.37.5", - "eslint-plugin-react-hooks": "^7.0.1", + "eslint-plugin-react-hooks": "^7.1.1", "eslint-plugin-rulesdir": "^0.2.2", "eslint-plugin-unicorn": "^61.0.2", "globals": "^15.14.0", "lodash": "^4.17.21", + "typescript-eslint": "^8.61.0", "underscore": "^1.13.6" } }, @@ -7696,9 +7384,9 @@ } }, "node_modules/eslint-config-expensify/node_modules/eslint-plugin-react-hooks": { - "version": "7.0.1", - "resolved": "https://registry.npmjs.org/eslint-plugin-react-hooks/-/eslint-plugin-react-hooks-7.0.1.tgz", - "integrity": "sha512-O0d0m04evaNzEPoSW+59Mezf8Qt0InfgGIBJnpC0h3NH/WjUAR7BIKUfysC6todmtiZ/A0oUVS8Gce0WhBrHsA==", + "version": "7.1.1", + "resolved": "https://registry.npmjs.org/eslint-plugin-react-hooks/-/eslint-plugin-react-hooks-7.1.1.tgz", + "integrity": "sha512-f2I7Gw6JbvCexzIInuSbZpfdQ44D7iqdWX01FKLvrPgqxoE7oMj8clOfto8U6vYiz4yd5oKu39rRSVOe1zRu0g==", "dev": true, "license": "MIT", "dependencies": { @@ -7712,7 +7400,7 @@ "node": ">=18" }, "peerDependencies": { - "eslint": "^3.0.0 || ^4.0.0 || ^5.0.0 || ^6.0.0 || ^7.0.0 || ^8.0.0-0 || ^9.0.0" + "eslint": "^3.0.0 || ^4.0.0 || ^5.0.0 || ^6.0.0 || ^7.0.0 || ^8.0.0-0 || ^9.0.0 || ^10.0.0" } }, "node_modules/eslint-config-expensify/node_modules/eslint-plugin-unicorn": { @@ -7848,14 +7536,17 @@ } }, "node_modules/eslint-config-prettier": { - "version": "9.1.2", - "resolved": "https://registry.npmjs.org/eslint-config-prettier/-/eslint-config-prettier-9.1.2.tgz", - "integrity": "sha512-iI1f+D2ViGn+uvv5HuHVUamg8ll4tN+JRHGc6IJi4TP9Kl976C57fzPXgseXNs8v0iA8aSJpHsTWjDb9QJamGQ==", + "version": "10.1.8", + "resolved": "https://registry.npmjs.org/eslint-config-prettier/-/eslint-config-prettier-10.1.8.tgz", + "integrity": "sha512-82GZUjRS0p/jganf6q1rEO25VSoHH0hKPCTrgillPjdI/3bgBhAE1QzHrHTizjpRvy6pGAvKjDJtk2pF9NDq8w==", "dev": true, "license": "MIT", "bin": { "eslint-config-prettier": "bin/cli.js" }, + "funding": { + "url": "https://opencollective.com/eslint-config-prettier" + }, "peerDependencies": { "eslint": ">=7.0.0" } @@ -7908,9 +7599,9 @@ } }, "node_modules/eslint-import-resolver-typescript": { - "version": "4.4.4", - "resolved": "https://registry.npmjs.org/eslint-import-resolver-typescript/-/eslint-import-resolver-typescript-4.4.4.tgz", - "integrity": "sha512-1iM2zeBvrYmUNTj2vSC/90JTHDth+dfOfiNKkxApWRsTJYNrc8rOdxxIf5vazX+BiAXTeOT0UvWpGI/7qIWQOw==", + "version": "4.4.5", + "resolved": "https://registry.npmjs.org/eslint-import-resolver-typescript/-/eslint-import-resolver-typescript-4.4.5.tgz", + "integrity": "sha512-nbE5XLph6TLtGYcu/U6e6ZVXyKBhbDWK5cLGk76eJ7NdZpwf1P9EFkpt1Z01mNZNrrilsAYWKH6zUkL4reoXbw==", "dev": true, "license": "ISC", "dependencies": { @@ -7990,54 +7681,6 @@ "eslint": ">=4.19.1" } }, - "node_modules/eslint-plugin-eslint-comments": { - "version": "3.2.0", - "resolved": "https://registry.npmjs.org/eslint-plugin-eslint-comments/-/eslint-plugin-eslint-comments-3.2.0.tgz", - "integrity": "sha512-0jkOl0hfojIHHmEHgmNdqv4fmh7300NdpA9FFpF7zaoLvB/QeXOGNLIo86oAveJFrfB1p05kC8hpEMHM8DwWVQ==", - "dev": true, - "license": "MIT", - "dependencies": { - "escape-string-regexp": "^1.0.5", - "ignore": "^5.0.5" - }, - "engines": { - "node": ">=6.5.0" - }, - "funding": { - "url": "https://github.com/sponsors/mysticatea" - }, - "peerDependencies": { - "eslint": ">=4.19.1" - } - }, - "node_modules/eslint-plugin-eslint-comments/node_modules/escape-string-regexp": { - "version": "1.0.5", - "resolved": "https://registry.npmjs.org/escape-string-regexp/-/escape-string-regexp-1.0.5.tgz", - "integrity": "sha512-vbRorB5FUQWvla16U8R/qgaFIya2qGzwDrNmCZuYKrbdSUMG6I1ZCGQRefkRVhuOkIGVne7BQ35DSfo1qvJqFg==", - "dev": true, - "license": "MIT", - "engines": { - "node": ">=0.8.0" - } - }, - "node_modules/eslint-plugin-ft-flow": { - "version": "2.0.3", - "resolved": "https://registry.npmjs.org/eslint-plugin-ft-flow/-/eslint-plugin-ft-flow-2.0.3.tgz", - "integrity": "sha512-Vbsd/b+LYA99jUbsL6viEUWShFaYQt2YQs3QN3f+aeszOhh2sgdcU0mjzDyD4yyBvMc8qy2uwvBBWfMzEX06tg==", - "dev": true, - "license": "MIT", - "dependencies": { - "lodash": "^4.17.21", - "string-natural-compare": "^3.0.1" - }, - "engines": { - "node": ">=12.22.0" - }, - "peerDependencies": { - "@babel/eslint-parser": "^7.12.0", - "eslint": "^8.1.0" - } - }, "node_modules/eslint-plugin-import": { "version": "2.32.0", "resolved": "https://registry.npmjs.org/eslint-plugin-import/-/eslint-plugin-import-2.32.0.tgz", @@ -8119,6 +7762,36 @@ "node": "*" } }, + "node_modules/eslint-plugin-jest": { + "version": "29.15.2", + "resolved": "https://registry.npmjs.org/eslint-plugin-jest/-/eslint-plugin-jest-29.15.2.tgz", + "integrity": "sha512-kEN4r9RZl1xcsb4arGq89LrcVdOUFII/JSCwtTPJyv16mDwmPrcuEQwpxqZHeINvcsd7oK5O/rhdGlxFRaZwvQ==", + "dev": true, + "license": "MIT", + "dependencies": { + "@typescript-eslint/utils": "^8.0.0" + }, + "engines": { + "node": "^20.12.0 || ^22.0.0 || >=24.0.0" + }, + "peerDependencies": { + "@typescript-eslint/eslint-plugin": "^8.0.0", + "eslint": "^8.57.0 || ^9.0.0 || ^10.0.0", + "jest": "*", + "typescript": ">=4.8.4 <7.0.0" + }, + "peerDependenciesMeta": { + "@typescript-eslint/eslint-plugin": { + "optional": true + }, + "jest": { + "optional": true + }, + "typescript": { + "optional": true + } + } + }, "node_modules/eslint-plugin-jsdoc": { "version": "60.8.3", "resolved": "https://registry.npmjs.org/eslint-plugin-jsdoc/-/eslint-plugin-jsdoc-60.8.3.tgz", @@ -8213,28 +7886,6 @@ "node": "*" } }, - "node_modules/eslint-plugin-prettier": { - "version": "4.2.1", - "resolved": "https://registry.npmjs.org/eslint-plugin-prettier/-/eslint-plugin-prettier-4.2.1.tgz", - "integrity": "sha512-f/0rXLXUt0oFYs8ra4w49wYZBG5GKZpAYsJSm6rnYL5uVDjd+zowwMwVZHnAjf4edNrKpCDYfXDgmRE/Ak7QyQ==", - "dev": true, - "license": "MIT", - "dependencies": { - "prettier-linter-helpers": "^1.0.0" - }, - "engines": { - "node": ">=12.0.0" - }, - "peerDependencies": { - "eslint": ">=7.28.0", - "prettier": ">=2.0.0" - }, - "peerDependenciesMeta": { - "eslint-config-prettier": { - "optional": true - } - } - }, "node_modules/eslint-plugin-react": { "version": "7.37.5", "resolved": "https://registry.npmjs.org/eslint-plugin-react/-/eslint-plugin-react-7.37.5.tgz", @@ -8268,39 +7919,6 @@ "eslint": "^3 || ^4 || ^5 || ^6 || ^7 || ^8 || ^9.7" } }, - "node_modules/eslint-plugin-react-hooks": { - "version": "4.6.2", - "resolved": "https://registry.npmjs.org/eslint-plugin-react-hooks/-/eslint-plugin-react-hooks-4.6.2.tgz", - "integrity": "sha512-QzliNJq4GinDBcD8gPB5v0wh6g8q3SUi6EFF0x8N/BL9PoVs0atuGc47ozMRyOWAKdwaZ5OnbOEa3WR+dSGKuQ==", - "dev": true, - "license": "MIT", - "engines": { - "node": ">=10" - }, - "peerDependencies": { - "eslint": "^3.0.0 || ^4.0.0 || ^5.0.0 || ^6.0.0 || ^7.0.0 || ^8.0.0-0" - } - }, - "node_modules/eslint-plugin-react-native": { - "version": "4.1.0", - "resolved": "https://registry.npmjs.org/eslint-plugin-react-native/-/eslint-plugin-react-native-4.1.0.tgz", - "integrity": "sha512-QLo7rzTBOl43FvVqDdq5Ql9IoElIuTdjrz9SKAXCvULvBoRZ44JGSkx9z4999ZusCsb4rK3gjS8gOGyeYqZv2Q==", - "dev": true, - "license": "MIT", - "dependencies": { - "eslint-plugin-react-native-globals": "^0.1.1" - }, - "peerDependencies": { - "eslint": "^3.17.0 || ^4 || ^5 || ^6 || ^7 || ^8" - } - }, - "node_modules/eslint-plugin-react-native-globals": { - "version": "0.1.2", - "resolved": "https://registry.npmjs.org/eslint-plugin-react-native-globals/-/eslint-plugin-react-native-globals-0.1.2.tgz", - "integrity": "sha512-9aEPf1JEpiTjcFAmmyw8eiIXmcNZOqaZyHO77wgm0/dWfT/oxC1SrIq8ET38pMxHYrcB6Uew+TzUVsBeczF88g==", - "dev": true, - "license": "MIT" - }, "node_modules/eslint-plugin-react/node_modules/brace-expansion": { "version": "1.1.12", "resolved": "https://registry.npmjs.org/brace-expansion/-/brace-expansion-1.1.12.tgz", @@ -8390,6 +8008,31 @@ "node": ">=4.0" } }, + "node_modules/eslint-seatbelt": { + "version": "0.1.3", + "resolved": "https://registry.npmjs.org/eslint-seatbelt/-/eslint-seatbelt-0.1.3.tgz", + "integrity": "sha512-S8vot6z0Sr4wN1hGVmh3ex8hb7pSm0YNwZNS2Kkqd7CbtwjWQkqXIpoqZSEEW7PaCDMK6G6RBoXXRW/JygxTJg==", + "dev": true, + "license": "MIT", + "dependencies": { + "ts-command-line-args": "^2.5.1" + }, + "bin": { + "eslint-seatbelt": "dist/command.js" + }, + "peerDependencies": { + "@types/eslint": "*", + "eslint": "*" + }, + "peerDependenciesMeta": { + "@types/eslint": { + "optional": true + }, + "eslint": { + "optional": true + } + } + }, "node_modules/eslint-utils": { "version": "2.1.0", "resolved": "https://registry.npmjs.org/eslint-utils/-/eslint-utils-2.1.0.tgz", @@ -8675,49 +8318,12 @@ "dev": true, "license": "MIT" }, - "node_modules/fast-diff": { - "version": "1.3.0", - "resolved": "https://registry.npmjs.org/fast-diff/-/fast-diff-1.3.0.tgz", - "integrity": "sha512-VxPP4NqbUjj6MaAOafWeUn2cXWLcCtljklUtZf0Ind4XQ+QPtmA0b18zZy0jIQx+ExRVCR/ZQpBmik5lXshNsw==", - "dev": true, - "license": "Apache-2.0" - }, "node_modules/fast-equals": { "version": "4.0.3", "resolved": "https://registry.npmjs.org/fast-equals/-/fast-equals-4.0.3.tgz", "integrity": "sha512-G3BSX9cfKttjr+2o1O22tYMLq0DPluZnYtq1rXumE1SpL/F/SLIfHx08WYQoWSIpeMYf8sRbJ8++71+v6Pnxfg==", "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", @@ -8732,35 +8338,6 @@ "dev": true, "license": "MIT" }, - "node_modules/fast-xml-parser": { - "version": "4.5.3", - "resolved": "https://registry.npmjs.org/fast-xml-parser/-/fast-xml-parser-4.5.3.tgz", - "integrity": "sha512-RKihhV+SHsIUGXObeVy9AXiBbFwkVk7Syp8XgwN5U3JV416+Gwp/GO9i0JYKmikykgz/UHRrrV4ROuZEo/T0ig==", - "dev": true, - "funding": [ - { - "type": "github", - "url": "https://github.com/sponsors/NaturalIntelligence" - } - ], - "license": "MIT", - "dependencies": { - "strnum": "^1.1.1" - }, - "bin": { - "fxparser": "src/cli/cli.js" - } - }, - "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", @@ -9438,9 +9015,9 @@ } }, "node_modules/globals": { - "version": "15.14.0", - "resolved": "https://registry.npmjs.org/globals/-/globals-15.14.0.tgz", - "integrity": "sha512-OkToC372DtlQeje9/zHIo5CT8lRP/FUgEOKBEhU4e0abL7J7CD24fD9ohiLN5hagG/kWCYj4K5oaxxtj2Z0Dig==", + "version": "15.15.0", + "resolved": "https://registry.npmjs.org/globals/-/globals-15.15.0.tgz", + "integrity": "sha512-7ACyT3wmyp3I61S4fG682L0VA2RGD9otkqGJIwNUMF1SWUombIIk+af1unuDYgMm082aHYwD+mzJvv9Iu8dsgg==", "dev": true, "license": "MIT", "engines": { @@ -9467,27 +9044,6 @@ "url": "https://github.com/sponsors/ljharb" } }, - "node_modules/globby": { - "version": "11.1.0", - "resolved": "https://registry.npmjs.org/globby/-/globby-11.1.0.tgz", - "integrity": "sha512-jhIXaOzy1sb8IyocaruWSn1TjmnBVs8Ayhcy83rmxNJ8q2uWKCAj3CnJY+KpGSXCueAPc0i05kVvVKtP1t9S3g==", - "dev": true, - "license": "MIT", - "dependencies": { - "array-union": "^2.1.0", - "dir-glob": "^3.0.1", - "fast-glob": "^3.2.9", - "ignore": "^5.2.0", - "merge2": "^1.4.1", - "slash": "^3.0.0" - }, - "engines": { - "node": ">=10" - }, - "funding": { - "url": "https://github.com/sponsors/sindresorhus" - } - }, "node_modules/gopd": { "version": "1.2.0", "resolved": "https://registry.npmjs.org/gopd/-/gopd-1.2.0.tgz", @@ -9508,13 +9064,6 @@ "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/handlebars": { "version": "4.7.8", "resolved": "https://registry.npmjs.org/handlebars/-/handlebars-4.7.8.tgz", @@ -10630,6 +10179,7 @@ "integrity": "sha512-NIy3oAFp9shda19hy4HK0HRTWKtPJmGdnvywu01nOqNC2vZg+Z+fvJDxpMQA88eb2I9EcafcdjYgsDthnYTvGw==", "dev": true, "license": "MIT", + "peer": true, "dependencies": { "@jest/core": "^29.7.0", "@jest/types": "^29.6.3", @@ -11910,6 +11460,7 @@ "integrity": "sha512-a54IwgWPaeBCAAsv13YgmALOF1elABB08FxO9i+r4VFk5Vl4pKokRPeX8u5TCgSsPi6ec1otfLjdOpVcgbpshg==", "dev": true, "license": "MIT", + "peer": true, "dependencies": { "argparse": "^2.0.1", "entities": "^4.4.0", @@ -12008,16 +11559,6 @@ "dev": true, "license": "MIT" }, - "node_modules/merge2": { - "version": "1.4.1", - "resolved": "https://registry.npmjs.org/merge2/-/merge2-1.4.1.tgz", - "integrity": "sha512-8q7VEgMJW4J8tcfVPy8g09NcQwZdbwFEqhe/WZkoIzjn/3TGDwtOCYtXGxA3O8tPzpczCCDgv+P2P5y00ZJOOg==", - "dev": true, - "license": "MIT", - "engines": { - "node": ">= 8" - } - }, "node_modules/metro": { "version": "0.81.5", "resolved": "https://registry.npmjs.org/metro/-/metro-0.81.5.tgz", @@ -12623,13 +12164,6 @@ "dev": true, "license": "MIT" }, - "node_modules/natural-compare-lite": { - "version": "1.4.0", - "resolved": "https://registry.npmjs.org/natural-compare-lite/-/natural-compare-lite-1.4.0.tgz", - "integrity": "sha512-Tj+HTDSJJKaZnfiuw+iaF9skdPpTo2GtEly5JHnWV/hfv2Qj/9RKsGISQtLh2ox3l5EAGw487hnBee0sIJ6v2g==", - "dev": true, - "license": "MIT" - }, "node_modules/negotiator": { "version": "0.6.3", "resolved": "https://registry.npmjs.org/negotiator/-/negotiator-0.6.3.tgz", @@ -12774,11 +12308,14 @@ "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==", + "version": "2.0.48", + "resolved": "https://registry.npmjs.org/node-releases/-/node-releases-2.0.48.tgz", + "integrity": "sha512-1uz8041X6LoI6ZSdZacM9lVY28vuzDlSKitnpbSNK0RfKoIJkX29NBPVEFXhnuSuEOA9Ww0xnPJ+ILWbGAv8DA==", "dev": true, - "license": "MIT" + "license": "MIT", + "engines": { + "node": ">=18" + } }, "node_modules/nodemon": { "version": "3.1.10", @@ -13369,16 +12906,6 @@ "dev": true, "license": "ISC" }, - "node_modules/path-type": { - "version": "4.0.0", - "resolved": "https://registry.npmjs.org/path-type/-/path-type-4.0.0.tgz", - "integrity": "sha512-gDKb8aZMDeD/tZWs9P6+q0J9Mwkdl6xMV8TjnGP3qJVJ06bdMgkbBlLU8IdfOsIsFz2BW1rNVT3XuNEl8zPAvw==", - "dev": true, - "license": "MIT", - "engines": { - "node": ">=8" - } - }, "node_modules/picocolors": { "version": "1.1.1", "resolved": "https://registry.npmjs.org/picocolors/-/picocolors-1.1.1.tgz", @@ -13392,6 +12919,7 @@ "integrity": "sha512-5gTmgEY/sqK6gFXLIsQNH19lWb4ebPDLA4SdLP7dsWkIXHWlG66oPuVvXSGFPppYZz8ZDZq0dYYrbHfBCVUb1Q==", "dev": true, "license": "MIT", + "peer": true, "engines": { "node": ">=12" }, @@ -13562,19 +13090,6 @@ "url": "https://github.com/prettier/prettier?sponsor=1" } }, - "node_modules/prettier-linter-helpers": { - "version": "1.0.0", - "resolved": "https://registry.npmjs.org/prettier-linter-helpers/-/prettier-linter-helpers-1.0.0.tgz", - "integrity": "sha512-GbK2cP9nraSSUF9N2XwUwqfzlAFlMNYYl+ShE/V+H8a9uNl/oUqB1w2EL54Jh0OlyRSd8RfWYJ3coVS4TROP2w==", - "dev": true, - "license": "MIT", - "dependencies": { - "fast-diff": "^1.1.2" - }, - "engines": { - "node": ">=6.0.0" - } - }, "node_modules/pretty-format": { "version": "29.7.0", "resolved": "https://registry.npmjs.org/pretty-format/-/pretty-format-29.7.0.tgz", @@ -13731,27 +13246,6 @@ "inherits": "~2.0.3" } }, - "node_modules/queue-microtask": { - "version": "1.2.3", - "resolved": "https://registry.npmjs.org/queue-microtask/-/queue-microtask-1.2.3.tgz", - "integrity": "sha512-NuaNSa6flKT5JaSYQzJok04JzTL1CA6aGhv5rfLW3PgqA+M2ChpZQnAC8h8i4ZFkBS8X5RqkDBHA7r4hej3K9A==", - "dev": true, - "funding": [ - { - "type": "github", - "url": "https://github.com/sponsors/feross" - }, - { - "type": "patreon", - "url": "https://www.patreon.com/feross" - }, - { - "type": "consulting", - "url": "https://feross.org/support" - } - ], - "license": "MIT" - }, "node_modules/range-parser": { "version": "1.2.1", "resolved": "https://registry.npmjs.org/range-parser/-/range-parser-1.2.1.tgz", @@ -13794,6 +13288,7 @@ "integrity": "sha512-/3IjMdb2L9QbBdWiW5e3P2/npwMBaU9mHCSCUzNln0ZCYbcfTsGbTJrU/kGemdH2IWmB2ioZ+zkxtmq6g09fGQ==", "dev": true, "license": "MIT", + "peer": true, "dependencies": { "loose-envify": "^1.1.0" }, @@ -13847,6 +13342,7 @@ "integrity": "sha512-0TUhgmlouRNf6yuDIIAdbQl0g1VsONgCMsLs7Et64hjj5VLMCA7np+4dMrZvGZ3wRNqzgeyT9oWJsUm49AcwSQ==", "dev": true, "license": "MIT", + "peer": true, "dependencies": { "@jest/create-cache-key-function": "^29.6.3", "@react-native/assets-registry": "0.76.3", @@ -13919,6 +13415,7 @@ "integrity": "sha512-Eho1yEcLbsteGpBFn2XZOp5FIptnEciWzuYBW49S0jo41Un2LeyesIO/MqYLY/c5o7D9Fw9th4pxGtV7OAb0+g==", "dev": true, "license": "MIT", + "peer": true, "peerDependencies": { "react": "*", "react-native": "*" @@ -14036,6 +13533,7 @@ "integrity": "sha512-JWD+aQ0lh2gvh4NM3bBM42Kx+XybOxCpgYK7F8ugAlpaTSnWsX+39Z4XkOykGZAHrjwwTZT3x3KxswVWxHPUqA==", "dev": true, "license": "MIT", + "peer": true, "dependencies": { "react-is": "^18.2.0", "react-shallow-renderer": "^16.15.0", @@ -14239,9 +13737,9 @@ "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==", + "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": { @@ -14303,23 +13801,36 @@ } }, "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==", + "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.0", + "regenerate-unicode-properties": "^10.2.2", "regjsgen": "^0.8.0", - "regjsparser": "^0.12.0", + "regjsparser": "^0.13.0", "unicode-match-property-ecmascript": "^2.0.0", - "unicode-match-property-value-ecmascript": "^2.1.0" + "unicode-match-property-value-ecmascript": "^2.2.1" }, "engines": { "node": ">=4" } }, + "node_modules/regexpu-core/node_modules/regjsparser": { + "version": "0.13.2", + "resolved": "https://registry.npmjs.org/regjsparser/-/regjsparser-0.13.2.tgz", + "integrity": "sha512-NgRBy2Nx/bE+9F27nVHnqcN5HjyLmecqsqx2PJHu3/IEtADD4WuxuXIVExD5PoSDFVrl78dOonfcOe5O+5nbzQ==", + "dev": true, + "license": "BSD-2-Clause", + "dependencies": { + "jsesc": "~3.1.0" + }, + "bin": { + "regjsparser": "bin/parser" + } + }, "node_modules/regjsgen": { "version": "0.8.0", "resolved": "https://registry.npmjs.org/regjsgen/-/regjsgen-0.8.0.tgz", @@ -14381,13 +13892,14 @@ } }, "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.12", + "resolved": "https://registry.npmjs.org/resolve/-/resolve-1.22.12.tgz", + "integrity": "sha512-TyeJ1zif53BPfHootBGwPRYT1RUt6oGWsaQr8UyZW/eAm9bKoijtvruSDEmZHm92CwS9nj7/fWttqPCgzep8CA==", "dev": true, "license": "MIT", "dependencies": { - "is-core-module": "^2.16.0", + "es-errors": "^1.3.0", + "is-core-module": "^2.16.1", "path-parse": "^1.0.7", "supports-preserve-symlinks-flag": "^1.0.0" }, @@ -14454,17 +13966,6 @@ "node": ">=10" } }, - "node_modules/reusify": { - "version": "1.1.0", - "resolved": "https://registry.npmjs.org/reusify/-/reusify-1.1.0.tgz", - "integrity": "sha512-g6QUff04oZpHs0eG5p83rFLhHeV00ug/Yf9nZM6fLeUrPguBTkTQOdpAWWspMh55TZfVQDPaN3NQJfbVRAxdIw==", - "dev": true, - "license": "MIT", - "engines": { - "iojs": ">=1.0.0", - "node": ">=0.10.0" - } - }, "node_modules/rimraf": { "version": "3.0.2", "resolved": "https://registry.npmjs.org/rimraf/-/rimraf-3.0.2.tgz", @@ -14482,30 +13983,6 @@ "url": "https://github.com/sponsors/isaacs" } }, - "node_modules/run-parallel": { - "version": "1.2.0", - "resolved": "https://registry.npmjs.org/run-parallel/-/run-parallel-1.2.0.tgz", - "integrity": "sha512-5l4VyZR86LZ/lDxZTR6jqL8AFE2S0IFLMP26AbjsLVADxHdhB/c0GUsH+y39UfCi3dzz8OlQuPmnaJOMoDHQBA==", - "dev": true, - "funding": [ - { - "type": "github", - "url": "https://github.com/sponsors/feross" - }, - { - "type": "patreon", - "url": "https://www.patreon.com/feross" - }, - { - "type": "consulting", - "url": "https://feross.org/support" - } - ], - "license": "MIT", - "dependencies": { - "queue-microtask": "^1.2.2" - } - }, "node_modules/safe-array-concat": { "version": "1.1.3", "resolved": "https://registry.npmjs.org/safe-array-concat/-/safe-array-concat-1.1.3.tgz", @@ -15295,6 +14772,13 @@ "safe-buffer": "~5.2.0" } }, + "node_modules/string-format": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/string-format/-/string-format-2.0.0.tgz", + "integrity": "sha512-bbEs3scLeYNXLecRRuk6uJxdXUSj6le/8rNPHChIJTn2V79aXVTR1EH2OH5zLKKoz0V02fOUKZZcw01pLUShZA==", + "dev": true, + "license": "WTFPL OR MIT" + }, "node_modules/string-length": { "version": "4.0.2", "resolved": "https://registry.npmjs.org/string-length/-/string-length-4.0.2.tgz", @@ -15309,13 +14793,6 @@ "node": ">=10" } }, - "node_modules/string-natural-compare": { - "version": "3.0.1", - "resolved": "https://registry.npmjs.org/string-natural-compare/-/string-natural-compare-3.0.1.tgz", - "integrity": "sha512-n3sPwynL1nwKi3WJ6AIsClwBMa0zTi54fn2oLU6ndfTSIO05xaznjSf15PcBZU6FNWbmN5Q6cxT4V5hGvB4taw==", - "dev": true, - "license": "MIT" - }, "node_modules/string-width": { "version": "4.2.3", "resolved": "https://registry.npmjs.org/string-width/-/string-width-4.2.3.tgz", @@ -15547,19 +15024,6 @@ "url": "https://github.com/sponsors/sindresorhus" } }, - "node_modules/strnum": { - "version": "1.1.2", - "resolved": "https://registry.npmjs.org/strnum/-/strnum-1.1.2.tgz", - "integrity": "sha512-vrN+B7DBIoTTZjnPNewwhx6cBA/H+IS7rfW68n7XxC1y7uoiGQBxaKzqucGUgavX15dJgiGztLJ8vxuEzwqBdA==", - "dev": true, - "funding": [ - { - "type": "github", - "url": "https://github.com/sponsors/NaturalIntelligence" - } - ], - "license": "MIT" - }, "node_modules/supports-color": { "version": "7.2.0", "resolved": "https://registry.npmjs.org/supports-color/-/supports-color-7.2.0.tgz", @@ -15906,9 +15370,9 @@ } }, "node_modules/ts-api-utils": { - "version": "2.3.0", - "resolved": "https://registry.npmjs.org/ts-api-utils/-/ts-api-utils-2.3.0.tgz", - "integrity": "sha512-6eg3Y9SF7SsAvGzRHQvvc1skDAhwI4YQ32ui1scxD1Ccr0G5qIIbUBT3pFTKX8kmWIQClHobtUdNuaBgwdfdWg==", + "version": "2.5.0", + "resolved": "https://registry.npmjs.org/ts-api-utils/-/ts-api-utils-2.5.0.tgz", + "integrity": "sha512-OJ/ibxhPlqrMM0UiNHJ/0CKQkoKF243/AEmplt3qpRgkW8VG7IfOS41h7V8TjITqdByHzrjcS/2si+y4lIh8NA==", "dev": true, "license": "MIT", "engines": { @@ -15918,6 +15382,176 @@ "typescript": ">=4.8.4" } }, + "node_modules/ts-command-line-args": { + "version": "2.5.1", + "resolved": "https://registry.npmjs.org/ts-command-line-args/-/ts-command-line-args-2.5.1.tgz", + "integrity": "sha512-H69ZwTw3rFHb5WYpQya40YAX2/w7Ut75uUECbgBIsLmM+BNuYnxsltfyyLMxy6sEeKxgijLTnQtLd0nKd6+IYw==", + "dev": true, + "license": "ISC", + "dependencies": { + "chalk": "^4.1.0", + "command-line-args": "^5.1.1", + "command-line-usage": "^6.1.0", + "string-format": "^2.0.0" + }, + "bin": { + "write-markdown": "dist/write-markdown.js" + } + }, + "node_modules/ts-command-line-args/node_modules/ansi-styles": { + "version": "3.2.1", + "resolved": "https://registry.npmjs.org/ansi-styles/-/ansi-styles-3.2.1.tgz", + "integrity": "sha512-VT0ZI6kZRdTh8YyJw3SMbYm/u+NqfsAxEpWO0Pf9sq8/e94WxxOpPKx9FR1FlyCtOVDNOQ+8ntlqFxiRc+r5qA==", + "dev": true, + "license": "MIT", + "dependencies": { + "color-convert": "^1.9.0" + }, + "engines": { + "node": ">=4" + } + }, + "node_modules/ts-command-line-args/node_modules/array-back": { + "version": "4.0.2", + "resolved": "https://registry.npmjs.org/array-back/-/array-back-4.0.2.tgz", + "integrity": "sha512-NbdMezxqf94cnNfWLL7V/im0Ub+Anbb0IoZhvzie8+4HJ4nMQuzHuy49FkGYCJK2yAloZ3meiB6AVMClbrI1vg==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=8" + } + }, + "node_modules/ts-command-line-args/node_modules/color-convert": { + "version": "1.9.3", + "resolved": "https://registry.npmjs.org/color-convert/-/color-convert-1.9.3.tgz", + "integrity": "sha512-QfAUtd+vFdAtFQcC8CCyYt1fYWxSqAiK2cSD6zDB8N3cpsEBAvRxp9zOGg6G/SHHJYAT88/az/IuDGALsNVbGg==", + "dev": true, + "license": "MIT", + "dependencies": { + "color-name": "1.1.3" + } + }, + "node_modules/ts-command-line-args/node_modules/color-name": { + "version": "1.1.3", + "resolved": "https://registry.npmjs.org/color-name/-/color-name-1.1.3.tgz", + "integrity": "sha512-72fSenhMw2HZMTVHeCA9KCmpEIbzWiQsjN+BHcBbS9vr1mtt+vJjPdksIBNUmKAW8TFUDPJK5SUU3QhE9NEXDw==", + "dev": true, + "license": "MIT" + }, + "node_modules/ts-command-line-args/node_modules/command-line-usage": { + "version": "6.1.3", + "resolved": "https://registry.npmjs.org/command-line-usage/-/command-line-usage-6.1.3.tgz", + "integrity": "sha512-sH5ZSPr+7UStsloltmDh7Ce5fb8XPlHyoPzTpyyMuYCtervL65+ubVZ6Q61cFtFl62UyJlc8/JwERRbAFPUqgw==", + "dev": true, + "license": "MIT", + "dependencies": { + "array-back": "^4.0.2", + "chalk": "^2.4.2", + "table-layout": "^1.0.2", + "typical": "^5.2.0" + }, + "engines": { + "node": ">=8.0.0" + } + }, + "node_modules/ts-command-line-args/node_modules/command-line-usage/node_modules/chalk": { + "version": "2.4.2", + "resolved": "https://registry.npmjs.org/chalk/-/chalk-2.4.2.tgz", + "integrity": "sha512-Mti+f9lpJNcwF4tWV8/OrTTtF1gZi+f8FqlyAdouralcFWFQWF2+NgCHShjkCb+IFBLq9buZwE1xckQU4peSuQ==", + "dev": true, + "license": "MIT", + "dependencies": { + "ansi-styles": "^3.2.1", + "escape-string-regexp": "^1.0.5", + "supports-color": "^5.3.0" + }, + "engines": { + "node": ">=4" + } + }, + "node_modules/ts-command-line-args/node_modules/escape-string-regexp": { + "version": "1.0.5", + "resolved": "https://registry.npmjs.org/escape-string-regexp/-/escape-string-regexp-1.0.5.tgz", + "integrity": "sha512-vbRorB5FUQWvla16U8R/qgaFIya2qGzwDrNmCZuYKrbdSUMG6I1ZCGQRefkRVhuOkIGVne7BQ35DSfo1qvJqFg==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=0.8.0" + } + }, + "node_modules/ts-command-line-args/node_modules/has-flag": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/has-flag/-/has-flag-3.0.0.tgz", + "integrity": "sha512-sKJf1+ceQBr4SMkvQnBDNDtf4TXpVhVGateu0t918bl30FnbE2m4vNLX+VWe/dpjlb+HugGYzW7uQXH98HPEYw==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=4" + } + }, + "node_modules/ts-command-line-args/node_modules/reduce-flatten": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/reduce-flatten/-/reduce-flatten-2.0.0.tgz", + "integrity": "sha512-EJ4UNY/U1t2P/2k6oqotuX2Cc3T6nxJwsM0N0asT7dhrtH1ltUxDn4NalSYmPE2rCkVpcf/X6R0wDwcFpzhd4w==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=6" + } + }, + "node_modules/ts-command-line-args/node_modules/supports-color": { + "version": "5.5.0", + "resolved": "https://registry.npmjs.org/supports-color/-/supports-color-5.5.0.tgz", + "integrity": "sha512-QjVjwdXIt408MIiAqCX4oUKsgU2EqAGzs2Ppkm4aQYbjm+ZEWEcW4SfFNTr4uMNZma0ey4f5lgLrkB0aX0QMow==", + "dev": true, + "license": "MIT", + "dependencies": { + "has-flag": "^3.0.0" + }, + "engines": { + "node": ">=4" + } + }, + "node_modules/ts-command-line-args/node_modules/table-layout": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/table-layout/-/table-layout-1.0.2.tgz", + "integrity": "sha512-qd/R7n5rQTRFi+Zf2sk5XVVd9UQl6ZkduPFC3S7WEGJAmetDTjY3qPN50eSKzwuzEyQKy5TN2TiZdkIjos2L6A==", + "dev": true, + "license": "MIT", + "dependencies": { + "array-back": "^4.0.1", + "deep-extend": "~0.6.0", + "typical": "^5.2.0", + "wordwrapjs": "^4.0.0" + }, + "engines": { + "node": ">=8.0.0" + } + }, + "node_modules/ts-command-line-args/node_modules/typical": { + "version": "5.2.0", + "resolved": "https://registry.npmjs.org/typical/-/typical-5.2.0.tgz", + "integrity": "sha512-dvdQgNDNJo+8B2uBQoqdb11eUCE1JQXhvjC/CZtgvZseVd5TYMXnq0+vuUemXbd/Se29cTaUuPX3YIc2xgbvIg==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=8" + } + }, + "node_modules/ts-command-line-args/node_modules/wordwrapjs": { + "version": "4.0.1", + "resolved": "https://registry.npmjs.org/wordwrapjs/-/wordwrapjs-4.0.1.tgz", + "integrity": "sha512-kKlNACbvHrkpIw6oPeYDSmdCTu2hdMHoyXLTcUKala++lx5Y+wjJ/e474Jqv5abnVmwxw08DiTuHmw69lJGksA==", + "dev": true, + "license": "MIT", + "dependencies": { + "reduce-flatten": "^2.0.0", + "typical": "^5.2.0" + }, + "engines": { + "node": ">=8.0.0" + } + }, "node_modules/ts-markdown-builder": { "version": "0.4.1", "resolved": "https://registry.npmjs.org/ts-markdown-builder/-/ts-markdown-builder-0.4.1.tgz", @@ -15934,6 +15568,7 @@ "integrity": "sha512-f0FFpIdcHgn8zcPSbf1dRevwt047YMnaiJM3u2w2RewrB+fob/zePZcrOyQoLMMO7aBIddLcQIEK5dYjkLnGrQ==", "dev": true, "license": "MIT", + "peer": true, "dependencies": { "@cspotcode/source-map-support": "^0.8.0", "@tsconfig/node10": "^1.0.7", @@ -16025,29 +15660,6 @@ "dev": true, "license": "0BSD" }, - "node_modules/tsutils": { - "version": "3.21.0", - "resolved": "https://registry.npmjs.org/tsutils/-/tsutils-3.21.0.tgz", - "integrity": "sha512-mHKK3iUXL+3UF6xL5k0PEhKRUBKPBCv/+RkEOpjRWxxx27KKRBmmA60A9pgOUvMi8GKhRMPEmjBRPzs2W7O1OA==", - "dev": true, - "license": "MIT", - "dependencies": { - "tslib": "^1.8.1" - }, - "engines": { - "node": ">= 6" - }, - "peerDependencies": { - "typescript": ">=2.8.0 || >= 3.2.0-dev || >= 3.3.0-dev || >= 3.4.0-dev || >= 3.5.0-dev || >= 3.6.0-dev || >= 3.6.0-beta || >= 3.7.0-dev || >= 3.7.0-beta" - } - }, - "node_modules/tsutils/node_modules/tslib": { - "version": "1.14.1", - "resolved": "https://registry.npmjs.org/tslib/-/tslib-1.14.1.tgz", - "integrity": "sha512-Xni35NKzjgMrwevysHTCArtLDpPvye8zV/0E4EyYn43P7/7qvQwPh9BGkHewbMulVntbigmcT7rdX3BNo9wRJg==", - "dev": true, - "license": "0BSD" - }, "node_modules/tunnel": { "version": "0.0.6", "resolved": "https://registry.npmjs.org/tunnel/-/tunnel-0.0.6.tgz", @@ -16340,6 +15952,7 @@ "integrity": "sha512-CWBzXQrc/qOkhidw1OzBTQuYRbfyxDXJMVJ1XNwUHGROVmuaeiEm3OslpZ1RV96d7SKKjZKrSJu3+t/xlw3R9A==", "dev": true, "license": "Apache-2.0", + "peer": true, "bin": { "tsc": "bin/tsc", "tsserver": "bin/tsserver" @@ -16348,6 +15961,30 @@ "node": ">=14.17" } }, + "node_modules/typescript-eslint": { + "version": "8.61.1", + "resolved": "https://registry.npmjs.org/typescript-eslint/-/typescript-eslint-8.61.1.tgz", + "integrity": "sha512-V7PayAfJokV3pEHgN7/v03D1SpujhRfQtYLbLIiBfDDncdg4PAiRBfoS4cnCANK4jmAPncczi59QO3afiXUlNw==", + "dev": true, + "license": "MIT", + "dependencies": { + "@typescript-eslint/eslint-plugin": "8.61.1", + "@typescript-eslint/parser": "8.61.1", + "@typescript-eslint/typescript-estree": "8.61.1", + "@typescript-eslint/utils": "8.61.1" + }, + "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 || ^10.0.0", + "typescript": ">=4.8.4 <6.1.0" + } + }, "node_modules/typical": { "version": "2.6.1", "resolved": "https://registry.npmjs.org/typical/-/typical-2.6.1.tgz", @@ -16453,9 +16090,9 @@ } }, "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==", + "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": { @@ -16463,9 +16100,9 @@ } }, "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==", + "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": { @@ -16506,6 +16143,7 @@ "dev": true, "hasInstallScript": true, "license": "MIT", + "peer": true, "dependencies": { "napi-postinstall": "^0.3.0" }, @@ -16535,9 +16173,9 @@ } }, "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==", + "version": "1.2.3", + "resolved": "https://registry.npmjs.org/update-browserslist-db/-/update-browserslist-db-1.2.3.tgz", + "integrity": "sha512-Js0m9cx+qOgDxo0eMiFGEueWztz+d4+M3rGlmKPT+T4IS/jP4ylw3Nwpu6cpTTP8R1MAC1kF4VbdLt3ARf209w==", "dev": true, "funding": [ { @@ -17054,6 +16692,7 @@ "integrity": "sha512-gzUt/qt81nXsFGKIFcC3YnfEAx5NkunCfnDlvuBSSFS02bcXu4Lmea0AFIUwbLWxWPx3d9p8S5QoaujKcNQxcQ==", "dev": true, "license": "MIT", + "peer": true, "funding": { "url": "https://github.com/sponsors/colinhacks" } diff --git a/package.json b/package.json index 3c4548d5d..7b5816d8d 100644 --- a/package.json +++ b/package.json @@ -54,10 +54,9 @@ "devDependencies": { "@actions/core": "^1.10.1", "@actions/github": "^6.0.1", + "@eslint/eslintrc": "^3.3.1", "@jest/globals": "^29.7.0", - "@lwc/eslint-plugin-lwc": "^3.3.0", "@ngneat/falso": "^7.3.0", - "@react-native-community/eslint-config": "^3.2.0", "@react-native/babel-preset": "0.76.3", "@react-native/polyfills": "^2.0.0", "@testing-library/react-native": "^13.2.0", @@ -65,22 +64,19 @@ "@types/jest": "^29.5.14", "@types/jsdoc-to-markdown": "^7.0.6", "@types/lodash": "^4.14.202", + "@types/lodash.bindall": "^4.4.9", "@types/node": "^20.11.5", "@types/react": "^18.2.14", "@types/react-native": "^0.70.0", "@types/underscore": "^1.11.15", - "@typescript-eslint/eslint-plugin": "^8.51.0", - "@typescript-eslint/parser": "^8.51.0", "@vercel/ncc": "0.38.1", "better-sqlite3": "^12.10.0", "date-fns": "^4.1.0", "eslint": "^9.39.2", - "eslint-config-expensify": "2.0.108", - "eslint-config-prettier": "^9.1.0", - "eslint-import-resolver-typescript": "^4.4.4", - "eslint-plugin-import": "^2.31.0", - "eslint-plugin-jsx-a11y": "^6.10.2", - "eslint-plugin-react": "^7.37.5", + "eslint-config-expensify": "4.0.6", + "eslint-config-prettier": "^10.1.5", + "eslint-import-resolver-typescript": "^4.4.5", + "eslint-seatbelt": "^0.1.3", "fake-indexeddb": "^6.2.5", "idb-keyval": "^6.2.1", "jest": "^29.7.0", diff --git a/tests/perf-test/Onyx.perf-test.ts b/tests/perf-test/Onyx.perf-test.ts index 196f728b6..8bb5b18c4 100644 --- a/tests/perf-test/Onyx.perf-test.ts +++ b/tests/perf-test/Onyx.perf-test.ts @@ -2,7 +2,6 @@ import {measureAsyncFunction} from 'reassure'; import type {OnyxKey, OnyxUpdate} from '../../lib'; import Onyx from '../../lib'; import createRandomReportAction, {getRandomReportActions} from '../utils/collections/reportActions'; -import type GenericCollection from '../utils/GenericCollection'; import OnyxUtils from '../../lib/OnyxUtils'; import createDeferredTask from '../../lib/createDeferredTask'; import alternateLists from '../utils/alternateLists'; @@ -43,7 +42,7 @@ describe('Onyx', () => { describe('set', () => { test('10k calls with heavy objects', async () => { await measureAsyncFunction( - () => Promise.all(Object.values(mockedReportActionsMap).map((reportAction) => Onyx.set(`${collectionKey}${reportAction.reportActionID}`, reportAction))), + () => Promise.all(Object.values(mockedReportActionsMap).map((reportAction) => Onyx.set(`${collectionKey}${String(reportAction.reportActionID)}`, reportAction))), {afterEach: clearOnyxAfterEachMeasure}, ); }); @@ -51,7 +50,9 @@ describe('Onyx', () => { describe('multiSet', () => { test('one call with 10k heavy objects', async () => { - await measureAsyncFunction(() => Onyx.multiSet(mockedReportActionsMap), {afterEach: clearOnyxAfterEachMeasure}); + await measureAsyncFunction(() => Onyx.multiSet(mockedReportActionsMap), { + afterEach: clearOnyxAfterEachMeasure, + }); }); }); @@ -72,9 +73,7 @@ describe('Onyx', () => { describe('mergeCollection', () => { test('one call with 10k heavy objects', async () => { - const changedReportActions = Object.fromEntries( - Object.entries(mockedReportActionsMap).map(([k, v]) => [k, createRandomReportAction(Number(v.reportActionID))] as const), - ) as GenericCollection; + const changedReportActions = Object.fromEntries(Object.entries(mockedReportActionsMap).map(([k, v]) => [k, createRandomReportAction(Number(v.reportActionID))] as const)); await measureAsyncFunction(() => Onyx.mergeCollection(collectionKey, changedReportActions), { beforeEach: async () => { await Onyx.multiSet(mockedReportActionsMap); @@ -86,9 +85,7 @@ describe('Onyx', () => { describe('setCollection', () => { test('one call with 10k heavy objects', async () => { - const changedReportActions = Object.fromEntries( - Object.entries(mockedReportActionsMap).map(([k, v]) => [k, createRandomReportAction(Number(v.reportActionID))] as const), - ) as GenericCollection; + const changedReportActions = Object.fromEntries(Object.entries(mockedReportActionsMap).map(([k, v]) => [k, createRandomReportAction(Number(v.reportActionID))] as const)); await measureAsyncFunction(() => Onyx.setCollection(collectionKey, changedReportActions), { beforeEach: async () => { await Onyx.multiSet(mockedReportActionsMap); @@ -104,11 +101,23 @@ describe('Onyx', () => { const sets = Object.entries(changedReportActions) .filter(([, v]) => Number(v.reportActionID) % 2 === 0) - .map(([k, v]): OnyxUpdate => ({key: k, onyxMethod: Onyx.METHOD.SET, value: v})); + .map( + ([k, v]): OnyxUpdate => ({ + key: k, + onyxMethod: Onyx.METHOD.SET, + value: v, + }), + ); const merges = Object.entries(changedReportActions) .filter(([, v]) => Number(v.reportActionID) % 2 !== 0) - .map(([k, v]): OnyxUpdate => ({key: k, onyxMethod: Onyx.METHOD.MERGE, value: v})); + .map( + ([k, v]): OnyxUpdate => ({ + key: k, + onyxMethod: Onyx.METHOD.MERGE, + value: v, + }), + ); const updates = alternateLists(sets, merges) as Array>; diff --git a/tests/perf-test/OnyxCache.perf-test.ts b/tests/perf-test/OnyxCache.perf-test.ts index a100a4382..27f99b5ee 100644 --- a/tests/perf-test/OnyxCache.perf-test.ts +++ b/tests/perf-test/OnyxCache.perf-test.ts @@ -1,8 +1,8 @@ import {measureAsyncFunction, measureFunction} from 'reassure'; import type OnyxCache from '../../lib/OnyxCache'; import createRandomReportAction, {getRandomReportActions} from '../utils/collections/reportActions'; -import type GenericCollection from '../utils/GenericCollection'; import {TASK} from '../../lib/OnyxCache'; +import getAtIndex from '../utils/getAtIndex'; const ONYXKEYS = { TEST_KEY: 'test', @@ -23,6 +23,8 @@ const ONYXKEYS = { const collectionKey = ONYXKEYS.COLLECTION.TEST_KEY; const mockedReportActionsMap = getRandomReportActions(collectionKey); const mockedReportActionsKeys = Object.keys(mockedReportActionsMap); +const firstMockedReportActionKey = getAtIndex(mockedReportActionsKeys, 0); +const mockedReportActionKeyAt1000 = getAtIndex(mockedReportActionsKeys, 1000); let cache: typeof OnyxCache; @@ -55,7 +57,7 @@ describe('OnyxCache', () => { describe('addKey', () => { test('one call adding one key', async () => { - await measureFunction(() => cache.addKey(mockedReportActionsKeys[0]), { + await measureFunction(() => cache.addKey(firstMockedReportActionKey), { beforeEach: resetCacheBeforeEachMeasure, }); }); @@ -63,7 +65,7 @@ describe('OnyxCache', () => { describe('addNullishStorageKey', () => { test('one call adding one key', async () => { - await measureFunction(() => cache.addNullishStorageKey(mockedReportActionsKeys[0]), { + await measureFunction(() => cache.addNullishStorageKey(firstMockedReportActionKey), { beforeEach: resetCacheBeforeEachMeasure, }); }); @@ -71,7 +73,7 @@ describe('OnyxCache', () => { describe('hasNullishStorageKey', () => { test('one call checking one key among 10k ones', async () => { - await measureFunction(() => cache.hasNullishStorageKey(mockedReportActionsKeys[0]), { + await measureFunction(() => cache.hasNullishStorageKey(firstMockedReportActionKey), { beforeEach: async () => { resetCacheBeforeEachMeasure(); cache.setAllKeys(mockedReportActionsKeys); @@ -93,7 +95,7 @@ describe('OnyxCache', () => { describe('hasCacheForKey', () => { test('one call checking one key among 10k ones', async () => { - await measureFunction(() => cache.hasCacheForKey(mockedReportActionsKeys[0]), { + await measureFunction(() => cache.hasCacheForKey(firstMockedReportActionKey), { beforeEach: async () => { resetCacheBeforeEachMeasure(); cache.setAllKeys(mockedReportActionsKeys); @@ -104,7 +106,7 @@ describe('OnyxCache', () => { describe('get', () => { test('one call getting one key among 10k ones', async () => { - await measureFunction(() => cache.get(mockedReportActionsKeys[0]), { + await measureFunction(() => cache.get(firstMockedReportActionKey), { beforeEach: async () => { resetCacheBeforeEachMeasure(); for (const [k, v] of Object.entries(mockedReportActionsMap)) cache.set(k, v); @@ -115,8 +117,8 @@ describe('OnyxCache', () => { describe('set', () => { test('one call setting one key', async () => { - const value = mockedReportActionsMap[mockedReportActionsKeys[0]]; - await measureFunction(() => cache.set(mockedReportActionsKeys[0], value), { + const value = mockedReportActionsMap[firstMockedReportActionKey]; + await measureFunction(() => cache.set(firstMockedReportActionKey, value), { beforeEach: resetCacheBeforeEachMeasure, }); }); @@ -124,7 +126,7 @@ describe('OnyxCache', () => { describe('drop', () => { test('one call dropping one key among 10k ones', async () => { - await measureFunction(() => cache.drop(mockedReportActionsKeys[1000]), { + await measureFunction(() => cache.drop(mockedReportActionKeyAt1000), { beforeEach: async () => { resetCacheBeforeEachMeasure(); for (const [k, v] of Object.entries(mockedReportActionsMap)) cache.set(k, v); @@ -135,9 +137,7 @@ describe('OnyxCache', () => { describe('merge', () => { test('one call merging 10k keys', async () => { - const changedReportActions = Object.fromEntries( - Object.entries(mockedReportActionsMap).map(([k, v]) => [k, createRandomReportAction(Number(v.reportActionID))] as const), - ) as GenericCollection; + const changedReportActions = Object.fromEntries(Object.entries(mockedReportActionsMap).map(([k, v]) => [k, createRandomReportAction(Number(v.reportActionID))] as const)); await measureFunction(() => cache.merge(changedReportActions), { beforeEach: async () => { @@ -150,10 +150,10 @@ describe('OnyxCache', () => { describe('hasPendingTask', () => { test('one call checking one task', async () => { - await measureFunction(() => cache.hasPendingTask(`${TASK.GET}:${mockedReportActionsKeys[0]}`), { + await measureFunction(() => cache.hasPendingTask(`${TASK.GET}:${firstMockedReportActionKey}`), { beforeEach: async () => { resetCacheBeforeEachMeasure(); - cache.captureTask(`${TASK.GET}:${mockedReportActionsKeys[0]}`, Promise.resolve()); + cache.captureTask(`${TASK.GET}:${firstMockedReportActionKey}`, Promise.resolve()); }, }); }); @@ -161,10 +161,10 @@ describe('OnyxCache', () => { describe('getTaskPromise', () => { test('one call checking one task', async () => { - await measureAsyncFunction(() => cache.getTaskPromise(`${TASK.GET}:${mockedReportActionsKeys[0]}`) as Promise, { + await measureAsyncFunction(() => cache.getTaskPromise(`${TASK.GET}:${firstMockedReportActionKey}`)!, { beforeEach: async () => { resetCacheBeforeEachMeasure(); - cache.captureTask(`${TASK.GET}:${mockedReportActionsKeys[0]}`, Promise.resolve()); + cache.captureTask(`${TASK.GET}:${firstMockedReportActionKey}`, Promise.resolve()); }, }); }); @@ -172,14 +172,14 @@ describe('OnyxCache', () => { describe('captureTask', () => { test('one call capturing one task', async () => { - await measureAsyncFunction(() => cache.captureTask(`${TASK.GET}:${mockedReportActionsKeys[0]}`, Promise.resolve()), { + await measureAsyncFunction(() => cache.captureTask(`${TASK.GET}:${firstMockedReportActionKey}`, Promise.resolve()), { beforeEach: resetCacheBeforeEachMeasure, }); }); }); describe('hasValueChanged', () => { - const key = mockedReportActionsKeys[0]; + const key = firstMockedReportActionKey; const reportAction = mockedReportActionsMap[key]; const changedReportAction = createRandomReportAction(Number(reportAction.reportActionID)); diff --git a/tests/perf-test/OnyxConnectionManager.perf-test.ts b/tests/perf-test/OnyxConnectionManager.perf-test.ts index fc3e5c519..83bf5f11a 100644 --- a/tests/perf-test/OnyxConnectionManager.perf-test.ts +++ b/tests/perf-test/OnyxConnectionManager.perf-test.ts @@ -4,6 +4,8 @@ import type {Connection} from '../../lib/OnyxConnectionManager'; import connectionManager from '../../lib/OnyxConnectionManager'; import createDeferredTask from '../../lib/createDeferredTask'; import {getRandomReportActions} from '../utils/collections/reportActions'; +import getAtIndex from '../utils/getAtIndex'; +import {getOnyxConnectionManagerTestHarness} from '../utils/onyxConnectionManagerTestHarness'; const ONYXKEYS = { TEST_KEY: 'test', @@ -26,13 +28,13 @@ const ONYXKEYS = { const collectionKey = ONYXKEYS.COLLECTION.TEST_KEY; const mockedReportActionsMap = getRandomReportActions(collectionKey); const mockedReportActionsKeys = Object.keys(mockedReportActionsMap); +const firstMockedReportActionKey = getAtIndex(mockedReportActionsKeys, 0); // We need access to some internal properties of `connectionManager` during the tests but they are private, // so this workaround allows us to have access to them. -// eslint-disable-next-line dot-notation -const generateConnectionID = connectionManager['generateConnectionID']; -// eslint-disable-next-line dot-notation -const fireCallbacks = connectionManager['fireCallbacks']; +const connectionManagerTestHarness = getOnyxConnectionManagerTestHarness(); +const generateConnectionID = connectionManagerTestHarness.generateConnectionID.bind(connectionManager); +const fireCallbacks = connectionManagerTestHarness.fireCallbacks.bind(connectionManager); const resetConectionManagerAfterEachMeasure = () => { connectionManager.disconnectAll(); @@ -54,7 +56,7 @@ describe('OnyxConnectionManager', () => { describe('generateConnectionID', () => { test('one call', async () => { - await measureFunction(() => generateConnectionID({key: mockedReportActionsKeys[0]}), { + await measureFunction(() => generateConnectionID({key: firstMockedReportActionKey}), { afterEach: resetConectionManagerAfterEachMeasure, }); }); @@ -66,9 +68,15 @@ describe('OnyxConnectionManager', () => { await measureFunction(() => fireCallbacks(connectionID), { beforeEach: async () => { - connectionID = connectionManager.connect({key: mockedReportActionsKeys[0], callback: jest.fn()}).id; + connectionID = connectionManager.connect({ + key: firstMockedReportActionKey, + callback: jest.fn(), + }).id; for (let i = 0; i < 9999; i++) { - connectionManager.connect({key: mockedReportActionsKeys[0], callback: jest.fn()}); + connectionManager.connect({ + key: firstMockedReportActionKey, + callback: jest.fn(), + }); } }, afterEach: async () => { @@ -85,7 +93,7 @@ describe('OnyxConnectionManager', () => { async () => { const callback = createDeferredTask(); connectionManager.connect({ - key: mockedReportActionsKeys[0], + key: firstMockedReportActionKey, callback: () => { callback.resolve?.(); }, @@ -108,11 +116,14 @@ describe('OnyxConnectionManager', () => { await measureFunction( () => { - connectionManager.disconnect(connection as Connection); + connectionManager.disconnect(connection!); }, { beforeEach: async () => { - connection = connectionManager.connect({key: mockedReportActionsKeys[0], callback: jest.fn()}); + connection = connectionManager.connect({ + key: firstMockedReportActionKey, + callback: jest.fn(), + }); }, afterEach: async () => { resetConectionManagerAfterEachMeasure(); @@ -128,7 +139,10 @@ describe('OnyxConnectionManager', () => { await measureFunction(() => connectionManager.disconnectAll(), { beforeEach: async () => { for (let i = 0; i < 10000; i++) { - connectionManager.connect({key: mockedReportActionsKeys[0], callback: jest.fn()}); + connectionManager.connect({ + key: firstMockedReportActionKey, + callback: jest.fn(), + }); } }, afterEach: async () => { diff --git a/tests/perf-test/OnyxSnapshotCache.perf-test.ts b/tests/perf-test/OnyxSnapshotCache.perf-test.ts index 02f8ed65d..7d39d391d 100644 --- a/tests/perf-test/OnyxSnapshotCache.perf-test.ts +++ b/tests/perf-test/OnyxSnapshotCache.perf-test.ts @@ -2,6 +2,7 @@ import {measureFunction} from 'reassure'; import {OnyxSnapshotCache} from '../../lib/OnyxSnapshotCache'; import type {UseOnyxOptions, UseOnyxResult, UseOnyxSelector} from '../../lib/useOnyx'; import type {OnyxKey} from '../../lib'; +import getAtIndex from '../utils/getAtIndex'; // Define types for test data type MockData = { @@ -24,7 +25,12 @@ const ONYXKEYS = { // Mock selector functions const simpleSelector: UseOnyxSelector = (data) => (data as MockData | undefined)?.value; -type ComplexSelectorResult = {id?: number; name?: string; computed: number; formatted: string}; +type ComplexSelectorResult = { + id?: number; + name?: string; + computed: number; + formatted: string; +}; const complexSelector: UseOnyxSelector = (data) => { const mockData = data as MockData | undefined; return { @@ -114,7 +120,10 @@ describe('OnyxSnapshotCache', () => { () => { for (let i = 0; i < 1000; i++) { const selector: UseOnyxSelector = (data) => ((data as MockData | undefined)?.field ?? '') + i; - const options: UseOnyxOptions = {...selectorOptions, selector}; + const options: UseOnyxOptions = { + ...selectorOptions, + selector, + }; cache.registerConsumer(ONYXKEYS.TEST_KEY, options); } }, @@ -171,7 +180,7 @@ describe('OnyxSnapshotCache', () => { // Pre-populate cache with 1000 entries for (let i = 0; i < 1000; i++) { const key = `test_key_${i}`; - const result = mockResults[i]; + const result = getAtIndex(mockResults, i); cache.setCachedResult(key, `cache_key_${i}`, result); } // Set our target entry diff --git a/tests/perf-test/OnyxUtils.perf-test.ts b/tests/perf-test/OnyxUtils.perf-test.ts index 87c7dd7cb..13ffaa060 100644 --- a/tests/perf-test/OnyxUtils.perf-test.ts +++ b/tests/perf-test/OnyxUtils.perf-test.ts @@ -7,7 +7,6 @@ import StorageMock from '../../lib/storage'; import OnyxCache from '../../lib/OnyxCache'; import OnyxKeys from '../../lib/OnyxKeys'; import OnyxUtils, {clearOnyxUtilsInternals} from '../../lib/OnyxUtils'; -import type GenericCollection from '../utils/GenericCollection'; import type {OnyxUpdate} from '../../lib/Onyx'; import createDeferredTask from '../../lib/createDeferredTask'; import type {OnyxEntry, OnyxInputKeyValueMapping, OnyxKey, RetriableOnyxOperation} from '../../lib/types'; @@ -288,10 +287,10 @@ describe('OnyxUtils', () => { test('one call with 10k heavy objects', async () => { const changedReportActions = Object.fromEntries( Object.entries(mockedReportActionsMap).map(([k, v]) => [k, randBoolean() ? v : createRandomReportAction(Number(v.reportActionID))] as const), - ) as GenericCollection; + ); await measureAsyncFunction(() => OnyxUtils.partialSetCollection({collectionKey, collection: changedReportActions}), { beforeEach: async () => { - await Onyx.setCollection(collectionKey, mockedReportActionsMap as GenericCollection); + await Onyx.setCollection(collectionKey, mockedReportActionsMap); }, afterEach: clearOnyxAfterEachMeasure, }); @@ -302,9 +301,7 @@ describe('OnyxUtils', () => { test('one call with 10k heavy objects to update 10k subscribers', async () => { const subscriptionMap = new Map(); - const changedReportActions = Object.fromEntries( - Object.entries(mockedReportActionsMap).map(([k, v]) => [k, createRandomReportAction(Number(v.reportActionID))] as const), - ) as GenericCollection; + const changedReportActions = Object.fromEntries(Object.entries(mockedReportActionsMap).map(([k, v]) => [k, createRandomReportAction(Number(v.reportActionID))] as const)); await measureFunction(() => OnyxUtils.keysChanged(collectionKey, changedReportActions, mockedReportActionsMap), { beforeEach: async () => { @@ -519,9 +516,7 @@ describe('OnyxUtils', () => { describe('initializeWithDefaultKeyStates', () => { test('one call initializing 10k heavy objects', async () => { - const changedReportActions = Object.fromEntries( - Object.entries(mockedReportActionsMap).map(([k, v]) => [k, createRandomReportAction(Number(v.reportActionID))] as const), - ) as GenericCollection; + const changedReportActions = Object.fromEntries(Object.entries(mockedReportActionsMap).map(([k, v]) => [k, createRandomReportAction(Number(v.reportActionID))] as const)); await measureAsyncFunction(() => OnyxUtils.initializeWithDefaultKeyStates(), { beforeEach: async () => { @@ -575,7 +570,7 @@ describe('OnyxUtils', () => { describe('isValidNonEmptyCollectionForMerge', () => { test('one call', async () => { - await measureFunction(() => OnyxUtils.isValidNonEmptyCollectionForMerge(mockedReportActionsMap as GenericCollection)); + await measureFunction(() => OnyxUtils.isValidNonEmptyCollectionForMerge(mockedReportActionsMap)); }); }); diff --git a/tests/unit/DevToolsTest.ts b/tests/unit/DevToolsTest.ts index 60664c5b3..444c594c5 100644 --- a/tests/unit/DevToolsTest.ts +++ b/tests/unit/DevToolsTest.ts @@ -1,10 +1,17 @@ -/* eslint-disable dot-notation */ import Onyx from '../../lib'; import {getDevToolsInstance} from '../../lib/DevTools'; -import type {DevtoolsConnection, RealDevTools as RealDevToolsType} from '../../lib/DevTools'; +import type {DevtoolsConnection} from '../../lib/DevTools'; import RealDevTools from '../../lib/DevTools/RealDevTools'; import utils from '../../lib/utils'; -import type GenericCollection from '../utils/GenericCollection'; + +type RealDevToolsTestHarness = { + state: Record; + defaultState: Record; +}; + +function getDevToolsTestHarness(): RealDevToolsTestHarness { + return getDevToolsInstance() as unknown as RealDevToolsTestHarness; +} const ONYX_KEYS = { NUM_KEY: 'numKey', @@ -26,13 +33,16 @@ const initialKeyStates = { const exampleCollection = { [`${ONYX_KEYS.COLLECTION.NUM_KEY}1`]: 1, [`${ONYX_KEYS.COLLECTION.NUM_KEY}2`]: 2, -} as GenericCollection; +}; const exampleObject = {name: 'Pedro'}; const mergedCollection = {...initialKeyStates, ...exampleCollection}; -const mergedObject = {...initialKeyStates, [ONYX_KEYS.OBJECT_KEY]: {...exampleObject, id: 42}}; +const mergedObject = { + ...initialKeyStates, + [ONYX_KEYS.OBJECT_KEY]: {...exampleObject, id: 42}, +}; describe('DevTools', () => { let initMock: jest.Mock; @@ -45,7 +55,10 @@ describe('DevTools', () => { // Mock the connectViaExtension method to return our mock connection // This needs to happen before RealDevTools is instantiated - const mockConnection = {init: initMock, send: sendMock} as unknown as DevtoolsConnection; + const mockConnection = { + init: initMock, + send: sendMock, + } as unknown as DevtoolsConnection; jest.spyOn(RealDevTools.prototype, 'connectViaExtension').mockReturnValue(mockConnection); Onyx.init({ @@ -64,60 +77,87 @@ describe('DevTools', () => { expect(initMock).toHaveBeenCalledWith(initialKeyStates); }); it('Sets the default state correctly', () => { - const devToolsInstance = getDevToolsInstance() as RealDevToolsType; - expect(devToolsInstance['defaultState']).toEqual(initialKeyStates); + const devToolsInstance = getDevToolsTestHarness(); + expect(devToolsInstance.defaultState).toEqual(initialKeyStates); }); it('Sets the internal state correctly', () => { - const devToolsInstance = getDevToolsInstance() as RealDevToolsType; - expect(devToolsInstance['state']).toEqual(initialKeyStates); + const devToolsInstance = getDevToolsTestHarness(); + expect(devToolsInstance.state).toEqual(initialKeyStates); }); }); describe('Set', () => { it('Sends the set state correctly to the extension', async () => { await Onyx.set(ONYX_KEYS.SOME_KEY, 3); - expect(sendMock).toHaveBeenCalledWith({payload: 3, type: utils.formatActionName(Onyx.METHOD.SET, ONYX_KEYS.SOME_KEY)}, {...initialKeyStates, [ONYX_KEYS.SOME_KEY]: 3}); + expect(sendMock).toHaveBeenCalledWith( + { + payload: 3, + type: utils.formatActionName(Onyx.METHOD.SET, ONYX_KEYS.SOME_KEY), + }, + {...initialKeyStates, [ONYX_KEYS.SOME_KEY]: 3}, + ); }); it('Sets the internal state correctly', async () => { await Onyx.set(ONYX_KEYS.SOME_KEY, 3); - const devToolsInstance = getDevToolsInstance() as RealDevToolsType; - expect(devToolsInstance['state']).toEqual({...initialKeyStates, [ONYX_KEYS.SOME_KEY]: 3}); + const devToolsInstance = getDevToolsTestHarness(); + expect(devToolsInstance.state).toEqual({ + ...initialKeyStates, + [ONYX_KEYS.SOME_KEY]: 3, + }); }); }); describe('Merge', () => { it('Sends the merged state correctly to the extension', async () => { await Onyx.merge(ONYX_KEYS.OBJECT_KEY, exampleObject); - expect(sendMock).toHaveBeenCalledWith({payload: exampleObject, type: utils.formatActionName(Onyx.METHOD.MERGE, ONYX_KEYS.OBJECT_KEY)}, mergedObject); + expect(sendMock).toHaveBeenCalledWith( + { + payload: exampleObject, + type: utils.formatActionName(Onyx.METHOD.MERGE, ONYX_KEYS.OBJECT_KEY), + }, + mergedObject, + ); }); it('Sets the internal state correctly', async () => { await Onyx.merge(ONYX_KEYS.OBJECT_KEY, exampleObject); - const devToolsInstance = getDevToolsInstance() as RealDevToolsType; - expect(devToolsInstance['state']).toEqual(mergedObject); + const devToolsInstance = getDevToolsTestHarness(); + expect(devToolsInstance.state).toEqual(mergedObject); }); }); describe('MergeCollection', () => { it('Sends the mergecollection state correctly to the extension', async () => { await Onyx.mergeCollection(ONYX_KEYS.COLLECTION.NUM_KEY, exampleCollection); - expect(sendMock).toHaveBeenCalledWith({payload: exampleCollection, type: utils.formatActionName(Onyx.METHOD.MERGE_COLLECTION)}, mergedCollection); + expect(sendMock).toHaveBeenCalledWith( + { + payload: exampleCollection, + type: utils.formatActionName(Onyx.METHOD.MERGE_COLLECTION), + }, + mergedCollection, + ); }); it('Sets the internal state correctly', async () => { await Onyx.mergeCollection(ONYX_KEYS.COLLECTION.NUM_KEY, exampleCollection); - const devToolsInstance = getDevToolsInstance() as RealDevToolsType; - expect(devToolsInstance['state']).toEqual(mergedCollection); + const devToolsInstance = getDevToolsTestHarness(); + expect(devToolsInstance.state).toEqual(mergedCollection); }); }); describe('MultiSet', () => { it('Sends the multiset state correctly to the extension', async () => { await Onyx.multiSet(exampleCollection); - expect(sendMock).toHaveBeenCalledWith({payload: exampleCollection, type: utils.formatActionName(Onyx.METHOD.MULTI_SET)}, mergedCollection); + expect(sendMock).toHaveBeenCalledWith( + { + payload: exampleCollection, + type: utils.formatActionName(Onyx.METHOD.MULTI_SET), + }, + mergedCollection, + ); }); it('Sets the internal state correctly', async () => { await Onyx.multiSet(exampleCollection); - const devToolsInstance = getDevToolsInstance() as RealDevToolsType; - expect(devToolsInstance['state']).toEqual(mergedCollection); + const devToolsInstance = getDevToolsTestHarness(); + expect(devToolsInstance.state).toEqual(mergedCollection); }); }); @@ -134,15 +174,21 @@ describe('DevTools', () => { it('Clears internal state correctly', async () => { await Onyx.merge(ONYX_KEYS.NUM_KEY, 2); await Onyx.clear([ONYX_KEYS.NUM_KEY]); - const devToolsInstance = getDevToolsInstance() as RealDevToolsType; - expect(devToolsInstance['state']).toEqual({...initialKeyStates, [ONYX_KEYS.NUM_KEY]: 2}); + const devToolsInstance = getDevToolsTestHarness(); + expect(devToolsInstance.state).toEqual({ + ...initialKeyStates, + [ONYX_KEYS.NUM_KEY]: 2, + }); }); it('Preserves collection member keys when a collection key is passed to keysToPreserve', async () => { await Onyx.mergeCollection(ONYX_KEYS.COLLECTION.NUM_KEY, exampleCollection); await Onyx.clear([ONYX_KEYS.COLLECTION.NUM_KEY]); - const devToolsInstance = getDevToolsInstance() as RealDevToolsType; - expect(devToolsInstance['state']).toEqual({...initialKeyStates, ...exampleCollection}); + const devToolsInstance = getDevToolsTestHarness(); + expect(devToolsInstance.state).toEqual({ + ...initialKeyStates, + ...exampleCollection, + }); }); }); }); diff --git a/tests/unit/OnyxConnectionManagerTest.ts b/tests/unit/OnyxConnectionManagerTest.ts index 664c96f28..857324f62 100644 --- a/tests/unit/OnyxConnectionManagerTest.ts +++ b/tests/unit/OnyxConnectionManagerTest.ts @@ -3,17 +3,15 @@ import Onyx from '../../lib'; import type {Connection} from '../../lib/OnyxConnectionManager'; import connectionManager from '../../lib/OnyxConnectionManager'; import StorageMock from '../../lib/storage'; -import type GenericCollection from '../utils/GenericCollection'; +import {getOnyxConnectionManagerTestHarness} from '../utils/onyxConnectionManagerTestHarness'; import waitForPromisesToResolve from '../utils/waitForPromisesToResolve'; // We need access to some internal properties of `connectionManager` during the tests but they are private, // so this workaround allows us to have access to them. -// eslint-disable-next-line dot-notation -const connectionsMap = connectionManager['connectionsMap']; -// eslint-disable-next-line dot-notation -const generateConnectionID = connectionManager['generateConnectionID']; -// eslint-disable-next-line dot-notation -const getSessionID = () => connectionManager['sessionID']; +const testConnectionManager = getOnyxConnectionManagerTestHarness(); +const connectionsMap = testConnectionManager.connectionsMap; +const generateConnectionID = testConnectionManager.generateConnectionID.bind(connectionManager); +const getSessionID = () => testConnectionManager.sessionID; const ONYXKEYS = { TEST_KEY: 'test', @@ -43,13 +41,21 @@ describe('OnyxConnectionManager', () => { }); it('should generate a stable, reusable connection ID for collection keys', async () => { - const connectionID = generateConnectionID({key: ONYXKEYS.COLLECTION.TEST_KEY}); + const connectionID = generateConnectionID({ + key: ONYXKEYS.COLLECTION.TEST_KEY, + }); expect(connectionID).toEqual(`onyxKey=${ONYXKEYS.COLLECTION.TEST_KEY},sessionID=${getSessionID()}`); }); it('should generate unique connection IDs if certain options are passed', async () => { - const connectionID1 = generateConnectionID({key: ONYXKEYS.TEST_KEY, reuseConnection: false}); - const connectionID2 = generateConnectionID({key: ONYXKEYS.TEST_KEY, reuseConnection: false}); + const connectionID1 = generateConnectionID({ + key: ONYXKEYS.TEST_KEY, + reuseConnection: false, + }); + const connectionID2 = generateConnectionID({ + key: ONYXKEYS.TEST_KEY, + reuseConnection: false, + }); expect(connectionID1.startsWith(`onyxKey=${ONYXKEYS.TEST_KEY},sessionID=${getSessionID()},uniqueID=`)).toBeTruthy(); expect(connectionID2.startsWith(`onyxKey=${ONYXKEYS.TEST_KEY},sessionID=${getSessionID()},uniqueID=`)).toBeTruthy(); expect(connectionID1).not.toEqual(connectionID2); @@ -69,7 +75,10 @@ describe('OnyxConnectionManager', () => { await StorageMock.setItem(ONYXKEYS.TEST_KEY, 'test'); const callback1 = jest.fn(); - const connection = connectionManager.connect({key: ONYXKEYS.TEST_KEY, callback: callback1}); + const connection = connectionManager.connect({ + key: ONYXKEYS.TEST_KEY, + callback: callback1, + }); expect(connectionsMap.has(connection.id)).toBeTruthy(); @@ -87,10 +96,16 @@ describe('OnyxConnectionManager', () => { await StorageMock.setItem(ONYXKEYS.TEST_KEY, 'test'); const callback1 = jest.fn(); - const connection1 = connectionManager.connect({key: ONYXKEYS.TEST_KEY, callback: callback1}); + const connection1 = connectionManager.connect({ + key: ONYXKEYS.TEST_KEY, + callback: callback1, + }); const callback2 = jest.fn(); - const connection2 = connectionManager.connect({key: ONYXKEYS.TEST_KEY, callback: callback2}); + const connection2 = connectionManager.connect({ + key: ONYXKEYS.TEST_KEY, + callback: callback2, + }); expect(connection1.id).toEqual(connection2.id); expect(connectionsMap.size).toEqual(1); @@ -115,17 +130,23 @@ describe('OnyxConnectionManager', () => { const collection = { [`${ONYXKEYS.COLLECTION.TEST_KEY}entry1`]: obj1, [`${ONYXKEYS.COLLECTION.TEST_KEY}entry2`]: obj2, - } as GenericCollection; + }; await StorageMock.multiSet([ [`${ONYXKEYS.COLLECTION.TEST_KEY}entry1`, obj1], [`${ONYXKEYS.COLLECTION.TEST_KEY}entry2`, obj2], ]); const callback1 = jest.fn(); - const connection1 = connectionManager.connect({key: ONYXKEYS.COLLECTION.TEST_KEY, callback: callback1}); + const connection1 = connectionManager.connect({ + key: ONYXKEYS.COLLECTION.TEST_KEY, + callback: callback1, + }); const callback2 = jest.fn(); - const connection2 = connectionManager.connect({key: ONYXKEYS.COLLECTION.TEST_KEY, callback: callback2}); + const connection2 = connectionManager.connect({ + key: ONYXKEYS.COLLECTION.TEST_KEY, + callback: callback2, + }); // Collection-root connections are now always snapshot mode and are reused. expect(connection1.id).toEqual(connection2.id); @@ -148,7 +169,10 @@ describe('OnyxConnectionManager', () => { await StorageMock.setItem(ONYXKEYS.TEST_KEY, 'test'); const callback1 = jest.fn(); - connectionManager.connect({key: ONYXKEYS.TEST_KEY, callback: callback1}); + connectionManager.connect({ + key: ONYXKEYS.TEST_KEY, + callback: callback1, + }); await act(async () => waitForPromisesToResolve()); @@ -156,13 +180,22 @@ describe('OnyxConnectionManager', () => { expect(callback1).toHaveBeenCalledWith('test', ONYXKEYS.TEST_KEY); const callback2 = jest.fn(); - connectionManager.connect({key: ONYXKEYS.TEST_KEY, callback: callback2}); + connectionManager.connect({ + key: ONYXKEYS.TEST_KEY, + callback: callback2, + }); const callback3 = jest.fn(); - connectionManager.connect({key: ONYXKEYS.TEST_KEY, callback: callback3}); + connectionManager.connect({ + key: ONYXKEYS.TEST_KEY, + callback: callback3, + }); const callback4 = jest.fn(); - connectionManager.connect({key: ONYXKEYS.TEST_KEY, callback: callback4}); + connectionManager.connect({ + key: ONYXKEYS.TEST_KEY, + callback: callback4, + }); await act(async () => waitForPromisesToResolve()); @@ -178,7 +211,10 @@ describe('OnyxConnectionManager', () => { await StorageMock.setItem(ONYXKEYS.TEST_KEY, 'test'); const callback1 = jest.fn(); - connectionManager.connect({key: ONYXKEYS.TEST_KEY, callback: callback1}); + connectionManager.connect({ + key: ONYXKEYS.TEST_KEY, + callback: callback1, + }); await act(async () => waitForPromisesToResolve()); @@ -205,10 +241,17 @@ describe('OnyxConnectionManager', () => { await StorageMock.setItem(ONYXKEYS.TEST_KEY, 'test'); const callback1 = jest.fn(); - const connection1 = connectionManager.connect({key: ONYXKEYS.TEST_KEY, callback: callback1}); + const connection1 = connectionManager.connect({ + key: ONYXKEYS.TEST_KEY, + callback: callback1, + }); const callback2 = jest.fn(); - const connection2 = connectionManager.connect({key: ONYXKEYS.TEST_KEY, reuseConnection: false, callback: callback2}); + const connection2 = connectionManager.connect({ + key: ONYXKEYS.TEST_KEY, + reuseConnection: false, + callback: callback2, + }); expect(connection1.id).not.toEqual(connection2.id); expect(connectionsMap.size).toEqual(2); @@ -218,24 +261,39 @@ describe('OnyxConnectionManager', () => { it('should reuse the connection to the same collection key and deliver the whole collection object to all subscribers', async () => { const collection = { - [`${ONYXKEYS.COLLECTION.TEST_KEY}entry1`]: {id: 'entry1_id', name: 'entry1_name'}, - [`${ONYXKEYS.COLLECTION.TEST_KEY}entry2`]: {id: 'entry2_id', name: 'entry2_name'}, - [`${ONYXKEYS.COLLECTION.TEST_KEY}entry3`]: {id: 'entry3_id', name: 'entry3_name'}, + [`${ONYXKEYS.COLLECTION.TEST_KEY}entry1`]: { + id: 'entry1_id', + name: 'entry1_name', + }, + [`${ONYXKEYS.COLLECTION.TEST_KEY}entry2`]: { + id: 'entry2_id', + name: 'entry2_name', + }, + [`${ONYXKEYS.COLLECTION.TEST_KEY}entry3`]: { + id: 'entry3_id', + name: 'entry3_name', + }, }; - Onyx.mergeCollection(ONYXKEYS.COLLECTION.TEST_KEY, collection as GenericCollection); + Onyx.mergeCollection(ONYXKEYS.COLLECTION.TEST_KEY, collection); await act(async () => waitForPromisesToResolve()); const callback1 = jest.fn(); - const connection1 = connectionManager.connect({key: ONYXKEYS.COLLECTION.TEST_KEY, callback: callback1}); + const connection1 = connectionManager.connect({ + key: ONYXKEYS.COLLECTION.TEST_KEY, + callback: callback1, + }); await act(async () => waitForPromisesToResolve()); expect(callback1).toHaveBeenCalledWith(collection, ONYXKEYS.COLLECTION.TEST_KEY); const callback2 = jest.fn(); - const connection2 = connectionManager.connect({key: ONYXKEYS.COLLECTION.TEST_KEY, callback: callback2}); + const connection2 = connectionManager.connect({ + key: ONYXKEYS.COLLECTION.TEST_KEY, + callback: callback2, + }); expect(connection1.id).toEqual(connection2.id); expect(connectionsMap.size).toEqual(1); @@ -254,7 +312,10 @@ describe('OnyxConnectionManager', () => { }).not.toThrow(); expect(() => { - connectionManager.disconnect({id: 'connectionID1', callbackID: 'callbackID1'}); + connectionManager.disconnect({ + id: 'connectionID1', + callbackID: 'callbackID1', + }); }).not.toThrow(); }); @@ -262,7 +323,10 @@ describe('OnyxConnectionManager', () => { await StorageMock.setItem(ONYXKEYS.TEST_KEY, 'test'); const callback1 = jest.fn(); - connectionManager.connect({key: ONYXKEYS.TEST_KEY, callback: callback1}); + connectionManager.connect({ + key: ONYXKEYS.TEST_KEY, + callback: callback1, + }); expect(connectionsMap.size).toEqual(1); await act(async () => waitForPromisesToResolve()); @@ -278,10 +342,16 @@ describe('OnyxConnectionManager', () => { callback1.mockReset(); const callback2 = jest.fn(); - connectionManager.connect({key: ONYXKEYS.TEST_KEY, callback: callback2}); + connectionManager.connect({ + key: ONYXKEYS.TEST_KEY, + callback: callback2, + }); const callback3 = jest.fn(); - connectionManager.connect({key: ONYXKEYS.TEST_KEY, callback: callback3}); + connectionManager.connect({ + key: ONYXKEYS.TEST_KEY, + callback: callback3, + }); // We expect to have two connections for ONYXKEYS.TEST_KEY, one for the first subscription before Onyx.clear(), // and the other for the two subscriptions with the same key after Onyx.clear(). @@ -313,8 +383,16 @@ describe('OnyxConnectionManager', () => { it('should clean up the correct subscription ID from lastConnectionCallbackData on disconnect', async () => { const deleteSpy = jest.spyOn(Map.prototype, 'delete'); - const connectionA = Onyx.connect({key: ONYXKEYS.TEST_KEY, callback: jest.fn(), reuseConnection: false}); - Onyx.connect({key: ONYXKEYS.TEST_KEY, callback: jest.fn(), reuseConnection: false}); + const connectionA = Onyx.connect({ + key: ONYXKEYS.TEST_KEY, + callback: jest.fn(), + reuseConnection: false, + }); + Onyx.connect({ + key: ONYXKEYS.TEST_KEY, + callback: jest.fn(), + reuseConnection: false, + }); await act(async () => waitForPromisesToResolve()); const subscriptionIdA = connectionsMap.get(connectionA.id)?.subscriptionID; @@ -334,8 +412,16 @@ describe('OnyxConnectionManager', () => { it('should remove the subscription ID from onyxKeyToSubscriptionIDs on disconnect', async () => { const setSpy = jest.spyOn(Map.prototype, 'set'); - const connectionA = Onyx.connect({key: ONYXKEYS.TEST_KEY, callback: jest.fn(), reuseConnection: false}); - const connectionB = Onyx.connect({key: ONYXKEYS.TEST_KEY, callback: jest.fn(), reuseConnection: false}); + const connectionA = Onyx.connect({ + key: ONYXKEYS.TEST_KEY, + callback: jest.fn(), + reuseConnection: false, + }); + const connectionB = Onyx.connect({ + key: ONYXKEYS.TEST_KEY, + callback: jest.fn(), + reuseConnection: false, + }); await act(async () => waitForPromisesToResolve()); const subscriptionIdA = connectionsMap.get(connectionA.id)?.subscriptionID; @@ -347,7 +433,7 @@ describe('OnyxConnectionManager', () => { const setCallsForKey = setSpy.mock.calls.filter((call) => call[0] === ONYXKEYS.TEST_KEY); expect(setCallsForKey.length).toBeGreaterThan(0); - const updatedIDs = setCallsForKey[setCallsForKey.length - 1][1] as number[]; + const updatedIDs = setCallsForKey.at(setCallsForKey.length - 1)![1] as number[]; expect(updatedIDs).not.toContain(subscriptionIdA); expect(updatedIDs).toContain(subscriptionIdB); @@ -362,13 +448,22 @@ describe('OnyxConnectionManager', () => { await StorageMock.setItem(ONYXKEYS.TEST_KEY_2, 'test2'); const callback1 = jest.fn(); - const connection1 = connectionManager.connect({key: ONYXKEYS.TEST_KEY, callback: callback1}); + const connection1 = connectionManager.connect({ + key: ONYXKEYS.TEST_KEY, + callback: callback1, + }); const callback2 = jest.fn(); - const connection2 = connectionManager.connect({key: ONYXKEYS.TEST_KEY, callback: callback2}); + const connection2 = connectionManager.connect({ + key: ONYXKEYS.TEST_KEY, + callback: callback2, + }); const callback3 = jest.fn(); - const connection3 = connectionManager.connect({key: ONYXKEYS.TEST_KEY_2, callback: callback3}); + const connection3 = connectionManager.connect({ + key: ONYXKEYS.TEST_KEY_2, + callback: callback3, + }); expect(connection1.id).toEqual(connection2.id); expect(connectionsMap.size).toEqual(2); @@ -388,13 +483,19 @@ describe('OnyxConnectionManager', () => { await StorageMock.setItem(ONYXKEYS.TEST_KEY, 'test'); await StorageMock.setItem(ONYXKEYS.TEST_KEY_2, 'test2'); - const connection1 = connectionManager.connect({key: ONYXKEYS.TEST_KEY, callback: jest.fn()}); + const connection1 = connectionManager.connect({ + key: ONYXKEYS.TEST_KEY, + callback: jest.fn(), + }); expect(connectionsMap.size).toEqual(1); connectionManager.refreshSessionID(); - const connection2 = connectionManager.connect({key: ONYXKEYS.TEST_KEY, callback: jest.fn()}); + const connection2 = connectionManager.connect({ + key: ONYXKEYS.TEST_KEY, + callback: jest.fn(), + }); expect(connectionsMap.size).toEqual(2); expect(connectionsMap.has(connection1.id)).toBeTruthy(); diff --git a/tests/unit/collectionHydrationTest.ts b/tests/unit/collectionHydrationTest.ts index 9a77cc9db..5c1982629 100644 --- a/tests/unit/collectionHydrationTest.ts +++ b/tests/unit/collectionHydrationTest.ts @@ -13,9 +13,18 @@ describe('Collection hydration with connect() followed by immediate set()', () = beforeEach(async () => { // ===== Session 1 ===== // Data is written to persistent storage (simulates a previous app session). - await StorageMock.setItem(`${ONYX_KEYS.COLLECTION.TEST_KEY}1`, {id: 1, title: 'Test One'}); - await StorageMock.setItem(`${ONYX_KEYS.COLLECTION.TEST_KEY}2`, {id: 2, title: 'Test Two'}); - await StorageMock.setItem(`${ONYX_KEYS.COLLECTION.TEST_KEY}3`, {id: 3, title: 'Test Three'}); + await StorageMock.setItem(`${ONYX_KEYS.COLLECTION.TEST_KEY}1`, { + id: 1, + title: 'Test One', + }); + await StorageMock.setItem(`${ONYX_KEYS.COLLECTION.TEST_KEY}2`, { + id: 2, + title: 'Test Two', + }); + await StorageMock.setItem(`${ONYX_KEYS.COLLECTION.TEST_KEY}3`, { + id: 3, + title: 'Test Three', + }); await StorageMock.setItem(ONYX_KEYS.SINGLE_KEY, {title: 'old'}); // ===== Session 2 ===== @@ -35,19 +44,25 @@ describe('Collection hydration with connect() followed by immediate set()', () = callback: mockCallback, }); - Onyx.set(`${ONYX_KEYS.COLLECTION.TEST_KEY}1`, {id: 1, title: 'Updated Test One'}); + Onyx.set(`${ONYX_KEYS.COLLECTION.TEST_KEY}1`, { + id: 1, + title: 'Updated Test One', + }); await waitForPromisesToResolve(); // The subscriber should eventually receive ALL collection members. // The async hydration reads test_2 and test_3 from storage. - const lastCall = mockCallback.mock.calls[mockCallback.mock.calls.length - 1][0]; + const lastCall = mockCallback.mock.calls.at(mockCallback.mock.calls.length - 1)[0]; expect(lastCall).toHaveProperty(`${ONYX_KEYS.COLLECTION.TEST_KEY}1`); expect(lastCall).toHaveProperty(`${ONYX_KEYS.COLLECTION.TEST_KEY}2`); expect(lastCall).toHaveProperty(`${ONYX_KEYS.COLLECTION.TEST_KEY}3`); // Verify the updated value is present (not stale) - expect(lastCall[`${ONYX_KEYS.COLLECTION.TEST_KEY}1`]).toEqual({id: 1, title: 'Updated Test One'}); + expect(lastCall[`${ONYX_KEYS.COLLECTION.TEST_KEY}1`]).toEqual({ + id: 1, + title: 'Updated Test One', + }); }); test('single key: set() with non-shallow-equal value should not be overwritten by stale hydration', async () => { @@ -64,7 +79,7 @@ describe('Collection hydration with connect() followed by immediate set()', () = await waitForPromisesToResolve(); // The LAST value delivered to the subscriber must be the fresh one, not the stale storage value - const lastValue = mockCallback.mock.calls[mockCallback.mock.calls.length - 1][0]; + const lastValue = mockCallback.mock.calls.at(mockCallback.mock.calls.length - 1)[0]; expect(lastValue).toEqual({title: 'new'}); }); @@ -77,16 +92,28 @@ describe('Collection hydration with connect() followed by immediate set()', () = }); // Update key 1 with a non-shallow-equal value while hydration multiGet is in-flight - Onyx.set(`${ONYX_KEYS.COLLECTION.TEST_KEY}1`, {id: 1, title: 'Freshly Updated'}); + Onyx.set(`${ONYX_KEYS.COLLECTION.TEST_KEY}1`, { + id: 1, + title: 'Freshly Updated', + }); await waitForPromisesToResolve(); - const lastCall = mockCallback.mock.calls[mockCallback.mock.calls.length - 1][0]; + const lastCall = mockCallback.mock.calls.at(mockCallback.mock.calls.length - 1)[0]; // The final collection snapshot must have the fresh value, not the stale storage one - expect(lastCall[`${ONYX_KEYS.COLLECTION.TEST_KEY}1`]).toEqual({id: 1, title: 'Freshly Updated'}); + expect(lastCall[`${ONYX_KEYS.COLLECTION.TEST_KEY}1`]).toEqual({ + id: 1, + title: 'Freshly Updated', + }); // Other members should still be present from storage - expect(lastCall[`${ONYX_KEYS.COLLECTION.TEST_KEY}2`]).toEqual({id: 2, title: 'Test Two'}); - expect(lastCall[`${ONYX_KEYS.COLLECTION.TEST_KEY}3`]).toEqual({id: 3, title: 'Test Three'}); + expect(lastCall[`${ONYX_KEYS.COLLECTION.TEST_KEY}2`]).toEqual({ + id: 2, + title: 'Test Two', + }); + expect(lastCall[`${ONYX_KEYS.COLLECTION.TEST_KEY}3`]).toEqual({ + id: 3, + title: 'Test Three', + }); }); }); diff --git a/tests/unit/mocks/sqliteMock.ts b/tests/unit/mocks/sqliteMock.ts index 8b5cd1d45..0ff0c2003 100644 --- a/tests/unit/mocks/sqliteMock.ts +++ b/tests/unit/mocks/sqliteMock.ts @@ -49,7 +49,7 @@ function extractNamedParameterOrder(sql: string): string[] | null { function wrapRows(rowsArray: TRow[]): NitroSQLiteQueryResultRows { return { _array: rowsArray, - item: (index: number) => rowsArray[index], + item: (index: number) => rowsArray.at(index), length: rowsArray.length, }; } @@ -134,7 +134,7 @@ function makeConnection(name: string): Pick = {}; for (let index = 0; index < namedOrder.length; index++) { - bindings[namedOrder[index]] = row[index]; + bindings[namedOrder[index]] = row.at(index); } const info = statement.run(bindings); total += info.changes; diff --git a/tests/unit/onyxClearWebStorageTest.ts b/tests/unit/onyxClearWebStorageTest.ts index bd699b2df..80c79f845 100644 --- a/tests/unit/onyxClearWebStorageTest.ts +++ b/tests/unit/onyxClearWebStorageTest.ts @@ -2,7 +2,6 @@ import waitForPromisesToResolve from '../utils/waitForPromisesToResolve'; import StorageMock from '../../lib/storage'; import Onyx from '../../lib/Onyx'; import type OnyxCache from '../../lib/OnyxCache'; -import type GenericCollection from '../utils/GenericCollection'; import type {Connection} from '../../lib/OnyxConnectionManager'; const ONYX_KEYS = { @@ -136,7 +135,7 @@ describe('Set data while storage is clearing', () => { Onyx.mergeCollection(ONYX_KEYS.COLLECTION.TEST, { [collectionItemKey1]: {id: 1, name: 'first'}, [collectionItemKey2]: {id: 2, name: 'second'}, - } as GenericCollection) + }) // When clear is called with the collection prefix as a key to preserve .then(() => Onyx.clear([ONYX_KEYS.COLLECTION.TEST])) .then(() => waitForPromisesToResolve()) @@ -165,7 +164,7 @@ describe('Set data while storage is clearing', () => { // Given that Onyx has both a collection item and a regular key set return ( - Promise.all([Onyx.set(ONYX_KEYS.REGULAR_KEY, SET_VALUE), Onyx.mergeCollection(ONYX_KEYS.COLLECTION.TEST, {[collectionItemKey1]: 'value'} as GenericCollection)]) + Promise.all([Onyx.set(ONYX_KEYS.REGULAR_KEY, SET_VALUE), Onyx.mergeCollection(ONYX_KEYS.COLLECTION.TEST, {[collectionItemKey1]: 'value'})]) // When clear is called preserving only the collection .then(() => Onyx.clear([ONYX_KEYS.COLLECTION.TEST])) .then(() => waitForPromisesToResolve()) @@ -189,7 +188,7 @@ describe('Set data while storage is clearing', () => { // Given that Onyx has a collection item and a regular key set return ( - Promise.all([Onyx.set(ONYX_KEYS.REGULAR_KEY, SET_VALUE), Onyx.mergeCollection(ONYX_KEYS.COLLECTION.TEST, {[collectionItemKey1]: 'value'} as GenericCollection)]) + Promise.all([Onyx.set(ONYX_KEYS.REGULAR_KEY, SET_VALUE), Onyx.mergeCollection(ONYX_KEYS.COLLECTION.TEST, {[collectionItemKey1]: 'value'})]) // When clear is called preserving both the collection and the regular key .then(() => Onyx.clear([ONYX_KEYS.COLLECTION.TEST, ONYX_KEYS.REGULAR_KEY])) .then(() => waitForPromisesToResolve()) @@ -223,7 +222,7 @@ describe('Set data while storage is clearing', () => { [`${ONYX_KEYS.COLLECTION.TEST}2`]: 2, [`${ONYX_KEYS.COLLECTION.TEST}3`]: 3, [`${ONYX_KEYS.COLLECTION.TEST}4`]: 4, - } as GenericCollection), + }), ) // When onyx is cleared diff --git a/tests/unit/onyxMultiMergeWebStorageTest.ts b/tests/unit/onyxMultiMergeWebStorageTest.ts index 84ea6c40d..cdd7b042b 100644 --- a/tests/unit/onyxMultiMergeWebStorageTest.ts +++ b/tests/unit/onyxMultiMergeWebStorageTest.ts @@ -3,7 +3,6 @@ import waitForPromisesToResolve from '../utils/waitForPromisesToResolve'; import Storage from '../../lib/storage'; import type MockedStorage from '../../lib/storage/__mocks__'; import type OnyxInstance from '../../lib/Onyx'; -import type GenericCollection from '../utils/GenericCollection'; const StorageMock = Storage as unknown as typeof MockedStorage; @@ -54,7 +53,7 @@ describe('Onyx.mergeCollection() and WebStorage', () => { test_1: additionalDataOne, test_2: additionalDataOne, test_3: additionalDataOne, - } as GenericCollection); + }); // And call again consecutively with different data const additionalDataTwo = {d: 'd', e: [2]}; @@ -62,7 +61,7 @@ describe('Onyx.mergeCollection() and WebStorage', () => { test_1: additionalDataTwo, test_2: additionalDataTwo, test_3: additionalDataTwo, - } as GenericCollection); + }); return waitForPromisesToResolve().then(() => { const finalObject = { @@ -102,7 +101,7 @@ describe('Onyx.mergeCollection() and WebStorage', () => { test_1: data, test_2: data, test_3: data, - } as GenericCollection); + }); return waitForPromisesToResolve() .then(() => { @@ -124,7 +123,7 @@ describe('Onyx.mergeCollection() and WebStorage', () => { test_1: additionalData, test_2: additionalData, test_3: additionalData, - } as GenericCollection); + }); return waitForPromisesToResolve(); }) @@ -163,7 +162,7 @@ describe('Onyx.mergeCollection() and WebStorage', () => { // 2nd call Onyx.mergeCollection(ONYX_KEYS.COLLECTION.TEST_KEY, { test_1: {d: 'd', e: 'e'}, - } as GenericCollection); + }); // Last call Onyx.merge('test_1', {f: 'f'}); diff --git a/tests/unit/onyxTest.ts b/tests/unit/onyxTest.ts index 2ea8c6c6c..53e688f43 100644 --- a/tests/unit/onyxTest.ts +++ b/tests/unit/onyxTest.ts @@ -222,7 +222,9 @@ describe('Onyx', () => { await Onyx.set(collectionKey, []); expect((testKeyValue as Record)?.[collectionKey]).toStrictEqual([]); - await Onyx.mergeCollection(ONYX_KEYS.COLLECTION.TEST_KEY, {[collectionKey]: {test: 'value'}}); + await Onyx.mergeCollection(ONYX_KEYS.COLLECTION.TEST_KEY, { + [collectionKey]: {test: 'value'}, + }); expect((testKeyValue as Record)?.[collectionKey]).toStrictEqual({test: 'value'}); }); @@ -576,15 +578,21 @@ describe('Onyx', () => { }); }) .then(() => { - expect(testKeyValue).toEqual({test1: {test2: 'test2', test3: 'test3'}}); + expect(testKeyValue).toEqual({ + test1: {test2: 'test2', test3: 'test3'}, + }); return Onyx.merge(ONYX_KEYS.TEST_KEY, {test1: undefined}); }) .then(() => { - expect(testKeyValue).toEqual({test1: {test2: 'test2', test3: 'test3'}}); + expect(testKeyValue).toEqual({ + test1: {test2: 'test2', test3: 'test3'}, + }); return Onyx.merge(ONYX_KEYS.TEST_KEY, undefined); }) .then(() => { - expect(testKeyValue).toEqual({test1: {test2: 'test2', test3: 'test3'}}); + expect(testKeyValue).toEqual({ + test1: {test2: 'test2', test3: 'test3'}, + }); }); }); @@ -680,7 +688,7 @@ describe('Onyx', () => { ID: 345, value: 'three', }, - } as GenericCollection) + }) .then(() => // 2 key values to update and 2 new keys to add. // MergeCollection will perform a mix of multiSet and multiMerge @@ -702,14 +710,18 @@ describe('Onyx', () => { ID: 567, value: 'one', }, - } as GenericCollection), + }), ) .then(() => { // Callback fires once per mergeCollection with the full collection object. expect(mockCallback).toHaveBeenCalledTimes(2); expect(mockCallback).toHaveBeenNthCalledWith( 1, - {test_1: {ID: 123, value: 'one'}, test_2: {ID: 234, value: 'two'}, test_3: {ID: 345, value: 'three'}}, + { + test_1: {ID: 123, value: 'one'}, + test_2: {ID: 234, value: 'two'}, + test_3: {ID: 345, value: 'three'}, + }, ONYX_KEYS.COLLECTION.TEST_KEY, ); expect(mockCallback).toHaveBeenNthCalledWith( @@ -734,7 +746,10 @@ describe('Onyx', () => { callback: (data, key) => (valuesReceived[key] = data), }); - return Onyx.mergeCollection(ONYX_KEYS.COLLECTION.TEST_KEY, {test_1: {ID: 123}, notMyTest: {beep: 'boop'}} as GenericCollection).then(() => { + return Onyx.mergeCollection(ONYX_KEYS.COLLECTION.TEST_KEY, { + test_1: {ID: 123}, + notMyTest: {beep: 'boop'}, + }).then(() => { expect(valuesReceived).toEqual({}); }); }); @@ -764,7 +779,7 @@ describe('Onyx', () => { ID: 234, value: 'two', }, - } as GenericCollection), + }), ) .then(() => { expect(lastCollectionObject).toEqual({ @@ -808,7 +823,7 @@ describe('Onyx', () => { expect(mockCallback).toHaveBeenCalledTimes(1); // Should receive the entire merged collection - const receivedData = mockCallback.mock.calls[0][0]; + const receivedData = mockCallback.mock.calls.at(0)[0]; expect(receivedData).toEqual( expect.objectContaining({ [key1]: {id: '1', name: 'Item 1'}, @@ -851,7 +866,7 @@ describe('Onyx', () => { expect(mockCallback).toHaveBeenCalledTimes(1); // Should receive filtered collection - const receivedData = mockCallback.mock.calls[0][0]; + const receivedData = mockCallback.mock.calls.at(0)[0]; expect(receivedData).toEqual({ [key2]: {id: '2', name: 'Updated Item 2'}, }); @@ -934,7 +949,10 @@ describe('Onyx', () => { }) .then(() => { // The collection callback receives the whole collection object. - expect(mockCallback.mock.calls[mockCallback.mock.calls.length - 1][0]).toEqual({test_1: {existingData: 'test'}, test_2: {existingData: 'test'}}); + expect(mockCallback.mock.calls[mockCallback.mock.calls.length - 1][0]).toEqual({ + test_1: {existingData: 'test'}, + test_2: {existingData: 'test'}, + }); mockCallback.mockReset(); // When we pass a mergeCollection data object to Onyx.update @@ -955,7 +973,7 @@ describe('Onyx', () => { ID: 345, value: 'three', }, - } as GenericCollection, + }, }, ]); }) @@ -1194,12 +1212,25 @@ describe('Onyx', () => { connections.push(Onyx.connect({key: ONYX_KEYS.TEST_KEY, callback: testCallback})); connections.push(Onyx.connect({key: ONYX_KEYS.OTHER_TEST, callback: otherTestCallback})); - connections.push(Onyx.connect({key: ONYX_KEYS.COLLECTION.TEST_UPDATE, callback: collectionCallback})); + connections.push( + Onyx.connect({ + key: ONYX_KEYS.COLLECTION.TEST_UPDATE, + callback: collectionCallback, + }), + ); return waitForPromisesToResolve().then(() => Onyx.update([ {onyxMethod: Onyx.METHOD.SET, key: ONYX_KEYS.TEST_KEY, value: 'taco'}, - {onyxMethod: Onyx.METHOD.MERGE, key: ONYX_KEYS.OTHER_TEST, value: 'pizza'}, - {onyxMethod: Onyx.METHOD.MERGE_COLLECTION, key: ONYX_KEYS.COLLECTION.TEST_UPDATE, value: {[itemKey]: {a: 'a'}} as GenericCollection}, + { + onyxMethod: Onyx.METHOD.MERGE, + key: ONYX_KEYS.OTHER_TEST, + value: 'pizza', + }, + { + onyxMethod: Onyx.METHOD.MERGE_COLLECTION, + key: ONYX_KEYS.COLLECTION.TEST_UPDATE, + value: {[itemKey]: {a: 'a'}}, + }, ]).then(() => { expect(collectionCallback).toHaveBeenCalledTimes(2); expect(collectionCallback).toHaveBeenNthCalledWith(1, undefined, ONYX_KEYS.COLLECTION.TEST_UPDATE); @@ -1407,7 +1438,7 @@ describe('Onyx', () => { 3: null, }, }, - } as GenericCollection).then(() => { + }).then(() => { expect(result).toEqual({ [routineRoute]: { waypoints: { @@ -1494,10 +1525,14 @@ describe('Onyx', () => { await Onyx.update([{key: cat, value: finalValue, onyxMethod: Onyx.METHOD.MERGE}]); // The SNAPSHOT collection-root subscriber receives the whole collection. - expect(callback).toBeCalledTimes(2); - expect(callback.mock.calls[0][0]).toEqual({[snapshot1]: {data: {[cat]: initialValue}}}); + expect(callback).toHaveBeenCalledTimes(2); + expect(callback.mock.calls[0][0]).toEqual({ + [snapshot1]: {data: {[cat]: initialValue}}, + }); expect(callback.mock.calls[0][1]).toBe(ONYX_KEYS.COLLECTION.SNAPSHOT); - expect(callback.mock.calls[1][0]).toEqual({[snapshot1]: {data: {[cat]: finalValue}}}); + expect(callback.mock.calls[1][0]).toEqual({ + [snapshot1]: {data: {[cat]: finalValue}}, + }); expect(callback.mock.calls[1][1]).toBe(ONYX_KEYS.COLLECTION.SNAPSHOT); }); @@ -1528,10 +1563,22 @@ describe('Onyx', () => { await Onyx.update([{key: cat, value: finalValue, onyxMethod: Onyx.METHOD.MERGE}]); // The SNAPSHOT collection-root subscriber receives the whole collection. - expect(callback).toBeCalledTimes(2); - expect(callback.mock.calls[0][0]).toEqual({[snapshot1]: {data: {[cat]: initialValue}}}); + expect(callback).toHaveBeenCalledTimes(2); + expect(callback.mock.calls[0][0]).toEqual({ + [snapshot1]: {data: {[cat]: initialValue}}, + }); expect(callback.mock.calls[0][1]).toBe(ONYX_KEYS.COLLECTION.SNAPSHOT); - expect(callback.mock.calls[1][0]).toEqual({[snapshot1]: {data: {[cat]: {name: 'Kitty', pendingAction: 'delete', pendingFields: {preview: 'delete'}}}}}); + expect(callback.mock.calls[1][0]).toEqual({ + [snapshot1]: { + data: { + [cat]: { + name: 'Kitty', + pendingAction: 'delete', + pendingFields: {preview: 'delete'}, + }, + }, + }, + }); expect(callback.mock.calls[1][1]).toBe(ONYX_KEYS.COLLECTION.SNAPSHOT); }); @@ -1598,7 +1645,7 @@ describe('Onyx', () => { 0: 'Bed', }, }, - } as GenericCollection, + }, }, { onyxMethod: Onyx.METHOD.MERGE, @@ -1663,7 +1710,12 @@ describe('Onyx', () => { const catCallback = jest.fn(); connections.push(Onyx.connect({key: ONYX_KEYS.TEST_KEY, callback: testCallback})); - connections.push(Onyx.connect({key: ONYX_KEYS.OTHER_TEST, callback: otherTestCallback})); + connections.push( + Onyx.connect({ + key: ONYX_KEYS.OTHER_TEST, + callback: otherTestCallback, + }), + ); connections.push( Onyx.connect({ key: ONYX_KEYS.COLLECTION.ANIMALS, @@ -1679,11 +1731,31 @@ describe('Onyx', () => { connections.push(Onyx.connect({key: cat, callback: catCallback})); return Onyx.update([ - {onyxMethod: Onyx.METHOD.MERGE, key: ONYX_KEYS.TEST_KEY, value: 'none'}, - {onyxMethod: Onyx.METHOD.SET, key: ONYX_KEYS.TEST_KEY, value: {food: 'taco'}}, - {onyxMethod: Onyx.METHOD.MERGE, key: ONYX_KEYS.TEST_KEY, value: {drink: 'wine'}}, - {onyxMethod: Onyx.METHOD.MERGE, key: ONYX_KEYS.OTHER_TEST, value: {food: 'pizza'}}, - {onyxMethod: Onyx.METHOD.MERGE, key: ONYX_KEYS.OTHER_TEST, value: {drink: 'water'}}, + { + onyxMethod: Onyx.METHOD.MERGE, + key: ONYX_KEYS.TEST_KEY, + value: 'none', + }, + { + onyxMethod: Onyx.METHOD.SET, + key: ONYX_KEYS.TEST_KEY, + value: {food: 'taco'}, + }, + { + onyxMethod: Onyx.METHOD.MERGE, + key: ONYX_KEYS.TEST_KEY, + value: {drink: 'wine'}, + }, + { + onyxMethod: Onyx.METHOD.MERGE, + key: ONYX_KEYS.OTHER_TEST, + value: {food: 'pizza'}, + }, + { + onyxMethod: Onyx.METHOD.MERGE, + key: ONYX_KEYS.OTHER_TEST, + value: {drink: 'water'}, + }, {onyxMethod: Onyx.METHOD.MERGE, key: dog, value: {sound: 'woof'}}, { onyxMethod: Onyx.METHOD.MERGE_COLLECTION, @@ -1691,12 +1763,16 @@ describe('Onyx', () => { value: { [cat]: {age: 5, size: 'S'}, [dog]: {size: 'M'}, - } as GenericCollection, + }, }, {onyxMethod: Onyx.METHOD.SET, key: cat, value: {age: 3}}, {onyxMethod: Onyx.METHOD.MERGE, key: cat, value: {sound: 'meow'}}, {onyxMethod: Onyx.METHOD.MERGE, key: bob, value: {car: 'sedan'}}, - {onyxMethod: Onyx.METHOD.MERGE, key: lisa, value: {car: 'SUV', age: 21}}, + { + onyxMethod: Onyx.METHOD.MERGE, + key: lisa, + value: {car: 'SUV', age: 21}, + }, {onyxMethod: Onyx.METHOD.MERGE, key: bob, value: {age: 25}}, ]).then(() => { expect(testCallback).toHaveBeenNthCalledWith(1, {food: 'taco', drink: 'wine'}, ONYX_KEYS.TEST_KEY); @@ -1802,7 +1878,11 @@ describe('Onyx', () => { await Onyx.update(queuedUpdates); - expect(result).toEqual({[`${ONYX_KEYS.COLLECTION.TEST_UPDATE}entry1`]: {someKey: 'someValueChanged'}}); + expect(result).toEqual({ + [`${ONYX_KEYS.COLLECTION.TEST_UPDATE}entry1`]: { + someKey: 'someValueChanged', + }, + }); expect(await StorageMock.getItem(`${ONYX_KEYS.COLLECTION.TEST_UPDATE}entry1`)).toEqual({someKey: 'someValueChanged'}); }); @@ -1825,7 +1905,9 @@ describe('Onyx', () => { someKey: 'someValue', }, }; - await Onyx.multiSet({[`${ONYX_KEYS.COLLECTION.TEST_UPDATE}entry1`]: entry1}); + await Onyx.multiSet({ + [`${ONYX_KEYS.COLLECTION.TEST_UPDATE}entry1`]: entry1, + }); const entry1ExpectedResult = lodashCloneDeep(entry1); const queuedUpdates: Array> = []; @@ -1855,7 +1937,9 @@ describe('Onyx', () => { await Onyx.update(queuedUpdates); - expect(result).toEqual({[`${ONYX_KEYS.COLLECTION.TEST_UPDATE}entry1`]: entry1ExpectedResult}); + expect(result).toEqual({ + [`${ONYX_KEYS.COLLECTION.TEST_UPDATE}entry1`]: entry1ExpectedResult, + }); expect(await StorageMock.getItem(`${ONYX_KEYS.COLLECTION.TEST_UPDATE}entry1`)).toEqual(entry1ExpectedResult); }); @@ -1872,7 +1956,9 @@ describe('Onyx', () => { }, }, }; - await Onyx.multiSet({[`${ONYX_KEYS.COLLECTION.TEST_UPDATE}entry1`]: entry1}); + await Onyx.multiSet({ + [`${ONYX_KEYS.COLLECTION.TEST_UPDATE}entry1`]: entry1, + }); const entry1ExpectedResult = lodashCloneDeep(entry1); const queuedUpdates: Array> = []; @@ -1927,7 +2013,9 @@ describe('Onyx', () => { await Onyx.update(queuedUpdates); - expect(result).toEqual({[`${ONYX_KEYS.COLLECTION.TEST_UPDATE}entry1`]: entry1ExpectedResult}); + expect(result).toEqual({ + [`${ONYX_KEYS.COLLECTION.TEST_UPDATE}entry1`]: entry1ExpectedResult, + }); expect(await StorageMock.getItem(`${ONYX_KEYS.COLLECTION.TEST_UPDATE}entry1`)).toEqual(entry1ExpectedResult); }); @@ -1944,7 +2032,9 @@ describe('Onyx', () => { }, }, }; - await Onyx.multiSet({[`${ONYX_KEYS.COLLECTION.TEST_UPDATE}entry1`]: entry1}); + await Onyx.multiSet({ + [`${ONYX_KEYS.COLLECTION.TEST_UPDATE}entry1`]: entry1, + }); const entry1ExpectedResult = lodashCloneDeep(entry1); const queuedUpdates: Array> = []; @@ -1987,7 +2077,9 @@ describe('Onyx', () => { await Onyx.update(queuedUpdates); - expect(result).toEqual({[`${ONYX_KEYS.COLLECTION.TEST_UPDATE}entry1`]: entry1ExpectedResult}); + expect(result).toEqual({ + [`${ONYX_KEYS.COLLECTION.TEST_UPDATE}entry1`]: entry1ExpectedResult, + }); expect(await StorageMock.getItem(`${ONYX_KEYS.COLLECTION.TEST_UPDATE}entry1`)).toEqual(entry1ExpectedResult); }); @@ -2026,7 +2118,10 @@ describe('Onyx', () => { }, }, }); - entry1ExpectedResult = {someKey: 'someValueChanged', someNestedObject: {someNestedKey: 'someNestedValue'}}; + entry1ExpectedResult = { + someKey: 'someValueChanged', + someNestedObject: {someNestedKey: 'someNestedValue'}, + }; queuedUpdates.push({ key: `${ONYX_KEYS.COLLECTION.TEST_UPDATE}entry1`, @@ -2049,11 +2144,15 @@ describe('Onyx', () => { }, }, }); - entry1ExpectedResult.someNestedObject = {someNestedKeyChanged: 'someNestedValueChange'}; + entry1ExpectedResult.someNestedObject = { + someNestedKeyChanged: 'someNestedValueChange', + }; await Onyx.update(queuedUpdates); - expect(result).toEqual({[`${ONYX_KEYS.COLLECTION.TEST_UPDATE}entry1`]: entry1ExpectedResult}); + expect(result).toEqual({ + [`${ONYX_KEYS.COLLECTION.TEST_UPDATE}entry1`]: entry1ExpectedResult, + }); expect(await StorageMock.getItem(`${ONYX_KEYS.COLLECTION.TEST_UPDATE}entry1`)).toEqual(entry1ExpectedResult); }); @@ -2072,8 +2171,12 @@ describe('Onyx', () => { someKey: 'someValue', }, }; - await Onyx.multiSet({[`${ONYX_KEYS.COLLECTION.TEST_UPDATE}entry1`]: entry1}); - await Onyx.multiSet({[`${ONYX_KEYS.COLLECTION.TEST_UPDATE}entry2`]: entry2}); + await Onyx.multiSet({ + [`${ONYX_KEYS.COLLECTION.TEST_UPDATE}entry1`]: entry1, + }); + await Onyx.multiSet({ + [`${ONYX_KEYS.COLLECTION.TEST_UPDATE}entry2`]: entry2, + }); const entry1ExpectedResult = lodashCloneDeep(entry1); const entry2ExpectedResult = lodashCloneDeep(entry2); @@ -2179,7 +2282,7 @@ describe('Onyx', () => { [routeA]: {name: 'Route A'}, [routeB]: {name: 'Route B'}, [routeC]: {name: 'Route C'}, - } as GenericCollection) + }) .then(() => Onyx.update([ { @@ -2188,7 +2291,7 @@ describe('Onyx', () => { value: { [routeA]: {name: 'New Route A'}, [routeB]: {name: 'New Route B'}, - } as GenericCollection, + }, }, ]), ) @@ -2224,7 +2327,7 @@ describe('Onyx', () => { return Onyx.mergeCollection(ONYX_KEYS.COLLECTION.ROUTES, { [routeA]: {name: 'Route A'}, [routeB]: {name: 'Route B'}, - } as GenericCollection) + }) .then(() => Onyx.update([ { @@ -2237,7 +2340,7 @@ describe('Onyx', () => { key: ONYX_KEYS.COLLECTION.ROUTES, value: { [routeA]: {name: 'Final Route A'}, - } as GenericCollection, + }, }, { onyxMethod: Onyx.METHOD.MERGE, @@ -2290,7 +2393,7 @@ describe('Onyx', () => { value: { [key1]: {id: '1', name: 'Updated Item 1'}, [key2]: {id: '2', name: 'Updated Item 2'}, - } as GenericCollection, + }, }, ]); @@ -2347,8 +2450,12 @@ describe('Onyx', () => { await act(async () => Onyx.update([ {onyxMethod: 'set', key: ONYX_KEYS.TEST_KEY, value: 'test1'}, - // @ts-expect-error invalid method - {onyxMethod: 'invalidMethod', key: ONYX_KEYS.OTHER_TEST, value: 'test2'}, + { + // @ts-expect-error invalid method + onyxMethod: 'invalidMethod', + key: ONYX_KEYS.OTHER_TEST, + value: 'test2', + }, ]), ); @@ -2381,7 +2488,11 @@ describe('Onyx', () => { await act(async () => Onyx.update([ // @ts-expect-error invalid value - {onyxMethod: 'mergecollection', key: ONYX_KEYS.COLLECTION.TEST_KEY, value: 'test1'}, + { + onyxMethod: 'mergecollection', + key: ONYX_KEYS.COLLECTION.TEST_KEY, + value: 'test1', + }, ]), ); @@ -2418,7 +2529,11 @@ describe('Onyx', () => { await waitForPromisesToResolve(); - expect(result).toEqual({[`${ONYX_KEYS.COLLECTION.TEST_UPDATE}entry1`]: {someKey: 'someValueChanged'}}); + expect(result).toEqual({ + [`${ONYX_KEYS.COLLECTION.TEST_UPDATE}entry1`]: { + someKey: 'someValueChanged', + }, + }); expect(await StorageMock.getItem(`${ONYX_KEYS.COLLECTION.TEST_UPDATE}entry1`)).toEqual({someKey: 'someValueChanged'}); }); @@ -2441,7 +2556,9 @@ describe('Onyx', () => { someKey: 'someValue', }, }; - await Onyx.multiSet({[`${ONYX_KEYS.COLLECTION.TEST_UPDATE}entry1`]: entry1}); + await Onyx.multiSet({ + [`${ONYX_KEYS.COLLECTION.TEST_UPDATE}entry1`]: entry1, + }); const entry1ExpectedResult = lodashCloneDeep(entry1); @@ -2462,7 +2579,9 @@ describe('Onyx', () => { await waitForPromisesToResolve(); - expect(result).toEqual({[`${ONYX_KEYS.COLLECTION.TEST_UPDATE}entry1`]: entry1ExpectedResult}); + expect(result).toEqual({ + [`${ONYX_KEYS.COLLECTION.TEST_UPDATE}entry1`]: entry1ExpectedResult, + }); expect(await StorageMock.getItem(`${ONYX_KEYS.COLLECTION.TEST_UPDATE}entry1`)).toEqual(entry1ExpectedResult); }); @@ -2479,7 +2598,9 @@ describe('Onyx', () => { }, }, }; - await Onyx.multiSet({[`${ONYX_KEYS.COLLECTION.TEST_UPDATE}entry1`]: entry1}); + await Onyx.multiSet({ + [`${ONYX_KEYS.COLLECTION.TEST_UPDATE}entry1`]: entry1, + }); const entry1ExpectedResult = lodashCloneDeep(entry1); @@ -2521,7 +2642,9 @@ describe('Onyx', () => { await waitForPromisesToResolve(); - expect(result).toEqual({[`${ONYX_KEYS.COLLECTION.TEST_UPDATE}entry1`]: entry1ExpectedResult}); + expect(result).toEqual({ + [`${ONYX_KEYS.COLLECTION.TEST_UPDATE}entry1`]: entry1ExpectedResult, + }); expect(await StorageMock.getItem(`${ONYX_KEYS.COLLECTION.TEST_UPDATE}entry1`)).toEqual(entry1ExpectedResult); }); @@ -2538,7 +2661,9 @@ describe('Onyx', () => { }, }, }; - await Onyx.multiSet({[`${ONYX_KEYS.COLLECTION.TEST_UPDATE}entry1`]: entry1}); + await Onyx.multiSet({ + [`${ONYX_KEYS.COLLECTION.TEST_UPDATE}entry1`]: entry1, + }); const entry1ExpectedResult = lodashCloneDeep(entry1); @@ -2572,7 +2697,9 @@ describe('Onyx', () => { await waitForPromisesToResolve(); - expect(result).toEqual({[`${ONYX_KEYS.COLLECTION.TEST_UPDATE}entry1`]: entry1ExpectedResult}); + expect(result).toEqual({ + [`${ONYX_KEYS.COLLECTION.TEST_UPDATE}entry1`]: entry1ExpectedResult, + }); expect(await StorageMock.getItem(`${ONYX_KEYS.COLLECTION.TEST_UPDATE}entry1`)).toEqual(entry1ExpectedResult); }); }); @@ -2618,7 +2745,9 @@ describe('Onyx', () => { it('should not save a RAM-only key to storage when using merge', async () => { await Onyx.merge(ONYX_KEYS.RAM_ONLY_TEST_KEY, {someProperty: 'value'}); - expect(cache.get(ONYX_KEYS.RAM_ONLY_TEST_KEY)).toEqual({someProperty: 'value'}); + expect(cache.get(ONYX_KEYS.RAM_ONLY_TEST_KEY)).toEqual({ + someProperty: 'value', + }); expect(await StorageMock.getItem(ONYX_KEYS.RAM_ONLY_TEST_KEY)).toBeNull(); }); @@ -2648,7 +2777,9 @@ describe('Onyx', () => { .then(() => { expect(testKeyValue).toEqual(testData); - return Onyx.set(ONYX_KEYS.TEST_KEY, testData, {skipCacheCheck: true}); + return Onyx.set(ONYX_KEYS.TEST_KEY, testData, { + skipCacheCheck: true, + }); }) .then(() => { expect(testKeyValue).toEqual(testData); @@ -2708,14 +2839,14 @@ describe('Onyx', () => { [routeA]: {name: 'Route A'}, [routeB1]: {name: 'Route B1'}, [routeC]: {name: 'Route C'}, - } as GenericCollection); + }); // Replace with new collection data await Onyx.setCollection(ONYX_KEYS.COLLECTION.ROUTES, { [routeA]: {name: 'New Route A'}, [routeB]: {name: 'New Route B'}, [routeC]: {name: 'New Route C'}, - } as GenericCollection); + }); expect(result).toEqual({ [routeA]: {name: 'New Route A'}, @@ -2742,9 +2873,9 @@ describe('Onyx', () => { await Onyx.mergeCollection(ONYX_KEYS.COLLECTION.ROUTES, { [routeA]: {name: 'Route A'}, - } as GenericCollection); + }); - await Onyx.setCollection(ONYX_KEYS.COLLECTION.ROUTES, {} as GenericCollection); + await Onyx.setCollection(ONYX_KEYS.COLLECTION.ROUTES, {}); expect(result).toEqual({}); }); @@ -2761,7 +2892,7 @@ describe('Onyx', () => { await Onyx.mergeCollection(ONYX_KEYS.COLLECTION.ROUTES, { [routeA]: {name: 'Route A'}, - } as GenericCollection); + }); await Onyx.setCollection(ONYX_KEYS.COLLECTION.ROUTES, { [invalidRoute]: {name: 'Invalid Route'}, @@ -2792,13 +2923,13 @@ describe('Onyx', () => { [routeA]: null, [routeB]: {name: 'Route B'}, [routeC]: null, - } as GenericCollection); + }); // Should only be called once expect(mockCallback).toHaveBeenCalledTimes(1); // Should receive filtered collection (only non-null values) - const receivedData = mockCallback.mock.calls[0][0]; + const receivedData = mockCallback.mock.calls.at(0)[0]; expect(receivedData).toEqual({ [routeB]: {name: 'Route B'}, }); @@ -2830,11 +2961,20 @@ describe('Onyx', () => { }, }); - await Onyx.set(`${ONYX_KEYS.COLLECTION.TEST_KEY}entry1`, {id: 'entry1_id', name: 'entry2_name'}); - await Onyx.set(`${ONYX_KEYS.COLLECTION.TEST_KEY}skippable-id`, {id: 'skippable-id_id', name: 'skippable-id_name'}); + await Onyx.set(`${ONYX_KEYS.COLLECTION.TEST_KEY}entry1`, { + id: 'entry1_id', + name: 'entry2_name', + }); + await Onyx.set(`${ONYX_KEYS.COLLECTION.TEST_KEY}skippable-id`, { + id: 'skippable-id_id', + name: 'skippable-id_name', + }); expect(testKeyValue).toEqual({ - [`${ONYX_KEYS.COLLECTION.TEST_KEY}entry1`]: {id: 'entry1_id', name: 'entry2_name'}, + [`${ONYX_KEYS.COLLECTION.TEST_KEY}entry1`]: { + id: 'entry1_id', + name: 'entry2_name', + }, }); }); @@ -2847,11 +2987,20 @@ describe('Onyx', () => { }, }); - await Onyx.merge(`${ONYX_KEYS.COLLECTION.TEST_KEY}entry1`, {id: 'entry1_id', name: 'entry2_name'}); - await Onyx.merge(`${ONYX_KEYS.COLLECTION.TEST_KEY}skippable-id`, {id: 'skippable-id_id', name: 'skippable-id_name'}); + await Onyx.merge(`${ONYX_KEYS.COLLECTION.TEST_KEY}entry1`, { + id: 'entry1_id', + name: 'entry2_name', + }); + await Onyx.merge(`${ONYX_KEYS.COLLECTION.TEST_KEY}skippable-id`, { + id: 'skippable-id_id', + name: 'skippable-id_name', + }); expect(testKeyValue).toEqual({ - [`${ONYX_KEYS.COLLECTION.TEST_KEY}entry1`]: {id: 'entry1_id', name: 'entry2_name'}, + [`${ONYX_KEYS.COLLECTION.TEST_KEY}entry1`]: { + id: 'entry1_id', + name: 'entry2_name', + }, }); }); @@ -2865,14 +3014,29 @@ describe('Onyx', () => { }); await Onyx.mergeCollection(ONYX_KEYS.COLLECTION.TEST_KEY, { - [`${ONYX_KEYS.COLLECTION.TEST_KEY}entry1`]: {id: 'entry1_id', name: 'entry1_name'}, - [`${ONYX_KEYS.COLLECTION.TEST_KEY}entry2`]: {id: 'entry2_id', name: 'entry2_name'}, - [`${ONYX_KEYS.COLLECTION.TEST_KEY}skippable-id`]: {id: 'skippable-id_id', name: 'skippable-id_name'}, - } as GenericCollection); + [`${ONYX_KEYS.COLLECTION.TEST_KEY}entry1`]: { + id: 'entry1_id', + name: 'entry1_name', + }, + [`${ONYX_KEYS.COLLECTION.TEST_KEY}entry2`]: { + id: 'entry2_id', + name: 'entry2_name', + }, + [`${ONYX_KEYS.COLLECTION.TEST_KEY}skippable-id`]: { + id: 'skippable-id_id', + name: 'skippable-id_name', + }, + }); expect(testKeyValue).toEqual({ - [`${ONYX_KEYS.COLLECTION.TEST_KEY}entry1`]: {id: 'entry1_id', name: 'entry1_name'}, - [`${ONYX_KEYS.COLLECTION.TEST_KEY}entry2`]: {id: 'entry2_id', name: 'entry2_name'}, + [`${ONYX_KEYS.COLLECTION.TEST_KEY}entry1`]: { + id: 'entry1_id', + name: 'entry1_name', + }, + [`${ONYX_KEYS.COLLECTION.TEST_KEY}entry2`]: { + id: 'entry2_id', + name: 'entry2_name', + }, }); }); @@ -2886,14 +3050,29 @@ describe('Onyx', () => { }); await Onyx.setCollection(ONYX_KEYS.COLLECTION.TEST_KEY, { - [`${ONYX_KEYS.COLLECTION.TEST_KEY}entry1`]: {id: 'entry1_id', name: 'entry1_name'}, - [`${ONYX_KEYS.COLLECTION.TEST_KEY}entry2`]: {id: 'entry2_id', name: 'entry2_name'}, - [`${ONYX_KEYS.COLLECTION.TEST_KEY}skippable-id`]: {id: 'skippable-id_id', name: 'skippable-id_name'}, - } as GenericCollection); + [`${ONYX_KEYS.COLLECTION.TEST_KEY}entry1`]: { + id: 'entry1_id', + name: 'entry1_name', + }, + [`${ONYX_KEYS.COLLECTION.TEST_KEY}entry2`]: { + id: 'entry2_id', + name: 'entry2_name', + }, + [`${ONYX_KEYS.COLLECTION.TEST_KEY}skippable-id`]: { + id: 'skippable-id_id', + name: 'skippable-id_name', + }, + }); expect(testKeyValue).toEqual({ - [`${ONYX_KEYS.COLLECTION.TEST_KEY}entry1`]: {id: 'entry1_id', name: 'entry1_name'}, - [`${ONYX_KEYS.COLLECTION.TEST_KEY}entry2`]: {id: 'entry2_id', name: 'entry2_name'}, + [`${ONYX_KEYS.COLLECTION.TEST_KEY}entry1`]: { + id: 'entry1_id', + name: 'entry1_name', + }, + [`${ONYX_KEYS.COLLECTION.TEST_KEY}entry2`]: { + id: 'entry2_id', + name: 'entry2_name', + }, }); }); @@ -2907,14 +3086,29 @@ describe('Onyx', () => { }); await Onyx.multiSet({ - [`${ONYX_KEYS.COLLECTION.TEST_KEY}entry1`]: {id: 'entry1_id', name: 'entry1_name'}, - [`${ONYX_KEYS.COLLECTION.TEST_KEY}entry2`]: {id: 'entry2_id', name: 'entry2_name'}, - [`${ONYX_KEYS.COLLECTION.TEST_KEY}skippable-id`]: {id: 'skippable-id_id', name: 'skippable-id_name'}, - } as GenericCollection); + [`${ONYX_KEYS.COLLECTION.TEST_KEY}entry1`]: { + id: 'entry1_id', + name: 'entry1_name', + }, + [`${ONYX_KEYS.COLLECTION.TEST_KEY}entry2`]: { + id: 'entry2_id', + name: 'entry2_name', + }, + [`${ONYX_KEYS.COLLECTION.TEST_KEY}skippable-id`]: { + id: 'skippable-id_id', + name: 'skippable-id_name', + }, + }); expect(testKeyValue).toEqual({ - [`${ONYX_KEYS.COLLECTION.TEST_KEY}entry1`]: {id: 'entry1_id', name: 'entry1_name'}, - [`${ONYX_KEYS.COLLECTION.TEST_KEY}entry2`]: {id: 'entry2_id', name: 'entry2_name'}, + [`${ONYX_KEYS.COLLECTION.TEST_KEY}entry1`]: { + id: 'entry1_id', + name: 'entry1_name', + }, + [`${ONYX_KEYS.COLLECTION.TEST_KEY}entry2`]: { + id: 'entry2_id', + name: 'entry2_name', + }, }); }); it('should clear pending merge for a key during multiSet()', async () => { @@ -3010,7 +3204,9 @@ describe('Onyx.init', () => { }); it('mergeCollection', async () => { - Onyx.mergeCollection(ONYX_KEYS.COLLECTION.TEST_KEY, {[`${ONYX_KEYS.COLLECTION.TEST_KEY}entry1`]: 'test_1'}); + Onyx.mergeCollection(ONYX_KEYS.COLLECTION.TEST_KEY, { + [`${ONYX_KEYS.COLLECTION.TEST_KEY}entry1`]: 'test_1', + }); await act(async () => waitForPromisesToResolve()); expect(cache.get(`${ONYX_KEYS.COLLECTION.TEST_KEY}entry1`)).toBeUndefined(); @@ -3049,7 +3245,9 @@ describe('Onyx.init', () => { }); it('setCollection', async () => { - Onyx.setCollection(ONYX_KEYS.COLLECTION.TEST_KEY, {[`${ONYX_KEYS.COLLECTION.TEST_KEY}entry1`]: 'test_1'}); + Onyx.setCollection(ONYX_KEYS.COLLECTION.TEST_KEY, { + [`${ONYX_KEYS.COLLECTION.TEST_KEY}entry1`]: 'test_1', + }); await act(async () => waitForPromisesToResolve()); expect(cache.get(`${ONYX_KEYS.COLLECTION.TEST_KEY}entry1`)).toBeUndefined(); @@ -3135,7 +3333,9 @@ describe('RAM-only keys should not read from storage', () => { it('should not include stale RAM-only keys in getAllKeys results', async () => { // Simulate stale data in storage await StorageMock.setItem(ONYX_KEYS.RAM_ONLY_TEST_KEY, 'stale_value'); - await StorageMock.setItem(`${ONYX_KEYS.COLLECTION.RAM_ONLY_COLLECTION}1`, {stale: 'member'}); + await StorageMock.setItem(`${ONYX_KEYS.COLLECTION.RAM_ONLY_COLLECTION}1`, { + stale: 'member', + }); await StorageMock.setItem(ONYX_KEYS.OTHER_TEST, 'normal_value'); Onyx.init({ @@ -3171,7 +3371,10 @@ describe('RAM-only keys should not read from storage', () => { it('should not use stale storage data as merge base for RAM-only keys', async () => { // Simulate stale data in storage - await StorageMock.setItem(ONYX_KEYS.RAM_ONLY_TEST_KEY, {name: 'stale', token: 'old_token'}); + await StorageMock.setItem(ONYX_KEYS.RAM_ONLY_TEST_KEY, { + name: 'stale', + token: 'old_token', + }); Onyx.init({ keys: ONYX_KEYS, @@ -3259,7 +3462,7 @@ describe('RAM-only keys should not read from storage', () => { await act(async () => waitForPromisesToResolve()); // Get the callback that was passed to keepInstancesSync - const syncCallback = (StorageMock.keepInstancesSync as jest.Mock).mock.calls[0]?.[0]; + const syncCallback = (StorageMock.keepInstancesSync as jest.Mock).mock.calls.at(0)?.[0]; expect(syncCallback).toBeDefined(); let receivedValue: unknown; @@ -3304,7 +3507,9 @@ describe('RAM-only keys should not read from storage', () => { // Pre-seed storage with stale data for both normal and RAM-only keys await StorageMock.setItem(normalMember, 'normal_from_storage'); - await StorageMock.setItem(ramOnlyMember, {data: 'stale_collection_member'}); + await StorageMock.setItem(ramOnlyMember, { + data: 'stale_collection_member', + }); Onyx.init({ keys: ONYX_KEYS, @@ -3389,8 +3594,16 @@ describe('get() should prefer cache over stale storage', () => { // 2+ collection keys get batched into mergeCollectionWithPatches (deferred cache write) const updatePromise = Onyx.update([ - {onyxMethod: Onyx.METHOD.MERGE, key: member1, value: {isOptimistic: true, name: 'first'}}, - {onyxMethod: Onyx.METHOD.MERGE, key: member2, value: {isOptimistic: true, name: 'second'}}, + { + onyxMethod: Onyx.METHOD.MERGE, + key: member1, + value: {isOptimistic: true, name: 'first'}, + }, + { + onyxMethod: Onyx.METHOD.MERGE, + key: member2, + value: {isOptimistic: true, name: 'second'}, + }, ]); // Concurrent merge fires before cache write — its get() hits the delayed storage mock diff --git a/tests/unit/onyxUtilsTest.ts b/tests/unit/onyxUtilsTest.ts index 93899cfdc..87576136f 100644 --- a/tests/unit/onyxUtilsTest.ts +++ b/tests/unit/onyxUtilsTest.ts @@ -4,7 +4,6 @@ import OnyxUtils from '../../lib/OnyxUtils'; import type {GenericDeepRecord} from '../types'; import utils from '../../lib/utils'; import type {Collection, OnyxCollection} from '../../lib/types'; -import type GenericCollection from '../utils/GenericCollection'; import OnyxCache from '../../lib/OnyxCache'; import * as Logger from '../../lib/Logger'; import StorageMock from '../../lib/storage'; @@ -123,7 +122,7 @@ describe('OnyxUtils', () => { [routeA]: {name: 'Route A'}, [routeB1]: {name: 'Route B1'}, [routeC]: {name: 'Route C'}, - } as GenericCollection); + }); // Replace with new collection data await OnyxUtils.partialSetCollection({ @@ -132,7 +131,7 @@ describe('OnyxUtils', () => { [routeA]: {name: 'New Route A'}, [routeB]: {name: 'New Route B'}, [routeC]: {name: 'New Route C'}, - } as GenericCollection, + }, }); expect(result).toEqual({ @@ -155,11 +154,11 @@ describe('OnyxUtils', () => { await Onyx.mergeCollection(ONYXKEYS.COLLECTION.ROUTES, { [routeA]: {name: 'Route A'}, - } as GenericCollection); + }); await OnyxUtils.partialSetCollection({ collectionKey: ONYXKEYS.COLLECTION.ROUTES, - collection: {} as GenericCollection, + collection: {}, }); expect(result).toEqual({ @@ -180,13 +179,13 @@ describe('OnyxUtils', () => { await Onyx.mergeCollection(ONYXKEYS.COLLECTION.ROUTES, { [routeA]: {name: 'Route A'}, - } as GenericCollection); + }); await OnyxUtils.partialSetCollection({ collectionKey: ONYXKEYS.COLLECTION.ROUTES, collection: { [invalidRoute]: {name: 'Invalid Route'}, - } as GenericCollection, + }, }); expect(result).toEqual({ @@ -217,7 +216,7 @@ describe('OnyxUtils', () => { // Should be called only ONCE with the batched collection (not 3 times) expect(collectionCallback).toHaveBeenCalledTimes(1); - const [collection] = collectionCallback.mock.calls[0]; + const [collection] = collectionCallback.mock.calls.at(0); expect(collection[`${ONYXKEYS.COLLECTION.TEST_KEY}1`]).toEqual({id: 1}); expect(collection[`${ONYXKEYS.COLLECTION.TEST_KEY}2`]).toEqual({id: 2}); expect(collection[`${ONYXKEYS.COLLECTION.TEST_KEY}3`]).toEqual({id: 3}); @@ -550,7 +549,7 @@ describe('OnyxUtils', () => { expect(collectionCallback).toHaveBeenCalledTimes(1); // Collection subscriber receives the full cached collection and subscriber.key - const [receivedCollection, receivedKey] = collectionCallback.mock.calls[0]; + const [receivedCollection, receivedKey] = collectionCallback.mock.calls.at(0); expect(receivedKey).toBe(ONYXKEYS.COLLECTION.TEST_KEY); expect(receivedCollection[entryKey]).toEqual(entryData); @@ -684,11 +683,11 @@ describe('OnyxUtils', () => { d: { i: 'i', j: 'j', - [utils.ONYX_INTERNALS__REPLACE_OBJECT_MARK]: true, + [utils.ONYX_INTERNALS_REPLACE_OBJECT_MARK]: true, }, h: 'h', g: { - [utils.ONYX_INTERNALS__REPLACE_OBJECT_MARK]: true, + [utils.ONYX_INTERNALS_REPLACE_OBJECT_MARK]: true, k: 'k', }, }, @@ -722,11 +721,11 @@ describe('OnyxUtils', () => { d: { i: 'i', j: 'j', - [utils.ONYX_INTERNALS__REPLACE_OBJECT_MARK]: true, + [utils.ONYX_INTERNALS_REPLACE_OBJECT_MARK]: true, }, h: 'h', g: { - [utils.ONYX_INTERNALS__REPLACE_OBJECT_MARK]: true, + [utils.ONYX_INTERNALS_REPLACE_OBJECT_MARK]: true, k: 'k', }, }, @@ -779,7 +778,7 @@ describe('OnyxUtils', () => { // + name + message) is logged exactly once even though the operation retries 6 times. const unclassifiedCalls = logAlertSpy.mock.calls.filter((call) => typeof call[0] === 'string' && call[0].startsWith('Unclassified storage error.')); expect(unclassifiedCalls).toHaveLength(1); - expect(unclassifiedCalls[0][0]).toBe( + expect(unclassifiedCalls.at(0)![0]).toBe( `Unclassified storage error. provider: MemoryOnlyProvider. name: ${genericError.name}. message: ${genericError.message}. onyxMethod: setWithRetry.`, ); }); @@ -973,7 +972,7 @@ describe('OnyxUtils', () => { await Onyx.mergeCollection(collectionKey, { [existingMemberKey]: {value: 'merged'}, [newMemberKey]: {value: 'new'}, - } as GenericCollection); + }); // Cache must reflect the merge regardless of the multiMerge rejection. This is the // cache-first / storage-second invariant that mergeCollectionWithPatches must honor. @@ -1014,7 +1013,7 @@ describe('OnyxUtils', () => { await Onyx.mergeCollection(collectionKey, { [newMemberKey1]: {value: 'first'}, [newMemberKey2]: {value: 'second'}, - } as GenericCollection); + }); // Cache must reflect the merge regardless of the multiSet rejection. This is the // cache-first / storage-second invariant that mergeCollectionWithPatches must honor. @@ -1066,7 +1065,7 @@ describe('OnyxUtils', () => { await Onyx.mergeCollection(collectionKey, { [existingMemberKey]: {value: 'merged'}, [newMemberKey]: {value: 'new'}, - } as GenericCollection); + }); // Before this fix, every retry attempt re-fired keysChanged() — and // Collection-root subscribers fire on every keysChanged() call by contract. @@ -1116,7 +1115,7 @@ describe('OnyxUtils', () => { await Onyx.setCollection(collectionKey, { [memberKey1]: {value: 'first'}, [memberKey2]: {value: 'second'}, - } as GenericCollection); + }); expect(collectionCallback).toHaveBeenCalledTimes(1); }); @@ -1141,7 +1140,7 @@ describe('OnyxUtils', () => { collection: { [memberKey1]: {value: 'first'}, [memberKey2]: {value: 'second'}, - } as GenericCollection, + }, }); expect(collectionCallback).toHaveBeenCalledTimes(1); @@ -1191,7 +1190,7 @@ describe('OnyxUtils', () => { await Onyx.mergeCollection(collectionKey, { [existingKey1]: {value: 'merged-1'}, [existingKey2]: {value: 'merged-2'}, - } as GenericCollection); + }); // With every existingKey warm, the diff swaps Promise.all(get) for Promise.resolve(), // so no storage reads should happen during the pre-warm. @@ -1224,12 +1223,12 @@ describe('OnyxUtils', () => { [coldKey1]: {value: 'merged-1'}, [coldKey2]: {value: 'merged-2'}, [warmKey]: {value: 'merged-3'}, - } as GenericCollection); + }); // OnyxUtils.multiGet filters to cache-missing keys before issuing Storage.multiGet, so we // expect exactly one batched read containing only the cold keys (the warm key is skipped). expect(multiGetSpy).toHaveBeenCalledTimes(1); - const requestedKeys = multiGetSpy.mock.calls[0][0] as string[]; + const requestedKeys = multiGetSpy.mock.calls.at(0)![0]; expect(requestedKeys.sort()).toEqual([coldKey1, coldKey2].sort()); // No individual Storage.getItem calls during pre-warm. Old code path would have fired one @@ -1248,7 +1247,7 @@ describe('OnyxUtils', () => { await Onyx.mergeCollection(collectionKey, { [coldKey]: {c: 3}, - } as GenericCollection); + }); // If the pre-warm did NOT populate the cache from storage, fastMerge would treat the // previous value as undefined and the result would drop {a:1, b:2}. With the pre-warm @@ -1277,7 +1276,7 @@ describe('OnyxUtils', () => { key: collectionKey, value: { [existingKey]: {value: 'merged'}, - } as GenericCollection, + }, }, ]); @@ -1286,7 +1285,7 @@ describe('OnyxUtils', () => { // one merged value — not undefined first and the merged value on a later microtask. const broadcasts = collectionCallback.mock.calls.map((c) => c[0]); expect(broadcasts).toHaveLength(1); - expect(broadcasts[0]?.[existingKey]).toEqual({value: 'merged'}); + expect(broadcasts.at(0)?.[existingKey]).toEqual({value: 'merged'}); }); it('equivalence: warm-path and cold-path produce the same final cache state for the same merge', async () => { @@ -1298,7 +1297,7 @@ describe('OnyxUtils', () => { await Onyx.set(memberKey, {value: 'before', extra: 'kept'}); await Onyx.mergeCollection(collectionKey, { [memberKey]: delta, - } as GenericCollection); + }); const warmResult = OnyxCache.getCollectionData(collectionKey)?.[memberKey]; // Reset and replay with a cold cache before the merge. @@ -1307,7 +1306,7 @@ describe('OnyxUtils', () => { evictFromCache(memberKey); await Onyx.mergeCollection(collectionKey, { [memberKey]: delta, - } as GenericCollection); + }); const coldResult = OnyxCache.getCollectionData(collectionKey)?.[memberKey]; expect(warmResult).toEqual(coldResult); @@ -1350,7 +1349,7 @@ describe('OnyxUtils', () => { const result = await Onyx.mergeCollection(collectionKey, { [coldMemberKey]: {merged: true}, [newMemberKey]: {value: 'new'}, - } as GenericCollection).catch((e: unknown) => { + }).catch((e: unknown) => { outerRejected = e; }); expect(outerRejected).toBeNull(); @@ -1502,7 +1501,9 @@ describe('OnyxUtils', () => { // The probe ran: the evictable key was evicted, the write landed, and the successful retry closed the circuit. expect(LocalOnyxCache.hasCacheForKey(evictableKey)).toBe(false); - expect(LocalOnyxCache.get(ONYXKEYS.TEST_KEY)).toEqual({test: 'recovered'}); + expect(LocalOnyxCache.get(ONYXKEYS.TEST_KEY)).toEqual({ + test: 'recovered', + }); expect(LocalStorageCircuitBreaker.isAllowed()).toBe(true); expect(LocalStorageCircuitBreaker.isAllowed()).toBe(true); @@ -1675,14 +1676,19 @@ describe('OnyxUtils', () => { await LocalOnyx.set(unrelatedKey, {value: 'evict-me'}); const memberCalls: unknown[] = []; - LocalOnyx.connect({key: memberKey, callback: (value) => memberCalls.push(value)}); + LocalOnyx.connect({ + key: memberKey, + callback: (value) => memberCalls.push(value), + }); await waitForPromisesToResolve(); memberCalls.length = 0; // Storage.multiMerge rejects once with disk-full, then succeeds on retry. LocalStorageMock.multiMerge = jest.fn(LocalStorageMock.multiMerge).mockRejectedValueOnce(diskFullError).mockImplementation(LocalStorageMock.multiMerge); - await LocalOnyx.mergeCollection(collectionKey, {[memberKey]: {value: 'merged'}} as GenericCollection); + await LocalOnyx.mergeCollection(collectionKey, { + [memberKey]: {value: 'merged'}, + }); // The old code evicted the in-flight key and re-ran the merge against an empty cache, // collapsing {id: 1, value: 'orig'} + {value: 'merged'} to just {value: 'merged'}. Now @@ -1702,7 +1708,10 @@ describe('OnyxUtils', () => { expect(LocalOnyxCache.getKeyForEviction()).toBe(memberKey); const memberCalls: unknown[] = []; - LocalOnyx.connect({key: memberKey, callback: (value) => memberCalls.push(value)}); + LocalOnyx.connect({ + key: memberKey, + callback: (value) => memberCalls.push(value), + }); await waitForPromisesToResolve(); memberCalls.length = 0; @@ -1710,7 +1719,9 @@ describe('OnyxUtils', () => { // finds no acceptable key and reports the quota instead of dropping (and truncating) it. LocalStorageMock.multiMerge = jest.fn(LocalStorageMock.multiMerge).mockRejectedValue(diskFullError); - await LocalOnyx.mergeCollection(collectionKey, {[memberKey]: {value: 'merged'}} as GenericCollection); + await LocalOnyx.mergeCollection(collectionKey, { + [memberKey]: {value: 'merged'}, + }); expect(LocalOnyxCache.get(memberKey)).toEqual({id: 1, value: 'merged'}); expect(memberCalls.at(-1)).toEqual({id: 1, value: 'merged'}); diff --git a/tests/unit/storage/providers/IDBKeyvalProviderTest.ts b/tests/unit/storage/providers/IDBKeyvalProviderTest.ts index 26b0e9081..b057ba8fc 100644 --- a/tests/unit/storage/providers/IDBKeyvalProviderTest.ts +++ b/tests/unit/storage/providers/IDBKeyvalProviderTest.ts @@ -54,9 +54,9 @@ describe('IDBKeyValProvider', () => { await IDB.setMany(testEntries, IDBKeyValProvider.store); expect(await IDBKeyValProvider.multiGet([`${ONYXKEYS.COLLECTION.TEST_KEY}id1`, ONYXKEYS.TEST_KEY, ONYXKEYS.TEST_KEY_2])).toEqual([ - testEntries[3], - testEntries[0], - testEntries[1], + testEntries.at(3), + testEntries.at(0), + testEntries.at(1), ]); }); }); @@ -121,7 +121,7 @@ describe('IDBKeyValProvider', () => { property: { nestedProperty: { nestedKey2: 'nestedValue2_changed', - [utils.ONYX_INTERNALS__REPLACE_OBJECT_MARK]: true, + [utils.ONYX_INTERNALS_REPLACE_OBJECT_MARK]: true, }, newKey: 'newValue', }, @@ -134,7 +134,9 @@ describe('IDBKeyValProvider', () => { const expectedEntries = structuredClone(changedEntries); const expectedTestKey3Value = structuredClone(testEntries[2])[1] as GenericDeepRecord; expectedTestKey3Value.key = 'value_changed'; - expectedTestKey3Value.property.nestedProperty = {nestedKey2: 'nestedValue2_changed'}; + expectedTestKey3Value.property.nestedProperty = { + nestedKey2: 'nestedValue2_changed', + }; expectedTestKey3Value.property.newKey = 'newValue'; expectedEntries[2][1] = expectedTestKey3Value; @@ -175,7 +177,9 @@ describe('IDBKeyValProvider', () => { it('should insert a new record when key does not exist', async () => { await IDBKeyValProvider.multiMerge([[ONYXKEYS.TEST_KEY_2, {fresh: true}]]); - expect(await IDBKeyValProvider.getItem(ONYXKEYS.TEST_KEY_2)).toEqual({fresh: true}); + expect(await IDBKeyValProvider.getItem(ONYXKEYS.TEST_KEY_2)).toEqual({ + fresh: true, + }); }); }); @@ -263,7 +267,7 @@ describe('IDBKeyValProvider', () => { await IDBKeyValProvider.mergeItem(ONYXKEYS.TEST_KEY_3, { key: 'value_changed', property: { - [utils.ONYX_INTERNALS__REPLACE_OBJECT_MARK]: true, + [utils.ONYX_INTERNALS_REPLACE_OBJECT_MARK]: true, newKey: 'newValue', }, }); @@ -328,7 +332,15 @@ describe('IDBKeyValProvider', () => { beforeEach(() => { Object.defineProperty(window.navigator, 'storage', { value: { - estimate: jest.fn().mockResolvedValue({quota: 750000, usage: 250000, usageDetails: {caches: 100000, fileSystem: 50000, indexedDB: 100000}}), + estimate: jest.fn().mockResolvedValue({ + quota: 750000, + usage: 250000, + usageDetails: { + caches: 100000, + fileSystem: 50000, + indexedDB: 100000, + }, + }), }, configurable: true, }); diff --git a/tests/unit/storage/providers/SQLiteProviderTest.ts b/tests/unit/storage/providers/SQLiteProviderTest.ts index bc0cac00a..87f85a696 100644 --- a/tests/unit/storage/providers/SQLiteProviderTest.ts +++ b/tests/unit/storage/providers/SQLiteProviderTest.ts @@ -12,7 +12,9 @@ import {resetAllDatabases} from '../../mocks/sqliteMock'; // `jest.mock` is hoisted by Jest above the imports — register the SQLite mock // (overriding the global jestSetup.js mock) and a tiny device-info stub. jest.mock('react-native-nitro-sqlite', () => require('../../mocks/sqliteMock')); -jest.mock('react-native-device-info', () => ({getFreeDiskStorage: () => 12345})); +jest.mock('react-native-device-info', () => ({ + getFreeDiskStorage: () => 12345, +})); const ONYXKEYS = { TEST_KEY: 'test', @@ -69,9 +71,9 @@ describe('SQLiteProvider', () => { // (rows come back in primary-key order). IDB's getMany() does. So this // test mirrors the IDB one but asserts membership rather than order. it('should return the tuples for the keys supplied in a batch', async () => { - await SQLiteProvider.multiSet(testEntries as Array<[string, unknown]>); + await SQLiteProvider.multiSet(testEntries); const out = await SQLiteProvider.multiGet([`${ONYXKEYS.COLLECTION.TEST_KEY}id1`, ONYXKEYS.TEST_KEY, ONYXKEYS.TEST_KEY_2]); - expect(out).toEqual(expect.arrayContaining([testEntries[3], testEntries[0], testEntries[1]])); + expect(out).toEqual(expect.arrayContaining([testEntries.at(3), testEntries.at(0), testEntries.at(1)])); expect(out).toHaveLength(3); }); }); @@ -127,7 +129,7 @@ describe('SQLiteProvider', () => { // before serializing, otherwise JSON.stringify(undefined) === undefined // and the row would store a literal "undefined" string. it('treats undefined as null', async () => { - await SQLiteProvider.multiSet([[ONYXKEYS.TEST_KEY, undefined as unknown as null]]); + await SQLiteProvider.multiSet([[ONYXKEYS.TEST_KEY, undefined]]); expect(await SQLiteProvider.getItem(ONYXKEYS.TEST_KEY)).toBeNull(); }); }); @@ -145,7 +147,7 @@ describe('SQLiteProvider', () => { property: { nestedProperty: { nestedKey2: 'nestedValue2_changed', - [utils.ONYX_INTERNALS__REPLACE_OBJECT_MARK]: true, + [utils.ONYX_INTERNALS_REPLACE_OBJECT_MARK]: true, }, newKey: 'newValue', }, @@ -159,7 +161,9 @@ describe('SQLiteProvider', () => { const expectedTestKey3Value = structuredClone(testEntries[2])[1] as GenericDeepRecord; expectedTestKey3Value.key = 'value_changed'; - expectedTestKey3Value.property.nestedProperty = {nestedKey2: 'nestedValue2_changed'}; + expectedTestKey3Value.property.nestedProperty = { + nestedKey2: 'nestedValue2_changed', + }; expectedTestKey3Value.property.newKey = 'newValue'; await SQLiteProvider.multiMerge(changedEntries); @@ -173,13 +177,19 @@ describe('SQLiteProvider', () => { it('should insert a new record when key does not exist', async () => { await SQLiteProvider.multiMerge([[ONYXKEYS.TEST_KEY_2, {fresh: true}]]); - expect(await SQLiteProvider.getItem(ONYXKEYS.TEST_KEY_2)).toEqual({fresh: true}); + expect(await SQLiteProvider.getItem(ONYXKEYS.TEST_KEY_2)).toEqual({ + fresh: true, + }); }); it('should shallow-merge existing record_key value', async () => { await SQLiteProvider.setItem(ONYXKEYS.TEST_KEY_3, {a: 1, b: 2}); await SQLiteProvider.multiMerge([[ONYXKEYS.TEST_KEY_3, {b: 99, c: 3}]]); - expect(await SQLiteProvider.getItem(ONYXKEYS.TEST_KEY_3)).toEqual({a: 1, b: 99, c: 3}); + expect(await SQLiteProvider.getItem(ONYXKEYS.TEST_KEY_3)).toEqual({ + a: 1, + b: 99, + c: 3, + }); }); it('should deep-merge nested objects', async () => { @@ -198,7 +208,11 @@ describe('SQLiteProvider', () => { await SQLiteProvider.setItem(ONYXKEYS.TEST_KEY_3, { keepMe: 'still here', removeMe: 'gone soon', - outer: {keepInner: 1, removeInner: 2, deeper: {keepDeep: 'a', removeDeep: 'b'}}, + outer: { + keepInner: 1, + removeInner: 2, + deeper: {keepDeep: 'a', removeDeep: 'b'}, + }, }); await SQLiteProvider.multiMerge([ @@ -232,7 +246,7 @@ describe('SQLiteProvider', () => { nested: { // The mark is filtered out by SQLiteProvider's `objectMarkRemover` // before the value is stringified. - [utils.ONYX_INTERNALS__REPLACE_OBJECT_MARK]: true, + [utils.ONYX_INTERNALS_REPLACE_OBJECT_MARK]: true, newKey: 'newValue', }, }, @@ -255,7 +269,10 @@ describe('SQLiteProvider', () => { it('should merge all the supported kinds of data correctly', async () => { await SQLiteProvider.setItem(ONYXKEYS.TEST_KEY, 'value'); await SQLiteProvider.setItem(ONYXKEYS.TEST_KEY_2, 1000); - await SQLiteProvider.setItem(ONYXKEYS.TEST_KEY_3, {key: 'value', property: {propertyKey: 'propertyValue'}}); + await SQLiteProvider.setItem(ONYXKEYS.TEST_KEY_3, { + key: 'value', + property: {propertyKey: 'propertyValue'}, + }); await SQLiteProvider.setItem(`${ONYXKEYS.COLLECTION.TEST_KEY}id1` as string, true); await SQLiteProvider.setItem(`${ONYXKEYS.COLLECTION.TEST_KEY}id2` as string, ['a', {key: 'value'}, 1, true]); @@ -266,7 +283,7 @@ describe('SQLiteProvider', () => { { key: 'value_changed', property: { - [utils.ONYX_INTERNALS__REPLACE_OBJECT_MARK]: true, + [utils.ONYX_INTERNALS_REPLACE_OBJECT_MARK]: true, newKey: 'newValue', }, }, @@ -277,7 +294,10 @@ describe('SQLiteProvider', () => { expect(await SQLiteProvider.getItem(ONYXKEYS.TEST_KEY)).toEqual('value_changed'); expect(await SQLiteProvider.getItem(ONYXKEYS.TEST_KEY_2)).toEqual(1001); - expect(await SQLiteProvider.getItem(ONYXKEYS.TEST_KEY_3)).toEqual({key: 'value_changed', property: {newKey: 'newValue'}}); + expect(await SQLiteProvider.getItem(ONYXKEYS.TEST_KEY_3)).toEqual({ + key: 'value_changed', + property: {newKey: 'newValue'}, + }); expect(await SQLiteProvider.getItem(`${ONYXKEYS.COLLECTION.TEST_KEY}id1`)).toEqual(false); expect(await SQLiteProvider.getItem(`${ONYXKEYS.COLLECTION.TEST_KEY}id2`)).toEqual(['a', {newKey: 'newValue'}]); }); @@ -338,7 +358,9 @@ describe('SQLiteProvider', () => { // SQLite allocates pages on init (table + WAL), so bytesUsed is non-0 from the // start; assert that a write increases it rather than comparing to 0. const before = await SQLiteProvider.getDatabaseSize(); - await SQLiteProvider.setItem(ONYXKEYS.TEST_KEY, {payload: 'x'.repeat(64 * 1024)}); + await SQLiteProvider.setItem(ONYXKEYS.TEST_KEY, { + payload: 'x'.repeat(64 * 1024), + }); const after = await SQLiteProvider.getDatabaseSize(); expect(after.bytesUsed).toBeGreaterThan(before.bytesUsed); diff --git a/tests/unit/storage/providers/createStoreTest.ts b/tests/unit/storage/providers/createStoreTest.ts index 16f206747..c6193bec0 100644 --- a/tests/unit/storage/providers/createStoreTest.ts +++ b/tests/unit/storage/providers/createStoreTest.ts @@ -16,11 +16,10 @@ function uniqueDBName() { */ async function captureDB(store: ReturnType): Promise { const captured: {db?: IDBDatabase} = {}; - const original = IDBDatabase.prototype.transaction; const spy = jest.spyOn(IDBDatabase.prototype, 'transaction').mockImplementation(function (this: IDBDatabase, ...args) { captured.db = this; spy.mockRestore(); - return original.apply(this, args); + return this.transaction(...args); }); await store('readonly', (s) => IDB.promisifyRequest(s.getAllKeys())); return captured.db; @@ -48,14 +47,14 @@ describe('createStore', () => { return IDB.promisifyRequest(s.transaction); }); - const original = IDBDatabase.prototype.transaction; let callCount = 0; - jest.spyOn(IDBDatabase.prototype, 'transaction').mockImplementation(function (this: IDBDatabase, ...args) { + const spy = jest.spyOn(IDBDatabase.prototype, 'transaction').mockImplementation(function (this: IDBDatabase, ...args) { callCount += 1; if (callCount === 1) { throw new DOMException('The database connection is closing.', 'InvalidStateError'); } - return original.apply(this, args); + spy.mockRestore(); + return this.transaction(...args); }); const result = await store('readonly', (s) => IDB.promisifyRequest(s.get('key1'))); @@ -140,14 +139,14 @@ describe('createStore', () => { return IDB.promisifyRequest(s.transaction); }); - const original = IDBDatabase.prototype.transaction; let callCount = 0; - jest.spyOn(IDBDatabase.prototype, 'transaction').mockImplementation(function (this: IDBDatabase, ...args) { + const spy = jest.spyOn(IDBDatabase.prototype, 'transaction').mockImplementation(function (this: IDBDatabase, ...args) { callCount += 1; if (callCount === 1) { throw new DOMException('The database connection is closing.', 'InvalidStateError'); } - return original.apply(this, args); + spy.mockRestore(); + return this.transaction(...args); }); await store('readwrite', (s) => { @@ -173,14 +172,14 @@ describe('createStore', () => { return IDB.promisifyRequest(s.transaction); }); - const original = IDBDatabase.prototype.transaction; let callCount = 0; - jest.spyOn(IDBDatabase.prototype, 'transaction').mockImplementation(function (this: IDBDatabase, ...args) { + const spy = jest.spyOn(IDBDatabase.prototype, 'transaction').mockImplementation(function (this: IDBDatabase, ...args) { callCount += 1; if (callCount === 1) { throw new DOMException('The database connection is closing.', 'InvalidStateError'); } - return original.apply(this, args); + spy.mockRestore(); + return this.transaction(...args); }); await store('readwrite', (s) => { @@ -232,8 +231,10 @@ describe('createStore', () => { const store = createStore(dbName, STORE_NAME); const db = await captureDB(store); - expect(db).toBeDefined(); - const closeSpy = jest.spyOn(db!, 'close'); + if (!db) { + throw new Error('Expected db to be captured'); + } + const closeSpy = jest.spyOn(db, 'close'); // @ts-expect-error -- our handler ignores the event argument db?.onversionchange?.call(db, new Event('versionchange')); @@ -276,14 +277,14 @@ describe('createStore', () => { return IDB.promisifyRequest(s.transaction); }); - const original = IDBDatabase.prototype.transaction; let callCount = 0; - jest.spyOn(IDBDatabase.prototype, 'transaction').mockImplementation(function (this: IDBDatabase, ...args) { + const spy = jest.spyOn(IDBDatabase.prototype, 'transaction').mockImplementation(function (this: IDBDatabase, ...args) { callCount++; if (callCount === 1) { throw backingStoreError(); } - return original.apply(this, args); + spy.mockRestore(); + return this.transaction(...args); }); const result = await store('readonly', (s) => IDB.promisifyRequest(s.get('key1'))); @@ -324,7 +325,7 @@ describe('createStore', () => { value: backingStoreError(), configurable: true, }); - req.onerror?.(new Event('error') as Event & {target: IDBOpenDBRequest}); + req.onerror?.(new Event('error')); }); return req; } @@ -351,7 +352,7 @@ describe('createStore', () => { value: backingStoreError(), configurable: true, }); - req.onerror?.(new Event('error') as Event & {target: IDBOpenDBRequest}); + req.onerror?.(new Event('error')); }); return req; }); @@ -377,8 +378,6 @@ describe('createStore', () => { return IDB.promisifyRequest(s.transaction); }); - const original = IDBDatabase.prototype.transaction; - // Drain budget to 1 remaining: fail twice, each heals successfully for (let i = 0; i < 2; i++) { let callCount = 0; @@ -388,7 +387,7 @@ describe('createStore', () => { throw backingStoreError(); } spy.mockRestore(); - return original.apply(this, args); + return this.transaction(...args); }); await store('readonly', (s) => IDB.promisifyRequest(s.get('key1'))); } @@ -407,7 +406,7 @@ describe('createStore', () => { throw backingStoreError(); } spy.mockRestore(); - return original.apply(this, args); + return this.transaction(...args); }); const result = await store('readonly', (s) => IDB.promisifyRequest(s.get('key1'))); expect(result).toBe('value'); @@ -458,14 +457,14 @@ describe('createStore', () => { return IDB.promisifyRequest(s.transaction); }); - const original = IDBDatabase.prototype.transaction; let callCount = 0; - jest.spyOn(IDBDatabase.prototype, 'transaction').mockImplementation(function (this: IDBDatabase, ...args) { + const spy = jest.spyOn(IDBDatabase.prototype, 'transaction').mockImplementation(function (this: IDBDatabase, ...args) { callCount++; if (callCount === 1) { throw connectionLostError(); } - return original.apply(this, args); + spy.mockRestore(); + return this.transaction(...args); }); const result = await store('readonly', (s) => IDB.promisifyRequest(s.get('key1'))); @@ -490,7 +489,7 @@ describe('createStore', () => { value: connectionLostError(), configurable: true, }); - req.onerror?.(new Event('error') as Event & {target: IDBOpenDBRequest}); + req.onerror?.(new Event('error')); }); return req; }); @@ -511,14 +510,14 @@ describe('createStore', () => { return IDB.promisifyRequest(s.transaction); }); - const original = IDBDatabase.prototype.transaction; let callCount = 0; - jest.spyOn(IDBDatabase.prototype, 'transaction').mockImplementation(function (this: IDBDatabase, ...args) { + const spy = jest.spyOn(IDBDatabase.prototype, 'transaction').mockImplementation(function (this: IDBDatabase, ...args) { callCount++; if (callCount === 1) { throw new DOMException('Connection is closing.', 'UnknownError'); } - return original.apply(this, args); + spy.mockRestore(); + return this.transaction(...args); }); const result = await store('readonly', (s) => IDB.promisifyRequest(s.get('key1'))); @@ -534,7 +533,6 @@ describe('createStore', () => { return IDB.promisifyRequest(s.transaction); }); - const original = IDBDatabase.prototype.transaction; const backingStoreError = () => new DOMException('Internal error opening backing store for indexedDB.open.', 'UnknownError'); // Connection-lost errors are transient and must NOT decrement the backing-store budget. @@ -549,7 +547,7 @@ describe('createStore', () => { throw connectionLostError(); } spy.mockRestore(); - return original.apply(this, args); + return this.transaction(...args); }); await store('readonly', (s) => IDB.promisifyRequest(s.get('key1'))); } @@ -562,7 +560,7 @@ describe('createStore', () => { throw backingStoreError(); } backingSpy.mockRestore(); - return original.apply(this, args); + return this.transaction(...args); }); const result = await store('readonly', (s) => IDB.promisifyRequest(s.get('key1'))); expect(result).toBe('value'); @@ -582,14 +580,14 @@ describe('createStore', () => { return IDB.promisifyRequest(s.transaction); }); - const original = IDBDatabase.prototype.transaction; let callCount = 0; - jest.spyOn(IDBDatabase.prototype, 'transaction').mockImplementation(function (this: IDBDatabase, ...args) { + const spy = jest.spyOn(IDBDatabase.prototype, 'transaction').mockImplementation(function (this: IDBDatabase, ...args) { callCount++; if (callCount === 1) { throw new DOMException('IDB write transaction aborted without an error', 'AbortError'); } - return original.apply(this, args); + spy.mockRestore(); + return this.transaction(...args); }); const result = await store('readonly', (s) => IDB.promisifyRequest(s.get('key1'))); @@ -628,14 +626,14 @@ describe('createStore', () => { simulateVisibilityChange('hidden'); - const original = IDBDatabase.prototype.transaction; let probeIntercepted = false; - jest.spyOn(IDBDatabase.prototype, 'transaction').mockImplementation(function (this: IDBDatabase, ...args) { + const spy = jest.spyOn(IDBDatabase.prototype, 'transaction').mockImplementation(function (this: IDBDatabase, ...args) { if (!probeIntercepted) { probeIntercepted = true; throw new DOMException('Connection to Indexed Database server lost. Refresh the page to try again', 'UnknownError'); } - return original.apply(this, args); + spy.mockRestore(); + return this.transaction(...args); }); simulateVisibilityChange('visible'); @@ -698,14 +696,14 @@ describe('createStore', () => { simulateVisibilityChange('hidden'); - const original = IDBDatabase.prototype.transaction; let callCount = 0; - jest.spyOn(IDBDatabase.prototype, 'transaction').mockImplementation(function (this: IDBDatabase, ...args) { + const spy = jest.spyOn(IDBDatabase.prototype, 'transaction').mockImplementation(function (this: IDBDatabase, ...args) { callCount++; if (callCount === 1) { throw new DOMException('The database connection is closing.', 'InvalidStateError'); } - return original.apply(this, args); + spy.mockRestore(); + return this.transaction(...args); }); simulateVisibilityChange('visible'); @@ -738,7 +736,7 @@ describe('createStore', () => { value: new DOMException('probe open failed', 'UnknownError'), configurable: true, }); - req.onerror?.(new Event('error') as Event & {target: IDBOpenDBRequest}); + req.onerror?.(new Event('error')); }; return req; }); diff --git a/tests/unit/useOnyxTest.ts b/tests/unit/useOnyxTest.ts index d5b9d0017..b655bad4d 100644 --- a/tests/unit/useOnyxTest.ts +++ b/tests/unit/useOnyxTest.ts @@ -42,7 +42,9 @@ describe('useOnyx', () => { }); it('should not throw any errors when changing from a collection member key to another one', async () => { - const {rerender} = renderHook((key: string) => useOnyx(key), {initialProps: `${ONYXKEYS.COLLECTION.TEST_KEY}1` as string}); + const {rerender} = renderHook((key: string) => useOnyx(key), { + initialProps: `${ONYXKEYS.COLLECTION.TEST_KEY}1`, + }); try { await act(async () => { @@ -54,7 +56,9 @@ describe('useOnyx', () => { }); it('should transition through loading when switching between collection member keys that both resolve to undefined', async () => { - const {result, rerender} = renderHook((key: string) => useOnyx(key), {initialProps: `${ONYXKEYS.COLLECTION.TEST_KEY}1` as string}); + const {result, rerender} = renderHook((key: string) => useOnyx(key), { + initialProps: `${ONYXKEYS.COLLECTION.TEST_KEY}1`, + }); // Wait for initial key to fully load await act(async () => waitForPromisesToResolve()); @@ -77,7 +81,9 @@ describe('useOnyx', () => { it('should return cached value immediately with loaded status when switching to a key that has data', async () => { Onyx.set(`${ONYXKEYS.COLLECTION.TEST_KEY}2`, 'test_value'); - const {result, rerender} = renderHook((key: string) => useOnyx(key), {initialProps: `${ONYXKEYS.COLLECTION.TEST_KEY}1` as string}); + const {result, rerender} = renderHook((key: string) => useOnyx(key), { + initialProps: `${ONYXKEYS.COLLECTION.TEST_KEY}1`, + }); await act(async () => waitForPromisesToResolve()); @@ -100,7 +106,9 @@ describe('useOnyx', () => { it('should clear previous data and transition through loading when switching from a key with data to one without', async () => { Onyx.set(`${ONYXKEYS.COLLECTION.TEST_KEY}1`, 'initial_value'); - const {result, rerender} = renderHook((key: string) => useOnyx(key), {initialProps: `${ONYXKEYS.COLLECTION.TEST_KEY}1` as string}); + const {result, rerender} = renderHook((key: string) => useOnyx(key), { + initialProps: `${ONYXKEYS.COLLECTION.TEST_KEY}1`, + }); await act(async () => waitForPromisesToResolve()); @@ -123,7 +131,9 @@ describe('useOnyx', () => { Onyx.set(`${ONYXKEYS.COLLECTION.TEST_KEY}1`, 'value_one'); Onyx.set(`${ONYXKEYS.COLLECTION.TEST_KEY}2`, 'value_two'); - const {result, rerender} = renderHook((key: string) => useOnyx(key), {initialProps: `${ONYXKEYS.COLLECTION.TEST_KEY}1` as string}); + const {result, rerender} = renderHook((key: string) => useOnyx(key), { + initialProps: `${ONYXKEYS.COLLECTION.TEST_KEY}1`, + }); await act(async () => waitForPromisesToResolve()); @@ -140,12 +150,18 @@ describe('useOnyx', () => { }); it('should apply the selector against the new key data when switching keys', async () => { - Onyx.set(`${ONYXKEYS.COLLECTION.TEST_KEY}1`, {id: 'entry1_id', name: 'entry1_name'}); - Onyx.set(`${ONYXKEYS.COLLECTION.TEST_KEY}2`, {id: 'entry2_id', name: 'entry2_name'}); + Onyx.set(`${ONYXKEYS.COLLECTION.TEST_KEY}1`, { + id: 'entry1_id', + name: 'entry1_name', + }); + Onyx.set(`${ONYXKEYS.COLLECTION.TEST_KEY}2`, { + id: 'entry2_id', + name: 'entry2_name', + }); const selector = ((entry: OnyxEntry<{id: string; name: string}>) => entry?.name) as UseOnyxSelector; - const {result, rerender} = renderHook((key: string) => useOnyx(key, {selector}), {initialProps: `${ONYXKEYS.COLLECTION.TEST_KEY}1` as string}); + const {result, rerender} = renderHook((key: string) => useOnyx(key, {selector}), {initialProps: `${ONYXKEYS.COLLECTION.TEST_KEY}1`}); await act(async () => waitForPromisesToResolve()); @@ -166,7 +182,9 @@ describe('useOnyx', () => { Onyx.set(`${ONYXKEYS.COLLECTION.TEST_KEY}2`, 'value_two'); Onyx.set(`${ONYXKEYS.COLLECTION.TEST_KEY}3`, 'value_three'); - const {result, rerender} = renderHook((key: string) => useOnyx(key), {initialProps: `${ONYXKEYS.COLLECTION.TEST_KEY}1` as string}); + const {result, rerender} = renderHook((key: string) => useOnyx(key), { + initialProps: `${ONYXKEYS.COLLECTION.TEST_KEY}1`, + }); await act(async () => waitForPromisesToResolve()); @@ -186,7 +204,9 @@ describe('useOnyx', () => { Onyx.set(ONYXKEYS.TEST_KEY, 'value_one'); Onyx.set(ONYXKEYS.TEST_KEY_2, 'value_two'); - const {result, rerender} = renderHook((key: string) => useOnyx(key), {initialProps: ONYXKEYS.TEST_KEY as string}); + const {result, rerender} = renderHook((key: string) => useOnyx(key), { + initialProps: ONYXKEYS.TEST_KEY, + }); await act(async () => waitForPromisesToResolve()); @@ -209,7 +229,9 @@ describe('useOnyx', () => { Onyx.set(`${ONYXKEYS.COLLECTION.TEST_KEY}1`, 'value_one'); Onyx.set(`${ONYXKEYS.COLLECTION.TEST_KEY}2`, 'value_two'); - const {result, rerender} = renderHook((key: string) => useOnyx(key), {initialProps: `${ONYXKEYS.COLLECTION.TEST_KEY}1` as string}); + const {result, rerender} = renderHook((key: string) => useOnyx(key), { + initialProps: `${ONYXKEYS.COLLECTION.TEST_KEY}1`, + }); await act(async () => waitForPromisesToResolve()); @@ -427,17 +449,27 @@ describe('useOnyx', () => { Onyx.mergeCollection(ONYXKEYS.COLLECTION.TEST_KEY, { [`${ONYXKEYS.COLLECTION.TEST_KEY}entry1`]: entry1, [`${ONYXKEYS.COLLECTION.TEST_KEY}entry2`]: entry2, - } as GenericCollection); + }); const {result} = renderHook(() => useOnyx(ONYXKEYS.COLLECTION.TEST_KEY)); await act(async () => waitForPromisesToResolve()); - const firstCollection = result.current[0] as OnyxCollection<{id: string; name: string}>; + const firstCollection = result.current[0] as OnyxCollection<{ + id: string; + name: string; + }>; const firstEntry1Ref = firstCollection?.[`${ONYXKEYS.COLLECTION.TEST_KEY}entry1`]; - await act(async () => Onyx.merge(`${ONYXKEYS.COLLECTION.TEST_KEY}entry2`, {name: 'entry2_updated'})); + await act(async () => + Onyx.merge(`${ONYXKEYS.COLLECTION.TEST_KEY}entry2`, { + name: 'entry2_updated', + }), + ); - const secondCollection = result.current[0] as OnyxCollection<{id: string; name: string}>; + const secondCollection = result.current[0] as OnyxCollection<{ + id: string; + name: string; + }>; expect(secondCollection).not.toBe(firstCollection); expect(secondCollection?.[`${ONYXKEYS.COLLECTION.TEST_KEY}entry1`]).toBe(firstEntry1Ref); @@ -445,15 +477,23 @@ describe('useOnyx', () => { it('should keep the same collection reference when no members change', async () => { Onyx.mergeCollection(ONYXKEYS.COLLECTION.TEST_KEY, { - [`${ONYXKEYS.COLLECTION.TEST_KEY}entry1`]: {id: 'entry1_id', name: 'entry1_name'}, - } as GenericCollection); + [`${ONYXKEYS.COLLECTION.TEST_KEY}entry1`]: { + id: 'entry1_id', + name: 'entry1_name', + }, + }); const {result} = renderHook(() => useOnyx(ONYXKEYS.COLLECTION.TEST_KEY)); await act(async () => waitForPromisesToResolve()); const firstResult = result.current; - await act(async () => Onyx.set(`${ONYXKEYS.COLLECTION.TEST_KEY}entry1`, {id: 'entry1_id', name: 'entry1_name'})); + await act(async () => + Onyx.set(`${ONYXKEYS.COLLECTION.TEST_KEY}entry1`, { + id: 'entry1_id', + name: 'entry1_name', + }), + ); expect(result.current).toBe(firstResult); }); @@ -472,7 +512,12 @@ describe('useOnyx', () => { expect(result.current[0]).toEqual('id - test_id, name - test_name'); expect(result.current[1].status).toEqual('loaded'); - await act(async () => Onyx.merge(ONYXKEYS.TEST_KEY, {id: 'changed_id', name: 'changed_name'})); + await act(async () => + Onyx.merge(ONYXKEYS.TEST_KEY, { + id: 'changed_id', + name: 'changed_name', + }), + ); expect(result.current[0]).toEqual('id - changed_id, name - changed_name'); expect(result.current[1].status).toEqual('loaded'); @@ -480,10 +525,19 @@ describe('useOnyx', () => { it('should return selected data from a collection key', async () => { Onyx.mergeCollection(ONYXKEYS.COLLECTION.TEST_KEY, { - [`${ONYXKEYS.COLLECTION.TEST_KEY}entry1`]: {id: 'entry1_id', name: 'entry1_name'}, - [`${ONYXKEYS.COLLECTION.TEST_KEY}entry2`]: {id: 'entry2_id', name: 'entry2_name'}, - [`${ONYXKEYS.COLLECTION.TEST_KEY}entry3`]: {id: 'entry3_id', name: 'entry3_name'}, - } as GenericCollection); + [`${ONYXKEYS.COLLECTION.TEST_KEY}entry1`]: { + id: 'entry1_id', + name: 'entry1_name', + }, + [`${ONYXKEYS.COLLECTION.TEST_KEY}entry2`]: { + id: 'entry2_id', + name: 'entry2_name', + }, + [`${ONYXKEYS.COLLECTION.TEST_KEY}entry3`]: { + id: 'entry3_id', + name: 'entry3_name', + }, + }); const {result} = renderHook(() => useOnyx(ONYXKEYS.COLLECTION.TEST_KEY, { @@ -526,7 +580,9 @@ describe('useOnyx', () => { // object const {result: objectResult} = renderHook(() => useOnyx(ONYXKEYS.TEST_KEY, { - selector: ((entry: OnyxEntry<{id: string; name: string}>) => ({id: entry?.id})) as UseOnyxSelector, + selector: ((entry: OnyxEntry<{id: string; name: string}>) => ({ + id: entry?.id, + })) as UseOnyxSelector, }), ); @@ -553,14 +609,25 @@ describe('useOnyx', () => { it('should not change selected collection data if a property outside that data was changed', async () => { Onyx.mergeCollection(ONYXKEYS.COLLECTION.TEST_KEY, { - [`${ONYXKEYS.COLLECTION.TEST_KEY}entry1`]: {id: 'entry1_id', name: 'entry1_name'}, - [`${ONYXKEYS.COLLECTION.TEST_KEY}entry2`]: {id: 'entry2_id', name: 'entry2_name'}, - [`${ONYXKEYS.COLLECTION.TEST_KEY}entry3`]: {id: 'entry3_id', name: 'entry3_name'}, - } as GenericCollection); + [`${ONYXKEYS.COLLECTION.TEST_KEY}entry1`]: { + id: 'entry1_id', + name: 'entry1_name', + }, + [`${ONYXKEYS.COLLECTION.TEST_KEY}entry2`]: { + id: 'entry2_id', + name: 'entry2_name', + }, + [`${ONYXKEYS.COLLECTION.TEST_KEY}entry3`]: { + id: 'entry3_id', + name: 'entry3_name', + }, + }); const {result} = renderHook(() => useOnyx(ONYXKEYS.COLLECTION.TEST_KEY, { - selector: ((entry: OnyxEntry<{id: string; name: string}>) => ({id: entry?.id})) as UseOnyxSelector, + selector: ((entry: OnyxEntry<{id: string; name: string}>) => ({ + id: entry?.id, + })) as UseOnyxSelector, }), ); @@ -568,7 +635,11 @@ describe('useOnyx', () => { const oldResult = result.current; - await act(async () => Onyx.merge(`${ONYXKEYS.COLLECTION.TEST_KEY}entry2`, {name: 'entry2_changed'})); + await act(async () => + Onyx.merge(`${ONYXKEYS.COLLECTION.TEST_KEY}entry2`, { + name: 'entry2_changed', + }), + ); // must be the same reference expect(oldResult).toBe(result.current); @@ -611,7 +682,11 @@ describe('useOnyx', () => { }); it('should memoize selector output and return same reference when input unchanged', async () => { - Onyx.set(ONYXKEYS.TEST_KEY, {id: 'test_id', name: 'test_name', count: 1}); + Onyx.set(ONYXKEYS.TEST_KEY, { + id: 'test_id', + name: 'test_name', + count: 1, + }); const {result} = renderHook(() => useOnyx(ONYXKEYS.TEST_KEY, { @@ -654,7 +729,10 @@ describe('useOnyx', () => { // Should return a new reference since data changed expect(result.current[0]).not.toBe(firstResult); - expect(result.current[0]).toEqual({id: 'changed_id', name: 'test_name'}); + expect(result.current[0]).toEqual({ + id: 'changed_id', + name: 'test_name', + }); }); it('should memoize selector output using deep equality check', async () => { @@ -690,7 +768,7 @@ describe('useOnyx', () => { const {result} = renderHook(() => useOnyx(ONYXKEYS.TEST_KEY, { - selector: ((entry: OnyxEntry<{count: number; name: string}>) => entry?.count || 0) as UseOnyxSelector, + selector: ((entry: OnyxEntry<{count: number; name: string}>) => entry?.count ?? 0) as UseOnyxSelector, }), ); @@ -721,7 +799,7 @@ describe('useOnyx', () => { [`${ONYXKEYS.COLLECTION.TEST_KEY}3`]: {id: '3', value: 'item3'}, }; - await act(async () => Onyx.mergeCollection(ONYXKEYS.COLLECTION.TEST_KEY, testCollection as GenericCollection)); + await act(async () => Onyx.mergeCollection(ONYXKEYS.COLLECTION.TEST_KEY, testCollection)); let filterIds = ['1']; let selectorCallCount = 0; @@ -781,13 +859,29 @@ describe('useOnyx', () => { it('should handle complex dependency scenarios with multiple values', async () => { type TestItem = {id: string; category: string; priority: number}; const testData = { - [`${ONYXKEYS.COLLECTION.TEST_KEY}item1`]: {id: 'item1', category: 'A', priority: 1}, - [`${ONYXKEYS.COLLECTION.TEST_KEY}item2`]: {id: 'item2', category: 'B', priority: 2}, - [`${ONYXKEYS.COLLECTION.TEST_KEY}item3`]: {id: 'item3', category: 'A', priority: 3}, - [`${ONYXKEYS.COLLECTION.TEST_KEY}item4`]: {id: 'item4', category: 'B', priority: 4}, + [`${ONYXKEYS.COLLECTION.TEST_KEY}item1`]: { + id: 'item1', + category: 'A', + priority: 1, + }, + [`${ONYXKEYS.COLLECTION.TEST_KEY}item2`]: { + id: 'item2', + category: 'B', + priority: 2, + }, + [`${ONYXKEYS.COLLECTION.TEST_KEY}item3`]: { + id: 'item3', + category: 'A', + priority: 3, + }, + [`${ONYXKEYS.COLLECTION.TEST_KEY}item4`]: { + id: 'item4', + category: 'B', + priority: 4, + }, }; - await act(async () => Onyx.mergeCollection(ONYXKEYS.COLLECTION.TEST_KEY, testData as GenericCollection)); + await act(async () => Onyx.mergeCollection(ONYXKEYS.COLLECTION.TEST_KEY, testData)); let categoryFilter = 'A'; let sortAscending = true; @@ -972,10 +1066,19 @@ describe('useOnyx', () => { describe('dependencies', () => { it('should return the updated selected value when a external value passed to the dependencies list changes', async () => { Onyx.mergeCollection(ONYXKEYS.COLLECTION.TEST_KEY, { - [`${ONYXKEYS.COLLECTION.TEST_KEY}entry1`]: {id: 'entry1_id', name: 'entry1_name'}, - [`${ONYXKEYS.COLLECTION.TEST_KEY}entry2`]: {id: 'entry2_id', name: 'entry2_name'}, - [`${ONYXKEYS.COLLECTION.TEST_KEY}entry3`]: {id: 'entry3_id', name: 'entry3_name'}, - } as GenericCollection); + [`${ONYXKEYS.COLLECTION.TEST_KEY}entry1`]: { + id: 'entry1_id', + name: 'entry1_name', + }, + [`${ONYXKEYS.COLLECTION.TEST_KEY}entry2`]: { + id: 'entry2_id', + name: 'entry2_name', + }, + [`${ONYXKEYS.COLLECTION.TEST_KEY}entry3`]: { + id: 'entry3_id', + name: 'entry3_name', + }, + }); let externalValue = 'ex1'; @@ -1020,32 +1123,62 @@ describe('useOnyx', () => { describe('skippable collection member ids', () => { it('should always return undefined entry when subscribing to a collection with skippable member ids', async () => { Onyx.mergeCollection(ONYXKEYS.COLLECTION.TEST_KEY, { - [`${ONYXKEYS.COLLECTION.TEST_KEY}entry1`]: {id: 'entry1_id', name: 'entry1_name'}, - [`${ONYXKEYS.COLLECTION.TEST_KEY}entry2`]: {id: 'entry2_id', name: 'entry2_name'}, - [`${ONYXKEYS.COLLECTION.TEST_KEY}skippable-id`]: {id: 'skippable-id_id', name: 'skippable-id_name'}, - } as GenericCollection); + [`${ONYXKEYS.COLLECTION.TEST_KEY}entry1`]: { + id: 'entry1_id', + name: 'entry1_name', + }, + [`${ONYXKEYS.COLLECTION.TEST_KEY}entry2`]: { + id: 'entry2_id', + name: 'entry2_name', + }, + [`${ONYXKEYS.COLLECTION.TEST_KEY}skippable-id`]: { + id: 'skippable-id_id', + name: 'skippable-id_name', + }, + }); const {result} = renderHook(() => useOnyx(ONYXKEYS.COLLECTION.TEST_KEY)); await act(async () => waitForPromisesToResolve()); expect(result.current[0]).toEqual({ - [`${ONYXKEYS.COLLECTION.TEST_KEY}entry1`]: {id: 'entry1_id', name: 'entry1_name'}, - [`${ONYXKEYS.COLLECTION.TEST_KEY}entry2`]: {id: 'entry2_id', name: 'entry2_name'}, + [`${ONYXKEYS.COLLECTION.TEST_KEY}entry1`]: { + id: 'entry1_id', + name: 'entry1_name', + }, + [`${ONYXKEYS.COLLECTION.TEST_KEY}entry2`]: { + id: 'entry2_id', + name: 'entry2_name', + }, }); expect(result.current[1].status).toEqual('loaded'); await act(async () => Onyx.mergeCollection(ONYXKEYS.COLLECTION.TEST_KEY, { - [`${ONYXKEYS.COLLECTION.TEST_KEY}entry1`]: {id: 'entry1_id', name: 'entry1_name_changed'}, - [`${ONYXKEYS.COLLECTION.TEST_KEY}entry2`]: {id: 'entry2_id', name: 'entry2_name_changed'}, - [`${ONYXKEYS.COLLECTION.TEST_KEY}skippable-id`]: {id: 'skippable-id_id', name: 'skippable-id_name_changed'}, - } as GenericCollection), + [`${ONYXKEYS.COLLECTION.TEST_KEY}entry1`]: { + id: 'entry1_id', + name: 'entry1_name_changed', + }, + [`${ONYXKEYS.COLLECTION.TEST_KEY}entry2`]: { + id: 'entry2_id', + name: 'entry2_name_changed', + }, + [`${ONYXKEYS.COLLECTION.TEST_KEY}skippable-id`]: { + id: 'skippable-id_id', + name: 'skippable-id_name_changed', + }, + }), ); expect(result.current[0]).toEqual({ - [`${ONYXKEYS.COLLECTION.TEST_KEY}entry1`]: {id: 'entry1_id', name: 'entry1_name_changed'}, - [`${ONYXKEYS.COLLECTION.TEST_KEY}entry2`]: {id: 'entry2_id', name: 'entry2_name_changed'}, + [`${ONYXKEYS.COLLECTION.TEST_KEY}entry1`]: { + id: 'entry1_id', + name: 'entry1_name_changed', + }, + [`${ONYXKEYS.COLLECTION.TEST_KEY}entry2`]: { + id: 'entry2_id', + name: 'entry2_name_changed', + }, }); expect(result.current[1].status).toEqual('loaded'); }); @@ -1114,7 +1247,7 @@ describe('useOnyx', () => { Onyx.mergeCollection(ONYXKEYS.COLLECTION.RAM_ONLY_COLLECTION, { [`${ONYXKEYS.COLLECTION.RAM_ONLY_COLLECTION}entry1`]: {id: '1'}, [`${ONYXKEYS.COLLECTION.RAM_ONLY_COLLECTION}entry2`]: {id: '2'}, - } as GenericCollection), + }), ); const {result} = renderHook(() => useOnyx(ONYXKEYS.COLLECTION.RAM_ONLY_COLLECTION)); @@ -1136,7 +1269,11 @@ describe('useOnyx', () => { expect(result.current[0]).toBeUndefined(); expect(result.current[1].status).toEqual('loaded'); - await act(async () => Onyx.set(`${ONYXKEYS.COLLECTION.RAM_ONLY_COLLECTION}entry1`, {id: 'fresh'})); + await act(async () => + Onyx.set(`${ONYXKEYS.COLLECTION.RAM_ONLY_COLLECTION}entry1`, { + id: 'fresh', + }), + ); expect(result.current[0]).toEqual({id: 'fresh'}); expect(result.current[1].status).toEqual('loaded'); @@ -1261,7 +1398,7 @@ describe('useOnyx', () => { renders.push({value: r[0], status: r[1].status}); return r; }, - {initialProps: `${ONYXKEYS.COLLECTION.TEST_KEY}A` as string}, + {initialProps: `${ONYXKEYS.COLLECTION.TEST_KEY}A`}, ); await act(async () => waitForPromisesToResolve()); @@ -1285,7 +1422,7 @@ describe('useOnyx', () => { // Verify the reset took effect: a 'loading' frame must appear after the key change. const postSwitchStatuses = renders.slice(rendersAfterMount).map((r) => r.status); expect(postSwitchStatuses).toContain('loading'); - expect(postSwitchStatuses[postSwitchStatuses.length - 1]).toBe('loaded'); + expect(postSwitchStatuses.at(postSwitchStatuses.length - 1)).toBe('loaded'); }); it('should transition through loading and render exactly 3 times when switching between two cached keys', async () => { @@ -1299,7 +1436,7 @@ describe('useOnyx', () => { renders.push({value: r[0], status: r[1].status}); return r; }, - {initialProps: `${ONYXKEYS.COLLECTION.TEST_KEY}A` as string}, + {initialProps: `${ONYXKEYS.COLLECTION.TEST_KEY}A`}, ); await act(async () => waitForPromisesToResolve()); diff --git a/tests/unit/utilsTest.ts b/tests/unit/utilsTest.ts index 48e51d598..4a1a7a598 100644 --- a/tests/unit/utilsTest.ts +++ b/tests/unit/utilsTest.ts @@ -137,7 +137,7 @@ describe('utils', () => { }); it('should add the "ONYX_INTERNALS__REPLACE_OBJECT_MARK" flag to the merged object when the change is set to null and "objectRemovalMode" is set to "mark"', () => { - const result = utils.fastMerge(testMergeChanges[1], testMergeChanges[0], { + const result = utils.fastMerge(testMergeChanges.at(1), testMergeChanges.at(0), { shouldRemoveNestedNulls: true, objectRemovalMode: 'mark', }); @@ -146,7 +146,7 @@ describe('utils', () => { b: { d: { h: 'h', - [utils.ONYX_INTERNALS__REPLACE_OBJECT_MARK]: true, + [utils.ONYX_INTERNALS_REPLACE_OBJECT_MARK]: true, }, h: 'h', }, @@ -161,7 +161,7 @@ describe('utils', () => { b: { d: { h: 'h', - [utils.ONYX_INTERNALS__REPLACE_OBJECT_MARK]: true, + [utils.ONYX_INTERNALS_REPLACE_OBJECT_MARK]: true, }, h: 'h', }, diff --git a/tests/utils/alternateLists.ts b/tests/utils/alternateLists.ts index 061c418e8..64fdaa8a0 100644 --- a/tests/utils/alternateLists.ts +++ b/tests/utils/alternateLists.ts @@ -1,4 +1,6 @@ const alternateLists = (list1: unknown[], list2: unknown[]) => - list1.length > list2.length ? list1.flatMap((item, i) => [item, list2[i]]).filter((x) => x !== undefined) : list2.flatMap((item, i) => [list1[i], item]).filter((x) => x !== undefined); + list1.length > list2.length + ? list1.flatMap((item, i) => [item, list2.at(i)]).filter((x) => x !== undefined) + : list2.flatMap((item, i) => [list1.at(i), item]).filter((x) => x !== undefined); export default alternateLists; diff --git a/tests/utils/collections/reportActions.ts b/tests/utils/collections/reportActions.ts index 240fa654a..db1719cd2 100644 --- a/tests/utils/collections/reportActions.ts +++ b/tests/utils/collections/reportActions.ts @@ -14,7 +14,7 @@ const getRandomDate = (): string => { const getRandomReportActions = (collection: string, length = 10000) => createCollection>( - (item) => `${collection}${item.reportActionID}`, + (item) => `${collection}${String(item.reportActionID)}`, (index) => createRandomReportAction(index), length, ); diff --git a/tests/utils/getAtIndex.ts b/tests/utils/getAtIndex.ts new file mode 100644 index 000000000..e27069613 --- /dev/null +++ b/tests/utils/getAtIndex.ts @@ -0,0 +1,15 @@ +/** + * Returns the value at the given index, throwing if it is missing. + * Prefer this over non-null assertions when satisfying rulesdir/prefer-at. + */ +function getAtIndex(array: T[], index: number): T { + const value = array.at(index); + + if (value === undefined) { + throw new Error(`Expected value at index ${index}`); + } + + return value; +} + +export default getAtIndex; diff --git a/tests/utils/onyxConnectionManagerTestHarness.ts b/tests/utils/onyxConnectionManagerTestHarness.ts new file mode 100644 index 000000000..02add2447 --- /dev/null +++ b/tests/utils/onyxConnectionManagerTestHarness.ts @@ -0,0 +1,18 @@ +import connectionManager from '../../lib/OnyxConnectionManager'; +import type {ConnectOptions} from '../../lib/Onyx'; +import type {OnyxKey} from '../../lib/types'; + +export type ConnectionMetadataForTest = { + subscriptionID: number; +}; + +type OnyxConnectionManagerTestHarness = { + connectionsMap: Map; + generateConnectionID: (connectOptions: ConnectOptions) => string; + sessionID: string; + fireCallbacks: (connectionID: string) => void; +}; + +export function getOnyxConnectionManagerTestHarness(): OnyxConnectionManagerTestHarness { + return connectionManager as unknown as OnyxConnectionManagerTestHarness; +}