@12-apps/notifications 4.7.1 → 4.9.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (96) hide show
  1. package/ADOPTING.md +145 -0
  2. package/README.md +65 -0
  3. package/dist/catalog-BOVZ2b-b.d.ts +132 -0
  4. package/dist/{chunk-XE7HZVMH.js → chunk-BGI4BQ66.js} +1 -1
  5. package/dist/chunk-BGI4BQ66.js.map +1 -0
  6. package/dist/chunk-EKUSNUBT.js +222 -0
  7. package/dist/chunk-EKUSNUBT.js.map +1 -0
  8. package/dist/chunk-FTFLU6KH.js +114 -0
  9. package/dist/chunk-FTFLU6KH.js.map +1 -0
  10. package/dist/{chunk-HHMRCMQU.js → chunk-GGAZOLNN.js} +2 -2
  11. package/dist/{chunk-CUZW62JS.js → chunk-KU4FVOFA.js} +2 -2
  12. package/dist/{chunk-WHBMPHQE.js → chunk-KZG7QGFA.js} +28 -6
  13. package/dist/chunk-KZG7QGFA.js.map +1 -0
  14. package/dist/{chunk-MMLV4EZT.js → chunk-M5XTROYM.js} +2 -2
  15. package/dist/chunk-ZFIYBNZ7.js +502 -0
  16. package/dist/chunk-ZFIYBNZ7.js.map +1 -0
  17. package/dist/{create-api-notifications-B3u6Kx3x.d.ts → create-api-notifications-B-VMezVB.d.ts} +43 -4
  18. package/dist/{create-web-notifications-BHCzaU2y.d.ts → create-web-notifications-B-xQ6R0B.d.ts} +2 -2
  19. package/dist/email/index.d.ts +26 -0
  20. package/dist/email/index.js +20 -0
  21. package/dist/email/index.js.map +1 -0
  22. package/dist/email/locales.d.ts +43 -0
  23. package/dist/email/locales.js +29 -0
  24. package/dist/email/locales.js.map +1 -0
  25. package/dist/email/previews/hono.d.ts +46 -0
  26. package/dist/email/previews/hono.js +33 -0
  27. package/dist/email/previews/hono.js.map +1 -0
  28. package/dist/email/previews/index.d.ts +67 -0
  29. package/dist/email/previews/index.js +12 -0
  30. package/dist/email/previews/index.js.map +1 -0
  31. package/dist/email/previews/react/index.d.ts +43 -0
  32. package/dist/email/previews/react/index.js +58 -0
  33. package/dist/email/previews/react/index.js.map +1 -0
  34. package/dist/{generators-B9xt3sRh.d.ts → generators-BsuXe8Mp.d.ts} +1 -1
  35. package/dist/hono/index.d.ts +6 -5
  36. package/dist/hono/index.js +4 -3
  37. package/dist/hono/index.js.map +1 -1
  38. package/dist/index.d.ts +3 -3
  39. package/dist/index.js +5 -5
  40. package/dist/{jobs-BDZ7aGHV.d.ts → jobs-D64B_bg7.d.ts} +1 -1
  41. package/dist/manifest/index.d.ts +39 -1
  42. package/dist/manifest/index.js +9 -0
  43. package/dist/manifest/index.js.map +1 -1
  44. package/dist/manifest/server.d.ts +32 -7
  45. package/dist/manifest/server.js +15 -4
  46. package/dist/manifest/server.js.map +1 -1
  47. package/dist/manifest/web.d.ts +18 -4
  48. package/dist/manifest/web.js +9 -1
  49. package/dist/manifest/web.js.map +1 -1
  50. package/dist/{preferences-screen-IOW6Y2H2.js → preferences-screen-V3ZBWP7A.js} +2 -2
  51. package/dist/preview-screen-DYJRAnAY.d.ts +83 -0
  52. package/dist/react/index.d.ts +4 -4
  53. package/dist/react/index.js +10 -10
  54. package/dist/server/index.d.ts +8 -7
  55. package/dist/server/index.js +5 -4
  56. package/dist/template-Dlt5S54z.d.ts +196 -0
  57. package/dist/{types-CXLAG3UU.d.ts → types-BFmAY0P5.d.ts} +39 -2
  58. package/dist/web-push/index.d.ts +2 -2
  59. package/dist/{web-push-Cs14Wp9u.d.ts → web-push-D-ysQd5h.d.ts} +1 -1
  60. package/dist/{wire-6dzyfDE7.d.ts → wire-_coV3tJk.d.ts} +1 -1
  61. package/package.json +23 -3
  62. package/src/email/chrome.en-US.ts +8 -0
  63. package/src/email/chrome.pt-BR.ts +21 -0
  64. package/src/email/index.ts +43 -0
  65. package/src/email/locales.ts +29 -0
  66. package/src/email/previews/catalog.ts +208 -0
  67. package/src/email/previews/hono.ts +69 -0
  68. package/src/email/previews/index.ts +26 -0
  69. package/src/email/previews/react/copy.en-US.ts +24 -0
  70. package/src/email/previews/react/copy.pt-BR.ts +24 -0
  71. package/src/email/previews/react/copy.ts +42 -0
  72. package/src/email/previews/react/index.tsx +14 -0
  73. package/src/email/previews/react/loadable.tsx +83 -0
  74. package/src/email/previews/react/message-list.tsx +133 -0
  75. package/src/email/previews/react/message-view.tsx +167 -0
  76. package/src/email/previews/react/preview-screen.tsx +342 -0
  77. package/src/email/previews/react/transport.ts +60 -0
  78. package/src/email/previews/routes.ts +111 -0
  79. package/src/email/template.ts +365 -0
  80. package/src/email/text.ts +59 -0
  81. package/src/email/theme.ts +77 -0
  82. package/src/index.ts +1 -0
  83. package/src/manifest/index.ts +37 -0
  84. package/src/manifest/server.ts +23 -0
  85. package/src/manifest/web.ts +13 -0
  86. package/src/server/db.ts +12 -1
  87. package/src/server/dispatch.ts +4 -0
  88. package/src/server/router.ts +17 -1
  89. package/src/server/transports/email.ts +62 -0
  90. package/src/types.ts +39 -1
  91. package/dist/chunk-WHBMPHQE.js.map +0 -1
  92. package/dist/chunk-XE7HZVMH.js.map +0 -1
  93. /package/dist/{chunk-HHMRCMQU.js.map → chunk-GGAZOLNN.js.map} +0 -0
  94. /package/dist/{chunk-CUZW62JS.js.map → chunk-KU4FVOFA.js.map} +0 -0
  95. /package/dist/{chunk-MMLV4EZT.js.map → chunk-M5XTROYM.js.map} +0 -0
  96. /package/dist/{preferences-screen-IOW6Y2H2.js.map → preferences-screen-V3ZBWP7A.js.map} +0 -0
