From 57b2b7693288aa42ea2ba0012323bd1f4664e751 Mon Sep 17 00:00:00 2001 From: Andras Schmelczer Date: Sat, 7 Jun 2025 22:24:27 +0100 Subject: [PATCH] Lint & format --- .../src/utils/get-cursors-from-editor.ts | 2 +- .../src/utils/get-random-color.ts | 9 ++++ .../sync-client/src/services/sync-service.ts | 52 +++++++++++-------- .../src/services/types/ClientCursors.ts | 2 +- .../services/types/CreateDocumentVersion.ts | 4 +- .../types/CursorPositionFromClient.ts | 2 +- .../types/CursorPositionFromServer.ts | 2 +- .../src/services/types/CursorSpan.ts | 2 +- .../services/types/DeleteDocumentVersion.ts | 2 +- .../src/services/types/DocumentVersion.ts | 2 +- .../types/DocumentVersionWithoutContent.ts | 2 +- .../types/FetchLatestDocumentsResponse.ts | 4 +- .../src/services/types/PingResponse.ts | 4 +- .../src/services/types/SerializedError.ts | 2 +- .../services/types/UpdateDocumentVersion.ts | 2 +- .../src/services/types/WebSocketHandshake.ts | 2 +- .../services/types/WebSocketVaultUpdate.ts | 2 +- .../src/services/websocket-manager.ts | 48 ++++++++--------- frontend/sync-client/src/sync-client.ts | 10 ++-- .../sync-client/src/sync-operations/syncer.ts | 2 +- .../sync-operations/unrestricted-syncer.ts | 6 +-- 21 files changed, 89 insertions(+), 74 deletions(-) create mode 100644 frontend/obsidian-plugin/src/utils/get-random-color.ts diff --git a/frontend/obsidian-plugin/src/utils/get-cursors-from-editor.ts b/frontend/obsidian-plugin/src/utils/get-cursors-from-editor.ts index 8844942a..62113d1b 100644 --- a/frontend/obsidian-plugin/src/utils/get-cursors-from-editor.ts +++ b/frontend/obsidian-plugin/src/utils/get-cursors-from-editor.ts @@ -1,4 +1,4 @@ -import { Editor } from "obsidian"; +import type { Editor } from "obsidian"; import { lineAndColumnToPosition } from "./line-and-column-to-position"; export interface Cursor { diff --git a/frontend/obsidian-plugin/src/utils/get-random-color.ts b/frontend/obsidian-plugin/src/utils/get-random-color.ts new file mode 100644 index 00000000..eadd0927 --- /dev/null +++ b/frontend/obsidian-plugin/src/utils/get-random-color.ts @@ -0,0 +1,9 @@ +export function getRandomColor(name: string): string { + let hash = 0; + for (let i = 0; i < name.length; i++) { + hash = (hash << 5) - hash + name.charCodeAt(i); + hash |= 0; // Convert to 32bit integer + } + const normalised = hash / 0x7fffffff; + return `hsl(${Math.abs(normalised * 360)}, 70%, 30%)`; // HSL color +} diff --git a/frontend/sync-client/src/services/sync-service.ts b/frontend/sync-client/src/services/sync-service.ts index 3d1cfadb..5ac81d5b 100644 --- a/frontend/sync-client/src/services/sync-service.ts +++ b/frontend/sync-client/src/services/sync-service.ts @@ -9,13 +9,13 @@ import type { Settings } from "../persistence/settings"; import type { ConnectionStatus } from "./connection-status"; import { sleep } from "../utils/sleep"; import { SyncResetError } from "./sync-reset-error"; -import { SerializedError } from "./types/SerializedError"; -import { DocumentVersionWithoutContent } from "./types/DocumentVersionWithoutContent"; -import { DocumentUpdateResponse } from "./types/DocumentUpdateResponse"; -import { DocumentVersion } from "./types/DocumentVersion"; -import { FetchLatestDocumentsResponse } from "./types/FetchLatestDocumentsResponse"; -import { PingResponse } from "./types/PingResponse"; -import { DeleteDocumentVersion } from "./types/DeleteDocumentVersion"; +import type { SerializedError } from "./types/SerializedError"; +import type { DocumentVersionWithoutContent } from "./types/DocumentVersionWithoutContent"; +import type { DocumentUpdateResponse } from "./types/DocumentUpdateResponse"; +import type { DocumentVersion } from "./types/DocumentVersion"; +import type { FetchLatestDocumentsResponse } from "./types/FetchLatestDocumentsResponse"; +import type { PingResponse } from "./types/PingResponse"; +import type { DeleteDocumentVersion } from "./types/DeleteDocumentVersion"; export interface CheckConnectionResult { isSuccessful: boolean; @@ -24,8 +24,8 @@ export interface CheckConnectionResult { export class SyncService { private static readonly NETWORK_RETRY_INTERVAL_MS = 1000; - private client: typeof globalThis.fetch; - private pingClient: typeof globalThis.fetch; + private readonly client: typeof globalThis.fetch; + private readonly pingClient: typeof globalThis.fetch; public constructor( private readonly deviceId: string, @@ -35,7 +35,7 @@ export class SyncService { fetchImplementation: typeof globalThis.fetch = globalThis.fetch ) { // ensure that if it's called a method, `this` won't be bound to the instance - const unboundFetch: typeof globalThis.fetch = (...args) => + const unboundFetch: typeof globalThis.fetch = async (...args) => fetchImplementation(...args); this.client = this.connectionStatus.getFetchImplementation( @@ -45,12 +45,6 @@ export class SyncService { this.pingClient = unboundFetch; } - private getUrl(path: string): string { - let { vaultName, remoteUri } = this.settings.getSettings(); - remoteUri = remoteUri.replace(/\/+$/, ""); - return `${remoteUri}/vaults/${vaultName}${path}`; - } - private static formatError(error: SerializedError): string { let result = error.message; if (error.causes.length > 0) { @@ -85,7 +79,9 @@ export class SyncService { }); const result: SerializedError | DocumentVersionWithoutContent = - await response.json(); + (await response.json()) as // eslint-disable-line @typescript-eslint/no-unsafe-type-assertion + | SerializedError + | DocumentVersionWithoutContent; if ("errorType" in result) { throw new Error( @@ -133,7 +129,9 @@ export class SyncService { ); const result: SerializedError | DocumentUpdateResponse = - await response.json(); + (await response.json()) as // eslint-disable-line @typescript-eslint/no-unsafe-type-assertion + | SerializedError + | DocumentUpdateResponse; if ("errorType" in result) { throw new Error( @@ -175,7 +173,9 @@ export class SyncService { ); const result: SerializedError | DocumentVersionWithoutContent = - await response.json(); + (await response.json()) as // eslint-disable-line @typescript-eslint/no-unsafe-type-assertion + | SerializedError + | DocumentVersionWithoutContent; if ("errorType" in result) { throw new Error( @@ -205,7 +205,7 @@ export class SyncService { ); const result: SerializedError | DocumentVersion = - await response.json(); + (await response.json()) as SerializedError | DocumentVersion; // eslint-disable-line @typescript-eslint/no-unsafe-type-assertion if ("errorType" in result) { throw new Error( @@ -234,7 +234,9 @@ export class SyncService { }); const result: SerializedError | FetchLatestDocumentsResponse = - await response.json(); + (await response.json()) as // eslint-disable-line @typescript-eslint/no-unsafe-type-assertion + | SerializedError + | FetchLatestDocumentsResponse; if ("errorType" in result) { throw new Error( @@ -256,7 +258,7 @@ export class SyncService { headers: this.getDefaultHeaders() }); const result: PingResponse | SerializedError = - await response.json(); + (await response.json()) as PingResponse | SerializedError; // eslint-disable-line @typescript-eslint/no-unsafe-type-assertion if ("errorType" in result) { throw new Error( @@ -283,6 +285,12 @@ export class SyncService { } } + private getUrl(path: string): string { + const { vaultName, remoteUri } = this.settings.getSettings(); + const safeRemoteUri = remoteUri.replace(/\/+$/, ""); + return `${safeRemoteUri}/vaults/${vaultName}${path}`; + } + private getDefaultHeaders(): Record { return { "device-id": this.deviceId, diff --git a/frontend/sync-client/src/services/types/ClientCursors.ts b/frontend/sync-client/src/services/types/ClientCursors.ts index a70420b8..cec4f064 100644 --- a/frontend/sync-client/src/services/types/ClientCursors.ts +++ b/frontend/sync-client/src/services/types/ClientCursors.ts @@ -1,4 +1,4 @@ // This file was generated by [ts-rs](https://github.com/Aleph-Alpha/ts-rs). Do not edit this file manually. import type { CursorSpan } from "./CursorSpan"; -export type ClientCursors = { userName: string, deviceId: string, cursors: { [key in string]?: Array }, }; +export interface ClientCursors { userName: string, deviceId: string, cursors: Partial>, } diff --git a/frontend/sync-client/src/services/types/CreateDocumentVersion.ts b/frontend/sync-client/src/services/types/CreateDocumentVersion.ts index 4823105b..2a5ea1a0 100644 --- a/frontend/sync-client/src/services/types/CreateDocumentVersion.ts +++ b/frontend/sync-client/src/services/types/CreateDocumentVersion.ts @@ -1,10 +1,10 @@ // This file was generated by [ts-rs](https://github.com/Aleph-Alpha/ts-rs). Do not edit this file manually. -export type CreateDocumentVersion = { +export interface CreateDocumentVersion { /** * The client can decide the document id (if it wishes to) in order * to help with syncing. If the client does not provide a document id, * the server will generate one. If the client provides a document id * it must not already exist in the database. */ -document_id: string | null, relative_path: string, content: Array, }; +document_id: string | null, relative_path: string, content: number[], } diff --git a/frontend/sync-client/src/services/types/CursorPositionFromClient.ts b/frontend/sync-client/src/services/types/CursorPositionFromClient.ts index 87705d1c..f51b6603 100644 --- a/frontend/sync-client/src/services/types/CursorPositionFromClient.ts +++ b/frontend/sync-client/src/services/types/CursorPositionFromClient.ts @@ -1,4 +1,4 @@ // This file was generated by [ts-rs](https://github.com/Aleph-Alpha/ts-rs). Do not edit this file manually. import type { CursorSpan } from "./CursorSpan"; -export type CursorPositionFromClient = { documentToCursors: { [key in string]?: Array }, }; +export interface CursorPositionFromClient { documentToCursors: Partial>, } diff --git a/frontend/sync-client/src/services/types/CursorPositionFromServer.ts b/frontend/sync-client/src/services/types/CursorPositionFromServer.ts index c8444892..ed6ac7b2 100644 --- a/frontend/sync-client/src/services/types/CursorPositionFromServer.ts +++ b/frontend/sync-client/src/services/types/CursorPositionFromServer.ts @@ -1,4 +1,4 @@ // This file was generated by [ts-rs](https://github.com/Aleph-Alpha/ts-rs). Do not edit this file manually. import type { ClientCursors } from "./ClientCursors"; -export type CursorPositionFromServer = { clients: Array, }; +export interface CursorPositionFromServer { clients: ClientCursors[], } diff --git a/frontend/sync-client/src/services/types/CursorSpan.ts b/frontend/sync-client/src/services/types/CursorSpan.ts index d0bce6ea..7424067c 100644 --- a/frontend/sync-client/src/services/types/CursorSpan.ts +++ b/frontend/sync-client/src/services/types/CursorSpan.ts @@ -1,3 +1,3 @@ // This file was generated by [ts-rs](https://github.com/Aleph-Alpha/ts-rs). Do not edit this file manually. -export type CursorSpan = { start: number, end: number, }; +export interface CursorSpan { start: number, end: number, } diff --git a/frontend/sync-client/src/services/types/DeleteDocumentVersion.ts b/frontend/sync-client/src/services/types/DeleteDocumentVersion.ts index 6244f7ab..5d4bad98 100644 --- a/frontend/sync-client/src/services/types/DeleteDocumentVersion.ts +++ b/frontend/sync-client/src/services/types/DeleteDocumentVersion.ts @@ -1,3 +1,3 @@ // This file was generated by [ts-rs](https://github.com/Aleph-Alpha/ts-rs). Do not edit this file manually. -export type DeleteDocumentVersion = { relativePath: string, }; +export interface DeleteDocumentVersion { relativePath: string, } diff --git a/frontend/sync-client/src/services/types/DocumentVersion.ts b/frontend/sync-client/src/services/types/DocumentVersion.ts index 37bd32ca..3d50ae65 100644 --- a/frontend/sync-client/src/services/types/DocumentVersion.ts +++ b/frontend/sync-client/src/services/types/DocumentVersion.ts @@ -1,3 +1,3 @@ // This file was generated by [ts-rs](https://github.com/Aleph-Alpha/ts-rs). Do not edit this file manually. -export type DocumentVersion = { vaultUpdateId: number, documentId: string, relativePath: string, updatedDate: string, contentBase64: string, isDeleted: boolean, userId: string, deviceId: string, }; +export interface DocumentVersion { vaultUpdateId: number, documentId: string, relativePath: string, updatedDate: string, contentBase64: string, isDeleted: boolean, userId: string, deviceId: string, } diff --git a/frontend/sync-client/src/services/types/DocumentVersionWithoutContent.ts b/frontend/sync-client/src/services/types/DocumentVersionWithoutContent.ts index 03be2f63..af064db8 100644 --- a/frontend/sync-client/src/services/types/DocumentVersionWithoutContent.ts +++ b/frontend/sync-client/src/services/types/DocumentVersionWithoutContent.ts @@ -1,3 +1,3 @@ // This file was generated by [ts-rs](https://github.com/Aleph-Alpha/ts-rs). Do not edit this file manually. -export type DocumentVersionWithoutContent = { vaultUpdateId: number, documentId: string, relativePath: string, updatedDate: string, isDeleted: boolean, userId: string, deviceId: string, contentSize: number, }; +export interface DocumentVersionWithoutContent { vaultUpdateId: number, documentId: string, relativePath: string, updatedDate: string, isDeleted: boolean, userId: string, deviceId: string, contentSize: number, } diff --git a/frontend/sync-client/src/services/types/FetchLatestDocumentsResponse.ts b/frontend/sync-client/src/services/types/FetchLatestDocumentsResponse.ts index ce572684..3be625bd 100644 --- a/frontend/sync-client/src/services/types/FetchLatestDocumentsResponse.ts +++ b/frontend/sync-client/src/services/types/FetchLatestDocumentsResponse.ts @@ -4,8 +4,8 @@ import type { DocumentVersionWithoutContent } from "./DocumentVersionWithoutCont /** * Response to a fetch latest documents request. */ -export type FetchLatestDocumentsResponse = { latestDocuments: Array, +export interface FetchLatestDocumentsResponse { latestDocuments: DocumentVersionWithoutContent[], /** * The update ID of the latest document in the response. */ -lastUpdateId: bigint, }; +lastUpdateId: bigint, } diff --git a/frontend/sync-client/src/services/types/PingResponse.ts b/frontend/sync-client/src/services/types/PingResponse.ts index 6d3cba6e..7d64ea36 100644 --- a/frontend/sync-client/src/services/types/PingResponse.ts +++ b/frontend/sync-client/src/services/types/PingResponse.ts @@ -3,7 +3,7 @@ /** * Response to a ping request. */ -export type PingResponse = { +export interface PingResponse { /** * Semantic version of the server. */ @@ -12,4 +12,4 @@ serverVersion: string, * Whether the client is authenticated based on the sent Authorization * header. */ -isAuthenticated: boolean, }; +isAuthenticated: boolean, } diff --git a/frontend/sync-client/src/services/types/SerializedError.ts b/frontend/sync-client/src/services/types/SerializedError.ts index 5e3fa9b9..4389289e 100644 --- a/frontend/sync-client/src/services/types/SerializedError.ts +++ b/frontend/sync-client/src/services/types/SerializedError.ts @@ -1,3 +1,3 @@ // This file was generated by [ts-rs](https://github.com/Aleph-Alpha/ts-rs). Do not edit this file manually. -export type SerializedError = { errorType: string, message: string, causes: Array, }; +export interface SerializedError { errorType: string, message: string, causes: string[], } diff --git a/frontend/sync-client/src/services/types/UpdateDocumentVersion.ts b/frontend/sync-client/src/services/types/UpdateDocumentVersion.ts index 482e281a..e0ddd5ac 100644 --- a/frontend/sync-client/src/services/types/UpdateDocumentVersion.ts +++ b/frontend/sync-client/src/services/types/UpdateDocumentVersion.ts @@ -1,3 +1,3 @@ // This file was generated by [ts-rs](https://github.com/Aleph-Alpha/ts-rs). Do not edit this file manually. -export type UpdateDocumentVersion = { parent_version_id: bigint, relative_path: string, content: Array, }; +export interface UpdateDocumentVersion { parent_version_id: bigint, relative_path: string, content: number[], } diff --git a/frontend/sync-client/src/services/types/WebSocketHandshake.ts b/frontend/sync-client/src/services/types/WebSocketHandshake.ts index 85c2cf0d..d25651f9 100644 --- a/frontend/sync-client/src/services/types/WebSocketHandshake.ts +++ b/frontend/sync-client/src/services/types/WebSocketHandshake.ts @@ -1,3 +1,3 @@ // This file was generated by [ts-rs](https://github.com/Aleph-Alpha/ts-rs). Do not edit this file manually. -export type WebSocketHandshake = { token: string, deviceId: string, lastSeenVaultUpdateId: number | null, }; +export interface WebSocketHandshake { token: string, deviceId: string, lastSeenVaultUpdateId: number | null, } diff --git a/frontend/sync-client/src/services/types/WebSocketVaultUpdate.ts b/frontend/sync-client/src/services/types/WebSocketVaultUpdate.ts index b627ac3c..39e03b6f 100644 --- a/frontend/sync-client/src/services/types/WebSocketVaultUpdate.ts +++ b/frontend/sync-client/src/services/types/WebSocketVaultUpdate.ts @@ -1,4 +1,4 @@ // This file was generated by [ts-rs](https://github.com/Aleph-Alpha/ts-rs). Do not edit this file manually. import type { DocumentVersionWithoutContent } from "./DocumentVersionWithoutContent"; -export type WebSocketVaultUpdate = { documents: Array, isInitialSync: boolean, }; +export interface WebSocketVaultUpdate { documents: DocumentVersionWithoutContent[], isInitialSync: boolean, } diff --git a/frontend/sync-client/src/services/websocket-manager.ts b/frontend/sync-client/src/services/websocket-manager.ts index 0006e344..285d51f9 100644 --- a/frontend/sync-client/src/services/websocket-manager.ts +++ b/frontend/sync-client/src/services/websocket-manager.ts @@ -1,11 +1,11 @@ import type { Database } from "../persistence/database"; import type { Logger } from "../tracing/logger"; import type { Settings, SyncSettings } from "../persistence/settings"; -import { WebSocketServerMessage } from "./types/WebSocketServerMessage"; -import { Syncer } from "../sync-operations/syncer"; -import { WebSocketClientMessage } from "./types/WebSocketClientMessage"; -import { CursorPositionFromClient } from "./types/CursorPositionFromClient"; -import { ClientCursors } from "./types/ClientCursors"; +import type { WebSocketServerMessage } from "./types/WebSocketServerMessage"; +import type { Syncer } from "../sync-operations/syncer"; +import type { WebSocketClientMessage } from "./types/WebSocketClientMessage"; +import type { CursorPositionFromClient } from "./types/CursorPositionFromClient"; +import type { ClientCursors } from "./types/ClientCursors"; export class WebSocketManager { private readonly webSocketStatusChangeListeners: (() => unknown)[] = []; @@ -19,7 +19,6 @@ export class WebSocketManager { private readonly webSocketFactoryImplementation: typeof globalThis.WebSocket; - // eslint-disable-next-line @typescript-eslint/max-params public constructor( private readonly deviceId: string, private readonly logger: Logger, @@ -90,6 +89,23 @@ export class WebSocketManager { } } + public updateLocalCursors(cursorPositions: CursorPositionFromClient): void { + if (!this.isWebSocketConnected) { + this.logger.warn( + "WebSocket is not connected, cannot send cursor positions" + ); + return; + } + const message: WebSocketClientMessage = { + type: "cursorPositions", + ...cursorPositions + }; + this.webSocket?.send(JSON.stringify(message)); + this.logger.info( + `Sent cursor positions: ${JSON.stringify(cursorPositions)}` + ); + } + private updateWebSocket(settings: SyncSettings): void { try { this.webSocket?.close(); @@ -134,6 +150,7 @@ export class WebSocketManager { `Failed to sync remotely updated file: ${e}` ); } + // eslint-disable-next-line @typescript-eslint/no-unnecessary-condition } else if (message.type === "cursorPositions") { this.logger.info( `Received cursor positions for ${JSON.stringify(message.clients)}` @@ -159,7 +176,7 @@ export class WebSocketManager { listener(); }); - let message: WebSocketClientMessage = { + const message: WebSocketClientMessage = { type: "handshake", deviceId: this.deviceId, token: settings.token, @@ -178,23 +195,6 @@ export class WebSocketManager { }; } - public updateLocalCursors(cursorPositions: CursorPositionFromClient): void { - if (!this.isWebSocketConnected) { - this.logger.warn( - "WebSocket is not connected, cannot send cursor positions" - ); - return; - } - let message: WebSocketClientMessage = { - type: "cursorPositions", - ...cursorPositions - }; - this.webSocket?.send(JSON.stringify(message)); - this.logger.info( - `Sent cursor positions: ${JSON.stringify(cursorPositions)}` - ); - } - private setWebSocketRefreshInterval(): void { this.refreshWebSocketInterval = setInterval(() => { if ( diff --git a/frontend/sync-client/src/sync-client.ts b/frontend/sync-client/src/sync-client.ts index 1bb92f3c..70e100ed 100644 --- a/frontend/sync-client/src/sync-client.ts +++ b/frontend/sync-client/src/sync-client.ts @@ -19,8 +19,8 @@ import type { NetworkConnectionStatus } from "./types/network-connection-status" import { DocumentUpdateStatus } from "./types/document-update-status"; import { WebSocketManager } from "./services/websocket-manager"; import { createClientId } from "./utils/create-client-id"; -import { CursorSpan } from "./services/types/CursorSpan"; -import { ClientCursors } from "./services/types/ClientCursors"; +import type { CursorSpan } from "./services/types/CursorSpan"; +import type { ClientCursors } from "./services/types/ClientCursors"; export class SyncClient { private static readonly MINIMUM_SAVE_INTERVAL_MS = 1000; @@ -275,10 +275,8 @@ export class SyncClient { }); } - public async updateLocalCursors(documentToCursors: { - [path: RelativePath]: CursorSpan[]; - }): Promise { - return this.webSocketManager.updateLocalCursors({ documentToCursors }); + public async updateLocalCursors(documentToCursors: Record): Promise { + this.webSocketManager.updateLocalCursors({ documentToCursors }); } public addRemoteCursorsUpdateListener( diff --git a/frontend/sync-client/src/sync-operations/syncer.ts b/frontend/sync-client/src/sync-operations/syncer.ts index 9270a8ed..30e012d9 100644 --- a/frontend/sync-client/src/sync-operations/syncer.ts +++ b/frontend/sync-client/src/sync-operations/syncer.ts @@ -16,7 +16,7 @@ import type { UnrestrictedSyncer } from "./unrestricted-syncer"; import { createPromise } from "../utils/create-promise"; import { SyncResetError } from "../services/sync-reset-error"; import { Locks } from "../utils/locks"; -import { DocumentVersionWithoutContent } from "../services/types/DocumentVersionWithoutContent"; +import type { DocumentVersionWithoutContent } from "../services/types/DocumentVersionWithoutContent"; export class Syncer { private readonly remoteDocumentsLock: Locks; diff --git a/frontend/sync-client/src/sync-operations/unrestricted-syncer.ts b/frontend/sync-client/src/sync-operations/unrestricted-syncer.ts index f892e640..0d0f45ef 100644 --- a/frontend/sync-client/src/sync-operations/unrestricted-syncer.ts +++ b/frontend/sync-client/src/sync-operations/unrestricted-syncer.ts @@ -24,9 +24,9 @@ import { createPromise } from "../utils/create-promise"; import { FileNotFoundError } from "../file-operations/file-not-found-error"; import { SyncResetError } from "../services/sync-reset-error"; import { globsToRegexes } from "../utils/globs-to-regexes"; -import { DocumentVersion } from "../services/types/DocumentVersion"; -import { DocumentUpdateResponse } from "../services/types/DocumentUpdateResponse"; -import { DocumentVersionWithoutContent } from "../services/types/DocumentVersionWithoutContent"; +import type { DocumentVersion } from "../services/types/DocumentVersion"; +import type { DocumentUpdateResponse } from "../services/types/DocumentUpdateResponse"; +import type { DocumentVersionWithoutContent } from "../services/types/DocumentVersionWithoutContent"; export class UnrestrictedSyncer { private ignorePatterns: RegExp[];