Merge pull request #414 from iflytek/fix/web-js-compat

fix(web): restore compatibility with Chromium 83 (Debian 10)
This commit is contained in:
dongmucat 2026-05-09 15:03:02 +08:00 committed by GitHub
commit ab6fd07530
No known key found for this signature in database
GPG key ID: B5690EEEBB952194
4 changed files with 104 additions and 8 deletions

4
web/.browserslistrc Normal file
View file

@ -0,0 +1,4 @@
Chrome >= 83
Edge >= 83
Firefox >= 78
Safari >= 14

View file

@ -4,6 +4,7 @@
* Deployments inject `/runtime-config.js` at startup, and this file guarantees the app sees either
* that config or a safe fallback object before importing the main entry.
*/
import './legacy-polyfills'
async function loadRuntimeConfig() {
await new Promise<void>((resolve, reject) => {
const script = document.createElement('script')
@ -16,14 +17,16 @@ async function loadRuntimeConfig() {
}
function ensureRuntimeConfigFallback() {
window.__SKILLHUB_RUNTIME_CONFIG__ ??= {
apiBaseUrl: '',
appBaseUrl: '',
authDirectEnabled: 'false',
authDirectProvider: '',
authSessionBootstrapEnabled: 'false',
authSessionBootstrapProvider: '',
authSessionBootstrapAuto: 'false',
if (!window.__SKILLHUB_RUNTIME_CONFIG__) {
window.__SKILLHUB_RUNTIME_CONFIG__ = {
apiBaseUrl: '',
appBaseUrl: '',
authDirectEnabled: 'false',
authDirectProvider: '',
authSessionBootstrapEnabled: 'false',
authSessionBootstrapProvider: '',
authSessionBootstrapAuto: 'false',
}
}
}

View file

@ -0,0 +1,78 @@
/**
* Polyfills for older browsers (e.g. Chromium 83 on Debian 10) that lack a few
* ES2021+ runtime methods used by bundled dependencies. esbuild only transpiles
* syntax, not runtime APIs, so we patch the prototypes here before any other
* module runs.
*
* TypeScript's lib target is ES2020, so the methods we patch are referenced via
* string keys / loose casts to avoid compile-time type errors.
*/
type AnyStringReplacer = (substring: string, ...args: unknown[]) => string
const StringProto = String.prototype as unknown as Record<string, unknown>
const ArrayProto = Array.prototype as unknown as Record<string, unknown>
const ObjectCtor = Object as unknown as Record<string, unknown>
if (typeof StringProto.replaceAll !== 'function') {
Object.defineProperty(String.prototype, 'replaceAll', {
configurable: true,
writable: true,
value: function replaceAll(
this: string,
search: string | RegExp,
replacement: string | AnyStringReplacer,
): string {
if (search instanceof RegExp) {
if (!search.flags.includes('g')) {
throw new TypeError('String.prototype.replaceAll called with a non-global RegExp argument')
}
return this.replace(search, replacement as string)
}
const needle = String(search)
if (needle === '') {
return this.replace(new RegExp('', 'g'), replacement as string)
}
const escaped = needle.replace(/[-\\^$*+?.()|[\]{}]/g, '\\$&')
return this.replace(new RegExp(escaped, 'g'), replacement as string)
},
})
}
if (typeof ArrayProto.at !== 'function') {
Object.defineProperty(Array.prototype, 'at', {
configurable: true,
writable: true,
value: function at(this: unknown[], index: number) {
const len = this.length
const i = Math.trunc(index) || 0
const resolved = i < 0 ? len + i : i
if (resolved < 0 || resolved >= len) return undefined
return this[resolved]
},
})
}
if (typeof StringProto.at !== 'function') {
Object.defineProperty(String.prototype, 'at', {
configurable: true,
writable: true,
value: function at(this: string, index: number) {
const len = this.length
const i = Math.trunc(index) || 0
const resolved = i < 0 ? len + i : i
if (resolved < 0 || resolved >= len) return undefined
return this.charAt(resolved)
},
})
}
if (typeof ObjectCtor.hasOwn !== 'function') {
Object.defineProperty(Object, 'hasOwn', {
configurable: true,
writable: true,
value: function hasOwn(target: object, property: PropertyKey) {
return Object.prototype.hasOwnProperty.call(target, property)
},
})
}

View file

@ -2,6 +2,8 @@ import { defineConfig } from 'vite'
import react from '@vitejs/plugin-react'
import path from 'path'
const LEGACY_BROWSER_TARGETS = ['chrome83', 'edge83', 'firefox78', 'safari14']
export default defineConfig({
plugins: [react()],
resolve: {
@ -9,6 +11,15 @@ export default defineConfig({
'@': path.resolve(__dirname, './src'),
},
},
build: {
target: LEGACY_BROWSER_TARGETS,
cssTarget: LEGACY_BROWSER_TARGETS,
},
optimizeDeps: {
esbuildOptions: {
target: LEGACY_BROWSER_TARGETS,
},
},
test: {
exclude: ['**/node_modules/**', '**/e2e/**'],
},