@@ -1 +1 @@
1
- {"version":3,"sources":["../../src/manifest/index.ts"],"sourcesContent":["/**\n * `@12-apps/notifications/manifest` — the SHARED wiring manifest.\n *\n * Identity, the Prisma contribution (the four owned models) and the runtime\n * inventory: `http` on the server. Three narrowings are deliberate:\n *\n * - **No `notifications` blueprints.** This package is the notification\n * MECHANISM — the inbox, the preference matrix, the transports, the retry\n * sweep — not an author of alerts. The blueprints belong to whichever\n * package raises the alert (`@12-apps/product-research`'s budget warning\n * ships as a factory over host copy for exactly this reason), and this one\n * takes them through the `generators` seam at mount.\n * - **No `email` capability.** The transports are host-supplied config\n * (`config.transports`, `config.drivers`), so what would be declared here\n * is a seam the host already fills — and declaring it would oblige an\n * adopter to bind a mailer this package never owns.\n *\n * ON THE `jobs` DECLARATION. The dispatch fast path and the retry sweep are\n * this package's own cadence decisions — attempts, backoff, the five-minute\n * tick, the single-flight lease — and they were host code in every adopting\n * host, restated by hand from this package's docstrings. That is the\n * `paymentsJobBlueprints()` incident's shape exactly: a mechanism a host must\n * remember to schedule is a mechanism most hosts silently do not have, and the\n * one host that DID write them wrote them correctly only because someone read\n * the source. `./server`'s `NOTIFICATIONS_JOBS` declares both; a host with no\n * worker declines the capability in writing and the report says so.\n *\n * ON THE `web` INVENTORY, which this manifest used to narrow away. The reason\n * given — that listing it would oblige every SERVER host to answer for a React\n * surface it never mounts — is not how the consumer behaves: a capability\n * declared for the OTHER runtime is reported `out-of-scope`, and only an\n * applicable, unanswered one is `unbound`. So the narrowing protected nothing\n * and hid Bell, Panel and Preferences from every adopting host, which is why\n * the origin host hand-duplicated two of the three.\n *\n * ON THE `db` DECLARATION. The origin host already composes\n * `prisma/notifications.prisma` into its schema — but by STRUCTURAL\n * DISCOVERY, the assembler's fallback for a package that declares nothing.\n * That fallback is why the gap was invisible: four tables reached a host's\n * database with no declaration behind them, and the contract's whole claim\n * is that a package's models arrive because it said so. Declaring changes no\n * host behaviour (the assembler reads the declaration where it used to scan)\n * and closes the one case where composition was happening by accident.\n *\n * `@12-apps/wiring` is a TYPE-ONLY devDependency (the report-builder move):\n * the manifest is a plain `satisfies`-checked value, and the producer\n * factories' runtime assertions run in this package's own test suite.\n */\n\nimport type { PackageManifest } from '@12-apps/wiring';\n\nexport const notificationsManifest = {\n name: '@12-apps/notifications',\n contract: 1,\n db: { partial: 'prisma/notifications.prisma', migrations: 'prisma/migrations' },\n /**\n * Mandatory for runtime manifests since wiring 1.3.0: a delivery that\n * exhausts its attempts files under `notifications`, not nowhere.\n */\n observability: { namespace: 'notifications' },\n server: ['http', 'jobs'],\n web: ['surface'],\n} as const satisfies PackageManifest;\n"],"mappings":";;;AAmDO,IAAM,wBAAwB;AAAA,EACnC,MAAM;AAAA,EACN,UAAU;AAAA,EACV,IAAI,EAAE,SAAS,+BAA+B,YAAY,oBAAoB;AAAA;AAAA;AAAA;AAAA;AAAA,EAK9E,eAAe,EAAE,WAAW,gBAAgB;AAAA,EAC5C,QAAQ,CAAC,QAAQ,MAAM;AAAA,EACvB,KAAK,CAAC,SAAS;AACjB;","names":[]}
1
+ {"version":3,"sources":["../../src/manifest/index.ts"],"sourcesContent":["/**\n * `@12-apps/notifications/manifest` — the SHARED wiring manifest.\n *\n * Identity, the Prisma contribution (the four owned models) and the runtime\n * inventory: `http` on the server. Three narrowings are deliberate:\n *\n * - **No `notifications` blueprints.** This package is the notification\n * MECHANISM — the inbox, the preference matrix, the transports, the retry\n * sweep — not an author of alerts. The blueprints belong to whichever\n * package raises the alert (`@12-apps/product-research`'s budget warning\n * ships as a factory over host copy for exactly this reason), and this one\n * takes them through the `generators` seam at mount.\n * - **No `email` capability.** The transports are host-supplied config\n * (`config.transports`, `config.drivers`), so what would be declared here\n * is a seam the host already fills — and declaring it would oblige an\n * adopter to bind a mailer this package never owns.\n *\n * ON THE `jobs` DECLARATION. The dispatch fast path and the retry sweep are\n * this package's own cadence decisions — attempts, backoff, the five-minute\n * tick, the single-flight lease — and they were host code in every adopting\n * host, restated by hand from this package's docstrings. That is the\n * `paymentsJobBlueprints()` incident's shape exactly: a mechanism a host must\n * remember to schedule is a mechanism most hosts silently do not have, and the\n * one host that DID write them wrote them correctly only because someone read\n * the source. `./server`'s `NOTIFICATIONS_JOBS` declares both; a host with no\n * worker declines the capability in writing and the report says so.\n *\n * ON THE `web` INVENTORY, which this manifest used to narrow away. The reason\n * given — that listing it would oblige every SERVER host to answer for a React\n * surface it never mounts — is not how the consumer behaves: a capability\n * declared for the OTHER runtime is reported `out-of-scope`, and only an\n * applicable, unanswered one is `unbound`. So the narrowing protected nothing\n * and hid Bell, Panel and Preferences from every adopting host, which is why\n * the origin host hand-duplicated two of the three.\n *\n * ON THE `db` DECLARATION. The origin host already composes\n * `prisma/notifications.prisma` into its schema — but by STRUCTURAL\n * DISCOVERY, the assembler's fallback for a package that declares nothing.\n * That fallback is why the gap was invisible: four tables reached a host's\n * database with no declaration behind them, and the contract's whole claim\n * is that a package's models arrive because it said so. Declaring changes no\n * host behaviour (the assembler reads the declaration where it used to scan)\n * and closes the one case where composition was happening by accident.\n *\n * `@12-apps/wiring` is a TYPE-ONLY devDependency (the report-builder move):\n * the manifest is a plain `satisfies`-checked value, and the producer\n * factories' runtime assertions run in this package's own test suite.\n */\n\nimport type { PackageManifest } from '@12-apps/wiring';\n\nexport const notificationsManifest = {\n name: '@12-apps/notifications',\n contract: 1,\n db: { partial: 'prisma/notifications.prisma', migrations: 'prisma/migrations' },\n /**\n * Mandatory for runtime manifests since wiring 1.3.0: a delivery that\n * exhausts its attempts files under `notifications`, not nowhere.\n */\n observability: { namespace: 'notifications' },\n server: ['http', 'jobs'],\n web: ['surface'],\n} as const satisfies PackageManifest;\n\n/**\n * The e-mail PREVIEW console — a second manifest, from the same package.\n *\n * ## Why a second manifest and not two more keys on the first\n *\n * `notificationsManifest` has already spent both slots this surface needs:\n * `http` is the account inbox at `/api/account`, `surface` is the bell and the\n * preference matrix. A capability is singular by the contract's shape, so a\n * package with two genuinely different surfaces declares two manifests — which\n * is exactly what `@12-apps/auth` does with `@12-apps/auth-platform`, and for\n * the same reason it gives: those two switches turn a sign-in method off for\n * EVERYBODY, so they do not belong behind the same gate as \"reset my password\".\n *\n * The split here is the same one. The inbox ships to every signed-in user; this\n * console publishes the product's whole transactional-mail inventory and the\n * exact wording and link shape of its verification and reset mails, which is\n * the reference someone writing a convincing phishing mail would want. Two\n * manifests keep that expressible: a host mounts the inbox and DECLINES the\n * console, in writing, rather than silently getting both behind one gate.\n *\n * ## What it deliberately does not declare\n *\n * No `db` — a catalogue is DERIVED from what a host already sends, so an\n * adopter mounts it without touching its schema. No `notifications`, because\n * this surface authors no alert. And no `email`: that capability is a DELIVERY\n * port, and this manifest renders and never sends. The delivery port is the\n * OTHER half of this package, and it stays where it is.\n */\nexport const notificationEmailPreviewsManifest = {\n name: '@12-apps/notifications-email-previews',\n contract: 1,\n /** A refusal to render a preview files under its own namespace, not nowhere. */\n observability: { namespace: 'email-previews' },\n server: ['http'],\n web: ['surface'],\n} as const satisfies PackageManifest;\n"],"mappings":";;;AAmDO,IAAM,wBAAwB;AAAA,EACnC,MAAM;AAAA,EACN,UAAU;AAAA,EACV,IAAI,EAAE,SAAS,+BAA+B,YAAY,oBAAoB;AAAA;AAAA;AAAA;AAAA;AAAA,EAK9E,eAAe,EAAE,WAAW,gBAAgB;AAAA,EAC5C,QAAQ,CAAC,QAAQ,MAAM;AAAA,EACvB,KAAK,CAAC,SAAS;AACjB;AA8BO,IAAM,oCAAoC;AAAA,EAC/C,MAAM;AAAA,EACN,UAAU;AAAA;AAAA,EAEV,eAAe,EAAE,WAAW,iBAAiB;AAAA,EAC7C,QAAQ,CAAC,MAAM;AAAA,EACf,KAAK,CAAC,SAAS;AACjB;","names":[]}
@@ -1,11 +1,14 @@
1
+ import { EmailPreviewRoute } from '../email/previews/index.js';
1
2
  import * as _12_apps_wiring from '@12-apps/wiring';
