@riverbankcms/sdk 0.7.0 → 0.7.3
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +229 -0
- package/dist/cli/index.js +42 -95
- package/dist/cli/index.js.map +1 -1
- package/dist/cli/init-docs/content/agents-section.md +50 -0
- package/dist/cli/init-docs/content/cli-reference.md +574 -0
- package/dist/cli/init-docs/content/content-management.md +384 -0
- package/dist/cli/init-docs/content/context-brand.md +125 -0
- package/dist/cli/init-docs/content/context-brief.md +77 -0
- package/dist/cli/init-docs/content/context-knowledge.md +111 -0
- package/dist/cli/init-docs/content/getting-started.md +130 -0
- package/dist/cli/init-docs/content/site-workflows-readme.md +96 -0
- package/dist/cli/init-docs/content/workflow-add-block.md +228 -0
- package/dist/cli/init-docs/content/workflow-create-page.md +193 -0
- package/dist/cli/init-docs/content/workflow-publish.md +280 -0
- package/dist/client/bookings.d.mts +2 -0
- package/dist/client/bookings.d.ts +2 -0
- package/dist/client/bookings.js +2956 -104
- package/dist/client/bookings.js.map +1 -1
- package/dist/client/bookings.mjs +2929 -70
- package/dist/client/bookings.mjs.map +1 -1
- package/dist/client/client.d.mts +2 -2
- package/dist/client/client.d.ts +2 -2
- package/dist/client/client.js +602 -68
- package/dist/client/client.js.map +1 -1
- package/dist/client/client.mjs +602 -68
- package/dist/client/client.mjs.map +1 -1
- package/dist/client/hooks.d.mts +2 -2
- package/dist/client/hooks.d.ts +2 -2
- package/dist/client/rendering/client.js +3070 -259
- package/dist/client/rendering/client.js.map +1 -1
- package/dist/client/rendering/client.mjs +3212 -395
- package/dist/client/rendering/client.mjs.map +1 -1
- package/dist/client/spam-protection.d.mts +55 -0
- package/dist/client/spam-protection.d.ts +55 -0
- package/dist/client/spam-protection.js +2915 -0
- package/dist/client/spam-protection.js.map +1 -0
- package/dist/client/spam-protection.mjs +2893 -0
- package/dist/client/spam-protection.mjs.map +1 -0
- package/dist/client/{usePage-BiOReg0_.d.ts → usePage-BYmJCCm1.d.ts} +132 -11
- package/dist/client/{usePage-BXjk8BhD.d.mts → usePage-DZtrWajy.d.mts} +132 -11
- package/dist/server/{Layout-wBtJLTVX.d.ts → Layout-Yluyb6sK.d.ts} +1 -1
- package/dist/server/{Layout-B7cvis7r.d.mts → Layout-qWLdVm5-.d.mts} +1 -1
- package/dist/server/chunk-2IZ6S225.js +122 -0
- package/dist/server/chunk-2IZ6S225.js.map +1 -0
- package/dist/server/chunk-4CV4JOE5.js +27 -0
- package/dist/server/chunk-4CV4JOE5.js.map +1 -0
- package/dist/server/chunk-5LRR64Y6.mjs +72 -0
- package/dist/server/chunk-5LRR64Y6.mjs.map +1 -0
- package/dist/server/chunk-NBTRDLCM.js +72 -0
- package/dist/server/chunk-NBTRDLCM.js.map +1 -0
- package/dist/server/chunk-NFEGQTCC.mjs +27 -0
- package/dist/server/{chunk-7FIJSGHU.mjs → chunk-NFQLH5IA.mjs} +856 -74
- package/dist/server/chunk-NFQLH5IA.mjs.map +1 -0
- package/dist/server/chunk-PPHZV6YD.mjs +122 -0
- package/dist/server/chunk-PPHZV6YD.mjs.map +1 -0
- package/dist/server/{chunk-P7UVAMK6.js → chunk-VLXTNB2C.js} +866 -84
- package/dist/server/chunk-VLXTNB2C.js.map +1 -0
- package/dist/server/{components-CMMwDXTW.d.mts → components-DNHfSCML.d.mts} +3 -3
- package/dist/server/{components-CICSJyp_.d.ts → components-Di5ME6He.d.ts} +3 -3
- package/dist/server/components.d.mts +5 -5
- package/dist/server/components.d.ts +5 -5
- package/dist/server/components.js +1 -1
- package/dist/server/components.mjs +1 -1
- package/dist/server/config-validation.js +1 -1
- package/dist/server/config-validation.mjs +1 -1
- package/dist/server/config.js +1 -1
- package/dist/server/config.mjs +1 -1
- package/dist/server/data.d.mts +2 -2
- package/dist/server/data.d.ts +2 -2
- package/dist/server/data.js +1 -1
- package/dist/server/data.mjs +1 -1
- package/dist/server/env.d.mts +109 -0
- package/dist/server/env.d.ts +109 -0
- package/dist/server/env.js +14 -0
- package/dist/server/env.js.map +1 -0
- package/dist/server/env.mjs +14 -0
- package/dist/server/{index-DI_qlYx3.d.mts → index--Oyunk_B.d.mts} +2 -2
- package/dist/server/{index-BTwWvSBu.d.ts → index-C9Ra8dza.d.ts} +2 -2
- package/dist/server/{index-Bucs6UqG.d.mts → index-Clm3skz_.d.mts} +1 -1
- package/dist/server/{index-Cp7tJuRt.d.ts → index-DLvNddi-.d.ts} +1 -1
- package/dist/server/index.d.mts +216 -5
- package/dist/server/index.d.ts +216 -5
- package/dist/server/index.js +301 -4
- package/dist/server/index.js.map +1 -1
- package/dist/server/index.mjs +301 -4
- package/dist/server/index.mjs.map +1 -1
- package/dist/server/{loadContent-DmgpFcFC.d.ts → loadContent-D7LQwI0o.d.ts} +3 -3
- package/dist/server/{loadContent-C-YYUKQa.d.mts → loadContent-DVfuBLiZ.d.mts} +3 -3
- package/dist/server/{loadPage-IDGVDFBB.js → loadPage-AXNAERDS.js} +2 -2
- package/dist/server/{loadPage-IDGVDFBB.js.map → loadPage-AXNAERDS.js.map} +1 -1
- package/dist/server/{loadPage-DP3nrHBi.d.ts → loadPage-BmYJCe_V.d.ts} +2 -2
- package/dist/server/{loadPage-B8mQUUSo.d.mts → loadPage-BucnLHmE.d.mts} +2 -2
- package/dist/server/{loadPage-DNQTTRHL.mjs → loadPage-XR7ORQ2E.mjs} +2 -2
- package/dist/server/loadPage-XR7ORQ2E.mjs.map +1 -0
- package/dist/server/metadata.d.mts +4 -4
- package/dist/server/metadata.d.ts +4 -4
- package/dist/server/metadata.js +1 -1
- package/dist/server/metadata.mjs +1 -1
- package/dist/server/navigation.d.mts +2 -2
- package/dist/server/navigation.d.ts +2 -2
- package/dist/server/navigation.js +1 -1
- package/dist/server/navigation.mjs +1 -1
- package/dist/server/next/revalidate.d.mts +66 -0
- package/dist/server/next/revalidate.d.ts +66 -0
- package/dist/server/next/revalidate.js +60 -0
- package/dist/server/next/revalidate.js.map +1 -0
- package/dist/server/next/revalidate.mjs +60 -0
- package/dist/server/next/revalidate.mjs.map +1 -0
- package/dist/server/next/tags.d.mts +81 -0
- package/dist/server/next/tags.d.ts +81 -0
- package/dist/server/next/tags.js +36 -0
- package/dist/server/next/tags.js.map +1 -0
- package/dist/server/next/tags.mjs +36 -0
- package/dist/server/next/tags.mjs.map +1 -0
- package/dist/server/next.d.mts +164 -6
- package/dist/server/next.d.ts +164 -6
- package/dist/server/next.js +79 -11
- package/dist/server/next.js.map +1 -1
- package/dist/server/next.mjs +76 -8
- package/dist/server/next.mjs.map +1 -1
- package/dist/server/rendering/server.d.mts +4 -4
- package/dist/server/rendering/server.d.ts +4 -4
- package/dist/server/rendering/server.js +1 -1
- package/dist/server/rendering/server.mjs +1 -1
- package/dist/server/rendering.d.mts +7 -7
- package/dist/server/rendering.d.ts +7 -7
- package/dist/server/rendering.js +3 -3
- package/dist/server/rendering.js.map +1 -1
- package/dist/server/rendering.mjs +4 -4
- package/dist/server/routing.d.mts +3 -3
- package/dist/server/routing.d.ts +3 -3
- package/dist/server/routing.js +2 -2
- package/dist/server/routing.mjs +2 -2
- package/dist/server/server.d.mts +5 -5
- package/dist/server/server.d.ts +5 -5
- package/dist/server/server.js +5 -5
- package/dist/server/server.js.map +1 -1
- package/dist/server/server.mjs +5 -5
- package/dist/server/theme-bridge.js +1 -1
- package/dist/server/theme-bridge.mjs +1 -1
- package/dist/server/theme.js +1 -1
- package/dist/server/theme.mjs +1 -1
- package/dist/server/{types-BvcJU7zk.d.ts → types-BRQyLrQU.d.ts} +132 -11
- package/dist/server/{types-Dsu9wsUh.d.mts → types-BSV6Vc-P.d.mts} +2 -2
- package/dist/server/{types-1cLz0vnq.d.mts → types-C-LShyIg.d.mts} +132 -11
- package/dist/server/{types-CVykEqXN.d.ts → types-Dt98DeYa.d.ts} +2 -2
- package/dist/server/webhooks.d.mts +81 -0
- package/dist/server/webhooks.d.ts +81 -0
- package/dist/server/webhooks.js +12 -0
- package/dist/server/webhooks.js.map +1 -0
- package/dist/server/webhooks.mjs +12 -0
- package/dist/server/webhooks.mjs.map +1 -0
- package/package.json +29 -3
- package/dist/client/resolver-BhueZVxZ.d.mts +0 -61
- package/dist/client/resolver-BhueZVxZ.d.ts +0 -61
- package/dist/client/usePage--fGlyrgj.d.mts +0 -6439
- package/dist/client/usePage-BBcFCxOU.d.ts +0 -6297
- package/dist/client/usePage-BC8Q2E3t.d.mts +0 -6431
- package/dist/client/usePage-BTPnCuWC.d.mts +0 -6511
- package/dist/client/usePage-BafOS9UT.d.mts +0 -6512
- package/dist/client/usePage-BcjWPXvh.d.mts +0 -6388
- package/dist/client/usePage-Bnx-kA6x.d.mts +0 -6670
- package/dist/client/usePage-BvKAa3Zw.d.mts +0 -366
- package/dist/client/usePage-BvKAa3Zw.d.ts +0 -366
- package/dist/client/usePage-BydHcMYB.d.mts +0 -6297
- package/dist/client/usePage-C3ZKNwY7.d.mts +0 -6393
- package/dist/client/usePage-CE7X5NcN.d.ts +0 -6439
- package/dist/client/usePage-CHEybPMD.d.ts +0 -6429
- package/dist/client/usePage-CrKw1H6Y.d.ts +0 -6338
- package/dist/client/usePage-CyYpOJud.d.ts +0 -6388
- package/dist/client/usePage-D4fxZbRR.d.mts +0 -6429
- package/dist/client/usePage-DMI8ImsU.d.mts +0 -6338
- package/dist/client/usePage-DoPI6b8V.d.ts +0 -6511
- package/dist/client/usePage-DpRNZUtP.d.ts +0 -6431
- package/dist/client/usePage-QNWArrVO.d.ts +0 -6670
- package/dist/client/usePage-fBgPB6Oq.d.ts +0 -6512
- package/dist/client/usePage-gpVaeWDy.d.ts +0 -6393
- package/dist/server/chunk-7FIJSGHU.mjs.map +0 -1
- package/dist/server/chunk-BJTO5JO5.mjs +0 -11
- package/dist/server/chunk-DGUM43GV.js +0 -11
- package/dist/server/chunk-DGUM43GV.js.map +0 -1
- package/dist/server/chunk-P7UVAMK6.js.map +0 -1
- /package/dist/server/{chunk-BJTO5JO5.mjs.map → chunk-NFEGQTCC.mjs.map} +0 -0
- /package/dist/server/{loadPage-DNQTTRHL.mjs.map → env.mjs.map} +0 -0
|
@@ -0,0 +1,72 @@
|
|
|
1
|
+
// src/webhooks/verify.ts
|
|
2
|
+
import crypto from "crypto";
|
|
3
|
+
import { z } from "zod";
|
|
4
|
+
var WebhookEventSchema = z.enum([
|
|
5
|
+
"page.published",
|
|
6
|
+
"entry.published",
|
|
7
|
+
"navigation.updated",
|
|
8
|
+
"theme.updated",
|
|
9
|
+
"site.settings_updated"
|
|
10
|
+
]);
|
|
11
|
+
var PagePublishedDataSchema = z.object({
|
|
12
|
+
pageId: z.string(),
|
|
13
|
+
path: z.string(),
|
|
14
|
+
slug: z.string().optional()
|
|
15
|
+
});
|
|
16
|
+
var EntryPublishedDataSchema = z.object({
|
|
17
|
+
entryId: z.string(),
|
|
18
|
+
path: z.string(),
|
|
19
|
+
contentType: z.string()
|
|
20
|
+
});
|
|
21
|
+
var NavigationUpdatedDataSchema = z.object({
|
|
22
|
+
menuId: z.string().optional()
|
|
23
|
+
});
|
|
24
|
+
var EmptyDataSchema = z.object({});
|
|
25
|
+
var WebhookDataSchema = z.union([
|
|
26
|
+
PagePublishedDataSchema,
|
|
27
|
+
EntryPublishedDataSchema,
|
|
28
|
+
NavigationUpdatedDataSchema,
|
|
29
|
+
EmptyDataSchema
|
|
30
|
+
]);
|
|
31
|
+
var WebhookPayloadSchema = z.object({
|
|
32
|
+
/** Event type */
|
|
33
|
+
event: WebhookEventSchema,
|
|
34
|
+
/** ISO timestamp of when the event occurred */
|
|
35
|
+
timestamp: z.string(),
|
|
36
|
+
/** Site ID that triggered the event */
|
|
37
|
+
siteId: z.string(),
|
|
38
|
+
/** Event-specific data (shape depends on event type) */
|
|
39
|
+
data: z.record(z.string(), z.unknown()),
|
|
40
|
+
/** Cache invalidation tags (for tag-based revalidation) */
|
|
41
|
+
tags: z.array(z.string()).optional()
|
|
42
|
+
});
|
|
43
|
+
function verifyWebhookSignature(payload, signature, secret) {
|
|
44
|
+
const expectedSignature = crypto.createHmac("sha256", secret).update(payload).digest("hex");
|
|
45
|
+
if (signature.length !== expectedSignature.length) {
|
|
46
|
+
return false;
|
|
47
|
+
}
|
|
48
|
+
return crypto.timingSafeEqual(
|
|
49
|
+
Buffer.from(signature),
|
|
50
|
+
Buffer.from(expectedSignature)
|
|
51
|
+
);
|
|
52
|
+
}
|
|
53
|
+
function parseWebhookPayload(body) {
|
|
54
|
+
let parsed;
|
|
55
|
+
try {
|
|
56
|
+
parsed = JSON.parse(body);
|
|
57
|
+
} catch {
|
|
58
|
+
return { success: false, error: "Invalid JSON" };
|
|
59
|
+
}
|
|
60
|
+
const result = WebhookPayloadSchema.safeParse(parsed);
|
|
61
|
+
if (!result.success) {
|
|
62
|
+
return { success: false, error: `Invalid payload: ${result.error.message}` };
|
|
63
|
+
}
|
|
64
|
+
return { success: true, payload: result.data };
|
|
65
|
+
}
|
|
66
|
+
|
|
67
|
+
export {
|
|
68
|
+
WebhookPayloadSchema,
|
|
69
|
+
verifyWebhookSignature,
|
|
70
|
+
parseWebhookPayload
|
|
71
|
+
};
|
|
72
|
+
//# sourceMappingURL=chunk-5LRR64Y6.mjs.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"sources":["../../src/webhooks/verify.ts"],"sourcesContent":["/**\n * Webhook verification utilities for Riverbank SDK\n *\n * Provides framework-agnostic webhook signature verification and payload parsing.\n */\n\nimport crypto from 'crypto';\nimport { z } from 'zod';\n\n// ============================================================================\n// Webhook Event Types\n// ============================================================================\n\n/**\n * Supported webhook event types.\n * Keep in sync with apps/dashboard/src/lib/db/helpers/webhooks.ts\n */\nexport const WebhookEventSchema = z.enum([\n 'page.published',\n 'entry.published',\n 'navigation.updated',\n 'theme.updated',\n 'site.settings_updated',\n]);\n\nexport type WebhookEvent = z.infer<typeof WebhookEventSchema>;\n\n// ============================================================================\n// Event-Specific Data Schemas\n// ============================================================================\n\n/** Data for page.published events */\nexport const PagePublishedDataSchema = z.object({\n pageId: z.string(),\n path: z.string(),\n slug: z.string().optional(),\n});\n\n/** Data for entry.published events */\nexport const EntryPublishedDataSchema = z.object({\n entryId: z.string(),\n path: z.string(),\n contentType: z.string(),\n});\n\n/** Data for navigation.updated events */\nexport const NavigationUpdatedDataSchema = z.object({\n menuId: z.string().optional(),\n});\n\n/** Data for theme.updated and site.settings_updated events (empty) */\nexport const EmptyDataSchema = z.object({});\n\n/**\n * Combined data schema that accepts any valid event data.\n * The actual shape depends on the event type.\n */\nexport const WebhookDataSchema = z.union([\n PagePublishedDataSchema,\n EntryPublishedDataSchema,\n NavigationUpdatedDataSchema,\n EmptyDataSchema,\n]);\n\nexport type PagePublishedData = z.infer<typeof PagePublishedDataSchema>;\nexport type EntryPublishedData = z.infer<typeof EntryPublishedDataSchema>;\nexport type NavigationUpdatedData = z.infer<typeof NavigationUpdatedDataSchema>;\n\n// ============================================================================\n// Webhook Payload Schema\n// ============================================================================\n\n/**\n * Zod schema for webhook payload validation.\n */\nexport const WebhookPayloadSchema = z.object({\n /** Event type */\n event: WebhookEventSchema,\n /** ISO timestamp of when the event occurred */\n timestamp: z.string(),\n /** Site ID that triggered the event */\n siteId: z.string(),\n /** Event-specific data (shape depends on event type) */\n data: z.record(z.string(), z.unknown()),\n /** Cache invalidation tags (for tag-based revalidation) */\n tags: z.array(z.string()).optional(),\n});\n\n/**\n * Webhook payload structure sent by the CMS.\n */\nexport type WebhookPayload = z.infer<typeof WebhookPayloadSchema>;\n\n/**\n * Type-safe webhook payload with discriminated data based on event type.\n */\nexport type TypedWebhookPayload =\n | { event: 'page.published'; data: PagePublishedData; siteId: string; timestamp: string; tags?: string[] }\n | { event: 'entry.published'; data: EntryPublishedData; siteId: string; timestamp: string; tags?: string[] }\n | { event: 'navigation.updated'; data: NavigationUpdatedData; siteId: string; timestamp: string; tags?: string[] }\n | { event: 'theme.updated'; data: Record<string, never>; siteId: string; timestamp: string; tags?: string[] }\n | { event: 'site.settings_updated'; data: Record<string, never>; siteId: string; timestamp: string; tags?: string[] };\n\n/**\n * Verify a webhook signature using HMAC-SHA256.\n *\n * Uses timing-safe comparison to prevent timing attacks.\n *\n * @param payload - The raw request body as a string\n * @param signature - The X-Riverbank-Signature header value\n * @param secret - The webhook signing secret (RIVERBANK_WEBHOOK_SECRET)\n * @returns true if signature is valid, false otherwise\n *\n * @example\n * ```ts\n * const body = await request.text();\n * const signature = request.headers.get('x-riverbank-signature');\n * const secret = process.env.RIVERBANK_WEBHOOK_SECRET;\n *\n * if (!verifyWebhookSignature(body, signature, secret)) {\n * return new Response('Invalid signature', { status: 401 });\n * }\n * ```\n */\nexport function verifyWebhookSignature(\n payload: string,\n signature: string,\n secret: string\n): boolean {\n const expectedSignature = crypto\n .createHmac('sha256', secret)\n .update(payload)\n .digest('hex');\n\n // Length check before timing-safe comparison\n // timingSafeEqual throws if lengths differ\n if (signature.length !== expectedSignature.length) {\n return false;\n }\n\n // Timing-safe comparison to prevent timing attacks\n return crypto.timingSafeEqual(\n Buffer.from(signature),\n Buffer.from(expectedSignature)\n );\n}\n\n/**\n * Result type for webhook payload parsing.\n */\nexport type ParseWebhookResult =\n | { success: true; payload: WebhookPayload }\n | { success: false; error: string };\n\n/**\n * Parse and validate a webhook payload from the request body.\n *\n * Uses Zod schema validation to ensure the payload has the expected structure.\n *\n * @param body - The raw request body as a string\n * @returns A result object with either the validated payload or an error message\n *\n * @example\n * ```ts\n * const body = await request.text();\n * const result = parseWebhookPayload(body);\n * if (!result.success) {\n * return new Response(result.error, { status: 400 });\n * }\n * console.log(`Received ${result.payload.event} for site ${result.payload.siteId}`);\n * ```\n */\nexport function parseWebhookPayload(body: string): ParseWebhookResult {\n let parsed: unknown;\n try {\n parsed = JSON.parse(body);\n } catch {\n return { success: false, error: 'Invalid JSON' };\n }\n\n const result = WebhookPayloadSchema.safeParse(parsed);\n if (!result.success) {\n return { success: false, error: `Invalid payload: ${result.error.message}` };\n }\n\n return { success: true, payload: result.data };\n}\n"],"mappings":";AAMA,OAAO,YAAY;AACnB,SAAS,SAAS;AAUX,IAAM,qBAAqB,EAAE,KAAK;AAAA,EACvC;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AACF,CAAC;AASM,IAAM,0BAA0B,EAAE,OAAO;AAAA,EAC9C,QAAQ,EAAE,OAAO;AAAA,EACjB,MAAM,EAAE,OAAO;AAAA,EACf,MAAM,EAAE,OAAO,EAAE,SAAS;AAC5B,CAAC;AAGM,IAAM,2BAA2B,EAAE,OAAO;AAAA,EAC/C,SAAS,EAAE,OAAO;AAAA,EAClB,MAAM,EAAE,OAAO;AAAA,EACf,aAAa,EAAE,OAAO;AACxB,CAAC;AAGM,IAAM,8BAA8B,EAAE,OAAO;AAAA,EAClD,QAAQ,EAAE,OAAO,EAAE,SAAS;AAC9B,CAAC;AAGM,IAAM,kBAAkB,EAAE,OAAO,CAAC,CAAC;AAMnC,IAAM,oBAAoB,EAAE,MAAM;AAAA,EACvC;AAAA,EACA;AAAA,EACA;AAAA,EACA;AACF,CAAC;AAaM,IAAM,uBAAuB,EAAE,OAAO;AAAA;AAAA,EAE3C,OAAO;AAAA;AAAA,EAEP,WAAW,EAAE,OAAO;AAAA;AAAA,EAEpB,QAAQ,EAAE,OAAO;AAAA;AAAA,EAEjB,MAAM,EAAE,OAAO,EAAE,OAAO,GAAG,EAAE,QAAQ,CAAC;AAAA;AAAA,EAEtC,MAAM,EAAE,MAAM,EAAE,OAAO,CAAC,EAAE,SAAS;AACrC,CAAC;AAsCM,SAAS,uBACd,SACA,WACA,QACS;AACT,QAAM,oBAAoB,OACvB,WAAW,UAAU,MAAM,EAC3B,OAAO,OAAO,EACd,OAAO,KAAK;AAIf,MAAI,UAAU,WAAW,kBAAkB,QAAQ;AACjD,WAAO;AAAA,EACT;AAGA,SAAO,OAAO;AAAA,IACZ,OAAO,KAAK,SAAS;AAAA,IACrB,OAAO,KAAK,iBAAiB;AAAA,EAC/B;AACF;AA2BO,SAAS,oBAAoB,MAAkC;AACpE,MAAI;AACJ,MAAI;AACF,aAAS,KAAK,MAAM,IAAI;AAAA,EAC1B,QAAQ;AACN,WAAO,EAAE,SAAS,OAAO,OAAO,eAAe;AAAA,EACjD;AAEA,QAAM,SAAS,qBAAqB,UAAU,MAAM;AACpD,MAAI,CAAC,OAAO,SAAS;AACnB,WAAO,EAAE,SAAS,OAAO,OAAO,oBAAoB,OAAO,MAAM,OAAO,GAAG;AAAA,EAC7E;AAEA,SAAO,EAAE,SAAS,MAAM,SAAS,OAAO,KAAK;AAC/C;","names":[]}
|
|
@@ -0,0 +1,72 @@
|
|
|
1
|
+
"use strict";Object.defineProperty(exports, "__esModule", {value: true}); function _interopRequireDefault(obj) { return obj && obj.__esModule ? obj : { default: obj }; }// src/webhooks/verify.ts
|
|
2
|
+
var _crypto = require('crypto'); var _crypto2 = _interopRequireDefault(_crypto);
|
|
3
|
+
var _zod = require('zod');
|
|
4
|
+
var WebhookEventSchema = _zod.z.enum([
|
|
5
|
+
"page.published",
|
|
6
|
+
"entry.published",
|
|
7
|
+
"navigation.updated",
|
|
8
|
+
"theme.updated",
|
|
9
|
+
"site.settings_updated"
|
|
10
|
+
]);
|
|
11
|
+
var PagePublishedDataSchema = _zod.z.object({
|
|
12
|
+
pageId: _zod.z.string(),
|
|
13
|
+
path: _zod.z.string(),
|
|
14
|
+
slug: _zod.z.string().optional()
|
|
15
|
+
});
|
|
16
|
+
var EntryPublishedDataSchema = _zod.z.object({
|
|
17
|
+
entryId: _zod.z.string(),
|
|
18
|
+
path: _zod.z.string(),
|
|
19
|
+
contentType: _zod.z.string()
|
|
20
|
+
});
|
|
21
|
+
var NavigationUpdatedDataSchema = _zod.z.object({
|
|
22
|
+
menuId: _zod.z.string().optional()
|
|
23
|
+
});
|
|
24
|
+
var EmptyDataSchema = _zod.z.object({});
|
|
25
|
+
var WebhookDataSchema = _zod.z.union([
|
|
26
|
+
PagePublishedDataSchema,
|
|
27
|
+
EntryPublishedDataSchema,
|
|
28
|
+
NavigationUpdatedDataSchema,
|
|
29
|
+
EmptyDataSchema
|
|
30
|
+
]);
|
|
31
|
+
var WebhookPayloadSchema = _zod.z.object({
|
|
32
|
+
/** Event type */
|
|
33
|
+
event: WebhookEventSchema,
|
|
34
|
+
/** ISO timestamp of when the event occurred */
|
|
35
|
+
timestamp: _zod.z.string(),
|
|
36
|
+
/** Site ID that triggered the event */
|
|
37
|
+
siteId: _zod.z.string(),
|
|
38
|
+
/** Event-specific data (shape depends on event type) */
|
|
39
|
+
data: _zod.z.record(_zod.z.string(), _zod.z.unknown()),
|
|
40
|
+
/** Cache invalidation tags (for tag-based revalidation) */
|
|
41
|
+
tags: _zod.z.array(_zod.z.string()).optional()
|
|
42
|
+
});
|
|
43
|
+
function verifyWebhookSignature(payload, signature, secret) {
|
|
44
|
+
const expectedSignature = _crypto2.default.createHmac("sha256", secret).update(payload).digest("hex");
|
|
45
|
+
if (signature.length !== expectedSignature.length) {
|
|
46
|
+
return false;
|
|
47
|
+
}
|
|
48
|
+
return _crypto2.default.timingSafeEqual(
|
|
49
|
+
Buffer.from(signature),
|
|
50
|
+
Buffer.from(expectedSignature)
|
|
51
|
+
);
|
|
52
|
+
}
|
|
53
|
+
function parseWebhookPayload(body) {
|
|
54
|
+
let parsed;
|
|
55
|
+
try {
|
|
56
|
+
parsed = JSON.parse(body);
|
|
57
|
+
} catch (e) {
|
|
58
|
+
return { success: false, error: "Invalid JSON" };
|
|
59
|
+
}
|
|
60
|
+
const result = WebhookPayloadSchema.safeParse(parsed);
|
|
61
|
+
if (!result.success) {
|
|
62
|
+
return { success: false, error: `Invalid payload: ${result.error.message}` };
|
|
63
|
+
}
|
|
64
|
+
return { success: true, payload: result.data };
|
|
65
|
+
}
|
|
66
|
+
|
|
67
|
+
|
|
68
|
+
|
|
69
|
+
|
|
70
|
+
|
|
71
|
+
exports.WebhookPayloadSchema = WebhookPayloadSchema; exports.verifyWebhookSignature = verifyWebhookSignature; exports.parseWebhookPayload = parseWebhookPayload;
|
|
72
|
+
//# sourceMappingURL=chunk-NBTRDLCM.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"sources":["/Users/will/Projects/Business/cms/builder/packages/sdk/dist/server/chunk-NBTRDLCM.js","../../src/webhooks/verify.ts"],"names":[],"mappings":"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","file":"/Users/will/Projects/Business/cms/builder/packages/sdk/dist/server/chunk-NBTRDLCM.js","sourcesContent":[null,"/**\n * Webhook verification utilities for Riverbank SDK\n *\n * Provides framework-agnostic webhook signature verification and payload parsing.\n */\n\nimport crypto from 'crypto';\nimport { z } from 'zod';\n\n// ============================================================================\n// Webhook Event Types\n// ============================================================================\n\n/**\n * Supported webhook event types.\n * Keep in sync with apps/dashboard/src/lib/db/helpers/webhooks.ts\n */\nexport const WebhookEventSchema = z.enum([\n 'page.published',\n 'entry.published',\n 'navigation.updated',\n 'theme.updated',\n 'site.settings_updated',\n]);\n\nexport type WebhookEvent = z.infer<typeof WebhookEventSchema>;\n\n// ============================================================================\n// Event-Specific Data Schemas\n// ============================================================================\n\n/** Data for page.published events */\nexport const PagePublishedDataSchema = z.object({\n pageId: z.string(),\n path: z.string(),\n slug: z.string().optional(),\n});\n\n/** Data for entry.published events */\nexport const EntryPublishedDataSchema = z.object({\n entryId: z.string(),\n path: z.string(),\n contentType: z.string(),\n});\n\n/** Data for navigation.updated events */\nexport const NavigationUpdatedDataSchema = z.object({\n menuId: z.string().optional(),\n});\n\n/** Data for theme.updated and site.settings_updated events (empty) */\nexport const EmptyDataSchema = z.object({});\n\n/**\n * Combined data schema that accepts any valid event data.\n * The actual shape depends on the event type.\n */\nexport const WebhookDataSchema = z.union([\n PagePublishedDataSchema,\n EntryPublishedDataSchema,\n NavigationUpdatedDataSchema,\n EmptyDataSchema,\n]);\n\nexport type PagePublishedData = z.infer<typeof PagePublishedDataSchema>;\nexport type EntryPublishedData = z.infer<typeof EntryPublishedDataSchema>;\nexport type NavigationUpdatedData = z.infer<typeof NavigationUpdatedDataSchema>;\n\n// ============================================================================\n// Webhook Payload Schema\n// ============================================================================\n\n/**\n * Zod schema for webhook payload validation.\n */\nexport const WebhookPayloadSchema = z.object({\n /** Event type */\n event: WebhookEventSchema,\n /** ISO timestamp of when the event occurred */\n timestamp: z.string(),\n /** Site ID that triggered the event */\n siteId: z.string(),\n /** Event-specific data (shape depends on event type) */\n data: z.record(z.string(), z.unknown()),\n /** Cache invalidation tags (for tag-based revalidation) */\n tags: z.array(z.string()).optional(),\n});\n\n/**\n * Webhook payload structure sent by the CMS.\n */\nexport type WebhookPayload = z.infer<typeof WebhookPayloadSchema>;\n\n/**\n * Type-safe webhook payload with discriminated data based on event type.\n */\nexport type TypedWebhookPayload =\n | { event: 'page.published'; data: PagePublishedData; siteId: string; timestamp: string; tags?: string[] }\n | { event: 'entry.published'; data: EntryPublishedData; siteId: string; timestamp: string; tags?: string[] }\n | { event: 'navigation.updated'; data: NavigationUpdatedData; siteId: string; timestamp: string; tags?: string[] }\n | { event: 'theme.updated'; data: Record<string, never>; siteId: string; timestamp: string; tags?: string[] }\n | { event: 'site.settings_updated'; data: Record<string, never>; siteId: string; timestamp: string; tags?: string[] };\n\n/**\n * Verify a webhook signature using HMAC-SHA256.\n *\n * Uses timing-safe comparison to prevent timing attacks.\n *\n * @param payload - The raw request body as a string\n * @param signature - The X-Riverbank-Signature header value\n * @param secret - The webhook signing secret (RIVERBANK_WEBHOOK_SECRET)\n * @returns true if signature is valid, false otherwise\n *\n * @example\n * ```ts\n * const body = await request.text();\n * const signature = request.headers.get('x-riverbank-signature');\n * const secret = process.env.RIVERBANK_WEBHOOK_SECRET;\n *\n * if (!verifyWebhookSignature(body, signature, secret)) {\n * return new Response('Invalid signature', { status: 401 });\n * }\n * ```\n */\nexport function verifyWebhookSignature(\n payload: string,\n signature: string,\n secret: string\n): boolean {\n const expectedSignature = crypto\n .createHmac('sha256', secret)\n .update(payload)\n .digest('hex');\n\n // Length check before timing-safe comparison\n // timingSafeEqual throws if lengths differ\n if (signature.length !== expectedSignature.length) {\n return false;\n }\n\n // Timing-safe comparison to prevent timing attacks\n return crypto.timingSafeEqual(\n Buffer.from(signature),\n Buffer.from(expectedSignature)\n );\n}\n\n/**\n * Result type for webhook payload parsing.\n */\nexport type ParseWebhookResult =\n | { success: true; payload: WebhookPayload }\n | { success: false; error: string };\n\n/**\n * Parse and validate a webhook payload from the request body.\n *\n * Uses Zod schema validation to ensure the payload has the expected structure.\n *\n * @param body - The raw request body as a string\n * @returns A result object with either the validated payload or an error message\n *\n * @example\n * ```ts\n * const body = await request.text();\n * const result = parseWebhookPayload(body);\n * if (!result.success) {\n * return new Response(result.error, { status: 400 });\n * }\n * console.log(`Received ${result.payload.event} for site ${result.payload.siteId}`);\n * ```\n */\nexport function parseWebhookPayload(body: string): ParseWebhookResult {\n let parsed: unknown;\n try {\n parsed = JSON.parse(body);\n } catch {\n return { success: false, error: 'Invalid JSON' };\n }\n\n const result = WebhookPayloadSchema.safeParse(parsed);\n if (!result.success) {\n return { success: false, error: `Invalid payload: ${result.error.message}` };\n }\n\n return { success: true, payload: result.data };\n}\n"]}
|
|
@@ -0,0 +1,27 @@
|
|
|
1
|
+
var __defProp = Object.defineProperty;
|
|
2
|
+
var __getOwnPropDesc = Object.getOwnPropertyDescriptor;
|
|
3
|
+
var __getOwnPropNames = Object.getOwnPropertyNames;
|
|
4
|
+
var __hasOwnProp = Object.prototype.hasOwnProperty;
|
|
5
|
+
var __esm = (fn, res) => function __init() {
|
|
6
|
+
return fn && (res = (0, fn[__getOwnPropNames(fn)[0]])(fn = 0)), res;
|
|
7
|
+
};
|
|
8
|
+
var __export = (target, all) => {
|
|
9
|
+
for (var name in all)
|
|
10
|
+
__defProp(target, name, { get: all[name], enumerable: true });
|
|
11
|
+
};
|
|
12
|
+
var __copyProps = (to, from, except, desc) => {
|
|
13
|
+
if (from && typeof from === "object" || typeof from === "function") {
|
|
14
|
+
for (let key of __getOwnPropNames(from))
|
|
15
|
+
if (!__hasOwnProp.call(to, key) && key !== except)
|
|
16
|
+
__defProp(to, key, { get: () => from[key], enumerable: !(desc = __getOwnPropDesc(from, key)) || desc.enumerable });
|
|
17
|
+
}
|
|
18
|
+
return to;
|
|
19
|
+
};
|
|
20
|
+
var __toCommonJS = (mod) => __copyProps(__defProp({}, "__esModule", { value: true }), mod);
|
|
21
|
+
|
|
22
|
+
export {
|
|
23
|
+
__esm,
|
|
24
|
+
__export,
|
|
25
|
+
__toCommonJS
|
|
26
|
+
};
|
|
27
|
+
//# sourceMappingURL=chunk-NFEGQTCC.mjs.map
|