diff --git a/.gitignore b/.gitignore index de4e3dcc..a697371f 100644 --- a/.gitignore +++ b/.gitignore @@ -13,6 +13,14 @@ out/ .project .settings +# VSC +.vscode + # Python **/__pycache__/ -**/venv/ \ No newline at end of file +**/venv/ + +# Logs +logs +*.log +npm-debug.log* \ No newline at end of file diff --git a/web-components-reuse/.editorconfig b/web-components-reuse/.editorconfig new file mode 100644 index 00000000..a3c4095f --- /dev/null +++ b/web-components-reuse/.editorconfig @@ -0,0 +1,3 @@ +[*.{ts,tsx,js,vue}] +indent_style = space +indent_size = 2 \ No newline at end of file diff --git a/web-components-reuse/README.md b/web-components-reuse/README.md new file mode 100644 index 00000000..360a1c54 --- /dev/null +++ b/web-components-reuse/README.md @@ -0,0 +1,8 @@ +* as much reuse as possible +* components do not make network calls; they support injecting as much data externally as possible +* avoid ids when to necessary: + * bubbles: true for events + * this.querySelector('[data-{attr}]') pattern for partially updateable/configureable elements +* more explicit deps between components? +* refactor attrs vs props when/where needed which +* dev scoped deps for server! \ No newline at end of file diff --git a/web-components-reuse/components/README.md b/web-components-reuse/components/README.md new file mode 100644 index 00000000..b1810afe --- /dev/null +++ b/web-components-reuse/components/README.md @@ -0,0 +1,4 @@ +Define all components in this dir and provide a script to generate a single file with all of them to reuse in various contexts. + +TODO: https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Classes/Private_elements +TODO: refactor all components with private, check whether listeners are cleaned up \ No newline at end of file diff --git a/web-components-reuse/components/config.json b/web-components-reuse/components/config.json new file mode 100644 index 00000000..ec2ff60d --- /dev/null +++ b/web-components-reuse/components/config.json @@ -0,0 +1,21 @@ +{ + "components": [ + "base.js", + "tabs-container.js", + "drop-down.js", + "info-modal.js", + "error-modal.js", + "asset-element.js", + "currency-element.js", + "assets-and-currencies.js", + "markets-header.js", + "markets-comparator.js", + "projections-calculator.js", + "markets-projections.js" + ], + "outputs": [ + "../vue-app/src/components/web-components.js", + "../react-app/src/components/web-components.js", + "../server/assets/web-components.js" + ] +} \ No newline at end of file diff --git a/web-components-reuse/components/package.py b/web-components-reuse/components/package.py new file mode 100644 index 00000000..fc2ca20f --- /dev/null +++ b/web-components-reuse/components/package.py @@ -0,0 +1,45 @@ +import json +import subprocess +from os import path + +with open('config.json') as f: + config = json.load(f) + +register_lines = []; +lines_to_write = [] +for c in config['components']: + with open(path.join('src', c)) as c: + skip_next_line = False + for c_line in c.readlines(): + if 'customElements.define' in c_line: + register_lines.append(c_line) + skip_next_line = True + continue + + if skip_next_line: + skip_next_line = False + continue + + if 'import' in c_line or 'register()' in c_line: + continue + + lines_to_write.append(c_line) + + +def git_metadata(): + commit_hash = subprocess.check_output(["git", "rev-parse", "HEAD"]).strip().decode('utf-8') + branch = subprocess.check_output(["git", "rev-parse", "--abbrev-ref", "HEAD"]).strip().decode('utf-8') + return f'{branch}:{commit_hash}' + + +metadata = f'Generated by components/{path.basename(__file__)} from {git_metadata()}' + +for o in config['outputs']: + with open(o, "w") as of: + of.write(f"// {metadata}\n\n") + of.write(''.join(lines_to_write)) + of.write('\n') + of.write('export function registerComponents() {') + of.write('\n') + of.write(''.join(register_lines)) + of.write('}') diff --git a/web-components-reuse/components/src/asset-element.js b/web-components-reuse/components/src/asset-element.js new file mode 100644 index 00000000..ea29fab0 --- /dev/null +++ b/web-components-reuse/components/src/asset-element.js @@ -0,0 +1,64 @@ +import { formatMoney, BaseHTMLElement } from './base.js'; + +class AssetElement extends BaseHTMLElement { + + /** + * Supported attributes + * {string} id: asset id + * {string} name: asset name + * {number} market-size + * {number} previous-market-size + * {string} denomination + * {string} class: additional class to append to the root div + */ + connectedCallback() { + this.#render(); + } + + attributeChangedCallback(name, oldValue, newValue) { + this.#render(); + } + + #render() { + const [id, name, marketSize, previousMarketSize, denomination] = [this.getAttribute("id"), this.getAttribute("name"), + this.getAttribute("market-size"), this.getAttribute("previous-market-size"), this.getAttribute("denomination")]; + if (!id || !name) { + return; + } + const classesToAppend = this.getAttribute("class"); + + let previousMarketSizeComponent; + if (previousMarketSize && previousMarketSize != marketSize) { + const previousMarketSizeInt = parseInt(previousMarketSize); + const currentMarketSizeInt = parseInt(marketSize); + const marketIsUp = currentMarketSizeInt > previousMarketSizeInt; + let marketPercentageDiff; + if (marketIsUp) { + marketPercentageDiff = Math.round((currentMarketSizeInt - previousMarketSizeInt) * 100 * 100 / previousMarketSizeInt) / 100.0; + } else { + marketPercentageDiff = Math.round((previousMarketSizeInt - currentMarketSizeInt) * 100 * 100 / currentMarketSizeInt) / 100.0; + } + previousMarketSizeComponent = ` +
+ ${this.translation("previous-market-size-label")}:${formatMoney(previousMarketSize, denomination)} +
+

${marketIsUp ? this.translation('up-by-info') : this.translation('down-by-info')} ${marketPercentageDiff}%

`; + } else { + previousMarketSizeComponent = ``; + } + + this.innerHTML = ` +
+

${name}

+
+ ${this.translation('market-size-label')}:${formatMoney(marketSize, denomination)} +
+ ${previousMarketSizeComponent} +
+ `; + } +} + +export function register() { + customElements.define("asset-element", AssetElement); +} \ No newline at end of file diff --git a/web-components-reuse/components/src/assets-and-currencies.js b/web-components-reuse/components/src/assets-and-currencies.js new file mode 100644 index 00000000..9fb932e9 --- /dev/null +++ b/web-components-reuse/components/src/assets-and-currencies.js @@ -0,0 +1,146 @@ +import { BaseHTMLElement } from "./base.js"; + +/** +* @typedef {Object} AssetOrCurrencyElement +* @property {string} id +* @property {string} name +* @property {number} marketSize +* @property {string} denomination +*/ + +class AssetsAndCurrencies extends BaseHTMLElement { + + #assets = []; + #assetsValueChangeReason = null; + #currencies = []; + #denomination = "USD"; + #assetsContainer = null; + #currenciesContainer = null; + + /** @type {AssetOrCurrencyElement[]} */ + set assets(value) { + this.#assets = value; + this.#renderAssets(); + } + + set assetsValueChangeReason(value) { + this.#assetsValueChangeReason = value; + this.#renderAssets(); + } + + /** @type {AssetOrCurrencyElement[]} */ + set currencies(value) { + this.#currencies = value; + this.#renderCurrencies(); + } + + /** @type {string} */ + set denomination(value) { + this.#denomination = value; + this.#renderAssets(); + this.#renderCurrencies(); + } + + connectedCallback() { + this.innerHTML = ` +
+ +
+ ${this.translation('assets-header')} + ${this.translation('currencies-header')} +
+
+
+ ${this.#assetsHTML()} +
+
+ ${this.#currenciesHTML()} +
+
+
+
`; + + const tabsBody = this.querySelector("[data-tabs-body]"); + this.#assetsContainer = tabsBody.children[0]; + this.#currenciesContainer = tabsBody.children[1]; + } + + #assetsHTML(previousAssetElements = []) { + return this.#assets.map(a => { + const previousAsset = previousAssetElements.find(pa => pa.id == a.id); + let previousMarketSize; + if (!previousAsset) { + previousMarketSize = a.marketSize; + } else { + const previousAssetDenomination = previousAsset.getAttribute("denomination"); + const previousAssetMarketSize = previousAsset.getAttribute("market-size"); + // if denomination has changed, comparing current market size with the previous is meaningless + if (previousAssetDenomination != a.denomination) { + previousMarketSize = a.marketSize; + } else if (previousAssetMarketSize != a.marketSize) { + previousMarketSize = previousAssetMarketSize; + } else { + previousMarketSize = previousAsset.getAttribute("previous-market-size"); + } + } + + return ` + `; + }).join("\n"); + } + + #currenciesHTML(previousCurrencyElements = []) { + return this.#currencies.map(c => { + const previousCurrency = previousCurrencyElements.find(pc => pc.id == c.id); + let previousMarketSize; + if (!previousCurrency) { + previousMarketSize = c.marketSize; + } else { + const previousCurrencyDenomination = previousCurrency.getAttribute("denomination"); + const previousCurrencyMarketSize = previousCurrency.getAttribute("market-size"); + // if denomination has changed, comparing current market size with the previous is meaningless + if (previousCurrencyDenomination != c.denomination) { + previousMarketSize = c.marketSize; + } else if (previousCurrencyMarketSize != c.marketSize) { + previousMarketSize = previousCurrencyMarketSize; + } else { + previousMarketSize = previousCurrency.getAttribute("previous-market-size"); + } + } + + return ` + `}) + .join("\n"); + } + + #renderAssets() { + if (this.#assetsContainer) { + const currentAssetElements = [...this.querySelectorAll("asset-element")]; + this.#assetsContainer.innerHTML = this.#assetsHTML(currentAssetElements); + } + } + + #renderCurrencies() { + if (this.#currenciesContainer) { + const currentCurrencyElements = [...this.querySelectorAll("currency-element")]; + this.#currenciesContainer.innerHTML = this.#currenciesHTML(currentCurrencyElements); + } + } +} + +export function register() { + customElements.define('assets-and-currencies', AssetsAndCurrencies); +} \ No newline at end of file diff --git a/web-components-reuse/components/src/base.js b/web-components-reuse/components/src/base.js new file mode 100644 index 00000000..861e2810 --- /dev/null +++ b/web-components-reuse/components/src/base.js @@ -0,0 +1,63 @@ +// Common types definition +/** +* @typedef {Object} AssetOrCurrency +* @property {string} name +* @property {number} marketSize +*/ + +export function formatMoney(value, denomination) { + const zeros = value.length; + if (zeros > 15) { + return `${value.substring(0, zeros - 15)} ${value.substring(zeros - 15, zeros - 12)} ${value.substring(zeros - 12, zeros - 9)} ${value.substring(zeros - 9, zeros - 6)} ${value.substring(zeros - 6, zeros - 3)} ${value.substring(zeros - 3)} ${denomination}`; + } + if (zeros > 12) { + return `${value.substring(0, zeros - 12)} ${value.substring(zeros - 12, zeros - 9)} ${value.substring(zeros - 9, zeros - 6)} ${value.substring(zeros - 6, zeros - 3)} ${value.substring(zeros - 3)} ${denomination}`; + } + if (zeros > 9) { + return `${value.substring(0, zeros - 9)} ${value.substring(zeros - 9, zeros - 6)} ${value.substring(zeros - 6, zeros - 3)} ${value.substring(zeros - 3)} ${denomination}`; + } + if (zeros > 6) { + return `${value.substring(0, zeros - 6)} ${value.substring(zeros - 6, zeros - 3)} ${value.substring(zeros - 3)} ${denomination}`; + } + return `${value} ${denomination}`; +} + +export class BaseHTMLElement extends HTMLElement { + + _t = null; + _tNamespace = ''; + + set t(value) { + this._t = value; + // TODO: shouldn't all components be re-renderable and called from here? + } + + set tNamespace(value) { + this._tNamespace = value; + } + + translation(key) { + const namespacedKey = (this.getAttribute("t-namespace") ?? this._tNamespace) + key; + const attributeTranslation = this.getAttribute(`t-${namespacedKey}`); + if (attributeTranslation != undefined) { + return attributeTranslation; + } + return this._t ? this._t(namespacedKey) : null; + } + + translationAttribute(key) { + const translation = this.translation(key); + if (translation) { + return `t-${key}="${translation}"`; + } + return ""; + } + + translationAttributeRemovingNamespace(key, namespace) { + const translation = this.translation(namespace + key); + if (translation) { + return `t-${key}="${translation}"`; + } + return ""; + } +} \ No newline at end of file diff --git a/web-components-reuse/components/src/currency-element.js b/web-components-reuse/components/src/currency-element.js new file mode 100644 index 00000000..546c32ae --- /dev/null +++ b/web-components-reuse/components/src/currency-element.js @@ -0,0 +1,65 @@ +import { formatMoney, BaseHTMLElement } from './base.js'; + +class CurrencyElement extends BaseHTMLElement { + + /** + * Supported attributes + * {string} id: currency id + * {string} name: currency name + * {number} market-size + * {number} previous-market-size + * {string} denomination + * {string} class: additional class to append to the root div + */ + connectedCallback() { + this._render(); + } + + attributeChangedCallback(name, oldValue, newValue) { + this._render(); + } + + _render() { + const [id, name, marketSize, previousMarketSize, denomination] = [this.getAttribute("id"), this.getAttribute("name"), + this.getAttribute("market-size"), this.getAttribute("previous-market-size"), this.getAttribute("denomination") + ]; + if (id == undefined || name == undefined) { + return; + } + const classesToAppend = this.getAttribute("class"); + + let previousMarketSizeComponent; + if (previousMarketSize && previousMarketSize != marketSize) { + const previousMarketSizeInt = parseInt(previousMarketSize); + const currentMarketSizeInt = parseInt(marketSize); + const marketIsUp = currentMarketSizeInt > previousMarketSizeInt; + let marketPercentageDiff; + if (marketIsUp) { + marketPercentageDiff = Math.round((currentMarketSizeInt - previousMarketSizeInt) * 100 * 100 / previousMarketSizeInt) / 100.0; + } else { + marketPercentageDiff = Math.round((previousMarketSizeInt - currentMarketSizeInt) * 100 * 100 / currentMarketSizeInt) / 100.0; + } + previousMarketSizeComponent = ` +

${marketIsUp ? this.translation('up-by-info') : this.translation('down-by-info')} ${marketPercentageDiff}%

`; + } else { + previousMarketSizeComponent = ``; + } + + this.innerHTML = ` +
+

${name}

+
+ ${this.translation('daily-turnover-label')}:${formatMoney(marketSize, denomination)} +
+
+ ${this.translation('yearly-turnover-label')}:${formatMoney(`${365 * parseInt(marketSize)}`, denomination)} +
+ ${previousMarketSizeComponent} +
+ `; + } +} + +export function register() { + customElements.define("currency-element", CurrencyElement); +} \ No newline at end of file diff --git a/web-components-reuse/components/src/drop-down.js b/web-components-reuse/components/src/drop-down.js new file mode 100644 index 00000000..cac1f9cd --- /dev/null +++ b/web-components-reuse/components/src/drop-down.js @@ -0,0 +1,31 @@ +class DropDownContainer extends HTMLElement { + + connectedCallback() { + const optionsZIndex = this.getAttribute("options-z-index") ?? '99'; + const anchor = this.querySelector('[data-drop-down-anchor]') ?? this; + anchor.style = "position: relative; display: inline-block"; + + const options = this.querySelector("[data-drop-down-options]"); + if (!options) { + throw new Error("Options must be defined and marked with data-drop-down-options attribute!"); + } + options.style = `position: absolute; z-index: ${optionsZIndex}`; + options.classList.add("hidden"); + + anchor.onclick = (e) => { + // Do not hide other, opened DropDowns + e.stopPropagation(); + options.classList.toggle("hidden"); + }; + + window.addEventListener("click", e => { + if (e.target != anchor && e.target.parentNode != anchor) { + options.classList.add("hidden"); + } + }); + } +} + +export function register() { + customElements.define("drop-down-container", DropDownContainer); +} \ No newline at end of file diff --git a/web-components-reuse/components/src/error-modal.js b/web-components-reuse/components/src/error-modal.js new file mode 100644 index 00000000..0baeb34e --- /dev/null +++ b/web-components-reuse/components/src/error-modal.js @@ -0,0 +1,25 @@ +class ErrorModal extends HTMLElement { + + #modal = null; + #onShowHandler = null; + + connectedCallback() { + this.innerHTML = ``; + + this.#modal = this.querySelector("info-modal"); + this.#onShowHandler = (e) => { + const { title, error } = e.detail; + this.#modal.show({ title, content: error }); + }; + + document.addEventListener('em.show', this.#onShowHandler); + } + + disconnectedCallback() { + document.removeEventListener('em.show', this.#onShowHandler); + } +} + +export function register() { + customElements.define('error-modal', ErrorModal); +} \ No newline at end of file diff --git a/web-components-reuse/components/src/info-modal.js b/web-components-reuse/components/src/info-modal.js new file mode 100644 index 00000000..4abb15b8 --- /dev/null +++ b/web-components-reuse/components/src/info-modal.js @@ -0,0 +1,60 @@ +class InfoModal extends HTMLElement { + + #container = null; + #closeButton = null; + #titleElement = null; + #contentElement = null; + + connectedCallback() { + const titleClassToAppend = this.getAttribute('title-class'); + const title = this.getAttribute("title"); + const content = this.getAttribute("content"); + + let titleClass = "text-xl font-bold p-4"; + if (titleClassToAppend) { + titleClass = titleClass + " " + titleClassToAppend; + } + + this.innerHTML = ` + + `; + + this.#container = this.querySelector('[data-container]'); + this.#closeButton = this.querySelector('[data-close-button]'); + this.#titleElement = this.querySelector('[data-title-element]'); + this.#contentElement = this.querySelector('[data-content-element]'); + + this.#closeButton.onclick = () => this.close(); + + this.#container.addEventListener("click", e => { + if (e.target == this.#container) { + this.close(); + } + }); + } + + show({ title, content }) { + if (title) { + this.#titleElement.textContent = title; + } + if (content) { + this.#contentElement.textContent = content; + } + this.#container.style.display = "block"; + } + + close() { + this.#container.style.display = "none"; + this.dispatchEvent(new CustomEvent('im.closed', { bubbles: true })); + } +} + +export function register() { + customElements.define('info-modal', InfoModal); +} \ No newline at end of file diff --git a/web-components-reuse/components/src/markets-comparator.js b/web-components-reuse/components/src/markets-comparator.js new file mode 100644 index 00000000..e4d55e68 --- /dev/null +++ b/web-components-reuse/components/src/markets-comparator.js @@ -0,0 +1,290 @@ +import { BaseHTMLElement, AssetOrCurrency } from "./base.js"; + +class MarketsComparator extends BaseHTMLElement { + + static observedAttributes = ["asset-items", "currency-items"]; + + _fromMarketsComparatorInput = null; + _toMarketsComparatorInput = null; + _comparisonElement = null; + + _fromMarketSize = null; + _toMarketSize = null; + + /** @type {AssetOrCurrency[]} */ + set assets(value) { + if (this._fromMarketsComparatorInput) { + this._setValuesUsingAttributes(this._fromMarketsComparatorInput, value, "asset"); + } + if (this._toMarketsComparatorInput) { + this._setValuesUsingAttributes(this._toMarketsComparatorInput, value, "asset"); + } + } + + /** @type {AssetOrCurrency[]} */ + set currencies(value) { + if (this._fromMarketsComparatorInput) { + this._setValuesUsingAttributes(this._fromMarketsComparatorInput, value, "currency"); + } + if (this._toMarketsComparatorInput) { + this._setValuesUsingAttributes(this._toMarketsComparatorInput, value, "currency"); + } + } + + _setValuesUsingAttributes(element, values, prefix) { + for (let i = 0; i < values.length; i++) { + element.setAttribute(`${prefix}-${i}-name`, values[i].name); + element.setAttribute(`${prefix}-${i}-market-size`, values[i].marketSize); + } + element.setAttribute(`${prefix}-items`, values.length); + } + + connectedCallback() { + this._render(); + + this._chosenMarketSizeChangedEventHandler = e => { + const { name, marketSize } = e.detail; + if (e.target === this._fromMarketsComparatorInput) { + this._fromMarketSize = marketSize; + this._renderComparisonElementHTML(); + this.dispatchEvent(new CustomEvent("mc.from-market-size-changed", { + bubbles: true, + detail: { name, marketSize } + })); + } else if (e.target === this._toMarketsComparatorInput) { + this._toMarketSize = marketSize; + this._renderComparisonElementHTML(); + this.dispatchEvent(new CustomEvent("mc.to-market-size-changed", { + bubbles: true, + detail: { name, marketSize } + })); + } + } + document.addEventListener('mci.chosen-market-size-changed', this._chosenMarketSizeChangedEventHandler); + } + + disconnectedCallback() { + document.removeEventListener('mci.chosen-market-size-changed', this._chosenMarketSizeChangedEventHandler); + } + + attributeChangedCallback(name, oldValue, newValue) { + if (name == 'asset-items') { + this.assets = assetsFromAttributes(this); + } else if (name == 'currency-items') { + this.currencies = currenciesFromAttributes(this); + } + } + + _render() { + this.innerHTML = ` +
+ + +
${this.translation('markets-to')}
+ + +
${this._comparisonElementHTML()}
+
+ `; + + [this._fromMarketsComparatorInput, this._toMarketsComparatorInput] = this.querySelectorAll("markets-comparator-input"); + this._comparisonElement = this.querySelector('[data-comparison-element]'); + } + + _comparisonElementHTML() { + if (!this._fromMarketSize || !this._toMarketSize) { + return '-'; + } + return `${this._fromMarketSize.toExponential(3)} / ${this._toMarketSize.toExponential(3)} = ${this._chosenMarketsComparedValue()}`; + } + + _chosenMarketsComparedValue() { + if (!this._fromMarketSize || !this._toMarketSize) { + return 0; + } + return Math.round(this._fromMarketSize * 1000 / this._toMarketSize) / 1000.0; + } + + _renderComparisonElementHTML() { + this._comparisonElement.innerHTML = this._comparisonElementHTML(); + } +} + +function assetsFromAttributes(element) { + const countAttribute = element.getAttribute("asset-items"); + if (!countAttribute) { + return []; + } + const assets = []; + for (let i = 0; i < parseInt(countAttribute); i++) { + const name = element.getAttribute(`asset-${i}-name`); + const marketSize = element.getAttribute(`asset-${i}-market-size`); + if (name && marketSize) { + assets.push({ name, marketSize: parseInt(marketSize) }); + } + } + return assets; +} + +function currenciesFromAttributes(element) { + const countAttribute = element.getAttribute("currency-items"); + if (!countAttribute) { + return []; + } + const currencies = []; + for (let i = 0; i < parseInt(countAttribute); i++) { + const name = element.getAttribute(`currency-${i}-name`); + const marketSize = element.getAttribute(`currency-${i}-market-size`); + if (name && marketSize) { + currencies.push({ name, marketSize: parseInt(marketSize) }); + } + } + return currencies; +} + +class MarketsComparatorInput extends BaseHTMLElement { + + static observedAttributes = ["asset-items", "currency-items"]; + + /** @type {AssetOrCurrency[]} */ + _assets = []; + /** @type {AssetOrCurrency[]} */ + _currencies = []; + /** @type {AssetOrCurrency[]} */ + _assetOrCurrencyOptions = []; + + set assets(value) { + this._assets = value; + this._recalculateAssetOrCurrencyOptions(); + } + + set currencies(value) { + this._currencies = value; + this._recalculateAssetOrCurrencyOptions(); + } + + _assetOrCurrencyInput = null; + _curencyTurnoverInputMultiplier = 1; + + connectedCallback() { + this._render(); + } + + attributeChangedCallback(name, oldValue, newValue) { + if (name == 'asset-items') { + this.assets = assetsFromAttributes(this); + } else if (name == 'currency-items') { + this.currencies = currenciesFromAttributes(this); + } + } + + _render() { + const optionsZIndex = this.getAttribute("options-z-index") ?? '99'; + this.innerHTML = ` + +
+ ${this._dropDownHeaderHTML()} +
+
    + ${this._optionsHTML()} +
+
+ `; + + this._setOptionsClickHandlers(); + + this.querySelector('[data-drop-down-header]') + .querySelector("input")?.addEventListener("input", e => { + this._curencyTurnoverInputMultiplier = e.target.value; + this._calculateChosenMarketSizeChange(); + }); + } + + _optionsHTML() { + return this._assetOrCurrencyOptions.map(o => + `
  • ${o.name}
  • `) + .join('\n'); + } + + _dropDownHeaderHTML() { + let marketSizeHTML; + if (this._isAsset()) { + marketSizeHTML = `${this.translation('market-size-input-label')} + + ${this.translation('days-turnover-input-label')} + `; + } else { + marketSizeHTML = ``; + } + return ` + ${this._assetOrCurrencyInput ?? this.translation('asset-or-currency-input-placeholder')} + ${marketSizeHTML} + `; + } + + _setOptionsClickHandlers() { + [...this.querySelectorAll("li")].forEach(o => { + o.onclick = () => { + this._assetOrCurrencyInput = o.getAttribute("data-option-id"); + this._calculateChosenMarketSizeChange(); + this._render(); + }; + }); + } + + _renderOptionsHTML() { + const optionsContainer = this.querySelector("ul"); + if (optionsContainer) { + optionsContainer.innerHTML = this._optionsHTML(); + this._setOptionsClickHandlers(); + } + } + + _isAsset() { + return this._assetOrCurrencyInput ? this._assets.find(a => a.name == this._assetOrCurrencyInput) : false; + } + + _isCurrency() { + return this._assetOrCurrencyInput ? this._currencies.find(c => c.name == this._assetOrCurrencyInput) : false; + } + + _recalculateAssetOrCurrencyOptions() { + const assetOrCurrencyOptions = []; + this._assets.forEach(a => assetOrCurrencyOptions.push(a)); + this._currencies.forEach(c => assetOrCurrencyOptions.push(c)); + this._assetOrCurrencyOptions = assetOrCurrencyOptions; + + this._renderOptionsHTML(); + this._calculateChosenMarketSizeChange(); + } + + _calculateChosenMarketSizeChange() { + if (!this._assetOrCurrencyInput) { + return; + } + + const assetInput = this._assets.find(a => a.name == this._assetOrCurrencyInput); + const currencyInput = this._currencies.find(c => c.name == this._assetOrCurrencyInput); + + const inputMarketSize = assetInput ? assetInput.marketSize : currencyInput.marketSize * this._curencyTurnoverInputMultiplier; + + this.dispatchEvent(new CustomEvent("mci.chosen-market-size-changed", { + bubbles: true, + detail: { name: this._assetOrCurrencyInput, marketSize: inputMarketSize } + })); + } +} + +export function register() { + customElements.define("markets-comparator-input", MarketsComparatorInput); + customElements.define('markets-comparator', MarketsComparator); +} \ No newline at end of file diff --git a/web-components-reuse/components/src/markets-header.js b/web-components-reuse/components/src/markets-header.js new file mode 100644 index 00000000..65ee7a1d --- /dev/null +++ b/web-components-reuse/components/src/markets-header.js @@ -0,0 +1,78 @@ +import { BaseHTMLElement } from "./base.js"; + +class MarketsHeader extends BaseHTMLElement { + + _denomination = 'USD'; + _liveUpdatesEnabled = true; + _denominationExchangeRates = []; + _liveUpdatesEnabledElement = null; + _denominationElement = null; + + + set denomination(value) { + this._denomination = value; + if (this._denominationElement) { + this._denominationElement.textContent = this._denomination; + } + } + + set denominationExchangeRates(value) { + this._denominationExchangeRates = value; + this._renderDenominationOptions(); + } + + connectedCallback() { + this.innerHTML = ` +
    +
    ${this.translation('live-updates')} ${this._liveUpdatesElementText()} +
    + ${this.translation('markets-in')} + + ${this._denomination} +
      + ${this._denominationOptionsHTML()} +
    +
    +
    + `; + + this._liveUpdatesEnabledElement = this.querySelector("span"); + this._liveUpdatesEnabledElement.onclick = () => { + this._liveUpdatesEnabled = !this._liveUpdatesEnabled; + this._liveUpdatesEnabledElement.textContent = this._liveUpdatesElementText(); + this.dispatchEvent(new CustomEvent('mh.live-updates-toggled', { bubbles: true, detail: this._liveUpdatesEnabled })); + }; + + this._denominationElement = this.querySelector("drop-down-container > span"); + } + + _denominationOptionsHTML() { + return this._denominationExchangeRates.map(der => `
  • ${der.name}: ${der.exchangeRate}
  • `).join('\n'); + } + + _renderDenominationOptions() { + const optionsContainer = this.querySelector("ul"); + if (optionsContainer) { + optionsContainer.innerHTML = this._denominationOptionsHTML(); + this._setOptionsClickHandlers(); + } + } + + _setOptionsClickHandlers() { + [...this.querySelectorAll("li")].forEach(o => { + o.onclick = () => { + this._denomination = o.getAttribute("data-option-id"); + this._denominationElement.textContent = this._denomination; + this.dispatchEvent(new CustomEvent('mh.denomination-changed', { bubbles: true, detail: this._denomination })); + }; + }); + } + + _liveUpdatesElementText() { + return `${this._liveUpdatesEnabled ? this.translation('live-updates-on') : this.translation('live-updates-off')}`; + } +} + +export function register() { + customElements.define("markets-header", MarketsHeader); +} \ No newline at end of file diff --git a/web-components-reuse/components/src/markets-projections.js b/web-components-reuse/components/src/markets-projections.js new file mode 100644 index 00000000..3b288af6 --- /dev/null +++ b/web-components-reuse/components/src/markets-projections.js @@ -0,0 +1,66 @@ +import { BaseHTMLElement, AssetOrCurrency } from "./base.js"; + +class MarketsProjections extends BaseHTMLElement { + + _marketsComparatorComponent = null; + _projectionsCalculatorComponent = null; + + /** @type {?AssetOrCurrency} */ + _fromAssetOrCurrency = null; + /** @type {?AssetOrCurrency} */ + _toAssetOrCurrency = null; + + /** @type {AssetOrCurrency[]} */ + set assets(value) { + if (this._marketsComparatorComponent) { + this._marketsComparatorComponent.assets = value; + } + } + + /** @type {AssetOrCurrency[]} */ + set currencies(value) { + if (this._marketsComparatorComponent) { + this._marketsComparatorComponent.currencies = value; + } + } + + connectedCallback() { + this.innerHTML = ` +
    +

    ${this.translation('projections-header')}

    + + + + +
    + `; + + this._marketsComparatorComponent = this.querySelector("markets-comparator"); + this._projectionsCalculatorComponent = this.querySelector("projections-calculator"); + + const fromMarketSizeChangedEventHandler = e => { + this._fromAssetOrCurrency = e.detail; + this._projectionsCalculatorComponent.assetOrCurrency1 = this._fromAssetOrCurrency; + }; + const toMarketSizeChangedEventHandler = e => { + this._toAssetOrCurrency = e.detail; + this._projectionsCalculatorComponent.assetOrCurrency2 = this._toAssetOrCurrency; + }; + + this.addEventListener('mc.from-market-size-changed', fromMarketSizeChangedEventHandler); + this.addEventListener('mc.to-market-size-changed', toMarketSizeChangedEventHandler); + } +} + +export function register() { + customElements.define('markets-projections', MarketsProjections); +} \ No newline at end of file diff --git a/web-components-reuse/components/src/projections-calculator.js b/web-components-reuse/components/src/projections-calculator.js new file mode 100644 index 00000000..43474492 --- /dev/null +++ b/web-components-reuse/components/src/projections-calculator.js @@ -0,0 +1,287 @@ +import { BaseHTMLElement, AssetOrCurrency } from "./base.js"; + +/** +* @typedef {Object} AssetOrCurrencyProjection +* @property {number} marketSize +* @property {number} growthRate +*/ + +class ProjectionsCalculator extends BaseHTMLElement { + + /** @type {?AssetOrCurrency} */ + _assetOrCurrency1 = null; + /** @type {?AssetOrCurrency} */ + _assetOrCurrency2 = null; + /** @type {?number} */ + _assetOrCurrency1ExpectedGrowthRate = null; + /** @type {?number} */ + _assetOrCurrency2ExpectedGrowthRate = null; + /** @type {?number} */ + _customProjectionYears = null; + + set assetOrCurrency1(value) { + this._assetOrCurrency1 = value; + this._renderProjectionsResultsHTML(); + } + + set assetOrCurrency2(value) { + this._assetOrCurrency2 = value; + this._renderProjectionsResultsHTML(); + } + + _assetOrCurrency1Header = null; + _assetOrCurrency1Input = null; + _assetOrCurrency2Header = null; + _assetOrCurrency2Input = null; + _customProjectionInput = null; + _projectionsResultsContainer = null; + _customProjectionContainer = null; + _customProjectionTextElement = null; + _customProjectionResultContainer = null; + + connectedCallback() { + this.innerHTML = ` +
    +
    ${this._assetOrCurrencyHeaderText(this._assetOrCurrency1)}
    + +
    ${this._assetOrCurrencyHeaderText(this._assetOrCurrency2)}
    + +
    + ${this._projectionsResultsHTML()} +
    +
    + ${this._customProjectionHTML()} +
    +
    + `; + + const container = this.querySelector("div"); + + const divs = container.querySelectorAll("div"); + [this._assetOrCurrency1Header, this._assetOrCurrency2Header, this._projectionsResultsContainer] = divs; + this._customProjectionContainer = divs[divs.length - 1]; + + [this._assetOrCurrency1Input, this._assetOrCurrency2Input, + this._customProjectionInput] = container.querySelectorAll("input"); + + this._assetOrCurrency1Input.addEventListener("input", e => { + this._assetOrCurrency1ExpectedGrowthRate = parseInt(e.target.value); + this._renderProjectionsResultsHTML(); + }); + this._assetOrCurrency2Input.addEventListener("input", e => { + this._assetOrCurrency2ExpectedGrowthRate = parseInt(e.target.value); + this._renderProjectionsResultsHTML(); + }); + this._customProjectionInput.addEventListener("input", e => { + this._customProjectionYears = parseInt(e.target.value); + this._updateCustomProjectionText(); + this._updateCustomProjectionResult(); + }); + + this._customProjectionTextElement = this.querySelector('[data-custom-projection-text-element]'); + this._customProjectionResultContainer = this.querySelector('[data-custom-projection-result-container]'); + } + + _assetOrCurrencyHeaderText(assetOrCurrency) { + return `${assetOrCurrency ? assetOrCurrency.name : this.translation('asset-or-currency-placeholder')} ${this.translation('asset-or-currency-expected-annual-growth-rate')}:`; + } + + _renderProjectionsResultsHTML() { + if (this._projectionsResultsContainer && this._customProjectionContainer) { + this._projectionsResultsContainer.innerHTML = this._projectionsResultsHTML(); + this._updateCustomProjectionResult(); + } + } + + _projectionsResultsHTML() { + const inYearsText = (years) => `${this.translation('results-in-header')} ${years} ${this.translation(years == 1 ? 'year' : 'years')}`; + const currentYear = new Date().getFullYear(); + return [1, 5, 10].map(y => ` +
    ${inYearsText(y)} (${currentYear + y}):
    + ${this._projectionsResultHTML(y)}`) + .join('\n'); + } + + _projectionsResultHTML(years) { + const ac1 = this._assetOrCurrency1WithExpectedGrowthRate(); + const ac2 = this._assetOrCurrency2WithExpectedGrowthRate(); + if (years != null && ac1 != null && ac2 != null) { + return ` + + `; + } + return '
    -
    '; + } + + _customProjectionHTML() { + return ` + ${this.translation('results-in-header')} + ${this._customProjectionYearText()}: +
    ${this._projectionsResultHTML(this._customProjectionYears)}
    + `; + } + + _customProjectionYearText() { + const currentYear = new Date().getFullYear(); + return '(' + (this._customProjectionYears == null || Number.isNaN(this._customProjectionYears) ? + `${currentYear} + N` : (currentYear + this._customProjectionYears)) + ')'; + } + + _updateCustomProjectionText() { + this._customProjectionTextElement.textContent = this._customProjectionYearText(); + } + + _updateCustomProjectionResult() { + this._customProjectionContainer.innerHTML = this._projectionsResultHTML(this._customProjectionYears); + } + + _assetOrCurrency1WithExpectedGrowthRate() { + if (this._assetOrCurrency1 && this._assetOrCurrency1ExpectedGrowthRate != null) { + return { marketSize: this._assetOrCurrency1.marketSize, growthRate: this._assetOrCurrency1ExpectedGrowthRate }; + } + return null; + } + + _assetOrCurrency2WithExpectedGrowthRate() { + if (this._assetOrCurrency2 && this._assetOrCurrency2ExpectedGrowthRate != null) { + return { marketSize: this._assetOrCurrency2.marketSize, growthRate: this._assetOrCurrency2ExpectedGrowthRate }; + } + return null; + } +} + +class ProjectionsResult extends HTMLElement { + + static observedAttributes = [ + "years", + "asset-or-currency-1-market-size", "asset-or-currency-1-growth-rate", + "asset-or-currency-2-market-size", "asset-or-currency-2-growth-rate" + ]; + + /** @type {number} */ + _years = 1; + /** @type {?AssetOrCurrencyProjection} */ + _assetOrCurrency1 = null; + /** @type {?AssetOrCurrencyProjection} */ + _assetOrCurrency2 = null; + + set years(value) { + this._years = value; + this._render(); + } + + set assetOrCurrency1(value) { + this._assetOrCurrency1 = value; + this._render(); + } + + set assetOrCurrency2(value) { + this._assetOrCurrency2 = value; + this._render(); + } + + connectedCallback() { + this._render(); + } + + attributeChangedCallback(name, oldValue, newValue) { + if (name.includes("asset-or-currency-1")) { + const assetOrCurrency = this._assetOrCurrencyFromAttributes("asset-or-currency-1"); + if (assetOrCurrency) { + this.assetOrCurrency1 = assetOrCurrency; + } + } else if (name.includes("asset-or-currency-2")) { + const assetOrCurrency = this._assetOrCurrencyFromAttributes("asset-or-currency-2"); + if (assetOrCurrency) { + this.assetOrCurrency2 = assetOrCurrency; + } + } else if (name == 'years') { + this.years = parseInt(newValue); + } + } + + _assetOrCurrencyFromAttributes(prefix) { + const marketSize = this.getAttribute(`${prefix}-market-size`); + const growthRate = this.getAttribute(`${prefix}-growth-rate`); + if (marketSize && growthRate) { + return { marketSize: parseInt(marketSize), growthRate: parseInt(growthRate) }; + } + return null; + } + + _render() { + const nominator = this._exponentialNumberString(this._projectionNumerator()); + const denominator = this._exponentialNumberString(this._projectionDenominator()); + this.innerHTML = ` +
    ${nominator} / ${denominator} = ${this._projection()} +
    + `; + } + + _exponentialNumberString(n) { + return n?.toExponential(3) ?? ''; + } + + _marketSizeChangedByRate(marketSize, growthRate, decrease = false) { + let changedMarketSize; + if (decrease) { + changedMarketSize = marketSize - (marketSize * growthRate / 100.0); + } else { + changedMarketSize = marketSize + (marketSize * growthRate / 100.0); + } + if (changedMarketSize <= 0 || Number.isNaN(changedMarketSize)) { + return null; + } + return changedMarketSize; + } + + _marketSizeChangedByRateInGivenYears(marketSize, growthRate, years) { + const negativeYears = years < 0; + let increasedMarketSize = marketSize; + for (let i = 0; i < Math.abs(years); i++) { + increasedMarketSize = this._marketSizeChangedByRate(increasedMarketSize, growthRate, negativeYears); + if (!increasedMarketSize) { + return null; + } + } + return increasedMarketSize; + } + + _projectionNumerator() { + if (this._assetOrCurrency1) { + return this._marketSizeChangedByRateInGivenYears( + this._assetOrCurrency1.marketSize, + this._assetOrCurrency1.growthRate, + this._years); + } + return null; + } + + _projectionDenominator() { + if (this._assetOrCurrency2) { + return this._marketSizeChangedByRateInGivenYears( + this._assetOrCurrency2.marketSize, + this._assetOrCurrency2.growthRate, + this._years); + } + return null; + } + + _projection() { + const numerator = this._projectionNumerator(); + const denominator = this._projectionDenominator(); + if (numerator && denominator) { + return Math.round(numerator * 1000 / denominator) / 1000.0; + } + return ''; + } +} + +export function register() { + customElements.define("projections-result", ProjectionsResult); + customElements.define("projections-calculator", ProjectionsCalculator); +} \ No newline at end of file diff --git a/web-components-reuse/components/src/tabs-container.js b/web-components-reuse/components/src/tabs-container.js new file mode 100644 index 00000000..7f7ccc2a --- /dev/null +++ b/web-components-reuse/components/src/tabs-container.js @@ -0,0 +1,64 @@ +class TabsContainer extends HTMLElement { + + _activeTab = 0; + set activeTab(value) { + this._activeTab = value; + this._updateActiveTab(); + } + + activeTabClass = "underline"; + + connectedCallback() { + this._tabsHeader = this.querySelector("[data-tabs-header]"); + this._tabsBody = this.querySelector("[data-tabs-body]"); + this.activeTabClass = this.getAttribute("active-tab-class") ?? this.activeTabClass; + if (!this._tabsHeader) { + throw new Error("Tabs header must be defined and marked with data-tabs-header attribute!"); + } + if (!this._tabsBody) { + throw new Error("Tabs body must be defined and marked with data-tabs-body attribute!"); + } + + [...this._tabsHeader.children].forEach((tab, i) => { + tab.addEventListener('click', () => this.activeTab = i); + }); + + [...this._tabsBody.children].forEach((tab) => { + tab.classList.add("hidden"); + }); + + this._updateActiveTab(); + } + + _updateActiveTab() { + [...this._tabsHeader.children].forEach((tab, i) => { + if (i == this._activeTab) { + tab.classList.add(this.activeTabClass); + } else { + tab.classList.remove(this.activeTabClass); + } + }); + [...this._tabsBody.children].forEach((tab, i) => { + if (i == this._activeTab) { + tab.classList.remove('hidden'); + } else { + tab.classList.add('hidden'); + } + }); + } +} + +class TabHeader extends HTMLElement { + + connectedCallback() { + this.classList.add("text-2xl"); + this.classList.add("p-2"); + this.classList.add("cursor-pointer"); + this.classList.add("grow"); + } +} + +export function register() { + customElements.define('tabs-container', TabsContainer); + customElements.define('tab-header', TabHeader); +} \ No newline at end of file diff --git a/web-components-reuse/react-app/.env.development b/web-components-reuse/react-app/.env.development new file mode 100644 index 00000000..06b0a439 --- /dev/null +++ b/web-components-reuse/react-app/.env.development @@ -0,0 +1 @@ +VITE_API_BASE_URL="http://localhost:8080/api" \ No newline at end of file diff --git a/web-components-reuse/react-app/.env.production b/web-components-reuse/react-app/.env.production new file mode 100644 index 00000000..06b0a439 --- /dev/null +++ b/web-components-reuse/react-app/.env.production @@ -0,0 +1 @@ +VITE_API_BASE_URL="http://localhost:8080/api" \ No newline at end of file diff --git a/web-components-reuse/react-app/.gitignore b/web-components-reuse/react-app/.gitignore new file mode 100644 index 00000000..a547bf36 --- /dev/null +++ b/web-components-reuse/react-app/.gitignore @@ -0,0 +1,24 @@ +# Logs +logs +*.log +npm-debug.log* +yarn-debug.log* +yarn-error.log* +pnpm-debug.log* +lerna-debug.log* + +node_modules +dist +dist-ssr +*.local + +# Editor directories and files +.vscode/* +!.vscode/extensions.json +.idea +.DS_Store +*.suo +*.ntvs* +*.njsproj +*.sln +*.sw? diff --git a/web-components-reuse/react-app/README.md b/web-components-reuse/react-app/README.md new file mode 100644 index 00000000..d2e77611 --- /dev/null +++ b/web-components-reuse/react-app/README.md @@ -0,0 +1,73 @@ +# React + TypeScript + Vite + +This template provides a minimal setup to get React working in Vite with HMR and some ESLint rules. + +Currently, two official plugins are available: + +- [@vitejs/plugin-react](https://github.com/vitejs/vite-plugin-react/blob/main/packages/plugin-react) uses [Babel](https://babeljs.io/) (or [oxc](https://oxc.rs) when used in [rolldown-vite](https://vite.dev/guide/rolldown)) for Fast Refresh +- [@vitejs/plugin-react-swc](https://github.com/vitejs/vite-plugin-react/blob/main/packages/plugin-react-swc) uses [SWC](https://swc.rs/) for Fast Refresh + +## React Compiler + +The React Compiler is not enabled on this template because of its impact on dev & build performances. To add it, see [this documentation](https://react.dev/learn/react-compiler/installation). + +## Expanding the ESLint configuration + +If you are developing a production application, we recommend updating the configuration to enable type-aware lint rules: + +```js +export default defineConfig([ + globalIgnores(['dist']), + { + files: ['**/*.{ts,tsx}'], + extends: [ + // Other configs... + + // Remove tseslint.configs.recommended and replace with this + tseslint.configs.recommendedTypeChecked, + // Alternatively, use this for stricter rules + tseslint.configs.strictTypeChecked, + // Optionally, add this for stylistic rules + tseslint.configs.stylisticTypeChecked, + + // Other configs... + ], + languageOptions: { + parserOptions: { + project: ['./tsconfig.node.json', './tsconfig.app.json'], + tsconfigRootDir: import.meta.dirname, + }, + // other options... + }, + }, +]) +``` + +You can also install [eslint-plugin-react-x](https://github.com/Rel1cx/eslint-react/tree/main/packages/plugins/eslint-plugin-react-x) and [eslint-plugin-react-dom](https://github.com/Rel1cx/eslint-react/tree/main/packages/plugins/eslint-plugin-react-dom) for React-specific lint rules: + +```js +// eslint.config.js +import reactX from 'eslint-plugin-react-x' +import reactDom from 'eslint-plugin-react-dom' + +export default defineConfig([ + globalIgnores(['dist']), + { + files: ['**/*.{ts,tsx}'], + extends: [ + // Other configs... + // Enable lint rules for React + reactX.configs['recommended-typescript'], + // Enable lint rules for React DOM + reactDom.configs.recommended, + ], + languageOptions: { + parserOptions: { + project: ['./tsconfig.node.json', './tsconfig.app.json'], + tsconfigRootDir: import.meta.dirname, + }, + // other options... + }, + }, +]) +``` diff --git a/web-components-reuse/react-app/eslint.config.js b/web-components-reuse/react-app/eslint.config.js new file mode 100644 index 00000000..b19330b1 --- /dev/null +++ b/web-components-reuse/react-app/eslint.config.js @@ -0,0 +1,23 @@ +import js from '@eslint/js' +import globals from 'globals' +import reactHooks from 'eslint-plugin-react-hooks' +import reactRefresh from 'eslint-plugin-react-refresh' +import tseslint from 'typescript-eslint' +import { defineConfig, globalIgnores } from 'eslint/config' + +export default defineConfig([ + globalIgnores(['dist']), + { + files: ['**/*.{ts,tsx}'], + extends: [ + js.configs.recommended, + tseslint.configs.recommended, + reactHooks.configs['recommended-latest'], + reactRefresh.configs.vite, + ], + languageOptions: { + ecmaVersion: 2020, + globals: globals.browser, + }, + }, +]) diff --git a/web-components-reuse/react-app/index.html b/web-components-reuse/react-app/index.html new file mode 100644 index 00000000..d162c634 --- /dev/null +++ b/web-components-reuse/react-app/index.html @@ -0,0 +1,12 @@ + + + + + + Markets + + +
    + + + diff --git a/web-components-reuse/react-app/package-lock.json b/web-components-reuse/react-app/package-lock.json new file mode 100644 index 00000000..bc7e968c --- /dev/null +++ b/web-components-reuse/react-app/package-lock.json @@ -0,0 +1,4069 @@ +{ + "name": "react-app", + "version": "0.0.0", + "lockfileVersion": 3, + "requires": true, + "packages": { + "": { + "name": "react-app", + "version": "0.0.0", + "dependencies": { + "@tailwindcss/vite": "^4.1.14", + "react": "^19.1.1", + "react-dom": "^19.1.1", + "react-i18next": "^16.0.0", + "tailwindcss": "^4.1.14" + }, + "devDependencies": { + "@eslint/js": "^9.36.0", + "@types/node": "^24.6.0", + "@types/react": "^19.1.16", + "@types/react-dom": "^19.1.9", + "@vitejs/plugin-react": "^5.0.4", + "eslint": "^9.36.0", + "eslint-plugin-react-hooks": "^5.2.0", + "eslint-plugin-react-refresh": "^0.4.22", + "globals": "^16.4.0", + "typescript": "~5.9.3", + "typescript-eslint": "^8.45.0", + "vite": "^7.1.7" + } + }, + "node_modules/@babel/code-frame": { + "version": "7.27.1", + "resolved": "https://registry.npmjs.org/@babel/code-frame/-/code-frame-7.27.1.tgz", + "integrity": "sha512-cjQ7ZlQ0Mv3b47hABuTevyTuYN4i+loJKGeV9flcCgIK37cCXRh+L1bd3iBHlynerhQ7BhCkn2BPbQUL+rGqFg==", + "dev": true, + "license": "MIT", + "dependencies": { + "@babel/helper-validator-identifier": "^7.27.1", + "js-tokens": "^4.0.0", + "picocolors": "^1.1.1" + }, + "engines": { + "node": ">=6.9.0" + } + }, + "node_modules/@babel/compat-data": { + "version": "7.28.4", + "resolved": "https://registry.npmjs.org/@babel/compat-data/-/compat-data-7.28.4.tgz", + "integrity": "sha512-YsmSKC29MJwf0gF8Rjjrg5LQCmyh+j/nD8/eP7f+BeoQTKYqs9RoWbjGOdy0+1Ekr68RJZMUOPVQaQisnIo4Rw==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=6.9.0" + } + }, + "node_modules/@babel/core": { + "version": "7.28.4", + "resolved": "https://registry.npmjs.org/@babel/core/-/core-7.28.4.tgz", + "integrity": "sha512-2BCOP7TN8M+gVDj7/ht3hsaO/B/n5oDbiAyyvnRlNOs+u1o+JWNYTQrmpuNp1/Wq2gcFrI01JAW+paEKDMx/CA==", + "dev": true, + "license": "MIT", + "dependencies": { + "@babel/code-frame": "^7.27.1", + "@babel/generator": "^7.28.3", + "@babel/helper-compilation-targets": "^7.27.2", + "@babel/helper-module-transforms": "^7.28.3", + "@babel/helpers": "^7.28.4", + "@babel/parser": "^7.28.4", + "@babel/template": "^7.27.2", + "@babel/traverse": "^7.28.4", + "@babel/types": "^7.28.4", + "@jridgewell/remapping": "^2.3.5", + "convert-source-map": "^2.0.0", + "debug": "^4.1.0", + "gensync": "^1.0.0-beta.2", + "json5": "^2.2.3", + "semver": "^6.3.1" + }, + "engines": { + "node": ">=6.9.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/babel" + } + }, + "node_modules/@babel/generator": { + "version": "7.28.3", + "resolved": "https://registry.npmjs.org/@babel/generator/-/generator-7.28.3.tgz", + "integrity": "sha512-3lSpxGgvnmZznmBkCRnVREPUFJv2wrv9iAoFDvADJc0ypmdOxdUtcLeBgBJ6zE0PMeTKnxeQzyk0xTBq4Ep7zw==", + "dev": true, + "license": "MIT", + "dependencies": { + "@babel/parser": "^7.28.3", + "@babel/types": "^7.28.2", + "@jridgewell/gen-mapping": "^0.3.12", + "@jridgewell/trace-mapping": "^0.3.28", + "jsesc": "^3.0.2" + }, + "engines": { + "node": ">=6.9.0" + } + }, + "node_modules/@babel/helper-compilation-targets": { + "version": "7.27.2", + "resolved": "https://registry.npmjs.org/@babel/helper-compilation-targets/-/helper-compilation-targets-7.27.2.tgz", + "integrity": "sha512-2+1thGUUWWjLTYTHZWK1n8Yga0ijBz1XAhUXcKy81rd5g6yh7hGqMp45v7cadSbEHc9G3OTv45SyneRN3ps4DQ==", + "dev": true, + "license": "MIT", + "dependencies": { + "@babel/compat-data": "^7.27.2", + "@babel/helper-validator-option": "^7.27.1", + "browserslist": "^4.24.0", + "lru-cache": "^5.1.1", + "semver": "^6.3.1" + }, + "engines": { + "node": ">=6.9.0" + } + }, + "node_modules/@babel/helper-globals": { + "version": "7.28.0", + "resolved": "https://registry.npmjs.org/@babel/helper-globals/-/helper-globals-7.28.0.tgz", + "integrity": "sha512-+W6cISkXFa1jXsDEdYA8HeevQT/FULhxzR99pxphltZcVaugps53THCeiWA8SguxxpSp3gKPiuYfSWopkLQ4hw==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=6.9.0" + } + }, + "node_modules/@babel/helper-module-imports": { + "version": "7.27.1", + "resolved": "https://registry.npmjs.org/@babel/helper-module-imports/-/helper-module-imports-7.27.1.tgz", + "integrity": "sha512-0gSFWUPNXNopqtIPQvlD5WgXYI5GY2kP2cCvoT8kczjbfcfuIljTbcWrulD1CIPIX2gt1wghbDy08yE1p+/r3w==", + "dev": true, + "license": "MIT", + "dependencies": { + "@babel/traverse": "^7.27.1", + "@babel/types": "^7.27.1" + }, + "engines": { + "node": ">=6.9.0" + } + }, + "node_modules/@babel/helper-module-transforms": { + "version": "7.28.3", + "resolved": "https://registry.npmjs.org/@babel/helper-module-transforms/-/helper-module-transforms-7.28.3.tgz", + "integrity": "sha512-gytXUbs8k2sXS9PnQptz5o0QnpLL51SwASIORY6XaBKF88nsOT0Zw9szLqlSGQDP/4TljBAD5y98p2U1fqkdsw==", + "dev": true, + "license": "MIT", + "dependencies": { + "@babel/helper-module-imports": "^7.27.1", + "@babel/helper-validator-identifier": "^7.27.1", + "@babel/traverse": "^7.28.3" + }, + "engines": { + "node": ">=6.9.0" + }, + "peerDependencies": { + "@babel/core": "^7.0.0" + } + }, + "node_modules/@babel/helper-plugin-utils": { + "version": "7.27.1", + "resolved": "https://registry.npmjs.org/@babel/helper-plugin-utils/-/helper-plugin-utils-7.27.1.tgz", + "integrity": "sha512-1gn1Up5YXka3YYAHGKpbideQ5Yjf1tDa9qYcgysz+cNCXukyLl6DjPXhD3VRwSb8c0J9tA4b2+rHEZtc6R0tlw==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=6.9.0" + } + }, + "node_modules/@babel/helper-string-parser": { + "version": "7.27.1", + "resolved": "https://registry.npmjs.org/@babel/helper-string-parser/-/helper-string-parser-7.27.1.tgz", + "integrity": "sha512-qMlSxKbpRlAridDExk92nSobyDdpPijUq2DW6oDnUqd0iOGxmQjyqhMIihI9+zv4LPyZdRje2cavWPbCbWm3eA==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=6.9.0" + } + }, + "node_modules/@babel/helper-validator-identifier": { + "version": "7.27.1", + "resolved": "https://registry.npmjs.org/@babel/helper-validator-identifier/-/helper-validator-identifier-7.27.1.tgz", + "integrity": "sha512-D2hP9eA+Sqx1kBZgzxZh0y1trbuU+JoDkiEwqhQ36nodYqJwyEIhPSdMNd7lOm/4io72luTPWH20Yda0xOuUow==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=6.9.0" + } + }, + "node_modules/@babel/helper-validator-option": { + "version": "7.27.1", + "resolved": "https://registry.npmjs.org/@babel/helper-validator-option/-/helper-validator-option-7.27.1.tgz", + "integrity": "sha512-YvjJow9FxbhFFKDSuFnVCe2WxXk1zWc22fFePVNEaWJEu8IrZVlda6N0uHwzZrUM1il7NC9Mlp4MaJYbYd9JSg==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=6.9.0" + } + }, + "node_modules/@babel/helpers": { + "version": "7.28.4", + "resolved": "https://registry.npmjs.org/@babel/helpers/-/helpers-7.28.4.tgz", + "integrity": "sha512-HFN59MmQXGHVyYadKLVumYsA9dBFun/ldYxipEjzA4196jpLZd8UjEEBLkbEkvfYreDqJhZxYAWFPtrfhNpj4w==", + "dev": true, + "license": "MIT", + "dependencies": { + "@babel/template": "^7.27.2", + "@babel/types": "^7.28.4" + }, + "engines": { + "node": ">=6.9.0" + } + }, + "node_modules/@babel/parser": { + "version": "7.28.4", + "resolved": "https://registry.npmjs.org/@babel/parser/-/parser-7.28.4.tgz", + "integrity": "sha512-yZbBqeM6TkpP9du/I2pUZnJsRMGGvOuIrhjzC1AwHwW+6he4mni6Bp/m8ijn0iOuZuPI2BfkCoSRunpyjnrQKg==", + "dev": true, + "license": "MIT", + "dependencies": { + "@babel/types": "^7.28.4" + }, + "bin": { + "parser": "bin/babel-parser.js" + }, + "engines": { + "node": ">=6.0.0" + } + }, + "node_modules/@babel/plugin-transform-react-jsx-self": { + "version": "7.27.1", + "resolved": "https://registry.npmjs.org/@babel/plugin-transform-react-jsx-self/-/plugin-transform-react-jsx-self-7.27.1.tgz", + "integrity": "sha512-6UzkCs+ejGdZ5mFFC/OCUrv028ab2fp1znZmCZjAOBKiBK2jXD1O+BPSfX8X2qjJ75fZBMSnQn3Rq2mrBJK2mw==", + "dev": true, + "license": "MIT", + "dependencies": { + "@babel/helper-plugin-utils": "^7.27.1" + }, + "engines": { + "node": ">=6.9.0" + }, + "peerDependencies": { + "@babel/core": "^7.0.0-0" + } + }, + "node_modules/@babel/plugin-transform-react-jsx-source": { + "version": "7.27.1", + "resolved": "https://registry.npmjs.org/@babel/plugin-transform-react-jsx-source/-/plugin-transform-react-jsx-source-7.27.1.tgz", + "integrity": "sha512-zbwoTsBruTeKB9hSq73ha66iFeJHuaFkUbwvqElnygoNbj/jHRsSeokowZFN3CZ64IvEqcmmkVe89OPXc7ldAw==", + "dev": true, + "license": "MIT", + "dependencies": { + "@babel/helper-plugin-utils": "^7.27.1" + }, + "engines": { + "node": ">=6.9.0" + }, + "peerDependencies": { + "@babel/core": "^7.0.0-0" + } + }, + "node_modules/@babel/runtime": { + "version": "7.28.4", + "resolved": "https://registry.npmjs.org/@babel/runtime/-/runtime-7.28.4.tgz", + "integrity": "sha512-Q/N6JNWvIvPnLDvjlE1OUBLPQHH6l3CltCEsHIujp45zQUSSh8K+gHnaEX45yAT1nyngnINhvWtzN+Nb9D8RAQ==", + "license": "MIT", + "engines": { + "node": ">=6.9.0" + } + }, + "node_modules/@babel/template": { + "version": "7.27.2", + "resolved": "https://registry.npmjs.org/@babel/template/-/template-7.27.2.tgz", + "integrity": "sha512-LPDZ85aEJyYSd18/DkjNh4/y1ntkE5KwUHWTiqgRxruuZL2F1yuHligVHLvcHY2vMHXttKFpJn6LwfI7cw7ODw==", + "dev": true, + "license": "MIT", + "dependencies": { + "@babel/code-frame": "^7.27.1", + "@babel/parser": "^7.27.2", + "@babel/types": "^7.27.1" + }, + "engines": { + "node": ">=6.9.0" + } + }, + "node_modules/@babel/traverse": { + "version": "7.28.4", + "resolved": "https://registry.npmjs.org/@babel/traverse/-/traverse-7.28.4.tgz", + "integrity": "sha512-YEzuboP2qvQavAcjgQNVgsvHIDv6ZpwXvcvjmyySP2DIMuByS/6ioU5G9pYrWHM6T2YDfc7xga9iNzYOs12CFQ==", + "dev": true, + "license": "MIT", + "dependencies": { + "@babel/code-frame": "^7.27.1", + "@babel/generator": "^7.28.3", + "@babel/helper-globals": "^7.28.0", + "@babel/parser": "^7.28.4", + "@babel/template": "^7.27.2", + "@babel/types": "^7.28.4", + "debug": "^4.3.1" + }, + "engines": { + "node": ">=6.9.0" + } + }, + "node_modules/@babel/types": { + "version": "7.28.4", + "resolved": "https://registry.npmjs.org/@babel/types/-/types-7.28.4.tgz", + "integrity": "sha512-bkFqkLhh3pMBUQQkpVgWDWq/lqzc2678eUyDlTBhRqhCHFguYYGM0Efga7tYk4TogG/3x0EEl66/OQ+WGbWB/Q==", + "dev": true, + "license": "MIT", + "dependencies": { + "@babel/helper-string-parser": "^7.27.1", + "@babel/helper-validator-identifier": "^7.27.1" + }, + "engines": { + "node": ">=6.9.0" + } + }, + "node_modules/@esbuild/aix-ppc64": { + "version": "0.25.10", + "resolved": "https://registry.npmjs.org/@esbuild/aix-ppc64/-/aix-ppc64-0.25.10.tgz", + "integrity": "sha512-0NFWnA+7l41irNuaSVlLfgNT12caWJVLzp5eAVhZ0z1qpxbockccEt3s+149rE64VUI3Ml2zt8Nv5JVc4QXTsw==", + "cpu": [ + "ppc64" + ], + "license": "MIT", + "optional": true, + "os": [ + "aix" + ], + "engines": { + "node": ">=18" + } + }, + "node_modules/@esbuild/android-arm": { + "version": "0.25.10", + "resolved": "https://registry.npmjs.org/@esbuild/android-arm/-/android-arm-0.25.10.tgz", + "integrity": "sha512-dQAxF1dW1C3zpeCDc5KqIYuZ1tgAdRXNoZP7vkBIRtKZPYe2xVr/d3SkirklCHudW1B45tGiUlz2pUWDfbDD4w==", + "cpu": [ + "arm" + ], + "license": "MIT", + "optional": true, + "os": [ + "android" + ], + "engines": { + "node": ">=18" + } + }, + "node_modules/@esbuild/android-arm64": { + "version": "0.25.10", + "resolved": "https://registry.npmjs.org/@esbuild/android-arm64/-/android-arm64-0.25.10.tgz", + "integrity": "sha512-LSQa7eDahypv/VO6WKohZGPSJDq5OVOo3UoFR1E4t4Gj1W7zEQMUhI+lo81H+DtB+kP+tDgBp+M4oNCwp6kffg==", + "cpu": [ + "arm64" + ], + "license": "MIT", + "optional": true, + "os": [ + "android" + ], + "engines": { + "node": ">=18" + } + }, + "node_modules/@esbuild/android-x64": { + "version": "0.25.10", + "resolved": "https://registry.npmjs.org/@esbuild/android-x64/-/android-x64-0.25.10.tgz", + "integrity": "sha512-MiC9CWdPrfhibcXwr39p9ha1x0lZJ9KaVfvzA0Wxwz9ETX4v5CHfF09bx935nHlhi+MxhA63dKRRQLiVgSUtEg==", + "cpu": [ + "x64" + ], + "license": "MIT", + "optional": true, + "os": [ + "android" + ], + "engines": { + "node": ">=18" + } + }, + "node_modules/@esbuild/darwin-arm64": { + "version": "0.25.10", + "resolved": "https://registry.npmjs.org/@esbuild/darwin-arm64/-/darwin-arm64-0.25.10.tgz", + "integrity": "sha512-JC74bdXcQEpW9KkV326WpZZjLguSZ3DfS8wrrvPMHgQOIEIG/sPXEN/V8IssoJhbefLRcRqw6RQH2NnpdprtMA==", + "cpu": [ + "arm64" + ], + "license": "MIT", + "optional": true, + "os": [ + "darwin" + ], + "engines": { + "node": ">=18" + } + }, + "node_modules/@esbuild/darwin-x64": { + "version": "0.25.10", + "resolved": "https://registry.npmjs.org/@esbuild/darwin-x64/-/darwin-x64-0.25.10.tgz", + "integrity": "sha512-tguWg1olF6DGqzws97pKZ8G2L7Ig1vjDmGTwcTuYHbuU6TTjJe5FXbgs5C1BBzHbJ2bo1m3WkQDbWO2PvamRcg==", + "cpu": [ + "x64" + ], + "license": "MIT", + "optional": true, + "os": [ + "darwin" + ], + "engines": { + "node": ">=18" + } + }, + "node_modules/@esbuild/freebsd-arm64": { + "version": "0.25.10", + "resolved": "https://registry.npmjs.org/@esbuild/freebsd-arm64/-/freebsd-arm64-0.25.10.tgz", + "integrity": "sha512-3ZioSQSg1HT2N05YxeJWYR+Libe3bREVSdWhEEgExWaDtyFbbXWb49QgPvFH8u03vUPX10JhJPcz7s9t9+boWg==", + "cpu": [ + "arm64" + ], + "license": "MIT", + "optional": true, + "os": [ + "freebsd" + ], + "engines": { + "node": ">=18" + } + }, + "node_modules/@esbuild/freebsd-x64": { + "version": "0.25.10", + "resolved": "https://registry.npmjs.org/@esbuild/freebsd-x64/-/freebsd-x64-0.25.10.tgz", + "integrity": "sha512-LLgJfHJk014Aa4anGDbh8bmI5Lk+QidDmGzuC2D+vP7mv/GeSN+H39zOf7pN5N8p059FcOfs2bVlrRr4SK9WxA==", + "cpu": [ + "x64" + ], + "license": "MIT", + "optional": true, + "os": [ + "freebsd" + ], + "engines": { + "node": ">=18" + } + }, + "node_modules/@esbuild/linux-arm": { + "version": "0.25.10", + "resolved": "https://registry.npmjs.org/@esbuild/linux-arm/-/linux-arm-0.25.10.tgz", + "integrity": "sha512-oR31GtBTFYCqEBALI9r6WxoU/ZofZl962pouZRTEYECvNF/dtXKku8YXcJkhgK/beU+zedXfIzHijSRapJY3vg==", + "cpu": [ + "arm" + ], + "license": "MIT", + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": ">=18" + } + }, + "node_modules/@esbuild/linux-arm64": { + "version": "0.25.10", + "resolved": "https://registry.npmjs.org/@esbuild/linux-arm64/-/linux-arm64-0.25.10.tgz", + "integrity": "sha512-5luJWN6YKBsawd5f9i4+c+geYiVEw20FVW5x0v1kEMWNq8UctFjDiMATBxLvmmHA4bf7F6hTRaJgtghFr9iziQ==", + "cpu": [ + "arm64" + ], + "license": "MIT", + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": ">=18" + } + }, + "node_modules/@esbuild/linux-ia32": { + "version": "0.25.10", + "resolved": "https://registry.npmjs.org/@esbuild/linux-ia32/-/linux-ia32-0.25.10.tgz", + "integrity": "sha512-NrSCx2Kim3EnnWgS4Txn0QGt0Xipoumb6z6sUtl5bOEZIVKhzfyp/Lyw4C1DIYvzeW/5mWYPBFJU3a/8Yr75DQ==", + "cpu": [ + "ia32" + ], + "license": "MIT", + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": ">=18" + } + }, + "node_modules/@esbuild/linux-loong64": { + "version": "0.25.10", + "resolved": "https://registry.npmjs.org/@esbuild/linux-loong64/-/linux-loong64-0.25.10.tgz", + "integrity": "sha512-xoSphrd4AZda8+rUDDfD9J6FUMjrkTz8itpTITM4/xgerAZZcFW7Dv+sun7333IfKxGG8gAq+3NbfEMJfiY+Eg==", + "cpu": [ + "loong64" + ], + "license": "MIT", + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": ">=18" + } + }, + "node_modules/@esbuild/linux-mips64el": { + "version": "0.25.10", + "resolved": "https://registry.npmjs.org/@esbuild/linux-mips64el/-/linux-mips64el-0.25.10.tgz", + "integrity": "sha512-ab6eiuCwoMmYDyTnyptoKkVS3k8fy/1Uvq7Dj5czXI6DF2GqD2ToInBI0SHOp5/X1BdZ26RKc5+qjQNGRBelRA==", + "cpu": [ + "mips64el" + ], + "license": "MIT", + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": ">=18" + } + }, + "node_modules/@esbuild/linux-ppc64": { + "version": "0.25.10", + "resolved": "https://registry.npmjs.org/@esbuild/linux-ppc64/-/linux-ppc64-0.25.10.tgz", + "integrity": "sha512-NLinzzOgZQsGpsTkEbdJTCanwA5/wozN9dSgEl12haXJBzMTpssebuXR42bthOF3z7zXFWH1AmvWunUCkBE4EA==", + "cpu": [ + "ppc64" + ], + "license": "MIT", + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": ">=18" + } + }, + "node_modules/@esbuild/linux-riscv64": { + "version": "0.25.10", + "resolved": "https://registry.npmjs.org/@esbuild/linux-riscv64/-/linux-riscv64-0.25.10.tgz", + "integrity": "sha512-FE557XdZDrtX8NMIeA8LBJX3dC2M8VGXwfrQWU7LB5SLOajfJIxmSdyL/gU1m64Zs9CBKvm4UAuBp5aJ8OgnrA==", + "cpu": [ + "riscv64" + ], + "license": "MIT", + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": ">=18" + } + }, + "node_modules/@esbuild/linux-s390x": { + "version": "0.25.10", + "resolved": "https://registry.npmjs.org/@esbuild/linux-s390x/-/linux-s390x-0.25.10.tgz", + "integrity": "sha512-3BBSbgzuB9ajLoVZk0mGu+EHlBwkusRmeNYdqmznmMc9zGASFjSsxgkNsqmXugpPk00gJ0JNKh/97nxmjctdew==", + "cpu": [ + "s390x" + ], + "license": "MIT", + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": ">=18" + } + }, + "node_modules/@esbuild/linux-x64": { + "version": "0.25.10", + "resolved": "https://registry.npmjs.org/@esbuild/linux-x64/-/linux-x64-0.25.10.tgz", + "integrity": "sha512-QSX81KhFoZGwenVyPoberggdW1nrQZSvfVDAIUXr3WqLRZGZqWk/P4T8p2SP+de2Sr5HPcvjhcJzEiulKgnxtA==", + "cpu": [ + "x64" + ], + "license": "MIT", + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": ">=18" + } + }, + "node_modules/@esbuild/netbsd-arm64": { + "version": "0.25.10", + "resolved": "https://registry.npmjs.org/@esbuild/netbsd-arm64/-/netbsd-arm64-0.25.10.tgz", + "integrity": "sha512-AKQM3gfYfSW8XRk8DdMCzaLUFB15dTrZfnX8WXQoOUpUBQ+NaAFCP1kPS/ykbbGYz7rxn0WS48/81l9hFl3u4A==", + "cpu": [ + "arm64" + ], + "license": "MIT", + "optional": true, + "os": [ + "netbsd" + ], + "engines": { + "node": ">=18" + } + }, + "node_modules/@esbuild/netbsd-x64": { + "version": "0.25.10", + "resolved": "https://registry.npmjs.org/@esbuild/netbsd-x64/-/netbsd-x64-0.25.10.tgz", + "integrity": "sha512-7RTytDPGU6fek/hWuN9qQpeGPBZFfB4zZgcz2VK2Z5VpdUxEI8JKYsg3JfO0n/Z1E/6l05n0unDCNc4HnhQGig==", + "cpu": [ + "x64" + ], + "license": "MIT", + "optional": true, + "os": [ + "netbsd" + ], + "engines": { + "node": ">=18" + } + }, + "node_modules/@esbuild/openbsd-arm64": { + "version": "0.25.10", + "resolved": "https://registry.npmjs.org/@esbuild/openbsd-arm64/-/openbsd-arm64-0.25.10.tgz", + "integrity": "sha512-5Se0VM9Wtq797YFn+dLimf2Zx6McttsH2olUBsDml+lm0GOCRVebRWUvDtkY4BWYv/3NgzS8b/UM3jQNh5hYyw==", + "cpu": [ + "arm64" + ], + "license": "MIT", + "optional": true, + "os": [ + "openbsd" + ], + "engines": { + "node": ">=18" + } + }, + "node_modules/@esbuild/openbsd-x64": { + "version": "0.25.10", + "resolved": "https://registry.npmjs.org/@esbuild/openbsd-x64/-/openbsd-x64-0.25.10.tgz", + "integrity": "sha512-XkA4frq1TLj4bEMB+2HnI0+4RnjbuGZfet2gs/LNs5Hc7D89ZQBHQ0gL2ND6Lzu1+QVkjp3x1gIcPKzRNP8bXw==", + "cpu": [ + "x64" + ], + "license": "MIT", + "optional": true, + "os": [ + "openbsd" + ], + "engines": { + "node": ">=18" + } + }, + "node_modules/@esbuild/openharmony-arm64": { + "version": "0.25.10", + "resolved": "https://registry.npmjs.org/@esbuild/openharmony-arm64/-/openharmony-arm64-0.25.10.tgz", + "integrity": "sha512-AVTSBhTX8Y/Fz6OmIVBip9tJzZEUcY8WLh7I59+upa5/GPhh2/aM6bvOMQySspnCCHvFi79kMtdJS1w0DXAeag==", + "cpu": [ + "arm64" + ], + "license": "MIT", + "optional": true, + "os": [ + "openharmony" + ], + "engines": { + "node": ">=18" + } + }, + "node_modules/@esbuild/sunos-x64": { + "version": "0.25.10", + "resolved": "https://registry.npmjs.org/@esbuild/sunos-x64/-/sunos-x64-0.25.10.tgz", + "integrity": "sha512-fswk3XT0Uf2pGJmOpDB7yknqhVkJQkAQOcW/ccVOtfx05LkbWOaRAtn5SaqXypeKQra1QaEa841PgrSL9ubSPQ==", + "cpu": [ + "x64" + ], + "license": "MIT", + "optional": true, + "os": [ + "sunos" + ], + "engines": { + "node": ">=18" + } + }, + "node_modules/@esbuild/win32-arm64": { + "version": "0.25.10", + "resolved": "https://registry.npmjs.org/@esbuild/win32-arm64/-/win32-arm64-0.25.10.tgz", + "integrity": "sha512-ah+9b59KDTSfpaCg6VdJoOQvKjI33nTaQr4UluQwW7aEwZQsbMCfTmfEO4VyewOxx4RaDT/xCy9ra2GPWmO7Kw==", + "cpu": [ + "arm64" + ], + "license": "MIT", + "optional": true, + "os": [ + "win32" + ], + "engines": { + "node": ">=18" + } + }, + "node_modules/@esbuild/win32-ia32": { + "version": "0.25.10", + "resolved": "https://registry.npmjs.org/@esbuild/win32-ia32/-/win32-ia32-0.25.10.tgz", + "integrity": "sha512-QHPDbKkrGO8/cz9LKVnJU22HOi4pxZnZhhA2HYHez5Pz4JeffhDjf85E57Oyco163GnzNCVkZK0b/n4Y0UHcSw==", + "cpu": [ + "ia32" + ], + "license": "MIT", + "optional": true, + "os": [ + "win32" + ], + "engines": { + "node": ">=18" + } + }, + "node_modules/@esbuild/win32-x64": { + "version": "0.25.10", + "resolved": "https://registry.npmjs.org/@esbuild/win32-x64/-/win32-x64-0.25.10.tgz", + "integrity": "sha512-9KpxSVFCu0iK1owoez6aC/s/EdUQLDN3adTxGCqxMVhrPDj6bt5dbrHDXUuq+Bs2vATFBBrQS5vdQ/Ed2P+nbw==", + "cpu": [ + "x64" + ], + "license": "MIT", + "optional": true, + "os": [ + "win32" + ], + "engines": { + "node": ">=18" + } + }, + "node_modules/@eslint-community/eslint-utils": { + "version": "4.9.0", + "resolved": "https://registry.npmjs.org/@eslint-community/eslint-utils/-/eslint-utils-4.9.0.tgz", + "integrity": "sha512-ayVFHdtZ+hsq1t2Dy24wCmGXGe4q9Gu3smhLYALJrr473ZH27MsnSL+LKUlimp4BWJqMDMLmPpx/Q9R3OAlL4g==", + "dev": true, + "license": "MIT", + "dependencies": { + "eslint-visitor-keys": "^3.4.3" + }, + "engines": { + "node": "^12.22.0 || ^14.17.0 || >=16.0.0" + }, + "funding": { + "url": "https://opencollective.com/eslint" + }, + "peerDependencies": { + "eslint": "^6.0.0 || ^7.0.0 || >=8.0.0" + } + }, + "node_modules/@eslint-community/eslint-utils/node_modules/eslint-visitor-keys": { + "version": "3.4.3", + "resolved": "https://registry.npmjs.org/eslint-visitor-keys/-/eslint-visitor-keys-3.4.3.tgz", + "integrity": "sha512-wpc+LXeiyiisxPlEkUzU6svyS1frIO3Mgxj1fdy7Pm8Ygzguax2N3Fa/D/ag1WqbOprdI+uY6wMUl8/a2G+iag==", + "dev": true, + "license": "Apache-2.0", + "engines": { + "node": "^12.22.0 || ^14.17.0 || >=16.0.0" + }, + "funding": { + "url": "https://opencollective.com/eslint" + } + }, + "node_modules/@eslint-community/regexpp": { + "version": "4.12.1", + "resolved": "https://registry.npmjs.org/@eslint-community/regexpp/-/regexpp-4.12.1.tgz", + "integrity": "sha512-CCZCDJuduB9OUkFkY2IgppNZMi2lBQgD2qzwXkEia16cge2pijY/aXi96CJMquDMn3nJdlPV1A5KrJEXwfLNzQ==", + "dev": true, + "license": "MIT", + "engines": { + "node": "^12.0.0 || ^14.0.0 || >=16.0.0" + } + }, + "node_modules/@eslint/config-array": { + "version": "0.21.0", + "resolved": "https://registry.npmjs.org/@eslint/config-array/-/config-array-0.21.0.tgz", + "integrity": "sha512-ENIdc4iLu0d93HeYirvKmrzshzofPw6VkZRKQGe9Nv46ZnWUzcF1xV01dcvEg/1wXUR61OmmlSfyeyO7EvjLxQ==", + "dev": true, + "license": "Apache-2.0", + "dependencies": { + "@eslint/object-schema": "^2.1.6", + "debug": "^4.3.1", + "minimatch": "^3.1.2" + }, + "engines": { + "node": "^18.18.0 || ^20.9.0 || >=21.1.0" + } + }, + "node_modules/@eslint/config-helpers": { + "version": "0.4.0", + "resolved": "https://registry.npmjs.org/@eslint/config-helpers/-/config-helpers-0.4.0.tgz", + "integrity": "sha512-WUFvV4WoIwW8Bv0KeKCIIEgdSiFOsulyN0xrMu+7z43q/hkOLXjvb5u7UC9jDxvRzcrbEmuZBX5yJZz1741jog==", + "dev": true, + "license": "Apache-2.0", + "dependencies": { + "@eslint/core": "^0.16.0" + }, + "engines": { + "node": "^18.18.0 || ^20.9.0 || >=21.1.0" + } + }, + "node_modules/@eslint/core": { + "version": "0.16.0", + "resolved": "https://registry.npmjs.org/@eslint/core/-/core-0.16.0.tgz", + "integrity": "sha512-nmC8/totwobIiFcGkDza3GIKfAw1+hLiYVrh3I1nIomQ8PEr5cxg34jnkmGawul/ep52wGRAcyeDCNtWKSOj4Q==", + "dev": true, + "license": "Apache-2.0", + "dependencies": { + "@types/json-schema": "^7.0.15" + }, + "engines": { + "node": "^18.18.0 || ^20.9.0 || >=21.1.0" + } + }, + "node_modules/@eslint/eslintrc": { + "version": "3.3.1", + "resolved": "https://registry.npmjs.org/@eslint/eslintrc/-/eslintrc-3.3.1.tgz", + "integrity": "sha512-gtF186CXhIl1p4pJNGZw8Yc6RlshoePRvE0X91oPGb3vZ8pM3qOS9W9NGPat9LziaBV7XrJWGylNQXkGcnM3IQ==", + "dev": true, + "license": "MIT", + "dependencies": { + "ajv": "^6.12.4", + "debug": "^4.3.2", + "espree": "^10.0.1", + "globals": "^14.0.0", + "ignore": "^5.2.0", + "import-fresh": "^3.2.1", + "js-yaml": "^4.1.0", + "minimatch": "^3.1.2", + "strip-json-comments": "^3.1.1" + }, + "engines": { + "node": "^18.18.0 || ^20.9.0 || >=21.1.0" + }, + "funding": { + "url": "https://opencollective.com/eslint" + } + }, + "node_modules/@eslint/eslintrc/node_modules/globals": { + "version": "14.0.0", + "resolved": "https://registry.npmjs.org/globals/-/globals-14.0.0.tgz", + "integrity": "sha512-oahGvuMGQlPw/ivIYBjVSrWAfWLBeku5tpPE2fOPLi+WHffIWbuh2tCjhyQhTBPMf5E9jDEH4FOmTYgYwbKwtQ==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=18" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/@eslint/js": { + "version": "9.37.0", + "resolved": "https://registry.npmjs.org/@eslint/js/-/js-9.37.0.tgz", + "integrity": "sha512-jaS+NJ+hximswBG6pjNX0uEJZkrT0zwpVi3BA3vX22aFGjJjmgSTSmPpZCRKmoBL5VY/M6p0xsSJx7rk7sy5gg==", + "dev": true, + "license": "MIT", + "engines": { + "node": "^18.18.0 || ^20.9.0 || >=21.1.0" + }, + "funding": { + "url": "https://eslint.org/donate" + } + }, + "node_modules/@eslint/object-schema": { + "version": "2.1.6", + "resolved": "https://registry.npmjs.org/@eslint/object-schema/-/object-schema-2.1.6.tgz", + "integrity": "sha512-RBMg5FRL0I0gs51M/guSAj5/e14VQ4tpZnQNWwuDT66P14I43ItmPfIZRhO9fUVIPOAQXU47atlywZ/czoqFPA==", + "dev": true, + "license": "Apache-2.0", + "engines": { + "node": "^18.18.0 || ^20.9.0 || >=21.1.0" + } + }, + "node_modules/@eslint/plugin-kit": { + "version": "0.4.0", + "resolved": "https://registry.npmjs.org/@eslint/plugin-kit/-/plugin-kit-0.4.0.tgz", + "integrity": "sha512-sB5uyeq+dwCWyPi31B2gQlVlo+j5brPlWx4yZBrEaRo/nhdDE8Xke1gsGgtiBdaBTxuTkceLVuVt/pclrasb0A==", + "dev": true, + "license": "Apache-2.0", + "dependencies": { + "@eslint/core": "^0.16.0", + "levn": "^0.4.1" + }, + "engines": { + "node": "^18.18.0 || ^20.9.0 || >=21.1.0" + } + }, + "node_modules/@humanfs/core": { + "version": "0.19.1", + "resolved": "https://registry.npmjs.org/@humanfs/core/-/core-0.19.1.tgz", + "integrity": "sha512-5DyQ4+1JEUzejeK1JGICcideyfUbGixgS9jNgex5nqkW+cY7WZhxBigmieN5Qnw9ZosSNVC9KQKyb+GUaGyKUA==", + "dev": true, + "license": "Apache-2.0", + "engines": { + "node": ">=18.18.0" + } + }, + "node_modules/@humanfs/node": { + "version": "0.16.7", + "resolved": "https://registry.npmjs.org/@humanfs/node/-/node-0.16.7.tgz", + "integrity": "sha512-/zUx+yOsIrG4Y43Eh2peDeKCxlRt/gET6aHfaKpuq267qXdYDFViVHfMaLyygZOnl0kGWxFIgsBy8QFuTLUXEQ==", + "dev": true, + "license": "Apache-2.0", + "dependencies": { + "@humanfs/core": "^0.19.1", + "@humanwhocodes/retry": "^0.4.0" + }, + "engines": { + "node": ">=18.18.0" + } + }, + "node_modules/@humanwhocodes/module-importer": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/@humanwhocodes/module-importer/-/module-importer-1.0.1.tgz", + "integrity": "sha512-bxveV4V8v5Yb4ncFTT3rPSgZBOpCkjfK0y4oVVVJwIuDVBRMDXrPyXRL988i5ap9m9bnyEEjWfm5WkBmtffLfA==", + "dev": true, + "license": "Apache-2.0", + "engines": { + "node": ">=12.22" + }, + "funding": { + "type": "github", + "url": "https://github.com/sponsors/nzakas" + } + }, + "node_modules/@humanwhocodes/retry": { + "version": "0.4.3", + "resolved": "https://registry.npmjs.org/@humanwhocodes/retry/-/retry-0.4.3.tgz", + "integrity": "sha512-bV0Tgo9K4hfPCek+aMAn81RppFKv2ySDQeMoSZuvTASywNTnVJCArCZE2FWqpvIatKu7VMRLWlR1EazvVhDyhQ==", + "dev": true, + "license": "Apache-2.0", + "engines": { + "node": ">=18.18" + }, + "funding": { + "type": "github", + "url": "https://github.com/sponsors/nzakas" + } + }, + "node_modules/@isaacs/fs-minipass": { + "version": "4.0.1", + "resolved": "https://registry.npmjs.org/@isaacs/fs-minipass/-/fs-minipass-4.0.1.tgz", + "integrity": "sha512-wgm9Ehl2jpeqP3zw/7mo3kRHFp5MEDhqAdwy1fTGkHAwnkGOVsgpvQhL8B5n1qlb01jV3n/bI0ZfZp5lWA1k4w==", + "license": "ISC", + "dependencies": { + "minipass": "^7.0.4" + }, + "engines": { + "node": ">=18.0.0" + } + }, + "node_modules/@jridgewell/gen-mapping": { + "version": "0.3.13", + "resolved": "https://registry.npmjs.org/@jridgewell/gen-mapping/-/gen-mapping-0.3.13.tgz", + "integrity": "sha512-2kkt/7niJ6MgEPxF0bYdQ6etZaA+fQvDcLKckhy1yIQOzaoKjBBjSj63/aLVjYE3qhRt5dvM+uUyfCg6UKCBbA==", + "license": "MIT", + "dependencies": { + "@jridgewell/sourcemap-codec": "^1.5.0", + "@jridgewell/trace-mapping": "^0.3.24" + } + }, + "node_modules/@jridgewell/remapping": { + "version": "2.3.5", + "resolved": "https://registry.npmjs.org/@jridgewell/remapping/-/remapping-2.3.5.tgz", + "integrity": "sha512-LI9u/+laYG4Ds1TDKSJW2YPrIlcVYOwi2fUC6xB43lueCjgxV4lffOCZCtYFiH6TNOX+tQKXx97T4IKHbhyHEQ==", + "license": "MIT", + "dependencies": { + "@jridgewell/gen-mapping": "^0.3.5", + "@jridgewell/trace-mapping": "^0.3.24" + } + }, + "node_modules/@jridgewell/resolve-uri": { + "version": "3.1.2", + "resolved": "https://registry.npmjs.org/@jridgewell/resolve-uri/-/resolve-uri-3.1.2.tgz", + "integrity": "sha512-bRISgCIjP20/tbWSPWMEi54QVPRZExkuD9lJL+UIxUKtwVJA8wW1Trb1jMs1RFXo1CBTNZ/5hpC9QvmKWdopKw==", + "license": "MIT", + "engines": { + "node": ">=6.0.0" + } + }, + "node_modules/@jridgewell/sourcemap-codec": { + "version": "1.5.5", + "resolved": "https://registry.npmjs.org/@jridgewell/sourcemap-codec/-/sourcemap-codec-1.5.5.tgz", + "integrity": "sha512-cYQ9310grqxueWbl+WuIUIaiUaDcj7WOq5fVhEljNVgRfOUhY9fy2zTvfoqWsnebh8Sl70VScFbICvJnLKB0Og==", + "license": "MIT" + }, + "node_modules/@jridgewell/trace-mapping": { + "version": "0.3.31", + "resolved": "https://registry.npmjs.org/@jridgewell/trace-mapping/-/trace-mapping-0.3.31.tgz", + "integrity": "sha512-zzNR+SdQSDJzc8joaeP8QQoCQr8NuYx2dIIytl1QeBEZHJ9uW6hebsrYgbz8hJwUQao3TWCMtmfV8Nu1twOLAw==", + "license": "MIT", + "dependencies": { + "@jridgewell/resolve-uri": "^3.1.0", + "@jridgewell/sourcemap-codec": "^1.4.14" + } + }, + "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/@rolldown/pluginutils": { + "version": "1.0.0-beta.38", + "resolved": "https://registry.npmjs.org/@rolldown/pluginutils/-/pluginutils-1.0.0-beta.38.tgz", + "integrity": "sha512-N/ICGKleNhA5nc9XXQG/kkKHJ7S55u0x0XUJbbkmdCnFuoRkM1Il12q9q0eX19+M7KKUEPw/daUPIRnxhcxAIw==", + "dev": true, + "license": "MIT" + }, + "node_modules/@rollup/rollup-android-arm-eabi": { + "version": "4.52.4", + "resolved": "https://registry.npmjs.org/@rollup/rollup-android-arm-eabi/-/rollup-android-arm-eabi-4.52.4.tgz", + "integrity": "sha512-BTm2qKNnWIQ5auf4deoetINJm2JzvihvGb9R6K/ETwKLql/Bb3Eg2H1FBp1gUb4YGbydMA3jcmQTR73q7J+GAA==", + "cpu": [ + "arm" + ], + "license": "MIT", + "optional": true, + "os": [ + "android" + ] + }, + "node_modules/@rollup/rollup-android-arm64": { + "version": "4.52.4", + "resolved": "https://registry.npmjs.org/@rollup/rollup-android-arm64/-/rollup-android-arm64-4.52.4.tgz", + "integrity": "sha512-P9LDQiC5vpgGFgz7GSM6dKPCiqR3XYN1WwJKA4/BUVDjHpYsf3iBEmVz62uyq20NGYbiGPR5cNHI7T1HqxNs2w==", + "cpu": [ + "arm64" + ], + "license": "MIT", + "optional": true, + "os": [ + "android" + ] + }, + "node_modules/@rollup/rollup-darwin-arm64": { + "version": "4.52.4", + "resolved": "https://registry.npmjs.org/@rollup/rollup-darwin-arm64/-/rollup-darwin-arm64-4.52.4.tgz", + "integrity": "sha512-QRWSW+bVccAvZF6cbNZBJwAehmvG9NwfWHwMy4GbWi/BQIA/laTIktebT2ipVjNncqE6GLPxOok5hsECgAxGZg==", + "cpu": [ + "arm64" + ], + "license": "MIT", + "optional": true, + "os": [ + "darwin" + ] + }, + "node_modules/@rollup/rollup-darwin-x64": { + "version": "4.52.4", + "resolved": "https://registry.npmjs.org/@rollup/rollup-darwin-x64/-/rollup-darwin-x64-4.52.4.tgz", + "integrity": "sha512-hZgP05pResAkRJxL1b+7yxCnXPGsXU0fG9Yfd6dUaoGk+FhdPKCJ5L1Sumyxn8kvw8Qi5PvQ8ulenUbRjzeCTw==", + "cpu": [ + "x64" + ], + "license": "MIT", + "optional": true, + "os": [ + "darwin" + ] + }, + "node_modules/@rollup/rollup-freebsd-arm64": { + "version": "4.52.4", + "resolved": "https://registry.npmjs.org/@rollup/rollup-freebsd-arm64/-/rollup-freebsd-arm64-4.52.4.tgz", + "integrity": "sha512-xmc30VshuBNUd58Xk4TKAEcRZHaXlV+tCxIXELiE9sQuK3kG8ZFgSPi57UBJt8/ogfhAF5Oz4ZSUBN77weM+mQ==", + "cpu": [ + "arm64" + ], + "license": "MIT", + "optional": true, + "os": [ + "freebsd" + ] + }, + "node_modules/@rollup/rollup-freebsd-x64": { + "version": "4.52.4", + "resolved": "https://registry.npmjs.org/@rollup/rollup-freebsd-x64/-/rollup-freebsd-x64-4.52.4.tgz", + "integrity": "sha512-WdSLpZFjOEqNZGmHflxyifolwAiZmDQzuOzIq9L27ButpCVpD7KzTRtEG1I0wMPFyiyUdOO+4t8GvrnBLQSwpw==", + "cpu": [ + "x64" + ], + "license": "MIT", + "optional": true, + "os": [ + "freebsd" + ] + }, + "node_modules/@rollup/rollup-linux-arm-gnueabihf": { + "version": "4.52.4", + "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-arm-gnueabihf/-/rollup-linux-arm-gnueabihf-4.52.4.tgz", + "integrity": "sha512-xRiOu9Of1FZ4SxVbB0iEDXc4ddIcjCv2aj03dmW8UrZIW7aIQ9jVJdLBIhxBI+MaTnGAKyvMwPwQnoOEvP7FgQ==", + "cpu": [ + "arm" + ], + "license": "MIT", + "optional": true, + "os": [ + "linux" + ] + }, + "node_modules/@rollup/rollup-linux-arm-musleabihf": { + "version": "4.52.4", + "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-arm-musleabihf/-/rollup-linux-arm-musleabihf-4.52.4.tgz", + "integrity": "sha512-FbhM2p9TJAmEIEhIgzR4soUcsW49e9veAQCziwbR+XWB2zqJ12b4i/+hel9yLiD8pLncDH4fKIPIbt5238341Q==", + "cpu": [ + "arm" + ], + "license": "MIT", + "optional": true, + "os": [ + "linux" + ] + }, + "node_modules/@rollup/rollup-linux-arm64-gnu": { + "version": "4.52.4", + "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-arm64-gnu/-/rollup-linux-arm64-gnu-4.52.4.tgz", + "integrity": "sha512-4n4gVwhPHR9q/g8lKCyz0yuaD0MvDf7dV4f9tHt0C73Mp8h38UCtSCSE6R9iBlTbXlmA8CjpsZoujhszefqueg==", + "cpu": [ + "arm64" + ], + "license": "MIT", + "optional": true, + "os": [ + "linux" + ] + }, + "node_modules/@rollup/rollup-linux-arm64-musl": { + "version": "4.52.4", + "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-arm64-musl/-/rollup-linux-arm64-musl-4.52.4.tgz", + "integrity": "sha512-u0n17nGA0nvi/11gcZKsjkLj1QIpAuPFQbR48Subo7SmZJnGxDpspyw2kbpuoQnyK+9pwf3pAoEXerJs/8Mi9g==", + "cpu": [ + "arm64" + ], + "license": "MIT", + "optional": true, + "os": [ + "linux" + ] + }, + "node_modules/@rollup/rollup-linux-loong64-gnu": { + "version": "4.52.4", + "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-loong64-gnu/-/rollup-linux-loong64-gnu-4.52.4.tgz", + "integrity": "sha512-0G2c2lpYtbTuXo8KEJkDkClE/+/2AFPdPAbmaHoE870foRFs4pBrDehilMcrSScrN/fB/1HTaWO4bqw+ewBzMQ==", + "cpu": [ + "loong64" + ], + "license": "MIT", + "optional": true, + "os": [ + "linux" + ] + }, + "node_modules/@rollup/rollup-linux-ppc64-gnu": { + "version": "4.52.4", + "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-ppc64-gnu/-/rollup-linux-ppc64-gnu-4.52.4.tgz", + "integrity": "sha512-teSACug1GyZHmPDv14VNbvZFX779UqWTsd7KtTM9JIZRDI5NUwYSIS30kzI8m06gOPB//jtpqlhmraQ68b5X2g==", + "cpu": [ + "ppc64" + ], + "license": "MIT", + "optional": true, + "os": [ + "linux" + ] + }, + "node_modules/@rollup/rollup-linux-riscv64-gnu": { + "version": "4.52.4", + "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-riscv64-gnu/-/rollup-linux-riscv64-gnu-4.52.4.tgz", + "integrity": "sha512-/MOEW3aHjjs1p4Pw1Xk4+3egRevx8Ji9N6HUIA1Ifh8Q+cg9dremvFCUbOX2Zebz80BwJIgCBUemjqhU5XI5Eg==", + "cpu": [ + "riscv64" + ], + "license": "MIT", + "optional": true, + "os": [ + "linux" + ] + }, + "node_modules/@rollup/rollup-linux-riscv64-musl": { + "version": "4.52.4", + "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-riscv64-musl/-/rollup-linux-riscv64-musl-4.52.4.tgz", + "integrity": "sha512-1HHmsRyh845QDpEWzOFtMCph5Ts+9+yllCrREuBR/vg2RogAQGGBRC8lDPrPOMnrdOJ+mt1WLMOC2Kao/UwcvA==", + "cpu": [ + "riscv64" + ], + "license": "MIT", + "optional": true, + "os": [ + "linux" + ] + }, + "node_modules/@rollup/rollup-linux-s390x-gnu": { + "version": "4.52.4", + "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-s390x-gnu/-/rollup-linux-s390x-gnu-4.52.4.tgz", + "integrity": "sha512-seoeZp4L/6D1MUyjWkOMRU6/iLmCU2EjbMTyAG4oIOs1/I82Y5lTeaxW0KBfkUdHAWN7j25bpkt0rjnOgAcQcA==", + "cpu": [ + "s390x" + ], + "license": "MIT", + "optional": true, + "os": [ + "linux" + ] + }, + "node_modules/@rollup/rollup-linux-x64-gnu": { + "version": "4.52.4", + "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-x64-gnu/-/rollup-linux-x64-gnu-4.52.4.tgz", + "integrity": "sha512-Wi6AXf0k0L7E2gteNsNHUs7UMwCIhsCTs6+tqQ5GPwVRWMaflqGec4Sd8n6+FNFDw9vGcReqk2KzBDhCa1DLYg==", + "cpu": [ + "x64" + ], + "license": "MIT", + "optional": true, + "os": [ + "linux" + ] + }, + "node_modules/@rollup/rollup-linux-x64-musl": { + "version": "4.52.4", + "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-x64-musl/-/rollup-linux-x64-musl-4.52.4.tgz", + "integrity": "sha512-dtBZYjDmCQ9hW+WgEkaffvRRCKm767wWhxsFW3Lw86VXz/uJRuD438/XvbZT//B96Vs8oTA8Q4A0AfHbrxP9zw==", + "cpu": [ + "x64" + ], + "license": "MIT", + "optional": true, + "os": [ + "linux" + ] + }, + "node_modules/@rollup/rollup-openharmony-arm64": { + "version": "4.52.4", + "resolved": "https://registry.npmjs.org/@rollup/rollup-openharmony-arm64/-/rollup-openharmony-arm64-4.52.4.tgz", + "integrity": "sha512-1ox+GqgRWqaB1RnyZXL8PD6E5f7YyRUJYnCqKpNzxzP0TkaUh112NDrR9Tt+C8rJ4x5G9Mk8PQR3o7Ku2RKqKA==", + "cpu": [ + "arm64" + ], + "license": "MIT", + "optional": true, + "os": [ + "openharmony" + ] + }, + "node_modules/@rollup/rollup-win32-arm64-msvc": { + "version": "4.52.4", + "resolved": "https://registry.npmjs.org/@rollup/rollup-win32-arm64-msvc/-/rollup-win32-arm64-msvc-4.52.4.tgz", + "integrity": "sha512-8GKr640PdFNXwzIE0IrkMWUNUomILLkfeHjXBi/nUvFlpZP+FA8BKGKpacjW6OUUHaNI6sUURxR2U2g78FOHWQ==", + "cpu": [ + "arm64" + ], + "license": "MIT", + "optional": true, + "os": [ + "win32" + ] + }, + "node_modules/@rollup/rollup-win32-ia32-msvc": { + "version": "4.52.4", + "resolved": "https://registry.npmjs.org/@rollup/rollup-win32-ia32-msvc/-/rollup-win32-ia32-msvc-4.52.4.tgz", + "integrity": "sha512-AIy/jdJ7WtJ/F6EcfOb2GjR9UweO0n43jNObQMb6oGxkYTfLcnN7vYYpG+CN3lLxrQkzWnMOoNSHTW54pgbVxw==", + "cpu": [ + "ia32" + ], + "license": "MIT", + "optional": true, + "os": [ + "win32" + ] + }, + "node_modules/@rollup/rollup-win32-x64-gnu": { + "version": "4.52.4", + "resolved": "https://registry.npmjs.org/@rollup/rollup-win32-x64-gnu/-/rollup-win32-x64-gnu-4.52.4.tgz", + "integrity": "sha512-UF9KfsH9yEam0UjTwAgdK0anlQ7c8/pWPU2yVjyWcF1I1thABt6WXE47cI71pGiZ8wGvxohBoLnxM04L/wj8mQ==", + "cpu": [ + "x64" + ], + "license": "MIT", + "optional": true, + "os": [ + "win32" + ] + }, + "node_modules/@rollup/rollup-win32-x64-msvc": { + "version": "4.52.4", + "resolved": "https://registry.npmjs.org/@rollup/rollup-win32-x64-msvc/-/rollup-win32-x64-msvc-4.52.4.tgz", + "integrity": "sha512-bf9PtUa0u8IXDVxzRToFQKsNCRz9qLYfR/MpECxl4mRoWYjAeFjgxj1XdZr2M/GNVpT05p+LgQOHopYDlUu6/w==", + "cpu": [ + "x64" + ], + "license": "MIT", + "optional": true, + "os": [ + "win32" + ] + }, + "node_modules/@tailwindcss/node": { + "version": "4.1.14", + "resolved": "https://registry.npmjs.org/@tailwindcss/node/-/node-4.1.14.tgz", + "integrity": "sha512-hpz+8vFk3Ic2xssIA3e01R6jkmsAhvkQdXlEbRTk6S10xDAtiQiM3FyvZVGsucefq764euO/b8WUW9ysLdThHw==", + "license": "MIT", + "dependencies": { + "@jridgewell/remapping": "^2.3.4", + "enhanced-resolve": "^5.18.3", + "jiti": "^2.6.0", + "lightningcss": "1.30.1", + "magic-string": "^0.30.19", + "source-map-js": "^1.2.1", + "tailwindcss": "4.1.14" + } + }, + "node_modules/@tailwindcss/oxide": { + "version": "4.1.14", + "resolved": "https://registry.npmjs.org/@tailwindcss/oxide/-/oxide-4.1.14.tgz", + "integrity": "sha512-23yx+VUbBwCg2x5XWdB8+1lkPajzLmALEfMb51zZUBYaYVPDQvBSD/WYDqiVyBIo2BZFa3yw1Rpy3G2Jp+K0dw==", + "hasInstallScript": true, + "license": "MIT", + "dependencies": { + "detect-libc": "^2.0.4", + "tar": "^7.5.1" + }, + "engines": { + "node": ">= 10" + }, + "optionalDependencies": { + "@tailwindcss/oxide-android-arm64": "4.1.14", + "@tailwindcss/oxide-darwin-arm64": "4.1.14", + "@tailwindcss/oxide-darwin-x64": "4.1.14", + "@tailwindcss/oxide-freebsd-x64": "4.1.14", + "@tailwindcss/oxide-linux-arm-gnueabihf": "4.1.14", + "@tailwindcss/oxide-linux-arm64-gnu": "4.1.14", + "@tailwindcss/oxide-linux-arm64-musl": "4.1.14", + "@tailwindcss/oxide-linux-x64-gnu": "4.1.14", + "@tailwindcss/oxide-linux-x64-musl": "4.1.14", + "@tailwindcss/oxide-wasm32-wasi": "4.1.14", + "@tailwindcss/oxide-win32-arm64-msvc": "4.1.14", + "@tailwindcss/oxide-win32-x64-msvc": "4.1.14" + } + }, + "node_modules/@tailwindcss/oxide-android-arm64": { + "version": "4.1.14", + "resolved": "https://registry.npmjs.org/@tailwindcss/oxide-android-arm64/-/oxide-android-arm64-4.1.14.tgz", + "integrity": "sha512-a94ifZrGwMvbdeAxWoSuGcIl6/DOP5cdxagid7xJv6bwFp3oebp7y2ImYsnZBMTwjn5Ev5xESvS3FFYUGgPODQ==", + "cpu": [ + "arm64" + ], + "license": "MIT", + "optional": true, + "os": [ + "android" + ], + "engines": { + "node": ">= 10" + } + }, + "node_modules/@tailwindcss/oxide-darwin-arm64": { + "version": "4.1.14", + "resolved": "https://registry.npmjs.org/@tailwindcss/oxide-darwin-arm64/-/oxide-darwin-arm64-4.1.14.tgz", + "integrity": "sha512-HkFP/CqfSh09xCnrPJA7jud7hij5ahKyWomrC3oiO2U9i0UjP17o9pJbxUN0IJ471GTQQmzwhp0DEcpbp4MZTA==", + "cpu": [ + "arm64" + ], + "license": "MIT", + "optional": true, + "os": [ + "darwin" + ], + "engines": { + "node": ">= 10" + } + }, + "node_modules/@tailwindcss/oxide-darwin-x64": { + "version": "4.1.14", + "resolved": "https://registry.npmjs.org/@tailwindcss/oxide-darwin-x64/-/oxide-darwin-x64-4.1.14.tgz", + "integrity": "sha512-eVNaWmCgdLf5iv6Qd3s7JI5SEFBFRtfm6W0mphJYXgvnDEAZ5sZzqmI06bK6xo0IErDHdTA5/t7d4eTfWbWOFw==", + "cpu": [ + "x64" + ], + "license": "MIT", + "optional": true, + "os": [ + "darwin" + ], + "engines": { + "node": ">= 10" + } + }, + "node_modules/@tailwindcss/oxide-freebsd-x64": { + "version": "4.1.14", + "resolved": "https://registry.npmjs.org/@tailwindcss/oxide-freebsd-x64/-/oxide-freebsd-x64-4.1.14.tgz", + "integrity": "sha512-QWLoRXNikEuqtNb0dhQN6wsSVVjX6dmUFzuuiL09ZeXju25dsei2uIPl71y2Ic6QbNBsB4scwBoFnlBfabHkEw==", + "cpu": [ + "x64" + ], + "license": "MIT", + "optional": true, + "os": [ + "freebsd" + ], + "engines": { + "node": ">= 10" + } + }, + "node_modules/@tailwindcss/oxide-linux-arm-gnueabihf": { + "version": "4.1.14", + "resolved": "https://registry.npmjs.org/@tailwindcss/oxide-linux-arm-gnueabihf/-/oxide-linux-arm-gnueabihf-4.1.14.tgz", + "integrity": "sha512-VB4gjQni9+F0VCASU+L8zSIyjrLLsy03sjcR3bM0V2g4SNamo0FakZFKyUQ96ZVwGK4CaJsc9zd/obQy74o0Fw==", + "cpu": [ + "arm" + ], + "license": "MIT", + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": ">= 10" + } + }, + "node_modules/@tailwindcss/oxide-linux-arm64-gnu": { + "version": "4.1.14", + "resolved": "https://registry.npmjs.org/@tailwindcss/oxide-linux-arm64-gnu/-/oxide-linux-arm64-gnu-4.1.14.tgz", + "integrity": "sha512-qaEy0dIZ6d9vyLnmeg24yzA8XuEAD9WjpM5nIM1sUgQ/Zv7cVkharPDQcmm/t/TvXoKo/0knI3me3AGfdx6w1w==", + "cpu": [ + "arm64" + ], + "license": "MIT", + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": ">= 10" + } + }, + "node_modules/@tailwindcss/oxide-linux-arm64-musl": { + "version": "4.1.14", + "resolved": "https://registry.npmjs.org/@tailwindcss/oxide-linux-arm64-musl/-/oxide-linux-arm64-musl-4.1.14.tgz", + "integrity": "sha512-ISZjT44s59O8xKsPEIesiIydMG/sCXoMBCqsphDm/WcbnuWLxxb+GcvSIIA5NjUw6F8Tex7s5/LM2yDy8RqYBQ==", + "cpu": [ + "arm64" + ], + "license": "MIT", + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": ">= 10" + } + }, + "node_modules/@tailwindcss/oxide-linux-x64-gnu": { + "version": "4.1.14", + "resolved": "https://registry.npmjs.org/@tailwindcss/oxide-linux-x64-gnu/-/oxide-linux-x64-gnu-4.1.14.tgz", + "integrity": "sha512-02c6JhLPJj10L2caH4U0zF8Hji4dOeahmuMl23stk0MU1wfd1OraE7rOloidSF8W5JTHkFdVo/O7uRUJJnUAJg==", + "cpu": [ + "x64" + ], + "license": "MIT", + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": ">= 10" + } + }, + "node_modules/@tailwindcss/oxide-linux-x64-musl": { + "version": "4.1.14", + "resolved": "https://registry.npmjs.org/@tailwindcss/oxide-linux-x64-musl/-/oxide-linux-x64-musl-4.1.14.tgz", + "integrity": "sha512-TNGeLiN1XS66kQhxHG/7wMeQDOoL0S33x9BgmydbrWAb9Qw0KYdd8o1ifx4HOGDWhVmJ+Ul+JQ7lyknQFilO3Q==", + "cpu": [ + "x64" + ], + "license": "MIT", + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": ">= 10" + } + }, + "node_modules/@tailwindcss/oxide-wasm32-wasi": { + "version": "4.1.14", + "resolved": "https://registry.npmjs.org/@tailwindcss/oxide-wasm32-wasi/-/oxide-wasm32-wasi-4.1.14.tgz", + "integrity": "sha512-uZYAsaW/jS/IYkd6EWPJKW/NlPNSkWkBlaeVBi/WsFQNP05/bzkebUL8FH1pdsqx4f2fH/bWFcUABOM9nfiJkQ==", + "bundleDependencies": [ + "@napi-rs/wasm-runtime", + "@emnapi/core", + "@emnapi/runtime", + "@tybys/wasm-util", + "@emnapi/wasi-threads", + "tslib" + ], + "cpu": [ + "wasm32" + ], + "license": "MIT", + "optional": true, + "dependencies": { + "@emnapi/core": "^1.5.0", + "@emnapi/runtime": "^1.5.0", + "@emnapi/wasi-threads": "^1.1.0", + "@napi-rs/wasm-runtime": "^1.0.5", + "@tybys/wasm-util": "^0.10.1", + "tslib": "^2.4.0" + }, + "engines": { + "node": ">=14.0.0" + } + }, + "node_modules/@tailwindcss/oxide-win32-arm64-msvc": { + "version": "4.1.14", + "resolved": "https://registry.npmjs.org/@tailwindcss/oxide-win32-arm64-msvc/-/oxide-win32-arm64-msvc-4.1.14.tgz", + "integrity": "sha512-Az0RnnkcvRqsuoLH2Z4n3JfAef0wElgzHD5Aky/e+0tBUxUhIeIqFBTMNQvmMRSP15fWwmvjBxZ3Q8RhsDnxAA==", + "cpu": [ + "arm64" + ], + "license": "MIT", + "optional": true, + "os": [ + "win32" + ], + "engines": { + "node": ">= 10" + } + }, + "node_modules/@tailwindcss/oxide-win32-x64-msvc": { + "version": "4.1.14", + "resolved": "https://registry.npmjs.org/@tailwindcss/oxide-win32-x64-msvc/-/oxide-win32-x64-msvc-4.1.14.tgz", + "integrity": "sha512-ttblVGHgf68kEE4om1n/n44I0yGPkCPbLsqzjvybhpwa6mKKtgFfAzy6btc3HRmuW7nHe0OOrSeNP9sQmmH9XA==", + "cpu": [ + "x64" + ], + "license": "MIT", + "optional": true, + "os": [ + "win32" + ], + "engines": { + "node": ">= 10" + } + }, + "node_modules/@tailwindcss/vite": { + "version": "4.1.14", + "resolved": "https://registry.npmjs.org/@tailwindcss/vite/-/vite-4.1.14.tgz", + "integrity": "sha512-BoFUoU0XqgCUS1UXWhmDJroKKhNXeDzD7/XwabjkDIAbMnc4ULn5e2FuEuBbhZ6ENZoSYzKlzvZ44Yr6EUDUSA==", + "license": "MIT", + "dependencies": { + "@tailwindcss/node": "4.1.14", + "@tailwindcss/oxide": "4.1.14", + "tailwindcss": "4.1.14" + }, + "peerDependencies": { + "vite": "^5.2.0 || ^6 || ^7" + } + }, + "node_modules/@types/babel__core": { + "version": "7.20.5", + "resolved": "https://registry.npmjs.org/@types/babel__core/-/babel__core-7.20.5.tgz", + "integrity": "sha512-qoQprZvz5wQFJwMDqeseRXWv3rqMvhgpbXFfVyWhbx9X47POIA6i/+dXefEmZKoAgOaTdaIgNSMqMIU61yRyzA==", + "dev": true, + "license": "MIT", + "dependencies": { + "@babel/parser": "^7.20.7", + "@babel/types": "^7.20.7", + "@types/babel__generator": "*", + "@types/babel__template": "*", + "@types/babel__traverse": "*" + } + }, + "node_modules/@types/babel__generator": { + "version": "7.27.0", + "resolved": "https://registry.npmjs.org/@types/babel__generator/-/babel__generator-7.27.0.tgz", + "integrity": "sha512-ufFd2Xi92OAVPYsy+P4n7/U7e68fex0+Ee8gSG9KX7eo084CWiQ4sdxktvdl0bOPupXtVJPY19zk6EwWqUQ8lg==", + "dev": true, + "license": "MIT", + "dependencies": { + "@babel/types": "^7.0.0" + } + }, + "node_modules/@types/babel__template": { + "version": "7.4.4", + "resolved": "https://registry.npmjs.org/@types/babel__template/-/babel__template-7.4.4.tgz", + "integrity": "sha512-h/NUaSyG5EyxBIp8YRxo4RMe2/qQgvyowRwVMzhYhBCONbW8PUsg4lkFMrhgZhUe5z3L3MiLDuvyJ/CaPa2A8A==", + "dev": true, + "license": "MIT", + "dependencies": { + "@babel/parser": "^7.1.0", + "@babel/types": "^7.0.0" + } + }, + "node_modules/@types/babel__traverse": { + "version": "7.28.0", + "resolved": "https://registry.npmjs.org/@types/babel__traverse/-/babel__traverse-7.28.0.tgz", + "integrity": "sha512-8PvcXf70gTDZBgt9ptxJ8elBeBjcLOAcOtoO/mPJjtji1+CdGbHgm77om1GrsPxsiE+uXIpNSK64UYaIwQXd4Q==", + "dev": true, + "license": "MIT", + "dependencies": { + "@babel/types": "^7.28.2" + } + }, + "node_modules/@types/estree": { + "version": "1.0.8", + "resolved": "https://registry.npmjs.org/@types/estree/-/estree-1.0.8.tgz", + "integrity": "sha512-dWHzHa2WqEXI/O1E9OjrocMTKJl2mSrEolh1Iomrv6U+JuNwaHXsXx9bLu5gG7BUWFIN0skIQJQ/L1rIex4X6w==", + "license": "MIT" + }, + "node_modules/@types/json-schema": { + "version": "7.0.15", + "resolved": "https://registry.npmjs.org/@types/json-schema/-/json-schema-7.0.15.tgz", + "integrity": "sha512-5+fP8P8MFNC+AyZCDxrB2pkZFPGzqQWUzpSeuuVLvm8VMcorNYavBqoFcxK8bQz4Qsbn4oUEEem4wDLfcysGHA==", + "dev": true, + "license": "MIT" + }, + "node_modules/@types/node": { + "version": "24.7.0", + "resolved": "https://registry.npmjs.org/@types/node/-/node-24.7.0.tgz", + "integrity": "sha512-IbKooQVqUBrlzWTi79E8Fw78l8k1RNtlDDNWsFZs7XonuQSJ8oNYfEeclhprUldXISRMLzBpILuKgPlIxm+/Yw==", + "devOptional": true, + "license": "MIT", + "dependencies": { + "undici-types": "~7.14.0" + } + }, + "node_modules/@types/react": { + "version": "19.2.2", + "resolved": "https://registry.npmjs.org/@types/react/-/react-19.2.2.tgz", + "integrity": "sha512-6mDvHUFSjyT2B2yeNx2nUgMxh9LtOWvkhIU3uePn2I2oyNymUAX1NIsdgviM4CH+JSrp2D2hsMvJOkxY+0wNRA==", + "dev": true, + "license": "MIT", + "dependencies": { + "csstype": "^3.0.2" + } + }, + "node_modules/@types/react-dom": { + "version": "19.2.1", + "resolved": "https://registry.npmjs.org/@types/react-dom/-/react-dom-19.2.1.tgz", + "integrity": "sha512-/EEvYBdT3BflCWvTMO7YkYBHVE9Ci6XdqZciZANQgKpaiDRGOLIlRo91jbTNRQjgPFWVaRxcYc0luVNFitz57A==", + "dev": true, + "license": "MIT", + "peerDependencies": { + "@types/react": "^19.2.0" + } + }, + "node_modules/@typescript-eslint/eslint-plugin": { + "version": "8.46.0", + "resolved": "https://registry.npmjs.org/@typescript-eslint/eslint-plugin/-/eslint-plugin-8.46.0.tgz", + "integrity": "sha512-hA8gxBq4ukonVXPy0OKhiaUh/68D0E88GSmtC1iAEnGaieuDi38LhS7jdCHRLi6ErJBNDGCzvh5EnzdPwUc0DA==", + "dev": true, + "license": "MIT", + "dependencies": { + "@eslint-community/regexpp": "^4.10.0", + "@typescript-eslint/scope-manager": "8.46.0", + "@typescript-eslint/type-utils": "8.46.0", + "@typescript-eslint/utils": "8.46.0", + "@typescript-eslint/visitor-keys": "8.46.0", + "graphemer": "^1.4.0", + "ignore": "^7.0.0", + "natural-compare": "^1.4.0", + "ts-api-utils": "^2.1.0" + }, + "engines": { + "node": "^18.18.0 || ^20.9.0 || >=21.1.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/typescript-eslint" + }, + "peerDependencies": { + "@typescript-eslint/parser": "^8.46.0", + "eslint": "^8.57.0 || ^9.0.0", + "typescript": ">=4.8.4 <6.0.0" + } + }, + "node_modules/@typescript-eslint/eslint-plugin/node_modules/ignore": { + "version": "7.0.5", + "resolved": "https://registry.npmjs.org/ignore/-/ignore-7.0.5.tgz", + "integrity": "sha512-Hs59xBNfUIunMFgWAbGX5cq6893IbWg4KnrjbYwX3tx0ztorVgTDA6B2sxf8ejHJ4wz8BqGUMYlnzNBer5NvGg==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">= 4" + } + }, + "node_modules/@typescript-eslint/parser": { + "version": "8.46.0", + "resolved": "https://registry.npmjs.org/@typescript-eslint/parser/-/parser-8.46.0.tgz", + "integrity": "sha512-n1H6IcDhmmUEG7TNVSspGmiHHutt7iVKtZwRppD7e04wha5MrkV1h3pti9xQLcCMt6YWsncpoT0HMjkH1FNwWQ==", + "dev": true, + "license": "MIT", + "dependencies": { + "@typescript-eslint/scope-manager": "8.46.0", + "@typescript-eslint/types": "8.46.0", + "@typescript-eslint/typescript-estree": "8.46.0", + "@typescript-eslint/visitor-keys": "8.46.0", + "debug": "^4.3.4" + }, + "engines": { + "node": "^18.18.0 || ^20.9.0 || >=21.1.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/typescript-eslint" + }, + "peerDependencies": { + "eslint": "^8.57.0 || ^9.0.0", + "typescript": ">=4.8.4 <6.0.0" + } + }, + "node_modules/@typescript-eslint/project-service": { + "version": "8.46.0", + "resolved": "https://registry.npmjs.org/@typescript-eslint/project-service/-/project-service-8.46.0.tgz", + "integrity": "sha512-OEhec0mH+U5Je2NZOeK1AbVCdm0ChyapAyTeXVIYTPXDJ3F07+cu87PPXcGoYqZ7M9YJVvFnfpGg1UmCIqM+QQ==", + "dev": true, + "license": "MIT", + "dependencies": { + "@typescript-eslint/tsconfig-utils": "^8.46.0", + "@typescript-eslint/types": "^8.46.0", + "debug": "^4.3.4" + }, + "engines": { + "node": "^18.18.0 || ^20.9.0 || >=21.1.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/typescript-eslint" + }, + "peerDependencies": { + "typescript": ">=4.8.4 <6.0.0" + } + }, + "node_modules/@typescript-eslint/scope-manager": { + "version": "8.46.0", + "resolved": "https://registry.npmjs.org/@typescript-eslint/scope-manager/-/scope-manager-8.46.0.tgz", + "integrity": "sha512-lWETPa9XGcBes4jqAMYD9fW0j4n6hrPtTJwWDmtqgFO/4HF4jmdH/Q6wggTw5qIT5TXjKzbt7GsZUBnWoO3dqw==", + "dev": true, + "license": "MIT", + "dependencies": { + "@typescript-eslint/types": "8.46.0", + "@typescript-eslint/visitor-keys": "8.46.0" + }, + "engines": { + "node": "^18.18.0 || ^20.9.0 || >=21.1.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/typescript-eslint" + } + }, + "node_modules/@typescript-eslint/tsconfig-utils": { + "version": "8.46.0", + "resolved": "https://registry.npmjs.org/@typescript-eslint/tsconfig-utils/-/tsconfig-utils-8.46.0.tgz", + "integrity": "sha512-WrYXKGAHY836/N7zoK/kzi6p8tXFhasHh8ocFL9VZSAkvH956gfeRfcnhs3xzRy8qQ/dq3q44v1jvQieMFg2cw==", + "dev": true, + "license": "MIT", + "engines": { + "node": "^18.18.0 || ^20.9.0 || >=21.1.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/typescript-eslint" + }, + "peerDependencies": { + "typescript": ">=4.8.4 <6.0.0" + } + }, + "node_modules/@typescript-eslint/type-utils": { + "version": "8.46.0", + "resolved": "https://registry.npmjs.org/@typescript-eslint/type-utils/-/type-utils-8.46.0.tgz", + "integrity": "sha512-hy+lvYV1lZpVs2jRaEYvgCblZxUoJiPyCemwbQZ+NGulWkQRy0HRPYAoef/CNSzaLt+MLvMptZsHXHlkEilaeg==", + "dev": true, + "license": "MIT", + "dependencies": { + "@typescript-eslint/types": "8.46.0", + "@typescript-eslint/typescript-estree": "8.46.0", + "@typescript-eslint/utils": "8.46.0", + "debug": "^4.3.4", + "ts-api-utils": "^2.1.0" + }, + "engines": { + "node": "^18.18.0 || ^20.9.0 || >=21.1.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/typescript-eslint" + }, + "peerDependencies": { + "eslint": "^8.57.0 || ^9.0.0", + "typescript": ">=4.8.4 <6.0.0" + } + }, + "node_modules/@typescript-eslint/types": { + "version": "8.46.0", + "resolved": "https://registry.npmjs.org/@typescript-eslint/types/-/types-8.46.0.tgz", + "integrity": "sha512-bHGGJyVjSE4dJJIO5yyEWt/cHyNwga/zXGJbJJ8TiO01aVREK6gCTu3L+5wrkb1FbDkQ+TKjMNe9R/QQQP9+rA==", + "dev": true, + "license": "MIT", + "engines": { + "node": "^18.18.0 || ^20.9.0 || >=21.1.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/typescript-eslint" + } + }, + "node_modules/@typescript-eslint/typescript-estree": { + "version": "8.46.0", + "resolved": "https://registry.npmjs.org/@typescript-eslint/typescript-estree/-/typescript-estree-8.46.0.tgz", + "integrity": "sha512-ekDCUfVpAKWJbRfm8T1YRrCot1KFxZn21oV76v5Fj4tr7ELyk84OS+ouvYdcDAwZL89WpEkEj2DKQ+qg//+ucg==", + "dev": true, + "license": "MIT", + "dependencies": { + "@typescript-eslint/project-service": "8.46.0", + "@typescript-eslint/tsconfig-utils": "8.46.0", + "@typescript-eslint/types": "8.46.0", + "@typescript-eslint/visitor-keys": "8.46.0", + "debug": "^4.3.4", + "fast-glob": "^3.3.2", + "is-glob": "^4.0.3", + "minimatch": "^9.0.4", + "semver": "^7.6.0", + "ts-api-utils": "^2.1.0" + }, + "engines": { + "node": "^18.18.0 || ^20.9.0 || >=21.1.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/typescript-eslint" + }, + "peerDependencies": { + "typescript": ">=4.8.4 <6.0.0" + } + }, + "node_modules/@typescript-eslint/typescript-estree/node_modules/brace-expansion": { + "version": "2.0.2", + "resolved": "https://registry.npmjs.org/brace-expansion/-/brace-expansion-2.0.2.tgz", + "integrity": "sha512-Jt0vHyM+jmUBqojB7E1NIYadt0vI0Qxjxd2TErW94wDz+E2LAm5vKMXXwg6ZZBTHPuUlDgQHKXvjGBdfcF1ZDQ==", + "dev": true, + "license": "MIT", + "dependencies": { + "balanced-match": "^1.0.0" + } + }, + "node_modules/@typescript-eslint/typescript-estree/node_modules/minimatch": { + "version": "9.0.5", + "resolved": "https://registry.npmjs.org/minimatch/-/minimatch-9.0.5.tgz", + "integrity": "sha512-G6T0ZX48xgozx7587koeX9Ys2NYy6Gmv//P89sEte9V9whIapMNF4idKxnW2QtCcLiTWlb/wfCabAtAFWhhBow==", + "dev": true, + "license": "ISC", + "dependencies": { + "brace-expansion": "^2.0.1" + }, + "engines": { + "node": ">=16 || 14 >=14.17" + }, + "funding": { + "url": "https://github.com/sponsors/isaacs" + } + }, + "node_modules/@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==", + "dev": true, + "license": "ISC", + "bin": { + "semver": "bin/semver.js" + }, + "engines": { + "node": ">=10" + } + }, + "node_modules/@typescript-eslint/utils": { + "version": "8.46.0", + "resolved": "https://registry.npmjs.org/@typescript-eslint/utils/-/utils-8.46.0.tgz", + "integrity": "sha512-nD6yGWPj1xiOm4Gk0k6hLSZz2XkNXhuYmyIrOWcHoPuAhjT9i5bAG+xbWPgFeNR8HPHHtpNKdYUXJl/D3x7f5g==", + "dev": true, + "license": "MIT", + "dependencies": { + "@eslint-community/eslint-utils": "^4.7.0", + "@typescript-eslint/scope-manager": "8.46.0", + "@typescript-eslint/types": "8.46.0", + "@typescript-eslint/typescript-estree": "8.46.0" + }, + "engines": { + "node": "^18.18.0 || ^20.9.0 || >=21.1.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/typescript-eslint" + }, + "peerDependencies": { + "eslint": "^8.57.0 || ^9.0.0", + "typescript": ">=4.8.4 <6.0.0" + } + }, + "node_modules/@typescript-eslint/visitor-keys": { + "version": "8.46.0", + "resolved": "https://registry.npmjs.org/@typescript-eslint/visitor-keys/-/visitor-keys-8.46.0.tgz", + "integrity": "sha512-FrvMpAK+hTbFy7vH5j1+tMYHMSKLE6RzluFJlkFNKD0p9YsUT75JlBSmr5so3QRzvMwU5/bIEdeNrxm8du8l3Q==", + "dev": true, + "license": "MIT", + "dependencies": { + "@typescript-eslint/types": "8.46.0", + "eslint-visitor-keys": "^4.2.1" + }, + "engines": { + "node": "^18.18.0 || ^20.9.0 || >=21.1.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/typescript-eslint" + } + }, + "node_modules/@vitejs/plugin-react": { + "version": "5.0.4", + "resolved": "https://registry.npmjs.org/@vitejs/plugin-react/-/plugin-react-5.0.4.tgz", + "integrity": "sha512-La0KD0vGkVkSk6K+piWDKRUyg8Rl5iAIKRMH0vMJI0Eg47bq1eOxmoObAaQG37WMW9MSyk7Cs8EIWwJC1PtzKA==", + "dev": true, + "license": "MIT", + "dependencies": { + "@babel/core": "^7.28.4", + "@babel/plugin-transform-react-jsx-self": "^7.27.1", + "@babel/plugin-transform-react-jsx-source": "^7.27.1", + "@rolldown/pluginutils": "1.0.0-beta.38", + "@types/babel__core": "^7.20.5", + "react-refresh": "^0.17.0" + }, + "engines": { + "node": "^20.19.0 || >=22.12.0" + }, + "peerDependencies": { + "vite": "^4.2.0 || ^5.0.0 || ^6.0.0 || ^7.0.0" + } + }, + "node_modules/acorn": { + "version": "8.15.0", + "resolved": "https://registry.npmjs.org/acorn/-/acorn-8.15.0.tgz", + "integrity": "sha512-NZyJarBfL7nWwIq+FDL6Zp/yHEhePMNnnJ0y3qfieCrmNvYct8uvtiV41UvlSe6apAfk0fY1FbWx+NwfmpvtTg==", + "dev": true, + "license": "MIT", + "bin": { + "acorn": "bin/acorn" + }, + "engines": { + "node": ">=0.4.0" + } + }, + "node_modules/acorn-jsx": { + "version": "5.3.2", + "resolved": "https://registry.npmjs.org/acorn-jsx/-/acorn-jsx-5.3.2.tgz", + "integrity": "sha512-rq9s+JNhf0IChjtDXxllJ7g41oZk5SlXtp0LHwyA5cejwn7vKmKp4pPri6YEePv2PU65sAsegbXtIinmDFDXgQ==", + "dev": true, + "license": "MIT", + "peerDependencies": { + "acorn": "^6.0.0 || ^7.0.0 || ^8.0.0" + } + }, + "node_modules/ajv": { + "version": "6.12.6", + "resolved": "https://registry.npmjs.org/ajv/-/ajv-6.12.6.tgz", + "integrity": "sha512-j3fVLgvTo527anyYyJOGTYJbG+vnnQYvE0m5mmkc1TK+nxAppkCLMIL0aZ4dblVCNoGShhm+kzE4ZUykBoMg4g==", + "dev": true, + "license": "MIT", + "dependencies": { + "fast-deep-equal": "^3.1.1", + "fast-json-stable-stringify": "^2.0.0", + "json-schema-traverse": "^0.4.1", + "uri-js": "^4.2.2" + }, + "funding": { + "type": "github", + "url": "https://github.com/sponsors/epoberezkin" + } + }, + "node_modules/ansi-styles": { + "version": "4.3.0", + "resolved": "https://registry.npmjs.org/ansi-styles/-/ansi-styles-4.3.0.tgz", + "integrity": "sha512-zbB9rCJAT1rbjiVDb2hqKFHNYLxgtk8NURxZ3IZwD3F6NtxbXZQCnnSi1Lkx+IDohdPlFp222wVALIheZJQSEg==", + "dev": true, + "license": "MIT", + "dependencies": { + "color-convert": "^2.0.1" + }, + "engines": { + "node": ">=8" + }, + "funding": { + "url": "https://github.com/chalk/ansi-styles?sponsor=1" + } + }, + "node_modules/argparse": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/argparse/-/argparse-2.0.1.tgz", + "integrity": "sha512-8+9WqebbFzpX9OR+Wa6O29asIogeRMzcGtAINdpMHHyAg10f05aSFVBbcEqGf/PXw1EjAZ+q2/bEBg3DvurK3Q==", + "dev": true, + "license": "Python-2.0" + }, + "node_modules/balanced-match": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/balanced-match/-/balanced-match-1.0.2.tgz", + "integrity": "sha512-3oSeUO0TMV67hN1AmbXsK4yaqU7tjiHlbxRDZOpH0KW9+CeX4bRAaX0Anxt0tx2MrpRpWwQaPwIlISEJhYU5Pw==", + "dev": true, + "license": "MIT" + }, + "node_modules/baseline-browser-mapping": { + "version": "2.8.14", + "resolved": "https://registry.npmjs.org/baseline-browser-mapping/-/baseline-browser-mapping-2.8.14.tgz", + "integrity": "sha512-GM9c0cWWR8Ga7//Ves/9KRgTS8nLausCkP3CGiFLrnwA2CDUluXgaQqvrULoR2Ujrd/mz/lkX87F5BHFsNr5sQ==", + "dev": true, + "license": "Apache-2.0", + "bin": { + "baseline-browser-mapping": "dist/cli.js" + } + }, + "node_modules/brace-expansion": { + "version": "1.1.12", + "resolved": "https://registry.npmjs.org/brace-expansion/-/brace-expansion-1.1.12.tgz", + "integrity": "sha512-9T9UjW3r0UW5c1Q7GTwllptXwhvYmEzFhzMfZ9H7FQWt+uZePjZPjBP/W1ZEyZ1twGWom5/56TF4lPcqjnDHcg==", + "dev": true, + "license": "MIT", + "dependencies": { + "balanced-match": "^1.0.0", + "concat-map": "0.0.1" + } + }, + "node_modules/braces": { + "version": "3.0.3", + "resolved": "https://registry.npmjs.org/braces/-/braces-3.0.3.tgz", + "integrity": "sha512-yQbXgO/OSZVD2IsiLlro+7Hf6Q18EJrKSEsdoMzKePKXct3gvD8oLcOQdIzGupr5Fj+EDe8gO/lxc1BzfMpxvA==", + "dev": true, + "license": "MIT", + "dependencies": { + "fill-range": "^7.1.1" + }, + "engines": { + "node": ">=8" + } + }, + "node_modules/browserslist": { + "version": "4.26.3", + "resolved": "https://registry.npmjs.org/browserslist/-/browserslist-4.26.3.tgz", + "integrity": "sha512-lAUU+02RFBuCKQPj/P6NgjlbCnLBMp4UtgTx7vNHd3XSIJF87s9a5rA3aH2yw3GS9DqZAUbOtZdCCiZeVRqt0w==", + "dev": true, + "funding": [ + { + "type": "opencollective", + "url": "https://opencollective.com/browserslist" + }, + { + "type": "tidelift", + "url": "https://tidelift.com/funding/github/npm/browserslist" + }, + { + "type": "github", + "url": "https://github.com/sponsors/ai" + } + ], + "license": "MIT", + "dependencies": { + "baseline-browser-mapping": "^2.8.9", + "caniuse-lite": "^1.0.30001746", + "electron-to-chromium": "^1.5.227", + "node-releases": "^2.0.21", + "update-browserslist-db": "^1.1.3" + }, + "bin": { + "browserslist": "cli.js" + }, + "engines": { + "node": "^6 || ^7 || ^8 || ^9 || ^10 || ^11 || ^12 || >=13.7" + } + }, + "node_modules/callsites": { + "version": "3.1.0", + "resolved": "https://registry.npmjs.org/callsites/-/callsites-3.1.0.tgz", + "integrity": "sha512-P8BjAsXvZS+VIDUI11hHCQEv74YT67YUi5JJFNWIqL235sBmjX4+qx9Muvls5ivyNENctx46xQLQ3aTuE7ssaQ==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=6" + } + }, + "node_modules/caniuse-lite": { + "version": "1.0.30001749", + "resolved": "https://registry.npmjs.org/caniuse-lite/-/caniuse-lite-1.0.30001749.tgz", + "integrity": "sha512-0rw2fJOmLfnzCRbkm8EyHL8SvI2Apu5UbnQuTsJ0ClgrH8hcwFooJ1s5R0EP8o8aVrFu8++ae29Kt9/gZAZp/Q==", + "dev": true, + "funding": [ + { + "type": "opencollective", + "url": "https://opencollective.com/browserslist" + }, + { + "type": "tidelift", + "url": "https://tidelift.com/funding/github/npm/caniuse-lite" + }, + { + "type": "github", + "url": "https://github.com/sponsors/ai" + } + ], + "license": "CC-BY-4.0" + }, + "node_modules/chalk": { + "version": "4.1.2", + "resolved": "https://registry.npmjs.org/chalk/-/chalk-4.1.2.tgz", + "integrity": "sha512-oKnbhFyRIXpUuez8iBMmyEa4nbj4IOQyuhc/wy9kY7/WVPcwIO9VA668Pu8RkO7+0G76SLROeyw9CpQ061i4mA==", + "dev": true, + "license": "MIT", + "dependencies": { + "ansi-styles": "^4.1.0", + "supports-color": "^7.1.0" + }, + "engines": { + "node": ">=10" + }, + "funding": { + "url": "https://github.com/chalk/chalk?sponsor=1" + } + }, + "node_modules/chownr": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/chownr/-/chownr-3.0.0.tgz", + "integrity": "sha512-+IxzY9BZOQd/XuYPRmrvEVjF/nqj5kgT4kEq7VofrDoM1MxoRjEWkrCC3EtLi59TVawxTAn+orJwFQcrqEN1+g==", + "license": "BlueOak-1.0.0", + "engines": { + "node": ">=18" + } + }, + "node_modules/color-convert": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/color-convert/-/color-convert-2.0.1.tgz", + "integrity": "sha512-RRECPsj7iu/xb5oKYcsFHSppFNnsj/52OVTRKb4zP5onXwVF3zVmmToNcOfGC+CRDpfK/U584fMg38ZHCaElKQ==", + "dev": true, + "license": "MIT", + "dependencies": { + "color-name": "~1.1.4" + }, + "engines": { + "node": ">=7.0.0" + } + }, + "node_modules/color-name": { + "version": "1.1.4", + "resolved": "https://registry.npmjs.org/color-name/-/color-name-1.1.4.tgz", + "integrity": "sha512-dOy+3AuW3a2wNbZHIuMZpTcgjGuLU/uBL/ubcZF9OXbDo8ff4O8yVp5Bf0efS8uEoYo5q4Fx7dY9OgQGXgAsQA==", + "dev": true, + "license": "MIT" + }, + "node_modules/concat-map": { + "version": "0.0.1", + "resolved": "https://registry.npmjs.org/concat-map/-/concat-map-0.0.1.tgz", + "integrity": "sha512-/Srv4dswyQNBfohGpz9o6Yb3Gz3SrUDqBH5rTuhGR7ahtlbYKnVxw2bCFMRljaA7EXHaXZ8wsHdodFvbkhKmqg==", + "dev": true, + "license": "MIT" + }, + "node_modules/convert-source-map": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/convert-source-map/-/convert-source-map-2.0.0.tgz", + "integrity": "sha512-Kvp459HrV2FEJ1CAsi1Ku+MY3kasH19TFykTz2xWmMeq6bk2NU3XXvfJ+Q61m0xktWwt+1HSYf3JZsTms3aRJg==", + "dev": true, + "license": "MIT" + }, + "node_modules/cross-spawn": { + "version": "7.0.6", + "resolved": "https://registry.npmjs.org/cross-spawn/-/cross-spawn-7.0.6.tgz", + "integrity": "sha512-uV2QOWP2nWzsy2aMp8aRibhi9dlzF5Hgh5SHaB9OiTGEyDTiJJyx0uy51QXdyWbtAHNua4XJzUKca3OzKUd3vA==", + "dev": true, + "license": "MIT", + "dependencies": { + "path-key": "^3.1.0", + "shebang-command": "^2.0.0", + "which": "^2.0.1" + }, + "engines": { + "node": ">= 8" + } + }, + "node_modules/csstype": { + "version": "3.1.3", + "resolved": "https://registry.npmjs.org/csstype/-/csstype-3.1.3.tgz", + "integrity": "sha512-M1uQkMl8rQK/szD0LNhtqxIPLpimGm8sOBwU7lLnCpSbTyY3yeU1Vc7l4KT5zT4s/yOxHH5O7tIuuLOCnLADRw==", + "dev": true, + "license": "MIT" + }, + "node_modules/debug": { + "version": "4.4.3", + "resolved": "https://registry.npmjs.org/debug/-/debug-4.4.3.tgz", + "integrity": "sha512-RGwwWnwQvkVfavKVt22FGLw+xYSdzARwm0ru6DhTVA3umU5hZc28V3kO4stgYryrTlLpuvgI9GiijltAjNbcqA==", + "dev": true, + "license": "MIT", + "dependencies": { + "ms": "^2.1.3" + }, + "engines": { + "node": ">=6.0" + }, + "peerDependenciesMeta": { + "supports-color": { + "optional": true + } + } + }, + "node_modules/deep-is": { + "version": "0.1.4", + "resolved": "https://registry.npmjs.org/deep-is/-/deep-is-0.1.4.tgz", + "integrity": "sha512-oIPzksmTg4/MriiaYGO+okXDT7ztn/w3Eptv/+gSIdMdKsJo0u4CfYNFJPy+4SKMuCqGw2wxnA+URMg3t8a/bQ==", + "dev": true, + "license": "MIT" + }, + "node_modules/detect-libc": { + "version": "2.1.2", + "resolved": "https://registry.npmjs.org/detect-libc/-/detect-libc-2.1.2.tgz", + "integrity": "sha512-Btj2BOOO83o3WyH59e8MgXsxEQVcarkUOpEYrubB0urwnN10yQ364rsiByU11nZlqWYZm05i/of7io4mzihBtQ==", + "license": "Apache-2.0", + "engines": { + "node": ">=8" + } + }, + "node_modules/electron-to-chromium": { + "version": "1.5.233", + "resolved": "https://registry.npmjs.org/electron-to-chromium/-/electron-to-chromium-1.5.233.tgz", + "integrity": "sha512-iUdTQSf7EFXsDdQsp8MwJz5SVk4APEFqXU/S47OtQ0YLqacSwPXdZ5vRlMX3neb07Cy2vgioNuRnWUXFwuslkg==", + "dev": true, + "license": "ISC" + }, + "node_modules/enhanced-resolve": { + "version": "5.18.3", + "resolved": "https://registry.npmjs.org/enhanced-resolve/-/enhanced-resolve-5.18.3.tgz", + "integrity": "sha512-d4lC8xfavMeBjzGr2vECC3fsGXziXZQyJxD868h2M/mBI3PwAuODxAkLkq5HYuvrPYcUtiLzsTo8U3PgX3Ocww==", + "license": "MIT", + "dependencies": { + "graceful-fs": "^4.2.4", + "tapable": "^2.2.0" + }, + "engines": { + "node": ">=10.13.0" + } + }, + "node_modules/esbuild": { + "version": "0.25.10", + "resolved": "https://registry.npmjs.org/esbuild/-/esbuild-0.25.10.tgz", + "integrity": "sha512-9RiGKvCwaqxO2owP61uQ4BgNborAQskMR6QusfWzQqv7AZOg5oGehdY2pRJMTKuwxd1IDBP4rSbI5lHzU7SMsQ==", + "hasInstallScript": true, + "license": "MIT", + "bin": { + "esbuild": "bin/esbuild" + }, + "engines": { + "node": ">=18" + }, + "optionalDependencies": { + "@esbuild/aix-ppc64": "0.25.10", + "@esbuild/android-arm": "0.25.10", + "@esbuild/android-arm64": "0.25.10", + "@esbuild/android-x64": "0.25.10", + "@esbuild/darwin-arm64": "0.25.10", + "@esbuild/darwin-x64": "0.25.10", + "@esbuild/freebsd-arm64": "0.25.10", + "@esbuild/freebsd-x64": "0.25.10", + "@esbuild/linux-arm": "0.25.10", + "@esbuild/linux-arm64": "0.25.10", + "@esbuild/linux-ia32": "0.25.10", + "@esbuild/linux-loong64": "0.25.10", + "@esbuild/linux-mips64el": "0.25.10", + "@esbuild/linux-ppc64": "0.25.10", + "@esbuild/linux-riscv64": "0.25.10", + "@esbuild/linux-s390x": "0.25.10", + "@esbuild/linux-x64": "0.25.10", + "@esbuild/netbsd-arm64": "0.25.10", + "@esbuild/netbsd-x64": "0.25.10", + "@esbuild/openbsd-arm64": "0.25.10", + "@esbuild/openbsd-x64": "0.25.10", + "@esbuild/openharmony-arm64": "0.25.10", + "@esbuild/sunos-x64": "0.25.10", + "@esbuild/win32-arm64": "0.25.10", + "@esbuild/win32-ia32": "0.25.10", + "@esbuild/win32-x64": "0.25.10" + } + }, + "node_modules/escalade": { + "version": "3.2.0", + "resolved": "https://registry.npmjs.org/escalade/-/escalade-3.2.0.tgz", + "integrity": "sha512-WUj2qlxaQtO4g6Pq5c29GTcWGDyd8itL8zTlipgECz3JesAiiOKotd8JU6otB3PACgG6xkJUyVhboMS+bje/jA==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=6" + } + }, + "node_modules/escape-string-regexp": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/escape-string-regexp/-/escape-string-regexp-4.0.0.tgz", + "integrity": "sha512-TtpcNJ3XAzx3Gq8sWRzJaVajRs0uVxA2YAkdb1jm2YkPz4G6egUFAyA3n5vtEIZefPk5Wa4UXbKuS5fKkJWdgA==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=10" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/eslint": { + "version": "9.37.0", + "resolved": "https://registry.npmjs.org/eslint/-/eslint-9.37.0.tgz", + "integrity": "sha512-XyLmROnACWqSxiGYArdef1fItQd47weqB7iwtfr9JHwRrqIXZdcFMvvEcL9xHCmL0SNsOvF0c42lWyM1U5dgig==", + "dev": true, + "license": "MIT", + "dependencies": { + "@eslint-community/eslint-utils": "^4.8.0", + "@eslint-community/regexpp": "^4.12.1", + "@eslint/config-array": "^0.21.0", + "@eslint/config-helpers": "^0.4.0", + "@eslint/core": "^0.16.0", + "@eslint/eslintrc": "^3.3.1", + "@eslint/js": "9.37.0", + "@eslint/plugin-kit": "^0.4.0", + "@humanfs/node": "^0.16.6", + "@humanwhocodes/module-importer": "^1.0.1", + "@humanwhocodes/retry": "^0.4.2", + "@types/estree": "^1.0.6", + "@types/json-schema": "^7.0.15", + "ajv": "^6.12.4", + "chalk": "^4.0.0", + "cross-spawn": "^7.0.6", + "debug": "^4.3.2", + "escape-string-regexp": "^4.0.0", + "eslint-scope": "^8.4.0", + "eslint-visitor-keys": "^4.2.1", + "espree": "^10.4.0", + "esquery": "^1.5.0", + "esutils": "^2.0.2", + "fast-deep-equal": "^3.1.3", + "file-entry-cache": "^8.0.0", + "find-up": "^5.0.0", + "glob-parent": "^6.0.2", + "ignore": "^5.2.0", + "imurmurhash": "^0.1.4", + "is-glob": "^4.0.0", + "json-stable-stringify-without-jsonify": "^1.0.1", + "lodash.merge": "^4.6.2", + "minimatch": "^3.1.2", + "natural-compare": "^1.4.0", + "optionator": "^0.9.3" + }, + "bin": { + "eslint": "bin/eslint.js" + }, + "engines": { + "node": "^18.18.0 || ^20.9.0 || >=21.1.0" + }, + "funding": { + "url": "https://eslint.org/donate" + }, + "peerDependencies": { + "jiti": "*" + }, + "peerDependenciesMeta": { + "jiti": { + "optional": true + } + } + }, + "node_modules/eslint-plugin-react-hooks": { + "version": "5.2.0", + "resolved": "https://registry.npmjs.org/eslint-plugin-react-hooks/-/eslint-plugin-react-hooks-5.2.0.tgz", + "integrity": "sha512-+f15FfK64YQwZdJNELETdn5ibXEUQmW1DZL6KXhNnc2heoy/sg9VJJeT7n8TlMWouzWqSWavFkIhHyIbIAEapg==", + "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 || ^9.0.0" + } + }, + "node_modules/eslint-plugin-react-refresh": { + "version": "0.4.23", + "resolved": "https://registry.npmjs.org/eslint-plugin-react-refresh/-/eslint-plugin-react-refresh-0.4.23.tgz", + "integrity": "sha512-G4j+rv0NmbIR45kni5xJOrYvCtyD3/7LjpVH8MPPcudXDcNu8gv+4ATTDXTtbRR8rTCM5HxECvCSsRmxKnWDsA==", + "dev": true, + "license": "MIT", + "peerDependencies": { + "eslint": ">=8.40" + } + }, + "node_modules/eslint-scope": { + "version": "8.4.0", + "resolved": "https://registry.npmjs.org/eslint-scope/-/eslint-scope-8.4.0.tgz", + "integrity": "sha512-sNXOfKCn74rt8RICKMvJS7XKV/Xk9kA7DyJr8mJik3S7Cwgy3qlkkmyS2uQB3jiJg6VNdZd/pDBJu0nvG2NlTg==", + "dev": true, + "license": "BSD-2-Clause", + "dependencies": { + "esrecurse": "^4.3.0", + "estraverse": "^5.2.0" + }, + "engines": { + "node": "^18.18.0 || ^20.9.0 || >=21.1.0" + }, + "funding": { + "url": "https://opencollective.com/eslint" + } + }, + "node_modules/eslint-visitor-keys": { + "version": "4.2.1", + "resolved": "https://registry.npmjs.org/eslint-visitor-keys/-/eslint-visitor-keys-4.2.1.tgz", + "integrity": "sha512-Uhdk5sfqcee/9H/rCOJikYz67o0a2Tw2hGRPOG2Y1R2dg7brRe1uG0yaNQDHu+TO/uQPF/5eCapvYSmHUjt7JQ==", + "dev": true, + "license": "Apache-2.0", + "engines": { + "node": "^18.18.0 || ^20.9.0 || >=21.1.0" + }, + "funding": { + "url": "https://opencollective.com/eslint" + } + }, + "node_modules/espree": { + "version": "10.4.0", + "resolved": "https://registry.npmjs.org/espree/-/espree-10.4.0.tgz", + "integrity": "sha512-j6PAQ2uUr79PZhBjP5C5fhl8e39FmRnOjsD5lGnWrFU8i2G776tBK7+nP8KuQUTTyAZUwfQqXAgrVH5MbH9CYQ==", + "dev": true, + "license": "BSD-2-Clause", + "dependencies": { + "acorn": "^8.15.0", + "acorn-jsx": "^5.3.2", + "eslint-visitor-keys": "^4.2.1" + }, + "engines": { + "node": "^18.18.0 || ^20.9.0 || >=21.1.0" + }, + "funding": { + "url": "https://opencollective.com/eslint" + } + }, + "node_modules/esquery": { + "version": "1.6.0", + "resolved": "https://registry.npmjs.org/esquery/-/esquery-1.6.0.tgz", + "integrity": "sha512-ca9pw9fomFcKPvFLXhBKUK90ZvGibiGOvRJNbjljY7s7uq/5YO4BOzcYtJqExdx99rF6aAcnRxHmcUHcz6sQsg==", + "dev": true, + "license": "BSD-3-Clause", + "dependencies": { + "estraverse": "^5.1.0" + }, + "engines": { + "node": ">=0.10" + } + }, + "node_modules/esrecurse": { + "version": "4.3.0", + "resolved": "https://registry.npmjs.org/esrecurse/-/esrecurse-4.3.0.tgz", + "integrity": "sha512-KmfKL3b6G+RXvP8N1vr3Tq1kL/oCFgn2NYXEtqP8/L3pKapUA4G8cFVaoF3SU323CD4XypR/ffioHmkti6/Tag==", + "dev": true, + "license": "BSD-2-Clause", + "dependencies": { + "estraverse": "^5.2.0" + }, + "engines": { + "node": ">=4.0" + } + }, + "node_modules/estraverse": { + "version": "5.3.0", + "resolved": "https://registry.npmjs.org/estraverse/-/estraverse-5.3.0.tgz", + "integrity": "sha512-MMdARuVEQziNTeJD8DgMqmhwR11BRQ/cBP+pLtYdSTnf3MIO8fFeiINEbX36ZdNlfU/7A9f3gUw49B3oQsvwBA==", + "dev": true, + "license": "BSD-2-Clause", + "engines": { + "node": ">=4.0" + } + }, + "node_modules/esutils": { + "version": "2.0.3", + "resolved": "https://registry.npmjs.org/esutils/-/esutils-2.0.3.tgz", + "integrity": "sha512-kVscqXk4OCp68SZ0dkgEKVi6/8ij300KBWTJq32P/dYeWTSwK41WyTxalN1eRmA5Z9UU/LX9D7FWSmV9SAYx6g==", + "dev": true, + "license": "BSD-2-Clause", + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/fast-deep-equal": { + "version": "3.1.3", + "resolved": "https://registry.npmjs.org/fast-deep-equal/-/fast-deep-equal-3.1.3.tgz", + "integrity": "sha512-f3qQ9oQy9j2AhBe/H9VC91wLmKBCCU/gDOnKNAYG5hswO7BLKj09Hc5HYNz9cGI++xlpDCIgDaitVs03ATR84Q==", + "dev": true, + "license": "MIT" + }, + "node_modules/fast-glob": { + "version": "3.3.3", + "resolved": "https://registry.npmjs.org/fast-glob/-/fast-glob-3.3.3.tgz", + "integrity": "sha512-7MptL8U0cqcFdzIzwOTHoilX9x5BrNqye7Z/LuC7kCMRio1EMSyqRK3BEAUD7sXRq4iT4AzTVuZdhgQ2TCvYLg==", + "dev": true, + "license": "MIT", + "dependencies": { + "@nodelib/fs.stat": "^2.0.2", + "@nodelib/fs.walk": "^1.2.3", + "glob-parent": "^5.1.2", + "merge2": "^1.3.0", + "micromatch": "^4.0.8" + }, + "engines": { + "node": ">=8.6.0" + } + }, + "node_modules/fast-glob/node_modules/glob-parent": { + "version": "5.1.2", + "resolved": "https://registry.npmjs.org/glob-parent/-/glob-parent-5.1.2.tgz", + "integrity": "sha512-AOIgSQCepiJYwP3ARnGx+5VnTu2HBYdzbGP45eLw1vr3zB3vZLeyed1sC9hnbcOc9/SrMyM5RPQrkGz4aS9Zow==", + "dev": true, + "license": "ISC", + "dependencies": { + "is-glob": "^4.0.1" + }, + "engines": { + "node": ">= 6" + } + }, + "node_modules/fast-json-stable-stringify": { + "version": "2.1.0", + "resolved": "https://registry.npmjs.org/fast-json-stable-stringify/-/fast-json-stable-stringify-2.1.0.tgz", + "integrity": "sha512-lhd/wF+Lk98HZoTCtlVraHtfh5XYijIjalXck7saUtuanSDyLMxnHhSXEDJqHxD7msR8D0uCmqlkwjCV8xvwHw==", + "dev": true, + "license": "MIT" + }, + "node_modules/fast-levenshtein": { + "version": "2.0.6", + "resolved": "https://registry.npmjs.org/fast-levenshtein/-/fast-levenshtein-2.0.6.tgz", + "integrity": "sha512-DCXu6Ifhqcks7TZKY3Hxp3y6qphY5SJZmrWMDrKcERSOXWQdMhU9Ig/PYrzyw/ul9jOIyh0N4M0tbC5hodg8dw==", + "dev": true, + "license": "MIT" + }, + "node_modules/fastq": { + "version": "1.19.1", + "resolved": "https://registry.npmjs.org/fastq/-/fastq-1.19.1.tgz", + "integrity": "sha512-GwLTyxkCXjXbxqIhTsMI2Nui8huMPtnxg7krajPJAjnEG/iiOS7i+zCtWGZR9G0NBKbXKh6X9m9UIsYX/N6vvQ==", + "dev": true, + "license": "ISC", + "dependencies": { + "reusify": "^1.0.4" + } + }, + "node_modules/file-entry-cache": { + "version": "8.0.0", + "resolved": "https://registry.npmjs.org/file-entry-cache/-/file-entry-cache-8.0.0.tgz", + "integrity": "sha512-XXTUwCvisa5oacNGRP9SfNtYBNAMi+RPwBFmblZEF7N7swHYQS6/Zfk7SRwx4D5j3CH211YNRco1DEMNVfZCnQ==", + "dev": true, + "license": "MIT", + "dependencies": { + "flat-cache": "^4.0.0" + }, + "engines": { + "node": ">=16.0.0" + } + }, + "node_modules/fill-range": { + "version": "7.1.1", + "resolved": "https://registry.npmjs.org/fill-range/-/fill-range-7.1.1.tgz", + "integrity": "sha512-YsGpe3WHLK8ZYi4tWDg2Jy3ebRz2rXowDxnld4bkQB00cc/1Zw9AWnC0i9ztDJitivtQvaI9KaLyKrc+hBW0yg==", + "dev": true, + "license": "MIT", + "dependencies": { + "to-regex-range": "^5.0.1" + }, + "engines": { + "node": ">=8" + } + }, + "node_modules/find-up": { + "version": "5.0.0", + "resolved": "https://registry.npmjs.org/find-up/-/find-up-5.0.0.tgz", + "integrity": "sha512-78/PXT1wlLLDgTzDs7sjq9hzz0vXD+zn+7wypEe4fXQxCmdmqfGsEPQxmiCSQI3ajFV91bVSsvNtrJRiW6nGng==", + "dev": true, + "license": "MIT", + "dependencies": { + "locate-path": "^6.0.0", + "path-exists": "^4.0.0" + }, + "engines": { + "node": ">=10" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/flat-cache": { + "version": "4.0.1", + "resolved": "https://registry.npmjs.org/flat-cache/-/flat-cache-4.0.1.tgz", + "integrity": "sha512-f7ccFPK3SXFHpx15UIGyRJ/FJQctuKZ0zVuN3frBo4HnK3cay9VEW0R6yPYFHC0AgqhukPzKjq22t5DmAyqGyw==", + "dev": true, + "license": "MIT", + "dependencies": { + "flatted": "^3.2.9", + "keyv": "^4.5.4" + }, + "engines": { + "node": ">=16" + } + }, + "node_modules/flatted": { + "version": "3.3.3", + "resolved": "https://registry.npmjs.org/flatted/-/flatted-3.3.3.tgz", + "integrity": "sha512-GX+ysw4PBCz0PzosHDepZGANEuFCMLrnRTiEy9McGjmkCQYwRq4A/X786G/fjM/+OjsWSU1ZrY5qyARZmO/uwg==", + "dev": true, + "license": "ISC" + }, + "node_modules/fsevents": { + "version": "2.3.3", + "resolved": "https://registry.npmjs.org/fsevents/-/fsevents-2.3.3.tgz", + "integrity": "sha512-5xoDfX+fL7faATnagmWPpbFtwh/R77WmMMqqHGS65C3vvB0YHrgF+B1YmZ3441tMj5n63k0212XNoJwzlhffQw==", + "hasInstallScript": true, + "license": "MIT", + "optional": true, + "os": [ + "darwin" + ], + "engines": { + "node": "^8.16.0 || ^10.6.0 || >=11.0.0" + } + }, + "node_modules/gensync": { + "version": "1.0.0-beta.2", + "resolved": "https://registry.npmjs.org/gensync/-/gensync-1.0.0-beta.2.tgz", + "integrity": "sha512-3hN7NaskYvMDLQY55gnW3NQ+mesEAepTqlg+VEbj7zzqEMBVNhzcGYYeqFo/TlYz6eQiFcp1HcsCZO+nGgS8zg==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=6.9.0" + } + }, + "node_modules/glob-parent": { + "version": "6.0.2", + "resolved": "https://registry.npmjs.org/glob-parent/-/glob-parent-6.0.2.tgz", + "integrity": "sha512-XxwI8EOhVQgWp6iDL+3b0r86f4d6AX6zSU55HfB4ydCEuXLXc5FcYeOu+nnGftS4TEju/11rt4KJPTMgbfmv4A==", + "dev": true, + "license": "ISC", + "dependencies": { + "is-glob": "^4.0.3" + }, + "engines": { + "node": ">=10.13.0" + } + }, + "node_modules/globals": { + "version": "16.4.0", + "resolved": "https://registry.npmjs.org/globals/-/globals-16.4.0.tgz", + "integrity": "sha512-ob/2LcVVaVGCYN+r14cnwnoDPUufjiYgSqRhiFD0Q1iI4Odora5RE8Iv1D24hAz5oMophRGkGz+yuvQmmUMnMw==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=18" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/graceful-fs": { + "version": "4.2.11", + "resolved": "https://registry.npmjs.org/graceful-fs/-/graceful-fs-4.2.11.tgz", + "integrity": "sha512-RbJ5/jmFcNNCcDV5o9eTnBLJ/HszWV0P73bc+Ff4nS/rJj+YaS6IGyiOL0VoBYX+l1Wrl3k63h/KrH+nhJ0XvQ==", + "license": "ISC" + }, + "node_modules/graphemer": { + "version": "1.4.0", + "resolved": "https://registry.npmjs.org/graphemer/-/graphemer-1.4.0.tgz", + "integrity": "sha512-EtKwoO6kxCL9WO5xipiHTZlSzBm7WLT627TqC/uVRd0HKmq8NXyebnNYxDoBi7wt8eTWrUrKXCOVaFq9x1kgag==", + "dev": true, + "license": "MIT" + }, + "node_modules/has-flag": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/has-flag/-/has-flag-4.0.0.tgz", + "integrity": "sha512-EykJT/Q1KjTWctppgIAgfSO0tKVuZUjhgMr17kqTumMl6Afv3EISleU7qZUzoXDFTAHTDC4NOoG/ZxU3EvlMPQ==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=8" + } + }, + "node_modules/html-parse-stringify": { + "version": "3.0.1", + "resolved": "https://registry.npmjs.org/html-parse-stringify/-/html-parse-stringify-3.0.1.tgz", + "integrity": "sha512-KknJ50kTInJ7qIScF3jeaFRpMpE8/lfiTdzf/twXyPBLAGrLRTmkz3AdTnKeh40X8k9L2fdYwEp/42WGXIRGcg==", + "license": "MIT", + "dependencies": { + "void-elements": "3.1.0" + } + }, + "node_modules/i18next": { + "version": "25.5.3", + "resolved": "https://registry.npmjs.org/i18next/-/i18next-25.5.3.tgz", + "integrity": "sha512-joFqorDeQ6YpIXni944upwnuHBf5IoPMuqAchGVeQLdWC2JOjxgM9V8UGLhNIIH/Q8QleRxIi0BSRQehSrDLcg==", + "funding": [ + { + "type": "individual", + "url": "https://locize.com" + }, + { + "type": "individual", + "url": "https://locize.com/i18next.html" + }, + { + "type": "individual", + "url": "https://www.i18next.com/how-to/faq#i18next-is-awesome.-how-can-i-support-the-project" + } + ], + "license": "MIT", + "peer": true, + "dependencies": { + "@babel/runtime": "^7.27.6" + }, + "peerDependencies": { + "typescript": "^5" + }, + "peerDependenciesMeta": { + "typescript": { + "optional": true + } + } + }, + "node_modules/ignore": { + "version": "5.3.2", + "resolved": "https://registry.npmjs.org/ignore/-/ignore-5.3.2.tgz", + "integrity": "sha512-hsBTNUqQTDwkWtcdYI2i06Y/nUBEsNEDJKjWdigLvegy8kDuJAS8uRlpkkcQpyEXL0Z/pjDy5HBmMjRCJ2gq+g==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">= 4" + } + }, + "node_modules/import-fresh": { + "version": "3.3.1", + "resolved": "https://registry.npmjs.org/import-fresh/-/import-fresh-3.3.1.tgz", + "integrity": "sha512-TR3KfrTZTYLPB6jUjfx6MF9WcWrHL9su5TObK4ZkYgBdWKPOFoSoQIdEuTuR82pmtxH2spWG9h6etwfr1pLBqQ==", + "dev": true, + "license": "MIT", + "dependencies": { + "parent-module": "^1.0.0", + "resolve-from": "^4.0.0" + }, + "engines": { + "node": ">=6" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/imurmurhash": { + "version": "0.1.4", + "resolved": "https://registry.npmjs.org/imurmurhash/-/imurmurhash-0.1.4.tgz", + "integrity": "sha512-JmXMZ6wuvDmLiHEml9ykzqO6lwFbof0GG4IkcGaENdCRDDmMVnny7s5HsIgHCbaq0w2MyPhDqkhTUgS2LU2PHA==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=0.8.19" + } + }, + "node_modules/is-extglob": { + "version": "2.1.1", + "resolved": "https://registry.npmjs.org/is-extglob/-/is-extglob-2.1.1.tgz", + "integrity": "sha512-SbKbANkN603Vi4jEZv49LeVJMn4yGwsbzZworEoyEiutsN3nJYdbO36zfhGJ6QEDpOZIFkDtnq5JRxmvl3jsoQ==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/is-glob": { + "version": "4.0.3", + "resolved": "https://registry.npmjs.org/is-glob/-/is-glob-4.0.3.tgz", + "integrity": "sha512-xelSayHH36ZgE7ZWhli7pW34hNbNl8Ojv5KVmkJD4hBdD3th8Tfk9vYasLM+mXWOZhFkgZfxhLSnrwRr4elSSg==", + "dev": true, + "license": "MIT", + "dependencies": { + "is-extglob": "^2.1.1" + }, + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/is-number": { + "version": "7.0.0", + "resolved": "https://registry.npmjs.org/is-number/-/is-number-7.0.0.tgz", + "integrity": "sha512-41Cifkg6e8TylSpdtTpeLVMqvSBEVzTttHvERD741+pnZ8ANv0004MRL43QKPDlK9cGvNp6NZWZUBlbGXYxxng==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=0.12.0" + } + }, + "node_modules/isexe": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/isexe/-/isexe-2.0.0.tgz", + "integrity": "sha512-RHxMLp9lnKHGHRng9QFhRCMbYAcVpn69smSGcq3f36xjgVVWThj4qqLbTLlq7Ssj8B+fIQ1EuCEGI2lKsyQeIw==", + "dev": true, + "license": "ISC" + }, + "node_modules/jiti": { + "version": "2.6.1", + "resolved": "https://registry.npmjs.org/jiti/-/jiti-2.6.1.tgz", + "integrity": "sha512-ekilCSN1jwRvIbgeg/57YFh8qQDNbwDb9xT/qu2DAHbFFZUicIl4ygVaAvzveMhMVr3LnpSKTNnwt8PoOfmKhQ==", + "license": "MIT", + "bin": { + "jiti": "lib/jiti-cli.mjs" + } + }, + "node_modules/js-tokens": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/js-tokens/-/js-tokens-4.0.0.tgz", + "integrity": "sha512-RdJUflcE3cUzKiMqQgsCu06FPu9UdIJO0beYbPhHN4k6apgJtifcoCtT9bcxOpYBtpD2kCM6Sbzg4CausW/PKQ==", + "dev": true, + "license": "MIT" + }, + "node_modules/js-yaml": { + "version": "4.1.0", + "resolved": "https://registry.npmjs.org/js-yaml/-/js-yaml-4.1.0.tgz", + "integrity": "sha512-wpxZs9NoxZaJESJGIZTyDEaYpl0FKSA+FB9aJiyemKhMwkxQg63h4T1KJgUGHpTqPDNRcmmYLugrRjJlBtWvRA==", + "dev": true, + "license": "MIT", + "dependencies": { + "argparse": "^2.0.1" + }, + "bin": { + "js-yaml": "bin/js-yaml.js" + } + }, + "node_modules/jsesc": { + "version": "3.1.0", + "resolved": "https://registry.npmjs.org/jsesc/-/jsesc-3.1.0.tgz", + "integrity": "sha512-/sM3dO2FOzXjKQhJuo0Q173wf2KOo8t4I8vHy6lF9poUp7bKT0/NHE8fPX23PwfhnykfqnC2xRxOnVw5XuGIaA==", + "dev": true, + "license": "MIT", + "bin": { + "jsesc": "bin/jsesc" + }, + "engines": { + "node": ">=6" + } + }, + "node_modules/json-buffer": { + "version": "3.0.1", + "resolved": "https://registry.npmjs.org/json-buffer/-/json-buffer-3.0.1.tgz", + "integrity": "sha512-4bV5BfR2mqfQTJm+V5tPPdf+ZpuhiIvTuAB5g8kcrXOZpTT/QwwVRWBywX1ozr6lEuPdbHxwaJlm9G6mI2sfSQ==", + "dev": true, + "license": "MIT" + }, + "node_modules/json-schema-traverse": { + "version": "0.4.1", + "resolved": "https://registry.npmjs.org/json-schema-traverse/-/json-schema-traverse-0.4.1.tgz", + "integrity": "sha512-xbbCH5dCYU5T8LcEhhuh7HJ88HXuW3qsI3Y0zOZFKfZEHcpWiHU/Jxzk629Brsab/mMiHQti9wMP+845RPe3Vg==", + "dev": true, + "license": "MIT" + }, + "node_modules/json-stable-stringify-without-jsonify": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/json-stable-stringify-without-jsonify/-/json-stable-stringify-without-jsonify-1.0.1.tgz", + "integrity": "sha512-Bdboy+l7tA3OGW6FjyFHWkP5LuByj1Tk33Ljyq0axyzdk9//JSi2u3fP1QSmd1KNwq6VOKYGlAu87CisVir6Pw==", + "dev": true, + "license": "MIT" + }, + "node_modules/json5": { + "version": "2.2.3", + "resolved": "https://registry.npmjs.org/json5/-/json5-2.2.3.tgz", + "integrity": "sha512-XmOWe7eyHYH14cLdVPoyg+GOH3rYX++KpzrylJwSW98t3Nk+U8XOl8FWKOgwtzdb8lXGf6zYwDUzeHMWfxasyg==", + "dev": true, + "license": "MIT", + "bin": { + "json5": "lib/cli.js" + }, + "engines": { + "node": ">=6" + } + }, + "node_modules/keyv": { + "version": "4.5.4", + "resolved": "https://registry.npmjs.org/keyv/-/keyv-4.5.4.tgz", + "integrity": "sha512-oxVHkHR/EJf2CNXnWxRLW6mg7JyCCUcG0DtEGmL2ctUo1PNTin1PUil+r/+4r5MpVgC/fn1kjsx7mjSujKqIpw==", + "dev": true, + "license": "MIT", + "dependencies": { + "json-buffer": "3.0.1" + } + }, + "node_modules/levn": { + "version": "0.4.1", + "resolved": "https://registry.npmjs.org/levn/-/levn-0.4.1.tgz", + "integrity": "sha512-+bT2uH4E5LGE7h/n3evcS/sQlJXCpIp6ym8OWJ5eV6+67Dsql/LaaT7qJBAt2rzfoa/5QBGBhxDix1dMt2kQKQ==", + "dev": true, + "license": "MIT", + "dependencies": { + "prelude-ls": "^1.2.1", + "type-check": "~0.4.0" + }, + "engines": { + "node": ">= 0.8.0" + } + }, + "node_modules/lightningcss": { + "version": "1.30.1", + "resolved": "https://registry.npmjs.org/lightningcss/-/lightningcss-1.30.1.tgz", + "integrity": "sha512-xi6IyHML+c9+Q3W0S4fCQJOym42pyurFiJUHEcEyHS0CeKzia4yZDEsLlqOFykxOdHpNy0NmvVO31vcSqAxJCg==", + "license": "MPL-2.0", + "dependencies": { + "detect-libc": "^2.0.3" + }, + "engines": { + "node": ">= 12.0.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/parcel" + }, + "optionalDependencies": { + "lightningcss-darwin-arm64": "1.30.1", + "lightningcss-darwin-x64": "1.30.1", + "lightningcss-freebsd-x64": "1.30.1", + "lightningcss-linux-arm-gnueabihf": "1.30.1", + "lightningcss-linux-arm64-gnu": "1.30.1", + "lightningcss-linux-arm64-musl": "1.30.1", + "lightningcss-linux-x64-gnu": "1.30.1", + "lightningcss-linux-x64-musl": "1.30.1", + "lightningcss-win32-arm64-msvc": "1.30.1", + "lightningcss-win32-x64-msvc": "1.30.1" + } + }, + "node_modules/lightningcss-darwin-arm64": { + "version": "1.30.1", + "resolved": "https://registry.npmjs.org/lightningcss-darwin-arm64/-/lightningcss-darwin-arm64-1.30.1.tgz", + "integrity": "sha512-c8JK7hyE65X1MHMN+Viq9n11RRC7hgin3HhYKhrMyaXflk5GVplZ60IxyoVtzILeKr+xAJwg6zK6sjTBJ0FKYQ==", + "cpu": [ + "arm64" + ], + "license": "MPL-2.0", + "optional": true, + "os": [ + "darwin" + ], + "engines": { + "node": ">= 12.0.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/parcel" + } + }, + "node_modules/lightningcss-darwin-x64": { + "version": "1.30.1", + "resolved": "https://registry.npmjs.org/lightningcss-darwin-x64/-/lightningcss-darwin-x64-1.30.1.tgz", + "integrity": "sha512-k1EvjakfumAQoTfcXUcHQZhSpLlkAuEkdMBsI/ivWw9hL+7FtilQc0Cy3hrx0AAQrVtQAbMI7YjCgYgvn37PzA==", + "cpu": [ + "x64" + ], + "license": "MPL-2.0", + "optional": true, + "os": [ + "darwin" + ], + "engines": { + "node": ">= 12.0.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/parcel" + } + }, + "node_modules/lightningcss-freebsd-x64": { + "version": "1.30.1", + "resolved": "https://registry.npmjs.org/lightningcss-freebsd-x64/-/lightningcss-freebsd-x64-1.30.1.tgz", + "integrity": "sha512-kmW6UGCGg2PcyUE59K5r0kWfKPAVy4SltVeut+umLCFoJ53RdCUWxcRDzO1eTaxf/7Q2H7LTquFHPL5R+Gjyig==", + "cpu": [ + "x64" + ], + "license": "MPL-2.0", + "optional": true, + "os": [ + "freebsd" + ], + "engines": { + "node": ">= 12.0.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/parcel" + } + }, + "node_modules/lightningcss-linux-arm-gnueabihf": { + "version": "1.30.1", + "resolved": "https://registry.npmjs.org/lightningcss-linux-arm-gnueabihf/-/lightningcss-linux-arm-gnueabihf-1.30.1.tgz", + "integrity": "sha512-MjxUShl1v8pit+6D/zSPq9S9dQ2NPFSQwGvxBCYaBYLPlCWuPh9/t1MRS8iUaR8i+a6w7aps+B4N0S1TYP/R+Q==", + "cpu": [ + "arm" + ], + "license": "MPL-2.0", + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": ">= 12.0.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/parcel" + } + }, + "node_modules/lightningcss-linux-arm64-gnu": { + "version": "1.30.1", + "resolved": "https://registry.npmjs.org/lightningcss-linux-arm64-gnu/-/lightningcss-linux-arm64-gnu-1.30.1.tgz", + "integrity": "sha512-gB72maP8rmrKsnKYy8XUuXi/4OctJiuQjcuqWNlJQ6jZiWqtPvqFziskH3hnajfvKB27ynbVCucKSm2rkQp4Bw==", + "cpu": [ + "arm64" + ], + "license": "MPL-2.0", + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": ">= 12.0.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/parcel" + } + }, + "node_modules/lightningcss-linux-arm64-musl": { + "version": "1.30.1", + "resolved": "https://registry.npmjs.org/lightningcss-linux-arm64-musl/-/lightningcss-linux-arm64-musl-1.30.1.tgz", + "integrity": "sha512-jmUQVx4331m6LIX+0wUhBbmMX7TCfjF5FoOH6SD1CttzuYlGNVpA7QnrmLxrsub43ClTINfGSYyHe2HWeLl5CQ==", + "cpu": [ + "arm64" + ], + "license": "MPL-2.0", + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": ">= 12.0.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/parcel" + } + }, + "node_modules/lightningcss-linux-x64-gnu": { + "version": "1.30.1", + "resolved": "https://registry.npmjs.org/lightningcss-linux-x64-gnu/-/lightningcss-linux-x64-gnu-1.30.1.tgz", + "integrity": "sha512-piWx3z4wN8J8z3+O5kO74+yr6ze/dKmPnI7vLqfSqI8bccaTGY5xiSGVIJBDd5K5BHlvVLpUB3S2YCfelyJ1bw==", + "cpu": [ + "x64" + ], + "license": "MPL-2.0", + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": ">= 12.0.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/parcel" + } + }, + "node_modules/lightningcss-linux-x64-musl": { + "version": "1.30.1", + "resolved": "https://registry.npmjs.org/lightningcss-linux-x64-musl/-/lightningcss-linux-x64-musl-1.30.1.tgz", + "integrity": "sha512-rRomAK7eIkL+tHY0YPxbc5Dra2gXlI63HL+v1Pdi1a3sC+tJTcFrHX+E86sulgAXeI7rSzDYhPSeHHjqFhqfeQ==", + "cpu": [ + "x64" + ], + "license": "MPL-2.0", + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": ">= 12.0.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/parcel" + } + }, + "node_modules/lightningcss-win32-arm64-msvc": { + "version": "1.30.1", + "resolved": "https://registry.npmjs.org/lightningcss-win32-arm64-msvc/-/lightningcss-win32-arm64-msvc-1.30.1.tgz", + "integrity": "sha512-mSL4rqPi4iXq5YVqzSsJgMVFENoa4nGTT/GjO2c0Yl9OuQfPsIfncvLrEW6RbbB24WtZ3xP/2CCmI3tNkNV4oA==", + "cpu": [ + "arm64" + ], + "license": "MPL-2.0", + "optional": true, + "os": [ + "win32" + ], + "engines": { + "node": ">= 12.0.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/parcel" + } + }, + "node_modules/lightningcss-win32-x64-msvc": { + "version": "1.30.1", + "resolved": "https://registry.npmjs.org/lightningcss-win32-x64-msvc/-/lightningcss-win32-x64-msvc-1.30.1.tgz", + "integrity": "sha512-PVqXh48wh4T53F/1CCu8PIPCxLzWyCnn/9T5W1Jpmdy5h9Cwd+0YQS6/LwhHXSafuc61/xg9Lv5OrCby6a++jg==", + "cpu": [ + "x64" + ], + "license": "MPL-2.0", + "optional": true, + "os": [ + "win32" + ], + "engines": { + "node": ">= 12.0.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/parcel" + } + }, + "node_modules/locate-path": { + "version": "6.0.0", + "resolved": "https://registry.npmjs.org/locate-path/-/locate-path-6.0.0.tgz", + "integrity": "sha512-iPZK6eYjbxRu3uB4/WZ3EsEIMJFMqAoopl3R+zuq0UjcAm/MO6KCweDgPfP3elTztoKP3KtnVHxTn2NHBSDVUw==", + "dev": true, + "license": "MIT", + "dependencies": { + "p-locate": "^5.0.0" + }, + "engines": { + "node": ">=10" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/lodash.merge": { + "version": "4.6.2", + "resolved": "https://registry.npmjs.org/lodash.merge/-/lodash.merge-4.6.2.tgz", + "integrity": "sha512-0KpjqXRVvrYyCsX1swR/XTK0va6VQkQM6MNo7PqW77ByjAhoARA8EfrP1N4+KlKj8YS0ZUCtRT/YUuhyYDujIQ==", + "dev": true, + "license": "MIT" + }, + "node_modules/lru-cache": { + "version": "5.1.1", + "resolved": "https://registry.npmjs.org/lru-cache/-/lru-cache-5.1.1.tgz", + "integrity": "sha512-KpNARQA3Iwv+jTA0utUVVbrh+Jlrr1Fv0e56GGzAFOXN7dk/FviaDW8LHmK52DlcH4WP2n6gI8vN1aesBFgo9w==", + "dev": true, + "license": "ISC", + "dependencies": { + "yallist": "^3.0.2" + } + }, + "node_modules/magic-string": { + "version": "0.30.19", + "resolved": "https://registry.npmjs.org/magic-string/-/magic-string-0.30.19.tgz", + "integrity": "sha512-2N21sPY9Ws53PZvsEpVtNuSW+ScYbQdp4b9qUaL+9QkHUrGFKo56Lg9Emg5s9V/qrtNBmiR01sYhUOwu3H+VOw==", + "license": "MIT", + "dependencies": { + "@jridgewell/sourcemap-codec": "^1.5.5" + } + }, + "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/micromatch": { + "version": "4.0.8", + "resolved": "https://registry.npmjs.org/micromatch/-/micromatch-4.0.8.tgz", + "integrity": "sha512-PXwfBhYu0hBCPw8Dn0E+WDYb7af3dSLVWKi3HGv84IdF4TyFoC0ysxFd0Goxw7nSv4T/PzEJQxsYsEiFCKo2BA==", + "dev": true, + "license": "MIT", + "dependencies": { + "braces": "^3.0.3", + "picomatch": "^2.3.1" + }, + "engines": { + "node": ">=8.6" + } + }, + "node_modules/minimatch": { + "version": "3.1.2", + "resolved": "https://registry.npmjs.org/minimatch/-/minimatch-3.1.2.tgz", + "integrity": "sha512-J7p63hRiAjw1NDEww1W7i37+ByIrOWO5XQQAzZ3VOcL0PNybwpfmV/N05zFAzwQ9USyEcX6t3UO+K5aqBQOIHw==", + "dev": true, + "license": "ISC", + "dependencies": { + "brace-expansion": "^1.1.7" + }, + "engines": { + "node": "*" + } + }, + "node_modules/minipass": { + "version": "7.1.2", + "resolved": "https://registry.npmjs.org/minipass/-/minipass-7.1.2.tgz", + "integrity": "sha512-qOOzS1cBTWYF4BH8fVePDBOO9iptMnGUEZwNc/cMWnTV2nVLZ7VoNWEPHkYczZA0pdoA7dl6e7FL659nX9S2aw==", + "license": "ISC", + "engines": { + "node": ">=16 || 14 >=14.17" + } + }, + "node_modules/minizlib": { + "version": "3.1.0", + "resolved": "https://registry.npmjs.org/minizlib/-/minizlib-3.1.0.tgz", + "integrity": "sha512-KZxYo1BUkWD2TVFLr0MQoM8vUUigWD3LlD83a/75BqC+4qE0Hb1Vo5v1FgcfaNXvfXzr+5EhQ6ing/CaBijTlw==", + "license": "MIT", + "dependencies": { + "minipass": "^7.1.2" + }, + "engines": { + "node": ">= 18" + } + }, + "node_modules/ms": { + "version": "2.1.3", + "resolved": "https://registry.npmjs.org/ms/-/ms-2.1.3.tgz", + "integrity": "sha512-6FlzubTLZG3J2a/NVCAleEhjzq5oxgHyaCU9yYXvcLsvoVaHJq/s5xXI6/XXP6tz7R9xAOtHnSO/tXtF3WRTlA==", + "dev": true, + "license": "MIT" + }, + "node_modules/nanoid": { + "version": "3.3.11", + "resolved": "https://registry.npmjs.org/nanoid/-/nanoid-3.3.11.tgz", + "integrity": "sha512-N8SpfPUnUp1bK+PMYW8qSWdl9U+wwNWI4QKxOYDy9JAro3WMX7p2OeVRF9v+347pnakNevPmiHhNmZ2HbFA76w==", + "funding": [ + { + "type": "github", + "url": "https://github.com/sponsors/ai" + } + ], + "license": "MIT", + "bin": { + "nanoid": "bin/nanoid.cjs" + }, + "engines": { + "node": "^10 || ^12 || ^13.7 || ^14 || >=15.0.1" + } + }, + "node_modules/natural-compare": { + "version": "1.4.0", + "resolved": "https://registry.npmjs.org/natural-compare/-/natural-compare-1.4.0.tgz", + "integrity": "sha512-OWND8ei3VtNC9h7V60qff3SVobHr996CTwgxubgyQYEpg290h9J0buyECNNJexkFm5sOajh5G116RYA1c8ZMSw==", + "dev": true, + "license": "MIT" + }, + "node_modules/node-releases": { + "version": "2.0.23", + "resolved": "https://registry.npmjs.org/node-releases/-/node-releases-2.0.23.tgz", + "integrity": "sha512-cCmFDMSm26S6tQSDpBCg/NR8NENrVPhAJSf+XbxBG4rPFaaonlEoE9wHQmun+cls499TQGSb7ZyPBRlzgKfpeg==", + "dev": true, + "license": "MIT" + }, + "node_modules/optionator": { + "version": "0.9.4", + "resolved": "https://registry.npmjs.org/optionator/-/optionator-0.9.4.tgz", + "integrity": "sha512-6IpQ7mKUxRcZNLIObR0hz7lxsapSSIYNZJwXPGeF0mTVqGKFIXj1DQcMoT22S3ROcLyY/rz0PWaWZ9ayWmad9g==", + "dev": true, + "license": "MIT", + "dependencies": { + "deep-is": "^0.1.3", + "fast-levenshtein": "^2.0.6", + "levn": "^0.4.1", + "prelude-ls": "^1.2.1", + "type-check": "^0.4.0", + "word-wrap": "^1.2.5" + }, + "engines": { + "node": ">= 0.8.0" + } + }, + "node_modules/p-limit": { + "version": "3.1.0", + "resolved": "https://registry.npmjs.org/p-limit/-/p-limit-3.1.0.tgz", + "integrity": "sha512-TYOanM3wGwNGsZN2cVTYPArw454xnXj5qmWF1bEoAc4+cU/ol7GVh7odevjp1FNHduHc3KZMcFduxU5Xc6uJRQ==", + "dev": true, + "license": "MIT", + "dependencies": { + "yocto-queue": "^0.1.0" + }, + "engines": { + "node": ">=10" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/p-locate": { + "version": "5.0.0", + "resolved": "https://registry.npmjs.org/p-locate/-/p-locate-5.0.0.tgz", + "integrity": "sha512-LaNjtRWUBY++zB5nE/NwcaoMylSPk+S+ZHNB1TzdbMJMny6dynpAGt7X/tl/QYq3TIeE6nxHppbo2LGymrG5Pw==", + "dev": true, + "license": "MIT", + "dependencies": { + "p-limit": "^3.0.2" + }, + "engines": { + "node": ">=10" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/parent-module": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/parent-module/-/parent-module-1.0.1.tgz", + "integrity": "sha512-GQ2EWRpQV8/o+Aw8YqtfZZPfNRWZYkbidE9k5rpl/hC3vtHHBfGm2Ifi6qWV+coDGkrUKZAxE3Lot5kcsRlh+g==", + "dev": true, + "license": "MIT", + "dependencies": { + "callsites": "^3.0.0" + }, + "engines": { + "node": ">=6" + } + }, + "node_modules/path-exists": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/path-exists/-/path-exists-4.0.0.tgz", + "integrity": "sha512-ak9Qy5Q7jYb2Wwcey5Fpvg2KoAc/ZIhLSLOSBmRmygPsGwkVVt0fZa0qrtMz+m6tJTAHfZQ8FnmB4MG4LWy7/w==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=8" + } + }, + "node_modules/path-key": { + "version": "3.1.1", + "resolved": "https://registry.npmjs.org/path-key/-/path-key-3.1.1.tgz", + "integrity": "sha512-ojmeN0qd+y0jszEtoY48r0Peq5dwMEkIlCOu6Q5f41lfkswXuKtYrhgoTpLnyIcHm24Uhqx+5Tqm2InSwLhE6Q==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=8" + } + }, + "node_modules/picocolors": { + "version": "1.1.1", + "resolved": "https://registry.npmjs.org/picocolors/-/picocolors-1.1.1.tgz", + "integrity": "sha512-xceH2snhtb5M9liqDsmEw56le376mTZkEX/jEb/RxNFyegNul7eNslCXP9FDj/Lcu0X8KEyMceP2ntpaHrDEVA==", + "license": "ISC" + }, + "node_modules/picomatch": { + "version": "2.3.1", + "resolved": "https://registry.npmjs.org/picomatch/-/picomatch-2.3.1.tgz", + "integrity": "sha512-JU3teHTNjmE2VCGFzuY8EXzCDVwEqB2a8fsIvwaStHhAWJEeVd1o1QD80CU6+ZdEXXSLbSsuLwJjkCBWqRQUVA==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=8.6" + }, + "funding": { + "url": "https://github.com/sponsors/jonschlinkert" + } + }, + "node_modules/postcss": { + "version": "8.5.6", + "resolved": "https://registry.npmjs.org/postcss/-/postcss-8.5.6.tgz", + "integrity": "sha512-3Ybi1tAuwAP9s0r1UQ2J4n5Y0G05bJkpUIO0/bI9MhwmD70S5aTWbXGBwxHrelT+XM1k6dM0pk+SwNkpTRN7Pg==", + "funding": [ + { + "type": "opencollective", + "url": "https://opencollective.com/postcss/" + }, + { + "type": "tidelift", + "url": "https://tidelift.com/funding/github/npm/postcss" + }, + { + "type": "github", + "url": "https://github.com/sponsors/ai" + } + ], + "license": "MIT", + "dependencies": { + "nanoid": "^3.3.11", + "picocolors": "^1.1.1", + "source-map-js": "^1.2.1" + }, + "engines": { + "node": "^10 || ^12 || >=14" + } + }, + "node_modules/prelude-ls": { + "version": "1.2.1", + "resolved": "https://registry.npmjs.org/prelude-ls/-/prelude-ls-1.2.1.tgz", + "integrity": "sha512-vkcDPrRZo1QZLbn5RLGPpg/WmIQ65qoWWhcGKf/b5eplkkarX0m9z8ppCat4mlOqUsWpyNuYgO3VRyrYHSzX5g==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">= 0.8.0" + } + }, + "node_modules/punycode": { + "version": "2.3.1", + "resolved": "https://registry.npmjs.org/punycode/-/punycode-2.3.1.tgz", + "integrity": "sha512-vYt7UD1U9Wg6138shLtLOvdAu+8DsC/ilFtEVHcH+wydcSpNE20AfSOduf6MkRFahL5FY7X1oU7nKVZFtfq8Fg==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=6" + } + }, + "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/react": { + "version": "19.2.0", + "resolved": "https://registry.npmjs.org/react/-/react-19.2.0.tgz", + "integrity": "sha512-tmbWg6W31tQLeB5cdIBOicJDJRR2KzXsV7uSK9iNfLWQ5bIZfxuPEHp7M8wiHyHnn0DD1i7w3Zmin0FtkrwoCQ==", + "license": "MIT", + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/react-dom": { + "version": "19.2.0", + "resolved": "https://registry.npmjs.org/react-dom/-/react-dom-19.2.0.tgz", + "integrity": "sha512-UlbRu4cAiGaIewkPyiRGJk0imDN2T3JjieT6spoL2UeSf5od4n5LB/mQ4ejmxhCFT1tYe8IvaFulzynWovsEFQ==", + "license": "MIT", + "dependencies": { + "scheduler": "^0.27.0" + }, + "peerDependencies": { + "react": "^19.2.0" + } + }, + "node_modules/react-i18next": { + "version": "16.0.0", + "resolved": "https://registry.npmjs.org/react-i18next/-/react-i18next-16.0.0.tgz", + "integrity": "sha512-JQ+dFfLnFSKJQt7W01lJHWRC0SX7eDPobI+MSTJ3/gP39xH2g33AuTE7iddAfXYHamJdAeMGM0VFboPaD3G68Q==", + "license": "MIT", + "dependencies": { + "@babel/runtime": "^7.27.6", + "html-parse-stringify": "^3.0.1" + }, + "peerDependencies": { + "i18next": ">= 25.5.2", + "react": ">= 16.8.0", + "typescript": "^5" + }, + "peerDependenciesMeta": { + "react-dom": { + "optional": true + }, + "react-native": { + "optional": true + }, + "typescript": { + "optional": true + } + } + }, + "node_modules/react-refresh": { + "version": "0.17.0", + "resolved": "https://registry.npmjs.org/react-refresh/-/react-refresh-0.17.0.tgz", + "integrity": "sha512-z6F7K9bV85EfseRCp2bzrpyQ0Gkw1uLoCel9XBVWPg/TjRj94SkJzUTGfOa4bs7iJvBWtQG0Wq7wnI0syw3EBQ==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/resolve-from": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/resolve-from/-/resolve-from-4.0.0.tgz", + "integrity": "sha512-pb/MYmXstAkysRFx8piNI1tGFNQIFA3vkE3Gq4EuA1dF6gHp/+vgZqsCGJapvy8N3Q+4o7FwvquPJcnZ7RYy4g==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=4" + } + }, + "node_modules/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/rollup": { + "version": "4.52.4", + "resolved": "https://registry.npmjs.org/rollup/-/rollup-4.52.4.tgz", + "integrity": "sha512-CLEVl+MnPAiKh5pl4dEWSyMTpuflgNQiLGhMv8ezD5W/qP8AKvmYpCOKRRNOh7oRKnauBZ4SyeYkMS+1VSyKwQ==", + "license": "MIT", + "dependencies": { + "@types/estree": "1.0.8" + }, + "bin": { + "rollup": "dist/bin/rollup" + }, + "engines": { + "node": ">=18.0.0", + "npm": ">=8.0.0" + }, + "optionalDependencies": { + "@rollup/rollup-android-arm-eabi": "4.52.4", + "@rollup/rollup-android-arm64": "4.52.4", + "@rollup/rollup-darwin-arm64": "4.52.4", + "@rollup/rollup-darwin-x64": "4.52.4", + "@rollup/rollup-freebsd-arm64": "4.52.4", + "@rollup/rollup-freebsd-x64": "4.52.4", + "@rollup/rollup-linux-arm-gnueabihf": "4.52.4", + "@rollup/rollup-linux-arm-musleabihf": "4.52.4", + "@rollup/rollup-linux-arm64-gnu": "4.52.4", + "@rollup/rollup-linux-arm64-musl": "4.52.4", + "@rollup/rollup-linux-loong64-gnu": "4.52.4", + "@rollup/rollup-linux-ppc64-gnu": "4.52.4", + "@rollup/rollup-linux-riscv64-gnu": "4.52.4", + "@rollup/rollup-linux-riscv64-musl": "4.52.4", + "@rollup/rollup-linux-s390x-gnu": "4.52.4", + "@rollup/rollup-linux-x64-gnu": "4.52.4", + "@rollup/rollup-linux-x64-musl": "4.52.4", + "@rollup/rollup-openharmony-arm64": "4.52.4", + "@rollup/rollup-win32-arm64-msvc": "4.52.4", + "@rollup/rollup-win32-ia32-msvc": "4.52.4", + "@rollup/rollup-win32-x64-gnu": "4.52.4", + "@rollup/rollup-win32-x64-msvc": "4.52.4", + "fsevents": "~2.3.2" + } + }, + "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/scheduler": { + "version": "0.27.0", + "resolved": "https://registry.npmjs.org/scheduler/-/scheduler-0.27.0.tgz", + "integrity": "sha512-eNv+WrVbKu1f3vbYJT/xtiF5syA5HPIMtf9IgY/nKg0sWqzAUEvqY/xm7OcZc/qafLx/iO9FgOmeSAp4v5ti/Q==", + "license": "MIT" + }, + "node_modules/semver": { + "version": "6.3.1", + "resolved": "https://registry.npmjs.org/semver/-/semver-6.3.1.tgz", + "integrity": "sha512-BR7VvDCVHO+q2xBEWskxS6DJE1qRnb7DxzUrogb71CWoSficBxYsiAGd+Kl0mmq/MprG9yArRkyrQxTO6XjMzA==", + "dev": true, + "license": "ISC", + "bin": { + "semver": "bin/semver.js" + } + }, + "node_modules/shebang-command": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/shebang-command/-/shebang-command-2.0.0.tgz", + "integrity": "sha512-kHxr2zZpYtdmrN1qDjrrX/Z1rR1kG8Dx+gkpK1G4eXmvXswmcE1hTWBWYUzlraYw1/yZp6YuDY77YtvbN0dmDA==", + "dev": true, + "license": "MIT", + "dependencies": { + "shebang-regex": "^3.0.0" + }, + "engines": { + "node": ">=8" + } + }, + "node_modules/shebang-regex": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/shebang-regex/-/shebang-regex-3.0.0.tgz", + "integrity": "sha512-7++dFhtcx3353uBaq8DDR4NuxBetBzC7ZQOhmTQInHEd6bSrXdiEyzCvG07Z44UYdLShWUyXt5M/yhz8ekcb1A==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=8" + } + }, + "node_modules/source-map-js": { + "version": "1.2.1", + "resolved": "https://registry.npmjs.org/source-map-js/-/source-map-js-1.2.1.tgz", + "integrity": "sha512-UXWMKhLOwVKb728IUtQPXxfYU+usdybtUrK/8uGE8CQMvrhOpwvzDBwj0QhSL7MQc7vIsISBG8VQ8+IDQxpfQA==", + "license": "BSD-3-Clause", + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/strip-json-comments": { + "version": "3.1.1", + "resolved": "https://registry.npmjs.org/strip-json-comments/-/strip-json-comments-3.1.1.tgz", + "integrity": "sha512-6fPc+R4ihwqP6N/aIv2f1gMH8lOVtWQHoqC4yK6oSDVVocumAsfCqjkXnqiYMhmMwS/mEHLp7Vehlt3ql6lEig==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=8" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/supports-color": { + "version": "7.2.0", + "resolved": "https://registry.npmjs.org/supports-color/-/supports-color-7.2.0.tgz", + "integrity": "sha512-qpCAvRl9stuOHveKsn7HncJRvv501qIacKzQlO/+Lwxc9+0q2wLyv4Dfvt80/DPn2pqOBsJdDiogXGR9+OvwRw==", + "dev": true, + "license": "MIT", + "dependencies": { + "has-flag": "^4.0.0" + }, + "engines": { + "node": ">=8" + } + }, + "node_modules/tailwindcss": { + "version": "4.1.14", + "resolved": "https://registry.npmjs.org/tailwindcss/-/tailwindcss-4.1.14.tgz", + "integrity": "sha512-b7pCxjGO98LnxVkKjaZSDeNuljC4ueKUddjENJOADtubtdo8llTaJy7HwBMeLNSSo2N5QIAgklslK1+Ir8r6CA==", + "license": "MIT" + }, + "node_modules/tapable": { + "version": "2.3.0", + "resolved": "https://registry.npmjs.org/tapable/-/tapable-2.3.0.tgz", + "integrity": "sha512-g9ljZiwki/LfxmQADO3dEY1CbpmXT5Hm2fJ+QaGKwSXUylMybePR7/67YW7jOrrvjEgL1Fmz5kzyAjWVWLlucg==", + "license": "MIT", + "engines": { + "node": ">=6" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/webpack" + } + }, + "node_modules/tar": { + "version": "7.5.1", + "resolved": "https://registry.npmjs.org/tar/-/tar-7.5.1.tgz", + "integrity": "sha512-nlGpxf+hv0v7GkWBK2V9spgactGOp0qvfWRxUMjqHyzrt3SgwE48DIv/FhqPHJYLHpgW1opq3nERbz5Anq7n1g==", + "license": "ISC", + "dependencies": { + "@isaacs/fs-minipass": "^4.0.0", + "chownr": "^3.0.0", + "minipass": "^7.1.2", + "minizlib": "^3.1.0", + "yallist": "^5.0.0" + }, + "engines": { + "node": ">=18" + } + }, + "node_modules/tar/node_modules/yallist": { + "version": "5.0.0", + "resolved": "https://registry.npmjs.org/yallist/-/yallist-5.0.0.tgz", + "integrity": "sha512-YgvUTfwqyc7UXVMrB+SImsVYSmTS8X/tSrtdNZMImM+n7+QTriRXyXim0mBrTXNeqzVF0KWGgHPeiyViFFrNDw==", + "license": "BlueOak-1.0.0", + "engines": { + "node": ">=18" + } + }, + "node_modules/tinyglobby": { + "version": "0.2.15", + "resolved": "https://registry.npmjs.org/tinyglobby/-/tinyglobby-0.2.15.tgz", + "integrity": "sha512-j2Zq4NyQYG5XMST4cbs02Ak8iJUdxRM0XI5QyxXuZOzKOINmWurp3smXu3y5wDcJrptwpSjgXHzIQxR0omXljQ==", + "license": "MIT", + "dependencies": { + "fdir": "^6.5.0", + "picomatch": "^4.0.3" + }, + "engines": { + "node": ">=12.0.0" + }, + "funding": { + "url": "https://github.com/sponsors/SuperchupuDev" + } + }, + "node_modules/tinyglobby/node_modules/fdir": { + "version": "6.5.0", + "resolved": "https://registry.npmjs.org/fdir/-/fdir-6.5.0.tgz", + "integrity": "sha512-tIbYtZbucOs0BRGqPJkshJUYdL+SDH7dVM8gjy+ERp3WAUjLEFJE+02kanyHtwjWOnwrKYBiwAmM0p4kLJAnXg==", + "license": "MIT", + "engines": { + "node": ">=12.0.0" + }, + "peerDependencies": { + "picomatch": "^3 || ^4" + }, + "peerDependenciesMeta": { + "picomatch": { + "optional": true + } + } + }, + "node_modules/tinyglobby/node_modules/picomatch": { + "version": "4.0.3", + "resolved": "https://registry.npmjs.org/picomatch/-/picomatch-4.0.3.tgz", + "integrity": "sha512-5gTmgEY/sqK6gFXLIsQNH19lWb4ebPDLA4SdLP7dsWkIXHWlG66oPuVvXSGFPppYZz8ZDZq0dYYrbHfBCVUb1Q==", + "license": "MIT", + "engines": { + "node": ">=12" + }, + "funding": { + "url": "https://github.com/sponsors/jonschlinkert" + } + }, + "node_modules/to-regex-range": { + "version": "5.0.1", + "resolved": "https://registry.npmjs.org/to-regex-range/-/to-regex-range-5.0.1.tgz", + "integrity": "sha512-65P7iz6X5yEr1cwcgvQxbbIw7Uk3gOy5dIdtZ4rDveLqhrdJP+Li/Hx6tyK0NEb+2GCyneCMJiGqrADCSNk8sQ==", + "dev": true, + "license": "MIT", + "dependencies": { + "is-number": "^7.0.0" + }, + "engines": { + "node": ">=8.0" + } + }, + "node_modules/ts-api-utils": { + "version": "2.1.0", + "resolved": "https://registry.npmjs.org/ts-api-utils/-/ts-api-utils-2.1.0.tgz", + "integrity": "sha512-CUgTZL1irw8u29bzrOD/nH85jqyc74D6SshFgujOIA7osm2Rz7dYH77agkx7H4FBNxDq7Cjf+IjaX/8zwFW+ZQ==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=18.12" + }, + "peerDependencies": { + "typescript": ">=4.8.4" + } + }, + "node_modules/type-check": { + "version": "0.4.0", + "resolved": "https://registry.npmjs.org/type-check/-/type-check-0.4.0.tgz", + "integrity": "sha512-XleUoc9uwGXqjWwXaUTZAmzMcFZ5858QA2vvx1Ur5xIcixXIP+8LnFDgRplU30us6teqdlskFfu+ae4K79Ooew==", + "dev": true, + "license": "MIT", + "dependencies": { + "prelude-ls": "^1.2.1" + }, + "engines": { + "node": ">= 0.8.0" + } + }, + "node_modules/typescript": { + "version": "5.9.3", + "resolved": "https://registry.npmjs.org/typescript/-/typescript-5.9.3.tgz", + "integrity": "sha512-jl1vZzPDinLr9eUt3J/t7V6FgNEw9QjvBPdysz9KfQDD41fQrC2Y4vKQdiaUpFT4bXlb1RHhLpp8wtm6M5TgSw==", + "devOptional": true, + "license": "Apache-2.0", + "bin": { + "tsc": "bin/tsc", + "tsserver": "bin/tsserver" + }, + "engines": { + "node": ">=14.17" + } + }, + "node_modules/typescript-eslint": { + "version": "8.46.0", + "resolved": "https://registry.npmjs.org/typescript-eslint/-/typescript-eslint-8.46.0.tgz", + "integrity": "sha512-6+ZrB6y2bT2DX3K+Qd9vn7OFOJR+xSLDj+Aw/N3zBwUt27uTw2sw2TE2+UcY1RiyBZkaGbTkVg9SSdPNUG6aUw==", + "dev": true, + "license": "MIT", + "dependencies": { + "@typescript-eslint/eslint-plugin": "8.46.0", + "@typescript-eslint/parser": "8.46.0", + "@typescript-eslint/typescript-estree": "8.46.0", + "@typescript-eslint/utils": "8.46.0" + }, + "engines": { + "node": "^18.18.0 || ^20.9.0 || >=21.1.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/typescript-eslint" + }, + "peerDependencies": { + "eslint": "^8.57.0 || ^9.0.0", + "typescript": ">=4.8.4 <6.0.0" + } + }, + "node_modules/undici-types": { + "version": "7.14.0", + "resolved": "https://registry.npmjs.org/undici-types/-/undici-types-7.14.0.tgz", + "integrity": "sha512-QQiYxHuyZ9gQUIrmPo3IA+hUl4KYk8uSA7cHrcKd/l3p1OTpZcM0Tbp9x7FAtXdAYhlasd60ncPpgu6ihG6TOA==", + "devOptional": true, + "license": "MIT" + }, + "node_modules/update-browserslist-db": { + "version": "1.1.3", + "resolved": "https://registry.npmjs.org/update-browserslist-db/-/update-browserslist-db-1.1.3.tgz", + "integrity": "sha512-UxhIZQ+QInVdunkDAaiazvvT/+fXL5Osr0JZlJulepYu6Jd7qJtDZjlur0emRlT71EN3ScPoE7gvsuIKKNavKw==", + "dev": true, + "funding": [ + { + "type": "opencollective", + "url": "https://opencollective.com/browserslist" + }, + { + "type": "tidelift", + "url": "https://tidelift.com/funding/github/npm/browserslist" + }, + { + "type": "github", + "url": "https://github.com/sponsors/ai" + } + ], + "license": "MIT", + "dependencies": { + "escalade": "^3.2.0", + "picocolors": "^1.1.1" + }, + "bin": { + "update-browserslist-db": "cli.js" + }, + "peerDependencies": { + "browserslist": ">= 4.21.0" + } + }, + "node_modules/uri-js": { + "version": "4.4.1", + "resolved": "https://registry.npmjs.org/uri-js/-/uri-js-4.4.1.tgz", + "integrity": "sha512-7rKUyy33Q1yc98pQ1DAmLtwX109F7TIfWlW1Ydo8Wl1ii1SeHieeh0HHfPeL2fMXK6z0s8ecKs9frCuLJvndBg==", + "dev": true, + "license": "BSD-2-Clause", + "dependencies": { + "punycode": "^2.1.0" + } + }, + "node_modules/vite": { + "version": "7.1.9", + "resolved": "https://registry.npmjs.org/vite/-/vite-7.1.9.tgz", + "integrity": "sha512-4nVGliEpxmhCL8DslSAUdxlB6+SMrhB0a1v5ijlh1xB1nEPuy1mxaHxysVucLHuWryAxLWg6a5ei+U4TLn/rFg==", + "license": "MIT", + "dependencies": { + "esbuild": "^0.25.0", + "fdir": "^6.5.0", + "picomatch": "^4.0.3", + "postcss": "^8.5.6", + "rollup": "^4.43.0", + "tinyglobby": "^0.2.15" + }, + "bin": { + "vite": "bin/vite.js" + }, + "engines": { + "node": "^20.19.0 || >=22.12.0" + }, + "funding": { + "url": "https://github.com/vitejs/vite?sponsor=1" + }, + "optionalDependencies": { + "fsevents": "~2.3.3" + }, + "peerDependencies": { + "@types/node": "^20.19.0 || >=22.12.0", + "jiti": ">=1.21.0", + "less": "^4.0.0", + "lightningcss": "^1.21.0", + "sass": "^1.70.0", + "sass-embedded": "^1.70.0", + "stylus": ">=0.54.8", + "sugarss": "^5.0.0", + "terser": "^5.16.0", + "tsx": "^4.8.1", + "yaml": "^2.4.2" + }, + "peerDependenciesMeta": { + "@types/node": { + "optional": true + }, + "jiti": { + "optional": true + }, + "less": { + "optional": true + }, + "lightningcss": { + "optional": true + }, + "sass": { + "optional": true + }, + "sass-embedded": { + "optional": true + }, + "stylus": { + "optional": true + }, + "sugarss": { + "optional": true + }, + "terser": { + "optional": true + }, + "tsx": { + "optional": true + }, + "yaml": { + "optional": true + } + } + }, + "node_modules/vite/node_modules/fdir": { + "version": "6.5.0", + "resolved": "https://registry.npmjs.org/fdir/-/fdir-6.5.0.tgz", + "integrity": "sha512-tIbYtZbucOs0BRGqPJkshJUYdL+SDH7dVM8gjy+ERp3WAUjLEFJE+02kanyHtwjWOnwrKYBiwAmM0p4kLJAnXg==", + "license": "MIT", + "engines": { + "node": ">=12.0.0" + }, + "peerDependencies": { + "picomatch": "^3 || ^4" + }, + "peerDependenciesMeta": { + "picomatch": { + "optional": true + } + } + }, + "node_modules/vite/node_modules/picomatch": { + "version": "4.0.3", + "resolved": "https://registry.npmjs.org/picomatch/-/picomatch-4.0.3.tgz", + "integrity": "sha512-5gTmgEY/sqK6gFXLIsQNH19lWb4ebPDLA4SdLP7dsWkIXHWlG66oPuVvXSGFPppYZz8ZDZq0dYYrbHfBCVUb1Q==", + "license": "MIT", + "engines": { + "node": ">=12" + }, + "funding": { + "url": "https://github.com/sponsors/jonschlinkert" + } + }, + "node_modules/void-elements": { + "version": "3.1.0", + "resolved": "https://registry.npmjs.org/void-elements/-/void-elements-3.1.0.tgz", + "integrity": "sha512-Dhxzh5HZuiHQhbvTW9AMetFfBHDMYpo23Uo9btPXgdYP+3T5S+p+jgNy7spra+veYhBP2dCSgxR/i2Y02h5/6w==", + "license": "MIT", + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/which": { + "version": "2.0.2", + "resolved": "https://registry.npmjs.org/which/-/which-2.0.2.tgz", + "integrity": "sha512-BLI3Tl1TW3Pvl70l3yq3Y64i+awpwXqsGBYWkkqMtnbXgrMD+yj7rhW0kuEDxzJaYXGjEW5ogapKNMEKNMjibA==", + "dev": true, + "license": "ISC", + "dependencies": { + "isexe": "^2.0.0" + }, + "bin": { + "node-which": "bin/node-which" + }, + "engines": { + "node": ">= 8" + } + }, + "node_modules/word-wrap": { + "version": "1.2.5", + "resolved": "https://registry.npmjs.org/word-wrap/-/word-wrap-1.2.5.tgz", + "integrity": "sha512-BN22B5eaMMI9UMtjrGd5g5eCYPpCPDUy0FJXbYsaT5zYxjFOckS53SQDE3pWkVoWpHXVb3BrYcEN4Twa55B5cA==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/yallist": { + "version": "3.1.1", + "resolved": "https://registry.npmjs.org/yallist/-/yallist-3.1.1.tgz", + "integrity": "sha512-a4UGQaWPH59mOXUYnAG2ewncQS4i4F43Tv3JoAM+s2VDAmS9NsK8GpDMLrCHPksFT7h3K6TOoUNn2pb7RoXx4g==", + "dev": true, + "license": "ISC" + }, + "node_modules/yocto-queue": { + "version": "0.1.0", + "resolved": "https://registry.npmjs.org/yocto-queue/-/yocto-queue-0.1.0.tgz", + "integrity": "sha512-rVksvsnNCdJ/ohGc6xgPwyN8eheCxsiLM8mxuE/t/mOVqJewPuO1miLpTHQiRgTKCLexL4MeAFVagts7HmNZ2Q==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=10" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + } + } +} diff --git a/web-components-reuse/react-app/package.json b/web-components-reuse/react-app/package.json new file mode 100644 index 00000000..9d8ce142 --- /dev/null +++ b/web-components-reuse/react-app/package.json @@ -0,0 +1,33 @@ +{ + "name": "react-app", + "private": true, + "version": "0.0.0", + "type": "module", + "scripts": { + "dev": "vite", + "build": "tsc -b && vite build", + "lint": "eslint .", + "preview": "vite preview" + }, + "dependencies": { + "@tailwindcss/vite": "^4.1.14", + "react": "^19.1.1", + "react-dom": "^19.1.1", + "react-i18next": "^16.0.0", + "tailwindcss": "^4.1.14" + }, + "devDependencies": { + "@eslint/js": "^9.36.0", + "@types/node": "^24.6.0", + "@types/react": "^19.1.16", + "@types/react-dom": "^19.1.9", + "@vitejs/plugin-react": "^5.0.4", + "eslint": "^9.36.0", + "eslint-plugin-react-hooks": "^5.2.0", + "eslint-plugin-react-refresh": "^0.4.22", + "globals": "^16.4.0", + "typescript": "~5.9.3", + "typescript-eslint": "^8.45.0", + "vite": "^7.1.7" + } +} diff --git a/web-components-reuse/react-app/src/App.tsx b/web-components-reuse/react-app/src/App.tsx new file mode 100644 index 00000000..e23f3b38 --- /dev/null +++ b/web-components-reuse/react-app/src/App.tsx @@ -0,0 +1,26 @@ +import { useEffect } from "react"; +import { useTranslation } from "react-i18next"; +import Home from "./components/Home"; + +export default function App() { + const { t } = useTranslation(); + + useEffect(() => { + document.addEventListener('events.show-error-modal', e => { + const { error } = (e as CustomEvent).detail; + document.dispatchEvent(new CustomEvent('em.show', { + detail: { + title: t('errors.title'), + error: t('errors.' + error) + } + })); + }); + }, []); + + return ( + <> + + + + ) +} diff --git a/web-components-reuse/react-app/src/components/Home.tsx b/web-components-reuse/react-app/src/components/Home.tsx new file mode 100644 index 00000000..fc78c0d7 --- /dev/null +++ b/web-components-reuse/react-app/src/components/Home.tsx @@ -0,0 +1,116 @@ +import { useState, useEffect, useMemo } from "react"; +import { useTranslation } from "react-i18next"; +import { type Asset, type Currency, type ExchangeRate, api } from '../data/api'; +import { CurrencyCode } from '../data/codes'; +import { useUpdater } from '../data/updater'; +import * as Events from './events'; + +export default function Home() { + const { t } = useTranslation(); + + const [, setLiveUpdatesEnabled] = useState(true); + + const [denomination, setDenomination] = useState(CurrencyCode.USD); + const [, setDenominationToUSDExchangeRate] = useState(1); + const [denominationExchangeRates, setDenominationExchangeRates] = useState([]); + const [denominationExchangeRateVersion, setDenominationExchangeRateVersion] = useState(); + + const [assets, setAssets] = useState([]); + const [currencies, setCurrencies] = useState([]); + const [assetsVersion, setAssetsVersion] = useState(); + const [currenciesVersion, setCurrenciesVersion] = useState(); + + const assetName = (a: Asset) => t('asset-code.' + a.code); + const currencyName = (c: Currency) => t('currency-code.' + c.code); + const assetInputOptions = useMemo<{ name: string, marketSize: number }[]>(() => + assets.map(a => ({ name: assetName(a), marketSize: a.marketSize })), [assets]); + const currencyInputOptions = useMemo<{ name: string, marketSize: number }[]>(() => + currencies.map(c => ({ name: currencyName(c), marketSize: c.marketSize })), [currencies]); + + const fetchAssets = async () => { + const response = await api.assets(denomination, assetsVersion); + if (response.success()) { + if (response.hasValue()) { + const responseValue = response.value(); + setAssets(responseValue.assets); + setAssetsVersion(responseValue.responseVersion); + } + } else { + Events.showErrorModal(response.error()); + } + }; + const fetchCurrencies = async () => { + const response = await api.currencies(denomination, currenciesVersion); + if (response.success()) { + if (response.hasValue()) { + const responseValue = response.value(); + setCurrencies(responseValue.currencies); + setCurrenciesVersion(responseValue.responseVersion); + } + } else { + Events.showErrorModal(response.error()); + } + }; + const fetchExchangeRates = async () => { + const response = await api.exchangeRates(denomination, denominationExchangeRateVersion); + if (response.success()) { + if (response.hasValue()) { + const responseValue = response.value(); + setDenominationExchangeRates(responseValue.exchangeRates); + setDenominationToUSDExchangeRate(responseValue.exchangeRates.find(er => er.to == CurrencyCode.USD)?.value ?? 1); + setDenominationExchangeRateVersion(responseValue.responseVersion); + } + } else { + Events.showErrorModal(response.error()); + } + }; + + const liveUpdatesToggledEventHandler = (e: Event) => { + const enabled = (e as CustomEvent).detail as boolean; + setLiveUpdatesEnabled(enabled); + useUpdater().paused = !enabled; + }; + + const denominationChangedEventHandler = (e: Event) => { + const denomination = (e as CustomEvent).detail as CurrencyCode; + setDenomination(denomination); + }; + + useEffect(() => { + fetchAssets(); + useUpdater().fetchAssets = fetchAssets; + }, [denomination, assetsVersion]); + + useEffect(() => { + fetchCurrencies(); + useUpdater().fetchCurrencies = fetchCurrencies; + }, [denomination, currenciesVersion]); + + useEffect(() => { + fetchExchangeRates(); + useUpdater().fetchExchangeRates = fetchExchangeRates; + }, [denomination, denominationExchangeRateVersion]); + + useEffect(() => { + document.addEventListener('mh.live-updates-toggled', liveUpdatesToggledEventHandler); + document.addEventListener('mh.denomination-changed', denominationChangedEventHandler); + return () => { + document.removeEventListener('mh.live-updates-toggled', liveUpdatesToggledEventHandler); + document.removeEventListener('mh.denomination-changed', denominationChangedEventHandler); + useUpdater().clear(); + }; + }, []); + + return (<> + ({ name: d.to, exchangeRate: d.value }))}> + + ({ id: a.code, name: assetName(a), marketSize: a.marketSize, denomination: a.denomination }))} + currencies={currencies.map(c => ({ id: c.code, name: currencyName(c), marketSize: c.marketSize, denomination: c.denomination }))} + denomination={denomination}> + + + + ); +} \ No newline at end of file diff --git a/web-components-reuse/react-app/src/components/events.ts b/web-components-reuse/react-app/src/components/events.ts new file mode 100644 index 00000000..b22b9a13 --- /dev/null +++ b/web-components-reuse/react-app/src/components/events.ts @@ -0,0 +1,5 @@ +export function showErrorModal(error: string) { + document.dispatchEvent(new CustomEvent('events.show-error-modal', { + detail: { error } + })); +} \ No newline at end of file diff --git a/web-components-reuse/react-app/src/components/web-components.js b/web-components-reuse/react-app/src/components/web-components.js new file mode 100644 index 00000000..35de4061 --- /dev/null +++ b/web-components-reuse/react-app/src/components/web-components.js @@ -0,0 +1,1214 @@ +// Generated by components/package.py from web-components-reuse:7eb3ba7b7ccce0614f4d4e4417665f665559ce57 + +// Common types definition +/** +* @typedef {Object} AssetOrCurrency +* @property {string} name +* @property {number} marketSize +*/ + +export function formatMoney(value, denomination) { + const zeros = value.length; + if (zeros > 15) { + return `${value.substring(0, zeros - 15)} ${value.substring(zeros - 15, zeros - 12)} ${value.substring(zeros - 12, zeros - 9)} ${value.substring(zeros - 9, zeros - 6)} ${value.substring(zeros - 6, zeros - 3)} ${value.substring(zeros - 3)} ${denomination}`; + } + if (zeros > 12) { + return `${value.substring(0, zeros - 12)} ${value.substring(zeros - 12, zeros - 9)} ${value.substring(zeros - 9, zeros - 6)} ${value.substring(zeros - 6, zeros - 3)} ${value.substring(zeros - 3)} ${denomination}`; + } + if (zeros > 9) { + return `${value.substring(0, zeros - 9)} ${value.substring(zeros - 9, zeros - 6)} ${value.substring(zeros - 6, zeros - 3)} ${value.substring(zeros - 3)} ${denomination}`; + } + if (zeros > 6) { + return `${value.substring(0, zeros - 6)} ${value.substring(zeros - 6, zeros - 3)} ${value.substring(zeros - 3)} ${denomination}`; + } + return `${value} ${denomination}`; +} + +export class BaseHTMLElement extends HTMLElement { + + _t = null; + _tNamespace = ''; + + set t(value) { + this._t = value; + // TODO: shouldn't all components be re-renderable and called from here? + } + + set tNamespace(value) { + this._tNamespace = value; + } + + translation(key) { + const namespacedKey = (this.getAttribute("t-namespace") ?? this._tNamespace) + key; + const attributeTranslation = this.getAttribute(`t-${namespacedKey}`); + if (attributeTranslation != undefined) { + return attributeTranslation; + } + return this._t ? this._t(namespacedKey) : null; + } + + translationAttribute(key) { + const translation = this.translation(key); + if (translation) { + return `t-${key}="${translation}"`; + } + return ""; + } + + translationAttributeRemovingNamespace(key, namespace) { + const translation = this.translation(namespace + key); + if (translation) { + return `t-${key}="${translation}"`; + } + return ""; + } +}class TabsContainer extends HTMLElement { + + _activeTab = 0; + set activeTab(value) { + this._activeTab = value; + this._updateActiveTab(); + } + + activeTabClass = "underline"; + + connectedCallback() { + this._tabsHeader = this.querySelector("[data-tabs-header]"); + this._tabsBody = this.querySelector("[data-tabs-body]"); + this.activeTabClass = this.getAttribute("active-tab-class") ?? this.activeTabClass; + if (!this._tabsHeader) { + throw new Error("Tabs header must be defined and marked with data-tabs-header attribute!"); + } + if (!this._tabsBody) { + throw new Error("Tabs body must be defined and marked with data-tabs-body attribute!"); + } + + [...this._tabsHeader.children].forEach((tab, i) => { + tab.addEventListener('click', () => this.activeTab = i); + }); + + [...this._tabsBody.children].forEach((tab) => { + tab.classList.add("hidden"); + }); + + this._updateActiveTab(); + } + + _updateActiveTab() { + [...this._tabsHeader.children].forEach((tab, i) => { + if (i == this._activeTab) { + tab.classList.add(this.activeTabClass); + } else { + tab.classList.remove(this.activeTabClass); + } + }); + [...this._tabsBody.children].forEach((tab, i) => { + if (i == this._activeTab) { + tab.classList.remove('hidden'); + } else { + tab.classList.add('hidden'); + } + }); + } +} + +class TabHeader extends HTMLElement { + + connectedCallback() { + this.classList.add("text-2xl"); + this.classList.add("p-2"); + this.classList.add("cursor-pointer"); + this.classList.add("grow"); + } +} + +class DropDownContainer extends HTMLElement { + + connectedCallback() { + const optionsZIndex = this.getAttribute("options-z-index") ?? '99'; + const anchor = this.querySelector('[data-drop-down-anchor]') ?? this; + anchor.style = "position: relative; display: inline-block"; + + const options = this.querySelector("[data-drop-down-options]"); + if (!options) { + throw new Error("Options must be defined and marked with data-drop-down-options attribute!"); + } + options.style = `position: absolute; z-index: ${optionsZIndex}`; + options.classList.add("hidden"); + + anchor.onclick = (e) => { + // Do not hide other, opened DropDowns + e.stopPropagation(); + options.classList.toggle("hidden"); + }; + + window.addEventListener("click", e => { + if (e.target != anchor && e.target.parentNode != anchor) { + options.classList.add("hidden"); + } + }); + } +} + +class InfoModal extends HTMLElement { + + #container = null; + #closeButton = null; + #titleElement = null; + #contentElement = null; + + connectedCallback() { + const titleClassToAppend = this.getAttribute('title-class'); + const title = this.getAttribute("title"); + const content = this.getAttribute("content"); + + let titleClass = "text-xl font-bold p-4"; + if (titleClassToAppend) { + titleClass = titleClass + " " + titleClassToAppend; + } + + this.innerHTML = ` + + `; + + this.#container = this.querySelector('[data-container]'); + this.#closeButton = this.querySelector('[data-close-button]'); + this.#titleElement = this.querySelector('[data-title-element]'); + this.#contentElement = this.querySelector('[data-content-element]'); + + this.#closeButton.onclick = () => this.close(); + + this.#container.addEventListener("click", e => { + if (e.target == this.#container) { + this.close(); + } + }); + } + + show({ title, content }) { + if (title) { + this.#titleElement.textContent = title; + } + if (content) { + this.#contentElement.textContent = content; + } + this.#container.style.display = "block"; + } + + close() { + this.#container.style.display = "none"; + this.dispatchEvent(new CustomEvent('im.closed', { bubbles: true })); + } +} + +class ErrorModal extends HTMLElement { + + #modal = null; + #onShowHandler = null; + + connectedCallback() { + this.innerHTML = ``; + + this.#modal = this.querySelector("info-modal"); + this.#onShowHandler = (e) => { + const { title, error } = e.detail; + this.#modal.show({ title, content: error }); + }; + + document.addEventListener('em.show', this.#onShowHandler); + } + + disconnectedCallback() { + document.removeEventListener('em.show', this.#onShowHandler); + } +} + + +class AssetElement extends BaseHTMLElement { + + /** + * Supported attributes + * {string} id: asset id + * {string} name: asset name + * {number} market-size + * {number} previous-market-size + * {string} denomination + * {string} class: additional class to append to the root div + */ + connectedCallback() { + this.#render(); + } + + attributeChangedCallback(name, oldValue, newValue) { + this.#render(); + } + + #render() { + const [id, name, marketSize, previousMarketSize, denomination] = [this.getAttribute("id"), this.getAttribute("name"), + this.getAttribute("market-size"), this.getAttribute("previous-market-size"), this.getAttribute("denomination")]; + if (!id || !name) { + return; + } + const classesToAppend = this.getAttribute("class"); + + let previousMarketSizeComponent; + if (previousMarketSize && previousMarketSize != marketSize) { + const previousMarketSizeInt = parseInt(previousMarketSize); + const currentMarketSizeInt = parseInt(marketSize); + const marketIsUp = currentMarketSizeInt > previousMarketSizeInt; + let marketPercentageDiff; + if (marketIsUp) { + marketPercentageDiff = Math.round((currentMarketSizeInt - previousMarketSizeInt) * 100 * 100 / previousMarketSizeInt) / 100.0; + } else { + marketPercentageDiff = Math.round((previousMarketSizeInt - currentMarketSizeInt) * 100 * 100 / currentMarketSizeInt) / 100.0; + } + previousMarketSizeComponent = ` +
    + ${this.translation("previous-market-size-label")}:${formatMoney(previousMarketSize, denomination)} +
    +

    ${marketIsUp ? this.translation('up-by-info') : this.translation('down-by-info')} ${marketPercentageDiff}%

    `; + } else { + previousMarketSizeComponent = ``; + } + + this.innerHTML = ` +
    +

    ${name}

    +
    + ${this.translation('market-size-label')}:${formatMoney(marketSize, denomination)} +
    + ${previousMarketSizeComponent} +
    + `; + } +} + + +class CurrencyElement extends BaseHTMLElement { + + /** + * Supported attributes + * {string} id: currency id + * {string} name: currency name + * {number} market-size + * {number} previous-market-size + * {string} denomination + * {string} class: additional class to append to the root div + */ + connectedCallback() { + this._render(); + } + + attributeChangedCallback(name, oldValue, newValue) { + this._render(); + } + + _render() { + const [id, name, marketSize, previousMarketSize, denomination] = [this.getAttribute("id"), this.getAttribute("name"), + this.getAttribute("market-size"), this.getAttribute("previous-market-size"), this.getAttribute("denomination") + ]; + if (id == undefined || name == undefined) { + return; + } + const classesToAppend = this.getAttribute("class"); + + let previousMarketSizeComponent; + if (previousMarketSize && previousMarketSize != marketSize) { + const previousMarketSizeInt = parseInt(previousMarketSize); + const currentMarketSizeInt = parseInt(marketSize); + const marketIsUp = currentMarketSizeInt > previousMarketSizeInt; + let marketPercentageDiff; + if (marketIsUp) { + marketPercentageDiff = Math.round((currentMarketSizeInt - previousMarketSizeInt) * 100 * 100 / previousMarketSizeInt) / 100.0; + } else { + marketPercentageDiff = Math.round((previousMarketSizeInt - currentMarketSizeInt) * 100 * 100 / currentMarketSizeInt) / 100.0; + } + previousMarketSizeComponent = ` +

    ${marketIsUp ? this.translation('up-by-info') : this.translation('down-by-info')} ${marketPercentageDiff}%

    `; + } else { + previousMarketSizeComponent = ``; + } + + this.innerHTML = ` +
    +

    ${name}

    +
    + ${this.translation('daily-turnover-label')}:${formatMoney(marketSize, denomination)} +
    +
    + ${this.translation('yearly-turnover-label')}:${formatMoney(`${365 * parseInt(marketSize)}`, denomination)} +
    + ${previousMarketSizeComponent} +
    + `; + } +} + + +/** +* @typedef {Object} AssetOrCurrencyElement +* @property {string} id +* @property {string} name +* @property {number} marketSize +* @property {string} denomination +*/ + +class AssetsAndCurrencies extends BaseHTMLElement { + + #assets = []; + #assetsValueChangeReason = null; + #currencies = []; + #denomination = "USD"; + #assetsContainer = null; + #currenciesContainer = null; + + /** @type {AssetOrCurrencyElement[]} */ + set assets(value) { + this.#assets = value; + this.#renderAssets(); + } + + set assetsValueChangeReason(value) { + this.#assetsValueChangeReason = value; + this.#renderAssets(); + } + + /** @type {AssetOrCurrencyElement[]} */ + set currencies(value) { + this.#currencies = value; + this.#renderCurrencies(); + } + + /** @type {string} */ + set denomination(value) { + this.#denomination = value; + this.#renderAssets(); + this.#renderCurrencies(); + } + + connectedCallback() { + this.innerHTML = ` +
    + +
    + ${this.translation('assets-header')} + ${this.translation('currencies-header')} +
    +
    +
    + ${this.#assetsHTML()} +
    +
    + ${this.#currenciesHTML()} +
    +
    +
    +
    `; + + const tabsBody = this.querySelector("[data-tabs-body]"); + this.#assetsContainer = tabsBody.children[0]; + this.#currenciesContainer = tabsBody.children[1]; + } + + #assetsHTML(previousAssetElements = []) { + return this.#assets.map(a => { + const previousAsset = previousAssetElements.find(pa => pa.id == a.id); + let previousMarketSize; + if (!previousAsset) { + previousMarketSize = a.marketSize; + } else { + const previousAssetDenomination = previousAsset.getAttribute("denomination"); + const previousAssetMarketSize = previousAsset.getAttribute("market-size"); + // if denomination has changed, comparing current market size with the previous is meaningless + if (previousAssetDenomination != a.denomination) { + previousMarketSize = a.marketSize; + } else if (previousAssetMarketSize != a.marketSize) { + previousMarketSize = previousAssetMarketSize; + } else { + previousMarketSize = previousAsset.getAttribute("previous-market-size"); + } + } + + return ` + `; + }).join("\n"); + } + + #currenciesHTML(previousCurrencyElements = []) { + return this.#currencies.map(c => { + const previousCurrency = previousCurrencyElements.find(pc => pc.id == c.id); + let previousMarketSize; + if (!previousCurrency) { + previousMarketSize = c.marketSize; + } else { + const previousCurrencyDenomination = previousCurrency.getAttribute("denomination"); + const previousCurrencyMarketSize = previousCurrency.getAttribute("market-size"); + // if denomination has changed, comparing current market size with the previous is meaningless + if (previousCurrencyDenomination != c.denomination) { + previousMarketSize = c.marketSize; + } else if (previousCurrencyMarketSize != c.marketSize) { + previousMarketSize = previousCurrencyMarketSize; + } else { + previousMarketSize = previousCurrency.getAttribute("previous-market-size"); + } + } + + return ` + `}) + .join("\n"); + } + + #renderAssets() { + if (this.#assetsContainer) { + const currentAssetElements = [...this.querySelectorAll("asset-element")]; + this.#assetsContainer.innerHTML = this.#assetsHTML(currentAssetElements); + } + } + + #renderCurrencies() { + if (this.#currenciesContainer) { + const currentCurrencyElements = [...this.querySelectorAll("currency-element")]; + this.#currenciesContainer.innerHTML = this.#currenciesHTML(currentCurrencyElements); + } + } +} + + +class MarketsHeader extends BaseHTMLElement { + + _denomination = 'USD'; + _liveUpdatesEnabled = true; + _denominationExchangeRates = []; + _liveUpdatesEnabledElement = null; + _denominationElement = null; + + + set denomination(value) { + this._denomination = value; + if (this._denominationElement) { + this._denominationElement.textContent = this._denomination; + } + } + + set denominationExchangeRates(value) { + this._denominationExchangeRates = value; + this._renderDenominationOptions(); + } + + connectedCallback() { + this.innerHTML = ` +
    +
    ${this.translation('live-updates')} ${this._liveUpdatesElementText()} +
    + ${this.translation('markets-in')} + + ${this._denomination} +
      + ${this._denominationOptionsHTML()} +
    +
    +
    + `; + + this._liveUpdatesEnabledElement = this.querySelector("span"); + this._liveUpdatesEnabledElement.onclick = () => { + this._liveUpdatesEnabled = !this._liveUpdatesEnabled; + this._liveUpdatesEnabledElement.textContent = this._liveUpdatesElementText(); + this.dispatchEvent(new CustomEvent('mh.live-updates-toggled', { bubbles: true, detail: this._liveUpdatesEnabled })); + }; + + this._denominationElement = this.querySelector("drop-down-container > span"); + } + + _denominationOptionsHTML() { + return this._denominationExchangeRates.map(der => `
  • ${der.name}: ${der.exchangeRate}
  • `).join('\n'); + } + + _renderDenominationOptions() { + const optionsContainer = this.querySelector("ul"); + if (optionsContainer) { + optionsContainer.innerHTML = this._denominationOptionsHTML(); + this._setOptionsClickHandlers(); + } + } + + _setOptionsClickHandlers() { + [...this.querySelectorAll("li")].forEach(o => { + o.onclick = () => { + this._denomination = o.getAttribute("data-option-id"); + this._denominationElement.textContent = this._denomination; + this.dispatchEvent(new CustomEvent('mh.denomination-changed', { bubbles: true, detail: this._denomination })); + }; + }); + } + + _liveUpdatesElementText() { + return `${this._liveUpdatesEnabled ? this.translation('live-updates-on') : this.translation('live-updates-off')}`; + } +} + + +class MarketsComparator extends BaseHTMLElement { + + static observedAttributes = ["asset-items", "currency-items"]; + + _fromMarketsComparatorInput = null; + _toMarketsComparatorInput = null; + _comparisonElement = null; + + _fromMarketSize = null; + _toMarketSize = null; + + /** @type {AssetOrCurrency[]} */ + set assets(value) { + if (this._fromMarketsComparatorInput) { + this._setValuesUsingAttributes(this._fromMarketsComparatorInput, value, "asset"); + } + if (this._toMarketsComparatorInput) { + this._setValuesUsingAttributes(this._toMarketsComparatorInput, value, "asset"); + } + } + + /** @type {AssetOrCurrency[]} */ + set currencies(value) { + if (this._fromMarketsComparatorInput) { + this._setValuesUsingAttributes(this._fromMarketsComparatorInput, value, "currency"); + } + if (this._toMarketsComparatorInput) { + this._setValuesUsingAttributes(this._toMarketsComparatorInput, value, "currency"); + } + } + + _setValuesUsingAttributes(element, values, prefix) { + for (let i = 0; i < values.length; i++) { + element.setAttribute(`${prefix}-${i}-name`, values[i].name); + element.setAttribute(`${prefix}-${i}-market-size`, values[i].marketSize); + } + element.setAttribute(`${prefix}-items`, values.length); + } + + connectedCallback() { + this._render(); + + this._chosenMarketSizeChangedEventHandler = e => { + const { name, marketSize } = e.detail; + if (e.target === this._fromMarketsComparatorInput) { + this._fromMarketSize = marketSize; + this._renderComparisonElementHTML(); + this.dispatchEvent(new CustomEvent("mc.from-market-size-changed", { + bubbles: true, + detail: { name, marketSize } + })); + } else if (e.target === this._toMarketsComparatorInput) { + this._toMarketSize = marketSize; + this._renderComparisonElementHTML(); + this.dispatchEvent(new CustomEvent("mc.to-market-size-changed", { + bubbles: true, + detail: { name, marketSize } + })); + } + } + document.addEventListener('mci.chosen-market-size-changed', this._chosenMarketSizeChangedEventHandler); + } + + disconnectedCallback() { + document.removeEventListener('mci.chosen-market-size-changed', this._chosenMarketSizeChangedEventHandler); + } + + attributeChangedCallback(name, oldValue, newValue) { + if (name == 'asset-items') { + this.assets = assetsFromAttributes(this); + } else if (name == 'currency-items') { + this.currencies = currenciesFromAttributes(this); + } + } + + _render() { + this.innerHTML = ` +
    + + +
    ${this.translation('markets-to')}
    + + +
    ${this._comparisonElementHTML()}
    +
    + `; + + [this._fromMarketsComparatorInput, this._toMarketsComparatorInput] = this.querySelectorAll("markets-comparator-input"); + this._comparisonElement = this.querySelector('[data-comparison-element]'); + } + + _comparisonElementHTML() { + if (!this._fromMarketSize || !this._toMarketSize) { + return '-'; + } + return `${this._fromMarketSize.toExponential(3)} / ${this._toMarketSize.toExponential(3)} = ${this._chosenMarketsComparedValue()}`; + } + + _chosenMarketsComparedValue() { + if (!this._fromMarketSize || !this._toMarketSize) { + return 0; + } + return Math.round(this._fromMarketSize * 1000 / this._toMarketSize) / 1000.0; + } + + _renderComparisonElementHTML() { + this._comparisonElement.innerHTML = this._comparisonElementHTML(); + } +} + +function assetsFromAttributes(element) { + const countAttribute = element.getAttribute("asset-items"); + if (!countAttribute) { + return []; + } + const assets = []; + for (let i = 0; i < parseInt(countAttribute); i++) { + const name = element.getAttribute(`asset-${i}-name`); + const marketSize = element.getAttribute(`asset-${i}-market-size`); + if (name && marketSize) { + assets.push({ name, marketSize: parseInt(marketSize) }); + } + } + return assets; +} + +function currenciesFromAttributes(element) { + const countAttribute = element.getAttribute("currency-items"); + if (!countAttribute) { + return []; + } + const currencies = []; + for (let i = 0; i < parseInt(countAttribute); i++) { + const name = element.getAttribute(`currency-${i}-name`); + const marketSize = element.getAttribute(`currency-${i}-market-size`); + if (name && marketSize) { + currencies.push({ name, marketSize: parseInt(marketSize) }); + } + } + return currencies; +} + +class MarketsComparatorInput extends BaseHTMLElement { + + static observedAttributes = ["asset-items", "currency-items"]; + + /** @type {AssetOrCurrency[]} */ + _assets = []; + /** @type {AssetOrCurrency[]} */ + _currencies = []; + /** @type {AssetOrCurrency[]} */ + _assetOrCurrencyOptions = []; + + set assets(value) { + this._assets = value; + this._recalculateAssetOrCurrencyOptions(); + } + + set currencies(value) { + this._currencies = value; + this._recalculateAssetOrCurrencyOptions(); + } + + _assetOrCurrencyInput = null; + _curencyTurnoverInputMultiplier = 1; + + connectedCallback() { + this._render(); + } + + attributeChangedCallback(name, oldValue, newValue) { + if (name == 'asset-items') { + this.assets = assetsFromAttributes(this); + } else if (name == 'currency-items') { + this.currencies = currenciesFromAttributes(this); + } + } + + _render() { + const optionsZIndex = this.getAttribute("options-z-index") ?? '99'; + this.innerHTML = ` + +
    + ${this._dropDownHeaderHTML()} +
    +
      + ${this._optionsHTML()} +
    +
    + `; + + this._setOptionsClickHandlers(); + + this.querySelector('[data-drop-down-header]') + .querySelector("input")?.addEventListener("input", e => { + this._curencyTurnoverInputMultiplier = e.target.value; + this._calculateChosenMarketSizeChange(); + }); + } + + _optionsHTML() { + return this._assetOrCurrencyOptions.map(o => + `
  • ${o.name}
  • `) + .join('\n'); + } + + _dropDownHeaderHTML() { + let marketSizeHTML; + if (this._isAsset()) { + marketSizeHTML = `${this.translation('market-size-input-label')} + + ${this.translation('days-turnover-input-label')} + `; + } else { + marketSizeHTML = ``; + } + return ` + ${this._assetOrCurrencyInput ?? this.translation('asset-or-currency-input-placeholder')} + ${marketSizeHTML} + `; + } + + _setOptionsClickHandlers() { + [...this.querySelectorAll("li")].forEach(o => { + o.onclick = () => { + this._assetOrCurrencyInput = o.getAttribute("data-option-id"); + this._calculateChosenMarketSizeChange(); + this._render(); + }; + }); + } + + _renderOptionsHTML() { + const optionsContainer = this.querySelector("ul"); + if (optionsContainer) { + optionsContainer.innerHTML = this._optionsHTML(); + this._setOptionsClickHandlers(); + } + } + + _isAsset() { + return this._assetOrCurrencyInput ? this._assets.find(a => a.name == this._assetOrCurrencyInput) : false; + } + + _isCurrency() { + return this._assetOrCurrencyInput ? this._currencies.find(c => c.name == this._assetOrCurrencyInput) : false; + } + + _recalculateAssetOrCurrencyOptions() { + const assetOrCurrencyOptions = []; + this._assets.forEach(a => assetOrCurrencyOptions.push(a)); + this._currencies.forEach(c => assetOrCurrencyOptions.push(c)); + this._assetOrCurrencyOptions = assetOrCurrencyOptions; + + this._renderOptionsHTML(); + this._calculateChosenMarketSizeChange(); + } + + _calculateChosenMarketSizeChange() { + if (!this._assetOrCurrencyInput) { + return; + } + + const assetInput = this._assets.find(a => a.name == this._assetOrCurrencyInput); + const currencyInput = this._currencies.find(c => c.name == this._assetOrCurrencyInput); + + const inputMarketSize = assetInput ? assetInput.marketSize : currencyInput.marketSize * this._curencyTurnoverInputMultiplier; + + this.dispatchEvent(new CustomEvent("mci.chosen-market-size-changed", { + bubbles: true, + detail: { name: this._assetOrCurrencyInput, marketSize: inputMarketSize } + })); + } +} + + +/** +* @typedef {Object} AssetOrCurrencyProjection +* @property {number} marketSize +* @property {number} growthRate +*/ + +class ProjectionsCalculator extends BaseHTMLElement { + + /** @type {?AssetOrCurrency} */ + _assetOrCurrency1 = null; + /** @type {?AssetOrCurrency} */ + _assetOrCurrency2 = null; + /** @type {?number} */ + _assetOrCurrency1ExpectedGrowthRate = null; + /** @type {?number} */ + _assetOrCurrency2ExpectedGrowthRate = null; + /** @type {?number} */ + _customProjectionYears = null; + + set assetOrCurrency1(value) { + this._assetOrCurrency1 = value; + this._renderProjectionsResultsHTML(); + } + + set assetOrCurrency2(value) { + this._assetOrCurrency2 = value; + this._renderProjectionsResultsHTML(); + } + + _assetOrCurrency1Header = null; + _assetOrCurrency1Input = null; + _assetOrCurrency2Header = null; + _assetOrCurrency2Input = null; + _customProjectionInput = null; + _projectionsResultsContainer = null; + _customProjectionContainer = null; + _customProjectionTextElement = null; + _customProjectionResultContainer = null; + + connectedCallback() { + this.innerHTML = ` +
    +
    ${this._assetOrCurrencyHeaderText(this._assetOrCurrency1)}
    + +
    ${this._assetOrCurrencyHeaderText(this._assetOrCurrency2)}
    + +
    + ${this._projectionsResultsHTML()} +
    +
    + ${this._customProjectionHTML()} +
    +
    + `; + + const container = this.querySelector("div"); + + const divs = container.querySelectorAll("div"); + [this._assetOrCurrency1Header, this._assetOrCurrency2Header, this._projectionsResultsContainer] = divs; + this._customProjectionContainer = divs[divs.length - 1]; + + [this._assetOrCurrency1Input, this._assetOrCurrency2Input, + this._customProjectionInput] = container.querySelectorAll("input"); + + this._assetOrCurrency1Input.addEventListener("input", e => { + this._assetOrCurrency1ExpectedGrowthRate = parseInt(e.target.value); + this._renderProjectionsResultsHTML(); + }); + this._assetOrCurrency2Input.addEventListener("input", e => { + this._assetOrCurrency2ExpectedGrowthRate = parseInt(e.target.value); + this._renderProjectionsResultsHTML(); + }); + this._customProjectionInput.addEventListener("input", e => { + this._customProjectionYears = parseInt(e.target.value); + this._updateCustomProjectionText(); + this._updateCustomProjectionResult(); + }); + + this._customProjectionTextElement = this.querySelector('[data-custom-projection-text-element]'); + this._customProjectionResultContainer = this.querySelector('[data-custom-projection-result-container]'); + } + + _assetOrCurrencyHeaderText(assetOrCurrency) { + return `${assetOrCurrency ? assetOrCurrency.name : this.translation('asset-or-currency-placeholder')} ${this.translation('asset-or-currency-expected-annual-growth-rate')}:`; + } + + _renderProjectionsResultsHTML() { + if (this._projectionsResultsContainer && this._customProjectionContainer) { + this._projectionsResultsContainer.innerHTML = this._projectionsResultsHTML(); + this._updateCustomProjectionResult(); + } + } + + _projectionsResultsHTML() { + const inYearsText = (years) => `${this.translation('results-in-header')} ${years} ${this.translation(years == 1 ? 'year' : 'years')}`; + const currentYear = new Date().getFullYear(); + return [1, 5, 10].map(y => ` +
    ${inYearsText(y)} (${currentYear + y}):
    + ${this._projectionsResultHTML(y)}`) + .join('\n'); + } + + _projectionsResultHTML(years) { + const ac1 = this._assetOrCurrency1WithExpectedGrowthRate(); + const ac2 = this._assetOrCurrency2WithExpectedGrowthRate(); + if (years != null && ac1 != null && ac2 != null) { + return ` + + `; + } + return '
    -
    '; + } + + _customProjectionHTML() { + return ` + ${this.translation('results-in-header')} + ${this._customProjectionYearText()}: +
    ${this._projectionsResultHTML(this._customProjectionYears)}
    + `; + } + + _customProjectionYearText() { + const currentYear = new Date().getFullYear(); + return '(' + (this._customProjectionYears == null || Number.isNaN(this._customProjectionYears) ? + `${currentYear} + N` : (currentYear + this._customProjectionYears)) + ')'; + } + + _updateCustomProjectionText() { + this._customProjectionTextElement.textContent = this._customProjectionYearText(); + } + + _updateCustomProjectionResult() { + this._customProjectionContainer.innerHTML = this._projectionsResultHTML(this._customProjectionYears); + } + + _assetOrCurrency1WithExpectedGrowthRate() { + if (this._assetOrCurrency1 && this._assetOrCurrency1ExpectedGrowthRate != null) { + return { marketSize: this._assetOrCurrency1.marketSize, growthRate: this._assetOrCurrency1ExpectedGrowthRate }; + } + return null; + } + + _assetOrCurrency2WithExpectedGrowthRate() { + if (this._assetOrCurrency2 && this._assetOrCurrency2ExpectedGrowthRate != null) { + return { marketSize: this._assetOrCurrency2.marketSize, growthRate: this._assetOrCurrency2ExpectedGrowthRate }; + } + return null; + } +} + +class ProjectionsResult extends HTMLElement { + + static observedAttributes = [ + "years", + "asset-or-currency-1-market-size", "asset-or-currency-1-growth-rate", + "asset-or-currency-2-market-size", "asset-or-currency-2-growth-rate" + ]; + + /** @type {number} */ + _years = 1; + /** @type {?AssetOrCurrencyProjection} */ + _assetOrCurrency1 = null; + /** @type {?AssetOrCurrencyProjection} */ + _assetOrCurrency2 = null; + + set years(value) { + this._years = value; + this._render(); + } + + set assetOrCurrency1(value) { + this._assetOrCurrency1 = value; + this._render(); + } + + set assetOrCurrency2(value) { + this._assetOrCurrency2 = value; + this._render(); + } + + connectedCallback() { + this._render(); + } + + attributeChangedCallback(name, oldValue, newValue) { + if (name.includes("asset-or-currency-1")) { + const assetOrCurrency = this._assetOrCurrencyFromAttributes("asset-or-currency-1"); + if (assetOrCurrency) { + this.assetOrCurrency1 = assetOrCurrency; + } + } else if (name.includes("asset-or-currency-2")) { + const assetOrCurrency = this._assetOrCurrencyFromAttributes("asset-or-currency-2"); + if (assetOrCurrency) { + this.assetOrCurrency2 = assetOrCurrency; + } + } else if (name == 'years') { + this.years = parseInt(newValue); + } + } + + _assetOrCurrencyFromAttributes(prefix) { + const marketSize = this.getAttribute(`${prefix}-market-size`); + const growthRate = this.getAttribute(`${prefix}-growth-rate`); + if (marketSize && growthRate) { + return { marketSize: parseInt(marketSize), growthRate: parseInt(growthRate) }; + } + return null; + } + + _render() { + const nominator = this._exponentialNumberString(this._projectionNumerator()); + const denominator = this._exponentialNumberString(this._projectionDenominator()); + this.innerHTML = ` +
    ${nominator} / ${denominator} = ${this._projection()} +
    + `; + } + + _exponentialNumberString(n) { + return n?.toExponential(3) ?? ''; + } + + _marketSizeChangedByRate(marketSize, growthRate, decrease = false) { + let changedMarketSize; + if (decrease) { + changedMarketSize = marketSize - (marketSize * growthRate / 100.0); + } else { + changedMarketSize = marketSize + (marketSize * growthRate / 100.0); + } + if (changedMarketSize <= 0 || Number.isNaN(changedMarketSize)) { + return null; + } + return changedMarketSize; + } + + _marketSizeChangedByRateInGivenYears(marketSize, growthRate, years) { + const negativeYears = years < 0; + let increasedMarketSize = marketSize; + for (let i = 0; i < Math.abs(years); i++) { + increasedMarketSize = this._marketSizeChangedByRate(increasedMarketSize, growthRate, negativeYears); + if (!increasedMarketSize) { + return null; + } + } + return increasedMarketSize; + } + + _projectionNumerator() { + if (this._assetOrCurrency1) { + return this._marketSizeChangedByRateInGivenYears( + this._assetOrCurrency1.marketSize, + this._assetOrCurrency1.growthRate, + this._years); + } + return null; + } + + _projectionDenominator() { + if (this._assetOrCurrency2) { + return this._marketSizeChangedByRateInGivenYears( + this._assetOrCurrency2.marketSize, + this._assetOrCurrency2.growthRate, + this._years); + } + return null; + } + + _projection() { + const numerator = this._projectionNumerator(); + const denominator = this._projectionDenominator(); + if (numerator && denominator) { + return Math.round(numerator * 1000 / denominator) / 1000.0; + } + return ''; + } +} + + +class MarketsProjections extends BaseHTMLElement { + + _marketsComparatorComponent = null; + _projectionsCalculatorComponent = null; + + /** @type {?AssetOrCurrency} */ + _fromAssetOrCurrency = null; + /** @type {?AssetOrCurrency} */ + _toAssetOrCurrency = null; + + /** @type {AssetOrCurrency[]} */ + set assets(value) { + if (this._marketsComparatorComponent) { + this._marketsComparatorComponent.assets = value; + } + } + + /** @type {AssetOrCurrency[]} */ + set currencies(value) { + if (this._marketsComparatorComponent) { + this._marketsComparatorComponent.currencies = value; + } + } + + connectedCallback() { + this.innerHTML = ` +
    +

    ${this.translation('projections-header')}

    + + + + +
    + `; + + this._marketsComparatorComponent = this.querySelector("markets-comparator"); + this._projectionsCalculatorComponent = this.querySelector("projections-calculator"); + + const fromMarketSizeChangedEventHandler = e => { + this._fromAssetOrCurrency = e.detail; + this._projectionsCalculatorComponent.assetOrCurrency1 = this._fromAssetOrCurrency; + }; + const toMarketSizeChangedEventHandler = e => { + this._toAssetOrCurrency = e.detail; + this._projectionsCalculatorComponent.assetOrCurrency2 = this._toAssetOrCurrency; + }; + + this.addEventListener('mc.from-market-size-changed', fromMarketSizeChangedEventHandler); + this.addEventListener('mc.to-market-size-changed', toMarketSizeChangedEventHandler); + } +} + + +export function registerComponents() { + customElements.define('tabs-container', TabsContainer); + customElements.define('tab-header', TabHeader); + customElements.define("drop-down-container", DropDownContainer); + customElements.define('info-modal', InfoModal); + customElements.define('error-modal', ErrorModal); + customElements.define("asset-element", AssetElement); + customElements.define("currency-element", CurrencyElement); + customElements.define('assets-and-currencies', AssetsAndCurrencies); + customElements.define("markets-header", MarketsHeader); + customElements.define("markets-comparator-input", MarketsComparatorInput); + customElements.define('markets-comparator', MarketsComparator); + customElements.define("projections-result", ProjectionsResult); + customElements.define("projections-calculator", ProjectionsCalculator); + customElements.define('markets-projections', MarketsProjections); +} \ No newline at end of file diff --git a/web-components-reuse/react-app/src/data/api.ts b/web-components-reuse/react-app/src/data/api.ts new file mode 100644 index 00000000..33d555ae --- /dev/null +++ b/web-components-reuse/react-app/src/data/api.ts @@ -0,0 +1,94 @@ +import type { CurrencyCode, AssetCode } from "./codes"; +import { HttpApi } from "./http-api"; + +export interface Asset { + code: AssetCode; + marketSize: number; + denomination: CurrencyCode +} + +export interface Currency { + code: CurrencyCode; + marketSize: number; + denomination: CurrencyCode; +} + +export interface ExchangeRate { + from: CurrencyCode; + to: CurrencyCode; + value: number; +} + +export interface AssetsResponse { + assets: Asset[]; + assetsVersion: string; + exchangeRatesVersion: string; + responseVersion: string; +} + +export interface CurrenciesResponse { + currencies: Currency[]; + responseVersion: string; +} + +export interface ExchangeRatesResponse { + exchangeRates: ExchangeRate[]; + responseVersion: string; +} + +export class Response { + + private readonly _value: T | null | undefined; + private readonly _error: string | null; + + constructor(value: T | null | undefined, error: string | null) { + this._value = value; + this._error = error; + } + + static ofSuccess(value?: T): Response { + return new Response(value, null); + } + + static ofFailure(error: string): Response { + return new Response(null as T, error); + } + + success(): boolean { + return this._error == null; + } + + value(): T { + if (!this.success()) { + throw new Error("Cannot return value from failed response"); + } + if (this.hasValue()) { + return this._value as T; + } + throw new Error("Cannot return value from empty success response"); + } + + hasValue() { + return this.success() && this._value; + } + + error(): string { + if (!this.success()) { + return this._error as string; + } + throw new Error("Cannot return error from success response"); + } +} + +export type PromiseResponse = Promise>; + +export interface Api { + + assets(denomination: CurrencyCode, version?: string): PromiseResponse + + currencies(denomination: CurrencyCode, version?: string): PromiseResponse + + exchangeRates(from: CurrencyCode, version?: string): PromiseResponse +} + +export const api: Api = new HttpApi(import.meta.env.VITE_API_BASE_URL); \ No newline at end of file diff --git a/web-components-reuse/react-app/src/data/codes.ts b/web-components-reuse/react-app/src/data/codes.ts new file mode 100644 index 00000000..5854c9f0 --- /dev/null +++ b/web-components-reuse/react-app/src/data/codes.ts @@ -0,0 +1,30 @@ +export const CurrencyCode = { + USD: "USD", + EUR: "EUR", + JPY: "JPY", + GBP: "GBP", + CNY: "CNY", + PLN: "PLN" +}; + +export type CurrencyCode = typeof CurrencyCode[keyof typeof CurrencyCode]; + +export const currencyCodes: CurrencyCode[] = [ + CurrencyCode.USD, + CurrencyCode.EUR, + CurrencyCode.JPY, + CurrencyCode.GBP, + CurrencyCode.CNY, + CurrencyCode.PLN +]; + +export const AssetCode = { + BONDS: "BONDS", + STOCKS: "STOCKS", + GOLD: "GOLD", + CASH: "CASH", + RLEST: "RLEST", + BTC: "BTC" +}; + +export type AssetCode = typeof AssetCode[keyof typeof AssetCode]; \ No newline at end of file diff --git a/web-components-reuse/react-app/src/data/http-api.ts b/web-components-reuse/react-app/src/data/http-api.ts new file mode 100644 index 00000000..f26de251 --- /dev/null +++ b/web-components-reuse/react-app/src/data/http-api.ts @@ -0,0 +1,40 @@ +import { Response, type PromiseResponse, type Api, type AssetsResponse, type CurrenciesResponse, type ExchangeRatesResponse } from "./api"; +import type { CurrencyCode } from "./codes"; + +export class HttpApi implements Api { + + private readonly baseUrl: string; + + constructor(baseUrl: string) { + this.baseUrl = baseUrl; + } + + assets(denomination: CurrencyCode, version?: string): PromiseResponse { + return this.get(`assets?denomination=${denomination}`, version); + } + + async get(path: string, version?: string): PromiseResponse { + try { + const response = await fetch(`${this.baseUrl}/${path}`, { method: "GET", headers: version ? { 'If-None-Match': version } : {} }); + if (response.status == 304) { + return Response.ofSuccess(); + } + const jsonResponse = await response.json(); + if (response.ok) { + return Response.ofSuccess(jsonResponse as T); + } + return Response.ofFailure(jsonResponse['error'] ?? 'UnsupportedErrorFormat'); + } catch (e: any) { + console.error("Failed to fetch", e); + return Response.ofFailure("UnknownFetchError"); + } + } + + currencies(denomination: CurrencyCode, version?: string): PromiseResponse { + return this.get(`currencies?denomination=${denomination}`, version); + } + + exchangeRates(from: CurrencyCode, version?: string): PromiseResponse { + return this.get(`exchange-rates/${from}`, version); + } +} \ No newline at end of file diff --git a/web-components-reuse/react-app/src/data/updater.ts b/web-components-reuse/react-app/src/data/updater.ts new file mode 100644 index 00000000..c273add7 --- /dev/null +++ b/web-components-reuse/react-app/src/data/updater.ts @@ -0,0 +1,51 @@ +export class Updater { + + fetchAssets: Function | null = null; + fetchCurrencies: Function | null = null; + fetchExchangeRates: Function | null = null; + private _paused = false; + + set paused(value: boolean) { + this._paused = value; + } + + with(fetchAssets: Function, fetchCurrencies: Function, fetchExchangeRates: Function) { + this.fetchAssets = fetchAssets; + this.fetchCurrencies = fetchCurrencies; + this.fetchExchangeRates = fetchExchangeRates; + } + + public start() { + setInterval(() => this.update(), 1000); + } + + public clear() { + this.fetchAssets = null; + this.fetchCurrencies = null; + this.fetchExchangeRates = null; + } + + private async update() { + if (this._paused) { + return; + } + if (this.fetchAssets) { + this.fetchAssets(); + } + if (this.fetchCurrencies) { + this.fetchCurrencies(); + } + if (this.fetchExchangeRates) { + this.fetchExchangeRates(); + } + } +} + +let updater: Updater | undefined; +export function useUpdater(): Updater { + if (!updater) { + updater = new Updater(); + updater.start(); + } + return updater; +} \ No newline at end of file diff --git a/web-components-reuse/react-app/src/global.d.ts b/web-components-reuse/react-app/src/global.d.ts new file mode 100644 index 00000000..c9ee39b4 --- /dev/null +++ b/web-components-reuse/react-app/src/global.d.ts @@ -0,0 +1,12 @@ +export { }; + +declare module 'react' { + namespace JSX { + interface IntrinsicElements { + 'markets-header': any; + 'assets-and-currencies': any; + 'markets-projections': any; + 'error-modal': any; + } + } +} \ No newline at end of file diff --git a/web-components-reuse/react-app/src/i18n.ts b/web-components-reuse/react-app/src/i18n.ts new file mode 100644 index 00000000..7482e1cd --- /dev/null +++ b/web-components-reuse/react-app/src/i18n.ts @@ -0,0 +1,76 @@ +import i18n from 'i18next'; +import { initReactI18next } from 'react-i18next'; + +const enTranslations = { + 'errors': { + 'title': "Something went wrong...", + 'UnknownFetchError': "Unknown error while fetching data", + 'UnsupportedErrorFormat': "Server responded in an unexpected way" + }, + 'currency-code': { + 'USD': "US Dollar", + 'EUR': "Euro", + 'JPY': "Japanesse Yen", + 'GBP': "British Pound", + 'CNY': "Chinese Yuan", + 'PLN': "Polish Zloty" + }, + 'asset-code': { + 'BONDS': "Bonds", + 'STOCKS': "Stocks", + 'GOLD': "Gold", + 'CASH': "Cash Reserves", + 'RLEST': "Real Estate", + 'BTC': "Bitcoin" + }, + 'markets-header': { + 'markets-in': "Markets in", + 'live-updates': "Live updates:", + 'live-updates-on': "ON", + 'live-updates-off': "OFF" + }, + 'assets-and-currencies': { + 'assets-header': "Assets", + 'currencies-header': "Currencies", + 'market-size-label': "Market size", + 'previous-market-size-label': "Previous market size", + 'up-by-info': "UP by", + 'down-by-info': "DOWN by", + 'daily-turnover-label': "Daily turnover", + 'yearly-turnover-label': "Yearly turnover" + }, + 'markets-projections': { + 'projections-header': 'Projections', + 'markets-comparator': { + 'asset-or-currency-input-placeholder': 'Asset/Currency', + 'market-size-input-label': 'market size', + 'days-turnover-input-label': 'days turnover', + 'markets-to': 'to' + }, + 'projections-calculator': { + 'asset-or-currency-placeholder': 'Asset/Currency', + 'asset-or-currency-expected-annual-growth-rate': 'expected annual growth rate', + 'results-in-header': 'In', + 'year': 'year', + 'years': 'years' + } + } +}; + +i18n.use(initReactI18next) + .init({ + resources: { + en: { + translation: enTranslations + } + }, + lng: "en", + // language to use, more information here: https://www.i18next.com/overview/configuration-options#languages-namespaces-resources + // you can use the i18n.changeLanguage function to change the language manually: https://www.i18next.com/overview/api#changelanguage + // if you're using a language detector, do not define the lng option + interpolation: { + escapeValue: false // react already saves from xss + } + }); + +export default i18n; \ No newline at end of file diff --git a/web-components-reuse/react-app/src/index.css b/web-components-reuse/react-app/src/index.css new file mode 100644 index 00000000..a461c505 --- /dev/null +++ b/web-components-reuse/react-app/src/index.css @@ -0,0 +1 @@ +@import "tailwindcss"; \ No newline at end of file diff --git a/web-components-reuse/react-app/src/main.tsx b/web-components-reuse/react-app/src/main.tsx new file mode 100644 index 00000000..b218db57 --- /dev/null +++ b/web-components-reuse/react-app/src/main.tsx @@ -0,0 +1,16 @@ +import { StrictMode } from 'react'; +import { createRoot } from 'react-dom/client'; +import './index.css'; +import './i18n'; +import App from './App.tsx'; + +// @ts-ignore +import { registerComponents } from './components/web-components.js'; + +registerComponents(); + +createRoot(document.getElementById('root')!).render( + + + , +) diff --git a/web-components-reuse/react-app/tsconfig.app.json b/web-components-reuse/react-app/tsconfig.app.json new file mode 100644 index 00000000..acf11450 --- /dev/null +++ b/web-components-reuse/react-app/tsconfig.app.json @@ -0,0 +1,28 @@ +{ + "compilerOptions": { + "tsBuildInfoFile": "./node_modules/.tmp/tsconfig.app.tsbuildinfo", + "target": "ES2022", + "useDefineForClassFields": true, + "lib": ["ES2022", "DOM", "DOM.Iterable"], + "module": "ESNext", + "types": ["vite/client"], + "skipLibCheck": true, + + /* Bundler mode */ + "moduleResolution": "bundler", + "allowImportingTsExtensions": true, + "verbatimModuleSyntax": true, + "moduleDetection": "force", + "noEmit": true, + "jsx": "react-jsx", + + /* Linting */ + "strict": true, + "noUnusedLocals": true, + "noUnusedParameters": true, + "erasableSyntaxOnly": true, + "noFallthroughCasesInSwitch": true, + "noUncheckedSideEffectImports": true + }, + "include": ["src", "global.d.ts"] +} diff --git a/web-components-reuse/react-app/tsconfig.json b/web-components-reuse/react-app/tsconfig.json new file mode 100644 index 00000000..1ffef600 --- /dev/null +++ b/web-components-reuse/react-app/tsconfig.json @@ -0,0 +1,7 @@ +{ + "files": [], + "references": [ + { "path": "./tsconfig.app.json" }, + { "path": "./tsconfig.node.json" } + ] +} diff --git a/web-components-reuse/react-app/tsconfig.node.json b/web-components-reuse/react-app/tsconfig.node.json new file mode 100644 index 00000000..8a67f62f --- /dev/null +++ b/web-components-reuse/react-app/tsconfig.node.json @@ -0,0 +1,26 @@ +{ + "compilerOptions": { + "tsBuildInfoFile": "./node_modules/.tmp/tsconfig.node.tsbuildinfo", + "target": "ES2023", + "lib": ["ES2023"], + "module": "ESNext", + "types": ["node"], + "skipLibCheck": true, + + /* Bundler mode */ + "moduleResolution": "bundler", + "allowImportingTsExtensions": true, + "verbatimModuleSyntax": true, + "moduleDetection": "force", + "noEmit": true, + + /* Linting */ + "strict": true, + "noUnusedLocals": true, + "noUnusedParameters": true, + "erasableSyntaxOnly": true, + "noFallthroughCasesInSwitch": true, + "noUncheckedSideEffectImports": true + }, + "include": ["vite.config.ts"] +} diff --git a/web-components-reuse/react-app/vite.config.ts b/web-components-reuse/react-app/vite.config.ts new file mode 100644 index 00000000..c4069b77 --- /dev/null +++ b/web-components-reuse/react-app/vite.config.ts @@ -0,0 +1,8 @@ +import { defineConfig } from 'vite' +import react from '@vitejs/plugin-react' +import tailwindcss from '@tailwindcss/vite' + +// https://vite.dev/config/ +export default defineConfig({ + plugins: [react(), tailwindcss()], +}) diff --git a/web-components-reuse/server/assets/input.css b/web-components-reuse/server/assets/input.css new file mode 100644 index 00000000..a461c505 --- /dev/null +++ b/web-components-reuse/server/assets/input.css @@ -0,0 +1 @@ +@import "tailwindcss"; \ No newline at end of file diff --git a/web-components-reuse/server/assets/output.css b/web-components-reuse/server/assets/output.css new file mode 100644 index 00000000..c99002a9 --- /dev/null +++ b/web-components-reuse/server/assets/output.css @@ -0,0 +1,161 @@ +/*! tailwindcss v4.1.14 | MIT License | https://tailwindcss.com */ +@layer theme, base, components, utilities; +@layer theme { + :root, :host { + --font-sans: ui-sans-serif, system-ui, sans-serif, "Apple Color Emoji", + "Segoe UI Emoji", "Segoe UI Symbol", "Noto Color Emoji"; + --font-mono: ui-monospace, SFMono-Regular, Menlo, Monaco, Consolas, "Liberation Mono", + "Courier New", monospace; + --default-font-family: var(--font-sans); + --default-mono-font-family: var(--font-mono); + } +} +@layer base { + *, ::after, ::before, ::backdrop, ::file-selector-button { + box-sizing: border-box; + margin: 0; + padding: 0; + border: 0 solid; + } + html, :host { + line-height: 1.5; + -webkit-text-size-adjust: 100%; + tab-size: 4; + font-family: var(--default-font-family, ui-sans-serif, system-ui, sans-serif, "Apple Color Emoji", "Segoe UI Emoji", "Segoe UI Symbol", "Noto Color Emoji"); + font-feature-settings: var(--default-font-feature-settings, normal); + font-variation-settings: var(--default-font-variation-settings, normal); + -webkit-tap-highlight-color: transparent; + } + hr { + height: 0; + color: inherit; + border-top-width: 1px; + } + abbr:where([title]) { + -webkit-text-decoration: underline dotted; + text-decoration: underline dotted; + } + h1, h2, h3, h4, h5, h6 { + font-size: inherit; + font-weight: inherit; + } + a { + color: inherit; + -webkit-text-decoration: inherit; + text-decoration: inherit; + } + b, strong { + font-weight: bolder; + } + code, kbd, samp, pre { + font-family: var(--default-mono-font-family, ui-monospace, SFMono-Regular, Menlo, Monaco, Consolas, "Liberation Mono", "Courier New", monospace); + font-feature-settings: var(--default-mono-font-feature-settings, normal); + font-variation-settings: var(--default-mono-font-variation-settings, normal); + font-size: 1em; + } + small { + font-size: 80%; + } + sub, sup { + font-size: 75%; + line-height: 0; + position: relative; + vertical-align: baseline; + } + sub { + bottom: -0.25em; + } + sup { + top: -0.5em; + } + table { + text-indent: 0; + border-color: inherit; + border-collapse: collapse; + } + :-moz-focusring { + outline: auto; + } + progress { + vertical-align: baseline; + } + summary { + display: list-item; + } + ol, ul, menu { + list-style: none; + } + img, svg, video, canvas, audio, iframe, embed, object { + display: block; + vertical-align: middle; + } + img, video { + max-width: 100%; + height: auto; + } + button, input, select, optgroup, textarea, ::file-selector-button { + font: inherit; + font-feature-settings: inherit; + font-variation-settings: inherit; + letter-spacing: inherit; + color: inherit; + border-radius: 0; + background-color: transparent; + opacity: 1; + } + :where(select:is([multiple], [size])) optgroup { + font-weight: bolder; + } + :where(select:is([multiple], [size])) optgroup option { + padding-inline-start: 20px; + } + ::file-selector-button { + margin-inline-end: 4px; + } + ::placeholder { + opacity: 1; + } + @supports (not (-webkit-appearance: -apple-pay-button)) or (contain-intrinsic-size: 1px) { + ::placeholder { + color: currentcolor; + @supports (color: color-mix(in lab, red, red)) { + color: color-mix(in oklab, currentcolor 50%, transparent); + } + } + } + textarea { + resize: vertical; + } + ::-webkit-search-decoration { + -webkit-appearance: none; + } + ::-webkit-date-and-time-value { + min-height: 1lh; + text-align: inherit; + } + ::-webkit-datetime-edit { + display: inline-flex; + } + ::-webkit-datetime-edit-fields-wrapper { + padding: 0; + } + ::-webkit-datetime-edit, ::-webkit-datetime-edit-year-field, ::-webkit-datetime-edit-month-field, ::-webkit-datetime-edit-day-field, ::-webkit-datetime-edit-hour-field, ::-webkit-datetime-edit-minute-field, ::-webkit-datetime-edit-second-field, ::-webkit-datetime-edit-millisecond-field, ::-webkit-datetime-edit-meridiem-field { + padding-block: 0; + } + ::-webkit-calendar-picker-indicator { + line-height: 1; + } + :-moz-ui-invalid { + box-shadow: none; + } + button, input:where([type="button"], [type="reset"], [type="submit"]), ::file-selector-button { + appearance: button; + } + ::-webkit-inner-spin-button, ::-webkit-outer-spin-button { + height: auto; + } + [hidden]:where(:not([hidden="until-found"])) { + display: none !important; + } +} +@layer utilities; diff --git a/web-components-reuse/server/assets/web-components.js b/web-components-reuse/server/assets/web-components.js new file mode 100644 index 00000000..35de4061 --- /dev/null +++ b/web-components-reuse/server/assets/web-components.js @@ -0,0 +1,1214 @@ +// Generated by components/package.py from web-components-reuse:7eb3ba7b7ccce0614f4d4e4417665f665559ce57 + +// Common types definition +/** +* @typedef {Object} AssetOrCurrency +* @property {string} name +* @property {number} marketSize +*/ + +export function formatMoney(value, denomination) { + const zeros = value.length; + if (zeros > 15) { + return `${value.substring(0, zeros - 15)} ${value.substring(zeros - 15, zeros - 12)} ${value.substring(zeros - 12, zeros - 9)} ${value.substring(zeros - 9, zeros - 6)} ${value.substring(zeros - 6, zeros - 3)} ${value.substring(zeros - 3)} ${denomination}`; + } + if (zeros > 12) { + return `${value.substring(0, zeros - 12)} ${value.substring(zeros - 12, zeros - 9)} ${value.substring(zeros - 9, zeros - 6)} ${value.substring(zeros - 6, zeros - 3)} ${value.substring(zeros - 3)} ${denomination}`; + } + if (zeros > 9) { + return `${value.substring(0, zeros - 9)} ${value.substring(zeros - 9, zeros - 6)} ${value.substring(zeros - 6, zeros - 3)} ${value.substring(zeros - 3)} ${denomination}`; + } + if (zeros > 6) { + return `${value.substring(0, zeros - 6)} ${value.substring(zeros - 6, zeros - 3)} ${value.substring(zeros - 3)} ${denomination}`; + } + return `${value} ${denomination}`; +} + +export class BaseHTMLElement extends HTMLElement { + + _t = null; + _tNamespace = ''; + + set t(value) { + this._t = value; + // TODO: shouldn't all components be re-renderable and called from here? + } + + set tNamespace(value) { + this._tNamespace = value; + } + + translation(key) { + const namespacedKey = (this.getAttribute("t-namespace") ?? this._tNamespace) + key; + const attributeTranslation = this.getAttribute(`t-${namespacedKey}`); + if (attributeTranslation != undefined) { + return attributeTranslation; + } + return this._t ? this._t(namespacedKey) : null; + } + + translationAttribute(key) { + const translation = this.translation(key); + if (translation) { + return `t-${key}="${translation}"`; + } + return ""; + } + + translationAttributeRemovingNamespace(key, namespace) { + const translation = this.translation(namespace + key); + if (translation) { + return `t-${key}="${translation}"`; + } + return ""; + } +}class TabsContainer extends HTMLElement { + + _activeTab = 0; + set activeTab(value) { + this._activeTab = value; + this._updateActiveTab(); + } + + activeTabClass = "underline"; + + connectedCallback() { + this._tabsHeader = this.querySelector("[data-tabs-header]"); + this._tabsBody = this.querySelector("[data-tabs-body]"); + this.activeTabClass = this.getAttribute("active-tab-class") ?? this.activeTabClass; + if (!this._tabsHeader) { + throw new Error("Tabs header must be defined and marked with data-tabs-header attribute!"); + } + if (!this._tabsBody) { + throw new Error("Tabs body must be defined and marked with data-tabs-body attribute!"); + } + + [...this._tabsHeader.children].forEach((tab, i) => { + tab.addEventListener('click', () => this.activeTab = i); + }); + + [...this._tabsBody.children].forEach((tab) => { + tab.classList.add("hidden"); + }); + + this._updateActiveTab(); + } + + _updateActiveTab() { + [...this._tabsHeader.children].forEach((tab, i) => { + if (i == this._activeTab) { + tab.classList.add(this.activeTabClass); + } else { + tab.classList.remove(this.activeTabClass); + } + }); + [...this._tabsBody.children].forEach((tab, i) => { + if (i == this._activeTab) { + tab.classList.remove('hidden'); + } else { + tab.classList.add('hidden'); + } + }); + } +} + +class TabHeader extends HTMLElement { + + connectedCallback() { + this.classList.add("text-2xl"); + this.classList.add("p-2"); + this.classList.add("cursor-pointer"); + this.classList.add("grow"); + } +} + +class DropDownContainer extends HTMLElement { + + connectedCallback() { + const optionsZIndex = this.getAttribute("options-z-index") ?? '99'; + const anchor = this.querySelector('[data-drop-down-anchor]') ?? this; + anchor.style = "position: relative; display: inline-block"; + + const options = this.querySelector("[data-drop-down-options]"); + if (!options) { + throw new Error("Options must be defined and marked with data-drop-down-options attribute!"); + } + options.style = `position: absolute; z-index: ${optionsZIndex}`; + options.classList.add("hidden"); + + anchor.onclick = (e) => { + // Do not hide other, opened DropDowns + e.stopPropagation(); + options.classList.toggle("hidden"); + }; + + window.addEventListener("click", e => { + if (e.target != anchor && e.target.parentNode != anchor) { + options.classList.add("hidden"); + } + }); + } +} + +class InfoModal extends HTMLElement { + + #container = null; + #closeButton = null; + #titleElement = null; + #contentElement = null; + + connectedCallback() { + const titleClassToAppend = this.getAttribute('title-class'); + const title = this.getAttribute("title"); + const content = this.getAttribute("content"); + + let titleClass = "text-xl font-bold p-4"; + if (titleClassToAppend) { + titleClass = titleClass + " " + titleClassToAppend; + } + + this.innerHTML = ` + + `; + + this.#container = this.querySelector('[data-container]'); + this.#closeButton = this.querySelector('[data-close-button]'); + this.#titleElement = this.querySelector('[data-title-element]'); + this.#contentElement = this.querySelector('[data-content-element]'); + + this.#closeButton.onclick = () => this.close(); + + this.#container.addEventListener("click", e => { + if (e.target == this.#container) { + this.close(); + } + }); + } + + show({ title, content }) { + if (title) { + this.#titleElement.textContent = title; + } + if (content) { + this.#contentElement.textContent = content; + } + this.#container.style.display = "block"; + } + + close() { + this.#container.style.display = "none"; + this.dispatchEvent(new CustomEvent('im.closed', { bubbles: true })); + } +} + +class ErrorModal extends HTMLElement { + + #modal = null; + #onShowHandler = null; + + connectedCallback() { + this.innerHTML = ``; + + this.#modal = this.querySelector("info-modal"); + this.#onShowHandler = (e) => { + const { title, error } = e.detail; + this.#modal.show({ title, content: error }); + }; + + document.addEventListener('em.show', this.#onShowHandler); + } + + disconnectedCallback() { + document.removeEventListener('em.show', this.#onShowHandler); + } +} + + +class AssetElement extends BaseHTMLElement { + + /** + * Supported attributes + * {string} id: asset id + * {string} name: asset name + * {number} market-size + * {number} previous-market-size + * {string} denomination + * {string} class: additional class to append to the root div + */ + connectedCallback() { + this.#render(); + } + + attributeChangedCallback(name, oldValue, newValue) { + this.#render(); + } + + #render() { + const [id, name, marketSize, previousMarketSize, denomination] = [this.getAttribute("id"), this.getAttribute("name"), + this.getAttribute("market-size"), this.getAttribute("previous-market-size"), this.getAttribute("denomination")]; + if (!id || !name) { + return; + } + const classesToAppend = this.getAttribute("class"); + + let previousMarketSizeComponent; + if (previousMarketSize && previousMarketSize != marketSize) { + const previousMarketSizeInt = parseInt(previousMarketSize); + const currentMarketSizeInt = parseInt(marketSize); + const marketIsUp = currentMarketSizeInt > previousMarketSizeInt; + let marketPercentageDiff; + if (marketIsUp) { + marketPercentageDiff = Math.round((currentMarketSizeInt - previousMarketSizeInt) * 100 * 100 / previousMarketSizeInt) / 100.0; + } else { + marketPercentageDiff = Math.round((previousMarketSizeInt - currentMarketSizeInt) * 100 * 100 / currentMarketSizeInt) / 100.0; + } + previousMarketSizeComponent = ` +
    + ${this.translation("previous-market-size-label")}:${formatMoney(previousMarketSize, denomination)} +
    +

    ${marketIsUp ? this.translation('up-by-info') : this.translation('down-by-info')} ${marketPercentageDiff}%

    `; + } else { + previousMarketSizeComponent = ``; + } + + this.innerHTML = ` +
    +

    ${name}

    +
    + ${this.translation('market-size-label')}:${formatMoney(marketSize, denomination)} +
    + ${previousMarketSizeComponent} +
    + `; + } +} + + +class CurrencyElement extends BaseHTMLElement { + + /** + * Supported attributes + * {string} id: currency id + * {string} name: currency name + * {number} market-size + * {number} previous-market-size + * {string} denomination + * {string} class: additional class to append to the root div + */ + connectedCallback() { + this._render(); + } + + attributeChangedCallback(name, oldValue, newValue) { + this._render(); + } + + _render() { + const [id, name, marketSize, previousMarketSize, denomination] = [this.getAttribute("id"), this.getAttribute("name"), + this.getAttribute("market-size"), this.getAttribute("previous-market-size"), this.getAttribute("denomination") + ]; + if (id == undefined || name == undefined) { + return; + } + const classesToAppend = this.getAttribute("class"); + + let previousMarketSizeComponent; + if (previousMarketSize && previousMarketSize != marketSize) { + const previousMarketSizeInt = parseInt(previousMarketSize); + const currentMarketSizeInt = parseInt(marketSize); + const marketIsUp = currentMarketSizeInt > previousMarketSizeInt; + let marketPercentageDiff; + if (marketIsUp) { + marketPercentageDiff = Math.round((currentMarketSizeInt - previousMarketSizeInt) * 100 * 100 / previousMarketSizeInt) / 100.0; + } else { + marketPercentageDiff = Math.round((previousMarketSizeInt - currentMarketSizeInt) * 100 * 100 / currentMarketSizeInt) / 100.0; + } + previousMarketSizeComponent = ` +

    ${marketIsUp ? this.translation('up-by-info') : this.translation('down-by-info')} ${marketPercentageDiff}%

    `; + } else { + previousMarketSizeComponent = ``; + } + + this.innerHTML = ` +
    +

    ${name}

    +
    + ${this.translation('daily-turnover-label')}:${formatMoney(marketSize, denomination)} +
    +
    + ${this.translation('yearly-turnover-label')}:${formatMoney(`${365 * parseInt(marketSize)}`, denomination)} +
    + ${previousMarketSizeComponent} +
    + `; + } +} + + +/** +* @typedef {Object} AssetOrCurrencyElement +* @property {string} id +* @property {string} name +* @property {number} marketSize +* @property {string} denomination +*/ + +class AssetsAndCurrencies extends BaseHTMLElement { + + #assets = []; + #assetsValueChangeReason = null; + #currencies = []; + #denomination = "USD"; + #assetsContainer = null; + #currenciesContainer = null; + + /** @type {AssetOrCurrencyElement[]} */ + set assets(value) { + this.#assets = value; + this.#renderAssets(); + } + + set assetsValueChangeReason(value) { + this.#assetsValueChangeReason = value; + this.#renderAssets(); + } + + /** @type {AssetOrCurrencyElement[]} */ + set currencies(value) { + this.#currencies = value; + this.#renderCurrencies(); + } + + /** @type {string} */ + set denomination(value) { + this.#denomination = value; + this.#renderAssets(); + this.#renderCurrencies(); + } + + connectedCallback() { + this.innerHTML = ` +
    + +
    + ${this.translation('assets-header')} + ${this.translation('currencies-header')} +
    +
    +
    + ${this.#assetsHTML()} +
    +
    + ${this.#currenciesHTML()} +
    +
    +
    +
    `; + + const tabsBody = this.querySelector("[data-tabs-body]"); + this.#assetsContainer = tabsBody.children[0]; + this.#currenciesContainer = tabsBody.children[1]; + } + + #assetsHTML(previousAssetElements = []) { + return this.#assets.map(a => { + const previousAsset = previousAssetElements.find(pa => pa.id == a.id); + let previousMarketSize; + if (!previousAsset) { + previousMarketSize = a.marketSize; + } else { + const previousAssetDenomination = previousAsset.getAttribute("denomination"); + const previousAssetMarketSize = previousAsset.getAttribute("market-size"); + // if denomination has changed, comparing current market size with the previous is meaningless + if (previousAssetDenomination != a.denomination) { + previousMarketSize = a.marketSize; + } else if (previousAssetMarketSize != a.marketSize) { + previousMarketSize = previousAssetMarketSize; + } else { + previousMarketSize = previousAsset.getAttribute("previous-market-size"); + } + } + + return ` + `; + }).join("\n"); + } + + #currenciesHTML(previousCurrencyElements = []) { + return this.#currencies.map(c => { + const previousCurrency = previousCurrencyElements.find(pc => pc.id == c.id); + let previousMarketSize; + if (!previousCurrency) { + previousMarketSize = c.marketSize; + } else { + const previousCurrencyDenomination = previousCurrency.getAttribute("denomination"); + const previousCurrencyMarketSize = previousCurrency.getAttribute("market-size"); + // if denomination has changed, comparing current market size with the previous is meaningless + if (previousCurrencyDenomination != c.denomination) { + previousMarketSize = c.marketSize; + } else if (previousCurrencyMarketSize != c.marketSize) { + previousMarketSize = previousCurrencyMarketSize; + } else { + previousMarketSize = previousCurrency.getAttribute("previous-market-size"); + } + } + + return ` + `}) + .join("\n"); + } + + #renderAssets() { + if (this.#assetsContainer) { + const currentAssetElements = [...this.querySelectorAll("asset-element")]; + this.#assetsContainer.innerHTML = this.#assetsHTML(currentAssetElements); + } + } + + #renderCurrencies() { + if (this.#currenciesContainer) { + const currentCurrencyElements = [...this.querySelectorAll("currency-element")]; + this.#currenciesContainer.innerHTML = this.#currenciesHTML(currentCurrencyElements); + } + } +} + + +class MarketsHeader extends BaseHTMLElement { + + _denomination = 'USD'; + _liveUpdatesEnabled = true; + _denominationExchangeRates = []; + _liveUpdatesEnabledElement = null; + _denominationElement = null; + + + set denomination(value) { + this._denomination = value; + if (this._denominationElement) { + this._denominationElement.textContent = this._denomination; + } + } + + set denominationExchangeRates(value) { + this._denominationExchangeRates = value; + this._renderDenominationOptions(); + } + + connectedCallback() { + this.innerHTML = ` +
    +
    ${this.translation('live-updates')} ${this._liveUpdatesElementText()} +
    + ${this.translation('markets-in')} + + ${this._denomination} +
      + ${this._denominationOptionsHTML()} +
    +
    +
    + `; + + this._liveUpdatesEnabledElement = this.querySelector("span"); + this._liveUpdatesEnabledElement.onclick = () => { + this._liveUpdatesEnabled = !this._liveUpdatesEnabled; + this._liveUpdatesEnabledElement.textContent = this._liveUpdatesElementText(); + this.dispatchEvent(new CustomEvent('mh.live-updates-toggled', { bubbles: true, detail: this._liveUpdatesEnabled })); + }; + + this._denominationElement = this.querySelector("drop-down-container > span"); + } + + _denominationOptionsHTML() { + return this._denominationExchangeRates.map(der => `
  • ${der.name}: ${der.exchangeRate}
  • `).join('\n'); + } + + _renderDenominationOptions() { + const optionsContainer = this.querySelector("ul"); + if (optionsContainer) { + optionsContainer.innerHTML = this._denominationOptionsHTML(); + this._setOptionsClickHandlers(); + } + } + + _setOptionsClickHandlers() { + [...this.querySelectorAll("li")].forEach(o => { + o.onclick = () => { + this._denomination = o.getAttribute("data-option-id"); + this._denominationElement.textContent = this._denomination; + this.dispatchEvent(new CustomEvent('mh.denomination-changed', { bubbles: true, detail: this._denomination })); + }; + }); + } + + _liveUpdatesElementText() { + return `${this._liveUpdatesEnabled ? this.translation('live-updates-on') : this.translation('live-updates-off')}`; + } +} + + +class MarketsComparator extends BaseHTMLElement { + + static observedAttributes = ["asset-items", "currency-items"]; + + _fromMarketsComparatorInput = null; + _toMarketsComparatorInput = null; + _comparisonElement = null; + + _fromMarketSize = null; + _toMarketSize = null; + + /** @type {AssetOrCurrency[]} */ + set assets(value) { + if (this._fromMarketsComparatorInput) { + this._setValuesUsingAttributes(this._fromMarketsComparatorInput, value, "asset"); + } + if (this._toMarketsComparatorInput) { + this._setValuesUsingAttributes(this._toMarketsComparatorInput, value, "asset"); + } + } + + /** @type {AssetOrCurrency[]} */ + set currencies(value) { + if (this._fromMarketsComparatorInput) { + this._setValuesUsingAttributes(this._fromMarketsComparatorInput, value, "currency"); + } + if (this._toMarketsComparatorInput) { + this._setValuesUsingAttributes(this._toMarketsComparatorInput, value, "currency"); + } + } + + _setValuesUsingAttributes(element, values, prefix) { + for (let i = 0; i < values.length; i++) { + element.setAttribute(`${prefix}-${i}-name`, values[i].name); + element.setAttribute(`${prefix}-${i}-market-size`, values[i].marketSize); + } + element.setAttribute(`${prefix}-items`, values.length); + } + + connectedCallback() { + this._render(); + + this._chosenMarketSizeChangedEventHandler = e => { + const { name, marketSize } = e.detail; + if (e.target === this._fromMarketsComparatorInput) { + this._fromMarketSize = marketSize; + this._renderComparisonElementHTML(); + this.dispatchEvent(new CustomEvent("mc.from-market-size-changed", { + bubbles: true, + detail: { name, marketSize } + })); + } else if (e.target === this._toMarketsComparatorInput) { + this._toMarketSize = marketSize; + this._renderComparisonElementHTML(); + this.dispatchEvent(new CustomEvent("mc.to-market-size-changed", { + bubbles: true, + detail: { name, marketSize } + })); + } + } + document.addEventListener('mci.chosen-market-size-changed', this._chosenMarketSizeChangedEventHandler); + } + + disconnectedCallback() { + document.removeEventListener('mci.chosen-market-size-changed', this._chosenMarketSizeChangedEventHandler); + } + + attributeChangedCallback(name, oldValue, newValue) { + if (name == 'asset-items') { + this.assets = assetsFromAttributes(this); + } else if (name == 'currency-items') { + this.currencies = currenciesFromAttributes(this); + } + } + + _render() { + this.innerHTML = ` +
    + + +
    ${this.translation('markets-to')}
    + + +
    ${this._comparisonElementHTML()}
    +
    + `; + + [this._fromMarketsComparatorInput, this._toMarketsComparatorInput] = this.querySelectorAll("markets-comparator-input"); + this._comparisonElement = this.querySelector('[data-comparison-element]'); + } + + _comparisonElementHTML() { + if (!this._fromMarketSize || !this._toMarketSize) { + return '-'; + } + return `${this._fromMarketSize.toExponential(3)} / ${this._toMarketSize.toExponential(3)} = ${this._chosenMarketsComparedValue()}`; + } + + _chosenMarketsComparedValue() { + if (!this._fromMarketSize || !this._toMarketSize) { + return 0; + } + return Math.round(this._fromMarketSize * 1000 / this._toMarketSize) / 1000.0; + } + + _renderComparisonElementHTML() { + this._comparisonElement.innerHTML = this._comparisonElementHTML(); + } +} + +function assetsFromAttributes(element) { + const countAttribute = element.getAttribute("asset-items"); + if (!countAttribute) { + return []; + } + const assets = []; + for (let i = 0; i < parseInt(countAttribute); i++) { + const name = element.getAttribute(`asset-${i}-name`); + const marketSize = element.getAttribute(`asset-${i}-market-size`); + if (name && marketSize) { + assets.push({ name, marketSize: parseInt(marketSize) }); + } + } + return assets; +} + +function currenciesFromAttributes(element) { + const countAttribute = element.getAttribute("currency-items"); + if (!countAttribute) { + return []; + } + const currencies = []; + for (let i = 0; i < parseInt(countAttribute); i++) { + const name = element.getAttribute(`currency-${i}-name`); + const marketSize = element.getAttribute(`currency-${i}-market-size`); + if (name && marketSize) { + currencies.push({ name, marketSize: parseInt(marketSize) }); + } + } + return currencies; +} + +class MarketsComparatorInput extends BaseHTMLElement { + + static observedAttributes = ["asset-items", "currency-items"]; + + /** @type {AssetOrCurrency[]} */ + _assets = []; + /** @type {AssetOrCurrency[]} */ + _currencies = []; + /** @type {AssetOrCurrency[]} */ + _assetOrCurrencyOptions = []; + + set assets(value) { + this._assets = value; + this._recalculateAssetOrCurrencyOptions(); + } + + set currencies(value) { + this._currencies = value; + this._recalculateAssetOrCurrencyOptions(); + } + + _assetOrCurrencyInput = null; + _curencyTurnoverInputMultiplier = 1; + + connectedCallback() { + this._render(); + } + + attributeChangedCallback(name, oldValue, newValue) { + if (name == 'asset-items') { + this.assets = assetsFromAttributes(this); + } else if (name == 'currency-items') { + this.currencies = currenciesFromAttributes(this); + } + } + + _render() { + const optionsZIndex = this.getAttribute("options-z-index") ?? '99'; + this.innerHTML = ` + +
    + ${this._dropDownHeaderHTML()} +
    +
      + ${this._optionsHTML()} +
    +
    + `; + + this._setOptionsClickHandlers(); + + this.querySelector('[data-drop-down-header]') + .querySelector("input")?.addEventListener("input", e => { + this._curencyTurnoverInputMultiplier = e.target.value; + this._calculateChosenMarketSizeChange(); + }); + } + + _optionsHTML() { + return this._assetOrCurrencyOptions.map(o => + `
  • ${o.name}
  • `) + .join('\n'); + } + + _dropDownHeaderHTML() { + let marketSizeHTML; + if (this._isAsset()) { + marketSizeHTML = `${this.translation('market-size-input-label')} + + ${this.translation('days-turnover-input-label')} + `; + } else { + marketSizeHTML = ``; + } + return ` + ${this._assetOrCurrencyInput ?? this.translation('asset-or-currency-input-placeholder')} + ${marketSizeHTML} + `; + } + + _setOptionsClickHandlers() { + [...this.querySelectorAll("li")].forEach(o => { + o.onclick = () => { + this._assetOrCurrencyInput = o.getAttribute("data-option-id"); + this._calculateChosenMarketSizeChange(); + this._render(); + }; + }); + } + + _renderOptionsHTML() { + const optionsContainer = this.querySelector("ul"); + if (optionsContainer) { + optionsContainer.innerHTML = this._optionsHTML(); + this._setOptionsClickHandlers(); + } + } + + _isAsset() { + return this._assetOrCurrencyInput ? this._assets.find(a => a.name == this._assetOrCurrencyInput) : false; + } + + _isCurrency() { + return this._assetOrCurrencyInput ? this._currencies.find(c => c.name == this._assetOrCurrencyInput) : false; + } + + _recalculateAssetOrCurrencyOptions() { + const assetOrCurrencyOptions = []; + this._assets.forEach(a => assetOrCurrencyOptions.push(a)); + this._currencies.forEach(c => assetOrCurrencyOptions.push(c)); + this._assetOrCurrencyOptions = assetOrCurrencyOptions; + + this._renderOptionsHTML(); + this._calculateChosenMarketSizeChange(); + } + + _calculateChosenMarketSizeChange() { + if (!this._assetOrCurrencyInput) { + return; + } + + const assetInput = this._assets.find(a => a.name == this._assetOrCurrencyInput); + const currencyInput = this._currencies.find(c => c.name == this._assetOrCurrencyInput); + + const inputMarketSize = assetInput ? assetInput.marketSize : currencyInput.marketSize * this._curencyTurnoverInputMultiplier; + + this.dispatchEvent(new CustomEvent("mci.chosen-market-size-changed", { + bubbles: true, + detail: { name: this._assetOrCurrencyInput, marketSize: inputMarketSize } + })); + } +} + + +/** +* @typedef {Object} AssetOrCurrencyProjection +* @property {number} marketSize +* @property {number} growthRate +*/ + +class ProjectionsCalculator extends BaseHTMLElement { + + /** @type {?AssetOrCurrency} */ + _assetOrCurrency1 = null; + /** @type {?AssetOrCurrency} */ + _assetOrCurrency2 = null; + /** @type {?number} */ + _assetOrCurrency1ExpectedGrowthRate = null; + /** @type {?number} */ + _assetOrCurrency2ExpectedGrowthRate = null; + /** @type {?number} */ + _customProjectionYears = null; + + set assetOrCurrency1(value) { + this._assetOrCurrency1 = value; + this._renderProjectionsResultsHTML(); + } + + set assetOrCurrency2(value) { + this._assetOrCurrency2 = value; + this._renderProjectionsResultsHTML(); + } + + _assetOrCurrency1Header = null; + _assetOrCurrency1Input = null; + _assetOrCurrency2Header = null; + _assetOrCurrency2Input = null; + _customProjectionInput = null; + _projectionsResultsContainer = null; + _customProjectionContainer = null; + _customProjectionTextElement = null; + _customProjectionResultContainer = null; + + connectedCallback() { + this.innerHTML = ` +
    +
    ${this._assetOrCurrencyHeaderText(this._assetOrCurrency1)}
    + +
    ${this._assetOrCurrencyHeaderText(this._assetOrCurrency2)}
    + +
    + ${this._projectionsResultsHTML()} +
    +
    + ${this._customProjectionHTML()} +
    +
    + `; + + const container = this.querySelector("div"); + + const divs = container.querySelectorAll("div"); + [this._assetOrCurrency1Header, this._assetOrCurrency2Header, this._projectionsResultsContainer] = divs; + this._customProjectionContainer = divs[divs.length - 1]; + + [this._assetOrCurrency1Input, this._assetOrCurrency2Input, + this._customProjectionInput] = container.querySelectorAll("input"); + + this._assetOrCurrency1Input.addEventListener("input", e => { + this._assetOrCurrency1ExpectedGrowthRate = parseInt(e.target.value); + this._renderProjectionsResultsHTML(); + }); + this._assetOrCurrency2Input.addEventListener("input", e => { + this._assetOrCurrency2ExpectedGrowthRate = parseInt(e.target.value); + this._renderProjectionsResultsHTML(); + }); + this._customProjectionInput.addEventListener("input", e => { + this._customProjectionYears = parseInt(e.target.value); + this._updateCustomProjectionText(); + this._updateCustomProjectionResult(); + }); + + this._customProjectionTextElement = this.querySelector('[data-custom-projection-text-element]'); + this._customProjectionResultContainer = this.querySelector('[data-custom-projection-result-container]'); + } + + _assetOrCurrencyHeaderText(assetOrCurrency) { + return `${assetOrCurrency ? assetOrCurrency.name : this.translation('asset-or-currency-placeholder')} ${this.translation('asset-or-currency-expected-annual-growth-rate')}:`; + } + + _renderProjectionsResultsHTML() { + if (this._projectionsResultsContainer && this._customProjectionContainer) { + this._projectionsResultsContainer.innerHTML = this._projectionsResultsHTML(); + this._updateCustomProjectionResult(); + } + } + + _projectionsResultsHTML() { + const inYearsText = (years) => `${this.translation('results-in-header')} ${years} ${this.translation(years == 1 ? 'year' : 'years')}`; + const currentYear = new Date().getFullYear(); + return [1, 5, 10].map(y => ` +
    ${inYearsText(y)} (${currentYear + y}):
    + ${this._projectionsResultHTML(y)}`) + .join('\n'); + } + + _projectionsResultHTML(years) { + const ac1 = this._assetOrCurrency1WithExpectedGrowthRate(); + const ac2 = this._assetOrCurrency2WithExpectedGrowthRate(); + if (years != null && ac1 != null && ac2 != null) { + return ` + + `; + } + return '
    -
    '; + } + + _customProjectionHTML() { + return ` + ${this.translation('results-in-header')} + ${this._customProjectionYearText()}: +
    ${this._projectionsResultHTML(this._customProjectionYears)}
    + `; + } + + _customProjectionYearText() { + const currentYear = new Date().getFullYear(); + return '(' + (this._customProjectionYears == null || Number.isNaN(this._customProjectionYears) ? + `${currentYear} + N` : (currentYear + this._customProjectionYears)) + ')'; + } + + _updateCustomProjectionText() { + this._customProjectionTextElement.textContent = this._customProjectionYearText(); + } + + _updateCustomProjectionResult() { + this._customProjectionContainer.innerHTML = this._projectionsResultHTML(this._customProjectionYears); + } + + _assetOrCurrency1WithExpectedGrowthRate() { + if (this._assetOrCurrency1 && this._assetOrCurrency1ExpectedGrowthRate != null) { + return { marketSize: this._assetOrCurrency1.marketSize, growthRate: this._assetOrCurrency1ExpectedGrowthRate }; + } + return null; + } + + _assetOrCurrency2WithExpectedGrowthRate() { + if (this._assetOrCurrency2 && this._assetOrCurrency2ExpectedGrowthRate != null) { + return { marketSize: this._assetOrCurrency2.marketSize, growthRate: this._assetOrCurrency2ExpectedGrowthRate }; + } + return null; + } +} + +class ProjectionsResult extends HTMLElement { + + static observedAttributes = [ + "years", + "asset-or-currency-1-market-size", "asset-or-currency-1-growth-rate", + "asset-or-currency-2-market-size", "asset-or-currency-2-growth-rate" + ]; + + /** @type {number} */ + _years = 1; + /** @type {?AssetOrCurrencyProjection} */ + _assetOrCurrency1 = null; + /** @type {?AssetOrCurrencyProjection} */ + _assetOrCurrency2 = null; + + set years(value) { + this._years = value; + this._render(); + } + + set assetOrCurrency1(value) { + this._assetOrCurrency1 = value; + this._render(); + } + + set assetOrCurrency2(value) { + this._assetOrCurrency2 = value; + this._render(); + } + + connectedCallback() { + this._render(); + } + + attributeChangedCallback(name, oldValue, newValue) { + if (name.includes("asset-or-currency-1")) { + const assetOrCurrency = this._assetOrCurrencyFromAttributes("asset-or-currency-1"); + if (assetOrCurrency) { + this.assetOrCurrency1 = assetOrCurrency; + } + } else if (name.includes("asset-or-currency-2")) { + const assetOrCurrency = this._assetOrCurrencyFromAttributes("asset-or-currency-2"); + if (assetOrCurrency) { + this.assetOrCurrency2 = assetOrCurrency; + } + } else if (name == 'years') { + this.years = parseInt(newValue); + } + } + + _assetOrCurrencyFromAttributes(prefix) { + const marketSize = this.getAttribute(`${prefix}-market-size`); + const growthRate = this.getAttribute(`${prefix}-growth-rate`); + if (marketSize && growthRate) { + return { marketSize: parseInt(marketSize), growthRate: parseInt(growthRate) }; + } + return null; + } + + _render() { + const nominator = this._exponentialNumberString(this._projectionNumerator()); + const denominator = this._exponentialNumberString(this._projectionDenominator()); + this.innerHTML = ` +
    ${nominator} / ${denominator} = ${this._projection()} +
    + `; + } + + _exponentialNumberString(n) { + return n?.toExponential(3) ?? ''; + } + + _marketSizeChangedByRate(marketSize, growthRate, decrease = false) { + let changedMarketSize; + if (decrease) { + changedMarketSize = marketSize - (marketSize * growthRate / 100.0); + } else { + changedMarketSize = marketSize + (marketSize * growthRate / 100.0); + } + if (changedMarketSize <= 0 || Number.isNaN(changedMarketSize)) { + return null; + } + return changedMarketSize; + } + + _marketSizeChangedByRateInGivenYears(marketSize, growthRate, years) { + const negativeYears = years < 0; + let increasedMarketSize = marketSize; + for (let i = 0; i < Math.abs(years); i++) { + increasedMarketSize = this._marketSizeChangedByRate(increasedMarketSize, growthRate, negativeYears); + if (!increasedMarketSize) { + return null; + } + } + return increasedMarketSize; + } + + _projectionNumerator() { + if (this._assetOrCurrency1) { + return this._marketSizeChangedByRateInGivenYears( + this._assetOrCurrency1.marketSize, + this._assetOrCurrency1.growthRate, + this._years); + } + return null; + } + + _projectionDenominator() { + if (this._assetOrCurrency2) { + return this._marketSizeChangedByRateInGivenYears( + this._assetOrCurrency2.marketSize, + this._assetOrCurrency2.growthRate, + this._years); + } + return null; + } + + _projection() { + const numerator = this._projectionNumerator(); + const denominator = this._projectionDenominator(); + if (numerator && denominator) { + return Math.round(numerator * 1000 / denominator) / 1000.0; + } + return ''; + } +} + + +class MarketsProjections extends BaseHTMLElement { + + _marketsComparatorComponent = null; + _projectionsCalculatorComponent = null; + + /** @type {?AssetOrCurrency} */ + _fromAssetOrCurrency = null; + /** @type {?AssetOrCurrency} */ + _toAssetOrCurrency = null; + + /** @type {AssetOrCurrency[]} */ + set assets(value) { + if (this._marketsComparatorComponent) { + this._marketsComparatorComponent.assets = value; + } + } + + /** @type {AssetOrCurrency[]} */ + set currencies(value) { + if (this._marketsComparatorComponent) { + this._marketsComparatorComponent.currencies = value; + } + } + + connectedCallback() { + this.innerHTML = ` +
    +

    ${this.translation('projections-header')}

    + + + + +
    + `; + + this._marketsComparatorComponent = this.querySelector("markets-comparator"); + this._projectionsCalculatorComponent = this.querySelector("projections-calculator"); + + const fromMarketSizeChangedEventHandler = e => { + this._fromAssetOrCurrency = e.detail; + this._projectionsCalculatorComponent.assetOrCurrency1 = this._fromAssetOrCurrency; + }; + const toMarketSizeChangedEventHandler = e => { + this._toAssetOrCurrency = e.detail; + this._projectionsCalculatorComponent.assetOrCurrency2 = this._toAssetOrCurrency; + }; + + this.addEventListener('mc.from-market-size-changed', fromMarketSizeChangedEventHandler); + this.addEventListener('mc.to-market-size-changed', toMarketSizeChangedEventHandler); + } +} + + +export function registerComponents() { + customElements.define('tabs-container', TabsContainer); + customElements.define('tab-header', TabHeader); + customElements.define("drop-down-container", DropDownContainer); + customElements.define('info-modal', InfoModal); + customElements.define('error-modal', ErrorModal); + customElements.define("asset-element", AssetElement); + customElements.define("currency-element", CurrencyElement); + customElements.define('assets-and-currencies', AssetsAndCurrencies); + customElements.define("markets-header", MarketsHeader); + customElements.define("markets-comparator-input", MarketsComparatorInput); + customElements.define('markets-comparator', MarketsComparator); + customElements.define("projections-result", ProjectionsResult); + customElements.define("projections-calculator", ProjectionsCalculator); + customElements.define('markets-projections', MarketsProjections); +} \ No newline at end of file diff --git a/web-components-reuse/server/package-lock.json b/web-components-reuse/server/package-lock.json new file mode 100644 index 00000000..9cd1d45b --- /dev/null +++ b/web-components-reuse/server/package-lock.json @@ -0,0 +1,1978 @@ +{ + "name": "web-components-reuse-server", + "version": "1.0.0", + "lockfileVersion": 3, + "requires": true, + "packages": { + "": { + "name": "web-components-reuse-server", + "version": "1.0.0", + "license": "ISC", + "dependencies": { + "@tailwindcss/cli": "^4.1.14", + "cors": "^2.8.5", + "express": "^5.1.0", + "tailwindcss": "^4.1.14" + }, + "engines": { + "node": ">=20.0.0" + } + }, + "node_modules/@isaacs/fs-minipass": { + "version": "4.0.1", + "resolved": "https://registry.npmjs.org/@isaacs/fs-minipass/-/fs-minipass-4.0.1.tgz", + "integrity": "sha512-wgm9Ehl2jpeqP3zw/7mo3kRHFp5MEDhqAdwy1fTGkHAwnkGOVsgpvQhL8B5n1qlb01jV3n/bI0ZfZp5lWA1k4w==", + "license": "ISC", + "dependencies": { + "minipass": "^7.0.4" + }, + "engines": { + "node": ">=18.0.0" + } + }, + "node_modules/@jridgewell/gen-mapping": { + "version": "0.3.13", + "resolved": "https://registry.npmjs.org/@jridgewell/gen-mapping/-/gen-mapping-0.3.13.tgz", + "integrity": "sha512-2kkt/7niJ6MgEPxF0bYdQ6etZaA+fQvDcLKckhy1yIQOzaoKjBBjSj63/aLVjYE3qhRt5dvM+uUyfCg6UKCBbA==", + "license": "MIT", + "dependencies": { + "@jridgewell/sourcemap-codec": "^1.5.0", + "@jridgewell/trace-mapping": "^0.3.24" + } + }, + "node_modules/@jridgewell/remapping": { + "version": "2.3.5", + "resolved": "https://registry.npmjs.org/@jridgewell/remapping/-/remapping-2.3.5.tgz", + "integrity": "sha512-LI9u/+laYG4Ds1TDKSJW2YPrIlcVYOwi2fUC6xB43lueCjgxV4lffOCZCtYFiH6TNOX+tQKXx97T4IKHbhyHEQ==", + "license": "MIT", + "dependencies": { + "@jridgewell/gen-mapping": "^0.3.5", + "@jridgewell/trace-mapping": "^0.3.24" + } + }, + "node_modules/@jridgewell/resolve-uri": { + "version": "3.1.2", + "resolved": "https://registry.npmjs.org/@jridgewell/resolve-uri/-/resolve-uri-3.1.2.tgz", + "integrity": "sha512-bRISgCIjP20/tbWSPWMEi54QVPRZExkuD9lJL+UIxUKtwVJA8wW1Trb1jMs1RFXo1CBTNZ/5hpC9QvmKWdopKw==", + "license": "MIT", + "engines": { + "node": ">=6.0.0" + } + }, + "node_modules/@jridgewell/sourcemap-codec": { + "version": "1.5.5", + "resolved": "https://registry.npmjs.org/@jridgewell/sourcemap-codec/-/sourcemap-codec-1.5.5.tgz", + "integrity": "sha512-cYQ9310grqxueWbl+WuIUIaiUaDcj7WOq5fVhEljNVgRfOUhY9fy2zTvfoqWsnebh8Sl70VScFbICvJnLKB0Og==", + "license": "MIT" + }, + "node_modules/@jridgewell/trace-mapping": { + "version": "0.3.31", + "resolved": "https://registry.npmjs.org/@jridgewell/trace-mapping/-/trace-mapping-0.3.31.tgz", + "integrity": "sha512-zzNR+SdQSDJzc8joaeP8QQoCQr8NuYx2dIIytl1QeBEZHJ9uW6hebsrYgbz8hJwUQao3TWCMtmfV8Nu1twOLAw==", + "license": "MIT", + "dependencies": { + "@jridgewell/resolve-uri": "^3.1.0", + "@jridgewell/sourcemap-codec": "^1.4.14" + } + }, + "node_modules/@parcel/watcher": { + "version": "2.5.1", + "resolved": "https://registry.npmjs.org/@parcel/watcher/-/watcher-2.5.1.tgz", + "integrity": "sha512-dfUnCxiN9H4ap84DvD2ubjw+3vUNpstxa0TneY/Paat8a3R4uQZDLSvWjmznAY/DoahqTHl9V46HF/Zs3F29pg==", + "hasInstallScript": true, + "license": "MIT", + "dependencies": { + "detect-libc": "^1.0.3", + "is-glob": "^4.0.3", + "micromatch": "^4.0.5", + "node-addon-api": "^7.0.0" + }, + "engines": { + "node": ">= 10.0.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/parcel" + }, + "optionalDependencies": { + "@parcel/watcher-android-arm64": "2.5.1", + "@parcel/watcher-darwin-arm64": "2.5.1", + "@parcel/watcher-darwin-x64": "2.5.1", + "@parcel/watcher-freebsd-x64": "2.5.1", + "@parcel/watcher-linux-arm-glibc": "2.5.1", + "@parcel/watcher-linux-arm-musl": "2.5.1", + "@parcel/watcher-linux-arm64-glibc": "2.5.1", + "@parcel/watcher-linux-arm64-musl": "2.5.1", + "@parcel/watcher-linux-x64-glibc": "2.5.1", + "@parcel/watcher-linux-x64-musl": "2.5.1", + "@parcel/watcher-win32-arm64": "2.5.1", + "@parcel/watcher-win32-ia32": "2.5.1", + "@parcel/watcher-win32-x64": "2.5.1" + } + }, + "node_modules/@parcel/watcher-android-arm64": { + "version": "2.5.1", + "resolved": "https://registry.npmjs.org/@parcel/watcher-android-arm64/-/watcher-android-arm64-2.5.1.tgz", + "integrity": "sha512-KF8+j9nNbUN8vzOFDpRMsaKBHZ/mcjEjMToVMJOhTozkDonQFFrRcfdLWn6yWKCmJKmdVxSgHiYvTCef4/qcBA==", + "cpu": [ + "arm64" + ], + "license": "MIT", + "optional": true, + "os": [ + "android" + ], + "engines": { + "node": ">= 10.0.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/parcel" + } + }, + "node_modules/@parcel/watcher-darwin-arm64": { + "version": "2.5.1", + "resolved": "https://registry.npmjs.org/@parcel/watcher-darwin-arm64/-/watcher-darwin-arm64-2.5.1.tgz", + "integrity": "sha512-eAzPv5osDmZyBhou8PoF4i6RQXAfeKL9tjb3QzYuccXFMQU0ruIc/POh30ePnaOyD1UXdlKguHBmsTs53tVoPw==", + "cpu": [ + "arm64" + ], + "license": "MIT", + "optional": true, + "os": [ + "darwin" + ], + "engines": { + "node": ">= 10.0.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/parcel" + } + }, + "node_modules/@parcel/watcher-darwin-x64": { + "version": "2.5.1", + "resolved": "https://registry.npmjs.org/@parcel/watcher-darwin-x64/-/watcher-darwin-x64-2.5.1.tgz", + "integrity": "sha512-1ZXDthrnNmwv10A0/3AJNZ9JGlzrF82i3gNQcWOzd7nJ8aj+ILyW1MTxVk35Db0u91oD5Nlk9MBiujMlwmeXZg==", + "cpu": [ + "x64" + ], + "license": "MIT", + "optional": true, + "os": [ + "darwin" + ], + "engines": { + "node": ">= 10.0.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/parcel" + } + }, + "node_modules/@parcel/watcher-freebsd-x64": { + "version": "2.5.1", + "resolved": "https://registry.npmjs.org/@parcel/watcher-freebsd-x64/-/watcher-freebsd-x64-2.5.1.tgz", + "integrity": "sha512-SI4eljM7Flp9yPuKi8W0ird8TI/JK6CSxju3NojVI6BjHsTyK7zxA9urjVjEKJ5MBYC+bLmMcbAWlZ+rFkLpJQ==", + "cpu": [ + "x64" + ], + "license": "MIT", + "optional": true, + "os": [ + "freebsd" + ], + "engines": { + "node": ">= 10.0.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/parcel" + } + }, + "node_modules/@parcel/watcher-linux-arm-glibc": { + "version": "2.5.1", + "resolved": "https://registry.npmjs.org/@parcel/watcher-linux-arm-glibc/-/watcher-linux-arm-glibc-2.5.1.tgz", + "integrity": "sha512-RCdZlEyTs8geyBkkcnPWvtXLY44BCeZKmGYRtSgtwwnHR4dxfHRG3gR99XdMEdQ7KeiDdasJwwvNSF5jKtDwdA==", + "cpu": [ + "arm" + ], + "license": "MIT", + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": ">= 10.0.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/parcel" + } + }, + "node_modules/@parcel/watcher-linux-arm-musl": { + "version": "2.5.1", + "resolved": "https://registry.npmjs.org/@parcel/watcher-linux-arm-musl/-/watcher-linux-arm-musl-2.5.1.tgz", + "integrity": "sha512-6E+m/Mm1t1yhB8X412stiKFG3XykmgdIOqhjWj+VL8oHkKABfu/gjFj8DvLrYVHSBNC+/u5PeNrujiSQ1zwd1Q==", + "cpu": [ + "arm" + ], + "license": "MIT", + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": ">= 10.0.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/parcel" + } + }, + "node_modules/@parcel/watcher-linux-arm64-glibc": { + "version": "2.5.1", + "resolved": "https://registry.npmjs.org/@parcel/watcher-linux-arm64-glibc/-/watcher-linux-arm64-glibc-2.5.1.tgz", + "integrity": "sha512-LrGp+f02yU3BN9A+DGuY3v3bmnFUggAITBGriZHUREfNEzZh/GO06FF5u2kx8x+GBEUYfyTGamol4j3m9ANe8w==", + "cpu": [ + "arm64" + ], + "license": "MIT", + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": ">= 10.0.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/parcel" + } + }, + "node_modules/@parcel/watcher-linux-arm64-musl": { + "version": "2.5.1", + "resolved": "https://registry.npmjs.org/@parcel/watcher-linux-arm64-musl/-/watcher-linux-arm64-musl-2.5.1.tgz", + "integrity": "sha512-cFOjABi92pMYRXS7AcQv9/M1YuKRw8SZniCDw0ssQb/noPkRzA+HBDkwmyOJYp5wXcsTrhxO0zq1U11cK9jsFg==", + "cpu": [ + "arm64" + ], + "license": "MIT", + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": ">= 10.0.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/parcel" + } + }, + "node_modules/@parcel/watcher-linux-x64-glibc": { + "version": "2.5.1", + "resolved": "https://registry.npmjs.org/@parcel/watcher-linux-x64-glibc/-/watcher-linux-x64-glibc-2.5.1.tgz", + "integrity": "sha512-GcESn8NZySmfwlTsIur+49yDqSny2IhPeZfXunQi48DMugKeZ7uy1FX83pO0X22sHntJ4Ub+9k34XQCX+oHt2A==", + "cpu": [ + "x64" + ], + "license": "MIT", + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": ">= 10.0.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/parcel" + } + }, + "node_modules/@parcel/watcher-linux-x64-musl": { + "version": "2.5.1", + "resolved": "https://registry.npmjs.org/@parcel/watcher-linux-x64-musl/-/watcher-linux-x64-musl-2.5.1.tgz", + "integrity": "sha512-n0E2EQbatQ3bXhcH2D1XIAANAcTZkQICBPVaxMeaCVBtOpBZpWJuf7LwyWPSBDITb7In8mqQgJ7gH8CILCURXg==", + "cpu": [ + "x64" + ], + "license": "MIT", + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": ">= 10.0.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/parcel" + } + }, + "node_modules/@parcel/watcher-win32-arm64": { + "version": "2.5.1", + "resolved": "https://registry.npmjs.org/@parcel/watcher-win32-arm64/-/watcher-win32-arm64-2.5.1.tgz", + "integrity": "sha512-RFzklRvmc3PkjKjry3hLF9wD7ppR4AKcWNzH7kXR7GUe0Igb3Nz8fyPwtZCSquGrhU5HhUNDr/mKBqj7tqA2Vw==", + "cpu": [ + "arm64" + ], + "license": "MIT", + "optional": true, + "os": [ + "win32" + ], + "engines": { + "node": ">= 10.0.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/parcel" + } + }, + "node_modules/@parcel/watcher-win32-ia32": { + "version": "2.5.1", + "resolved": "https://registry.npmjs.org/@parcel/watcher-win32-ia32/-/watcher-win32-ia32-2.5.1.tgz", + "integrity": "sha512-c2KkcVN+NJmuA7CGlaGD1qJh1cLfDnQsHjE89E60vUEMlqduHGCdCLJCID5geFVM0dOtA3ZiIO8BoEQmzQVfpQ==", + "cpu": [ + "ia32" + ], + "license": "MIT", + "optional": true, + "os": [ + "win32" + ], + "engines": { + "node": ">= 10.0.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/parcel" + } + }, + "node_modules/@parcel/watcher-win32-x64": { + "version": "2.5.1", + "resolved": "https://registry.npmjs.org/@parcel/watcher-win32-x64/-/watcher-win32-x64-2.5.1.tgz", + "integrity": "sha512-9lHBdJITeNR++EvSQVUcaZoWupyHfXe1jZvGZ06O/5MflPcuPLtEphScIBL+AiCWBO46tDSHzWyD0uDmmZqsgA==", + "cpu": [ + "x64" + ], + "license": "MIT", + "optional": true, + "os": [ + "win32" + ], + "engines": { + "node": ">= 10.0.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/parcel" + } + }, + "node_modules/@tailwindcss/cli": { + "version": "4.1.14", + "resolved": "https://registry.npmjs.org/@tailwindcss/cli/-/cli-4.1.14.tgz", + "integrity": "sha512-2cErQRcsI8jIObUMVwcd1H2AWgGxwzozHJk7AKM2KB1taOp7L15xQ8kEsZrvVbOjNrb8yXtnSvNtJ+mhCB7EBg==", + "license": "MIT", + "dependencies": { + "@parcel/watcher": "^2.5.1", + "@tailwindcss/node": "4.1.14", + "@tailwindcss/oxide": "4.1.14", + "enhanced-resolve": "^5.18.3", + "mri": "^1.2.0", + "picocolors": "^1.1.1", + "tailwindcss": "4.1.14" + }, + "bin": { + "tailwindcss": "dist/index.mjs" + } + }, + "node_modules/@tailwindcss/node": { + "version": "4.1.14", + "resolved": "https://registry.npmjs.org/@tailwindcss/node/-/node-4.1.14.tgz", + "integrity": "sha512-hpz+8vFk3Ic2xssIA3e01R6jkmsAhvkQdXlEbRTk6S10xDAtiQiM3FyvZVGsucefq764euO/b8WUW9ysLdThHw==", + "license": "MIT", + "dependencies": { + "@jridgewell/remapping": "^2.3.4", + "enhanced-resolve": "^5.18.3", + "jiti": "^2.6.0", + "lightningcss": "1.30.1", + "magic-string": "^0.30.19", + "source-map-js": "^1.2.1", + "tailwindcss": "4.1.14" + } + }, + "node_modules/@tailwindcss/oxide": { + "version": "4.1.14", + "resolved": "https://registry.npmjs.org/@tailwindcss/oxide/-/oxide-4.1.14.tgz", + "integrity": "sha512-23yx+VUbBwCg2x5XWdB8+1lkPajzLmALEfMb51zZUBYaYVPDQvBSD/WYDqiVyBIo2BZFa3yw1Rpy3G2Jp+K0dw==", + "hasInstallScript": true, + "license": "MIT", + "dependencies": { + "detect-libc": "^2.0.4", + "tar": "^7.5.1" + }, + "engines": { + "node": ">= 10" + }, + "optionalDependencies": { + "@tailwindcss/oxide-android-arm64": "4.1.14", + "@tailwindcss/oxide-darwin-arm64": "4.1.14", + "@tailwindcss/oxide-darwin-x64": "4.1.14", + "@tailwindcss/oxide-freebsd-x64": "4.1.14", + "@tailwindcss/oxide-linux-arm-gnueabihf": "4.1.14", + "@tailwindcss/oxide-linux-arm64-gnu": "4.1.14", + "@tailwindcss/oxide-linux-arm64-musl": "4.1.14", + "@tailwindcss/oxide-linux-x64-gnu": "4.1.14", + "@tailwindcss/oxide-linux-x64-musl": "4.1.14", + "@tailwindcss/oxide-wasm32-wasi": "4.1.14", + "@tailwindcss/oxide-win32-arm64-msvc": "4.1.14", + "@tailwindcss/oxide-win32-x64-msvc": "4.1.14" + } + }, + "node_modules/@tailwindcss/oxide-android-arm64": { + "version": "4.1.14", + "resolved": "https://registry.npmjs.org/@tailwindcss/oxide-android-arm64/-/oxide-android-arm64-4.1.14.tgz", + "integrity": "sha512-a94ifZrGwMvbdeAxWoSuGcIl6/DOP5cdxagid7xJv6bwFp3oebp7y2ImYsnZBMTwjn5Ev5xESvS3FFYUGgPODQ==", + "cpu": [ + "arm64" + ], + "license": "MIT", + "optional": true, + "os": [ + "android" + ], + "engines": { + "node": ">= 10" + } + }, + "node_modules/@tailwindcss/oxide-darwin-arm64": { + "version": "4.1.14", + "resolved": "https://registry.npmjs.org/@tailwindcss/oxide-darwin-arm64/-/oxide-darwin-arm64-4.1.14.tgz", + "integrity": "sha512-HkFP/CqfSh09xCnrPJA7jud7hij5ahKyWomrC3oiO2U9i0UjP17o9pJbxUN0IJ471GTQQmzwhp0DEcpbp4MZTA==", + "cpu": [ + "arm64" + ], + "license": "MIT", + "optional": true, + "os": [ + "darwin" + ], + "engines": { + "node": ">= 10" + } + }, + "node_modules/@tailwindcss/oxide-darwin-x64": { + "version": "4.1.14", + "resolved": "https://registry.npmjs.org/@tailwindcss/oxide-darwin-x64/-/oxide-darwin-x64-4.1.14.tgz", + "integrity": "sha512-eVNaWmCgdLf5iv6Qd3s7JI5SEFBFRtfm6W0mphJYXgvnDEAZ5sZzqmI06bK6xo0IErDHdTA5/t7d4eTfWbWOFw==", + "cpu": [ + "x64" + ], + "license": "MIT", + "optional": true, + "os": [ + "darwin" + ], + "engines": { + "node": ">= 10" + } + }, + "node_modules/@tailwindcss/oxide-freebsd-x64": { + "version": "4.1.14", + "resolved": "https://registry.npmjs.org/@tailwindcss/oxide-freebsd-x64/-/oxide-freebsd-x64-4.1.14.tgz", + "integrity": "sha512-QWLoRXNikEuqtNb0dhQN6wsSVVjX6dmUFzuuiL09ZeXju25dsei2uIPl71y2Ic6QbNBsB4scwBoFnlBfabHkEw==", + "cpu": [ + "x64" + ], + "license": "MIT", + "optional": true, + "os": [ + "freebsd" + ], + "engines": { + "node": ">= 10" + } + }, + "node_modules/@tailwindcss/oxide-linux-arm-gnueabihf": { + "version": "4.1.14", + "resolved": "https://registry.npmjs.org/@tailwindcss/oxide-linux-arm-gnueabihf/-/oxide-linux-arm-gnueabihf-4.1.14.tgz", + "integrity": "sha512-VB4gjQni9+F0VCASU+L8zSIyjrLLsy03sjcR3bM0V2g4SNamo0FakZFKyUQ96ZVwGK4CaJsc9zd/obQy74o0Fw==", + "cpu": [ + "arm" + ], + "license": "MIT", + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": ">= 10" + } + }, + "node_modules/@tailwindcss/oxide-linux-arm64-gnu": { + "version": "4.1.14", + "resolved": "https://registry.npmjs.org/@tailwindcss/oxide-linux-arm64-gnu/-/oxide-linux-arm64-gnu-4.1.14.tgz", + "integrity": "sha512-qaEy0dIZ6d9vyLnmeg24yzA8XuEAD9WjpM5nIM1sUgQ/Zv7cVkharPDQcmm/t/TvXoKo/0knI3me3AGfdx6w1w==", + "cpu": [ + "arm64" + ], + "license": "MIT", + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": ">= 10" + } + }, + "node_modules/@tailwindcss/oxide-linux-arm64-musl": { + "version": "4.1.14", + "resolved": "https://registry.npmjs.org/@tailwindcss/oxide-linux-arm64-musl/-/oxide-linux-arm64-musl-4.1.14.tgz", + "integrity": "sha512-ISZjT44s59O8xKsPEIesiIydMG/sCXoMBCqsphDm/WcbnuWLxxb+GcvSIIA5NjUw6F8Tex7s5/LM2yDy8RqYBQ==", + "cpu": [ + "arm64" + ], + "license": "MIT", + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": ">= 10" + } + }, + "node_modules/@tailwindcss/oxide-linux-x64-gnu": { + "version": "4.1.14", + "resolved": "https://registry.npmjs.org/@tailwindcss/oxide-linux-x64-gnu/-/oxide-linux-x64-gnu-4.1.14.tgz", + "integrity": "sha512-02c6JhLPJj10L2caH4U0zF8Hji4dOeahmuMl23stk0MU1wfd1OraE7rOloidSF8W5JTHkFdVo/O7uRUJJnUAJg==", + "cpu": [ + "x64" + ], + "license": "MIT", + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": ">= 10" + } + }, + "node_modules/@tailwindcss/oxide-linux-x64-musl": { + "version": "4.1.14", + "resolved": "https://registry.npmjs.org/@tailwindcss/oxide-linux-x64-musl/-/oxide-linux-x64-musl-4.1.14.tgz", + "integrity": "sha512-TNGeLiN1XS66kQhxHG/7wMeQDOoL0S33x9BgmydbrWAb9Qw0KYdd8o1ifx4HOGDWhVmJ+Ul+JQ7lyknQFilO3Q==", + "cpu": [ + "x64" + ], + "license": "MIT", + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": ">= 10" + } + }, + "node_modules/@tailwindcss/oxide-wasm32-wasi": { + "version": "4.1.14", + "resolved": "https://registry.npmjs.org/@tailwindcss/oxide-wasm32-wasi/-/oxide-wasm32-wasi-4.1.14.tgz", + "integrity": "sha512-uZYAsaW/jS/IYkd6EWPJKW/NlPNSkWkBlaeVBi/WsFQNP05/bzkebUL8FH1pdsqx4f2fH/bWFcUABOM9nfiJkQ==", + "bundleDependencies": [ + "@napi-rs/wasm-runtime", + "@emnapi/core", + "@emnapi/runtime", + "@tybys/wasm-util", + "@emnapi/wasi-threads", + "tslib" + ], + "cpu": [ + "wasm32" + ], + "license": "MIT", + "optional": true, + "dependencies": { + "@emnapi/core": "^1.5.0", + "@emnapi/runtime": "^1.5.0", + "@emnapi/wasi-threads": "^1.1.0", + "@napi-rs/wasm-runtime": "^1.0.5", + "@tybys/wasm-util": "^0.10.1", + "tslib": "^2.4.0" + }, + "engines": { + "node": ">=14.0.0" + } + }, + "node_modules/@tailwindcss/oxide-win32-arm64-msvc": { + "version": "4.1.14", + "resolved": "https://registry.npmjs.org/@tailwindcss/oxide-win32-arm64-msvc/-/oxide-win32-arm64-msvc-4.1.14.tgz", + "integrity": "sha512-Az0RnnkcvRqsuoLH2Z4n3JfAef0wElgzHD5Aky/e+0tBUxUhIeIqFBTMNQvmMRSP15fWwmvjBxZ3Q8RhsDnxAA==", + "cpu": [ + "arm64" + ], + "license": "MIT", + "optional": true, + "os": [ + "win32" + ], + "engines": { + "node": ">= 10" + } + }, + "node_modules/@tailwindcss/oxide-win32-x64-msvc": { + "version": "4.1.14", + "resolved": "https://registry.npmjs.org/@tailwindcss/oxide-win32-x64-msvc/-/oxide-win32-x64-msvc-4.1.14.tgz", + "integrity": "sha512-ttblVGHgf68kEE4om1n/n44I0yGPkCPbLsqzjvybhpwa6mKKtgFfAzy6btc3HRmuW7nHe0OOrSeNP9sQmmH9XA==", + "cpu": [ + "x64" + ], + "license": "MIT", + "optional": true, + "os": [ + "win32" + ], + "engines": { + "node": ">= 10" + } + }, + "node_modules/@tailwindcss/oxide/node_modules/detect-libc": { + "version": "2.1.2", + "resolved": "https://registry.npmjs.org/detect-libc/-/detect-libc-2.1.2.tgz", + "integrity": "sha512-Btj2BOOO83o3WyH59e8MgXsxEQVcarkUOpEYrubB0urwnN10yQ364rsiByU11nZlqWYZm05i/of7io4mzihBtQ==", + "license": "Apache-2.0", + "engines": { + "node": ">=8" + } + }, + "node_modules/accepts": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/accepts/-/accepts-2.0.0.tgz", + "integrity": "sha512-5cvg6CtKwfgdmVqY1WIiXKc3Q1bkRqGLi+2W/6ao+6Y7gu/RCwRuAhGEzh5B4KlszSuTLgZYuqFqo5bImjNKng==", + "license": "MIT", + "dependencies": { + "mime-types": "^3.0.0", + "negotiator": "^1.0.0" + }, + "engines": { + "node": ">= 0.6" + } + }, + "node_modules/body-parser": { + "version": "2.2.0", + "resolved": "https://registry.npmjs.org/body-parser/-/body-parser-2.2.0.tgz", + "integrity": "sha512-02qvAaxv8tp7fBa/mw1ga98OGm+eCbqzJOKoRt70sLmfEEi+jyBYVTDGfCL/k06/4EMk/z01gCe7HoCH/f2LTg==", + "license": "MIT", + "dependencies": { + "bytes": "^3.1.2", + "content-type": "^1.0.5", + "debug": "^4.4.0", + "http-errors": "^2.0.0", + "iconv-lite": "^0.6.3", + "on-finished": "^2.4.1", + "qs": "^6.14.0", + "raw-body": "^3.0.0", + "type-is": "^2.0.0" + }, + "engines": { + "node": ">=18" + } + }, + "node_modules/braces": { + "version": "3.0.3", + "resolved": "https://registry.npmjs.org/braces/-/braces-3.0.3.tgz", + "integrity": "sha512-yQbXgO/OSZVD2IsiLlro+7Hf6Q18EJrKSEsdoMzKePKXct3gvD8oLcOQdIzGupr5Fj+EDe8gO/lxc1BzfMpxvA==", + "license": "MIT", + "dependencies": { + "fill-range": "^7.1.1" + }, + "engines": { + "node": ">=8" + } + }, + "node_modules/bytes": { + "version": "3.1.2", + "resolved": "https://registry.npmjs.org/bytes/-/bytes-3.1.2.tgz", + "integrity": "sha512-/Nf7TyzTx6S3yRJObOAV7956r8cr2+Oj8AC5dt8wSP3BQAoeX58NoHyCU8P8zGkNXStjTSi6fzO6F0pBdcYbEg==", + "license": "MIT", + "engines": { + "node": ">= 0.8" + } + }, + "node_modules/call-bind-apply-helpers": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/call-bind-apply-helpers/-/call-bind-apply-helpers-1.0.2.tgz", + "integrity": "sha512-Sp1ablJ0ivDkSzjcaJdxEunN5/XvksFJ2sMBFfq6x0ryhQV/2b/KwFe21cMpmHtPOSij8K99/wSfoEuTObmuMQ==", + "license": "MIT", + "dependencies": { + "es-errors": "^1.3.0", + "function-bind": "^1.1.2" + }, + "engines": { + "node": ">= 0.4" + } + }, + "node_modules/call-bound": { + "version": "1.0.4", + "resolved": "https://registry.npmjs.org/call-bound/-/call-bound-1.0.4.tgz", + "integrity": "sha512-+ys997U96po4Kx/ABpBCqhA9EuxJaQWDQg7295H4hBphv3IZg0boBKuwYpt4YXp6MZ5AmZQnU/tyMTlRpaSejg==", + "license": "MIT", + "dependencies": { + "call-bind-apply-helpers": "^1.0.2", + "get-intrinsic": "^1.3.0" + }, + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/chownr": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/chownr/-/chownr-3.0.0.tgz", + "integrity": "sha512-+IxzY9BZOQd/XuYPRmrvEVjF/nqj5kgT4kEq7VofrDoM1MxoRjEWkrCC3EtLi59TVawxTAn+orJwFQcrqEN1+g==", + "license": "BlueOak-1.0.0", + "engines": { + "node": ">=18" + } + }, + "node_modules/content-disposition": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/content-disposition/-/content-disposition-1.0.0.tgz", + "integrity": "sha512-Au9nRL8VNUut/XSzbQA38+M78dzP4D+eqg3gfJHMIHHYa3bg067xj1KxMUWj+VULbiZMowKngFFbKczUrNJ1mg==", + "license": "MIT", + "dependencies": { + "safe-buffer": "5.2.1" + }, + "engines": { + "node": ">= 0.6" + } + }, + "node_modules/content-type": { + "version": "1.0.5", + "resolved": "https://registry.npmjs.org/content-type/-/content-type-1.0.5.tgz", + "integrity": "sha512-nTjqfcBFEipKdXCv4YDQWCfmcLZKm81ldF0pAopTvyrFGVbcR6P/VAAd5G7N+0tTr8QqiU0tFadD6FK4NtJwOA==", + "license": "MIT", + "engines": { + "node": ">= 0.6" + } + }, + "node_modules/cookie": { + "version": "0.7.2", + "resolved": "https://registry.npmjs.org/cookie/-/cookie-0.7.2.tgz", + "integrity": "sha512-yki5XnKuf750l50uGTllt6kKILY4nQ1eNIQatoXEByZ5dWgnKqbnqmTrBE5B4N7lrMJKQ2ytWMiTO2o0v6Ew/w==", + "license": "MIT", + "engines": { + "node": ">= 0.6" + } + }, + "node_modules/cookie-signature": { + "version": "1.2.2", + "resolved": "https://registry.npmjs.org/cookie-signature/-/cookie-signature-1.2.2.tgz", + "integrity": "sha512-D76uU73ulSXrD1UXF4KE2TMxVVwhsnCgfAyTg9k8P6KGZjlXKrOLe4dJQKI3Bxi5wjesZoFXJWElNWBjPZMbhg==", + "license": "MIT", + "engines": { + "node": ">=6.6.0" + } + }, + "node_modules/cors": { + "version": "2.8.5", + "resolved": "https://registry.npmjs.org/cors/-/cors-2.8.5.tgz", + "integrity": "sha512-KIHbLJqu73RGr/hnbrO9uBeixNGuvSQjul/jdFvS/KFSIH1hWVd1ng7zOHx+YrEfInLG7q4n6GHQ9cDtxv/P6g==", + "license": "MIT", + "dependencies": { + "object-assign": "^4", + "vary": "^1" + }, + "engines": { + "node": ">= 0.10" + } + }, + "node_modules/debug": { + "version": "4.4.3", + "resolved": "https://registry.npmjs.org/debug/-/debug-4.4.3.tgz", + "integrity": "sha512-RGwwWnwQvkVfavKVt22FGLw+xYSdzARwm0ru6DhTVA3umU5hZc28V3kO4stgYryrTlLpuvgI9GiijltAjNbcqA==", + "license": "MIT", + "dependencies": { + "ms": "^2.1.3" + }, + "engines": { + "node": ">=6.0" + }, + "peerDependenciesMeta": { + "supports-color": { + "optional": true + } + } + }, + "node_modules/depd": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/depd/-/depd-2.0.0.tgz", + "integrity": "sha512-g7nH6P6dyDioJogAAGprGpCtVImJhpPk/roCzdb3fIh61/s/nPsfR6onyMwkCAR/OlC3yBC0lESvUoQEAssIrw==", + "license": "MIT", + "engines": { + "node": ">= 0.8" + } + }, + "node_modules/detect-libc": { + "version": "1.0.3", + "resolved": "https://registry.npmjs.org/detect-libc/-/detect-libc-1.0.3.tgz", + "integrity": "sha512-pGjwhsmsp4kL2RTz08wcOlGN83otlqHeD/Z5T8GXZB+/YcpQ/dgo+lbU8ZsGxV0HIvqqxo9l7mqYwyYMD9bKDg==", + "license": "Apache-2.0", + "bin": { + "detect-libc": "bin/detect-libc.js" + }, + "engines": { + "node": ">=0.10" + } + }, + "node_modules/dunder-proto": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/dunder-proto/-/dunder-proto-1.0.1.tgz", + "integrity": "sha512-KIN/nDJBQRcXw0MLVhZE9iQHmG68qAVIBg9CqmUYjmQIhgij9U5MFvrqkUL5FbtyyzZuOeOt0zdeRe4UY7ct+A==", + "license": "MIT", + "dependencies": { + "call-bind-apply-helpers": "^1.0.1", + "es-errors": "^1.3.0", + "gopd": "^1.2.0" + }, + "engines": { + "node": ">= 0.4" + } + }, + "node_modules/ee-first": { + "version": "1.1.1", + "resolved": "https://registry.npmjs.org/ee-first/-/ee-first-1.1.1.tgz", + "integrity": "sha512-WMwm9LhRUo+WUaRN+vRuETqG89IgZphVSNkdFgeb6sS/E4OrDIN7t48CAewSHXc6C8lefD8KKfr5vY61brQlow==", + "license": "MIT" + }, + "node_modules/encodeurl": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/encodeurl/-/encodeurl-2.0.0.tgz", + "integrity": "sha512-Q0n9HRi4m6JuGIV1eFlmvJB7ZEVxu93IrMyiMsGC0lrMJMWzRgx6WGquyfQgZVb31vhGgXnfmPNNXmxnOkRBrg==", + "license": "MIT", + "engines": { + "node": ">= 0.8" + } + }, + "node_modules/enhanced-resolve": { + "version": "5.18.3", + "resolved": "https://registry.npmjs.org/enhanced-resolve/-/enhanced-resolve-5.18.3.tgz", + "integrity": "sha512-d4lC8xfavMeBjzGr2vECC3fsGXziXZQyJxD868h2M/mBI3PwAuODxAkLkq5HYuvrPYcUtiLzsTo8U3PgX3Ocww==", + "license": "MIT", + "dependencies": { + "graceful-fs": "^4.2.4", + "tapable": "^2.2.0" + }, + "engines": { + "node": ">=10.13.0" + } + }, + "node_modules/es-define-property": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/es-define-property/-/es-define-property-1.0.1.tgz", + "integrity": "sha512-e3nRfgfUZ4rNGL232gUgX06QNyyez04KdjFrF+LTRoOXmrOgFKDg4BCdsjW8EnT69eqdYGmRpJwiPVYNrCaW3g==", + "license": "MIT", + "engines": { + "node": ">= 0.4" + } + }, + "node_modules/es-errors": { + "version": "1.3.0", + "resolved": "https://registry.npmjs.org/es-errors/-/es-errors-1.3.0.tgz", + "integrity": "sha512-Zf5H2Kxt2xjTvbJvP2ZWLEICxA6j+hAmMzIlypy4xcBg1vKVnx89Wy0GbS+kf5cwCVFFzdCFh2XSCFNULS6csw==", + "license": "MIT", + "engines": { + "node": ">= 0.4" + } + }, + "node_modules/es-object-atoms": { + "version": "1.1.1", + "resolved": "https://registry.npmjs.org/es-object-atoms/-/es-object-atoms-1.1.1.tgz", + "integrity": "sha512-FGgH2h8zKNim9ljj7dankFPcICIK9Cp5bm+c2gQSYePhpaG5+esrLODihIorn+Pe6FGJzWhXQotPv73jTaldXA==", + "license": "MIT", + "dependencies": { + "es-errors": "^1.3.0" + }, + "engines": { + "node": ">= 0.4" + } + }, + "node_modules/escape-html": { + "version": "1.0.3", + "resolved": "https://registry.npmjs.org/escape-html/-/escape-html-1.0.3.tgz", + "integrity": "sha512-NiSupZ4OeuGwr68lGIeym/ksIZMJodUGOSCZ/FSnTxcrekbvqrgdUxlJOMpijaKZVjAJrWrGs/6Jy8OMuyj9ow==", + "license": "MIT" + }, + "node_modules/etag": { + "version": "1.8.1", + "resolved": "https://registry.npmjs.org/etag/-/etag-1.8.1.tgz", + "integrity": "sha512-aIL5Fx7mawVa300al2BnEE4iNvo1qETxLrPI/o05L7z6go7fCw1J6EQmbK4FmJ2AS7kgVF/KEZWufBfdClMcPg==", + "license": "MIT", + "engines": { + "node": ">= 0.6" + } + }, + "node_modules/express": { + "version": "5.1.0", + "resolved": "https://registry.npmjs.org/express/-/express-5.1.0.tgz", + "integrity": "sha512-DT9ck5YIRU+8GYzzU5kT3eHGA5iL+1Zd0EutOmTE9Dtk+Tvuzd23VBU+ec7HPNSTxXYO55gPV/hq4pSBJDjFpA==", + "license": "MIT", + "dependencies": { + "accepts": "^2.0.0", + "body-parser": "^2.2.0", + "content-disposition": "^1.0.0", + "content-type": "^1.0.5", + "cookie": "^0.7.1", + "cookie-signature": "^1.2.1", + "debug": "^4.4.0", + "encodeurl": "^2.0.0", + "escape-html": "^1.0.3", + "etag": "^1.8.1", + "finalhandler": "^2.1.0", + "fresh": "^2.0.0", + "http-errors": "^2.0.0", + "merge-descriptors": "^2.0.0", + "mime-types": "^3.0.0", + "on-finished": "^2.4.1", + "once": "^1.4.0", + "parseurl": "^1.3.3", + "proxy-addr": "^2.0.7", + "qs": "^6.14.0", + "range-parser": "^1.2.1", + "router": "^2.2.0", + "send": "^1.1.0", + "serve-static": "^2.2.0", + "statuses": "^2.0.1", + "type-is": "^2.0.1", + "vary": "^1.1.2" + }, + "engines": { + "node": ">= 18" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/express" + } + }, + "node_modules/fill-range": { + "version": "7.1.1", + "resolved": "https://registry.npmjs.org/fill-range/-/fill-range-7.1.1.tgz", + "integrity": "sha512-YsGpe3WHLK8ZYi4tWDg2Jy3ebRz2rXowDxnld4bkQB00cc/1Zw9AWnC0i9ztDJitivtQvaI9KaLyKrc+hBW0yg==", + "license": "MIT", + "dependencies": { + "to-regex-range": "^5.0.1" + }, + "engines": { + "node": ">=8" + } + }, + "node_modules/finalhandler": { + "version": "2.1.0", + "resolved": "https://registry.npmjs.org/finalhandler/-/finalhandler-2.1.0.tgz", + "integrity": "sha512-/t88Ty3d5JWQbWYgaOGCCYfXRwV1+be02WqYYlL6h0lEiUAMPM8o8qKGO01YIkOHzka2up08wvgYD0mDiI+q3Q==", + "license": "MIT", + "dependencies": { + "debug": "^4.4.0", + "encodeurl": "^2.0.0", + "escape-html": "^1.0.3", + "on-finished": "^2.4.1", + "parseurl": "^1.3.3", + "statuses": "^2.0.1" + }, + "engines": { + "node": ">= 0.8" + } + }, + "node_modules/forwarded": { + "version": "0.2.0", + "resolved": "https://registry.npmjs.org/forwarded/-/forwarded-0.2.0.tgz", + "integrity": "sha512-buRG0fpBtRHSTCOASe6hD258tEubFoRLb4ZNA6NxMVHNw2gOcwHo9wyablzMzOA5z9xA9L1KNjk/Nt6MT9aYow==", + "license": "MIT", + "engines": { + "node": ">= 0.6" + } + }, + "node_modules/fresh": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/fresh/-/fresh-2.0.0.tgz", + "integrity": "sha512-Rx/WycZ60HOaqLKAi6cHRKKI7zxWbJ31MhntmtwMoaTeF7XFH9hhBp8vITaMidfljRQ6eYWCKkaTK+ykVJHP2A==", + "license": "MIT", + "engines": { + "node": ">= 0.8" + } + }, + "node_modules/function-bind": { + "version": "1.1.2", + "resolved": "https://registry.npmjs.org/function-bind/-/function-bind-1.1.2.tgz", + "integrity": "sha512-7XHNxH7qX9xG5mIwxkhumTox/MIRNcOgDrxWsMt2pAr23WHp6MrRlN7FBSFpCpr+oVO0F744iUgR82nJMfG2SA==", + "license": "MIT", + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/get-intrinsic": { + "version": "1.3.0", + "resolved": "https://registry.npmjs.org/get-intrinsic/-/get-intrinsic-1.3.0.tgz", + "integrity": "sha512-9fSjSaos/fRIVIp+xSJlE6lfwhES7LNtKaCBIamHsjr2na1BiABJPo0mOjjz8GJDURarmCPGqaiVg5mfjb98CQ==", + "license": "MIT", + "dependencies": { + "call-bind-apply-helpers": "^1.0.2", + "es-define-property": "^1.0.1", + "es-errors": "^1.3.0", + "es-object-atoms": "^1.1.1", + "function-bind": "^1.1.2", + "get-proto": "^1.0.1", + "gopd": "^1.2.0", + "has-symbols": "^1.1.0", + "hasown": "^2.0.2", + "math-intrinsics": "^1.1.0" + }, + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/get-proto": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/get-proto/-/get-proto-1.0.1.tgz", + "integrity": "sha512-sTSfBjoXBp89JvIKIefqw7U2CCebsc74kiY6awiGogKtoSGbgjYE/G/+l9sF3MWFPNc9IcoOC4ODfKHfxFmp0g==", + "license": "MIT", + "dependencies": { + "dunder-proto": "^1.0.1", + "es-object-atoms": "^1.0.0" + }, + "engines": { + "node": ">= 0.4" + } + }, + "node_modules/gopd": { + "version": "1.2.0", + "resolved": "https://registry.npmjs.org/gopd/-/gopd-1.2.0.tgz", + "integrity": "sha512-ZUKRh6/kUFoAiTAtTYPZJ3hw9wNxx+BIBOijnlG9PnrJsCcSjs1wyyD6vJpaYtgnzDrKYRSqf3OO6Rfa93xsRg==", + "license": "MIT", + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/graceful-fs": { + "version": "4.2.11", + "resolved": "https://registry.npmjs.org/graceful-fs/-/graceful-fs-4.2.11.tgz", + "integrity": "sha512-RbJ5/jmFcNNCcDV5o9eTnBLJ/HszWV0P73bc+Ff4nS/rJj+YaS6IGyiOL0VoBYX+l1Wrl3k63h/KrH+nhJ0XvQ==", + "license": "ISC" + }, + "node_modules/has-symbols": { + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/has-symbols/-/has-symbols-1.1.0.tgz", + "integrity": "sha512-1cDNdwJ2Jaohmb3sg4OmKaMBwuC48sYni5HUw2DvsC8LjGTLK9h+eb1X6RyuOHe4hT0ULCW68iomhjUoKUqlPQ==", + "license": "MIT", + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/hasown": { + "version": "2.0.2", + "resolved": "https://registry.npmjs.org/hasown/-/hasown-2.0.2.tgz", + "integrity": "sha512-0hJU9SCPvmMzIBdZFqNPXWa6dqh7WdH0cII9y+CyS8rG3nL48Bclra9HmKhVVUHyPWNH5Y7xDwAB7bfgSjkUMQ==", + "license": "MIT", + "dependencies": { + "function-bind": "^1.1.2" + }, + "engines": { + "node": ">= 0.4" + } + }, + "node_modules/http-errors": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/http-errors/-/http-errors-2.0.0.tgz", + "integrity": "sha512-FtwrG/euBzaEjYeRqOgly7G0qviiXoJWnvEH2Z1plBdXgbyjv34pHTSb9zoeHMyDy33+DWy5Wt9Wo+TURtOYSQ==", + "license": "MIT", + "dependencies": { + "depd": "2.0.0", + "inherits": "2.0.4", + "setprototypeof": "1.2.0", + "statuses": "2.0.1", + "toidentifier": "1.0.1" + }, + "engines": { + "node": ">= 0.8" + } + }, + "node_modules/http-errors/node_modules/statuses": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/statuses/-/statuses-2.0.1.tgz", + "integrity": "sha512-RwNA9Z/7PrK06rYLIzFMlaF+l73iwpzsqRIFgbMLbTcLD6cOao82TaWefPXQvB2fOC4AjuYSEndS7N/mTCbkdQ==", + "license": "MIT", + "engines": { + "node": ">= 0.8" + } + }, + "node_modules/iconv-lite": { + "version": "0.6.3", + "resolved": "https://registry.npmjs.org/iconv-lite/-/iconv-lite-0.6.3.tgz", + "integrity": "sha512-4fCk79wshMdzMp2rH06qWrJE4iolqLhCUH+OiuIgU++RB0+94NlDL81atO7GX55uUKueo0txHNtvEyI6D7WdMw==", + "license": "MIT", + "dependencies": { + "safer-buffer": ">= 2.1.2 < 3.0.0" + }, + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/inherits": { + "version": "2.0.4", + "resolved": "https://registry.npmjs.org/inherits/-/inherits-2.0.4.tgz", + "integrity": "sha512-k/vGaX4/Yla3WzyMCvTQOXYeIHvqOKtnqBduzTHpzpQZzAskKMhZ2K+EnBiSM9zGSoIFeMpXKxa4dYeZIQqewQ==", + "license": "ISC" + }, + "node_modules/ipaddr.js": { + "version": "1.9.1", + "resolved": "https://registry.npmjs.org/ipaddr.js/-/ipaddr.js-1.9.1.tgz", + "integrity": "sha512-0KI/607xoxSToH7GjN1FfSbLoU0+btTicjsQSWQlh/hZykN8KpmMf7uYwPW3R+akZ6R/w18ZlXSHBYXiYUPO3g==", + "license": "MIT", + "engines": { + "node": ">= 0.10" + } + }, + "node_modules/is-extglob": { + "version": "2.1.1", + "resolved": "https://registry.npmjs.org/is-extglob/-/is-extglob-2.1.1.tgz", + "integrity": "sha512-SbKbANkN603Vi4jEZv49LeVJMn4yGwsbzZworEoyEiutsN3nJYdbO36zfhGJ6QEDpOZIFkDtnq5JRxmvl3jsoQ==", + "license": "MIT", + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/is-glob": { + "version": "4.0.3", + "resolved": "https://registry.npmjs.org/is-glob/-/is-glob-4.0.3.tgz", + "integrity": "sha512-xelSayHH36ZgE7ZWhli7pW34hNbNl8Ojv5KVmkJD4hBdD3th8Tfk9vYasLM+mXWOZhFkgZfxhLSnrwRr4elSSg==", + "license": "MIT", + "dependencies": { + "is-extglob": "^2.1.1" + }, + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/is-number": { + "version": "7.0.0", + "resolved": "https://registry.npmjs.org/is-number/-/is-number-7.0.0.tgz", + "integrity": "sha512-41Cifkg6e8TylSpdtTpeLVMqvSBEVzTttHvERD741+pnZ8ANv0004MRL43QKPDlK9cGvNp6NZWZUBlbGXYxxng==", + "license": "MIT", + "engines": { + "node": ">=0.12.0" + } + }, + "node_modules/is-promise": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/is-promise/-/is-promise-4.0.0.tgz", + "integrity": "sha512-hvpoI6korhJMnej285dSg6nu1+e6uxs7zG3BYAm5byqDsgJNWwxzM6z6iZiAgQR4TJ30JmBTOwqZUw3WlyH3AQ==", + "license": "MIT" + }, + "node_modules/jiti": { + "version": "2.6.1", + "resolved": "https://registry.npmjs.org/jiti/-/jiti-2.6.1.tgz", + "integrity": "sha512-ekilCSN1jwRvIbgeg/57YFh8qQDNbwDb9xT/qu2DAHbFFZUicIl4ygVaAvzveMhMVr3LnpSKTNnwt8PoOfmKhQ==", + "license": "MIT", + "bin": { + "jiti": "lib/jiti-cli.mjs" + } + }, + "node_modules/lightningcss": { + "version": "1.30.1", + "resolved": "https://registry.npmjs.org/lightningcss/-/lightningcss-1.30.1.tgz", + "integrity": "sha512-xi6IyHML+c9+Q3W0S4fCQJOym42pyurFiJUHEcEyHS0CeKzia4yZDEsLlqOFykxOdHpNy0NmvVO31vcSqAxJCg==", + "license": "MPL-2.0", + "dependencies": { + "detect-libc": "^2.0.3" + }, + "engines": { + "node": ">= 12.0.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/parcel" + }, + "optionalDependencies": { + "lightningcss-darwin-arm64": "1.30.1", + "lightningcss-darwin-x64": "1.30.1", + "lightningcss-freebsd-x64": "1.30.1", + "lightningcss-linux-arm-gnueabihf": "1.30.1", + "lightningcss-linux-arm64-gnu": "1.30.1", + "lightningcss-linux-arm64-musl": "1.30.1", + "lightningcss-linux-x64-gnu": "1.30.1", + "lightningcss-linux-x64-musl": "1.30.1", + "lightningcss-win32-arm64-msvc": "1.30.1", + "lightningcss-win32-x64-msvc": "1.30.1" + } + }, + "node_modules/lightningcss-darwin-arm64": { + "version": "1.30.1", + "resolved": "https://registry.npmjs.org/lightningcss-darwin-arm64/-/lightningcss-darwin-arm64-1.30.1.tgz", + "integrity": "sha512-c8JK7hyE65X1MHMN+Viq9n11RRC7hgin3HhYKhrMyaXflk5GVplZ60IxyoVtzILeKr+xAJwg6zK6sjTBJ0FKYQ==", + "cpu": [ + "arm64" + ], + "license": "MPL-2.0", + "optional": true, + "os": [ + "darwin" + ], + "engines": { + "node": ">= 12.0.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/parcel" + } + }, + "node_modules/lightningcss-darwin-x64": { + "version": "1.30.1", + "resolved": "https://registry.npmjs.org/lightningcss-darwin-x64/-/lightningcss-darwin-x64-1.30.1.tgz", + "integrity": "sha512-k1EvjakfumAQoTfcXUcHQZhSpLlkAuEkdMBsI/ivWw9hL+7FtilQc0Cy3hrx0AAQrVtQAbMI7YjCgYgvn37PzA==", + "cpu": [ + "x64" + ], + "license": "MPL-2.0", + "optional": true, + "os": [ + "darwin" + ], + "engines": { + "node": ">= 12.0.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/parcel" + } + }, + "node_modules/lightningcss-freebsd-x64": { + "version": "1.30.1", + "resolved": "https://registry.npmjs.org/lightningcss-freebsd-x64/-/lightningcss-freebsd-x64-1.30.1.tgz", + "integrity": "sha512-kmW6UGCGg2PcyUE59K5r0kWfKPAVy4SltVeut+umLCFoJ53RdCUWxcRDzO1eTaxf/7Q2H7LTquFHPL5R+Gjyig==", + "cpu": [ + "x64" + ], + "license": "MPL-2.0", + "optional": true, + "os": [ + "freebsd" + ], + "engines": { + "node": ">= 12.0.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/parcel" + } + }, + "node_modules/lightningcss-linux-arm-gnueabihf": { + "version": "1.30.1", + "resolved": "https://registry.npmjs.org/lightningcss-linux-arm-gnueabihf/-/lightningcss-linux-arm-gnueabihf-1.30.1.tgz", + "integrity": "sha512-MjxUShl1v8pit+6D/zSPq9S9dQ2NPFSQwGvxBCYaBYLPlCWuPh9/t1MRS8iUaR8i+a6w7aps+B4N0S1TYP/R+Q==", + "cpu": [ + "arm" + ], + "license": "MPL-2.0", + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": ">= 12.0.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/parcel" + } + }, + "node_modules/lightningcss-linux-arm64-gnu": { + "version": "1.30.1", + "resolved": "https://registry.npmjs.org/lightningcss-linux-arm64-gnu/-/lightningcss-linux-arm64-gnu-1.30.1.tgz", + "integrity": "sha512-gB72maP8rmrKsnKYy8XUuXi/4OctJiuQjcuqWNlJQ6jZiWqtPvqFziskH3hnajfvKB27ynbVCucKSm2rkQp4Bw==", + "cpu": [ + "arm64" + ], + "license": "MPL-2.0", + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": ">= 12.0.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/parcel" + } + }, + "node_modules/lightningcss-linux-arm64-musl": { + "version": "1.30.1", + "resolved": "https://registry.npmjs.org/lightningcss-linux-arm64-musl/-/lightningcss-linux-arm64-musl-1.30.1.tgz", + "integrity": "sha512-jmUQVx4331m6LIX+0wUhBbmMX7TCfjF5FoOH6SD1CttzuYlGNVpA7QnrmLxrsub43ClTINfGSYyHe2HWeLl5CQ==", + "cpu": [ + "arm64" + ], + "license": "MPL-2.0", + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": ">= 12.0.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/parcel" + } + }, + "node_modules/lightningcss-linux-x64-gnu": { + "version": "1.30.1", + "resolved": "https://registry.npmjs.org/lightningcss-linux-x64-gnu/-/lightningcss-linux-x64-gnu-1.30.1.tgz", + "integrity": "sha512-piWx3z4wN8J8z3+O5kO74+yr6ze/dKmPnI7vLqfSqI8bccaTGY5xiSGVIJBDd5K5BHlvVLpUB3S2YCfelyJ1bw==", + "cpu": [ + "x64" + ], + "license": "MPL-2.0", + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": ">= 12.0.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/parcel" + } + }, + "node_modules/lightningcss-linux-x64-musl": { + "version": "1.30.1", + "resolved": "https://registry.npmjs.org/lightningcss-linux-x64-musl/-/lightningcss-linux-x64-musl-1.30.1.tgz", + "integrity": "sha512-rRomAK7eIkL+tHY0YPxbc5Dra2gXlI63HL+v1Pdi1a3sC+tJTcFrHX+E86sulgAXeI7rSzDYhPSeHHjqFhqfeQ==", + "cpu": [ + "x64" + ], + "license": "MPL-2.0", + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": ">= 12.0.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/parcel" + } + }, + "node_modules/lightningcss-win32-arm64-msvc": { + "version": "1.30.1", + "resolved": "https://registry.npmjs.org/lightningcss-win32-arm64-msvc/-/lightningcss-win32-arm64-msvc-1.30.1.tgz", + "integrity": "sha512-mSL4rqPi4iXq5YVqzSsJgMVFENoa4nGTT/GjO2c0Yl9OuQfPsIfncvLrEW6RbbB24WtZ3xP/2CCmI3tNkNV4oA==", + "cpu": [ + "arm64" + ], + "license": "MPL-2.0", + "optional": true, + "os": [ + "win32" + ], + "engines": { + "node": ">= 12.0.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/parcel" + } + }, + "node_modules/lightningcss-win32-x64-msvc": { + "version": "1.30.1", + "resolved": "https://registry.npmjs.org/lightningcss-win32-x64-msvc/-/lightningcss-win32-x64-msvc-1.30.1.tgz", + "integrity": "sha512-PVqXh48wh4T53F/1CCu8PIPCxLzWyCnn/9T5W1Jpmdy5h9Cwd+0YQS6/LwhHXSafuc61/xg9Lv5OrCby6a++jg==", + "cpu": [ + "x64" + ], + "license": "MPL-2.0", + "optional": true, + "os": [ + "win32" + ], + "engines": { + "node": ">= 12.0.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/parcel" + } + }, + "node_modules/lightningcss/node_modules/detect-libc": { + "version": "2.1.2", + "resolved": "https://registry.npmjs.org/detect-libc/-/detect-libc-2.1.2.tgz", + "integrity": "sha512-Btj2BOOO83o3WyH59e8MgXsxEQVcarkUOpEYrubB0urwnN10yQ364rsiByU11nZlqWYZm05i/of7io4mzihBtQ==", + "license": "Apache-2.0", + "engines": { + "node": ">=8" + } + }, + "node_modules/magic-string": { + "version": "0.30.19", + "resolved": "https://registry.npmjs.org/magic-string/-/magic-string-0.30.19.tgz", + "integrity": "sha512-2N21sPY9Ws53PZvsEpVtNuSW+ScYbQdp4b9qUaL+9QkHUrGFKo56Lg9Emg5s9V/qrtNBmiR01sYhUOwu3H+VOw==", + "license": "MIT", + "dependencies": { + "@jridgewell/sourcemap-codec": "^1.5.5" + } + }, + "node_modules/math-intrinsics": { + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/math-intrinsics/-/math-intrinsics-1.1.0.tgz", + "integrity": "sha512-/IXtbwEk5HTPyEwyKX6hGkYXxM9nbj64B+ilVJnC/R6B0pH5G4V3b0pVbL7DBj4tkhBAppbQUlf6F6Xl9LHu1g==", + "license": "MIT", + "engines": { + "node": ">= 0.4" + } + }, + "node_modules/media-typer": { + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/media-typer/-/media-typer-1.1.0.tgz", + "integrity": "sha512-aisnrDP4GNe06UcKFnV5bfMNPBUw4jsLGaWwWfnH3v02GnBuXX2MCVn5RbrWo0j3pczUilYblq7fQ7Nw2t5XKw==", + "license": "MIT", + "engines": { + "node": ">= 0.8" + } + }, + "node_modules/merge-descriptors": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/merge-descriptors/-/merge-descriptors-2.0.0.tgz", + "integrity": "sha512-Snk314V5ayFLhp3fkUREub6WtjBfPdCPY1Ln8/8munuLuiYhsABgBVWsozAG+MWMbVEvcdcpbi9R7ww22l9Q3g==", + "license": "MIT", + "engines": { + "node": ">=18" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/micromatch": { + "version": "4.0.8", + "resolved": "https://registry.npmjs.org/micromatch/-/micromatch-4.0.8.tgz", + "integrity": "sha512-PXwfBhYu0hBCPw8Dn0E+WDYb7af3dSLVWKi3HGv84IdF4TyFoC0ysxFd0Goxw7nSv4T/PzEJQxsYsEiFCKo2BA==", + "license": "MIT", + "dependencies": { + "braces": "^3.0.3", + "picomatch": "^2.3.1" + }, + "engines": { + "node": ">=8.6" + } + }, + "node_modules/mime-db": { + "version": "1.54.0", + "resolved": "https://registry.npmjs.org/mime-db/-/mime-db-1.54.0.tgz", + "integrity": "sha512-aU5EJuIN2WDemCcAp2vFBfp/m4EAhWJnUNSSw0ixs7/kXbd6Pg64EmwJkNdFhB8aWt1sH2CTXrLxo/iAGV3oPQ==", + "license": "MIT", + "engines": { + "node": ">= 0.6" + } + }, + "node_modules/mime-types": { + "version": "3.0.1", + "resolved": "https://registry.npmjs.org/mime-types/-/mime-types-3.0.1.tgz", + "integrity": "sha512-xRc4oEhT6eaBpU1XF7AjpOFD+xQmXNB5OVKwp4tqCuBpHLS/ZbBDrc07mYTDqVMg6PfxUjjNp85O6Cd2Z/5HWA==", + "license": "MIT", + "dependencies": { + "mime-db": "^1.54.0" + }, + "engines": { + "node": ">= 0.6" + } + }, + "node_modules/minipass": { + "version": "7.1.2", + "resolved": "https://registry.npmjs.org/minipass/-/minipass-7.1.2.tgz", + "integrity": "sha512-qOOzS1cBTWYF4BH8fVePDBOO9iptMnGUEZwNc/cMWnTV2nVLZ7VoNWEPHkYczZA0pdoA7dl6e7FL659nX9S2aw==", + "license": "ISC", + "engines": { + "node": ">=16 || 14 >=14.17" + } + }, + "node_modules/minizlib": { + "version": "3.1.0", + "resolved": "https://registry.npmjs.org/minizlib/-/minizlib-3.1.0.tgz", + "integrity": "sha512-KZxYo1BUkWD2TVFLr0MQoM8vUUigWD3LlD83a/75BqC+4qE0Hb1Vo5v1FgcfaNXvfXzr+5EhQ6ing/CaBijTlw==", + "license": "MIT", + "dependencies": { + "minipass": "^7.1.2" + }, + "engines": { + "node": ">= 18" + } + }, + "node_modules/mri": { + "version": "1.2.0", + "resolved": "https://registry.npmjs.org/mri/-/mri-1.2.0.tgz", + "integrity": "sha512-tzzskb3bG8LvYGFF/mDTpq3jpI6Q9wc3LEmBaghu+DdCssd1FakN7Bc0hVNmEyGq1bq3RgfkCb3cmQLpNPOroA==", + "license": "MIT", + "engines": { + "node": ">=4" + } + }, + "node_modules/ms": { + "version": "2.1.3", + "resolved": "https://registry.npmjs.org/ms/-/ms-2.1.3.tgz", + "integrity": "sha512-6FlzubTLZG3J2a/NVCAleEhjzq5oxgHyaCU9yYXvcLsvoVaHJq/s5xXI6/XXP6tz7R9xAOtHnSO/tXtF3WRTlA==", + "license": "MIT" + }, + "node_modules/negotiator": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/negotiator/-/negotiator-1.0.0.tgz", + "integrity": "sha512-8Ofs/AUQh8MaEcrlq5xOX0CQ9ypTF5dl78mjlMNfOK08fzpgTHQRQPBxcPlEtIw0yRpws+Zo/3r+5WRby7u3Gg==", + "license": "MIT", + "engines": { + "node": ">= 0.6" + } + }, + "node_modules/node-addon-api": { + "version": "7.1.1", + "resolved": "https://registry.npmjs.org/node-addon-api/-/node-addon-api-7.1.1.tgz", + "integrity": "sha512-5m3bsyrjFWE1xf7nz7YXdN4udnVtXK6/Yfgn5qnahL6bCkf2yKt4k3nuTKAtT4r3IG8JNR2ncsIMdZuAzJjHQQ==", + "license": "MIT" + }, + "node_modules/object-assign": { + "version": "4.1.1", + "resolved": "https://registry.npmjs.org/object-assign/-/object-assign-4.1.1.tgz", + "integrity": "sha512-rJgTQnkUnH1sFw8yT6VSU3zD3sWmu6sZhIseY8VX+GRu3P6F7Fu+JNDoXfklElbLJSnc3FUQHVe4cU5hj+BcUg==", + "license": "MIT", + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/object-inspect": { + "version": "1.13.4", + "resolved": "https://registry.npmjs.org/object-inspect/-/object-inspect-1.13.4.tgz", + "integrity": "sha512-W67iLl4J2EXEGTbfeHCffrjDfitvLANg0UlX3wFUUSTx92KXRFegMHUVgSqE+wvhAbi4WqjGg9czysTV2Epbew==", + "license": "MIT", + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/on-finished": { + "version": "2.4.1", + "resolved": "https://registry.npmjs.org/on-finished/-/on-finished-2.4.1.tgz", + "integrity": "sha512-oVlzkg3ENAhCk2zdv7IJwd/QUD4z2RxRwpkcGY8psCVcCYZNq4wYnVWALHM+brtuJjePWiYF/ClmuDr8Ch5+kg==", + "license": "MIT", + "dependencies": { + "ee-first": "1.1.1" + }, + "engines": { + "node": ">= 0.8" + } + }, + "node_modules/once": { + "version": "1.4.0", + "resolved": "https://registry.npmjs.org/once/-/once-1.4.0.tgz", + "integrity": "sha512-lNaJgI+2Q5URQBkccEKHTQOPaXdUxnZZElQTZY0MFUAuaEqe1E+Nyvgdz/aIyNi6Z9MzO5dv1H8n58/GELp3+w==", + "license": "ISC", + "dependencies": { + "wrappy": "1" + } + }, + "node_modules/parseurl": { + "version": "1.3.3", + "resolved": "https://registry.npmjs.org/parseurl/-/parseurl-1.3.3.tgz", + "integrity": "sha512-CiyeOxFT/JZyN5m0z9PfXw4SCBJ6Sygz1Dpl0wqjlhDEGGBP1GnsUVEL0p63hoG1fcj3fHynXi9NYO4nWOL+qQ==", + "license": "MIT", + "engines": { + "node": ">= 0.8" + } + }, + "node_modules/path-to-regexp": { + "version": "8.3.0", + "resolved": "https://registry.npmjs.org/path-to-regexp/-/path-to-regexp-8.3.0.tgz", + "integrity": "sha512-7jdwVIRtsP8MYpdXSwOS0YdD0Du+qOoF/AEPIt88PcCFrZCzx41oxku1jD88hZBwbNUIEfpqvuhjFaMAqMTWnA==", + "license": "MIT", + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/express" + } + }, + "node_modules/picocolors": { + "version": "1.1.1", + "resolved": "https://registry.npmjs.org/picocolors/-/picocolors-1.1.1.tgz", + "integrity": "sha512-xceH2snhtb5M9liqDsmEw56le376mTZkEX/jEb/RxNFyegNul7eNslCXP9FDj/Lcu0X8KEyMceP2ntpaHrDEVA==", + "license": "ISC" + }, + "node_modules/picomatch": { + "version": "2.3.1", + "resolved": "https://registry.npmjs.org/picomatch/-/picomatch-2.3.1.tgz", + "integrity": "sha512-JU3teHTNjmE2VCGFzuY8EXzCDVwEqB2a8fsIvwaStHhAWJEeVd1o1QD80CU6+ZdEXXSLbSsuLwJjkCBWqRQUVA==", + "license": "MIT", + "engines": { + "node": ">=8.6" + }, + "funding": { + "url": "https://github.com/sponsors/jonschlinkert" + } + }, + "node_modules/proxy-addr": { + "version": "2.0.7", + "resolved": "https://registry.npmjs.org/proxy-addr/-/proxy-addr-2.0.7.tgz", + "integrity": "sha512-llQsMLSUDUPT44jdrU/O37qlnifitDP+ZwrmmZcoSKyLKvtZxpyV0n2/bD/N4tBAAZ/gJEdZU7KMraoK1+XYAg==", + "license": "MIT", + "dependencies": { + "forwarded": "0.2.0", + "ipaddr.js": "1.9.1" + }, + "engines": { + "node": ">= 0.10" + } + }, + "node_modules/qs": { + "version": "6.14.0", + "resolved": "https://registry.npmjs.org/qs/-/qs-6.14.0.tgz", + "integrity": "sha512-YWWTjgABSKcvs/nWBi9PycY/JiPJqOD4JA6o9Sej2AtvSGarXxKC3OQSk4pAarbdQlKAh5D4FCQkJNkW+GAn3w==", + "license": "BSD-3-Clause", + "dependencies": { + "side-channel": "^1.1.0" + }, + "engines": { + "node": ">=0.6" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/range-parser": { + "version": "1.2.1", + "resolved": "https://registry.npmjs.org/range-parser/-/range-parser-1.2.1.tgz", + "integrity": "sha512-Hrgsx+orqoygnmhFbKaHE6c296J+HTAQXoxEF6gNupROmmGJRoyzfG3ccAveqCBrwr/2yxQ5BVd/GTl5agOwSg==", + "license": "MIT", + "engines": { + "node": ">= 0.6" + } + }, + "node_modules/raw-body": { + "version": "3.0.1", + "resolved": "https://registry.npmjs.org/raw-body/-/raw-body-3.0.1.tgz", + "integrity": "sha512-9G8cA+tuMS75+6G/TzW8OtLzmBDMo8p1JRxN5AZ+LAp8uxGA8V8GZm4GQ4/N5QNQEnLmg6SS7wyuSmbKepiKqA==", + "license": "MIT", + "dependencies": { + "bytes": "3.1.2", + "http-errors": "2.0.0", + "iconv-lite": "0.7.0", + "unpipe": "1.0.0" + }, + "engines": { + "node": ">= 0.10" + } + }, + "node_modules/raw-body/node_modules/iconv-lite": { + "version": "0.7.0", + "resolved": "https://registry.npmjs.org/iconv-lite/-/iconv-lite-0.7.0.tgz", + "integrity": "sha512-cf6L2Ds3h57VVmkZe+Pn+5APsT7FpqJtEhhieDCvrE2MK5Qk9MyffgQyuxQTm6BChfeZNtcOLHp9IcWRVcIcBQ==", + "license": "MIT", + "dependencies": { + "safer-buffer": ">= 2.1.2 < 3.0.0" + }, + "engines": { + "node": ">=0.10.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/express" + } + }, + "node_modules/router": { + "version": "2.2.0", + "resolved": "https://registry.npmjs.org/router/-/router-2.2.0.tgz", + "integrity": "sha512-nLTrUKm2UyiL7rlhapu/Zl45FwNgkZGaCpZbIHajDYgwlJCOzLSk+cIPAnsEqV955GjILJnKbdQC1nVPz+gAYQ==", + "license": "MIT", + "dependencies": { + "debug": "^4.4.0", + "depd": "^2.0.0", + "is-promise": "^4.0.0", + "parseurl": "^1.3.3", + "path-to-regexp": "^8.0.0" + }, + "engines": { + "node": ">= 18" + } + }, + "node_modules/safe-buffer": { + "version": "5.2.1", + "resolved": "https://registry.npmjs.org/safe-buffer/-/safe-buffer-5.2.1.tgz", + "integrity": "sha512-rp3So07KcdmmKbGvgaNxQSJr7bGVSVk5S9Eq1F+ppbRo70+YeaDxkw5Dd8NPN+GD6bjnYm2VuPuCXmpuYvmCXQ==", + "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/safer-buffer": { + "version": "2.1.2", + "resolved": "https://registry.npmjs.org/safer-buffer/-/safer-buffer-2.1.2.tgz", + "integrity": "sha512-YZo3K82SD7Riyi0E1EQPojLz7kpepnSQI9IyPbHHg1XXXevb5dJI7tpyN2ADxGcQbHG7vcyRHk0cbwqcQriUtg==", + "license": "MIT" + }, + "node_modules/send": { + "version": "1.2.0", + "resolved": "https://registry.npmjs.org/send/-/send-1.2.0.tgz", + "integrity": "sha512-uaW0WwXKpL9blXE2o0bRhoL2EGXIrZxQ2ZQ4mgcfoBxdFmQold+qWsD2jLrfZ0trjKL6vOw0j//eAwcALFjKSw==", + "license": "MIT", + "dependencies": { + "debug": "^4.3.5", + "encodeurl": "^2.0.0", + "escape-html": "^1.0.3", + "etag": "^1.8.1", + "fresh": "^2.0.0", + "http-errors": "^2.0.0", + "mime-types": "^3.0.1", + "ms": "^2.1.3", + "on-finished": "^2.4.1", + "range-parser": "^1.2.1", + "statuses": "^2.0.1" + }, + "engines": { + "node": ">= 18" + } + }, + "node_modules/serve-static": { + "version": "2.2.0", + "resolved": "https://registry.npmjs.org/serve-static/-/serve-static-2.2.0.tgz", + "integrity": "sha512-61g9pCh0Vnh7IutZjtLGGpTA355+OPn2TyDv/6ivP2h/AdAVX9azsoxmg2/M6nZeQZNYBEwIcsne1mJd9oQItQ==", + "license": "MIT", + "dependencies": { + "encodeurl": "^2.0.0", + "escape-html": "^1.0.3", + "parseurl": "^1.3.3", + "send": "^1.2.0" + }, + "engines": { + "node": ">= 18" + } + }, + "node_modules/setprototypeof": { + "version": "1.2.0", + "resolved": "https://registry.npmjs.org/setprototypeof/-/setprototypeof-1.2.0.tgz", + "integrity": "sha512-E5LDX7Wrp85Kil5bhZv46j8jOeboKq5JMmYM3gVGdGH8xFpPWXUMsNrlODCrkoxMEeNi/XZIwuRvY4XNwYMJpw==", + "license": "ISC" + }, + "node_modules/side-channel": { + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/side-channel/-/side-channel-1.1.0.tgz", + "integrity": "sha512-ZX99e6tRweoUXqR+VBrslhda51Nh5MTQwou5tnUDgbtyM0dBgmhEDtWGP/xbKn6hqfPRHujUNwz5fy/wbbhnpw==", + "license": "MIT", + "dependencies": { + "es-errors": "^1.3.0", + "object-inspect": "^1.13.3", + "side-channel-list": "^1.0.0", + "side-channel-map": "^1.0.1", + "side-channel-weakmap": "^1.0.2" + }, + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/side-channel-list": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/side-channel-list/-/side-channel-list-1.0.0.tgz", + "integrity": "sha512-FCLHtRD/gnpCiCHEiJLOwdmFP+wzCmDEkc9y7NsYxeF4u7Btsn1ZuwgwJGxImImHicJArLP4R0yX4c2KCrMrTA==", + "license": "MIT", + "dependencies": { + "es-errors": "^1.3.0", + "object-inspect": "^1.13.3" + }, + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/side-channel-map": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/side-channel-map/-/side-channel-map-1.0.1.tgz", + "integrity": "sha512-VCjCNfgMsby3tTdo02nbjtM/ewra6jPHmpThenkTYh8pG9ucZ/1P8So4u4FGBek/BjpOVsDCMoLA/iuBKIFXRA==", + "license": "MIT", + "dependencies": { + "call-bound": "^1.0.2", + "es-errors": "^1.3.0", + "get-intrinsic": "^1.2.5", + "object-inspect": "^1.13.3" + }, + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/side-channel-weakmap": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/side-channel-weakmap/-/side-channel-weakmap-1.0.2.tgz", + "integrity": "sha512-WPS/HvHQTYnHisLo9McqBHOJk2FkHO/tlpvldyrnem4aeQp4hai3gythswg6p01oSoTl58rcpiFAjF2br2Ak2A==", + "license": "MIT", + "dependencies": { + "call-bound": "^1.0.2", + "es-errors": "^1.3.0", + "get-intrinsic": "^1.2.5", + "object-inspect": "^1.13.3", + "side-channel-map": "^1.0.1" + }, + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/source-map-js": { + "version": "1.2.1", + "resolved": "https://registry.npmjs.org/source-map-js/-/source-map-js-1.2.1.tgz", + "integrity": "sha512-UXWMKhLOwVKb728IUtQPXxfYU+usdybtUrK/8uGE8CQMvrhOpwvzDBwj0QhSL7MQc7vIsISBG8VQ8+IDQxpfQA==", + "license": "BSD-3-Clause", + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/statuses": { + "version": "2.0.2", + "resolved": "https://registry.npmjs.org/statuses/-/statuses-2.0.2.tgz", + "integrity": "sha512-DvEy55V3DB7uknRo+4iOGT5fP1slR8wQohVdknigZPMpMstaKJQWhwiYBACJE3Ul2pTnATihhBYnRhZQHGBiRw==", + "license": "MIT", + "engines": { + "node": ">= 0.8" + } + }, + "node_modules/tailwindcss": { + "version": "4.1.14", + "resolved": "https://registry.npmjs.org/tailwindcss/-/tailwindcss-4.1.14.tgz", + "integrity": "sha512-b7pCxjGO98LnxVkKjaZSDeNuljC4ueKUddjENJOADtubtdo8llTaJy7HwBMeLNSSo2N5QIAgklslK1+Ir8r6CA==", + "license": "MIT" + }, + "node_modules/tapable": { + "version": "2.3.0", + "resolved": "https://registry.npmjs.org/tapable/-/tapable-2.3.0.tgz", + "integrity": "sha512-g9ljZiwki/LfxmQADO3dEY1CbpmXT5Hm2fJ+QaGKwSXUylMybePR7/67YW7jOrrvjEgL1Fmz5kzyAjWVWLlucg==", + "license": "MIT", + "engines": { + "node": ">=6" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/webpack" + } + }, + "node_modules/tar": { + "version": "7.5.1", + "resolved": "https://registry.npmjs.org/tar/-/tar-7.5.1.tgz", + "integrity": "sha512-nlGpxf+hv0v7GkWBK2V9spgactGOp0qvfWRxUMjqHyzrt3SgwE48DIv/FhqPHJYLHpgW1opq3nERbz5Anq7n1g==", + "license": "ISC", + "dependencies": { + "@isaacs/fs-minipass": "^4.0.0", + "chownr": "^3.0.0", + "minipass": "^7.1.2", + "minizlib": "^3.1.0", + "yallist": "^5.0.0" + }, + "engines": { + "node": ">=18" + } + }, + "node_modules/to-regex-range": { + "version": "5.0.1", + "resolved": "https://registry.npmjs.org/to-regex-range/-/to-regex-range-5.0.1.tgz", + "integrity": "sha512-65P7iz6X5yEr1cwcgvQxbbIw7Uk3gOy5dIdtZ4rDveLqhrdJP+Li/Hx6tyK0NEb+2GCyneCMJiGqrADCSNk8sQ==", + "license": "MIT", + "dependencies": { + "is-number": "^7.0.0" + }, + "engines": { + "node": ">=8.0" + } + }, + "node_modules/toidentifier": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/toidentifier/-/toidentifier-1.0.1.tgz", + "integrity": "sha512-o5sSPKEkg/DIQNmH43V0/uerLrpzVedkUh8tGNvaeXpfpuwjKenlSox/2O/BTlZUtEe+JG7s5YhEz608PlAHRA==", + "license": "MIT", + "engines": { + "node": ">=0.6" + } + }, + "node_modules/type-is": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/type-is/-/type-is-2.0.1.tgz", + "integrity": "sha512-OZs6gsjF4vMp32qrCbiVSkrFmXtG/AZhY3t0iAMrMBiAZyV9oALtXO8hsrHbMXF9x6L3grlFuwW2oAz7cav+Gw==", + "license": "MIT", + "dependencies": { + "content-type": "^1.0.5", + "media-typer": "^1.1.0", + "mime-types": "^3.0.0" + }, + "engines": { + "node": ">= 0.6" + } + }, + "node_modules/unpipe": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/unpipe/-/unpipe-1.0.0.tgz", + "integrity": "sha512-pjy2bYhSsufwWlKwPc+l3cN7+wuJlK6uz0YdJEOlQDbl6jo/YlPi4mb8agUkVC8BF7V8NuzeyPNqRksA3hztKQ==", + "license": "MIT", + "engines": { + "node": ">= 0.8" + } + }, + "node_modules/vary": { + "version": "1.1.2", + "resolved": "https://registry.npmjs.org/vary/-/vary-1.1.2.tgz", + "integrity": "sha512-BNGbWLfd0eUPabhkXUVm0j8uuvREyTh5ovRa/dyow/BqAbZJyC+5fU+IzQOzmAKzYqYRAISoRhdQr3eIZ/PXqg==", + "license": "MIT", + "engines": { + "node": ">= 0.8" + } + }, + "node_modules/wrappy": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/wrappy/-/wrappy-1.0.2.tgz", + "integrity": "sha512-l4Sp/DRseor9wL6EvV2+TuQn63dMkPjZ/sp9XkghTEbV9KlPS1xUsZ3u7/IQO4wxtcFB4bgpQPRcR3QCvezPcQ==", + "license": "ISC" + }, + "node_modules/yallist": { + "version": "5.0.0", + "resolved": "https://registry.npmjs.org/yallist/-/yallist-5.0.0.tgz", + "integrity": "sha512-YgvUTfwqyc7UXVMrB+SImsVYSmTS8X/tSrtdNZMImM+n7+QTriRXyXim0mBrTXNeqzVF0KWGgHPeiyViFFrNDw==", + "license": "BlueOak-1.0.0", + "engines": { + "node": ">=18" + } + } + } +} diff --git a/web-components-reuse/server/package.json b/web-components-reuse/server/package.json new file mode 100644 index 00000000..62f1691b --- /dev/null +++ b/web-components-reuse/server/package.json @@ -0,0 +1,21 @@ +{ + "name": "web-components-reuse-server", + "version": "1.0.0", + "description": "Web Components Reuse API and HTMX app", + "main": "src/server.js", + "scripts": { + "watch:css": "npx @tailwindcss/cli -i ./assets/input.css -o ./assets/output.css --watch" + }, + "author": "Igor", + "license": "ISC", + "type": "module", + "engines": { + "node": ">=20.0.0" + }, + "dependencies": { + "@tailwindcss/cli": "^4.1.14", + "cors": "^2.8.5", + "express": "^5.1.0", + "tailwindcss": "^4.1.14" + } +} diff --git a/web-components-reuse/server/src/api.js b/web-components-reuse/server/src/api.js new file mode 100644 index 00000000..04b5b6a9 --- /dev/null +++ b/web-components-reuse/server/src/api.js @@ -0,0 +1,274 @@ +import express from "express"; +import * as Data from "./data.js"; + +/** + * @enum {string} + */ +const AssetCode = { + BONDS: "BONDS", + STOCKS: "STOCKS", + GOLD: "GOLD", + CASH: "CASH", + RLEST: "RLEST", + BTC: "BTC" +}; + +/** + * @enum {string} + */ +const CurrencyCode = { + USD: "USD", + EUR: "EUR", + JPY: "JPY", + GBP: "GBP", + CNY: "CNY", + PLN: "PLN" +}; + +/** + * @typedef {Object} Asset + * @property {AssetCode} code + * @property {number} marketSize + * @property {CurrencyCode} denomination + * + * @typedef {Object} Currency + * @property {CurrencyCode} code + * @property {number} marketSize + * @property {CurrencyCode} denomination + * + * @typedef {Object} ExchangeRate + * @property {CurrencyCode} from + * @property {CurrencyCode} to + * @property {number} value + */ + +// TODO: percentage of all global assets? +/** @type {Asset[]} */ +const assets = [ + { + code: AssetCode.BONDS, + marketSize: 145.1e12, + denomination: CurrencyCode.USD + }, + { + code: AssetCode.STOCKS, + marketSize: 126.7e12, + denomination: CurrencyCode.USD + + }, + { + code: AssetCode.GOLD, + marketSize: 22.6e12, + denomination: CurrencyCode.USD + }, + { + code: AssetCode.CASH, + marketSize: 12.6e12, + denomination: CurrencyCode.USD + }, + { + code: AssetCode.RLEST, + marketSize: 12.5e12, + denomination: CurrencyCode.USD + }, + { + code: AssetCode.BTC, + marketSize: 2.3e12, + denomination: CurrencyCode.USD + } +]; +let nextAssets = assets; + +/** @type {Currency[]} */ +const currencies = [ + { + code: CurrencyCode.USD, + marketSize: 6.639e12, + denomination: CurrencyCode.USD + }, + { + code: CurrencyCode.EUR, + marketSize: 2.292e12, + denomination: CurrencyCode.USD + }, + { + code: CurrencyCode.JPY, + marketSize: 1.253e12, + denomination: CurrencyCode.USD + }, + { + code: CurrencyCode.GBP, + marketSize: 968e9, + denomination: CurrencyCode.USD + }, + { + code: CurrencyCode.CNY, + marketSize: 526.2e9, + denomination: CurrencyCode.USD + }, + { + code: CurrencyCode.PLN, + marketSize: 13e9, + denomination: CurrencyCode.USD + } +]; +let nextCurrencies = currencies; + +const usdExchangeRates = [ + { + code: CurrencyCode.USD, + value: 1 + }, + { + code: CurrencyCode.EUR, + value: 0.85 + }, + { + code: CurrencyCode.JPY, + value: 148 + }, + { + code: CurrencyCode.GBP, + value: 0.73 + }, + { + code: CurrencyCode.CNY, + value: 7.11 + }, + { + code: CurrencyCode.PLN, + value: 3.63 + }, +]; +let nextUsdExchangeRates = usdExchangeRates; + +let assetsVersion = 1; +let currenciesVersion = 1; +let exchangeRatesVersion = 1; + +export class ValidationError extends Error { + constructor(message) { + super(message); + } +} + +export const router = express.Router(); + +router.get('/assets', (req, res) => { + const denomination = req.query.denomination ?? CurrencyCode.USD; + const clientAssetsVersion = versionFromHeader(req) ?? prefixedVersion(denomination, 0); + const assets = Data.versionedAssets(denomination, clientAssetsVersion); + if (!assets) { + returnNotModified(res); + return; + } + + returnVersionedJson(res, assets.responseVersion, assets); +}); + +router.get('/currencies', (req, res) => { + const denomination = req.query.denomination ?? CurrencyCode.USD; + const clientCurrenciesVersion = versionFromHeader(req) ?? prefixedVersion(denomination, 0); + const serverCurrenciesVersion = prefixedVersion(denomination, currenciesVersion); + if (clientCurrenciesVersion == serverCurrenciesVersion) { + returnNotModified(res); + return; + } + + const exchangeRateValue = exchangeRateFor(denomination); + const denominatedCurrencies = nextCurrencies.map(c => ({ ...c, marketSize: Math.round(c.marketSize * exchangeRateValue), denomination })); + returnVersionedJson(res, serverCurrenciesVersion, { + currencies: denominatedCurrencies, + responseVersion: serverCurrenciesVersion + }); +}); + +router.get('/exchange-rates/:from', (req, res) => { + const from = req.params.from; + + const clientExchangeRatesVersion = versionFromHeader(req) ?? prefixedVersion(from, 0); + const serverExchangeRatesVersion = prefixedVersion(from, exchangeRatesVersion); + if (clientExchangeRatesVersion == serverExchangeRatesVersion) { + returnNotModified(res); + return; + } + + const fromRequestedToDollarExchangeRate = 1 / exchangeRateFor(from); + + const exchangeRates = nextUsdExchangeRates.map(er => ({ + from, + to: er.code, + value: Math.round(fromRequestedToDollarExchangeRate * er.value * 100) / 100.0 + })); + + returnVersionedJson(res, serverExchangeRatesVersion, { + exchangeRates, responseVersion: serverExchangeRatesVersion + }); +}); + +function exchangeRateFor(denomination) { + if (denomination == CurrencyCode.USD) { + return 1; + } + const exchangeRate = nextUsdExchangeRates.find(er => er.code === denomination); + if (!exchangeRate) { + throw new ValidationError(`There is no exchange rate for ${denomination} denomination!`); + } + return exchangeRate.value; +} + +function prefixedVersion(prefix, version) { + return `${prefix}:${version}`; +} + +function versionFromHeader(req) { + return req.headers['If-None-Match']; +} + +function returnVersionedJson(res, version, object, status = 200) { + res.setHeader('ETag', version); + res.status(status).send(object); +} + +function returnNotModified(res) { + res.status(304).send(); +} + +export function scheduleDataRandomizer() { + return setInterval(() => { + if (Math.random() > 0.5) { + randomizeAssets(); + } + if (Math.random() > 0.5) { + randomizeCurrencies(); + } + if (Math.random() > 0.5) { + randomizeExchangeRates(); + } + }, 1000); +} + +function randomizeAssets() { + nextAssets = nextAssets.map(a => ({ ...a, marketSize: a.marketSize * nextValueMultiplier(0.9, 1.1) })); + assetsVersion++; +} + +function randomizeCurrencies() { + nextCurrencies = nextCurrencies.map(c => ({ ...c, marketSize: c.marketSize * nextValueMultiplier(0.9, 1.1) })); + currenciesVersion++; +} + +function randomizeExchangeRates() { + nextUsdExchangeRates = nextUsdExchangeRates.map(er => ({ + ...er, + value: er.code == CurrencyCode.USD ? er.value : er.value * nextValueMultiplier(0.9, 1.1) + })); + exchangeRatesVersion++; +} + +function nextValueMultiplier(min, max) { + if (min > max) { + throw new Error(`Min (${min}) cannot be greater than max (${max})`); + } + return min + (max - min) * Math.random(); +} diff --git a/web-components-reuse/server/src/data.js b/web-components-reuse/server/src/data.js new file mode 100644 index 00000000..3b755194 --- /dev/null +++ b/web-components-reuse/server/src/data.js @@ -0,0 +1,266 @@ +/** + * @enum {string} + */ +const AssetCode = { + BONDS: "BONDS", + STOCKS: "STOCKS", + GOLD: "GOLD", + CASH: "CASH", + RLEST: "RLEST", + BTC: "BTC" +}; + +/** + * @enum {string} + */ +const CurrencyCode = { + USD: "USD", + EUR: "EUR", + JPY: "JPY", + GBP: "GBP", + CNY: "CNY", + PLN: "PLN" +}; + +/** + * @typedef {Object} Asset + * @property {AssetCode} code + * @property {number} marketSize + * @property {CurrencyCode} denomination + * + * @typedef {Object} Currency + * @property {CurrencyCode} code + * @property {number} marketSize + * @property {CurrencyCode} denomination + * + * @typedef {Object} ExchangeRate + * @property {CurrencyCode} from + * @property {CurrencyCode} to + * @property {number} value + */ + +// TODO: percentage of all global assets? +/** @type {Asset[]} */ +const assets = [ + { + code: AssetCode.BONDS, + marketSize: 145.1e12, + denomination: CurrencyCode.USD + }, + { + code: AssetCode.STOCKS, + marketSize: 126.7e12, + denomination: CurrencyCode.USD + + }, + { + code: AssetCode.GOLD, + marketSize: 22.6e12, + denomination: CurrencyCode.USD + }, + { + code: AssetCode.CASH, + marketSize: 12.6e12, + denomination: CurrencyCode.USD + }, + { + code: AssetCode.RLEST, + marketSize: 12.5e12, + denomination: CurrencyCode.USD + }, + { + code: AssetCode.BTC, + marketSize: 2.3e12, + denomination: CurrencyCode.USD + } +]; +let nextAssets = assets; + +/** @type {Currency[]} */ +const currencies = [ + { + code: CurrencyCode.USD, + marketSize: 6.639e12, + denomination: CurrencyCode.USD + }, + { + code: CurrencyCode.EUR, + marketSize: 2.292e12, + denomination: CurrencyCode.USD + }, + { + code: CurrencyCode.JPY, + marketSize: 1.253e12, + denomination: CurrencyCode.USD + }, + { + code: CurrencyCode.GBP, + marketSize: 968e9, + denomination: CurrencyCode.USD + }, + { + code: CurrencyCode.CNY, + marketSize: 526.2e9, + denomination: CurrencyCode.USD + }, + { + code: CurrencyCode.PLN, + marketSize: 13e9, + denomination: CurrencyCode.USD + } +]; +let nextCurrencies = currencies; + +const usdExchangeRates = [ + { + code: CurrencyCode.USD, + value: 1 + }, + { + code: CurrencyCode.EUR, + value: 0.85 + }, + { + code: CurrencyCode.JPY, + value: 148 + }, + { + code: CurrencyCode.GBP, + value: 0.73 + }, + { + code: CurrencyCode.CNY, + value: 7.11 + }, + { + code: CurrencyCode.PLN, + value: 3.63 + }, +]; +let nextUsdExchangeRates = usdExchangeRates; + +let assetsVersion = 1; +let currenciesVersion = 1; +let exchangeRatesVersion = 1; + +export function versionedAssets(denomination, version) { + const denominatedAsetsVersion = prefixedVersion(denomination, assetsVersion); + if (version == denominatedAsetsVersion) { + return null; + } + + const exchangeRateValue = exchangeRateFor(denomination); + const denominatedAssets = nextAssets.map(a => ({ ...a, marketSize: Math.round(a.marketSize * exchangeRateValue), denomination })); + + return { + assets: denominatedAssets, + responseVersion: denominatedAsetsVersion + }; +} + +// router.get('/currencies', (req, res) => { +// const denomination = req.query.denomination ?? CurrencyCode.USD; +// const clientCurrenciesVersion = versionFromHeader(req) ?? prefixedVersion(denomination, 0); +// const serverCurrenciesVersion = prefixedVersion(denomination, currenciesVersion); +// if (clientCurrenciesVersion == serverCurrenciesVersion) { +// returnNotModified(res); +// return; +// } + +// const exchangeRateValue = exchangeRateFor(denomination); +// const denominatedCurrencies = nextCurrencies.map(c => ({ ...c, marketSize: Math.round(c.marketSize * exchangeRateValue), denomination })); +// returnVersionedJson(res, serverCurrenciesVersion, { +// currencies: denominatedCurrencies, +// responseVersion: serverCurrenciesVersion +// }); +// }); + +// router.get('/exchange-rates/:from', (req, res) => { +// const from = req.params.from; + +// const clientExchangeRatesVersion = versionFromHeader(req) ?? prefixedVersion(from, 0); +// const serverExchangeRatesVersion = prefixedVersion(from, exchangeRatesVersion); +// if (clientExchangeRatesVersion == serverExchangeRatesVersion) { +// returnNotModified(res); +// return; +// } + +// const fromRequestedToDollarExchangeRate = 1 / exchangeRateFor(from); + +// const exchangeRates = nextUsdExchangeRates.map(er => ({ +// from, +// to: er.code, +// value: Math.round(fromRequestedToDollarExchangeRate * er.value * 100) / 100.0 +// })); + +// returnVersionedJson(res, serverExchangeRatesVersion, { +// exchangeRates, responseVersion: serverExchangeRatesVersion +// }); +// }); + +function exchangeRateFor(denomination) { + if (denomination == CurrencyCode.USD) { + return 1; + } + const exchangeRate = nextUsdExchangeRates.find(er => er.code === denomination); + if (!exchangeRate) { + throw new ValidationError(`There is no exchange rate for ${denomination} denomination!`); + } + return exchangeRate.value; +} + +function prefixedVersion(prefix, version) { + return `${prefix}:${version}`; +} + +function versionFromHeader(req) { + return req.headers['If-None-Match']; +} + +function returnVersionedJson(res, version, object, status = 200) { + res.setHeader('ETag', version); + res.status(status).send(object); +} + +function returnNotModified(res) { + res.status(304).send(); +} + +export function scheduleDataRandomizer() { + return setInterval(() => { + if (Math.random() > 0.5) { + randomizeAssets(); + } + if (Math.random() > 0.5) { + randomizeCurrencies(); + } + if (Math.random() > 0.5) { + randomizeExchangeRates(); + } + }, 1000); +} + +function randomizeAssets() { + nextAssets = nextAssets.map(a => ({ ...a, marketSize: a.marketSize * nextValueMultiplier(0.9, 1.1) })); + assetsVersion++; +} + +function randomizeCurrencies() { + nextCurrencies = nextCurrencies.map(c => ({ ...c, marketSize: c.marketSize * nextValueMultiplier(0.9, 1.1) })); + currenciesVersion++; +} + +function randomizeExchangeRates() { + nextUsdExchangeRates = nextUsdExchangeRates.map(er => ({ + ...er, + value: er.code == CurrencyCode.USD ? er.value : er.value * nextValueMultiplier(0.9, 1.1) + })); + exchangeRatesVersion++; +} + +function nextValueMultiplier(min, max) { + if (min > max) { + throw new Error(`Min (${min}) cannot be greater than max (${max})`); + } + return min + (max - min) * Math.random(); +} diff --git a/web-components-reuse/server/src/htmx-app.js b/web-components-reuse/server/src/htmx-app.js new file mode 100644 index 00000000..57f23e8d --- /dev/null +++ b/web-components-reuse/server/src/htmx-app.js @@ -0,0 +1,124 @@ +import express from "express"; + +import path from "path"; +import fs from "fs"; + +const CSS_PATH = path.join("assets", "output.css"); +const WEB_COMPONENTS_PATH = path.join("assets", "web-components.js"); + +const HTMX_SCRIPT = ''; +const WEB_COMPONENTS_SCRIPT = ``; + +export const router = express.Router(); + +router.get("/assets/*any", async (req, res) => { + if (req.url.includes(".css")) { + await returnFile(res, CSS_PATH); + } else { + const filePath = req.url.substring(1); + await returnFile(res, filePath); + } +}); + +router.get("/", (req, res) => { + returnHomePage(res); +}); + +function returnHomePage(res) { + // TODO: does HTMX script must be in the head? + const html = ` + + + + + + Markets + + + +
    + ${marketsHeaderHTML()} + ${assetsAndCurrenciesHTML()} + ${marketsProjectionsHTML()} +
    + + ${HTMX_SCRIPT} + ${WEB_COMPONENTS_SCRIPT} + + + `; + returnHtml(res, html); +} + +// TODO: params for all +function marketsHeaderHTML() { + return ` + + `; +} + +function assetsAndCurrenciesHTML() { + return ` + + + `; +} + +function marketsProjectionsHTML() { + return ` + + + `; +} + +function returnHtml(res, html, status = 200) { + res.contentType('text/html') + .status(status) + .send(html); +} + +function returnText(res, text, status = 200) { + res.contentType('text/plain') + .status(status) + .send(text); +} + +function returnJson(res, object, status = 200) { + res.status(status).send(object); +} + +function returnTextError(res, error, status = 400) { + returnText(res, error, status); +} + +function returnJs(res, js) { + res.contentType("text/javascript").send(js); +} + +async function returnFile(res, filePath, contentType = null) { + const file = await fs.promises.readFile(filePath); + const resolvedContentType = contentType ? contentType : contentTypeFromFilePath(filePath); + if (resolvedContentType) { + res.contentType(resolvedContentType); + } + res.send(file); +} + +function contentTypeFromFilePath(filePath) { + if (filePath.endsWith("png")) { + return "image/png"; + } + if (filePath.endsWith("jpg") || filePath.endsWith("jpeg")) { + return "image/jpeg"; + } + if (filePath.endsWith("svg")) { + return "image/svg+xml"; + } + if (filePath.endsWith("css")) { + return "text/css"; + } + if (filePath.endsWith("js")) { + return "text/javascript"; + } + return null; +} \ No newline at end of file diff --git a/web-components-reuse/server/src/server.js b/web-components-reuse/server/src/server.js new file mode 100644 index 00000000..047cd5e0 --- /dev/null +++ b/web-components-reuse/server/src/server.js @@ -0,0 +1,60 @@ +import bodyParser from "body-parser"; +import express from "express"; +import cors from 'cors'; + +import * as Api from './api.js'; +import * as HtmxApp from './htmx-app.js'; + +const SERVER_PORT = process.env.SERVER_PORT || 8080; + +const app = express(); + +app.use(cors()); + +app.use("/api", Api.router); +app.use("/", HtmxApp.router); + +app.use((err, req, res, next) => { + console.error(err.stack); + if (err instanceof Api.ValidationError) { + res.status(400) + .send({ + error: "ValidationError", + message: err.message + }); + } else { + res.status(500) + .send({ + error: "Unknown", + message: "Internal error" + }); + } +}); + +const server = app.listen(SERVER_PORT, () => { + console.log(`Server has started on port ${SERVER_PORT}!`); +}); + +const randomizerTaskId = Api.scheduleDataRandomizer(); + +process.on('SIGTERM', () => { + console.log('SIGTERM signal received: closing HTTP server'); + onShutdown(); +}); + +process.on('SIGINT', () => { + console.log('SIGINT signal received: closing HTTP server'); + onShutdown(); +}); + +function onShutdown() { + clearInterval(randomizerTaskId); + server.close(() => { + console.log('HTTP server closed'); + }); + server.closeIdleConnections(); + setTimeout(() => { + console.log("Closing all remaining connections..."); + server.closeAllConnections(); + }, 1000); +} \ No newline at end of file diff --git a/web-components-reuse/vue-app/.env.development b/web-components-reuse/vue-app/.env.development new file mode 100644 index 00000000..06b0a439 --- /dev/null +++ b/web-components-reuse/vue-app/.env.development @@ -0,0 +1 @@ +VITE_API_BASE_URL="http://localhost:8080/api" \ No newline at end of file diff --git a/web-components-reuse/vue-app/.env.production b/web-components-reuse/vue-app/.env.production new file mode 100644 index 00000000..06b0a439 --- /dev/null +++ b/web-components-reuse/vue-app/.env.production @@ -0,0 +1 @@ +VITE_API_BASE_URL="http://localhost:8080/api" \ No newline at end of file diff --git a/web-components-reuse/vue-app/README.md b/web-components-reuse/vue-app/README.md new file mode 100644 index 00000000..33895ab2 --- /dev/null +++ b/web-components-reuse/vue-app/README.md @@ -0,0 +1,5 @@ +# Vue 3 + TypeScript + Vite + +This template should help get you started developing with Vue 3 and TypeScript in Vite. The template uses Vue 3 ` + + diff --git a/web-components-reuse/vue-app/package-lock.json b/web-components-reuse/vue-app/package-lock.json new file mode 100644 index 00000000..69828a13 --- /dev/null +++ b/web-components-reuse/vue-app/package-lock.json @@ -0,0 +1,2123 @@ +{ + "name": "vue-app", + "version": "0.0.0", + "lockfileVersion": 3, + "requires": true, + "packages": { + "": { + "name": "vue-app", + "version": "0.0.0", + "dependencies": { + "@tailwindcss/vite": "^4.1.13", + "tailwindcss": "^4.1.13", + "vue": "^3.5.18", + "vue-i18n": "^11.1.12" + }, + "devDependencies": { + "@vitejs/plugin-vue": "^6.0.1", + "@vue/tsconfig": "^0.7.0", + "typescript": "~5.8.3", + "vite": "^7.1.2", + "vue-tsc": "^3.0.5" + } + }, + "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==", + "license": "MIT", + "engines": { + "node": ">=6.9.0" + } + }, + "node_modules/@babel/helper-validator-identifier": { + "version": "7.27.1", + "resolved": "https://registry.npmjs.org/@babel/helper-validator-identifier/-/helper-validator-identifier-7.27.1.tgz", + "integrity": "sha512-D2hP9eA+Sqx1kBZgzxZh0y1trbuU+JoDkiEwqhQ36nodYqJwyEIhPSdMNd7lOm/4io72luTPWH20Yda0xOuUow==", + "license": "MIT", + "engines": { + "node": ">=6.9.0" + } + }, + "node_modules/@babel/parser": { + "version": "7.28.4", + "resolved": "https://registry.npmjs.org/@babel/parser/-/parser-7.28.4.tgz", + "integrity": "sha512-yZbBqeM6TkpP9du/I2pUZnJsRMGGvOuIrhjzC1AwHwW+6he4mni6Bp/m8ijn0iOuZuPI2BfkCoSRunpyjnrQKg==", + "license": "MIT", + "dependencies": { + "@babel/types": "^7.28.4" + }, + "bin": { + "parser": "bin/babel-parser.js" + }, + "engines": { + "node": ">=6.0.0" + } + }, + "node_modules/@babel/types": { + "version": "7.28.4", + "resolved": "https://registry.npmjs.org/@babel/types/-/types-7.28.4.tgz", + "integrity": "sha512-bkFqkLhh3pMBUQQkpVgWDWq/lqzc2678eUyDlTBhRqhCHFguYYGM0Efga7tYk4TogG/3x0EEl66/OQ+WGbWB/Q==", + "license": "MIT", + "dependencies": { + "@babel/helper-string-parser": "^7.27.1", + "@babel/helper-validator-identifier": "^7.27.1" + }, + "engines": { + "node": ">=6.9.0" + } + }, + "node_modules/@esbuild/aix-ppc64": { + "version": "0.25.9", + "resolved": "https://registry.npmjs.org/@esbuild/aix-ppc64/-/aix-ppc64-0.25.9.tgz", + "integrity": "sha512-OaGtL73Jck6pBKjNIe24BnFE6agGl+6KxDtTfHhy1HmhthfKouEcOhqpSL64K4/0WCtbKFLOdzD/44cJ4k9opA==", + "cpu": [ + "ppc64" + ], + "license": "MIT", + "optional": true, + "os": [ + "aix" + ], + "engines": { + "node": ">=18" + } + }, + "node_modules/@esbuild/android-arm": { + "version": "0.25.9", + "resolved": "https://registry.npmjs.org/@esbuild/android-arm/-/android-arm-0.25.9.tgz", + "integrity": "sha512-5WNI1DaMtxQ7t7B6xa572XMXpHAaI/9Hnhk8lcxF4zVN4xstUgTlvuGDorBguKEnZO70qwEcLpfifMLoxiPqHQ==", + "cpu": [ + "arm" + ], + "license": "MIT", + "optional": true, + "os": [ + "android" + ], + "engines": { + "node": ">=18" + } + }, + "node_modules/@esbuild/android-arm64": { + "version": "0.25.9", + "resolved": "https://registry.npmjs.org/@esbuild/android-arm64/-/android-arm64-0.25.9.tgz", + "integrity": "sha512-IDrddSmpSv51ftWslJMvl3Q2ZT98fUSL2/rlUXuVqRXHCs5EUF1/f+jbjF5+NG9UffUDMCiTyh8iec7u8RlTLg==", + "cpu": [ + "arm64" + ], + "license": "MIT", + "optional": true, + "os": [ + "android" + ], + "engines": { + "node": ">=18" + } + }, + "node_modules/@esbuild/android-x64": { + "version": "0.25.9", + "resolved": "https://registry.npmjs.org/@esbuild/android-x64/-/android-x64-0.25.9.tgz", + "integrity": "sha512-I853iMZ1hWZdNllhVZKm34f4wErd4lMyeV7BLzEExGEIZYsOzqDWDf+y082izYUE8gtJnYHdeDpN/6tUdwvfiw==", + "cpu": [ + "x64" + ], + "license": "MIT", + "optional": true, + "os": [ + "android" + ], + "engines": { + "node": ">=18" + } + }, + "node_modules/@esbuild/darwin-arm64": { + "version": "0.25.9", + "resolved": "https://registry.npmjs.org/@esbuild/darwin-arm64/-/darwin-arm64-0.25.9.tgz", + "integrity": "sha512-XIpIDMAjOELi/9PB30vEbVMs3GV1v2zkkPnuyRRURbhqjyzIINwj+nbQATh4H9GxUgH1kFsEyQMxwiLFKUS6Rg==", + "cpu": [ + "arm64" + ], + "license": "MIT", + "optional": true, + "os": [ + "darwin" + ], + "engines": { + "node": ">=18" + } + }, + "node_modules/@esbuild/darwin-x64": { + "version": "0.25.9", + "resolved": "https://registry.npmjs.org/@esbuild/darwin-x64/-/darwin-x64-0.25.9.tgz", + "integrity": "sha512-jhHfBzjYTA1IQu8VyrjCX4ApJDnH+ez+IYVEoJHeqJm9VhG9Dh2BYaJritkYK3vMaXrf7Ogr/0MQ8/MeIefsPQ==", + "cpu": [ + "x64" + ], + "license": "MIT", + "optional": true, + "os": [ + "darwin" + ], + "engines": { + "node": ">=18" + } + }, + "node_modules/@esbuild/freebsd-arm64": { + "version": "0.25.9", + "resolved": "https://registry.npmjs.org/@esbuild/freebsd-arm64/-/freebsd-arm64-0.25.9.tgz", + "integrity": "sha512-z93DmbnY6fX9+KdD4Ue/H6sYs+bhFQJNCPZsi4XWJoYblUqT06MQUdBCpcSfuiN72AbqeBFu5LVQTjfXDE2A6Q==", + "cpu": [ + "arm64" + ], + "license": "MIT", + "optional": true, + "os": [ + "freebsd" + ], + "engines": { + "node": ">=18" + } + }, + "node_modules/@esbuild/freebsd-x64": { + "version": "0.25.9", + "resolved": "https://registry.npmjs.org/@esbuild/freebsd-x64/-/freebsd-x64-0.25.9.tgz", + "integrity": "sha512-mrKX6H/vOyo5v71YfXWJxLVxgy1kyt1MQaD8wZJgJfG4gq4DpQGpgTB74e5yBeQdyMTbgxp0YtNj7NuHN0PoZg==", + "cpu": [ + "x64" + ], + "license": "MIT", + "optional": true, + "os": [ + "freebsd" + ], + "engines": { + "node": ">=18" + } + }, + "node_modules/@esbuild/linux-arm": { + "version": "0.25.9", + "resolved": "https://registry.npmjs.org/@esbuild/linux-arm/-/linux-arm-0.25.9.tgz", + "integrity": "sha512-HBU2Xv78SMgaydBmdor38lg8YDnFKSARg1Q6AT0/y2ezUAKiZvc211RDFHlEZRFNRVhcMamiToo7bDx3VEOYQw==", + "cpu": [ + "arm" + ], + "license": "MIT", + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": ">=18" + } + }, + "node_modules/@esbuild/linux-arm64": { + "version": "0.25.9", + "resolved": "https://registry.npmjs.org/@esbuild/linux-arm64/-/linux-arm64-0.25.9.tgz", + "integrity": "sha512-BlB7bIcLT3G26urh5Dmse7fiLmLXnRlopw4s8DalgZ8ef79Jj4aUcYbk90g8iCa2467HX8SAIidbL7gsqXHdRw==", + "cpu": [ + "arm64" + ], + "license": "MIT", + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": ">=18" + } + }, + "node_modules/@esbuild/linux-ia32": { + "version": "0.25.9", + "resolved": "https://registry.npmjs.org/@esbuild/linux-ia32/-/linux-ia32-0.25.9.tgz", + "integrity": "sha512-e7S3MOJPZGp2QW6AK6+Ly81rC7oOSerQ+P8L0ta4FhVi+/j/v2yZzx5CqqDaWjtPFfYz21Vi1S0auHrap3Ma3A==", + "cpu": [ + "ia32" + ], + "license": "MIT", + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": ">=18" + } + }, + "node_modules/@esbuild/linux-loong64": { + "version": "0.25.9", + "resolved": "https://registry.npmjs.org/@esbuild/linux-loong64/-/linux-loong64-0.25.9.tgz", + "integrity": "sha512-Sbe10Bnn0oUAB2AalYztvGcK+o6YFFA/9829PhOCUS9vkJElXGdphz0A3DbMdP8gmKkqPmPcMJmJOrI3VYB1JQ==", + "cpu": [ + "loong64" + ], + "license": "MIT", + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": ">=18" + } + }, + "node_modules/@esbuild/linux-mips64el": { + "version": "0.25.9", + "resolved": "https://registry.npmjs.org/@esbuild/linux-mips64el/-/linux-mips64el-0.25.9.tgz", + "integrity": "sha512-YcM5br0mVyZw2jcQeLIkhWtKPeVfAerES5PvOzaDxVtIyZ2NUBZKNLjC5z3/fUlDgT6w89VsxP2qzNipOaaDyA==", + "cpu": [ + "mips64el" + ], + "license": "MIT", + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": ">=18" + } + }, + "node_modules/@esbuild/linux-ppc64": { + "version": "0.25.9", + "resolved": "https://registry.npmjs.org/@esbuild/linux-ppc64/-/linux-ppc64-0.25.9.tgz", + "integrity": "sha512-++0HQvasdo20JytyDpFvQtNrEsAgNG2CY1CLMwGXfFTKGBGQT3bOeLSYE2l1fYdvML5KUuwn9Z8L1EWe2tzs1w==", + "cpu": [ + "ppc64" + ], + "license": "MIT", + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": ">=18" + } + }, + "node_modules/@esbuild/linux-riscv64": { + "version": "0.25.9", + "resolved": "https://registry.npmjs.org/@esbuild/linux-riscv64/-/linux-riscv64-0.25.9.tgz", + "integrity": "sha512-uNIBa279Y3fkjV+2cUjx36xkx7eSjb8IvnL01eXUKXez/CBHNRw5ekCGMPM0BcmqBxBcdgUWuUXmVWwm4CH9kg==", + "cpu": [ + "riscv64" + ], + "license": "MIT", + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": ">=18" + } + }, + "node_modules/@esbuild/linux-s390x": { + "version": "0.25.9", + "resolved": "https://registry.npmjs.org/@esbuild/linux-s390x/-/linux-s390x-0.25.9.tgz", + "integrity": "sha512-Mfiphvp3MjC/lctb+7D287Xw1DGzqJPb/J2aHHcHxflUo+8tmN/6d4k6I2yFR7BVo5/g7x2Monq4+Yew0EHRIA==", + "cpu": [ + "s390x" + ], + "license": "MIT", + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": ">=18" + } + }, + "node_modules/@esbuild/linux-x64": { + "version": "0.25.9", + "resolved": "https://registry.npmjs.org/@esbuild/linux-x64/-/linux-x64-0.25.9.tgz", + "integrity": "sha512-iSwByxzRe48YVkmpbgoxVzn76BXjlYFXC7NvLYq+b+kDjyyk30J0JY47DIn8z1MO3K0oSl9fZoRmZPQI4Hklzg==", + "cpu": [ + "x64" + ], + "license": "MIT", + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": ">=18" + } + }, + "node_modules/@esbuild/netbsd-arm64": { + "version": "0.25.9", + "resolved": "https://registry.npmjs.org/@esbuild/netbsd-arm64/-/netbsd-arm64-0.25.9.tgz", + "integrity": "sha512-9jNJl6FqaUG+COdQMjSCGW4QiMHH88xWbvZ+kRVblZsWrkXlABuGdFJ1E9L7HK+T0Yqd4akKNa/lO0+jDxQD4Q==", + "cpu": [ + "arm64" + ], + "license": "MIT", + "optional": true, + "os": [ + "netbsd" + ], + "engines": { + "node": ">=18" + } + }, + "node_modules/@esbuild/netbsd-x64": { + "version": "0.25.9", + "resolved": "https://registry.npmjs.org/@esbuild/netbsd-x64/-/netbsd-x64-0.25.9.tgz", + "integrity": "sha512-RLLdkflmqRG8KanPGOU7Rpg829ZHu8nFy5Pqdi9U01VYtG9Y0zOG6Vr2z4/S+/3zIyOxiK6cCeYNWOFR9QP87g==", + "cpu": [ + "x64" + ], + "license": "MIT", + "optional": true, + "os": [ + "netbsd" + ], + "engines": { + "node": ">=18" + } + }, + "node_modules/@esbuild/openbsd-arm64": { + "version": "0.25.9", + "resolved": "https://registry.npmjs.org/@esbuild/openbsd-arm64/-/openbsd-arm64-0.25.9.tgz", + "integrity": "sha512-YaFBlPGeDasft5IIM+CQAhJAqS3St3nJzDEgsgFixcfZeyGPCd6eJBWzke5piZuZ7CtL656eOSYKk4Ls2C0FRQ==", + "cpu": [ + "arm64" + ], + "license": "MIT", + "optional": true, + "os": [ + "openbsd" + ], + "engines": { + "node": ">=18" + } + }, + "node_modules/@esbuild/openbsd-x64": { + "version": "0.25.9", + "resolved": "https://registry.npmjs.org/@esbuild/openbsd-x64/-/openbsd-x64-0.25.9.tgz", + "integrity": "sha512-1MkgTCuvMGWuqVtAvkpkXFmtL8XhWy+j4jaSO2wxfJtilVCi0ZE37b8uOdMItIHz4I6z1bWWtEX4CJwcKYLcuA==", + "cpu": [ + "x64" + ], + "license": "MIT", + "optional": true, + "os": [ + "openbsd" + ], + "engines": { + "node": ">=18" + } + }, + "node_modules/@esbuild/openharmony-arm64": { + "version": "0.25.9", + "resolved": "https://registry.npmjs.org/@esbuild/openharmony-arm64/-/openharmony-arm64-0.25.9.tgz", + "integrity": "sha512-4Xd0xNiMVXKh6Fa7HEJQbrpP3m3DDn43jKxMjxLLRjWnRsfxjORYJlXPO4JNcXtOyfajXorRKY9NkOpTHptErg==", + "cpu": [ + "arm64" + ], + "license": "MIT", + "optional": true, + "os": [ + "openharmony" + ], + "engines": { + "node": ">=18" + } + }, + "node_modules/@esbuild/sunos-x64": { + "version": "0.25.9", + "resolved": "https://registry.npmjs.org/@esbuild/sunos-x64/-/sunos-x64-0.25.9.tgz", + "integrity": "sha512-WjH4s6hzo00nNezhp3wFIAfmGZ8U7KtrJNlFMRKxiI9mxEK1scOMAaa9i4crUtu+tBr+0IN6JCuAcSBJZfnphw==", + "cpu": [ + "x64" + ], + "license": "MIT", + "optional": true, + "os": [ + "sunos" + ], + "engines": { + "node": ">=18" + } + }, + "node_modules/@esbuild/win32-arm64": { + "version": "0.25.9", + "resolved": "https://registry.npmjs.org/@esbuild/win32-arm64/-/win32-arm64-0.25.9.tgz", + "integrity": "sha512-mGFrVJHmZiRqmP8xFOc6b84/7xa5y5YvR1x8djzXpJBSv/UsNK6aqec+6JDjConTgvvQefdGhFDAs2DLAds6gQ==", + "cpu": [ + "arm64" + ], + "license": "MIT", + "optional": true, + "os": [ + "win32" + ], + "engines": { + "node": ">=18" + } + }, + "node_modules/@esbuild/win32-ia32": { + "version": "0.25.9", + "resolved": "https://registry.npmjs.org/@esbuild/win32-ia32/-/win32-ia32-0.25.9.tgz", + "integrity": "sha512-b33gLVU2k11nVx1OhX3C8QQP6UHQK4ZtN56oFWvVXvz2VkDoe6fbG8TOgHFxEvqeqohmRnIHe5A1+HADk4OQww==", + "cpu": [ + "ia32" + ], + "license": "MIT", + "optional": true, + "os": [ + "win32" + ], + "engines": { + "node": ">=18" + } + }, + "node_modules/@esbuild/win32-x64": { + "version": "0.25.9", + "resolved": "https://registry.npmjs.org/@esbuild/win32-x64/-/win32-x64-0.25.9.tgz", + "integrity": "sha512-PPOl1mi6lpLNQxnGoyAfschAodRFYXJ+9fs6WHXz7CSWKbOqiMZsubC+BQsVKuul+3vKLuwTHsS2c2y9EoKwxQ==", + "cpu": [ + "x64" + ], + "license": "MIT", + "optional": true, + "os": [ + "win32" + ], + "engines": { + "node": ">=18" + } + }, + "node_modules/@intlify/core-base": { + "version": "11.1.12", + "resolved": "https://registry.npmjs.org/@intlify/core-base/-/core-base-11.1.12.tgz", + "integrity": "sha512-whh0trqRsSqVLNEUCwU59pyJZYpU8AmSWl8M3Jz2Mv5ESPP6kFh4juas2NpZ1iCvy7GlNRffUD1xr84gceimjg==", + "license": "MIT", + "dependencies": { + "@intlify/message-compiler": "11.1.12", + "@intlify/shared": "11.1.12" + }, + "engines": { + "node": ">= 16" + }, + "funding": { + "url": "https://github.com/sponsors/kazupon" + } + }, + "node_modules/@intlify/message-compiler": { + "version": "11.1.12", + "resolved": "https://registry.npmjs.org/@intlify/message-compiler/-/message-compiler-11.1.12.tgz", + "integrity": "sha512-Fv9iQSJoJaXl4ZGkOCN1LDM3trzze0AS2zRz2EHLiwenwL6t0Ki9KySYlyr27yVOj5aVz0e55JePO+kELIvfdQ==", + "license": "MIT", + "dependencies": { + "@intlify/shared": "11.1.12", + "source-map-js": "^1.0.2" + }, + "engines": { + "node": ">= 16" + }, + "funding": { + "url": "https://github.com/sponsors/kazupon" + } + }, + "node_modules/@intlify/shared": { + "version": "11.1.12", + "resolved": "https://registry.npmjs.org/@intlify/shared/-/shared-11.1.12.tgz", + "integrity": "sha512-Om86EjuQtA69hdNj3GQec9ZC0L0vPSAnXzB3gP/gyJ7+mA7t06d9aOAiqMZ+xEOsumGP4eEBlfl8zF2LOTzf2A==", + "license": "MIT", + "engines": { + "node": ">= 16" + }, + "funding": { + "url": "https://github.com/sponsors/kazupon" + } + }, + "node_modules/@isaacs/fs-minipass": { + "version": "4.0.1", + "resolved": "https://registry.npmjs.org/@isaacs/fs-minipass/-/fs-minipass-4.0.1.tgz", + "integrity": "sha512-wgm9Ehl2jpeqP3zw/7mo3kRHFp5MEDhqAdwy1fTGkHAwnkGOVsgpvQhL8B5n1qlb01jV3n/bI0ZfZp5lWA1k4w==", + "license": "ISC", + "dependencies": { + "minipass": "^7.0.4" + }, + "engines": { + "node": ">=18.0.0" + } + }, + "node_modules/@jridgewell/gen-mapping": { + "version": "0.3.13", + "resolved": "https://registry.npmjs.org/@jridgewell/gen-mapping/-/gen-mapping-0.3.13.tgz", + "integrity": "sha512-2kkt/7niJ6MgEPxF0bYdQ6etZaA+fQvDcLKckhy1yIQOzaoKjBBjSj63/aLVjYE3qhRt5dvM+uUyfCg6UKCBbA==", + "license": "MIT", + "dependencies": { + "@jridgewell/sourcemap-codec": "^1.5.0", + "@jridgewell/trace-mapping": "^0.3.24" + } + }, + "node_modules/@jridgewell/remapping": { + "version": "2.3.5", + "resolved": "https://registry.npmjs.org/@jridgewell/remapping/-/remapping-2.3.5.tgz", + "integrity": "sha512-LI9u/+laYG4Ds1TDKSJW2YPrIlcVYOwi2fUC6xB43lueCjgxV4lffOCZCtYFiH6TNOX+tQKXx97T4IKHbhyHEQ==", + "license": "MIT", + "dependencies": { + "@jridgewell/gen-mapping": "^0.3.5", + "@jridgewell/trace-mapping": "^0.3.24" + } + }, + "node_modules/@jridgewell/resolve-uri": { + "version": "3.1.2", + "resolved": "https://registry.npmjs.org/@jridgewell/resolve-uri/-/resolve-uri-3.1.2.tgz", + "integrity": "sha512-bRISgCIjP20/tbWSPWMEi54QVPRZExkuD9lJL+UIxUKtwVJA8wW1Trb1jMs1RFXo1CBTNZ/5hpC9QvmKWdopKw==", + "license": "MIT", + "engines": { + "node": ">=6.0.0" + } + }, + "node_modules/@jridgewell/sourcemap-codec": { + "version": "1.5.5", + "resolved": "https://registry.npmjs.org/@jridgewell/sourcemap-codec/-/sourcemap-codec-1.5.5.tgz", + "integrity": "sha512-cYQ9310grqxueWbl+WuIUIaiUaDcj7WOq5fVhEljNVgRfOUhY9fy2zTvfoqWsnebh8Sl70VScFbICvJnLKB0Og==", + "license": "MIT" + }, + "node_modules/@jridgewell/trace-mapping": { + "version": "0.3.31", + "resolved": "https://registry.npmjs.org/@jridgewell/trace-mapping/-/trace-mapping-0.3.31.tgz", + "integrity": "sha512-zzNR+SdQSDJzc8joaeP8QQoCQr8NuYx2dIIytl1QeBEZHJ9uW6hebsrYgbz8hJwUQao3TWCMtmfV8Nu1twOLAw==", + "license": "MIT", + "dependencies": { + "@jridgewell/resolve-uri": "^3.1.0", + "@jridgewell/sourcemap-codec": "^1.4.14" + } + }, + "node_modules/@rolldown/pluginutils": { + "version": "1.0.0-beta.29", + "resolved": "https://registry.npmjs.org/@rolldown/pluginutils/-/pluginutils-1.0.0-beta.29.tgz", + "integrity": "sha512-NIJgOsMjbxAXvoGq/X0gD7VPMQ8j9g0BiDaNjVNVjvl+iKXxL3Jre0v31RmBYeLEmkbj2s02v8vFTbUXi5XS2Q==", + "dev": true, + "license": "MIT" + }, + "node_modules/@rollup/rollup-android-arm-eabi": { + "version": "4.50.1", + "resolved": "https://registry.npmjs.org/@rollup/rollup-android-arm-eabi/-/rollup-android-arm-eabi-4.50.1.tgz", + "integrity": "sha512-HJXwzoZN4eYTdD8bVV22DN8gsPCAj3V20NHKOs8ezfXanGpmVPR7kalUHd+Y31IJp9stdB87VKPFbsGY3H/2ag==", + "cpu": [ + "arm" + ], + "license": "MIT", + "optional": true, + "os": [ + "android" + ] + }, + "node_modules/@rollup/rollup-android-arm64": { + "version": "4.50.1", + "resolved": "https://registry.npmjs.org/@rollup/rollup-android-arm64/-/rollup-android-arm64-4.50.1.tgz", + "integrity": "sha512-PZlsJVcjHfcH53mOImyt3bc97Ep3FJDXRpk9sMdGX0qgLmY0EIWxCag6EigerGhLVuL8lDVYNnSo8qnTElO4xw==", + "cpu": [ + "arm64" + ], + "license": "MIT", + "optional": true, + "os": [ + "android" + ] + }, + "node_modules/@rollup/rollup-darwin-arm64": { + "version": "4.50.1", + "resolved": "https://registry.npmjs.org/@rollup/rollup-darwin-arm64/-/rollup-darwin-arm64-4.50.1.tgz", + "integrity": "sha512-xc6i2AuWh++oGi4ylOFPmzJOEeAa2lJeGUGb4MudOtgfyyjr4UPNK+eEWTPLvmPJIY/pgw6ssFIox23SyrkkJw==", + "cpu": [ + "arm64" + ], + "license": "MIT", + "optional": true, + "os": [ + "darwin" + ] + }, + "node_modules/@rollup/rollup-darwin-x64": { + "version": "4.50.1", + "resolved": "https://registry.npmjs.org/@rollup/rollup-darwin-x64/-/rollup-darwin-x64-4.50.1.tgz", + "integrity": "sha512-2ofU89lEpDYhdLAbRdeyz/kX3Y2lpYc6ShRnDjY35bZhd2ipuDMDi6ZTQ9NIag94K28nFMofdnKeHR7BT0CATw==", + "cpu": [ + "x64" + ], + "license": "MIT", + "optional": true, + "os": [ + "darwin" + ] + }, + "node_modules/@rollup/rollup-freebsd-arm64": { + "version": "4.50.1", + "resolved": "https://registry.npmjs.org/@rollup/rollup-freebsd-arm64/-/rollup-freebsd-arm64-4.50.1.tgz", + "integrity": "sha512-wOsE6H2u6PxsHY/BeFHA4VGQN3KUJFZp7QJBmDYI983fgxq5Th8FDkVuERb2l9vDMs1D5XhOrhBrnqcEY6l8ZA==", + "cpu": [ + "arm64" + ], + "license": "MIT", + "optional": true, + "os": [ + "freebsd" + ] + }, + "node_modules/@rollup/rollup-freebsd-x64": { + "version": "4.50.1", + "resolved": "https://registry.npmjs.org/@rollup/rollup-freebsd-x64/-/rollup-freebsd-x64-4.50.1.tgz", + "integrity": "sha512-A/xeqaHTlKbQggxCqispFAcNjycpUEHP52mwMQZUNqDUJFFYtPHCXS1VAG29uMlDzIVr+i00tSFWFLivMcoIBQ==", + "cpu": [ + "x64" + ], + "license": "MIT", + "optional": true, + "os": [ + "freebsd" + ] + }, + "node_modules/@rollup/rollup-linux-arm-gnueabihf": { + "version": "4.50.1", + "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-arm-gnueabihf/-/rollup-linux-arm-gnueabihf-4.50.1.tgz", + "integrity": "sha512-54v4okehwl5TaSIkpp97rAHGp7t3ghinRd/vyC1iXqXMfjYUTm7TfYmCzXDoHUPTTf36L8pr0E7YsD3CfB3ZDg==", + "cpu": [ + "arm" + ], + "license": "MIT", + "optional": true, + "os": [ + "linux" + ] + }, + "node_modules/@rollup/rollup-linux-arm-musleabihf": { + "version": "4.50.1", + "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-arm-musleabihf/-/rollup-linux-arm-musleabihf-4.50.1.tgz", + "integrity": "sha512-p/LaFyajPN/0PUHjv8TNyxLiA7RwmDoVY3flXHPSzqrGcIp/c2FjwPPP5++u87DGHtw+5kSH5bCJz0mvXngYxw==", + "cpu": [ + "arm" + ], + "license": "MIT", + "optional": true, + "os": [ + "linux" + ] + }, + "node_modules/@rollup/rollup-linux-arm64-gnu": { + "version": "4.50.1", + "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-arm64-gnu/-/rollup-linux-arm64-gnu-4.50.1.tgz", + "integrity": "sha512-2AbMhFFkTo6Ptna1zO7kAXXDLi7H9fGTbVaIq2AAYO7yzcAsuTNWPHhb2aTA6GPiP+JXh85Y8CiS54iZoj4opw==", + "cpu": [ + "arm64" + ], + "license": "MIT", + "optional": true, + "os": [ + "linux" + ] + }, + "node_modules/@rollup/rollup-linux-arm64-musl": { + "version": "4.50.1", + "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-arm64-musl/-/rollup-linux-arm64-musl-4.50.1.tgz", + "integrity": "sha512-Cgef+5aZwuvesQNw9eX7g19FfKX5/pQRIyhoXLCiBOrWopjo7ycfB292TX9MDcDijiuIJlx1IzJz3IoCPfqs9w==", + "cpu": [ + "arm64" + ], + "license": "MIT", + "optional": true, + "os": [ + "linux" + ] + }, + "node_modules/@rollup/rollup-linux-loongarch64-gnu": { + "version": "4.50.1", + "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-loongarch64-gnu/-/rollup-linux-loongarch64-gnu-4.50.1.tgz", + "integrity": "sha512-RPhTwWMzpYYrHrJAS7CmpdtHNKtt2Ueo+BlLBjfZEhYBhK00OsEqM08/7f+eohiF6poe0YRDDd8nAvwtE/Y62Q==", + "cpu": [ + "loong64" + ], + "license": "MIT", + "optional": true, + "os": [ + "linux" + ] + }, + "node_modules/@rollup/rollup-linux-ppc64-gnu": { + "version": "4.50.1", + "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-ppc64-gnu/-/rollup-linux-ppc64-gnu-4.50.1.tgz", + "integrity": "sha512-eSGMVQw9iekut62O7eBdbiccRguuDgiPMsw++BVUg+1K7WjZXHOg/YOT9SWMzPZA+w98G+Fa1VqJgHZOHHnY0Q==", + "cpu": [ + "ppc64" + ], + "license": "MIT", + "optional": true, + "os": [ + "linux" + ] + }, + "node_modules/@rollup/rollup-linux-riscv64-gnu": { + "version": "4.50.1", + "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-riscv64-gnu/-/rollup-linux-riscv64-gnu-4.50.1.tgz", + "integrity": "sha512-S208ojx8a4ciIPrLgazF6AgdcNJzQE4+S9rsmOmDJkusvctii+ZvEuIC4v/xFqzbuP8yDjn73oBlNDgF6YGSXQ==", + "cpu": [ + "riscv64" + ], + "license": "MIT", + "optional": true, + "os": [ + "linux" + ] + }, + "node_modules/@rollup/rollup-linux-riscv64-musl": { + "version": "4.50.1", + "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-riscv64-musl/-/rollup-linux-riscv64-musl-4.50.1.tgz", + "integrity": "sha512-3Ag8Ls1ggqkGUvSZWYcdgFwriy2lWo+0QlYgEFra/5JGtAd6C5Hw59oojx1DeqcA2Wds2ayRgvJ4qxVTzCHgzg==", + "cpu": [ + "riscv64" + ], + "license": "MIT", + "optional": true, + "os": [ + "linux" + ] + }, + "node_modules/@rollup/rollup-linux-s390x-gnu": { + "version": "4.50.1", + "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-s390x-gnu/-/rollup-linux-s390x-gnu-4.50.1.tgz", + "integrity": "sha512-t9YrKfaxCYe7l7ldFERE1BRg/4TATxIg+YieHQ966jwvo7ddHJxPj9cNFWLAzhkVsbBvNA4qTbPVNsZKBO4NSg==", + "cpu": [ + "s390x" + ], + "license": "MIT", + "optional": true, + "os": [ + "linux" + ] + }, + "node_modules/@rollup/rollup-linux-x64-gnu": { + "version": "4.50.1", + "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-x64-gnu/-/rollup-linux-x64-gnu-4.50.1.tgz", + "integrity": "sha512-MCgtFB2+SVNuQmmjHf+wfI4CMxy3Tk8XjA5Z//A0AKD7QXUYFMQcns91K6dEHBvZPCnhJSyDWLApk40Iq/H3tA==", + "cpu": [ + "x64" + ], + "license": "MIT", + "optional": true, + "os": [ + "linux" + ] + }, + "node_modules/@rollup/rollup-linux-x64-musl": { + "version": "4.50.1", + "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-x64-musl/-/rollup-linux-x64-musl-4.50.1.tgz", + "integrity": "sha512-nEvqG+0jeRmqaUMuwzlfMKwcIVffy/9KGbAGyoa26iu6eSngAYQ512bMXuqqPrlTyfqdlB9FVINs93j534UJrg==", + "cpu": [ + "x64" + ], + "license": "MIT", + "optional": true, + "os": [ + "linux" + ] + }, + "node_modules/@rollup/rollup-openharmony-arm64": { + "version": "4.50.1", + "resolved": "https://registry.npmjs.org/@rollup/rollup-openharmony-arm64/-/rollup-openharmony-arm64-4.50.1.tgz", + "integrity": "sha512-RDsLm+phmT3MJd9SNxA9MNuEAO/J2fhW8GXk62G/B4G7sLVumNFbRwDL6v5NrESb48k+QMqdGbHgEtfU0LCpbA==", + "cpu": [ + "arm64" + ], + "license": "MIT", + "optional": true, + "os": [ + "openharmony" + ] + }, + "node_modules/@rollup/rollup-win32-arm64-msvc": { + "version": "4.50.1", + "resolved": "https://registry.npmjs.org/@rollup/rollup-win32-arm64-msvc/-/rollup-win32-arm64-msvc-4.50.1.tgz", + "integrity": "sha512-hpZB/TImk2FlAFAIsoElM3tLzq57uxnGYwplg6WDyAxbYczSi8O2eQ+H2Lx74504rwKtZ3N2g4bCUkiamzS6TQ==", + "cpu": [ + "arm64" + ], + "license": "MIT", + "optional": true, + "os": [ + "win32" + ] + }, + "node_modules/@rollup/rollup-win32-ia32-msvc": { + "version": "4.50.1", + "resolved": "https://registry.npmjs.org/@rollup/rollup-win32-ia32-msvc/-/rollup-win32-ia32-msvc-4.50.1.tgz", + "integrity": "sha512-SXjv8JlbzKM0fTJidX4eVsH+Wmnp0/WcD8gJxIZyR6Gay5Qcsmdbi9zVtnbkGPG8v2vMR1AD06lGWy5FLMcG7A==", + "cpu": [ + "ia32" + ], + "license": "MIT", + "optional": true, + "os": [ + "win32" + ] + }, + "node_modules/@rollup/rollup-win32-x64-msvc": { + "version": "4.50.1", + "resolved": "https://registry.npmjs.org/@rollup/rollup-win32-x64-msvc/-/rollup-win32-x64-msvc-4.50.1.tgz", + "integrity": "sha512-StxAO/8ts62KZVRAm4JZYq9+NqNsV7RvimNK+YM7ry//zebEH6meuugqW/P5OFUCjyQgui+9fUxT6d5NShvMvA==", + "cpu": [ + "x64" + ], + "license": "MIT", + "optional": true, + "os": [ + "win32" + ] + }, + "node_modules/@tailwindcss/node": { + "version": "4.1.13", + "resolved": "https://registry.npmjs.org/@tailwindcss/node/-/node-4.1.13.tgz", + "integrity": "sha512-eq3ouolC1oEFOAvOMOBAmfCIqZBJuvWvvYWh5h5iOYfe1HFC6+GZ6EIL0JdM3/niGRJmnrOc+8gl9/HGUaaptw==", + "license": "MIT", + "dependencies": { + "@jridgewell/remapping": "^2.3.4", + "enhanced-resolve": "^5.18.3", + "jiti": "^2.5.1", + "lightningcss": "1.30.1", + "magic-string": "^0.30.18", + "source-map-js": "^1.2.1", + "tailwindcss": "4.1.13" + } + }, + "node_modules/@tailwindcss/oxide": { + "version": "4.1.13", + "resolved": "https://registry.npmjs.org/@tailwindcss/oxide/-/oxide-4.1.13.tgz", + "integrity": "sha512-CPgsM1IpGRa880sMbYmG1s4xhAy3xEt1QULgTJGQmZUeNgXFR7s1YxYygmJyBGtou4SyEosGAGEeYqY7R53bIA==", + "hasInstallScript": true, + "license": "MIT", + "dependencies": { + "detect-libc": "^2.0.4", + "tar": "^7.4.3" + }, + "engines": { + "node": ">= 10" + }, + "optionalDependencies": { + "@tailwindcss/oxide-android-arm64": "4.1.13", + "@tailwindcss/oxide-darwin-arm64": "4.1.13", + "@tailwindcss/oxide-darwin-x64": "4.1.13", + "@tailwindcss/oxide-freebsd-x64": "4.1.13", + "@tailwindcss/oxide-linux-arm-gnueabihf": "4.1.13", + "@tailwindcss/oxide-linux-arm64-gnu": "4.1.13", + "@tailwindcss/oxide-linux-arm64-musl": "4.1.13", + "@tailwindcss/oxide-linux-x64-gnu": "4.1.13", + "@tailwindcss/oxide-linux-x64-musl": "4.1.13", + "@tailwindcss/oxide-wasm32-wasi": "4.1.13", + "@tailwindcss/oxide-win32-arm64-msvc": "4.1.13", + "@tailwindcss/oxide-win32-x64-msvc": "4.1.13" + } + }, + "node_modules/@tailwindcss/oxide-android-arm64": { + "version": "4.1.13", + "resolved": "https://registry.npmjs.org/@tailwindcss/oxide-android-arm64/-/oxide-android-arm64-4.1.13.tgz", + "integrity": "sha512-BrpTrVYyejbgGo57yc8ieE+D6VT9GOgnNdmh5Sac6+t0m+v+sKQevpFVpwX3pBrM2qKrQwJ0c5eDbtjouY/+ew==", + "cpu": [ + "arm64" + ], + "license": "MIT", + "optional": true, + "os": [ + "android" + ], + "engines": { + "node": ">= 10" + } + }, + "node_modules/@tailwindcss/oxide-darwin-arm64": { + "version": "4.1.13", + "resolved": "https://registry.npmjs.org/@tailwindcss/oxide-darwin-arm64/-/oxide-darwin-arm64-4.1.13.tgz", + "integrity": "sha512-YP+Jksc4U0KHcu76UhRDHq9bx4qtBftp9ShK/7UGfq0wpaP96YVnnjFnj3ZFrUAjc5iECzODl/Ts0AN7ZPOANQ==", + "cpu": [ + "arm64" + ], + "license": "MIT", + "optional": true, + "os": [ + "darwin" + ], + "engines": { + "node": ">= 10" + } + }, + "node_modules/@tailwindcss/oxide-darwin-x64": { + "version": "4.1.13", + "resolved": "https://registry.npmjs.org/@tailwindcss/oxide-darwin-x64/-/oxide-darwin-x64-4.1.13.tgz", + "integrity": "sha512-aAJ3bbwrn/PQHDxCto9sxwQfT30PzyYJFG0u/BWZGeVXi5Hx6uuUOQEI2Fa43qvmUjTRQNZnGqe9t0Zntexeuw==", + "cpu": [ + "x64" + ], + "license": "MIT", + "optional": true, + "os": [ + "darwin" + ], + "engines": { + "node": ">= 10" + } + }, + "node_modules/@tailwindcss/oxide-freebsd-x64": { + "version": "4.1.13", + "resolved": "https://registry.npmjs.org/@tailwindcss/oxide-freebsd-x64/-/oxide-freebsd-x64-4.1.13.tgz", + "integrity": "sha512-Wt8KvASHwSXhKE/dJLCCWcTSVmBj3xhVhp/aF3RpAhGeZ3sVo7+NTfgiN8Vey/Fi8prRClDs6/f0KXPDTZE6nQ==", + "cpu": [ + "x64" + ], + "license": "MIT", + "optional": true, + "os": [ + "freebsd" + ], + "engines": { + "node": ">= 10" + } + }, + "node_modules/@tailwindcss/oxide-linux-arm-gnueabihf": { + "version": "4.1.13", + "resolved": "https://registry.npmjs.org/@tailwindcss/oxide-linux-arm-gnueabihf/-/oxide-linux-arm-gnueabihf-4.1.13.tgz", + "integrity": "sha512-mbVbcAsW3Gkm2MGwA93eLtWrwajz91aXZCNSkGTx/R5eb6KpKD5q8Ueckkh9YNboU8RH7jiv+ol/I7ZyQ9H7Bw==", + "cpu": [ + "arm" + ], + "license": "MIT", + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": ">= 10" + } + }, + "node_modules/@tailwindcss/oxide-linux-arm64-gnu": { + "version": "4.1.13", + "resolved": "https://registry.npmjs.org/@tailwindcss/oxide-linux-arm64-gnu/-/oxide-linux-arm64-gnu-4.1.13.tgz", + "integrity": "sha512-wdtfkmpXiwej/yoAkrCP2DNzRXCALq9NVLgLELgLim1QpSfhQM5+ZxQQF8fkOiEpuNoKLp4nKZ6RC4kmeFH0HQ==", + "cpu": [ + "arm64" + ], + "license": "MIT", + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": ">= 10" + } + }, + "node_modules/@tailwindcss/oxide-linux-arm64-musl": { + "version": "4.1.13", + "resolved": "https://registry.npmjs.org/@tailwindcss/oxide-linux-arm64-musl/-/oxide-linux-arm64-musl-4.1.13.tgz", + "integrity": "sha512-hZQrmtLdhyqzXHB7mkXfq0IYbxegaqTmfa1p9MBj72WPoDD3oNOh1Lnxf6xZLY9C3OV6qiCYkO1i/LrzEdW2mg==", + "cpu": [ + "arm64" + ], + "license": "MIT", + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": ">= 10" + } + }, + "node_modules/@tailwindcss/oxide-linux-x64-gnu": { + "version": "4.1.13", + "resolved": "https://registry.npmjs.org/@tailwindcss/oxide-linux-x64-gnu/-/oxide-linux-x64-gnu-4.1.13.tgz", + "integrity": "sha512-uaZTYWxSXyMWDJZNY1Ul7XkJTCBRFZ5Fo6wtjrgBKzZLoJNrG+WderJwAjPzuNZOnmdrVg260DKwXCFtJ/hWRQ==", + "cpu": [ + "x64" + ], + "license": "MIT", + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": ">= 10" + } + }, + "node_modules/@tailwindcss/oxide-linux-x64-musl": { + "version": "4.1.13", + "resolved": "https://registry.npmjs.org/@tailwindcss/oxide-linux-x64-musl/-/oxide-linux-x64-musl-4.1.13.tgz", + "integrity": "sha512-oXiPj5mi4Hdn50v5RdnuuIms0PVPI/EG4fxAfFiIKQh5TgQgX7oSuDWntHW7WNIi/yVLAiS+CRGW4RkoGSSgVQ==", + "cpu": [ + "x64" + ], + "license": "MIT", + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": ">= 10" + } + }, + "node_modules/@tailwindcss/oxide-wasm32-wasi": { + "version": "4.1.13", + "resolved": "https://registry.npmjs.org/@tailwindcss/oxide-wasm32-wasi/-/oxide-wasm32-wasi-4.1.13.tgz", + "integrity": "sha512-+LC2nNtPovtrDwBc/nqnIKYh/W2+R69FA0hgoeOn64BdCX522u19ryLh3Vf3F8W49XBcMIxSe665kwy21FkhvA==", + "bundleDependencies": [ + "@napi-rs/wasm-runtime", + "@emnapi/core", + "@emnapi/runtime", + "@tybys/wasm-util", + "@emnapi/wasi-threads", + "tslib" + ], + "cpu": [ + "wasm32" + ], + "license": "MIT", + "optional": true, + "dependencies": { + "@emnapi/core": "^1.4.5", + "@emnapi/runtime": "^1.4.5", + "@emnapi/wasi-threads": "^1.0.4", + "@napi-rs/wasm-runtime": "^0.2.12", + "@tybys/wasm-util": "^0.10.0", + "tslib": "^2.8.0" + }, + "engines": { + "node": ">=14.0.0" + } + }, + "node_modules/@tailwindcss/oxide-win32-arm64-msvc": { + "version": "4.1.13", + "resolved": "https://registry.npmjs.org/@tailwindcss/oxide-win32-arm64-msvc/-/oxide-win32-arm64-msvc-4.1.13.tgz", + "integrity": "sha512-dziTNeQXtoQ2KBXmrjCxsuPk3F3CQ/yb7ZNZNA+UkNTeiTGgfeh+gH5Pi7mRncVgcPD2xgHvkFCh/MhZWSgyQg==", + "cpu": [ + "arm64" + ], + "license": "MIT", + "optional": true, + "os": [ + "win32" + ], + "engines": { + "node": ">= 10" + } + }, + "node_modules/@tailwindcss/oxide-win32-x64-msvc": { + "version": "4.1.13", + "resolved": "https://registry.npmjs.org/@tailwindcss/oxide-win32-x64-msvc/-/oxide-win32-x64-msvc-4.1.13.tgz", + "integrity": "sha512-3+LKesjXydTkHk5zXX01b5KMzLV1xl2mcktBJkje7rhFUpUlYJy7IMOLqjIRQncLTa1WZZiFY/foAeB5nmaiTw==", + "cpu": [ + "x64" + ], + "license": "MIT", + "optional": true, + "os": [ + "win32" + ], + "engines": { + "node": ">= 10" + } + }, + "node_modules/@tailwindcss/vite": { + "version": "4.1.13", + "resolved": "https://registry.npmjs.org/@tailwindcss/vite/-/vite-4.1.13.tgz", + "integrity": "sha512-0PmqLQ010N58SbMTJ7BVJ4I2xopiQn/5i6nlb4JmxzQf8zcS5+m2Cv6tqh+sfDwtIdjoEnOvwsGQ1hkUi8QEHQ==", + "license": "MIT", + "dependencies": { + "@tailwindcss/node": "4.1.13", + "@tailwindcss/oxide": "4.1.13", + "tailwindcss": "4.1.13" + }, + "peerDependencies": { + "vite": "^5.2.0 || ^6 || ^7" + } + }, + "node_modules/@types/estree": { + "version": "1.0.8", + "resolved": "https://registry.npmjs.org/@types/estree/-/estree-1.0.8.tgz", + "integrity": "sha512-dWHzHa2WqEXI/O1E9OjrocMTKJl2mSrEolh1Iomrv6U+JuNwaHXsXx9bLu5gG7BUWFIN0skIQJQ/L1rIex4X6w==", + "license": "MIT" + }, + "node_modules/@vitejs/plugin-vue": { + "version": "6.0.1", + "resolved": "https://registry.npmjs.org/@vitejs/plugin-vue/-/plugin-vue-6.0.1.tgz", + "integrity": "sha512-+MaE752hU0wfPFJEUAIxqw18+20euHHdxVtMvbFcOEpjEyfqXH/5DCoTHiVJ0J29EhTJdoTkjEv5YBKU9dnoTw==", + "dev": true, + "license": "MIT", + "dependencies": { + "@rolldown/pluginutils": "1.0.0-beta.29" + }, + "engines": { + "node": "^20.19.0 || >=22.12.0" + }, + "peerDependencies": { + "vite": "^5.0.0 || ^6.0.0 || ^7.0.0", + "vue": "^3.2.25" + } + }, + "node_modules/@volar/language-core": { + "version": "2.4.23", + "resolved": "https://registry.npmjs.org/@volar/language-core/-/language-core-2.4.23.tgz", + "integrity": "sha512-hEEd5ET/oSmBC6pi1j6NaNYRWoAiDhINbT8rmwtINugR39loROSlufGdYMF9TaKGfz+ViGs1Idi3mAhnuPcoGQ==", + "dev": true, + "license": "MIT", + "dependencies": { + "@volar/source-map": "2.4.23" + } + }, + "node_modules/@volar/source-map": { + "version": "2.4.23", + "resolved": "https://registry.npmjs.org/@volar/source-map/-/source-map-2.4.23.tgz", + "integrity": "sha512-Z1Uc8IB57Lm6k7q6KIDu/p+JWtf3xsXJqAX/5r18hYOTpJyBn0KXUR8oTJ4WFYOcDzWC9n3IflGgHowx6U6z9Q==", + "dev": true, + "license": "MIT" + }, + "node_modules/@volar/typescript": { + "version": "2.4.23", + "resolved": "https://registry.npmjs.org/@volar/typescript/-/typescript-2.4.23.tgz", + "integrity": "sha512-lAB5zJghWxVPqfcStmAP1ZqQacMpe90UrP5RJ3arDyrhy4aCUQqmxPPLB2PWDKugvylmO41ljK7vZ+t6INMTag==", + "dev": true, + "license": "MIT", + "dependencies": { + "@volar/language-core": "2.4.23", + "path-browserify": "^1.0.1", + "vscode-uri": "^3.0.8" + } + }, + "node_modules/@vue/compiler-core": { + "version": "3.5.21", + "resolved": "https://registry.npmjs.org/@vue/compiler-core/-/compiler-core-3.5.21.tgz", + "integrity": "sha512-8i+LZ0vf6ZgII5Z9XmUvrCyEzocvWT+TeR2VBUVlzIH6Tyv57E20mPZ1bCS+tbejgUgmjrEh7q/0F0bibskAmw==", + "license": "MIT", + "dependencies": { + "@babel/parser": "^7.28.3", + "@vue/shared": "3.5.21", + "entities": "^4.5.0", + "estree-walker": "^2.0.2", + "source-map-js": "^1.2.1" + } + }, + "node_modules/@vue/compiler-dom": { + "version": "3.5.21", + "resolved": "https://registry.npmjs.org/@vue/compiler-dom/-/compiler-dom-3.5.21.tgz", + "integrity": "sha512-jNtbu/u97wiyEBJlJ9kmdw7tAr5Vy0Aj5CgQmo+6pxWNQhXZDPsRr1UWPN4v3Zf82s2H3kF51IbzZ4jMWAgPlQ==", + "license": "MIT", + "dependencies": { + "@vue/compiler-core": "3.5.21", + "@vue/shared": "3.5.21" + } + }, + "node_modules/@vue/compiler-sfc": { + "version": "3.5.21", + "resolved": "https://registry.npmjs.org/@vue/compiler-sfc/-/compiler-sfc-3.5.21.tgz", + "integrity": "sha512-SXlyk6I5eUGBd2v8Ie7tF6ADHE9kCR6mBEuPyH1nUZ0h6Xx6nZI29i12sJKQmzbDyr2tUHMhhTt51Z6blbkTTQ==", + "license": "MIT", + "dependencies": { + "@babel/parser": "^7.28.3", + "@vue/compiler-core": "3.5.21", + "@vue/compiler-dom": "3.5.21", + "@vue/compiler-ssr": "3.5.21", + "@vue/shared": "3.5.21", + "estree-walker": "^2.0.2", + "magic-string": "^0.30.18", + "postcss": "^8.5.6", + "source-map-js": "^1.2.1" + } + }, + "node_modules/@vue/compiler-ssr": { + "version": "3.5.21", + "resolved": "https://registry.npmjs.org/@vue/compiler-ssr/-/compiler-ssr-3.5.21.tgz", + "integrity": "sha512-vKQ5olH5edFZdf5ZrlEgSO1j1DMA4u23TVK5XR1uMhvwnYvVdDF0nHXJUblL/GvzlShQbjhZZ2uvYmDlAbgo9w==", + "license": "MIT", + "dependencies": { + "@vue/compiler-dom": "3.5.21", + "@vue/shared": "3.5.21" + } + }, + "node_modules/@vue/compiler-vue2": { + "version": "2.7.16", + "resolved": "https://registry.npmjs.org/@vue/compiler-vue2/-/compiler-vue2-2.7.16.tgz", + "integrity": "sha512-qYC3Psj9S/mfu9uVi5WvNZIzq+xnXMhOwbTFKKDD7b1lhpnn71jXSFdTQ+WsIEk0ONCd7VV2IMm7ONl6tbQ86A==", + "dev": true, + "license": "MIT", + "dependencies": { + "de-indent": "^1.0.2", + "he": "^1.2.0" + } + }, + "node_modules/@vue/devtools-api": { + "version": "6.6.4", + "resolved": "https://registry.npmjs.org/@vue/devtools-api/-/devtools-api-6.6.4.tgz", + "integrity": "sha512-sGhTPMuXqZ1rVOk32RylztWkfXTRhuS7vgAKv0zjqk8gbsHkJ7xfFf+jbySxt7tWObEJwyKaHMikV/WGDiQm8g==", + "license": "MIT" + }, + "node_modules/@vue/language-core": { + "version": "3.0.7", + "resolved": "https://registry.npmjs.org/@vue/language-core/-/language-core-3.0.7.tgz", + "integrity": "sha512-0sqqyqJ0Gn33JH3TdIsZLCZZ8Gr4kwlg8iYOnOrDDkJKSjFurlQY/bEFQx5zs7SX2C/bjMkmPYq/NiyY1fTOkw==", + "dev": true, + "license": "MIT", + "dependencies": { + "@volar/language-core": "2.4.23", + "@vue/compiler-dom": "^3.5.0", + "@vue/compiler-vue2": "^2.7.16", + "@vue/shared": "^3.5.0", + "alien-signals": "^2.0.5", + "muggle-string": "^0.4.1", + "path-browserify": "^1.0.1", + "picomatch": "^4.0.2" + }, + "peerDependencies": { + "typescript": "*" + }, + "peerDependenciesMeta": { + "typescript": { + "optional": true + } + } + }, + "node_modules/@vue/reactivity": { + "version": "3.5.21", + "resolved": "https://registry.npmjs.org/@vue/reactivity/-/reactivity-3.5.21.tgz", + "integrity": "sha512-3ah7sa+Cwr9iiYEERt9JfZKPw4A2UlbY8RbbnH2mGCE8NwHkhmlZt2VsH0oDA3P08X3jJd29ohBDtX+TbD9AsA==", + "license": "MIT", + "dependencies": { + "@vue/shared": "3.5.21" + } + }, + "node_modules/@vue/runtime-core": { + "version": "3.5.21", + "resolved": "https://registry.npmjs.org/@vue/runtime-core/-/runtime-core-3.5.21.tgz", + "integrity": "sha512-+DplQlRS4MXfIf9gfD1BOJpk5RSyGgGXD/R+cumhe8jdjUcq/qlxDawQlSI8hCKupBlvM+3eS1se5xW+SuNAwA==", + "license": "MIT", + "dependencies": { + "@vue/reactivity": "3.5.21", + "@vue/shared": "3.5.21" + } + }, + "node_modules/@vue/runtime-dom": { + "version": "3.5.21", + "resolved": "https://registry.npmjs.org/@vue/runtime-dom/-/runtime-dom-3.5.21.tgz", + "integrity": "sha512-3M2DZsOFwM5qI15wrMmNF5RJe1+ARijt2HM3TbzBbPSuBHOQpoidE+Pa+XEaVN+czbHf81ETRoG1ltztP2em8w==", + "license": "MIT", + "dependencies": { + "@vue/reactivity": "3.5.21", + "@vue/runtime-core": "3.5.21", + "@vue/shared": "3.5.21", + "csstype": "^3.1.3" + } + }, + "node_modules/@vue/server-renderer": { + "version": "3.5.21", + "resolved": "https://registry.npmjs.org/@vue/server-renderer/-/server-renderer-3.5.21.tgz", + "integrity": "sha512-qr8AqgD3DJPJcGvLcJKQo2tAc8OnXRcfxhOJCPF+fcfn5bBGz7VCcO7t+qETOPxpWK1mgysXvVT/j+xWaHeMWA==", + "license": "MIT", + "dependencies": { + "@vue/compiler-ssr": "3.5.21", + "@vue/shared": "3.5.21" + }, + "peerDependencies": { + "vue": "3.5.21" + } + }, + "node_modules/@vue/shared": { + "version": "3.5.21", + "resolved": "https://registry.npmjs.org/@vue/shared/-/shared-3.5.21.tgz", + "integrity": "sha512-+2k1EQpnYuVuu3N7atWyG3/xoFWIVJZq4Mz8XNOdScFI0etES75fbny/oU4lKWk/577P1zmg0ioYvpGEDZ3DLw==", + "license": "MIT" + }, + "node_modules/@vue/tsconfig": { + "version": "0.7.0", + "resolved": "https://registry.npmjs.org/@vue/tsconfig/-/tsconfig-0.7.0.tgz", + "integrity": "sha512-ku2uNz5MaZ9IerPPUyOHzyjhXoX2kVJaVf7hL315DC17vS6IiZRmmCPfggNbU16QTvM80+uYYy3eYJB59WCtvg==", + "dev": true, + "license": "MIT", + "peerDependencies": { + "typescript": "5.x", + "vue": "^3.4.0" + }, + "peerDependenciesMeta": { + "typescript": { + "optional": true + }, + "vue": { + "optional": true + } + } + }, + "node_modules/alien-signals": { + "version": "2.0.7", + "resolved": "https://registry.npmjs.org/alien-signals/-/alien-signals-2.0.7.tgz", + "integrity": "sha512-wE7y3jmYeb0+h6mr5BOovuqhFv22O/MV9j5p0ndJsa7z1zJNPGQ4ph5pQk/kTTCWRC3xsA4SmtwmkzQO+7NCNg==", + "dev": true, + "license": "MIT" + }, + "node_modules/chownr": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/chownr/-/chownr-3.0.0.tgz", + "integrity": "sha512-+IxzY9BZOQd/XuYPRmrvEVjF/nqj5kgT4kEq7VofrDoM1MxoRjEWkrCC3EtLi59TVawxTAn+orJwFQcrqEN1+g==", + "license": "BlueOak-1.0.0", + "engines": { + "node": ">=18" + } + }, + "node_modules/csstype": { + "version": "3.1.3", + "resolved": "https://registry.npmjs.org/csstype/-/csstype-3.1.3.tgz", + "integrity": "sha512-M1uQkMl8rQK/szD0LNhtqxIPLpimGm8sOBwU7lLnCpSbTyY3yeU1Vc7l4KT5zT4s/yOxHH5O7tIuuLOCnLADRw==", + "license": "MIT" + }, + "node_modules/de-indent": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/de-indent/-/de-indent-1.0.2.tgz", + "integrity": "sha512-e/1zu3xH5MQryN2zdVaF0OrdNLUbvWxzMbi+iNA6Bky7l1RoP8a2fIbRocyHclXt/arDrrR6lL3TqFD9pMQTsg==", + "dev": true, + "license": "MIT" + }, + "node_modules/detect-libc": { + "version": "2.0.4", + "resolved": "https://registry.npmjs.org/detect-libc/-/detect-libc-2.0.4.tgz", + "integrity": "sha512-3UDv+G9CsCKO1WKMGw9fwq/SWJYbI0c5Y7LU1AXYoDdbhE2AHQ6N6Nb34sG8Fj7T5APy8qXDCKuuIHd1BR0tVA==", + "license": "Apache-2.0", + "engines": { + "node": ">=8" + } + }, + "node_modules/enhanced-resolve": { + "version": "5.18.3", + "resolved": "https://registry.npmjs.org/enhanced-resolve/-/enhanced-resolve-5.18.3.tgz", + "integrity": "sha512-d4lC8xfavMeBjzGr2vECC3fsGXziXZQyJxD868h2M/mBI3PwAuODxAkLkq5HYuvrPYcUtiLzsTo8U3PgX3Ocww==", + "license": "MIT", + "dependencies": { + "graceful-fs": "^4.2.4", + "tapable": "^2.2.0" + }, + "engines": { + "node": ">=10.13.0" + } + }, + "node_modules/entities": { + "version": "4.5.0", + "resolved": "https://registry.npmjs.org/entities/-/entities-4.5.0.tgz", + "integrity": "sha512-V0hjH4dGPh9Ao5p0MoRY6BVqtwCjhz6vI5LT8AJ55H+4g9/4vbHx1I54fS0XuclLhDHArPQCiMjDxjaL8fPxhw==", + "license": "BSD-2-Clause", + "engines": { + "node": ">=0.12" + }, + "funding": { + "url": "https://github.com/fb55/entities?sponsor=1" + } + }, + "node_modules/esbuild": { + "version": "0.25.9", + "resolved": "https://registry.npmjs.org/esbuild/-/esbuild-0.25.9.tgz", + "integrity": "sha512-CRbODhYyQx3qp7ZEwzxOk4JBqmD/seJrzPa/cGjY1VtIn5E09Oi9/dB4JwctnfZ8Q8iT7rioVv5k/FNT/uf54g==", + "hasInstallScript": true, + "license": "MIT", + "bin": { + "esbuild": "bin/esbuild" + }, + "engines": { + "node": ">=18" + }, + "optionalDependencies": { + "@esbuild/aix-ppc64": "0.25.9", + "@esbuild/android-arm": "0.25.9", + "@esbuild/android-arm64": "0.25.9", + "@esbuild/android-x64": "0.25.9", + "@esbuild/darwin-arm64": "0.25.9", + "@esbuild/darwin-x64": "0.25.9", + "@esbuild/freebsd-arm64": "0.25.9", + "@esbuild/freebsd-x64": "0.25.9", + "@esbuild/linux-arm": "0.25.9", + "@esbuild/linux-arm64": "0.25.9", + "@esbuild/linux-ia32": "0.25.9", + "@esbuild/linux-loong64": "0.25.9", + "@esbuild/linux-mips64el": "0.25.9", + "@esbuild/linux-ppc64": "0.25.9", + "@esbuild/linux-riscv64": "0.25.9", + "@esbuild/linux-s390x": "0.25.9", + "@esbuild/linux-x64": "0.25.9", + "@esbuild/netbsd-arm64": "0.25.9", + "@esbuild/netbsd-x64": "0.25.9", + "@esbuild/openbsd-arm64": "0.25.9", + "@esbuild/openbsd-x64": "0.25.9", + "@esbuild/openharmony-arm64": "0.25.9", + "@esbuild/sunos-x64": "0.25.9", + "@esbuild/win32-arm64": "0.25.9", + "@esbuild/win32-ia32": "0.25.9", + "@esbuild/win32-x64": "0.25.9" + } + }, + "node_modules/estree-walker": { + "version": "2.0.2", + "resolved": "https://registry.npmjs.org/estree-walker/-/estree-walker-2.0.2.tgz", + "integrity": "sha512-Rfkk/Mp/DL7JVje3u18FxFujQlTNR2q6QfMSMB7AvCBx91NGj/ba3kCfza0f6dVDbw7YlRf/nDrn7pQrCCyQ/w==", + "license": "MIT" + }, + "node_modules/fdir": { + "version": "6.5.0", + "resolved": "https://registry.npmjs.org/fdir/-/fdir-6.5.0.tgz", + "integrity": "sha512-tIbYtZbucOs0BRGqPJkshJUYdL+SDH7dVM8gjy+ERp3WAUjLEFJE+02kanyHtwjWOnwrKYBiwAmM0p4kLJAnXg==", + "license": "MIT", + "engines": { + "node": ">=12.0.0" + }, + "peerDependencies": { + "picomatch": "^3 || ^4" + }, + "peerDependenciesMeta": { + "picomatch": { + "optional": true + } + } + }, + "node_modules/fsevents": { + "version": "2.3.3", + "resolved": "https://registry.npmjs.org/fsevents/-/fsevents-2.3.3.tgz", + "integrity": "sha512-5xoDfX+fL7faATnagmWPpbFtwh/R77WmMMqqHGS65C3vvB0YHrgF+B1YmZ3441tMj5n63k0212XNoJwzlhffQw==", + "hasInstallScript": true, + "license": "MIT", + "optional": true, + "os": [ + "darwin" + ], + "engines": { + "node": "^8.16.0 || ^10.6.0 || >=11.0.0" + } + }, + "node_modules/graceful-fs": { + "version": "4.2.11", + "resolved": "https://registry.npmjs.org/graceful-fs/-/graceful-fs-4.2.11.tgz", + "integrity": "sha512-RbJ5/jmFcNNCcDV5o9eTnBLJ/HszWV0P73bc+Ff4nS/rJj+YaS6IGyiOL0VoBYX+l1Wrl3k63h/KrH+nhJ0XvQ==", + "license": "ISC" + }, + "node_modules/he": { + "version": "1.2.0", + "resolved": "https://registry.npmjs.org/he/-/he-1.2.0.tgz", + "integrity": "sha512-F/1DnUGPopORZi0ni+CvrCgHQ5FyEAHRLSApuYWMmrbSwoN2Mn/7k+Gl38gJnR7yyDZk6WLXwiGod1JOWNDKGw==", + "dev": true, + "license": "MIT", + "bin": { + "he": "bin/he" + } + }, + "node_modules/jiti": { + "version": "2.5.1", + "resolved": "https://registry.npmjs.org/jiti/-/jiti-2.5.1.tgz", + "integrity": "sha512-twQoecYPiVA5K/h6SxtORw/Bs3ar+mLUtoPSc7iMXzQzK8d7eJ/R09wmTwAjiamETn1cXYPGfNnu7DMoHgu12w==", + "license": "MIT", + "bin": { + "jiti": "lib/jiti-cli.mjs" + } + }, + "node_modules/lightningcss": { + "version": "1.30.1", + "resolved": "https://registry.npmjs.org/lightningcss/-/lightningcss-1.30.1.tgz", + "integrity": "sha512-xi6IyHML+c9+Q3W0S4fCQJOym42pyurFiJUHEcEyHS0CeKzia4yZDEsLlqOFykxOdHpNy0NmvVO31vcSqAxJCg==", + "license": "MPL-2.0", + "dependencies": { + "detect-libc": "^2.0.3" + }, + "engines": { + "node": ">= 12.0.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/parcel" + }, + "optionalDependencies": { + "lightningcss-darwin-arm64": "1.30.1", + "lightningcss-darwin-x64": "1.30.1", + "lightningcss-freebsd-x64": "1.30.1", + "lightningcss-linux-arm-gnueabihf": "1.30.1", + "lightningcss-linux-arm64-gnu": "1.30.1", + "lightningcss-linux-arm64-musl": "1.30.1", + "lightningcss-linux-x64-gnu": "1.30.1", + "lightningcss-linux-x64-musl": "1.30.1", + "lightningcss-win32-arm64-msvc": "1.30.1", + "lightningcss-win32-x64-msvc": "1.30.1" + } + }, + "node_modules/lightningcss-darwin-arm64": { + "version": "1.30.1", + "resolved": "https://registry.npmjs.org/lightningcss-darwin-arm64/-/lightningcss-darwin-arm64-1.30.1.tgz", + "integrity": "sha512-c8JK7hyE65X1MHMN+Viq9n11RRC7hgin3HhYKhrMyaXflk5GVplZ60IxyoVtzILeKr+xAJwg6zK6sjTBJ0FKYQ==", + "cpu": [ + "arm64" + ], + "license": "MPL-2.0", + "optional": true, + "os": [ + "darwin" + ], + "engines": { + "node": ">= 12.0.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/parcel" + } + }, + "node_modules/lightningcss-darwin-x64": { + "version": "1.30.1", + "resolved": "https://registry.npmjs.org/lightningcss-darwin-x64/-/lightningcss-darwin-x64-1.30.1.tgz", + "integrity": "sha512-k1EvjakfumAQoTfcXUcHQZhSpLlkAuEkdMBsI/ivWw9hL+7FtilQc0Cy3hrx0AAQrVtQAbMI7YjCgYgvn37PzA==", + "cpu": [ + "x64" + ], + "license": "MPL-2.0", + "optional": true, + "os": [ + "darwin" + ], + "engines": { + "node": ">= 12.0.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/parcel" + } + }, + "node_modules/lightningcss-freebsd-x64": { + "version": "1.30.1", + "resolved": "https://registry.npmjs.org/lightningcss-freebsd-x64/-/lightningcss-freebsd-x64-1.30.1.tgz", + "integrity": "sha512-kmW6UGCGg2PcyUE59K5r0kWfKPAVy4SltVeut+umLCFoJ53RdCUWxcRDzO1eTaxf/7Q2H7LTquFHPL5R+Gjyig==", + "cpu": [ + "x64" + ], + "license": "MPL-2.0", + "optional": true, + "os": [ + "freebsd" + ], + "engines": { + "node": ">= 12.0.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/parcel" + } + }, + "node_modules/lightningcss-linux-arm-gnueabihf": { + "version": "1.30.1", + "resolved": "https://registry.npmjs.org/lightningcss-linux-arm-gnueabihf/-/lightningcss-linux-arm-gnueabihf-1.30.1.tgz", + "integrity": "sha512-MjxUShl1v8pit+6D/zSPq9S9dQ2NPFSQwGvxBCYaBYLPlCWuPh9/t1MRS8iUaR8i+a6w7aps+B4N0S1TYP/R+Q==", + "cpu": [ + "arm" + ], + "license": "MPL-2.0", + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": ">= 12.0.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/parcel" + } + }, + "node_modules/lightningcss-linux-arm64-gnu": { + "version": "1.30.1", + "resolved": "https://registry.npmjs.org/lightningcss-linux-arm64-gnu/-/lightningcss-linux-arm64-gnu-1.30.1.tgz", + "integrity": "sha512-gB72maP8rmrKsnKYy8XUuXi/4OctJiuQjcuqWNlJQ6jZiWqtPvqFziskH3hnajfvKB27ynbVCucKSm2rkQp4Bw==", + "cpu": [ + "arm64" + ], + "license": "MPL-2.0", + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": ">= 12.0.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/parcel" + } + }, + "node_modules/lightningcss-linux-arm64-musl": { + "version": "1.30.1", + "resolved": "https://registry.npmjs.org/lightningcss-linux-arm64-musl/-/lightningcss-linux-arm64-musl-1.30.1.tgz", + "integrity": "sha512-jmUQVx4331m6LIX+0wUhBbmMX7TCfjF5FoOH6SD1CttzuYlGNVpA7QnrmLxrsub43ClTINfGSYyHe2HWeLl5CQ==", + "cpu": [ + "arm64" + ], + "license": "MPL-2.0", + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": ">= 12.0.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/parcel" + } + }, + "node_modules/lightningcss-linux-x64-gnu": { + "version": "1.30.1", + "resolved": "https://registry.npmjs.org/lightningcss-linux-x64-gnu/-/lightningcss-linux-x64-gnu-1.30.1.tgz", + "integrity": "sha512-piWx3z4wN8J8z3+O5kO74+yr6ze/dKmPnI7vLqfSqI8bccaTGY5xiSGVIJBDd5K5BHlvVLpUB3S2YCfelyJ1bw==", + "cpu": [ + "x64" + ], + "license": "MPL-2.0", + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": ">= 12.0.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/parcel" + } + }, + "node_modules/lightningcss-linux-x64-musl": { + "version": "1.30.1", + "resolved": "https://registry.npmjs.org/lightningcss-linux-x64-musl/-/lightningcss-linux-x64-musl-1.30.1.tgz", + "integrity": "sha512-rRomAK7eIkL+tHY0YPxbc5Dra2gXlI63HL+v1Pdi1a3sC+tJTcFrHX+E86sulgAXeI7rSzDYhPSeHHjqFhqfeQ==", + "cpu": [ + "x64" + ], + "license": "MPL-2.0", + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": ">= 12.0.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/parcel" + } + }, + "node_modules/lightningcss-win32-arm64-msvc": { + "version": "1.30.1", + "resolved": "https://registry.npmjs.org/lightningcss-win32-arm64-msvc/-/lightningcss-win32-arm64-msvc-1.30.1.tgz", + "integrity": "sha512-mSL4rqPi4iXq5YVqzSsJgMVFENoa4nGTT/GjO2c0Yl9OuQfPsIfncvLrEW6RbbB24WtZ3xP/2CCmI3tNkNV4oA==", + "cpu": [ + "arm64" + ], + "license": "MPL-2.0", + "optional": true, + "os": [ + "win32" + ], + "engines": { + "node": ">= 12.0.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/parcel" + } + }, + "node_modules/lightningcss-win32-x64-msvc": { + "version": "1.30.1", + "resolved": "https://registry.npmjs.org/lightningcss-win32-x64-msvc/-/lightningcss-win32-x64-msvc-1.30.1.tgz", + "integrity": "sha512-PVqXh48wh4T53F/1CCu8PIPCxLzWyCnn/9T5W1Jpmdy5h9Cwd+0YQS6/LwhHXSafuc61/xg9Lv5OrCby6a++jg==", + "cpu": [ + "x64" + ], + "license": "MPL-2.0", + "optional": true, + "os": [ + "win32" + ], + "engines": { + "node": ">= 12.0.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/parcel" + } + }, + "node_modules/magic-string": { + "version": "0.30.19", + "resolved": "https://registry.npmjs.org/magic-string/-/magic-string-0.30.19.tgz", + "integrity": "sha512-2N21sPY9Ws53PZvsEpVtNuSW+ScYbQdp4b9qUaL+9QkHUrGFKo56Lg9Emg5s9V/qrtNBmiR01sYhUOwu3H+VOw==", + "license": "MIT", + "dependencies": { + "@jridgewell/sourcemap-codec": "^1.5.5" + } + }, + "node_modules/minipass": { + "version": "7.1.2", + "resolved": "https://registry.npmjs.org/minipass/-/minipass-7.1.2.tgz", + "integrity": "sha512-qOOzS1cBTWYF4BH8fVePDBOO9iptMnGUEZwNc/cMWnTV2nVLZ7VoNWEPHkYczZA0pdoA7dl6e7FL659nX9S2aw==", + "license": "ISC", + "engines": { + "node": ">=16 || 14 >=14.17" + } + }, + "node_modules/minizlib": { + "version": "3.0.2", + "resolved": "https://registry.npmjs.org/minizlib/-/minizlib-3.0.2.tgz", + "integrity": "sha512-oG62iEk+CYt5Xj2YqI5Xi9xWUeZhDI8jjQmC5oThVH5JGCTgIjr7ciJDzC7MBzYd//WvR1OTmP5Q38Q8ShQtVA==", + "license": "MIT", + "dependencies": { + "minipass": "^7.1.2" + }, + "engines": { + "node": ">= 18" + } + }, + "node_modules/mkdirp": { + "version": "3.0.1", + "resolved": "https://registry.npmjs.org/mkdirp/-/mkdirp-3.0.1.tgz", + "integrity": "sha512-+NsyUUAZDmo6YVHzL/stxSu3t9YS1iljliy3BSDrXJ/dkn1KYdmtZODGGjLcc9XLgVVpH4KshHB8XmZgMhaBXg==", + "license": "MIT", + "bin": { + "mkdirp": "dist/cjs/src/bin.js" + }, + "engines": { + "node": ">=10" + }, + "funding": { + "url": "https://github.com/sponsors/isaacs" + } + }, + "node_modules/muggle-string": { + "version": "0.4.1", + "resolved": "https://registry.npmjs.org/muggle-string/-/muggle-string-0.4.1.tgz", + "integrity": "sha512-VNTrAak/KhO2i8dqqnqnAHOa3cYBwXEZe9h+D5h/1ZqFSTEFHdM65lR7RoIqq3tBBYavsOXV84NoHXZ0AkPyqQ==", + "dev": true, + "license": "MIT" + }, + "node_modules/nanoid": { + "version": "3.3.11", + "resolved": "https://registry.npmjs.org/nanoid/-/nanoid-3.3.11.tgz", + "integrity": "sha512-N8SpfPUnUp1bK+PMYW8qSWdl9U+wwNWI4QKxOYDy9JAro3WMX7p2OeVRF9v+347pnakNevPmiHhNmZ2HbFA76w==", + "funding": [ + { + "type": "github", + "url": "https://github.com/sponsors/ai" + } + ], + "license": "MIT", + "bin": { + "nanoid": "bin/nanoid.cjs" + }, + "engines": { + "node": "^10 || ^12 || ^13.7 || ^14 || >=15.0.1" + } + }, + "node_modules/path-browserify": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/path-browserify/-/path-browserify-1.0.1.tgz", + "integrity": "sha512-b7uo2UCUOYZcnF/3ID0lulOJi/bafxa1xPe7ZPsammBSpjSWQkjNxlt635YGS2MiR9GjvuXCtz2emr3jbsz98g==", + "dev": true, + "license": "MIT" + }, + "node_modules/picocolors": { + "version": "1.1.1", + "resolved": "https://registry.npmjs.org/picocolors/-/picocolors-1.1.1.tgz", + "integrity": "sha512-xceH2snhtb5M9liqDsmEw56le376mTZkEX/jEb/RxNFyegNul7eNslCXP9FDj/Lcu0X8KEyMceP2ntpaHrDEVA==", + "license": "ISC" + }, + "node_modules/picomatch": { + "version": "4.0.3", + "resolved": "https://registry.npmjs.org/picomatch/-/picomatch-4.0.3.tgz", + "integrity": "sha512-5gTmgEY/sqK6gFXLIsQNH19lWb4ebPDLA4SdLP7dsWkIXHWlG66oPuVvXSGFPppYZz8ZDZq0dYYrbHfBCVUb1Q==", + "license": "MIT", + "engines": { + "node": ">=12" + }, + "funding": { + "url": "https://github.com/sponsors/jonschlinkert" + } + }, + "node_modules/postcss": { + "version": "8.5.6", + "resolved": "https://registry.npmjs.org/postcss/-/postcss-8.5.6.tgz", + "integrity": "sha512-3Ybi1tAuwAP9s0r1UQ2J4n5Y0G05bJkpUIO0/bI9MhwmD70S5aTWbXGBwxHrelT+XM1k6dM0pk+SwNkpTRN7Pg==", + "funding": [ + { + "type": "opencollective", + "url": "https://opencollective.com/postcss/" + }, + { + "type": "tidelift", + "url": "https://tidelift.com/funding/github/npm/postcss" + }, + { + "type": "github", + "url": "https://github.com/sponsors/ai" + } + ], + "license": "MIT", + "dependencies": { + "nanoid": "^3.3.11", + "picocolors": "^1.1.1", + "source-map-js": "^1.2.1" + }, + "engines": { + "node": "^10 || ^12 || >=14" + } + }, + "node_modules/rollup": { + "version": "4.50.1", + "resolved": "https://registry.npmjs.org/rollup/-/rollup-4.50.1.tgz", + "integrity": "sha512-78E9voJHwnXQMiQdiqswVLZwJIzdBKJ1GdI5Zx6XwoFKUIk09/sSrr+05QFzvYb8q6Y9pPV45zzDuYa3907TZA==", + "license": "MIT", + "dependencies": { + "@types/estree": "1.0.8" + }, + "bin": { + "rollup": "dist/bin/rollup" + }, + "engines": { + "node": ">=18.0.0", + "npm": ">=8.0.0" + }, + "optionalDependencies": { + "@rollup/rollup-android-arm-eabi": "4.50.1", + "@rollup/rollup-android-arm64": "4.50.1", + "@rollup/rollup-darwin-arm64": "4.50.1", + "@rollup/rollup-darwin-x64": "4.50.1", + "@rollup/rollup-freebsd-arm64": "4.50.1", + "@rollup/rollup-freebsd-x64": "4.50.1", + "@rollup/rollup-linux-arm-gnueabihf": "4.50.1", + "@rollup/rollup-linux-arm-musleabihf": "4.50.1", + "@rollup/rollup-linux-arm64-gnu": "4.50.1", + "@rollup/rollup-linux-arm64-musl": "4.50.1", + "@rollup/rollup-linux-loongarch64-gnu": "4.50.1", + "@rollup/rollup-linux-ppc64-gnu": "4.50.1", + "@rollup/rollup-linux-riscv64-gnu": "4.50.1", + "@rollup/rollup-linux-riscv64-musl": "4.50.1", + "@rollup/rollup-linux-s390x-gnu": "4.50.1", + "@rollup/rollup-linux-x64-gnu": "4.50.1", + "@rollup/rollup-linux-x64-musl": "4.50.1", + "@rollup/rollup-openharmony-arm64": "4.50.1", + "@rollup/rollup-win32-arm64-msvc": "4.50.1", + "@rollup/rollup-win32-ia32-msvc": "4.50.1", + "@rollup/rollup-win32-x64-msvc": "4.50.1", + "fsevents": "~2.3.2" + } + }, + "node_modules/source-map-js": { + "version": "1.2.1", + "resolved": "https://registry.npmjs.org/source-map-js/-/source-map-js-1.2.1.tgz", + "integrity": "sha512-UXWMKhLOwVKb728IUtQPXxfYU+usdybtUrK/8uGE8CQMvrhOpwvzDBwj0QhSL7MQc7vIsISBG8VQ8+IDQxpfQA==", + "license": "BSD-3-Clause", + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/tailwindcss": { + "version": "4.1.13", + "resolved": "https://registry.npmjs.org/tailwindcss/-/tailwindcss-4.1.13.tgz", + "integrity": "sha512-i+zidfmTqtwquj4hMEwdjshYYgMbOrPzb9a0M3ZgNa0JMoZeFC6bxZvO8yr8ozS6ix2SDz0+mvryPeBs2TFE+w==", + "license": "MIT" + }, + "node_modules/tapable": { + "version": "2.2.3", + "resolved": "https://registry.npmjs.org/tapable/-/tapable-2.2.3.tgz", + "integrity": "sha512-ZL6DDuAlRlLGghwcfmSn9sK3Hr6ArtyudlSAiCqQ6IfE+b+HHbydbYDIG15IfS5do+7XQQBdBiubF/cV2dnDzg==", + "license": "MIT", + "engines": { + "node": ">=6" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/webpack" + } + }, + "node_modules/tar": { + "version": "7.4.3", + "resolved": "https://registry.npmjs.org/tar/-/tar-7.4.3.tgz", + "integrity": "sha512-5S7Va8hKfV7W5U6g3aYxXmlPoZVAwUMy9AOKyF2fVuZa2UD3qZjg578OrLRt8PcNN1PleVaL/5/yYATNL0ICUw==", + "license": "ISC", + "dependencies": { + "@isaacs/fs-minipass": "^4.0.0", + "chownr": "^3.0.0", + "minipass": "^7.1.2", + "minizlib": "^3.0.1", + "mkdirp": "^3.0.1", + "yallist": "^5.0.0" + }, + "engines": { + "node": ">=18" + } + }, + "node_modules/tinyglobby": { + "version": "0.2.15", + "resolved": "https://registry.npmjs.org/tinyglobby/-/tinyglobby-0.2.15.tgz", + "integrity": "sha512-j2Zq4NyQYG5XMST4cbs02Ak8iJUdxRM0XI5QyxXuZOzKOINmWurp3smXu3y5wDcJrptwpSjgXHzIQxR0omXljQ==", + "license": "MIT", + "dependencies": { + "fdir": "^6.5.0", + "picomatch": "^4.0.3" + }, + "engines": { + "node": ">=12.0.0" + }, + "funding": { + "url": "https://github.com/sponsors/SuperchupuDev" + } + }, + "node_modules/typescript": { + "version": "5.8.3", + "resolved": "https://registry.npmjs.org/typescript/-/typescript-5.8.3.tgz", + "integrity": "sha512-p1diW6TqL9L07nNxvRMM7hMMw4c5XOo/1ibL4aAIGmSAt9slTE1Xgw5KWuof2uTOvCg9BY7ZRi+GaF+7sfgPeQ==", + "devOptional": true, + "license": "Apache-2.0", + "bin": { + "tsc": "bin/tsc", + "tsserver": "bin/tsserver" + }, + "engines": { + "node": ">=14.17" + } + }, + "node_modules/vite": { + "version": "7.1.5", + "resolved": "https://registry.npmjs.org/vite/-/vite-7.1.5.tgz", + "integrity": "sha512-4cKBO9wR75r0BeIWWWId9XK9Lj6La5X846Zw9dFfzMRw38IlTk2iCcUt6hsyiDRcPidc55ZParFYDXi0nXOeLQ==", + "license": "MIT", + "dependencies": { + "esbuild": "^0.25.0", + "fdir": "^6.5.0", + "picomatch": "^4.0.3", + "postcss": "^8.5.6", + "rollup": "^4.43.0", + "tinyglobby": "^0.2.15" + }, + "bin": { + "vite": "bin/vite.js" + }, + "engines": { + "node": "^20.19.0 || >=22.12.0" + }, + "funding": { + "url": "https://github.com/vitejs/vite?sponsor=1" + }, + "optionalDependencies": { + "fsevents": "~2.3.3" + }, + "peerDependencies": { + "@types/node": "^20.19.0 || >=22.12.0", + "jiti": ">=1.21.0", + "less": "^4.0.0", + "lightningcss": "^1.21.0", + "sass": "^1.70.0", + "sass-embedded": "^1.70.0", + "stylus": ">=0.54.8", + "sugarss": "^5.0.0", + "terser": "^5.16.0", + "tsx": "^4.8.1", + "yaml": "^2.4.2" + }, + "peerDependenciesMeta": { + "@types/node": { + "optional": true + }, + "jiti": { + "optional": true + }, + "less": { + "optional": true + }, + "lightningcss": { + "optional": true + }, + "sass": { + "optional": true + }, + "sass-embedded": { + "optional": true + }, + "stylus": { + "optional": true + }, + "sugarss": { + "optional": true + }, + "terser": { + "optional": true + }, + "tsx": { + "optional": true + }, + "yaml": { + "optional": true + } + } + }, + "node_modules/vscode-uri": { + "version": "3.1.0", + "resolved": "https://registry.npmjs.org/vscode-uri/-/vscode-uri-3.1.0.tgz", + "integrity": "sha512-/BpdSx+yCQGnCvecbyXdxHDkuk55/G3xwnC0GqY4gmQ3j+A+g8kzzgB4Nk/SINjqn6+waqw3EgbVF2QKExkRxQ==", + "dev": true, + "license": "MIT" + }, + "node_modules/vue": { + "version": "3.5.21", + "resolved": "https://registry.npmjs.org/vue/-/vue-3.5.21.tgz", + "integrity": "sha512-xxf9rum9KtOdwdRkiApWL+9hZEMWE90FHh8yS1+KJAiWYh+iGWV1FquPjoO9VUHQ+VIhsCXNNyZ5Sf4++RVZBA==", + "license": "MIT", + "dependencies": { + "@vue/compiler-dom": "3.5.21", + "@vue/compiler-sfc": "3.5.21", + "@vue/runtime-dom": "3.5.21", + "@vue/server-renderer": "3.5.21", + "@vue/shared": "3.5.21" + }, + "peerDependencies": { + "typescript": "*" + }, + "peerDependenciesMeta": { + "typescript": { + "optional": true + } + } + }, + "node_modules/vue-i18n": { + "version": "11.1.12", + "resolved": "https://registry.npmjs.org/vue-i18n/-/vue-i18n-11.1.12.tgz", + "integrity": "sha512-BnstPj3KLHLrsqbVU2UOrPmr0+Mv11bsUZG0PyCOzsawCivk8W00GMXHeVUWIDOgNaScCuZah47CZFE+Wnl8mw==", + "license": "MIT", + "dependencies": { + "@intlify/core-base": "11.1.12", + "@intlify/shared": "11.1.12", + "@vue/devtools-api": "^6.5.0" + }, + "engines": { + "node": ">= 16" + }, + "funding": { + "url": "https://github.com/sponsors/kazupon" + }, + "peerDependencies": { + "vue": "^3.0.0" + } + }, + "node_modules/vue-tsc": { + "version": "3.0.7", + "resolved": "https://registry.npmjs.org/vue-tsc/-/vue-tsc-3.0.7.tgz", + "integrity": "sha512-BSMmW8GGEgHykrv7mRk6zfTdK+tw4MBZY/x6fFa7IkdXK3s/8hQRacPjG9/8YKFDIWGhBocwi6PlkQQ/93OgIQ==", + "dev": true, + "license": "MIT", + "dependencies": { + "@volar/typescript": "2.4.23", + "@vue/language-core": "3.0.7" + }, + "bin": { + "vue-tsc": "bin/vue-tsc.js" + }, + "peerDependencies": { + "typescript": ">=5.0.0" + } + }, + "node_modules/yallist": { + "version": "5.0.0", + "resolved": "https://registry.npmjs.org/yallist/-/yallist-5.0.0.tgz", + "integrity": "sha512-YgvUTfwqyc7UXVMrB+SImsVYSmTS8X/tSrtdNZMImM+n7+QTriRXyXim0mBrTXNeqzVF0KWGgHPeiyViFFrNDw==", + "license": "BlueOak-1.0.0", + "engines": { + "node": ">=18" + } + } + } +} diff --git a/web-components-reuse/vue-app/package.json b/web-components-reuse/vue-app/package.json new file mode 100644 index 00000000..d21e309b --- /dev/null +++ b/web-components-reuse/vue-app/package.json @@ -0,0 +1,24 @@ +{ + "name": "vue-app", + "private": true, + "version": "0.0.0", + "type": "module", + "scripts": { + "dev": "vite", + "build": "vue-tsc -b && vite build", + "preview": "vite preview" + }, + "dependencies": { + "@tailwindcss/vite": "^4.1.13", + "tailwindcss": "^4.1.13", + "vue": "^3.5.18", + "vue-i18n": "^11.1.12" + }, + "devDependencies": { + "@vitejs/plugin-vue": "^6.0.1", + "@vue/tsconfig": "^0.7.0", + "typescript": "~5.8.3", + "vite": "^7.1.2", + "vue-tsc": "^3.0.5" + } +} diff --git a/web-components-reuse/vue-app/src/App.vue b/web-components-reuse/vue-app/src/App.vue new file mode 100644 index 00000000..3995abbe --- /dev/null +++ b/web-components-reuse/vue-app/src/App.vue @@ -0,0 +1,27 @@ + + + + + diff --git a/web-components-reuse/vue-app/src/components/Home.vue b/web-components-reuse/vue-app/src/components/Home.vue new file mode 100644 index 00000000..d97293e5 --- /dev/null +++ b/web-components-reuse/vue-app/src/components/Home.vue @@ -0,0 +1,114 @@ + + + diff --git a/web-components-reuse/vue-app/src/components/events.ts b/web-components-reuse/vue-app/src/components/events.ts new file mode 100644 index 00000000..b22b9a13 --- /dev/null +++ b/web-components-reuse/vue-app/src/components/events.ts @@ -0,0 +1,5 @@ +export function showErrorModal(error: string) { + document.dispatchEvent(new CustomEvent('events.show-error-modal', { + detail: { error } + })); +} \ No newline at end of file diff --git a/web-components-reuse/vue-app/src/components/web-components.js b/web-components-reuse/vue-app/src/components/web-components.js new file mode 100644 index 00000000..35de4061 --- /dev/null +++ b/web-components-reuse/vue-app/src/components/web-components.js @@ -0,0 +1,1214 @@ +// Generated by components/package.py from web-components-reuse:7eb3ba7b7ccce0614f4d4e4417665f665559ce57 + +// Common types definition +/** +* @typedef {Object} AssetOrCurrency +* @property {string} name +* @property {number} marketSize +*/ + +export function formatMoney(value, denomination) { + const zeros = value.length; + if (zeros > 15) { + return `${value.substring(0, zeros - 15)} ${value.substring(zeros - 15, zeros - 12)} ${value.substring(zeros - 12, zeros - 9)} ${value.substring(zeros - 9, zeros - 6)} ${value.substring(zeros - 6, zeros - 3)} ${value.substring(zeros - 3)} ${denomination}`; + } + if (zeros > 12) { + return `${value.substring(0, zeros - 12)} ${value.substring(zeros - 12, zeros - 9)} ${value.substring(zeros - 9, zeros - 6)} ${value.substring(zeros - 6, zeros - 3)} ${value.substring(zeros - 3)} ${denomination}`; + } + if (zeros > 9) { + return `${value.substring(0, zeros - 9)} ${value.substring(zeros - 9, zeros - 6)} ${value.substring(zeros - 6, zeros - 3)} ${value.substring(zeros - 3)} ${denomination}`; + } + if (zeros > 6) { + return `${value.substring(0, zeros - 6)} ${value.substring(zeros - 6, zeros - 3)} ${value.substring(zeros - 3)} ${denomination}`; + } + return `${value} ${denomination}`; +} + +export class BaseHTMLElement extends HTMLElement { + + _t = null; + _tNamespace = ''; + + set t(value) { + this._t = value; + // TODO: shouldn't all components be re-renderable and called from here? + } + + set tNamespace(value) { + this._tNamespace = value; + } + + translation(key) { + const namespacedKey = (this.getAttribute("t-namespace") ?? this._tNamespace) + key; + const attributeTranslation = this.getAttribute(`t-${namespacedKey}`); + if (attributeTranslation != undefined) { + return attributeTranslation; + } + return this._t ? this._t(namespacedKey) : null; + } + + translationAttribute(key) { + const translation = this.translation(key); + if (translation) { + return `t-${key}="${translation}"`; + } + return ""; + } + + translationAttributeRemovingNamespace(key, namespace) { + const translation = this.translation(namespace + key); + if (translation) { + return `t-${key}="${translation}"`; + } + return ""; + } +}class TabsContainer extends HTMLElement { + + _activeTab = 0; + set activeTab(value) { + this._activeTab = value; + this._updateActiveTab(); + } + + activeTabClass = "underline"; + + connectedCallback() { + this._tabsHeader = this.querySelector("[data-tabs-header]"); + this._tabsBody = this.querySelector("[data-tabs-body]"); + this.activeTabClass = this.getAttribute("active-tab-class") ?? this.activeTabClass; + if (!this._tabsHeader) { + throw new Error("Tabs header must be defined and marked with data-tabs-header attribute!"); + } + if (!this._tabsBody) { + throw new Error("Tabs body must be defined and marked with data-tabs-body attribute!"); + } + + [...this._tabsHeader.children].forEach((tab, i) => { + tab.addEventListener('click', () => this.activeTab = i); + }); + + [...this._tabsBody.children].forEach((tab) => { + tab.classList.add("hidden"); + }); + + this._updateActiveTab(); + } + + _updateActiveTab() { + [...this._tabsHeader.children].forEach((tab, i) => { + if (i == this._activeTab) { + tab.classList.add(this.activeTabClass); + } else { + tab.classList.remove(this.activeTabClass); + } + }); + [...this._tabsBody.children].forEach((tab, i) => { + if (i == this._activeTab) { + tab.classList.remove('hidden'); + } else { + tab.classList.add('hidden'); + } + }); + } +} + +class TabHeader extends HTMLElement { + + connectedCallback() { + this.classList.add("text-2xl"); + this.classList.add("p-2"); + this.classList.add("cursor-pointer"); + this.classList.add("grow"); + } +} + +class DropDownContainer extends HTMLElement { + + connectedCallback() { + const optionsZIndex = this.getAttribute("options-z-index") ?? '99'; + const anchor = this.querySelector('[data-drop-down-anchor]') ?? this; + anchor.style = "position: relative; display: inline-block"; + + const options = this.querySelector("[data-drop-down-options]"); + if (!options) { + throw new Error("Options must be defined and marked with data-drop-down-options attribute!"); + } + options.style = `position: absolute; z-index: ${optionsZIndex}`; + options.classList.add("hidden"); + + anchor.onclick = (e) => { + // Do not hide other, opened DropDowns + e.stopPropagation(); + options.classList.toggle("hidden"); + }; + + window.addEventListener("click", e => { + if (e.target != anchor && e.target.parentNode != anchor) { + options.classList.add("hidden"); + } + }); + } +} + +class InfoModal extends HTMLElement { + + #container = null; + #closeButton = null; + #titleElement = null; + #contentElement = null; + + connectedCallback() { + const titleClassToAppend = this.getAttribute('title-class'); + const title = this.getAttribute("title"); + const content = this.getAttribute("content"); + + let titleClass = "text-xl font-bold p-4"; + if (titleClassToAppend) { + titleClass = titleClass + " " + titleClassToAppend; + } + + this.innerHTML = ` + + `; + + this.#container = this.querySelector('[data-container]'); + this.#closeButton = this.querySelector('[data-close-button]'); + this.#titleElement = this.querySelector('[data-title-element]'); + this.#contentElement = this.querySelector('[data-content-element]'); + + this.#closeButton.onclick = () => this.close(); + + this.#container.addEventListener("click", e => { + if (e.target == this.#container) { + this.close(); + } + }); + } + + show({ title, content }) { + if (title) { + this.#titleElement.textContent = title; + } + if (content) { + this.#contentElement.textContent = content; + } + this.#container.style.display = "block"; + } + + close() { + this.#container.style.display = "none"; + this.dispatchEvent(new CustomEvent('im.closed', { bubbles: true })); + } +} + +class ErrorModal extends HTMLElement { + + #modal = null; + #onShowHandler = null; + + connectedCallback() { + this.innerHTML = ``; + + this.#modal = this.querySelector("info-modal"); + this.#onShowHandler = (e) => { + const { title, error } = e.detail; + this.#modal.show({ title, content: error }); + }; + + document.addEventListener('em.show', this.#onShowHandler); + } + + disconnectedCallback() { + document.removeEventListener('em.show', this.#onShowHandler); + } +} + + +class AssetElement extends BaseHTMLElement { + + /** + * Supported attributes + * {string} id: asset id + * {string} name: asset name + * {number} market-size + * {number} previous-market-size + * {string} denomination + * {string} class: additional class to append to the root div + */ + connectedCallback() { + this.#render(); + } + + attributeChangedCallback(name, oldValue, newValue) { + this.#render(); + } + + #render() { + const [id, name, marketSize, previousMarketSize, denomination] = [this.getAttribute("id"), this.getAttribute("name"), + this.getAttribute("market-size"), this.getAttribute("previous-market-size"), this.getAttribute("denomination")]; + if (!id || !name) { + return; + } + const classesToAppend = this.getAttribute("class"); + + let previousMarketSizeComponent; + if (previousMarketSize && previousMarketSize != marketSize) { + const previousMarketSizeInt = parseInt(previousMarketSize); + const currentMarketSizeInt = parseInt(marketSize); + const marketIsUp = currentMarketSizeInt > previousMarketSizeInt; + let marketPercentageDiff; + if (marketIsUp) { + marketPercentageDiff = Math.round((currentMarketSizeInt - previousMarketSizeInt) * 100 * 100 / previousMarketSizeInt) / 100.0; + } else { + marketPercentageDiff = Math.round((previousMarketSizeInt - currentMarketSizeInt) * 100 * 100 / currentMarketSizeInt) / 100.0; + } + previousMarketSizeComponent = ` +
    + ${this.translation("previous-market-size-label")}:${formatMoney(previousMarketSize, denomination)} +
    +

    ${marketIsUp ? this.translation('up-by-info') : this.translation('down-by-info')} ${marketPercentageDiff}%

    `; + } else { + previousMarketSizeComponent = ``; + } + + this.innerHTML = ` +
    +

    ${name}

    +
    + ${this.translation('market-size-label')}:${formatMoney(marketSize, denomination)} +
    + ${previousMarketSizeComponent} +
    + `; + } +} + + +class CurrencyElement extends BaseHTMLElement { + + /** + * Supported attributes + * {string} id: currency id + * {string} name: currency name + * {number} market-size + * {number} previous-market-size + * {string} denomination + * {string} class: additional class to append to the root div + */ + connectedCallback() { + this._render(); + } + + attributeChangedCallback(name, oldValue, newValue) { + this._render(); + } + + _render() { + const [id, name, marketSize, previousMarketSize, denomination] = [this.getAttribute("id"), this.getAttribute("name"), + this.getAttribute("market-size"), this.getAttribute("previous-market-size"), this.getAttribute("denomination") + ]; + if (id == undefined || name == undefined) { + return; + } + const classesToAppend = this.getAttribute("class"); + + let previousMarketSizeComponent; + if (previousMarketSize && previousMarketSize != marketSize) { + const previousMarketSizeInt = parseInt(previousMarketSize); + const currentMarketSizeInt = parseInt(marketSize); + const marketIsUp = currentMarketSizeInt > previousMarketSizeInt; + let marketPercentageDiff; + if (marketIsUp) { + marketPercentageDiff = Math.round((currentMarketSizeInt - previousMarketSizeInt) * 100 * 100 / previousMarketSizeInt) / 100.0; + } else { + marketPercentageDiff = Math.round((previousMarketSizeInt - currentMarketSizeInt) * 100 * 100 / currentMarketSizeInt) / 100.0; + } + previousMarketSizeComponent = ` +

    ${marketIsUp ? this.translation('up-by-info') : this.translation('down-by-info')} ${marketPercentageDiff}%

    `; + } else { + previousMarketSizeComponent = ``; + } + + this.innerHTML = ` +
    +

    ${name}

    +
    + ${this.translation('daily-turnover-label')}:${formatMoney(marketSize, denomination)} +
    +
    + ${this.translation('yearly-turnover-label')}:${formatMoney(`${365 * parseInt(marketSize)}`, denomination)} +
    + ${previousMarketSizeComponent} +
    + `; + } +} + + +/** +* @typedef {Object} AssetOrCurrencyElement +* @property {string} id +* @property {string} name +* @property {number} marketSize +* @property {string} denomination +*/ + +class AssetsAndCurrencies extends BaseHTMLElement { + + #assets = []; + #assetsValueChangeReason = null; + #currencies = []; + #denomination = "USD"; + #assetsContainer = null; + #currenciesContainer = null; + + /** @type {AssetOrCurrencyElement[]} */ + set assets(value) { + this.#assets = value; + this.#renderAssets(); + } + + set assetsValueChangeReason(value) { + this.#assetsValueChangeReason = value; + this.#renderAssets(); + } + + /** @type {AssetOrCurrencyElement[]} */ + set currencies(value) { + this.#currencies = value; + this.#renderCurrencies(); + } + + /** @type {string} */ + set denomination(value) { + this.#denomination = value; + this.#renderAssets(); + this.#renderCurrencies(); + } + + connectedCallback() { + this.innerHTML = ` +
    + +
    + ${this.translation('assets-header')} + ${this.translation('currencies-header')} +
    +
    +
    + ${this.#assetsHTML()} +
    +
    + ${this.#currenciesHTML()} +
    +
    +
    +
    `; + + const tabsBody = this.querySelector("[data-tabs-body]"); + this.#assetsContainer = tabsBody.children[0]; + this.#currenciesContainer = tabsBody.children[1]; + } + + #assetsHTML(previousAssetElements = []) { + return this.#assets.map(a => { + const previousAsset = previousAssetElements.find(pa => pa.id == a.id); + let previousMarketSize; + if (!previousAsset) { + previousMarketSize = a.marketSize; + } else { + const previousAssetDenomination = previousAsset.getAttribute("denomination"); + const previousAssetMarketSize = previousAsset.getAttribute("market-size"); + // if denomination has changed, comparing current market size with the previous is meaningless + if (previousAssetDenomination != a.denomination) { + previousMarketSize = a.marketSize; + } else if (previousAssetMarketSize != a.marketSize) { + previousMarketSize = previousAssetMarketSize; + } else { + previousMarketSize = previousAsset.getAttribute("previous-market-size"); + } + } + + return ` + `; + }).join("\n"); + } + + #currenciesHTML(previousCurrencyElements = []) { + return this.#currencies.map(c => { + const previousCurrency = previousCurrencyElements.find(pc => pc.id == c.id); + let previousMarketSize; + if (!previousCurrency) { + previousMarketSize = c.marketSize; + } else { + const previousCurrencyDenomination = previousCurrency.getAttribute("denomination"); + const previousCurrencyMarketSize = previousCurrency.getAttribute("market-size"); + // if denomination has changed, comparing current market size with the previous is meaningless + if (previousCurrencyDenomination != c.denomination) { + previousMarketSize = c.marketSize; + } else if (previousCurrencyMarketSize != c.marketSize) { + previousMarketSize = previousCurrencyMarketSize; + } else { + previousMarketSize = previousCurrency.getAttribute("previous-market-size"); + } + } + + return ` + `}) + .join("\n"); + } + + #renderAssets() { + if (this.#assetsContainer) { + const currentAssetElements = [...this.querySelectorAll("asset-element")]; + this.#assetsContainer.innerHTML = this.#assetsHTML(currentAssetElements); + } + } + + #renderCurrencies() { + if (this.#currenciesContainer) { + const currentCurrencyElements = [...this.querySelectorAll("currency-element")]; + this.#currenciesContainer.innerHTML = this.#currenciesHTML(currentCurrencyElements); + } + } +} + + +class MarketsHeader extends BaseHTMLElement { + + _denomination = 'USD'; + _liveUpdatesEnabled = true; + _denominationExchangeRates = []; + _liveUpdatesEnabledElement = null; + _denominationElement = null; + + + set denomination(value) { + this._denomination = value; + if (this._denominationElement) { + this._denominationElement.textContent = this._denomination; + } + } + + set denominationExchangeRates(value) { + this._denominationExchangeRates = value; + this._renderDenominationOptions(); + } + + connectedCallback() { + this.innerHTML = ` +
    +
    ${this.translation('live-updates')} ${this._liveUpdatesElementText()} +
    + ${this.translation('markets-in')} + + ${this._denomination} +
      + ${this._denominationOptionsHTML()} +
    +
    +
    + `; + + this._liveUpdatesEnabledElement = this.querySelector("span"); + this._liveUpdatesEnabledElement.onclick = () => { + this._liveUpdatesEnabled = !this._liveUpdatesEnabled; + this._liveUpdatesEnabledElement.textContent = this._liveUpdatesElementText(); + this.dispatchEvent(new CustomEvent('mh.live-updates-toggled', { bubbles: true, detail: this._liveUpdatesEnabled })); + }; + + this._denominationElement = this.querySelector("drop-down-container > span"); + } + + _denominationOptionsHTML() { + return this._denominationExchangeRates.map(der => `
  • ${der.name}: ${der.exchangeRate}
  • `).join('\n'); + } + + _renderDenominationOptions() { + const optionsContainer = this.querySelector("ul"); + if (optionsContainer) { + optionsContainer.innerHTML = this._denominationOptionsHTML(); + this._setOptionsClickHandlers(); + } + } + + _setOptionsClickHandlers() { + [...this.querySelectorAll("li")].forEach(o => { + o.onclick = () => { + this._denomination = o.getAttribute("data-option-id"); + this._denominationElement.textContent = this._denomination; + this.dispatchEvent(new CustomEvent('mh.denomination-changed', { bubbles: true, detail: this._denomination })); + }; + }); + } + + _liveUpdatesElementText() { + return `${this._liveUpdatesEnabled ? this.translation('live-updates-on') : this.translation('live-updates-off')}`; + } +} + + +class MarketsComparator extends BaseHTMLElement { + + static observedAttributes = ["asset-items", "currency-items"]; + + _fromMarketsComparatorInput = null; + _toMarketsComparatorInput = null; + _comparisonElement = null; + + _fromMarketSize = null; + _toMarketSize = null; + + /** @type {AssetOrCurrency[]} */ + set assets(value) { + if (this._fromMarketsComparatorInput) { + this._setValuesUsingAttributes(this._fromMarketsComparatorInput, value, "asset"); + } + if (this._toMarketsComparatorInput) { + this._setValuesUsingAttributes(this._toMarketsComparatorInput, value, "asset"); + } + } + + /** @type {AssetOrCurrency[]} */ + set currencies(value) { + if (this._fromMarketsComparatorInput) { + this._setValuesUsingAttributes(this._fromMarketsComparatorInput, value, "currency"); + } + if (this._toMarketsComparatorInput) { + this._setValuesUsingAttributes(this._toMarketsComparatorInput, value, "currency"); + } + } + + _setValuesUsingAttributes(element, values, prefix) { + for (let i = 0; i < values.length; i++) { + element.setAttribute(`${prefix}-${i}-name`, values[i].name); + element.setAttribute(`${prefix}-${i}-market-size`, values[i].marketSize); + } + element.setAttribute(`${prefix}-items`, values.length); + } + + connectedCallback() { + this._render(); + + this._chosenMarketSizeChangedEventHandler = e => { + const { name, marketSize } = e.detail; + if (e.target === this._fromMarketsComparatorInput) { + this._fromMarketSize = marketSize; + this._renderComparisonElementHTML(); + this.dispatchEvent(new CustomEvent("mc.from-market-size-changed", { + bubbles: true, + detail: { name, marketSize } + })); + } else if (e.target === this._toMarketsComparatorInput) { + this._toMarketSize = marketSize; + this._renderComparisonElementHTML(); + this.dispatchEvent(new CustomEvent("mc.to-market-size-changed", { + bubbles: true, + detail: { name, marketSize } + })); + } + } + document.addEventListener('mci.chosen-market-size-changed', this._chosenMarketSizeChangedEventHandler); + } + + disconnectedCallback() { + document.removeEventListener('mci.chosen-market-size-changed', this._chosenMarketSizeChangedEventHandler); + } + + attributeChangedCallback(name, oldValue, newValue) { + if (name == 'asset-items') { + this.assets = assetsFromAttributes(this); + } else if (name == 'currency-items') { + this.currencies = currenciesFromAttributes(this); + } + } + + _render() { + this.innerHTML = ` +
    + + +
    ${this.translation('markets-to')}
    + + +
    ${this._comparisonElementHTML()}
    +
    + `; + + [this._fromMarketsComparatorInput, this._toMarketsComparatorInput] = this.querySelectorAll("markets-comparator-input"); + this._comparisonElement = this.querySelector('[data-comparison-element]'); + } + + _comparisonElementHTML() { + if (!this._fromMarketSize || !this._toMarketSize) { + return '-'; + } + return `${this._fromMarketSize.toExponential(3)} / ${this._toMarketSize.toExponential(3)} = ${this._chosenMarketsComparedValue()}`; + } + + _chosenMarketsComparedValue() { + if (!this._fromMarketSize || !this._toMarketSize) { + return 0; + } + return Math.round(this._fromMarketSize * 1000 / this._toMarketSize) / 1000.0; + } + + _renderComparisonElementHTML() { + this._comparisonElement.innerHTML = this._comparisonElementHTML(); + } +} + +function assetsFromAttributes(element) { + const countAttribute = element.getAttribute("asset-items"); + if (!countAttribute) { + return []; + } + const assets = []; + for (let i = 0; i < parseInt(countAttribute); i++) { + const name = element.getAttribute(`asset-${i}-name`); + const marketSize = element.getAttribute(`asset-${i}-market-size`); + if (name && marketSize) { + assets.push({ name, marketSize: parseInt(marketSize) }); + } + } + return assets; +} + +function currenciesFromAttributes(element) { + const countAttribute = element.getAttribute("currency-items"); + if (!countAttribute) { + return []; + } + const currencies = []; + for (let i = 0; i < parseInt(countAttribute); i++) { + const name = element.getAttribute(`currency-${i}-name`); + const marketSize = element.getAttribute(`currency-${i}-market-size`); + if (name && marketSize) { + currencies.push({ name, marketSize: parseInt(marketSize) }); + } + } + return currencies; +} + +class MarketsComparatorInput extends BaseHTMLElement { + + static observedAttributes = ["asset-items", "currency-items"]; + + /** @type {AssetOrCurrency[]} */ + _assets = []; + /** @type {AssetOrCurrency[]} */ + _currencies = []; + /** @type {AssetOrCurrency[]} */ + _assetOrCurrencyOptions = []; + + set assets(value) { + this._assets = value; + this._recalculateAssetOrCurrencyOptions(); + } + + set currencies(value) { + this._currencies = value; + this._recalculateAssetOrCurrencyOptions(); + } + + _assetOrCurrencyInput = null; + _curencyTurnoverInputMultiplier = 1; + + connectedCallback() { + this._render(); + } + + attributeChangedCallback(name, oldValue, newValue) { + if (name == 'asset-items') { + this.assets = assetsFromAttributes(this); + } else if (name == 'currency-items') { + this.currencies = currenciesFromAttributes(this); + } + } + + _render() { + const optionsZIndex = this.getAttribute("options-z-index") ?? '99'; + this.innerHTML = ` + +
    + ${this._dropDownHeaderHTML()} +
    +
      + ${this._optionsHTML()} +
    +
    + `; + + this._setOptionsClickHandlers(); + + this.querySelector('[data-drop-down-header]') + .querySelector("input")?.addEventListener("input", e => { + this._curencyTurnoverInputMultiplier = e.target.value; + this._calculateChosenMarketSizeChange(); + }); + } + + _optionsHTML() { + return this._assetOrCurrencyOptions.map(o => + `
  • ${o.name}
  • `) + .join('\n'); + } + + _dropDownHeaderHTML() { + let marketSizeHTML; + if (this._isAsset()) { + marketSizeHTML = `${this.translation('market-size-input-label')} + + ${this.translation('days-turnover-input-label')} + `; + } else { + marketSizeHTML = ``; + } + return ` + ${this._assetOrCurrencyInput ?? this.translation('asset-or-currency-input-placeholder')} + ${marketSizeHTML} + `; + } + + _setOptionsClickHandlers() { + [...this.querySelectorAll("li")].forEach(o => { + o.onclick = () => { + this._assetOrCurrencyInput = o.getAttribute("data-option-id"); + this._calculateChosenMarketSizeChange(); + this._render(); + }; + }); + } + + _renderOptionsHTML() { + const optionsContainer = this.querySelector("ul"); + if (optionsContainer) { + optionsContainer.innerHTML = this._optionsHTML(); + this._setOptionsClickHandlers(); + } + } + + _isAsset() { + return this._assetOrCurrencyInput ? this._assets.find(a => a.name == this._assetOrCurrencyInput) : false; + } + + _isCurrency() { + return this._assetOrCurrencyInput ? this._currencies.find(c => c.name == this._assetOrCurrencyInput) : false; + } + + _recalculateAssetOrCurrencyOptions() { + const assetOrCurrencyOptions = []; + this._assets.forEach(a => assetOrCurrencyOptions.push(a)); + this._currencies.forEach(c => assetOrCurrencyOptions.push(c)); + this._assetOrCurrencyOptions = assetOrCurrencyOptions; + + this._renderOptionsHTML(); + this._calculateChosenMarketSizeChange(); + } + + _calculateChosenMarketSizeChange() { + if (!this._assetOrCurrencyInput) { + return; + } + + const assetInput = this._assets.find(a => a.name == this._assetOrCurrencyInput); + const currencyInput = this._currencies.find(c => c.name == this._assetOrCurrencyInput); + + const inputMarketSize = assetInput ? assetInput.marketSize : currencyInput.marketSize * this._curencyTurnoverInputMultiplier; + + this.dispatchEvent(new CustomEvent("mci.chosen-market-size-changed", { + bubbles: true, + detail: { name: this._assetOrCurrencyInput, marketSize: inputMarketSize } + })); + } +} + + +/** +* @typedef {Object} AssetOrCurrencyProjection +* @property {number} marketSize +* @property {number} growthRate +*/ + +class ProjectionsCalculator extends BaseHTMLElement { + + /** @type {?AssetOrCurrency} */ + _assetOrCurrency1 = null; + /** @type {?AssetOrCurrency} */ + _assetOrCurrency2 = null; + /** @type {?number} */ + _assetOrCurrency1ExpectedGrowthRate = null; + /** @type {?number} */ + _assetOrCurrency2ExpectedGrowthRate = null; + /** @type {?number} */ + _customProjectionYears = null; + + set assetOrCurrency1(value) { + this._assetOrCurrency1 = value; + this._renderProjectionsResultsHTML(); + } + + set assetOrCurrency2(value) { + this._assetOrCurrency2 = value; + this._renderProjectionsResultsHTML(); + } + + _assetOrCurrency1Header = null; + _assetOrCurrency1Input = null; + _assetOrCurrency2Header = null; + _assetOrCurrency2Input = null; + _customProjectionInput = null; + _projectionsResultsContainer = null; + _customProjectionContainer = null; + _customProjectionTextElement = null; + _customProjectionResultContainer = null; + + connectedCallback() { + this.innerHTML = ` +
    +
    ${this._assetOrCurrencyHeaderText(this._assetOrCurrency1)}
    + +
    ${this._assetOrCurrencyHeaderText(this._assetOrCurrency2)}
    + +
    + ${this._projectionsResultsHTML()} +
    +
    + ${this._customProjectionHTML()} +
    +
    + `; + + const container = this.querySelector("div"); + + const divs = container.querySelectorAll("div"); + [this._assetOrCurrency1Header, this._assetOrCurrency2Header, this._projectionsResultsContainer] = divs; + this._customProjectionContainer = divs[divs.length - 1]; + + [this._assetOrCurrency1Input, this._assetOrCurrency2Input, + this._customProjectionInput] = container.querySelectorAll("input"); + + this._assetOrCurrency1Input.addEventListener("input", e => { + this._assetOrCurrency1ExpectedGrowthRate = parseInt(e.target.value); + this._renderProjectionsResultsHTML(); + }); + this._assetOrCurrency2Input.addEventListener("input", e => { + this._assetOrCurrency2ExpectedGrowthRate = parseInt(e.target.value); + this._renderProjectionsResultsHTML(); + }); + this._customProjectionInput.addEventListener("input", e => { + this._customProjectionYears = parseInt(e.target.value); + this._updateCustomProjectionText(); + this._updateCustomProjectionResult(); + }); + + this._customProjectionTextElement = this.querySelector('[data-custom-projection-text-element]'); + this._customProjectionResultContainer = this.querySelector('[data-custom-projection-result-container]'); + } + + _assetOrCurrencyHeaderText(assetOrCurrency) { + return `${assetOrCurrency ? assetOrCurrency.name : this.translation('asset-or-currency-placeholder')} ${this.translation('asset-or-currency-expected-annual-growth-rate')}:`; + } + + _renderProjectionsResultsHTML() { + if (this._projectionsResultsContainer && this._customProjectionContainer) { + this._projectionsResultsContainer.innerHTML = this._projectionsResultsHTML(); + this._updateCustomProjectionResult(); + } + } + + _projectionsResultsHTML() { + const inYearsText = (years) => `${this.translation('results-in-header')} ${years} ${this.translation(years == 1 ? 'year' : 'years')}`; + const currentYear = new Date().getFullYear(); + return [1, 5, 10].map(y => ` +
    ${inYearsText(y)} (${currentYear + y}):
    + ${this._projectionsResultHTML(y)}`) + .join('\n'); + } + + _projectionsResultHTML(years) { + const ac1 = this._assetOrCurrency1WithExpectedGrowthRate(); + const ac2 = this._assetOrCurrency2WithExpectedGrowthRate(); + if (years != null && ac1 != null && ac2 != null) { + return ` + + `; + } + return '
    -
    '; + } + + _customProjectionHTML() { + return ` + ${this.translation('results-in-header')} + ${this._customProjectionYearText()}: +
    ${this._projectionsResultHTML(this._customProjectionYears)}
    + `; + } + + _customProjectionYearText() { + const currentYear = new Date().getFullYear(); + return '(' + (this._customProjectionYears == null || Number.isNaN(this._customProjectionYears) ? + `${currentYear} + N` : (currentYear + this._customProjectionYears)) + ')'; + } + + _updateCustomProjectionText() { + this._customProjectionTextElement.textContent = this._customProjectionYearText(); + } + + _updateCustomProjectionResult() { + this._customProjectionContainer.innerHTML = this._projectionsResultHTML(this._customProjectionYears); + } + + _assetOrCurrency1WithExpectedGrowthRate() { + if (this._assetOrCurrency1 && this._assetOrCurrency1ExpectedGrowthRate != null) { + return { marketSize: this._assetOrCurrency1.marketSize, growthRate: this._assetOrCurrency1ExpectedGrowthRate }; + } + return null; + } + + _assetOrCurrency2WithExpectedGrowthRate() { + if (this._assetOrCurrency2 && this._assetOrCurrency2ExpectedGrowthRate != null) { + return { marketSize: this._assetOrCurrency2.marketSize, growthRate: this._assetOrCurrency2ExpectedGrowthRate }; + } + return null; + } +} + +class ProjectionsResult extends HTMLElement { + + static observedAttributes = [ + "years", + "asset-or-currency-1-market-size", "asset-or-currency-1-growth-rate", + "asset-or-currency-2-market-size", "asset-or-currency-2-growth-rate" + ]; + + /** @type {number} */ + _years = 1; + /** @type {?AssetOrCurrencyProjection} */ + _assetOrCurrency1 = null; + /** @type {?AssetOrCurrencyProjection} */ + _assetOrCurrency2 = null; + + set years(value) { + this._years = value; + this._render(); + } + + set assetOrCurrency1(value) { + this._assetOrCurrency1 = value; + this._render(); + } + + set assetOrCurrency2(value) { + this._assetOrCurrency2 = value; + this._render(); + } + + connectedCallback() { + this._render(); + } + + attributeChangedCallback(name, oldValue, newValue) { + if (name.includes("asset-or-currency-1")) { + const assetOrCurrency = this._assetOrCurrencyFromAttributes("asset-or-currency-1"); + if (assetOrCurrency) { + this.assetOrCurrency1 = assetOrCurrency; + } + } else if (name.includes("asset-or-currency-2")) { + const assetOrCurrency = this._assetOrCurrencyFromAttributes("asset-or-currency-2"); + if (assetOrCurrency) { + this.assetOrCurrency2 = assetOrCurrency; + } + } else if (name == 'years') { + this.years = parseInt(newValue); + } + } + + _assetOrCurrencyFromAttributes(prefix) { + const marketSize = this.getAttribute(`${prefix}-market-size`); + const growthRate = this.getAttribute(`${prefix}-growth-rate`); + if (marketSize && growthRate) { + return { marketSize: parseInt(marketSize), growthRate: parseInt(growthRate) }; + } + return null; + } + + _render() { + const nominator = this._exponentialNumberString(this._projectionNumerator()); + const denominator = this._exponentialNumberString(this._projectionDenominator()); + this.innerHTML = ` +
    ${nominator} / ${denominator} = ${this._projection()} +
    + `; + } + + _exponentialNumberString(n) { + return n?.toExponential(3) ?? ''; + } + + _marketSizeChangedByRate(marketSize, growthRate, decrease = false) { + let changedMarketSize; + if (decrease) { + changedMarketSize = marketSize - (marketSize * growthRate / 100.0); + } else { + changedMarketSize = marketSize + (marketSize * growthRate / 100.0); + } + if (changedMarketSize <= 0 || Number.isNaN(changedMarketSize)) { + return null; + } + return changedMarketSize; + } + + _marketSizeChangedByRateInGivenYears(marketSize, growthRate, years) { + const negativeYears = years < 0; + let increasedMarketSize = marketSize; + for (let i = 0; i < Math.abs(years); i++) { + increasedMarketSize = this._marketSizeChangedByRate(increasedMarketSize, growthRate, negativeYears); + if (!increasedMarketSize) { + return null; + } + } + return increasedMarketSize; + } + + _projectionNumerator() { + if (this._assetOrCurrency1) { + return this._marketSizeChangedByRateInGivenYears( + this._assetOrCurrency1.marketSize, + this._assetOrCurrency1.growthRate, + this._years); + } + return null; + } + + _projectionDenominator() { + if (this._assetOrCurrency2) { + return this._marketSizeChangedByRateInGivenYears( + this._assetOrCurrency2.marketSize, + this._assetOrCurrency2.growthRate, + this._years); + } + return null; + } + + _projection() { + const numerator = this._projectionNumerator(); + const denominator = this._projectionDenominator(); + if (numerator && denominator) { + return Math.round(numerator * 1000 / denominator) / 1000.0; + } + return ''; + } +} + + +class MarketsProjections extends BaseHTMLElement { + + _marketsComparatorComponent = null; + _projectionsCalculatorComponent = null; + + /** @type {?AssetOrCurrency} */ + _fromAssetOrCurrency = null; + /** @type {?AssetOrCurrency} */ + _toAssetOrCurrency = null; + + /** @type {AssetOrCurrency[]} */ + set assets(value) { + if (this._marketsComparatorComponent) { + this._marketsComparatorComponent.assets = value; + } + } + + /** @type {AssetOrCurrency[]} */ + set currencies(value) { + if (this._marketsComparatorComponent) { + this._marketsComparatorComponent.currencies = value; + } + } + + connectedCallback() { + this.innerHTML = ` +
    +

    ${this.translation('projections-header')}

    + + + + +
    + `; + + this._marketsComparatorComponent = this.querySelector("markets-comparator"); + this._projectionsCalculatorComponent = this.querySelector("projections-calculator"); + + const fromMarketSizeChangedEventHandler = e => { + this._fromAssetOrCurrency = e.detail; + this._projectionsCalculatorComponent.assetOrCurrency1 = this._fromAssetOrCurrency; + }; + const toMarketSizeChangedEventHandler = e => { + this._toAssetOrCurrency = e.detail; + this._projectionsCalculatorComponent.assetOrCurrency2 = this._toAssetOrCurrency; + }; + + this.addEventListener('mc.from-market-size-changed', fromMarketSizeChangedEventHandler); + this.addEventListener('mc.to-market-size-changed', toMarketSizeChangedEventHandler); + } +} + + +export function registerComponents() { + customElements.define('tabs-container', TabsContainer); + customElements.define('tab-header', TabHeader); + customElements.define("drop-down-container", DropDownContainer); + customElements.define('info-modal', InfoModal); + customElements.define('error-modal', ErrorModal); + customElements.define("asset-element", AssetElement); + customElements.define("currency-element", CurrencyElement); + customElements.define('assets-and-currencies', AssetsAndCurrencies); + customElements.define("markets-header", MarketsHeader); + customElements.define("markets-comparator-input", MarketsComparatorInput); + customElements.define('markets-comparator', MarketsComparator); + customElements.define("projections-result", ProjectionsResult); + customElements.define("projections-calculator", ProjectionsCalculator); + customElements.define('markets-projections', MarketsProjections); +} \ No newline at end of file diff --git a/web-components-reuse/vue-app/src/data/api.ts b/web-components-reuse/vue-app/src/data/api.ts new file mode 100644 index 00000000..33d555ae --- /dev/null +++ b/web-components-reuse/vue-app/src/data/api.ts @@ -0,0 +1,94 @@ +import type { CurrencyCode, AssetCode } from "./codes"; +import { HttpApi } from "./http-api"; + +export interface Asset { + code: AssetCode; + marketSize: number; + denomination: CurrencyCode +} + +export interface Currency { + code: CurrencyCode; + marketSize: number; + denomination: CurrencyCode; +} + +export interface ExchangeRate { + from: CurrencyCode; + to: CurrencyCode; + value: number; +} + +export interface AssetsResponse { + assets: Asset[]; + assetsVersion: string; + exchangeRatesVersion: string; + responseVersion: string; +} + +export interface CurrenciesResponse { + currencies: Currency[]; + responseVersion: string; +} + +export interface ExchangeRatesResponse { + exchangeRates: ExchangeRate[]; + responseVersion: string; +} + +export class Response { + + private readonly _value: T | null | undefined; + private readonly _error: string | null; + + constructor(value: T | null | undefined, error: string | null) { + this._value = value; + this._error = error; + } + + static ofSuccess(value?: T): Response { + return new Response(value, null); + } + + static ofFailure(error: string): Response { + return new Response(null as T, error); + } + + success(): boolean { + return this._error == null; + } + + value(): T { + if (!this.success()) { + throw new Error("Cannot return value from failed response"); + } + if (this.hasValue()) { + return this._value as T; + } + throw new Error("Cannot return value from empty success response"); + } + + hasValue() { + return this.success() && this._value; + } + + error(): string { + if (!this.success()) { + return this._error as string; + } + throw new Error("Cannot return error from success response"); + } +} + +export type PromiseResponse = Promise>; + +export interface Api { + + assets(denomination: CurrencyCode, version?: string): PromiseResponse + + currencies(denomination: CurrencyCode, version?: string): PromiseResponse + + exchangeRates(from: CurrencyCode, version?: string): PromiseResponse +} + +export const api: Api = new HttpApi(import.meta.env.VITE_API_BASE_URL); \ No newline at end of file diff --git a/web-components-reuse/vue-app/src/data/codes.ts b/web-components-reuse/vue-app/src/data/codes.ts new file mode 100644 index 00000000..0a06e9a2 --- /dev/null +++ b/web-components-reuse/vue-app/src/data/codes.ts @@ -0,0 +1,26 @@ +export enum CurrencyCode { + USD = "USD", + EUR = "EUR", + JPY = "JPY", + GBP = "GBP", + CNY = "CNY", + PLN = "PLN" +}; + +export const currencyCodes: CurrencyCode[] = [ + CurrencyCode.USD, + CurrencyCode.EUR, + CurrencyCode.JPY, + CurrencyCode.GBP, + CurrencyCode.CNY, + CurrencyCode.PLN +]; + +export enum AssetCode { + BONDS = "BONDS", + STOCKS = "STOCKS", + GOLD = "GOLD", + CASH = "CASH", + RLEST = "RLEST", + BTC = "BTC" +}; \ No newline at end of file diff --git a/web-components-reuse/vue-app/src/data/http-api.ts b/web-components-reuse/vue-app/src/data/http-api.ts new file mode 100644 index 00000000..f26de251 --- /dev/null +++ b/web-components-reuse/vue-app/src/data/http-api.ts @@ -0,0 +1,40 @@ +import { Response, type PromiseResponse, type Api, type AssetsResponse, type CurrenciesResponse, type ExchangeRatesResponse } from "./api"; +import type { CurrencyCode } from "./codes"; + +export class HttpApi implements Api { + + private readonly baseUrl: string; + + constructor(baseUrl: string) { + this.baseUrl = baseUrl; + } + + assets(denomination: CurrencyCode, version?: string): PromiseResponse { + return this.get(`assets?denomination=${denomination}`, version); + } + + async get(path: string, version?: string): PromiseResponse { + try { + const response = await fetch(`${this.baseUrl}/${path}`, { method: "GET", headers: version ? { 'If-None-Match': version } : {} }); + if (response.status == 304) { + return Response.ofSuccess(); + } + const jsonResponse = await response.json(); + if (response.ok) { + return Response.ofSuccess(jsonResponse as T); + } + return Response.ofFailure(jsonResponse['error'] ?? 'UnsupportedErrorFormat'); + } catch (e: any) { + console.error("Failed to fetch", e); + return Response.ofFailure("UnknownFetchError"); + } + } + + currencies(denomination: CurrencyCode, version?: string): PromiseResponse { + return this.get(`currencies?denomination=${denomination}`, version); + } + + exchangeRates(from: CurrencyCode, version?: string): PromiseResponse { + return this.get(`exchange-rates/${from}`, version); + } +} \ No newline at end of file diff --git a/web-components-reuse/vue-app/src/data/updater.ts b/web-components-reuse/vue-app/src/data/updater.ts new file mode 100644 index 00000000..01b29ac9 --- /dev/null +++ b/web-components-reuse/vue-app/src/data/updater.ts @@ -0,0 +1,51 @@ +export class Updater { + + private fetchAssets: Function | null = null; + private fetchCurrencies: Function | null = null; + private fetchExchangeRates: Function | null = null; + private _paused = false; + + set paused(value: boolean) { + this._paused = value; + } + + with(fetchAssets: Function, fetchCurrencies: Function, fetchExchangeRates: Function) { + this.fetchAssets = fetchAssets; + this.fetchCurrencies = fetchCurrencies; + this.fetchExchangeRates = fetchExchangeRates; + } + + public start() { + setInterval(() => this.update(), 1000); + } + + public clear() { + this.fetchAssets = null; + this.fetchCurrencies = null; + this.fetchExchangeRates = null; + } + + private async update() { + if (this._paused) { + return; + } + if (this.fetchAssets) { + this.fetchAssets(); + } + if (this.fetchCurrencies) { + this.fetchCurrencies(); + } + if (this.fetchExchangeRates) { + this.fetchExchangeRates(); + } + } +} + +let updater: Updater | undefined; +export function useUpdater(): Updater { + if (!updater) { + updater = new Updater(); + updater.start(); + } + return updater; +} \ No newline at end of file diff --git a/web-components-reuse/vue-app/src/main.ts b/web-components-reuse/vue-app/src/main.ts new file mode 100644 index 00000000..7a191bf9 --- /dev/null +++ b/web-components-reuse/vue-app/src/main.ts @@ -0,0 +1,77 @@ +import { createApp } from 'vue'; +import { createI18n } from 'vue-i18n'; +import './style.css'; +import App from './App.vue'; + +// @ts-ignore +import { registerComponents } from './components/web-components.js'; + +registerComponents(); + + +const i18n = createI18n({ + legacy: false, + locale: 'en', + fallbackLocale: 'en', + messages: { + en: { + 'errors': { + 'title': "Something went wrong...", + 'UnknownFetchError': "Unknown error while fetching data", + 'UnsupportedErrorFormat': "Server responded in an unexpected way" + }, + 'currency-code': { + 'USD': "US Dollar", + 'EUR': "Euro", + 'JPY': "Japanesse Yen", + 'GBP': "British Pound", + 'CNY': "Chinese Yuan", + 'PLN': "Polish Zloty" + }, + 'asset-code': { + 'BONDS': "Bonds", + 'STOCKS': "Stocks", + 'GOLD': "Gold", + 'CASH': "Cash Reserves", + 'RLEST': "Real Estate", + 'BTC': "Bitcoin" + }, + 'markets-header': { + 'markets-in': "Markets in", + 'live-updates': "Live updates:", + 'live-updates-on': "ON", + 'live-updates-off': "OFF" + }, + 'assets-and-currencies': { + 'assets-header': "Assets", + 'currencies-header': "Currencies", + 'market-size-label': "Market size", + 'previous-market-size-label': "Previous market size", + 'up-by-info': "UP by", + 'down-by-info': "DOWN by", + 'daily-turnover-label': "Daily turnover", + 'yearly-turnover-label': "Yearly turnover" + }, + 'markets-projections': { + 'projections-header': 'Projections', + 'markets-comparator': { + 'asset-or-currency-input-placeholder': 'Asset/Currency', + 'market-size-input-label': 'market size', + 'days-turnover-input-label': 'days turnover', + 'markets-to': 'to' + }, + 'projections-calculator': { + 'asset-or-currency-placeholder': 'Asset/Currency', + 'asset-or-currency-expected-annual-growth-rate': 'expected annual growth rate', + 'results-in-header': 'In', + 'year': 'year', + 'years': 'years' + } + } + } + } +}); + +createApp(App) + .use(i18n) + .mount('#app'); \ No newline at end of file diff --git a/web-components-reuse/vue-app/src/style.css b/web-components-reuse/vue-app/src/style.css new file mode 100644 index 00000000..a461c505 --- /dev/null +++ b/web-components-reuse/vue-app/src/style.css @@ -0,0 +1 @@ +@import "tailwindcss"; \ No newline at end of file diff --git a/web-components-reuse/vue-app/src/vite-env.d.ts b/web-components-reuse/vue-app/src/vite-env.d.ts new file mode 100644 index 00000000..11f02fe2 --- /dev/null +++ b/web-components-reuse/vue-app/src/vite-env.d.ts @@ -0,0 +1 @@ +/// diff --git a/web-components-reuse/vue-app/tsconfig.app.json b/web-components-reuse/vue-app/tsconfig.app.json new file mode 100644 index 00000000..f6b63e6f --- /dev/null +++ b/web-components-reuse/vue-app/tsconfig.app.json @@ -0,0 +1,16 @@ +{ + "extends": "@vue/tsconfig/tsconfig.dom.json", + "compilerOptions": { + "tsBuildInfoFile": "./node_modules/.tmp/tsconfig.app.tsbuildinfo", + + /* Linting */ + "strict": true, + "noUnusedLocals": true, + "noUnusedParameters": true, + /* Was true */ + "erasableSyntaxOnly": false, + "noFallthroughCasesInSwitch": true, + "noUncheckedSideEffectImports": true + }, + "include": ["src/**/*.ts", "src/**/*.tsx", "src/**/*.vue"] +} diff --git a/web-components-reuse/vue-app/tsconfig.json b/web-components-reuse/vue-app/tsconfig.json new file mode 100644 index 00000000..1ffef600 --- /dev/null +++ b/web-components-reuse/vue-app/tsconfig.json @@ -0,0 +1,7 @@ +{ + "files": [], + "references": [ + { "path": "./tsconfig.app.json" }, + { "path": "./tsconfig.node.json" } + ] +} diff --git a/web-components-reuse/vue-app/tsconfig.node.json b/web-components-reuse/vue-app/tsconfig.node.json new file mode 100644 index 00000000..9361bcc4 --- /dev/null +++ b/web-components-reuse/vue-app/tsconfig.node.json @@ -0,0 +1,24 @@ +{ + "compilerOptions": { + "tsBuildInfoFile": "./node_modules/.tmp/tsconfig.node.tsbuildinfo", + "target": "ES2023", + "lib": ["ES2023"], + "module": "ESNext", + "skipLibCheck": true, + + /* Bundler mode */ + "moduleResolution": "bundler", + "allowImportingTsExtensions": true, + "verbatimModuleSyntax": true, + "moduleDetection": "force", + "noEmit": true, + + /* Linting */ + "strict": true, + "noUnusedLocals": true, + "noUnusedParameters": true, + "noFallthroughCasesInSwitch": true, + "noUncheckedSideEffectImports": true + }, + "include": ["vite.config.ts"] +} diff --git a/web-components-reuse/vue-app/vite.config.ts b/web-components-reuse/vue-app/vite.config.ts new file mode 100644 index 00000000..ed3f72fc --- /dev/null +++ b/web-components-reuse/vue-app/vite.config.ts @@ -0,0 +1,14 @@ +import { defineConfig } from 'vite' +import vue from '@vitejs/plugin-vue' +import tailwindcss from '@tailwindcss/vite' + +// https://vite.dev/config/ +export default defineConfig({ + plugins: [vue({ + template: { + compilerOptions: { + isCustomElement: (tag) => tag.includes('-') + } + } + }), tailwindcss()], +})