2
3
  import { WireRequest } from '@12-apps/wiring';
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
- import '../generators-B9xt3sRh.js';
6
- import '../types-CXLAG3UU.js';
7
- import '../wire-6dzyfDE7.js';
8
- import '../web-push-Cs14Wp9u.js';
4
+ import { N as NotificationsServerConfig, A as ApiNotifications, b as NotificationsRoute } from '../create-api-notifications-B-VMezVB.js';
5
+ import { N as NotificationsJobDeps } from '../jobs-D64B_bg7.js';
6
+ import { E as EmailPreviewsConfig } from '../catalog-BOVZ2b-b.js';
7
+ import '../generators-BsuXe8Mp.js';
8
+ import '../types-BFmAY0P5.js';
9
+ import '../wire-_coV3tJk.js';
10
+ import '../web-push-D-ysQd5h.js';
11
+ import '../template-Dlt5S54z.js';
9
12
 
10
13
  /** One `NotificationsRoute` as the wiring contract reads it. */
11
14
  declare function asWireRoute(route: NotificationsRoute): {
@@ -41,5 +44,27 @@ declare const notificationsServerManifest: {
41
44
  };
42
45
  };
43
46
  };
47
+ /**
48
+ * The preview console's server half — the two endpoints over the catalogue.
49
+ *
50
+ * A CONSTANT, not a factory. `http.create(config)` receives whatever the host
51
+ * bound at adoption, and "which messages exist" is precisely a host's binding:
52
+ * a package cannot know that a product sends a "your quota is exhausted"
53
+ * notice, let alone what data it renders from. Writing `EmailPreviewsConfig`
54
+ * as a factory argument instead would move that decision out of
55
+ * `bindings.http`, where `assemble()` can report on it, into a call the report
56
+ * never sees.
57
+ *
58
+ * The routes carry `kind: 'authenticated'` — see `../email/previews/routes`
59
+ * for why the descriptor states a posture it cannot itself enforce.
60
+ */
61
+ declare const notificationEmailPreviewsServerManifest: {
62
+ readonly name: "@12-apps/notifications-email-previews";
63
+ readonly http: {
64
+ readonly create: (config: EmailPreviewsConfig) => {
65
+ routes: EmailPreviewRoute[];
66
+ };
67
+ };
68
+ };
44
69
 
