@clipboard-health/notifications 0.6.0 → 0.7.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.
Files changed (39) hide show
  1. package/README.md +133 -43
  2. package/package.json +5 -5
  3. package/src/index.d.ts +2 -2
  4. package/src/index.js +2 -2
  5. package/src/index.js.map +1 -1
  6. package/src/lib/idempotencyKey.d.ts +46 -0
  7. package/src/lib/idempotencyKey.js +32 -0
  8. package/src/lib/idempotencyKey.js.map +1 -0
  9. package/src/lib/internal/chunkRecipients.d.ts +18 -0
  10. package/src/lib/internal/chunkRecipients.js +28 -0
  11. package/src/lib/internal/chunkRecipients.js.map +1 -0
  12. package/src/lib/internal/createDeterministicHash.d.ts +13 -0
  13. package/src/lib/internal/createDeterministicHash.js +23 -0
  14. package/src/lib/internal/createDeterministicHash.js.map +1 -0
  15. package/src/lib/internal/createTriggerLogParams.d.ts +1 -1
  16. package/src/lib/internal/createTriggerLogParams.js +2 -2
  17. package/src/lib/internal/createTriggerLogParams.js.map +1 -1
  18. package/src/lib/internal/createTriggerTraceOptions.js +2 -2
  19. package/src/lib/internal/createTriggerTraceOptions.js.map +1 -1
  20. package/src/lib/internal/idempotencyKeyDoNotImportOutsideNotificationsLibrary.d.ts +35 -0
  21. package/src/lib/internal/idempotencyKeyDoNotImportOutsideNotificationsLibrary.js +43 -0
  22. package/src/lib/internal/idempotencyKeyDoNotImportOutsideNotificationsLibrary.js.map +1 -0
  23. package/src/lib/internal/idempotentKnock.d.ts +5 -0
  24. package/src/lib/internal/idempotentKnock.js +5 -0
  25. package/src/lib/internal/idempotentKnock.js.map +1 -1
  26. package/src/lib/notificationClient.d.ts +30 -37
  27. package/src/lib/notificationClient.js +35 -35
  28. package/src/lib/notificationClient.js.map +1 -1
  29. package/src/lib/notificationJobEnqueuer.d.ts +96 -0
  30. package/src/lib/notificationJobEnqueuer.js +101 -0
  31. package/src/lib/notificationJobEnqueuer.js.map +1 -0
  32. package/src/lib/types.d.ts +17 -20
  33. package/src/lib/types.js.map +1 -1
  34. package/src/lib/chunkRecipients.d.ts +0 -7
  35. package/src/lib/chunkRecipients.js +0 -18
  36. package/src/lib/chunkRecipients.js.map +0 -1
  37. package/src/lib/createIdempotencyKey.d.ts +0 -17
  38. package/src/lib/createIdempotencyKey.js +0 -28
  39. package/src/lib/createIdempotencyKey.js.map +0 -1
package/README.md CHANGED
@@ -6,7 +6,6 @@ Send notifications through third-party providers.
6
6
 
