browser-extension-settings 0.8.9 → 0.9.0

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/lib/settings.ts CHANGED
@@ -2,7 +2,7 @@ import {
2
2
  addValueChangeListener,
3
3
  getValue,
4
4
  setValue,
5
- } from "browser-extension-storage"
5
+ } from 'browser-extension-storage'
6
6
  import {
7
7
  $,
8
8
  $$,
@@ -15,17 +15,17 @@ import {
15
15
  registerMenuCommand,
16
16
  removeEventListener,
17
17
  createElement,
18
- } from "browser-extension-utils"
19
- import styleText from "data-text:./style.scss"
18
+ } from 'browser-extension-utils'
19
+ import styleText from 'data-text:./style.scss'
20
20
  import {
21
21
  initAvailableLocales,
22
22
  getPrefferedLocale,
23
- } from "browser-extension-i18n"
24
- import { createSwitchOption } from "./switch"
25
- import { besVersion } from "./common"
26
- import { i, resetI18n, localeNames } from "./messages/index"
23
+ } from 'browser-extension-i18n'
24
+ import { createSwitchOption } from './switch'
25
+ import { besVersion } from './common'
26
+ import { i, resetI18n, localeNames } from './messages/index'
27
27
 
28
- const prefix = "browser_extension_settings_v2_"
28
+ const prefix = 'browser_extension_settings_v2_'
29
29
 
