{"id":15813437,"url":"https://github.com/skn0tt/interval-on-edge-functions","last_synced_at":"2025-04-01T00:43:19.092Z","repository":{"id":88224431,"uuid":"507271968","full_name":"Skn0tt/interval-on-edge-functions","owner":"Skn0tt","description":null,"archived":false,"fork":false,"pushed_at":"2023-12-16T05:57:58.000Z","size":37,"stargazers_count":1,"open_issues_count":2,"forks_count":0,"subscribers_count":0,"default_branch":"main","last_synced_at":"2024-10-06T04:02:12.632Z","etag":null,"topics":[],"latest_commit_sha":null,"homepage":"https://interval-on-edge-functions.netlify.app/","language":"TypeScript","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/Skn0tt.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,"dei":null,"publiccode":null,"codemeta":null}},"created_at":"2022-06-25T09:47:43.000Z","updated_at":"2022-06-27T11:43:30.000Z","dependencies_parsed_at":"2024-10-26T09:45:43.945Z","dependency_job_id":"0688864a-3f6c-4a08-bbac-8247a5691af0","html_url":"https://github.com/Skn0tt/interval-on-edge-functions","commit_stats":null,"previous_names":[],"tags_count":3,"template":false,"template_full_name":null,"repository_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/Skn0tt%2Finterval-on-edge-functions","tags_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/Skn0tt%2Finterval-on-edge-functions/tags","releases_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/Skn0tt%2Finterval-on-edge-functions/releases","manifests_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/Skn0tt%2Finterval-on-edge-functions/manifests","owner_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners/Skn0tt","download_url":"https://codeload.github.com/Skn0tt/interval-on-edge-functions/tar.gz/refs/heads/main","host":{"name":"GitHub","url":"https://github.com","kind":"github","repositories_count":246563384,"owners_count":20797446,"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":[],"created_at":"2024-10-05T04:02:07.466Z","updated_at":"2025-04-01T00:43:19.070Z","avatar_url":"https://github.com/Skn0tt.png","language":"TypeScript","funding_links":[],"categories":[],"sub_categories":[],"readme":"# Interval on Edge Functions\n\nI stumbled accross https://interval.com, and found it *really* interesting.\nIt's a tool for rapidly building internal tooling based on an existing codebase,\nand uses an interesting paradigm where state is kept in a long-lived session on the server,\nand the UI is \"remote-controlled\" by it.\n\nIt got me thinking: What would it take to implement something like this via Edge Functions?\n\n## How does Interval work?\n\nTo understand the idea of this proof-of-concept, let's look at why I find Interval so\nintriguing.\n\nThis codesnippet is taken from Interval's landing page, and would live in your application's codebase:\n\n```ts\nasync function refond_user(io, ctx) {\n  const email = await io.input.email(\"Email\");\n\n  const charges = await getCharges({ email });\n\n  const toRefund = await io.select.table(\n    \"Select one or more charges to refund\",\n    { data: charges }\n  );\n\n  ctx.loading.start({\n    label: \"Refunding charges\",\n    itemsInQueue: toRefund.length,\n  });\n\n  for (const charge of toRefund) {\n    await refundCharge(charge.id);\n    ctx.loading.completeOne();\n  }\n}\n\nnew Interval({ actions: { refund_user } }).listen();\n```\n\nThis code runs as part of your application codebase. `new Interval(...).listen()` opens a long-runnning connection to the Interval servers and registers the available workflows (Interval calls them `actions`).\nWhen a workflow is started, the Interval servers call your application server to start a workflow session (Interval calls them `transactions`).\nThe workflows then can use the `await io.input.[...]` methods to request user input,\nwhich Interval relays to the logged-in user.\n\nThe model is: your application keeps all state, and `Interval` is an easy-to-build interaction layer.\nI really like it, as it allows colocating internal tooling with the rest of the codebase.\nThough, with the current paradigm of stateless + shortlived servers, the idea of a long-running session\non the server sounds like an impedance mismatch.\nThis would simply not be possible to implement with AWS Lambda, for example!\nIt's maximum execution time of 15 minutes limits what kind of workflows one can implement using this.\n\nHowever, with Deno limiting by *CPU time* as opposed to *execution time*, this can become more feasible.\nThese workflows idle for most of the time, so they should be relatively easy to keep running for long.\n\n## How does the PoC work?\n\nThe goal of this repoistory is to re-implement the functionality described above as simple as possible,\nand to use Netlify Edge Functions for it.\nThe Edge Function implements the workflow, makes requests to databases and APIs, a single-page UI\nacts as a remote-controlled UI.\nUI and workflow need a two-way communication\nchannel. Ideally, this would be a websocket.\nEdge Functions don't directly support them, so we'll use [SimpleMQ](https://gitlab.com/Skn0tt/simplemq) as a relay for now.\n\n\u003e [I've tried using WebSockets](https://github.com/Skn0tt/interval-on-edge-functions/tree/attempt-websockets) directly from the Edge Function, but to no success.\n\u003e Netlify seems to strip out the `Update` header, which prevents WebSocket connections from initialising.\n\n\u003e [I've tried using `BroadcastChannel` + `EventSource`](https://github.com/Skn0tt/interval-on-edge-functions/tree/attempt-broadcastchannel), as a replacement for SimpleMQ, but `BroadcastChannel` seems to be [unstable](https://github.com/denoland/deno/blob/6bb555b549a203ebe921260379f3da70787be655/ext/broadcast_channel/lib.rs#L44), and thus unavailable, at the moment.\n\n## Try it out\n\nGo to https://interval-on-edge-functions.netlify.app to check it out,\nand read through the code in this repository to check how it's implemented!\n","project_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Fskn0tt%2Finterval-on-edge-functions","html_url":"https://awesome.ecosyste.ms/projects/github.com%2Fskn0tt%2Finterval-on-edge-functions","lists_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Fskn0tt%2Finterval-on-edge-functions/lists"}