@10x-media/webhooks 0.1.0-beta.0 → 0.1.0-beta.2

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.
Files changed (67) hide show
  1. package/CHANGELOG.md +38 -0
  2. package/README.md +21 -149
  3. package/dist/collections/deliveries.js +99 -0
  4. package/dist/collections/deliveries.js.map +1 -0
  5. package/dist/collections/subscriptions.js +123 -0
  6. package/dist/collections/subscriptions.js.map +1 -0
  7. package/dist/constants.js +29 -0
  8. package/dist/constants.js.map +1 -0
  9. package/dist/delivery/DeliveryStatusCell.d.ts +10 -0
  10. package/dist/delivery/DeliveryStatusCell.js +42 -0
  11. package/dist/delivery/DeliveryStatusCell.js.map +1 -0
  12. package/dist/delivery/RedeliverButton.d.ts +6 -0
  13. package/dist/delivery/RedeliverButton.js +43 -0
  14. package/dist/delivery/RedeliverButton.js.map +1 -0
  15. package/dist/delivery/buildPayload.js +32 -0
  16. package/dist/delivery/buildPayload.js.map +1 -0
  17. package/dist/delivery/deliver.js +36 -0
  18. package/dist/delivery/deliver.js.map +1 -0
  19. package/dist/delivery/deliverTask.js +80 -0
  20. package/dist/delivery/deliverTask.js.map +1 -0
  21. package/dist/delivery/deriveDeliveryStatus.js +10 -0
  22. package/dist/delivery/deriveDeliveryStatus.js.map +1 -0
  23. package/dist/delivery/redeliver.js +83 -0
  24. package/dist/delivery/redeliver.js.map +1 -0
  25. package/dist/delivery/sendDelivery.js +32 -0
  26. package/dist/delivery/sendDelivery.js.map +1 -0
  27. package/dist/delivery/sign.js +10 -0
  28. package/dist/delivery/sign.js.map +1 -0
  29. package/dist/events/eventTypes.js +27 -0
  30. package/dist/events/eventTypes.js.map +1 -0
  31. package/dist/events/hooks.js +132 -0
  32. package/dist/events/hooks.js.map +1 -0
  33. package/dist/exports/client.d.ts +3 -14
  34. package/dist/exports/client.js +2 -81
  35. package/dist/exports/i18n.d.ts +3 -37
  36. package/dist/exports/i18n.js +2 -2
  37. package/dist/exports/types.d.ts +1 -1
  38. package/dist/index.d.ts +20 -2
  39. package/dist/index.js +3 -817
  40. package/dist/index.js.map +1 -1
  41. package/dist/{index-CpsTtHO1.d.ts → options.d.ts} +17 -18
  42. package/dist/options.js +15 -0
  43. package/dist/options.js.map +1 -0
  44. package/dist/plugin/registerTranslations.js +19 -0
  45. package/dist/plugin/registerTranslations.js.map +1 -0
  46. package/dist/plugin/registerWebhooks.js +109 -0
  47. package/dist/plugin/registerWebhooks.js.map +1 -0
  48. package/dist/plugin/resolveMode.js +15 -0
  49. package/dist/plugin/resolveMode.js.map +1 -0
  50. package/dist/plugin/resolveSubscriptions.js +53 -0
  51. package/dist/plugin/resolveSubscriptions.js.map +1 -0
  52. package/dist/{translations-CnkwrCJ3.js → translations/en.js} +3 -22
  53. package/dist/translations/en.js.map +1 -0
  54. package/dist/translations/index.d.ts +14 -0
  55. package/dist/translations/index.js +26 -0
  56. package/dist/translations/index.js.map +1 -0
  57. package/dist/translations/keys.d.ts +31 -0
  58. package/dist/{keys-BdNiD3rC.js → translations/keys.js} +2 -2
  59. package/dist/translations/keys.js.map +1 -0
  60. package/dist/translations/server.js +13 -0
  61. package/dist/translations/server.js.map +1 -0
  62. package/dist/translations/useTranslation.js +12 -0
  63. package/dist/translations/useTranslation.js.map +1 -0
  64. package/package.json +4 -3
  65. package/dist/exports/client.js.map +0 -1
  66. package/dist/keys-BdNiD3rC.js.map +0 -1
  67. package/dist/translations-CnkwrCJ3.js.map +0 -1
