Merge pull request #34132 from ericyhwang/sharedb

[sharedb] Update type signatures for Backend #use and #addProjection
This commit is contained in:
Benjamin Lichtman
2019-04-02 18:56:58 -07:00
committed by GitHub
3 changed files with 253 additions and 8 deletions

View File

@@ -1,7 +1,9 @@
// Type definitions for sharedb 1.0
// Project: https://github.com/share/sharedb
// Definitions by: Steve Oney <https://github.com/soney>
// Eric Hwang <https://github.com/ericyhwang>
// Definitions: https://github.com/DefinitelyTyped/DefinitelyTyped
// TypeScript Version: 2.1
/// <reference path="lib/sharedb.d.ts" />
@@ -18,20 +20,33 @@ export = sharedb;
declare class sharedb {
constructor(options?: {db?: any, pubsub?: sharedb.PubSub, disableDocAction?: boolean, disableSpaceDelimitedActions?: boolean});
connect: () => sharedb.Connection;
addProjection(name: string, collection: string, fields: {}): any;
connect: (connection?: any, req?: any) => sharedb.Connection;
/**
* Registers a projection that can be used from clients just like a normal collection.
*
* @param name name of the projection
* @param collection name of the backing collection
* @param fields field whitelist for the projection
*/
addProjection(name: string, collection: string, fields: ProjectionFields): void;
listen(stream: any): void;
close(callback?: (err: Error) => any): void;
use(action: sharedb.UseAction, fn: sharedb.UseCallback): void;
close(callback?: (err?: Error) => any): void;
/**
* Registers a server middleware function.
*
* @param action name of an action from https://github.com/share/sharedb#middlewares
* @param fn listener invoked when the specified action occurs
*/
use<A extends keyof middleware.ActionContextMap>(
action: A,
fn: (context: middleware.ActionContextMap[A], callback: (err?: any) => void) => void,
): void;
static types: {
register: (type: { name?: string, uri?: string, [key: string]: any}) => void;
};
}
declare namespace sharedb {
type UseAction = 'connect' | 'op' | 'doc' | 'query' | 'submit' | 'apply' | 'commit' | 'after submit' | 'receive';
type UseCallback = ((request: {action: UseAction, agent: any, req: any, collection: string, id: string, query: any, op: ShareDB.RawOp}, callback: () => void) => void);
abstract class DB {
projectsSnapshots: boolean;
disableSubscribe: boolean;
@@ -100,3 +115,108 @@ declare namespace sharedb {
type Path = ShareDB.Path;
type ShareDBSourceOptions = ShareDB.ShareDBSourceOptions;
}
declare namespace middleware {
interface ActionContextMap {
afterSubmit: SubmitContext;
apply: ApplyContext;
commit: CommitContext;
connect: ConnectContext;
doc: DocContext; // Deprecated, use 'readSnapshots' instead.
op: OpContext;
query: QueryContext;
readSnapshots: ReadSnapshotsContext;
receive: ReceiveContext;
reply: ReplyContext;
submit: SubmitContext;
}
interface BaseContext {
action: keyof ActionContextMap;
agent: any;
backend: sharedb;
}
interface ApplyContext extends BaseContext, SubmitRequest {
}
interface CommitContext extends BaseContext, SubmitRequest {
}
interface ConnectContext extends BaseContext {
stream: any;
req: any; // Property always exists, value may be undefined
}
interface DocContext extends BaseContext {
collection: string;
id: string;
snapshot: ShareDB.Snapshot;
}
interface OpContext extends BaseContext {
collection: string;
id: string;
op: ShareDB.Op;
}
interface QueryContext extends BaseContext {
index: string;
collection: string;
projection: Projection | undefined;
fields: ProjectionFields | undefined;
channel: string;
query: ShareDB.JSONObject;
options: ShareDB.JSONObject;
db: sharedb.DB | null;
snapshotProjection: Projection | null;
}
interface ReadSnapshotsContext extends BaseContext {
collection: string;
snapshots: ShareDB.Snapshot[];
snapshotType: SnapshotType;
}
interface ReceiveContext extends BaseContext {
data: ShareDB.JSONObject; // ClientRequest, but before any validation
}
interface ReplyContext extends BaseContext {
request: ShareDB.ClientRequest;
reply: ShareDB.JSONObject;
}
type SnapshotType = 'current' | 'byVersion' | 'byTimestamp';
interface SubmitContext extends BaseContext, SubmitRequest {
}
}
interface Projection {
target: string;
fields: ProjectionFields;
}
interface ProjectionFields {
[propertyName: string]: true;
}
interface SubmitRequest {
index: string;
projection: Projection | undefined;
collection: string;
id: string;
op: sharedb.Op;
options: any;
start: number;
saveMilestoneSnapshot: boolean | null;
suppressPublish: boolean | null;
maxRetries: number | null;
retries: number;
snapshot: ShareDB.Snapshot | null;
ops: ShareDB.Op[];
channels: string[] | null;
}

View File

@@ -1,7 +1,26 @@
import { EventEmitter } from 'events';
export type JSONValue = string | number | boolean | null | JSONObject | JSONArray;
export interface JSONObject {
[name: string]: JSONValue;
}
export interface JSONArray extends Array<JSONValue> {}
export type Path = ReadonlyArray<string|number>;
export type Snapshot = number;
export interface Snapshot {
id: string;
v: number;
type: string | null;
data: JSONObject | undefined;
m: SnapshotMeta | null;
}
export interface SnapshotMeta {
ctime: number;
mtime: number;
// Users can use server middleware to add additional metadata to snapshots.
[key: string]: JSONValue;
}
export interface AddNumOp { p: Path; na: number; }
@@ -83,3 +102,12 @@ export class Query extends EventEmitter {
removeListener(event: QueryEvent, listener: (...args: any[]) => any): this;
destroy(): void;
}
export type RequestAction = 'qf' | 'qs' | 'qu' | 'bf' | 'bs' | 'bu' | 'f' | 's' | 'u' | 'op' | 'nf' | 'nt';
export interface ClientRequest {
/** Short name of the request's action */
a: RequestAction;
[propertyName: string]: JSONValue;
}

View File

@@ -30,7 +30,104 @@ class WebSocketJSONStream extends Duplex {
}
const backend = new ShareDB();
backend.addProjection('notes_minimal', 'notes', {title: true, creator: true, lastUpdateTime: true});
// Exercise middleware (backend.use)
type SubmitRelatedActions = 'afterSubmit' | 'apply' | 'commit' | 'submit';
const submitRelatedActions: SubmitRelatedActions[] = ['afterSubmit', 'apply', 'commit', 'submit'];
for (const action of submitRelatedActions) {
backend.use(action, (request, callback) => {
console.log(
request.action,
request.agent,
request.backend,
request.index,
request.collection,
request.projection,
request.id,
request.op,
request.options,
request.snapshot,
request.ops,
request.channels,
);
callback();
});
}
backend.use('connect', (context, callback) => {
console.log(
context.action,
context.agent,
context.backend,
context.stream,
context.req,
);
callback();
});
backend.use('op', (context, callback) => {
console.log(
context.action,
context.agent,
context.backend,
context.collection,
context.id,
context.op,
);
callback();
});
backend.use('query', (context, callback) => {
console.log(
context.action,
context.agent,
context.backend,
context.index,
context.collection,
context.projection,
context.fields,
context.channel,
context.query,
context.options,
context.snapshotProjection,
);
callback();
});
backend.use('receive', (context, callback) => {
console.log(
context.action,
context.agent,
context.backend,
context.data,
);
callback();
});
backend.use('reply', (context, callback) => {
console.log(
context.action,
context.agent,
context.backend,
context.request,
context.reply,
);
callback();
});
backend.use('readSnapshots', (context, callback) => {
console.log(
context.action,
context.agent,
context.backend,
context.collection,
context.snapshots,
context.snapshotType,
);
callback();
});
const connection = backend.connect();
const netRequest = {}; // Passed through to 'connect' middleware, not used by sharedb itself
const connectionWithReq = backend.connect(null, netRequest);
const reboundConnection = backend.connect(backend.connect(), netRequest);
const doc = connection.get('examples', 'counter');
doc.fetch((err) => {