@12-apps/notifications 4.6.0 → 4.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.
- package/dist/{chunk-YE24MDS6.js → chunk-6HLHQDKS.js} +2 -2
- package/dist/{chunk-AHNRSA6U.js → chunk-HQU4R4SG.js} +2 -2
- package/dist/{chunk-WU6QJLSZ.js → chunk-QRAXX3GR.js} +2 -2
- package/dist/{chunk-ORXJH3VM.js → chunk-TIGTBSAQ.js} +25 -17
- package/dist/chunk-TIGTBSAQ.js.map +1 -0
- package/dist/{chunk-4TTYQVPK.js → chunk-Y34FX24X.js} +4 -3
- package/dist/{chunk-4TTYQVPK.js.map → chunk-Y34FX24X.js.map} +1 -1
- package/dist/{create-api-notifications-CgBdjfyF.d.ts → create-api-notifications-B3u6Kx3x.d.ts} +28 -5
- package/dist/{create-web-notifications-Du3hTs7P.d.ts → create-web-notifications-BpNR8qH3.d.ts} +1 -1
- package/dist/hono/index.d.ts +2 -2
- package/dist/hono/index.js +3 -3
- package/dist/index.d.ts +1 -1
- package/dist/index.js +2 -2
- package/dist/{jobs-DhDjrAX5.d.ts → jobs-BDZ7aGHV.d.ts} +1 -1
- package/dist/manifest/server.d.ts +3 -3
- package/dist/manifest/server.js +4 -4
- package/dist/manifest/web.d.ts +2 -2
- package/dist/manifest/web.js +2 -2
- package/dist/react/index.d.ts +3 -3
- package/dist/react/index.js +2 -2
- package/dist/server/index.d.ts +4 -4
- package/dist/server/index.js +4 -4
- package/dist/{wire-5IRin4zH.d.ts → wire-6dzyfDE7.d.ts} +31 -5
- package/package.json +2 -2
- package/src/messages.ts +37 -4
- package/src/server/context.ts +10 -0
- package/src/server/create-api-notifications.ts +30 -6
- package/src/server/routes.ts +32 -16
- package/dist/chunk-ORXJH3VM.js.map +0 -1
- /package/dist/{chunk-YE24MDS6.js.map → chunk-6HLHQDKS.js.map} +0 -0
- /package/dist/{chunk-AHNRSA6U.js.map → chunk-HQU4R4SG.js.map} +0 -0
- /package/dist/{chunk-WU6QJLSZ.js.map → chunk-QRAXX3GR.js.map} +0 -0
package/dist/{create-api-notifications-CgBdjfyF.d.ts → create-api-notifications-B3u6Kx3x.d.ts}
RENAMED
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
import { N as NotificationGeneratorRegistry } from './generators-B9xt3sRh.js';
|
|
2
|
-
import { b as NotificationRow, C as ChannelMatrix, a as ChannelRow, L as ListNotificationsResult, c as NotificationWireMessages } from './wire-
|
|
2
|
+
import { b as NotificationRow, C as ChannelMatrix, a as ChannelRow, L as ListNotificationsResult, h as NotificationsCopySource, c as NotificationWireMessages } from './wire-6dzyfDE7.js';
|
|
3
3
|
import { b as NotificationChannel, D as DeliveryStatus, f as NotificationLogger, i as NotificationTransport, c as NotificationContent, a as NotificationCategory, d as NotificationEvent, e as NotificationGenerator } from './types-CXLAG3UU.js';
|
|
4
4
|
import { e as WebPushSubscriptionSource, D as DriverDeclarationBase, a as WebPushDriverDeclaration, c as WebPushSender } from './web-push-Cs14Wp9u.js';
|
|
5
5
|
|
|
@@ -753,6 +753,16 @@ interface NotificationsRequest {
|
|
|
753
753
|
body?: unknown;
|
|
754
754
|
/** Headers the surface reads (`user-agent`, for the device hint). */
|
|
755
755
|
headers?: Record<string, string | undefined>;
|
|
756
|
+
/**
|
|
757
|
+
* The language to answer this caller in, as a language tag (`pt-BR`,
|
|
758
|
+
* `en-US`) — the same field `@12-apps/wiring`'s `WireRequest` carries.
|
|
759
|
+
*
|
|
760
|
+
* Populated by the host's adapter, which is the only layer that can
|
|
761
|
+
* negotiate one. Absent is meaningful and not an error: a host with one
|
|
762
|
+
* audience never sets it, and this surface must then answer with the words
|
|
763
|
+
* it was configured with rather than invent a language.
|
|
764
|
+
*/
|
|
765
|
+
locale?: string;
|
|
756
766
|
}
|
|
757
767
|
/** What a handler answers with; the adapter maps it onto its response type. */
|
|
758
768
|
interface NotificationsResponse {
|
|
@@ -874,8 +884,15 @@ interface NotificationsServerConfig {
|
|
|
874
884
|
onInboxChanged?: (userId: string) => void;
|
|
875
885
|
/** The host's authorization engine, for `notifyByPermission`. */
|
|
876
886
|
audience?: NotificationAudienceDirectory;
|
|
877
|
-
/**
|
|
878
|
-
|
|
887
|
+
/**
|
|
888
|
+
* Every user-facing sentence this surface can produce — REQUIRED host config.
|
|
889
|
+
*
|
|
890
|
+
* A pack, or a RESOLVER for a host whose callers do not share a language.
|
|
891
|
+
* Passed to the routes UNRESOLVED: this factory runs once per process (and
|
|
892
|
+
* at least one host memoises its call), so resolving here would answer every
|
|
893
|
+
* later request in the language the process started with.
|
|
894
|
+
*/
|
|
895
|
+
messages: NotificationsCopySource<NotificationWireMessages>;
|
|
879
896
|
/** The host's logger. Defaults to the console. */
|
|
880
897
|
logger?: NotificationLogger;
|
|
881
898
|
}
|
|
@@ -901,8 +918,14 @@ interface ApiNotifications {
|
|
|
901
918
|
registerGenerator: NotificationGeneratorRegistry['register'];
|
|
902
919
|
/** The declared transports, for diagnostics and availability probes. */
|
|
903
920
|
transports: TransportRegistry;
|
|
904
|
-
/**
|
|
905
|
-
|
|
921
|
+
/**
|
|
922
|
+
* The copy source in force, so a host's own screens can reuse a sentence.
|
|
923
|
+
*
|
|
924
|
+
* The SOURCE rather than a resolved pack, for the same reason the routes get
|
|
925
|
+
* one: a host screen serving two readers must be able to ask per reader.
|
|
926
|
+
* Read it with `messagesOf({ messages }, locale)`.
|
|
927
|
+
*/
|
|
928
|
+
messages: NotificationsCopySource<NotificationWireMessages>;
|
|
906
929
|
}
|
|
907
930
|
declare function createApiNotifications(config: NotificationsServerConfig): ApiNotifications;
|
|
908
931
|
|
package/dist/{create-web-notifications-Du3hTs7P.d.ts → create-web-notifications-BpNR8qH3.d.ts}
RENAMED
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
import { JSX, ComponentType } from 'react';
|
|
2
|
-
import { L as ListNotificationsResult, a as ChannelRow, I as InboxNotification, N as NotificationMessages } from './wire-
|
|
2
|
+
import { L as ListNotificationsResult, a as ChannelRow, I as InboxNotification, N as NotificationMessages } from './wire-6dzyfDE7.js';
|
|
3
3
|
import { b as NotificationChannel } from './types-CXLAG3UU.js';
|
|
4
4
|
|
|
5
5
|
/**
|
package/dist/hono/index.d.ts
CHANGED
|
@@ -1,8 +1,8 @@
|
|
|
1
1
|
import { Hono, Context } from 'hono';
|
|
2
|
-
import { A as ApiNotifications, N as NotificationsServerConfig, a as NotificationsActor } from '../create-api-notifications-
|
|
2
|
+
import { A as ApiNotifications, N as NotificationsServerConfig, a as NotificationsActor } from '../create-api-notifications-B3u6Kx3x.js';
|
|
3
3
|
import '../generators-B9xt3sRh.js';
|
|
4
4
|
import '../types-CXLAG3UU.js';
|
|
5
|
-
import '../wire-
|
|
5
|
+
import '../wire-6dzyfDE7.js';
|
|
6
6
|
import '../web-push-Cs14Wp9u.js';
|
|
7
7
|
|
|
8
8
|
/**
|
package/dist/hono/index.js
CHANGED
|
@@ -1,10 +1,10 @@
|
|
|
1
1
|
import {
|
|
2
2
|
createApiNotifications
|
|
3
|
-
} from "../chunk-
|
|
4
|
-
import "../chunk-
|
|
3
|
+
} from "../chunk-TIGTBSAQ.js";
|
|
4
|
+
import "../chunk-HQU4R4SG.js";
|
|
5
5
|
import {
|
|
6
6
|
messagesOf
|
|
7
|
-
} from "../chunk-
|
|
7
|
+
} from "../chunk-Y34FX24X.js";
|
|
8
8
|
import {
|
|
9
9
|
__name
|
|
10
10
|
} from "../chunk-7QVYU63E.js";
|
package/dist/index.d.ts
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
export { D as DeliveryStatus, N as NOTIFICATION_CHANNELS, a as NotificationCategory, b as NotificationChannel, c as NotificationContent, d as NotificationEvent, e as NotificationGenerator, f as NotificationLogger, g as NotificationRecipient, h as NotificationTaxonomy, i as NotificationTransport, T as TransportRecipient, t as taxonomyOf } from './types-CXLAG3UU.js';
|
|
2
2
|
export { N as NotificationGeneratorRegistry, c as createGeneratorRegistry } from './generators-B9xt3sRh.js';
|
|
3
|
-
export { C as ChannelMatrix, a as ChannelRow, D as DEFAULT_CHANNEL_ROW, I as InboxNotification, L as ListNotificationsResult, N as NotificationMessages, b as NotificationRow, c as NotificationWireMessages, d as defaultChannelMatrix, e as enabledChannelsOf, i as inboxWire, m as mergeChoices, f as mergeStoredRow, g as messagesOf } from './wire-
|
|
3
|
+
export { C as ChannelMatrix, a as ChannelRow, D as DEFAULT_CHANNEL_ROW, I as InboxNotification, L as ListNotificationsResult, N as NotificationMessages, b as NotificationRow, c as NotificationWireMessages, d as defaultChannelMatrix, e as enabledChannelsOf, i as inboxWire, m as mergeChoices, f as mergeStoredRow, g as messagesOf } from './wire-6dzyfDE7.js';
|
|
4
4
|
|
|
5
5
|
/** Thrown by `notify` when no generator is registered for the event type. */
|
|
6
6
|
declare class UnknownNotificationTypeError extends Error {
|
package/dist/index.js
CHANGED
|
@@ -9,12 +9,12 @@ import {
|
|
|
9
9
|
mergeChoices,
|
|
10
10
|
mergeStoredRow,
|
|
11
11
|
normalizePhoneE164
|
|
12
|
-
} from "./chunk-
|
|
12
|
+
} from "./chunk-HQU4R4SG.js";
|
|
13
13
|
import {
|
|
14
14
|
NOTIFICATION_CHANNELS,
|
|
15
15
|
messagesOf,
|
|
16
16
|
taxonomyOf
|
|
17
|
-
} from "./chunk-
|
|
17
|
+
} from "./chunk-Y34FX24X.js";
|
|
18
18
|
import "./chunk-7QVYU63E.js";
|
|
19
19
|
export {
|
|
20
20
|
DEFAULT_CHANNEL_ROW,
|
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
import { WireJobBlueprint } from '@12-apps/wiring';
|
|
2
|
-
import { A as ApiNotifications } from './create-api-notifications-
|
|
2
|
+
import { A as ApiNotifications } from './create-api-notifications-B3u6Kx3x.js';
|
|
3
3
|
|
|
4
4
|
/**
|
|
5
5
|
* The two background jobs getting a message out actually needs.
|
|
@@ -1,10 +1,10 @@
|
|
|
1
1
|
import * as _12_apps_wiring from '@12-apps/wiring';
|
|
2
2
|
import { WireRequest } from '@12-apps/wiring';
|
|
3
|
-
import { N as NotificationsServerConfig, A as ApiNotifications, b as NotificationsRoute } from '../create-api-notifications-
|
|
4
|
-
import { N as NotificationsJobDeps } from '../jobs-
|
|
3
|
+
import { N as NotificationsServerConfig, A as ApiNotifications, b as NotificationsRoute } from '../create-api-notifications-B3u6Kx3x.js';
|
|
4
|
+
import { N as NotificationsJobDeps } from '../jobs-BDZ7aGHV.js';
|
|
5
5
|
import '../generators-B9xt3sRh.js';
|
|
6
6
|
import '../types-CXLAG3UU.js';
|
|
7
|
-
import '../wire-
|
|
7
|
+
import '../wire-6dzyfDE7.js';
|
|
8
8
|
import '../web-push-Cs14Wp9u.js';
|
|
9
9
|
|
|
10
10
|
/** One `NotificationsRoute` as the wiring contract reads it. */
|
package/dist/manifest/server.js
CHANGED
|
@@ -1,11 +1,11 @@
|
|
|
1
1
|
import {
|
|
2
2
|
NOTIFICATIONS_JOBS
|
|
3
|
-
} from "../chunk-
|
|
3
|
+
} from "../chunk-QRAXX3GR.js";
|
|
4
4
|
import {
|
|
5
5
|
createApiNotifications
|
|
6
|
-
} from "../chunk-
|
|
7
|
-
import "../chunk-
|
|
8
|
-
import "../chunk-
|
|
6
|
+
} from "../chunk-TIGTBSAQ.js";
|
|
7
|
+
import "../chunk-HQU4R4SG.js";
|
|
8
|
+
import "../chunk-Y34FX24X.js";
|
|
9
9
|
import {
|
|
10
10
|
__name
|
|
11
11
|
} from "../chunk-7QVYU63E.js";
|
package/dist/manifest/web.d.ts
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
|
-
import { c as createWebNotifications } from '../create-web-notifications-
|
|
1
|
+
import { c as createWebNotifications } from '../create-web-notifications-BpNR8qH3.js';
|
|
2
2
|
import 'react';
|
|
3
|
-
import '../wire-
|
|
3
|
+
import '../wire-6dzyfDE7.js';
|
|
4
4
|
import '../types-CXLAG3UU.js';
|
|
5
5
|
|
|
6
6
|
/**
|
package/dist/manifest/web.js
CHANGED
package/dist/react/index.d.ts
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
|
-
import { N as NotificationsApiClient } from '../create-web-notifications-
|
|
2
|
-
export { B as BADGE_POLL_MS, a as BADGE_RECONCILE_MS, b as BellButtonProps, I as InboxListStatus, d as InboxState, e as InboxStore, f as NotificationsHttpError, g as NotificationsPanelProps, h as NotificationsResult, i as NotificationsSignalHook, j as NotificationsSubscribe, k as NotificationsTransport, l as NotificationsWebConfig, P as PAGE_SIZE, m as PreferencesPayload, n as PreferencesScreenProps, o as PushRegistrationPayload, W as WebNotifications, p as WebPushPlatformHint, q as WebPushSetupConfig, r as createInboxStore, s as createNotificationsApiClient, c as createWebNotifications, t as httpNotificationsTransport, u as useInboxList, v as useInboxState, w as useUnreadCount } from '../create-web-notifications-
|
|
1
|
+
import { N as NotificationsApiClient } from '../create-web-notifications-BpNR8qH3.js';
|
|
2
|
+
export { B as BADGE_POLL_MS, a as BADGE_RECONCILE_MS, b as BellButtonProps, I as InboxListStatus, d as InboxState, e as InboxStore, f as NotificationsHttpError, g as NotificationsPanelProps, h as NotificationsResult, i as NotificationsSignalHook, j as NotificationsSubscribe, k as NotificationsTransport, l as NotificationsWebConfig, P as PAGE_SIZE, m as PreferencesPayload, n as PreferencesScreenProps, o as PushRegistrationPayload, W as WebNotifications, p as WebPushPlatformHint, q as WebPushSetupConfig, r as createInboxStore, s as createNotificationsApiClient, c as createWebNotifications, t as httpNotificationsTransport, u as useInboxList, v as useInboxState, w as useUnreadCount } from '../create-web-notifications-BpNR8qH3.js';
|
|
3
3
|
import { JSX } from 'react';
|
|
4
|
-
import { N as NotificationMessages } from '../wire-
|
|
4
|
+
import { N as NotificationMessages } from '../wire-6dzyfDE7.js';
|
|
5
5
|
import '../types-CXLAG3UU.js';
|
|
6
6
|
|
|
7
7
|
/** Inline SVG bell (no icon-library dependency in this package). */
|
package/dist/react/index.js
CHANGED
|
@@ -16,8 +16,8 @@ import {
|
|
|
16
16
|
useInboxList,
|
|
17
17
|
useInboxState,
|
|
18
18
|
useUnreadCount
|
|
19
|
-
} from "../chunk-
|
|
20
|
-
import "../chunk-
|
|
19
|
+
} from "../chunk-6HLHQDKS.js";
|
|
20
|
+
import "../chunk-Y34FX24X.js";
|
|
21
21
|
import "../chunk-7QVYU63E.js";
|
|
22
22
|
export {
|
|
23
23
|
BADGE_POLL_MS,
|
package/dist/server/index.d.ts
CHANGED
|
@@ -1,11 +1,11 @@
|
|
|
1
|
-
import { A as ApiNotifications } from '../create-api-notifications-
|
|
2
|
-
export { C as CommittedNotification, E as EMAIL_DRIVERS, c as EmailDriver, d as EmailDriverDeclaration, e as EmailMessage, f as ExtraDrivers, L as ListNotificationsInput, g as NotificationAudienceDirectory, h as NotificationChannelPolicy, i as NotificationCommittedListener, j as NotificationContactDirectory, k as NotificationCreateData, l as NotificationDelegate, m as NotificationDeliveryDelegate, n as NotificationDeliveryRow, o as NotificationDeliveryWhere, p as NotificationDispatchScheduler, q as NotificationInboxStore, r as NotificationPageAfter, s as NotificationPreferenceDelegate, t as NotificationPreferenceRow, u as NotificationPreferenceStore, v as NotificationRouter, w as NotificationWhere, a as NotificationsActor, x as NotificationsApiError, y as NotificationsDb, z as NotificationsDbClient, B as NotificationsDbProvider, D as NotificationsRequest, F as NotificationsResponse, b as NotificationsRoute, N as NotificationsServerConfig, G as NotifyByPermission, H as NotifyOptions, I as NotifyResult, P as PermissionNotificationResult, J as PermissionNotificationSkip, K as PushSubscriptionDelegate, M as PushSubscriptionInput, O as PushSubscriptionRow, Q as PushSubscriptionStore, S as SMS_DRIVERS, R as SmsDriver, T as SmsDriverDeclaration, U as SmsMessage, V as TransportDeclaration, W as TransportRegistry, X as WHATSAPP_DRIVERS, Y as WhatsAppDriver, Z as WhatsAppDriverDeclaration, _ as WhatsAppMessage, $ as createApiNotifications, a0 as createTransportRegistry, a1 as emailTransport, a2 as foldApiError, a3 as formatEmail, a4 as formatSms, a5 as formatWhatsApp, a6 as ok, a7 as smsTransport, a8 as whatsAppTransport } from '../create-api-notifications-
|
|
1
|
+
import { A as ApiNotifications } from '../create-api-notifications-B3u6Kx3x.js';
|
|
2
|
+
export { C as CommittedNotification, E as EMAIL_DRIVERS, c as EmailDriver, d as EmailDriverDeclaration, e as EmailMessage, f as ExtraDrivers, L as ListNotificationsInput, g as NotificationAudienceDirectory, h as NotificationChannelPolicy, i as NotificationCommittedListener, j as NotificationContactDirectory, k as NotificationCreateData, l as NotificationDelegate, m as NotificationDeliveryDelegate, n as NotificationDeliveryRow, o as NotificationDeliveryWhere, p as NotificationDispatchScheduler, q as NotificationInboxStore, r as NotificationPageAfter, s as NotificationPreferenceDelegate, t as NotificationPreferenceRow, u as NotificationPreferenceStore, v as NotificationRouter, w as NotificationWhere, a as NotificationsActor, x as NotificationsApiError, y as NotificationsDb, z as NotificationsDbClient, B as NotificationsDbProvider, D as NotificationsRequest, F as NotificationsResponse, b as NotificationsRoute, N as NotificationsServerConfig, G as NotifyByPermission, H as NotifyOptions, I as NotifyResult, P as PermissionNotificationResult, J as PermissionNotificationSkip, K as PushSubscriptionDelegate, M as PushSubscriptionInput, O as PushSubscriptionRow, Q as PushSubscriptionStore, S as SMS_DRIVERS, R as SmsDriver, T as SmsDriverDeclaration, U as SmsMessage, V as TransportDeclaration, W as TransportRegistry, X as WHATSAPP_DRIVERS, Y as WhatsAppDriver, Z as WhatsAppDriverDeclaration, _ as WhatsAppMessage, $ as createApiNotifications, a0 as createTransportRegistry, a1 as emailTransport, a2 as foldApiError, a3 as formatEmail, a4 as formatSms, a5 as formatWhatsApp, a6 as ok, a7 as smsTransport, a8 as whatsAppTransport } from '../create-api-notifications-B3u6Kx3x.js';
|
|
3
3
|
export { D as DriverDeclarationBase, F as FetchImpl, N as NotificationProviderError, W as WEB_PUSH_DRIVERS, a as WebPushDriverDeclaration, b as WebPushMessage, c as WebPushSender, d as WebPushSubscription, e as WebPushSubscriptionSource, f as absoluteLink, g as formatWebPush, w as webPushTransport } from '../web-push-Cs14Wp9u.js';
|
|
4
4
|
import { NotifyPort } from '@12-apps/wiring/ports';
|
|
5
|
-
export { a as NOTIFICATIONS_DRAIN_CRON, b as NOTIFICATIONS_DRAIN_LEASE_MS, c as NOTIFICATIONS_JOBS, d as NOTIFICATIONS_SWEEP_QUEUE, N as NotificationsJobDeps } from '../jobs-
|
|
5
|
+
export { a as NOTIFICATIONS_DRAIN_CRON, b as NOTIFICATIONS_DRAIN_LEASE_MS, c as NOTIFICATIONS_JOBS, d as NOTIFICATIONS_SWEEP_QUEUE, N as NotificationsJobDeps } from '../jobs-BDZ7aGHV.js';
|
|
6
6
|
import '../generators-B9xt3sRh.js';
|
|
7
7
|
import '../types-CXLAG3UU.js';
|
|
8
|
-
import '../wire-
|
|
8
|
+
import '../wire-6dzyfDE7.js';
|
|
9
9
|
import '@12-apps/wiring';
|
|
10
10
|
|
|
11
11
|
/**
|
package/dist/server/index.js
CHANGED
|
@@ -4,7 +4,7 @@ import {
|
|
|
4
4
|
NOTIFICATIONS_JOBS,
|
|
5
5
|
NOTIFICATIONS_SWEEP_QUEUE,
|
|
6
6
|
wireNotifyPort
|
|
7
|
-
} from "../chunk-
|
|
7
|
+
} from "../chunk-QRAXX3GR.js";
|
|
8
8
|
import {
|
|
9
9
|
EMAIL_DRIVERS,
|
|
10
10
|
NotificationProviderError,
|
|
@@ -25,9 +25,9 @@ import {
|
|
|
25
25
|
smsTransport,
|
|
26
26
|
webPushTransport,
|
|
27
27
|
whatsAppTransport
|
|
28
|
-
} from "../chunk-
|
|
29
|
-
import "../chunk-
|
|
30
|
-
import "../chunk-
|
|
28
|
+
} from "../chunk-TIGTBSAQ.js";
|
|
29
|
+
import "../chunk-HQU4R4SG.js";
|
|
30
|
+
import "../chunk-Y34FX24X.js";
|
|
31
31
|
import "../chunk-7QVYU63E.js";
|
|
32
32
|
export {
|
|
33
33
|
EMAIL_DRIVERS,
|
|
@@ -83,7 +83,20 @@ interface NotificationMessages extends NotificationWireMessages {
|
|
|
83
83
|
devicePushEnabling: string;
|
|
84
84
|
}
|
|
85
85
|
/**
|
|
86
|
-
*
|
|
86
|
+
* What a copy field takes once its words can follow a reader.
|
|
87
|
+
*
|
|
88
|
+
* Declared here rather than imported from `@12-apps/i18n`: this package must
|
|
89
|
+
* stay liftable into a repo that has never heard of it, so the two agree
|
|
90
|
+
* STRUCTURALLY and nothing forces the dependency. The context is deliberately
|
|
91
|
+
* loose — a raw tag off the wire, unnarrowed — because matching it is the host
|
|
92
|
+
* resolver's job, not this package's.
|
|
93
|
+
*/
|
|
94
|
+
type NotificationsCopyResolver<T> = (context: {
|
|
95
|
+
readonly locale?: string | null;
|
|
96
|
+
}) => T;
|
|
97
|
+
type NotificationsCopySource<T> = T | NotificationsCopyResolver<T>;
|
|
98
|
+
/**
|
|
99
|
+
* The messages in force, for ONE reader.
|
|
87
100
|
*
|
|
88
101
|
* A pass-through rather than a merge: there is nothing left to merge WITH, and
|
|
89
102
|
* that is the point of the change. The old version spread the host's table over
|
|
@@ -94,11 +107,24 @@ interface NotificationMessages extends NotificationWireMessages {
|
|
|
94
107
|
* the same screen.
|
|
95
108
|
*
|
|
96
109
|
* Kept as a function because all three mounts read it off a config object, and
|
|
97
|
-
* because a later rule (a blank-string refusal, say) belongs in one place
|
|
110
|
+
* because a later rule (a blank-string refusal, say) belongs in one place —
|
|
111
|
+
* which is exactly what made it the right place to put the RESOLUTION when the
|
|
112
|
+
* field learned to take a resolver.
|
|
113
|
+
*
|
|
114
|
+
* **Call it where the sentence is used.** `createApiNotifications` runs once
|
|
115
|
+
* per process, and at least one host memoises its call behind an `if
|
|
116
|
+
* (assembled) return assembled;`, so a value read there answers every later
|
|
117
|
+
* request in the language the process started with — and a single-locale host
|
|
118
|
+
* cannot tell the difference. The route handlers call it per request; the
|
|
119
|
+
* parsers below them keep taking a plain pack, so one request resolves exactly
|
|
120
|
+
* once and no helper can disagree with another about the language.
|
|
121
|
+
*
|
|
122
|
+
* The generic survives the widening: a host whose pack carries extra keys of
|
|
123
|
+
* its own still gets them back, resolver or not.
|
|
98
124
|
*/
|
|
99
125
|
declare function messagesOf<T extends NotificationWireMessages>(config: {
|
|
100
|
-
messages: T
|
|
101
|
-
}): T;
|
|
126
|
+
messages: NotificationsCopySource<T>;
|
|
127
|
+
}, locale?: string): T;
|
|
102
128
|
|
|
103
129
|
/**
|
|
104
130
|
* The preference POLICY, with no storage in it (12-15): which channels a
|
|
@@ -194,4 +220,4 @@ interface NotificationRow {
|
|
|
194
220
|
/** Row → wire. Dates become ISO strings; a null `data` becomes `{}`. */
|
|
195
221
|
declare function inboxWire(row: NotificationRow): InboxNotification;
|
|
196
222
|
|
|
197
|
-
export { type ChannelMatrix as C, DEFAULT_CHANNEL_ROW as D, type InboxNotification as I, type ListNotificationsResult as L, type NotificationMessages as N, type ChannelRow as a, type NotificationRow as b, type NotificationWireMessages as c, defaultChannelMatrix as d, enabledChannelsOf as e, mergeStoredRow as f, messagesOf as g, inboxWire as i, mergeChoices as m };
|
|
223
|
+
export { type ChannelMatrix as C, DEFAULT_CHANNEL_ROW as D, type InboxNotification as I, type ListNotificationsResult as L, type NotificationMessages as N, type ChannelRow as a, type NotificationRow as b, type NotificationWireMessages as c, defaultChannelMatrix as d, enabledChannelsOf as e, mergeStoredRow as f, messagesOf as g, type NotificationsCopySource as h, inboxWire as i, mergeChoices as m };
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@12-apps/notifications",
|
|
3
|
-
"version": "4.
|
|
3
|
+
"version": "4.7.0",
|
|
4
4
|
"type": "module",
|
|
5
5
|
"sideEffects": false,
|
|
6
6
|
"description": "Plug-and-play notification system (12-15): an always-on in-app inbox, per-user × per-category channel preferences, and email / SMS / WhatsApp / web-push transports behind vendor DRIVERS so a second provider is a config entry. Framework-free core (.), host-mounted backend surface (./server: inbox / preferences / push-subscription endpoints, the channel router with delivery records + retry sweep, the permission fan-out, duck-typed Prisma seam), Hono adapter (./hono), React surface (./react: bell + badge, inbox drawer, preferences screen), VAPID sender (./web-push) and the package-owned Prisma partial + migrations. Standardized adoption contract in ADOPTING.md.",
|
|
@@ -51,7 +51,7 @@
|
|
|
51
51
|
"prisma:sync:check": "node scripts/sync-notifications-schema.mjs --check"
|
|
52
52
|
},
|
|
53
53
|
"dependencies": {
|
|
54
|
-
"@12-apps/ui": "^6.
|
|
54
|
+
"@12-apps/ui": "^6.11.0"
|
|
55
55
|
},
|
|
56
56
|
"peerDependencies": {
|
|
57
57
|
"@12-apps/wiring": ">=1.3.0",
|
package/src/messages.ts
CHANGED
|
@@ -85,7 +85,21 @@ export interface NotificationMessages extends NotificationWireMessages {
|
|
|
85
85
|
}
|
|
86
86
|
|
|
87
87
|
/**
|
|
88
|
-
*
|
|
88
|
+
* What a copy field takes once its words can follow a reader.
|
|
89
|
+
*
|
|
90
|
+
* Declared here rather than imported from `@12-apps/i18n`: this package must
|
|
91
|
+
* stay liftable into a repo that has never heard of it, so the two agree
|
|
92
|
+
* STRUCTURALLY and nothing forces the dependency. The context is deliberately
|
|
93
|
+
* loose — a raw tag off the wire, unnarrowed — because matching it is the host
|
|
94
|
+
* resolver's job, not this package's.
|
|
95
|
+
*/
|
|
96
|
+
export type NotificationsCopyResolver<T> = (context: {
|
|
97
|
+
readonly locale?: string | null;
|
|
98
|
+
}) => T;
|
|
99
|
+
export type NotificationsCopySource<T> = T | NotificationsCopyResolver<T>;
|
|
100
|
+
|
|
101
|
+
/**
|
|
102
|
+
* The messages in force, for ONE reader.
|
|
89
103
|
*
|
|
90
104
|
* A pass-through rather than a merge: there is nothing left to merge WITH, and
|
|
91
105
|
* that is the point of the change. The old version spread the host's table over
|
|
@@ -96,8 +110,27 @@ export interface NotificationMessages extends NotificationWireMessages {
|
|
|
96
110
|
* the same screen.
|
|
97
111
|
*
|
|
98
112
|
* Kept as a function because all three mounts read it off a config object, and
|
|
99
|
-
* because a later rule (a blank-string refusal, say) belongs in one place
|
|
113
|
+
* because a later rule (a blank-string refusal, say) belongs in one place —
|
|
114
|
+
* which is exactly what made it the right place to put the RESOLUTION when the
|
|
115
|
+
* field learned to take a resolver.
|
|
116
|
+
*
|
|
117
|
+
* **Call it where the sentence is used.** `createApiNotifications` runs once
|
|
118
|
+
* per process, and at least one host memoises its call behind an `if
|
|
119
|
+
* (assembled) return assembled;`, so a value read there answers every later
|
|
120
|
+
* request in the language the process started with — and a single-locale host
|
|
121
|
+
* cannot tell the difference. The route handlers call it per request; the
|
|
122
|
+
* parsers below them keep taking a plain pack, so one request resolves exactly
|
|
123
|
+
* once and no helper can disagree with another about the language.
|
|
124
|
+
*
|
|
125
|
+
* The generic survives the widening: a host whose pack carries extra keys of
|
|
126
|
+
* its own still gets them back, resolver or not.
|
|
100
127
|
*/
|
|
101
|
-
export function messagesOf<T extends NotificationWireMessages>(
|
|
102
|
-
|
|
128
|
+
export function messagesOf<T extends NotificationWireMessages>(
|
|
129
|
+
config: { messages: NotificationsCopySource<T> },
|
|
130
|
+
locale?: string,
|
|
131
|
+
): T {
|
|
132
|
+
const source = config.messages;
|
|
133
|
+
return typeof source === 'function'
|
|
134
|
+
? (source as NotificationsCopyResolver<T>)({ locale })
|
|
135
|
+
: source;
|
|
103
136
|
}
|
package/src/server/context.ts
CHANGED
|
@@ -32,6 +32,16 @@ export interface NotificationsRequest {
|
|
|
32
32
|
body?: unknown;
|
|
33
33
|
/** Headers the surface reads (`user-agent`, for the device hint). */
|
|
34
34
|
headers?: Record<string, string | undefined>;
|
|
35
|
+
/**
|
|
36
|
+
* The language to answer this caller in, as a language tag (`pt-BR`,
|
|
37
|
+
* `en-US`) — the same field `@12-apps/wiring`'s `WireRequest` carries.
|
|
38
|
+
*
|
|
39
|
+
* Populated by the host's adapter, which is the only layer that can
|
|
40
|
+
* negotiate one. Absent is meaningful and not an error: a host with one
|
|
41
|
+
* audience never sets it, and this surface must then answer with the words
|
|
42
|
+
* it was configured with rather than invent a language.
|
|
43
|
+
*/
|
|
44
|
+
locale?: string;
|
|
35
45
|
}
|
|
36
46
|
|
|
37
47
|
/** What a handler answers with; the adapter maps it onto its response type. */
|
|
@@ -1,5 +1,8 @@
|
|
|
1
1
|
import { createGeneratorRegistry, type NotificationGeneratorRegistry } from '../generators';
|
|
2
|
-
import
|
|
2
|
+
import type {
|
|
3
|
+
NotificationsCopySource,
|
|
4
|
+
NotificationWireMessages,
|
|
5
|
+
} from '../messages';
|
|
3
6
|
import type { ChannelRow } from '../preferences-core';
|
|
4
7
|
import {
|
|
5
8
|
taxonomyOf,
|
|
@@ -104,8 +107,15 @@ export interface NotificationsServerConfig {
|
|
|
104
107
|
onInboxChanged?: (userId: string) => void;
|
|
105
108
|
/** The host's authorization engine, for `notifyByPermission`. */
|
|
106
109
|
audience?: NotificationAudienceDirectory;
|
|
107
|
-
/**
|
|
108
|
-
|
|
110
|
+
/**
|
|
111
|
+
* Every user-facing sentence this surface can produce — REQUIRED host config.
|
|
112
|
+
*
|
|
113
|
+
* A pack, or a RESOLVER for a host whose callers do not share a language.
|
|
114
|
+
* Passed to the routes UNRESOLVED: this factory runs once per process (and
|
|
115
|
+
* at least one host memoises its call), so resolving here would answer every
|
|
116
|
+
* later request in the language the process started with.
|
|
117
|
+
*/
|
|
118
|
+
messages: NotificationsCopySource<NotificationWireMessages>;
|
|
109
119
|
/** The host's logger. Defaults to the console. */
|
|
110
120
|
logger?: NotificationLogger;
|
|
111
121
|
}
|
|
@@ -132,8 +142,14 @@ export interface ApiNotifications {
|
|
|
132
142
|
registerGenerator: NotificationGeneratorRegistry['register'];
|
|
133
143
|
/** The declared transports, for diagnostics and availability probes. */
|
|
134
144
|
transports: TransportRegistry;
|
|
135
|
-
/**
|
|
136
|
-
|
|
145
|
+
/**
|
|
146
|
+
* The copy source in force, so a host's own screens can reuse a sentence.
|
|
147
|
+
*
|
|
148
|
+
* The SOURCE rather than a resolved pack, for the same reason the routes get
|
|
149
|
+
* one: a host screen serving two readers must be able to ask per reader.
|
|
150
|
+
* Read it with `messagesOf({ messages }, locale)`.
|
|
151
|
+
*/
|
|
152
|
+
messages: NotificationsCopySource<NotificationWireMessages>;
|
|
137
153
|
}
|
|
138
154
|
|
|
139
155
|
/** Drop the keys the host left unset, so an absent seam stays absent. */
|
|
@@ -150,7 +166,15 @@ const consoleLogger: NotificationLogger = {
|
|
|
150
166
|
};
|
|
151
167
|
|
|
152
168
|
export function createApiNotifications(config: NotificationsServerConfig): ApiNotifications {
|
|
153
|
-
|
|
169
|
+
/**
|
|
170
|
+
* The SOURCE travels; nothing is resolved here.
|
|
171
|
+
*
|
|
172
|
+
* This factory runs once per process — and at least one host memoises its
|
|
173
|
+
* call behind an `if (assembled) return assembled;` — so a `messagesOf(config)`
|
|
174
|
+
* on this line would word every later request in the language the process
|
|
175
|
+
* started with. The handlers resolve per request instead.
|
|
176
|
+
*/
|
|
177
|
+
const messages = config.messages;
|
|
154
178
|
const taxonomy = taxonomyOf(config);
|
|
155
179
|
const logger = config.logger ?? consoleLogger;
|
|
156
180
|
|
package/src/server/routes.ts
CHANGED
|
@@ -1,4 +1,8 @@
|
|
|
1
|
-
import
|
|
1
|
+
import {
|
|
2
|
+
messagesOf,
|
|
3
|
+
type NotificationsCopySource,
|
|
4
|
+
type NotificationWireMessages,
|
|
5
|
+
} from '../messages';
|
|
2
6
|
import { NOTIFICATION_CHANNELS, type NotificationChannel } from '../types';
|
|
3
7
|
|
|
4
8
|
import {
|
|
@@ -40,7 +44,11 @@ interface NotificationRoutesDeps {
|
|
|
40
44
|
transports: TransportRegistry;
|
|
41
45
|
contacts: NotificationContactDirectory;
|
|
42
46
|
categories: readonly string[];
|
|
43
|
-
|
|
47
|
+
/**
|
|
48
|
+
* The SOURCE, not a resolved pack — the route table is built once per
|
|
49
|
+
* process and every handler below runs per request.
|
|
50
|
+
*/
|
|
51
|
+
messages: NotificationsCopySource<NotificationWireMessages>;
|
|
44
52
|
/** Told when a write actually changed something (for a realtime hint). */
|
|
45
53
|
onInboxChanged?: (userId: string) => void;
|
|
46
54
|
}
|
|
@@ -98,8 +106,13 @@ function inboxRoutes(deps: NotificationRoutesDeps): NotificationsRoute[] {
|
|
|
98
106
|
{
|
|
99
107
|
method: 'GET',
|
|
100
108
|
path: '/notifications',
|
|
101
|
-
handle: guarded(async ({ actor, query }) =>
|
|
102
|
-
ok(
|
|
109
|
+
handle: guarded(async ({ actor, query, locale }) =>
|
|
110
|
+
ok(
|
|
111
|
+
await deps.inbox.list(
|
|
112
|
+
actor.userId,
|
|
113
|
+
parseListQuery(query, messagesOf(deps, locale)),
|
|
114
|
+
),
|
|
115
|
+
),
|
|
103
116
|
),
|
|
104
117
|
},
|
|
105
118
|
{
|
|
@@ -113,8 +126,8 @@ function inboxRoutes(deps: NotificationRoutesDeps): NotificationsRoute[] {
|
|
|
113
126
|
{
|
|
114
127
|
method: 'POST',
|
|
115
128
|
path: '/notifications/mark-read',
|
|
116
|
-
handle: guarded(async ({ actor, body }) => {
|
|
117
|
-
const target = parseMarkReadBody(body, deps
|
|
129
|
+
handle: guarded(async ({ actor, body, locale }) => {
|
|
130
|
+
const target = parseMarkReadBody(body, messagesOf(deps, locale));
|
|
118
131
|
const updated =
|
|
119
132
|
'all' in target
|
|
120
133
|
? await deps.inbox.markAllRead(actor.userId)
|
|
@@ -131,10 +144,10 @@ function inboxRoutes(deps: NotificationRoutesDeps): NotificationsRoute[] {
|
|
|
131
144
|
method: 'POST',
|
|
132
145
|
// POST, not DELETE, because the ids travel in a JSON body.
|
|
133
146
|
path: '/notifications/delete',
|
|
134
|
-
handle: guarded(async ({ actor, body }) => {
|
|
147
|
+
handle: guarded(async ({ actor, body, locale }) => {
|
|
135
148
|
const deleted = await deps.inbox.softDelete(
|
|
136
149
|
actor.userId,
|
|
137
|
-
parseDeleteBody(body, deps
|
|
150
|
+
parseDeleteBody(body, messagesOf(deps, locale)),
|
|
138
151
|
);
|
|
139
152
|
// Same rule as mark-read. A delete can move the badge too — an UNREAD
|
|
140
153
|
// row that is removed takes its place in the count with it.
|
|
@@ -155,10 +168,13 @@ function preferenceRoutes(deps: NotificationRoutesDeps): NotificationsRoute[] {
|
|
|
155
168
|
{
|
|
156
169
|
method: 'PUT',
|
|
157
170
|
path: '/notification-preferences',
|
|
158
|
-
handle: guarded(async ({ actor, body }) => {
|
|
171
|
+
handle: guarded(async ({ actor, body, locale }) => {
|
|
159
172
|
// The dispatch pipeline reads these on every emit, so a save takes
|
|
160
173
|
// effect immediately — no cache to invalidate.
|
|
161
|
-
await deps.preferences.save(
|
|
174
|
+
await deps.preferences.save(
|
|
175
|
+
actor.userId,
|
|
176
|
+
parsePreferencesBody(body, messagesOf(deps, locale)),
|
|
177
|
+
);
|
|
162
178
|
return ok(await preferencesPayload(deps, actor.userId));
|
|
163
179
|
}),
|
|
164
180
|
},
|
|
@@ -170,8 +186,8 @@ function pushRoutes(deps: NotificationRoutesDeps): NotificationsRoute[] {
|
|
|
170
186
|
{
|
|
171
187
|
method: 'GET',
|
|
172
188
|
path: '/push-subscriptions',
|
|
173
|
-
handle: guarded(async ({ actor, query }) => {
|
|
174
|
-
const endpoint = parsePushEndpointQuery(query, deps
|
|
189
|
+
handle: guarded(async ({ actor, query, locale }) => {
|
|
190
|
+
const endpoint = parsePushEndpointQuery(query, messagesOf(deps, locale));
|
|
175
191
|
return ok({
|
|
176
192
|
// null = web push is not configured on this deployment.
|
|
177
193
|
vapidPublicKey: deps.transports.webPushPublicKey(),
|
|
@@ -188,8 +204,8 @@ function pushRoutes(deps: NotificationRoutesDeps): NotificationsRoute[] {
|
|
|
188
204
|
{
|
|
189
205
|
method: 'POST',
|
|
190
206
|
path: '/push-subscriptions',
|
|
191
|
-
handle: guarded(async ({ actor, body, headers }) => {
|
|
192
|
-
const input = parsePushSubscriptionBody(body, deps
|
|
207
|
+
handle: guarded(async ({ actor, body, headers, locale }) => {
|
|
208
|
+
const input = parsePushSubscriptionBody(body, messagesOf(deps, locale));
|
|
193
209
|
const userAgent = headers?.['user-agent'];
|
|
194
210
|
await deps.pushSubscriptions.save(actor.userId, {
|
|
195
211
|
...input,
|
|
@@ -202,10 +218,10 @@ function pushRoutes(deps: NotificationRoutesDeps): NotificationsRoute[] {
|
|
|
202
218
|
method: 'DELETE',
|
|
203
219
|
// The endpoint is a long opaque URL, unusable as a path param.
|
|
204
220
|
path: '/push-subscriptions',
|
|
205
|
-
handle: guarded(async ({ actor, body }) => {
|
|
221
|
+
handle: guarded(async ({ actor, body, locale }) => {
|
|
206
222
|
await deps.pushSubscriptions.remove(
|
|
207
223
|
actor.userId,
|
|
208
|
-
parsePushEndpointBody(body, deps
|
|
224
|
+
parsePushEndpointBody(body, messagesOf(deps, locale)),
|
|
209
225
|
);
|
|
210
226
|
return ok({ count: await deps.pushSubscriptions.count(actor.userId) });
|
|
211
227
|
}),
|