mirror of https://github.com/coder/code-server.git
Merge branch 'main' into vscode-1.56
This commit is contained in:
commit
a631d19636
|
@ -406,3 +406,61 @@ jobs:
|
||||||
with:
|
with:
|
||||||
name: release-images
|
name: release-images
|
||||||
path: ./release-images
|
path: ./release-images
|
||||||
|
|
||||||
|
trivy-scan-image:
|
||||||
|
runs-on: ubuntu-20.04
|
||||||
|
needs: docker-amd64
|
||||||
|
|
||||||
|
steps:
|
||||||
|
- name: Checkout code
|
||||||
|
uses: actions/checkout@v2
|
||||||
|
|
||||||
|
- name: Download release images
|
||||||
|
uses: actions/download-artifact@v2
|
||||||
|
with:
|
||||||
|
name: release-images
|
||||||
|
path: ./release-images
|
||||||
|
|
||||||
|
- name: Run Trivy vulnerability scanner in image mode
|
||||||
|
# Commit SHA for v0.0.14
|
||||||
|
uses: aquasecurity/trivy-action@b38389f8efef9798810fe0c5b5096ac198cffd54
|
||||||
|
with:
|
||||||
|
input: "./release-images/code-server-amd64-*.tar"
|
||||||
|
scan-type: "image"
|
||||||
|
ignore-unfixed: true
|
||||||
|
format: "template"
|
||||||
|
template: "@/contrib/sarif.tpl"
|
||||||
|
output: "trivy-image-results.sarif"
|
||||||
|
severity: "HIGH,CRITICAL"
|
||||||
|
|
||||||
|
- name: Upload Trivy scan results to GitHub Security tab
|
||||||
|
uses: github/codeql-action/upload-sarif@v1
|
||||||
|
with:
|
||||||
|
sarif_file: "trivy-image-results.sarif"
|
||||||
|
|
||||||
|
# We have to use two trivy jobs
|
||||||
|
# because GitHub only allows
|
||||||
|
# codeql/upload-sarif action per job
|
||||||
|
trivy-scan-repo:
|
||||||
|
runs-on: ubuntu-20.04
|
||||||
|
|
||||||
|
steps:
|
||||||
|
- name: Checkout code
|
||||||
|
uses: actions/checkout@v2
|
||||||
|
|
||||||
|
- name: Run Trivy vulnerability scanner in repo mode
|
||||||
|
# Commit SHA for v0.0.14
|
||||||
|
uses: aquasecurity/trivy-action@b38389f8efef9798810fe0c5b5096ac198cffd54
|
||||||
|
with:
|
||||||
|
scan-type: "fs"
|
||||||
|
scan-ref: "."
|
||||||
|
ignore-unfixed: true
|
||||||
|
format: "template"
|
||||||
|
template: "@/contrib/sarif.tpl"
|
||||||
|
output: "trivy-repo-results.sarif"
|
||||||
|
severity: "HIGH,CRITICAL"
|
||||||
|
|
||||||
|
- name: Upload Trivy scan results to GitHub Security tab
|
||||||
|
uses: github/codeql-action/upload-sarif@v1
|
||||||
|
with:
|
||||||
|
sarif_file: "trivy-repo-results.sarif"
|
||||||
|
|
|
@ -19,9 +19,15 @@ main() {
|
||||||
fi
|
fi
|
||||||
|
|
||||||
if ! [ -f ./lib/coder-cloud-agent ]; then
|
if ! [ -f ./lib/coder-cloud-agent ]; then
|
||||||
|
echo "Downloading the cloud agent..."
|
||||||
|
|
||||||
|
# for arch; we do not use OS from lib.sh and get our own.
|
||||||
|
# lib.sh normalizes macos to darwin - but cloud-agent's binaries do not
|
||||||
|
source ./ci/lib.sh
|
||||||
OS="$(uname | tr '[:upper:]' '[:lower:]')"
|
OS="$(uname | tr '[:upper:]' '[:lower:]')"
|
||||||
|
|
||||||
set +e
|
set +e
|
||||||
curl -fsSL "https://storage.googleapis.com/coder-cloud-releases/agent/latest/$OS/cloud-agent" -o ./lib/coder-cloud-agent
|
curl -fsSL "https://github.com/cdr/cloud-agent/releases/latest/download/cloud-agent-$OS-$ARCH" -o ./lib/coder-cloud-agent
|
||||||
chmod +x ./lib/coder-cloud-agent
|
chmod +x ./lib/coder-cloud-agent
|
||||||
set -e
|
set -e
|
||||||
fi
|
fi
|
||||||
|
|
|
@ -5,7 +5,9 @@
|
||||||
- [Maintaining](#maintaining)
|
- [Maintaining](#maintaining)
|
||||||
- [Workflow](#workflow)
|
- [Workflow](#workflow)
|
||||||
- [Milestones](#milestones)
|
- [Milestones](#milestones)
|
||||||
|
- [Triage](#triage)
|
||||||
- [Project Boards](#project-boards)
|
- [Project Boards](#project-boards)
|
||||||
|
- [Versioning](#versioning)
|
||||||
|
|
||||||
<!-- END doctoc generated TOC please keep comment here to allow auto update -->
|
<!-- END doctoc generated TOC please keep comment here to allow auto update -->
|
||||||
|
|
||||||
|
@ -36,6 +38,18 @@ Here are the milestones we use and how we use them:
|
||||||
|
|
||||||
With this flow, any un-assigned issues are essentially in triage state and once triaged are either "Backlog" or "Backlog Candidates". They will eventually move to "On Deck" (or be closed). Lastly, they will end up on a version milestone where they will be worked on.
|
With this flow, any un-assigned issues are essentially in triage state and once triaged are either "Backlog" or "Backlog Candidates". They will eventually move to "On Deck" (or be closed). Lastly, they will end up on a version milestone where they will be worked on.
|
||||||
|
|
||||||
|
### Triage
|
||||||
|
|
||||||
|
We use the following process for triaging GitHub issues:
|
||||||
|
|
||||||
|
1. a submitter creates an issue
|
||||||
|
1. add appropriate labels
|
||||||
|
1. if we need to look into it further, add "needs-investigation"
|
||||||
|
1. add to milestone
|
||||||
|
1. if it should be fixed soon, add to version milestone or "On Deck"
|
||||||
|
1. if not urgent, add to "Backlog"
|
||||||
|
1. otherwise, add to "Backlog Candidate" if it should be considered
|
||||||
|
|
||||||
### Project Boards
|
### Project Boards
|
||||||
|
|
||||||
We use project boards for projects or goals that span multiple milestones.
|
We use project boards for projects or goals that span multiple milestones.
|
||||||
|
@ -43,3 +57,9 @@ We use project boards for projects or goals that span multiple milestones.
|
||||||
Think of this as a place to put miscellaneous things (like testing, clean up stuff, etc). As a maintainer, random todos may come up here and there. This gives you a place to add notes temporarily before opening a new issue. Given that our release milestones function off of issues, we believe tasks should have dedicated issues.
|
Think of this as a place to put miscellaneous things (like testing, clean up stuff, etc). As a maintainer, random todos may come up here and there. This gives you a place to add notes temporarily before opening a new issue. Given that our release milestones function off of issues, we believe tasks should have dedicated issues.
|
||||||
|
|
||||||
It also gives us a way to separate the issue triage from bigger-picture, long-term work.
|
It also gives us a way to separate the issue triage from bigger-picture, long-term work.
|
||||||
|
|
||||||
|
## Versioning
|
||||||
|
|
||||||
|
`<major.minor.patch>`
|
||||||
|
|
||||||
|
The code-server project follows traditional [semantic versioning](ttps://semver.org/), with the objective of minimizing major changes that break backward compatibility. We increment the patch level for all releases, except when the upstream Visual Studio Code project increments its minor version or we change the plugin API in a backward-compatible manner. In those cases, we increment the minor version rather than the patch level.
|
||||||
|
|
|
@ -19,3 +19,4 @@
|
||||||
# These are code-server code symlinks.
|
# These are code-server code symlinks.
|
||||||
src/vs/base/node/proxy_agent.ts
|
src/vs/base/node/proxy_agent.ts
|
||||||
src/vs/ipc.d.ts
|
src/vs/ipc.d.ts
|
||||||
|
src/vs/server/common/util.ts
|
||||||
|
|
|
@ -1,8 +1,10 @@
|
||||||
import * as path from 'vs/base/common/path';
|
import * as path from 'vs/base/common/path';
|
||||||
import { URI } from 'vs/base/common/uri';
|
|
||||||
import { Options } from 'vs/ipc';
|
import { Options } from 'vs/ipc';
|
||||||
import { localize } from 'vs/nls';
|
import { localize } from 'vs/nls';
|
||||||
|
import { MenuId, MenuRegistry } from 'vs/platform/actions/common/actions';
|
||||||
|
import { CommandsRegistry } from 'vs/platform/commands/common/commands';
|
||||||
import { Extensions, IConfigurationRegistry } from 'vs/platform/configuration/common/configurationRegistry';
|
import { Extensions, IConfigurationRegistry } from 'vs/platform/configuration/common/configurationRegistry';
|
||||||
|
import { ContextKeyExpr, IContextKeyService } from 'vs/platform/contextkey/common/contextkey';
|
||||||
import { registerSingleton } from 'vs/platform/instantiation/common/extensions';
|
import { registerSingleton } from 'vs/platform/instantiation/common/extensions';
|
||||||
import { ServiceCollection } from 'vs/platform/instantiation/common/serviceCollection';
|
import { ServiceCollection } from 'vs/platform/instantiation/common/serviceCollection';
|
||||||
import { ILogService } from 'vs/platform/log/common/log';
|
import { ILogService } from 'vs/platform/log/common/log';
|
||||||
|
@ -11,10 +13,18 @@ import { Registry } from 'vs/platform/registry/common/platform';
|
||||||
import { IStorageService, StorageScope, StorageTarget } from 'vs/platform/storage/common/storage';
|
import { IStorageService, StorageScope, StorageTarget } from 'vs/platform/storage/common/storage';
|
||||||
import { ITelemetryService } from 'vs/platform/telemetry/common/telemetry';
|
import { ITelemetryService } from 'vs/platform/telemetry/common/telemetry';
|
||||||
import { TelemetryChannelClient } from 'vs/server/common/telemetry';
|
import { TelemetryChannelClient } from 'vs/server/common/telemetry';
|
||||||
|
import { getOptions } from 'vs/server/common/util';
|
||||||
import 'vs/workbench/contrib/localizations/browser/localizations.contribution';
|
import 'vs/workbench/contrib/localizations/browser/localizations.contribution';
|
||||||
import 'vs/workbench/services/localizations/browser/localizationsService';
|
import 'vs/workbench/services/localizations/browser/localizationsService';
|
||||||
import { IRemoteAgentService } from 'vs/workbench/services/remote/common/remoteAgentService';
|
import { IRemoteAgentService } from 'vs/workbench/services/remote/common/remoteAgentService';
|
||||||
|
|
||||||
|
/**
|
||||||
|
* All client-side customization to VS Code should live in this file when
|
||||||
|
* possible.
|
||||||
|
*/
|
||||||
|
|
||||||
|
const options = getOptions<Options>();
|
||||||
|
|
||||||
class TelemetryService extends TelemetryChannelClient {
|
class TelemetryService extends TelemetryChannelClient {
|
||||||
public constructor(
|
public constructor(
|
||||||
@IRemoteAgentService remoteAgentService: IRemoteAgentService,
|
@IRemoteAgentService remoteAgentService: IRemoteAgentService,
|
||||||
|
@ -23,26 +33,6 @@ class TelemetryService extends TelemetryChannelClient {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
|
||||||
* Remove extra slashes in a URL.
|
|
||||||
*/
|
|
||||||
export const normalize = (url: string, keepTrailing = false): string => {
|
|
||||||
return url.replace(/\/\/+/g, '/').replace(/\/+$/, keepTrailing ? '/' : '');
|
|
||||||
};
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Get options embedded in the HTML.
|
|
||||||
*/
|
|
||||||
export const getOptions = <T extends Options>(): T => {
|
|
||||||
try {
|
|
||||||
return JSON.parse(document.getElementById('coder-options')!.getAttribute('data-settings')!);
|
|
||||||
} catch (error) {
|
|
||||||
return {} as T;
|
|
||||||
}
|
|
||||||
};
|
|
||||||
|
|
||||||
const options = getOptions();
|
|
||||||
|
|
||||||
const TELEMETRY_SECTION_ID = 'telemetry';
|
const TELEMETRY_SECTION_ID = 'telemetry';
|
||||||
Registry.as<IConfigurationRegistry>(Extensions.Configuration).registerConfiguration({
|
Registry.as<IConfigurationRegistry>(Extensions.Configuration).registerConfiguration({
|
||||||
'id': TELEMETRY_SECTION_ID,
|
'id': TELEMETRY_SECTION_ID,
|
||||||
|
@ -173,38 +163,36 @@ export const initialize = async (services: ServiceCollection): Promise<void> =>
|
||||||
if (theme) {
|
if (theme) {
|
||||||
localStorage.setItem('colorThemeData', theme);
|
localStorage.setItem('colorThemeData', theme);
|
||||||
}
|
}
|
||||||
};
|
|
||||||
|
|
||||||
export interface Query {
|
// Use to show or hide logout commands and menu options.
|
||||||
[key: string]: string | undefined;
|
const contextKeyService = (services.get(IContextKeyService) as IContextKeyService);
|
||||||
}
|
contextKeyService.createKey('code-server.authed', options.authed);
|
||||||
|
|
||||||
/**
|
// Add a logout command.
|
||||||
* Split a string up to the delimiter. If the delimiter doesn't exist the first
|
const logoutEndpoint = path.join(options.base, '/logout') + `?base=${options.base}`;
|
||||||
* item will have all the text and the second item will be an empty string.
|
const LOGOUT_COMMAND_ID = 'code-server.logout';
|
||||||
*/
|
CommandsRegistry.registerCommand(
|
||||||
export const split = (str: string, delimiter: string): [string, string] => {
|
LOGOUT_COMMAND_ID,
|
||||||
const index = str.indexOf(delimiter);
|
() => {
|
||||||
return index !== -1 ? [str.substring(0, index).trim(), str.substring(index + 1)] : [str, ''];
|
window.location.href = logoutEndpoint;
|
||||||
};
|
},
|
||||||
|
);
|
||||||
|
|
||||||
/**
|
// Add logout to command palette.
|
||||||
* Return the URL modified with the specified query variables. It's pretty
|
MenuRegistry.appendMenuItem(MenuId.CommandPalette, {
|
||||||
* stupid so it probably doesn't cover any edge cases. Undefined values will
|
command: {
|
||||||
* unset existing values. Doesn't allow duplicates.
|
id: LOGOUT_COMMAND_ID,
|
||||||
*/
|
title: localize('logout', "Log out")
|
||||||
export const withQuery = (url: string, replace: Query): string => {
|
},
|
||||||
const uri = URI.parse(url);
|
when: ContextKeyExpr.has('code-server.authed')
|
||||||
const query = { ...replace };
|
});
|
||||||
uri.query.split('&').forEach((kv) => {
|
|
||||||
const [key, value] = split(kv, '=');
|
// Add logout to the (web-only) home menu.
|
||||||
if (!(key in query)) {
|
MenuRegistry.appendMenuItem(MenuId.MenubarHomeMenu, {
|
||||||
query[key] = value;
|
command: {
|
||||||
}
|
id: LOGOUT_COMMAND_ID,
|
||||||
|
title: localize('logout', "Log out")
|
||||||
|
},
|
||||||
|
when: ContextKeyExpr.has('code-server.authed')
|
||||||
});
|
});
|
||||||
return uri.with({
|
|
||||||
query: Object.keys(query)
|
|
||||||
.filter((k) => typeof query[k] !== 'undefined')
|
|
||||||
.map((k) => `${k}=${query[k]}`).join('&'),
|
|
||||||
}).toString(true);
|
|
||||||
};
|
};
|
||||||
|
|
|
@ -1,3 +0,0 @@
|
||||||
export enum Cookie {
|
|
||||||
Key = 'key',
|
|
||||||
}
|
|
|
@ -0,0 +1 @@
|
||||||
|
../../../../../../src/common/util.ts
|
|
@ -9,7 +9,7 @@ import { registerThemingParticipant, IThemeService } from 'vs/platform/theme/com
|
||||||
import { MenuBarVisibility, getTitleBarStyle, IWindowOpenable, getMenuBarVisibility } from 'vs/platform/windows/common/windows';
|
import { MenuBarVisibility, getTitleBarStyle, IWindowOpenable, getMenuBarVisibility } from 'vs/platform/windows/common/windows';
|
||||||
import { IContextKeyService } from 'vs/platform/contextkey/common/contextkey';
|
import { IContextKeyService } from 'vs/platform/contextkey/common/contextkey';
|
||||||
import { IAction, Action, SubmenuAction, Separator } from 'vs/base/common/actions';
|
import { IAction, Action, SubmenuAction, Separator } from 'vs/base/common/actions';
|
||||||
import { addDisposableListener, Dimension, EventType, getCookieValue } from 'vs/base/browser/dom';
|
import { addDisposableListener, Dimension, EventType } from 'vs/base/browser/dom';
|
||||||
import { IKeybindingService } from 'vs/platform/keybinding/common/keybinding';
|
import { IKeybindingService } from 'vs/platform/keybinding/common/keybinding';
|
||||||
import { isMacintosh, isWeb, isIOS, isNative } from 'vs/base/common/platform';
|
import { isMacintosh, isWeb, isIOS, isNative } from 'vs/base/common/platform';
|
||||||
import { IConfigurationService, IConfigurationChangeEvent } from 'vs/platform/configuration/common/configuration';
|
import { IConfigurationService, IConfigurationChangeEvent } from 'vs/platform/configuration/common/configuration';
|
||||||
|
@ -38,8 +38,6 @@ import { KeyCode } from 'vs/base/common/keyCodes';
|
||||||
import { KeybindingWeight } from 'vs/platform/keybinding/common/keybindingsRegistry';
|
import { KeybindingWeight } from 'vs/platform/keybinding/common/keybindingsRegistry';
|
||||||
import { IsWebContext } from 'vs/platform/contextkey/common/contextkeys';
|
import { IsWebContext } from 'vs/platform/contextkey/common/contextkeys';
|
||||||
import { ICommandService } from 'vs/platform/commands/common/commands';
|
import { ICommandService } from 'vs/platform/commands/common/commands';
|
||||||
import { ILogService } from 'vs/platform/log/common/log';
|
|
||||||
import { Cookie } from 'vs/server/common/cookie';
|
|
||||||
|
|
||||||
export type IOpenRecentAction = IAction & { uri: URI, remoteAuthority?: string };
|
export type IOpenRecentAction = IAction & { uri: URI, remoteAuthority?: string };
|
||||||
|
|
||||||
|
@ -303,7 +301,6 @@ export class CustomMenubarControl extends MenubarControl {
|
||||||
private readonly _onVisibilityChange: Emitter<boolean>;
|
private readonly _onVisibilityChange: Emitter<boolean>;
|
||||||
private readonly _onFocusStateChange: Emitter<boolean>;
|
private readonly _onFocusStateChange: Emitter<boolean>;
|
||||||
|
|
||||||
// NOTE@coder: add logService (used in logout)
|
|
||||||
constructor(
|
constructor(
|
||||||
@IMenuService menuService: IMenuService,
|
@IMenuService menuService: IMenuService,
|
||||||
@IWorkspacesService workspacesService: IWorkspacesService,
|
@IWorkspacesService workspacesService: IWorkspacesService,
|
||||||
|
@ -320,8 +317,7 @@ export class CustomMenubarControl extends MenubarControl {
|
||||||
@IThemeService private readonly themeService: IThemeService,
|
@IThemeService private readonly themeService: IThemeService,
|
||||||
@IWorkbenchLayoutService private readonly layoutService: IWorkbenchLayoutService,
|
@IWorkbenchLayoutService private readonly layoutService: IWorkbenchLayoutService,
|
||||||
@IHostService hostService: IHostService,
|
@IHostService hostService: IHostService,
|
||||||
@ICommandService commandService: ICommandService,
|
@ICommandService commandService: ICommandService
|
||||||
@ILogService private readonly logService: ILogService
|
|
||||||
) {
|
) {
|
||||||
super(menuService, workspacesService, contextKeyService, keybindingService, configurationService, labelService, updateService, storageService, notificationService, preferencesService, environmentService, accessibilityService, hostService, commandService);
|
super(menuService, workspacesService, contextKeyService, keybindingService, configurationService, labelService, updateService, storageService, notificationService, preferencesService, environmentService, accessibilityService, hostService, commandService);
|
||||||
|
|
||||||
|
@ -723,28 +719,6 @@ export class CustomMenubarControl extends MenubarControl {
|
||||||
webNavigationActions.pop();
|
webNavigationActions.pop();
|
||||||
}
|
}
|
||||||
|
|
||||||
webNavigationActions.push(new Action('logout', localize('logout', "Log out"), undefined, true,
|
|
||||||
async (_event?: any) => {
|
|
||||||
const COOKIE_KEY = Cookie.Key;
|
|
||||||
const loginCookie = getCookieValue(COOKIE_KEY);
|
|
||||||
|
|
||||||
this.logService.info('Logging out of code-server');
|
|
||||||
|
|
||||||
if(loginCookie) {
|
|
||||||
this.logService.info(`Removing cookie under ${COOKIE_KEY}`);
|
|
||||||
|
|
||||||
if (document && document.cookie) {
|
|
||||||
// We delete the cookie by setting the expiration to a date/time in the past
|
|
||||||
document.cookie = COOKIE_KEY +'=; Path=/; Expires=Thu, 01 Jan 1970 00:00:01 GMT;';
|
|
||||||
window.location.href = '/login';
|
|
||||||
} else {
|
|
||||||
this.logService.warn('Could not delete cookie because document and/or document.cookie is undefined');
|
|
||||||
}
|
|
||||||
} else {
|
|
||||||
this.logService.warn('Could not log out because we could not find cookie');
|
|
||||||
}
|
|
||||||
}));
|
|
||||||
|
|
||||||
return webNavigationActions;
|
return webNavigationActions;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -48,14 +48,14 @@
|
||||||
"@types/proxy-from-env": "^1.0.1",
|
"@types/proxy-from-env": "^1.0.1",
|
||||||
"@types/safe-compare": "^1.1.0",
|
"@types/safe-compare": "^1.1.0",
|
||||||
"@types/semver": "^7.1.0",
|
"@types/semver": "^7.1.0",
|
||||||
"@types/split2": "^2.1.6",
|
"@types/split2": "^3.2.0",
|
||||||
"@types/tar-fs": "^2.0.0",
|
"@types/tar-fs": "^2.0.0",
|
||||||
"@types/tar-stream": "^2.1.0",
|
"@types/tar-stream": "^2.1.0",
|
||||||
"@types/ws": "^7.2.6",
|
"@types/ws": "^7.2.6",
|
||||||
"@types/wtfnode": "^0.7.0",
|
"@types/wtfnode": "^0.7.0",
|
||||||
"@typescript-eslint/eslint-plugin": "^4.7.0",
|
"@typescript-eslint/eslint-plugin": "^4.7.0",
|
||||||
"@typescript-eslint/parser": "^4.7.0",
|
"@typescript-eslint/parser": "^4.7.0",
|
||||||
"audit-ci": "^3.1.1",
|
"audit-ci": "^4.0.0",
|
||||||
"codecov": "^3.8.1",
|
"codecov": "^3.8.1",
|
||||||
"doctoc": "^2.0.0",
|
"doctoc": "^2.0.0",
|
||||||
"eslint": "^7.7.0",
|
"eslint": "^7.7.0",
|
||||||
|
|
|
@ -1,3 +1,4 @@
|
||||||
|
import { logger } from "@coder/logger"
|
||||||
import { getOptions, normalize, logError } from "../common/util"
|
import { getOptions, normalize, logError } from "../common/util"
|
||||||
|
|
||||||
import "./pages/error.css"
|
import "./pages/error.css"
|
||||||
|
@ -6,19 +7,21 @@ import "./pages/login.css"
|
||||||
|
|
||||||
export async function registerServiceWorker(): Promise<void> {
|
export async function registerServiceWorker(): Promise<void> {
|
||||||
const options = getOptions()
|
const options = getOptions()
|
||||||
|
logger.level = options.logLevel
|
||||||
|
|
||||||
const path = normalize(`${options.csStaticBase}/dist/serviceWorker.js`)
|
const path = normalize(`${options.csStaticBase}/dist/serviceWorker.js`)
|
||||||
try {
|
try {
|
||||||
await navigator.serviceWorker.register(path, {
|
await navigator.serviceWorker.register(path, {
|
||||||
scope: options.base + "/",
|
scope: options.base + "/",
|
||||||
})
|
})
|
||||||
console.log("[Service Worker] registered")
|
logger.info(`[Service Worker] registered`)
|
||||||
} catch (error) {
|
} catch (error) {
|
||||||
logError(`[Service Worker] registration`, error)
|
logError(logger, `[Service Worker] registration`, error)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
if (typeof navigator !== "undefined" && "serviceWorker" in navigator) {
|
if (typeof navigator !== "undefined" && "serviceWorker" in navigator) {
|
||||||
registerServiceWorker()
|
registerServiceWorker()
|
||||||
} else {
|
} else {
|
||||||
console.error(`[Service Worker] navigator is undefined`)
|
logger.error(`[Service Worker] navigator is undefined`)
|
||||||
}
|
}
|
||||||
|
|
|
@ -1,5 +1,13 @@
|
||||||
import { logger, field } from "@coder/logger"
|
/*
|
||||||
|
* This file exists in two locations:
|
||||||
|
* - src/common/util.ts
|
||||||
|
* - lib/vscode/src/vs/server/common/util.ts
|
||||||
|
* The second is a symlink to the first.
|
||||||
|
*/
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Base options included on every page.
|
||||||
|
*/
|
||||||
export interface Options {
|
export interface Options {
|
||||||
base: string
|
base: string
|
||||||
csStaticBase: string
|
csStaticBase: string
|
||||||
|
@ -69,6 +77,9 @@ export const getOptions = <T extends Options>(): T => {
|
||||||
options = {} as T
|
options = {} as T
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// You can also pass options in stringified form to the options query
|
||||||
|
// variable. Options provided here will override the ones in the options
|
||||||
|
// element.
|
||||||
const params = new URLSearchParams(location.search)
|
const params = new URLSearchParams(location.search)
|
||||||
const queryOpts = params.get("options")
|
const queryOpts = params.get("options")
|
||||||
if (queryOpts) {
|
if (queryOpts) {
|
||||||
|
@ -78,13 +89,9 @@ export const getOptions = <T extends Options>(): T => {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
logger.level = options.logLevel
|
|
||||||
|
|
||||||
options.base = resolveBase(options.base)
|
options.base = resolveBase(options.base)
|
||||||
options.csStaticBase = resolveBase(options.csStaticBase)
|
options.csStaticBase = resolveBase(options.csStaticBase)
|
||||||
|
|
||||||
logger.debug("got options", field("options", options))
|
|
||||||
|
|
||||||
return options
|
return options
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -113,7 +120,8 @@ export const getFirstString = (value: string | string[] | object | undefined): s
|
||||||
return typeof value === "string" ? value : undefined
|
return typeof value === "string" ? value : undefined
|
||||||
}
|
}
|
||||||
|
|
||||||
export function logError(prefix: string, err: any): void {
|
// TODO: Might make sense to add Error handling to the logger itself.
|
||||||
|
export function logError(logger: { error: (msg: string) => void }, prefix: string, err: Error | string): void {
|
||||||
if (err instanceof Error) {
|
if (err instanceof Error) {
|
||||||
logger.error(`${prefix}: ${err.message} ${err.stack}`)
|
logger.error(`${prefix}: ${err.message} ${err.stack}`)
|
||||||
} else {
|
} else {
|
||||||
|
|
|
@ -37,7 +37,7 @@ export const createApp = async (args: DefaultedArgs): Promise<[Express, Express,
|
||||||
reject(err)
|
reject(err)
|
||||||
} else {
|
} else {
|
||||||
// Promise resolved earlier so this is an unrelated error.
|
// Promise resolved earlier so this is an unrelated error.
|
||||||
util.logError("http server error", err)
|
util.logError(logger, "http server error", err)
|
||||||
}
|
}
|
||||||
})
|
})
|
||||||
|
|
||||||
|
|
|
@ -20,6 +20,7 @@ import * as apps from "./apps"
|
||||||
import * as domainProxy from "./domainProxy"
|
import * as domainProxy from "./domainProxy"
|
||||||
import * as health from "./health"
|
import * as health from "./health"
|
||||||
import * as login from "./login"
|
import * as login from "./login"
|
||||||
|
import * as logout from "./logout"
|
||||||
import * as pathProxy from "./pathProxy"
|
import * as pathProxy from "./pathProxy"
|
||||||
// static is a reserved keyword.
|
// static is a reserved keyword.
|
||||||
import * as _static from "./static"
|
import * as _static from "./static"
|
||||||
|
@ -136,10 +137,10 @@ export const register = async (
|
||||||
|
|
||||||
if (args.auth === AuthType.Password) {
|
if (args.auth === AuthType.Password) {
|
||||||
app.use("/login", login.router)
|
app.use("/login", login.router)
|
||||||
|
app.use("/logout", logout.router)
|
||||||
} else {
|
} else {
|
||||||
app.all("/login", (req, res) => {
|
app.all("/login", (req, res) => redirect(req, res, "/", {}))
|
||||||
redirect(req, res, "/", {})
|
app.all("/logout", (req, res) => redirect(req, res, "/", {}))
|
||||||
})
|
|
||||||
}
|
}
|
||||||
|
|
||||||
app.use("/static", _static.router)
|
app.use("/static", _static.router)
|
||||||
|
|
|
@ -0,0 +1,17 @@
|
||||||
|
import { Router } from "express"
|
||||||
|
import { getCookieDomain, redirect } from "../http"
|
||||||
|
import { Cookie } from "./login"
|
||||||
|
|
||||||
|
export const router = Router()
|
||||||
|
|
||||||
|
router.get("/", async (req, res) => {
|
||||||
|
// Must use the *identical* properties used to set the cookie.
|
||||||
|
res.clearCookie(Cookie.Key, {
|
||||||
|
domain: getCookieDomain(req.headers.host || "", req.args["proxy-domain"]),
|
||||||
|
path: req.body.base || "/",
|
||||||
|
sameSite: "lax",
|
||||||
|
})
|
||||||
|
|
||||||
|
const to = (typeof req.query.to === "string" && req.query.to) || "/"
|
||||||
|
return redirect(req, res, to, { to: undefined, base: undefined })
|
||||||
|
})
|
|
@ -3,6 +3,7 @@ import { Request, Router } from "express"
|
||||||
import { promises as fs } from "fs"
|
import { promises as fs } from "fs"
|
||||||
import * as path from "path"
|
import * as path from "path"
|
||||||
import qs from "qs"
|
import qs from "qs"
|
||||||
|
import * as ipc from "../../../typings/ipc"
|
||||||
import { Emitter } from "../../common/emitter"
|
import { Emitter } from "../../common/emitter"
|
||||||
import { HttpCode, HttpError } from "../../common/http"
|
import { HttpCode, HttpError } from "../../common/http"
|
||||||
import { getFirstString } from "../../common/util"
|
import { getFirstString } from "../../common/util"
|
||||||
|
@ -39,12 +40,13 @@ router.get("/", async (req, res) => {
|
||||||
options.productConfiguration.codeServerVersion = version
|
options.productConfiguration.codeServerVersion = version
|
||||||
|
|
||||||
res.send(
|
res.send(
|
||||||
replaceTemplates(
|
replaceTemplates<ipc.Options>(
|
||||||
req,
|
req,
|
||||||
// Uncomment prod blocks if not in development. TODO: Would this be
|
// Uncomment prod blocks if not in development. TODO: Would this be
|
||||||
// better as a build step? Or maintain two HTML files again?
|
// better as a build step? Or maintain two HTML files again?
|
||||||
commit !== "development" ? content.replace(/<!-- PROD_ONLY/g, "").replace(/END_PROD_ONLY -->/g, "") : content,
|
commit !== "development" ? content.replace(/<!-- PROD_ONLY/g, "").replace(/END_PROD_ONLY -->/g, "") : content,
|
||||||
{
|
{
|
||||||
|
authed: req.args.auth !== "none",
|
||||||
disableTelemetry: !!req.args["disable-telemetry"],
|
disableTelemetry: !!req.args["disable-telemetry"],
|
||||||
disableUpdateCheck: !!req.args["disable-update-check"],
|
disableUpdateCheck: !!req.args["disable-update-check"],
|
||||||
},
|
},
|
||||||
|
|
|
@ -52,6 +52,7 @@ const config: Config = {
|
||||||
testDir: path.join(__dirname, "e2e"), // Search for tests in this directory.
|
testDir: path.join(__dirname, "e2e"), // Search for tests in this directory.
|
||||||
timeout: 60000, // Each test is given 60 seconds.
|
timeout: 60000, // Each test is given 60 seconds.
|
||||||
retries: 3, // Retry failing tests 2 times
|
retries: 3, // Retry failing tests 2 times
|
||||||
|
workers: 1,
|
||||||
}
|
}
|
||||||
|
|
||||||
if (process.env.CI) {
|
if (process.env.CI) {
|
||||||
|
|
|
@ -38,6 +38,10 @@ test.describe("CodeServer", () => {
|
||||||
expect(await codeServer.isEditorVisible()).toBe(true)
|
expect(await codeServer.isEditorVisible()).toBe(true)
|
||||||
})
|
})
|
||||||
|
|
||||||
|
test("should always have a connection", options, async ({ page }) => {
|
||||||
|
expect(await codeServer.isConnected()).toBe(true)
|
||||||
|
})
|
||||||
|
|
||||||
test("should show the Integrated Terminal", options, async ({ page }) => {
|
test("should show the Integrated Terminal", options, async ({ page }) => {
|
||||||
await codeServer.focusTerminal()
|
await codeServer.focusTerminal()
|
||||||
expect(await page.isVisible("#terminal")).toBe(true)
|
expect(await page.isVisible("#terminal")).toBe(true)
|
||||||
|
|
|
@ -30,7 +30,7 @@ test.describe("login", () => {
|
||||||
await page.waitForLoadState("networkidle")
|
await page.waitForLoadState("networkidle")
|
||||||
// We do this because occassionally code-server doesn't load on Firefox
|
// We do this because occassionally code-server doesn't load on Firefox
|
||||||
// but loads if you reload once or twice
|
// but loads if you reload once or twice
|
||||||
await codeServer.reloadUntilEditorIsVisible()
|
await codeServer.reloadUntilEditorIsReady()
|
||||||
// Make sure the editor actually loaded
|
// Make sure the editor actually loaded
|
||||||
expect(await codeServer.isEditorVisible()).toBe(true)
|
expect(await codeServer.isEditorVisible()).toBe(true)
|
||||||
})
|
})
|
||||||
|
|
|
@ -25,7 +25,7 @@ test.describe("logout", () => {
|
||||||
await page.waitForLoadState("networkidle")
|
await page.waitForLoadState("networkidle")
|
||||||
// We do this because occassionally code-server doesn't load on Firefox
|
// We do this because occassionally code-server doesn't load on Firefox
|
||||||
// but loads if you reload once or twice
|
// but loads if you reload once or twice
|
||||||
await codeServer.reloadUntilEditorIsVisible()
|
await codeServer.reloadUntilEditorIsReady()
|
||||||
// Make sure the editor actually loaded
|
// Make sure the editor actually loaded
|
||||||
expect(await codeServer.isEditorVisible()).toBe(true)
|
expect(await codeServer.isEditorVisible()).toBe(true)
|
||||||
|
|
||||||
|
|
|
@ -20,17 +20,20 @@ export class CodeServer {
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Checks if the editor is visible
|
* Checks if the editor is visible
|
||||||
* and reloads until it is
|
* and that we are connected to the host
|
||||||
|
*
|
||||||
|
* Reload until both checks pass
|
||||||
*/
|
*/
|
||||||
async reloadUntilEditorIsVisible() {
|
async reloadUntilEditorIsReady() {
|
||||||
const editorIsVisible = await this.isEditorVisible()
|
const editorIsVisible = await this.isEditorVisible()
|
||||||
|
const editorIsConnected = await this.isConnected()
|
||||||
let reloadCount = 0
|
let reloadCount = 0
|
||||||
|
|
||||||
// Occassionally code-server timeouts in Firefox
|
// Occassionally code-server timeouts in Firefox
|
||||||
// we're not sure why
|
// we're not sure why
|
||||||
// but usually a reload or two fixes it
|
// but usually a reload or two fixes it
|
||||||
// TODO@jsjoeio @oxy look into Firefox reconnection/timeout issues
|
// TODO@jsjoeio @oxy look into Firefox reconnection/timeout issues
|
||||||
while (!editorIsVisible) {
|
while (!editorIsVisible && !editorIsConnected) {
|
||||||
// When a reload happens, we want to wait for all resources to be
|
// When a reload happens, we want to wait for all resources to be
|
||||||
// loaded completely. Hence why we use that instead of DOMContentLoaded
|
// loaded completely. Hence why we use that instead of DOMContentLoaded
|
||||||
// Read more: https://thisthat.dev/dom-content-loaded-vs-load/
|
// Read more: https://thisthat.dev/dom-content-loaded-vs-load/
|
||||||
|
@ -38,8 +41,8 @@ export class CodeServer {
|
||||||
// Give it an extra second just in case it's feeling extra slow
|
// Give it an extra second just in case it's feeling extra slow
|
||||||
await this.page.waitForTimeout(1000)
|
await this.page.waitForTimeout(1000)
|
||||||
reloadCount += 1
|
reloadCount += 1
|
||||||
if (await this.isEditorVisible()) {
|
if ((await this.isEditorVisible()) && (await this.isConnected)) {
|
||||||
console.log(` Editor became visible after ${reloadCount} reloads`)
|
console.log(` Editor became ready after ${reloadCount} reloads`)
|
||||||
break
|
break
|
||||||
}
|
}
|
||||||
await this.page.reload()
|
await this.page.reload()
|
||||||
|
@ -56,6 +59,19 @@ export class CodeServer {
|
||||||
return await this.page.isVisible(this.editorSelector)
|
return await this.page.isVisible(this.editorSelector)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Checks if the editor is visible
|
||||||
|
*/
|
||||||
|
async isConnected() {
|
||||||
|
await this.page.waitForLoadState("networkidle")
|
||||||
|
|
||||||
|
const host = new URL(CODE_SERVER_ADDRESS).host
|
||||||
|
const hostSelector = `[title="Editing on ${host}"]`
|
||||||
|
await this.page.waitForSelector(hostSelector)
|
||||||
|
|
||||||
|
return await this.page.isVisible(hostSelector)
|
||||||
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Focuses Integrated Terminal
|
* Focuses Integrated Terminal
|
||||||
* by using "Terminal: Focus Terminal"
|
* by using "Terminal: Focus Terminal"
|
||||||
|
@ -90,12 +106,12 @@ export class CodeServer {
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Navigates to CODE_SERVER_ADDRESS
|
* Navigates to CODE_SERVER_ADDRESS
|
||||||
* and reloads until the editor is visible
|
* and reloads until the editor is ready
|
||||||
*
|
*
|
||||||
* Helpful for running before tests
|
* Helpful for running before tests
|
||||||
*/
|
*/
|
||||||
async setup() {
|
async setup() {
|
||||||
await this.navigate()
|
await this.navigate()
|
||||||
await this.reloadUntilEditorIsVisible()
|
await this.reloadUntilEditorIsReady()
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -50,7 +50,7 @@ test.describe("Integrated Terminal", () => {
|
||||||
await codeServer.focusTerminal()
|
await codeServer.focusTerminal()
|
||||||
|
|
||||||
await page.waitForLoadState("load")
|
await page.waitForLoadState("load")
|
||||||
await page.keyboard.type(`echo '${testString}' > '${tmpFile}'`)
|
await page.keyboard.type(`echo ${testString} > ${tmpFile}`)
|
||||||
await page.keyboard.press("Enter")
|
await page.keyboard.press("Enter")
|
||||||
// It may take a second to process
|
// It may take a second to process
|
||||||
await page.waitForTimeout(1000)
|
await page.waitForTimeout(1000)
|
||||||
|
|
|
@ -22,11 +22,11 @@ describe("register", () => {
|
||||||
})
|
})
|
||||||
|
|
||||||
beforeEach(() => {
|
beforeEach(() => {
|
||||||
|
jest.clearAllMocks()
|
||||||
jest.mock("@coder/logger", () => loggerModule)
|
jest.mock("@coder/logger", () => loggerModule)
|
||||||
})
|
})
|
||||||
|
|
||||||
afterEach(() => {
|
afterEach(() => {
|
||||||
mockRegisterFn.mockClear()
|
|
||||||
jest.resetModules()
|
jest.resetModules()
|
||||||
})
|
})
|
||||||
|
|
||||||
|
@ -39,6 +39,7 @@ describe("register", () => {
|
||||||
global.navigator = (undefined as unknown) as Navigator & typeof globalThis
|
global.navigator = (undefined as unknown) as Navigator & typeof globalThis
|
||||||
global.location = (undefined as unknown) as Location & typeof globalThis
|
global.location = (undefined as unknown) as Location & typeof globalThis
|
||||||
})
|
})
|
||||||
|
|
||||||
it("test should have access to browser globals from beforeAll", () => {
|
it("test should have access to browser globals from beforeAll", () => {
|
||||||
expect(typeof global.window).not.toBeFalsy()
|
expect(typeof global.window).not.toBeFalsy()
|
||||||
expect(typeof global.document).not.toBeFalsy()
|
expect(typeof global.document).not.toBeFalsy()
|
||||||
|
@ -74,24 +75,24 @@ describe("register", () => {
|
||||||
})
|
})
|
||||||
|
|
||||||
describe("when navigator and serviceWorker are NOT defined", () => {
|
describe("when navigator and serviceWorker are NOT defined", () => {
|
||||||
let spy: jest.SpyInstance
|
|
||||||
|
|
||||||
beforeEach(() => {
|
beforeEach(() => {
|
||||||
spy = jest.spyOn(console, "error")
|
jest.clearAllMocks()
|
||||||
|
jest.mock("@coder/logger", () => loggerModule)
|
||||||
})
|
})
|
||||||
|
|
||||||
afterAll(() => {
|
afterAll(() => {
|
||||||
jest.restoreAllMocks()
|
jest.restoreAllMocks()
|
||||||
})
|
})
|
||||||
|
|
||||||
it("should log an error to the console", () => {
|
it("should log an error", () => {
|
||||||
// Load service worker like you would in the browser
|
// Load service worker like you would in the browser
|
||||||
require("../../src/browser/register")
|
require("../../src/browser/register")
|
||||||
expect(spy).toHaveBeenCalled()
|
expect(loggerModule.logger.error).toHaveBeenCalled()
|
||||||
expect(spy).toHaveBeenCalledTimes(1)
|
expect(loggerModule.logger.error).toHaveBeenCalledTimes(1)
|
||||||
expect(spy).toHaveBeenCalledWith("[Service Worker] navigator is undefined")
|
expect(loggerModule.logger.error).toHaveBeenCalledWith("[Service Worker] navigator is undefined")
|
||||||
})
|
})
|
||||||
})
|
})
|
||||||
|
|
||||||
describe("registerServiceWorker", () => {
|
describe("registerServiceWorker", () => {
|
||||||
let serviceWorkerPath: string
|
let serviceWorkerPath: string
|
||||||
let serviceWorkerScope: string
|
let serviceWorkerScope: string
|
||||||
|
|
|
@ -5,7 +5,7 @@ import { tmpdir } from "../../src/node/constants"
|
||||||
import { SettingsProvider, UpdateSettings } from "../../src/node/settings"
|
import { SettingsProvider, UpdateSettings } from "../../src/node/settings"
|
||||||
import { LatestResponse, UpdateProvider } from "../../src/node/update"
|
import { LatestResponse, UpdateProvider } from "../../src/node/update"
|
||||||
|
|
||||||
describe.skip("update", () => {
|
describe("update", () => {
|
||||||
let version = "1.0.0"
|
let version = "1.0.0"
|
||||||
let spy: string[] = []
|
let spy: string[] = []
|
||||||
const server = http.createServer((request: http.IncomingMessage, response: http.ServerResponse) => {
|
const server = http.createServer((request: http.IncomingMessage, response: http.ServerResponse) => {
|
||||||
|
@ -75,7 +75,7 @@ describe.skip("update", () => {
|
||||||
await expect(settings.read()).resolves.toEqual({ update })
|
await expect(settings.read()).resolves.toEqual({ update })
|
||||||
expect(isNaN(update.checked)).toEqual(false)
|
expect(isNaN(update.checked)).toEqual(false)
|
||||||
expect(update.checked < Date.now() && update.checked >= now).toEqual(true)
|
expect(update.checked < Date.now() && update.checked >= now).toEqual(true)
|
||||||
expect(update.version).toBe("2.1.0")
|
expect(update.version).toStrictEqual("2.1.0")
|
||||||
expect(spy).toEqual(["/latest"])
|
expect(spy).toEqual(["/latest"])
|
||||||
})
|
})
|
||||||
|
|
||||||
|
@ -87,9 +87,9 @@ describe.skip("update", () => {
|
||||||
const update = await p.getUpdate()
|
const update = await p.getUpdate()
|
||||||
|
|
||||||
await expect(settings.read()).resolves.toEqual({ update })
|
await expect(settings.read()).resolves.toEqual({ update })
|
||||||
expect(isNaN(update.checked)).toBe(false)
|
expect(isNaN(update.checked)).toStrictEqual(false)
|
||||||
expect(update.checked < now).toBe(true)
|
expect(update.checked < now).toBe(true)
|
||||||
expect(update.version).toBe("2.1.0")
|
expect(update.version).toStrictEqual("2.1.0")
|
||||||
expect(spy).toEqual([])
|
expect(spy).toEqual([])
|
||||||
})
|
})
|
||||||
|
|
||||||
|
@ -101,10 +101,10 @@ describe.skip("update", () => {
|
||||||
const update = await p.getUpdate(true)
|
const update = await p.getUpdate(true)
|
||||||
|
|
||||||
await expect(settings.read()).resolves.toEqual({ update })
|
await expect(settings.read()).resolves.toEqual({ update })
|
||||||
expect(isNaN(update.checked)).toBe(false)
|
expect(isNaN(update.checked)).toStrictEqual(false)
|
||||||
expect(update.checked < Date.now() && update.checked >= now).toBe(true)
|
expect(update.checked < Date.now() && update.checked >= now).toStrictEqual(true)
|
||||||
expect(update.version).toBe("4.1.1")
|
expect(update.version).toStrictEqual("4.1.1")
|
||||||
expect(spy).toBe(["/latest"])
|
expect(spy).toStrictEqual(["/latest"])
|
||||||
})
|
})
|
||||||
|
|
||||||
it("should get latest after interval passes", async () => {
|
it("should get latest after interval passes", async () => {
|
||||||
|
@ -121,8 +121,8 @@ describe.skip("update", () => {
|
||||||
await settings.write({ update: { checked, version } })
|
await settings.write({ update: { checked, version } })
|
||||||
|
|
||||||
const update = await p.getUpdate()
|
const update = await p.getUpdate()
|
||||||
expect(update.checked).not.toBe(checked)
|
expect(update.checked).not.toStrictEqual(checked)
|
||||||
expect(spy).toBe(["/latest"])
|
expect(spy).toStrictEqual(["/latest"])
|
||||||
})
|
})
|
||||||
|
|
||||||
it("should check if it's the current version", async () => {
|
it("should check if it's the current version", async () => {
|
||||||
|
@ -130,24 +130,31 @@ describe.skip("update", () => {
|
||||||
|
|
||||||
const p = provider()
|
const p = provider()
|
||||||
let update = await p.getUpdate(true)
|
let update = await p.getUpdate(true)
|
||||||
expect(p.isLatestVersion(update)).toBe(false)
|
expect(p.isLatestVersion(update)).toStrictEqual(false)
|
||||||
|
|
||||||
version = "0.0.0"
|
version = "0.0.0"
|
||||||
update = await p.getUpdate(true)
|
update = await p.getUpdate(true)
|
||||||
expect(p.isLatestVersion(update)).toBe(true)
|
expect(p.isLatestVersion(update)).toStrictEqual(true)
|
||||||
|
|
||||||
// Old version format; make sure it doesn't report as being later.
|
// Old version format; make sure it doesn't report as being later.
|
||||||
version = "999999.9999-invalid999.99.9"
|
version = "999999.9999-invalid999.99.9"
|
||||||
update = await p.getUpdate(true)
|
update = await p.getUpdate(true)
|
||||||
expect(p.isLatestVersion(update)).toBe(true)
|
expect(p.isLatestVersion(update)).toStrictEqual(true)
|
||||||
})
|
})
|
||||||
|
|
||||||
it("should not reject if unable to fetch", async () => {
|
it("should not reject if unable to fetch", async () => {
|
||||||
expect.assertions(2)
|
|
||||||
let provider = new UpdateProvider("invalid", settings)
|
let provider = new UpdateProvider("invalid", settings)
|
||||||
await expect(() => provider.getUpdate(true)).resolves.toBe(undefined)
|
let now = Date.now()
|
||||||
|
let update = await provider.getUpdate(true)
|
||||||
|
expect(isNaN(update.checked)).toStrictEqual(false)
|
||||||
|
expect(update.checked < Date.now() && update.checked >= now).toEqual(true)
|
||||||
|
expect(update.version).toStrictEqual("unknown")
|
||||||
|
|
||||||
provider = new UpdateProvider("http://probably.invalid.dev.localhost/latest", settings)
|
provider = new UpdateProvider("http://probably.invalid.dev.localhost/latest", settings)
|
||||||
await expect(() => provider.getUpdate(true)).resolves.toBe(undefined)
|
now = Date.now()
|
||||||
|
update = await provider.getUpdate(true)
|
||||||
|
expect(isNaN(update.checked)).toStrictEqual(false)
|
||||||
|
expect(update.checked < Date.now() && update.checked >= now).toEqual(true)
|
||||||
|
expect(update.version).toStrictEqual("unknown")
|
||||||
})
|
})
|
||||||
})
|
})
|
||||||
|
|
|
@ -18,9 +18,6 @@ global.document = dom.window.document
|
||||||
|
|
||||||
export type LocationLike = Pick<Location, "pathname" | "origin">
|
export type LocationLike = Pick<Location, "pathname" | "origin">
|
||||||
|
|
||||||
// jest.mock is hoisted above the imports so we must use `require` here.
|
|
||||||
jest.mock("@coder/logger", () => require("../utils/helpers").loggerModule)
|
|
||||||
|
|
||||||
describe("util", () => {
|
describe("util", () => {
|
||||||
describe("normalize", () => {
|
describe("normalize", () => {
|
||||||
it("should remove multiple slashes", () => {
|
it("should remove multiple slashes", () => {
|
||||||
|
@ -236,14 +233,14 @@ describe("util", () => {
|
||||||
const message = "You don't have access to that folder."
|
const message = "You don't have access to that folder."
|
||||||
const error = new Error(message)
|
const error = new Error(message)
|
||||||
|
|
||||||
logError("ui", error)
|
logError(loggerModule.logger, "ui", error)
|
||||||
|
|
||||||
expect(loggerModule.logger.error).toHaveBeenCalled()
|
expect(loggerModule.logger.error).toHaveBeenCalled()
|
||||||
expect(loggerModule.logger.error).toHaveBeenCalledWith(`ui: ${error.message} ${error.stack}`)
|
expect(loggerModule.logger.error).toHaveBeenCalledWith(`ui: ${error.message} ${error.stack}`)
|
||||||
})
|
})
|
||||||
|
|
||||||
it("should log an error, even if not an instance of error", () => {
|
it("should log an error, even if not an instance of error", () => {
|
||||||
logError("api", "oh no")
|
logError(loggerModule.logger, "api", "oh no")
|
||||||
|
|
||||||
expect(loggerModule.logger.error).toHaveBeenCalled()
|
expect(loggerModule.logger.error).toHaveBeenCalled()
|
||||||
expect(loggerModule.logger.error).toHaveBeenCalledWith("api: oh no")
|
expect(loggerModule.logger.error).toHaveBeenCalledWith("api: oh no")
|
||||||
|
|
|
@ -1,3 +1,4 @@
|
||||||
|
import { logger } from "@coder/logger"
|
||||||
import * as express from "express"
|
import * as express from "express"
|
||||||
import * as http from "http"
|
import * as http from "http"
|
||||||
import * as net from "net"
|
import * as net from "net"
|
||||||
|
@ -45,7 +46,7 @@ export class HttpServer {
|
||||||
rej(err)
|
rej(err)
|
||||||
} else {
|
} else {
|
||||||
// Promise resolved earlier so this is some other error.
|
// Promise resolved earlier so this is some other error.
|
||||||
util.logError("http server error", err)
|
util.logError(logger, "http server error", err)
|
||||||
}
|
}
|
||||||
})
|
})
|
||||||
})
|
})
|
||||||
|
|
|
@ -6,9 +6,12 @@
|
||||||
* The second is a symlink to the first.
|
* The second is a symlink to the first.
|
||||||
*/
|
*/
|
||||||
export interface Options {
|
export interface Options {
|
||||||
|
authed: boolean
|
||||||
base: string
|
base: string
|
||||||
|
csStaticBase: string
|
||||||
disableTelemetry: boolean
|
disableTelemetry: boolean
|
||||||
disableUpdateCheck: boolean
|
disableUpdateCheck: boolean
|
||||||
|
logLevel: number
|
||||||
}
|
}
|
||||||
|
|
||||||
export interface InitMessage {
|
export interface InitMessage {
|
||||||
|
|
185
yarn.lock
185
yarn.lock
|
@ -1102,9 +1102,9 @@
|
||||||
integrity sha512-fZQQafSREFyuZcdWFAExYjBiCL7AUCdgsk80iO0q4yihYYdcIiH28CcuPTGFgLOCC8RlW49GSQxdHwZP+I7CNg==
|
integrity sha512-fZQQafSREFyuZcdWFAExYjBiCL7AUCdgsk80iO0q4yihYYdcIiH28CcuPTGFgLOCC8RlW49GSQxdHwZP+I7CNg==
|
||||||
|
|
||||||
"@types/node@*", "@types/node@~12.20.7":
|
"@types/node@*", "@types/node@~12.20.7":
|
||||||
version "12.20.11"
|
version "12.20.12"
|
||||||
resolved "https://registry.yarnpkg.com/@types/node/-/node-12.20.11.tgz#980832cd56efafff8c18aa148c4085eb02a483f4"
|
resolved "https://registry.yarnpkg.com/@types/node/-/node-12.20.12.tgz#fd9c1c2cfab536a2383ed1ef70f94adea743a226"
|
||||||
integrity sha512-gema+apZ6qLQK7k7F0dGkGCWQYsL0qqKORWOQO6tq46q+x+1C0vbOiOqOwRVlh4RAdbQwV/j/ryr3u5NOG1fPQ==
|
integrity sha512-KQZ1al2hKOONAs2MFv+yTQP1LkDWMrRJ9YCVRalXltOfXsBmH5IownLxQaiq0lnAHwAViLnh2aTYqrPcRGEbgg==
|
||||||
|
|
||||||
"@types/normalize-package-data@^2.4.0":
|
"@types/normalize-package-data@^2.4.0":
|
||||||
version "2.4.0"
|
version "2.4.0"
|
||||||
|
@ -1170,10 +1170,10 @@
|
||||||
"@types/mime" "^1"
|
"@types/mime" "^1"
|
||||||
"@types/node" "*"
|
"@types/node" "*"
|
||||||
|
|
||||||
"@types/split2@^2.1.6":
|
"@types/split2@^3.2.0":
|
||||||
version "2.1.6"
|
version "3.2.0"
|
||||||
resolved "https://registry.yarnpkg.com/@types/split2/-/split2-2.1.6.tgz#b095c9e064853824b22c67993d99b066777402b1"
|
resolved "https://registry.yarnpkg.com/@types/split2/-/split2-3.2.0.tgz#a14f5acb1719aca5e6bcd3706f0df5c5b986802b"
|
||||||
integrity sha512-ddaFSOMuy2Rp97l6q/LEteQygvTQJuEZ+SRhxFKR0uXGsdbFDqX/QF2xoGcOqLQ8XV91v01SnAv2vpgihNgW/Q==
|
integrity sha512-Wb9kp2BW5Qs38oyAS36t+wDN9eE6bFt8fpJ0DpYX6R5Og5tY003m8v1H2cEv8bpF9vLpPXago5pgsCgPka8BVQ==
|
||||||
dependencies:
|
dependencies:
|
||||||
"@types/node" "*"
|
"@types/node" "*"
|
||||||
|
|
||||||
|
@ -1210,12 +1210,12 @@
|
||||||
integrity sha512-kdBHgE9+M1Os7UqWZtiLhKye5reFl8cPBYyCsP2fatwZRz7F7GdIxIHZ20Kkc0hYBfbXE+lzPOTUU1I0qgjtHA==
|
integrity sha512-kdBHgE9+M1Os7UqWZtiLhKye5reFl8cPBYyCsP2fatwZRz7F7GdIxIHZ20Kkc0hYBfbXE+lzPOTUU1I0qgjtHA==
|
||||||
|
|
||||||
"@typescript-eslint/eslint-plugin@^4.7.0":
|
"@typescript-eslint/eslint-plugin@^4.7.0":
|
||||||
version "4.22.0"
|
version "4.22.1"
|
||||||
resolved "https://registry.yarnpkg.com/@typescript-eslint/eslint-plugin/-/eslint-plugin-4.22.0.tgz#3d5f29bb59e61a9dba1513d491b059e536e16dbc"
|
resolved "https://registry.yarnpkg.com/@typescript-eslint/eslint-plugin/-/eslint-plugin-4.22.1.tgz#6bcdbaa4548553ab861b4e5f34936ead1349a543"
|
||||||
integrity sha512-U8SP9VOs275iDXaL08Ln1Fa/wLXfj5aTr/1c0t0j6CdbOnxh+TruXu1p4I0NAvdPBQgoPjHsgKn28mOi0FzfoA==
|
integrity sha512-kVTAghWDDhsvQ602tHBc6WmQkdaYbkcTwZu+7l24jtJiYvm9l+/y/b2BZANEezxPDiX5MK2ZecE+9BFi/YJryw==
|
||||||
dependencies:
|
dependencies:
|
||||||
"@typescript-eslint/experimental-utils" "4.22.0"
|
"@typescript-eslint/experimental-utils" "4.22.1"
|
||||||
"@typescript-eslint/scope-manager" "4.22.0"
|
"@typescript-eslint/scope-manager" "4.22.1"
|
||||||
debug "^4.1.1"
|
debug "^4.1.1"
|
||||||
functional-red-black-tree "^1.0.1"
|
functional-red-black-tree "^1.0.1"
|
||||||
lodash "^4.17.15"
|
lodash "^4.17.15"
|
||||||
|
@ -1223,60 +1223,60 @@
|
||||||
semver "^7.3.2"
|
semver "^7.3.2"
|
||||||
tsutils "^3.17.1"
|
tsutils "^3.17.1"
|
||||||
|
|
||||||
"@typescript-eslint/experimental-utils@4.22.0":
|
"@typescript-eslint/experimental-utils@4.22.1":
|
||||||
version "4.22.0"
|
version "4.22.1"
|
||||||
resolved "https://registry.yarnpkg.com/@typescript-eslint/experimental-utils/-/experimental-utils-4.22.0.tgz#68765167cca531178e7b650a53456e6e0bef3b1f"
|
resolved "https://registry.yarnpkg.com/@typescript-eslint/experimental-utils/-/experimental-utils-4.22.1.tgz#3938a5c89b27dc9a39b5de63a62ab1623ab27497"
|
||||||
integrity sha512-xJXHHl6TuAxB5AWiVrGhvbGL8/hbiCQ8FiWwObO3r0fnvBdrbWEDy1hlvGQOAWc6qsCWuWMKdVWlLAEMpxnddg==
|
integrity sha512-svYlHecSMCQGDO2qN1v477ax/IDQwWhc7PRBiwAdAMJE7GXk5stF4Z9R/8wbRkuX/5e9dHqbIWxjeOjckK3wLQ==
|
||||||
dependencies:
|
dependencies:
|
||||||
"@types/json-schema" "^7.0.3"
|
"@types/json-schema" "^7.0.3"
|
||||||
"@typescript-eslint/scope-manager" "4.22.0"
|
"@typescript-eslint/scope-manager" "4.22.1"
|
||||||
"@typescript-eslint/types" "4.22.0"
|
"@typescript-eslint/types" "4.22.1"
|
||||||
"@typescript-eslint/typescript-estree" "4.22.0"
|
"@typescript-eslint/typescript-estree" "4.22.1"
|
||||||
eslint-scope "^5.0.0"
|
eslint-scope "^5.0.0"
|
||||||
eslint-utils "^2.0.0"
|
eslint-utils "^2.0.0"
|
||||||
|
|
||||||
"@typescript-eslint/parser@^4.7.0":
|
"@typescript-eslint/parser@^4.7.0":
|
||||||
version "4.22.0"
|
version "4.22.1"
|
||||||
resolved "https://registry.yarnpkg.com/@typescript-eslint/parser/-/parser-4.22.0.tgz#e1637327fcf796c641fe55f73530e90b16ac8fe8"
|
resolved "https://registry.yarnpkg.com/@typescript-eslint/parser/-/parser-4.22.1.tgz#a95bda0fd01d994a15fc3e99dc984294f25c19cc"
|
||||||
integrity sha512-z/bGdBJJZJN76nvAY9DkJANYgK3nlRstRRi74WHm3jjgf2I8AglrSY+6l7ogxOmn55YJ6oKZCLLy+6PW70z15Q==
|
integrity sha512-l+sUJFInWhuMxA6rtirzjooh8cM/AATAe3amvIkqKFeMzkn85V+eLzb1RyuXkHak4dLfYzOmF6DXPyflJvjQnw==
|
||||||
dependencies:
|
dependencies:
|
||||||
"@typescript-eslint/scope-manager" "4.22.0"
|
"@typescript-eslint/scope-manager" "4.22.1"
|
||||||
"@typescript-eslint/types" "4.22.0"
|
"@typescript-eslint/types" "4.22.1"
|
||||||
"@typescript-eslint/typescript-estree" "4.22.0"
|
"@typescript-eslint/typescript-estree" "4.22.1"
|
||||||
debug "^4.1.1"
|
debug "^4.1.1"
|
||||||
|
|
||||||
"@typescript-eslint/scope-manager@4.22.0":
|
"@typescript-eslint/scope-manager@4.22.1":
|
||||||
version "4.22.0"
|
version "4.22.1"
|
||||||
resolved "https://registry.yarnpkg.com/@typescript-eslint/scope-manager/-/scope-manager-4.22.0.tgz#ed411545e61161a8d702e703a4b7d96ec065b09a"
|
resolved "https://registry.yarnpkg.com/@typescript-eslint/scope-manager/-/scope-manager-4.22.1.tgz#5bb357f94f9cd8b94e6be43dd637eb73b8f355b4"
|
||||||
integrity sha512-OcCO7LTdk6ukawUM40wo61WdeoA7NM/zaoq1/2cs13M7GyiF+T4rxuA4xM+6LeHWjWbss7hkGXjFDRcKD4O04Q==
|
integrity sha512-d5bAiPBiessSmNi8Amq/RuLslvcumxLmyhf1/Xa9IuaoFJ0YtshlJKxhlbY7l2JdEk3wS0EnmnfeJWSvADOe0g==
|
||||||
dependencies:
|
dependencies:
|
||||||
"@typescript-eslint/types" "4.22.0"
|
"@typescript-eslint/types" "4.22.1"
|
||||||
"@typescript-eslint/visitor-keys" "4.22.0"
|
"@typescript-eslint/visitor-keys" "4.22.1"
|
||||||
|
|
||||||
"@typescript-eslint/types@4.22.0":
|
"@typescript-eslint/types@4.22.1":
|
||||||
version "4.22.0"
|
version "4.22.1"
|
||||||
resolved "https://registry.yarnpkg.com/@typescript-eslint/types/-/types-4.22.0.tgz#0ca6fde5b68daf6dba133f30959cc0688c8dd0b6"
|
resolved "https://registry.yarnpkg.com/@typescript-eslint/types/-/types-4.22.1.tgz#bf99c6cec0b4a23d53a61894816927f2adad856a"
|
||||||
integrity sha512-sW/BiXmmyMqDPO2kpOhSy2Py5w6KvRRsKZnV0c4+0nr4GIcedJwXAq+RHNK4lLVEZAJYFltnnk1tJSlbeS9lYA==
|
integrity sha512-2HTkbkdAeI3OOcWbqA8hWf/7z9c6gkmnWNGz0dKSLYLWywUlkOAQ2XcjhlKLj5xBFDf8FgAOF5aQbnLRvgNbCw==
|
||||||
|
|
||||||
"@typescript-eslint/typescript-estree@4.22.0":
|
"@typescript-eslint/typescript-estree@4.22.1":
|
||||||
version "4.22.0"
|
version "4.22.1"
|
||||||
resolved "https://registry.yarnpkg.com/@typescript-eslint/typescript-estree/-/typescript-estree-4.22.0.tgz#b5d95d6d366ff3b72f5168c75775a3e46250d05c"
|
resolved "https://registry.yarnpkg.com/@typescript-eslint/typescript-estree/-/typescript-estree-4.22.1.tgz#dca379eead8cdfd4edc04805e83af6d148c164f9"
|
||||||
integrity sha512-TkIFeu5JEeSs5ze/4NID+PIcVjgoU3cUQUIZnH3Sb1cEn1lBo7StSV5bwPuJQuoxKXlzAObjYTilOEKRuhR5yg==
|
integrity sha512-p3We0pAPacT+onSGM+sPR+M9CblVqdA9F1JEdIqRVlxK5Qth4ochXQgIyb9daBomyQKAXbygxp1aXQRV0GC79A==
|
||||||
dependencies:
|
dependencies:
|
||||||
"@typescript-eslint/types" "4.22.0"
|
"@typescript-eslint/types" "4.22.1"
|
||||||
"@typescript-eslint/visitor-keys" "4.22.0"
|
"@typescript-eslint/visitor-keys" "4.22.1"
|
||||||
debug "^4.1.1"
|
debug "^4.1.1"
|
||||||
globby "^11.0.1"
|
globby "^11.0.1"
|
||||||
is-glob "^4.0.1"
|
is-glob "^4.0.1"
|
||||||
semver "^7.3.2"
|
semver "^7.3.2"
|
||||||
tsutils "^3.17.1"
|
tsutils "^3.17.1"
|
||||||
|
|
||||||
"@typescript-eslint/visitor-keys@4.22.0":
|
"@typescript-eslint/visitor-keys@4.22.1":
|
||||||
version "4.22.0"
|
version "4.22.1"
|
||||||
resolved "https://registry.yarnpkg.com/@typescript-eslint/visitor-keys/-/visitor-keys-4.22.0.tgz#169dae26d3c122935da7528c839f42a8a42f6e47"
|
resolved "https://registry.yarnpkg.com/@typescript-eslint/visitor-keys/-/visitor-keys-4.22.1.tgz#6045ae25a11662c671f90b3a403d682dfca0b7a6"
|
||||||
integrity sha512-nnMu4F+s4o0sll6cBSsTeVsT4cwxB7zECK3dFxzEjPBii9xLpq4yqqsy/FU5zMfan6G60DKZSCXAa3sHJZrcYw==
|
integrity sha512-WPkOrIRm+WCLZxXQHCi+WG8T2MMTUFR70rWjdWYddLT7cEfb2P4a3O/J2U1FBVsSFTocXLCoXWY6MZGejeStvQ==
|
||||||
dependencies:
|
dependencies:
|
||||||
"@typescript-eslint/types" "4.22.0"
|
"@typescript-eslint/types" "4.22.1"
|
||||||
eslint-visitor-keys "^2.0.0"
|
eslint-visitor-keys "^2.0.0"
|
||||||
|
|
||||||
JSONStream@^1.3.5:
|
JSONStream@^1.3.5:
|
||||||
|
@ -1328,11 +1328,6 @@ acorn@^7.1.1, acorn@^7.4.0:
|
||||||
resolved "https://registry.yarnpkg.com/acorn/-/acorn-7.4.1.tgz#feaed255973d2e77555b83dbc08851a6c63520fa"
|
resolved "https://registry.yarnpkg.com/acorn/-/acorn-7.4.1.tgz#feaed255973d2e77555b83dbc08851a6c63520fa"
|
||||||
integrity sha512-nQyp0o1/mNdbTO1PO6kHkwSrmgZ0MT/jCCpNiwbUjGoRN4dlBhqJtoQuCnEOKzgTVwg0ZWiCoQy6SxMebQVh8A==
|
integrity sha512-nQyp0o1/mNdbTO1PO6kHkwSrmgZ0MT/jCCpNiwbUjGoRN4dlBhqJtoQuCnEOKzgTVwg0ZWiCoQy6SxMebQVh8A==
|
||||||
|
|
||||||
agent-base@5:
|
|
||||||
version "5.1.1"
|
|
||||||
resolved "https://registry.yarnpkg.com/agent-base/-/agent-base-5.1.1.tgz#e8fb3f242959db44d63be665db7a8e739537a32c"
|
|
||||||
integrity sha512-TMeqbNl2fMW0nMjTEPOwe3J/PRFP4vqeoNuQMG0HlMrtm5QxKqdvAkZ1pRBQ/ulIyDD5Yq0nJ7YbdD8ey0TO3g==
|
|
||||||
|
|
||||||
agent-base@6, agent-base@^6.0.0:
|
agent-base@6, agent-base@^6.0.0:
|
||||||
version "6.0.2"
|
version "6.0.2"
|
||||||
resolved "https://registry.yarnpkg.com/agent-base/-/agent-base-6.0.2.tgz#49fff58577cfee3f37176feab4c22e00f86d7f77"
|
resolved "https://registry.yarnpkg.com/agent-base/-/agent-base-6.0.2.tgz#49fff58577cfee3f37176feab4c22e00f86d7f77"
|
||||||
|
@ -1572,10 +1567,10 @@ atob@^2.1.2:
|
||||||
resolved "https://registry.yarnpkg.com/atob/-/atob-2.1.2.tgz#6d9517eb9e030d2436666651e86bd9f6f13533c9"
|
resolved "https://registry.yarnpkg.com/atob/-/atob-2.1.2.tgz#6d9517eb9e030d2436666651e86bd9f6f13533c9"
|
||||||
integrity sha512-Wm6ukoaOGJi/73p/cl2GvLjTI5JM1k/O14isD73YML8StrH/7/lRFgmg8nICZgD3bZZvjwCGxtMOD3wWNAu8cg==
|
integrity sha512-Wm6ukoaOGJi/73p/cl2GvLjTI5JM1k/O14isD73YML8StrH/7/lRFgmg8nICZgD3bZZvjwCGxtMOD3wWNAu8cg==
|
||||||
|
|
||||||
audit-ci@^3.1.1:
|
audit-ci@^4.0.0:
|
||||||
version "3.2.0"
|
version "4.0.0"
|
||||||
resolved "https://registry.yarnpkg.com/audit-ci/-/audit-ci-3.2.0.tgz#5a42e3e31dbd9d259f7f417803b80a2bd50ef73d"
|
resolved "https://registry.yarnpkg.com/audit-ci/-/audit-ci-4.0.0.tgz#d3223d001042cb478377b6536e73c3bc9f7627f9"
|
||||||
integrity sha512-kRFfl/AdmyCrnuc/M4T3l/G/Hy8U4JsgnyRJgGq1532bCwh62ZGeL5rEk2Snk8Umyd3CRgY4V+mVI/LzQoN/Rg==
|
integrity sha512-8+wcRoHoZ47jP2EdvDpNU9Nb7rnNij7bmEmfiFn+L/hMHvD6rYdO6ji94sVoOmVXEDwnuib+YNHsKDUY9V/hnA==
|
||||||
dependencies:
|
dependencies:
|
||||||
JSONStream "^1.3.5"
|
JSONStream "^1.3.5"
|
||||||
cross-spawn "^7.0.3"
|
cross-spawn "^7.0.3"
|
||||||
|
@ -2056,10 +2051,10 @@ chalk@^2.0.0, chalk@^2.0.1, chalk@^2.1.0, chalk@^2.3.1, chalk@^2.4.1, chalk@^2.4
|
||||||
escape-string-regexp "^1.0.5"
|
escape-string-regexp "^1.0.5"
|
||||||
supports-color "^5.3.0"
|
supports-color "^5.3.0"
|
||||||
|
|
||||||
chalk@^4.0.0, chalk@^4.1.0:
|
chalk@^4.0.0, chalk@^4.1.0, chalk@^4.1.1:
|
||||||
version "4.1.0"
|
version "4.1.1"
|
||||||
resolved "https://registry.yarnpkg.com/chalk/-/chalk-4.1.0.tgz#4e14870a618d9e2edd97dd8345fd9d9dc315646a"
|
resolved "https://registry.yarnpkg.com/chalk/-/chalk-4.1.1.tgz#c80b3fab28bf6371e6863325eee67e618b77e6ad"
|
||||||
integrity sha512-qwx12AxXe2Q5xQ43Ac//I6v5aXTipYrSESdOgzrN+9XjgEpyjpKuvSGaN4qE93f7TQTlerQQ8S+EQ0EyDoVL1A==
|
integrity sha512-diHzdDKxcU+bAsUboHLPEDQiw0qEe0qd7SYUn3HgcFlWgbDcfLGswOHYeGrHKzG9z6UYf01d9VFMfZxPM1xZSg==
|
||||||
dependencies:
|
dependencies:
|
||||||
ansi-styles "^4.1.0"
|
ansi-styles "^4.1.0"
|
||||||
supports-color "^7.1.0"
|
supports-color "^7.1.0"
|
||||||
|
@ -2174,14 +2169,14 @@ coa@^2.0.2:
|
||||||
q "^1.1.2"
|
q "^1.1.2"
|
||||||
|
|
||||||
codecov@^3.8.1:
|
codecov@^3.8.1:
|
||||||
version "3.8.1"
|
version "3.8.2"
|
||||||
resolved "https://registry.yarnpkg.com/codecov/-/codecov-3.8.1.tgz#06fe026b75525ed1ce864d4a34f1010c52c51546"
|
resolved "https://registry.yarnpkg.com/codecov/-/codecov-3.8.2.tgz#ab24f18783998c39e809ea210af899f8dbcc790e"
|
||||||
integrity sha512-Qm7ltx1pzLPsliZY81jyaQ80dcNR4/JpcX0IHCIWrHBXgseySqbdbYfkdiXd7o/xmzQpGRVCKGYeTrHUpn6Dcw==
|
integrity sha512-6w/kt/xvmPsWMfDFPE/T054txA9RTgcJEw36PNa6MYX+YV29jCHCRFXwbQ3QZBTOgnex1J2WP8bo2AT8TWWz9g==
|
||||||
dependencies:
|
dependencies:
|
||||||
argv "0.0.2"
|
argv "0.0.2"
|
||||||
ignore-walk "3.0.3"
|
ignore-walk "3.0.3"
|
||||||
js-yaml "3.14.0"
|
js-yaml "3.14.1"
|
||||||
teeny-request "6.0.1"
|
teeny-request "7.0.1"
|
||||||
urlgrey "0.4.4"
|
urlgrey "0.4.4"
|
||||||
|
|
||||||
collapse-white-space@^1.0.2:
|
collapse-white-space@^1.0.2:
|
||||||
|
@ -3141,11 +3136,6 @@ eslint-plugin-import@^2.18.2:
|
||||||
resolve "^1.17.0"
|
resolve "^1.17.0"
|
||||||
tsconfig-paths "^3.9.0"
|
tsconfig-paths "^3.9.0"
|
||||||
|
|
||||||
eslint-plugin-jest-playwright@^0.2.1:
|
|
||||||
version "0.2.1"
|
|
||||||
resolved "https://registry.yarnpkg.com/eslint-plugin-jest-playwright/-/eslint-plugin-jest-playwright-0.2.1.tgz#8778fee9d5915132a03d94370d3eea0a7ddd08f3"
|
|
||||||
integrity sha512-BicKUJUpVPsLbHN8c5hYaZn6pv8PCMjBGHXUfvlY1p75fh4APVfX2gTK14HuiR8/Bv3fKBQu5MTaqCro4E3OHg==
|
|
||||||
|
|
||||||
eslint-plugin-prettier@^3.1.0:
|
eslint-plugin-prettier@^3.1.0:
|
||||||
version "3.4.0"
|
version "3.4.0"
|
||||||
resolved "https://registry.yarnpkg.com/eslint-plugin-prettier/-/eslint-plugin-prettier-3.4.0.tgz#cdbad3bf1dbd2b177e9825737fe63b476a08f0c7"
|
resolved "https://registry.yarnpkg.com/eslint-plugin-prettier/-/eslint-plugin-prettier-3.4.0.tgz#cdbad3bf1dbd2b177e9825737fe63b476a08f0c7"
|
||||||
|
@ -4094,14 +4084,6 @@ https-proxy-agent@5, https-proxy-agent@^5.0.0:
|
||||||
agent-base "6"
|
agent-base "6"
|
||||||
debug "4"
|
debug "4"
|
||||||
|
|
||||||
https-proxy-agent@^4.0.0:
|
|
||||||
version "4.0.0"
|
|
||||||
resolved "https://registry.yarnpkg.com/https-proxy-agent/-/https-proxy-agent-4.0.0.tgz#702b71fb5520a132a66de1f67541d9e62154d82b"
|
|
||||||
integrity sha512-zoDhWrkR3of1l9QAL8/scJZyLu8j/gBkcwcaQOZh7Gyh/+uJQzGVETdgT30akuwkpL8HTRfssqI3BZuV18teDg==
|
|
||||||
dependencies:
|
|
||||||
agent-base "5"
|
|
||||||
debug "4"
|
|
||||||
|
|
||||||
iconv-lite@0.4.24:
|
iconv-lite@0.4.24:
|
||||||
version "0.4.24"
|
version "0.4.24"
|
||||||
resolved "https://registry.yarnpkg.com/iconv-lite/-/iconv-lite-0.4.24.tgz#2022b4b25fbddc21d2f524974a474aafe733908b"
|
resolved "https://registry.yarnpkg.com/iconv-lite/-/iconv-lite-0.4.24.tgz#2022b4b25fbddc21d2f524974a474aafe733908b"
|
||||||
|
@ -4556,15 +4538,7 @@ js-tokens@^4.0.0:
|
||||||
resolved "https://registry.yarnpkg.com/js-tokens/-/js-tokens-4.0.0.tgz#19203fb59991df98e3a287050d4647cdeaf32499"
|
resolved "https://registry.yarnpkg.com/js-tokens/-/js-tokens-4.0.0.tgz#19203fb59991df98e3a287050d4647cdeaf32499"
|
||||||
integrity sha512-RdJUflcE3cUzKiMqQgsCu06FPu9UdIJO0beYbPhHN4k6apgJtifcoCtT9bcxOpYBtpD2kCM6Sbzg4CausW/PKQ==
|
integrity sha512-RdJUflcE3cUzKiMqQgsCu06FPu9UdIJO0beYbPhHN4k6apgJtifcoCtT9bcxOpYBtpD2kCM6Sbzg4CausW/PKQ==
|
||||||
|
|
||||||
js-yaml@3.14.0:
|
js-yaml@3.14.1, js-yaml@^3.10.0, js-yaml@^3.13.1:
|
||||||
version "3.14.0"
|
|
||||||
resolved "https://registry.yarnpkg.com/js-yaml/-/js-yaml-3.14.0.tgz#a7a34170f26a21bb162424d8adacb4113a69e482"
|
|
||||||
integrity sha512-/4IbIeHcD9VMHFqDR/gQ7EdZdLimOvW2DdcxFjdyyZ9NsbS+ccrXqVWDtab/lRl5AlUqmpBx8EhPaWR+OtY17A==
|
|
||||||
dependencies:
|
|
||||||
argparse "^1.0.7"
|
|
||||||
esprima "^4.0.0"
|
|
||||||
|
|
||||||
js-yaml@^3.10.0, js-yaml@^3.13.1:
|
|
||||||
version "3.14.1"
|
version "3.14.1"
|
||||||
resolved "https://registry.yarnpkg.com/js-yaml/-/js-yaml-3.14.1.tgz#dae812fdb3825fa306609a8717383c50c36a0537"
|
resolved "https://registry.yarnpkg.com/js-yaml/-/js-yaml-3.14.1.tgz#dae812fdb3825fa306609a8717383c50c36a0537"
|
||||||
integrity sha512-okMH7OXXJ7YrN9Ok3/SXrnu4iX9yOk+25nqX4imS2npuvTYDmo/QEZoqwZkYaIDk3jVvBOTOIEgEhaLOynBS9g==
|
integrity sha512-okMH7OXXJ7YrN9Ok3/SXrnu4iX9yOk+25nqX4imS2npuvTYDmo/QEZoqwZkYaIDk3jVvBOTOIEgEhaLOynBS9g==
|
||||||
|
@ -5203,7 +5177,7 @@ node-addon-api@^1.7.1:
|
||||||
resolved "https://registry.yarnpkg.com/node-addon-api/-/node-addon-api-1.7.2.tgz#3df30b95720b53c24e59948b49532b662444f54d"
|
resolved "https://registry.yarnpkg.com/node-addon-api/-/node-addon-api-1.7.2.tgz#3df30b95720b53c24e59948b49532b662444f54d"
|
||||||
integrity sha512-ibPK3iA+vaY1eEjESkQkM0BbCqFOaZMiXRTtdB0u7b4djtY6JnsjvPdUHVMg6xQt3B8fpTTWHI9A+ADjM9frzg==
|
integrity sha512-ibPK3iA+vaY1eEjESkQkM0BbCqFOaZMiXRTtdB0u7b4djtY6JnsjvPdUHVMg6xQt3B8fpTTWHI9A+ADjM9frzg==
|
||||||
|
|
||||||
node-fetch@^2.2.0, node-fetch@^2.6.1:
|
node-fetch@^2.6.1:
|
||||||
version "2.6.1"
|
version "2.6.1"
|
||||||
resolved "https://registry.yarnpkg.com/node-fetch/-/node-fetch-2.6.1.tgz#045bd323631f76ed2e2b55573394416b639a0052"
|
resolved "https://registry.yarnpkg.com/node-fetch/-/node-fetch-2.6.1.tgz#045bd323631f76ed2e2b55573394416b639a0052"
|
||||||
integrity sha512-V4aYg89jEoVRxRb2fJdAg8FHvI7cEyYdVAh94HH0UIK8oJxUfkjlDQN9RbMx+bEjP7+ggMiFRprSti032Oipxw==
|
integrity sha512-V4aYg89jEoVRxRb2fJdAg8FHvI7cEyYdVAh94HH0UIK8oJxUfkjlDQN9RbMx+bEjP7+ggMiFRprSti032Oipxw==
|
||||||
|
@ -7417,15 +7391,15 @@ stylelint-config-recommended@^5.0.0:
|
||||||
integrity sha512-c8aubuARSu5A3vEHLBeOSJt1udOdS+1iue7BmJDTSXoCBmfEQmmWX+59vYIj3NQdJBY6a/QRv1ozVFpaB9jaqA==
|
integrity sha512-c8aubuARSu5A3vEHLBeOSJt1udOdS+1iue7BmJDTSXoCBmfEQmmWX+59vYIj3NQdJBY6a/QRv1ozVFpaB9jaqA==
|
||||||
|
|
||||||
stylelint@^13.0.0:
|
stylelint@^13.0.0:
|
||||||
version "13.13.0"
|
version "13.13.1"
|
||||||
resolved "https://registry.yarnpkg.com/stylelint/-/stylelint-13.13.0.tgz#1a33bffde765920ac985f16ae6250ff914b27804"
|
resolved "https://registry.yarnpkg.com/stylelint/-/stylelint-13.13.1.tgz#fca9c9f5de7990ab26a00f167b8978f083a18f3c"
|
||||||
integrity sha512-jvkM1iuH88vAvjdKPwPm6abiMP2/D/1chbfb+4GVONddOOskHuCXc0loyrLdxO1AwwH6jdnjYskkTKHQD7cXwQ==
|
integrity sha512-Mv+BQr5XTUrKqAXmpqm6Ddli6Ief+AiPZkRsIrAoUKFuq/ElkUh9ZMYxXD0iQNZ5ADghZKLOWz1h7hTClB7zgQ==
|
||||||
dependencies:
|
dependencies:
|
||||||
"@stylelint/postcss-css-in-js" "^0.37.2"
|
"@stylelint/postcss-css-in-js" "^0.37.2"
|
||||||
"@stylelint/postcss-markdown" "^0.36.2"
|
"@stylelint/postcss-markdown" "^0.36.2"
|
||||||
autoprefixer "^9.8.6"
|
autoprefixer "^9.8.6"
|
||||||
balanced-match "^2.0.0"
|
balanced-match "^2.0.0"
|
||||||
chalk "^4.1.0"
|
chalk "^4.1.1"
|
||||||
cosmiconfig "^7.0.0"
|
cosmiconfig "^7.0.0"
|
||||||
debug "^4.3.1"
|
debug "^4.3.1"
|
||||||
execall "^2.0.0"
|
execall "^2.0.0"
|
||||||
|
@ -7466,7 +7440,7 @@ stylelint@^13.0.0:
|
||||||
style-search "^0.1.0"
|
style-search "^0.1.0"
|
||||||
sugarss "^2.0.0"
|
sugarss "^2.0.0"
|
||||||
svg-tags "^1.0.0"
|
svg-tags "^1.0.0"
|
||||||
table "^6.5.1"
|
table "^6.6.0"
|
||||||
v8-compile-cache "^2.3.0"
|
v8-compile-cache "^2.3.0"
|
||||||
write-file-atomic "^3.0.3"
|
write-file-atomic "^3.0.3"
|
||||||
|
|
||||||
|
@ -7539,10 +7513,10 @@ symbol-tree@^3.2.2:
|
||||||
resolved "https://registry.yarnpkg.com/symbol-tree/-/symbol-tree-3.2.4.tgz#430637d248ba77e078883951fb9aa0eed7c63fa2"
|
resolved "https://registry.yarnpkg.com/symbol-tree/-/symbol-tree-3.2.4.tgz#430637d248ba77e078883951fb9aa0eed7c63fa2"
|
||||||
integrity sha512-9QNk5KwDF+Bvz+PyObkmSYjI5ksVUYtjW7AU22r2NKcfLJcXp96hkDWU3+XndOsUb+AQ9QhfzfCT2O+CNWT5Tw==
|
integrity sha512-9QNk5KwDF+Bvz+PyObkmSYjI5ksVUYtjW7AU22r2NKcfLJcXp96hkDWU3+XndOsUb+AQ9QhfzfCT2O+CNWT5Tw==
|
||||||
|
|
||||||
table@^6.0.4, table@^6.5.1:
|
table@^6.0.4, table@^6.6.0:
|
||||||
version "6.5.1"
|
version "6.6.0"
|
||||||
resolved "https://registry.yarnpkg.com/table/-/table-6.5.1.tgz#930885a7430f15f8766b35cd1e36de40793db523"
|
resolved "https://registry.yarnpkg.com/table/-/table-6.6.0.tgz#905654b79df98d9e9a973de1dd58682532c40e8e"
|
||||||
integrity sha512-xGDXWTBJxahkzPQCsn1S9ESHEenU7TbMD5Iv4FeopXv/XwJyWatFjfbor+6ipI10/MNPXBYUamYukOrbPZ9L/w==
|
integrity sha512-iZMtp5tUvcnAdtHpZTWLPF0M7AgiQsURR2DwmxnJwSy8I3+cY+ozzVvYha3BOLG2TB+L0CqjIz+91htuj6yCXg==
|
||||||
dependencies:
|
dependencies:
|
||||||
ajv "^8.0.1"
|
ajv "^8.0.1"
|
||||||
lodash.clonedeep "^4.5.0"
|
lodash.clonedeep "^4.5.0"
|
||||||
|
@ -7573,16 +7547,16 @@ tar-stream@^2.1.4, tar-stream@^2.2.0:
|
||||||
inherits "^2.0.3"
|
inherits "^2.0.3"
|
||||||
readable-stream "^3.1.1"
|
readable-stream "^3.1.1"
|
||||||
|
|
||||||
teeny-request@6.0.1:
|
teeny-request@7.0.1:
|
||||||
version "6.0.1"
|
version "7.0.1"
|
||||||
resolved "https://registry.yarnpkg.com/teeny-request/-/teeny-request-6.0.1.tgz#9b1f512cef152945827ba7e34f62523a4ce2c5b0"
|
resolved "https://registry.yarnpkg.com/teeny-request/-/teeny-request-7.0.1.tgz#bdd41fdffea5f8fbc0d29392cb47bec4f66b2b4c"
|
||||||
integrity sha512-TAK0c9a00ELOqLrZ49cFxvPVogMUFaWY8dUsQc/0CuQPGF+BOxOQzXfE413BAk2kLomwNplvdtMpeaeGWmoc2g==
|
integrity sha512-sasJmQ37klOlplL4Ia/786M5YlOcoLGQyq2TE4WHSRupbAuDaQW0PfVxV4MtdBtRJ4ngzS+1qim8zP6Zp35qCw==
|
||||||
dependencies:
|
dependencies:
|
||||||
http-proxy-agent "^4.0.0"
|
http-proxy-agent "^4.0.0"
|
||||||
https-proxy-agent "^4.0.0"
|
https-proxy-agent "^5.0.0"
|
||||||
node-fetch "^2.2.0"
|
node-fetch "^2.6.1"
|
||||||
stream-events "^1.0.5"
|
stream-events "^1.0.5"
|
||||||
uuid "^3.3.2"
|
uuid "^8.0.0"
|
||||||
|
|
||||||
terser@^3.7.3:
|
terser@^3.7.3:
|
||||||
version "3.17.0"
|
version "3.17.0"
|
||||||
|
@ -8109,6 +8083,11 @@ uuid@^3.3.2:
|
||||||
resolved "https://registry.yarnpkg.com/uuid/-/uuid-3.4.0.tgz#b23e4358afa8a202fe7a100af1f5f883f02007ee"
|
resolved "https://registry.yarnpkg.com/uuid/-/uuid-3.4.0.tgz#b23e4358afa8a202fe7a100af1f5f883f02007ee"
|
||||||
integrity sha512-HjSDRw6gZE5JMggctHBcjVak08+KEVhSIiDzFnT9S9aegmp85S/bReBVTb4QTFaRNptJ9kuYaNhnbNEOkbKb/A==
|
integrity sha512-HjSDRw6gZE5JMggctHBcjVak08+KEVhSIiDzFnT9S9aegmp85S/bReBVTb4QTFaRNptJ9kuYaNhnbNEOkbKb/A==
|
||||||
|
|
||||||
|
uuid@^8.0.0:
|
||||||
|
version "8.3.2"
|
||||||
|
resolved "https://registry.yarnpkg.com/uuid/-/uuid-8.3.2.tgz#80d5b5ced271bb9af6c445f21a1a04c606cefbe2"
|
||||||
|
integrity sha512-+NYs2QeMWy+GWFOEm9xnn6HCDp0l7QBD7ml8zLUmJ+93Q5NF0NocErnwkTkXVFNiX3/fpC6afS8Dhb/gz7R7eg==
|
||||||
|
|
||||||
v8-compile-cache@^2.0.0, v8-compile-cache@^2.0.3, v8-compile-cache@^2.3.0:
|
v8-compile-cache@^2.0.0, v8-compile-cache@^2.0.3, v8-compile-cache@^2.3.0:
|
||||||
version "2.3.0"
|
version "2.3.0"
|
||||||
resolved "https://registry.yarnpkg.com/v8-compile-cache/-/v8-compile-cache-2.3.0.tgz#2de19618c66dc247dcfb6f99338035d8245a2cee"
|
resolved "https://registry.yarnpkg.com/v8-compile-cache/-/v8-compile-cache-2.3.0.tgz#2de19618c66dc247dcfb6f99338035d8245a2cee"
|
||||||
|
|
Loading…
Reference in New Issue