@aglyn/plugins-marketing 1.0.0-beta.182 → 1.0.0-beta.183

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/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@aglyn/plugins-marketing",
3
- "version": "1.0.0-beta.182",
3
+ "version": "1.0.0-beta.183",
4
4
  "license": "Apache-2.0",
5
5
  "homepage": "https://aglyn.com",
6
6
  "repository": {
@@ -25,20 +25,20 @@
25
25
  "./package.json": "./package.json"
26
26
  },
27
27
  "dependencies": {
28
- "@aglyn/aglyn": "1.0.0-beta.182",
29
- "@aglyn/shared-data-enums": "1.0.0-beta.182",
30
- "@aglyn/shared-data-mdi": "1.0.0-beta.182",
31
- "@aglyn/shared-ui-email-campaigns": "1.0.0-beta.182",
32
- "@aglyn/shared-ui-jsx": "1.0.0-beta.182",
33
- "@aglyn/shared-ui-jsx-forms": "1.0.0-beta.182",
34
- "@aglyn/shared-ui-next": "1.0.0-beta.182",
35
- "@aglyn/shared-ui-snackstack": "1.0.0-beta.182",
36
- "@aglyn/shared-util-email": "1.0.0-beta.182",
37
- "@aglyn/shared-util-http": "1.0.0-beta.182",
38
- "@aglyn/shared-util-timestamp": "1.0.0-beta.182",
39
- "@aglyn/tenant-data-admin": "1.0.0-beta.182",
40
- "@aglyn/tenant-feature-instance": "1.0.0-beta.182",
41
- "@aglyn/tenant-runtime": "1.0.0-beta.182",
28
+ "@aglyn/aglyn": "1.0.0-beta.183",
29
+ "@aglyn/shared-data-enums": "1.0.0-beta.183",
30
+ "@aglyn/shared-data-mdi": "1.0.0-beta.183",
31
+ "@aglyn/shared-ui-email-campaigns": "1.0.0-beta.183",
32
+ "@aglyn/shared-ui-jsx": "1.0.0-beta.183",
33
+ "@aglyn/shared-ui-jsx-forms": "1.0.0-beta.183",
34
+ "@aglyn/shared-ui-next": "1.0.0-beta.183",
35
+ "@aglyn/shared-ui-snackstack": "1.0.0-beta.183",
36
+ "@aglyn/shared-util-email": "1.0.0-beta.183",
37
+ "@aglyn/shared-util-http": "1.0.0-beta.183",
38
+ "@aglyn/shared-util-timestamp": "1.0.0-beta.183",
39
+ "@aglyn/tenant-data-admin": "1.0.0-beta.183",
40
+ "@aglyn/tenant-feature-instance": "1.0.0-beta.183",
41
+ "@aglyn/tenant-runtime": "1.0.0-beta.183",
42
42
  "@swc/helpers": "0.5.23"
43
43
  },
44
44
  "peerDependencies": {
@@ -16,6 +16,7 @@ import { _ as _extends } from "@swc/helpers/_/_extends";
16
16
  * limitations under the License.
17
17
  */ import { checkQuota, contactMatchesSegment, isEmailTopicId, DEFAULT_CAMPAIGN_TOPIC_ID, readMarketingBasis, resolveMarketingConsentPolicy, splitByMarketingConsent, createResourceUid, decodeStoredNodes, emailStarterSendBlock, resolveBrandingProfile, visibleToHost } from "@aglyn/aglyn/server";
18
18
  import { renderRecipientEmail } from "@aglyn/aglyn/app-utils/recipient-email-render";
19
+ import { composeHostComponentNodes } from "@aglyn/aglyn/app-utils/load-referenced-components";
19
20
  import { assignExperimentVariant } from "../model/index.js";
20
21
  import { campaignPlacedOnHost } from "@aglyn/shared-ui-email-campaigns/model";
21
22
  import { readPluginRecordCard } from "@aglyn/aglyn/plugin-manager/plugin-record-cards";
@@ -195,6 +196,11 @@ const EMAIL_PATTERN = /^[^\s@]+@[^\s@]+\.[^\s@]+$/;
195
196
  /**
196
197
  * Loads a designed email template's nodes + referenced products for the
197
198
  * render pipeline. Throws 400 when the screen isn't an email document.
199
+ *
200
+ * The nodes come back COMPOSED: every reusable block the design places — a
201
+ * shared header, a footer — expanded into the blocks it stands for (AGL-3287).
202
+ * Everything downstream renders from this one map: each recipient's copy, the
203
+ * test send and the composer's preview.
198
204
  */ async function loadEmailTemplate(hostId, screenId) {
199
205
  var _decodeStoredNodes, _ref, _screenSnapshot_get, _screenSnapshot_get1;
200
206
  const firestore = firebaseAdmin.app().firestore();
@@ -225,8 +231,8 @@ const EMAIL_PATTERN = /^[^\s@]+@[^\s@]+\.[^\s@]+$/;
225
231
  * blocks missing — discovered by the recipients. Decoding first is also what
226
232
  * makes the guard mean something: `decodeStoredNodes` returns null for an
227
233
  * undecodable payload, so the send is refused instead of mailed empty.
228
- */ const nodes = (_decodeStoredNodes = decodeStoredNodes(versionSnapshot == null ? void 0 : versionSnapshot.get('nodes'))) != null ? _decodeStoredNodes : {};
229
- if (!Object.keys(nodes).length) {
234
+ */ const stored = (_decodeStoredNodes = decodeStoredNodes(versionSnapshot == null ? void 0 : versionSnapshot.get('nodes'))) != null ? _decodeStoredNodes : {};
235
+ if (!Object.keys(stored).length) {
230
236
  throw new CampaignSendError('The email template is empty', 400);
231
237
  }
232
238
  /**
@@ -260,6 +266,24 @@ const EMAIL_PATTERN = /^[^\s@]+@[^\s@]+\.[^\s@]+$/;
260
266
  });
261
267
  if (block) throw new CampaignSendError(block.reason, 409);
262
268
  }
269
+ /*
270
+ * THE SITE'S REUSABLE BLOCKS, EXPANDED (AGL-3287).
271
+ *
272
+ * A header or footer placed in the design is a `reusableInstance` naming one
273
+ * of the site's components, and the mail renderer draws a node it does not
274
+ * know as nothing — so without this every campaign went out without the
275
+ * blocks its author could see on the canvas. Grafted from the components'
276
+ * PUBLISHED definitions, read fresh, never from a cache: a send is not a
277
+ * page, and a header republished a minute ago is the one the author expects.
278
+ *
279
+ * BEFORE the product scan below, which is the ordering that matters: a
280
+ * product block inside a footer only exists once the footer is grafted.
281
+ * Each placement's property values and attribute overrides apply; its style
282
+ * overrides land in `sx`, which the mail renderer does not read.
283
+ */ const nodes = await composeHostComponentNodes(stored, {
284
+ firestore,
285
+ hostId
286
+ });
263
287
  // Resolve emailProduct references (by id — rename-safe, AGL-343).
264
288
  const productIds = [
265
289
  ...new Set(Object.values(nodes).filter((node)=>(node == null ? void 0 : node.componentId) === 'emailProduct').map((node)=>{