diff --git a/Makefile b/Makefile index d22ce4b..4aa7833 100644 --- a/Makefile +++ b/Makefile @@ -97,7 +97,7 @@ build: frontend system .PHONY: dev dev: - make -j2 frontend-dev system-dev server-dev + make -j3 frontend-dev system-dev server-dev ### Run application code diff --git a/backend/config.js b/backend/config.js deleted file mode 100644 index 15a678f..0000000 --- a/backend/config.js +++ /dev/null @@ -1,3 +0,0 @@ -import process from "process"; - -export const PRIVILEGED = process.env.RIJU_PRIVILEGED ? true : false; diff --git a/backend/langs.js b/backend/langs.js index cf45b8e..7225d96 100644 --- a/backend/langs.js +++ b/backend/langs.js @@ -1,2 +1,36 @@ -// TODO -export const langs = {}; +import { promises as fs } from "fs"; +import path from "path"; + +import { log } from "./util.js"; + +// Map from language IDs to language configuration objects. This is +// populated at runtime and updated asynchronously. +export let langs = {}; + +async function readLangsFromDisk() { + const newLangs = {}; + for (const filename of await fs.readdir("/opt/riju/langs")) { + if (path.parse(filename).ext !== ".json") { + continue; + } + const id = path.parse(filename).name; + const langConfig = JSON.parse( + await fs.readFile(`/opt/riju/langs/${filename}`, "utf-8") + ); + if (langConfig.id !== id) { + log.error( + "Language config ${filename} has mismatched language ID ${id}, ignoring" + ); + continue; + } + newLangs[id] = langConfig; + } + log.info( + `Loaded ${Object.keys(newLangs).length} language configuration(s) from disk` + ); + langs = newLangs; +} + +readLangsFromDisk().catch((err) => { + log.error("Failed to read languages from disk:", err); +}); diff --git a/backend/users.js b/backend/users.js index aaf3f04..2c6dfb6 100644 --- a/backend/users.js +++ b/backend/users.js @@ -6,7 +6,6 @@ import AsyncLock from "async-lock"; import _ from "lodash"; import parsePasswd from "parse-passwd"; -import { PRIVILEGED } from "./config.js"; import { privilegedUseradd, run } from "./util.js"; // Keep in sync with system/src/riju-system-privileged.c @@ -70,27 +69,23 @@ export async function ignoreUsers(uids, log) { } export async function borrowUser(log) { - if (!PRIVILEGED) { - return { uid: CUR_UID, returnUID: async () => {} }; - } else { - return await lock.acquire("key", async () => { - if (availIds === null || nextId === null) { - await readExistingUsers(log); - } - let uid; - if (availIds.length > 0) { - uid = availIds.pop(); - } else { - uid = await createUser(log); - } - return { - uid, - returnUID: async () => { - await lock.acquire("key", () => { - availIds.push(uid); - }); - }, - }; - }); - } + return await lock.acquire("key", async () => { + if (availIds === null || nextId === null) { + await readExistingUsers(log); + } + let uid; + if (availIds.length > 0) { + uid = availIds.pop(); + } else { + uid = await createUser(log); + } + return { + uid, + returnUID: async () => { + await lock.acquire("key", () => { + availIds.push(uid); + }); + }, + }; + }); } diff --git a/backend/util.js b/backend/util.js index bd9285c..a61f640 100644 --- a/backend/util.js +++ b/backend/util.js @@ -4,7 +4,6 @@ import process from "process"; import { quote } from "shell-quote"; -import { PRIVILEGED } from "./config.js"; import { MIN_UID, MAX_UID } from "./users.js"; export const rijuSystemPrivileged = "system/out/riju-system-privileged"; @@ -39,7 +38,7 @@ function getEnv({ uid, uuid }) { LANG: process.env.LANG || "", LC_ALL: process.env.LC_ALL || "", LOGNAME: username, - PATH: PRIVILEGED ? path.join(":") : process.env.PATH || "", + PATH: path.join(":"), PWD: cwd, SHELL: "/usr/bin/bash", TERM: "xterm-256color", @@ -122,3 +121,11 @@ export function bash(cmdline) { } return ["bash", "-c", cmdline]; } + +export const log = { + trace: console.error, + debug: console.error, + info: console.error, + warn: console.error, + error: console.error, +}; diff --git a/docker/app/Dockerfile b/docker/app/Dockerfile index 9af3c7a..31c21c7 100644 --- a/docker/app/Dockerfile +++ b/docker/app/Dockerfile @@ -4,7 +4,6 @@ FROM riju:composite RUN useradd -p '!' -m -l -s /usr/bin/bash riju WORKDIR /src -ENV RIJU_PRIVILEGED=1 COPY --chown=riju:riju --from=compile . ./ RUN chown root:riju system/out/*-privileged && chmod a=,g=rx,u=rwxs system/out/*-privileged diff --git a/docker/compile/Dockerfile b/docker/compile/Dockerfile index 041debe..6307c6e 100644 --- a/docker/compile/Dockerfile +++ b/docker/compile/Dockerfile @@ -4,7 +4,6 @@ COPY docker/runtime/install.bash /tmp/ RUN /tmp/install.bash WORKDIR /src -ENV RIJU_PRIVILEGED=1 COPY package.json yarn.lock ./ RUN yarn install diff --git a/docker/runtime/Dockerfile b/docker/runtime/Dockerfile index 30035fb..b3e4fd0 100644 --- a/docker/runtime/Dockerfile +++ b/docker/runtime/Dockerfile @@ -9,3 +9,4 @@ ENTRYPOINT ["/usr/local/sbin/my_init", "/usr/local/sbin/pid1.bash"] CMD ["bash"] EXPOSE 6119 EXPOSE 6120 +ENV HOST=0.0.0.0 diff --git a/frontend/pages/index.ejs b/frontend/pages/index.ejs index 59d2ff2..252f498 100644 --- a/frontend/pages/index.ejs +++ b/frontend/pages/index.ejs @@ -7,25 +7,29 @@
- - Created by - Radon Rosborough. - Check out the project - on GitHub. - -
+ <% if (Object.keys(langs).length > 0) { %> + Pick your favorite language to get started: ++ + Created by + Radon Rosborough. + Check out the project + on GitHub. + +
+ <% } else { %> + Riju is loading language configuration... + <% } %> <% if (analyticsEnabled) { %> <% } %> diff --git a/package.json b/package.json index 79a5172..4215bad 100644 --- a/package.json +++ b/package.json @@ -9,6 +9,7 @@ "@babel/preset-env": "^7.12.11", "async-lock": "^1.2.6", "babel-loader": "^8.2.2", + "buffer": "^6.0.3", "commander": "^6.2.1", "css-loader": "^5.0.1", "ejs": "^3.1.5", @@ -22,6 +23,7 @@ "node-pty": "^0.9.0", "p-queue": "^6.6.2", "parse-passwd": "^1.0.0", + "regenerator-runtime": "^0.13.7", "shell-quote": "^1.7.2", "style-loader": "^2.0.0", "uuid": "^8.3.2", diff --git a/system/compile.bash b/system/compile.bash index 2813db4..dbe7da1 100755 --- a/system/compile.bash +++ b/system/compile.bash @@ -18,7 +18,7 @@ for src in system/src/*.c; do out="${src/src/out}" out="${out/.c}" verbosely clang -Wall -Wextra -Werror -std=c11 "${src}" -o "${out}" - if [[ "${out}" == *-privileged && -n "${RIJU_PRIVILEGED:-}" ]]; then + if [[ "${out}" == *-privileged ]]; then sudo chown root:riju "${out}" sudo chmod a=,g=rx,u=rwxs "${out}" fi diff --git a/webpack.config.cjs b/webpack.config.cjs index 14701bf..9301d74 100644 --- a/webpack.config.cjs +++ b/webpack.config.cjs @@ -1,9 +1,10 @@ const path = require("path"); const MonacoWebpackPlugin = require("monaco-editor-webpack-plugin"); +const webpack = require("webpack"); function isProduction(argv) { - return !argv.development; + return argv.mode !== "development"; } module.exports = (_, argv) => ({ @@ -40,7 +41,13 @@ module.exports = (_, argv) => ({ performance: { hints: false, }, - plugins: [new MonacoWebpackPlugin()], + plugins: [ + new webpack.ProvidePlugin({ + Buffer: ["buffer", "Buffer"], + regeneratorRuntime: "regenerator-runtime/runtime", + }), + new MonacoWebpackPlugin(), + ], resolve: { alias: { vscode: require.resolve("monaco-languageclient/lib/vscode-compatibility"), diff --git a/yarn.lock b/yarn.lock index 3c18da9..b047f70 100644 --- a/yarn.lock +++ b/yarn.lock @@ -814,6 +814,11 @@ lodash "^4.17.19" to-fast-properties "^2.0.0" +"@discoveryjs/json-ext@^0.5.0": + version "0.5.1" + resolved "https://registry.yarnpkg.com/@discoveryjs/json-ext/-/json-ext-0.5.1.tgz#cf87081ac9b0f3eb3b5740415b50b7966bac8fc5" + integrity sha512-Oee4NT60Lxe90m7VTYBU4UbABNaz0N4Q3G62CPB+6mGE4KuLMsTACmH8q3PH5u9pSZCuOdE9JClJ9vBqsp6DQg== + "@types/eslint-scope@^3.7.0": version "3.7.0" resolved "https://registry.yarnpkg.com/@types/eslint-scope/-/eslint-scope-3.7.0.tgz#4792816e31119ebd506902a482caec4951fabd86" @@ -990,17 +995,17 @@ "@webassemblyjs/wast-parser" "1.9.1" "@xtuc/long" "4.2.2" -"@webpack-cli/info@^1.1.0": - version "1.1.0" - resolved "https://registry.yarnpkg.com/@webpack-cli/info/-/info-1.1.0.tgz#c596d5bc48418b39df00c5ed7341bf0f102dbff1" - integrity sha512-uNWSdaYHc+f3LdIZNwhdhkjjLDDl3jP2+XBqAq9H8DjrJUvlOKdP8TNruy1yEaDfgpAIgbSAN7pye4FEHg9tYQ== +"@webpack-cli/info@^1.2.0": + version "1.2.0" + resolved "https://registry.yarnpkg.com/@webpack-cli/info/-/info-1.2.0.tgz#6051d6adf3618df664f4945a2b76355c00f83f0d" + integrity sha512-+wA8lBKopgKmN76BSGJVJby5ZXDlsrO6p/nm7fUBsHznRNWB/ozotJP7Yfcz8JPfqeG2LxwYlTH2u6D9a/0XAw== dependencies: envinfo "^7.7.3" -"@webpack-cli/serve@^1.1.0": - version "1.1.0" - resolved "https://registry.yarnpkg.com/@webpack-cli/serve/-/serve-1.1.0.tgz#13ad38f89b6e53d1133bac0006a128217a6ebf92" - integrity sha512-7RfnMXCpJ/NThrhq4gYQYILB18xWyoQcBey81oIyVbmgbc6m5ZHHyFK+DyH7pLHJf0p14MxL4mTsoPAgBSTpIg== +"@webpack-cli/serve@^1.2.0": + version "1.2.0" + resolved "https://registry.yarnpkg.com/@webpack-cli/serve/-/serve-1.2.0.tgz#8cb2c1e95426f5caed1f3bf9d7ccf3ea41d85f52" + integrity sha512-jI3P7jMp/AXDSPkM+ClwRcJZbxnlvNC8bVZBmyRr4scMMZ4p5WQcXkw3Q+Hc7RQekomJlBMN+UQGliT4hhG8Vw== "@xtuc/ieee754@^1.2.0": version "1.2.0" @@ -1052,11 +1057,6 @@ ansi-styles@^3.2.1: dependencies: color-convert "^1.9.0" -array-back@^4.0.1: - version "4.0.1" - resolved "https://registry.yarnpkg.com/array-back/-/array-back-4.0.1.tgz#9b80312935a52062e1a233a9c7abeb5481b30e90" - integrity sha512-Z/JnaVEXv+A9xabHzN43FiiiWEE7gPCRXMrVmRm00tWbjZRul1iHm7ECzlyNq1p4a4ATXz+G9FJ3GqGOkOV3fg== - array-flatten@1.1.1: version "1.1.1" resolved "https://registry.yarnpkg.com/array-flatten/-/array-flatten-1.1.1.tgz#9a5f699051b1e7073328f2a008968b64ea2955d2" @@ -1099,6 +1099,11 @@ balanced-match@^1.0.0: resolved "https://registry.yarnpkg.com/balanced-match/-/balanced-match-1.0.0.tgz#89b4d199ab2bee49de164ea02b89ce462d71b767" integrity sha1-ibTRmasr7kneFk6gK4nORi1xt2c= +base64-js@^1.3.1: + version "1.5.1" + resolved "https://registry.yarnpkg.com/base64-js/-/base64-js-1.5.1.tgz#1b1b440160a5bf7ad40b650f095963481903930a" + integrity sha512-AKpaYlHn8t4SVbOHCy+b5+KKgvR4vrsD8vbvrbiQJps7fKDTkjkDry6ji0rUJjC0kzbNePLwzxq8iypo41qeWA== + big.js@^5.2.2: version "5.2.2" resolved "https://registry.yarnpkg.com/big.js/-/big.js-5.2.2.tgz#65f0af382f578bcdc742bd9c281e9cb2d7768328" @@ -1144,6 +1149,14 @@ buffer-from@^1.0.0: resolved "https://registry.yarnpkg.com/buffer-from/-/buffer-from-1.1.1.tgz#32713bc028f75c02fdb710d7c7bcec1f2c6070ef" integrity sha512-MQcXEUbCKtEo7bhqEs6560Hyd4XaovZlO/k9V3hjVUF/zwW7KBVdSK4gIt/bzwS9MbR5qob+F5jusZsb0YQK2A== +buffer@^6.0.3: + version "6.0.3" + resolved "https://registry.yarnpkg.com/buffer/-/buffer-6.0.3.tgz#2ace578459cc8fbe2a70aaa8f52ee63b6a74c6c6" + integrity sha512-FTiCpNxtwiZZHEZbcbTIcZjERVICn9yq/pDFkTl95/AxzD1naBctN7YO68riM/gLSDY7sdrMby8hofADYuuqOA== + dependencies: + base64-js "^1.3.1" + ieee754 "^1.2.1" + bytes@3.1.0: version "3.1.0" resolved "https://registry.yarnpkg.com/bytes/-/bytes-3.1.0.tgz#f6cf7933a360e0588fa9fde85651cdc7f805d1f6" @@ -1200,16 +1213,6 @@ colorette@^1.2.1: resolved "https://registry.yarnpkg.com/colorette/-/colorette-1.2.1.tgz#4d0b921325c14faf92633086a536db6e89564b1b" integrity sha512-puCDz0CzydiSYOrnXpz/PKd69zRrribezjtE9yd4zvytoRc8+RY/KJPvtPFKZS3E3wP6neGyMe0vOTlHO5L3Pw== -command-line-usage@^6.1.0: - version "6.1.1" - resolved "https://registry.yarnpkg.com/command-line-usage/-/command-line-usage-6.1.1.tgz#c908e28686108917758a49f45efb4f02f76bc03f" - integrity sha512-F59pEuAR9o1SF/bD0dQBDluhpT4jJQNWUHEuVBqpDmCUo6gPjCi+m9fCWnWZVR/oG6cMTUms4h+3NPl74wGXvA== - dependencies: - array-back "^4.0.1" - chalk "^2.4.2" - table-layout "^1.0.1" - typical "^5.2.0" - commander@^2.20.0: version "2.20.3" resolved "https://registry.yarnpkg.com/commander/-/commander-2.20.3.tgz#fd485e84c03eb4881c20722ba48035e8531aeb33" @@ -1313,11 +1316,6 @@ debug@^4.1.0: dependencies: ms "2.1.2" -deep-extend@~0.6.0: - version "0.6.0" - resolved "https://registry.yarnpkg.com/deep-extend/-/deep-extend-0.6.0.tgz#c4fa7c95404a17a9c3e8ca7e1537312b736330ac" - integrity sha512-LOHxIOaPYdHlJRtCQfDIVZtfw/ufM8+rVj649RIHzcm/vGwQRXFt6OPqIFWsm2XEMrNIEtWR64sY1LEKD2vAOA== - define-properties@^1.1.3: version "1.1.3" resolved "https://registry.yarnpkg.com/define-properties/-/define-properties-1.1.3.tgz#cf88da6cbee26fe6db7094f61d870cbd84cee9f1" @@ -1517,6 +1515,11 @@ 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== +fastest-levenshtein@^1.0.12: + version "1.0.12" + resolved "https://registry.yarnpkg.com/fastest-levenshtein/-/fastest-levenshtein-1.0.12.tgz#9990f7d3a88cc5a9ffd1f1745745251700d497e2" + integrity sha512-On2N+BpYJ15xIC974QNVuYGMOlEVt4s0EOI3wwMqOmK1fdDY+FN/zltPV8vosq4ad4c/gJ1KHScUn/6AWIgiow== + file-loader@^6.2.0: version "6.2.0" resolved "https://registry.yarnpkg.com/file-loader/-/file-loader-6.2.0.tgz#baef7cf8e1840df325e4390b4484879480eebe4d" @@ -1687,6 +1690,11 @@ icss-utils@^5.0.0: resolved "https://registry.yarnpkg.com/icss-utils/-/icss-utils-5.1.0.tgz#c6be6858abd013d768e98366ae47e25d5887b1ae" integrity sha512-soFhflCVWLfRNOPU3iv5Z9VUdT44xFRbzjLsEzSr5AQmgqPMTHdU3PMT1Cf1ssx8fLNJDA1juftYl+PUcv3MqA== +ieee754@^1.2.1: + version "1.2.1" + resolved "https://registry.yarnpkg.com/ieee754/-/ieee754-1.2.1.tgz#8eb7a10a63fff25d15a57b001586d177d1b0d352" + integrity sha512-dcyqhDvX1C46lXZcVqCpK+FtMRQVdIMN6/Df5js2zouUsqG7I6sFxitIC+7KYK29KdXOLHdu9zL4sFnoVQnqaA== + import-local@^3.0.2: version "3.0.2" resolved "https://registry.yarnpkg.com/import-local/-/import-local-3.0.2.tgz#a8cfd0431d1de4a2199703d003e3e62364fa6db6" @@ -1795,11 +1803,6 @@ json5@^2.1.2: dependencies: minimist "^1.2.5" -leven@^3.1.0: - version "3.1.0" - resolved "https://registry.yarnpkg.com/leven/-/leven-3.1.0.tgz#77891de834064cccba82ae7842bb6b14a13ed7f2" - integrity sha512-qsda+H8jTaUaN/x5vzW2rzc+8Rw4TAQ/4KjB46IwK5VH+IlVeeeje/EoZRpiXvIqjFgK84QffqPztGI3VBLG1A== - loader-runner@^4.1.0: version "4.1.0" resolved "https://registry.yarnpkg.com/loader-runner/-/loader-runner-4.1.0.tgz#f70bc0c29edbabdf2043e7ee73ccc3fe1c96b42d" @@ -2226,11 +2229,6 @@ rechoir@^0.7.0: dependencies: resolve "^1.9.0" -reduce-flatten@^2.0.0: - version "2.0.0" - resolved "https://registry.yarnpkg.com/reduce-flatten/-/reduce-flatten-2.0.0.tgz#734fd84e65f375d7ca4465c69798c25c9d10ae27" - integrity sha512-EJ4UNY/U1t2P/2k6oqotuX2Cc3T6nxJwsM0N0asT7dhrtH1ltUxDn4NalSYmPE2rCkVpcf/X6R0wDwcFpzhd4w== - regenerate-unicode-properties@^8.2.0: version "8.2.0" resolved "https://registry.yarnpkg.com/regenerate-unicode-properties/-/regenerate-unicode-properties-8.2.0.tgz#e5de7111d655e7ba60c057dbe9ff37c87e65cdec" @@ -2243,7 +2241,7 @@ regenerate@^1.4.0: resolved "https://registry.yarnpkg.com/regenerate/-/regenerate-1.4.2.tgz#b9346d8827e8f5a32f7ba29637d398b69014848a" integrity sha512-zrceR/XhGYU/d/opr2EKO7aRHUeiBI8qjtfHqADTwZd6Szfy16la6kqD0MIUs5z5hx6AaKa+PixpPrR289+I0A== -regenerator-runtime@^0.13.4: +regenerator-runtime@^0.13.4, regenerator-runtime@^0.13.7: version "0.13.7" resolved "https://registry.yarnpkg.com/regenerator-runtime/-/regenerator-runtime-0.13.7.tgz#cac2dacc8a1ea675feaabaeb8ae833898ae46f55" integrity sha512-a54FxoJDIr27pgf7IgeQGxmqUNYrcV338lf/6gH456HZ/PhX+5BcwHXG9ajESmwe6WRO0tAzRUrRmNONWgkrew== @@ -2477,16 +2475,6 @@ supports-color@^7.0.0: dependencies: has-flag "^4.0.0" -table-layout@^1.0.1: - version "1.0.1" - resolved "https://registry.yarnpkg.com/table-layout/-/table-layout-1.0.1.tgz#8411181ee951278ad0638aea2f779a9ce42894f9" - integrity sha512-dEquqYNJiGwY7iPfZ3wbXDI944iqanTSchrACLL2nOB+1r+h1Nzu2eH+DuPPvWvm5Ry7iAPeFlgEtP5bIp5U7Q== - dependencies: - array-back "^4.0.1" - deep-extend "~0.6.0" - typical "^5.2.0" - wordwrapjs "^4.0.0" - tapable@^2.1.1, tapable@^2.2.0: version "2.2.0" resolved "https://registry.yarnpkg.com/tapable/-/tapable-2.2.0.tgz#5c373d281d9c672848213d0e037d1c4165ab426b" @@ -2536,11 +2524,6 @@ type-is@~1.6.17, type-is@~1.6.18: media-typer "0.3.0" mime-types "~2.1.24" -typical@^5.0.0, typical@^5.2.0: - version "5.2.0" - resolved "https://registry.yarnpkg.com/typical/-/typical-5.2.0.tgz#4daaac4f2b5315460804f0acf6cb69c52bb93066" - integrity sha512-dvdQgNDNJo+8B2uBQoqdb11eUCE1JQXhvjC/CZtgvZseVd5TYMXnq0+vuUemXbd/Se29cTaUuPX3YIc2xgbvIg== - unicode-canonical-property-names-ecmascript@^1.0.4: version "1.0.4" resolved "https://registry.yarnpkg.com/unicode-canonical-property-names-ecmascript/-/unicode-canonical-property-names-ecmascript-1.0.4.tgz#2619800c4c825800efdd8343af7dd9933cbe2818" @@ -2664,20 +2647,20 @@ watchpack@^2.0.0: graceful-fs "^4.1.2" webpack-cli@^4.2.0: - version "4.2.0" - resolved "https://registry.yarnpkg.com/webpack-cli/-/webpack-cli-4.2.0.tgz#10a09030ad2bd4d8b0f78322fba6ea43ec56aaaa" - integrity sha512-EIl3k88vaF4fSxWSgtAQR+VwicfLMTZ9amQtqS4o+TDPW9HGaEpbFBbAZ4A3ZOT5SOnMxNOzROsSTPiE8tBJPA== + version "4.3.0" + resolved "https://registry.yarnpkg.com/webpack-cli/-/webpack-cli-4.3.0.tgz#e39303bf9f8002de122903e97029f3443d0f9174" + integrity sha512-gve+BBKrzMPTOYDjupzV8JchUznhVWMKtWM1hFIQWi6XoeLvGNoQwkrtMWVb+aJ437GgCKdta7sIn10v621pKA== dependencies: - "@webpack-cli/info" "^1.1.0" - "@webpack-cli/serve" "^1.1.0" + "@discoveryjs/json-ext" "^0.5.0" + "@webpack-cli/info" "^1.2.0" + "@webpack-cli/serve" "^1.2.0" colorette "^1.2.1" - command-line-usage "^6.1.0" commander "^6.2.0" enquirer "^2.3.6" execa "^4.1.0" + fastest-levenshtein "^1.0.12" import-local "^3.0.2" interpret "^2.2.0" - leven "^3.1.0" rechoir "^0.7.0" v8-compile-cache "^2.2.0" webpack-merge "^4.2.2" @@ -2734,14 +2717,6 @@ which@^2.0.1: dependencies: isexe "^2.0.0" -wordwrapjs@^4.0.0: - version "4.0.0" - resolved "https://registry.yarnpkg.com/wordwrapjs/-/wordwrapjs-4.0.0.tgz#9aa9394155993476e831ba8e59fb5795ebde6800" - integrity sha512-Svqw723a3R34KvsMgpjFBYCgNOSdcW3mQFK4wIfhGQhtaFVOJmdYoXgi63ne3dTlWgatVcUc7t4HtQ/+bUVIzQ== - dependencies: - reduce-flatten "^2.0.0" - typical "^5.0.0" - wrappy@1: version "1.0.2" resolved "https://registry.yarnpkg.com/wrappy/-/wrappy-1.0.2.tgz#b5243d8f3ec1aa35f1364605bc0d1036e30ab69f"