@12-apps/notifications 4.8.0 → 4.10.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/ADOPTING.md +173 -1
- package/README.md +138 -0
- package/dist/catalog-BOVZ2b-b.d.ts +132 -0
- package/dist/{chunk-M5XTROYM.js → chunk-5Y7QRORV.js} +8 -4
- package/dist/chunk-5Y7QRORV.js.map +1 -0
- package/dist/{chunk-BGI4BQ66.js → chunk-6W7INOYQ.js} +1 -1
- package/dist/chunk-6W7INOYQ.js.map +1 -0
- package/dist/chunk-EKUSNUBT.js +222 -0
- package/dist/chunk-EKUSNUBT.js.map +1 -0
- package/dist/chunk-FTFLU6KH.js +114 -0
- package/dist/chunk-FTFLU6KH.js.map +1 -0
- package/dist/chunk-JCVRQ42B.js +226 -0
- package/dist/chunk-JCVRQ42B.js.map +1 -0
- package/dist/chunk-RTURLH5U.js +33 -0
- package/dist/chunk-RTURLH5U.js.map +1 -0
- package/dist/{chunk-GGAZOLNN.js → chunk-SWOWHIFE.js} +2 -2
- package/dist/{chunk-7234ROOP.js → chunk-WVRODNXQ.js} +28 -6
- package/dist/chunk-WVRODNXQ.js.map +1 -0
- package/dist/chunk-ZFIYBNZ7.js +502 -0
- package/dist/chunk-ZFIYBNZ7.js.map +1 -0
- package/dist/{chunk-KU4FVOFA.js → chunk-ZIR3ILFH.js} +2 -2
- package/dist/{create-api-notifications-WaexrZnn.d.ts → create-api-notifications-CcPYrM3p.d.ts} +35 -4
- package/dist/{create-web-notifications-DrgqtU4Q.d.ts → create-web-notifications-_NVYmlvy.d.ts} +89 -4
- package/dist/email/index.d.ts +26 -0
- package/dist/email/index.js +20 -0
- package/dist/email/index.js.map +1 -0
- package/dist/email/locales.d.ts +43 -0
- package/dist/email/locales.js +29 -0
- package/dist/email/locales.js.map +1 -0
- package/dist/email/previews/hono.d.ts +46 -0
- package/dist/email/previews/hono.js +33 -0
- package/dist/email/previews/hono.js.map +1 -0
- package/dist/email/previews/index.d.ts +67 -0
- package/dist/email/previews/index.js +12 -0
- package/dist/email/previews/index.js.map +1 -0
- package/dist/email/previews/react/index.d.ts +43 -0
- package/dist/email/previews/react/index.js +58 -0
- package/dist/email/previews/react/index.js.map +1 -0
- package/dist/{generators-CH5m8Ucj.d.ts → generators-qAD4fNPq.d.ts} +1 -1
- package/dist/hono/index.d.ts +6 -5
- package/dist/hono/index.js +5 -3
- package/dist/hono/index.js.map +1 -1
- package/dist/index.d.ts +4 -3
- package/dist/index.js +15 -5
- package/dist/{jobs-CcsSA0YR.d.ts → jobs--fex87-q.d.ts} +1 -1
- package/dist/live-DYxEFO49.d.ts +158 -0
- package/dist/manifest/index.d.ts +39 -1
- package/dist/manifest/index.js +9 -0
- package/dist/manifest/index.js.map +1 -1
- package/dist/manifest/server.d.ts +32 -7
- package/dist/manifest/server.js +16 -4
- package/dist/manifest/server.js.map +1 -1
- package/dist/manifest/web.d.ts +19 -4
- package/dist/manifest/web.js +9 -1
- package/dist/manifest/web.js.map +1 -1
- package/dist/{panel-UFXNO4AF.js → panel-T36JEMO3.js} +37 -13
- package/dist/panel-T36JEMO3.js.map +1 -0
- package/dist/{preferences-screen-V3ZBWP7A.js → preferences-screen-SXUIGECY.js} +2 -2
- package/dist/preview-screen-DYJRAnAY.d.ts +83 -0
- package/dist/react/index.d.ts +55 -6
- package/dist/react/index.js +16 -10
- package/dist/server/index.d.ts +8 -7
- package/dist/server/index.js +6 -4
- package/dist/template-Dlt5S54z.d.ts +196 -0
- package/dist/{types-CYJN1xCM.d.ts → types-BlqZkCWZ.d.ts} +11 -2
- package/dist/web-push/index.d.ts +2 -2
- package/dist/{web-push-iER5StBm.d.ts → web-push-Dnyaha2z.d.ts} +21 -1
- package/dist/{wire-CmRrB3_s.d.ts → wire-BG1kuoXX.d.ts} +1 -1
- package/package.json +22 -2
- package/src/email/chrome.en-US.ts +8 -0
- package/src/email/chrome.pt-BR.ts +21 -0
- package/src/email/index.ts +43 -0
- package/src/email/locales.ts +29 -0
- package/src/email/previews/catalog.ts +208 -0
- package/src/email/previews/hono.ts +69 -0
- package/src/email/previews/index.ts +26 -0
- package/src/email/previews/react/copy.en-US.ts +24 -0
- package/src/email/previews/react/copy.pt-BR.ts +24 -0
- package/src/email/previews/react/copy.ts +42 -0
- package/src/email/previews/react/index.tsx +14 -0
- package/src/email/previews/react/loadable.tsx +83 -0
- package/src/email/previews/react/message-list.tsx +133 -0
- package/src/email/previews/react/message-view.tsx +167 -0
- package/src/email/previews/react/preview-screen.tsx +342 -0
- package/src/email/previews/react/transport.ts +60 -0
- package/src/email/previews/routes.ts +111 -0
- package/src/email/template.ts +365 -0
- package/src/email/text.ts +59 -0
- package/src/email/theme.ts +77 -0
- package/src/index.ts +10 -0
- package/src/live.ts +179 -0
- package/src/manifest/index.ts +37 -0
- package/src/manifest/server.ts +23 -0
- package/src/manifest/web.ts +13 -0
- package/src/react/create-web-notifications.tsx +15 -1
- package/src/react/index.ts +29 -0
- package/src/react/live-card.tsx +279 -0
- package/src/react/live-config.ts +79 -0
- package/src/react/live-section.tsx +141 -0
- package/src/react/panel-lazy.tsx +3 -0
- package/src/react/panel.tsx +57 -10
- package/src/react/relative-time.ts +14 -2
- package/src/server/transports/email.ts +62 -0
- package/src/server/transports/web-push.ts +22 -0
- package/src/types.ts +10 -1
- package/dist/chunk-7234ROOP.js.map +0 -1
- package/dist/chunk-BGI4BQ66.js.map +0 -1
- package/dist/chunk-M5XTROYM.js.map +0 -1
- package/dist/chunk-O5BVUXPO.js +0 -22
- package/dist/chunk-O5BVUXPO.js.map +0 -1
- package/dist/panel-UFXNO4AF.js.map +0 -1
- /package/dist/{chunk-GGAZOLNN.js.map → chunk-SWOWHIFE.js.map} +0 -0
- /package/dist/{chunk-KU4FVOFA.js.map → chunk-ZIR3ILFH.js.map} +0 -0
- /package/dist/{preferences-screen-V3ZBWP7A.js.map → preferences-screen-SXUIGECY.js.map} +0 -0
|
@@ -0,0 +1,83 @@
|
|
|
1
|
+
import { JSX } from 'react';
|
|
2
|
+
|
|
3
|
+
/**
|
|
4
|
+
* The SHAPE of the operator screen's words — no words.
|
|
5
|
+
*
|
|
6
|
+
* REQUIRED config with no default in any language, the copy-portability
|
|
7
|
+
* doctrine. `./copy.pt-BR` and `./copy.en-US` ship packs a host passes BY NAME.
|
|
8
|
+
*
|
|
9
|
+
* What is NOT here is anything from the mails themselves: a subject, a body and
|
|
10
|
+
* a button label all arrive from the server already rendered in whichever
|
|
11
|
+
* language the preview was asked for. That separation is the point of the
|
|
12
|
+
* language switch — the CHROME follows the operator, the MESSAGE follows its
|
|
13
|
+
* own recipient.
|
|
14
|
+
*/
|
|
15
|
+
interface EmailPreviewScreenCopy {
|
|
16
|
+
readonly title: string;
|
|
17
|
+
readonly description: string;
|
|
18
|
+
/** The sidebar's filter field. */
|
|
19
|
+
readonly searchLabel: string;
|
|
20
|
+
readonly searchPlaceholder: string;
|
|
21
|
+
/** Nothing matched the filter. */
|
|
22
|
+
readonly noMatches: string;
|
|
23
|
+
/** No row is selected yet. */
|
|
24
|
+
readonly pickOne: string;
|
|
25
|
+
/** The three views of one message. */
|
|
26
|
+
readonly tabHtml: string;
|
|
27
|
+
readonly tabText: string;
|
|
28
|
+
readonly tabSource: string;
|
|
29
|
+
/** The two widths the HTML view renders at. */
|
|
30
|
+
readonly widthDesktop: string;
|
|
31
|
+
readonly widthMobile: string;
|
|
32
|
+
/** The subject line's label above the preview. */
|
|
33
|
+
readonly subjectLabel: string;
|
|
34
|
+
/** The sandboxed frame's title, for screen readers. */
|
|
35
|
+
readonly frameTitle: string;
|
|
36
|
+
/** The coverage strip, when the surface reports a gap. */
|
|
37
|
+
readonly coverageTitle: string;
|
|
38
|
+
readonly missingSamples: (keys: string) => string;
|
|
39
|
+
readonly orphanSamples: (keys: string) => string;
|
|
40
|
+
/** Loading and failure states. */
|
|
41
|
+
readonly loading: string;
|
|
42
|
+
readonly loadError: string;
|
|
43
|
+
readonly retry: string;
|
|
44
|
+
}
|
|
45
|
+
|
|
46
|
+
/**
|
|
47
|
+
* The operator screen over the `./email/previews` catalogue.
|
|
48
|
+
*
|
|
49
|
+
* ## What a host supplies, and what it does not
|
|
50
|
+
*
|
|
51
|
+
* `apiBase` — where the routes are mounted — and `copy`, by name. Nothing else:
|
|
52
|
+
* the owners, the languages, the messages and their subjects all arrive from
|
|
53
|
+
* the surface, because they are facts about the host's own mail that no prop
|
|
54
|
+
* could usefully restate.
|
|
55
|
+
*
|
|
56
|
+
* ## The selection lives in the URL, without a router
|
|
57
|
+
*
|
|
58
|
+
* `?id=` and `?locale=`, read and written through `history.replaceState`. A
|
|
59
|
+
* link to one mail in one language is the actual workflow this screen serves —
|
|
60
|
+
* "look at what the reset mail says now" — and local state would make every
|
|
61
|
+
* such conversation a set of instructions instead of a link.
|
|
62
|
+
*
|
|
63
|
+
* Deliberately NOT a router integration: this package cannot know whether a
|
|
64
|
+
* host runs react-router, TanStack Router or a framework's own, and a screen
|
|
65
|
+
* that imported one would be unmountable in the other two. `replaceState` is
|
|
66
|
+
* the one API all of them are built on, and `replace` rather than `push` so
|
|
67
|
+
* browsing twenty mails is not twenty back-button steps.
|
|
68
|
+
*/
|
|
69
|
+
interface EmailPreviewScreenConfig {
|
|
70
|
+
/** Where the routes are mounted, e.g. `/api/platform/email-previews`. */
|
|
71
|
+
readonly apiBase: string;
|
|
72
|
+
/** The screen's words. REQUIRED — see `./copy`. */
|
|
73
|
+
readonly copy: EmailPreviewScreenCopy;
|
|
74
|
+
}
|
|
75
|
+
/**
|
|
76
|
+
* Build the screen. One call, one config object — the shape every factory in
|
|
77
|
+
* this estate has.
|
|
78
|
+
*/
|
|
79
|
+
declare function createEmailPreviewScreen(config: EmailPreviewScreenConfig): {
|
|
80
|
+
page: () => JSX.Element;
|
|
81
|
+
};
|
|
82
|
+
|
|
83
|
+
export { type EmailPreviewScreenCopy as E, type EmailPreviewScreenConfig as a, createEmailPreviewScreen as c };
|
package/dist/react/index.d.ts
CHANGED
|
@@ -1,8 +1,10 @@
|
|
|
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
|
|
1
|
+
import { L as LiveActivitiesConfig, N as NotificationsApiClient } from '../create-web-notifications-_NVYmlvy.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 LiveActivitiesHook, g as LiveActivityMessages, h as NotificationsHttpError, i as NotificationsPanelProps, j as NotificationsResult, k as NotificationsSignalHook, l as NotificationsSubscribe, m as NotificationsTransport, n as NotificationsWebConfig, P as PAGE_SIZE, o as PreferencesPayload, p as PreferencesScreenProps, q as PushRegistrationPayload, W as WebNotifications, r as WebPushPlatformHint, s as WebPushSetupConfig, t as createInboxStore, u as createNotificationsApiClient, c as createWebNotifications, v as httpNotificationsTransport, w as useInboxList, x as useInboxState, y as useUnreadCount } from '../create-web-notifications-_NVYmlvy.js';
|
|
3
3
|
import { JSX } from 'react';
|
|
4
|
-
import {
|
|
5
|
-
|
|
4
|
+
import { b as LiveActivity } from '../live-DYxEFO49.js';
|
|
5
|
+
export { c as LiveActivityLane, d as LiveActivityStep, l as liveActivityLane } from '../live-DYxEFO49.js';
|
|
6
|
+
import { N as NotificationMessages } from '../wire-BG1kuoXX.js';
|
|
7
|
+
import '../types-BlqZkCWZ.js';
|
|
6
8
|
|
|
7
9
|
/** Inline SVG bell (no icon-library dependency in this package). */
|
|
8
10
|
|
|
@@ -11,12 +13,59 @@ declare function BellIcon({ size, dim, }: {
|
|
|
11
13
|
dim?: boolean;
|
|
12
14
|
}): JSX.Element;
|
|
13
15
|
|
|
16
|
+
/**
|
|
17
|
+
* The pinned block at the top of the panel: everything that is happening NOW,
|
|
18
|
+
* above everything that has already happened.
|
|
19
|
+
*
|
|
20
|
+
* ## Why it is here and not a second surface
|
|
21
|
+
*
|
|
22
|
+
* The notification centre is where a person goes to find out what they missed.
|
|
23
|
+
* Splitting "happening" into its own bell would make them check two places to
|
|
24
|
+
* answer one question, and the half they would stop checking is the one that
|
|
25
|
+
* only has something in it occasionally — which is this one. Above the list,
|
|
26
|
+
* inside the same drawer, it is on the path they already walk.
|
|
27
|
+
*
|
|
28
|
+
* ## What it deliberately does NOT do
|
|
29
|
+
*
|
|
30
|
+
* - It does not touch `unread`. A live entry is not news; counting it would put
|
|
31
|
+
* a number on the bell that no amount of reading can clear.
|
|
32
|
+
* - It renders nothing at all when there is nothing live — no heading, no empty
|
|
33
|
+
* state, no reserved space. A panel with one permanent empty section in it is
|
|
34
|
+
* a panel that has taught its reader to skip the top.
|
|
35
|
+
* - It does not fetch. `useActivities` is the host's, and `active` tells it
|
|
36
|
+
* whether anyone is looking.
|
|
37
|
+
*/
|
|
38
|
+
|
|
39
|
+
interface LiveSectionProps {
|
|
40
|
+
config: LiveActivitiesConfig;
|
|
41
|
+
messages: NotificationMessages;
|
|
42
|
+
/** Whether the panel is open — passed straight through to the host's hook. */
|
|
43
|
+
active: boolean;
|
|
44
|
+
/**
|
|
45
|
+
* Follow a card's link.
|
|
46
|
+
*
|
|
47
|
+
* Optional, and the panel omits it for a host with no router: a card that
|
|
48
|
+
* cannot go anywhere renders as text rather than as a named control that
|
|
49
|
+
* does nothing.
|
|
50
|
+
*/
|
|
51
|
+
onOpen?: (activity: LiveActivity) => void;
|
|
52
|
+
}
|
|
53
|
+
declare function LiveSection({ config, messages, active, onOpen, }: LiveSectionProps): JSX.Element | null;
|
|
54
|
+
|
|
14
55
|
/**
|
|
15
56
|
* "há 5 min"-style relative timestamp, falling back to an absolute date for
|
|
16
57
|
* anything older than a week. Every word comes from the messages table, so a
|
|
17
58
|
* host in another locale changes the copy and the locale together.
|
|
59
|
+
*
|
|
60
|
+
* `now` is a parameter rather than a read, and the live section is why. A
|
|
61
|
+
* relative phrase is only true for the instant it was computed, so something
|
|
62
|
+
* has to CAUSE the render that recomputes it — and the live entries' own data
|
|
63
|
+
* cannot: a host backed by react-query gets the previous object back whenever a
|
|
64
|
+
* poll is deep-equal (`structuralSharing`, on by default), which within one
|
|
65
|
+
* stage it always is. The section therefore ticks a clock and hands it down.
|
|
66
|
+
* Defaulted, so every existing caller reads the wall clock exactly as before.
|
|
18
67
|
*/
|
|
19
|
-
declare function relativeTime(iso: string, messages: NotificationMessages): string;
|
|
68
|
+
declare function relativeTime(iso: string, messages: NotificationMessages, now?: number): string;
|
|
20
69
|
|
|
21
70
|
/**
|
|
22
71
|
* The browser half of Web Push: register the service worker, ask permission,
|
|
@@ -44,4 +93,4 @@ declare function enableWebPush(api: NotificationsApiClient, swPath?: string): Pr
|
|
|
44
93
|
/** Disable flow: unsubscribe the browser and drop the server-side row. */
|
|
45
94
|
declare function disableWebPush(api: NotificationsApiClient): Promise<void>;
|
|
46
95
|
|
|
47
|
-
export { BellIcon, NotificationsApiClient, type PushSetupResult, disableWebPush, enableWebPush, getExistingPushSubscription, pushSupported, relativeTime };
|
|
96
|
+
export { BellIcon, LiveActivitiesConfig, LiveActivity, LiveSection, type LiveSectionProps, NotificationsApiClient, type PushSetupResult, disableWebPush, enableWebPush, getExistingPushSubscription, pushSupported, relativeTime };
|
package/dist/react/index.js
CHANGED
|
@@ -1,18 +1,13 @@
|
|
|
1
|
-
import {
|
|
2
|
-
relativeTime
|
|
3
|
-
} from "../chunk-O5BVUXPO.js";
|
|
4
|
-
import {
|
|
5
|
-
disableWebPush,
|
|
6
|
-
enableWebPush,
|
|
7
|
-
getExistingPushSubscription,
|
|
8
|
-
pushSupported
|
|
9
|
-
} from "../chunk-CQZMTFPY.js";
|
|
10
1
|
import {
|
|
11
2
|
NotificationsHttpError,
|
|
12
3
|
createNotificationsApiClient,
|
|
13
4
|
createWebNotifications,
|
|
14
5
|
httpNotificationsTransport
|
|
15
|
-
} from "../chunk-
|
|
6
|
+
} from "../chunk-5Y7QRORV.js";
|
|
7
|
+
import {
|
|
8
|
+
LiveSection,
|
|
9
|
+
relativeTime
|
|
10
|
+
} from "../chunk-JCVRQ42B.js";
|
|
16
11
|
import {
|
|
17
12
|
BADGE_POLL_MS,
|
|
18
13
|
BADGE_RECONCILE_MS,
|
|
@@ -23,12 +18,22 @@ import {
|
|
|
23
18
|
useInboxState,
|
|
24
19
|
useUnreadCount
|
|
25
20
|
} from "../chunk-BW723CX2.js";
|
|
21
|
+
import {
|
|
22
|
+
disableWebPush,
|
|
23
|
+
enableWebPush,
|
|
24
|
+
getExistingPushSubscription,
|
|
25
|
+
pushSupported
|
|
26
|
+
} from "../chunk-CQZMTFPY.js";
|
|
26
27
|
import "../chunk-M2TVBVH2.js";
|
|
28
|
+
import {
|
|
29
|
+
liveActivityLane
|
|
30
|
+
} from "../chunk-RTURLH5U.js";
|
|
27
31
|
import "../chunk-7QVYU63E.js";
|
|
28
32
|
export {
|
|
29
33
|
BADGE_POLL_MS,
|
|
30
34
|
BADGE_RECONCILE_MS,
|
|
31
35
|
BellIcon,
|
|
36
|
+
LiveSection,
|
|
32
37
|
NotificationsHttpError,
|
|
33
38
|
PAGE_SIZE,
|
|
34
39
|
createInboxStore,
|
|
@@ -38,6 +43,7 @@ export {
|
|
|
38
43
|
enableWebPush,
|
|
39
44
|
getExistingPushSubscription,
|
|
40
45
|
httpNotificationsTransport,
|
|
46
|
+
liveActivityLane,
|
|
41
47
|
pushSupported,
|
|
42
48
|
relativeTime,
|
|
43
49
|
useInboxList,
|
package/dist/server/index.d.ts
CHANGED
|
@@ -1,11 +1,12 @@
|
|
|
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-
|
|
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-
|
|
1
|
+
import { A as ApiNotifications } from '../create-api-notifications-CcPYrM3p.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-CcPYrM3p.js';
|
|
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-Dnyaha2z.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-
|
|
6
|
-
import '../generators-
|
|
7
|
-
import '../types-
|
|
8
|
-
import '../wire-
|
|
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--fex87-q.js';
|
|
6
|
+
import '../generators-qAD4fNPq.js';
|
|
7
|
+
import '../types-BlqZkCWZ.js';
|
|
8
|
+
import '../wire-BG1kuoXX.js';
|
|
9
|
+
import '../template-Dlt5S54z.js';
|
|
9
10
|
import '@12-apps/wiring';
|
|
10
11
|
|
|
11
12
|
/**
|
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-ZIR3ILFH.js";
|
|
8
8
|
import {
|
|
9
9
|
EMAIL_DRIVERS,
|
|
10
10
|
NotificationProviderError,
|
|
@@ -25,10 +25,12 @@ import {
|
|
|
25
25
|
smsTransport,
|
|
26
26
|
webPushTransport,
|
|
27
27
|
whatsAppTransport
|
|
28
|
-
} from "../chunk-
|
|
29
|
-
import "../chunk-
|
|
30
|
-
import "../chunk-BGI4BQ66.js";
|
|
28
|
+
} from "../chunk-WVRODNXQ.js";
|
|
29
|
+
import "../chunk-SWOWHIFE.js";
|
|
31
30
|
import "../chunk-M2TVBVH2.js";
|
|
31
|
+
import "../chunk-RTURLH5U.js";
|
|
32
|
+
import "../chunk-6W7INOYQ.js";
|
|
33
|
+
import "../chunk-EKUSNUBT.js";
|
|
32
34
|
import "../chunk-7QVYU63E.js";
|
|
33
35
|
export {
|
|
34
36
|
EMAIL_DRIVERS,
|
|
@@ -0,0 +1,196 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* The layout's design tokens — the nine colours a mail is drawn from.
|
|
3
|
+
*
|
|
4
|
+
* ## Why a theme is a VALUE here and copy is required config
|
|
5
|
+
*
|
|
6
|
+
* The copy-portability doctrine is that a package ships no user-facing
|
|
7
|
+
* sentences: a default in the origin host's language reads as finished to the
|
|
8
|
+
* next host right up until somebody's customer gets mail in a language they do
|
|
9
|
+
* not speak. Colour is not that. {@link NEUTRAL_EMAIL_THEME} is not anybody's
|
|
10
|
+
* brand — it is grey on white, which reads as *unstyled but tidy* rather than
|
|
11
|
+
* as *somebody else's product*. A host that passes nothing gets a mail that
|
|
12
|
+
* looks plain, not a mail that looks like a different company.
|
|
13
|
+
*
|
|
14
|
+
* So this one has a default and the copy does not, and the difference is
|
|
15
|
+
* exactly whether getting it wrong is invisible. A missing theme is visible in
|
|
16
|
+
* the first preview; a wrong-language default is invisible until a customer
|
|
17
|
+
* complains.
|
|
18
|
+
*
|
|
19
|
+
* ## What a host should know before overriding it
|
|
20
|
+
*
|
|
21
|
+
* Two rungs of most brand palettes cannot carry light text, and a mail has no
|
|
22
|
+
* theme to fall back on when a client ignores a colour. {@link accent} is the
|
|
23
|
+
* one that MUST clear 4.5:1 against white — it is the CTA fill and every link.
|
|
24
|
+
* A warm mid-tone that looks fine on a screen at 2.6:1 is unreadable in an
|
|
25
|
+
* inbox, and nothing in the pipeline will say so.
|
|
26
|
+
*/
|
|
27
|
+
interface EmailTheme {
|
|
28
|
+
/** The page behind the card. Never pure white: a white card needs a ground. */
|
|
29
|
+
readonly page: string;
|
|
30
|
+
/** The card itself. */
|
|
31
|
+
readonly surface: string;
|
|
32
|
+
/** A quieter panel inside the card — the facts table. */
|
|
33
|
+
readonly panel: string;
|
|
34
|
+
/** Every hairline. */
|
|
35
|
+
readonly border: string;
|
|
36
|
+
/** Body text. */
|
|
37
|
+
readonly ink: string;
|
|
38
|
+
/** Secondary text: the footer, the fallback link, a fact's label. */
|
|
39
|
+
readonly muted: string;
|
|
40
|
+
/** The action colour — the CTA fill and every link. Must clear 4.5:1 on white. */
|
|
41
|
+
readonly accent: string;
|
|
42
|
+
/** Ink ON {@link accent}. */
|
|
43
|
+
readonly onAccent: string;
|
|
44
|
+
/** The rule under the wordmark. Decorative — nothing is written on it. */
|
|
45
|
+
readonly rule: string;
|
|
46
|
+
}
|
|
47
|
+
/**
|
|
48
|
+
* A brand-free default: greys, one blue, nothing anybody would mistake for a
|
|
49
|
+
* product. Every value clears its contrast requirement against its own ground.
|
|
50
|
+
*/
|
|
51
|
+
declare const NEUTRAL_EMAIL_THEME: EmailTheme;
|
|
52
|
+
/**
|
|
53
|
+
* The font stack, as one string.
|
|
54
|
+
*
|
|
55
|
+
* System fonts only, and no web font: a `@font-face` is stripped by Gmail and
|
|
56
|
+
* by Outlook, so a mail that depends on one renders in whatever the client
|
|
57
|
+
* substitutes — never the fallback the designer chose. The stack ends in the
|
|
58
|
+
* emoji faces so a status glyph in a subject line does not drop to a tofu box
|
|
59
|
+
* on Windows.
|
|
60
|
+
*/
|
|
61
|
+
declare const EMAIL_FONT_STACK = "-apple-system, BlinkMacSystemFont, 'Segoe UI', Roboto, 'Helvetica Neue', Arial, sans-serif, 'Apple Color Emoji', 'Segoe UI Emoji'";
|
|
62
|
+
/** The card's width. 600px is the width every client lays out without scroll. */
|
|
63
|
+
declare const EMAIL_CONTENT_WIDTH = 600;
|
|
64
|
+
|
|
65
|
+
/**
|
|
66
|
+
* THE transactional-mail layout: one document model, one HTML renderer, one
|
|
67
|
+
* plain-text twin.
|
|
68
|
+
*
|
|
69
|
+
* ## The problem it exists for
|
|
70
|
+
*
|
|
71
|
+
* A host that sends mail from more than one place ends up rendering it in more
|
|
72
|
+
* than one way — the notification pipeline emits a bold paragraph and a bare
|
|
73
|
+
* anchor, the sign-in flow emits a `div` with a max-width, the receipt builds
|
|
74
|
+
* its own list — and each grows its own `escapeHtml` and its own text twin. The
|
|
75
|
+
* three look like three different products, because a paragraph with no
|
|
76
|
+
* document around it inherits whatever the client decides: 13px Arial in Gmail,
|
|
77
|
+
* Times New Roman in Outlook, no centring anywhere.
|
|
78
|
+
*
|
|
79
|
+
* That is a LAYOUT problem rather than a wording one, which is what makes it a
|
|
80
|
+
* package's to solve. The words stay the host's; the document does not.
|
|
81
|
+
*
|
|
82
|
+
* ## Why the document is a MODEL and not a string
|
|
83
|
+
*
|
|
84
|
+
* Every caller hands over structure — a heading, some paragraphs, an optional
|
|
85
|
+
* facts table, at most one call to action — and never markup. Three things fall
|
|
86
|
+
* out of that, and each is a real defect in the hand-rolled renderers this
|
|
87
|
+
* replaces:
|
|
88
|
+
*
|
|
89
|
+
* 1. **Escaping cannot be forgotten.** Every field is escaped here, once. Of
|
|
90
|
+
* the three renderers this was extracted from, two escaped the body and
|
|
91
|
+
* interpolated the LINK raw.
|
|
92
|
+
* 2. **The plain-text twin cannot drift.** Both halves render from the same
|
|
93
|
+
* object, so a line added to one is in the other. A `text/html` part with no
|
|
94
|
+
* `text/plain` twin is scored by every major spam filter, and it is what a
|
|
95
|
+
* watch, a terminal client and a screen reader in plain-text mode show.
|
|
96
|
+
* 3. **A preview is honest.** `./server`'s catalogue renders exactly this, from
|
|
97
|
+
* exactly these inputs, so a preview cannot be right about a mail nobody
|
|
98
|
+
* gets.
|
|
99
|
+
*
|
|
100
|
+
* ## The client constraints this encodes
|
|
101
|
+
*
|
|
102
|
+
* Stated once here, so no caller has to know them:
|
|
103
|
+
*
|
|
104
|
+
* - **Tables, not divs.** Outlook 2016+ on Windows lays HTML out with Word,
|
|
105
|
+
* which supports no `flex`, no `grid` and no reliable `max-width` on a block.
|
|
106
|
+
* A centred 600px table is the one construction every client agrees on.
|
|
107
|
+
* - **Inline styles.** Gmail strips `<style>` from the document it renders in
|
|
108
|
+
* several contexts, and always in the Gmail app for a non-Gmail account. The
|
|
109
|
+
* `<style>` block below therefore carries only the mobile media query —
|
|
110
|
+
* progressive enhancement, never anything the layout depends on.
|
|
111
|
+
* - **No web fonts, no `color-mix()`, no CSS variables.** See `./theme`.
|
|
112
|
+
* - **A preheader.** The inbox list shows the first text in the body after the
|
|
113
|
+
* subject; without one it shows the footer's legal line.
|
|
114
|
+
* - **`role="presentation"`** on every layout table, so a screen reader reads
|
|
115
|
+
* the message rather than announcing a five-column grid.
|
|
116
|
+
*/
|
|
117
|
+
/** The words the LAYOUT itself needs — never the message's own sentences. */
|
|
118
|
+
interface EmailChromeCopy {
|
|
119
|
+
/** "If the button does not work, paste this address into your browser:" */
|
|
120
|
+
readonly fallbackHint: string;
|
|
121
|
+
/** "This is an automated message — please do not reply." */
|
|
122
|
+
readonly automated: string;
|
|
123
|
+
/** The footer's one line about the product, given the brand name. */
|
|
124
|
+
readonly tagline: (brand: string) => string;
|
|
125
|
+
}
|
|
126
|
+
/** The one call to action a message may carry. More than one dilutes both. */
|
|
127
|
+
interface EmailAction {
|
|
128
|
+
readonly label: string;
|
|
129
|
+
readonly href: string;
|
|
130
|
+
}
|
|
131
|
+
/** One row of the facts panel — a receipt's total, an order's reference. */
|
|
132
|
+
interface EmailFact {
|
|
133
|
+
readonly label: string;
|
|
134
|
+
readonly value: string;
|
|
135
|
+
/** Renders bolder and larger — the one number the reader came for. */
|
|
136
|
+
readonly emphasis?: boolean;
|
|
137
|
+
}
|
|
138
|
+
/** Everything a message says, with no opinion about how it looks. */
|
|
139
|
+
interface EmailDocument {
|
|
140
|
+
/** The subject line. Also the document `<title>`. */
|
|
141
|
+
readonly subject: string;
|
|
142
|
+
/**
|
|
143
|
+
* The inbox-list preview, ~90 chars. Falls back to the first paragraph,
|
|
144
|
+
* which is right far more often than it is wrong.
|
|
145
|
+
*/
|
|
146
|
+
readonly preheader?: string;
|
|
147
|
+
/** The `<h1>`. Usually a restatement of the subject in the reader's terms. */
|
|
148
|
+
readonly heading: string;
|
|
149
|
+
/** The body, one entry per paragraph. Plain sentences — never markup. */
|
|
150
|
+
readonly paragraphs?: readonly string[];
|
|
151
|
+
/** An optional label/value panel under the body. */
|
|
152
|
+
readonly facts?: readonly EmailFact[];
|
|
153
|
+
/** At most one CTA. */
|
|
154
|
+
readonly action?: EmailAction;
|
|
155
|
+
/** Small print under the CTA — a deadline, a "you can ignore this". */
|
|
156
|
+
readonly notes?: readonly string[];
|
|
157
|
+
/**
|
|
158
|
+
* The layout's own words, in the recipient's language.
|
|
159
|
+
*
|
|
160
|
+
* REQUIRED, with no default in any language — the copy-portability doctrine.
|
|
161
|
+
* `@12-apps/notifications/email/locales` ships a pack per language and a host
|
|
162
|
+
* passes one BY
|
|
163
|
+
* NAME, which is a decision rather than a silence.
|
|
164
|
+
*/
|
|
165
|
+
readonly chrome: EmailChromeCopy;
|
|
166
|
+
/**
|
|
167
|
+
* The product name in the header and the footer. REQUIRED: a package that
|
|
168
|
+
* defaulted this would sign another company's mail.
|
|
169
|
+
*/
|
|
170
|
+
readonly brand: string;
|
|
171
|
+
/** The recipient's language, for the document's `lang` attribute. */
|
|
172
|
+
readonly locale: string;
|
|
173
|
+
/** Defaults to {@link NEUTRAL_EMAIL_THEME} — see `./theme` for why this one may. */
|
|
174
|
+
readonly theme?: EmailTheme;
|
|
175
|
+
}
|
|
176
|
+
/** A URL as an attribute value, or `#` when it is not one this layout will emit. */
|
|
177
|
+
declare function safeHref(href: string): string;
|
|
178
|
+
/**
|
|
179
|
+
* Render the document as the HTML half of the message.
|
|
180
|
+
*
|
|
181
|
+
* A complete document rather than a fragment: a `<!DOCTYPE>` is what puts
|
|
182
|
+
* Outlook into standards mode, and `x-apple-disable-message-reformatting` is
|
|
183
|
+
* what stops iOS Mail re-flowing the card to the screen width and shrinking the
|
|
184
|
+
* type with it.
|
|
185
|
+
*/
|
|
186
|
+
declare function renderEmailHtml(document: EmailDocument): string;
|
|
187
|
+
/** Both halves plus the subject — what every driver in this repo is handed. */
|
|
188
|
+
interface RenderedEmail {
|
|
189
|
+
readonly subject: string;
|
|
190
|
+
readonly html: string;
|
|
191
|
+
readonly text: string;
|
|
192
|
+
}
|
|
193
|
+
/** Render one document into the message shape `EmailDriver.send` takes. */
|
|
194
|
+
declare function renderEmail(document: EmailDocument): RenderedEmail;
|
|
195
|
+
|
|
196
|
+
export { type EmailDocument as E, NEUTRAL_EMAIL_THEME as N, type RenderedEmail as R, EMAIL_CONTENT_WIDTH as a, EMAIL_FONT_STACK as b, type EmailAction as c, type EmailChromeCopy as d, type EmailFact as e, type EmailTheme as f, renderEmailHtml as g, renderEmail as r, safeHref as s };
|
|
@@ -60,7 +60,16 @@ interface NotificationContent {
|
|
|
60
60
|
body: string;
|
|
61
61
|
/** In-app deep link (a same-origin path such as `/orders/123`). */
|
|
62
62
|
link?: string;
|
|
63
|
-
/**
|
|
63
|
+
/**
|
|
64
|
+
* Structured extras for consumers that want more than text.
|
|
65
|
+
*
|
|
66
|
+
* ONE KEY IS RESERVED: `liveSubject` (`LIVE_SUBJECT_KEY` in `./live`) ties
|
|
67
|
+
* this notification to a live activity, and the WEB_PUSH transport turns it
|
|
68
|
+
* into the tray `tag` that collapses every push about one subject onto a
|
|
69
|
+
* single entry. A host already using that name for something else acquires
|
|
70
|
+
* that behaviour without asking for it — so it is named here, on the field a
|
|
71
|
+
* generator actually writes, and not only where the feature is documented.
|
|
72
|
+
*/
|
|
64
73
|
data?: Record<string, unknown>;
|
|
65
74
|
}
|
|
66
75
|
/** Who receives a notification. `clientId` scopes it to a tenant when set. */
|
|
@@ -189,4 +198,4 @@ interface NotificationLogger {
|
|
|
189
198
|
error(message: string, ...meta: unknown[]): void;
|
|
190
199
|
}
|
|
191
200
|
|
|
192
|
-
export { type DeliveryStatus as D,
|
|
201
|
+
export { type DeliveryStatus as D, type NotificationGenerator as N, type TransportRecipient as T, NOTIFICATION_CHANNELS as a, type NotificationCategory as b, type NotificationChannel as c, type NotificationContent as d, type NotificationEvent as e, type NotificationGenerateContext as f, type NotificationLogger as g, type NotificationRecipient as h, type NotificationTaxonomy as i, type NotificationTransport as j, taxonomyOf as t };
|
package/dist/web-push/index.d.ts
CHANGED
|
@@ -1,5 +1,5 @@
|
|
|
1
|
-
import { c as WebPushSender } from '../web-push-
|
|
2
|
-
import '../types-
|
|
1
|
+
import { c as WebPushSender } from '../web-push-Dnyaha2z.js';
|
|
2
|
+
import '../types-BlqZkCWZ.js';
|
|
3
3
|
|
|
4
4
|
/**
|
|
5
5
|
* `@12-apps/notifications/web-push` — the VAPID sender, behind its own subpath.
|
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
import { g as NotificationLogger,
|
|
1
|
+
import { g as NotificationLogger, d as NotificationContent, j as NotificationTransport } from './types-BlqZkCWZ.js';
|
|
2
2
|
|
|
3
3
|
/**
|
|
4
4
|
* The DRIVER port — the reason a host adds a vendor with a config entry and no
|
|
@@ -98,6 +98,26 @@ interface WebPushMessage {
|
|
|
98
98
|
body: string;
|
|
99
99
|
link: string | null;
|
|
100
100
|
data: Record<string, unknown>;
|
|
101
|
+
/**
|
|
102
|
+
* The OS tray tag, or `null` for an ordinary event.
|
|
103
|
+
*
|
|
104
|
+
* Present for a notification about a LIVE SUBJECT (see `../../live`), and
|
|
105
|
+
* this is the whole of the live feature's reach onto a locked phone: a tag
|
|
106
|
+
* makes the next push about the same subject REPLACE the one already in the
|
|
107
|
+
* tray — silently — instead of stacking under it. Four stages then cost one
|
|
108
|
+
* entry and one buzz, and the entry that remains is the current one.
|
|
109
|
+
*
|
|
110
|
+
* {@link formatWebPush} always sets it, `null` included, so on the WIRE the
|
|
111
|
+
* key is always there and a service worker reads one shape. A worker that
|
|
112
|
+
* ignores it keeps today's behaviour exactly, which is what makes this safe
|
|
113
|
+
* to ship ahead of the workers.
|
|
114
|
+
*
|
|
115
|
+
* OPTIONAL in the type, and only in the type: a host that hand-writes its own
|
|
116
|
+
* `NotificationTransport<WebPushMessage>` — a fixture, a second driver —
|
|
117
|
+
* would otherwise stop compiling on a field it has no way to know about and
|
|
118
|
+
* no reason to set.
|
|
119
|
+
*/
|
|
120
|
+
tag?: string | null;
|
|
101
121
|
}
|
|
102
122
|
interface WebPushDriverDeclaration extends DriverDeclarationBase {
|
|
103
123
|
channel: 'WEB_PUSH';
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@12-apps/notifications",
|
|
3
|
-
"version": "4.
|
|
3
|
+
"version": "4.10.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.",
|
|
@@ -9,6 +9,26 @@
|
|
|
9
9
|
"types": "./dist/index.d.ts",
|
|
10
10
|
"default": "./dist/index.js"
|
|
11
11
|
},
|
|
12
|
+
"./email": {
|
|
13
|
+
"types": "./dist/email/index.d.ts",
|
|
14
|
+
"default": "./dist/email/index.js"
|
|
15
|
+
},
|
|
16
|
+
"./email/locales": {
|
|
17
|
+
"types": "./dist/email/locales.d.ts",
|
|
18
|
+
"default": "./dist/email/locales.js"
|
|
19
|
+
},
|
|
20
|
+
"./email/previews": {
|
|
21
|
+
"types": "./dist/email/previews/index.d.ts",
|
|
22
|
+
"default": "./dist/email/previews/index.js"
|
|
23
|
+
},
|
|
24
|
+
"./email/previews/hono": {
|
|
25
|
+
"types": "./dist/email/previews/hono.d.ts",
|
|
26
|
+
"default": "./dist/email/previews/hono.js"
|
|
27
|
+
},
|
|
28
|
+
"./email/previews/react": {
|
|
29
|
+
"types": "./dist/email/previews/react/index.d.ts",
|
|
30
|
+
"default": "./dist/email/previews/react/index.js"
|
|
31
|
+
},
|
|
12
32
|
"./server": {
|
|
13
33
|
"types": "./dist/server/index.d.ts",
|
|
14
34
|
"default": "./dist/server/index.js"
|
|
@@ -51,7 +71,7 @@
|
|
|
51
71
|
"prisma:sync:check": "node scripts/sync-notifications-schema.mjs --check"
|
|
52
72
|
},
|
|
53
73
|
"dependencies": {
|
|
54
|
-
"@12-apps/ui": "^6.
|
|
74
|
+
"@12-apps/ui": "^6.17.0"
|
|
55
75
|
},
|
|
56
76
|
"peerDependencies": {
|
|
57
77
|
"@12-apps/wiring": ">=1.3.0",
|
|
@@ -0,0 +1,8 @@
|
|
|
1
|
+
import type { EmailChromeCopy } from './template';
|
|
2
|
+
|
|
3
|
+
/** US English — the twin that makes {@link PT_BR_EMAIL_CHROME} a choice. */
|
|
4
|
+
export const EN_US_EMAIL_CHROME: EmailChromeCopy = {
|
|
5
|
+
fallbackHint: 'If the button above does not work, copy and paste this address into your browser:',
|
|
6
|
+
automated: 'This is an automated message. Please do not reply to this email.',
|
|
7
|
+
tagline: (brand) => `Sent by ${brand}.`,
|
|
8
|
+
};
|
|
@@ -0,0 +1,21 @@
|
|
|
1
|
+
import type { EmailChromeCopy } from './template';
|
|
2
|
+
|
|
3
|
+
/**
|
|
4
|
+
* Brazilian Portuguese, as a NAMED pack.
|
|
5
|
+
*
|
|
6
|
+
* A host passes this by name — `chrome: PT_BR_EMAIL_CHROME`, or
|
|
7
|
+
* `localeCopy(EMAIL_CHROME)` for a host whose readers do not share one
|
|
8
|
+
* language. It is never a default: a pack a host CHOOSES is a decision in a
|
|
9
|
+
* diff, and a pack a package applies silently is another product's voice in
|
|
10
|
+
* somebody's inbox.
|
|
11
|
+
*
|
|
12
|
+
* The tagline names the brand and stops. It is deliberately not a description
|
|
13
|
+
* of what the product does — that sentence is the host's, it belongs to a
|
|
14
|
+
* vocabulary this package cannot know, and a package that guessed one would be
|
|
15
|
+
* putting words in every adopter's footer.
|
|
16
|
+
*/
|
|
17
|
+
export const PT_BR_EMAIL_CHROME: EmailChromeCopy = {
|
|
18
|
+
fallbackHint: 'Se o botão acima não funcionar, copie e cole este endereço no seu navegador:',
|
|
19
|
+
automated: 'Esta é uma mensagem automática. Não responda a este e-mail.',
|
|
20
|
+
tagline: (brand) => `Enviado por ${brand}.`,
|
|
21
|
+
};
|