feat(syntax_highlight): add syntax highlighting based on file path/content/mimetype
+ 390
- 29
@@ -1,5 +1,5 @@
 {
-  "_generatedAtUnix": 1663697683881,
+  "_generatedAtUnix": 1663707497509,
   "_hashAlgorithm": "sha1",
   "_version": 2,
   "islands": {

...
@@ -50,7 +50,7 @@
       "pathSource": "./app/views/auth/RegisterView.tsx"
     },
     "RepositoryBrowserView": {
-      "hash": "8ceec940b4618a5ba23c0fe40d31c042f334da35",
+      "hash": "7ab4730aead5bb85352c1c9a896f3336bf3ae743",
       "pathSource": "./app/views/repository/RepositoryBrowserView.tsx"
     },
     "RepositoryCreateView": {

app/controllers/repository/getRepositoryBrowserView.ts
@@ -56,19 +56,23 @@ const getRepositoryBrowserView: ReqHandler = async (request, reply) => {
     });
   }
 
-  const fileContent = await repoService.getRepositoryFileContent(
-    repo,
-    path,
-    ref
-  );
+  let fileContent = await repoService.getRepositoryFileContent(repo, path, ref);
 
   if (fileContent == null) {
     return reply.status(404).callNotFound();
   }
 
+  const linguistInfos = await request.codeAnalysisService.getLinguistFileInfos(
+    path,
+    fileContent.content
+  );
+
+  fileContent.mimeType = linguistInfos.mimeType;
+
   return reqHandler<RepositoryBrowserViewProps>(RepositoryBrowserView.name, {
     currentUser,
     fileContent,
+    linguistInfos,
     parentOrg,
     path,
     ref,

new file
app/plugins/codeAnalysis.ts
@@ -0,0 +1,23 @@
+// 3rd-party
+import { FastifyPluginAsync } from "fastify";
+import fp from "fastify-plugin";
+import languagesMap from "language-map";
+import languageDetect from "language-detect";
+// app
+import { makeCodeAnalysisService } from "../services/codeAnalysis";
+
+const serviceKey = "codeAnalysisService";
+export const codeAnalysisPlugin: FastifyPluginAsync<never> = fp(
+  async (server) => {
+    const service = makeCodeAnalysisService({
+      languageDetect,
+      languagesMap,
+    });
+    server.decorate(serviceKey, {
+      getter: () => service,
+    });
+    server.decorateRequest(serviceKey, {
+      getter: () => service,
+    });
+  }
+);

@@ -1,2 +1,3 @@
+export { codeAnalysisPlugin } from "./codeAnalysis";
 export { cryptoPlugin } from "./crypto";
 export { prismaPlugin } from "./prisma";

@@ -22,7 +22,7 @@ import { version as appVersion } from "../package.json";
 // app
 import { Const } from "./const";
 import { Env } from "./env";
-import { cryptoPlugin, prismaPlugin } from "./plugins";
+import { codeAnalysisPlugin, cryptoPlugin, prismaPlugin } from "./plugins";
 import { makeGitServerService } from "./services/gitServer";
 import {
   getEnv,

...
@@ -90,6 +90,7 @@ async function main(): Promise<AppServer> {
       },
     ],
     baseScriptTags: [
+      // add ES Module shim as long as browsers (firefox) not all supports it
       {
         async: true,
         id: "es-importmap-shim",

...
@@ -105,9 +106,16 @@ async function main(): Promise<AppServer> {
       },*/
     ],
     setupServerBeforeRoutes(s) {
+      // add a preHandler to warn against bad localhost usage (so cookies works)
       s.addHook("preHandler", localAppDomainPreHandler);
+
+      // add a reply decorator so we can reply with common props from app
       s.decorateReply("makeRequestHandler", makeRequestHandler);
 
+      // register the code analysis plugin
+      s.register(codeAnalysisPlugin);
+
+      // register the crypto plugin
       s.register(cryptoPlugin).after(() => {
         const gitService = makeGitServerService({
           cryptoService: s.cryptoService,

...
@@ -116,6 +124,8 @@ async function main(): Promise<AppServer> {
           } as any,
         });
 
+        // register the Git Server plugin and bind the resolvers/callbacks
+        // to the gitService instance made above
         s.register(fastifyGitServer, {
           withSideBandMessages: true,
           authorizationResolver: gitService.authorizationResolver,

...
@@ -124,15 +134,19 @@ async function main(): Promise<AppServer> {
         });
       });
 
+      // register the Prisma client plugin
       s.register(prismaPlugin, { prisma });
 
+      // register the plugin to be able to decode Form's body (multipart)
       s.register(fastifyFormBody);
 
+      // register the plugin to serve static assets from public folder
       s.register(fastifyServeStatic, {
         root: Paths.PUBLIC_FOLDER,
         prefix: publicBaseUrl,
       });
 
+      // register the cookies plugin
       s.register(fastifyCookie, {
         secret: Env.COOKIE_SECRET,
         parseOptions: cookiesOpts,

...
@@ -160,10 +174,12 @@ async function main(): Promise<AppServer> {
         },
       });
 
+      // serve the import-map service worker interceptor
       s.get("/interceptor-imsw.js", {}, async (_, reply) => {
         return reply.sendFile("interceptor-imsw.js");
       });
 
+      // serve the import-map service worker register
       s.get("/register-imsw.js", {}, async (_, reply) => {
         return reply.sendFile("register-imsw.js");
       });

new file
app/services/codeAnalysis/getLinguistFileInfos.ts
@@ -0,0 +1,41 @@
+// 1st-party
+import type { ServiceMethodFactory } from "@ethicdevs/react-monolith";
+// service
+import type { CodeAnalysisServiceDeps, LinguistFileInfos } from "./types";
+
+const makeGetLinguistFileInfos: ServiceMethodFactory<
+  CodeAnalysisServiceDeps,
+  [string, string | undefined],
+  Promise<LinguistFileInfos>
+> = (deps) => {
+  return async (path, content = undefined) => {
+    const language =
+      content != null
+        ? deps.languageDetect.contents(path, content)
+        : deps.languageDetect.filename(path);
+
+    if (language == null || language in deps.languagesMap === false) {
+      throw new Error(`Invalid language: null.`);
+    }
+
+    const languageInfos = deps.languagesMap[language];
+    if (languageInfos == null) {
+      throw new Error(`Invalid language: not in languages map.`);
+    }
+
+    const languageResults: LinguistFileInfos = {
+      color: languageInfos.color || "gray",
+      extensions: languageInfos.extensions || [],
+      languageDisplayName: language,
+      language: languageInfos.aceMode,
+      languageId: languageInfos.languageId,
+      mimeType:
+        languageInfos.codemirrorMimeType || `text/${languageInfos.aceMode}`,
+      type: languageInfos.type,
+    };
+
+    return Promise.resolve(languageResults);
+  };
+};
+
+export default makeGetLinguistFileInfos;

new file
app/services/codeAnalysis/index.ts
@@ -0,0 +1,13 @@
+// 1st-party
+import { makeService } from "@ethicdevs/react-monolith";
+// service
+import type { CodeAnalysisServiceAPI, CodeAnalysisServiceDeps } from "./types";
+// service methods
+import { default as makeGetLinguistFileInfos } from "./getLinguistFileInfos";
+
+export const makeCodeAnalysisService = makeService<
+  CodeAnalysisServiceAPI,
+  CodeAnalysisServiceDeps
+>({
+  getLinguistFileInfos: makeGetLinguistFileInfos,
+});

new file
app/services/codeAnalysis/types.ts
@@ -0,0 +1,46 @@
+// 1st-party
+import type { ServiceApiContract } from "@ethicdevs/react-monolith";
+// app
+import type { LanguageDetectFn } from "../../types";
+
+export interface LinguistFileInfos {
+  color: string;
+  extensions: string[];
+  filenames?: string[];
+  group?: string;
+  language: string;
+  languageDisplayName: string;
+  languageId: number;
+  mimeType: string;
+  type: string;
+}
+
+export interface CodeAnalysisServiceAPI extends ServiceApiContract {
+  getLinguistFileInfos(
+    path: string,
+    content?: string
+  ): Promise<LinguistFileInfos>;
+}
+
+export interface CodeAnalysisServiceDeps {
+  languageDetect: LanguageDetectFn;
+
+  // types for NPM package's: language-map
+  languagesMap: {
+    [languageName: string]: {
+      aceMode: string;
+      aliases?: string[];
+      color?: string;
+      codemirrorMode?: string;
+      codemirrorMimeType?: string;
+      extensions?: string[];
+      filenames?: string[];
+      group?: string;
+      interpreters?: string[];
+      languageId: number;
+      tmScope: string;
+      type: string;
+      wrap?: boolean;
+    };
+  };
+}

@@ -26,6 +26,15 @@ export interface CommonViewProps {
 
 export type CommonProps = { commonProps: CommonViewProps };
 
+export interface LanguageDetectFn {
+  (path: string, callback: (err: Error, language: string | null) => void): void;
+  sync: (path: string | null) => string | null;
+  contents: (path: string, content: string) => string | null;
+  filename: (path: string) => string | null;
+  shebang: (content: string) => string | null;
+  classify: (content: string) => string | null;
+}
+
 export interface RepositoryHead {
   treeId: string;
   parentId: null | string;

app/views/repository/RepositoryBrowserView.tsx
@@ -4,6 +4,8 @@ import type { ReactView } from "@ethicdevs/react-monolith";
 import React from "react";
 // generated via script[prisma:generate]
 import type { Organization, Repository, User } from "@prisma/client";
+// app service
+import type { LinguistFileInfos } from "../../services/codeAnalysis/types";
 // app
 import type { CommonProps, RepositoryFileContent } from "../../types";
 import { Code, Layout, PageWrapper, getThemedCodeCss } from "../../components";

...
@@ -11,6 +13,7 @@ import { Code, Layout, PageWrapper, getThemedCodeCss } from "../../components";
 export interface RepositoryBrowserViewProps extends CommonProps {
   currentUser: null | User;
   fileContent: RepositoryFileContent;
+  linguistInfos: LinguistFileInfos;
   parentOrg: Organization;
   path: string;
   ref: string;

...
@@ -20,6 +23,7 @@ export interface RepositoryBrowserViewProps extends CommonProps {
 const RepositoryBrowserView: ReactView<RepositoryBrowserViewProps> = ({
   commonProps,
   fileContent,
+  linguistInfos,
   parentOrg,
   path,
   ref,

...
@@ -38,10 +42,11 @@ const RepositoryBrowserView: ReactView<RepositoryBrowserViewProps> = ({
           {path}
         </h1>
         <div style={{ width: "100%" }}>
+          <span>lang: {linguistInfos.language}</span>
           {getThemedCodeCss(commonProps.themeScheme)}
           <Code
             code={fileContent.content}
-            language={"ts"}
+            language={linguistInfos.language}
             themeScheme={commonProps.themeScheme}
           />
         </div>

@@ -7,6 +7,7 @@
     "schema": "db/schema.prisma"
   },
   "scripts": {
+    "postinstall": "patch-package",
     "clean": "rm -rf dist/",
     "env:local": "export $(echo $(cat .env.local | sed 's/#.*//g'| xargs) | envsubst)",
     "env:prod": "export $(echo $(cat .env.production | sed 's/#.*//g'| xargs) | envsubst)",

...
@@ -34,16 +35,20 @@
     "@fastify/cookie": "6.0.0",
     "@fastify/formbody": "6.0.0",
     "@prisma/client": "3.15.2",
-    "cuid": "^2.1.8",
     "cross-fetch": "^3.1.5",
+    "cuid": "^2.1.8",
     "dotenv-flow": "^3.2.0",
     "fastify": "^3.27.4",
     "fastify-static": "^4.6.1",
     "git-http-backend": "^1.1.2",
     "gray-matter": "^4.0.3",
+    "language-detect": "^1.1.0",
+    "language-map": "^1.5.0",
     "markdown-to-jsx": "^7.1.7",
     "markdown-toc": "^1.2.0",
+    "patch-package": "^6.4.7",
     "pg": "^8.7.3",
+    "postinstall-postinstall": "^2.1.0",
     "prismjs": "^1.29.0",
     "react": "^17.0.2",
     "react-dom": "^17.0.2",

new file
patches/language-detect+1.1.0.patch
@@ -0,0 +1,24 @@
+diff --git a/node_modules/language-detect/vendor/extensions.json b/node_modules/language-detect/vendor/extensions.json
+index 903ba4e..283f806 100644
+--- a/node_modules/language-detect/vendor/extensions.json
++++ b/node_modules/language-detect/vendor/extensions.json
+@@ -696,7 +696,8 @@
+   ".tu": "Turing",
+   ".ttl": "Turtle",
+   ".twig": "Twig",
+-  ".ts": "XML",
++  ".ts": "TypeScript",
++  ".tsx": "TypeScript",
+   ".upc": "Unified Parallel C",
+   ".anim": "Unity3D Asset",
+   ".asset": "Unity3D Asset",
+diff --git a/node_modules/language-detect/vendor/filenames.json b/node_modules/language-detect/vendor/filenames.json
+index 18b7307..66fa064 100644
+--- a/node_modules/language-detect/vendor/filenames.json
++++ b/node_modules/language-detect/vendor/filenames.json
+@@ -1,4 +1,5 @@
+ {
++  ".gitignore": "Shell",
+   "ant.xml": "Ant Build System",
+   "build.xml": "Ant Build System",
+   "CMakeLists.txt": "CMake",

types/global/index.d.ts
@@ -6,38 +6,44 @@ import fastify from "fastify";
 import { PrismaClient } from "@prisma/client";
 // app
 import type { AppSessionData, AppThemeScheme } from "../../app/types";
+import type { CodeAnalysisServiceAPI } from "../../app/services/codeAnalysis/types";
 import type { CryptoServiceAPI } from "../../app/services/crypto/types";
 
 declare module "@ethicdevs/fastify-custom-session" {
+  // from app types
   declare interface CustomSession extends AppSessionData {}
 }
 
 declare module "fastify" {
   interface FastifyInstance {
+    // from codeAnalysis plugin
+    codeAnalysisService: CodeAnalysisServiceAPI;
     // from crypto plugin
     cryptoService: CryptoServiceAPI;
     // from prisma plugin
     prisma: PrismaClient;
   }
   interface FastifyRequest {
-    // from git server plugin
-    spawnGitCommand: GitServer.SpawnGitCommandDecorator;
-    // from crypto plugin
-    cryptoService: CryptoServiceAPI;
-    // from prisma plugin
-    prisma: PrismaClient;
-    // from cookies
+    // from cookies plugin
     cookies: {
       theme_scheme: AppThemeScheme;
     };
-    // from react-monolith
-    // A request utility that maps a viewName to its routerPath
+    // from codeAnalysis plugin
+    codeAnalysisService: CodeAnalysisServiceAPI;
+    // from crypto plugin
+    cryptoService: CryptoServiceAPI;
+    // from react-monolith: request utility that maps a viewName to its routerPath
     namedViewsPathMap: Record<string, string>;
-    // A request utility that maps a routerPath to its viewName
+    // from react-monolith: request utility that maps a routerPath to its viewName
     pathNamedViewsMap: Record<string, string>;
+    // from prisma plugin
+    prisma: PrismaClient;
+    // from git server plugin
+    spawnGitCommand: GitServer.SpawnGitCommandDecorator;
   }
 
   interface FastifyReply {
+    // from makeRequestHandler factory
     makeRequestHandler: (
       request: FastifyRequest,
       reply: FastifyReply

new file
types/language-detect.d.ts
@@ -0,0 +1 @@
+declare module "language-detect";

@@ -800,6 +800,34 @@
   version "1.1.0"
   resolved "https://registry.yarnpkg.com/@protobufjs/utf8/-/utf8-1.1.0.tgz#a777360b5b39a1a2e5106f8e858f2fd2d060c570"
 
+"@redis/bloom@1.0.2":
+  version "1.0.2"
+  resolved "https://registry.yarnpkg.com/@redis/bloom/-/bloom-1.0.2.tgz#42b82ec399a92db05e29fffcdfd9235a5fc15cdf"
+
+"@redis/client@1.3.0":
+  version "1.3.0"
+  resolved "https://registry.yarnpkg.com/@redis/client/-/client-1.3.0.tgz#c62ccd707f16370a2dc2f9e158a28b7da049fa77"
+  dependencies:
+    cluster-key-slot "1.1.0"
+    generic-pool "3.8.2"
+    yallist "4.0.0"
+
+"@redis/graph@1.0.1":
+  version "1.0.1"
+  resolved "https://registry.yarnpkg.com/@redis/graph/-/graph-1.0.1.tgz#eabc58ba99cd70d0c907169c02b55497e4ec8a99"
+
+"@redis/json@1.0.4":
+  version "1.0.4"
+  resolved "https://registry.yarnpkg.com/@redis/json/-/json-1.0.4.tgz#f372b5f93324e6ffb7f16aadcbcb4e5c3d39bda1"
+
+"@redis/search@1.1.0":
+  version "1.1.0"
+  resolved "https://registry.yarnpkg.com/@redis/search/-/search-1.1.0.tgz#7abb18d431f27ceafe6bcb4dd83a3fa67e9ab4df"
+
+"@redis/time-series@1.0.3":
+  version "1.0.3"
+  resolved "https://registry.yarnpkg.com/@redis/time-series/-/time-series-1.0.3.tgz#4cfca8e564228c0bddcdf4418cba60c20b224ac4"
+
 "@sinclair/typebox@^0.24.1":
   version "0.24.41"
   resolved "https://registry.yarnpkg.com/@sinclair/typebox/-/typebox-0.24.41.tgz#45470b8bae32a28f1e0501066d0bacbd8b772804"

...
@@ -1074,6 +1102,10 @@
   dependencies:
     "@types/yargs-parser" "*"
 
+"@yarnpkg/lockfile@^1.1.0":
+  version "1.1.0"
+  resolved "https://registry.yarnpkg.com/@yarnpkg/lockfile/-/lockfile-1.1.0.tgz#e77a97fbd345b76d83245edcd17d393b1b41fb31"
+
 abab@^2.0.3, abab@^2.0.5:
   version "2.0.6"
   resolved "https://registry.yarnpkg.com/abab/-/abab-2.0.6.tgz#41b80f2c871d19686216b82309231cfd3cb3d291"

...
@@ -1382,7 +1414,7 @@ caniuse-lite@^1.0.30001359:
   version "1.0.30001361"
   resolved "https://registry.yarnpkg.com/caniuse-lite/-/caniuse-lite-1.0.30001361.tgz#ba2adb2527566fb96f3ac7c67698ae7fc495a28d"
 
-chalk@^2.0.0, chalk@^2.4.1:
+chalk@^2.0.0, chalk@^2.4.1, chalk@^2.4.2:
   version "2.4.2"
   resolved "https://registry.yarnpkg.com/chalk/-/chalk-2.4.2.tgz#cd42541677a54333cf541a49108c1432b44c9424"
   dependencies:

...
@@ -1415,6 +1447,10 @@ chokidar@^3.5.1:
   optionalDependencies:
     fsevents "~2.3.2"
 
+ci-info@^2.0.0:
+  version "2.0.0"
+  resolved "https://registry.yarnpkg.com/ci-info/-/ci-info-2.0.0.tgz#67a9e964be31a51e15e5010d58e6f12834002f46"
+
 ci-info@^3.2.0:
   version "3.3.2"
   resolved "https://registry.yarnpkg.com/ci-info/-/ci-info-3.3.2.tgz#6d2967ffa407466481c6c90b6e16b3098f080128"

...
@@ -1423,6 +1459,13 @@ cjs-module-lexer@^1.0.0:
   version "1.2.2"
   resolved "https://registry.yarnpkg.com/cjs-module-lexer/-/cjs-module-lexer-1.2.2.tgz#9f84ba3244a512f3a54e5277e8eef4c489864e40"
 
+classifier@~0.1.0:
+  version "0.1.0"
+  resolved "https://registry.yarnpkg.com/classifier/-/classifier-0.1.0.tgz#e2b185bba63bbf927cf7770eea1eb70aeb3f9596"
+  dependencies:
+    redis ">=0.7.0"
+    underscore ">=1.1.0"
+
 cliui@^7.0.2:
   version "7.0.4"
   resolved "https://registry.yarnpkg.com/cliui/-/cliui-7.0.4.tgz#a0265ee655476fc807aea9df3df8df7783808b4f"

...
@@ -1431,6 +1474,10 @@ cliui@^7.0.2:
     strip-ansi "^6.0.0"
     wrap-ansi "^7.0.0"
 
+cluster-key-slot@1.1.0:
+  version "1.1.0"
+  resolved "https://registry.yarnpkg.com/cluster-key-slot/-/cluster-key-slot-1.1.0.tgz#30474b2a981fb12172695833052bc0d01336d10d"
+
 co@^4.6.0:
   version "4.6.0"
   resolved "https://registry.yarnpkg.com/co/-/co-4.6.0.tgz#6ea6bdf3d853ae54ccb8e47bfa0bf3f9031fb184"

...
@@ -2148,6 +2195,12 @@ find-up@^4.0.0, find-up@^4.1.0:
     locate-path "^5.0.0"
     path-exists "^4.0.0"
 
+find-yarn-workspace-root@^2.0.0:
+  version "2.0.0"
+  resolved "https://registry.yarnpkg.com/find-yarn-workspace-root/-/find-yarn-workspace-root-2.0.0.tgz#f47fb8d239c900eb78179aa81b66673eac88f7bd"
+  dependencies:
+    micromatch "^4.0.2"
+
 firebase-admin@^11.0.0:
   version "11.0.1"
   resolved "https://registry.yarnpkg.com/firebase-admin/-/firebase-admin-11.0.1.tgz#68b11b55af70eb9b9a4c8b2cdfa86c892e9ba91b"

...
@@ -2188,6 +2241,14 @@ fresh@0.5.2:
   version "0.5.2"
   resolved "https://registry.yarnpkg.com/fresh/-/fresh-0.5.2.tgz#3d8cadd90d976569fa835ab1f8e4b23a105605a7"
 
+fs-extra@^7.0.1:
+  version "7.0.1"
+  resolved "https://registry.yarnpkg.com/fs-extra/-/fs-extra-7.0.1.tgz#4f189c44aa123b895f722804f55ea23eadc348e9"
+  dependencies:
+    graceful-fs "^4.1.2"
+    jsonfile "^4.0.0"
+    universalify "^0.1.0"
+
 fs.realpath@^1.0.0:
   version "1.0.0"
   resolved "https://registry.yarnpkg.com/fs.realpath/-/fs.realpath-1.0.0.tgz#1504ad2523158caa40db4a2787cb01411994ea4f"

...
@@ -2250,6 +2311,10 @@ gcp-metadata@^5.0.0:
     gaxios "^5.0.0"
     json-bigint "^1.0.0"
 
+generic-pool@3.8.2:
+  version "3.8.2"
+  resolved "https://registry.yarnpkg.com/generic-pool/-/generic-pool-3.8.2.tgz#aab4f280adb522fdfbdc5e5b64d718d3683f04e9"
+
 gensync@^1.0.0-beta.2:
   version "1.0.0-beta.2"
   resolved "https://registry.yarnpkg.com/gensync/-/gensync-1.0.0-beta.2.tgz#32a6ee76c3d7f52d46b2b1ae5d93fea8580a25e0"

...
@@ -2371,7 +2436,7 @@ google-p12-pem@^4.0.0:
   dependencies:
     node-forge "^1.3.1"
 
-graceful-fs@^4.1.2, graceful-fs@^4.2.9:
+graceful-fs@^4.1.11, graceful-fs@^4.1.2, graceful-fs@^4.1.6, graceful-fs@^4.2.9:
   version "4.2.10"
   resolved "https://registry.yarnpkg.com/graceful-fs/-/graceful-fs-4.2.10.tgz#147d3a006da4ca3ce14728c7aefc287c367d7a6c"
 

...
@@ -2584,6 +2649,12 @@ is-callable@^1.1.4, is-callable@^1.2.4:
   version "1.2.4"
   resolved "https://registry.yarnpkg.com/is-callable/-/is-callable-1.2.4.tgz#47301d58dd0259407865547853df6d61fe471945"
 
+is-ci@^2.0.0:
+  version "2.0.0"
+  resolved "https://registry.yarnpkg.com/is-ci/-/is-ci-2.0.0.tgz#6bc6334181810e04b5c22b3d589fdca55026404c"
+  dependencies:
+    ci-info "^2.0.0"
+
 is-core-module@^2.9.0:
   version "2.9.0"
   resolved "https://registry.yarnpkg.com/is-core-module/-/is-core-module-2.9.0.tgz#e1c34429cd51c6dd9e09e0799e396e27b19a9c69"

...
@@ -2596,6 +2667,10 @@ is-date-object@^1.0.1:
   dependencies:
     has-tostringtag "^1.0.0"
 
+is-docker@^2.0.0:
+  version "2.2.1"
+  resolved "https://registry.yarnpkg.com/is-docker/-/is-docker-2.2.1.tgz#33eeabe23cfe86f14bde4408a02c0cfb853acdaa"
+
 is-extendable@^0.1.0:
   version "0.1.1"
   resolved "https://registry.yarnpkg.com/is-extendable/-/is-extendable-0.1.1.tgz#62b110e289a471418e3ec36a617d472e301dfc89"

...
@@ -2701,6 +2776,12 @@ is-weakref@^1.0.2:
   dependencies:
     call-bind "^1.0.2"
 
+is-wsl@^2.1.1:
+  version "2.2.0"
+  resolved "https://registry.yarnpkg.com/is-wsl/-/is-wsl-2.2.0.tgz#74a4c76e77ca9fd3f932f290c17ea326cd157271"
+  dependencies:
+    is-docker "^2.0.0"
+
 isarray@1.0.0, isarray@~1.0.0:
   version "1.0.0"
   resolved "https://registry.yarnpkg.com/isarray/-/isarray-1.0.0.tgz#bb935d48582cba168c06834957a54a3e07124f11"

...
@@ -3258,6 +3339,12 @@ json5@^2.2.1:
   version "2.2.1"
   resolved "https://registry.yarnpkg.com/json5/-/json5-2.2.1.tgz#655d50ed1e6f95ad1a3caababd2b0efda10b395c"
 
+jsonfile@^4.0.0:
+  version "4.0.0"
+  resolved "https://registry.yarnpkg.com/jsonfile/-/jsonfile-4.0.0.tgz#8771aae0799b64076b76640fca058f9c10e33ecb"
+  optionalDependencies:
+    graceful-fs "^4.1.6"
+
 jsonwebtoken@^8.5.1:
   version "8.5.1"
   resolved "https://registry.yarnpkg.com/jsonwebtoken/-/jsonwebtoken-8.5.1.tgz#00e71e0b8df54c2121a1f26137df2280673bcc0d"

...
@@ -3324,10 +3411,32 @@ kind-of@^6.0.0, kind-of@^6.0.2:
   version "6.0.3"
   resolved "https://registry.yarnpkg.com/kind-of/-/kind-of-6.0.3.tgz#07c05034a6c349fa06e24fa35aa76db4580ce4dd"
 
+klaw-sync@^6.0.0:
+  version "6.0.0"
+  resolved "https://registry.yarnpkg.com/klaw-sync/-/klaw-sync-6.0.0.tgz#1fd2cfd56ebb6250181114f0a581167099c2b28c"
+  dependencies:
+    graceful-fs "^4.1.11"
+
 kleur@^3.0.3:
   version "3.0.3"
   resolved "https://registry.yarnpkg.com/kleur/-/kleur-3.0.3.tgz#a79c9ecc86ee1ce3fa6206d1216c501f147fc07e"
 
+language-classifier@0.0.1:
+  version "0.0.1"
+  resolved "https://registry.yarnpkg.com/language-classifier/-/language-classifier-0.0.1.tgz#b0c44cab331948e68a879cd63d5c1da68936774b"
+  dependencies:
+    classifier "~0.1.0"
+
+language-detect@^1.1.0:
+  version "1.1.0"
+  resolved "https://registry.yarnpkg.com/language-detect/-/language-detect-1.1.0.tgz#274bb8f58bec499cef2a8cfdb1028124eebb18e1"
+  dependencies:
+    language-classifier "0.0.1"
+
+language-map@^1.5.0:
+  version "1.5.0"
+  resolved "https://registry.yarnpkg.com/language-map/-/language-map-1.5.0.tgz#65c6d2c7493efa6708585386f0c2799d544fa367"
+
 lazy-cache@^2.0.2:
   version "2.0.2"
   resolved "https://registry.yarnpkg.com/lazy-cache/-/lazy-cache-2.0.2.tgz#b9190a4f913354694840859f8a8f7084d8822264"

...
@@ -3544,7 +3653,7 @@ merge-stream@^2.0.0:
   version "2.0.0"
   resolved "https://registry.yarnpkg.com/merge-stream/-/merge-stream-2.0.0.tgz#52823629a14dd00c9770fb6ad47dc6310f2c1f60"
 
-micromatch@^4.0.4:
+micromatch@^4.0.2, micromatch@^4.0.4:
   version "4.0.5"
   resolved "https://registry.yarnpkg.com/micromatch/-/micromatch-4.0.5.tgz#bc8999a7cbbf77cdc89f132f6e467051b49090c6"
   dependencies:

...
@@ -3724,6 +3833,13 @@ onetime@^5.1.2:
   dependencies:
     mimic-fn "^2.1.0"
 
+open@^7.4.2:
+  version "7.4.2"
+  resolved "https://registry.yarnpkg.com/open/-/open-7.4.2.tgz#b8147e26dcf3e426316c730089fd71edd29c2321"
+  dependencies:
+    is-docker "^2.0.0"
+    is-wsl "^2.1.1"
+
 optionator@^0.8.1:
   version "0.8.3"
   resolved "https://registry.yarnpkg.com/optionator/-/optionator-0.8.3.tgz#84fa1d036fe9d3c7e21d99884b601167ec8fb495"

...
@@ -3735,6 +3851,10 @@ optionator@^0.8.1:
     type-check "~0.3.2"
     word-wrap "~1.2.3"
 
+os-tmpdir@~1.0.2:
+  version "1.0.2"
+  resolved "https://registry.yarnpkg.com/os-tmpdir/-/os-tmpdir-1.0.2.tgz#bbe67406c79aa85c5cfec766fe5734555dfa1274"
+
 p-limit@^2.2.0:
   version "2.3.0"
   resolved "https://registry.yarnpkg.com/p-limit/-/p-limit-2.3.0.tgz#3dd33c647a214fdfffd835933eb086da0dc21db1"

...
@@ -3787,6 +3907,24 @@ parse5@6.0.1:
   version "6.0.1"
   resolved "https://registry.yarnpkg.com/parse5/-/parse5-6.0.1.tgz#e1a1c085c569b3dc08321184f19a39cc27f7c30b"
 
+patch-package@^6.4.7:
+  version "6.4.7"
+  resolved "https://registry.yarnpkg.com/patch-package/-/patch-package-6.4.7.tgz#2282d53c397909a0d9ef92dae3fdeb558382b148"
+  dependencies:
+    "@yarnpkg/lockfile" "^1.1.0"
+    chalk "^2.4.2"
+    cross-spawn "^6.0.5"
+    find-yarn-workspace-root "^2.0.0"
+    fs-extra "^7.0.1"
+    is-ci "^2.0.0"
+    klaw-sync "^6.0.0"
+    minimist "^1.2.0"
+    open "^7.4.2"
+    rimraf "^2.6.3"
+    semver "^5.6.0"
+    slash "^2.0.0"
+    tmp "^0.0.33"
+
 path-exists@^4.0.0:
   version "4.0.0"
   resolved "https://registry.yarnpkg.com/path-exists/-/path-exists-4.0.0.tgz#513bdbe2d3b95d7762e8c1137efa195c6c61b5b3"

...
@@ -3921,6 +4059,10 @@ postgres-interval@^1.1.0:
   dependencies:
     xtend "^4.0.0"
 
+postinstall-postinstall@^2.1.0:
+  version "2.1.0"
+  resolved "https://registry.yarnpkg.com/postinstall-postinstall/-/postinstall-postinstall-2.1.0.tgz#4f7f77441ef539d1512c40bd04c71b06a4704ca3"
+
 prelude-ls@~1.1.2:
   version "1.1.2"
   resolved "https://registry.yarnpkg.com/prelude-ls/-/prelude-ls-1.1.2.tgz#21932a549f5e52ffd9a827f570e04be62a97da54"

...
@@ -4112,6 +4254,17 @@ readdirp@~3.6.0:
   dependencies:
     picomatch "^2.2.1"
 
+redis@>=0.7.0:
+  version "4.3.1"
+  resolved "https://registry.yarnpkg.com/redis/-/redis-4.3.1.tgz#290532a0c22221e05e991162ac4dca1e1b2ff6da"
+  dependencies:
+    "@redis/bloom" "1.0.2"
+    "@redis/client" "1.3.0"
+    "@redis/graph" "1.0.1"
+    "@redis/json" "1.0.4"
+    "@redis/search" "1.1.0"
+    "@redis/time-series" "1.0.3"
+
 regexp.prototype.flags@^1.4.3:
   version "1.4.3"
   resolved "https://registry.yarnpkg.com/regexp.prototype.flags/-/regexp.prototype.flags-1.4.3.tgz#87cab30f80f66660181a3bb7bf5981a872b367ac"

...
@@ -4195,7 +4348,7 @@ rfdc@^1.1.4, rfdc@^1.2.0:
   version "1.3.0"
   resolved "https://registry.yarnpkg.com/rfdc/-/rfdc-1.3.0.tgz#d0b7c441ab2720d05dc4cf26e01c89631d9da08b"
 
-rimraf@^2.6.1:
+rimraf@^2.6.1, rimraf@^2.6.3:
   version "2.7.1"
   resolved "https://registry.yarnpkg.com/rimraf/-/rimraf-2.7.1.tgz#35797f13a7fdadc566142c29d4f07ccad483e3ec"
   dependencies:

...
@@ -4343,6 +4496,10 @@ sisteransi@^1.0.5:
   version "1.0.5"
   resolved "https://registry.yarnpkg.com/sisteransi/-/sisteransi-1.0.5.tgz#134d681297756437cc05ca01370d3a7a571075ed"
 
+slash@^2.0.0:
+  version "2.0.0"
+  resolved "https://registry.yarnpkg.com/slash/-/slash-2.0.0.tgz#de552851a1759df3a8f206535442f5ec4ddeab44"
+
 slash@^3.0.0:
   version "3.0.0"
   resolved "https://registry.yarnpkg.com/slash/-/slash-3.0.0.tgz#6539be870c165adbd5240220dbe361f1bc4d4634"

...
@@ -4613,6 +4770,12 @@ tiny-lru@^8.0.1:
   version "8.0.2"
   resolved "https://registry.yarnpkg.com/tiny-lru/-/tiny-lru-8.0.2.tgz#812fccbe6e622ded552e3ff8a4c3b5ff34a85e4c"
 
+tmp@^0.0.33:
+  version "0.0.33"
+  resolved "https://registry.yarnpkg.com/tmp/-/tmp-0.0.33.tgz#6d34335889768d21b2bcda0aa277ced3b1bfadf9"
+  dependencies:
+    os-tmpdir "~1.0.2"
+
 tmpl@1.0.5:
   version "1.0.5"
   resolved "https://registry.yarnpkg.com/tmpl/-/tmpl-1.0.5.tgz#8683e0b902bb9c20c4f726e3c0b69f36518c07cc"

...
@@ -4757,7 +4920,11 @@ unbox-primitive@^1.0.2:
     has-symbols "^1.0.3"
     which-boxed-primitive "^1.0.2"
 
-universalify@^0.1.2:
+underscore@>=1.1.0:
+  version "1.13.4"
+  resolved "https://registry.yarnpkg.com/underscore/-/underscore-1.13.4.tgz#7886b46bbdf07f768e0052f1828e1dcab40c0dee"
+
+universalify@^0.1.0, universalify@^0.1.2:
   version "0.1.2"
   resolved "https://registry.yarnpkg.com/universalify/-/universalify-0.1.2.tgz#b646f69be3942dabcecc9d6639c80dc105efaa66"
 

...
@@ -4942,14 +5109,14 @@ y18n@^5.0.5:
   version "5.0.8"
   resolved "https://registry.yarnpkg.com/y18n/-/y18n-5.0.8.tgz#7f4934d0f7ca8c56f95314939ddcd2dd91ce1d55"
 
+yallist@4.0.0, yallist@^4.0.0:
+  version "4.0.0"
+  resolved "https://registry.yarnpkg.com/yallist/-/yallist-4.0.0.tgz#9bb92790d9c0effec63be73519e11a35019a3a72"
+
 yallist@^2.0.0:
   version "2.1.2"
   resolved "https://registry.yarnpkg.com/yallist/-/yallist-2.1.2.tgz#1c11f9218f076089a47dd512f93c6699a6a81d52"
 
-yallist@^4.0.0:
-  version "4.0.0"
-  resolved "https://registry.yarnpkg.com/yallist/-/yallist-4.0.0.tgz#9bb92790d9c0effec63be73519e11a35019a3a72"
-
 yargs-parser@^20.2.2:
   version "20.2.9"
   resolved "https://registry.yarnpkg.com/yargs-parser/-/yargs-parser-20.2.9.tgz#2eb7dc3b0289718fc295f362753845c41a0c94ee"