@hocuspocus/extension-webhook 1.0.0-alpha.34

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (58) hide show
  1. package/CHANGELOG.md +272 -0
  2. package/LICENSE.md +21 -0
  3. package/dist/demos/backend/src/create-document.d.ts +1 -0
  4. package/dist/demos/backend/src/express.d.ts +1 -0
  5. package/dist/demos/backend/src/koa.d.ts +1 -0
  6. package/dist/demos/backend/src/minimal.d.ts +1 -0
  7. package/dist/demos/backend/src/monitor.d.ts +1 -0
  8. package/dist/demos/backend/src/redis.d.ts +1 -0
  9. package/dist/demos/backend/src/slow.d.ts +1 -0
  10. package/dist/demos/backend/src/webhook.d.ts +1 -0
  11. package/dist/hocuspocus-webhook.cjs +158 -0
  12. package/dist/hocuspocus-webhook.cjs.map +1 -0
  13. package/dist/hocuspocus-webhook.esm.js +150 -0
  14. package/dist/hocuspocus-webhook.esm.js.map +1 -0
  15. package/dist/packages/logger/src/index.d.ts +13 -0
  16. package/dist/packages/monitor/src/Collector.d.ts +62 -0
  17. package/dist/packages/monitor/src/Dashboard.d.ts +29 -0
  18. package/dist/packages/monitor/src/Storage.d.ts +35 -0
  19. package/dist/packages/monitor/src/index.d.ts +38 -0
  20. package/dist/packages/provider/src/EventEmitter.d.ts +9 -0
  21. package/dist/packages/provider/src/HocuspocusProvider.d.ts +158 -0
  22. package/dist/packages/provider/src/IncomingMessage.d.ts +14 -0
  23. package/dist/packages/provider/src/MessageReceiver.d.ts +13 -0
  24. package/dist/packages/provider/src/MessageSender.d.ts +10 -0
  25. package/dist/packages/provider/src/OutgoingMessage.d.ts +9 -0
  26. package/dist/packages/provider/src/OutgoingMessages/AuthenticationMessage.d.ts +7 -0
  27. package/dist/packages/provider/src/OutgoingMessages/AwarenessMessage.d.ts +8 -0
  28. package/dist/packages/provider/src/OutgoingMessages/QueryAwarenessMessage.d.ts +8 -0
  29. package/dist/packages/provider/src/OutgoingMessages/SyncStepOneMessage.d.ts +8 -0
  30. package/dist/packages/provider/src/OutgoingMessages/SyncStepTwoMessage.d.ts +8 -0
  31. package/dist/packages/provider/src/OutgoingMessages/UpdateMessage.d.ts +7 -0
  32. package/dist/packages/provider/src/index.d.ts +3 -0
  33. package/dist/packages/provider/src/types.d.ts +34 -0
  34. package/dist/packages/provider/src/utils/awarenessStatesToArray.d.ts +4 -0
  35. package/dist/packages/provider/src/utils/index.d.ts +1 -0
  36. package/dist/packages/redis/src/Redis.d.ts +16 -0
  37. package/dist/packages/redis/src/RedisCluster.d.ts +4 -0
  38. package/dist/packages/redis/src/index.d.ts +2 -0
  39. package/dist/packages/rocksdb/src/index.d.ts +22 -0
  40. package/dist/packages/server/src/CloseEvents.d.ts +4 -0
  41. package/dist/packages/server/src/Connection.d.ts +62 -0
  42. package/dist/packages/server/src/Debugger.d.ts +15 -0
  43. package/dist/packages/server/src/Document.d.ts +75 -0
  44. package/dist/packages/server/src/Hocuspocus.d.ts +86 -0
  45. package/dist/packages/server/src/IncomingMessage.d.ts +19 -0
  46. package/dist/packages/server/src/MessageReceiver.d.ts +10 -0
  47. package/dist/packages/server/src/OutgoingMessage.d.ts +16 -0
  48. package/dist/packages/server/src/index.d.ts +2 -0
  49. package/dist/packages/server/src/types.d.ts +133 -0
  50. package/dist/packages/throttle/src/index.d.ts +20 -0
  51. package/dist/packages/transformer/src/Prosemirror.d.ts +11 -0
  52. package/dist/packages/transformer/src/Tiptap.d.ts +10 -0
  53. package/dist/packages/transformer/src/index.d.ts +3 -0
  54. package/dist/packages/transformer/src/types.d.ts +5 -0
  55. package/dist/packages/webhook/src/index.d.ts +59 -0
  56. package/dist/shared/protocols/auth.d.ts +6 -0
  57. package/package.json +32 -0
  58. package/src/index.ts +197 -0
