Simple, single-user event aggregation platform, for use in personal websites and other related places.
event-streaming single-user events event-aggregation

✨ Add GET and POST endpoints for events

Jo fa0ad7f3 5930a49f

+222 -1
+1 -1
README.md
··· 22 ### Installation 23 To install dependencies, run the following command: 24 ```sh 25 - bun install snowflake 26 ``` 27 28 ### Execution
··· 22 ### Installation 23 To install dependencies, run the following command: 24 ```sh 25 + bun install 26 ``` 27 28 ### Execution
+26
bun.lock
···
··· 1 + { 2 + "lockfileVersion": 1, 3 + "configVersion": 1, 4 + "workspaces": { 5 + "": { 6 + "name": "snowflake", 7 + "dependencies": { 8 + "hono": "^4.11.4", 9 + }, 10 + "devDependencies": { 11 + "@types/bun": "^1.3.6", 12 + }, 13 + }, 14 + }, 15 + "packages": { 16 + "@types/bun": ["@types/bun@1.3.6", "", { "dependencies": { "bun-types": "1.3.6" } }, "sha512-uWCv6FO/8LcpREhenN1d1b6fcspAB+cefwD7uti8C8VffIv0Um08TKMn98FynpTiU38+y2dUO55T11NgDt8VAA=="], 17 + 18 + "@types/node": ["@types/node@25.0.8", "", { "dependencies": { "undici-types": "~7.16.0" } }, "sha512-powIePYMmC3ibL0UJ2i2s0WIbq6cg6UyVFQxSCpaPxxzAaziRfimGivjdF943sSGV6RADVbk0Nvlm5P/FB44Zg=="], 19 + 20 + "bun-types": ["bun-types@1.3.6", "", { "dependencies": { "@types/node": "*" } }, "sha512-OlFwHcnNV99r//9v5IIOgQ9Uk37gZqrNMCcqEaExdkVq3Avwqok1bJFmvGMCkCE0FqzdY8VMOZpfpR3lwI+CsQ=="], 21 + 22 + "hono": ["hono@4.11.4", "", {}, "sha512-U7tt8JsyrxSRKspfhtLET79pU8K+tInj5QZXs1jSugO1Vq5dFj3kmZsRldo29mTBfcjDRVRXrEZ6LS63Cog9ZA=="], 23 + 24 + "undici-types": ["undici-types@7.16.0", "", {}, "sha512-Zz+aZWSj8LE6zoxD+xrjh4VfkIG8Ya6LvYkZqtUQGJPZjYl53ypCaUwWqo7eI0x66KBGeRo+mlBEkMSeSZ38Nw=="], 25 + } 26 + }
+6
package.json
··· 12 "scripts": { 13 "dev": "bun --watch .", 14 "prod": "bun ." 15 } 16 }
··· 12 "scripts": { 13 "dev": "bun --watch .", 14 "prod": "bun ." 15 + }, 16 + "devDependencies": { 17 + "@types/bun": "^1.3.6" 18 + }, 19 + "dependencies": { 20 + "hono": "^4.11.4" 21 } 22 }
+17
src/index.ts
···
··· 1 + // ------------------------------------------------------------ 2 + // Imports & Initialization 3 + // ------------------------------------------------------------ 4 + 5 + import { Hono } from "hono"; 6 + 7 + // Define new Hono instance and bind routes directory 8 + const app = new Hono().route("/", (await import("./routes")).default); 9 + 10 + // ------------------------------------------------------------ 11 + // Exports 12 + // ------------------------------------------------------------ 13 + 14 + export default { 15 + fetch: app.fetch, 16 + port: process.env.PORT || 4300, 17 + };
+51
src/lib/stores.ts
···
··· 1 + // ------------------------------------------------------------ 2 + // Snowflake 3 + // ------------------------------------------------------------ 4 + 5 + export interface ISnowflake< 6 + Type extends string, 7 + Template extends object = object, 8 + > { 9 + id: string; 10 + type: Type; 11 + data: Template; 12 + createdAt: number; 13 + } 14 + 15 + export class Snowflake< 16 + Type extends string, 17 + Template extends object = object, 18 + > implements ISnowflake<Type, Template> { 19 + constructor( 20 + public id: string, 21 + public type: Type, 22 + public data: Template, 23 + public createdAt: number = Date.now(), 24 + ) {} 25 + } 26 + 27 + // ------------------------------------------------------------ 28 + // Event 29 + // ------------------------------------------------------------ 30 + 31 + export interface IEvent<T extends object = object> { 32 + source: string; 33 + name: string; 34 + data: T; 35 + } 36 + 37 + export class Event<T extends object = object> implements IEvent<T> { 38 + constructor( 39 + public source: string, 40 + public name: string, 41 + public data: T = {} as T, 42 + ) {} 43 + } 44 + 45 + export const events: Snowflake<"event", IEvent>[] = []; 46 + 47 + // ------------------------------------------------------------ 48 + // State 49 + // ------------------------------------------------------------ 50 + 51 + export const states: Snowflake<"state", object>[] = [];
+47
src/routes/events/index.ts
···
··· 1 + // ------------------------------------------------------------ 2 + // Imports & Initialization 3 + // ------------------------------------------------------------ 4 + 5 + import { Hono } from "hono"; 6 + import { Snowflake, Event, events } from "../../lib/stores"; 7 + 8 + const app = new Hono(); 9 + 10 + // ------------------------------------------------------------ 11 + // Endpoints 12 + // ------------------------------------------------------------ 13 + 14 + app.get("/", async (c) => { 15 + events.push( 16 + new Snowflake( 17 + crypto.randomUUID(), 18 + "event", 19 + new Event("internal", "endpoint.events.get"), 20 + ), 21 + ); 22 + 23 + return c.json(events); 24 + }); 25 + 26 + app.post("/", async (c) => { 27 + const body = await c.req.json(); 28 + if (!body.source || !body.name || !body.data) { 29 + return c.json({ error: "Invalid event" }, 400); 30 + } 31 + 32 + // Build event and wrap it in a snowflake 33 + const event = new Snowflake( 34 + crypto.randomUUID(), 35 + "event", 36 + new Event(body.source, body.name, body.data), 37 + ); 38 + 39 + events.push(event); 40 + return c.json(body); 41 + }); 42 + 43 + // ------------------------------------------------------------ 44 + // Exports 45 + // ------------------------------------------------------------ 46 + 47 + export default app;
+74
src/routes/index.ts
···
··· 1 + // ------------------------------------------------------------ 2 + // Imports & Initialization 3 + // ------------------------------------------------------------ 4 + 5 + import { Hono } from "hono"; 6 + 7 + const app = new Hono(); 8 + 9 + // ------------------------------------------------------------ 10 + // Endpoints 11 + // ------------------------------------------------------------ 12 + 13 + // Homepage 14 + app.get("/", (c) => 15 + c.html(` 16 + <style> 17 + * { 18 + margin: 0; 19 + padding: 0; 20 + box-sizing: border-box; 21 + position: relative; 22 + } 23 + 24 + body { 25 + padding: 60px; 26 + 27 + background-color: #101014; 28 + color: #fff; 29 + font-family: Arial, sans-serif; 30 + 31 + width: 100vw; 32 + height: 100vh; 33 + } 34 + 35 + .d-flex { 36 + display: flex; 37 + flex-direction: row; 38 + } 39 + 40 + .flex-column { 41 + flex-direction: column; 42 + } 43 + 44 + .flex-center { 45 + justify-content: center; 46 + align-items: center; 47 + } 48 + 49 + .w-full { 50 + width: 100%; 51 + } 52 + 53 + .gap { 54 + gap: 8px; 55 + } 56 + </style> 57 + <div class="d-flex flex-column flex-center w-full gap"> 58 + <h1>This is a Snowflake instance!</h1> 59 + <div class="d-flex flex-column flex-center gap"> 60 + <p>version: ${process.env.npm_package_version}</p> 61 + <p>Runtime: Bun ${Bun.version}</p> 62 + <p>Powered by <a href="https://tangled.org/thevoid.cafe/snowflake">Snowflake</a></p> 63 + </div> 64 + </div> 65 + `), 66 + ); 67 + 68 + app.route("/events", (await import("./events")).default); 69 + 70 + // ------------------------------------------------------------ 71 + // Exports 72 + // ------------------------------------------------------------ 73 + 74 + export default app;