@classytic/arc-notifications 0.6.0 → 0.6.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 (2) hide show
  1. package/dist/index.mjs +41 -7
  2. package/package.json +1 -1
package/dist/index.mjs CHANGED
@@ -340,14 +340,39 @@ function interpolate$1(template, vars) {
340
340
  return v === void 0 || v === null ? `{${key}}` : String(v);
341
341
  });
342
342
  }
343
+ const UNRESOLVED = /\{\w+\}/;
344
+ /**
345
+ * Interpolate a MACHINE field — a link, an entity ref — and return `undefined`
346
+ * when any placeholder went unresolved.
347
+ *
348
+ * Leaving `{key}` verbatim is right for COPY: a literal `{orderNumber}` in a
349
+ * sentence is a visible bug someone fixes. It is wrong for a field something
350
+ * PARSES, because the placeholder becomes a plausible value:
351
+ *
352
+ * - `link: '/dashboard/orders?_id={orderId}'` filters for the literal string and
353
+ * returns zero rows, which reads as "the order was deleted".
354
+ * - `entityId: '{orderId}'` is the SAME identity for every order, so a feed that
355
+ * deduplicates on it collapses distinct orders into one notification — worse
356
+ * than not deduplicating.
357
+ *
358
+ * Absent degrades honestly: the link falls back to the unfiltered list, and a
359
+ * missing entity ref makes the feed skip dedup rather than dedupe wrongly.
360
+ */
361
+ function interpolateRef(template, vars) {
362
+ const out = interpolate$1(template, vars);
363
+ return UNRESOLVED.test(out) ? void 0 : out;
364
+ }
343
365
  /** Render a trigger's in-app template against event data + priority. */
344
366
  function renderInApp(template, vars, priority = "normal") {
367
+ const link = template.link === void 0 ? void 0 : interpolateRef(template.link, vars);
368
+ const entityType = template.entityType === void 0 ? void 0 : interpolateRef(template.entityType, vars);
369
+ const entityId = template.entityId === void 0 ? void 0 : interpolateRef(template.entityId, vars);
345
370
  return {
346
371
  title: interpolate$1(template.title, vars),
347
372
  message: interpolate$1(template.message, vars),
348
- ...template.link !== void 0 ? { link: interpolate$1(template.link, vars) } : {},
349
- ...template.entityType !== void 0 ? { entityType: interpolate$1(template.entityType, vars) } : {},
350
- ...template.entityId !== void 0 ? { entityId: interpolate$1(template.entityId, vars) } : {},
373
+ ...link !== void 0 ? { link } : {},
374
+ ...entityType !== void 0 ? { entityType } : {},
375
+ ...entityId !== void 0 ? { entityId } : {},
351
376
  priority
352
377
  };
353
378
  }
@@ -1288,11 +1313,20 @@ function candidateChannels(t, catalog) {
1288
1313
  * knows what it registered, so a connector that exists is offered and one that
1289
1314
  * does not is absent, with no edit in this package.
1290
1315
  *
1291
- * Falling back to the trigger's own declaration keeps a caller that supplies
1292
- * no catalog working: the degradation is "no NEW channels offered", never
1293
- * "no channels at all".
1316
+ * A genuine UNION, not `catalog ?? declared`. The fallback form silently DROPPED
1317
+ * a code-declared channel the catalog happened not to carry: `channels:
1318
+ * ['telegram']` under a catalog of `['email']` expanded to `[in-app, email]`,
1319
+ * so no telegram trigger existed to dispatch. And the loss was invisible from
1320
+ * the operator's side rather than merely quiet — `defaults.channels` is
1321
+ * `declaredChannels(t)`, which still contains telegram, so
1322
+ * `resolveEffectiveSettings` reported the channel ENABLED while nothing was
1323
+ * subscribed to deliver it. Supplying a catalog must only ever ADD reach; a
1324
+ * trigger's own declaration is the floor.
1325
+ *
1326
+ * With no catalog this still degrades to the declared set — "no NEW channels
1327
+ * offered", never "no channels at all".
1294
1328
  */
1295
- const configurable = (catalog?.list() ?? t.channels ?? []).filter((c) => c !== IN_APP_CHANNEL);
1329
+ const configurable = [...catalog?.list() ?? [], ...t.channels ?? []].filter((c) => c !== IN_APP_CHANNEL);
1296
1330
  const unique = [...new Set(configurable)];
1297
1331
  /**
1298
1332
  * A customer audience never gets `in-app`: buyers are not dashboard users, so
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@classytic/arc-notifications",
3
- "version": "0.6.0",
3
+ "version": "0.6.2",
4
4
  "description": "Notifications module for @classytic/arc — domain events → multi-channel notifications via @classytic/notifications, with DB-draftable templates, repo-backed delivery log, and a templates resource",
5
5
  "type": "module",
6
6
  "exports": {