@fluojs/notifications 1.0.0-beta.3 → 1.0.0-beta.5
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/README.ko.md +9 -4
- package/README.md +9 -4
- package/dist/service.d.ts +4 -1
- package/dist/service.d.ts.map +1 -1
- package/dist/service.js +126 -14
- package/dist/types.d.ts +8 -0
- package/dist/types.d.ts.map +1 -1
- package/package.json +4 -4
package/README.ko.md
CHANGED
|
@@ -120,9 +120,10 @@ Behavioral contract 메모:
|
|
|
120
120
|
- `dispatch()`는 queue adapter가 구성되어 있어도 기본적으로 직접 전달을 유지합니다. 단건 알림을 큐로 보내려면 `dispatch(..., { queue: true })`를 사용합니다.
|
|
121
121
|
- queue adapter가 있어도 직접 전달을 강제하려면 `dispatch(..., { queue: false })`를 사용합니다.
|
|
122
122
|
- 큐 기반 전달은 단건 dispatch에서는 opt-in이고, `dispatchMany(...)`에서는 threshold 기반으로 동작합니다.
|
|
123
|
-
- `
|
|
124
|
-
-
|
|
125
|
-
- `
|
|
123
|
+
- Queue job에는 `notification.id`가 있으면 그 값을, 없으면 notification envelope에서 파생한 deterministic `id` idempotency key가 포함됩니다. Queue adapter는 deduplication을 지원하는 backing queue에 이 값을 전달해야 합니다.
|
|
124
|
+
- `dispatchMany(..., { continueOnError: true })`는 direct delivery 또는 순차 queue fallback enqueue에서 첫 실패를 던지는 대신 실패들을 수집합니다.
|
|
125
|
+
- queue enqueue가 실패하면 서비스는 enqueue 에러를 다시 던지기 전에 결정적인 `notification.dispatch.failed` 라이프사이클 이벤트를 발행합니다. queued bulk dispatch는 queue 미구성, channel 해석, provider/adapter failure 경로를 포함해 이미 `requested`를 발행한 모든 notification에 대해 terminal `queued` 또는 `failed` 이벤트도 발행합니다.
|
|
126
|
+
- `enqueueMany(...)`가 없으면 대량 queue delivery는 input order대로 각 job을 개별 enqueue하는 방식으로 fallback합니다. `continueOnError: true`이면 성공한 enqueue는 `results`에 남고 실패한 enqueue는 `failures`로 반환됩니다. 그렇지 않으면 첫 enqueue failure를 다시 던지기 전에 아직 terminal 상태가 없는 나머지 requested fallback job에 `failed` 라이프사이클 이벤트를 발행합니다.
|
|
126
127
|
- foundation 패키지는 특정 큐 구현을 가정하거나 import하지 않습니다.
|
|
127
128
|
|
|
128
129
|
### 이벤트 발행자를 통한 라이프사이클 발행
|
|
@@ -150,7 +151,7 @@ NotificationsModule.forRoot({
|
|
|
150
151
|
- `notification.dispatch.delivered`
|
|
151
152
|
- `notification.dispatch.failed`
|
|
152
153
|
|
|
153
|
-
`events.publisher`가 구성되어 있으면 `publishLifecycleEvents: false`를 설정하지 않는 한 lifecycle event publication은 기본으로 켜집니다. 채널 delivery가 `externalId`를 생략하면 dispatch result가 호출자에게 안정적으로
|
|
154
|
+
`events.publisher`가 구성되어 있으면 `publishLifecycleEvents: false`를 설정하지 않는 한 lifecycle event publication은 기본으로 켜집니다. 채널 delivery가 `externalId`를 생략하면 시간이나 난수에 의존하지 않고 notification envelope에서 파생한 deterministic fallback delivery id가 부여되어 dispatch result가 호출자에게 안정적으로 유지됩니다. 채널 해석 실패는 `NotificationChannelNotFoundError`를 던지기 전에 `requested` 이후 `failed` 이벤트를 발행하며, 이는 영구적인 구성 오류로 취급해야 합니다. Queue enqueue와 provider delivery 실패도 `failed` 이벤트를 발행하지만, retry 여부는 underlying adapter/provider error를 기준으로 분류해야 합니다. 성공 경로 lifecycle event의 publication failure는 이미 전달된 알림을 애플리케이션 실패로 바꾸지 않도록 best-effort로 유지됩니다. `notification.dispatch.failed` publication failure는 원래 dispatch error와 publisher error를 모두 포함하는 `AggregateError`로 호출자에게 드러나므로 failed-event 보장이 조용히 약해지지 않습니다.
|
|
154
155
|
|
|
155
156
|
### 의도적인 제한 사항
|
|
156
157
|
|
|
@@ -170,6 +171,7 @@ foundation 패키지는 의도적으로 다음을 **포함하지 않습니다**:
|
|
|
170
171
|
- `NotificationsModule.forRoot(options)` / `NotificationsModule.forRootAsync(options)`
|
|
171
172
|
- `NotificationsService`
|
|
172
173
|
- `NotificationsService.createPlatformStatusSnapshot()`
|
|
174
|
+
- `Notifications`
|
|
173
175
|
- `NOTIFICATIONS`
|
|
174
176
|
- `NOTIFICATION_CHANNELS`
|
|
175
177
|
|
|
@@ -179,6 +181,7 @@ foundation 패키지는 의도적으로 다음을 **포함하지 않습니다**:
|
|
|
179
181
|
- `NotificationDispatchOptions`
|
|
180
182
|
- `NotificationDispatchManyOptions`
|
|
181
183
|
- `NotificationDispatchResult`
|
|
184
|
+
- `NotificationDispatchBatchResult`
|
|
182
185
|
- `NotificationDispatchFailure`
|
|
183
186
|
- `NotificationDispatchStatus`
|
|
184
187
|
- `NotificationChannel`
|
|
@@ -187,6 +190,8 @@ foundation 패키지는 의도적으로 다음을 **포함하지 않습니다**:
|
|
|
187
190
|
- `NotificationPayload`
|
|
188
191
|
- `NotificationsQueueAdapter`
|
|
189
192
|
- `NotificationsQueueJob`
|
|
193
|
+
- `NotificationsQueueOptions`
|
|
194
|
+
- `NotificationsModuleOptions`
|
|
190
195
|
- `NotificationsAsyncModuleOptions`
|
|
191
196
|
- `NotificationsEventsOptions`
|
|
192
197
|
- `NotificationsEventPublisher`
|
package/README.md
CHANGED
|
@@ -120,9 +120,10 @@ Behavioral contract notes:
|
|
|
120
120
|
- `dispatch()` stays direct by default even when a queue adapter is configured. Use `dispatch(..., { queue: true })` to opt one single notification into queue-backed delivery.
|
|
121
121
|
- Use `dispatch(..., { queue: false })` to force direct delivery even when a queue adapter exists.
|
|
122
122
|
- Queue-backed delivery is opt-in for single dispatch and threshold-driven for `dispatchMany(...)`.
|
|
123
|
-
-
|
|
124
|
-
-
|
|
125
|
-
-
|
|
123
|
+
- Queue jobs include a deterministic `id` idempotency key derived from `notification.id` when present, otherwise from the notification envelope. Queue adapters should pass this value to backing queues that support deduplication.
|
|
124
|
+
- `dispatchMany(..., { continueOnError: true })` collects failures instead of throwing on the first failed direct delivery or sequential queue fallback enqueue.
|
|
125
|
+
- When queue enqueue fails, the service emits deterministic `notification.dispatch.failed` lifecycle events before rethrowing the enqueue error to the caller. Queued bulk dispatch also publishes a terminal `queued` or `failed` event for every notification that already emitted `requested`, including queue-missing, channel-resolution, and provider/adapter failure paths.
|
|
126
|
+
- If `enqueueMany(...)` is unavailable, bulk queue delivery falls back to enqueueing each job individually in input order. With `continueOnError: true`, successful enqueues remain visible in `results` while failed enqueues are returned in `failures`; without it, the first enqueue failure is rethrown after the remaining requested fallback jobs receive `failed` lifecycle events.
|
|
126
127
|
- The foundation package does not assume or import a concrete queue implementation.
|
|
127
128
|
|
|
128
129
|
### Lifecycle publication through an event publisher
|
|
@@ -150,7 +151,7 @@ Published event names:
|
|
|
150
151
|
- `notification.dispatch.delivered`
|
|
151
152
|
- `notification.dispatch.failed`
|
|
152
153
|
|
|
153
|
-
If `events.publisher` is configured, lifecycle event publication defaults to on unless `publishLifecycleEvents: false` is set. Channel deliveries that omit `externalId` receive a deterministic fallback delivery id so dispatch results remain stable for callers.
|
|
154
|
+
If `events.publisher` is configured, lifecycle event publication defaults to on unless `publishLifecycleEvents: false` is set. Channel deliveries that omit `externalId` receive a deterministic fallback delivery id derived from the notification envelope so dispatch results remain stable for callers without relying on time or random data. Channel resolution failures publish `requested` and then `failed` events before throwing `NotificationChannelNotFoundError`; treat those failures as permanent configuration errors. Queue enqueue and provider delivery failures also publish `failed` events, but callers should classify their retry behavior from the underlying adapter/provider error. Publication failures for success-path lifecycle events remain best-effort so a delivered notification is not converted into an application failure. Publication failures for `notification.dispatch.failed` are caller-visible as `AggregateError` values that include both the original dispatch error and the publisher error so failed-event guarantees are not silently weakened.
|
|
154
155
|
|
|
155
156
|
### Intentional limitations
|
|
156
157
|
|
|
@@ -170,6 +171,7 @@ These limitations are part of the package contract so leaf packages can evolve i
|
|
|
170
171
|
- `NotificationsModule.forRoot(options)` / `NotificationsModule.forRootAsync(options)`
|
|
171
172
|
- `NotificationsService`
|
|
172
173
|
- `NotificationsService.createPlatformStatusSnapshot()`
|
|
174
|
+
- `Notifications`
|
|
173
175
|
- `NOTIFICATIONS`
|
|
174
176
|
- `NOTIFICATION_CHANNELS`
|
|
175
177
|
|
|
@@ -179,6 +181,7 @@ These limitations are part of the package contract so leaf packages can evolve i
|
|
|
179
181
|
- `NotificationDispatchOptions`
|
|
180
182
|
- `NotificationDispatchManyOptions`
|
|
181
183
|
- `NotificationDispatchResult`
|
|
184
|
+
- `NotificationDispatchBatchResult`
|
|
182
185
|
- `NotificationDispatchFailure`
|
|
183
186
|
- `NotificationDispatchStatus`
|
|
184
187
|
- `NotificationChannel`
|
|
@@ -187,6 +190,8 @@ These limitations are part of the package contract so leaf packages can evolve i
|
|
|
187
190
|
- `NotificationPayload`
|
|
188
191
|
- `NotificationsQueueAdapter`
|
|
189
192
|
- `NotificationsQueueJob`
|
|
193
|
+
- `NotificationsQueueOptions`
|
|
194
|
+
- `NotificationsModuleOptions`
|
|
190
195
|
- `NotificationsAsyncModuleOptions`
|
|
191
196
|
- `NotificationsEventsOptions`
|
|
192
197
|
- `NotificationsEventPublisher`
|
package/dist/service.d.ts
CHANGED
|
@@ -10,7 +10,6 @@ import type { NormalizedNotificationsModuleOptions, NotificationChannel, Notific
|
|
|
10
10
|
export declare class NotificationsService implements Notifications {
|
|
11
11
|
private readonly options;
|
|
12
12
|
private readonly channelsByName;
|
|
13
|
-
private fallbackDeliveryIdSequence;
|
|
14
13
|
constructor(options: NormalizedNotificationsModuleOptions, channels: readonly NotificationChannel[]);
|
|
15
14
|
/**
|
|
16
15
|
* Dispatches one notification through a registered channel or the configured queue seam.
|
|
@@ -50,6 +49,7 @@ export declare class NotificationsService implements Notifications {
|
|
|
50
49
|
*/
|
|
51
50
|
createPlatformStatusSnapshot(): import("./status.js").NotificationsPlatformStatusSnapshot;
|
|
52
51
|
private createQueueJob;
|
|
52
|
+
private createQueueJobId;
|
|
53
53
|
private requireChannel;
|
|
54
54
|
private normalizeDeliveryId;
|
|
55
55
|
private requireQueueAdapter;
|
|
@@ -58,5 +58,8 @@ export declare class NotificationsService implements Notifications {
|
|
|
58
58
|
private shouldQueue;
|
|
59
59
|
private publishLifecycleEvent;
|
|
60
60
|
private publishLifecycleEventSafely;
|
|
61
|
+
private publishFailureLifecycleEvent;
|
|
62
|
+
private publishFailureLifecycleEvents;
|
|
63
|
+
private dispatchManyThroughSequentialQueueFallback;
|
|
61
64
|
}
|
|
62
65
|
//# sourceMappingURL=service.d.ts.map
|
package/dist/service.d.ts.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"service.d.ts","sourceRoot":"","sources":["../src/service.ts"],"names":[],"mappings":"AAQA,OAAO,KAAK,EACV,oCAAoC,EACpC,mBAAmB,EACnB,+BAA+B,EAC/B,+BAA+B,EAC/B,2BAA2B,EAC3B,2BAA2B,EAC3B,0BAA0B,EAE1B,aAAa,EAEd,MAAM,YAAY,CAAC;AAEpB;;;;;;;GAOG;AACH,qBACa,oBAAqB,YAAW,aAAa;
|
|
1
|
+
{"version":3,"file":"service.d.ts","sourceRoot":"","sources":["../src/service.ts"],"names":[],"mappings":"AAQA,OAAO,KAAK,EACV,oCAAoC,EACpC,mBAAmB,EACnB,+BAA+B,EAC/B,+BAA+B,EAC/B,2BAA2B,EAC3B,2BAA2B,EAC3B,0BAA0B,EAE1B,aAAa,EAEd,MAAM,YAAY,CAAC;AAEpB;;;;;;;GAOG;AACH,qBACa,oBAAqB,YAAW,aAAa;IAItD,OAAO,CAAC,QAAQ,CAAC,OAAO;IAH1B,OAAO,CAAC,QAAQ,CAAC,cAAc,CAA0C;gBAGtD,OAAO,EAAE,oCAAoC,EAC9D,QAAQ,EAAE,SAAS,mBAAmB,EAAE;IAO1C;;;;;;;;;;;;;;;;;;;OAmBG;IACG,QAAQ,CAAC,QAAQ,SAAS,2BAA2B,EACzD,YAAY,EAAE,QAAQ,EACtB,OAAO,GAAE,2BAAgC,GACxC,OAAO,CAAC,0BAA0B,CAAC;IA+DtC;;;;;;;;OAQG;IACG,YAAY,CAAC,QAAQ,SAAS,2BAA2B,EAC7D,aAAa,EAAE,SAAS,QAAQ,EAAE,EAClC,OAAO,GAAE,+BAAoC,GAC5C,OAAO,CAAC,+BAA+B,CAAC,QAAQ,CAAC,CAAC;IAmGrD;;;;OAIG;IACH,4BAA4B;IAS5B,OAAO,CAAC,cAAc;IAStB,OAAO,CAAC,gBAAgB;IAQxB,OAAO,CAAC,cAAc;IAUtB,OAAO,CAAC,mBAAmB;IAY3B,OAAO,CAAC,mBAAmB;IAQ3B,OAAO,CAAC,4BAA4B;IAQpC,OAAO,CAAC,yBAAyB;IAIjC,OAAO,CAAC,WAAW;YAYL,qBAAqB;YA4BrB,2BAA2B;YAc3B,4BAA4B;YAY5B,6BAA6B;YAoB7B,0CAA0C;CA2DzD"}
|
package/dist/service.js
CHANGED
|
@@ -22,7 +22,6 @@ class NotificationsService {
|
|
|
22
22
|
[_NotificationsService, _initClass] = _applyDecs(this, [Inject(NOTIFICATIONS_OPTIONS, NOTIFICATION_CHANNELS)], []).c;
|
|
23
23
|
}
|
|
24
24
|
channelsByName = new Map();
|
|
25
|
-
fallbackDeliveryIdSequence = 0;
|
|
26
25
|
constructor(options, channels) {
|
|
27
26
|
this.options = options;
|
|
28
27
|
for (const channel of channels) {
|
|
@@ -53,7 +52,12 @@ class NotificationsService {
|
|
|
53
52
|
async dispatch(notification, options = {}) {
|
|
54
53
|
await this.publishLifecycleEventSafely('notification.dispatch.requested', notification, options);
|
|
55
54
|
if (this.shouldQueueSingleDispatch(options)) {
|
|
56
|
-
|
|
55
|
+
try {
|
|
56
|
+
this.requireChannel(notification.channel);
|
|
57
|
+
} catch (error) {
|
|
58
|
+
await this.publishFailureLifecycleEvent(notification, options, error);
|
|
59
|
+
throw error;
|
|
60
|
+
}
|
|
57
61
|
const job = this.createQueueJob(notification);
|
|
58
62
|
try {
|
|
59
63
|
const deliveryId = await this.requireQueueAdapter().enqueue(job);
|
|
@@ -66,11 +70,17 @@ class NotificationsService {
|
|
|
66
70
|
await this.publishLifecycleEventSafely('notification.dispatch.queued', notification, options, result.deliveryId);
|
|
67
71
|
return result;
|
|
68
72
|
} catch (error) {
|
|
69
|
-
await this.
|
|
73
|
+
await this.publishFailureLifecycleEvent(notification, options, error);
|
|
70
74
|
throw error;
|
|
71
75
|
}
|
|
72
76
|
}
|
|
73
|
-
|
|
77
|
+
let channel;
|
|
78
|
+
try {
|
|
79
|
+
channel = this.requireChannel(notification.channel);
|
|
80
|
+
} catch (error) {
|
|
81
|
+
await this.publishFailureLifecycleEvent(notification, options, error);
|
|
82
|
+
throw error;
|
|
83
|
+
}
|
|
74
84
|
try {
|
|
75
85
|
const delivery = await channel.send(notification, {
|
|
76
86
|
signal: options.signal
|
|
@@ -85,7 +95,7 @@ class NotificationsService {
|
|
|
85
95
|
await this.publishLifecycleEventSafely(result.queued ? 'notification.dispatch.queued' : 'notification.dispatch.delivered', notification, options, result.deliveryId);
|
|
86
96
|
return result;
|
|
87
97
|
} catch (error) {
|
|
88
|
-
await this.
|
|
98
|
+
await this.publishFailureLifecycleEvent(notification, options, error);
|
|
89
99
|
throw error;
|
|
90
100
|
}
|
|
91
101
|
}
|
|
@@ -110,19 +120,33 @@ class NotificationsService {
|
|
|
110
120
|
};
|
|
111
121
|
}
|
|
112
122
|
if (this.shouldQueue(notifications.length, options)) {
|
|
113
|
-
const queue = this.requireQueueAdapter();
|
|
114
123
|
for (const notification of notifications) {
|
|
115
|
-
this.
|
|
124
|
+
await this.publishLifecycleEventSafely('notification.dispatch.requested', notification, options);
|
|
125
|
+
}
|
|
126
|
+
let queue;
|
|
127
|
+
try {
|
|
128
|
+
queue = this.requireQueueAdapter();
|
|
129
|
+
} catch (error) {
|
|
130
|
+
await this.publishFailureLifecycleEvents(notifications, options, error);
|
|
131
|
+
throw error;
|
|
132
|
+
}
|
|
133
|
+
try {
|
|
134
|
+
for (const notification of notifications) {
|
|
135
|
+
this.requireChannel(notification.channel);
|
|
136
|
+
}
|
|
137
|
+
} catch (error) {
|
|
138
|
+
await this.publishFailureLifecycleEvents(notifications, options, error);
|
|
139
|
+
throw error;
|
|
116
140
|
}
|
|
117
141
|
const jobs = notifications.map(notification => this.createQueueJob(notification));
|
|
118
|
-
|
|
119
|
-
|
|
142
|
+
if (!queue.enqueueMany) {
|
|
143
|
+
return this.dispatchManyThroughSequentialQueueFallback(notifications, jobs, options);
|
|
120
144
|
}
|
|
121
145
|
let ids;
|
|
122
146
|
try {
|
|
123
|
-
ids =
|
|
147
|
+
ids = await queue.enqueueMany(jobs);
|
|
124
148
|
} catch (error) {
|
|
125
|
-
await
|
|
149
|
+
await this.publishFailureLifecycleEvents(notifications, options, error);
|
|
126
150
|
throw error;
|
|
127
151
|
}
|
|
128
152
|
const results = notifications.map((notification, index) => ({
|
|
@@ -184,10 +208,17 @@ class NotificationsService {
|
|
|
184
208
|
createQueueJob(notification) {
|
|
185
209
|
return {
|
|
186
210
|
channel: notification.channel,
|
|
211
|
+
id: this.createQueueJobId(notification),
|
|
187
212
|
notification,
|
|
188
213
|
queuedAt: new Date().toISOString()
|
|
189
214
|
};
|
|
190
215
|
}
|
|
216
|
+
createQueueJobId(notification) {
|
|
217
|
+
if (notification.id && notification.id.length > 0) {
|
|
218
|
+
return notification.id;
|
|
219
|
+
}
|
|
220
|
+
return `notification:${notification.channel}:${stableNotificationHash(notification)}`;
|
|
221
|
+
}
|
|
191
222
|
requireChannel(channelName) {
|
|
192
223
|
const channel = this.channelsByName.get(channelName);
|
|
193
224
|
if (!channel) {
|
|
@@ -202,8 +233,7 @@ class NotificationsService {
|
|
|
202
233
|
if (fallback.id) {
|
|
203
234
|
return fallback.id;
|
|
204
235
|
}
|
|
205
|
-
|
|
206
|
-
return `${fallback.channel}:${Date.now().toString(36)}:${this.fallbackDeliveryIdSequence.toString(36)}:${Math.random().toString(36).slice(2, 10)}`;
|
|
236
|
+
return `fallback:${fallback.channel}:${stableNotificationHash(fallback)}`;
|
|
207
237
|
}
|
|
208
238
|
requireQueueAdapter() {
|
|
209
239
|
if (!this.options.queue) {
|
|
@@ -253,8 +283,90 @@ class NotificationsService {
|
|
|
253
283
|
return;
|
|
254
284
|
}
|
|
255
285
|
}
|
|
286
|
+
async publishFailureLifecycleEvent(notification, options, error) {
|
|
287
|
+
try {
|
|
288
|
+
await this.publishLifecycleEvent('notification.dispatch.failed', notification, options, undefined, error);
|
|
289
|
+
} catch (publicationError) {
|
|
290
|
+
throw createLifecyclePublicationFailureError(error, publicationError);
|
|
291
|
+
}
|
|
292
|
+
}
|
|
293
|
+
async publishFailureLifecycleEvents(notifications, options, error) {
|
|
294
|
+
const failurePublicationResults = await Promise.allSettled(notifications.map(notification => this.publishLifecycleEvent('notification.dispatch.failed', notification, options, undefined, error)));
|
|
295
|
+
const publicationFailures = failurePublicationResults.filter(result => result.status === 'rejected').map(result => result.reason);
|
|
296
|
+
if (publicationFailures.length > 0) {
|
|
297
|
+
throw createLifecyclePublicationFailureError(error, ...publicationFailures);
|
|
298
|
+
}
|
|
299
|
+
}
|
|
300
|
+
async dispatchManyThroughSequentialQueueFallback(notifications, jobs, options) {
|
|
301
|
+
const queue = this.requireQueueAdapter();
|
|
302
|
+
const results = [];
|
|
303
|
+
const failures = [];
|
|
304
|
+
for (let index = 0; index < jobs.length; index += 1) {
|
|
305
|
+
const job = jobs[index];
|
|
306
|
+
const notification = notifications[index];
|
|
307
|
+
if (!job || !notification) {
|
|
308
|
+
continue;
|
|
309
|
+
}
|
|
310
|
+
try {
|
|
311
|
+
const deliveryId = this.normalizeDeliveryId(await queue.enqueue(job), notification);
|
|
312
|
+
const result = {
|
|
313
|
+
channel: notification.channel,
|
|
314
|
+
deliveryId,
|
|
315
|
+
queued: true,
|
|
316
|
+
status: 'queued'
|
|
317
|
+
};
|
|
318
|
+
results.push(result);
|
|
319
|
+
await this.publishLifecycleEventSafely('notification.dispatch.queued', notification, options, deliveryId);
|
|
320
|
+
} catch (error) {
|
|
321
|
+
const failure = {
|
|
322
|
+
error: error instanceof Error ? error : new Error('Notification queue enqueue failed.'),
|
|
323
|
+
notification
|
|
324
|
+
};
|
|
325
|
+
if (!(options.continueOnError ?? false)) {
|
|
326
|
+
await this.publishFailureLifecycleEvents(notifications.slice(index), options, error);
|
|
327
|
+
throw error;
|
|
328
|
+
}
|
|
329
|
+
try {
|
|
330
|
+
await this.publishFailureLifecycleEvent(notification, options, error);
|
|
331
|
+
} catch (publicationError) {
|
|
332
|
+
failure.error = publicationError instanceof Error ? publicationError : createLifecyclePublicationFailureError(error, publicationError);
|
|
333
|
+
}
|
|
334
|
+
failures.push(failure);
|
|
335
|
+
}
|
|
336
|
+
}
|
|
337
|
+
return {
|
|
338
|
+
failed: failures.length,
|
|
339
|
+
failures,
|
|
340
|
+
queued: results.length,
|
|
341
|
+
results,
|
|
342
|
+
succeeded: results.length
|
|
343
|
+
};
|
|
344
|
+
}
|
|
256
345
|
static {
|
|
257
346
|
_initClass();
|
|
258
347
|
}
|
|
259
348
|
}
|
|
260
|
-
export { _NotificationsService as NotificationsService };
|
|
349
|
+
export { _NotificationsService as NotificationsService };
|
|
350
|
+
function createLifecyclePublicationFailureError(dispatchError, ...publicationErrors) {
|
|
351
|
+
const primaryMessage = dispatchError instanceof Error ? dispatchError.message : 'Notification dispatch failed.';
|
|
352
|
+
return new AggregateError([dispatchError, ...publicationErrors], `Notification dispatch failed, and failed lifecycle event publication also failed: ${primaryMessage}`);
|
|
353
|
+
}
|
|
354
|
+
function stableNotificationHash(notification) {
|
|
355
|
+
let hash = 0x811c9dc5;
|
|
356
|
+
const input = stableStringify(notification);
|
|
357
|
+
for (let index = 0; index < input.length; index += 1) {
|
|
358
|
+
hash ^= input.charCodeAt(index);
|
|
359
|
+
hash = Math.imul(hash, 0x01000193) >>> 0;
|
|
360
|
+
}
|
|
361
|
+
return hash.toString(36).padStart(7, '0');
|
|
362
|
+
}
|
|
363
|
+
function stableStringify(value) {
|
|
364
|
+
if (value === null || typeof value !== 'object') {
|
|
365
|
+
return JSON.stringify(value) ?? String(value);
|
|
366
|
+
}
|
|
367
|
+
if (Array.isArray(value)) {
|
|
368
|
+
return `[${value.map(entry => stableStringify(entry)).join(',')}]`;
|
|
369
|
+
}
|
|
370
|
+
const entries = Object.entries(value).filter(([, entry]) => entry !== undefined).sort(([left], [right]) => left.localeCompare(right));
|
|
371
|
+
return `{${entries.map(([key, entry]) => `${JSON.stringify(key)}:${stableStringify(entry)}`).join(',')}}`;
|
|
372
|
+
}
|
package/dist/types.d.ts
CHANGED
|
@@ -53,6 +53,14 @@ export interface NotificationChannel<TRequest extends NotificationDispatchReques
|
|
|
53
53
|
/** Job payload forwarded to an optional queue adapter for deferred delivery. */
|
|
54
54
|
export interface NotificationsQueueJob<TRequest extends NotificationDispatchRequest = NotificationDispatchRequest> {
|
|
55
55
|
channel: string;
|
|
56
|
+
/**
|
|
57
|
+
* Deterministic idempotency key derived from the notification envelope.
|
|
58
|
+
*
|
|
59
|
+
* @remarks
|
|
60
|
+
* Queue adapters should use this value as their deduplication key when the
|
|
61
|
+
* backing queue supports idempotent enqueue semantics.
|
|
62
|
+
*/
|
|
63
|
+
id: string;
|
|
56
64
|
notification: TRequest;
|
|
57
65
|
queuedAt: string;
|
|
58
66
|
}
|
package/dist/types.d.ts.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"types.d.ts","sourceRoot":"","sources":["../src/types.ts"],"names":[],"mappings":"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"}
|
|
1
|
+
{"version":3,"file":"types.d.ts","sourceRoot":"","sources":["../src/types.ts"],"names":[],"mappings":"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"}
|
package/package.json
CHANGED
|
@@ -9,7 +9,7 @@
|
|
|
9
9
|
"event-bus",
|
|
10
10
|
"channels"
|
|
11
11
|
],
|
|
12
|
-
"version": "1.0.0-beta.
|
|
12
|
+
"version": "1.0.0-beta.5",
|
|
13
13
|
"private": false,
|
|
14
14
|
"license": "MIT",
|
|
15
15
|
"repository": {
|
|
@@ -36,9 +36,9 @@
|
|
|
36
36
|
"dist"
|
|
37
37
|
],
|
|
38
38
|
"dependencies": {
|
|
39
|
-
"@fluojs/core": "^1.0.0-beta.
|
|
40
|
-
"@fluojs/di": "^1.0.0-beta.
|
|
41
|
-
"@fluojs/runtime": "^1.0.0-beta.
|
|
39
|
+
"@fluojs/core": "^1.0.0-beta.6",
|
|
40
|
+
"@fluojs/di": "^1.0.0-beta.8",
|
|
41
|
+
"@fluojs/runtime": "^1.0.0-beta.12"
|
|
42
42
|
},
|
|
43
43
|
"devDependencies": {
|
|
44
44
|
"vitest": "^3.2.4"
|