45
- export { createWireApiNotifications, notificationsServerManifest };
70
+ export { createWireApiNotifications, notificationEmailPreviewsServerManifest, notificationsServerManifest };
@@ -1,12 +1,16 @@
1
+ import {
2
+ emailPreviewRoutes
3
+ } from "../chunk-FTFLU6KH.js";
1
4
  import {
2
5
  NOTIFICATIONS_JOBS
3
- } from "../chunk-CUZW62JS.js";
6
+ } from "../chunk-KU4FVOFA.js";
4
7
  import {
5
8
  createApiNotifications
6
- } from "../chunk-WHBMPHQE.js";
7
- import "../chunk-HHMRCMQU.js";
8
- import "../chunk-XE7HZVMH.js";
9
+ } from "../chunk-KZG7QGFA.js";
10
+ import "../chunk-GGAZOLNN.js";
9
11
  import "../chunk-M2TVBVH2.js";
12
+ import "../chunk-BGI4BQ66.js";
13
+ import "../chunk-EKUSNUBT.js";
10
14
  import {
11
15
  __name
12
16
  } from "../chunk-7QVYU63E.js";
@@ -51,8 +55,15 @@ var notificationsServerManifest = {
51
55
  */
52
56
  jobs: NOTIFICATIONS_JOBS
53
57
  };
58
+ var notificationEmailPreviewsServerManifest = {
59
+ name: "@12-apps/notifications-email-previews",
60
+ http: {
61
+ create: /* @__PURE__ */ __name((config) => ({ routes: emailPreviewRoutes(config) }), "create")
62
+ }
63
+ };
54
64
  export {
55
65
  createWireApiNotifications,
66
+ notificationEmailPreviewsServerManifest,
56
67
  notificationsServerManifest
57
68
  };
58
69
  //# sourceMappingURL=server.js.map
