@mobilizehub/payload-plugin 0.4.0 → 0.5.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.
@@ -17,7 +17,7 @@ export const generateEmailsCollection = (emailsConfig)=>{
17
17
  position: 'sidebar',
18
18
  readOnly: true
19
19
  },
20
- defaultValue: 'draft',
20
+ defaultValue: 'queued',
21
21
  options: [
22
22
  {
23
23
  label: 'Queued',
@@ -1 +1 @@
1
- {"version":3,"sources":["../../../src/collections/emails/generateEmailsCollection.ts"],"sourcesContent":["import type { CollectionConfig, Field } from 'payload'\n\nimport type { MobilizehubPluginConfig } from '../../types/index.js'\n\nimport { authenticated } from '../../access/authenticated.js'\nimport { syncStatusFromActivityBeforeChangeHook } from './hooks/sync-status-from-activity.js'\n\nexport const generateEmailsCollection = (emailsConfig: MobilizehubPluginConfig) => {\n const defaultFields: Field[] = [\n {\n name: 'providerId',\n type: 'text',\n admin: {\n position: 'sidebar',\n readOnly: true,\n },\n },\n {\n name: 'status',\n type: 'select',\n admin: {\n position: 'sidebar',\n readOnly: true,\n },\n defaultValue: 'draft',\n options: [\n {\n label: 'Queued',\n value: 'queued',\n },\n {\n label: 'Failed',\n value: 'failed',\n },\n {\n label: 'Sent',\n value: 'sent',\n },\n {\n label: 'Delivered',\n value: 'delivered',\n },\n {\n label: 'Bounced',\n value: 'bounced',\n },\n {\n label: 'Unsubscribed',\n value: 'unsubscribed',\n },\n {\n label: 'Complained',\n value: 'complained',\n },\n ],\n required: true,\n },\n {\n name: 'broadcast',\n type: 'relationship',\n admin: {\n position: 'sidebar',\n readOnly: true,\n },\n relationTo: emailsConfig.broadcastsOverrides?.slug || 'broadcasts',\n },\n {\n name: 'contact',\n type: 'relationship',\n admin: {\n position: 'sidebar',\n readOnly: true,\n },\n relationTo: emailsConfig.contactsOverrides?.slug || 'contacts',\n },\n\n {\n name: 'activityField',\n type: 'ui',\n admin: {\n components: {\n Field: '@mobilizehub/payload-plugin/rsc#EmailActivityField',\n },\n position: 'sidebar',\n },\n },\n {\n name: 'from',\n type: 'text',\n admin: {\n readOnly: true,\n },\n required: true,\n },\n {\n name: 'activity',\n type: 'array',\n admin: {\n hidden: true,\n readOnly: true,\n },\n fields: [\n {\n name: 'type',\n type: 'select',\n options: [\n { label: 'Sent', value: 'sent' },\n { label: 'Delivered', value: 'delivered' },\n { label: 'Opened', value: 'opened' },\n { label: 'Clicked', value: 'clicked' },\n { label: 'Bounced', value: 'bounced' },\n { label: 'Unsubscribed', value: 'unsubscribed' },\n { label: 'Complained', value: 'complained' },\n ],\n required: true,\n },\n {\n name: 'timestamp',\n type: 'date',\n required: true,\n },\n ],\n label: '',\n },\n {\n name: 'subject',\n type: 'text',\n admin: {\n readOnly: true,\n },\n required: true,\n },\n {\n name: 'to',\n type: 'text',\n admin: {\n readOnly: true,\n },\n required: true,\n },\n {\n name: 'html',\n type: 'text',\n admin: {\n hidden: true,\n readOnly: true,\n },\n required: true,\n },\n {\n name: 'emailPreview',\n type: 'ui',\n admin: {\n components: {\n Field: '@mobilizehub/payload-plugin/client#EmailPreviewField',\n },\n },\n },\n ]\n\n const config: CollectionConfig = {\n ...(emailsConfig.emailsOverrides || {}),\n slug: emailsConfig.emailsOverrides?.slug || 'emails',\n access: {\n create: () => false,\n delete: () => false,\n read: authenticated,\n update: () => false,\n ...(emailsConfig.emailsOverrides?.access || {}),\n },\n admin: {\n ...(emailsConfig.emailsOverrides?.admin || {}),\n hidden: emailsConfig.emailsOverrides?.admin?.hidden || false,\n useAsTitle: emailsConfig.emailsOverrides?.admin?.useAsTitle || 'to',\n },\n fields: emailsConfig.emailsOverrides?.fields\n ? emailsConfig.emailsOverrides.fields({ defaultFields })\n : defaultFields,\n hooks: {\n beforeChange: [\n syncStatusFromActivityBeforeChangeHook,\n ...(emailsConfig.emailsOverrides?.hooks?.beforeChange || []),\n ],\n ...(emailsConfig.emailsOverrides?.hooks || {}),\n },\n }\n\n return config\n}\n"],"names":["authenticated","syncStatusFromActivityBeforeChangeHook","generateEmailsCollection","emailsConfig","defaultFields","name","type","admin","position","readOnly","defaultValue","options","label","value","required","relationTo","broadcastsOverrides","slug","contactsOverrides","components","Field","hidden","fields","config","emailsOverrides","access","create","delete","read","update","useAsTitle","hooks","beforeChange"],"mappings":"AAIA,SAASA,aAAa,QAAQ,gCAA+B;AAC7D,SAASC,sCAAsC,QAAQ,uCAAsC;AAE7F,OAAO,MAAMC,2BAA2B,CAACC;IACvC,MAAMC,gBAAyB;QAC7B;YACEC,MAAM;YACNC,MAAM;YACNC,OAAO;gBACLC,UAAU;gBACVC,UAAU;YACZ;QACF;QACA;YACEJ,MAAM;YACNC,MAAM;YACNC,OAAO;gBACLC,UAAU;gBACVC,UAAU;YACZ;YACAC,cAAc;YACdC,SAAS;gBACP;oBACEC,OAAO;oBACPC,OAAO;gBACT;gBACA;oBACED,OAAO;oBACPC,OAAO;gBACT;gBACA;oBACED,OAAO;oBACPC,OAAO;gBACT;gBACA;oBACED,OAAO;oBACPC,OAAO;gBACT;gBACA;oBACED,OAAO;oBACPC,OAAO;gBACT;gBACA;oBACED,OAAO;oBACPC,OAAO;gBACT;gBACA;oBACED,OAAO;oBACPC,OAAO;gBACT;aACD;YACDC,UAAU;QACZ;QACA;YACET,MAAM;YACNC,MAAM;YACNC,OAAO;gBACLC,UAAU;gBACVC,UAAU;YACZ;YACAM,YAAYZ,aAAaa,mBAAmB,EAAEC,QAAQ;QACxD;QACA;YACEZ,MAAM;YACNC,MAAM;YACNC,OAAO;gBACLC,UAAU;gBACVC,UAAU;YACZ;YACAM,YAAYZ,aAAae,iBAAiB,EAAED,QAAQ;QACtD;QAEA;YACEZ,MAAM;YACNC,MAAM;YACNC,OAAO;gBACLY,YAAY;oBACVC,OAAO;gBACT;gBACAZ,UAAU;YACZ;QACF;QACA;YACEH,MAAM;YACNC,MAAM;YACNC,OAAO;gBACLE,UAAU;YACZ;YACAK,UAAU;QACZ;QACA;YACET,MAAM;YACNC,MAAM;YACNC,OAAO;gBACLc,QAAQ;gBACRZ,UAAU;YACZ;YACAa,QAAQ;gBACN;oBACEjB,MAAM;oBACNC,MAAM;oBACNK,SAAS;wBACP;4BAAEC,OAAO;4BAAQC,OAAO;wBAAO;wBAC/B;4BAAED,OAAO;4BAAaC,OAAO;wBAAY;wBACzC;4BAAED,OAAO;4BAAUC,OAAO;wBAAS;wBACnC;4BAAED,OAAO;4BAAWC,OAAO;wBAAU;wBACrC;4BAAED,OAAO;4BAAWC,OAAO;wBAAU;wBACrC;4BAAED,OAAO;4BAAgBC,OAAO;wBAAe;wBAC/C;4BAAED,OAAO;4BAAcC,OAAO;wBAAa;qBAC5C;oBACDC,UAAU;gBACZ;gBACA;oBACET,MAAM;oBACNC,MAAM;oBACNQ,UAAU;gBACZ;aACD;YACDF,OAAO;QACT;QACA;YACEP,MAAM;YACNC,MAAM;YACNC,OAAO;gBACLE,UAAU;YACZ;YACAK,UAAU;QACZ;QACA;YACET,MAAM;YACNC,MAAM;YACNC,OAAO;gBACLE,UAAU;YACZ;YACAK,UAAU;QACZ;QACA;YACET,MAAM;YACNC,MAAM;YACNC,OAAO;gBACLc,QAAQ;gBACRZ,UAAU;YACZ;YACAK,UAAU;QACZ;QACA;YACET,MAAM;YACNC,MAAM;YACNC,OAAO;gBACLY,YAAY;oBACVC,OAAO;gBACT;YACF;QACF;KACD;IAED,MAAMG,SAA2B;QAC/B,GAAIpB,aAAaqB,eAAe,IAAI,CAAC,CAAC;QACtCP,MAAMd,aAAaqB,eAAe,EAAEP,QAAQ;QAC5CQ,QAAQ;YACNC,QAAQ,IAAM;YACdC,QAAQ,IAAM;YACdC,MAAM5B;YACN6B,QAAQ,IAAM;YACd,GAAI1B,aAAaqB,eAAe,EAAEC,UAAU,CAAC,CAAC;QAChD;QACAlB,OAAO;YACL,GAAIJ,aAAaqB,eAAe,EAAEjB,SAAS,CAAC,CAAC;YAC7Cc,QAAQlB,aAAaqB,eAAe,EAAEjB,OAAOc,UAAU;YACvDS,YAAY3B,aAAaqB,eAAe,EAAEjB,OAAOuB,cAAc;QACjE;QACAR,QAAQnB,aAAaqB,eAAe,EAAEF,SAClCnB,aAAaqB,eAAe,CAACF,MAAM,CAAC;YAAElB;QAAc,KACpDA;QACJ2B,OAAO;YACLC,cAAc;gBACZ/B;mBACIE,aAAaqB,eAAe,EAAEO,OAAOC,gBAAgB,EAAE;aAC5D;YACD,GAAI7B,aAAaqB,eAAe,EAAEO,SAAS,CAAC,CAAC;QAC/C;IACF;IAEA,OAAOR;AACT,EAAC"}
1
+ {"version":3,"sources":["../../../src/collections/emails/generateEmailsCollection.ts"],"sourcesContent":["import type { CollectionConfig, Field } from 'payload'\n\nimport type { MobilizehubPluginConfig } from '../../types/index.js'\n\nimport { authenticated } from '../../access/authenticated.js'\nimport { syncStatusFromActivityBeforeChangeHook } from './hooks/sync-status-from-activity.js'\n\nexport const generateEmailsCollection = (emailsConfig: MobilizehubPluginConfig) => {\n const defaultFields: Field[] = [\n {\n name: 'providerId',\n type: 'text',\n admin: {\n position: 'sidebar',\n readOnly: true,\n },\n },\n {\n name: 'status',\n type: 'select',\n admin: {\n position: 'sidebar',\n readOnly: true,\n },\n defaultValue: 'queued',\n options: [\n {\n label: 'Queued',\n value: 'queued',\n },\n {\n label: 'Failed',\n value: 'failed',\n },\n {\n label: 'Sent',\n value: 'sent',\n },\n {\n label: 'Delivered',\n value: 'delivered',\n },\n {\n label: 'Bounced',\n value: 'bounced',\n },\n {\n label: 'Unsubscribed',\n value: 'unsubscribed',\n },\n {\n label: 'Complained',\n value: 'complained',\n },\n ],\n required: true,\n },\n {\n name: 'broadcast',\n type: 'relationship',\n admin: {\n position: 'sidebar',\n readOnly: true,\n },\n relationTo: emailsConfig.broadcastsOverrides?.slug || 'broadcasts',\n },\n {\n name: 'contact',\n type: 'relationship',\n admin: {\n position: 'sidebar',\n readOnly: true,\n },\n relationTo: emailsConfig.contactsOverrides?.slug || 'contacts',\n },\n\n {\n name: 'activityField',\n type: 'ui',\n admin: {\n components: {\n Field: '@mobilizehub/payload-plugin/rsc#EmailActivityField',\n },\n position: 'sidebar',\n },\n },\n {\n name: 'from',\n type: 'text',\n admin: {\n readOnly: true,\n },\n required: true,\n },\n {\n name: 'activity',\n type: 'array',\n admin: {\n hidden: true,\n readOnly: true,\n },\n fields: [\n {\n name: 'type',\n type: 'select',\n options: [\n { label: 'Sent', value: 'sent' },\n { label: 'Delivered', value: 'delivered' },\n { label: 'Opened', value: 'opened' },\n { label: 'Clicked', value: 'clicked' },\n { label: 'Bounced', value: 'bounced' },\n { label: 'Unsubscribed', value: 'unsubscribed' },\n { label: 'Complained', value: 'complained' },\n ],\n required: true,\n },\n {\n name: 'timestamp',\n type: 'date',\n required: true,\n },\n ],\n label: '',\n },\n {\n name: 'subject',\n type: 'text',\n admin: {\n readOnly: true,\n },\n required: true,\n },\n {\n name: 'to',\n type: 'text',\n admin: {\n readOnly: true,\n },\n required: true,\n },\n {\n name: 'html',\n type: 'text',\n admin: {\n hidden: true,\n readOnly: true,\n },\n required: true,\n },\n {\n name: 'emailPreview',\n type: 'ui',\n admin: {\n components: {\n Field: '@mobilizehub/payload-plugin/client#EmailPreviewField',\n },\n },\n },\n ]\n\n const config: CollectionConfig = {\n ...(emailsConfig.emailsOverrides || {}),\n slug: emailsConfig.emailsOverrides?.slug || 'emails',\n access: {\n create: () => false,\n delete: () => false,\n read: authenticated,\n update: () => false,\n ...(emailsConfig.emailsOverrides?.access || {}),\n },\n admin: {\n ...(emailsConfig.emailsOverrides?.admin || {}),\n hidden: emailsConfig.emailsOverrides?.admin?.hidden || false,\n useAsTitle: emailsConfig.emailsOverrides?.admin?.useAsTitle || 'to',\n },\n fields: emailsConfig.emailsOverrides?.fields\n ? emailsConfig.emailsOverrides.fields({ defaultFields })\n : defaultFields,\n hooks: {\n beforeChange: [\n syncStatusFromActivityBeforeChangeHook,\n ...(emailsConfig.emailsOverrides?.hooks?.beforeChange || []),\n ],\n ...(emailsConfig.emailsOverrides?.hooks || {}),\n },\n }\n\n return config\n}\n"],"names":["authenticated","syncStatusFromActivityBeforeChangeHook","generateEmailsCollection","emailsConfig","defaultFields","name","type","admin","position","readOnly","defaultValue","options","label","value","required","relationTo","broadcastsOverrides","slug","contactsOverrides","components","Field","hidden","fields","config","emailsOverrides","access","create","delete","read","update","useAsTitle","hooks","beforeChange"],"mappings":"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"}
@@ -0,0 +1,9 @@
1
+ import type { PayloadHandler } from 'payload';
2
+ import type { MobilizehubPluginConfig } from '../types/index.js';
3
+ /**
4
+ * Creates the email webhook endpoint handler.
5
+ *
6
+ * Receives webhook events from email providers (e.g., Resend) and delegates
7
+ * processing to the configured email adapter's webhookHandler.
8
+ */
9
+ export declare const emailWebhookHandler: (config: Pick<MobilizehubPluginConfig, "email">) => PayloadHandler;
@@ -0,0 +1,37 @@
1
+ // src/endpoints/emailWebhookHandler.ts
2
+ import { ErrorCodes, errorResponse, successResponse } from '../utils/api-response.js';
3
+ /**
4
+ * Creates the email webhook endpoint handler.
5
+ *
6
+ * Receives webhook events from email providers (e.g., Resend) and delegates
7
+ * processing to the configured email adapter's webhookHandler.
8
+ */ export const emailWebhookHandler = (config)=>{
9
+ return async (req)=>{
10
+ const { payload } = req;
11
+ const logger = payload.logger;
12
+ try {
13
+ const adapter = config.email({
14
+ payload
15
+ });
16
+ if (!adapter.webhookHandler) {
17
+ logger.warn('Email webhook received but no webhookHandler configured');
18
+ return errorResponse(ErrorCodes.BAD_REQUEST, 'Webhook handler not configured for this email adapter', 501);
19
+ }
20
+ const result = await adapter.webhookHandler(req);
21
+ logger.info('Email webhook processed successfully');
22
+ return successResponse(result?.body ?? {
23
+ received: true
24
+ }, result?.status ?? 200);
25
+ } catch (error) {
26
+ logger.error(error, 'Email webhook processing failed');
27
+ // Return 200 to prevent retry storms from providers
28
+ // Log the error but acknowledge receipt
29
+ return successResponse({
30
+ processed: false,
31
+ received: true
32
+ }, 200);
33
+ }
34
+ };
35
+ };
36
+
37
+ //# sourceMappingURL=emailWebhookHandler.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"sources":["../../src/endpoints/emailWebhookHandler.ts"],"sourcesContent":["// src/endpoints/emailWebhookHandler.ts\nimport type { PayloadHandler } from 'payload'\n\nimport type { MobilizehubPluginConfig } from '../types/index.js'\n\nimport { ErrorCodes, errorResponse, successResponse } from '../utils/api-response.js'\n\n/**\n * Creates the email webhook endpoint handler.\n *\n * Receives webhook events from email providers (e.g., Resend) and delegates\n * processing to the configured email adapter's webhookHandler.\n */\nexport const emailWebhookHandler = (\n config: Pick<MobilizehubPluginConfig, 'email'>,\n): PayloadHandler => {\n return async (req) => {\n const { payload } = req\n const logger = payload.logger\n\n try {\n const adapter = config.email({ payload })\n\n if (!adapter.webhookHandler) {\n logger.warn('Email webhook received but no webhookHandler configured')\n return errorResponse(\n ErrorCodes.BAD_REQUEST,\n 'Webhook handler not configured for this email adapter',\n 501,\n )\n }\n\n const result = await adapter.webhookHandler(req)\n\n logger.info('Email webhook processed successfully')\n\n return successResponse(result?.body ?? { received: true }, result?.status ?? 200)\n } catch (error) {\n logger.error(error as Error, 'Email webhook processing failed')\n\n // Return 200 to prevent retry storms from providers\n // Log the error but acknowledge receipt\n return successResponse({ processed: false, received: true }, 200)\n }\n }\n}\n"],"names":["ErrorCodes","errorResponse","successResponse","emailWebhookHandler","config","req","payload","logger","adapter","email","webhookHandler","warn","BAD_REQUEST","result","info","body","received","status","error","processed"],"mappings":"AAAA,uCAAuC;AAKvC,SAASA,UAAU,EAAEC,aAAa,EAAEC,eAAe,QAAQ,2BAA0B;AAErF;;;;;CAKC,GACD,OAAO,MAAMC,sBAAsB,CACjCC;IAEA,OAAO,OAAOC;QACZ,MAAM,EAAEC,OAAO,EAAE,GAAGD;QACpB,MAAME,SAASD,QAAQC,MAAM;QAE7B,IAAI;YACF,MAAMC,UAAUJ,OAAOK,KAAK,CAAC;gBAAEH;YAAQ;YAEvC,IAAI,CAACE,QAAQE,cAAc,EAAE;gBAC3BH,OAAOI,IAAI,CAAC;gBACZ,OAAOV,cACLD,WAAWY,WAAW,EACtB,yDACA;YAEJ;YAEA,MAAMC,SAAS,MAAML,QAAQE,cAAc,CAACL;YAE5CE,OAAOO,IAAI,CAAC;YAEZ,OAAOZ,gBAAgBW,QAAQE,QAAQ;gBAAEC,UAAU;YAAK,GAAGH,QAAQI,UAAU;QAC/E,EAAE,OAAOC,OAAO;YACdX,OAAOW,KAAK,CAACA,OAAgB;YAE7B,oDAAoD;YACpD,wCAAwC;YACxC,OAAOhB,gBAAgB;gBAAEiB,WAAW;gBAAOH,UAAU;YAAK,GAAG;QAC/D;IACF;AACF,EAAC"}
package/dist/index.js CHANGED
@@ -6,6 +6,7 @@ import { generateFormsCollection } from './collections/forms/generateFormsCollec
6
6
  import { generatePagesCollection } from './collections/pages/generatePagesCollection.js';
7
7
  import { generateTagsCollection } from './collections/tags/generateTagsCollection.js';
8
8
  import { generateUnsubscribeTokensCollection } from './collections/unsubscribe-tokens/generateUnsubscribeTokens.js';
9
+ import { emailWebhookHandler } from './endpoints/emailWebhookHandler.js';
9
10
  import { sendBroadcastHandler } from './endpoints/sendBroadcastHandler.js';
10
11
  import { sendTestEmailHandler } from './endpoints/sendTestBroadcastHandler.js';
11
12
  import { unsubscribeHandler } from './endpoints/unsubscribeHandler.js';
@@ -38,6 +39,11 @@ export const mobilizehubPlugin = (pluginOptions)=>(config)=>{
38
39
  handler: unsubscribeHandler(),
39
40
  method: 'post',
40
41
  path: '/unsubscribe'
42
+ },
43
+ {
44
+ handler: emailWebhookHandler(pluginOptions),
45
+ method: 'post',
46
+ path: '/webhooks/email'
41
47
  }
42
48
  ];
43
49
  config.endpoints = [
package/dist/index.js.map CHANGED
@@ -1 +1 @@
1
- {"version":3,"sources":["../src/index.ts"],"sourcesContent":["import type { Config, Endpoint, TaskConfig } from 'payload'\n\nimport type { MobilizehubPluginConfig } from './types/index.js'\n\nimport { generateBroadcastsCollection } from './collections/broadcasts/generateBroadcastsCollection.js'\nimport { generateContactsCollection } from './collections/contacts/generateContactsCollection.js'\nimport { generateEmailsCollection } from './collections/emails/generateEmailsCollection.js'\nimport { generateFormSubmissionsCollection } from './collections/form-submissions/generateFormSubmissionsCollection.js'\nimport { generateFormsCollection } from './collections/forms/generateFormsCollection.js'\nimport { generatePagesCollection } from './collections/pages/generatePagesCollection.js'\nimport { generateTagsCollection } from './collections/tags/generateTagsCollection.js'\nimport { generateUnsubscribeTokensCollection } from './collections/unsubscribe-tokens/generateUnsubscribeTokens.js'\nimport { sendBroadcastHandler } from './endpoints/sendBroadcastHandler.js'\nimport { sendTestEmailHandler } from './endpoints/sendTestBroadcastHandler.js'\nimport { unsubscribeHandler } from './endpoints/unsubscribeHandler.js'\nimport { createSendBroadcastsTask } from './tasks/sendBroadcastsTask.js'\nimport { createSendEmailTask } from './tasks/sendEmailTask.js'\n\nexport * from './types/index.js'\n\nexport const mobilizehubPlugin =\n (pluginOptions: MobilizehubPluginConfig) =>\n (config: Config): Config => {\n if (!config.collections) {\n config.collections = []\n }\n\n config.collections.push(\n generateTagsCollection(pluginOptions),\n generateContactsCollection(pluginOptions),\n generateBroadcastsCollection(pluginOptions),\n generateEmailsCollection(pluginOptions),\n generatePagesCollection(pluginOptions),\n generateUnsubscribeTokensCollection(),\n generateFormSubmissionsCollection(pluginOptions),\n generateFormsCollection(pluginOptions),\n )\n\n if (pluginOptions.disabled) {\n return config\n }\n\n if (!config.endpoints) {\n config.endpoints = []\n }\n\n const endpoints: Endpoint[] = [\n {\n handler: sendBroadcastHandler(),\n method: 'post',\n path: '/send-broadcast',\n },\n {\n handler: sendTestEmailHandler(pluginOptions),\n method: 'post',\n path: '/send-test-email',\n },\n {\n handler: unsubscribeHandler(),\n method: 'post',\n path: '/unsubscribe',\n },\n ]\n\n config.endpoints = [...config.endpoints, ...endpoints]\n\n if (!config.jobs) {\n config.jobs = {\n tasks: [],\n }\n }\n\n const tasks: TaskConfig[] = [\n createSendBroadcastsTask(pluginOptions),\n createSendEmailTask(pluginOptions),\n ]\n\n config.jobs.tasks = [...(config.jobs.tasks ?? []), ...tasks]\n\n const incomingOnInit = config.onInit\n\n config.onInit = async (payload) => {\n if (incomingOnInit) {\n await incomingOnInit(payload)\n }\n }\n\n return config\n }\n"],"names":["generateBroadcastsCollection","generateContactsCollection","generateEmailsCollection","generateFormSubmissionsCollection","generateFormsCollection","generatePagesCollection","generateTagsCollection","generateUnsubscribeTokensCollection","sendBroadcastHandler","sendTestEmailHandler","unsubscribeHandler","createSendBroadcastsTask","createSendEmailTask","mobilizehubPlugin","pluginOptions","config","collections","push","disabled","endpoints","handler","method","path","jobs","tasks","incomingOnInit","onInit","payload"],"mappings":"AAIA,SAASA,4BAA4B,QAAQ,2DAA0D;AACvG,SAASC,0BAA0B,QAAQ,uDAAsD;AACjG,SAASC,wBAAwB,QAAQ,mDAAkD;AAC3F,SAASC,iCAAiC,QAAQ,sEAAqE;AACvH,SAASC,uBAAuB,QAAQ,iDAAgD;AACxF,SAASC,uBAAuB,QAAQ,iDAAgD;AACxF,SAASC,sBAAsB,QAAQ,+CAA8C;AACrF,SAASC,mCAAmC,QAAQ,gEAA+D;AACnH,SAASC,oBAAoB,QAAQ,sCAAqC;AAC1E,SAASC,oBAAoB,QAAQ,0CAAyC;AAC9E,SAASC,kBAAkB,QAAQ,oCAAmC;AACtE,SAASC,wBAAwB,QAAQ,gCAA+B;AACxE,SAASC,mBAAmB,QAAQ,2BAA0B;AAE9D,cAAc,mBAAkB;AAEhC,OAAO,MAAMC,oBACX,CAACC,gBACD,CAACC;QACC,IAAI,CAACA,OAAOC,WAAW,EAAE;YACvBD,OAAOC,WAAW,GAAG,EAAE;QACzB;QAEAD,OAAOC,WAAW,CAACC,IAAI,CACrBX,uBAAuBQ,gBACvBb,2BAA2Ba,gBAC3Bd,6BAA6Bc,gBAC7BZ,yBAAyBY,gBACzBT,wBAAwBS,gBACxBP,uCACAJ,kCAAkCW,gBAClCV,wBAAwBU;QAG1B,IAAIA,cAAcI,QAAQ,EAAE;YAC1B,OAAOH;QACT;QAEA,IAAI,CAACA,OAAOI,SAAS,EAAE;YACrBJ,OAAOI,SAAS,GAAG,EAAE;QACvB;QAEA,MAAMA,YAAwB;YAC5B;gBACEC,SAASZ;gBACTa,QAAQ;gBACRC,MAAM;YACR;YACA;gBACEF,SAASX,qBAAqBK;gBAC9BO,QAAQ;gBACRC,MAAM;YACR;YACA;gBACEF,SAASV;gBACTW,QAAQ;gBACRC,MAAM;YACR;SACD;QAEDP,OAAOI,SAAS,GAAG;eAAIJ,OAAOI,SAAS;eAAKA;SAAU;QAEtD,IAAI,CAACJ,OAAOQ,IAAI,EAAE;YAChBR,OAAOQ,IAAI,GAAG;gBACZC,OAAO,EAAE;YACX;QACF;QAEA,MAAMA,QAAsB;YAC1Bb,yBAAyBG;YACzBF,oBAAoBE;SACrB;QAEDC,OAAOQ,IAAI,CAACC,KAAK,GAAG;eAAKT,OAAOQ,IAAI,CAACC,KAAK,IAAI,EAAE;eAAMA;SAAM;QAE5D,MAAMC,iBAAiBV,OAAOW,MAAM;QAEpCX,OAAOW,MAAM,GAAG,OAAOC;YACrB,IAAIF,gBAAgB;gBAClB,MAAMA,eAAeE;YACvB;QACF;QAEA,OAAOZ;IACT,EAAC"}
1
+ {"version":3,"sources":["../src/index.ts"],"sourcesContent":["import type { Config, Endpoint, TaskConfig } from 'payload'\n\nimport type { MobilizehubPluginConfig } from './types/index.js'\n\nimport { generateBroadcastsCollection } from './collections/broadcasts/generateBroadcastsCollection.js'\nimport { generateContactsCollection } from './collections/contacts/generateContactsCollection.js'\nimport { generateEmailsCollection } from './collections/emails/generateEmailsCollection.js'\nimport { generateFormSubmissionsCollection } from './collections/form-submissions/generateFormSubmissionsCollection.js'\nimport { generateFormsCollection } from './collections/forms/generateFormsCollection.js'\nimport { generatePagesCollection } from './collections/pages/generatePagesCollection.js'\nimport { generateTagsCollection } from './collections/tags/generateTagsCollection.js'\nimport { generateUnsubscribeTokensCollection } from './collections/unsubscribe-tokens/generateUnsubscribeTokens.js'\nimport { emailWebhookHandler } from './endpoints/emailWebhookHandler.js'\nimport { sendBroadcastHandler } from './endpoints/sendBroadcastHandler.js'\nimport { sendTestEmailHandler } from './endpoints/sendTestBroadcastHandler.js'\nimport { unsubscribeHandler } from './endpoints/unsubscribeHandler.js'\nimport { createSendBroadcastsTask } from './tasks/sendBroadcastsTask.js'\nimport { createSendEmailTask } from './tasks/sendEmailTask.js'\n\nexport * from './types/index.js'\n\nexport const mobilizehubPlugin =\n (pluginOptions: MobilizehubPluginConfig) =>\n (config: Config): Config => {\n if (!config.collections) {\n config.collections = []\n }\n\n config.collections.push(\n generateTagsCollection(pluginOptions),\n generateContactsCollection(pluginOptions),\n generateBroadcastsCollection(pluginOptions),\n generateEmailsCollection(pluginOptions),\n generatePagesCollection(pluginOptions),\n generateUnsubscribeTokensCollection(),\n generateFormSubmissionsCollection(pluginOptions),\n generateFormsCollection(pluginOptions),\n )\n\n if (pluginOptions.disabled) {\n return config\n }\n\n if (!config.endpoints) {\n config.endpoints = []\n }\n\n const endpoints: Endpoint[] = [\n {\n handler: sendBroadcastHandler(),\n method: 'post',\n path: '/send-broadcast',\n },\n {\n handler: sendTestEmailHandler(pluginOptions),\n method: 'post',\n path: '/send-test-email',\n },\n {\n handler: unsubscribeHandler(),\n method: 'post',\n path: '/unsubscribe',\n },\n {\n handler: emailWebhookHandler(pluginOptions),\n method: 'post',\n path: '/webhooks/email',\n },\n ]\n\n config.endpoints = [...config.endpoints, ...endpoints]\n\n if (!config.jobs) {\n config.jobs = {\n tasks: [],\n }\n }\n\n const tasks: TaskConfig[] = [\n createSendBroadcastsTask(pluginOptions),\n createSendEmailTask(pluginOptions),\n ]\n\n config.jobs.tasks = [...(config.jobs.tasks ?? []), ...tasks]\n\n const incomingOnInit = config.onInit\n\n config.onInit = async (payload) => {\n if (incomingOnInit) {\n await incomingOnInit(payload)\n }\n }\n\n return config\n }\n"],"names":["generateBroadcastsCollection","generateContactsCollection","generateEmailsCollection","generateFormSubmissionsCollection","generateFormsCollection","generatePagesCollection","generateTagsCollection","generateUnsubscribeTokensCollection","emailWebhookHandler","sendBroadcastHandler","sendTestEmailHandler","unsubscribeHandler","createSendBroadcastsTask","createSendEmailTask","mobilizehubPlugin","pluginOptions","config","collections","push","disabled","endpoints","handler","method","path","jobs","tasks","incomingOnInit","onInit","payload"],"mappings":"AAIA,SAASA,4BAA4B,QAAQ,2DAA0D;AACvG,SAASC,0BAA0B,QAAQ,uDAAsD;AACjG,SAASC,wBAAwB,QAAQ,mDAAkD;AAC3F,SAASC,iCAAiC,QAAQ,sEAAqE;AACvH,SAASC,uBAAuB,QAAQ,iDAAgD;AACxF,SAASC,uBAAuB,QAAQ,iDAAgD;AACxF,SAASC,sBAAsB,QAAQ,+CAA8C;AACrF,SAASC,mCAAmC,QAAQ,gEAA+D;AACnH,SAASC,mBAAmB,QAAQ,qCAAoC;AACxE,SAASC,oBAAoB,QAAQ,sCAAqC;AAC1E,SAASC,oBAAoB,QAAQ,0CAAyC;AAC9E,SAASC,kBAAkB,QAAQ,oCAAmC;AACtE,SAASC,wBAAwB,QAAQ,gCAA+B;AACxE,SAASC,mBAAmB,QAAQ,2BAA0B;AAE9D,cAAc,mBAAkB;AAEhC,OAAO,MAAMC,oBACX,CAACC,gBACD,CAACC;QACC,IAAI,CAACA,OAAOC,WAAW,EAAE;YACvBD,OAAOC,WAAW,GAAG,EAAE;QACzB;QAEAD,OAAOC,WAAW,CAACC,IAAI,CACrBZ,uBAAuBS,gBACvBd,2BAA2Bc,gBAC3Bf,6BAA6Be,gBAC7Bb,yBAAyBa,gBACzBV,wBAAwBU,gBACxBR,uCACAJ,kCAAkCY,gBAClCX,wBAAwBW;QAG1B,IAAIA,cAAcI,QAAQ,EAAE;YAC1B,OAAOH;QACT;QAEA,IAAI,CAACA,OAAOI,SAAS,EAAE;YACrBJ,OAAOI,SAAS,GAAG,EAAE;QACvB;QAEA,MAAMA,YAAwB;YAC5B;gBACEC,SAASZ;gBACTa,QAAQ;gBACRC,MAAM;YACR;YACA;gBACEF,SAASX,qBAAqBK;gBAC9BO,QAAQ;gBACRC,MAAM;YACR;YACA;gBACEF,SAASV;gBACTW,QAAQ;gBACRC,MAAM;YACR;YACA;gBACEF,SAASb,oBAAoBO;gBAC7BO,QAAQ;gBACRC,MAAM;YACR;SACD;QAEDP,OAAOI,SAAS,GAAG;eAAIJ,OAAOI,SAAS;eAAKA;SAAU;QAEtD,IAAI,CAACJ,OAAOQ,IAAI,EAAE;YAChBR,OAAOQ,IAAI,GAAG;gBACZC,OAAO,EAAE;YACX;QACF;QAEA,MAAMA,QAAsB;YAC1Bb,yBAAyBG;YACzBF,oBAAoBE;SACrB;QAEDC,OAAOQ,IAAI,CAACC,KAAK,GAAG;eAAKT,OAAOQ,IAAI,CAACC,KAAK,IAAI,EAAE;eAAMA;SAAM;QAE5D,MAAMC,iBAAiBV,OAAOW,MAAM;QAEpCX,OAAOW,MAAM,GAAG,OAAOC;YACrB,IAAIF,gBAAgB;gBAClB,MAAMA,eAAeE;YACvB;QACF;QAEA,OAAOZ;IACT,EAAC"}
@@ -85,7 +85,8 @@ import z from 'zod';
85
85
  */ export const createSendBroadcastsTask = (pluginConfig)=>{
86
86
  const BATCH_SIZE = pluginConfig.broadcastConfig?.batchSize || 100;
87
87
  const TASK_SCHEDULE = pluginConfig.broadcastConfig?.taskSchedule || '*/5 * * * *';
88
- const QUEUE_NAME = pluginConfig.broadcastConfig?.broadcastQueueName || 'send-broadcasts';
88
+ const BROADCAST_QUEUE_NAME = pluginConfig.broadcastConfig?.broadcastQueueName || 'send-broadcasts';
89
+ const EMAIL_QUEUE_NAME = pluginConfig.broadcastConfig?.emailQueueName || 'send-emails';
89
90
  return {
90
91
  slug: 'send-broadcasts',
91
92
  handler: async ({ req })=>{
@@ -157,7 +158,7 @@ import z from 'zod';
157
158
  broadcastId: broadcast.id,
158
159
  contactId: contact.id
159
160
  },
160
- queue: 'send-emails',
161
+ queue: EMAIL_QUEUE_NAME,
161
162
  task: 'send-email'
162
163
  })));
