fix(front): ensure compatibility with 94% of browsers in the wild #2501

2501-fix-compatibility-with-older-browsers
Flupsi 2025-08-03 15:52:10 +02:00
rodzic 351db1fd5b
commit 7ff624b51b
7 zmienionych plików z 134 dodań i 55 usunięć

Wyświetl plik

@ -0,0 +1,24 @@
# Browser support targeting 95% coverage while enabling modern features
# This targets browsers that support ES2020+ and modern CSS features
# Cover 95% of global usage
> 1%
last 2 versions
not dead
# Exclude problematic browsers
not ie 11
not op_mini all
not android <= 4.4
not samsung <= 4
# Ensure modern browser support for ES2020+ features
chrome >= 87
firefox >= 78
safari >= 14
edge >= 88
# Mobile browsers
ios >= 14
and_chr >= 87
and_ff >= 78

Wyświetl plik

@ -100,6 +100,7 @@
"@vue/eslint-config-typescript": "12.0.0",
"@vue/test-utils": "2.4.1",
"@vue/tsconfig": "0.6.0",
"autoprefixer": "10.4.21",
"cypress": "13.6.4",
"eslint": "8.57.0",
"eslint-config-standard": "17.1.0",
@ -115,6 +116,7 @@
"msw-auto-mock": "0.18.0",
"openapi-typescript": "7.6.0",
"patch-package": "8.0.0",
"postcss": "8.5.6",
"rollup-plugin-visualizer": "5.9.0",
"sass": "1.68.0",
"sinon": "15.0.2",

Wyświetl plik

@ -0,0 +1,18 @@
export default {
plugins: {
autoprefixer: {
overrideBrowserslist: [
'> 1%',
'last 2 versions',
'not dead',
'not ie 11',
'not op_mini all',
'chrome >= 87',
'firefox >= 78',
'safari >= 14',
'edge >= 88',
'ios >= 14'
]
}
}
}

Wyświetl plik

