From 5e7cfb11c8ba7a82ca7218924ce1e93dd5cc7768 Mon Sep 17 00:00:00 2001 From: Michael Francis Date: Fri, 30 Jan 2026 14:26:12 -0500 Subject: [PATCH] fix(adapters): make shallow equality handle Temporal value objects MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit The adapters’ default `shallow` equality treated any two “keyless” objects as equal (no enumerable keys => no comparisons), which caused Temporal objects to be considered unchanged and skip UI updates. Adjust `shallow` so keyless values are only equal for plain objects/arrays, and use `.equals()` when available (e.g. Temporal) to preserve value semantics. Add Temporal regression coverage across React/Preact/Solid/Vue/Svelte, plus an Angular integration test to ensure Temporal updates trigger re-render. --- .changeset/olive-pens-draw.md | 10 ++ package.json | 1 + packages/angular-store/src/index.ts | 41 ++++++++ packages/angular-store/tests/index.test.ts | 49 +++++++++ packages/preact-store/src/index.ts | 41 ++++++++ packages/preact-store/tests/index.test.tsx | 11 ++ packages/react-store/src/index.ts | 38 +++++++ packages/react-store/tests/index.test.tsx | 11 ++ packages/solid-store/src/index.tsx | 41 ++++++++ packages/solid-store/tests/index.test.tsx | 17 +++- packages/svelte-store/src/index.svelte.ts | 47 ++++++++- packages/svelte-store/tests/index.test.ts | 11 ++ packages/vue-store/src/index.ts | 41 ++++++++ packages/vue-store/tests/index.test.tsx | 11 ++ pnpm-lock.yaml | 111 ++++++++++++++++++++- 15 files changed, 472 insertions(+), 9 deletions(-) create mode 100644 .changeset/olive-pens-draw.md diff --git a/.changeset/olive-pens-draw.md b/.changeset/olive-pens-draw.md new file mode 100644 index 00000000..12b126f7 --- /dev/null +++ b/.changeset/olive-pens-draw.md @@ -0,0 +1,10 @@ +--- +'@tanstack/angular-store': patch +'@tanstack/preact-store': patch +'@tanstack/svelte-store': patch +'@tanstack/react-store': patch +'@tanstack/solid-store': patch +'@tanstack/vue-store': patch +--- + +Fix adapter `shallow` equality for keyless value objects (e.g. Temporal) so updates aren’t skipped. diff --git a/package.json b/package.json index 5af6e0cf..ccf6f8b1 100644 --- a/package.json +++ b/package.json @@ -60,6 +60,7 @@ "prettier-plugin-svelte": "^3.4.0", "publint": "^0.3.15", "sherif": "^1.9.0", + "temporal-polyfill": "^0.3.0", "tinyglobby": "^0.2.15", "typescript": "5.6.3", "typescript50": "npm:typescript@5.0", diff --git a/packages/angular-store/src/index.ts b/packages/angular-store/src/index.ts index 130120a4..0e2930e4 100644 --- a/packages/angular-store/src/index.ts +++ b/packages/angular-store/src/index.ts @@ -95,6 +95,30 @@ function shallow(objA: T, objB: T) { return false } + // Many "value objects" (e.g. Temporal) have no enumerable keys, which would + // otherwise make any two instances appear "shallow equal". Only treat + // keyless values as equal when both are plain objects or both are arrays. + if (keysA.length === 0) { + const aIsPlain = isPlainObject(objA) + const bIsPlain = isPlainObject(objB) + const aIsArray = Array.isArray(objA) + const bIsArray = Array.isArray(objB) + + if ((aIsPlain && bIsPlain) || (aIsArray && bIsArray)) { + return true + } + + if (hasEquals(objA) && hasEquals(objB)) { + try { + return objA.equals(objB) + } catch { + return false + } + } + + return false + } + for (let i = 0; i < keysA.length; i++) { if ( !Object.prototype.hasOwnProperty.call(objB, keysA[i] as string) || @@ -105,3 +129,20 @@ function shallow(objA: T, objB: T) { } return true } + +function isPlainObject(value: unknown): value is object { + if (typeof value !== 'object' || value === null) return false + const proto = Object.getPrototypeOf(value) + return proto === Object.prototype || proto === null +} + +function hasEquals( + value: TValue, +): value is TValue & { equals: (other: unknown) => boolean } { + return ( + typeof value === 'object' && + value !== null && + 'equals' in (value as object) && + typeof (value as any).equals === 'function' + ) +} diff --git a/packages/angular-store/tests/index.test.ts b/packages/angular-store/tests/index.test.ts index 17c5e465..0f290e87 100644 --- a/packages/angular-store/tests/index.test.ts +++ b/packages/angular-store/tests/index.test.ts @@ -3,6 +3,7 @@ import { Component, effect } from '@angular/core' import { TestBed } from '@angular/core/testing' import { By } from '@angular/platform-browser' import { Store } from '@tanstack/store' +import { Temporal } from 'temporal-polyfill' import { injectStore } from '../src/index' describe('injectStore', () => { @@ -142,4 +143,52 @@ describe('dataType', () => { debugElement.query(By.css('p#displayStoreVal')).nativeElement.textContent, ).toContain(new Date('2025-03-29T21:06:40.401Z')) }) + + test('temporal change trigger re-render', () => { + const store = new Store({ date: Temporal.PlainDate.from('2025-03-29') }) + + @Component({ + template: ` +
+