163
164
  await payload.update({
@@ -187,7 +188,7 @@ import z from 'zod';
187
188
  schedule: [
188
189
  {
189
190
  cron: TASK_SCHEDULE,
190
- queue: QUEUE_NAME
191
+ queue: BROADCAST_QUEUE_NAME
191
192
  }
192
193
  ]
193
194
  };
@@ -1 +1 @@
1
- {"version":3,"sources":["../../src/tasks/sendBroadcastsTask.ts"],"sourcesContent":["import type { TaskConfig, Where } from 'payload'\n\nimport z from 'zod'\n\nimport type { MobilizehubPluginConfig } from '../types/index.js'\n\n/**\n * Schema for validating broadcast documents before processing.\n */\nconst BroadcastSchema = z.object({\n id: z.number(),\n content: z.any().refine((val) => val !== undefined && val !== null, {\n message: 'Broadcast content is missing',\n }),\n fromAddress: z.email({ message: 'Invalid from address email format' }),\n fromName: z.string().min(1, { message: 'From name is required' }),\n meta: z.object({\n contactsCount: z.number().min(0),\n lastProcessedContactId: z.number().min(0).default(0),\n processedCount: z.number().min(0),\n }),\n subject: z.string().min(1, { message: 'Subject is required' }),\n tags: z.array(z.any()).optional(),\n to: z.literal('all').or(z.literal('tags')),\n})\n\ntype ParsedBroadcast = z.infer<typeof BroadcastSchema>\n\n/**\n * Validates a broadcast document against the schema.\n * Returns typed data on success, or flattened field errors on failure.\n */\nfunction safeParseBroadcast(broadcast: unknown) {\n const result = BroadcastSchema.safeParse(broadcast)\n\n if (!result.success) {\n return {\n data: null,\n errors: z.flattenError(result.error).fieldErrors,\n success: false as const,\n }\n }\n\n return {\n data: result.data,\n success: true as const,\n }\n}\n\n/**\n * Builds the where clause for fetching the next batch of contacts.\n * Uses cursor-based pagination (id > lastProcessedContactId) for consistent\n * ordering and efficient queries at scale.\n */\nfunction buildContactsWhereClause(\n broadcast: ParsedBroadcast,\n rawBroadcast: Record<string, unknown>,\n): Where {\n const conditions: Where[] = [\n { emailOptIn: { equals: true } },\n { id: { greater_than: broadcast.meta.lastProcessedContactId } },\n ]\n\n // Tags may be populated objects or raw IDs depending on query depth\n if (broadcast.to === 'tags' && Array.isArray(rawBroadcast.tags) && rawBroadcast.tags.length > 0) {\n const tagIds = rawBroadcast.tags.map((t: { id: number | string } | number | string) =>\n typeof t === 'object' ? t.id : t,\n )\n conditions.push({ tags: { in: tagIds } })\n }\n\n return { and: conditions }\n}\n\n/**\n * Extracts a numeric ID from a contact, handling string IDs from some database adapters.\n */\nfunction getLastContactId(contact: { id: number | string }): number {\n return typeof contact.id === 'number' ? contact.id : parseInt(contact.id, 10)\n}\n\n/**\n * Creates the send-broadcasts scheduled task.\n *\n * Processes broadcasts by polling for documents with status 'sending' and\n * queuing batches of send-email jobs. Each invocation processes one broadcast\n * and one batch of contacts, allowing the task to be distributed across\n * multiple schedule intervals.\n */\nexport const createSendBroadcastsTask = (pluginConfig: MobilizehubPluginConfig): TaskConfig => {\n const BATCH_SIZE = pluginConfig.broadcastConfig?.batchSize || 100\n const TASK_SCHEDULE = pluginConfig.broadcastConfig?.taskSchedule || '*/5 * * * *'\n const QUEUE_NAME = pluginConfig.broadcastConfig?.broadcastQueueName || 'send-broadcasts'\n\n return {\n slug: 'send-broadcasts',\n handler: async ({ req }) => {\n const { payload } = req\n const logger = payload.logger\n\n logger.info('Send Broadcast task handler called')\n\n const { docs } = await payload.find({\n collection: 'broadcasts',\n limit: 1,\n sort: 'id',\n where: { status: { equals: 'sending' } },\n })\n\n const rawBroadcast = docs[0]\n\n if (!rawBroadcast) {\n logger.info('No broadcasts with status \"sending\" found')\n return { output: { success: true } }\n }\n\n const parsed = safeParseBroadcast(rawBroadcast)\n\n if (!parsed.success) {\n logger.error({ errors: parsed.errors }, `Broadcast ${rawBroadcast.id} validation failed`)\n await payload.update({\n id: rawBroadcast.id,\n collection: 'broadcasts',\n data: { status: 'failed' },\n })\n return { output: { success: false } }\n }\n\n const broadcast = parsed.data\n const whereClause = buildContactsWhereClause(\n broadcast,\n rawBroadcast as Record<string, unknown>,\n )\n\n const { docs: contacts } = await payload.find({\n collection: 'contacts',\n limit: BATCH_SIZE,\n sort: 'id',\n where: whereClause,\n })\n\n if (contacts.length === 0) {\n logger.info(\n `Broadcast ${broadcast.id} complete. ` +\n `Processed: ${broadcast.meta.processedCount}, Expected: ${broadcast.meta.contactsCount}`,\n )\n await payload.update({\n id: broadcast.id,\n collection: 'broadcasts',\n data: { status: 'sent' },\n })\n return { output: { success: true } }\n }\n\n await Promise.all(\n contacts.map((contact) =>\n payload.jobs.queue({\n input: { broadcastId: broadcast.id, contactId: contact.id },\n queue: 'send-emails',\n task: 'send-email',\n }),\n ),\n )\n\n await payload.update({\n id: broadcast.id,\n collection: 'broadcasts',\n data: {\n meta: {\n lastProcessedContactId: getLastContactId(contacts[contacts.length - 1]),\n processedCount: broadcast.meta.processedCount + contacts.length,\n },\n },\n })\n\n logger.info(\n `Broadcast ${broadcast.id}: queued ${contacts.length} emails, ` +\n `total processed: ${broadcast.meta.processedCount + contacts.length}`,\n )\n\n return { output: { success: true } }\n },\n outputSchema: [\n {\n name: 'success',\n type: 'checkbox',\n },\n ],\n retries: 3,\n schedule: [\n {\n cron: TASK_SCHEDULE,\n queue: QUEUE_NAME,\n },\n ],\n }\n}\n"],"names":["z","BroadcastSchema","object","id","number","content","any","refine","val","undefined","message","fromAddress","email","fromName","string","min","meta","contactsCount","lastProcessedContactId","default","processedCount","subject","tags","array","optional","to","literal","or","safeParseBroadcast","broadcast","result","safeParse","success","data","errors","flattenError","error","fieldErrors","buildContactsWhereClause","rawBroadcast","conditions","emailOptIn","equals","greater_than","Array","isArray","length","tagIds","map","t","push","in","and","getLastContactId","contact","parseInt","createSendBroadcastsTask","pluginConfig","BATCH_SIZE","broadcastConfig","batchSize","TASK_SCHEDULE","taskSchedule","QUEUE_NAME","broadcastQueueName","slug","handler","req","payload","logger","info","docs","find","collection","limit","sort","where","status","output","parsed","update","whereClause","contacts","Promise","all","jobs","queue","input","broadcastId","contactId","task","outputSchema","name","type","retries","schedule","cron"],"mappings":"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"}
1
+ {"version":3,"sources":["../../src/tasks/sendBroadcastsTask.ts"],"sourcesContent":["import type { TaskConfig, Where } from 'payload'\n\nimport z from 'zod'\n\nimport type { MobilizehubPluginConfig } from '../types/index.js'\n\n/**\n * Schema for validating broadcast documents before processing.\n */\nconst BroadcastSchema = z.object({\n id: z.number(),\n content: z.any().refine((val) => val !== undefined && val !== null, {\n message: 'Broadcast content is missing',\n }),\n fromAddress: z.email({ message: 'Invalid from address email format' }),\n fromName: z.string().min(1, { message: 'From name is required' }),\n meta: z.object({\n contactsCount: z.number().min(0),\n lastProcessedContactId: z.number().min(0).default(0),\n processedCount: z.number().min(0),\n }),\n subject: z.string().min(1, { message: 'Subject is required' }),\n tags: z.array(z.any()).optional(),\n to: z.literal('all').or(z.literal('tags')),\n})\n\ntype ParsedBroadcast = z.infer<typeof BroadcastSchema>\n\n/**\n * Validates a broadcast document against the schema.\n * Returns typed data on success, or flattened field errors on failure.\n */\nfunction safeParseBroadcast(broadcast: unknown) {\n const result = BroadcastSchema.safeParse(broadcast)\n\n if (!result.success) {\n return {\n data: null,\n errors: z.flattenError(result.error).fieldErrors,\n success: false as const,\n }\n }\n\n return {\n data: result.data,\n success: true as const,\n }\n}\n\n/**\n * Builds the where clause for fetching the next batch of contacts.\n * Uses cursor-based pagination (id > lastProcessedContactId) for consistent\n * ordering and efficient queries at scale.\n */\nfunction buildContactsWhereClause(\n broadcast: ParsedBroadcast,\n rawBroadcast: Record<string, unknown>,\n): Where {\n const conditions: Where[] = [\n { emailOptIn: { equals: true } },\n { id: { greater_than: broadcast.meta.lastProcessedContactId } },\n ]\n\n // Tags may be populated objects or raw IDs depending on query depth\n if (broadcast.to === 'tags' && Array.isArray(rawBroadcast.tags) && rawBroadcast.tags.length > 0) {\n const tagIds = rawBroadcast.tags.map((t: { id: number | string } | number | string) =>\n typeof t === 'object' ? t.id : t,\n )\n conditions.push({ tags: { in: tagIds } })\n }\n\n return { and: conditions }\n}\n\n/**\n * Extracts a numeric ID from a contact, handling string IDs from some database adapters.\n */\nfunction getLastContactId(contact: { id: number | string }): number {\n return typeof contact.id === 'number' ? contact.id : parseInt(contact.id, 10)\n}\n\n/**\n * Creates the send-broadcasts scheduled task.\n *\n * Processes broadcasts by polling for documents with status 'sending' and\n * queuing batches of send-email jobs. Each invocation processes one broadcast\n * and one batch of contacts, allowing the task to be distributed across\n * multiple schedule intervals.\n */\nexport const createSendBroadcastsTask = (pluginConfig: MobilizehubPluginConfig): TaskConfig => {\n const BATCH_SIZE = pluginConfig.broadcastConfig?.batchSize || 100\n const TASK_SCHEDULE = pluginConfig.broadcastConfig?.taskSchedule || '*/5 * * * *'\n const BROADCAST_QUEUE_NAME = pluginConfig.broadcastConfig?.broadcastQueueName || 'send-broadcasts'\n const EMAIL_QUEUE_NAME = pluginConfig.broadcastConfig?.emailQueueName || 'send-emails'\n\n return {\n slug: 'send-broadcasts',\n handler: async ({ req }) => {\n const { payload } = req\n const logger = payload.logger\n\n logger.info('Send Broadcast task handler called')\n\n const { docs } = await payload.find({\n collection: 'broadcasts',\n limit: 1,\n sort: 'id',\n where: { status: { equals: 'sending' } },\n })\n\n const rawBroadcast = docs[0]\n\n if (!rawBroadcast) {\n logger.info('No broadcasts with status \"sending\" found')\n return { output: { success: true } }\n }\n\n const parsed = safeParseBroadcast(rawBroadcast)\n\n if (!parsed.success) {\n logger.error({ errors: parsed.errors }, `Broadcast ${rawBroadcast.id} validation failed`)\n await payload.update({\n id: rawBroadcast.id,\n collection: 'broadcasts',\n data: { status: 'failed' },\n })\n return { output: { success: false } }\n }\n\n const broadcast = parsed.data\n const whereClause = buildContactsWhereClause(\n broadcast,\n rawBroadcast as Record<string, unknown>,\n )\n\n const { docs: contacts } = await payload.find({\n collection: 'contacts',\n limit: BATCH_SIZE,\n sort: 'id',\n where: whereClause,\n })\n\n if (contacts.length === 0) {\n logger.info(\n `Broadcast ${broadcast.id} complete. ` +\n `Processed: ${broadcast.meta.processedCount}, Expected: ${broadcast.meta.contactsCount}`,\n )\n await payload.update({\n id: broadcast.id,\n collection: 'broadcasts',\n data: { status: 'sent' },\n })\n return { output: { success: true } }\n }\n\n await Promise.all(\n contacts.map((contact) =>\n payload.jobs.queue({\n input: { broadcastId: broadcast.id, contactId: contact.id },\n queue: EMAIL_QUEUE_NAME,\n task: 'send-email',\n }),\n ),\n )\n\n await payload.update({\n id: broadcast.id,\n collection: 'broadcasts',\n data: {\n meta: {\n lastProcessedContactId: getLastContactId(contacts[contacts.length - 1]),\n processedCount: broadcast.meta.processedCount + contacts.length,\n },\n },\n })\n\n logger.info(\n `Broadcast ${broadcast.id}: queued ${contacts.length} emails, ` +\n `total processed: ${broadcast.meta.processedCount + contacts.length}`,\n )\n\n return { output: { success: true } }\n },\n outputSchema: [\n {\n name: 'success',\n type: 'checkbox',\n },\n ],\n retries: 3,\n schedule: [\n {\n cron: TASK_SCHEDULE,\n queue: BROADCAST_QUEUE_NAME,\n },\n ],\n }\n}\n"],"names":["z","BroadcastSchema","object","id","number","content","any","refine","val","undefined","message","fromAddress","email","fromName","string","min","meta","contactsCount","lastProcessedContactId","default","processedCount","subject","tags","array","optional","to","literal","or","safeParseBroadcast","broadcast","result","safeParse","success","data","errors","flattenError","error","fieldErrors","buildContactsWhereClause","rawBroadcast","conditions","emailOptIn","equals","greater_than","Array","isArray","length","tagIds","map","t","push","in","and","getLastContactId","contact","parseInt","createSendBroadcastsTask","pluginConfig","BATCH_SIZE","broadcastConfig","batchSize","TASK_SCHEDULE","taskSchedule","BROADCAST_QUEUE_NAME","broadcastQueueName","EMAIL_QUEUE_NAME","emailQueueName","slug","handler","req","payload","logger","info","docs","find","collection","limit","sort","where","status","output","parsed","update","whereClause","contacts","Promise","all","jobs","queue","input","broadcastId","contactId","task","outputSchema","name","type","retries","schedule","cron"],"mappings":"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"}
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@mobilizehub/payload-plugin",
3
- "version": "0.4.0",
3
+ "version": "0.5.0",
4
4
  "description": "Edvocacy plugin for Payload",
5
5
  "license": "MIT",
6
6
  "private": false,