@@ -0,0 +1,32 @@
1
+ import { eventId } from "../events/eventTypes.js";
2
+ //#region src/delivery/buildPayload.ts
3
+ /** Build the outbound body, applying any per-collection transform/redaction. */
4
+ const buildPayload = (args) => {
5
+ const { deliveryId, collection, operation, doc, previousDoc, occurredAt, config, req } = args;
6
+ const data = config?.transform ? config.transform({
7
+ doc,
8
+ previousDoc,
9
+ operation,
10
+ req,
11
+ target: "data"
12
+ }) : doc;
13
+ const body = {
14
+ id: deliveryId,
15
+ event: eventId(collection, operation),
16
+ collection,
17
+ operation,
18
+ occurredAt,
19
+ data
20
+ };
21
+ if (operation === "update" && config?.includePreviousData && previousDoc) body.previousData = config?.transform ? config.transform({
22
+ doc: previousDoc,
23
+ operation,
24
+ req,
25
+ target: "previousData"
26
+ }) : previousDoc;
27
+ return body;
28
+ };
29
+ //#endregion
30
+ export { buildPayload };
31
+
32
+ //# sourceMappingURL=buildPayload.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"buildPayload.js","names":[],"sources":["../../src/delivery/buildPayload.ts"],"sourcesContent":["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\n\t\t? config.transform({ doc, previousDoc, operation, req, target: 'data' })\n\t\t: 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 = config?.transform\n\t\t\t? config.transform({ doc: previousDoc, operation, req, target: 'previousData' })\n\t\t\t: previousDoc\n\t}\n\treturn body\n}\n"],"mappings":";;;AAiBA,MAAa,gBAAgB,SASV;CAClB,MAAM,EAAE,YAAY,YAAY,WAAW,KAAK,aAAa,YAAY,QAAQ,QAAQ;CACzF,MAAM,OAAO,QAAQ,YAClB,OAAO,UAAU;EAAE;EAAK;EAAa;EAAW;EAAK,QAAQ;CAAO,CAAC,IACrE;CACH,MAAM,OAAoB;EACzB,IAAI;EACJ,OAAO,QAAQ,YAAY,SAAS;EACpC;EACA;EACA;EACA;CACD;CACA,IAAI,cAAc,YAAY,QAAQ,uBAAuB,aAC5D,KAAK,eAAe,QAAQ,YACzB,OAAO,UAAU;EAAE,KAAK;EAAa;EAAW;EAAK,QAAQ;CAAe,CAAC,IAC7E;CAEJ,OAAO;AACR"}
@@ -0,0 +1,36 @@
1
+ //#region src/delivery/deliver.ts
2
+ /** Max response-body characters retained for the delivery log. */
3
+ const MAX_RESPONSE_BODY = 2e3;
4
+ /** POST `body` to `url` with a hard timeout; never throws. */
5
+ const deliver = async (args) => {
6
+ const controller = new AbortController();
7
+ const timer = setTimeout(() => controller.abort(), args.timeoutMs);
8
+ const start = Date.now();
9
+ try {
10
+ const res = await fetch(args.url, {
11
+ method: "POST",
12
+ headers: args.headers,
13
+ body: args.body,
14
+ signal: controller.signal
15
+ });
16
+ const text = await res.text();
17
+ return {
18
+ ok: res.ok,
19
+ responseStatus: res.status,
20
+ responseBody: text.slice(0, MAX_RESPONSE_BODY),
21
+ durationMs: Date.now() - start
22
+ };
23
+ } catch (err) {
24
+ return {
25
+ ok: false,
26
+ error: err instanceof Error ? err.message : String(err),
27
+ durationMs: Date.now() - start
28
+ };
29
+ } finally {
30
+ clearTimeout(timer);
31
+ }
32
+ };
33
+ //#endregion
34
+ export { deliver };
35
+
36
+ //# sourceMappingURL=deliver.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"deliver.js","names":[],"sources":["../../src/delivery/deliver.ts"],"sourcesContent":["/** 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"],"mappings":";;AACA,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"}
@@ -0,0 +1,80 @@
1
+ import { WEBHOOK_DELIVER_TASK } from "../constants.js";
2
+ import { resolveSubscriptionById } from "../plugin/resolveSubscriptions.js";
3
+ import { deriveDeliveryStatus } from "./deriveDeliveryStatus.js";
4
+ import { sendDelivery } from "./sendDelivery.js";
5
+ //#region src/delivery/deliverTask.ts
6
+ /** Native Payload jobs task that performs one queued delivery attempt. */
7
+ const buildDeliverTask = (deps) => ({
8
+ slug: WEBHOOK_DELIVER_TASK,
9
+ retries: deps.retries,
10
+ inputSchema: [{
11
+ name: "deliveryId",
12
+ type: "text",
13
+ required: true
14
+ }],
15
+ handler: async ({ input, job, req }) => {
16
+ const { payload } = req;
17
+ const deliveryId = input.deliveryId;
18
+ const delivery = await payload.findByID({
19
+ collection: deps.deliveriesSlug,
20
+ id: deliveryId,
21
+ overrideAccess: true,
22
+ req
23
+ });
24
+ const subscription = await resolveSubscriptionById({
25
+ id: String(delivery.subscriptionId),
26
+ codeSubscriptions: deps.codeSubscriptions,
27
+ subscriptionsSlug: deps.subscriptionsSlug,
28
+ payload,
29
+ req
30
+ });
31
+ if (!subscription?.enabled) {
32
+ await payload.update({
33
+ collection: deps.deliveriesSlug,
34
+ id: deliveryId,
35
+ data: {
36
+ status: "dead",
37
+ error: subscription ? "subscription disabled" : "subscription not found"
38
+ },
39
+ overrideAccess: true,
40
+ req
41
+ });
42
+ return { output: {} };
43
+ }
44
+ const attempt = Number(job.totalTried ?? 0) + 1;
45
+ const result = await sendDelivery({
46
+ subscription,
47
+ deliveryId,
48
+ event: String(delivery.event),
49
+ body: JSON.stringify(delivery.payload),
50
+ timeoutMs: deps.timeoutMs,
51
+ now: Date.now()
52
+ });
53
+ const status = deriveDeliveryStatus({
54
+ ok: result.ok,
55
+ attempt,
56
+ maxRetries: deps.retries
57
+ });
58
+ await payload.update({
59
+ collection: deps.deliveriesSlug,
60
+ id: deliveryId,
61
+ data: {
62
+ status,
63
+ attempt,
64
+ responseStatus: result.responseStatus,
65
+ responseBody: result.responseBody,
66
+ error: result.error,
67
+ durationMs: result.durationMs,
68
+ jobId: String(job.id)
69
+ },
70
+ overrideAccess: true,
71
+ req
72
+ });
73
+ if (!result.ok) throw new Error(`Webhook delivery failed: ${result.error ?? result.responseStatus}`);
74
+ return { output: {} };
75
+ }
76
+ });
77
+ //#endregion
78
+ export { buildDeliverTask };
79
+
80
+ //# sourceMappingURL=deliverTask.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"deliverTask.js","names":[],"sources":["../../src/delivery/deliverTask.ts"],"sourcesContent":["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?.enabled) {\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: {\n\t\t\t\t\t\tstatus: 'dead',\n\t\t\t\t\t\terror: subscription ? 'subscription disabled' : 'subscription not found',\n\t\t\t\t\t},\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"],"mappings":";;;;;;AAkBA,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,SAAS;GAC3B,MAAM,QAAQ,OAAO;IACpB,YAAY,KAAK;IACjB,IAAI;IACJ,MAAM;KACL,QAAQ;KACR,OAAO,eAAe,0BAA0B;IACjD;IACA,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"}
@@ -0,0 +1,10 @@
1
+ //#region src/delivery/deriveDeliveryStatus.ts
2
+ /** Delivery status from the attempt outcome. `attempt` is 1-based; total tries = maxRetries + 1. */
3
+ const deriveDeliveryStatus = (args) => {
4
+ if (args.ok) return "success";
5
+ return args.attempt > args.maxRetries ? "dead" : "failed";
6
+ };
7
+ //#endregion
8
+ export { deriveDeliveryStatus };
9
+
10
+ //# sourceMappingURL=deriveDeliveryStatus.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"deriveDeliveryStatus.js","names":[],"sources":["../../src/delivery/deriveDeliveryStatus.ts"],"sourcesContent":["/** 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"],"mappings":";;AACA,MAAa,wBAAwB,SAIA;CACpC,IAAI,KAAK,IACR,OAAO;CAER,OAAO,KAAK,UAAU,KAAK,aAAa,SAAS;AAClD"}
@@ -0,0 +1,83 @@
1
+ import { WEBHOOK_DELIVER_TASK } from "../constants.js";
2
+ import { resolveSubscriptionById } from "../plugin/resolveSubscriptions.js";
3
+ import { sendDelivery } from "./sendDelivery.js";
4
+ //#region src/delivery/redeliver.ts
5
+ /** Re-dispatch a past delivery from its stored payload, creating a new linked row. */
6
+ const redeliverDelivery = async (args) => {
7
+ const { deps, deliveryId, payload, req } = args;
8
+ const original = await payload.findByID({
9
+ collection: deps.deliveriesSlug,
10
+ id: deliveryId,
11
+ overrideAccess: true,
12
+ req
13
+ });
14
+ const subscription = await resolveSubscriptionById({
15
+ id: String(original.subscriptionId),
16
+ codeSubscriptions: deps.codeSubscriptions,
17
+ subscriptionsSlug: deps.subscriptionsSlug,
18
+ payload,
19
+ req
20
+ });
21
+ const created = await payload.create({
22
+ collection: deps.deliveriesSlug,
23
+ data: {
24
+ subscriptionId: original.subscriptionId,
25
+ endpoint: subscription?.url ?? original.endpoint,
26
+ event: original.event,
27
+ payload: original.payload,
28
+ status: "pending",
29
+ attempt: 0
30
+ },
31
+ overrideAccess: true,
32
+ req
33
+ });
34
+ const newId = String(created.id);
35
+ if (deps.mode === "queue") {
36
+ await payload.jobs.queue({
37
+ task: WEBHOOK_DELIVER_TASK,
38
+ input: { deliveryId: newId },
39
+ queue: deps.queue
40
+ });
41
+ return { id: newId };
42
+ }
43
+ if (!subscription?.enabled) {
44
+ await payload.update({
45
+ collection: deps.deliveriesSlug,
46
+ id: newId,
47
+ data: {
48
+ status: "dead",
49
+ error: subscription ? "subscription disabled" : "subscription not found"
50
+ },
51
+ overrideAccess: true,
52
+ req
53
+ });
54
+ return { id: newId };
55
+ }
56
+ const result = await sendDelivery({
57
+ subscription,
58
+ deliveryId: newId,
59
+ event: String(original.event),
60
+ body: JSON.stringify(original.payload),
61
+ timeoutMs: deps.timeoutMs,
62
+ now: Date.now()
63
+ });
64
+ await payload.update({
65
+ collection: deps.deliveriesSlug,
66
+ id: newId,
67
+ data: {
68
+ status: result.ok ? "success" : "dead",
69
+ attempt: 1,
70
+ responseStatus: result.responseStatus,
71
+ responseBody: result.responseBody,
72
+ error: result.error,
73
+ durationMs: result.durationMs
74
+ },
75
+ overrideAccess: true,
76
+ req
77
+ });
78
+ return { id: newId };
79
+ };
80
+ //#endregion
81
+ export { redeliverDelivery };
82
+
83
+ //# sourceMappingURL=redeliver.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"redeliver.js","names":[],"sources":["../../src/delivery/redeliver.ts"],"sourcesContent":["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\t// Resolved up front (cheap) so the new row's endpoint reflects the subscription's current URL\n\t// rather than whatever was stored at the time of the original delivery.\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\tconst created = await payload.create({\n\t\tcollection: deps.deliveriesSlug,\n\t\tdata: {\n\t\t\tsubscriptionId: original.subscriptionId,\n\t\t\tendpoint: subscription?.url ?? 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\t// deliverTask re-resolves the subscription (and re-checks enabled) when it runs, so no\n\t\t// missing/disabled gate is needed here.\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\tif (!subscription?.enabled) {\n\t\tawait payload.update({\n\t\t\tcollection: deps.deliveriesSlug,\n\t\t\tid: newId,\n\t\t\tdata: {\n\t\t\t\tstatus: 'dead',\n\t\t\t\terror: subscription ? 'subscription disabled' : 'subscription not found',\n\t\t\t},\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"],"mappings":";;;;;AAkBA,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;CAGD,MAAM,eAAe,MAAM,wBAAwB;EAClD,IAAI,OAAO,SAAS,cAAc;EAClC,mBAAmB,KAAK;EACxB,mBAAmB,KAAK;EACxB;EACA;CACD,CAAC;CACD,MAAM,UAAU,MAAM,QAAQ,OAAO;EACpC,YAAY,KAAK;EACjB,MAAM;GACL,gBAAgB,SAAS;GACzB,UAAU,cAAc,OAAO,SAAS;GACxC,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;EAG1B,MAAM,QAAQ,KAAK,MAAM;GACxB,MAAM;GACN,OAAO,EAAE,YAAY,MAAM;GAC3B,OAAO,KAAK;EACb,CAAC;EACD,OAAO,EAAE,IAAI,MAAM;CACpB;CAEA,IAAI,CAAC,cAAc,SAAS;EAC3B,MAAM,QAAQ,OAAO;GACpB,YAAY,KAAK;GACjB,IAAI;GACJ,MAAM;IACL,QAAQ;IACR,OAAO,eAAe,0BAA0B;GACjD;GACA,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"}
@@ -0,0 +1,32 @@
1
+ import { deliver } from "./deliver.js";
2
+ import { signPayload, signatureHeader } from "./sign.js";
3
+ //#region src/delivery/sendDelivery.ts
4
+ const USER_AGENT = "10x-media-webhooks";
5
+ /** Assemble headers (+ signature) and POST the body to the subscription's URL. */
6
+ const sendDelivery = (args) => {
7
+ const { subscription, deliveryId, event, body, timeoutMs, now } = args;
8
+ const timestamp = Math.floor(now / 1e3);
9
+ const headers = {
10
+ "Content-Type": "application/json",
11
+ "User-Agent": USER_AGENT,
12
+ "X-Webhook-Id": deliveryId,
13
+ "X-Webhook-Event": event,
14
+ "X-Webhook-Timestamp": String(timestamp),
15
+ ...subscription.headers
16
+ };
17
+ if (subscription.secret) headers["X-Webhook-Signature"] = signatureHeader(signPayload({
18
+ secret: subscription.secret,
19
+ timestamp,
20
+ body
21
+ }));
22
+ return deliver({
23
+ url: subscription.url,
24
+ body,
25
+ headers,
26
+ timeoutMs
27
+ });
28
+ };
29
+ //#endregion
30
+ export { sendDelivery };
31
+
32
+ //# sourceMappingURL=sendDelivery.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"sendDelivery.js","names":[],"sources":["../../src/delivery/sendDelivery.ts"],"sourcesContent":["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"],"mappings":";;;AAIA,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"}
@@ -0,0 +1,10 @@
1
+ import { createHmac } from "node:crypto";
2
+ //#region src/delivery/sign.ts
3
+ /** HMAC-SHA256 over `${timestamp}.${body}`, hex-encoded. */
4
+ const signPayload = (args) => createHmac("sha256", args.secret).update(`${args.timestamp}.${args.body}`).digest("hex");
5
+ /** Wrap a signature hex in the versioned header value. */
6
+ const signatureHeader = (hex) => `v1=${hex}`;
7
+ //#endregion
8
+ export { signPayload, signatureHeader };
9
+
10
+ //# sourceMappingURL=sign.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"sign.js","names":[],"sources":["../../src/delivery/sign.ts"],"sourcesContent":["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"],"mappings":";;;AAGA,MAAa,eAAe,SAC3B,WAAW,UAAU,KAAK,MAAM,EAAE,OAAO,GAAG,KAAK,UAAU,GAAG,KAAK,MAAM,EAAE,OAAO,KAAK;;AAGxF,MAAa,mBAAmB,QAAwB,MAAM"}
@@ -0,0 +1,27 @@
1
+ //#region src/events/eventTypes.ts
2
+ const OP_TO_EVENT = {
3
+ create: "created",
4
+ update: "updated",
5
+ delete: "deleted"
6
+ };
7
+ /** Past-tense public event suffix for a Payload operation. */
8
+ const operationToEvent = (op) => OP_TO_EVENT[op];
9
+ /** Public event id, e.g. `posts.created`. */
10
+ const eventId = (collectionSlug, op) => `${collectionSlug}.${operationToEvent(op)}`;
11
+ /** Every event id the given opt-in collections can emit, in declaration order. */
12
+ const eventCatalog = (collections) => {
13
+ const out = [];
14
+ for (const [slug, cfg] of Object.entries(collections)) {
15
+ const ops = (cfg === true ? void 0 : cfg.operations) ?? [
16
+ "create",
17
+ "update",
18
+ "delete"
19
+ ];
20
+ for (const op of ops) out.push(eventId(slug, op));
21
+ }
22
+ return out;
23
+ };
24
+ //#endregion
25
+ export { eventCatalog, eventId };
26
+
27
+ //# sourceMappingURL=eventTypes.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"eventTypes.js","names":[],"sources":["../../src/events/eventTypes.ts"],"sourcesContent":["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"],"mappings":";AAEA,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"}
@@ -0,0 +1,132 @@
1
+ import { SECRET_REVEAL_CONTEXT, WEBHOOK_DELIVER_TASK } from "../constants.js";
2
+ import { fromCodeSubscription, fromCollectionRow, matchSubscriptions } from "../plugin/resolveSubscriptions.js";
3
+ import { sendDelivery } from "../delivery/sendDelivery.js";
4
+ import { eventId } from "./eventTypes.js";
5
+ import { buildPayload } from "../delivery/buildPayload.js";
6
+ //#region src/events/hooks.ts
7
+ /** Max collection subscriptions scanned per event (pagination is a future enhancement). */
8
+ const SUBSCRIPTION_SCAN_LIMIT = 1e3;
9
+ const resolveListening = async (args) => {
10
+ const code = args.deps.codeSubscriptions.map(fromCodeSubscription);
11
+ args.req.context[SECRET_REVEAL_CONTEXT.forSigning] = true;
12
+ let res;
13
+ try {
14
+ res = await args.req.payload.find({
15
+ collection: args.deps.subscriptionsSlug,
16
+ where: { enabled: { not_equals: false } },
17
+ limit: SUBSCRIPTION_SCAN_LIMIT,
18
+ depth: 0,
19
+ overrideAccess: true,
20
+ req: args.req
21
+ });
22
+ } finally {
23
+ args.req.context[SECRET_REVEAL_CONTEXT.forSigning] = false;
24
+ }
25
+ if (res.docs.length >= SUBSCRIPTION_SCAN_LIMIT) args.req.payload.logger.warn(`@10x-media/webhooks: subscription scan hit the ${SUBSCRIPTION_SCAN_LIMIT} cap; some subscriptions may be skipped for ${args.event}.`);
26
+ const collection = res.docs.map((d) => fromCollectionRow(d));
27
+ return matchSubscriptions([...code, ...collection], args.event);
28
+ };
29
+ const dispatch = async (args) => {
30
+ const { deps, operation, doc, previousDoc, req } = args;
31
+ if (!deps.operations.includes(operation)) return;
32
+ const { payload } = req;
33
+ const event = eventId(deps.collectionSlug, operation);
34
+ const subscriptions = await resolveListening({
35
+ deps,
36
+ event,
37
+ req
38
+ });
39
+ if (!subscriptions.length) return;
40
+ const occurredAt = (/* @__PURE__ */ new Date()).toISOString();
41
+ for (const subscription of subscriptions) {
42
+ const created = await payload.create({
43
+ collection: deps.deliveriesSlug,
44
+ data: {
45
+ subscriptionId: subscription.id,
46
+ endpoint: subscription.url,
47
+ event,
48
+ status: "pending",
49
+ attempt: 0
50
+ },
51
+ overrideAccess: true,
52
+ req
53
+ });
54
+ const deliveryId = String(created.id);
55
+ const body = buildPayload({
56
+ deliveryId,
57
+ collection: deps.collectionSlug,
58
+ operation,
59
+ doc,
60
+ previousDoc,
61
+ occurredAt,
62
+ config: deps.config,
63
+ req
64
+ });
65
+ await payload.update({
66
+ collection: deps.deliveriesSlug,
67
+ id: deliveryId,
68
+ data: { payload: body },
69
+ overrideAccess: true,
70
+ req
71
+ });
72
+ if (deps.mode === "queue") {
73
+ await payload.jobs.queue({
74
+ task: WEBHOOK_DELIVER_TASK,
75
+ input: { deliveryId },
76
+ queue: deps.queue
77
+ });
78
+ continue;
79
+ }
80
+ try {
81
+ const result = await sendDelivery({
82
+ subscription,
83
+ deliveryId,
84
+ event,
85
+ body: JSON.stringify(body),
86
+ timeoutMs: deps.timeoutMs,
87
+ now: Date.now()
88
+ });
89
+ await payload.update({
90
+ collection: deps.deliveriesSlug,
91
+ id: deliveryId,
92
+ data: {
93
+ status: result.ok ? "success" : "dead",
94
+ attempt: 1,
95
+ responseStatus: result.responseStatus,
96
+ responseBody: result.responseBody,
97
+ error: result.error,
98
+ durationMs: result.durationMs
99
+ },
100
+ overrideAccess: true,
101
+ req
102
+ });
103
+ } catch (err) {
104
+ payload.logger.error(`@10x-media/webhooks: inline delivery ${deliveryId} threw: ${err instanceof Error ? err.message : String(err)}`);
105
+ }
106
+ }
107
+ };
108
+ /** afterChange hook factory for an opt-in source collection. */
109
+ const makeAfterChange = (deps) => async ({ doc, previousDoc, operation, req }) => {
110
+ await dispatch({
111
+ deps,
112
+ operation: operation === "create" ? "create" : "update",
113
+ doc,
114
+ previousDoc,
115
+ req
116
+ });
117
+ return doc;
118
+ };
119
+ /** afterDelete hook factory for an opt-in source collection. */
120
+ const makeAfterDelete = (deps) => async ({ doc, req }) => {
121
+ await dispatch({
122
+ deps,
123
+ operation: "delete",
124
+ doc,
125
+ req
126
+ });
127
+ return doc;
128
+ };
129
+ //#endregion
130
+ export { makeAfterChange, makeAfterDelete };
131
+
132
+ //# sourceMappingURL=hooks.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"hooks.js","names":[],"sources":["../../src/events/hooks.ts"],"sourcesContent":["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"],"mappings":"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"}
@@ -1,14 +1,3 @@
1
- import { DefaultCellComponentProps } from "payload";
2
-
3
- //#region src/delivery/DeliveryStatusCell.d.ts
4
- /** List cell rendering a delivery's status as a native Payload Pill. */
5
- declare const DeliveryStatusCell: ({
6
- cellData
7
- }: DefaultCellComponentProps) => import("react/jsx-runtime").JSX.Element;
8
- //#endregion
9
- //#region src/delivery/RedeliverButton.d.ts
10
- /** Doc-view action that POSTs to the deliveries redeliver endpoint. */
11
- declare const RedeliverButton: () => import("react/jsx-runtime").JSX.Element | null;
12
- //#endregion
13
- export { DeliveryStatusCell, RedeliverButton };
14
- //# sourceMappingURL=client.d.ts.map
1
+ import { DeliveryStatusCell } from "../delivery/DeliveryStatusCell.js";
2
+ import { RedeliverButton } from "../delivery/RedeliverButton.js";
3
+ export { DeliveryStatusCell, RedeliverButton };
@@ -1,83 +1,4 @@
1
1
  "use client";
