diff --git a/.github/workflows/test_infrastructure.yml b/.github/workflows/test_infrastructure.yml new file mode 100644 index 0000000000..1ab3c182bc --- /dev/null +++ b/.github/workflows/test_infrastructure.yml @@ -0,0 +1,35 @@ +name: Build and Test / Infrastructure + +concurrency: + group: '${{ github.head_ref || github.ref }} Infrastructure' + cancel-in-progress: true + +on: + pull_request: + types: + - opened + - synchronize + push: + branches: + - master + +jobs: + test: + name: Infrastructure Test + runs-on: ubuntu-latest + steps: + - name: Checkout + uses: actions/checkout@v2.3.4 + + - name: Install Node + uses: actions/setup-node@v3 + with: + node-version-file: .nvmrc + cache: npm + cache-dependency-path: ./infrastructure/package.json + + - name: Install NPM Dependencies + run: npm ci + + - name: Infrastructure Test + run: npm run action infrastructure/test diff --git a/client/infrastructure/i18n.ts b/client/infrastructure/i18n.ts deleted file mode 100644 index 52011620df..0000000000 --- a/client/infrastructure/i18n.ts +++ /dev/null @@ -1,12 +0,0 @@ -import {PrimitiveType, FormatXMLElementFn} from 'intl-messageformat'; - -export type FormattableMessage = - | string - | symbol - | object - | PrimitiveType - | FormatXMLElementFn; - -export interface Localizer { - (messageID: string, ...formatKeyValueList: FormattableMessage[]): string; -} diff --git a/client/infrastructure/memory_storage.ts b/client/infrastructure/memory_storage.ts deleted file mode 100644 index 7449db07e1..0000000000 --- a/client/infrastructure/memory_storage.ts +++ /dev/null @@ -1,41 +0,0 @@ -// Copyright 2020 The Outline Authors -// -// Licensed under the Apache License, Version 2.0 (the "License"); -// you may not use this file except in compliance with the License. -// You may obtain a copy of the License at -// -// http://www.apache.org/licenses/LICENSE-2.0 -// -// Unless required by applicable law or agreed to in writing, software -// distributed under the License is distributed on an "AS IS" BASIS, -// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -// See the License for the specific language governing permissions and -// limitations under the License. - -export class InMemoryStorage implements Storage { - readonly length = 0; - [key: string]: {}; - [index: number]: string; - - constructor(private store: Map = new Map()) {} - - clear(): void { - throw new Error('InMemoryStorage.clear not implemented'); - } - - getItem(key: string): string | null { - return this.store.get(key) || null; - } - - key(): string | null { - throw new Error('InMemoryStorage.key not implemented'); - } - - removeItem(key: string): void { - this.store.delete(key); - } - - setItem(key: string, data: string): void { - this.store.set(key, data); - } -} diff --git a/client/package.json b/client/package.json index 7d7bb2e7fe..e086c54d09 100644 --- a/client/package.json +++ b/client/package.json @@ -45,6 +45,7 @@ "cordova-plugin-statusbar": "^2.2.3", "electron-updater": "^5.0.5", "lit": "^2.2.2", + "@outline/infrastructure": "file:../infrastructure", "ShadowsocksConfig": "github:Jigsaw-Code/outline-shadowsocksconfig#v0.2.1", "socks": "^1.1.10", "sudo-prompt": "^9.2.1", @@ -95,7 +96,6 @@ "html-webpack-plugin": "^5.1.0", "husky": "^1.3.1", "i18n-strings-files": "^2.0.0", - "intl-messageformat": "^9.12.0", "istanbul": "^0.4.5", "karma": "^6.4.2", "karma-chrome-launcher": "^3.1.0", diff --git a/client/src/www/app/app.ts b/client/src/www/app/app.ts index d6b741ac93..e067987e8a 100644 --- a/client/src/www/app/app.ts +++ b/client/src/www/app/app.ts @@ -12,6 +12,8 @@ // See the License for the specific language governing permissions and // limitations under the License. +import {Localizer} from '@outline/infrastructure/i18n'; +import {OperationTimedOut} from '@outline/infrastructure/timeout_promise'; import {Clipboard} from './clipboard'; import {EnvironmentVariables} from './environment'; @@ -20,8 +22,6 @@ import {Settings, SettingsKey} from './settings'; import {Updater} from './updater'; import {UrlInterceptor} from './url_interceptor'; import {VpnInstaller} from './vpn_installer'; -import {Localizer} from '../../../infrastructure/i18n'; -import {OperationTimedOut} from '../../../infrastructure/timeout_promise'; import * as errors from '../model/errors'; import * as events from '../model/events'; import {Server} from '../model/server'; diff --git a/client/src/www/app/main.ts b/client/src/www/app/main.ts index 30ec90eaf9..46ac8b4b33 100644 --- a/client/src/www/app/main.ts +++ b/client/src/www/app/main.ts @@ -15,6 +15,7 @@ import '../ui_components/app-root.js'; +import {Localizer} from '@outline/infrastructure/i18n'; import {makeConfig, SIP002_URI} from 'ShadowsocksConfig'; import {App} from './app'; @@ -23,7 +24,6 @@ import {OutlineServerRepository} from './outline_server_repository'; import {OutlinePlatform} from './platform'; import {Settings} from './settings'; import {TunnelFactory} from './tunnel'; -import {Localizer} from '../../../infrastructure/i18n.js'; import {EventQueue} from '../model/events'; // Used to determine whether to use Polymer functionality on app initialization failure. diff --git a/client/src/www/app/outline_server_repository/outline_server_repository.spec.ts b/client/src/www/app/outline_server_repository/outline_server_repository.spec.ts index 9025f950b6..e6298666ec 100644 --- a/client/src/www/app/outline_server_repository/outline_server_repository.spec.ts +++ b/client/src/www/app/outline_server_repository/outline_server_repository.spec.ts @@ -12,11 +12,11 @@ // See the License for the specific language governing permissions and // limitations under the License. +import {InMemoryStorage} from '@outline/infrastructure/memory_storage'; import {makeConfig, SIP002_URI} from 'ShadowsocksConfig'; import {OutlineServerRepository, ServersStorageV0, ServersStorageV1, serversStorageV0ConfigToAccessKey} from '.'; import {OutlineServer} from './server'; -import {InMemoryStorage} from '../../../../infrastructure/memory_storage'; import {ServerIncompatible, ServerUrlInvalid, ShadowsocksUnsupportedCipher} from '../../model/errors'; import {EventQueue, ServerAdded, ServerForgetUndone, ServerForgotten, ServerRenamed} from '../../model/events'; import {FakeOutlineTunnel} from '../fake_tunnel'; diff --git a/client/src/www/app/settings.spec.ts b/client/src/www/app/settings.spec.ts index 23d72f4c28..b171f2fbe4 100644 --- a/client/src/www/app/settings.spec.ts +++ b/client/src/www/app/settings.spec.ts @@ -12,8 +12,9 @@ // See the License for the specific language governing permissions and // limitations under the License. +import {InMemoryStorage} from '@outline/infrastructure/memory_storage'; + import {Settings, SettingsKey} from './settings'; -import {InMemoryStorage} from '../../../infrastructure/memory_storage'; const FAKE_SETTINGS_KEYS = ['key', 'key1', 'key2']; diff --git a/client/src/www/model/errors.ts b/client/src/www/model/errors.ts index adc49d79a3..8a6a743158 100644 --- a/client/src/www/model/errors.ts +++ b/client/src/www/model/errors.ts @@ -12,8 +12,9 @@ // See the License for the specific language governing permissions and // limitations under the License. +import {CustomError} from '@outline/infrastructure/custom_error'; + import {Server} from './server'; -import {CustomError} from '../../../infrastructure/custom_error'; export class ServerAlreadyAdded extends CustomError { constructor(public readonly server: Server) { diff --git a/client/src/www/testing/localize.ts b/client/src/www/testing/localize.ts index dcc5ff593f..b6c4547354 100644 --- a/client/src/www/testing/localize.ts +++ b/client/src/www/testing/localize.ts @@ -14,9 +14,9 @@ limitations under the License. */ +import type {FormattableMessage, Localizer} from '@outline/infrastructure/i18n'; import IntlMessageFormat from 'intl-messageformat'; -import type {FormattableMessage, Localizer} from '../../../infrastructure/i18n'; import englishMessages from '../messages/en.json'; export const localize: Localizer = (messageID: string, ...formatKeyValueList: FormattableMessage[]): string => { diff --git a/client/src/www/views/contact_view/index.ts b/client/src/www/views/contact_view/index.ts index 4508eff6fc..d287b611cd 100644 --- a/client/src/www/views/contact_view/index.ts +++ b/client/src/www/views/contact_view/index.ts @@ -22,6 +22,7 @@ import '@material/mwc-radio'; import '@material/mwc-select'; import '@material/mwc-formfield'; +import {Localizer} from '@outline/infrastructure/i18n'; import {html, css, LitElement, TemplateResult, nothing} from 'lit'; import {customElement, property, state} from 'lit/decorators.js'; import {Ref, createRef, ref} from 'lit/directives/ref.js'; @@ -31,7 +32,6 @@ import './support_form'; import {AppType} from './app_type'; import {IssueType, UNSUPPORTED_ISSUE_TYPE_HELPPAGES} from './issue_type'; import {FormValues, SupportForm, ValidFormValues} from './support_form'; -import {Localizer} from '../../../../infrastructure/i18n'; import {OutlineErrorReporter} from '../../shared/error_reporter'; /** The possible steps in the stepper. Only one step is shown at a time. */ diff --git a/client/src/www/views/contact_view/support_form/index.ts b/client/src/www/views/contact_view/support_form/index.ts index 559d4a6deb..e80a5ff448 100644 --- a/client/src/www/views/contact_view/support_form/index.ts +++ b/client/src/www/views/contact_view/support_form/index.ts @@ -21,12 +21,12 @@ import '@material/mwc-select'; import '@material/mwc-textarea'; import '@material/mwc-textfield'; +import {Localizer} from '@outline/infrastructure/i18n'; import {html, css, LitElement, TemplateResult, nothing, PropertyValues} from 'lit'; import {customElement, property, state} from 'lit/decorators.js'; import {live} from 'lit/directives/live.js'; import {createRef, Ref, ref} from 'lit/directives/ref.js'; -import {Localizer} from '../../../../../infrastructure/i18n'; import {AppType} from '../app_type'; diff --git a/client/src/www/views/servers_view/index.ts b/client/src/www/views/servers_view/index.ts index 2722cd227a..896b396ea3 100644 --- a/client/src/www/views/servers_view/index.ts +++ b/client/src/www/views/servers_view/index.ts @@ -16,6 +16,7 @@ import '@material/mwc-button'; +import { Localizer } from '@outline/infrastructure/i18n'; import {css, html, LitElement} from 'lit'; import {customElement, property} from 'lit/decorators.js'; import { DirectiveResult } from 'lit/directive'; @@ -25,7 +26,6 @@ import {ServerConnectionState as _ServerConnectionState} from './server_connecti import './server_connection_indicator'; import './server_list'; import {ServerListItem as _ServerListItem} from './server_list_item'; -import { Localizer } from '../../../../infrastructure/i18n'; export type ServerListItem = _ServerListItem; diff --git a/client/src/www/views/servers_view/server_list/index.ts b/client/src/www/views/servers_view/server_list/index.ts index 7985e3c4bf..266ddef124 100644 --- a/client/src/www/views/servers_view/server_list/index.ts +++ b/client/src/www/views/servers_view/server_list/index.ts @@ -11,11 +11,11 @@ limitations under the License. */ +import {Localizer} from '@outline/infrastructure/i18n'; import {css, html, LitElement} from 'lit'; import {customElement, property} from 'lit/decorators.js'; import '../server_list_item/server_card'; -import {Localizer} from '../../../../../infrastructure/i18n'; import {ServerListItem} from '../server_list_item'; @customElement('server-list') diff --git a/client/src/www/views/servers_view/server_list_item/index.ts b/client/src/www/views/servers_view/server_list_item/index.ts index ba397799b1..ad76fd9a18 100644 --- a/client/src/www/views/servers_view/server_list_item/index.ts +++ b/client/src/www/views/servers_view/server_list_item/index.ts @@ -13,9 +13,9 @@ import {Menu} from '@material/mwc-menu'; +import {type Localizer} from '@outline/infrastructure/i18n'; import {Ref} from 'lit/directives/ref'; -import {Localizer} from '../../../../../infrastructure/i18n'; import {ServerConnectionState} from '../server_connection_indicator'; export enum ServerListItemEvent { diff --git a/client/src/www/views/servers_view/server_list_item/server_card/index.ts b/client/src/www/views/servers_view/server_list_item/server_card/index.ts index 024bb6d627..ba5cbf6642 100644 --- a/client/src/www/views/servers_view/server_list_item/server_card/index.ts +++ b/client/src/www/views/servers_view/server_list_item/server_card/index.ts @@ -16,14 +16,13 @@ import '@material/mwc-button'; import '@material/mwc-icon-button'; import '@material/mwc-menu'; +import {Localizer} from '@outline/infrastructure/i18n'; import {css, html, LitElement} from 'lit'; import {customElement, property} from 'lit/decorators.js'; import {createRef, Ref, ref} from 'lit/directives/ref.js'; - import '../../server_connection_indicator'; import {ServerListItem, ServerListItemElement, ServerListItemEvent} from '..'; -import {Localizer} from '../../../../../../infrastructure/i18n'; import {ServerConnectionState} from '../../server_connection_indicator'; const sharedCSS = css` diff --git a/commitlint.config.js b/commitlint.config.js index fe4a3f9766..bc39363b30 100644 --- a/commitlint.config.js +++ b/commitlint.config.js @@ -16,6 +16,7 @@ module.exports = { 'client/electron/windows', 'devtools', 'docs', + 'infrastructure', 'manager', 'manager/linux', 'manager/mac', diff --git a/server_manager/infrastructure/crypto.ts b/infrastructure/crypto.ts similarity index 98% rename from server_manager/infrastructure/crypto.ts rename to infrastructure/crypto.ts index ec4329cad3..f693ff58c2 100644 --- a/server_manager/infrastructure/crypto.ts +++ b/infrastructure/crypto.ts @@ -15,7 +15,7 @@ import * as forge from 'node-forge'; // Keys are in OpenSSH format -export class KeyPair { +export interface KeyPair { public: string; private: string; } diff --git a/client/infrastructure/custom_error.ts b/infrastructure/custom_error.ts similarity index 100% rename from client/infrastructure/custom_error.ts rename to infrastructure/custom_error.ts diff --git a/server_manager/infrastructure/hex_encoding.ts b/infrastructure/hex_encoding.ts similarity index 100% rename from server_manager/infrastructure/hex_encoding.ts rename to infrastructure/hex_encoding.ts diff --git a/server_manager/infrastructure/i18n.spec.ts b/infrastructure/i18n.spec.ts similarity index 90% rename from server_manager/infrastructure/i18n.spec.ts rename to infrastructure/i18n.spec.ts index d9d62b1fc4..c2a79f6beb 100644 --- a/server_manager/infrastructure/i18n.spec.ts +++ b/infrastructure/i18n.spec.ts @@ -19,25 +19,25 @@ describe('LanguageMatcher', () => { const SUPPORTED_LANGUAGES = i18n.languageList(['es', 'pt-BR', 'ru']); const matcher = new i18n.LanguageMatcher(SUPPORTED_LANGUAGES, undefined); const supportedLanguage = matcher.getBestSupportedLanguage(i18n.languageList(['pt-PT'])); - expect(supportedLanguage.string()).toEqual('pt-BR'); + expect(supportedLanguage?.string()).toEqual('pt-BR'); }); it('returns the right variant', () => { const SUPPORTED_LANGUAGES = i18n.languageList(['en-GB', 'en-IN', 'en-US']); const matcher = new i18n.LanguageMatcher(SUPPORTED_LANGUAGES, undefined); const supportedLanguage = matcher.getBestSupportedLanguage(i18n.languageList(['en-IN'])); - expect(supportedLanguage.string()).toEqual('en-IN'); + expect(supportedLanguage?.string()).toEqual('en-IN'); }); it('prefers first matched user language', () => { const SUPPORTED_LANGUAGES = i18n.languageList(['en-US', 'pt-BR']); const matcher = new i18n.LanguageMatcher(SUPPORTED_LANGUAGES, undefined); const supportedLanguage = matcher.getBestSupportedLanguage(i18n.languageList(['cn', 'en-GB', 'pt-BR'])); - expect(supportedLanguage.string()).toEqual('en-US'); + expect(supportedLanguage?.string()).toEqual('en-US'); }); it('returns default on no match', () => { const SUPPORTED_LANGUAGES = i18n.languageList(['es', 'pt-BR', 'ru']); const matcher = new i18n.LanguageMatcher(SUPPORTED_LANGUAGES, new i18n.LanguageCode('fr')); const supportedLanguage = matcher.getBestSupportedLanguage(i18n.languageList(['cn'])); - expect(supportedLanguage.string()).toEqual('fr'); + expect(supportedLanguage?.string()).toEqual('fr'); }); it('returns undefined on no match and no default', () => { const SUPPORTED_LANGUAGES = i18n.languageList(['es', 'pt-BR', 'ru']); diff --git a/server_manager/infrastructure/i18n.ts b/infrastructure/i18n.ts similarity index 88% rename from server_manager/infrastructure/i18n.ts rename to infrastructure/i18n.ts index fc1935cde6..d31b13bfc8 100644 --- a/server_manager/infrastructure/i18n.ts +++ b/infrastructure/i18n.ts @@ -12,6 +12,19 @@ // See the License for the specific language governing permissions and // limitations under the License. +import {PrimitiveType, FormatXMLElementFn} from 'intl-messageformat'; + +export type FormattableMessage = + | string + | symbol + | object + | PrimitiveType + | FormatXMLElementFn; + +export interface Localizer { + (messageID: string, ...formatKeyValueList: FormattableMessage[]): string; +} + export class LanguageCode { private language: string; private normalizedLanguage: string; @@ -32,7 +45,7 @@ export class LanguageCode { } export class LanguageMatcher { - constructor(private supportedLanguages: LanguageCode[], private defaultLanguage: LanguageCode = undefined) {} + constructor(private supportedLanguages: LanguageCode[], private defaultLanguage?: LanguageCode) {} // Goes over each user language, trying to find the supported language that matches // the best. We'll trim variants of the user and supported languages in order to find diff --git a/server_manager/infrastructure/memory_storage.ts b/infrastructure/memory_storage.ts similarity index 97% rename from server_manager/infrastructure/memory_storage.ts rename to infrastructure/memory_storage.ts index 63298186be..35bdfb0b36 100644 --- a/server_manager/infrastructure/memory_storage.ts +++ b/infrastructure/memory_storage.ts @@ -13,7 +13,7 @@ // limitations under the License. export class InMemoryStorage implements Storage { - readonly length: number; + readonly length = 0; [key: string]: {}; [index: number]: string; diff --git a/infrastructure/package.json b/infrastructure/package.json new file mode 100644 index 0000000000..e7601293d6 --- /dev/null +++ b/infrastructure/package.json @@ -0,0 +1,21 @@ +{ + "name": "@outline/infrastructure", + "version": "0.0.0", + "private": true, + "description": "Shared infrastructure code.", + "scripts": { + "clean": "rm -rf node_modules" + }, + "devDependencies": { + "@types/jasmine": "^5.1.4", + "@types/node-forge": "^1.3.11", + "https-browserify": "^1.0.0", + "intl-messageformat": "^10.5.11", + "jasmine": "^5.1.0", + "stream-http": "^3.2.0", + "typescript": "^5.4.5" + }, + "dependencies": { + "node-forge": "^1.3.1" + } +} diff --git a/server_manager/infrastructure/path_api.spec.ts b/infrastructure/path_api.spec.ts similarity index 87% rename from server_manager/infrastructure/path_api.spec.ts rename to infrastructure/path_api.spec.ts index 1e80972733..e80eb99df4 100644 --- a/server_manager/infrastructure/path_api.spec.ts +++ b/infrastructure/path_api.spec.ts @@ -12,16 +12,16 @@ // See the License for the specific language governing permissions and // limitations under the License. -import {PathApiClient} from './path_api'; +import {type HttpRequest, type HttpResponse, PathApiClient} from './path_api'; describe('PathApi', () => { // Mock fetcher - let lastRequest: HttpRequest; - let nextResponse: Promise; + let lastRequest: HttpRequest | undefined; + let nextResponse: Promise | undefined; - const fetcher = (request: HttpRequest) => { + const fetcher = (request: HttpRequest): Promise => { lastRequest = request; - return nextResponse; + return nextResponse!; }; beforeEach(() => { diff --git a/server_manager/infrastructure/path_api.ts b/infrastructure/path_api.ts similarity index 97% rename from server_manager/infrastructure/path_api.ts rename to infrastructure/path_api.ts index 4529a1f3fd..50aa0105b1 100644 --- a/server_manager/infrastructure/path_api.ts +++ b/infrastructure/path_api.ts @@ -119,13 +119,14 @@ export class PathApiClient { try { response = await this.fetcher(request); } catch (e) { - throw new ServerApiError(`API request to ${path} failed due to network error: ${e.message}`); + const msg = e instanceof Error ? e.message : 'unknown'; + throw new ServerApiError(`API request to ${path} failed due to network error: ${msg}`); } if (response.status < 200 || response.status >= 300) { throw new ServerApiError(`API request to ${path} failed with status ${response.status}`, response); } if (!response.body) { - return; + return undefined as T; } // Assume JSON and unsafe cast to `T`. return JSON.parse(response.body); diff --git a/server_manager/infrastructure/sentry.spec.ts b/infrastructure/sentry.spec.ts similarity index 100% rename from server_manager/infrastructure/sentry.spec.ts rename to infrastructure/sentry.spec.ts diff --git a/server_manager/infrastructure/sentry.ts b/infrastructure/sentry.ts similarity index 100% rename from server_manager/infrastructure/sentry.ts rename to infrastructure/sentry.ts diff --git a/server_manager/infrastructure/sleep.ts b/infrastructure/sleep.ts similarity index 100% rename from server_manager/infrastructure/sleep.ts rename to infrastructure/sleep.ts diff --git a/infrastructure/test.action.sh b/infrastructure/test.action.sh new file mode 100644 index 0000000000..84234704fc --- /dev/null +++ b/infrastructure/test.action.sh @@ -0,0 +1,26 @@ +#!/bin/bash -eu +# +# Copyright 2024 The Outline Authors +# +# Licensed under the Apache License, Version 2.0 (the "License"); +# you may not use this file except in compliance with the License. +# You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. + +set -e + +readonly TEST_DIR="${BUILD_DIR}/js/infrastructure/" +rm -rf "${TEST_DIR}" + +# Use commonjs modules, jasmine runs in node. +tsc -p "${ROOT_DIR}/infrastructure" --outDir "${TEST_DIR}" --module commonjs +jasmine "output/build/js/**/*.spec.js" + +rm -rf "${TEST_DIR}" diff --git a/client/infrastructure/timeout_promise.ts b/infrastructure/timeout_promise.ts similarity index 100% rename from client/infrastructure/timeout_promise.ts rename to infrastructure/timeout_promise.ts diff --git a/infrastructure/tsconfig.json b/infrastructure/tsconfig.json new file mode 100644 index 0000000000..50c5851b3a --- /dev/null +++ b/infrastructure/tsconfig.json @@ -0,0 +1,17 @@ +{ + "compilerOptions": { + "experimentalDecorators": true, + "target": "es2022", + "module": "commonjs", + "noImplicitAny": true, + "noImplicitThis": true, + "rootDir": ".", + "outDir": "./dist", + "declaration": true, + "esModuleInterop": true, + "forceConsistentCasingInFileNames": true, + "strict": true, + "skipLibCheck": true + }, + "include": ["*.ts"] +} diff --git a/server_manager/infrastructure/value_stream.spec.ts b/infrastructure/value_stream.spec.ts similarity index 100% rename from server_manager/infrastructure/value_stream.spec.ts rename to infrastructure/value_stream.spec.ts diff --git a/server_manager/infrastructure/value_stream.ts b/infrastructure/value_stream.ts similarity index 84% rename from server_manager/infrastructure/value_stream.ts rename to infrastructure/value_stream.ts index 05753be2be..01ddcedc35 100644 --- a/server_manager/infrastructure/value_stream.ts +++ b/infrastructure/value_stream.ts @@ -20,7 +20,7 @@ * guaranteed to see the last value in a series of updates. */ export class ValueStream { - private wakers: Array<(closed: boolean) => void> = []; + private wakers: Array<(closed: boolean) => void> | null = []; constructor(private value: T) {} get(): T { @@ -28,7 +28,7 @@ export class ValueStream { } set(newValue: T) { - if (this.isClosed()) { + if (this.wakers === null) { throw new Error('Cannot change a closed value stream'); } this.value = newValue; @@ -38,7 +38,7 @@ export class ValueStream { } close() { - if (this.isClosed()) { + if (this.wakers === null) { return; } const finalWakers = this.wakers; @@ -51,10 +51,12 @@ export class ValueStream { } private nextChange(): Promise { - if (this.isClosed()) { - return Promise.resolve(true); - } - return new Promise(resolve => this.wakers.push(resolve)); + return new Promise(resolve => { + if (this.wakers === null) { + return resolve(true); + } + return this.wakers.push(resolve) + }); } async *watch(): AsyncGenerator { diff --git a/jasmine.json b/jasmine.json deleted file mode 100644 index 0ec6b82d6b..0000000000 --- a/jasmine.json +++ /dev/null @@ -1,5 +0,0 @@ -{ - "spec_dir": ".", - "spec_files": ["output/build/js/**/*.spec.js"], - "stopSpecOnExpectationFailure": false -} diff --git a/package-lock.json b/package-lock.json index d148810b3c..3877a8f0bd 100644 --- a/package-lock.json +++ b/package-lock.json @@ -8,7 +8,8 @@ "name": "outline-apps", "workspaces": [ "server_manager", - "client" + "client", + "infrastructure" ], "devDependencies": { "@types/node-fetch": "^2.6.7", @@ -16,7 +17,6 @@ "@typescript-eslint/parser": "^7.7.0", "electron-builder": "^24.13.3", "eslint-plugin-import": "^2.29.1", - "jasmine": "^5.1.0", "node-fetch": "^2.6.7" }, "engines": { @@ -36,6 +36,7 @@ "@material/mwc-select": "^0.25.3", "@material/mwc-textarea": "^0.25.3", "@material/mwc-textfield": "^0.25.3", + "@outline/infrastructure": "file:../infrastructure", "@polymer/app-layout": "^3.1.0", "@polymer/app-localize-behavior": "^3.0.1", "@polymer/app-route": "^3.0.2", @@ -116,7 +117,6 @@ "html-webpack-plugin": "^5.1.0", "husky": "^1.3.1", "i18n-strings-files": "^2.0.0", - "intl-messageformat": "^9.12.0", "istanbul": "^0.4.5", "karma": "^6.4.2", "karma-chrome-launcher": "^3.1.0", @@ -149,6 +149,12 @@ "ios-deploy": "^1.11.4" } }, + "client/node_modules/@types/jasmine": { + "version": "4.6.4", + "resolved": "https://registry.npmjs.org/@types/jasmine/-/jasmine-4.6.4.tgz", + "integrity": "sha512-qCw5sVW+ylTnrEhe5kfX4l6MgU9REXIVDa/lWEcvTOUmd+LqDYwyjovDq+Zk9blElaEHOj1URDQ/djEBVRf+pw==", + "dev": true + }, "client/node_modules/electron-builder": { "version": "23.6.0", "resolved": "https://registry.npmjs.org/electron-builder/-/electron-builder-23.6.0.tgz", @@ -224,11 +230,40 @@ "url": "https://opencollective.com/node-fetch" } }, + "client/node_modules/typescript": { + "version": "4.9.5", + "resolved": "https://registry.npmjs.org/typescript/-/typescript-4.9.5.tgz", + "integrity": "sha512-1FXk9E2Hm+QzZQ7z+McJiHL4NW1F2EzMu9Nq9i3zAaGqibafqYwCVU6WyWAuyQRRzOlxou8xZSyXLEN8oKj24g==", + "dev": true, + "bin": { + "tsc": "bin/tsc", + "tsserver": "bin/tsserver" + }, + "engines": { + "node": ">=4.2.0" + } + }, "client/src/cordova/plugin": { "name": "cordova-plugin-outline", "version": "0.0.0", "dev": true }, + "infrastructure": { + "name": "@outline/infrastructure", + "version": "0.0.0", + "dependencies": { + "node-forge": "^1.3.1" + }, + "devDependencies": { + "@types/jasmine": "^5.1.4", + "@types/node-forge": "^1.3.11", + "https-browserify": "^1.0.0", + "intl-messageformat": "^10.5.11", + "jasmine": "^5.1.0", + "stream-http": "^3.2.0", + "typescript": "^5.4.5" + } + }, "node_modules/@aashutoshrathi/word-wrap": { "version": "1.2.6", "resolved": "https://registry.npmjs.org/@aashutoshrathi/word-wrap/-/word-wrap-1.2.6.tgz", @@ -2203,47 +2238,52 @@ } }, "node_modules/@formatjs/ecma402-abstract": { - "version": "1.11.4", + "version": "1.18.2", + "resolved": "https://registry.npmjs.org/@formatjs/ecma402-abstract/-/ecma402-abstract-1.18.2.tgz", + "integrity": "sha512-+QoPW4csYALsQIl8GbN14igZzDbuwzcpWrku9nyMXlaqAlwRBgl5V+p0vWMGFqHOw37czNXaP/lEk4wbLgcmtA==", "dev": true, - "license": "MIT", "dependencies": { - "@formatjs/intl-localematcher": "0.2.25", - "tslib": "^2.1.0" + "@formatjs/intl-localematcher": "0.5.4", + "tslib": "^2.4.0" } }, "node_modules/@formatjs/fast-memoize": { - "version": "1.2.1", + "version": "2.2.0", + "resolved": "https://registry.npmjs.org/@formatjs/fast-memoize/-/fast-memoize-2.2.0.tgz", + "integrity": "sha512-hnk/nY8FyrL5YxwP9e4r9dqeM6cAbo8PeU9UjyXojZMNvVad2Z06FAVHyR3Ecw6fza+0GH7vdJgiKIVXTMbSBA==", "dev": true, - "license": "MIT", "dependencies": { - "tslib": "^2.1.0" + "tslib": "^2.4.0" } }, "node_modules/@formatjs/icu-messageformat-parser": { - "version": "2.1.0", + "version": "2.7.6", + "resolved": "https://registry.npmjs.org/@formatjs/icu-messageformat-parser/-/icu-messageformat-parser-2.7.6.tgz", + "integrity": "sha512-etVau26po9+eewJKYoiBKP6743I1br0/Ie00Pb/S/PtmYfmjTcOn2YCh2yNkSZI12h6Rg+BOgQYborXk46BvkA==", "dev": true, - "license": "MIT", "dependencies": { - "@formatjs/ecma402-abstract": "1.11.4", - "@formatjs/icu-skeleton-parser": "1.3.6", - "tslib": "^2.1.0" + "@formatjs/ecma402-abstract": "1.18.2", + "@formatjs/icu-skeleton-parser": "1.8.0", + "tslib": "^2.4.0" } }, "node_modules/@formatjs/icu-skeleton-parser": { - "version": "1.3.6", + "version": "1.8.0", + "resolved": "https://registry.npmjs.org/@formatjs/icu-skeleton-parser/-/icu-skeleton-parser-1.8.0.tgz", + "integrity": "sha512-QWLAYvM0n8hv7Nq5BEs4LKIjevpVpbGLAJgOaYzg9wABEoX1j0JO1q2/jVkO6CVlq0dbsxZCngS5aXbysYueqA==", "dev": true, - "license": "MIT", "dependencies": { - "@formatjs/ecma402-abstract": "1.11.4", - "tslib": "^2.1.0" + "@formatjs/ecma402-abstract": "1.18.2", + "tslib": "^2.4.0" } }, "node_modules/@formatjs/intl-localematcher": { - "version": "0.2.25", + "version": "0.5.4", + "resolved": "https://registry.npmjs.org/@formatjs/intl-localematcher/-/intl-localematcher-0.5.4.tgz", + "integrity": "sha512-zTwEpWOzZ2CiKcB93BLngUX59hQkuZjT2+SAQEscSm52peDW/getsawMcWF1rGRpMCX6D7nSJA3CzJ8gn13N/g==", "dev": true, - "license": "MIT", "dependencies": { - "tslib": "^2.1.0" + "tslib": "^2.4.0" } }, "node_modules/@formatjs/intl-unified-numberformat": { @@ -4937,6 +4977,10 @@ "node": ">=6" } }, + "node_modules/@outline/infrastructure": { + "resolved": "infrastructure", + "link": true + }, "node_modules/@pkgjs/parseargs": { "version": "0.11.0", "resolved": "https://registry.npmjs.org/@pkgjs/parseargs/-/parseargs-0.11.0.tgz", @@ -6145,9 +6189,9 @@ } }, "node_modules/@types/jasmine": { - "version": "4.3.6", - "resolved": "https://registry.npmjs.org/@types/jasmine/-/jasmine-4.3.6.tgz", - "integrity": "sha512-3N0FpQTeiWjm+Oo1WUYWguUS7E6JLceiGTriFrG8k5PU7zRLJCzLcWURU3wjMbZGS//a2/LgjsnO3QxIlwxt9g==", + "version": "5.1.4", + "resolved": "https://registry.npmjs.org/@types/jasmine/-/jasmine-5.1.4.tgz", + "integrity": "sha512-px7OMFO/ncXxixDe1zR13V1iycqWae0MxTaw62RpFlksUi5QuNWgQJFkTQjIOvrmutJbI7Fp2Y2N1F6D2R4G6w==", "dev": true }, "node_modules/@types/json-schema": { @@ -9872,6 +9916,12 @@ "url": "https://github.com/sponsors/sindresorhus" } }, + "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": "sha512-HpGFw18DgFWlncDfjTa2rcQ4W88O1mC8e8yZ2AvQY5KDaktSTwo+KRf6nHK6FRI5FyRyb/5T6+TSxfP7QyGsmQ==", + "dev": true + }, "node_modules/builtins": { "version": "1.0.3", "dev": true, @@ -10892,19 +10942,6 @@ "node": ">=16 || 14 >=14.17" } }, - "node_modules/config-file-ts/node_modules/typescript": { - "version": "5.4.5", - "resolved": "https://registry.npmjs.org/typescript/-/typescript-5.4.5.tgz", - "integrity": "sha512-vcI4UpRgg81oIRUFwR0WSIHKt11nJ7SAVlYNIu+QpqeyXP+gpQJy/Z4+F0aGxSE4MqwjyXvW/TzgkLAx2AGHwQ==", - "dev": true, - "bin": { - "tsc": "bin/tsc", - "tsserver": "bin/tsserver" - }, - "engines": { - "node": ">=14.17" - } - }, "node_modules/connect": { "version": "3.7.0", "dev": true, @@ -18476,6 +18513,12 @@ "npm": ">=1.3.7" } }, + "node_modules/https-browserify": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/https-browserify/-/https-browserify-1.0.0.tgz", + "integrity": "sha512-J+FkSdyD+0mA0N+81tMotaRMfSL9SGi+xpD3T6YApKsc3bGSXJlfXri3VyFOeYkfLRQisDk1W+jIFFKBeUBbBg==", + "dev": true + }, "node_modules/https-proxy-agent": { "version": "5.0.1", "license": "MIT", @@ -19028,14 +19071,15 @@ "integrity": "sha512-OEUYNA7D06agqPOYhbTkl0T8HA3QKSuwWh1HiClEnpd9vw7N+3XsQt5iZ0GUEchp5CW1fQk/tary+NsbF3yQ1Q==" }, "node_modules/intl-messageformat": { - "version": "9.13.0", + "version": "10.5.11", + "resolved": "https://registry.npmjs.org/intl-messageformat/-/intl-messageformat-10.5.11.tgz", + "integrity": "sha512-eYq5fkFBVxc7GIFDzpFQkDOZgNayNTQn4Oufe8jw6YY6OHVw70/4pA3FyCsQ0Gb2DnvEJEMmN2tOaXUGByM+kg==", "dev": true, - "license": "BSD-3-Clause", "dependencies": { - "@formatjs/ecma402-abstract": "1.11.4", - "@formatjs/fast-memoize": "1.2.1", - "@formatjs/icu-messageformat-parser": "2.1.0", - "tslib": "^2.1.0" + "@formatjs/ecma402-abstract": "1.18.2", + "@formatjs/fast-memoize": "2.2.0", + "@formatjs/icu-messageformat-parser": "2.7.6", + "tslib": "^2.4.0" } }, "node_modules/intl-messageformat-parser": { @@ -20950,21 +20994,46 @@ } }, "node_modules/karma-webpack": { - "version": "5.0.0", + "version": "5.0.1", + "resolved": "https://registry.npmjs.org/karma-webpack/-/karma-webpack-5.0.1.tgz", + "integrity": "sha512-oo38O+P3W2mSPCSUrQdySSPv1LvPpXP+f+bBimNomS5sW+1V4SuhCuW8TfJzV+rDv921w2fDSDw0xJbPe6U+kQ==", "dev": true, - "license": "MIT", "dependencies": { "glob": "^7.1.3", - "minimatch": "^3.0.4", + "minimatch": "^9.0.3", "webpack-merge": "^4.1.5" }, "engines": { - "node": ">= 6" + "node": ">= 18" }, "peerDependencies": { "webpack": "^5.0.0" } }, + "node_modules/karma-webpack/node_modules/brace-expansion": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/brace-expansion/-/brace-expansion-2.0.1.tgz", + "integrity": "sha512-XnAIvQ8eM+kC6aULx6wuQiwVsnzsi9d3WxzV3FpWTGA19F621kwdbsAcFKXgKUHZWsy+mY6iL1sHTxWEFCytDA==", + "dev": true, + "dependencies": { + "balanced-match": "^1.0.0" + } + }, + "node_modules/karma-webpack/node_modules/minimatch": { + "version": "9.0.4", + "resolved": "https://registry.npmjs.org/minimatch/-/minimatch-9.0.4.tgz", + "integrity": "sha512-KqWh+VchfxcMNRAJjj2tnsSJdNbHsVgnkBhTNrW7AjVo6OvLtxw8zfT9oLw1JSohlFzJ8jCoTgaoXvJ+kHt6fw==", + "dev": true, + "dependencies": { + "brace-expansion": "^2.0.1" + }, + "engines": { + "node": ">=16 || 14 >=14.17" + }, + "funding": { + "url": "https://github.com/sponsors/isaacs" + } + }, "node_modules/karma-webpack/node_modules/webpack-merge": { "version": "4.2.2", "dev": true, @@ -29143,6 +29212,32 @@ "integrity": "sha512-b/qaq/GlBK5xaq1yrK9/zFcyRSTNxmcZwFLGSTG0mXgZl/4Z6GgiyYOXOvY7N3eEvFRAG1bkDRz5EPGSvPYQlw==", "dev": true }, + "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, + "dependencies": { + "builtin-status-codes": "^3.0.0", + "inherits": "^2.0.4", + "readable-stream": "^3.6.0", + "xtend": "^4.0.2" + } + }, + "node_modules/stream-http/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, + "dependencies": { + "inherits": "^2.0.3", + "string_decoder": "^1.1.1", + "util-deprecate": "^1.0.1" + }, + "engines": { + "node": ">= 6" + } + }, "node_modules/stream-shift": { "version": "1.0.1", "dev": true, @@ -30795,16 +30890,16 @@ } }, "node_modules/typescript": { - "version": "4.9.5", - "resolved": "https://registry.npmjs.org/typescript/-/typescript-4.9.5.tgz", - "integrity": "sha512-1FXk9E2Hm+QzZQ7z+McJiHL4NW1F2EzMu9Nq9i3zAaGqibafqYwCVU6WyWAuyQRRzOlxou8xZSyXLEN8oKj24g==", + "version": "5.4.5", + "resolved": "https://registry.npmjs.org/typescript/-/typescript-5.4.5.tgz", + "integrity": "sha512-vcI4UpRgg81oIRUFwR0WSIHKt11nJ7SAVlYNIu+QpqeyXP+gpQJy/Z4+F0aGxSE4MqwjyXvW/TzgkLAx2AGHwQ==", "dev": true, "bin": { "tsc": "bin/tsc", "tsserver": "bin/tsserver" }, "engines": { - "node": ">=4.2.0" + "node": ">=14.17" } }, "node_modules/typical": { @@ -32937,6 +33032,7 @@ "name": "outline-manager", "version": "0.0.0-debug", "dependencies": { + "@outline/infrastructure": "file:../infrastructure", "@polymer/app-layout": "^3.1.0", "@polymer/app-localize-behavior": "^3.0.1", "@polymer/font-roboto": "^3.0.2", @@ -32992,6 +33088,7 @@ "gulp-posthtml": "^3.0.4", "gulp-replace": "^1.0.0", "html-webpack-plugin": "^5.5.3", + "jasmine": "^5.1.0", "karma": "^6.3.16", "karma-chrome-launcher": "^3.1.0", "karma-jasmine": "^5.1.0", @@ -35224,42 +35321,52 @@ } }, "@formatjs/ecma402-abstract": { - "version": "1.11.4", + "version": "1.18.2", + "resolved": "https://registry.npmjs.org/@formatjs/ecma402-abstract/-/ecma402-abstract-1.18.2.tgz", + "integrity": "sha512-+QoPW4csYALsQIl8GbN14igZzDbuwzcpWrku9nyMXlaqAlwRBgl5V+p0vWMGFqHOw37czNXaP/lEk4wbLgcmtA==", "dev": true, "requires": { - "@formatjs/intl-localematcher": "0.2.25", - "tslib": "^2.1.0" + "@formatjs/intl-localematcher": "0.5.4", + "tslib": "^2.4.0" } }, "@formatjs/fast-memoize": { - "version": "1.2.1", + "version": "2.2.0", + "resolved": "https://registry.npmjs.org/@formatjs/fast-memoize/-/fast-memoize-2.2.0.tgz", + "integrity": "sha512-hnk/nY8FyrL5YxwP9e4r9dqeM6cAbo8PeU9UjyXojZMNvVad2Z06FAVHyR3Ecw6fza+0GH7vdJgiKIVXTMbSBA==", "dev": true, "requires": { - "tslib": "^2.1.0" + "tslib": "^2.4.0" } }, "@formatjs/icu-messageformat-parser": { - "version": "2.1.0", + "version": "2.7.6", + "resolved": "https://registry.npmjs.org/@formatjs/icu-messageformat-parser/-/icu-messageformat-parser-2.7.6.tgz", + "integrity": "sha512-etVau26po9+eewJKYoiBKP6743I1br0/Ie00Pb/S/PtmYfmjTcOn2YCh2yNkSZI12h6Rg+BOgQYborXk46BvkA==", "dev": true, "requires": { - "@formatjs/ecma402-abstract": "1.11.4", - "@formatjs/icu-skeleton-parser": "1.3.6", - "tslib": "^2.1.0" + "@formatjs/ecma402-abstract": "1.18.2", + "@formatjs/icu-skeleton-parser": "1.8.0", + "tslib": "^2.4.0" } }, "@formatjs/icu-skeleton-parser": { - "version": "1.3.6", + "version": "1.8.0", + "resolved": "https://registry.npmjs.org/@formatjs/icu-skeleton-parser/-/icu-skeleton-parser-1.8.0.tgz", + "integrity": "sha512-QWLAYvM0n8hv7Nq5BEs4LKIjevpVpbGLAJgOaYzg9wABEoX1j0JO1q2/jVkO6CVlq0dbsxZCngS5aXbysYueqA==", "dev": true, "requires": { - "@formatjs/ecma402-abstract": "1.11.4", - "tslib": "^2.1.0" + "@formatjs/ecma402-abstract": "1.18.2", + "tslib": "^2.4.0" } }, "@formatjs/intl-localematcher": { - "version": "0.2.25", + "version": "0.5.4", + "resolved": "https://registry.npmjs.org/@formatjs/intl-localematcher/-/intl-localematcher-0.5.4.tgz", + "integrity": "sha512-zTwEpWOzZ2CiKcB93BLngUX59hQkuZjT2+SAQEscSm52peDW/getsawMcWF1rGRpMCX6D7nSJA3CzJ8gn13N/g==", "dev": true, "requires": { - "tslib": "^2.1.0" + "tslib": "^2.4.0" } }, "@formatjs/intl-unified-numberformat": { @@ -37511,6 +37618,19 @@ } } }, + "@outline/infrastructure": { + "version": "file:infrastructure", + "requires": { + "@types/jasmine": "^5.1.4", + "@types/node-forge": "^1.3.11", + "https-browserify": "^1.0.0", + "intl-messageformat": "^10.5.11", + "jasmine": "^5.1.0", + "node-forge": "^1.3.1", + "stream-http": "^3.2.0", + "typescript": "^5.4.5" + } + }, "@pkgjs/parseargs": { "version": "0.11.0", "resolved": "https://registry.npmjs.org/@pkgjs/parseargs/-/parseargs-0.11.0.tgz", @@ -38568,9 +38688,9 @@ } }, "@types/jasmine": { - "version": "4.3.6", - "resolved": "https://registry.npmjs.org/@types/jasmine/-/jasmine-4.3.6.tgz", - "integrity": "sha512-3N0FpQTeiWjm+Oo1WUYWguUS7E6JLceiGTriFrG8k5PU7zRLJCzLcWURU3wjMbZGS//a2/LgjsnO3QxIlwxt9g==", + "version": "5.1.4", + "resolved": "https://registry.npmjs.org/@types/jasmine/-/jasmine-5.1.4.tgz", + "integrity": "sha512-px7OMFO/ncXxixDe1zR13V1iycqWae0MxTaw62RpFlksUi5QuNWgQJFkTQjIOvrmutJbI7Fp2Y2N1F6D2R4G6w==", "dev": true }, "@types/json-schema": { @@ -41398,6 +41518,12 @@ "version": "3.3.0", "dev": true }, + "builtin-status-codes": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/builtin-status-codes/-/builtin-status-codes-3.0.0.tgz", + "integrity": "sha512-HpGFw18DgFWlncDfjTa2rcQ4W88O1mC8e8yZ2AvQY5KDaktSTwo+KRf6nHK6FRI5FyRyb/5T6+TSxfP7QyGsmQ==", + "dev": true + }, "builtins": { "version": "1.0.3", "dev": true @@ -42131,12 +42257,6 @@ "resolved": "https://registry.npmjs.org/minipass/-/minipass-7.0.4.tgz", "integrity": "sha512-jYofLM5Dam9279rdkWzqHozUo4ybjdZmCsDHePy5V/PbBcVMiSZR97gmAy45aqi8CK1lG2ECd356FU86avfwUQ==", "dev": true - }, - "typescript": { - "version": "5.4.5", - "resolved": "https://registry.npmjs.org/typescript/-/typescript-5.4.5.tgz", - "integrity": "sha512-vcI4UpRgg81oIRUFwR0WSIHKt11nJ7SAVlYNIu+QpqeyXP+gpQJy/Z4+F0aGxSE4MqwjyXvW/TzgkLAx2AGHwQ==", - "dev": true } } }, @@ -47743,6 +47863,12 @@ "sshpk": "^1.7.0" } }, + "https-browserify": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/https-browserify/-/https-browserify-1.0.0.tgz", + "integrity": "sha512-J+FkSdyD+0mA0N+81tMotaRMfSL9SGi+xpD3T6YApKsc3bGSXJlfXri3VyFOeYkfLRQisDk1W+jIFFKBeUBbBg==", + "dev": true + }, "https-proxy-agent": { "version": "5.0.1", "requires": { @@ -48137,13 +48263,15 @@ "integrity": "sha512-OEUYNA7D06agqPOYhbTkl0T8HA3QKSuwWh1HiClEnpd9vw7N+3XsQt5iZ0GUEchp5CW1fQk/tary+NsbF3yQ1Q==" }, "intl-messageformat": { - "version": "9.13.0", + "version": "10.5.11", + "resolved": "https://registry.npmjs.org/intl-messageformat/-/intl-messageformat-10.5.11.tgz", + "integrity": "sha512-eYq5fkFBVxc7GIFDzpFQkDOZgNayNTQn4Oufe8jw6YY6OHVw70/4pA3FyCsQ0Gb2DnvEJEMmN2tOaXUGByM+kg==", "dev": true, "requires": { - "@formatjs/ecma402-abstract": "1.11.4", - "@formatjs/fast-memoize": "1.2.1", - "@formatjs/icu-messageformat-parser": "2.1.0", - "tslib": "^2.1.0" + "@formatjs/ecma402-abstract": "1.18.2", + "@formatjs/fast-memoize": "2.2.0", + "@formatjs/icu-messageformat-parser": "2.7.6", + "tslib": "^2.4.0" } }, "intl-messageformat-parser": { @@ -49547,14 +49675,34 @@ } }, "karma-webpack": { - "version": "5.0.0", + "version": "5.0.1", + "resolved": "https://registry.npmjs.org/karma-webpack/-/karma-webpack-5.0.1.tgz", + "integrity": "sha512-oo38O+P3W2mSPCSUrQdySSPv1LvPpXP+f+bBimNomS5sW+1V4SuhCuW8TfJzV+rDv921w2fDSDw0xJbPe6U+kQ==", "dev": true, "requires": { "glob": "^7.1.3", - "minimatch": "^3.0.4", + "minimatch": "^9.0.3", "webpack-merge": "^4.1.5" }, "dependencies": { + "brace-expansion": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/brace-expansion/-/brace-expansion-2.0.1.tgz", + "integrity": "sha512-XnAIvQ8eM+kC6aULx6wuQiwVsnzsi9d3WxzV3FpWTGA19F621kwdbsAcFKXgKUHZWsy+mY6iL1sHTxWEFCytDA==", + "dev": true, + "requires": { + "balanced-match": "^1.0.0" + } + }, + "minimatch": { + "version": "9.0.4", + "resolved": "https://registry.npmjs.org/minimatch/-/minimatch-9.0.4.tgz", + "integrity": "sha512-KqWh+VchfxcMNRAJjj2tnsSJdNbHsVgnkBhTNrW7AjVo6OvLtxw8zfT9oLw1JSohlFzJ8jCoTgaoXvJ+kHt6fw==", + "dev": true, + "requires": { + "brace-expansion": "^2.0.1" + } + }, "webpack-merge": { "version": "4.2.2", "dev": true, @@ -52120,6 +52268,7 @@ "@material/mwc-textfield": "^0.25.3", "@open-wc/testing": "^3.2.0", "@open-wc/testing-karma": "^4.0.9", + "@outline/infrastructure": "file:../infrastructure", "@polymer/app-layout": "^3.1.0", "@polymer/app-localize-behavior": "^3.0.1", "@polymer/app-route": "^3.0.2", @@ -52185,7 +52334,6 @@ "html-webpack-plugin": "^5.1.0", "husky": "^1.3.1", "i18n-strings-files": "^2.0.0", - "intl-messageformat": "^9.12.0", "ios-deploy": "^1.11.4", "istanbul": "^0.4.5", "karma": "^6.4.2", @@ -52222,6 +52370,12 @@ "xmlbuilder2": "^3.1.1" }, "dependencies": { + "@types/jasmine": { + "version": "4.6.4", + "resolved": "https://registry.npmjs.org/@types/jasmine/-/jasmine-4.6.4.tgz", + "integrity": "sha512-qCw5sVW+ylTnrEhe5kfX4l6MgU9REXIVDa/lWEcvTOUmd+LqDYwyjovDq+Zk9blElaEHOj1URDQ/djEBVRf+pw==", + "dev": true + }, "electron-builder": { "version": "23.6.0", "resolved": "https://registry.npmjs.org/electron-builder/-/electron-builder-23.6.0.tgz", @@ -52275,12 +52429,19 @@ "fetch-blob": "^3.1.4", "formdata-polyfill": "^4.0.10" } + }, + "typescript": { + "version": "4.9.5", + "resolved": "https://registry.npmjs.org/typescript/-/typescript-4.9.5.tgz", + "integrity": "sha512-1FXk9E2Hm+QzZQ7z+McJiHL4NW1F2EzMu9Nq9i3zAaGqibafqYwCVU6WyWAuyQRRzOlxou8xZSyXLEN8oKj24g==", + "dev": true } } }, "outline-manager": { "version": "file:server_manager", "requires": { + "@outline/infrastructure": "file:../infrastructure", "@polymer/app-layout": "^3.1.0", "@polymer/app-localize-behavior": "^3.0.1", "@polymer/font-roboto": "^3.0.2", @@ -52329,6 +52490,7 @@ "gulp-replace": "^1.0.0", "html-webpack-plugin": "^5.5.3", "intl-messageformat": "^7.8.4", + "jasmine": "^5.1.0", "jsonic": "^0.3.1", "karma": "^6.3.16", "karma-chrome-launcher": "^3.1.0", @@ -56436,6 +56598,31 @@ "integrity": "sha512-b/qaq/GlBK5xaq1yrK9/zFcyRSTNxmcZwFLGSTG0mXgZl/4Z6GgiyYOXOvY7N3eEvFRAG1bkDRz5EPGSvPYQlw==", "dev": true }, + "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, + "requires": { + "builtin-status-codes": "^3.0.0", + "inherits": "^2.0.4", + "readable-stream": "^3.6.0", + "xtend": "^4.0.2" + }, + "dependencies": { + "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, + "requires": { + "inherits": "^2.0.3", + "string_decoder": "^1.1.1", + "util-deprecate": "^1.0.1" + } + } + } + }, "stream-shift": { "version": "1.0.1", "dev": true @@ -57638,9 +57825,9 @@ } }, "typescript": { - "version": "4.9.5", - "resolved": "https://registry.npmjs.org/typescript/-/typescript-4.9.5.tgz", - "integrity": "sha512-1FXk9E2Hm+QzZQ7z+McJiHL4NW1F2EzMu9Nq9i3zAaGqibafqYwCVU6WyWAuyQRRzOlxou8xZSyXLEN8oKj24g==", + "version": "5.4.5", + "resolved": "https://registry.npmjs.org/typescript/-/typescript-5.4.5.tgz", + "integrity": "sha512-vcI4UpRgg81oIRUFwR0WSIHKt11nJ7SAVlYNIu+QpqeyXP+gpQJy/Z4+F0aGxSE4MqwjyXvW/TzgkLAx2AGHwQ==", "dev": true }, "typical": { diff --git a/package.json b/package.json index 6b862b4937..25c22f8733 100644 --- a/package.json +++ b/package.json @@ -17,6 +17,7 @@ }, "private": true, "workspaces": [ + "infrastructure", "server_manager", "client" ], @@ -26,7 +27,6 @@ "@typescript-eslint/parser": "^7.7.0", "electron-builder": "^24.13.3", "eslint-plugin-import": "^2.29.1", - "jasmine": "^5.1.0", "node-fetch": "^2.6.7" }, "main": "build/electron/electron/index.js" diff --git a/server_manager/cloud/digitalocean_api.ts b/server_manager/cloud/digitalocean_api.ts index c67c8cbcae..b8c5b9cc7f 100644 --- a/server_manager/cloud/digitalocean_api.ts +++ b/server_manager/cloud/digitalocean_api.ts @@ -12,7 +12,7 @@ // See the License for the specific language governing permissions and // limitations under the License. -import * as errors from '../infrastructure/custom_error'; +import {CustomError} from '@outline/infrastructure/custom_error'; export interface DigitalOceanDropletSpecification { installCommand: string; @@ -65,7 +65,7 @@ export type RegionInfo = Readonly<{ // Marker class for errors due to network or authentication. // See below for more details on when this is raised. -export class XhrError extends errors.CustomError { +export class XhrError extends CustomError { constructor() { // No message because XMLHttpRequest.onerror provides no useful info. super(); diff --git a/server_manager/electron_app/fetch.ts b/server_manager/electron_app/fetch.ts index 0e56192949..63f1b805bb 100644 --- a/server_manager/electron_app/fetch.ts +++ b/server_manager/electron_app/fetch.ts @@ -18,7 +18,7 @@ import {TLSSocket} from 'tls'; import {urlToHttpOptions} from 'url'; -import type {HttpRequest, HttpResponse} from '../infrastructure/path_api'; +import type {HttpRequest, HttpResponse} from '@outline/infrastructure/path_api'; export const fetchWithPin = async (req: HttpRequest, fingerprint: string): Promise => { const response = await new Promise((resolve, reject) => { diff --git a/server_manager/electron_app/index.ts b/server_manager/electron_app/index.ts index 7e202d0294..59991f2bea 100644 --- a/server_manager/electron_app/index.ts +++ b/server_manager/electron_app/index.ts @@ -15,6 +15,7 @@ import * as path from 'path'; import {URL, URLSearchParams} from 'url'; +import type {HttpRequest, HttpResponse} from '@outline/infrastructure/path_api'; import * as Sentry from '@sentry/electron/main'; import * as dotenv from 'dotenv'; import * as electron from 'electron'; @@ -22,7 +23,6 @@ import {autoUpdater} from 'electron-updater'; import {fetchWithPin} from './fetch'; import * as menu from './menu'; -import type {HttpRequest, HttpResponse} from '../infrastructure/path_api'; // Injected by webpack during build declare const SENTRY_DSN: string | undefined; diff --git a/server_manager/electron_app/preload.ts b/server_manager/electron_app/preload.ts index 0d1ebc88ec..f67b49b8d9 100644 --- a/server_manager/electron_app/preload.ts +++ b/server_manager/electron_app/preload.ts @@ -14,12 +14,12 @@ import {contextBridge, ipcRenderer} from 'electron'; import '@sentry/electron/preload'; +import type {HttpRequest, HttpResponse} from '@outline/infrastructure/path_api'; import {Breadcrumb} from '@sentry/electron'; import * as digitalocean_oauth from './digitalocean_oauth'; import * as gcp_oauth from './gcp_oauth'; import {redactManagerUrl} from './util'; -import {HttpRequest, HttpResponse} from '../infrastructure/path_api'; // This file is run in the renderer process *before* nodeIntegration is disabled. // diff --git a/server_manager/electron_app/tsconfig.json b/server_manager/electron_app/tsconfig.json index b4dcbff75f..b6daebf77b 100644 --- a/server_manager/electron_app/tsconfig.json +++ b/server_manager/electron_app/tsconfig.json @@ -1,13 +1,4 @@ { - "compilerOptions": { - "target": "es2021", - "removeComments": false, - "noImplicitAny": true, - "module": "commonjs", - "rootDir": "..", - "lib": ["dom", "es2021"] - }, - "include": ["*.ts", "../infrastructure/path_api.ts", "../types/*.d.ts"], - "exclude": ["node_modules"], + "extends": "../tsconfig.json", "compileOnSave": true } diff --git a/server_manager/infrastructure/custom_error.ts b/server_manager/infrastructure/custom_error.ts deleted file mode 100644 index 1250836a62..0000000000 --- a/server_manager/infrastructure/custom_error.ts +++ /dev/null @@ -1,23 +0,0 @@ -// Copyright 2018 The Outline Authors -// -// Licensed under the Apache License, Version 2.0 (the "License"); -// you may not use this file except in compliance with the License. -// You may obtain a copy of the License at -// -// http://www.apache.org/licenses/LICENSE-2.0 -// -// Unless required by applicable law or agreed to in writing, software -// distributed under the License is distributed on an "AS IS" BASIS, -// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -// See the License for the specific language governing permissions and -// limitations under the License. - -export class CustomError extends Error { - constructor(message?: string) { - // ref: - // https://www.typescriptlang.org/docs/handbook/release-notes/typescript-2-2.html#support-for-newtarget - super(message); // 'Error' breaks prototype chain here - Object.setPrototypeOf(this, new.target.prototype); // restore prototype chain - this.name = new.target.name; - } -} diff --git a/server_manager/model/server.ts b/server_manager/model/server.ts index 6f23ca929f..1495f30c4c 100644 --- a/server_manager/model/server.ts +++ b/server_manager/model/server.ts @@ -12,8 +12,9 @@ // See the License for the specific language governing permissions and // limitations under the License. +import {CustomError} from '@outline/infrastructure/custom_error'; + import {CloudLocation} from './location'; -import {CustomError} from '../infrastructure/custom_error'; export interface Server { // Gets a globally unique identifier for this Server. THIS MUST NOT make a network request, as diff --git a/server_manager/package.json b/server_manager/package.json index 6f178365bf..78542741e9 100644 --- a/server_manager/package.json +++ b/server_manager/package.json @@ -17,6 +17,7 @@ } }, "dependencies": { + "@outline/infrastructure": "file:../infrastructure", "@polymer/app-layout": "^3.1.0", "@polymer/app-localize-behavior": "^3.0.1", "@polymer/font-roboto": "^3.0.2", @@ -87,6 +88,7 @@ "gulp-posthtml": "^3.0.4", "gulp-replace": "^1.0.0", "html-webpack-plugin": "^5.5.3", + "jasmine": "^5.1.0", "karma": "^6.3.16", "karma-chrome-launcher": "^3.1.0", "karma-jasmine": "^5.1.0", diff --git a/server_manager/test.action.sh b/server_manager/test.action.sh index cf32b49251..f0d5fdc43e 100755 --- a/server_manager/test.action.sh +++ b/server_manager/test.action.sh @@ -23,7 +23,7 @@ npm run action server_manager/web_app/build_install_script # Use commonjs modules, jasmine runs in node. tsc -p "${ROOT_DIR}/server_manager" --outDir "${TEST_DIR}" --module commonjs -jasmine --config="${ROOT_DIR}/jasmine.json" +jasmine "output/build/js/**/*.spec.js" "!output/build/js/server_manager/web_app/**/*" npm run action server_manager/web_app/test diff --git a/server_manager/tsconfig.json b/server_manager/tsconfig.json index 6f84d54eac..fec117b1f0 100644 --- a/server_manager/tsconfig.json +++ b/server_manager/tsconfig.json @@ -1,16 +1,18 @@ { "compilerOptions": { - "target": "es2018", "removeComments": false, "noImplicitAny": true, "noImplicitThis": true, + "module": "commonjs", "moduleResolution": "Node", "sourceMap": true, "experimentalDecorators": true, "allowJs": true, "resolveJsonModule": true, "noUnusedLocals": true, - "skipLibCheck": true + "skipLibCheck": true, + "target": "es2021", + "lib": ["dom", "es2022"] }, "rootDir": ".", "include": ["**/*.ts"], diff --git a/server_manager/types/preload.d.ts b/server_manager/types/preload.d.ts index 339e24b091..382c6050c2 100644 --- a/server_manager/types/preload.d.ts +++ b/server_manager/types/preload.d.ts @@ -17,8 +17,8 @@ type SentryBreadcrumb = import('@sentry/electron').Breadcrumb; declare function redactSentryBreadcrumbUrl(breadcrumb: SentryBreadcrumb): SentryBreadcrumb; -type HttpRequest = import('../infrastructure/path_api').HttpRequest; -type HttpResponse = import('../infrastructure/path_api').HttpResponse; +type HttpRequest = import('@outline/infrastructure/path_api').HttpRequest; +type HttpResponse = import('@outline/infrastructure/path_api').HttpResponse; declare function fetchWithPin(request: HttpRequest, fingerprint: string): Promise; declare function openImage(basename: string): void; diff --git a/server_manager/web_app/app.ts b/server_manager/web_app/app.ts index 84704e8029..8ee961ee8f 100644 --- a/server_manager/web_app/app.ts +++ b/server_manager/web_app/app.ts @@ -12,6 +12,9 @@ // See the License for the specific language governing permissions and // limitations under the License. +import {CustomError} from '@outline/infrastructure/custom_error'; +import * as path_api from '@outline/infrastructure/path_api'; +import {sleep} from '@outline/infrastructure/sleep'; import * as Sentry from '@sentry/electron/renderer'; import * as semver from 'semver'; @@ -23,9 +26,6 @@ import type {FeedbackDetail} from './ui_components/outline-feedback-dialog'; import type {DisplayAccessKey, ServerView} from './ui_components/outline-server-view'; import * as digitalocean_api from '../cloud/digitalocean_api'; import {HttpError} from '../cloud/gcp_api'; -import {CustomError} from '../infrastructure/custom_error'; -import * as path_api from '../infrastructure/path_api'; -import {sleep} from '../infrastructure/sleep'; import * as accounts from '../model/accounts'; import * as digitalocean from '../model/digitalocean'; import * as gcp from '../model/gcp'; diff --git a/server_manager/web_app/cloud_accounts.spec.ts b/server_manager/web_app/cloud_accounts.spec.ts index e57416164d..5c31ac108f 100644 --- a/server_manager/web_app/cloud_accounts.spec.ts +++ b/server_manager/web_app/cloud_accounts.spec.ts @@ -12,8 +12,9 @@ // See the License for the specific language governing permissions and // limitations under the License. +import {InMemoryStorage} from '@outline/infrastructure/memory_storage'; + import {CloudAccounts} from './cloud_accounts'; -import {InMemoryStorage} from '../infrastructure/memory_storage'; describe('CloudAccounts', () => { diff --git a/server_manager/web_app/digitalocean_account.ts b/server_manager/web_app/digitalocean_account.ts index 90e2517b35..2aeccd0700 100644 --- a/server_manager/web_app/digitalocean_account.ts +++ b/server_manager/web_app/digitalocean_account.ts @@ -12,10 +12,11 @@ // See the License for the specific language governing permissions and // limitations under the License. +import * as crypto from '@outline/infrastructure/crypto'; + import {DigitalOceanServer} from './digitalocean_server'; import {getShellExportCommands, ShadowboxSettings} from './server_install'; import {DigitalOceanSession, DropletInfo, RestApiSession} from '../cloud/digitalocean_api'; -import * as crypto from '../infrastructure/crypto'; import * as do_install_script from '../install_scripts/do_install_script'; import * as digitalocean from '../model/digitalocean'; import * as server from '../model/server'; diff --git a/server_manager/web_app/digitalocean_server.ts b/server_manager/web_app/digitalocean_server.ts index a5f6e335fc..5d25dad2c2 100644 --- a/server_manager/web_app/digitalocean_server.ts +++ b/server_manager/web_app/digitalocean_server.ts @@ -12,12 +12,13 @@ // See the License for the specific language governing permissions and // limitations under the License. +import {hexToString} from '@outline/infrastructure/hex_encoding'; +import {sleep} from '@outline/infrastructure/sleep'; +import {ValueStream} from '@outline/infrastructure/value_stream'; + import {makePathApiClient} from './fetcher'; import {ShadowboxServer} from './shadowbox_server'; import {DigitalOceanSession, DropletInfo} from '../cloud/digitalocean_api'; -import {hexToString} from '../infrastructure/hex_encoding'; -import {sleep} from '../infrastructure/sleep'; -import {ValueStream} from '../infrastructure/value_stream'; import {Region} from '../model/digitalocean'; import * as server from '../model/server'; diff --git a/server_manager/web_app/fetcher.ts b/server_manager/web_app/fetcher.ts index 72bd01ad24..191696f801 100644 --- a/server_manager/web_app/fetcher.ts +++ b/server_manager/web_app/fetcher.ts @@ -12,7 +12,7 @@ // See the License for the specific language governing permissions and // limitations under the License. -import {Fetcher, PathApiClient} from '../infrastructure/path_api'; +import {Fetcher, PathApiClient} from '@outline/infrastructure/path_api'; async function fetchWrapper(request: HttpRequest): Promise { const response = await fetch(request.url, request); diff --git a/server_manager/web_app/gcp_account.ts b/server_manager/web_app/gcp_account.ts index 6be00249c1..b48b73a465 100644 --- a/server_manager/web_app/gcp_account.ts +++ b/server_manager/web_app/gcp_account.ts @@ -12,10 +12,11 @@ // See the License for the specific language governing permissions and // limitations under the License. +import {sleep} from '@outline/infrastructure/sleep'; + import {GcpServer} from './gcp_server'; import * as server_install from './server_install'; import * as gcp_api from '../cloud/gcp_api'; -import {sleep} from '../infrastructure/sleep'; import {SCRIPT} from '../install_scripts/gcp_install_script'; import * as gcp from '../model/gcp'; import {BillingAccount, Project} from '../model/gcp'; diff --git a/server_manager/web_app/gcp_server.ts b/server_manager/web_app/gcp_server.ts index bd05bb45f2..d790370e31 100644 --- a/server_manager/web_app/gcp_server.ts +++ b/server_manager/web_app/gcp_server.ts @@ -12,11 +12,12 @@ // See the License for the specific language governing permissions and // limitations under the License. +import {sleep} from '@outline/infrastructure/sleep'; +import {ValueStream} from '@outline/infrastructure/value_stream'; + import {makePathApiClient} from './fetcher'; import {ShadowboxServer} from './shadowbox_server'; import * as gcp_api from '../cloud/gcp_api'; -import {sleep} from '../infrastructure/sleep'; -import {ValueStream} from '../infrastructure/value_stream'; import {Zone} from '../model/gcp'; import * as server from '../model/server'; import {DataAmount, ManagedServerHost, MonetaryCost} from '../model/server'; diff --git a/server_manager/web_app/main.ts b/server_manager/web_app/main.ts index 6887975119..f61a3b8c46 100644 --- a/server_manager/web_app/main.ts +++ b/server_manager/web_app/main.ts @@ -15,12 +15,13 @@ import './ui_components/app-root'; +import * as i18n from '@outline/infrastructure/i18n'; + import {App} from './app'; import {CloudAccounts} from './cloud_accounts'; import {ManualServerRepository} from './manual_server'; import {AppRoot} from './ui_components/app-root'; import {LanguageDef} from './ui_components/outline-language-picker'; -import * as i18n from '../infrastructure/i18n'; const SUPPORTED_LANGUAGES: {[key: string]: LanguageDef} = { af: {id: 'af', name: 'Afrikaans', dir: 'ltr'}, diff --git a/server_manager/web_app/manual_server.ts b/server_manager/web_app/manual_server.ts index 6aa84d1439..8c0aff1d16 100644 --- a/server_manager/web_app/manual_server.ts +++ b/server_manager/web_app/manual_server.ts @@ -12,9 +12,10 @@ // See the License for the specific language governing permissions and // limitations under the License. +import {hexToString} from '@outline/infrastructure/hex_encoding'; + import {makePathApiClient} from './fetcher'; import {ShadowboxServer} from './shadowbox_server'; -import {hexToString} from '../infrastructure/hex_encoding'; import * as server from '../model/server'; diff --git a/server_manager/web_app/shadowbox_server.ts b/server_manager/web_app/shadowbox_server.ts index 0b1cbdda6e..ef52c7504a 100644 --- a/server_manager/web_app/shadowbox_server.ts +++ b/server_manager/web_app/shadowbox_server.ts @@ -12,9 +12,9 @@ // See the License for the specific language governing permissions and // limitations under the License. +import {PathApiClient} from '@outline/infrastructure/path_api'; import * as semver from 'semver'; -import {PathApiClient} from '../infrastructure/path_api'; import * as server from '../model/server'; interface AccessKeyJson { diff --git a/server_manager/web_app/survey.spec.ts b/server_manager/web_app/survey.spec.ts index 2b42d0d76f..00816a36b5 100644 --- a/server_manager/web_app/survey.spec.ts +++ b/server_manager/web_app/survey.spec.ts @@ -12,8 +12,9 @@ // See the License for the specific language governing permissions and // limitations under the License. +import {InMemoryStorage} from '@outline/infrastructure/memory_storage'; + import {OutlineSurveys} from './survey'; -import {InMemoryStorage} from '../infrastructure/memory_storage'; describe('Surveys', () => { diff --git a/server_manager/web_app/survey.ts b/server_manager/web_app/survey.ts index 3afbf736f1..cb7679b815 100644 --- a/server_manager/web_app/survey.ts +++ b/server_manager/web_app/survey.ts @@ -12,7 +12,8 @@ // See the License for the specific language governing permissions and // limitations under the License. -import {sleep} from '../infrastructure/sleep'; +import {sleep} from '@outline/infrastructure/sleep'; + import {Surveys} from '../model/survey'; export const DEFAULT_PROMPT_IMPRESSION_DELAY_MS = 3000; diff --git a/src/electron/connectivity.ts b/src/electron/connectivity.ts index 1150c165e0..d3a242d372 100644 --- a/src/electron/connectivity.ts +++ b/src/electron/connectivity.ts @@ -14,7 +14,7 @@ import * as dns from 'dns'; -import {timeoutPromise} from '../../client/infrastructure/timeout_promise'; +import {timeoutPromise} from '@outline/infrastructure/timeout_promise'; import * as errors from '../../client/src/www/model/errors'; const DNS_LOOKUP_TIMEOUT_MS = 10000;