@@ -1 +1 @@
1
- {"version":3,"sources":["../../src/manifest/server.ts"],"sourcesContent":["/**\n * `@12-apps/notifications/manifest/server` — the server capabilities.\n *\n * `http.create` wraps `createApiNotifications` in a WIRE VIEW, and the reason\n * is one field. `NotificationsRequest` carries `headers` — the contract's\n * `WireRequest` does not, because headers are the adapter's business\n * everywhere else — and exactly one descriptor reads it: push-subscribe takes\n * `user-agent` as the DEVICE HINT it labels a subscription with. Without the\n * view the field would simply be absent at runtime while still type-checking,\n * and every saved device would come back unnamed: a silent quality loss, the\n * failure mode the wiring contract exists to convert into a loud one.\n *\n * So the view derives `headers` from the raw request the contract already\n * carries for the handlers `params`/`query`/`body` cannot serve. A host whose\n * adapter leaves `request` unset still gets a working surface — every route\n * answers, the subscription saves — with an unnamed device, which is the\n * honest degradation for a hint. `@12-apps/notifications/hono` populates it.\n *\n * Everything else rides beside the mapped routes on the aggregate unchanged:\n * `notify`, `notifyByPermission`, `dispatchDeliveries`, `drainPending`, the\n * three stores, `registerGenerator` and the transports registry. A host still\n * calls those directly — being mounted does not make the emit front door stop\n * being a library.\n */\n\nimport type { AnyServerManifest, WireRequest } from '@12-apps/wiring';\n\nimport {\n createApiNotifications,\n NOTIFICATIONS_JOBS,\n type ApiNotifications,\n type NotificationsRoute,\n type NotificationsServerConfig,\n} from '../server';\n\n/** The header names this surface reads — the device hint, and nothing else. */\nconst READ_HEADERS = ['user-agent'] as const;\n\n/** The headers the package expects, taken off the raw request when there is one. */\nfunction headersOf(request: WireRequest<never>): Record<string, string | undefined> {\n const raw = request.request;\n if (!raw) return {};\n return Object.fromEntries(\n READ_HEADERS.map((name) => [name, raw.headers.get(name) ?? undefined]),\n );\n}\n\n/** One `NotificationsRoute` as the wiring contract reads it. */\nfunction asWireRoute(route: NotificationsRoute): {\n method: NotificationsRoute['method'];\n path: string;\n handle(request: WireRequest<never>): Promise<{ status: number; body: unknown }>;\n} {\n return {\n method: route.method,\n path: route.path,\n handle: (request) =>\n route.handle({\n actor: request.actor,\n params: request.params,\n query: request.query,\n body: request.body,\n headers: headersOf(request),\n }),\n };\n}\n\n/** `createApiNotifications`, its routes re-shaped for the aggregate. */\nexport function createWireApiNotifications(\n config: NotificationsServerConfig,\n): Omit<ApiNotifications, 'routes'> & { routes: ReturnType<typeof asWireRoute>[] } {\n const api = createApiNotifications(config);\n return { ...api, routes: api.routes.map(asWireRoute) };\n}\n\nexport const notificationsServerManifest = {\n name: '@12-apps/notifications',\n http: { create: createWireApiNotifications },\n /**\n * The dispatch fast path and the retry sweep, with their cadence. The host\n * binds `{ dispatchDeliveries, drainPending }` off its own mount — the two\n * methods the aggregate already hands it — and deletes the hand-rolled\n * copies. See `../server/jobs` for why the numbers are the package's.\n */\n jobs: NOTIFICATIONS_JOBS,\n} as const satisfies AnyServerManifest;\n"],"mappings":";;;;;;;;;;;;;;AAoCA,IAAM,eAAe,CAAC,YAAY;AAGlC,SAAS,UAAU,SAAiE;AAClF,QAAM,MAAM,QAAQ;AACpB,MAAI,CAAC,IAAK,QAAO,CAAC;AAClB,SAAO,OAAO;AAAA,IACZ,aAAa,IAAI,CAAC,SAAS,CAAC,MAAM,IAAI,QAAQ,IAAI,IAAI,KAAK,MAAS,CAAC;AAAA,EACvE;AACF;AANS;AAST,SAAS,YAAY,OAInB;AACA,SAAO;AAAA,IACL,QAAQ,MAAM;AAAA,IACd,MAAM,MAAM;AAAA,IACZ,QAAQ,wBAAC,YACP,MAAM,OAAO;AAAA,MACX,OAAO,QAAQ;AAAA,MACf,QAAQ,QAAQ;AAAA,MAChB,OAAO,QAAQ;AAAA,MACf,MAAM,QAAQ;AAAA,MACd,SAAS,UAAU,OAAO;AAAA,IAC5B,CAAC,GAPK;AAAA,EAQV;AACF;AAjBS;AAoBF,SAAS,2BACd,QACiF;AACjF,QAAM,MAAM,uBAAuB,MAAM;AACzC,SAAO,EAAE,GAAG,KAAK,QAAQ,IAAI,OAAO,IAAI,WAAW,EAAE;AACvD;AALgB;AAOT,IAAM,8BAA8B;AAAA,EACzC,MAAM;AAAA,EACN,MAAM,EAAE,QAAQ,2BAA2B;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA,EAO3C,MAAM;AACR;","names":[]}
1
+ {"version":3,"sources":["../../src/manifest/server.ts"],"sourcesContent":["/**\n * `@12-apps/notifications/manifest/server` — the server capabilities.\n *\n * `http.create` wraps `createApiNotifications` in a WIRE VIEW, and the reason\n * is one field. `NotificationsRequest` carries `headers` — the contract's\n * `WireRequest` does not, because headers are the adapter's business\n * everywhere else — and exactly one descriptor reads it: push-subscribe takes\n * `user-agent` as the DEVICE HINT it labels a subscription with. Without the\n * view the field would simply be absent at runtime while still type-checking,\n * and every saved device would come back unnamed: a silent quality loss, the\n * failure mode the wiring contract exists to convert into a loud one.\n *\n * So the view derives `headers` from the raw request the contract already\n * carries for the handlers `params`/`query`/`body` cannot serve. A host whose\n * adapter leaves `request` unset still gets a working surface — every route\n * answers, the subscription saves — with an unnamed device, which is the\n * honest degradation for a hint. `@12-apps/notifications/hono` populates it.\n *\n * Everything else rides beside the mapped routes on the aggregate unchanged:\n * `notify`, `notifyByPermission`, `dispatchDeliveries`, `drainPending`, the\n * three stores, `registerGenerator` and the transports registry. A host still\n * calls those directly — being mounted does not make the emit front door stop\n * being a library.\n */\n\nimport type { AnyServerManifest, WireRequest } from '@12-apps/wiring';\n\nimport { emailPreviewRoutes } from '../email/previews/routes';\nimport type { EmailPreviewsConfig } from '../email/previews/catalog';\nimport {\n createApiNotifications,\n NOTIFICATIONS_JOBS,\n type ApiNotifications,\n type NotificationsRoute,\n type NotificationsServerConfig,\n} from '../server';\n\n/** The header names this surface reads — the device hint, and nothing else. */\nconst READ_HEADERS = ['user-agent'] as const;\n\n/** The headers the package expects, taken off the raw request when there is one. */\nfunction headersOf(request: WireRequest<never>): Record<string, string | undefined> {\n const raw = request.request;\n if (!raw) return {};\n return Object.fromEntries(\n READ_HEADERS.map((name) => [name, raw.headers.get(name) ?? undefined]),\n );\n}\n\n/** One `NotificationsRoute` as the wiring contract reads it. */\nfunction asWireRoute(route: NotificationsRoute): {\n method: NotificationsRoute['method'];\n path: string;\n handle(request: WireRequest<never>): Promise<{ status: number; body: unknown }>;\n} {\n return {\n method: route.method,\n path: route.path,\n handle: (request) =>\n route.handle({\n actor: request.actor,\n params: request.params,\n query: request.query,\n body: request.body,\n headers: headersOf(request),\n }),\n };\n}\n\n/** `createApiNotifications`, its routes re-shaped for the aggregate. */\nexport function createWireApiNotifications(\n config: NotificationsServerConfig,\n): Omit<ApiNotifications, 'routes'> & { routes: ReturnType<typeof asWireRoute>[] } {\n const api = createApiNotifications(config);\n return { ...api, routes: api.routes.map(asWireRoute) };\n}\n\nexport const notificationsServerManifest = {\n name: '@12-apps/notifications',\n http: { create: createWireApiNotifications },\n /**\n * The dispatch fast path and the retry sweep, with their cadence. The host\n * binds `{ dispatchDeliveries, drainPending }` off its own mount — the two\n * methods the aggregate already hands it — and deletes the hand-rolled\n * copies. See `../server/jobs` for why the numbers are the package's.\n */\n jobs: NOTIFICATIONS_JOBS,\n} as const satisfies AnyServerManifest;\n\n/**\n * The preview console's server half — the two endpoints over the catalogue.\n *\n * A CONSTANT, not a factory. `http.create(config)` receives whatever the host\n * bound at adoption, and \"which messages exist\" is precisely a host's binding:\n * a package cannot know that a product sends a \"your quota is exhausted\"\n * notice, let alone what data it renders from. Writing `EmailPreviewsConfig`\n * as a factory argument instead would move that decision out of\n * `bindings.http`, where `assemble()` can report on it, into a call the report\n * never sees.\n *\n * The routes carry `kind: 'authenticated'` — see `../email/previews/routes`\n * for why the descriptor states a posture it cannot itself enforce.\n */\nexport const notificationEmailPreviewsServerManifest = {\n name: '@12-apps/notifications-email-previews',\n http: {\n create: (config: EmailPreviewsConfig) => ({ routes: emailPreviewRoutes(config) }),\n },\n} as const satisfies AnyServerManifest;\n"],"mappings":";;;;;;;;;;;;;;;;;;AAsCA,IAAM,eAAe,CAAC,YAAY;AAGlC,SAAS,UAAU,SAAiE;AAClF,QAAM,MAAM,QAAQ;AACpB,MAAI,CAAC,IAAK,QAAO,CAAC;AAClB,SAAO,OAAO;AAAA,IACZ,aAAa,IAAI,CAAC,SAAS,CAAC,MAAM,IAAI,QAAQ,IAAI,IAAI,KAAK,MAAS,CAAC;AAAA,EACvE;AACF;AANS;AAST,SAAS,YAAY,OAInB;AACA,SAAO;AAAA,IACL,QAAQ,MAAM;AAAA,IACd,MAAM,MAAM;AAAA,IACZ,QAAQ,wBAAC,YACP,MAAM,OAAO;AAAA,MACX,OAAO,QAAQ;AAAA,MACf,QAAQ,QAAQ;AAAA,MAChB,OAAO,QAAQ;AAAA,MACf,MAAM,QAAQ;AAAA,MACd,SAAS,UAAU,OAAO;AAAA,IAC5B,CAAC,GAPK;AAAA,EAQV;AACF;AAjBS;AAoBF,SAAS,2BACd,QACiF;AACjF,QAAM,MAAM,uBAAuB,MAAM;AACzC,SAAO,EAAE,GAAG,KAAK,QAAQ,IAAI,OAAO,IAAI,WAAW,EAAE;AACvD;AALgB;AAOT,IAAM,8BAA8B;AAAA,EACzC,MAAM;AAAA,EACN,MAAM,EAAE,QAAQ,2BAA2B;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA,EAO3C,MAAM;AACR;AAgBO,IAAM,0CAA0C;AAAA,EACrD,MAAM;AAAA,EACN,MAAM;AAAA,IACJ,QAAQ,wBAAC,YAAiC,EAAE,QAAQ,mBAAmB,MAAM,EAAE,IAAvE;AAAA,EACV;AACF;","names":[]}
@@ -1,7 +1,8 @@
1
- import { c as createWebNotifications } from '../create-web-notifications-BHCzaU2y.js';
1
+ import { c as createEmailPreviewScreen } from '../preview-screen-DYJRAnAY.js';
2
+ import { c as createWebNotifications } from '../create-web-notifications-B-xQ6R0B.js';
2
3
  import 'react';
