tracks lexicons and how many times they appeared on the jetstream

refactor(client): cleanup code

ptr.pet 73978e71 8a4a46f2

verified
+15
client/src/lib/api.ts
···
+
import { dev } from "$app/environment";
+
import type { EventRecord } from "./types";
+
import { PUBLIC_API_URL } from "$env/static/public";
+
+
export const fetchEvents = async (): Promise<EventRecord[]> => {
+
const response = await fetch(
+
`${dev ? "http" : "https"}://${PUBLIC_API_URL}/events`,
+
);
+
if (!response.ok) {
+
throw new Error(`(${response.status}): ${await response.json()}`);
+
}
+
+
const data = await response.json();
+
return data.events;
+
};
+3 -5
client/src/lib/components/EventCard.svelte
···
<script lang="ts">
+
import { formatNumber, formatTimestamp } from "$lib/format";
import type { EventRecord } from "$lib/types";
import { onMount, onDestroy } from "svelte";
interface Props {
-
nsid: string;
event: EventRecord;
index: number;
-
formatNumber: (num: number) => string;
-
formatTimestamp: (timestamp: number) => string;
}
-
let { nsid, event, index, formatNumber, formatTimestamp }: Props = $props();
+
let { event, index }: Props = $props();
// Border animation state
let borderThickness = $state(0);
···
</div>
</div>
<div class="font-mono text-sm text-gray-700 mb-2 break-all leading-relaxed">
-
{nsid}
+
{event.nsid}
</div>
<div class="text-lg font-bold text-green-600">
{formatNumber(event.count)} created
+1 -1
client/src/lib/components/FilterControls.svelte
···
class="wsbadge !mt-0 !font-normal bg-yellow-100 hover:bg-yellow-200 border-yellow-300"
>
<input checked={dontShowBsky} type="checkbox" />
-
<span class="ml-0.5"> don't show app.bsky.* </span>
+
<span class="ml-0.5"> hide app.bsky.* </span>
</button>
</div>
+17 -16
client/src/lib/components/StatsCard.svelte
···
<script lang="ts">
+
import { formatNumber } from "$lib/format";
+
interface Props {
title: string;
value: number;
-
colorScheme: 'green' | 'red' | 'orange';
-
formatNumber: (num: number) => string;
+
colorScheme: "green" | "red" | "orange";
}
-
let { title, value, colorScheme, formatNumber }: Props = $props();
+
let { title, value, colorScheme }: Props = $props();
const colorClasses = {
green: {
-
bg: 'from-green-50 to-green-100',
-
border: 'border-green-200',
-
titleText: 'text-green-700',
-
valueText: 'text-green-900'
+
bg: "from-green-50 to-green-100",
+
border: "border-green-200",
+
titleText: "text-green-700",
+
valueText: "text-green-900",
},
red: {
-
bg: 'from-red-50 to-red-100',
-
border: 'border-red-200',
-
titleText: 'text-red-700',
-
valueText: 'text-red-900'
+
bg: "from-red-50 to-red-100",
+
border: "border-red-200",
+
titleText: "text-red-700",
+
valueText: "text-red-900",
},
orange: {
-
bg: 'from-orange-50 to-orange-100',
-
border: 'border-orange-200',
-
titleText: 'text-orange-700',
-
valueText: 'text-orange-900'
-
}
+
bg: "from-orange-50 to-orange-100",
+
border: "border-orange-200",
+
titleText: "text-orange-700",
+
valueText: "text-orange-900",
+
},
};
const colors = $derived(colorClasses[colorScheme]);
+23
client/src/lib/filter.ts
···
+
export const createRegexFilter = (pattern: string): RegExp | null => {
+
if (!pattern) return null;
+
+
try {
+
// Check if pattern contains regex metacharacters
+
const hasRegexChars = /[.*+?^${}()|[\]\\]/.test(pattern);
+
+
if (hasRegexChars) {
+
// Use as regex with case-insensitive flag
+
return new RegExp(pattern, "i");
+
} else {
+
// Smart case: case-insensitive unless pattern has uppercase
+
const hasUppercase = /[A-Z]/.test(pattern);
+
const flags = hasUppercase ? "" : "i";
+
// Escape the pattern for literal matching
+
const escapedPattern = pattern.replace(/[.*+?^${}()|[\]\\]/g, "\\$&");
+
return new RegExp(escapedPattern, flags);
+
}
+
} catch (e) {
+
// Invalid regex, return null
+
return null;
+
}
+
};
+7
client/src/lib/format.ts
···
+
export const formatNumber = (num: number): string => {
+
return num.toLocaleString();
+
};
+
+
export const formatTimestamp = (timestamp: number): string => {
+
return new Date(timestamp / 1000).toLocaleString();
+
};
+7
client/src/lib/types.ts
···
export type EventRecord = {
+
nsid: string;
+
last_seen: number;
+
count: number;
+
deleted_count: number;
+
};
+
+
export type NsidCounts = {
last_seen: number;
count: number;
deleted_count: number;
+25 -75
client/src/routes/+page.svelte
···
<script lang="ts">
import { dev } from "$app/environment";
-
import type { EventRecord } from "$lib/types";
+
import type { EventRecord, NsidCounts } from "$lib/types";
import { onMount, onDestroy } from "svelte";
-
import { get, writable } from "svelte/store";
+
import { writable } from "svelte/store";
+
import { PUBLIC_API_URL } from "$env/static/public";
+
import { fetchEvents } from "$lib/api";
+
import { createRegexFilter } from "$lib/filter";
import StatsCard from "$lib/components/StatsCard.svelte";
import StatusBadge from "$lib/components/StatusBadge.svelte";
import EventCard from "$lib/components/EventCard.svelte";
import FilterControls from "$lib/components/FilterControls.svelte";
-
import { PUBLIC_API_URL } from "$env/static/public";
-
const events = writable(new Map<string, EventRecord>());
-
let eventsList: { nsid: string; event: EventRecord }[] = $state([]);
+
const events = writable(new Map<string, NsidCounts>());
+
let eventsList: EventRecord[] = $state([]);
events.subscribe((value) => {
eventsList = value
.entries()
.map(([nsid, event]) => ({
nsid,
-
event,
+
...event,
}))
.toArray();
-
eventsList.sort((a, b) => b.event.count - a.event.count);
+
eventsList.sort((a, b) => b.count - a.count);
});
// Backpressure system
-
let eventBuffer: { nsid: string; event: EventRecord }[] = [];
+
let eventBuffer: EventRecord[] = [];
let updateTimer: number | null = null;
-
let bufferedEventsCount = $state(0);
const BATCH_SIZE = 10;
const UPDATE_INTERVAL = 100; // ms
···
if (eventBuffer.length === 0) return;
events.update((map) => {
-
for (const { nsid, event } of eventBuffer) {
-
map.set(nsid, event);
+
for (const event of eventBuffer) {
+
map.set(event.nsid, event);
}
return map;
});
eventBuffer = [];
-
bufferedEventsCount = 0;
};
const scheduleUpdate = () => {
···
updateTimer = null;
}, UPDATE_INTERVAL);
};
-
let all: EventRecord = $derived(
+
let all: NsidCounts = $derived(
eventsList.reduce(
-
(acc, { nsid, event }) => {
+
(acc, event) => {
return {
last_seen:
acc.last_seen > event.last_seen
···
const jsonData = JSON.parse(jsonStr);
// Add to buffer instead of immediate update
-
eventBuffer.push({ nsid: jsonData.nsid, event: jsonData });
-
bufferedEventsCount = eventBuffer.length;
+
eventBuffer.push(jsonData);
// If buffer is full, flush immediately
if (eventBuffer.length >= BATCH_SIZE) {
···
const loadData = async () => {
try {
error = null;
-
-
const response = await fetch(
-
`${dev ? "http" : "https"}://${PUBLIC_API_URL}/events`,
-
);
-
if (!response.ok) {
-
throw new Error(`HTTP error! status: ${response.status}`);
-
}
-
-
const data = await response.json();
+
const data = await fetchEvents();
events.update((map) => {
-
for (const event of data.events) {
+
for (const event of data) {
map.set(event.nsid, event);
}
return map;
···
}
});
-
const formatNumber = (num: number): string => {
-
return num.toLocaleString();
-
};
-
-
const formatTimestamp = (timestamp: number): string => {
-
return new Date(timestamp / 1000).toLocaleString();
-
};
-
-
const createRegexFilter = (pattern: string): RegExp | null => {
-
if (!pattern) return null;
-
-
try {
-
// Check if pattern contains regex metacharacters
-
const hasRegexChars = /[.*+?^${}()|[\]\\]/.test(pattern);
-
-
if (hasRegexChars) {
-
// Use as regex with case-insensitive flag
-
return new RegExp(pattern, "i");
-
} else {
-
// Smart case: case-insensitive unless pattern has uppercase
-
const hasUppercase = /[A-Z]/.test(pattern);
-
const flags = hasUppercase ? "" : "i";
-
// Escape the pattern for literal matching
-
const escapedPattern = pattern.replace(
-
/[.*+?^${}()|[\]\\]/g,
-
"\\$&",
-
);
-
return new RegExp(escapedPattern, flags);
-
}
-
} catch (e) {
-
// Invalid regex, return null
-
return null;
-
}
-
};
-
-
const filterEvents = (events: { nsid: string; event: EventRecord }[]) => {
+
const filterEvents = (events: EventRecord[]) => {
let filtered = events;
// Apply regex filter
···
</script>
<svelte:head>
-
<title>bluesky event tracker</title>
-
<meta name="description" content="tracks bluesky events by collection" />
+
<title>bluesky jetstream tracker</title>
+
<meta
+
name="description"
+
content="tracks bluesky jetstream events by collection"
+
/>
</svelte:head>
<div class="md:max-w-[60vw] mx-auto p-2">
···
title="total creation"
value={all.count}
colorScheme="green"
-
{formatNumber}
/>
<StatsCard
title="total deletion"
value={all.deleted_count}
colorScheme="red"
-
{formatNumber}
/>
<StatsCard
title="unique collections"
value={eventsList.length}
colorScheme="orange"
-
{formatNumber}
/>
</div>
···
onBskyToggle={() => (dontShowBsky = !dontShowBsky)}
/>
<div class="grid grid-cols-2 lg:grid-cols-3 xl:grid-cols-4 gap-4">
-
{#each filterEvents(eventsList) as { nsid, event }, index (nsid)}
-
<EventCard
-
{nsid}
-
{event}
-
{index}
-
{formatNumber}
-
{formatTimestamp}
-
/>
+
{#each filterEvents(eventsList) as event, index (event.nsid)}
+
<EventCard {event} {index} />
{/each}
</div>
</div>