@hocuspocus/extension-webhook 4.0.0-rc.2 → 4.0.0-rc.4
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.
|
@@ -1,36 +1,7 @@
|
|
|
1
1
|
Object.defineProperty(exports, Symbol.toStringTag, { value: 'Module' });
|
|
2
|
-
//#region \0rolldown/runtime.js
|
|
3
|
-
var __create = Object.create;
|
|
4
|
-
var __defProp = Object.defineProperty;
|
|
5
|
-
var __getOwnPropDesc = Object.getOwnPropertyDescriptor;
|
|
6
|
-
var __getOwnPropNames = Object.getOwnPropertyNames;
|
|
7
|
-
var __getProtoOf = Object.getPrototypeOf;
|
|
8
|
-
var __hasOwnProp = Object.prototype.hasOwnProperty;
|
|
9
|
-
var __copyProps = (to, from, except, desc) => {
|
|
10
|
-
if (from && typeof from === "object" || typeof from === "function") {
|
|
11
|
-
for (var keys = __getOwnPropNames(from), i = 0, n = keys.length, key; i < n; i++) {
|
|
12
|
-
key = keys[i];
|
|
13
|
-
if (!__hasOwnProp.call(to, key) && key !== except) {
|
|
14
|
-
__defProp(to, key, {
|
|
15
|
-
get: ((k) => from[k]).bind(null, key),
|
|
16
|
-
enumerable: !(desc = __getOwnPropDesc(from, key)) || desc.enumerable
|
|
17
|
-
});
|
|
18
|
-
}
|
|
19
|
-
}
|
|
20
|
-
}
|
|
21
|
-
return to;
|
|
22
|
-
};
|
|
23
|
-
var __toESM = (mod, isNodeMode, target) => (target = mod != null ? __create(__getProtoOf(mod)) : {}, __copyProps(isNodeMode || !mod || !mod.__esModule ? __defProp(target, "default", {
|
|
24
|
-
value: mod,
|
|
25
|
-
enumerable: true
|
|
26
|
-
}) : target, mod));
|
|
27
|
-
|
|
28
|
-
//#endregion
|
|
29
2
|
let node_crypto = require("node:crypto");
|
|
30
3
|
let _hocuspocus_common = require("@hocuspocus/common");
|
|
31
4
|
let _hocuspocus_transformer = require("@hocuspocus/transformer");
|
|
32
|
-
let axios = require("axios");
|
|
33
|
-
axios = __toESM(axios);
|
|
34
5
|
|
|
35
6
|
//#region packages/extension-webhook/src/index.ts
|
|
36
7
|
let Events = /* @__PURE__ */ function(Events) {
|
|
@@ -91,10 +62,21 @@ var Webhook = class {
|
|
|
91
62
|
event,
|
|
92
63
|
payload
|
|
93
64
|
});
|
|
94
|
-
|
|
95
|
-
"
|
|
96
|
-
|
|
97
|
-
|
|
65
|
+
const response = await fetch(this.configuration.url, {
|
|
66
|
+
method: "POST",
|
|
67
|
+
body: json,
|
|
68
|
+
headers: {
|
|
69
|
+
"X-Hocuspocus-Signature-256": this.createSignature(json),
|
|
70
|
+
"Content-Type": "application/json"
|
|
71
|
+
}
|
|
72
|
+
});
|
|
73
|
+
if (!response.ok) throw new Error(`Webhook request to ${this.configuration.url} failed with status ${response.status}`);
|
|
74
|
+
const text = await response.text();
|
|
75
|
+
const data = (response.headers.get("content-type") ?? "").includes("application/json") && text ? JSON.parse(text) : text;
|
|
76
|
+
return {
|
|
77
|
+
status: response.status,
|
|
78
|
+
data
|
|
79
|
+
};
|
|
98
80
|
}
|
|
99
81
|
/**
|
|
100
82
|
* onChange hook
|
|
@@ -123,13 +105,12 @@ var Webhook = class {
|
|
|
123
105
|
async onLoadDocument(data) {
|
|
124
106
|
if (!this.configuration.events.includes(Events.onCreate)) return;
|
|
125
107
|
try {
|
|
126
|
-
const
|
|
108
|
+
const document = (await this.sendRequest(Events.onCreate, {
|
|
127
109
|
documentName: data.documentName,
|
|
128
110
|
requestHeaders: data.requestHeaders,
|
|
129
111
|
requestParameters: Object.fromEntries(data.requestParameters.entries())
|
|
130
|
-
});
|
|
131
|
-
if (
|
|
132
|
-
const document = typeof response.data === "string" ? JSON.parse(response.data) : response.data;
|
|
112
|
+
})).data;
|
|
113
|
+
if (!document) return;
|
|
133
114
|
for (const fieldName in document) if (data.document.isEmpty(fieldName)) data.document.merge(this.configuration.transformer.toYdoc(document[fieldName], fieldName));
|
|
134
115
|
} catch (e) {
|
|
135
116
|
console.error(`Caught error in extension-webhook: ${e}`);
|
|
@@ -141,12 +122,11 @@ var Webhook = class {
|
|
|
141
122
|
async onConnect(data) {
|
|
142
123
|
if (!this.configuration.events.includes(Events.onConnect)) return;
|
|
143
124
|
try {
|
|
144
|
-
|
|
125
|
+
return (await this.sendRequest(Events.onConnect, {
|
|
145
126
|
documentName: data.documentName,
|
|
146
127
|
requestHeaders: data.requestHeaders,
|
|
147
128
|
requestParameters: Object.fromEntries(data.requestParameters.entries())
|
|
148
|
-
});
|
|
149
|
-
return typeof response.data === "string" && response.data.length > 0 ? JSON.parse(response.data) : response.data;
|
|
129
|
+
})).data;
|
|
150
130
|
} catch (e) {
|
|
151
131
|
console.error(`Caught error in extension-webhook: ${e}`);
|
|
152
132
|
throw _hocuspocus_common.Forbidden;
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"hocuspocus-webhook.cjs","names":["TiptapTransformer","Forbidden"],"sources":["../src/index.ts"],"sourcesContent":["import { createHmac } from \"node:crypto\";\nimport { Forbidden } from \"@hocuspocus/common\";\nimport type {\n\tExtension,\n\tonChangePayload,\n\tonConnectPayload,\n\tonDisconnectPayload,\n\tonLoadDocumentPayload,\n} from \"@hocuspocus/server\";\nimport type { Transformer } from \"@hocuspocus/transformer\";\nimport { TiptapTransformer } from \"@hocuspocus/transformer\";\nimport
|
|
1
|
+
{"version":3,"file":"hocuspocus-webhook.cjs","names":["TiptapTransformer","Forbidden"],"sources":["../src/index.ts"],"sourcesContent":["import { createHmac } from \"node:crypto\";\nimport { Forbidden } from \"@hocuspocus/common\";\nimport type {\n\tExtension,\n\tonChangePayload,\n\tonConnectPayload,\n\tonDisconnectPayload,\n\tonLoadDocumentPayload,\n} from \"@hocuspocus/server\";\nimport type { Transformer } from \"@hocuspocus/transformer\";\nimport { TiptapTransformer } from \"@hocuspocus/transformer\";\nimport type { Doc } from \"yjs\";\n\nexport enum Events {\n\tonChange = \"change\",\n\tonConnect = \"connect\",\n\tonCreate = \"create\",\n\tonDisconnect = \"disconnect\",\n}\n\nexport interface Configuration {\n\tdebounce: number | false | null;\n\tdebounceMaxWait: number;\n\tsecret: string;\n\ttransformer:\n\t\t| Transformer\n\t\t| {\n\t\t\t\ttoYdoc: (document: any) => Doc;\n\t\t\t\tfromYdoc: (document: Doc) => any;\n\t\t };\n\turl: string;\n\tevents: Array<Events>;\n}\n\nexport class Webhook implements Extension {\n\tconfiguration: Configuration = {\n\t\tdebounce: 2000,\n\t\tdebounceMaxWait: 10000,\n\t\tsecret: \"\",\n\t\ttransformer: TiptapTransformer,\n\t\turl: \"\",\n\t\tevents: [Events.onChange],\n\t};\n\n\tdebounced: Map<string, { timeout: NodeJS.Timeout; start: number }> =\n\t\tnew Map();\n\n\t/**\n\t * Constructor\n\t */\n\tconstructor(configuration?: Partial<Configuration>) {\n\t\tthis.configuration = {\n\t\t\t...this.configuration,\n\t\t\t...configuration,\n\t\t};\n\n\t\tif (!this.configuration.url) {\n\t\t\tthrow new Error(\"url is required!\");\n\t\t}\n\t}\n\n\t/**\n\t * Create a signature for the response body\n\t */\n\tcreateSignature(body: string): string {\n\t\tconst hmac = createHmac(\"sha256\", this.configuration.secret);\n\n\t\treturn `sha256=${hmac.update(body).digest(\"hex\")}`;\n\t}\n\n\t/**\n\t * debounce the given function, using the given identifier\n\t */\n\t// eslint-disable-next-line @typescript-eslint/no-unsafe-function-type\n\tdebounce(id: string, func: Function) {\n\t\tconst old = this.debounced.get(id);\n\t\tconst start = old?.start || Date.now();\n\n\t\tconst run = () => {\n\t\t\tthis.debounced.delete(id);\n\t\t\tfunc();\n\t\t};\n\n\t\tif (old?.timeout) clearTimeout(old.timeout);\n\t\tif (Date.now() - start >= this.configuration.debounceMaxWait) return run();\n\n\t\tthis.debounced.set(id, {\n\t\t\tstart,\n\t\t\ttimeout: setTimeout(run, <number>this.configuration.debounce),\n\t\t});\n\t}\n\n\t/**\n\t * Send a request to the given url containing the given data\n\t */\n\tasync sendRequest(event: Events, payload: any) {\n\t\tconst json = JSON.stringify({ event, payload });\n\n\t\tconst response = await fetch(this.configuration.url, {\n\t\t\tmethod: \"POST\",\n\t\t\tbody: json,\n\t\t\theaders: {\n\t\t\t\t\"X-Hocuspocus-Signature-256\": this.createSignature(json),\n\t\t\t\t\"Content-Type\": \"application/json\",\n\t\t\t},\n\t\t});\n\n\t\tif (!response.ok) {\n\t\t\tthrow new Error(\n\t\t\t\t`Webhook request to ${this.configuration.url} failed with status ${response.status}`,\n\t\t\t);\n\t\t}\n\n\t\tconst text = await response.text();\n\t\tconst contentType = response.headers.get(\"content-type\") ?? \"\";\n\t\tconst data =\n\t\t\tcontentType.includes(\"application/json\") && text\n\t\t\t\t? JSON.parse(text)\n\t\t\t\t: text;\n\n\t\treturn { status: response.status, data };\n\t}\n\n\t/**\n\t * onChange hook\n\t */\n\tasync onChange(data: onChangePayload) {\n\t\tif (!this.configuration.events.includes(Events.onChange)) {\n\t\t\treturn;\n\t\t}\n\n\t\tconst save = async () => {\n\t\t\ttry {\n\t\t\t\tawait this.sendRequest(Events.onChange, {\n\t\t\t\t\tdocument: this.configuration.transformer.fromYdoc(data.document),\n\t\t\t\t\tdocumentName: data.documentName,\n\t\t\t\t\tcontext: data.context,\n\t\t\t\t\trequestHeaders: data.requestHeaders,\n\t\t\t\t\trequestParameters: Object.fromEntries(\n\t\t\t\t\t\tdata.requestParameters.entries(),\n\t\t\t\t\t),\n\t\t\t\t});\n\t\t\t} catch (e) {\n\t\t\t\tconsole.error(`Caught error in extension-webhook: ${e}`);\n\t\t\t}\n\t\t};\n\n\t\tif (!this.configuration.debounce) {\n\t\t\treturn save();\n\t\t}\n\n\t\tthis.debounce(data.documentName, save);\n\t}\n\n\t/**\n\t * onLoadDocument hook\n\t */\n\tasync onLoadDocument(data: onLoadDocumentPayload) {\n\t\tif (!this.configuration.events.includes(Events.onCreate)) {\n\t\t\treturn;\n\t\t}\n\n\t\ttry {\n\t\t\tconst response = await this.sendRequest(Events.onCreate, {\n\t\t\t\tdocumentName: data.documentName,\n\t\t\t\trequestHeaders: data.requestHeaders,\n\t\t\t\trequestParameters: Object.fromEntries(data.requestParameters.entries()),\n\t\t\t});\n\n\t\t\tconst document = response.data;\n\t\t\tif (!document) return;\n\n\t\t\t// eslint-disable-next-line guard-for-in,no-restricted-syntax\n\t\t\tfor (const fieldName in document) {\n\t\t\t\tif (data.document.isEmpty(fieldName)) {\n\t\t\t\t\tdata.document.merge(\n\t\t\t\t\t\tthis.configuration.transformer.toYdoc(\n\t\t\t\t\t\t\tdocument[fieldName],\n\t\t\t\t\t\t\tfieldName,\n\t\t\t\t\t\t),\n\t\t\t\t\t);\n\t\t\t\t}\n\t\t\t}\n\t\t} catch (e) {\n\t\t\tconsole.error(`Caught error in extension-webhook: ${e}`);\n\t\t}\n\t}\n\n\t/**\n\t * onConnect hook\n\t */\n\tasync onConnect(data: onConnectPayload) {\n\t\tif (!this.configuration.events.includes(Events.onConnect)) {\n\t\t\treturn;\n\t\t}\n\n\t\ttry {\n\t\t\tconst response = await this.sendRequest(Events.onConnect, {\n\t\t\t\tdocumentName: data.documentName,\n\t\t\t\trequestHeaders: data.requestHeaders,\n\t\t\t\trequestParameters: Object.fromEntries(data.requestParameters.entries()),\n\t\t\t});\n\n\t\t\treturn response.data;\n\t\t} catch (e) {\n\t\t\tconsole.error(`Caught error in extension-webhook: ${e}`);\n\t\t\tthrow Forbidden;\n\t\t}\n\t}\n\n\tasync onDisconnect(data: onDisconnectPayload) {\n\t\tif (!this.configuration.events.includes(Events.onDisconnect)) {\n\t\t\treturn;\n\t\t}\n\n\t\ttry {\n\t\t\tawait this.sendRequest(Events.onDisconnect, {\n\t\t\t\tdocumentName: data.documentName,\n\t\t\t\trequestHeaders: data.requestHeaders,\n\t\t\t\trequestParameters: Object.fromEntries(data.requestParameters.entries()),\n\t\t\t\tcontext: data.context,\n\t\t\t});\n\t\t} catch (e) {\n\t\t\tconsole.error(`Caught error in extension-webhook: ${e}`);\n\t\t}\n\t}\n}\n"],"mappings":";;;;;;AAaA,IAAY,SAAL;AACN;AACA;AACA;AACA;;KACA;AAgBD,IAAa,UAAb,MAA0C;;;;CAgBzC,YAAY,eAAwC;uBAfrB;GAC9B,UAAU;GACV,iBAAiB;GACjB,QAAQ;GACR,aAAaA;GACb,KAAK;GACL,QAAQ,CAAC,OAAO,SAAS;GACzB;mCAGA,IAAI,KAAK;AAMT,OAAK,gBAAgB;GACpB,GAAG,KAAK;GACR,GAAG;GACH;AAED,MAAI,CAAC,KAAK,cAAc,IACvB,OAAM,IAAI,MAAM,mBAAmB;;;;;CAOrC,gBAAgB,MAAsB;AAGrC,SAAO,sCAFiB,UAAU,KAAK,cAAc,OAAO,CAEtC,OAAO,KAAK,CAAC,OAAO,MAAM;;;;;CAOjD,SAAS,IAAY,MAAgB;EACpC,MAAM,MAAM,KAAK,UAAU,IAAI,GAAG;EAClC,MAAM,QAAQ,KAAK,SAAS,KAAK,KAAK;EAEtC,MAAM,YAAY;AACjB,QAAK,UAAU,OAAO,GAAG;AACzB,SAAM;;AAGP,MAAI,KAAK,QAAS,cAAa,IAAI,QAAQ;AAC3C,MAAI,KAAK,KAAK,GAAG,SAAS,KAAK,cAAc,gBAAiB,QAAO,KAAK;AAE1E,OAAK,UAAU,IAAI,IAAI;GACtB;GACA,SAAS,WAAW,KAAa,KAAK,cAAc,SAAS;GAC7D,CAAC;;;;;CAMH,MAAM,YAAY,OAAe,SAAc;EAC9C,MAAM,OAAO,KAAK,UAAU;GAAE;GAAO;GAAS,CAAC;EAE/C,MAAM,WAAW,MAAM,MAAM,KAAK,cAAc,KAAK;GACpD,QAAQ;GACR,MAAM;GACN,SAAS;IACR,8BAA8B,KAAK,gBAAgB,KAAK;IACxD,gBAAgB;IAChB;GACD,CAAC;AAEF,MAAI,CAAC,SAAS,GACb,OAAM,IAAI,MACT,sBAAsB,KAAK,cAAc,IAAI,sBAAsB,SAAS,SAC5E;EAGF,MAAM,OAAO,MAAM,SAAS,MAAM;EAElC,MAAM,QADc,SAAS,QAAQ,IAAI,eAAe,IAAI,IAE/C,SAAS,mBAAmB,IAAI,OACzC,KAAK,MAAM,KAAK,GAChB;AAEJ,SAAO;GAAE,QAAQ,SAAS;GAAQ;GAAM;;;;;CAMzC,MAAM,SAAS,MAAuB;AACrC,MAAI,CAAC,KAAK,cAAc,OAAO,SAAS,OAAO,SAAS,CACvD;EAGD,MAAM,OAAO,YAAY;AACxB,OAAI;AACH,UAAM,KAAK,YAAY,OAAO,UAAU;KACvC,UAAU,KAAK,cAAc,YAAY,SAAS,KAAK,SAAS;KAChE,cAAc,KAAK;KACnB,SAAS,KAAK;KACd,gBAAgB,KAAK;KACrB,mBAAmB,OAAO,YACzB,KAAK,kBAAkB,SAAS,CAChC;KACD,CAAC;YACM,GAAG;AACX,YAAQ,MAAM,sCAAsC,IAAI;;;AAI1D,MAAI,CAAC,KAAK,cAAc,SACvB,QAAO,MAAM;AAGd,OAAK,SAAS,KAAK,cAAc,KAAK;;;;;CAMvC,MAAM,eAAe,MAA6B;AACjD,MAAI,CAAC,KAAK,cAAc,OAAO,SAAS,OAAO,SAAS,CACvD;AAGD,MAAI;GAOH,MAAM,YANW,MAAM,KAAK,YAAY,OAAO,UAAU;IACxD,cAAc,KAAK;IACnB,gBAAgB,KAAK;IACrB,mBAAmB,OAAO,YAAY,KAAK,kBAAkB,SAAS,CAAC;IACvE,CAAC,EAEwB;AAC1B,OAAI,CAAC,SAAU;AAGf,QAAK,MAAM,aAAa,SACvB,KAAI,KAAK,SAAS,QAAQ,UAAU,CACnC,MAAK,SAAS,MACb,KAAK,cAAc,YAAY,OAC9B,SAAS,YACT,UACA,CACD;WAGK,GAAG;AACX,WAAQ,MAAM,sCAAsC,IAAI;;;;;;CAO1D,MAAM,UAAU,MAAwB;AACvC,MAAI,CAAC,KAAK,cAAc,OAAO,SAAS,OAAO,UAAU,CACxD;AAGD,MAAI;AAOH,WANiB,MAAM,KAAK,YAAY,OAAO,WAAW;IACzD,cAAc,KAAK;IACnB,gBAAgB,KAAK;IACrB,mBAAmB,OAAO,YAAY,KAAK,kBAAkB,SAAS,CAAC;IACvE,CAAC,EAEc;WACR,GAAG;AACX,WAAQ,MAAM,sCAAsC,IAAI;AACxD,SAAMC;;;CAIR,MAAM,aAAa,MAA2B;AAC7C,MAAI,CAAC,KAAK,cAAc,OAAO,SAAS,OAAO,aAAa,CAC3D;AAGD,MAAI;AACH,SAAM,KAAK,YAAY,OAAO,cAAc;IAC3C,cAAc,KAAK;IACnB,gBAAgB,KAAK;IACrB,mBAAmB,OAAO,YAAY,KAAK,kBAAkB,SAAS,CAAC;IACvE,SAAS,KAAK;IACd,CAAC;WACM,GAAG;AACX,WAAQ,MAAM,sCAAsC,IAAI"}
|
|
@@ -1,7 +1,6 @@
|
|
|
1
1
|
import { createHmac } from "node:crypto";
|
|
2
2
|
import { Forbidden } from "@hocuspocus/common";
|
|
3
3
|
import { TiptapTransformer } from "@hocuspocus/transformer";
|
|
4
|
-
import axios from "axios";
|
|
5
4
|
|
|
6
5
|
//#region packages/extension-webhook/src/index.ts
|
|
7
6
|
let Events = /* @__PURE__ */ function(Events) {
|
|
@@ -62,10 +61,21 @@ var Webhook = class {
|
|
|
62
61
|
event,
|
|
63
62
|
payload
|
|
64
63
|
});
|
|
65
|
-
|
|
66
|
-
"
|
|
67
|
-
|
|
68
|
-
|
|
64
|
+
const response = await fetch(this.configuration.url, {
|
|
65
|
+
method: "POST",
|
|
66
|
+
body: json,
|
|
67
|
+
headers: {
|
|
68
|
+
"X-Hocuspocus-Signature-256": this.createSignature(json),
|
|
69
|
+
"Content-Type": "application/json"
|
|
70
|
+
}
|
|
71
|
+
});
|
|
72
|
+
if (!response.ok) throw new Error(`Webhook request to ${this.configuration.url} failed with status ${response.status}`);
|
|
73
|
+
const text = await response.text();
|
|
74
|
+
const data = (response.headers.get("content-type") ?? "").includes("application/json") && text ? JSON.parse(text) : text;
|
|
75
|
+
return {
|
|
76
|
+
status: response.status,
|
|
77
|
+
data
|
|
78
|
+
};
|
|
69
79
|
}
|
|
70
80
|
/**
|
|
71
81
|
* onChange hook
|
|
@@ -94,13 +104,12 @@ var Webhook = class {
|
|
|
94
104
|
async onLoadDocument(data) {
|
|
95
105
|
if (!this.configuration.events.includes(Events.onCreate)) return;
|
|
96
106
|
try {
|
|
97
|
-
const
|
|
107
|
+
const document = (await this.sendRequest(Events.onCreate, {
|
|
98
108
|
documentName: data.documentName,
|
|
99
109
|
requestHeaders: data.requestHeaders,
|
|
100
110
|
requestParameters: Object.fromEntries(data.requestParameters.entries())
|
|
101
|
-
});
|
|
102
|
-
if (
|
|
103
|
-
const document = typeof response.data === "string" ? JSON.parse(response.data) : response.data;
|
|
111
|
+
})).data;
|
|
112
|
+
if (!document) return;
|
|
104
113
|
for (const fieldName in document) if (data.document.isEmpty(fieldName)) data.document.merge(this.configuration.transformer.toYdoc(document[fieldName], fieldName));
|
|
105
114
|
} catch (e) {
|
|
106
115
|
console.error(`Caught error in extension-webhook: ${e}`);
|
|
@@ -112,12 +121,11 @@ var Webhook = class {
|
|
|
112
121
|
async onConnect(data) {
|
|
113
122
|
if (!this.configuration.events.includes(Events.onConnect)) return;
|
|
114
123
|
try {
|
|
115
|
-
|
|
124
|
+
return (await this.sendRequest(Events.onConnect, {
|
|
116
125
|
documentName: data.documentName,
|
|
117
126
|
requestHeaders: data.requestHeaders,
|
|
118
127
|
requestParameters: Object.fromEntries(data.requestParameters.entries())
|
|
119
|
-
});
|
|
120
|
-
return typeof response.data === "string" && response.data.length > 0 ? JSON.parse(response.data) : response.data;
|
|
128
|
+
})).data;
|
|
121
129
|
} catch (e) {
|
|
122
130
|
console.error(`Caught error in extension-webhook: ${e}`);
|
|
123
131
|
throw Forbidden;
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"hocuspocus-webhook.esm.js","names":[],"sources":["../src/index.ts"],"sourcesContent":["import { createHmac } from \"node:crypto\";\nimport { Forbidden } from \"@hocuspocus/common\";\nimport type {\n\tExtension,\n\tonChangePayload,\n\tonConnectPayload,\n\tonDisconnectPayload,\n\tonLoadDocumentPayload,\n} from \"@hocuspocus/server\";\nimport type { Transformer } from \"@hocuspocus/transformer\";\nimport { TiptapTransformer } from \"@hocuspocus/transformer\";\nimport
|
|
1
|
+
{"version":3,"file":"hocuspocus-webhook.esm.js","names":[],"sources":["../src/index.ts"],"sourcesContent":["import { createHmac } from \"node:crypto\";\nimport { Forbidden } from \"@hocuspocus/common\";\nimport type {\n\tExtension,\n\tonChangePayload,\n\tonConnectPayload,\n\tonDisconnectPayload,\n\tonLoadDocumentPayload,\n} from \"@hocuspocus/server\";\nimport type { Transformer } from \"@hocuspocus/transformer\";\nimport { TiptapTransformer } from \"@hocuspocus/transformer\";\nimport type { Doc } from \"yjs\";\n\nexport enum Events {\n\tonChange = \"change\",\n\tonConnect = \"connect\",\n\tonCreate = \"create\",\n\tonDisconnect = \"disconnect\",\n}\n\nexport interface Configuration {\n\tdebounce: number | false | null;\n\tdebounceMaxWait: number;\n\tsecret: string;\n\ttransformer:\n\t\t| Transformer\n\t\t| {\n\t\t\t\ttoYdoc: (document: any) => Doc;\n\t\t\t\tfromYdoc: (document: Doc) => any;\n\t\t };\n\turl: string;\n\tevents: Array<Events>;\n}\n\nexport class Webhook implements Extension {\n\tconfiguration: Configuration = {\n\t\tdebounce: 2000,\n\t\tdebounceMaxWait: 10000,\n\t\tsecret: \"\",\n\t\ttransformer: TiptapTransformer,\n\t\turl: \"\",\n\t\tevents: [Events.onChange],\n\t};\n\n\tdebounced: Map<string, { timeout: NodeJS.Timeout; start: number }> =\n\t\tnew Map();\n\n\t/**\n\t * Constructor\n\t */\n\tconstructor(configuration?: Partial<Configuration>) {\n\t\tthis.configuration = {\n\t\t\t...this.configuration,\n\t\t\t...configuration,\n\t\t};\n\n\t\tif (!this.configuration.url) {\n\t\t\tthrow new Error(\"url is required!\");\n\t\t}\n\t}\n\n\t/**\n\t * Create a signature for the response body\n\t */\n\tcreateSignature(body: string): string {\n\t\tconst hmac = createHmac(\"sha256\", this.configuration.secret);\n\n\t\treturn `sha256=${hmac.update(body).digest(\"hex\")}`;\n\t}\n\n\t/**\n\t * debounce the given function, using the given identifier\n\t */\n\t// eslint-disable-next-line @typescript-eslint/no-unsafe-function-type\n\tdebounce(id: string, func: Function) {\n\t\tconst old = this.debounced.get(id);\n\t\tconst start = old?.start || Date.now();\n\n\t\tconst run = () => {\n\t\t\tthis.debounced.delete(id);\n\t\t\tfunc();\n\t\t};\n\n\t\tif (old?.timeout) clearTimeout(old.timeout);\n\t\tif (Date.now() - start >= this.configuration.debounceMaxWait) return run();\n\n\t\tthis.debounced.set(id, {\n\t\t\tstart,\n\t\t\ttimeout: setTimeout(run, <number>this.configuration.debounce),\n\t\t});\n\t}\n\n\t/**\n\t * Send a request to the given url containing the given data\n\t */\n\tasync sendRequest(event: Events, payload: any) {\n\t\tconst json = JSON.stringify({ event, payload });\n\n\t\tconst response = await fetch(this.configuration.url, {\n\t\t\tmethod: \"POST\",\n\t\t\tbody: json,\n\t\t\theaders: {\n\t\t\t\t\"X-Hocuspocus-Signature-256\": this.createSignature(json),\n\t\t\t\t\"Content-Type\": \"application/json\",\n\t\t\t},\n\t\t});\n\n\t\tif (!response.ok) {\n\t\t\tthrow new Error(\n\t\t\t\t`Webhook request to ${this.configuration.url} failed with status ${response.status}`,\n\t\t\t);\n\t\t}\n\n\t\tconst text = await response.text();\n\t\tconst contentType = response.headers.get(\"content-type\") ?? \"\";\n\t\tconst data =\n\t\t\tcontentType.includes(\"application/json\") && text\n\t\t\t\t? JSON.parse(text)\n\t\t\t\t: text;\n\n\t\treturn { status: response.status, data };\n\t}\n\n\t/**\n\t * onChange hook\n\t */\n\tasync onChange(data: onChangePayload) {\n\t\tif (!this.configuration.events.includes(Events.onChange)) {\n\t\t\treturn;\n\t\t}\n\n\t\tconst save = async () => {\n\t\t\ttry {\n\t\t\t\tawait this.sendRequest(Events.onChange, {\n\t\t\t\t\tdocument: this.configuration.transformer.fromYdoc(data.document),\n\t\t\t\t\tdocumentName: data.documentName,\n\t\t\t\t\tcontext: data.context,\n\t\t\t\t\trequestHeaders: data.requestHeaders,\n\t\t\t\t\trequestParameters: Object.fromEntries(\n\t\t\t\t\t\tdata.requestParameters.entries(),\n\t\t\t\t\t),\n\t\t\t\t});\n\t\t\t} catch (e) {\n\t\t\t\tconsole.error(`Caught error in extension-webhook: ${e}`);\n\t\t\t}\n\t\t};\n\n\t\tif (!this.configuration.debounce) {\n\t\t\treturn save();\n\t\t}\n\n\t\tthis.debounce(data.documentName, save);\n\t}\n\n\t/**\n\t * onLoadDocument hook\n\t */\n\tasync onLoadDocument(data: onLoadDocumentPayload) {\n\t\tif (!this.configuration.events.includes(Events.onCreate)) {\n\t\t\treturn;\n\t\t}\n\n\t\ttry {\n\t\t\tconst response = await this.sendRequest(Events.onCreate, {\n\t\t\t\tdocumentName: data.documentName,\n\t\t\t\trequestHeaders: data.requestHeaders,\n\t\t\t\trequestParameters: Object.fromEntries(data.requestParameters.entries()),\n\t\t\t});\n\n\t\t\tconst document = response.data;\n\t\t\tif (!document) return;\n\n\t\t\t// eslint-disable-next-line guard-for-in,no-restricted-syntax\n\t\t\tfor (const fieldName in document) {\n\t\t\t\tif (data.document.isEmpty(fieldName)) {\n\t\t\t\t\tdata.document.merge(\n\t\t\t\t\t\tthis.configuration.transformer.toYdoc(\n\t\t\t\t\t\t\tdocument[fieldName],\n\t\t\t\t\t\t\tfieldName,\n\t\t\t\t\t\t),\n\t\t\t\t\t);\n\t\t\t\t}\n\t\t\t}\n\t\t} catch (e) {\n\t\t\tconsole.error(`Caught error in extension-webhook: ${e}`);\n\t\t}\n\t}\n\n\t/**\n\t * onConnect hook\n\t */\n\tasync onConnect(data: onConnectPayload) {\n\t\tif (!this.configuration.events.includes(Events.onConnect)) {\n\t\t\treturn;\n\t\t}\n\n\t\ttry {\n\t\t\tconst response = await this.sendRequest(Events.onConnect, {\n\t\t\t\tdocumentName: data.documentName,\n\t\t\t\trequestHeaders: data.requestHeaders,\n\t\t\t\trequestParameters: Object.fromEntries(data.requestParameters.entries()),\n\t\t\t});\n\n\t\t\treturn response.data;\n\t\t} catch (e) {\n\t\t\tconsole.error(`Caught error in extension-webhook: ${e}`);\n\t\t\tthrow Forbidden;\n\t\t}\n\t}\n\n\tasync onDisconnect(data: onDisconnectPayload) {\n\t\tif (!this.configuration.events.includes(Events.onDisconnect)) {\n\t\t\treturn;\n\t\t}\n\n\t\ttry {\n\t\t\tawait this.sendRequest(Events.onDisconnect, {\n\t\t\t\tdocumentName: data.documentName,\n\t\t\t\trequestHeaders: data.requestHeaders,\n\t\t\t\trequestParameters: Object.fromEntries(data.requestParameters.entries()),\n\t\t\t\tcontext: data.context,\n\t\t\t});\n\t\t} catch (e) {\n\t\t\tconsole.error(`Caught error in extension-webhook: ${e}`);\n\t\t}\n\t}\n}\n"],"mappings":"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"}
|
package/dist/index.d.ts
CHANGED
|
@@ -1,4 +1,3 @@
|
|
|
1
|
-
import * as axios from "axios";
|
|
2
1
|
import { Extension, onChangePayload, onConnectPayload, onDisconnectPayload, onLoadDocumentPayload } from "@hocuspocus/server";
|
|
3
2
|
import { Transformer } from "@hocuspocus/transformer";
|
|
4
3
|
import { Doc } from "yjs";
|
|
@@ -42,7 +41,10 @@ declare class Webhook implements Extension {
|
|
|
42
41
|
/**
|
|
43
42
|
* Send a request to the given url containing the given data
|
|
44
43
|
*/
|
|
45
|
-
sendRequest(event: Events, payload: any): Promise<
|
|
44
|
+
sendRequest(event: Events, payload: any): Promise<{
|
|
45
|
+
status: number;
|
|
46
|
+
data: any;
|
|
47
|
+
}>;
|
|
46
48
|
/**
|
|
47
49
|
* onChange hook
|
|
48
50
|
*/
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@hocuspocus/extension-webhook",
|
|
3
|
-
"version": "4.0.0-rc.
|
|
3
|
+
"version": "4.0.0-rc.4",
|
|
4
4
|
"description": "hocuspocus webhook extension",
|
|
5
5
|
"homepage": "https://hocuspocus.dev",
|
|
6
6
|
"keywords": [
|
|
@@ -30,15 +30,14 @@
|
|
|
30
30
|
"dist"
|
|
31
31
|
],
|
|
32
32
|
"dependencies": {
|
|
33
|
-
"@hocuspocus/common": "^4.0.0-rc.
|
|
34
|
-
"@hocuspocus/server": "^4.0.0-rc.
|
|
35
|
-
"@hocuspocus/transformer": "^4.0.0-rc.
|
|
36
|
-
"axios": "^1.12.2"
|
|
33
|
+
"@hocuspocus/common": "^4.0.0-rc.4",
|
|
34
|
+
"@hocuspocus/server": "^4.0.0-rc.4",
|
|
35
|
+
"@hocuspocus/transformer": "^4.0.0-rc.4"
|
|
37
36
|
},
|
|
38
37
|
"peerDependencies": {
|
|
39
38
|
"yjs": "^13.6.8"
|
|
40
39
|
},
|
|
41
|
-
"gitHead": "
|
|
40
|
+
"gitHead": "7e50dc8532dbb1bd1465a63721faeb170b99f196",
|
|
42
41
|
"repository": {
|
|
43
42
|
"url": "https://github.com/ueberdosis/hocuspocus"
|
|
44
43
|
},
|
package/src/index.ts
CHANGED
|
@@ -9,7 +9,6 @@ import type {
|
|
|
9
9
|
} from "@hocuspocus/server";
|
|
10
10
|
import type { Transformer } from "@hocuspocus/transformer";
|
|
11
11
|
import { TiptapTransformer } from "@hocuspocus/transformer";
|
|
12
|
-
import axios from "axios";
|
|
13
12
|
import type { Doc } from "yjs";
|
|
14
13
|
|
|
15
14
|
export enum Events {
|
|
@@ -97,12 +96,29 @@ export class Webhook implements Extension {
|
|
|
97
96
|
async sendRequest(event: Events, payload: any) {
|
|
98
97
|
const json = JSON.stringify({ event, payload });
|
|
99
98
|
|
|
100
|
-
|
|
99
|
+
const response = await fetch(this.configuration.url, {
|
|
100
|
+
method: "POST",
|
|
101
|
+
body: json,
|
|
101
102
|
headers: {
|
|
102
103
|
"X-Hocuspocus-Signature-256": this.createSignature(json),
|
|
103
104
|
"Content-Type": "application/json",
|
|
104
105
|
},
|
|
105
106
|
});
|
|
107
|
+
|
|
108
|
+
if (!response.ok) {
|
|
109
|
+
throw new Error(
|
|
110
|
+
`Webhook request to ${this.configuration.url} failed with status ${response.status}`,
|
|
111
|
+
);
|
|
112
|
+
}
|
|
113
|
+
|
|
114
|
+
const text = await response.text();
|
|
115
|
+
const contentType = response.headers.get("content-type") ?? "";
|
|
116
|
+
const data =
|
|
117
|
+
contentType.includes("application/json") && text
|
|
118
|
+
? JSON.parse(text)
|
|
119
|
+
: text;
|
|
120
|
+
|
|
121
|
+
return { status: response.status, data };
|
|
106
122
|
}
|
|
107
123
|
|
|
108
124
|
/**
|
|
@@ -151,12 +167,8 @@ export class Webhook implements Extension {
|
|
|
151
167
|
requestParameters: Object.fromEntries(data.requestParameters.entries()),
|
|
152
168
|
});
|
|
153
169
|
|
|
154
|
-
|
|
155
|
-
|
|
156
|
-
const document =
|
|
157
|
-
typeof response.data === "string"
|
|
158
|
-
? JSON.parse(response.data)
|
|
159
|
-
: response.data;
|
|
170
|
+
const document = response.data;
|
|
171
|
+
if (!document) return;
|
|
160
172
|
|
|
161
173
|
// eslint-disable-next-line guard-for-in,no-restricted-syntax
|
|
162
174
|
for (const fieldName in document) {
|
|
@@ -189,9 +201,7 @@ export class Webhook implements Extension {
|
|
|
189
201
|
requestParameters: Object.fromEntries(data.requestParameters.entries()),
|
|
190
202
|
});
|
|
191
203
|
|
|
192
|
-
return
|
|
193
|
-
? JSON.parse(response.data)
|
|
194
|
-
: response.data;
|
|
204
|
+
return response.data;
|
|
195
205
|
} catch (e) {
|
|
196
206
|
console.error(`Caught error in extension-webhook: ${e}`);
|
|
197
207
|
throw Forbidden;
|