3
- import '../wire-6dzyfDE7.js';
4
- import '../types-CXLAG3UU.js';
4
+ import '../wire-_coV3tJk.js';
5
+ import '../types-BFmAY0P5.js';
5
6
 
6
7
  /**
7
8
  * `@12-apps/notifications/manifest/web` — the web capabilities.
@@ -47,5 +48,18 @@ declare const notificationsWebManifest: {
47
48
  readonly create: typeof createWebNotifications;
48
49
  };
49
50
  };
51
+ /**
52
+ * The preview console's web half.
53
+ *
54
+ * One screen, named `page`, because a screen's NAME is what an area row
55
+ * resolves against — a surface that was itself the component is the shape that
56
+ * made an area row resolve to `undefined` in `@12-apps/auth` for a release.
57
+ */
58
+ declare const notificationEmailPreviewsWebManifest: {
59
+ readonly name: "@12-apps/notifications-email-previews";
60
+ readonly surface: {
61
+ readonly create: typeof createEmailPreviewScreen;
62
+ };
63
+ };
50
64
 
51
- export { notificationsWebManifest };
65
+ export { notificationEmailPreviewsWebManifest, notificationsWebManifest };
@@ -1,7 +1,10 @@
1
1
  import {
2
2
  createWebNotifications
3
- } from "../chunk-MMLV4EZT.js";
3
+ } from "../chunk-M5XTROYM.js";
4
4
  import "../chunk-BW723CX2.js";
