@voyantjs/notifications 0.6.8 → 0.7.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/README.md CHANGED
@@ -94,7 +94,7 @@ For finance-aware collection sends, the routes also support:
94
94
  - `GET /bookings/:id/document-bundle`
95
95
  - `POST /bookings/:id/send-documents`
96
96
 
97
- Those routes resolve recipients from the payment session, invoice, and linked booking participants, then render the selected notification template with finance context such as payment links, invoice balances, and booking references.
97
+ Those routes resolve recipients from the payment session, invoice, and linked booking travelers, then render the selected notification template with finance context such as payment links, invoice balances, and booking references.
98
98
 
99
99
  Booking document sends bundle the latest customer-facing contract attachment and
100
100
  ready invoice/proforma rendition for a booking. By default they use the stored
package/dist/index.d.ts CHANGED
@@ -1,5 +1,7 @@
1
1
  import type { HonoModule } from "@voyantjs/hono/module";
2
+ import type { PostgresJsDatabase } from "drizzle-orm/postgres-js";
2
3
  import { type NotificationsRoutesOptions } from "./routes.js";
4
+ export { notificationLiquidEngine, renderLiquidTemplate, } from "./liquid.js";
3
5
  export type { DefaultNotificationProviderOptions } from "./provider-resolution.js";
4
6
  export { createDefaultNotificationProviders, createResendProviderFromEnv, createTwilioProviderFromEnv, } from "./provider-resolution.js";
5
7
  export type { LocalProviderOptions } from "./providers/local.js";
