Merge pull request #617 from vector-im/bwindels/sdk-build

SDK build
This commit is contained in:
Bruno Windels 2021-12-22 16:54:03 +01:00 committed by GitHub
commit b5fe65d0cc
No known key found for this signature in database
GPG key ID: 4AEE18F83AFDEB23
16 changed files with 390 additions and 115 deletions

View file

@ -1,70 +1,69 @@
# How to use Hydrogen as an SDK
# Hydrogen View SDK
If you want to use end-to-end encryption, it is recommended to use a [supported build system](../src/sdk/paths/) (currently only vite) to be able to locate the olm library files.
**NOTE**: For now, these instructions will only work at development time, support when building (e.g. `vite build`) is being worked on and tracked in [#529](https://github.com/vector-im/hydrogen-web/issues/529).
The Hydrogen view SDK allows developers to integrate parts of the Hydrogen application into the UI of their own application. Hydrogen is written with the MVVM pattern, so to construct a view, you'd first construct a view model, which you then pass into the view. For most view models, you will first need a running client.
You can create a project using the following commands
## Example
The Hydrogen SDK requires some assets to be shipped along with your app for things like downloading attachments, and end-to-end encryption. A convenient way to make this happen is provided by the SDK (importing `hydrogen-view-sdk/paths/vite`) but depends on your build system. Currently, only [vite](https://vitejs.dev/) is supported, so that's what we'll be using in the example below.
You can create a vite project using the following commands:
```sh
# you can pick "vanilla-ts" here for project type if you're not using react or vue
yarn create vite
cd <your-project-name>
yarn
yarn add https://github.com/vector-im/hydrogen-web.git
yarn add hydrogen-view-sdk
```
If you go into the `src` directory, you should see a `main.ts` file. If you put this code in there, you should see a basic timeline after login and initial sync have finished.
You should see a `index.html` in the project root directory, containing an element with `id="app"`. Add the attribute `class="hydrogen"` to this element, as the CSS we'll include from the SDK assumes for now that the app is rendered in an element with this classname.
If you go into the `src` directory, you should see a `main.ts` file. If you put this code in there, you should see a basic timeline after login and initial sync have finished (might take a while before you see anything on the screen actually).
You'll need to provide the username and password of a user that is already in the [#element-dev:matrix.org](https://matrix.to/#/#element-dev:matrix.org) room (or change the room id).
```ts
import {
Platform,
SessionContainer,
Client,
LoadStatus,
createNavigation,
createRouter,
RoomViewModel,
TimelineView
} from "hydrogen-web";
import {olmPaths, downloadSandboxPath} from "hydrogen-web/src/sdk/paths/vite";
} from "hydrogen-view-sdk";
import assetPaths from "hydrogen-view-sdk/paths/vite";
import "hydrogen-view-sdk/style.css";
const app = document.querySelector<HTMLDivElement>('#app')!
// bootstrap a session container
const platform = new Platform(app, {
downloadSandbox: downloadSandboxPath,
olm: olmPaths,
}, null, { development: true });
const navigation = createNavigation();
platform.setNavigation(navigation);
const urlRouter = createRouter({
async function main() {
const app = document.querySelector<HTMLDivElement>('#app')!
const platform = new Platform(app, assetPaths, { development: import.meta.env.DEV });
const navigation = createNavigation();
platform.setNavigation(navigation);
const urlRouter = createRouter({
navigation: navigation,
history: platform.history
});
urlRouter.attach();
const sessionContainer = new SessionContainer({
platform,
olmPromise: platform.loadOlm(),
workerPromise: platform.loadOlmWorker()
});
});
urlRouter.attach();
const client = new Client(platform);
// wait for login and first sync to finish
const loginOptions = await sessionContainer.queryLogin("matrix.org").result;
sessionContainer.startWithLogin(loginOptions.password("user", "password"));
await sessionContainer.loadStatus.waitFor((status: string) => {
const loginOptions = await client.queryLogin("matrix.org").result;
client.startWithLogin(loginOptions.password("username", "password"));
await client.loadStatus.waitFor((status: string) => {
return status === LoadStatus.Ready ||
status === LoadStatus.Error ||
status === LoadStatus.LoginFailed;
}).promise;
// check the result
if (sessionContainer.loginFailure) {
alert("login failed: " + sessionContainer.loginFailure);
} else if (sessionContainer.loadError) {
alert("load failed: " + sessionContainer.loadError.message);
} else {
// we're logged in, we can access the room now
const {session} = sessionContainer;
// room id for #element-dev:matrix.org
}).promise;
if (client.loginFailure) {
alert("login failed: " + client.loginFailure);
} else if (client.loadError) {
alert("load failed: " + client.loadError.message);
} else {
const {session} = client;
// looks for room corresponding to #element-dev:matrix.org, assuming it is already joined
const room = session.rooms.get("!bEWtlqtDwCLFIAKAcv:matrix.org");
const vm = new RoomViewModel({
room,
@ -76,5 +75,20 @@ if (sessionContainer.loginFailure) {
await vm.load();
const view = new TimelineView(vm.timelineViewModel);
app.appendChild(view.mount());
}
}
main();
```
## Typescript support
There is partial typescript support while we are still in the process of converting the Hydrogen codebase to typesccript.
## API Stability
This library follows semantic versioning; there is no API stability promised as long as the major version is still 0. Once 1.0.0 is released, breaking changes will be released with a change in major versioning.
## Third-party licenses
This package bundles the bs58 package ([license](https://github.com/cryptocoinjs/bs58/blob/master/LICENSE)), and the Inter font ([license](https://github.com/rsms/inter/blob/master/LICENSE.txt)).

View file

@ -2,7 +2,6 @@
"name": "hydrogen-web",
"version": "0.2.22",
"description": "A javascript matrix client prototype, trying to minize RAM usage by offloading as much as possible to IndexedDB",
"main": "src/lib.ts",
"directories": {
"doc": "doc"
},
@ -12,7 +11,8 @@
"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/",
"start": "vite --port 3000",
"build": "vite build"
"build": "vite build",
"build:sdk": "./scripts/sdk/build.sh"
},
"repository": {
"type": "git",
@ -27,27 +27,31 @@
"devDependencies": {
"@typescript-eslint/eslint-plugin": "^4.29.2",
"@typescript-eslint/parser": "^4.29.2",
"acorn": "^8.6.0",
"acorn-walk": "^8.2.0",
"aes-js": "^3.1.2",
"core-js": "^3.6.5",
"es6-promise": "https://github.com/bwindels/es6-promise.git#bwindels/expose-flush",
"escodegen": "^2.0.0",
"eslint": "^7.32.0",
"fake-indexeddb": "^3.1.2",
"impunity": "^1.0.9",
"mdn-polyfills": "^5.20.0",
"merge-options": "^3.0.4",
"node-html-parser": "^4.0.0",
"postcss-css-variables": "^0.18.0",
"postcss-flexbugs-fixes": "^5.0.2",
"regenerator-runtime": "^0.13.7",
"text-encoding": "^0.7.0",
"typescript": "^4.3.5",
"vite": "^2.6.14",
"xxhashjs": "^0.2.2"
},
"dependencies": {
"@matrix-org/olm": "https://gitlab.matrix.org/api/v4/projects/27/packages/npm/@matrix-org/olm/-/@matrix-org/olm-3.2.3.tgz",
"aes-js": "^3.1.2",
"another-json": "^0.2.0",
"base64-arraybuffer": "^0.2.0",
"bs58": "^4.0.1",
"dompurify": "^2.3.0",
"es6-promise": "https://github.com/bwindels/es6-promise.git#bwindels/expose-flush",
"text-encoding": "^0.7.0"
"dompurify": "^2.3.0"
}
}

View file

@ -12,6 +12,7 @@ function injectServiceWorker(swFile, otherUnhashedFiles, placeholdersPerChunk) {
const swName = path.basename(swFile);
let root;
let version;
let logger;
return {
name: "hydrogen:injectServiceWorker",
@ -27,6 +28,7 @@ function injectServiceWorker(swFile, otherUnhashedFiles, placeholdersPerChunk) {
configResolved: config => {
root = config.root;
version = JSON.parse(config.define.DEFINE_VERSION); // unquote
logger = config.logger;
},
generateBundle: async function(options, bundle) {
const unhashedFilenames = [swName].concat(otherUnhashedFiles);
@ -46,7 +48,7 @@ function injectServiceWorker(swFile, otherUnhashedFiles, placeholdersPerChunk) {
...getCacheFileNamePlaceholderValues(swName, unhashedFilenames, assets, placeholdersPerChunk)
};
replacePlaceholdersInChunks(assets, placeholdersPerChunk, placeholderValues);
console.log(`\nBuilt ${version} (${globalHash})`);
logger.info(`\nBuilt ${version} (${globalHash})`);
}
};
}

View file

@ -0,0 +1,14 @@
{
"name": "hydrogen-view-sdk",
"description": "Embeddable matrix client library, including view components",
"version": "0.0.1",
"main": "./hydrogen.cjs.js",
"exports": {
".": {
"import": "./hydrogen.es.js",
"require": "./hydrogen.cjs.js"
},
"./paths/vite": "./paths/vite.js"
},
"types": "types/lib.d.ts"
}

20
scripts/sdk/build.sh Executable file
View file

@ -0,0 +1,20 @@
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
./scripts/sdk/create-manifest.js ./target/package.json
mkdir target/paths
./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
mv index.*.css ../../style.css
mv download-sandbox.*.html ../../download-sandbox.html
rm *.js *.wasm
mv ./* ../../
popd
rm -rf asset-build
mv lib-build/* .
rm -rf lib-build
popd

15
scripts/sdk/create-manifest.js Executable file
View file

@ -0,0 +1,15 @@
#!/usr/bin/env node
const fs = require("fs");
const appManifest = require("../../package.json")
const baseSDKManifest = require("./base-manifest.json")
const mergeOptions = require('merge-options');
const manifestExtension = {
devDependencies: undefined,
scripts: undefined,
};
const manifest = mergeOptions(appManifest, baseSDKManifest, manifestExtension);
const json = JSON.stringify(manifest, undefined, 2);
const outFile = process.argv[2];
fs.writeFileSync(outFile, json, {encoding: "utf8"});

36
scripts/sdk/transform-paths.js Executable file
View file

@ -0,0 +1,36 @@
#!/usr/bin/env node
/**
This script transforms the string literals in the sdk path files to adjust paths
from what they are at development time to what they will be in the sdk package.
It does this by looking in all string literals in the paths file and looking for file names
that we expect and need replacing (as they are bundled with the sdk).
Usage: ./transform-paths.js <input file> <output file>
*/
const acorn = require("acorn");
const walk = require("acorn-walk")
const escodegen = require("escodegen");
const fs = require("fs");
const code = fs.readFileSync(process.argv[2], {encoding: "utf8"});
const ast = acorn.parse(code, {ecmaVersion: "13", sourceType: "module"});
function changePrefix(value, file, newPrefix = "") {
const idx = value.indexOf(file);
if (idx !== -1) {
return newPrefix + value.substr(idx);
}
return value;
}
walk.simple(ast, {
Literal(node) {
node.value = changePrefix(node.value, "download-sandbox.html", "../");
node.value = changePrefix(node.value, "main.js", "../");
}
});
const transformedCode = escodegen.generate(ast);
fs.writeFileSync(process.argv[3], transformedCode, {encoding: "utf8"})

13
src/index.html Normal file
View file

@ -0,0 +1,13 @@
<!DOCTYPE html>
<!-- this file contains all references to include in the SDK asset build (using vite.sdk-assets-config.js) -->
<html>
<head>
<link rel="stylesheet" type="text/css" href="./platform/web/ui/css/main.css">
<link rel="stylesheet" type="text/css" href="./platform/web/ui/css/themes/element/theme.css">
</head>
<body>
<script type="module">
import "./platform/web/sdk/paths/vite";
</script>
</body>
</html>

14
tsconfig-declaration.json Normal file
View file

@ -0,0 +1,14 @@
{
"extends": "./tsconfig.json",
"compilerOptions": {
"noEmit": false,
"emitDeclarationOnly": true,
"declaration": true,
"outDir": "target/types",
"rootDir": "src"
},
"exclude": [
"src/sdk/paths/*"
],
"include": ["src/**/*"],
}

View file

@ -1,9 +1,12 @@
{
"compilerOptions": {
"strictNullChecks": true,
"noImplicitAny": false,
"noEmit": true,
"target": "ES2020",
"moduleResolution": "node"
"module": "ES2020",
"moduleResolution": "node",
"esModuleInterop": true
},
"exclude": [
"src/sdk/paths/*"

49
vite.common-config.js Normal file
View file

@ -0,0 +1,49 @@
const cssvariables = require("postcss-css-variables");
const flexbugsFixes = require("postcss-flexbugs-fixes");
const fs = require("fs");
const path = require("path");
const manifest = require("./package.json");
const version = manifest.version;
const commonOptions = {
logLevel: "warn",
publicDir: false,
server: {
hmr: false
},
resolve: {
alias: {
// these should only be imported by the base-x package in any runtime code
// and works in the browser with a Uint8Array shim,
// rather than including a ton of polyfill code
"safe-buffer": "./scripts/package-overrides/safe-buffer/index.js",
"buffer": "./scripts/package-overrides/buffer/index.js",
}
},
build: {
emptyOutDir: true,
assetsInlineLimit: 0,
polyfillModulePreload: false,
},
define: {
DEFINE_VERSION: JSON.stringify(version),
DEFINE_GLOBAL_HASH: JSON.stringify(null),
},
css: {
postcss: {
plugins: [
cssvariables({
preserve: (declaration) => {
return declaration.value.indexOf("var(--ios-") == 0;
}
}),
// the grid option creates some source fragment that causes the vite warning reporter to crash because
// it wants to log a warning on a line that does not exist in the source fragment.
// autoprefixer({overrideBrowserslist: ["IE 11"], grid: "no-autoplace"}),
flexbugsFixes()
]
}
}
};
module.exports = commonOptions;

View file

@ -1,39 +1,18 @@
const cssvariables = require("postcss-css-variables");
const flexbugsFixes = require("postcss-flexbugs-fixes");
const fs = require("fs");
const path = require("path");
const injectWebManifest = require("./scripts/build-plugins/manifest");
const {injectServiceWorker, createPlaceholderValues} = require("./scripts/build-plugins/service-worker");
const {defineConfig} = require('vite');
const version = JSON.parse(fs.readFileSync(path.join(__dirname, "package.json"), "utf8")).version;
const mergeOptions = require('merge-options').bind({concatArrays: true});
const commonOptions = require("./vite.common-config.js");
export default defineConfig(({mode}) => {
const definePlaceholders = createPlaceholderValues(mode);
return {
public: false,
return mergeOptions(commonOptions, {
root: "src/platform/web",
base: "./",
server: {
hmr: false
},
resolve: {
alias: {
// these should only be imported by the base-x package in any runtime code
// and works in the browser with a Uint8Array shim,
// rather than including a ton of polyfill code
"safe-buffer": "./scripts/package-overrides/safe-buffer/index.js",
"buffer": "./scripts/package-overrides/buffer/index.js",
}
},
build: {
outDir: "../../../target",
emptyOutDir: true,
minify: true,
sourcemap: true,
assetsInlineLimit: 0,
polyfillModulePreload: false,
},
plugins: [
// important this comes before service worker
@ -45,28 +24,6 @@ export default defineConfig(({mode}) => {
"sw": definePlaceholders
}),
],
define: {
DEFINE_VERSION: JSON.stringify(version),
...definePlaceholders
},
css: {
postcss: {
plugins: [
cssvariables({
preserve: (declaration) => {
return declaration.value.indexOf("var(--ios-") == 0;
}
}),
// the grid option creates some source fragment that causes the vite warning reporter to crash because
// it wants to log a warning on a line that does not exist in the source fragment.
// autoprefixer({overrideBrowserslist: ["IE 11"], grid: "no-autoplace"}),
flexbugsFixes()
]
}
}
};
define: definePlaceholders,
});
});
function scriptTagPath(htmlFile, index) {
return `${htmlFile}?html-proxy&index=${index}.js`;
}

11
vite.sdk-assets-config.js Normal file
View file

@ -0,0 +1,11 @@
const path = require("path");
const mergeOptions = require('merge-options');
const commonOptions = require("./vite.common-config.js");
export default mergeOptions(commonOptions, {
root: "src/",
base: "./",
build: {
outDir: "../target/asset-build/",
},
});

47
vite.sdk-lib-config.js Normal file
View file

@ -0,0 +1,47 @@
const path = require("path");
const mergeOptions = require('merge-options');
const commonOptions = require("./vite.common-config.js");
const manifest = require("./package.json");
const externalDependencies = Object.keys(manifest.dependencies)
// just in case for safety in case fake-indexeddb wouldn't be
// treeshake'd out of the bundle
.concat(Object.keys(manifest.devDependencies))
// bundle bs58 because it uses buffer indirectly, which is a pain to bundle,
// so we don't annoy our library users with it.
.filter(d => d !== "bs58");
const moduleDir = path.join(__dirname, "node_modules");
export default mergeOptions(commonOptions, {
root: "src/",
build: {
lib: {
entry: path.resolve(__dirname, 'src/lib.ts'),
formats: ["cjs", "es"],
fileName: format => `hydrogen.${format}.js`,
},
minify: false,
sourcemap: false,
outDir: "../target/lib-build",
// don't bundle any dependencies, they should be imported/required
rollupOptions: {
external(id) {
return externalDependencies.some(d => id === d || id.startsWith(d + "/"));
},
/* don't bundle, so we can override imports per file at build time to replace components */
// output: {
// manualChunks: (id) => {
// if (id.startsWith(srcDir)) {
// const idPath = id.substring(srcDir.length);
// const pathWithoutExt = idPath.substring(0, idPath.lastIndexOf("."));
// return pathWithoutExt;
// } else {
// return "index";
// }
// },
// minifyInternalExports: false,
// chunkFileNames: "[format]/[name].js"
// }
}
},
});

View file

@ -156,11 +156,21 @@ acorn-jsx@^5.3.1:
resolved "https://registry.yarnpkg.com/acorn-jsx/-/acorn-jsx-5.3.1.tgz#fc8661e11b7ac1539c47dbfea2e72b3af34d267b"
integrity sha512-K0Ptm/47OKfQRpNQ2J/oIN/3QYiK6FwW+eJbILhsdxh2WTLdl+30o8aGdTbm5JbffpFFAg/g+zi1E+jvJha5ng==
acorn-walk@^8.2.0:
version "8.2.0"
resolved "https://registry.yarnpkg.com/acorn-walk/-/acorn-walk-8.2.0.tgz#741210f2e2426454508853a2f44d0ab83b7f69c1"
integrity sha512-k+iyHEuPgSw6SbuDpGQM+06HQUa04DZ3o+F6CSzXMvvI5KMvnaEqXe+YVe555R9nn6GPt404fos4wcgpw12SDA==
acorn@^7.4.0:
version "7.4.1"
resolved "https://registry.yarnpkg.com/acorn/-/acorn-7.4.1.tgz#feaed255973d2e77555b83dbc08851a6c63520fa"
integrity sha512-nQyp0o1/mNdbTO1PO6kHkwSrmgZ0MT/jCCpNiwbUjGoRN4dlBhqJtoQuCnEOKzgTVwg0ZWiCoQy6SxMebQVh8A==
acorn@^8.6.0:
version "8.6.0"
resolved "https://registry.yarnpkg.com/acorn/-/acorn-8.6.0.tgz#e3692ba0eb1a0c83eaa4f37f5fa7368dd7142895"
integrity sha512-U1riIR+lBSNi3IbxtaHOIKdH8sLFv3NYfNv8sg7ZsNhcfl4HF2++BfqqrNAxoCLQW1iiylOj76ecnaUxz+z9yw==
aes-js@^3.1.2:
version "3.1.2"
resolved "https://registry.yarnpkg.com/aes-js/-/aes-js-3.1.2.tgz#db9aabde85d5caabbfc0d4f2a4446960f627146a"
@ -401,6 +411,11 @@ deep-is@^0.1.3:
resolved "https://registry.yarnpkg.com/deep-is/-/deep-is-0.1.3.tgz#b369d6fb5dbc13eecf524f91b070feedc357cf34"
integrity sha1-s2nW+128E+7PUk+RsHD+7cNXzzQ=
deep-is@~0.1.3:
version "0.1.4"
resolved "https://registry.yarnpkg.com/deep-is/-/deep-is-0.1.4.tgz#a6f2dce612fadd2ef1f519b73551f17e85199831"
integrity sha512-oIPzksmTg4/MriiaYGO+okXDT7ztn/w3Eptv/+gSIdMdKsJo0u4CfYNFJPy+4SKMuCqGw2wxnA+URMg3t8a/bQ==
dir-glob@^3.0.1:
version "3.0.1"
resolved "https://registry.yarnpkg.com/dir-glob/-/dir-glob-3.0.1.tgz#56dbf73d992a4a93ba1584f4534063fd2e41717f"
@ -608,6 +623,18 @@ escape-string-regexp@^4.0.0:
resolved "https://registry.yarnpkg.com/escape-string-regexp/-/escape-string-regexp-4.0.0.tgz#14ba83a5d373e3d311e5afca29cf5bfad965bf34"
integrity sha512-TtpcNJ3XAzx3Gq8sWRzJaVajRs0uVxA2YAkdb1jm2YkPz4G6egUFAyA3n5vtEIZefPk5Wa4UXbKuS5fKkJWdgA==
escodegen@^2.0.0:
version "2.0.0"
resolved "https://registry.yarnpkg.com/escodegen/-/escodegen-2.0.0.tgz#5e32b12833e8aa8fa35e1bf0befa89380484c7dd"
integrity sha512-mmHKys/C8BFUGI+MAWNcSYoORYLMdPzjrknd2Vc+bUsjN5bXcr8EhrNB+UTqfL1y3I9c4fw2ihgtMPQLBRiQxw==
dependencies:
esprima "^4.0.1"
estraverse "^5.2.0"
esutils "^2.0.2"
optionator "^0.8.1"
optionalDependencies:
source-map "~0.6.1"
eslint-scope@^5.1.1:
version "5.1.1"
resolved "https://registry.yarnpkg.com/eslint-scope/-/eslint-scope-5.1.1.tgz#e786e59a66cb92b3f6c1fb0d508aab174848f48c"
@ -695,7 +722,7 @@ espree@^7.3.0, espree@^7.3.1:
acorn-jsx "^5.3.1"
eslint-visitor-keys "^1.3.0"
esprima@^4.0.0:
esprima@^4.0.0, esprima@^4.0.1:
version "4.0.1"
resolved "https://registry.yarnpkg.com/esprima/-/esprima-4.0.1.tgz#13b04cdb3e6c5d19df91ab6987a8695619b0aa71"
integrity sha512-eGuFFw7Upda+g4p+QHvnW0RyTX/SVeJBDM/gCtMARO0cLuT2HcEKnTPvhjV6aGeqrCB/sbNop0Kszm0jsaWU4A==
@ -763,7 +790,7 @@ fast-json-stable-stringify@^2.0.0:
resolved "https://registry.yarnpkg.com/fast-json-stable-stringify/-/fast-json-stable-stringify-2.1.0.tgz#874bf69c6f404c2b5d99c481341399fd55892633"
integrity sha512-lhd/wF+Lk98HZoTCtlVraHtfh5XYijIjalXck7saUtuanSDyLMxnHhSXEDJqHxD7msR8D0uCmqlkwjCV8xvwHw==
fast-levenshtein@^2.0.6:
fast-levenshtein@^2.0.6, fast-levenshtein@~2.0.6:
version "2.0.6"
resolved "https://registry.yarnpkg.com/fast-levenshtein/-/fast-levenshtein-2.0.6.tgz#3d8a5c66883a16a30ca8643e851f19baa7797917"
integrity sha1-PYpcZog6FqMMqGQ+hR8Zuqd5eRc=
@ -963,6 +990,11 @@ is-number@^7.0.0:
resolved "https://registry.yarnpkg.com/is-number/-/is-number-7.0.0.tgz#7535345b896734d5f80c4d06c50955527a14f12b"
integrity sha512-41Cifkg6e8TylSpdtTpeLVMqvSBEVzTttHvERD741+pnZ8ANv0004MRL43QKPDlK9cGvNp6NZWZUBlbGXYxxng==
is-plain-obj@^2.1.0:
version "2.1.0"
resolved "https://registry.yarnpkg.com/is-plain-obj/-/is-plain-obj-2.1.0.tgz#45e42e37fccf1f40da8e5f76ee21515840c09287"
integrity sha512-YWnfyRwxL/+SsrWYfOpUtz5b3YD+nyfkHvjbcanzk8zgyO4ASD67uVMRt8k5bM4lLMDnXfriRhOpemw+NfT1eA==
isexe@^2.0.0:
version "2.0.0"
resolved "https://registry.yarnpkg.com/isexe/-/isexe-2.0.0.tgz#e8fbf374dc556ff8947a10dcb0572d633f2cfa10"
@ -1004,6 +1036,14 @@ levn@^0.4.1:
prelude-ls "^1.2.1"
type-check "~0.4.0"
levn@~0.3.0:
version "0.3.0"
resolved "https://registry.yarnpkg.com/levn/-/levn-0.3.0.tgz#3b09924edf9f083c0490fdd4c0bc4421e04764ee"
integrity sha1-OwmSTt+fCDwEkP3UwLxEIeBHZO4=
dependencies:
prelude-ls "~1.1.2"
type-check "~0.3.2"
lodash.clonedeep@^4.5.0:
version "4.5.0"
resolved "https://registry.yarnpkg.com/lodash.clonedeep/-/lodash.clonedeep-4.5.0.tgz#e23f3f9c4f8fbdde872529c1071857a086e5ccef"
@ -1036,6 +1076,13 @@ mdn-polyfills@^5.20.0:
resolved "https://registry.yarnpkg.com/mdn-polyfills/-/mdn-polyfills-5.20.0.tgz#ca8247edf20a4f60dec6804372229812b348260b"
integrity sha512-AbTv1ytcoOUAkxw6u5oo2QPf27kEZgxBAQr49jFb4i2VnTnFGfJbcIQ9UDBOdfNECeXsgkYFwB2BkdeTfOzztw==
merge-options@^3.0.4:
version "3.0.4"
resolved "https://registry.yarnpkg.com/merge-options/-/merge-options-3.0.4.tgz#84709c2aa2a4b24c1981f66c179fe5565cc6dbb7"
integrity sha512-2Sug1+knBjkaMsMgf1ctR1Ujx+Ayku4EdJN4Z+C2+JzoeF7A3OZ9KM2GY0CpQS51NR61LTurMJrRKPhSs3ZRTQ==
dependencies:
is-plain-obj "^2.1.0"
merge2@^1.3.0:
version "1.4.1"
resolved "https://registry.yarnpkg.com/merge2/-/merge2-1.4.1.tgz#4368892f885e907455a6fd7dc55c0c9d404990ae"
@ -1093,6 +1140,18 @@ once@^1.3.0:
dependencies:
wrappy "1"
optionator@^0.8.1:
version "0.8.3"
resolved "https://registry.yarnpkg.com/optionator/-/optionator-0.8.3.tgz#84fa1d036fe9d3c7e21d99884b601167ec8fb495"
integrity sha512-+IW9pACdk3XWmmTXG8m3upGUJst5XRGzxMRjXzAuJ1XnIFNvfhjjIuYkDvysnPQ7qzqVzLt78BCruntqRhWQbA==
dependencies:
deep-is "~0.1.3"
fast-levenshtein "~2.0.6"
levn "~0.3.0"
prelude-ls "~1.1.2"
type-check "~0.3.2"
word-wrap "~1.2.3"
optionator@^0.9.1:
version "0.9.1"
resolved "https://registry.yarnpkg.com/optionator/-/optionator-0.9.1.tgz#4f236a6373dae0566a6d43e1326674f50c291499"
@ -1170,6 +1229,11 @@ prelude-ls@^1.2.1:
resolved "https://registry.yarnpkg.com/prelude-ls/-/prelude-ls-1.2.1.tgz#debc6489d7a6e6b0e7611888cec880337d316396"
integrity sha512-vkcDPrRZo1QZLbn5RLGPpg/WmIQ65qoWWhcGKf/b5eplkkarX0m9z8ppCat4mlOqUsWpyNuYgO3VRyrYHSzX5g==
prelude-ls@~1.1.2:
version "1.1.2"
resolved "https://registry.yarnpkg.com/prelude-ls/-/prelude-ls-1.1.2.tgz#21932a549f5e52ffd9a827f570e04be62a97da54"
integrity sha1-IZMqVJ9eUv/ZqCf1cOBL5iqX2lQ=
progress@^2.0.0:
version "2.0.3"
resolved "https://registry.yarnpkg.com/progress/-/progress-2.0.3.tgz#7e8cf8d8f5b8f239c1bc68beb4eb78567d572ef8"
@ -1297,6 +1361,11 @@ source-map-js@^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@~0.6.1:
version "0.6.1"
resolved "https://registry.yarnpkg.com/source-map/-/source-map-0.6.1.tgz#74722af32e9614e9c287a8d0bbde48b5e2f1a263"
integrity sha512-UjgapumWlbMhkBgzT7Ykc5YXUT46F0iKu8SGXq0bcwP5dz/h0Plj6enJqjz1Zbq2l5WaqYnrVbwWOWMyF3F47g==
sprintf-js@~1.0.2:
version "1.0.3"
resolved "https://registry.yarnpkg.com/sprintf-js/-/sprintf-js-1.0.3.tgz#04e6926f662895354f3dd015203633b857297e2c"
@ -1392,6 +1461,13 @@ type-check@^0.4.0, type-check@~0.4.0:
dependencies:
prelude-ls "^1.2.1"
type-check@~0.3.2:
version "0.3.2"
resolved "https://registry.yarnpkg.com/type-check/-/type-check-0.3.2.tgz#5884cab512cf1d355e3fb784f30804b2b520db72"
integrity sha1-WITKtRLPHTVeP7eE8wgEsrUg23I=
dependencies:
prelude-ls "~1.1.2"
type-fest@^0.20.2:
version "0.20.2"
resolved "https://registry.yarnpkg.com/type-fest/-/type-fest-0.20.2.tgz#1bf207f4b28f91583666cb5fbd327887301cd5f4"
@ -1471,7 +1547,7 @@ which@^2.0.1:
dependencies:
isexe "^2.0.0"
word-wrap@^1.2.3:
word-wrap@^1.2.3, word-wrap@~1.2.3:
version "1.2.3"
resolved "https://registry.yarnpkg.com/word-wrap/-/word-wrap-1.2.3.tgz#610636f6b1f703891bd34771ccb17fb93b47079c"
integrity sha512-Hz/mrNwitNRh/HUAtM/VT/5VH+ygD6DV7mYKZAtHOrbs8U7lvPS6xf7EJKMF0uW1KJCl0H701g3ZGus+muE5vQ==