@ -3,19 +3,9 @@ export interface Token {
value: string
}
/**
* Normalizes a query string by splitting it into tokens while respecting quoted phrases.
*
* @param query - The input query string to normalize
* @returns Array of normalized tokens with quoted phrases preserved as single tokens
*
* @example
* ```
* normalizeQuery('this is "my query" go')
* // Returns: ['this', 'is', 'my query', 'go']
* ```
*/
export function normalizeQuery (query: string): string[] {
// given a string such as 'this is "my query" go', returns
// an array of tokens like this: ['this', 'is', 'my query', 'go']
if (!query) return []
const match = query.match(/\\?.|^$/g)
@ -42,46 +32,51 @@ const unquote = (str: string) => {
return str
}
const quoteIfNecessary = (str: string) =>
str.includes(' ')
? `"${str}"`
: str
/**
* Parses an array of normalized query tokens into structured Token objects.
*
* @param normalizedQuery - Array of tokens as returned by normalizeQuery
* @returns Array of Token objects with field and value properties
*
* @example
* ```
* parseTokens(['status:pending', 'hello'])
* // Returns:
* // [
* // { field: 'status', value: 'pending' },
* // { field: null, value: 'hello' }
* // ]
* ```
*/
export const parseTokens = (normalizedQuery: string[]): Token[] =>
normalizedQuery.map(t => {
// Split the token on ":" to separate field from value
export function parseTokens (normalizedQuery: string[]): Token[] {
// given an array of tokens as returned by normalizeQuery,
// returns a list of objects such as [
// {
// field: 'status',
// value: 'pending'
// },
// {
// field: null,
// value: 'hello'
// }
// ]
return normalizedQuery.map(t => {
// we split the token on ":"
const parts = t.split(/:(.+)/)
return parts.length === 1
? { field: null, value: t } // No field specified
: { field: parts[0], value: unquote(parts[1]) } // Field:value format, remove quotes
if (parts.length === 1) {
// no field specified
return { field: null, value: t }
}
// first item is the field, second is the value, possibly quoted
const [field, value] = parts
// we remove surrounding quotes if any
return { field, value: unquote(value) }
})
}
export function compileTokens (tokens: Token[]) {
// given a list of tokens as returned by parseTokens,
// returns a string query
const parts = tokens.map(token => {
const { field } = token
let { value } = token
if (value.includes(' ')) {
value = `"${value}"`
}
if (field) {
return `${field}:${value}`
}
return value
})
/**
* Compiles an array of Token objects back into a query string.
*
* @param tokens - Array of Token objects as returned by parseTokens
* @returns A formatted query string
*/
export const compileTokens = (tokens: Token[]) =>
tokens.map(({field, value}) =>{
field
? `${field}:${quoteIfNecessary(value)}`
: quoteIfNecessary(value)
})
.join(' ')
return parts.join(' ')
}

Wyświetl plik

@ -6,7 +6,7 @@
"noUnusedLocals": true,
"noImplicitAny": true,
"experimentalDecorators": true,
"lib": ["ES2022", "DOM", "DOM.Iterable"],
"lib": ["ES2023", "DOM", "DOM.Iterable"],
"typeRoots": ["node_modules", "node_modules/@types"],
"types": [
"vitest/globals",

Wyświetl plik

@ -4,6 +4,7 @@ import { VitePWA } from 'vite-plugin-pwa'
import { fileURLToPath, URL } from 'node:url'
import UnoCSS from 'unocss/vite'
import manifest from './pwa-manifest.json'
import VueI18n from '@intlify/unplugin-vue-i18n/vite'
@ -93,7 +94,14 @@ export default defineConfig(({ mode }) => ({
}
}
},
esbuild: {
target: 'es2020',
supported: {
'top-level-await': true
}
},
build: {
target: ['es2020', 'chrome87', 'firefox78', 'safari14', 'edge88'],
sourcemap: true,
// https://rollupjs.org/configuration-options/
rollupOptions: {

Wyświetl plik

@ -4434,6 +4434,18 @@ automation-events@^7.0.9:
"@babel/runtime" "^7.27.6"
tslib "^2.8.1"
autoprefixer@10.4.21:
version "10.4.21"
resolved "https://registry.yarnpkg.com/autoprefixer/-/autoprefixer-10.4.21.tgz#77189468e7a8ad1d9a37fbc08efc9f480cf0a95d"
integrity sha512-O+A6LWV5LDHSJD3LjHYoNi4VLsj/Whi7k6zG12xTYaU4cQ8oxQGckXNX8cRHK5yOZ/ppVHe0ZBXGzSV9jXdVbQ==
dependencies:
browserslist "^4.24.4"
caniuse-lite "^1.0.30001702"
fraction.js "^4.3.7"
normalize-range "^0.1.2"
picocolors "^1.1.1"
postcss-value-parser "^4.2.0"
available-typed-arrays@^1.0.7:
version "1.0.7"
resolved "https://registry.yarnpkg.com/available-typed-arrays/-/available-typed-arrays-1.0.7.tgz#a5cc375d6a03c2efc87a553f3e0b1522def14846"
@ -4647,7 +4659,7 @@ browserify-zlib@^0.2.0:
dependencies:
pako "~1.0.5"
browserslist@^4.24.0, browserslist@^4.25.1:
browserslist@^4.24.0, browserslist@^4.24.4, browserslist@^4.25.1:
version "4.25.1"
resolved "https://registry.yarnpkg.com/browserslist/-/browserslist-4.25.1.tgz#ba9e8e6f298a1d86f829c9b975e07948967bb111"
integrity sha512-KGj0KoOMXLpSNkkEI6Z6mShmQy0bc1I+T7K9N81k4WWMrfz+6fQ6es80B/YLAeRoKvjYE1YSHHOW1qe9xIVzHw==
@ -4783,6 +4795,11 @@ callsites@^3.0.0:
resolved "https://registry.yarnpkg.com/callsites/-/callsites-3.1.0.tgz#b3630abd8943432f54b3f0519238e33cd7df2f73"
integrity sha512-P8BjAsXvZS+VIDUI11hHCQEv74YT67YUi5JJFNWIqL235sBmjX4+qx9Muvls5ivyNENctx46xQLQ3aTuE7ssaQ==
caniuse-lite@^1.0.30001702:
version "1.0.30001731"
resolved "https://registry.yarnpkg.com/caniuse-lite/-/caniuse-lite-1.0.30001731.tgz#277c07416ea4613ec564e5b0ffb47e7b60f32e2f"
integrity sha512-lDdp2/wrOmTRWuoB5DpfNkC0rJDU8DqRa6nYL6HK6sytw70QMopt/NIc/9SM7ylItlBWfACXk0tEn37UWM/+mg==
caniuse-lite@^1.0.30001726:
version "1.0.30001727"
resolved "https://registry.yarnpkg.com/caniuse-lite/-/caniuse-lite-1.0.30001727.tgz#22e9706422ad37aa50556af8c10e40e2d93a8b85"
@ -6411,6 +6428,11 @@ form-data@^4.0.0, form-data@~4.0.0:
hasown "^2.0.2"
mime-types "^2.1.12"
fraction.js@^4.3.7:
version "4.3.7"
resolved "https://registry.yarnpkg.com/fraction.js/-/fraction.js-4.3.7.tgz#06ca0085157e42fda7f9e726e79fefc4068840f7"
integrity sha512-ZsDfxO51wGAXREY55a7la9LScWpwv9RxIrYABrlvOFBlH/ShPnrtsXeuUIfXKKOVicNxQ+o8JTbJvjS4M89yew==
fs-extra@^11.2.0:
version "11.3.0"
resolved "https://registry.yarnpkg.com/fs-extra/-/fs-extra-11.3.0.tgz#0daced136bbaf65a555a326719af931adc7a314d"
@ -8215,6 +8237,11 @@ normalize-path@^3.0.0, normalize-path@~3.0.0:
resolved "https://registry.yarnpkg.com/normalize-path/-/normalize-path-3.0.0.tgz#0dcd69ff23a1c9b11fd0978316644a0388216a65"
integrity sha512-6eZs5Ls3WtCisHWp9S2GUy8dqkpGi4BVSz3GaqiE6ezub0512ESztXUwUB6C6IKbQkY2Pnb/mD4WYojCRwcwLA==
normalize-range@^0.1.2:
version "0.1.2"
resolved "https://registry.yarnpkg.com/normalize-range/-/normalize-range-0.1.2.tgz#2d10c06bdfd312ea9777695a4d28439456b75942"
integrity sha512-bdok/XvKII3nUpklnV6P2hxtMNrCboOjAcyBuQnWEhO665FwrSNRxU+AqpsyvO6LgGYPspN+lu5CLtw4jPRKNA==
npm-run-path@^4.0.0:
version "4.0.1"
resolved "https://registry.yarnpkg.com/npm-run-path/-/npm-run-path-4.0.1.tgz#b7ecd1e5ed53da8e37a55e1c2269e0b97ed748ea"
@ -8790,7 +8817,12 @@ postcss-selector-parser@^6.0.15:
cssesc "^3.0.0"
util-deprecate "^1.0.2"
postcss@^8.4.32, postcss@^8.4.38, postcss@^8.4.43, postcss@^8.4.48, postcss@^8.5.6:
postcss-value-parser@^4.2.0:
version "4.2.0"
resolved "https://registry.yarnpkg.com/postcss-value-parser/-/postcss-value-parser-4.2.0.tgz#723c09920836ba6d3e5af019f92bc0971c02e514"
integrity sha512-1NNCs6uurfkVbeXG4S8JFT9t19m45ICnif8zWLd5oPSZ50QnwMfK+H3jv408d4jw/7Bttv5axS5IiHoLaVNHeQ==
postcss@8.5.6, postcss@^8.4.32, postcss@^8.4.38, postcss@^8.4.43, postcss@^8.4.48, postcss@^8.5.6:
version "8.5.6"
resolved "https://registry.yarnpkg.com/postcss/-/postcss-8.5.6.tgz#2825006615a619b4f62a9e7426cc120b349a8f3c"
integrity sha512-3Ybi1tAuwAP9s0r1UQ2J4n5Y0G05bJkpUIO0/bI9MhwmD70S5aTWbXGBwxHrelT+XM1k6dM0pk+SwNkpTRN7Pg==