payload-plugin-newsletter 0.16.5 → 0.16.7

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/dist/index.js CHANGED
@@ -240,29 +240,46 @@ var init_broadcast2 = __esm({
240
240
  async create(data) {
241
241
  try {
242
242
  this.validateRequiredFields(data, ["name", "subject", "content"]);
243
+ const requestBody = {
244
+ broadcast: {
245
+ name: data.name,
246
+ subject: data.subject,
247
+ preheader: data.preheader,
248
+ body: data.content,
249
+ html_body: true,
250
+ track_opens: data.trackOpens ?? true,
251
+ track_clicks: data.trackClicks ?? true,
252
+ reply_to: data.replyTo,
253
+ segment_ids: data.audienceIds
254
+ }
255
+ };
256
+ console.log("[BroadcastApiProvider] Creating broadcast:", {
257
+ url: `${this.apiUrl}/api/v1/broadcasts`,
258
+ method: "POST",
259
+ hasToken: !!this.token,
260
+ tokenLength: this.token?.length,
261
+ body: JSON.stringify(requestBody, null, 2)
262
+ });
243
263
  const response = await fetch(`${this.apiUrl}/api/v1/broadcasts`, {
244
264
  method: "POST",
245
265
  headers: {
246
266
  "Authorization": `Bearer ${this.token}`,
247
267
  "Content-Type": "application/json"
248
268
  },
249
- body: JSON.stringify({
250
- broadcast: {
251
- name: data.name,
252
- subject: data.subject,
253
- preheader: data.preheader,
254
- body: data.content,
255
- html_body: true,
256
- track_opens: data.trackOpens ?? true,
257
- track_clicks: data.trackClicks ?? true,
258
- reply_to: data.replyTo,
259
- segment_ids: data.audienceIds
260
- }
261
- })
269
+ body: JSON.stringify(requestBody)
262
270
  });
271
+ console.log("[BroadcastApiProvider] Response status:", response.status);
272
+ console.log("[BroadcastApiProvider] Response headers:", Object.fromEntries(response.headers.entries()));
263
273
  if (!response.ok) {
264
- const error = await response.text();
265
- throw new Error(`Broadcast API error: ${response.status} - ${error}`);
274
+ const errorText = await response.text();
275
+ console.error("[BroadcastApiProvider] Error response body:", errorText);
276
+ let errorDetails;
277
+ try {
278
+ errorDetails = JSON.parse(errorText);
279
+ console.error("[BroadcastApiProvider] Parsed error:", errorDetails);
280
+ } catch {
281
+ }
282
+ throw new Error(`Broadcast API error: ${response.status} - ${errorText}`);
266
283
  }
267
284
  const result = await response.json();
268
285
  return this.get(result.id.toString());
@@ -4372,58 +4389,162 @@ var createBroadcastsCollection = (pluginConfig) => {
4372
4389
  }
4373
4390
  ],
4374
4391
  hooks: {
4375
- // Sync with provider on create
4392
+ // Sync with provider on create and update
4376
4393
  afterChange: [
4377
- async ({ doc, operation, req }) => {
4378
- if (!hasProviders || operation !== "create") return doc;
4379
- try {
4380
- const providerConfig = await getBroadcastConfig(req, pluginConfig);
4381
- if (!providerConfig || !providerConfig.token) {
4382
- req.payload.logger.error("Broadcast provider not configured in Newsletter Settings or environment variables");
4383
- return doc;
4384
- }
4385
- const { BroadcastApiProvider: BroadcastApiProvider2 } = await Promise.resolve().then(() => (init_broadcast2(), broadcast_exports));
4386
- const provider = new BroadcastApiProvider2(providerConfig);
4387
- const htmlContent = await convertToEmailSafeHtml(doc.contentSection?.content);
4388
- const providerBroadcast = await provider.create({
4389
- name: doc.subject,
4390
- // Use subject as name since we removed the name field
4391
- subject: doc.subject,
4392
- preheader: doc.contentSection?.preheader,
4393
- content: htmlContent,
4394
- trackOpens: doc.settings?.trackOpens,
4395
- trackClicks: doc.settings?.trackClicks,
4396
- replyTo: doc.settings?.replyTo || providerConfig.replyTo,
4397
- audienceIds: doc.audienceIds?.map((a) => a.audienceId)
4398
- });
4399
- await req.payload.update({
4400
- collection: "broadcasts",
4401
- id: doc.id,
4402
- data: {
4394
+ async ({ doc, operation, req, previousDoc }) => {
4395
+ if (!hasProviders) return doc;
4396
+ if (operation === "create") {
4397
+ try {
4398
+ const providerConfig = await getBroadcastConfig(req, pluginConfig);
4399
+ if (!providerConfig || !providerConfig.token) {
4400
+ req.payload.logger.error("Broadcast provider not configured in Newsletter Settings or environment variables");
4401
+ return doc;
4402
+ }
4403
+ const { BroadcastApiProvider: BroadcastApiProvider2 } = await Promise.resolve().then(() => (init_broadcast2(), broadcast_exports));
4404
+ const provider = new BroadcastApiProvider2(providerConfig);
4405
+ req.payload.logger.info("Converting content to HTML...");
4406
+ const htmlContent = await convertToEmailSafeHtml(doc.contentSection?.content);
4407
+ const createData = {
4408
+ name: doc.subject,
4409
+ // Use subject as name since we removed the name field
4410
+ subject: doc.subject,
4411
+ preheader: doc.contentSection?.preheader,
4412
+ content: htmlContent,
4413
+ trackOpens: doc.settings?.trackOpens,
4414
+ trackClicks: doc.settings?.trackClicks,
4415
+ replyTo: doc.settings?.replyTo || providerConfig.replyTo,
4416
+ audienceIds: doc.audienceIds?.map((a) => a.audienceId)
4417
+ };
4418
+ req.payload.logger.info("Creating broadcast with data:", {
4419
+ name: createData.name,
4420
+ subject: createData.subject,
4421
+ preheader: createData.preheader || "NONE",
4422
+ contentLength: htmlContent ? htmlContent.length : 0,
4423
+ contentPreview: htmlContent ? htmlContent.substring(0, 100) + "..." : "EMPTY",
4424
+ trackOpens: createData.trackOpens,
4425
+ trackClicks: createData.trackClicks,
4426
+ replyTo: createData.replyTo,
4427
+ audienceIds: createData.audienceIds || [],
4428
+ apiUrl: providerConfig.apiUrl,
4429
+ hasToken: !!providerConfig.token
4430
+ });
4431
+ const providerBroadcast = await provider.create(createData);
4432
+ await req.payload.update({
4433
+ collection: "broadcasts",
4434
+ id: doc.id,
4435
+ data: {
4436
+ providerId: providerBroadcast.id,
4437
+ providerData: providerBroadcast.providerData
4438
+ },
4439
+ req
4440
+ });
4441
+ return {
4442
+ ...doc,
4403
4443
  providerId: providerBroadcast.id,
4404
4444
  providerData: providerBroadcast.providerData
4405
- },
4406
- req
4407
- });
4408
- return {
4409
- ...doc,
4410
- providerId: providerBroadcast.id,
4411
- providerData: providerBroadcast.providerData
4412
- };
4413
- } catch (error) {
4414
- if (error instanceof Error) {
4415
- req.payload.logger.error("Failed to create broadcast in provider:", {
4416
- message: error.message,
4417
- stack: error.stack,
4418
- name: error.name,
4419
- // If it's a BroadcastProviderError, it might have additional details
4420
- ...error.details
4445
+ };
4446
+ } catch (error) {
4447
+ req.payload.logger.error("Raw error from broadcast provider:");
4448
+ req.payload.logger.error(error);
4449
+ if (error instanceof Error) {
4450
+ req.payload.logger.error("Error is instance of Error:", {
4451
+ message: error.message,
4452
+ stack: error.stack,
4453
+ name: error.name,
4454
+ // If it's a BroadcastProviderError, it might have additional details
4455
+ ...error.details,
4456
+ // Check if it's a fetch response error
4457
+ ...error.response,
4458
+ ...error.data,
4459
+ ...error.status,
4460
+ ...error.statusText
4461
+ });
4462
+ } else if (typeof error === "string") {
4463
+ req.payload.logger.error("Error is a string:", error);
4464
+ } else if (error && typeof error === "object") {
4465
+ req.payload.logger.error("Error is an object:", JSON.stringify(error, null, 2));
4466
+ } else {
4467
+ req.payload.logger.error("Unknown error type:", typeof error);
4468
+ }
4469
+ req.payload.logger.error("Failed broadcast document:", {
4470
+ id: doc.id,
4471
+ subject: doc.subject,
4472
+ hasContent: !!doc.contentSection?.content,
4473
+ contentType: doc.contentSection?.content ? typeof doc.contentSection.content : "none"
4421
4474
  });
4422
- } else {
4423
- req.payload.logger.error("Failed to create broadcast in provider:", error);
4475
+ return doc;
4424
4476
  }
4425
- return doc;
4426
4477
  }
4478
+ if (operation === "update" && doc.providerId) {
4479
+ req.payload.logger.info("Broadcast afterChange update hook triggered", {
4480
+ operation,
4481
+ hasProviderId: !!doc.providerId,
4482
+ sendStatus: doc.sendStatus,
4483
+ publishStatus: doc._status
4484
+ });
4485
+ try {
4486
+ const providerConfig = await getBroadcastConfig(req, pluginConfig);
4487
+ if (!providerConfig || !providerConfig.token) {
4488
+ req.payload.logger.error("Broadcast provider not configured in Newsletter Settings or environment variables");
4489
+ return doc;
4490
+ }
4491
+ const { BroadcastApiProvider: BroadcastApiProvider2 } = await Promise.resolve().then(() => (init_broadcast2(), broadcast_exports));
4492
+ const provider = new BroadcastApiProvider2(providerConfig);
4493
+ const capabilities = provider.getCapabilities();
4494
+ const sendStatus = doc.sendStatus || "draft" /* DRAFT */;
4495
+ if (!capabilities.editableStatuses.includes(sendStatus)) {
4496
+ req.payload.logger.info(`Skipping sync for broadcast in status: ${sendStatus}`);
4497
+ return doc;
4498
+ }
4499
+ const contentChanged = doc.subject !== previousDoc?.subject || doc.contentSection?.preheader !== previousDoc?.contentSection?.preheader || JSON.stringify(doc.contentSection?.content) !== JSON.stringify(previousDoc?.contentSection?.content) || doc.settings?.trackOpens !== previousDoc?.settings?.trackOpens || doc.settings?.trackClicks !== previousDoc?.settings?.trackClicks || doc.settings?.replyTo !== previousDoc?.settings?.replyTo || JSON.stringify(doc.audienceIds) !== JSON.stringify(previousDoc?.audienceIds);
4500
+ if (contentChanged) {
4501
+ const updates = {};
4502
+ if (doc.subject !== previousDoc?.subject) {
4503
+ updates.name = doc.subject;
4504
+ updates.subject = doc.subject;
4505
+ }
4506
+ if (doc.contentSection?.preheader !== previousDoc?.contentSection?.preheader) {
4507
+ updates.preheader = doc.contentSection?.preheader;
4508
+ }
4509
+ if (JSON.stringify(doc.contentSection?.content) !== JSON.stringify(previousDoc?.contentSection?.content)) {
4510
+ updates.content = await convertToEmailSafeHtml(doc.contentSection?.content);
4511
+ }
4512
+ if (doc.settings?.trackOpens !== previousDoc?.settings?.trackOpens) {
4513
+ updates.trackOpens = doc.settings.trackOpens;
4514
+ }
4515
+ if (doc.settings?.trackClicks !== previousDoc?.settings?.trackClicks) {
4516
+ updates.trackClicks = doc.settings.trackClicks;
4517
+ }
4518
+ if (doc.settings?.replyTo !== previousDoc?.settings?.replyTo) {
4519
+ updates.replyTo = doc.settings.replyTo || providerConfig.replyTo;
4520
+ }
4521
+ if (JSON.stringify(doc.audienceIds) !== JSON.stringify(previousDoc?.audienceIds)) {
4522
+ updates.audienceIds = doc.audienceIds?.map((a) => a.audienceId);
4523
+ }
4524
+ req.payload.logger.info("Syncing broadcast updates to provider", {
4525
+ providerId: doc.providerId,
4526
+ updates
4527
+ });
4528
+ await provider.update(doc.providerId, updates);
4529
+ req.payload.logger.info(`Broadcast ${doc.id} synced to provider successfully`);
4530
+ } else {
4531
+ req.payload.logger.info("No content changes to sync to provider");
4532
+ }
4533
+ } catch (error) {
4534
+ if (error instanceof Error) {
4535
+ req.payload.logger.error("Failed to sync broadcast update to provider:", {
4536
+ message: error.message,
4537
+ stack: error.stack,
4538
+ name: error.name,
4539
+ // If it's a BroadcastProviderError, it might have additional details
4540
+ ...error.details
4541
+ });
4542
+ } else {
4543
+ req.payload.logger.error("Failed to sync broadcast update to provider:", error);
4544
+ }
4545
+ }
4546
+ }
4547
+ return doc;
4427
4548
  },
4428
4549
  // Hook to send when published
4429
4550
  async ({ doc, operation, previousDoc, req }) => {
@@ -4481,79 +4602,8 @@ var createBroadcastsCollection = (pluginConfig) => {
4481
4602
  return doc;
4482
4603
  }
4483
4604
  ],
4484
- // Sync updates with provider
4485
- beforeChange: [
4486
- async ({ data, originalDoc, operation, req }) => {
4487
- if (!hasProviders || !originalDoc?.providerId || operation !== "update") return data;
4488
- req.payload.logger.info("Broadcast beforeChange update hook triggered", {
4489
- operation,
4490
- hasProviderId: !!originalDoc?.providerId,
4491
- originalSendStatus: originalDoc?.sendStatus,
4492
- originalPublishStatus: originalDoc?._status,
4493
- newSendStatus: data?.sendStatus,
4494
- newPublishStatus: data?._status
4495
- });
4496
- try {
4497
- const providerConfig = await getBroadcastConfig(req, pluginConfig);
4498
- if (!providerConfig || !providerConfig.token) {
4499
- req.payload.logger.error("Broadcast provider not configured in Newsletter Settings or environment variables");
4500
- return data;
4501
- }
4502
- const { BroadcastApiProvider: BroadcastApiProvider2 } = await Promise.resolve().then(() => (init_broadcast2(), broadcast_exports));
4503
- const provider = new BroadcastApiProvider2(providerConfig);
4504
- const capabilities = provider.getCapabilities();
4505
- const sendStatus = originalDoc.sendStatus || "draft" /* DRAFT */;
4506
- if (!capabilities.editableStatuses.includes(sendStatus)) {
4507
- req.payload.logger.info(`Skipping sync for broadcast in status: ${sendStatus}`);
4508
- return data;
4509
- }
4510
- const updates = {};
4511
- if (data.subject !== originalDoc.subject) {
4512
- updates.name = data.subject;
4513
- updates.subject = data.subject;
4514
- }
4515
- if (data.contentSection?.preheader !== originalDoc.contentSection?.preheader) updates.preheader = data.contentSection?.preheader;
4516
- if (data.contentSection?.content !== originalDoc.contentSection?.content) {
4517
- updates.content = await convertToEmailSafeHtml(data.contentSection?.content);
4518
- }
4519
- if (data.settings?.trackOpens !== originalDoc.settings?.trackOpens) {
4520
- updates.trackOpens = data.settings.trackOpens;
4521
- }
4522
- if (data.settings?.trackClicks !== originalDoc.settings?.trackClicks) {
4523
- updates.trackClicks = data.settings.trackClicks;
4524
- }
4525
- if (data.settings?.replyTo !== originalDoc.settings?.replyTo) {
4526
- updates.replyTo = data.settings.replyTo || providerConfig.replyTo;
4527
- }
4528
- if (JSON.stringify(data.audienceIds) !== JSON.stringify(originalDoc.audienceIds)) {
4529
- updates.audienceIds = data.audienceIds?.map((a) => a.audienceId);
4530
- }
4531
- if (Object.keys(updates).length > 0) {
4532
- req.payload.logger.info("Syncing broadcast updates to provider", {
4533
- providerId: originalDoc.providerId,
4534
- updates
4535
- });
4536
- await provider.update(originalDoc.providerId, updates);
4537
- req.payload.logger.info("Successfully synced broadcast updates to provider");
4538
- } else {
4539
- req.payload.logger.info("No broadcast updates to sync to provider");
4540
- }
4541
- } catch (error) {
4542
- if (error instanceof Error) {
4543
- req.payload.logger.error("Failed to update broadcast in provider:", {
4544
- message: error.message,
4545
- stack: error.stack,
4546
- name: error.name,
4547
- // If it's a BroadcastProviderError, it might have additional details
4548
- ...error.details
4549
- });
4550
- } else {
4551
- req.payload.logger.error("Failed to update broadcast in provider:", error);
4552
- }
4553
- }
4554
- return data;
4555
- }
4556
- ],
4605
+ // beforeChange hooks can be added here if needed
4606
+ beforeChange: [],
4557
4607
  // Handle deletion
4558
4608
  afterDelete: [
4559
4609
  async ({ doc, req }) => {