@fluojs/notifications 1.0.2 → 2.0.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.
- package/README.ko.md +43 -8
- package/README.md +43 -8
- package/dist/errors.d.ts +7 -0
- package/dist/errors.d.ts.map +1 -1
- package/dist/errors.js +11 -0
- package/dist/index.d.ts +3 -3
- package/dist/index.d.ts.map +1 -1
- package/dist/index.js +1 -1
- package/dist/module.d.ts +4 -2
- package/dist/module.d.ts.map +1 -1
- package/dist/module.js +9 -11
- package/dist/service.d.ts +3 -0
- package/dist/service.d.ts.map +1 -1
- package/dist/service.js +195 -68
- package/dist/snapshots.d.ts +28 -0
- package/dist/snapshots.d.ts.map +1 -0
- package/dist/snapshots.js +240 -0
- package/dist/status.d.ts +32 -2
- package/dist/status.d.ts.map +1 -1
- package/dist/status.js +37 -13
- package/dist/types.d.ts +91 -12
- package/dist/types.d.ts.map +1 -1
- package/package.json +7 -6
package/README.ko.md
CHANGED
|
@@ -2,6 +2,8 @@
|
|
|
2
2
|
|
|
3
3
|
<p><a href="./README.md"><kbd>English</kbd></a> <strong><kbd>한국어</kbd></strong></p>
|
|
4
4
|
|
|
5
|
+
Node.js 지원 범위는 `>=24.0.0 <27`입니다. 업그레이드 절차는 [Node.js 지원 및 마이그레이션](../../docs/reference/node-support.ko.md)을 참조하세요.
|
|
6
|
+
|
|
5
7
|
fluo를 위한 채널 중립(notification channel-agnostic) 알림 오케스트레이션 패키지입니다. 알림 채널의 공통 계약을 고정하고, 친숙한 dynamic-module 사용감을 가진 명시적 모듈 등록 API를 제공하며, 선택적인 큐 기반 전달 심(seam)과 라이프사이클 이벤트 발행 심을 노출합니다.
|
|
6
8
|
|
|
7
9
|
## 목차
|
|
@@ -104,10 +106,10 @@ NotificationsModule.forRoot({
|
|
|
104
106
|
channels: [emailChannel],
|
|
105
107
|
queue: {
|
|
106
108
|
adapter: {
|
|
107
|
-
async enqueue(job) {
|
|
109
|
+
async enqueue(job, context) {
|
|
108
110
|
return queue.enqueue(job);
|
|
109
111
|
},
|
|
110
|
-
async enqueueMany(jobs) {
|
|
112
|
+
async enqueueMany(jobs, context) {
|
|
111
113
|
return Promise.all(jobs.map((job) => queue.enqueue(job)));
|
|
112
114
|
},
|
|
113
115
|
},
|
|
@@ -118,11 +120,16 @@ NotificationsModule.forRoot({
|
|
|
118
120
|
|
|
119
121
|
Behavioral contract 메모:
|
|
120
122
|
|
|
121
|
-
-
|
|
123
|
+
- `bulkThreshold`를 생략하면 기본값 `10`을 사용합니다. 명시적 값은 유한한 양의 정수여야 하며, 잘못된 값은 notification service provider가 생성되기 전에 module option 해석을 `NotificationsConfigurationError`로 실패시킵니다.
|
|
124
|
+
- 알림 개수가 `bulkThreshold` 이상이면 대량 큐 위임이 시작되며, `dispatchMany(..., { queue: true })`는 batch가 threshold보다 작아도 queue-backed delivery를 명시적으로 강제합니다.
|
|
122
125
|
- `dispatch()`는 queue adapter가 구성되어 있어도 기본적으로 직접 전달을 유지합니다. 단건 알림을 큐로 보내려면 `dispatch(..., { queue: true })`를 사용합니다.
|
|
123
126
|
- queue adapter가 있어도 직접 전달을 강제하려면 `dispatch(..., { queue: false })`를 사용합니다.
|
|
124
|
-
- 큐 기반 전달은 단건 dispatch에서는 opt-in이고, `dispatchMany(...)`에서는 threshold 기반으로 동작합니다.
|
|
125
|
-
-
|
|
127
|
+
- 큐 기반 전달은 단건 dispatch에서는 opt-in이고, `dispatchMany(...)`에서는 caller가 `{ queue: true }`를 명시하지 않는 한 threshold 기반으로 동작합니다.
|
|
128
|
+
- `NotificationsQueueContext`는 `enqueue(...)`와 `enqueueMany(...)`의 선택적인 두 번째 인자입니다. dispatch의 정확한 caller-owned `AbortSignal`을 전달하며, 기존의 한 인자 adapter도 계속 유효합니다.
|
|
129
|
+
- 서비스는 모든 queue handoff 직전에 signal을 확인한 뒤 같은 live signal을 adapter에 전달합니다. 이미 abort된 queued dispatch는 lifecycle event가 활성화된 경우 `requested` 다음 `failed`를 발행하지만 `enqueue(...)`나 `enqueueMany(...)`를 호출하지 않습니다. Adapter가 job을 수락한 뒤 발생한 abort signal은 이미 수락된 queue job을 취소하지 못합니다.
|
|
130
|
+
- Queue adapter는 queue별 cancellation policy와 enqueue operation이 settle될 때 abort listener를 제거할 책임을 집니다. Adapter가 signal abort로 reject하면 native bulk dispatch는 reject되고 모든 requested job에 `failed`를 발행합니다. Sequential fallback은 이후 job을 queue에 넘기지 않습니다. `continueOnError: true`이면 앞서 수락된 job은 `results`에 남고 abort된 현재/나머지 job은 `failures`에 들어갑니다.
|
|
131
|
+
- Queue adapter는 모든 `enqueue()` 결과와 모든 `enqueueMany()` 결과 항목을 non-empty string identifier로 resolve해야 합니다. Native `enqueueMany()`는 admission된 job count와 정확히 일치하는 own data-property 항목의 dense array를 반환해야 하며, accessor-backed 항목, sparse array, length drift는 거부됩니다. 서비스는 malformed value를 `NotificationQueueResultIntegrityError`로 거부하며 notification envelope에서 queued delivery id를 fabricate하지 않습니다.
|
|
132
|
+
- Queue job은 caller가 제공한 `notification.id`를 authoritative idempotency key로 보존합니다. 값이 없으면 object key를 locale-independent code-unit order로 정렬하는 runtime-neutral serialization과 더 넓은 64-bit digest에서 deterministic fallback `id`를 파생합니다. Queue adapter는 deduplication을 지원하는 backing queue에 이 값을 전달해야 합니다. 생성된 fallback key는 cyclic opaque payload를 포함해 동등한 지원 입력에 대해 deterministic하지만, cross-release durable identity 계약은 아닙니다. 애플리케이션 또는 package upgrade를 넘어 안정적인 identity가 필요하면 `notification.id`를 설정하세요.
|
|
126
133
|
- `dispatchMany(..., { continueOnError: true })`는 direct delivery 또는 순차 queue fallback enqueue에서 첫 실패를 던지는 대신 실패들을 수집합니다.
|
|
127
134
|
- queue enqueue가 실패하면 서비스는 enqueue 에러를 다시 던지기 전에 결정적인 `notification.dispatch.failed` 라이프사이클 이벤트를 발행합니다. queued bulk dispatch는 queue 미구성, channel 해석, provider/adapter failure 경로를 포함해 이미 `requested`를 발행한 모든 notification에 대해 terminal `queued` 또는 `failed` 이벤트도 발행합니다.
|
|
128
135
|
- `enqueueMany(...)`가 없으면 대량 queue delivery는 input order대로 각 job을 개별 enqueue하는 방식으로 fallback합니다. `continueOnError: true`이면 성공한 enqueue는 `results`에 남고 실패한 enqueue는 `failures`로 반환됩니다. 그렇지 않으면 첫 enqueue failure를 다시 던지기 전에 아직 terminal 상태가 없는 나머지 requested fallback job에 `failed` 라이프사이클 이벤트를 발행합니다.
|
|
@@ -153,7 +160,20 @@ NotificationsModule.forRoot({
|
|
|
153
160
|
- `notification.dispatch.delivered`
|
|
154
161
|
- `notification.dispatch.failed`
|
|
155
162
|
|
|
156
|
-
`events.publisher`가 구성되어 있으면 `publishLifecycleEvents: false`를 설정하지 않는 한 lifecycle event publication은 기본으로 켜집니다. 채널 delivery가 `externalId`를 생략하면 시간이나 난수에 의존하지
|
|
163
|
+
`events.publisher`가 구성되어 있으면 `publishLifecycleEvents: false`를 설정하지 않는 한 lifecycle event publication은 기본으로 켜집니다. 서비스는 비어 있지 않은 `dispatchMany(...)` batch 전체를 첫 lifecycle publication 전에 admission 시점에 snapshot하고, 단건 `dispatch(...)` envelope도 channel resolution, queue job, generated identity, provider delivery에 사용하기 위해 admission 시점에 snapshot한 뒤, 별도의 immutable lifecycle event snapshot을 발행합니다. Lifecycle snapshot은 native mutable built-in을 노출하지 않습니다. `ArrayBuffer`는 byte length와 bytes를 저장하고 `ArrayBufferView` 값은 byte offset과 view kind도 보존합니다. `Map`과 `Set`은 순서가 보존된 entry/value data가 되고, `Date`는 epoch milliseconds(유효하지 않은 날짜는 `null`), `URL`은 `href`, `URLSearchParams`는 query string, `RegExp`는 source, flags, `lastIndex`를 저장합니다. Publisher는 lifecycle event를 observation-only로 다뤄야 하며 mutation으로 delivery에 영향을 주어서는 안 됩니다. 채널 delivery가 `externalId`를 생략하면 시간이나 난수에 의존하지 않는 deterministic fallback delivery id가 부여되어 dispatch result가 호출자에게 안정적으로 유지됩니다. 생성된 fallback id는 queue job id와 동일한 locale-independent key ordering 및 64-bit runtime-neutral digest를 사용하며, caller가 제공한 `notification.id`는 계속 authoritative합니다. 생성 값은 현재 envelope shape를 위한 key이지, 문서화된 full-payload hash 계약이 아닙니다. release를 넘어 durable identity가 필요하면 `notification.id`를 설정하세요. 채널 해석 실패는 `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 보장이 조용히 약해지지 않습니다.
|
|
164
|
+
|
|
165
|
+
### Lifecycle 내장 표현
|
|
166
|
+
|
|
167
|
+
| 인터페이스 | 불변 필드 |
|
|
168
|
+
| --- | --- |
|
|
169
|
+
| `NotificationSnapshotArrayBuffer` | `kind: 'ArrayBuffer'`, `byteLength`, `bytes` |
|
|
170
|
+
| `NotificationSnapshotArrayBufferView` | `kind: 'ArrayBufferView'`, `byteOffset`, `byteLength`, `bytes`, `view` |
|
|
171
|
+
| `NotificationSnapshotDate` | `kind: 'Date'`, `epochMilliseconds: number \| null` |
|
|
172
|
+
| `NotificationSnapshotMap<TKey, TValue>` | `kind: 'Map'`, `entries` |
|
|
173
|
+
| `NotificationSnapshotRegExp` | `kind: 'RegExp'`, `source`, `flags`, `lastIndex` |
|
|
174
|
+
| `NotificationSnapshotSet<TValue>` | `kind: 'Set'`, `values` |
|
|
175
|
+
| `NotificationSnapshotUrl` | `kind: 'URL'`, `href` |
|
|
176
|
+
| `NotificationSnapshotUrlSearchParams` | `kind: 'URLSearchParams'`, `query` |
|
|
157
177
|
|
|
158
178
|
### 의도적인 제한 사항
|
|
159
179
|
|
|
@@ -191,7 +211,17 @@ foundation 패키지는 의도적으로 다음을 **포함하지 않습니다**:
|
|
|
191
211
|
- `NotificationChannelContext`
|
|
192
212
|
- `NotificationChannelDelivery`
|
|
193
213
|
- `NotificationPayload`
|
|
214
|
+
- `NotificationSnapshot`
|
|
215
|
+
- `NotificationSnapshotDate`
|
|
216
|
+
- `NotificationSnapshotMap<TKey, TValue>`
|
|
217
|
+
- `NotificationSnapshotRegExp`
|
|
218
|
+
- `NotificationSnapshotSet<TValue>`
|
|
219
|
+
- `NotificationSnapshotUrl`
|
|
220
|
+
- `NotificationSnapshotUrlSearchParams`
|
|
221
|
+
- `NotificationSnapshotArrayBuffer`
|
|
222
|
+
- `NotificationSnapshotArrayBufferView`
|
|
194
223
|
- `NotificationsQueueAdapter`
|
|
224
|
+
- `NotificationsQueueContext`
|
|
195
225
|
- `NotificationsQueueJob`
|
|
196
226
|
- `NotificationsQueueOptions`
|
|
197
227
|
- `NotificationsModuleOptions`
|
|
@@ -204,14 +234,19 @@ foundation 패키지는 의도적으로 다음을 **포함하지 않습니다**:
|
|
|
204
234
|
### 상태 및 에러
|
|
205
235
|
|
|
206
236
|
- `createNotificationsPlatformStatusSnapshot(...)`
|
|
237
|
+
- `NotificationsOperationMode`
|
|
207
238
|
- `NotificationsPlatformStatusSnapshot`
|
|
208
239
|
- `NotificationsStatusAdapterInput`
|
|
240
|
+
- `NotificationsStatusDetails`
|
|
209
241
|
- `NotificationsConfigurationError`
|
|
210
242
|
- `NotificationChannelNotFoundError`
|
|
211
243
|
- `NotificationQueueNotConfiguredError`
|
|
244
|
+
- `NotificationQueueResultIntegrityError`
|
|
245
|
+
|
|
246
|
+
상태 snapshot은 platform diagnostics를 위해 `readiness`, `health`, `ownership`, 그리고 `details` object를 포함합니다.
|
|
247
|
+
`operationMode`, `dependencies`, `bulkQueueThreshold`, `queueConfigured`, `eventPublisherConfigured`, `eventPublicationEnabled`는 `details` 아래에 있으며 top-level snapshot field가 아닙니다. `eventPublisherConfigured`는 publisher wiring을, `eventPublicationEnabled`는 해당 publisher가 lifecycle event를 실제로 발행하는지를 기록합니다. Queue adapter가 구성되면 `details.dependencies`에 `notifications.queue-adapter`가 포함되고, lifecycle event가 event publisher를 통해 활성화되면 `notifications.event-publisher`가 포함됩니다. `publishLifecycleEvents: false`인 configured publisher는 configured 상태로 남지만 active dependency, event-backed operation mode, external ownership을 추가하지 않습니다. 활성화된 선택적 통합은 `ownership.externallyManaged: true`로 표시되지만, foundation 패키지가 concrete queue 또는 event-bus 리소스를 create/close/drain하지 않으므로 `ownsResources: false`를 유지합니다.
|
|
212
248
|
|
|
213
|
-
|
|
214
|
-
Queue adapter가 구성되면 `details.dependencies`에 `notifications.queue-adapter`가 포함되고, lifecycle event가 event publisher를 통해 발행되면 `notifications.event-publisher`가 포함됩니다. 이러한 선택적 통합은 `ownership.externallyManaged: true`로 표시되지만, foundation 패키지가 concrete queue 또는 event-bus 리소스를 create/close/drain하지 않으므로 `ownsResources: false`를 유지합니다.
|
|
249
|
+
<!-- notifications-status-contract: health=eventPublisherConfigured;operationMode=eventPublicationEnabled;dependencies=eventPublicationEnabled;externalOwnership=eventPublicationEnabled;configured-but-disabled-no-channels=degraded -->
|
|
215
250
|
|
|
216
251
|
## 관련 패키지
|
|
217
252
|
|
package/README.md
CHANGED
|
@@ -2,6 +2,8 @@
|
|
|
2
2
|
|
|
3
3
|
<p><strong><kbd>English</kbd></strong> <a href="./README.ko.md"><kbd>한국어</kbd></a></p>
|
|
4
4
|
|
|
5
|
+
Node.js support is `>=24.0.0 <27`. See [Node.js support and migration](../../docs/reference/node-support.md) before upgrading.
|
|
6
|
+
|
|
5
7
|
Channel-agnostic notification orchestration for fluo. It freezes the shared contract for notification channels, provides explicit module registration with familiar dynamic-module ergonomics, and exposes optional queue-backed delivery and lifecycle event publication seams.
|
|
6
8
|
|
|
7
9
|
## Table of Contents
|
|
@@ -104,10 +106,10 @@ NotificationsModule.forRoot({
|
|
|
104
106
|
channels: [emailChannel],
|
|
105
107
|
queue: {
|
|
106
108
|
adapter: {
|
|
107
|
-
async enqueue(job) {
|
|
109
|
+
async enqueue(job, context) {
|
|
108
110
|
return queue.enqueue(job);
|
|
109
111
|
},
|
|
110
|
-
async enqueueMany(jobs) {
|
|
112
|
+
async enqueueMany(jobs, context) {
|
|
111
113
|
return Promise.all(jobs.map((job) => queue.enqueue(job)));
|
|
112
114
|
},
|
|
113
115
|
},
|
|
@@ -118,11 +120,16 @@ NotificationsModule.forRoot({
|
|
|
118
120
|
|
|
119
121
|
Behavioral contract notes:
|
|
120
122
|
|
|
121
|
-
-
|
|
123
|
+
- `bulkThreshold` defaults to `10` when omitted. Explicit values must be finite positive integers; invalid values cause module options resolution to throw `NotificationsConfigurationError` before notification service providers can be constructed.
|
|
124
|
+
- Bulk queue delegation starts when the notification count reaches `bulkThreshold`, and `dispatchMany(..., { queue: true })` explicitly forces queue-backed delivery even when the batch is below that threshold.
|
|
122
125
|
- `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.
|
|
123
126
|
- Use `dispatch(..., { queue: false })` to force direct delivery even when a queue adapter exists.
|
|
124
|
-
- Queue-backed delivery is opt-in for single dispatch and threshold-driven for `dispatchMany(...)`.
|
|
125
|
-
-
|
|
127
|
+
- Queue-backed delivery is opt-in for single dispatch and threshold-driven for `dispatchMany(...)` unless the caller explicitly passes `{ queue: true }`.
|
|
128
|
+
- `NotificationsQueueContext` is the optional second argument of `enqueue(...)` and `enqueueMany(...)`. It carries the exact caller-owned `AbortSignal` from dispatch; existing one-argument adapters remain valid.
|
|
129
|
+
- Before every queue handoff, the service checks the signal and then passes that same live signal to the adapter. A pre-aborted queued dispatch emits `requested` then `failed` when lifecycle events are enabled, but never calls `enqueue(...)` or `enqueueMany(...)`. A signal that aborts after an adapter accepts a job cannot revoke that accepted queue job.
|
|
130
|
+
- Queue adapters own queue-specific cancellation policy and must remove any abort listeners when their enqueue operation settles. If an adapter rejects because the signal aborts, native bulk dispatch rejects and publishes `failed` for every requested job; sequential fallback never hands subsequent jobs to the queue. With `continueOnError: true`, earlier accepted jobs remain in `results` and aborted current/remaining jobs appear in `failures`.
|
|
131
|
+
- Queue adapters must resolve every `enqueue()` result and every `enqueueMany()` result entry with a non-empty string identifier. Native `enqueueMany()` must return a dense array whose own data-property entries exactly match the admitted job count; accessor-backed entries, sparse arrays, and length drift are rejected. The service rejects malformed values with `NotificationQueueResultIntegrityError`; it never fabricates a queued delivery id from the notification envelope.
|
|
132
|
+
- Queue jobs preserve caller-provided `notification.id` as the authoritative idempotency key. Otherwise, they derive a deterministic fallback `id` from a runtime-neutral serialization that orders object keys by locale-independent code-unit order and uses a wider 64-bit digest. Queue adapters should pass this value to backing queues that support deduplication. The generated fallback key is deterministic for equivalent supported inputs, including cyclic opaque payloads, but it is not a durable cross-release identity contract; set `notification.id` when callers need stable identity across application or package upgrades.
|
|
126
133
|
- `dispatchMany(..., { continueOnError: true })` collects failures instead of throwing on the first failed direct delivery or sequential queue fallback enqueue.
|
|
127
134
|
- 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.
|
|
128
135
|
- 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.
|
|
@@ -153,7 +160,20 @@ Published event names:
|
|
|
153
160
|
- `notification.dispatch.delivered`
|
|
154
161
|
- `notification.dispatch.failed`
|
|
155
162
|
|
|
156
|
-
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
|
|
163
|
+
If `events.publisher` is configured, lifecycle event publication defaults to on unless `publishLifecycleEvents: false` is set. The service snapshots every non-empty `dispatchMany(...)` batch at admission before its first lifecycle publication, and snapshots a single `dispatch(...)` envelope at admission for channel resolution, queue jobs, generated identity, and provider delivery. It then publishes a separate immutable lifecycle event snapshot. Lifecycle snapshots never expose native mutable built-ins: `ArrayBuffer` stores byte length and bytes, while `ArrayBufferView` values additionally preserve byte offset and view kind; `Map` and `Set` become ordered entry/value data, `Date` stores epoch milliseconds (`null` for an invalid date), `URL` stores `href`, `URLSearchParams` stores its query string, and `RegExp` stores source, flags, and `lastIndex`. Publishers must treat lifecycle events as observation-only and must not mutate them to influence delivery. Channel deliveries that omit `externalId` receive a deterministic fallback delivery id so dispatch results remain stable for callers without relying on time or random data. Generated fallback ids use the same locale-independent key ordering and 64-bit runtime-neutral digest as queue job ids, while caller-provided `notification.id` remains authoritative. They are keys for the current envelope shape, not a documented full-payload hash contract; set `notification.id` when callers need durable identity across releases. 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.
|
|
164
|
+
|
|
165
|
+
### Lifecycle built-in representations
|
|
166
|
+
|
|
167
|
+
| Interface | Immutable fields |
|
|
168
|
+
| --- | --- |
|
|
169
|
+
| `NotificationSnapshotArrayBuffer` | `kind: 'ArrayBuffer'`, `byteLength`, `bytes` |
|
|
170
|
+
| `NotificationSnapshotArrayBufferView` | `kind: 'ArrayBufferView'`, `byteOffset`, `byteLength`, `bytes`, `view` |
|
|
171
|
+
| `NotificationSnapshotDate` | `kind: 'Date'`, `epochMilliseconds: number \| null` |
|
|
172
|
+
| `NotificationSnapshotMap<TKey, TValue>` | `kind: 'Map'`, `entries` |
|
|
173
|
+
| `NotificationSnapshotRegExp` | `kind: 'RegExp'`, `source`, `flags`, `lastIndex` |
|
|
174
|
+
| `NotificationSnapshotSet<TValue>` | `kind: 'Set'`, `values` |
|
|
175
|
+
| `NotificationSnapshotUrl` | `kind: 'URL'`, `href` |
|
|
176
|
+
| `NotificationSnapshotUrlSearchParams` | `kind: 'URLSearchParams'`, `query` |
|
|
157
177
|
|
|
158
178
|
### Intentional limitations
|
|
159
179
|
|
|
@@ -191,7 +211,17 @@ These limitations are part of the package contract so leaf packages can evolve i
|
|
|
191
211
|
- `NotificationChannelContext`
|
|
192
212
|
- `NotificationChannelDelivery`
|
|
193
213
|
- `NotificationPayload`
|
|
214
|
+
- `NotificationSnapshot`
|
|
215
|
+
- `NotificationSnapshotDate`
|
|
216
|
+
- `NotificationSnapshotMap<TKey, TValue>`
|
|
217
|
+
- `NotificationSnapshotRegExp`
|
|
218
|
+
- `NotificationSnapshotSet<TValue>`
|
|
219
|
+
- `NotificationSnapshotUrl`
|
|
220
|
+
- `NotificationSnapshotUrlSearchParams`
|
|
221
|
+
- `NotificationSnapshotArrayBuffer`
|
|
222
|
+
- `NotificationSnapshotArrayBufferView`
|
|
194
223
|
- `NotificationsQueueAdapter`
|
|
224
|
+
- `NotificationsQueueContext`
|
|
195
225
|
- `NotificationsQueueJob`
|
|
196
226
|
- `NotificationsQueueOptions`
|
|
197
227
|
- `NotificationsModuleOptions`
|
|
@@ -204,14 +234,19 @@ These limitations are part of the package contract so leaf packages can evolve i
|
|
|
204
234
|
### Status and errors
|
|
205
235
|
|
|
206
236
|
- `createNotificationsPlatformStatusSnapshot(...)`
|
|
237
|
+
- `NotificationsOperationMode`
|
|
207
238
|
- `NotificationsPlatformStatusSnapshot`
|
|
208
239
|
- `NotificationsStatusAdapterInput`
|
|
240
|
+
- `NotificationsStatusDetails`
|
|
209
241
|
- `NotificationsConfigurationError`
|
|
210
242
|
- `NotificationChannelNotFoundError`
|
|
211
243
|
- `NotificationQueueNotConfiguredError`
|
|
244
|
+
- `NotificationQueueResultIntegrityError`
|
|
245
|
+
|
|
246
|
+
Status snapshots include `readiness`, `health`, `ownership`, and a `details` object for platform diagnostics.
|
|
247
|
+
`operationMode`, `dependencies`, `bulkQueueThreshold`, `queueConfigured`, `eventPublisherConfigured`, and `eventPublicationEnabled` live under `details`; they are not top-level snapshot fields. `eventPublisherConfigured` records publisher wiring, while `eventPublicationEnabled` records whether that publisher emits lifecycle events. When a queue adapter is configured, `details.dependencies` includes `notifications.queue-adapter`; when lifecycle events are enabled through an event publisher, it includes `notifications.event-publisher`. A configured publisher with `publishLifecycleEvents: false` remains visible as configured but does not add an active dependency, event-backed operation mode, or external ownership. Active optional integrations mark `ownership.externallyManaged: true` while the foundation package still reports `ownsResources: false` because it does not create, close, or drain concrete queue or event-bus resources.
|
|
212
248
|
|
|
213
|
-
|
|
214
|
-
When a queue adapter is configured, `details.dependencies` includes `notifications.queue-adapter`; when lifecycle events are published through an event publisher, it includes `notifications.event-publisher`. Those optional integrations mark `ownership.externallyManaged: true` while the foundation package still reports `ownsResources: false` because it does not create, close, or drain concrete queue or event-bus resources.
|
|
249
|
+
<!-- notifications-status-contract: health=eventPublisherConfigured;operationMode=eventPublicationEnabled;dependencies=eventPublicationEnabled;externalOwnership=eventPublicationEnabled;configured-but-disabled-no-channels=degraded -->
|
|
215
250
|
|
|
216
251
|
## Related Packages
|
|
217
252
|
|
package/dist/errors.d.ts
CHANGED
|
@@ -17,4 +17,11 @@ export declare class NotificationChannelNotFoundError extends Error {
|
|
|
17
17
|
export declare class NotificationQueueNotConfiguredError extends Error {
|
|
18
18
|
constructor();
|
|
19
19
|
}
|
|
20
|
+
/**
|
|
21
|
+
* Thrown when a queue adapter returns an invalid queue-assigned delivery identifier.
|
|
22
|
+
*/
|
|
23
|
+
export declare class NotificationQueueResultIntegrityError extends Error {
|
|
24
|
+
readonly operation: 'enqueue' | 'enqueueMany';
|
|
25
|
+
constructor(operation: 'enqueue' | 'enqueueMany', message: string);
|
|
26
|
+
}
|
|
20
27
|
//# sourceMappingURL=errors.d.ts.map
|
package/dist/errors.d.ts.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"errors.d.ts","sourceRoot":"","sources":["../src/errors.ts"],"names":[],"mappings":"AAAA;;GAEG;AACH,qBAAa,+BAAgC,SAAQ,KAAK;gBAC5C,OAAO,EAAE,MAAM;CAI5B;AAED;;GAEG;AACH,qBAAa,gCAAiC,SAAQ,KAAK;IAC7C,QAAQ,CAAC,OAAO,EAAE,MAAM;gBAAf,OAAO,EAAE,MAAM;CAIrC;AAED;;GAEG;AACH,qBAAa,mCAAoC,SAAQ,KAAK;;CAK7D"}
|
|
1
|
+
{"version":3,"file":"errors.d.ts","sourceRoot":"","sources":["../src/errors.ts"],"names":[],"mappings":"AAAA;;GAEG;AACH,qBAAa,+BAAgC,SAAQ,KAAK;gBAC5C,OAAO,EAAE,MAAM;CAI5B;AAED;;GAEG;AACH,qBAAa,gCAAiC,SAAQ,KAAK;IAC7C,QAAQ,CAAC,OAAO,EAAE,MAAM;gBAAf,OAAO,EAAE,MAAM;CAIrC;AAED;;GAEG;AACH,qBAAa,mCAAoC,SAAQ,KAAK;;CAK7D;AAED;;GAEG;AACH,qBAAa,qCAAsC,SAAQ,KAAK;IAE5D,QAAQ,CAAC,SAAS,EAAE,SAAS,GAAG,aAAa;gBAApC,SAAS,EAAE,SAAS,GAAG,aAAa,EAC7C,OAAO,EAAE,MAAM;CAKlB"}
|
package/dist/errors.js
CHANGED
|
@@ -27,4 +27,15 @@ export class NotificationQueueNotConfiguredError extends Error {
|
|
|
27
27
|
super('Queue-backed notification delivery requires a configured queue adapter.');
|
|
28
28
|
this.name = 'NotificationQueueNotConfiguredError';
|
|
29
29
|
}
|
|
30
|
+
}
|
|
31
|
+
|
|
32
|
+
/**
|
|
33
|
+
* Thrown when a queue adapter returns an invalid queue-assigned delivery identifier.
|
|
34
|
+
*/
|
|
35
|
+
export class NotificationQueueResultIntegrityError extends Error {
|
|
36
|
+
constructor(operation, message) {
|
|
37
|
+
super(`Notifications queue adapter returned an invalid ${operation}() result: ${message}.`);
|
|
38
|
+
this.operation = operation;
|
|
39
|
+
this.name = 'NotificationQueueResultIntegrityError';
|
|
40
|
+
}
|
|
30
41
|
}
|
package/dist/index.d.ts
CHANGED
|
@@ -1,8 +1,8 @@
|
|
|
1
|
-
export { NotificationChannelNotFoundError, NotificationQueueNotConfiguredError, NotificationsConfigurationError, } from './errors.js';
|
|
1
|
+
export { NotificationChannelNotFoundError, NotificationQueueNotConfiguredError, NotificationQueueResultIntegrityError, NotificationsConfigurationError, } from './errors.js';
|
|
2
2
|
export { NotificationsModule } from './module.js';
|
|
3
3
|
export { NotificationsService } from './service.js';
|
|
4
|
+
export type { NotificationsOperationMode, NotificationsPlatformStatusSnapshot, NotificationsStatusAdapterInput, NotificationsStatusDetails, } from './status.js';
|
|
4
5
|
export { createNotificationsPlatformStatusSnapshot } from './status.js';
|
|
5
|
-
export type { NotificationsPlatformStatusSnapshot, NotificationsStatusAdapterInput } from './status.js';
|
|
6
6
|
export { NOTIFICATION_CHANNELS, NOTIFICATIONS } from './tokens.js';
|
|
7
|
-
export type { NotificationChannel, NotificationChannelContext, NotificationChannelDelivery, NotificationDispatchBatchResult, NotificationDispatchFailure, NotificationDispatchManyOptions, NotificationDispatchOptions, NotificationDispatchRequest, NotificationDispatchResult, NotificationDispatchStatus, NotificationLifecycleEvent, NotificationLifecycleEventName, NotificationPayload, Notifications, NotificationsAsyncModuleOptions, NotificationsEventPublisher, NotificationsEventsOptions, NotificationsModuleOptions, NotificationsQueueAdapter, NotificationsQueueJob, NotificationsQueueOptions, } from './types.js';
|
|
7
|
+
export type { NotificationChannel, NotificationChannelContext, NotificationChannelDelivery, NotificationDispatchBatchResult, NotificationDispatchFailure, NotificationDispatchManyOptions, NotificationDispatchOptions, NotificationDispatchRequest, NotificationDispatchResult, NotificationDispatchStatus, NotificationLifecycleEvent, NotificationLifecycleEventName, NotificationPayload, NotificationSnapshot, NotificationSnapshotArrayBuffer, NotificationSnapshotArrayBufferView, NotificationSnapshotDate, NotificationSnapshotMap, NotificationSnapshotRegExp, NotificationSnapshotSet, NotificationSnapshotUrl, NotificationSnapshotUrlSearchParams, Notifications, NotificationsAsyncModuleOptions, NotificationsEventPublisher, NotificationsEventsOptions, NotificationsModuleOptions, NotificationsQueueAdapter, NotificationsQueueContext, NotificationsQueueJob, NotificationsQueueOptions, } from './types.js';
|
|
8
8
|
//# sourceMappingURL=index.d.ts.map
|
package/dist/index.d.ts.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":"AAAA,OAAO,EACL,gCAAgC,EAChC,mCAAmC,EACnC,+BAA+B,GAChC,MAAM,aAAa,CAAC;AACrB,OAAO,EAAE,mBAAmB,EAAE,MAAM,aAAa,CAAC;AAClD,OAAO,EAAE,oBAAoB,EAAE,MAAM,cAAc,CAAC;AACpD,
|
|
1
|
+
{"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":"AAAA,OAAO,EACL,gCAAgC,EAChC,mCAAmC,EACnC,qCAAqC,EACrC,+BAA+B,GAChC,MAAM,aAAa,CAAC;AACrB,OAAO,EAAE,mBAAmB,EAAE,MAAM,aAAa,CAAC;AAClD,OAAO,EAAE,oBAAoB,EAAE,MAAM,cAAc,CAAC;AACpD,YAAY,EACV,0BAA0B,EAC1B,mCAAmC,EACnC,+BAA+B,EAC/B,0BAA0B,GAC3B,MAAM,aAAa,CAAC;AACrB,OAAO,EAAE,yCAAyC,EAAE,MAAM,aAAa,CAAC;AACxE,OAAO,EAAE,qBAAqB,EAAE,aAAa,EAAE,MAAM,aAAa,CAAC;AACnE,YAAY,EACV,mBAAmB,EACnB,0BAA0B,EAC1B,2BAA2B,EAC3B,+BAA+B,EAC/B,2BAA2B,EAC3B,+BAA+B,EAC/B,2BAA2B,EAC3B,2BAA2B,EAC3B,0BAA0B,EAC1B,0BAA0B,EAC1B,0BAA0B,EAC1B,8BAA8B,EAC9B,mBAAmB,EACnB,oBAAoB,EACpB,+BAA+B,EAC/B,mCAAmC,EACnC,wBAAwB,EACxB,uBAAuB,EACvB,0BAA0B,EAC1B,uBAAuB,EACvB,uBAAuB,EACvB,mCAAmC,EACnC,aAAa,EACb,+BAA+B,EAC/B,2BAA2B,EAC3B,0BAA0B,EAC1B,0BAA0B,EAC1B,yBAAyB,EACzB,yBAAyB,EACzB,qBAAqB,EACrB,yBAAyB,GAC1B,MAAM,YAAY,CAAC"}
|
package/dist/index.js
CHANGED
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
export { NotificationChannelNotFoundError, NotificationQueueNotConfiguredError, NotificationsConfigurationError } from './errors.js';
|
|
1
|
+
export { NotificationChannelNotFoundError, NotificationQueueNotConfiguredError, NotificationQueueResultIntegrityError, NotificationsConfigurationError } from './errors.js';
|
|
2
2
|
export { NotificationsModule } from './module.js';
|
|
3
3
|
export { NotificationsService } from './service.js';
|
|
4
4
|
export { createNotificationsPlatformStatusSnapshot } from './status.js';
|
package/dist/module.d.ts
CHANGED
|
@@ -6,7 +6,8 @@ export declare class NotificationsModule {
|
|
|
6
6
|
* Registers notifications providers using static options.
|
|
7
7
|
*
|
|
8
8
|
* @param options Static notifications module options including channels and optional queue/event integrations.
|
|
9
|
-
* @returns A
|
|
9
|
+
* @returns A module definition that exports {@link NotificationsService}, `NOTIFICATIONS`, and `NOTIFICATION_CHANNELS`; exports are global unless `global: false`.
|
|
10
|
+
* @throws {NotificationsConfigurationError} When channel registrations are duplicated or `queue.bulkThreshold` is not a finite positive integer.
|
|
10
11
|
*
|
|
11
12
|
* @example
|
|
12
13
|
* ```ts
|
|
@@ -20,7 +21,8 @@ export declare class NotificationsModule {
|
|
|
20
21
|
* Registers notifications providers from an async DI factory.
|
|
21
22
|
*
|
|
22
23
|
* @param options Async module options that resolve channels and optional integration seams.
|
|
23
|
-
* @returns A
|
|
24
|
+
* @returns A module definition that resolves async options once per application container and exports {@link NotificationsService}, `NOTIFICATIONS`, and `NOTIFICATION_CHANNELS`; exports are global unless `global: false`.
|
|
25
|
+
* @throws {NotificationsConfigurationError} During options resolution when channel registrations are duplicated or `queue.bulkThreshold` is not a finite positive integer.
|
|
24
26
|
*
|
|
25
27
|
* @example
|
|
26
28
|
* ```ts
|
package/dist/module.d.ts.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"module.d.ts","sourceRoot":"","sources":["../src/module.ts"],"names":[],"mappings":"AAEA,OAAO,EAAgB,KAAK,UAAU,EAAE,MAAM,iBAAiB,CAAC;AAKhE,OAAO,KAAK,EAMV,+BAA+B,EAC/B,0BAA0B,EAC3B,MAAM,YAAY,CAAC;
|
|
1
|
+
{"version":3,"file":"module.d.ts","sourceRoot":"","sources":["../src/module.ts"],"names":[],"mappings":"AAEA,OAAO,EAAgB,KAAK,UAAU,EAAE,MAAM,iBAAiB,CAAC;AAKhE,OAAO,KAAK,EAMV,+BAA+B,EAC/B,0BAA0B,EAC3B,MAAM,YAAY,CAAC;AA8GpB,gEAAgE;AAChE,qBAAa,mBAAmB;IAC9B;;;;;;;;;;;;;OAaG;IACH,MAAM,CAAC,OAAO,CAAC,OAAO,GAAE,0BAA+B,GAAG,UAAU;IAIpE;;;;;;;;;;;;;;;;OAgBG;IACH,MAAM,CAAC,YAAY,CAAC,OAAO,EAAE,+BAA+B,GAAG,UAAU;CAG1E"}
|
package/dist/module.js
CHANGED
|
@@ -12,6 +12,9 @@ function normalizeNotificationsModuleOptions(options = {}) {
|
|
|
12
12
|
}
|
|
13
13
|
seenChannelNames.add(channel.channel);
|
|
14
14
|
}
|
|
15
|
+
if (options.queue?.bulkThreshold !== undefined && (!Number.isFinite(options.queue.bulkThreshold) || !Number.isInteger(options.queue.bulkThreshold) || options.queue.bulkThreshold <= 0)) {
|
|
16
|
+
throw new NotificationsConfigurationError('Notifications queue bulkThreshold must be a finite positive integer.');
|
|
17
|
+
}
|
|
15
18
|
return {
|
|
16
19
|
channels: Object.freeze(channels),
|
|
17
20
|
events: options.events ? {
|
|
@@ -20,7 +23,7 @@ function normalizeNotificationsModuleOptions(options = {}) {
|
|
|
20
23
|
} : undefined,
|
|
21
24
|
queue: options.queue ? {
|
|
22
25
|
adapter: options.queue.adapter,
|
|
23
|
-
bulkThreshold:
|
|
26
|
+
bulkThreshold: options.queue.bulkThreshold ?? DEFAULT_BULK_QUEUE_THRESHOLD
|
|
24
27
|
} : undefined
|
|
25
28
|
};
|
|
26
29
|
}
|
|
@@ -55,13 +58,6 @@ function buildNotificationsModule(options) {
|
|
|
55
58
|
function buildNotificationsModuleAsync(options) {
|
|
56
59
|
class NotificationsAsyncModuleDefinition {}
|
|
57
60
|
const factory = options.useFactory;
|
|
58
|
-
let cachedResult;
|
|
59
|
-
const memoizedFactory = (...deps) => {
|
|
60
|
-
if (!cachedResult) {
|
|
61
|
-
cachedResult = Promise.resolve(factory(...deps)).then(resolved => normalizeNotificationsModuleOptions(resolved));
|
|
62
|
-
}
|
|
63
|
-
return cachedResult;
|
|
64
|
-
};
|
|
65
61
|
return defineModule(NotificationsAsyncModuleDefinition, {
|
|
66
62
|
exports: [NotificationsService, NOTIFICATIONS, NOTIFICATION_CHANNELS],
|
|
67
63
|
global: options.global ?? true,
|
|
@@ -69,7 +65,7 @@ function buildNotificationsModuleAsync(options) {
|
|
|
69
65
|
inject: options.inject,
|
|
70
66
|
provide: NOTIFICATIONS_OPTIONS,
|
|
71
67
|
scope: 'singleton',
|
|
72
|
-
useFactory: (...deps) =>
|
|
68
|
+
useFactory: (...deps) => Promise.resolve(factory(...deps)).then(resolved => normalizeNotificationsModuleOptions(resolved))
|
|
73
69
|
})
|
|
74
70
|
});
|
|
75
71
|
}
|
|
@@ -80,7 +76,8 @@ export class NotificationsModule {
|
|
|
80
76
|
* Registers notifications providers using static options.
|
|
81
77
|
*
|
|
82
78
|
* @param options Static notifications module options including channels and optional queue/event integrations.
|
|
83
|
-
* @returns A
|
|
79
|
+
* @returns A module definition that exports {@link NotificationsService}, `NOTIFICATIONS`, and `NOTIFICATION_CHANNELS`; exports are global unless `global: false`.
|
|
80
|
+
* @throws {NotificationsConfigurationError} When channel registrations are duplicated or `queue.bulkThreshold` is not a finite positive integer.
|
|
84
81
|
*
|
|
85
82
|
* @example
|
|
86
83
|
* ```ts
|
|
@@ -97,7 +94,8 @@ export class NotificationsModule {
|
|
|
97
94
|
* Registers notifications providers from an async DI factory.
|
|
98
95
|
*
|
|
99
96
|
* @param options Async module options that resolve channels and optional integration seams.
|
|
100
|
-
* @returns A
|
|
97
|
+
* @returns A module definition that resolves async options once per application container and exports {@link NotificationsService}, `NOTIFICATIONS`, and `NOTIFICATION_CHANNELS`; exports are global unless `global: false`.
|
|
98
|
+
* @throws {NotificationsConfigurationError} During options resolution when channel registrations are duplicated or `queue.bulkThreshold` is not a finite positive integer.
|
|
101
99
|
*
|
|
102
100
|
* @example
|
|
103
101
|
* ```ts
|
package/dist/service.d.ts
CHANGED
|
@@ -20,6 +20,7 @@ export declare class NotificationsService implements Notifications {
|
|
|
20
20
|
* @returns A normalized dispatch result describing direct vs queued delivery.
|
|
21
21
|
* @throws {NotificationChannelNotFoundError} When no registered channel matches `notification.channel`.
|
|
22
22
|
* @throws {NotificationQueueNotConfiguredError} When queue delivery is requested without a queue adapter.
|
|
23
|
+
* @throws {NotificationQueueResultIntegrityError} When a queue adapter returns an invalid delivery identifier.
|
|
23
24
|
*
|
|
24
25
|
* @example
|
|
25
26
|
* ```ts
|
|
@@ -32,6 +33,7 @@ export declare class NotificationsService implements Notifications {
|
|
|
32
33
|
* ```
|
|
33
34
|
*/
|
|
34
35
|
dispatch<TRequest extends NotificationDispatchRequest>(notification: TRequest, options?: NotificationDispatchOptions): Promise<NotificationDispatchResult>;
|
|
36
|
+
private dispatchAdmitted;
|
|
35
37
|
/**
|
|
36
38
|
* Dispatches multiple notifications in input order with optional bulk queue delegation.
|
|
37
39
|
*
|
|
@@ -40,6 +42,7 @@ export declare class NotificationsService implements Notifications {
|
|
|
40
42
|
* @param options Optional queue preference and tolerant error-handling controls.
|
|
41
43
|
* @returns A batch summary containing successes and captured failures.
|
|
42
44
|
* @throws {NotificationQueueNotConfiguredError} When queue-backed bulk delivery is requested without a queue adapter.
|
|
45
|
+
* @throws {NotificationQueueResultIntegrityError} When a queue adapter returns invalid delivery identifiers.
|
|
43
46
|
*/
|
|
44
47
|
dispatchMany<TRequest extends NotificationDispatchRequest>(notifications: readonly TRequest[], options?: NotificationDispatchManyOptions): Promise<NotificationDispatchBatchResult<TRequest>>;
|
|
45
48
|
/**
|
package/dist/service.d.ts.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"service.d.ts","sourceRoot":"","sources":["../src/service.ts"],"names":[],"mappings":"
|
|
1
|
+
{"version":3,"file":"service.d.ts","sourceRoot":"","sources":["../src/service.ts"],"names":[],"mappings":"AAaA,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;;;;;;;;;;;;;;;;;;;;OAoBG;IACG,QAAQ,CAAC,QAAQ,SAAS,2BAA2B,EACzD,YAAY,EAAE,QAAQ,EACtB,OAAO,GAAE,2BAAgC,GACxC,OAAO,CAAC,0BAA0B,CAAC;YAMxB,gBAAgB;IA6E9B;;;;;;;;;OASG;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;IAiHrD;;;;OAIG;IACH,4BAA4B;IAU5B,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,+BAA+B;YAgB/B,4BAA4B;YAmB5B,6BAA6B;YA0B7B,+BAA+B;YAmB/B,0CAA0C;CA6DzD"}
|