5
+ import {
6
+ createEmailPreviewScreen
7
+ } from "../chunk-ZFIYBNZ7.js";
5
8
  import "../chunk-M2TVBVH2.js";
6
9
  import "../chunk-7QVYU63E.js";
7
10
 
@@ -10,7 +13,12 @@ var notificationsWebManifest = {
10
13
  name: "@12-apps/notifications",
11
14
  surface: { create: createWebNotifications }
12
15
  };
16
+ var notificationEmailPreviewsWebManifest = {
17
+ name: "@12-apps/notifications-email-previews",
18
+ surface: { create: createEmailPreviewScreen }
19
+ };
13
20
  export {
21
+ notificationEmailPreviewsWebManifest,
14
22
  notificationsWebManifest
15
23
  };
16
24
  //# sourceMappingURL=web.js.map
@@ -1 +1 @@
1
- {"version":3,"sources":["../../src/manifest/web.ts"],"sourcesContent":["/**\n * `@12-apps/notifications/manifest/web` — the web capabilities.\n *\n * `surface.create` IS `createWebNotifications`, unchanged: the bell, the inbox\n * slide-over, the preferences screen and the bound store, built once per\n * adoption by the consumer's binder (the members are component TYPES, so\n * rebuilding per render unmounts the panel mid-interaction — the memoisation\n * rule every hand wiring carries as a comment today).\n *\n * ## Why this manifest exists now\n *\n * It was narrowed away with a reason that reads plausibly and is FALSE:\n * \"listing `web` would oblige every server host adopting this manifest to\n * answer for a React surface it never mounts.\" The consumer does not work\n * that way. A capability declared for the other runtime is reported\n * `out-of-scope` — \"a web host answers for this\" — and `assemble()` returns\n * fine; only a capability applicable to THIS runtime and unanswered is\n * `unbound`. `wiring`'s own fixture package declares both halves and its\n * server-host suite asserts exactly that.\n *\n * The narrowing therefore bought nothing and cost the thing the capability is\n * for. `./react` and `./web-push` ship Bell, Panel and Preferences — the exact\n * screens the adaptation report wanted to stop being hand-duplicated in hosts,\n * and the origin host duplicated the preferences screen and the push setup\n * anyway, because a manifest that never mentions them is a manifest nobody\n * discovers them from.\n *\n * ## Why there are no `areas`\n *\n * The bell is not a routed screen: it lives in a host's header chrome, beside\n * whatever else that host puts there, and no package can suggest a route for\n * it. Preferences IS routed, but where it belongs differs per host — under\n * account settings in one app, under a tenant's configuration in another — and\n * it is offered as `page` on the surface for the host to route at its own\n * call site. A suggested nav row would be wrong for every host but the first,\n * which is the `AreaContribution` doctrine's own test.\n */\n\nimport type { AnyWebManifest } from '@12-apps/wiring';\n\nimport { createWebNotifications } from '../react/create-web-notifications';\n\nexport const notificationsWebManifest = {\n name: '@12-apps/notifications',\n surface: { create: createWebNotifications },\n} as const satisfies AnyWebManifest;\n"],"mappings":";;;;;;;;AA0CO,IAAM,2BAA2B;AAAA,EACtC,MAAM;AAAA,EACN,SAAS,EAAE,QAAQ,uBAAuB;AAC5C;","names":[]}
1
+ {"version":3,"sources":["../../src/manifest/web.ts"],"sourcesContent":["/**\n * `@12-apps/notifications/manifest/web` — the web capabilities.\n *\n * `surface.create` IS `createWebNotifications`, unchanged: the bell, the inbox\n * slide-over, the preferences screen and the bound store, built once per\n * adoption by the consumer's binder (the members are component TYPES, so\n * rebuilding per render unmounts the panel mid-interaction — the memoisation\n * rule every hand wiring carries as a comment today).\n *\n * ## Why this manifest exists now\n *\n * It was narrowed away with a reason that reads plausibly and is FALSE:\n * \"listing `web` would oblige every server host adopting this manifest to\n * answer for a React surface it never mounts.\" The consumer does not work\n * that way. A capability declared for the other runtime is reported\n * `out-of-scope` — \"a web host answers for this\" — and `assemble()` returns\n * fine; only a capability applicable to THIS runtime and unanswered is\n * `unbound`. `wiring`'s own fixture package declares both halves and its\n * server-host suite asserts exactly that.\n *\n * The narrowing therefore bought nothing and cost the thing the capability is\n * for. `./react` and `./web-push` ship Bell, Panel and Preferences — the exact\n * screens the adaptation report wanted to stop being hand-duplicated in hosts,\n * and the origin host duplicated the preferences screen and the push setup\n * anyway, because a manifest that never mentions them is a manifest nobody\n * discovers them from.\n *\n * ## Why there are no `areas`\n *\n * The bell is not a routed screen: it lives in a host's header chrome, beside\n * whatever else that host puts there, and no package can suggest a route for\n * it. Preferences IS routed, but where it belongs differs per host — under\n * account settings in one app, under a tenant's configuration in another — and\n * it is offered as `page` on the surface for the host to route at its own\n * call site. A suggested nav row would be wrong for every host but the first,\n * which is the `AreaContribution` doctrine's own test.\n */\n\nimport type { AnyWebManifest } from '@12-apps/wiring';\n\nimport { createEmailPreviewScreen } from '../email/previews/react/preview-screen';\nimport { createWebNotifications } from '../react/create-web-notifications';\n\nexport const notificationsWebManifest = {\n name: '@12-apps/notifications',\n surface: { create: createWebNotifications },\n} as const satisfies AnyWebManifest;\n\n/**\n * The preview console's web half.\n *\n * One screen, named `page`, because a screen's NAME is what an area row\n * resolves against — a surface that was itself the component is the shape that\n * made an area row resolve to `undefined` in `@12-apps/auth` for a release.\n */\nexport const notificationEmailPreviewsWebManifest = {\n name: '@12-apps/notifications-email-previews',\n surface: { create: createEmailPreviewScreen },\n} as const satisfies AnyWebManifest;\n"],"mappings":";;;;;;;;;;;AA2CO,IAAM,2BAA2B;AAAA,EACtC,MAAM;AAAA,EACN,SAAS,EAAE,QAAQ,uBAAuB;AAC5C;AASO,IAAM,uCAAuC;AAAA,EAClD,MAAM;AAAA,EACN,SAAS,EAAE,QAAQ,yBAAyB;AAC9C;","names":[]}
@@ -4,7 +4,7 @@ import {
4
4
  } from "./chunk-CQZMTFPY.js";