@@ -13,13 +15,35 @@ export { buildNotificationsRouteRuntime, createNotificationsRoutes, NOTIFICATION
13
15
  export type { NewNotificationDelivery, NewNotificationReminderRule, NewNotificationReminderRun, NewNotificationTemplate, NotificationDelivery, NotificationReminderRule, NotificationReminderRun, NotificationsHonoModule, NotificationTemplate, } from "./schema.js";
14
16
  export { notificationChannelEnum, notificationDeliveries, notificationDeliveryStatusEnum, notificationReminderRules, notificationReminderRunStatusEnum, notificationReminderRuns, notificationReminderStatusEnum, notificationReminderTargetTypeEnum, notificationsModule, notificationTargetTypeEnum, notificationTemplateStatusEnum, notificationTemplates, } from "./schema.js";
15
17
  export type { NotificationService } from "./service.js";
16
- export { createDefaultBookingDocumentAttachment, createNotificationService, NotificationError, notificationsService, renderNotificationTemplate, } from "./service.js";
18
+ export { createDefaultBookingDocumentAttachment, createNotificationService, NotificationError, notificationsService, previewNotificationTemplate, renderNotificationTemplate, } from "./service.js";
17
19
  export type { BookingDocumentAttachmentResolver, BookingDocumentsSentEvent, SendBookingDocumentsRuntimeOptions, } from "./service-booking-documents.js";
18
20
  export { bookingDocumentNotificationsService } from "./service-booking-documents.js";
19
21
  export type { NotificationTaskEnv, NotificationTaskRuntime, NotificationTaskRuntimeOptions, ReminderDeliveryJob, } from "./task-runtime.js";
20
22
  export { buildNotificationTaskRuntime } from "./task-runtime.js";
21
23
  export { deliverQueuedNotificationReminder, sendDueNotificationReminders } from "./tasks/index.js";
24
+ export type { NotificationLiquidSnippet, NotificationTemplateVariableCategory, NotificationTemplateVariableDefinition, NotificationTemplateVariableType, } from "./template-authoring.js";
25
+ export { notificationLiquidSnippets, notificationTemplateVariableCatalog, } from "./template-authoring.js";
22
26
  export type { NotificationAttachment, NotificationChannel, NotificationPayload, NotificationProvider, NotificationResult, } from "./types.js";
23
- export { bookingDocumentBundleItemSchema, bookingDocumentBundleSchema, insertNotificationReminderRuleSchema, insertNotificationTemplateSchema, notificationAttachmentSchema, notificationChannelSchema, notificationDeliveryListQuerySchema, notificationDeliveryStatusSchema, notificationDocumentSourceSchema, notificationDocumentTypeSchema, notificationReminderRuleListQuerySchema, notificationReminderRunDeliverySummarySchema, notificationReminderRunLinksSchema, notificationReminderRunListQuerySchema, notificationReminderRunListResponseSchema, notificationReminderRunRecordSchema, notificationReminderRunRuleSummarySchema, notificationReminderRunStatusSchema, notificationReminderStatusSchema, notificationReminderTargetTypeSchema, notificationTargetTypeSchema, notificationTemplateListQuerySchema, notificationTemplateStatusSchema, runDueRemindersSchema, sendBookingDocumentsNotificationResultSchema, sendBookingDocumentsNotificationSchema, sendInvoiceNotificationSchema, sendNotificationSchema, sendPaymentSessionNotificationSchema, updateNotificationReminderRuleSchema, updateNotificationTemplateSchema, } from "./validation.js";
24
- export declare function createNotificationsHonoModule(options?: NotificationsRoutesOptions): HonoModule;
27
+ export { bookingDocumentBundleItemSchema, bookingDocumentBundleSchema, insertNotificationReminderRuleSchema, insertNotificationTemplateSchema, notificationAttachmentSchema, notificationChannelSchema, notificationDeliveryListQuerySchema, notificationDeliveryStatusSchema, notificationDocumentSourceSchema, notificationDocumentTypeSchema, notificationReminderRuleListQuerySchema, notificationReminderRunDeliverySummarySchema, notificationReminderRunLinksSchema, notificationReminderRunListQuerySchema, notificationReminderRunListResponseSchema, notificationReminderRunRecordSchema, notificationReminderRunRuleSummarySchema, notificationReminderRunStatusSchema, notificationReminderStatusSchema, notificationReminderTargetTypeSchema, notificationTargetTypeSchema, notificationTemplateListQuerySchema, notificationTemplateStatusSchema, previewNotificationTemplateResultSchema, previewNotificationTemplateSchema, runDueRemindersSchema, sendBookingDocumentsNotificationResultSchema, sendBookingDocumentsNotificationSchema, sendInvoiceNotificationSchema, sendNotificationSchema, sendPaymentSessionNotificationSchema, updateNotificationReminderRuleSchema, updateNotificationTemplateSchema, } from "./validation.js";
28
+ /**
29
+ * Auto-dispatch policy for the `booking.confirmed` subscriber. Set `enabled:
30
+ * false` (or leave the option off entirely) to opt out.
31
+ */
32
+ export interface NotificationsAutoConfirmAndDispatchOptions {
33
+ enabled?: boolean;
34
+ /** Notification template slug used when the handler fires. */
35
+ templateSlug?: string;
36
+ /** Optional allowlist of document types to attach; defaults to all. */
37
+ documentTypes?: Array<"contract" | "invoice" | "proforma">;
38
+ }
39
+ export interface CreateNotificationsHonoModuleOptions extends NotificationsRoutesOptions {
40
+ /**
41
+ * Resolves a database from runtime bindings. Required for
42
+ * `autoConfirmAndDispatch` — the `booking.confirmed` subscriber fires
43
+ * outside a request scope and needs its own db handle.
44
+ */
45
+ resolveDb?: (bindings: Record<string, unknown>) => PostgresJsDatabase;
46
+ autoConfirmAndDispatch?: NotificationsAutoConfirmAndDispatchOptions;
47
+ }
48
+ export declare function createNotificationsHonoModule(options?: CreateNotificationsHonoModuleOptions): HonoModule;
25
49
  //# sourceMappingURL=index.d.ts.map
@@ -1 +1 @@
1
- {"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":"AACA,OAAO,KAAK,EAAE,UAAU,EAAE,MAAM,uBAAuB,CAAA;AAEvD,OAAO,EAIL,KAAK,0BAA0B,EAChC,MAAM,aAAa,CAAA;AAGpB,YAAY,EAAE,kCAAkC,EAAE,MAAM,0BAA0B,CAAA;AAClF,OAAO,EACL,kCAAkC,EAClC,2BAA2B,EAC3B,2BAA2B,GAC5B,MAAM,0BAA0B,CAAA;AACjC,YAAY,EAAE,oBAAoB,EAAE,MAAM,sBAAsB,CAAA;AAChE,OAAO,EAAE,mBAAmB,EAAE,MAAM,sBAAsB,CAAA;AAC1D,YAAY,EACV,WAAW,EACX,qBAAqB,EACrB,mBAAmB,GACpB,MAAM,uBAAuB,CAAA;AAC9B,OAAO,EAAE,oBAAoB,EAAE,MAAM,uBAAuB,CAAA;AAC5D,YAAY,EAAE,WAAW,EAAE,qBAAqB,EAAE,iBAAiB,EAAE,MAAM,uBAAuB,CAAA;AAClG,OAAO,EAAE,oBAAoB,EAAE,MAAM,uBAAuB,CAAA;AAC5D,YAAY,EAAE,yBAAyB,EAAE,0BAA0B,EAAE,MAAM,aAAa,CAAA;AACxF,OAAO,EACL,8BAA8B,EAC9B,yBAAyB,EACzB,yCAAyC,GAC1C,MAAM,aAAa,CAAA;AACpB,YAAY,EACV,uBAAuB,EACvB,2BAA2B,EAC3B,0BAA0B,EAC1B,uBAAuB,EACvB,oBAAoB,EACpB,wBAAwB,EACxB,uBAAuB,EACvB,uBAAuB,EACvB,oBAAoB,GACrB,MAAM,aAAa,CAAA;AACpB,OAAO,EACL,uBAAuB,EACvB,sBAAsB,EACtB,8BAA8B,EAC9B,yBAAyB,EACzB,iCAAiC,EACjC,wBAAwB,EACxB,8BAA8B,EAC9B,kCAAkC,EAClC,mBAAmB,EACnB,0BAA0B,EAC1B,8BAA8B,EAC9B,qBAAqB,GACtB,MAAM,aAAa,CAAA;AACpB,YAAY,EAAE,mBAAmB,EAAE,MAAM,cAAc,CAAA;AACvD,OAAO,EACL,sCAAsC,EACtC,yBAAyB,EACzB,iBAAiB,EACjB,oBAAoB,EACpB,0BAA0B,GAC3B,MAAM,cAAc,CAAA;AACrB,YAAY,EACV,iCAAiC,EACjC,yBAAyB,EACzB,kCAAkC,GACnC,MAAM,gCAAgC,CAAA;AACvC,OAAO,EAAE,mCAAmC,EAAE,MAAM,gCAAgC,CAAA;AACpF,YAAY,EACV,mBAAmB,EACnB,uBAAuB,EACvB,8BAA8B,EAC9B,mBAAmB,GACpB,MAAM,mBAAmB,CAAA;AAC1B,OAAO,EAAE,4BAA4B,EAAE,MAAM,mBAAmB,CAAA;AAChE,OAAO,EAAE,iCAAiC,EAAE,4BAA4B,EAAE,MAAM,kBAAkB,CAAA;AAClG,YAAY,EACV,sBAAsB,EACtB,mBAAmB,EACnB,mBAAmB,EACnB,oBAAoB,EACpB,kBAAkB,GACnB,MAAM,YAAY,CAAA;AACnB,OAAO,EACL,+BAA+B,EAC/B,2BAA2B,EAC3B,oCAAoC,EACpC,gCAAgC,EAChC,4BAA4B,EAC5B,yBAAyB,EACzB,mCAAmC,EACnC,gCAAgC,EAChC,gCAAgC,EAChC,8BAA8B,EAC9B,uCAAuC,EACvC,4CAA4C,EAC5C,kCAAkC,EAClC,sCAAsC,EACtC,yCAAyC,EACzC,mCAAmC,EACnC,wCAAwC,EACxC,mCAAmC,EACnC,gCAAgC,EAChC,oCAAoC,EACpC,4BAA4B,EAC5B,mCAAmC,EACnC,gCAAgC,EAChC,qBAAqB,EACrB,4CAA4C,EAC5C,sCAAsC,EACtC,6BAA6B,EAC7B,sBAAsB,EACtB,oCAAoC,EACpC,oCAAoC,EACpC,gCAAgC,GACjC,MAAM,iBAAiB,CAAA;AAExB,wBAAgB,6BAA6B,CAAC,OAAO,CAAC,EAAE,0BAA0B,GAAG,UAAU,CAe9F"}
1
+ {"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":"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"}
package/dist/index.js CHANGED
@@ -1,25 +1,61 @@
1
1
  import { buildNotificationsRouteRuntime, createNotificationsRoutes, NOTIFICATIONS_ROUTE_RUNTIME_CONTAINER_KEY, } from "./routes.js";
2
2
  import { notificationsModule } from "./schema.js";
3
+ import { createNotificationService } from "./service.js";
4
+ import { bookingDocumentNotificationsService } from "./service-booking-documents.js";
5
+ export { notificationLiquidEngine, renderLiquidTemplate, } from "./liquid.js";
3
6
  export { createDefaultNotificationProviders, createResendProviderFromEnv, createTwilioProviderFromEnv, } from "./provider-resolution.js";
4
7
  export { createLocalProvider } from "./providers/local.js";
5
8
  export { createResendProvider } from "./providers/resend.js";
6
9
  export { createTwilioProvider } from "./providers/twilio.js";
7
10
  export { buildNotificationsRouteRuntime, createNotificationsRoutes, NOTIFICATIONS_ROUTE_RUNTIME_CONTAINER_KEY, } from "./routes.js";
8
11
  export { notificationChannelEnum, notificationDeliveries, notificationDeliveryStatusEnum, notificationReminderRules, notificationReminderRunStatusEnum, notificationReminderRuns, notificationReminderStatusEnum, notificationReminderTargetTypeEnum, notificationsModule, notificationTargetTypeEnum, notificationTemplateStatusEnum, notificationTemplates, } from "./schema.js";
9
- export { createDefaultBookingDocumentAttachment, createNotificationService, NotificationError, notificationsService, renderNotificationTemplate, } from "./service.js";
12
+ export { createDefaultBookingDocumentAttachment, createNotificationService, NotificationError, notificationsService, previewNotificationTemplate, renderNotificationTemplate, } from "./service.js";
10
13
  export { bookingDocumentNotificationsService } from "./service-booking-documents.js";
11
14
  export { buildNotificationTaskRuntime } from "./task-runtime.js";
12
15
  export { deliverQueuedNotificationReminder, sendDueNotificationReminders } from "./tasks/index.js";
13
- export { bookingDocumentBundleItemSchema, bookingDocumentBundleSchema, insertNotificationReminderRuleSchema, insertNotificationTemplateSchema, notificationAttachmentSchema, notificationChannelSchema, notificationDeliveryListQuerySchema, notificationDeliveryStatusSchema, notificationDocumentSourceSchema, notificationDocumentTypeSchema, notificationReminderRuleListQuerySchema, notificationReminderRunDeliverySummarySchema, notificationReminderRunLinksSchema, notificationReminderRunListQuerySchema, notificationReminderRunListResponseSchema, notificationReminderRunRecordSchema, notificationReminderRunRuleSummarySchema, notificationReminderRunStatusSchema, notificationReminderStatusSchema, notificationReminderTargetTypeSchema, notificationTargetTypeSchema, notificationTemplateListQuerySchema, notificationTemplateStatusSchema, runDueRemindersSchema, sendBookingDocumentsNotificationResultSchema, sendBookingDocumentsNotificationSchema, sendInvoiceNotificationSchema, sendNotificationSchema, sendPaymentSessionNotificationSchema, updateNotificationReminderRuleSchema, updateNotificationTemplateSchema, } from "./validation.js";
16
+ export { notificationLiquidSnippets, notificationTemplateVariableCatalog, } from "./template-authoring.js";
17
+ export { bookingDocumentBundleItemSchema, bookingDocumentBundleSchema, insertNotificationReminderRuleSchema, insertNotificationTemplateSchema, notificationAttachmentSchema, notificationChannelSchema, notificationDeliveryListQuerySchema, notificationDeliveryStatusSchema, notificationDocumentSourceSchema, notificationDocumentTypeSchema, notificationReminderRuleListQuerySchema, notificationReminderRunDeliverySummarySchema, notificationReminderRunLinksSchema, notificationReminderRunListQuerySchema, notificationReminderRunListResponseSchema, notificationReminderRunRecordSchema, notificationReminderRunRuleSummarySchema, notificationReminderRunStatusSchema, notificationReminderStatusSchema, notificationReminderTargetTypeSchema, notificationTargetTypeSchema, notificationTemplateListQuerySchema, notificationTemplateStatusSchema, previewNotificationTemplateResultSchema, previewNotificationTemplateSchema, runDueRemindersSchema, sendBookingDocumentsNotificationResultSchema, sendBookingDocumentsNotificationSchema, sendInvoiceNotificationSchema, sendNotificationSchema, sendPaymentSessionNotificationSchema, updateNotificationReminderRuleSchema, updateNotificationTemplateSchema, } from "./validation.js";
14
18
  export function createNotificationsHonoModule(options) {
19
+ const routes = createNotificationsRoutes(options);
15
20
  const module = {
16
21
  ...notificationsModule,
17
- bootstrap: ({ bindings, container }) => {
22
+ bootstrap: ({ bindings, container, eventBus }) => {
18
23
  container.register(NOTIFICATIONS_ROUTE_RUNTIME_CONTAINER_KEY, buildNotificationsRouteRuntime(bindings, options));
24
+ // Auto-dispatch wiring — opt-in. When enabled, every `booking.confirmed`
25
+ // event triggers a `confirmAndDispatchBooking` call so the operator
26
+ // doesn't have to click a second button. The handler runs in the same
27
+ // process as the emitter (the in-process event bus) but outside the
28
+ // request scope, so we resolve our own db handle from bindings.
29
+ if (options?.autoConfirmAndDispatch?.enabled && options.resolveDb) {
30
+ const resolveDb = options.resolveDb;
31
+ const autoOptions = options.autoConfirmAndDispatch;
32
+ const runtime = buildNotificationsRouteRuntime(bindings, options);
33
+ const dispatcher = createNotificationService(runtime.providers);
34
+ eventBus.subscribe("booking.confirmed", async (event) => {
35
+ try {
36
+ const db = resolveDb(bindings);
37
+ await bookingDocumentNotificationsService.confirmAndDispatchBooking(db, dispatcher, event.data.bookingId, {
38
+ templateSlug: autoOptions.templateSlug ?? null,
39
+ documentTypes: autoOptions.documentTypes ?? null,
40
+ }, {
41
+ attachmentResolver: runtime.documentAttachmentResolver,
42
+ eventBus,
43
+ });
44
+ }
45
+ catch (error) {
46
+ // Per the EventBus contract, handler failures are logged, not
47
+ // rethrown. We surface the context so ops can diagnose without
48
+ // digging through stack traces.
49
+ const message = error instanceof Error ? error.message : String(error);
50
+ console.error(`[notifications] auto-dispatch failed for booking ${event.data.bookingId}: ${message}`);
51
+ }
52
+ });
53
+ }
19
54
  },
20
55
  };
21
56
  return {
22
57
  module,
23
- routes: createNotificationsRoutes(options),
58
+ adminRoutes: routes,
59
+ routes,
24
60
  };
25
61
  }
@@ -0,0 +1,5 @@
1
+ import { Liquid } from "liquidjs";
2
+ declare const engine: Liquid;
3
+ export declare function renderLiquidTemplate(template: string | null | undefined, data: Record<string, unknown>): string | null;
4
+ export { engine as notificationLiquidEngine };
5
+ //# sourceMappingURL=liquid.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"liquid.d.ts","sourceRoot":"","sources":["../src/liquid.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,MAAM,EAAE,MAAM,UAAU,CAAA;AAEjC,QAAA,MAAM,MAAM,QAOV,CAAA;AAqIF,wBAAgB,oBAAoB,CAClC,QAAQ,EAAE,MAAM,GAAG,IAAI,GAAG,SAAS,EACnC,IAAI,EAAE,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,GAC5B,MAAM,GAAG,IAAI,CAiBf;AAED,OAAO,EAAE,MAAM,IAAI,wBAAwB,EAAE,CAAA"}
package/dist/liquid.js ADDED
@@ -0,0 +1,156 @@
1
+ import { Liquid } from "liquidjs";
2
+ const engine = new Liquid({
3
+ strictFilters: false,
4
+ strictVariables: false,
5
+ trimTagLeft: false,
6
+ trimTagRight: false,
7
+ greedy: false,
8
+ jsTruthy: true,
9
+ });
10
+ engine.registerFilter("json", (value) => JSON.stringify(value ?? null));
11
+ engine.registerFilter("currency", (value, currency = "EUR", locale = "en-US") => {
12
+ const num = typeof value === "number" ? value : Number.parseFloat(String(value));
13
+ if (Number.isNaN(num))
14
+ return String(value ?? "");
15
+ return new Intl.NumberFormat(String(locale), {
16
+ style: "currency",
17
+ currency: String(currency || "EUR"),
18
+ }).format(num);
19
+ });
20
+ engine.registerFilter("date_format", (value, _format, locale = "en-US") => {
21
+ if (!value)
22
+ return "";
23
+ const date = value instanceof Date ? value : new Date(String(value));
24
+ if (Number.isNaN(date.getTime()))
25
+ return String(value);
26
+ return date.toLocaleDateString(String(locale), {
27
+ year: "numeric",
28
+ month: "long",
29
+ day: "numeric",
30
+ });
31
+ });
32
+ engine.registerFilter("phone", (value) => {
33
+ if (!value)
34
+ return "";
35
+ const phone = String(value).replace(/\D/g, "");
36
+ if (phone.length === 10) {
37
+ return `(${phone.slice(0, 3)}) ${phone.slice(3, 6)}-${phone.slice(6)}`;
38
+ }
39
+ if (phone.length === 11 && phone.startsWith("1")) {
40
+ return `+1 (${phone.slice(1, 4)}) ${phone.slice(4, 7)}-${phone.slice(7)}`;
41
+ }
42
+ if (phone.length > 10) {
43
+ return `+${phone.slice(0, -9)} ${phone.slice(-9, -6)} ${phone.slice(-6, -3)} ${phone.slice(-3)}`;
44
+ }
45
+ return String(value);
46
+ });
47
+ engine.registerFilter("default", (value, defaultValue) => {
48
+ if (value === null || value === undefined || value === "")
49
+ return defaultValue;
50
+ return value;
51
+ });
52
+ engine.registerFilter("pluralize", (count, singular, plural) => {
53
+ const num = typeof count === "number" ? count : Number.parseInt(String(count), 10);
54
+ return num === 1 ? singular : plural;
55
+ });
56
+ function toSnakeCase(str) {
57
+ return str.replace(/[A-Z]/g, (letter) => `_${letter.toLowerCase()}`);
58
+ }
59
+ function normalizeKeys(obj) {
60
+ const result = {};
61
+ for (const [key, value] of Object.entries(obj)) {
62
+ result[key] =
63
+ value && typeof value === "object" && !Array.isArray(value)
64
+ ? normalizeKeys(value)
65
+ : value;
66
+ const snakeKey = toSnakeCase(key);
67
+ if (snakeKey !== key) {
68
+ result[snakeKey] = result[key];
69
+ }
70
+ }
71
+ return result;
72
+ }
73
+ function decodeHtmlEntities(str) {
74
+ return str
75
+ .replace(/&quot;/g, '"')
76
+ .replace(/&#39;/g, "'")
77
+ .replace(/&amp;/g, "&")
78
+ .replace(/&lt;/g, "<")
79
+ .replace(/&gt;/g, ">");
80
+ }
81
+ function healBrokenTags(html) {
82
+ html = html.replace(/\{\{([\s\S]{1,300}?)\}\}/g, (match) => {
83
+ let healed = match;
84
+ if (/</.test(healed))
85
+ healed = healed.replace(/<[^>]+>/g, "").replace(/\s+/g, " ");
86
+ if (/&\w+;/.test(healed))
87
+ healed = decodeHtmlEntities(healed);
88
+ return healed;
89
+ });
90
+ html = html.replace(/\{%([\s\S]{1,300}?)%\}/g, (match) => {
91
+ let healed = match;
92
+ if (/</.test(healed))
93
+ healed = healed.replace(/<[^>]+>/g, "").replace(/\s+/g, " ");
94
+ if (/&\w+;/.test(healed))
95
+ healed = decodeHtmlEntities(healed);
96
+ return healed;
97
+ });
98
+ return html;
99
+ }
100
+ function healSyntax(template) {
101
+ let result = healBrokenTags(template);
102
+ result = result.replace(/\{%[-\s]*for\s+(\w+)\s*[-]?%\}/gi, (match, collection) => {
103
+ if (/\bfor\s+\w+\s+in\s+/i.test(match))
104
+ return match;
105
+ const singular = collection.endsWith("ies")
106
+ ? `${collection.slice(0, -3)}y`
107
+ : collection.endsWith("s")
108
+ ? collection.slice(0, -1)
109
+ : `${collection}_item`;
110
+ return `{% for ${singular} in ${collection} %}`;
111
+ });
112
+ result = result.replace(/\{%[-\s]*(endfor|endif|else|endunless|endcase|endcapture|endraw)\s*[-]?%\}/gi, (_match, tag) => `{% ${tag.toLowerCase()} %}`);
113
+ return result;
114
+ }
115
+ function regexFallback(template, data) {
116
+ function resolve(obj, path) {
117
+ const parts = path.split(".");
118
+ let cur = obj;
119
+ for (const p of parts) {
120
+ if (cur == null || typeof cur !== "object")
121
+ return undefined;
122
+ cur = cur[p];
123
+ }
124
+ return cur;
125
+ }
126
+ let result = template;
127
+ result = result.replace(/\{\{\s*([^}|]+?)(?:\s*\|[^}]*)?\s*\}\}/g, (_match, rawPath) => {
128
+ const path = rawPath.trim();
129
+ const value = resolve(data, path);
130
+ if (value !== undefined && value !== null)
131
+ return String(value);
132
+ return _match;
133
+ });
134
+ result = result.replace(/\{%[\s\S]*?%\}/g, "");
135
+ return result;
136
+ }
137
+ export function renderLiquidTemplate(template, data) {
138
+ if (!template)
139
+ return null;
140
+ const normalized = normalizeKeys(data);
141
+ const healed = healSyntax(template);
142
+ try {
143
+ return engine.parseAndRenderSync(healed, normalized);
144
+ }
145
+ catch (error) {
146
+ console.error("[notifications:liquid] template parsing failed, falling back to regex", error);
147
+ }
148
+ try {
149
+ return regexFallback(healed, normalized);
150
+ }
151
+ catch (fallbackError) {
152
+ console.error("[notifications:liquid] regex fallback failed", fallbackError);
153
+ return template;
154
+ }
155
+ }
156
+ export { engine as notificationLiquidEngine };
package/dist/routes.d.ts CHANGED
@@ -170,6 +170,24 @@ export declare function createNotificationsRoutes(options?: NotificationsRoutesO
170
170
  status: import("hono/utils/http-status").ContentfulStatusCode;
171
171
  };
172
172
  };