@@ -0,0 +1,19 @@
1
+ import { Decoder } from 'lib0/decoding';
2
+ import { Encoder } from 'lib0/encoding';
3
+ import { MessageType } from './types';
4
+ export declare class IncomingMessage {
5
+ /**
6
+ * Access to the received message.
7
+ */
8
+ decoder: Decoder;
9
+ /**
10
+ * Access to the reply.
11
+ */
12
+ encoder: Encoder;
13
+ constructor(input: any);
14
+ readVarUint8Array(): Uint8Array;
15
+ readVarUint(): number;
16
+ toUint8Array(): Uint8Array;
17
+ writeVarUint(type: MessageType): void;
18
+ get length(): number;
19
+ }
@@ -0,0 +1,10 @@
1
+ import Connection from './Connection';
2
+ import { IncomingMessage } from './IncomingMessage';
3
+ import { MessageLogger } from './Debugger';
4
+ export declare class MessageReceiver {
5
+ message: IncomingMessage;
6
+ debugger: MessageLogger;
7
+ constructor(message: IncomingMessage);
8
+ apply(connection: Connection): void;
9
+ readSyncMessage(message: IncomingMessage, connection: Connection): 0 | 1 | 2;
10
+ }
@@ -0,0 +1,16 @@
1
+ import { Encoder } from 'lib0/encoding';
2
+ import { Awareness } from 'y-protocols/awareness';
3
+ import Document from './Document';
4
+ export declare class OutgoingMessage {
5
+ encoder: Encoder;
6
+ type?: number;
7
+ category?: string;
8
+ constructor();
9
+ createSyncMessage(): OutgoingMessage;
10
+ createAwarenessUpdateMessage(awareness: Awareness, changedClients?: Array<any>): OutgoingMessage;
11
+ writeAuthenticated(): OutgoingMessage;
12
+ writePermissionDenied(reason: string): OutgoingMessage;
13
+ writeFirstSyncStepFor(document: Document): OutgoingMessage;
14
+ writeUpdate(update: Uint8Array): OutgoingMessage;
15
+ toUint8Array(): Uint8Array;
16
+ }
@@ -0,0 +1,2 @@
1
+ export * from './Hocuspocus';
2
+ export * from './types';
@@ -0,0 +1,133 @@
1
+ /// <reference types="node" />
2
+ import { IncomingHttpHeaders, IncomingMessage, ServerResponse } from 'http';
3
+ import { URLSearchParams } from 'url';
4
+ import { Socket } from 'net';
5
+ import Document from './Document';
6
+ import { Hocuspocus } from './Hocuspocus';
7
+ export declare enum MessageType {
8
+ Unknown = -1,
9
+ Sync = 0,
10
+ Awareness = 1,
11
+ Auth = 2
12
+ }
13
+ /**
14
+ * State of the WebSocket connection.
15
+ * https://developer.mozilla.org/de/docs/Web/API/WebSocket/readyState
16
+ */
17
+ export declare enum WsReadyStates {
18
+ Connecting = 0,
19
+ Open = 1,
20
+ Closing = 2,
21
+ Closed = 3
22
+ }
23
+ export interface AwarenessUpdate {
24
+ added: Array<any>;
25
+ updated: Array<any>;
26
+ removed: Array<any>;
27
+ }
28
+ export interface ConnectionConfig {
29
+ readOnly: boolean;
30
+ isAuthenticated: boolean;
31
+ }
32
+ export interface Extension {
33
+ onAuthenticate?(data: onAuthenticatePayload): Promise<any>;
34
+ onChange?(data: onChangePayload): Promise<any>;
35
+ onConnect?(data: onConnectPayload): Promise<any>;
36
+ onConfigure?(data: onConfigurePayload): Promise<any>;
37
+ onCreateDocument?(data: onCreateDocumentPayload): Promise<any>;
38
+ onDestroy?(data: onDestroyPayload): Promise<any>;
39
+ onDisconnect?(data: onDisconnectPayload): Promise<any>;
40
+ onListen?(data: onListenPayload): Promise<any>;
41
+ onRequest?(data: onRequestPayload): Promise<any>;
42
+ onUpgrade?(data: onUpgradePayload): Promise<any>;
43
+ }
44
+ export interface Configuration extends Extension {
45
+ /**
46
+ * A list of hocuspocus extenions.
47
+ */
48
+ extensions: Array<Extension>;
49
+ /**
50
+ * The port which the server listens on.
51
+ */
52
+ port: number | null;
53
+ /**
54
+ * Defines in which interval the server sends a ping, and closes the connection when no pong is sent back.
55
+ */
56
+ timeout: number;
57
+ }
58
+ export interface onAuthenticatePayload {
59
+ documentName: string;
60
+ instance: Hocuspocus;
61
+ requestHeaders: IncomingHttpHeaders;
62
+ requestParameters: URLSearchParams;
63
+ socketId: string;
64
+ token: string;
65
+ connection: ConnectionConfig;
66
+ }
67
+ export interface onConnectPayload {
68
+ documentName: string;
69
+ instance: Hocuspocus;
70
+ request: IncomingMessage;
71
+ requestHeaders: IncomingHttpHeaders;
72
+ requestParameters: URLSearchParams;
73
+ socketId: string;
74
+ connection: ConnectionConfig;
75
+ }
76
+ export interface onCreateDocumentPayload {
77
+ context: any;
78
+ document: Document;
79
+ documentName: string;
80
+ instance: Hocuspocus;
81
+ requestHeaders: IncomingHttpHeaders;
82
+ requestParameters: URLSearchParams;
83
+ socketId: string;
84
+ connection: ConnectionConfig;
85
+ }
86
+ export interface onChangePayload {
87
+ clientsCount: number;
88
+ context: any;
89
+ document: Document;
90
+ documentName: string;
91
+ instance: Hocuspocus;
92
+ requestHeaders: IncomingHttpHeaders;
93
+ requestParameters: URLSearchParams;
94
+ update: Uint8Array;
95
+ socketId: string;
96
+ }
97
+ export interface onDisconnectPayload {
98
+ clientsCount: number;
99
+ context: any;
100
+ document: Document;
101
+ documentName: string;
102
+ instance: Hocuspocus;
103
+ requestHeaders: IncomingHttpHeaders;
104
+ requestParameters: URLSearchParams;
105
+ socketId: string;
106
+ }
107
+ export interface onRequestPayload {
108
+ request: IncomingMessage;
109
+ response: ServerResponse;
110
+ instance: Hocuspocus;
111
+ }
112
+ export interface onUpgradePayload {
113
+ head: any;
114
+ request: IncomingMessage;
115
+ socket: Socket;
116
+ instance: Hocuspocus;
117
+ }
118
+ export interface onListenPayload {
119
+ port: number;
120
+ }
121
+ export interface onDestroyPayload {
122
+ instance: Hocuspocus;
123
+ }
124
+ export interface onConfigurePayload {
125
+ configuration: Configuration;
126
+ version: string;
127
+ yjsVersion: string;
128
+ instance: Hocuspocus;
129
+ }
130
+ export interface CloseEvent {
131
+ code: number;
132
+ reason: string;
133
+ }
@@ -0,0 +1,20 @@
1
+ import { Extension, onConnectPayload } from '@hocuspocus/server';
2
+ export interface Configuration {
3
+ throttle: number | null | false;
4
+ banTime: number;
5
+ }
6
+ export declare class Throttle implements Extension {
7
+ configuration: Configuration;
8
+ connectionsByIp: Map<string, Array<number>>;
9
+ bannedIps: Map<string, number>;
10
+ /**
11
+ * Throttle requests
12
+ * @private
13
+ */
14
+ private throttle;
15
+ /**
16
+ * onConnect hook
17
+ * @param data
18
+ */
19
+ onConnect(data: onConnectPayload): Promise<any>;
20
+ }
@@ -0,0 +1,11 @@
1
+ import { Doc } from 'yjs';
2
+ import { Schema } from 'prosemirror-model';
3
+ import { Transformer } from './types';
4
+ declare class Prosemirror implements Transformer {
5
+ defaultSchema: Schema;
6
+ schema(schema: Schema): Prosemirror;
7
+ fromYdoc(document: Doc, fieldName?: string | Array<string>): any;
8
+ toYdoc(document: any, fieldName?: string | Array<string>, schema?: Schema): Doc;
9
+ }
10
+ export declare const ProsemirrorTransformer: Prosemirror;
11
+ export {};
@@ -0,0 +1,10 @@
1
+ import { Doc } from 'yjs';
2
+ import { Extensions } from '@tiptap/core/dist/packages/core/src/types';
3
+ import { Transformer } from './types';
4
+ export declare class Tiptap implements Transformer {
5
+ defaultExtensions: Extensions;
6
+ extensions(extensions: Extensions): Tiptap;
7
+ fromYdoc(document: Doc, fieldName?: string | Array<string>): any;
8
+ toYdoc(document: any, fieldName?: string | Array<string>, extensions?: Extensions): Doc;
9
+ }
10
+ export declare const TiptapTransformer: Tiptap;
@@ -0,0 +1,3 @@
1
+ export * from './Prosemirror';
2
+ export * from './Tiptap';
3
+ export * from './types';
@@ -0,0 +1,5 @@
1
+ import { Doc } from 'yjs';
2
+ export interface Transformer {
3
+ fromYdoc: (document: Doc, fieldName?: string | Array<string>) => any;
4
+ toYdoc: (document: any, fieldName: string) => Doc;
5
+ }
@@ -0,0 +1,59 @@
1
+ /// <reference types="node" />
2
+ import { Extension, onChangePayload, onConnectPayload, onCreateDocumentPayload, onDisconnectPayload } from '@hocuspocus/server';
3
+ import { Doc } from 'yjs';
4
+ import { Transformer } from '@hocuspocus/transformer';
5
+ import { AxiosResponse } from 'axios';
6
+ import Timeout = NodeJS.Timeout;
7
+ export declare enum Events {
8
+ onChange = "change",
9
+ onConnect = "connect",
10
+ onCreate = "create",
11
+ onDisconnect = "disconnect"
12
+ }
13
+ export interface Configuration {
14
+ debounce: number | false | null;
15
+ debounceMaxWait: number;
16
+ secret: string;
17
+ transformer: Transformer | {
18
+ toYdoc: (document: any) => Doc;
19
+ fromYdoc: (document: Doc) => any;
20
+ };
21
+ url: string;
22
+ events: Array<Events>;
23
+ }
24
+ export declare class Webhook implements Extension {
25
+ configuration: Configuration;
26
+ debounced: Map<string, {
27
+ timeout: Timeout;
28
+ start: number;
29
+ }>;
30
+ /**
31
+ * Constructor
32
+ */
33
+ constructor(configuration?: Partial<Configuration>);
34
+ /**
35
+ * Create a signature for the response body
36
+ */
37
+ createSignature(body: string): string;
38
+ /**
39
+ * debounce the given function, using the given identifier
40
+ */
41
+ debounce(id: string, func: Function): void;
42
+ /**
43
+ * Send a request to the given url containing the given data
44
+ */
45
+ sendRequest(event: Events, payload: any): Promise<AxiosResponse<any>>;
46
+ /**
47
+ * onChange hook
48
+ */
49
+ onChange(data: onChangePayload): Promise<void>;
50
+ /**
51
+ * onCreateDocument hook
52
+ */
53
+ onCreateDocument(data: onCreateDocumentPayload): Promise<void>;
54
+ /**
55
+ * onConnect hook
56
+ */
57
+ onConnect(data: onConnectPayload): Promise<any>;
58
+ onDisconnect(data: onDisconnectPayload): Promise<void>;
59
+ }
@@ -0,0 +1,6 @@
1
+ import * as encoding from 'lib0/encoding';
2
+ import * as decoding from 'lib0/decoding';
3
+ export declare const writeAuthentication: (encoder: encoding.Encoder, auth: string) => void;
4
+ export declare const writePermissionDenied: (encoder: encoding.Encoder, reason: string) => void;
5
+ export declare const writeAuthenticated: (encoder: encoding.Encoder) => void;
6
+ export declare const readAuthMessage: (decoder: decoding.Decoder, permissionDeniedHandler: (reason: string) => void, authenticatedHandler: () => void) => void;
package/package.json ADDED
@@ -0,0 +1,32 @@
1
+ {
2
+ "name": "@hocuspocus/extension-webhook",
3
+ "version": "1.0.0-alpha.34",
4
+ "description": "hocuspocus webhook extension",
5
+ "homepage": "https://hocuspocus.dev",
6
+ "keywords": [
7
+ "hocuspocus",
8
+ "webhook",
9
+ "api",
10
+ "rest",
11
+ "yjs"
12
+ ],
13
+ "license": "MIT",
14
+ "type": "module",
15
+ "main": "dist/hocuspocus-webhook.esm.js",
16
+ "module": "dist/hocuspocus-webhook.esm.js",
17
+ "types": "dist/packages/webhook/src/index.d.ts",
18
+ "exports": {
19
+ "import": "./dist/hocuspocus-webhook.esm.js",
20
+ "require": "./dist/hocuspocus-webhook.cjs"
21
+ },
22
+ "files": [
23
+ "src",
24
+ "dist"
25
+ ],
26
+ "dependencies": {
27
+ "@hocuspocus/server": "^1.0.0-alpha.69",
28
+ "@hocuspocus/transformer": "^1.0.0-alpha.14",
29
+ "axios": "^0.21.1"
30
+ },
31
+ "gitHead": "d84b5516f65dfe096fd6c633f96309a6f65a9811"
32
+ }
package/src/index.ts ADDED
@@ -0,0 +1,197 @@
1
+ import { createHmac } from 'crypto'
2
+ import {
3
+ Extension,
4
+ onChangePayload,
5
+ onConnectPayload,
6
+ onCreateDocumentPayload,
7
+ onDisconnectPayload,
8
+ } from '@hocuspocus/server'
9
+ import { Doc } from 'yjs'
10
+ import { TiptapTransformer, Transformer } from '@hocuspocus/transformer'
11
+ import axios, { AxiosResponse } from 'axios'
12
+ import Timeout = NodeJS.Timeout
13
+
14
+ export enum Events {
15
+ onChange = 'change',
16
+ onConnect = 'connect',
17
+ onCreate = 'create',
18
+ onDisconnect = 'disconnect',
19
+ }
20
+
21
+ export interface Configuration {
22
+ debounce: number | false | null,
23
+ debounceMaxWait: number,
24
+ secret: string,
25
+ transformer: Transformer | {
26
+ toYdoc: (document: any) => Doc,
27
+ fromYdoc: (document: Doc) => any,
28
+ },
29
+ url: string,
30
+ events: Array<Events>,
31
+ }
32
+
33
+ export class Webhook implements Extension {
34
+
35
+ configuration: Configuration = {
36
+ debounce: 2000,
37
+ debounceMaxWait: 10000,
38
+ secret: '',
39
+ transformer: TiptapTransformer,
40
+ url: '',
41
+ events: [
42
+ Events.onChange,
43
+ ],
44
+ }
45
+
46
+ debounced: Map<string, { timeout: Timeout, start: number }> = new Map()
47
+
48
+ /**
49
+ * Constructor
50
+ */
51
+ constructor(configuration?: Partial<Configuration>) {
52
+ this.configuration = {
53
+ ...this.configuration,
54
+ ...configuration,
55
+ }
56
+
57
+ if (!this.configuration.url) {
58
+ throw new Error('url is required!')
59
+ }
60
+ }
61
+
62
+ /**
63
+ * Create a signature for the response body
64
+ */
65
+ createSignature(body: string): string {
66
+ const hmac = createHmac('sha256', this.configuration.secret)
67
+
68
+ return `sha256=${hmac.update(body).digest('hex')}`
69
+ }
70
+
71
+ /**
72
+ * debounce the given function, using the given identifier
73
+ */
74
+ debounce(id: string, func: Function) {
75
+ const old = this.debounced.get(id)
76
+ const start = old?.start || Date.now()
77
+
78
+ const run = () => {
79
+ this.debounced.delete(id)
80
+ func()
81
+ }
82
+
83
+ if (old?.timeout) clearTimeout(old.timeout)
84
+ if (Date.now() - start >= this.configuration.debounceMaxWait) return run()
85
+
86
+ this.debounced.set(id, {
87
+ start,
88
+ timeout: setTimeout(run, <number> this.configuration.debounce),
89
+ })
90
+ }
91
+
92
+ /**
93
+ * Send a request to the given url containing the given data
94
+ */
95
+ async sendRequest(event: Events, payload: any) {
96
+ const json = JSON.stringify({ event, payload })
97
+
98
+ return axios.post(
99
+ this.configuration.url,
100
+ json,
101
+ { headers: { 'X-Hocuspocus-Signature-256': this.createSignature(json) } },
102
+ )
103
+ }
104
+
105
+ /**
106
+ * onChange hook
107
+ */
108
+ async onChange(data: onChangePayload) {
109
+ if (!this.configuration.events.includes(Events.onChange)) {
110
+ return
111
+ }
112
+
113
+ const save = () => {
114
+ this.sendRequest(Events.onChange, {
115
+ document: this.configuration.transformer.fromYdoc(data.document),
116
+ documentName: data.documentName,
117
+ context: data.context,
118
+ requestHeaders: data.requestHeaders,
119
+ requestParameters: Object.fromEntries(data.requestParameters.entries()),
120
+ })
121
+ }
122
+
123
+ if (!this.configuration.debounce) {
124
+ return save()
125
+ }
126
+
127
+ this.debounce(data.documentName, save)
128
+ }
129
+
130
+ /**
131
+ * onCreateDocument hook
132
+ */
133
+ async onCreateDocument(data: onCreateDocumentPayload) {
134
+ if (!this.configuration.events.includes(Events.onCreate)) {
135
+ return
136
+ }
137
+
138
+ const response = <AxiosResponse> await this.sendRequest(Events.onCreate, {
139
+ documentName: data.documentName,
140
+ requestHeaders: data.requestHeaders,
141
+ requestParameters: Object.fromEntries(data.requestParameters.entries()),
142
+ })
143
+
144
+ if (response.status !== 200 || !response.data) return
145
+
146
+ const document = typeof response.data === 'string'
147
+ ? JSON.parse(response.data)
148
+ : response.data
149
+
150
+ // eslint-disable-next-line guard-for-in,no-restricted-syntax
151
+ for (const fieldName in document) {
152
+ if (data.document.isEmpty(fieldName)) {
153
+ data.document.merge(
154
+ this.configuration.transformer.toYdoc(document[fieldName], fieldName),
155
+ )
156
+ }
157
+ }
158
+ }
159
+
160
+ /**
161
+ * onConnect hook
162
+ */
163
+ async onConnect(data: onConnectPayload) {
164
+ if (!this.configuration.events.includes(Events.onConnect)) {
165
+ return
166
+ }
167
+
168
+ try {
169
+ const response = <AxiosResponse> await this.sendRequest(Events.onConnect, {
170
+ documentName: data.documentName,
171
+ requestHeaders: data.requestHeaders,
172
+ requestParameters: Object.fromEntries(data.requestParameters.entries()),
173
+ })
174
+
175
+ return typeof response.data === 'string'
176
+ ? JSON.parse(response.data)
177
+ : response.data
178
+ } catch (e) {
179
+ // eslint-disable-next-line no-throw-literal
180
+ throw null
181
+ }
182
+ }
183
+
184
+ async onDisconnect(data: onDisconnectPayload) {
185
+ if (!this.configuration.events.includes(Events.onConnect)) {
186
+ return
187
+ }
188
+
189
+ await this.sendRequest(Events.onDisconnect, {
190
+ documentName: data.documentName,
191
+ requestHeaders: data.requestHeaders,
192
+ requestParameters: Object.fromEntries(data.requestParameters.entries()),
193
+ context: data.context,
194
+ })
195
+ }
196
+
197
+ }