diff --git a/README.md b/README.md index 52e1c6fb..476f1955 100644 --- a/README.md +++ b/README.md @@ -570,6 +570,78 @@ Used for internal logging. Defaults to [`console`](https://developer.mozilla.org +### validateEventNameOrNames() + +```js +validateEventNameOrNames(event); +``` + + + + + + + + +
+ event + + string | string[] + + + Required. + The event name or an array of event names to validate. +
+ +Returns `true` if the provided event name(s) match a known event name; otherwise, returns `false`. + +Example: + +```js +const event = "user.created"; +if (validateEventNameOrNames(event)) { + console.log("Event is valid!"); +} +``` + +--- + +### validatePayload() + +```js +validatePayload(input); +``` + + + + + + + + +
+ input + + Object + + + Required. + The JSON input to validate and type-cast to the event type. Should contain properties `name`, `id`, and `payload`. +
+ +Returns a boolean value. If the input matches the expected structure and contains a valid event name, an ID, and a payload, it returns `true`. Otherwise, it returns `false`. + +Example: + +```js +const webhookData = { name: 'user.created', id: '12345', payload: { ... } }; + +if (validatePayload(webhookData)) { + // You can safely access webhookData as an EmitterWebhookEvent + console.log('Webhook payload is valid!'); +} +``` + ### Webhook events See the full list of [event types with example payloads](https://docs.github.com/developers/webhooks-and-events/webhook-events-and-payloads/). diff --git a/src/index.ts b/src/index.ts index 7d8b6e02..94edd1ce 100644 --- a/src/index.ts +++ b/src/index.ts @@ -20,6 +20,7 @@ import type { export { createNodeMiddleware } from "./middleware/node/index.js"; export { createWebMiddleware } from "./middleware/web/index.js"; export { emitterEventNames } from "./generated/webhook-names.js"; +export { validateEventNameOrNames, validatePayload } from "./validate-event.js"; // U holds the return value of `transform` function in Options class Webhooks { diff --git a/src/validate-event.ts b/src/validate-event.ts new file mode 100644 index 00000000..46c46495 --- /dev/null +++ b/src/validate-event.ts @@ -0,0 +1,63 @@ +import { emitterEventNames } from "./generated/webhook-names.js"; +import type { EmitterWebhookEventName, EmitterWebhookEvent } from "./types.js"; + +/** + * Validates if the provided event name or names are a know event name. + * If validation is successful, the types get narrowed to {@link EmitterWebhookEventName}. + * + * @param {string | string[]} event The event name or an array of event names to validate. + * @returns {event is EmitterWebhookEventName | EmitterWebhookEventName[]} True if the event name(s) are valid, otherwise false. + */ +export function validateEventNameOrNames( + event: string | string[], +): event is EmitterWebhookEventName | EmitterWebhookEventName[] { + if (Array.isArray(event)) { + return event.every((e) => + emitterEventNames.includes(e as EmitterWebhookEventName), + ); + } + return emitterEventNames.includes(event as EmitterWebhookEventName); +} + +/** + * Validates the structure and type of a webhook payload and narrows the type to the corresponding event type. + * + * This function ensures that the provided input matches the expected structure of an event, + * including a valid event name, an ID, and a payload. If the validation passes, the input is + * narrowed to the specific {@link EmitterWebhookEvent} type. + * + * @template {EmitterWebhookEventName} TName The specific event name to validate against. + * @param {unknown} input The JSON input to validate and type-cast to the event type. + * @returns {input is EmitterWebhookEvent} True if the input is a valid event payload, otherwise false. + */ +export function validatePayload( + input: unknown, +): input is EmitterWebhookEvent { + // Ensure the input is an object and has the necessary properties + if ( + typeof input !== "object" || + input === null || + !("name" in input) || + !("id" in input) || + !("payload" in input) + ) { + return false; + } + + const typedInput = input as { + name: Object | undefined; + id: Object | undefined; + payload: Object | undefined; + }; + + if ( + typeof typedInput.name !== "string" || + typeof typedInput.id !== "string" || + typeof typedInput.payload !== "object" || + typedInput.payload === null + ) { + return false; + } + + return validateEventNameOrNames(typedInput.name); +}