@hocuspocus/extension-webhook 2.1.0-alpha.0 → 2.1.0
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.
- package/dist/hocuspocus-webhook.cjs +157 -157
- package/dist/hocuspocus-webhook.esm.js +157 -157
- package/dist/packages/common/src/CloseEvents.d.ts +29 -29
- package/dist/packages/common/src/auth.d.ts +6 -6
- package/dist/packages/common/src/awarenessStatesToArray.d.ts +3 -3
- package/dist/packages/common/src/index.d.ts +4 -4
- package/dist/packages/common/src/types.d.ts +10 -10
- package/dist/packages/extension-database/src/Database.d.ts +30 -30
- package/dist/packages/extension-database/src/index.d.ts +1 -1
- package/dist/packages/extension-logger/src/Logger.d.ts +67 -67
- package/dist/packages/extension-logger/src/index.d.ts +1 -1
- package/dist/packages/extension-redis/src/Redis.d.ts +116 -95
- package/dist/packages/extension-redis/src/index.d.ts +1 -1
- package/dist/packages/extension-sqlite/src/SQLite.d.ts +26 -26
- package/dist/packages/extension-sqlite/src/index.d.ts +1 -1
- package/dist/packages/extension-throttle/src/index.d.ts +31 -31
- package/dist/packages/extension-webhook/src/index.d.ts +57 -57
- package/dist/packages/provider/src/EventEmitter.d.ts +9 -9
- package/dist/packages/provider/src/HocuspocusProvider.d.ts +110 -110
- package/dist/packages/provider/src/HocuspocusProviderWebsocket.d.ts +115 -115
- package/dist/packages/provider/src/IncomingMessage.d.ts +16 -16
- package/dist/packages/provider/src/MessageReceiver.d.ts +13 -13
- package/dist/packages/provider/src/MessageSender.d.ts +10 -10
- package/dist/packages/provider/src/OutgoingMessage.d.ts +9 -9
- package/dist/packages/provider/src/OutgoingMessages/AuthenticationMessage.d.ts +7 -7
- package/dist/packages/provider/src/OutgoingMessages/AwarenessMessage.d.ts +8 -8
- package/dist/packages/provider/src/OutgoingMessages/CloseMessage.d.ts +8 -8
- package/dist/packages/provider/src/OutgoingMessages/QueryAwarenessMessage.d.ts +8 -8
- package/dist/packages/provider/src/OutgoingMessages/StatelessMessage.d.ts +7 -7
- package/dist/packages/provider/src/OutgoingMessages/SyncStepOneMessage.d.ts +8 -8
- package/dist/packages/provider/src/OutgoingMessages/SyncStepTwoMessage.d.ts +8 -8
- package/dist/packages/provider/src/OutgoingMessages/UpdateMessage.d.ts +7 -7
- package/dist/packages/provider/src/TiptapCollabProvider.d.ts +11 -11
- package/dist/packages/provider/src/TiptapCollabProviderWebsocket.d.ts +11 -11
- package/dist/packages/provider/src/index.d.ts +5 -5
- package/dist/packages/provider/src/types.d.ts +84 -84
- package/dist/packages/server/src/Connection.d.ts +71 -71
- package/dist/packages/server/src/Debugger.d.ts +14 -14
- package/dist/packages/server/src/DirectConnection.d.ts +13 -0
- package/dist/packages/server/src/Document.d.ts +91 -88
- package/dist/packages/server/src/Hocuspocus.d.ts +111 -108
- package/dist/packages/server/src/IncomingMessage.d.ts +21 -21
- package/dist/packages/server/src/MessageReceiver.d.ts +12 -12
- package/dist/packages/server/src/OutgoingMessage.d.ts +20 -20
- package/dist/packages/server/src/index.d.ts +8 -8
- package/dist/packages/server/src/types.d.ts +266 -264
- package/dist/packages/transformer/src/Prosemirror.d.ts +11 -11
- package/dist/packages/transformer/src/Tiptap.d.ts +10 -10
- package/dist/packages/transformer/src/index.d.ts +3 -3
- package/dist/packages/transformer/src/types.d.ts +5 -5
- package/dist/playground/backend/src/default.d.ts +1 -1
- package/dist/playground/backend/src/express.d.ts +1 -1
- package/dist/playground/backend/src/koa.d.ts +1 -1
- package/dist/playground/backend/src/load-document.d.ts +1 -1
- package/dist/playground/backend/src/redis.d.ts +1 -1
- package/dist/playground/backend/src/slow.d.ts +1 -1
- package/dist/playground/backend/src/tiptapcollab.d.ts +1 -1
- package/dist/playground/backend/src/webhook.d.ts +1 -1
- package/dist/playground/frontend/src/main.d.ts +1 -1
- package/dist/playground/frontend/vite.config.d.ts +2 -2
- package/dist/tests/extension-database/fetch.d.ts +1 -1
- package/dist/tests/extension-logger/onListen.d.ts +1 -1
- package/dist/tests/extension-redis/closeConnections.d.ts +1 -1
- package/dist/tests/extension-redis/getConnectionCount.d.ts +1 -1
- package/dist/tests/extension-redis/getDocumentsCount.d.ts +1 -1
- package/dist/tests/extension-redis/onAwarenessChange.d.ts +1 -1
- package/dist/tests/extension-redis/onChange.d.ts +1 -1
- package/dist/tests/extension-redis/onStateless.d.ts +1 -1
- package/dist/tests/extension-redis/onStoreDocument.d.ts +1 -1
- package/dist/tests/extension-throttle/banning.d.ts +1 -1
- package/dist/tests/extension-throttle/configuration.d.ts +1 -1
- package/dist/tests/provider/observe.d.ts +1 -1
- package/dist/tests/provider/observeDeep.d.ts +1 -1
- package/dist/tests/provider/onAuthenticated.d.ts +1 -1
- package/dist/tests/provider/onAuthenticationFailed.d.ts +1 -1
- package/dist/tests/provider/onAwarenessChange.d.ts +1 -1
- package/dist/tests/provider/onAwarenessUpdate.d.ts +1 -1
- package/dist/tests/provider/onClose.d.ts +1 -1
- package/dist/tests/provider/onConnect.d.ts +1 -1
- package/dist/tests/provider/onDisconnect.d.ts +1 -1
- package/dist/tests/provider/onMessage.d.ts +1 -1
- package/dist/tests/provider/onOpen.d.ts +1 -1
- package/dist/tests/provider/onStateless.d.ts +1 -1
- package/dist/tests/provider/onSynced.d.ts +1 -1
- package/dist/tests/providerwebsocket/configuration.d.ts +1 -1
- package/dist/tests/server/address.d.ts +1 -1
- package/dist/tests/server/afterStoreDocument.d.ts +1 -1
- package/dist/tests/server/beforeBroadcastStateless.d.ts +1 -1
- package/dist/tests/server/beforeHandleMessage.d.ts +1 -1
- package/dist/tests/server/closeConnections.d.ts +1 -1
- package/dist/tests/server/getConnectionsCount.d.ts +1 -1
- package/dist/tests/server/getDocumentsCount.d.ts +1 -1
- package/dist/tests/server/getMessageLogs.d.ts +1 -1
- package/dist/tests/server/listen.d.ts +1 -1
- package/dist/tests/server/onAuthenticate.d.ts +1 -1
- package/dist/tests/server/onAwarenessUpdate.d.ts +1 -1
- package/dist/tests/server/onChange.d.ts +1 -1
- package/dist/tests/server/onClose.d.ts +1 -1
- package/dist/tests/server/onConfigure.d.ts +1 -1
- package/dist/tests/server/onConnect.d.ts +1 -1
- package/dist/tests/server/onDestroy.d.ts +1 -1
- package/dist/tests/server/onDisconnect.d.ts +1 -1
- package/dist/tests/server/onListen.d.ts +1 -1
- package/dist/tests/server/onLoadDocument.d.ts +1 -1
- package/dist/tests/server/onRequest.d.ts +1 -1
- package/dist/tests/server/onStateless.d.ts +1 -1
- package/dist/tests/server/onStoreDocument.d.ts +1 -1
- package/dist/tests/server/onUpgrade.d.ts +1 -1
- package/dist/tests/server/openDirectConnection.d.ts +1 -0
- package/dist/tests/server/requiresAuthentication.d.ts +1 -1
- package/dist/tests/server/websocketError.d.ts +1 -1
- package/dist/tests/transformer/TiptapTransformer.d.ts +1 -1
- package/dist/tests/utils/createDirectory.d.ts +1 -1
- package/dist/tests/utils/flushRedis.d.ts +1 -1
- package/dist/tests/utils/index.d.ts +9 -9
- package/dist/tests/utils/newHocuspocus.d.ts +2 -2
- package/dist/tests/utils/newHocuspocusProvider.d.ts +3 -3
- package/dist/tests/utils/newHocuspocusProviderWebsocket.d.ts +3 -3
- package/dist/tests/utils/randomInteger.d.ts +1 -1
- package/dist/tests/utils/redisConnectionSettings.d.ts +4 -4
- package/dist/tests/utils/removeDirectory.d.ts +1 -1
- package/dist/tests/utils/retryableAssertion.d.ts +2 -2
- package/dist/tests/utils/sleep.d.ts +1 -1
- package/package.json +6 -5
|
@@ -11,163 +11,163 @@ function _interopDefaultLegacy (e) { return e && typeof e === 'object' && 'defau
|
|
|
11
11
|
|
|
12
12
|
var axios__default = /*#__PURE__*/_interopDefaultLegacy(axios);
|
|
13
13
|
|
|
14
|
-
exports.Events = void 0;
|
|
15
|
-
(function (Events) {
|
|
16
|
-
Events["onChange"] = "change";
|
|
17
|
-
Events["onConnect"] = "connect";
|
|
18
|
-
Events["onCreate"] = "create";
|
|
19
|
-
Events["onDisconnect"] = "disconnect";
|
|
20
|
-
})(exports.Events || (exports.Events = {}));
|
|
21
|
-
class Webhook {
|
|
22
|
-
/**
|
|
23
|
-
* Constructor
|
|
24
|
-
*/
|
|
25
|
-
constructor(configuration) {
|
|
26
|
-
this.configuration = {
|
|
27
|
-
debounce: 2000,
|
|
28
|
-
debounceMaxWait: 10000,
|
|
29
|
-
secret: '',
|
|
30
|
-
transformer: transformer.TiptapTransformer,
|
|
31
|
-
url: '',
|
|
32
|
-
events: [
|
|
33
|
-
exports.Events.onChange,
|
|
34
|
-
],
|
|
35
|
-
};
|
|
36
|
-
this.debounced = new Map();
|
|
37
|
-
this.configuration = {
|
|
38
|
-
...this.configuration,
|
|
39
|
-
...configuration,
|
|
40
|
-
};
|
|
41
|
-
if (!this.configuration.url) {
|
|
42
|
-
throw new Error('url is required!');
|
|
43
|
-
}
|
|
44
|
-
}
|
|
45
|
-
/**
|
|
46
|
-
* Create a signature for the response body
|
|
47
|
-
*/
|
|
48
|
-
createSignature(body) {
|
|
49
|
-
const hmac = crypto.createHmac('sha256', this.configuration.secret);
|
|
50
|
-
return `sha256=${hmac.update(body).digest('hex')}`;
|
|
51
|
-
}
|
|
52
|
-
/**
|
|
53
|
-
* debounce the given function, using the given identifier
|
|
54
|
-
*/
|
|
55
|
-
debounce(id, func) {
|
|
56
|
-
const old = this.debounced.get(id);
|
|
57
|
-
const start = (old === null || old === void 0 ? void 0 : old.start) || Date.now();
|
|
58
|
-
const run = () => {
|
|
59
|
-
this.debounced.delete(id);
|
|
60
|
-
func();
|
|
61
|
-
};
|
|
62
|
-
if (old === null || old === void 0 ? void 0 : old.timeout)
|
|
63
|
-
clearTimeout(old.timeout);
|
|
64
|
-
if (Date.now() - start >= this.configuration.debounceMaxWait)
|
|
65
|
-
return run();
|
|
66
|
-
this.debounced.set(id, {
|
|
67
|
-
start,
|
|
68
|
-
timeout: setTimeout(run, this.configuration.debounce),
|
|
69
|
-
});
|
|
70
|
-
}
|
|
71
|
-
/**
|
|
72
|
-
* Send a request to the given url containing the given data
|
|
73
|
-
*/
|
|
74
|
-
async sendRequest(event, payload) {
|
|
75
|
-
const json = JSON.stringify({ event, payload });
|
|
76
|
-
return axios__default["default"].post(this.configuration.url, json, { headers: { 'X-Hocuspocus-Signature-256': this.createSignature(json), 'Content-Type': 'application/json' } });
|
|
77
|
-
}
|
|
78
|
-
/**
|
|
79
|
-
* onChange hook
|
|
80
|
-
*/
|
|
81
|
-
async onChange(data) {
|
|
82
|
-
if (!this.configuration.events.includes(exports.Events.onChange)) {
|
|
83
|
-
return;
|
|
84
|
-
}
|
|
85
|
-
const save = () => {
|
|
86
|
-
try {
|
|
87
|
-
this.sendRequest(exports.Events.onChange, {
|
|
88
|
-
document: this.configuration.transformer.fromYdoc(data.document),
|
|
89
|
-
documentName: data.documentName,
|
|
90
|
-
context: data.context,
|
|
91
|
-
requestHeaders: data.requestHeaders,
|
|
92
|
-
requestParameters: Object.fromEntries(data.requestParameters.entries()),
|
|
93
|
-
});
|
|
94
|
-
}
|
|
95
|
-
catch (e) {
|
|
96
|
-
console.error(`Caught error in extension-webhook: ${e}`);
|
|
97
|
-
}
|
|
98
|
-
};
|
|
99
|
-
if (!this.configuration.debounce) {
|
|
100
|
-
return save();
|
|
101
|
-
}
|
|
102
|
-
this.debounce(data.documentName, save);
|
|
103
|
-
}
|
|
104
|
-
/**
|
|
105
|
-
* onLoadDocument hook
|
|
106
|
-
*/
|
|
107
|
-
async onLoadDocument(data) {
|
|
108
|
-
if (!this.configuration.events.includes(exports.Events.onCreate)) {
|
|
109
|
-
return;
|
|
110
|
-
}
|
|
111
|
-
try {
|
|
112
|
-
const response = await this.sendRequest(exports.Events.onCreate, {
|
|
113
|
-
documentName: data.documentName,
|
|
114
|
-
requestHeaders: data.requestHeaders,
|
|
115
|
-
requestParameters: Object.fromEntries(data.requestParameters.entries()),
|
|
116
|
-
});
|
|
117
|
-
if (response.status !== 200 || !response.data)
|
|
118
|
-
return;
|
|
119
|
-
const document = typeof response.data === 'string'
|
|
120
|
-
? JSON.parse(response.data)
|
|
121
|
-
: response.data;
|
|
122
|
-
// eslint-disable-next-line guard-for-in,no-restricted-syntax
|
|
123
|
-
for (const fieldName in document) {
|
|
124
|
-
if (data.document.isEmpty(fieldName)) {
|
|
125
|
-
data.document.merge(this.configuration.transformer.toYdoc(document[fieldName], fieldName));
|
|
126
|
-
}
|
|
127
|
-
}
|
|
128
|
-
}
|
|
129
|
-
catch (e) {
|
|
130
|
-
console.error(`Caught error in extension-webhook: ${e}`);
|
|
131
|
-
}
|
|
132
|
-
}
|
|
133
|
-
/**
|
|
134
|
-
* onConnect hook
|
|
135
|
-
*/
|
|
136
|
-
async onConnect(data) {
|
|
137
|
-
if (!this.configuration.events.includes(exports.Events.onConnect)) {
|
|
138
|
-
return;
|
|
139
|
-
}
|
|
140
|
-
try {
|
|
141
|
-
const response = await this.sendRequest(exports.Events.onConnect, {
|
|
142
|
-
documentName: data.documentName,
|
|
143
|
-
requestHeaders: data.requestHeaders,
|
|
144
|
-
requestParameters: Object.fromEntries(data.requestParameters.entries()),
|
|
145
|
-
});
|
|
146
|
-
return typeof response.data === 'string' && response.data.length > 0
|
|
147
|
-
? JSON.parse(response.data)
|
|
148
|
-
: response.data;
|
|
149
|
-
}
|
|
150
|
-
catch (e) {
|
|
151
|
-
console.error(`Caught error in extension-webhook: ${e}`);
|
|
152
|
-
throw common.Forbidden;
|
|
153
|
-
}
|
|
154
|
-
}
|
|
155
|
-
async onDisconnect(data) {
|
|
156
|
-
if (!this.configuration.events.includes(exports.Events.onDisconnect)) {
|
|
157
|
-
return;
|
|
158
|
-
}
|
|
159
|
-
try {
|
|
160
|
-
await this.sendRequest(exports.Events.onDisconnect, {
|
|
161
|
-
documentName: data.documentName,
|
|
162
|
-
requestHeaders: data.requestHeaders,
|
|
163
|
-
requestParameters: Object.fromEntries(data.requestParameters.entries()),
|
|
164
|
-
context: data.context,
|
|
165
|
-
});
|
|
166
|
-
}
|
|
167
|
-
catch (e) {
|
|
168
|
-
console.error(`Caught error in extension-webhook: ${e}`);
|
|
169
|
-
}
|
|
170
|
-
}
|
|
14
|
+
exports.Events = void 0;
|
|
15
|
+
(function (Events) {
|
|
16
|
+
Events["onChange"] = "change";
|
|
17
|
+
Events["onConnect"] = "connect";
|
|
18
|
+
Events["onCreate"] = "create";
|
|
19
|
+
Events["onDisconnect"] = "disconnect";
|
|
20
|
+
})(exports.Events || (exports.Events = {}));
|
|
21
|
+
class Webhook {
|
|
22
|
+
/**
|
|
23
|
+
* Constructor
|
|
24
|
+
*/
|
|
25
|
+
constructor(configuration) {
|
|
26
|
+
this.configuration = {
|
|
27
|
+
debounce: 2000,
|
|
28
|
+
debounceMaxWait: 10000,
|
|
29
|
+
secret: '',
|
|
30
|
+
transformer: transformer.TiptapTransformer,
|
|
31
|
+
url: '',
|
|
32
|
+
events: [
|
|
33
|
+
exports.Events.onChange,
|
|
34
|
+
],
|
|
35
|
+
};
|
|
36
|
+
this.debounced = new Map();
|
|
37
|
+
this.configuration = {
|
|
38
|
+
...this.configuration,
|
|
39
|
+
...configuration,
|
|
40
|
+
};
|
|
41
|
+
if (!this.configuration.url) {
|
|
42
|
+
throw new Error('url is required!');
|
|
43
|
+
}
|
|
44
|
+
}
|
|
45
|
+
/**
|
|
46
|
+
* Create a signature for the response body
|
|
47
|
+
*/
|
|
48
|
+
createSignature(body) {
|
|
49
|
+
const hmac = crypto.createHmac('sha256', this.configuration.secret);
|
|
50
|
+
return `sha256=${hmac.update(body).digest('hex')}`;
|
|
51
|
+
}
|
|
52
|
+
/**
|
|
53
|
+
* debounce the given function, using the given identifier
|
|
54
|
+
*/
|
|
55
|
+
debounce(id, func) {
|
|
56
|
+
const old = this.debounced.get(id);
|
|
57
|
+
const start = (old === null || old === void 0 ? void 0 : old.start) || Date.now();
|
|
58
|
+
const run = () => {
|
|
59
|
+
this.debounced.delete(id);
|
|
60
|
+
func();
|
|
61
|
+
};
|
|
62
|
+
if (old === null || old === void 0 ? void 0 : old.timeout)
|
|
63
|
+
clearTimeout(old.timeout);
|
|
64
|
+
if (Date.now() - start >= this.configuration.debounceMaxWait)
|
|
65
|
+
return run();
|
|
66
|
+
this.debounced.set(id, {
|
|
67
|
+
start,
|
|
68
|
+
timeout: setTimeout(run, this.configuration.debounce),
|
|
69
|
+
});
|
|
70
|
+
}
|
|
71
|
+
/**
|
|
72
|
+
* Send a request to the given url containing the given data
|
|
73
|
+
*/
|
|
74
|
+
async sendRequest(event, payload) {
|
|
75
|
+
const json = JSON.stringify({ event, payload });
|
|
76
|
+
return axios__default["default"].post(this.configuration.url, json, { headers: { 'X-Hocuspocus-Signature-256': this.createSignature(json), 'Content-Type': 'application/json' } });
|
|
77
|
+
}
|
|
78
|
+
/**
|
|
79
|
+
* onChange hook
|
|
80
|
+
*/
|
|
81
|
+
async onChange(data) {
|
|
82
|
+
if (!this.configuration.events.includes(exports.Events.onChange)) {
|
|
83
|
+
return;
|
|
84
|
+
}
|
|
85
|
+
const save = () => {
|
|
86
|
+
try {
|
|
87
|
+
this.sendRequest(exports.Events.onChange, {
|
|
88
|
+
document: this.configuration.transformer.fromYdoc(data.document),
|
|
89
|
+
documentName: data.documentName,
|
|
90
|
+
context: data.context,
|
|
91
|
+
requestHeaders: data.requestHeaders,
|
|
92
|
+
requestParameters: Object.fromEntries(data.requestParameters.entries()),
|
|
93
|
+
});
|
|
94
|
+
}
|
|
95
|
+
catch (e) {
|
|
96
|
+
console.error(`Caught error in extension-webhook: ${e}`);
|
|
97
|
+
}
|
|
98
|
+
};
|
|
99
|
+
if (!this.configuration.debounce) {
|
|
100
|
+
return save();
|
|
101
|
+
}
|
|
102
|
+
this.debounce(data.documentName, save);
|
|
103
|
+
}
|
|
104
|
+
/**
|
|
105
|
+
* onLoadDocument hook
|
|
106
|
+
*/
|
|
107
|
+
async onLoadDocument(data) {
|
|
108
|
+
if (!this.configuration.events.includes(exports.Events.onCreate)) {
|
|
109
|
+
return;
|
|
110
|
+
}
|
|
111
|
+
try {
|
|
112
|
+
const response = await this.sendRequest(exports.Events.onCreate, {
|
|
113
|
+
documentName: data.documentName,
|
|
114
|
+
requestHeaders: data.requestHeaders,
|
|
115
|
+
requestParameters: Object.fromEntries(data.requestParameters.entries()),
|
|
116
|
+
});
|
|
117
|
+
if (response.status !== 200 || !response.data)
|
|
118
|
+
return;
|
|
119
|
+
const document = typeof response.data === 'string'
|
|
120
|
+
? JSON.parse(response.data)
|
|
121
|
+
: response.data;
|
|
122
|
+
// eslint-disable-next-line guard-for-in,no-restricted-syntax
|
|
123
|
+
for (const fieldName in document) {
|
|
124
|
+
if (data.document.isEmpty(fieldName)) {
|
|
125
|
+
data.document.merge(this.configuration.transformer.toYdoc(document[fieldName], fieldName));
|
|
126
|
+
}
|
|
127
|
+
}
|
|
128
|
+
}
|
|
129
|
+
catch (e) {
|
|
130
|
+
console.error(`Caught error in extension-webhook: ${e}`);
|
|
131
|
+
}
|
|
132
|
+
}
|
|
133
|
+
/**
|
|
134
|
+
* onConnect hook
|
|
135
|
+
*/
|
|
136
|
+
async onConnect(data) {
|
|
137
|
+
if (!this.configuration.events.includes(exports.Events.onConnect)) {
|
|
138
|
+
return;
|
|
139
|
+
}
|
|
140
|
+
try {
|
|
141
|
+
const response = await this.sendRequest(exports.Events.onConnect, {
|
|
142
|
+
documentName: data.documentName,
|
|
143
|
+
requestHeaders: data.requestHeaders,
|
|
144
|
+
requestParameters: Object.fromEntries(data.requestParameters.entries()),
|
|
145
|
+
});
|
|
146
|
+
return typeof response.data === 'string' && response.data.length > 0
|
|
147
|
+
? JSON.parse(response.data)
|
|
148
|
+
: response.data;
|
|
149
|
+
}
|
|
150
|
+
catch (e) {
|
|
151
|
+
console.error(`Caught error in extension-webhook: ${e}`);
|
|
152
|
+
throw common.Forbidden;
|
|
153
|
+
}
|
|
154
|
+
}
|
|
155
|
+
async onDisconnect(data) {
|
|
156
|
+
if (!this.configuration.events.includes(exports.Events.onDisconnect)) {
|
|
157
|
+
return;
|
|
158
|
+
}
|
|
159
|
+
try {
|
|
160
|
+
await this.sendRequest(exports.Events.onDisconnect, {
|
|
161
|
+
documentName: data.documentName,
|
|
162
|
+
requestHeaders: data.requestHeaders,
|
|
163
|
+
requestParameters: Object.fromEntries(data.requestParameters.entries()),
|
|
164
|
+
context: data.context,
|
|
165
|
+
});
|
|
166
|
+
}
|
|
167
|
+
catch (e) {
|
|
168
|
+
console.error(`Caught error in extension-webhook: ${e}`);
|
|
169
|
+
}
|
|
170
|
+
}
|
|
171
171
|
}
|
|
172
172
|
|
|
173
173
|
exports.Webhook = Webhook;
|
|
@@ -3,163 +3,163 @@ import { TiptapTransformer } from '@hocuspocus/transformer';
|
|
|
3
3
|
import axios from 'axios';
|
|
4
4
|
import { Forbidden } from '@hocuspocus/common';
|
|
5
5
|
|
|
6
|
-
var Events;
|
|
7
|
-
(function (Events) {
|
|
8
|
-
Events["onChange"] = "change";
|
|
9
|
-
Events["onConnect"] = "connect";
|
|
10
|
-
Events["onCreate"] = "create";
|
|
11
|
-
Events["onDisconnect"] = "disconnect";
|
|
12
|
-
})(Events || (Events = {}));
|
|
13
|
-
class Webhook {
|
|
14
|
-
/**
|
|
15
|
-
* Constructor
|
|
16
|
-
*/
|
|
17
|
-
constructor(configuration) {
|
|
18
|
-
this.configuration = {
|
|
19
|
-
debounce: 2000,
|
|
20
|
-
debounceMaxWait: 10000,
|
|
21
|
-
secret: '',
|
|
22
|
-
transformer: TiptapTransformer,
|
|
23
|
-
url: '',
|
|
24
|
-
events: [
|
|
25
|
-
Events.onChange,
|
|
26
|
-
],
|
|
27
|
-
};
|
|
28
|
-
this.debounced = new Map();
|
|
29
|
-
this.configuration = {
|
|
30
|
-
...this.configuration,
|
|
31
|
-
...configuration,
|
|
32
|
-
};
|
|
33
|
-
if (!this.configuration.url) {
|
|
34
|
-
throw new Error('url is required!');
|
|
35
|
-
}
|
|
36
|
-
}
|
|
37
|
-
/**
|
|
38
|
-
* Create a signature for the response body
|
|
39
|
-
*/
|
|
40
|
-
createSignature(body) {
|
|
41
|
-
const hmac = createHmac('sha256', this.configuration.secret);
|
|
42
|
-
return `sha256=${hmac.update(body).digest('hex')}`;
|
|
43
|
-
}
|
|
44
|
-
/**
|
|
45
|
-
* debounce the given function, using the given identifier
|
|
46
|
-
*/
|
|
47
|
-
debounce(id, func) {
|
|
48
|
-
const old = this.debounced.get(id);
|
|
49
|
-
const start = (old === null || old === void 0 ? void 0 : old.start) || Date.now();
|
|
50
|
-
const run = () => {
|
|
51
|
-
this.debounced.delete(id);
|
|
52
|
-
func();
|
|
53
|
-
};
|
|
54
|
-
if (old === null || old === void 0 ? void 0 : old.timeout)
|
|
55
|
-
clearTimeout(old.timeout);
|
|
56
|
-
if (Date.now() - start >= this.configuration.debounceMaxWait)
|
|
57
|
-
return run();
|
|
58
|
-
this.debounced.set(id, {
|
|
59
|
-
start,
|
|
60
|
-
timeout: setTimeout(run, this.configuration.debounce),
|
|
61
|
-
});
|
|
62
|
-
}
|
|
63
|
-
/**
|
|
64
|
-
* Send a request to the given url containing the given data
|
|
65
|
-
*/
|
|
66
|
-
async sendRequest(event, payload) {
|
|
67
|
-
const json = JSON.stringify({ event, payload });
|
|
68
|
-
return axios.post(this.configuration.url, json, { headers: { 'X-Hocuspocus-Signature-256': this.createSignature(json), 'Content-Type': 'application/json' } });
|
|
69
|
-
}
|
|
70
|
-
/**
|
|
71
|
-
* onChange hook
|
|
72
|
-
*/
|
|
73
|
-
async onChange(data) {
|
|
74
|
-
if (!this.configuration.events.includes(Events.onChange)) {
|
|
75
|
-
return;
|
|
76
|
-
}
|
|
77
|
-
const save = () => {
|
|
78
|
-
try {
|
|
79
|
-
this.sendRequest(Events.onChange, {
|
|
80
|
-
document: this.configuration.transformer.fromYdoc(data.document),
|
|
81
|
-
documentName: data.documentName,
|
|
82
|
-
context: data.context,
|
|
83
|
-
requestHeaders: data.requestHeaders,
|
|
84
|
-
requestParameters: Object.fromEntries(data.requestParameters.entries()),
|
|
85
|
-
});
|
|
86
|
-
}
|
|
87
|
-
catch (e) {
|
|
88
|
-
console.error(`Caught error in extension-webhook: ${e}`);
|
|
89
|
-
}
|
|
90
|
-
};
|
|
91
|
-
if (!this.configuration.debounce) {
|
|
92
|
-
return save();
|
|
93
|
-
}
|
|
94
|
-
this.debounce(data.documentName, save);
|
|
95
|
-
}
|
|
96
|
-
/**
|
|
97
|
-
* onLoadDocument hook
|
|
98
|
-
*/
|
|
99
|
-
async onLoadDocument(data) {
|
|
100
|
-
if (!this.configuration.events.includes(Events.onCreate)) {
|
|
101
|
-
return;
|
|
102
|
-
}
|
|
103
|
-
try {
|
|
104
|
-
const response = await this.sendRequest(Events.onCreate, {
|
|
105
|
-
documentName: data.documentName,
|
|
106
|
-
requestHeaders: data.requestHeaders,
|
|
107
|
-
requestParameters: Object.fromEntries(data.requestParameters.entries()),
|
|
108
|
-
});
|
|
109
|
-
if (response.status !== 200 || !response.data)
|
|
110
|
-
return;
|
|
111
|
-
const document = typeof response.data === 'string'
|
|
112
|
-
? JSON.parse(response.data)
|
|
113
|
-
: response.data;
|
|
114
|
-
// eslint-disable-next-line guard-for-in,no-restricted-syntax
|
|
115
|
-
for (const fieldName in document) {
|
|
116
|
-
if (data.document.isEmpty(fieldName)) {
|
|
117
|
-
data.document.merge(this.configuration.transformer.toYdoc(document[fieldName], fieldName));
|
|
118
|
-
}
|
|
119
|
-
}
|
|
120
|
-
}
|
|
121
|
-
catch (e) {
|
|
122
|
-
console.error(`Caught error in extension-webhook: ${e}`);
|
|
123
|
-
}
|
|
124
|
-
}
|
|
125
|
-
/**
|
|
126
|
-
* onConnect hook
|
|
127
|
-
*/
|
|
128
|
-
async onConnect(data) {
|
|
129
|
-
if (!this.configuration.events.includes(Events.onConnect)) {
|
|
130
|
-
return;
|
|
131
|
-
}
|
|
132
|
-
try {
|
|
133
|
-
const response = await this.sendRequest(Events.onConnect, {
|
|
134
|
-
documentName: data.documentName,
|
|
135
|
-
requestHeaders: data.requestHeaders,
|
|
136
|
-
requestParameters: Object.fromEntries(data.requestParameters.entries()),
|
|
137
|
-
});
|
|
138
|
-
return typeof response.data === 'string' && response.data.length > 0
|
|
139
|
-
? JSON.parse(response.data)
|
|
140
|
-
: response.data;
|
|
141
|
-
}
|
|
142
|
-
catch (e) {
|
|
143
|
-
console.error(`Caught error in extension-webhook: ${e}`);
|
|
144
|
-
throw Forbidden;
|
|
145
|
-
}
|
|
146
|
-
}
|
|
147
|
-
async onDisconnect(data) {
|
|
148
|
-
if (!this.configuration.events.includes(Events.onDisconnect)) {
|
|
149
|
-
return;
|
|
150
|
-
}
|
|
151
|
-
try {
|
|
152
|
-
await this.sendRequest(Events.onDisconnect, {
|
|
153
|
-
documentName: data.documentName,
|
|
154
|
-
requestHeaders: data.requestHeaders,
|
|
155
|
-
requestParameters: Object.fromEntries(data.requestParameters.entries()),
|
|
156
|
-
context: data.context,
|
|
157
|
-
});
|
|
158
|
-
}
|
|
159
|
-
catch (e) {
|
|
160
|
-
console.error(`Caught error in extension-webhook: ${e}`);
|
|
161
|
-
}
|
|
162
|
-
}
|
|
6
|
+
var Events;
|
|
7
|
+
(function (Events) {
|
|
8
|
+
Events["onChange"] = "change";
|
|
9
|
+
Events["onConnect"] = "connect";
|
|
10
|
+
Events["onCreate"] = "create";
|
|
11
|
+
Events["onDisconnect"] = "disconnect";
|
|
12
|
+
})(Events || (Events = {}));
|
|
13
|
+
class Webhook {
|
|
14
|
+
/**
|
|
15
|
+
* Constructor
|
|
16
|
+
*/
|
|
17
|
+
constructor(configuration) {
|
|
18
|
+
this.configuration = {
|
|
19
|
+
debounce: 2000,
|
|
20
|
+
debounceMaxWait: 10000,
|
|
21
|
+
secret: '',
|
|
22
|
+
transformer: TiptapTransformer,
|
|
23
|
+
url: '',
|
|
24
|
+
events: [
|
|
25
|
+
Events.onChange,
|
|
26
|
+
],
|
|
27
|
+
};
|
|
28
|
+
this.debounced = new Map();
|
|
29
|
+
this.configuration = {
|
|
30
|
+
...this.configuration,
|
|
31
|
+
...configuration,
|
|
32
|
+
};
|
|
33
|
+
if (!this.configuration.url) {
|
|
34
|
+
throw new Error('url is required!');
|
|
35
|
+
}
|
|
36
|
+
}
|
|
37
|
+
/**
|
|
38
|
+
* Create a signature for the response body
|
|
39
|
+
*/
|
|
40
|
+
createSignature(body) {
|
|
41
|
+
const hmac = createHmac('sha256', this.configuration.secret);
|
|
42
|
+
return `sha256=${hmac.update(body).digest('hex')}`;
|
|
43
|
+
}
|
|
44
|
+
/**
|
|
45
|
+
* debounce the given function, using the given identifier
|
|
46
|
+
*/
|
|
47
|
+
debounce(id, func) {
|
|
48
|
+
const old = this.debounced.get(id);
|
|
49
|
+
const start = (old === null || old === void 0 ? void 0 : old.start) || Date.now();
|
|
50
|
+
const run = () => {
|
|
51
|
+
this.debounced.delete(id);
|
|
52
|
+
func();
|
|
53
|
+
};
|
|
54
|
+
if (old === null || old === void 0 ? void 0 : old.timeout)
|
|
55
|
+
clearTimeout(old.timeout);
|
|
56
|
+
if (Date.now() - start >= this.configuration.debounceMaxWait)
|
|
57
|
+
return run();
|
|
58
|
+
this.debounced.set(id, {
|
|
59
|
+
start,
|
|
60
|
+
timeout: setTimeout(run, this.configuration.debounce),
|
|
61
|
+
});
|
|
62
|
+
}
|
|
63
|
+
/**
|
|
64
|
+
* Send a request to the given url containing the given data
|
|
65
|
+
*/
|
|
66
|
+
async sendRequest(event, payload) {
|
|
67
|
+
const json = JSON.stringify({ event, payload });
|
|
68
|
+
return axios.post(this.configuration.url, json, { headers: { 'X-Hocuspocus-Signature-256': this.createSignature(json), 'Content-Type': 'application/json' } });
|
|
69
|
+
}
|
|
70
|
+
/**
|
|
71
|
+
* onChange hook
|
|
72
|
+
*/
|
|
73
|
+
async onChange(data) {
|
|
74
|
+
if (!this.configuration.events.includes(Events.onChange)) {
|
|
75
|
+
return;
|
|
76
|
+
}
|
|
77
|
+
const save = () => {
|
|
78
|
+
try {
|
|
79
|
+
this.sendRequest(Events.onChange, {
|
|
80
|
+
document: this.configuration.transformer.fromYdoc(data.document),
|
|
81
|
+
documentName: data.documentName,
|
|
82
|
+
context: data.context,
|
|
83
|
+
requestHeaders: data.requestHeaders,
|
|
84
|
+
requestParameters: Object.fromEntries(data.requestParameters.entries()),
|
|
85
|
+
});
|
|
86
|
+
}
|
|
87
|
+
catch (e) {
|
|
88
|
+
console.error(`Caught error in extension-webhook: ${e}`);
|
|
89
|
+
}
|
|
90
|
+
};
|
|
91
|
+
if (!this.configuration.debounce) {
|
|
92
|
+
return save();
|
|
93
|
+
}
|
|
94
|
+
this.debounce(data.documentName, save);
|
|
95
|
+
}
|
|
96
|
+
/**
|
|
97
|
+
* onLoadDocument hook
|
|
98
|
+
*/
|
|
99
|
+
async onLoadDocument(data) {
|
|
100
|
+
if (!this.configuration.events.includes(Events.onCreate)) {
|
|
101
|
+
return;
|
|
102
|
+
}
|
|
103
|
+
try {
|
|
104
|
+
const response = await this.sendRequest(Events.onCreate, {
|
|
105
|
+
documentName: data.documentName,
|
|
106
|
+
requestHeaders: data.requestHeaders,
|
|
107
|
+
requestParameters: Object.fromEntries(data.requestParameters.entries()),
|
|
108
|
+
});
|
|
109
|
+
if (response.status !== 200 || !response.data)
|
|
110
|
+
return;
|
|
111
|
+
const document = typeof response.data === 'string'
|
|
112
|
+
? JSON.parse(response.data)
|
|
113
|
+
: response.data;
|
|
114
|
+
// eslint-disable-next-line guard-for-in,no-restricted-syntax
|
|
115
|
+
for (const fieldName in document) {
|
|
116
|
+
if (data.document.isEmpty(fieldName)) {
|
|
117
|
+
data.document.merge(this.configuration.transformer.toYdoc(document[fieldName], fieldName));
|
|
118
|
+
}
|
|
119
|
+
}
|
|
120
|
+
}
|
|
121
|
+
catch (e) {
|
|
122
|
+
console.error(`Caught error in extension-webhook: ${e}`);
|
|
123
|
+
}
|
|
124
|
+
}
|
|
125
|
+
/**
|
|
126
|
+
* onConnect hook
|
|
127
|
+
*/
|
|
128
|
+
async onConnect(data) {
|
|
129
|
+
if (!this.configuration.events.includes(Events.onConnect)) {
|
|
130
|
+
return;
|
|
131
|
+
}
|
|
132
|
+
try {
|
|
133
|
+
const response = await this.sendRequest(Events.onConnect, {
|
|
134
|
+
documentName: data.documentName,
|
|
135
|
+
requestHeaders: data.requestHeaders,
|
|
136
|
+
requestParameters: Object.fromEntries(data.requestParameters.entries()),
|
|
137
|
+
});
|
|
138
|
+
return typeof response.data === 'string' && response.data.length > 0
|
|
139
|
+
? JSON.parse(response.data)
|
|
140
|
+
: response.data;
|
|
141
|
+
}
|
|
142
|
+
catch (e) {
|
|
143
|
+
console.error(`Caught error in extension-webhook: ${e}`);
|
|
144
|
+
throw Forbidden;
|
|
145
|
+
}
|
|
146
|
+
}
|
|
147
|
+
async onDisconnect(data) {
|
|
148
|
+
if (!this.configuration.events.includes(Events.onDisconnect)) {
|
|
149
|
+
return;
|
|
150
|
+
}
|
|
151
|
+
try {
|
|
152
|
+
await this.sendRequest(Events.onDisconnect, {
|
|
153
|
+
documentName: data.documentName,
|
|
154
|
+
requestHeaders: data.requestHeaders,
|
|
155
|
+
requestParameters: Object.fromEntries(data.requestParameters.entries()),
|
|
156
|
+
context: data.context,
|
|
157
|
+
});
|
|
158
|
+
}
|
|
159
|
+
catch (e) {
|
|
160
|
+
console.error(`Caught error in extension-webhook: ${e}`);
|
|
161
|
+
}
|
|
162
|
+
}
|
|
163
163
|
}
|
|
164
164
|
|
|
165
165
|
export { Events, Webhook };
|