5
5
  import {
6
6
  NOTIFICATION_CHANNELS
7
- } from "./chunk-XE7HZVMH.js";
7
+ } from "./chunk-BGI4BQ66.js";
8
8
  import {
9
9
  __name
10
10
  } from "./chunk-7QVYU63E.js";
@@ -291,4 +291,4 @@ __name(PreferencesScreen, "PreferencesScreen");
291
291
  export {
292
292
  PreferencesScreen
293
293
  };
294
- //# sourceMappingURL=preferences-screen-IOW6Y2H2.js.map
294
+ //# sourceMappingURL=preferences-screen-V3ZBWP7A.js.map
@@ -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 };
@@ -1,8 +1,8 @@
1
- import { N as NotificationsApiClient } from '../create-web-notifications-BHCzaU2y.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-BHCzaU2y.js';
1
+ import { N as NotificationsApiClient } from '../create-web-notifications-B-xQ6R0B.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-B-xQ6R0B.js';
3
3
  import { JSX } from 'react';
4
- import { N as NotificationMessages } from '../wire-6dzyfDE7.js';
5
- import '../types-CXLAG3UU.js';
4
+ import { N as NotificationMessages } from '../wire-_coV3tJk.js';
5
+ import '../types-BFmAY0P5.js';
6
6
 
7
7
  /** Inline SVG bell (no icon-library dependency in this package). */
8
8
 
@@ -1,18 +1,12 @@
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-MMLV4EZT.js";
6
+ } from "../chunk-M5XTROYM.js";
7
+ import {
8
+ relativeTime
9
+ } from "../chunk-O5BVUXPO.js";
16
10
  import {
17
11
  BADGE_POLL_MS,
18
12
  BADGE_RECONCILE_MS,
@@ -23,6 +17,12 @@ import {
23
17
  useInboxState,
24
18
  useUnreadCount
25
19
  } from "../chunk-BW723CX2.js";
20
+ import {
21
+ disableWebPush,
22
+ enableWebPush,
23
+ getExistingPushSubscription,
24
+ pushSupported
25
+ } from "../chunk-CQZMTFPY.js";
26
26
  import "../chunk-M2TVBVH2.js";
27
27
  import "../chunk-7QVYU63E.js";
28
28
  export {
@@ -1,11 +1,12 @@
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
- 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';
1
+ import { A as ApiNotifications } from '../create-api-notifications-B-VMezVB.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-B-VMezVB.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-D-ysQd5h.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-BDZ7aGHV.js';
6
- import '../generators-B9xt3sRh.js';
7
- import '../types-CXLAG3UU.js';
8
- import '../wire-6dzyfDE7.js';
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-D64B_bg7.js';
6
+ import '../generators-BsuXe8Mp.js';
7
+ import '../types-BFmAY0P5.js';
8
+ import '../wire-_coV3tJk.js';
9
+ import '../template-Dlt5S54z.js';
9
10
  import '@12-apps/wiring';
10
11
 
11
12
  /**
@@ -4,7 +4,7 @@ import {
4
4
  NOTIFICATIONS_JOBS,
5
5
  NOTIFICATIONS_SWEEP_QUEUE,
6
6
  wireNotifyPort
7
- } from "../chunk-CUZW62JS.js";
7
+ } from "../chunk-KU4FVOFA.js";
8
8
  import {
9
9
  EMAIL_DRIVERS,
10
10
  NotificationProviderError,
@@ -25,10 +25,11 @@ import {
25
25
  smsTransport,
26
26
  webPushTransport,
27
27
  whatsAppTransport
28
- } from "../chunk-WHBMPHQE.js";
29
- import "../chunk-HHMRCMQU.js";
30
- import "../chunk-XE7HZVMH.js";
28
+ } from "../chunk-KZG7QGFA.js";
29
+ import "../chunk-GGAZOLNN.js";
31
30
  import "../chunk-M2TVBVH2.js";
31
+ import "../chunk-BGI4BQ66.js";
32
+ import "../chunk-EKUSNUBT.js";
32
33
  import "../chunk-7QVYU63E.js";
33
34
  export {
34
35
  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 };