30
30
  type SettingsOptions = {
31
31
  id: string
@@ -51,7 +51,7 @@ type SettingsSwitchItem = {
51
51
  title: string
52
52
  icon?: string
53
53
  defaultValue: boolean
54
- type?: "switch"
54
+ type?: 'switch'
55
55
  onConfirmChange?: (checked: boolean) => boolean
56
56
  group?: number
57
57
  }
@@ -78,7 +78,7 @@ type SettingsActionItem = {
78
78
  type SettingsSelectItem = {
79
79
  title: string
80
80
  icon?: string
81
- type: "select"
81
+ type: 'select'
82
82
  options: Record<string, string | number>
83
83
  group?: number
84
84
  defaultValue?: string | number
@@ -108,7 +108,7 @@ const getSettingsElement = () => {
108
108
  )
109
109
  }
110
110
 
111
- const storageKey = "settings"
111
+ const storageKey = 'settings'
112
112
 
113
113
  let settingsOptions: SettingsOptions
114
114
  let settingsTable: SettingsTable = {}
@@ -119,7 +119,7 @@ async function getSettings(): Promise<
119
119
  let settings =
120
120
  await getValue<Record<string, boolean | string | undefined>>(storageKey)
121
121
 
122
- if (!settings || typeof settings !== "object") {
122
+ if (!settings || typeof settings !== 'object') {
123
123
  settings = {}
124
124
  }
125
125
 
@@ -176,19 +176,19 @@ const closeModal = () => {
176
176
  settingsContainer.remove()
177
177
  }
178
178
 
179
- removeEventListener(doc, "click", onDocumentClick, true)
180
- removeEventListener(doc, "keydown", onDocumentKeyDown, true)
181
- removeEventListener(win, "beforeShowSettings", onBeforeShowSettings, true)
179
+ removeEventListener(doc, 'click', onDocumentClick, true)
180
+ removeEventListener(doc, 'keydown', onDocumentKeyDown, true)
181
+ removeEventListener(doc, 'beforeShowSettings', onBeforeShowSettings, true)
182
182
  }
183
183
 
184
184
  export function hideSettings() {
185
185
  if (win.self !== win.top) {
186
186
  win.top?.postMessage(
187
187
  {
188
- type: "bes-hide-settings",
188
+ type: 'bes-hide-settings',
189
189
  id: settingsOptions?.id,
190
190
  },
191
- "*"
191
+ '*'
192
192
  )
193
193
  return
194
194
  }
@@ -223,7 +223,7 @@ const onDocumentKeyDown = (event: KeyboardEvent) => {
223
223
  return // 如果事件已经在进行中,则不做任何事。
224
224
  }
225
225
 
226
- if (event.key === "Escape") {
226
+ if (event.key === 'Escape') {
227
227
  // 按“ESC”键时要做的事。
228
228
  closeModal()
229
229
  // 取消默认动作,从而避免处理两次。
@@ -239,11 +239,11 @@ async function updateOptions() {
239
239
  for (const key in settingsTable) {
240
240
  if (Object.hasOwn(settingsTable, key)) {
241
241
  const item = settingsTable[key]
242
- const type = item.type || "switch"
242
+ const type = item.type || 'switch'
243
243
 
244
244
  // console.log(key, type)
245
245
  switch (type) {
246
- case "switch": {
246
+ case 'switch': {
247
247
  const root = getSettingsElement()!
248
248
  const checkbox = $(
249
249
  `.option_groups .switch_option[data-key="${key}"] input`,
@@ -256,7 +256,7 @@ async function updateOptions() {
256
256
  break
257
257
  }
258
258
 
259
- case "select": {
259
+ case 'select': {
260
260
  const root = getSettingsElement()!
261
261
  const options = $$(
262
262
  `.option_groups .select_option[data-key="${key}"] .bes_select option`,
@@ -270,7 +270,7 @@ async function updateOptions() {
270
270
  break
271
271
  }
272
272
 
273
- case "textarea": {
273
+ case 'textarea': {
274
274
  const root = getSettingsElement()!
275
275
  const textArea = $(
276
276
  `.option_groups textarea[data-key="${key}"]`,
@@ -290,7 +290,7 @@ async function updateOptions() {
290
290
  }
291
291
  }
292
292
 
293
- if (typeof settingsOptions.onViewUpdate === "function") {
293
+ if (typeof settingsOptions.onViewUpdate === 'function') {
294
294
  const settingsMain = createSettingsElement()
295
295
  settingsOptions.onViewUpdate(settingsMain!)
296
296
  }
@@ -308,9 +308,9 @@ function getSettingsContainer(create = false) {
308
308
  }
309
309
 
310
310
  if (create) {
311
- return addElement(doc.documentElement, "div", {
311
+ return addElement(doc.documentElement, 'div', {
312
312
  class: `${prefix}container`,
313
- "data-bes-version": besVersion,
313
+ 'data-bes-version': besVersion,
314
314
  })
315
315
  }
316
316
  }
@@ -318,7 +318,7 @@ function getSettingsContainer(create = false) {
318
318
  function getSettingsShadowRoot(): ShadowRoot | undefined {
319
319
  const container = getSettingsContainer(true)
320
320
  if (container?.attachShadow) {
321
- return container.shadowRoot || container.attachShadow({ mode: "open" })
321
+ return container.shadowRoot || container.attachShadow({ mode: 'open' })
322
322
  }
323
323
 
324
324
  return undefined
@@ -330,18 +330,18 @@ function getSettingsWrapper() {
330
330
  const container = getSettingsContainer(true)!
331
331
  return (
332
332
  $(`.${prefix}wrapper`, container) ||
333
- addElement(container, "div", { class: `${prefix}wrapper` })
333
+ addElement(container, 'div', { class: `${prefix}wrapper` })
334
334
  )
335
335
  }
336
336
 
337
337
  let wrapper = shadow.querySelector(`.${prefix}wrapper`)
338
338
  if (!wrapper) {
339
- wrapper = createElement("div", { class: `${prefix}wrapper` })
339
+ wrapper = createElement('div', { class: `${prefix}wrapper` })
340
340
  shadow.append(wrapper)
341
341
 
342
342
  const existStyle = shadow.querySelector(`style`)
343
343
  if (!existStyle) {
344
- const styleElm = createElement("style")
344
+ const styleElm = createElement('style')
345
345
  styleElm.textContent = styleText
346
346
  shadow.append(styleElm)
347
347
  }
@@ -359,14 +359,14 @@ function createSettingsElement() {
359
359
  element.remove()
360
360
  }
361
361
 
362
- settingsMain = addElement(wrapper, "div", {
362
+ settingsMain = addElement(wrapper, 'div', {
363
363
  class: `${prefix}main thin_scrollbar`,
364
364
  })
365
365
 
366
- const header = addElement(settingsMain, "header", {
367
- style: "display: flex; justify-content: flex-end;",
366
+ const header = addElement(settingsMain, 'header', {
367
+ style: 'display: flex; justify-content: flex-end;',
368
368
  })
369
- addElement(header, "div", {
369
+ addElement(header, 'div', {
370
370
  class: `close-button`,
371
371
  // eslint-disable-next-line @typescript-eslint/naming-convention
372
372
  innerHTML: createHTML(
@@ -376,15 +376,15 @@ function createSettingsElement() {
376
376
  })
377
377
 
378
378
  if (settingsOptions.title) {
379
- addElement(settingsMain, "h2", { textContent: settingsOptions.title })
379
+ addElement(settingsMain, 'h2', { textContent: settingsOptions.title })
380
380
  }
381
381
 
382
382
  const optionGroups: HTMLElement[] = []
383
383
  const getOptionGroup = (index: number) => {
384
384
  if (index > optionGroups.length) {
385
385
  for (let i = optionGroups.length; i < index; i++) {
386
- const optionGroup = addElement(settingsMain, "div", {
387
- class: "option_groups",
386
+ const optionGroup = addElement(settingsMain, 'div', {
387
+ class: 'option_groups',
388
388
  })
389
389
  if (optionGroup) optionGroups.push(optionGroup)
390
390
  }
@@ -396,12 +396,12 @@ function createSettingsElement() {
396
396
  for (const key in settingsTable) {
397
397
  if (Object.hasOwn(settingsTable, key)) {
398
398
  const item = settingsTable[key]
399
- const type = item.type || "switch"
399
+ const type = item.type || 'switch'
400
400
  const group = item.group || 1
401
401
  const optionGroup = getOptionGroup(group)
402
402
  // console.log(key, item, type, group)
403
403
  switch (type) {
404
- case "switch": {
404
+ case 'switch': {
405
405
  const switchOption = createSwitchOption(item.icon, item.title, {
406
406
  async onchange(event: Event) {
407
407
  const checkbox = event.target as HTMLInputElement
@@ -409,7 +409,7 @@ function createSettingsElement() {
409
409
  let result = true
410
410
  if (
411
411
  typeof (item as SettingsSwitchItem).onConfirmChange ===
412
- "function"
412
+ 'function'
413
413
  ) {
414
414
  result = (item as SettingsSwitchItem).onConfirmChange!(
415
415
  checkbox.checked
@@ -432,14 +432,14 @@ function createSettingsElement() {
432
432
  break
433
433
  }
434
434
 
435
- case "textarea": {
435
+ case 'textarea': {
436
436
  let timeoutId: ReturnType<typeof setTimeout> | undefined
437
- const div = addElement(optionGroup, "div", {
438
- class: "bes_textarea",
437
+ const div = addElement(optionGroup, 'div', {
438
+ class: 'bes_textarea',
439
439
  })
440
- addElement(div, "textarea", {
441
- "data-key": key,
442
- placeholder: (item as SettingsInputItem).placeholder || "",
440
+ addElement(div, 'textarea', {
441
+ 'data-key': key,
442
+ placeholder: (item as SettingsInputItem).placeholder || '',
443
443
  onkeyup(event: Event) {
444
444
  const textArea = event.target as HTMLTextAreaElement
445
445
  if (timeoutId) {
@@ -451,17 +451,17 @@ function createSettingsElement() {
451
451
  if (textArea) {
452
452
  await saveSettingsValue(key, textArea.value.trim())
453
453
  }
454
- }, 100)
454
+ }, 2000)
455
455
  },
456
456
  })
457
457
 
458
458
  break
459
459
  }
460
460
 
461
- case "action": {
462
- addElement(optionGroup, "a", {
463
- "data-key": key,
464
- class: "action",
461
+ case 'action': {
462
+ addElement(optionGroup, 'a', {
463
+ 'data-key': key,
464
+ class: 'action',
465
465
  textContent: item.title,
466
466
  onclick: (item as SettingsActionItem).onclick,
467
467
  })
@@ -469,36 +469,36 @@ function createSettingsElement() {
469
469
  break
470
470
  }
471
471
 
472
- case "externalLink": {
473
- const div4 = addElement(optionGroup, "div", {
474
- class: "bes_external_link",
472
+ case 'externalLink': {
473
+ const div4 = addElement(optionGroup, 'div', {
474
+ class: 'bes_external_link',
475
475
  })
476
476
 
477
- addElement(div4, "a", {
478
- "data-key": key,
477
+ addElement(div4, 'a', {
478
+ 'data-key': key,
479
479
  textContent: item.title,
480
480
  href: (item as SettingsActionItem).url,
481
- target: "_blank",
481
+ target: '_blank',
482
482
  })
483
483
  break
484
484
  }
485
485
 
486
- case "select": {
487
- const div = addElement(optionGroup, "div", {
488
- class: "select_option bes_option",
489
- "data-key": key,
486
+ case 'select': {
487
+ const div = addElement(optionGroup, 'div', {
488
+ class: 'select_option bes_option',
489
+ 'data-key': key,
490
490
  })
491
491
  if (item.icon) {
492
- addElement(div, "img", { src: item.icon, class: "bes_icon" })
492
+ addElement(div, 'img', { src: item.icon, class: 'bes_icon' })
493
493
  }
494
494
 
495
- addElement(div, "span", {
495
+ addElement(div, 'span', {
496
496
  textContent: item.title,
497
- class: "bes_title",
497
+ class: 'bes_title',
498
498
  })
499
499
 
500
- const select = addElement(div, "select", {
501
- class: "bes_select",
500
+ const select = addElement(div, 'select', {
501
+ class: 'bes_select',
502
502
  async onchange() {
503
503
  await saveSettingsValue(key, select.value)
504
504
  },
@@ -507,7 +507,7 @@ function createSettingsElement() {
507
507
  for (const option of Object.entries(
508
508
  (item as SettingsSelectItem).options
509
509
  )) {
510
- addElement(select, "option", {
510
+ addElement(select, 'option', {
511
511
  textContent: option[0],
512
512
  value: option[1],
513
513
  })
@@ -516,16 +516,16 @@ function createSettingsElement() {
516
516
  break
517
517
  }
518
518
 
519
- case "tip": {
520
- const tip = addElement(optionGroup, "div", {
521
- class: "bes_tip",
519
+ case 'tip': {
520
+ const tip = addElement(optionGroup, 'div', {
521
+ class: 'bes_tip',
522
522
  })
523
- addElement(tip, "a", {
524
- class: "bes_tip_anchor",
523
+ addElement(tip, 'a', {
524
+ class: 'bes_tip_anchor',
525
525
  textContent: item.title,
526
526
  })
527
- const tipContent = addElement(tip, "div", {
528
- class: "bes_tip_content",
527
+ const tipContent = addElement(tip, 'div', {
528
+ class: 'bes_tip_content',
529
529
  // eslint-disable-next-line @typescript-eslint/naming-convention
530
530
  innerHTML: createHTML((item as SettingsTipItem).tipContent),
531
531
  })
@@ -541,9 +541,9 @@ function createSettingsElement() {
541
541
  }
542
542
 
543
543
  if (settingsOptions.footer) {
544
- const footer = addElement(settingsMain, "footer")
544
+ const footer = addElement(settingsMain, 'footer')
545
545
  footer!.innerHTML = createHTML(
546
- typeof settingsOptions.footer === "string"
546
+ typeof settingsOptions.footer === 'string'
547
547
  ? settingsOptions.footer
548
548
  : `<p>Made with ❤️ by
549
549
  <a href="https://www.pipecraft.net/" target="_blank">
@@ -575,9 +575,9 @@ function addCommonSettings(
575
575
  // Switch locale
576
576
  // eslint-disable-next-line @typescript-eslint/consistent-type-assertions
577
577
  settingsTable.locale = {
578
- title: i("settings.locale"),
579
- type: "select",
580
- defaultValue: "",
578
+ title: i('settings.locale'),
579
+ type: 'select',
580
+ defaultValue: '',
581
581
  options: {},
582
582
  group: ++maxGroup,
583
583
  } as SettingsSelectItem
@@ -597,7 +597,7 @@ function handleShowSettingsUrl() {
597
597
  const hashString = `#!show-settings-${settingsOptions.id}`
598
598
  if (location.hash === hashString) {
599
599
  setTimeout(showSettings, 100)
600
- history.replaceState({}, "", location.href.replace(hashString, ""))
600
+ history.replaceState({}, '', location.href.replace(hashString, ''))
601
601
  }
602
602
  }
603
603
 
@@ -611,28 +611,28 @@ export async function showSettings() {
611
611
  if (win.self !== win.top) {
612
612
  win.top?.postMessage(
613
613
  {
614
- type: "bes-show-settings",
614
+ type: 'bes-show-settings',
615
615
  id: settingsOptions?.id,
616
616
  },
617
- "*"
617
+ '*'
618
618
  )
619
619
  return
620
620
  }
621
621
 
622
622
  closeModal()
623
623
 
624
- const event = new CustomEvent("beforeShowSettings")
624
+ const event = new CustomEvent('beforeShowSettings')
625
625
  // Dispatch beforeShowSettings event to close other extension's settings
626
- win.dispatchEvent(event)
626
+ doc.dispatchEvent(event)
627
627
 
628
628
  // Listen to beforeShowSettings event to close opened modal before showing settings from other extension
629
- addEventListener(win, "beforeShowSettings", onBeforeShowSettings, true)
629
+ addEventListener(doc, 'beforeShowSettings', onBeforeShowSettings, true)
630
630
 
631
631
  createSettingsElement()
632
632
  await updateOptions()
633
633
 
634
- addEventListener(doc, "click", onDocumentClick, true)
635
- addEventListener(doc, "keydown", onDocumentKeyDown, true)
634
+ addEventListener(doc, 'click', onDocumentClick, true)
635
+ addEventListener(doc, 'keydown', onDocumentKeyDown, true)
636
636
  // activeExtension(settingsOptions.id)
637
637
  // deactiveExtensionList()
638
638
  }
@@ -642,7 +642,7 @@ let lastLocale: string | undefined
642
642
  // Reset settings UI on init and locale change
643
643
  // eslint-disable-next-line @typescript-eslint/naming-convention
644
644
  const resetSettingsUI = (optionsProvider: () => SettingsOptions) => {
645
- lastLocale = getSettingsValue("locale") || getPrefferedLocale()
645
+ lastLocale = getSettingsValue('locale') || getPrefferedLocale()
646
646
  resetI18n(lastLocale)
647
647
 
648
648
  const options = optionsProvider()
@@ -656,7 +656,7 @@ const resetSettingsUI = (optionsProvider: () => SettingsOptions) => {
656
656
  initAvailableLocales(availableLocales)
657
657
  const localeSelect = settingsTable.locale as SettingsSelectItem
658
658
  localeSelect.options = {
659
- [i("settings.systemLanguage")]: "",
659
+ [i('settings.systemLanguage')]: '',
660
660
  }
661
661
  for (const locale of availableLocales) {
662
662
  // Use language display name from localeNames, fallback to locale code if not found
@@ -681,7 +681,7 @@ export const initSettings = async (optionsProvider: () => SettingsOptions) => {
681
681
  // addSideMenu()
682
682
 
683
683
  const newLocale =
684
- getSettingsValue<string | undefined>("locale") || getPrefferedLocale()
684
+ getSettingsValue<string | undefined>('locale') || getPrefferedLocale()
685
685
  // console.log("lastLocale:", lastLocale, "newLocale:", newLocale)
686
686
  if (lastLocale !== newLocale) {
687
687
  const isShown = isSettingsShown()
@@ -698,7 +698,7 @@ export const initSettings = async (optionsProvider: () => SettingsOptions) => {
698
698
  }
699
699
  }
700
700
 
701
- if (typeof settingsOptions.onValueChange === "function") {
701
+ if (typeof settingsOptions.onValueChange === 'function') {
702
702
  settingsOptions.onValueChange()
703
703
  }
704
704
  })
@@ -711,18 +711,18 @@ export const initSettings = async (optionsProvider: () => SettingsOptions) => {
711
711
  resetSettingsUI(optionsProvider)
712
712
  }, 50)
713
713
 
714
- void registerMenuCommand(i("settings.menu.settings"), showSettings, {
715
- accessKey: "o",
714
+ void registerMenuCommand(i('settings.menu.settings'), showSettings, {
715
+ accessKey: 'o',
716
716
  })
717
717
 
718
- addEventListener(win, "message", (event: MessageEvent) => {
718
+ addEventListener(win, 'message', (event: MessageEvent) => {
719
719
  if (!event.data || event.data.id !== settingsOptions?.id) {
720
720
  return
721
721
  }
722
722
 
723
- if (event.data.type === "bes-show-settings") {
723
+ if (event.data.type === 'bes-show-settings') {
724
724
  void showSettings()
725
- } else if (event.data.type === "bes-hide-settings") {
725
+ } else if (event.data.type === 'bes-hide-settings') {
726
726
  hideSettings()
727
727
  }
728
728
  })
package/lib/style.scss CHANGED
@@ -220,7 +220,8 @@
220
220
  .option_groups .bes_tip {
221
221
  position: relative;
222
222
  margin: 0;
223
- padding: 0 15px 0 0;
223
+ margin-top: -1px;
224
+ padding: 6px 15px 6px 0;
224
225
  border: none;
225
226
  max-width: none;
226
227
  font-size: 14px;
package/lib/switch.ts CHANGED
@@ -2,7 +2,7 @@ import {
2
2
  addElement,
3
3
  addEventListener,
4
4
  createElement,
5
- } from "browser-extension-utils"
5
+ } from 'browser-extension-utils'
6
6
 
7
7
  type SwichOptions = {
8
8
  checked?: boolean
@@ -10,17 +10,17 @@ type SwichOptions = {
10
10
  }
11
11
 
12
12
  export function createSwitch(options = {} as SwichOptions): HTMLElement {
13
- const container = createElement("label", { class: "bes_switch_container" })
13
+ const container = createElement('label', { class: 'bes_switch_container' })
14
14
  const checkbox = createElement(
15
- "input",
16
- options.checked ? { type: "checkbox", checked: "" } : { type: "checkbox" }
15
+ 'input',
16
+ options.checked ? { type: 'checkbox', checked: '' } : { type: 'checkbox' }
17
17
  )
18
18
  addElement(container, checkbox)
19
- const switchElm = createElement("span", { class: "bes_switch" })
20
- addElement(switchElm, "span", { class: "bes_slider" })
19
+ const switchElm = createElement('span', { class: 'bes_switch' })
20
+ addElement(switchElm, 'span', { class: 'bes_slider' })
21
21
  addElement(container, switchElm)
22
22
  if (options.onchange) {
23
- addEventListener(checkbox, "change", options.onchange)
23
+ addEventListener(checkbox, 'change', options.onchange)
24
24
  }
25
25
 
26
26
  return container
@@ -40,16 +40,16 @@ export function createSwitchOption(
40
40
  text: string | SwichOptions,
41
41
  options?: SwichOptions
42
42
  ): HTMLElement {
43
- if (typeof text !== "string") {
43
+ if (typeof text !== 'string') {
44
44
  return createSwitchOption(undefined, icon, text)
45
45
  }
46
46
 
47
- const div = createElement("div", { class: "switch_option bes_option" })
47
+ const div = createElement('div', { class: 'switch_option bes_option' })
48
48
  if (icon) {
49
- addElement(div, "img", { src: icon, class: "bes_icon" })
49
+ addElement(div, 'img', { src: icon, class: 'bes_icon' })
50
50
  }
51
51
 
52
- addElement(div, "span", { textContent: text, class: "bes_title" })
52
+ addElement(div, 'span', { textContent: text, class: 'bes_title' })
53
53
  div.append(createSwitch(options))
54
54
  return div
55
55
  }
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "browser-extension-settings",
3
- "version": "0.8.9",
3
+ "version": "0.9.0",
4
4
  "description": "Settings module for developing browser extensions and userscripts",
5
5
  "type": "module",
6
6
  "main": "./lib/index.ts",
@@ -31,18 +31,18 @@
31
31
  "homepage": "https://github.com/utags/browser-extension-settings#readme",
32
32
  "dependencies": {
33
33
  "browser-extension-i18n": "^0.1.3",
34
- "browser-extension-storage": "^0.2.8",
35
- "browser-extension-utils": "^0.3.4"
34
+ "browser-extension-storage": "^0.2.10",
35
+ "browser-extension-utils": "^0.5.1"
36
36
  },
37
37
  "devDependencies": {
38
- "@types/chrome": "^0.1.32",
39
- "@vitest/coverage-v8": "^4.0.16",
40
- "jsdom": "^27.4.0",
38
+ "@types/chrome": "^0.1.38",
39
+ "@vitest/coverage-v8": "^4.1.0",
40
+ "jsdom": "^29.0.0",
41
41
  "npm-run-all": "^4.1.5",
42
- "prettier": "^3.7.4",
42
+ "prettier": "^3.8.1",
43
43
  "typescript": "^5.9.3",
44
- "vitest": "^4.0.16",
45
- "xo": "^1.2.3"
44
+ "vitest": "^4.1.0",
45
+ "xo": "^2.0.2"
46
46
  },
47
47
  "files": [
48
48
  "lib/",