@hocuspocus/extension-webhook 2.0.0-alpha.1 → 2.0.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 +43 -28
- package/dist/hocuspocus-webhook.cjs.map +1 -1
- package/dist/hocuspocus-webhook.esm.js +43 -28
- package/dist/hocuspocus-webhook.esm.js.map +1 -1
- package/dist/packages/extension-monitor/src/Dashboard.d.ts +1 -0
- package/dist/packages/extension-redis/src/Redis.d.ts +1 -1
- package/dist/packages/extension-webhook/src/index.d.ts +1 -2
- package/dist/packages/provider/src/HocuspocusCloudProvider.d.ts +1 -1
- package/dist/packages/provider/src/HocuspocusProvider.d.ts +3 -2
- package/dist/packages/provider/src/HocuspocusProviderWebsocket.d.ts +1 -1
- package/dist/packages/provider/src/TiptapCollabProvider.d.ts +11 -0
- package/dist/packages/provider/src/index.d.ts +1 -1
- package/dist/packages/provider/src/types.d.ts +13 -13
- package/dist/packages/server/src/Connection.d.ts +2 -1
- package/dist/packages/server/src/Hocuspocus.d.ts +1 -0
- package/dist/packages/server/src/types.d.ts +5 -3
- package/dist/tests/server/onClose.d.ts +1 -0
- package/dist/tests/utils/newHocuspocus.d.ts +1 -1
- package/package.json +4 -4
- package/src/index.ts +43 -31
|
@@ -83,13 +83,18 @@ class Webhook {
|
|
|
83
83
|
return;
|
|
84
84
|
}
|
|
85
85
|
const save = () => {
|
|
86
|
-
|
|
87
|
-
|
|
88
|
-
|
|
89
|
-
|
|
90
|
-
|
|
91
|
-
|
|
92
|
-
|
|
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
|
+
}
|
|
93
98
|
};
|
|
94
99
|
if (!this.configuration.debounce) {
|
|
95
100
|
return save();
|
|
@@ -103,22 +108,27 @@ class Webhook {
|
|
|
103
108
|
if (!this.configuration.events.includes(exports.Events.onCreate)) {
|
|
104
109
|
return;
|
|
105
110
|
}
|
|
106
|
-
|
|
107
|
-
|
|
108
|
-
|
|
109
|
-
|
|
110
|
-
|
|
111
|
-
|
|
112
|
-
|
|
113
|
-
|
|
114
|
-
|
|
115
|
-
|
|
116
|
-
|
|
117
|
-
|
|
118
|
-
|
|
119
|
-
data.document.
|
|
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
|
+
}
|
|
120
127
|
}
|
|
121
128
|
}
|
|
129
|
+
catch (e) {
|
|
130
|
+
console.error(`Caught error in extension-webhook: ${e}`);
|
|
131
|
+
}
|
|
122
132
|
}
|
|
123
133
|
/**
|
|
124
134
|
* onConnect hook
|
|
@@ -133,7 +143,7 @@ class Webhook {
|
|
|
133
143
|
requestHeaders: data.requestHeaders,
|
|
134
144
|
requestParameters: Object.fromEntries(data.requestParameters.entries()),
|
|
135
145
|
});
|
|
136
|
-
return typeof response.data === 'string'
|
|
146
|
+
return typeof response.data === 'string' && response.data.length > 0
|
|
137
147
|
? JSON.parse(response.data)
|
|
138
148
|
: response.data;
|
|
139
149
|
}
|
|
@@ -146,12 +156,17 @@ class Webhook {
|
|
|
146
156
|
if (!this.configuration.events.includes(exports.Events.onDisconnect)) {
|
|
147
157
|
return;
|
|
148
158
|
}
|
|
149
|
-
|
|
150
|
-
|
|
151
|
-
|
|
152
|
-
|
|
153
|
-
|
|
154
|
-
|
|
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
|
+
}
|
|
155
170
|
}
|
|
156
171
|
}
|
|
157
172
|
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"hocuspocus-webhook.cjs","sources":["../src/index.ts"],"sourcesContent":["import { createHmac } from 'crypto'\nimport {\n Extension,\n onChangePayload,\n onConnectPayload,\n onLoadDocumentPayload,\n onDisconnectPayload,\n} from '@hocuspocus/server'\nimport { Doc } from 'yjs'\nimport { TiptapTransformer, Transformer } from '@hocuspocus/transformer'\nimport axios, { AxiosResponse } from 'axios'\nimport { Forbidden } from '@hocuspocus/common'\n\nexport enum Events {\n onChange = 'change',\n onConnect = 'connect',\n onCreate = 'create',\n onDisconnect = 'disconnect',\n}\n\nexport interface Configuration {\n debounce: number | false | null,\n debounceMaxWait: number,\n secret: string,\n transformer: Transformer | {\n toYdoc: (document: any) => Doc,\n fromYdoc: (document: Doc) => any,\n },\n url: string,\n events: Array<Events>,\n}\n\nexport class Webhook implements Extension {\n\n configuration: Configuration = {\n debounce: 2000,\n debounceMaxWait: 10000,\n secret: '',\n transformer: TiptapTransformer,\n url: '',\n events: [\n Events.onChange,\n ],\n }\n\n debounced: Map<string, { timeout: NodeJS.Timeout, start: number }> = new Map()\n\n /**\n * Constructor\n */\n constructor(configuration?: Partial<Configuration>) {\n this.configuration = {\n ...this.configuration,\n ...configuration,\n }\n\n if (!this.configuration.url) {\n throw new Error('url is required!')\n }\n }\n\n /**\n * Create a signature for the response body\n */\n createSignature(body: string): string {\n const hmac = createHmac('sha256', this.configuration.secret)\n\n return `sha256=${hmac.update(body).digest('hex')}`\n }\n\n /**\n * debounce the given function, using the given identifier\n */\n debounce(id: string, func: Function) {\n const old = this.debounced.get(id)\n const start = old?.start || Date.now()\n\n const run = () => {\n this.debounced.delete(id)\n func()\n }\n\n if (old?.timeout) clearTimeout(old.timeout)\n if (Date.now() - start >= this.configuration.debounceMaxWait) return run()\n\n this.debounced.set(id, {\n start,\n timeout: setTimeout(run, <number> this.configuration.debounce),\n })\n }\n\n /**\n * Send a request to the given url containing the given data\n */\n async sendRequest(event: Events, payload: any) {\n const json = JSON.stringify({ event, payload })\n\n return axios.post(\n this.configuration.url,\n json,\n { headers: { 'X-Hocuspocus-Signature-256': this.createSignature(json), 'Content-Type': 'application/json' } },\n )\n }\n\n /**\n * onChange hook\n */\n async onChange(data: onChangePayload) {\n if (!this.configuration.events.includes(Events.onChange)) {\n return\n }\n\n const save = () => {\n this.sendRequest(Events.onChange, {\n document: this.configuration.transformer.fromYdoc(data.document),\n documentName: data.documentName,\n context: data.context,\n requestHeaders: data.requestHeaders,\n requestParameters: Object.fromEntries(data.requestParameters.entries()),\n })\n }\n\n if (!this.configuration.debounce) {\n return save()\n }\n\n this.debounce(data.documentName, save)\n }\n\n /**\n * onLoadDocument hook\n */\n async onLoadDocument(data: onLoadDocumentPayload) {\n if (!this.configuration.events.includes(Events.onCreate)) {\n return\n }\n\n const response = <AxiosResponse> await this.sendRequest(Events.onCreate, {\n documentName: data.documentName,\n requestHeaders: data.requestHeaders,\n requestParameters: Object.fromEntries(data.requestParameters.entries()),\n })\n\n if (response.status !== 200 || !response.data) return\n\n const document = typeof response.data === 'string'\n ? JSON.parse(response.data)\n : response.data\n\n // eslint-disable-next-line guard-for-in,no-restricted-syntax\n for (const fieldName in document) {\n if (data.document.isEmpty(fieldName)) {\n data.document.merge(\n this.configuration.transformer.toYdoc(document[fieldName], fieldName),\n )\n }\n }\n }\n\n /**\n * onConnect hook\n */\n async onConnect(data: onConnectPayload) {\n if (!this.configuration.events.includes(Events.onConnect)) {\n return\n }\n\n try {\n const response = <AxiosResponse> await this.sendRequest(Events.onConnect, {\n documentName: data.documentName,\n requestHeaders: data.requestHeaders,\n requestParameters: Object.fromEntries(data.requestParameters.entries()),\n })\n\n return typeof response.data === 'string'\n ? JSON.parse(response.data)\n : response.data\n } catch (e) {\n console.error(`Caught error in extension-webhook: ${e}`)\n throw Forbidden\n }\n }\n\n async onDisconnect(data: onDisconnectPayload) {\n if (!this.configuration.events.includes(Events.onDisconnect)) {\n return\n }\n\n await this.sendRequest(Events.onDisconnect, {\n documentName: data.documentName,\n requestHeaders: data.requestHeaders,\n requestParameters: Object.fromEntries(data.requestParameters.entries()),\n context: data.context,\n })\n }\n\n}\n"],"names":["Events","TiptapTransformer","createHmac","axios","Forbidden"],"mappings":"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"}
|
|
1
|
+
{"version":3,"file":"hocuspocus-webhook.cjs","sources":["../src/index.ts"],"sourcesContent":["import { createHmac } from 'crypto'\nimport {\n Extension,\n onChangePayload,\n onConnectPayload,\n onLoadDocumentPayload,\n onDisconnectPayload,\n} from '@hocuspocus/server'\nimport { Doc } from 'yjs'\nimport { TiptapTransformer, Transformer } from '@hocuspocus/transformer'\nimport axios from 'axios'\nimport { Forbidden } from '@hocuspocus/common'\n\nexport enum Events {\n onChange = 'change',\n onConnect = 'connect',\n onCreate = 'create',\n onDisconnect = 'disconnect',\n}\n\nexport interface Configuration {\n debounce: number | false | null,\n debounceMaxWait: number,\n secret: string,\n transformer: Transformer | {\n toYdoc: (document: any) => Doc,\n fromYdoc: (document: Doc) => any,\n },\n url: string,\n events: Array<Events>,\n}\n\nexport class Webhook implements Extension {\n\n configuration: Configuration = {\n debounce: 2000,\n debounceMaxWait: 10000,\n secret: '',\n transformer: TiptapTransformer,\n url: '',\n events: [\n Events.onChange,\n ],\n }\n\n debounced: Map<string, { timeout: NodeJS.Timeout, start: number }> = new Map()\n\n /**\n * Constructor\n */\n constructor(configuration?: Partial<Configuration>) {\n this.configuration = {\n ...this.configuration,\n ...configuration,\n }\n\n if (!this.configuration.url) {\n throw new Error('url is required!')\n }\n }\n\n /**\n * Create a signature for the response body\n */\n createSignature(body: string): string {\n const hmac = createHmac('sha256', this.configuration.secret)\n\n return `sha256=${hmac.update(body).digest('hex')}`\n }\n\n /**\n * debounce the given function, using the given identifier\n */\n debounce(id: string, func: Function) {\n const old = this.debounced.get(id)\n const start = old?.start || Date.now()\n\n const run = () => {\n this.debounced.delete(id)\n func()\n }\n\n if (old?.timeout) clearTimeout(old.timeout)\n if (Date.now() - start >= this.configuration.debounceMaxWait) return run()\n\n this.debounced.set(id, {\n start,\n timeout: setTimeout(run, <number> this.configuration.debounce),\n })\n }\n\n /**\n * Send a request to the given url containing the given data\n */\n async sendRequest(event: Events, payload: any) {\n const json = JSON.stringify({ event, payload })\n\n return axios.post(\n this.configuration.url,\n json,\n { headers: { 'X-Hocuspocus-Signature-256': this.createSignature(json), 'Content-Type': 'application/json' } },\n )\n }\n\n /**\n * onChange hook\n */\n async onChange(data: onChangePayload) {\n if (!this.configuration.events.includes(Events.onChange)) {\n return\n }\n\n const save = () => {\n try {\n this.sendRequest(Events.onChange, {\n document: this.configuration.transformer.fromYdoc(data.document),\n documentName: data.documentName,\n context: data.context,\n requestHeaders: data.requestHeaders,\n requestParameters: Object.fromEntries(data.requestParameters.entries()),\n })\n } catch (e) {\n console.error(`Caught error in extension-webhook: ${e}`)\n }\n }\n\n if (!this.configuration.debounce) {\n return save()\n }\n\n this.debounce(data.documentName, save)\n }\n\n /**\n * onLoadDocument hook\n */\n async onLoadDocument(data: onLoadDocumentPayload) {\n if (!this.configuration.events.includes(Events.onCreate)) {\n return\n }\n\n try {\n const response = await this.sendRequest(Events.onCreate, {\n documentName: data.documentName,\n requestHeaders: data.requestHeaders,\n requestParameters: Object.fromEntries(data.requestParameters.entries()),\n })\n\n if (response.status !== 200 || !response.data) return\n\n const document = typeof response.data === 'string'\n ? JSON.parse(response.data)\n : response.data\n\n // eslint-disable-next-line guard-for-in,no-restricted-syntax\n for (const fieldName in document) {\n if (data.document.isEmpty(fieldName)) {\n data.document.merge(\n this.configuration.transformer.toYdoc(document[fieldName], fieldName),\n )\n }\n }\n } catch (e) {\n console.error(`Caught error in extension-webhook: ${e}`)\n }\n }\n\n /**\n * onConnect hook\n */\n async onConnect(data: onConnectPayload) {\n if (!this.configuration.events.includes(Events.onConnect)) {\n return\n }\n\n try {\n const response = await this.sendRequest(Events.onConnect, {\n documentName: data.documentName,\n requestHeaders: data.requestHeaders,\n requestParameters: Object.fromEntries(data.requestParameters.entries()),\n })\n\n return typeof response.data === 'string' && response.data.length > 0\n ? JSON.parse(response.data)\n : response.data\n } catch (e) {\n console.error(`Caught error in extension-webhook: ${e}`)\n throw Forbidden\n }\n }\n\n async onDisconnect(data: onDisconnectPayload) {\n if (!this.configuration.events.includes(Events.onDisconnect)) {\n return\n }\n\n try {\n await this.sendRequest(Events.onDisconnect, {\n documentName: data.documentName,\n requestHeaders: data.requestHeaders,\n requestParameters: Object.fromEntries(data.requestParameters.entries()),\n context: data.context,\n })\n } catch (e) {\n console.error(`Caught error in extension-webhook: ${e}`)\n }\n }\n\n}\n"],"names":["Events","TiptapTransformer","createHmac","axios","Forbidden"],"mappings":"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"}
|
|
@@ -75,13 +75,18 @@ class Webhook {
|
|
|
75
75
|
return;
|
|
76
76
|
}
|
|
77
77
|
const save = () => {
|
|
78
|
-
|
|
79
|
-
|
|
80
|
-
|
|
81
|
-
|
|
82
|
-
|
|
83
|
-
|
|
84
|
-
|
|
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
|
+
}
|
|
85
90
|
};
|
|
86
91
|
if (!this.configuration.debounce) {
|
|
87
92
|
return save();
|
|
@@ -95,22 +100,27 @@ class Webhook {
|
|
|
95
100
|
if (!this.configuration.events.includes(Events.onCreate)) {
|
|
96
101
|
return;
|
|
97
102
|
}
|
|
98
|
-
|
|
99
|
-
|
|
100
|
-
|
|
101
|
-
|
|
102
|
-
|
|
103
|
-
|
|
104
|
-
|
|
105
|
-
|
|
106
|
-
|
|
107
|
-
|
|
108
|
-
|
|
109
|
-
|
|
110
|
-
|
|
111
|
-
data.document.
|
|
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
|
+
}
|
|
112
119
|
}
|
|
113
120
|
}
|
|
121
|
+
catch (e) {
|
|
122
|
+
console.error(`Caught error in extension-webhook: ${e}`);
|
|
123
|
+
}
|
|
114
124
|
}
|
|
115
125
|
/**
|
|
116
126
|
* onConnect hook
|
|
@@ -125,7 +135,7 @@ class Webhook {
|
|
|
125
135
|
requestHeaders: data.requestHeaders,
|
|
126
136
|
requestParameters: Object.fromEntries(data.requestParameters.entries()),
|
|
127
137
|
});
|
|
128
|
-
return typeof response.data === 'string'
|
|
138
|
+
return typeof response.data === 'string' && response.data.length > 0
|
|
129
139
|
? JSON.parse(response.data)
|
|
130
140
|
: response.data;
|
|
131
141
|
}
|
|
@@ -138,12 +148,17 @@ class Webhook {
|
|
|
138
148
|
if (!this.configuration.events.includes(Events.onDisconnect)) {
|
|
139
149
|
return;
|
|
140
150
|
}
|
|
141
|
-
|
|
142
|
-
|
|
143
|
-
|
|
144
|
-
|
|
145
|
-
|
|
146
|
-
|
|
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
|
+
}
|
|
147
162
|
}
|
|
148
163
|
}
|
|
149
164
|
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"hocuspocus-webhook.esm.js","sources":["../src/index.ts"],"sourcesContent":["import { createHmac } from 'crypto'\nimport {\n Extension,\n onChangePayload,\n onConnectPayload,\n onLoadDocumentPayload,\n onDisconnectPayload,\n} from '@hocuspocus/server'\nimport { Doc } from 'yjs'\nimport { TiptapTransformer, Transformer } from '@hocuspocus/transformer'\nimport axios, { AxiosResponse } from 'axios'\nimport { Forbidden } from '@hocuspocus/common'\n\nexport enum Events {\n onChange = 'change',\n onConnect = 'connect',\n onCreate = 'create',\n onDisconnect = 'disconnect',\n}\n\nexport interface Configuration {\n debounce: number | false | null,\n debounceMaxWait: number,\n secret: string,\n transformer: Transformer | {\n toYdoc: (document: any) => Doc,\n fromYdoc: (document: Doc) => any,\n },\n url: string,\n events: Array<Events>,\n}\n\nexport class Webhook implements Extension {\n\n configuration: Configuration = {\n debounce: 2000,\n debounceMaxWait: 10000,\n secret: '',\n transformer: TiptapTransformer,\n url: '',\n events: [\n Events.onChange,\n ],\n }\n\n debounced: Map<string, { timeout: NodeJS.Timeout, start: number }> = new Map()\n\n /**\n * Constructor\n */\n constructor(configuration?: Partial<Configuration>) {\n this.configuration = {\n ...this.configuration,\n ...configuration,\n }\n\n if (!this.configuration.url) {\n throw new Error('url is required!')\n }\n }\n\n /**\n * Create a signature for the response body\n */\n createSignature(body: string): string {\n const hmac = createHmac('sha256', this.configuration.secret)\n\n return `sha256=${hmac.update(body).digest('hex')}`\n }\n\n /**\n * debounce the given function, using the given identifier\n */\n debounce(id: string, func: Function) {\n const old = this.debounced.get(id)\n const start = old?.start || Date.now()\n\n const run = () => {\n this.debounced.delete(id)\n func()\n }\n\n if (old?.timeout) clearTimeout(old.timeout)\n if (Date.now() - start >= this.configuration.debounceMaxWait) return run()\n\n this.debounced.set(id, {\n start,\n timeout: setTimeout(run, <number> this.configuration.debounce),\n })\n }\n\n /**\n * Send a request to the given url containing the given data\n */\n async sendRequest(event: Events, payload: any) {\n const json = JSON.stringify({ event, payload })\n\n return axios.post(\n this.configuration.url,\n json,\n { headers: { 'X-Hocuspocus-Signature-256': this.createSignature(json), 'Content-Type': 'application/json' } },\n )\n }\n\n /**\n * onChange hook\n */\n async onChange(data: onChangePayload) {\n if (!this.configuration.events.includes(Events.onChange)) {\n return\n }\n\n const save = () => {\n this.sendRequest(Events.onChange, {\n document: this.configuration.transformer.fromYdoc(data.document),\n documentName: data.documentName,\n context: data.context,\n requestHeaders: data.requestHeaders,\n requestParameters: Object.fromEntries(data.requestParameters.entries()),\n })\n }\n\n if (!this.configuration.debounce) {\n return save()\n }\n\n this.debounce(data.documentName, save)\n }\n\n /**\n * onLoadDocument hook\n */\n async onLoadDocument(data: onLoadDocumentPayload) {\n if (!this.configuration.events.includes(Events.onCreate)) {\n return\n }\n\n const response = <AxiosResponse> await this.sendRequest(Events.onCreate, {\n documentName: data.documentName,\n requestHeaders: data.requestHeaders,\n requestParameters: Object.fromEntries(data.requestParameters.entries()),\n })\n\n if (response.status !== 200 || !response.data) return\n\n const document = typeof response.data === 'string'\n ? JSON.parse(response.data)\n : response.data\n\n // eslint-disable-next-line guard-for-in,no-restricted-syntax\n for (const fieldName in document) {\n if (data.document.isEmpty(fieldName)) {\n data.document.merge(\n this.configuration.transformer.toYdoc(document[fieldName], fieldName),\n )\n }\n }\n }\n\n /**\n * onConnect hook\n */\n async onConnect(data: onConnectPayload) {\n if (!this.configuration.events.includes(Events.onConnect)) {\n return\n }\n\n try {\n const response = <AxiosResponse> await this.sendRequest(Events.onConnect, {\n documentName: data.documentName,\n requestHeaders: data.requestHeaders,\n requestParameters: Object.fromEntries(data.requestParameters.entries()),\n })\n\n return typeof response.data === 'string'\n ? JSON.parse(response.data)\n : response.data\n } catch (e) {\n console.error(`Caught error in extension-webhook: ${e}`)\n throw Forbidden\n }\n }\n\n async onDisconnect(data: onDisconnectPayload) {\n if (!this.configuration.events.includes(Events.onDisconnect)) {\n return\n }\n\n await this.sendRequest(Events.onDisconnect, {\n documentName: data.documentName,\n requestHeaders: data.requestHeaders,\n requestParameters: Object.fromEntries(data.requestParameters.entries()),\n context: data.context,\n })\n }\n\n}\n"],"names":[],"mappings":"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"}
|
|
1
|
+
{"version":3,"file":"hocuspocus-webhook.esm.js","sources":["../src/index.ts"],"sourcesContent":["import { createHmac } from 'crypto'\nimport {\n Extension,\n onChangePayload,\n onConnectPayload,\n onLoadDocumentPayload,\n onDisconnectPayload,\n} from '@hocuspocus/server'\nimport { Doc } from 'yjs'\nimport { TiptapTransformer, Transformer } from '@hocuspocus/transformer'\nimport axios from 'axios'\nimport { Forbidden } from '@hocuspocus/common'\n\nexport enum Events {\n onChange = 'change',\n onConnect = 'connect',\n onCreate = 'create',\n onDisconnect = 'disconnect',\n}\n\nexport interface Configuration {\n debounce: number | false | null,\n debounceMaxWait: number,\n secret: string,\n transformer: Transformer | {\n toYdoc: (document: any) => Doc,\n fromYdoc: (document: Doc) => any,\n },\n url: string,\n events: Array<Events>,\n}\n\nexport class Webhook implements Extension {\n\n configuration: Configuration = {\n debounce: 2000,\n debounceMaxWait: 10000,\n secret: '',\n transformer: TiptapTransformer,\n url: '',\n events: [\n Events.onChange,\n ],\n }\n\n debounced: Map<string, { timeout: NodeJS.Timeout, start: number }> = new Map()\n\n /**\n * Constructor\n */\n constructor(configuration?: Partial<Configuration>) {\n this.configuration = {\n ...this.configuration,\n ...configuration,\n }\n\n if (!this.configuration.url) {\n throw new Error('url is required!')\n }\n }\n\n /**\n * Create a signature for the response body\n */\n createSignature(body: string): string {\n const hmac = createHmac('sha256', this.configuration.secret)\n\n return `sha256=${hmac.update(body).digest('hex')}`\n }\n\n /**\n * debounce the given function, using the given identifier\n */\n debounce(id: string, func: Function) {\n const old = this.debounced.get(id)\n const start = old?.start || Date.now()\n\n const run = () => {\n this.debounced.delete(id)\n func()\n }\n\n if (old?.timeout) clearTimeout(old.timeout)\n if (Date.now() - start >= this.configuration.debounceMaxWait) return run()\n\n this.debounced.set(id, {\n start,\n timeout: setTimeout(run, <number> this.configuration.debounce),\n })\n }\n\n /**\n * Send a request to the given url containing the given data\n */\n async sendRequest(event: Events, payload: any) {\n const json = JSON.stringify({ event, payload })\n\n return axios.post(\n this.configuration.url,\n json,\n { headers: { 'X-Hocuspocus-Signature-256': this.createSignature(json), 'Content-Type': 'application/json' } },\n )\n }\n\n /**\n * onChange hook\n */\n async onChange(data: onChangePayload) {\n if (!this.configuration.events.includes(Events.onChange)) {\n return\n }\n\n const save = () => {\n try {\n this.sendRequest(Events.onChange, {\n document: this.configuration.transformer.fromYdoc(data.document),\n documentName: data.documentName,\n context: data.context,\n requestHeaders: data.requestHeaders,\n requestParameters: Object.fromEntries(data.requestParameters.entries()),\n })\n } catch (e) {\n console.error(`Caught error in extension-webhook: ${e}`)\n }\n }\n\n if (!this.configuration.debounce) {\n return save()\n }\n\n this.debounce(data.documentName, save)\n }\n\n /**\n * onLoadDocument hook\n */\n async onLoadDocument(data: onLoadDocumentPayload) {\n if (!this.configuration.events.includes(Events.onCreate)) {\n return\n }\n\n try {\n const response = await this.sendRequest(Events.onCreate, {\n documentName: data.documentName,\n requestHeaders: data.requestHeaders,\n requestParameters: Object.fromEntries(data.requestParameters.entries()),\n })\n\n if (response.status !== 200 || !response.data) return\n\n const document = typeof response.data === 'string'\n ? JSON.parse(response.data)\n : response.data\n\n // eslint-disable-next-line guard-for-in,no-restricted-syntax\n for (const fieldName in document) {\n if (data.document.isEmpty(fieldName)) {\n data.document.merge(\n this.configuration.transformer.toYdoc(document[fieldName], fieldName),\n )\n }\n }\n } catch (e) {\n console.error(`Caught error in extension-webhook: ${e}`)\n }\n }\n\n /**\n * onConnect hook\n */\n async onConnect(data: onConnectPayload) {\n if (!this.configuration.events.includes(Events.onConnect)) {\n return\n }\n\n try {\n const response = await this.sendRequest(Events.onConnect, {\n documentName: data.documentName,\n requestHeaders: data.requestHeaders,\n requestParameters: Object.fromEntries(data.requestParameters.entries()),\n })\n\n return typeof response.data === 'string' && response.data.length > 0\n ? JSON.parse(response.data)\n : response.data\n } catch (e) {\n console.error(`Caught error in extension-webhook: ${e}`)\n throw Forbidden\n }\n }\n\n async onDisconnect(data: onDisconnectPayload) {\n if (!this.configuration.events.includes(Events.onDisconnect)) {\n return\n }\n\n try {\n await this.sendRequest(Events.onDisconnect, {\n documentName: data.documentName,\n requestHeaders: data.requestHeaders,\n requestParameters: Object.fromEntries(data.requestParameters.entries()),\n context: data.context,\n })\n } catch (e) {\n console.error(`Caught error in extension-webhook: ${e}`)\n }\n }\n\n}\n"],"names":[],"mappings":"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"}
|
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
import RedisClient, { ClusterNode, ClusterOptions, RedisOptions } from 'ioredis';
|
|
2
2
|
import Redlock from 'redlock';
|
|
3
3
|
import { Document, Extension, afterLoadDocumentPayload, afterStoreDocumentPayload, onDisconnectPayload, onStoreDocumentPayload, onAwarenessUpdatePayload, onChangePayload, Debugger, onConfigurePayload, onListenPayload, beforeBroadcastStatelessPayload } from '@hocuspocus/server';
|
|
4
|
-
export
|
|
4
|
+
export type RedisInstance = RedisClient.Cluster | RedisClient.Redis;
|
|
5
5
|
export interface Configuration {
|
|
6
6
|
/**
|
|
7
7
|
* Redis port
|
|
@@ -2,7 +2,6 @@
|
|
|
2
2
|
import { Extension, onChangePayload, onConnectPayload, onLoadDocumentPayload, onDisconnectPayload } from '@hocuspocus/server';
|
|
3
3
|
import { Doc } from 'yjs';
|
|
4
4
|
import { Transformer } from '@hocuspocus/transformer';
|
|
5
|
-
import { AxiosResponse } from 'axios';
|
|
6
5
|
export declare enum Events {
|
|
7
6
|
onChange = "change",
|
|
8
7
|
onConnect = "connect",
|
|
@@ -41,7 +40,7 @@ export declare class Webhook implements Extension {
|
|
|
41
40
|
/**
|
|
42
41
|
* Send a request to the given url containing the given data
|
|
43
42
|
*/
|
|
44
|
-
sendRequest(event: Events, payload: any): Promise<AxiosResponse<any, any>>;
|
|
43
|
+
sendRequest(event: Events, payload: any): Promise<import("axios").AxiosResponse<any, any>>;
|
|
45
44
|
/**
|
|
46
45
|
* onChange hook
|
|
47
46
|
*/
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
import { HocuspocusProvider, HocuspocusProviderConfiguration } from './HocuspocusProvider';
|
|
2
2
|
import { HocuspocusProviderWebsocketConfiguration } from './HocuspocusProviderWebsocket';
|
|
3
|
-
export
|
|
3
|
+
export type HocuspocusCloudProviderConfiguration = Required<Pick<HocuspocusProviderConfiguration, 'name'>> & Partial<HocuspocusProviderConfiguration> & Partial<Pick<HocuspocusProviderWebsocketConfiguration, 'url'>> & AdditionalHocuspocusCloudProviderConfiguration;
|
|
4
4
|
export interface AdditionalHocuspocusCloudProviderConfiguration {
|
|
5
5
|
/**
|
|
6
6
|
* A Hocuspocus Cloud key, get one here: https://hocuspocus.cloud/
|
|
@@ -4,9 +4,9 @@ import * as mutex from 'lib0/mutex';
|
|
|
4
4
|
import type { CloseEvent, Event, MessageEvent } from 'ws';
|
|
5
5
|
import EventEmitter from './EventEmitter';
|
|
6
6
|
import { ConstructableOutgoingMessage, onAuthenticationFailedParameters, onCloseParameters, onDisconnectParameters, onMessageParameters, onOpenParameters, onOutgoingMessageParameters, onStatelessParameters, onStatusParameters, onSyncedParameters, WebSocketStatus } from './types';
|
|
7
|
-
import { HocuspocusProviderWebsocket } from './HocuspocusProviderWebsocket';
|
|
7
|
+
import { CompleteHocuspocusProviderWebsocketConfiguration, HocuspocusProviderWebsocket } from './HocuspocusProviderWebsocket';
|
|
8
8
|
import { onAwarenessChangeParameters, onAwarenessUpdateParameters } from '.';
|
|
9
|
-
export
|
|
9
|
+
export type HocuspocusProviderConfiguration = Required<Pick<CompleteHocuspocusProviderConfiguration, 'name'>> & Partial<CompleteHocuspocusProviderConfiguration> & (Required<Pick<CompleteHocuspocusProviderWebsocketConfiguration, 'url'>> | Required<Pick<CompleteHocuspocusProviderConfiguration, 'websocketProvider'>>);
|
|
10
10
|
export interface CompleteHocuspocusProviderConfiguration {
|
|
11
11
|
/**
|
|
12
12
|
* The identifier/name of your document
|
|
@@ -70,6 +70,7 @@ export declare class HocuspocusProvider extends EventEmitter {
|
|
|
70
70
|
isAuthenticated: boolean;
|
|
71
71
|
mux: mutex.mutex;
|
|
72
72
|
intervals: any;
|
|
73
|
+
isConnected: boolean;
|
|
73
74
|
constructor(configuration: HocuspocusProviderConfiguration);
|
|
74
75
|
onStatus({ status }: onStatusParameters): void;
|
|
75
76
|
setConfiguration(configuration?: Partial<HocuspocusProviderConfiguration>): void;
|
|
@@ -4,7 +4,7 @@ import { Event } from 'ws';
|
|
|
4
4
|
import EventEmitter from './EventEmitter';
|
|
5
5
|
import { onCloseParameters, onDisconnectParameters, onMessageParameters, onOpenParameters, onOutgoingMessageParameters, onStatusParameters, WebSocketStatus } from './types';
|
|
6
6
|
import { HocuspocusProvider, onAwarenessChangeParameters, onAwarenessUpdateParameters } from '.';
|
|
7
|
-
export
|
|
7
|
+
export type HocuspocusProviderWebsocketConfiguration = Required<Pick<CompleteHocuspocusProviderWebsocketConfiguration, 'url'>> & Partial<CompleteHocuspocusProviderWebsocketConfiguration>;
|
|
8
8
|
export interface CompleteHocuspocusProviderWebsocketConfiguration {
|
|
9
9
|
/**
|
|
10
10
|
* URL of your @hocuspocus/server instance
|
|
@@ -0,0 +1,11 @@
|
|
|
1
|
+
import { HocuspocusProvider, HocuspocusProviderConfiguration } from './HocuspocusProvider';
|
|
2
|
+
export type TiptapCollabProviderConfiguration = Required<Pick<HocuspocusProviderConfiguration, 'name'>> & Partial<HocuspocusProviderConfiguration> & AdditionalTiptapCollabProviderConfiguration;
|
|
3
|
+
export interface AdditionalTiptapCollabProviderConfiguration {
|
|
4
|
+
/**
|
|
5
|
+
* A Hocuspocus Cloud App ID, get one here: https://collab.tiptap.dev
|
|
6
|
+
*/
|
|
7
|
+
appId: string;
|
|
8
|
+
}
|
|
9
|
+
export declare class TiptapCollabProvider extends HocuspocusProvider {
|
|
10
|
+
constructor(configuration: TiptapCollabProviderConfiguration);
|
|
11
|
+
}
|
|
@@ -43,42 +43,42 @@ export interface OutgoingMessageArguments {
|
|
|
43
43
|
export interface Constructable<T> {
|
|
44
44
|
new (...args: any): T;
|
|
45
45
|
}
|
|
46
|
-
export
|
|
47
|
-
export
|
|
46
|
+
export type ConstructableOutgoingMessage = Constructable<AuthenticationMessage> | Constructable<AwarenessMessage> | Constructable<QueryAwarenessMessage> | Constructable<SyncStepOneMessage> | Constructable<SyncStepTwoMessage> | Constructable<UpdateMessage>;
|
|
47
|
+
export type onAuthenticationFailedParameters = {
|
|
48
48
|
reason: string;
|
|
49
49
|
};
|
|
50
|
-
export
|
|
50
|
+
export type onOpenParameters = {
|
|
51
51
|
event: Event;
|
|
52
52
|
};
|
|
53
|
-
export
|
|
53
|
+
export type onMessageParameters = {
|
|
54
54
|
event: MessageEvent;
|
|
55
55
|
message: IncomingMessage;
|
|
56
56
|
};
|
|
57
|
-
export
|
|
57
|
+
export type onOutgoingMessageParameters = {
|
|
58
58
|
message: OutgoingMessage;
|
|
59
59
|
};
|
|
60
|
-
export
|
|
60
|
+
export type onStatusParameters = {
|
|
61
61
|
status: WebSocketStatus;
|
|
62
62
|
};
|
|
63
|
-
export
|
|
63
|
+
export type onSyncedParameters = {
|
|
64
64
|
state: boolean;
|
|
65
65
|
};
|
|
66
|
-
export
|
|
66
|
+
export type onDisconnectParameters = {
|
|
67
67
|
event: CloseEvent;
|
|
68
68
|
};
|
|
69
|
-
export
|
|
69
|
+
export type onCloseParameters = {
|
|
70
70
|
event: CloseEvent;
|
|
71
71
|
};
|
|
72
|
-
export
|
|
72
|
+
export type onAwarenessUpdateParameters = {
|
|
73
73
|
states: StatesArray;
|
|
74
74
|
};
|
|
75
|
-
export
|
|
75
|
+
export type onAwarenessChangeParameters = {
|
|
76
76
|
states: StatesArray;
|
|
77
77
|
};
|
|
78
|
-
export
|
|
78
|
+
export type onStatelessParameters = {
|
|
79
79
|
payload: string;
|
|
80
80
|
};
|
|
81
|
-
export
|
|
81
|
+
export type StatesArray = {
|
|
82
82
|
clientId: number;
|
|
83
83
|
[key: string | number]: any;
|
|
84
84
|
}[];
|
|
@@ -1,4 +1,5 @@
|
|
|
1
1
|
/// <reference types="node" />
|
|
2
|
+
/// <reference types="node" />
|
|
2
3
|
import { IncomingMessage as HTTPIncomingMessage } from 'http';
|
|
3
4
|
import AsyncLock from 'async-lock';
|
|
4
5
|
import WebSocket from 'ws';
|
|
@@ -23,7 +24,7 @@ export declare class Connection {
|
|
|
23
24
|
* Constructor.
|
|
24
25
|
*/
|
|
25
26
|
constructor(connection: WebSocket, request: HTTPIncomingMessage, document: Document, timeout: number, socketId: string, context: any, readOnly: boolean | undefined, logger: Debugger);
|
|
26
|
-
boundClose: (event?: CloseEvent
|
|
27
|
+
boundClose: (event?: CloseEvent) => void;
|
|
27
28
|
boundHandleMessage: (data: Uint8Array) => void;
|
|
28
29
|
boundHandlePong: () => void;
|
|
29
30
|
handlePong(): void;
|
|
@@ -1,4 +1,5 @@
|
|
|
1
1
|
/// <reference types="node" />
|
|
2
|
+
/// <reference types="node" />
|
|
2
3
|
import { IncomingMessage, Server as HTTPServer } from 'http';
|
|
3
4
|
import WebSocket, { AddressInfo, WebSocketServer } from 'ws';
|
|
4
5
|
import { Configuration, HookName, HookPayload, onListenPayload } from './types';
|
|
@@ -1,4 +1,6 @@
|
|
|
1
1
|
/// <reference types="node" />
|
|
2
|
+
/// <reference types="node" />
|
|
3
|
+
/// <reference types="node" />
|
|
2
4
|
import { IncomingHttpHeaders, IncomingMessage, ServerResponse } from 'http';
|
|
3
5
|
import { URLSearchParams } from 'url';
|
|
4
6
|
import { Awareness } from 'y-protocols/awareness';
|
|
@@ -47,8 +49,8 @@ export interface Extension {
|
|
|
47
49
|
onDisconnect?(data: onDisconnectPayload): Promise<any>;
|
|
48
50
|
onDestroy?(data: onDestroyPayload): Promise<any>;
|
|
49
51
|
}
|
|
50
|
-
export
|
|
51
|
-
export
|
|
52
|
+
export type HookName = 'onConfigure' | 'onListen' | 'onUpgrade' | 'onConnect' | 'connected' | 'onAuthenticate' | 'onLoadDocument' | 'afterLoadDocument' | 'beforeHandleMessage' | 'beforeBroadcastStateless' | 'onStateless' | 'onChange' | 'onStoreDocument' | 'afterStoreDocument' | 'onAwarenessUpdate' | 'onRequest' | 'onDisconnect' | 'onDestroy';
|
|
53
|
+
export type HookPayload = onConfigurePayload | onListenPayload | onUpgradePayload | onConnectPayload | connectedPayload | onAuthenticatePayload | onLoadDocumentPayload | onStatelessPayload | beforeHandleMessagePayload | beforeBroadcastStatelessPayload | onChangePayload | onStoreDocumentPayload | afterStoreDocumentPayload | onAwarenessUpdatePayload | onRequestPayload | onDisconnectPayload | onDestroyPayload;
|
|
52
54
|
export interface Configuration extends Extension {
|
|
53
55
|
/**
|
|
54
56
|
* A name for the instance, used for logging.
|
|
@@ -208,7 +210,7 @@ export interface onAwarenessUpdatePayload {
|
|
|
208
210
|
awareness: Awareness;
|
|
209
211
|
states: StatesArray;
|
|
210
212
|
}
|
|
211
|
-
export
|
|
213
|
+
export type StatesArray = {
|
|
212
214
|
clientId: number;
|
|
213
215
|
[key: string | number]: any;
|
|
214
216
|
}[];
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
export {};
|
|
@@ -1,2 +1,2 @@
|
|
|
1
1
|
import { Hocuspocus, Configuration } from '@hocuspocus/server';
|
|
2
|
-
export declare const newHocuspocus: (options?: Partial<Configuration>
|
|
2
|
+
export declare const newHocuspocus: (options?: Partial<Configuration>) => Promise<Hocuspocus>;
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@hocuspocus/extension-webhook",
|
|
3
|
-
"version": "2.0.0
|
|
3
|
+
"version": "2.0.0",
|
|
4
4
|
"description": "hocuspocus webhook extension",
|
|
5
5
|
"homepage": "https://hocuspocus.dev",
|
|
6
6
|
"keywords": [
|
|
@@ -29,9 +29,9 @@
|
|
|
29
29
|
"dist"
|
|
30
30
|
],
|
|
31
31
|
"dependencies": {
|
|
32
|
-
"@hocuspocus/common": "^2.0.0
|
|
33
|
-
"@hocuspocus/server": "^2.0.0
|
|
34
|
-
"@hocuspocus/transformer": "^2.0.0
|
|
32
|
+
"@hocuspocus/common": "^2.0.0",
|
|
33
|
+
"@hocuspocus/server": "^2.0.0",
|
|
34
|
+
"@hocuspocus/transformer": "^2.0.0",
|
|
35
35
|
"axios": "^1.2.2"
|
|
36
36
|
},
|
|
37
37
|
"peerDependencies": {
|
package/src/index.ts
CHANGED
|
@@ -8,7 +8,7 @@ import {
|
|
|
8
8
|
} from '@hocuspocus/server'
|
|
9
9
|
import { Doc } from 'yjs'
|
|
10
10
|
import { TiptapTransformer, Transformer } from '@hocuspocus/transformer'
|
|
11
|
-
import axios
|
|
11
|
+
import axios from 'axios'
|
|
12
12
|
import { Forbidden } from '@hocuspocus/common'
|
|
13
13
|
|
|
14
14
|
export enum Events {
|
|
@@ -111,13 +111,17 @@ export class Webhook implements Extension {
|
|
|
111
111
|
}
|
|
112
112
|
|
|
113
113
|
const save = () => {
|
|
114
|
-
|
|
115
|
-
|
|
116
|
-
|
|
117
|
-
|
|
118
|
-
|
|
119
|
-
|
|
120
|
-
|
|
114
|
+
try {
|
|
115
|
+
this.sendRequest(Events.onChange, {
|
|
116
|
+
document: this.configuration.transformer.fromYdoc(data.document),
|
|
117
|
+
documentName: data.documentName,
|
|
118
|
+
context: data.context,
|
|
119
|
+
requestHeaders: data.requestHeaders,
|
|
120
|
+
requestParameters: Object.fromEntries(data.requestParameters.entries()),
|
|
121
|
+
})
|
|
122
|
+
} catch (e) {
|
|
123
|
+
console.error(`Caught error in extension-webhook: ${e}`)
|
|
124
|
+
}
|
|
121
125
|
}
|
|
122
126
|
|
|
123
127
|
if (!this.configuration.debounce) {
|
|
@@ -135,25 +139,29 @@ export class Webhook implements Extension {
|
|
|
135
139
|
return
|
|
136
140
|
}
|
|
137
141
|
|
|
138
|
-
|
|
139
|
-
|
|
140
|
-
|
|
141
|
-
|
|
142
|
-
|
|
142
|
+
try {
|
|
143
|
+
const response = await this.sendRequest(Events.onCreate, {
|
|
144
|
+
documentName: data.documentName,
|
|
145
|
+
requestHeaders: data.requestHeaders,
|
|
146
|
+
requestParameters: Object.fromEntries(data.requestParameters.entries()),
|
|
147
|
+
})
|
|
143
148
|
|
|
144
|
-
|
|
149
|
+
if (response.status !== 200 || !response.data) return
|
|
145
150
|
|
|
146
|
-
|
|
147
|
-
|
|
148
|
-
|
|
151
|
+
const document = typeof response.data === 'string'
|
|
152
|
+
? JSON.parse(response.data)
|
|
153
|
+
: response.data
|
|
149
154
|
|
|
150
|
-
|
|
151
|
-
|
|
152
|
-
|
|
153
|
-
|
|
154
|
-
|
|
155
|
-
|
|
155
|
+
// eslint-disable-next-line guard-for-in,no-restricted-syntax
|
|
156
|
+
for (const fieldName in document) {
|
|
157
|
+
if (data.document.isEmpty(fieldName)) {
|
|
158
|
+
data.document.merge(
|
|
159
|
+
this.configuration.transformer.toYdoc(document[fieldName], fieldName),
|
|
160
|
+
)
|
|
161
|
+
}
|
|
156
162
|
}
|
|
163
|
+
} catch (e) {
|
|
164
|
+
console.error(`Caught error in extension-webhook: ${e}`)
|
|
157
165
|
}
|
|
158
166
|
}
|
|
159
167
|
|
|
@@ -166,13 +174,13 @@ export class Webhook implements Extension {
|
|
|
166
174
|
}
|
|
167
175
|
|
|
168
176
|
try {
|
|
169
|
-
const response =
|
|
177
|
+
const response = await this.sendRequest(Events.onConnect, {
|
|
170
178
|
documentName: data.documentName,
|
|
171
179
|
requestHeaders: data.requestHeaders,
|
|
172
180
|
requestParameters: Object.fromEntries(data.requestParameters.entries()),
|
|
173
181
|
})
|
|
174
182
|
|
|
175
|
-
return typeof response.data === 'string'
|
|
183
|
+
return typeof response.data === 'string' && response.data.length > 0
|
|
176
184
|
? JSON.parse(response.data)
|
|
177
185
|
: response.data
|
|
178
186
|
} catch (e) {
|
|
@@ -186,12 +194,16 @@ export class Webhook implements Extension {
|
|
|
186
194
|
return
|
|
187
195
|
}
|
|
188
196
|
|
|
189
|
-
|
|
190
|
-
|
|
191
|
-
|
|
192
|
-
|
|
193
|
-
|
|
194
|
-
|
|
197
|
+
try {
|
|
198
|
+
await this.sendRequest(Events.onDisconnect, {
|
|
199
|
+
documentName: data.documentName,
|
|
200
|
+
requestHeaders: data.requestHeaders,
|
|
201
|
+
requestParameters: Object.fromEntries(data.requestParameters.entries()),
|
|
202
|
+
context: data.context,
|
|
203
|
+
})
|
|
204
|
+
} catch (e) {
|
|
205
|
+
console.error(`Caught error in extension-webhook: ${e}`)
|
|
206
|
+
}
|
|
195
207
|
}
|
|
196
208
|
|
|
197
209
|
}
|