Fix loading order of locale polyfills

Refs FOO-2929
flag=none

There are not a lot of locales that need to be
polyfilled (native browser support exists in almost
all cases), so it's no surprise that it was not
properly loading some of the locale data, due to
us not checking for prerequisite polyfills and also
to not loading polyfills in the required order.

See https://formatjs.io/docs/polyfills/ ... there's
a pretty clear tree of dependencies.

This commit changes the order of loading (if needed)
of the polyfills and also brings in a couple of
polyfills that I didn't even realize were required
for what we are loading. Also I removed a not-longer-
needed directory in /packages that used to handle the
locale polyfills before it was moved to the "engine"
stuff; also now the dependencies are in the correct
package.json.

Test plan:
* re-run the test plan for g/294705
* If you are an expert at how dates should look in
  Welsh or Armenian, make sure they're really being
  done correctly now?

Change-Id: Ie65ec591b9c9c063a093dec8a921759354abc37d
Reviewed-on: https://gerrit.instructure.com/c/canvas-lms/+/310469
Tested-by: Service Cloud Jenkins <svc.cloudjenkins@instructure.com>
Reviewed-by: Aaron Shafovaloff <ashafovaloff@instructure.com>
Reviewed-by: August Thornton <august@instructure.com>
QA-Review: Charley Kline <ckline@instructure.com>
Product-Review: Charley Kline <ckline@instructure.com>
This commit is contained in:
Charley Kline 2023-02-06 11:11:51 -06:00
parent 850cfe964c
commit 3dfe09c5e1
9 changed files with 174 additions and 233 deletions

View File

@ -133,7 +133,6 @@
"immer": "^3",
"immutability-helper": "^3",
"immutable": "^3.8.2",
"intl-polyfills": "*",
"is-valid-domain": "^0.0.11",
"jquery": "https://github.com/instructure/jquery.git#1.7.2-with-AMD-and-CommonJS",
"jquery-getscrollbarwidth": "^1.0.0",

View File

@ -1,32 +0,0 @@
/* Copyright (C) 2020 - present Instructure, Inc.
*
* This file is part of Canvas.
*
* Canvas is free software: you can redistribute it and/or modify it under
* the terms of the GNU Affero General Public License as published by the Free
* Software Foundation, version 3 of the License.
*
* Canvas is distributed in the hope that it will be useful, but WITHOUT ANY
* WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR
* A PARTICULAR PURPOSE. See the GNU Affero General Public License for more
* details.
*
* You should have received a copy of the GNU Affero General Public License along
* with this program. If not, see <http://www.gnu.org/licenses/>.
*/
import {installIntlPolyfills} from '../index'
describe('IntlPolyFills::', () => {
beforeAll(installIntlPolyfills)
describe('Basics', () => {
it('creates an Intl object', () => {
expect(Intl).toBeInstanceOf(Object)
})
it('has all the Intl functions we need', () => {
expect(Intl.RelativeTimeFormat).toBeInstanceOf(Function)
})
})
})

View File