7
7
  - [Install](#install)
8
8
  - [Usage](#usage)
9
- - [`NotificationClient`](#notificationclient)
10
9
  - [Local development commands](#local-development-commands)
11
10
 
12
11
  ## Install
@@ -17,49 +16,140 @@ npm install @clipboard-health/notifications
17
16
 
18
17
  ## Usage
19
18
 
20
- ### `NotificationClient`
21
-
22
- <embedex source="packages/notifications/examples/notificationClient.ts">
23
-
24
- ```ts
25
- import { NotificationClient, type Span } from "@clipboard-health/notifications";
26
- import { isSuccess } from "@clipboard-health/util-ts";
27
-
28
- const client = new NotificationClient({
29
- apiKey: "test-api-key",
30
- logger: {
31
- info: console.log,
32
- warn: console.warn,
33
- error: console.error,
34
- } as const,
35
- tracer: {
36
- trace: <T>(_name: string, _options: unknown, fun: (span?: Span | undefined) => T): T => fun(),
37
- },
38
- });
39
-
40
- async function triggerNotification(job: { attemptsCount: number }) {
41
- const result = await client.trigger({
42
- attempt: (job?.attemptsCount ?? 0) + 1,
43
- body: {
44
- recipients: ["user-1"],
45
- data: { favoriteColor: "blue", secret: "2" },
46
- },
47
- expiresAt: new Date(Date.now() + 300_000), // 5 minutes
48
- idempotencyKey: "welcome-user-4",
49
- key: "welcome-email",
50
- keysToRedact: ["secret"],
51
- });
52
-
53
- if (isSuccess(result)) {
54
- console.log("Notification sent:", result.value.id);
55
- }
56
- }
57
-
58
- // eslint-disable-next-line unicorn/prefer-top-level-await
59
- void triggerNotification({ attemptsCount: 0 });
60
- ```
19
+ 1. Export a `NotificationJobEnqueuer` instance:
20
+
21
+ <embedex source="packages/notifications/examples/notificationJobEnqueuer.ts">
22
+
23
+ ```ts
24
+ import { NotificationJobEnqueuer } from "@clipboard-health/notifications";
25
+
26
+ import { BackgroundJobsService } from "./setup";
27
+
28
+ // Provide this in your microservice.
29
+ export const notificationJobEnqueuer = new NotificationJobEnqueuer({
30
+ adapter: new BackgroundJobsService(),
31
+ });
32
+ ```
33
+
34
+ </embedex>
35
+
36
+ 1. Enqueue your job:
37
+
38
+ <embedex source="packages/notifications/examples/enqueueNotificationJob.ts">
39
+
40
+ ```ts
41
+ import { IdempotencyKey } from "@clipboard-health/notifications";
42
+
43
+ import { ExampleNotificationJob } from "./exampleNotification.job";
44
+ import { notificationJobEnqueuer } from "./notificationJobEnqueuer";
45
+
46
+ async function enqueueNotificationJob() {
47
+ await notificationJobEnqueuer.enqueueOneOrMore(ExampleNotificationJob, {
48
+ // Set expiresAt at enqueue-time so it remains stable across job retries.
49
+ expiresAt: minutesFromNow(60),
50
+ // Set idempotencyKey at enqueue-time so it remains stable across job retries.
51
+ idempotencyKey: new IdempotencyKey({
52
+ resourceId: "event-123",
53
+ }),
54
+ // Set recipients at enqueue-time so they respect our notification provider's limits.
55
+ recipients: ["user-1"],
56
+
57
+ workflowKey: "event-starting-reminder",
58
+
59
+ // Any additional enqueue-time data passed to the job:
60
+ workplaceId: "workplace-123",
61
+ });
62
+ }
63
+
64
+ // eslint-disable-next-line unicorn/prefer-top-level-await
65
+ void enqueueNotificationJob();
66
+
67
+ function minutesFromNow(minutes: number) {
68
+ return new Date(Date.now() + minutes * 60_000);
69
+ }
70
+ ```
71
+
72
+ </embedex>
73
+
74
+ 1. Implement your job, which should be minimal, calling off to a service to send the actual notification:
75
+
76
+ <embedex source="packages/notifications/examples/exampleNotification.job.ts">
77
+
78
+ ```ts
79
+ import { type BaseHandler } from "@clipboard-health/background-jobs-adapter";
80
+ import {
81
+ errorsInResult,
82
+ type NotificationEnqueueData,
83
+ type NotificationJobData,
84
+ RETRYABLE_ERRORS,
85
+ } from "@clipboard-health/notifications";
86
+ import { toError } from "@clipboard-health/util-ts";
87
+
88
+ import { type ExampleNotificationService } from "./exampleNotification.service";
89
+
90
+ interface ExampleNotificationData {
91
+ workplaceId: string;
92
+ }
93
+
94
+ export type ExampleNotificationEnqueueData = NotificationEnqueueData & ExampleNotificationData;
95
+ export type ExampleNotificationJobData = NotificationJobData & ExampleNotificationData;
96
+
97
+ export class ExampleNotificationJob implements BaseHandler<ExampleNotificationJobData> {
98
+ constructor(private readonly service: ExampleNotificationService) {}
99
+
100
+ async perform(data: ExampleNotificationJobData, job: { attemptsCount: number }) {
101
+ const result = await this.service.sendNotification({
102
+ ...data,
103
+ // Include the job's attempts count for debugging, this is called `retryAttempts` in `background-jobs-postgres`.
104
+ attempt: job.attemptsCount + 1,
105
+ });
106
+
107
+ if (errorsInResult(result, RETRYABLE_ERRORS)) {
108
+ throw toError(result.error);
109
+ }
110
+ }
111
+ }
112
+ ```
113
+
114
+ </embedex>
115
+
116
+ 1. Trigger the job in your service:
117
+
118
+ <embedex source="packages/notifications/examples/exampleNotification.service.ts">
119
+
120
+ ```ts
121
+ import { type NotificationClient } from "@clipboard-health/notifications";
122
+
123
+ import { type ExampleNotificationJobData } from "./exampleNotification.job";
124
+
125
+ type ExampleNotificationDo = ExampleNotificationJobData & { attempt: number };
126
+
127
+ export class ExampleNotificationService {
128
+ constructor(private readonly client: NotificationClient) {}
129
+
130
+ async sendNotification(params: ExampleNotificationDo) {
131
+ const { attempt, expiresAt, idempotencyKey, recipients, workflowKey, workplaceId } = params;
132
+
133
+ // Assume this comes from a database and, for example, are used as template variables...
134
+ const data = { favoriteColor: "blue", secret: "2" };
135
+
136
+ return await this.client.trigger({
137
+ attempt,
138
+ body: {
139
+ recipients,
140
+ data,
141
+ workplaceId,
142
+ },
143
+ expiresAt,
144
+ idempotencyKey,
145
+ workflowKey,
146
+ keysToRedact: ["secret"],
147
+ });
148
+ }
149
+ }
150
+ ```
61
151
 
62
- </embedex>
152
+ </embedex>
63
153
 
64
154
  ## Local development commands
65
155
 
package/package.json CHANGED
@@ -1,17 +1,17 @@
1
1
  {
2
2
  "name": "@clipboard-health/notifications",
3
3
  "description": "Send notifications through third-party providers.",
4
- "version": "0.6.0",
4
+ "version": "0.7.0",
5
5
  "bugs": "https://github.com/ClipboardHealth/core-utils/issues",
6
6
  "dependencies": {
7
- "@clipboard-health/phone-number": "0.5.0",
8
- "@clipboard-health/util-ts": "3.14.0",
7
+ "@clipboard-health/background-jobs-adapter": "0.2.0",
8
+ "@clipboard-health/phone-number": "0.6.0",
9
+ "@clipboard-health/util-ts": "3.15.0",
9
10
  "@knocklabs/node": "1.16.0",
10
- "fast-json-stable-stringify": "2.1.0",
11
11
  "tslib": "2.8.1"
12
12
  },
13
13
  "devDependencies": {
14
- "@clipboard-health/testing-core": "0.23.0"
14
+ "@clipboard-health/testing-core": "0.24.0"
15
15
  },
16
16
  "keywords": [],
17
17
  "license": "MIT",
package/src/index.d.ts CHANGED
@@ -1,5 +1,5 @@
1
- export * from "./lib/chunkRecipients";
2
- export * from "./lib/createIdempotencyKey";
3
1
  export * from "./lib/errorsInResult";
2
+ export * from "./lib/idempotencyKey";
4
3
  export * from "./lib/notificationClient";
4
+ export * from "./lib/notificationJobEnqueuer";
5
5
  export * from "./lib/types";
package/src/index.js CHANGED
@@ -1,9 +1,9 @@
1
1
  "use strict";
2
2
  Object.defineProperty(exports, "__esModule", { value: true });
3
3
  const tslib_1 = require("tslib");
4
- tslib_1.__exportStar(require("./lib/chunkRecipients"), exports);
5
- tslib_1.__exportStar(require("./lib/createIdempotencyKey"), exports);
6
4
  tslib_1.__exportStar(require("./lib/errorsInResult"), exports);
5
+ tslib_1.__exportStar(require("./lib/idempotencyKey"), exports);
7
6
  tslib_1.__exportStar(require("./lib/notificationClient"), exports);
7
+ tslib_1.__exportStar(require("./lib/notificationJobEnqueuer"), exports);
8
8
  tslib_1.__exportStar(require("./lib/types"), exports);
9
9
  //# sourceMappingURL=index.js.map
package/src/index.js.map CHANGED
@@ -1 +1 @@
1
- {"version":3,"file":"index.js","sourceRoot":"","sources":["../../../../packages/notifications/src/index.ts"],"names":[],"mappings":";;;AAAA,gEAAsC;AACtC,qEAA2C;AAC3C,+DAAqC;AACrC,mEAAyC;AACzC,sDAA4B"}
1
+ {"version":3,"file":"index.js","sourceRoot":"","sources":["../../../../packages/notifications/src/index.ts"],"names":[],"mappings":";;;AAAA,+DAAqC;AACrC,+DAAqC;AACrC,mEAAyC;AACzC,wEAA8C;AAC9C,sDAA4B"}
@@ -0,0 +1,46 @@
1
+ export interface IdempotencyKeyParams {
2
+ /**
3
+ * Prefer `resourceId` over `eventOccurredAt`; it's harder to misuse.
4
+ *
5
+ * If an event triggered your workflow and it doesn't have a unique ID, you may decide to use its
6
+ * occurrence timestamp. For example, if you have a daily CRON job, use the date it ran.
7
+ *
8
+ * Take care when using `Date.now()` or `new Date()`. These change each time your job runs, which
9
+ * means the idempotency key will always be different and doesn't prevent duplicate notifications.
10
+ */
11
+ eventOccurredAt?: Date | undefined;
12
+ /**
13
+ * If a resource triggered your workflow, include its unique ID.
14
+ *
15
+ * @note `recipients` (and `workplaceId` if it exists) are included in the idempotency key
16
+ * automatically from the trigger body.
17
+ *
18
+ * @example
19
+ * 1. For a "meeting starts in one hour" notification, set resourceId to the meeting ID.
20
+ * 2. For a payout notification, set resourceId to the payment ID.
21
+ */
22
+ resourceId?: string | undefined;
23
+ }
24
+ /**
25
+ * Idempotency keys prevent duplicate notifications. They should be deterministic and remain the
26
+ * same across retry logic.
27
+ *
28
+ * If you retry a request with the same idempotency key within 24 hours, the client returns the same
29
+ * response as the original request.
30
+ *
31
+ * @note `recipients` (and `workplaceId` if it exists) are included in the idempotency key
32
+ * automatically from the trigger body.
33
+ *
34
+ * We provide this class because idempotency keys can be difficult to use correctly. If the key
35
+ * changes on each retry (e.g., Date.now() or uuid.v4()), it won't prevent duplicate notifications.
36
+ * Conversely, if you don't provide enough information, you prevent recipients from receiving
37
+ * notifications they otherwise should have. For example, if you use the trigger key and the
38
+ * recipient's ID as the idempotency key, but it's possible the recipient could receive the same
39
+ * notification multiple times within the idempotency key's validity window, the recipient will only
40
+ * receive the first notification.
41
+ */
42
+ export declare class IdempotencyKey {
43
+ protected readonly eventOccurredAt: IdempotencyKeyParams["eventOccurredAt"];
44
+ protected readonly resourceId: IdempotencyKeyParams["resourceId"];
45
+ constructor(params: IdempotencyKeyParams);
46
+ }
@@ -0,0 +1,32 @@
1
+ "use strict";
2
+ Object.defineProperty(exports, "__esModule", { value: true });
3
+ exports.IdempotencyKey = void 0;
4
+ /**
5
+ * Idempotency keys prevent duplicate notifications. They should be deterministic and remain the
6
+ * same across retry logic.
7
+ *
8
+ * If you retry a request with the same idempotency key within 24 hours, the client returns the same
9
+ * response as the original request.
10
+ *
11
+ * @note `recipients` (and `workplaceId` if it exists) are included in the idempotency key
12
+ * automatically from the trigger body.
13
+ *
14
+ * We provide this class because idempotency keys can be difficult to use correctly. If the key
15
+ * changes on each retry (e.g., Date.now() or uuid.v4()), it won't prevent duplicate notifications.
16
+ * Conversely, if you don't provide enough information, you prevent recipients from receiving
17
+ * notifications they otherwise should have. For example, if you use the trigger key and the
18
+ * recipient's ID as the idempotency key, but it's possible the recipient could receive the same
19
+ * notification multiple times within the idempotency key's validity window, the recipient will only
20
+ * receive the first notification.
21
+ */
22
+ class IdempotencyKey {
23
+ eventOccurredAt;
24
+ resourceId;
25
+ constructor(params) {
26
+ const { eventOccurredAt, resourceId } = params;
27
+ this.eventOccurredAt = eventOccurredAt;
28
+ this.resourceId = resourceId;
29
+ }
30
+ }
31
+ exports.IdempotencyKey = IdempotencyKey;
32
+ //# sourceMappingURL=idempotencyKey.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"idempotencyKey.js","sourceRoot":"","sources":["../../../../../packages/notifications/src/lib/idempotencyKey.ts"],"names":[],"mappings":";;;AAyBA;;;;;;;;;;;;;;;;;GAiBG;AACH,MAAa,cAAc;IACN,eAAe,CAA0C;IACzD,UAAU,CAAqC;IAElE,YAAmB,MAA4B;QAC7C,MAAM,EAAE,eAAe,EAAE,UAAU,EAAE,GAAG,MAAM,CAAC;QAE/C,IAAI,CAAC,eAAe,GAAG,eAAe,CAAC;QACvC,IAAI,CAAC,UAAU,GAAG,UAAU,CAAC;IAC/B,CAAC;CACF;AAVD,wCAUC"}
@@ -0,0 +1,18 @@
1
+ /**
2
+ * Chunks recipients into groups that don't exceed the maximum recipients count,
3
+ * assigning a unique chunk number to each group.
4
+ *
5
+ * This function is used to split large recipient lists into smaller batches that
6
+ * comply with notification provider limits. Each chunk is numbered sequentially
7
+ * starting from 1.
8
+ *
9
+ * @param params - The chunking parameters
10
+ * @param params.recipients - Array of recipient IDs to chunk
11
+ * @returns Array of chunks, each containing a chunk number and recipients array
12
+ */
13
+ export declare function chunkRecipients(params: {
14
+ recipients: string[];
15
+ }): Array<{
16
+ number: number;
17
+ recipients: string[];
18
+ }>;
@@ -0,0 +1,28 @@
1
+ "use strict";
2
+ Object.defineProperty(exports, "__esModule", { value: true });
3
+ exports.chunkRecipients = chunkRecipients;
4
+ const util_ts_1 = require("@clipboard-health/util-ts");
5
+ const notificationClient_1 = require("../notificationClient");
6
+ /**
7
+ * Chunks recipients into groups that don't exceed the maximum recipients count,
8
+ * assigning a unique chunk number to each group.
9
+ *
10
+ * This function is used to split large recipient lists into smaller batches that
11
+ * comply with notification provider limits. Each chunk is numbered sequentially
12
+ * starting from 1.
13
+ *
14
+ * @param params - The chunking parameters
15
+ * @param params.recipients - Array of recipient IDs to chunk
16
+ * @returns Array of chunks, each containing a chunk number and recipients array
17
+ */
18
+ function chunkRecipients(params) {
19
+ const { recipients } = params;
20
+ if (recipients.length === 0) {
21
+ return [{ number: 1, recipients: [] }];
22
+ }
23
+ return (0, util_ts_1.chunk)(recipients, notificationClient_1.MAXIMUM_RECIPIENTS_COUNT).map((recipientsChunk, index) => ({
24
+ number: index + 1,
25
+ recipients: recipientsChunk,
26
+ }));
27
+ }
28
+ //# sourceMappingURL=chunkRecipients.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"chunkRecipients.js","sourceRoot":"","sources":["../../../../../../packages/notifications/src/lib/internal/chunkRecipients.ts"],"names":[],"mappings":";;AAgBA,0CAaC;AA7BD,uDAAkD;AAElD,8DAAiE;AAEjE;;;;;;;;;;;GAWG;AACH,SAAgB,eAAe,CAAC,MAE/B;IACC,MAAM,EAAE,UAAU,EAAE,GAAG,MAAM,CAAC;IAE9B,IAAI,UAAU,CAAC,MAAM,KAAK,CAAC,EAAE,CAAC;QAC5B,OAAO,CAAC,EAAE,MAAM,EAAE,CAAC,EAAE,UAAU,EAAE,EAAE,EAAE,CAAC,CAAC;IACzC,CAAC;IAED,OAAO,IAAA,eAAK,EAAC,UAAU,EAAE,6CAAwB,CAAC,CAAC,GAAG,CAAC,CAAC,eAAe,EAAE,KAAK,EAAE,EAAE,CAAC,CAAC;QAClF,MAAM,EAAE,KAAK,GAAG,CAAC;QACjB,UAAU,EAAE,eAAe;KAC5B,CAAC,CAAC,CAAC;AACN,CAAC"}
@@ -0,0 +1,13 @@
1
+ /**
2
+ * Creates a deterministic hash.
3
+ *
4
+ * Normalizes `value` using stable JSON serialization for non-string values and generates
5
+ * a SHA-256 hash.
6
+ *
7
+ * Note: Non-JSON-serializable values (undefined, Symbol, functions, circular refs) may be
8
+ * dropped or cause errors during serialization.
9
+ *
10
+ * @param value - Value to hash (string, string[], object, etc.).
11
+ * @returns A hex string representing the hash.
12
+ */
13
+ export declare function createDeterministicHash(value: unknown): string;
@@ -0,0 +1,23 @@
1
+ "use strict";
2
+ Object.defineProperty(exports, "__esModule", { value: true });
3
+ exports.createDeterministicHash = createDeterministicHash;
4
+ const node_crypto_1 = require("node:crypto");
5
+ const util_ts_1 = require("@clipboard-health/util-ts");
6
+ /**
7
+ * Creates a deterministic hash.
8
+ *
9
+ * Normalizes `value` using stable JSON serialization for non-string values and generates
10
+ * a SHA-256 hash.
11
+ *
12
+ * Note: Non-JSON-serializable values (undefined, Symbol, functions, circular refs) may be
13
+ * dropped or cause errors during serialization.
14
+ *
15
+ * @param value - Value to hash (string, string[], object, etc.).
16
+ * @returns A hex string representing the hash.
17
+ */
18
+ function createDeterministicHash(value) {
19
+ return (0, node_crypto_1.createHash)("sha256")
20
+ .update(typeof value === "string" ? value : (0, util_ts_1.stringify)(value))
21
+ .digest("hex");
22
+ }
23
+ //# sourceMappingURL=createDeterministicHash.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"createDeterministicHash.js","sourceRoot":"","sources":["../../../../../../packages/notifications/src/lib/internal/createDeterministicHash.ts"],"names":[],"mappings":";;AAgBA,0DAIC;AApBD,6CAAyC;AAEzC,uDAAsD;AAEtD;;;;;;;;;;;GAWG;AACH,SAAgB,uBAAuB,CAAC,KAAc;IACpD,OAAO,IAAA,wBAAU,EAAC,QAAQ,CAAC;SACxB,MAAM,CAAC,OAAO,KAAK,KAAK,QAAQ,CAAC,CAAC,CAAC,KAAK,CAAC,CAAC,CAAC,IAAA,mBAAS,EAAC,KAAK,CAAC,CAAC;SAC5D,MAAM,CAAC,KAAK,CAAC,CAAC;AACnB,CAAC"}
@@ -1,3 +1,3 @@
1
1
  import type { LogParams, TriggerRequest } from "../types";
2
- export type TriggerLogContext = LogParams & Pick<TriggerRequest, "attempt" | "idempotencyKey" | "key">;
2
+ export type TriggerLogContext = LogParams & Pick<TriggerRequest, "attempt" | "idempotencyKey" | "workflowKey">;
3
3
  export declare function createTriggerLogParams(params: TriggerRequest & LogParams): TriggerLogContext;
@@ -2,12 +2,12 @@
2
2
  Object.defineProperty(exports, "__esModule", { value: true });
3
3
  exports.createTriggerLogParams = createTriggerLogParams;
4
4
  function createTriggerLogParams(params) {
5
- const { attempt, destination, idempotencyKey, key, traceName } = params;
5
+ const { attempt, destination, idempotencyKey, key, workflowKey, traceName } = params;
6
6
  return {
7
7
  attempt,
8
8
  destination,
9
9
  idempotencyKey,
10
- key,
10
+ workflowKey: /* istanbul ignore next */ workflowKey ?? key,
11
11
  traceName,
12
12
  };
13
13
  }
@@ -1 +1 @@
1
- {"version":3,"file":"createTriggerLogParams.js","sourceRoot":"","sources":["../../../../../../packages/notifications/src/lib/internal/createTriggerLogParams.ts"],"names":[],"mappings":";;AAKA,wDAUC;AAVD,SAAgB,sBAAsB,CAAC,MAAkC;IACvE,MAAM,EAAE,OAAO,EAAE,WAAW,EAAE,cAAc,EAAE,GAAG,EAAE,SAAS,EAAE,GAAG,MAAM,CAAC;IAExE,OAAO;QACL,OAAO;QACP,WAAW;QACX,cAAc;QACd,GAAG;QACH,SAAS;KACV,CAAC;AACJ,CAAC"}
1
+ {"version":3,"file":"createTriggerLogParams.js","sourceRoot":"","sources":["../../../../../../packages/notifications/src/lib/internal/createTriggerLogParams.ts"],"names":[],"mappings":";;AAKA,wDAUC;AAVD,SAAgB,sBAAsB,CAAC,MAAkC;IACvE,MAAM,EAAE,OAAO,EAAE,WAAW,EAAE,cAAc,EAAE,GAAG,EAAE,WAAW,EAAE,SAAS,EAAE,GAAG,MAAM,CAAC;IAErF,OAAO;QACL,OAAO;QACP,WAAW;QACX,cAAc;QACd,WAAW,EAAE,0BAA0B,CAAC,WAAW,IAAI,GAAG;QAC1D,SAAS;KACV,CAAC;AACJ,CAAC"}
@@ -2,9 +2,9 @@
2
2
  Object.defineProperty(exports, "__esModule", { value: true });
3
3
  exports.createTriggerTraceOptions = createTriggerTraceOptions;
4
4
  function createTriggerTraceOptions(params) {
5
- const { key, attempt, destination } = params;
5
+ const { workflowKey, attempt, destination } = params;
6
6
  return {
7
- resource: `notification.${key}`,
7
+ resource: `notification.${workflowKey}`,
8
8
  /**
9
9
  * Don't include high cardinality tags like expiresAt and idempotencyKey to reduce Datadog
10
10
  * costs.
@@ -1 +1 @@
1
- {"version":3,"file":"createTriggerTraceOptions.js","sourceRoot":"","sources":["../../../../../../packages/notifications/src/lib/internal/createTriggerTraceOptions.ts"],"names":[],"mappings":";;AAGA,8DAkBC;AAlBD,SAAgB,yBAAyB,CAAC,MAAyB;IACjE,MAAM,EAAE,GAAG,EAAE,OAAO,EAAE,WAAW,EAAE,GAAG,MAAM,CAAC;IAE7C,OAAO;QACL,QAAQ,EAAE,gBAAgB,GAAG,EAAE;QAC/B;;;WAGG;QACH,IAAI,EAAE;YACJ,WAAW,EAAE,UAAU;YACvB,SAAS,EAAE,wBAAwB;YACnC,kBAAkB,EAAE,WAAW;YAC/B,qBAAqB,EAAE,SAAS;YAChC,uBAAuB,EAAE,WAAW;YACpC,sBAAsB,EAAE,OAAO,CAAC,QAAQ,EAAE;SAC3C;KACF,CAAC;AACJ,CAAC"}
1
+ {"version":3,"file":"createTriggerTraceOptions.js","sourceRoot":"","sources":["../../../../../../packages/notifications/src/lib/internal/createTriggerTraceOptions.ts"],"names":[],"mappings":";;AAGA,8DAkBC;AAlBD,SAAgB,yBAAyB,CAAC,MAAyB;IACjE,MAAM,EAAE,WAAW,EAAE,OAAO,EAAE,WAAW,EAAE,GAAG,MAAM,CAAC;IAErD,OAAO;QACL,QAAQ,EAAE,gBAAgB,WAAW,EAAE;QACvC;;;WAGG;QACH,IAAI,EAAE;YACJ,WAAW,EAAE,UAAU;YACvB,SAAS,EAAE,wBAAwB;YACnC,kBAAkB,EAAE,WAAW;YAC/B,qBAAqB,EAAE,SAAS;YAChC,uBAAuB,EAAE,WAAW;YACpC,sBAAsB,EAAE,OAAO,CAAC,QAAQ,EAAE;SAC3C;KACF,CAAC;AACJ,CAAC"}
@@ -0,0 +1,35 @@
1
+ import { IdempotencyKey, type IdempotencyKeyParams } from "../idempotencyKey";
2
+ interface IdempotencyKeyDoNotImportOutsideNotificationsLibraryParams extends IdempotencyKeyParams {
3
+ /**
4
+ * The recipient chunk number.
5
+ */
6
+ chunk: number;
7
+ /**
8
+ * The recipients in the chunk; maximum of MAXIMUM_RECIPIENTS_COUNT.
9
+ */
10
+ recipients: string[];
11
+ /**
12
+ * The workflow key.
13
+ */
14
+ workflowKey: string;
15
+ }
16
+ /**
17
+ * Idempotency keys prevent duplicate notifications. `NotificationClient.trigger` should be called
18
+ * after properly enqueuing a job using `NotificationJobEnqueuer.enqueueOneOrMore` to help ensure
19
+ * we're following best practices so customers don't receive duplicate or stale notifications.
20
+ *
21
+ * Yes, you could import this class into your service and call `NotificationClient.trigger`
22
+ * directly. We're using the honor system in hopes that enforcement is unnecessary.
23
+ *
24
+ * @see {@link NotificationJobEnqueuer.enqueueOneOrMore}.
25
+ */
26
+ export declare class IdempotencyKeyDoNotImportOutsideNotificationsLibrary extends IdempotencyKey {
27
+ private readonly chunk;
28
+ private readonly recipients;
29
+ private readonly workflowKey;
30
+ constructor(params: IdempotencyKeyDoNotImportOutsideNotificationsLibraryParams);
31
+ toHash(params: {
32
+ workplaceId?: string | undefined;
33
+ }): string;
34
+ }
35
+ export {};
@@ -0,0 +1,43 @@
1
+ "use strict";
2
+ Object.defineProperty(exports, "__esModule", { value: true });
3
+ exports.IdempotencyKeyDoNotImportOutsideNotificationsLibrary = void 0;
4
+ const util_ts_1 = require("@clipboard-health/util-ts");
5
+ const idempotencyKey_1 = require("../idempotencyKey");
6
+ const createDeterministicHash_1 = require("./createDeterministicHash");
7
+ /**
8
+ * Idempotency keys prevent duplicate notifications. `NotificationClient.trigger` should be called
9
+ * after properly enqueuing a job using `NotificationJobEnqueuer.enqueueOneOrMore` to help ensure
10
+ * we're following best practices so customers don't receive duplicate or stale notifications.
11
+ *
12
+ * Yes, you could import this class into your service and call `NotificationClient.trigger`
13
+ * directly. We're using the honor system in hopes that enforcement is unnecessary.
14
+ *
15
+ * @see {@link NotificationJobEnqueuer.enqueueOneOrMore}.
16
+ */
17
+ class IdempotencyKeyDoNotImportOutsideNotificationsLibrary extends idempotencyKey_1.IdempotencyKey {
18
+ chunk;
19
+ recipients;
20
+ workflowKey;
21
+ constructor(params) {
22
+ const { chunk, recipients, workflowKey, ...rest } = params;
23
+ super(rest);
24
+ this.chunk = chunk;
25
+ this.recipients = recipients;
26
+ this.workflowKey = workflowKey;
27
+ }
28
+ toHash(params) {
29
+ const { workplaceId } = params;
30
+ return (0, createDeterministicHash_1.createDeterministicHash)([
31
+ this.workflowKey,
32
+ this.chunk,
33
+ this.resourceId,
34
+ this.eventOccurredAt?.toISOString(),
35
+ this.recipients.join(","),
36
+ workplaceId,
37
+ ]
38
+ .filter(util_ts_1.isDefined)
39
+ .join(","));
40
+ }
41
+ }
42
+ exports.IdempotencyKeyDoNotImportOutsideNotificationsLibrary = IdempotencyKeyDoNotImportOutsideNotificationsLibrary;
43
+ //# sourceMappingURL=idempotencyKeyDoNotImportOutsideNotificationsLibrary.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"idempotencyKeyDoNotImportOutsideNotificationsLibrary.js","sourceRoot":"","sources":["../../../../../../packages/notifications/src/lib/internal/idempotencyKeyDoNotImportOutsideNotificationsLibrary.ts"],"names":[],"mappings":";;;AAAA,uDAAsD;AAEtD,sDAA8E;AAK9E,uEAAoE;AAmBpE;;;;;;;;;GASG;AACH,MAAa,oDAAqD,SAAQ,+BAAc;IACrE,KAAK,CAAsE;IAC3E,UAAU,CAA2E;IACrF,WAAW,CAA4E;IAExG,YAAY,MAAkE;QAC5E,MAAM,EAAE,KAAK,EAAE,UAAU,EAAE,WAAW,EAAE,GAAG,IAAI,EAAE,GAAG,MAAM,CAAC;QAE3D,KAAK,CAAC,IAAI,CAAC,CAAC;QACZ,IAAI,CAAC,KAAK,GAAG,KAAK,CAAC;QACnB,IAAI,CAAC,UAAU,GAAG,UAAU,CAAC;QAC7B,IAAI,CAAC,WAAW,GAAG,WAAW,CAAC;IACjC,CAAC;IAED,MAAM,CAAC,MAA4C;QACjD,MAAM,EAAE,WAAW,EAAE,GAAG,MAAM,CAAC;QAE/B,OAAO,IAAA,iDAAuB,EAC5B;YACE,IAAI,CAAC,WAAW;YAChB,IAAI,CAAC,KAAK;YACV,IAAI,CAAC,UAAU;YACf,IAAI,CAAC,eAAe,EAAE,WAAW,EAAE;YACnC,IAAI,CAAC,UAAU,CAAC,IAAI,CAAC,GAAG,CAAC;YACzB,WAAW;SACZ;aACE,MAAM,CAAC,mBAAS,CAAC;aACjB,IAAI,CAAC,GAAG,CAAC,CACb,CAAC;IACJ,CAAC;CACF;AA9BD,oHA8BC"}
@@ -1,5 +1,10 @@
1
1
  import { type Logger } from "@clipboard-health/util-ts";
2
2
  import { Knock } from "@knocklabs/node";
3
+ /**
4
+ * Extended Knock client with custom configuration for idempotent operations.
5
+ *
6
+ * @see {@link https://docs.knock.app/api-reference/overview/idempotent-requests}
7
+ */
3
8
  export declare class IdempotentKnock extends Knock {
4
9
  logLevel: "warn";
5
10
  maxRetries: number;
@@ -2,6 +2,11 @@
2
2
  Object.defineProperty(exports, "__esModule", { value: true });
3
3
  exports.IdempotentKnock = void 0;
4
4
  const node_1 = require("@knocklabs/node");
5
+ /**
6
+ * Extended Knock client with custom configuration for idempotent operations.
7
+ *
8
+ * @see {@link https://docs.knock.app/api-reference/overview/idempotent-requests}
9
+ */
5
10
  class IdempotentKnock extends node_1.Knock {
6
11
  logLevel = "warn"; // Knock's default.
7
12
  maxRetries = 1; // Knock's default is 2, but we rely on background job retries.
@@ -1 +1 @@
1
- {"version":3,"file":"idempotentKnock.js","sourceRoot":"","sources":["../../../../../../packages/notifications/src/lib/internal/idempotentKnock.ts"],"names":[],"mappings":";;;AACA,0CAAwC;AAExC,MAAa,eAAgB,SAAQ,YAAK;IACxB,QAAQ,GAAG,MAAe,CAAC,CAAC,mBAAmB;IAC/C,UAAU,GAAG,CAAC,CAAC,CAAC,+DAA+D;IAC/E,OAAO,GAAG,MAAM,CAAC,CAAC,mBAAmB;IAElC,iBAAiB,GAAG,iBAAiB,CAAC;IAEzD,YAAY,MAA0C;QACpD,MAAM,EAAE,MAAM,EAAE,MAAM,EAAE,GAAG,MAAM,CAAC;QAElC,KAAK,CAAC;YACJ,MAAM;YACN,MAAM,EAAE;gBACN,GAAG,MAAM;gBACT,KAAK,EAAE,MAAM,CAAC,IAAI;aACnB;SACF,CAAC,CAAC;IACL,CAAC;CACF;AAlBD,0CAkBC"}
1
+ {"version":3,"file":"idempotentKnock.js","sourceRoot":"","sources":["../../../../../../packages/notifications/src/lib/internal/idempotentKnock.ts"],"names":[],"mappings":";;;AACA,0CAAwC;AAExC;;;;GAIG;AACH,MAAa,eAAgB,SAAQ,YAAK;IACxB,QAAQ,GAAG,MAAe,CAAC,CAAC,mBAAmB;IAC/C,UAAU,GAAG,CAAC,CAAC,CAAC,+DAA+D;IAC/E,OAAO,GAAG,MAAM,CAAC,CAAC,mBAAmB;IAElC,iBAAiB,GAAG,iBAAiB,CAAC;IAEzD,YAAY,MAA0C;QACpD,MAAM,EAAE,MAAM,EAAE,MAAM,EAAE,GAAG,MAAM,CAAC;QAElC,KAAK,CAAC;YACJ,MAAM;YACN,MAAM,EAAE;gBACN,GAAG,MAAM;gBACT,KAAK,EAAE,MAAM,CAAC,IAAI;aACnB;SACF,CAAC,CAAC;IACL,CAAC;CACF;AAlBD,0CAkBC"}
@@ -1,6 +1,5 @@
1
- import { type LogFunction, type Logger, type ServiceResult } from "@clipboard-health/util-ts";
2
- import { IdempotentKnock } from "./internal/idempotentKnock";
3
- import type { AppendPushTokenRequest, AppendPushTokenResponse, LogParams, NotificationClientParams, SignUserTokenRequest, SignUserTokenResponse, Span, Tracer, TriggerRequest, TriggerResponse, UpsertWorkplaceRequest, UpsertWorkplaceResponse } from "./types";
1
+ import { type LogFunction, type ServiceResult } from "@clipboard-health/util-ts";
2
+ import type { AppendPushTokenRequest, AppendPushTokenResponse, LogParams, NotificationClientParams, SignUserTokenRequest, SignUserTokenResponse, Span, TriggerRequest, TriggerResponse, UpsertWorkplaceRequest, UpsertWorkplaceResponse } from "./types";
4
3
  export declare const MAXIMUM_RECIPIENTS_COUNT = 1000;
5
4
  export declare const ERROR_CODES: {
6
5
  readonly expired: "expired";
@@ -18,9 +17,9 @@ interface NotificationError {
18
17
  * Client for sending notifications through third-party providers.
19
18
  */
20
19
  export declare class NotificationClient {
21
- protected readonly logger: Logger;
22
- protected readonly provider: IdempotentKnock;
23
- protected readonly tracer: Tracer;
20
+ protected readonly logger: NotificationClientParams["logger"];
21
+ protected readonly provider: Required<NotificationClientParams>["provider"];
22
+ protected readonly tracer: NotificationClientParams["tracer"];
24
23
  private readonly signingKey;
25
24
  /**
26
25
  * Creates a new NotificationClient instance.
@@ -39,44 +38,38 @@ export declare class NotificationClient {
39
38
  * @returns Promise resolving to either an error or successful response.
40
39
  *
41
40
  * @example
42
- * <embedex source="packages/notifications/examples/notificationClient.ts">
41
+ * <embedex source="packages/notifications/examples/exampleNotification.service.ts">
43
42
  *
44
43
  * ```ts
45
- * import { NotificationClient, type Span } from "@clipboard-health/notifications";
46
- * import { isSuccess } from "@clipboard-health/util-ts";
44
+ * import { type NotificationClient } from "@clipboard-health/notifications";
47
45
  *
48
- * const client = new NotificationClient({
49
- * apiKey: "test-api-key",
50
- * logger: {
51
- * info: console.log,
52
- * warn: console.warn,
53
- * error: console.error,
54
- * } as const,
55
- * tracer: {
56
- * trace: <T>(_name: string, _options: unknown, fun: (span?: Span | undefined) => T): T => fun(),
57
- * },
58
- * });
46
+ * import { type ExampleNotificationJobData } from "./exampleNotification.job";
59
47
  *
60
- * async function triggerNotification(job: { attemptsCount: number }) {
61
- * const result = await client.trigger({
62
- * attempt: (job?.attemptsCount ?? 0) + 1,
63
- * body: {
64
- * recipients: ["user-1"],
65
- * data: { favoriteColor: "blue", secret: "2" },
66
- * },
67
- * expiresAt: new Date(Date.now() + 300_000), // 5 minutes
68
- * idempotencyKey: "welcome-user-4",
69
- * key: "welcome-email",
70
- * keysToRedact: ["secret"],
71
- * });
48
+ * type ExampleNotificationDo = ExampleNotificationJobData & { attempt: number };
72
49
  *
73
- * if (isSuccess(result)) {
74
- * console.log("Notification sent:", result.value.id);
50
+ * export class ExampleNotificationService {
51
+ * constructor(private readonly client: NotificationClient) {}
52
+ *
53
+ * async sendNotification(params: ExampleNotificationDo) {
54
+ * const { attempt, expiresAt, idempotencyKey, recipients, workflowKey, workplaceId } = params;
55
+ *
56
+ * // Assume this comes from a database and, for example, are used as template variables...
57
+ * const data = { favoriteColor: "blue", secret: "2" };
58
+ *
59
+ * return await this.client.trigger({
60
+ * attempt,
61
+ * body: {
62
+ * recipients,
63
+ * data,
64
+ * workplaceId,
65
+ * },
66
+ * expiresAt,
67
+ * idempotencyKey,
68
+ * workflowKey,
69
+ * keysToRedact: ["secret"],
70
+ * });
75
71
  * }
76
72
  * }
77
- *
78
- * // eslint-disable-next-line unicorn/prefer-top-level-await
79
- * void triggerNotification({ attemptsCount: 0 });
80
73
  * ```
81
74
  *
82
75
  * </embedex>
@@ -5,6 +5,7 @@ const util_ts_1 = require("@clipboard-health/util-ts");
5
5
  const node_1 = require("@knocklabs/node");
6
6
  const createTriggerLogParams_1 = require("./internal/createTriggerLogParams");
7
7
  const createTriggerTraceOptions_1 = require("./internal/createTriggerTraceOptions");
8
+ const idempotencyKeyDoNotImportOutsideNotificationsLibrary_1 = require("./internal/idempotencyKeyDoNotImportOutsideNotificationsLibrary");
8
9
  const idempotentKnock_1 = require("./internal/idempotentKnock");
9
10
  const redact_1 = require("./internal/redact");
10
11
  const toTenantSetRequest_1 = require("./internal/toTenantSetRequest");
@@ -71,44 +72,38 @@ class NotificationClient {
71
72
  * @returns Promise resolving to either an error or successful response.
72
73
  *
73
74
  * @example
74
- * <embedex source="packages/notifications/examples/notificationClient.ts">
75
+ * <embedex source="packages/notifications/examples/exampleNotification.service.ts">
75
76
  *
76
77
  * ```ts
77
- * import { NotificationClient, type Span } from "@clipboard-health/notifications";
78
- * import { isSuccess } from "@clipboard-health/util-ts";
78
+ * import { type NotificationClient } from "@clipboard-health/notifications";
79
79
  *
80
- * const client = new NotificationClient({
81
- * apiKey: "test-api-key",
82
- * logger: {
83
- * info: console.log,
84
- * warn: console.warn,
85
- * error: console.error,
86
- * } as const,
87
- * tracer: {
88
- * trace: <T>(_name: string, _options: unknown, fun: (span?: Span | undefined) => T): T => fun(),
89
- * },
90
- * });
80
+ * import { type ExampleNotificationJobData } from "./exampleNotification.job";
91
81
  *
92
- * async function triggerNotification(job: { attemptsCount: number }) {
93
- * const result = await client.trigger({
94
- * attempt: (job?.attemptsCount ?? 0) + 1,
95
- * body: {
96
- * recipients: ["user-1"],
97
- * data: { favoriteColor: "blue", secret: "2" },
98
- * },
99
- * expiresAt: new Date(Date.now() + 300_000), // 5 minutes
100
- * idempotencyKey: "welcome-user-4",
101
- * key: "welcome-email",
102
- * keysToRedact: ["secret"],
103
- * });
82
+ * type ExampleNotificationDo = ExampleNotificationJobData & { attempt: number };
104
83
  *
105
- * if (isSuccess(result)) {
106
- * console.log("Notification sent:", result.value.id);
84
+ * export class ExampleNotificationService {
85
+ * constructor(private readonly client: NotificationClient) {}
86
+ *
87
+ * async sendNotification(params: ExampleNotificationDo) {
88
+ * const { attempt, expiresAt, idempotencyKey, recipients, workflowKey, workplaceId } = params;
89
+ *
90
+ * // Assume this comes from a database and, for example, are used as template variables...
91
+ * const data = { favoriteColor: "blue", secret: "2" };
92
+ *
93
+ * return await this.client.trigger({
94
+ * attempt,
95
+ * body: {
96
+ * recipients,
97
+ * data,
98
+ * workplaceId,
99
+ * },
100
+ * expiresAt,
101
+ * idempotencyKey,
102
+ * workflowKey,
103
+ * keysToRedact: ["secret"],
104
+ * });
107
105
  * }
108
106
  * }
109
- *
110
- * // eslint-disable-next-line unicorn/prefer-top-level-await
111
- * void triggerNotification({ attemptsCount: 0 });
112
107
  * ```
113
108
  *
114
109
  * </embedex>
@@ -121,10 +116,15 @@ class NotificationClient {
121
116
  return validated;
122
117
  }
123
118
  try {
124
- const { key, body, idempotencyKey, keysToRedact = [] } = validated.value;
119
+ const { body, idempotencyKey, key, keysToRedact = [], workflowKey } = validated.value;
120
+ const triggerBody = (0, toTriggerBody_1.toTriggerBody)(body);
125
121
  this.logTriggerRequest({ logParams, body, keysToRedact });
126
- const response = await this.provider.workflows.trigger(key, (0, toTriggerBody_1.toTriggerBody)(body), {
127
- idempotencyKey,
122
+ const response = await this.provider.workflows.trigger(
123
+ /* istanbul ignore next */ workflowKey ?? key, triggerBody, {
124
+ idempotencyKey: idempotencyKey instanceof idempotencyKeyDoNotImportOutsideNotificationsLibrary_1.IdempotencyKeyDoNotImportOutsideNotificationsLibrary
125
+ ? idempotencyKey.toHash({ workplaceId: params.body.workplaceId })
126
+ : /* istanbul ignore next */
127
+ idempotencyKey,
128
128
  });
129
129
  const id = response.workflow_run_id;
130
130
  this.logTriggerResponse({ span, response, id, logParams });
@@ -297,7 +297,7 @@ class NotificationClient {
297
297
  });
298
298
  }
299
299
  const now = new Date();
300
- if (now > expiresAt) {
300
+ if (expiresAt instanceof Date && now > expiresAt) {
301
301
  return this.createAndLogError({
302
302
  notificationError: {
303
303
  code: exports.ERROR_CODES.expired,
@@ -1 +1 @@
1
- {"version":3,"file":"notificationClient.js","sourceRoot":"","sources":["../../../../../packages/notifications/src/lib/notificationClient.ts"],"names":[],"mappings":"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"}
1
+ {"version":3,"file":"notificationClient.js","sourceRoot":"","sources":["../../../../../packages/notifications/src/lib/notificationClient.ts"],"names":[],"mappings":"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"}
@@ -0,0 +1,96 @@
1
+ import { type BackgroundJobsAdapter } from "@clipboard-health/background-jobs-adapter";
2
+ import { type IdempotencyKey } from "./idempotencyKey";
3
+ import { IdempotencyKeyDoNotImportOutsideNotificationsLibrary } from "./internal/idempotencyKeyDoNotImportOutsideNotificationsLibrary";
4
+ import { type ErrorCode } from "./notificationClient";
5
+ import { type TriggerRequest } from "./types";
6
+ /**
7
+ * Assuming `NotificationClient` is called from a background job with unchanging recipients, only
8
+ * the following are retryable.
9
+ */
10
+ export declare const RETRYABLE_ERRORS: ErrorCode[];
11
+ type EnqueueParameters = Parameters<BackgroundJobsAdapter["enqueue"]>;
12
+ export interface NotificationEnqueueData {
13
+ idempotencyKey: IdempotencyKey;
14
+ /** @see {@link TriggerRequest.expiresAt} */
15
+ expiresAt: TriggerRequest["expiresAt"];
16
+ /** @see {@link TriggerBody.recipients} */
17
+ recipients: string[];
18
+ /** @see {@link TriggerRequest.workflowKey} */
19
+ workflowKey: string;
20
+ }
21
+ export interface NotificationJobData extends Omit<NotificationEnqueueData, "idempotencyKey"> {
22
+ idempotencyKey: IdempotencyKeyDoNotImportOutsideNotificationsLibrary;
23
+ }
24
+ interface NotificationJobEnqueuerParams {
25
+ adapter: BackgroundJobsAdapter;
26
+ }
27
+ export declare class NotificationJobEnqueuer {
28
+ private readonly adapter;
29
+ constructor(params: NotificationJobEnqueuerParams);
30
+ /**
31
+ * In short: It's important that notification jobs use this method. It enforces best practices to
32
+ * ensure customers don't receive duplicate or stale notifications.
33
+ *
34
+ * @remarks
35
+ * The following are true:
36
+ * 1. There is a maximum of MAXIMUM_RECIPIENTS_COUNT recipients per trigger request.
37
+ * 2. Our notification provider throws if we use the same idempotency key, but the body changes.
38
+ * 3. We want to be able to query for template variables in jobs so we're getting the most
39
+ * up-to-date values.
40
+ *
41
+ * Taken together, we need to ensure each job only contains one chunk of recipients so it either
42
+ * succeeds or fails and retries on its own. If we moved chunking to the
43
+ * `NotificationClient.trigger` method, for example, the following could happen:
44
+ * 1. A job with >MAXIMUM_RECIPIENTS_COUNT recipients runs.
45
+ * 2. The first chunk succeeds, but the second fails because of a transient issue.
46
+ * 3. The job retries, but a first batch template variable changes in the meantime.
47
+ *
48
+ * Now the job will fail indefinitely and the later batches won't get their notifications.
49
+ *
50
+ * Even if you're sure you won't have more >MAXIMUM_RECIPIENTS_COUNT recipients, the method
51
+ * enforces other best practices, like setting `expiresAt` on enqueue instead of calculating it in
52
+ * your job on each run. Doing this usually means it's always in the future and doesn't help
53
+ * prevent stale notifications.
54
+ *
55
+ * So please, use this method if not for customers, then to save fellow engineers time debugging!
56
+ *
57
+ * @example
58
+ * <embedex source="packages/notifications/examples/enqueueNotificationJob.ts">
59
+ *
60
+ * ```ts
61
+ * import { IdempotencyKey } from "@clipboard-health/notifications";
62
+ *
63
+ * import { ExampleNotificationJob } from "./exampleNotification.job";
64
+ * import { notificationJobEnqueuer } from "./notificationJobEnqueuer";
65
+ *
66
+ * async function enqueueNotificationJob() {
67
+ * await notificationJobEnqueuer.enqueueOneOrMore(ExampleNotificationJob, {
68
+ * // Set expiresAt at enqueue-time so it remains stable across job retries.
69
+ * expiresAt: minutesFromNow(60),
70
+ * // Set idempotencyKey at enqueue-time so it remains stable across job retries.
71
+ * idempotencyKey: new IdempotencyKey({
72
+ * resourceId: "event-123",
73
+ * }),
74
+ * // Set recipients at enqueue-time so they respect our notification provider's limits.
75
+ * recipients: ["user-1"],
76
+ *
77
+ * workflowKey: "event-starting-reminder",
78
+ *
79
+ * // Any additional enqueue-time data passed to the job:
80
+ * workplaceId: "workplace-123",
81
+ * });
82
+ * }
83
+ *
84
+ * // eslint-disable-next-line unicorn/prefer-top-level-await
85
+ * void enqueueNotificationJob();
86
+ *
87
+ * function minutesFromNow(minutes: number) {
88
+ * return new Date(Date.now() + minutes * 60_000);
89
+ * }
90
+ * ```
91
+ *
92
+ * </embedex>
93
+ */
94
+ enqueueOneOrMore<TEnqueueData extends NotificationEnqueueData>(handlerClassOrInstance: EnqueueParameters[0], data: TEnqueueData, options?: Omit<EnqueueParameters[2], "idempotencyKey" | "unique">): Promise<void>;
95
+ }
96
+ export {};
@@ -0,0 +1,101 @@
1
+ "use strict";
2
+ Object.defineProperty(exports, "__esModule", { value: true });
3
+ exports.NotificationJobEnqueuer = exports.RETRYABLE_ERRORS = void 0;
4
+ const background_jobs_adapter_1 = require("@clipboard-health/background-jobs-adapter");
5
+ const chunkRecipients_1 = require("./internal/chunkRecipients");
6
+ const idempotencyKeyDoNotImportOutsideNotificationsLibrary_1 = require("./internal/idempotencyKeyDoNotImportOutsideNotificationsLibrary");
7
+ const notificationClient_1 = require("./notificationClient");
8
+ /**
9
+ * Assuming `NotificationClient` is called from a background job with unchanging recipients, only
10
+ * the following are retryable.
11
+ */
12
+ exports.RETRYABLE_ERRORS = [notificationClient_1.ERROR_CODES.unknown];
13
+ class NotificationJobEnqueuer {
14
+ adapter;
15
+ constructor(params) {
16
+ const { adapter } = params;
17
+ this.adapter = adapter;
18
+ }
19
+ /**
20
+ * In short: It's important that notification jobs use this method. It enforces best practices to
21
+ * ensure customers don't receive duplicate or stale notifications.
22
+ *
23
+ * @remarks
24
+ * The following are true:
25
+ * 1. There is a maximum of MAXIMUM_RECIPIENTS_COUNT recipients per trigger request.
26
+ * 2. Our notification provider throws if we use the same idempotency key, but the body changes.
27
+ * 3. We want to be able to query for template variables in jobs so we're getting the most
28
+ * up-to-date values.
29
+ *
30
+ * Taken together, we need to ensure each job only contains one chunk of recipients so it either
31
+ * succeeds or fails and retries on its own. If we moved chunking to the
32
+ * `NotificationClient.trigger` method, for example, the following could happen:
33
+ * 1. A job with >MAXIMUM_RECIPIENTS_COUNT recipients runs.
34
+ * 2. The first chunk succeeds, but the second fails because of a transient issue.
35
+ * 3. The job retries, but a first batch template variable changes in the meantime.
36
+ *
37
+ * Now the job will fail indefinitely and the later batches won't get their notifications.
38
+ *
39
+ * Even if you're sure you won't have more >MAXIMUM_RECIPIENTS_COUNT recipients, the method
40
+ * enforces other best practices, like setting `expiresAt` on enqueue instead of calculating it in
41
+ * your job on each run. Doing this usually means it's always in the future and doesn't help
42
+ * prevent stale notifications.
43
+ *
44
+ * So please, use this method if not for customers, then to save fellow engineers time debugging!
45
+ *
46
+ * @example
47
+ * <embedex source="packages/notifications/examples/enqueueNotificationJob.ts">
48
+ *
49
+ * ```ts
50
+ * import { IdempotencyKey } from "@clipboard-health/notifications";
51
+ *
52
+ * import { ExampleNotificationJob } from "./exampleNotification.job";
53
+ * import { notificationJobEnqueuer } from "./notificationJobEnqueuer";
54
+ *
55
+ * async function enqueueNotificationJob() {
56
+ * await notificationJobEnqueuer.enqueueOneOrMore(ExampleNotificationJob, {
57
+ * // Set expiresAt at enqueue-time so it remains stable across job retries.
58
+ * expiresAt: minutesFromNow(60),
59
+ * // Set idempotencyKey at enqueue-time so it remains stable across job retries.
60
+ * idempotencyKey: new IdempotencyKey({
61
+ * resourceId: "event-123",
62
+ * }),
63
+ * // Set recipients at enqueue-time so they respect our notification provider's limits.
64
+ * recipients: ["user-1"],
65
+ *
66
+ * workflowKey: "event-starting-reminder",
67
+ *
68
+ * // Any additional enqueue-time data passed to the job:
69
+ * workplaceId: "workplace-123",
70
+ * });
71
+ * }
72
+ *
73
+ * // eslint-disable-next-line unicorn/prefer-top-level-await
74
+ * void enqueueNotificationJob();
75
+ *
76
+ * function minutesFromNow(minutes: number) {
77
+ * return new Date(Date.now() + minutes * 60_000);
78
+ * }
79
+ * ```
80
+ *
81
+ * </embedex>
82
+ */
83
+ async enqueueOneOrMore(handlerClassOrInstance, data,
84
+ // The job's idempotency/unique key is set automatically.
85
+ options) {
86
+ await Promise.all((0, chunkRecipients_1.chunkRecipients)({ recipients: data.recipients }).map(async ({ number, recipients }) => {
87
+ const idempotencyKey = new idempotencyKeyDoNotImportOutsideNotificationsLibrary_1.IdempotencyKeyDoNotImportOutsideNotificationsLibrary({
88
+ ...data.idempotencyKey,
89
+ chunk: number,
90
+ recipients,
91
+ workflowKey: data.workflowKey,
92
+ });
93
+ await this.adapter.enqueue(handlerClassOrInstance, { ...data, recipients, idempotencyKey }, {
94
+ ...(options ? { ...options } : {}),
95
+ [background_jobs_adapter_1.ENQUEUE_FIELD_NAMES[this.adapter.implementation].idempotencyKey]: idempotencyKey.toHash({}),
96
+ });
97
+ }));
98
+ }
99
+ }
100
+ exports.NotificationJobEnqueuer = NotificationJobEnqueuer;
101
+ //# sourceMappingURL=notificationJobEnqueuer.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"notificationJobEnqueuer.js","sourceRoot":"","sources":["../../../../../packages/notifications/src/lib/notificationJobEnqueuer.ts"],"names":[],"mappings":";;;AAAA,uFAGmD;AAGnD,gEAA6D;AAC7D,0IAAuI;AACvI,6DAAmE;AAOnE;;;GAGG;AACU,QAAA,gBAAgB,GAAgB,CAAC,gCAAW,CAAC,OAAO,CAAC,CAAC;AAyBnE,MAAa,uBAAuB;IACjB,OAAO,CAA2C;IAEnE,YAAY,MAAqC;QAC/C,MAAM,EAAE,OAAO,EAAE,GAAG,MAAM,CAAC;QAE3B,IAAI,CAAC,OAAO,GAAG,OAAO,CAAC;IACzB,CAAC;IAED;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;OA+DG;IACH,KAAK,CAAC,gBAAgB,CACpB,sBAA4C,EAC5C,IAAkB;IAClB,yDAAyD;IACzD,OAAiE;QAEjE,MAAM,OAAO,CAAC,GAAG,CACf,IAAA,iCAAe,EAAC,EAAE,UAAU,EAAE,IAAI,CAAC,UAAU,EAAE,CAAC,CAAC,GAAG,CAAC,KAAK,EAAE,EAAE,MAAM,EAAE,UAAU,EAAE,EAAE,EAAE;YACpF,MAAM,cAAc,GAAG,IAAI,2GAAoD,CAAC;gBAC9E,GAAG,IAAI,CAAC,cAAc;gBACtB,KAAK,EAAE,MAAM;gBACb,UAAU;gBACV,WAAW,EAAE,IAAI,CAAC,WAAW;aAC9B,CAAC,CAAC;YAEH,MAAM,IAAI,CAAC,OAAO,CAAC,OAAO,CACxB,sBAAsB,EACtB,EAAE,GAAG,IAAI,EAAE,UAAU,EAAE,cAAc,EAAE,EACvC;gBACE,GAAG,CAAC,OAAO,CAAC,CAAC,CAAC,EAAE,GAAG,OAAO,EAAE,CAAC,CAAC,CAAC,EAAE,CAAC;gBAClC,CAAC,6CAAmB,CAAC,IAAI,CAAC,OAAO,CAAC,cAAc,CAAC,CAAC,cAAc,CAAC,EAC/D,cAAc,CAAC,MAAM,CAAC,EAAE,CAAC;aAC5B,CACF,CAAC;QACJ,CAAC,CAAC,CACH,CAAC;IACJ,CAAC;CACF;AApGD,0DAoGC"}
@@ -1,4 +1,5 @@
1
1
  import { type Logger } from "@clipboard-health/util-ts";
2
+ import { type IdempotencyKeyDoNotImportOutsideNotificationsLibrary } from "./internal/idempotencyKeyDoNotImportOutsideNotificationsLibrary";
2
3
  import { type IdempotentKnock } from "./internal/idempotentKnock";
3
4
  export type Tags = Record<string, unknown>;
4
5
  export interface TraceOptions {
@@ -123,37 +124,33 @@ export interface TriggerBody {
123
124
  * Request parameters for triggering a notification.
124
125
  */
125
126
  export interface TriggerRequest {
126
- /** Notification key. */
127
+ /** Workflow key.
128
+ *
129
+ * @deprecated Use `workflowKey` instead.
130
+ */
127
131
  key: string;
128
- /** Notification payload. */
132
+ /** Workflow key. */
133
+ workflowKey?: string;
134
+ /** Trigger payload. */
129
135
  body: TriggerBody;
130
136
  /**
131
- * Key to prevent duplicate requests if provider supports it. It's important it is deterministic
132
- * ({@link createIdempotencyKey}) and remains the same across retry logic.
133
- *
134
- * If you retry a request with the same idempotency key within 24 hours from the original request,
135
- * we will return the same response as the original request. Idempotent requests are expected to
136
- * be identical. To prevent accidental misuse, the client throws an error when incoming parameters
137
- * don't match those from the original request.
138
- *
139
- * Ensure your idempotency key doesn't prevent recipients from receiving notifications. For
140
- * example, if you use the workflow key and the recipient's ID as the idempotency key, but it's
141
- * possible the recipient could receive the notification multiple times within the idempotency
142
- * key's validity window, the recipient will only receive the first notification.
137
+ * @see {@link IdempotencyKeyDoNotImportOutsideNotificationsLibrary}
143
138
  */
144
- idempotencyKey: string;
139
+ idempotencyKey: string | IdempotencyKeyDoNotImportOutsideNotificationsLibrary;
145
140
  /** Array of data keys to redact in logs for privacy. */
146
141
  keysToRedact?: string[];
147
142
  /**
148
- * Expiration timestamp after which the request is dropped. Use this to prevent stale
149
- * notifications. If, for example, you're notifying about an event that starts in one hour, you
150
- * might set this to one hour from now.
143
+ * `expiresAt` prevents stale notifications across retries by dropping the request when `now >
144
+ * expiresAt`. If, for example, you're notifying about an event that starts in one hour, you might
145
+ * set this to one hour from now.
146
+ *
147
+ * If you are not retrying or your notification is never stale, set it to `false`.
151
148
  *
152
- * If you're triggering from a background job, don't set this at the call site! Set it when you
149
+ * If you're triggering from a background job, don't set this at the call site, set it when you
153
150
  * enqueue the job. Otherwise, it gets updated each time the job retries, will always be in the
154
151
  * future, and won't prevent stale notifications.
155
152
  */
156
- expiresAt: Date;
153
+ expiresAt: Date | false;
157
154
  /** Attempt number for tracing. */
158
155
  attempt: number;
159
156
  }
@@ -1 +1 @@
1
- {"version":3,"file":"types.js","sourceRoot":"","sources":["../../../../../packages/notifications/src/lib/types.ts"],"names":[],"mappings":";;;AA2Ca,QAAA,gBAAgB,GAAG,CAAC,SAAS,EAAE,KAAK,CAAU,CAAC"}
1
+ {"version":3,"file":"types.js","sourceRoot":"","sources":["../../../../../packages/notifications/src/lib/types.ts"],"names":[],"mappings":";;;AA4Ca,QAAA,gBAAgB,GAAG,CAAC,SAAS,EAAE,KAAK,CAAU,CAAC"}
@@ -1,7 +0,0 @@
1
- export declare function chunkRecipients(params: {
2
- idempotencyKey: string;
3
- recipients: string[];
4
- }): Array<{
5
- idempotencyKey: string;
6
- recipients: string[];
7
- }>;
@@ -1,18 +0,0 @@
1
- "use strict";
2
- Object.defineProperty(exports, "__esModule", { value: true });
3
- exports.chunkRecipients = chunkRecipients;
4
- const util_ts_1 = require("@clipboard-health/util-ts");
5
- const notificationClient_1 = require("./notificationClient");
6
- function chunkRecipients(params) {
7
- const { recipients, idempotencyKey } = params;
8
- if (recipients.length === 0) {
9
- return [{ idempotencyKey, recipients: [] }];
10
- }
11
- const idChunks = (0, util_ts_1.chunk)(recipients, notificationClient_1.MAXIMUM_RECIPIENTS_COUNT);
12
- const singleChunk = idChunks.length === 1;
13
- return idChunks.map((ids, index) => ({
14
- idempotencyKey: singleChunk ? idempotencyKey : `${idempotencyKey}-chunk-${index + 1}`,
15
- recipients: ids,
16
- }));
17
- }
18
- //# sourceMappingURL=chunkRecipients.js.map
@@ -1 +0,0 @@
1
- {"version":3,"file":"chunkRecipients.js","sourceRoot":"","sources":["../../../../../packages/notifications/src/lib/chunkRecipients.ts"],"names":[],"mappings":";;AAIA,0CAgBC;AApBD,uDAAkD;AAElD,6DAAgE;AAEhE,SAAgB,eAAe,CAAC,MAG/B;IACC,MAAM,EAAE,UAAU,EAAE,cAAc,EAAE,GAAG,MAAM,CAAC;IAE9C,IAAI,UAAU,CAAC,MAAM,KAAK,CAAC,EAAE,CAAC;QAC5B,OAAO,CAAC,EAAE,cAAc,EAAE,UAAU,EAAE,EAAE,EAAE,CAAC,CAAC;IAC9C,CAAC;IAED,MAAM,QAAQ,GAAG,IAAA,eAAK,EAAC,UAAU,EAAE,6CAAwB,CAAC,CAAC;IAC7D,MAAM,WAAW,GAAG,QAAQ,CAAC,MAAM,KAAK,CAAC,CAAC;IAC1C,OAAO,QAAQ,CAAC,GAAG,CAAC,CAAC,GAAG,EAAE,KAAK,EAAE,EAAE,CAAC,CAAC;QACnC,cAAc,EAAE,WAAW,CAAC,CAAC,CAAC,cAAc,CAAC,CAAC,CAAC,GAAG,cAAc,UAAU,KAAK,GAAG,CAAC,EAAE;QACrF,UAAU,EAAE,GAAG;KAChB,CAAC,CAAC,CAAC;AACN,CAAC"}
@@ -1,17 +0,0 @@
1
- export declare const MAX_IDEMPOTENCY_KEY_LENGTH = 255;
2
- /**
3
- * Creates a deterministic hash for use as an idempotency key.
4
- *
5
- * The function normalizes `value` (using a stable JSON.stringify for non-string values), generates
6
- * a SHA-256 hash, prepends the workflow key, and truncates the result to MAX_IDEMPOTENCY_KEY_LENGTH
7
- * maximum.
8
- *
9
- * @param params.key - Workflow key to prepend to the hash.
10
- * @param params.value - Value to hash (string, string[], object, etc.).
11
- *
12
- * @returns A hash string prefixed with the workflow key.
13
- */
14
- export declare function createIdempotencyKey(params: {
15
- key: string;
16
- value: unknown;
17
- }): string;
@@ -1,28 +0,0 @@
1
- "use strict";
2
- Object.defineProperty(exports, "__esModule", { value: true });
3
- exports.MAX_IDEMPOTENCY_KEY_LENGTH = void 0;
4
- exports.createIdempotencyKey = createIdempotencyKey;
5
- const tslib_1 = require("tslib");
6
- const node_crypto_1 = require("node:crypto");
7
- const fast_json_stable_stringify_1 = tslib_1.__importDefault(require("fast-json-stable-stringify"));
8
- exports.MAX_IDEMPOTENCY_KEY_LENGTH = 255;
9
- /**
10
- * Creates a deterministic hash for use as an idempotency key.
11
- *
12
- * The function normalizes `value` (using a stable JSON.stringify for non-string values), generates
13
- * a SHA-256 hash, prepends the workflow key, and truncates the result to MAX_IDEMPOTENCY_KEY_LENGTH
14
- * maximum.
15
- *
16
- * @param params.key - Workflow key to prepend to the hash.
17
- * @param params.value - Value to hash (string, string[], object, etc.).
18
- *
19
- * @returns A hash string prefixed with the workflow key.
20
- */
21
- function createIdempotencyKey(params) {
22
- const { key, value } = params;
23
- const hash = (0, node_crypto_1.createHash)("sha256")
24
- .update(typeof value === "string" ? value : (0, fast_json_stable_stringify_1.default)(value))
25
- .digest("hex");
26
- return `${key}:${hash}`.slice(0, exports.MAX_IDEMPOTENCY_KEY_LENGTH);
27
- }
28
- //# sourceMappingURL=createIdempotencyKey.js.map
@@ -1 +0,0 @@
1
- {"version":3,"file":"createIdempotencyKey.js","sourceRoot":"","sources":["../../../../../packages/notifications/src/lib/createIdempotencyKey.ts"],"names":[],"mappings":";;;AAkBA,oDAQC;;AA1BD,6CAAyC;AAEzC,oGAAmD;AAEtC,QAAA,0BAA0B,GAAG,GAAG,CAAC;AAE9C;;;;;;;;;;;GAWG;AACH,SAAgB,oBAAoB,CAAC,MAAuC;IAC1E,MAAM,EAAE,GAAG,EAAE,KAAK,EAAE,GAAG,MAAM,CAAC;IAE9B,MAAM,IAAI,GAAG,IAAA,wBAAU,EAAC,QAAQ,CAAC;SAC9B,MAAM,CAAC,OAAO,KAAK,KAAK,QAAQ,CAAC,CAAC,CAAC,KAAK,CAAC,CAAC,CAAC,IAAA,oCAAS,EAAC,KAAK,CAAC,CAAC;SAC5D,MAAM,CAAC,KAAK,CAAC,CAAC;IAEjB,OAAO,GAAG,GAAG,IAAI,IAAI,EAAE,CAAC,KAAK,CAAC,CAAC,EAAE,kCAA0B,CAAC,CAAC;AAC/D,CAAC"}