diff --git a/.devcontainer/Dockerfile b/.devcontainer/Dockerfile
index e49d9314d..43eb59fe9 100644
--- a/.devcontainer/Dockerfile
+++ b/.devcontainer/Dockerfile
@@ -2,9 +2,8 @@
# [Choice] Node.js version: 14, 12, 10
ARG VARIANT="14-buster"
-FROM mcr.microsoft.com/vscode/devcontainers/typescript-node:0-${VARIANT}
+FROM mcr.microsoft.com/devcontainers/typescript-node:${VARIANT}
-# same package list from github1s/scripts/pre-install.sh
RUN apt-get update && export DEBIAN_FRONTEND=noninteractive \
&& apt-get -y install --no-install-recommends libx11-dev libxkbfile-dev libsecret-1-dev rsync
@@ -13,7 +12,7 @@ RUN mkdir -p /opt/dev \
&& cd /opt/dev \
&& git clone https://github.com/emscripten-core/emsdk.git \
&& cd /opt/dev/emsdk \
- && ./emsdk install 2.0.6 \
- && ./emsdk activate 2.0.6
+ && ./emsdk install 3.1.21 \
+ && ./emsdk activate 3.1.21
-ENV PATH="/opt/dev/emsdk:/opt/dev/emsdk/node/12.9.1_64bit/bin:/opt/dev/emsdk/upstream/emscripten:${PATH}"
+ENV PATH="/opt/dev/emsdk:/opt/dev/emsdk/node/14.18.2_64bit/bin:/opt/dev/emsdk/upstream/emscripten:${PATH}"
diff --git a/.devcontainer/devcontainer.json b/.devcontainer/devcontainer.json
index d0cddbe37..eef260001 100644
--- a/.devcontainer/devcontainer.json
+++ b/.devcontainer/devcontainer.json
@@ -6,7 +6,7 @@
"dockerfile": "Dockerfile",
// Update 'VARIANT' to pick a Node version: 10, 12, 14
"args": {
- "VARIANT": "12"
+ "VARIANT": "20-bullseye"
}
},
@@ -21,11 +21,11 @@
],
// Use 'forwardPorts' to make a list of ports inside the container available locally.
- "forwardPorts": [5000],
+ "forwardPorts": [8080],
// Use 'postCreateCommand' to run commands after the container is created.
- //"postCreateCommand": "yarn",
+ //"postCreateCommand": "npm install",
// Comment out connect as root instead. More info: https://aka.ms/vscode-remote/containers/non-root.
- "remoteUser": "node",
+ "remoteUser": "node"
}
\ No newline at end of file
diff --git a/.eslintignore b/.eslintignore
deleted file mode 100644
index 431d8b13d..000000000
--- a/.eslintignore
+++ /dev/null
@@ -1,4 +0,0 @@
-**/node_modules/**
-**/lib/vscode/**
-**/dist/**
-**/src/vs/**
diff --git a/.eslintrc.json b/.eslintrc.json
deleted file mode 100644
index e347ebdd3..000000000
--- a/.eslintrc.json
+++ /dev/null
@@ -1,59 +0,0 @@
-{
- "root": true,
- "parser": "@typescript-eslint/parser",
- "parserOptions": {
- "ecmaVersion": 6,
- "sourceType": "module"
- },
- "extends": ["plugin:prettier/recommended"],
- "plugins": ["@typescript-eslint", "jsdoc"],
- "rules": {
- "constructor-super": "warn",
- "curly": "warn",
- "eqeqeq": "warn",
- "no-buffer-constructor": "warn",
- "no-caller": "warn",
- "no-debugger": "warn",
- "no-duplicate-case": "warn",
- "no-duplicate-imports": "warn",
- "no-eval": "warn",
- "no-extra-semi": "warn",
- "no-new-wrappers": "warn",
- "no-redeclare": "off",
- "no-sparse-arrays": "warn",
- "no-throw-literal": "warn",
- "no-unsafe-finally": "warn",
- "no-unused-labels": "warn",
- "no-restricted-globals": [
- "warn",
- "name",
- "length",
- "event",
- "closed",
- "external",
- "status",
- "origin",
- "orientation",
- "context"
- ], // non-complete list of globals that are easy to access unintentionally
- "no-var": "warn",
- "jsdoc/no-types": "warn",
- "semi": "off",
- "@typescript-eslint/semi": "warn",
- "@typescript-eslint/naming-convention": [
- "warn",
- {
- "selector": "class",
- "format": ["PascalCase"]
- }
- ]
- },
- "overrides": [
- {
- "files": ["*.js"],
- "rules": {
- "jsdoc/no-types": "off"
- }
- }
- ]
-}
diff --git a/.github/workflows/build.yml b/.github/workflows/build.yml
index 3ba06aca4..ed83fe6ef 100644
--- a/.github/workflows/build.yml
+++ b/.github/workflows/build.yml
@@ -12,31 +12,21 @@ jobs:
build:
strategy:
matrix:
- os: [ubuntu-20.04]
- node-version: [14.x, 15.x]
+ os: [macos-14]
+ node-version: [24.x]
runs-on: ${{ matrix.os }}
steps:
- - uses: actions/checkout@v2
+ - uses: actions/checkout@v4
- name: Use Node.js ${{ matrix.node-version }}
- uses: actions/setup-node@v2
+ uses: actions/setup-node@v4
with:
+ cache: 'npm'
node-version: ${{ matrix.node-version }}
- - name: Get yarn cache directory path
- id: yarn-cache-dir-path
- run: echo "::set-output name=dir::$(yarn cache dir)"
-
- - uses: actions/cache@v2
- with:
- path: ${{ steps.yarn-cache-dir-path.outputs.dir }}
- key: ${{ runner.os }}-yarn-${{ matrix.node-version }}-${{ hashFiles('**/yarn.lock') }}
- restore-keys: |
- ${{ runner.os }}-yarn-${{ matrix.node-version }}
-
- - run: yarn --frozen-lockfile
- - run: yarn eslint
- - run: yarn build
+ - run: npm install
+ - run: npm run eslint
+ - run: npm run build
- uses: microsoft/playwright-github-action@v1
- - run: yarn test:ci
+ - run: npm run test:ci
diff --git a/.github/workflows/codacy-analysis.yaml b/.github/workflows/codacy-analysis.yaml
index e755360c4..7abad0aae 100644
--- a/.github/workflows/codacy-analysis.yaml
+++ b/.github/workflows/codacy-analysis.yaml
@@ -12,23 +12,26 @@ jobs:
runs-on: ubuntu-latest
steps:
- name: Checkout code
- uses: actions/checkout@master
+ uses: actions/checkout@v4
- name: Run Codacy Analysis CLI
uses: codacy/codacy-analysis-cli-action@master
with:
- output: results.sarif
- format: sarif
- # Adjust severity of non-security issues
- gh-code-scanning-compat: true
- # Force 0 exit code to allow SARIF file generation
- # This will handover control about PR rejection to the GitHub side
+ # Run analysis without SARIF output to avoid GitHub Code Scanning integration issues
+ # See: https://github.com/codacy/codacy-analysis-cli-action/issues/142
+ # The Codacy tool generates multiple SARIF runs which is incompatible with
+ # GitHub's new policy as of July 2025
+ verbose: true
+ # Force 0 exit code to prevent workflow failures
max-allowed-issues: 2147483647
# only scan the github1s directory
directory: $GITHUB_WORKSPACE/extensions/github1s
- # Upload the SARIF file generated in the previous step
- - name: Upload SARIF results file
- uses: github/codeql-action/upload-sarif@main
- with:
- sarif_file: results.sarif
+ # SARIF upload is temporarily disabled due to incompatibility
+ # See: https://github.com/codacy/codacy-analysis-cli-action/issues/142
+ # TODO: Re-enable when Codacy fixes the multiple runs issue
+ # - name: Upload SARIF results file
+ # uses: github/codeql-action/upload-sarif@v4
+ # with:
+ # sarif_file: results.sarif
+ # category: codacy-security-scan
diff --git a/.github/workflows/test-wtih-vscode-build.yml b/.github/workflows/test-wtih-vscode-build.yml
index 8fa811d6b..d04876a72 100644
--- a/.github/workflows/test-wtih-vscode-build.yml
+++ b/.github/workflows/test-wtih-vscode-build.yml
@@ -9,34 +9,33 @@ on:
- master
jobs:
- build:
+ build-with-vscode-build:
+ permissions:
+ contents: read
+
strategy:
matrix:
- os: [ubuntu-20.04]
- node-version: [14.x, 15.x]
+ os: [macos-14]
+ node-version: [24.x]
runs-on: ${{ matrix.os }}
steps:
- - uses: actions/checkout@v2
+ - uses: actions/checkout@v4
+ with:
+ persist-credentials: false
- name: Use Node.js ${{ matrix.node-version }}
- uses: actions/setup-node@v2
+ uses: actions/setup-node@v4
with:
+ cache: 'npm'
node-version: ${{ matrix.node-version }}
- - name: Get yarn cache directory path
- id: yarn-cache-dir-path
- run: echo "::set-output name=dir::$(yarn cache dir)"
-
- - uses: actions/cache@v2
- with:
- path: ${{ steps.yarn-cache-dir-path.outputs.dir }}
- key: ${{ runner.os }}-yarn-${{ matrix.node-version }}-${{ hashFiles('**/yarn.lock') }}
- restore-keys: |
- ${{ runner.os }}-yarn-${{ matrix.node-version }}
-
- - run: yarn --frozen-lockfile
- - run: yarn build:vscode
- - run: yarn yalc && yarn build
+ - run: npm install && cd vscode-web && npm install
+ - name: Build VS Code web
+ working-directory: vscode-web
+ env:
+ GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}
+ run: npm run build
+ - run: npm run link && npm run build
- uses: microsoft/playwright-github-action@v1
- - run: yarn test:ci
+ - run: npm run test:ci
diff --git a/.github/workflows/welcome-first-time-contributors.yml b/.github/workflows/welcome-first-time-contributors.yml
new file mode 100644
index 000000000..09741ee73
--- /dev/null
+++ b/.github/workflows/welcome-first-time-contributors.yml
@@ -0,0 +1,37 @@
+name: Welcome first time contributors
+
+on:
+ pull_request_target:
+ types:
+ - opened
+ issues:
+ types:
+ - opened
+
+jobs:
+ welcome:
+ runs-on: ubuntu-latest
+ steps:
+ - uses: actions/first-interaction@v1
+ with:
+ repo-token: ${{ secrets.GITHUB_TOKEN }}
+ issue-message: |
+ Hello there ${{ github.actor }} 👋
+
+ Welcome to github1s !!💖🥳
+
+ Thank you and congratulations 🎉 for opening your very first issue in this project. github1s fosters an open and welcoming environment for all our contributors.🌸
+
+ Incase you want to claim this issue, please comment down below! We will try to get back to you as soon as we can.👀
+
+ Feel free to visit [github1s.com](https://github1s.com/). 👩💻 If you have any interesting ideas, just open an issue. We would love to hear you and engage in discussions.
+
+ pr-message: |
+ Hello there ${{ github.actor }} 👋
+
+ Thank you and congrats 🎉 for opening your first PR on this project.✨
+
+ We will review it soon!
+
+ github1s fosters an open and welcoming environment for all our contributors.🌸
+
diff --git a/.gitignore b/.gitignore
index 9a28bffee..7f61e2ee3 100644
--- a/.gitignore
+++ b/.gitignore
@@ -3,3 +3,4 @@ lib
dist
out
node_modules
+.worktrees/
diff --git a/.gitpod.yml b/.gitpod.yml
index e784cce0b..f5a3e68c5 100644
--- a/.gitpod.yml
+++ b/.gitpod.yml
@@ -2,18 +2,18 @@ image:
file: .gitpod.Dockerfile
tasks:
- init: |
- yarn
- yarn build
+ npm install
+ npm run build
command: |
echo "======================="
- echo "Please run 'yarn watch'"
+ echo "Please run 'npm run watch'"
echo "======================="
- command: |
echo "==========================================================================="
- echo "Please wait for 'yarn watch' to complete compilation, then run 'yarn serve'"
+ echo "Please wait for 'npm run watch' to complete compilation"
echo "==========================================================================="
ports:
- - port: 5000
+ - port: 8080
onOpen: open-browser
github:
prebuilds:
diff --git a/.husky/pre-commit b/.husky/pre-commit
index d2ae35e84..2312dc587 100755
--- a/.husky/pre-commit
+++ b/.husky/pre-commit
@@ -1,4 +1 @@
-#!/bin/sh
-. "$(dirname "$0")/_/husky.sh"
-
-yarn lint-staged
+npx lint-staged
diff --git a/.prettierignore b/.prettierignore
index e7d0610c2..b26157438 100644
--- a/.prettierignore
+++ b/.prettierignore
@@ -2,5 +2,9 @@ lib
dist
out
node_modules
-vscode-web-github1s/src/vs
-vscode-web-github1s/extensions
+vscode-web/src/vs
+vscode-web/extensions
+htm.module.js
+preact.module.js
+preact-hooks.module.js
+index.html
diff --git a/.prettierrc.js b/.prettierrc.js
index 7779ecf43..aa5eda3cd 100644
--- a/.prettierrc.js
+++ b/.prettierrc.js
@@ -1,8 +1,9 @@
-module.exports = {
+export default {
tabWidth: 2,
useTabs: true,
semi: true,
singleQuote: true,
+ printWidth: 120,
overrides: [
{
files: ['*.yml', '*.yaml', '*.json'],
diff --git a/README.md b/README.md
index 3c2b905ab..1f4960174 100644
--- a/README.md
+++ b/README.md
@@ -14,6 +14,8 @@ For example, try it on the VS Code repo:

+You can also use [https://gitlab1s.com](https://gitlab1s.com) or [https://npmjs1s.com](https://npmjs1s.com) in the same way.
+
For browser extensions, see [Third-party Related Projects](https://github.com/conwnet/github1s#third-party-related-projects).
Or save the following code snippet as a bookmarklet, you can use it to quickly switch between github.com and github1s.com (GitHub markdown doesn't allow js links, so just copy it into a bookmark).
@@ -58,10 +60,10 @@ You can start an online development environment with [Gitpod](https://www.gitpod
```bash
git clone git@github.com:conwnet/github1s.git
cd github1s
-yarn
-yarn watch
-yarn serve # in another shell
-# Then visit http://localhost:5000 or http://localhost:5000/conwnet/github1s once the build is completed.
+npm install
+npm run watch
+# The cli will automatically open http://localhost:8080 once the build is completed.
+# You can visit http://localhost:8080/conwnet/github1s if it doesn't.
```
#### Local development with full VS Code build
@@ -73,17 +75,17 @@ To verify the build:
```bash
cd github1s
-yarn build:vscode
+npm run build:vscode
```
-After the inital successful build, you could use the watch mode:
+After the initial successful build, you could use the watch mode:
```bash
cd github1s
-yarn
-yarn watch-with-vscode
-yarn serve # in another shell
-# Then visit http://localhost:5000 or http://localhost:5000/conwnet/github1s once the build is completed.
+npm install
+npm run watch-with-vscode
+# The cli will automatically open http://localhost:8080 once the build is completed.
+# You can visit http://localhost:8080/conwnet/github1s if it doesn't.
```
### ... or ... VS Code + Docker Development
@@ -97,19 +99,19 @@ You can use the VS Code plugin [Remote-Containers](https://marketplace.visualstu
- Now VS Code will create the docker container and connect to the new container so you can use this as a fully setup environment!
-5. Open a new VS Code Terminal, then you can run the `yarn` commands listed above.
+5. Open a new VS Code Terminal, then you can run the `npm install` commands listed above.
```bash
-yarn
-yarn watch
-yarn serve # in another shell
-# Then visit http://localhost:5000 or http://localhost:5000/conwnet/github1s once the build is completed.
+npm install
+npm run watch
+# The cli will automatically open http://localhost:8080 once the build is completed.
+# You can visit http://localhost:8080/conwnet/github1s if it doesn't.
```
### Format all codes
```bash
-yarn format
+npm run format
```
It uses `prettier` to format all possible codes.
@@ -117,17 +119,15 @@ It uses `prettier` to format all possible codes.
## Build
```bash
-yarn
-yarn build
+npm install
+npm run build
```
## Feedback
- If something is not working, [create an issue](https://github.com/conwnet/github1s/issues/new)
-- If you have a question, [discuss on gitter](https://gitter.im/conwnet/github1s)
-
-## Project Sponsors
+## Sponsors
The continued development and maintenance of GitHub1s is made possible by these generous sponsors:
@@ -135,23 +135,46 @@ The continued development and maintenance of GitHub1s is made possible by these
-
-
+
+
+## Partners
+
+We are partnered with [OSS Insight](https://ossinsight.io/?utm_source=github1s&utm_medium=github&utm_campaign=ghtrending) to get the Trending Repositories & some more Interesting Analytics. [OSS Insight](https://ossinsight.io/?utm_source=github1s&utm_medium=github&utm_campaign=ghtrending) provides deep insights into GitHub repos, developers, and curated repo lists from billions of GitHub events. It’s built with [TiDB Cloud](https://www.pingcap.com/tidb-cloud/?utm_source=github1s&utm_medium=github&utm_campaign=ghtrending).
+
+
-## Third-party Related Projects
+## Maintainers! :blush:
+
+
+
+## Stargazers over time
+
+[](https://star-history.com/#conwnet/github1s&Date)
+
+
+Third-party Related Projects
+
### Chrome Extensions
- [Repositree](https://chrome.google.com/webstore/detail/repositree/lafjldoccjnjlcmdhmniholdpjkbgajo) ([chouglesaud/repositree](https://github.com/chouglesaud/repositree))
-- [Open in VS Code](https://chrome.google.com/webstore/detail/open-in-vs-code-github1sc/neloiopjjeflfnecdlajhopdlojlkhll) by [zulhfreelancer](https://github.com/zulhfreelancer)
-- [GitHub1s](https://chrome.google.com/webstore/detail/github1s/lodjfmkfbfkpdhnhkcdcoonghhghbkhe) ([fhefh2015/GitHub1s_chrome_extension](https://github.com/fhefh2015/GitHub1s_chrome_extension))
- [github-code-viewer](https://chrome.google.com/webstore/detail/github-code-viewer/ecddapgifccgblebfibdgkagfbdagjfn) ([febaoshan/edge-extensions-github-code-viewer](https://github.com/febaoshan/edge-extensions-github-code-viewer))
- Github1s Extension ([Darkempire78/GitHub1s-Extension](https://github.com/Darkempire78/GitHub1s-Extension))
- [Github Web IDE](https://chrome.google.com/webstore/detail/adjiklnjodbiaioggfpbpkhbfcnhgkfe) ([zvizvi/Github-Web-IDE](https://github.com/zvizvi/Github-Web-IDE))
- [shortcut to github1s](https://chrome.google.com/webstore/detail/shortcut-to-github1s/gfcdbodapcbfckbfpmgeldfkkgjknceo) ([katsuhisa91/github1s-shortcut](https://github.com/katsuhisa91/github1s-shortcut))
-- [Github1s Shortut - Open source](https://github.com/Fauzdar1/Github1s)
+- [Github1s Shortut - Open source](https://github.com/Fauzdar1/Github1s)
+- [⚡️ 1s to GitHub1s!](https://github.com/holazz/webext-github1s)
+- [github1s Google Chrome Extensions](https://github.com/Lonely-Mr-zhang/github_1s_vscode)
### Firefox Extensions
@@ -172,17 +195,4 @@ The continued development and maintenance of GitHub1s is made possible by these
### Tampermonkey scripts
- [Mr-B0b/TamperMonkeyScripts/vscode.js](https://github.com/Mr-B0b/TamperMonkeyScripts/blob/main/vscode.js)
-
-## Maintainers! :blush:
-
-
-
-## Stargazers over time
-
-[](https://starchart.cc/conwnet/github1s)
+
diff --git a/api/github-auth-callback/index.js b/api/github-auth-callback/index.js
deleted file mode 100644
index a1db0ebcf..000000000
--- a/api/github-auth-callback/index.js
+++ /dev/null
@@ -1,62 +0,0 @@
-/**
- * @file github auth callback
- * @author netcon
- */
-
-const got = require('got');
-
-const CLIENT_ID = process.env.GITHUB_OAUTH_ID || '';
-const CLIENT_SECRET = process.env.GITHUB_OAUTH_SECRET || '';
-// allow origins should split by ','
-const ALLOWED_ORIGINS = process.env.ALLOWED_ORIGINS || '';
-
-// return the data to the opener window by postMessage API,
-// and close current window then
-const getResponseHtml = (dataStr) => `
-
-`;
-
-const MISSING_CODE_ERROR = {
- error: 'request_invalid',
- error_description: 'Missing code',
-};
-const UNKNOWN_ERROR = {
- error: 'internal_error',
- error_description: 'Unknown error',
-};
-
-module.exports = async (req, res) => {
- const code = req.query.code;
- const sendResponseHtml = (status, data) => {
- res.status(status);
- const responseData = { type: 'authorizing', payload: data };
- res.send(getResponseHtml(JSON.stringify(responseData)));
- };
-
- if (!code) {
- return sendResponseHtml(401, MISSING_CODE_ERROR);
- }
-
- try {
- // https://docs.github.com/en/developers/apps/authorizing-oauth-apps#2-users-are-redirected-back-to-your-site-by-github
- const response = await got.post(
- 'https://github.com/login/oauth/access_token',
- {
- json: { client_id: CLIENT_ID, client_secret: CLIENT_SECRET, code },
- responseType: 'json',
- }
- );
- return sendResponseHtml(response.statusCode, response.body);
- } catch (e) {
- // the error is responded by GitHub
- if (e.response) {
- return sendResponseHtml(e.response.statusCode, e.response.body);
- }
- return sendResponseHtml(500, UNKNOWN_ERROR);
- }
-};
diff --git a/api/github-auth-callback/package.json b/api/github-auth-callback/package.json
deleted file mode 100644
index 3b69c9330..000000000
--- a/api/github-auth-callback/package.json
+++ /dev/null
@@ -1,10 +0,0 @@
-{
- "name": "github-auth-callback",
- "version": "0.0.0",
- "main": "index.js",
- "license": "MIT",
- "private": true,
- "dependencies": {
- "got": "^11.8.2"
- }
-}
diff --git a/api/github-auth-callback/yarn.lock b/api/github-auth-callback/yarn.lock
deleted file mode 100644
index b0648e453..000000000
--- a/api/github-auth-callback/yarn.lock
+++ /dev/null
@@ -1,204 +0,0 @@
-# THIS IS AN AUTOGENERATED FILE. DO NOT EDIT THIS FILE DIRECTLY.
-# yarn lockfile v1
-
-
-"@sindresorhus/is@^4.0.0":
- version "4.0.1"
- resolved "https://registry.yarnpkg.com/@sindresorhus/is/-/is-4.0.1.tgz#d26729db850fa327b7cacc5522252194404226f5"
- integrity sha512-Qm9hBEBu18wt1PO2flE7LPb30BHMQt1eQgbV76YntdNk73XZGpn3izvGTYxbGgzXKgbCjiia0uxTd3aTNQrY/g==
-
-"@szmarczak/http-timer@^4.0.5":
- version "4.0.5"
- resolved "https://registry.yarnpkg.com/@szmarczak/http-timer/-/http-timer-4.0.5.tgz#bfbd50211e9dfa51ba07da58a14cdfd333205152"
- integrity sha512-PyRA9sm1Yayuj5OIoJ1hGt2YISX45w9WcFbh6ddT0Z/0yaFxOtGLInr4jUfU1EAFVs0Yfyfev4RNwBlUaHdlDQ==
- dependencies:
- defer-to-connect "^2.0.0"
-
-"@types/cacheable-request@^6.0.1":
- version "6.0.1"
- resolved "https://registry.yarnpkg.com/@types/cacheable-request/-/cacheable-request-6.0.1.tgz#5d22f3dded1fd3a84c0bbeb5039a7419c2c91976"
- integrity sha512-ykFq2zmBGOCbpIXtoVbz4SKY5QriWPh3AjyU4G74RYbtt5yOc5OfaY75ftjg7mikMOla1CTGpX3lLbuJh8DTrQ==
- dependencies:
- "@types/http-cache-semantics" "*"
- "@types/keyv" "*"
- "@types/node" "*"
- "@types/responselike" "*"
-
-"@types/http-cache-semantics@*":
- version "4.0.0"
- resolved "https://registry.yarnpkg.com/@types/http-cache-semantics/-/http-cache-semantics-4.0.0.tgz#9140779736aa2655635ee756e2467d787cfe8a2a"
- integrity sha512-c3Xy026kOF7QOTn00hbIllV1dLR9hG9NkSrLQgCVs8NF6sBU+VGWjD3wLPhmh1TYAc7ugCFsvHYMN4VcBN1U1A==
-
-"@types/keyv@*":
- version "3.1.1"
- resolved "https://registry.yarnpkg.com/@types/keyv/-/keyv-3.1.1.tgz#e45a45324fca9dab716ab1230ee249c9fb52cfa7"
- integrity sha512-MPtoySlAZQ37VoLaPcTHCu1RWJ4llDkULYZIzOYxlhxBqYPB0RsRlmMU0R6tahtFe27mIdkHV+551ZWV4PLmVw==
- dependencies:
- "@types/node" "*"
-
-"@types/node@*":
- version "15.0.2"
- resolved "https://registry.yarnpkg.com/@types/node/-/node-15.0.2.tgz#51e9c0920d1b45936ea04341aa3e2e58d339fb67"
- integrity sha512-p68+a+KoxpoB47015IeYZYRrdqMUcpbK8re/zpFB8Ld46LHC1lPEbp3EXgkEhAYEcPvjJF6ZO+869SQ0aH1dcA==
-
-"@types/responselike@*", "@types/responselike@^1.0.0":
- version "1.0.0"
- resolved "https://registry.yarnpkg.com/@types/responselike/-/responselike-1.0.0.tgz#251f4fe7d154d2bad125abe1b429b23afd262e29"
- integrity sha512-85Y2BjiufFzaMIlvJDvTTB8Fxl2xfLo4HgmHzVBz08w4wDePCTjYw66PdrolO0kzli3yam/YCgRufyo1DdQVTA==
- dependencies:
- "@types/node" "*"
-
-cacheable-lookup@^5.0.3:
- version "5.0.4"
- resolved "https://registry.yarnpkg.com/cacheable-lookup/-/cacheable-lookup-5.0.4.tgz#5a6b865b2c44357be3d5ebc2a467b032719a7005"
- integrity sha512-2/kNscPhpcxrOigMZzbiWF7dz8ilhb/nIHU3EyZiXWXpeq/au8qJ8VhdftMkty3n7Gj6HIGalQG8oiBNB3AJgA==
-
-cacheable-request@^7.0.1:
- version "7.0.1"
- resolved "https://registry.yarnpkg.com/cacheable-request/-/cacheable-request-7.0.1.tgz#062031c2856232782ed694a257fa35da93942a58"
- integrity sha512-lt0mJ6YAnsrBErpTMWeu5kl/tg9xMAWjavYTN6VQXM1A/teBITuNcccXsCxF0tDQQJf9DfAaX5O4e0zp0KlfZw==
- dependencies:
- clone-response "^1.0.2"
- get-stream "^5.1.0"
- http-cache-semantics "^4.0.0"
- keyv "^4.0.0"
- lowercase-keys "^2.0.0"
- normalize-url "^4.1.0"
- responselike "^2.0.0"
-
-clone-response@^1.0.2:
- version "1.0.2"
- resolved "https://registry.yarnpkg.com/clone-response/-/clone-response-1.0.2.tgz#d1dc973920314df67fbeb94223b4ee350239e96b"
- integrity sha1-0dyXOSAxTfZ/vrlCI7TuNQI56Ws=
- dependencies:
- mimic-response "^1.0.0"
-
-decompress-response@^6.0.0:
- version "6.0.0"
- resolved "https://registry.yarnpkg.com/decompress-response/-/decompress-response-6.0.0.tgz#ca387612ddb7e104bd16d85aab00d5ecf09c66fc"
- integrity sha512-aW35yZM6Bb/4oJlZncMH2LCoZtJXTRxES17vE3hoRiowU2kWHaJKFkSBDnDR+cm9J+9QhXmREyIfv0pji9ejCQ==
- dependencies:
- mimic-response "^3.1.0"
-
-defer-to-connect@^2.0.0:
- version "2.0.1"
- resolved "https://registry.yarnpkg.com/defer-to-connect/-/defer-to-connect-2.0.1.tgz#8016bdb4143e4632b77a3449c6236277de520587"
- integrity sha512-4tvttepXG1VaYGrRibk5EwJd1t4udunSOVMdLSAL6mId1ix438oPwPZMALY41FCijukO1L0twNcGsdzS7dHgDg==
-
-end-of-stream@^1.1.0:
- version "1.4.4"
- resolved "https://registry.yarnpkg.com/end-of-stream/-/end-of-stream-1.4.4.tgz#5ae64a5f45057baf3626ec14da0ca5e4b2431eb0"
- integrity sha512-+uw1inIHVPQoaVuHzRyXd21icM+cnt4CzD5rW+NC1wjOUSTOs+Te7FOv7AhN7vS9x/oIyhLP5PR1H+phQAHu5Q==
- dependencies:
- once "^1.4.0"
-
-get-stream@^5.1.0:
- version "5.2.0"
- resolved "https://registry.yarnpkg.com/get-stream/-/get-stream-5.2.0.tgz#4966a1795ee5ace65e706c4b7beb71257d6e22d3"
- integrity sha512-nBF+F1rAZVCu/p7rjzgA+Yb4lfYXrpl7a6VmJrU8wF9I1CKvP/QwPNZHnOlwbTkY6dvtFIzFMSyQXbLoTQPRpA==
- dependencies:
- pump "^3.0.0"
-
-got@^11.8.2:
- version "11.8.2"
- resolved "https://registry.yarnpkg.com/got/-/got-11.8.2.tgz#7abb3959ea28c31f3576f1576c1effce23f33599"
- integrity sha512-D0QywKgIe30ODs+fm8wMZiAcZjypcCodPNuMz5H9Mny7RJ+IjJ10BdmGW7OM7fHXP+O7r6ZwapQ/YQmMSvB0UQ==
- dependencies:
- "@sindresorhus/is" "^4.0.0"
- "@szmarczak/http-timer" "^4.0.5"
- "@types/cacheable-request" "^6.0.1"
- "@types/responselike" "^1.0.0"
- cacheable-lookup "^5.0.3"
- cacheable-request "^7.0.1"
- decompress-response "^6.0.0"
- http2-wrapper "^1.0.0-beta.5.2"
- lowercase-keys "^2.0.0"
- p-cancelable "^2.0.0"
- responselike "^2.0.0"
-
-http-cache-semantics@^4.0.0:
- version "4.1.0"
- resolved "https://registry.yarnpkg.com/http-cache-semantics/-/http-cache-semantics-4.1.0.tgz#49e91c5cbf36c9b94bcfcd71c23d5249ec74e390"
- integrity sha512-carPklcUh7ROWRK7Cv27RPtdhYhUsela/ue5/jKzjegVvXDqM2ILE9Q2BGn9JZJh1g87cp56su/FgQSzcWS8cQ==
-
-http2-wrapper@^1.0.0-beta.5.2:
- version "1.0.3"
- resolved "https://registry.yarnpkg.com/http2-wrapper/-/http2-wrapper-1.0.3.tgz#b8f55e0c1f25d4ebd08b3b0c2c079f9590800b3d"
- integrity sha512-V+23sDMr12Wnz7iTcDeJr3O6AIxlnvT/bmaAAAP/Xda35C90p9599p0F1eHR/N1KILWSoWVAiOMFjBBXaXSMxg==
- dependencies:
- quick-lru "^5.1.1"
- resolve-alpn "^1.0.0"
-
-json-buffer@3.0.1:
- version "3.0.1"
- resolved "https://registry.yarnpkg.com/json-buffer/-/json-buffer-3.0.1.tgz#9338802a30d3b6605fbe0613e094008ca8c05a13"
- integrity sha512-4bV5BfR2mqfQTJm+V5tPPdf+ZpuhiIvTuAB5g8kcrXOZpTT/QwwVRWBywX1ozr6lEuPdbHxwaJlm9G6mI2sfSQ==
-
-keyv@^4.0.0:
- version "4.0.3"
- resolved "https://registry.yarnpkg.com/keyv/-/keyv-4.0.3.tgz#4f3aa98de254803cafcd2896734108daa35e4254"
- integrity sha512-zdGa2TOpSZPq5mU6iowDARnMBZgtCqJ11dJROFi6tg6kTn4nuUdU09lFyLFSaHrWqpIJ+EBq4E8/Dc0Vx5vLdA==
- dependencies:
- json-buffer "3.0.1"
-
-lowercase-keys@^2.0.0:
- version "2.0.0"
- resolved "https://registry.yarnpkg.com/lowercase-keys/-/lowercase-keys-2.0.0.tgz#2603e78b7b4b0006cbca2fbcc8a3202558ac9479"
- integrity sha512-tqNXrS78oMOE73NMxK4EMLQsQowWf8jKooH9g7xPavRT706R6bkQJ6DY2Te7QukaZsulxa30wQ7bk0pm4XiHmA==
-
-mimic-response@^1.0.0:
- version "1.0.1"
- resolved "https://registry.yarnpkg.com/mimic-response/-/mimic-response-1.0.1.tgz#4923538878eef42063cb8a3e3b0798781487ab1b"
- integrity sha512-j5EctnkH7amfV/q5Hgmoal1g2QHFJRraOtmx0JpIqkxhBhI/lJSl1nMpQ45hVarwNETOoWEimndZ4QK0RHxuxQ==
-
-mimic-response@^3.1.0:
- version "3.1.0"
- resolved "https://registry.yarnpkg.com/mimic-response/-/mimic-response-3.1.0.tgz#2d1d59af9c1b129815accc2c46a022a5ce1fa3c9"
- integrity sha512-z0yWI+4FDrrweS8Zmt4Ej5HdJmky15+L2e6Wgn3+iK5fWzb6T3fhNFq2+MeTRb064c6Wr4N/wv0DzQTjNzHNGQ==
-
-normalize-url@^4.1.0:
- version "4.5.0"
- resolved "https://registry.yarnpkg.com/normalize-url/-/normalize-url-4.5.0.tgz#453354087e6ca96957bd8f5baf753f5982142129"
- integrity sha512-2s47yzUxdexf1OhyRi4Em83iQk0aPvwTddtFz4hnSSw9dCEsLEGf6SwIO8ss/19S9iBb5sJaOuTvTGDeZI00BQ==
-
-once@^1.3.1, once@^1.4.0:
- version "1.4.0"
- resolved "https://registry.yarnpkg.com/once/-/once-1.4.0.tgz#583b1aa775961d4b113ac17d9c50baef9dd76bd1"
- integrity sha1-WDsap3WWHUsROsF9nFC6753Xa9E=
- dependencies:
- wrappy "1"
-
-p-cancelable@^2.0.0:
- version "2.1.1"
- resolved "https://registry.yarnpkg.com/p-cancelable/-/p-cancelable-2.1.1.tgz#aab7fbd416582fa32a3db49859c122487c5ed2cf"
- integrity sha512-BZOr3nRQHOntUjTrH8+Lh54smKHoHyur8We1V8DSMVrl5A2malOOwuJRnKRDjSnkoeBh4at6BwEnb5I7Jl31wg==
-
-pump@^3.0.0:
- version "3.0.0"
- resolved "https://registry.yarnpkg.com/pump/-/pump-3.0.0.tgz#b4a2116815bde2f4e1ea602354e8c75565107a64"
- integrity sha512-LwZy+p3SFs1Pytd/jYct4wpv49HiYCqd9Rlc5ZVdk0V+8Yzv6jR5Blk3TRmPL1ft69TxP0IMZGJ+WPFU2BFhww==
- dependencies:
- end-of-stream "^1.1.0"
- once "^1.3.1"
-
-quick-lru@^5.1.1:
- version "5.1.1"
- resolved "https://registry.yarnpkg.com/quick-lru/-/quick-lru-5.1.1.tgz#366493e6b3e42a3a6885e2e99d18f80fb7a8c932"
- integrity sha512-WuyALRjWPDGtt/wzJiadO5AXY+8hZ80hVpe6MyivgraREW751X3SbhRvG3eLKOYN+8VEvqLcf3wdnt44Z4S4SA==
-
-resolve-alpn@^1.0.0:
- version "1.1.2"
- resolved "https://registry.yarnpkg.com/resolve-alpn/-/resolve-alpn-1.1.2.tgz#30b60cfbb0c0b8dc897940fe13fe255afcdd4d28"
- integrity sha512-8OyfzhAtA32LVUsJSke3auIyINcwdh5l3cvYKdKO0nvsYSKuiLfTM5i78PJswFPT8y6cPW+L1v6/hE95chcpDA==
-
-responselike@^2.0.0:
- version "2.0.0"
- resolved "https://registry.yarnpkg.com/responselike/-/responselike-2.0.0.tgz#26391bcc3174f750f9a79eacc40a12a5c42d7723"
- integrity sha512-xH48u3FTB9VsZw7R+vvgaKeLKzT6jOogbQhEe/jewwnZgzPcnyWui2Av6JpoYZF/91uueC+lqhWqeURw5/qhCw==
- dependencies:
- lowercase-keys "^2.0.0"
-
-wrappy@1:
- version "1.0.2"
- resolved "https://registry.yarnpkg.com/wrappy/-/wrappy-1.0.2.tgz#b5243d8f3ec1aa35f1364605bc0d1036e30ab69f"
- integrity sha1-tSQ9jz7BqjXxNkYFvA0QNuMKtp8=
diff --git a/api/static-fallback/index.js b/api/static-fallback/index.js
deleted file mode 100644
index 32c04b7c3..000000000
--- a/api/static-fallback/index.js
+++ /dev/null
@@ -1,17 +0,0 @@
-/**
- * @file static files not found fallback
- * According to the config in vercel.json, we cached the
- * static files with `max-age=X` header, but we may get 404
- * response durning deploying, the default behavior for vercel
- * would add the cache header to this 404 response, which caused
- * the user can not get right resources anymore. so we should
- * fallback to here and clear the cache header for such 404 requests.
- * See also: https://github.com/conwnet/github1s/issues/299
- * @author netcon
- */
-
-module.exports = async (req, res) => {
- res.status(404);
- res.setHeader('Cache-Control', 'no-store');
- res.send('Not Found');
-};
diff --git a/docs/guide.md b/docs/guide.md
index 3c559620c..4c30e8934 100644
--- a/docs/guide.md
+++ b/docs/guide.md
@@ -1,6 +1,6 @@
# How it works
-GitHub1s is based on [VS Code 1.55.0](https://github.com/microsoft/vscode/tree/1.52.1) now. VS Code can be built for a browser version officially. I also used the code and got inspired by [Code Server](https://github.com/cdr/code-server).
+GitHub1s is based on [VS Code 1.66.2](https://github.com/microsoft/vscode/tree/1.66.2) now. VS Code can be built for a browser version officially. I also used the code and got inspired by [Code Server](https://github.com/cdr/code-server).
Thanks to the very powerful and flexible extensibility of VS Code, we can easily implement a VS Code extension that provides the custom File IO ability using [FileSystemProvider API](https://code.visualstudio.com/api/references/vscode-api#FileSystemProvider). There is an official demo named [vscode-web-playground](https://github.com/microsoft/vscode-web-playground) which shows how it is used.
@@ -12,7 +12,7 @@ _We may switch to the GitHub GraphQL API for more friendly user experience in th
~~GitHub1s is a purely static web app (because it really doesn't need a backend service, does it?). So we just deploy it on [GitHub Pages](https://pages.github.com/) now (the `gh-pages` branch of this repository), and it is free. The service of GitHub1s could be reliable (GitHub is very reliable) because nobody needs to pay the web hosting bills.~~
-We deploy GitHub1s on [Vercel](https://vercel.com/) now for minimize delays in loading and better developer experience. Thanks for the wonderful service provide by Vercel.
+We deploy GitHub1s on [Cloudflare Pages](https://cloudflare.com/) now for minimize delays in loading and better developer experience. Thanks for the wonderful service provide by Cloudflare.
# Rate Limit
@@ -26,42 +26,48 @@ So, if you meet some problems when you use GitHub1s, even if you are using newer
But this does not mean the token is absolutely safe, **don't forget to clean it while you are using a device that doesn't belong to you**.
+# Sourcegraph API
+
+Due to the potential RateLimit of the GitHub API, we will prioritize the use of the [Sourcegraph API](https://sourcegraph.com/docs) for public repositories, with the exception of the Read interface, the code search capability is also provided by the Sourcegraph API.
+
+By default, GitHub1s will only try to use the GitHub API when the Sourcegraph API request fails, and you can adjust this option in the settings.
+
# Development
As you see, running GitHub1s locally is not difficult. After cloning the repository, just run these commands:
```shell
-$ yarn
-$ yarn watch # or yarn build, it may take minutes, wait please
+$ npm install
+$ npm run watch
```
-Then, there will be a new directory named `dist` generated in the project root. You can run `yarn serve` in another shell, and it will create a static file server for the `dist` directory.
+Then, there will be a new directory named `dist` generated in the project root. The `npm run watch:dev-server` (part of `npm run watch` command) will automatically open http://localhost:8080 in the browser.
-Now you can visit http://localhost:5000 in the browser. If you get a 404 error for some static files, please wait a minute for the building to complete.
+If you get a 404 error for some static files, please wait a minute for the building to complete.
## Watch Mode
-What happens after you run `yarn watch-with-vscode`?
+What happens after you run `npm run watch-with-vscode`?
-1. Copy some necessary resources (`index.html`, extensions config, libraries, etc.) to the `dist` directory.
+1. Copy some necessary resources (`index.html`, `favicons.ico`, etc.) to the `dist` directory.
-2. Go to `vscode-web-github1s/lib/vscode` and run `yarn gulp compile-web` to build the necessary extensions, then copy it to the `dist/extensions` directory.
+2. This command will compile the codes in `src` and generate application entry script (see `webpack.config.js`). This command also compile the custom extensions (for example `github1s`) in `extensions` directory.
-3. Go to `vscode-web-github1s/lib/vscode` and run `yarn watch` (the native watch of vscode), it will trigger a new build if something in it has been changed.
+3. Redirect vscode-web static requests (vscode, extensisions, dependencies) to `vscode-web/lib/vscode` which should be generated by vscode compile process.
-4. Watch the `vscode-web-github1s/src` directory, merge it in to `vscode-web-github1s/lib/vscode/src` if something in it has been changed. (When a new file is merged into `lib/vscode/src`, it will trigger the watcher that is described in Step 3)
+You should also compile the vscode manually in another terminal.
-5. Go to `extensions/github1s` and run `yarn watch`, it will trigger a new build if something has been changed.
+1. Go to `vscode-web` and run `npm install && npm run watch` (the native watch of vscode), it will trigger a new build if something in it has been changed.
-6. Watch the `extensions` directory and the `lib/vscode/out` directory, merge them into the `dist` directory if something changed in them.
+2. This command will alose watch the `vscode-web/src` and `vscode-web/extensions` directory, merge it in to `vscode-web/lib/vscode` if something in it has been changed. (When a new file is merged into `lib/vscode`, it will trigger the watcher that is described in Step 3)
Note that since we have modified the source code of VS Code, it may get into trouble when merging a newer version VS Code.
It is a little laborious to complete the watch process, but I didn't think of a better solution.
-What happens after you run `yarn watch`?
+What happens after you run `npm run watch`?
-It's the same procedure as `yarn watch-with-vscode` without the step 2, 3, and 4. Instead of the local VS Code, it uses the prebuilt [@github1s/vscode-web](https://www.npmjs.com/package/@github1s/vscode-web) version.
+It's the same procedure as `` without the step 3. Instead of the local VS Code, it uses the prebuilt [@github1s/vscode-web](https://www.npmjs.com/package/@github1s/vscode-web) version.
## Build mode
@@ -73,6 +79,8 @@ Put simply, we build the necessary code and do a minify. The minify script is mo
- `src` - the code in here will be patched into VS Code source.
+- `vscode-web` - This contains the code to patch VS Code.
+
- `scripts` - some scripts for build, watch, package, etc.
- `resources` - some resource files such as templates, pictures, configuration files, etc.
diff --git a/eslint.config.js b/eslint.config.js
new file mode 100644
index 000000000..0cc42d3dd
--- /dev/null
+++ b/eslint.config.js
@@ -0,0 +1,17 @@
+import jsdoc from 'eslint-plugin-jsdoc';
+import tseslint from 'typescript-eslint';
+import eslintPluginPrettierRecommended from 'eslint-plugin-prettier/recommended';
+
+export default [
+ { ignores: ['**/dist', '**/assets', 'vscode-web/lib', '**/vs', '**/vscode.proposed.d.ts'] },
+ ...tseslint.configs.recommended,
+ jsdoc.configs['flat/recommended-typescript'],
+ eslintPluginPrettierRecommended,
+ {
+ rules: {
+ '@typescript-eslint/no-explicit-any': 'off',
+ '@typescript-eslint/no-unused-expressions': 'off',
+ '@typescript-eslint/no-unused-vars': 'off',
+ },
+ },
+];
diff --git a/extensions/dart-web/LICENSE b/extensions/dart-web/LICENSE
deleted file mode 100644
index 1a4f6b68c..000000000
--- a/extensions/dart-web/LICENSE
+++ /dev/null
@@ -1,21 +0,0 @@
-MIT License
-
-Copyright (c) 2016 Danny Tuppeny
-
-Permission is hereby granted, free of charge, to any person obtaining a copy
-of this software and associated documentation files (the "Software"), to deal
-in the Software without restriction, including without limitation the rights
-to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
-copies of the Software, and to permit persons to whom the Software is
-furnished to do so, subject to the following conditions:
-
-The above copyright notice and this permission notice shall be included in all
-copies or substantial portions of the Software.
-
-THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
-IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
-FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
-AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
-LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
-OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
-SOFTWARE.
\ No newline at end of file
diff --git a/extensions/dart-web/README.md b/extensions/dart-web/README.md
deleted file mode 100644
index ee961bf05..000000000
--- a/extensions/dart-web/README.md
+++ /dev/null
@@ -1,31 +0,0 @@
-[](https://discord.gg/xSTPEqm)
-[](https://gitter.im/dart-code/Dart-Code)
-[](https://twitter.com/DartCode)
-[](https://github.com/Dart-Code/Dart-Code/blob/master/CONTRIBUTING.md)
-
-# This extension is a fork from [Dart-Code](https://github.com/Dart-Code/Dart-Code) for github1s.
-
-# At present only languages features is reserved
-
-# I have deleted some files and only reserved the necessary code
-
-## Introduction
-
-Dart Code extends [VS Code](https://code.visualstudio.com/) with support for the
-[Dart](https://www.dartlang.org/) programming language and for [Flutter](https://flutter.dev/)
-mobile apps, and [AngularDart](https://angulardart.org) web apps.
-
-## Installation
-
-Dart Code can be [installed from the Visual Studio Code Marketplace](https://marketplace.visualstudio.com/items?itemName=Dart-Code.dart-code) or by [searching within VS Code](https://code.visualstudio.com/docs/editor/extension-gallery#_search-for-an-extension).
-
-## Features
-
-- Syntax highlighting
-- Workspace symbol search
-- Document symbol search
-- Snippets
-
-## Release Notes
-
-For full release notes, see [the changelog](https://dartcode.org/releases/).
diff --git a/extensions/dart-web/media/dart.png b/extensions/dart-web/media/dart.png
deleted file mode 100755
index d091c28f0..000000000
Binary files a/extensions/dart-web/media/dart.png and /dev/null differ
diff --git a/extensions/dart-web/package.json b/extensions/dart-web/package.json
deleted file mode 100644
index e0dc7cca3..000000000
--- a/extensions/dart-web/package.json
+++ /dev/null
@@ -1,96 +0,0 @@
-{
- "name": "dart-code",
- "displayName": "Dart",
- "description": "Dart language support and debugger for Visual Studio Code.",
- "version": "3.20.0-dev",
- "publisher": "Dart-Code",
- "engines": {
- "vscode": "^1.52.0"
- },
- "license": "SEE LICENSE IN LICENSE",
- "bugs": {
- "url": "https://github.com/Dart-Code/Dart-Code/issues"
- },
- "homepage": "https://dartcode.org/",
- "repository": {
- "type": "git",
- "url": "https://github.com/Dart-Code/Dart-Code.git"
- },
- "categories": [
- "Programming Languages",
- "Snippets",
- "Linters",
- "Formatters",
- "Debuggers"
- ],
- "keywords": [
- "dart",
- "flutter",
- "fuchsia",
- "multi-root ready"
- ],
- "icon": "media/dart.png",
- "activationEvents": [
- "onLanguage:dart"
- ],
- "contributes": {
- "languages": [
- {
- "id": "dart",
- "extensions": [
- ".dart"
- ],
- "aliases": [
- "Dart"
- ],
- "configuration": "./syntaxes/dart-language-configuration.json"
- }
- ],
- "grammars": [
- {
- "language": "dart",
- "scopeName": "source.dart",
- "path": "./syntaxes/dart.json"
- }
- ],
- "colors": [
- {
- "id": "dart.closingLabels",
- "description": "The color of the 'closing label' annotations shown against constructor, method invocations and lists that span multiple lines. If not supplied, the color for 'tab.inactiveForeground' will be used.",
- "defaults": {
- "dark": "tab.inactiveForeground",
- "light": "tab.inactiveForeground",
- "highContrast": "tab.inactiveForeground"
- }
- }
- ],
- "semanticTokenScopes": [
- {
- "language": "dart",
- "scopes": {
- "keyword": [
- "keyword"
- ],
- "keyword.control": [
- "keyword.control"
- ],
- "property.declaration": [
- "entity.name.function.member"
- ]
- }
- }
- ],
- "configurationDefaults": {
- "[dart]": {
- "editor.tabSize": 2,
- "editor.insertSpaces": true,
- "editor.detectIndentation": false,
- "editor.suggest.insertMode": "replace"
- }
- }
- },
- "scripts": {
- "compile": "echo done",
- "watch": "echo done"
- }
-}
diff --git a/extensions/dart-web/snippets/dart.json b/extensions/dart-web/snippets/dart.json
deleted file mode 100644
index 7e2541e3c..000000000
--- a/extensions/dart-web/snippets/dart.json
+++ /dev/null
@@ -1,81 +0,0 @@
-{
- ".source.dart": {
- "main": {
- "prefix": "main",
- "description": "Insert a main function, used as an entry point.",
- "body": ["main(List args) {", " $0", "}"]
- },
- "try": {
- "prefix": "try",
- "description": "Insert a try/catch block.",
- "body": ["try {", " $0", "} catch (${1:e}) {", "}"]
- },
- "if": {
- "prefix": "if",
- "description": "Insert an if statement.",
- "body": ["if ($1) {", " $0", "}"]
- },
- "if else": {
- "prefix": "ife",
- "description": "Insert an if statement with an else block.",
- "body": ["if ($1) {", " $0", "} else {", "}"]
- },
- "switch case": {
- "prefix": "switch",
- "description": "Insert a switch statement.",
- "body": [
- "switch ($1) {",
- " case $2:",
- " $0",
- " break;",
- " default:",
- "}"
- ]
- },
- "for": {
- "prefix": "for",
- "description": "Insert a for loop.",
- "body": ["for (var i = 0; i < ${1:count}; i++) {", " $0", "}"]
- },
- "for in": {
- "prefix": "fori",
- "description": "Insert a for-in loop.",
- "body": ["for (var ${1:item} in ${2:items}) {", " $0", "}"]
- },
- "while": {
- "prefix": "while",
- "description": "Insert a while loop.",
- "body": ["while ($1) {", " $0", "}"]
- },
- "do while": {
- "prefix": "do",
- "description": "Insert a do-while loop.",
- "body": ["do {", " $0", "} while ($1);"]
- },
- "fun": {
- "prefix": "fun",
- "description": "Insert a function definition.",
- "body": ["${3:void} ${1:name}(${2:args}) {", " $0", "}"]
- },
- "class": {
- "prefix": "class",
- "description": "Insert a class definition.",
- "body": ["class ${1:Name} {", " $0", "}"]
- },
- "typedef": {
- "prefix": "typedef",
- "description": "Insert a typedef.",
- "body": "typedef ${1:Type} ${2:Name}(${3:params});"
- },
- "test": {
- "prefix": "test",
- "description": "Insert a test block.",
- "body": ["test('$1', () {", " $0", "});"]
- },
- "group": {
- "prefix": "group",
- "description": "Insert a test group block.",
- "body": ["group('$1', () {", " $0", "});"]
- }
- }
-}
diff --git a/extensions/dart-web/snippets/flutter.json b/extensions/dart-web/snippets/flutter.json
deleted file mode 100644
index 3c09049c3..000000000
--- a/extensions/dart-web/snippets/flutter.json
+++ /dev/null
@@ -1,71 +0,0 @@
-{
- ".source.dart": {
- "Flutter stateless widget": {
- "prefix": "stless",
- "description": "Insert a StatelessWidget",
- "body": [
- "class $1 extends StatelessWidget {",
- " @override",
- " Widget build(BuildContext context) {",
- " return Container(",
- " $2",
- " );",
- " }",
- "}"
- ]
- },
- "Flutter stateful widget": {
- "prefix": "stful",
- "description": "Insert a StatefulWidget",
- "body": [
- "class $1 extends StatefulWidget {",
- " @override",
- " _$1State createState() => _$1State();",
- "}",
- "",
- "class _$1State extends State<$1> {",
- " @override",
- " Widget build(BuildContext context) {",
- " return Container(",
- " $2",
- " );",
- " }",
- "}"
- ]
- },
- "Flutter widget with AnimationController": {
- "prefix": "stanim",
- "description": "Insert a StatefulWidget with an AnimationController",
- "body": [
- "class $1 extends StatefulWidget {",
- " @override",
- " _$1State createState() => _$1State();",
- "}",
- "",
- "class _$1State extends State<$1>",
- " with SingleTickerProviderStateMixin {",
- " AnimationController _controller;",
- "",
- " @override",
- " void initState() {",
- " super.initState();",
- " _controller = AnimationController(vsync: this);",
- " }",
- "",
- " @override",
- " void dispose() {",
- " super.dispose();",
- " _controller.dispose();",
- " }",
- "",
- " @override",
- " Widget build(BuildContext context) {",
- " return Container(",
- " $2",
- " );",
- " }",
- "}"
- ]
- }
- }
-}
diff --git a/extensions/dart-web/syntaxes/dart-language-configuration.json b/extensions/dart-web/syntaxes/dart-language-configuration.json
deleted file mode 100644
index 171fa5beb..000000000
--- a/extensions/dart-web/syntaxes/dart-language-configuration.json
+++ /dev/null
@@ -1,53 +0,0 @@
-{
- "comments": {
- "lineComment": "//",
- "blockComment": ["/*", "*/"]
- },
- "brackets": [
- ["{", "}"],
- ["[", "]"],
- ["(", ")"]
- ],
- "autoClosingPairs": [{
- "open": "{",
- "close": "}"
- },
- {
- "open": "[",
- "close": "]"
- },
- {
- "open": "(",
- "close": ")"
- },
- {
- "open": "'",
- "close": "'",
- "notIn": ["string", "comment"]
- },
- {
- "open": "\"",
- "close": "\"",
- "notIn": ["string"]
- },
- {
- "open": "`",
- "close": "`",
- "notIn": ["string", "comment"]
- },
- {
- "open": "/**",
- "close": " */",
- "notIn": ["string"]
- }
- ],
- "surroundingPairs": [
- ["{", "}"],
- ["[", "]"],
- ["(", ")"],
- ["<", ">"],
- ["'", "'"],
- ["\"", "\""],
- ["`", "`"]
- ]
-}
\ No newline at end of file
diff --git a/extensions/dart-web/syntaxes/dart.json b/extensions/dart-web/syntaxes/dart.json
deleted file mode 100644
index d8b255cd1..000000000
--- a/extensions/dart-web/syntaxes/dart.json
+++ /dev/null
@@ -1,436 +0,0 @@
-{
- "fileTypes": ["dart"],
- "foldingStartMarker": "\\{\\s*$",
- "foldingStopMarker": "^\\s*\\}",
- "name": "Dart",
- "patterns": [
- {
- "match": "^(#!.*)$",
- "name": "meta.preprocessor.script.dart"
- },
- {
- "begin": "^\\w*\\b(library|import|part of|part|export)\\b",
- "beginCaptures": {
- "0": {
- "name": "keyword.other.import.dart"
- }
- },
- "end": ";",
- "endCaptures": {
- "0": {
- "name": "punctuation.terminator.dart"
- }
- },
- "name": "meta.declaration.dart",
- "patterns": [
- {
- "include": "#strings"
- },
- {
- "include": "#comments"
- },
- {
- "match": "\\b(as|show|hide)\\b",
- "name": "keyword.other.import.dart"
- }
- ]
- },
- {
- "include": "#comments"
- },
- {
- "include": "#punctuation"
- },
- {
- "include": "#annotations"
- },
- {
- "include": "#keywords"
- },
- {
- "include": "#constants-and-special-vars"
- },
- {
- "include": "#strings"
- }
- ],
- "repository": {
- "dartdoc": {
- "patterns": [
- {
- "match": "(\\[.*?\\])",
- "captures": {
- "0": {
- "name": "variable.name.source.dart"
- }
- }
- },
- {
- "match": "^ {4,}(?![ \\*]).*",
- "captures": {
- "0": {
- "name": "variable.name.source.dart"
- }
- }
- },
- {
- "begin": "```.*?$",
- "end": "```",
- "contentName": "variable.other.source.dart"
- },
- {
- "match": "(`.*?`)",
- "captures": {
- "0": {
- "name": "variable.other.source.dart"
- }
- }
- },
- {
- "match": "(`.*?`)",
- "captures": {
- "0": {
- "name": "variable.other.source.dart"
- }
- }
- },
- {
- "match": "(\\* (( ).*))$",
- "captures": {
- "2": {
- "name": "variable.other.source.dart"
- }
- }
- },
- {
- "match": "(\\* .*)$"
- }
- ]
- },
- "comments": {
- "patterns": [
- {
- "captures": {
- "0": {
- "name": "punctuation.definition.comment.dart"
- }
- },
- "match": "/\\*\\*/",
- "name": "comment.block.empty.dart"
- },
- {
- "include": "#comments-doc-oldschool"
- },
- {
- "include": "#comments-doc"
- },
- {
- "include": "#comments-inline"
- }
- ]
- },
- "comments-doc-oldschool": {
- "patterns": [
- {
- "begin": "/\\*\\*",
- "end": "\\*/",
- "name": "comment.block.documentation.dart",
- "patterns": [
- {
- "include": "#comments-doc-oldschool"
- },
- {
- "include": "#comments-block"
- },
- {
- "include": "#dartdoc"
- }
- ]
- }
- ]
- },
- "comments-doc": {
- "patterns": [
- {
- "begin": "///",
- "while": "^\\s*///",
- "patterns": [
- {
- "include": "#dartdoc"
- }
- ],
- "name": "comment.block.documentation.dart"
- }
- ]
- },
- "comments-inline": {
- "patterns": [
- {
- "include": "#comments-block"
- },
- {
- "captures": {
- "1": {
- "name": "comment.line.double-slash.dart"
- }
- },
- "match": "((//).*)$"
- }
- ]
- },
- "comments-block": {
- "patterns": [
- {
- "begin": "/\\*",
- "end": "\\*/",
- "name": "comment.block.dart",
- "patterns": [
- {
- "include": "#comments-block"
- }
- ]
- }
- ]
- },
- "annotations": {
- "patterns": [
- {
- "match": "@[a-zA-Z]+",
- "name": "storage.type.annotation.dart"
- }
- ]
- },
- "constants-and-special-vars": {
- "patterns": [
- {
- "match": "(?)",
- "captures": {
- "1": {
- "name": "entity.name.function.dart"
- }
- }
- }
- ]
- },
- "keywords": {
- "patterns": [
- {
- "match": "(?>>?|~|\\^|\\||&)",
- "name": "keyword.operator.bitwise.dart"
- },
- {
- "match": "((&|\\^|\\||<<|>>>?)=)",
- "name": "keyword.operator.assignment.bitwise.dart"
- },
- {
- "match": "(=>)",
- "name": "keyword.operator.closure.dart"
- },
- {
- "match": "(==|!=|<=?|>=?)",
- "name": "keyword.operator.comparison.dart"
- },
- {
- "match": "(([+*/%-]|\\~)=)",
- "name": "keyword.operator.assignment.arithmetic.dart"
- },
- {
- "match": "(=)",
- "name": "keyword.operator.assignment.dart"
- },
- {
- "match": "(\\-\\-|\\+\\+)",
- "name": "keyword.operator.increment-decrement.dart"
- },
- {
- "match": "(\\-|\\+|\\*|\\/|\\~\\/|%)",
- "name": "keyword.operator.arithmetic.dart"
- },
- {
- "match": "(!|&&|\\|\\|)",
- "name": "keyword.operator.logical.dart"
- },
- {
- "match": "(?
+
+
+
diff --git a/extensions/github1s/assets/pages/assets/gitlab.svg b/extensions/github1s/assets/pages/assets/gitlab.svg
new file mode 100644
index 000000000..8cca7d76f
--- /dev/null
+++ b/extensions/github1s/assets/pages/assets/gitlab.svg
@@ -0,0 +1,7 @@
+
+
+
+
+
+
+
diff --git a/extensions/github1s/assets/pages/components.css b/extensions/github1s/assets/pages/components.css
new file mode 100644
index 000000000..4932fc33d
--- /dev/null
+++ b/extensions/github1s/assets/pages/components.css
@@ -0,0 +1,202 @@
+.vscode-button {
+ border: none;
+ display: inline-block;
+ padding: 0 10px;
+ height: 26px;
+ line-height: 26px;
+ font-size: 14px;
+ outline: 1px solid transparent;
+ outline-offset: 2px !important;
+ color: var(--vscode-button-foreground);
+ background: var(--vscode-button-background);
+ cursor: pointer;
+}
+
+.vscode-button.loading {
+ opacity: .5;
+}
+
+.vscode-button.size-mini {
+ padding: 0 8px;
+ height: 22px;
+ line-height: 22px;
+ font-size: 12px;
+}
+
+.vscode-button.size-middle {
+ padding: 0 12px;
+ height: 30px;
+ line-height: 30px;
+ font-size: 14px;
+}
+
+.vscode-button.size-large {
+ padding: 0 14px;
+ height: 34px;
+ line-height: 34px;
+ font-size: 18px;
+}
+
+.vscode-button:not([disabled]):hover {
+ background: var(--vscode-button-hoverBackground);
+}
+
+.vscode-button:not([disabled]):focus {
+ outline-color: var(--vscode-focusBorder);
+}
+
+.vscode-input {
+ border: none;
+ display: inline-block;
+ padding: 0 4px;
+ height: 26px;
+ line-height: 26px;
+ font-size: 13px;
+ outline: 1px solid transparent;
+ font-family: var(--vscode-font-family);
+ color: var(--vscode-input-foreground);
+ border: 1px solid var(--vscode-input-border, var(--vscode-widget-border));
+ background-color: var(--vscode-input-background);
+}
+
+.vscode-input.size-mini {
+ padding: 0 2px;
+ height: 22px;
+ line-height: 22px;
+ font-size: 12px;
+}
+
+.vscode-input.size-middle {
+ padding: 0 4px;
+ height: 30px;
+ line-height: 30px;
+ font-size: 13px;
+}
+
+.vscode-input.size-large {
+ padding: 0 6px;
+ height: 34px;
+ line-height: 34px;
+ font-size: 18px;
+}
+
+.vscode-input:not([disabled]):focus {
+ outline-color: var(--vscode-focusBorder);
+ outline: 1px solid -webkit-focus-ring-color;
+ outline-offset: -1px;
+}
+
+.vscode-textarea {
+ border: none;
+ display: inline-block;
+ outline: 1px solid transparent;
+ font-family: var(--vscode-font-family);
+ color: var(--vscode-input-foreground);
+ border: 1px solid var(--vscode-input-background);
+ background-color: var(--vscode-input-background);
+}
+
+.vscode-textarea:not([disabled]):focus {
+ outline-color: var(--vscode-focusBorder);
+ outline: 1px solid -webkit-focus-ring-color;
+ outline-offset: -1px;
+}
+
+.vscode-loading {
+ width: 100%;
+ text-align: center;
+ height: 26px;
+}
+
+.vscode-loading.align-left {
+ text-align: left;
+}
+
+.vscode-loading.align-right {
+ text-align: right;
+}
+
+.vscode-loading > span {
+ height: 100%;
+ width: 5px;
+ display: inline-block;
+ margin-right: 4px;
+ background: var(--vscode-button-background);
+ animation: vscodeLoading 1.2s infinite ease-in-out;
+}
+
+.vscode-loading > span:nth-child(2) {
+ animation-delay: -1s;
+}
+
+.vscode-loading > span:nth-child(3) {
+ animation-delay: -0.9s;
+}
+
+.vscode-loading > span:nth-child(4) {
+ animation-delay: -0.8s;
+}
+
+.vscode-loading > span:nth-child(5) {
+ animation-delay: -0.7s;
+}
+
+.vscode-loading > span:nth-child(6) {
+ animation-delay: -0.6s;
+}
+
+.vscode-loading > span:nth-child(7) {
+ animation-delay: -0.5s;
+}
+
+.vscode-loading > span:nth-child(8) {
+ animation-delay: -0.4s;
+}
+
+.vscode-loading > span:nth-child(9) {
+ animation-delay: -0.3s;
+}
+
+.vscode-loading > span:last-child {
+ margin-right: 0 !important;
+}
+
+@keyframes vscodeLoading {
+ 0% {
+ transform: scaleY(0.4);
+ }
+ 25% {
+ transform: scaleY(1);
+ }
+ 50% {
+ transform: scaleY(0.4);
+ }
+ 75% {
+ transform: scaleY(0.4);
+ }
+ 100% {
+ transform: scaleY(0.4);
+ }
+}
+
+.vscode-link {
+ cursor: pointer;
+ color: var(--vscode-textLink-foreground);
+ text-decoration: underline;
+}
+
+.vscode-link:hover {
+ color: var(--vscode-textLink-activeForeground);
+}
+
+.success-text {
+ color: #89d185;
+}
+
+.warning-text {
+ color: #cca700;
+}
+
+.error-text {
+ color: #f48771;
+}
diff --git a/extensions/github1s/assets/pages/components.js b/extensions/github1s/assets/pages/components.js
new file mode 100644
index 000000000..ad73142b2
--- /dev/null
+++ b/extensions/github1s/assets/pages/components.js
@@ -0,0 +1,74 @@
+import { h } from './libraries/preact.module.js';
+import htm from './libraries/htm.module.js';
+
+export const html = htm.bind(h);
+
+export const VscodeButton = ({ size, loading, ...props }) => {
+ const sizeClass = `size-${size || 'small'}`;
+ const loadingClass = loading ? 'loading' : '';
+ const classes = `vscode-button ${sizeClass} ${loadingClass}`;
+
+ return html` `;
+};
+
+export const VscodeInput = ({ size, ...props }) => {
+ const classes = `vscode-input size-${size || 'small'}`;
+
+ return html` `;
+};
+
+export const VscodeTextarea = (props) => {
+ return html` `;
+};
+
+export const VscodeLoading = ({ blockWidth, blockSpacing, dots = 5, align = 'center', ...props }) => {
+ const _blockWidth = blockWidth || '5px';
+ const _blockSpacing = blockSpacing || '4px';
+ const styleStr = `width: ${_blockWidth}; margin-right: ${_blockSpacing}`;
+ const blocks = Array.from({ length: dots }).map(() => html` `);
+ const classes = 'vscode-loading' + (align !== 'center' ? ` align-${align}` : '');
+
+ return html`${blocks}
`;
+};
+
+export const VscodeLink = ({ to, external, ...props }) => {
+ const hrefProp = to ? { href: to } : {};
+ const roleProp = to ? {} : { role: 'button' };
+ const targetProp = external ? { target: '_blank' } : {};
+ const combineProps = { ...hrefProp, ...roleProp, ...targetProp };
+
+ return html` `;
+};
+
+const postMessage = (() => {
+ const vscode = window.acquireVsCodeApi();
+ const uniqueId = (
+ (id) => () =>
+ id++
+ )(1);
+ const messageMap = new Map();
+
+ window.addEventListener('message', ({ data }) => {
+ messageMap.has(data.id) && messageMap.get(data.id)(data.data);
+ });
+
+ return (type, data) => {
+ const messageId = uniqueId();
+ vscode.postMessage({ type, data, id: messageId });
+ return new Promise((resolve) => {
+ messageMap.set(messageId, resolve);
+ });
+ };
+})();
+
+export const bridgeCommands = {
+ getToken: () => postMessage('get-token'),
+ setToken: (token) => postMessage('set-token', token),
+ validateToken: (token) => postMessage('validate-token', token),
+ openDetailPage: () => postMessage('open-detail-page'),
+ OAuthAuthenticate: () => postMessage('oauth-authorizing'),
+ alertMessage: (messageArgs) => postMessage('call-vscode-message-api', messageArgs),
+ getPreferSgApi: () => postMessage('get-prefer-sourcegraph-api'),
+ setPreferSgApi: (value) => postMessage('set-prefer-sourcegraph-api', value),
+ getNotice: () => postMessage('get-notice'),
+};
diff --git a/extensions/github1s/assets/pages/github1s-authentication.css b/extensions/github1s/assets/pages/github1s-authentication.css
new file mode 100644
index 000000000..67caee11e
--- /dev/null
+++ b/extensions/github1s/assets/pages/github1s-authentication.css
@@ -0,0 +1,244 @@
+.authentication-page {
+ display: flex;
+ flex-direction: column;
+ align-items: center;
+}
+
+.authentication-page .page-notice {
+ font-size: 16px;
+ margin-bottom: 24px;
+ font-weight: bold;
+ color: #cca700;
+ max-width: 520px;
+ text-align: center;
+}
+
+.authentication-form .form-title {
+ font-size: 24px;
+ text-align: center;
+ margin-bottom: 32px;
+ color: var(--vscode-button-foreground);
+}
+
+.authentication-form .form-content {
+ width: 320px;
+}
+
+.authentication-features {
+ list-style: none;
+ font-size: 14px;
+ padding: 0;
+ margin: 0 0 32px;
+}
+
+.authentication-features .feature-item {
+ white-space: nowrap;
+ margin-bottom: 4px;
+}
+
+.authentication-features .feature-item:last-child {
+ margin-bottom: 0;
+}
+
+.authentication-features li.feature-item a {
+ color: var(--vscode-foreground);
+ text-decoration: none;
+}
+
+.authentication-features li.feature-item a.link:hover {
+ color: #40a9ff;
+}
+
+.authentication-features li.feature-item a.link:active {
+ color: #096dd9;
+}
+
+.authentication-features .feature-item::before {
+ content: '✓';
+ color: #32d74b;
+ margin-right: 4px;
+}
+
+.authentication-features .feature-item {
+ white-space: nowrap;
+ line-height: 20px;
+}
+
+.github1s-authorizing-dialog li.feature-item a {
+ color: var(--vscode-foreground);
+ text-decoration: none;
+}
+
+.authentication-button {
+ width: 100%;
+ height: 40px;
+ outline: none;
+ margin: 0 auto;
+ display: flex;
+ align-items: center;
+ justify-content: center;
+ background-color: #fff;
+ box-shadow: rgba(255, 255, 255, 0.2) 0px 2px 4px;
+ border: 1px solid var(--vscode-foreground);
+ border-radius: 4px;
+ font-size: 16px;
+ transition: all 0.1s;
+ cursor: pointer;
+ color: #000;
+}
+
+.authentication-button:not([disabled]):hover {
+ transform: scale(1.03);
+ box-shadow: rgba(255, 255, 255, 0.25) 0px 2px 8px;
+}
+
+.authentication-button:not([disabled]):active {
+ transform: scale(0.98);
+}
+
+.authentication-token .token-input-line {
+ display: flex;
+ margin-bottom: 6px;
+}
+
+.authentication-token .token-input-line .vscode-input {
+ flex: 1;
+ margin-right: 8px;
+}
+
+.split-line {
+ height: 0;
+ border: 1px solid var(--vscode-foreground);
+ margin: 28px 0;
+ position: relative;
+}
+
+.split-line::after {
+ content: 'OR';
+ display: block;
+ position: absolute;
+ top: 50%;
+ left: 50%;
+ transform: translateY(-50%) translateX(-50%);
+ border: 2px solid var(--vscode-foreground);
+ padding: 4px 16px;
+ border-radius: 8px;
+ background-color: var(--vscode-editor-background);
+ font-size: 12px;
+ font-weight: bold;
+}
+
+.authentication-footer {
+ margin-top: 24px;
+ color: var(--vscode-foreground);
+ width: 320px;
+}
+
+.authentication-footer > div:first-child {
+ margin-bottom: 4px;
+}
+
+.authentication-form {
+ margin: auto;
+ display: flex;
+ flex-direction: column;
+ align-items: center;
+}
+
+#app {
+ margin-top: 60px;
+}
+
+.authentication-button[disabled] .auth-button-logo {
+ opacity: .5;
+}
+
+.auth-button-logo {
+ width: 24px;
+ height: 24px;
+ display: inline-block;
+ background-size: 100% 100%;
+ background-position: 50% 50%;
+ background-repeat: no-repeat;
+ margin-right: 8px;
+}
+
+.authentication-detail {
+ display: flex;
+ flex-direction: column;
+ align-items: center;
+}
+
+.detail-title {
+ width: 320px;
+ font-size: 24px;
+ margin-bottom: 32px;
+ color: var(--vscode-button-foreground);
+}
+
+.token-status {
+ display: flex;
+ align-items: center;
+ width: 320px;
+ margin-bottom: 8px;
+}
+
+.token-status .refresh-button {
+ cursor: pointer;
+ margin-left: 6px;
+}
+
+.token-status .refresh-button:hover {
+ color: #3794ff;
+}
+
+.token-status .refresh-button:active {
+ color: #094771;
+}
+
+.login-text > span:first-child {
+ margin-right: 4px;
+}
+
+.user-avatar {
+ display: inline-block;
+ height: 14px;
+ width: 14px;
+ margin-right: 4px;
+ vertical-align: bottom;
+ border-radius: 50%;
+ background-color: #ccc;
+}
+
+.rate-limit-info {
+ width: 320px;
+ list-style: none;
+ padding-left: 0;
+}
+
+.rate-limit-info li {
+ margin-bottom: 8px;
+}
+
+.token-text {
+ width: 320px;
+ margin-bottom: 8px;
+}
+
+.token-text.token-valid {
+ color: #73c991;
+}
+
+.token-text.token-invalid {
+ color: #f88070;
+}
+
+.token-operations {
+ width: 320px;
+ margin-top: 12px;
+}
+
+.token-operations > * {
+ width: 240px;
+}
+
diff --git a/extensions/github1s/assets/pages/github1s-authentication.js b/extensions/github1s/assets/pages/github1s-authentication.js
new file mode 100644
index 000000000..af2076c28
--- /dev/null
+++ b/extensions/github1s/assets/pages/github1s-authentication.js
@@ -0,0 +1,227 @@
+import { render } from './libraries/preact.module.js';
+import { useState, useCallback, useEffect } from './libraries/preact-hooks.module.js';
+import { html, VscodeButton, VscodeInput, VscodeLoading, VscodeLink, bridgeCommands } from './components.js';
+
+const pageConfig = window.pageConfig || {};
+
+const AuthenticationFeatures = () => {
+ return html`
+
+ ${(pageConfig.authenticationFeatures || []).map(
+ (feature) =>
+ html`
+ ${feature.text}
+ `,
+ )}
+
+ `;
+};
+
+const AuthenticationButton = (props) => {
+ const [authenticating, setAuthenticating] = useState(false);
+
+ const handleButtonClick = useCallback(() => {
+ setAuthenticating(true);
+ bridgeCommands.OAuthAuthenticate().then(() => setAuthenticating(false));
+ }, []);
+
+ const buttonLogoUrl = encodeURI(`${pageConfig.extensionUri}/${pageConfig.OAuthButtonLogo}`);
+ return html`
+
+
+ ${pageConfig.OAuthButtonText}
+
+ `;
+};
+
+const ManualInputToken = () => {
+ const [inputToken, setInputToken] = useState('');
+ const [loading, setLoading] = useState(false);
+ const handleInputTokenChange = useCallback((event) => {
+ setInputToken(event.target.value);
+ }, []);
+
+ const handleSubmit = useCallback(() => {
+ if (inputToken) {
+ setLoading(true);
+ bridgeCommands.validateToken(inputToken).then((tokenStatus) => {
+ if (!tokenStatus) {
+ const messageArgs = { level: 'info', args: ['This AccessToken is invalid'] };
+ bridgeCommands.alertMessage(messageArgs);
+ setLoading(false);
+ return;
+ }
+ bridgeCommands.setToken(inputToken).then(() => setLoading(false));
+ });
+ }
+ }, [inputToken]);
+
+ return html`
+
+
+ <${VscodeInput}
+ autocomplete="off"
+ value=${inputToken}
+ disabled="${loading}"
+ onInput=${handleInputTokenChange}
+ />
+ <${VscodeButton} loading=${loading} onClick=${handleSubmit}>Submit/>
+
+ <${VscodeLink} to="${pageConfig.createTokenLink}" external>Create New AccessToken/>
+
+ `;
+};
+
+const SplitLine = () => {
+ return html`
`;
+};
+
+const AuthenticationFooter = () => {
+ return html`
+
+ `;
+};
+
+const AuthenticationForm = () => {
+ return html`
+
+ `;
+};
+
+const StatusNumberText = ({ count }) => {
+ if (count <= 0) {
+ return html`${count} `;
+ }
+ if (count <= 99) {
+ return html`${count} `;
+ }
+
+ return html`${count} `;
+};
+
+const RateLimitsInfo = ({ info }) => {
+ return html`
+
+ ------- Rate Limit Info -------
+ X-RateLimit-Limit: <${StatusNumberText} count=${info.limit} />
+ X-RateLimit-Remaining: <${StatusNumberText} count=${info.remaining} />
+ X-RateLimit-Used: ${info.used}
+ X-RateLimit-Reset: ${info.reset}
+ ------------------------------
+
+ Current rate limit window will reset after
+ ${Math.max(info.reset - Math.ceil(Date.now() / 1000), 0)}s
+
+
+ <${VscodeLink} to="${pageConfig.rateLimitDocLink}" external>${pageConfig.rateLimitDocLinkText}/>
+
+
+
`;
+};
+
+const AuthenticationDetail = ({ accessToken }) => {
+ const [tokenStatus, setTokenStatus] = useState(null);
+ const [validating, setValidating] = useState(true);
+
+ const displayToken = accessToken.slice(0, 7) + '*********************************';
+ const tokenClasses = `token-text ${validating ? '' : tokenStatus ? 'token-valid' : 'token-invalid'}`;
+
+ const handleRefreshTokenStatus = useCallback(() => {
+ setValidating(true);
+ bridgeCommands
+ .validateToken(accessToken)
+ .then((tokenStatus) => setTokenStatus(tokenStatus))
+ .finally(() => setValidating(false));
+ }, [accessToken]);
+
+ const handleClearToken = useCallback(() => {
+ const messageArgs = {
+ level: 'warning',
+ args: ['Would you want to clear the saved this AccessToken?', { modal: true }, 'Confirm'],
+ };
+ bridgeCommands.alertMessage(messageArgs).then((choose) => choose === 'Confirm' && bridgeCommands.setToken(''));
+ }, []);
+
+ useEffect(() => {
+ handleRefreshTokenStatus();
+ }, [handleRefreshTokenStatus]);
+
+ if (validating) {
+ return html`<${VscodeLoading} />`;
+ }
+
+ return html`
+
+
Authorization Detail
+
+ ${tokenStatus
+ ? html`
+
Logged in as
+ <${VscodeLink} to=${tokenStatus.profile_url} external>
+
${tokenStatus.username}
+
+
`
+ : html`
Current AccessToken is INVALID .
`}
+
↻
+
+
${displayToken}
+ ${tokenStatus && tokenStatus.rateLimits ? html`<${RateLimitsInfo} info=${tokenStatus.rateLimits} />` : null}
+
+ <${VscodeButton} size="middle" onClick=${handleClearToken}>Clear Access Token/>
+
+ <${AuthenticationFooter} />
+
+ `;
+};
+
+const App = () => {
+ const [loading, setLoading] = useState(true);
+ const [accessToken, setAccessToken] = useState('');
+ const [notice, setNotice] = useState('');
+
+ useEffect(() => {
+ bridgeCommands.getToken().then((token) => {
+ setAccessToken(token);
+ setLoading(false);
+ });
+ bridgeCommands.getNotice().then((notice) => setNotice(notice));
+ }, []);
+
+ useEffect(() => {
+ const handler = ({ data }) => {
+ if (data.type === 'token-changed') {
+ setAccessToken(data.token);
+ bridgeCommands.getNotice().then((notice) => setNotice(notice));
+ }
+ };
+ window.addEventListener('message', handler);
+ return () => window.removeEventListener('message', handler);
+ }, [setAccessToken]);
+
+ if (loading) {
+ return html`<${VscodeLoading} />`;
+ }
+
+ return html`
+
+ ${notice ? html`
${notice}
` : null}
+ <${accessToken ? AuthenticationDetail : AuthenticationForm} accessToken=${accessToken} />
+
+ `;
+};
+
+const loadingElement = document.getElementById('page-loading');
+loadingElement.parentNode.removeChild(loadingElement);
+render(html`<${App} />`, document.getElementById('app'));
diff --git a/extensions/github1s/assets/pages/github1s-settings.css b/extensions/github1s/assets/pages/github1s-settings.css
new file mode 100644
index 000000000..67003f9be
--- /dev/null
+++ b/extensions/github1s/assets/pages/github1s-settings.css
@@ -0,0 +1,82 @@
+.vscode-input {
+ margin-bottom: 10px;
+}
+
+.vscode-button {
+ margin-bottom: 10px;
+}
+
+#app {
+ padding: 10px;
+}
+
+.flex-line {
+ display: flex;
+}
+
+.flex-line > * {
+ flex: 1;
+}
+
+.page-title {
+ font-size: 16px;
+ font-weight: bold;
+ margin-bottom: 10px;
+}
+
+.page-description {
+ font-size: 12px;
+ margin-bottom: 12px;
+}
+
+.description div:last-child {
+ margin-bottom: 0;
+}
+
+.content-block {
+ margin-bottom: 8px;
+}
+
+.content-block-title {
+ font-weight: normal;
+ margin-bottom: 8px;
+}
+
+.input-token-block {
+ margin-bottom: 8px;
+}
+
+.input-token-block-title {
+ font-weight: normal;
+ margin-bottom: 8px;
+}
+
+.create-token-link {
+ margin-bottom: 10px;
+}
+
+.page-footer {
+ display: flex;
+ align-items: center;
+ margin-top: 12px;
+}
+
+.page-footer input {
+ margin-right: 4px;
+ margin-left: 0;
+}
+
+.login-text > span:first-child {
+ margin-right: 4px;
+}
+
+.user-avatar {
+ display: inline-block;
+ height: 14px;
+ width: 14px;
+ margin-right: 4px;
+ vertical-align: bottom;
+ border-radius: 50%;
+ background-color: #ccc;
+}
+
diff --git a/extensions/github1s/assets/pages/github1s-settings.js b/extensions/github1s/assets/pages/github1s-settings.js
new file mode 100644
index 000000000..a0402adaf
--- /dev/null
+++ b/extensions/github1s/assets/pages/github1s-settings.js
@@ -0,0 +1,242 @@
+import { render } from './libraries/preact.module.js';
+import { useState, useCallback, useEffect } from './libraries/preact-hooks.module.js';
+import { html, VscodeButton, VscodeInput, VscodeLink, VscodeLoading, bridgeCommands } from './components.js';
+
+const pageConfig = window.pageConfig || {};
+
+export const PageHeader = ({ title, children, ...props }) => {
+ return html`
+
${title}
+
${children}
+
`;
+};
+
+export const OAuthBlock = ({ buttonText, command, ...props }) => {
+ const [loading, setLoading] = useState(false);
+ const handleButtonClick = useCallback(() => {
+ setLoading(true);
+ bridgeCommands.OAuthAuthenticate().then(() => setLoading(false));
+ }, []);
+
+ return html`
+
+
Authenticating OAuth App
+
+ <${VscodeButton} loading=${loading} onClick=${handleButtonClick}>${buttonText}/>
+
+
+ `;
+};
+
+export const InputTokenBlock = ({ createLink, isEditing, onCancel, ...props }) => {
+ const [inputToken, setInputToken] = useState('');
+ const [loading, setLoading] = useState(false);
+ const handleInputTokenChange = useCallback((event) => {
+ setInputToken(event.target.value);
+ }, []);
+
+ const handleSubmit = useCallback(() => {
+ if (inputToken) {
+ setLoading(true);
+ bridgeCommands.validateToken(inputToken).then((tokenStatus) => {
+ if (!tokenStatus) {
+ const messageArgs = { level: 'info', args: ['This AccessToken is invalid'] };
+ bridgeCommands.alertMessage(messageArgs);
+ setLoading(false);
+ return;
+ }
+ bridgeCommands.setToken(inputToken).then(() => setLoading(false));
+ });
+ }
+ }, [inputToken]);
+
+ return html`
+
+ `;
+};
+
+export const TokenDetailBlock = ({ token, validating, onEditClick, validateToken, ...props }) => {
+ const handleValidateClick = useCallback(() => {
+ validateToken(token).then((tokenStatus) => {
+ const statusMessage = `Current AccessToken is ${tokenStatus ? 'VALID' : 'INVALID'}`;
+ const messageArgs = { level: 'info', args: [statusMessage] };
+ bridgeCommands.alertMessage(messageArgs);
+ });
+ }, [validateToken, token]);
+
+ const handleClearClick = useCallback(() => {
+ const messageArgs = {
+ level: 'warning',
+ args: ['Would you want to clear the saved this AccessToken?', { modal: true }, 'Confirm'],
+ };
+ bridgeCommands.alertMessage(messageArgs).then((choose) => choose === 'Confirm' && bridgeCommands.setToken(''));
+ }, []);
+
+ return html`
+
+
<${VscodeButton} onClick=${bridgeCommands.openDetailPage}>Detail/>
+
<${VscodeButton} loading=${validating} onClick=${handleValidateClick}>Validate/>
+
<${VscodeButton} onClick=${onEditClick}>Edit/>
+
<${VscodeButton} onClick=${handleClearClick}>Clear/>
+
+ `;
+};
+
+const PageFooter = () => {
+ const [preferSgApi, setPreferSgApi] = useState(false);
+
+ const updatePreferSgApi = useCallback(() => {
+ bridgeCommands.getPreferSgApi().then((value) => {
+ setPreferSgApi(value);
+ });
+ }, []);
+
+ const handleCheckboxChange = useCallback(
+ (event) => {
+ bridgeCommands.setPreferSgApi(event.target.checked).then(() => {
+ updatePreferSgApi();
+ });
+ },
+ [updatePreferSgApi],
+ );
+
+ useEffect(() => {
+ const handler = ({ data }) => {
+ if (data.type === 'prefer-sourcegraph-api-changed') {
+ updatePreferSgApi();
+ }
+ };
+ window.addEventListener('message', handler);
+ return () => window.removeEventListener('message', handler);
+ }, []);
+
+ useEffect(() => {
+ updatePreferSgApi();
+ }, [updatePreferSgApi]);
+
+ return html`
+
+ `;
+};
+
+const TokenEditPage = ({ token, onCancel, ...props }) => {
+ const pageDescription = (pageConfig.pageDescriptionLines || []).map((line) => html`${line}
`);
+
+ return html`
+
+ <${PageHeader} title="Set AccessToken">${pageDescription}/>
+ <${OAuthBlock} buttonText=${pageConfig.OAuthButtonText} command=${pageConfig.OAuthCommand} />
+ <${InputTokenBlock} createLink=${pageConfig.createTokenLink} isEditing=${!!token} onCancel=${onCancel} />
+
+ `;
+};
+
+const TokenDetailPage = ({ token, onEditClick, ...props }) => {
+ const [tokenStatus, setTokenStatus] = useState(null);
+ const [validating, setValidating] = useState(true);
+
+ const validateToken = useCallback((token) => {
+ setValidating(true);
+ return bridgeCommands.validateToken(token).then((tokenStatus) => {
+ setValidating(false);
+ setTokenStatus(tokenStatus);
+ return tokenStatus;
+ });
+ }, []);
+
+ const validateResult = tokenStatus
+ ? html``
+ : html`Current AccessToken is INVALID .
`;
+
+ useEffect(() => {
+ token && validateToken(token);
+ }, [token, validateToken]);
+
+ return html`
+
+ <${PageHeader} title="You have authenticated">
+ ${validating ? html`<${VscodeLoading} dots=${8} align="left" style="height: 14px" />` : validateResult}
+ />
+ <${TokenDetailBlock}
+ token=${token}
+ validating=${validating}
+ onEditClick=${onEditClick}
+ validateToken=${validateToken}
+ />
+
+ `;
+};
+
+const App = () => {
+ const [loading, setLoading] = useState(true);
+ const [pageType, setPageType] = useState('EDIT');
+ const [token, setToken] = useState('');
+
+ const switchToEdit = useCallback(() => setPageType('EDIT'), []);
+ const switchToDetail = useCallback(() => setPageType('DETAIL'), []);
+
+ useEffect(() => {
+ bridgeCommands.getToken().then((token) => {
+ setToken(token);
+ setLoading(false);
+ setPageType(token ? 'DETAIL' : 'EDIT');
+ });
+ }, []);
+
+ useEffect(() => {
+ const handler = ({ data }) => {
+ if (data.type === 'token-changed') {
+ setToken(data.token);
+ setPageType(data.token ? 'DETAIL' : 'EDIT');
+ }
+ };
+ window.addEventListener('message', handler);
+ return () => window.removeEventListener('message', handler);
+ }, []);
+
+ if (loading) {
+ return html`<${VscodeLoading} />`;
+ }
+
+ return html`
+
+ ${pageType === 'DETAIL'
+ ? html`<${TokenDetailPage} token=${token} onEditClick=${switchToEdit} />`
+ : html`<${TokenEditPage} token=${token} onCancel=${switchToDetail} />`}
+ <${PageFooter} />
+
+ `;
+};
+
+const loadingElement = document.getElementById('page-loading');
+loadingElement.parentNode.removeChild(loadingElement);
+render(html`<${App} />`, document.getElementById('app'));
diff --git a/extensions/github1s/assets/pages/libraries/htm.module.js b/extensions/github1s/assets/pages/libraries/htm.module.js
new file mode 100644
index 000000000..34902c1dc
--- /dev/null
+++ b/extensions/github1s/assets/pages/libraries/htm.module.js
@@ -0,0 +1,2 @@
+// https://github.com/developit/htm, download from https://unpkg.com/htm@3.1.0/dist/htm.module.js
+var n=function(t,s,r,e){var u;s[0]=0;for(var h=1;h=5&&((e||!n&&5===r)&&(h.push(r,0,e,s),r=6),n&&(h.push(r,n,0,s),r=6)),e=""},a=0;a"===t?(r=1,e=""):e=t+e[0]:u?t===u?u="":e+=t:'"'===t||"'"===t?u=t:">"===t?(p(),r=1):r&&("="===t?(r=5,s=e,e=""):"/"===t&&(r<5||">"===n[a][l+1])?(p(),3===r&&(h=h[0]),r=h,(h=h[0]).push(2,0,r),r=0):" "===t||"\t"===t||"\n"===t||"\r"===t?(p(),r=2):e+=t),3===r&&"!--"===e&&(r=4,h=h[0])}return p(),h}(s)),r),arguments,[])).length>1?r:r[0]}
\ No newline at end of file
diff --git a/extensions/github1s/assets/pages/libraries/preact-hooks.module.js b/extensions/github1s/assets/pages/libraries/preact-hooks.module.js
new file mode 100644
index 000000000..ab0937d9e
--- /dev/null
+++ b/extensions/github1s/assets/pages/libraries/preact-hooks.module.js
@@ -0,0 +1,2 @@
+// https://github.com/preactjs/preact, download from https://unpkg.com/preact@10.7.1/hooks/dist/hooks.module.js
+import{options as n}from"./preact.module.js";var t,u,r,o=0,i=[],c=n.__b,f=n.__r,e=n.diffed,a=n.__c,v=n.unmount;function l(t,r){n.__h&&n.__h(u,t,o||r),o=0;var i=u.__H||(u.__H={__:[],__h:[]});return t>=i.__.length&&i.__.push({}),i.__[t]}function m(n){return o=1,p(w,n)}function p(n,r,o){var i=l(t++,2);return i.t=n,i.__c||(i.__=[o?o(r):w(void 0,r),function(n){var t=i.t(i.__[0],n);i.__[0]!==t&&(i.__=[t,i.__[1]],i.__c.setState({}))}],i.__c=u),i.__}function y(r,o){var i=l(t++,3);!n.__s&&k(i.__H,o)&&(i.__=r,i.__H=o,u.__H.__h.push(i))}function d(r,o){var i=l(t++,4);!n.__s&&k(i.__H,o)&&(i.__=r,i.__H=o,u.__h.push(i))}function h(n){return o=5,_(function(){return{current:n}},[])}function s(n,t,u){o=6,d(function(){return"function"==typeof n?(n(t()),function(){return n(null)}):n?(n.current=t(),function(){return n.current=null}):void 0},null==u?u:u.concat(n))}function _(n,u){var r=l(t++,7);return k(r.__H,u)&&(r.__=n(),r.__H=u,r.__h=n),r.__}function A(n,t){return o=8,_(function(){return n},t)}function F(n){var r=u.context[n.__c],o=l(t++,9);return o.c=n,r?(null==o.__&&(o.__=!0,r.sub(u)),r.props.value):n.__}function T(t,u){n.useDebugValue&&n.useDebugValue(u?u(t):t)}function q(n){var r=l(t++,10),o=m();return r.__=n,u.componentDidCatch||(u.componentDidCatch=function(n){r.__&&r.__(n),o[1](n)}),[o[0],function(){o[1](void 0)}]}function x(){for(var t;t=i.shift();)if(t.__P)try{t.__H.__h.forEach(g),t.__H.__h.forEach(j),t.__H.__h=[]}catch(u){t.__H.__h=[],n.__e(u,t.__v)}}n.__b=function(n){u=null,c&&c(n)},n.__r=function(n){f&&f(n),t=0;var r=(u=n.__c).__H;r&&(r.__h.forEach(g),r.__h.forEach(j),r.__h=[])},n.diffed=function(t){e&&e(t);var o=t.__c;o&&o.__H&&o.__H.__h.length&&(1!==i.push(o)&&r===n.requestAnimationFrame||((r=n.requestAnimationFrame)||function(n){var t,u=function(){clearTimeout(r),b&&cancelAnimationFrame(t),setTimeout(n)},r=setTimeout(u,100);b&&(t=requestAnimationFrame(u))})(x)),u=null},n.__c=function(t,u){u.some(function(t){try{t.__h.forEach(g),t.__h=t.__h.filter(function(n){return!n.__||j(n)})}catch(r){u.some(function(n){n.__h&&(n.__h=[])}),u=[],n.__e(r,t.__v)}}),a&&a(t,u)},n.unmount=function(t){v&&v(t);var u,r=t.__c;r&&r.__H&&(r.__H.__.forEach(function(n){try{g(n)}catch(n){u=n}}),u&&n.__e(u,r.__v))};var b="function"==typeof requestAnimationFrame;function g(n){var t=u,r=n.__c;"function"==typeof r&&(n.__c=void 0,r()),u=t}function j(n){var t=u;n.__c=n.__(),u=t}function k(n,t){return!n||n.length!==t.length||t.some(function(t,u){return t!==n[u]})}function w(n,t){return"function"==typeof t?t(n):t}export{m as useState,p as useReducer,y as useEffect,d as useLayoutEffect,h as useRef,s as useImperativeHandle,_ as useMemo,A as useCallback,F as useContext,T as useDebugValue,q as useErrorBoundary};
\ No newline at end of file
diff --git a/extensions/github1s/assets/pages/libraries/preact.module.js b/extensions/github1s/assets/pages/libraries/preact.module.js
new file mode 100644
index 000000000..12a96e186
--- /dev/null
+++ b/extensions/github1s/assets/pages/libraries/preact.module.js
@@ -0,0 +1,2 @@
+// https://github.com/preactjs/preact, download from https://unpkg.com/preact@10.7.1/dist/preact.module.js
+var n,l,u,i,t,o,r,f,e={},c=[],s=/acit|ex(?:s|g|n|p|$)|rph|grid|ows|mnc|ntw|ine[ch]|zoo|^ord|itera/i;function a(n,l){for(var u in l)n[u]=l[u];return n}function h(n){var l=n.parentNode;l&&l.removeChild(n)}function v(l,u,i){var t,o,r,f={};for(r in u)"key"==r?t=u[r]:"ref"==r?o=u[r]:f[r]=u[r];if(arguments.length>2&&(f.children=arguments.length>3?n.call(arguments,2):i),"function"==typeof l&&null!=l.defaultProps)for(r in l.defaultProps)void 0===f[r]&&(f[r]=l.defaultProps[r]);return y(l,f,t,o,null)}function y(n,i,t,o,r){var f={type:n,props:i,key:t,ref:o,__k:null,__:null,__b:0,__e:null,__d:void 0,__c:null,__h:null,constructor:void 0,__v:null==r?++u:r};return null==r&&null!=l.vnode&&l.vnode(f),f}function p(){return{current:null}}function d(n){return n.children}function _(n,l){this.props=n,this.context=l}function k(n,l){if(null==l)return n.__?k(n.__,n.__.__k.indexOf(n)+1):null;for(var u;l0?y(_.type,_.props,_.key,null,_.__v):_)){if(_.__=u,_.__b=u.__b+1,null===(p=w[h])||p&&_.key==p.key&&_.type===p.type)w[h]=void 0;else for(v=0;v2&&(f.children=arguments.length>3?n.call(arguments,2):i),y(l.type,f,t||l.key,o||l.ref,null)}function D(n,l){var u={__c:l="__cC"+f++,__:n,Consumer:function(n,l){return n.children(l)},Provider:function(n){var u,i;return this.getChildContext||(u=[],(i={})[l]=this,this.getChildContext=function(){return i},this.shouldComponentUpdate=function(n){this.props.value!==n.value&&u.some(m)},this.sub=function(n){u.push(n);var l=n.componentWillUnmount;n.componentWillUnmount=function(){u.splice(u.indexOf(n),1),l&&l.call(n)}}),n.children}};return u.Provider.__=u.Consumer.contextType=u}n=c.slice,l={__e:function(n,l,u,i){for(var t,o,r;l=l.__;)if((t=l.__c)&&!t.__)try{if((o=t.constructor)&&null!=o.getDerivedStateFromError&&(t.setState(o.getDerivedStateFromError(n)),r=t.__d),null!=t.componentDidCatch&&(t.componentDidCatch(n,i||{}),r=t.__d),r)return t.__E=t}catch(l){n=l}throw n}},u=0,i=function(n){return null!=n&&void 0===n.constructor},_.prototype.setState=function(n,l){var u;u=null!=this.__s&&this.__s!==this.state?this.__s:this.__s=a({},this.state),"function"==typeof n&&(n=n(a({},u),this.props)),n&&a(u,n),null!=n&&this.__v&&(l&&this.__h.push(l),m(this))},_.prototype.forceUpdate=function(n){this.__v&&(this.__e=!0,n&&this.__h.push(n),m(this))},_.prototype.render=d,t=[],o="function"==typeof Promise?Promise.prototype.then.bind(Promise.resolve()):setTimeout,g.__r=0,f=0;export{S as render,q as hydrate,v as createElement,v as h,d as Fragment,p as createRef,i as isValidElement,_ as Component,B as cloneElement,D as createContext,A as toChildArray,l as options};
\ No newline at end of file
diff --git a/extensions/github1s/assets/github1s.svg b/extensions/github1s/assets/settings.svg
similarity index 100%
rename from extensions/github1s/assets/github1s.svg
rename to extensions/github1s/assets/settings.svg
diff --git a/extensions/github1s/package-lock.json b/extensions/github1s/package-lock.json
new file mode 100644
index 000000000..50b96716c
--- /dev/null
+++ b/extensions/github1s/package-lock.json
@@ -0,0 +1,3768 @@
+{
+ "name": "github1s",
+ "version": "0.0.0",
+ "lockfileVersion": 3,
+ "requires": true,
+ "packages": {
+ "": {
+ "name": "github1s",
+ "version": "0.0.0",
+ "license": "MIT",
+ "dependencies": {
+ "@apollo/client": "^3.12.5",
+ "@octokit/core": "^6.1.4",
+ "dayjs": "^1.11.1",
+ "graphql": "^16.8.1",
+ "history": "^5.3.0",
+ "js-base64": "^3.7.2",
+ "json-stable-stringify": "^1.0.1",
+ "match-sorter": "^6.3.1",
+ "p-finally": "^3.0.0",
+ "process": "^0.11.10",
+ "query-string": "^9.5.0"
+ },
+ "devDependencies": {
+ "@types/vscode": "^1.96.0",
+ "node-polyfill-webpack-plugin": "^1.1.4",
+ "ts-loader": "^9.2.8",
+ "typescript": "^5.7.3",
+ "webpack": "^5.105.0",
+ "webpack-cli": "^4.9.2"
+ },
+ "engines": {
+ "vscode": "^1.48.0"
+ }
+ },
+ "node_modules/@apollo/client": {
+ "version": "3.12.5",
+ "resolved": "https://registry.npmjs.org/@apollo/client/-/client-3.12.5.tgz",
+ "integrity": "sha512-lOE2TlHx1el4rHs8vaTE4IroyIO9/PD2w598YYiDahF0XSMDdsXMrTpOVh+FuQ6tZ+DXT+hsaMlilZqcFRgu+A==",
+ "license": "MIT",
+ "dependencies": {
+ "@graphql-typed-document-node/core": "^3.1.1",
+ "@wry/caches": "^1.0.0",
+ "@wry/equality": "^0.5.6",
+ "@wry/trie": "^0.5.0",
+ "graphql-tag": "^2.12.6",
+ "hoist-non-react-statics": "^3.3.2",
+ "optimism": "^0.18.0",
+ "prop-types": "^15.7.2",
+ "rehackt": "^0.1.0",
+ "response-iterator": "^0.2.6",
+ "symbol-observable": "^4.0.0",
+ "ts-invariant": "^0.10.3",
+ "tslib": "^2.3.0",
+ "zen-observable-ts": "^1.2.5"
+ },
+ "peerDependencies": {
+ "graphql": "^15.0.0 || ^16.0.0",
+ "graphql-ws": "^5.5.5",
+ "react": "^16.8.0 || ^17.0.0 || ^18.0.0 || >=19.0.0-rc",
+ "react-dom": "^16.8.0 || ^17.0.0 || ^18.0.0 || >=19.0.0-rc",
+ "subscriptions-transport-ws": "^0.9.0 || ^0.11.0"
+ },
+ "peerDependenciesMeta": {
+ "graphql-ws": {
+ "optional": true
+ },
+ "react": {
+ "optional": true
+ },
+ "react-dom": {
+ "optional": true
+ },
+ "subscriptions-transport-ws": {
+ "optional": true
+ }
+ }
+ },
+ "node_modules/@babel/runtime": {
+ "version": "7.27.0",
+ "resolved": "https://registry.npmjs.org/@babel/runtime/-/runtime-7.27.0.tgz",
+ "integrity": "sha512-VtPOkrdPHZsKc/clNqyi9WUA8TINkZ4cGk63UUE3u4pmB2k+ZMQRDuIOagv8UVd6j7k0T3+RRIb7beKTebNbcw==",
+ "license": "MIT",
+ "dependencies": {
+ "regenerator-runtime": "^0.14.0"
+ },
+ "engines": {
+ "node": ">=6.9.0"
+ }
+ },
+ "node_modules/@discoveryjs/json-ext": {
+ "version": "0.5.7",
+ "resolved": "https://registry.npmjs.org/@discoveryjs/json-ext/-/json-ext-0.5.7.tgz",
+ "integrity": "sha512-dBVuXR082gk3jsFp7Rd/JI4kytwGHecnCoTtXFb7DB6CNHp4rg5k1bhg0nWdLGLnOV71lmDzGQaLMy8iPLY0pw==",
+ "dev": true,
+ "license": "MIT",
+ "engines": {
+ "node": ">=10.0.0"
+ }
+ },
+ "node_modules/@graphql-typed-document-node/core": {
+ "version": "3.2.0",
+ "resolved": "https://registry.npmjs.org/@graphql-typed-document-node/core/-/core-3.2.0.tgz",
+ "integrity": "sha512-mB9oAsNCm9aM3/SOv4YtBMqZbYj10R7dkq8byBqxGY/ncFwhf2oQzMV+LCRlWoDSEBJ3COiR1yeDvMtsoOsuFQ==",
+ "license": "MIT",
+ "peerDependencies": {
+ "graphql": "^0.8.0 || ^0.9.0 || ^0.10.0 || ^0.11.0 || ^0.12.0 || ^0.13.0 || ^14.0.0 || ^15.0.0 || ^16.0.0 || ^17.0.0"
+ }
+ },
+ "node_modules/@jridgewell/gen-mapping": {
+ "version": "0.3.13",
+ "resolved": "https://registry.npmjs.org/@jridgewell/gen-mapping/-/gen-mapping-0.3.13.tgz",
+ "integrity": "sha512-2kkt/7niJ6MgEPxF0bYdQ6etZaA+fQvDcLKckhy1yIQOzaoKjBBjSj63/aLVjYE3qhRt5dvM+uUyfCg6UKCBbA==",
+ "dev": true,
+ "license": "MIT",
+ "dependencies": {
+ "@jridgewell/sourcemap-codec": "^1.5.0",
+ "@jridgewell/trace-mapping": "^0.3.24"
+ }
+ },
+ "node_modules/@jridgewell/resolve-uri": {
+ "version": "3.1.2",
+ "resolved": "https://registry.npmjs.org/@jridgewell/resolve-uri/-/resolve-uri-3.1.2.tgz",
+ "integrity": "sha512-bRISgCIjP20/tbWSPWMEi54QVPRZExkuD9lJL+UIxUKtwVJA8wW1Trb1jMs1RFXo1CBTNZ/5hpC9QvmKWdopKw==",
+ "dev": true,
+ "license": "MIT",
+ "engines": {
+ "node": ">=6.0.0"
+ }
+ },
+ "node_modules/@jridgewell/source-map": {
+ "version": "0.3.11",
+ "resolved": "https://registry.npmjs.org/@jridgewell/source-map/-/source-map-0.3.11.tgz",
+ "integrity": "sha512-ZMp1V8ZFcPG5dIWnQLr3NSI1MiCU7UETdS/A0G8V/XWHvJv3ZsFqutJn1Y5RPmAPX6F3BiE397OqveU/9NCuIA==",
+ "dev": true,
+ "license": "MIT",
+ "dependencies": {
+ "@jridgewell/gen-mapping": "^0.3.5",
+ "@jridgewell/trace-mapping": "^0.3.25"
+ }
+ },
+ "node_modules/@jridgewell/sourcemap-codec": {
+ "version": "1.5.5",
+ "resolved": "https://registry.npmjs.org/@jridgewell/sourcemap-codec/-/sourcemap-codec-1.5.5.tgz",
+ "integrity": "sha512-cYQ9310grqxueWbl+WuIUIaiUaDcj7WOq5fVhEljNVgRfOUhY9fy2zTvfoqWsnebh8Sl70VScFbICvJnLKB0Og==",
+ "dev": true,
+ "license": "MIT"
+ },
+ "node_modules/@jridgewell/trace-mapping": {
+ "version": "0.3.31",
+ "resolved": "https://registry.npmjs.org/@jridgewell/trace-mapping/-/trace-mapping-0.3.31.tgz",
+ "integrity": "sha512-zzNR+SdQSDJzc8joaeP8QQoCQr8NuYx2dIIytl1QeBEZHJ9uW6hebsrYgbz8hJwUQao3TWCMtmfV8Nu1twOLAw==",
+ "dev": true,
+ "license": "MIT",
+ "dependencies": {
+ "@jridgewell/resolve-uri": "^3.1.0",
+ "@jridgewell/sourcemap-codec": "^1.4.14"
+ }
+ },
+ "node_modules/@octokit/auth-token": {
+ "version": "5.1.2",
+ "resolved": "https://registry.npmjs.org/@octokit/auth-token/-/auth-token-5.1.2.tgz",
+ "integrity": "sha512-JcQDsBdg49Yky2w2ld20IHAlwr8d/d8N6NiOXbtuoPCqzbsiJgF633mVUw3x4mo0H5ypataQIX7SFu3yy44Mpw==",
+ "license": "MIT",
+ "engines": {
+ "node": ">= 18"
+ }
+ },
+ "node_modules/@octokit/core": {
+ "version": "6.1.4",
+ "resolved": "https://registry.npmjs.org/@octokit/core/-/core-6.1.4.tgz",
+ "integrity": "sha512-lAS9k7d6I0MPN+gb9bKDt7X8SdxknYqAMh44S5L+lNqIN2NuV8nvv3g8rPp7MuRxcOpxpUIATWprO0C34a8Qmg==",
+ "license": "MIT",
+ "dependencies": {
+ "@octokit/auth-token": "^5.0.0",
+ "@octokit/graphql": "^8.1.2",
+ "@octokit/request": "^9.2.1",
+ "@octokit/request-error": "^6.1.7",
+ "@octokit/types": "^13.6.2",
+ "before-after-hook": "^3.0.2",
+ "universal-user-agent": "^7.0.0"
+ },
+ "engines": {
+ "node": ">= 18"
+ }
+ },
+ "node_modules/@octokit/endpoint": {
+ "version": "10.1.3",
+ "resolved": "https://registry.npmjs.org/@octokit/endpoint/-/endpoint-10.1.3.tgz",
+ "integrity": "sha512-nBRBMpKPhQUxCsQQeW+rCJ/OPSMcj3g0nfHn01zGYZXuNDvvXudF/TYY6APj5THlurerpFN4a/dQAIAaM6BYhA==",
+ "license": "MIT",
+ "dependencies": {
+ "@octokit/types": "^13.6.2",
+ "universal-user-agent": "^7.0.2"
+ },
+ "engines": {
+ "node": ">= 18"
+ }
+ },
+ "node_modules/@octokit/graphql": {
+ "version": "8.2.0",
+ "resolved": "https://registry.npmjs.org/@octokit/graphql/-/graphql-8.2.0.tgz",
+ "integrity": "sha512-gejfDywEml/45SqbWTWrhfwvLBrcGYhOn50sPOjIeVvH6i7D16/9xcFA8dAJNp2HMcd+g4vru41g4E2RBiZvfQ==",
+ "license": "MIT",
+ "dependencies": {
+ "@octokit/request": "^9.1.4",
+ "@octokit/types": "^13.8.0",
+ "universal-user-agent": "^7.0.0"
+ },
+ "engines": {
+ "node": ">= 18"
+ }
+ },
+ "node_modules/@octokit/openapi-types": {
+ "version": "23.0.1",
+ "resolved": "https://registry.npmjs.org/@octokit/openapi-types/-/openapi-types-23.0.1.tgz",
+ "integrity": "sha512-izFjMJ1sir0jn0ldEKhZ7xegCTj/ObmEDlEfpFrx4k/JyZSMRHbO3/rBwgE7f3m2DHt+RrNGIVw4wSmwnm3t/g==",
+ "license": "MIT"
+ },
+ "node_modules/@octokit/request": {
+ "version": "9.2.1",
+ "resolved": "https://registry.npmjs.org/@octokit/request/-/request-9.2.1.tgz",
+ "integrity": "sha512-TqHLIdw1KFvx8WvLc7Jv94r3C3+AzKY2FWq7c20zvrxmCIa6MCVkLCE/826NCXnml3LFJjLsidDh1BhMaGEDQw==",
+ "license": "MIT",
+ "dependencies": {
+ "@octokit/endpoint": "^10.1.3",
+ "@octokit/request-error": "^6.1.6",
+ "@octokit/types": "^13.6.2",
+ "fast-content-type-parse": "^2.0.0",
+ "universal-user-agent": "^7.0.2"
+ },
+ "engines": {
+ "node": ">= 18"
+ }
+ },
+ "node_modules/@octokit/request-error": {
+ "version": "6.1.7",
+ "resolved": "https://registry.npmjs.org/@octokit/request-error/-/request-error-6.1.7.tgz",
+ "integrity": "sha512-69NIppAwaauwZv6aOzb+VVLwt+0havz9GT5YplkeJv7fG7a40qpLt/yZKyiDxAhgz0EtgNdNcb96Z0u+Zyuy2g==",
+ "license": "MIT",
+ "dependencies": {
+ "@octokit/types": "^13.6.2"
+ },
+ "engines": {
+ "node": ">= 18"
+ }
+ },
+ "node_modules/@octokit/types": {
+ "version": "13.8.0",
+ "resolved": "https://registry.npmjs.org/@octokit/types/-/types-13.8.0.tgz",
+ "integrity": "sha512-x7DjTIbEpEWXK99DMd01QfWy0hd5h4EN+Q7shkdKds3otGQP+oWE/y0A76i1OvH9fygo4ddvNf7ZvF0t78P98A==",
+ "license": "MIT",
+ "dependencies": {
+ "@octokit/openapi-types": "^23.0.1"
+ }
+ },
+ "node_modules/@types/eslint": {
+ "version": "9.6.1",
+ "resolved": "https://registry.npmjs.org/@types/eslint/-/eslint-9.6.1.tgz",
+ "integrity": "sha512-FXx2pKgId/WyYo2jXw63kk7/+TY7u7AziEJxJAnSFzHlqTAS3Ync6SvgYAN/k4/PQpnnVuzoMuVnByKK2qp0ag==",
+ "dev": true,
+ "dependencies": {
+ "@types/estree": "*",
+ "@types/json-schema": "*"
+ }
+ },
+ "node_modules/@types/eslint-scope": {
+ "version": "3.7.7",
+ "resolved": "https://registry.npmjs.org/@types/eslint-scope/-/eslint-scope-3.7.7.tgz",
+ "integrity": "sha512-MzMFlSLBqNF2gcHWO0G1vP/YQyfvrxZ0bF+u7mzUdZ1/xK4A4sru+nraZz5i3iEIk1l1uyicaDVTB4QbbEkAYg==",
+ "dev": true,
+ "dependencies": {
+ "@types/eslint": "*",
+ "@types/estree": "*"
+ }
+ },
+ "node_modules/@types/estree": {
+ "version": "1.0.8",
+ "resolved": "https://registry.npmjs.org/@types/estree/-/estree-1.0.8.tgz",
+ "integrity": "sha512-dWHzHa2WqEXI/O1E9OjrocMTKJl2mSrEolh1Iomrv6U+JuNwaHXsXx9bLu5gG7BUWFIN0skIQJQ/L1rIex4X6w==",
+ "dev": true,
+ "license": "MIT"
+ },
+ "node_modules/@types/json-schema": {
+ "version": "7.0.15",
+ "resolved": "https://registry.npmjs.org/@types/json-schema/-/json-schema-7.0.15.tgz",
+ "integrity": "sha512-5+fP8P8MFNC+AyZCDxrB2pkZFPGzqQWUzpSeuuVLvm8VMcorNYavBqoFcxK8bQz4Qsbn4oUEEem4wDLfcysGHA==",
+ "dev": true
+ },
+ "node_modules/@types/node": {
+ "version": "25.2.1",
+ "resolved": "https://registry.npmjs.org/@types/node/-/node-25.2.1.tgz",
+ "integrity": "sha512-CPrnr8voK8vC6eEtyRzvMpgp3VyVRhgclonE7qYi6P9sXwYb59ucfrnmFBTaP0yUi8Gk4yZg/LlTJULGxvTNsg==",
+ "dev": true,
+ "license": "MIT",
+ "dependencies": {
+ "undici-types": "~7.16.0"
+ }
+ },
+ "node_modules/@types/vscode": {
+ "version": "1.96.0",
+ "resolved": "https://registry.npmjs.org/@types/vscode/-/vscode-1.96.0.tgz",
+ "integrity": "sha512-qvZbSZo+K4ZYmmDuaodMbAa67Pl6VDQzLKFka6rq+3WUTY4Kro7Bwoi0CuZLO/wema0ygcmpwow7zZfPJTs5jg==",
+ "dev": true,
+ "license": "MIT"
+ },
+ "node_modules/@webassemblyjs/ast": {
+ "version": "1.14.1",
+ "resolved": "https://registry.npmjs.org/@webassemblyjs/ast/-/ast-1.14.1.tgz",
+ "integrity": "sha512-nuBEDgQfm1ccRp/8bCQrx1frohyufl4JlbMMZ4P1wpeOfDhF6FQkxZJ1b/e+PLwr6X1Nhw6OLme5usuBWYBvuQ==",
+ "dev": true,
+ "dependencies": {
+ "@webassemblyjs/helper-numbers": "1.13.2",
+ "@webassemblyjs/helper-wasm-bytecode": "1.13.2"
+ }
+ },
+ "node_modules/@webassemblyjs/floating-point-hex-parser": {
+ "version": "1.13.2",
+ "resolved": "https://registry.npmjs.org/@webassemblyjs/floating-point-hex-parser/-/floating-point-hex-parser-1.13.2.tgz",
+ "integrity": "sha512-6oXyTOzbKxGH4steLbLNOu71Oj+C8Lg34n6CqRvqfS2O71BxY6ByfMDRhBytzknj9yGUPVJ1qIKhRlAwO1AovA==",
+ "dev": true
+ },
+ "node_modules/@webassemblyjs/helper-api-error": {
+ "version": "1.13.2",
+ "resolved": "https://registry.npmjs.org/@webassemblyjs/helper-api-error/-/helper-api-error-1.13.2.tgz",
+ "integrity": "sha512-U56GMYxy4ZQCbDZd6JuvvNV/WFildOjsaWD3Tzzvmw/mas3cXzRJPMjP83JqEsgSbyrmaGjBfDtV7KDXV9UzFQ==",
+ "dev": true
+ },
+ "node_modules/@webassemblyjs/helper-buffer": {
+ "version": "1.14.1",
+ "resolved": "https://registry.npmjs.org/@webassemblyjs/helper-buffer/-/helper-buffer-1.14.1.tgz",
+ "integrity": "sha512-jyH7wtcHiKssDtFPRB+iQdxlDf96m0E39yb0k5uJVhFGleZFoNw1c4aeIcVUPPbXUVJ94wwnMOAqUHyzoEPVMA==",
+ "dev": true
+ },
+ "node_modules/@webassemblyjs/helper-numbers": {
+ "version": "1.13.2",
+ "resolved": "https://registry.npmjs.org/@webassemblyjs/helper-numbers/-/helper-numbers-1.13.2.tgz",
+ "integrity": "sha512-FE8aCmS5Q6eQYcV3gI35O4J789wlQA+7JrqTTpJqn5emA4U2hvwJmvFRC0HODS+3Ye6WioDklgd6scJ3+PLnEA==",
+ "dev": true,
+ "dependencies": {
+ "@webassemblyjs/floating-point-hex-parser": "1.13.2",
+ "@webassemblyjs/helper-api-error": "1.13.2",
+ "@xtuc/long": "4.2.2"
+ }
+ },
+ "node_modules/@webassemblyjs/helper-wasm-bytecode": {
+ "version": "1.13.2",
+ "resolved": "https://registry.npmjs.org/@webassemblyjs/helper-wasm-bytecode/-/helper-wasm-bytecode-1.13.2.tgz",
+ "integrity": "sha512-3QbLKy93F0EAIXLh0ogEVR6rOubA9AoZ+WRYhNbFyuB70j3dRdwH9g+qXhLAO0kiYGlg3TxDV+I4rQTr/YNXkA==",
+ "dev": true
+ },
+ "node_modules/@webassemblyjs/helper-wasm-section": {
+ "version": "1.14.1",
+ "resolved": "https://registry.npmjs.org/@webassemblyjs/helper-wasm-section/-/helper-wasm-section-1.14.1.tgz",
+ "integrity": "sha512-ds5mXEqTJ6oxRoqjhWDU83OgzAYjwsCV8Lo/N+oRsNDmx/ZDpqalmrtgOMkHwxsG0iI//3BwWAErYRHtgn0dZw==",
+ "dev": true,
+ "dependencies": {
+ "@webassemblyjs/ast": "1.14.1",
+ "@webassemblyjs/helper-buffer": "1.14.1",
+ "@webassemblyjs/helper-wasm-bytecode": "1.13.2",
+ "@webassemblyjs/wasm-gen": "1.14.1"
+ }
+ },
+ "node_modules/@webassemblyjs/ieee754": {
+ "version": "1.13.2",
+ "resolved": "https://registry.npmjs.org/@webassemblyjs/ieee754/-/ieee754-1.13.2.tgz",
+ "integrity": "sha512-4LtOzh58S/5lX4ITKxnAK2USuNEvpdVV9AlgGQb8rJDHaLeHciwG4zlGr0j/SNWlr7x3vO1lDEsuePvtcDNCkw==",
+ "dev": true,
+ "dependencies": {
+ "@xtuc/ieee754": "^1.2.0"
+ }
+ },
+ "node_modules/@webassemblyjs/leb128": {
+ "version": "1.13.2",
+ "resolved": "https://registry.npmjs.org/@webassemblyjs/leb128/-/leb128-1.13.2.tgz",
+ "integrity": "sha512-Lde1oNoIdzVzdkNEAWZ1dZ5orIbff80YPdHx20mrHwHrVNNTjNr8E3xz9BdpcGqRQbAEa+fkrCb+fRFTl/6sQw==",
+ "dev": true,
+ "dependencies": {
+ "@xtuc/long": "4.2.2"
+ }
+ },
+ "node_modules/@webassemblyjs/utf8": {
+ "version": "1.13.2",
+ "resolved": "https://registry.npmjs.org/@webassemblyjs/utf8/-/utf8-1.13.2.tgz",
+ "integrity": "sha512-3NQWGjKTASY1xV5m7Hr0iPeXD9+RDobLll3T9d2AO+g3my8xy5peVyjSag4I50mR1bBSN/Ct12lo+R9tJk0NZQ==",
+ "dev": true
+ },
+ "node_modules/@webassemblyjs/wasm-edit": {
+ "version": "1.14.1",
+ "resolved": "https://registry.npmjs.org/@webassemblyjs/wasm-edit/-/wasm-edit-1.14.1.tgz",
+ "integrity": "sha512-RNJUIQH/J8iA/1NzlE4N7KtyZNHi3w7at7hDjvRNm5rcUXa00z1vRz3glZoULfJ5mpvYhLybmVcwcjGrC1pRrQ==",
+ "dev": true,
+ "dependencies": {
+ "@webassemblyjs/ast": "1.14.1",
+ "@webassemblyjs/helper-buffer": "1.14.1",
+ "@webassemblyjs/helper-wasm-bytecode": "1.13.2",
+ "@webassemblyjs/helper-wasm-section": "1.14.1",
+ "@webassemblyjs/wasm-gen": "1.14.1",
+ "@webassemblyjs/wasm-opt": "1.14.1",
+ "@webassemblyjs/wasm-parser": "1.14.1",
+ "@webassemblyjs/wast-printer": "1.14.1"
+ }
+ },
+ "node_modules/@webassemblyjs/wasm-gen": {
+ "version": "1.14.1",
+ "resolved": "https://registry.npmjs.org/@webassemblyjs/wasm-gen/-/wasm-gen-1.14.1.tgz",
+ "integrity": "sha512-AmomSIjP8ZbfGQhumkNvgC33AY7qtMCXnN6bL2u2Js4gVCg8fp735aEiMSBbDR7UQIj90n4wKAFUSEd0QN2Ukg==",
+ "dev": true,
+ "dependencies": {
+ "@webassemblyjs/ast": "1.14.1",
+ "@webassemblyjs/helper-wasm-bytecode": "1.13.2",
+ "@webassemblyjs/ieee754": "1.13.2",
+ "@webassemblyjs/leb128": "1.13.2",
+ "@webassemblyjs/utf8": "1.13.2"
+ }
+ },
+ "node_modules/@webassemblyjs/wasm-opt": {
+ "version": "1.14.1",
+ "resolved": "https://registry.npmjs.org/@webassemblyjs/wasm-opt/-/wasm-opt-1.14.1.tgz",
+ "integrity": "sha512-PTcKLUNvBqnY2U6E5bdOQcSM+oVP/PmrDY9NzowJjislEjwP/C4an2303MCVS2Mg9d3AJpIGdUFIQQWbPds0Sw==",
+ "dev": true,
+ "dependencies": {
+ "@webassemblyjs/ast": "1.14.1",
+ "@webassemblyjs/helper-buffer": "1.14.1",
+ "@webassemblyjs/wasm-gen": "1.14.1",
+ "@webassemblyjs/wasm-parser": "1.14.1"
+ }
+ },
+ "node_modules/@webassemblyjs/wasm-parser": {
+ "version": "1.14.1",
+ "resolved": "https://registry.npmjs.org/@webassemblyjs/wasm-parser/-/wasm-parser-1.14.1.tgz",
+ "integrity": "sha512-JLBl+KZ0R5qB7mCnud/yyX08jWFw5MsoalJ1pQ4EdFlgj9VdXKGuENGsiCIjegI1W7p91rUlcB/LB5yRJKNTcQ==",
+ "dev": true,
+ "dependencies": {
+ "@webassemblyjs/ast": "1.14.1",
+ "@webassemblyjs/helper-api-error": "1.13.2",
+ "@webassemblyjs/helper-wasm-bytecode": "1.13.2",
+ "@webassemblyjs/ieee754": "1.13.2",
+ "@webassemblyjs/leb128": "1.13.2",
+ "@webassemblyjs/utf8": "1.13.2"
+ }
+ },
+ "node_modules/@webassemblyjs/wast-printer": {
+ "version": "1.14.1",
+ "resolved": "https://registry.npmjs.org/@webassemblyjs/wast-printer/-/wast-printer-1.14.1.tgz",
+ "integrity": "sha512-kPSSXE6De1XOR820C90RIo2ogvZG+c3KiHzqUoO/F34Y2shGzesfqv7o57xrxovZJH/MetF5UjroJ/R/3isoiw==",
+ "dev": true,
+ "dependencies": {
+ "@webassemblyjs/ast": "1.14.1",
+ "@xtuc/long": "4.2.2"
+ }
+ },
+ "node_modules/@webpack-cli/configtest": {
+ "version": "1.1.1",
+ "resolved": "https://registry.npmjs.org/@webpack-cli/configtest/-/configtest-1.1.1.tgz",
+ "integrity": "sha512-1FBc1f9G4P/AxMqIgfZgeOTuRnwZMten8E7zap5zgpPInnCrP8D4Q81+4CWIch8i/Nf7nXjP0v6CjjbHOrXhKg==",
+ "dev": true,
+ "license": "MIT",
+ "peerDependencies": {
+ "webpack": "4.x.x || 5.x.x",
+ "webpack-cli": "4.x.x"
+ }
+ },
+ "node_modules/@webpack-cli/info": {
+ "version": "1.4.1",
+ "resolved": "https://registry.npmjs.org/@webpack-cli/info/-/info-1.4.1.tgz",
+ "integrity": "sha512-PKVGmazEq3oAo46Q63tpMr4HipI3OPfP7LiNOEJg963RMgT0rqheag28NCML0o3GIzA3DmxP1ZIAv9oTX1CUIA==",
+ "dev": true,
+ "license": "MIT",
+ "dependencies": {
+ "envinfo": "^7.7.3"
+ },
+ "peerDependencies": {
+ "webpack-cli": "4.x.x"
+ }
+ },
+ "node_modules/@webpack-cli/serve": {
+ "version": "1.6.1",
+ "resolved": "https://registry.npmjs.org/@webpack-cli/serve/-/serve-1.6.1.tgz",
+ "integrity": "sha512-gNGTiTrjEVQ0OcVnzsRSqTxaBSr+dmTfm+qJsCDluky8uhdLWep7Gcr62QsAKHTMxjCS/8nEITsmFAhfIx+QSw==",
+ "dev": true,
+ "license": "MIT",
+ "peerDependencies": {
+ "webpack-cli": "4.x.x"
+ },
+ "peerDependenciesMeta": {
+ "webpack-dev-server": {
+ "optional": true
+ }
+ }
+ },
+ "node_modules/@wry/caches": {
+ "version": "1.0.1",
+ "resolved": "https://registry.npmjs.org/@wry/caches/-/caches-1.0.1.tgz",
+ "integrity": "sha512-bXuaUNLVVkD20wcGBWRyo7j9N3TxePEWFZj2Y+r9OoUzfqmavM84+mFykRicNsBqatba5JLay1t48wxaXaWnlA==",
+ "license": "MIT",
+ "dependencies": {
+ "tslib": "^2.3.0"
+ },
+ "engines": {
+ "node": ">=8"
+ }
+ },
+ "node_modules/@wry/context": {
+ "version": "0.7.4",
+ "resolved": "https://registry.npmjs.org/@wry/context/-/context-0.7.4.tgz",
+ "integrity": "sha512-jmT7Sb4ZQWI5iyu3lobQxICu2nC/vbUhP0vIdd6tHC9PTfenmRmuIFqktc6GH9cgi+ZHnsLWPvfSvc4DrYmKiQ==",
+ "license": "MIT",
+ "dependencies": {
+ "tslib": "^2.3.0"
+ },
+ "engines": {
+ "node": ">=8"
+ }
+ },
+ "node_modules/@wry/equality": {
+ "version": "0.5.7",
+ "resolved": "https://registry.npmjs.org/@wry/equality/-/equality-0.5.7.tgz",
+ "integrity": "sha512-BRFORjsTuQv5gxcXsuDXx6oGRhuVsEGwZy6LOzRRfgu+eSfxbhUQ9L9YtSEIuIjY/o7g3iWFjrc5eSY1GXP2Dw==",
+ "license": "MIT",
+ "dependencies": {
+ "tslib": "^2.3.0"
+ },
+ "engines": {
+ "node": ">=8"
+ }
+ },
+ "node_modules/@wry/trie": {
+ "version": "0.5.0",
+ "resolved": "https://registry.npmjs.org/@wry/trie/-/trie-0.5.0.tgz",
+ "integrity": "sha512-FNoYzHawTMk/6KMQoEG5O4PuioX19UbwdQKF44yw0nLfOypfQdjtfZzo/UIJWAJ23sNIFbD1Ug9lbaDGMwbqQA==",
+ "license": "MIT",
+ "dependencies": {
+ "tslib": "^2.3.0"
+ },
+ "engines": {
+ "node": ">=8"
+ }
+ },
+ "node_modules/@xtuc/ieee754": {
+ "version": "1.2.0",
+ "resolved": "https://registry.npmjs.org/@xtuc/ieee754/-/ieee754-1.2.0.tgz",
+ "integrity": "sha512-DX8nKgqcGwsc0eJSqYt5lwP4DH5FlHnmuWWBRy7X0NcaGR0ZtuyeESgMwTYVEtxmsNGY+qit4QYT/MIYTOTPeA==",
+ "dev": true
+ },
+ "node_modules/@xtuc/long": {
+ "version": "4.2.2",
+ "resolved": "https://registry.npmjs.org/@xtuc/long/-/long-4.2.2.tgz",
+ "integrity": "sha512-NuHqBY1PB/D8xU6s/thBgOAiAP7HOYDQ32+BFZILJ8ivkUkAHQnWfn6WhL79Owj1qmUnoN/YPhktdIoucipkAQ==",
+ "dev": true
+ },
+ "node_modules/acorn": {
+ "version": "8.15.0",
+ "resolved": "https://registry.npmjs.org/acorn/-/acorn-8.15.0.tgz",
+ "integrity": "sha512-NZyJarBfL7nWwIq+FDL6Zp/yHEhePMNnnJ0y3qfieCrmNvYct8uvtiV41UvlSe6apAfk0fY1FbWx+NwfmpvtTg==",
+ "dev": true,
+ "license": "MIT",
+ "bin": {
+ "acorn": "bin/acorn"
+ },
+ "engines": {
+ "node": ">=0.4.0"
+ }
+ },
+ "node_modules/acorn-import-phases": {
+ "version": "1.0.4",
+ "resolved": "https://registry.npmjs.org/acorn-import-phases/-/acorn-import-phases-1.0.4.tgz",
+ "integrity": "sha512-wKmbr/DDiIXzEOiWrTTUcDm24kQ2vGfZQvM2fwg2vXqR5uW6aapr7ObPtj1th32b9u90/Pf4AItvdTh42fBmVQ==",
+ "dev": true,
+ "license": "MIT",
+ "engines": {
+ "node": ">=10.13.0"
+ },
+ "peerDependencies": {
+ "acorn": "^8.14.0"
+ }
+ },
+ "node_modules/ajv": {
+ "version": "8.17.1",
+ "resolved": "https://registry.npmjs.org/ajv/-/ajv-8.17.1.tgz",
+ "integrity": "sha512-B/gBuNg5SiMTrPkC+A2+cW0RszwxYmn6VYxB/inlBStS5nx6xHIt/ehKRhIMhqusl7a8LjQoZnjCs5vhwxOQ1g==",
+ "dev": true,
+ "license": "MIT",
+ "dependencies": {
+ "fast-deep-equal": "^3.1.3",
+ "fast-uri": "^3.0.1",
+ "json-schema-traverse": "^1.0.0",
+ "require-from-string": "^2.0.2"
+ },
+ "funding": {
+ "type": "github",
+ "url": "https://github.com/sponsors/epoberezkin"
+ }
+ },
+ "node_modules/ajv-formats": {
+ "version": "2.1.1",
+ "resolved": "https://registry.npmjs.org/ajv-formats/-/ajv-formats-2.1.1.tgz",
+ "integrity": "sha512-Wx0Kx52hxE7C18hkMEggYlEifqWZtYaRgouJor+WMdPnQyEK13vgEWyVNup7SoeeoLMsr4kf5h6dOW11I15MUA==",
+ "dev": true,
+ "license": "MIT",
+ "dependencies": {
+ "ajv": "^8.0.0"
+ },
+ "peerDependencies": {
+ "ajv": "^8.0.0"
+ },
+ "peerDependenciesMeta": {
+ "ajv": {
+ "optional": true
+ }
+ }
+ },
+ "node_modules/ajv-keywords": {
+ "version": "5.1.0",
+ "resolved": "https://registry.npmjs.org/ajv-keywords/-/ajv-keywords-5.1.0.tgz",
+ "integrity": "sha512-YCS/JNFAUyr5vAuhk1DWm1CBxRHW9LbJ2ozWeemrIqpbsqKjHVxYPyi5GC0rjZIT5JxJ3virVTS8wk4i/Z+krw==",
+ "dev": true,
+ "license": "MIT",
+ "dependencies": {
+ "fast-deep-equal": "^3.1.3"
+ },
+ "peerDependencies": {
+ "ajv": "^8.8.2"
+ }
+ },
+ "node_modules/ansi-styles": {
+ "version": "4.3.0",
+ "resolved": "https://registry.npmjs.org/ansi-styles/-/ansi-styles-4.3.0.tgz",
+ "integrity": "sha512-zbB9rCJAT1rbjiVDb2hqKFHNYLxgtk8NURxZ3IZwD3F6NtxbXZQCnnSi1Lkx+IDohdPlFp222wVALIheZJQSEg==",
+ "dev": true,
+ "license": "MIT",
+ "dependencies": {
+ "color-convert": "^2.0.1"
+ },
+ "engines": {
+ "node": ">=8"
+ },
+ "funding": {
+ "url": "https://github.com/chalk/ansi-styles?sponsor=1"
+ }
+ },
+ "node_modules/asn1.js": {
+ "version": "4.10.1",
+ "resolved": "https://registry.npmjs.org/asn1.js/-/asn1.js-4.10.1.tgz",
+ "integrity": "sha512-p32cOF5q0Zqs9uBiONKYLm6BClCoBCM5O9JfeUSlnQLBTxYdTK+pW+nXflm8UkKd2UYlEbYz5qEi0JuZR9ckSw==",
+ "dev": true,
+ "license": "MIT",
+ "dependencies": {
+ "bn.js": "^4.0.0",
+ "inherits": "^2.0.1",
+ "minimalistic-assert": "^1.0.0"
+ }
+ },
+ "node_modules/asn1.js/node_modules/bn.js": {
+ "version": "4.12.2",
+ "resolved": "https://registry.npmjs.org/bn.js/-/bn.js-4.12.2.tgz",
+ "integrity": "sha512-n4DSx829VRTRByMRGdjQ9iqsN0Bh4OolPsFnaZBLcbi8iXcB+kJ9s7EnRt4wILZNV3kPLHkRVfOc/HvhC3ovDw==",
+ "dev": true,
+ "license": "MIT"
+ },
+ "node_modules/assert": {
+ "version": "2.0.0",
+ "resolved": "https://registry.npmjs.org/assert/-/assert-2.0.0.tgz",
+ "integrity": "sha512-se5Cd+js9dXJnu6Ag2JFc00t+HmHOen+8Q+L7O9zI0PqQXr20uk2J0XQqMxZEeo5U50o8Nvmmx7dZrl+Ufr35A==",
+ "dev": true,
+ "license": "MIT",
+ "dependencies": {
+ "es6-object-assign": "^1.1.0",
+ "is-nan": "^1.2.1",
+ "object-is": "^1.0.1",
+ "util": "^0.12.0"
+ }
+ },
+ "node_modules/available-typed-arrays": {
+ "version": "1.0.7",
+ "resolved": "https://registry.npmjs.org/available-typed-arrays/-/available-typed-arrays-1.0.7.tgz",
+ "integrity": "sha512-wvUjBtSGN7+7SjNpq/9M2Tg350UZD3q62IFZLbRAR1bSMlCo1ZaeW+BJ+D090e4hIIZLBcTDWe4Mh4jvUDajzQ==",
+ "dev": true,
+ "license": "MIT",
+ "dependencies": {
+ "possible-typed-array-names": "^1.0.0"
+ },
+ "engines": {
+ "node": ">= 0.4"
+ },
+ "funding": {
+ "url": "https://github.com/sponsors/ljharb"
+ }
+ },
+ "node_modules/base64-js": {
+ "version": "1.5.1",
+ "resolved": "https://registry.npmjs.org/base64-js/-/base64-js-1.5.1.tgz",
+ "integrity": "sha512-AKpaYlHn8t4SVbOHCy+b5+KKgvR4vrsD8vbvrbiQJps7fKDTkjkDry6ji0rUJjC0kzbNePLwzxq8iypo41qeWA==",
+ "dev": true,
+ "funding": [
+ {
+ "type": "github",
+ "url": "https://github.com/sponsors/feross"
+ },
+ {
+ "type": "patreon",
+ "url": "https://www.patreon.com/feross"
+ },
+ {
+ "type": "consulting",
+ "url": "https://feross.org/support"
+ }
+ ],
+ "license": "MIT"
+ },
+ "node_modules/baseline-browser-mapping": {
+ "version": "2.9.19",
+ "resolved": "https://registry.npmjs.org/baseline-browser-mapping/-/baseline-browser-mapping-2.9.19.tgz",
+ "integrity": "sha512-ipDqC8FrAl/76p2SSWKSI+H9tFwm7vYqXQrItCuiVPt26Km0jS+NzSsBWAaBusvSbQcfJG+JitdMm+wZAgTYqg==",
+ "dev": true,
+ "license": "Apache-2.0",
+ "bin": {
+ "baseline-browser-mapping": "dist/cli.js"
+ }
+ },
+ "node_modules/before-after-hook": {
+ "version": "3.0.2",
+ "resolved": "https://registry.npmjs.org/before-after-hook/-/before-after-hook-3.0.2.tgz",
+ "integrity": "sha512-Nik3Sc0ncrMK4UUdXQmAnRtzmNQTAAXmXIopizwZ1W1t8QmfJj+zL4OA2I7XPTPW5z5TDqv4hRo/JzouDJnX3A==",
+ "license": "Apache-2.0"
+ },
+ "node_modules/bn.js": {
+ "version": "5.2.2",
+ "resolved": "https://registry.npmjs.org/bn.js/-/bn.js-5.2.2.tgz",
+ "integrity": "sha512-v2YAxEmKaBLahNwE1mjp4WON6huMNeuDvagFZW+ASCuA/ku0bXR9hSMw0XpiqMoA3+rmnyck/tPRSFQkoC9Cuw==",
+ "dev": true,
+ "license": "MIT"
+ },
+ "node_modules/braces": {
+ "version": "3.0.3",
+ "resolved": "https://registry.npmjs.org/braces/-/braces-3.0.3.tgz",
+ "integrity": "sha512-yQbXgO/OSZVD2IsiLlro+7Hf6Q18EJrKSEsdoMzKePKXct3gvD8oLcOQdIzGupr5Fj+EDe8gO/lxc1BzfMpxvA==",
+ "dev": true,
+ "license": "MIT",
+ "dependencies": {
+ "fill-range": "^7.1.1"
+ },
+ "engines": {
+ "node": ">=8"
+ }
+ },
+ "node_modules/brorand": {
+ "version": "1.1.0",
+ "resolved": "https://registry.npmjs.org/brorand/-/brorand-1.1.0.tgz",
+ "integrity": "sha1-EsJe/kCkXjwyPrhnWgoM5XsiNx8= sha512-cKV8tMCEpQs4hK/ik71d6LrPOnpkpGBR0wzxqr68g2m/LB2GxVYQroAjMJZRVM1Y4BCjCKc3vAamxSzOY2RP+w==",
+ "dev": true,
+ "license": "MIT"
+ },
+ "node_modules/browserify-aes": {
+ "version": "1.2.0",
+ "resolved": "https://registry.npmjs.org/browserify-aes/-/browserify-aes-1.2.0.tgz",
+ "integrity": "sha512-+7CHXqGuspUn/Sl5aO7Ea0xWGAtETPXNSAjHo48JfLdPWcMng33Xe4znFvQweqc/uzk5zSOI3H52CYnjCfb5hA==",
+ "dev": true,
+ "license": "MIT",
+ "dependencies": {
+ "buffer-xor": "^1.0.3",
+ "cipher-base": "^1.0.0",
+ "create-hash": "^1.1.0",
+ "evp_bytestokey": "^1.0.3",
+ "inherits": "^2.0.1",
+ "safe-buffer": "^5.0.1"
+ }
+ },
+ "node_modules/browserify-cipher": {
+ "version": "1.0.1",
+ "resolved": "https://registry.npmjs.org/browserify-cipher/-/browserify-cipher-1.0.1.tgz",
+ "integrity": "sha512-sPhkz0ARKbf4rRQt2hTpAHqn47X3llLkUGn+xEJzLjwY8LRs2p0v7ljvI5EyoRO/mexrNunNECisZs+gw2zz1w==",
+ "dev": true,
+ "license": "MIT",
+ "dependencies": {
+ "browserify-aes": "^1.0.4",
+ "browserify-des": "^1.0.0",
+ "evp_bytestokey": "^1.0.0"
+ }
+ },
+ "node_modules/browserify-des": {
+ "version": "1.0.2",
+ "resolved": "https://registry.npmjs.org/browserify-des/-/browserify-des-1.0.2.tgz",
+ "integrity": "sha512-BioO1xf3hFwz4kc6iBhI3ieDFompMhrMlnDFC4/0/vd5MokpuAc3R+LYbwTA9A5Yc9pq9UYPqffKpW2ObuwX5A==",
+ "dev": true,
+ "license": "MIT",
+ "dependencies": {
+ "cipher-base": "^1.0.1",
+ "des.js": "^1.0.0",
+ "inherits": "^2.0.1",
+ "safe-buffer": "^5.1.2"
+ }
+ },
+ "node_modules/browserify-rsa": {
+ "version": "4.1.1",
+ "resolved": "https://registry.npmjs.org/browserify-rsa/-/browserify-rsa-4.1.1.tgz",
+ "integrity": "sha512-YBjSAiTqM04ZVei6sXighu679a3SqWORA3qZTEqZImnlkDIFtKc6pNutpjyZ8RJTjQtuYfeetkxM11GwoYXMIQ==",
+ "dev": true,
+ "license": "MIT",
+ "dependencies": {
+ "bn.js": "^5.2.1",
+ "randombytes": "^2.1.0",
+ "safe-buffer": "^5.2.1"
+ },
+ "engines": {
+ "node": ">= 0.10"
+ }
+ },
+ "node_modules/browserify-sign": {
+ "version": "4.2.5",
+ "resolved": "https://registry.npmjs.org/browserify-sign/-/browserify-sign-4.2.5.tgz",
+ "integrity": "sha512-C2AUdAJg6rlM2W5QMp2Q4KGQMVBwR1lIimTsUnutJ8bMpW5B52pGpR2gEnNBNwijumDo5FojQ0L9JrXA8m4YEw==",
+ "dev": true,
+ "license": "ISC",
+ "dependencies": {
+ "bn.js": "^5.2.2",
+ "browserify-rsa": "^4.1.1",
+ "create-hash": "^1.2.0",
+ "create-hmac": "^1.1.7",
+ "elliptic": "^6.6.1",
+ "inherits": "^2.0.4",
+ "parse-asn1": "^5.1.9",
+ "readable-stream": "^2.3.8",
+ "safe-buffer": "^5.2.1"
+ },
+ "engines": {
+ "node": ">= 0.10"
+ }
+ },
+ "node_modules/browserify-sign/node_modules/isarray": {
+ "version": "1.0.0",
+ "resolved": "https://registry.npmjs.org/isarray/-/isarray-1.0.0.tgz",
+ "integrity": "sha512-VLghIWNM6ELQzo7zwmcg0NmTVyWKYjvIeM83yjp0wRDTmUnrM678fQbcKBo6n2CJEF0szoG//ytg+TKla89ALQ==",
+ "dev": true,
+ "license": "MIT"
+ },
+ "node_modules/browserify-sign/node_modules/readable-stream": {
+ "version": "2.3.8",
+ "resolved": "https://registry.npmjs.org/readable-stream/-/readable-stream-2.3.8.tgz",
+ "integrity": "sha512-8p0AUk4XODgIewSi0l8Epjs+EVnWiK7NoDIEGU0HhE7+ZyY8D1IMY7odu5lRrFXGg71L15KG8QrPmum45RTtdA==",
+ "dev": true,
+ "license": "MIT",
+ "dependencies": {
+ "core-util-is": "~1.0.0",
+ "inherits": "~2.0.3",
+ "isarray": "~1.0.0",
+ "process-nextick-args": "~2.0.0",
+ "safe-buffer": "~5.1.1",
+ "string_decoder": "~1.1.1",
+ "util-deprecate": "~1.0.1"
+ }
+ },
+ "node_modules/browserify-sign/node_modules/readable-stream/node_modules/safe-buffer": {
+ "version": "5.1.2",
+ "resolved": "https://registry.npmjs.org/safe-buffer/-/safe-buffer-5.1.2.tgz",
+ "integrity": "sha512-Gd2UZBJDkXlY7GbJxfsE8/nvKkUEU1G38c1siN6QP6a9PT9MmHB8GnpscSmMJSoF8LOIrt8ud/wPtojys4G6+g==",
+ "dev": true,
+ "license": "MIT"
+ },
+ "node_modules/browserify-sign/node_modules/string_decoder": {
+ "version": "1.1.1",
+ "resolved": "https://registry.npmjs.org/string_decoder/-/string_decoder-1.1.1.tgz",
+ "integrity": "sha512-n/ShnvDi6FHbbVfviro+WojiFzv+s8MPMHBczVePfUpDJLwoLT0ht1l4YwBCbi8pJAveEEdnkHyPyTP/mzRfwg==",
+ "dev": true,
+ "license": "MIT",
+ "dependencies": {
+ "safe-buffer": "~5.1.0"
+ }
+ },
+ "node_modules/browserify-sign/node_modules/string_decoder/node_modules/safe-buffer": {
+ "version": "5.1.2",
+ "resolved": "https://registry.npmjs.org/safe-buffer/-/safe-buffer-5.1.2.tgz",
+ "integrity": "sha512-Gd2UZBJDkXlY7GbJxfsE8/nvKkUEU1G38c1siN6QP6a9PT9MmHB8GnpscSmMJSoF8LOIrt8ud/wPtojys4G6+g==",
+ "dev": true,
+ "license": "MIT"
+ },
+ "node_modules/browserify-zlib": {
+ "version": "0.2.0",
+ "resolved": "https://registry.npmjs.org/browserify-zlib/-/browserify-zlib-0.2.0.tgz",
+ "integrity": "sha512-Z942RysHXmJrhqk88FmKBVq/v5tqmSkDz7p54G/MGyjMnCFFnC79XWNbg+Vta8W6Wb2qtSZTSxIGkJrRpCFEiA==",
+ "dev": true,
+ "license": "MIT",
+ "dependencies": {
+ "pako": "~1.0.5"
+ }
+ },
+ "node_modules/browserslist": {
+ "version": "4.28.1",
+ "resolved": "https://registry.npmjs.org/browserslist/-/browserslist-4.28.1.tgz",
+ "integrity": "sha512-ZC5Bd0LgJXgwGqUknZY/vkUQ04r8NXnJZ3yYi4vDmSiZmC/pdSN0NbNRPxZpbtO4uAfDUAFffO8IZoM3Gj8IkA==",
+ "dev": true,
+ "funding": [
+ {
+ "type": "opencollective",
+ "url": "https://opencollective.com/browserslist"
+ },
+ {
+ "type": "tidelift",
+ "url": "https://tidelift.com/funding/github/npm/browserslist"
+ },
+ {
+ "type": "github",
+ "url": "https://github.com/sponsors/ai"
+ }
+ ],
+ "license": "MIT",
+ "dependencies": {
+ "baseline-browser-mapping": "^2.9.0",
+ "caniuse-lite": "^1.0.30001759",
+ "electron-to-chromium": "^1.5.263",
+ "node-releases": "^2.0.27",
+ "update-browserslist-db": "^1.2.0"
+ },
+ "bin": {
+ "browserslist": "cli.js"
+ },
+ "engines": {
+ "node": "^6 || ^7 || ^8 || ^9 || ^10 || ^11 || ^12 || >=13.7"
+ }
+ },
+ "node_modules/buffer": {
+ "version": "6.0.3",
+ "resolved": "https://registry.npmjs.org/buffer/-/buffer-6.0.3.tgz",
+ "integrity": "sha512-FTiCpNxtwiZZHEZbcbTIcZjERVICn9yq/pDFkTl95/AxzD1naBctN7YO68riM/gLSDY7sdrMby8hofADYuuqOA==",
+ "dev": true,
+ "funding": [
+ {
+ "type": "github",
+ "url": "https://github.com/sponsors/feross"
+ },
+ {
+ "type": "patreon",
+ "url": "https://www.patreon.com/feross"
+ },
+ {
+ "type": "consulting",
+ "url": "https://feross.org/support"
+ }
+ ],
+ "license": "MIT",
+ "dependencies": {
+ "base64-js": "^1.3.1",
+ "ieee754": "^1.2.1"
+ }
+ },
+ "node_modules/buffer-from": {
+ "version": "1.1.2",
+ "resolved": "https://registry.npmjs.org/buffer-from/-/buffer-from-1.1.2.tgz",
+ "integrity": "sha512-E+XQCRwSbaaiChtv6k6Dwgc+bx+Bs6vuKJHHl5kox/BaKbhiXzqQOwK4cO22yElGp2OCmjwVhT3HmxgyPGnJfQ==",
+ "dev": true,
+ "license": "MIT"
+ },
+ "node_modules/buffer-xor": {
+ "version": "1.0.3",
+ "resolved": "https://registry.npmjs.org/buffer-xor/-/buffer-xor-1.0.3.tgz",
+ "integrity": "sha1-JuYe0UIvtw3ULm42cp7VHYVf6Nk= sha512-571s0T7nZWK6vB67HI5dyUF7wXiNcfaPPPTl6zYCNApANjIvYJTg7hlud/+cJpdAhS7dVzqMLmfhfHR3rAcOjQ==",
+ "dev": true,
+ "license": "MIT"
+ },
+ "node_modules/builtin-status-codes": {
+ "version": "3.0.0",
+ "resolved": "https://registry.npmjs.org/builtin-status-codes/-/builtin-status-codes-3.0.0.tgz",
+ "integrity": "sha1-hZgoeOIbmOHGZCXgPQF0eI9Wnug= sha512-HpGFw18DgFWlncDfjTa2rcQ4W88O1mC8e8yZ2AvQY5KDaktSTwo+KRf6nHK6FRI5FyRyb/5T6+TSxfP7QyGsmQ==",
+ "dev": true,
+ "license": "MIT"
+ },
+ "node_modules/call-bind": {
+ "version": "1.0.8",
+ "resolved": "https://registry.npmjs.org/call-bind/-/call-bind-1.0.8.tgz",
+ "integrity": "sha512-oKlSFMcMwpUg2ednkhQ454wfWiU/ul3CkJe/PEHcTKuiX6RpbehUiFMXu13HalGZxfUwCQzZG747YXBn1im9ww==",
+ "dev": true,
+ "license": "MIT",
+ "dependencies": {
+ "call-bind-apply-helpers": "^1.0.0",
+ "es-define-property": "^1.0.0",
+ "get-intrinsic": "^1.2.4",
+ "set-function-length": "^1.2.2"
+ },
+ "engines": {
+ "node": ">= 0.4"
+ },
+ "funding": {
+ "url": "https://github.com/sponsors/ljharb"
+ }
+ },
+ "node_modules/call-bind-apply-helpers": {
+ "version": "1.0.2",
+ "resolved": "https://registry.npmjs.org/call-bind-apply-helpers/-/call-bind-apply-helpers-1.0.2.tgz",
+ "integrity": "sha512-Sp1ablJ0ivDkSzjcaJdxEunN5/XvksFJ2sMBFfq6x0ryhQV/2b/KwFe21cMpmHtPOSij8K99/wSfoEuTObmuMQ==",
+ "dev": true,
+ "license": "MIT",
+ "dependencies": {
+ "es-errors": "^1.3.0",
+ "function-bind": "^1.1.2"
+ },
+ "engines": {
+ "node": ">= 0.4"
+ }
+ },
+ "node_modules/call-bound": {
+ "version": "1.0.4",
+ "resolved": "https://registry.npmjs.org/call-bound/-/call-bound-1.0.4.tgz",
+ "integrity": "sha512-+ys997U96po4Kx/ABpBCqhA9EuxJaQWDQg7295H4hBphv3IZg0boBKuwYpt4YXp6MZ5AmZQnU/tyMTlRpaSejg==",
+ "dev": true,
+ "license": "MIT",
+ "dependencies": {
+ "call-bind-apply-helpers": "^1.0.2",
+ "get-intrinsic": "^1.3.0"
+ },
+ "engines": {
+ "node": ">= 0.4"
+ },
+ "funding": {
+ "url": "https://github.com/sponsors/ljharb"
+ }
+ },
+ "node_modules/caniuse-lite": {
+ "version": "1.0.30001768",
+ "resolved": "https://registry.npmjs.org/caniuse-lite/-/caniuse-lite-1.0.30001768.tgz",
+ "integrity": "sha512-qY3aDRZC5nWPgHUgIB84WL+nySuo19wk0VJpp/XI9T34lrvkyhRvNVOFJOp2kxClQhiFBu+TaUSudf6oa3vkSA==",
+ "dev": true,
+ "funding": [
+ {
+ "type": "opencollective",
+ "url": "https://opencollective.com/browserslist"
+ },
+ {
+ "type": "tidelift",
+ "url": "https://tidelift.com/funding/github/npm/caniuse-lite"
+ },
+ {
+ "type": "github",
+ "url": "https://github.com/sponsors/ai"
+ }
+ ],
+ "license": "CC-BY-4.0"
+ },
+ "node_modules/chalk": {
+ "version": "4.1.2",
+ "resolved": "https://registry.npmjs.org/chalk/-/chalk-4.1.2.tgz",
+ "integrity": "sha512-oKnbhFyRIXpUuez8iBMmyEa4nbj4IOQyuhc/wy9kY7/WVPcwIO9VA668Pu8RkO7+0G76SLROeyw9CpQ061i4mA==",
+ "dev": true,
+ "license": "MIT",
+ "dependencies": {
+ "ansi-styles": "^4.1.0",
+ "supports-color": "^7.1.0"
+ },
+ "engines": {
+ "node": ">=10"
+ },
+ "funding": {
+ "url": "https://github.com/chalk/chalk?sponsor=1"
+ }
+ },
+ "node_modules/chrome-trace-event": {
+ "version": "1.0.3",
+ "resolved": "https://registry.npmjs.org/chrome-trace-event/-/chrome-trace-event-1.0.3.tgz",
+ "integrity": "sha512-p3KULyQg4S7NIHixdwbGX+nFHkoBiA4YQmyWtjb8XngSKV124nJmRysgAeujbUVb15vh+RvFUfCPqU7rXk+hZg==",
+ "dev": true,
+ "license": "MIT",
+ "engines": {
+ "node": ">=6.0"
+ }
+ },
+ "node_modules/cipher-base": {
+ "version": "1.0.6",
+ "resolved": "https://registry.npmjs.org/cipher-base/-/cipher-base-1.0.6.tgz",
+ "integrity": "sha512-3Ek9H3X6pj5TgenXYtNWdaBon1tgYCaebd+XPg0keyjEbEfkD4KkmAxkQ/i1vYvxdcT5nscLBfq9VJRmCBcFSw==",
+ "dev": true,
+ "license": "MIT",
+ "dependencies": {
+ "inherits": "^2.0.4",
+ "safe-buffer": "^5.2.1"
+ },
+ "engines": {
+ "node": ">= 0.10"
+ }
+ },
+ "node_modules/clone-deep": {
+ "version": "4.0.1",
+ "resolved": "https://registry.npmjs.org/clone-deep/-/clone-deep-4.0.1.tgz",
+ "integrity": "sha512-neHB9xuzh/wk0dIHweyAXv2aPGZIVk3pLMe+/RNzINf17fe0OG96QroktYAUm7SM1PBnzTabaLboqqxDyMU+SQ==",
+ "dev": true,
+ "license": "MIT",
+ "dependencies": {
+ "is-plain-object": "^2.0.4",
+ "kind-of": "^6.0.2",
+ "shallow-clone": "^3.0.0"
+ },
+ "engines": {
+ "node": ">=6"
+ }
+ },
+ "node_modules/clone-deep/node_modules/is-plain-object": {
+ "version": "2.0.4",
+ "resolved": "https://registry.npmjs.org/is-plain-object/-/is-plain-object-2.0.4.tgz",
+ "integrity": "sha512-h5PpgXkWitc38BBMYawTYMWJHFZJVnBquFE57xFpjB8pJFiF6gZ+bU+WyI/yqXiFR5mdLsgYNaPe8uao6Uv9Og==",
+ "dev": true,
+ "license": "MIT",
+ "dependencies": {
+ "isobject": "^3.0.1"
+ },
+ "engines": {
+ "node": ">=0.10.0"
+ }
+ },
+ "node_modules/color-convert": {
+ "version": "2.0.1",
+ "resolved": "https://registry.npmjs.org/color-convert/-/color-convert-2.0.1.tgz",
+ "integrity": "sha512-RRECPsj7iu/xb5oKYcsFHSppFNnsj/52OVTRKb4zP5onXwVF3zVmmToNcOfGC+CRDpfK/U584fMg38ZHCaElKQ==",
+ "dev": true,
+ "license": "MIT",
+ "dependencies": {
+ "color-name": "~1.1.4"
+ },
+ "engines": {
+ "node": ">=7.0.0"
+ }
+ },
+ "node_modules/color-name": {
+ "version": "1.1.4",
+ "resolved": "https://registry.npmjs.org/color-name/-/color-name-1.1.4.tgz",
+ "integrity": "sha512-dOy+3AuW3a2wNbZHIuMZpTcgjGuLU/uBL/ubcZF9OXbDo8ff4O8yVp5Bf0efS8uEoYo5q4Fx7dY9OgQGXgAsQA==",
+ "dev": true,
+ "license": "MIT"
+ },
+ "node_modules/colorette": {
+ "version": "2.0.16",
+ "resolved": "https://registry.npmjs.org/colorette/-/colorette-2.0.16.tgz",
+ "integrity": "sha512-hUewv7oMjCp+wkBv5Rm0v87eJhq4woh5rSR+42YSQJKecCqgIqNkZ6lAlQms/BwHPJA5NKMRlpxPRv0n8HQW6g==",
+ "dev": true,
+ "license": "MIT"
+ },
+ "node_modules/commander": {
+ "version": "2.20.3",
+ "resolved": "https://registry.npmjs.org/commander/-/commander-2.20.3.tgz",
+ "integrity": "sha512-GpVkmM8vF2vQUkj2LvZmD35JxeJOLCwJ9cUkugyk2nuhbv3+mJvpLYYt+0+USMxE+oj+ey/lJEnhZw75x/OMcQ==",
+ "dev": true,
+ "license": "MIT"
+ },
+ "node_modules/console-browserify": {
+ "version": "1.2.0",
+ "resolved": "https://registry.npmjs.org/console-browserify/-/console-browserify-1.2.0.tgz",
+ "integrity": "sha512-ZMkYO/LkF17QvCPqM0gxw8yUzigAOZOSWSHg91FH6orS7vcEj5dVZTidN2fQ14yBSdg97RqhSNwLUXInd52OTA==",
+ "dev": true
+ },
+ "node_modules/constants-browserify": {
+ "version": "1.0.0",
+ "resolved": "https://registry.npmjs.org/constants-browserify/-/constants-browserify-1.0.0.tgz",
+ "integrity": "sha1-wguW2MYXdIqvHBYCF2DNJ/y4y3U= sha512-xFxOwqIzR/e1k1gLiWEophSCMqXcwVHIH7akf7b/vxcUeGunlj3hvZaaqxwHsTgn+IndtkQJgSztIDWeumWJDQ==",
+ "dev": true,
+ "license": "MIT"
+ },
+ "node_modules/core-util-is": {
+ "version": "1.0.3",
+ "resolved": "https://registry.npmjs.org/core-util-is/-/core-util-is-1.0.3.tgz",
+ "integrity": "sha512-ZQBvi1DcpJ4GDqanjucZ2Hj3wEO5pZDS89BWbkcrvdxksJorwUDDZamX9ldFkp9aw2lmBDLgkObEA4DWNJ9FYQ==",
+ "dev": true,
+ "license": "MIT"
+ },
+ "node_modules/create-ecdh": {
+ "version": "4.0.4",
+ "resolved": "https://registry.npmjs.org/create-ecdh/-/create-ecdh-4.0.4.tgz",
+ "integrity": "sha512-mf+TCx8wWc9VpuxfP2ht0iSISLZnt0JgWlrOKZiNqyUZWnjIaCIVNQArMHnCZKfEYRg6IM7A+NeJoN8gf/Ws0A==",
+ "dev": true,
+ "license": "MIT",
+ "dependencies": {
+ "bn.js": "^4.1.0",
+ "elliptic": "^6.5.3"
+ }
+ },
+ "node_modules/create-ecdh/node_modules/bn.js": {
+ "version": "4.12.0",
+ "resolved": "https://registry.npmjs.org/bn.js/-/bn.js-4.12.0.tgz",
+ "integrity": "sha512-c98Bf3tPniI+scsdk237ku1Dc3ujXQTSgyiPUDEOe7tRkhrqridvh8klBv0HCEso1OLOYcHuCv/cS6DNxKH+ZA==",
+ "dev": true,
+ "license": "MIT"
+ },
+ "node_modules/create-hash": {
+ "version": "1.2.0",
+ "resolved": "https://registry.npmjs.org/create-hash/-/create-hash-1.2.0.tgz",
+ "integrity": "sha512-z00bCGNHDG8mHAkP7CtT1qVu+bFQUPjYq/4Iv3C3kWjTFV10zIjfSoeqXo9Asws8gwSHDGj/hl2u4OGIjapeCg==",
+ "dev": true,
+ "license": "MIT",
+ "dependencies": {
+ "cipher-base": "^1.0.1",
+ "inherits": "^2.0.1",
+ "md5.js": "^1.3.4",
+ "ripemd160": "^2.0.1",
+ "sha.js": "^2.4.0"
+ }
+ },
+ "node_modules/create-hmac": {
+ "version": "1.1.7",
+ "resolved": "https://registry.npmjs.org/create-hmac/-/create-hmac-1.1.7.tgz",
+ "integrity": "sha512-MJG9liiZ+ogc4TzUwuvbER1JRdgvUFSB5+VR/g5h82fGaIRWMWddtKBHi7/sVhfjQZ6SehlyhvQYrcYkaUIpLg==",
+ "dev": true,
+ "license": "MIT",
+ "dependencies": {
+ "cipher-base": "^1.0.3",
+ "create-hash": "^1.1.0",
+ "inherits": "^2.0.1",
+ "ripemd160": "^2.0.0",
+ "safe-buffer": "^5.0.1",
+ "sha.js": "^2.4.8"
+ }
+ },
+ "node_modules/cross-spawn": {
+ "version": "7.0.6",
+ "resolved": "https://registry.npmjs.org/cross-spawn/-/cross-spawn-7.0.6.tgz",
+ "integrity": "sha512-uV2QOWP2nWzsy2aMp8aRibhi9dlzF5Hgh5SHaB9OiTGEyDTiJJyx0uy51QXdyWbtAHNua4XJzUKca3OzKUd3vA==",
+ "dev": true,
+ "license": "MIT",
+ "dependencies": {
+ "path-key": "^3.1.0",
+ "shebang-command": "^2.0.0",
+ "which": "^2.0.1"
+ },
+ "engines": {
+ "node": ">= 8"
+ }
+ },
+ "node_modules/crypto-browserify": {
+ "version": "3.12.1",
+ "resolved": "https://registry.npmjs.org/crypto-browserify/-/crypto-browserify-3.12.1.tgz",
+ "integrity": "sha512-r4ESw/IlusD17lgQi1O20Fa3qNnsckR126TdUuBgAu7GBYSIPvdNyONd3Zrxh0xCwA4+6w/TDArBPsMvhur+KQ==",
+ "dev": true,
+ "license": "MIT",
+ "dependencies": {
+ "browserify-cipher": "^1.0.1",
+ "browserify-sign": "^4.2.3",
+ "create-ecdh": "^4.0.4",
+ "create-hash": "^1.2.0",
+ "create-hmac": "^1.1.7",
+ "diffie-hellman": "^5.0.3",
+ "hash-base": "~3.0.4",
+ "inherits": "^2.0.4",
+ "pbkdf2": "^3.1.2",
+ "public-encrypt": "^4.0.3",
+ "randombytes": "^2.1.0",
+ "randomfill": "^1.0.4"
+ },
+ "engines": {
+ "node": ">= 0.10"
+ },
+ "funding": {
+ "url": "https://github.com/sponsors/ljharb"
+ }
+ },
+ "node_modules/crypto-browserify/node_modules/hash-base": {
+ "version": "3.0.5",
+ "resolved": "https://registry.npmjs.org/hash-base/-/hash-base-3.0.5.tgz",
+ "integrity": "sha512-vXm0l45VbcHEVlTCzs8M+s0VeYsB2lnlAaThoLKGXr3bE/VWDOelNUnycUPEhKEaXARL2TEFjBOyUiM6+55KBg==",
+ "dev": true,
+ "license": "MIT",
+ "dependencies": {
+ "inherits": "^2.0.4",
+ "safe-buffer": "^5.2.1"
+ },
+ "engines": {
+ "node": ">= 0.10"
+ }
+ },
+ "node_modules/dayjs": {
+ "version": "1.11.2",
+ "resolved": "https://registry.npmjs.org/dayjs/-/dayjs-1.11.2.tgz",
+ "integrity": "sha512-F4LXf1OeU9hrSYRPTTj/6FbO4HTjPKXvEIC1P2kcnFurViINCVk3ZV0xAS3XVx9MkMsXbbqlK6hjseaYbgKEHw==",
+ "license": "MIT"
+ },
+ "node_modules/decode-uri-component": {
+ "version": "0.5.0",
+ "resolved": "https://registry.npmjs.org/decode-uri-component/-/decode-uri-component-0.5.0.tgz",
+ "integrity": "sha512-1BiQVoK8C9gUbQU6NzAtO/tkz2qOFpEObMWpcFvhx4fYnj4Oc5yzaJN/LD36ihkVUdXyh5ZekzX+yM+ty/SrPg==",
+ "license": "MIT",
+ "engines": {
+ "node": ">=14.16"
+ }
+ },
+ "node_modules/define-data-property": {
+ "version": "1.1.4",
+ "resolved": "https://registry.npmjs.org/define-data-property/-/define-data-property-1.1.4.tgz",
+ "integrity": "sha512-rBMvIzlpA8v6E+SJZoo++HAYqsLrkg7MSfIinMPFhmkorw7X+dOXVJQs+QT69zGkzMyfDnIMN2Wid1+NbL3T+A==",
+ "dev": true,
+ "license": "MIT",
+ "dependencies": {
+ "es-define-property": "^1.0.0",
+ "es-errors": "^1.3.0",
+ "gopd": "^1.0.1"
+ },
+ "engines": {
+ "node": ">= 0.4"
+ },
+ "funding": {
+ "url": "https://github.com/sponsors/ljharb"
+ }
+ },
+ "node_modules/define-properties": {
+ "version": "1.1.4",
+ "resolved": "https://registry.npmjs.org/define-properties/-/define-properties-1.1.4.tgz",
+ "integrity": "sha512-uckOqKcfaVvtBdsVkdPv3XjveQJsNQqmhXgRi8uhvWWuPYZCNlzT8qAyblUgNoXdHdjMTzAqeGjAoli8f+bzPA==",
+ "dev": true,
+ "license": "MIT",
+ "dependencies": {
+ "has-property-descriptors": "^1.0.0",
+ "object-keys": "^1.1.1"
+ },
+ "engines": {
+ "node": ">= 0.4"
+ },
+ "funding": {
+ "url": "https://github.com/sponsors/ljharb"
+ }
+ },
+ "node_modules/des.js": {
+ "version": "1.0.1",
+ "resolved": "https://registry.npmjs.org/des.js/-/des.js-1.0.1.tgz",
+ "integrity": "sha512-Q0I4pfFrv2VPd34/vfLrFOoRmlYj3OV50i7fskps1jZWK1kApMWWT9G6RRUeYedLcBDIhnSDaUvJMb3AhUlaEA==",
+ "dev": true,
+ "license": "MIT",
+ "dependencies": {
+ "inherits": "^2.0.1",
+ "minimalistic-assert": "^1.0.0"
+ }
+ },
+ "node_modules/diffie-hellman": {
+ "version": "5.0.3",
+ "resolved": "https://registry.npmjs.org/diffie-hellman/-/diffie-hellman-5.0.3.tgz",
+ "integrity": "sha512-kqag/Nl+f3GwyK25fhUMYj81BUOrZ9IuJsjIcDE5icNM9FJHAVm3VcUDxdLPoQtTuUylWm6ZIknYJwwaPxsUzg==",
+ "dev": true,
+ "license": "MIT",
+ "dependencies": {
+ "bn.js": "^4.1.0",
+ "miller-rabin": "^4.0.0",
+ "randombytes": "^2.0.0"
+ }
+ },
+ "node_modules/diffie-hellman/node_modules/bn.js": {
+ "version": "4.12.0",
+ "resolved": "https://registry.npmjs.org/bn.js/-/bn.js-4.12.0.tgz",
+ "integrity": "sha512-c98Bf3tPniI+scsdk237ku1Dc3ujXQTSgyiPUDEOe7tRkhrqridvh8klBv0HCEso1OLOYcHuCv/cS6DNxKH+ZA==",
+ "dev": true,
+ "license": "MIT"
+ },
+ "node_modules/domain-browser": {
+ "version": "4.22.0",
+ "resolved": "https://registry.npmjs.org/domain-browser/-/domain-browser-4.22.0.tgz",
+ "integrity": "sha512-IGBwjF7tNk3cwypFNH/7bfzBcgSCbaMOD3GsaY1AU/JRrnHnYgEM0+9kQt52iZxjNsjBtJYtao146V+f8jFZNw==",
+ "dev": true,
+ "license": "MIT",
+ "engines": {
+ "node": ">=10"
+ },
+ "funding": {
+ "url": "https://bevry.me/fund"
+ }
+ },
+ "node_modules/dunder-proto": {
+ "version": "1.0.1",
+ "resolved": "https://registry.npmjs.org/dunder-proto/-/dunder-proto-1.0.1.tgz",
+ "integrity": "sha512-KIN/nDJBQRcXw0MLVhZE9iQHmG68qAVIBg9CqmUYjmQIhgij9U5MFvrqkUL5FbtyyzZuOeOt0zdeRe4UY7ct+A==",
+ "dev": true,
+ "license": "MIT",
+ "dependencies": {
+ "call-bind-apply-helpers": "^1.0.1",
+ "es-errors": "^1.3.0",
+ "gopd": "^1.2.0"
+ },
+ "engines": {
+ "node": ">= 0.4"
+ }
+ },
+ "node_modules/electron-to-chromium": {
+ "version": "1.5.286",
+ "resolved": "https://registry.npmjs.org/electron-to-chromium/-/electron-to-chromium-1.5.286.tgz",
+ "integrity": "sha512-9tfDXhJ4RKFNerfjdCcZfufu49vg620741MNs26a9+bhLThdB+plgMeou98CAaHu/WATj2iHOOHTp1hWtABj2A==",
+ "dev": true,
+ "license": "ISC"
+ },
+ "node_modules/elliptic": {
+ "version": "6.6.1",
+ "resolved": "https://registry.npmjs.org/elliptic/-/elliptic-6.6.1.tgz",
+ "integrity": "sha512-RaddvvMatK2LJHqFJ+YA4WysVN5Ita9E35botqIYspQ4TkRAlCicdzKOjlyv/1Za5RyTNn7di//eEV0uTAfe3g==",
+ "dev": true,
+ "license": "MIT",
+ "dependencies": {
+ "bn.js": "^4.11.9",
+ "brorand": "^1.1.0",
+ "hash.js": "^1.0.0",
+ "hmac-drbg": "^1.0.1",
+ "inherits": "^2.0.4",
+ "minimalistic-assert": "^1.0.1",
+ "minimalistic-crypto-utils": "^1.0.1"
+ }
+ },
+ "node_modules/elliptic/node_modules/bn.js": {
+ "version": "4.12.0",
+ "resolved": "https://registry.npmjs.org/bn.js/-/bn.js-4.12.0.tgz",
+ "integrity": "sha512-c98Bf3tPniI+scsdk237ku1Dc3ujXQTSgyiPUDEOe7tRkhrqridvh8klBv0HCEso1OLOYcHuCv/cS6DNxKH+ZA==",
+ "dev": true,
+ "license": "MIT"
+ },
+ "node_modules/enhanced-resolve": {
+ "version": "5.19.0",
+ "resolved": "https://registry.npmjs.org/enhanced-resolve/-/enhanced-resolve-5.19.0.tgz",
+ "integrity": "sha512-phv3E1Xl4tQOShqSte26C7Fl84EwUdZsyOuSSk9qtAGyyQs2s3jJzComh+Abf4g187lUUAvH+H26omrqia2aGg==",
+ "dev": true,
+ "license": "MIT",
+ "dependencies": {
+ "graceful-fs": "^4.2.4",
+ "tapable": "^2.3.0"
+ },
+ "engines": {
+ "node": ">=10.13.0"
+ }
+ },
+ "node_modules/envinfo": {
+ "version": "7.8.1",
+ "resolved": "https://registry.npmjs.org/envinfo/-/envinfo-7.8.1.tgz",
+ "integrity": "sha512-/o+BXHmB7ocbHEAs6F2EnG0ogybVVUdkRunTT2glZU9XAaGmhqskrvKwqXuDfNjEO0LZKWdejEEpnq8aM0tOaw==",
+ "dev": true,
+ "license": "MIT",
+ "bin": {
+ "envinfo": "dist/cli.js"
+ },
+ "engines": {
+ "node": ">=4"
+ }
+ },
+ "node_modules/es-define-property": {
+ "version": "1.0.1",
+ "resolved": "https://registry.npmjs.org/es-define-property/-/es-define-property-1.0.1.tgz",
+ "integrity": "sha512-e3nRfgfUZ4rNGL232gUgX06QNyyez04KdjFrF+LTRoOXmrOgFKDg4BCdsjW8EnT69eqdYGmRpJwiPVYNrCaW3g==",
+ "dev": true,
+ "license": "MIT",
+ "engines": {
+ "node": ">= 0.4"
+ }
+ },
+ "node_modules/es-errors": {
+ "version": "1.3.0",
+ "resolved": "https://registry.npmjs.org/es-errors/-/es-errors-1.3.0.tgz",
+ "integrity": "sha512-Zf5H2Kxt2xjTvbJvP2ZWLEICxA6j+hAmMzIlypy4xcBg1vKVnx89Wy0GbS+kf5cwCVFFzdCFh2XSCFNULS6csw==",
+ "dev": true,
+ "license": "MIT",
+ "engines": {
+ "node": ">= 0.4"
+ }
+ },
+ "node_modules/es-module-lexer": {
+ "version": "2.0.0",
+ "resolved": "https://registry.npmjs.org/es-module-lexer/-/es-module-lexer-2.0.0.tgz",
+ "integrity": "sha512-5POEcUuZybH7IdmGsD8wlf0AI55wMecM9rVBTI/qEAy2c1kTOm3DjFYjrBdI2K3BaJjJYfYFeRtM0t9ssnRuxw==",
+ "dev": true,
+ "license": "MIT"
+ },
+ "node_modules/es-object-atoms": {
+ "version": "1.1.1",
+ "resolved": "https://registry.npmjs.org/es-object-atoms/-/es-object-atoms-1.1.1.tgz",
+ "integrity": "sha512-FGgH2h8zKNim9ljj7dankFPcICIK9Cp5bm+c2gQSYePhpaG5+esrLODihIorn+Pe6FGJzWhXQotPv73jTaldXA==",
+ "dev": true,
+ "license": "MIT",
+ "dependencies": {
+ "es-errors": "^1.3.0"
+ },
+ "engines": {
+ "node": ">= 0.4"
+ }
+ },
+ "node_modules/es6-object-assign": {
+ "version": "1.1.0",
+ "resolved": "https://registry.npmjs.org/es6-object-assign/-/es6-object-assign-1.1.0.tgz",
+ "integrity": "sha1-wsNYJlYkfDnqEHyx5mUrb58kUjw= sha512-MEl9uirslVwqQU369iHNWZXsI8yaZYGg/D65aOgZkeyFJwHYSxilf7rQzXKI7DdDuBPrBXbfk3sl9hJhmd5AUw==",
+ "dev": true,
+ "license": "MIT"
+ },
+ "node_modules/escalade": {
+ "version": "3.2.0",
+ "resolved": "https://registry.npmjs.org/escalade/-/escalade-3.2.0.tgz",
+ "integrity": "sha512-WUj2qlxaQtO4g6Pq5c29GTcWGDyd8itL8zTlipgECz3JesAiiOKotd8JU6otB3PACgG6xkJUyVhboMS+bje/jA==",
+ "dev": true,
+ "license": "MIT",
+ "engines": {
+ "node": ">=6"
+ }
+ },
+ "node_modules/eslint-scope": {
+ "version": "5.1.1",
+ "resolved": "https://registry.npmjs.org/eslint-scope/-/eslint-scope-5.1.1.tgz",
+ "integrity": "sha512-2NxwbF/hZ0KpepYN0cNbo+FN6XoK7GaHlQhgx/hIZl6Va0bF45RQOOwhLIy8lQDbuCiadSLCBnH2CFYquit5bw==",
+ "dev": true,
+ "license": "BSD-2-Clause",
+ "dependencies": {
+ "esrecurse": "^4.3.0",
+ "estraverse": "^4.1.1"
+ },
+ "engines": {
+ "node": ">=8.0.0"
+ }
+ },
+ "node_modules/esrecurse": {
+ "version": "4.3.0",
+ "resolved": "https://registry.npmjs.org/esrecurse/-/esrecurse-4.3.0.tgz",
+ "integrity": "sha512-KmfKL3b6G+RXvP8N1vr3Tq1kL/oCFgn2NYXEtqP8/L3pKapUA4G8cFVaoF3SU323CD4XypR/ffioHmkti6/Tag==",
+ "dev": true,
+ "license": "BSD-2-Clause",
+ "dependencies": {
+ "estraverse": "^5.2.0"
+ },
+ "engines": {
+ "node": ">=4.0"
+ }
+ },
+ "node_modules/esrecurse/node_modules/estraverse": {
+ "version": "5.3.0",
+ "resolved": "https://registry.npmjs.org/estraverse/-/estraverse-5.3.0.tgz",
+ "integrity": "sha512-MMdARuVEQziNTeJD8DgMqmhwR11BRQ/cBP+pLtYdSTnf3MIO8fFeiINEbX36ZdNlfU/7A9f3gUw49B3oQsvwBA==",
+ "dev": true,
+ "license": "BSD-2-Clause",
+ "engines": {
+ "node": ">=4.0"
+ }
+ },
+ "node_modules/estraverse": {
+ "version": "4.3.0",
+ "resolved": "https://registry.npmjs.org/estraverse/-/estraverse-4.3.0.tgz",
+ "integrity": "sha512-39nnKffWz8xN1BU/2c79n9nB9HDzo0niYUqx6xyqUnyoAnQyyWpOTdZEeiCch8BBu515t4wp9ZmgVfVhn9EBpw==",
+ "dev": true,
+ "license": "BSD-2-Clause",
+ "engines": {
+ "node": ">=4.0"
+ }
+ },
+ "node_modules/events": {
+ "version": "3.3.0",
+ "resolved": "https://registry.npmjs.org/events/-/events-3.3.0.tgz",
+ "integrity": "sha512-mQw+2fkQbALzQ7V0MY0IqdnXNOeTtP4r0lN9z7AAawCXgqea7bDii20AYrIBrFd/Hx0M2Ocz6S111CaFkUcb0Q==",
+ "dev": true,
+ "license": "MIT",
+ "engines": {
+ "node": ">=0.8.x"
+ }
+ },
+ "node_modules/evp_bytestokey": {
+ "version": "1.0.3",
+ "resolved": "https://registry.npmjs.org/evp_bytestokey/-/evp_bytestokey-1.0.3.tgz",
+ "integrity": "sha512-/f2Go4TognH/KvCISP7OUsHn85hT9nUkxxA9BEWxFn+Oj9o8ZNLm/40hdlgSLyuOimsrTKLUMEorQexp/aPQeA==",
+ "dev": true,
+ "license": "MIT",
+ "dependencies": {
+ "md5.js": "^1.3.4",
+ "safe-buffer": "^5.1.1"
+ }
+ },
+ "node_modules/execa": {
+ "version": "5.1.1",
+ "resolved": "https://registry.npmjs.org/execa/-/execa-5.1.1.tgz",
+ "integrity": "sha512-8uSpZZocAZRBAPIEINJj3Lo9HyGitllczc27Eh5YYojjMFMn8yHMDMaUHE2Jqfq05D/wucwI4JGURyXt1vchyg==",
+ "dev": true,
+ "license": "MIT",
+ "dependencies": {
+ "cross-spawn": "^7.0.3",
+ "get-stream": "^6.0.0",
+ "human-signals": "^2.1.0",
+ "is-stream": "^2.0.0",
+ "merge-stream": "^2.0.0",
+ "npm-run-path": "^4.0.1",
+ "onetime": "^5.1.2",
+ "signal-exit": "^3.0.3",
+ "strip-final-newline": "^2.0.0"
+ },
+ "engines": {
+ "node": ">=10"
+ },
+ "funding": {
+ "url": "https://github.com/sindresorhus/execa?sponsor=1"
+ }
+ },
+ "node_modules/fast-content-type-parse": {
+ "version": "2.0.1",
+ "resolved": "https://registry.npmjs.org/fast-content-type-parse/-/fast-content-type-parse-2.0.1.tgz",
+ "integrity": "sha512-nGqtvLrj5w0naR6tDPfB4cUmYCqouzyQiz6C5y/LtcDllJdrcc6WaWW6iXyIIOErTa/XRybj28aasdn4LkVk6Q==",
+ "funding": [
+ {
+ "type": "github",
+ "url": "https://github.com/sponsors/fastify"
+ },
+ {
+ "type": "opencollective",
+ "url": "https://opencollective.com/fastify"
+ }
+ ],
+ "license": "MIT"
+ },
+ "node_modules/fast-deep-equal": {
+ "version": "3.1.3",
+ "resolved": "https://registry.npmjs.org/fast-deep-equal/-/fast-deep-equal-3.1.3.tgz",
+ "integrity": "sha512-f3qQ9oQy9j2AhBe/H9VC91wLmKBCCU/gDOnKNAYG5hswO7BLKj09Hc5HYNz9cGI++xlpDCIgDaitVs03ATR84Q==",
+ "dev": true,
+ "license": "MIT"
+ },
+ "node_modules/fast-uri": {
+ "version": "3.1.2",
+ "resolved": "https://registry.npmjs.org/fast-uri/-/fast-uri-3.1.2.tgz",
+ "integrity": "sha512-rVjf7ArG3LTk+FS6Yw81V1DLuZl1bRbNrev6Tmd/9RaroeeRRJhAt7jg/6YFxbvAQXUCavSoZhPPj6oOx+5KjQ==",
+ "dev": true,
+ "funding": [
+ {
+ "type": "github",
+ "url": "https://github.com/sponsors/fastify"
+ },
+ {
+ "type": "opencollective",
+ "url": "https://opencollective.com/fastify"
+ }
+ ],
+ "license": "BSD-3-Clause"
+ },
+ "node_modules/fastest-levenshtein": {
+ "version": "1.0.12",
+ "resolved": "https://registry.npmjs.org/fastest-levenshtein/-/fastest-levenshtein-1.0.12.tgz",
+ "integrity": "sha512-On2N+BpYJ15xIC974QNVuYGMOlEVt4s0EOI3wwMqOmK1fdDY+FN/zltPV8vosq4ad4c/gJ1KHScUn/6AWIgiow==",
+ "dev": true,
+ "license": "MIT"
+ },
+ "node_modules/fill-range": {
+ "version": "7.1.1",
+ "resolved": "https://registry.npmjs.org/fill-range/-/fill-range-7.1.1.tgz",
+ "integrity": "sha512-YsGpe3WHLK8ZYi4tWDg2Jy3ebRz2rXowDxnld4bkQB00cc/1Zw9AWnC0i9ztDJitivtQvaI9KaLyKrc+hBW0yg==",
+ "dev": true,
+ "license": "MIT",
+ "dependencies": {
+ "to-regex-range": "^5.0.1"
+ },
+ "engines": {
+ "node": ">=8"
+ }
+ },
+ "node_modules/filter-obj": {
+ "version": "2.0.2",
+ "resolved": "https://registry.npmjs.org/filter-obj/-/filter-obj-2.0.2.tgz",
+ "integrity": "sha512-lO3ttPjHZRfjMcxWKb1j1eDhTFsu4meeR3lnMcnBFhk6RuLhvEiuALu2TlfL310ph4lCYYwgF/ElIjdP739tdg==",
+ "dev": true,
+ "license": "MIT",
+ "engines": {
+ "node": ">=8"
+ }
+ },
+ "node_modules/find-up": {
+ "version": "4.1.0",
+ "resolved": "https://registry.npmjs.org/find-up/-/find-up-4.1.0.tgz",
+ "integrity": "sha512-PpOwAdQ/YlXQ2vj8a3h8IipDuYRi3wceVQQGYWxNINccq40Anw7BlsEXCMbt1Zt+OLA6Fq9suIpIWD0OsnISlw==",
+ "dev": true,
+ "license": "MIT",
+ "dependencies": {
+ "locate-path": "^5.0.0",
+ "path-exists": "^4.0.0"
+ },
+ "engines": {
+ "node": ">=8"
+ }
+ },
+ "node_modules/for-each": {
+ "version": "0.3.5",
+ "resolved": "https://registry.npmjs.org/for-each/-/for-each-0.3.5.tgz",
+ "integrity": "sha512-dKx12eRCVIzqCxFGplyFKJMPvLEWgmNtUrpTiJIR5u97zEhRG8ySrtboPHZXx7daLxQVrl643cTzbab2tkQjxg==",
+ "dev": true,
+ "license": "MIT",
+ "dependencies": {
+ "is-callable": "^1.2.7"
+ },
+ "engines": {
+ "node": ">= 0.4"
+ },
+ "funding": {
+ "url": "https://github.com/sponsors/ljharb"
+ }
+ },
+ "node_modules/function-bind": {
+ "version": "1.1.2",
+ "resolved": "https://registry.npmjs.org/function-bind/-/function-bind-1.1.2.tgz",
+ "integrity": "sha512-7XHNxH7qX9xG5mIwxkhumTox/MIRNcOgDrxWsMt2pAr23WHp6MrRlN7FBSFpCpr+oVO0F744iUgR82nJMfG2SA==",
+ "dev": true,
+ "license": "MIT",
+ "funding": {
+ "url": "https://github.com/sponsors/ljharb"
+ }
+ },
+ "node_modules/get-intrinsic": {
+ "version": "1.3.0",
+ "resolved": "https://registry.npmjs.org/get-intrinsic/-/get-intrinsic-1.3.0.tgz",
+ "integrity": "sha512-9fSjSaos/fRIVIp+xSJlE6lfwhES7LNtKaCBIamHsjr2na1BiABJPo0mOjjz8GJDURarmCPGqaiVg5mfjb98CQ==",
+ "dev": true,
+ "license": "MIT",
+ "dependencies": {
+ "call-bind-apply-helpers": "^1.0.2",
+ "es-define-property": "^1.0.1",
+ "es-errors": "^1.3.0",
+ "es-object-atoms": "^1.1.1",
+ "function-bind": "^1.1.2",
+ "get-proto": "^1.0.1",
+ "gopd": "^1.2.0",
+ "has-symbols": "^1.1.0",
+ "hasown": "^2.0.2",
+ "math-intrinsics": "^1.1.0"
+ },
+ "engines": {
+ "node": ">= 0.4"
+ },
+ "funding": {
+ "url": "https://github.com/sponsors/ljharb"
+ }
+ },
+ "node_modules/get-proto": {
+ "version": "1.0.1",
+ "resolved": "https://registry.npmjs.org/get-proto/-/get-proto-1.0.1.tgz",
+ "integrity": "sha512-sTSfBjoXBp89JvIKIefqw7U2CCebsc74kiY6awiGogKtoSGbgjYE/G/+l9sF3MWFPNc9IcoOC4ODfKHfxFmp0g==",
+ "dev": true,
+ "license": "MIT",
+ "dependencies": {
+ "dunder-proto": "^1.0.1",
+ "es-object-atoms": "^1.0.0"
+ },
+ "engines": {
+ "node": ">= 0.4"
+ }
+ },
+ "node_modules/get-stream": {
+ "version": "6.0.1",
+ "resolved": "https://registry.npmjs.org/get-stream/-/get-stream-6.0.1.tgz",
+ "integrity": "sha512-ts6Wi+2j3jQjqi70w5AlN8DFnkSwC+MqmxEzdEALB2qXZYV3X/b1CTfgPLGJNMeAWxdPfU8FO1ms3NUfaHCPYg==",
+ "dev": true,
+ "license": "MIT",
+ "engines": {
+ "node": ">=10"
+ },
+ "funding": {
+ "url": "https://github.com/sponsors/sindresorhus"
+ }
+ },
+ "node_modules/glob-to-regexp": {
+ "version": "0.4.1",
+ "resolved": "https://registry.npmjs.org/glob-to-regexp/-/glob-to-regexp-0.4.1.tgz",
+ "integrity": "sha512-lkX1HJXwyMcprw/5YUZc2s7DrpAiHB21/V+E1rHUrVNokkvB6bqMzT0VfV6/86ZNabt1k14YOIaT7nDvOX3Iiw==",
+ "dev": true,
+ "license": "BSD-2-Clause"
+ },
+ "node_modules/gopd": {
+ "version": "1.2.0",
+ "resolved": "https://registry.npmjs.org/gopd/-/gopd-1.2.0.tgz",
+ "integrity": "sha512-ZUKRh6/kUFoAiTAtTYPZJ3hw9wNxx+BIBOijnlG9PnrJsCcSjs1wyyD6vJpaYtgnzDrKYRSqf3OO6Rfa93xsRg==",
+ "dev": true,
+ "license": "MIT",
+ "engines": {
+ "node": ">= 0.4"
+ },
+ "funding": {
+ "url": "https://github.com/sponsors/ljharb"
+ }
+ },
+ "node_modules/graceful-fs": {
+ "version": "4.2.11",
+ "resolved": "https://registry.npmjs.org/graceful-fs/-/graceful-fs-4.2.11.tgz",
+ "integrity": "sha512-RbJ5/jmFcNNCcDV5o9eTnBLJ/HszWV0P73bc+Ff4nS/rJj+YaS6IGyiOL0VoBYX+l1Wrl3k63h/KrH+nhJ0XvQ==",
+ "dev": true
+ },
+ "node_modules/graphql": {
+ "version": "16.8.1",
+ "resolved": "https://registry.npmjs.org/graphql/-/graphql-16.8.1.tgz",
+ "integrity": "sha512-59LZHPdGZVh695Ud9lRzPBVTtlX9ZCV150Er2W43ro37wVof0ctenSaskPPjN7lVTIN8mSZt8PHUNKZuNQUuxw==",
+ "license": "MIT",
+ "engines": {
+ "node": "^12.22.0 || ^14.16.0 || ^16.0.0 || >=17.0.0"
+ }
+ },
+ "node_modules/graphql-tag": {
+ "version": "2.12.6",
+ "resolved": "https://registry.npmjs.org/graphql-tag/-/graphql-tag-2.12.6.tgz",
+ "integrity": "sha512-FdSNcu2QQcWnM2VNvSCCDCVS5PpPqpzgFT8+GXzqJuoDd0CBncxCY278u4mhRO7tMgo2JjgJA5aZ+nWSQ/Z+xg==",
+ "license": "MIT",
+ "dependencies": {
+ "tslib": "^2.1.0"
+ },
+ "engines": {
+ "node": ">=10"
+ },
+ "peerDependencies": {
+ "graphql": "^0.9.0 || ^0.10.0 || ^0.11.0 || ^0.12.0 || ^0.13.0 || ^14.0.0 || ^15.0.0 || ^16.0.0"
+ }
+ },
+ "node_modules/has": {
+ "version": "1.0.3",
+ "resolved": "https://registry.npmjs.org/has/-/has-1.0.3.tgz",
+ "integrity": "sha512-f2dvO0VU6Oej7RkWJGrehjbzMAjFp5/VKPp5tTpWIV4JHHZK1/BxbFRtf/siA2SWTe09caDmVtYYzWEIbBS4zw==",
+ "dev": true,
+ "license": "MIT",
+ "dependencies": {
+ "function-bind": "^1.1.1"
+ },
+ "engines": {
+ "node": ">= 0.4.0"
+ }
+ },
+ "node_modules/has-flag": {
+ "version": "4.0.0",
+ "resolved": "https://registry.npmjs.org/has-flag/-/has-flag-4.0.0.tgz",
+ "integrity": "sha512-EykJT/Q1KjTWctppgIAgfSO0tKVuZUjhgMr17kqTumMl6Afv3EISleU7qZUzoXDFTAHTDC4NOoG/ZxU3EvlMPQ==",
+ "dev": true,
+ "license": "MIT",
+ "engines": {
+ "node": ">=8"
+ }
+ },
+ "node_modules/has-property-descriptors": {
+ "version": "1.0.2",
+ "resolved": "https://registry.npmjs.org/has-property-descriptors/-/has-property-descriptors-1.0.2.tgz",
+ "integrity": "sha512-55JNKuIW+vq4Ke1BjOTjM2YctQIvCT7GFzHwmfZPGo5wnrgkid0YQtnAleFSqumZm4az3n2BS+erby5ipJdgrg==",
+ "dev": true,
+ "license": "MIT",
+ "dependencies": {
+ "es-define-property": "^1.0.0"
+ },
+ "funding": {
+ "url": "https://github.com/sponsors/ljharb"
+ }
+ },
+ "node_modules/has-symbols": {
+ "version": "1.1.0",
+ "resolved": "https://registry.npmjs.org/has-symbols/-/has-symbols-1.1.0.tgz",
+ "integrity": "sha512-1cDNdwJ2Jaohmb3sg4OmKaMBwuC48sYni5HUw2DvsC8LjGTLK9h+eb1X6RyuOHe4hT0ULCW68iomhjUoKUqlPQ==",
+ "dev": true,
+ "license": "MIT",
+ "engines": {
+ "node": ">= 0.4"
+ },
+ "funding": {
+ "url": "https://github.com/sponsors/ljharb"
+ }
+ },
+ "node_modules/has-tostringtag": {
+ "version": "1.0.2",
+ "resolved": "https://registry.npmjs.org/has-tostringtag/-/has-tostringtag-1.0.2.tgz",
+ "integrity": "sha512-NqADB8VjPFLM2V0VvHUewwwsw0ZWBaIdgo+ieHtK3hasLz4qeCRjYcqfB6AQrBggRKppKF8L52/VqdVsO47Dlw==",
+ "dev": true,
+ "license": "MIT",
+ "dependencies": {
+ "has-symbols": "^1.0.3"
+ },
+ "engines": {
+ "node": ">= 0.4"
+ },
+ "funding": {
+ "url": "https://github.com/sponsors/ljharb"
+ }
+ },
+ "node_modules/hash-base": {
+ "version": "3.1.2",
+ "resolved": "https://registry.npmjs.org/hash-base/-/hash-base-3.1.2.tgz",
+ "integrity": "sha512-Bb33KbowVTIj5s7Ked1OsqHUeCpz//tPwR+E2zJgJKo9Z5XolZ9b6bdUgjmYlwnWhoOQKoTd1TYToZGn5mAYOg==",
+ "dev": true,
+ "license": "MIT",
+ "dependencies": {
+ "inherits": "^2.0.4",
+ "readable-stream": "^2.3.8",
+ "safe-buffer": "^5.2.1",
+ "to-buffer": "^1.2.1"
+ },
+ "engines": {
+ "node": ">= 0.8"
+ }
+ },
+ "node_modules/hash-base/node_modules/isarray": {
+ "version": "1.0.0",
+ "resolved": "https://registry.npmjs.org/isarray/-/isarray-1.0.0.tgz",
+ "integrity": "sha512-VLghIWNM6ELQzo7zwmcg0NmTVyWKYjvIeM83yjp0wRDTmUnrM678fQbcKBo6n2CJEF0szoG//ytg+TKla89ALQ==",
+ "dev": true,
+ "license": "MIT"
+ },
+ "node_modules/hash-base/node_modules/readable-stream": {
+ "version": "2.3.8",
+ "resolved": "https://registry.npmjs.org/readable-stream/-/readable-stream-2.3.8.tgz",
+ "integrity": "sha512-8p0AUk4XODgIewSi0l8Epjs+EVnWiK7NoDIEGU0HhE7+ZyY8D1IMY7odu5lRrFXGg71L15KG8QrPmum45RTtdA==",
+ "dev": true,
+ "license": "MIT",
+ "dependencies": {
+ "core-util-is": "~1.0.0",
+ "inherits": "~2.0.3",
+ "isarray": "~1.0.0",
+ "process-nextick-args": "~2.0.0",
+ "safe-buffer": "~5.1.1",
+ "string_decoder": "~1.1.1",
+ "util-deprecate": "~1.0.1"
+ }
+ },
+ "node_modules/hash-base/node_modules/readable-stream/node_modules/safe-buffer": {
+ "version": "5.1.2",
+ "resolved": "https://registry.npmjs.org/safe-buffer/-/safe-buffer-5.1.2.tgz",
+ "integrity": "sha512-Gd2UZBJDkXlY7GbJxfsE8/nvKkUEU1G38c1siN6QP6a9PT9MmHB8GnpscSmMJSoF8LOIrt8ud/wPtojys4G6+g==",
+ "dev": true,
+ "license": "MIT"
+ },
+ "node_modules/hash-base/node_modules/string_decoder": {
+ "version": "1.1.1",
+ "resolved": "https://registry.npmjs.org/string_decoder/-/string_decoder-1.1.1.tgz",
+ "integrity": "sha512-n/ShnvDi6FHbbVfviro+WojiFzv+s8MPMHBczVePfUpDJLwoLT0ht1l4YwBCbi8pJAveEEdnkHyPyTP/mzRfwg==",
+ "dev": true,
+ "license": "MIT",
+ "dependencies": {
+ "safe-buffer": "~5.1.0"
+ }
+ },
+ "node_modules/hash-base/node_modules/string_decoder/node_modules/safe-buffer": {
+ "version": "5.1.2",
+ "resolved": "https://registry.npmjs.org/safe-buffer/-/safe-buffer-5.1.2.tgz",
+ "integrity": "sha512-Gd2UZBJDkXlY7GbJxfsE8/nvKkUEU1G38c1siN6QP6a9PT9MmHB8GnpscSmMJSoF8LOIrt8ud/wPtojys4G6+g==",
+ "dev": true,
+ "license": "MIT"
+ },
+ "node_modules/hash.js": {
+ "version": "1.1.7",
+ "resolved": "https://registry.npmjs.org/hash.js/-/hash.js-1.1.7.tgz",
+ "integrity": "sha512-taOaskGt4z4SOANNseOviYDvjEJinIkRgmp7LbKP2YTTmVxWBl87s/uzK9r+44BclBSp2X7K1hqeNfz9JbBeXA==",
+ "dev": true,
+ "license": "MIT",
+ "dependencies": {
+ "inherits": "^2.0.3",
+ "minimalistic-assert": "^1.0.1"
+ }
+ },
+ "node_modules/hasown": {
+ "version": "2.0.2",
+ "resolved": "https://registry.npmjs.org/hasown/-/hasown-2.0.2.tgz",
+ "integrity": "sha512-0hJU9SCPvmMzIBdZFqNPXWa6dqh7WdH0cII9y+CyS8rG3nL48Bclra9HmKhVVUHyPWNH5Y7xDwAB7bfgSjkUMQ==",
+ "dev": true,
+ "license": "MIT",
+ "dependencies": {
+ "function-bind": "^1.1.2"
+ },
+ "engines": {
+ "node": ">= 0.4"
+ }
+ },
+ "node_modules/history": {
+ "version": "5.3.0",
+ "resolved": "https://registry.npmjs.org/history/-/history-5.3.0.tgz",
+ "integrity": "sha512-ZqaKwjjrAYUYfLG+htGaIIZ4nioX2L70ZUMIFysS3xvBsSG4x/n1V6TXV3N8ZYNuFGlDirFg32T7B6WOUPDYcQ==",
+ "license": "MIT",
+ "dependencies": {
+ "@babel/runtime": "^7.7.6"
+ }
+ },
+ "node_modules/hmac-drbg": {
+ "version": "1.0.1",
+ "resolved": "https://registry.npmjs.org/hmac-drbg/-/hmac-drbg-1.0.1.tgz",
+ "integrity": "sha1-0nRXAQJabHdabFRXk+1QL8DGSaE= sha512-Tti3gMqLdZfhOQY1Mzf/AanLiqh1WTiJgEj26ZuYQ9fbkLomzGchCws4FyrSd4VkpBfiNhaE1On+lOz894jvXg==",
+ "dev": true,
+ "license": "MIT",
+ "dependencies": {
+ "hash.js": "^1.0.3",
+ "minimalistic-assert": "^1.0.0",
+ "minimalistic-crypto-utils": "^1.0.1"
+ }
+ },
+ "node_modules/hoist-non-react-statics": {
+ "version": "3.3.2",
+ "resolved": "https://registry.npmjs.org/hoist-non-react-statics/-/hoist-non-react-statics-3.3.2.tgz",
+ "integrity": "sha512-/gGivxi8JPKWNm/W0jSmzcMPpfpPLc3dY/6GxhX2hQ9iGj3aDfklV4ET7NjKpSinLpJ5vafa9iiGIEZg10SfBw==",
+ "license": "BSD-3-Clause",
+ "dependencies": {
+ "react-is": "^16.7.0"
+ }
+ },
+ "node_modules/https-browserify": {
+ "version": "1.0.0",
+ "resolved": "https://registry.npmjs.org/https-browserify/-/https-browserify-1.0.0.tgz",
+ "integrity": "sha1-7AbBDgo0wPL68Zn3/X/Hj//QPHM= sha512-J+FkSdyD+0mA0N+81tMotaRMfSL9SGi+xpD3T6YApKsc3bGSXJlfXri3VyFOeYkfLRQisDk1W+jIFFKBeUBbBg==",
+ "dev": true,
+ "license": "MIT"
+ },
+ "node_modules/human-signals": {
+ "version": "2.1.0",
+ "resolved": "https://registry.npmjs.org/human-signals/-/human-signals-2.1.0.tgz",
+ "integrity": "sha512-B4FFZ6q/T2jhhksgkbEW3HBvWIfDW85snkQgawt07S7J5QXTk6BkNV+0yAeZrM5QpMAdYlocGoljn0sJ/WQkFw==",
+ "dev": true,
+ "license": "Apache-2.0",
+ "engines": {
+ "node": ">=10.17.0"
+ }
+ },
+ "node_modules/ieee754": {
+ "version": "1.2.1",
+ "resolved": "https://registry.npmjs.org/ieee754/-/ieee754-1.2.1.tgz",
+ "integrity": "sha512-dcyqhDvX1C46lXZcVqCpK+FtMRQVdIMN6/Df5js2zouUsqG7I6sFxitIC+7KYK29KdXOLHdu9zL4sFnoVQnqaA==",
+ "dev": true,
+ "funding": [
+ {
+ "type": "github",
+ "url": "https://github.com/sponsors/feross"
+ },
+ {
+ "type": "patreon",
+ "url": "https://www.patreon.com/feross"
+ },
+ {
+ "type": "consulting",
+ "url": "https://feross.org/support"
+ }
+ ],
+ "license": "BSD-3-Clause"
+ },
+ "node_modules/import-local": {
+ "version": "3.1.0",
+ "resolved": "https://registry.npmjs.org/import-local/-/import-local-3.1.0.tgz",
+ "integrity": "sha512-ASB07uLtnDs1o6EHjKpX34BKYDSqnFerfTOJL2HvMqF70LnxpjkzDB8J44oT9pu4AMPkQwf8jl6szgvNd2tRIg==",
+ "dev": true,
+ "license": "MIT",
+ "dependencies": {
+ "pkg-dir": "^4.2.0",
+ "resolve-cwd": "^3.0.0"
+ },
+ "bin": {
+ "import-local-fixture": "fixtures/cli.js"
+ },
+ "engines": {
+ "node": ">=8"
+ },
+ "funding": {
+ "url": "https://github.com/sponsors/sindresorhus"
+ }
+ },
+ "node_modules/inherits": {
+ "version": "2.0.4",
+ "resolved": "https://registry.npmjs.org/inherits/-/inherits-2.0.4.tgz",
+ "integrity": "sha512-k/vGaX4/Yla3WzyMCvTQOXYeIHvqOKtnqBduzTHpzpQZzAskKMhZ2K+EnBiSM9zGSoIFeMpXKxa4dYeZIQqewQ==",
+ "dev": true,
+ "license": "ISC"
+ },
+ "node_modules/interpret": {
+ "version": "2.2.0",
+ "resolved": "https://registry.npmjs.org/interpret/-/interpret-2.2.0.tgz",
+ "integrity": "sha512-Ju0Bz/cEia55xDwUWEa8+olFpCiQoypjnQySseKtmjNrnps3P+xfpUmGr90T7yjlVJmOtybRvPXhKMbHr+fWnw==",
+ "dev": true,
+ "license": "MIT",
+ "engines": {
+ "node": ">= 0.10"
+ }
+ },
+ "node_modules/is-arguments": {
+ "version": "1.1.1",
+ "resolved": "https://registry.npmjs.org/is-arguments/-/is-arguments-1.1.1.tgz",
+ "integrity": "sha512-8Q7EARjzEnKpt/PCD7e1cgUS0a6X8u5tdSiMqXhojOdoV9TsMsiO+9VLC5vAmO8N7/GmXn7yjR8qnA6bVAEzfA==",
+ "dev": true,
+ "license": "MIT",
+ "dependencies": {
+ "call-bind": "^1.0.2",
+ "has-tostringtag": "^1.0.0"
+ },
+ "engines": {
+ "node": ">= 0.4"
+ },
+ "funding": {
+ "url": "https://github.com/sponsors/ljharb"
+ }
+ },
+ "node_modules/is-callable": {
+ "version": "1.2.7",
+ "resolved": "https://registry.npmjs.org/is-callable/-/is-callable-1.2.7.tgz",
+ "integrity": "sha512-1BC0BVFhS/p0qtw6enp8e+8OD0UrK0oFLztSjNzhcKA3WDuJxxAPXzPuPtKkjEY9UUoEWlX/8fgKeu2S8i9JTA==",
+ "dev": true,
+ "license": "MIT",
+ "engines": {
+ "node": ">= 0.4"
+ },
+ "funding": {
+ "url": "https://github.com/sponsors/ljharb"
+ }
+ },
+ "node_modules/is-core-module": {
+ "version": "2.9.0",
+ "resolved": "https://registry.npmjs.org/is-core-module/-/is-core-module-2.9.0.tgz",
+ "integrity": "sha512-+5FPy5PnwmO3lvfMb0AsoPaBG+5KHUI0wYFXOtYPnVVVspTFUuMZNfNaNVRt3FZadstu2c8x23vykRW/NBoU6A==",
+ "dev": true,
+ "license": "MIT",
+ "dependencies": {
+ "has": "^1.0.3"
+ },
+ "funding": {
+ "url": "https://github.com/sponsors/ljharb"
+ }
+ },
+ "node_modules/is-generator-function": {
+ "version": "1.0.10",
+ "resolved": "https://registry.npmjs.org/is-generator-function/-/is-generator-function-1.0.10.tgz",
+ "integrity": "sha512-jsEjy9l3yiXEQ+PsXdmBwEPcOxaXWLspKdplFUVI9vq1iZgIekeC0L167qeu86czQaxed3q/Uzuw0swL0irL8A==",
+ "dev": true,
+ "license": "MIT",
+ "dependencies": {
+ "has-tostringtag": "^1.0.0"
+ },
+ "engines": {
+ "node": ">= 0.4"
+ },
+ "funding": {
+ "url": "https://github.com/sponsors/ljharb"
+ }
+ },
+ "node_modules/is-nan": {
+ "version": "1.3.2",
+ "resolved": "https://registry.npmjs.org/is-nan/-/is-nan-1.3.2.tgz",
+ "integrity": "sha512-E+zBKpQ2t6MEo1VsonYmluk9NxGrbzpeeLC2xIViuO2EjU2xsXsBPwTr3Ykv9l08UYEVEdWeRZNouaZqF6RN0w==",
+ "dev": true,
+ "license": "MIT",
+ "dependencies": {
+ "call-bind": "^1.0.0",
+ "define-properties": "^1.1.3"
+ },
+ "engines": {
+ "node": ">= 0.4"
+ },
+ "funding": {
+ "url": "https://github.com/sponsors/ljharb"
+ }
+ },
+ "node_modules/is-number": {
+ "version": "7.0.0",
+ "resolved": "https://registry.npmjs.org/is-number/-/is-number-7.0.0.tgz",
+ "integrity": "sha512-41Cifkg6e8TylSpdtTpeLVMqvSBEVzTttHvERD741+pnZ8ANv0004MRL43QKPDlK9cGvNp6NZWZUBlbGXYxxng==",
+ "dev": true,
+ "license": "MIT",
+ "engines": {
+ "node": ">=0.12.0"
+ }
+ },
+ "node_modules/is-stream": {
+ "version": "2.0.1",
+ "resolved": "https://registry.npmjs.org/is-stream/-/is-stream-2.0.1.tgz",
+ "integrity": "sha512-hFoiJiTl63nn+kstHGBtewWSKnQLpyb155KHheA1l39uvtO9nWIop1p3udqPcUd/xbF1VLMO4n7OI6p7RbngDg==",
+ "dev": true,
+ "license": "MIT",
+ "engines": {
+ "node": ">=8"
+ },
+ "funding": {
+ "url": "https://github.com/sponsors/sindresorhus"
+ }
+ },
+ "node_modules/is-typed-array": {
+ "version": "1.1.15",
+ "resolved": "https://registry.npmjs.org/is-typed-array/-/is-typed-array-1.1.15.tgz",
+ "integrity": "sha512-p3EcsicXjit7SaskXHs1hA91QxgTw46Fv6EFKKGS5DRFLD8yKnohjF3hxoju94b/OcMZoQukzpPpBE9uLVKzgQ==",
+ "dev": true,
+ "license": "MIT",
+ "dependencies": {
+ "which-typed-array": "^1.1.16"
+ },
+ "engines": {
+ "node": ">= 0.4"
+ },
+ "funding": {
+ "url": "https://github.com/sponsors/ljharb"
+ }
+ },
+ "node_modules/isarray": {
+ "version": "2.0.5",
+ "resolved": "https://registry.npmjs.org/isarray/-/isarray-2.0.5.tgz",
+ "integrity": "sha512-xHjhDr3cNBK0BzdUJSPXZntQUx/mwMS5Rw4A7lPJ90XGAO6ISP/ePDNuo0vhqOZU+UD5JoodwCAAoZQd3FeAKw==",
+ "dev": true,
+ "license": "MIT"
+ },
+ "node_modules/isexe": {
+ "version": "2.0.0",
+ "resolved": "https://registry.npmjs.org/isexe/-/isexe-2.0.0.tgz",
+ "integrity": "sha1-6PvzdNxVb/iUehDcsFctYz8s+hA= sha512-RHxMLp9lnKHGHRng9QFhRCMbYAcVpn69smSGcq3f36xjgVVWThj4qqLbTLlq7Ssj8B+fIQ1EuCEGI2lKsyQeIw==",
+ "dev": true,
+ "license": "ISC"
+ },
+ "node_modules/isobject": {
+ "version": "3.0.1",
+ "resolved": "https://registry.npmjs.org/isobject/-/isobject-3.0.1.tgz",
+ "integrity": "sha1-TkMekrEalzFjaqH5yNHMvP2reN8= sha512-WhB9zCku7EGTj/HQQRz5aUQEUeoQZH2bWcltRErOpymJ4boYE6wL9Tbr23krRPSZ+C5zqNSrSw+Cc7sZZ4b7vg==",
+ "dev": true,
+ "license": "MIT",
+ "engines": {
+ "node": ">=0.10.0"
+ }
+ },
+ "node_modules/jest-worker": {
+ "version": "27.5.1",
+ "resolved": "https://registry.npmjs.org/jest-worker/-/jest-worker-27.5.1.tgz",
+ "integrity": "sha512-7vuh85V5cdDofPyxn58nrPjBktZo0u9x1g8WtjQol+jZDaE+fhN+cIvTj11GndBnMnyfrUOG1sZQxCdjKh+DKg==",
+ "dev": true,
+ "license": "MIT",
+ "dependencies": {
+ "@types/node": "*",
+ "merge-stream": "^2.0.0",
+ "supports-color": "^8.0.0"
+ },
+ "engines": {
+ "node": ">= 10.13.0"
+ }
+ },
+ "node_modules/jest-worker/node_modules/supports-color": {
+ "version": "8.1.1",
+ "resolved": "https://registry.npmjs.org/supports-color/-/supports-color-8.1.1.tgz",
+ "integrity": "sha512-MpUEN2OodtUzxvKQl72cUF7RQ5EiHsGvSsVG0ia9c5RbWGL2CI4C7EpPS8UTBIplnlzZiNuV56w+FuNxy3ty2Q==",
+ "dev": true,
+ "license": "MIT",
+ "dependencies": {
+ "has-flag": "^4.0.0"
+ },
+ "engines": {
+ "node": ">=10"
+ },
+ "funding": {
+ "url": "https://github.com/chalk/supports-color?sponsor=1"
+ }
+ },
+ "node_modules/js-base64": {
+ "version": "3.7.2",
+ "resolved": "https://registry.npmjs.org/js-base64/-/js-base64-3.7.2.tgz",
+ "integrity": "sha512-NnRs6dsyqUXejqk/yv2aiXlAvOs56sLkX6nUdeaNezI5LFFLlsZjOThmwnrcwh5ZZRwZlCMnVAY3CvhIhoVEKQ==",
+ "license": "BSD-3-Clause"
+ },
+ "node_modules/js-tokens": {
+ "version": "4.0.0",
+ "resolved": "https://registry.npmjs.org/js-tokens/-/js-tokens-4.0.0.tgz",
+ "integrity": "sha512-RdJUflcE3cUzKiMqQgsCu06FPu9UdIJO0beYbPhHN4k6apgJtifcoCtT9bcxOpYBtpD2kCM6Sbzg4CausW/PKQ==",
+ "license": "MIT"
+ },
+ "node_modules/json-parse-even-better-errors": {
+ "version": "2.3.1",
+ "resolved": "https://registry.npmjs.org/json-parse-even-better-errors/-/json-parse-even-better-errors-2.3.1.tgz",
+ "integrity": "sha512-xyFwyhro/JEof6Ghe2iz2NcXoj2sloNsWr/XsERDK/oiPCfaNhl5ONfp+jQdAZRQQ0IJWNzH9zIZF7li91kh2w==",
+ "dev": true,
+ "license": "MIT"
+ },
+ "node_modules/json-schema-traverse": {
+ "version": "1.0.0",
+ "resolved": "https://registry.npmjs.org/json-schema-traverse/-/json-schema-traverse-1.0.0.tgz",
+ "integrity": "sha512-NM8/P9n3XjXhIZn1lLhkFaACTOURQXjWhV4BA/RnOv8xvgqtqpAX9IO4mRQxSx1Rlo4tqzeqb0sOlruaOy3dug==",
+ "dev": true,
+ "license": "MIT"
+ },
+ "node_modules/json-stable-stringify": {
+ "version": "1.0.1",
+ "resolved": "https://registry.npmjs.org/json-stable-stringify/-/json-stable-stringify-1.0.1.tgz",
+ "integrity": "sha1-mnWdOcXy/1A/1TAGRu1EX4jE+a8= sha512-i/J297TW6xyj7sDFa7AmBPkQvLIxWr2kKPWI26tXydnZrzVAocNqn5DMNT1Mzk0vit1V5UkRM7C1KdVNp7Lmcg==",
+ "license": "MIT",
+ "dependencies": {
+ "jsonify": "~0.0.0"
+ }
+ },
+ "node_modules/jsonify": {
+ "version": "0.0.0",
+ "resolved": "https://registry.npmjs.org/jsonify/-/jsonify-0.0.0.tgz",
+ "integrity": "sha1-LHS27kHZPKUbe1qu6PUDYx0lKnM= sha512-trvBk1ki43VZptdBI5rIlG4YOzyeH/WefQt5rj1grasPn4iiZWKet8nkgc4GlsAylaztn0qZfUYOiTsASJFdNA==",
+ "license": "Public Domain",
+ "engines": {
+ "node": "*"
+ }
+ },
+ "node_modules/kind-of": {
+ "version": "6.0.3",
+ "resolved": "https://registry.npmjs.org/kind-of/-/kind-of-6.0.3.tgz",
+ "integrity": "sha512-dcS1ul+9tmeD95T+x28/ehLgd9mENa3LsvDTtzm3vyBEO7RPptvAD+t44WVXaUjTBRcrpFeFlC8WCruUR456hw==",
+ "dev": true,
+ "license": "MIT",
+ "engines": {
+ "node": ">=0.10.0"
+ }
+ },
+ "node_modules/loader-runner": {
+ "version": "4.3.1",
+ "resolved": "https://registry.npmjs.org/loader-runner/-/loader-runner-4.3.1.tgz",
+ "integrity": "sha512-IWqP2SCPhyVFTBtRcgMHdzlf9ul25NwaFx4wCEH/KjAXuuHY4yNjvPXsBokp8jCB936PyWRaPKUNh8NvylLp2Q==",
+ "dev": true,
+ "license": "MIT",
+ "engines": {
+ "node": ">=6.11.5"
+ },
+ "funding": {
+ "type": "opencollective",
+ "url": "https://opencollective.com/webpack"
+ }
+ },
+ "node_modules/locate-path": {
+ "version": "5.0.0",
+ "resolved": "https://registry.npmjs.org/locate-path/-/locate-path-5.0.0.tgz",
+ "integrity": "sha512-t7hw9pI+WvuwNJXwk5zVHpyhIqzg2qTlklJOf0mVxGSbe3Fp2VieZcduNYjaLDoy6p9uGpQEGWG87WpMKlNq8g==",
+ "dev": true,
+ "license": "MIT",
+ "dependencies": {
+ "p-locate": "^4.1.0"
+ },
+ "engines": {
+ "node": ">=8"
+ }
+ },
+ "node_modules/loose-envify": {
+ "version": "1.4.0",
+ "resolved": "https://registry.npmjs.org/loose-envify/-/loose-envify-1.4.0.tgz",
+ "integrity": "sha512-lyuxPGr/Wfhrlem2CL/UcnUc1zcqKAImBDzukY7Y5F/yQiNdko6+fRLevlw1HgMySw7f611UIY408EtxRSoK3Q==",
+ "license": "MIT",
+ "dependencies": {
+ "js-tokens": "^3.0.0 || ^4.0.0"
+ },
+ "bin": {
+ "loose-envify": "cli.js"
+ }
+ },
+ "node_modules/lru-cache": {
+ "version": "6.0.0",
+ "resolved": "https://registry.npmjs.org/lru-cache/-/lru-cache-6.0.0.tgz",
+ "integrity": "sha512-Jo6dJ04CmSjuznwJSS3pUeWmd/H0ffTlkXXgwZi+eq1UCmqQwCh+eLsYOYCwY991i2Fah4h1BEMCx4qThGbsiA==",
+ "dev": true,
+ "license": "ISC",
+ "dependencies": {
+ "yallist": "^4.0.0"
+ },
+ "engines": {
+ "node": ">=10"
+ }
+ },
+ "node_modules/match-sorter": {
+ "version": "6.3.1",
+ "resolved": "https://registry.npmjs.org/match-sorter/-/match-sorter-6.3.1.tgz",
+ "integrity": "sha512-mxybbo3pPNuA+ZuCUhm5bwNkXrJTbsk5VWbR5wiwz/GC6LIiegBGn2w3O08UG/jdbYLinw51fSQ5xNU1U3MgBw==",
+ "license": "MIT",
+ "dependencies": {
+ "@babel/runtime": "^7.12.5",
+ "remove-accents": "0.4.2"
+ }
+ },
+ "node_modules/math-intrinsics": {
+ "version": "1.1.0",
+ "resolved": "https://registry.npmjs.org/math-intrinsics/-/math-intrinsics-1.1.0.tgz",
+ "integrity": "sha512-/IXtbwEk5HTPyEwyKX6hGkYXxM9nbj64B+ilVJnC/R6B0pH5G4V3b0pVbL7DBj4tkhBAppbQUlf6F6Xl9LHu1g==",
+ "dev": true,
+ "license": "MIT",
+ "engines": {
+ "node": ">= 0.4"
+ }
+ },
+ "node_modules/md5.js": {
+ "version": "1.3.5",
+ "resolved": "https://registry.npmjs.org/md5.js/-/md5.js-1.3.5.tgz",
+ "integrity": "sha512-xitP+WxNPcTTOgnTJcrhM0xvdPepipPSf3I8EIpGKeFLjt3PlJLIDG3u8EX53ZIubkb+5U2+3rELYpEhHhzdkg==",
+ "dev": true,
+ "license": "MIT",
+ "dependencies": {
+ "hash-base": "^3.0.0",
+ "inherits": "^2.0.1",
+ "safe-buffer": "^5.1.2"
+ }
+ },
+ "node_modules/merge-stream": {
+ "version": "2.0.0",
+ "resolved": "https://registry.npmjs.org/merge-stream/-/merge-stream-2.0.0.tgz",
+ "integrity": "sha512-abv/qOcuPfk3URPfDzmZU1LKmuw8kT+0nIHvKrKgFrwifol/doWcdA4ZqsWQ8ENrFKkd67Mfpo/LovbIUsbt3w==",
+ "dev": true,
+ "license": "MIT"
+ },
+ "node_modules/micromatch": {
+ "version": "4.0.8",
+ "resolved": "https://registry.npmjs.org/micromatch/-/micromatch-4.0.8.tgz",
+ "integrity": "sha512-PXwfBhYu0hBCPw8Dn0E+WDYb7af3dSLVWKi3HGv84IdF4TyFoC0ysxFd0Goxw7nSv4T/PzEJQxsYsEiFCKo2BA==",
+ "dev": true,
+ "license": "MIT",
+ "dependencies": {
+ "braces": "^3.0.3",
+ "picomatch": "^2.3.1"
+ },
+ "engines": {
+ "node": ">=8.6"
+ }
+ },
+ "node_modules/miller-rabin": {
+ "version": "4.0.1",
+ "resolved": "https://registry.npmjs.org/miller-rabin/-/miller-rabin-4.0.1.tgz",
+ "integrity": "sha512-115fLhvZVqWwHPbClyntxEVfVDfl9DLLTuJvq3g2O/Oxi8AiNouAHvDSzHS0viUJc+V5vm3eq91Xwqn9dp4jRA==",
+ "dev": true,
+ "license": "MIT",
+ "dependencies": {
+ "bn.js": "^4.0.0",
+ "brorand": "^1.0.1"
+ },
+ "bin": {
+ "miller-rabin": "bin/miller-rabin"
+ }
+ },
+ "node_modules/miller-rabin/node_modules/bn.js": {
+ "version": "4.12.0",
+ "resolved": "https://registry.npmjs.org/bn.js/-/bn.js-4.12.0.tgz",
+ "integrity": "sha512-c98Bf3tPniI+scsdk237ku1Dc3ujXQTSgyiPUDEOe7tRkhrqridvh8klBv0HCEso1OLOYcHuCv/cS6DNxKH+ZA==",
+ "dev": true,
+ "license": "MIT"
+ },
+ "node_modules/mime-db": {
+ "version": "1.52.0",
+ "resolved": "https://registry.npmjs.org/mime-db/-/mime-db-1.52.0.tgz",
+ "integrity": "sha512-sPU4uV7dYlvtWJxwwxHD0PuihVNiE7TyAbQ5SWxDCB9mUYvOgroQOwYQQOKPJ8CIbE+1ETVlOoK1UC2nU3gYvg==",
+ "dev": true,
+ "license": "MIT",
+ "engines": {
+ "node": ">= 0.6"
+ }
+ },
+ "node_modules/mime-types": {
+ "version": "2.1.35",
+ "resolved": "https://registry.npmjs.org/mime-types/-/mime-types-2.1.35.tgz",
+ "integrity": "sha512-ZDY+bPm5zTTF+YpCrAU9nK0UgICYPT0QtT1NZWFv4s++TNkcgVaT0g6+4R2uI4MjQjzysHB1zxuWL50hzaeXiw==",
+ "dev": true,
+ "license": "MIT",
+ "dependencies": {
+ "mime-db": "1.52.0"
+ },
+ "engines": {
+ "node": ">= 0.6"
+ }
+ },
+ "node_modules/mimic-fn": {
+ "version": "2.1.0",
+ "resolved": "https://registry.npmjs.org/mimic-fn/-/mimic-fn-2.1.0.tgz",
+ "integrity": "sha512-OqbOk5oEQeAZ8WXWydlu9HJjz9WVdEIvamMCcXmuqUYjTknH/sqsWvhQ3vgwKFRR1HpjvNBKQ37nbJgYzGqGcg==",
+ "dev": true,
+ "license": "MIT",
+ "engines": {
+ "node": ">=6"
+ }
+ },
+ "node_modules/minimalistic-assert": {
+ "version": "1.0.1",
+ "resolved": "https://registry.npmjs.org/minimalistic-assert/-/minimalistic-assert-1.0.1.tgz",
+ "integrity": "sha512-UtJcAD4yEaGtjPezWuO9wC4nwUnVH/8/Im3yEHQP4b67cXlD/Qr9hdITCU1xDbSEXg2XKNaP8jsReV7vQd00/A==",
+ "dev": true,
+ "license": "ISC"
+ },
+ "node_modules/minimalistic-crypto-utils": {
+ "version": "1.0.1",
+ "resolved": "https://registry.npmjs.org/minimalistic-crypto-utils/-/minimalistic-crypto-utils-1.0.1.tgz",
+ "integrity": "sha1-9sAMHAsIIkblxNmd+4x8CDsrWCo= sha512-JIYlbt6g8i5jKfJ3xz7rF0LXmv2TkDxBLUkiBeZ7bAx4GnnNMr8xFpGnOxn6GhTEHx3SjRrZEoU+j04prX1ktg==",
+ "dev": true,
+ "license": "MIT"
+ },
+ "node_modules/neo-async": {
+ "version": "2.6.2",
+ "resolved": "https://registry.npmjs.org/neo-async/-/neo-async-2.6.2.tgz",
+ "integrity": "sha512-Yd3UES5mWCSqR+qNT93S3UoYUkqAZ9lLg8a7g9rimsWmYGK8cVToA4/sF3RrshdyV3sAGMXVUmpMYOw+dLpOuw==",
+ "dev": true,
+ "license": "MIT"
+ },
+ "node_modules/node-polyfill-webpack-plugin": {
+ "version": "1.1.4",
+ "resolved": "https://registry.npmjs.org/node-polyfill-webpack-plugin/-/node-polyfill-webpack-plugin-1.1.4.tgz",
+ "integrity": "sha512-Z0XTKj1wRWO8o/Vjobsw5iOJCN+Sua3EZEUc2Ziy9CyVvmHKu6o+t4gUH9GOE0czyPR94LI6ZCV/PpcM8b5yow==",
+ "dev": true,
+ "license": "MIT",
+ "dependencies": {
+ "assert": "^2.0.0",
+ "browserify-zlib": "^0.2.0",
+ "buffer": "^6.0.3",
+ "console-browserify": "^1.2.0",
+ "constants-browserify": "^1.0.0",
+ "crypto-browserify": "^3.12.0",
+ "domain-browser": "^4.19.0",
+ "events": "^3.3.0",
+ "filter-obj": "^2.0.2",
+ "https-browserify": "^1.0.0",
+ "os-browserify": "^0.3.0",
+ "path-browserify": "^1.0.1",
+ "process": "^0.11.10",
+ "punycode": "^2.1.1",
+ "querystring-es3": "^0.2.1",
+ "readable-stream": "^3.6.0",
+ "stream-browserify": "^3.0.0",
+ "stream-http": "^3.2.0",
+ "string_decoder": "^1.3.0",
+ "timers-browserify": "^2.0.12",
+ "tty-browserify": "^0.0.1",
+ "url": "^0.11.0",
+ "util": "^0.12.4",
+ "vm-browserify": "^1.1.2"
+ },
+ "engines": {
+ "node": ">=10"
+ },
+ "peerDependencies": {
+ "webpack": ">=5"
+ }
+ },
+ "node_modules/node-releases": {
+ "version": "2.0.27",
+ "resolved": "https://registry.npmjs.org/node-releases/-/node-releases-2.0.27.tgz",
+ "integrity": "sha512-nmh3lCkYZ3grZvqcCH+fjmQ7X+H0OeZgP40OierEaAptX4XofMh5kwNbWh7lBduUzCcV/8kZ+NDLCwm2iorIlA==",
+ "dev": true,
+ "license": "MIT"
+ },
+ "node_modules/npm-run-path": {
+ "version": "4.0.1",
+ "resolved": "https://registry.npmjs.org/npm-run-path/-/npm-run-path-4.0.1.tgz",
+ "integrity": "sha512-S48WzZW777zhNIrn7gxOlISNAqi9ZC/uQFnRdbeIHhZhCA6UqpkOT8T1G7BvfdgP4Er8gF4sUbaS0i7QvIfCWw==",
+ "dev": true,
+ "license": "MIT",
+ "dependencies": {
+ "path-key": "^3.0.0"
+ },
+ "engines": {
+ "node": ">=8"
+ }
+ },
+ "node_modules/object-assign": {
+ "version": "4.1.1",
+ "resolved": "https://registry.npmjs.org/object-assign/-/object-assign-4.1.1.tgz",
+ "integrity": "sha512-rJgTQnkUnH1sFw8yT6VSU3zD3sWmu6sZhIseY8VX+GRu3P6F7Fu+JNDoXfklElbLJSnc3FUQHVe4cU5hj+BcUg==",
+ "license": "MIT",
+ "engines": {
+ "node": ">=0.10.0"
+ }
+ },
+ "node_modules/object-is": {
+ "version": "1.1.5",
+ "resolved": "https://registry.npmjs.org/object-is/-/object-is-1.1.5.tgz",
+ "integrity": "sha512-3cyDsyHgtmi7I7DfSSI2LDp6SK2lwvtbg0p0R1e0RvTqF5ceGx+K2dfSjm1bKDMVCFEDAQvy+o8c6a7VujOddw==",
+ "dev": true,
+ "license": "MIT",
+ "dependencies": {
+ "call-bind": "^1.0.2",
+ "define-properties": "^1.1.3"
+ },
+ "engines": {
+ "node": ">= 0.4"
+ },
+ "funding": {
+ "url": "https://github.com/sponsors/ljharb"
+ }
+ },
+ "node_modules/object-keys": {
+ "version": "1.1.1",
+ "resolved": "https://registry.npmjs.org/object-keys/-/object-keys-1.1.1.tgz",
+ "integrity": "sha512-NuAESUOUMrlIXOfHKzD6bpPu3tYt3xvjNdRIQ+FeT0lNb4K8WR70CaDxhuNguS2XG+GjkyMwOzsN5ZktImfhLA==",
+ "dev": true,
+ "license": "MIT",
+ "engines": {
+ "node": ">= 0.4"
+ }
+ },
+ "node_modules/onetime": {
+ "version": "5.1.2",
+ "resolved": "https://registry.npmjs.org/onetime/-/onetime-5.1.2.tgz",
+ "integrity": "sha512-kbpaSSGJTWdAY5KPVeMOKXSrPtr8C8C7wodJbcsd51jRnmD+GZu8Y0VoU6Dm5Z4vWr0Ig/1NKuWRKf7j5aaYSg==",
+ "dev": true,
+ "license": "MIT",
+ "dependencies": {
+ "mimic-fn": "^2.1.0"
+ },
+ "engines": {
+ "node": ">=6"
+ },
+ "funding": {
+ "url": "https://github.com/sponsors/sindresorhus"
+ }
+ },
+ "node_modules/optimism": {
+ "version": "0.18.1",
+ "resolved": "https://registry.npmjs.org/optimism/-/optimism-0.18.1.tgz",
+ "integrity": "sha512-mLXNwWPa9dgFyDqkNi54sjDyNJ9/fTI6WGBLgnXku1vdKY/jovHfZT5r+aiVeFFLOz+foPNOm5YJ4mqgld2GBQ==",
+ "license": "MIT",
+ "dependencies": {
+ "@wry/caches": "^1.0.0",
+ "@wry/context": "^0.7.0",
+ "@wry/trie": "^0.5.0",
+ "tslib": "^2.3.0"
+ }
+ },
+ "node_modules/os-browserify": {
+ "version": "0.3.0",
+ "resolved": "https://registry.npmjs.org/os-browserify/-/os-browserify-0.3.0.tgz",
+ "integrity": "sha1-hUNzx/XCMVkU/Jv8a9gjj92h7Cc= sha512-gjcpUc3clBf9+210TRaDWbf+rZZZEshZ+DlXMRCeAjp0xhTrnQsKHypIy1J3d5hKdUzj69t708EHtU8P6bUn0A==",
+ "dev": true,
+ "license": "MIT"
+ },
+ "node_modules/p-finally": {
+ "version": "3.0.0",
+ "resolved": "https://registry.npmjs.org/p-finally/-/p-finally-3.0.0.tgz",
+ "integrity": "sha512-zvzo4d4xg+IQiZTakibWdwAh+LDYqM/iJO18lwtWbMyDOnOBIWY+AMDvOrP4eQshuCKXRi9R9CTXWFh4R0wOEw==",
+ "deprecated": "Deprecated",
+ "license": "MIT",
+ "engines": {
+ "node": ">=12"
+ },
+ "funding": {
+ "url": "https://github.com/sponsors/sindresorhus"
+ }
+ },
+ "node_modules/p-limit": {
+ "version": "2.3.0",
+ "resolved": "https://registry.npmjs.org/p-limit/-/p-limit-2.3.0.tgz",
+ "integrity": "sha512-//88mFWSJx8lxCzwdAABTJL2MyWB12+eIY7MDL2SqLmAkeKU9qxRvWuSyTjm3FUmpBEMuFfckAIqEaVGUDxb6w==",
+ "dev": true,
+ "license": "MIT",
+ "dependencies": {
+ "p-try": "^2.0.0"
+ },
+ "engines": {
+ "node": ">=6"
+ },
+ "funding": {
+ "url": "https://github.com/sponsors/sindresorhus"
+ }
+ },
+ "node_modules/p-locate": {
+ "version": "4.1.0",
+ "resolved": "https://registry.npmjs.org/p-locate/-/p-locate-4.1.0.tgz",
+ "integrity": "sha512-R79ZZ/0wAxKGu3oYMlz8jy/kbhsNrS7SKZ7PxEHBgJ5+F2mtFW2fK2cOtBh1cHYkQsbzFV7I+EoRKe6Yt0oK7A==",
+ "dev": true,
+ "license": "MIT",
+ "dependencies": {
+ "p-limit": "^2.2.0"
+ },
+ "engines": {
+ "node": ">=8"
+ }
+ },
+ "node_modules/p-try": {
+ "version": "2.2.0",
+ "resolved": "https://registry.npmjs.org/p-try/-/p-try-2.2.0.tgz",
+ "integrity": "sha512-R4nPAVTAU0B9D35/Gk3uJf/7XYbQcyohSKdvAxIRSNghFl4e71hVoGnBNQz9cWaXxO2I10KTC+3jMdvvoKw6dQ==",
+ "dev": true,
+ "license": "MIT",
+ "engines": {
+ "node": ">=6"
+ }
+ },
+ "node_modules/pako": {
+ "version": "1.0.11",
+ "resolved": "https://registry.npmjs.org/pako/-/pako-1.0.11.tgz",
+ "integrity": "sha512-4hLB8Py4zZce5s4yd9XzopqwVv/yGNhV1Bl8NTmCq1763HeK2+EwVTv+leGeL13Dnh2wfbqowVPXCIO0z4taYw==",
+ "dev": true,
+ "license": "(MIT AND Zlib)"
+ },
+ "node_modules/parse-asn1": {
+ "version": "5.1.9",
+ "resolved": "https://registry.npmjs.org/parse-asn1/-/parse-asn1-5.1.9.tgz",
+ "integrity": "sha512-fIYNuZ/HastSb80baGOuPRo1O9cf4baWw5WsAp7dBuUzeTD/BoaG8sVTdlPFksBE2lF21dN+A1AnrpIjSWqHHg==",
+ "dev": true,
+ "license": "ISC",
+ "dependencies": {
+ "asn1.js": "^4.10.1",
+ "browserify-aes": "^1.2.0",
+ "evp_bytestokey": "^1.0.3",
+ "pbkdf2": "^3.1.5",
+ "safe-buffer": "^5.2.1"
+ },
+ "engines": {
+ "node": ">= 0.10"
+ }
+ },
+ "node_modules/path-browserify": {
+ "version": "1.0.1",
+ "resolved": "https://registry.npmjs.org/path-browserify/-/path-browserify-1.0.1.tgz",
+ "integrity": "sha512-b7uo2UCUOYZcnF/3ID0lulOJi/bafxa1xPe7ZPsammBSpjSWQkjNxlt635YGS2MiR9GjvuXCtz2emr3jbsz98g==",
+ "dev": true,
+ "license": "MIT"
+ },
+ "node_modules/path-exists": {
+ "version": "4.0.0",
+ "resolved": "https://registry.npmjs.org/path-exists/-/path-exists-4.0.0.tgz",
+ "integrity": "sha512-ak9Qy5Q7jYb2Wwcey5Fpvg2KoAc/ZIhLSLOSBmRmygPsGwkVVt0fZa0qrtMz+m6tJTAHfZQ8FnmB4MG4LWy7/w==",
+ "dev": true,
+ "license": "MIT",
+ "engines": {
+ "node": ">=8"
+ }
+ },
+ "node_modules/path-key": {
+ "version": "3.1.1",
+ "resolved": "https://registry.npmjs.org/path-key/-/path-key-3.1.1.tgz",
+ "integrity": "sha512-ojmeN0qd+y0jszEtoY48r0Peq5dwMEkIlCOu6Q5f41lfkswXuKtYrhgoTpLnyIcHm24Uhqx+5Tqm2InSwLhE6Q==",
+ "dev": true,
+ "license": "MIT",
+ "engines": {
+ "node": ">=8"
+ }
+ },
+ "node_modules/path-parse": {
+ "version": "1.0.7",
+ "resolved": "https://registry.npmjs.org/path-parse/-/path-parse-1.0.7.tgz",
+ "integrity": "sha512-LDJzPVEEEPR+y48z93A0Ed0yXb8pAByGWo/k5YYdYgpY2/2EsOsksJrq7lOHxryrVOn1ejG6oAp8ahvOIQD8sw==",
+ "dev": true,
+ "license": "MIT"
+ },
+ "node_modules/pbkdf2": {
+ "version": "3.1.5",
+ "resolved": "https://registry.npmjs.org/pbkdf2/-/pbkdf2-3.1.5.tgz",
+ "integrity": "sha512-Q3CG/cYvCO1ye4QKkuH7EXxs3VC/rI1/trd+qX2+PolbaKG0H+bgcZzrTt96mMyRtejk+JMCiLUn3y29W8qmFQ==",
+ "dev": true,
+ "license": "MIT",
+ "dependencies": {
+ "create-hash": "^1.2.0",
+ "create-hmac": "^1.1.7",
+ "ripemd160": "^2.0.3",
+ "safe-buffer": "^5.2.1",
+ "sha.js": "^2.4.12",
+ "to-buffer": "^1.2.1"
+ },
+ "engines": {
+ "node": ">= 0.10"
+ }
+ },
+ "node_modules/picocolors": {
+ "version": "1.1.1",
+ "resolved": "https://registry.npmjs.org/picocolors/-/picocolors-1.1.1.tgz",
+ "integrity": "sha512-xceH2snhtb5M9liqDsmEw56le376mTZkEX/jEb/RxNFyegNul7eNslCXP9FDj/Lcu0X8KEyMceP2ntpaHrDEVA==",
+ "dev": true,
+ "license": "ISC"
+ },
+ "node_modules/picomatch": {
+ "version": "2.3.2",
+ "resolved": "https://registry.npmjs.org/picomatch/-/picomatch-2.3.2.tgz",
+ "integrity": "sha512-V7+vQEJ06Z+c5tSye8S+nHUfI51xoXIXjHQ99cQtKUkQqqO1kO/KCJUfZXuB47h/YBlDhah2H3hdUGXn8ie0oA==",
+ "dev": true,
+ "license": "MIT",
+ "engines": {
+ "node": ">=8.6"
+ },
+ "funding": {
+ "url": "https://github.com/sponsors/jonschlinkert"
+ }
+ },
+ "node_modules/pkg-dir": {
+ "version": "4.2.0",
+ "resolved": "https://registry.npmjs.org/pkg-dir/-/pkg-dir-4.2.0.tgz",
+ "integrity": "sha512-HRDzbaKjC+AOWVXxAU/x54COGeIv9eb+6CkDSQoNTt4XyWoIJvuPsXizxu/Fr23EiekbtZwmh1IcIG/l/a10GQ==",
+ "dev": true,
+ "license": "MIT",
+ "dependencies": {
+ "find-up": "^4.0.0"
+ },
+ "engines": {
+ "node": ">=8"
+ }
+ },
+ "node_modules/possible-typed-array-names": {
+ "version": "1.1.0",
+ "resolved": "https://registry.npmjs.org/possible-typed-array-names/-/possible-typed-array-names-1.1.0.tgz",
+ "integrity": "sha512-/+5VFTchJDoVj3bhoqi6UeymcD00DAwb1nJwamzPvHEszJ4FpF6SNNbUbOS8yI56qHzdV8eK0qEfOSiodkTdxg==",
+ "dev": true,
+ "license": "MIT",
+ "engines": {
+ "node": ">= 0.4"
+ }
+ },
+ "node_modules/process": {
+ "version": "0.11.10",
+ "resolved": "https://registry.npmjs.org/process/-/process-0.11.10.tgz",
+ "integrity": "sha1-czIwDoQBYb2j5podHZGn1LwW8YI= sha512-cdGef/drWFoydD1JsMzuFf8100nZl+GT+yacc2bEced5f9Rjk4z+WtFUTBu9PhOi9j/jfmBPu0mMEY4wIdAF8A==",
+ "license": "MIT",
+ "engines": {
+ "node": ">= 0.6.0"
+ }
+ },
+ "node_modules/process-nextick-args": {
+ "version": "2.0.1",
+ "resolved": "https://registry.npmjs.org/process-nextick-args/-/process-nextick-args-2.0.1.tgz",
+ "integrity": "sha512-3ouUOpQhtgrbOa17J7+uxOTpITYWaGP7/AhoR3+A+/1e9skrzelGi/dXzEYyvbxubEF6Wn2ypscTKiKJFFn1ag==",
+ "dev": true,
+ "license": "MIT"
+ },
+ "node_modules/prop-types": {
+ "version": "15.8.1",
+ "resolved": "https://registry.npmjs.org/prop-types/-/prop-types-15.8.1.tgz",
+ "integrity": "sha512-oj87CgZICdulUohogVAR7AjlC0327U4el4L6eAvOqCeudMDVU0NThNaV+b9Df4dXgSP1gXMTnPdhfe/2qDH5cg==",
+ "license": "MIT",
+ "dependencies": {
+ "loose-envify": "^1.4.0",
+ "object-assign": "^4.1.1",
+ "react-is": "^16.13.1"
+ }
+ },
+ "node_modules/public-encrypt": {
+ "version": "4.0.3",
+ "resolved": "https://registry.npmjs.org/public-encrypt/-/public-encrypt-4.0.3.tgz",
+ "integrity": "sha512-zVpa8oKZSz5bTMTFClc1fQOnyyEzpl5ozpi1B5YcvBrdohMjH2rfsBtyXcuNuwjsDIXmBYlF2N5FlJYhR29t8Q==",
+ "dev": true,
+ "license": "MIT",
+ "dependencies": {
+ "bn.js": "^4.1.0",
+ "browserify-rsa": "^4.0.0",
+ "create-hash": "^1.1.0",
+ "parse-asn1": "^5.0.0",
+ "randombytes": "^2.0.1",
+ "safe-buffer": "^5.1.2"
+ }
+ },
+ "node_modules/public-encrypt/node_modules/bn.js": {
+ "version": "4.12.0",
+ "resolved": "https://registry.npmjs.org/bn.js/-/bn.js-4.12.0.tgz",
+ "integrity": "sha512-c98Bf3tPniI+scsdk237ku1Dc3ujXQTSgyiPUDEOe7tRkhrqridvh8klBv0HCEso1OLOYcHuCv/cS6DNxKH+ZA==",
+ "dev": true,
+ "license": "MIT"
+ },
+ "node_modules/punycode": {
+ "version": "2.1.1",
+ "resolved": "https://registry.npmjs.org/punycode/-/punycode-2.1.1.tgz",
+ "integrity": "sha512-XRsRjdf+j5ml+y/6GKHPZbrF/8p2Yga0JPtdqTIY2Xe5ohJPD9saDJJLPvp9+NSBprVvevdXZybnj2cv8OEd0A==",
+ "dev": true,
+ "license": "MIT",
+ "engines": {
+ "node": ">=6"
+ }
+ },
+ "node_modules/query-string": {
+ "version": "9.5.0",
+ "resolved": "https://registry.npmjs.org/query-string/-/query-string-9.5.0.tgz",
+ "integrity": "sha512-YlJmwNyi0RGYjlxYcuDncMsxFU7YyutbuI7gTm8ySxIGBlwx5yiBCOD5ig9ZNoHkawk/1Dey0N5mEfcUybMVAA==",
+ "license": "MIT",
+ "dependencies": {
+ "decode-uri-component": "^0.5.0",
+ "filter-obj": "^5.1.0",
+ "split-on-first": "^3.0.0"
+ },
+ "engines": {
+ "node": ">=18"
+ },
+ "funding": {
+ "url": "https://github.com/sponsors/sindresorhus"
+ }
+ },
+ "node_modules/query-string/node_modules/filter-obj": {
+ "version": "5.1.0",
+ "resolved": "https://registry.npmjs.org/filter-obj/-/filter-obj-5.1.0.tgz",
+ "integrity": "sha512-qWeTREPoT7I0bifpPUXtxkZJ1XJzxWtfoWWkdVGqa+eCr3SHW/Ocp89o8vLvbUuQnadybJpjOKu4V+RwO6sGng==",
+ "license": "MIT",
+ "engines": {
+ "node": ">=14.16"
+ },
+ "funding": {
+ "url": "https://github.com/sponsors/sindresorhus"
+ }
+ },
+ "node_modules/querystring": {
+ "version": "0.2.0",
+ "resolved": "https://registry.npmjs.org/querystring/-/querystring-0.2.0.tgz",
+ "integrity": "sha1-sgmEkgO7Jd+CDadW50cAWHhSFiA= sha512-X/xY82scca2tau62i9mDyU9K+I+djTMUsvwf7xnUX5GLvVzgJybOJf4Y6o9Zx3oJK/LSXg5tTZBjwzqVPaPO2g==",
+ "deprecated": "The querystring API is considered Legacy. new code should use the URLSearchParams API instead.",
+ "dev": true,
+ "engines": {
+ "node": ">=0.4.x"
+ }
+ },
+ "node_modules/querystring-es3": {
+ "version": "0.2.1",
+ "resolved": "https://registry.npmjs.org/querystring-es3/-/querystring-es3-0.2.1.tgz",
+ "integrity": "sha1-nsYfeQSYdXB9aUFFlv2Qek1xHnM= sha512-773xhDQnZBMFobEiztv8LIl70ch5MSF/jUQVlhwFyBILqq96anmoctVIYz+ZRp0qbCKATTn6ev02M3r7Ga5vqA==",
+ "dev": true,
+ "engines": {
+ "node": ">=0.4.x"
+ }
+ },
+ "node_modules/randombytes": {
+ "version": "2.1.0",
+ "resolved": "https://registry.npmjs.org/randombytes/-/randombytes-2.1.0.tgz",
+ "integrity": "sha512-vYl3iOX+4CKUWuxGi9Ukhie6fsqXqS9FE2Zaic4tNFD2N2QQaXOMFbuKK4QmDHC0JO6B1Zp41J0LpT0oR68amQ==",
+ "dev": true,
+ "license": "MIT",
+ "dependencies": {
+ "safe-buffer": "^5.1.0"
+ }
+ },
+ "node_modules/randomfill": {
+ "version": "1.0.4",
+ "resolved": "https://registry.npmjs.org/randomfill/-/randomfill-1.0.4.tgz",
+ "integrity": "sha512-87lcbR8+MhcWcUiQ+9e+Rwx8MyR2P7qnt15ynUlbm3TU/fjbgz4GsvfSUDTemtCCtVCqb4ZcEFlyPNTh9bBTLw==",
+ "dev": true,
+ "license": "MIT",
+ "dependencies": {
+ "randombytes": "^2.0.5",
+ "safe-buffer": "^5.1.0"
+ }
+ },
+ "node_modules/react-is": {
+ "version": "16.13.1",
+ "resolved": "https://registry.npmjs.org/react-is/-/react-is-16.13.1.tgz",
+ "integrity": "sha512-24e6ynE2H+OKt4kqsOvNd8kBpV65zoxbA4BVsEOB3ARVWQki/DHzaUoC5KuON/BiccDaCCTZBuOcfZs70kR8bQ==",
+ "license": "MIT"
+ },
+ "node_modules/readable-stream": {
+ "version": "3.6.2",
+ "resolved": "https://registry.npmjs.org/readable-stream/-/readable-stream-3.6.2.tgz",
+ "integrity": "sha512-9u/sniCrY3D5WdsERHzHE4G2YCXqoG5FTHUiCC4SIbr6XcLZBY05ya9EKjYek9O5xOAwjGq+1JdGBAS7Q9ScoA==",
+ "dev": true,
+ "license": "MIT",
+ "dependencies": {
+ "inherits": "^2.0.3",
+ "string_decoder": "^1.1.1",
+ "util-deprecate": "^1.0.1"
+ },
+ "engines": {
+ "node": ">= 6"
+ }
+ },
+ "node_modules/rechoir": {
+ "version": "0.7.1",
+ "resolved": "https://registry.npmjs.org/rechoir/-/rechoir-0.7.1.tgz",
+ "integrity": "sha512-/njmZ8s1wVeR6pjTZ+0nCnv8SpZNRMT2D1RLOJQESlYFDBvwpTA4KWJpZ+sBJ4+vhjILRcK7JIFdGCdxEAAitg==",
+ "dev": true,
+ "license": "MIT",
+ "dependencies": {
+ "resolve": "^1.9.0"
+ },
+ "engines": {
+ "node": ">= 0.10"
+ }
+ },
+ "node_modules/regenerator-runtime": {
+ "version": "0.14.1",
+ "resolved": "https://registry.npmjs.org/regenerator-runtime/-/regenerator-runtime-0.14.1.tgz",
+ "integrity": "sha512-dYnhHh0nJoMfnkZs6GmmhFknAGRrLznOu5nc9ML+EJxGvrx6H7teuevqVqCuPcPK//3eDrrjQhehXVx9cnkGdw==",
+ "license": "MIT"
+ },
+ "node_modules/rehackt": {
+ "version": "0.1.0",
+ "resolved": "https://registry.npmjs.org/rehackt/-/rehackt-0.1.0.tgz",
+ "integrity": "sha512-7kRDOuLHB87D/JESKxQoRwv4DzbIdwkAGQ7p6QKGdVlY1IZheUnVhlk/4UZlNUVxdAXpyxikE3URsG067ybVzw==",
+ "license": "MIT",
+ "peerDependencies": {
+ "@types/react": "*",
+ "react": "*"
+ },
+ "peerDependenciesMeta": {
+ "@types/react": {
+ "optional": true
+ },
+ "react": {
+ "optional": true
+ }
+ }
+ },
+ "node_modules/remove-accents": {
+ "version": "0.4.2",
+ "resolved": "https://registry.npmjs.org/remove-accents/-/remove-accents-0.4.2.tgz",
+ "integrity": "sha1-CkPTqq4egNuRngeuJUsoXZ4ce7U= sha512-7pXIJqJOq5tFgG1A2Zxti3Ht8jJF337m4sowbuHsW30ZnkQFnDzy9qBNhgzX8ZLW4+UBcXiiR7SwR6pokHsxiA==",
+ "license": "MIT"
+ },
+ "node_modules/require-from-string": {
+ "version": "2.0.2",
+ "resolved": "https://registry.npmjs.org/require-from-string/-/require-from-string-2.0.2.tgz",
+ "integrity": "sha512-Xf0nWe6RseziFMu+Ap9biiUbmplq6S9/p+7w7YXP/JBHhrUDDUhwa+vANyubuqfZWTveU//DYVGsDG7RKL/vEw==",
+ "dev": true,
+ "license": "MIT",
+ "engines": {
+ "node": ">=0.10.0"
+ }
+ },
+ "node_modules/resolve": {
+ "version": "1.22.0",
+ "resolved": "https://registry.npmjs.org/resolve/-/resolve-1.22.0.tgz",
+ "integrity": "sha512-Hhtrw0nLeSrFQ7phPp4OOcVjLPIeMnRlr5mcnVuMe7M/7eBn98A3hmFRLoFo3DLZkivSYwhRUJTyPyWAk56WLw==",
+ "dev": true,
+ "license": "MIT",
+ "dependencies": {
+ "is-core-module": "^2.8.1",
+ "path-parse": "^1.0.7",
+ "supports-preserve-symlinks-flag": "^1.0.0"
+ },
+ "bin": {
+ "resolve": "bin/resolve"
+ },
+ "funding": {
+ "url": "https://github.com/sponsors/ljharb"
+ }
+ },
+ "node_modules/resolve-cwd": {
+ "version": "3.0.0",
+ "resolved": "https://registry.npmjs.org/resolve-cwd/-/resolve-cwd-3.0.0.tgz",
+ "integrity": "sha512-OrZaX2Mb+rJCpH/6CpSqt9xFVpN++x01XnN2ie9g6P5/3xelLAkXWVADpdz1IHD/KFfEXyE6V0U01OQ3UO2rEg==",
+ "dev": true,
+ "license": "MIT",
+ "dependencies": {
+ "resolve-from": "^5.0.0"
+ },
+ "engines": {
+ "node": ">=8"
+ }
+ },
+ "node_modules/resolve-from": {
+ "version": "5.0.0",
+ "resolved": "https://registry.npmjs.org/resolve-from/-/resolve-from-5.0.0.tgz",
+ "integrity": "sha512-qYg9KP24dD5qka9J47d0aVky0N+b4fTU89LN9iDnjB5waksiC49rvMB0PrUJQGoTmH50XPiqOvAjDfaijGxYZw==",
+ "dev": true,
+ "license": "MIT",
+ "engines": {
+ "node": ">=8"
+ }
+ },
+ "node_modules/response-iterator": {
+ "version": "0.2.11",
+ "resolved": "https://registry.npmjs.org/response-iterator/-/response-iterator-0.2.11.tgz",
+ "integrity": "sha512-5tdhcAeGMSyM0/FoxAYjoOxQZ2tRR2H/S/t6kGRXu6iiWcGY5UnZgkVANbTwBVUSGqWu0ADctmoi6lOCIF8uKQ==",
+ "license": "MIT",
+ "engines": {
+ "node": ">=0.8"
+ }
+ },
+ "node_modules/ripemd160": {
+ "version": "2.0.3",
+ "resolved": "https://registry.npmjs.org/ripemd160/-/ripemd160-2.0.3.tgz",
+ "integrity": "sha512-5Di9UC0+8h1L6ZD2d7awM7E/T4uA1fJRlx6zk/NvdCCVEoAnFqvHmCuNeIKoCeIixBX/q8uM+6ycDvF8woqosA==",
+ "dev": true,
+ "license": "MIT",
+ "dependencies": {
+ "hash-base": "^3.1.2",
+ "inherits": "^2.0.4"
+ },
+ "engines": {
+ "node": ">= 0.8"
+ }
+ },
+ "node_modules/safe-buffer": {
+ "version": "5.2.1",
+ "resolved": "https://registry.npmjs.org/safe-buffer/-/safe-buffer-5.2.1.tgz",
+ "integrity": "sha512-rp3So07KcdmmKbGvgaNxQSJr7bGVSVk5S9Eq1F+ppbRo70+YeaDxkw5Dd8NPN+GD6bjnYm2VuPuCXmpuYvmCXQ==",
+ "dev": true,
+ "funding": [
+ {
+ "type": "github",
+ "url": "https://github.com/sponsors/feross"
+ },
+ {
+ "type": "patreon",
+ "url": "https://www.patreon.com/feross"
+ },
+ {
+ "type": "consulting",
+ "url": "https://feross.org/support"
+ }
+ ],
+ "license": "MIT"
+ },
+ "node_modules/schema-utils": {
+ "version": "4.3.3",
+ "resolved": "https://registry.npmjs.org/schema-utils/-/schema-utils-4.3.3.tgz",
+ "integrity": "sha512-eflK8wEtyOE6+hsaRVPxvUKYCpRgzLqDTb8krvAsRIwOGlHoSgYLgBXoubGgLd2fT41/OUYdb48v4k4WWHQurA==",
+ "dev": true,
+ "license": "MIT",
+ "dependencies": {
+ "@types/json-schema": "^7.0.9",
+ "ajv": "^8.9.0",
+ "ajv-formats": "^2.1.1",
+ "ajv-keywords": "^5.1.0"
+ },
+ "engines": {
+ "node": ">= 10.13.0"
+ },
+ "funding": {
+ "type": "opencollective",
+ "url": "https://opencollective.com/webpack"
+ }
+ },
+ "node_modules/semver": {
+ "version": "7.5.3",
+ "resolved": "https://registry.npmjs.org/semver/-/semver-7.5.3.tgz",
+ "integrity": "sha512-QBlUtyVk/5EeHbi7X0fw6liDZc7BBmEaSYn01fMU1OUYbf6GPsbTtd8WmnqbI20SeycoHSeiybkE/q1Q+qlThQ==",
+ "dev": true,
+ "license": "ISC",
+ "dependencies": {
+ "lru-cache": "^6.0.0"
+ },
+ "bin": {
+ "semver": "bin/semver.js"
+ },
+ "engines": {
+ "node": ">=10"
+ }
+ },
+ "node_modules/set-function-length": {
+ "version": "1.2.2",
+ "resolved": "https://registry.npmjs.org/set-function-length/-/set-function-length-1.2.2.tgz",
+ "integrity": "sha512-pgRc4hJ4/sNjWCSS9AmnS40x3bNMDTknHgL5UaMBTMyJnU90EgWh1Rz+MC9eFu4BuN/UwZjKQuY/1v3rM7HMfg==",
+ "dev": true,
+ "license": "MIT",
+ "dependencies": {
+ "define-data-property": "^1.1.4",
+ "es-errors": "^1.3.0",
+ "function-bind": "^1.1.2",
+ "get-intrinsic": "^1.2.4",
+ "gopd": "^1.0.1",
+ "has-property-descriptors": "^1.0.2"
+ },
+ "engines": {
+ "node": ">= 0.4"
+ }
+ },
+ "node_modules/setimmediate": {
+ "version": "1.0.5",
+ "resolved": "https://registry.npmjs.org/setimmediate/-/setimmediate-1.0.5.tgz",
+ "integrity": "sha1-KQy7Iy4waULX1+qbg3Mqt4VvgoU= sha512-MATJdZp8sLqDl/68LfQmbP8zKPLQNV6BIZoIgrscFDQ+RsvK/BxeDQOgyxKKoh0y/8h3BqVFnCqQ/gd+reiIXA==",
+ "dev": true,
+ "license": "MIT"
+ },
+ "node_modules/sha.js": {
+ "version": "2.4.12",
+ "resolved": "https://registry.npmjs.org/sha.js/-/sha.js-2.4.12.tgz",
+ "integrity": "sha512-8LzC5+bvI45BjpfXU8V5fdU2mfeKiQe1D1gIMn7XUlF3OTUrpdJpPPH4EMAnF0DsHHdSZqCdSss5qCmJKuiO3w==",
+ "dev": true,
+ "license": "(MIT AND BSD-3-Clause)",
+ "dependencies": {
+ "inherits": "^2.0.4",
+ "safe-buffer": "^5.2.1",
+ "to-buffer": "^1.2.0"
+ },
+ "bin": {
+ "sha.js": "bin.js"
+ },
+ "engines": {
+ "node": ">= 0.10"
+ },
+ "funding": {
+ "url": "https://github.com/sponsors/ljharb"
+ }
+ },
+ "node_modules/shallow-clone": {
+ "version": "3.0.1",
+ "resolved": "https://registry.npmjs.org/shallow-clone/-/shallow-clone-3.0.1.tgz",
+ "integrity": "sha512-/6KqX+GVUdqPuPPd2LxDDxzX6CAbjJehAAOKlNpqqUpAqPM6HeL8f+o3a+JsyGjn2lv0WY8UsTgUJjU9Ok55NA==",
+ "dev": true,
+ "license": "MIT",
+ "dependencies": {
+ "kind-of": "^6.0.2"
+ },
+ "engines": {
+ "node": ">=8"
+ }
+ },
+ "node_modules/shebang-command": {
+ "version": "2.0.0",
+ "resolved": "https://registry.npmjs.org/shebang-command/-/shebang-command-2.0.0.tgz",
+ "integrity": "sha512-kHxr2zZpYtdmrN1qDjrrX/Z1rR1kG8Dx+gkpK1G4eXmvXswmcE1hTWBWYUzlraYw1/yZp6YuDY77YtvbN0dmDA==",
+ "dev": true,
+ "license": "MIT",
+ "dependencies": {
+ "shebang-regex": "^3.0.0"
+ },
+ "engines": {
+ "node": ">=8"
+ }
+ },
+ "node_modules/shebang-regex": {
+ "version": "3.0.0",
+ "resolved": "https://registry.npmjs.org/shebang-regex/-/shebang-regex-3.0.0.tgz",
+ "integrity": "sha512-7++dFhtcx3353uBaq8DDR4NuxBetBzC7ZQOhmTQInHEd6bSrXdiEyzCvG07Z44UYdLShWUyXt5M/yhz8ekcb1A==",
+ "dev": true,
+ "license": "MIT",
+ "engines": {
+ "node": ">=8"
+ }
+ },
+ "node_modules/signal-exit": {
+ "version": "3.0.7",
+ "resolved": "https://registry.npmjs.org/signal-exit/-/signal-exit-3.0.7.tgz",
+ "integrity": "sha512-wnD2ZE+l+SPC/uoS0vXeE9L1+0wuaMqKlfz9AMUo38JsyLSBWSFcHR1Rri62LZc12vLr1gb3jl7iwQhgwpAbGQ==",
+ "dev": true,
+ "license": "ISC"
+ },
+ "node_modules/source-map": {
+ "version": "0.6.1",
+ "resolved": "https://registry.npmjs.org/source-map/-/source-map-0.6.1.tgz",
+ "integrity": "sha512-UjgapumWlbMhkBgzT7Ykc5YXUT46F0iKu8SGXq0bcwP5dz/h0Plj6enJqjz1Zbq2l5WaqYnrVbwWOWMyF3F47g==",
+ "dev": true,
+ "license": "BSD-3-Clause",
+ "engines": {
+ "node": ">=0.10.0"
+ }
+ },
+ "node_modules/source-map-support": {
+ "version": "0.5.21",
+ "resolved": "https://registry.npmjs.org/source-map-support/-/source-map-support-0.5.21.tgz",
+ "integrity": "sha512-uBHU3L3czsIyYXKX88fdrGovxdSCoTGDRZ6SYXtSRxLZUzHg5P/66Ht6uoUlHu9EZod+inXhKo3qQgwXUT/y1w==",
+ "dev": true,
+ "license": "MIT",
+ "dependencies": {
+ "buffer-from": "^1.0.0",
+ "source-map": "^0.6.0"
+ }
+ },
+ "node_modules/split-on-first": {
+ "version": "3.0.0",
+ "resolved": "https://registry.npmjs.org/split-on-first/-/split-on-first-3.0.0.tgz",
+ "integrity": "sha512-qxQJTx2ryR0Dw0ITYyekNQWpz6f8dGd7vffGNflQQ3Iqj9NJ6qiZ7ELpZsJ/QBhIVAiDfXdag3+Gp8RvWa62AA==",
+ "license": "MIT",
+ "engines": {
+ "node": ">=12"
+ },
+ "funding": {
+ "url": "https://github.com/sponsors/sindresorhus"
+ }
+ },
+ "node_modules/stream-browserify": {
+ "version": "3.0.0",
+ "resolved": "https://registry.npmjs.org/stream-browserify/-/stream-browserify-3.0.0.tgz",
+ "integrity": "sha512-H73RAHsVBapbim0tU2JwwOiXUj+fikfiaoYAKHF3VJfA0pe2BCzkhAHBlLG6REzE+2WNZcxOXjK7lkso+9euLA==",
+ "dev": true,
+ "license": "MIT",
+ "dependencies": {
+ "inherits": "~2.0.4",
+ "readable-stream": "^3.5.0"
+ }
+ },
+ "node_modules/stream-http": {
+ "version": "3.2.0",
+ "resolved": "https://registry.npmjs.org/stream-http/-/stream-http-3.2.0.tgz",
+ "integrity": "sha512-Oq1bLqisTyK3TSCXpPbT4sdeYNdmyZJv1LxpEm2vu1ZhK89kSE5YXwZc3cWk0MagGaKriBh9mCFbVGtO+vY29A==",
+ "dev": true,
+ "license": "MIT",
+ "dependencies": {
+ "builtin-status-codes": "^3.0.0",
+ "inherits": "^2.0.4",
+ "readable-stream": "^3.6.0",
+ "xtend": "^4.0.2"
+ }
+ },
+ "node_modules/string_decoder": {
+ "version": "1.3.0",
+ "resolved": "https://registry.npmjs.org/string_decoder/-/string_decoder-1.3.0.tgz",
+ "integrity": "sha512-hkRX8U1WjJFd8LsDJ2yQ/wWWxaopEsABU1XfkM8A+j0+85JAGppt16cr1Whg6KIbb4okU6Mql6BOj+uup/wKeA==",
+ "dev": true,
+ "license": "MIT",
+ "dependencies": {
+ "safe-buffer": "~5.2.0"
+ }
+ },
+ "node_modules/strip-final-newline": {
+ "version": "2.0.0",
+ "resolved": "https://registry.npmjs.org/strip-final-newline/-/strip-final-newline-2.0.0.tgz",
+ "integrity": "sha512-BrpvfNAE3dcvq7ll3xVumzjKjZQ5tI1sEUIKr3Uoks0XUl45St3FlatVqef9prk4jRDzhW6WZg+3bk93y6pLjA==",
+ "dev": true,
+ "license": "MIT",
+ "engines": {
+ "node": ">=6"
+ }
+ },
+ "node_modules/supports-color": {
+ "version": "7.2.0",
+ "resolved": "https://registry.npmjs.org/supports-color/-/supports-color-7.2.0.tgz",
+ "integrity": "sha512-qpCAvRl9stuOHveKsn7HncJRvv501qIacKzQlO/+Lwxc9+0q2wLyv4Dfvt80/DPn2pqOBsJdDiogXGR9+OvwRw==",
+ "dev": true,
+ "license": "MIT",
+ "dependencies": {
+ "has-flag": "^4.0.0"
+ },
+ "engines": {
+ "node": ">=8"
+ }
+ },
+ "node_modules/supports-preserve-symlinks-flag": {
+ "version": "1.0.0",
+ "resolved": "https://registry.npmjs.org/supports-preserve-symlinks-flag/-/supports-preserve-symlinks-flag-1.0.0.tgz",
+ "integrity": "sha512-ot0WnXS9fgdkgIcePe6RHNk1WA8+muPa6cSjeR3V8K27q9BB1rTE3R1p7Hv0z1ZyAc8s6Vvv8DIyWf681MAt0w==",
+ "dev": true,
+ "license": "MIT",
+ "engines": {
+ "node": ">= 0.4"
+ },
+ "funding": {
+ "url": "https://github.com/sponsors/ljharb"
+ }
+ },
+ "node_modules/symbol-observable": {
+ "version": "4.0.0",
+ "resolved": "https://registry.npmjs.org/symbol-observable/-/symbol-observable-4.0.0.tgz",
+ "integrity": "sha512-b19dMThMV4HVFynSAM1++gBHAbk2Tc/osgLIBZMKsyqh34jb2e8Os7T6ZW/Bt3pJFdBTd2JwAnAAEQV7rSNvcQ==",
+ "license": "MIT",
+ "engines": {
+ "node": ">=0.10"
+ }
+ },
+ "node_modules/tapable": {
+ "version": "2.3.0",
+ "resolved": "https://registry.npmjs.org/tapable/-/tapable-2.3.0.tgz",
+ "integrity": "sha512-g9ljZiwki/LfxmQADO3dEY1CbpmXT5Hm2fJ+QaGKwSXUylMybePR7/67YW7jOrrvjEgL1Fmz5kzyAjWVWLlucg==",
+ "dev": true,
+ "license": "MIT",
+ "engines": {
+ "node": ">=6"
+ },
+ "funding": {
+ "type": "opencollective",
+ "url": "https://opencollective.com/webpack"
+ }
+ },
+ "node_modules/terser": {
+ "version": "5.46.0",
+ "resolved": "https://registry.npmjs.org/terser/-/terser-5.46.0.tgz",
+ "integrity": "sha512-jTwoImyr/QbOWFFso3YoU3ik0jBBDJ6JTOQiy/J2YxVJdZCc+5u7skhNwiOR3FQIygFqVUPHl7qbbxtjW2K3Qg==",
+ "dev": true,
+ "license": "BSD-2-Clause",
+ "dependencies": {
+ "@jridgewell/source-map": "^0.3.3",
+ "acorn": "^8.15.0",
+ "commander": "^2.20.0",
+ "source-map-support": "~0.5.20"
+ },
+ "bin": {
+ "terser": "bin/terser"
+ },
+ "engines": {
+ "node": ">=10"
+ }
+ },
+ "node_modules/terser-webpack-plugin": {
+ "version": "5.4.0",
+ "resolved": "https://registry.npmjs.org/terser-webpack-plugin/-/terser-webpack-plugin-5.4.0.tgz",
+ "integrity": "sha512-Bn5vxm48flOIfkdl5CaD2+1CiUVbonWQ3KQPyP7/EuIl9Gbzq/gQFOzaMFUEgVjB1396tcK0SG8XcNJ/2kDH8g==",
+ "dev": true,
+ "license": "MIT",
+ "dependencies": {
+ "@jridgewell/trace-mapping": "^0.3.25",
+ "jest-worker": "^27.4.5",
+ "schema-utils": "^4.3.0",
+ "terser": "^5.31.1"
+ },
+ "engines": {
+ "node": ">= 10.13.0"
+ },
+ "funding": {
+ "type": "opencollective",
+ "url": "https://opencollective.com/webpack"
+ },
+ "peerDependencies": {
+ "webpack": "^5.1.0"
+ },
+ "peerDependenciesMeta": {
+ "@swc/core": {
+ "optional": true
+ },
+ "esbuild": {
+ "optional": true
+ },
+ "uglify-js": {
+ "optional": true
+ }
+ }
+ },
+ "node_modules/timers-browserify": {
+ "version": "2.0.12",
+ "resolved": "https://registry.npmjs.org/timers-browserify/-/timers-browserify-2.0.12.tgz",
+ "integrity": "sha512-9phl76Cqm6FhSX9Xe1ZUAMLtm1BLkKj2Qd5ApyWkXzsMRaA7dgr81kf4wJmQf/hAvg8EEyJxDo3du/0KlhPiKQ==",
+ "dev": true,
+ "license": "MIT",
+ "dependencies": {
+ "setimmediate": "^1.0.4"
+ },
+ "engines": {
+ "node": ">=0.6.0"
+ }
+ },
+ "node_modules/to-buffer": {
+ "version": "1.2.1",
+ "resolved": "https://registry.npmjs.org/to-buffer/-/to-buffer-1.2.1.tgz",
+ "integrity": "sha512-tB82LpAIWjhLYbqjx3X4zEeHN6M8CiuOEy2JY8SEQVdYRe3CCHOFaqrBW1doLDrfpWhplcW7BL+bO3/6S3pcDQ==",
+ "dev": true,
+ "license": "MIT",
+ "dependencies": {
+ "isarray": "^2.0.5",
+ "safe-buffer": "^5.2.1",
+ "typed-array-buffer": "^1.0.3"
+ },
+ "engines": {
+ "node": ">= 0.4"
+ }
+ },
+ "node_modules/to-regex-range": {
+ "version": "5.0.1",
+ "resolved": "https://registry.npmjs.org/to-regex-range/-/to-regex-range-5.0.1.tgz",
+ "integrity": "sha512-65P7iz6X5yEr1cwcgvQxbbIw7Uk3gOy5dIdtZ4rDveLqhrdJP+Li/Hx6tyK0NEb+2GCyneCMJiGqrADCSNk8sQ==",
+ "dev": true,
+ "license": "MIT",
+ "dependencies": {
+ "is-number": "^7.0.0"
+ },
+ "engines": {
+ "node": ">=8.0"
+ }
+ },
+ "node_modules/ts-invariant": {
+ "version": "0.10.3",
+ "resolved": "https://registry.npmjs.org/ts-invariant/-/ts-invariant-0.10.3.tgz",
+ "integrity": "sha512-uivwYcQaxAucv1CzRp2n/QdYPo4ILf9VXgH19zEIjFx2EJufV16P0JtJVpYHy89DItG6Kwj2oIUjrcK5au+4tQ==",
+ "license": "MIT",
+ "dependencies": {
+ "tslib": "^2.1.0"
+ },
+ "engines": {
+ "node": ">=8"
+ }
+ },
+ "node_modules/ts-loader": {
+ "version": "9.3.0",
+ "resolved": "https://registry.npmjs.org/ts-loader/-/ts-loader-9.3.0.tgz",
+ "integrity": "sha512-2kLLAdAD+FCKijvGKi9sS0OzoqxLCF3CxHpok7rVgCZ5UldRzH0TkbwG9XECKjBzHsAewntC5oDaI/FwKzEUog==",
+ "dev": true,
+ "license": "MIT",
+ "dependencies": {
+ "chalk": "^4.1.0",
+ "enhanced-resolve": "^5.0.0",
+ "micromatch": "^4.0.0",
+ "semver": "^7.3.4"
+ },
+ "engines": {
+ "node": ">=12.0.0"
+ },
+ "peerDependencies": {
+ "typescript": "*",
+ "webpack": "^5.0.0"
+ }
+ },
+ "node_modules/tslib": {
+ "version": "2.8.1",
+ "resolved": "https://registry.npmjs.org/tslib/-/tslib-2.8.1.tgz",
+ "integrity": "sha512-oJFu94HQb+KVduSUQL7wnpmqnfmLsOA/nAh6b6EH0wCEoK0/mPeXU6c3wKDV83MkOuHPRHtSXKKU99IBazS/2w==",
+ "license": "0BSD"
+ },
+ "node_modules/tty-browserify": {
+ "version": "0.0.1",
+ "resolved": "https://registry.npmjs.org/tty-browserify/-/tty-browserify-0.0.1.tgz",
+ "integrity": "sha512-C3TaO7K81YvjCgQH9Q1S3R3P3BtN3RIM8n+OvX4il1K1zgE8ZhI0op7kClgkxtutIE8hQrcrHBXvIheqKUUCxw==",
+ "dev": true,
+ "license": "MIT"
+ },
+ "node_modules/typed-array-buffer": {
+ "version": "1.0.3",
+ "resolved": "https://registry.npmjs.org/typed-array-buffer/-/typed-array-buffer-1.0.3.tgz",
+ "integrity": "sha512-nAYYwfY3qnzX30IkA6AQZjVbtK6duGontcQm1WSG1MD94YLqK0515GNApXkoxKOWMusVssAHWLh9SeaoefYFGw==",
+ "dev": true,
+ "license": "MIT",
+ "dependencies": {
+ "call-bound": "^1.0.3",
+ "es-errors": "^1.3.0",
+ "is-typed-array": "^1.1.14"
+ },
+ "engines": {
+ "node": ">= 0.4"
+ }
+ },
+ "node_modules/typescript": {
+ "version": "5.7.3",
+ "resolved": "https://registry.npmjs.org/typescript/-/typescript-5.7.3.tgz",
+ "integrity": "sha512-84MVSjMEHP+FQRPy3pX9sTVV/INIex71s9TL2Gm5FG/WG1SqXeKyZ0k7/blY/4FdOzI12CBy1vGc4og/eus0fw==",
+ "dev": true,
+ "license": "Apache-2.0",
+ "bin": {
+ "tsc": "bin/tsc",
+ "tsserver": "bin/tsserver"
+ },
+ "engines": {
+ "node": ">=14.17"
+ }
+ },
+ "node_modules/undici-types": {
+ "version": "7.16.0",
+ "resolved": "https://registry.npmjs.org/undici-types/-/undici-types-7.16.0.tgz",
+ "integrity": "sha512-Zz+aZWSj8LE6zoxD+xrjh4VfkIG8Ya6LvYkZqtUQGJPZjYl53ypCaUwWqo7eI0x66KBGeRo+mlBEkMSeSZ38Nw==",
+ "dev": true,
+ "license": "MIT"
+ },
+ "node_modules/universal-user-agent": {
+ "version": "7.0.2",
+ "resolved": "https://registry.npmjs.org/universal-user-agent/-/universal-user-agent-7.0.2.tgz",
+ "integrity": "sha512-0JCqzSKnStlRRQfCdowvqy3cy0Dvtlb8xecj/H8JFZuCze4rwjPZQOgvFvn0Ws/usCHQFGpyr+pB9adaGwXn4Q==",
+ "license": "ISC"
+ },
+ "node_modules/update-browserslist-db": {
+ "version": "1.2.3",
+ "resolved": "https://registry.npmjs.org/update-browserslist-db/-/update-browserslist-db-1.2.3.tgz",
+ "integrity": "sha512-Js0m9cx+qOgDxo0eMiFGEueWztz+d4+M3rGlmKPT+T4IS/jP4ylw3Nwpu6cpTTP8R1MAC1kF4VbdLt3ARf209w==",
+ "dev": true,
+ "funding": [
+ {
+ "type": "opencollective",
+ "url": "https://opencollective.com/browserslist"
+ },
+ {
+ "type": "tidelift",
+ "url": "https://tidelift.com/funding/github/npm/browserslist"
+ },
+ {
+ "type": "github",
+ "url": "https://github.com/sponsors/ai"
+ }
+ ],
+ "license": "MIT",
+ "dependencies": {
+ "escalade": "^3.2.0",
+ "picocolors": "^1.1.1"
+ },
+ "bin": {
+ "update-browserslist-db": "cli.js"
+ },
+ "peerDependencies": {
+ "browserslist": ">= 4.21.0"
+ }
+ },
+ "node_modules/url": {
+ "version": "0.11.0",
+ "resolved": "https://registry.npmjs.org/url/-/url-0.11.0.tgz",
+ "integrity": "sha1-ODjpfPxgUh63PFJajlW/3Z4uKPE= sha512-kbailJa29QrtXnxgq+DdCEGlbTeYM2eJUxsz6vjZavrCYPMIFHMKQmSKYAIuUK2i7hgPm28a8piX5NTUtM/LKQ==",
+ "dev": true,
+ "license": "MIT",
+ "dependencies": {
+ "punycode": "1.3.2",
+ "querystring": "0.2.0"
+ }
+ },
+ "node_modules/url/node_modules/punycode": {
+ "version": "1.3.2",
+ "resolved": "https://registry.npmjs.org/punycode/-/punycode-1.3.2.tgz",
+ "integrity": "sha1-llOgNvt8HuQjQvIyXM7v6jkmxI0= sha512-RofWgt/7fL5wP1Y7fxE7/EmTLzQVnB0ycyibJ0OOHIlJqTNzglYFxVwETOcIoJqJmpDXJ9xImDv+Fq34F/d4Dw==",
+ "dev": true,
+ "license": "MIT"
+ },
+ "node_modules/util": {
+ "version": "0.12.4",
+ "resolved": "https://registry.npmjs.org/util/-/util-0.12.4.tgz",
+ "integrity": "sha512-bxZ9qtSlGUWSOy9Qa9Xgk11kSslpuZwaxCg4sNIDj6FLucDab2JxnHwyNTCpHMtK1MjoQiWQ6DiUMZYbSrO+Sw==",
+ "dev": true,
+ "license": "MIT",
+ "dependencies": {
+ "inherits": "^2.0.3",
+ "is-arguments": "^1.0.4",
+ "is-generator-function": "^1.0.7",
+ "is-typed-array": "^1.1.3",
+ "safe-buffer": "^5.1.2",
+ "which-typed-array": "^1.1.2"
+ }
+ },
+ "node_modules/util-deprecate": {
+ "version": "1.0.2",
+ "resolved": "https://registry.npmjs.org/util-deprecate/-/util-deprecate-1.0.2.tgz",
+ "integrity": "sha1-RQ1Nyfpw3nMnYvvS1KKJgUGaDM8= sha512-EPD5q1uXyFxJpCrLnCc1nHnq3gOa6DZBocAIiI2TaSCA7VCJ1UJDMagCzIkXNsUYfD1daK//LTEQ8xiIbrHtcw==",
+ "dev": true,
+ "license": "MIT"
+ },
+ "node_modules/vm-browserify": {
+ "version": "1.1.2",
+ "resolved": "https://registry.npmjs.org/vm-browserify/-/vm-browserify-1.1.2.tgz",
+ "integrity": "sha512-2ham8XPWTONajOR0ohOKOHXkm3+gaBmGut3SRuu75xLd/RRaY6vqgh8NBYYk7+RW3u5AtzPQZG8F10LHkl0lAQ==",
+ "dev": true,
+ "license": "MIT"
+ },
+ "node_modules/watchpack": {
+ "version": "2.5.1",
+ "resolved": "https://registry.npmjs.org/watchpack/-/watchpack-2.5.1.tgz",
+ "integrity": "sha512-Zn5uXdcFNIA1+1Ei5McRd+iRzfhENPCe7LeABkJtNulSxjma+l7ltNx55BWZkRlwRnpOgHqxnjyaDgJnNXnqzg==",
+ "dev": true,
+ "license": "MIT",
+ "dependencies": {
+ "glob-to-regexp": "^0.4.1",
+ "graceful-fs": "^4.1.2"
+ },
+ "engines": {
+ "node": ">=10.13.0"
+ }
+ },
+ "node_modules/webpack": {
+ "version": "5.105.0",
+ "resolved": "https://registry.npmjs.org/webpack/-/webpack-5.105.0.tgz",
+ "integrity": "sha512-gX/dMkRQc7QOMzgTe6KsYFM7DxeIONQSui1s0n/0xht36HvrgbxtM1xBlgx596NbpHuQU8P7QpKwrZYwUX48nw==",
+ "dev": true,
+ "license": "MIT",
+ "dependencies": {
+ "@types/eslint-scope": "^3.7.7",
+ "@types/estree": "^1.0.8",
+ "@types/json-schema": "^7.0.15",
+ "@webassemblyjs/ast": "^1.14.1",
+ "@webassemblyjs/wasm-edit": "^1.14.1",
+ "@webassemblyjs/wasm-parser": "^1.14.1",
+ "acorn": "^8.15.0",
+ "acorn-import-phases": "^1.0.3",
+ "browserslist": "^4.28.1",
+ "chrome-trace-event": "^1.0.2",
+ "enhanced-resolve": "^5.19.0",
+ "es-module-lexer": "^2.0.0",
+ "eslint-scope": "5.1.1",
+ "events": "^3.2.0",
+ "glob-to-regexp": "^0.4.1",
+ "graceful-fs": "^4.2.11",
+ "json-parse-even-better-errors": "^2.3.1",
+ "loader-runner": "^4.3.1",
+ "mime-types": "^2.1.27",
+ "neo-async": "^2.6.2",
+ "schema-utils": "^4.3.3",
+ "tapable": "^2.3.0",
+ "terser-webpack-plugin": "^5.3.16",
+ "watchpack": "^2.5.1",
+ "webpack-sources": "^3.3.3"
+ },
+ "bin": {
+ "webpack": "bin/webpack.js"
+ },
+ "engines": {
+ "node": ">=10.13.0"
+ },
+ "funding": {
+ "type": "opencollective",
+ "url": "https://opencollective.com/webpack"
+ },
+ "peerDependenciesMeta": {
+ "webpack-cli": {
+ "optional": true
+ }
+ }
+ },
+ "node_modules/webpack-cli": {
+ "version": "4.9.2",
+ "resolved": "https://registry.npmjs.org/webpack-cli/-/webpack-cli-4.9.2.tgz",
+ "integrity": "sha512-m3/AACnBBzK/kMTcxWHcZFPrw/eQuY4Df1TxvIWfWM2x7mRqBQCqKEd96oCUa9jkapLBaFfRce33eGDb4Pr7YQ==",
+ "dev": true,
+ "license": "MIT",
+ "dependencies": {
+ "@discoveryjs/json-ext": "^0.5.0",
+ "@webpack-cli/configtest": "^1.1.1",
+ "@webpack-cli/info": "^1.4.1",
+ "@webpack-cli/serve": "^1.6.1",
+ "colorette": "^2.0.14",
+ "commander": "^7.0.0",
+ "execa": "^5.0.0",
+ "fastest-levenshtein": "^1.0.12",
+ "import-local": "^3.0.2",
+ "interpret": "^2.2.0",
+ "rechoir": "^0.7.0",
+ "webpack-merge": "^5.7.3"
+ },
+ "bin": {
+ "webpack-cli": "bin/cli.js"
+ },
+ "engines": {
+ "node": ">=10.13.0"
+ },
+ "peerDependencies": {
+ "webpack": "4.x.x || 5.x.x"
+ },
+ "peerDependenciesMeta": {
+ "@webpack-cli/generators": {
+ "optional": true
+ },
+ "@webpack-cli/migrate": {
+ "optional": true
+ },
+ "webpack-bundle-analyzer": {
+ "optional": true
+ },
+ "webpack-dev-server": {
+ "optional": true
+ }
+ }
+ },
+ "node_modules/webpack-cli/node_modules/commander": {
+ "version": "7.2.0",
+ "resolved": "https://registry.npmjs.org/commander/-/commander-7.2.0.tgz",
+ "integrity": "sha512-QrWXB+ZQSVPmIWIhtEO9H+gwHaMGYiF5ChvoJ+K9ZGHG/sVsa6yiesAD1GC/x46sET00Xlwo1u49RVVVzvcSkw==",
+ "dev": true,
+ "license": "MIT",
+ "engines": {
+ "node": ">= 10"
+ }
+ },
+ "node_modules/webpack-merge": {
+ "version": "5.8.0",
+ "resolved": "https://registry.npmjs.org/webpack-merge/-/webpack-merge-5.8.0.tgz",
+ "integrity": "sha512-/SaI7xY0831XwP6kzuwhKWVKDP9t1QY1h65lAFLbZqMPIuYcD9QAW4u9STIbU9kaJbPBB/geU/gLr1wDjOhQ+Q==",
+ "dev": true,
+ "license": "MIT",
+ "dependencies": {
+ "clone-deep": "^4.0.1",
+ "wildcard": "^2.0.0"
+ },
+ "engines": {
+ "node": ">=10.0.0"
+ }
+ },
+ "node_modules/webpack-sources": {
+ "version": "3.3.3",
+ "resolved": "https://registry.npmjs.org/webpack-sources/-/webpack-sources-3.3.3.tgz",
+ "integrity": "sha512-yd1RBzSGanHkitROoPFd6qsrxt+oFhg/129YzheDGqeustzX0vTZJZsSsQjVQC4yzBQ56K55XU8gaNCtIzOnTg==",
+ "dev": true,
+ "license": "MIT",
+ "engines": {
+ "node": ">=10.13.0"
+ }
+ },
+ "node_modules/which": {
+ "version": "2.0.2",
+ "resolved": "https://registry.npmjs.org/which/-/which-2.0.2.tgz",
+ "integrity": "sha512-BLI3Tl1TW3Pvl70l3yq3Y64i+awpwXqsGBYWkkqMtnbXgrMD+yj7rhW0kuEDxzJaYXGjEW5ogapKNMEKNMjibA==",
+ "dev": true,
+ "license": "ISC",
+ "dependencies": {
+ "isexe": "^2.0.0"
+ },
+ "bin": {
+ "node-which": "bin/node-which"
+ },
+ "engines": {
+ "node": ">= 8"
+ }
+ },
+ "node_modules/which-typed-array": {
+ "version": "1.1.19",
+ "resolved": "https://registry.npmjs.org/which-typed-array/-/which-typed-array-1.1.19.tgz",
+ "integrity": "sha512-rEvr90Bck4WZt9HHFC4DJMsjvu7x+r6bImz0/BrbWb7A2djJ8hnZMrWnHo9F8ssv0OMErasDhftrfROTyqSDrw==",
+ "dev": true,
+ "license": "MIT",
+ "dependencies": {
+ "available-typed-arrays": "^1.0.7",
+ "call-bind": "^1.0.8",
+ "call-bound": "^1.0.4",
+ "for-each": "^0.3.5",
+ "get-proto": "^1.0.1",
+ "gopd": "^1.2.0",
+ "has-tostringtag": "^1.0.2"
+ },
+ "engines": {
+ "node": ">= 0.4"
+ },
+ "funding": {
+ "url": "https://github.com/sponsors/ljharb"
+ }
+ },
+ "node_modules/wildcard": {
+ "version": "2.0.0",
+ "resolved": "https://registry.npmjs.org/wildcard/-/wildcard-2.0.0.tgz",
+ "integrity": "sha512-JcKqAHLPxcdb9KM49dufGXn2x3ssnfjbcaQdLlfZsL9rH9wgDQjUtDxbo8NE0F6SFvydeu1VhZe7hZuHsB2/pw==",
+ "dev": true,
+ "license": "MIT"
+ },
+ "node_modules/xtend": {
+ "version": "4.0.2",
+ "resolved": "https://registry.npmjs.org/xtend/-/xtend-4.0.2.tgz",
+ "integrity": "sha512-LKYU1iAXJXUgAXn9URjiu+MWhyUXHsvfp7mcuYm9dSUKK0/CjtrUwFAxD82/mCWbtLsGjFIad0wIsod4zrTAEQ==",
+ "dev": true,
+ "license": "MIT",
+ "engines": {
+ "node": ">=0.4"
+ }
+ },
+ "node_modules/yallist": {
+ "version": "4.0.0",
+ "resolved": "https://registry.npmjs.org/yallist/-/yallist-4.0.0.tgz",
+ "integrity": "sha512-3wdGidZyq5PB084XLES5TpOSRA3wjXAlIWMhum2kRcv/41Sn2emQ0dycQW4uZXLejwKvg6EsvbdlVL+FYEct7A==",
+ "dev": true,
+ "license": "ISC"
+ },
+ "node_modules/zen-observable": {
+ "version": "0.8.15",
+ "resolved": "https://registry.npmjs.org/zen-observable/-/zen-observable-0.8.15.tgz",
+ "integrity": "sha512-PQ2PC7R9rslx84ndNBZB/Dkv8V8fZEpk83RLgXtYd0fwUgEjseMn1Dgajh2x6S8QbZAFa9p2qVCEuYZNgve0dQ==",
+ "license": "MIT"
+ },
+ "node_modules/zen-observable-ts": {
+ "version": "1.2.5",
+ "resolved": "https://registry.npmjs.org/zen-observable-ts/-/zen-observable-ts-1.2.5.tgz",
+ "integrity": "sha512-QZWQekv6iB72Naeake9hS1KxHlotfRpe+WGNbNx5/ta+R3DNjVO2bswf63gXlWDcs+EMd7XY8HfVQyP1X6T4Zg==",
+ "license": "MIT",
+ "dependencies": {
+ "zen-observable": "0.8.15"
+ }
+ }
+ }
+}
diff --git a/extensions/github1s/package.json b/extensions/github1s/package.json
index 2a589bc57..a33cc7336 100644
--- a/extensions/github1s/package.json
+++ b/extensions/github1s/package.json
@@ -4,211 +4,337 @@
"publisher": "github1s",
"description": "",
"private": true,
- "enableProposedApi": true,
+ "enabledApiProposals": [
+ "fileSearchProvider",
+ "textSearchProvider",
+ "resolvers"
+ ],
"directories": {
"lib": "lib"
},
"activationEvents": [
+ "onStartupFinished",
"onFileSystem:github1s",
- "onCommand:github1s.validate-token",
- "onCommand:github1s.update-token",
- "onCommand:github1s.clear-token",
- "onCommand:github1s.get-current-authority",
- "onCommand:github1s.checkout-ref",
- "onCommand:github1s.switch-to-pull",
- "onCommand:github1s.switch-to-commit",
- "onCommand:github1s.diff-view-open-left-file",
- "onCommand:github1s.diff-view-open-right-file",
- "onCommand:github1s.open-on-github",
- "onView:github1s"
+ "onFileSystem:gitlab1s",
+ "onFileSystem:bitbucket1s",
+ "onFileSystem:npmjs1s",
+ "onFileSystem:ossinsight",
+ "onCommand:remoteHub.openRepository"
],
"browser": "./dist/extension",
"engines": {
"vscode": "^1.48.0"
},
"contributes": {
+ "resourceLabelFormatters": [
+ {
+ "scheme": "github1s",
+ "authority": "?*/**",
+ "formatting": {
+ "label": "${path} (${authoritySuffix})",
+ "separator": "/"
+ }
+ },
+ {
+ "scheme": "gitlab1s",
+ "authority": "?*/**",
+ "formatting": {
+ "label": "${path} (${authoritySuffix})",
+ "separator": "/"
+ }
+ },
+ {
+ "scheme": "bitbucket1s",
+ "authority": "?*/**",
+ "formatting": {
+ "label": "${path} (${authoritySuffix})",
+ "separator": "/"
+ }
+ }
+ ],
"viewsContainers": {
"activitybar": [
{
- "id": "github1s",
- "title": "GitHub1s",
- "icon": "assets/github1s.svg"
+ "id": "settings",
+ "title": "Settings",
+ "icon": "assets/settings.svg"
}
]
},
"views": {
- "github1s": [
+ "settings": [
{
"id": "github1s.views.settings",
"name": "Settings",
- "type": "webview"
+ "type": "webview",
+ "when": "github1s:views:settings:visible == true"
}
],
"scm": [
{
- "id": "github1s.views.pull-request-list",
- "name": "Pull Requests"
+ "id": "github1s.views.fileHistory",
+ "name": "File History",
+ "when": "github1s:views:fileHistory:visible == true"
+ },
+ {
+ "id": "github1s.views.commitList",
+ "name": "Commits",
+ "when": "github1s:views:commitList:visible == true"
},
{
- "id": "github1s.views.commit-list",
- "name": "Commits"
+ "id": "github1s.views.codeReviewList",
+ "name": "Code Reviews",
+ "when": "github1s:views:codeReviewList:visible == true"
}
]
},
"commands": [
{
- "command": "github1s.dev-test",
- "title": "GitHub1s Dev Test",
+ "command": "github1s.commands.openRepository",
+ "title": "Open Repository...",
"category": "GitHub1s"
},
{
- "command": "github1s.update-token",
- "title": "Update GitHub OAuth Token",
+ "command": "github1s.commands.openOnlineEditor",
+ "title": "Open Online Editor...",
"category": "GitHub1s"
},
{
- "command": "github1s.validate-token",
- "title": "Validate Current GitHub OAuth Token",
+ "command": "github1s.commands.checkoutTo",
+ "title": "Checkout to...",
"category": "GitHub1s"
},
{
- "command": "github1s.clear-token",
- "title": "Clear Current GitHub OAuth Token",
- "category": "GitHub1s"
+ "command": "github1s.commands.refreshCodeReviewList",
+ "title": "Refresh",
+ "category": "GitHub1s",
+ "icon": "$(refresh)"
},
{
- "command": "github1s.checkout-ref",
- "title": "Checkout to...",
- "category": "GitHub1s"
+ "command": "github1s.commands.searchCodeReview",
+ "title": "Search",
+ "category": "GitHub1s",
+ "icon": "$(search)"
},
{
- "command": "github1s.open-gitpod",
- "title": "Edit files in Gitpod",
- "category": "GitHub1s"
+ "command": "github1s.commands.switchToPullRequest",
+ "title": "Switch to Pull Request",
+ "category": "GitHub1s",
+ "icon": "$(log-in)",
+ "enablement": "github1s:adapters:default:codeReviewType == 'PullRequest'"
},
{
- "command": "github1s.switch-to-pull",
- "title": "Search a Pull Request",
+ "command": "github1s.commands.switchToMergeRequest",
+ "title": "Switch to Merge Request",
"category": "GitHub1s",
- "icon": "$(search)"
+ "icon": "$(log-in)",
+ "enablement": "github1s:adapters:default:codeReviewType == 'MergeRequest'"
},
{
- "command": "github1s.pull-view-refresh-pull-list",
- "title": "Refresh",
+ "command": "github1s.commands.switchToChangeRequest",
+ "title": "Switch to Change Request",
"category": "GitHub1s",
- "icon": "$(refresh)"
+ "icon": "$(log-in)",
+ "enablement": "github1s:adapters:default:codeReviewType == 'ChangeRequest'"
},
{
- "command": "github1s.pull-view-item-switch-to-pull",
- "title": "Switch to Pull Request",
+ "command": "github1s.commands.switchToCodeReview",
+ "title": "Switch to Code Review",
"category": "GitHub1s",
- "icon": "$(log-in)"
+ "icon": "$(log-in)",
+ "enablement": "github1s:adapters:default:codeReviewType != 'PullRequest' && github1s:adapters:default:codeReviewType != 'MergeRequest' && github1s:adapters:default:codeReviewType != 'ChangeRequest'"
},
{
- "command": "github1s.pull-view-item-open-on-github",
+ "command": "github1s.commands.openCodeReviewOnGitHub",
"title": "Open on GitHub",
"category": "GitHub1s",
- "icon": "$(globe)"
+ "icon": "$(globe)",
+ "enablement": "github1s:adapters:default:platformName == 'GitHub'"
},
{
- "command": "github1s.switch-to-commit",
- "title": "Search a Commit",
+ "command": "github1s.commands.openCodeReviewOnGitLab",
+ "title": "Open on GitLab",
"category": "GitHub1s",
- "icon": "$(search)"
+ "icon": "$(globe)",
+ "enablement": "github1s:adapters:default:platformName == 'GitLab'"
},
{
- "command": "github1s.commit-view-refresh-commit-list",
+ "command": "github1s.commands.openCodeReviewOnBitbucket",
+ "title": "Open on Bitbucket",
+ "category": "GitHub1s",
+ "icon": "$(globe)",
+ "enablement": "github1s:adapters:default:platformName == 'Bitbucket'"
+ },
+ {
+ "command": "github1s.commands.openCodeReviewOnOfficialPage",
+ "title": "Open on Official Page",
+ "category": "GitHub1s",
+ "icon": "$(globe)",
+ "enablement": "github1s:adapters:default:platformName != 'GitHub' && github1s:adapters:default:platformName != 'GitLab' && github1s:adapters:default:platformName != 'Bitbucket'"
+ },
+ {
+ "command": "github1s.commands.refreshFileHistoryCommitList",
+ "title": "Refresh",
+ "category": "GitHub1s",
+ "icon": "$(refresh)"
+ },
+ {
+ "command": "github1s.commands.refreshCommitList",
"title": "Refresh",
"category": "GitHub1s",
"icon": "$(refresh)"
},
{
- "command": "github1s.commit-view-item-switch-to-commit",
+ "command": "github1s.commands.searchCommit",
+ "title": "Search",
+ "category": "GitHub1s",
+ "icon": "$(search)"
+ },
+ {
+ "command": "github1s.commands.switchToCommit",
"title": "Switch to Commit",
"category": "GitHub1s",
"icon": "$(log-in)"
},
{
- "command": "github1s.commit-view-item-open-on-github",
+ "command": "github1s.commands.diffCommitFile",
+ "title": "Diff File",
+ "category": "GitHub1s",
+ "icon": "$(diff)"
+ },
+ {
+ "command": "github1s.commands.openCommitOnGitHub",
"title": "Open on GitHub",
"category": "GitHub1s",
- "icon": "$(globe)"
+ "icon": "$(globe)",
+ "enablement": "github1s:adapters:default:platformName == 'GitHub'"
+ },
+ {
+ "command": "github1s.commands.openCommitOnGitLab",
+ "title": "Open on GitLab",
+ "category": "GitHub1s",
+ "icon": "$(globe)",
+ "enablement": "github1s:adapters:default:platformName == 'GitLab'"
},
{
- "command": "github1s.editor-view-open-changes",
+ "command": "github1s.commands.openCommitOnBitbucket",
+ "title": "Open on Bitbucket",
+ "category": "GitHub1s",
+ "icon": "$(globe)",
+ "enablement": "github1s:adapters:default:platformName == 'Bitbucket'"
+ },
+ {
+ "command": "github1s.commands.openCommitOnOfficialPage",
+ "title": "Open on Official Page",
+ "category": "GitHub1s",
+ "icon": "$(globe)",
+ "enablement": "github1s:adapters:default:platformName != 'GitHub' && github1s:adapters:default:platformName != 'GitLab' && github1s:adapters:default:platformName != 'Bitbucket'"
+ },
+ {
+ "command": "github1s.commands.diffChangedFile",
"title": "Open Changes",
"category": "GitHub1s",
"icon": "$(compare-changes)"
},
{
- "command": "github1s.diff-view-open-left-file",
+ "command": "github1s.commands.diffViewOpenLeftFile",
"title": "Open Left File",
"category": "GitHub1s",
"icon": {
"dark": "assets/icons/dark/open-left-file.svg",
"light": "assets/icons/light/open-left-file.svg"
},
- "enablement": "isInDiffEditor && resourceScheme =~ /^github1s/ && resource =~ /^(?![^?]*\\?[^#]*(%26|\\b)base(=|%3D|%3d)github1s-empty-file)/"
+ "enablement": "isInDiffEditor && resourceScheme =~ /^(github1s|gitlab1s|bitbucket1s)/ && resource =~ /^(?![^?]*\\?[^#]*(%26|\\b)base(=|%3D|%3d)github1s-empty-file)/"
},
{
- "command": "github1s.diff-view-open-right-file",
+ "command": "github1s.commands.diffViewOpenRightFile",
"title": "Open Right File",
"category": "GitHub1s",
"icon": {
"dark": "assets/icons/dark/open-right-file.svg",
"light": "assets/icons/light/open-right-file.svg"
},
- "enablement": "isInDiffEditor && resourceScheme =~ /^github1s/ && resource =~ /^(?![^?]*\\?[^#]*(%26|\\b)head(=|%3D|%3d)github1s-empty-file)/"
+ "enablement": "isInDiffEditor && resourceScheme =~ /^(github1s|gitlab1s|bitbucket1s)/ && resource =~ /^(?![^?]*\\?[^#]*(%26|\\b)head(=|%3D|%3d)github1s-empty-file)/"
},
{
- "command": "github1s.editor-view-open-prev-revision",
+ "command": "github1s.commands.openFilePreviousRevision",
"title": "Open Previous Revision",
"category": "GitHub1s",
"icon": "$(arrow-left)",
- "enablement": "resourceScheme =~ /^github1s$/ && resource =~ /^(?![^?]*\\?[^#]*(%26|\\b)base(=|%3D|%3d)github1s-empty-file)/"
+ "enablement": "resourceScheme =~ /^(github1s|gitlab1s|bitbucket1s)$/ && resource =~ /^(?![^?]*\\?[^#]*(%26|\\b)base(=|%3D|%3d)github1s-empty-file)/"
},
{
- "command": "github1s.editor-view-open-next-revision",
+ "command": "github1s.commands.openFileNextRevision",
"title": "Open Next Revision",
"category": "GitHub1s",
"icon": "$(arrow-right)",
"enablement": "resource =~ /^[^?]*\\?[^#]*(%26|\\b)hasNextRevision(=|%3D|%3d)true/"
},
{
- "command": "github1s.toggle-editor-gutter-blame",
+ "command": "github1s.commands.toggleEditorGutterBlame",
"title": "Toggle File Blame",
"category": "GitHub1s",
- "enablement": "!isInDiffEditor && resourceScheme =~ /^github1s$/"
+ "enablement": "!isInDiffEditor"
},
{
- "command": "github1s.open-editor-gutter-blame",
+ "command": "github1s.commands.openEditorGutterBlame",
"title": "Toggle File Blame",
"category": "GitHub1s",
"icon": {
"dark": "assets/icons/dark/open-blame.svg",
"light": "assets/icons/light/open-blame.svg"
},
- "enablement": "!isInDiffEditor && resourceScheme =~ /^github1s$/"
+ "enablement": "!isInDiffEditor"
},
{
- "command": "github1s.close-editor-gutter-blame",
+ "command": "github1s.commands.closeEditorGutterBlame",
"title": "Toggle File Blame",
"category": "GitHub1s",
"icon": {
"dark": "assets/icons/dark/close-blame.svg",
"light": "assets/icons/light/close-blame.svg"
},
- "enablement": "!isInDiffEditor && resourceScheme =~ /^github1s$/"
+ "enablement": "!isInDiffEditor"
},
{
- "command": "github1s.open-on-github",
+ "command": "github1s.commands.openOnGitHub",
"title": "Open on GitHub",
- "category": "GitHub1s"
+ "category": "GitHub1s",
+ "icon": "$(globe)",
+ "enablement": "github1s:adapters:default:platformName == 'GitHub'"
+ },
+ {
+ "command": "github1s.commands.openOnGitLab",
+ "title": "Open on GitLab",
+ "category": "GitHub1s",
+ "icon": "$(globe)",
+ "enablement": "github1s:adapters:default:platformName == 'GitLab'"
+ },
+ {
+ "command": "github1s.commands.openOnBitbucket",
+ "title": "Open on Bitbucket",
+ "category": "GitHub1s",
+ "icon": "$(globe)",
+ "enablement": "github1s:adapters:default:platformName == 'Bitbucket'"
+ },
+ {
+ "command": "github1s.commands.openOnNpm",
+ "title": "Open on npm",
+ "category": "GitHub1s",
+ "icon": "$(globe)",
+ "enablement": "github1s:adapters:default:platformName == 'npm'"
+ },
+ {
+ "command": "github1s.commands.openOnOfficialPage",
+ "title": "Open on Official Page",
+ "category": "GitHub1s",
+ "icon": "$(globe)",
+ "enablement": "github1s:adapters:default:platformName != 'GitHub' && github1s:adapters:default:platformName != 'GitLab' && github1s:adapters:default:platformName != 'Bitbucket' && github1s:adapters:default:platformName != 'npm'"
}
],
"colors": [
{
- "id": "gitDecoration.addedResourceForeground",
+ "id": "github1s.colors.addedResourceForeground",
"description": "%colors.added%",
"defaults": {
"light": "#587c0c",
@@ -217,7 +343,7 @@
}
},
{
- "id": "gitDecoration.deletedResourceForeground",
+ "id": "github1s.colors.deletedResourceForeground",
"description": "%colors.deleted%",
"defaults": {
"light": "#ad0707",
@@ -226,7 +352,7 @@
}
},
{
- "id": "gitDecoration.modifiedResourceForeground",
+ "id": "github1s.colors.modifiedResourceForeground",
"description": "%colors.modified%",
"defaults": {
"light": "#895503",
@@ -235,7 +361,7 @@
}
},
{
- "id": "gitDecoration.submoduleResourceForeground",
+ "id": "github1s.colors.submoduleResourceForeground",
"description": "%colors.submodule%",
"defaults": {
"light": "#1258a7",
@@ -274,167 +400,253 @@
"menus": {
"commandPalette": [
{
- "command": "github1s.pull-view-refresh-pull-list",
+ "command": "github1s.commands.refreshCodeReviewList",
+ "when": "false"
+ },
+ {
+ "command": "github1s.commands.searchCodeReview",
+ "when": "false"
+ },
+ {
+ "command": "github1s.commands.openCodeReviewOnGitHub",
+ "when": "false"
+ },
+ {
+ "command": "github1s.commands.openCodeReviewOnGitLab",
+ "when": "false"
+ },
+ {
+ "command": "github1s.commands.openCodeReviewOnBitbucket",
+ "when": "false"
+ },
+ {
+ "command": "github1s.commands.openCodeReviewOnOfficialPage",
"when": "false"
},
{
- "command": "github1s.pull-view-item-switch-to-pull",
+ "command": "github1s.commands.refreshCommitList",
"when": "false"
},
{
- "command": "github1s.pull-view-item-open-on-github",
+ "command": "github1s.commands.searchCommit",
"when": "false"
},
{
- "command": "github1s.commit-view-refresh-commit-list",
+ "command": "github1s.commands.openCommitOnGitHub",
"when": "false"
},
{
- "command": "github1s.commit-view-item-switch-to-commit",
+ "command": "github1s.commands.openCommitOnGitLab",
"when": "false"
},
{
- "command": "github1s.commit-view-item-open-on-github",
+ "command": "github1s.commands.openCommitOnBitbucket",
"when": "false"
},
{
- "command": "github1s.editor-view-open-changes",
+ "command": "github1s.commands.openCommitOnOfficialPage",
"when": "false"
},
{
- "command": "github1s.diff-view-open-left-file",
+ "command": "github1s.commands.diffChangedFile",
"when": "false"
},
{
- "command": "github1s.diff-view-open-right-file",
+ "command": "github1s.commands.diffViewOpenLeftFile",
"when": "false"
},
{
- "command": "github1s.editor-view-open-prev-revision",
+ "command": "github1s.commands.diffViewOpenRightFile",
"when": "false"
},
{
- "command": "github1s.editor-view-open-next-revision",
+ "command": "github1s.commands.openFilePreviousRevision",
"when": "false"
},
{
- "command": "github1s.open-editor-gutter-blame",
+ "command": "github1s.commands.openFileNextRevision",
"when": "false"
},
{
- "command": "github1s.close-editor-gutter-blame",
+ "command": "github1s.commands.openEditorGutterBlame",
+ "when": "false"
+ },
+ {
+ "command": "github1s.commands.closeEditorGutterBlame",
"when": "false"
}
],
"view/title": [
{
- "command": "github1s.pull-view-refresh-pull-list",
- "when": "view =~ /^github1s\\.views\\.pull-request-list$/",
+ "command": "github1s.commands.refreshCodeReviewList",
+ "when": "view == 'github1s.views.codeReviewList'",
"group": "navigation@1"
},
{
- "command": "github1s.switch-to-pull",
- "when": "view =~ /^github1s\\.views\\.pull-request-list$/",
+ "command": "github1s.commands.searchCodeReview",
+ "when": "view == 'github1s.views.codeReviewList'",
"group": "navigation@2"
},
{
- "command": "github1s.commit-view-refresh-commit-list",
- "when": "view =~ /^github1s\\.views\\.commit-list$/",
+ "command": "github1s.commands.refreshFileHistoryCommitList",
+ "when": "view == 'github1s.views.fileHistory'",
"group": "navigation@1"
},
{
- "command": "github1s.switch-to-commit",
- "when": "view =~ /^github1s\\.views\\.commit-list$/",
+ "command": "github1s.commands.searchCommit",
+ "when": "view == 'github1s.views.fileHistory'",
+ "group": "navigation@2"
+ },
+ {
+ "command": "github1s.commands.refreshCommitList",
+ "when": "view == 'github1s.views.commitList'",
+ "group": "navigation@1"
+ },
+ {
+ "command": "github1s.commands.searchCommit",
+ "when": "view == 'github1s.views.commitList'",
"group": "navigation@2"
}
],
"view/item/context": [
{
- "command": "github1s.pull-view-item-switch-to-pull",
- "when": "viewItem =~ /^github1s:pull-request$/",
+ "command": "github1s.commands.switchToPullRequest",
+ "when": "viewItem == 'github1s:viewItems:codeReviewListItem' && github1s:adapters:default:codeReviewType == 'PullRequest'",
+ "group": "inline@1"
+ },
+ {
+ "command": "github1s.commands.switchToMergeRequest",
+ "when": "viewItem == 'github1s:viewItems:codeReviewListItem' && github1s:adapters:default:codeReviewType == 'MergeRequest'",
+ "group": "inline@1"
+ },
+ {
+ "command": "github1s.commands.switchToChangeRequest",
+ "when": "viewItem == 'github1s:viewItems:codeReviewListItem' && github1s:adapters:default:codeReviewType == 'ChangeRequest'",
+ "group": "inline@1"
+ },
+ {
+ "command": "github1s.commands.switchToCodeReview",
+ "when": "viewItem == 'github1s:viewItems:codeReviewListItem' && github1s:adapters:default:codeReviewType != 'PullRequest' && github1s:adapters:default:codeReviewType != 'MergeRequest' && github1s:adapters:default:codeReviewType != 'ChangeRequest'",
"group": "inline@1"
},
{
- "command": "github1s.pull-view-item-open-on-github",
- "when": "viewItem =~ /^github1s:pull-request$/",
+ "command": "github1s.commands.openCodeReviewOnGitHub",
+ "when": "viewItem == 'github1s:viewItems:codeReviewListItem' && github1s:adapters:default:platformName == 'GitHub'",
+ "group": "inline@2"
+ },
+ {
+ "command": "github1s.commands.openCodeReviewOnGitLab",
+ "when": "viewItem == 'github1s:viewItems:codeReviewListItem' && github1s:adapters:default:platformName == 'GitLab'",
+ "group": "inline@2"
+ },
+ {
+ "command": "github1s.commands.openCodeReviewOnBitbucket",
+ "when": "viewItem == 'github1s:viewItems:codeReviewListItem' && github1s:adapters:default:platformName == 'Bitbucket'",
"group": "inline@2"
},
{
- "command": "github1s.commit-view-item-switch-to-commit",
- "when": "viewItem =~ /^github1s:commit$/",
+ "command": "github1s.commands.openCodeReviewOnOfficialPage",
+ "when": "viewItem == 'github1s:viewItems:codeReviewListItem' && github1s:adapters:default:platformName != 'GitHub' && github1s:adapters:default:platformName != 'GitLab' && github1s:adapters:default:platformName != 'Bitbucket' && github1s:adapters:default:platformName != 'npm'",
+ "group": "inline@2"
+ },
+ {
+ "command": "github1s.commands.switchToCommit",
+ "when": "viewItem == 'github1s:viewItems:commitListItem'",
"group": "inline@1"
},
{
- "command": "github1s.commit-view-item-open-on-github",
- "when": "viewItem =~ /^github1s:commit$/",
+ "command": "github1s.commands.openCommitOnGitHub",
+ "when": "viewItem == 'github1s:viewItems:commitListItem' && github1s:adapters:default:platformName == 'GitHub'",
+ "group": "inline@2"
+ },
+ {
+ "command": "github1s.commands.diffCommitFile",
+ "when": "viewItem == 'github1s:viewItems:commitListItem' && view == 'github1s.views.fileHistory'",
+ "group": "inline@3"
+ },
+ {
+ "command": "github1s.commands.openCommitOnGitLab",
+ "when": "viewItem == 'github1s:viewItems:commitListItem' && github1s:adapters:default:platformName == 'GitLab'",
+ "group": "inline@2"
+ },
+ {
+ "command": "github1s.commands.openCommitOnBitbucket",
+ "when": "viewItem == 'github1s:viewItems:commitListItem' && github1s:adapters:default:platformName == 'Bitbucket'",
+ "group": "inline@2"
+ },
+ {
+ "command": "github1s.commands.openCommitOnOfficialPage",
+ "when": "viewItem == 'github1s:viewItems:commitListItem' && github1s:adapters:default:platformName != 'GitHub' && github1s:adapters:default:platformName != 'GitLab' && github1s:adapters:default:platformName != 'Bitbucket' && github1s:adapters:default:platformName != 'npm'",
"group": "inline@2"
}
],
"editor/title": [
{
- "command": "github1s.editor-view-open-changes",
- "when": "!isInDiffEditor && github1s.context.showOpenChangesInEditorTitle",
+ "command": "github1s.commands.diffChangedFile",
+ "when": "!isInDiffEditor && github1s:editors:showDiffChangedFile",
"group": "navigation@1"
},
{
- "command": "github1s.diff-view-open-left-file",
+ "command": "github1s.commands.diffViewOpenLeftFile",
"when": "isInDiffEditor",
"group": "navigation@2"
},
{
- "command": "github1s.diff-view-open-right-file",
+ "command": "github1s.commands.diffViewOpenRightFile",
"when": "isInDiffEditor",
"group": "navigation@3"
},
{
- "command": "github1s.open-editor-gutter-blame",
- "when": "!isInDiffEditor && resourceScheme =~ /^github1s$/ && !github1s.context.gutterBlameOpening",
+ "command": "github1s.commands.openFilePreviousRevision",
+ "when": "resourceScheme =~ /^(github1s|gitlab1s|bitbucket1s)/",
"group": "navigation@4"
},
{
- "command": "github1s.close-editor-gutter-blame",
- "when": "!isInDiffEditor && resourceScheme =~ /^github1s$/ && github1s.context.gutterBlameOpening",
- "group": "navigation@4"
+ "command": "github1s.commands.openFileNextRevision",
+ "when": "resourceScheme =~ /^(github1s|gitlab1s|bitbucket1s)/",
+ "group": "navigation@5"
},
{
- "command": "github1s.editor-view-open-prev-revision",
- "when": "resourceScheme =~ /^github1s/",
- "group": "navigation@98"
+ "command": "github1s.commands.openEditorGutterBlame",
+ "when": "!isInDiffEditor && github1s:features:gutterBlame:enabled && !github1s:features:gutterBlame:open",
+ "group": "navigation@6"
},
{
- "command": "github1s.editor-view-open-next-revision",
- "when": "resourceScheme =~ /^github1s/",
- "group": "navigation@99"
+ "command": "github1s.commands.closeEditorGutterBlame",
+ "when": "!isInDiffEditor && github1s:features:gutterBlame:enabled && github1s:features:gutterBlame:open",
+ "group": "navigation@6"
}
]
}
},
"scripts": {
"clean": "rm -rf dist out",
- "build": "webpack --config extension.webpack.config.js --mode production",
- "compile": "webpack --config extension.webpack.config.js --mode production",
- "dev": "webpack --config extension.webpack.config.js --watch"
+ "watch": "webpack --config webpack.config.js --watch",
+ "compile": "webpack --config webpack.config.js --mode production",
+ "test": "node --experimental-strip-types --test test/*.test.ts"
},
"keywords": [],
"author": "",
"license": "MIT",
"dependencies": {
- "@apollo/client": "^3.3.9",
- "dayjs": "^1.10.4",
- "graphql": "^15.5.0",
- "history": "^5.0.0",
- "js-base64": "^3.6.0",
+ "@apollo/client": "^3.12.5",
+ "@octokit/core": "^6.1.4",
+ "dayjs": "^1.11.1",
+ "graphql": "^16.8.1",
+ "history": "^5.3.0",
+ "js-base64": "^3.7.2",
"json-stable-stringify": "^1.0.1",
- "match-sorter": "^6.3.0",
- "p-finally": "^2.0.1",
+ "match-sorter": "^6.3.1",
+ "p-finally": "^3.0.0",
"process": "^0.11.10",
- "query-string": "^7.0.0",
- "webpack-cli": "^4.6.0"
+ "query-string": "^9.5.0"
},
"devDependencies": {
- "@types/vscode": "1.55.0",
- "ts-loader": "^8.1.0",
- "typescript": "^4.2.3",
- "webpack": "^5.30.0"
+ "@types/vscode": "^1.96.0",
+ "node-polyfill-webpack-plugin": "^1.1.4",
+ "ts-loader": "^9.2.8",
+ "typescript": "^5.7.3",
+ "webpack": "^5.105.0",
+ "webpack-cli": "^4.9.2"
}
}
diff --git a/extensions/github1s/src/adapters/bitbucket1s/index.ts b/extensions/github1s/src/adapters/bitbucket1s/index.ts
new file mode 100644
index 000000000..8be7a8b89
--- /dev/null
+++ b/extensions/github1s/src/adapters/bitbucket1s/index.ts
@@ -0,0 +1,35 @@
+/**
+ * @file Bitbucket adapter
+ * @author netcon
+ */
+
+import { BitbucketRouterParser } from './router-parser';
+import { SourcegraphDataSource } from '../sourcegraph/data-source';
+import { Adapter, CodeReviewType, PlatformName } from '../types';
+import { setVSCodeContext } from '@/helpers/vscode';
+
+export class BitbucketAdapter implements Adapter {
+ public scheme: string = 'bitbucket1s';
+ public platformName = PlatformName.Bitbucket;
+ public codeReviewType = CodeReviewType.PullRequest;
+
+ resolveDataSource() {
+ return Promise.resolve(SourcegraphDataSource.getInstance('bitbucket'));
+ }
+
+ resolveRouterParser() {
+ return Promise.resolve(BitbucketRouterParser.getInstance());
+ }
+
+ activateAsDefault() {
+ setVSCodeContext('github1s:views:commitList:visible', true);
+ setVSCodeContext('github1s:views:fileHistory:visible', true);
+ setVSCodeContext('github1s:features:gutterBlame:enabled', true);
+ }
+
+ deactivateAsDefault() {
+ setVSCodeContext('github1s:views:commitList:visible', false);
+ setVSCodeContext('github1s:views:fileHistory:visible', false);
+ setVSCodeContext('github1s:features:gutterBlame:enabled', false);
+ }
+}
diff --git a/extensions/github1s/src/adapters/bitbucket1s/parse-path.ts b/extensions/github1s/src/adapters/bitbucket1s/parse-path.ts
new file mode 100644
index 000000000..4ba8f48f1
--- /dev/null
+++ b/extensions/github1s/src/adapters/bitbucket1s/parse-path.ts
@@ -0,0 +1,74 @@
+/**
+ * @file parse bitbucket path
+ * @author netcon
+ */
+
+import { parsePath } from 'history';
+import { FileType, PageType, RouterState } from '@/adapters/types';
+import { SourcegraphDataSource } from '../sourcegraph/data-source';
+
+const parseTreeOrBlobUrl = async (path: string): Promise => {
+ const pathParts = parsePath(path).pathname!.split('/').filter(Boolean);
+ const [owner, repo, _pageType, ...restParts] = pathParts;
+ const repoFullName = `${owner}/${repo}`;
+ const dataSource = SourcegraphDataSource.getInstance('bitbucket');
+ const { ref, path: filePath } = await dataSource.extractRefPath(repoFullName, restParts.join('/'));
+ const fileType = await dataSource.detectPathFileType(repoFullName, ref, filePath);
+
+ return {
+ pageType: fileType === FileType.Directory ? PageType.Tree : PageType.Blob,
+ repo: repoFullName,
+ ref,
+ filePath,
+ };
+};
+
+const parseCommitsUrl = async (path: string): Promise => {
+ const pathParts = parsePath(path).pathname!.split('/').filter(Boolean);
+ const [owner, repo, _pageType, ...refParts] = pathParts;
+
+ return {
+ repo: `${owner}/${repo}`,
+ pageType: PageType.CommitList,
+ ref: refParts.length ? refParts.join('/') : 'HEAD',
+ };
+};
+
+const parseCommitUrl = async (path: string): Promise => {
+ const pathParts = parsePath(path).pathname!.split('/').filter(Boolean);
+ const [owner, repo, _pageType, ...refParts] = pathParts;
+ const commitSha = refParts.join('/');
+
+ return { repo: `${owner}/${repo}`, pageType: PageType.Commit, ref: commitSha, commitSha };
+};
+
+const PAGE_TYPE_MAP = {
+ src: PageType.Tree,
+ commit: PageType.Commit,
+ commits: PageType.CommitList,
+};
+
+export const parseBitbucketPath = async (path: string): Promise => {
+ const pathParts = parsePath(path).pathname?.split('/').filter(Boolean) || [];
+ // detect concrete PageType the *third part* in url.path
+ const pageType = pathParts[2] ? PAGE_TYPE_MAP[pathParts[2]] || PageType.Unknown : PageType.Tree;
+
+ if (pathParts.length >= 2) {
+ switch (pageType) {
+ case PageType.Tree:
+ return parseTreeOrBlobUrl(path);
+ case PageType.Commit:
+ return parseCommitUrl(path);
+ case PageType.CommitList:
+ return parseCommitsUrl(path);
+ }
+ }
+
+ // fallback to default
+ return {
+ repo: 'atlassian/clover',
+ ref: 'HEAD',
+ pageType: PageType.Tree,
+ filePath: '/',
+ };
+};
diff --git a/extensions/github1s/src/adapters/bitbucket1s/router-parser.ts b/extensions/github1s/src/adapters/bitbucket1s/router-parser.ts
new file mode 100644
index 000000000..d5d4de6a0
--- /dev/null
+++ b/extensions/github1s/src/adapters/bitbucket1s/router-parser.ts
@@ -0,0 +1,51 @@
+/**
+ * @file router parser
+ * @author netcon
+ */
+
+import * as adapterTypes from '../types';
+import { parseBitbucketPath } from './parse-path';
+
+export class BitbucketRouterParser extends adapterTypes.RouterParser {
+ private static instance: BitbucketRouterParser | null = null;
+
+ public static getInstance(): BitbucketRouterParser {
+ if (BitbucketRouterParser.instance) {
+ return BitbucketRouterParser.instance;
+ }
+ return (BitbucketRouterParser.instance = new BitbucketRouterParser());
+ }
+
+ parsePath(path: string): Promise {
+ return parseBitbucketPath(path);
+ }
+
+ buildTreePath(repo: string, ref?: string, filePath?: string): string {
+ return ref ? `/${repo}/src/${ref}${filePath && filePath !== '/' ? filePath : ''}` : `/${repo}`;
+ }
+
+ buildBlobPath(repo: string, ref: string, filePath: string, startLine?: number, endLine?: number): string {
+ const hash = startLine ? (endLine ? `#L${startLine}-L${endLine}` : `#L${startLine}`) : '';
+ return `/${repo}/src/${ref}${filePath}${hash}`;
+ }
+
+ buildCommitListPath(repo: string): string {
+ return `/${repo}/commits`;
+ }
+
+ buildCommitPath(repo: string, commitSha: string): string {
+ return `/${repo}/commits/${commitSha}`;
+ }
+
+ buildCodeReviewListPath(repo: string): string {
+ return `/${repo}/pull-requests`;
+ }
+
+ buildCodeReviewPath(repo: string, codeReviewId: string): string {
+ return `/${repo}/pull-requests/${codeReviewId}`;
+ }
+
+ buildExternalLink(path: string): string {
+ return 'https://bitbucket.org' + (path.startsWith('/') ? path : `/${path}`);
+ }
+}
diff --git a/extensions/github1s/src/adapters/github1s/authentication.ts b/extensions/github1s/src/adapters/github1s/authentication.ts
new file mode 100644
index 000000000..26fa76579
--- /dev/null
+++ b/extensions/github1s/src/adapters/github1s/authentication.ts
@@ -0,0 +1,136 @@
+/**
+ * @file github authentication page
+ * @author netcon
+ */
+
+import * as vscode from 'vscode';
+import { Barrier } from '@/helpers/async';
+import { getExtensionContext } from '@/helpers/context';
+import { createPageHtml, getWebviewOptions } from '@/helpers/page';
+import { GitHubTokenManager } from './token';
+import { messageApiMap } from './settings';
+
+export class GitHub1sAuthenticationView {
+ protected static instance: GitHub1sAuthenticationView | null = null;
+ public static viewType = 'github1s.views.github1s-authentication';
+ protected tokenManager = GitHubTokenManager.getInstance();
+ private webviewPanel: vscode.WebviewPanel | null = null;
+ // using for waiting token
+ private tokenBarrier: Barrier | null = null;
+ // using for displaying open page reason
+ private notice: string = '';
+
+ protected pageTitle = 'Authenticating to GitHub';
+ protected OAuthCommand = 'github1s.commands.vscode.connectToGitHub';
+ protected pageConfig: Record = {
+ authenticationFormTitle: 'Authenticating to GitHub',
+ OAuthButtonText: 'Connect to GitHub',
+ OAuthButtonLogo: 'assets/pages/assets/github.svg',
+ createTokenLink: `${GITHUB_ORIGIN}/settings/tokens/new?scopes=repo&description=GitHub1s`,
+ rateLimitDocLink: 'https://docs.github.com/rest/overview/resources-in-the-rest-api#rate-limiting',
+ rateLimitDocLinkText: 'GitHub Rate limiting Documentation',
+ authenticationFeatures: [
+ {
+ text: 'Access GitHub personal repository',
+ link: 'https://docs.github.com/en/account-and-profile/setting-up-and-managing-your-github-user-account/managing-access-to-your-personal-repositories',
+ },
+ {
+ text: 'Higher rate limit for GitHub official API',
+ link: 'https://docs.github.com/en/rest/overview/resources-in-the-rest-api#rate-limiting',
+ },
+ {
+ text: 'Support for GitHub GraphQL API',
+ link: 'https://docs.github.com/en/graphql/guides/forming-calls-with-graphql#authenticating-with-graphql',
+ },
+ ],
+ };
+
+ protected constructor() {}
+
+ public static getInstance(): GitHub1sAuthenticationView {
+ if (GitHub1sAuthenticationView.instance) {
+ return GitHub1sAuthenticationView.instance;
+ }
+ return (GitHub1sAuthenticationView.instance = new this());
+ }
+
+ private registerListeners() {
+ if (!this.webviewPanel) {
+ throw new Error('webview is not init yet');
+ }
+
+ this.webviewPanel.webview.onDidReceiveMessage((message) => {
+ const commonResponse = { id: message.id, type: message.type };
+ const postMessage = (data?: unknown) => this.webviewPanel!.webview.postMessage({ ...commonResponse, data });
+
+ switch (message.type) {
+ case 'get-notice':
+ postMessage(this.notice);
+ break;
+ case 'get-token':
+ postMessage(this.tokenManager.getToken());
+ break;
+ case 'set-token':
+ message.data && (this.notice = '');
+ this.tokenManager.setToken(message.data || '').then(() => postMessage());
+ break;
+ case 'validate-token':
+ this.tokenManager.validateToken(message.data).then((tokenStatus) => postMessage(tokenStatus));
+ break;
+ case 'oauth-authorizing':
+ vscode.commands.executeCommand(this.OAuthCommand).then((data: any) => {
+ if (data && data.error_description) {
+ vscode.window.showErrorMessage(data.error_description);
+ } else if (data && data.access_token) {
+ this.tokenManager.setToken(data.access_token || '').then(() => postMessage());
+ }
+ postMessage();
+ });
+ break;
+ case 'call-vscode-message-api':
+ const messageApi = messageApiMap[message.data?.level];
+ messageApi && messageApi(...message.data?.args).then((response) => postMessage(response));
+ break;
+ }
+ });
+
+ this.tokenManager.onDidChangeToken((token) => {
+ this.tokenBarrier && this.tokenBarrier.open();
+ this.tokenBarrier && (this.tokenBarrier = null);
+ this.webviewPanel?.webview.postMessage({ type: 'token-changed', token });
+ });
+ }
+
+ public open(notice: string = '', withBarrier = false) {
+ const extensionContext = getExtensionContext();
+
+ this.notice = notice;
+ withBarrier && !this.tokenBarrier && (this.tokenBarrier = new Barrier(600 * 1000));
+
+ if (!this.webviewPanel) {
+ this.webviewPanel = vscode.window.createWebviewPanel(
+ GitHub1sAuthenticationView.viewType,
+ this.pageTitle,
+ vscode.ViewColumn.One,
+ getWebviewOptions(extensionContext.extensionUri),
+ );
+ this.registerListeners();
+ this.webviewPanel.onDidDispose(() => (this.webviewPanel = null));
+ }
+
+ const styles = [
+ vscode.Uri.joinPath(extensionContext.extensionUri, 'assets/pages/components.css').toString(),
+ vscode.Uri.joinPath(extensionContext.extensionUri, 'assets/pages/github1s-authentication.css').toString(),
+ ];
+ const globalPageConfig = { ...this.pageConfig, extensionUri: extensionContext.extensionUri.toString() };
+ const scripts = [
+ 'data:text/javascript;base64,' +
+ Buffer.from(`window.pageConfig=${JSON.stringify(globalPageConfig)};`).toString('base64'),
+ vscode.Uri.joinPath(extensionContext.extensionUri, 'assets/pages/github1s-authentication.js').toString(),
+ ];
+
+ const webview = this.webviewPanel.webview;
+ webview.html = createPageHtml(this.pageTitle, styles, scripts);
+ return withBarrier ? this.tokenBarrier!.wait() : Promise.resolve();
+ }
+}
diff --git a/extensions/github1s/src/adapters/github1s/data-source.ts b/extensions/github1s/src/adapters/github1s/data-source.ts
new file mode 100644
index 000000000..88b102e34
--- /dev/null
+++ b/extensions/github1s/src/adapters/github1s/data-source.ts
@@ -0,0 +1,437 @@
+/**
+ * @file github1s data-source-provider
+ * @author netcon
+ */
+
+import {
+ Branch,
+ CodeReview,
+ CodeReviewState,
+ TextSearchOptions,
+ Commit,
+ CommonQueryOptions,
+ DataSource,
+ Directory,
+ DirectoryEntry,
+ File,
+ BlameRange,
+ FileType,
+ Tag,
+ TextSearchResults,
+ TextSearchQuery,
+ SymbolDefinitions,
+ SymbolReferences,
+ FileChangeStatus,
+ ChangedFile,
+ SymbolHover,
+ CommitsQueryOptions,
+ CodeReviewsQueryOptions,
+} from '../types';
+import { toUint8Array } from 'js-base64';
+import { matchSorter } from 'match-sorter';
+import { FILE_BLAME_QUERY } from './graphql';
+import { GitHubFetcher } from './fetcher';
+import { SourcegraphDataSource } from '../sourcegraph/data-source';
+import { decorate, memorize } from '@/helpers/func';
+import { normalizePath, trimStart, concatPath, isString } from '@/helpers/util';
+
+const parseRepoFullName = (repoFullName: string) => {
+ const [owner, repo] = repoFullName.split('/');
+ return { owner, repo };
+};
+
+const encodeFilePath = (filePath: string): string => {
+ const pathParts = filePath.split('/').filter(Boolean);
+ return `/${pathParts.map((segment) => encodeURIComponent(segment)).join('/')}`;
+};
+
+const FileTypeMap = {
+ blob: FileType.File,
+ tree: FileType.Directory,
+ commit: FileType.Submodule,
+};
+
+const getPullState = (pull: { state: string; merged_at: string | null }): CodeReviewState => {
+ // current pull request is open
+ if (pull.state === 'open') {
+ return CodeReviewState.Open;
+ }
+ // current pull request is merged
+ if (pull.state === 'closed' && pull.merged_at) {
+ return CodeReviewState.Merged;
+ }
+ // current pull is closed
+ return CodeReviewState.Closed;
+};
+
+const sourcegraphDataSource = SourcegraphDataSource.getInstance('github');
+const trySourcegraphApiFirst = (_target: any, propertyKey: string, descriptor: PropertyDescriptor) => {
+ const originalMethod = descriptor.value;
+
+ descriptor.value = async function Promise>(...args: Parameters) {
+ const githubFetcher = GitHubFetcher.getInstance();
+ if (await githubFetcher.getPreferSourcegraphApi(args[0])) {
+ try {
+ return await sourcegraphDataSource[propertyKey](...args);
+ } catch (e) {}
+ }
+ return originalMethod.apply(this, args);
+ };
+};
+
+export class GitHub1sDataSource extends DataSource {
+ private static instance: GitHub1sDataSource | null = null;
+ private branchesPromiseMap: Map> = new Map();
+ private tagsPromiseMap: Map> = new Map();
+ private refPathPromiseMap: Map> = new Map();
+ private matchedRefsMap = new Map();
+
+ public static getInstance(): GitHub1sDataSource {
+ if (GitHub1sDataSource.instance) {
+ return GitHub1sDataSource.instance;
+ }
+ return (GitHub1sDataSource.instance = new GitHub1sDataSource());
+ }
+
+ @trySourcegraphApiFirst
+ async provideRepository(repoFullName: string): Promise<{ private: boolean; defaultBranch: string } | null> {
+ const fetcher = GitHubFetcher.getInstance();
+ const { owner, repo } = parseRepoFullName(repoFullName);
+ const requestParams = { owner, repo };
+ const response = await fetcher.request('GET /repos/{owner}/{repo}', requestParams).catch(() => null);
+ return response?.data ? { private: response.data.private, defaultBranch: response.data.default_branch } : null;
+ }
+
+ @trySourcegraphApiFirst
+ async provideDirectory(repoFullName: string, ref: string, path: string, recursive = false): Promise {
+ const fetcher = GitHubFetcher.getInstance();
+ const encodedPath = trimStart(encodeFilePath(path), '/');
+ // github api will return all files if `recursive` exists, even the value if false
+ const recursiveParams = recursive ? { recursive } : {};
+ const requestParams = { ref, path: encodedPath, ...parseRepoFullName(repoFullName), ...recursiveParams };
+ const { data } = await fetcher.request('GET /repos/{owner}/{repo}/git/trees/{ref}:{path}', requestParams);
+ const parseTreeItem = (treeItem): DirectoryEntry => ({
+ path: concatPath(path, treeItem.path),
+ type: FileTypeMap[treeItem.type] || FileType.File,
+ commitSha: FileTypeMap[treeItem.type] === FileType.Submodule ? treeItem.sha || 'HEAD' : undefined,
+ size: treeItem.size,
+ });
+
+ return {
+ entries: (data.tree || []).map(parseTreeItem),
+ truncated: !!data.truncated,
+ };
+ }
+
+ @trySourcegraphApiFirst
+ async provideFile(repoFullName: string, ref: string, path: string): Promise {
+ const fetcher = GitHubFetcher.getInstance();
+ const { owner, repo } = parseRepoFullName(repoFullName);
+ const requestParams = { owner, repo, ref, path: trimStart(path, '/') };
+ const { data } = await fetcher.request('GET /repos/{owner}/{repo}/contents/{path}', requestParams);
+ return { content: toUint8Array((data as any).content) };
+ }
+
+ async getMatchingRefs(repoFullName: string, ref: 'heads' | 'tags'): Promise {
+ const fetcher = GitHubFetcher.getInstance();
+ const { owner, repo } = parseRepoFullName(repoFullName);
+ const requestParams = { owner, repo, ref };
+ const { data } = await fetcher.request('GET /repos/{owner}/{repo}/git/matching-refs/{ref}', requestParams);
+ return data.map((item) => ({
+ name: item.ref.slice(ref === 'heads' ? 11 : 10),
+ commitSha: item.object.sha,
+ description: `${ref === 'heads' ? 'Branch' : 'Tag'} at ${item.object.sha.slice(0, 8)}`,
+ }));
+ }
+
+ @decorate(memorize)
+ async getDefaultBranch(repoFullName: string) {
+ return (await this.provideRepository(repoFullName))?.defaultBranch || 'HEAD';
+ }
+
+ @trySourcegraphApiFirst
+ async extractRefPath(repoFullName: string, refAndPath: string): Promise<{ ref: string; path: string }> {
+ if (!this.matchedRefsMap.has(repoFullName)) {
+ this.matchedRefsMap.set(repoFullName, []);
+ }
+ const matchPathRef = (ref) => refAndPath.startsWith(`${ref}/`) || refAndPath === ref;
+ const matchedRef = this.matchedRefsMap.get(repoFullName)?.find(matchPathRef);
+ if (matchedRef) {
+ return { ref: matchedRef, path: normalizePath(refAndPath.slice(matchedRef.length + 1)) };
+ }
+ const mapKey = `${repoFullName} ${refAndPath}`;
+ if (!this.refPathPromiseMap.has(mapKey)) {
+ const refPathPromise = new Promise<{ ref: string; path: string }>(async (resolve, reject) => {
+ if (!refAndPath) {
+ return resolve({ ref: await this.getDefaultBranch(repoFullName), path: '/' });
+ }
+ if (refAndPath.match(/^HEAD(\/.*)?$/i)) {
+ return resolve({ ref: 'HEAD', path: normalizePath(refAndPath.slice(5)) });
+ }
+
+ const fetcher = GitHubFetcher.getInstance();
+ const { owner, repo } = parseRepoFullName(repoFullName);
+ const requestParams = { owner, repo, refAndPath };
+ const requestUrl = `GET /repos/{owner}/{repo}/git/extract-ref/{refAndPath}`;
+ const response = await fetcher.request(requestUrl, requestParams).catch(reject);
+ response?.data?.ref && this.matchedRefsMap.get(repoFullName)?.push(response.data.ref);
+ const result = response?.data || { ref: 'HEAD', path: '/' };
+ return resolve({ ...result, path: normalizePath(result.path || '') });
+ });
+ this.refPathPromiseMap.set(mapKey, refPathPromise);
+ }
+ return this.refPathPromiseMap.get(mapKey)!;
+ }
+
+ @trySourcegraphApiFirst
+ async provideBranches(repoFullName: string, options?: CommonQueryOptions): Promise {
+ if (!this.branchesPromiseMap.has(repoFullName)) {
+ this.branchesPromiseMap.set(repoFullName, this.getMatchingRefs(repoFullName, 'heads'));
+ }
+ return this.branchesPromiseMap.get(repoFullName)!.then((branches) => {
+ const matchOptions = { keys: ['name'] };
+ const matchedBranches = options?.query ? matchSorter(branches, options.query, matchOptions) : branches;
+ if (options?.pageSize) {
+ const page = options.page || 1;
+ const pageSize = options.pageSize;
+ return matchedBranches.slice(pageSize * (page - 1), pageSize * page);
+ }
+ return matchedBranches;
+ });
+ }
+
+ @trySourcegraphApiFirst
+ async provideBranch(repoFullName: string, branchName: string): Promise {
+ const branches = await this.provideBranches(repoFullName);
+ return branches.find((item) => item.name === branchName) || null;
+ }
+
+ @trySourcegraphApiFirst
+ async provideTags(repoFullName: string, options?: CommonQueryOptions): Promise {
+ if (!this.tagsPromiseMap.has(repoFullName)) {
+ this.tagsPromiseMap.set(repoFullName, this.getMatchingRefs(repoFullName, 'tags'));
+ }
+ return this.tagsPromiseMap.get(repoFullName)!.then((tags) => {
+ const matchOptions = { keys: ['name'] };
+ const matchedTags = options?.query ? matchSorter(tags, options.query, matchOptions) : tags;
+ if (options?.pageSize) {
+ const page = options.page || 1;
+ const pageSize = options.pageSize;
+ return matchedTags.slice(pageSize * (page - 1), pageSize * page);
+ }
+ return matchedTags;
+ });
+ }
+
+ @trySourcegraphApiFirst
+ async provideTag(repoFullName: string, tagName: string): Promise {
+ const tags = await this.provideTags(repoFullName);
+ return tags.find((item) => item.name === tagName) || null;
+ }
+
+ async provideTextSearchResults(
+ repoFullName: string,
+ ref: string,
+ query: TextSearchQuery,
+ options: TextSearchOptions,
+ ): Promise {
+ return sourcegraphDataSource.provideTextSearchResults(repoFullName, ref, query, options);
+ }
+
+ @trySourcegraphApiFirst
+ async provideCommits(
+ repoFullName: string,
+ options?: CommitsQueryOptions,
+ ): Promise<(Commit & { files?: ChangedFile[] })[]> {
+ const fetcher = GitHubFetcher.getInstance();
+ const { owner, repo } = parseRepoFullName(repoFullName);
+ const queryParams = {
+ page: options?.page,
+ per_page: options?.pageSize,
+ sha: options?.from,
+ path: isString(options?.path) ? trimStart(options.path, '/') : undefined,
+ author: options?.author,
+ };
+ const requestParams = { owner, repo, ...queryParams };
+ const { data } = await fetcher.request('GET /repos/{owner}/{repo}/commits', requestParams);
+ return data.map((item) => ({
+ sha: item.sha,
+ author: item.commit.author?.name,
+ email: item.commit.author?.email,
+ message: item.commit.message,
+ committer: item.commit.committer?.name,
+ createTime: item.commit.author?.date ? new Date(item.commit.author.date) : undefined,
+ parents: item.parents.map((parent) => parent.sha) || [],
+ avatarUrl: item.author?.avatar_url,
+ }));
+ }
+
+ @trySourcegraphApiFirst
+ async provideCommit(repoFullName: string, ref: string): Promise {
+ const fetcher = GitHubFetcher.getInstance();
+ const { owner, repo } = parseRepoFullName(repoFullName);
+ const requestParams = { owner, repo, ref };
+ const { data } = await fetcher.request('GET /repos/{owner}/{repo}/commits/{ref}', requestParams);
+ return {
+ sha: data.sha,
+ author: data.commit.author?.name,
+ email: data.commit.author?.email,
+ message: data.commit.message,
+ committer: data.commit.committer?.name,
+ createTime: data.commit.author?.date ? new Date(data.commit.author.date) : undefined,
+ parents: data.parents.map((parent) => parent.sha) || [],
+ files: data.files?.map((item) => ({
+ path: normalizePath(item.filename || item.previous_filename!),
+ previousPath: item.previous_filename ? normalizePath(item.previous_filename) : undefined,
+ status: item.status as FileChangeStatus,
+ })),
+ avatarUrl: data.author?.avatar_url,
+ };
+ }
+
+ @trySourcegraphApiFirst
+ async provideCommitChangedFiles(
+ repoFullName: string,
+ ref: string,
+ _options?: CommonQueryOptions,
+ ): Promise {
+ const fetcher = GitHubFetcher.getInstance();
+ const { owner, repo } = parseRepoFullName(repoFullName);
+ const requestParams = { owner, repo, ref };
+ const { data } = await fetcher.request('GET /repos/{owner}/{repo}/commits/{ref}', requestParams);
+ return (
+ data.files?.map((item) => ({
+ path: normalizePath(item.filename || item.previous_filename!),
+ previousPath: item.previous_filename ? normalizePath(item.previous_filename) : undefined,
+ status: item.status as FileChangeStatus,
+ })) || []
+ );
+ }
+
+ async provideCodeReviews(
+ repoFullName: string,
+ options?: CodeReviewsQueryOptions,
+ ): Promise<(CodeReview & { files?: ChangedFile[] })[]> {
+ const fetcher = GitHubFetcher.getInstance();
+ const { owner, repo } = parseRepoFullName(repoFullName);
+ const state = options?.state ? (options.state === CodeReviewState.Open ? 'open' : 'closed') : 'all';
+ const queryParams = { state, page: options?.page, per_page: options?.pageSize, creator: options?.creator };
+ const requestParams = { owner, repo, ...queryParams };
+ const { data } = await fetcher.request('GET /repos/{owner}/{repo}/pulls', requestParams as any);
+
+ return data.map((item) => ({
+ id: `${item.number}`,
+ title: item.title,
+ state: getPullState(item),
+ creator: item.user?.login,
+ createTime: new Date(item.created_at),
+ mergeTime: item.merged_at ? new Date(item.merged_at) : null,
+ closeTime: item.closed_at ? new Date(item.closed_at) : null,
+ source: item.head.label,
+ target: item.base.label,
+ sourceSha: item.head.sha,
+ targetSha: item.base.sha,
+ avatarUrl: item.user?.avatar_url,
+ }));
+ }
+
+ async provideCodeReview(repoFullName: string, id: string) {
+ const fetcher = GitHubFetcher.getInstance();
+ const { owner, repo } = parseRepoFullName(repoFullName);
+ const pullRequestParams = { owner, repo, pull_number: Number(id) };
+ const { data } = await fetcher.request('GET /repos/{owner}/{repo}/pulls/{pull_number}', pullRequestParams);
+
+ return {
+ id: `${data.number}`,
+ title: data.title,
+ state: getPullState(data),
+ creator: data.user?.login,
+ createTime: new Date(data.created_at),
+ mergeTime: data.merged_at ? new Date(data.merged_at) : null,
+ closeTime: data.closed_at ? new Date(data.closed_at) : null,
+ source: data.head.label,
+ target: data.base.label,
+ sourceSha: data.head.sha,
+ targetSha: data.base.sha,
+ avatarUrl: data.user?.avatar_url,
+ };
+ }
+
+ async provideCodeReviewChangedFiles(
+ repoFullName: string,
+ id: string,
+ options?: CommonQueryOptions,
+ ): Promise {
+ const fetcher = GitHubFetcher.getInstance();
+ const { owner, repo } = parseRepoFullName(repoFullName);
+ const pageParams = { per_page: options?.pageSize, page: options?.page };
+ const filesRequestParams = { owner, repo, pull_number: Number(id), ...pageParams };
+ const { data } = await fetcher.request('GET /repos/{owner}/{repo}/pulls/{pull_number}/files', filesRequestParams);
+
+ return data.map((item) => ({
+ path: normalizePath(item.filename),
+ previousPath: item.previous_filename ? normalizePath(item.previous_filename) : undefined,
+ status: item.status as FileChangeStatus,
+ }));
+ }
+
+ @trySourcegraphApiFirst
+ async provideFileBlameRanges(repoFullName: string, ref: string, path: string): Promise {
+ const fetcher = GitHubFetcher.getInstance();
+ const { owner, repo } = parseRepoFullName(repoFullName);
+ const requestParams = { owner, repo, ref, path: trimStart(path, '/') };
+ const data = await fetcher.graphql(FILE_BLAME_QUERY, requestParams);
+ const blameRanges = (data as any)?.repository?.object?.blame?.ranges;
+
+ return (blameRanges || []).map((item) => ({
+ age: item.age as number,
+ startingLine: item.startingLine as number,
+ endingLine: item.endingLine as number,
+ commit: {
+ sha: item.commit?.sha as string,
+ author: item.commit?.author?.name as string,
+ email: item.commit?.author?.email as string,
+ message: item.commit?.message as string,
+ createTime: new Date(item.commit?.authoredDate),
+ avatarUrl: item.commit?.author?.avatarUrl as string,
+ },
+ }));
+ }
+
+ provideSymbolDefinitions(
+ repoFullName: string,
+ ref: string,
+ path: string,
+ line: number,
+ character: number,
+ symbol: string,
+ ): Promise {
+ return sourcegraphDataSource.provideSymbolDefinitions(repoFullName, ref, path, line, character, symbol);
+ }
+
+ async provideSymbolReferences(
+ repoFullName: string,
+ ref: string,
+ path: string,
+ line: number,
+ character: number,
+ symbol: string,
+ ): Promise {
+ return sourcegraphDataSource.provideSymbolReferences(repoFullName, ref, path, line, character, symbol);
+ }
+
+ async provideSymbolHover(
+ repoFullName: string,
+ ref: string,
+ path: string,
+ line: number,
+ character: number,
+ _symbol: string,
+ ): Promise {
+ return sourcegraphDataSource.provideSymbolHover(repoFullName, ref, path, line, character, _symbol);
+ }
+
+ provideUserAvatarLink(user: string): string {
+ return `${GITHUB_ORIGIN}/${user}.png`;
+ }
+}
diff --git a/extensions/github1s/src/adapters/github1s/fetcher.ts b/extensions/github1s/src/adapters/github1s/fetcher.ts
new file mode 100644
index 000000000..3b48abb45
--- /dev/null
+++ b/extensions/github1s/src/adapters/github1s/fetcher.ts
@@ -0,0 +1,148 @@
+/**
+ * @file github api fetcher base octokit
+ * @author netcon
+ */
+
+(self as any).global = self;
+import * as vscode from 'vscode';
+import { getExtensionContext } from '@/helpers/context';
+import { Octokit } from '@octokit/core';
+import { GitHub1sAuthenticationView } from './authentication';
+import { GitHubTokenManager } from './token';
+import { isNil } from '@/helpers/util';
+import { getCurrentRepo } from './parse-path';
+import { SourcegraphDataSource } from '../sourcegraph/data-source';
+
+export const errorMessages = {
+ rateLimited: {
+ anonymous: 'API Rate Limit Exceeded, please authenticate to github and retry',
+ authenticated: 'API Rate Limit Exceeded for this token, please try another account',
+ },
+ badCredentials: {
+ anonymous: 'Bad credentials, please authenticate to github and retry',
+ authenticated: 'This token is invalid, please try another one',
+ },
+ repoNotFound: {
+ anonymous: 'Repository not found, if it is private, you can provide an AccessToken to access it',
+ authenticated: 'Repository not found, if it is private, you can try change an AccessToken to access it',
+ },
+ noPermission: {
+ anonymous: 'You have no permission for this operation, please authenticate to github and retry',
+ authenticated: 'You have no permission for this operation, please try another account',
+ },
+};
+
+const detectErrorMessage = (response: any, authenticated: boolean) => {
+ if (response?.status === 403 && +response?.headers?.['x-ratelimit-remaining'] === 0) {
+ return errorMessages.rateLimited[authenticated ? 'authenticated' : 'anonymous'];
+ }
+ if (response?.status === 401 && response?.data?.message?.includes?.('Bad credentials')) {
+ return errorMessages.badCredentials[authenticated ? 'authenticated' : 'anonymous'];
+ }
+ if (response?.status === 404) {
+ return errorMessages.repoNotFound[authenticated ? 'authenticated' : 'anonymous'];
+ }
+ if (response?.status === 403) {
+ return errorMessages.noPermission[authenticated ? 'authenticated' : 'anonymous'];
+ }
+ return response?.data?.message || '';
+};
+
+const PREFER_SOURCEGRAPH_API = 'PREFER_SOURCEGRAPH_API';
+
+export class GitHubFetcher {
+ private static instance: GitHubFetcher | null = null;
+ private _emitter = new vscode.EventEmitter();
+ private _request: Octokit['request'] | null = null;
+ public onDidChangePreferSourcegraphApi = this._emitter.event;
+ private _currentRepoPromise: Promise | null = null;
+ private _sgApiTimeout: boolean = false;
+
+ public request: Octokit['request'];
+ public graphql: Octokit['graphql'];
+
+ public static getInstance(): GitHubFetcher {
+ if (GitHubFetcher.instance) {
+ return GitHubFetcher.instance;
+ }
+ return (GitHubFetcher.instance = new this());
+ }
+
+ private constructor() {
+ this.initFetcherMethods();
+ this.initPreferSourcegraphApi();
+ GitHubTokenManager.getInstance().onDidChangeToken(() => this.initFetcherMethods());
+ GitHubTokenManager.getInstance().onDidChangeToken(() => this.initPreferSourcegraphApi());
+ }
+
+ // initial fetcher methods in this way for correct `request/graphql` type inference
+ initFetcherMethods() {
+ const accessToken = GitHubTokenManager.getInstance().getToken();
+ const octokit = new Octokit({ auth: accessToken, request: { fetch }, baseUrl: GITHUB_API_PREFIX });
+
+ this._request = octokit.request;
+ this.request = Object.assign((...args: Parameters) => {
+ return octokit.request(...args).catch(async (error) => {
+ const errorStatus = error?.response?.status as number | undefined;
+ const repoNotFound = errorStatus === 404 && !(await this.resolveCurrentRepo());
+ if ((errorStatus && [401, 403].includes(errorStatus)) || repoNotFound) {
+ // maybe we have to acquire github access token to continue
+ const message = detectErrorMessage(error?.response, !!accessToken);
+ await GitHub1sAuthenticationView.getInstance().open(message, true);
+ return this._request!(...args);
+ }
+ });
+ }, this._request);
+
+ this.graphql = Object.assign(async (...args: Parameters) => {
+ if (!GitHubTokenManager.getInstance().getToken()) {
+ const message = 'GraphQL API only works for authenticated users';
+ await GitHub1sAuthenticationView.getInstance().open(message, true);
+ }
+ return octokit.graphql(...args);
+ }, octokit.graphql);
+ }
+
+ private resolveCurrentRepo(forceUpdate: boolean = false) {
+ if (this._currentRepoPromise && !forceUpdate) {
+ return this._currentRepoPromise;
+ }
+ const requestPattern = '/repos/{owner}/{repo}' as const;
+ const getOwnerRepo = () => getCurrentRepo().then((repo) => repo.split('/'));
+ return (this._currentRepoPromise = Promise.resolve(getOwnerRepo())
+ .then(([owner, repo]) => this._request?.(requestPattern, { owner, repo }).then((res) => res.data))
+ .catch(() => null));
+ }
+
+ private async initPreferSourcegraphApi() {
+ if (await this.getPreferSourcegraphApi()) {
+ const sgDataSource = SourcegraphDataSource.getInstance('github');
+ try {
+ if (!(await sgDataSource.provideRepository(await getCurrentRepo()))) {
+ this.resolveCurrentRepo(true).then((repo) => {
+ repo?.private && this.setPreferSourcegraphApi(false);
+ });
+ }
+ } catch (e) {
+ if (e.message && e.message.includes('signal is aborted')) {
+ this._sgApiTimeout = true;
+ }
+ }
+ }
+ }
+
+ public async getPreferSourcegraphApi(repo?: string): Promise {
+ const targetRepo = repo || (await getCurrentRepo());
+ const globalState = getExtensionContext().globalState;
+ const cachedData: Record | undefined = globalState.get(PREFER_SOURCEGRAPH_API);
+ return !isNil(cachedData?.[targetRepo]) ? !!cachedData?.[targetRepo] : this._sgApiTimeout ? false : true;
+ }
+
+ public async setPreferSourcegraphApi(value: boolean, repo?: string) {
+ const targetRepo = repo || (await getCurrentRepo());
+ const globalState = getExtensionContext().globalState;
+ const cachedData: Record | undefined = globalState.get(PREFER_SOURCEGRAPH_API);
+ await globalState.update(PREFER_SOURCEGRAPH_API, { ...cachedData, [targetRepo]: value });
+ this._emitter.fire(value);
+ }
+}
diff --git a/extensions/github1s/src/adapters/github1s/graphql.ts b/extensions/github1s/src/adapters/github1s/graphql.ts
new file mode 100644
index 000000000..3ea838044
--- /dev/null
+++ b/extensions/github1s/src/adapters/github1s/graphql.ts
@@ -0,0 +1,32 @@
+/**
+ * @file github graphql queries
+ * @author netcon
+ */
+
+export const FILE_BLAME_QUERY = `
+query fileBlameQuery($owner: String!, $repo: String!, $ref: String!, $path: String!) {
+ repository(owner: $owner, name: $repo) {
+ object(expression: $ref) {
+ ... on Commit {
+ blame(path: $path) {
+ ranges {
+ age
+ startingLine
+ endingLine
+ commit {
+ sha: oid
+ message
+ authoredDate
+ author {
+ avatarUrl
+ name
+ email
+ }
+ }
+ }
+ }
+ }
+ }
+ }
+}
+`;
diff --git a/extensions/github1s/src/adapters/github1s/index.ts b/extensions/github1s/src/adapters/github1s/index.ts
new file mode 100644
index 000000000..7451ae337
--- /dev/null
+++ b/extensions/github1s/src/adapters/github1s/index.ts
@@ -0,0 +1,60 @@
+/**
+ * @file GitHub1s adapter
+ * @author netcon
+ */
+
+import * as vscode from 'vscode';
+import { setVSCodeContext } from '@/helpers/vscode';
+import { GitHub1sDataSource } from './data-source';
+import { GitHub1sRouterParser } from './router-parser';
+import { GitHub1sSettingsViewProvider } from './settings';
+import { GitHub1sAuthenticationView } from './authentication';
+import { Adapter, CodeReviewType, PlatformName } from '../types';
+import { SourcegraphDataSource } from '../sourcegraph/data-source';
+import { getCurrentRepo } from './parse-path';
+
+export class GitHub1sAdapter implements Adapter {
+ public scheme: string = 'github1s';
+ public platformName = PlatformName.GitHub;
+ public codeReviewType = CodeReviewType.PullRequest;
+
+ resolveDataSource() {
+ return Promise.resolve(GitHub1sDataSource.getInstance());
+ }
+
+ resolveRouterParser() {
+ return Promise.resolve(GitHub1sRouterParser.getInstance());
+ }
+
+ activateAsDefault() {
+ // register settings view and show it in activity bar
+ setVSCodeContext('github1s:views:settings:visible', true);
+ setVSCodeContext('github1s:views:codeReviewList:visible', true);
+ setVSCodeContext('github1s:views:commitList:visible', true);
+ setVSCodeContext('github1s:views:fileHistory:visible', true);
+ setVSCodeContext('github1s:features:gutterBlame:enabled', true);
+
+ vscode.window.registerWebviewViewProvider(
+ GitHub1sSettingsViewProvider.viewType,
+ new GitHub1sSettingsViewProvider(),
+ );
+ vscode.commands.registerCommand('github1s.commands.openGitHub1sAuthPage', () => {
+ return GitHub1sAuthenticationView.getInstance().open();
+ });
+ vscode.commands.registerCommand('github1s.commands.syncSourcegraphRepository', async () => {
+ const dataSource = SourcegraphDataSource.getInstance('github');
+ const randomRef = (Math.random() + 1).toString(36).slice(2);
+ return dataSource.provideCommit(await getCurrentRepo(), randomRef).then(() => {
+ return vscode.commands.executeCommand('workbench.action.reloadWindow');
+ });
+ });
+ }
+
+ deactivateAsDefault() {
+ setVSCodeContext('github1s:views:settings:visible', false);
+ setVSCodeContext('github1s:views:codeReviewList:visible', false);
+ setVSCodeContext('github1s:views:commitList:visible', false);
+ setVSCodeContext('github1s:views:fileHistory:visible', false);
+ setVSCodeContext('github1s:features:gutterBlame:enabled', false);
+ }
+}
diff --git a/extensions/github1s/src/adapters/github1s/parse-path.ts b/extensions/github1s/src/adapters/github1s/parse-path.ts
new file mode 100644
index 000000000..cf20392ef
--- /dev/null
+++ b/extensions/github1s/src/adapters/github1s/parse-path.ts
@@ -0,0 +1,169 @@
+/**
+ * @file parse github path
+ * @author netcon
+ */
+
+import * as vscode from 'vscode';
+import { parsePath } from 'history';
+import { PageType, RouterState } from '@/adapters/types';
+import { GitHub1sDataSource } from './data-source';
+import * as queryString from 'query-string';
+import { memorize } from '@/helpers/func';
+import { getBrowserUrl } from '@/helpers/context';
+
+export const DEFAULT_REPO = 'conwnet/github1s';
+
+export const getCurrentRepo = memorize(() => {
+ return getBrowserUrl().then((browserUrl: string) => {
+ const pathParts = vscode.Uri.parse(browserUrl).path.split('/').filter(Boolean);
+ return pathParts.length >= 2 ? (pathParts.slice(0, 2) as [string, string]).join('/') : DEFAULT_REPO;
+ });
+});
+
+export const getDefaultBranch = async (repo: string): Promise => {
+ const dataSource = GitHub1sDataSource.getInstance();
+ return dataSource.getDefaultBranch(repo);
+};
+
+const parseTreeUrl = async (path: string): Promise => {
+ const pathParts = parsePath(path).pathname!.split('/').filter(Boolean);
+ const [owner, repo, _pageType, ...restParts] = pathParts;
+ const repoFullName = `${owner}/${repo}`;
+ const dataSource = GitHub1sDataSource.getInstance();
+ const { ref, path: filePath } = await dataSource.extractRefPath(repoFullName, restParts.join('/'));
+
+ return { pageType: PageType.Tree, repo: repoFullName, ref, filePath };
+};
+
+const parseBlobUrl = async (path: string): Promise => {
+ const routerState = (await parseTreeUrl(path)) as any;
+ const { hash: routerHash } = parsePath(path);
+
+ if (!routerHash) {
+ return { ...routerState, pageType: PageType.Blob };
+ }
+
+ // get selected line number range from path which looks like:
+ // `/conwnet/github1s/blob/master/package.json#L10-L20`
+ const matches = routerHash.match(/^#L(\d+)(?:-L(\d+))?/);
+ const [_, startLineNumber = '0', endLineNumber] = matches ? matches : [];
+
+ return {
+ ...routerState,
+ pageType: PageType.Blob,
+ startLine: parseInt(startLineNumber, 10),
+ endLine: parseInt(endLineNumber || startLineNumber, 10),
+ };
+};
+
+const parseCommitsUrl = async (path: string): Promise => {
+ const pathParts = parsePath(path).pathname!.split('/').filter(Boolean);
+ const [owner, repo, _pageType, ...refParts] = pathParts;
+
+ return {
+ repo: `${owner}/${repo}`,
+ pageType: PageType.CommitList,
+ ref: refParts.length ? refParts.join('/') : await getDefaultBranch(`${owner}/${repo}`),
+ };
+};
+
+const parseCommitUrl = async (path: string): Promise => {
+ const pathParts = parsePath(path).pathname!.split('/').filter(Boolean);
+ const [owner, repo, _pageType, ...refParts] = pathParts;
+ const commitSha = refParts.join('/');
+
+ return { repo: `${owner}/${repo}`, pageType: PageType.Commit, ref: commitSha, commitSha };
+};
+
+const parsePullsUrl = async (path: string): Promise => {
+ const [owner, repo] = parsePath(path).pathname!.split('/').filter(Boolean);
+
+ return {
+ repo: `${owner}/${repo}`,
+ ref: await getDefaultBranch(`${owner}/${repo}`),
+ pageType: PageType.CodeReviewList,
+ };
+};
+
+const parsePullUrl = async (path: string): Promise => {
+ const pathParts = parsePath(path).pathname!.split('/').filter(Boolean);
+ const [owner, repo, _pageType, codeReviewId] = pathParts;
+ const repoFullName = `${owner}/${repo}`;
+ const codeReview = await GitHub1sDataSource.getInstance().provideCodeReview(repoFullName, codeReviewId);
+
+ return {
+ repo: `${owner}/${repo}`,
+ pageType: PageType.CodeReview,
+ ref: codeReview.targetSha,
+ codeReviewId,
+ };
+};
+
+const parseSearchUrl = async (path: string): Promise => {
+ const { pathname, search } = parsePath(path);
+ const pathParts = pathname!.split('/').filter(Boolean);
+ const [owner, repo, _pageType] = pathParts;
+ const queryOptions = queryString.parse(search || '');
+ const query = typeof queryOptions.q === 'string' ? queryOptions.q : '';
+ const isRegex = queryOptions.regex === 'yes';
+ const isCaseSensitive = queryOptions.case === 'yes';
+ const matchWholeWord = queryOptions.whole === 'yes';
+ const filesToInclude = typeof queryOptions['files-to-include'] === 'string' ? queryOptions['files-to-include'] : '';
+ const filesToExclude = typeof queryOptions['files-to-exclude'] === 'string' ? queryOptions['files-to-exclude'] : '';
+
+ return {
+ repo: `${owner}/${repo}`,
+ pageType: PageType.Search,
+ ref: await getDefaultBranch(`${owner}/${repo}`),
+ query,
+ isRegex,
+ isCaseSensitive,
+ matchWholeWord,
+ filesToInclude,
+ filesToExclude,
+ };
+};
+
+const PAGE_TYPE_MAP = {
+ tree: PageType.Tree,
+ blob: PageType.Blob,
+ pulls: PageType.CodeReviewList,
+ pull: PageType.CodeReview,
+ commit: PageType.Commit,
+ commits: PageType.CommitList,
+ search: PageType.Search,
+};
+
+export const parseGitHubPath = async (path: string): Promise => {
+ const pathParts = parsePath(path).pathname?.split('/').filter(Boolean) || [];
+ // detect concrete PageType the *third part* in url.path
+ const pageType = pathParts[2] ? PAGE_TYPE_MAP[pathParts[2]] || PageType.Unknown : PageType.Tree;
+
+ if (pathParts.length >= 2) {
+ switch (pageType) {
+ case PageType.Tree:
+ case PageType.Unknown:
+ return parseTreeUrl(path);
+ case PageType.Blob:
+ return parseBlobUrl(path);
+ case PageType.CodeReview:
+ return parsePullUrl(path);
+ case PageType.CodeReviewList:
+ return parsePullsUrl(path);
+ case PageType.Commit:
+ return parseCommitUrl(path);
+ case PageType.CommitList:
+ return parseCommitsUrl(path);
+ case PageType.Search:
+ return parseSearchUrl(path);
+ }
+ }
+
+ // fallback to default
+ return {
+ repo: DEFAULT_REPO,
+ ref: await getDefaultBranch(DEFAULT_REPO),
+ pageType: PageType.Tree,
+ filePath: '/',
+ };
+};
diff --git a/extensions/github1s/src/adapters/github1s/router-parser.ts b/extensions/github1s/src/adapters/github1s/router-parser.ts
new file mode 100644
index 000000000..a38dab5bc
--- /dev/null
+++ b/extensions/github1s/src/adapters/github1s/router-parser.ts
@@ -0,0 +1,52 @@
+/**
+ * @file router parser
+ * @author netcon
+ */
+
+import { joinPath } from '@/helpers/util';
+import * as adapterTypes from '../types';
+import { parseGitHubPath } from './parse-path';
+
+export class GitHub1sRouterParser extends adapterTypes.RouterParser {
+ protected static instance: GitHub1sRouterParser | null = null;
+
+ public static getInstance(): GitHub1sRouterParser {
+ if (GitHub1sRouterParser.instance) {
+ return GitHub1sRouterParser.instance;
+ }
+ return (GitHub1sRouterParser.instance = new GitHub1sRouterParser());
+ }
+
+ parsePath(path: string): Promise {
+ return parseGitHubPath(path);
+ }
+
+ buildTreePath(repo: string, ref?: string, filePath?: string): string {
+ return ref ? `/${repo}/tree/${ref}${filePath && filePath !== '/' ? filePath : ''}` : `/${repo}`;
+ }
+
+ buildBlobPath(repo: string, ref: string, filePath: string, startLine?: number, endLine?: number): string {
+ const hash = startLine ? (endLine ? `#L${startLine}-L${endLine}` : `#L${startLine}`) : '';
+ return `/${repo}/blob/${ref}${filePath}${hash}`;
+ }
+
+ buildCommitListPath(repo: string): string {
+ return `/${repo}/commits`;
+ }
+
+ buildCommitPath(repo: string, commitSha: string): string {
+ return `/${repo}/commit/${commitSha}`;
+ }
+
+ buildCodeReviewListPath(repo: string): string {
+ return `/${repo}/pulls`;
+ }
+
+ buildCodeReviewPath(repo: string, codeReviewId: string): string {
+ return `/${repo}/pull/${codeReviewId}`;
+ }
+
+ buildExternalLink(path: string): string {
+ return joinPath(GITHUB_ORIGIN, path);
+ }
+}
diff --git a/extensions/github1s/src/adapters/github1s/settings.ts b/extensions/github1s/src/adapters/github1s/settings.ts
new file mode 100644
index 000000000..2b2862632
--- /dev/null
+++ b/extensions/github1s/src/adapters/github1s/settings.ts
@@ -0,0 +1,107 @@
+/**
+ * @file GitHub1s Settings Webview Provider
+ * @author netcon
+ */
+
+import * as vscode from 'vscode';
+import { getExtensionContext } from '@/helpers/context';
+import { createPageHtml, getWebviewOptions } from '@/helpers/page';
+import { GitHubTokenManager } from './token';
+import { GitHubFetcher } from './fetcher';
+
+export const messageApiMap = {
+ info: vscode.window.showInformationMessage,
+ warning: vscode.window.showWarningMessage,
+ error: vscode.window.showErrorMessage,
+};
+
+export class GitHub1sSettingsViewProvider implements vscode.WebviewViewProvider {
+ public static readonly viewType = 'github1s.views.settings';
+
+ protected tokenManager = GitHubTokenManager.getInstance();
+ protected apiFetcher: Pick<
+ GitHubFetcher,
+ 'getPreferSourcegraphApi' | 'setPreferSourcegraphApi' | 'onDidChangePreferSourcegraphApi'
+ > = GitHubFetcher.getInstance();
+
+ protected pageTitle = 'GitHub1s Settings';
+ protected OAuthCommand = 'github1s.commands.vscode.connectToGitHub';
+ protected detailPageCommand = 'github1s.commands.openGitHub1sAuthPage';
+ protected pageConfig = {
+ pageDescriptionLines: [
+ 'For unauthenticated requests, the rate limit of GitHub allows for up to 60 requests per hour.',
+ 'For API requests using Authentication, you can make up to 5,000 requests per hour.',
+ ],
+ OAuthButtonText: 'Connect to GitHub',
+ createTokenLink: `${GITHUB_ORIGIN}/settings/tokens/new?scopes=repo&description=GitHub1s`,
+ };
+
+ public registerListeners(webviewView: vscode.WebviewView) {
+ webviewView.webview.onDidReceiveMessage((message) => {
+ const commonResponse = { id: message.id, type: message.type };
+ const postMessage = (data?: unknown) => webviewView.webview.postMessage({ ...commonResponse, data });
+
+ switch (message.type) {
+ case 'get-token':
+ postMessage(this.tokenManager.getToken());
+ break;
+ case 'set-token':
+ this.tokenManager.setToken(message.data || '').then(() => postMessage());
+ break;
+ case 'validate-token':
+ this.tokenManager.validateToken(message.data).then((tokenStatus) => postMessage(tokenStatus));
+ break;
+ case 'open-detail-page':
+ vscode.commands.executeCommand(this.detailPageCommand).then(() => postMessage());
+ break;
+ case 'oauth-authorizing':
+ vscode.commands.executeCommand(this.OAuthCommand).then((data: any) => {
+ if (data && data.error_description) {
+ vscode.window.showErrorMessage(data.error_description);
+ } else if (data && data.access_token) {
+ this.tokenManager.setToken(data.access_token || '');
+ }
+ postMessage();
+ });
+ break;
+ case 'call-vscode-message-api':
+ const messageApi = messageApiMap[message.data?.level];
+ messageApi && messageApi(...message.data?.args).then((response) => postMessage(response));
+ break;
+ case 'get-prefer-sourcegraph-api':
+ this.apiFetcher.getPreferSourcegraphApi().then((value) => postMessage(value));
+ break;
+ case 'set-prefer-sourcegraph-api':
+ this.apiFetcher.setPreferSourcegraphApi(message.data);
+ postMessage(message.data);
+ break;
+ }
+ });
+
+ this.tokenManager.onDidChangeToken((token) => {
+ webviewView.webview.postMessage({ type: 'token-changed', token });
+ });
+ this.apiFetcher.onDidChangePreferSourcegraphApi((value) => {
+ webviewView.webview.postMessage({ type: 'prefer-sourcegraph-api-changed', value });
+ });
+ }
+
+ public resolveWebviewView(webviewView: vscode.WebviewView): void | Thenable {
+ const extensionContext = getExtensionContext();
+
+ this.registerListeners(webviewView);
+ webviewView.webview.options = getWebviewOptions(extensionContext.extensionUri);
+
+ const styles = [
+ vscode.Uri.joinPath(extensionContext.extensionUri, 'assets/pages/components.css').toString(),
+ vscode.Uri.joinPath(extensionContext.extensionUri, 'assets/pages/github1s-settings.css').toString(),
+ ];
+ const globalPageConfig = { ...this.pageConfig, extensionUri: extensionContext.extensionUri.toString() };
+ const scripts = [
+ 'data:text/javascript;base64,' +
+ Buffer.from(`window.pageConfig=${JSON.stringify(globalPageConfig)};`).toString('base64'),
+ vscode.Uri.joinPath(extensionContext.extensionUri, 'assets/pages/github1s-settings.js').toString(),
+ ];
+ webviewView.webview.html = createPageHtml(this.pageTitle, styles, scripts);
+ }
+}
diff --git a/extensions/github1s/src/adapters/github1s/token.ts b/extensions/github1s/src/adapters/github1s/token.ts
new file mode 100644
index 000000000..69e4e8f4e
--- /dev/null
+++ b/extensions/github1s/src/adapters/github1s/token.ts
@@ -0,0 +1,72 @@
+/**
+ * @file github api auth token manager
+ */
+
+import * as vscode from 'vscode';
+import { getExtensionContext } from '@/helpers/context';
+
+export interface ValidateResult {
+ username: string;
+ avatar_url: string;
+ profile_url: string;
+ ratelimits?: {
+ limit?: number;
+ remaining?: number;
+ reset?: number;
+ resource?: number;
+ used?: number;
+ };
+}
+
+export class GitHubTokenManager {
+ protected static instance: GitHubTokenManager | null = null;
+ private _emitter = new vscode.EventEmitter();
+ public onDidChangeToken = this._emitter.event;
+ public tokenStateKey = 'github-oauth-token';
+
+ protected constructor() {}
+ public static getInstance(): GitHubTokenManager {
+ if (GitHubTokenManager.instance) {
+ return GitHubTokenManager.instance;
+ }
+ return (GitHubTokenManager.instance = new this());
+ }
+
+ public getToken(): string {
+ return getExtensionContext().globalState.get(this.tokenStateKey) || '';
+ }
+
+ public async setToken(token: string) {
+ const isTokenChanged = this.getToken() !== token;
+ return getExtensionContext()
+ .globalState.update(this.tokenStateKey, token)
+ .then(() => isTokenChanged && this._emitter.fire(token));
+ }
+
+ public async validateToken(token?: string): Promise {
+ const accessToken = token === undefined ? this.getToken() : token;
+ if (!accessToken) {
+ return Promise.resolve(null);
+ }
+ const fetchOptions = accessToken ? { headers: { Authorization: `token ${accessToken}` } } : {};
+ return fetch(`${GITHUB_API_PREFIX}/user`, fetchOptions)
+ .then((response) => {
+ if (response.status === 401) {
+ return null;
+ }
+ return response.json().then((data) => ({
+ username: data.login,
+ avatar_url: data.avatar_url,
+ profile_url: data.html_url,
+ rateLimits: {
+ limit: +response.headers.get('x-ratelimit-limit')! || 0,
+ remaining: +response.headers.get('x-ratelimit-remaining')! || 0,
+ reset: +response.headers.get('x-ratelimit-reset')! || 0,
+ resource: +response.headers.get('ratelimit-resource')! || 0,
+ used: +response.headers.get('x-ratelimit-used')! || 0,
+ },
+ }));
+ })
+ .catch(() => null);
+ }
+}
diff --git a/extensions/github1s/src/adapters/gitlab1s/authentication.ts b/extensions/github1s/src/adapters/gitlab1s/authentication.ts
new file mode 100644
index 000000000..920ec6b63
--- /dev/null
+++ b/extensions/github1s/src/adapters/gitlab1s/authentication.ts
@@ -0,0 +1,29 @@
+/**
+ * @file gitlab authentication page
+ * @author netcon
+ */
+
+import { GitHub1sAuthenticationView } from '../github1s/authentication';
+import { GitLabTokenManager } from './token';
+
+export class GitLab1sAuthenticationView extends GitHub1sAuthenticationView {
+ protected tokenManager = GitLabTokenManager.getInstance();
+ protected pageTitle = 'Authenticating to GitLab';
+ protected OAuthCommand = 'github1s.commands.vscode.connectToGitLab';
+ protected pageConfig = {
+ authenticationFormTitle: 'Authenticating to GitLab',
+ OAuthButtonText: 'Connect to GitLab',
+ OAuthButtonLogo: 'assets/pages/assets/gitlab.svg',
+ createTokenLink: `${GITLAB_ORIGIN}/-/profile/personal_access_tokens?scopes=read_api&name=GitLab1s`,
+ authenticationFeatures: [
+ {
+ text: 'Access GitLab personal repository',
+ link: 'https://docs.gitlab.com/ee/user/profile/personal_access_tokens.html',
+ },
+ {
+ text: 'Higher rate limit for GitLab official API',
+ link: 'https://docs.gitlab.com/ee/security/rate_limits.html',
+ },
+ ],
+ };
+}
diff --git a/extensions/github1s/src/adapters/gitlab1s/data-source.ts b/extensions/github1s/src/adapters/gitlab1s/data-source.ts
new file mode 100644
index 000000000..9b79a2a95
--- /dev/null
+++ b/extensions/github1s/src/adapters/gitlab1s/data-source.ts
@@ -0,0 +1,462 @@
+/**
+ * @file gitlab1s data-source-provider
+ * @author netcon
+ */
+
+import {
+ Branch,
+ CodeReview,
+ CodeReviewState,
+ TextSearchOptions,
+ Commit,
+ CommonQueryOptions,
+ DataSource,
+ Directory,
+ DirectoryEntry,
+ File,
+ BlameRange,
+ FileType,
+ Tag,
+ TextSearchResults,
+ TextSearchQuery,
+ SymbolDefinitions,
+ SymbolReferences,
+ FileChangeStatus,
+ ChangedFile,
+ SymbolHover,
+ CommitsQueryOptions,
+ CodeReviewsQueryOptions,
+} from '../types';
+import { toUint8Array } from 'js-base64';
+import { matchSorter } from 'match-sorter';
+import { GitLabFetcher } from './fetcher';
+import { SourcegraphDataSource } from '../sourcegraph/data-source';
+import { decorate, memorize } from '@/helpers/func';
+import { trimStart, normalizePath, isString } from '@/helpers/util';
+
+const FileTypeMap = {
+ blob: FileType.File,
+ tree: FileType.Directory,
+ commit: FileType.Submodule,
+};
+
+const getMergeRequestState = (mergeRequest: { state: string; merged_at: string | null }): CodeReviewState => {
+ // current merge request is open
+ if (mergeRequest.state === 'opened') {
+ return CodeReviewState.Open;
+ }
+ // current merge request is merged
+ if (mergeRequest.state === 'closed' && mergeRequest.merged_at) {
+ return CodeReviewState.Merged;
+ }
+ // current merge is closed
+ return CodeReviewState.Closed;
+};
+
+const resolveComputeAge = (timestamps: number[], ageLimit = 10) => {
+ const maxTimestamp = Math.max(...timestamps);
+ const minTimestamp = Math.min(...timestamps);
+ const step = (maxTimestamp - minTimestamp) / ageLimit;
+ return (timestamp: number) => {
+ const age = Math.floor((timestamp - minTimestamp) / (step || 1));
+ return (((Math.max(age, ageLimit - 1) % ageLimit) + ageLimit) % ageLimit) + 1;
+ };
+};
+
+const sourcegraphDataSource = SourcegraphDataSource.getInstance('gitlab');
+const trySourcegraphApiFirst = (_target: any, propertyKey: string, descriptor: PropertyDescriptor) => {
+ const originalMethod = descriptor.value;
+
+ descriptor.value = async function Promise>(...args: Parameters) {
+ const gitlabFetcher = GitLabFetcher.getInstance();
+ if (await gitlabFetcher.getPreferSourcegraphApi(args[0])) {
+ try {
+ return await sourcegraphDataSource[propertyKey](...args);
+ } catch (e) {}
+ }
+ return originalMethod.apply(this, args);
+ };
+};
+
+export class GitLab1sDataSource extends DataSource {
+ private static instance: GitLab1sDataSource | null = null;
+ private branchesPromiseMap: Map> = new Map();
+ private tagsPromiseMap: Map> = new Map();
+ private matchedRefsMap = new Map();
+
+ public static getInstance(): GitLab1sDataSource {
+ if (GitLab1sDataSource.instance) {
+ return GitLab1sDataSource.instance;
+ }
+ return (GitLab1sDataSource.instance = new GitLab1sDataSource());
+ }
+
+ async provideRepository(repo: string) {
+ const fetcher = GitLabFetcher.getInstance();
+ const { data } = await fetcher.request('GET /projects/{repo}', { repo });
+ return { private: data.visibility === 'private', defaultBranch: data.default_branch };
+ }
+
+ @trySourcegraphApiFirst
+ async provideDirectory(repo: string, ref: string, path: string, recursive = false): Promise {
+ const fetcher = GitLabFetcher.getInstance();
+ let page = 1;
+ let files = [];
+ const parseTreeItem = (treeItem): DirectoryEntry => ({
+ path: normalizePath(treeItem.path),
+ type: FileTypeMap[treeItem.type] || FileType.File,
+ commitSha: FileTypeMap[treeItem.type] === FileType.Submodule ? treeItem.sha || 'HEAD' : undefined,
+ size: treeItem.size,
+ });
+ while (page > 0) {
+ const requestParams = { ref, page, path: trimStart(path, '/'), repo, recursive };
+ const { data, headers } = await fetcher.request(
+ 'GET /projects/{repo}/repository/tree?recursive={recursive}&per_page=100&page={page}&ref={ref}&path={path}',
+ requestParams,
+ );
+ files = files.concat(data);
+ const nextPage = Number(headers!.get('x-next-page'));
+ page = nextPage > page ? nextPage : 0;
+ }
+
+ return {
+ entries: files.map(parseTreeItem),
+ truncated: false,
+ };
+ }
+
+ @trySourcegraphApiFirst
+ async provideFile(repo: string, ref: string, path: string): Promise {
+ const fetcher = GitLabFetcher.getInstance();
+ const requestParams = { ref, path: trimStart(path, '/'), repo };
+ const { data } = await fetcher.request('GET /projects/{repo}/repository/files/{path}?ref={ref}', requestParams);
+ return { content: toUint8Array((data as any).content) };
+ }
+
+ @decorate(memorize)
+ async getBranches(repo: string, ref: 'heads' | 'tags'): Promise {
+ const fetcher = GitLabFetcher.getInstance();
+ const requestParams = { repo, ref };
+ const { data } = await fetcher.request('GET /projects/{repo}/repository/branches', requestParams);
+ return data.map((item) => ({
+ name: item.name,
+ commitSha: item.commit.id,
+ description: `${ref === 'heads' ? 'Branch' : 'Tag'} at ${item.commit.short_id}`,
+ }));
+ }
+
+ @decorate(memorize)
+ async getTags(repo: string, ref: 'heads' | 'tags'): Promise {
+ const fetcher = GitLabFetcher.getInstance();
+ const requestParams = { repo, ref };
+ const { data } = await fetcher.request('GET /projects/{repo}/repository/tags', requestParams);
+ return data.map((item) => ({
+ name: item.name,
+ commitSha: item.commit.id,
+ description: `${ref === 'heads' ? 'Branch' : 'Tag'} at ${item.commit.short_id}`,
+ }));
+ }
+
+ @decorate(memorize)
+ async getDefaultBranch(repo: string) {
+ return (await this.provideRepository(repo))?.defaultBranch || 'HEAD';
+ }
+
+ @trySourcegraphApiFirst
+ async extractRefPath(repo: string, refAndPath: string): Promise<{ ref: string; path: string }> {
+ if (!refAndPath) {
+ return { ref: await this.getDefaultBranch(repo), path: '/' };
+ }
+ if (refAndPath.match(/^HEAD(\/.*)?$/i)) {
+ return { ref: 'HEAD', path: normalizePath(refAndPath.slice(5)) };
+ }
+ if (!this.matchedRefsMap.has(repo)) {
+ this.matchedRefsMap.set(repo, []);
+ }
+ const matchPathRef = (ref) => refAndPath.startsWith(`${ref}/`) || refAndPath === ref;
+ const pathRef = this.matchedRefsMap.get(repo)?.find(matchPathRef);
+ if (pathRef) {
+ return { ref: pathRef, path: normalizePath(refAndPath.slice(pathRef.length + 1)) };
+ }
+ const [branches, tags] = await this.prepareAllRefs(repo);
+ const exactRef = [...branches, ...tags].map((item) => item.name).find(matchPathRef);
+ const ref = exactRef || refAndPath.split('/')[0] || 'HEAD';
+ exactRef && this.matchedRefsMap.get(repo)?.push(ref);
+ return { ref, path: normalizePath(refAndPath.slice(ref.length + 1)) };
+ }
+
+ async prepareAllRefs(repo: string) {
+ return Promise.all([this.provideBranches(repo), this.provideTags(repo)]);
+ }
+
+ @trySourcegraphApiFirst
+ async provideBranches(repo: string, options?: CommonQueryOptions): Promise {
+ if (!this.branchesPromiseMap.has(repo)) {
+ this.branchesPromiseMap.set(repo, this.getBranches(repo, 'heads'));
+ }
+ return this.branchesPromiseMap.get(repo)!.then((branches) => {
+ const matchOptions = { keys: ['name'] };
+ const matchedBranches = options?.query ? matchSorter(branches, options.query, matchOptions) : branches;
+ if (options?.pageSize) {
+ const page = options.page || 1;
+ const pageSize = options.pageSize;
+ return matchedBranches.slice(pageSize * (page - 1), pageSize * page);
+ }
+ return matchedBranches;
+ });
+ }
+
+ @trySourcegraphApiFirst
+ async provideBranch(repo: string, branchName: string): Promise {
+ const branches = await this.provideBranches(repo);
+ return branches.find((item) => item.name === branchName) || null;
+ }
+
+ @trySourcegraphApiFirst
+ async provideTags(repoFullName: string, options?: CommonQueryOptions): Promise {
+ if (!this.tagsPromiseMap.has(repoFullName)) {
+ this.tagsPromiseMap.set(repoFullName, this.getTags(repoFullName, 'tags'));
+ }
+ return this.tagsPromiseMap.get(repoFullName)!.then((tags) => {
+ const matchOptions = { keys: ['name'] };
+ const matchedTags = options?.query ? matchSorter(tags, options.query, matchOptions) : tags;
+ if (options?.pageSize) {
+ const page = options.page || 1;
+ const pageSize = options.pageSize;
+ return matchedTags.slice(pageSize * (page - 1), pageSize * page);
+ }
+ return matchedTags;
+ });
+ }
+
+ @trySourcegraphApiFirst
+ async provideTag(repoFullName: string, tagName: string): Promise {
+ const tags = await this.provideTags(repoFullName);
+ return tags.find((item) => item.name === tagName) || null;
+ }
+
+ async provideTextSearchResults(
+ repoFullName: string,
+ ref: string,
+ query: TextSearchQuery,
+ options: TextSearchOptions,
+ ): Promise {
+ return sourcegraphDataSource.provideTextSearchResults(repoFullName, ref, query, options);
+ }
+
+ @trySourcegraphApiFirst
+ async provideCommits(repo: string, options?: CommitsQueryOptions): Promise<(Commit & { files?: ChangedFile[] })[]> {
+ const fetcher = GitLabFetcher.getInstance();
+ const queryParams = {
+ page: options?.page,
+ per_page: options?.pageSize,
+ sha: options?.from,
+ path: isString(options?.path) ? trimStart(options.path, '/') : undefined,
+ author: options?.author,
+ };
+ const requestParams = { repo, ...queryParams };
+ const { data } = await fetcher.request(
+ 'GET /projects/{repo}/repository/commits?per_page={per_page}&page={page}&path={path}&ref_name={sha}',
+ requestParams,
+ );
+ return Promise.all(
+ data.map(async (item) => ({
+ sha: item.id,
+ author: item.author_name,
+ email: item.author_email,
+ message: item.message,
+ committer: item.committer_name,
+ createTime: item.created_at ? new Date(item.created_at) : undefined,
+ parents: item.parent_ids.map((parent) => parent) || [],
+ avatarUrl: item.author?.avatar_url || (await this.provideUserAvatarLink(item.author_name)),
+ })),
+ );
+ }
+
+ @trySourcegraphApiFirst
+ async provideCommit(repo: string, ref: string): Promise {
+ const fetcher = GitLabFetcher.getInstance();
+ const requestParams = { repo, ref };
+ const { data } = await fetcher.request('GET /projects/{repo}/repository/commits/{ref}', requestParams);
+ return {
+ sha: data.id,
+ author: data.author_name,
+ email: data.author_email,
+ message: data.message,
+ committer: data.committer_name,
+ createTime: data.created_at ? new Date(data.created_at) : undefined,
+ parents: data.parent_ids || [],
+ files: data.files?.map((item) => ({
+ path: normalizePath(item.filename || item.previous_filename!),
+ previousPath: item.previous_filename ? normalizePath(item.previous_filename) : undefined,
+ status: item.status as FileChangeStatus,
+ })),
+ avatarUrl: data?.avatar_url,
+ };
+ }
+
+ @trySourcegraphApiFirst
+ async provideCommitChangedFiles(repo: string, ref: string, _options?: CommonQueryOptions): Promise {
+ const fetcher = GitLabFetcher.getInstance();
+ const requestParams = { repo, ref };
+ const { data } = await fetcher.request('GET /projects/{repo}/repository/commits/{ref}/diff', requestParams);
+ return (
+ data?.map((item) => ({
+ path: normalizePath(item.new_path || item.old_path!),
+ previousPath: item.old_path ? normalizePath(item.old_path) : undefined,
+ status: item.new_file
+ ? FileChangeStatus.Added
+ : item.deleted_file
+ ? FileChangeStatus.Removed
+ : item.renamed_file
+ ? FileChangeStatus.Renamed
+ : FileChangeStatus.Modified,
+ })) || []
+ );
+ }
+
+ async provideCodeReviews(
+ repo: string,
+ options?: CodeReviewsQueryOptions,
+ ): Promise<(CodeReview & { files?: ChangedFile[] })[]> {
+ const fetcher = GitLabFetcher.getInstance();
+ const state = options?.state ? (options.state === CodeReviewState.Open ? 'open' : 'closed') : 'all';
+ // per_page=100&page={page}
+ const queryParams = { state, page: options?.page, per_page: options?.pageSize, creator: options?.creator };
+ const requestParams = { repo, ...queryParams };
+ const { data } = await fetcher.request(
+ 'GET /projects/{repo}/merge_requests?per_page={per_page}&page={page}',
+ requestParams as any,
+ );
+
+ return data.map((item) => ({
+ id: `${item.iid}`,
+ title: item.title,
+ state: getMergeRequestState(item),
+ creator: item.author?.name || item.author?.username,
+ createTime: new Date(item.created_at),
+ mergeTime: item.merged_at ? new Date(item.merged_at) : null,
+ closeTime: item.closed_at ? new Date(item.closed_at) : null,
+ source: item.source_branch,
+ target: item.target_branch,
+ avatarUrl: item.author?.avatar_url,
+ }));
+ }
+
+ async provideCodeReview(repo: string, id: string) {
+ const fetcher = GitLabFetcher.getInstance();
+ const requestParams = { repo, id };
+ const { data } = await fetcher.request('GET /projects/{repo}/merge_requests/{id}', requestParams);
+
+ return {
+ id: `${data.iid}`,
+ title: data.title,
+ state: getMergeRequestState(data),
+ creator: data.author?.name || data.author?.username,
+ createTime: new Date(data.created_at),
+ mergeTime: data.merged_at ? new Date(data.merged_at) : null,
+ closeTime: data.closed_at ? new Date(data.closed_at) : null,
+ source: data.source_branch,
+ target: data.target_branch,
+ sourceSha: data.diff_refs.head_sha,
+ targetSha: data.diff_refs.base_sha,
+ avatarUrl: data.author?.avatar_url,
+ };
+ }
+
+ async provideCodeReviewChangedFiles(repo: string, id: string, options?: CommonQueryOptions): Promise {
+ const fetcher = GitLabFetcher.getInstance();
+ const pageParams = { per_page: options?.pageSize, page: options?.page };
+ const filesRequestParams = { repo, id, ...pageParams };
+ const { data } = await fetcher.request(
+ 'GET /projects/{repo}/merge_requests/{id}/changes?per_page={per_page}&page={page}',
+ filesRequestParams,
+ );
+
+ return data.changes.map((item) => ({
+ path: normalizePath(item.new_path),
+ previousPath: item.old_path ? normalizePath(item.old_path) : undefined,
+ status: item.new_file
+ ? FileChangeStatus.Added
+ : item.deleted_file
+ ? FileChangeStatus.Removed
+ : item.renamed_file
+ ? FileChangeStatus.Renamed
+ : FileChangeStatus.Modified,
+ }));
+ }
+
+ @trySourcegraphApiFirst
+ async provideFileBlameRanges(repo: string, ref: string, path: string): Promise {
+ const fetcher = GitLabFetcher.getInstance();
+ const requestParams = { repo, ref, path: trimStart(path, '/') };
+ const { data } = await fetcher.request(
+ 'GET /projects/{repo}/repository/files/{path}/blame?ref={ref}',
+ requestParams,
+ );
+ let startLine = 1;
+ const timestamps = data.map(({ commit }) => +new Date(commit.authored_date) || 0);
+ const computeAge = resolveComputeAge(timestamps);
+ return (data || []).map(({ commit, lines }) => {
+ const startingLine = startLine;
+ const endingLine = startingLine + lines.length;
+ startLine = endingLine + 1;
+ return {
+ age: computeAge(+new Date(commit?.authored_date) || 0),
+ startingLine,
+ endingLine,
+ commit: {
+ sha: commit?.id as string,
+ author: commit?.author_name as string,
+ email: commit?.author_email as string,
+ message: commit?.message as string,
+ createTime: new Date(commit?.authored_date),
+ avatarUrl: this.provideUserAvatarLink(encodeURIComponent(commit?.author?.name)),
+ },
+ };
+ });
+ }
+
+ async getAvatar(email): Promise {
+ const fetcher = GitLabFetcher.getInstance();
+ const { data } = await fetcher.request('GET /avatar?email={email}', { email });
+ return data.avatar_url;
+ }
+
+ provideSymbolDefinitions(
+ repoFullName: string,
+ ref: string,
+ path: string,
+ line: number,
+ character: number,
+ symbol: string,
+ ): Promise {
+ return sourcegraphDataSource.provideSymbolDefinitions(repoFullName, ref, path, line, character, symbol);
+ }
+
+ async provideSymbolReferences(
+ repoFullName: string,
+ ref: string,
+ path: string,
+ line: number,
+ character: number,
+ symbol: string,
+ ): Promise {
+ return sourcegraphDataSource.provideSymbolReferences(repoFullName, ref, path, line, character, symbol);
+ }
+
+ async provideSymbolHover(
+ repoFullName: string,
+ ref: string,
+ path: string,
+ line: number,
+ character: number,
+ _symbol: string,
+ ): Promise {
+ return sourcegraphDataSource.provideSymbolHover(repoFullName, ref, path, line, character, _symbol);
+ }
+
+ provideUserAvatarLink(user: string): string {
+ return `https://www.gravatar.com/avatar/${user}?d=identicon`;
+ }
+}
diff --git a/extensions/github1s/src/adapters/gitlab1s/fetcher.ts b/extensions/github1s/src/adapters/gitlab1s/fetcher.ts
new file mode 100644
index 000000000..c6c3e0f10
--- /dev/null
+++ b/extensions/github1s/src/adapters/gitlab1s/fetcher.ts
@@ -0,0 +1,137 @@
+/**
+ * @file gitlab api fetcher
+ * @author netcon
+ */
+
+import * as vscode from 'vscode';
+import { getExtensionContext } from '@/helpers/context';
+import { GitLab1sAuthenticationView } from './authentication';
+import { GitLabTokenManager } from './token';
+import { isNil } from '@/helpers/util';
+import { reuseable } from '@/helpers/func';
+import { getCurrentRepo } from './parse-path';
+import { SourcegraphDataSource } from '../sourcegraph/data-source';
+
+export const errorMessages = {
+ badCredentials: {
+ anonymous: 'Bad credentials, please authenticate to gitlab and retry',
+ authenticated: 'This token is invalid, please try another one',
+ },
+ repoNotFound: {
+ anonymous: 'Repository not found, if it is private, you can provide an AccessToken to access it',
+ authenticated: 'Repository not found, if it is private, you can try change an AccessToken to access it',
+ },
+ noPermission: {
+ anonymous: 'You have no permission for this operation, please authenticate to gitlab and retry',
+ authenticated: 'You have no permission for this operation, please try another account',
+ },
+};
+
+const detectErrorMessage = (response: any, authenticated: boolean) => {
+ if (response?.status === 401 && response?.data?.message?.includes?.('Unauthorized')) {
+ return errorMessages.badCredentials[authenticated ? 'authenticated' : 'anonymous'];
+ }
+ if (response?.status === 404) {
+ return errorMessages.repoNotFound[authenticated ? 'authenticated' : 'anonymous'];
+ }
+ if (response?.status === 403) {
+ return errorMessages.noPermission[authenticated ? 'authenticated' : 'anonymous'];
+ }
+ return response?.data?.message || response?.data?.error || '';
+};
+
+const PREFER_SOURCEGRAPH_API = 'PREFER_SOURCEGRAPH_API';
+
+export class GitLabFetcher {
+ private static instance: GitLabFetcher | null = null;
+ private _emitter = new vscode.EventEmitter();
+ public onDidChangePreferSourcegraphApi = this._emitter.event;
+ private _currentRepoPromise: Promise | null = null;
+
+ public static getInstance(): GitLabFetcher {
+ if (GitLabFetcher.instance) {
+ return GitLabFetcher.instance;
+ }
+ return (GitLabFetcher.instance = new GitLabFetcher());
+ }
+
+ private constructor() {
+ this.initPreferSourcegraphApi();
+ GitLabTokenManager.getInstance().onDidChangeToken(() => this.initPreferSourcegraphApi());
+ }
+
+ private _request = reuseable(
+ (
+ command: string,
+ params: Record,
+ ): Promise<{ status: number; data: any; headers: Headers }> => {
+ // eslint-disable-next-line prefer-const
+ let [method, path] = command.split(/\s+/).filter(Boolean);
+ Object.keys(params).forEach((el) => {
+ path = path.replace(`{${el}}`, `${encodeURIComponent(params[el] || '')}`);
+ });
+ const accessToken = GitLabTokenManager.getInstance().getToken();
+ const fetchOptions: { headers: Record } =
+ accessToken?.length < 60
+ ? { headers: { 'PRIVATE-TOKEN': `${accessToken}` } }
+ : { headers: { Authorization: `Bearer ${accessToken}` } };
+ return fetch(GITLAB_API_PREFIX + path, {
+ ...fetchOptions,
+ method,
+ }).then(async (response: Response & { data: any }) => {
+ response.data = await response.json();
+ return response.ok ? response : Promise.reject({ response });
+ });
+ },
+ );
+
+ public request = (command: string, params: Record) => {
+ return this._request(command, params).catch(async (error: { response: any }) => {
+ const errorStatus = error?.response?.status as number | undefined;
+ const repoNotFound = errorStatus === 404 && !(await this.resolveCurrentRepo());
+ if ((errorStatus && [401, 403].includes(errorStatus)) || repoNotFound) {
+ // maybe we have to acquire github access token to continue
+ const accessToken = GitLabTokenManager.getInstance().getToken();
+ const message = detectErrorMessage(error?.response, !!accessToken);
+ await GitLab1sAuthenticationView.getInstance().open(message, true);
+ return this._request(command, params);
+ }
+ throw error;
+ });
+ };
+
+ private resolveCurrentRepo(forceUpdate: boolean = false) {
+ if (this._currentRepoPromise && !forceUpdate) {
+ return this._currentRepoPromise;
+ }
+ return (this._currentRepoPromise = Promise.resolve(getCurrentRepo())
+ .then(async (repo) => this._request('GET /projects/{repo}', { repo }).then((res) => res.data))
+ .catch(() => null));
+ }
+
+ private async initPreferSourcegraphApi() {
+ if (await this.getPreferSourcegraphApi()) {
+ const sgDataSource = SourcegraphDataSource.getInstance('github');
+ if (!(await sgDataSource.provideRepository(await getCurrentRepo()))) {
+ this.resolveCurrentRepo(true).then((repo) => {
+ repo?.visibility === 'private' && this.setPreferSourcegraphApi(false);
+ });
+ }
+ }
+ }
+
+ public async getPreferSourcegraphApi(repo?: string): Promise {
+ const targetRepo = repo || (await getCurrentRepo());
+ const globalState = getExtensionContext().globalState;
+ const cachedData: Record | undefined = globalState.get(PREFER_SOURCEGRAPH_API);
+ return !isNil(cachedData?.[targetRepo]) ? !!cachedData?.[targetRepo] : true;
+ }
+
+ public async setPreferSourcegraphApi(value: boolean, repo?: string) {
+ const targetRepo = repo || (await getCurrentRepo());
+ const globalState = getExtensionContext().globalState;
+ const cachedData: Record | undefined = globalState.get(PREFER_SOURCEGRAPH_API);
+ await globalState.update(PREFER_SOURCEGRAPH_API, { ...cachedData, [targetRepo]: value });
+ this._emitter.fire(value);
+ }
+}
diff --git a/extensions/github1s/src/adapters/gitlab1s/index.ts b/extensions/github1s/src/adapters/gitlab1s/index.ts
new file mode 100644
index 000000000..5d6db81c0
--- /dev/null
+++ b/extensions/github1s/src/adapters/gitlab1s/index.ts
@@ -0,0 +1,58 @@
+/**
+ * @file GitLab1s adapter
+ * @author netcon
+ */
+
+import * as vscode from 'vscode';
+import { GitLab1sRouterParser } from './router-parser';
+import { GitLab1sDataSource } from './data-source';
+import { SourcegraphDataSource } from '../sourcegraph/data-source';
+import { Adapter, CodeReviewType, PlatformName } from '../types';
+import { GitLab1sSettingsViewProvider } from './settings';
+import { GitLab1sAuthenticationView } from './authentication';
+import { setVSCodeContext } from '@/helpers/vscode';
+import { getCurrentRepo } from './parse-path';
+
+export class GitLab1sAdapter implements Adapter {
+ public scheme: string = 'gitlab1s';
+ public platformName = PlatformName.GitLab;
+ public codeReviewType = CodeReviewType.MergeRequest;
+
+ resolveDataSource() {
+ return Promise.resolve(GitLab1sDataSource.getInstance());
+ }
+
+ resolveRouterParser() {
+ return Promise.resolve(GitLab1sRouterParser.getInstance());
+ }
+
+ activateAsDefault() {
+ // register settings view and show it in activity bar
+ setVSCodeContext('github1s:views:settings:visible', true);
+ setVSCodeContext('github1s:views:codeReviewList:visible', true);
+ setVSCodeContext('github1s:views:commitList:visible', true);
+ setVSCodeContext('github1s:views:fileHistory:visible', true);
+ setVSCodeContext('github1s:features:gutterBlame:enabled', true);
+
+ vscode.window.registerWebviewViewProvider(
+ GitLab1sSettingsViewProvider.viewType,
+ new GitLab1sSettingsViewProvider(),
+ );
+ vscode.commands.registerCommand('github1s.commands.openGitLab1sAuthPage', () => {
+ return GitLab1sAuthenticationView.getInstance().open();
+ });
+ vscode.commands.registerCommand('github1s.commands.syncSourcegraphRepository', async () => {
+ const dataSource = SourcegraphDataSource.getInstance('gitlab');
+ const randomRef = (Math.random() + 1).toString(36).slice(2);
+ return dataSource.provideCommit(await getCurrentRepo(), randomRef);
+ });
+ }
+
+ deactivateAsDefault() {
+ setVSCodeContext('github1s:views:settings:visible', false);
+ setVSCodeContext('github1s:views:codeReviewList:visible', false);
+ setVSCodeContext('github1s:views:commitList:visible', false);
+ setVSCodeContext('github1s:views:fileHistory:visible', false);
+ setVSCodeContext('github1s:features:gutterBlame:enabled', false);
+ }
+}
diff --git a/extensions/github1s/src/adapters/gitlab1s/parse-path.ts b/extensions/github1s/src/adapters/gitlab1s/parse-path.ts
new file mode 100644
index 000000000..9e108ba6d
--- /dev/null
+++ b/extensions/github1s/src/adapters/gitlab1s/parse-path.ts
@@ -0,0 +1,169 @@
+/**
+ * @file parse gitlab path
+ * @author netcon
+ */
+
+import * as vscode from 'vscode';
+import { parsePath } from 'history';
+import { PageType, RouterState } from '../types';
+import { GitLab1sDataSource } from './data-source';
+import { memorize } from '@/helpers/func';
+import { getBrowserUrl } from '@/helpers/context';
+
+export const DEFAULT_REPO = 'gitlab-org/gitlab-docs';
+
+export const getCurrentRepo = memorize(() => {
+ return getBrowserUrl().then((browserUrl: string) => {
+ const pathParts = vscode.Uri.parse(browserUrl).path.split('/').filter(Boolean);
+ const dashIndex = pathParts.indexOf('-');
+ return (dashIndex < 0 ? pathParts : pathParts.slice(0, dashIndex)).join('/') || DEFAULT_REPO;
+ });
+});
+
+const getDefaultBranch = async (repo: string): Promise => {
+ const dataSource = GitLab1sDataSource.getInstance();
+ return dataSource.getDefaultBranch(repo);
+};
+
+const parseTreeUrl = async (path: string): Promise => {
+ const pathParts = parsePath(path).pathname!.split('/').filter(Boolean);
+ const dashIndex = pathParts.indexOf('-');
+ const repo = (dashIndex < 0 ? pathParts : pathParts.slice(0, dashIndex)).join('/');
+ const restParts = dashIndex < 0 ? [] : pathParts.slice(dashIndex + 2);
+ const dataSource = GitLab1sDataSource.getInstance();
+ const { ref, path: filePath } = await dataSource.extractRefPath(repo, restParts.join('/'));
+
+ return { pageType: PageType.Tree, repo, ref, filePath };
+};
+
+const parseBlobUrl = async (path: string): Promise => {
+ const routerState = (await parseTreeUrl(path)) as any;
+ const { hash: routerHash } = parsePath(path);
+
+ if (!routerHash) {
+ return { ...routerState, pageType: PageType.Blob };
+ }
+
+ // get selected line number range from path which looks like:
+ // `/gitlab-org/gitlab/-/blob/main/package.json#L10-L20`
+ const matches = routerHash.match(/^#L(\d+)(?:-L(\d+))?/);
+ const [_, startLineNumber = '0', endLineNumber] = matches ? matches : [];
+
+ return {
+ ...routerState,
+ pageType: PageType.Blob,
+ startLine: parseInt(startLineNumber, 10),
+ endLine: parseInt(endLineNumber || startLineNumber, 10),
+ };
+};
+
+const parseCommitsUrl = async (path: string): Promise => {
+ const pathParts = parsePath(path).pathname!.split('/').filter(Boolean);
+ const dashIndex = pathParts.indexOf('-');
+ const repo = (dashIndex < 0 ? pathParts : pathParts.slice(0, dashIndex)).join('/');
+ const ref = dashIndex < 0 ? await getDefaultBranch(repo) : pathParts.slice(dashIndex + 2).join('/');
+
+ return { repo, pageType: PageType.CommitList, ref };
+};
+
+const parseCommitUrl = async (path: string): Promise => {
+ const pathParts = parsePath(path).pathname!.split('/').filter(Boolean);
+ const dashIndex = pathParts.indexOf('-');
+ const repo = (dashIndex < 0 ? pathParts : pathParts.slice(0, dashIndex)).join('/');
+ const commitSha = dashIndex < 0 ? await getDefaultBranch(repo) : pathParts.slice(dashIndex + 2).join('/');
+
+ return { repo, pageType: PageType.Commit, ref: commitSha, commitSha };
+};
+
+const parseMergeRequestsUrl = async (path: string): Promise => {
+ const [owner, repo] = parsePath(path).pathname!.split('/').filter(Boolean);
+
+ return {
+ repo: `${owner}/${repo}`,
+ ref: await getDefaultBranch(`${owner}/${repo}`),
+ pageType: PageType.CodeReviewList,
+ };
+};
+
+const parseMergeRequestUrl = async (path: string): Promise => {
+ const pathParts = parsePath(path).pathname!.split('/').filter(Boolean);
+ const [owner, repo, , _pageType, codeReviewId] = pathParts;
+ const repoFullName = `${owner}/${repo}`;
+ const codeReview = await GitLab1sDataSource.getInstance().provideCodeReview(repoFullName, codeReviewId);
+
+ return {
+ repo: `${owner}/${repo}`,
+ pageType: PageType.CodeReview,
+ ref: codeReview.targetSha,
+ codeReviewId,
+ };
+};
+
+// const parseSearchUrl = async (path: string): Promise => {
+// const { pathname, search } = parsePath(path);
+// const pathParts = pathname!.split('/').filter(Boolean);
+// const [owner, repo, _pageType] = pathParts;
+// const queryOptions = queryString.parse(search || '');
+// const query = typeof queryOptions.q === 'string' ? queryOptions.q : '';
+// const isRegex = queryOptions.regex === 'yes';
+// const isCaseSensitive = queryOptions.case === 'yes';
+// const matchWholeWord = queryOptions.whole === 'yes';
+// const filesToInclude = typeof queryOptions['files-to-include'] === 'string' ? queryOptions['files-to-include'] : '';
+// const filesToExclude = typeof queryOptions['files-to-exclude'] === 'string' ? queryOptions['files-to-exclude'] : '';
+
+// return {
+// repo: `${owner}/${repo}`,
+// pageType: PageType.Search,
+// ref: 'HEAD',
+// query,
+// isRegex,
+// isCaseSensitive,
+// matchWholeWord,
+// filesToInclude,
+// filesToExclude,
+// };
+// };
+
+const PAGE_TYPE_MAP = {
+ tree: PageType.Tree,
+ blob: PageType.Blob,
+ merge_requests: PageType.CodeReview,
+ commit: PageType.Commit,
+ commits: PageType.CommitList,
+};
+
+export const parseGitLabPath = async (path: string): Promise => {
+ const pathParts = parsePath(path).pathname?.split('/').filter(Boolean) || [];
+ const dashIndex = pathParts.indexOf('-');
+ const typeSegment = dashIndex < 0 ? '' : pathParts[dashIndex + 1];
+ const pageType = typeSegment ? PAGE_TYPE_MAP[typeSegment] || PageType.Unknown : PageType.Tree;
+
+ if (pathParts.length) {
+ switch (pageType) {
+ case PageType.Tree:
+ case PageType.Unknown:
+ return parseTreeUrl(path);
+ case PageType.Blob:
+ return parseBlobUrl(path);
+ case PageType.CodeReview:
+ if (pathParts.length > dashIndex + 2) {
+ return parseMergeRequestUrl(path);
+ }
+ return parseMergeRequestsUrl(path);
+ case PageType.CodeReviewList:
+ return parseMergeRequestsUrl(path);
+ case PageType.Commit:
+ return parseCommitUrl(path);
+ case PageType.CommitList:
+ return parseCommitsUrl(path);
+ }
+ }
+
+ // fallback to default
+ return {
+ repo: DEFAULT_REPO,
+ ref: await getDefaultBranch(DEFAULT_REPO),
+ pageType: PageType.Tree,
+ filePath: '/',
+ };
+};
diff --git a/extensions/github1s/src/adapters/gitlab1s/router-parser.ts b/extensions/github1s/src/adapters/gitlab1s/router-parser.ts
new file mode 100644
index 000000000..40aca6882
--- /dev/null
+++ b/extensions/github1s/src/adapters/gitlab1s/router-parser.ts
@@ -0,0 +1,52 @@
+/**
+ * @file router parser
+ * @author netcon
+ */
+
+import { joinPath } from '@/helpers/util';
+import * as adapterTypes from '../types';
+import { parseGitLabPath } from './parse-path';
+
+export class GitLab1sRouterParser extends adapterTypes.RouterParser {
+ private static instance: GitLab1sRouterParser | null = null;
+
+ public static getInstance(): GitLab1sRouterParser {
+ if (GitLab1sRouterParser.instance) {
+ return GitLab1sRouterParser.instance;
+ }
+ return (GitLab1sRouterParser.instance = new GitLab1sRouterParser());
+ }
+
+ parsePath(path: string): Promise {
+ return parseGitLabPath(path);
+ }
+
+ buildTreePath(repo: string, ref?: string, filePath?: string): string {
+ return ref ? `/${repo}/-/tree/${ref}${filePath && filePath !== '/' ? filePath : ''}` : `/${repo}`;
+ }
+
+ buildBlobPath(repo: string, ref: string, filePath: string, startLine?: number, endLine?: number): string {
+ const hash = startLine ? (endLine ? `#L${startLine}-L${endLine}` : `#L${startLine}`) : '';
+ return `/${repo}/-/blob/${ref}${filePath}${hash}`;
+ }
+
+ buildCommitListPath(repo: string): string {
+ return `/${repo}/-/commits`;
+ }
+
+ buildCommitPath(repo: string, commitSha: string): string {
+ return `/${repo}/-/commit/${commitSha}`;
+ }
+
+ buildCodeReviewListPath(repo: string): string {
+ return `/${repo}/-/merge_requests`;
+ }
+
+ buildCodeReviewPath(repo: string, codeReviewId: string): string {
+ return `/${repo}/-/merge_requests/${codeReviewId}`;
+ }
+
+ buildExternalLink(path: string): string {
+ return joinPath(GITLAB_ORIGIN, path);
+ }
+}
diff --git a/extensions/github1s/src/adapters/gitlab1s/settings.ts b/extensions/github1s/src/adapters/gitlab1s/settings.ts
new file mode 100644
index 000000000..c77ea3e23
--- /dev/null
+++ b/extensions/github1s/src/adapters/gitlab1s/settings.ts
@@ -0,0 +1,24 @@
+/**
+ * @file GitLab1s Settings Webview Provider
+ * @author netcon
+ */
+
+import { GitLabTokenManager } from './token';
+import { GitLabFetcher } from './fetcher';
+import { GitHub1sSettingsViewProvider } from '../github1s/settings';
+
+export class GitLab1sSettingsViewProvider extends GitHub1sSettingsViewProvider {
+ protected tokenManager = GitLabTokenManager.getInstance();
+ protected apiFetcher = GitLabFetcher.getInstance();
+
+ protected OAuthCommand = 'github1s.commands.vscode.connectToGitLab';
+ protected detailPageCommand = 'github1s.commands.openGitLab1sAuthPage';
+ protected pageConfig = {
+ pageDescriptionLines: [
+ 'You can provide a Personal Access Token or an OAuth token to access private repositories or to increase rate limits.',
+ "Your token will only be stored locally in your browser. Don't forget to clean it while you are using a public device.",
+ ],
+ OAuthButtonText: 'Connect to GitLab',
+ createTokenLink: `${GITLAB_ORIGIN}/-/profile/personal_access_tokens?scopes=read_api&name=GitLab1s`,
+ };
+}
diff --git a/extensions/github1s/src/adapters/gitlab1s/token.ts b/extensions/github1s/src/adapters/gitlab1s/token.ts
new file mode 100644
index 000000000..19a9ea1e0
--- /dev/null
+++ b/extensions/github1s/src/adapters/gitlab1s/token.ts
@@ -0,0 +1,40 @@
+/**
+ * @file gitlab api auth token manager
+ */
+
+import { GitHubTokenManager, ValidateResult } from '../github1s/token';
+
+export class GitLabTokenManager extends GitHubTokenManager {
+ protected static instance: GitLabTokenManager | null = null;
+ public tokenStateKey = 'gitlab-oauth-token';
+
+ public static getInstance(): GitLabTokenManager {
+ if (GitLabTokenManager.instance) {
+ return GitLabTokenManager.instance;
+ }
+ return (GitLabTokenManager.instance = new GitLabTokenManager());
+ }
+
+ public async validateToken(token?: string): Promise {
+ const accessToken = token === undefined ? this.getToken() : token;
+ if (!accessToken) {
+ return Promise.resolve(null);
+ }
+ const fetchOptions: { headers: Record } =
+ accessToken?.length < 60
+ ? { headers: { 'PRIVATE-TOKEN': `${accessToken}` } }
+ : { headers: { Authorization: `Bearer ${accessToken}` } };
+ return fetch(`${GITLAB_API_PREFIX}/user`, fetchOptions)
+ .then((response) => {
+ if (response.status === 401) {
+ return null;
+ }
+ return response.json().then((data) => ({
+ username: data.username,
+ avatar_url: data.avatar_url,
+ profile_url: data.web_url,
+ }));
+ })
+ .catch(() => null);
+ }
+}
diff --git a/extensions/github1s/src/adapters/index.ts b/extensions/github1s/src/adapters/index.ts
new file mode 100644
index 000000000..87fbe9425
--- /dev/null
+++ b/extensions/github1s/src/adapters/index.ts
@@ -0,0 +1,38 @@
+/**
+ * @file register the adapters
+ * @author netcon
+ */
+
+import adapterManager from './manager';
+import { GitHub1sAdapter } from './github1s';
+import { GitLab1sAdapter } from './gitlab1s';
+import { BitbucketAdapter } from './bitbucket1s';
+import { Npmjs1sAdapter } from './npmjs1s';
+import { OSSInsightAdapter } from './ossinsight';
+import { Adapter, DataSource, PlatformName, RouterParser } from './types';
+
+const emptyAdapter = {
+ scheme: 'empty',
+ platformName: PlatformName.GitHub,
+ resolveDataSource: () => new DataSource(),
+ resolveRouterParser: () => new RouterParser(),
+};
+
+export const registerAdapters = async (): Promise => {
+ await Promise.all([
+ adapterManager.registerAdapter(emptyAdapter),
+ adapterManager.registerAdapter(new GitHub1sAdapter()),
+ adapterManager.registerAdapter(new GitLab1sAdapter()),
+ adapterManager.registerAdapter(new BitbucketAdapter()),
+ adapterManager.registerAdapter(new Npmjs1sAdapter()),
+ adapterManager.registerAdapter(new OSSInsightAdapter()),
+ ]);
+};
+
+export const getAdapter = (scheme?: string): Adapter => {
+ return adapterManager.getAdapter(scheme);
+};
+
+export const getAllAdapters = (): Adapter[] => {
+ return adapterManager.getAllAdapters();
+};
diff --git a/extensions/github1s/src/adapters/manager.ts b/extensions/github1s/src/adapters/manager.ts
new file mode 100644
index 000000000..82a91a060
--- /dev/null
+++ b/extensions/github1s/src/adapters/manager.ts
@@ -0,0 +1,56 @@
+/**
+ * @file Manages the adapters.
+ * @author netcon
+ */
+
+import * as vscode from 'vscode';
+import { setVSCodeContext } from '@/helpers/vscode';
+import { Adapter, Promisable } from './types';
+
+export class AdapterManager {
+ private static instance: AdapterManager | null = null;
+ private adaptersMap: Map = new Map();
+ private defaultAdapter: Adapter | null = null;
+
+ private constructor() {}
+
+ public static getInstance(): AdapterManager {
+ if (AdapterManager.instance) {
+ return AdapterManager.instance;
+ }
+ return (AdapterManager.instance = new AdapterManager());
+ }
+
+ public registerAdapter(adapter: Adapter): Promisable {
+ if (this.adaptersMap.has(adapter.scheme)) {
+ throw new Error(`Adapter scheme '${adapter.scheme}' is already registered.`);
+ }
+ if (this.defaultAdapter && this.defaultAdapter.deactivateAsDefault) {
+ this.defaultAdapter.deactivateAsDefault();
+ }
+ this.adaptersMap.set(adapter.scheme, adapter);
+ if (this.getCurrentScheme() === adapter.scheme) {
+ setVSCodeContext('github1s:adapters:default:platformName', adapter.platformName);
+ setVSCodeContext('github1s:adapters:default:codeReviewType', adapter.codeReviewType);
+ return adapter.activateAsDefault && adapter.activateAsDefault();
+ }
+ }
+
+ public getAllAdapters(): Adapter[] {
+ return Array.from(this.adaptersMap.values());
+ }
+
+ public getAdapter(scheme?: string): Adapter {
+ scheme = scheme || this.getCurrentScheme();
+ if (!this.adaptersMap.has(scheme)) {
+ throw new Error(`Adapter with scheme '${scheme}' can not found.`);
+ }
+ return this.adaptersMap.get(scheme)!;
+ }
+
+ private getCurrentScheme(): string {
+ return vscode.workspace.workspaceFolders?.[0]?.uri?.scheme || 'empty';
+ }
+}
+
+export default AdapterManager.getInstance();
diff --git a/extensions/github1s/src/adapters/npmjs1s/data-source.ts b/extensions/github1s/src/adapters/npmjs1s/data-source.ts
new file mode 100644
index 000000000..e53cf9ea5
--- /dev/null
+++ b/extensions/github1s/src/adapters/npmjs1s/data-source.ts
@@ -0,0 +1,141 @@
+/**
+ * @file npm data-source-provider
+ * @author netcon
+ */
+
+import { CommonQueryOptions, DataSource, Directory, DirectoryEntry, File, FileType, Tag } from '../types';
+import { matchSorter } from 'match-sorter';
+import * as dayjs from 'dayjs';
+import { normalizePath } from '@/helpers/util';
+
+type PackageFile = {
+ path: string;
+ size: number;
+ type: 'file';
+};
+
+type PackageDirectory = {
+ files: PackageEntry[];
+ type: 'directory';
+ path: string;
+};
+
+type PackageEntry = PackageFile | PackageDirectory;
+
+type PackageVersion = { name: string; tag?: string; time?: Date };
+
+const retrieveFiles = (files: PackageEntry[], recursive: boolean) => {
+ const entries: DirectoryEntry[] = [];
+ for (const item of files) {
+ const fileType = item.type === 'directory' ? FileType.Directory : FileType.File;
+ entries.push({ type: fileType, path: normalizePath(item.path) });
+ if (recursive && item.type === 'directory' && item.files?.length) {
+ entries.push(...retrieveFiles(item.files, recursive));
+ }
+ }
+ return entries;
+};
+
+export class Npmjs1sDataSource extends DataSource {
+ private static instance: Npmjs1sDataSource | null = null;
+ private _filesPromiseMap: Map> = new Map();
+ private _versionsPromiseMap: Map> = new Map();
+
+ public static getInstance(): Npmjs1sDataSource {
+ if (Npmjs1sDataSource.instance) {
+ return Npmjs1sDataSource.instance;
+ }
+ return (Npmjs1sDataSource.instance = new Npmjs1sDataSource());
+ }
+
+ getPackageFiles(packageName: string, version: string): Promise {
+ const mapKey = `${packageName} ${version}`;
+ if (!this._filesPromiseMap.has(mapKey)) {
+ const filesPromise = (async () => {
+ const packageVersions = version === 'latest' ? await this.getPackageVersions(packageName) : [];
+ const exactVersion = packageVersions.find((packageVersion) => packageVersion.tag === 'latest')?.name || version;
+ const requestUrl = `https://unpkg.com/${packageName}@${exactVersion}/?meta`;
+ const data = await fetch(requestUrl).then((response) => response.json());
+ return data?.files || [];
+ })();
+ this._filesPromiseMap.set(mapKey, filesPromise);
+ }
+ return this._filesPromiseMap.get(mapKey)!;
+ }
+
+ async provideDirectory(packageName: string, version: string, path: string, recursive = false): Promise {
+ const pathParts = path.split(/\/+/).filter(Boolean);
+ const parentFiles = pathParts.reduce(
+ (prevFiles, _, index) => {
+ const currentPath = `/${pathParts.slice(0, index + 1).join('/')}`;
+ const fileNode = prevFiles?.find((item) => item.path === currentPath);
+ return fileNode?.type === 'directory' ? fileNode.files : null;
+ },
+ await this.getPackageFiles(packageName, version),
+ );
+ const entries = parentFiles ? retrieveFiles(parentFiles, recursive) : [];
+ return { entries, truncated: false };
+ }
+
+ async provideFile(packageName: string, version: string, path: string): Promise {
+ const response = await fetch(`https://unpkg.com/${packageName}@${version}${path}`);
+ return { content: new Uint8Array(await response.arrayBuffer()) };
+ }
+
+ getPackageVersions(packageName: string): Promise {
+ if (!this._versionsPromiseMap.has(packageName)) {
+ const versionsPromise = new Promise(async (resolve, reject) => {
+ const requestUrl = `https://registry.npmjs.org/${packageName}`;
+ const data = await fetch(requestUrl).then((response) => response.json(), reject);
+ const tagEntries = Object.entries(data?.['dist-tags'] || {}) as [string, string][];
+ const versionTag = tagEntries.reduce((prevVersionTag, [tag, version]) => {
+ if (!prevVersionTag[version] || tag === 'latest') {
+ prevVersionTag[version] = tag;
+ }
+ return prevVersionTag;
+ }, {}) as Record;
+ const packageVersions = Object.keys(data?.versions || {}).map((version) => {
+ const releaseTime = data?.time?.[version] ? new Date(data.time[version]) : undefined;
+ return { name: version, tag: versionTag[version], time: releaseTime };
+ });
+ const sortByTimeDesc = (versionA, versionB) => {
+ if (+!versionA.tag ^ +!versionB.tag) {
+ return versionA.tag ? -1 : 1;
+ }
+ return versionB.time?.getTime() - versionA.time?.getTime();
+ };
+ return resolve(packageVersions.sort(sortByTimeDesc));
+ });
+ this._versionsPromiseMap.set(packageName, versionsPromise);
+ }
+ return this._versionsPromiseMap.get(packageName)!;
+ }
+
+ transformVersionToTag(version: PackageVersion) {
+ const tagPart = version.tag ? `${version.tag}, ` : '';
+ const timePart = version.time ? `published at ${dayjs(version.time).format('YYYY-MM-DD HH:mm:ss')}` : '';
+ return { name: version.name, description: `${tagPart}${timePart}` };
+ }
+
+ async provideTags(packageName: string, options?: CommonQueryOptions): Promise {
+ const versions = await this.getPackageVersions(packageName);
+ const allTags = versions.map((version) => this.transformVersionToTag(version));
+ const matchedTags = options?.query ? matchSorter(allTags, options.query, { keys: ['name'] }) : allTags;
+ if (options?.pageSize) {
+ const page = options.page || 1;
+ const pageSize = options.pageSize;
+ return matchedTags.slice(pageSize * (page - 1), pageSize * page);
+ }
+ return matchedTags;
+ }
+
+ async provideTag(packageName: string, tagName: string): Promise {
+ const versions = await this.getPackageVersions(packageName);
+ const matchedVersion = versions.find((tag) => tag.name === tagName);
+ return matchedVersion ? this.transformVersionToTag(matchedVersion) : null;
+ }
+
+ provideUserAvatarLink(user: string): string {
+ return `https://www.gravatar.com/avatar/${user}?d=identicon`;
+ }
+}
diff --git a/extensions/github1s/src/adapters/npmjs1s/index.ts b/extensions/github1s/src/adapters/npmjs1s/index.ts
new file mode 100644
index 000000000..c52c1eee9
--- /dev/null
+++ b/extensions/github1s/src/adapters/npmjs1s/index.ts
@@ -0,0 +1,21 @@
+/**
+ * @file Npmjs1s adapter
+ * @author netcon
+ */
+
+import { Npmjs1sDataSource } from './data-source';
+import { Npmjs1sRouterParser } from './router-parser';
+import { Adapter, PlatformName } from '../types';
+
+export class Npmjs1sAdapter implements Adapter {
+ public scheme: string = 'npmjs1s';
+ public platformName = PlatformName.npm;
+
+ resolveDataSource() {
+ return Npmjs1sDataSource.getInstance();
+ }
+
+ resolveRouterParser() {
+ return Npmjs1sRouterParser.getInstance();
+ }
+}
diff --git a/extensions/github1s/src/adapters/npmjs1s/router-parser.ts b/extensions/github1s/src/adapters/npmjs1s/router-parser.ts
new file mode 100644
index 000000000..5b67a229a
--- /dev/null
+++ b/extensions/github1s/src/adapters/npmjs1s/router-parser.ts
@@ -0,0 +1,67 @@
+/**
+ * @file router parser
+ * @author netcon
+ */
+
+import { parsePath } from 'history';
+import { PageType, RouterParser, RouterState } from '../types';
+
+export const parseNpmPath = async (path: string): Promise => {
+ const pathParts = parsePath(path).pathname?.split('/').filter(Boolean) || [];
+
+ if (!pathParts.length) {
+ return { pageType: PageType.Tree, repo: 'lodash', ref: 'latest', filePath: '/' };
+ }
+
+ const trimedParts = pathParts[0] === 'package' ? pathParts.slice(1) : pathParts;
+ const packagePartsLength = trimedParts[0] && trimedParts[0][0] === '@' ? 2 : 1;
+ const packageParts = trimedParts.slice(0, packagePartsLength);
+ const packageName = packageParts.join('/') || 'package';
+ const packageVersion =
+ trimedParts[packagePartsLength] === 'v' ? trimedParts[packagePartsLength + 1] || 'latest' : 'latest';
+
+ return { pageType: PageType.Tree as const, repo: packageName, ref: packageVersion, filePath: '/' };
+};
+
+export class Npmjs1sRouterParser extends RouterParser {
+ private static instance: Npmjs1sRouterParser | null = null;
+
+ public static getInstance(): Npmjs1sRouterParser {
+ if (Npmjs1sRouterParser.instance) {
+ return Npmjs1sRouterParser.instance;
+ }
+ return (Npmjs1sRouterParser.instance = new Npmjs1sRouterParser());
+ }
+
+ parsePath(path: string): Promise {
+ return parseNpmPath(path);
+ }
+
+ buildTreePath(packageName: string, version: string): string {
+ return `/package/${packageName}${version === 'latest' ? '' : `/v/${version}`}`;
+ }
+
+ buildBlobPath(packageName: string, version: string): string {
+ return `/package/${packageName}${version === 'latest' ? '' : `/v/${version}`}`;
+ }
+
+ buildCommitListPath(packageName: string): string {
+ return `/package/${packageName}`;
+ }
+
+ buildCommitPath(packageName: string): string {
+ return `/package/${packageName}`;
+ }
+
+ buildCodeReviewListPath(packageName: string): string {
+ return `/package/${packageName}`;
+ }
+
+ buildCodeReviewPath(packageName: string): string {
+ return `/package/${packageName}`;
+ }
+
+ buildExternalLink(path: string): string {
+ return 'https://npmjs.com' + (path.startsWith('/') ? path : `/${path}`);
+ }
+}
diff --git a/extensions/github1s/src/adapters/ossinsight/constants.ts b/extensions/github1s/src/adapters/ossinsight/constants.ts
new file mode 100644
index 000000000..85738cadf
--- /dev/null
+++ b/extensions/github1s/src/adapters/ossinsight/constants.ts
@@ -0,0 +1,53 @@
+/**
+ * @file trending constants
+ * @author netcon
+ */
+
+export enum RankingPeriod {
+ Today = 'Today',
+ ThisWeek = 'ThisWeek',
+ ThisMonth = 'ThisMonth',
+}
+
+export const RankingLanguages = [
+ 'JavaScript',
+ 'Java',
+ 'Python',
+ 'PHP',
+ 'C++',
+ 'C#',
+ 'TypeScript',
+ 'Shell',
+ 'C',
+ 'Ruby',
+ 'Rust',
+ 'Go',
+ 'Kotlin',
+ 'HCL',
+ 'PowerShell',
+ 'CMake',
+ 'Groovy',
+ 'PLpgSQL',
+ 'TSQL',
+ 'Dart',
+ 'Swift',
+ 'HTML',
+ 'CSS',
+ 'Elixir',
+ 'Haskell',
+ 'Solidity',
+ 'Assembly',
+ 'R',
+ 'Scala',
+ 'Julia',
+ 'Lua',
+ 'Clojure',
+ 'Erlang',
+ 'Common Lisp',
+ 'Emacs Lisp',
+ 'OCaml',
+ 'MATLAB',
+ 'Objective-C',
+ 'Perl',
+ 'Fortran',
+];
diff --git a/extensions/github1s/src/adapters/ossinsight/data-source.ts b/extensions/github1s/src/adapters/ossinsight/data-source.ts
new file mode 100644
index 000000000..a9a19278e
--- /dev/null
+++ b/extensions/github1s/src/adapters/ossinsight/data-source.ts
@@ -0,0 +1,162 @@
+/**
+ * @file trending page data source
+ * @author netcon
+ */
+
+import { joinPath } from '@/helpers/util';
+import { getCollections } from './interfaces';
+import { RankingLanguages, RankingPeriod } from './constants';
+import { DataSource, Directory, File, FileType, Promisable } from '../types';
+import {
+ createCollectionPageMarkdown,
+ createCollectionsListPageMarkdown,
+ createLanguageListPageMarkdown,
+ createRankingPageMarkdown,
+} from './templates';
+
+type StructureItem =
+ | {
+ type: FileType.Directory;
+ name: string;
+ children: StructureItem[] | (() => Promisable);
+ }
+ | {
+ type: FileType.File;
+ name: string;
+ content?: string | (() => Promisable);
+ };
+
+export class OSSInsightDataSource extends DataSource {
+ private static instance: OSSInsightDataSource | null = null;
+ private textEncodder = new TextEncoder();
+ private rootStructure: StructureItem = {
+ type: FileType.Directory,
+ name: '',
+ children: [
+ {
+ type: FileType.Directory,
+ name: 'collections',
+ children: () =>
+ getCollections().then((collections) => {
+ const collectionsSet = new Set(collections.map((collection) => collection.name));
+ return Array.from(collectionsSet).map((collection) => ({
+ type: FileType.File,
+ name: `${collection}.md`,
+ content: () => createCollectionPageMarkdown(collection),
+ }));
+ }),
+ },
+ {
+ type: FileType.Directory,
+ name: 'languages',
+ children: () =>
+ RankingLanguages.map((language) => {
+ return {
+ type: FileType.Directory,
+ name: language,
+ children: [
+ {
+ type: FileType.File,
+ name: `${language}_Today.md`,
+ content: () => createRankingPageMarkdown(RankingPeriod.Today, language),
+ },
+ {
+ type: FileType.File,
+ name: `${language}_ThisWeek.md`,
+ content: () => createRankingPageMarkdown(RankingPeriod.ThisWeek, language),
+ },
+ {
+ type: FileType.File,
+ name: `${language}_ThisMonth.md`,
+ content: () => createRankingPageMarkdown(RankingPeriod.ThisMonth, language),
+ },
+ ],
+ };
+ }),
+ },
+ {
+ type: FileType.File,
+ name: 'Collections.md',
+ content: () => createCollectionsListPageMarkdown(),
+ },
+ {
+ type: FileType.File,
+ name: 'Languages.md',
+ content: () => createLanguageListPageMarkdown(),
+ },
+ {
+ type: FileType.File,
+ name: 'README.md',
+ content: () => createRankingPageMarkdown(RankingPeriod.Today),
+ },
+ {
+ type: FileType.File,
+ name: 'ThisMonth.md',
+ content: () => createRankingPageMarkdown(RankingPeriod.ThisMonth),
+ },
+ {
+ type: FileType.File,
+ name: 'ThisWeek.md',
+ content: () => createRankingPageMarkdown(RankingPeriod.ThisWeek),
+ },
+ ],
+ };
+
+ public static getInstance(): OSSInsightDataSource {
+ if (OSSInsightDataSource.instance) {
+ return OSSInsightDataSource.instance;
+ }
+ return (OSSInsightDataSource.instance = new OSSInsightDataSource());
+ }
+
+ async getStructureItemChildren(item?: StructureItem) {
+ if (item?.type !== FileType.Directory) {
+ throw new Error('Not a directory');
+ }
+ if (typeof item.children === 'function') {
+ item.children = await item.children();
+ }
+ return item.children;
+ }
+
+ async resolveStructureItem(path: string) {
+ let structureItem: StructureItem | undefined = this.rootStructure;
+ for (const pathPart of path.split('/').filter(Boolean)) {
+ const children = await this.getStructureItemChildren(structureItem);
+ structureItem = children.find((child) => child.name === pathPart);
+ }
+ return structureItem;
+ }
+
+ async provideDirectory(repo: string, ref: string, path: string, recursive?: boolean): Promise {
+ const walk = async (item: StructureItem | undefined, recursive = false, basePath = '/') => {
+ const directoryEntires: Directory['entries'] = [];
+ for (const child of await this.getStructureItemChildren(item)) {
+ const currentPath = joinPath(basePath, child.name);
+ directoryEntires.push({ type: child.type, path: currentPath });
+ if (recursive && child.type === FileType.Directory) {
+ directoryEntires.push(...(await walk(child, recursive, currentPath)));
+ }
+ }
+ return directoryEntires;
+ };
+
+ return {
+ truncated: false,
+ entries: await walk(await this.resolveStructureItem(path), recursive, path),
+ };
+ }
+
+ async provideFile(repo: string, ref: string, path: string): Promise {
+ const structureItem = await this.resolveStructureItem(path);
+ if (structureItem?.type !== FileType.File) {
+ throw new Error('Not a file');
+ }
+ if (typeof structureItem.content === 'function') {
+ structureItem.content = await structureItem.content();
+ }
+ return {
+ content: this.textEncodder.encode(structureItem.content) || '',
+ };
+ }
+}
diff --git a/extensions/github1s/src/adapters/ossinsight/index.ts b/extensions/github1s/src/adapters/ossinsight/index.ts
new file mode 100644
index 000000000..5cf88cc9a
--- /dev/null
+++ b/extensions/github1s/src/adapters/ossinsight/index.ts
@@ -0,0 +1,25 @@
+/**
+ * @file OSSInsight adapter
+ * @author netcon
+ */
+
+import { OSSInsightDataSource } from './data-source';
+import { OSSInsightRouterParser } from './router-parser';
+import { Adapter, PlatformName } from '../types';
+
+export class OSSInsightAdapter implements Adapter {
+ public scheme: string = 'ossinsight';
+ public platformName = PlatformName.GitHub;
+
+ async resolveDataSource() {
+ return OSSInsightDataSource.getInstance();
+ }
+
+ async resolveRouterParser() {
+ return OSSInsightRouterParser.getInstance();
+ }
+
+ activateAsDefault() {}
+
+ deactivateAsDefault() {}
+}
diff --git a/extensions/github1s/src/adapters/ossinsight/interfaces.ts b/extensions/github1s/src/adapters/ossinsight/interfaces.ts
new file mode 100644
index 000000000..8fb38b93f
--- /dev/null
+++ b/extensions/github1s/src/adapters/ossinsight/interfaces.ts
@@ -0,0 +1,93 @@
+/**
+ * @file trending interfaces
+ * @author netcon
+ */
+
+import { memorize } from '@/helpers/func';
+import { RankingPeriod } from './constants';
+
+const OSSInsightEndpoint = `https://api.ossinsight.io`;
+
+const resolveDataFromResponse = (response: Response) => {
+ if (response.status < 200 || response.status >= 300) {
+ return [];
+ }
+ return response.json().then((body) => body?.data?.rows || []);
+};
+
+export type RepoItem = {
+ collection_names?: string;
+ contributor_logins?: string;
+ description?: string;
+ forks: string;
+ language?: string;
+ repo_name: string;
+ stars: string;
+ total_score: string;
+};
+
+export const getTrendingRepos = (period: RankingPeriod, language: string): Promise => {
+ const encodedLanguage = encodeURIComponent(language) || 'All';
+ const periodValue =
+ period === RankingPeriod.Today ? 'past_24_hours' : RankingPeriod.ThisWeek ? 'past_week' : 'past_month';
+ const requestUrl = `${OSSInsightEndpoint}/v1/trends/repos/?language=${encodedLanguage}&period=${periodValue}`;
+ return fetch(requestUrl).then(resolveDataFromResponse);
+};
+
+export type RecentHotCollectionItem = {
+ id: string;
+ name: string;
+ repos: string;
+ repo_id: string;
+ repo_name: string;
+ repo_rank_changes: string;
+ repo_past_period_rank: string;
+ repo_current_period_rank: string;
+};
+
+export const getRecentHotCollections = (): Promise => {
+ const requestUrl = `${OSSInsightEndpoint}/v1/collections/hot/`;
+ return fetch(requestUrl).then(resolveDataFromResponse);
+};
+
+export type CollectionItem = {
+ id: string;
+ name: string;
+};
+
+export const getCollections = memorize((): Promise => {
+ const requestUrl = `${OSSInsightEndpoint}/v1/collections/`;
+ return fetch(requestUrl).then(resolveDataFromResponse);
+});
+
+export const getCollectionIdByName = async (collectionName: string): Promise => {
+ const collections = await getCollections();
+ return collections.find((item) => item.name === collectionName)?.id || null;
+};
+
+export type Last28DaysRankItem = {
+ repo_id: string;
+ repo_name: string;
+ current_period_growth: string;
+ past_period_growth: string;
+ growth_pop: string;
+ rank_pop: string;
+ total: string;
+ current_period_rank: string;
+ past_period_rank: string;
+};
+
+export const getCollectionStarsLast28DaysRank = (collectionId: string): Promise => {
+ const requestUrl = `${OSSInsightEndpoint}/v1/collections/${collectionId}/ranking_by_stars/`;
+ return fetch(requestUrl).then(resolveDataFromResponse);
+};
+
+export const getCollectionPullRequestsLast28DaysRank = (collectionId: string): Promise => {
+ const requestUrl = `${OSSInsightEndpoint}/v1/collections/${collectionId}/ranking_by_issues/`;
+ return fetch(requestUrl).then(resolveDataFromResponse);
+};
+
+export const getCollectionIssuesLast28DaysRank = (collectionId: string): Promise => {
+ const requestUrl = `${OSSInsightEndpoint}/v1/collections/${collectionId}/ranking_by_prs/`;
+ return fetch(requestUrl).then(resolveDataFromResponse);
+};
diff --git a/extensions/github1s/src/adapters/ossinsight/router-parser.ts b/extensions/github1s/src/adapters/ossinsight/router-parser.ts
new file mode 100644
index 000000000..043e41de3
--- /dev/null
+++ b/extensions/github1s/src/adapters/ossinsight/router-parser.ts
@@ -0,0 +1,52 @@
+/**
+ * @file router parser
+ * @author netcon
+ */
+
+import { parsePath } from 'history';
+import * as queryString from 'query-string';
+import * as adapterTypes from '../types';
+import { GitHub1sRouterParser } from '../github1s/router-parser';
+import { normalizePath } from '@/helpers/util';
+
+export class OSSInsightRouterParser extends GitHub1sRouterParser {
+ protected static instance: OSSInsightRouterParser | null = null;
+
+ public static getInstance(): OSSInsightRouterParser {
+ if (OSSInsightRouterParser.instance) {
+ return OSSInsightRouterParser.instance;
+ }
+ return (OSSInsightRouterParser.instance = new OSSInsightRouterParser());
+ }
+
+ async parsePath(path: string): Promise {
+ const { path: pathsOrNull } = queryString.parse((parsePath(path).search || '').slice(1));
+ const filePath = normalizePath((Array.isArray(pathsOrNull) ? pathsOrNull[0] : pathsOrNull) || '');
+ const pageType = filePath.endsWith('.md') ? adapterTypes.PageType.Blob : adapterTypes.PageType.Tree;
+ return { pageType, repo: '', ref: '', filePath };
+ }
+
+ buildTreePath(repo: string, ref?: string, filePath?: string) {
+ return repo ? super.buildTreePath(repo, ref, filePath) : '/';
+ }
+
+ buildBlobPath(repo: string, ref: string, filePath: string, startLine?: number, endLine?: number) {
+ return repo ? super.buildBlobPath(repo, ref, filePath, startLine, endLine) : '/';
+ }
+
+ buildCommitListPath(repo: string) {
+ return repo ? super.buildCommitListPath(repo) : '/';
+ }
+
+ buildCommitPath(repo: string, commitSha: string) {
+ return repo ? super.buildCommitPath(repo, commitSha) : '/';
+ }
+
+ buildCodeReviewListPath(repo: string) {
+ return repo ? super.buildCodeReviewListPath(repo) : '/';
+ }
+
+ buildCodeReviewPath(repo: string, codeReviewId: string) {
+ return repo ? super.buildCodeReviewPath(repo, codeReviewId) : '/';
+ }
+}
diff --git a/extensions/github1s/src/adapters/ossinsight/templates.ts b/extensions/github1s/src/adapters/ossinsight/templates.ts
new file mode 100644
index 000000000..19ff56137
--- /dev/null
+++ b/extensions/github1s/src/adapters/ossinsight/templates.ts
@@ -0,0 +1,260 @@
+/**
+ * @file Trending page
+ * @author netcon
+ */
+
+import { trimEnd } from '@/helpers/util';
+import { RankingLanguages, RankingPeriod } from './constants';
+import {
+ getCollections,
+ getCollectionIdByName,
+ getCollectionIssuesLast28DaysRank,
+ getCollectionPullRequestsLast28DaysRank,
+ getCollectionStarsLast28DaysRank,
+ getRecentHotCollections,
+ getTrendingRepos,
+ RepoItem,
+} from './interfaces';
+
+const buildRepoLink = (repo: string) => `https://github1s.com/${repo}`;
+
+const dataSourceMarkdown = ` (Data source: [👁️ OSS Insight](https://ossinsight.io/?utm_source=github1s&utm_medium=github&utm_campaign=ghtrending))`;
+
+const createRepoItemMarkdown = (repo: RepoItem, period: RankingPeriod) => {
+ const contributorAvatars = (repo.contributor_logins?.split(',') || []).map((username) => {
+ // too many avatars may trigger github rate limits
+ // return `[ ](https://github.com/${username})`;
+ return `[${username}](https://github.com/${username})`;
+ });
+ const repoBadges = [
+ `[](https://github.com/${repo.repo_name})`,
+ `[](https://github.com/${repo.repo_name}/watchers)`,
+ `[](https://github.com/${repo.repo_name}/forks)`,
+ `[](https://github.com/${repo.repo_name}/stargazers)`,
+ `[](https://github.com/${repo.repo_name}/commits)`,
+ ];
+
+ const increaseStarsText = (+repo.stars || 0) >= 0 ? `+${repo.stars || 0}` : `-${repo.stars}`;
+ const increaseForksText = (+repo.forks || 0) >= 0 ? `+${repo.forks || 0}` : `-${repo.forks}`;
+ const contributorsMarkdown = contributorAvatars.length
+ ? ' Built by ' + contributorAvatars.join(' ')
+ : '';
+ const collectionMarkdown = repo.collection_names ? ` ${repo.collection_names} ` : '';
+ const fireThreshold = period === RankingPeriod.ThisWeek ? 5000 : period === RankingPeriod.ThisMonth ? 12000 : 1000;
+
+ return `
+## ${(+repo.total_score || 0) >= fireThreshold ? '🔥' : '🚀'} [${repo.repo_name}](${buildRepoLink(repo.repo_name)})
+
+${repo.description || ''}
+
+⭐️ ${increaseStarsText} 🔗 ${increaseForksText} ${contributorsMarkdown}${collectionMarkdown}
+
+${repoBadges.join(' ')}
+`;
+};
+
+export type PageType = RankingPeriod | 'Languages' | 'Collections' | '';
+const createRankingsLinksMarkdown = (page: PageType) => {
+ const rankingLinks = [
+ page !== RankingPeriod.Today ? `[Today](/README.md)` : 'Today',
+ page !== RankingPeriod.ThisWeek ? `[This Week](/ThisWeek.md)` : 'This Week',
+ page !== RankingPeriod.ThisMonth ? `[This Month](/ThisMonth.md)` : 'This Month',
+ page !== 'Languages' ? `[Languages](/Languages.md)` : 'Languages',
+ page !== 'Collections' ? `[Collections](/Collections.md)` : 'Collections',
+ `[GitHub Trending](https://github.com/trending)`,
+ ];
+ return rankingLinks.join(' ');
+};
+
+export const createRankingPageMarkdown = async (period: RankingPeriod, language = '', collection = '') => {
+ const periodTextInTitle =
+ period === RankingPeriod.ThisWeek ? 'This Week' : period === RankingPeriod.ThisMonth ? 'This Month' : 'Today';
+ const periodTextInContent =
+ period === RankingPeriod.ThisWeek ? 'this week' : period === RankingPeriod.ThisMonth ? 'this month' : 'today';
+
+ const repoItems = (await getTrendingRepos(period, language)).slice(0, 30);
+ const repoItemsMarkdown = repoItems.map((repo) => createRepoItemMarkdown(repo, period));
+ const currentPage = language || collection ? '' : period;
+
+ return `
+# Trending Repos ${periodTextInTitle}${language ? ` (${language})` : ''}
+
+See what the GitHub community is most excited about ${periodTextInContent}.${dataSourceMarkdown}
+
+Other Rankings: ${createRankingsLinksMarkdown(currentPage)}
+
+***
+
+${repoItemsMarkdown.join('\n\n***\n\n')}
+
+***
+`;
+};
+
+export const createLanguageListPageMarkdown = () => {
+ const languageListMarkdown = RankingLanguages.map((language) => {
+ const todayLink = encodeURIComponent(`./languages/${language}/${language}_Today.md`);
+ const thisWeekLink = encodeURIComponent(`./languages/${language}/${language}_ThisWeek.md`);
+ const thisMonthLink = encodeURIComponent(`./languages/${language}/${language}_ThisMonth.md`);
+ return `${language} | [Today](${todayLink}) | [This Week](${thisWeekLink}) | [This Month](${thisMonthLink})`;
+ });
+
+ return `
+# Trending Repos by Languages
+
+See what the GitHub community is most excited by languages.${dataSourceMarkdown}
+
+Other Rankings: ${createRankingsLinksMarkdown('Languages')}
+
+***
+
+| Language | Today | This Week | This Month |
+| -- | -- | -- | -- |
+${languageListMarkdown.join('\n')}
+
+***
+`;
+};
+
+const getPopCountText = (pop_count: number, percentage = false) => {
+ const absValue = Math.abs(pop_count);
+ const valueText = percentage ? `${trimEnd(absValue.toFixed(1), '0.')}%` : absValue;
+ return pop_count > 0
+ ? ` (+${valueText}) `
+ : pop_count < 0
+ ? ` (-${valueText}) `
+ : '';
+};
+
+const getRankChangeText = (rank_change: number) => {
+ const absValue = Math.abs(rank_change);
+ return rank_change > 0
+ ? ` (↓${absValue}) `
+ : rank_change < 0
+ ? ` (↑${absValue}) `
+ : '';
+};
+
+export const createCollectionsListPageMarkdown = async () => {
+ const collectionReposMap = new Map();
+ const [hotCollections, allCollections] = await Promise.all([getRecentHotCollections(), getCollections()]);
+ const sortedCollections = hotCollections.sort(
+ (itemA, itemB) => +itemA.repo_current_period_rank - +itemB.repo_current_period_rank,
+ );
+ const uniqueCollections = sortedCollections.filter((collection) => {
+ const relativeRankText = getRankChangeText(+collection.repo_rank_changes);
+ if (!collectionReposMap.has(collection.name)) {
+ collectionReposMap.set(collection.name, [[collection.repo_name, relativeRankText]]);
+ return true;
+ }
+ collectionReposMap.get(collection.name)?.push([collection.repo_name, relativeRankText]);
+ return false;
+ });
+
+ const hotCollectionsMarkdown = uniqueCollections.map((collection) => {
+ const collectionMarkdown = ` [${collection.name}](/collections/${encodeURIComponent(collection.name)}.md)`;
+ const repos = [...(collectionReposMap.get(collection.name) || [])];
+ const firstRepo = repos[0] ? `[${repos[0][0]}](${buildRepoLink(repos[0][0])})${repos[0][1]} ` : '';
+ const secondRepo = repos[1] ? `[${repos[1][0]}](${buildRepoLink(repos[1][0])})${repos[1][1]} ` : '';
+ const thirdRepo = repos[2] ? `[${repos[2][0]}](${buildRepoLink(repos[2][0])})${repos[2][1]} ` : '';
+ const hotReposMarkdown = ` ${firstRepo}| ${secondRepo}| ${thirdRepo}|`;
+ return `|${collectionMarkdown} | ${collection.repos} |${hotReposMarkdown} |`;
+ });
+
+ const allCollectionsMarkdown = allCollections.map((collection) => {
+ return `[${collection.name}](/collections/${encodeURIComponent(collection.name)}.md)`;
+ });
+
+ return `
+# Trending Repos by Collections
+
+Insights about the monthly and historical rankings and trends in technical fields with curated repository lists.${dataSourceMarkdown}
+
+Other Rankings: ${createRankingsLinksMarkdown('Collections')}
+
+***
+
+## Recent Hot Collections
+
+| Collection | Repos | 1st repo | 2nd repo | 3rd repo |
+| -- | -- | -- | -- | -- |
+${hotCollectionsMarkdown.join('\n')}
+
+***
+
+## All Collections
+
+${allCollectionsMarkdown.join(' | ')}
+
+***
+
+`;
+};
+
+export const createCollectionPageMarkdown = async (collectionName: string) => {
+ const collectionId = await getCollectionIdByName(collectionName);
+ if (!collectionId) {
+ throw new Error('Unable to find ranking data for ' + collectionName);
+ }
+ const [starsData, pullsData, issuesData] = await Promise.all([
+ getCollectionStarsLast28DaysRank(collectionId),
+ getCollectionPullRequestsLast28DaysRank(collectionId),
+ getCollectionIssuesLast28DaysRank(collectionId),
+ ]);
+
+ const starRankListMarkdown = starsData.map((item) => {
+ const rankMarkdown = ` ${item.current_period_rank}${getRankChangeText(+item.rank_pop)}`;
+ const repoMarkdown = ` [${item.repo_name}](${buildRepoLink(item.repo_name)})`;
+ const starsMarkdown = ` ${item.current_period_growth}${getPopCountText(+item.growth_pop, true)}`;
+ return `|${rankMarkdown} |${repoMarkdown} |${starsMarkdown} | ${item.past_period_growth} | ${item.total} |`;
+ });
+
+ const pullRankListMarkdown = pullsData.map((item) => {
+ const rankMarkdown = ` ${item.current_period_rank}${getRankChangeText(+item.rank_pop)}`;
+ const repoMarkdown = ` [${item.repo_name}](${buildRepoLink(item.repo_name)})`;
+ const starsMarkdown = ` ${item.current_period_growth}${getPopCountText(+item.growth_pop, true)}`;
+ return `|${rankMarkdown} |${repoMarkdown} |${starsMarkdown} | ${item.past_period_growth} | ${item.total} |`;
+ });
+
+ const issuesRankListMarkdown = issuesData.map((item) => {
+ const rankMarkdown = ` ${item.current_period_rank}${getRankChangeText(+item.rank_pop)}`;
+ const repoMarkdown = ` [${item.repo_name}](${buildRepoLink(item.repo_name)})`;
+ const starsMarkdown = ` ${item.current_period_growth}${getPopCountText(+item.growth_pop, true)}`;
+ return `|${rankMarkdown} |${repoMarkdown} |${starsMarkdown} | ${item.past_period_growth} | ${item.total} |`;
+ });
+
+ return `
+# ${collectionName} - Ranking
+
+Last 28 days ranking of repos in this collection by stars, pull requests, issues. Historical Ranking by Popularity.${dataSourceMarkdown}
+
+Other Rankings: ${createRankingsLinksMarkdown('')}
+
+***
+
+## Last 28 days Ranking - Stars
+
+| Rank | Repo | Last 28 Days | Last 56 Days | Total |
+| -- | -- | -- | -- | -- |
+${starRankListMarkdown.join('\n')}
+
+***
+
+## Last 28 days Ranking - Pull Requests
+
+| Rank | Repo | Last 28 Days | Last 56 Days | Total |
+| -- | -- | -- | -- | -- |
+${pullRankListMarkdown.join('\n')}
+
+***
+
+## Last 28 days Ranking - Issues
+
+| Rank | Repo | Last 28 Days | Last 56 Days | Total |
+| -- | -- | -- | -- | -- |
+${issuesRankListMarkdown.join('\n')}
+
+***
+
+`;
+};
diff --git a/extensions/github1s/src/adapters/sourcegraph/blame.ts b/extensions/github1s/src/adapters/sourcegraph/blame.ts
new file mode 100644
index 000000000..9fd90fb5b
--- /dev/null
+++ b/extensions/github1s/src/adapters/sourcegraph/blame.ts
@@ -0,0 +1,71 @@
+/**
+ * @file sourcegraph blame api
+ * @author netcon
+ */
+
+import { gql } from '@apollo/client/core';
+import { BlameRange } from '../types';
+import { querySourcegraphRepository } from './common';
+
+const BlameRangesQuery = gql`
+ query ($repository: String!, $ref: String!, $path: String!) {
+ repository(name: $repository) {
+ commit(rev: $ref) {
+ blob(path: $path) {
+ blame(startLine: 1, endLine: 99999) {
+ startLine
+ endLine
+ commit {
+ oid
+ author {
+ person {
+ name
+ avatarURL
+ }
+ date
+ }
+ committer {
+ person {
+ email
+ name
+ }
+ }
+ message
+ }
+ }
+ }
+ }
+ }
+ }
+`;
+
+const computeBlameRangeAge = (time: number, oldestTime: number, newestTime: number) => {
+ const blockWidth = (newestTime - oldestTime) / 10;
+ return 10 - Math.floor((time - oldestTime) / blockWidth);
+};
+
+export const getFileBlameRanges = async (repository: string, ref: string, path: string): Promise => {
+ const repositoryData = await querySourcegraphRepository({
+ query: BlameRangesQuery,
+ variables: { repository, ref, path },
+ });
+
+ const blames = repositoryData.commit?.blob?.blame || [];
+ const commitTimes = blames.map((blame) => new Date(blame?.commit?.author?.date).getTime()).sort();
+ const oldestTime = commitTimes[0];
+ const newestTime = commitTimes[commitTimes.length - 1];
+ return blames.map((blame) => ({
+ age: computeBlameRangeAge(new Date(blame.commit?.author?.date).getTime(), oldestTime, newestTime),
+ startingLine: blame.startLine,
+ endingLine: blame.endLine - 1,
+ commit: {
+ sha: blame.commit?.oid,
+ author: blame.commit?.author?.person?.name,
+ email: blame.commit?.committer?.person?.email,
+ message: blame.commit?.message,
+ committer: blame.commit?.commiter?.person?.name,
+ createTime: new Date(blame.commit?.author?.date),
+ avatarUrl: blame.commit?.author?.person?.avatarURL,
+ },
+ }));
+};
diff --git a/extensions/github1s/src/adapters/sourcegraph/commit.ts b/extensions/github1s/src/adapters/sourcegraph/commit.ts
new file mode 100644
index 000000000..2d1f7285f
--- /dev/null
+++ b/extensions/github1s/src/adapters/sourcegraph/commit.ts
@@ -0,0 +1,102 @@
+/**
+ * @file Sourcegraph commits api (tree/blob)
+ * @author netcon
+ */
+
+import { gql } from '@apollo/client/core';
+import { Commit } from '../types';
+import { querySourcegraphRepository } from './common';
+
+const CommitDetail = `
+ oid
+ author {
+ person {
+ name
+ avatarURL
+ }
+ date
+ }
+ committer {
+ person {
+ email
+ name
+ }
+ }
+ message
+ parents {
+ oid
+ }
+`;
+
+const CommitsQuery = gql`
+ query($repository: String!, $ref: String!, $path: String!, $first: Int) {
+ repository(name: $repository) {
+ commit(rev: $ref) {
+ ${CommitDetail}
+ ancestors(path: $path, first: $first) {
+ nodes {
+ ${CommitDetail}
+ }
+ }
+ }
+ }
+ }
+`;
+
+const getUserAvatarUrl = (username: string, isGitHub: boolean) => {
+ if (isGitHub && !username.includes(' ')) {
+ return `https://github.com/${username.slice(-5) === '[bot]' ? username.slice(0, -5) : username}.png`;
+ }
+ return `https://www.gravatar.com/avatar/${username}?d=identicon`;
+};
+
+const formatCommit = (commit: any, isGitHub: boolean) => {
+ if (!commit) {
+ return null;
+ }
+ const authorName = commit?.author?.person?.name;
+ return {
+ sha: commit?.oid,
+ author: authorName,
+ email: commit?.committer?.person?.email,
+ message: commit?.message,
+ committer: commit?.commiter?.person?.name,
+ createTime: new Date(commit?.author?.date),
+ parents: commit?.parents?.map?.((item) => item?.oid) || [],
+ avatarUrl: commit?.author?.person?.avatarURL || getUserAvatarUrl(authorName, isGitHub),
+ };
+};
+
+export const getCommits = async (repository: string, ref: string, path?: string, limit?: number): Promise => {
+ const repositoryData = await querySourcegraphRepository({
+ query: CommitsQuery,
+ variables: { repository, ref, path: path || '', first: limit ? limit : undefined },
+ });
+
+ const firstCommit = repositoryData.commit;
+ const isGitHub = repository.startsWith('github.com/');
+ const restCommits = firstCommit?.ancestors?.nodes?.map?.((item) => formatCommit(item, isGitHub)) || [];
+ if (firstCommit.oid === restCommits[0]?.sha) {
+ return restCommits.filter(Boolean);
+ }
+ return [formatCommit(firstCommit, isGitHub), ...restCommits].slice(0, limit).filter(Boolean);
+};
+
+const CommitQuery = gql`
+query($repository: String!, $ref: String!) {
+ repository(name: $repository) {
+ commit(rev: $ref) {
+ ${CommitDetail}
+ }
+ }
+}
+`;
+
+export const getCommit = async (repository: string, ref: string): Promise => {
+ const repositoryData = await querySourcegraphRepository({
+ query: CommitQuery,
+ variables: { repository, ref },
+ });
+ const isGitHub = repository.startsWith('github.com/');
+ return formatCommit(repositoryData.commit, isGitHub);
+};
diff --git a/extensions/github1s/src/adapters/sourcegraph/common.ts b/extensions/github1s/src/adapters/sourcegraph/common.ts
new file mode 100644
index 000000000..436b47d37
--- /dev/null
+++ b/extensions/github1s/src/adapters/sourcegraph/common.ts
@@ -0,0 +1,66 @@
+/**
+ * @file Sourcegraph api common utils
+ * @author netcon
+ */
+
+import { ApolloClient, createHttpLink, InMemoryCache } from '@apollo/client/core';
+import { isNil, trimEnd, trimStart } from '@/helpers/util';
+
+const createFetchWithTimeout = (timeout: number): typeof fetch => {
+ return (uri, opts) => {
+ const ctrl = new AbortController();
+ const timeoutId = setTimeout(() => ctrl.abort(), timeout);
+ return fetch(uri, { ...opts, signal: ctrl.signal }).finally(() => {
+ clearTimeout(timeoutId);
+ });
+ };
+};
+
+const sourcegraphLink = createHttpLink({
+ uri: 'https://sourcegraph.com/.api/graphql',
+ fetch: createFetchWithTimeout(3000),
+});
+
+export const sourcegraphClient = new ApolloClient({
+ link: sourcegraphLink,
+ cache: new InMemoryCache(),
+});
+
+export const querySourcegraphRepository = async (
+ ...args: Parameters
+): Promise> => {
+ const response = await sourcegraphClient.query(...args);
+ if (isNil((response.data as any).repository)) {
+ const error = new Error('repository is not found');
+ (error as any).repositoryNotFound = true;
+ throw error;
+ }
+ return (response.data as any).repository;
+};
+
+export const canBeConvertToRegExp = (str: string) => {
+ try {
+ new RegExp(str);
+ return true;
+ } catch (e) {
+ return false;
+ }
+};
+
+export const combineGlobsToRegExp = (globs: string[]) => {
+ // only support very simple globs convert now
+ const result = Array.from(
+ new Set(globs.map((glob: string) => trimEnd(trimStart(glob, '*/'), '*/').replace(/^\./, '\\.'))),
+ )
+ // if the glob still not can be convert to a regexp, just ignore it
+ .filter((item) => canBeConvertToRegExp(item))
+ .join('|');
+ // ensure the result can be convert to a regexp
+ return canBeConvertToRegExp(result) ? result : '';
+};
+
+export const escapeRegexp = (text: string): string => text.replace(/[-[\]{}()*+?.,\\^$|#\s]/g, '\\$&');
+
+export const buildRepoPattern = (repo: string) => {
+ return `^${escapeRegexp(repo)}$`;
+};
diff --git a/extensions/github1s/src/adapters/sourcegraph/comparison.ts b/extensions/github1s/src/adapters/sourcegraph/comparison.ts
new file mode 100644
index 000000000..dc7607ef1
--- /dev/null
+++ b/extensions/github1s/src/adapters/sourcegraph/comparison.ts
@@ -0,0 +1,56 @@
+/**
+ * @file sourcegraph comparison api
+ * @author netcon
+ */
+
+import { gql } from '@apollo/client/core';
+import { ChangedFile, FileChangeStatus } from '../types';
+import { querySourcegraphRepository } from './common';
+
+const ComparisonQuery = gql`
+ query ($repository: String!, $base: String!, $head: String!) {
+ repository(name: $repository) {
+ comparison(base: $base, head: $head) {
+ fileDiffs {
+ nodes {
+ newPath
+ oldPath
+ }
+ }
+ }
+ }
+ }
+`;
+
+const getFileChangeStatus = (oldPath: string | null, newPath: string | null): FileChangeStatus => {
+ if (!oldPath) {
+ return FileChangeStatus.Added;
+ }
+ if (!newPath) {
+ return FileChangeStatus.Removed;
+ }
+ if (oldPath !== newPath) {
+ return FileChangeStatus.Renamed;
+ }
+ return FileChangeStatus.Modified;
+};
+
+export const compareCommits = async (repository: string, base: string, head: string): Promise => {
+ const repositoryData = await querySourcegraphRepository({
+ query: ComparisonQuery,
+ variables: { repository, base, head },
+ });
+ const diffFiles = repositoryData.comparison?.fileDiffs?.nodes || [];
+
+ return diffFiles
+ .filter((file) => file.oldPath || file.newPath)
+ .map((file) => {
+ const status = getFileChangeStatus(file.oldPath, file.newPath);
+
+ return {
+ status,
+ path: file.newPath || file.oldPath,
+ previousPath: status === FileChangeStatus.Renamed ? file.oldPath : undefined,
+ };
+ });
+};
diff --git a/extensions/github1s/src/adapters/sourcegraph/data-source.ts b/extensions/github1s/src/adapters/sourcegraph/data-source.ts
new file mode 100644
index 000000000..d46302920
--- /dev/null
+++ b/extensions/github1s/src/adapters/sourcegraph/data-source.ts
@@ -0,0 +1,269 @@
+/**
+ * @file sourcegraph data-source-provider
+ * @author netcon
+ */
+
+import { matchSorter } from 'match-sorter';
+import {
+ Branch,
+ TextSearchOptions,
+ Commit,
+ CommonQueryOptions,
+ DataSource,
+ Directory,
+ File,
+ BlameRange,
+ Tag,
+ TextSearchResults,
+ TextSearchQuery,
+ ChangedFile,
+ SymbolHover,
+ SymbolReferences,
+ SymbolDefinitions,
+ CommitsQueryOptions,
+ FileType,
+} from '../types';
+import { getFileBlameRanges } from './blame';
+import { getCommit, getCommits } from './commit';
+import { compareCommits } from './comparison';
+import { getSymbolDefinitions } from './definition';
+import { readDirectory, readFile } from './file';
+import { getSymbolHover } from './hover';
+import { getAllRefs } from './ref';
+import { getSymbolReferences } from './reference';
+import { getRepository } from './repository';
+import { getTextSearchResults } from './search';
+import { decorate, memorize } from '@/helpers/func';
+import { normalizePath, trimStart } from '@/helpers/util';
+
+type SupportedPlatform = 'github' | 'gitlab' | 'bitbucket';
+
+export class SourcegraphDataSource extends DataSource {
+ private static instanceMap: Map = new Map();
+ private refsPromiseMap: Map> = new Map();
+ private repositoryPromiseMap: Map> = new Map();
+ private fileTypeMap: Map = new Map(); // cache if path is a directory
+ private matchedRefsMap: Map = new Map();
+ private textEncoder = new TextEncoder();
+
+ public static getInstance(platform: SupportedPlatform): SourcegraphDataSource {
+ if (!SourcegraphDataSource.instanceMap.has(platform)) {
+ SourcegraphDataSource.instanceMap.set(platform, new SourcegraphDataSource(platform));
+ }
+ return SourcegraphDataSource.instanceMap.get(platform)!;
+ }
+
+ private constructor(private platform: SupportedPlatform) {
+ super();
+ }
+
+ buildRepository(repo: string) {
+ if (this.platform === 'github') {
+ return `github.com/${repo}`;
+ }
+ if (this.platform === 'gitlab') {
+ return `gitlab.com/${repo}`;
+ }
+ if (this.platform === 'bitbucket') {
+ return `bitbucket.org/${repo}`;
+ }
+ return repo;
+ }
+
+ async provideDirectory(repo: string, ref: string, path: string, recursive = false): Promise {
+ const directories = await readDirectory(this.buildRepository(repo), ref, trimStart(path, '/'), recursive);
+ directories.entries.forEach((entry) => {
+ const mapKey = `${repo} ${ref} ${entry.path}`;
+ this.fileTypeMap.set(mapKey, entry.type);
+ });
+ return directories;
+ }
+
+ async detectPathFileType(repo: string, ref: string, path: string) {
+ if (path === '/') {
+ return FileType.Directory;
+ }
+ const mapKey = `${repo} ${ref} ${path}`;
+ if (this.fileTypeMap.has(mapKey)) {
+ return this.fileTypeMap.get(mapKey)!;
+ }
+ await this.provideDirectory(repo, ref, normalizePath(path.split('/').slice(0, -1).join('/')), false);
+ return this.fileTypeMap.get(mapKey) || FileType.File;
+ }
+
+ async provideRepository(repo: string) {
+ if (!this.repositoryPromiseMap.has(repo)) {
+ this.repositoryPromiseMap.set(repo, getRepository(this.buildRepository(repo)));
+ }
+ return this.repositoryPromiseMap.get(repo);
+ }
+
+ async provideFile(repo: string, ref: string, path: string): Promise {
+ const apiPath = trimStart(path, '/');
+ // sourcegraph api break binary files and text coding, so we use github api first here
+ if (this.platform === 'github') {
+ // For GitHub repositories, request GitHub User Content API first (it seems no Rate Limit),
+ // because Sourcegraph API returns binary data such as pictures in error. If the GitHub User
+ // Content API goes wrong, then try Sourcegraph API. Use `try catch` because if fallback to
+ // GitHub REST API may trigger a pop-up window to request authentication for anonymous users.
+ try {
+ return fetch(encodeURI(`https://raw.githubusercontent.com/${repo}/${ref}/${apiPath}`))
+ .then((response) => (response.ok ? response.arrayBuffer() : Promise.reject({ response })))
+ .then((buffer) => ({ content: new Uint8Array(buffer) }));
+ } catch {}
+ }
+ // TODO: support binary files for other platforms
+ const { content } = await readFile(this.buildRepository(repo), ref, apiPath);
+ return { content: this.textEncoder.encode(content) };
+ }
+
+ async prepareAllRefs(repo: string) {
+ if (!this.refsPromiseMap.has(repo)) {
+ this.refsPromiseMap.set(repo, getAllRefs(this.buildRepository(repo)));
+ }
+ return this.refsPromiseMap.get(repo)!;
+ }
+
+ @decorate(memorize)
+ private async getDefaultBranch(repo: string) {
+ return (await this.provideRepository(repo))?.defaultBranch || 'HEAD';
+ }
+
+ async extractRefPath(repo: string, refAndPath: string): Promise<{ ref: string; path: string }> {
+ if (!refAndPath) {
+ return { ref: await this.getDefaultBranch(repo), path: '/' };
+ }
+ if (refAndPath.match(/^HEAD(\/.*)?$/i)) {
+ return { ref: 'HEAD', path: normalizePath(refAndPath.slice(5)) };
+ }
+ if (!this.matchedRefsMap.has(repo)) {
+ this.matchedRefsMap.set(repo, []);
+ }
+ const matchPathRef = (ref) => refAndPath.startsWith(`${ref}/`) || refAndPath === ref;
+ const pathRef = this.matchedRefsMap.get(repo)?.find(matchPathRef);
+ if (pathRef) {
+ return { ref: pathRef, path: normalizePath(refAndPath.slice(pathRef.length + 1)) };
+ }
+ const { branches, tags } = await this.prepareAllRefs(repo);
+ const exactRef = [...branches, ...tags].map((item) => item.name).find(matchPathRef);
+ const ref = exactRef || refAndPath.split('/')[0] || 'HEAD';
+ exactRef && this.matchedRefsMap.get(repo)?.push(ref);
+ return { ref, path: normalizePath(refAndPath.slice(ref.length + 1)) };
+ }
+
+ async provideBranches(repo: string, options?: CommonQueryOptions): Promise {
+ const cachedRefs = await this.prepareAllRefs(repo);
+ const matchedBranches = options?.query
+ ? matchSorter(cachedRefs?.branches || [], options.query, { keys: ['name'] })
+ : cachedRefs?.branches || [];
+ if (options?.pageSize) {
+ const page = options.page || 1;
+ const pageSize = options.pageSize;
+ return matchedBranches.slice(pageSize * (page - 1), pageSize * page);
+ }
+ return matchedBranches;
+ }
+
+ async provideTags(repo: string, options?: CommonQueryOptions): Promise {
+ const cachedRefs = await this.prepareAllRefs(repo);
+ const matchedTags = options?.query
+ ? matchSorter(cachedRefs?.tags || [], options.query, { keys: ['name'] })
+ : cachedRefs?.tags || [];
+ if (options?.pageSize) {
+ const page = options.page || 1;
+ const pageSize = options.pageSize;
+ return matchedTags.slice(pageSize * (page - 1), pageSize * page);
+ }
+ return matchedTags;
+ }
+
+ async provideTextSearchResults(
+ repo: string,
+ ref: string,
+ query: TextSearchQuery,
+ options: TextSearchOptions,
+ ): Promise {
+ const results = await getTextSearchResults(this.buildRepository(repo), ref, query, options);
+ return {
+ ...results,
+ results: results.results.map((result) => ({ ...result, path: normalizePath(result.path) })),
+ };
+ }
+
+ async provideCommits(repo: string, options?: CommitsQueryOptions): Promise<(Commit & { files?: ChangedFile[] })[]> {
+ let commits = await getCommits(
+ this.buildRepository(repo),
+ options?.from || 'HEAD',
+ options?.path === undefined ? undefined : trimStart(options.path, '/'),
+ options?.pageSize ? options.pageSize * (options.page || 1) : undefined,
+ );
+ if (options?.path && options.path !== '/' && commits.length) {
+ // find the latested that related the `options.path` file
+ const changedFiles = await this.provideCommitChangedFiles(repo, commits[0].sha);
+ commits = changedFiles.find((file) => file.path === options.path) ? commits : commits.slice(1);
+ }
+ return options?.pageSize ? commits.slice(options.pageSize * ((options.page || 1) - 1)) : commits;
+ }
+
+ async provideCommit(repo: string, ref: string): Promise {
+ return getCommit(this.buildRepository(repo), ref);
+ }
+
+ async provideCommitChangedFiles(repo: string, ref: string, _options?: CommonQueryOptions): Promise {
+ return (await compareCommits(this.buildRepository(repo), `${ref}~`, ref)).map((file) => ({
+ ...file,
+ path: normalizePath(file.path),
+ previousPath: file.previousPath ? normalizePath(file.previousPath) : undefined,
+ }));
+ }
+
+ async provideFileBlameRanges(repo: string, ref: string, path: string): Promise {
+ return getFileBlameRanges(this.buildRepository(repo), ref, trimStart(path, '/'));
+ }
+
+ async provideSymbolDefinitions(
+ repo: string,
+ ref: string,
+ path: string,
+ line: number,
+ character: number,
+ symbol: string,
+ ): Promise {
+ const apiPath = trimStart(path, '/');
+ return getSymbolDefinitions(this.buildRepository(repo), ref, apiPath, line, character, symbol).then((locations) =>
+ locations.map((location) => ({ ...location, path: normalizePath(location.path) })),
+ );
+ }
+
+ async provideSymbolReferences(
+ repo: string,
+ ref: string,
+ path: string,
+ line: number,
+ character: number,
+ symbol: string,
+ ): Promise {
+ const apiPath = trimStart(path, '/');
+ return getSymbolReferences(this.buildRepository(repo), ref, apiPath, line, character, symbol).then((locations) =>
+ locations.map((location) => ({ ...location, path: normalizePath(location.path) })),
+ );
+ }
+
+ async provideSymbolHover(
+ repo: string,
+ ref: string,
+ path: string,
+ line: number,
+ character: number,
+ _symbol: string,
+ ): Promise {
+ return getSymbolHover(this.buildRepository(repo), ref, path, line, character);
+ }
+
+ provideUserAvatarLink(user: string): string {
+ if (this.platform === 'github') {
+ return `https://github.com/${user}.png`;
+ }
+ return `https://www.gravatar.com/avatar/${user}?d=identicon`;
+ }
+}
diff --git a/extensions/github1s/src/interfaces/sourcegraph/definition.ts b/extensions/github1s/src/adapters/sourcegraph/definition.ts
similarity index 51%
rename from extensions/github1s/src/interfaces/sourcegraph/definition.ts
rename to extensions/github1s/src/adapters/sourcegraph/definition.ts
index 2e70332de..84d1164f2 100644
--- a/extensions/github1s/src/interfaces/sourcegraph/definition.ts
+++ b/extensions/github1s/src/adapters/sourcegraph/definition.ts
@@ -6,33 +6,10 @@
import { gql } from '@apollo/client/core';
import { sourcegraphClient } from './common';
import { getSymbolPositions } from './position';
-
-export interface SymbolDefinition {
- precise: boolean;
- owner: string;
- repo: string;
- ref: string;
- path: string;
- range: {
- start: {
- line: number;
- character: number;
- };
- end: {
- line: number;
- character: number;
- };
- };
-}
+import { SymbolDefinitions } from '../types';
const LSIFDefinitionsQuery = gql`
- query(
- $repository: String!
- $ref: String!
- $path: String!
- $line: Int!
- $character: Int!
- ) {
+ query ($repository: String!, $ref: String!, $path: String!, $line: Int!, $character: Int!) {
repository(name: $repository) {
commit(rev: $ref) {
blob(path: $path) {
@@ -70,68 +47,42 @@ const LSIFDefinitionsQuery = gql`
// find definitions with Sourcegraph LSIF
// https://docs.sourcegraph.com/code_intelligence/explanations/precise_code_intelligence
const getLSIFDefinitions = async (
- owner: string,
- repo: string,
+ repository: string,
ref: string,
path: string,
line: number,
- character: number
-): Promise => {
+ character: number,
+): Promise => {
const response = await sourcegraphClient.query({
query: LSIFDefinitionsQuery,
- variables: {
- repository: `github.com/${owner}/${repo}`,
- ref,
- path: path.slice(1),
- line,
- character,
- },
+ variables: { repository, ref, path, line, character },
});
- const definitionNodes =
- response?.data?.repository?.commit?.blob?.lsif?.definitions?.nodes;
+ const definitionNodes = response.data?.repository?.commit?.blob?.lsif?.definitions?.nodes;
+ // TODO: cross-repository symbols
return (definitionNodes || []).map(({ resource, range }) => {
- const [owner, repo] = resource.repository.name
- .split('/')
- .filter(Boolean)
- .slice(-2);
- return {
- precise: true,
- owner,
- repo,
- ref: resource.commit.oid,
- path: `/${resource.path}`,
- range,
- };
+ return { path: resource.path, range };
});
};
export const getSymbolDefinitions = (
- owner: string,
- repo: string,
+ repository: string,
ref: string,
path: string,
line: number,
character: number,
- symbol: string
-): Promise => {
+ symbol: string,
+): Promise => {
// if failed to find definitions from LSIF,
// fallback to search-based definitions, using
// two promise instead of `await` to request in
// parallel for getting result as soon as possible
- const LSIFDefinitionsPromise = getLSIFDefinitions(
- owner,
- repo,
- ref,
- path,
- line,
- character
- );
- const searchDefinitionsPromise = getSymbolPositions(owner, repo, ref, symbol);
+ const LSIFDefinitionsPromise = getLSIFDefinitions(repository, ref, path, line, character);
+ const searchDefinitionsPromise = getSymbolPositions(repository, ref, symbol);
- return LSIFDefinitionsPromise.then((LSIFDefinitions) => {
- if (LSIFDefinitions.length) {
- return LSIFDefinitions;
+ return LSIFDefinitionsPromise.then((LSIFResults) => {
+ if (LSIFResults.length) {
+ return LSIFResults;
}
- return searchDefinitionsPromise as Promise;
+ return searchDefinitionsPromise;
});
};
diff --git a/extensions/github1s/src/adapters/sourcegraph/file.ts b/extensions/github1s/src/adapters/sourcegraph/file.ts
new file mode 100644
index 000000000..280c1bda5
--- /dev/null
+++ b/extensions/github1s/src/adapters/sourcegraph/file.ts
@@ -0,0 +1,78 @@
+/**
+ * @file Sourcegraph file api (tree/blob)
+ * @author netcon
+ */
+
+import { gql } from '@apollo/client/core';
+import { querySourcegraphRepository } from './common';
+import { Directory, FileType } from '../types';
+import { normalizePath } from '@/helpers/util';
+
+const FILE_COUNT_LIMIT = 50000;
+
+const DirectoryQuery = gql`
+ query($repository: String!, $ref: String!, $path: String!, $recursive: Boolean = false) {
+ repository(name: $repository) {
+ commit(rev: $ref) {
+ tree(path: $path) {
+ entries(first: ${FILE_COUNT_LIMIT}, recursive: $recursive) {
+ path
+ isDirectory
+ # TODO submodule don't work now
+ submodule {
+ commit
+ }
+ }
+ }
+ }
+ }
+ }
+`;
+
+export const readDirectory = async (
+ repository: string,
+ ref: string,
+ path: string,
+ recursive = false,
+): Promise => {
+ const repositoryData = await querySourcegraphRepository({
+ query: DirectoryQuery,
+ variables: { repository, ref, path, recursive },
+ });
+ const files = repositoryData.commit?.tree?.entries || [];
+ return {
+ entries: files.map((file) => ({
+ path: normalizePath(file.path),
+ type: file.isDirectory ? FileType.Directory : file.submodule ? FileType.Submodule : FileType.File,
+ commitSha: file.submodule?.commit,
+ })),
+ truncated: files.length >= FILE_COUNT_LIMIT,
+ };
+};
+
+const FileQuery = gql`
+ query ($repository: String!, $ref: String!, $path: String!) {
+ repository(name: $repository) {
+ commit(rev: $ref) {
+ blob(path: $path) {
+ content
+ binary
+ }
+ }
+ }
+ }
+`;
+
+export const readFile = async (
+ repository: string,
+ ref: string,
+ path: string,
+ recursive = false,
+): Promise<{ content: string; binary: boolean }> => {
+ const repositoryData = await querySourcegraphRepository({
+ query: FileQuery,
+ variables: { repository, ref, path, recursive },
+ });
+ const blob = repositoryData.commit?.blob;
+ return { content: blob?.content || '', binary: blob?.binary || false };
+};
diff --git a/extensions/github1s/src/interfaces/sourcegraph/hover.ts b/extensions/github1s/src/adapters/sourcegraph/hover.ts
similarity index 64%
rename from extensions/github1s/src/interfaces/sourcegraph/hover.ts
rename to extensions/github1s/src/adapters/sourcegraph/hover.ts
index d106308a8..a320d53e6 100644
--- a/extensions/github1s/src/interfaces/sourcegraph/hover.ts
+++ b/extensions/github1s/src/adapters/sourcegraph/hover.ts
@@ -4,21 +4,11 @@
*/
import { gql } from '@apollo/client/core';
+import { SymbolHover } from '../types';
import { sourcegraphClient } from './common';
-export interface SymbolHover {
- precise: boolean;
- markdown: string;
-}
-
const LSIFHoverQuery = gql`
- query(
- $repository: String!
- $ref: String!
- $path: String!
- $line: Int!
- $character: Int!
- ) {
+ query ($repository: String!, $ref: String!, $path: String!, $line: Int!, $character: Int!) {
repository(name: $repository) {
commit(rev: $ref) {
blob(path: $path) {
@@ -38,40 +28,37 @@ const LSIFHoverQuery = gql`
// find Hover with Sourcegraph LSIF
// https://docs.sourcegraph.com/code_intelligence/explanations/precise_code_intelligence
const getLSIFHover = async (
- owner: string,
- repo: string,
+ repository: string,
ref: string,
path: string,
line: number,
- character: number
+ character: number,
): Promise => {
const response = await sourcegraphClient.query({
query: LSIFHoverQuery,
variables: {
- repository: `github.com/${owner}/${repo}`,
+ repository,
ref,
path: path.slice(1),
line,
character,
},
});
- const markdown =
- response?.data?.repository?.commit?.blob?.lsif?.hover?.markdown?.text;
+ const markdown = response.data?.repository?.commit?.blob?.lsif?.hover?.markdown?.text;
if (!markdown) {
return null;
}
- return { precise: true, markdown };
+ return { markdown };
};
export const getSymbolHover = (
- owner: string,
- repo: string,
+ repository: string,
ref: string,
path: string,
line: number,
- character: number
+ character: number,
): Promise => {
- return getLSIFHover(owner, repo, ref, path, line, character);
+ return getLSIFHover(repository, ref, path, line, character);
};
diff --git a/extensions/github1s/src/adapters/sourcegraph/position.ts b/extensions/github1s/src/adapters/sourcegraph/position.ts
new file mode 100644
index 000000000..6afeb6e9b
--- /dev/null
+++ b/extensions/github1s/src/adapters/sourcegraph/position.ts
@@ -0,0 +1,72 @@
+/**
+ * @file Sourcegraph api for searching symbol positions
+ * @author netcon
+ */
+
+import { gql } from '@apollo/client/core';
+import { buildRepoPattern, escapeRegexp, sourcegraphClient } from './common';
+import { CodeLocation } from '../types';
+
+const searchPositionsQuery = gql`
+ query ($query: String!) {
+ search(query: $query) {
+ results {
+ results {
+ ... on FileMatch {
+ symbols {
+ name
+ kind
+ location {
+ resource {
+ path
+ repository {
+ name
+ }
+ commit {
+ oid
+ }
+ }
+ range {
+ start {
+ line
+ character
+ }
+ end {
+ line
+ character
+ }
+ }
+ }
+ }
+ }
+ }
+ }
+ }
+ }
+`;
+
+// get symbol position information base on search,
+// used by definition, reference and hover
+export const getSymbolPositions = async (repository: string, ref: string, symbol: string): Promise => {
+ const repoPattern = buildRepoPattern(repository);
+ const repoRefString = ref.toUpperCase() === 'HEAD' ? `repo:${repoPattern}` : `repo:${repoPattern}@${ref}`;
+ const optionsString = ['context:global', 'type:symbol', 'patternType:regexp', 'case:yes'].join(' ');
+ const patternString = `^${escapeRegexp(symbol)}$`;
+ const query = [repoRefString, optionsString, patternString].join(' ');
+ const response = await sourcegraphClient.query({
+ query: searchPositionsQuery,
+ variables: { query },
+ });
+
+ const resultSymbols = response?.data?.search?.results?.results?.flatMap((item) => item.symbols) || [];
+ return resultSymbols
+ .map((symbol) => {
+ const { resource, range } = symbol.location;
+ // TODO: cross-repository symbols
+ return {
+ path: resource.path,
+ range,
+ };
+ })
+ .filter(Boolean);
+};
diff --git a/extensions/github1s/src/adapters/sourcegraph/ref.ts b/extensions/github1s/src/adapters/sourcegraph/ref.ts
new file mode 100644
index 000000000..e8129c271
--- /dev/null
+++ b/extensions/github1s/src/adapters/sourcegraph/ref.ts
@@ -0,0 +1,69 @@
+/**
+ * @file Sourcegraph git ref api (branch/tag)
+ * @author netcon
+ */
+
+import { gql } from '@apollo/client/core';
+import { querySourcegraphRepository } from './common';
+import { Branch, Tag } from '../types';
+
+const BranchTagQuery = gql`
+ query ($repository: String!) {
+ repository(name: $repository) {
+ branches {
+ nodes {
+ displayName
+ target {
+ commit {
+ oid
+ author {
+ date
+ }
+ }
+ }
+ }
+ }
+ tags {
+ nodes {
+ displayName
+ target {
+ commit {
+ oid
+ author {
+ date
+ }
+ }
+ }
+ }
+ }
+ }
+ }
+`;
+
+export const getAllRefs = async (repository: string): Promise<{ branches: Branch[]; tags: Tag[] }> => {
+ const repositoryData = await querySourcegraphRepository({
+ query: BranchTagQuery,
+ variables: { repository },
+ });
+
+ const sortByTimeDesc = (branchA, branchB) => {
+ return branchA.target?.commit?.author?.date > branchB.target?.commit?.author?.date ? -1 : 1;
+ };
+ const branches = (repositoryData.branches?.nodes || [])
+ ?.filter(Boolean)
+ ?.sort(sortByTimeDesc)
+ ?.map?.((branch) => ({
+ name: branch.displayName,
+ commitSha: branch.target?.commit?.oid,
+ description: `Branch at ${branch.target?.commit?.oid?.slice(0, 8)}`,
+ }));
+ const tags = (repositoryData.tags?.nodes || [])
+ ?.filter(Boolean)
+ ?.sort(sortByTimeDesc)
+ ?.map?.((tag) => ({
+ name: tag?.displayName,
+ commitSha: tag?.target?.commit?.oid,
+ description: `Tag at ${tag?.target?.commit?.oid?.slice(0, 8)}`,
+ }));
+ return { branches, tags };
+};
diff --git a/extensions/github1s/src/interfaces/sourcegraph/reference.ts b/extensions/github1s/src/adapters/sourcegraph/reference.ts
similarity index 55%
rename from extensions/github1s/src/interfaces/sourcegraph/reference.ts
rename to extensions/github1s/src/adapters/sourcegraph/reference.ts
index cf9054aa7..87c96b390 100644
--- a/extensions/github1s/src/interfaces/sourcegraph/reference.ts
+++ b/extensions/github1s/src/adapters/sourcegraph/reference.ts
@@ -6,33 +6,10 @@
import { gql } from '@apollo/client/core';
import { sourcegraphClient } from './common';
import { getSymbolPositions } from './position';
-
-export interface SymbolReference {
- precise: boolean;
- owner: string;
- repo: string;
- ref: string;
- path: string;
- range: {
- start: {
- line: number;
- character: number;
- };
- end: {
- line: number;
- character: number;
- };
- };
-}
+import { SymbolReferences } from '../types';
const LSIFReferencesQuery = gql`
- query(
- $repository: String!
- $ref: String!
- $path: String!
- $line: Int!
- $character: Int!
- ) {
+ query ($repository: String!, $ref: String!, $path: String!, $line: Int!, $character: Int!) {
repository(name: $repository) {
commit(rev: $ref) {
blob(path: $path) {
@@ -70,68 +47,42 @@ const LSIFReferencesQuery = gql`
// find references with Sourcegraph LSIF
// https://docs.sourcegraph.com/code_intelligence/explanations/precise_code_intelligence
const getLSIFReferences = async (
- owner: string,
- repo: string,
+ repository: string,
ref: string,
path: string,
line: number,
- character: number
-): Promise => {
+ character: number,
+): Promise => {
const response = await sourcegraphClient.query({
query: LSIFReferencesQuery,
- variables: {
- repository: `github.com/${owner}/${repo}`,
- ref,
- path: path.slice(1),
- line,
- character,
- },
+ variables: { repository, ref, path, line, character },
});
- const referenceNodes =
- response?.data?.repository?.commit?.blob?.lsif?.references?.nodes;
+ const referenceNodes = response.data?.repository?.commit?.blob?.lsif?.references?.nodes;
+ // TODO: cross-repository symbols
return (referenceNodes || []).map(({ resource, range }) => {
- const [owner, repo] = resource.repository.name
- .split('/')
- .filter(Boolean)
- .slice(-2);
- return {
- precise: true,
- owner,
- repo,
- ref: resource.commit.oid,
- path: `/${resource.path}`,
- range,
- };
+ return { path: resource.path, range };
});
};
export const getSymbolReferences = (
- owner: string,
- repo: string,
+ repository: string,
ref: string,
path: string,
line: number,
character: number,
- symbol: string
-): Promise => {
+ symbol: string,
+): Promise => {
// if failed to find references from LSIF,
// fallback to search-based references, using
// two promise instead of `await` to request in
// parallel for getting result as soon as possible
- const LSIFReferencesPromise = getLSIFReferences(
- owner,
- repo,
- ref,
- path,
- line,
- character
- );
- const searchReferencesPromise = getSymbolPositions(owner, repo, ref, symbol);
+ const LSIFReferencesPromise = getLSIFReferences(repository, ref, path, line, character);
+ const searchReferencesPromise = getSymbolPositions(repository, ref, symbol);
return LSIFReferencesPromise.then((LSIFReferences) => {
if (LSIFReferences.length) {
return LSIFReferences;
}
- return searchReferencesPromise as Promise;
+ return searchReferencesPromise;
});
};
diff --git a/extensions/github1s/src/adapters/sourcegraph/repository.ts b/extensions/github1s/src/adapters/sourcegraph/repository.ts
new file mode 100644
index 000000000..1021e43f1
--- /dev/null
+++ b/extensions/github1s/src/adapters/sourcegraph/repository.ts
@@ -0,0 +1,32 @@
+/**
+ * @file Sourcegraph file api (tree/blob)
+ * @author netcon
+ */
+
+import { gql } from '@apollo/client/core';
+import { sourcegraphClient } from './common';
+
+const RepositoryQuery = gql`
+ query ($repository: String!) {
+ repository(name: $repository) {
+ name
+ isPrivate
+ defaultBranch {
+ displayName
+ }
+ }
+ }
+`;
+
+export const getRepository = async (
+ repository: string,
+): Promise<{ private: boolean; defaultBranch: string } | null> => {
+ const response = await sourcegraphClient.query({
+ query: RepositoryQuery,
+ variables: { repository },
+ });
+ const repositoryData = response.data?.repository;
+ return repositoryData
+ ? { private: repositoryData.isPrivate, defaultBranch: repositoryData.defaultBranch?.displayName }
+ : null;
+};
diff --git a/extensions/github1s/src/adapters/sourcegraph/search.ts b/extensions/github1s/src/adapters/sourcegraph/search.ts
new file mode 100644
index 000000000..513118cca
--- /dev/null
+++ b/extensions/github1s/src/adapters/sourcegraph/search.ts
@@ -0,0 +1,120 @@
+/**
+ * @file Sourcegraph search api
+ * @author netcon
+ */
+
+import { gql } from '@apollo/client/core';
+import { TextSearchOptions, TextSearchQuery, TextSearchResults } from '../types';
+import { escapeRegexp, sourcegraphClient, combineGlobsToRegExp, buildRepoPattern } from './common';
+
+// build text search query for sourcegraph graphql request
+export const buildTextSearchQueryString = (
+ repository: string,
+ ref: string,
+ query: TextSearchQuery,
+ options: TextSearchOptions,
+): string => {
+ const repoPattern = buildRepoPattern(repository);
+ const countString = `count:${options.pageSize ? (options.page || 1) * options.pageSize : 100}`;
+ const repoRefString = ref.toUpperCase() === 'HEAD' ? `repo:${repoPattern}` : `repo:${repoPattern}@${ref}`;
+ // the string may looks like `case:yse file:src -file:node_modules`
+ const optionsString = [
+ query.isCaseSensitive ? `case:yes` : '',
+ options.includes?.length ? `file:${combineGlobsToRegExp(options.includes)}` : '',
+ options.excludes?.length ? `-file:${combineGlobsToRegExp(options.excludes)}` : '',
+ ]
+ .filter(Boolean)
+ .join(' ');
+ // convert the pattern to adapt the sourcegraph API
+ let patternString = query.pattern;
+
+ if (!query.isRegExp && !query.isWordMatch) {
+ patternString = `"${patternString}"`;
+ } else if (!query.isRegExp && query.isWordMatch) {
+ patternString = `/\\b${escapeRegexp(patternString)}\\b/`;
+ } else if (query.isRegExp && !query.isWordMatch) {
+ patternString = `/${patternString}/`;
+ } else if (query.isRegExp && query.isWordMatch) {
+ return `/\b${patternString}\b/`;
+ }
+
+ return [repoRefString, optionsString, patternString, countString].filter(Boolean).join(' ');
+};
+
+const textSearchQuery = gql`
+ query ($query: String!) {
+ search(query: $query) {
+ results {
+ __typename
+ limitHit
+ matchCount
+ approximateResultCount
+ missing {
+ name
+ }
+ cloning {
+ name
+ }
+ timedout {
+ name
+ }
+ indexUnavailable
+ results {
+ ... on FileMatch {
+ __typename
+ file {
+ name
+ path
+ }
+ lineMatches {
+ preview
+ lineNumber
+ offsetAndLengths
+ }
+ }
+ }
+ }
+ }
+ }
+`;
+
+const formatTextSearchResults = (searchResults, offset: number, limit: number) => {
+ const truncated = !!searchResults?.limitHit;
+ const results = (searchResults?.results || []).slice(offset, limit).flatMap((fileMatch) => {
+ const path = fileMatch?.file?.path;
+
+ return (fileMatch?.lineMatches || []).map((lineMatch) => {
+ const lineNumber = lineMatch?.lineNumber;
+ const ranges = (lineMatch?.offsetAndLengths || []).map((segment) => ({
+ start: { line: lineNumber, character: segment[0] },
+ end: { line: lineNumber, character: segment[0] + segment[1] },
+ }));
+ const previewMatches = (lineMatch?.offsetAndLengths || []).map((segment) => ({
+ start: { line: 0, character: segment[0] },
+ end: { line: 0, character: segment[0] + segment[1] },
+ }));
+ const preview = { text: lineMatch.preview, matches: previewMatches };
+
+ return { path, ranges, preview };
+ });
+ });
+
+ return { results, truncated };
+};
+
+export const getTextSearchResults = (
+ repository: string,
+ ref: string,
+ query: TextSearchQuery,
+ options: TextSearchOptions,
+): Promise => {
+ const offset = options.pageSize ? ((options.page || 1) - 1) * options.pageSize : 0;
+ const limit = options.pageSize ? options.pageSize : 100;
+
+ return sourcegraphClient
+ .query({
+ query: textSearchQuery,
+ variables: { query: buildTextSearchQueryString(repository, ref, query, options) },
+ })
+ .then((response) => formatTextSearchResults(response?.data?.search?.results, offset, limit));
+};
diff --git a/extensions/github1s/src/adapters/types.ts b/extensions/github1s/src/adapters/types.ts
new file mode 100644
index 000000000..ecc563d99
--- /dev/null
+++ b/extensions/github1s/src/adapters/types.ts
@@ -0,0 +1,413 @@
+/**
+ * @file GitHub1s Type Definitions
+ * @author netcon
+ */
+
+export type Promisable = PromiseLike | T;
+
+export enum FileType {
+ Directory = 'Directory',
+ File = 'File',
+ Link = 'Link',
+ Submodule = 'Submodule',
+}
+
+// `page` starts from 1
+type PaginationOptions = { page: number; pageSize: number } | { page?: number; pageSize?: number };
+export type CommonQueryOptions = { query?: string } & PaginationOptions;
+
+export type DirectoryEntry =
+ | { type: FileType.Directory; path: string } // for director or link
+ | { type: FileType.File; path: string; size?: number } // for a file
+ | { type: FileType.Submodule; path: string; commitSha?: string }; // for a submodule
+
+export interface Directory {
+ entries: DirectoryEntry[];
+ // entries doesn't contains all results if `truncated` is true
+ truncated: boolean;
+}
+
+export interface File {
+ content: Uint8Array;
+}
+
+export interface Submodule {
+ // which commit the submodule repository point to
+ ref: string;
+}
+
+export interface SymbolicLink {
+ path: string;
+}
+
+export interface Branch {
+ name: string;
+ commitSha?: string;
+ description?: string;
+}
+
+export interface Tag {
+ name: string;
+ commitSha?: string;
+ description?: string;
+}
+
+export type CommitsQueryOptions = { from?: string; author?: string; path?: string } & CommonQueryOptions;
+
+export interface Commit {
+ sha: string;
+ author?: string; // original commit author
+ email?: string;
+ message: string;
+ committer?: string;
+ createTime?: Date;
+ parents: string[]; // empty array for first commit
+ avatarUrl?: string;
+}
+
+export interface TextSearchQuery {
+ pattern: string;
+ isMultiline?: boolean;
+ isRegExp?: boolean;
+ isCaseSensitive?: boolean;
+ isWordMatch?: boolean;
+}
+
+// `includes` and `excludes` both are glob strings
+export type TextSearchOptions = { includes?: string[]; excludes?: string[] } & PaginationOptions;
+
+export interface TextSearchResults {
+ results: {
+ scope?: ResourceScope;
+ path: string;
+ ranges: Range | Range[];
+ preview: {
+ text: string;
+ matches: Range | Range[];
+ };
+ }[];
+ truncated: boolean;
+}
+
+export interface Position {
+ line: number;
+ character: number;
+}
+
+export interface Range {
+ start: Position;
+ end: Position;
+}
+
+// for cross-repository data,
+// reference to another repository
+export interface ResourceScope {
+ scheme: string; // github / github / bitbucket...
+ repo: string;
+ ref: string;
+}
+
+export enum CodeReviewState {
+ Open = 'Open', // icon: 🟢
+ Merged = 'Merged', // icon: 🟣
+ Closed = 'Closed', // icon: 🔴
+}
+
+export type CodeReviewsQueryOptions = { state?: CodeReviewState; creator?: string } & CommonQueryOptions;
+
+// may be a Pull Request for GitHub,
+// or a Merge Request for GitLab,
+// or a Change Request for Gerrit
+export interface CodeReview {
+ id: string;
+ title: string;
+ state: CodeReviewState;
+ creator?: string;
+ createTime: Date;
+ mergeTime: Date | null;
+ closeTime: Date | null;
+ source: string;
+ target: string;
+ sourceSha?: string;
+ targetSha?: string;
+ avatarUrl?: string;
+}
+
+export enum FileChangeStatus {
+ Added = 'added',
+ Removed = 'removed',
+ Modified = 'modified',
+ Renamed = 'renamed',
+}
+
+export interface ChangedFile {
+ scope?: ResourceScope;
+ status: FileChangeStatus;
+ path: string;
+ // only exists for renamed file
+ previousPath?: string;
+}
+
+export interface BlameRange {
+ age: number;
+ startingLine: number; // starts from 1
+ endingLine: number;
+ commit: Commit & { parents?: string[] };
+}
+
+export interface CodeLocation {
+ // we can set `scope` to marked that current
+ // result is referenced to another repository
+ scope?: ResourceScope;
+ path: string;
+ range: Range;
+}
+
+export type SymbolDefinitions = CodeLocation[];
+
+export type SymbolReferences = CodeLocation[];
+
+export type SymbolHover = { markdown: string };
+
+// All repository path parameters and return values start with '/'.
+export class DataSource {
+ // if `recursive` is true, it should try to return all subtrees
+ provideDirectory(repo: string, ref: string, path: string, recursive = false): Promisable {
+ return null;
+ }
+
+ // the ref here may be a commitSha, branch, tag, or 'HEAD'
+ provideFile(repo: string, ref: string, path: string): Promisable {
+ return null;
+ }
+
+ provideBranches(repo: string, options?: CommonQueryOptions): Promisable {
+ return [];
+ }
+
+ provideBranch(repo: string, branchName: string): Promisable {
+ return null;
+ }
+
+ provideTags(repo: string, options?: CommonQueryOptions): Promisable {
+ return [];
+ }
+
+ provideTag(repo: string, tagName: string): Promisable {
+ return null;
+ }
+
+ // use `report` to populate search results gradually
+ provideTextSearchResults(
+ repo: string,
+ ref: string,
+ query: TextSearchQuery,
+ options?: TextSearchOptions,
+ ): Promisable {
+ return { results: [], truncated: false };
+ }
+
+ // optionally return changed files (if `files` exists can reduce api calls)
+ // commits should be returned by the order of `new to old`, if `options.path` is not empty,
+ // the first commit should be the latest commit that related the `options.path` file (maybe not `ref`)
+ provideCommits(repo: string, options?: CommitsQueryOptions): Promisable<(Commit & { files?: ChangedFile[] })[]> {
+ return [];
+ }
+
+ // the ref here may be a commitSha, branch, tag, or 'HEAD'
+ // optionally return changed files (if `files` exists can reduce api calls)
+ provideCommit(repo: string, ref: string): Promisable<(Commit & { files?: ChangedFile[] }) | null> {
+ return null;
+ }
+
+ provideCommitChangedFiles(repo: string, ref: string, options?: CommonQueryOptions): Promisable {
+ return [];
+ }
+
+ // optionally return changed files (if `files` exists can reduce api calls)
+ provideCodeReviews(
+ repo: string,
+ options?: CodeReviewsQueryOptions,
+ ): Promisable<(CodeReview & { files?: ChangedFile[] })[]> {
+ return [];
+ }
+
+ // optionally return changed files (if `files` exists can reduce api calls)
+ provideCodeReview(
+ repo: string,
+ id: string,
+ ): Promisable<(CodeReview & { sourceSha: string; targetSha: string; files?: ChangedFile[] }) | null> {
+ return null;
+ }
+
+ provideCodeReviewChangedFiles(repo: string, id: string, options?: CommonQueryOptions): Promisable {
+ return [];
+ }
+
+ provideFileBlameRanges(repo: string, ref: string, path: string): Promisable {
+ return [];
+ }
+
+ provideSymbolDefinitions(
+ repo: string,
+ ref: string,
+ path: string,
+ line: number,
+ character: number,
+ symbol: string,
+ ): Promisable {
+ return [];
+ }
+
+ provideSymbolReferences(
+ repo: string,
+ ref: string,
+ path: string,
+ line: number,
+ character: number,
+ symbol: string,
+ ): Promisable {
+ return [];
+ }
+
+ provideSymbolHover(
+ repo: string,
+ ref: string,
+ path: string,
+ line: number,
+ character: number,
+ symbol: string,
+ ): Promisable {
+ return null;
+ }
+
+ provideUserAvatarLink(user: string): string {
+ return '';
+ }
+}
+
+export enum PageType {
+ // show the structure of a dictionary
+ // e.g. https://github.com/conwnet/github1s/tree/master/src/vs
+ Tree = 'Tree',
+
+ // show the content for a file
+ // e.g. https://github.com/conwnet/github1s/blob/master/extensions/github1s/src/extension.ts
+ Blob = 'Blob',
+
+ // show the commit list of a repository
+ // e.g. https://github.com/conwnet/github1s/commits/master
+ CommitList = 'CommitList',
+
+ // show the detail of a commit
+ // e.g. https://github.com/conwnet/github1s/commit/c1264f7338833c7aa3a502c4629df8aa6b7d6ccf
+ Commit = 'Commit',
+
+ // show the pull request (or merge request) list of a repository
+ // e.g. https://github.com/conwnet/github1s/pulls
+ CodeReviewList = 'CodeReviewList',
+
+ // show the detail of a pull request (or merge request)
+ // e.g. https://github.com/conwnet/github1s/pull/81
+ CodeReview = 'CodeReview',
+
+ // show the file blame
+ // e.g. https://github.com/conwnet/github1s/blame/master/.gitignore
+ FileBlame = 'FileBlame',
+
+ // show search result
+ Search = 'Search',
+
+ // branches, tags, wiki, gist should be on the way
+ Unknown = 'Unknown',
+}
+
+export type RouterState = { repo: string; ref: string } & (
+ | { pageType: PageType.Tree; filePath: string } // for tree page
+ | { pageType: PageType.Blob; filePath: string; startLine?: number; endLine?: number } // for blob page
+ | { pageType: PageType.CommitList } // for commit list page
+ | { pageType: PageType.Commit; commitSha: string } // for commit detail page
+ | { pageType: PageType.CodeReviewList } // for code review list page
+ | { pageType: PageType.CodeReview; codeReviewId: string }
+ | {
+ pageType: PageType.Search;
+ query?: string;
+ isRegex?: boolean;
+ isCaseSensitive?: boolean;
+ matchWholeWord?: boolean;
+ filesToInclude?: string;
+ filesToExclude?: string;
+ }
+);
+
+export class RouterParser {
+ // parse giving path (starts with '/', may includes search and hash) to Router state,
+ parsePath(path: string): Promisable {
+ return { repo: '', ref: 'HEAD', pageType: PageType.Tree, filePath: '/' };
+ }
+
+ // build the tree page path
+ buildTreePath(repo: string, ref?: string, filePath?: string): Promisable {
+ return '/' + repo;
+ }
+
+ // build the blob page path
+ // startLine/endLine begins from 1
+
+ buildBlobPath(repo: string, ref: string, filePath: string, startLine?: number, endLine?: number): Promisable {
+ return '/' + repo;
+ }
+ // build the commit list page path
+ buildCommitListPath(repo: string): Promisable {
+ return '/' + repo;
+ }
+
+ // build the commit page path
+ buildCommitPath(repo: string, commitSha: string): Promisable {
+ return '/' + repo;
+ }
+
+ // build the code review list page path
+ buildCodeReviewListPath(repo: string): Promisable {
+ return '/' + repo;
+ }
+
+ // build the code review page path
+ buildCodeReviewPath(repo: string, codeReviewId: string): Promisable {
+ return '/' + repo;
+ }
+
+ // convert giving path to the external link (using for jumping back to origin platform)
+ buildExternalLink(path: string): Promisable {
+ return path;
+ }
+}
+
+export enum CodeReviewType {
+ PullRequest = 'PullRequest',
+ MergeRequest = 'MergeRequest',
+ ChangeRequest = 'ChangeRequest',
+ CodeReview = 'CodeReview', // as fallback
+}
+
+export enum PlatformName {
+ GitHub = 'GitHub',
+ GitLab = 'GitLab',
+ Bitbucket = 'Bitbucket',
+ npm = 'npm',
+ OfficialPage = 'OfficialPage', // as fallback
+}
+
+export interface Adapter {
+ // specify which scheme of workspace should current adapter should work with
+ readonly scheme: string;
+ // platform name, using for displaying text such as: `open on **GitHub**`
+ readonly platformName: PlatformName;
+ // the code review type
+ readonly codeReviewType?: CodeReviewType;
+
+ resolveDataSource(): Promisable;
+ resolveRouterParser(): Promisable;
+ activateAsDefault?(): Promisable;
+ deactivateAsDefault?(): Promisable;
+}
diff --git a/extensions/github1s/src/changes/files.ts b/extensions/github1s/src/changes/files.ts
new file mode 100644
index 000000000..8ff463660
--- /dev/null
+++ b/extensions/github1s/src/changes/files.ts
@@ -0,0 +1,131 @@
+/**
+ * @file source control change file list
+ * @author netcon
+ */
+
+import * as vscode from 'vscode';
+import * as queryString from 'query-string';
+import * as adapterTypes from '@/adapters/types';
+import router from '@/router';
+import { basename } from '@/helpers/util';
+import { emptyFileUri } from '@/providers';
+import { Repository } from '@/repository';
+
+interface VSCodeChangedFile {
+ baseFileUri: vscode.Uri;
+ headFileUri: vscode.Uri;
+ status: adapterTypes.FileChangeStatus;
+}
+
+// get the change files of a codeReview
+export const getCodeReviewChangedFiles = async (
+ codeReview: adapterTypes.CodeReview & { sourceSha: string; targetSha: string },
+) => {
+ const repository = Repository.getCurrentInstance();
+ const baseRootUri = router.buildUri({ ref: codeReview.targetSha });
+ const headRootUri = router.buildUri({ ref: codeReview.sourceSha }, baseRootUri);
+ const changedFiles = await repository.getCodeReviewChangedFiles(codeReview.id);
+
+ return changedFiles.map((changedFile) => {
+ // the `previous_filename` field only exists in `RENAMED` file,
+ // fallback to `filename` otherwise
+ const baseFilePath = changedFile.previousPath || changedFile.path;
+ const headFilePath = changedFile.path;
+ return {
+ baseFileUri: baseRootUri.with({ path: baseFilePath }),
+ headFileUri: headRootUri.with({ path: headFilePath }),
+ status: changedFile.status,
+ };
+ });
+};
+
+export const getCommitChangedFiles = async (commit: adapterTypes.Commit) => {
+ const repository = Repository.getCurrentInstance();
+ // if the commit.parents is more than one element
+ // the parents[1].sha should be the merge source commitSha
+ // so we use the parents[0].sha as the parent commitSha
+ const parentCommitSha = commit?.parents?.[0] || '';
+ const baseRootUri = router.buildUri({ ref: parentCommitSha });
+ const headRootUri = router.buildUri({ ref: commit.sha }, baseRootUri);
+ const changedFiles = await repository.getCommitChangedFiles(commit.sha);
+
+ return changedFiles.map((commitFile) => {
+ // the `previous_filename` field only exists in `RENAMED` file,
+ // fallback to `filename` otherwise
+ const baseFilePath = commitFile.previousPath || commitFile.path;
+ const headFilePath = commitFile.path;
+ return {
+ baseFileUri: baseRootUri.with({ path: baseFilePath }),
+ headFileUri: headRootUri.with({ path: headFilePath }),
+ status: commitFile.status,
+ };
+ });
+};
+
+export const getChangedFiles = async (): Promise => {
+ const routerState = router.getState();
+ const repository = Repository.getCurrentInstance();
+
+ // code review page
+ if (routerState.pageType === adapterTypes.PageType.CodeReview) {
+ const codeReview = await repository.getCodeReviewItem(routerState.codeReviewId);
+ return codeReview ? getCodeReviewChangedFiles(codeReview) : [];
+ }
+ // commit page
+ else if (routerState.pageType === adapterTypes.PageType.Commit) {
+ const commit = await repository.getCommitItem(routerState.commitSha);
+ return commit ? getCommitChangedFiles(commit) : [];
+ }
+ return [];
+};
+
+// get the title of the diff editor
+export const getChangedFileDiffTitle = (
+ baseFileUri: vscode.Uri,
+ headFileUri: vscode.Uri,
+ status: adapterTypes.FileChangeStatus,
+) => {
+ const baseFileName = basename(baseFileUri.path);
+ const headFileName = basename(headFileUri.path);
+ const { ref: baseCommitSha } = router.parseUri(baseFileUri);
+ const { ref: headCommitSha } = router.parseUri(headFileUri);
+ const baseFileLabel = `${baseFileName} (${baseCommitSha?.slice(0, 7)})`;
+ const headFileLabel = `${headFileName} (${headCommitSha?.slice(0, 7)})`;
+
+ if (status === adapterTypes.FileChangeStatus.Added) {
+ return `${headFileName} (added in ${headCommitSha?.slice(0, 7)})`;
+ }
+
+ if (status === adapterTypes.FileChangeStatus.Removed) {
+ return `${baseFileName} (deleted from ${baseCommitSha?.slice(0, 7)})`;
+ }
+
+ return `${baseFileLabel} ⟷ ${headFileLabel}`;
+};
+
+export const getChangedFileDiffCommand = (changedFile: VSCodeChangedFile): vscode.Command => {
+ let baseFileUri = changedFile.baseFileUri;
+ let headFileUri = changedFile.headFileUri;
+ const status = changedFile.status;
+
+ if (status === adapterTypes.FileChangeStatus.Added) {
+ baseFileUri = emptyFileUri;
+ }
+
+ if (status === adapterTypes.FileChangeStatus.Removed) {
+ headFileUri = emptyFileUri;
+ }
+
+ const title = getChangedFileDiffTitle(baseFileUri, headFileUri, status);
+ const query = queryString.stringify({
+ status,
+ base: baseFileUri.with({ query: '' }).toString(),
+ head: headFileUri.with({ query: '' }).toString(),
+ });
+
+ return {
+ title: 'Diff',
+ command: 'vscode.diff',
+ arguments: [baseFileUri.with({ query }), headFileUri.with({ query }), title],
+ };
+};
diff --git a/extensions/github1s/src/changes/index.ts b/extensions/github1s/src/changes/index.ts
new file mode 100644
index 000000000..4a3456f8b
--- /dev/null
+++ b/extensions/github1s/src/changes/index.ts
@@ -0,0 +1,30 @@
+/**
+ * @file Source Control Changed Files
+ * @author netcon
+ */
+
+import * as vscode from 'vscode';
+import * as adapterTypes from '@/adapters/types';
+import { GitHub1sQuickDiffProvider } from './quick-diff';
+import { getChangedFileDiffCommand, getChangedFiles } from './files';
+
+export const updateSourceControlChanges = (() => {
+ const sourceControl = vscode.scm.createSourceControl('github1s', 'GitHub1s');
+ const changesGroup = sourceControl.createResourceGroup('changes', 'Changes');
+ sourceControl.quickDiffProvider = new GitHub1sQuickDiffProvider();
+
+ return async () => {
+ const changedFiles = await getChangedFiles();
+
+ changesGroup.resourceStates = changedFiles.map((changedFile) => {
+ return {
+ resourceUri: changedFile.headFileUri.with({ authority: '' }),
+ decorations: {
+ strikeThrough: changedFile.status === adapterTypes.FileChangeStatus.Removed,
+ tooltip: changedFile.status,
+ },
+ command: getChangedFileDiffCommand(changedFile),
+ };
+ });
+ };
+})();
diff --git a/extensions/github1s/src/changes/quick-diff.ts b/extensions/github1s/src/changes/quick-diff.ts
new file mode 100644
index 000000000..a2b2afc88
--- /dev/null
+++ b/extensions/github1s/src/changes/quick-diff.ts
@@ -0,0 +1,82 @@
+/**
+ * @file GitHub1s quickDiffProvider for pull/commit change files
+ * @author netcon
+ */
+
+import * as vscode from 'vscode';
+import router from '@/router';
+import { getAdapter } from '@/adapters';
+import { Repository } from '@/repository';
+import { emptyFileUri } from '@/providers';
+import * as adapterTypes from '@/adapters/types';
+
+// get the original source uri when the `routerState.pageType` is `PageType.PULL`
+const getOriginalResourceForPull = async (uri: vscode.Uri, codeReviewId: string): Promise => {
+ const repository = Repository.getCurrentInstance();
+ const codeReviewFiles = await repository.getCodeReviewChangedFiles(codeReviewId);
+ const changedFile = codeReviewFiles?.find((changedFile) => changedFile.path === uri.path);
+
+ if (
+ !changedFile ||
+ changedFile.status === adapterTypes.FileChangeStatus.Added ||
+ changedFile.status === adapterTypes.FileChangeStatus.Removed
+ ) {
+ return null;
+ }
+
+ const codeReview = await repository.getCodeReviewItem(codeReviewId);
+ if (!codeReview?.targetSha) {
+ return null;
+ }
+
+ return router.buildUri({
+ ref: codeReview.targetSha,
+ path: changedFile.previousPath || uri.path,
+ });
+};
+
+// get the original source uri when the `routerState.pageType` is `PageType.COMMIT`
+const getOriginalResourceForCommit = async (uri: vscode.Uri, commitSha: string) => {
+ const repository = Repository.getCurrentInstance();
+ const commitFiles = await repository.getCommitChangedFiles(commitSha);
+ const changedFile = commitFiles?.find((changedFile) => changedFile.path === uri.path);
+
+ if (
+ !changedFile ||
+ changedFile.status === adapterTypes.FileChangeStatus.Added ||
+ changedFile.status === adapterTypes.FileChangeStatus.Removed
+ ) {
+ return null;
+ }
+
+ const commit = await repository.getCommitItem(commitSha);
+ const parentCommitSha = commit?.parents?.[0];
+ if (!parentCommitSha) {
+ return emptyFileUri;
+ }
+
+ return router.buildUri({
+ ref: parentCommitSha,
+ path: changedFile.previousPath || uri.path,
+ });
+};
+
+export class GitHub1sQuickDiffProvider implements vscode.QuickDiffProvider {
+ provideOriginalResource(uri: vscode.Uri, _token: vscode.CancellationToken): vscode.ProviderResult {
+ const routerState = router.getState();
+ // only the file belong to current workspace could be provided a quick diff
+ if (uri.scheme !== getAdapter().scheme || uri.authority) {
+ return null;
+ }
+
+ if (routerState.pageType === adapterTypes.PageType.CodeReview) {
+ return getOriginalResourceForPull(uri, routerState.codeReviewId);
+ }
+
+ if (routerState.pageType === adapterTypes.PageType.Commit) {
+ return getOriginalResourceForCommit(uri, routerState.commitSha);
+ }
+
+ return null;
+ }
+}
diff --git a/extensions/github1s/src/commands/blame.ts b/extensions/github1s/src/commands/blame.ts
index 21f38237f..967228a3e 100644
--- a/extensions/github1s/src/commands/blame.ts
+++ b/extensions/github1s/src/commands/blame.ts
@@ -6,12 +6,11 @@
import * as vscode from 'vscode';
import { relativeTimeTo } from '@/helpers/date';
import { last } from '@/helpers/util';
-import { hasValidToken } from '@/helpers/context';
import { setVSCodeContext } from '@/helpers/vscode';
import router from '@/router';
-import repository from '@/repository';
-import { BlameRange } from '@/repository/types';
-import { showFileBlameAuthorizedRequiredMessage } from '@/messages';
+import { getAdapter } from '@/adapters';
+import { Repository } from '@/repository';
+import { BlameRange, PlatformName } from '@/adapters/types';
const ageColors = [
'#f66a0a',
@@ -27,13 +26,31 @@ const ageColors = [
'#0a60f6',
];
-const createCommitMessagePreviewMarkdown = (blameRange: BlameRange) => {
+const openOnOfficialPageCommand = {
+ [PlatformName.GitHub]: {
+ command: 'github1s.commands.openCommitOnGitHub',
+ title: 'Open on GitHub',
+ },
+ [PlatformName.GitLab]: {
+ command: 'github1s.commands.openCommitOnGitLab',
+ title: 'Open on GitLab',
+ },
+ [PlatformName.Bitbucket]: {
+ command: 'github1s.commands.openCommitOnBitbucket',
+ title: 'Open on Bitbucket',
+ },
+ [PlatformName.OfficialPage]: {
+ command: 'github1s.commands.openCommitOnOfficialPage',
+ title: 'Open on Official Page',
+ },
+};
+
+const createCommitMessagePreviewMarkdown = (blameRange: BlameRange, platformName: PlatformName) => {
const commit = blameRange.commit;
- const commitAuthor = blameRange.commit.author;
- const messageTextLines = [];
+ const messageTextLines: string[] = [];
messageTextLines.push(
- ` [${commitAuthor.name}](mailto:${commit.author.email}), ${relativeTimeTo(commit.authoredDate)} (*${commit.authoredDate}*)` // prettier-ignore
+ ` [${commit.author}](mailto:${commit.email}), ${relativeTimeTo(commit.createTime)} (*${commit.createTime}*)`, // prettier-ignore
);
messageTextLines.push(`Commit ID: ${commit.sha}`);
@@ -41,16 +58,14 @@ const createCommitMessagePreviewMarkdown = (blameRange: BlameRange) => {
messageTextLines.push(`~~~\n${commit.message}\n~~~`);
messageTextLines.push('---');
- const switchToCommitCommandText = `command:github1s.switch-to-commit?${encodeURIComponent(JSON.stringify([commit.sha]))}`; // prettier-ignore
- const openOnGitHubCommandText = `command:github1s.open-commit-on-github?${encodeURIComponent(JSON.stringify([commit.sha]))}`; // prettier-ignore
+ const commandConfig = openOnOfficialPageCommand[platformName];
+ const switchToCommitCommandText = `command:github1s.commands.switchToCommit?${encodeURIComponent(JSON.stringify([commit.sha]))}`; // prettier-ignore
+ const openOnGitHubCommandText = `command:${commandConfig.command}?${encodeURIComponent(JSON.stringify([commit.sha]))}`; // prettier-ignore
messageTextLines.push(
- `[$(log-in) Switch to Commit](${switchToCommitCommandText}) | [$(globe) Open on GitHub](${openOnGitHubCommandText})`
+ `[$(log-in) Switch to Commit](${switchToCommitCommandText}) | [$(globe) ${commandConfig.title}](${openOnGitHubCommandText})`,
);
- const markdownString = new vscode.MarkdownString(
- messageTextLines.join('\n\n'),
- true
- );
+ const markdownString = new vscode.MarkdownString(messageTextLines.join('\n\n'), true);
markdownString.isTrusted = true;
return markdownString;
};
@@ -81,9 +96,7 @@ const commonLineDecorationTypeOptions = {
z-index: -1;
text-align: right;
padding-right: 6px`,
- backgroundColor: new vscode.ThemeColor(
- 'github1s.colors.gutterBlameBackground'
- ),
+ backgroundColor: new vscode.ThemeColor('github1s.colors.gutterBlameBackground'),
},
};
@@ -92,12 +105,8 @@ const commonLineDecorationTypeOptions = {
const createSelectedLineDecorationType = () =>
vscode.window.createTextEditorDecorationType({
isWholeLine: true,
- overviewRulerColor: new vscode.ThemeColor(
- 'github1s.colors.highlightGutterBlameBackground'
- ),
- backgroundColor: new vscode.ThemeColor(
- 'github1s.colors.highlightGutterBlameBackground'
- ),
+ overviewRulerColor: new vscode.ThemeColor('github1s.colors.highlightGutterBlameBackground'),
+ backgroundColor: new vscode.ThemeColor('github1s.colors.highlightGutterBlameBackground'),
});
// the decoration type for the first line of **a blame block**
@@ -110,14 +119,13 @@ const createFirstLineDecorationType = (blameRange: BlameRange) => {
text-overflow: ellipsis;
vertical-align: bottom;
text-indent: 2em;
- background-image: url(${encodeURI(blameRange.commit.author.avatarUrl)});
+ background-image: url(${encodeURI(blameRange.commit?.avatarUrl || '')});
background-size: auto 95%;
background-position: 0 50%;
background-repeat: no-repeat`;
// set the split line with other blame blocks
const firstLineAfterTextDecorationCss =
- commonLineDecorationTypeOptions.after.textDecoration +
- `; box-shadow: 0 -1px 0 rgba(0, 0, 0, .3)`;
+ commonLineDecorationTypeOptions.after.textDecoration + `; box-shadow: 0 -1px 0 rgba(0, 0, 0, .3)`;
return vscode.window.createTextEditorDecorationType({
...commonLineDecorationTypeOptions,
@@ -126,11 +134,11 @@ const createFirstLineDecorationType = (blameRange: BlameRange) => {
contentText: blameRange.commit.message,
color: new vscode.ThemeColor('foreground'),
textDecoration: firstLineBeforeTextDecorationCss,
- borderColor: ageColors[blameRange.age || 10],
+ borderColor: ageColors[blameRange.age % 11 || 10],
},
after: {
...commonLineDecorationTypeOptions.after,
- contentText: relativeTimeTo(blameRange.commit.authoredDate),
+ contentText: relativeTimeTo(blameRange.commit.createTime),
color: new vscode.ThemeColor('descriptionForeground'),
textDecoration: firstLineAfterTextDecorationCss,
},
@@ -143,7 +151,7 @@ const createRestLinesDecorationType = (blameRange: BlameRange) => {
...commonLineDecorationTypeOptions,
before: {
...commonLineDecorationTypeOptions.before,
- borderColor: ageColors[blameRange.age || 10],
+ borderColor: ageColors[blameRange.age % 11 || 10],
},
});
};
@@ -151,17 +159,14 @@ const createRestLinesDecorationType = (blameRange: BlameRange) => {
// create decoration option for each line of a blame block
const createLineDecorationOptions = (
blameRange: BlameRange,
- hoverMessage?: vscode.MarkdownString | string
+ hoverMessage?: vscode.MarkdownString | string,
): vscode.DecorationOptions[] => {
- const decorationOptions = [];
+ const decorationOptions: vscode.DecorationOptions[] = [];
const { startingLine, endingLine } = blameRange;
for (let lineIndex = startingLine - 1; lineIndex < endingLine; lineIndex++) {
decorationOptions.push({
- range: new vscode.Range(
- new vscode.Position(lineIndex, 0),
- new vscode.Position(lineIndex, 0)
- ),
+ range: new vscode.Range(new vscode.Position(lineIndex, 0), new vscode.Position(lineIndex, 0)),
hoverMessage,
});
}
@@ -169,7 +174,7 @@ const createLineDecorationOptions = (
};
class EditorGitBlame {
- private static instanceMap = new WeakMap();
+ private static instanceMap = new WeakMap();
private opening: boolean = false; // if the editor blame is showing now
private refreshDisposables: vscode.Disposable[] = [];
private selectionDisposables: vscode.Disposable[] = [];
@@ -182,48 +187,38 @@ class EditorGitBlame {
});
}
- public static getInstance(editor) {
+ public static getInstance(editor: vscode.TextEditor) {
if (!EditorGitBlame.instanceMap.has(editor)) {
EditorGitBlame.instanceMap.set(editor, new EditorGitBlame(editor));
}
- return EditorGitBlame.instanceMap.get(editor);
+ return EditorGitBlame.instanceMap.get(editor)!;
}
- async getBlameRanges() {
- const filePath = this.editor.document?.uri.path;
- const fileAuthority =
- this.editor.document?.uri.authority || (await router.getAuthority());
- const [_owner, _repo, ref] = fileAuthority.split('+').filter(Boolean);
- return filePath ? repository.getFileBlame(filePath, ref) : [];
+ async getBlameRanges(): Promise {
+ const { scheme, repo, ref, path } = router.parseUri(this.editor.document?.uri);
+ const repository = Repository.getInstance(scheme, repo);
+ return path.length > 1 ? repository.getFileBlameRanges(ref, path) : [];
}
async open() {
this.refreshDisposables.forEach((disposable) => disposable.dispose());
- setVSCodeContext('github1s.context.gutterBlameOpening', true);
+ setVSCodeContext('github1s:features:gutterBlame:open', true);
+ const { scheme } = router.parseUri(this.editor.document.uri);
+ const platformName = getAdapter(scheme).platformName;
(await this.getBlameRanges()).forEach((blameRange) => {
- const hoverMessage = createCommitMessagePreviewMarkdown(blameRange);
+ const hoverMessage = createCommitMessagePreviewMarkdown(blameRange, platformName);
const firstLineDecorationType = createFirstLineDecorationType(blameRange);
- const lineDecorationOptions = createLineDecorationOptions(
- blameRange,
- hoverMessage
- );
+ const lineDecorationOptions = createLineDecorationOptions(blameRange, hoverMessage);
this.refreshDisposables.push(firstLineDecorationType);
- this.editor.setDecorations(firstLineDecorationType, [
- lineDecorationOptions[0],
- ]);
+ this.editor.setDecorations(firstLineDecorationType, [lineDecorationOptions[0]]);
if (lineDecorationOptions.length > 1) {
- const restLinesDecorationType = createRestLinesDecorationType(
- blameRange
- );
+ const restLinesDecorationType = createRestLinesDecorationType(blameRange);
this.refreshDisposables.push(restLinesDecorationType);
- this.editor.setDecorations(
- restLinesDecorationType,
- lineDecorationOptions.slice(1)
- );
+ this.editor.setDecorations(restLinesDecorationType, lineDecorationOptions.slice(1));
}
});
this.opening = true;
@@ -234,15 +229,11 @@ class EditorGitBlame {
const blameRanges = await this.getBlameRanges();
const selectedCommitSha = blameRanges.find(
- (blameRange) =>
- blameRange.startingLine - 1 <= lineIndex &&
- blameRange.endingLine - 1 >= lineIndex
+ (blameRange) => blameRange.startingLine - 1 <= lineIndex && blameRange.endingLine - 1 >= lineIndex,
)?.commit.sha;
- const selectedBlameRanges = blameRanges.filter(
- (blameRange) => blameRange.commit.sha === selectedCommitSha
- );
+ const selectedBlameRanges = blameRanges.filter((blameRange) => blameRange.commit.sha === selectedCommitSha);
const decorationType = createSelectedLineDecorationType();
- const decorationOptions = [];
+ const decorationOptions: vscode.DecorationOptions[] = [];
this.selectionDisposables.push(decorationType);
selectedBlameRanges.forEach((blameRange) => {
@@ -252,7 +243,7 @@ class EditorGitBlame {
}
close() {
- setVSCodeContext('github1s.context.gutterBlameOpening', false);
+ setVSCodeContext('github1s:features:gutterBlame:open', false);
this.refreshDisposables.forEach((disposable) => disposable.dispose());
this.selectionDisposables.forEach((disposable) => disposable.dispose());
this.opening = false;
@@ -263,28 +254,28 @@ class EditorGitBlame {
}
}
-const ensureUserAuthorized = () => {
- if (hasValidToken()) {
- return true;
- }
- showFileBlameAuthorizedRequiredMessage();
- return false;
-};
-
-export const commandToggleEditorGutterBlame = () => {
- if (ensureUserAuthorized() && vscode.window.activeTextEditor) {
+const commandToggleEditorGutterBlame = () => {
+ if (vscode.window.activeTextEditor) {
return EditorGitBlame.getInstance(vscode.window.activeTextEditor).toggle();
}
};
-export const commandOpenEditorGutterBlame = () => {
- if (ensureUserAuthorized() && vscode.window.activeTextEditor) {
+const commandOpenEditorGutterBlame = () => {
+ if (vscode.window.activeTextEditor) {
return EditorGitBlame.getInstance(vscode.window.activeTextEditor).open();
}
};
-export const commandCloseEditorGutterBlame = () => {
- if (ensureUserAuthorized() && vscode.window.activeTextEditor) {
+const commandCloseEditorGutterBlame = () => {
+ if (vscode.window.activeTextEditor) {
return EditorGitBlame.getInstance(vscode.window.activeTextEditor).close();
}
};
+
+export const registerBlameCommands = (context: vscode.ExtensionContext) => {
+ return context.subscriptions.push(
+ vscode.commands.registerCommand('github1s.commands.toggleEditorGutterBlame', commandToggleEditorGutterBlame),
+ vscode.commands.registerCommand('github1s.commands.openEditorGutterBlame', commandOpenEditorGutterBlame),
+ vscode.commands.registerCommand('github1s.commands.closeEditorGutterBlame', commandCloseEditorGutterBlame),
+ );
+};
diff --git a/extensions/github1s/src/commands/code-review.ts b/extensions/github1s/src/commands/code-review.ts
new file mode 100644
index 000000000..8664749d6
--- /dev/null
+++ b/extensions/github1s/src/commands/code-review.ts
@@ -0,0 +1,147 @@
+/**
+ * @file GitHub1s Code Review Related Commands
+ * @author netcon
+ */
+
+import * as vscode from 'vscode';
+import router from '@/router';
+import { getAdapter } from '@/adapters';
+import {
+ CodeReviewTreeItem,
+ getCodeReviewTreeItemLabel,
+ getCodeReviewTreeItemDescription,
+} from '@/views/code-review-list';
+import { codeReviewRequestTreeDataProvider } from '@/views';
+import { CodeReviewType } from '@/adapters/types';
+import { Repository } from '@/repository';
+
+const CodeReviewTypeName = {
+ [CodeReviewType.PullRequest]: 'pull request',
+ [CodeReviewType.MergeRequest]: 'merge request',
+ [CodeReviewType.ChangeRequest]: 'change request',
+ [CodeReviewType.CodeReview]: 'code review',
+};
+
+const checkCodeReviewExists = async (repo: string, codeReviewId: string) => {
+ const adapter = getAdapter();
+ const dataSoruce = await adapter.resolveDataSource();
+ try {
+ return !!(await dataSoruce.provideCodeReview(repo, codeReviewId));
+ } catch (error) {
+ const typeName = CodeReviewTypeName[adapter.codeReviewType || CodeReviewType.CodeReview];
+ const errorMessage =
+ (error as any)?.response?.status === 404
+ ? `No ${typeName} found for id: ${codeReviewId}`
+ : error?.response?.data?.message;
+ vscode.window.showErrorMessage(errorMessage || `Get ${typeName} ${codeReviewId} error`);
+ return false;
+ }
+};
+
+const commandSwitchToCodeReview = async (codeReviewItemOrId?: string | CodeReviewTreeItem) => {
+ let codeReviewId: string | undefined = codeReviewItemOrId
+ ? typeof codeReviewItemOrId === 'string'
+ ? codeReviewItemOrId
+ : codeReviewItemOrId.codeReview.id
+ : '';
+ const adapter = getAdapter();
+ const { repo } = router.getState();
+ const repository = Repository.getCurrentInstance();
+ const typeName = CodeReviewTypeName[adapter.codeReviewType || CodeReviewType.CodeReview];
+
+ // if the a codeReviewId isn't provided, use quickInput
+ if (!codeReviewId) {
+ // manual input a codeReviewId
+ const inputCodeReviewIdItem: vscode.QuickPickItem = {
+ label: `$(git-pull-request) Manual input the ${typeName} id`,
+ alwaysShow: true,
+ };
+ // use the code review list as the candidates
+ const codeReviews = await repository.getCodeReviewList();
+ const codeReviewItems: vscode.QuickPickItem[] = codeReviews.map((codeReview) => ({
+ codeReviewId: codeReview.id,
+ label: getCodeReviewTreeItemLabel(codeReview),
+ description: getCodeReviewTreeItemDescription(codeReview),
+ }));
+
+ const quickPick = vscode.window.createQuickPick();
+ quickPick.matchOnDescription = true;
+ quickPick.items = [inputCodeReviewIdItem, ...codeReviewItems];
+ quickPick.show();
+
+ const choice = (await new Promise((resolve) =>
+ quickPick.onDidAccept(() => resolve(quickPick.activeItems[0])),
+ )) as vscode.QuickPickItem & { codeReviewId?: string };
+ quickPick.hide();
+
+ // select nothing
+ if (!choice) {
+ return;
+ }
+
+ // select `manual input the code review id`
+ if (choice === inputCodeReviewIdItem) {
+ codeReviewId = await vscode.window.showInputBox({
+ placeHolder: `Please input the ${typeName} id`,
+ });
+ } else {
+ // select a code review item
+ codeReviewId = choice.codeReviewId;
+ }
+ }
+
+ const routerParser = router.getParser();
+ (await checkCodeReviewExists(repo, codeReviewId!)) &&
+ router.replace(await routerParser.buildCodeReviewPath(repo, codeReviewId!));
+};
+
+// this command is used in `source control code review view`
+const commandOpenCodeReviewOnOfficialPage = async (codeReviewItemOrId?: string | CodeReviewTreeItem) => {
+ const codeReviewId: string | undefined = codeReviewItemOrId
+ ? typeof codeReviewItemOrId === 'string'
+ ? codeReviewItemOrId
+ : codeReviewItemOrId.codeReview.id
+ : '';
+ if (codeReviewId) {
+ const { repo } = router.getState();
+ const routerParser = router.getParser();
+ const codeReviewPath = await routerParser.buildCodeReviewPath(repo, codeReviewId);
+ const codeReviewLink = await routerParser.buildExternalLink(codeReviewPath);
+ return vscode.commands.executeCommand('vscode.open', vscode.Uri.parse(codeReviewLink));
+ }
+};
+
+const commandRefreshCodeReviewList = (forceUpdate = true) => {
+ return codeReviewRequestTreeDataProvider.updateTree(forceUpdate);
+};
+
+const commandLoadMoreCodeReviews = async () => {
+ return codeReviewRequestTreeDataProvider.loadMoreCodeReviews();
+};
+
+const commandLoadMoreCodeReviewChangedFiles = async (codeReviewId: string) => {
+ return codeReviewRequestTreeDataProvider.loadMoreChangedFiles(codeReviewId);
+};
+
+export const registerCodeReviewCommands = (context: vscode.ExtensionContext) => {
+ return context.subscriptions.push(
+ vscode.commands.registerCommand('github1s.commands.refreshCodeReviewList', commandRefreshCodeReviewList),
+ vscode.commands.registerCommand('github1s.commands.searchCodeReview', commandSwitchToCodeReview),
+ vscode.commands.registerCommand('github1s.commands.switchToPullRequest', commandSwitchToCodeReview),
+ vscode.commands.registerCommand('github1s.commands.switchToMergeRequest', commandSwitchToCodeReview),
+ vscode.commands.registerCommand('github1s.commands.switchToChangeRequest', commandSwitchToCodeReview),
+ vscode.commands.registerCommand('github1s.commands.switchToCodeReview', commandSwitchToCodeReview),
+ vscode.commands.registerCommand('github1s.commands.openCodeReviewOnGitHub', commandOpenCodeReviewOnOfficialPage),
+ vscode.commands.registerCommand('github1s.commands.openCodeReviewOnGitLab', commandOpenCodeReviewOnOfficialPage),
+ vscode.commands.registerCommand('github1s.commands.openCodeReviewOnBitbucket', commandOpenCodeReviewOnOfficialPage),
+ vscode.commands.registerCommand(
+ 'github1s.commands.openCodeReviewOnOfficialPage',
+ commandOpenCodeReviewOnOfficialPage,
+ ),
+ vscode.commands.registerCommand('github1s.commands.load-more-code-reviews', commandLoadMoreCodeReviews),
+ vscode.commands.registerCommand(
+ 'github1s.commands.load-more-code-review-changed-files',
+ commandLoadMoreCodeReviewChangedFiles,
+ ),
+ );
+};
diff --git a/extensions/github1s/src/commands/commit.ts b/extensions/github1s/src/commands/commit.ts
index 289ca6e27..97e3df402 100644
--- a/extensions/github1s/src/commands/commit.ts
+++ b/extensions/github1s/src/commands/commit.ts
@@ -5,29 +5,33 @@
import * as vscode from 'vscode';
import router from '@/router';
-import repository from '@/repository';
-import {
- CommitTreeItem,
- getCommitTreeItemDescription,
-} from '@/views/commit-list-view';
-import { commitTreeDataProvider } from '@/views';
-import { RequestNotFoundError } from '@/helpers/fetch';
-
-const checkCommitExists = async (commitSha: string) => {
+import { getAdapter } from '@/adapters';
+import { Repository } from '@/repository';
+import { CommitTreeItem, getCommitTreeItemDescription } from '@/views/commit-list';
+import { commitTreeDataProvider, fileHistoryTreeDataProvider } from '@/views';
+
+export const checkCommitExists = async (repo: string, commitSha: string) => {
+ const dataSoruce = await getAdapter().resolveDataSource();
try {
- return !!(await repository.getCommitManager().getItem(commitSha));
- } catch (e) {
- vscode.window.showErrorMessage(
- e instanceof RequestNotFoundError
- ? `No commit found for SHA: ${commitSha}`
- : e.message
- );
+ return !!(await dataSoruce.provideCommit(repo, commitSha));
+ } catch (error) {
+ const errorMessage =
+ (error as any)?.response?.status === 404
+ ? `No commit found for commitSha: ${commitSha}`
+ : error?.response?.data?.message;
+ vscode.window.showErrorMessage(errorMessage || `Get commit ${commitSha}} error`);
return false;
}
};
-export const commandSwitchToCommit = async (commitSha?: string) => {
- const { owner, repo } = await router.getState();
+const commandSwitchToCommit = async (commitItemOrSha?: string | CommitTreeItem) => {
+ let commitSha: string | undefined = commitItemOrSha
+ ? typeof commitItemOrSha === 'string'
+ ? commitItemOrSha
+ : commitItemOrSha.commit.sha
+ : '';
+ const { repo } = router.getState();
+ const repository = Repository.getCurrentInstance();
// if the a commitSha isn't provided, use quickInput
if (!commitSha) {
@@ -37,11 +41,10 @@ export const commandSwitchToCommit = async (commitSha?: string) => {
alwaysShow: true,
};
// use the commit list as the candidates
- const commitItems: vscode.QuickPickItem[] = (
- await repository.getCommitManager().getList((await router.getState()).ref)
- ).map((commit) => ({
+ const commits = await repository.getCommitList();
+ const commitItems: vscode.QuickPickItem[] = commits.map((commit) => ({
commitSha: commit.sha,
- label: commit.commit.message,
+ label: commit.message,
description: getCommitTreeItemDescription(commit),
}));
@@ -50,9 +53,8 @@ export const commandSwitchToCommit = async (commitSha?: string) => {
quickPick.items = [inputCommitShaItem, ...commitItems];
quickPick.show();
- const choice = (await new Promise(
- (resolve) =>
- quickPick.onDidAccept(() => resolve(quickPick.activeItems[0]))
+ const choice = (await new Promise((resolve) =>
+ quickPick.onDidAccept(() => resolve(quickPick.activeItems[0])),
)) as vscode.QuickPickItem & { commitSha?: string };
quickPick.hide();
@@ -72,39 +74,85 @@ export const commandSwitchToCommit = async (commitSha?: string) => {
}
}
- (await checkCommitExists(commitSha)) &&
- router.replace(`/${owner}/${repo}/commit/${commitSha}`);
-};
-
-// this command is used in `source control commits view`
-export const commandCommitViewItemSwitchToCommit = (
- viewItem: CommitTreeItem
-) => {
- return commandSwitchToCommit(viewItem?.commit?.sha);
+ const routerParser = router.getParser();
+ if (await checkCommitExists(repo, commitSha!)) {
+ router.replace(await routerParser.buildCommitPath(repo, commitSha!));
+ }
};
-export const commandOpenCommitOnGitHub = async (commitSha: string) => {
- const { owner, repo } = await router.getState();
- return vscode.commands.executeCommand(
- 'vscode.open',
- vscode.Uri.parse(`https://github.com/${owner}/${repo}/commit/${commitSha}`)
- );
+const commandDiffCommitFile = async (commitItem: CommitTreeItem) => {
+ const commitSha = commitItem.commit.sha;
+ if (!commitSha) {
+ return;
+ }
+ const activeDocumentUri = vscode.window.activeTextEditor?.document?.uri;
+ if (!activeDocumentUri) {
+ return;
+ }
+ const fileUri = router.buildUri({ ref: commitSha }, activeDocumentUri).with({ query: '' });
+ return vscode.commands.executeCommand('github1s.commands.openFilePreviousRevision', fileUri);
};
// this command is used in `source control commit list view`
-export const commandCommitViewItemOpenOnGitHub = async (
- viewItem: CommitTreeItem
-) => {
- const commitSha = viewItem?.commit?.sha;
- commitSha && commandOpenCommitOnGitHub(commitSha);
+const commandOpenCommitOnOfficialPage = async (commitItemOrSha?: string | CommitTreeItem) => {
+ const commitSha = commitItemOrSha
+ ? typeof commitItemOrSha === 'string'
+ ? commitItemOrSha
+ : commitItemOrSha.commit.sha
+ : '';
+ if (commitSha) {
+ const { repo } = router.getState();
+ const routerParser = router.getParser();
+ const commitPath = await routerParser.buildCommitPath(repo, commitSha);
+ const commitLink = await routerParser.buildExternalLink(commitPath);
+ return vscode.commands.executeCommand('vscode.open', vscode.Uri.parse(commitLink));
+ }
};
-export const commandCommitViewRefreshCommitList = (forceUpdate = true) => {
+const commandRefreshCommitList = (forceUpdate = true) => {
return commitTreeDataProvider.updateTree(forceUpdate);
};
-export const commandCommitViewLoadMoreCommits = async () => {
- const { ref } = await router.getState();
- repository.getCommitManager().loadMore(ref);
- return commandCommitViewRefreshCommitList(false);
+const commandLoadMoreCommits = async () => {
+ return commitTreeDataProvider.loadMoreCommits();
+};
+
+const commandLoadMoreCommitChangedFiles = async (commitSha: string) => {
+ return commitTreeDataProvider.loadMoreChangedFiles(commitSha);
+};
+
+const commandRefreshFileHistoryCommitList = (forceUpdate = true) => {
+ return fileHistoryTreeDataProvider.updateTree(forceUpdate);
+};
+
+const commandLoadMoreFileHistoryCommits = async () => {
+ return fileHistoryTreeDataProvider.loadMoreCommits();
+};
+
+const commandLoadMoreFileHistoryCommitChangedFiles = async (commitSha: string) => {
+ return fileHistoryTreeDataProvider.loadMoreChangedFiles(commitSha);
+};
+
+export const registerCommitCommands = (context: vscode.ExtensionContext) => {
+ return context.subscriptions.push(
+ vscode.commands.registerCommand('github1s.commands.refreshCommitList', commandRefreshCommitList),
+ vscode.commands.registerCommand('github1s.commands.searchCommit', commandSwitchToCommit),
+ vscode.commands.registerCommand('github1s.commands.switchToCommit', commandSwitchToCommit),
+ vscode.commands.registerCommand('github1s.commands.diffCommitFile', commandDiffCommitFile),
+ vscode.commands.registerCommand('github1s.commands.openCommitOnGitHub', commandOpenCommitOnOfficialPage),
+ vscode.commands.registerCommand('github1s.commands.openCommitOnGitLab', commandOpenCommitOnOfficialPage),
+ vscode.commands.registerCommand('github1s.commands.openCommitOnBitbucket', commandOpenCommitOnOfficialPage),
+ vscode.commands.registerCommand('github1s.commands.openCommitOnOfficialPage', commandOpenCommitOnOfficialPage),
+ vscode.commands.registerCommand('github1s.commands.loadMoreCommits', commandLoadMoreCommits),
+ vscode.commands.registerCommand('github1s.commands.loadMoreCommitChangedFiles', commandLoadMoreCommitChangedFiles),
+ vscode.commands.registerCommand('github1s.commands.loadMoreFileHistoryCommits', commandLoadMoreFileHistoryCommits),
+ vscode.commands.registerCommand(
+ 'github1s.commands.loadMoreFileHistoryCommitChangedFiles',
+ commandLoadMoreFileHistoryCommitChangedFiles,
+ ),
+ vscode.commands.registerCommand(
+ 'github1s.commands.refreshFileHistoryCommitList',
+ commandRefreshFileHistoryCommitList,
+ ),
+ );
};
diff --git a/extensions/github1s/src/commands/editor.ts b/extensions/github1s/src/commands/editor.ts
index de10e62f7..2eeeb9d94 100644
--- a/extensions/github1s/src/commands/editor.ts
+++ b/extensions/github1s/src/commands/editor.ts
@@ -6,20 +6,12 @@
import * as vscode from 'vscode';
import * as queryString from 'query-string';
import router from '@/router';
-import repository from '@/repository';
import { emptyFileUri } from '@/providers';
-import { basename } from '@/helpers/util';
-import {
- ChangedFile,
- getChangedFiles,
- getChangedFileCommand,
- getChangedFileDiffTitle,
-} from '@/source-control/changes';
-import { FileChangeType } from '@/repository/types';
-
-export const getChangedFileFromSourceControl = async (
- fileUri: vscode.Uri
-): Promise => {
+import { FileChangeStatus } from '@/adapters/types';
+import { Repository } from '@/repository';
+import { getChangedFiles, getChangedFileDiffCommand, getChangedFileDiffTitle } from '@/changes/files';
+
+export const getChangedFileFromSourceControl = async (fileUri: vscode.Uri) => {
// the file should belong to current workspace
if (fileUri.authority) {
return;
@@ -32,108 +24,60 @@ export const getChangedFileFromSourceControl = async (
// open the diff editor of a file, such as click it in source-control-panel,
// only work when we can found the corresponding file in source-control-panel
-export const commandEditorViewOpenChanges = async (fileUri: vscode.Uri) => {
+const commandDiffChangedFile = async (fileUri: vscode.Uri) => {
const changedFile = await getChangedFileFromSourceControl(fileUri);
if (!changedFile) {
return;
}
- const command = await getChangedFileCommand(changedFile);
- vscode.commands.executeCommand(command.command, ...command.arguments);
+ const command = await getChangedFileDiffCommand(changedFile);
+ vscode.commands.executeCommand(command.command, ...(command.arguments || []));
};
const openFileToEditor = async (fileUri) => {
- const isCurrentAuthority =
- fileUri.authority === (await router.getAuthority());
-
- // In order to make the file explorer focus corresponding file when
- // the `fileUri.authority` equals `current authority`, set the
- // `fileUri.authority` to '' in this case
- const targetFileUri = isCurrentAuthority
- ? fileUri.with({ authority: '' })
- : fileUri;
-
- let editorLabel = undefined;
- if (!isCurrentAuthority) {
- // the authority here should be `{owner}+{repo}+{commitSha}`
- const [_owner, _repo, commitSha] = targetFileUri.authority.split('+');
- editorLabel = `${basename(targetFileUri.path)} (${commitSha.slice(0, 7)})`;
- }
-
- return vscode.commands.executeCommand(
- 'vscode.open',
- targetFileUri,
- { preview: false },
- editorLabel
- );
+ return vscode.commands.executeCommand('vscode.open', fileUri, { preview: false });
};
// open the left file in the diff editor title
-export const commandDiffViewOpenLeftFile = async (fileUri: vscode.Uri) => {
+const commandDiffViewOpenLeftFile = async (fileUri: vscode.Uri) => {
const query = queryString.parse(fileUri?.query || '');
- if (!query.base) {
- return;
- }
- return openFileToEditor(vscode.Uri.parse(query.base as string));
+ return query.base ? openFileToEditor(vscode.Uri.parse(query.base as string)) : null;
};
// open the right file in the diff editor title
-export const commandDiffViewOpenRightFile = async (fileUri: vscode.Uri) => {
+const commandDiffViewOpenRightFile = async (fileUri: vscode.Uri) => {
const query = queryString.parse(fileUri?.query || '');
- if (!query.head) {
- return;
- }
- return openFileToEditor(vscode.Uri.parse(query.head as string));
+ return query.head ? openFileToEditor(vscode.Uri.parse(query.head as string)) : null;
};
-// get the file uri with the latest commit, the `ref` in
+// get the file uri with the concrete commit sha, the `ref` in
// `fileUri.authority` maybe newer but not related this file
-const getLatestFileUri = async (fileUri: vscode.Uri) => {
- // the `fileUri.authority` maybe empty, fallback
- // to router.getAuthority() in this case
- const fileAuthority = fileUri.authority || (await router.getAuthority());
- const [owner, repo, ref] = fileAuthority.split('+').filter(Boolean);
- const latestCommitSha = await repository
- .getCommitManager()
- .getFileCommitSha(fileUri.path, ref);
-
- return fileUri.with({
- authority: `${owner}+${repo}+${latestCommitSha}`,
- });
+const getConcreteFileUri = async (fileUri: vscode.Uri) => {
+ const { scheme, repo, ref, path } = router.parseUri(fileUri);
+ const repository = Repository.getInstance(scheme, repo);
+ const commit = await repository.getFileLatestCommit(ref, path);
+ const latestCommitSha = commit?.sha || (await repository.getCommitItem(ref))?.sha;
+
+ return router.buildUri({ ref: latestCommitSha }, fileUri);
};
// show the file's diff between current commit and previous commit
-export const commandEditorViewOpenPrevRevision = async (
- fileUri: vscode.Uri
-) => {
+const commandOpenFilePreviousRevision = async (fileUri: vscode.Uri) => {
const queryBaseUriStr = queryString.parse(fileUri.query).base;
- const rightFileUri = await getLatestFileUri(
- // if the `queryBaseUriStr` is empty, which means this command
- // is called from a normal file editor (not a diff editor),
- // just use `fileUri` in this case
- queryBaseUriStr ? vscode.Uri.parse(queryBaseUriStr as string) : fileUri
+ const rightFileUri = await getConcreteFileUri(
+ // if the `queryBaseUriStr` is empty, which means this command is called from
+ // a normal file editor (not a diff editor), just use `fileUri` in this case
+ queryBaseUriStr ? vscode.Uri.parse(queryBaseUriStr as string) : fileUri,
);
+ const { scheme, repo, ref: rightCommitSha } = router.parseUri(rightFileUri);
+ const repository = Repository.getInstance(scheme, repo);
+ const leftCommit = await repository.getPreviousCommit(rightCommitSha, rightFileUri.path);
+ // if we can't find previous commit, use the `emptyFileUri` as the leftFileUri
+ const leftFileUri = leftCommit ? router.buildUri({ ref: leftCommit.sha }, rightFileUri) : emptyFileUri;
- const [owner, repo, rightCommitSha] = rightFileUri.authority
- .split('+')
- .filter(Boolean);
- const leftCommitSha = await repository
- .getCommitManager()
- .getFilePrevCommitSha(rightFileUri.path, rightCommitSha);
-
- // if we can't find prevCommitSha, use the the `emptyFileUri` as the leftFileUri
- const leftFileUri = leftCommitSha
- ? rightFileUri.with({ authority: `${owner}+${repo}+${leftCommitSha}` })
- : emptyFileUri;
-
- const changedStatus = leftCommitSha
- ? FileChangeType.MODIFIED
- : FileChangeType.ADDED;
-
- const hasNextRevision = !!(await repository
- .getCommitManager()
- .getFileNextCommitSha(rightFileUri.path, rightCommitSha));
+ const changedStatus = leftCommit ? FileChangeStatus.Modified : FileChangeStatus.Added;
+ const hasNextRevision = !!(await repository.getNextCommit(rightCommitSha, rightFileUri.path));
const query = queryString.stringify({
base: leftFileUri.with({ query: '' }).toString(),
@@ -143,62 +87,51 @@ export const commandEditorViewOpenPrevRevision = async (
// the `Show Next Commit` Button would be disabled.
hasNextRevision,
});
- const title = getChangedFileDiffTitle(
- leftFileUri,
- rightFileUri,
- changedStatus
- );
return vscode.commands.executeCommand(
'vscode.diff',
leftFileUri.with({ query }),
rightFileUri.with({ query }),
- title
+ getChangedFileDiffTitle(leftFileUri, rightFileUri, changedStatus),
);
};
// show the file's diff between current commit and next commit
-export const commandEditorViewOpenNextRevision = async (
- fileUri: vscode.Uri
-) => {
- const leftFileUri = await getLatestFileUri(fileUri);
-
- const [owner, repo, leftCommitSha] = leftFileUri.authority
- .split('+')
- .filter(Boolean);
- const rightCommitSha = await repository
- .getCommitManager()
- .getFileNextCommitSha(leftFileUri.path, leftCommitSha);
-
- if (!rightCommitSha) {
- return vscode.window.showInformationMessage(
- 'There is no next commit found.'
- );
+const commandOpenFileNextRevision = async (fileUri: vscode.Uri) => {
+ const leftFileUri = await getConcreteFileUri(fileUri);
+
+ const { scheme, repo, ref: leftCommitSha } = router.parseUri(leftFileUri);
+ const repository = Repository.getInstance(scheme, repo);
+ const rightCommit = await repository.getNextCommit(leftCommitSha, leftFileUri.path);
+
+ if (!rightCommit) {
+ return vscode.window.showInformationMessage('There is no next commit found.');
}
- const rightFileUri = leftFileUri.with({
- authority: `${owner}+${repo}+${rightCommitSha}`,
- });
+ const rightFileUri = router.buildUri({ ref: rightCommit.sha }, leftFileUri);
+ const hasNextRevision = !!(await repository.getNextCommit(rightCommit.sha, rightFileUri.path));
- const hasNextRevision = !!(await repository
- .getCommitManager()
- .getFileNextCommitSha(rightFileUri.path, rightCommitSha));
const query = queryString.stringify({
base: leftFileUri.with({ query: '' }).toString(),
head: rightFileUri.with({ query: '' }).toString(),
- status: FileChangeType.MODIFIED,
+ status: FileChangeStatus.Modified,
hasNextRevision,
});
- const title = getChangedFileDiffTitle(
- leftFileUri,
- rightFileUri,
- FileChangeType.MODIFIED
- );
return vscode.commands.executeCommand(
'vscode.diff',
leftFileUri.with({ query }),
rightFileUri.with({ query }),
- title
+ getChangedFileDiffTitle(leftFileUri, rightFileUri, FileChangeStatus.Modified),
+ );
+};
+
+export const registerEditorCommands = (context: vscode.ExtensionContext) => {
+ return context.subscriptions.push(
+ vscode.commands.registerCommand('github1s.commands.diffChangedFile', commandDiffChangedFile),
+ vscode.commands.registerCommand('github1s.commands.diffViewOpenLeftFile', commandDiffViewOpenLeftFile),
+ vscode.commands.registerCommand('github1s.commands.diffViewOpenRightFile', commandDiffViewOpenRightFile),
+ vscode.commands.registerCommand('github1s.commands.openFilePreviousRevision', commandOpenFilePreviousRevision),
+ vscode.commands.registerCommand('github1s.commands.openFileNextRevision', commandOpenFileNextRevision),
);
};
diff --git a/extensions/github1s/src/commands/gitpod.ts b/extensions/github1s/src/commands/gitpod.ts
deleted file mode 100644
index bb89782cf..000000000
--- a/extensions/github1s/src/commands/gitpod.ts
+++ /dev/null
@@ -1,19 +0,0 @@
-/**
- * @file GitHub1s GitPod Related Commands
- * @author mikenikles
- */
-
-import * as vscode from 'vscode';
-import router from '@/router';
-
-export const commandOpenGitpod = () => {
- return router.getAuthority().then((currentAuthority) => {
- const [currentOwner, currentRepo] = currentAuthority.split('+');
- vscode.commands.executeCommand(
- 'vscode.open',
- vscode.Uri.parse(
- `https://gitpod.io/#https://github.com/${currentOwner}/${currentRepo}`
- )
- );
- });
-};
diff --git a/extensions/github1s/src/commands/global.ts b/extensions/github1s/src/commands/global.ts
index 9b12ed8a4..ccaa79cdc 100644
--- a/extensions/github1s/src/commands/global.ts
+++ b/extensions/github1s/src/commands/global.ts
@@ -5,15 +5,91 @@
import * as vscode from 'vscode';
import router from '@/router';
+import { getAdapter } from '@/adapters';
+import { relativeTimeTo } from '@/helpers/date';
+import { getRecentRepositories, removeRecentRepository } from '@/helpers/context';
-export const commandOpenOnGitHub = async () => {
- const location = router.history.location;
- const githubPath =
- location.pathname === '/'
- ? '/conwnet/github1s'
- : `${location.pathname}${location.search}${location.hash}`;
- const GITHUB_ORIGIN = 'https://github.com';
- const gitHubUri = vscode.Uri.parse(GITHUB_ORIGIN + githubPath);
+export const commandOpenOnOfficialPage = async () => {
+ const location = router.getHistory().location;
+ const routerParser = router.getParser();
+ const fullPath = `${location.pathname}${location.search}${location.hash}`;
+ const externalLink = await routerParser.buildExternalLink(fullPath);
- return vscode.commands.executeCommand('vscode.open', gitHubUri);
+ return vscode.commands.executeCommand('vscode.open', vscode.Uri.parse(externalLink));
+};
+
+const repoPickItemButtons = [{ iconPath: new vscode.ThemeIcon('close') }];
+
+const getRecentRepoPickItems = () =>
+ getRecentRepositories().map((record) => ({
+ label: record.name,
+ description: relativeTimeTo(record.timestamp),
+ buttons: repoPickItemButtons,
+ }));
+
+export const commandOpenRepository = async () => {
+ const quickPick = vscode.window.createQuickPick();
+ const manualInputItem = { label: '' };
+ let recentRepoPickItems = getRecentRepoPickItems();
+
+ const updatePickerItems = () => {
+ if (manualInputItem.label) {
+ return (quickPick.items = [...recentRepoPickItems, manualInputItem]);
+ }
+ return (quickPick.items = recentRepoPickItems);
+ };
+
+ quickPick.placeholder = 'Select to open...';
+ updatePickerItems();
+
+ quickPick.show();
+ quickPick.onDidTriggerItemButton(async (event) => {
+ if (event.button === repoPickItemButtons[0]) {
+ await removeRecentRepository(event.item.label);
+ recentRepoPickItems = getRecentRepoPickItems();
+ updatePickerItems();
+ }
+ });
+
+ quickPick.onDidChangeValue((value) => {
+ manualInputItem.label = value ? `Open ${value}...` : '';
+ updatePickerItems();
+ });
+
+ quickPick.onDidAccept(async () => {
+ const choice = quickPick.activeItems[0];
+ const repository = choice === manualInputItem ? quickPick.value : choice.label;
+ const targetLink = vscode.Uri.parse((await router.href()) || '').with({
+ path: await router.getParser().buildTreePath(repository),
+ });
+ vscode.commands.executeCommand('vscode.open', targetLink);
+ quickPick.hide();
+ });
+};
+
+const commandOpenOnlineEditor = async () => {
+ const onlineEditorPath = ['github1s', 'ossinsight'].includes(getAdapter().scheme) ? '/editor' : '/';
+ const targetLink = vscode.Uri.parse((await router.href()) || '').with({ path: onlineEditorPath });
+ return vscode.commands.executeCommand('vscode.open', targetLink);
+};
+
+const commandRefreshRepository = async () => {
+ if (['github1s', 'gitlab1s'].includes(getAdapter().scheme)) {
+ await vscode.commands.executeCommand('github1s.commands.syncSourcegraphRepository');
+ }
+ vscode.commands.executeCommand('workbench.action.reloadWindow');
+};
+
+export const registerGlobalCommands = (context: vscode.ExtensionContext) => {
+ return context.subscriptions.push(
+ vscode.commands.registerCommand('github1s.commands.openOnGitHub', commandOpenOnOfficialPage),
+ vscode.commands.registerCommand('github1s.commands.openOnGitLab', commandOpenOnOfficialPage),
+ vscode.commands.registerCommand('github1s.commands.openOnBitbucket', commandOpenOnOfficialPage),
+ vscode.commands.registerCommand('github1s.commands.openOnNpm', commandOpenOnOfficialPage),
+ vscode.commands.registerCommand('github1s.commands.openOnOfficialPage', commandOpenOnOfficialPage),
+ vscode.commands.registerCommand('github1s.commands.openRepository', commandOpenRepository),
+ vscode.commands.registerCommand('github1s.commands.openOnlineEditor', commandOpenOnlineEditor),
+ vscode.commands.registerCommand('github1s.commands.refreshRepository', commandRefreshRepository),
+ vscode.commands.registerCommand('remoteHub.openRepository', commandOpenRepository),
+ );
};
diff --git a/extensions/github1s/src/commands/index.ts b/extensions/github1s/src/commands/index.ts
index de1b04e37..342ffdffd 100644
--- a/extensions/github1s/src/commands/index.ts
+++ b/extensions/github1s/src/commands/index.ts
@@ -3,122 +3,21 @@
* @author netcon
*/
-import * as vscode from 'vscode';
import { getExtensionContext } from '@/helpers/context';
-import {
- commandValidateToken,
- commandUpdateToken,
- commandClearToken,
- commandAuthorizingGithub,
- commandAuthorizingGithubWithOverlay,
-} from './token';
-import { commandGetCurrentAuthority, commandCheckoutRef } from './ref';
-import {
- commandSwitchToPull,
- commandPullViewItemSwitchToPull,
- commandPullViewItemOpenOnGitHub,
- commandPullViewRefreshPullList,
- commandPullViewLoadMorePulls,
-} from './pull';
-import {
- commandSwitchToCommit,
- commandOpenCommitOnGitHub,
- commandCommitViewItemSwitchToCommit,
- commandCommitViewItemOpenOnGitHub,
- commandCommitViewRefreshCommitList,
- commandCommitViewLoadMoreCommits,
-} from './commit';
-import { commandOpenGitpod } from './gitpod';
-import {
- commandEditorViewOpenChanges,
- commandDiffViewOpenLeftFile,
- commandDiffViewOpenRightFile,
- commandEditorViewOpenNextRevision,
- commandEditorViewOpenPrevRevision,
-} from './editor';
-import {
- commandToggleEditorGutterBlame,
- commandOpenEditorGutterBlame,
- commandCloseEditorGutterBlame,
-} from './blame';
-import { commandOpenOnGitHub } from './global';
-
-const commands: { id: string; callback: (...args: any[]) => any }[] = [
- // validate GitHub OAuth Token
- { id: 'github1s.validate-token', callback: commandValidateToken },
- // update GitHub OAuth Token
- { id: 'github1s.update-token', callback: commandUpdateToken },
- // clear GitHub OAuth Token
- { id: 'github1s.clear-token', callback: commandClearToken },
- // authorizing github with `Web application flow`
- { id: 'github1s.authorizing-github', callback: commandAuthorizingGithub },
- // open the overlay on the page and authorizing github with `Web application flow`
- { id: 'github1s.authorizing-github-with-overlay', callback: commandAuthorizingGithubWithOverlay }, // prettier-ignore
-
- // get current authority (`${owner}+${repo}+${ref}`)
- { id: 'github1s.get-current-authority', callback: commandGetCurrentAuthority }, // prettier-ignore
- // checkout to other branch/tag/commit
- { id: 'github1s.checkout-ref', callback: commandCheckoutRef },
-
- // switch to a pull request & input pull number manually
- { id: 'github1s.switch-to-pull', callback: commandSwitchToPull },
- // update the pull request list in the pull requests view
- { id: 'github1s.pull-view-refresh-pull-list', callback: commandPullViewRefreshPullList }, // prettier-ignore
- // load more pulls in the pull requests tree view
- { id: 'github1s.pull-view-load-more-pulls', callback: commandPullViewLoadMorePulls }, // prettier-ignore
- // switch to a pull request in the pull requests view
- { id: 'github1s.pull-view-item-switch-to-pull', callback: commandPullViewItemSwitchToPull }, // prettier-ignore
- // open pull on github in the pull requests view
- { id: 'github1s.pull-view-item-open-on-github', callback: commandPullViewItemOpenOnGitHub }, // prettier-ignore
-
- // switch to a commit & input pull number manually
- { id: 'github1s.switch-to-commit', callback: commandSwitchToCommit },
- // open a commit on GitHub's website
- { id: 'github1s.open-commit-on-github', callback: commandOpenCommitOnGitHub },
- // update the commit list in the commits view
- { id: 'github1s.commit-view-refresh-commit-list', callback: commandCommitViewRefreshCommitList }, // prettier-ignore
- // load more commits in the commits tree view
- { id: 'github1s.commit-view-load-more-commits', callback: commandCommitViewLoadMoreCommits }, // prettier-ignore
- // switch to a commit in the commits view
- { id: 'github1s.commit-view-item-switch-to-commit', callback: commandCommitViewItemSwitchToCommit }, // prettier-ignore
- // open commit on github in the commits view
- { id: 'github1s.commit-view-item-open-on-github', callback: commandCommitViewItemOpenOnGitHub }, // prettier-ignore
-
- // open current repository on gitpod
- { id: 'github1s.open-gitpod', callback: commandOpenGitpod },
-
- // open the changes of a file
- { id: 'github1s.editor-view-open-changes', callback: commandEditorViewOpenChanges }, // prettier-ignore
- // open the left file in diff editor
- { id: 'github1s.diff-view-open-left-file', callback: commandDiffViewOpenLeftFile }, // prettier-ignore
- // open the right file in diff editor
- { id: 'github1s.diff-view-open-right-file', callback: commandDiffViewOpenRightFile }, // prettier-ignore
- // open the previous revision of a file
- { id: 'github1s.editor-view-open-prev-revision', callback: commandEditorViewOpenPrevRevision }, // prettier-ignore
- // open the next revision of a file
- { id: 'github1s.editor-view-open-next-revision', callback: commandEditorViewOpenNextRevision }, // prettier-ignore
-
- // toggle the gutter blame of a editor
- { id: 'github1s.toggle-editor-gutter-blame', callback: commandToggleEditorGutterBlame }, // prettier-ignore
- // open the gutter blame of a editor
- { id: 'github1s.open-editor-gutter-blame', callback: commandOpenEditorGutterBlame }, // prettier-ignore
- // close the gutter blame of a editor
- { id: 'github1s.close-editor-gutter-blame', callback: commandCloseEditorGutterBlame }, // prettier-ignore
-
- // open current page on GitHub
- { id: 'github1s.open-on-github', callback: commandOpenOnGitHub },
-];
+import { registerRefCommands } from './ref';
+import { registerCodeReviewCommands } from './code-review';
+import { registerCommitCommands } from './commit';
+import { registerEditorCommands } from './editor';
+import { registerBlameCommands } from './blame';
+import { registerGlobalCommands } from './global';
export const registerGitHub1sCommands = () => {
const context = getExtensionContext();
- context.subscriptions.push(
- ...commands.map((command) =>
- vscode.commands.registerCommand(command.id, command.callback)
- )
- );
-
- vscode.commands.registerCommand('github1s.dev-test', () => {
- console.log(vscode.window.activeTextEditor);
- });
+ registerRefCommands(context);
+ registerEditorCommands(context);
+ registerCodeReviewCommands(context);
+ registerCommitCommands(context);
+ registerBlameCommands(context);
+ registerGlobalCommands(context);
};
diff --git a/extensions/github1s/src/commands/pull.ts b/extensions/github1s/src/commands/pull.ts
deleted file mode 100644
index 86edb412b..000000000
--- a/extensions/github1s/src/commands/pull.ts
+++ /dev/null
@@ -1,110 +0,0 @@
-/**
- * @file GitHub1s Pull Request Related Commands
- * @author netcon
- */
-
-import * as vscode from 'vscode';
-import router from '@/router';
-import repository from '@/repository';
-import {
- PullTreeItem,
- getPullTreeItemLabel,
- getPullTreeItemDescription,
-} from '@/views/pull-list-view';
-import { pullRequestTreeDataProvider } from '@/views';
-import { RequestNotFoundError } from '@/helpers/fetch';
-
-const checkPullExists = async (pullNumber: number) => {
- try {
- return !!(await repository.getPullManager().getItem(pullNumber));
- } catch (e) {
- vscode.window.showErrorMessage(
- e instanceof RequestNotFoundError
- ? `No pull request found for number: ${pullNumber}`
- : e.message
- );
- return false;
- }
-};
-
-export const commandSwitchToPull = async (pullNumber?: number) => {
- const { owner, repo } = await router.getState();
-
- // if the a pull number isn't provided, use quickInput
- if (!pullNumber) {
- // manual input a pull number
- const inputPullNumberItem: vscode.QuickPickItem = {
- label: '$(git-pull-request) Manual input the pull number',
- alwaysShow: true,
- };
- // use the pull list as the candidates
- const pullRequestItems: vscode.QuickPickItem[] = (
- await repository.getPullManager().getList()
- ).map((pull) => ({
- pullNumber: pull.number,
- label: getPullTreeItemLabel(pull),
- description: getPullTreeItemDescription(pull),
- }));
-
- const quickPick = vscode.window.createQuickPick();
- quickPick.matchOnDescription = true;
- quickPick.items = [inputPullNumberItem, ...pullRequestItems];
- quickPick.show();
-
- const choice = (await new Promise(
- (resolve) =>
- quickPick.onDidAccept(() => resolve(quickPick.activeItems[0]))
- )) as vscode.QuickPickItem & { pullNumber?: number };
- quickPick.hide();
-
- // select nothing
- if (!choice) {
- return;
- }
-
- // select `manual input the pull number`
- if (choice === inputPullNumberItem) {
- pullNumber = parseInt(
- await vscode.window.showInputBox({
- placeHolder: 'Please input the pull number',
- }),
- 10
- );
- } else {
- // select a pull item
- pullNumber = choice.pullNumber;
- }
- }
-
- (await checkPullExists(pullNumber)) &&
- router.replace(`/${owner}/${repo}/pull/${pullNumber}`);
-};
-
-// this command is used in `source control pull request view`
-export const commandPullViewItemSwitchToPull = (viewItem: PullTreeItem) => {
- return commandSwitchToPull(viewItem?.pull?.number);
-};
-
-// this command is used in `source control pull request view`
-export const commandPullViewItemOpenOnGitHub = async (
- viewItem: PullTreeItem
-) => {
- const pullNumber = viewItem?.pull?.number;
-
- if (pullNumber) {
- const { owner, repo } = await router.getState();
- return vscode.commands.executeCommand(
- 'vscode.open',
- vscode.Uri.parse(`https://github.com/${owner}/${repo}/pull/${pullNumber}`)
- );
- }
-};
-
-export const commandPullViewRefreshPullList = (forceUpdate = true) => {
- return pullRequestTreeDataProvider.updateTree(forceUpdate);
-};
-
-export const commandPullViewLoadMorePulls = () => {
- repository.getPullManager().loadMore();
- return commandPullViewRefreshPullList(false);
-};
diff --git a/extensions/github1s/src/commands/ref.ts b/extensions/github1s/src/commands/ref.ts
index 2cdd8fb09..fa1174a43 100644
--- a/extensions/github1s/src/commands/ref.ts
+++ b/extensions/github1s/src/commands/ref.ts
@@ -5,41 +5,54 @@
import * as vscode from 'vscode';
import router from '@/router';
-import repository from '@/repository';
-
-export const commandGetCurrentAuthority = () => router.getAuthority();
-
-export const commandCheckoutRef = async () => {
- const [branchRefs, tagRefs] = await Promise.all([
- repository.getBranches(),
- repository.getTags(),
- ]);
- const branchPickerItems: vscode.QuickPickItem[] = branchRefs.map(
- (branchRef) => ({
- label: branchRef.name,
- description: (branchRef.object?.sha || '').slice(0, 8),
- })
- );
- const tagPickerItems: vscode.QuickPickItem[] = tagRefs.map((tagRef) => ({
- label: tagRef.name,
- description: `Tag at ${(tagRef.object?.sha || '').slice(0, 8)}`,
- }));
+import { Repository } from '@/repository';
+const loadMorePickerItem: vscode.QuickPickItem = {
+ label: '$(more) Load More',
+ alwaysShow: true,
+};
+
+const checkoutToItem: vscode.QuickPickItem = {
+ label: '$(debug-disconnect) Checkout detached',
+ alwaysShow: true,
+};
+
+// check out to branch/tag/commit
+const commandCheckoutTo = async () => {
+ const routeState = router.getState();
+ const repository = Repository.getCurrentInstance();
const quickPick = vscode.window.createQuickPick();
- const routerState = await router.getState();
- quickPick.placeholder = routerState.ref;
- quickPick.items = [...branchPickerItems, ...tagPickerItems];
+ const loadMoreRefPickerItems = async () => {
+ quickPick.busy = true;
+ await Promise.all([repository.loadMoreBranches(), repository.loadMoreTags()]);
+ const [branchRefs, tagRefs] = await Promise.all([repository.getBranchList(), repository.getTagList()]);
+ const refPickerItems = [...branchRefs, ...tagRefs].map((ref) => ({
+ label: ref.name,
+ description: ref.description,
+ }));
+ const hasMore = (await Promise.all([repository.hasMoreBranches(), repository.hasMoreTags()])).some(Boolean);
+ quickPick.items = [...refPickerItems, hasMore ? loadMorePickerItem : null!, checkoutToItem].filter(Boolean);
+ quickPick.busy = false;
+ };
+
+ quickPick.placeholder = 'Input a ref to checkout';
+ quickPick.items = [checkoutToItem];
+ loadMoreRefPickerItems();
quickPick.show();
- const choice = await new Promise(
- (resolve) => quickPick.onDidAccept(() => resolve(quickPick.activeItems[0]))
- );
- quickPick.hide();
-
- const targetRef = choice?.label || quickPick.value;
- if (targetRef.toUpperCase() === 'HEAD') {
- router.replace(`/${routerState.owner}/${routerState.repo}`);
- return;
- }
- router.replace(`/${routerState.owner}/${routerState.repo}/tree/${targetRef}`);
+
+ quickPick.onDidAccept(async () => {
+ const choice = quickPick.activeItems[0];
+ if (choice === loadMorePickerItem) {
+ return loadMoreRefPickerItems();
+ }
+ const selectedRef = choice === checkoutToItem ? quickPick.value : choice?.label;
+ const targetRef = selectedRef.toUpperCase() !== 'HEAD' ? selectedRef : undefined;
+ router.push(await router.getParser().buildTreePath(routeState.repo, targetRef));
+ quickPick.hide();
+ });
+};
+
+export const registerRefCommands = (context: vscode.ExtensionContext) => {
+ return context.subscriptions.push(vscode.commands.registerCommand('github1s.commands.checkoutTo', commandCheckoutTo));
};
diff --git a/extensions/github1s/src/commands/token.ts b/extensions/github1s/src/commands/token.ts
deleted file mode 100644
index 330fa9988..000000000
--- a/extensions/github1s/src/commands/token.ts
+++ /dev/null
@@ -1,154 +0,0 @@
-/**
- * @file GitHub1s Token Related Commands
- * @author netcon
- */
-
-import * as vscode from 'vscode';
-import { getExtensionContext, getOAuthToken } from '@/helpers/context';
-import { validateToken } from '@/interfaces/github-api-rest';
-import { GITHUB_OAUTH_TOKEN } from '@/helpers/constants';
-
-export const commandValidateToken = (
- token: string,
- silent: boolean = false
-) => {
- // if we can not get a token from arguments,
- // just verify current token which has been saved
- const oAuthToken = token || getOAuthToken();
- return validateToken(oAuthToken).then((tokenStatus) => {
- if (!silent) {
- const remaining = tokenStatus.remaining;
- if (!oAuthToken) {
- if (remaining > 0) {
- vscode.window.showWarningMessage(
- `You haven\'t set a GitHub OAuth Token yet, and you can have ${remaining} requests in the current rate limit window.`
- );
- } else {
- vscode.window.showWarningMessage(
- "You haven't set a GitHub OAuth Token yet, and the rate limit is exceeded."
- );
- }
- } else if (!tokenStatus.valid) {
- vscode.window.showErrorMessage(
- 'Current GitHub OAuth Token is invalid.'
- );
- } else if (tokenStatus.valid && tokenStatus.remaining > 0) {
- vscode.window.showInformationMessage(
- `Current GitHub OAuth Token is OK, and you can have ${remaining} requests in the current rate limit window.`
- );
- } else if (tokenStatus.valid && tokenStatus.remaining <= 0) {
- vscode.window.showWarningMessage(
- 'Current GitHub OAuth Token is Valid, but the rate limit is exceeded.'
- );
- }
- }
- return tokenStatus;
- });
-};
-
-export const commandUpdateToken = async (
- token: string,
- silent: boolean = false
-) => {
- if (!token) {
- // if the token isn't passed by arguments,
- // open an input box and request user input it
- token = await vscode.window.showInputBox({
- placeHolder: 'Please input the GitHub OAuth Token',
- });
- }
-
- if (!token) {
- return;
- }
-
- await getExtensionContext()!.globalState.update(
- GITHUB_OAUTH_TOKEN,
- token || ''
- );
- vscode.commands.executeCommand('workbench.files.action.refreshFilesExplorer');
-
- if (silent) {
- return;
- }
-
- // we don't need wait validate, so we don't `return`
- validateToken(token).then((tokenStatus) => {
- if (!tokenStatus.valid) {
- vscode.window.showErrorMessage(
- 'GitHub OAuth Token have updated, but it is invalid.'
- );
- } else if (tokenStatus.remaining <= 0) {
- vscode.window.showWarningMessage(
- 'GitHub OAuth Token have updated, but the rate limit is exceeded.'
- );
- } else {
- vscode.window.showInformationMessage('GitHub OAuth Token have updated.');
- }
- });
-};
-
-export const commandClearToken = (silent: boolean = false) => {
- return vscode.window
- .showWarningMessage(
- 'Would you want to clear the saved GitHub OAuth Token?',
- { modal: true },
- 'Confirm'
- )
- .then((choose) => {
- if (choose === 'Confirm') {
- return getExtensionContext()!
- .globalState.update(GITHUB_OAUTH_TOKEN, '')
- .then(() => {
- !silent &&
- vscode.window.showInformationMessage(
- 'You have cleared the saved GitHb OAuth Token.'
- );
- })
- .then(() => true);
- }
- return false;
- });
-};
-
-type AuthMessageData =
- | { access_token: string; token_type: string; scope: string }
- | { error: string; error_description: string; error_uri?: string };
-
-export const commandAuthorizingGithub = async (
- silent: boolean = false
-): Promise => {
- // vscode-web-github1s/src/vs/github1s/authorizing-github.ts
- // retry with authorizing overlay if browser blocked opening authorizing window
- const data: AuthMessageData = await vscode.commands.executeCommand(
- 'github1s.vscode.get-github-access-token',
- true
- );
-
- if ('access_token' in data) {
- // update the access_token into extension context
- await commandUpdateToken(data.access_token);
- return data.access_token;
- }
-
- if ('error' in data && !silent) {
- const seeMoreLinkText = data.error_uri ? ` [See more](data.error_uri)` : '';
- vscode.window.showErrorMessage(
- `${data.error_description}${seeMoreLinkText}`
- );
- }
- return '';
-};
-
-// open the overlay on the page, and get the access_token from it
-export const commandAuthorizingGithubWithOverlay = async () => {
- const data: AuthMessageData = await vscode.commands.executeCommand(
- 'github1s.vscode.get-github-access-token-with-overlay'
- );
-
- if ('access_token' in data) {
- await commandUpdateToken(data.access_token);
- return data.access_token;
- }
- return '';
-};
diff --git a/extensions/github1s/src/extension.ts b/extensions/github1s/src/extension.ts
index 72de61822..7be17e063 100644
--- a/extensions/github1s/src/extension.ts
+++ b/extensions/github1s/src/extension.ts
@@ -3,80 +3,73 @@
* @author netcon
*/
+import router from '@/router';
import * as vscode from 'vscode';
-import { setExtensionContext } from '@/helpers/context';
-import { registerGitHub1sCommands } from '@/commands';
-import { registerVSCodeProviders } from '@/providers';
+import { PageType } from '@/adapters/types';
+import { registerAdapters } from '@/adapters';
import { registerCustomViews } from '@/views';
-import { GitHub1sFileSystemProvider } from '@/providers/fileSystemProvider';
-import { showSponsors } from '@/sponsors';
-import { showGitpod } from '@/gitpod';
-import router from '@/router';
-import { activateSourceControl } from '@/source-control';
+import { decorateStatusBar } from '@/statusbar';
import { registerEventListeners } from '@/listeners';
-import { PageType } from './router/types';
+import { registerVSCodeProviders } from '@/providers';
+import { registerGitHub1sCommands } from '@/commands';
+import { updateSourceControlChanges } from '@/changes';
+import { addRecentRepositories, setExtensionContext } from '@/helpers/context';
-export async function activate(context: vscode.ExtensionContext) {
- const browserUrl = (await vscode.commands.executeCommand(
- 'github1s.vscode.get-browser-url'
- )) as string;
+const browserUrlManager = {
+ href: () => vscode.commands.executeCommand('github1s.commands.vscode.getBrowserUrl') as Promise,
+ push: (url: string) =>
+ vscode.commands.executeCommand('github1s.commands.vscode.pushBrowserUrl', url) as Promise,
+ replace: (url: string) =>
+ vscode.commands.executeCommand('github1s.commands.vscode.replaceBrowserUrl', url) as Promise,
+};
+// eslint-disable-next-line jsdoc/require-jsdoc
+export async function activate(context: vscode.ExtensionContext) {
// set the global context for convenient
setExtensionContext(context);
- // Ensure the router has been initialized at first
- await router.initialize(browserUrl);
- // register the necessary event listeners
- registerEventListeners();
- // register VS Code providers
- registerVSCodeProviders();
- // register custom views
- registerCustomViews();
- // register GitHub1s Commands
- registerGitHub1sCommands();
+ // register platform adapters
+ await registerAdapters();
- // activate SourceControl features,
- activateSourceControl();
+ // Ensure the router has been initialized
+ await router.initialize(browserUrlManager);
- // sponsors in Status Bar
- showSponsors();
- showGitpod();
+ // do follow-up works in parallel
+ await Promise.all([
+ registerVSCodeProviders(),
+ registerEventListeners(),
+ registerGitHub1sCommands(),
+ registerCustomViews(),
+ updateSourceControlChanges(),
+ decorateStatusBar(),
+ ]);
- // initialize the VSCode's state
initialVSCodeState();
}
// initialize the VSCode's state according to the router url
const initialVSCodeState = async () => {
- const routerState = await router.getState();
- const { filePath, pageType } = routerState;
- const scheme = GitHub1sFileSystemProvider.scheme;
-
- if (filePath && pageType === PageType.TREE) {
- vscode.commands.executeCommand(
- 'revealInExplorer',
- vscode.Uri.parse('').with({ scheme, path: filePath })
- );
- } else if (filePath && pageType === PageType.BLOB) {
- const { startLineNumber, endLineNumber } = routerState;
- const start = new vscode.Position(startLineNumber - 1, 0);
- const end = new vscode.Position(endLineNumber - 1, 999999);
- const documentShowOptions: vscode.TextDocumentShowOptions = startLineNumber
- ? { selection: new vscode.Range(start, end) }
- : {};
+ const routerState = router.getState();
- // TODO: the selection of the opening file may be cleared
- // when editor try to restore previous state in the same file
- vscode.commands.executeCommand(
- 'vscode.open',
- vscode.Uri.parse('').with({ scheme, path: filePath }),
- documentShowOptions
- );
- } else if (pageType === PageType.PULL_LIST) {
- vscode.commands.executeCommand('github1s.views.pull-request-list.focus');
- } else if (pageType === PageType.COMMIT_LIST) {
- vscode.commands.executeCommand('github1s.views.commit-list.focus');
- } else if ([PageType.PULL, PageType.COMMIT].includes(pageType)) {
+ if (routerState.pageType === PageType.Tree && routerState.filePath !== '/') {
+ vscode.commands.executeCommand('revealInExplorer', router.buildUri({ path: routerState.filePath }));
+ } else if (routerState.pageType === PageType.Blob && routerState.filePath !== '/') {
+ const { startLine, endLine } = routerState;
+ let documentShowOptions: vscode.TextDocumentShowOptions = {};
+ if (startLine || endLine) {
+ const startPosition = new vscode.Position((startLine || endLine)! - 1, 0);
+ const endPosition = new vscode.Position((endLine || startLine)! - 1, 1 << 20);
+ documentShowOptions = { selection: new vscode.Range(startPosition, endPosition) };
+ }
+ vscode.window.showTextDocument(router.buildUri({ path: routerState.filePath }), documentShowOptions);
+ } else if (routerState.pageType === PageType.CodeReviewList) {
+ vscode.commands.executeCommand('github1s.views.codeReviewList.focus');
+ } else if (routerState.pageType === PageType.CommitList) {
+ vscode.commands.executeCommand('github1s.views.commitList.focus');
+ } else if ([PageType.CodeReview, PageType.Commit].includes(routerState.pageType)) {
vscode.commands.executeCommand('workbench.scm.focus');
+ } else if (routerState.pageType === PageType.Search) {
+ vscode.commands.executeCommand('workbench.action.findInFiles', routerState);
}
+ routerState.repo && addRecentRepositories(routerState.repo);
};
diff --git a/extensions/github1s/src/gitpod.ts b/extensions/github1s/src/gitpod.ts
deleted file mode 100644
index ecfa8ed2d..000000000
--- a/extensions/github1s/src/gitpod.ts
+++ /dev/null
@@ -1,31 +0,0 @@
-/**
- * @file Show Gitpod In Status Bar
- * @author mikenikles
- */
-
-import * as vscode from 'vscode';
-import router from '@/router';
-
-const getGitpodRepoUri = async () => {
- const { owner, repo } = await router.getState();
- return vscode.Uri.parse(
- `https://gitpod.io/#https://github.com/${owner}/${repo}`
- );
-};
-
-export const showGitpod = async () => {
- const gitpodRepoUri = await getGitpodRepoUri();
-
- const item = vscode.window.createStatusBarItem(
- vscode.StatusBarAlignment.Left,
- 0
- );
- item.text = ' $(pencil) Develop your project on Gitpod';
- item.tooltip = 'Edit files, open/review PRs, use Docker - all online.';
- item.command = {
- title: `Open Gitpod`,
- command: 'vscode.open',
- arguments: [gitpodRepoUri],
- };
- item.show();
-};
diff --git a/extensions/github1s/src/global.d.ts b/extensions/github1s/src/global.d.ts
new file mode 100644
index 000000000..819440645
--- /dev/null
+++ b/extensions/github1s/src/global.d.ts
@@ -0,0 +1,4 @@
+declare const GITHUB_ORIGIN: string;
+declare const GITHUB_API_PREFIX: string;
+declare const GITLAB_ORIGIN: string;
+declare const GITLAB_API_PREFIX: string;
diff --git a/extensions/github1s/src/helpers/async.ts b/extensions/github1s/src/helpers/async.ts
index 415b8adbd..6d37f3d72 100644
--- a/extensions/github1s/src/helpers/async.ts
+++ b/extensions/github1s/src/helpers/async.ts
@@ -3,17 +3,18 @@
*/
// below code is comes from:
-//https://github.com/microsoft/vscode/blob/a3415e669a8f3879c290af5616a8ed45dd0534af/src/vs/base/common/async.ts#L344
+// https://github.com/microsoft/vscode/blob/a3415e669a8f3879c290af5616a8ed45dd0534af/src/vs/base/common/async.ts#L344
export class Barrier {
private _isOpen: boolean;
private _promise: Promise;
private _completePromise!: (v: boolean) => void;
- constructor() {
+ constructor(timeout = 0) {
this._isOpen = false;
this._promise = new Promise((c, e) => {
this._completePromise = c;
});
+ timeout && setTimeout(() => this.open(), timeout);
}
isOpen(): boolean {
diff --git a/extensions/github1s/src/helpers/constants.ts b/extensions/github1s/src/helpers/constants.ts
deleted file mode 100644
index 1c18980e7..000000000
--- a/extensions/github1s/src/helpers/constants.ts
+++ /dev/null
@@ -1,6 +0,0 @@
-/**
- * @file extension constants variables
- * @author xcv58
- */
-
-export const GITHUB_OAUTH_TOKEN = 'github-oauth-token';
diff --git a/extensions/github1s/src/helpers/context.ts b/extensions/github1s/src/helpers/context.ts
index 3761b146d..a1f8e40a5 100644
--- a/extensions/github1s/src/helpers/context.ts
+++ b/extensions/github1s/src/helpers/context.ts
@@ -4,13 +4,10 @@
*/
import * as vscode from 'vscode';
-import { GITHUB_OAUTH_TOKEN } from './constants';
let extensionContext: vscode.ExtensionContext | null = null;
-export const setExtensionContext = (
- _extensionContext: vscode.ExtensionContext
-) => {
+export const setExtensionContext = (_extensionContext: vscode.ExtensionContext) => {
extensionContext = _extensionContext;
};
@@ -22,9 +19,23 @@ export const getExtensionContext = (): vscode.ExtensionContext => {
return extensionContext;
};
-export const getOAuthToken = () => {
- const context = getExtensionContext();
- return (context.globalState.get(GITHUB_OAUTH_TOKEN) as string) || '';
+const RECENT_REPOSITORIES = 'github1s-recent-repositories';
+export const getRecentRepositories = (): { name: string; timestamp: number }[] => {
+ return getExtensionContext().globalState.get(RECENT_REPOSITORIES) || [];
};
-export const hasValidToken = () => getOAuthToken() !== '';
+export const addRecentRepositories = (name: string, timestamp = 0) => {
+ const currentRecord = { name, timestamp: timestamp || Date.now() };
+ const restRecords = getRecentRepositories().filter((record) => record.name !== name);
+ const newRecords = [currentRecord, ...restRecords.slice(0, 49)]; // max to 50 records
+ return getExtensionContext().globalState.update(RECENT_REPOSITORIES, newRecords);
+};
+
+export const removeRecentRepository = (name: string) => {
+ const newRecords = getRecentRepositories().filter((record) => record.name !== name);
+ return getExtensionContext().globalState.update(RECENT_REPOSITORIES, newRecords);
+};
+
+export const getBrowserUrl = () => {
+ return vscode.commands.executeCommand('github1s.commands.vscode.getBrowserUrl');
+};
diff --git a/extensions/github1s/src/helpers/date.ts b/extensions/github1s/src/helpers/date.ts
index 6dc15ccb8..0e36b4bff 100644
--- a/extensions/github1s/src/helpers/date.ts
+++ b/extensions/github1s/src/helpers/date.ts
@@ -8,5 +8,6 @@ import * as relativeTimePlugin from 'dayjs/plugin/relativeTime';
dayjs.extend(relativeTimePlugin);
-export const relativeTimeTo = (date: dayjs.ConfigType) =>
- dayjs().to(dayjs(date));
+export const relativeTimeTo = (date: dayjs.ConfigType) => dayjs().to(dayjs(date));
+
+export const toISOString = (date: dayjs.ConfigType) => dayjs(date).toISOString();
diff --git a/extensions/github1s/src/helpers/fetch.ts b/extensions/github1s/src/helpers/fetch.ts
deleted file mode 100644
index e4393a365..000000000
--- a/extensions/github1s/src/helpers/fetch.ts
+++ /dev/null
@@ -1,126 +0,0 @@
-/**
- * @file github1s fetch api
- * @author netcon
- */
-
-import * as vscode from 'vscode';
-import { reuseable, throttle } from './func';
-import { getOAuthToken } from './context';
-import { noop } from './util';
-
-export class RequestError extends Error {
- constructor(message: string, public token: string) {
- super(message);
- if (typeof (Object).setPrototypeOf === 'function') {
- (Object).setPrototypeOf(this, RequestError.prototype);
- }
- }
-}
-
-export class RequestNotFoundError extends RequestError {
- constructor(message: string, token: string) {
- super(message, token);
- if (typeof (Object).setPrototypeOf === 'function') {
- (Object).setPrototypeOf(this, RequestNotFoundError.prototype);
- }
- }
-}
-
-export class RequestRateLimitError extends RequestError {
- constructor(message: string, token: string) {
- super(message, token);
- if (typeof (Object).setPrototypeOf === 'function') {
- (Object).setPrototypeOf(this, RequestRateLimitError.prototype);
- }
- }
-}
-
-export class RequestInvalidTokenError extends RequestError {
- constructor(message: string, token: string) {
- super(message, token);
- if (typeof (Object).setPrototypeOf === 'function') {
- (Object).setPrototypeOf(this, RequestInvalidTokenError.prototype);
- }
- }
-}
-
-// only report network error once in 5 seconds
-export const throttledReportNetworkError = throttle(
- () =>
- vscode.window.showErrorMessage('Request Failed, Maybe an Network Error'),
- 5000
-);
-
-export const getFetchOptions = (forceUpdate?: boolean): RequestInit => {
- if (forceUpdate) {
- return { cache: 'reload' };
- }
-};
-
-const cache = new Map();
-
-const isGitHubApi = (url: string) => url.startsWith('https://api.github.com/');
-
-export const fetch = reuseable(async (url: string, options?: RequestInit) => {
- const token = getOAuthToken();
- const authHeaders =
- token && isGitHubApi(url) ? { Authorization: `token ${token}` } : {};
- const customHeaders = options && 'headers' in options ? options.headers : {};
- /**
- * We are reusing the same values from the https://developer.mozilla.org/en-US/docs/Web/API/Request/cache.
- * But the way is not the same because we couldn't control how the cache-control returns from external APIs.
- * Instead of relying on the browser caching stragety and API resposne header, we use an im-memory map to
- * cache all requests.
- */
- if (
- cache.has(url) &&
- !['no-store', 'no-cache', 'reload'].includes(options?.cache)
- ) {
- return cache.get(url);
- }
-
- let response: Response;
- try {
- response = await self.fetch(url, {
- mode: 'cors',
- ...options,
- headers: { ...authHeaders, ...customHeaders },
- });
- } catch (e) {
- throttledReportNetworkError();
- throw new RequestError('Request Failed, Maybe an Network Error', token);
- }
- if (response.status < 400) {
- cache.set(url, await response.json());
- return cache.get(url);
- }
- if (response.status === 403) {
- // if there is no token saved and the rate limit exceeded,
- // open the authorizing overly for requesting a access token
- if (!token && isGitHubApi(url)) {
- vscode.commands.executeCommand(
- 'github1s.authorizing-github-with-overlay'
- );
- }
- return response.json().then((data) => {
- throw new RequestRateLimitError(data.message, token);
- });
- }
- if (response.status === 401) {
- return response.json().then((data) => {
- // current token is invalid
- if (data.message?.includes('Bad credentials')) {
- vscode.commands.executeCommand('github1s.views.settings.focus');
- }
- throw new RequestInvalidTokenError(data.message, token);
- });
- }
- if (response.status === 404) {
- throw new RequestNotFoundError('Not Found', token);
- }
- throw new RequestError(
- (await response.json().catch(noop))?.message ||
- `GitHub1s: Request got HTTP ${response.status} response`,
- token
- );
-});
diff --git a/extensions/github1s/src/helpers/func.ts b/extensions/github1s/src/helpers/func.ts
index d60eeda2e..b3f2e78c5 100644
--- a/extensions/github1s/src/helpers/func.ts
+++ b/extensions/github1s/src/helpers/func.ts
@@ -4,7 +4,7 @@
*/
import * as jsonStableStringify from 'json-stable-stringify';
-import * as pFinally from 'p-finally';
+import pFinally from 'p-finally';
const defaultComputeCacheKey = (...args) => jsonStableStringify([...args]);
@@ -12,14 +12,14 @@ const defaultComputeCacheKey = (...args) => jsonStableStringify([...args]);
// and previous request not completed
export const reuseable = Promise>(
func: T,
- computeCacheKey: (...args: Parameters) => string = defaultComputeCacheKey
+ computeCacheKey: (...args: Parameters) => string = defaultComputeCacheKey,
) => {
const cache = new Map>();
return function (...args: Parameters): ReturnType {
const key = computeCacheKey(...args);
if (cache.has(key)) {
- return cache.get(key);
+ return cache.get(key)!;
}
const promise = func.call(this, ...args);
@@ -28,12 +28,9 @@ export const reuseable = Promise>(
};
};
-export const throttle = any>(
- func: T,
- interval: number
-) => {
- let timer = null;
- return function (...args: Parameters): ReturnType {
+export const throttle = any>(func: T, interval: number) => {
+ let timer: ReturnType | null = null;
+ return function (...args: Parameters): ReturnType | undefined {
if (timer) {
return;
}
@@ -42,24 +39,18 @@ export const throttle = any>(
};
};
-export const debounce = any>(
- func: T,
- wait: number
-) => {
- let timer = null;
+export const debounce = any>(func: T, wait: number) => {
+ let timer: ReturnType | null = null;
return function (...args: Parameters): void {
timer && clearTimeout(timer);
- timer = setTimeout(() => func.call(this, ...args), timer);
+ timer = setTimeout(() => func.call(this, ...args), wait);
};
};
// debounce an async func. once an async func canceled, it throws a exception
-export const debounceAsyncFunc = Promise>(
- func: T,
- wait: number
-) => {
- let timer = null;
- let previousReject = null;
+export const debounceAsyncFunc = Promise>(func: T, wait: number) => {
+ let timer: ReturnType | null = null;
+ let previousReject: (() => any) | null = null;
return function (...args: Parameters): ReturnType {
return new Promise((resolve, reject) => {
timer && clearTimeout(timer);
@@ -69,3 +60,29 @@ export const debounceAsyncFunc = Promise>(
}) as ReturnType;
};
};
+
+// memorize function result, beware of memory leaks!
+export const memorize = any>(
+ func: T,
+ computeCacheKey: (...args: Parameters) => string = defaultComputeCacheKey,
+) => {
+ const cache = new Map>();
+
+ return function (...args: Parameters): ReturnType {
+ const key = computeCacheKey(...args);
+ if (cache.has(key)) {
+ return cache.get(key)!;
+ }
+
+ const result = func.call(this, ...args);
+ cache.set(key, result);
+ return result;
+ };
+};
+
+export const decorate = unknown>(transformer: (func: F) => F) => {
+ return