@humanspeak/svelte-virtual-list 0.2.6-beta.6 → 0.2.6-beta.7
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/dist/SvelteVirtualList.svelte +461 -130
- package/dist/SvelteVirtualList.svelte.d.ts +2 -2
- package/dist/reactive-height-manager/INTEGRATION_EXAMPLE.md +136 -0
- package/dist/reactive-height-manager/README.md +324 -0
- package/dist/reactive-height-manager/ReactiveHeightManager.svelte.d.ts +116 -0
- package/dist/reactive-height-manager/ReactiveHeightManager.svelte.js +200 -0
- package/dist/reactive-height-manager/benchmark.d.ts +5 -0
- package/dist/reactive-height-manager/benchmark.js +25 -0
- package/dist/reactive-height-manager/index.d.ts +50 -0
- package/dist/reactive-height-manager/index.js +55 -0
- package/dist/reactive-height-manager/types.d.ts +41 -0
- package/dist/reactive-height-manager/types.js +1 -0
- package/dist/utils/heightCalculation.d.ts +8 -1
- package/dist/utils/heightCalculation.js +5 -4
- package/dist/utils/heightChangeDetection.d.ts +12 -0
- package/dist/utils/heightChangeDetection.js +20 -0
- package/dist/utils/resizeObserver.d.ts +0 -33
- package/dist/utils/resizeObserver.js +0 -57
- package/dist/utils/scrollCalculation.d.ts +2 -2
- package/dist/utils/scrollCalculation.js +34 -21
- package/dist/utils/throttle.d.ts +95 -0
- package/dist/utils/throttle.js +155 -0
- package/dist/utils/virtualList.d.ts +11 -14
- package/dist/utils/virtualList.js +100 -53
- package/dist/utils/virtualListDebug.d.ts +1 -1
- package/dist/utils/virtualListDebug.js +1 -2
- package/package.json +21 -20
|
@@ -31,31 +31,63 @@ export const calculateScrollPosition = (totalItems, itemHeight, containerHeight)
|
|
|
31
31
|
* @param {SvelteVirtualListMode} mode - Scroll direction mode
|
|
32
32
|
* @returns {SvelteVirtualListPreviousVisibleRange} Range of indices to render
|
|
33
33
|
*/
|
|
34
|
-
export const calculateVisibleRange = (scrollTop, viewportHeight, itemHeight, totalItems, bufferSize, mode) => {
|
|
34
|
+
export const calculateVisibleRange = (scrollTop, viewportHeight, itemHeight, totalItems, bufferSize, mode, atBottom, wasAtBottomBeforeHeightChange, lastVisibleRange, totalContentHeight) => {
|
|
35
35
|
if (mode === 'bottomToTop') {
|
|
36
|
+
// if (wasAtBottomBeforeHeightChange && lastVisibleRange) {
|
|
37
|
+
// // console.log('calculateVisibleRange:wasAtBottomBeforeHeightChange', {
|
|
38
|
+
// // lastVisibleRange,
|
|
39
|
+
// // atBottom,
|
|
40
|
+
// // wasAtBottomBeforeHeightChange
|
|
41
|
+
// // })
|
|
42
|
+
// return lastVisibleRange
|
|
43
|
+
// }
|
|
36
44
|
const visibleCount = Math.ceil(viewportHeight / itemHeight) + 1;
|
|
37
|
-
|
|
38
|
-
//
|
|
39
|
-
//
|
|
40
|
-
|
|
41
|
-
|
|
42
|
-
|
|
43
|
-
|
|
44
|
-
|
|
45
|
-
|
|
46
|
-
|
|
45
|
+
// In bottomToTop mode, scrollTop represents distance from the total content end
|
|
46
|
+
// scrollTop = 0 means we're at the beginning (showing first items)
|
|
47
|
+
// scrollTop = maxScrollTop means we're at the end (showing last items)
|
|
48
|
+
const totalHeight = totalContentHeight ?? totalItems * itemHeight;
|
|
49
|
+
const maxScrollTop = Math.max(0, totalHeight - viewportHeight);
|
|
50
|
+
// Convert scrollTop to "distance from start" for bottomToTop
|
|
51
|
+
const distanceFromStart = maxScrollTop - scrollTop;
|
|
52
|
+
const startIndex = Math.floor(distanceFromStart / itemHeight);
|
|
53
|
+
// console.log(
|
|
54
|
+
// `[DEBUG] calculateVisibleRange bottomToTop: scrollTop=${scrollTop}, maxScrollTop=${maxScrollTop}, distanceFromStart=${distanceFromStart}, startIndex=${startIndex}`
|
|
55
|
+
// )
|
|
56
|
+
// Safeguard: handle edge cases
|
|
57
|
+
if (startIndex < 0) {
|
|
58
|
+
// We're scrolled beyond the maximum (showing first items)
|
|
59
|
+
const start = 0;
|
|
60
|
+
const end = Math.min(totalItems, visibleCount + bufferSize * 2);
|
|
61
|
+
// console.log(
|
|
62
|
+
// `[DEBUG] calculateVisibleRange (startIndex < 0): start=${start}, end=${end}`
|
|
63
|
+
// )
|
|
64
|
+
// console.log('calculateVisibleRange:startIndex < 0', {
|
|
65
|
+
// start,
|
|
66
|
+
// end,
|
|
67
|
+
// atBottom,
|
|
68
|
+
// wasAtBottomBeforeHeightChange,
|
|
69
|
+
// lastVisibleRange
|
|
70
|
+
// })
|
|
47
71
|
return { start, end };
|
|
48
72
|
}
|
|
49
73
|
// Add buffer to both ends
|
|
50
|
-
const start = Math.max(0,
|
|
51
|
-
const end = Math.min(totalItems,
|
|
74
|
+
const start = Math.max(0, startIndex - bufferSize);
|
|
75
|
+
const end = Math.min(totalItems, startIndex + visibleCount + bufferSize);
|
|
76
|
+
// console.log(`[DEBUG] calculateVisibleRange result: start=${start}, end=${end}`)
|
|
77
|
+
// console.log('calculateVisibleRange:startIndex >= 0', {
|
|
78
|
+
// start,
|
|
79
|
+
// end,
|
|
80
|
+
// atBottom,
|
|
81
|
+
// wasAtBottomBeforeHeightChange,
|
|
82
|
+
// lastVisibleRange
|
|
83
|
+
// })
|
|
52
84
|
return { start, end };
|
|
53
85
|
}
|
|
54
86
|
else {
|
|
55
87
|
const start = Math.floor(scrollTop / itemHeight);
|
|
56
88
|
const end = Math.min(totalItems, start + Math.ceil(viewportHeight / itemHeight) + 1);
|
|
57
89
|
// Safeguard for topToBottom: ensure last item is fully visible when at max scroll
|
|
58
|
-
const totalHeight = totalItems * itemHeight;
|
|
90
|
+
const totalHeight = totalContentHeight ?? totalItems * itemHeight;
|
|
59
91
|
const maxScrollTop = Math.max(0, totalHeight - viewportHeight);
|
|
60
92
|
// Add dynamic tolerance based on item height for browser rendering precision
|
|
61
93
|
const tolerance = Math.max(itemHeight, 10); // At least one full item height or 10px minimum
|
|
@@ -65,16 +97,24 @@ export const calculateVisibleRange = (scrollTop, viewportHeight, itemHeight, tot
|
|
|
65
97
|
const adjustedEnd = totalItems;
|
|
66
98
|
const visibleItemCount = Math.ceil(viewportHeight / itemHeight) + bufferSize + 1;
|
|
67
99
|
const adjustedStart = Math.max(0, adjustedEnd - visibleItemCount);
|
|
68
|
-
// TopToBottom safeguard is now active
|
|
69
100
|
return {
|
|
70
101
|
start: adjustedStart,
|
|
71
102
|
end: adjustedEnd
|
|
72
103
|
};
|
|
73
104
|
}
|
|
105
|
+
// console.log('calculateVisibleRange:isNotAtBottom', {
|
|
106
|
+
// start: Math.max(0, start - bufferSize),
|
|
107
|
+
// end: Math.min(totalItems, end + bufferSize),
|
|
108
|
+
// atBottom,
|
|
109
|
+
// wasAtBottomBeforeHeightChange,
|
|
110
|
+
// lastVisibleRange
|
|
111
|
+
// })
|
|
74
112
|
// Add buffer to both ends
|
|
113
|
+
const finalStart = Math.max(0, start - bufferSize);
|
|
114
|
+
const finalEnd = Math.min(totalItems, end + bufferSize);
|
|
75
115
|
return {
|
|
76
|
-
start:
|
|
77
|
-
end:
|
|
116
|
+
start: finalStart,
|
|
117
|
+
end: finalEnd
|
|
78
118
|
};
|
|
79
119
|
}
|
|
80
120
|
};
|
|
@@ -90,14 +130,18 @@ export const calculateVisibleRange = (scrollTop, viewportHeight, itemHeight, tot
|
|
|
90
130
|
* @param {number} visibleEnd - Index of the last visible item
|
|
91
131
|
* @param {number} visibleStart - Index of the first visible item
|
|
92
132
|
* @param {number} itemHeight - Height of each list item in pixels
|
|
133
|
+
* @param {number} viewportHeight - Height of the viewport in pixels
|
|
93
134
|
* @returns {number} The calculated transform Y value in pixels
|
|
94
135
|
*/
|
|
95
|
-
export const calculateTransformY = (mode, totalItems, visibleEnd, visibleStart, itemHeight) => {
|
|
136
|
+
export const calculateTransformY = (mode, totalItems, visibleEnd, visibleStart, itemHeight, viewportHeight, totalContentHeight) => {
|
|
96
137
|
if (mode === 'bottomToTop') {
|
|
97
|
-
// In bottomToTop mode,
|
|
98
|
-
|
|
99
|
-
//
|
|
100
|
-
|
|
138
|
+
// In bottomToTop mode, position items so they stack from bottom up
|
|
139
|
+
const actualTotalHeight = totalContentHeight ?? totalItems * itemHeight;
|
|
140
|
+
// Calculate transform to position visible items correctly
|
|
141
|
+
const basicTransform = (totalItems - visibleEnd) * itemHeight;
|
|
142
|
+
// When content is smaller than viewport, push to bottom
|
|
143
|
+
const bottomOffset = Math.max(0, viewportHeight - actualTotalHeight);
|
|
144
|
+
return basicTransform + bottomOffset;
|
|
101
145
|
}
|
|
102
146
|
else {
|
|
103
147
|
return visibleStart * itemHeight;
|
|
@@ -158,7 +202,7 @@ export const updateHeightAndScroll = (state, setters, immediate = false) => {
|
|
|
158
202
|
* 40
|
|
159
203
|
* )
|
|
160
204
|
*/
|
|
161
|
-
export const calculateAverageHeight = (itemElements, visibleRange, heightCache, currentItemHeight, dirtyItems, currentTotalHeight = 0, currentValidCount = 0) => {
|
|
205
|
+
export const calculateAverageHeight = (itemElements, visibleRange, heightCache, currentItemHeight, dirtyItems, currentTotalHeight = 0, currentValidCount = 0, mode = 'topToBottom') => {
|
|
162
206
|
const validElements = itemElements.filter((el) => el);
|
|
163
207
|
if (validElements.length === 0) {
|
|
164
208
|
return {
|
|
@@ -167,11 +211,13 @@ export const calculateAverageHeight = (itemElements, visibleRange, heightCache,
|
|
|
167
211
|
updatedHeightCache: heightCache,
|
|
168
212
|
clearedDirtyItems: new Set(),
|
|
169
213
|
newTotalHeight: currentTotalHeight,
|
|
170
|
-
newValidCount: currentValidCount
|
|
214
|
+
newValidCount: currentValidCount,
|
|
215
|
+
heightChanges: []
|
|
171
216
|
};
|
|
172
217
|
}
|
|
173
218
|
const newHeightCache = { ...heightCache };
|
|
174
219
|
const clearedDirtyItems = new Set();
|
|
220
|
+
const heightChanges = [];
|
|
175
221
|
// Start with current running totals (O(1) instead of O(n))
|
|
176
222
|
let totalValidHeight = currentTotalHeight;
|
|
177
223
|
let validHeightCount = currentValidCount;
|
|
@@ -179,15 +225,36 @@ export const calculateAverageHeight = (itemElements, visibleRange, heightCache,
|
|
|
179
225
|
if (dirtyItems.size > 0) {
|
|
180
226
|
// Process only dirty items
|
|
181
227
|
dirtyItems.forEach((itemIndex) => {
|
|
182
|
-
|
|
228
|
+
// Map original item index to position in itemElements array
|
|
229
|
+
let elementIndex;
|
|
230
|
+
if (mode === 'bottomToTop') {
|
|
231
|
+
// In bottomToTop, itemElements is reversed relative to the visible range
|
|
232
|
+
// elementIndex should be based on position within the actual array, not theoretical end
|
|
233
|
+
elementIndex = validElements.length - 1 - (itemIndex - visibleRange.start);
|
|
234
|
+
}
|
|
235
|
+
else {
|
|
236
|
+
// In topToBottom, itemElements is normal: [item0, item1, ..., item44, item45]
|
|
237
|
+
elementIndex = itemIndex - visibleRange.start;
|
|
238
|
+
}
|
|
183
239
|
const element = validElements[elementIndex];
|
|
184
240
|
if (element && elementIndex >= 0 && elementIndex < validElements.length) {
|
|
185
241
|
try {
|
|
242
|
+
// await tick()
|
|
243
|
+
void element.offsetHeight;
|
|
186
244
|
const height = element.getBoundingClientRect().height;
|
|
245
|
+
const oldHeight = newHeightCache[itemIndex];
|
|
187
246
|
if (Number.isFinite(height) && height > 0) {
|
|
188
|
-
const oldHeight = newHeightCache[itemIndex];
|
|
189
247
|
// Only update if height actually changed (use smaller tolerance for precision)
|
|
190
248
|
if (!oldHeight || Math.abs(oldHeight - height) >= 0.1) {
|
|
249
|
+
// Track the height change for scroll correction
|
|
250
|
+
const actualOldHeight = oldHeight || currentItemHeight;
|
|
251
|
+
const delta = height - actualOldHeight;
|
|
252
|
+
heightChanges.push({
|
|
253
|
+
index: itemIndex,
|
|
254
|
+
oldHeight: actualOldHeight,
|
|
255
|
+
newHeight: height,
|
|
256
|
+
delta
|
|
257
|
+
});
|
|
191
258
|
// Update running totals
|
|
192
259
|
if (oldHeight && Number.isFinite(oldHeight) && oldHeight > 0) {
|
|
193
260
|
// Replace old height with new height in running total
|
|
@@ -208,6 +275,9 @@ export const calculateAverageHeight = (itemElements, visibleRange, heightCache,
|
|
|
208
275
|
clearedDirtyItems.add(itemIndex);
|
|
209
276
|
}
|
|
210
277
|
}
|
|
278
|
+
else {
|
|
279
|
+
clearedDirtyItems.add(itemIndex); // Still clear it from dirty items
|
|
280
|
+
}
|
|
211
281
|
});
|
|
212
282
|
}
|
|
213
283
|
else {
|
|
@@ -237,7 +307,8 @@ export const calculateAverageHeight = (itemElements, visibleRange, heightCache,
|
|
|
237
307
|
updatedHeightCache: newHeightCache,
|
|
238
308
|
clearedDirtyItems,
|
|
239
309
|
newTotalHeight: totalValidHeight,
|
|
240
|
-
newValidCount: validHeightCount
|
|
310
|
+
newValidCount: validHeightCount,
|
|
311
|
+
heightChanges
|
|
241
312
|
};
|
|
242
313
|
};
|
|
243
314
|
/**
|
|
@@ -283,31 +354,6 @@ onComplete) => {
|
|
|
283
354
|
};
|
|
284
355
|
await processChunk(0);
|
|
285
356
|
};
|
|
286
|
-
/**
|
|
287
|
-
* Builds a block sum array for fast offset calculation in large virtual lists.
|
|
288
|
-
* Each entry in the array is the total height up to the end of that block (exclusive).
|
|
289
|
-
*
|
|
290
|
-
* @param {HeightCache} heightCache - Map of measured item heights with dirty tracking
|
|
291
|
-
* @param {number} calculatedItemHeight - Estimated height for unmeasured items
|
|
292
|
-
* @param {number} totalItems - Total number of items in the list
|
|
293
|
-
* @param {number} blockSize - Number of items per block
|
|
294
|
-
* @returns {number[]} Array of prefix sums at each block boundary
|
|
295
|
-
*/
|
|
296
|
-
export const buildBlockSums = (heightCache, calculatedItemHeight, totalItems, blockSize = 1000) => {
|
|
297
|
-
const blockSums = [];
|
|
298
|
-
let sum = 0;
|
|
299
|
-
for (let i = 0; i < totalItems; i++) {
|
|
300
|
-
sum += heightCache[i] ?? calculatedItemHeight;
|
|
301
|
-
if ((i + 1) % blockSize === 0) {
|
|
302
|
-
blockSums.push(sum);
|
|
303
|
-
}
|
|
304
|
-
}
|
|
305
|
-
// Push the last partial block if needed
|
|
306
|
-
if (totalItems % blockSize !== 0) {
|
|
307
|
-
blockSums.push(sum);
|
|
308
|
-
}
|
|
309
|
-
return blockSums;
|
|
310
|
-
};
|
|
311
357
|
/**
|
|
312
358
|
* Calculates the scroll offset (in pixels) needed to bring a specific item into view in a virtual list.
|
|
313
359
|
*
|
|
@@ -336,7 +382,8 @@ export const getScrollOffsetForIndex = (heightCache, calculatedItemHeight, idx,
|
|
|
336
382
|
// Fallback: O(n) for a single query
|
|
337
383
|
let offset = 0;
|
|
338
384
|
for (let i = 0; i < idx; i++) {
|
|
339
|
-
|
|
385
|
+
const height = heightCache[i] ?? calculatedItemHeight;
|
|
386
|
+
offset += height;
|
|
340
387
|
}
|
|
341
388
|
return offset;
|
|
342
389
|
}
|
|
@@ -74,4 +74,4 @@ export declare function shouldShowDebugInfo(prevRange: {
|
|
|
74
74
|
export declare function createDebugInfo(visibleRange: {
|
|
75
75
|
start: number;
|
|
76
76
|
end: number;
|
|
77
|
-
}, totalItems: number, processedItems: number, averageItemHeight: number, scrollTop: number, viewportHeight: number): SvelteVirtualListDebugInfo;
|
|
77
|
+
}, totalItems: number, processedItems: number, averageItemHeight: number, scrollTop: number, viewportHeight: number, totalHeight: number): SvelteVirtualListDebugInfo;
|
|
@@ -70,8 +70,7 @@ export function shouldShowDebugInfo(prevRange, currentRange, prevHeight, current
|
|
|
70
70
|
*
|
|
71
71
|
* @throws {Error} Will throw if end index is less than start index in visibleRange
|
|
72
72
|
*/
|
|
73
|
-
export function createDebugInfo(visibleRange, totalItems, processedItems, averageItemHeight, scrollTop, viewportHeight) {
|
|
74
|
-
const totalHeight = totalItems * averageItemHeight;
|
|
73
|
+
export function createDebugInfo(visibleRange, totalItems, processedItems, averageItemHeight, scrollTop, viewportHeight, totalHeight) {
|
|
75
74
|
const atTop = scrollTop <= 1; // Small tolerance for floating point precision
|
|
76
75
|
const atBottom = scrollTop >= totalHeight - viewportHeight - 1; // Small tolerance
|
|
77
76
|
return {
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@humanspeak/svelte-virtual-list",
|
|
3
|
-
"version": "0.2.6-beta.
|
|
3
|
+
"version": "0.2.6-beta.7",
|
|
4
4
|
"description": "A lightweight, high-performance virtual list component for Svelte 5 that renders large datasets with minimal memory usage. Features include dynamic height support, smooth scrolling, TypeScript support, and efficient DOM recycling. Ideal for infinite scrolling lists, data tables, chat interfaces, and any application requiring the rendering of thousands of items without compromising performance. Zero dependencies and fully customizable.",
|
|
5
5
|
"keywords": [
|
|
6
6
|
"svelte",
|
|
@@ -44,7 +44,8 @@
|
|
|
44
44
|
"dist",
|
|
45
45
|
"!dist/**/*.test.*",
|
|
46
46
|
"!dist/**/*.spec.*",
|
|
47
|
-
"!dist/test/**/*"
|
|
47
|
+
"!dist/test/**/*",
|
|
48
|
+
"!dist/reactive-height-manager/test/**/*"
|
|
48
49
|
],
|
|
49
50
|
"scripts": {
|
|
50
51
|
"build": "vite build && npm run package",
|
|
@@ -58,14 +59,14 @@
|
|
|
58
59
|
"prepare": "husky",
|
|
59
60
|
"prepublishOnly": "npm run package",
|
|
60
61
|
"preview": "vite preview",
|
|
61
|
-
"test": "vitest run --coverage",
|
|
62
|
+
"test": "vitest run --coverage --",
|
|
62
63
|
"test:all": "npm run test && npm run test:e2e",
|
|
63
|
-
"test:e2e": "playwright test",
|
|
64
|
-
"test:e2e:debug": "playwright test --debug",
|
|
64
|
+
"test:e2e": "playwright test --",
|
|
65
|
+
"test:e2e:debug": "playwright test --debug --",
|
|
65
66
|
"test:e2e:report": "playwright show-report",
|
|
66
|
-
"test:e2e:ui": "playwright test --ui",
|
|
67
|
-
"test:only": "vitest run",
|
|
68
|
-
"test:watch": "vitest"
|
|
67
|
+
"test:e2e:ui": "playwright test --ui --",
|
|
68
|
+
"test:only": "vitest run --",
|
|
69
|
+
"test:watch": "vitest --"
|
|
69
70
|
},
|
|
70
71
|
"overrides": {
|
|
71
72
|
"@sveltejs/kit": {
|
|
@@ -79,17 +80,17 @@
|
|
|
79
80
|
"@eslint/compat": "^1.3.1",
|
|
80
81
|
"@eslint/js": "^9.32.0",
|
|
81
82
|
"@faker-js/faker": "^9.9.0",
|
|
82
|
-
"@playwright/test": "^1.54.
|
|
83
|
-
"@sveltejs/adapter-auto": "^6.0.
|
|
84
|
-
"@sveltejs/kit": "^2.
|
|
85
|
-
"@sveltejs/package": "^2.4.
|
|
83
|
+
"@playwright/test": "^1.54.2",
|
|
84
|
+
"@sveltejs/adapter-auto": "^6.0.2",
|
|
85
|
+
"@sveltejs/kit": "^2.27.3",
|
|
86
|
+
"@sveltejs/package": "^2.4.1",
|
|
86
87
|
"@sveltejs/vite-plugin-svelte": "^6.1.0",
|
|
87
88
|
"@testing-library/jest-dom": "^6.6.4",
|
|
88
89
|
"@testing-library/svelte": "^5.2.8",
|
|
89
90
|
"@testing-library/user-event": "^14.6.1",
|
|
90
|
-
"@types/node": "^24.
|
|
91
|
-
"@typescript-eslint/eslint-plugin": "^8.
|
|
92
|
-
"@typescript-eslint/parser": "^8.
|
|
91
|
+
"@types/node": "^24.2.0",
|
|
92
|
+
"@typescript-eslint/eslint-plugin": "^8.39.0",
|
|
93
|
+
"@typescript-eslint/parser": "^8.39.0",
|
|
93
94
|
"@vitest/coverage-v8": "^3.2.4",
|
|
94
95
|
"eslint": "^9.32.0",
|
|
95
96
|
"eslint-config-prettier": "^10.1.8",
|
|
@@ -105,11 +106,11 @@
|
|
|
105
106
|
"prettier-plugin-svelte": "^3.4.0",
|
|
106
107
|
"prettier-plugin-tailwindcss": "^0.6.14",
|
|
107
108
|
"publint": "^0.3.12",
|
|
108
|
-
"svelte": "^5.
|
|
109
|
-
"svelte-check": "^4.3.
|
|
110
|
-
"typescript": "^5.
|
|
111
|
-
"typescript-eslint": "^8.
|
|
112
|
-
"vite": "^7.0
|
|
109
|
+
"svelte": "^5.38.0",
|
|
110
|
+
"svelte-check": "^4.3.1",
|
|
111
|
+
"typescript": "^5.9.2",
|
|
112
|
+
"typescript-eslint": "^8.39.0",
|
|
113
|
+
"vite": "^7.1.0",
|
|
113
114
|
"vitest": "^3.2.4"
|
|
114
115
|
},
|
|
115
116
|
"peerDependencies": {
|