@ -1,116 +0,0 @@
/*
* Copyright (C) 2020 - present Instructure, Inc.
*
* This file is part of Canvas.
*
* Canvas is free software: you can redistribute it and/or modify it under
* the terms of the GNU Affero General Public License as published by the Free
* Software Foundation, version 3 of the License.
*
* Canvas is distributed in the hope that it will be useful, but WITHOUT ANY
* WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR
* A PARTICULAR PURPOSE. See the GNU Affero General Public License for more
* details.
*
* You should have received a copy of the GNU Affero General Public License along
* with this program. If not, see <http://www.gnu.org/licenses/>.
*/
// Now that Canvas is on Node 14, there is at least some support for the ICU
// functionality. In case it is not 100% complete, though, this skeleton will
// remain in case we need to add any Intl polyfills in the future. Hopefully
// we will not ever have to.
import {shouldPolyfill as spfNF} from '@formatjs/intl-numberformat/should-polyfill'
import {shouldPolyfill as spfDTF} from '@formatjs/intl-datetimeformat/should-polyfill'
import {shouldPolyfill as spfRTF} from '@formatjs/intl-relativetimeformat/should-polyfill'
export function installIntlPolyfills() {
if (typeof window.Intl === 'undefined') window.Intl = {}
}
const shouldPolyfill = {
NumberFormat: spfNF,
DateTimeFormat: spfDTF,
RelativeTimeFormat: spfRTF
}
//
// Intl polyfills for locale-specific output of Dates, Times, Numbers, and
// Relative Times.
//
const polyfillImports = {
DateTimeFormat: async () => {
await import('@formatjs/intl-datetimeformat/polyfill-force')
return import(/* webpackIgnore: true */ '/dist/@formatjs/intl-datetimeformat/add-all-tz.js')
},
NumberFormat: () => import('@formatjs/intl-numberformat/polyfill-force'),
RelativeTimeFormat: () => import('@formatjs/intl-relativetimeformat/polyfill-force')
}
const localeImports = {
DateTimeFormat: l => import(/* webpackIgnore: true */ `/dist/@formatjs/intl-datetimeformat/locale-data/${l}.js` ),
NumberFormat: l => import(/* webpackIgnore: true */ `/dist/@formatjs/intl-numberformat/locale-data/${l}.js`),
RelativeTimeFormat: l => import(/* webpackIgnore: true */ `/dist/@formatjs/intl-relativetimeformat/locale-data/${l}.js`),
}
// Check to see if there is native support in the specified Intl subsystem for
// any of the locales given in the list (they are tried in order). If there is not
// load an appropriate locale polyfill for the list of locales from @formatjs.
//
// Return value is a Promise which resolves to an hash with the following properties:
//
// subsys - the subsystem being operated on
// locale - the locale that was requested
// loaded - the locale that was actually polyfilled (is missing, an error occurred)
// source - how that locale is available ('native' or 'polyfill')
// error - if an error occurred, contains the error message
//
// In most cases, if none of the locales in the list have either native support
// nor can any of them be polyfilled, the subsystem will fall back to 'en' as a
// locale (this is what the browser's native Intl would also do).
//
async function doPolyfill(givenLocales, subsys) {
// 'en' is the final fallback, don't settle for that unless it's the only
// available locale, in which case we do nothing.
const locales = [...givenLocales]
if (locales.length < 1 || (locales.length === 1 && locales[0] === 'en'))
return {subsys, locale: 'en', source: 'native'}
if (locales.slice(-1)[0] === 'en') locales.pop()
try {
/* eslint-disable no-await-in-loop */ // it's actually fine in for-loops
for (const locale of locales) {
const native = Intl[subsys].supportedLocalesOf([locale])
if (native.length > 0) return {subsys, locale: native[0], source: 'native'}
const doable = shouldPolyfill[subsys](locale)
if (!doable || doable === 'en') continue
const origSubsys = Intl[subsys]
await polyfillImports[subsys]()
await localeImports[subsys](doable)
Intl[`Native${subsys}`] = origSubsys
return {subsys, locale, source: 'polyfill', loaded: doable}
}
/* eslint-enable no-await-in-loop */
return {subsys, locale: locales[0], error: 'polyfill unavailable'}
} catch (e) {
return {subsys, locale: locales[0], error: e.message}
}
}
// (Possibly) load the Intl polyfill for each of the given subsystems,
// for the best available locale in the given list.
// Returns a Promise that resolves to an array of the result objects
// (see above) for each subsystem.
// It is an error for the subsystems array to contain the name of an
// Intl subsystem that we are not prepared to polyfill.
export function loadAllLocalePolyfills(locales, subsystems) {
subsystems.forEach(sys => {
if (!Object.keys(shouldPolyfill).includes(sys)) {
throw new RangeError(`Intl subsystem ${sys} is not polyfillable!`)
}
})
return Promise.all(subsystems.map(sys => doPolyfill(locales, sys)))
}

View File

