@etainabl/nodejs-sdk 1.3.181 → 1.3.186

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
@@ -1445,10 +1445,10 @@ var init_ChecksumStream = __esm({
1445
1445
  }
1446
1446
  _read(size) {
1447
1447
  }
1448
- _write(chunk2, encoding, callback) {
1448
+ _write(chunk3, encoding, callback) {
1449
1449
  try {
1450
- this.checksum.update(chunk2);
1451
- this.push(chunk2);
1450
+ this.checksum.update(chunk3);
1451
+ this.push(chunk3);
1452
1452
  } catch (e7) {
1453
1453
  return callback(e7);
1454
1454
  }
@@ -1514,9 +1514,9 @@ var init_createChecksumStream_browser = __esm({
1514
1514
  const transform = new TransformStream({
1515
1515
  start() {
1516
1516
  },
1517
- async transform(chunk2, controller) {
1518
- checksum.update(chunk2);
1519
- controller.enqueue(chunk2);
1517
+ async transform(chunk3, controller) {
1518
+ checksum.update(chunk3);
1519
+ controller.enqueue(chunk3);
1520
1520
  },
1521
1521
  async flush(controller) {
1522
1522
  const digest = await checksum.digest();
@@ -1602,7 +1602,7 @@ function createBufferedReadableStream(upstream, size, logger2) {
1602
1602
  let mode = -1;
1603
1603
  const pull = async (controller) => {
1604
1604
  const { value, done } = await reader.read();
1605
- const chunk2 = value;
1605
+ const chunk3 = value;
1606
1606
  if (done) {
1607
1607
  if (mode !== -1) {
1608
1608
  const remainder = flush(buffers, mode);
@@ -1612,7 +1612,7 @@ function createBufferedReadableStream(upstream, size, logger2) {
1612
1612
  }
1613
1613
  controller.close();
1614
1614
  } else {
1615
- const chunkMode = modeOf(chunk2, false);
1615
+ const chunkMode = modeOf(chunk3, false);
1616
1616
  if (mode !== chunkMode) {
1617
1617
  if (mode >= 0) {
1618
1618
  controller.enqueue(flush(buffers, mode));
@@ -1620,16 +1620,16 @@ function createBufferedReadableStream(upstream, size, logger2) {
1620
1620
  mode = chunkMode;
1621
1621
  }
1622
1622
  if (mode === -1) {
1623
- controller.enqueue(chunk2);
1623
+ controller.enqueue(chunk3);
1624
1624
  return;
1625
1625
  }
1626
- const chunkSize = sizeOf(chunk2);
1626
+ const chunkSize = sizeOf(chunk3);
1627
1627
  bytesSeen += chunkSize;
1628
1628
  const bufferSize = sizeOf(buffers[mode]);
1629
1629
  if (chunkSize >= size && bufferSize === 0) {
1630
- controller.enqueue(chunk2);
1630
+ controller.enqueue(chunk3);
1631
1631
  } else {
1632
- const newSize = merge(buffers, mode, chunk2);
1632
+ const newSize = merge(buffers, mode, chunk3);
1633
1633
  if (!streamBufferingLoggedWarning && bytesSeen > size * 2) {
1634
1634
  streamBufferingLoggedWarning = true;
1635
1635
  logger2?.warn(`@smithy/util-stream - stream chunk size ${chunkSize} is below threshold of ${size}, automatically buffering.`);
@@ -1646,14 +1646,14 @@ function createBufferedReadableStream(upstream, size, logger2) {
1646
1646
  pull
1647
1647
  });
1648
1648
  }
1649
- function merge(buffers, mode, chunk2) {
1649
+ function merge(buffers, mode, chunk3) {
1650
1650
  switch (mode) {
1651
1651
  case 0:
1652
- buffers[0] += chunk2;
1652
+ buffers[0] += chunk3;
1653
1653
  return sizeOf(buffers[0]);
1654
1654
  case 1:
1655
1655
  case 2:
1656
- buffers[mode].push(chunk2);
1656
+ buffers[mode].push(chunk3);
1657
1657
  return sizeOf(buffers[mode]);
1658
1658
  }
1659
1659
  }
@@ -1669,17 +1669,17 @@ function flush(buffers, mode) {
1669
1669
  }
1670
1670
  throw new Error(`@smithy/util-stream - invalid index ${mode} given to flush()`);
1671
1671
  }
1672
- function sizeOf(chunk2) {
1673
- return chunk2?.byteLength ?? chunk2?.length ?? 0;
1672
+ function sizeOf(chunk3) {
1673
+ return chunk3?.byteLength ?? chunk3?.length ?? 0;
1674
1674
  }
1675
- function modeOf(chunk2, allowBuffer = true) {
1676
- if (allowBuffer && typeof Buffer !== "undefined" && chunk2 instanceof Buffer) {
1675
+ function modeOf(chunk3, allowBuffer = true) {
1676
+ if (allowBuffer && typeof Buffer !== "undefined" && chunk3 instanceof Buffer) {
1677
1677
  return 2;
1678
1678
  }
1679
- if (chunk2 instanceof Uint8Array) {
1679
+ if (chunk3 instanceof Uint8Array) {
1680
1680
  return 1;
1681
1681
  }
1682
- if (typeof chunk2 === "string") {
1682
+ if (typeof chunk3 === "string") {
1683
1683
  return 0;
1684
1684
  }
1685
1685
  return -1;
@@ -1706,8 +1706,8 @@ function createBufferedReadable(upstream, size, logger2) {
1706
1706
  new ByteArrayCollector((size2) => Buffer.from(new Uint8Array(size2)))
1707
1707
  ];
1708
1708
  let mode = -1;
1709
- upstream.on("data", (chunk2) => {
1710
- const chunkMode = modeOf(chunk2, true);
1709
+ upstream.on("data", (chunk3) => {
1710
+ const chunkMode = modeOf(chunk3, true);
1711
1711
  if (mode !== chunkMode) {
1712
1712
  if (mode >= 0) {
1713
1713
  downstream.push(flush(buffers, mode));
@@ -1715,16 +1715,16 @@ function createBufferedReadable(upstream, size, logger2) {
1715
1715
  mode = chunkMode;
1716
1716
  }
1717
1717
  if (mode === -1) {
1718
- downstream.push(chunk2);
1718
+ downstream.push(chunk3);
1719
1719
  return;
1720
1720
  }
1721
- const chunkSize = sizeOf(chunk2);
1721
+ const chunkSize = sizeOf(chunk3);
1722
1722
  bytesSeen += chunkSize;
1723
1723
  const bufferSize = sizeOf(buffers[mode]);
1724
1724
  if (chunkSize >= size && bufferSize === 0) {
1725
- downstream.push(chunk2);
1725
+ downstream.push(chunk3);
1726
1726
  } else {
1727
- const newSize = merge(buffers, mode, chunk2);
1727
+ const newSize = merge(buffers, mode, chunk3);
1728
1728
  if (!streamBufferingLoggedWarning && bytesSeen > size * 2) {
1729
1729
  streamBufferingLoggedWarning = true;
1730
1730
  logger2?.warn(`@smithy/util-stream - stream chunk size ${chunkSize} is below threshold of ${size}, automatically buffering.`);
@@ -1812,14 +1812,14 @@ async function headStream(stream, bytes) {
1812
1812
  reader.releaseLock();
1813
1813
  const collected = new Uint8Array(Math.min(bytes, byteLengthCounter));
1814
1814
  let offset = 0;
1815
- for (const chunk2 of chunks) {
1816
- if (chunk2.byteLength > collected.byteLength - offset) {
1817
- collected.set(chunk2.subarray(0, collected.byteLength - offset), offset);
1815
+ for (const chunk3 of chunks) {
1816
+ if (chunk3.byteLength > collected.byteLength - offset) {
1817
+ collected.set(chunk3.subarray(0, collected.byteLength - offset), offset);
1818
1818
  break;
1819
1819
  } else {
1820
- collected.set(chunk2, offset);
1820
+ collected.set(chunk3, offset);
1821
1821
  }
1822
- offset += chunk2.length;
1822
+ offset += chunk3.length;
1823
1823
  }
1824
1824
  return collected;
1825
1825
  }
@@ -1860,9 +1860,9 @@ var init_headStream = __esm({
1860
1860
  buffers = [];
1861
1861
  limit = Infinity;
1862
1862
  bytesBuffered = 0;
1863
- _write(chunk2, encoding, callback) {
1864
- this.buffers.push(chunk2);
1865
- this.bytesBuffered += chunk2.byteLength ?? 0;
1863
+ _write(chunk3, encoding, callback) {
1864
+ this.buffers.push(chunk3);
1865
+ this.bytesBuffered += chunk3.byteLength ?? 0;
1866
1866
  if (this.bytesBuffered >= this.limit) {
1867
1867
  const excess = this.bytesBuffered - this.limit;
1868
1868
  const tailBuffer = this.buffers[this.buffers.length - 1];
@@ -2395,8 +2395,8 @@ var init_collector = __esm({
2395
2395
  import_stream5 = require("stream");
2396
2396
  Collector2 = class extends import_stream5.Writable {
2397
2397
  bufferedBytes = [];
2398
- _write(chunk2, encoding, callback) {
2399
- this.bufferedBytes.push(chunk2);
2398
+ _write(chunk3, encoding, callback) {
2399
+ this.bufferedBytes.push(chunk3);
2400
2400
  callback();
2401
2401
  }
2402
2402
  };
@@ -2419,9 +2419,9 @@ async function collectReadableStream(stream) {
2419
2419
  }
2420
2420
  const collected = new Uint8Array(length);
2421
2421
  let offset = 0;
2422
- for (const chunk2 of chunks) {
2423
- collected.set(chunk2, offset);
2424
- offset += chunk2.length;
2422
+ for (const chunk3 of chunks) {
2423
+ collected.set(chunk3, offset);
2424
+ offset += chunk3.length;
2425
2425
  }
2426
2426
  return collected;
2427
2427
  }
@@ -2490,9 +2490,9 @@ async function collectStream(stream) {
2490
2490
  }
2491
2491
  const collected = new Uint8Array(length);
2492
2492
  let offset = 0;
2493
- for (const chunk2 of chunks) {
2494
- collected.set(chunk2, offset);
2495
- offset += chunk2.length;
2493
+ for (const chunk3 of chunks) {
2494
+ collected.set(chunk3, offset);
2495
+ offset += chunk3.length;
2496
2496
  }
2497
2497
  return collected;
2498
2498
  }
@@ -7137,9 +7137,9 @@ var init_HeaderFormatter = __esm({
7137
7137
  }
7138
7138
  const out = new Uint8Array(chunks.reduce((carry, bytes) => carry + bytes.byteLength, 0));
7139
7139
  let position = 0;
7140
- for (const chunk2 of chunks) {
7141
- out.set(chunk2, position);
7142
- position += chunk2.byteLength;
7140
+ for (const chunk3 of chunks) {
7141
+ out.set(chunk3, position);
7142
+ position += chunk3.byteLength;
7143
7143
  }
7144
7144
  return out;
7145
7145
  }
@@ -11519,8 +11519,8 @@ function httpRequest(options) {
11519
11519
  req.destroy();
11520
11520
  }
11521
11521
  const chunks = [];
11522
- res.on("data", (chunk2) => {
11523
- chunks.push(chunk2);
11522
+ res.on("data", (chunk3) => {
11523
+ chunks.push(chunk3);
11524
11524
  });
11525
11525
  res.on("end", () => {
11526
11526
  resolve(import_buffer2.Buffer.concat(chunks));
@@ -19822,7 +19822,7 @@ var require_lodash = __commonJS({
19822
19822
  result2.__values__ = wrapper.__values__;
19823
19823
  return result2;
19824
19824
  }
19825
- function chunk2(array, size2, guard) {
19825
+ function chunk3(array, size2, guard) {
19826
19826
  if (guard ? isIterateeCall(array, size2, guard) : size2 === undefined2) {
19827
19827
  size2 = 1;
19828
19828
  } else {
@@ -21694,7 +21694,7 @@ var require_lodash = __commonJS({
21694
21694
  lodash.bindKey = bindKey;
21695
21695
  lodash.castArray = castArray;
21696
21696
  lodash.chain = chain2;
21697
- lodash.chunk = chunk2;
21697
+ lodash.chunk = chunk3;
21698
21698
  lodash.compact = compact;
21699
21699
  lodash.concat = concat;
21700
21700
  lodash.cond = cond;
@@ -22178,7 +22178,6 @@ __export(index_exports, {
22178
22178
  EventNamesByCategory: () => EventNamesByCategory,
22179
22179
  NotificationCategoryList: () => NotificationCategoryList,
22180
22180
  NotificationDefaultChannels: () => NotificationDefaultChannels,
22181
- NotificationTemplateCategories: () => NotificationTemplateCategories,
22182
22181
  ObjectId: () => import_mongodb2.ObjectId,
22183
22182
  api: () => api_default,
22184
22183
  consumption: () => consumption_exports,
@@ -22449,19 +22448,6 @@ var api_default = (auth, instanceOptions = {}) => {
22449
22448
  removeAccount: factory.remove(etainablApi, "accounts"),
22450
22449
  getAccountSchema: factory.get(etainablApi, "accounts", "schema"),
22451
22450
  invalidateAccountCache: factory.customWithId(etainablApi, "put", "accounts", "invalidate-cache"),
22452
- // alert triggers
22453
- getAlertTrigger: factory.getWithId(etainablApi, "alert-triggers"),
22454
- listAlertTriggers: factory.list(etainablApi, "alert-triggers"),
22455
- updateAlertTrigger: factory.update(etainablApi, "alert-triggers"),
22456
- createAlertTrigger: factory.create(etainablApi, "alert-triggers"),
22457
- removeAlertTrigger: factory.remove(etainablApi, "alert-triggers"),
22458
- bulkUpdateAlertTriggers: factory.create(
22459
- etainablApi,
22460
- "alert-triggers",
22461
- "bulk-update"
22462
- ),
22463
- retryAlertTrigger: factory.customWithId(etainablApi, "post", "alert-triggers", "retry"),
22464
- cancelAlertTrigger: factory.customWithId(etainablApi, "post", "alert-triggers", "cancel"),
22465
22451
  // custom alert triggers
22466
22452
  getCustomAlertTrigger: factory.getWithId(etainablApi, "custom-alert-triggers"),
22467
22453
  listCustomAlertTriggers: factory.list(etainablApi, "custom-alert-triggers"),
@@ -22860,28 +22846,28 @@ var emailTemplate_default = (options) => {
22860
22846
  var import_mjml = __toESM(require("mjml"), 1);
22861
22847
  var severityConfig = {
22862
22848
  error: {
22863
- color: "#DC2626",
22864
- bgColor: "#FEE2E2",
22849
+ color: "#FFFFFF",
22850
+ bgColor: "#e04f1a",
22865
22851
  label: "ERROR"
22866
22852
  },
22867
22853
  success: {
22868
- color: "#EA580C",
22869
- bgColor: "#23ae2aff",
22854
+ color: "#FFFFFF",
22855
+ bgColor: "#82b54b",
22870
22856
  label: "SUCCESS"
22871
22857
  },
22872
22858
  warning: {
22873
- color: "#F59E0B",
22874
- bgColor: "#FEF3C7",
22859
+ color: "#FFFFFF",
22860
+ bgColor: "#ff9f43",
22875
22861
  label: "WARNING"
22876
22862
  },
22877
22863
  tip: {
22878
- color: "#3B82F6",
22879
- bgColor: "#DBEAFE",
22864
+ color: "#FFFFFF",
22865
+ bgColor: "#00bad1",
22880
22866
  label: "TIP"
22881
22867
  },
22882
22868
  info: {
22883
- color: "#5ECFB1",
22884
- bgColor: "#D1FAE5",
22869
+ color: "#FFFFFF",
22870
+ bgColor: "#5ECFB1",
22885
22871
  label: "INFO"
22886
22872
  }
22887
22873
  };
@@ -22889,13 +22875,17 @@ function generateNotificationEmail(options) {
22889
22875
  if (!process.env.ETAINABL_UI_URL) {
22890
22876
  throw new Error("ETAINABL_UI_URL environment variable is not set");
22891
22877
  }
22892
- const { title, message, severity = "info", category = "Notification", actions = [], metadata = {} } = options;
22878
+ const { title, message, severity = "info", category = "Notification", actions = [], metadata = {}, items = [] } = options;
22893
22879
  const config = severityConfig[severity];
22880
+ const isSingleItem = items.length === 1;
22881
+ const hasMultipleItems = items.length > 1;
22882
+ const mainTitle = isSingleItem && items[0]?.message ? items[0].message : message;
22883
+ const subtitle = isSingleItem ? message : title;
22894
22884
  const mjmlTemplate = `
22895
22885
  <mjml>
22896
22886
  <mj-head>
22897
- <mj-title>${message} - Etainabl</mj-title>
22898
- <mj-preview>${title}</mj-preview>
22887
+ <mj-title>${mainTitle}</mj-title>
22888
+ <mj-preview>${subtitle}</mj-preview>
22899
22889
 
22900
22890
  <mj-attributes>
22901
22891
  <mj-all font-family="-apple-system, BlinkMacSystemFont, 'Segoe UI', 'Roboto', 'Helvetica', 'Arial', sans-serif" />
@@ -22906,9 +22896,9 @@ function generateNotificationEmail(options) {
22906
22896
  <mj-style inline="inline">
22907
22897
  .severity-badge {
22908
22898
  display: inline-block;
22909
- padding: 4px 12px;
22910
- border-radius: 12px;
22911
- font-size: 11px;
22899
+ padding: 2px 6px;
22900
+ border-radius: 6px;
22901
+ font-size: 9px;
22912
22902
  font-weight: 600;
22913
22903
  letter-spacing: 0.5px;
22914
22904
  background-color: ${config.bgColor};
@@ -22950,7 +22940,7 @@ function generateNotificationEmail(options) {
22950
22940
  </mj-section>
22951
22941
 
22952
22942
  <!-- Spacer -->
22953
- <mj-section background-color="#F3F4F6" padding="20px 0">
22943
+ <mj-section background-color="#F3F4F6" padding="6px 0">
22954
22944
  <mj-column>
22955
22945
  <mj-text></mj-text>
22956
22946
  </mj-column>
@@ -22973,24 +22963,61 @@ function generateNotificationEmail(options) {
22973
22963
  </mj-column>
22974
22964
  </mj-section>
22975
22965
 
22976
- <!-- Message (Main Title) -->
22966
+ <!-- Main Title -->
22977
22967
  <mj-section padding="0 32px 8px 32px">
22978
22968
  <mj-column>
22979
22969
  <mj-text font-size="24px" font-weight="600" color="#1F2937" line-height="1.3" padding="0">
22980
- ${message}
22970
+ ${mainTitle}
22981
22971
  </mj-text>
22982
22972
  </mj-column>
22983
22973
  </mj-section>
22984
22974
 
22985
- <!-- Title (Subtitle) -->
22975
+ <!-- Subtitle -->
22986
22976
  <mj-section padding="0 32px 24px 32px">
22987
22977
  <mj-column>
22988
22978
  <mj-text font-size="14px" color="#6B7280" padding="0">
22989
- ${title}
22979
+ ${subtitle}
22990
22980
  </mj-text>
22991
22981
  </mj-column>
22992
22982
  </mj-section>
22993
22983
 
22984
+ ${hasMultipleItems ? (() => {
22985
+ const maxItems = 3;
22986
+ const displayItems = items.slice(0, maxItems);
22987
+ const remainingCount = items.length - maxItems;
22988
+ return `
22989
+ <!-- Items List -->
22990
+ <mj-section padding="0 32px 24px 32px">
22991
+ <mj-column>
22992
+ <mj-text padding="0">
22993
+ <table style="width: 100%; border-collapse: collapse;">
22994
+ ${displayItems.map(
22995
+ (item) => `
22996
+ <tr>
22997
+ <td style="padding: 0 0 8px 0;">
22998
+ <table style="width: 100%; border-collapse: collapse; background-color: #F9FAFB; border-radius: 6px; overflow: hidden;">
22999
+ <tr>
23000
+ <td style="width: 6px; background-color: #5ECFB1; border-radius: 10px;"></td>
23001
+ <td style="padding: 16px;">
23002
+ <span style="color: #1F2937; font-size: 14px; line-height: 1.5;">${item.message}</span>
23003
+ </td>
23004
+ </tr>
23005
+ </table>
23006
+ </td>
23007
+ </tr>`
23008
+ ).join("")}${remainingCount > 0 ? `
23009
+ <tr>
23010
+ <td style="padding: 8px 0 0 0;">
23011
+ <span style="color: #6B7280; font-size: 13px; font-style: italic;">and ${remainingCount} more item${remainingCount === 1 ? "" : "s"}...</span>
23012
+ </td>
23013
+ </tr>` : ""}
23014
+ </table>
23015
+ </mj-text>
23016
+ </mj-column>
23017
+ </mj-section>
23018
+ `;
23019
+ })() : ""}
23020
+
22994
23021
  ${Object.keys(metadata).length > 0 ? `
22995
23022
  <!-- Metadata Section -->
22996
23023
  <mj-section padding="0 32px 24px 32px">
@@ -23042,7 +23069,7 @@ function generateNotificationEmail(options) {
23042
23069
  <mj-section padding="0 32px 24px 32px">
23043
23070
  <mj-column>
23044
23071
  <mj-button
23045
- background-color="#1F2937"
23072
+ background-color="#65c198"
23046
23073
  color="#FFFFFF"
23047
23074
  border-radius="6px"
23048
23075
  font-weight="600"
@@ -23087,7 +23114,7 @@ function generateNotificationEmail(options) {
23087
23114
  <a href="${process.env.ETAINABL_UI_URL}/messages/notifications" style="color: #5ECFB1; text-decoration: none;">Manage notification preferences</a>
23088
23115
  </mj-text>
23089
23116
  <mj-text align="center" font-size="11px" color="#9CA3AF" padding-top="16px">
23090
- \xA9 ${(/* @__PURE__ */ new Date()).getFullYear()} Etainabl. All rights reserved.
23117
+ \xA9 ${(/* @__PURE__ */ new Date()).getFullYear()} Etainabl Ltd. All rights reserved.
23091
23118
  </mj-text>
23092
23119
  </mj-column>
23093
23120
  </mj-section>
@@ -24773,9 +24800,7 @@ async function sendNotificationMessageBatch({
24773
24800
  const results = await import_bluebird.default.mapSeries(chunks, async (batch) => {
24774
24801
  const entries = batch.map((msg, index) => ({
24775
24802
  Id: `msg-${index}`,
24776
- MessageBody: JSON.stringify(
24777
- createNotificationMessage(msg.eventName, msg.context, msg.input)
24778
- )
24803
+ MessageBody: JSON.stringify(createNotificationMessage(msg.eventName, msg.context, msg.input))
24779
24804
  }));
24780
24805
  const command = new SendMessageBatchCommand({
24781
24806
  QueueUrl: queueUrl,
@@ -25077,7 +25102,11 @@ __export(utils_exports, {
25077
25102
  automationSources: () => automationSources,
25078
25103
  getMeterPointNumberBottomLine: () => getMeterPointNumberBottomLine,
25079
25104
  isTransientError: () => isTransientError2,
25105
+ paginatedFetch: () => paginatedFetch,
25106
+ resolveNotificationPreferences: () => resolveNotificationPreferences,
25107
+ sendToSqsBatched: () => sendToSqsBatched,
25080
25108
  units: () => units,
25109
+ updateBatchJobStatus: () => updateBatchJobStatus,
25081
25110
  utilityTypes: () => utilityTypes,
25082
25111
  wasteCategories: () => wasteCategories
25083
25112
  });
@@ -25384,6 +25413,64 @@ var getMeterPointNumberBottomLine = (meterPointNumber) => {
25384
25413
  return "";
25385
25414
  };
25386
25415
 
25416
+ // src/utils/job.ts
25417
+ async function updateBatchJobStatus({
25418
+ etnApi,
25419
+ automationRun,
25420
+ accountKey,
25421
+ status,
25422
+ errorMessage
25423
+ }) {
25424
+ if (!automationRun) return;
25425
+ if (automationRun.category !== "account") return;
25426
+ const jobKey = automationRun.jobId;
25427
+ const resolvedAccountKey = accountKey ?? automationRun.accountResults?.[0]?.id?.toString();
25428
+ if (!jobKey || !resolvedAccountKey) return;
25429
+ let jobEntry;
25430
+ try {
25431
+ jobEntry = await etnApi.getJob(jobKey);
25432
+ } catch (error) {
25433
+ console.error("Failed to fetch job for status update", {
25434
+ jobKey,
25435
+ accountKey: resolvedAccountKey,
25436
+ error
25437
+ });
25438
+ return;
25439
+ }
25440
+ if (!jobEntry || jobEntry.status === "completed" || jobEntry.status === "failed") return;
25441
+ const metadata = jobEntry.metadata ?? {};
25442
+ const completedAccountKeys = new Set(metadata.completedAccountKeys ?? []);
25443
+ const failedAccountKeys = new Set(metadata.failedAccountKeys ?? []);
25444
+ const { accountCount } = metadata;
25445
+ const now = (/* @__PURE__ */ new Date()).toISOString();
25446
+ if (status === "failed") {
25447
+ failedAccountKeys.add(resolvedAccountKey);
25448
+ } else {
25449
+ completedAccountKeys.add(resolvedAccountKey);
25450
+ }
25451
+ const updatePayload = {
25452
+ metadata: {
25453
+ ...metadata,
25454
+ completedAccountKeys: Array.from(completedAccountKeys),
25455
+ failedAccountKeys: Array.from(failedAccountKeys),
25456
+ completedCount: completedAccountKeys.size,
25457
+ failedCount: failedAccountKeys.size
25458
+ }
25459
+ };
25460
+ const totalProcessed = completedAccountKeys.size + failedAccountKeys.size;
25461
+ const hasFailures = failedAccountKeys.size > 0;
25462
+ if (status === "failed") {
25463
+ updatePayload.metadata = { ...updatePayload.metadata, result: "failed" };
25464
+ updatePayload.error = errorMessage || "Automation run failed";
25465
+ }
25466
+ if (accountCount && totalProcessed >= accountCount) {
25467
+ updatePayload.status = "completed";
25468
+ updatePayload.completedAt = now;
25469
+ updatePayload.metadata = { ...updatePayload.metadata, result: hasFailures ? "failed" : "success" };
25470
+ }
25471
+ await etnApi.updateJob(jobEntry._id, updatePayload);
25472
+ }
25473
+
25387
25474
  // node_modules/@aws-sdk/middleware-expect-continue/dist-es/index.js
25388
25475
  init_dist_es2();
25389
25476
  function addExpectContinueMiddleware(options) {
@@ -28766,9 +28853,9 @@ var HeaderMarshaller = class {
28766
28853
  }
28767
28854
  const out = new Uint8Array(chunks.reduce((carry, bytes) => carry + bytes.byteLength, 0));
28768
28855
  let position = 0;
28769
- for (const chunk2 of chunks) {
28770
- out.set(chunk2, position);
28771
- position += chunk2.byteLength;
28856
+ for (const chunk3 of chunks) {
28857
+ out.set(chunk3, position);
28858
+ position += chunk3.byteLength;
28772
28859
  }
28773
28860
  return out;
28774
28861
  }
@@ -29088,8 +29175,8 @@ var SmithyMessageEncoderStream = class {
29088
29175
  return this.asyncIterator();
29089
29176
  }
29090
29177
  async *asyncIterator() {
29091
- for await (const chunk2 of this.options.inputStream) {
29092
- const payloadBuf = this.options.serializer(chunk2);
29178
+ for await (const chunk3 of this.options.inputStream) {
29179
+ const payloadBuf = this.options.serializer(chunk3);
29093
29180
  yield payloadBuf;
29094
29181
  }
29095
29182
  }
@@ -29277,9 +29364,9 @@ var HashCalculator = class extends import_stream10.Writable {
29277
29364
  super(options);
29278
29365
  this.hash = hash;
29279
29366
  }
29280
- _write(chunk2, encoding, callback) {
29367
+ _write(chunk3, encoding, callback) {
29281
29368
  try {
29282
- this.hash.update(toUint8Array(chunk2));
29369
+ this.hash.update(toUint8Array(chunk3));
29283
29370
  } catch (err) {
29284
29371
  return callback(err);
29285
29372
  }
@@ -29901,6 +29988,128 @@ function isTransientError2(error) {
29901
29988
  return false;
29902
29989
  }
29903
29990
 
29991
+ // src/utils/pagination.ts
29992
+ var paginatedFetch = async (fetchPage, options = {}) => {
29993
+ const { pageSize = 100 } = options;
29994
+ const fetchAllPages = async (skip, accumulated) => {
29995
+ const response = await fetchPage({ $limit: pageSize, $skip: skip });
29996
+ accumulated.push(...response.data);
29997
+ return response.data.length < pageSize ? accumulated : fetchAllPages(skip + pageSize, accumulated);
29998
+ };
29999
+ return fetchAllPages(0, []);
30000
+ };
30001
+
30002
+ // src/utils/sqs.ts
30003
+ var import_bluebird2 = __toESM(require("bluebird"), 1);
30004
+ var import_lodash2 = __toESM(require_lodash(), 1);
30005
+ var sendToSqsBatched = async (messages, options) => {
30006
+ const { queueUrl, concurrency = 5, batchSize = 10 } = options;
30007
+ const sqsClient = options.sqsClient ?? new SQSClient({ region: "eu-west-1" });
30008
+ if (messages.length === 0) {
30009
+ return { successful: 0, failed: 0 };
30010
+ }
30011
+ const batches = (0, import_lodash2.chunk)(messages, batchSize);
30012
+ let successful = 0;
30013
+ let failed = 0;
30014
+ await import_bluebird2.default.map(
30015
+ batches,
30016
+ async (batch, batchIndex) => {
30017
+ const entries = batch.map((message, index) => ({
30018
+ Id: message.id ?? `msg_${batchIndex}_${index}`,
30019
+ MessageBody: JSON.stringify(message.body),
30020
+ ...message.attributes && { MessageAttributes: message.attributes }
30021
+ }));
30022
+ const response = await sqsClient.send(
30023
+ new SendMessageBatchCommand({
30024
+ QueueUrl: queueUrl,
30025
+ Entries: entries
30026
+ })
30027
+ );
30028
+ successful += response.Successful?.length ?? 0;
30029
+ if (response.Failed && response.Failed.length > 0) {
30030
+ failed += response.Failed.length;
30031
+ throw new Error(`Failed to send ${response.Failed.length} SQS messages in batch ${batchIndex}`);
30032
+ }
30033
+ },
30034
+ { concurrency }
30035
+ );
30036
+ return { successful, failed };
30037
+ };
30038
+
30039
+ // src/types/notification.ts
30040
+ var NotificationCategoryList = [
30041
+ "automation",
30042
+ "report",
30043
+ "team",
30044
+ "dataQuality",
30045
+ "discussion",
30046
+ "scraper"
30047
+ ];
30048
+ var NotificationDefaultChannels = {
30049
+ inApp: true,
30050
+ email: false,
30051
+ sms: false
30052
+ };
30053
+
30054
+ // src/utils/notification.ts
30055
+ function extractTemplates(source) {
30056
+ if (!source) return {};
30057
+ const templates = source.templates !== void 0 ? source.templates : source;
30058
+ if (templates && typeof templates === "object" && !Array.isArray(templates)) {
30059
+ return { ...templates };
30060
+ }
30061
+ return {};
30062
+ }
30063
+ function resolveNotificationPreferences(userPreferences, systemTemplates) {
30064
+ const prefs = userPreferences || {};
30065
+ const userCategories = prefs.categories || {};
30066
+ const userChannels = prefs.channels || {};
30067
+ const globalChannels = {
30068
+ inApp: userChannels.inApp !== void 0 ? userChannels.inApp : NotificationDefaultChannels.inApp,
30069
+ sms: userChannels.sms !== void 0 ? userChannels.sms : NotificationDefaultChannels.sms,
30070
+ email: userChannels.email !== void 0 ? userChannels.email : NotificationDefaultChannels.email
30071
+ };
30072
+ const convertTemplateToStringId = (template) => ({
30073
+ ...template,
30074
+ _id: template._id.toString()
30075
+ });
30076
+ const templatesByCategory = systemTemplates.reduce(
30077
+ (acc, template) => {
30078
+ const cat = template.category || "uncategorised";
30079
+ if (!acc[cat]) acc[cat] = [];
30080
+ acc[cat].push(convertTemplateToStringId(template));
30081
+ return acc;
30082
+ },
30083
+ {}
30084
+ );
30085
+ const categories = NotificationCategoryList.reduce(
30086
+ (acc, category) => {
30087
+ const userCategory = userCategories[category] || {};
30088
+ const userCategoryChannels = userCategory.channels || {};
30089
+ const userTemplates = extractTemplates(userCategory);
30090
+ const templateList = templatesByCategory[category] || [];
30091
+ acc[category] = {
30092
+ channels: {
30093
+ inApp: userCategoryChannels.inApp !== void 0 ? userCategoryChannels.inApp : NotificationDefaultChannels.inApp,
30094
+ sms: userCategoryChannels.sms !== void 0 ? userCategoryChannels.sms : NotificationDefaultChannels.sms,
30095
+ email: userCategoryChannels.email !== void 0 ? userCategoryChannels.email : NotificationDefaultChannels.email
30096
+ },
30097
+ templates: templateList.map((template) => ({
30098
+ _id: template._id,
30099
+ name: template.name,
30100
+ description: template.description,
30101
+ category: template.category,
30102
+ severity: template.severity,
30103
+ enabled: userTemplates[template._id] !== void 0 ? userTemplates[template._id] : template.enabledByDefault ?? true
30104
+ }))
30105
+ };
30106
+ return acc;
30107
+ },
30108
+ {}
30109
+ );
30110
+ return { channels: globalChannels, categories };
30111
+ }
30112
+
29904
30113
  // src/openai/index.ts
29905
30114
  var openai_exports = {};
29906
30115
  __export(openai_exports, {
@@ -32056,7 +32265,16 @@ var errorCodeMap = {
32056
32265
  unique: true
32057
32266
  }
32058
32267
  };
32059
- var dataFetchersIds = ["bacnet", "solis", "solarman", "gridfetch", "smartflow", "smartvatten", "beringar", "4dmonitoring"];
32268
+ var dataFetchersIds = [
32269
+ "bacnet",
32270
+ "solis",
32271
+ "solarman",
32272
+ "gridfetch",
32273
+ "smartflow",
32274
+ "smartvatten",
32275
+ "beringar",
32276
+ "4dmonitoring"
32277
+ ];
32060
32278
  function sendEmail(lambdaSource, context, error, destinations) {
32061
32279
  const sesClient = new SESClient({ region: "eu-west-1" });
32062
32280
  const template = emailTemplate_default({
@@ -32185,6 +32403,13 @@ async function handleError({ context, etnApi, automationRun, error, lambdaSource
32185
32403
  if (automationRun.category === "account" && accountId) {
32186
32404
  await etnApi.updateAccountStatusForAutomation(automation._id, accountId, { status: "error", error: error.message });
32187
32405
  }
32406
+ await updateBatchJobStatus({
32407
+ etnApi,
32408
+ automationRun,
32409
+ accountKey: accountId,
32410
+ status: "failed",
32411
+ errorMessage: error.message
32412
+ });
32188
32413
  }
32189
32414
  async function deleteMessage(queueUrl, receiptHandle, sqsClient) {
32190
32415
  if (!queueUrl || !receiptHandle) {
@@ -32207,44 +32432,20 @@ var scrapers_exports = {};
32207
32432
  // src/types/index.ts
32208
32433
  var import_mongodb2 = require("mongodb");
32209
32434
 
32210
- // src/types/notification.ts
32211
- var NotificationCategoryList = [
32212
- "automation",
32213
- "report",
32214
- "team",
32215
- "dataQuality",
32216
- "discussion",
32217
- "scraper"
32218
- ];
32219
- var NotificationDefaultChannels = {
32220
- inApp: true,
32221
- email: false,
32222
- sms: false
32223
- };
32224
-
32225
32435
  // src/types/notificationTemplate.ts
32226
- var NotificationTemplateCategories = [
32227
- "automation",
32228
- "report",
32229
- "team",
32230
- "dataQuality",
32231
- "discussion",
32232
- "scraper"
32233
- ];
32234
32436
  var EventNamesByCategory = {
32235
- automation: ["automationFailed", "runCompleted", "hasnotRun", "fileNotExpected"],
32437
+ automation: ["automationFailed", "automationCompleted", "automationOverdue", "automationExtractorOverdue"],
32236
32438
  report: ["reportSuccess", "reportFailed"],
32237
32439
  team: ["newUserSignIn"],
32238
- dataQuality: ["thresholdBreached", "approachingThreshold", "readingOverdue", "readingDueSoon", "contractRenewalUpcoming", "contractRenewalOverdue"],
32239
- discussion: ["commentAdded", "mentionInComment"],
32240
- scraper: ["scraperLoginError", "scraperHasnotRun", "scraperRunCompleted"]
32440
+ dataQuality: ["invoiceOverdue", "contractOverdue", "contractDueSoon", "readingOverdue", "readingDueSoon"],
32441
+ discussion: ["newDiscussionReply", "userAssignedToDiscussion", "discussionMessageDue"],
32442
+ scraper: ["scraperLoginError", "scraperHasNotRun", "scraperRunCompleted"]
32241
32443
  };
32242
32444
  // Annotate the CommonJS export names for ESM import in node:
32243
32445
  0 && (module.exports = {
32244
32446
  EventNamesByCategory,
32245
32447
  NotificationCategoryList,
32246
32448
  NotificationDefaultChannels,
32247
- NotificationTemplateCategories,
32248
32449
  ObjectId,
32249
32450
  api,
32250
32451
  consumption,