{"id":19890119,"url":"https://github.com/floodfx/undead","last_synced_at":"2025-05-02T18:30:53.982Z","repository":{"id":199795338,"uuid":"703617210","full_name":"floodfx/undead","owner":"floodfx","description":"LiveView server implementation for the JVM","archived":false,"fork":false,"pushed_at":"2023-12-17T00:39:00.000Z","size":477,"stargazers_count":61,"open_issues_count":1,"forks_count":2,"subscribers_count":2,"default_branch":"main","last_synced_at":"2025-04-07T04:33:04.218Z","etag":null,"topics":["java","kotlin","liveview","stringtemplate","undead"],"latest_commit_sha":null,"homepage":"","language":"JavaScript","has_issues":true,"has_wiki":null,"has_pages":null,"mirror_url":null,"source_name":null,"license":null,"status":null,"scm":"git","pull_requests_enabled":true,"icon_url":"https://github.com/floodfx.png","metadata":{"files":{"readme":"README.md","changelog":null,"contributing":null,"funding":null,"license":null,"code_of_conduct":null,"threat_model":null,"audit":null,"citation":null,"codeowners":null,"security":null,"support":null,"governance":null,"roadmap":null,"authors":null}},"created_at":"2023-10-11T15:18:07.000Z","updated_at":"2024-12-09T10:17:29.000Z","dependencies_parsed_at":"2023-10-17T05:44:17.168Z","dependency_job_id":"bb11abb0-4a64-42e7-938b-eef6c7de43fd","html_url":"https://github.com/floodfx/undead","commit_stats":null,"previous_names":["floodfx/undead4j"],"tags_count":0,"template":false,"template_full_name":null,"repository_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/floodfx%2Fundead","tags_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/floodfx%2Fundead/tags","releases_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/floodfx%2Fundead/releases","manifests_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/floodfx%2Fundead/manifests","owner_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners/floodfx","download_url":"https://codeload.github.com/floodfx/undead/tar.gz/refs/heads/main","host":{"name":"GitHub","url":"https://github.com","kind":"github","repositories_count":252088246,"owners_count":21692764,"icon_url":"https://github.com/github.png","version":null,"created_at":"2022-05-30T11:31:42.601Z","updated_at":"2022-07-04T15:15:14.044Z","host_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub","repositories_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories","repository_names_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repository_names","owners_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners"}},"keywords":["java","kotlin","liveview","stringtemplate","undead"],"created_at":"2024-11-12T18:12:59.104Z","updated_at":"2025-05-02T18:30:53.134Z","avatar_url":"https://github.com/floodfx.png","language":"JavaScript","readme":"# 🧟🧛‍♀️🐺 Undead - LiveViews for the JVM\n[![javadoc](https://javadoc.io/badge2/run.undead/undead-core/javadoc.svg)](https://javadoc.io/doc/run.undead/undead-core)\n\n\n## What is Undead?\n\n`Undead` is dead-serious library for JVM devs who want to build scary fast, bewitching front-end experiences on the JVM\n*without writing javascript* 🙀.  \n\n`Undead` is a LiveView implementation for the JVM. (The name \"Undead\" is a play on the \"Live\" part of LiveViews 👻.)\nLiveViews let you build front-end experiences like those you can build with [React](https://react.dev)\nor [Vue.js](https://vuejs.org)\nbut all without leaving the server.  (See [what is a liveview](#what-is-a-liveview) for more details on LiveViews but \nyou don't need to know what a LiveView is to use `Undead`.)\n\n### NOTE: Undead relies on Java 21 Preview Features\n`Undead` is built on top [StringTemplate](https://openjdk.org/jeps/430)s which is a \"Preview Feature\" of Java 21.  In order to \nbuild and run `Undead` apps you have to run your IDE and/or the JVM with the `--enable-preview` flag and target Java `21`.  The `pom.xml` \nin this repo show you where you need to add flags.  If you are running IntelliJ you have to futz with the settings...again this repo ships\nwith the IntelliJ settings.  I don't know about Eclipse and last time I checked VSCode plugins they didn't support Java 21 yet.  \n\nSometimes you have to work a little to be on the bleeding edge...🔪\n\n## 🍬 Benefits of building apps with Undead\n\n* No need to write javascript\n* Very fast, SEO-friendly, fully rendered HTML initial response\n* Extremely efficient, diff-based updates over Websockets\n* Small, easy-to-learn, yet powerful API\n* Modern, type-safe templating engine (String Templates)\n* Just another route on your existing web server\n\nFurther `Undead` does the following:\n* Makes _normal_ things **trivial**\n  * Dynamic, validated forms that map into model objects (and vice versa)\n  * Dynamic page updates based on user actions (clicks, keys, focus, etc)\n  * Debounce and throttle user events with ease\n* Makes _hard_ things **easy**\n  * File uploads with drag-n-drop, progress, and image previews\n  * Multiplayer UIs, presence, and other real-time interactions using pub/sub\n  * Only ship diffs reducing network overhead by huge amounts (transparent to dev btw)\n* Makes _expensive_/_complicated_ things **unecessary**\n  * No need for REST or GraphQL APIs (but easier to integrate with them if you do)\n  * No need for front-end vs back-end code bases, FE vs BE state synchronization, etc\n  * No need to build or maintain a large Javascript component library\n  * No need to write Javascript period.\n\n## 🦠 Current Status: Incubating / Alpha\n\n`Undead` is currently in the \"incubating\" stage. It is probably not yet ready for production but \"it's alive!\" and\na decent chunk of functionality is implemented along with pretty good documentation (especially the javadocs). If you\naren't too afraid 👻 you can try it out and provide feedback. Check out working examples in\nthe [example](src/main/java/run/undead/javalin/example)\ncode directory. You can run these examples by checking out this repository and running:\n\n```shell\nmvn package exec:exec\n```\n\nThen open your browser to http://localhost:1313 and you should see the examples.\n\n## ⭐️ Stars, Feedback, and Signups Welcome\n\nPlease feel free to open an issue or PR if you have any feedback or suggestions. If you like this project, feel free\nto ⭐️ it!  You can also [signup for updates](https://mailchi.mp/40717440e95b/undead-signup).  (Only the cursed would\nshare your email address.)\n\n## Adding `Undead` to your project\n\nIf you are brave enough to try it out, you can add `Undead` to your project by adding the following dependency to your\n`pom.xml`:\n\n```xml\n\n\u003cdependency\u003e\n    \u003cgroupId\u003erun.undead\u003c/groupId\u003e\n    \u003cartifactId\u003eundead-core\u003c/artifactId\u003e\n    \u003cversion\u003e0.0.15\u003c/version\u003e\n\u003c/dependency\u003e\n```\n\n## Example Undead View\n\n```java\n// Sample View that counts up and down \npublic class UndeadCounter implements View {\n  private int count;\n\n  public UndeadCounter() {\n    this.count = 0;\n  }\n\n  // Handle events from the browser\n  public void handleEvent(Socket context, UndeadEvent event) {\n    if (\"inc\".equals(event.type())) {\n      this.count++;\n    } else if (\"dec\".equals(event.type())) {\n      this.count--;\n    }\n  }\n\n  // Render the HTML based on the current state (e.g. count)\n  public UndeadTemplate render(Meta meta) {\n    return HTML.\"\"\"\n        \u003cdiv class=\"flex flex-col space-y-4 mx-4\"\u003e\n          \u003ch2 class=\"text-2xl\"\u003eCount:\n            \u003cspan class=\"\\{ If(count \u003e 0, HTML.\"text-success\", HTML.\"text-warning\") }\"\u003e\n              \\{ count }\n            \u003c/span\u003e\n          \u003c/h2\u003e\n          \u003cdiv class=\"flex space-x-4\"\u003e\n            \u003cbutton class=\"btn btn-primary\" type=\"button\" ud-click=\"dec\"\u003eDecrement\u003c/button\u003e\n            \u003cbutton class=\"btn btn-primary\" type=\"button\" ud-click=\"inc\"\u003eIncrement\u003c/button\u003e\n          \u003c/div\u003e\n          \"\"\";\n  }\n\n}\n```\n\n## Undead Views\n\nUndead `View`s are the lifeblood of the Undead framework.  `View` provides a simple yet powerful interface that for\nhandling events from the browser, updating state, and rendering HTML.\n\n### `Undead` View interface\n\nThe View interface defines the lifecycle callbacks for an Undead View. Views have a lifecycle that consists of a short,\nfast HTTP request/response and a long-lived WebSocket connection.\n\n**Implementation Note:** Views should have a no-arg constructor as they are instantiated by reflection.\n\n#### View Interface methods\n\n- `View.mount` - (optional) mount is where a View can be initialized based on session data and/or path or query\n  parameters\n- `View.handleParams` - (optional) handleParams is called after mount and whenever there is a URI change\n- `View.handleEvent` - (optional) handleEvent is called when an event (click, keypress, etc) is received from the\n  browser\n- `View.handleInfo` - (optional) handleInfo is called when an event (a.k.a info) is received from the server\n- `View.render` - (required) render returns an `UndeadTemplate` based on the state of the View\n- `View.shutdown` - (optional) shutdown is called when the View is shutting down and is a good place to clean up\n\nAs you can see the only required method is `View.render` which is called to render the View based on its state. The\nother methods are optional so you can implement only the callbacks that you need for your View.\n\n## Templating\n\nUndead provides a [String Templates](https://openjdk.java.net/jeps/430)-based template engine that make LiveViews\npossible.  \nString Templates are a new feature in Java 21 and therefore Undead **only works on Java 21** (and theoretically above).\nString\nTemplates have a slightly weird escape syntax (e.g. `\\{ count }`) but they are very powerful, easy to use, type-safe,\nand can\ncreate whatever HTML you want. Undead provides a number of pre-built directives that make building complex HTML easy.\n\n### Example Undead Template\n\n```java\nUndead.HTML.\"\"\"\n\u003cdiv class=\"flex flex-col space-y-4 mx-4\"\u003e\n  \u003ch2 class=\"text-2xl\"\u003eCount:\n    \u003cspan class=\"\\{ If(count \u003e 0, HTML.\"text-success\", HTML.\"text-warning\") }\"\u003e\n      \\{ count }\n    \u003c/span\u003e\n  \u003c/h2\u003e\n  \u003cdiv class=\"flex space-x-4\"\u003e\n    \u003cbutton class=\"btn btn-primary\" type=\"button\" ud-click=\"dec\"\u003eDecrement\u003c/button\u003e\n    \u003cbutton class=\"btn btn-primary\" type=\"button\" ud-click=\"inc\"\u003eIncrement\u003c/button\u003e\n  \u003c/div\u003e\n  \"\"\";\n```\n\n### `Undead.HTML` Templates\n\nThe `Undead.HTML` processor is a [String Template Processor](https://openjdk.java.net/jeps/430) that is used to build\nHTML template in a type-safe, LiveView-friendly way.  `Undead.HTML` automatically escapes HTML entities in order to\nprevent XSS attacks. Beyond escaping HTML\nentities, it does no other parsing or validation of the HTML but it does take advantage of StringTemplates to optimize\ndiff being sent over the wire.  \nA simple example of `Undead.HTML` in action is below:\n\n```java\n// Single line\nUndead.HTML.\"Hello, \\{ name }!\";\n\n// Multiline\nUndead.HTML.\"\"\"\n  \u003cdiv class=\"flex flex-col space-y-4 mx-4\"\u003e\n    \u003ch2 class=\"text-2xl\"\u003eHello, \\{ name }!\u003c/h2\u003e      \n  \u003c/div\u003e\n\"\"\";\n```\n\nYou can read more about String Templates [here](https://openjdk.java.net/jeps/430)\nand [here](https://www.infoq.com/news/2023/04/java-gets-a-boost-with-string/). But you really don't\nneed to know much about them to use `Undead.HTML` templates other than how to embed values (i.e. `\\{ myValue }`). Note,\nyou can statically import `Undead.HTML` to shorten it to `HTML`.\n\n### Undead Template Directives\n\nUndead provides template directives for common needs like if statements, switch statements, mapping over a collection,\nranges, etc. Basically all the normal\ntemplating things you'd expect. You can also easily add your own template directives and/or custom functions that can be\nused in your templates. Below is a list\nof the built-in directives:\n\n* `If` Directive - `If` models an if statement in a template. If the provided condition is true the trueCase template is\n  returned\n  otherwise an empty template is returned. There is also an `If` directive that can return either the trueCase or\n  falseCase template.\n* `Switch` Directive - `Switch` models a switch statement in a template. The provided value is compared to each case and\n  if there is a match\n  the corresponding template is returned. If there is no match, the default template is returned (or an empty template\n  if there is no default).\n* `For` Directive - `For` enables transforming a collection of values into a collection of templates. The provided\n  collection is iterated applying the provided template function to each value in the collection.\n* `Range` Directive - `Range` enables iterating over a range of integer values optionally by a given step.\n* `Join` Directive - `Join` enabled joining a collection of templates together with a given separator template.\n\nThere are typically two types of logic directive, those that take a simple boolean condition (e.g. x == 1) and\nthose that take a `Predicate`s which are used into lambda form (e.g. x -\u003e x == 1). The former is more concise but the\nlatter is more flexible.  \nAdditionally, the directives that accept a `Predicate` also require a `Function\u003cT, UndeadTemplate\u003e` to be provided. This\nmeans the function that\nreturns the template has access to the value being tested which is very useful for adding to a template or\nusing `Function` defined elsewhere.\n\nExamples\n\n```java\n// If Directive\nHTML.\"\"\"\n  \\{ If(zombieCount \u003e 0, HTML.\"\u003cspan\u003e🙀Uh oh, there are zombies!\u003c/span\u003e\") }\n  \"\"\";\n\n// If/Else Directive\nHTML.\"\"\"\n  \u003cdiv\u003e\\{ \n    If(brainsEaten \u003c 10, \n      HTML.\"Need more 🧠s...\", \n      HTML.\"Time for 🛏!\") \n    }\n  \u003c/div\u003e\n  \"\"\";\n\n// Switch Directive\nHTML.\"\"\"\n  \u003cdiv class=\"\\{\n    Switch(\n      Case.of(\"blue\".equals(color), HTML.\"text-blue\"),\n      Case.of(\"red\".equals(color), HTML.\"text-red\"),\n      Case.defaultOf(HTML.\"text-green\")\n    )}\"\u003e\n    \\{ color } is a nice color\n  \u003c/div\u003e\n\"\"\";\n\n// For Directive\nHTML.\"\"\"\n  \u003cul\u003e\n    \\{ For(zombies, zombie -\u003e HTML.\"\u003cli\u003e\\{ zombie.name }\u003c/li\u003e\") }\n  \u003c/ul\u003e\n\"\"\";\n\n// Range Directive\nHTML.\"\"\"\n  \\{For(\n      Range(10),\n      i -\u003e HTML.\"\u003cdiv\u003eStep \\{i}\u003c/div\u003e\"\n  )}\n\"\"\";\n// Range Directive with step\nHTML.\"\"\"\n  \\{For(\n      Range(2, 10, 2),\n      i -\u003e HTML.\"\u003cdiv\u003e\\{i}\u003c/div\u003e\"\n  )}\n\"\"\"\n\n```\n\n### Using Undead Templates for \"DeadViews\" (i.e. traditional) HTML rendering\n\nIt is fine to use Undead Templates to render HTML outside of LiveViews so feel free to do so. You can use\nthe `Undead.HTML` processor\nthe same way and call `toString()` on it to get the rendered HTML.\n\n## Javascript Commands\n\nUndead provides a number of Javascript commands that can be used to show/hide elements, add/remove classes, dispatch\nevents, etc. These commands are\ncalled JS Commands and can be part of the Undead Template you define in a `View`s `render` function. JS Commands are\nloaded on the client-side\nwhen the template is rendered and executed based on user interactions. JS Commands are a powerful way to manipulate the\nDOM without having to write javascript.  \nBelow is a list of the commands:\n\n* JS.addClass - Adds a class to an element\n* JS.dispatch - Dispatches an event on an element\n* JS.exec - Executes JS commands on an element's attribute\n* JS.focus - Focuses an element\n* JS.focusFirst - Focuses the first element in a selector\n* JS.hide - Hides an element\n* JS.navigate - Sends a navigation event to the server\n* JS.patch - Sends a patch event to the server\n* JS.popFocus - Pops the focus stack\n* JS.push - Pushes an event to the server\n* JS.pushFocus - Pushes the focus stack\n* JS.removeAttr - Removes an attribute from an element\n* JS.removeClass - Removes a class from an element\n* JS.setAttr - Sets an attribute on an element\n* JS.show - Shows an element\n* JS.toggle - Toggles visibility of an element\n* JS.transition - Applies css transitions to an element\n\nSee the JS Commands javadocs for more details on each command and its options.\n\n## `Undead` Javascript\n\nLike I said, you don't have to write javascript...and if javascript is too ghastly for you to write, you can continue\nto use the `undead.js` as is (see the resources directory) and just make sure it is loaded on your page.  \nHowever, if you want to wade into the gory details of the javascript, check out the `/js` directory. Inside is a simple\nnode project that uses [esbuild](https://esbuild.github.io/) to build the `undead.js` file from typescript\n(i.e. `js/undead.ts`). Assuming you have npm installed, to run it:\n\n```shell\ncd js\nnpm install\nnpm run build\n```\n\n## Why did you write Undead?\n\nLiveViews (a.k.a UndeadViews 🧟) are an enchanting, new paradigm for building bewitching front-end experiences and I\nwanted to see if how hard it would be to implement them on the JVM. I poked around the existing templating libraries\nand didn't find anything that was ideal but when I learned about String Templates I thought they might be a good fit\nand waited for them to be released. Once they were released, it was just a matter of dusting off the ol' Java skills\n(still very rusty - thank you very much).\n\nIt also helps that I've written (or co-written) LiveView libraries for [Javascript](https://liveviewjs.com)\nand [Go](https://github.com/canopyclimate/golive) so know the\nprotocol fairly well at this point.\n\n## TODO\n\nMost of these are advanced features or internal details that you don't need to know about to use Undead.\n~~This is very early / PoC stage / \"nothing works\".~~ The basics work:\n\n- [x] Templating - `Undead.HTML`, `If`, `Switch`, `For`, `Range`, `Join` Directives\n- [x] Message JSON parsing and generation\n- [x] Basic Protocol `phx-join`, `heartbeat`, page events (`click`, `keyup`, `blur`, etc) and `form` events\n- [x] Internal Events (i.e. InfoEvents)\n- [x] Example Project - Javalin + Undead with examples: `UndeadCounter`, `UndeadUserForm`, `UndeadSalesDashnoard`\n- [x] Parts Diffing\n- [x] Push Events\n- [ ] Live Patch / Navigation\n- [ ] File Uploads\n- [ ] UndeadComponents (i.e. LiveComponents)\n- [x] Building Client Javascript\n- [x] JS Commands\n- [x] Pub/Sub\n\n## What is a LiveView?\nAt a high level, a Live...err... an UndeadView, is a server process that receives events (clicks, form input, etc) from\nthe browser, updates\nits state, and sends back diffs which are applied to the browser. You could say it makes \"spooky action at a\ndistance\"  very easy for developers ✨. `Undead` handles all the spine-chillingly difficult things; automatically routing\nevents, providing a clean API for devs, and rendering (and diffing) the HTML, and applying those diffs efficiently so\nthe\ndevelopers can just focus on enchanting their users.\n\nLiveViews where invented and popularized by the [Phoenix Framework](https://www.phoenixframework.org/) which is written\nin Elixir and\nruns on the Erlang VM. Obviously, `Undead` is not Elixir or Erlang but it adheres to the LiveView protocol and reuses\nthe client-side\njavascript from the Phoenix Framework.  (Suffice it to say, the Phoenix Framework is awesome and kudos to the\nElixir/Erlang community for\ninventing LiveViews! 🙌)\n\n\n## Feedback Welcome\n\nI haven't written Java professionally in about 12 years so would love any feedback and/or PRs!\n\n## Elixir / Erlang folks\n\nPlease don't [Greenspun's tenth rule](https://en.wikipedia.org/wiki/Greenspun%27s_tenth_rule) me about \"the BEAM\" 🙀😀.\n","funding_links":[],"categories":[],"sub_categories":[],"project_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Ffloodfx%2Fundead","html_url":"https://awesome.ecosyste.ms/projects/github.com%2Ffloodfx%2Fundead","lists_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Ffloodfx%2Fundead/lists"}