From 68af95d927064149f4fdec6a70ad955b083ece81 Mon Sep 17 00:00:00 2001 From: Arlen22 Date: Wed, 29 Apr 2020 12:27:51 -0400 Subject: [PATCH 01/14] Create server-sent-events.js --- core/modules/server/server-sent-events.js | 68 +++++++++++++++++++++++ 1 file changed, 68 insertions(+) create mode 100644 core/modules/server/server-sent-events.js diff --git a/core/modules/server/server-sent-events.js b/core/modules/server/server-sent-events.js new file mode 100644 index 00000000000..3f0308b1dfe --- /dev/null +++ b/core/modules/server/server-sent-events.js @@ -0,0 +1,68 @@ +/*\ +title: $:/core/modules/server/server-sent-events.js +type: application/javascript +module-type: library +\*/ +class ServerSentEvents { + /** + * @param {string} prefix + * Usually the plugin path, such as `plugins/tiddlywiki/tiddlyweb`. + * The route will match `/events/${prefix}` exactly. + * @param {( + * request: import("http").IncomingMessage, + * state, + * emit: (event: string, data: string) => void, + * end: () => void + * ) => void} handler + * A function that will be called each time a request + * comes in with the request and state from the + * route and an emit function to call. + */ + constructor(prefix, handler) { + this.handler = handler; + this.prefix = prefix; + } + getExports() { + return { + bodyFormat: "stream", + method: "GET", + path: new RegExp("^/events/" + this.prefix + "$"), + handler: this.handleEventRequest.bind(this) + } + } + /** + * + * @param {import("http").IncomingMessage} request + * @param {import("http").ServerResponse} response + * @param {*} state + */ + handleEventRequest(request, response, state) { + if (request.headers.accept && request.headers.accept.startsWith('text/event-stream')) { + response.writeHead(200, { + 'Content-Type': 'text/event-stream', + 'Cache-Control': 'no-cache', + 'Connection': 'keep-alive' + }); + response.write("\n", "utf8"); + this.handler(request, state, this.emit.bind(this, response), this.end.bind(this, response)); + } else { + response.writeHead(406, "Not Acceptable", {}); + response.end(); + } + } + emit(response, event, data) { + + if (typeof event !== "string" || event.indexOf("\n") !== -1) + throw new Error("type must be a single-line string"); + + if (typeof data !== "string" || data.indexOf("\n") !== -1) + throw new Error("data must be a single-line string"); + + response.write("event: " + type + "\n", "utf8"); + response.write("data: " + data + "\n\n", "utf8"); + } + end(response) { + response.end(); + } +} +exports.ServerSentEvents = ServerSentEvents; From 3d59bd2862b9eb6a4adf478a74defb92f3aba0af Mon Sep 17 00:00:00 2001 From: Arlen22 Date: Wed, 29 Apr 2020 12:29:45 -0400 Subject: [PATCH 02/14] Create sse-change-listener.js --- .../tiddlyweb/sse-change-listener.js | 21 +++++++++++++++++++ 1 file changed, 21 insertions(+) create mode 100644 plugins/tiddlywiki/tiddlyweb/sse-change-listener.js diff --git a/plugins/tiddlywiki/tiddlyweb/sse-change-listener.js b/plugins/tiddlywiki/tiddlyweb/sse-change-listener.js new file mode 100644 index 00000000000..c56988bcee6 --- /dev/null +++ b/plugins/tiddlywiki/tiddlyweb/sse-change-listener.js @@ -0,0 +1,21 @@ +const { ServerSentEvents } = require("$:/core/modules/server/server-sent-events.js"); +/** @type {Record void, + * end: () => void + * }[]>} */ +const wikis = {}; +function setupWiki(wiki) { + wikis[state.wiki] = []; + state.wiki.addEventListener("change", (changes) => { + wikis[state.wiki].forEach(({ request, state, emit, end }) => { + emit("change", JSON.stringify(changes)); + }); + }); +} +const events = new ServerSentEvents("test", (request, state, emit, end) => { + if (!wikis[state.wiki]) setupWiki(state.wiki); + wikis[state.wiki].push({ request, state, emit, end }); +}); +module.exports = events.getExports(); From 8da6630aee05d5f6fbfc04d58c73d84a86fb6f0c Mon Sep 17 00:00:00 2001 From: Arlen22 Date: Wed, 29 Apr 2020 12:49:59 -0400 Subject: [PATCH 03/14] Implement server sent events --- core/modules/server/server-sent-events.js | 5 ++-- .../tiddlyweb/sse-change-listener.js | 21 -------------- plugins/tiddlywiki/tiddlyweb/sse-client.js | 18 ++++++++++++ plugins/tiddlywiki/tiddlyweb/sse-server.js | 29 +++++++++++++++++++ 4 files changed, 49 insertions(+), 24 deletions(-) delete mode 100644 plugins/tiddlywiki/tiddlyweb/sse-change-listener.js create mode 100644 plugins/tiddlywiki/tiddlyweb/sse-client.js create mode 100644 plugins/tiddlywiki/tiddlyweb/sse-server.js diff --git a/core/modules/server/server-sent-events.js b/core/modules/server/server-sent-events.js index 3f0308b1dfe..bfe7a19a8e9 100644 --- a/core/modules/server/server-sent-events.js +++ b/core/modules/server/server-sent-events.js @@ -43,7 +43,7 @@ class ServerSentEvents { 'Cache-Control': 'no-cache', 'Connection': 'keep-alive' }); - response.write("\n", "utf8"); + this.handler(request, state, this.emit.bind(this, response), this.end.bind(this, response)); } else { response.writeHead(406, "Not Acceptable", {}); @@ -58,8 +58,7 @@ class ServerSentEvents { if (typeof data !== "string" || data.indexOf("\n") !== -1) throw new Error("data must be a single-line string"); - response.write("event: " + type + "\n", "utf8"); - response.write("data: " + data + "\n\n", "utf8"); + response.write(`event: ${event}\ndata: ${data}\n\n`, "utf8"); } end(response) { response.end(); diff --git a/plugins/tiddlywiki/tiddlyweb/sse-change-listener.js b/plugins/tiddlywiki/tiddlyweb/sse-change-listener.js deleted file mode 100644 index c56988bcee6..00000000000 --- a/plugins/tiddlywiki/tiddlyweb/sse-change-listener.js +++ /dev/null @@ -1,21 +0,0 @@ -const { ServerSentEvents } = require("$:/core/modules/server/server-sent-events.js"); -/** @type {Record void, - * end: () => void - * }[]>} */ -const wikis = {}; -function setupWiki(wiki) { - wikis[state.wiki] = []; - state.wiki.addEventListener("change", (changes) => { - wikis[state.wiki].forEach(({ request, state, emit, end }) => { - emit("change", JSON.stringify(changes)); - }); - }); -} -const events = new ServerSentEvents("test", (request, state, emit, end) => { - if (!wikis[state.wiki]) setupWiki(state.wiki); - wikis[state.wiki].push({ request, state, emit, end }); -}); -module.exports = events.getExports(); diff --git a/plugins/tiddlywiki/tiddlyweb/sse-client.js b/plugins/tiddlywiki/tiddlyweb/sse-client.js new file mode 100644 index 00000000000..7301bf43096 --- /dev/null +++ b/plugins/tiddlywiki/tiddlyweb/sse-client.js @@ -0,0 +1,18 @@ +/*\ +title: $:/plugins/tiddlywiki/tiddlyweb/sse-client.js +type: application/javascript +module-type: startup + +GET /recipes/default/tiddlers/:title + +\*/ +exports.name = "/events/plugins/tiddlywiki/tiddlyweb"; +exports.after = ["startup"]; +exports.synchronous = true; +exports.platforms = ["browser"]; +exports.startup = function () { + var events = new EventSource("/events/plugins/tiddlywiki/tiddlyweb"); + events.addEventListener("change", function () { + $tw.syncer.syncFromServer(); + }); +} \ No newline at end of file diff --git a/plugins/tiddlywiki/tiddlyweb/sse-server.js b/plugins/tiddlywiki/tiddlyweb/sse-server.js new file mode 100644 index 00000000000..0c2a1bd72e3 --- /dev/null +++ b/plugins/tiddlywiki/tiddlyweb/sse-server.js @@ -0,0 +1,29 @@ +/*\ +title: $:/plugins/tiddlywiki/tiddlyweb/sse-server.js +type: application/javascript +module-type: route + +GET /events/plugins/tiddlywiki/tiddlyweb + +\*/ +var ServerSentEvents = require("$:/core/modules/server/server-sent-events.js").ServerSentEvents; +/** @type {Record void, + * end: () => void + * }[]>} */ +var wikis = {}; +function setupWiki(wiki) { + wikis[wiki] = []; + wiki.addEventListener("change", function (changes) { + wikis[wiki].forEach(function (item) { + item.emit("change", JSON.stringify(changes)); + }); + }); +} +var events = new ServerSentEvents("plugins/tiddlywiki/tiddlyweb", (request, state, emit, end) => { + if (!wikis[state.wiki]) setupWiki(state.wiki); + wikis[state.wiki].push({ request: request, state: state, emit: emit, end: end }); +}); +module.exports = events.getExports(); From c013a90db8718a8e640e00387a79107f6736963e Mon Sep 17 00:00:00 2001 From: Arlen22 Date: Wed, 29 Apr 2020 13:02:09 -0400 Subject: [PATCH 04/14] Convert to ES5 and wrap in function --- core/modules/server/server-sent-events.js | 80 ++++++++++++---------- plugins/tiddlywiki/tiddlyweb/sse-client.js | 10 ++- plugins/tiddlywiki/tiddlyweb/sse-server.js | 19 +++-- 3 files changed, 67 insertions(+), 42 deletions(-) diff --git a/core/modules/server/server-sent-events.js b/core/modules/server/server-sent-events.js index bfe7a19a8e9..fd3b3331078 100644 --- a/core/modules/server/server-sent-events.js +++ b/core/modules/server/server-sent-events.js @@ -3,65 +3,73 @@ title: $:/core/modules/server/server-sent-events.js type: application/javascript module-type: library \*/ -class ServerSentEvents { - /** - * @param {string} prefix - * Usually the plugin path, such as `plugins/tiddlywiki/tiddlyweb`. - * The route will match `/events/${prefix}` exactly. - * @param {( - * request: import("http").IncomingMessage, - * state, - * emit: (event: string, data: string) => void, - * end: () => void - * ) => void} handler - * A function that will be called each time a request - * comes in with the request and state from the - * route and an emit function to call. - */ - constructor(prefix, handler) { +(function(){ + +/*jslint node: true, browser: true */ +/*global $tw: false */ +"use strict"; + +/** + * @param {string} prefix + * Usually the plugin path, such as `plugins/tiddlywiki/tiddlyweb`. + * The route will match `/events/${prefix}` exactly. + * @param {( + * request: import("http").IncomingMessage, + * state, + * emit: (event: string, data: string) => void, + * end: () => void + * ) => void} handler + * A function that will be called each time a request + * comes in with the request and state from the + * route and an emit function to call. + */ +var ServerSentEvents = /** @class */ (function () { + + function ServerSentEvents(prefix, handler) { this.handler = handler; this.prefix = prefix; } - getExports() { + ServerSentEvents.prototype.getExports = function () { return { bodyFormat: "stream", method: "GET", path: new RegExp("^/events/" + this.prefix + "$"), handler: this.handleEventRequest.bind(this) - } - } + }; + }; /** - * - * @param {import("http").IncomingMessage} request - * @param {import("http").ServerResponse} response - * @param {*} state + * + * @param {import("http").IncomingMessage} request + * @param {import("http").ServerResponse} response + * @param {*} state */ - handleEventRequest(request, response, state) { + ServerSentEvents.prototype.handleEventRequest = function (request, response, state) { if (request.headers.accept && request.headers.accept.startsWith('text/event-stream')) { response.writeHead(200, { 'Content-Type': 'text/event-stream', 'Cache-Control': 'no-cache', 'Connection': 'keep-alive' }); - this.handler(request, state, this.emit.bind(this, response), this.end.bind(this, response)); - } else { + } + else { response.writeHead(406, "Not Acceptable", {}); response.end(); } - } - emit(response, event, data) { - + }; + ServerSentEvents.prototype.emit = function (response, event, data) { if (typeof event !== "string" || event.indexOf("\n") !== -1) throw new Error("type must be a single-line string"); - if (typeof data !== "string" || data.indexOf("\n") !== -1) throw new Error("data must be a single-line string"); - - response.write(`event: ${event}\ndata: ${data}\n\n`, "utf8"); - } - end(response) { + response.write("event: " + event + "\ndata: " + data + "\n\n", "utf8"); + }; + ServerSentEvents.prototype.end = function (response) { response.end(); - } -} + }; + return ServerSentEvents; +}()); + exports.ServerSentEvents = ServerSentEvents; + +})(); \ No newline at end of file diff --git a/plugins/tiddlywiki/tiddlyweb/sse-client.js b/plugins/tiddlywiki/tiddlyweb/sse-client.js index 7301bf43096..6aa89caccd6 100644 --- a/plugins/tiddlywiki/tiddlyweb/sse-client.js +++ b/plugins/tiddlywiki/tiddlyweb/sse-client.js @@ -6,6 +6,12 @@ module-type: startup GET /recipes/default/tiddlers/:title \*/ +(function(){ + +/*jslint node: true, browser: true */ +/*global $tw: false */ +"use strict"; + exports.name = "/events/plugins/tiddlywiki/tiddlyweb"; exports.after = ["startup"]; exports.synchronous = true; @@ -15,4 +21,6 @@ exports.startup = function () { events.addEventListener("change", function () { $tw.syncer.syncFromServer(); }); -} \ No newline at end of file +} + +})(); \ No newline at end of file diff --git a/plugins/tiddlywiki/tiddlyweb/sse-server.js b/plugins/tiddlywiki/tiddlyweb/sse-server.js index 0c2a1bd72e3..18c4a751185 100644 --- a/plugins/tiddlywiki/tiddlyweb/sse-server.js +++ b/plugins/tiddlywiki/tiddlyweb/sse-server.js @@ -6,11 +6,17 @@ module-type: route GET /events/plugins/tiddlywiki/tiddlyweb \*/ +(function(){ + +/*jslint node: true, browser: true */ +/*global $tw: false */ +"use strict"; + var ServerSentEvents = require("$:/core/modules/server/server-sent-events.js").ServerSentEvents; /** @type {Record void, + * request: import("http").IncomingMessage, + * state, + * emit: (event: string, data: string) => void, * end: () => void * }[]>} */ var wikis = {}; @@ -22,8 +28,11 @@ function setupWiki(wiki) { }); }); } -var events = new ServerSentEvents("plugins/tiddlywiki/tiddlyweb", (request, state, emit, end) => { - if (!wikis[state.wiki]) setupWiki(state.wiki); +var events = new ServerSentEvents("plugins/tiddlywiki/tiddlyweb", function (request, state, emit, end) { + if (!wikis[state.wiki]) + setupWiki(state.wiki); wikis[state.wiki].push({ request: request, state: state, emit: emit, end: end }); }); module.exports = events.getExports(); + +})(); \ No newline at end of file From 85c55883274b53cf99bf037f398309f9d13dca40 Mon Sep 17 00:00:00 2001 From: Arlen22 Date: Wed, 29 Apr 2020 13:24:30 -0400 Subject: [PATCH 05/14] Use the host string from tiddlyweb --- plugins/tiddlywiki/tiddlyweb/sse-client.js | 9 ++++++++- 1 file changed, 8 insertions(+), 1 deletion(-) diff --git a/plugins/tiddlywiki/tiddlyweb/sse-client.js b/plugins/tiddlywiki/tiddlyweb/sse-client.js index 6aa89caccd6..5f73788b427 100644 --- a/plugins/tiddlywiki/tiddlyweb/sse-client.js +++ b/plugins/tiddlywiki/tiddlyweb/sse-client.js @@ -17,7 +17,14 @@ exports.after = ["startup"]; exports.synchronous = true; exports.platforms = ["browser"]; exports.startup = function () { - var events = new EventSource("/events/plugins/tiddlywiki/tiddlyweb"); + //make sure we're actually being used + if($tw.syncadaptor.name !== "tiddlyweb") return; + //get the mount point in case a path prefix is used + var host = $tw.syncadaptor.getHost(); + //make sure it ends with a slash (it usually does) + if(!host.endsWith("/")) host += "/"; + //setup the event listener + var events = new EventSource(host + "events/plugins/tiddlywiki/tiddlyweb"); events.addEventListener("change", function () { $tw.syncer.syncFromServer(); }); From 63b5710f6b831dafbabefe4c64cb08792118bf3d Mon Sep 17 00:00:00 2001 From: Arlen22 Date: Mon, 11 May 2020 20:51:51 -0400 Subject: [PATCH 06/14] Improve comments in sse-server.js --- plugins/tiddlywiki/tiddlyweb/sse-server.js | 38 ++++++++++++++++++---- 1 file changed, 31 insertions(+), 7 deletions(-) diff --git a/plugins/tiddlywiki/tiddlyweb/sse-server.js b/plugins/tiddlywiki/tiddlyweb/sse-server.js index 18c4a751185..9c5a9900b3b 100644 --- a/plugins/tiddlywiki/tiddlyweb/sse-server.js +++ b/plugins/tiddlywiki/tiddlyweb/sse-server.js @@ -12,27 +12,51 @@ GET /events/plugins/tiddlywiki/tiddlyweb /*global $tw: false */ "use strict"; -var ServerSentEvents = require("$:/core/modules/server/server-sent-events.js").ServerSentEvents; /** @type {Record void, * end: () => void * }[]>} */ var wikis = {}; +/** + * Setups up the array for this wiki and adds the change listener + * + * @param {$tw.Wiki} wiki The wiki object to listen to changes on + */ function setupWiki(wiki) { + // add a new array for this wiki (object references work as keys) wikis[wiki] = []; + // add the change listener for this wiki wiki.addEventListener("change", function (changes) { wikis[wiki].forEach(function (item) { item.emit("change", JSON.stringify(changes)); }); }); } -var events = new ServerSentEvents("plugins/tiddlywiki/tiddlyweb", function (request, state, emit, end) { - if (!wikis[state.wiki]) - setupWiki(state.wiki); - wikis[state.wiki].push({ request: request, state: state, emit: emit, end: end }); -}); +/** + * + * @param {import("http").IncomingMessage} request + * @param {{wiki,pathPrefix}} state + * @param {(event: string, data: string) => void} emit + * @param {() => void} end + */ +function handleConnection(request, state, emit, end) { + // setup this particular wiki if we haven't seen it before + if (!wikis[state.wiki]) setupWiki(state.wiki); + // add the connection to the list of connections for this wiki + var item = { request: request, state: state, emit: emit, end: end }; + wikis[state.wiki].push(item); + // remove the connection when it closes + request.on("close",function(){ + wikis[state.wiki].splice(wikis[state.wiki].indexOf(item),1); + }); +} +// import the ServerSentEvents class +var ServerSentEvents = require("$:/core/modules/server/server-sent-events.js").ServerSentEvents; +// instantiate the class +var events = new ServerSentEvents("plugins/tiddlywiki/tiddlyweb", handleConnection); +// export the route definition for this server sent events instance module.exports = events.getExports(); })(); \ No newline at end of file From 65a5daee4ce051a56230dabc8f30472dc937ff97 Mon Sep 17 00:00:00 2001 From: Arlen22 Date: Fri, 15 May 2020 09:43:39 -0400 Subject: [PATCH 07/14] Can't use object reference as key --- plugins/tiddlywiki/tiddlyweb/sse-server.js | 17 +++++++++++------ 1 file changed, 11 insertions(+), 6 deletions(-) diff --git a/plugins/tiddlywiki/tiddlyweb/sse-server.js b/plugins/tiddlywiki/tiddlyweb/sse-server.js index 9c5a9900b3b..e34423eaca1 100644 --- a/plugins/tiddlywiki/tiddlyweb/sse-server.js +++ b/plugins/tiddlywiki/tiddlyweb/sse-server.js @@ -18,21 +18,25 @@ GET /events/plugins/tiddlywiki/tiddlyweb * emit: (event: string, data: string) => void, * end: () => void * }[]>} */ -var wikis = {}; +var wikis = []; +var conns = []; /** * Setups up the array for this wiki and adds the change listener * * @param {$tw.Wiki} wiki The wiki object to listen to changes on */ function setupWiki(wiki) { + var index = wikis.length; // add a new array for this wiki (object references work as keys) - wikis[wiki] = []; + wikis.push(wiki); + conns.push([]); // add the change listener for this wiki wiki.addEventListener("change", function (changes) { - wikis[wiki].forEach(function (item) { + conns[index].forEach(function (item) { item.emit("change", JSON.stringify(changes)); }); }); + return index; } /** * @@ -42,14 +46,15 @@ function setupWiki(wiki) { * @param {() => void} end */ function handleConnection(request, state, emit, end) { + var index = wikis.indexOf(state.wiki); // setup this particular wiki if we haven't seen it before - if (!wikis[state.wiki]) setupWiki(state.wiki); + if (index === -1) index = setupWiki(state.wiki); // add the connection to the list of connections for this wiki var item = { request: request, state: state, emit: emit, end: end }; - wikis[state.wiki].push(item); + conns[index].push(item); // remove the connection when it closes request.on("close",function(){ - wikis[state.wiki].splice(wikis[state.wiki].indexOf(item),1); + conns[index].splice(wikis[state.wiki].indexOf(item),1); }); } // import the ServerSentEvents class From 2632661245eae9d3da834df238273196438232d5 Mon Sep 17 00:00:00 2001 From: Arlen22 Date: Fri, 15 May 2020 12:05:36 -0400 Subject: [PATCH 08/14] Add retry timeout --- core/modules/server/server-sent-events.js | 3 +-- plugins/tiddlywiki/tiddlyweb/sse-client.js | 10 +++++++++- 2 files changed, 10 insertions(+), 3 deletions(-) diff --git a/core/modules/server/server-sent-events.js b/core/modules/server/server-sent-events.js index fd3b3331078..00c047800d5 100644 --- a/core/modules/server/server-sent-events.js +++ b/core/modules/server/server-sent-events.js @@ -51,8 +51,7 @@ var ServerSentEvents = /** @class */ (function () { 'Connection': 'keep-alive' }); this.handler(request, state, this.emit.bind(this, response), this.end.bind(this, response)); - } - else { + } else { response.writeHead(406, "Not Acceptable", {}); response.end(); } diff --git a/plugins/tiddlywiki/tiddlyweb/sse-client.js b/plugins/tiddlywiki/tiddlyweb/sse-client.js index 5f73788b427..573d7a015fc 100644 --- a/plugins/tiddlywiki/tiddlyweb/sse-client.js +++ b/plugins/tiddlywiki/tiddlyweb/sse-client.js @@ -24,10 +24,18 @@ exports.startup = function () { //make sure it ends with a slash (it usually does) if(!host.endsWith("/")) host += "/"; //setup the event listener + setupEvents(host); +} +function setupEvents(host){ var events = new EventSource(host + "events/plugins/tiddlywiki/tiddlyweb"); events.addEventListener("change", function () { $tw.syncer.syncFromServer(); }); + events.onerror = function() { + events.close(); + setTimeout(function() { + setupEvents(host); + }, $tw.syncer.errorRetryInterval) + }; } - })(); \ No newline at end of file From e8d62fe5571df9a8c0d62b0798ad79678de9ffe3 Mon Sep 17 00:00:00 2001 From: Arlen22 Date: Wed, 20 May 2020 17:47:59 -0400 Subject: [PATCH 09/14] Fix a bug --- plugins/tiddlywiki/tiddlyweb/sse-server.js | 10 ++++++---- 1 file changed, 6 insertions(+), 4 deletions(-) diff --git a/plugins/tiddlywiki/tiddlyweb/sse-server.js b/plugins/tiddlywiki/tiddlyweb/sse-server.js index e34423eaca1..e54470693ca 100644 --- a/plugins/tiddlywiki/tiddlyweb/sse-server.js +++ b/plugins/tiddlywiki/tiddlyweb/sse-server.js @@ -12,13 +12,14 @@ GET /events/plugins/tiddlywiki/tiddlyweb /*global $tw: false */ "use strict"; -/** @type {Record void, * end: () => void - * }[]>} */ -var wikis = []; + * }[][]} */ var conns = []; /** * Setups up the array for this wiki and adds the change listener @@ -54,7 +55,8 @@ function handleConnection(request, state, emit, end) { conns[index].push(item); // remove the connection when it closes request.on("close",function(){ - conns[index].splice(wikis[state.wiki].indexOf(item),1); + var remIndex = wikis[index].indexOf(item); + if(remIndex > -1) conns[index].splice(remIndex,1); }); } // import the ServerSentEvents class From 06009b04d9d03fcd9ee845246bc02faef908ee22 Mon Sep 17 00:00:00 2001 From: Arlen22 Date: Sat, 23 May 2020 13:31:55 -0400 Subject: [PATCH 10/14] bug fix --- plugins/tiddlywiki/tiddlyweb/sse-server.js | 7 ++++--- 1 file changed, 4 insertions(+), 3 deletions(-) diff --git a/plugins/tiddlywiki/tiddlyweb/sse-server.js b/plugins/tiddlywiki/tiddlyweb/sse-server.js index e54470693ca..43a787465d8 100644 --- a/plugins/tiddlywiki/tiddlyweb/sse-server.js +++ b/plugins/tiddlywiki/tiddlyweb/sse-server.js @@ -28,12 +28,13 @@ var conns = []; */ function setupWiki(wiki) { var index = wikis.length; + var connections = []; // add a new array for this wiki (object references work as keys) wikis.push(wiki); - conns.push([]); + conns.push(connections); // add the change listener for this wiki wiki.addEventListener("change", function (changes) { - conns[index].forEach(function (item) { + connections.forEach(function (item) { item.emit("change", JSON.stringify(changes)); }); }); @@ -55,7 +56,7 @@ function handleConnection(request, state, emit, end) { conns[index].push(item); // remove the connection when it closes request.on("close",function(){ - var remIndex = wikis[index].indexOf(item); + var remIndex = conns[index].indexOf(item); if(remIndex > -1) conns[index].splice(remIndex,1); }); } From b861b5886682a9ff46e1635dc76c082d9d3650ff Mon Sep 17 00:00:00 2001 From: Arlen22 Date: Thu, 29 Oct 2020 14:31:17 -0400 Subject: [PATCH 11/14] Fix formatting --- core/modules/server/server-sent-events.js | 84 +++++++++++----------- plugins/tiddlywiki/tiddlyweb/sse-client.js | 10 ++- plugins/tiddlywiki/tiddlyweb/sse-server.js | 4 +- 3 files changed, 51 insertions(+), 47 deletions(-) diff --git a/core/modules/server/server-sent-events.js b/core/modules/server/server-sent-events.js index 00c047800d5..088d8522169 100644 --- a/core/modules/server/server-sent-events.js +++ b/core/modules/server/server-sent-events.js @@ -23,52 +23,52 @@ module-type: library * comes in with the request and state from the * route and an emit function to call. */ -var ServerSentEvents = /** @class */ (function () { +var ServerSentEvents = function ServerSentEvents(prefix, handler) { + this.handler = handler; + this.prefix = prefix; +} - function ServerSentEvents(prefix, handler) { - this.handler = handler; - this.prefix = prefix; - } - ServerSentEvents.prototype.getExports = function () { - return { - bodyFormat: "stream", - method: "GET", - path: new RegExp("^/events/" + this.prefix + "$"), - handler: this.handleEventRequest.bind(this) - }; - }; - /** - * - * @param {import("http").IncomingMessage} request - * @param {import("http").ServerResponse} response - * @param {*} state - */ - ServerSentEvents.prototype.handleEventRequest = function (request, response, state) { - if (request.headers.accept && request.headers.accept.startsWith('text/event-stream')) { - response.writeHead(200, { - 'Content-Type': 'text/event-stream', - 'Cache-Control': 'no-cache', - 'Connection': 'keep-alive' - }); - this.handler(request, state, this.emit.bind(this, response), this.end.bind(this, response)); - } else { - response.writeHead(406, "Not Acceptable", {}); - response.end(); - } - }; - ServerSentEvents.prototype.emit = function (response, event, data) { - if (typeof event !== "string" || event.indexOf("\n") !== -1) - throw new Error("type must be a single-line string"); - if (typeof data !== "string" || data.indexOf("\n") !== -1) - throw new Error("data must be a single-line string"); - response.write("event: " + event + "\ndata: " + data + "\n\n", "utf8"); +ServerSentEvents.prototype.getExports = function() { + return { + bodyFormat: "stream", + method: "GET", + path: new RegExp("^/events/" + this.prefix + "$"), + handler: this.handleEventRequest.bind(this) }; - ServerSentEvents.prototype.end = function (response) { +}; + +/** + * + * @param {import("http").IncomingMessage} request + * @param {import("http").ServerResponse} response + * @param {*} state + */ +ServerSentEvents.prototype.handleEventRequest = function(request, response, state) { + if (request.headers.accept && request.headers.accept.startsWith('text/event-stream')) { + response.writeHead(200, { + 'Content-Type': 'text/event-stream', + 'Cache-Control': 'no-cache', + 'Connection': 'keep-alive' + }); + this.handler(request, state, this.emit.bind(this, response), this.end.bind(this, response)); + } else { + response.writeHead(406, "Not Acceptable", {}); response.end(); - }; - return ServerSentEvents; -}()); + } +}; + +ServerSentEvents.prototype.emit = function(response, event, data) { + if (typeof event !== "string" || event.indexOf("\n") !== -1) + throw new Error("type must be a single-line string"); + if (typeof data !== "string" || data.indexOf("\n") !== -1) + throw new Error("data must be a single-line string"); + response.write("event: " + event + "\ndata: " + data + "\n\n", "utf8"); +}; +ServerSentEvents.prototype.end = function(response) { + response.end(); +}; + exports.ServerSentEvents = ServerSentEvents; })(); \ No newline at end of file diff --git a/plugins/tiddlywiki/tiddlyweb/sse-client.js b/plugins/tiddlywiki/tiddlyweb/sse-client.js index 573d7a015fc..31f820cb4d2 100644 --- a/plugins/tiddlywiki/tiddlyweb/sse-client.js +++ b/plugins/tiddlywiki/tiddlyweb/sse-client.js @@ -16,7 +16,7 @@ exports.name = "/events/plugins/tiddlywiki/tiddlyweb"; exports.after = ["startup"]; exports.synchronous = true; exports.platforms = ["browser"]; -exports.startup = function () { +exports.startup = function() { //make sure we're actually being used if($tw.syncadaptor.name !== "tiddlyweb") return; //get the mount point in case a path prefix is used @@ -28,8 +28,12 @@ exports.startup = function () { } function setupEvents(host){ var events = new EventSource(host + "events/plugins/tiddlywiki/tiddlyweb"); - events.addEventListener("change", function () { - $tw.syncer.syncFromServer(); + var timeout = null; + events.addEventListener("change", function() { + if(timeout) clearTimeout(timeout); + timeout = setTimeout(function(){ + $tw.syncer.syncFromServer(); + }, $tw.syncer.throttleInterval); }); events.onerror = function() { events.close(); diff --git a/plugins/tiddlywiki/tiddlyweb/sse-server.js b/plugins/tiddlywiki/tiddlyweb/sse-server.js index 43a787465d8..a1207ea189d 100644 --- a/plugins/tiddlywiki/tiddlyweb/sse-server.js +++ b/plugins/tiddlywiki/tiddlyweb/sse-server.js @@ -33,8 +33,8 @@ function setupWiki(wiki) { wikis.push(wiki); conns.push(connections); // add the change listener for this wiki - wiki.addEventListener("change", function (changes) { - connections.forEach(function (item) { + wiki.addEventListener("change", function(changes) { + connections.forEach(function(item) { item.emit("change", JSON.stringify(changes)); }); }); From 5fb2c5cd88967e57894bf8397cc7acb003c3f761 Mon Sep 17 00:00:00 2001 From: Arlen22 Date: Thu, 29 Oct 2020 14:33:50 -0400 Subject: [PATCH 12/14] Fix ES5 compat --- plugins/tiddlywiki/tiddlyweb/sse-client.js | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/plugins/tiddlywiki/tiddlyweb/sse-client.js b/plugins/tiddlywiki/tiddlyweb/sse-client.js index 31f820cb4d2..1e57dbfa89c 100644 --- a/plugins/tiddlywiki/tiddlyweb/sse-client.js +++ b/plugins/tiddlywiki/tiddlyweb/sse-client.js @@ -22,7 +22,7 @@ exports.startup = function() { //get the mount point in case a path prefix is used var host = $tw.syncadaptor.getHost(); //make sure it ends with a slash (it usually does) - if(!host.endsWith("/")) host += "/"; + if(host[host.length - 1] !== "/") host += "/"; //setup the event listener setupEvents(host); } From eed4c1829486e5c93345b3b18b7c35b03385081a Mon Sep 17 00:00:00 2001 From: Arlen22 Date: Thu, 29 Oct 2020 14:35:14 -0400 Subject: [PATCH 13/14] capitalize comments --- plugins/tiddlywiki/tiddlyweb/sse-client.js | 8 ++++---- plugins/tiddlywiki/tiddlyweb/sse-server.js | 6 +++--- 2 files changed, 7 insertions(+), 7 deletions(-) diff --git a/plugins/tiddlywiki/tiddlyweb/sse-client.js b/plugins/tiddlywiki/tiddlyweb/sse-client.js index 1e57dbfa89c..a9819eceb63 100644 --- a/plugins/tiddlywiki/tiddlyweb/sse-client.js +++ b/plugins/tiddlywiki/tiddlyweb/sse-client.js @@ -17,13 +17,13 @@ exports.after = ["startup"]; exports.synchronous = true; exports.platforms = ["browser"]; exports.startup = function() { - //make sure we're actually being used + // Make sure we're actually being used if($tw.syncadaptor.name !== "tiddlyweb") return; - //get the mount point in case a path prefix is used + // Get the mount point in case a path prefix is used var host = $tw.syncadaptor.getHost(); - //make sure it ends with a slash (it usually does) + // Make sure it ends with a slash (it usually does) if(host[host.length - 1] !== "/") host += "/"; - //setup the event listener + // Setup the event listener setupEvents(host); } function setupEvents(host){ diff --git a/plugins/tiddlywiki/tiddlyweb/sse-server.js b/plugins/tiddlywiki/tiddlyweb/sse-server.js index a1207ea189d..c03d0085ab4 100644 --- a/plugins/tiddlywiki/tiddlyweb/sse-server.js +++ b/plugins/tiddlywiki/tiddlyweb/sse-server.js @@ -60,11 +60,11 @@ function handleConnection(request, state, emit, end) { if(remIndex > -1) conns[index].splice(remIndex,1); }); } -// import the ServerSentEvents class +// Import the ServerSentEvents class var ServerSentEvents = require("$:/core/modules/server/server-sent-events.js").ServerSentEvents; -// instantiate the class +// Instantiate the class var events = new ServerSentEvents("plugins/tiddlywiki/tiddlyweb", handleConnection); -// export the route definition for this server sent events instance +// Export the route definition for this server sent events instance module.exports = events.getExports(); })(); \ No newline at end of file From 2264946bb4d5cd2181f782b617adf38825613ca8 Mon Sep 17 00:00:00 2001 From: Arlen22 Date: Thu, 29 Oct 2020 14:41:22 -0400 Subject: [PATCH 14/14] more fixes --- core/modules/server/server-sent-events.js | 18 ++++++++++-------- plugins/tiddlywiki/tiddlyweb/sse-client.js | 1 + plugins/tiddlywiki/tiddlyweb/sse-server.js | 12 +++++++----- 3 files changed, 18 insertions(+), 13 deletions(-) diff --git a/core/modules/server/server-sent-events.js b/core/modules/server/server-sent-events.js index 088d8522169..061f1182d27 100644 --- a/core/modules/server/server-sent-events.js +++ b/core/modules/server/server-sent-events.js @@ -44,11 +44,11 @@ ServerSentEvents.prototype.getExports = function() { * @param {*} state */ ServerSentEvents.prototype.handleEventRequest = function(request, response, state) { - if (request.headers.accept && request.headers.accept.startsWith('text/event-stream')) { + if(request.headers.accept && request.headers.accept.startsWith("text/event-stream")) { response.writeHead(200, { - 'Content-Type': 'text/event-stream', - 'Cache-Control': 'no-cache', - 'Connection': 'keep-alive' + "Content-Type": "text/event-stream", + "Cache-Control": "no-cache", + "Connection": "keep-alive" }); this.handler(request, state, this.emit.bind(this, response), this.end.bind(this, response)); } else { @@ -58,10 +58,12 @@ ServerSentEvents.prototype.handleEventRequest = function(request, response, stat }; ServerSentEvents.prototype.emit = function(response, event, data) { - if (typeof event !== "string" || event.indexOf("\n") !== -1) - throw new Error("type must be a single-line string"); - if (typeof data !== "string" || data.indexOf("\n") !== -1) - throw new Error("data must be a single-line string"); + if(typeof event !== "string" || event.indexOf("\n") !== -1) { + throw new Error("Type must be a single-line string"); + } + if(typeof data !== "string" || data.indexOf("\n") !== -1) { + throw new Error("Data must be a single-line string"); + } response.write("event: " + event + "\ndata: " + data + "\n\n", "utf8"); }; diff --git a/plugins/tiddlywiki/tiddlyweb/sse-client.js b/plugins/tiddlywiki/tiddlyweb/sse-client.js index a9819eceb63..a8639f8e3d3 100644 --- a/plugins/tiddlywiki/tiddlyweb/sse-client.js +++ b/plugins/tiddlywiki/tiddlyweb/sse-client.js @@ -26,6 +26,7 @@ exports.startup = function() { // Setup the event listener setupEvents(host); } + function setupEvents(host){ var events = new EventSource(host + "events/plugins/tiddlywiki/tiddlyweb"); var timeout = null; diff --git a/plugins/tiddlywiki/tiddlyweb/sse-server.js b/plugins/tiddlywiki/tiddlyweb/sse-server.js index c03d0085ab4..129b19f6936 100644 --- a/plugins/tiddlywiki/tiddlyweb/sse-server.js +++ b/plugins/tiddlywiki/tiddlyweb/sse-server.js @@ -29,10 +29,10 @@ var conns = []; function setupWiki(wiki) { var index = wikis.length; var connections = []; - // add a new array for this wiki (object references work as keys) + // Add a new array for this wiki (object references work as keys) wikis.push(wiki); conns.push(connections); - // add the change listener for this wiki + // Add the change listener for this wiki wiki.addEventListener("change", function(changes) { connections.forEach(function(item) { item.emit("change", JSON.stringify(changes)); @@ -40,6 +40,7 @@ function setupWiki(wiki) { }); return index; } + /** * * @param {import("http").IncomingMessage} request @@ -49,17 +50,18 @@ function setupWiki(wiki) { */ function handleConnection(request, state, emit, end) { var index = wikis.indexOf(state.wiki); - // setup this particular wiki if we haven't seen it before + // Setup this particular wiki if we haven't seen it before if (index === -1) index = setupWiki(state.wiki); - // add the connection to the list of connections for this wiki + // Add the connection to the list of connections for this wiki var item = { request: request, state: state, emit: emit, end: end }; conns[index].push(item); - // remove the connection when it closes + // Remove the connection when it closes request.on("close",function(){ var remIndex = conns[index].indexOf(item); if(remIndex > -1) conns[index].splice(remIndex,1); }); } + // Import the ServerSentEvents class var ServerSentEvents = require("$:/core/modules/server/server-sent-events.js").ServerSentEvents; // Instantiate the class