diff --git a/package-lock.json b/package-lock.json index 1c2e975..f92b576 100644 --- a/package-lock.json +++ b/package-lock.json @@ -7,6 +7,7 @@ "name": "devict-events", "dependencies": { "@fastify/swagger": "^9.7.0", + "@fastify/type-provider-json-schema-to-ts": "^5.0.0", "fastify": "^5.8.5" }, "devDependencies": { @@ -20,6 +21,15 @@ "npm": ">=11.0.0" } }, + "node_modules/@babel/runtime": { + "version": "7.29.7", + "resolved": "https://registry.npmjs.org/@babel/runtime/-/runtime-7.29.7.tgz", + "integrity": "sha512-Nq8OhGWiZIZGV6hLHoyAKLLcJihP/xFeBMGJoUrxTX2psI8dCifzLhZISFb+VWS3wFMRDmCGw5R+dOySCqPLhw==", + "license": "MIT", + "engines": { + "node": ">=6.9.0" + } + }, "node_modules/@fastify/ajv-compiler": { "version": "4.0.5", "resolved": "https://registry.npmjs.org/@fastify/ajv-compiler/-/ajv-compiler-4.0.5.tgz", @@ -154,6 +164,15 @@ "yaml": "^2.4.2" } }, + "node_modules/@fastify/type-provider-json-schema-to-ts": { + "version": "5.0.0", + "resolved": "https://registry.npmjs.org/@fastify/type-provider-json-schema-to-ts/-/type-provider-json-schema-to-ts-5.0.0.tgz", + "integrity": "sha512-erEJvxucxc09XLdHygdQua4QFyxBp+QParhXl3hrB6UvE2R6ZB9KG6TznUTz6I5HK7t5xm/vn4DG7EIOAPOO4w==", + "license": "MIT", + "dependencies": { + "json-schema-to-ts": "^3.1.0" + } + }, "node_modules/@oxfmt/binding-android-arm-eabi": { "version": "0.54.0", "resolved": "https://registry.npmjs.org/@oxfmt/binding-android-arm-eabi/-/binding-android-arm-eabi-0.54.0.tgz", @@ -1149,6 +1168,19 @@ "url": "https://github.com/Eomm/json-schema-resolver?sponsor=1" } }, + "node_modules/json-schema-to-ts": { + "version": "3.1.1", + "resolved": "https://registry.npmjs.org/json-schema-to-ts/-/json-schema-to-ts-3.1.1.tgz", + "integrity": "sha512-+DWg8jCJG2TEnpy7kOm/7/AxaYoaRbjVB4LFZLySZlWn8exGs3A4OLJR966cVvU26N7X9TWxl+Jsw7dzAqKT6g==", + "license": "MIT", + "dependencies": { + "@babel/runtime": "^7.18.3", + "ts-algebra": "^2.0.0" + }, + "engines": { + "node": ">=16" + } + }, "node_modules/json-schema-traverse": { "version": "1.0.0", "resolved": "https://registry.npmjs.org/json-schema-traverse/-/json-schema-traverse-1.0.0.tgz", @@ -1536,6 +1568,12 @@ "node": ">=20" } }, + "node_modules/ts-algebra": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/ts-algebra/-/ts-algebra-2.0.0.tgz", + "integrity": "sha512-FPAhNPFMrkwz76P7cdjdmiShwMynZYN6SgOujD1urY4oNm80Ou9oMdmbR45LotcKOXoy7wSmHkRFE6Mxbrhefw==", + "license": "MIT" + }, "node_modules/typescript": { "version": "6.0.3", "resolved": "https://registry.npmjs.org/typescript/-/typescript-6.0.3.tgz", diff --git a/package.json b/package.json index b901793..6a93088 100644 --- a/package.json +++ b/package.json @@ -16,6 +16,7 @@ }, "dependencies": { "@fastify/swagger": "^9.7.0", + "@fastify/type-provider-json-schema-to-ts": "^5.0.0", "fastify": "^5.8.5" }, "devDependencies": { diff --git a/src/api/api.ts b/src/api/api.ts index 2a8e3be..faffafe 100644 --- a/src/api/api.ts +++ b/src/api/api.ts @@ -3,6 +3,8 @@ import Fastify from "fastify"; import swagger from "@fastify/swagger"; +import type { JsonSchemaToTsProvider } from "@fastify/type-provider-json-schema-to-ts"; +import type { JSONSchema } from "json-schema-to-ts"; import type { Config } from "../config.ts"; import { getOpenMeetClient, type OpenMeetClient } from "../openmeet/openmeet.ts"; @@ -17,9 +19,9 @@ const upcomingEventsResponse = { properties: { name: { type: "string" }, slug: { type: "string" }, - location: { tpe: "string" }, - lat: { tpe: "string" }, - lon: { tpe: "string" }, + location: { type: "string" }, + lat: { type: "string" }, + lon: { type: "string" }, startDate: { type: "string", format: "date-time" }, endDate: { type: "string", format: "date-time" }, series: { @@ -38,14 +40,14 @@ const upcomingEventsResponse = { }, required: ["name", "slug", "location", "startDate", "endDate"], }, -} as const; +} as const satisfies JSONSchema; export async function startApi( cfg: Config, opts: { aborter?: AbortController; omClient?: OpenMeetClient } = {}, ): Promise { const { aborter, omClient } = opts; - const app = Fastify({ logger: true }); + const app = Fastify({ logger: true }).withTypeProvider(); await app.register(swagger, { openapi: { diff --git a/src/notifier/channels/slack.test.ts b/src/notifier/channels/slack.test.ts index 647b0ec..3de6969 100644 --- a/src/notifier/channels/slack.test.ts +++ b/src/notifier/channels/slack.test.ts @@ -8,6 +8,11 @@ const event = (overrides: Partial<{ name: string; slug: string; startDate: strin slug: "hack-night", startDate: "2026-06-15T18:00:00Z", endDate: "2026-06-15T20:00:00Z", + location: "Boise", + lat: "43.615", + lon: "-116.202", + series: { id: 1, name: "Hack Night" }, + image: { path: "/img/hack-night.png" }, ...overrides, }); diff --git a/src/notifier/notifier.test.ts b/src/notifier/notifier.test.ts index 71e7bf1..ce224d9 100644 --- a/src/notifier/notifier.test.ts +++ b/src/notifier/notifier.test.ts @@ -17,7 +17,17 @@ const ev = ( startDate = "2026-06-10T18:00:00Z", name = slug, endDate = "2026-06-15T20:00:00Z", -): OpenMeetEvent => ({ name, slug, startDate, endDate }); +): OpenMeetEvent => ({ + name, + slug, + startDate, + endDate, + location: "Boise", + lat: "43.615", + lon: "-116.202", + series: { id: 1, name }, + image: { path: `/img/${slug}.png` }, +}); // A Config with only the fields the notifier reads. The injected client // ignores cfg, so the atproto/openmeet fields don't matter here. diff --git a/src/openmeet/openmeet.ts b/src/openmeet/openmeet.ts index ecddddf..baf87b5 100644 --- a/src/openmeet/openmeet.ts +++ b/src/openmeet/openmeet.ts @@ -16,6 +16,16 @@ export type OpenMeetEvent = Readonly<{ slug: string; startDate: string; endDate: string; + location: string; + lat: string; + lon: string; + series: { + id: number; + name: string; + }; + image: { + path: string; + }; }>; export type Tokens = Readonly<{