-
Notifications
You must be signed in to change notification settings - Fork 3
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
- Loading branch information
Showing
10 changed files
with
548 additions
and
504 deletions.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -1,22 +1,26 @@ | ||
var $tw = require("tiddlywiki").TiddlyWiki(); | ||
var path = require("path"); | ||
var fs = require("fs"); | ||
|
||
// Pass the command line arguments to the boot kernel | ||
$tw.boot.argv = ["."]; | ||
// we only need the first phase of startup | ||
$tw.boot.initStartup({}); | ||
// get the bundle output path | ||
var output = path.resolve("plugins/sse/plugin.info"); | ||
// create the directory tree | ||
if(!fs.existsSync(path.dirname(output))) { | ||
fs.mkdirSync(path.dirname(output),{recursive: true}); | ||
} | ||
// load the plugin from the dist folder | ||
var plugin = $tw.loadPluginFolder(path.join(__dirname,"dist")); | ||
// put the plugin tiddlers back into the tiddlers field | ||
$tw.utils.extend(plugin,JSON.parse(plugin.text)); | ||
// remove the text field | ||
delete plugin.text; | ||
// write the bundled plugin to file | ||
fs.writeFileSync(output,JSON.stringify(plugin,null,"\t")); | ||
var $tw = require("tiddlywiki").TiddlyWiki(); | ||
var path = require("path"); | ||
var fs = require("fs"); | ||
|
||
// Pass the command line arguments to the boot kernel | ||
$tw.boot.argv = ["."]; | ||
// we only need the first phase of startup | ||
$tw.boot.initStartup({}); | ||
// get the bundle output path | ||
var output = [path.resolve("plugins/sse/plugin.info"), path.resolve("plugins/sse/plugin.json")]; | ||
|
||
// load the plugin from the dist folder | ||
var plugin = $tw.loadPluginFolder(path.join(__dirname, "dist")); | ||
// put the plugin tiddlers back into the tiddlers field | ||
$tw.utils.extend(plugin, JSON.parse(plugin.text)); | ||
// remove the text field | ||
delete plugin.text; | ||
// write the bundled plugin to file | ||
output.forEach(output => { | ||
// create the directory tree | ||
if (!fs.existsSync(path.dirname(output))) { | ||
fs.mkdirSync(path.dirname(output), { recursive: true }); | ||
} | ||
fs.writeFileSync(output, JSON.stringify(plugin, null, "\t")); | ||
}) | ||
|
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -1,9 +1,9 @@ | ||
{ | ||
"title": "$:/plugins/twcloud/tiddlyweb-sse", | ||
"name": "TiddlyWeb SSE", | ||
"description": "Sync changes immediately from the browser to TW5 (node.js) using SSE", | ||
"list": "readme", | ||
"plugin-priority": 10, | ||
"license": "MIT - Copyright Arlen Beiler 2021", | ||
"version": "1.0.1" | ||
} | ||
{ | ||
"title": "$:/plugins/twcloud/tiddlyweb-sse", | ||
"name": "TiddlyWeb SSE", | ||
"description": "Sync changes immediately from the browser to TW5 (node.js) using SSE", | ||
"list": "readme", | ||
"plugin-priority": 10, | ||
"license": "MIT - Copyright Arlen Beiler 2021", | ||
"version": "1.0.2" | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -1,3 +1,3 @@ | ||
title: $:/core/modules/server/server-sent-events.js | ||
type: application/javascript | ||
module-type: library | ||
title: $:/core/modules/server/server-sent-events.js | ||
type: application/javascript | ||
module-type: library |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -1,81 +1,81 @@ | ||
/*\ | ||
title: $:/plugins/twcloud/tiddlyweb-sse/sse-client.js | ||
type: application/javascript | ||
module-type: startup | ||
Miscellaneous startup logic for both the client and server. | ||
\*/ | ||
(function() { | ||
|
||
/*jslint node: true, browser: true */ | ||
/*global $tw: false */ | ||
"use strict"; | ||
var checks = [ | ||
"$:/status/IsLoggedIn", | ||
"$:/status/UserName", | ||
"$:/status/IsAnonymous", | ||
"$:/status/IsReadOnly" | ||
]; | ||
// Export name and synchronous status | ||
exports.name = "tiddlyweb-sse-hook"; | ||
exports.after = ["startup"]; | ||
exports.platforms = ["browser"]; | ||
exports.synchronous = true; | ||
exports.startup = function() { | ||
var source = null; | ||
if(!$tw.syncer || !$tw.syncer.syncadaptor || $tw.syncer.syncadaptor.name !== "tiddlyweb") {return;} | ||
$tw.wiki.addEventListener("change",function(changes) { | ||
if(checks.filter(e => changes[e]).length === 0) {return;} | ||
// check if we have a previous one and close it if we do | ||
if(source && source.readyState !== source.CLOSED) {source.close();} | ||
// Get the mount point in case a path prefix is used | ||
var host = $tw.syncer.syncadaptor.getHost(); | ||
// Make sure it ends with a slash (it usually does) | ||
if(host[host.length - 1] !== "/") {host += "/";} | ||
// get the endpoint | ||
var endpoint = host + "events/plugins/twcloud/tiddlyweb-sse/wiki-change"; | ||
// set the syncer poll to one hour | ||
$tw.syncer.pollTimerInterval = 1000 * 60 * 60; | ||
// Setup the event listener | ||
source = exports.setupSSE(endpoint,$tw.syncer); | ||
}); | ||
} | ||
|
||
function debounce(interval,callback) { | ||
var timeout = null; | ||
return function() { | ||
clearTimeout(timeout); | ||
timeout = setTimeout(callback,interval); | ||
}; | ||
} | ||
|
||
exports.setupSSE = function setupSSE(endpoint,syncer,refresh) { | ||
if(window.EventSource) { | ||
var source = new EventSource(endpoint,{withCredentials: true}); | ||
var debouncedSync = debounce(syncer.throttleInterval,syncer.syncFromServer.bind(syncer)); | ||
source.addEventListener("change",debouncedSync); | ||
source.onerror = function() { | ||
// return if we're reconnecting because that's handled automatically | ||
if(source.readyState === source.CONNECTING) {return;} | ||
// wait for the errorRetryInterval | ||
setTimeout(function() { | ||
//call this function to set everything up again | ||
exports.setupSSE(endpoint,syncer,true); | ||
},syncer.errorRetryInterval); | ||
}; | ||
source.onopen = function() { | ||
// only run this on first open, not on auto reconnect | ||
source.onopen = function() {}; | ||
// once we've properly opened, disable polling | ||
syncer.wiki.addTiddler({title: syncer.titleSyncDisablePolling,text: "yes"}); | ||
//sync from server manually here to make sure we stay up to date | ||
if(refresh) {syncer.syncFromServer();} | ||
} | ||
return source; | ||
} else { | ||
return null; | ||
} | ||
} | ||
|
||
})(); | ||
/*\ | ||
title: $:/plugins/twcloud/tiddlyweb-sse/sse-client.js | ||
type: application/javascript | ||
module-type: startup | ||
Miscellaneous startup logic for both the client and server. | ||
\*/ | ||
(function() { | ||
|
||
/*jslint node: true, browser: true */ | ||
/*global $tw: false */ | ||
"use strict"; | ||
var checks = [ | ||
"$:/status/IsLoggedIn", | ||
"$:/status/UserName", | ||
"$:/status/IsAnonymous", | ||
"$:/status/IsReadOnly" | ||
]; | ||
// Export name and synchronous status | ||
exports.name = "tiddlyweb-sse-hook"; | ||
exports.after = ["startup"]; | ||
exports.platforms = ["browser"]; | ||
exports.synchronous = true; | ||
exports.startup = function() { | ||
var source = null; | ||
if(!$tw.syncer || !$tw.syncer.syncadaptor || $tw.syncer.syncadaptor.name !== "tiddlyweb") {return;} | ||
$tw.wiki.addEventListener("change",function(changes) { | ||
if(checks.filter(e => changes[e]).length === 0) {return;} | ||
// check if we have a previous one and close it if we do | ||
if(source && source.readyState !== source.CLOSED) {source.close();} | ||
// Get the mount point in case a path prefix is used | ||
var host = $tw.syncer.syncadaptor.getHost(); | ||
// Make sure it ends with a slash (it usually does) | ||
if(host[host.length - 1] !== "/") {host += "/";} | ||
// get the endpoint | ||
var endpoint = host + "events/plugins/twcloud/tiddlyweb-sse/wiki-change"; | ||
// set the syncer poll to one hour | ||
$tw.syncer.pollTimerInterval = 1000 * 60 * 60; | ||
// Setup the event listener | ||
source = exports.setupSSE(endpoint,$tw.syncer); | ||
}); | ||
} | ||
|
||
function debounce(interval,callback) { | ||
var timeout = null; | ||
return function() { | ||
clearTimeout(timeout); | ||
timeout = setTimeout(callback,interval); | ||
}; | ||
} | ||
|
||
exports.setupSSE = function setupSSE(endpoint,syncer,refresh) { | ||
if(window.EventSource) { | ||
var source = new EventSource(endpoint,{withCredentials: true}); | ||
var debouncedSync = debounce(syncer.throttleInterval,syncer.syncFromServer.bind(syncer)); | ||
source.addEventListener("change",debouncedSync); | ||
source.onerror = function() { | ||
// return if we're reconnecting because that's handled automatically | ||
if(source.readyState === source.CONNECTING) {return;} | ||
// wait for the errorRetryInterval | ||
setTimeout(function() { | ||
//call this function to set everything up again | ||
exports.setupSSE(endpoint,syncer,true); | ||
},syncer.errorRetryInterval); | ||
}; | ||
source.onopen = function() { | ||
// only run this on first open, not on auto reconnect | ||
source.onopen = function() {}; | ||
// once we've properly opened, disable polling | ||
syncer.wiki.addTiddler({title: syncer.titleSyncDisablePolling,text: "yes"}); | ||
//sync from server manually here to make sure we stay up to date | ||
if(refresh) {syncer.syncFromServer();} | ||
} | ||
return source; | ||
} else { | ||
return null; | ||
} | ||
} | ||
|
||
})(); |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -1,77 +1,77 @@ | ||
/*\ | ||
title: $:/plugins/twcloud/tiddlyweb-sse/sse-server.js | ||
type: application/javascript | ||
module-type: route | ||
GET /events/plugins/twcloud/tiddlyweb/(channel) | ||
\*/ | ||
(function() { | ||
|
||
/*jslint node: true, browser: true */ | ||
/*global $tw: false */ | ||
"use strict"; | ||
|
||
var wikis = []; | ||
|
||
// Import the Journal class | ||
var Journal = require("$:/core/modules/server/server-sent-events.js").Journal; | ||
|
||
/* | ||
Setup up the array for this wiki and add the change listener | ||
*/ | ||
function setupWiki(wiki) { | ||
function filter(conn) { | ||
return conn.state.wiki === wiki; | ||
} | ||
// Listen to change events for this wiki | ||
wiki.addEventListener("change",function(changes) { | ||
var jsonChanges = JSON.stringify(changes); | ||
eventServer.emitEvent("wiki-change","change",jsonChanges,filter); | ||
}); | ||
wikis.push(wiki); | ||
} | ||
|
||
/* | ||
Setup this particular wiki if we haven't seen it before | ||
*/ | ||
function ensureChannelSetup(channel,wiki) { | ||
// setup wikis for the wiki-change channel | ||
if(channel === "wiki-change" && wikis.indexOf(wiki) === -1) { setupWiki(wiki); } | ||
} | ||
/** @type {import('../server-sent-events').Journal} */ | ||
var eventServer = new Journal(); | ||
|
||
// this filter is called for the emitter route, which recieves | ||
// messages from clients and forwards them to all listeners. It | ||
// does not affect messages sent directly by the server. | ||
// We don't use it in tiddlyweb so just set it to false | ||
eventServer.emitterFilter = function(sender) { | ||
// do not allow clients to broadcast | ||
// they can't anyway unless a route is specified | ||
return function() { return false; }; | ||
} | ||
|
||
if(!$tw.wiki.getTiddler("$:/plugins/tiddlywiki/tiddlyweb")) { | ||
$tw.utils.warning("Warning: Plugin \"twcloud/tiddlyweb-sse\" specified but \"tiddlywiki/tiddlyweb\" is missing"); | ||
} | ||
|
||
// Export the route definition for this server sent events handler. | ||
// We don't need an emitter route, otherwise we could put the common | ||
// instance in a library tiddler export and require it in both files. | ||
|
||
module.exports = eventServer.handlerExports( | ||
"plugins/twcloud/tiddlyweb-sse", | ||
function(request,response,state) { | ||
if(state.params[0] !== "wiki-change") { | ||
response.writeHead(404); | ||
response.end(); | ||
return; | ||
} | ||
// remove the socket timeout | ||
request.setTimeout(0); | ||
ensureChannelSetup(state.params[0],state.wiki); | ||
eventServer.handler(request,response,state); | ||
} | ||
); | ||
/*\ | ||
title: $:/plugins/twcloud/tiddlyweb-sse/sse-server.js | ||
type: application/javascript | ||
module-type: route | ||
GET /events/plugins/twcloud/tiddlyweb/(channel) | ||
\*/ | ||
(function() { | ||
|
||
/*jslint node: true, browser: true */ | ||
/*global $tw: false */ | ||
"use strict"; | ||
|
||
var wikis = []; | ||
|
||
// Import the Journal class | ||
var Journal = require("$:/core/modules/server/server-sent-events.js").Journal; | ||
|
||
/* | ||
Setup up the array for this wiki and add the change listener | ||
*/ | ||
function setupWiki(wiki) { | ||
function filter(conn) { | ||
return conn.state.wiki === wiki; | ||
} | ||
// Listen to change events for this wiki | ||
wiki.addEventListener("change",function(changes) { | ||
var jsonChanges = JSON.stringify(changes); | ||
eventServer.emitEvent("wiki-change","change",jsonChanges,filter); | ||
}); | ||
wikis.push(wiki); | ||
} | ||
|
||
/* | ||
Setup this particular wiki if we haven't seen it before | ||
*/ | ||
function ensureChannelSetup(channel,wiki) { | ||
// setup wikis for the wiki-change channel | ||
if(channel === "wiki-change" && wikis.indexOf(wiki) === -1) { setupWiki(wiki); } | ||
} | ||
/** @type {import('../server-sent-events').Journal} */ | ||
var eventServer = new Journal(); | ||
|
||
// this filter is called for the emitter route, which recieves | ||
// messages from clients and forwards them to all listeners. It | ||
// does not affect messages sent directly by the server. | ||
// We don't use it in tiddlyweb so just set it to false | ||
eventServer.emitterFilter = function(sender) { | ||
// do not allow clients to broadcast | ||
// they can't anyway unless a route is specified | ||
return function() { return false; }; | ||
} | ||
|
||
if(!$tw.wiki.getTiddler("$:/plugins/tiddlywiki/tiddlyweb")) { | ||
$tw.utils.warning("Warning: Plugin \"twcloud/tiddlyweb-sse\" specified but \"tiddlywiki/tiddlyweb\" is missing"); | ||
} | ||
|
||
// Export the route definition for this server sent events handler. | ||
// We don't need an emitter route, otherwise we could put the common | ||
// instance in a library tiddler export and require it in both files. | ||
|
||
module.exports = eventServer.handlerExports( | ||
"plugins/twcloud/tiddlyweb-sse", | ||
function(request,response,state) { | ||
if(state.params[0] !== "wiki-change") { | ||
response.writeHead(404); | ||
response.end(); | ||
return; | ||
} | ||
// remove the socket timeout | ||
request.setTimeout(0); | ||
ensureChannelSetup(state.params[0],state.wiki); | ||
eventServer.handler(request,response,state); | ||
} | ||
); | ||
})(); |
Oops, something went wrong.