{{ storeVal().toString() }}

+ +
+ `, + standalone: true, + }) + class MyCmp { + storeVal = injectStore(store, (state) => state.date) + + constructor() { + effect(() => { + console.log(this.storeVal()) + }) + } + + updateDate() { + store.setState((v) => ({ + ...v, + date: Temporal.PlainDate.from('2025-03-30'), + })) + } + } + + const fixture = TestBed.createComponent(MyCmp) + fixture.detectChanges() + + const debugElement = fixture.debugElement + + expect( + debugElement.query(By.css('p#displayStoreVal')).nativeElement.textContent, + ).toContain('2025-03-29') + + debugElement + .query(By.css('button#updateDate')) + .triggerEventHandler('click', null) + + fixture.detectChanges() + expect( + debugElement.query(By.css('p#displayStoreVal')).nativeElement.textContent, + ).toContain('2025-03-30') + }) }) diff --git a/packages/preact-store/src/index.ts b/packages/preact-store/src/index.ts index 7b2c5bcc..2789b75c 100644 --- a/packages/preact-store/src/index.ts +++ b/packages/preact-store/src/index.ts @@ -167,6 +167,30 @@ export function shallow(objA: T, objB: T) { return false } + // Many "value objects" (e.g. Temporal) have no enumerable keys, which would + // otherwise make any two instances appear "shallow equal". Only treat + // keyless values as equal when both are plain objects or both are arrays. + if (keysA.length === 0) { + const aIsPlain = isPlainObject(objA) + const bIsPlain = isPlainObject(objB) + const aIsArray = Array.isArray(objA) + const bIsArray = Array.isArray(objB) + + if ((aIsPlain && bIsPlain) || (aIsArray && bIsArray)) { + return true + } + + if (hasEquals(objA) && hasEquals(objB)) { + try { + return objA.equals(objB) + } catch { + return false + } + } + + return false + } + for (const key of keysA) { if ( !Object.prototype.hasOwnProperty.call(objB, key as string) || @@ -178,6 +202,23 @@ export function shallow(objA: T, objB: T) { return true } +function isPlainObject(value: unknown): value is object { + if (typeof value !== 'object' || value === null) return false + const proto = Object.getPrototypeOf(value) + return proto === Object.prototype || proto === null +} + +function hasEquals( + value: TValue, +): value is TValue & { equals: (other: unknown) => boolean } { + return ( + typeof value === 'object' && + value !== null && + 'equals' in (value as object) && + typeof (value as any).equals === 'function' + ) +} + function getOwnKeys(obj: object): Array { return (Object.keys(obj) as Array).concat( Object.getOwnPropertySymbols(obj), diff --git a/packages/preact-store/tests/index.test.tsx b/packages/preact-store/tests/index.test.tsx index 06315d51..843721d8 100644 --- a/packages/preact-store/tests/index.test.tsx +++ b/packages/preact-store/tests/index.test.tsx @@ -3,6 +3,7 @@ import { render, waitFor } from '@testing-library/preact' import { Derived, Store } from '@tanstack/store' import { useState } from 'preact/hooks' import { userEvent } from '@testing-library/user-event' +import { Temporal } from 'temporal-polyfill' import { shallow, useStore } from '../src/index' const user = userEvent.setup() @@ -303,4 +304,14 @@ describe('shallow', () => { const objB = new Date('2025-02-10') expect(shallow(objA, objB)).toBe(true) }) + + test('should return false for empty object vs empty array', () => { + expect(shallow({}, [])).toBe(false) + }) + + test('should return false for temporal objects with different values', () => { + const objA = Temporal.PlainDate.from('2025-02-10') + const objB = Temporal.PlainDate.from('2025-02-11') + expect(shallow(objA, objB)).toBe(false) + }) }) diff --git a/packages/react-store/src/index.ts b/packages/react-store/src/index.ts index 21d3a478..c9aca182 100644 --- a/packages/react-store/src/index.ts +++ b/packages/react-store/src/index.ts @@ -79,6 +79,27 @@ export function shallow(objA: T, objB: T) { return false } + if (keysA.length === 0) { + const aIsPlain = isPlainObject(objA) + const bIsPlain = isPlainObject(objB) + const aIsArray = Array.isArray(objA) + const bIsArray = Array.isArray(objB) + + if ((aIsPlain && bIsPlain) || (aIsArray && bIsArray)) { + return true + } + + if (hasEquals(objA) && hasEquals(objB)) { + try { + return objA.equals(objB) + } catch { + return false + } + } + + return false + } + for (let i = 0; i < keysA.length; i++) { if ( !Object.prototype.hasOwnProperty.call(objB, keysA[i] as string) || @@ -90,6 +111,23 @@ export function shallow(objA: T, objB: T) { return true } +function isPlainObject(value: unknown): value is object { + if (typeof value !== 'object' || value === null) return false + const proto = Object.getPrototypeOf(value) + return proto === Object.prototype || proto === null +} + +function hasEquals( + value: TValue, +): value is TValue & { equals: (other: unknown) => boolean } { + return ( + typeof value === 'object' && + value !== null && + 'equals' in (value as object) && + typeof (value as any).equals === 'function' + ) +} + function getOwnKeys(obj: object): Array { return (Object.keys(obj) as Array).concat( Object.getOwnPropertySymbols(obj), diff --git a/packages/react-store/tests/index.test.tsx b/packages/react-store/tests/index.test.tsx index 37d1d37c..073b918c 100644 --- a/packages/react-store/tests/index.test.tsx +++ b/packages/react-store/tests/index.test.tsx @@ -3,6 +3,7 @@ import { render, waitFor } from '@testing-library/react' import { Derived, Store } from '@tanstack/store' import { useState } from 'react' import { userEvent } from '@testing-library/user-event' +import { Temporal } from 'temporal-polyfill' import { shallow, useStore } from '../src/index' const user = userEvent.setup() @@ -302,4 +303,14 @@ describe('shallow', () => { const objB = new Date('2025-02-10') expect(shallow(objA, objB)).toBe(true) }) + + test('should return false for empty object vs empty array', () => { + expect(shallow({}, [])).toBe(false) + }) + + test('should return false for temporal objects with different values', () => { + const objA = Temporal.PlainDate.from('2025-02-10') + const objB = Temporal.PlainDate.from('2025-02-11') + expect(shallow(objA, objB)).toBe(false) + }) }) diff --git a/packages/solid-store/src/index.tsx b/packages/solid-store/src/index.tsx index 104653b8..c69f08d0 100644 --- a/packages/solid-store/src/index.tsx +++ b/packages/solid-store/src/index.tsx @@ -86,6 +86,30 @@ export function shallow(objA: T, objB: T) { return false } + // Many "value objects" (e.g. Temporal) have no enumerable keys, which would + // otherwise make any two instances appear "shallow equal". Only treat + // keyless values as equal when both are plain objects or both are arrays. + if (keysA.length === 0) { + const aIsPlain = isPlainObject(objA) + const bIsPlain = isPlainObject(objB) + const aIsArray = Array.isArray(objA) + const bIsArray = Array.isArray(objB) + + if ((aIsPlain && bIsPlain) || (aIsArray && bIsArray)) { + return true + } + + if (hasEquals(objA) && hasEquals(objB)) { + try { + return objA.equals(objB) + } catch { + return false + } + } + + return false + } + for (let i = 0; i < keysA.length; i++) { if ( !Object.prototype.hasOwnProperty.call(objB, keysA[i] as string) || @@ -96,3 +120,20 @@ export function shallow(objA: T, objB: T) { } return true } + +function isPlainObject(value: unknown): value is object { + if (typeof value !== 'object' || value === null) return false + const proto = Object.getPrototypeOf(value) + return proto === Object.prototype || proto === null +} + +function hasEquals( + value: TValue, +): value is TValue & { equals: (other: unknown) => boolean } { + return ( + typeof value === 'object' && + value !== null && + 'equals' in (value as object) && + typeof (value as any).equals === 'function' + ) +} diff --git a/packages/solid-store/tests/index.test.tsx b/packages/solid-store/tests/index.test.tsx index 2efa725c..e82a3a40 100644 --- a/packages/solid-store/tests/index.test.tsx +++ b/packages/solid-store/tests/index.test.tsx @@ -1,7 +1,8 @@ -import { describe, expect, it } from 'vitest' +import { describe, expect, it, test } from 'vitest' import { render, renderHook } from '@solidjs/testing-library' import { Store } from '@tanstack/store' -import { useStore } from '../src/index' +import { Temporal } from 'temporal-polyfill' +import { shallow, useStore } from '../src/index' describe('useStore', () => { it.todo('allows us to select state using a selector', () => { @@ -53,3 +54,15 @@ describe('useStore', () => { expect(result()).toStrictEqual(new Date('2025-03-29T21:06:40.401Z')) }) }) + +describe('shallow', () => { + test('should return false for empty object vs empty array', () => { + expect(shallow({}, [])).toBe(false) + }) + + test('should return false for temporal objects with different values', () => { + const objA = Temporal.PlainDate.from('2025-02-10') + const objB = Temporal.PlainDate.from('2025-02-11') + expect(shallow(objA, objB)).toBe(false) + }) +}) diff --git a/packages/svelte-store/src/index.svelte.ts b/packages/svelte-store/src/index.svelte.ts index 636dd433..e6f4f940 100644 --- a/packages/svelte-store/src/index.svelte.ts +++ b/packages/svelte-store/src/index.svelte.ts @@ -88,13 +88,54 @@ export function shallow(objA: T, objB: T) { return false } - for (let i = 0; i < keysA.length; i++) { + // Many "value objects" (e.g. Temporal) have no enumerable keys, which would + // otherwise make any two instances appear "shallow equal". Only treat + // keyless values as equal when both are plain objects or both are arrays. + if (keysA.length === 0) { + const aIsPlain = isPlainObject(objA) + const bIsPlain = isPlainObject(objB) + const aIsArray = Array.isArray(objA) + const bIsArray = Array.isArray(objB) + + if ((aIsPlain && bIsPlain) || (aIsArray && bIsArray)) { + return true + } + + if (hasEquals(objA) && hasEquals(objB)) { + try { + return objA.equals(objB) + } catch { + return false + } + } + + return false + } + + for (const key of keysA) { if ( - !Object.prototype.hasOwnProperty.call(objB, keysA[i] as string) || - !Object.is(objA[keysA[i] as keyof T], objB[keysA[i] as keyof T]) + !Object.prototype.hasOwnProperty.call(objB, key) || + !Object.is(objA[key as keyof T], objB[key as keyof T]) ) { return false } } return true } + +function isPlainObject(value: unknown): value is object { + if (typeof value !== 'object' || value === null) return false + const proto = Object.getPrototypeOf(value) + return proto === Object.prototype || proto === null +} + +function hasEquals( + value: TValue, +): value is TValue & { equals: (other: unknown) => boolean } { + return ( + typeof value === 'object' && + value !== null && + 'equals' in (value as object) && + typeof (value as any).equals === 'function' + ) +} diff --git a/packages/svelte-store/tests/index.test.ts b/packages/svelte-store/tests/index.test.ts index 24f7a3dd..d160c999 100644 --- a/packages/svelte-store/tests/index.test.ts +++ b/packages/svelte-store/tests/index.test.ts @@ -1,6 +1,7 @@ import { describe, expect, it, test } from 'vitest' import { render, waitFor } from '@testing-library/svelte' import { userEvent } from '@testing-library/user-event' +import { Temporal } from 'temporal-polyfill' import { shallow } from '../src/index.svelte.js' import TestBaseStore from './BaseStore.test.svelte' import TestRerender from './Render.test.svelte' @@ -91,4 +92,14 @@ describe('shallow', () => { const objB = new Date('2025-02-10') expect(shallow(objA, objB)).toBe(true) }) + + test('should return false for empty object vs empty array', () => { + expect(shallow({}, [])).toBe(false) + }) + + test('should return false for temporal objects with different values', () => { + const objA = Temporal.PlainDate.from('2025-02-10') + const objB = Temporal.PlainDate.from('2025-02-11') + expect(shallow(objA, objB)).toBe(false) + }) }) diff --git a/packages/vue-store/src/index.ts b/packages/vue-store/src/index.ts index 6b2ebd0b..1ed45cdf 100644 --- a/packages/vue-store/src/index.ts +++ b/packages/vue-store/src/index.ts @@ -92,6 +92,30 @@ export function shallow(objA: T, objB: T) { return false } + // Many "value objects" (e.g. Temporal) have no enumerable keys, which would + // otherwise make any two instances appear "shallow equal". Only treat + // keyless values as equal when both are plain objects or both are arrays. + if (keysA.length === 0) { + const aIsPlain = isPlainObject(objA) + const bIsPlain = isPlainObject(objB) + const aIsArray = Array.isArray(objA) + const bIsArray = Array.isArray(objB) + + if ((aIsPlain && bIsPlain) || (aIsArray && bIsArray)) { + return true + } + + if (hasEquals(objA) && hasEquals(objB)) { + try { + return objA.equals(objB) + } catch { + return false + } + } + + return false + } + for (let i = 0; i < keysA.length; i++) { if ( !Object.prototype.hasOwnProperty.call(objB, keysA[i] as string) || @@ -102,3 +126,20 @@ export function shallow(objA: T, objB: T) { } return true } + +function isPlainObject(value: unknown): value is object { + if (typeof value !== 'object' || value === null) return false + const proto = Object.getPrototypeOf(value) + return proto === Object.prototype || proto === null +} + +function hasEquals( + value: TValue, +): value is TValue & { equals: (other: unknown) => boolean } { + return ( + typeof value === 'object' && + value !== null && + 'equals' in (value as object) && + typeof (value as any).equals === 'function' + ) +} diff --git a/packages/vue-store/tests/index.test.tsx b/packages/vue-store/tests/index.test.tsx index ebdb3f98..52c7ec17 100644 --- a/packages/vue-store/tests/index.test.tsx +++ b/packages/vue-store/tests/index.test.tsx @@ -4,6 +4,7 @@ import { defineComponent, h } from 'vue-demi' import { render, waitFor } from '@testing-library/vue' import { Store } from '@tanstack/store' import { userEvent } from '@testing-library/user-event' +import { Temporal } from 'temporal-polyfill' import { shallow, useStore } from '../src/index' const user = userEvent.setup() @@ -168,4 +169,14 @@ describe('shallow', () => { const objB = new Date('2025-02-10') expect(shallow(objA, objB)).toBe(true) }) + + test('should return false for empty object vs empty array', () => { + expect(shallow({}, [])).toBe(false) + }) + + test('should return false for temporal objects with different values', () => { + const objA = Temporal.PlainDate.from('2025-02-10') + const objB = Temporal.PlainDate.from('2025-02-11') + expect(shallow(objA, objB)).toBe(false) + }) }) diff --git a/pnpm-lock.yaml b/pnpm-lock.yaml index 9fe910cf..acc6288e 100644 --- a/pnpm-lock.yaml +++ b/pnpm-lock.yaml @@ -71,6 +71,9 @@ importers: sherif: specifier: ^1.9.0 version: 1.9.0 + temporal-polyfill: + specifier: ^0.3.0 + version: 0.3.0 tinyglobby: specifier: ^0.2.15 version: 0.2.15 @@ -7187,6 +7190,13 @@ packages: tar@7.5.2: resolution: {integrity: sha512-7NyxrTE4Anh8km8iEy7o0QYPs+0JKBTj5ZaqHg6B39erLg0qYXN3BijtShwbsNSvQ+LN75+KV+C4QR/f6Gwnpg==} engines: {node: '>=18'} + deprecated: Old versions of tar are not supported, and contain widely publicized security vulnerabilities, which have been fixed in the current version. Please update. Support for old versions may be purchased (at exhorbitant rates) by contacting i@izs.me + + temporal-polyfill@0.3.0: + resolution: {integrity: sha512-qNsTkX9K8hi+FHDfHmf22e/OGuXmfBm9RqNismxBrnSmZVJKegQ+HYYXT+R7Ha8F/YSm2Y34vmzD4cxMu2u95g==} + + temporal-spec@0.3.0: + resolution: {integrity: sha512-n+noVpIqz4hYgFSMOSiINNOUOMFtV5cZQNCmmszA6GiVFVRt3G7AqVyhXjhCSmowvQn+NsGn+jMDMKJYHd3bSQ==} term-size@2.2.1: resolution: {integrity: sha512-wK0Ri4fOGjv/XPy8SBHZChl8CM7uMc5VML7SqiQ0zG7+J5Vr+RMQDoHa2CNT6KHUnTGIXH34UDMkPzAUyapBZg==} @@ -8000,9 +8010,9 @@ snapshots: tree-kill: 1.2.2 tslib: 2.8.1 typescript: 5.6.3 - webpack: 5.98.0(esbuild@0.25.4) + webpack: 5.98.0 webpack-dev-middleware: 7.4.2(webpack@5.98.0(esbuild@0.25.4)) - webpack-dev-server: 5.2.2(webpack@5.98.0(esbuild@0.25.4)) + webpack-dev-server: 5.2.2(webpack@5.98.0) webpack-merge: 6.0.1 webpack-subresource-integrity: 5.1.0(webpack@5.98.0(esbuild@0.25.4)) optionalDependencies: @@ -8164,7 +8174,7 @@ snapshots: '@babel/helper-split-export-declaration': 7.24.7 '@babel/plugin-syntax-import-attributes': 7.26.0(@babel/core@7.26.10) '@inquirer/confirm': 5.1.6(@types/node@24.9.2) - '@vitejs/plugin-basic-ssl': 1.2.0(vite@6.4.1(@types/node@24.9.2)(jiti@2.6.1)(less@4.2.2)(sass@1.85.0)(terser@5.39.0)(tsx@4.21.0)(yaml@2.8.1)) + '@vitejs/plugin-basic-ssl': 1.2.0(vite@6.4.1(@types/node@24.9.2)(jiti@2.6.1)(less@4.4.2)(sass@1.93.3)(terser@5.39.0)(tsx@4.21.0)(yaml@2.8.1)) beasties: 0.3.2 browserslist: 4.27.0 esbuild: 0.25.4 @@ -11176,7 +11186,6 @@ snapshots: '@vitejs/plugin-basic-ssl@1.2.0(vite@6.4.1(@types/node@24.9.2)(jiti@2.6.1)(less@4.4.2)(sass@1.93.3)(terser@5.39.0)(tsx@4.21.0)(yaml@2.8.1))': dependencies: vite: 6.4.1(@types/node@24.9.2)(jiti@2.6.1)(less@4.4.2)(sass@1.93.3)(terser@5.39.0)(tsx@4.21.0)(yaml@2.8.1) - optional: true '@vitejs/plugin-react@4.7.0(vite@6.4.1(@types/node@24.9.2)(jiti@2.6.1)(less@4.4.2)(sass@1.93.3)(terser@5.39.0)(tsx@4.21.0)(yaml@2.8.1))': dependencies: @@ -15685,6 +15694,12 @@ snapshots: minizlib: 3.1.0 yallist: 5.0.0 + temporal-polyfill@0.3.0: + dependencies: + temporal-spec: 0.3.0 + + temporal-spec@0.3.0: {} + term-size@2.2.1: {} terser-webpack-plugin@5.3.14(esbuild@0.25.4)(webpack@5.98.0(esbuild@0.25.4)): @@ -15698,6 +15713,15 @@ snapshots: optionalDependencies: esbuild: 0.25.4 + terser-webpack-plugin@5.3.14(webpack@5.98.0(esbuild@0.25.4)): + dependencies: + '@jridgewell/trace-mapping': 0.3.31 + jest-worker: 27.5.1 + schema-utils: 4.3.3 + serialize-javascript: 6.0.2 + terser: 5.39.0 + webpack: 5.98.0 + terser@5.39.0: dependencies: '@jridgewell/source-map': 0.3.11 @@ -16273,6 +16297,17 @@ snapshots: optionalDependencies: webpack: 5.98.0(esbuild@0.25.4) + webpack-dev-middleware@7.4.2(webpack@5.98.0): + dependencies: + colorette: 2.0.20 + memfs: 4.50.0 + mime-types: 2.1.35 + on-finished: 2.4.1 + range-parser: 1.2.1 + schema-utils: 4.3.3 + optionalDependencies: + webpack: 5.98.0 + webpack-dev-server@5.2.2(webpack@5.98.0(esbuild@0.25.4)): dependencies: '@types/bonjour': 3.5.13 @@ -16311,6 +16346,44 @@ snapshots: - supports-color - utf-8-validate + webpack-dev-server@5.2.2(webpack@5.98.0): + dependencies: + '@types/bonjour': 3.5.13 + '@types/connect-history-api-fallback': 1.5.4 + '@types/express': 4.17.25 + '@types/express-serve-static-core': 4.19.7 + '@types/serve-index': 1.9.4 + '@types/serve-static': 1.15.10 + '@types/sockjs': 0.3.36 + '@types/ws': 8.18.1 + ansi-html-community: 0.0.8 + bonjour-service: 1.3.0 + chokidar: 3.6.0 + colorette: 2.0.20 + compression: 1.8.1 + connect-history-api-fallback: 2.0.0 + express: 4.21.2 + graceful-fs: 4.2.11 + http-proxy-middleware: 2.0.9(@types/express@4.17.25) + ipaddr.js: 2.2.0 + launch-editor: 2.12.0 + open: 10.1.0 + p-retry: 6.2.1 + schema-utils: 4.3.3 + selfsigned: 2.4.1 + serve-index: 1.9.1 + sockjs: 0.3.24 + spdy: 4.0.2 + webpack-dev-middleware: 7.4.2(webpack@5.98.0) + ws: 8.18.3 + optionalDependencies: + webpack: 5.98.0 + transitivePeerDependencies: + - bufferutil + - debug + - supports-color + - utf-8-validate + webpack-merge@6.0.1: dependencies: clone-deep: 4.0.1 @@ -16327,6 +16400,36 @@ snapshots: webpack-virtual-modules@0.6.2: optional: true + webpack@5.98.0: + dependencies: + '@types/eslint-scope': 3.7.7 + '@types/estree': 1.0.8 + '@webassemblyjs/ast': 1.14.1 + '@webassemblyjs/wasm-edit': 1.14.1 + '@webassemblyjs/wasm-parser': 1.14.1 + acorn: 8.15.0 + browserslist: 4.27.0 + chrome-trace-event: 1.0.4 + enhanced-resolve: 5.18.3 + es-module-lexer: 1.7.0 + eslint-scope: 5.1.1 + events: 3.3.0 + glob-to-regexp: 0.4.1 + graceful-fs: 4.2.11 + json-parse-even-better-errors: 2.3.1 + loader-runner: 4.3.1 + mime-types: 2.1.35 + neo-async: 2.6.2 + schema-utils: 4.3.3 + tapable: 2.3.0 + terser-webpack-plugin: 5.3.14(webpack@5.98.0(esbuild@0.25.4)) + watchpack: 2.4.4 + webpack-sources: 3.3.3 + transitivePeerDependencies: + - '@swc/core' + - esbuild + - uglify-js + webpack@5.98.0(esbuild@0.25.4): dependencies: '@types/eslint-scope': 3.7.7