open-collaboration-protocol
Version:
Open Collaboration Protocol implementation, part of the Open Collaboration Tools project
378 lines (315 loc) • 9.5 kB
text/typescript
// ******************************************************************************
// Copyright 2024 TypeFox GmbH
// This program and the accompanying materials are made available under the
// terms of the MIT License, which is available in the project root.
// ******************************************************************************
import type { CompressionAlgorithm } from './messaging/messages.js';
import { isObject } from './utils/types.js';
export type Path = string;
export type Token = string;
export type Id = string;
export type Binary = Uint8Array;
// HTTP API
export interface CreateRoomResponse {
roomId: Id;
roomToken: Token;
loginToken?: Token;
}
export namespace CreateRoomResponse {
export function is(arg: unknown): arg is CreateRoomResponse {
return isObject<CreateRoomResponse>(arg) && typeof arg.roomId === 'string' && typeof arg.roomToken === 'string';
}
export function create(roomId: Id, roomToken: Token, loginToken?: Token): CreateRoomResponse {
return { roomId, roomToken, loginToken };
}
}
export interface LoginValidateResponse {
valid: boolean;
}
export namespace LoginValidateResponse {
export function is(arg: unknown): arg is LoginValidateResponse {
return isObject<LoginValidateResponse>(arg) && typeof arg.valid === 'boolean';
}
export function create(valid: boolean): LoginValidateResponse {
return { valid };
}
}
export interface LoginInitialResponse {
pollToken: string;
auth: AuthMetadata;
}
export namespace LoginInitialResponse {
export function is(arg: unknown): arg is LoginInitialResponse {
return isObject<LoginInitialResponse>(arg) && typeof arg.pollToken === 'string' && typeof arg.auth === 'object';
}
export function create(pollToken: string, auth: AuthMetadata): LoginInitialResponse {
return { pollToken, auth };
}
}
export interface AuthMetadata {
providers: AuthProvider[];
loginPageUrl?: string;
defaultSuccessUrl?: string;
}
export type AuthProvider = FormAuthProvider | WebAuthProvider;
export interface CommonAuthProvider {
name: string;
group: InfoMessage;
label: InfoMessage;
details?: InfoMessage;
}
export interface FormAuthProvider extends CommonAuthProvider {
type: 'form';
endpoint: string;
fields: FormAuthProviderField[];
}
export interface FormAuthProviderField {
name: string;
required: boolean;
label: InfoMessage;
placeHolder?: InfoMessage;
}
export interface WebAuthProvider extends CommonAuthProvider {
type: 'web';
endpoint: string;
}
export interface LoginPollResponse {
loginToken?: string;
}
export namespace LoginPollResponse {
export function is(arg: unknown): arg is LoginPollResponse {
return isObject<LoginPollResponse>(arg) && (typeof arg.loginToken === 'undefined' || typeof arg.loginToken === 'string');
}
export function create(loginToken?: string): LoginPollResponse {
return { loginToken };
}
}
export interface JoinRoomInitialResponse {
pollToken: string;
roomId: string;
}
export namespace JoinRoomInitialResponse {
export function is(arg: unknown): arg is JoinRoomInitialResponse {
return isObject<JoinRoomInitialResponse>(arg) && typeof arg.pollToken === 'string' && typeof arg.roomId === 'string';
}
export function create(pollToken: string, roomId: string): JoinRoomInitialResponse {
return { pollToken, roomId };
}
}
export interface JoinRoomResponse {
roomId: Id;
roomToken: Token;
loginToken?: Token;
workspace: Workspace;
host: Peer;
}
export namespace JoinRoomResponse {
export function is(arg: unknown): arg is JoinRoomResponse {
return isObject<JoinRoomResponse>(arg)
&& typeof arg.roomId === 'string'
&& typeof arg.roomToken === 'string'
&& typeof arg.workspace === 'object'
&& typeof arg.host === 'object';
}
export function create(roomId: Id, roomToken: Token, workspace: Workspace, host: Peer): JoinRoomResponse {
return { roomId, roomToken, workspace, host };
}
}
export interface JoinRoomPollResponse extends InfoMessage {
failure: boolean;
}
export namespace JoinRoomPollResponse {
export function is(arg: unknown): arg is JoinRoomPollResponse {
return InfoMessage.is(arg) && typeof (arg as JoinRoomPollResponse).failure === 'boolean';
}
export function create(code: string, params: string[], message: string, failure: boolean): JoinRoomPollResponse {
return { code, params, message, failure };
}
}
export interface ProtocolServerMetaData {
owner: string;
version: string;
transports: string[];
}
export interface InfoMessage {
code: string;
params: string[];
message: string;
}
export namespace InfoMessage {
export function is(arg: unknown): arg is InfoMessage {
return isObject<InfoMessage>(arg)
&& typeof arg.code === 'string'
&& Array.isArray(arg.params)
&& arg.params.every(param => typeof param === 'string')
&& typeof arg.message === 'string';
}
export function create(code: string, params: string[], message: string): InfoMessage {
return { code, params, message };
}
}
// Transport based API
export interface User {
name: string
email?: string
authProvider?: string
}
export interface JoinResponse {
workspace: Workspace
}
export interface Peer {
id: Id
host: boolean
name: string
email?: string
metadata: PeerMetaData
}
export interface PeerMetaData {
encryption: EncryptionMetaData
compression: CompressionMetaData;
}
export interface EncryptionMetaData {
publicKey: string;
}
export interface CompressionMetaData {
supported: CompressionAlgorithm[];
}
export interface InitRequest {
protocol: string;
}
export interface InitData {
protocol: string
host: Peer
guests: Peer[]
permissions: Permissions
capabilities: Capabilities
workspace: Workspace
}
export interface Workspace {
name: string
folders: string[]
}
// eslint-disable-next-line @typescript-eslint/no-empty-object-type
export interface Capabilities {
}
export interface Room {
id: string
host: Peer
guests: Peer[]
permissions: Permissions
}
export interface Permissions {
readonly: boolean;
[key: string]: string | boolean;
}
export interface FileSystemStat {
type: FileType;
mtime: number;
ctime: number;
size: number;
permissions?: FilePermission;
}
export interface FileSystemDirectory {
[name: string]: FileType
}
export interface FileData {
content: Binary
}
export enum FilePermission {
/**
* File is readonly.
*/
Readonly = 1
}
export enum FileType {
Unknown = 0,
File = 1,
Directory = 2,
SymbolicLink = 64
}
export interface FileChangeEvent {
changes: FileChange[]
}
export interface FileChange {
type: FileChangeEventType
path: Path
}
export enum FileChangeEventType {
Create = 0,
Update = 1,
Delete = 2
}
export interface ClientAwareness {
/**
* The peer id of the client.
*/
peer: string;
/**
* The client's current editor selection.
*/
selection?: ClientSelection;
}
export interface ClientSelection {
path: Path;
}
export interface ClientTextSelection extends ClientSelection {
visibleRanges?: Range[];
textSelections: RelativeTextSelection[];
}
export namespace ClientTextSelection {
export function is(selection?: ClientSelection): selection is ClientTextSelection {
if (!selection) {
return false;
}
const textSelection = selection as ClientTextSelection;
return Array.isArray(textSelection.textSelections);
}
}
export namespace SelectionDirection {
export const LeftToRight = 1;
export const RightToLeft = 2;
}
export interface ClientId {
client: number
clock: number
}
export interface RelativeTextSelection {
start: RelativeTextPosition;
end: RelativeTextPosition;
direction: SelectionDirection;
}
export interface RelativeTextPosition {
type: ClientId | null
tname: string | null
item: ClientId | null
assoc: number
}
export interface TextDiffChange {
range: Range;
text: string;
}
export interface Position {
line: number;
character: number;
}
export namespace Position {
export function is(arg: any): arg is Position {
return arg && typeof arg.line === 'number' && typeof arg.character === 'number';
}
export function create(line: number, character: number): Position {
return { line, character };
}
}
export interface Range {
start: Position;
end: Position;
}
export namespace Range {
export function is(arg: any): arg is Range {
return arg && Position.is(arg.start) && Position.is(arg.end);
}
export function create(start: Position, end: Position): Range {
return { start, end };
}
}
export type SelectionDirection = 1 | 2;