{"version":3,"sources":["../../../src/server/webhooks/index.ts","../../../src/server/webhooks/verify.ts","../../../src/http/errors.ts","../../../src/server/webhooks/handler.ts"],"sourcesContent":["export type {\n  WebhookHandlerConfig,\n  WebhookEvent,\n  BaseWebhookEvent,\n  EntryPublishEvent,\n  EntryUnpublishEvent,\n  EntryUpdateEvent,\n  EntryCreateEvent,\n  EntryDeleteEvent,\n  AssetPublishEvent,\n  AssetUnpublishEvent,\n  AssetDeleteEvent,\n  ContentTypeCreateEvent,\n  ContentTypeUpdateEvent,\n  ContentTypeDeleteEvent,\n} from \"./types.js\"\nexport { verifyWebhookSignature } from \"./verify.js\"\nexport { createWebhookHandler } from \"./handler.js\"\n","/**\n * Webhook signature verification using HMAC-SHA256.\n * Uses the Web Crypto API for cross-runtime compatibility (Node 18+, Deno, Bun, edge).\n */\n\nfunction hexEncode(buffer: ArrayBuffer): string {\n  const bytes = new Uint8Array(buffer)\n  let hex = \"\"\n  for (const byte of bytes) {\n    hex += byte.toString(16).padStart(2, \"0\")\n  }\n  return hex\n}\n\nfunction hexDecode(hex: string): Uint8Array {\n  const bytes = new Uint8Array(hex.length / 2)\n  for (let i = 0; i < hex.length; i += 2) {\n    bytes[i / 2] = Number.parseInt(hex.substring(i, i + 2), 16)\n  }\n  return bytes\n}\n\n/**\n * Constant-time comparison of two byte arrays.\n * Prevents timing attacks by always comparing all bytes regardless of mismatch.\n */\nfunction timingSafeEqual(a: Uint8Array, b: Uint8Array): boolean {\n  if (a.length !== b.length) return false\n  let result = 0\n  for (let i = 0; i < a.length; i++) {\n    result |= (a[i] ?? 0) ^ (b[i] ?? 0)\n  }\n  return result === 0\n}\n\n/**\n * Verify a Contentstack webhook signature using HMAC-SHA256.\n *\n * @param body - The raw request body string\n * @param signature - The hex-encoded signature from the `X-Contentstack-Request-Signature` header\n * @param secret - The webhook secret configured in Contentstack\n * @returns `true` if the signature is valid\n */\nexport async function verifyWebhookSignature(\n  body: string,\n  signature: string,\n  secret: string,\n): Promise<boolean> {\n  const encoder = new TextEncoder()\n  const key = await globalThis.crypto.subtle.importKey(\n    \"raw\",\n    encoder.encode(secret),\n    { name: \"HMAC\", hash: \"SHA-256\" },\n    false,\n    [\"sign\"],\n  )\n\n  const expectedBuffer = await globalThis.crypto.subtle.sign(\"HMAC\", key, encoder.encode(body))\n  const expectedBytes = new Uint8Array(expectedBuffer)\n  const signatureBytes = hexDecode(signature)\n\n  return timingSafeEqual(expectedBytes, signatureBytes)\n}\n","/**\n * Base error class for all Contentstack SDK errors.\n * Every error includes an actionable message and relevant context.\n */\nexport class ContentstackError extends Error {\n  override readonly name: string = \"ContentstackError\"\n  readonly status?: number\n  readonly errorCode?: number\n  readonly errors?: Record<string, string[]>\n  readonly requestPath?: string\n\n  constructor(\n    message: string,\n    options?: {\n      status?: number\n      errorCode?: number\n      errors?: Record<string, string[]>\n      requestPath?: string\n      cause?: Error\n    },\n  ) {\n    super(message, options?.cause ? { cause: options.cause } : undefined)\n    this.status = options?.status\n    this.errorCode = options?.errorCode\n    this.errors = options?.errors\n    this.requestPath = options?.requestPath\n  }\n}\n\nexport class ContentstackAuthError extends ContentstackError {\n  override readonly name = \"ContentstackAuthError\"\n  override readonly status = 401\n}\n\nexport class ContentstackForbiddenError extends ContentstackError {\n  override readonly name = \"ContentstackForbiddenError\"\n  override readonly status = 403\n}\n\nexport class ContentstackNotFoundError extends ContentstackError {\n  override readonly name = \"ContentstackNotFoundError\"\n  override readonly status = 404\n}\n\nexport class ContentstackValidationError extends ContentstackError {\n  override readonly name = \"ContentstackValidationError\"\n  override readonly status: number\n\n  constructor(\n    message: string,\n    options?: {\n      status?: number\n      errorCode?: number\n      errors?: Record<string, string[]>\n      requestPath?: string\n      cause?: Error\n    },\n  ) {\n    super(message, options)\n    this.status = options?.status ?? 400\n  }\n}\n\nexport class ContentstackInvalidApiKeyError extends ContentstackError {\n  override readonly name = \"ContentstackInvalidApiKeyError\"\n  override readonly status = 412\n}\n\nexport class ContentstackRateLimitError extends ContentstackError {\n  override readonly name = \"ContentstackRateLimitError\"\n  override readonly status = 429\n  readonly retryAfter?: number\n\n  constructor(\n    message: string,\n    options?: {\n      errorCode?: number\n      errors?: Record<string, string[]>\n      requestPath?: string\n      cause?: Error\n      retryAfter?: number\n    },\n  ) {\n    super(message, { ...options, status: 429 })\n    this.retryAfter = options?.retryAfter\n  }\n}\n\nexport class ContentstackServerError extends ContentstackError {\n  override readonly name = \"ContentstackServerError\"\n}\n\nexport class ContentstackConfigError extends ContentstackError {\n  override readonly name = \"ContentstackConfigError\"\n}\n","import { ContentstackAuthError } from \"../../index.js\"\nimport type { WebhookEvent, WebhookHandlerConfig } from \"./types.js\"\nimport { verifyWebhookSignature } from \"./verify.js\"\n\nconst SIGNATURE_HEADER = \"x-contentstack-request-signature\"\n\nfunction parseWebhookBody(body: string): WebhookEvent {\n  const raw = JSON.parse(body) as Record<string, unknown>\n  const module = raw.module as string\n  const event = raw.event as string\n  const type = `${module}.${event}`\n\n  return {\n    ...raw,\n    type,\n    module,\n    event,\n  } as WebhookEvent\n}\n\n/**\n * Create a webhook handler with signature verification and typed event parsing.\n *\n * @example\n * ```ts\n * const handler = createWebhookHandler({ secret: process.env.WEBHOOK_SECRET })\n *\n * // In a route handler:\n * const event = await handler.verify(request)\n * if (event.type === \"entry.publish\") {\n *   console.log(event.data.entry.uid)\n * }\n * ```\n */\nexport function createWebhookHandler(config: WebhookHandlerConfig) {\n  return {\n    /**\n     * Read the request body, verify the signature, and return a typed event.\n     * Throws `ContentstackAuthError` if the signature is missing or invalid.\n     */\n    async verify(request: Request): Promise<WebhookEvent> {\n      const body = await request.text()\n      const signature = request.headers.get(SIGNATURE_HEADER)\n\n      if (!signature) {\n        throw new ContentstackAuthError(\"Missing webhook signature header\", {\n          status: 401,\n          requestPath: new URL(request.url).pathname,\n        })\n      }\n\n      const valid = await verifyWebhookSignature(body, signature, config.secret)\n      if (!valid) {\n        throw new ContentstackAuthError(\"Invalid webhook signature\", {\n          status: 401,\n          requestPath: new URL(request.url).pathname,\n        })\n      }\n\n      return parseWebhookBody(body)\n    },\n\n    /**\n     * Read the request body and return a typed event without verifying the signature.\n     */\n    async parse(request: Request): Promise<WebhookEvent> {\n      const body = await request.text()\n      return parseWebhookBody(body)\n    },\n  }\n}\n"],"mappings":"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","names":["module"]}