@ -1,12 +0,0 @@
{
"name": "intl-polyfills",
"private": true,
"version": "1.0.0",
"author": "Charley Kline, ckline@instructure.com",
"main": "./index.js",
"dependencies": {
"@formatjs/intl-datetimeformat": "^4.5",
"@formatjs/intl-numberformat": "^7.4",
"@formatjs/intl-relativetimeformat": "^9.5"
}
}

View File

@ -17,6 +17,9 @@
*/
import type {Capability} from '@instructure/updown';
import {shouldPolyfill as spfGCL} from '@formatjs/intl-getcanonicallocales/should-polyfill';
import {shouldPolyfill as spfL} from '@formatjs/intl-locale/should-polyfill';
import {shouldPolyfill as spfPR} from '@formatjs/intl-pluralrules/should-polyfill';
import {shouldPolyfill as spfNF} from '@formatjs/intl-numberformat/should-polyfill';
import {shouldPolyfill as spfDTF} from '@formatjs/intl-datetimeformat/should-polyfill';
import {shouldPolyfill as spfRTF} from '@formatjs/intl-relativetimeformat/should-polyfill';
@ -41,18 +44,33 @@ type PolyfillerUpValue = {
};
type PolyfillerArgs = {
subsys: Function
should: (locale: string) => string | undefined
subsysName: string
should: (locale?: string) => string | boolean | undefined
polyfill: () => Promise<unknown>
localeLoader: (locale: string) => Promise<void>
localeLoader?: (locale: string) => Promise<void>
};
function polyfillerFactory({subsys, should, polyfill, localeLoader}: PolyfillerArgs): Capability {
const subsysName = subsys.name;
function polyfillerFactory({subsysName, should, polyfill, localeLoader}: PolyfillerArgs): Capability {
const subsys = Intl[subsysName];
const native = subsys;
const nativeName = 'Native' + subsysName;
async function up(givenLocales: Array<string>): Promise<PolyfillerUpValue> {
// If this subsystem doesn't provide `supportedLocalesOf` then it is not
// locale-specific, so we merely have to check if it is there.
if (!(subsys?.supportedLocalesOf instanceof Function)) {
if (should()) {
await polyfill();
return {subsys: subsysName, locale: 'all locales', source: 'polyfill' };
}
return {subsys: subsysName, locale: 'all locales', source: 'native'};
}
// If on the other hand it IS locale-specific, make sure we were actually
// passed a localeLoader function
if (!(localeLoader instanceof Function))
throw new TypeError(`polyfillerFactory needs localeLoader for ${subsysName}`);
// 'en' is the final fallback, don't settle for that unless it's the only
// available locale, in which case we do nothing.
const locales = [...givenLocales];
@ -66,14 +84,16 @@ function polyfillerFactory({subsys, should, polyfill, localeLoader}: PolyfillerA
for (const locale of locales) {
const nativeSupport = Intl[subsysName].supportedLocalesOf([locale]);
if (nativeSupport.length > 0)
return {subsys: subsysName, locale: native[0], source: 'native'};
return {subsys: subsysName, locale: nativeSupport[0], source: 'native'};
const doable = should(locale);
if (!doable || doable === 'en') continue;
await polyfill();
await localeLoader(doable);
if (typeof doable === 'string') await localeLoader(doable);
Intl[nativeName] = native;
return {subsys: subsysName, locale, source: 'polyfill', loaded: doable};
const retval: PolyfillerUpValue = {subsys: subsysName, locale, source: 'polyfill'};
if (typeof doable === 'string') retval.loaded = doable;
return retval;
}
/* eslint-enable no-await-in-loop */
return {subsys: subsysName, locale: fallback, error: 'polyfill unavailable'};
@ -84,8 +104,10 @@ function polyfillerFactory({subsys, should, polyfill, localeLoader}: PolyfillerA
}
function down(): void {
delete Intl[nativeName];
Intl[subsysName] = native;
if (subsysName) {
delete Intl[nativeName];
Intl[subsysName] = native;
}
}
return {
@ -97,47 +119,90 @@ function polyfillerFactory({subsys, should, polyfill, localeLoader}: PolyfillerA
};
}
const intlSubsystemsInUse: Capability[] = [
polyfillerFactory({
subsys: Intl.DateTimeFormat,
const subsystems: { [subsys: string]: Capability } = {
getcanonicallocales: polyfillerFactory({
subsysName: 'getCanonicalLocales',
should: spfGCL,
polyfill: () => import('@formatjs/intl-getcanonicallocales/polyfill'),
}),
locale: polyfillerFactory({
subsysName: 'Locale',
should: spfL,
polyfill: () => import('@formatjs/intl-locale/polyfill-force'),
}),
pluralrules: polyfillerFactory({
subsysName: 'PluralRules',
should: spfPR,
polyfill: () => import('@formatjs/intl-pluralrules/polyfill-force'),
localeLoader: (l: string) => import(/* webpackIgnore: true */ localeDataFor('pluralrules', l)),
}),
datetimeformat: polyfillerFactory({
subsysName: 'DateTimeFormat',
should: spfDTF,
polyfill: async () => {
await import('@formatjs/intl-datetimeformat/polyfill-force');
await import(/* webpackIgnore: true */ `${FORMAT_JS_DIR}/intl-datetimeformat/add-all-tz.js`);
},
localeLoader: (l: string) =>
import(/* webpackIgnore: true */ localeDataFor('datetimeformat', l)),
localeLoader: (l: string) => import(/* webpackIgnore: true */ localeDataFor('datetimeformat', l)),
}),
polyfillerFactory({
subsys: Intl.NumberFormat,
numberformat: polyfillerFactory({
subsysName: 'NumberFormat',
should: spfNF,
polyfill: () => import('@formatjs/intl-numberformat/polyfill-force'),
localeLoader: (l: string) => import(/* webpackIgnore: true */ localeDataFor('numberformat', l)),
}),
polyfillerFactory({
subsys: Intl.RelativeTimeFormat,
relativetimeformat: polyfillerFactory({
subsysName: 'RelativeTimeFormat',
should: spfRTF,
polyfill: () => import('@formatjs/intl-relativetimeformat/polyfill-force'),
localeLoader: (l: string) =>
import(/* webpackIgnore: true */ localeDataFor('relativetimeformat', l)),
localeLoader: (l: string) => import(/* webpackIgnore: true */ localeDataFor('relativetimeformat', l)),
}),
];
};
function polyfillUp(...polyfills: unknown[]) {
polyfills.forEach(polyfillResult => {
if (typeof polyfillResult === 'undefined') return;
const r = polyfillResult as PolyfillerUpValue;
if (r.error)
// eslint-disable-next-line no-console
console.error(`${r.subsys} polyfill for locale "${r.locale}" failed: ${r.error}`);
if (r.source === 'polyfill')
// eslint-disable-next-line no-console
console.info(`${r.subsys} polyfilled "${r.loaded}" for locale "${r.locale}"`);
});
}
// See https://formatjs.io/docs/polyfills/ for a good graphical explanation of why
// these all have to be loaded in the dependent order specified.
const level1: Capability = {
up: polyfillUp,
requires: [subsystems.getcanonicallocales],
};
const level2: Capability = {
up: polyfillUp,
requires: [level1, subsystems.locale],
};
const level3: Capability = {
up: polyfillUp,
requires: [level2, subsystems.pluralrules],
};
const level4: Capability = {
up: polyfillUp,
requires: [level3, subsystems.numberformat]
};
const IntlPolyfills: Capability = {
up: (...polyfills) => {
polyfills.forEach(polyfillResult => {
const r = polyfillResult as PolyfillerUpValue;
if (r.error)
// eslint-disable-next-line no-console
console.error(`${r.subsys} polyfill for locale "${r.locale}" failed: ${r.error}`);
if (r.source === 'polyfill')
// eslint-disable-next-line no-console
console.info(`${r.subsys} polyfilled "${r.loaded}" for locale "${r.locale}"`);
});
},
requires: intlSubsystemsInUse,
up:polyfillUp,
requires: [level4, subsystems.datetimeformat, subsystems.relativetimeformat],
};
export default IntlPolyfills;

View File

@ -18,6 +18,12 @@
declare module '@canvas/i18n'
declare module '@canvas/do-fetch-api-effect'
// a little disappointed this has to be done by hand
declare namespace Intl {
function getCanonicalLocales(locales: string | string[]): string[];
function Locale(locale: string): object;
}
declare module '*.json' {
const value: {[key: string]: string};
export default value;

View File

@ -13,7 +13,13 @@
"build:canvas": "yarn run check && yarn run lint"
},
"dependencies": {
"@instructure/updown": "^1.3"
"@instructure/updown": "^1.3",
"@formatjs/intl-getcanonicallocales": "^2",
"@formatjs/intl-locale": "^3",
"@formatjs/intl-pluralrules": "^5",
"@formatjs/intl-datetimeformat": "^6",
"@formatjs/intl-numberformat": "^8",
"@formatjs/intl-relativetimeformat": "^11"
},
"eslintConfig": {
"env": {

View File

@ -3,7 +3,7 @@
"allowJs": true, // allows importing of .js files from .ts files
"esModuleInterop": true, // more accurately transpiles to the ES6 module spec (maybe not needed w/ babel transpilation)
"isolatedModules": true, // required to adhere to babel's single-file transpilation process
"lib": ["dom", "es2020", "esnext"], // include types for DOM APIs and standard JS up to ES2020
"lib": ["dom", "ES2020", "ES2020.Intl"], // include types for DOM APIs and standard JS up to ES2020
"module": "es2020", // support the most modern ES6-style module syntax
"moduleResolution": "node", // required for non-commonjs imports
"noEmit": true, // don't generate transpiled JS files, source-maps, or .d.ts files for Canvas source code

View File

@ -1495,47 +1495,72 @@
minimatch "^3.0.4"
strip-json-comments "^3.1.1"
"@formatjs/ecma402-abstract@1.11.2":
version "1.11.2"
resolved "https://registry.yarnpkg.com/@formatjs/ecma402-abstract/-/ecma402-abstract-1.11.2.tgz#7f01595e6985a28983aae26bede9b78b273fee3d"
integrity sha512-qDgOL0vtfJ51cc0pRbFB/oXc4qDbamG22Z6h/QWy6FBxaQgppiy8JF0iYbmNO35cC8r88bQGsgfd/eM6/eTEQQ==
"@formatjs/ecma402-abstract@1.14.3":
version "1.14.3"
resolved "https://registry.yarnpkg.com/@formatjs/ecma402-abstract/-/ecma402-abstract-1.14.3.tgz#6428f243538a11126180d121ce8d4b2f17465738"
integrity sha512-SlsbRC/RX+/zg4AApWIFNDdkLtFbkq3LNoZWXZCE/nHVKqoIJyaoQyge/I0Y38vLxowUn9KTtXgusLD91+orbg==
dependencies:
"@formatjs/intl-localematcher" "0.2.23"
tslib "^2.1.0"
"@formatjs/intl-localematcher" "0.2.32"
tslib "^2.4.0"
"@formatjs/intl-datetimeformat@^4.5":
version "4.5.1"
resolved "https://registry.yarnpkg.com/@formatjs/intl-datetimeformat/-/intl-datetimeformat-4.5.1.tgz#588d5d9875bd19417873fdc67112456c3b28b2ff"
integrity sha512-4Ncg6bC5X8QyA4jsyHCGyO84aLulqpKXbxrvsPF1Y4deMFcYBVzg43fT/Dbp6ZqfdViGJ3K/RTBD8XlAD8evIg==
"@formatjs/intl-datetimeformat@^6":
version "6.4.3"
resolved "https://registry.yarnpkg.com/@formatjs/intl-datetimeformat/-/intl-datetimeformat-6.4.3.tgz#1ef2b578186c9843603d1c07f9f9c369f5ab292d"
integrity sha512-+fLPuBHTZZqJJzjfK4cqamW00j8yCtcQekkAVvfSn/2xibJaQvhqrpDs++4/qM847lZtnDU3q33lelM3H5JMbQ==
dependencies:
"@formatjs/ecma402-abstract" "1.11.2"
"@formatjs/intl-localematcher" "0.2.23"
tslib "^2.1.0"
"@formatjs/ecma402-abstract" "1.14.3"
"@formatjs/intl-localematcher" "0.2.32"
tslib "^2.4.0"
"@formatjs/intl-localematcher@0.2.23":
version "0.2.23"
resolved "https://registry.yarnpkg.com/@formatjs/intl-localematcher/-/intl-localematcher-0.2.23.tgz#5a0b1d81df1f392ecf37e556ca7040a7ec9f72e8"
integrity sha512-oCe2TOciTtB1bEbJ85EvYrXQxD0epusmVJfJ7AduO0tlbXP42CmDIYIH2CZ+kP2GE+PTLQD1Hbt9kpOpl939MQ==
"@formatjs/intl-getcanonicallocales@2.0.5", "@formatjs/intl-getcanonicallocales@^2":
version "2.0.5"
resolved "https://registry.yarnpkg.com/@formatjs/intl-getcanonicallocales/-/intl-getcanonicallocales-2.0.5.tgz#d405cf5221f49531e62ecfde50acdfb62fcc4854"
integrity sha512-YOk+Fa5gpPq5bdpm8JDAY5bkfCkR+NENZKQbLHeqhm8JchHcclPwZ9FU48gYGg3CW6Wi/cTCOvmOrzsIhlkr0w==
dependencies:
tslib "^2.1.0"
tslib "^2.4.0"
"@formatjs/intl-numberformat@^7.4":
version "7.4.1"
resolved "https://registry.yarnpkg.com/@formatjs/intl-numberformat/-/intl-numberformat-7.4.1.tgz#41b452505e4e3864ddb5e5c5e176dd467bc00abd"
integrity sha512-9F+n4fO/u9p5bvUHDZK5pvuVqFKjrWRe3SVf5D216IZcjGO8veeRPDPjja+YoRpNDwR0ITIyMlV+IdUHXmfwvA==
"@formatjs/intl-locale@^3":
version "3.0.11"
resolved "https://registry.yarnpkg.com/@formatjs/intl-locale/-/intl-locale-3.0.11.tgz#6b3bee5692fab3c70a0ce9c642c2a2bec3700aa4"
integrity sha512-gLEX9kzebBjIVCkXMMN+VFMUV2aj0vhmrP+nke2muxUSJ3fLs/DJjlkv+s59rAL3nNaGdvphqKLhQsul0mmhAw==
dependencies:
"@formatjs/ecma402-abstract" "1.11.2"
"@formatjs/intl-localematcher" "0.2.23"
tslib "^2.1.0"
"@formatjs/ecma402-abstract" "1.14.3"
"@formatjs/intl-getcanonicallocales" "2.0.5"
tslib "^2.4.0"
"@formatjs/intl-relativetimeformat@^9.5":
version "9.5.1"
resolved "https://registry.yarnpkg.com/@formatjs/intl-relativetimeformat/-/intl-relativetimeformat-9.5.1.tgz#d00f2bf9cf9840da3c0e8b27a2ea26e12ecc376a"
integrity sha512-A4kL6tTjcvVgyKH33BREBWBW2nPgTJSkhjKcTGfNHdmvWN8WAbDovRVOUZ6UmufRW7ZZfoDA8QT7sEOKuxKqyg==
"@formatjs/intl-localematcher@0.2.32":
version "0.2.32"
resolved "https://registry.yarnpkg.com/@formatjs/intl-localematcher/-/intl-localematcher-0.2.32.tgz#00d4d307cd7d514b298e15a11a369b86c8933ec1"
integrity sha512-k/MEBstff4sttohyEpXxCmC3MqbUn9VvHGlZ8fauLzkbwXmVrEeyzS+4uhrvAk9DWU9/7otYWxyDox4nT/KVLQ==
dependencies:
"@formatjs/ecma402-abstract" "1.11.2"
"@formatjs/intl-localematcher" "0.2.23"
tslib "^2.1.0"
tslib "^2.4.0"
"@formatjs/intl-numberformat@^8":
version "8.3.3"
resolved "https://registry.yarnpkg.com/@formatjs/intl-numberformat/-/intl-numberformat-8.3.3.tgz#bfba1a90a22a28479dae4cf8cc16fb81ff659a36"
integrity sha512-11mSFZb5RsCVZMVaHbRcDYNxQ+tsstReL62AJcTCBZdvAZMqECOEsDkJODZ90nf/ClKqp0/KxwVlshxprn22Nw==
dependencies:
"@formatjs/ecma402-abstract" "1.14.3"
"@formatjs/intl-localematcher" "0.2.32"
tslib "^2.4.0"
"@formatjs/intl-pluralrules@^5":
version "5.1.8"
resolved "https://registry.yarnpkg.com/@formatjs/intl-pluralrules/-/intl-pluralrules-5.1.8.tgz#11eeca3cde088fd68d258a09b0791b327a8eb019"
integrity sha512-uevO916EWoeuueqeNzHjnUzpfWZzXFJibC/sEvPR/ZiZH5btWuOLeJLdb1To4nMH8ZJQlmAf8SDpFf+eWvz5lQ==
dependencies:
"@formatjs/ecma402-abstract" "1.14.3"
"@formatjs/intl-localematcher" "0.2.32"
tslib "^2.4.0"
"@formatjs/intl-relativetimeformat@^11":
version "11.1.8"
resolved "https://registry.yarnpkg.com/@formatjs/intl-relativetimeformat/-/intl-relativetimeformat-11.1.8.tgz#8bce76cc396d143558cb8335f2b4b12b85259413"
integrity sha512-5evaQTfSiSNLXt6GxfPEq2YGtPCH2UKNEoPB0Rg9+3IPSPp5o39Oks91bDZE8SCk5rK2ABlmrc8ZnMILfZhl/Q==
dependencies:
"@formatjs/ecma402-abstract" "1.14.3"
"@formatjs/intl-localematcher" "0.2.32"
tslib "^2.4.0"
"@fullstory/browser@^1.4.10":
version "1.4.10"
@ -24716,10 +24741,10 @@ tslib@^1.10.0, tslib@^1.8.1, tslib@^1.9.0, tslib@^1.9.3:
resolved "https://registry.yarnpkg.com/tslib/-/tslib-1.14.1.tgz#cf2d38bdc34a134bcaf1091c41f6619e2f672d00"
integrity sha512-Xni35NKzjgMrwevysHTCArtLDpPvye8zV/0E4EyYn43P7/7qvQwPh9BGkHewbMulVntbigmcT7rdX3BNo9wRJg==
tslib@^2.0.0, tslib@^2.0.1, tslib@^2.0.3, tslib@^2.1.0, tslib@^2.2.0, tslib@^2.3.0:
version "2.3.1"
resolved "https://registry.yarnpkg.com/tslib/-/tslib-2.3.1.tgz#e8a335add5ceae51aa261d32a490158ef042ef01"
integrity sha512-77EbyPPpMz+FRFRuAFlWMtmgUWGe9UOG2Z25NqCwiIjRhOf5iKGuzSe5P2w1laq+FkRy4p+PCuVkJSGkzTEKVw==
tslib@^2.0.0, tslib@^2.0.1, tslib@^2.0.3, tslib@^2.1.0, tslib@^2.2.0, tslib@^2.3.0, tslib@^2.4.0:
version "2.5.0"
resolved "https://registry.yarnpkg.com/tslib/-/tslib-2.5.0.tgz#42bfed86f5787aeb41d031866c8f402429e0fddf"
integrity sha512-336iVw3rtn2BUK7ORdIAHTyxHGRIHVReokCR3XjbckJMK7ms8FysBfhLR8IXnAgy7T0PTPNBWKiH514FOW/WSg==
tsscmp@1.0.6:
version "1.0.6"