diff --git a/README.md b/README.md
index 6ee25ae6..6c447024 100644
--- a/README.md
+++ b/README.md
@@ -10,13 +10,34 @@ Hydrogen's goals are:
- It is a standalone webapp, but can also be easily embedded into an existing website/webapp to add chat capabilities.
- Loading (unused) parts of the application after initial page load should be supported
+For embedded usage, see the [SDK instructions](doc/SDK.md).
+
If you find this interesting, come and discuss on [`#hydrogen:matrix.org`](https://matrix.to/#/#hydrogen:matrix.org).
# How to use
-Hydrogen is deployed to [hydrogen.element.io](https://hydrogen.element.io). You can run it locally `yarn install` (only the first time) and `yarn start` in the terminal, and point your browser to `http://localhost:3000`. If you prefer, you can also [use docker](doc/docker.md).
+Hydrogen is deployed to [hydrogen.element.io](https://hydrogen.element.io). You can also deploy Hydrogen on your own web server:
-Hydrogen uses symbolic links in the codebase, so if you are on Windows, have a look at [making git & symlinks work](https://github.com/git-for-windows/git/wiki/Symbolic-Links) there.
+ 1. Download the [latest release package](https://github.com/vector-im/hydrogen-web/releases).
+ 1. Extract the package to the public directory of your web server.
+ 1. If this is your first deploy:
+ 1. copy `config.sample.json` to `config.json` and if needed, make any modifications (unless you've set up your own [sygnal](https://github.com/matrix-org/sygnal) instance, you don't need to change anything in the `push` section).
+ 1. Disable caching entirely on the server for:
+ - `index.html`
+ - `sw.js`
+ - `config.json`
+ - All theme manifests referenced in the `themeManifests` of `config.json`, these files are typically called `theme-{name}.json`.
+
+ These resources will still be cached client-side by the service worker. Because of this; you'll still need to refresh the app twice before config.json changes are applied.
+
+## Set up a dev environment
+
+You can run Hydrogen locally by the following commands in the terminal:
+
+ - `yarn install` (only the first time)
+ - `yarn start` in the terminal
+
+Now point your browser to `http://localhost:3000`. If you prefer, you can also [use docker](doc/docker.md).
# FAQ
diff --git a/package.json b/package.json
index 28b726e2..d89917e8 100644
--- a/package.json
+++ b/package.json
@@ -1,6 +1,6 @@
{
"name": "hydrogen-web",
- "version": "0.2.28",
+ "version": "0.2.29",
"description": "A javascript matrix client prototype, trying to minize RAM usage by offloading as much as possible to IndexedDB",
"directories": {
"doc": "doc"
@@ -11,9 +11,11 @@
"lint-ci": "eslint src/",
"test": "impunity --entry-point src/platform/web/main.js src/platform/web/Platform.js --force-esm-dirs lib/ src/ --root-dir src/",
"test:postcss": "impunity --entry-point scripts/postcss/tests/css-compile-variables.test.js scripts/postcss/tests/css-url-to-variables.test.js",
+ "test:sdk": "yarn build:sdk && cd ./scripts/sdk/test/ && yarn --no-lockfile && node test-sdk-in-esm-vite-build-env.js && node test-sdk-in-commonjs-env.js",
"start": "vite --port 3000",
- "build": "vite build",
- "build:sdk": "./scripts/sdk/build.sh"
+ "build": "vite build && ./scripts/cleanup.sh",
+ "build:sdk": "./scripts/sdk/build.sh",
+ "watch:sdk": "./scripts/sdk/build.sh && yarn run vite build -c vite.sdk-lib-config.js --watch"
},
"repository": {
"type": "git",
@@ -48,7 +50,7 @@
"regenerator-runtime": "^0.13.7",
"text-encoding": "^0.7.0",
"typescript": "^4.4",
- "vite": "^2.6.14",
+ "vite": "^2.9.8",
"xxhashjs": "^0.2.2"
},
"dependencies": {
diff --git a/scripts/build-plugins/rollup-plugin-build-themes.js b/scripts/build-plugins/rollup-plugin-build-themes.js
index e7a2bb2b..da2db73b 100644
--- a/scripts/build-plugins/rollup-plugin-build-themes.js
+++ b/scripts/build-plugins/rollup-plugin-build-themes.js
@@ -31,6 +31,18 @@ function appendVariablesToCSS(variables, cssSource) {
return cssSource + getRootSectionWithVariables(variables);
}
+function addThemesToConfig(bundle, manifestLocations, defaultThemes) {
+ for (const [fileName, info] of Object.entries(bundle)) {
+ if (fileName === "config.json") {
+ const source = new TextDecoder().decode(info.source);
+ const config = JSON.parse(source);
+ config["themeManifests"] = manifestLocations;
+ config["defaultTheme"] = defaultThemes;
+ info.source = new TextEncoder().encode(JSON.stringify(config, undefined, 2));
+ }
+ }
+}
+
function parseBundle(bundle) {
const chunkMap = new Map();
const assetMap = new Map();
@@ -72,7 +84,7 @@ function parseBundle(bundle) {
}
module.exports = function buildThemes(options) {
- let manifest, variants, defaultDark, defaultLight;
+ let manifest, variants, defaultDark, defaultLight, defaultThemes = {};
let isDevelopment = false;
const virtualModuleId = '@theme/'
const resolvedVirtualModuleId = '\0' + virtualModuleId;
@@ -99,9 +111,11 @@ module.exports = function buildThemes(options) {
// This is the default theme, stash the file name for later
if (details.dark) {
defaultDark = fileName;
+ defaultThemes["dark"] = `${name}-${variant}`;
}
else {
defaultLight = fileName;
+ defaultThemes["light"] = `${name}-${variant}`;
}
}
// emit the css as built theme bundle
@@ -215,6 +229,7 @@ module.exports = function buildThemes(options) {
type: "text/css",
media: "(prefers-color-scheme: dark)",
href: `./${darkThemeLocation}`,
+ class: "theme",
}
},
{
@@ -224,6 +239,7 @@ module.exports = function buildThemes(options) {
type: "text/css",
media: "(prefers-color-scheme: light)",
href: `./${lightThemeLocation}`,
+ class: "theme",
}
},
];
@@ -231,24 +247,36 @@ module.exports = function buildThemes(options) {
generateBundle(_, bundle) {
const { assetMap, chunkMap, runtimeThemeChunk } = parseBundle(bundle);
+ const manifestLocations = [];
for (const [location, chunkArray] of chunkMap) {
const manifest = require(`${location}/manifest.json`);
const compiledVariables = options.compiledVariables.get(location);
const derivedVariables = compiledVariables["derived-variables"];
const icon = compiledVariables["icon"];
+ const builtAssets = {};
+ /**
+ * Generate a mapping from theme name to asset hashed location of said theme in build output.
+ * This can be used to enumerate themes during runtime.
+ */
+ for (const chunk of chunkArray) {
+ const [, name, variant] = chunk.fileName.match(/theme-(.+)-(.+)\.css/);
+ builtAssets[`${name}-${variant}`] = assetMap.get(chunk.fileName).fileName;
+ }
manifest.source = {
- "built-asset": chunkArray.map(chunk => assetMap.get(chunk.fileName).fileName),
+ "built-assets": builtAssets,
"runtime-asset": assetMap.get(runtimeThemeChunk.fileName).fileName,
"derived-variables": derivedVariables,
"icon": icon
};
const name = `theme-${manifest.name}.json`;
+ manifestLocations.push(`assets/${name}`);
this.emitFile({
type: "asset",
name,
source: JSON.stringify(manifest),
});
}
+ addThemesToConfig(bundle, manifestLocations, defaultThemes);
},
}
}
diff --git a/scripts/build-plugins/service-worker.js b/scripts/build-plugins/service-worker.js
index 805f6000..85619545 100644
--- a/scripts/build-plugins/service-worker.js
+++ b/scripts/build-plugins/service-worker.js
@@ -8,7 +8,7 @@ function contentHash(str) {
return hasher.digest();
}
-function injectServiceWorker(swFile, otherUnhashedFiles, placeholdersPerChunk) {
+function injectServiceWorker(swFile, findUnhashedFileNamesFromBundle, placeholdersPerChunk) {
const swName = path.basename(swFile);
let root;
let version;
@@ -31,6 +31,7 @@ function injectServiceWorker(swFile, otherUnhashedFiles, placeholdersPerChunk) {
logger = config.logger;
},
generateBundle: async function(options, bundle) {
+ const otherUnhashedFiles = findUnhashedFileNamesFromBundle(bundle);
const unhashedFilenames = [swName].concat(otherUnhashedFiles);
const unhashedFileContentMap = unhashedFilenames.reduce((map, fileName) => {
const chunkOrAsset = bundle[fileName];
diff --git a/scripts/cleanup.sh b/scripts/cleanup.sh
new file mode 100755
index 00000000..6917af5e
--- /dev/null
+++ b/scripts/cleanup.sh
@@ -0,0 +1,3 @@
+#!/bin/sh
+# Remove icons created in .tmp
+rm -rf .tmp
diff --git a/scripts/package.sh b/scripts/package.sh
index 8146fe58..6ad136a3 100755
--- a/scripts/package.sh
+++ b/scripts/package.sh
@@ -2,6 +2,9 @@ VERSION=$(jq -r ".version" package.json)
PACKAGE=hydrogen-web-$VERSION.tar.gz
yarn build
pushd target
+# move config file so we don't override it
+# when deploying a new version
+mv config.json config.sample.json
tar -czvf ../$PACKAGE ./
popd
echo $PACKAGE
diff --git a/scripts/sdk/base-manifest.json b/scripts/sdk/base-manifest.json
index 0ed9fdab..de940752 100644
--- a/scripts/sdk/base-manifest.json
+++ b/scripts/sdk/base-manifest.json
@@ -2,6 +2,18 @@
"name": "hydrogen-view-sdk",
"description": "Embeddable matrix client library, including view components",
"version": "0.0.12",
- "main": "./hydrogen.es.js",
- "type": "module"
+ "main": "./lib-build/hydrogen.cjs.js",
+ "exports": {
+ ".": {
+ "import": "./lib-build/hydrogen.es.js",
+ "require": "./lib-build/hydrogen.cjs.js"
+ },
+ "./paths/vite": "./paths/vite.js",
+ "./style.css": "./asset-build/assets/theme-element-light.css",
+ "./theme-element-light.css": "./asset-build/assets/theme-element-light.css",
+ "./theme-element-dark.css": "./asset-build/assets/theme-element-dark.css",
+ "./main.js": "./asset-build/assets/main.js",
+ "./download-sandbox.html": "./asset-build/assets/download-sandbox.html",
+ "./assets/*": "./asset-build/assets/*"
+ }
}
diff --git a/scripts/sdk/build.sh b/scripts/sdk/build.sh
index ae3a794e..5e1632d3 100755
--- a/scripts/sdk/build.sh
+++ b/scripts/sdk/build.sh
@@ -2,8 +2,12 @@
# Exit whenever one of the commands fail with a non-zero exit code
set -e
set -o pipefail
+# Enable extended globs so we can use the `!(filename)` glob syntax
+shopt -s extglob
-rm -rf target
+# Only remove the directory contents instead of the whole directory to maintain
+# the `npm link`/`yarn link` symlink
+rm -rf target/*
yarn run vite build -c vite.sdk-assets-config.js
yarn run vite build -c vite.sdk-lib-config.js
yarn tsc -p tsconfig-declaration.json
@@ -12,19 +16,10 @@ mkdir target/paths
# this doesn't work, the ?url imports need to be in the consuming project, so disable for now
# ./scripts/sdk/transform-paths.js ./src/platform/web/sdk/paths/vite.js ./target/paths/vite.js
cp doc/SDK.md target/README.md
-pushd target
-pushd asset-build/assets
-mv main.*.js ../../main.js
-# Create a copy of light theme for backwards compatibility
-cp theme-element-light.*.css ../../style.css
-# Remove asset hash from css files
-mv theme-element-light.*.css ../../theme-element-light.css
-mv theme-element-dark.*.css ../../theme-element-dark.css
-mv download-sandbox.*.html ../../download-sandbox.html
-rm *.js *.wasm
-mv ./* ../../
+pushd target/asset-build
+rm index.html
popd
-rm -rf asset-build
-mv lib-build/* .
-rm -rf lib-build
+pushd target/asset-build/assets
+# Remove all `*.wasm` and `*.js` files except for `main.js`
+rm !(main).js *.wasm
popd
diff --git a/scripts/sdk/create-manifest.js b/scripts/sdk/create-manifest.js
index b420e679..9d5cebb2 100755
--- a/scripts/sdk/create-manifest.js
+++ b/scripts/sdk/create-manifest.js
@@ -3,21 +3,7 @@ const fs = require("fs");
const appManifest = require("../../package.json");
const baseSDKManifest = require("./base-manifest.json");
/*
- need to leave exports out of base-manifest.json because of #vite-bug,
- with the downside that we can't support environments that support
- both esm and commonjs modules, so we pick just esm.
- ```
- "exports": {
- ".": {
- "import": "./hydrogen.es.js",
- "require": "./hydrogen.cjs.js"
- },
- "./paths/vite": "./paths/vite.js",
- "./style.css": "./style.css"
- },
- ```
-
- Also need to leave typescript type definitions out until the
+ Need to leave typescript type definitions out until the
typescript conversion is complete and all imports in the d.ts files
exists.
```
diff --git a/scripts/sdk/test/.gitignore b/scripts/sdk/test/.gitignore
new file mode 100644
index 00000000..cf762fe6
--- /dev/null
+++ b/scripts/sdk/test/.gitignore
@@ -0,0 +1,3 @@
+node_modules
+dist
+yarn.lock
diff --git a/scripts/sdk/test/deps.d.ts b/scripts/sdk/test/deps.d.ts
new file mode 100644
index 00000000..4c7d0327
--- /dev/null
+++ b/scripts/sdk/test/deps.d.ts
@@ -0,0 +1,2 @@
+// Keep TypeScripts from complaining about hydrogen-view-sdk not having types yet
+declare module "hydrogen-view-sdk";
diff --git a/scripts/sdk/test/esm-entry.ts b/scripts/sdk/test/esm-entry.ts
new file mode 100644
index 00000000..1f3b7114
--- /dev/null
+++ b/scripts/sdk/test/esm-entry.ts
@@ -0,0 +1,21 @@
+import * as hydrogenViewSdk from "hydrogen-view-sdk";
+import downloadSandboxPath from 'hydrogen-view-sdk/download-sandbox.html?url';
+import workerPath from 'hydrogen-view-sdk/main.js?url';
+import olmWasmPath from '@matrix-org/olm/olm.wasm?url';
+import olmJsPath from '@matrix-org/olm/olm.js?url';
+import olmLegacyJsPath from '@matrix-org/olm/olm_legacy.js?url';
+const assetPaths = {
+ downloadSandbox: downloadSandboxPath,
+ worker: workerPath,
+ olm: {
+ wasm: olmWasmPath,
+ legacyBundle: olmLegacyJsPath,
+ wasmBundle: olmJsPath
+ }
+};
+import "hydrogen-view-sdk/theme-element-light.css";
+
+console.log('hydrogenViewSdk', hydrogenViewSdk);
+console.log('assetPaths', assetPaths);
+
+console.log('Entry ESM works ✅');
diff --git a/scripts/sdk/test/index.html b/scripts/sdk/test/index.html
new file mode 100644
index 00000000..2ee14116
--- /dev/null
+++ b/scripts/sdk/test/index.html
@@ -0,0 +1,12 @@
+
+
+
+
+
+ Vite App
+
+
+
+
+
+
diff --git a/scripts/sdk/test/package.json b/scripts/sdk/test/package.json
new file mode 100644
index 00000000..a81da82c
--- /dev/null
+++ b/scripts/sdk/test/package.json
@@ -0,0 +1,8 @@
+{
+ "name": "test-sdk",
+ "version": "0.0.0",
+ "description": "",
+ "dependencies": {
+ "hydrogen-view-sdk": "link:../../../target"
+ }
+}
diff --git a/scripts/sdk/test/test-sdk-in-commonjs-env.js b/scripts/sdk/test/test-sdk-in-commonjs-env.js
new file mode 100644
index 00000000..3fd19d46
--- /dev/null
+++ b/scripts/sdk/test/test-sdk-in-commonjs-env.js
@@ -0,0 +1,13 @@
+// Make sure the SDK can be used in a CommonJS environment.
+// Usage: node scripts/sdk/test/test-sdk-in-commonjs-env.js
+const hydrogenViewSdk = require('hydrogen-view-sdk');
+
+// Test that the "exports" are available:
+// Worker
+require.resolve('hydrogen-view-sdk/main.js');
+// Styles
+require.resolve('hydrogen-view-sdk/theme-element-light.css');
+// Can access files in the assets/* directory
+require.resolve('hydrogen-view-sdk/assets/main.js');
+
+console.log('SDK works in CommonJS ✅');
diff --git a/scripts/sdk/test/test-sdk-in-esm-vite-build-env.js b/scripts/sdk/test/test-sdk-in-esm-vite-build-env.js
new file mode 100644
index 00000000..6fc87da7
--- /dev/null
+++ b/scripts/sdk/test/test-sdk-in-esm-vite-build-env.js
@@ -0,0 +1,19 @@
+const { resolve } = require('path');
+const { build } = require('vite');
+
+async function main() {
+ await build({
+ outDir: './dist',
+ build: {
+ rollupOptions: {
+ input: {
+ main: resolve(__dirname, 'index.html')
+ }
+ }
+ }
+ });
+
+ console.log('SDK works in Vite build ✅');
+}
+
+main();
diff --git a/src/domain/session/settings/SettingsViewModel.js b/src/domain/session/settings/SettingsViewModel.js
index d0f2e91d..55687a89 100644
--- a/src/domain/session/settings/SettingsViewModel.js
+++ b/src/domain/session/settings/SettingsViewModel.js
@@ -50,6 +50,7 @@ export class SettingsViewModel extends ViewModel {
this.minSentImageSizeLimit = 400;
this.maxSentImageSizeLimit = 4000;
this.pushNotifications = new PushNotificationStatus();
+ this._activeTheme = undefined;
}
get _session() {
@@ -76,6 +77,9 @@ export class SettingsViewModel extends ViewModel {
this.sentImageSizeLimit = await this.platform.settingsStorage.getInt("sentImageSizeLimit");
this.pushNotifications.supported = await this.platform.notificationService.supportsPush();
this.pushNotifications.enabled = await this._session.arePushNotificationsEnabled();
+ if (!import.meta.env.DEV) {
+ this._activeTheme = await this.platform.themeLoader.getActiveTheme();
+ }
this.emitChange("");
}
@@ -127,6 +131,18 @@ export class SettingsViewModel extends ViewModel {
return this._formatBytes(this._estimate?.usage);
}
+ get themes() {
+ return this.platform.themeLoader.themes;
+ }
+
+ get activeTheme() {
+ return this._activeTheme;
+ }
+
+ setTheme(name) {
+ this.platform.themeLoader.setTheme(name);
+ }
+
_formatBytes(n) {
if (typeof n === "number") {
return Math.round(n / (1024 * 1024)).toFixed(1) + " MB";
diff --git a/src/matrix/e2ee/DeviceTracker.js b/src/matrix/e2ee/DeviceTracker.js
index 0068a1f9..f8c3bca8 100644
--- a/src/matrix/e2ee/DeviceTracker.js
+++ b/src/matrix/e2ee/DeviceTracker.js
@@ -214,11 +214,12 @@ export class DeviceTracker {
const allDeviceIdentities = [];
const deviceIdentitiesToStore = [];
// filter out devices that have changed their ed25519 key since last time we queried them
- deviceIdentities = await Promise.all(deviceIdentities.map(async deviceIdentity => {
+ await Promise.all(deviceIdentities.map(async deviceIdentity => {
if (knownDeviceIds.includes(deviceIdentity.deviceId)) {
const existingDevice = await txn.deviceIdentities.get(deviceIdentity.userId, deviceIdentity.deviceId);
if (existingDevice.ed25519Key !== deviceIdentity.ed25519Key) {
allDeviceIdentities.push(existingDevice);
+ return;
}
}
allDeviceIdentities.push(deviceIdentity);
@@ -363,3 +364,154 @@ export class DeviceTracker {
return await txn.deviceIdentities.getByCurve25519Key(curve25519Key);
}
}
+
+import {createMockStorage} from "../../mocks/Storage";
+import {Instance as NullLoggerInstance} from "../../logging/NullLogger";
+
+export function tests() {
+
+ function createUntrackedRoomMock(roomId, joinedUserIds, invitedUserIds = []) {
+ return {
+ isTrackingMembers: false,
+ isEncrypted: true,
+ loadMemberList: () => {
+ const joinedMembers = joinedUserIds.map(userId => {return {membership: "join", roomId, userId};});
+ const invitedMembers = invitedUserIds.map(userId => {return {membership: "invite", roomId, userId};});
+ const members = joinedMembers.concat(invitedMembers);
+ const memberMap = members.reduce((map, member) => {
+ map.set(member.userId, member);
+ return map;
+ }, new Map());
+ return {members: memberMap, release() {}}
+ },
+ writeIsTrackingMembers(isTrackingMembers) {
+ if (this.isTrackingMembers !== isTrackingMembers) {
+ return isTrackingMembers;
+ }
+ return undefined;
+ },
+ applyIsTrackingMembersChanges(isTrackingMembers) {
+ if (isTrackingMembers !== undefined) {
+ this.isTrackingMembers = isTrackingMembers;
+ }
+ },
+ }
+ }
+
+ function createQueryKeysHSApiMock(createKey = (algorithm, userId, deviceId) => `${algorithm}:${userId}:${deviceId}:key`) {
+ return {
+ queryKeys(payload) {
+ const {device_keys: deviceKeys} = payload;
+ const userKeys = Object.entries(deviceKeys).reduce((userKeys, [userId, deviceIds]) => {
+ if (deviceIds.length === 0) {
+ deviceIds = ["device1"];
+ }
+ userKeys[userId] = deviceIds.filter(d => d === "device1").reduce((deviceKeys, deviceId) => {
+ deviceKeys[deviceId] = {
+ "algorithms": [
+ "m.olm.v1.curve25519-aes-sha2",
+ "m.megolm.v1.aes-sha2"
+ ],
+ "device_id": deviceId,
+ "keys": {
+ [`curve25519:${deviceId}`]: createKey("curve25519", userId, deviceId),
+ [`ed25519:${deviceId}`]: createKey("ed25519", userId, deviceId),
+ },
+ "signatures": {
+ [userId]: {
+ [`ed25519:${deviceId}`]: `ed25519:${userId}:${deviceId}:signature`
+ }
+ },
+ "unsigned": {
+ "device_display_name": `${userId} Phone`
+ },
+ "user_id": userId
+ };
+ return deviceKeys;
+ }, {});
+ return userKeys;
+ }, {});
+ const response = {device_keys: userKeys};
+ return {
+ async response() {
+ return response;
+ }
+ };
+ }
+ };
+ }
+ const roomId = "!abc:hs.tld";
+
+ return {
+ "trackRoom only writes joined members": async assert => {
+ const storage = await createMockStorage();
+ const tracker = new DeviceTracker({
+ storage,
+ getSyncToken: () => "token",
+ olmUtil: {ed25519_verify: () => {}}, // valid if it does not throw
+ ownUserId: "@alice:hs.tld",
+ ownDeviceId: "ABCD",
+ });
+ const room = createUntrackedRoomMock(roomId, ["@alice:hs.tld", "@bob:hs.tld"], ["@charly:hs.tld"]);
+ await tracker.trackRoom(room, NullLoggerInstance.item);
+ const txn = await storage.readTxn([storage.storeNames.userIdentities]);
+ assert.deepEqual(await txn.userIdentities.get("@alice:hs.tld"), {
+ userId: "@alice:hs.tld",
+ roomIds: [roomId],
+ deviceTrackingStatus: TRACKING_STATUS_OUTDATED
+ });
+ assert.deepEqual(await txn.userIdentities.get("@bob:hs.tld"), {
+ userId: "@bob:hs.tld",
+ roomIds: [roomId],
+ deviceTrackingStatus: TRACKING_STATUS_OUTDATED
+ });
+ assert.equal(await txn.userIdentities.get("@charly:hs.tld"), undefined);
+ },
+ "getting devices for tracked room yields correct keys": async assert => {
+ const storage = await createMockStorage();
+ const tracker = new DeviceTracker({
+ storage,
+ getSyncToken: () => "token",
+ olmUtil: {ed25519_verify: () => {}}, // valid if it does not throw
+ ownUserId: "@alice:hs.tld",
+ ownDeviceId: "ABCD",
+ });
+ const room = createUntrackedRoomMock(roomId, ["@alice:hs.tld", "@bob:hs.tld"]);
+ await tracker.trackRoom(room, NullLoggerInstance.item);
+ const hsApi = createQueryKeysHSApiMock();
+ const devices = await tracker.devicesForRoomMembers(roomId, ["@alice:hs.tld", "@bob:hs.tld"], hsApi, NullLoggerInstance.item);
+ assert.equal(devices.length, 2);
+ assert.equal(devices.find(d => d.userId === "@alice:hs.tld").ed25519Key, "ed25519:@alice:hs.tld:device1:key");
+ assert.equal(devices.find(d => d.userId === "@bob:hs.tld").ed25519Key, "ed25519:@bob:hs.tld:device1:key");
+ },
+ "device with changed key is ignored": async assert => {
+ const storage = await createMockStorage();
+ const tracker = new DeviceTracker({
+ storage,
+ getSyncToken: () => "token",
+ olmUtil: {ed25519_verify: () => {}}, // valid if it does not throw
+ ownUserId: "@alice:hs.tld",
+ ownDeviceId: "ABCD",
+ });
+ const room = createUntrackedRoomMock(roomId, ["@alice:hs.tld", "@bob:hs.tld"]);
+ await tracker.trackRoom(room, NullLoggerInstance.item);
+ const hsApi = createQueryKeysHSApiMock();
+ // query devices first time
+ await tracker.devicesForRoomMembers(roomId, ["@alice:hs.tld", "@bob:hs.tld"], hsApi, NullLoggerInstance.item);
+ const txn = await storage.readWriteTxn([storage.storeNames.userIdentities]);
+ // mark alice as outdated, so keys will be fetched again
+ tracker.writeDeviceChanges(["@alice:hs.tld"], txn, NullLoggerInstance.item);
+ await txn.complete();
+ const hsApiWithChangedAliceKey = createQueryKeysHSApiMock((algo, userId, deviceId) => {
+ return `${algo}:${userId}:${deviceId}:${userId === "@alice:hs.tld" ? "newKey" : "key"}`;
+ });
+ const devices = await tracker.devicesForRoomMembers(roomId, ["@alice:hs.tld", "@bob:hs.tld"], hsApiWithChangedAliceKey, NullLoggerInstance.item);
+ assert.equal(devices.length, 2);
+ assert.equal(devices.find(d => d.userId === "@alice:hs.tld").ed25519Key, "ed25519:@alice:hs.tld:device1:key");
+ assert.equal(devices.find(d => d.userId === "@bob:hs.tld").ed25519Key, "ed25519:@bob:hs.tld:device1:key");
+ const txn2 = await storage.readTxn([storage.storeNames.deviceIdentities]);
+ // also check the modified key was not stored
+ assert.equal((await txn2.deviceIdentities.get("@alice:hs.tld", "device1")).ed25519Key, "ed25519:@alice:hs.tld:device1:key");
+ }
+ }
+}
diff --git a/src/platform/web/Platform.js b/src/platform/web/Platform.js
index 846f7b1c..cbd41ec7 100644
--- a/src/platform/web/Platform.js
+++ b/src/platform/web/Platform.js
@@ -41,6 +41,7 @@ import {parseHTML} from "./parsehtml.js";
import {handleAvatarError} from "./ui/avatar";
import {MediaDevicesWrapper} from "./dom/MediaDevices";
import {DOMWebRTC} from "./dom/WebRTC";
+import {ThemeLoader} from "./ThemeLoader";
function addScript(src) {
return new Promise(function (resolve, reject) {
@@ -169,20 +170,36 @@ export class Platform {
this._workerPromise = undefined;
this.mediaDevices = new MediaDevicesWrapper(navigator.mediaDevices);
this.webRTC = new DOMWebRTC();
+ this._themeLoader = import.meta.env.DEV? null: new ThemeLoader(this);
}
async init() {
- if (!this._config) {
- if (!this._configURL) {
- throw new Error("Neither config nor configURL was provided!");
- }
- const {body}= await this.request(this._configURL, {method: "GET", format: "json", cache: true}).response();
- this._config = body;
+ try {
+ await this.logger.run("Platform init", async (log) => {
+ if (!this._config) {
+ if (!this._configURL) {
+ throw new Error("Neither config nor configURL was provided!");
+ }
+ const {status, body}= await this.request(this._configURL, {method: "GET", format: "json", cache: true}).response();
+ if (status === 404) {
+ throw new Error(`Could not find ${this._configURL}. Did you copy over config.sample.json?`);
+ } else if (status >= 400) {
+ throw new Error(`Got status ${status} while trying to fetch ${this._configURL}`);
+ }
+ this._config = body;
+ }
+ this.notificationService = new NotificationService(
+ this._serviceWorkerHandler,
+ this._config.push
+ );
+ const manifests = this.config["themeManifests"];
+ await this._themeLoader?.init(manifests);
+ this._themeLoader?.setTheme(await this._themeLoader.getActiveTheme(), log);
+ });
+ } catch (err) {
+ this._container.innerText = err.message;
+ throw err;
}
- this.notificationService = new NotificationService(
- this._serviceWorkerHandler,
- this._config.push
- );
}
_createLogger(isDevelopment) {
@@ -314,6 +331,23 @@ export class Platform {
return DEFINE_VERSION;
}
+ get themeLoader() {
+ return this._themeLoader;
+ }
+
+ replaceStylesheet(newPath) {
+ const head = document.querySelector("head");
+ // remove default theme
+ document.querySelectorAll(".theme").forEach(e => e.remove());
+ // add new theme
+ const styleTag = document.createElement("link");
+ styleTag.href = `./${newPath}`;
+ styleTag.rel = "stylesheet";
+ styleTag.type = "text/css";
+ styleTag.className = "theme";
+ head.appendChild(styleTag);
+ }
+
dispose() {
this._disposables.dispose();
}
diff --git a/src/platform/web/ThemeLoader.ts b/src/platform/web/ThemeLoader.ts
new file mode 100644
index 00000000..d9aaabb6
--- /dev/null
+++ b/src/platform/web/ThemeLoader.ts
@@ -0,0 +1,75 @@
+/*
+Copyright 2020 The Matrix.org Foundation C.I.C.
+
+Licensed under the Apache License, Version 2.0 (the "License");
+you may not use this file except in compliance with the License.
+You may obtain a copy of the License at
+
+ http://www.apache.org/licenses/LICENSE-2.0
+
+Unless required by applicable law or agreed to in writing, software
+distributed under the License is distributed on an "AS IS" BASIS,
+WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+See the License for the specific language governing permissions and
+limitations under the License.
+*/
+
+import type {ILogItem} from "../../logging/types.js";
+import type {Platform} from "./Platform.js";
+
+export class ThemeLoader {
+ private _platform: Platform;
+ private _themeMapping: Record = {};
+
+ constructor(platform: Platform) {
+ this._platform = platform;
+ }
+
+ async init(manifestLocations: string[]): Promise {
+ for (const manifestLocation of manifestLocations) {
+ const { body } = await this._platform
+ .request(manifestLocation, {
+ method: "GET",
+ format: "json",
+ cache: true,
+ })
+ .response();
+ /*
+ After build has finished, the source section of each theme manifest
+ contains `built-assets` which is a mapping from the theme-name to the
+ location of the css file in build.
+ */
+ Object.assign(this._themeMapping, body["source"]["built-assets"]);
+ }
+ }
+
+ setTheme(themeName: string, log?: ILogItem) {
+ this._platform.logger.wrapOrRun(log, {l: "change theme", id: themeName}, () => {
+ const themeLocation = this._themeMapping[themeName];
+ if (!themeLocation) {
+ throw new Error( `Cannot find theme location for theme "${themeName}"!`);
+ }
+ this._platform.replaceStylesheet(themeLocation);
+ this._platform.settingsStorage.setString("theme", themeName);
+ });
+ }
+
+ get themes(): string[] {
+ return Object.keys(this._themeMapping);
+ }
+
+ async getActiveTheme(): Promise {
+ // check if theme is set via settings
+ let theme = await this._platform.settingsStorage.getString("theme");
+ if (theme) {
+ return theme;
+ }
+ // return default theme
+ if (window.matchMedia("(prefers-color-scheme: dark)").matches) {
+ return this._platform.config["defaultTheme"].dark;
+ } else if (window.matchMedia("(prefers-color-scheme: light)").matches) {
+ return this._platform.config["defaultTheme"].light;
+ }
+ return undefined;
+ }
+}
diff --git a/src/platform/web/sw.js b/src/platform/web/sw.js
index a9a92979..088bc059 100644
--- a/src/platform/web/sw.js
+++ b/src/platform/web/sw.js
@@ -95,8 +95,8 @@ let pendingFetchAbortController = new AbortController();
async function handleRequest(request) {
try {
- if (request.url.includes("config.json")) {
- return handleConfigRequest(request);
+ if (request.url.includes("config.json") || /theme-.+\.json/.test(request.url)) {
+ return handleStaleWhileRevalidateRequest(request);
}
const url = new URL(request.url);
// rewrite / to /index.html so it hits the cache
@@ -123,9 +123,13 @@ async function handleRequest(request) {
}
}
-async function handleConfigRequest(request) {
+/**
+ * Stale-while-revalidate caching for certain files
+ * see https://developer.chrome.com/docs/workbox/caching-strategies-overview/#stale-while-revalidate
+ */
+async function handleStaleWhileRevalidateRequest(request) {
let response = await readCache(request);
- const networkResponsePromise = fetchAndUpdateConfig(request);
+ const networkResponsePromise = fetchAndUpdateCache(request);
if (response) {
return response;
} else {
@@ -133,7 +137,7 @@ async function handleConfigRequest(request) {
}
}
-async function fetchAndUpdateConfig(request) {
+async function fetchAndUpdateCache(request) {
const response = await fetch(request, {
signal: pendingFetchAbortController.signal,
headers: {
@@ -156,8 +160,14 @@ async function updateCache(request, response) {
cache.put(request, response.clone());
} else if (request.url.startsWith(baseURL)) {
let assetName = request.url.substr(baseURL.length);
+ let cacheName;
if (HASHED_CACHED_ON_REQUEST_ASSETS.includes(assetName)) {
- const cache = await caches.open(hashedCacheName);
+ cacheName = hashedCacheName;
+ } else if (UNHASHED_PRECACHED_ASSETS.includes(assetName)) {
+ cacheName = unhashedCacheName;
+ }
+ if (cacheName) {
+ const cache = await caches.open(cacheName);
await cache.put(request, response.clone());
}
}
diff --git a/src/platform/web/ui/session/settings/SettingsView.js b/src/platform/web/ui/session/settings/SettingsView.js
index f375a4be..40207a8f 100644
--- a/src/platform/web/ui/session/settings/SettingsView.js
+++ b/src/platform/web/ui/session/settings/SettingsView.js
@@ -93,10 +93,13 @@ export class SettingsView extends TemplateView {
]);
})
);
-
+
settingNodes.push(
t.h3("Preferences"),
row(t, vm.i18n`Scale down images when sending`, this._imageCompressionRange(t, vm)),
+ t.if(vm => !import.meta.env.DEV && vm.activeTheme, (t, vm) => {
+ return row(t, vm.i18n`Use the following theme`, this._themeOptions(t, vm));
+ }),
);
const logButtons = [t.button({onClick: () => vm.exportLogs()}, "Export")];
if (import.meta.env.DEV) {
@@ -140,6 +143,15 @@ export class SettingsView extends TemplateView {
vm.i18n`no resizing`;
})];
}
+
+ _themeOptions(t, vm) {
+ const activeTheme = vm.activeTheme;
+ const optionTags = [];
+ for (const name of vm.themes) {
+ optionTags.push(t.option({value: name, selected: name === activeTheme}, name));
+ }
+ return t.select({onChange: (e) => vm.setTheme(e.target.value)}, optionTags);
+ }
}
diff --git a/vite.config.js b/vite.config.js
index 97fb8885..e784a4ad 100644
--- a/vite.config.js
+++ b/vite.config.js
@@ -16,25 +16,39 @@ export default defineConfig(({mode}) => {
sourcemap: true,
rollupOptions: {
output: {
- assetFileNames: (asset) => asset.name.includes("config.json") ? "assets/[name][extname]": "assets/[name].[hash][extname]",
+ assetFileNames: (asset) => {
+ if (asset.name.includes("config.json")) {
+ return "[name][extname]";
+ }
+ else if (asset.name.match(/theme-.+\.json/)) {
+ return "assets/[name][extname]";
+ }
+ else {
+ return "assets/[name].[hash][extname]";
+ }
+ }
},
},
},
plugins: [
themeBuilder({
themeConfig: {
- themes: {"element": "./src/platform/web/ui/css/themes/element"},
+ themes: {
+ element: "./src/platform/web/ui/css/themes/element",
+ },
default: "element",
},
- compiledVariables
+ compiledVariables,
}),
// important this comes before service worker
// otherwise the manifest and the icons it refers to won't be cached
injectWebManifest("assets/manifest.json"),
- injectServiceWorker("./src/platform/web/sw.js", ["index.html"], {
+ injectServiceWorker("./src/platform/web/sw.js", findUnhashedFileNamesFromBundle, {
// placeholders to replace at end of build by chunk name
- "index": {DEFINE_GLOBAL_HASH: definePlaceholders.DEFINE_GLOBAL_HASH},
- "sw": definePlaceholders
+ index: {
+ DEFINE_GLOBAL_HASH: definePlaceholders.DEFINE_GLOBAL_HASH,
+ },
+ sw: definePlaceholders,
}),
],
define: Object.assign({
@@ -42,3 +56,16 @@ export default defineConfig(({mode}) => {
}, definePlaceholders),
});
});
+
+function findUnhashedFileNamesFromBundle(bundle) {
+ const names = ["index.html"];
+ for (const fileName of Object.keys(bundle)) {
+ if (fileName.includes("config.json")) {
+ names.push(fileName);
+ }
+ if (/theme-.+\.json/.test(fileName)) {
+ names.push(fileName);
+ }
+ }
+ return names;
+}
diff --git a/vite.sdk-assets-config.js b/vite.sdk-assets-config.js
index 5fb9c82f..beb7bb37 100644
--- a/vite.sdk-assets-config.js
+++ b/vite.sdk-assets-config.js
@@ -3,11 +3,33 @@ const mergeOptions = require('merge-options');
const themeBuilder = require("./scripts/build-plugins/rollup-plugin-build-themes");
const {commonOptions, compiledVariables} = require("./vite.common-config.js");
+// These paths will be saved without their hash so they havea consisent path to
+// reference
+const pathsToExport = [
+ "main.js",
+ "download-sandbox.html",
+ "theme-element-light.css",
+ "theme-element-dark.css",
+];
+
export default mergeOptions(commonOptions, {
root: "src/",
base: "./",
build: {
outDir: "../target/asset-build/",
+ rollupOptions: {
+ output: {
+ assetFileNames: (chunkInfo) => {
+ // Get rid of the hash so we can consistently reference these
+ // files in our `package.json` `exports`
+ if(pathsToExport.includes(path.basename(chunkInfo.name))) {
+ return "assets/[name].[ext]";
+ }
+
+ return "assets/[name]-[hash][extname]";
+ }
+ }
+ }
},
plugins: [
themeBuilder({
diff --git a/yarn.lock b/yarn.lock
index cbce8b06..77d14eb6 100644
--- a/yarn.lock
+++ b/yarn.lock
@@ -503,66 +503,141 @@ entities@^2.0.0:
version "4.2.8"
resolved "https://github.com/bwindels/es6-promise.git#112f78f5829e627055b0ff56a52fecb63f6003b1"
+esbuild-android-64@0.14.31:
+ version "0.14.31"
+ resolved "https://registry.yarnpkg.com/esbuild-android-64/-/esbuild-android-64-0.14.31.tgz#4b7dfbbeee62b3495ba78220b85fb590eb68d5bf"
+ integrity sha512-MYkuJ91w07nGmr4EouejOZK2j/f5TCnsKxY8vRr2+wpKKfHD1LTJK28VbZa+y1+AL7v1V9G98ezTUwsV3CmXNw==
+
esbuild-android-arm64@0.13.15:
version "0.13.15"
resolved "https://registry.yarnpkg.com/esbuild-android-arm64/-/esbuild-android-arm64-0.13.15.tgz#3fc3ff0bab76fe35dd237476b5d2b32bb20a3d44"
integrity sha512-m602nft/XXeO8YQPUDVoHfjyRVPdPgjyyXOxZ44MK/agewFFkPa8tUo6lAzSWh5Ui5PB4KR9UIFTSBKh/RrCmg==
+esbuild-android-arm64@0.14.31:
+ version "0.14.31"
+ resolved "https://registry.yarnpkg.com/esbuild-android-arm64/-/esbuild-android-arm64-0.14.31.tgz#24c3d693924e044fb0d23206c3e627502b10b930"
+ integrity sha512-0rkH/35s7ZVcsw6nS0IAkR0dekSbjZGWdlOAf3jV0lGoPqqw0x6/TmaV9w7DQgUERTH1ApmPlpAMU4kVkCq9Jg==
+
esbuild-darwin-64@0.13.15:
version "0.13.15"
resolved "https://registry.yarnpkg.com/esbuild-darwin-64/-/esbuild-darwin-64-0.13.15.tgz#8e9169c16baf444eacec60d09b24d11b255a8e72"
integrity sha512-ihOQRGs2yyp7t5bArCwnvn2Atr6X4axqPpEdCFPVp7iUj4cVSdisgvEKdNR7yH3JDjW6aQDw40iQFoTqejqxvQ==
+esbuild-darwin-64@0.14.31:
+ version "0.14.31"
+ resolved "https://registry.yarnpkg.com/esbuild-darwin-64/-/esbuild-darwin-64-0.14.31.tgz#285fbdb6dc74d4410f43dee59e6a14ebff82a9d7"
+ integrity sha512-kP6xPZHxtJa36Hb0jC05L3VzQSZBW2f3bpnQS20czXTRGEmM2GDiYpGdI5g2QYaw6vC4PYXjnigq8usd9g9jnQ==
+
esbuild-darwin-arm64@0.13.15:
version "0.13.15"
resolved "https://registry.yarnpkg.com/esbuild-darwin-arm64/-/esbuild-darwin-arm64-0.13.15.tgz#1b07f893b632114f805e188ddfca41b2b778229a"
integrity sha512-i1FZssTVxUqNlJ6cBTj5YQj4imWy3m49RZRnHhLpefFIh0To05ow9DTrXROTE1urGTQCloFUXTX8QfGJy1P8dQ==
+esbuild-darwin-arm64@0.14.31:
+ version "0.14.31"
+ resolved "https://registry.yarnpkg.com/esbuild-darwin-arm64/-/esbuild-darwin-arm64-0.14.31.tgz#b39c471a8134ce2c7811eb96fab9c500b256261c"
+ integrity sha512-1ZMog4hkNsdBGtDDtsftUqX6S9N52gEx4vX5aVehsSptgoBFIar1XrPiBTQty7YNH+bJasTpSVaZQgElCVvPKQ==
+
esbuild-freebsd-64@0.13.15:
version "0.13.15"
resolved "https://registry.yarnpkg.com/esbuild-freebsd-64/-/esbuild-freebsd-64-0.13.15.tgz#0b8b7eca1690c8ec94c75680c38c07269c1f4a85"
integrity sha512-G3dLBXUI6lC6Z09/x+WtXBXbOYQZ0E8TDBqvn7aMaOCzryJs8LyVXKY4CPnHFXZAbSwkCbqiPuSQ1+HhrNk7EA==
+esbuild-freebsd-64@0.14.31:
+ version "0.14.31"
+ resolved "https://registry.yarnpkg.com/esbuild-freebsd-64/-/esbuild-freebsd-64-0.14.31.tgz#7ca700ef60ae12154bae63094ad41b21c6ae1a23"
+ integrity sha512-Zo0BYj7QpVFWoUpkv6Ng0RO2eJ4zk/WDaHMO88+jr5HuYmxsOre0imgwaZVPquTuJnCvL1G48BFucJ3tFflSeQ==
+
esbuild-freebsd-arm64@0.13.15:
version "0.13.15"
resolved "https://registry.yarnpkg.com/esbuild-freebsd-arm64/-/esbuild-freebsd-arm64-0.13.15.tgz#2e1a6c696bfdcd20a99578b76350b41db1934e52"
integrity sha512-KJx0fzEDf1uhNOZQStV4ujg30WlnwqUASaGSFPhznLM/bbheu9HhqZ6mJJZM32lkyfGJikw0jg7v3S0oAvtvQQ==
+esbuild-freebsd-arm64@0.14.31:
+ version "0.14.31"
+ resolved "https://registry.yarnpkg.com/esbuild-freebsd-arm64/-/esbuild-freebsd-arm64-0.14.31.tgz#f793085c7184fcd08649b8d185edc5c2ce112e82"
+ integrity sha512-t85bS6jbRpmdjr4pdr/FY/fpx8lo1vv9S7BAs2EsXKJQhRDMIiC3QW+k2acYJoRuqirlvJcJVFQGCq/PfyC1kA==
+
esbuild-linux-32@0.13.15:
version "0.13.15"
resolved "https://registry.yarnpkg.com/esbuild-linux-32/-/esbuild-linux-32-0.13.15.tgz#6fd39f36fc66dd45b6b5f515728c7bbebc342a69"
integrity sha512-ZvTBPk0YWCLMCXiFmD5EUtB30zIPvC5Itxz0mdTu/xZBbbHJftQgLWY49wEPSn2T/TxahYCRDWun5smRa0Tu+g==
+esbuild-linux-32@0.14.31:
+ version "0.14.31"
+ resolved "https://registry.yarnpkg.com/esbuild-linux-32/-/esbuild-linux-32-0.14.31.tgz#cac97ec7da6fbde0c21dbe08babd0d2a034f317d"
+ integrity sha512-XYtOk/GodSkv+UOYVwryGpGPuFnszsMvRMKq6cIUfFfdssHuKDsU9IZveyCG44J106J39ABenQ5EetbYtVJHUw==
+
esbuild-linux-64@0.13.15:
version "0.13.15"
resolved "https://registry.yarnpkg.com/esbuild-linux-64/-/esbuild-linux-64-0.13.15.tgz#9cb8e4bcd7574e67946e4ee5f1f1e12386bb6dd3"
integrity sha512-eCKzkNSLywNeQTRBxJRQ0jxRCl2YWdMB3+PkWFo2BBQYC5mISLIVIjThNtn6HUNqua1pnvgP5xX0nHbZbPj5oA==
+esbuild-linux-64@0.14.31:
+ version "0.14.31"
+ resolved "https://registry.yarnpkg.com/esbuild-linux-64/-/esbuild-linux-64-0.14.31.tgz#ec94cd5228e6777d2feb3c24a1fe1cbf8817d6da"
+ integrity sha512-Zf9CZxAxaXWHLqCg/QZ/hs0RU0XV3IBxV+ENQzy00S4QOTnZAvSLgPciILHHrVJ0lPIlb4XzAqlLM5y6iI2LIw==
+
esbuild-linux-arm64@0.13.15:
version "0.13.15"
resolved "https://registry.yarnpkg.com/esbuild-linux-arm64/-/esbuild-linux-arm64-0.13.15.tgz#3891aa3704ec579a1b92d2a586122e5b6a2bfba1"
integrity sha512-bYpuUlN6qYU9slzr/ltyLTR9YTBS7qUDymO8SV7kjeNext61OdmqFAzuVZom+OLW1HPHseBfJ/JfdSlx8oTUoA==
+esbuild-linux-arm64@0.14.31:
+ version "0.14.31"
+ resolved "https://registry.yarnpkg.com/esbuild-linux-arm64/-/esbuild-linux-arm64-0.14.31.tgz#d119188fccd6384db5c703de24c46dacaee3e9e8"
+ integrity sha512-V/H0tv+xpQ9IOHM+o85oCKNNidIEc5CcnDWl0V+hPd2F03dqdbFkWPBGphx8rD4JSQn6UefUQ1iH7y1qIzO8Fw==
+
esbuild-linux-arm@0.13.15:
version "0.13.15"
resolved "https://registry.yarnpkg.com/esbuild-linux-arm/-/esbuild-linux-arm-0.13.15.tgz#8a00e99e6a0c6c9a6b7f334841364d8a2b4aecfe"
integrity sha512-wUHttDi/ol0tD8ZgUMDH8Ef7IbDX+/UsWJOXaAyTdkT7Yy9ZBqPg8bgB/Dn3CZ9SBpNieozrPRHm0BGww7W/jA==
+esbuild-linux-arm@0.14.31:
+ version "0.14.31"
+ resolved "https://registry.yarnpkg.com/esbuild-linux-arm/-/esbuild-linux-arm-0.14.31.tgz#63e10846886901e5632a591d44160f95c5d12ba7"
+ integrity sha512-RpiaeHPRlgCCDskxoyIsI49BhcDtZ4cl8+SLffizDm0yMNWP538SUg0ezQ2TTOPj3/svaGIbkRDwYtAon0Sjkg==
+
esbuild-linux-mips64le@0.13.15:
version "0.13.15"
resolved "https://registry.yarnpkg.com/esbuild-linux-mips64le/-/esbuild-linux-mips64le-0.13.15.tgz#36b07cc47c3d21e48db3bb1f4d9ef8f46aead4f7"
integrity sha512-KlVjIG828uFPyJkO/8gKwy9RbXhCEUeFsCGOJBepUlpa7G8/SeZgncUEz/tOOUJTcWMTmFMtdd3GElGyAtbSWg==
+esbuild-linux-mips64le@0.14.31:
+ version "0.14.31"
+ resolved "https://registry.yarnpkg.com/esbuild-linux-mips64le/-/esbuild-linux-mips64le-0.14.31.tgz#1cd44f72cde6489a5d6deea7c54efa6f3d6590ee"
+ integrity sha512-9/oBfAckInRuUg6AEgdCLLn6KJ6UOJDOLmUinTsReVSg6AfV6wxYQJq9iQM2idRogP7GUpomJ+bvCdWXpotQRQ==
+
esbuild-linux-ppc64le@0.13.15:
version "0.13.15"
resolved "https://registry.yarnpkg.com/esbuild-linux-ppc64le/-/esbuild-linux-ppc64le-0.13.15.tgz#f7e6bba40b9a11eb9dcae5b01550ea04670edad2"
integrity sha512-h6gYF+OsaqEuBjeesTBtUPw0bmiDu7eAeuc2OEH9S6mV9/jPhPdhOWzdeshb0BskRZxPhxPOjqZ+/OqLcxQwEQ==
+esbuild-linux-ppc64le@0.14.31:
+ version "0.14.31"
+ resolved "https://registry.yarnpkg.com/esbuild-linux-ppc64le/-/esbuild-linux-ppc64le-0.14.31.tgz#3b5ccc05e5b8ef5c494f30a61fdd27811d2bbeeb"
+ integrity sha512-NMcb14Pg+8q8raGkzor9/R3vQwKzgxE3694BtO2SDLBwJuL2C1dQ1ZtM1t7ZvArQBgT8RiZVxb0/3fD+qGNk7g==
+
+esbuild-linux-riscv64@0.14.31:
+ version "0.14.31"
+ resolved "https://registry.yarnpkg.com/esbuild-linux-riscv64/-/esbuild-linux-riscv64-0.14.31.tgz#d74ca78c8ed1d9b40bc907a9e3ef6e83fc06189c"
+ integrity sha512-l13yvmsVfawAnoYfcpuvml+nTlrOmtdceXYufSkXl2DOb0JKcuR6ARlAzuQCDcpo49SOJy1cCxpwlOIsUQBfzA==
+
+esbuild-linux-s390x@0.14.31:
+ version "0.14.31"
+ resolved "https://registry.yarnpkg.com/esbuild-linux-s390x/-/esbuild-linux-s390x-0.14.31.tgz#1bd547b8b027e323b77a838d265cb56ece2543af"
+ integrity sha512-GIwV9mY3koYja9MCSkKLk1P7rj+MkPV0UsGsZ575hEcIBrXeKN9jBi6X/bxDDPEN/SUAH35cJhBNrZU4x9lEfg==
+
esbuild-netbsd-64@0.13.15:
version "0.13.15"
resolved "https://registry.yarnpkg.com/esbuild-netbsd-64/-/esbuild-netbsd-64-0.13.15.tgz#a2fedc549c2b629d580a732d840712b08d440038"
integrity sha512-3+yE9emwoevLMyvu+iR3rsa+Xwhie7ZEHMGDQ6dkqP/ndFzRHkobHUKTe+NCApSqG5ce2z4rFu+NX/UHnxlh3w==
+esbuild-netbsd-64@0.14.31:
+ version "0.14.31"
+ resolved "https://registry.yarnpkg.com/esbuild-netbsd-64/-/esbuild-netbsd-64-0.14.31.tgz#964a45dbad4fac92aa0a15056e38a182735bd6c6"
+ integrity sha512-bJ+pyLvKQm+Obp5k7/Wk8e9Gdkls56F1aiI3uptoIfOIUqsZImH7pDyTrSufwqsFp62kO9LRuwXnjDwQtPyhFQ==
+
esbuild-node-loader@^0.6.3:
version "0.6.3"
resolved "https://registry.yarnpkg.com/esbuild-node-loader/-/esbuild-node-loader-0.6.3.tgz#3b90012f8bc2fcbb2ef76a659482c2c99840c5e8"
@@ -575,27 +650,52 @@ esbuild-openbsd-64@0.13.15:
resolved "https://registry.yarnpkg.com/esbuild-openbsd-64/-/esbuild-openbsd-64-0.13.15.tgz#b22c0e5806d3a1fbf0325872037f885306b05cd7"
integrity sha512-wTfvtwYJYAFL1fSs8yHIdf5GEE4NkbtbXtjLWjM3Cw8mmQKqsg8kTiqJ9NJQe5NX/5Qlo7Xd9r1yKMMkHllp5g==
+esbuild-openbsd-64@0.14.31:
+ version "0.14.31"
+ resolved "https://registry.yarnpkg.com/esbuild-openbsd-64/-/esbuild-openbsd-64-0.14.31.tgz#7d2a1d46450321b0459263d3e7072e6d3924ce46"
+ integrity sha512-NRAAPPca05H9j9Xab0kVXK0V6/pyZGGy8d2Y8KS0BMwWEydlD4KCJDmH8/7bWCKYLRGOOCE9/GPBJyPWHFW3sg==
+
esbuild-sunos-64@0.13.15:
version "0.13.15"
resolved "https://registry.yarnpkg.com/esbuild-sunos-64/-/esbuild-sunos-64-0.13.15.tgz#d0b6454a88375ee8d3964daeff55c85c91c7cef4"
integrity sha512-lbivT9Bx3t1iWWrSnGyBP9ODriEvWDRiweAs69vI+miJoeKwHWOComSRukttbuzjZ8r1q0mQJ8Z7yUsDJ3hKdw==
+esbuild-sunos-64@0.14.31:
+ version "0.14.31"
+ resolved "https://registry.yarnpkg.com/esbuild-sunos-64/-/esbuild-sunos-64-0.14.31.tgz#3b3e4363951cd1dda14a14fee6d94ca426108e0c"
+ integrity sha512-9uA+V8w9Eehu4ldb95lPWdgCMcMO5HH6pXmfkk5usn3JsSZxKdLKsXB4hYgP80wscZvVYXJl2G+KNxsUTfPhZw==
+
esbuild-windows-32@0.13.15:
version "0.13.15"
resolved "https://registry.yarnpkg.com/esbuild-windows-32/-/esbuild-windows-32-0.13.15.tgz#c96d0b9bbb52f3303322582ef8e4847c5ad375a7"
integrity sha512-fDMEf2g3SsJ599MBr50cY5ve5lP1wyVwTe6aLJsM01KtxyKkB4UT+fc5MXQFn3RLrAIAZOG+tHC+yXObpSn7Nw==
+esbuild-windows-32@0.14.31:
+ version "0.14.31"
+ resolved "https://registry.yarnpkg.com/esbuild-windows-32/-/esbuild-windows-32-0.14.31.tgz#997026a41c04535bfb7c014a0458940b49145820"
+ integrity sha512-VGdncQTqoxD9q3v/dk0Yugbmx2FzOkcs0OemBYc1X9KXOLQYH0uQbLJIckZdZOC3J+JKSExbYFrzYCOwWPuNyA==
+
esbuild-windows-64@0.13.15:
version "0.13.15"
resolved "https://registry.yarnpkg.com/esbuild-windows-64/-/esbuild-windows-64-0.13.15.tgz#1f79cb9b1e1bb02fb25cd414cb90d4ea2892c294"
integrity sha512-9aMsPRGDWCd3bGjUIKG/ZOJPKsiztlxl/Q3C1XDswO6eNX/Jtwu4M+jb6YDH9hRSUflQWX0XKAfWzgy5Wk54JQ==
+esbuild-windows-64@0.14.31:
+ version "0.14.31"
+ resolved "https://registry.yarnpkg.com/esbuild-windows-64/-/esbuild-windows-64-0.14.31.tgz#5d4b0ea686c9b60036303b3415c472f2761bdafc"
+ integrity sha512-v/2ye5zBqpmCzi3bLCagStbNQlnOsY7WtMrD2Q0xZxeSIXONxji15KYtVee5o7nw4lXWbQSS1BL8G6BBMvtq4A==
+
esbuild-windows-arm64@0.13.15:
version "0.13.15"
resolved "https://registry.yarnpkg.com/esbuild-windows-arm64/-/esbuild-windows-arm64-0.13.15.tgz#482173070810df22a752c686509c370c3be3b3c3"
integrity sha512-zzvyCVVpbwQQATaf3IG8mu1IwGEiDxKkYUdA4FpoCHi1KtPa13jeScYDjlW0Qh+ebWzpKfR2ZwvqAQkSWNcKjA==
-esbuild@^0.13.12, esbuild@^0.13.2:
+esbuild-windows-arm64@0.14.31:
+ version "0.14.31"
+ resolved "https://registry.yarnpkg.com/esbuild-windows-arm64/-/esbuild-windows-arm64-0.14.31.tgz#4f3b9fc34c4a33abbd0171df6cbb657ccbdbfc67"
+ integrity sha512-RXeU42FJoG1sriNHg73h4S+5B7L/gw+8T7U9u8IWqSSEbY6fZvBh4uofugiU1szUDqqP00GHwZ09WgYe3lGZiw==
+
+esbuild@^0.13.12:
version "0.13.15"
resolved "https://registry.yarnpkg.com/esbuild/-/esbuild-0.13.15.tgz#db56a88166ee373f87dbb2d8798ff449e0450cdf"
integrity sha512-raCxt02HBKv8RJxE8vkTSCXGIyKHdEdGfUmiYb8wnabnaEmHzyW7DCHb5tEN0xU8ryqg5xw54mcwnYkC4x3AIw==
@@ -618,6 +718,32 @@ esbuild@^0.13.12, esbuild@^0.13.2:
esbuild-windows-64 "0.13.15"
esbuild-windows-arm64 "0.13.15"
+esbuild@^0.14.27:
+ version "0.14.31"
+ resolved "https://registry.yarnpkg.com/esbuild/-/esbuild-0.14.31.tgz#f7d0db114edc615f66d84972ee9fbd2b267f4029"
+ integrity sha512-QA0fUM13+JZzcvg1bdrhi7wo8Lr5IRHA9ypNn2znqxGqb66dSK6pAh01TjyBOhzZGazPQJZ1K26VrCAQJ715qA==
+ optionalDependencies:
+ esbuild-android-64 "0.14.31"
+ esbuild-android-arm64 "0.14.31"
+ esbuild-darwin-64 "0.14.31"
+ esbuild-darwin-arm64 "0.14.31"
+ esbuild-freebsd-64 "0.14.31"
+ esbuild-freebsd-arm64 "0.14.31"
+ esbuild-linux-32 "0.14.31"
+ esbuild-linux-64 "0.14.31"
+ esbuild-linux-arm "0.14.31"
+ esbuild-linux-arm64 "0.14.31"
+ esbuild-linux-mips64le "0.14.31"
+ esbuild-linux-ppc64le "0.14.31"
+ esbuild-linux-riscv64 "0.14.31"
+ esbuild-linux-s390x "0.14.31"
+ esbuild-netbsd-64 "0.14.31"
+ esbuild-openbsd-64 "0.14.31"
+ esbuild-sunos-64 "0.14.31"
+ esbuild-windows-32 "0.14.31"
+ esbuild-windows-64 "0.14.31"
+ esbuild-windows-arm64 "0.14.31"
+
escape-string-regexp@^1.0.3, escape-string-regexp@^1.0.5:
version "1.0.5"
resolved "https://registry.yarnpkg.com/escape-string-regexp/-/escape-string-regexp-1.0.5.tgz#1b61c0562190a8dff6ae3bb2cf0200ca130b86d4"
@@ -966,10 +1092,10 @@ inherits@2:
resolved "https://registry.yarnpkg.com/inherits/-/inherits-2.0.4.tgz#0fa2c64f932917c3433a0ded55363aae37416b7c"
integrity sha512-k/vGaX4/Yla3WzyMCvTQOXYeIHvqOKtnqBduzTHpzpQZzAskKMhZ2K+EnBiSM9zGSoIFeMpXKxa4dYeZIQqewQ==
-is-core-module@^2.2.0:
- version "2.5.0"
- resolved "https://registry.yarnpkg.com/is-core-module/-/is-core-module-2.5.0.tgz#f754843617c70bfd29b7bd87327400cda5c18491"
- integrity sha512-TXCMSDsEHMEEZ6eCA8rwRDbLu55MRGmrctljsBX/2v1d9/GzqHOxW5c5oPSgrUt2vBFXebu9rGqckXGPWOlYpg==
+is-core-module@^2.8.1:
+ version "2.8.1"
+ resolved "https://registry.yarnpkg.com/is-core-module/-/is-core-module-2.8.1.tgz#f59fdfca701d5879d0a6b100a40aa1560ce27211"
+ integrity sha512-SdNCUs284hr40hFTFP6l0IfZ/RSrMXF3qgoRHd3/79unUTvrFO/JoXwkGm+5J/Oe3E/b5GsnG330uUNgRpu1PA==
dependencies:
has "^1.0.3"
@@ -1113,10 +1239,10 @@ ms@2.1.2:
resolved "https://registry.yarnpkg.com/ms/-/ms-2.1.2.tgz#d09d1f357b443f493382a8eb3ccd183872ae6009"
integrity sha512-sGkPx+VjMtmA6MX27oA4FBFELFCZZ4S4XqeGOXCv68tT+jb3vk/RyaKWP0PTKyWtmLSM0b+adUTEvbs1PEaH2w==
-nanoid@^3.1.28:
- version "3.1.28"
- resolved "https://registry.yarnpkg.com/nanoid/-/nanoid-3.1.28.tgz#3c01bac14cb6c5680569014cc65a2f26424c6bd4"
- integrity sha512-gSu9VZ2HtmoKYe/lmyPFES5nknFrHa+/DT9muUFWFMi6Jh9E1I7bkvlQ8xxf1Kos9pi9o8lBnIOkatMhKX/YUw==
+nanoid@^3.3.3:
+ version "3.3.4"
+ resolved "https://registry.yarnpkg.com/nanoid/-/nanoid-3.3.4.tgz#730b67e3cd09e2deacf03c027c81c9d9dbc5e8ab"
+ integrity sha512-MqBkQh/OHTS2egovRtLk45wEyNXwF+cokD+1YPf9u5VfJiRdAiRwB2froX5Co9Rh20xs4siNPm8naNotSD6RBw==
natural-compare@^1.4.0:
version "1.4.0"
@@ -1193,20 +1319,20 @@ path-key@^3.1.0:
resolved "https://registry.yarnpkg.com/path-key/-/path-key-3.1.1.tgz#581f6ade658cbba65a0d3380de7753295054f375"
integrity sha512-ojmeN0qd+y0jszEtoY48r0Peq5dwMEkIlCOu6Q5f41lfkswXuKtYrhgoTpLnyIcHm24Uhqx+5Tqm2InSwLhE6Q==
-path-parse@^1.0.6:
- version "1.0.6"
- resolved "https://registry.yarnpkg.com/path-parse/-/path-parse-1.0.6.tgz#d62dbb5679405d72c4737ec58600e9ddcf06d24c"
- integrity sha512-GSmOT2EbHrINBf9SR7CDELwlJ8AENk3Qn7OikK4nFYAu3Ote2+JYNVvkpAEQm3/TLNEJFD/xZJjzyxg3KBWOzw==
+path-parse@^1.0.7:
+ version "1.0.7"
+ resolved "https://registry.yarnpkg.com/path-parse/-/path-parse-1.0.7.tgz#fbc114b60ca42b30d9daf5858e4bd68bbedb6735"
+ integrity sha512-LDJzPVEEEPR+y48z93A0Ed0yXb8pAByGWo/k5YYdYgpY2/2EsOsksJrq7lOHxryrVOn1ejG6oAp8ahvOIQD8sw==
path-type@^4.0.0:
version "4.0.0"
resolved "https://registry.yarnpkg.com/path-type/-/path-type-4.0.0.tgz#84ed01c0a7ba380afe09d90a8c180dcd9d03043b"
integrity sha512-gDKb8aZMDeD/tZWs9P6+q0J9Mwkdl6xMV8TjnGP3qJVJ06bdMgkbBlLU8IdfOsIsFz2BW1rNVT3XuNEl8zPAvw==
-picocolors@^0.2.1:
- version "0.2.1"
- resolved "https://registry.yarnpkg.com/picocolors/-/picocolors-0.2.1.tgz#570670f793646851d1ba135996962abad587859f"
- integrity sha512-cMlDqaLEqfSaW8Z7N5Jw+lyIW869EzT73/F5lhtY9cLGoVxSXznfgfXMO0Z5K0o0Q2TkTXq+0KFsdnSe3jDViA==
+picocolors@^1.0.0:
+ version "1.0.0"
+ resolved "https://registry.yarnpkg.com/picocolors/-/picocolors-1.0.0.tgz#cb5bdc74ff3f51892236eaf79d68bc44564ab81c"
+ integrity sha512-1fygroTLlHu66zi26VoTDv8yRgm0Fccecssto+MhsZ0D/DGW2sm8E8AjW7NU5VVTRt5GxbeZ5qBuJr+HyLYkjQ==
picomatch@^2.2.3:
version "2.3.0"
@@ -1232,14 +1358,14 @@ postcss-value-parser@^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.3.8:
- version "8.3.9"
- resolved "https://registry.yarnpkg.com/postcss/-/postcss-8.3.9.tgz#98754caa06c4ee9eb59cc48bd073bb6bd3437c31"
- integrity sha512-f/ZFyAKh9Dnqytx5X62jgjhhzttjZS7hMsohcI7HEI5tjELX/HxCy3EFhsRxyzGvrzFF+82XPvCS8T9TFleVJw==
+postcss@^8.4.13:
+ version "8.4.13"
+ resolved "https://registry.yarnpkg.com/postcss/-/postcss-8.4.13.tgz#7c87bc268e79f7f86524235821dfdf9f73e5d575"
+ integrity sha512-jtL6eTBrza5MPzy8oJLFuUscHDXTV5KcLlqAWHl5q5WYRfnNRGSmOZmOZ1T6Gy7A99mOZfqungmZMpMmCVJ8ZA==
dependencies:
- nanoid "^3.1.28"
- picocolors "^0.2.1"
- source-map-js "^0.6.2"
+ nanoid "^3.3.3"
+ picocolors "^1.0.0"
+ source-map-js "^1.0.2"
prelude-ls@^1.2.1:
version "1.2.1"
@@ -1296,13 +1422,14 @@ resolve-from@^4.0.0:
resolved "https://registry.yarnpkg.com/resolve-from/-/resolve-from-4.0.0.tgz#4abcd852ad32dd7baabfe9b40e00a36db5f392e6"
integrity sha512-pb/MYmXstAkysRFx8piNI1tGFNQIFA3vkE3Gq4EuA1dF6gHp/+vgZqsCGJapvy8N3Q+4o7FwvquPJcnZ7RYy4g==
-resolve@^1.20.0:
- version "1.20.0"
- resolved "https://registry.yarnpkg.com/resolve/-/resolve-1.20.0.tgz#629a013fb3f70755d6f0b7935cc1c2c5378b1975"
- integrity sha512-wENBPt4ySzg4ybFQW2TT1zMQucPK95HSh/nq2CFTZVOGut2+pQvSsgtda4d26YrYcr067wjbmzOG8byDPBX63A==
+resolve@^1.22.0:
+ version "1.22.0"
+ resolved "https://registry.yarnpkg.com/resolve/-/resolve-1.22.0.tgz#5e0b8c67c15df57a89bdbabe603a002f21731198"
+ integrity sha512-Hhtrw0nLeSrFQ7phPp4OOcVjLPIeMnRlr5mcnVuMe7M/7eBn98A3hmFRLoFo3DLZkivSYwhRUJTyPyWAk56WLw==
dependencies:
- is-core-module "^2.2.0"
- path-parse "^1.0.6"
+ is-core-module "^2.8.1"
+ path-parse "^1.0.7"
+ supports-preserve-symlinks-flag "^1.0.0"
reusify@^1.0.4:
version "1.0.4"
@@ -1316,10 +1443,10 @@ rimraf@^3.0.2:
dependencies:
glob "^7.1.3"
-rollup@^2.57.0:
- version "2.58.0"
- resolved "https://registry.yarnpkg.com/rollup/-/rollup-2.58.0.tgz#a643983365e7bf7f5b7c62a8331b983b7c4c67fb"
- integrity sha512-NOXpusKnaRpbS7ZVSzcEXqxcLDOagN6iFS8p45RkoiMqPHDLwJm758UF05KlMoCRbLBTZsPOIa887gZJ1AiXvw==
+rollup@^2.59.0:
+ version "2.70.1"
+ resolved "https://registry.yarnpkg.com/rollup/-/rollup-2.70.1.tgz#824b1f1f879ea396db30b0fc3ae8d2fead93523e"
+ integrity sha512-CRYsI5EuzLbXdxC6RnYhOuRdtz4bhejPMSWjsFLfVM/7w/85n2szZv6yExqUXsBdz5KT8eoubeyDUDjhLHEslA==
optionalDependencies:
fsevents "~2.3.2"
@@ -1373,10 +1500,10 @@ slice-ansi@^4.0.0:
astral-regex "^2.0.0"
is-fullwidth-code-point "^3.0.0"
-source-map-js@^0.6.2:
- version "0.6.2"
- resolved "https://registry.yarnpkg.com/source-map-js/-/source-map-js-0.6.2.tgz#0bb5de631b41cfbda6cfba8bd05a80efdfd2385e"
- integrity sha512-/3GptzWzu0+0MBQFrDKzw/DvvMTUORvgY6k6jd/VS6iCR4RDTKWH6v6WPwQoUO8667uQEf9Oe38DxAYWY5F/Ug==
+source-map-js@^1.0.2:
+ version "1.0.2"
+ resolved "https://registry.yarnpkg.com/source-map-js/-/source-map-js-1.0.2.tgz#adbc361d9c62df380125e7f161f71c826f1e490c"
+ integrity sha512-R0XvVJ9WusLiqTCEiGCmICCMplcCkIwwR11mOSD9CR5u+IXYdiseeEuXCVAjS54zqwkLcPNnmU4OeJ6tUrWhDw==
source-map@~0.6.1:
version "0.6.1"
@@ -1423,6 +1550,11 @@ supports-color@^7.1.0:
dependencies:
has-flag "^4.0.0"
+supports-preserve-symlinks-flag@^1.0.0:
+ version "1.0.0"
+ resolved "https://registry.yarnpkg.com/supports-preserve-symlinks-flag/-/supports-preserve-symlinks-flag-1.0.0.tgz#6eda4bd344a3c94aea376d4cc31bc77311039e09"
+ integrity sha512-ot0WnXS9fgdkgIcePe6RHNk1WA8+muPa6cSjeR3V8K27q9BB1rTE3R1p7Hv0z1ZyAc8s6Vvv8DIyWf681MAt0w==
+
table@^6.0.9:
version "6.7.1"
resolved "https://registry.yarnpkg.com/table/-/table-6.7.1.tgz#ee05592b7143831a8c94f3cee6aae4c1ccef33e2"
@@ -1526,15 +1658,15 @@ v8-compile-cache@^2.0.3:
resolved "https://registry.yarnpkg.com/v8-compile-cache/-/v8-compile-cache-2.3.0.tgz#2de19618c66dc247dcfb6f99338035d8245a2cee"
integrity sha512-l8lCEmLcLYZh4nbunNZvQCJc5pv7+RCwa8q/LdUx8u7lsWvPDKmpodJAJNwkAhJC//dFY48KuIEmjtd4RViDrA==
-vite@^2.6.14:
- version "2.6.14"
- resolved "https://registry.yarnpkg.com/vite/-/vite-2.6.14.tgz#35c09a15e4df823410819a2a239ab11efb186271"
- integrity sha512-2HA9xGyi+EhY2MXo0+A2dRsqsAG3eFNEVIo12olkWhOmc8LfiM+eMdrXf+Ruje9gdXgvSqjLI9freec1RUM5EA==
+vite@^2.9.8:
+ version "2.9.8"
+ resolved "https://registry.yarnpkg.com/vite/-/vite-2.9.8.tgz#2c2cb0790beb0fbe4b8c0995b80fe691a91c2545"
+ integrity sha512-zsBGwn5UT3YS0NLSJ7hnR54+vUKfgzMUh/Z9CxF1YKEBVIe213+63jrFLmZphgGI5zXwQCSmqIdbPuE8NJywPw==
dependencies:
- esbuild "^0.13.2"
- postcss "^8.3.8"
- resolve "^1.20.0"
- rollup "^2.57.0"
+ esbuild "^0.14.27"
+ postcss "^8.4.13"
+ resolve "^1.22.0"
+ rollup "^2.59.0"
optionalDependencies:
fsevents "~2.3.2"