2
- import { t as keys } from "../keys-BdNiD3rC.js";
3
- import { Button, Pill, toast, useConfig, useDocumentInfo, useTranslation } from "@payloadcms/ui";
4
- import { jsx } from "react/jsx-runtime";
5
- import { useState } from "react";
6
- //#region src/translations/useTranslation.ts
7
- /**
8
- * `useTranslation` bound to this plugin's keys, so `t(keys.X)` typechecks without
9
- * a per-call `@ts-expect-error`. Returns Payload's `{ t, i18n }` unchanged.
10
- */
11
- const useTranslation$1 = () => useTranslation();
12
- //#endregion
13
- //#region src/delivery/DeliveryStatusCell.tsx
14
- const META = {
15
- pending: {
16
- labelKey: keys.statusPending,
17
- pillStyle: "light"
18
- },
19
- success: {
20
- labelKey: keys.statusSuccess,
21
- pillStyle: "success"
22
- },
23
- failed: {
24
- labelKey: keys.statusFailed,
25
- pillStyle: "warning"
26
- },
27
- dead: {
28
- labelKey: keys.statusDead,
29
- pillStyle: "error"
30
- }
31
- };
32
- const FALLBACK = {
33
- labelKey: keys.statusPending,
34
- pillStyle: "light"
35
- };
36
- /** List cell rendering a delivery's status as a native Payload Pill. */
37
- const DeliveryStatusCell = ({ cellData }) => {
38
- const { t } = useTranslation$1();
39
- const meta = META[String(cellData)] ?? FALLBACK;
40
- return /* @__PURE__ */ jsx(Pill, {
41
- pillStyle: meta.pillStyle,
42
- size: "small",
43
- children: t(meta.labelKey)
44
- });
45
- };
46
- //#endregion
47
- //#region src/delivery/RedeliverButton.tsx
48
- /** Doc-view action that POSTs to the deliveries redeliver endpoint. */
49
- const RedeliverButton = () => {
50
- const { id, collectionSlug } = useDocumentInfo();
51
- const { config } = useConfig();
52
- const { t } = useTranslation$1();
53
- const [busy, setBusy] = useState(false);
54
- if (!id || !collectionSlug) return null;
55
- const apiRoute = config.routes?.api ?? "/api";
56
- const serverURL = config.serverURL ?? "";
57
- const onClick = async () => {
58
- setBusy(true);
59
- try {
60
- const res = await fetch(`${serverURL}${apiRoute}/${collectionSlug}/${encodeURIComponent(String(id))}/redeliver`, {
61
- method: "POST",
62
- credentials: "include"
63
- });
64
- if (!res.ok) throw new Error(String(res.status));
65
- toast.success(t(keys.redeliverDone));
66
- } catch {
67
- toast.error(t(keys.redeliver));
68
- } finally {
69
- setBusy(false);
70
- }
71
- };
72
- return /* @__PURE__ */ jsx(Button, {
73
- buttonStyle: "secondary",
74
- disabled: busy,
75
- onClick,
76
- size: "small",
77
- children: t(keys.redeliver)
78
- });
79
- };
80
- //#endregion
2
+ import { DeliveryStatusCell } from "../delivery/DeliveryStatusCell.js";
3
+ import { RedeliverButton } from "../delivery/RedeliverButton.js";
81
4
  export { DeliveryStatusCell, RedeliverButton };
82
-
83
- //# sourceMappingURL=client.js.map