├── Note history.png ├── Quine new instance.png ├── README.md ├── The Quine in Action.png ├── Working quine ├── boomarklet_quine.js ├── quine-github-gif.gif ├── stateful_quine.js └── stateful_quine_with_link.js /Note history.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/con-dog/serverless-architecture/f751eeaa754a15e58e239cb4d4bb8e35d67db83c/Note history.png -------------------------------------------------------------------------------- /Quine new instance.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/con-dog/serverless-architecture/f751eeaa754a15e58e239cb4d4bb8e35d67db83c/Quine new instance.png -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # TRUE SERVERLESS # 2 | 3 | ![Serverless site with quines and bookmarklets](quine-github-gif.gif) 4 | 5 | ## The Why 6 | Sick of hearing the constant marketing circle-jerk of "serverless" sites, I set out to test whether a truly server-free client only website is possible. 7 | 8 | > It is. 9 | 10 | But that's not nearly enough. 11 | 12 | I wanted to create a true serverless site that also fit the following criteria: 13 | - NO SERVER 14 | - NO DATABASE 15 | - ZERO Third party API calls 16 | - Easy to use 17 | - Stateful: It can have state and maintain its state when turned off 18 | - History 19 | - Cross Device: It can work cross devices 20 | - Self hosting (A Quine) 21 | 22 | ![Quine New Instance](Quine%20new%20instance.png) 23 | ![The Quine in Action](The%20Quine%20in%20Action.png) 24 | ![Saving and history](Note%20history.png) 25 | 26 | 27 | ## The How 28 | To achieve this I created a Bookmarklet (executable browser bookmark), that is a state-remembering quine (It outputs its own source code including current state and this output itself can be run as another program), that can sync across devices (using browser profile bookmark syncing), and to save state you simply drag a new bookmark into the bookmarks bar (easy to use) which is automatically timestamped and named for history purposes. 29 | 30 | ## How to run it? 31 | Basically once you have saved the code to a bookmark, you click the bookmark and it will run the code. Then you just interact with the notepad and write whatever. When you want to save, you drag the link at the bottom to the bookmarks bar and it will auto name it. Then, at a later stage you can click the new bookmark and it will rerun the original program, but with you previous state. 32 | 33 | You can share the link with friends and they can run it too. And if you have profile sync on, the bookmarklet should show up in the same browser on your other devices. 34 | 35 | ## Bold claims 36 | I think its a world first? Happy to be proven wrong. 37 | 38 | ## Closing remarks 39 | The final working code is the code in "stateful_quine_with_link.js". If you want to try the website out for yourself, simply save this code as a new bookmark (this is the url portion) and click the bookmark to run. (Note: Whenever you copy code from the internet please do understand what it does before executing it) 40 | 41 | ## Next Steps 42 | I want to make a Fully working Quine self hosted Python REPL bookmarklet that maintains its own history and state, as a next implementation of this project 43 | -------------------------------------------------------------------------------- /The Quine in Action.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/con-dog/serverless-architecture/f751eeaa754a15e58e239cb4d4bb8e35d67db83c/The Quine in Action.png -------------------------------------------------------------------------------- /Working quine: -------------------------------------------------------------------------------- 1 | 2 | javascript:(function(){const quine=function () { document.documentElement.textContent = `javascript:(function(){const quine=${quine.toString()};quine();})()`;};quine();})() 3 | -------------------------------------------------------------------------------- /boomarklet_quine.js: -------------------------------------------------------------------------------- 1 | javascript: (function () { 2 | const quine = function () { 3 | document.documentElement.textContent = `javascript:(function(){const quine=${quine.toString()};quine();})()`; 4 | }; 5 | 6 | quine(); 7 | })(); 8 | -------------------------------------------------------------------------------- /quine-github-gif.gif: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/con-dog/serverless-architecture/f751eeaa754a15e58e239cb4d4bb8e35d67db83c/quine-github-gif.gif -------------------------------------------------------------------------------- /stateful_quine.js: -------------------------------------------------------------------------------- 1 | javascript: (function () { 2 | const quine = function (state = "") { 3 | document.body.innerHTML = ""; 4 | const textarea = document.createElement("textarea"); 5 | textarea.value = state; 6 | document.body.appendChild(textarea); 7 | textarea.addEventListener("input", function () { 8 | const newState = textarea.value; 9 | const newQuine = `javascript:(function(){const quine=${quine.toString()};quine(\`${newState.replace( 10 | /`/g, 11 | "\\`" 12 | )}\`);})();`; 13 | navigator.clipboard.writeText(newQuine); 14 | }); 15 | textarea.addEventListener("keydown", function (e) { 16 | if (e.ctrlKey && e.key === "Enter") { 17 | const newState = textarea.value; 18 | const newQuine = `javascript:(function(){const quine=${quine.toString()};quine(\`${newState.replace( 19 | /`/g, 20 | "\\`" 21 | )}\`);})();`; 22 | navigator.clipboard.writeText(newQuine); 23 | alert("Copied quine with state to clipboard"); 24 | } 25 | }); 26 | }; 27 | quine(); 28 | })(); 29 | -------------------------------------------------------------------------------- /stateful_quine_with_link.js: -------------------------------------------------------------------------------- 1 | javascript: (function () { 2 | const quine = function (state = "") { 3 | document.head.innerHTML = ""; 4 | document.body.innerHTML = ""; 5 | document.body.style.display = "flex"; 6 | document.body.style.flexDirection = "column"; 7 | document.body.style.alignItems = "center"; 8 | const h1 = document.createElement("h1"); 9 | h1.textContent = "Serverless Notepad Quine Bookmarklet 🤖🔖"; 10 | const subheading = document.createElement("p"); 11 | subheading.textContent = 12 | "Write notes, save them, share them with others, and sync across devices - all without a server or database"; 13 | document.body.appendChild(h1); 14 | document.body.appendChild(subheading); 15 | const textarea = document.createElement("textarea"); 16 | textarea.style.width = "500px"; 17 | textarea.style.height = "500px"; 18 | textarea.style.display = "block"; 19 | textarea.style.padding = "1rem"; 20 | textarea.style.margin = "1.5rem"; 21 | const hyperlink = document.createElement("a"); 22 | hyperlink.textContent = 23 | ":: 🤏 Drag me to your bookmarks bar to save state 💾 ::"; 24 | hyperlink.style.display = "block"; 25 | hyperlink.style.cursor = "move"; 26 | hyperlink.style.padding = "1rem"; 27 | hyperlink.style.border = "1px solid blue"; 28 | hyperlink.style.borderRadius = "1rem"; 29 | textarea.value = state; 30 | document.body.appendChild(textarea); 31 | document.body.appendChild(hyperlink); 32 | const updateQuine = () => { 33 | const newState = textarea.value; 34 | const newQuine = `javascript:(function(){const quine=${quine.toString()};quine(\`${newState.replace( 35 | /`/g, 36 | "\\`" 37 | )}\`);})();`; 38 | hyperlink.href = newQuine; 39 | navigator.clipboard.writeText(newQuine); 40 | }; 41 | hyperlink.addEventListener("mouseover", function () { 42 | const options = { 43 | day: "2-digit", 44 | month: "2-digit", 45 | year: "2-digit", 46 | hour: "2-digit", 47 | minute: "2-digit", 48 | hour12: false, 49 | }; 50 | const dateTime = new Date() 51 | .toLocaleString("en-NZ", options) 52 | .replace(",", ""); 53 | hyperlink.textContent = `Notes: ${dateTime}`; 54 | }); 55 | hyperlink.addEventListener("mouseout", function () { 56 | hyperlink.textContent = 57 | ":: Drag me to your bookmarks bar to save state ::"; 58 | }); 59 | textarea.addEventListener("input", updateQuine); 60 | updateQuine(); 61 | }; 62 | quine(``); 63 | })(); 64 | --------------------------------------------------------------------------------