173
+ } & {
174
+ "/preview": {
175
+ $post: {
176
+ input: {};
177
+ output: {
178
+ data: {
179
+ channel: "email" | "sms";
180
+ provider: string | null;
181
+ fromAddress: string | null;
182
+ subject: string | null;
183
+ html: string | null;
184
+ text: string | null;
185
+ };
186
+ };
187
+ outputFormat: "json";
188
+ status: import("hono/utils/http-status").ContentfulStatusCode;
189
+ };
190
+ };
173
191
  } & {
174
192
  "/deliveries": {
175
193
  $get: {
@@ -430,7 +448,7 @@ export declare function createNotificationsRoutes(options?: NotificationsRoutesO
430
448
  targetType: "invoice" | "booking_payment_schedule";
431
449
  targetId: string;
432
450
  dedupeKey: string;
433
- status: "failed" | "sent" | "processing" | "queued" | "skipped";
451
+ status: "failed" | "sent" | "processing" | "skipped" | "queued";
434
452
  recipient: string | null;
435
453
  scheduledFor: string;
436
454
  processedAt: string;
@@ -507,7 +525,7 @@ export declare function createNotificationsRoutes(options?: NotificationsRoutesO
507
525
  targetType: "invoice" | "booking_payment_schedule";
508
526
  targetId: string;
509
527
  dedupeKey: string;
510
- status: "failed" | "sent" | "processing" | "queued" | "skipped";
528
+ status: "failed" | "sent" | "processing" | "skipped" | "queued";
511
529
  recipient: string | null;
512
530
  scheduledFor: string;
513
531
  processedAt: string;
@@ -766,6 +784,74 @@ export declare function createNotificationsRoutes(options?: NotificationsRoutesO
766
784
  status: import("hono/utils/http-status").ContentfulStatusCode;
767
785
  };
768
786
  };
787
+ } & {
788
+ "/bookings/:id/confirm-and-dispatch": {
789
+ $post: {
790
+ input: {
791
+ param: {
792
+ id: string;
793
+ };
794
+ };
795
+ output: {
796
+ error: string;
797
+ };
798
+ outputFormat: "json";
799
+ status: 404;
800
+ } | {
801
+ input: {
802
+ param: {
803
+ id: string;
804
+ };
805
+ };
806
+ output: {
807
+ data: {
808
+ bookingId: string;
809
+ documents: {
810
+ key: string;
811
+ source: "finance" | "legal";
812
+ documentType: "invoice" | "proforma" | "contract";
813
+ bookingId: string;
814
+ name: string;
815
+ createdAt: string;
816
+ contractId?: string | null | undefined;
817
+ invoiceId?: string | null | undefined;
818
+ attachmentId?: string | null | undefined;
819
+ renditionId?: string | null | undefined;
820
+ contractStatus?: string | null | undefined;
821
+ invoiceStatus?: string | null | undefined;
822
+ format?: string | null | undefined;
823
+ mimeType?: string | null | undefined;
824
+ storageKey?: string | null | undefined;
825
+ downloadUrl?: string | null | undefined;
826
+ language?: string | null | undefined;
827
+ metadata?: {
828
+ [x: string]: import("hono/utils/types").JSONValue;
829
+ } | null | undefined;
830
+ }[];
831
+ notification: {
832
+ recipient: string;
833
+ deliveryId: string;
834
+ status: "pending" | "cancelled" | "failed" | "sent";
835
+ provider?: string | null | undefined;
836
+ } | null;
837
+ skipReason: "preview_only" | "no_documents" | "no_recipient" | "no_attachments" | "send_failed" | null;
838
+ };
839
+ };
840
+ outputFormat: "json";
841
+ status: import("hono/utils/http-status").ContentfulStatusCode;
842
+ } | {
843
+ input: {
844
+ param: {
845
+ id: string;
846
+ };
847
+ };
848
+ output: {
849
+ error: string;
850
+ };
851
+ outputFormat: "json";
852
+ status: 400;
853
+ };
854
+ };
769
855
  } & {
770
856
  "/bookings/:id/send-documents": {
771
857
  $post: {
@@ -1 +1 @@
1
- {"version":3,"file":"routes.d.ts","sourceRoot":"","sources":["../src/routes.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,QAAQ,EAAE,eAAe,EAAE,MAAM,gBAAgB,CAAA;AAE/D,OAAO,KAAK,EAAE,kBAAkB,EAAE,MAAM,yBAAyB,CAAA;AAIjE,OAAO,KAAK,EAAE,iCAAiC,EAAE,MAAM,gCAAgC,CAAA;AACvF,OAAO,KAAK,EAAE,oBAAoB,EAAE,MAAM,YAAY,CAAA;AAmBtD,KAAK,GAAG,GAAG;IACT,QAAQ,EAAE,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,CAAA;IACjC,SAAS,EAAE;QACT,SAAS,EAAE,eAAe,CAAA;QAC1B,EAAE,EAAE,kBAAkB,CAAA;QACtB,MAAM,CAAC,EAAE,MAAM,CAAA;KAChB,CAAA;CACF,CAAA;AAED,MAAM,MAAM,0BAA0B,GAAG;IACvC,SAAS,CAAC,EAAE,aAAa,CAAC,oBAAoB,CAAC,CAAA;IAC/C,gBAAgB,CAAC,EAAE,CAAC,QAAQ,EAAE,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,KAAK,aAAa,CAAC,oBAAoB,CAAC,CAAA;IAC7F,0BAA0B,CAAC,EAAE,iCAAiC,CAAA;IAC9D,iCAAiC,CAAC,EAAE,CAClC,QAAQ,EAAE,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,KAC9B,iCAAiC,GAAG,SAAS,CAAA;IAClD,QAAQ,CAAC,EAAE,QAAQ,CAAA;IACnB,eAAe,CAAC,EAAE,CAAC,QAAQ,EAAE,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,KAAK,QAAQ,GAAG,SAAS,CAAA;CAC9E,CAAA;AAED,MAAM,MAAM,yBAAyB,GAAG;IACtC,SAAS,EAAE,aAAa,CAAC,oBAAoB,CAAC,CAAA;IAC9C,0BAA0B,CAAC,EAAE,iCAAiC,CAAA;IAC9D,QAAQ,CAAC,EAAE,QAAQ,CAAA;CACpB,CAAA;AAED,eAAO,MAAM,yCAAyC,oCAAoC,CAAA;AAE1F,wBAAgB,8BAA8B,CAC5C,QAAQ,EAAE,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,EACjC,OAAO,CAAC,EAAE,0BAA0B,GACnC,yBAAyB,CAO3B;AAcD,wBAAgB,yBAAyB,CAAC,OAAO,CAAC,EAAE,0BAA0B;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;iBA0L7E"}
1
+ {"version":3,"file":"routes.d.ts","sourceRoot":"","sources":["../src/routes.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,QAAQ,EAAE,eAAe,EAAE,MAAM,gBAAgB,CAAA;AAE/D,OAAO,KAAK,EAAE,kBAAkB,EAAE,MAAM,yBAAyB,CAAA;AAIjE,OAAO,KAAK,EAAE,iCAAiC,EAAE,MAAM,gCAAgC,CAAA;AACvF,OAAO,KAAK,EAAE,oBAAoB,EAAE,MAAM,YAAY,CAAA;AAuBtD,KAAK,GAAG,GAAG;IACT,QAAQ,EAAE,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,CAAA;IACjC,SAAS,EAAE;QACT,SAAS,EAAE,eAAe,CAAA;QAC1B,EAAE,EAAE,kBAAkB,CAAA;QACtB,MAAM,CAAC,EAAE,MAAM,CAAA;KAChB,CAAA;CACF,CAAA;AAED,MAAM,MAAM,0BAA0B,GAAG;IACvC,SAAS,CAAC,EAAE,aAAa,CAAC,oBAAoB,CAAC,CAAA;IAC/C,gBAAgB,CAAC,EAAE,CAAC,QAAQ,EAAE,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,KAAK,aAAa,CAAC,oBAAoB,CAAC,CAAA;IAC7F,0BAA0B,CAAC,EAAE,iCAAiC,CAAA;IAC9D,iCAAiC,CAAC,EAAE,CAClC,QAAQ,EAAE,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,KAC9B,iCAAiC,GAAG,SAAS,CAAA;IAClD,QAAQ,CAAC,EAAE,QAAQ,CAAA;IACnB,eAAe,CAAC,EAAE,CAAC,QAAQ,EAAE,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,KAAK,QAAQ,GAAG,SAAS,CAAA;CAC9E,CAAA;AAED,MAAM,MAAM,yBAAyB,GAAG;IACtC,SAAS,EAAE,aAAa,CAAC,oBAAoB,CAAC,CAAA;IAC9C,0BAA0B,CAAC,EAAE,iCAAiC,CAAA;IAC9D,QAAQ,CAAC,EAAE,QAAQ,CAAA;CACpB,CAAA;AAED,eAAO,MAAM,yCAAyC,oCAAoC,CAAA;AAE1F,wBAAgB,8BAA8B,CAC5C,QAAQ,EAAE,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,EACjC,OAAO,CAAC,EAAE,0BAA0B,GACnC,yBAAyB,CAO3B;AAcD,wBAAgB,yBAAyB,CAAC,OAAO,CAAC,EAAE,0BAA0B;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;iBAwP7E"}
package/dist/routes.js CHANGED
@@ -1,7 +1,7 @@
1
1
  import { parseJsonBody, parseOptionalJsonBody, parseQuery } from "@voyantjs/hono";
2
2
  import { Hono } from "hono";
3
3
  import { createNotificationService, notificationsService } from "./service.js";
4
- import { bookingDocumentBundleSchema, insertNotificationReminderRuleSchema, insertNotificationTemplateSchema, notificationDeliveryListQuerySchema, notificationReminderRuleListQuerySchema, notificationReminderRunListQuerySchema, notificationTemplateListQuerySchema, runDueRemindersSchema, sendBookingDocumentsNotificationResultSchema, sendBookingDocumentsNotificationSchema, sendInvoiceNotificationSchema, sendNotificationSchema, sendPaymentSessionNotificationSchema, updateNotificationReminderRuleSchema, updateNotificationTemplateSchema, } from "./validation.js";
4
+ import { bookingDocumentBundleSchema, confirmAndDispatchBookingResultSchema, confirmAndDispatchBookingSchema, insertNotificationReminderRuleSchema, insertNotificationTemplateSchema, notificationDeliveryListQuerySchema, notificationReminderRuleListQuerySchema, notificationReminderRunListQuerySchema, notificationTemplateListQuerySchema, previewNotificationTemplateResultSchema, previewNotificationTemplateSchema, runDueRemindersSchema, sendBookingDocumentsNotificationResultSchema, sendBookingDocumentsNotificationSchema, sendInvoiceNotificationSchema, sendNotificationSchema, sendPaymentSessionNotificationSchema, updateNotificationReminderRuleSchema, updateNotificationTemplateSchema, } from "./validation.js";
5
5
  export const NOTIFICATIONS_ROUTE_RUNTIME_CONTAINER_KEY = "providers.notifications.runtime";
6
6
  export function buildNotificationsRouteRuntime(bindings, options) {
7
7
  return {
@@ -39,6 +39,10 @@ export function createNotificationsRoutes(options) {
39
39
  if (!row)
40
40
  return c.json({ error: "Notification template not found" }, 404);
41
41
  return c.json({ data: row });
42
+ })
43
+ .post("/preview", async (c) => {
44
+ const rendered = notificationsService.previewNotificationTemplate(await parseJsonBody(c, previewNotificationTemplateSchema));
45
+ return c.json({ data: previewNotificationTemplateResultSchema.parse(rendered) });
42
46
  })
43
47
  .get("/deliveries", async (c) => {
44
48
  const query = parseQuery(c, notificationDeliveryListQuerySchema);
@@ -125,6 +129,55 @@ export function createNotificationsRoutes(options) {
125
129
  if (!bundle)
126
130
  return c.json({ error: "Booking not found" }, 404);
127
131
  return c.json({ data: bookingDocumentBundleSchema.parse(bundle) });
132
+ })
133
+ .post("/bookings/:id/confirm-and-dispatch", async (c) => {
134
+ try {
135
+ const runtime = getRuntime(c.env, options, (key) => c.var.container.resolve(key));
136
+ const dispatcher = createNotificationService(runtime.providers);
137
+ const result = await notificationsService.confirmAndDispatchBooking(c.get("db"), dispatcher, c.req.param("id"), await parseOptionalJsonBody(c, confirmAndDispatchBookingSchema), {
138
+ attachmentResolver: runtime.documentAttachmentResolver,
139
+ eventBus: runtime.eventBus,
140
+ });
141
+ if (result.status === "not_found")
142
+ return c.json({ error: "Booking not found" }, 404);
143
+ if (result.status === "preview") {
144
+ return c.json({
145
+ data: confirmAndDispatchBookingResultSchema.parse({
146
+ bookingId: result.bookingId,
147
+ documents: result.documents,
148
+ notification: null,
149
+ skipReason: "preview_only",
150
+ }),
151
+ });
152
+ }
153
+ if (result.status === "skipped") {
154
+ return c.json({
155
+ data: confirmAndDispatchBookingResultSchema.parse({
156
+ bookingId: result.bookingId,
157
+ documents: result.documents,
158
+ notification: null,
159
+ skipReason: result.skipReason,
160
+ }),
161
+ });
162
+ }
163
+ return c.json({
164
+ data: confirmAndDispatchBookingResultSchema.parse({
165
+ bookingId: result.bookingId,
166
+ documents: result.documents,
167
+ notification: {
168
+ recipient: result.recipient,
169
+ deliveryId: result.delivery.id,
170
+ provider: result.delivery.provider,
171
+ status: result.delivery.status,
172
+ },
173
+ skipReason: null,
174
+ }),
175
+ }, 201);
176
+ }
177
+ catch (error) {
178
+ const message = error instanceof Error ? error.message : "Confirm-and-dispatch failed";
179
+ return c.json({ error: message }, 400);
180
+ }
128
181
  })
129
182
  .post("/bookings/:id/send-documents", async (c) => {
130
183
  try {
package/dist/schema.d.ts CHANGED
@@ -1156,7 +1156,7 @@ export declare const notificationReminderRuns: import("drizzle-orm/pg-core").PgT
1156
1156
  tableName: "notification_reminder_runs";
1157
1157
  dataType: "string";
1158
1158
  columnType: "PgEnumColumn";
1159
- data: "failed" | "sent" | "processing" | "queued" | "skipped";
1159
+ data: "failed" | "sent" | "processing" | "skipped" | "queued";
1160
1160
  driverParam: string;
1161
1161
  notNull: true;
1162
1162
  hasDefault: false;