@10x-media/webhooks 0.1.0-beta.0
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/CHANGELOG.md +16 -0
- package/LICENSE +21 -0
- package/README.md +186 -0
- package/dist/exports/client.d.ts +14 -0
- package/dist/exports/client.js +83 -0
- package/dist/exports/client.js.map +1 -0
- package/dist/exports/i18n.d.ts +37 -0
- package/dist/exports/i18n.js +3 -0
- package/dist/exports/types.d.ts +2 -0
- package/dist/exports/types.js +1 -0
- package/dist/index-CpsTtHO1.d.ts +62 -0
- package/dist/index.d.ts +2 -0
- package/dist/index.js +849 -0
- package/dist/index.js.map +1 -0
- package/dist/keys-BdNiD3rC.js +31 -0
- package/dist/keys-BdNiD3rC.js.map +1 -0
- package/dist/translations-CnkwrCJ3.js +51 -0
- package/dist/translations-CnkwrCJ3.js.map +1 -0
- package/package.json +105 -0
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"index.js","names":["loggedIn"],"sources":["../src/plugin/registerTranslations.ts","../src/constants.ts","../src/translations/server.ts","../src/collections/deliveries.ts","../src/collections/subscriptions.ts","../src/plugin/resolveSubscriptions.ts","../src/delivery/deriveDeliveryStatus.ts","../src/delivery/deliver.ts","../src/delivery/sign.ts","../src/delivery/sendDelivery.ts","../src/delivery/deliverTask.ts","../src/delivery/redeliver.ts","../src/events/eventTypes.ts","../src/delivery/buildPayload.ts","../src/events/hooks.ts","../src/options.ts","../src/plugin/resolveMode.ts","../src/plugin/registerWebhooks.ts","../src/index.ts"],"sourcesContent":["import type { Config } from 'payload'\nimport { deepMergeSimple } from 'payload/shared'\n\nimport { translations } from '../translations'\n\ntype Translations = NonNullable<NonNullable<Config['i18n']>['translations']>\n\n/**\n * Merge this plugin's translations into the host config. A host value wins on\n * conflict (`deepMergeSimple` lets the second argument override), so projects can\n * override any string.\n */\nexport const registerTranslations = (config: Config): void => {\n\tconfig.i18n ??= {}\n\tconfig.i18n.translations = deepMergeSimple<Translations>(\n\t\ttranslations,\n\t\tconfig.i18n.translations ?? {}\n\t)\n}\n","export const DEFAULT_SUBSCRIPTIONS_SLUG = 'webhook-subscriptions'\nexport const DEFAULT_DELIVERIES_SLUG = 'webhook-deliveries'\nexport const ADMIN_GROUP = 'Webhooks'\nexport const WEBHOOK_DELIVER_TASK = 'webhooksDeliver'\nexport const DEFAULT_DELIVERY_QUEUE = 'default'\nexport const DEFAULT_TIMEOUT_MS = 10_000\nexport const DEFAULT_RETRIES = 4\n\n/** Placeholder returned for the signing secret on every read after its single create reveal. */\nexport const SECRET_MASK = '__redacted__'\n\n/**\n * `req.context` flags that opt a subscription read into seeing the raw signing secret.\n * The field `afterRead` mask runs even under `overrideAccess`, so internal signing reads\n * must set `revealSecretForSigning` to recover the raw value; `revealSecretOnce` is set by\n * the create `beforeChange` so the create response shows the secret exactly once.\n */\nexport const SECRET_REVEAL_CONTEXT = {\n\tforSigning: 'webhooksRevealSecretForSigning',\n\tonce: 'webhooksRevealSecretOnce',\n} as const\nexport const RESERVED_SLUGS = [\n\t'payload-jobs',\n\t'payload-locks',\n\t'payload-preferences',\n\t'payload-migrations',\n] as const\n","import type { LabelFunction } from 'payload'\n\nimport type { TranslationKey } from './keys'\n\n/** A `t`-like function narrowed to this plugin's typed keys. */\ntype Translate = (key: TranslationKey) => string\n\n/**\n * Adapt a Payload request `t` (typed to core keys only) so it also accepts this\n * plugin's keys. They are registered at config time and resolve at runtime; the\n * cast only widens the compile-time key domain.\n */\nexport const asTranslate = (t: unknown): Translate => t as Translate\n\n/** A field `label`/`description` backed by a typed key, resolved per request. */\nexport const labelForKey =\n\t(key: TranslationKey): LabelFunction =>\n\t({ t }) =>\n\t\tasTranslate(t)(key)\n","import type { CollectionConfig } from 'payload'\n\nimport { ADMIN_GROUP } from '../constants'\nimport { keys } from '../translations/keys'\nimport { labelForKey } from '../translations/server'\n\nconst STATUS_CELL = '@10x-media/webhooks/client#DeliveryStatusCell'\n\nconst loggedIn = ({ req }: { req: { user?: unknown } }) => Boolean(req.user)\n\n/** Runner-written delivery audit log; read-only in admin, server writes use overrideAccess. */\nexport const buildDeliveriesCollection = (args: {\n\tslug: string\n\thidden: boolean\n}): CollectionConfig => ({\n\tslug: args.slug,\n\tlabels: {\n\t\tsingular: labelForKey(keys.deliverySingular),\n\t\tplural: labelForKey(keys.deliveryPlural),\n\t},\n\tadmin: {\n\t\tgroup: ADMIN_GROUP,\n\t\tuseAsTitle: 'event',\n\t\tdefaultColumns: ['event', 'endpoint', 'status', 'responseStatus', 'attempt', 'createdAt'],\n\t\thidden: args.hidden,\n\t},\n\taccess: { read: loggedIn, create: () => false, update: () => false, delete: loggedIn },\n\tfields: [\n\t\t{ name: 'subscriptionId', type: 'text', index: true },\n\t\t{ name: 'endpoint', type: 'text' },\n\t\t{ name: 'event', type: 'text', index: true },\n\t\t{\n\t\t\tname: 'status',\n\t\t\ttype: 'select',\n\t\t\tdefaultValue: 'pending',\n\t\t\toptions: ['pending', 'success', 'failed', 'dead'],\n\t\t\tadmin: { components: { Cell: STATUS_CELL } },\n\t\t},\n\t\t{ name: 'attempt', type: 'number', defaultValue: 0 },\n\t\t{ name: 'responseStatus', type: 'number' },\n\t\t{ name: 'responseBody', type: 'textarea' },\n\t\t{ name: 'error', type: 'textarea' },\n\t\t{ name: 'durationMs', type: 'number' },\n\t\t{ name: 'jobId', type: 'text' },\n\t\t{ name: 'payload', type: 'json' },\n\t\t{\n\t\t\tname: 'redeliver',\n\t\t\ttype: 'ui',\n\t\t\tadmin: { components: { Field: '@10x-media/webhooks/client#RedeliverButton' } },\n\t\t},\n\t],\n})\n","import { randomBytes } from 'node:crypto'\nimport type {\n\tCollectionAfterChangeHook,\n\tCollectionBeforeChangeHook,\n\tCollectionConfig,\n\tFieldHook,\n} from 'payload'\n\nimport { ADMIN_GROUP, SECRET_MASK, SECRET_REVEAL_CONTEXT } from '../constants'\nimport { keys } from '../translations/keys'\nimport { labelForKey } from '../translations/server'\n\nconst generateSecret: CollectionBeforeChangeHook = ({ data, operation, req }) => {\n\tif (operation === 'create' && !data.secret) {\n\t\treq.context[SECRET_REVEAL_CONTEXT.once] = true\n\t\treturn { ...data, secret: randomBytes(24).toString('hex') }\n\t}\n\treturn data\n}\n\n/**\n * The DB always stores the raw secret; this mask only shapes read output. It runs even under\n * `overrideAccess` (field hooks are not gated by access), so the raw value reaches a reader only\n * when a reveal flag is set: `once` on the create response, `forSigning` on internal delivery reads.\n */\nconst maskSecret: FieldHook = ({ value, req }) => {\n\tif (req.context[SECRET_REVEAL_CONTEXT.forSigning] || req.context[SECRET_REVEAL_CONTEXT.once]) {\n\t\treturn value\n\t}\n\treturn value == null ? value : SECRET_MASK\n}\n\nconst clearRevealOnce: CollectionAfterChangeHook = ({ doc, req }) => {\n\treq.context[SECRET_REVEAL_CONTEXT.once] = false\n\treturn doc\n}\n\nconst loggedIn = ({ req }: { req: { user?: unknown } }) => Boolean(req.user)\n\n/** Admin-managed subscriptions collection; `events` options come from the catalog. */\nexport const buildSubscriptionsCollection = (args: {\n\tslug: string\n\tevents: string[]\n\thidden: boolean\n}): CollectionConfig => ({\n\tslug: args.slug,\n\tlabels: {\n\t\tsingular: labelForKey(keys.subscriptionSingular),\n\t\tplural: labelForKey(keys.subscriptionPlural),\n\t},\n\tadmin: {\n\t\tgroup: ADMIN_GROUP,\n\t\tuseAsTitle: 'name',\n\t\tdefaultColumns: ['name', 'url', 'enabled'],\n\t\thidden: args.hidden,\n\t},\n\taccess: { read: loggedIn, create: loggedIn, update: loggedIn, delete: loggedIn },\n\thooks: { beforeChange: [generateSecret], afterChange: [clearRevealOnce] },\n\tfields: [\n\t\t{ name: 'name', type: 'text', required: true, label: labelForKey(keys.fieldName) },\n\t\t{ name: 'url', type: 'text', required: true, label: labelForKey(keys.fieldUrl) },\n\t\t{\n\t\t\tname: 'enabled',\n\t\t\ttype: 'checkbox',\n\t\t\tdefaultValue: true,\n\t\t\tlabel: labelForKey(keys.fieldEnabled),\n\t\t},\n\t\t{\n\t\t\tname: 'events',\n\t\t\ttype: 'select',\n\t\t\thasMany: true,\n\t\t\tlabel: labelForKey(keys.fieldEvents),\n\t\t\toptions: args.events.length\n\t\t\t\t? args.events.map((e) => ({ label: e, value: e }))\n\t\t\t\t: [{ label: '(none)', value: '__none__' }],\n\t\t},\n\t\t{\n\t\t\tname: 'secret',\n\t\t\ttype: 'text',\n\t\t\tlabel: labelForKey(keys.fieldSecret),\n\t\t\tadmin: { readOnly: true, description: labelForKey(keys.fieldSecretHelp) },\n\t\t\taccess: { update: () => false },\n\t\t\thooks: { afterRead: [maskSecret] },\n\t\t},\n\t\t{\n\t\t\tname: 'headers',\n\t\t\ttype: 'array',\n\t\t\tlabel: labelForKey(keys.fieldHeaders),\n\t\t\tfields: [\n\t\t\t\t{ name: 'key', type: 'text', required: true },\n\t\t\t\t{ name: 'value', type: 'text' },\n\t\t\t],\n\t\t},\n\t\t{ name: 'description', type: 'textarea', label: labelForKey(keys.fieldDescription) },\n\t],\n})\n","import type { Payload, PayloadRequest } from 'payload'\n\nimport { SECRET_REVEAL_CONTEXT } from '../constants'\nimport type { CodeSubscription } from '../options'\n\n/** A subscription resolved from either source, ready to deliver to. */\nexport type ResolvedSubscription = {\n\tid: string\n\tsource: 'collection' | 'code'\n\turl: string\n\tevents: string[]\n\tsecret?: string\n\theaders?: Record<string, string>\n\tenabled: boolean\n}\n\nconst rowHeaders = (\n\theaders?: { key?: string | null; value?: string | null }[] | null\n): Record<string, string> | undefined => {\n\tif (!headers?.length) {\n\t\treturn undefined\n\t}\n\tconst out: Record<string, string> = {}\n\tfor (const h of headers) {\n\t\tif (h.key) {\n\t\t\tout[h.key] = h.value ?? ''\n\t\t}\n\t}\n\treturn Object.keys(out).length ? out : undefined\n}\n\n/** Normalize a subscriptions-collection document. */\nexport const fromCollectionRow = (row: {\n\tid: string | number\n\turl: string\n\tevents?: string[] | null\n\tsecret?: string | null\n\theaders?: { key?: string | null; value?: string | null }[] | null\n\tenabled?: boolean | null\n}): ResolvedSubscription => ({\n\tid: String(row.id),\n\tsource: 'collection',\n\turl: row.url,\n\tevents: row.events ?? [],\n\tsecret: row.secret ?? undefined,\n\theaders: rowHeaders(row.headers),\n\tenabled: row.enabled !== false,\n})\n\n/** Normalize a code-defined subscription. */\nexport const fromCodeSubscription = (sub: CodeSubscription): ResolvedSubscription => ({\n\tid: sub.id,\n\tsource: 'code',\n\turl: sub.url,\n\tevents: sub.events,\n\tsecret: sub.secret,\n\theaders: sub.headers,\n\tenabled: sub.enabled !== false,\n})\n\n/** Enabled subscriptions listening for `event`. */\nexport const matchSubscriptions = (\n\tsubs: ResolvedSubscription[],\n\tevent: string\n): ResolvedSubscription[] => subs.filter((s) => s.enabled && s.events.includes(event))\n\n/** Look up one subscription by id (code first, then the collection). */\nexport const resolveSubscriptionById = async (args: {\n\tid: string\n\tcodeSubscriptions: CodeSubscription[]\n\tsubscriptionsSlug: string\n\tpayload: Payload\n\treq: PayloadRequest\n}): Promise<ResolvedSubscription | null> => {\n\tconst code = args.codeSubscriptions.find((s) => s.id === args.id)\n\tif (code) {\n\t\treturn fromCodeSubscription(code)\n\t}\n\targs.req.context[SECRET_REVEAL_CONTEXT.forSigning] = true\n\ttry {\n\t\tconst res = await args.payload.find({\n\t\t\tcollection: args.subscriptionsSlug,\n\t\t\twhere: { id: { equals: args.id } },\n\t\t\tlimit: 1,\n\t\t\tdepth: 0,\n\t\t\toverrideAccess: true,\n\t\t\treq: args.req,\n\t\t})\n\t\tconst row = res.docs[0] as Parameters<typeof fromCollectionRow>[0] | undefined\n\t\treturn row ? fromCollectionRow(row) : null\n\t} finally {\n\t\targs.req.context[SECRET_REVEAL_CONTEXT.forSigning] = false\n\t}\n}\n","/** Delivery status from the attempt outcome. `attempt` is 1-based; total tries = maxRetries + 1. */\nexport const deriveDeliveryStatus = (args: {\n\tok: boolean\n\tattempt: number\n\tmaxRetries: number\n}): 'success' | 'failed' | 'dead' => {\n\tif (args.ok) {\n\t\treturn 'success'\n\t}\n\treturn args.attempt > args.maxRetries ? 'dead' : 'failed'\n}\n","/** Max response-body characters retained for the delivery log. */\nconst MAX_RESPONSE_BODY = 2_000\n\nexport type DeliverArgs = {\n\turl: string\n\tbody: string\n\theaders: Record<string, string>\n\ttimeoutMs: number\n}\n\nexport type DeliverResult = {\n\tok: boolean\n\tresponseStatus?: number\n\tresponseBody?: string\n\terror?: string\n\tdurationMs: number\n}\n\n/** POST `body` to `url` with a hard timeout; never throws. */\nexport const deliver = async (args: DeliverArgs): Promise<DeliverResult> => {\n\tconst controller = new AbortController()\n\tconst timer = setTimeout(() => controller.abort(), args.timeoutMs)\n\tconst start = Date.now()\n\ttry {\n\t\tconst res = await fetch(args.url, {\n\t\t\tmethod: 'POST',\n\t\t\theaders: args.headers,\n\t\t\tbody: args.body,\n\t\t\tsignal: controller.signal,\n\t\t})\n\t\tconst text = await res.text()\n\t\treturn {\n\t\t\tok: res.ok,\n\t\t\tresponseStatus: res.status,\n\t\t\tresponseBody: text.slice(0, MAX_RESPONSE_BODY),\n\t\t\tdurationMs: Date.now() - start,\n\t\t}\n\t} catch (err) {\n\t\treturn {\n\t\t\tok: false,\n\t\t\terror: err instanceof Error ? err.message : String(err),\n\t\t\tdurationMs: Date.now() - start,\n\t\t}\n\t} finally {\n\t\tclearTimeout(timer)\n\t}\n}\n","import { createHmac } from 'node:crypto'\n\n/** HMAC-SHA256 over `${timestamp}.${body}`, hex-encoded. */\nexport const signPayload = (args: { secret: string; timestamp: number; body: string }): string =>\n\tcreateHmac('sha256', args.secret).update(`${args.timestamp}.${args.body}`).digest('hex')\n\n/** Wrap a signature hex in the versioned header value. */\nexport const signatureHeader = (hex: string): string => `v1=${hex}`\n","import type { ResolvedSubscription } from '../plugin/resolveSubscriptions'\nimport { type DeliverResult, deliver } from './deliver'\nimport { signatureHeader, signPayload } from './sign'\n\nconst USER_AGENT = '10x-media-webhooks'\n\n/** Assemble headers (+ signature) and POST the body to the subscription's URL. */\nexport const sendDelivery = (args: {\n\tsubscription: ResolvedSubscription\n\tdeliveryId: string\n\tevent: string\n\tbody: string\n\ttimeoutMs: number\n\tnow: number\n}): Promise<DeliverResult> => {\n\tconst { subscription, deliveryId, event, body, timeoutMs, now } = args\n\tconst timestamp = Math.floor(now / 1000)\n\tconst headers: Record<string, string> = {\n\t\t'Content-Type': 'application/json',\n\t\t'User-Agent': USER_AGENT,\n\t\t'X-Webhook-Id': deliveryId,\n\t\t'X-Webhook-Event': event,\n\t\t'X-Webhook-Timestamp': String(timestamp),\n\t\t...subscription.headers,\n\t}\n\tif (subscription.secret) {\n\t\theaders['X-Webhook-Signature'] = signatureHeader(\n\t\t\tsignPayload({ secret: subscription.secret, timestamp, body })\n\t\t)\n\t}\n\treturn deliver({ url: subscription.url, body, headers, timeoutMs })\n}\n","import type { TaskConfig } from 'payload'\n\nimport { WEBHOOK_DELIVER_TASK } from '../constants'\nimport type { CodeSubscription } from '../options'\nimport { resolveSubscriptionById } from '../plugin/resolveSubscriptions'\nimport { deriveDeliveryStatus } from './deriveDeliveryStatus'\nimport { sendDelivery } from './sendDelivery'\n\n/** Shared dependencies the delivery task closes over. */\nexport type DeliverTaskDeps = {\n\tdeliveriesSlug: string\n\tsubscriptionsSlug: string\n\tcodeSubscriptions: CodeSubscription[]\n\ttimeoutMs: number\n\tretries: number\n}\n\n/** Native Payload jobs task that performs one queued delivery attempt. */\nexport const buildDeliverTask = (deps: DeliverTaskDeps): TaskConfig =>\n\t({\n\t\tslug: WEBHOOK_DELIVER_TASK,\n\t\tretries: deps.retries,\n\t\tinputSchema: [{ name: 'deliveryId', type: 'text', required: true }],\n\t\thandler: async ({ input, job, req }) => {\n\t\t\tconst { payload } = req\n\t\t\tconst deliveryId = (input as { deliveryId: string }).deliveryId\n\t\t\tconst delivery = await payload.findByID({\n\t\t\t\tcollection: deps.deliveriesSlug,\n\t\t\t\tid: deliveryId,\n\t\t\t\toverrideAccess: true,\n\t\t\t\treq,\n\t\t\t})\n\t\t\tconst subscription = await resolveSubscriptionById({\n\t\t\t\tid: String(delivery.subscriptionId),\n\t\t\t\tcodeSubscriptions: deps.codeSubscriptions,\n\t\t\t\tsubscriptionsSlug: deps.subscriptionsSlug,\n\t\t\t\tpayload,\n\t\t\t\treq,\n\t\t\t})\n\t\t\tif (!subscription) {\n\t\t\t\tawait payload.update({\n\t\t\t\t\tcollection: deps.deliveriesSlug,\n\t\t\t\t\tid: deliveryId,\n\t\t\t\t\tdata: { status: 'dead', error: 'subscription not found' },\n\t\t\t\t\toverrideAccess: true,\n\t\t\t\t\treq,\n\t\t\t\t})\n\t\t\t\treturn { output: {} }\n\t\t\t}\n\n\t\t\tconst attempt = Number(job.totalTried ?? 0) + 1\n\t\t\tconst result = await sendDelivery({\n\t\t\t\tsubscription,\n\t\t\t\tdeliveryId,\n\t\t\t\tevent: String(delivery.event),\n\t\t\t\tbody: JSON.stringify(delivery.payload),\n\t\t\t\ttimeoutMs: deps.timeoutMs,\n\t\t\t\tnow: Date.now(),\n\t\t\t})\n\t\t\tconst status = deriveDeliveryStatus({ ok: result.ok, attempt, maxRetries: deps.retries })\n\t\t\tawait payload.update({\n\t\t\t\tcollection: deps.deliveriesSlug,\n\t\t\t\tid: deliveryId,\n\t\t\t\tdata: {\n\t\t\t\t\tstatus,\n\t\t\t\t\tattempt,\n\t\t\t\t\tresponseStatus: result.responseStatus,\n\t\t\t\t\tresponseBody: result.responseBody,\n\t\t\t\t\terror: result.error,\n\t\t\t\t\tdurationMs: result.durationMs,\n\t\t\t\t\tjobId: String(job.id),\n\t\t\t\t},\n\t\t\t\toverrideAccess: true,\n\t\t\t\treq,\n\t\t\t})\n\t\t\tif (!result.ok) {\n\t\t\t\tthrow new Error(`Webhook delivery failed: ${result.error ?? result.responseStatus}`)\n\t\t\t}\n\t\t\treturn { output: {} }\n\t\t},\n\t}) as TaskConfig\n","import type { Payload, PayloadRequest } from 'payload'\n\nimport { WEBHOOK_DELIVER_TASK } from '../constants'\nimport type { CodeSubscription } from '../options'\nimport { resolveSubscriptionById } from '../plugin/resolveSubscriptions'\nimport { sendDelivery } from './sendDelivery'\n\n/** Dependencies for re-dispatching a stored delivery. */\nexport type RedeliverDeps = {\n\tdeliveriesSlug: string\n\tsubscriptionsSlug: string\n\tcodeSubscriptions: CodeSubscription[]\n\tmode: 'queue' | 'inline'\n\ttimeoutMs: number\n\tqueue: string\n}\n\n/** Re-dispatch a past delivery from its stored payload, creating a new linked row. */\nexport const redeliverDelivery = async (args: {\n\tdeps: RedeliverDeps\n\tdeliveryId: string\n\tpayload: Payload\n\treq: PayloadRequest\n}): Promise<{ id: string }> => {\n\tconst { deps, deliveryId, payload, req } = args\n\tconst original = await payload.findByID({\n\t\tcollection: deps.deliveriesSlug,\n\t\tid: deliveryId,\n\t\toverrideAccess: true,\n\t\treq,\n\t})\n\tconst created = await payload.create({\n\t\tcollection: deps.deliveriesSlug,\n\t\tdata: {\n\t\t\tsubscriptionId: original.subscriptionId,\n\t\t\tendpoint: original.endpoint,\n\t\t\tevent: original.event,\n\t\t\tpayload: original.payload,\n\t\t\tstatus: 'pending',\n\t\t\tattempt: 0,\n\t\t},\n\t\toverrideAccess: true,\n\t\treq,\n\t})\n\tconst newId = String(created.id)\n\n\tif (deps.mode === 'queue') {\n\t\tawait payload.jobs.queue({\n\t\t\ttask: WEBHOOK_DELIVER_TASK,\n\t\t\tinput: { deliveryId: newId },\n\t\t\tqueue: deps.queue,\n\t\t})\n\t\treturn { id: newId }\n\t}\n\n\tconst subscription = await resolveSubscriptionById({\n\t\tid: String(original.subscriptionId),\n\t\tcodeSubscriptions: deps.codeSubscriptions,\n\t\tsubscriptionsSlug: deps.subscriptionsSlug,\n\t\tpayload,\n\t\treq,\n\t})\n\tif (!subscription) {\n\t\tawait payload.update({\n\t\t\tcollection: deps.deliveriesSlug,\n\t\t\tid: newId,\n\t\t\tdata: { status: 'dead', error: 'subscription not found' },\n\t\t\toverrideAccess: true,\n\t\t\treq,\n\t\t})\n\t\treturn { id: newId }\n\t}\n\tconst result = await sendDelivery({\n\t\tsubscription,\n\t\tdeliveryId: newId,\n\t\tevent: String(original.event),\n\t\tbody: JSON.stringify(original.payload),\n\t\ttimeoutMs: deps.timeoutMs,\n\t\tnow: Date.now(),\n\t})\n\tawait payload.update({\n\t\tcollection: deps.deliveriesSlug,\n\t\tid: newId,\n\t\tdata: {\n\t\t\tstatus: result.ok ? 'success' : 'dead',\n\t\t\tattempt: 1,\n\t\t\tresponseStatus: result.responseStatus,\n\t\t\tresponseBody: result.responseBody,\n\t\t\terror: result.error,\n\t\t\tdurationMs: result.durationMs,\n\t\t},\n\t\toverrideAccess: true,\n\t\treq,\n\t})\n\treturn { id: newId }\n}\n","import type { WebhookOperation } from '../options'\n\nconst OP_TO_EVENT: Record<WebhookOperation, string> = {\n\tcreate: 'created',\n\tupdate: 'updated',\n\tdelete: 'deleted',\n}\n\n/** Past-tense public event suffix for a Payload operation. */\nexport const operationToEvent = (op: WebhookOperation): string => OP_TO_EVENT[op]\n\n/** Public event id, e.g. `posts.created`. */\nexport const eventId = (collectionSlug: string, op: WebhookOperation): string =>\n\t`${collectionSlug}.${operationToEvent(op)}`\n\n/** Every event id the given opt-in collections can emit, in declaration order. */\nexport const eventCatalog = (\n\tcollections: Record<string, true | { operations?: WebhookOperation[] }>\n): string[] => {\n\tconst out: string[] = []\n\tfor (const [slug, cfg] of Object.entries(collections)) {\n\t\tconst ops = (cfg === true ? undefined : cfg.operations) ?? ['create', 'update', 'delete']\n\t\tfor (const op of ops) {\n\t\t\tout.push(eventId(slug, op))\n\t\t}\n\t}\n\treturn out\n}\n","import type { PayloadRequest } from 'payload'\n\nimport { eventId } from '../events/eventTypes'\nimport type { CollectionWebhookConfig, WebhookOperation } from '../options'\n\n/** The JSON body POSTed to a subscriber. */\nexport type WebhookBody = {\n\tid: string\n\tevent: string\n\tcollection: string\n\toperation: WebhookOperation\n\toccurredAt: string\n\tdata: unknown\n\tpreviousData?: unknown\n}\n\n/** Build the outbound body, applying any per-collection transform/redaction. */\nexport const buildPayload = (args: {\n\tdeliveryId: string\n\tcollection: string\n\toperation: WebhookOperation\n\tdoc: Record<string, unknown>\n\tpreviousDoc?: Record<string, unknown>\n\toccurredAt: string\n\tconfig?: CollectionWebhookConfig\n\treq: PayloadRequest\n}): WebhookBody => {\n\tconst { deliveryId, collection, operation, doc, previousDoc, occurredAt, config, req } = args\n\tconst data = config?.transform ? config.transform({ doc, previousDoc, operation, req }) : doc\n\tconst body: WebhookBody = {\n\t\tid: deliveryId,\n\t\tevent: eventId(collection, operation),\n\t\tcollection,\n\t\toperation,\n\t\toccurredAt,\n\t\tdata,\n\t}\n\tif (operation === 'update' && config?.includePreviousData && previousDoc) {\n\t\tbody.previousData = previousDoc\n\t}\n\treturn body\n}\n","import type { CollectionAfterChangeHook, CollectionAfterDeleteHook, PayloadRequest } from 'payload'\n\nimport { SECRET_REVEAL_CONTEXT, WEBHOOK_DELIVER_TASK } from '../constants'\nimport { buildPayload } from '../delivery/buildPayload'\nimport { sendDelivery } from '../delivery/sendDelivery'\nimport type { CodeSubscription, CollectionWebhookConfig, WebhookOperation } from '../options'\nimport {\n\tfromCodeSubscription,\n\tfromCollectionRow,\n\tmatchSubscriptions,\n\ttype ResolvedSubscription,\n} from '../plugin/resolveSubscriptions'\nimport { eventId } from './eventTypes'\n\n/** Per-collection dispatch dependencies. */\nexport type WebhookDispatchDeps = {\n\tcollectionSlug: string\n\tconfig: CollectionWebhookConfig\n\toperations: WebhookOperation[]\n\tdeliveriesSlug: string\n\tsubscriptionsSlug: string\n\tcodeSubscriptions: CodeSubscription[]\n\tmode: 'queue' | 'inline'\n\ttimeoutMs: number\n\tqueue: string\n}\n\n/** Max collection subscriptions scanned per event (pagination is a future enhancement). */\nconst SUBSCRIPTION_SCAN_LIMIT = 1_000\n\nconst resolveListening = async (args: {\n\tdeps: WebhookDispatchDeps\n\tevent: string\n\treq: PayloadRequest\n}): Promise<ResolvedSubscription[]> => {\n\tconst code = args.deps.codeSubscriptions.map(fromCodeSubscription)\n\targs.req.context[SECRET_REVEAL_CONTEXT.forSigning] = true\n\tlet res: Awaited<ReturnType<typeof args.req.payload.find>>\n\ttry {\n\t\tres = await args.req.payload.find({\n\t\t\tcollection: args.deps.subscriptionsSlug,\n\t\t\twhere: { enabled: { not_equals: false } },\n\t\t\tlimit: SUBSCRIPTION_SCAN_LIMIT,\n\t\t\tdepth: 0,\n\t\t\toverrideAccess: true,\n\t\t\treq: args.req,\n\t\t})\n\t} finally {\n\t\targs.req.context[SECRET_REVEAL_CONTEXT.forSigning] = false\n\t}\n\tif (res.docs.length >= SUBSCRIPTION_SCAN_LIMIT) {\n\t\targs.req.payload.logger.warn(\n\t\t\t`@10x-media/webhooks: subscription scan hit the ${SUBSCRIPTION_SCAN_LIMIT} cap; some subscriptions may be skipped for ${args.event}.`\n\t\t)\n\t}\n\tconst collection = res.docs.map((d) =>\n\t\tfromCollectionRow(d as Parameters<typeof fromCollectionRow>[0])\n\t)\n\treturn matchSubscriptions([...code, ...collection], args.event)\n}\n\nconst dispatch = async (args: {\n\tdeps: WebhookDispatchDeps\n\toperation: WebhookOperation\n\tdoc: Record<string, unknown>\n\tpreviousDoc?: Record<string, unknown>\n\treq: PayloadRequest\n}): Promise<void> => {\n\tconst { deps, operation, doc, previousDoc, req } = args\n\tif (!deps.operations.includes(operation)) {\n\t\treturn\n\t}\n\tconst { payload } = req\n\tconst event = eventId(deps.collectionSlug, operation)\n\tconst subscriptions = await resolveListening({ deps, event, req })\n\tif (!subscriptions.length) {\n\t\treturn\n\t}\n\n\tconst occurredAt = new Date().toISOString()\n\tfor (const subscription of subscriptions) {\n\t\tconst created = await payload.create({\n\t\t\tcollection: deps.deliveriesSlug,\n\t\t\tdata: {\n\t\t\t\tsubscriptionId: subscription.id,\n\t\t\t\tendpoint: subscription.url,\n\t\t\t\tevent,\n\t\t\t\tstatus: 'pending',\n\t\t\t\tattempt: 0,\n\t\t\t},\n\t\t\toverrideAccess: true,\n\t\t\treq,\n\t\t})\n\t\tconst deliveryId = String(created.id)\n\t\tconst body = buildPayload({\n\t\t\tdeliveryId,\n\t\t\tcollection: deps.collectionSlug,\n\t\t\toperation,\n\t\t\tdoc,\n\t\t\tpreviousDoc,\n\t\t\toccurredAt,\n\t\t\tconfig: deps.config,\n\t\t\treq,\n\t\t})\n\t\tawait payload.update({\n\t\t\tcollection: deps.deliveriesSlug,\n\t\t\tid: deliveryId,\n\t\t\tdata: { payload: body },\n\t\t\toverrideAccess: true,\n\t\t\treq,\n\t\t})\n\n\t\tif (deps.mode === 'queue') {\n\t\t\tawait payload.jobs.queue({\n\t\t\t\ttask: WEBHOOK_DELIVER_TASK,\n\t\t\t\tinput: { deliveryId },\n\t\t\t\tqueue: deps.queue,\n\t\t\t})\n\t\t\tcontinue\n\t\t}\n\n\t\t// best-effort: a delivery failure must never abort the caller's write\n\t\ttry {\n\t\t\tconst result = await sendDelivery({\n\t\t\t\tsubscription,\n\t\t\t\tdeliveryId,\n\t\t\t\tevent,\n\t\t\t\tbody: JSON.stringify(body),\n\t\t\t\ttimeoutMs: deps.timeoutMs,\n\t\t\t\tnow: Date.now(),\n\t\t\t})\n\t\t\tawait payload.update({\n\t\t\t\tcollection: deps.deliveriesSlug,\n\t\t\t\tid: deliveryId,\n\t\t\t\tdata: {\n\t\t\t\t\tstatus: result.ok ? 'success' : 'dead',\n\t\t\t\t\tattempt: 1,\n\t\t\t\t\tresponseStatus: result.responseStatus,\n\t\t\t\t\tresponseBody: result.responseBody,\n\t\t\t\t\terror: result.error,\n\t\t\t\t\tdurationMs: result.durationMs,\n\t\t\t\t},\n\t\t\t\toverrideAccess: true,\n\t\t\t\treq,\n\t\t\t})\n\t\t} catch (err) {\n\t\t\tpayload.logger.error(\n\t\t\t\t`@10x-media/webhooks: inline delivery ${deliveryId} threw: ${err instanceof Error ? err.message : String(err)}`\n\t\t\t)\n\t\t}\n\t}\n}\n\n/** afterChange hook factory for an opt-in source collection. */\nexport const makeAfterChange =\n\t(deps: WebhookDispatchDeps): CollectionAfterChangeHook =>\n\tasync ({ doc, previousDoc, operation, req }) => {\n\t\tconst op: WebhookOperation = operation === 'create' ? 'create' : 'update'\n\t\tawait dispatch({ deps, operation: op, doc, previousDoc, req })\n\t\treturn doc\n\t}\n\n/** afterDelete hook factory for an opt-in source collection. */\nexport const makeAfterDelete =\n\t(deps: WebhookDispatchDeps): CollectionAfterDeleteHook =>\n\tasync ({ doc, req }) => {\n\t\tawait dispatch({ deps, operation: 'delete', doc: doc as Record<string, unknown>, req })\n\t\treturn doc\n\t}\n","import type { PayloadRequest } from 'payload'\n\nimport { DEFAULT_DELIVERY_QUEUE, DEFAULT_RETRIES, DEFAULT_TIMEOUT_MS } from './constants'\n\nexport type WebhookOperation = 'create' | 'update' | 'delete'\n\nexport type CollectionWebhookConfig = {\n\toperations?: WebhookOperation[]\n\tincludePreviousData?: boolean\n\ttransform?: (args: {\n\t\tdoc: Record<string, unknown>\n\t\tpreviousDoc?: Record<string, unknown>\n\t\toperation: WebhookOperation\n\t\treq: PayloadRequest\n\t}) => unknown\n}\n\nexport type CodeSubscription = {\n\tid: string\n\turl: string\n\tevents: string[]\n\tsecret?: string\n\theaders?: Record<string, string>\n\tenabled?: boolean\n}\n\nexport type DeliveryMode = 'auto' | 'queue' | 'inline'\n\nexport type DeliveryOptions = {\n\tmode?: DeliveryMode\n\ttimeoutMs?: number\n\tretries?: number\n\tqueue?: string\n}\n\nexport type WebhooksPluginOptions = {\n\tdisabled?: boolean\n\tcollections?: Record<string, true | CollectionWebhookConfig>\n\tsubscriptions?: CodeSubscription[]\n\tdelivery?: DeliveryMode | DeliveryOptions\n\tsubscriptionsCollection?: { slug?: string; hidden?: boolean }\n\tdeliveriesLog?: { slug?: string; hidden?: boolean }\n}\n\nexport type ResolvedDeliveryOptions = {\n\tmode: DeliveryMode\n\ttimeoutMs: number\n\tretries: number\n\tqueue: string\n}\n\nexport const resolveDeliveryOptions = (\n\tdelivery: WebhooksPluginOptions['delivery']\n): ResolvedDeliveryOptions => {\n\tconst opts: DeliveryOptions =\n\t\tdelivery === undefined ? {} : typeof delivery === 'string' ? { mode: delivery } : delivery\n\treturn {\n\t\tmode: opts.mode ?? 'auto',\n\t\ttimeoutMs: opts.timeoutMs ?? DEFAULT_TIMEOUT_MS,\n\t\tretries: opts.retries ?? DEFAULT_RETRIES,\n\t\tqueue: opts.queue ?? DEFAULT_DELIVERY_QUEUE,\n\t}\n}\n","import type { DeliveryMode } from '../options'\n\n/** Resolve the effective execution mode, warning when `queue` is forced with no runner. */\nexport const resolveMode = (args: {\n\tconfigured: DeliveryMode\n\thasAutoRun: boolean\n\thasJobsPlugin: boolean\n\twarn: (msg: string) => void\n}): 'queue' | 'inline' => {\n\tconst runnerLikely = args.hasAutoRun || args.hasJobsPlugin\n\tif (args.configured === 'queue') {\n\t\tif (!runnerLikely) {\n\t\t\targs.warn(\n\t\t\t\t'@10x-media/webhooks: delivery.mode=\"queue\" but no job runner detected (config.jobs.autoRun unset and @10x-media/jobs not installed); queued deliveries stay pending until a worker runs.'\n\t\t\t)\n\t\t}\n\t\treturn 'queue'\n\t}\n\tif (args.configured === 'inline') {\n\t\treturn 'inline'\n\t}\n\treturn runnerLikely ? 'queue' : 'inline'\n}\n","import type { Config, Endpoint } from 'payload'\n\nimport { buildDeliveriesCollection } from '../collections/deliveries'\nimport { buildSubscriptionsCollection } from '../collections/subscriptions'\nimport { DEFAULT_DELIVERIES_SLUG, DEFAULT_SUBSCRIPTIONS_SLUG, RESERVED_SLUGS } from '../constants'\nimport { buildDeliverTask } from '../delivery/deliverTask'\nimport { redeliverDelivery } from '../delivery/redeliver'\nimport { eventCatalog } from '../events/eventTypes'\nimport { makeAfterChange, makeAfterDelete } from '../events/hooks'\nimport {\n\ttype CollectionWebhookConfig,\n\tresolveDeliveryOptions,\n\ttype WebhooksPluginOptions,\n} from '../options'\nimport { resolveMode } from './resolveMode'\n\n/** Register collections, the delivery task, source hooks, and the redeliver endpoint. */\nexport const registerWebhooks = (args: {\n\tconfig: Config\n\toptions: WebhooksPluginOptions\n\thasJobsPlugin: boolean\n}): void => {\n\tconst { config, options } = args\n\tconst sources = options.collections ?? {}\n\tconst subscriptionsSlug = options.subscriptionsCollection?.slug ?? DEFAULT_SUBSCRIPTIONS_SLUG\n\tconst deliveriesSlug = options.deliveriesLog?.slug ?? DEFAULT_DELIVERIES_SLUG\n\tconst reserved = new Set<string>([...RESERVED_SLUGS, subscriptionsSlug, deliveriesSlug])\n\tconst sourceSlugs = Object.keys(sources).filter((s) => !reserved.has(s))\n\tconst delivery = resolveDeliveryOptions(options.delivery)\n\tconst codeSubscriptions = options.subscriptions ?? []\n\tconst catalog = eventCatalog(Object.fromEntries(sourceSlugs.map((s) => [s, sources[s] ?? true])))\n\n\tconst mode = resolveMode({\n\t\tconfigured: delivery.mode,\n\t\thasAutoRun: Boolean(config.jobs?.autoRun),\n\t\thasJobsPlugin: args.hasJobsPlugin,\n\t\t// config-build time: payload.logger does not exist yet, so console is the only channel\n\t\twarn: (m) => console.warn(m),\n\t})\n\n\tconfig.collections ??= []\n\tconfig.collections.push(\n\t\tbuildSubscriptionsCollection({\n\t\t\tslug: subscriptionsSlug,\n\t\t\tevents: catalog,\n\t\t\thidden: options.subscriptionsCollection?.hidden ?? false,\n\t\t})\n\t)\n\tconst deliveries = buildDeliveriesCollection({\n\t\tslug: deliveriesSlug,\n\t\thidden: options.deliveriesLog?.hidden ?? false,\n\t})\n\n\tconst redeliverEndpoint: Endpoint = {\n\t\tpath: '/:id/redeliver',\n\t\tmethod: 'post',\n\t\thandler: async (req) => {\n\t\t\t// coarse auth: any logged-in user may redeliver any delivery (matches the deliveries collection access)\n\t\t\tif (!req.user) {\n\t\t\t\treturn Response.json({ error: 'unauthorized' }, { status: 401 })\n\t\t\t}\n\t\t\tconst id = req.routeParams?.id\n\t\t\tif (typeof id !== 'string') {\n\t\t\t\treturn Response.json({ error: 'missing id' }, { status: 400 })\n\t\t\t}\n\t\t\tconst result = await redeliverDelivery({\n\t\t\t\tdeps: {\n\t\t\t\t\tdeliveriesSlug,\n\t\t\t\t\tsubscriptionsSlug,\n\t\t\t\t\tcodeSubscriptions,\n\t\t\t\t\tmode,\n\t\t\t\t\ttimeoutMs: delivery.timeoutMs,\n\t\t\t\t\tqueue: delivery.queue,\n\t\t\t\t},\n\t\t\t\tdeliveryId: id,\n\t\t\t\tpayload: req.payload,\n\t\t\t\treq,\n\t\t\t})\n\t\t\treturn Response.json(result, { status: 202 })\n\t\t},\n\t}\n\tdeliveries.endpoints = [...(deliveries.endpoints || []), redeliverEndpoint]\n\tconfig.collections.push(deliveries)\n\n\tconfig.jobs ??= {}\n\tconfig.jobs.tasks ??= []\n\tconfig.jobs.tasks.push(\n\t\tbuildDeliverTask({\n\t\t\tdeliveriesSlug,\n\t\t\tsubscriptionsSlug,\n\t\t\tcodeSubscriptions,\n\t\t\ttimeoutMs: delivery.timeoutMs,\n\t\t\tretries: delivery.retries,\n\t\t})\n\t)\n\n\tfor (let i = 0; i < config.collections.length; i++) {\n\t\tconst collection = config.collections[i]\n\t\tif (!collection || !sourceSlugs.includes(collection.slug)) {\n\t\t\tcontinue\n\t\t}\n\t\tconst cfg = sources[collection.slug]\n\t\tconst collectionConfig: CollectionWebhookConfig = cfg === true || cfg === undefined ? {} : cfg\n\t\tconst deps = {\n\t\t\tcollectionSlug: collection.slug,\n\t\t\tconfig: collectionConfig,\n\t\t\toperations: collectionConfig.operations ?? [\n\t\t\t\t'create' as const,\n\t\t\t\t'update' as const,\n\t\t\t\t'delete' as const,\n\t\t\t],\n\t\t\tdeliveriesSlug,\n\t\t\tsubscriptionsSlug,\n\t\t\tcodeSubscriptions,\n\t\t\tmode,\n\t\t\ttimeoutMs: delivery.timeoutMs,\n\t\t\tqueue: delivery.queue,\n\t\t}\n\t\tconfig.collections[i] = {\n\t\t\t...collection,\n\t\t\thooks: {\n\t\t\t\t...collection.hooks,\n\t\t\t\tafterChange: [...(collection.hooks?.afterChange ?? []), makeAfterChange(deps)],\n\t\t\t\tafterDelete: [...(collection.hooks?.afterDelete ?? []), makeAfterDelete(deps)],\n\t\t\t},\n\t\t}\n\t}\n}\n","import { type Config, definePlugin } from 'payload'\n\nimport type { WebhooksPluginOptions } from './options'\nimport { registerTranslations } from './plugin/registerTranslations'\nimport { registerWebhooks } from './plugin/registerWebhooks'\n\nexport type { WebhooksPluginOptions } from './options'\n\ndeclare module 'payload' {\n\tinterface RegisteredPlugins {\n\t\t'@10x-media/webhooks': WebhooksPluginOptions\n\t}\n}\n\n/** The trigger slug webhooks contributes to the automations catalog. */\nexport const WEBHOOK_TRIGGER_SLUG = 'webhook' as const\n\n/**\n * Webhooks plugin for Payload v3. Runs before automations (`order: 10`) so it can\n * push its `webhook` trigger into automations when present, and builds outbound\n * delivery: opt-in collections emit signed HTTP POSTs to subscribed endpoints,\n * delivered via native Payload jobs or bounded-await inline.\n */\nexport const webhooks = definePlugin<WebhooksPluginOptions>({\n\tslug: '@10x-media/webhooks',\n\torder: 10,\n\tplugin: ({ config, plugins, ...options }): Config => {\n\t\tif (options.disabled === true) {\n\t\t\treturn config\n\t\t}\n\t\tregisterTranslations(config)\n\n\t\tconst automationsPlugin = plugins['@10x-media/automations']\n\t\tif (automationsPlugin?.options) {\n\t\t\tconst opts = automationsPlugin.options as { triggers?: string[] }\n\t\t\topts.triggers = [...(opts.triggers ?? []), WEBHOOK_TRIGGER_SLUG]\n\t\t}\n\n\t\tregisterWebhooks({ config, options, hasJobsPlugin: Boolean(plugins['@10x-media/jobs']) })\n\n\t\treturn config\n\t},\n})\n\nexport type { WebhooksPluginOptions as PluginOptions } from './options'\n"],"mappings":";;;;;;;;;;;AAYA,MAAa,wBAAwB,WAAyB;CAC7D,OAAO,SAAS,CAAC;CACjB,OAAO,KAAK,eAAe,gBAC1B,cACA,OAAO,KAAK,gBAAgB,CAAC,CAC9B;AACD;AChBA,MAAa,cAAc;AAC3B,MAAa,uBAAuB;;AAMpC,MAAa,cAAc;;;;;;;AAQ3B,MAAa,wBAAwB;CACpC,YAAY;CACZ,MAAM;AACP;AACA,MAAa,iBAAiB;CAC7B;CACA;CACA;CACA;AACD;;;;;;;;ACdA,MAAa,eAAe,MAA0B;;AAGtD,MAAa,eACX,SACA,EAAE,QACF,YAAY,CAAC,EAAE,GAAG;;;ACZpB,MAAM,cAAc;AAEpB,MAAMA,cAAY,EAAE,UAAuC,QAAQ,IAAI,IAAI;;AAG3E,MAAa,6BAA6B,UAGjB;CACxB,MAAM,KAAK;CACX,QAAQ;EACP,UAAU,YAAY,KAAK,gBAAgB;EAC3C,QAAQ,YAAY,KAAK,cAAc;CACxC;CACA,OAAO;EACN,OAAO;EACP,YAAY;EACZ,gBAAgB;GAAC;GAAS;GAAY;GAAU;GAAkB;GAAW;EAAW;EACxF,QAAQ,KAAK;CACd;CACA,QAAQ;EAAE,MAAMA;EAAU,cAAc;EAAO,cAAc;EAAO,QAAQA;CAAS;CACrF,QAAQ;EACP;GAAE,MAAM;GAAkB,MAAM;GAAQ,OAAO;EAAK;EACpD;GAAE,MAAM;GAAY,MAAM;EAAO;EACjC;GAAE,MAAM;GAAS,MAAM;GAAQ,OAAO;EAAK;EAC3C;GACC,MAAM;GACN,MAAM;GACN,cAAc;GACd,SAAS;IAAC;IAAW;IAAW;IAAU;GAAM;GAChD,OAAO,EAAE,YAAY,EAAE,MAAM,YAAY,EAAE;EAC5C;EACA;GAAE,MAAM;GAAW,MAAM;GAAU,cAAc;EAAE;EACnD;GAAE,MAAM;GAAkB,MAAM;EAAS;EACzC;GAAE,MAAM;GAAgB,MAAM;EAAW;EACzC;GAAE,MAAM;GAAS,MAAM;EAAW;EAClC;GAAE,MAAM;GAAc,MAAM;EAAS;EACrC;GAAE,MAAM;GAAS,MAAM;EAAO;EAC9B;GAAE,MAAM;GAAW,MAAM;EAAO;EAChC;GACC,MAAM;GACN,MAAM;GACN,OAAO,EAAE,YAAY,EAAE,OAAO,6CAA6C,EAAE;EAC9E;CACD;AACD;;;ACvCA,MAAM,kBAA8C,EAAE,MAAM,WAAW,UAAU;CAChF,IAAI,cAAc,YAAY,CAAC,KAAK,QAAQ;EAC3C,IAAI,QAAQ,sBAAsB,QAAQ;EAC1C,OAAO;GAAE,GAAG;GAAM,QAAQ,YAAY,EAAE,EAAE,SAAS,KAAK;EAAE;CAC3D;CACA,OAAO;AACR;;;;;;AAOA,MAAM,cAAyB,EAAE,OAAO,UAAU;CACjD,IAAI,IAAI,QAAQ,sBAAsB,eAAe,IAAI,QAAQ,sBAAsB,OACtF,OAAO;CAER,OAAO,SAAS,OAAO,QAAQ;AAChC;AAEA,MAAM,mBAA8C,EAAE,KAAK,UAAU;CACpE,IAAI,QAAQ,sBAAsB,QAAQ;CAC1C,OAAO;AACR;AAEA,MAAM,YAAY,EAAE,UAAuC,QAAQ,IAAI,IAAI;;AAG3E,MAAa,gCAAgC,UAIpB;CACxB,MAAM,KAAK;CACX,QAAQ;EACP,UAAU,YAAY,KAAK,oBAAoB;EAC/C,QAAQ,YAAY,KAAK,kBAAkB;CAC5C;CACA,OAAO;EACN,OAAO;EACP,YAAY;EACZ,gBAAgB;GAAC;GAAQ;GAAO;EAAS;EACzC,QAAQ,KAAK;CACd;CACA,QAAQ;EAAE,MAAM;EAAU,QAAQ;EAAU,QAAQ;EAAU,QAAQ;CAAS;CAC/E,OAAO;EAAE,cAAc,CAAC,cAAc;EAAG,aAAa,CAAC,eAAe;CAAE;CACxE,QAAQ;EACP;GAAE,MAAM;GAAQ,MAAM;GAAQ,UAAU;GAAM,OAAO,YAAY,KAAK,SAAS;EAAE;EACjF;GAAE,MAAM;GAAO,MAAM;GAAQ,UAAU;GAAM,OAAO,YAAY,KAAK,QAAQ;EAAE;EAC/E;GACC,MAAM;GACN,MAAM;GACN,cAAc;GACd,OAAO,YAAY,KAAK,YAAY;EACrC;EACA;GACC,MAAM;GACN,MAAM;GACN,SAAS;GACT,OAAO,YAAY,KAAK,WAAW;GACnC,SAAS,KAAK,OAAO,SAClB,KAAK,OAAO,KAAK,OAAO;IAAE,OAAO;IAAG,OAAO;GAAE,EAAE,IAC/C,CAAC;IAAE,OAAO;IAAU,OAAO;GAAW,CAAC;EAC3C;EACA;GACC,MAAM;GACN,MAAM;GACN,OAAO,YAAY,KAAK,WAAW;GACnC,OAAO;IAAE,UAAU;IAAM,aAAa,YAAY,KAAK,eAAe;GAAE;GACxE,QAAQ,EAAE,cAAc,MAAM;GAC9B,OAAO,EAAE,WAAW,CAAC,UAAU,EAAE;EAClC;EACA;GACC,MAAM;GACN,MAAM;GACN,OAAO,YAAY,KAAK,YAAY;GACpC,QAAQ,CACP;IAAE,MAAM;IAAO,MAAM;IAAQ,UAAU;GAAK,GAC5C;IAAE,MAAM;IAAS,MAAM;GAAO,CAC/B;EACD;EACA;GAAE,MAAM;GAAe,MAAM;GAAY,OAAO,YAAY,KAAK,gBAAgB;EAAE;CACpF;AACD;;;AC/EA,MAAM,cACL,YACwC;CACxC,IAAI,CAAC,SAAS,QACb;CAED,MAAM,MAA8B,CAAC;CACrC,KAAK,MAAM,KAAK,SACf,IAAI,EAAE,KACL,IAAI,EAAE,OAAO,EAAE,SAAS;CAG1B,OAAO,OAAO,KAAK,GAAG,EAAE,SAAS,MAAM,KAAA;AACxC;;AAGA,MAAa,qBAAqB,SAOL;CAC5B,IAAI,OAAO,IAAI,EAAE;CACjB,QAAQ;CACR,KAAK,IAAI;CACT,QAAQ,IAAI,UAAU,CAAC;CACvB,QAAQ,IAAI,UAAU,KAAA;CACtB,SAAS,WAAW,IAAI,OAAO;CAC/B,SAAS,IAAI,YAAY;AAC1B;;AAGA,MAAa,wBAAwB,SAAiD;CACrF,IAAI,IAAI;CACR,QAAQ;CACR,KAAK,IAAI;CACT,QAAQ,IAAI;CACZ,QAAQ,IAAI;CACZ,SAAS,IAAI;CACb,SAAS,IAAI,YAAY;AAC1B;;AAGA,MAAa,sBACZ,MACA,UAC4B,KAAK,QAAQ,MAAM,EAAE,WAAW,EAAE,OAAO,SAAS,KAAK,CAAC;;AAGrF,MAAa,0BAA0B,OAAO,SAMF;CAC3C,MAAM,OAAO,KAAK,kBAAkB,MAAM,MAAM,EAAE,OAAO,KAAK,EAAE;CAChE,IAAI,MACH,OAAO,qBAAqB,IAAI;CAEjC,KAAK,IAAI,QAAQ,sBAAsB,cAAc;CACrD,IAAI;EASH,MAAM,OAAM,MARM,KAAK,QAAQ,KAAK;GACnC,YAAY,KAAK;GACjB,OAAO,EAAE,IAAI,EAAE,QAAQ,KAAK,GAAG,EAAE;GACjC,OAAO;GACP,OAAO;GACP,gBAAgB;GAChB,KAAK,KAAK;EACX,CAAC,GACe,KAAK;EACrB,OAAO,MAAM,kBAAkB,GAAG,IAAI;CACvC,UAAU;EACT,KAAK,IAAI,QAAQ,sBAAsB,cAAc;CACtD;AACD;;;;AC5FA,MAAa,wBAAwB,SAIA;CACpC,IAAI,KAAK,IACR,OAAO;CAER,OAAO,KAAK,UAAU,KAAK,aAAa,SAAS;AAClD;;;;ACTA,MAAM,oBAAoB;;AAkB1B,MAAa,UAAU,OAAO,SAA8C;CAC3E,MAAM,aAAa,IAAI,gBAAgB;CACvC,MAAM,QAAQ,iBAAiB,WAAW,MAAM,GAAG,KAAK,SAAS;CACjE,MAAM,QAAQ,KAAK,IAAI;CACvB,IAAI;EACH,MAAM,MAAM,MAAM,MAAM,KAAK,KAAK;GACjC,QAAQ;GACR,SAAS,KAAK;GACd,MAAM,KAAK;GACX,QAAQ,WAAW;EACpB,CAAC;EACD,MAAM,OAAO,MAAM,IAAI,KAAK;EAC5B,OAAO;GACN,IAAI,IAAI;GACR,gBAAgB,IAAI;GACpB,cAAc,KAAK,MAAM,GAAG,iBAAiB;GAC7C,YAAY,KAAK,IAAI,IAAI;EAC1B;CACD,SAAS,KAAK;EACb,OAAO;GACN,IAAI;GACJ,OAAO,eAAe,QAAQ,IAAI,UAAU,OAAO,GAAG;GACtD,YAAY,KAAK,IAAI,IAAI;EAC1B;CACD,UAAU;EACT,aAAa,KAAK;CACnB;AACD;;;;AC3CA,MAAa,eAAe,SAC3B,WAAW,UAAU,KAAK,MAAM,EAAE,OAAO,GAAG,KAAK,UAAU,GAAG,KAAK,MAAM,EAAE,OAAO,KAAK;;AAGxF,MAAa,mBAAmB,QAAwB,MAAM;;;ACH9D,MAAM,aAAa;;AAGnB,MAAa,gBAAgB,SAOC;CAC7B,MAAM,EAAE,cAAc,YAAY,OAAO,MAAM,WAAW,QAAQ;CAClE,MAAM,YAAY,KAAK,MAAM,MAAM,GAAI;CACvC,MAAM,UAAkC;EACvC,gBAAgB;EAChB,cAAc;EACd,gBAAgB;EAChB,mBAAmB;EACnB,uBAAuB,OAAO,SAAS;EACvC,GAAG,aAAa;CACjB;CACA,IAAI,aAAa,QAChB,QAAQ,yBAAyB,gBAChC,YAAY;EAAE,QAAQ,aAAa;EAAQ;EAAW;CAAK,CAAC,CAC7D;CAED,OAAO,QAAQ;EAAE,KAAK,aAAa;EAAK;EAAM;EAAS;CAAU,CAAC;AACnE;;;;ACbA,MAAa,oBAAoB,UAC/B;CACA,MAAM;CACN,SAAS,KAAK;CACd,aAAa,CAAC;EAAE,MAAM;EAAc,MAAM;EAAQ,UAAU;CAAK,CAAC;CAClE,SAAS,OAAO,EAAE,OAAO,KAAK,UAAU;EACvC,MAAM,EAAE,YAAY;EACpB,MAAM,aAAc,MAAiC;EACrD,MAAM,WAAW,MAAM,QAAQ,SAAS;GACvC,YAAY,KAAK;GACjB,IAAI;GACJ,gBAAgB;GAChB;EACD,CAAC;EACD,MAAM,eAAe,MAAM,wBAAwB;GAClD,IAAI,OAAO,SAAS,cAAc;GAClC,mBAAmB,KAAK;GACxB,mBAAmB,KAAK;GACxB;GACA;EACD,CAAC;EACD,IAAI,CAAC,cAAc;GAClB,MAAM,QAAQ,OAAO;IACpB,YAAY,KAAK;IACjB,IAAI;IACJ,MAAM;KAAE,QAAQ;KAAQ,OAAO;IAAyB;IACxD,gBAAgB;IAChB;GACD,CAAC;GACD,OAAO,EAAE,QAAQ,CAAC,EAAE;EACrB;EAEA,MAAM,UAAU,OAAO,IAAI,cAAc,CAAC,IAAI;EAC9C,MAAM,SAAS,MAAM,aAAa;GACjC;GACA;GACA,OAAO,OAAO,SAAS,KAAK;GAC5B,MAAM,KAAK,UAAU,SAAS,OAAO;GACrC,WAAW,KAAK;GAChB,KAAK,KAAK,IAAI;EACf,CAAC;EACD,MAAM,SAAS,qBAAqB;GAAE,IAAI,OAAO;GAAI;GAAS,YAAY,KAAK;EAAQ,CAAC;EACxF,MAAM,QAAQ,OAAO;GACpB,YAAY,KAAK;GACjB,IAAI;GACJ,MAAM;IACL;IACA;IACA,gBAAgB,OAAO;IACvB,cAAc,OAAO;IACrB,OAAO,OAAO;IACd,YAAY,OAAO;IACnB,OAAO,OAAO,IAAI,EAAE;GACrB;GACA,gBAAgB;GAChB;EACD,CAAC;EACD,IAAI,CAAC,OAAO,IACX,MAAM,IAAI,MAAM,4BAA4B,OAAO,SAAS,OAAO,gBAAgB;EAEpF,OAAO,EAAE,QAAQ,CAAC,EAAE;CACrB;AACD;;;;AC9DD,MAAa,oBAAoB,OAAO,SAKT;CAC9B,MAAM,EAAE,MAAM,YAAY,SAAS,QAAQ;CAC3C,MAAM,WAAW,MAAM,QAAQ,SAAS;EACvC,YAAY,KAAK;EACjB,IAAI;EACJ,gBAAgB;EAChB;CACD,CAAC;CACD,MAAM,UAAU,MAAM,QAAQ,OAAO;EACpC,YAAY,KAAK;EACjB,MAAM;GACL,gBAAgB,SAAS;GACzB,UAAU,SAAS;GACnB,OAAO,SAAS;GAChB,SAAS,SAAS;GAClB,QAAQ;GACR,SAAS;EACV;EACA,gBAAgB;EAChB;CACD,CAAC;CACD,MAAM,QAAQ,OAAO,QAAQ,EAAE;CAE/B,IAAI,KAAK,SAAS,SAAS;EAC1B,MAAM,QAAQ,KAAK,MAAM;GACxB,MAAM;GACN,OAAO,EAAE,YAAY,MAAM;GAC3B,OAAO,KAAK;EACb,CAAC;EACD,OAAO,EAAE,IAAI,MAAM;CACpB;CAEA,MAAM,eAAe,MAAM,wBAAwB;EAClD,IAAI,OAAO,SAAS,cAAc;EAClC,mBAAmB,KAAK;EACxB,mBAAmB,KAAK;EACxB;EACA;CACD,CAAC;CACD,IAAI,CAAC,cAAc;EAClB,MAAM,QAAQ,OAAO;GACpB,YAAY,KAAK;GACjB,IAAI;GACJ,MAAM;IAAE,QAAQ;IAAQ,OAAO;GAAyB;GACxD,gBAAgB;GAChB;EACD,CAAC;EACD,OAAO,EAAE,IAAI,MAAM;CACpB;CACA,MAAM,SAAS,MAAM,aAAa;EACjC;EACA,YAAY;EACZ,OAAO,OAAO,SAAS,KAAK;EAC5B,MAAM,KAAK,UAAU,SAAS,OAAO;EACrC,WAAW,KAAK;EAChB,KAAK,KAAK,IAAI;CACf,CAAC;CACD,MAAM,QAAQ,OAAO;EACpB,YAAY,KAAK;EACjB,IAAI;EACJ,MAAM;GACL,QAAQ,OAAO,KAAK,YAAY;GAChC,SAAS;GACT,gBAAgB,OAAO;GACvB,cAAc,OAAO;GACrB,OAAO,OAAO;GACd,YAAY,OAAO;EACpB;EACA,gBAAgB;EAChB;CACD,CAAC;CACD,OAAO,EAAE,IAAI,MAAM;AACpB;;;AC7FA,MAAM,cAAgD;CACrD,QAAQ;CACR,QAAQ;CACR,QAAQ;AACT;;AAGA,MAAa,oBAAoB,OAAiC,YAAY;;AAG9E,MAAa,WAAW,gBAAwB,OAC/C,GAAG,eAAe,GAAG,iBAAiB,EAAE;;AAGzC,MAAa,gBACZ,gBACc;CACd,MAAM,MAAgB,CAAC;CACvB,KAAK,MAAM,CAAC,MAAM,QAAQ,OAAO,QAAQ,WAAW,GAAG;EACtD,MAAM,OAAO,QAAQ,OAAO,KAAA,IAAY,IAAI,eAAe;GAAC;GAAU;GAAU;EAAQ;EACxF,KAAK,MAAM,MAAM,KAChB,IAAI,KAAK,QAAQ,MAAM,EAAE,CAAC;CAE5B;CACA,OAAO;AACR;;;;ACVA,MAAa,gBAAgB,SASV;CAClB,MAAM,EAAE,YAAY,YAAY,WAAW,KAAK,aAAa,YAAY,QAAQ,QAAQ;CACzF,MAAM,OAAO,QAAQ,YAAY,OAAO,UAAU;EAAE;EAAK;EAAa;EAAW;CAAI,CAAC,IAAI;CAC1F,MAAM,OAAoB;EACzB,IAAI;EACJ,OAAO,QAAQ,YAAY,SAAS;EACpC;EACA;EACA;EACA;CACD;CACA,IAAI,cAAc,YAAY,QAAQ,uBAAuB,aAC5D,KAAK,eAAe;CAErB,OAAO;AACR;;;;ACbA,MAAM,0BAA0B;AAEhC,MAAM,mBAAmB,OAAO,SAIO;CACtC,MAAM,OAAO,KAAK,KAAK,kBAAkB,IAAI,oBAAoB;CACjE,KAAK,IAAI,QAAQ,sBAAsB,cAAc;CACrD,IAAI;CACJ,IAAI;EACH,MAAM,MAAM,KAAK,IAAI,QAAQ,KAAK;GACjC,YAAY,KAAK,KAAK;GACtB,OAAO,EAAE,SAAS,EAAE,YAAY,MAAM,EAAE;GACxC,OAAO;GACP,OAAO;GACP,gBAAgB;GAChB,KAAK,KAAK;EACX,CAAC;CACF,UAAU;EACT,KAAK,IAAI,QAAQ,sBAAsB,cAAc;CACtD;CACA,IAAI,IAAI,KAAK,UAAU,yBACtB,KAAK,IAAI,QAAQ,OAAO,KACvB,kDAAkD,wBAAwB,8CAA8C,KAAK,MAAM,EACpI;CAED,MAAM,aAAa,IAAI,KAAK,KAAK,MAChC,kBAAkB,CAA4C,CAC/D;CACA,OAAO,mBAAmB,CAAC,GAAG,MAAM,GAAG,UAAU,GAAG,KAAK,KAAK;AAC/D;AAEA,MAAM,WAAW,OAAO,SAMH;CACpB,MAAM,EAAE,MAAM,WAAW,KAAK,aAAa,QAAQ;CACnD,IAAI,CAAC,KAAK,WAAW,SAAS,SAAS,GACtC;CAED,MAAM,EAAE,YAAY;CACpB,MAAM,QAAQ,QAAQ,KAAK,gBAAgB,SAAS;CACpD,MAAM,gBAAgB,MAAM,iBAAiB;EAAE;EAAM;EAAO;CAAI,CAAC;CACjE,IAAI,CAAC,cAAc,QAClB;CAGD,MAAM,8BAAa,IAAI,KAAK,GAAE,YAAY;CAC1C,KAAK,MAAM,gBAAgB,eAAe;EACzC,MAAM,UAAU,MAAM,QAAQ,OAAO;GACpC,YAAY,KAAK;GACjB,MAAM;IACL,gBAAgB,aAAa;IAC7B,UAAU,aAAa;IACvB;IACA,QAAQ;IACR,SAAS;GACV;GACA,gBAAgB;GAChB;EACD,CAAC;EACD,MAAM,aAAa,OAAO,QAAQ,EAAE;EACpC,MAAM,OAAO,aAAa;GACzB;GACA,YAAY,KAAK;GACjB;GACA;GACA;GACA;GACA,QAAQ,KAAK;GACb;EACD,CAAC;EACD,MAAM,QAAQ,OAAO;GACpB,YAAY,KAAK;GACjB,IAAI;GACJ,MAAM,EAAE,SAAS,KAAK;GACtB,gBAAgB;GAChB;EACD,CAAC;EAED,IAAI,KAAK,SAAS,SAAS;GAC1B,MAAM,QAAQ,KAAK,MAAM;IACxB,MAAM;IACN,OAAO,EAAE,WAAW;IACpB,OAAO,KAAK;GACb,CAAC;GACD;EACD;EAGA,IAAI;GACH,MAAM,SAAS,MAAM,aAAa;IACjC;IACA;IACA;IACA,MAAM,KAAK,UAAU,IAAI;IACzB,WAAW,KAAK;IAChB,KAAK,KAAK,IAAI;GACf,CAAC;GACD,MAAM,QAAQ,OAAO;IACpB,YAAY,KAAK;IACjB,IAAI;IACJ,MAAM;KACL,QAAQ,OAAO,KAAK,YAAY;KAChC,SAAS;KACT,gBAAgB,OAAO;KACvB,cAAc,OAAO;KACrB,OAAO,OAAO;KACd,YAAY,OAAO;IACpB;IACA,gBAAgB;IAChB;GACD,CAAC;EACF,SAAS,KAAK;GACb,QAAQ,OAAO,MACd,wCAAwC,WAAW,UAAU,eAAe,QAAQ,IAAI,UAAU,OAAO,GAAG,GAC7G;EACD;CACD;AACD;;AAGA,MAAa,mBACX,SACD,OAAO,EAAE,KAAK,aAAa,WAAW,UAAU;CAE/C,MAAM,SAAS;EAAE;EAAM,WADM,cAAc,WAAW,WAAW;EAC3B;EAAK;EAAa;CAAI,CAAC;CAC7D,OAAO;AACR;;AAGD,MAAa,mBACX,SACD,OAAO,EAAE,KAAK,UAAU;CACvB,MAAM,SAAS;EAAE;EAAM,WAAW;EAAe;EAAgC;CAAI,CAAC;CACtF,OAAO;AACR;;;ACrHD,MAAa,0BACZ,aAC6B;CAC7B,MAAM,OACL,aAAa,KAAA,IAAY,CAAC,IAAI,OAAO,aAAa,WAAW,EAAE,MAAM,SAAS,IAAI;CACnF,OAAO;EACN,MAAM,KAAK,QAAQ;EACnB,WAAW,KAAK,aAAA;EAChB,SAAS,KAAK,WAAA;EACd,OAAO,KAAK,SAAA;CACb;AACD;;;;AC3DA,MAAa,eAAe,SAKF;CACzB,MAAM,eAAe,KAAK,cAAc,KAAK;CAC7C,IAAI,KAAK,eAAe,SAAS;EAChC,IAAI,CAAC,cACJ,KAAK,KACJ,4LACD;EAED,OAAO;CACR;CACA,IAAI,KAAK,eAAe,UACvB,OAAO;CAER,OAAO,eAAe,UAAU;AACjC;;;;ACLA,MAAa,oBAAoB,SAIrB;CACX,MAAM,EAAE,QAAQ,YAAY;CAC5B,MAAM,UAAU,QAAQ,eAAe,CAAC;CACxC,MAAM,oBAAoB,QAAQ,yBAAyB,QAAA;CAC3D,MAAM,iBAAiB,QAAQ,eAAe,QAAA;CAC9C,MAAM,WAAW,IAAI,IAAY;EAAC,GAAG;EAAgB;EAAmB;CAAc,CAAC;CACvF,MAAM,cAAc,OAAO,KAAK,OAAO,EAAE,QAAQ,MAAM,CAAC,SAAS,IAAI,CAAC,CAAC;CACvE,MAAM,WAAW,uBAAuB,QAAQ,QAAQ;CACxD,MAAM,oBAAoB,QAAQ,iBAAiB,CAAC;CACpD,MAAM,UAAU,aAAa,OAAO,YAAY,YAAY,KAAK,MAAM,CAAC,GAAG,QAAQ,MAAM,IAAI,CAAC,CAAC,CAAC;CAEhG,MAAM,OAAO,YAAY;EACxB,YAAY,SAAS;EACrB,YAAY,QAAQ,OAAO,MAAM,OAAO;EACxC,eAAe,KAAK;EAEpB,OAAO,MAAM,QAAQ,KAAK,CAAC;CAC5B,CAAC;CAED,OAAO,gBAAgB,CAAC;CACxB,OAAO,YAAY,KAClB,6BAA6B;EAC5B,MAAM;EACN,QAAQ;EACR,QAAQ,QAAQ,yBAAyB,UAAU;CACpD,CAAC,CACF;CACA,MAAM,aAAa,0BAA0B;EAC5C,MAAM;EACN,QAAQ,QAAQ,eAAe,UAAU;CAC1C,CAAC;CAED,MAAM,oBAA8B;EACnC,MAAM;EACN,QAAQ;EACR,SAAS,OAAO,QAAQ;GAEvB,IAAI,CAAC,IAAI,MACR,OAAO,SAAS,KAAK,EAAE,OAAO,eAAe,GAAG,EAAE,QAAQ,IAAI,CAAC;GAEhE,MAAM,KAAK,IAAI,aAAa;GAC5B,IAAI,OAAO,OAAO,UACjB,OAAO,SAAS,KAAK,EAAE,OAAO,aAAa,GAAG,EAAE,QAAQ,IAAI,CAAC;GAE9D,MAAM,SAAS,MAAM,kBAAkB;IACtC,MAAM;KACL;KACA;KACA;KACA;KACA,WAAW,SAAS;KACpB,OAAO,SAAS;IACjB;IACA,YAAY;IACZ,SAAS,IAAI;IACb;GACD,CAAC;GACD,OAAO,SAAS,KAAK,QAAQ,EAAE,QAAQ,IAAI,CAAC;EAC7C;CACD;CACA,WAAW,YAAY,CAAC,GAAI,WAAW,aAAa,CAAC,GAAI,iBAAiB;CAC1E,OAAO,YAAY,KAAK,UAAU;CAElC,OAAO,SAAS,CAAC;CACjB,OAAO,KAAK,UAAU,CAAC;CACvB,OAAO,KAAK,MAAM,KACjB,iBAAiB;EAChB;EACA;EACA;EACA,WAAW,SAAS;EACpB,SAAS,SAAS;CACnB,CAAC,CACF;CAEA,KAAK,IAAI,IAAI,GAAG,IAAI,OAAO,YAAY,QAAQ,KAAK;EACnD,MAAM,aAAa,OAAO,YAAY;EACtC,IAAI,CAAC,cAAc,CAAC,YAAY,SAAS,WAAW,IAAI,GACvD;EAED,MAAM,MAAM,QAAQ,WAAW;EAC/B,MAAM,mBAA4C,QAAQ,QAAQ,QAAQ,KAAA,IAAY,CAAC,IAAI;EAC3F,MAAM,OAAO;GACZ,gBAAgB,WAAW;GAC3B,QAAQ;GACR,YAAY,iBAAiB,cAAc;IAC1C;IACA;IACA;GACD;GACA;GACA;GACA;GACA;GACA,WAAW,SAAS;GACpB,OAAO,SAAS;EACjB;EACA,OAAO,YAAY,KAAK;GACvB,GAAG;GACH,OAAO;IACN,GAAG,WAAW;IACd,aAAa,CAAC,GAAI,WAAW,OAAO,eAAe,CAAC,GAAI,gBAAgB,IAAI,CAAC;IAC7E,aAAa,CAAC,GAAI,WAAW,OAAO,eAAe,CAAC,GAAI,gBAAgB,IAAI,CAAC;GAC9E;EACD;CACD;AACD;;;;AChHA,MAAa,uBAAuB;;;;;;;AAQpC,MAAa,WAAW,aAAoC;CAC3D,MAAM;CACN,OAAO;CACP,SAAS,EAAE,QAAQ,SAAS,GAAG,cAAsB;EACpD,IAAI,QAAQ,aAAa,MACxB,OAAO;EAER,qBAAqB,MAAM;EAE3B,MAAM,oBAAoB,QAAQ;EAClC,IAAI,mBAAmB,SAAS;GAC/B,MAAM,OAAO,kBAAkB;GAC/B,KAAK,WAAW,CAAC,GAAI,KAAK,YAAY,CAAC,GAAI,oBAAoB;EAChE;EAEA,iBAAiB;GAAE;GAAQ;GAAS,eAAe,QAAQ,QAAQ,kBAAkB;EAAE,CAAC;EAExF,OAAO;CACR;AACD,CAAC"}
|
|
@@ -0,0 +1,31 @@
|
|
|
1
|
+
//#region src/translations/keys.ts
|
|
2
|
+
/**
|
|
3
|
+
* Typed translation keys. Lookups must go through these constants, not string
|
|
4
|
+
* literals. Every key here must have a value in every locale (`en.ts`), or it is
|
|
5
|
+
* a type error.
|
|
6
|
+
*/
|
|
7
|
+
const keys = {
|
|
8
|
+
pluginName: "webhooks:pluginName",
|
|
9
|
+
subscriptionSingular: "webhooks:subscriptionSingular",
|
|
10
|
+
subscriptionPlural: "webhooks:subscriptionPlural",
|
|
11
|
+
deliverySingular: "webhooks:deliverySingular",
|
|
12
|
+
deliveryPlural: "webhooks:deliveryPlural",
|
|
13
|
+
fieldName: "webhooks:fieldName",
|
|
14
|
+
fieldUrl: "webhooks:fieldUrl",
|
|
15
|
+
fieldEnabled: "webhooks:fieldEnabled",
|
|
16
|
+
fieldEvents: "webhooks:fieldEvents",
|
|
17
|
+
fieldSecret: "webhooks:fieldSecret",
|
|
18
|
+
fieldSecretHelp: "webhooks:fieldSecretHelp",
|
|
19
|
+
fieldHeaders: "webhooks:fieldHeaders",
|
|
20
|
+
fieldDescription: "webhooks:fieldDescription",
|
|
21
|
+
statusPending: "webhooks:statusPending",
|
|
22
|
+
statusSuccess: "webhooks:statusSuccess",
|
|
23
|
+
statusFailed: "webhooks:statusFailed",
|
|
24
|
+
statusDead: "webhooks:statusDead",
|
|
25
|
+
redeliver: "webhooks:redeliver",
|
|
26
|
+
redeliverDone: "webhooks:redeliverDone"
|
|
27
|
+
};
|
|
28
|
+
//#endregion
|
|
29
|
+
export { keys as t };
|
|
30
|
+
|
|
31
|
+
//# sourceMappingURL=keys-BdNiD3rC.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"keys-BdNiD3rC.js","names":[],"sources":["../src/translations/keys.ts"],"sourcesContent":["/**\n * Typed translation keys. Lookups must go through these constants, not string\n * literals. Every key here must have a value in every locale (`en.ts`), or it is\n * a type error.\n */\nexport const keys = {\n\tpluginName: 'webhooks:pluginName',\n\tsubscriptionSingular: 'webhooks:subscriptionSingular',\n\tsubscriptionPlural: 'webhooks:subscriptionPlural',\n\tdeliverySingular: 'webhooks:deliverySingular',\n\tdeliveryPlural: 'webhooks:deliveryPlural',\n\tfieldName: 'webhooks:fieldName',\n\tfieldUrl: 'webhooks:fieldUrl',\n\tfieldEnabled: 'webhooks:fieldEnabled',\n\tfieldEvents: 'webhooks:fieldEvents',\n\tfieldSecret: 'webhooks:fieldSecret',\n\tfieldSecretHelp: 'webhooks:fieldSecretHelp',\n\tfieldHeaders: 'webhooks:fieldHeaders',\n\tfieldDescription: 'webhooks:fieldDescription',\n\tstatusPending: 'webhooks:statusPending',\n\tstatusSuccess: 'webhooks:statusSuccess',\n\tstatusFailed: 'webhooks:statusFailed',\n\tstatusDead: 'webhooks:statusDead',\n\tredeliver: 'webhooks:redeliver',\n\tredeliverDone: 'webhooks:redeliverDone',\n} as const\n\nexport type TranslationKey = (typeof keys)[keyof typeof keys]\n"],"mappings":";;;;;;AAKA,MAAa,OAAO;CACnB,YAAY;CACZ,sBAAsB;CACtB,oBAAoB;CACpB,kBAAkB;CAClB,gBAAgB;CAChB,WAAW;CACX,UAAU;CACV,cAAc;CACd,aAAa;CACb,aAAa;CACb,iBAAiB;CACjB,cAAc;CACd,kBAAkB;CAClB,eAAe;CACf,eAAe;CACf,cAAc;CACd,YAAY;CACZ,WAAW;CACX,eAAe;AAChB"}
|
|
@@ -0,0 +1,51 @@
|
|
|
1
|
+
import { t as keys } from "./keys-BdNiD3rC.js";
|
|
2
|
+
//#region src/translations/en.ts
|
|
3
|
+
/**
|
|
4
|
+
* English values, keyed by the typed constants in `keys.ts`. The
|
|
5
|
+
* `Record<TranslationKey, string>` annotation makes a missing or unknown key a
|
|
6
|
+
* type error. `translations/index.ts` nests these for Payload.
|
|
7
|
+
*/
|
|
8
|
+
const en = {
|
|
9
|
+
[keys.pluginName]: "Webhooks",
|
|
10
|
+
[keys.subscriptionSingular]: "Subscription",
|
|
11
|
+
[keys.subscriptionPlural]: "Subscriptions",
|
|
12
|
+
[keys.deliverySingular]: "Delivery",
|
|
13
|
+
[keys.deliveryPlural]: "Deliveries",
|
|
14
|
+
[keys.fieldName]: "Name",
|
|
15
|
+
[keys.fieldUrl]: "Endpoint URL",
|
|
16
|
+
[keys.fieldEnabled]: "Enabled",
|
|
17
|
+
[keys.fieldEvents]: "Events",
|
|
18
|
+
[keys.fieldSecret]: "Signing secret",
|
|
19
|
+
[keys.fieldSecretHelp]: "Shown in full once on create, masked afterward. Used to sign deliveries; copy it to the receiver now.",
|
|
20
|
+
[keys.fieldHeaders]: "Custom headers",
|
|
21
|
+
[keys.fieldDescription]: "Description",
|
|
22
|
+
[keys.statusPending]: "Pending",
|
|
23
|
+
[keys.statusSuccess]: "Delivered",
|
|
24
|
+
[keys.statusFailed]: "Failed",
|
|
25
|
+
[keys.statusDead]: "Dead",
|
|
26
|
+
[keys.redeliver]: "Redeliver",
|
|
27
|
+
[keys.redeliverDone]: "Redelivery queued"
|
|
28
|
+
};
|
|
29
|
+
//#endregion
|
|
30
|
+
//#region src/translations/index.ts
|
|
31
|
+
/**
|
|
32
|
+
* Flat `webhooks:foo` entries to the nested `{ webhooks: { foo } }`
|
|
33
|
+
* shape Payload resolves `t('webhooks:foo')` against (it splits on `:`).
|
|
34
|
+
*/
|
|
35
|
+
const toNested = (flat) => {
|
|
36
|
+
const out = {};
|
|
37
|
+
for (const [fullKey, value] of Object.entries(flat)) {
|
|
38
|
+
const separator = fullKey.indexOf(":");
|
|
39
|
+
const namespace = fullKey.slice(0, separator);
|
|
40
|
+
const bucket = out[namespace] ?? {};
|
|
41
|
+
bucket[fullKey.slice(separator + 1)] = value;
|
|
42
|
+
out[namespace] = bucket;
|
|
43
|
+
}
|
|
44
|
+
return out;
|
|
45
|
+
};
|
|
46
|
+
/** Per-locale messages merged into `config.i18n.translations`. English only for now. */
|
|
47
|
+
const translations = { en: toNested(en) };
|
|
48
|
+
//#endregion
|
|
49
|
+
export { translations as t };
|
|
50
|
+
|
|
51
|
+
//# sourceMappingURL=translations-CnkwrCJ3.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"translations-CnkwrCJ3.js","names":[],"sources":["../src/translations/en.ts","../src/translations/index.ts"],"sourcesContent":["import { keys, type TranslationKey } from './keys'\n\n/**\n * English values, keyed by the typed constants in `keys.ts`. The\n * `Record<TranslationKey, string>` annotation makes a missing or unknown key a\n * type error. `translations/index.ts` nests these for Payload.\n */\nexport const en: Record<TranslationKey, string> = {\n\t[keys.pluginName]: 'Webhooks',\n\t[keys.subscriptionSingular]: 'Subscription',\n\t[keys.subscriptionPlural]: 'Subscriptions',\n\t[keys.deliverySingular]: 'Delivery',\n\t[keys.deliveryPlural]: 'Deliveries',\n\t[keys.fieldName]: 'Name',\n\t[keys.fieldUrl]: 'Endpoint URL',\n\t[keys.fieldEnabled]: 'Enabled',\n\t[keys.fieldEvents]: 'Events',\n\t[keys.fieldSecret]: 'Signing secret',\n\t[keys.fieldSecretHelp]:\n\t\t'Shown in full once on create, masked afterward. Used to sign deliveries; copy it to the receiver now.',\n\t[keys.fieldHeaders]: 'Custom headers',\n\t[keys.fieldDescription]: 'Description',\n\t[keys.statusPending]: 'Pending',\n\t[keys.statusSuccess]: 'Delivered',\n\t[keys.statusFailed]: 'Failed',\n\t[keys.statusDead]: 'Dead',\n\t[keys.redeliver]: 'Redeliver',\n\t[keys.redeliverDone]: 'Redelivery queued',\n}\n","import { en } from './en'\n\nexport type { TranslationKey } from './keys'\nexport { keys } from './keys'\n\n/**\n * Flat `webhooks:foo` entries to the nested `{ webhooks: { foo } }`\n * shape Payload resolves `t('webhooks:foo')` against (it splits on `:`).\n */\nconst toNested = (flat: Record<string, string>): Record<string, Record<string, string>> => {\n\tconst out: Record<string, Record<string, string>> = {}\n\tfor (const [fullKey, value] of Object.entries(flat)) {\n\t\tconst separator = fullKey.indexOf(':')\n\t\tconst namespace = fullKey.slice(0, separator)\n\t\tconst bucket = out[namespace] ?? {}\n\t\tbucket[fullKey.slice(separator + 1)] = value\n\t\tout[namespace] = bucket\n\t}\n\treturn out\n}\n\n/** Per-locale messages merged into `config.i18n.translations`. English only for now. */\nexport const translations = {\n\ten: toNested(en),\n}\n"],"mappings":";;;;;;;AAOA,MAAa,KAAqC;EAChD,KAAK,aAAa;EAClB,KAAK,uBAAuB;EAC5B,KAAK,qBAAqB;EAC1B,KAAK,mBAAmB;EACxB,KAAK,iBAAiB;EACtB,KAAK,YAAY;EACjB,KAAK,WAAW;EAChB,KAAK,eAAe;EACpB,KAAK,cAAc;EACnB,KAAK,cAAc;EACnB,KAAK,kBACL;EACA,KAAK,eAAe;EACpB,KAAK,mBAAmB;EACxB,KAAK,gBAAgB;EACrB,KAAK,gBAAgB;EACrB,KAAK,eAAe;EACpB,KAAK,aAAa;EAClB,KAAK,YAAY;EACjB,KAAK,gBAAgB;AACvB;;;;;;;ACnBA,MAAM,YAAY,SAAyE;CAC1F,MAAM,MAA8C,CAAC;CACrD,KAAK,MAAM,CAAC,SAAS,UAAU,OAAO,QAAQ,IAAI,GAAG;EACpD,MAAM,YAAY,QAAQ,QAAQ,GAAG;EACrC,MAAM,YAAY,QAAQ,MAAM,GAAG,SAAS;EAC5C,MAAM,SAAS,IAAI,cAAc,CAAC;EAClC,OAAO,QAAQ,MAAM,YAAY,CAAC,KAAK;EACvC,IAAI,aAAa;CAClB;CACA,OAAO;AACR;;AAGA,MAAa,eAAe,EAC3B,IAAI,SAAS,EAAE,EAChB"}
|
package/package.json
ADDED
|
@@ -0,0 +1,105 @@
|
|
|
1
|
+
{
|
|
2
|
+
"name": "@10x-media/webhooks",
|
|
3
|
+
"version": "0.1.0-beta.0",
|
|
4
|
+
"description": "Outbound webhook subscriptions for Payload v3",
|
|
5
|
+
"license": "MIT",
|
|
6
|
+
"repository": {
|
|
7
|
+
"type": "git",
|
|
8
|
+
"url": "https://github.com/10x-media/payload-plugins.git",
|
|
9
|
+
"directory": "packages/webhooks"
|
|
10
|
+
},
|
|
11
|
+
"homepage": "https://github.com/10x-media/payload-plugins/tree/main/packages/webhooks",
|
|
12
|
+
"bugs": "https://github.com/10x-media/payload-plugins/issues",
|
|
13
|
+
"keywords": [
|
|
14
|
+
"payload",
|
|
15
|
+
"payloadcms",
|
|
16
|
+
"plugin"
|
|
17
|
+
],
|
|
18
|
+
"author": "10x-media",
|
|
19
|
+
"engines": {
|
|
20
|
+
"node": ">=22.18.0"
|
|
21
|
+
},
|
|
22
|
+
"type": "module",
|
|
23
|
+
"sideEffects": false,
|
|
24
|
+
"exports": {
|
|
25
|
+
".": {
|
|
26
|
+
"types": "./dist/index.d.ts",
|
|
27
|
+
"import": "./dist/index.js",
|
|
28
|
+
"default": "./dist/index.js"
|
|
29
|
+
},
|
|
30
|
+
"./types": {
|
|
31
|
+
"types": "./dist/exports/types.d.ts",
|
|
32
|
+
"import": "./dist/exports/types.js",
|
|
33
|
+
"default": "./dist/exports/types.js"
|
|
34
|
+
},
|
|
35
|
+
"./client": {
|
|
36
|
+
"types": "./dist/exports/client.d.ts",
|
|
37
|
+
"import": "./dist/exports/client.js",
|
|
38
|
+
"default": "./dist/exports/client.js"
|
|
39
|
+
},
|
|
40
|
+
"./i18n": {
|
|
41
|
+
"types": "./dist/exports/i18n.d.ts",
|
|
42
|
+
"import": "./dist/exports/i18n.js",
|
|
43
|
+
"default": "./dist/exports/i18n.js"
|
|
44
|
+
}
|
|
45
|
+
},
|
|
46
|
+
"files": [
|
|
47
|
+
"dist",
|
|
48
|
+
"README.md",
|
|
49
|
+
"CHANGELOG.md",
|
|
50
|
+
"LICENSE"
|
|
51
|
+
],
|
|
52
|
+
"peerDependencies": {
|
|
53
|
+
"@payloadcms/ui": "^3.82.0",
|
|
54
|
+
"payload": "^3.82.0",
|
|
55
|
+
"react": "^19.0.0",
|
|
56
|
+
"react-dom": "^19.0.0"
|
|
57
|
+
},
|
|
58
|
+
"devDependencies": {
|
|
59
|
+
"@payloadcms/db-mongodb": "3.85.0",
|
|
60
|
+
"@payloadcms/db-postgres": "3.85.0",
|
|
61
|
+
"@payloadcms/ui": "3.85.0",
|
|
62
|
+
"@playwright/test": "1.60.0",
|
|
63
|
+
"@types/node": "22.19.19",
|
|
64
|
+
"@types/react": "19.2.15",
|
|
65
|
+
"@types/react-dom": "19.2.3",
|
|
66
|
+
"payload": "3.85.0",
|
|
67
|
+
"playwright": "1.60.0",
|
|
68
|
+
"react": "19.2.6",
|
|
69
|
+
"react-dom": "19.2.6",
|
|
70
|
+
"tsdown": "0.22.1",
|
|
71
|
+
"typescript": "5.9.3",
|
|
72
|
+
"vitest": "4.1.7",
|
|
73
|
+
"@10x-media/vitest-config": "0.0.0",
|
|
74
|
+
"@10x-media/payload-test-harness": "0.0.0",
|
|
75
|
+
"@10x-media/tsconfig": "0.0.0"
|
|
76
|
+
},
|
|
77
|
+
"publishConfig": {
|
|
78
|
+
"access": "public"
|
|
79
|
+
},
|
|
80
|
+
"scripts": {
|
|
81
|
+
"build": "tsdown",
|
|
82
|
+
"lint": "biome check src tests dev",
|
|
83
|
+
"lint:fix": "biome check --write src tests dev",
|
|
84
|
+
"typecheck": "tsc -p tsconfig.json --noEmit",
|
|
85
|
+
"test": "vitest run",
|
|
86
|
+
"test:unit": "vitest run src",
|
|
87
|
+
"test:int": "vitest run tests/int",
|
|
88
|
+
"test:matrix": "DB_MATRIX=mongo vitest run tests/int/matrix.int.spec.ts && DB_MATRIX=postgres vitest run tests/int/matrix.int.spec.ts",
|
|
89
|
+
"test:container": "TEST_DB=container DB_MATRIX=mongo vitest run tests/int/matrix.int.spec.ts && TEST_DB=container DB_MATRIX=postgres vitest run tests/int/matrix.int.spec.ts",
|
|
90
|
+
"test:e2e": "bash scripts/e2e.sh",
|
|
91
|
+
"dev": "pnpm --filter @10x-media/webhooks-dev dev",
|
|
92
|
+
"start": "pnpm --filter @10x-media/webhooks-dev start",
|
|
93
|
+
"generate": "pnpm --filter @10x-media/webhooks-dev generate",
|
|
94
|
+
"generate:types": "pnpm --filter @10x-media/webhooks-dev generate:types",
|
|
95
|
+
"generate:importmap": "pnpm --filter @10x-media/webhooks-dev generate:importmap",
|
|
96
|
+
"migrate": "pnpm --filter @10x-media/webhooks-dev migrate",
|
|
97
|
+
"migrate:create": "pnpm --filter @10x-media/webhooks-dev migrate:create",
|
|
98
|
+
"migrate:down": "pnpm --filter @10x-media/webhooks-dev migrate:down",
|
|
99
|
+
"migrate:refresh": "pnpm --filter @10x-media/webhooks-dev migrate:refresh",
|
|
100
|
+
"migrate:reset": "pnpm --filter @10x-media/webhooks-dev migrate:reset",
|
|
101
|
+
"migrate:status": "pnpm --filter @10x-media/webhooks-dev migrate:status",
|
|
102
|
+
"migrate:fresh": "pnpm --filter @10x-media/webhooks-dev migrate:fresh",
|
|
103
|
+
"clean": "rm -rf dist *.tsbuildinfo dev/.next"
|
|
104
|
+
}
|
|
105
|
+
}
|