UNPKG

11 kBSource Map (JSON)View Raw
1{"version":3,"file":"eventsource.modern.js","sources":["../src/lib/plugin/eventsource.js"],"sourcesContent":["///=========================================================================\n/// This module provides the EventSource (SSE) feature for hyperscript\n///=========================================================================\n\n/**\n * @param {HyperscriptObject} _hyperscript\n */\nexport default _hyperscript => {\n\t_hyperscript.addFeature(\"eventsource\", function (parser, runtime, tokens) {\n\t\tif (tokens.matchToken(\"eventsource\")) {\n\t\t\tvar urlElement;\n\t\t\tvar withCredentials = false;\n\n\t\t\t// Get the name we'll assign to this EventSource in the hyperscript context\n\t\t\t/** @type {string} */\n\t\t\tvar name = parser.requireElement(\"dotOrColonPath\", tokens).evaluate();\n\t\t\tvar nameSpace = name.split(\".\");\n\t\t\tvar eventSourceName = nameSpace.pop();\n\n\t\t\t// Get the URL of the EventSource\n\t\t\tif (tokens.matchToken(\"from\")) {\n\t\t\t\turlElement = parser.requireElement(\"stringLike\", tokens);\n\t\t\t}\n\n\t\t\t// Get option to connect with/without credentials\n\t\t\tif (tokens.matchToken(\"with\")) {\n\t\t\t\tif (tokens.matchToken(\"credentials\")) {\n\t\t\t\t\twithCredentials = true;\n\t\t\t\t}\n\t\t\t}\n\n\t\t\t/** @type {EventSourceStub} */\n\t\t\tvar stub = {\n\t\t\t\teventSource: null,\n\t\t\t\tlisteners: [],\n\t\t\t\tretryCount: 0,\n\t\t\t\topen: function (url) {\n\t\t\t\t\t// calculate default values for URL argument.\n\t\t\t\t\tif (url == undefined) {\n\t\t\t\t\t\tif (stub.eventSource != null && stub.eventSource.url != undefined) {\n\t\t\t\t\t\t\turl = stub.eventSource.url;\n\t\t\t\t\t\t} else {\n\t\t\t\t\t\t\tthrow \"no url defined for EventSource.\";\n\t\t\t\t\t\t}\n\t\t\t\t\t}\n\n\t\t\t\t\t// Guard multiple opens on the same EventSource\n\t\t\t\t\tif (stub.eventSource != null) {\n\t\t\t\t\t\t// If we're opening a new URL, then close the old one first.\n\t\t\t\t\t\tif (url != stub.eventSource.url) {\n\t\t\t\t\t\t\tstub.eventSource.close();\n\t\t\t\t\t\t} else if (stub.eventSource.readyState != EventSource.CLOSED) {\n\t\t\t\t\t\t\t// Otherwise, we already have the right connection open, so there's nothing left to do.\n\t\t\t\t\t\t\treturn;\n\t\t\t\t\t\t}\n\t\t\t\t\t}\n\n\t\t\t\t\t// Open the EventSource and get ready to populate event handlers\n\t\t\t\t\tstub.eventSource = new EventSource(url, {\n\t\t\t\t\t\twithCredentials: withCredentials,\n\t\t\t\t\t});\n\n\t\t\t\t\t// On successful connection. Reset retry count.\n\t\t\t\t\tstub.eventSource.addEventListener(\"open\", function (event) {\n\t\t\t\t\t\tstub.retryCount = 0;\n\t\t\t\t\t});\n\n\t\t\t\t\t// On connection error, use exponential backoff to retry (random values from 1 second to 2^7 (128) seconds\n\t\t\t\t\tstub.eventSource.addEventListener(\"error\", function (event) {\n\t\t\t\t\t\t// If the EventSource is closed, then try to reopen\n\t\t\t\t\t\tif (stub.eventSource.readyState == EventSource.CLOSED) {\n\t\t\t\t\t\t\tstub.retryCount = Math.min(7, stub.retryCount + 1);\n\t\t\t\t\t\t\tvar timeout = Math.random() * (2 ^ stub.retryCount) * 500;\n\t\t\t\t\t\t\twindow.setTimeout(stub.open, timeout);\n\t\t\t\t\t\t}\n\t\t\t\t\t});\n\n\t\t\t\t\t// Add event listeners\n\t\t\t\t\tfor (var index = 0; index < stub.listeners.length; index++) {\n\t\t\t\t\t\tvar item = stub.listeners[index];\n\t\t\t\t\t\tstub.eventSource.addEventListener(item.type, item.handler, item.options);\n\t\t\t\t\t}\n\t\t\t\t},\n\t\t\t\tclose: function () {\n\t\t\t\t\tif (stub.eventSource != undefined) {\n\t\t\t\t\t\tstub.eventSource.close();\n\t\t\t\t\t}\n\t\t\t\t\tstub.retryCount = 0;\n\t\t\t\t},\n\t\t\t\taddEventListener: function (type, handler, options) {\n\t\t\t\t\tstub.listeners.push({\n\t\t\t\t\t\ttype: type,\n\t\t\t\t\t\thandler: handler,\n\t\t\t\t\t\toptions: options,\n\t\t\t\t\t});\n\n\t\t\t\t\tif (stub.eventSource != null) {\n\t\t\t\t\t\tstub.eventSource.addEventListener(type, handler, options);\n\t\t\t\t\t}\n\t\t\t\t},\n\t\t\t};\n\n\t\t\t// Create the \"feature\" that will be returned by this function.\n\n\t\t\t/** @type {EventSourceFeature} */\n\t\t\tvar feature = {\n\t\t\t\tname: eventSourceName,\n\t\t\t\tobject: stub,\n\t\t\t\tinstall: function (target) {\n\t\t\t\t\truntime.assignToNamespace(target, nameSpace, eventSourceName, stub);\n\t\t\t\t},\n\t\t\t};\n\n\t\t\t// Parse each event listener and add it into the list\n\t\t\twhile (tokens.matchToken(\"on\")) {\n\t\t\t\t// get event name\n\t\t\t\tvar eventName = parser.requireElement(\"stringLike\", tokens, \"Expected event name\").evaluate(); // OK to evaluate this in real-time?\n\n\t\t\t\t// default encoding is \"\" (autodetect)\n\t\t\t\tvar encoding = \"\";\n\n\t\t\t\t// look for alternate encoding\n\t\t\t\tif (tokens.matchToken(\"as\")) {\n\t\t\t\t\tencoding = parser.requireElement(\"stringLike\", tokens, \"Expected encoding type\").evaluate(); // Ok to evaluate this in real time?\n\t\t\t\t}\n\n\t\t\t\t// get command list for this event handler\n\t\t\t\tvar commandList = parser.requireElement(\"commandList\", tokens);\n\t\t\t\taddImplicitReturnToCommandList(commandList);\n\t\t\t\ttokens.requireToken(\"end\");\n\n\t\t\t\t// Save the event listener into the feature. This lets us\n\t\t\t\t// connect listeners to new EventSources if we have to reconnect.\n\t\t\t\tstub.listeners.push({\n\t\t\t\t\ttype: eventName,\n\t\t\t\t\thandler: makeHandler(encoding, commandList),\n\t\t\t\t});\n\t\t\t}\n\n\t\t\ttokens.requireToken(\"end\");\n\n\t\t\t// If we have a URL element, then connect to the remote server now.\n\t\t\t// Otherwise, we can connect later with a call to .open()\n\t\t\tif (urlElement != undefined) {\n\t\t\t\tstub.open(urlElement.evaluate());\n\t\t\t}\n\n\t\t\t// Success!\n\t\t\treturn feature;\n\n\t\t\t////////////////////////////////////////////\n\t\t\t// ADDITIONAL HELPER FUNCTIONS HERE...\n\t\t\t////////////////////////////////////////////\n\n\t\t\t/**\n\t\t\t * Makes an eventHandler function that can execute the correct hyperscript commands\n\t\t\t * This is outside of the main loop so that closures don't cause us to run the wrong commands.\n\t\t\t *\n\t\t\t * @param {string} encoding\n\t\t\t * @param {*} commandList\n\t\t\t * @returns {EventHandlerNonNull}\n\t\t\t */\n\t\t\tfunction makeHandler(encoding, commandList) {\n\t\t\t\treturn function (evt) {\n\t\t\t\t\tvar data = decode(evt[\"data\"], encoding);\n\t\t\t\t\tvar context = runtime.makeContext(stub, feature, stub);\n\t\t\t\t\tcontext.event = evt;\n\t\t\t\t\tcontext.result = data;\n\t\t\t\t\tcommandList.execute(context);\n\t\t\t\t};\n\t\t\t}\n\n\t\t\t/**\n\t\t\t * Decodes/Unmarshals a string based on the selected encoding. If the\n\t\t\t * encoding is not recognized, attempts to auto-detect based on its content\n\t\t\t *\n\t\t\t * @param {string} data - The original data to be decoded\n\t\t\t * @param {string} encoding - The method that the data is currently encoded (\"string\", \"json\", or unknown)\n\t\t\t * @returns {string} - The decoded data\n\t\t\t */\n\t\t\tfunction decode(data, encoding) {\n\t\t\t\t// Force JSON encoding\n\t\t\t\tif (encoding == \"json\") {\n\t\t\t\t\treturn JSON.parse(data);\n\t\t\t\t}\n\n\t\t\t\t// Otherwise, return the data without modification\n\t\t\t\treturn data;\n\t\t\t}\n\n\t\t\t/**\n\t\t\t * Adds a \"HALT\" command to the commandList.\n\t\t\t * TODO: This seems like something that could be optimized:\n\t\t\t * maybe the parser could do automatically,\n\t\t\t * or could be a public function in the parser available to everyone,\n\t\t\t * or the command-executer-thingy could just handle nulls implicitly.\n\t\t\t *\n\t\t\t * @param {*} commandList\n\t\t\t * @returns void\n\t\t\t */\n\t\t\tfunction addImplicitReturnToCommandList(commandList) {\n\t\t\t\tif (commandList.next) {\n\t\t\t\t\treturn addImplicitReturnToCommandList(commandList.next);\n\t\t\t\t}\n\n\t\t\t\tcommandList.next = {\n\t\t\t\t\ttype: \"implicitReturn\",\n\t\t\t\t\top: function (/** @type {Context} */ _context) {\n\t\t\t\t\t\treturn runtime.HALT;\n\t\t\t\t\t},\n\t\t\t\t\texecute: function (/** @type {Context} */ _context) {\n\t\t\t\t\t\t// do nothing\n\t\t\t\t\t},\n\t\t\t\t};\n\t\t\t}\n\t\t}\n\t});\n}"],"names":["_hyperscript","addFeature","parser","runtime","tokens","matchToken","urlElement","withCredentials","nameSpace","requireElement","evaluate","split","eventSourceName","pop","stub","eventSource","listeners","retryCount","open","url","undefined","close","readyState","EventSource","CLOSED","addEventListener","event","Math","min","timeout","random","window","setTimeout","index","length","item","type","handler","options","push","feature","name","object","install","target","assignToNamespace","eventName","encoding","commandList","addImplicitReturnToCommandList","requireToken","makeHandler","evt","data","JSON","parse","decode","context","makeContext","result","execute","next","op","_context","HALT"],"mappings":"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"}
\No newline at end of file