UNPKG

@blinkk/editor

Version:

Structured content editor with live previews.

779 lines (778 loc) 19.9 kB
import { EditorNotification } from './parts/notifications'; import { FeatureManagerSettings } from '../utility/featureManager'; import { FieldConfig } from '@blinkk/selective-edit'; import { IncludeExcludeFilterConfig } from '../utility/filter'; import { LiveEditorLabels } from './editor'; import bent from 'bent'; /** * Interface for the live editor api. * * This defines how the editor works with the underlying data. The api * is responsible for all file or network operations needed to make the * editor function. */ export interface LiveEditorApiComponent { /** * Verify that the authentication for services that require auth. * * @returns True if the auth checks out. */ checkAuth(): boolean; /** * Copy a file. * * @param path Full path for the original file. * @param path Full path for the new file. */ copyFile(originalPath: string, path: string): Promise<FileData>; /** * Create a new file from scratch. * * @param path Full path for the new file. */ createFile(path: string): Promise<FileData>; /** * Create a new workspace based off an existing workspace. * * @param base Workspace to base new workspace from. * @param workspace New workspace name. */ createWorkspace(base: WorkspaceData, workspace: string): Promise<WorkspaceData>; /** * Delete an existing file. * * @param path Full path for the file being deleted. */ deleteFile(file: FileData): Promise<EmptyData>; /** * Retrieve the devices used for previews. */ getDevices(): Promise<Array<DeviceData>>; /** * Retrieve the file information. * * This is a complete loading of the file information and * configuration for use in rendering the editor for the * file. */ getFile(file: FileData): Promise<EditorFileData>; /** * Retrieve the files that can be edited in the editor. */ getFiles(): Promise<Array<FileData>>; /** * Retrieve the url to preview the file. * * When retrieving a list of files it is often slow */ getFileUrl(file: FileData): Promise<FileData>; /** * Retrieve information about the project. */ getProject(): Promise<ProjectData>; /** * Retrieve information about the active workspace. */ getWorkspace(): Promise<WorkspaceData>; /** * Retrieve information about available workspaces. */ getWorkspaces(): Promise<Array<WorkspaceData>>; /** * Load the workspace. * * This may redirect to a different URL. * (ex: workspaces may be domain based.) */ loadWorkspace(workspace: WorkspaceData): Promise<WorkspaceData>; /** * Project type specific apis. */ projectTypes: ApiProjectTypes; /** * Start the publish process. * * Begins the publish process. Some publish processes may take time and cannot * be completed right away. This api begins the process of publishing and * gives a status response on the new publish. */ publish(workspace: WorkspaceData, data?: Record<string, any>): Promise<PublishResult>; /** * Save the updated file data. * * @param file File data to be saved. * @param isRawEdit Is the edit to the raw file data? */ saveFile(file: EditorFileData, isRawEdit: boolean): Promise<EditorFileData>; /** * Upload a file. * * Uses a File object to provide a blob file that should be uploaded * or saved appropriately. Often for media like images or videos. */ uploadFile(file: File, options?: MediaOptions): Promise<MediaFileData>; } export interface ApiProjectTypes { amagaki: AmagakiProjectTypeApi; grow: GrowProjectTypeApi; } export interface AmagakiProjectTypeApi { /** * Retrieve the partials for the project for the partials field. */ getPartials(): Promise<Record<string, PartialData>>; } export interface GrowProjectTypeApi { /** * Retrieve the partials for the project for the partials field. */ getPartials(): Promise<Record<string, PartialData>>; /** * Retrieve the available strings used in the `!g.string` yaml constructor. * * Returns a mapping of strings podpath to the contents of the strings file. * * ```json * { * "/content/strings/example.yaml": { * "foo": "bar" * } * } * ``` */ getStrings(): Promise<Record<string, any>>; } /** * Interface for the structure of the editor settings file. * * Settings in a project's `editor.yaml` should follow this interface. */ export interface EditorFileSettings { /** * Title of the site to display in the editor. */ title?: string; /** * Devices to use in the editor preview. */ devices?: Array<DeviceData>; /** * Editor experiment flags and settings. * * Used to control editor.dev experiments for the project. */ experiments?: Record<string, boolean | FeatureManagerSettings>; /** * Editor feature flags and settings. * * Used to control editor.dev features for the project. */ features?: Record<string, boolean | FeatureManagerSettings>; /** * Media configuration for the project. * * This controls options around how the media is handled in the project. * Including custom providers for media upload. */ media?: ProjectMediaConfig; /** * Configuration for the site display in the editor. */ site?: SiteData; /** * Users or groups approved access to the editor. */ users?: Array<UserData>; /** * Settings for customizing the editor UI. */ ui?: EditorUiSettings; } export interface EditorUiSettings { /** * Labels for customizing the editor UI. */ labels?: LiveEditorLabels; } /** * Device information used for previews. */ export interface DeviceData { /** * Can the device preview be rotated? */ canRotate?: boolean; /** * Height of the device view. */ height?: number; /** * Label for the device. */ label: string; /** * Width of the device view. */ width: number; } /** * Full file information for rendering the file editor. */ export interface EditorFileData { /** * File contents. * * For example, the html in an html file, or the markdown body * in a markdown file. */ content?: string; /** * File data. * * For example, the frontmatter for a markdown file or the contents * of a yaml file. */ data?: any; /** * Raw file data. * * For example, the frontmatter for a markdown file or the contents * of a yaml file. This is the unprocessed data string that can be edited * in the 'Raw' content form. */ dataRaw?: string; /** * Editor configuration for the file. * * If not provided the editor will attempt to guess the fields to use. */ editor?: EditorFileConfig; /** * File information. */ file: FileData; /** * File repository history. */ history?: Array<RepoCommit>; /** * Sha of the file being edited. * * Used by the api to verify that there are no new changes to the file * since the edit started to avoid overwriting external changes. */ sha?: string; /** * URL for viewing the file in the preview iframe. * * If no url is provided the preview will be hidden. */ url?: string; /** * URLs for viewing the file in different environments. */ urls?: Array<UrlConfig>; } /** * Empty response from the api. */ export interface EmptyData { } /** * File information. */ export interface FileData { /** * Complete path for the file. */ path: string; /** * URL for serving the file. * * Used for showing previews of different files. * For example, image or video previews. * * For performance reasons, file data does not need to include url * information as it may require more time to properly retrieve the * url for a file which can slow down file list retrieval. * * `undefined` url is used to denote that the url was not retrieved. * `null` url is used to denote that there is no url for the file. * * The editor will attempt to use the `getFileUrl()` api method when * the url is `undefined` and the url is needed. If the value is `null` * the editor assumes that there is no url for the file and does not * make a request to the `getFileUrl()` method. */ url?: string | null; } export interface PartialData { partial: string; editor?: PartialEditorConfig; } /** * Configuration for rendering the file editor. */ export interface PartialEditorConfig { /** * Partial label. */ label?: string; /** * Partial description. */ description?: string; /** * Field configurations for the editor. */ fields: Array<FieldConfig>; /** * Preview field keys. * * When showing a preview of the partial, use these field keys to determine * the value to show for the preview. */ previewFields?: Array<string>; } /** * Overall project information. */ export interface ProjectData { /** * Project title */ title: string; /** * Editor experiment flags and settings. * * Experiments can be defined in the `editor.yaml` file for the project. * The API can override experiment flags if the API does not support * specific experiments of the editor. */ experiments?: Record<string, boolean | FeatureManagerSettings>; /** * Editor feature flags and settings. * * Features can be defined in the `editor.yaml` file for the project. * The API can override feature flags if the API does not support * specific features of the editor. */ features?: Record<string, boolean | FeatureManagerSettings>; /** * Media configuration for the project. * * This controls options around how the media is handled in the project. */ media?: ProjectMediaConfig; /** * Publish configuration for the project. * * This controls if the UI allows for publishing and what information * to collect for providing to the `publish` method on the api. */ publish?: ProjectPublishConfig; /** * Configuration for the site display. */ site?: SiteData; /** * Project type for the editor to use. */ type?: ProjectTypes | string; /** * Settings for customizing the editor UI. */ ui?: EditorUiSettings; /** * Users or groups approved access to the editor. */ users?: Array<UserData>; } /** * Result from pinging the api. */ export interface PingResult { /** * Status of the api. */ status: PingStatus | string; } /** * Result from starting the publish process. */ export interface PublishResult { /** * Status of the publish process. */ status: PublishStatus | string; /** * Updated workspace data. * * When a publish process is complete the workflow for the publish * process can either keep the same workspace open, or remove it. * In the case that the workspace is removed, the api can direct the * editor to load a different workspace instead. */ workspace?: WorkspaceData; /** * URLs for viewing the publish state. */ urls?: Array<UrlConfig>; } /** * Site information. */ export interface SiteData { /** * Site files configuration. */ files?: SiteFilesConfig; } /** * User information. */ export interface UserData { /** * Name of the user. */ name: string; /** * Email representing the user. */ email: string; /** * Is the user data a group? */ isGroup?: boolean; } /** * Workspace information. */ export interface WorkspaceData { /** * Full branch information from the workspace. */ branch: RepoBranch; /** * Short name for the workspace used in labels and lists. */ name: string; /** * Workspace specific publishing configuration. */ publish?: WorkspacePublishConfig; } /** * When there are errors with an api call the promise should * be rejected with an ApiError argument. * * ```typescript * createWorkspace(base: WorkspaceData, workspace: string): Promise<null> { * return new Promise<null>((resolve, reject) => { * // Successful api calls resolve() when done. * // Failure should reject() the promise with an ApiError argument. * reject({message: 'Houston we have a problem'} as ApiError); * }); * } * ``` */ export interface ApiError extends EditorNotification { /** * Additional meta information that can be used for a full report * or debugging of the error. */ details?: any; } /** * Catch and announce an api error. * * @param error Error from api. */ export declare function catchError(error: ApiError | bent.StatusError, callback?: (error: ApiError) => void): void; /** * Auxillary interfaces used in the api data. */ /** * Configuration for rendering the file editor. */ export interface EditorFileConfig { /** * Field configurations for the editor. */ fields: Array<FieldConfig>; } /** * Configuration for how media works in the editor UI. */ export interface ProjectMediaConfig { /** * Remote configuration for uploading media to a remote provider. * * This is used for things such as uploading to a CDN or * optimization service. */ remote?: RemoteMediaOptions; /** * Local media configuration for uploading media using the * connector api. */ options?: MediaOptions; } /** * Configuration for how publishing works in the editor UI. */ export interface ProjectPublishConfig { /** * Field information for collecting information for the publish process. * * If there are field configurations provided the UI will prompt the user * for the information and pass it on to the `publish` api call. */ fields?: Array<FieldConfig>; } /** * Configuration for how site files are displayed. */ export interface SiteFilesConfig { /** * Filter settings for how the site files are filtered. * * By default the site files filters: * - Only `.yaml`, `.md`, and `.html` files. * - Ignores files and directories starting with `_` and `.`. */ filter?: IncludeExcludeFilterConfig; } /** * Remote media providers supported in the editor. */ export declare enum RemoteMediaProviders { GCS = "GCS" } /** * Project types supported in the editor. */ export declare enum ProjectTypes { Amagaki = "Amagaki", Grow = "Grow" } /** * Status for the api ping. */ export declare enum PingStatus { /** * Api is available. */ Ok = "Ok" } /** * Status for the publish process. */ export declare enum PublishStatus { /** * There are no publish processes allowed. * * Some workspaces may not allow for publishing. * For example the `main` branch has no where to be published. */ NotAllowed = "NotAllowed", /** * There are no active publish processes. */ NotStarted = "NotStarted", /** * There are no changes to publish. * * For example, the main branch and the current branch are on the same * commit and there is nothing to publish. */ NoChanges = "NoChanges", /** * There is an active publish in process. */ Pending = "Pending", /** * The publish process has completed. */ Complete = "Complete", /** * There was a problem during the publish process. */ Failure = "Failure" } /** * Repository author information. */ export interface RepoAuthor { /** * Name of the author. */ name: string; /** * Email address of the author. */ email: string; } /** * Repository branch information. */ export interface RepoBranch { /** * Commit most recent commit. */ commit: RepoCommit; /** * Full branch name. */ name: string; /** * Url for viewing the branch in an external system. */ url?: string; } /** * Repository commit information. */ export interface RepoCommit { /** * Author of the last commit. */ author?: RepoAuthor; /** * Commit hash of the last commit. */ hash: string; /** * Full commit message. */ message?: string; /** * Summary of the commit. */ summary?: string; /** * Timestamp of commit. * * Needs to be in a `Date.parse()` valid datetime format. * For example: ISO 8601. */ timestamp?: string; /** * Url to view the commit externally. */ url?: string; } /** * Configuration for url the file editor. */ export interface UrlConfig { /** * Label for the url. */ label: string; /** * Access level for the url. */ level: UrlLevel | string; /** * URL for viewing. */ url: string; } /** * Editor url accessibility level for a resource. */ export declare enum UrlLevel { /** * Private url, should not be shared to others and not public. * * For example, the editor preview url. It should not be shared * widely due to the transitive nature of workspaces, but can * still be used to viewed when needed. */ Private = "Private", /** * Protected url, a shared service that is used for sharing * but still restricted in how it is accessed. * * For example, a staging server to preview changes before * they are live. */ Protected = "Protected", /** * Public url, a publicly accessbile way to access the resource. * * For example, the live version of the site that users normally * see. */ Public = "Public", /** * Source url, a remotely hosted version of the resource. * * For example, a url that shows the resource in a repository * like github. */ Source = "Source" } /** * Configuration for how publishing works with a workspace. */ export interface WorkspacePublishConfig { /** * Current status of the workspace publishing. */ status: PublishStatus | string; /** * URLs for viewing the publish state. */ urls?: Array<UrlConfig>; } /** * Media interfaces. */ /** * Metadata from a remote provider media upload. */ export interface MediaFileMetaInfo { height?: number; mimeType?: string; width?: number; } /** * File data specific to media files. */ export interface MediaFileData extends FileData { meta?: MediaFileMetaInfo; } /** * Configuration for remote media providers. * * Used by the file upload to support different services to upload the * media to be processed/stored. */ export interface MediaOptions { /** * Identifier for the provider that will be handling the upload * request. */ provider?: RemoteMediaProviders | string; } /** * Configuration for the Google remote media provider. * * Currently works with the backend from https://github.com/grow/grow-ext-google-cloud-images */ export interface GoogleMediaOptions extends MediaOptions { /** * Url for the endpoint to handle the file upload. */ url: string; /** * Bucket name to upload the media file to. */ bucket?: string; } /** * Supported options for remote media provider options. */ export declare type RemoteMediaOptions = GoogleMediaOptions;