import { appTool, defineApp, deliverWebhook, eventEnvelope, eventsError, EventsErrorCode } from "@allternit/apps-server";
const subscriptions = new Map(); // use real storage in production
export default defineApp({
name: "Price alerts",
tools: [/* ... */],
events: {
definitions: [
{
name: "price.changed",
description: "A watched symbol moved by more than the threshold.",
delivery: ["webhook"],
inputSchema: { type: "object", properties: { symbol: { type: "string" } }, required: ["symbol"] },
payloadSchema: { type: "object", properties: { symbol: { type: "string" }, price: { type: "number" } } },
},
],
async subscribe(sub) {
if (!isAllowed(sub.principal, sub.arguments)) throw eventsError(EventsErrorCode.Forbidden, "Not your symbol.");
subscriptions.set(sub.id, sub); // sub.url, sub.key, sub.arguments
return { refreshBefore: new Date(Date.now() + 24 * 3600_000).toISOString(), cursor: null };
},
async unsubscribe(sub) {
subscriptions.delete(sub.id);
},
},
});
// Later, when the event happens:
async function emit(symbol: string, price: number) {
for (const sub of subscriptions.values()) {
if (sub.arguments.symbol !== symbol) continue;
const id = crypto.randomUUID();
await deliverWebhook({
url: sub.url,
key: sub.key,
subscriptionId: sub.id,
msgId: id,
body: eventEnvelope(id, "price.changed", new Date(), { symbol, price }),
});
}
}