tangled
alpha
login
or
join now
thevoid.cafe
/
snowflake
0
fork
atom
Simple, single-user event aggregation platform, for use in personal websites and other related places.
event-streaming
single-user
events
event-aggregation
0
fork
atom
overview
issues
pulls
pipelines
✨ Add GET and POST endpoints for events
Jo
1 month ago
fa0ad7f3
5930a49f
+222
-1
7 changed files
expand all
collapse all
unified
split
README.md
bun.lock
package.json
src
index.ts
lib
stores.ts
routes
events
index.ts
index.ts
+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
···
0
0
0
0
0
0
0
0
0
0
0
0
0
0
0
0
0
0
0
0
0
0
0
0
0
0
···
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 ."
0
0
0
0
0
0
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
···
0
0
0
0
0
0
0
0
0
0
0
0
0
0
0
0
0
···
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
···
0
0
0
0
0
0
0
0
0
0
0
0
0
0
0
0
0
0
0
0
0
0
0
0
0
0
0
0
0
0
0
0
0
0
0
0
0
0
0
0
0
0
0
0
0
0
0
0
0
0
0
···
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
···
0
0
0
0
0
0
0
0
0
0
0
0
0
0
0
0
0
0
0
0
0
0
0
0
0
0
0
0
0
0
0
0
0
0
0
0
0
0
0
0
0
0
0
0
0
0
0
···
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
···
0
0
0
0
0
0
0
0
0
0
0
0
0
0
0
0
0
0
0
0
0
0
0
0
0
0
0
0
0
0
0
0
0
0
0
0
0
0
0
0
0
0
0
0
0
0
0
0
0
0
0
0
0
0
0
0
0
0
0
0
0
0
0
0
0
0
0
0
0
0
0
0
0
0
···
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;