|
@@ -1,11 +1,38 @@
|
|
-'use strict'
|
|
|
|
|
|
+import type { Uppy, BasePlugin } from '@uppy/core'
|
|
|
|
+import type { Body, Meta, UppyFile } from '@uppy/utils/lib/UppyFile'
|
|
|
|
+import RequestClient, {
|
|
|
|
+ authErrorStatusCode,
|
|
|
|
+ type RequestOptions,
|
|
|
|
+} from './RequestClient.ts'
|
|
|
|
+import * as tokenStorage from './tokenStorage.ts'
|
|
|
|
+
|
|
|
|
+// TODO: remove deprecated options in next major release
|
|
|
|
+export type Opts = {
|
|
|
|
+ /** @deprecated */
|
|
|
|
+ serverUrl?: string
|
|
|
|
+ /** @deprecated */
|
|
|
|
+ serverPattern?: string
|
|
|
|
+ companionUrl: string
|
|
|
|
+ companionAllowedHosts?: string | RegExp | Array<string | RegExp>
|
|
|
|
+ storage?: typeof tokenStorage
|
|
|
|
+ pluginId: string
|
|
|
|
+ name?: string
|
|
|
|
+ supportsRefreshToken?: boolean
|
|
|
|
+ provider: string
|
|
|
|
+}
|
|
|
|
|
|
-import RequestClient, { authErrorStatusCode } from './RequestClient.js'
|
|
|
|
-import * as tokenStorage from './tokenStorage.js'
|
|
|
|
|
|
+interface ProviderPlugin<M extends Meta, B extends Body>
|
|
|
|
+ extends BasePlugin<Opts, M, B> {
|
|
|
|
+ files: UppyFile<M, B>[]
|
|
|
|
|
|
|
|
+ storage: typeof tokenStorage
|
|
|
|
+}
|
|
|
|
|
|
-const getName = (id) => {
|
|
|
|
- return id.split('-').map((s) => s.charAt(0).toUpperCase() + s.slice(1)).join(' ')
|
|
|
|
|
|
+const getName = (id: string) => {
|
|
|
|
+ return id
|
|
|
|
+ .split('-')
|
|
|
|
+ .map((s) => s.charAt(0).toUpperCase() + s.slice(1))
|
|
|
|
+ .join(' ')
|
|
}
|
|
}
|
|
|
|
|
|
function getOrigin() {
|
|
function getOrigin() {
|
|
@@ -13,25 +40,51 @@ function getOrigin() {
|
|
return location.origin
|
|
return location.origin
|
|
}
|
|
}
|
|
|
|
|
|
-function getRegex(value) {
|
|
|
|
|
|
+function getRegex(value?: string | RegExp) {
|
|
if (typeof value === 'string') {
|
|
if (typeof value === 'string') {
|
|
return new RegExp(`^${value}$`)
|
|
return new RegExp(`^${value}$`)
|
|
- } if (value instanceof RegExp) {
|
|
|
|
|
|
+ }
|
|
|
|
+ if (value instanceof RegExp) {
|
|
return value
|
|
return value
|
|
}
|
|
}
|
|
return undefined
|
|
return undefined
|
|
}
|
|
}
|
|
|
|
|
|
-function isOriginAllowed(origin, allowedOrigin) {
|
|
|
|
- const patterns = Array.isArray(allowedOrigin) ? allowedOrigin.map(getRegex) : [getRegex(allowedOrigin)]
|
|
|
|
- return patterns
|
|
|
|
- .some((pattern) => pattern?.test(origin) || pattern?.test(`${origin}/`)) // allowing for trailing '/'
|
|
|
|
|
|
+function isOriginAllowed(
|
|
|
|
+ origin: string,
|
|
|
|
+ allowedOrigin: string | RegExp | Array<string | RegExp> | undefined,
|
|
|
|
+) {
|
|
|
|
+ const patterns = Array.isArray(allowedOrigin)
|
|
|
|
+ ? allowedOrigin.map(getRegex)
|
|
|
|
+ : [getRegex(allowedOrigin)]
|
|
|
|
+ return patterns.some(
|
|
|
|
+ (pattern) => pattern?.test(origin) || pattern?.test(`${origin}/`),
|
|
|
|
+ ) // allowing for trailing '/'
|
|
}
|
|
}
|
|
|
|
|
|
-export default class Provider extends RequestClient {
|
|
|
|
- #refreshingTokenPromise
|
|
|
|
|
|
+export default class Provider<
|
|
|
|
+ M extends Meta,
|
|
|
|
+ B extends Body,
|
|
|
|
+> extends RequestClient<M, B> {
|
|
|
|
+ #refreshingTokenPromise: Promise<void> | undefined
|
|
|
|
+
|
|
|
|
+ provider: string
|
|
|
|
+
|
|
|
|
+ id: string
|
|
|
|
+
|
|
|
|
+ name: string
|
|
|
|
+
|
|
|
|
+ pluginId: string
|
|
|
|
+
|
|
|
|
+ tokenKey: string
|
|
|
|
|
|
- constructor(uppy, opts) {
|
|
|
|
|
|
+ companionKeysParams?: Record<string, string>
|
|
|
|
+
|
|
|
|
+ preAuthToken: string | null
|
|
|
|
+
|
|
|
|
+ supportsRefreshToken: boolean
|
|
|
|
+
|
|
|
|
+ constructor(uppy: Uppy<M, B>, opts: Opts) {
|
|
super(uppy, opts)
|
|
super(uppy, opts)
|
|
this.provider = opts.provider
|
|
this.provider = opts.provider
|
|
this.id = this.provider
|
|
this.id = this.provider
|
|
@@ -43,9 +96,12 @@ export default class Provider extends RequestClient {
|
|
this.supportsRefreshToken = opts.supportsRefreshToken ?? true // todo false in next major
|
|
this.supportsRefreshToken = opts.supportsRefreshToken ?? true // todo false in next major
|
|
}
|
|
}
|
|
|
|
|
|
- async headers() {
|
|
|
|
- const [headers, token] = await Promise.all([super.headers(), this.#getAuthToken()])
|
|
|
|
- const authHeaders = {}
|
|
|
|
|
|
+ async headers(): Promise<Record<string, string>> {
|
|
|
|
+ const [headers, token] = await Promise.all([
|
|
|
|
+ super.headers(),
|
|
|
|
+ this.#getAuthToken(),
|
|
|
|
+ ])
|
|
|
|
+ const authHeaders: Record<string, string> = {}
|
|
if (token) {
|
|
if (token) {
|
|
authHeaders['uppy-auth-token'] = token
|
|
authHeaders['uppy-auth-token'] = token
|
|
}
|
|
}
|
|
@@ -58,48 +114,63 @@ export default class Provider extends RequestClient {
|
|
return { ...headers, ...authHeaders }
|
|
return { ...headers, ...authHeaders }
|
|
}
|
|
}
|
|
|
|
|
|
- onReceiveResponse(response) {
|
|
|
|
|
|
+ onReceiveResponse(response: Response): Response {
|
|
super.onReceiveResponse(response)
|
|
super.onReceiveResponse(response)
|
|
- const plugin = this.uppy.getPlugin(this.pluginId)
|
|
|
|
|
|
+ const plugin = this.#getPlugin()
|
|
const oldAuthenticated = plugin.getPluginState().authenticated
|
|
const oldAuthenticated = plugin.getPluginState().authenticated
|
|
- const authenticated = oldAuthenticated ? response.status !== authErrorStatusCode : response.status < 400
|
|
|
|
|
|
+ const authenticated = oldAuthenticated
|
|
|
|
+ ? response.status !== authErrorStatusCode
|
|
|
|
+ : response.status < 400
|
|
plugin.setPluginState({ authenticated })
|
|
plugin.setPluginState({ authenticated })
|
|
return response
|
|
return response
|
|
}
|
|
}
|
|
|
|
|
|
- async setAuthToken(token) {
|
|
|
|
- return this.uppy.getPlugin(this.pluginId).storage.setItem(this.tokenKey, token)
|
|
|
|
|
|
+ async setAuthToken(token: string): Promise<void> {
|
|
|
|
+ return this.#getPlugin().storage.setItem(this.tokenKey, token)
|
|
}
|
|
}
|
|
|
|
|
|
- async #getAuthToken() {
|
|
|
|
- return this.uppy.getPlugin(this.pluginId).storage.getItem(this.tokenKey)
|
|
|
|
|
|
+ async #getAuthToken(): Promise<string | null> {
|
|
|
|
+ return this.#getPlugin().storage.getItem(this.tokenKey)
|
|
}
|
|
}
|
|
|
|
|
|
- /** @protected */
|
|
|
|
- async removeAuthToken() {
|
|
|
|
- return this.uppy.getPlugin(this.pluginId).storage.removeItem(this.tokenKey)
|
|
|
|
|
|
+ protected async removeAuthToken(): Promise<void> {
|
|
|
|
+ return this.#getPlugin().storage.removeItem(this.tokenKey)
|
|
|
|
+ }
|
|
|
|
+
|
|
|
|
+ #getPlugin() {
|
|
|
|
+ const plugin = this.uppy.getPlugin(this.pluginId) as ProviderPlugin<M, B>
|
|
|
|
+ if (plugin == null) throw new Error('Plugin was nullish')
|
|
|
|
+ return plugin
|
|
}
|
|
}
|
|
|
|
|
|
/**
|
|
/**
|
|
* Ensure we have a preauth token if necessary. Attempts to fetch one if we don't,
|
|
* Ensure we have a preauth token if necessary. Attempts to fetch one if we don't,
|
|
* or rejects if loading one fails.
|
|
* or rejects if loading one fails.
|
|
*/
|
|
*/
|
|
- async ensurePreAuth() {
|
|
|
|
|
|
+ async ensurePreAuth(): Promise<void> {
|
|
if (this.companionKeysParams && !this.preAuthToken) {
|
|
if (this.companionKeysParams && !this.preAuthToken) {
|
|
await this.fetchPreAuthToken()
|
|
await this.fetchPreAuthToken()
|
|
|
|
|
|
if (!this.preAuthToken) {
|
|
if (!this.preAuthToken) {
|
|
- throw new Error('Could not load authentication data required for third-party login. Please try again later.')
|
|
|
|
|
|
+ throw new Error(
|
|
|
|
+ 'Could not load authentication data required for third-party login. Please try again later.',
|
|
|
|
+ )
|
|
}
|
|
}
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
|
|
- // eslint-disable-next-line class-methods-use-this
|
|
|
|
- authQuery() {
|
|
|
|
|
|
+ // eslint-disable-next-line class-methods-use-this, @typescript-eslint/no-unused-vars
|
|
|
|
+ authQuery(data: unknown): Record<string, string> {
|
|
return {}
|
|
return {}
|
|
}
|
|
}
|
|
|
|
|
|
- authUrl({ authFormData, query } = {}) {
|
|
|
|
|
|
+ authUrl({
|
|
|
|
+ authFormData,
|
|
|
|
+ query,
|
|
|
|
+ }: {
|
|
|
|
+ authFormData: unknown
|
|
|
|
+ query: Record<string, string>
|
|
|
|
+ }): string {
|
|
const params = new URLSearchParams({
|
|
const params = new URLSearchParams({
|
|
...query,
|
|
...query,
|
|
state: btoa(JSON.stringify({ origin: getOrigin() })),
|
|
state: btoa(JSON.stringify({ origin: getOrigin() })),
|
|
@@ -113,14 +184,33 @@ export default class Provider extends RequestClient {
|
|
return `${this.hostname}/${this.id}/connect?${params}`
|
|
return `${this.hostname}/${this.id}/connect?${params}`
|
|
}
|
|
}
|
|
|
|
|
|
- /** @protected */
|
|
|
|
- async loginSimpleAuth({ uppyVersions, authFormData, signal }) {
|
|
|
|
- const response = await this.post(`${this.id}/simple-auth`, { form: authFormData }, { qs: { uppyVersions }, signal })
|
|
|
|
|
|
+ protected async loginSimpleAuth({
|
|
|
|
+ uppyVersions,
|
|
|
|
+ authFormData,
|
|
|
|
+ signal,
|
|
|
|
+ }: {
|
|
|
|
+ uppyVersions: string
|
|
|
|
+ authFormData: unknown
|
|
|
|
+ signal: AbortSignal
|
|
|
|
+ }): Promise<void> {
|
|
|
|
+ type Res = { uppyAuthToken: string }
|
|
|
|
+ const response = await this.post<Res>(
|
|
|
|
+ `${this.id}/simple-auth`,
|
|
|
|
+ { form: authFormData },
|
|
|
|
+ { qs: { uppyVersions }, signal },
|
|
|
|
+ )
|
|
this.setAuthToken(response.uppyAuthToken)
|
|
this.setAuthToken(response.uppyAuthToken)
|
|
}
|
|
}
|
|
|
|
|
|
- /** @protected */
|
|
|
|
- async loginOAuth({ uppyVersions, authFormData, signal }) {
|
|
|
|
|
|
+ protected async loginOAuth({
|
|
|
|
+ uppyVersions,
|
|
|
|
+ authFormData,
|
|
|
|
+ signal,
|
|
|
|
+ }: {
|
|
|
|
+ uppyVersions: string
|
|
|
|
+ authFormData: unknown
|
|
|
|
+ signal: AbortSignal
|
|
|
|
+ }): Promise<void> {
|
|
await this.ensurePreAuth()
|
|
await this.ensurePreAuth()
|
|
|
|
|
|
signal.throwIfAborted()
|
|
signal.throwIfAborted()
|
|
@@ -129,9 +219,9 @@ export default class Provider extends RequestClient {
|
|
const link = this.authUrl({ query: { uppyVersions }, authFormData })
|
|
const link = this.authUrl({ query: { uppyVersions }, authFormData })
|
|
const authWindow = window.open(link, '_blank')
|
|
const authWindow = window.open(link, '_blank')
|
|
|
|
|
|
- let cleanup
|
|
|
|
|
|
+ let cleanup: () => void
|
|
|
|
|
|
- const handleToken = (e) => {
|
|
|
|
|
|
+ const handleToken = (e: MessageEvent<any>) => {
|
|
if (e.source !== authWindow) {
|
|
if (e.source !== authWindow) {
|
|
let jsonData = ''
|
|
let jsonData = ''
|
|
try {
|
|
try {
|
|
@@ -143,13 +233,20 @@ export default class Provider extends RequestClient {
|
|
} catch (err) {
|
|
} catch (err) {
|
|
// in case JSON.stringify fails (ignored)
|
|
// in case JSON.stringify fails (ignored)
|
|
}
|
|
}
|
|
- this.uppy.log(`ignoring event from unknown source ${jsonData}`, 'warning')
|
|
|
|
|
|
+ this.uppy.log(
|
|
|
|
+ `ignoring event from unknown source ${jsonData}`,
|
|
|
|
+ 'warning',
|
|
|
|
+ )
|
|
return
|
|
return
|
|
}
|
|
}
|
|
|
|
|
|
- const { companionAllowedHosts } = this.uppy.getPlugin(this.pluginId).opts
|
|
|
|
|
|
+ const { companionAllowedHosts } = this.#getPlugin().opts
|
|
if (!isOriginAllowed(e.origin, companionAllowedHosts)) {
|
|
if (!isOriginAllowed(e.origin, companionAllowedHosts)) {
|
|
- reject(new Error(`rejecting event from ${e.origin} vs allowed pattern ${companionAllowedHosts}`))
|
|
|
|
|
|
+ reject(
|
|
|
|
+ new Error(
|
|
|
|
+ `rejecting event from ${e.origin} vs allowed pattern ${companionAllowedHosts}`,
|
|
|
|
+ ),
|
|
|
|
+ )
|
|
return
|
|
return
|
|
}
|
|
}
|
|
|
|
|
|
@@ -175,7 +272,7 @@ export default class Provider extends RequestClient {
|
|
}
|
|
}
|
|
|
|
|
|
cleanup = () => {
|
|
cleanup = () => {
|
|
- authWindow.close()
|
|
|
|
|
|
+ authWindow?.close()
|
|
window.removeEventListener('message', handleToken)
|
|
window.removeEventListener('message', handleToken)
|
|
signal.removeEventListener('abort', cleanup)
|
|
signal.removeEventListener('abort', cleanup)
|
|
}
|
|
}
|
|
@@ -185,20 +282,29 @@ export default class Provider extends RequestClient {
|
|
})
|
|
})
|
|
}
|
|
}
|
|
|
|
|
|
- async login({ uppyVersions, authFormData, signal }) {
|
|
|
|
|
|
+ async login({
|
|
|
|
+ uppyVersions,
|
|
|
|
+ authFormData,
|
|
|
|
+ signal,
|
|
|
|
+ }: {
|
|
|
|
+ uppyVersions: string
|
|
|
|
+ authFormData: unknown
|
|
|
|
+ signal: AbortSignal
|
|
|
|
+ }): Promise<void> {
|
|
return this.loginOAuth({ uppyVersions, authFormData, signal })
|
|
return this.loginOAuth({ uppyVersions, authFormData, signal })
|
|
}
|
|
}
|
|
|
|
|
|
- refreshTokenUrl() {
|
|
|
|
|
|
+ refreshTokenUrl(): string {
|
|
return `${this.hostname}/${this.id}/refresh-token`
|
|
return `${this.hostname}/${this.id}/refresh-token`
|
|
}
|
|
}
|
|
|
|
|
|
- fileUrl(id) {
|
|
|
|
|
|
+ fileUrl(id: string): string {
|
|
return `${this.hostname}/${this.id}/get/${id}`
|
|
return `${this.hostname}/${this.id}/get/${id}`
|
|
}
|
|
}
|
|
|
|
|
|
- /** @protected */
|
|
|
|
- async request(...args) {
|
|
|
|
|
|
+ protected async request<ResBody extends Record<string, unknown>>(
|
|
|
|
+ ...args: Parameters<RequestClient<M, B>['request']>
|
|
|
|
+ ): Promise<ResBody> {
|
|
await this.#refreshingTokenPromise
|
|
await this.#refreshingTokenPromise
|
|
|
|
|
|
try {
|
|
try {
|
|
@@ -208,7 +314,7 @@ export default class Provider extends RequestClient {
|
|
// While uploading, go to your google account settings,
|
|
// While uploading, go to your google account settings,
|
|
// "Third-party apps & services", then click "Companion" and "Remove access".
|
|
// "Third-party apps & services", then click "Companion" and "Remove access".
|
|
|
|
|
|
- return await super.request(...args)
|
|
|
|
|
|
+ return await super.request<ResBody>(...args)
|
|
} catch (err) {
|
|
} catch (err) {
|
|
if (!this.supportsRefreshToken) throw err
|
|
if (!this.supportsRefreshToken) throw err
|
|
// only handle auth errors (401 from provider), and only handle them if we have a (refresh) token
|
|
// only handle auth errors (401 from provider), and only handle them if we have a (refresh) token
|
|
@@ -220,8 +326,14 @@ export default class Provider extends RequestClient {
|
|
// Once a refresh token operation has started, we need all other request to wait for this operation (atomically)
|
|
// Once a refresh token operation has started, we need all other request to wait for this operation (atomically)
|
|
this.#refreshingTokenPromise = (async () => {
|
|
this.#refreshingTokenPromise = (async () => {
|
|
try {
|
|
try {
|
|
- this.uppy.log(`[CompanionClient] Refreshing expired auth token`, 'info')
|
|
|
|
- const response = await super.request({ path: this.refreshTokenUrl(), method: 'POST' })
|
|
|
|
|
|
+ this.uppy.log(
|
|
|
|
+ `[CompanionClient] Refreshing expired auth token`,
|
|
|
|
+ 'info',
|
|
|
|
+ )
|
|
|
|
+ const response = await super.request<{ uppyAuthToken: string }>({
|
|
|
|
+ path: this.refreshTokenUrl(),
|
|
|
|
+ method: 'POST',
|
|
|
|
+ })
|
|
await this.setAuthToken(response.uppyAuthToken)
|
|
await this.setAuthToken(response.uppyAuthToken)
|
|
} catch (refreshTokenErr) {
|
|
} catch (refreshTokenErr) {
|
|
if (refreshTokenErr.isAuthError) {
|
|
if (refreshTokenErr.isAuthError) {
|
|
@@ -242,30 +354,44 @@ export default class Provider extends RequestClient {
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
|
|
- async fetchPreAuthToken() {
|
|
|
|
|
|
+ async fetchPreAuthToken(): Promise<void> {
|
|
if (!this.companionKeysParams) {
|
|
if (!this.companionKeysParams) {
|
|
return
|
|
return
|
|
}
|
|
}
|
|
|
|
|
|
try {
|
|
try {
|
|
- const res = await this.post(`${this.id}/preauth/`, { params: this.companionKeysParams })
|
|
|
|
|
|
+ const res = await this.post<{ token: string }>(`${this.id}/preauth/`, {
|
|
|
|
+ params: this.companionKeysParams,
|
|
|
|
+ })
|
|
this.preAuthToken = res.token
|
|
this.preAuthToken = res.token
|
|
} catch (err) {
|
|
} catch (err) {
|
|
- this.uppy.log(`[CompanionClient] unable to fetch preAuthToken ${err}`, 'warning')
|
|
|
|
|
|
+ this.uppy.log(
|
|
|
|
+ `[CompanionClient] unable to fetch preAuthToken ${err}`,
|
|
|
|
+ 'warning',
|
|
|
|
+ )
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
|
|
- list(directory, options) {
|
|
|
|
- return this.get(`${this.id}/list/${directory || ''}`, options)
|
|
|
|
|
|
+ list<ResBody extends Record<string, unknown>>(
|
|
|
|
+ directory: string | undefined,
|
|
|
|
+ options: RequestOptions,
|
|
|
|
+ ): Promise<ResBody> {
|
|
|
|
+ return this.get<ResBody>(`${this.id}/list/${directory || ''}`, options)
|
|
}
|
|
}
|
|
|
|
|
|
- async logout(options) {
|
|
|
|
- const response = await this.get(`${this.id}/logout`, options)
|
|
|
|
|
|
+ async logout<ResBody extends Record<string, unknown>>(
|
|
|
|
+ options: RequestOptions,
|
|
|
|
+ ): Promise<ResBody> {
|
|
|
|
+ const response = await this.get<ResBody>(`${this.id}/logout`, options)
|
|
await this.removeAuthToken()
|
|
await this.removeAuthToken()
|
|
return response
|
|
return response
|
|
}
|
|
}
|
|
|
|
|
|
- static initPlugin(plugin, opts, defaultOpts) {
|
|
|
|
|
|
+ static initPlugin(
|
|
|
|
+ plugin: ProviderPlugin<any, any>, // any because static methods cannot use class generics
|
|
|
|
+ opts: Opts,
|
|
|
|
+ defaultOpts: Record<string, unknown>,
|
|
|
|
+ ): void {
|
|
/* eslint-disable no-param-reassign */
|
|
/* eslint-disable no-param-reassign */
|
|
plugin.type = 'acquirer'
|
|
plugin.type = 'acquirer'
|
|
plugin.files = []
|
|
plugin.files = []
|
|
@@ -274,19 +400,30 @@ export default class Provider extends RequestClient {
|
|
}
|
|
}
|
|
|
|
|
|
if (opts.serverUrl || opts.serverPattern) {
|
|
if (opts.serverUrl || opts.serverPattern) {
|
|
- throw new Error('`serverUrl` and `serverPattern` have been renamed to `companionUrl` and `companionAllowedHosts` respectively in the 0.30.5 release. Please consult the docs (for example, https://uppy.io/docs/instagram/ for the Instagram plugin) and use the updated options.`')
|
|
|
|
|
|
+ throw new Error(
|
|
|
|
+ '`serverUrl` and `serverPattern` have been renamed to `companionUrl` and `companionAllowedHosts` respectively in the 0.30.5 release. Please consult the docs (for example, https://uppy.io/docs/instagram/ for the Instagram plugin) and use the updated options.`',
|
|
|
|
+ )
|
|
}
|
|
}
|
|
|
|
|
|
if (opts.companionAllowedHosts) {
|
|
if (opts.companionAllowedHosts) {
|
|
const pattern = opts.companionAllowedHosts
|
|
const pattern = opts.companionAllowedHosts
|
|
// validate companionAllowedHosts param
|
|
// validate companionAllowedHosts param
|
|
- if (typeof pattern !== 'string' && !Array.isArray(pattern) && !(pattern instanceof RegExp)) {
|
|
|
|
- throw new TypeError(`${plugin.id}: the option "companionAllowedHosts" must be one of string, Array, RegExp`)
|
|
|
|
|
|
+ if (
|
|
|
|
+ typeof pattern !== 'string' &&
|
|
|
|
+ !Array.isArray(pattern) &&
|
|
|
|
+ !(pattern instanceof RegExp)
|
|
|
|
+ ) {
|
|
|
|
+ throw new TypeError(
|
|
|
|
+ `${plugin.id}: the option "companionAllowedHosts" must be one of string, Array, RegExp`,
|
|
|
|
+ )
|
|
}
|
|
}
|
|
plugin.opts.companionAllowedHosts = pattern
|
|
plugin.opts.companionAllowedHosts = pattern
|
|
} else if (/^(?!https?:\/\/).*$/i.test(opts.companionUrl)) {
|
|
} else if (/^(?!https?:\/\/).*$/i.test(opts.companionUrl)) {
|
|
// does not start with https://
|
|
// does not start with https://
|
|
- plugin.opts.companionAllowedHosts = `https://${opts.companionUrl?.replace(/^\/\//, '')}`
|
|
|
|
|
|
+ plugin.opts.companionAllowedHosts = `https://${opts.companionUrl?.replace(
|
|
|
|
+ /^\/\//,
|
|
|
|
+ '',
|
|
|
|
+ )}`
|
|
} else {
|
|
} else {
|
|
plugin.opts.companionAllowedHosts = new URL(opts.companionUrl).origin
|
|
plugin.opts.companionAllowedHosts = new URL(opts.companionUrl).origin
|
|
}
|
|
}
|