@voyant-travel/inventory 0.3.9 → 0.4.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.
@@ -24,7 +24,9 @@ export interface CloudflareBrowserBrochurePrinterOptions {
24
24
  viewport?: Record<string, unknown>;
25
25
  setExtraHTTPHeaders?: Record<string, string>;
26
26
  }
27
+ export declare function brochureBodyToHtmlFragment(body: string, bodyFormat: StructuredTemplateBodyFormat): string;
27
28
  export declare function brochureBodyToHtml(body: string, bodyFormat: StructuredTemplateBodyFormat, title: string): string;
29
+ export declare function isBasicPdfProductBrochurePrinter(printer: ProductBrochurePrinter): boolean;
28
30
  export declare function createBasicPdfProductBrochurePrinter(): ProductBrochurePrinter;
29
31
  export declare function createCloudflareBrowserProductBrochurePrinter(options: CloudflareBrowserBrochurePrinterOptions): ProductBrochurePrinter;
30
32
  export declare function createCloudflareBrowserProductBrochurePrinterFromEnv(env?: Record<string, string | undefined>): ProductBrochurePrinter;
@@ -1 +1 @@
1
- {"version":3,"file":"brochure-printers.d.ts","sourceRoot":"","sources":["../../src/tasks/brochure-printers.ts"],"names":[],"mappings":"AACA,OAAO,KAAK,EAAE,4BAA4B,EAAE,MAAM,wCAAwC,CAAA;AAI1F,OAAO,KAAK,EACV,8BAA8B,EAC9B,+BAA+B,EAChC,MAAM,yBAAyB,CAAA;AAEhC,MAAM,WAAW,6BAA6B;IAC5C,QAAQ,EAAE,+BAA+B,CAAA;IACzC,OAAO,EAAE,8BAA8B,CAAA;CACxC;AAED,MAAM,WAAW,8BAA8B;IAC7C,IAAI,EAAE,UAAU,CAAA;IAChB,QAAQ,CAAC,EAAE,MAAM,GAAG,IAAI,CAAA;IACxB,QAAQ,CAAC,EAAE,MAAM,GAAG,IAAI,CAAA;IACxB,QAAQ,CAAC,EAAE,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,GAAG,IAAI,CAAA;CAC1C;AAED,MAAM,MAAM,sBAAsB,GAAG,CACnC,KAAK,EAAE,6BAA6B,KACjC,OAAO,CAAC,8BAA8B,CAAC,CAAA;AAE5C,MAAM,WAAW,uCAAuC;IACtD,SAAS,EAAE,MAAM,CAAA;IACjB,QAAQ,EAAE,MAAM,CAAA;IAChB,UAAU,CAAC,EAAE,MAAM,CAAA;IACnB,WAAW,CAAC,EAAE,KAAK,CAAC;QAAE,OAAO,CAAC,EAAE,MAAM,CAAC;QAAC,GAAG,CAAC,EAAE,MAAM,CAAA;KAAE,CAAC,CAAA;IACvD,UAAU,CAAC,EAAE,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,CAAA;IACpC,WAAW,CAAC,EAAE,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,CAAA;IACrC,QAAQ,CAAC,EAAE,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,CAAA;IAClC,mBAAmB,CAAC,EAAE,MAAM,CAAC,MAAM,EAAE,MAAM,CAAC,CAAA;CAC7C;AA0DD,wBAAgB,kBAAkB,CAChC,IAAI,EAAE,MAAM,EACZ,UAAU,EAAE,4BAA4B,EACxC,KAAK,EAAE,MAAM,UAsBd;AAED,wBAAgB,oCAAoC,IAAI,sBAAsB,CAmB7E;AAED,wBAAgB,6CAA6C,CAC3D,OAAO,EAAE,uCAAuC,GAC/C,sBAAsB,CA6CxB;AAED,wBAAgB,oDAAoD,CAClE,GAAG,CAAC,EAAE,MAAM,CAAC,MAAM,EAAE,MAAM,GAAG,SAAS,CAAC,GACvC,sBAAsB,CAkBxB"}
1
+ {"version":3,"file":"brochure-printers.d.ts","sourceRoot":"","sources":["../../src/tasks/brochure-printers.ts"],"names":[],"mappings":"AACA,OAAO,KAAK,EAAE,4BAA4B,EAAE,MAAM,wCAAwC,CAAA;AAI1F,OAAO,KAAK,EACV,8BAA8B,EAC9B,+BAA+B,EAChC,MAAM,yBAAyB,CAAA;AAEhC,MAAM,WAAW,6BAA6B;IAC5C,QAAQ,EAAE,+BAA+B,CAAA;IACzC,OAAO,EAAE,8BAA8B,CAAA;CACxC;AAED,MAAM,WAAW,8BAA8B;IAC7C,IAAI,EAAE,UAAU,CAAA;IAChB,QAAQ,CAAC,EAAE,MAAM,GAAG,IAAI,CAAA;IACxB,QAAQ,CAAC,EAAE,MAAM,GAAG,IAAI,CAAA;IACxB,QAAQ,CAAC,EAAE,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,GAAG,IAAI,CAAA;CAC1C;AAED,MAAM,MAAM,sBAAsB,GAAG,CACnC,KAAK,EAAE,6BAA6B,KACjC,OAAO,CAAC,8BAA8B,CAAC,CAAA;AAQ5C,MAAM,WAAW,uCAAuC;IACtD,SAAS,EAAE,MAAM,CAAA;IACjB,QAAQ,EAAE,MAAM,CAAA;IAChB,UAAU,CAAC,EAAE,MAAM,CAAA;IACnB,WAAW,CAAC,EAAE,KAAK,CAAC;QAAE,OAAO,CAAC,EAAE,MAAM,CAAC;QAAC,GAAG,CAAC,EAAE,MAAM,CAAA;KAAE,CAAC,CAAA;IACvD,UAAU,CAAC,EAAE,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,CAAA;IACpC,WAAW,CAAC,EAAE,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,CAAA;IACrC,QAAQ,CAAC,EAAE,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,CAAA;IAClC,mBAAmB,CAAC,EAAE,MAAM,CAAC,MAAM,EAAE,MAAM,CAAC,CAAA;CAC7C;AA0DD,wBAAgB,0BAA0B,CAAC,IAAI,EAAE,MAAM,EAAE,UAAU,EAAE,4BAA4B,UAUhG;AAED,wBAAgB,kBAAkB,CAChC,IAAI,EAAE,MAAM,EACZ,UAAU,EAAE,4BAA4B,EACxC,KAAK,EAAE,MAAM,UAed;AAED,wBAAgB,gCAAgC,CAAC,OAAO,EAAE,sBAAsB,WAK/E;AAED,wBAAgB,oCAAoC,IAAI,sBAAsB,CAsB7E;AAED,wBAAgB,6CAA6C,CAC3D,OAAO,EAAE,uCAAuC,GAC/C,sBAAsB,CA6CxB;AAED,wBAAgB,oDAAoD,CAClE,GAAG,CAAC,EAAE,MAAM,CAAC,MAAM,EAAE,MAAM,GAAG,SAAS,CAAC,GACvC,sBAAsB,CAkBxB"}
@@ -1,6 +1,7 @@
1
1
  import { renderPdfDocument } from "@voyant-travel/utils/pdf-renderer";
2
2
  import { marked } from "marked";
3
3
  import sanitizeHtml from "sanitize-html";
4
+ const BASIC_PDF_PRODUCT_BROCHURE_PRINTER_KIND = "voyant-basic-pdf";
4
5
  function escapeHtml(value) {
5
6
  return value
6
7
  .replaceAll("&", "&amp;")
@@ -55,17 +56,17 @@ function renderMarkdownBrochureBody(body) {
55
56
  disallowedTagsMode: "discard",
56
57
  });
57
58
  }
58
- export function brochureBodyToHtml(body, bodyFormat, title) {
59
- let content;
59
+ export function brochureBodyToHtmlFragment(body, bodyFormat) {
60
60
  if (bodyFormat === "html") {
61
- content = body;
62
- }
63
- else if (bodyFormat === "markdown") {
64
- content = renderMarkdownBrochureBody(body);
61
+ return body;
65
62
  }
66
- else {
67
- content = `<pre style="white-space: pre-wrap; font-family: system-ui, sans-serif;">${escapeHtml(body)}</pre>`;
63
+ if (bodyFormat === "markdown") {
64
+ return renderMarkdownBrochureBody(body);
68
65
  }
66
+ return `<pre style="white-space: pre-wrap; font-family: system-ui, sans-serif;">${escapeHtml(body)}</pre>`;
67
+ }
68
+ export function brochureBodyToHtml(body, bodyFormat, title) {
69
+ const content = brochureBodyToHtmlFragment(body, bodyFormat);
69
70
  return [
70
71
  "<!doctype html>",
71
72
  '<html lang="en">',
@@ -78,8 +79,12 @@ export function brochureBodyToHtml(body, bodyFormat, title) {
78
79
  "</html>",
79
80
  ].join("");
80
81
  }
82
+ export function isBasicPdfProductBrochurePrinter(printer) {
83
+ return (printer.__voyantProductBrochurePrinterKind ===
84
+ BASIC_PDF_PRODUCT_BROCHURE_PRINTER_KIND);
85
+ }
81
86
  export function createBasicPdfProductBrochurePrinter() {
82
- return async ({ template, context }) => {
87
+ const printer = async ({ template, context }) => {
83
88
  const body = await renderPdfDocument({
84
89
  title: template.title,
85
90
  content: template.body,
@@ -96,6 +101,8 @@ export function createBasicPdfProductBrochurePrinter() {
96
101
  },
97
102
  };
98
103
  };
104
+ printer.__voyantProductBrochurePrinterKind = BASIC_PDF_PRODUCT_BROCHURE_PRINTER_KIND;
105
+ return printer;
99
106
  }
100
107
  export function createCloudflareBrowserProductBrochurePrinter(options) {
101
108
  const apiBaseUrl = options.apiBaseUrl?.replace(/\/$/, "") || "https://api.cloudflare.com/client/v4";
@@ -1,8 +1,10 @@
1
1
  import { type StructuredTemplateBodyFormat } from "@voyant-travel/utils/template-renderer";
2
2
  import type { PostgresJsDatabase } from "drizzle-orm/postgres-js";
3
- import { productDayServices, productDays, products } from "../schema.js";
3
+ import { productDayServices, productDays, productMedia, productPaxPricingTiers, products } from "../schema.js";
4
4
  type ProductDayRecord = typeof productDays.$inferSelect;
5
5
  type ProductDayServiceRecord = typeof productDayServices.$inferSelect;
6
+ type ProductMediaRecord = typeof productMedia.$inferSelect;
7
+ type ProductPaxPricingTierRecord = typeof productPaxPricingTiers.$inferSelect;
6
8
  type ProductRecord = typeof products.$inferSelect;
7
9
  export interface ProductBrochureDayContext extends ProductDayRecord {
8
10
  services: Array<ProductDayServiceRecord>;
@@ -10,6 +12,8 @@ export interface ProductBrochureDayContext extends ProductDayRecord {
10
12
  export interface ProductBrochureTemplateContext {
11
13
  product: ProductRecord;
12
14
  days: ProductBrochureDayContext[];
15
+ media: ProductMediaRecord[];
16
+ pricingTiers: ProductPaxPricingTierRecord[];
13
17
  generatedAt: Date;
14
18
  }
15
19
  type TemplateResolver<T> = T | ((context: ProductBrochureTemplateContext) => Promise<T> | T);
@@ -1 +1 @@
1
- {"version":3,"file":"brochure-templates.d.ts","sourceRoot":"","sources":["../../src/tasks/brochure-templates.ts"],"names":[],"mappings":"AAAA,OAAO,EAEL,KAAK,4BAA4B,EAClC,MAAM,wCAAwC,CAAA;AAE/C,OAAO,KAAK,EAAE,kBAAkB,EAAE,MAAM,yBAAyB,CAAA;AAEjE,OAAO,EAAE,kBAAkB,EAAE,WAAW,EAAsB,QAAQ,EAAE,MAAM,cAAc,CAAA;AAE5F,KAAK,gBAAgB,GAAG,OAAO,WAAW,CAAC,YAAY,CAAA;AACvD,KAAK,uBAAuB,GAAG,OAAO,kBAAkB,CAAC,YAAY,CAAA;AACrE,KAAK,aAAa,GAAG,OAAO,QAAQ,CAAC,YAAY,CAAA;AAEjD,MAAM,WAAW,yBAA0B,SAAQ,gBAAgB;IACjE,QAAQ,EAAE,KAAK,CAAC,uBAAuB,CAAC,CAAA;CACzC;AAED,MAAM,WAAW,8BAA8B;IAC7C,OAAO,EAAE,aAAa,CAAA;IACtB,IAAI,EAAE,yBAAyB,EAAE,CAAA;IACjC,WAAW,EAAE,IAAI,CAAA;CAClB;AAED,KAAK,gBAAgB,CAAC,CAAC,IAAI,CAAC,GAAG,CAAC,CAAC,OAAO,EAAE,8BAA8B,KAAK,OAAO,CAAC,CAAC,CAAC,GAAG,CAAC,CAAC,CAAA;AAE5F,MAAM,WAAW,iCAAiC;IAChD,UAAU,EAAE,4BAA4B,CAAA;IACxC,IAAI,EAAE,gBAAgB,CAAC,MAAM,CAAC,CAAA;IAC9B,SAAS,CAAC,EACN,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,GACvB,CAAC,CACC,OAAO,EAAE,8BAA8B,KACpC,OAAO,CAAC,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,CAAC,GAAG,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,CAAC,CAAA;IACpE,KAAK,CAAC,EAAE,gBAAgB,CAAC,MAAM,CAAC,CAAA;IAChC,QAAQ,CAAC,EAAE,gBAAgB,CAAC,MAAM,CAAC,CAAA;IACnC,aAAa,CAAC,EAAE,gBAAgB,CAAC,MAAM,EAAE,CAAC,CAAA;CAC3C;AAED,MAAM,WAAW,+BAA+B;IAC9C,IAAI,EAAE,MAAM,CAAA;IACZ,UAAU,EAAE,4BAA4B,CAAA;IACxC,KAAK,EAAE,MAAM,CAAA;IACb,QAAQ,EAAE,MAAM,CAAA;IAChB,SAAS,EAAE,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,CAAA;IAClC,aAAa,EAAE,MAAM,EAAE,CAAA;CACxB;AAmBD,wBAAsB,kCAAkC,CACtD,EAAE,EAAE,kBAAkB,EACtB,SAAS,EAAE,MAAM,GAChB,OAAO,CAAC,8BAA8B,CAAC,CA+CzC;AAED,wBAAgB,oCAAoC,IAAI,iCAAiC,CAqCxF;AAED,wBAAsB,6BAA6B,CACjD,QAAQ,EAAE,iCAAiC,EAC3C,OAAO,EAAE,8BAA8B,GACtC,OAAO,CAAC,+BAA+B,CAAC,CAuB1C"}
1
+ {"version":3,"file":"brochure-templates.d.ts","sourceRoot":"","sources":["../../src/tasks/brochure-templates.ts"],"names":[],"mappings":"AAAA,OAAO,EAEL,KAAK,4BAA4B,EAClC,MAAM,wCAAwC,CAAA;AAE/C,OAAO,KAAK,EAAE,kBAAkB,EAAE,MAAM,yBAAyB,CAAA;AAEjE,OAAO,EACL,kBAAkB,EAClB,WAAW,EAEX,YAAY,EACZ,sBAAsB,EACtB,QAAQ,EACT,MAAM,cAAc,CAAA;AAErB,KAAK,gBAAgB,GAAG,OAAO,WAAW,CAAC,YAAY,CAAA;AACvD,KAAK,uBAAuB,GAAG,OAAO,kBAAkB,CAAC,YAAY,CAAA;AACrE,KAAK,kBAAkB,GAAG,OAAO,YAAY,CAAC,YAAY,CAAA;AAC1D,KAAK,2BAA2B,GAAG,OAAO,sBAAsB,CAAC,YAAY,CAAA;AAC7E,KAAK,aAAa,GAAG,OAAO,QAAQ,CAAC,YAAY,CAAA;AAEjD,MAAM,WAAW,yBAA0B,SAAQ,gBAAgB;IACjE,QAAQ,EAAE,KAAK,CAAC,uBAAuB,CAAC,CAAA;CACzC;AAED,MAAM,WAAW,8BAA8B;IAC7C,OAAO,EAAE,aAAa,CAAA;IACtB,IAAI,EAAE,yBAAyB,EAAE,CAAA;IACjC,KAAK,EAAE,kBAAkB,EAAE,CAAA;IAC3B,YAAY,EAAE,2BAA2B,EAAE,CAAA;IAC3C,WAAW,EAAE,IAAI,CAAA;CAClB;AAED,KAAK,gBAAgB,CAAC,CAAC,IAAI,CAAC,GAAG,CAAC,CAAC,OAAO,EAAE,8BAA8B,KAAK,OAAO,CAAC,CAAC,CAAC,GAAG,CAAC,CAAC,CAAA;AAE5F,MAAM,WAAW,iCAAiC;IAChD,UAAU,EAAE,4BAA4B,CAAA;IACxC,IAAI,EAAE,gBAAgB,CAAC,MAAM,CAAC,CAAA;IAC9B,SAAS,CAAC,EACN,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,GACvB,CAAC,CACC,OAAO,EAAE,8BAA8B,KACpC,OAAO,CAAC,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,CAAC,GAAG,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,CAAC,CAAA;IACpE,KAAK,CAAC,EAAE,gBAAgB,CAAC,MAAM,CAAC,CAAA;IAChC,QAAQ,CAAC,EAAE,gBAAgB,CAAC,MAAM,CAAC,CAAA;IACnC,aAAa,CAAC,EAAE,gBAAgB,CAAC,MAAM,EAAE,CAAC,CAAA;CAC3C;AAED,MAAM,WAAW,+BAA+B;IAC9C,IAAI,EAAE,MAAM,CAAA;IACZ,UAAU,EAAE,4BAA4B,CAAA;IACxC,KAAK,EAAE,MAAM,CAAA;IACb,QAAQ,EAAE,MAAM,CAAA;IAChB,SAAS,EAAE,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,CAAA;IAClC,aAAa,EAAE,MAAM,EAAE,CAAA;CACxB;AAmBD,wBAAsB,kCAAkC,CACtD,EAAE,EAAE,kBAAkB,EACtB,SAAS,EAAE,MAAM,GAChB,OAAO,CAAC,8BAA8B,CAAC,CAgEzC;AAED,wBAAgB,oCAAoC,IAAI,iCAAiC,CAqCxF;AAED,wBAAsB,6BAA6B,CACjD,QAAQ,EAAE,iCAAiC,EAC3C,OAAO,EAAE,8BAA8B,GACtC,OAAO,CAAC,+BAA+B,CAAC,CAyB1C"}
@@ -1,6 +1,6 @@
1
1
  import { renderStructuredTemplate, } from "@voyant-travel/utils/template-renderer";
2
2
  import { and, asc, eq } from "drizzle-orm";
3
- import { productDayServices, productDays, productItineraries, products } from "../schema.js";
3
+ import { productDayServices, productDays, productItineraries, productMedia, productPaxPricingTiers, products, } from "../schema.js";
4
4
  async function resolveTemplateValue(value, context) {
5
5
  if (typeof value === "function") {
6
6
  return await value(context);
@@ -17,6 +17,18 @@ export async function loadProductBrochureTemplateContext(db, productId) {
17
17
  if (!product) {
18
18
  throw new Error(`Product not found: ${productId}`);
19
19
  }
20
+ const [media, pricingTiers] = await Promise.all([
21
+ db
22
+ .select()
23
+ .from(productMedia)
24
+ .where(eq(productMedia.productId, productId))
25
+ .orderBy(asc(productMedia.sortOrder), asc(productMedia.createdAt)),
26
+ db
27
+ .select()
28
+ .from(productPaxPricingTiers)
29
+ .where(eq(productPaxPricingTiers.productId, productId))
30
+ .orderBy(asc(productPaxPricingTiers.tierPax), asc(productPaxPricingTiers.createdAt)),
31
+ ]);
20
32
  const [defaultItinerary] = await db
21
33
  .select({ id: productItineraries.id })
22
34
  .from(productItineraries)
@@ -26,6 +38,8 @@ export async function loadProductBrochureTemplateContext(db, productId) {
26
38
  return {
27
39
  product,
28
40
  days: [],
41
+ media,
42
+ pricingTiers,
29
43
  generatedAt: new Date(),
30
44
  };
31
45
  }
@@ -48,6 +62,8 @@ export async function loadProductBrochureTemplateContext(db, productId) {
48
62
  return {
49
63
  product,
50
64
  days: daysWithServices,
65
+ media,
66
+ pricingTiers,
51
67
  generatedAt: new Date(),
52
68
  };
53
69
  }
@@ -94,6 +110,8 @@ export async function renderProductBrochureTemplate(template, context) {
94
110
  const variables = (await resolveTemplateValue(template.variables, context)) ?? {
95
111
  product: context.product,
96
112
  days: context.days,
113
+ media: context.media,
114
+ pricingTiers: context.pricingTiers,
97
115
  generatedAt: context.generatedAt.toISOString(),
98
116
  };
99
117
  const title = (await resolveTemplateValue(template.title, context))?.trim() || context.product.name;
@@ -0,0 +1,13 @@
1
+ type ThemedBrochureStyleTheme = {
2
+ primaryColor: string;
3
+ accentColor: string;
4
+ backgroundColor: string;
5
+ surfaceColor: string;
6
+ textColor: string;
7
+ mutedTextColor: string;
8
+ borderColor: string;
9
+ fontFamily: string;
10
+ };
11
+ export declare function renderThemedBrochureStyles(theme: ThemedBrochureStyleTheme): string;
12
+ export {};
13
+ //# sourceMappingURL=brochure-themed-styles.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"brochure-themed-styles.d.ts","sourceRoot":"","sources":["../../src/tasks/brochure-themed-styles.ts"],"names":[],"mappings":"AAAA,KAAK,wBAAwB,GAAG;IAC9B,YAAY,EAAE,MAAM,CAAA;IACpB,WAAW,EAAE,MAAM,CAAA;IACnB,eAAe,EAAE,MAAM,CAAA;IACvB,YAAY,EAAE,MAAM,CAAA;IACpB,SAAS,EAAE,MAAM,CAAA;IACjB,cAAc,EAAE,MAAM,CAAA;IACtB,WAAW,EAAE,MAAM,CAAA;IACnB,UAAU,EAAE,MAAM,CAAA;CACnB,CAAA;AAED,wBAAgB,0BAA0B,CAAC,KAAK,EAAE,wBAAwB,UAmMzE"}
@@ -0,0 +1,196 @@
1
+ export function renderThemedBrochureStyles(theme) {
2
+ return `
3
+ :root {
4
+ --brand-primary: ${theme.primaryColor};
5
+ --brand-accent: ${theme.accentColor};
6
+ --page-bg: ${theme.backgroundColor};
7
+ --surface: ${theme.surfaceColor};
8
+ --text: ${theme.textColor};
9
+ --muted: ${theme.mutedTextColor};
10
+ --border: ${theme.borderColor};
11
+ --font: ${theme.fontFamily};
12
+ }
13
+ * { box-sizing: border-box; }
14
+ body {
15
+ margin: 0;
16
+ color: var(--text);
17
+ background: var(--page-bg);
18
+ font-family: var(--font);
19
+ line-height: 1.5;
20
+ }
21
+ .brochure-cover {
22
+ display: grid;
23
+ grid-template-columns: minmax(0, 1fr) minmax(20rem, 0.85fr);
24
+ min-height: 82vh;
25
+ background: var(--surface);
26
+ page-break-after: always;
27
+ }
28
+ .cover-image {
29
+ width: 100%;
30
+ height: 100%;
31
+ min-height: 32rem;
32
+ object-fit: cover;
33
+ }
34
+ .cover-copy {
35
+ display: flex;
36
+ flex-direction: column;
37
+ justify-content: center;
38
+ gap: 1.5rem;
39
+ padding: 4rem;
40
+ border-left: 0.5rem solid var(--brand-accent);
41
+ }
42
+ .brand-row {
43
+ display: flex;
44
+ align-items: center;
45
+ gap: 0.75rem;
46
+ color: var(--brand-primary);
47
+ font-size: 0.875rem;
48
+ font-weight: 700;
49
+ letter-spacing: 0;
50
+ text-transform: uppercase;
51
+ }
52
+ .brand-logo {
53
+ width: 2.5rem;
54
+ height: 2.5rem;
55
+ object-fit: contain;
56
+ }
57
+ h1, h2, h3, p { margin-top: 0; }
58
+ h1 {
59
+ margin-bottom: 0;
60
+ color: var(--brand-primary);
61
+ font-size: 3.25rem;
62
+ line-height: 1.05;
63
+ }
64
+ h2 {
65
+ color: var(--brand-primary);
66
+ font-size: 1.75rem;
67
+ line-height: 1.2;
68
+ }
69
+ h3 {
70
+ color: var(--brand-primary);
71
+ font-size: 1.1rem;
72
+ margin-bottom: 0.35rem;
73
+ }
74
+ .dek {
75
+ color: var(--muted);
76
+ font-size: 1.05rem;
77
+ }
78
+ .cover-facts {
79
+ display: grid;
80
+ grid-template-columns: repeat(3, minmax(0, 1fr));
81
+ gap: 1rem;
82
+ margin: 0;
83
+ }
84
+ .cover-facts div {
85
+ padding: 1rem;
86
+ border: 1px solid var(--border);
87
+ border-radius: 0.5rem;
88
+ }
89
+ dt {
90
+ color: var(--muted);
91
+ font-size: 0.75rem;
92
+ font-weight: 700;
93
+ text-transform: uppercase;
94
+ }
95
+ dd {
96
+ margin: 0.25rem 0 0;
97
+ font-weight: 700;
98
+ }
99
+ .brochure-section {
100
+ max-width: 64rem;
101
+ margin: 0 auto;
102
+ padding: 3rem 2rem;
103
+ background: var(--surface);
104
+ border-bottom: 1px solid var(--border);
105
+ }
106
+ .rich-body :is(h1, h2, h3) {
107
+ color: var(--brand-primary);
108
+ }
109
+ .rich-body img {
110
+ max-width: 100%;
111
+ }
112
+ .media-grid {
113
+ display: grid;
114
+ grid-template-columns: repeat(3, minmax(0, 1fr));
115
+ gap: 1rem;
116
+ }
117
+ figure {
118
+ margin: 0;
119
+ border: 1px solid var(--border);
120
+ border-radius: 0.5rem;
121
+ overflow: hidden;
122
+ }
123
+ figure img {
124
+ display: block;
125
+ width: 100%;
126
+ aspect-ratio: 4 / 3;
127
+ object-fit: cover;
128
+ }
129
+ figcaption {
130
+ padding: 0.75rem;
131
+ color: var(--muted);
132
+ font-size: 0.85rem;
133
+ }
134
+ .day {
135
+ display: grid;
136
+ grid-template-columns: 6rem minmax(0, 1fr);
137
+ gap: 1.5rem;
138
+ padding: 1.25rem 0;
139
+ border-top: 1px solid var(--border);
140
+ }
141
+ .day-number {
142
+ color: var(--brand-accent);
143
+ font-weight: 800;
144
+ }
145
+ .muted,
146
+ .day li span {
147
+ color: var(--muted);
148
+ }
149
+ .day ul {
150
+ margin: 1rem 0 0;
151
+ padding-left: 1.25rem;
152
+ }
153
+ table {
154
+ width: 100%;
155
+ border-collapse: collapse;
156
+ }
157
+ th, td {
158
+ padding: 0.75rem;
159
+ border-bottom: 1px solid var(--border);
160
+ text-align: left;
161
+ }
162
+ th {
163
+ color: var(--muted);
164
+ font-size: 0.75rem;
165
+ text-transform: uppercase;
166
+ }
167
+ .brochure-footer {
168
+ max-width: 64rem;
169
+ margin: 0 auto;
170
+ padding: 2rem;
171
+ color: var(--muted);
172
+ font-size: 0.85rem;
173
+ }
174
+ @media (max-width: 760px) {
175
+ .brochure-cover,
176
+ .day {
177
+ grid-template-columns: 1fr;
178
+ }
179
+ .cover-copy {
180
+ padding: 2rem;
181
+ border-left: 0;
182
+ border-top: 0.5rem solid var(--brand-accent);
183
+ }
184
+ h1 {
185
+ font-size: 2.25rem;
186
+ }
187
+ .cover-facts,
188
+ .media-grid {
189
+ grid-template-columns: 1fr;
190
+ }
191
+ }
192
+ @page {
193
+ margin: 18mm;
194
+ }
195
+ `;
196
+ }
@@ -0,0 +1,46 @@
1
+ import { type ProductBrochurePrinter } from "./brochure-printers.js";
2
+ import type { ProductBrochureTemplateContext, RenderedProductBrochureTemplate } from "./brochure-templates.js";
3
+ export interface ThemedBrochureTheme {
4
+ brandName?: string | null;
5
+ logoUrl?: string | null;
6
+ primaryColor?: string | null;
7
+ accentColor?: string | null;
8
+ backgroundColor?: string | null;
9
+ surfaceColor?: string | null;
10
+ textColor?: string | null;
11
+ mutedTextColor?: string | null;
12
+ borderColor?: string | null;
13
+ fontFamily?: string | null;
14
+ footerText?: string | null;
15
+ }
16
+ type ResolvedThemedBrochureTheme = {
17
+ [Key in keyof Required<ThemedBrochureTheme>]: string;
18
+ };
19
+ export interface ThemedBrochureRenderInput {
20
+ template: RenderedProductBrochureTemplate;
21
+ context: ProductBrochureTemplateContext;
22
+ theme: ResolvedThemedBrochureTheme;
23
+ }
24
+ export interface ThemedBrochureSection {
25
+ id: string;
26
+ render: (input: ThemedBrochureRenderInput) => string | null | undefined;
27
+ }
28
+ export interface RenderThemedBrochureHtmlOptions {
29
+ theme?: ThemedBrochureTheme;
30
+ /**
31
+ * Replaces the default brochure section set. Use this for a full redesign
32
+ * while keeping the shared print pipeline.
33
+ */
34
+ sections?: ReadonlyArray<ThemedBrochureSection>;
35
+ /** Appends sections after the default or replacement section set. */
36
+ additionalSections?: ReadonlyArray<ThemedBrochureSection>;
37
+ }
38
+ export interface CreateThemedBrochurePrinterOptions extends RenderThemedBrochureHtmlOptions {
39
+ printer: ProductBrochurePrinter;
40
+ }
41
+ export declare const defaultThemedBrochureSections: ReadonlyArray<ThemedBrochureSection>;
42
+ export declare function renderThemedBrochureHtml(template: RenderedProductBrochureTemplate, context: ProductBrochureTemplateContext, options?: RenderThemedBrochureHtmlOptions): string;
43
+ export declare function createThemedBrochurePrinter(options: CreateThemedBrochurePrinterOptions): ProductBrochurePrinter;
44
+ export declare const createThemedProductBrochurePrinter: typeof createThemedBrochurePrinter;
45
+ export {};
46
+ //# sourceMappingURL=brochure-themed.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"brochure-themed.d.ts","sourceRoot":"","sources":["../../src/tasks/brochure-themed.ts"],"names":[],"mappings":"AAAA,OAAO,EAGL,KAAK,sBAAsB,EAC5B,MAAM,wBAAwB,CAAA;AAC/B,OAAO,KAAK,EACV,8BAA8B,EAC9B,+BAA+B,EAChC,MAAM,yBAAyB,CAAA;AAGhC,MAAM,WAAW,mBAAmB;IAClC,SAAS,CAAC,EAAE,MAAM,GAAG,IAAI,CAAA;IACzB,OAAO,CAAC,EAAE,MAAM,GAAG,IAAI,CAAA;IACvB,YAAY,CAAC,EAAE,MAAM,GAAG,IAAI,CAAA;IAC5B,WAAW,CAAC,EAAE,MAAM,GAAG,IAAI,CAAA;IAC3B,eAAe,CAAC,EAAE,MAAM,GAAG,IAAI,CAAA;IAC/B,YAAY,CAAC,EAAE,MAAM,GAAG,IAAI,CAAA;IAC5B,SAAS,CAAC,EAAE,MAAM,GAAG,IAAI,CAAA;IACzB,cAAc,CAAC,EAAE,MAAM,GAAG,IAAI,CAAA;IAC9B,WAAW,CAAC,EAAE,MAAM,GAAG,IAAI,CAAA;IAC3B,UAAU,CAAC,EAAE,MAAM,GAAG,IAAI,CAAA;IAC1B,UAAU,CAAC,EAAE,MAAM,GAAG,IAAI,CAAA;CAC3B;AAED,KAAK,2BAA2B,GAAG;KAChC,GAAG,IAAI,MAAM,QAAQ,CAAC,mBAAmB,CAAC,GAAG,MAAM;CACrD,CAAA;AAED,MAAM,WAAW,yBAAyB;IACxC,QAAQ,EAAE,+BAA+B,CAAA;IACzC,OAAO,EAAE,8BAA8B,CAAA;IACvC,KAAK,EAAE,2BAA2B,CAAA;CACnC;AAED,MAAM,WAAW,qBAAqB;IACpC,EAAE,EAAE,MAAM,CAAA;IACV,MAAM,EAAE,CAAC,KAAK,EAAE,yBAAyB,KAAK,MAAM,GAAG,IAAI,GAAG,SAAS,CAAA;CACxE;AAED,MAAM,WAAW,+BAA+B;IAC9C,KAAK,CAAC,EAAE,mBAAmB,CAAA;IAC3B;;;OAGG;IACH,QAAQ,CAAC,EAAE,aAAa,CAAC,qBAAqB,CAAC,CAAA;IAC/C,qEAAqE;IACrE,kBAAkB,CAAC,EAAE,aAAa,CAAC,qBAAqB,CAAC,CAAA;CAC1D;AAED,MAAM,WAAW,kCAAmC,SAAQ,+BAA+B;IACzF,OAAO,EAAE,sBAAsB,CAAA;CAChC;AA4OD,eAAO,MAAM,6BAA6B,EAAE,aAAa,CAAC,qBAAqB,CAkB9E,CAAA;AAED,wBAAgB,wBAAwB,CACtC,QAAQ,EAAE,+BAA+B,EACzC,OAAO,EAAE,8BAA8B,EACvC,OAAO,GAAE,+BAAoC,UA4B9C;AAED,wBAAgB,2BAA2B,CACzC,OAAO,EAAE,kCAAkC,GAC1C,sBAAsB,CA0BxB;AAED,eAAO,MAAM,kCAAkC,oCAA8B,CAAA"}
@@ -0,0 +1,280 @@
1
+ import { brochureBodyToHtmlFragment, isBasicPdfProductBrochurePrinter, } from "./brochure-printers.js";
2
+ import { renderThemedBrochureStyles } from "./brochure-themed-styles.js";
3
+ const DEFAULT_THEME = {
4
+ brandName: "Voyant",
5
+ logoUrl: "",
6
+ primaryColor: "#172554",
7
+ accentColor: "#0f766e",
8
+ backgroundColor: "#f8fafc",
9
+ surfaceColor: "#ffffff",
10
+ textColor: "#111827",
11
+ mutedTextColor: "#64748b",
12
+ borderColor: "#dbe3ef",
13
+ fontFamily: 'Inter, ui-sans-serif, system-ui, -apple-system, BlinkMacSystemFont, "Segoe UI", sans-serif',
14
+ footerText: "",
15
+ };
16
+ function escapeHtml(value) {
17
+ return value
18
+ .replaceAll("&", "&amp;")
19
+ .replaceAll("<", "&lt;")
20
+ .replaceAll(">", "&gt;")
21
+ .replaceAll('"', "&quot;")
22
+ .replaceAll("'", "&#39;");
23
+ }
24
+ function safeCssValue(value) {
25
+ return value.replace(/[<>{};]/g, "").trim();
26
+ }
27
+ function resolveTheme(theme) {
28
+ return {
29
+ brandName: theme?.brandName?.trim() || DEFAULT_THEME.brandName,
30
+ logoUrl: theme?.logoUrl?.trim() || DEFAULT_THEME.logoUrl,
31
+ primaryColor: safeCssValue(theme?.primaryColor || DEFAULT_THEME.primaryColor),
32
+ accentColor: safeCssValue(theme?.accentColor || DEFAULT_THEME.accentColor),
33
+ backgroundColor: safeCssValue(theme?.backgroundColor || DEFAULT_THEME.backgroundColor),
34
+ surfaceColor: safeCssValue(theme?.surfaceColor || DEFAULT_THEME.surfaceColor),
35
+ textColor: safeCssValue(theme?.textColor || DEFAULT_THEME.textColor),
36
+ mutedTextColor: safeCssValue(theme?.mutedTextColor || DEFAULT_THEME.mutedTextColor),
37
+ borderColor: safeCssValue(theme?.borderColor || DEFAULT_THEME.borderColor),
38
+ fontFamily: safeCssValue(theme?.fontFamily || DEFAULT_THEME.fontFamily),
39
+ footerText: theme?.footerText?.trim() || DEFAULT_THEME.footerText,
40
+ };
41
+ }
42
+ function safeUrl(value) {
43
+ const trimmed = value?.trim();
44
+ if (!trimmed)
45
+ return null;
46
+ if (trimmed.startsWith("/") || trimmed.startsWith("#")) {
47
+ return trimmed;
48
+ }
49
+ try {
50
+ const url = new URL(trimmed);
51
+ return url.protocol === "http:" || url.protocol === "https:" ? trimmed : null;
52
+ }
53
+ catch {
54
+ return null;
55
+ }
56
+ }
57
+ function formatDate(value) {
58
+ if (!value)
59
+ return null;
60
+ const date = value instanceof Date ? value : new Date(value);
61
+ if (Number.isNaN(date.valueOf()))
62
+ return null;
63
+ return new Intl.DateTimeFormat("en", {
64
+ month: "short",
65
+ day: "numeric",
66
+ year: "numeric",
67
+ timeZone: "UTC",
68
+ }).format(date);
69
+ }
70
+ function formatMoney(amountCents, currency) {
71
+ if (amountCents == null || !currency)
72
+ return null;
73
+ return new Intl.NumberFormat("en", {
74
+ style: "currency",
75
+ currency,
76
+ }).format(amountCents / 100);
77
+ }
78
+ function renderCoverSection({ context, theme }) {
79
+ const { product } = context;
80
+ const cover = context.media.find((item) => item.mediaType === "image" && item.isCover);
81
+ const coverUrl = safeUrl(cover?.url);
82
+ const logoUrl = safeUrl(theme.logoUrl);
83
+ const dates = [formatDate(product.startDate), formatDate(product.endDate)]
84
+ .filter(Boolean)
85
+ .join(" - ");
86
+ const price = formatMoney(product.sellAmountCents, product.sellCurrency);
87
+ return [
88
+ '<section class="brochure-cover">',
89
+ coverUrl
90
+ ? `<img class="cover-image" src="${escapeHtml(coverUrl)}" alt="${escapeHtml(cover?.altText || cover?.name || product.name)}" />`
91
+ : "",
92
+ '<div class="cover-copy">',
93
+ '<div class="brand-row">',
94
+ logoUrl
95
+ ? `<img class="brand-logo" src="${escapeHtml(logoUrl)}" alt="${escapeHtml(theme.brandName)}" />`
96
+ : "",
97
+ `<span>${escapeHtml(theme.brandName)}</span>`,
98
+ "</div>",
99
+ `<h1>${escapeHtml(product.name)}</h1>`,
100
+ product.description ? `<p class="dek">${escapeHtml(product.description)}</p>` : "",
101
+ '<dl class="cover-facts">',
102
+ dates ? `<div><dt>Dates</dt><dd>${escapeHtml(dates)}</dd></div>` : "",
103
+ product.pax ? `<div><dt>Travelers</dt><dd>${escapeHtml(String(product.pax))}</dd></div>` : "",
104
+ price ? `<div><dt>From</dt><dd>${escapeHtml(price)}</dd></div>` : "",
105
+ "</dl>",
106
+ "</div>",
107
+ "</section>",
108
+ ].join("");
109
+ }
110
+ function renderOverviewSection({ template }) {
111
+ const body = brochureBodyToHtmlFragment(template.body, template.bodyFormat);
112
+ if (!body.trim())
113
+ return null;
114
+ return [
115
+ '<section class="brochure-section overview">',
116
+ "<h2>Overview</h2>",
117
+ `<div class="rich-body">${body}</div>`,
118
+ "</section>",
119
+ ].join("");
120
+ }
121
+ function renderMediaSection({ context }) {
122
+ const images = context.media
123
+ .filter((item) => item.mediaType === "image" && !item.isBrochure)
124
+ .map((item) => ({ item, url: safeUrl(item.url) }))
125
+ .filter((entry) => Boolean(entry.url));
126
+ if (images.length === 0)
127
+ return null;
128
+ return [
129
+ '<section class="brochure-section media-grid-section">',
130
+ "<h2>Gallery</h2>",
131
+ '<div class="media-grid">',
132
+ ...images
133
+ .slice(0, 6)
134
+ .map(({ item, url }) => `<figure><img src="${escapeHtml(url)}" alt="${escapeHtml(item.altText || item.name)}" /><figcaption>${escapeHtml(item.name)}</figcaption></figure>`),
135
+ "</div>",
136
+ "</section>",
137
+ ].join("");
138
+ }
139
+ function renderItinerarySection({ context }) {
140
+ if (context.days.length === 0)
141
+ return null;
142
+ return [
143
+ '<section class="brochure-section itinerary">',
144
+ "<h2>Itinerary</h2>",
145
+ ...context.days.map((day) => [
146
+ '<article class="day">',
147
+ `<div class="day-number">Day ${escapeHtml(String(day.dayNumber))}</div>`,
148
+ '<div class="day-content">',
149
+ `<h3>${escapeHtml(day.title || day.location || `Day ${day.dayNumber}`)}</h3>`,
150
+ day.location ? `<p class="muted">${escapeHtml(day.location)}</p>` : "",
151
+ day.description ? `<p>${escapeHtml(day.description)}</p>` : "",
152
+ day.services.length > 0
153
+ ? [
154
+ "<ul>",
155
+ ...day.services.map((service) => [
156
+ "<li>",
157
+ `<strong>${escapeHtml(service.name)}</strong>`,
158
+ ` <span>${escapeHtml(service.serviceType)}</span>`,
159
+ service.quantity > 1
160
+ ? ` <span>x${escapeHtml(String(service.quantity))}</span>`
161
+ : "",
162
+ service.notes ? `<p>${escapeHtml(service.notes)}</p>` : "",
163
+ "</li>",
164
+ ].join("")),
165
+ "</ul>",
166
+ ].join("")
167
+ : "",
168
+ "</div>",
169
+ "</article>",
170
+ ].join("")),
171
+ "</section>",
172
+ ].join("");
173
+ }
174
+ function renderPricingSection({ context }) {
175
+ if (context.pricingTiers.length === 0)
176
+ return null;
177
+ return [
178
+ '<section class="brochure-section pricing">',
179
+ "<h2>Pricing</h2>",
180
+ "<table>",
181
+ "<thead><tr><th>Occupancy</th><th>Price per traveler</th><th>Promotional price</th><th>Valid</th></tr></thead>",
182
+ "<tbody>",
183
+ ...context.pricingTiers.map((tier) => {
184
+ const effective = [formatDate(tier.effectiveFrom), formatDate(tier.effectiveTo)]
185
+ .filter(Boolean)
186
+ .join(" - ");
187
+ return [
188
+ "<tr>",
189
+ `<td>${escapeHtml(String(tier.tierPax))}</td>`,
190
+ `<td>${escapeHtml(formatMoney(tier.pricePerPaxCents, context.product.sellCurrency) ?? "On request")}</td>`,
191
+ `<td>${escapeHtml(formatMoney(tier.promoPricePerPaxCents, context.product.sellCurrency) ?? "-")}</td>`,
192
+ `<td>${escapeHtml(effective || "Always")}</td>`,
193
+ "</tr>",
194
+ ].join("");
195
+ }),
196
+ "</tbody>",
197
+ "</table>",
198
+ "</section>",
199
+ ].join("");
200
+ }
201
+ function renderHtmlListSection(title, html) {
202
+ if (!html?.trim())
203
+ return null;
204
+ return [
205
+ '<section class="brochure-section policy">',
206
+ `<h2>${escapeHtml(title)}</h2>`,
207
+ `<div class="rich-body">${brochureBodyToHtmlFragment(html, "markdown")}</div>`,
208
+ "</section>",
209
+ ].join("");
210
+ }
211
+ export const defaultThemedBrochureSections = [
212
+ { id: "cover", render: renderCoverSection },
213
+ { id: "overview", render: renderOverviewSection },
214
+ { id: "media", render: renderMediaSection },
215
+ { id: "itinerary", render: renderItinerarySection },
216
+ { id: "pricing", render: renderPricingSection },
217
+ {
218
+ id: "inclusions",
219
+ render: ({ context }) => renderHtmlListSection("Inclusions", context.product.inclusionsHtml),
220
+ },
221
+ {
222
+ id: "exclusions",
223
+ render: ({ context }) => renderHtmlListSection("Exclusions", context.product.exclusionsHtml),
224
+ },
225
+ {
226
+ id: "terms",
227
+ render: ({ context }) => renderHtmlListSection("Terms", context.product.termsHtml),
228
+ },
229
+ ];
230
+ export function renderThemedBrochureHtml(template, context, options = {}) {
231
+ const theme = resolveTheme(options.theme);
232
+ const sections = [
233
+ ...(options.sections ?? defaultThemedBrochureSections),
234
+ ...(options.additionalSections ?? []),
235
+ ];
236
+ const input = { template, context, theme };
237
+ const content = sections
238
+ .map((section) => section.render(input))
239
+ .filter((section) => Boolean(section?.trim()))
240
+ .join("");
241
+ const footer = theme.footerText
242
+ ? `<footer class="brochure-footer">${escapeHtml(theme.footerText)}</footer>`
243
+ : "";
244
+ return [
245
+ "<!doctype html>",
246
+ '<html lang="en">',
247
+ "<head>",
248
+ '<meta charset="utf-8" />',
249
+ `<title>${escapeHtml(template.title)}</title>`,
250
+ '<meta name="viewport" content="width=device-width, initial-scale=1" />',
251
+ `<style>${renderThemedBrochureStyles(theme)}</style>`,
252
+ "</head>",
253
+ `<body>${content}${footer}</body>`,
254
+ "</html>",
255
+ ].join("");
256
+ }
257
+ export function createThemedBrochurePrinter(options) {
258
+ if (isBasicPdfProductBrochurePrinter(options.printer)) {
259
+ throw new Error("createThemedBrochurePrinter requires an HTML-capable browser printer. The built-in basic PDF printer strips HTML tags and cannot render themed brochure styles.");
260
+ }
261
+ return async ({ template, context }) => {
262
+ const html = renderThemedBrochureHtml(template, context, options);
263
+ const printed = await options.printer({
264
+ template: {
265
+ ...template,
266
+ body: html,
267
+ bodyFormat: "html",
268
+ },
269
+ context,
270
+ });
271
+ return {
272
+ ...printed,
273
+ metadata: {
274
+ ...printed.metadata,
275
+ layout: "themed-brochure",
276
+ },
277
+ };
278
+ };
279
+ }
280
+ export const createThemedProductBrochurePrinter = createThemedBrochurePrinter;
@@ -1,5 +1,6 @@
1
- export { brochureBodyToHtml, type CloudflareBrowserBrochurePrinterOptions, createBasicPdfProductBrochurePrinter, createCloudflareBrowserProductBrochurePrinter, createCloudflareBrowserProductBrochurePrinterFromEnv, type PrintedProductBrochureArtifact, type ProductBrochurePrinter, type ProductBrochurePrinterContext, } from "./brochure-printers.js";
1
+ export { brochureBodyToHtml, brochureBodyToHtmlFragment, type CloudflareBrowserBrochurePrinterOptions, createBasicPdfProductBrochurePrinter, createCloudflareBrowserProductBrochurePrinter, createCloudflareBrowserProductBrochurePrinterFromEnv, isBasicPdfProductBrochurePrinter, type PrintedProductBrochureArtifact, type ProductBrochurePrinter, type ProductBrochurePrinterContext, } from "./brochure-printers.js";
2
2
  export { createDefaultProductBrochureTemplate, loadProductBrochureTemplateContext, type ProductBrochureDayContext, type ProductBrochureTemplateContext, type ProductBrochureTemplateDefinition, type RenderedProductBrochureTemplate, renderProductBrochureTemplate, } from "./brochure-templates.js";
3
+ export { type CreateThemedBrochurePrinterOptions, createThemedBrochurePrinter, createThemedProductBrochurePrinter, defaultThemedBrochureSections, type RenderThemedBrochureHtmlOptions, renderThemedBrochureHtml, type ThemedBrochureRenderInput, type ThemedBrochureSection, type ThemedBrochureTheme, } from "./brochure-themed.js";
3
4
  export { type GenerateAndStoreProductBrochureOptions, generateAndStoreProductBrochure, } from "./brochures.js";
4
5
  export { type GenerateProductPdfResult, generateProductPdf } from "./generate-pdf.js";
5
6
  //# sourceMappingURL=index.d.ts.map
@@ -1 +1 @@
1
- {"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../../src/tasks/index.ts"],"names":[],"mappings":"AAAA,OAAO,EACL,kBAAkB,EAClB,KAAK,uCAAuC,EAC5C,oCAAoC,EACpC,6CAA6C,EAC7C,oDAAoD,EACpD,KAAK,8BAA8B,EACnC,KAAK,sBAAsB,EAC3B,KAAK,6BAA6B,GACnC,MAAM,wBAAwB,CAAA;AAC/B,OAAO,EACL,oCAAoC,EACpC,kCAAkC,EAClC,KAAK,yBAAyB,EAC9B,KAAK,8BAA8B,EACnC,KAAK,iCAAiC,EACtC,KAAK,+BAA+B,EACpC,6BAA6B,GAC9B,MAAM,yBAAyB,CAAA;AAChC,OAAO,EACL,KAAK,sCAAsC,EAC3C,+BAA+B,GAChC,MAAM,gBAAgB,CAAA;AACvB,OAAO,EAAE,KAAK,wBAAwB,EAAE,kBAAkB,EAAE,MAAM,mBAAmB,CAAA"}
1
+ {"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../../src/tasks/index.ts"],"names":[],"mappings":"AAAA,OAAO,EACL,kBAAkB,EAClB,0BAA0B,EAC1B,KAAK,uCAAuC,EAC5C,oCAAoC,EACpC,6CAA6C,EAC7C,oDAAoD,EACpD,gCAAgC,EAChC,KAAK,8BAA8B,EACnC,KAAK,sBAAsB,EAC3B,KAAK,6BAA6B,GACnC,MAAM,wBAAwB,CAAA;AAC/B,OAAO,EACL,oCAAoC,EACpC,kCAAkC,EAClC,KAAK,yBAAyB,EAC9B,KAAK,8BAA8B,EACnC,KAAK,iCAAiC,EACtC,KAAK,+BAA+B,EACpC,6BAA6B,GAC9B,MAAM,yBAAyB,CAAA;AAChC,OAAO,EACL,KAAK,kCAAkC,EACvC,2BAA2B,EAC3B,kCAAkC,EAClC,6BAA6B,EAC7B,KAAK,+BAA+B,EACpC,wBAAwB,EACxB,KAAK,yBAAyB,EAC9B,KAAK,qBAAqB,EAC1B,KAAK,mBAAmB,GACzB,MAAM,sBAAsB,CAAA;AAC7B,OAAO,EACL,KAAK,sCAAsC,EAC3C,+BAA+B,GAChC,MAAM,gBAAgB,CAAA;AACvB,OAAO,EAAE,KAAK,wBAAwB,EAAE,kBAAkB,EAAE,MAAM,mBAAmB,CAAA"}
@@ -1,4 +1,5 @@
1
- export { brochureBodyToHtml, createBasicPdfProductBrochurePrinter, createCloudflareBrowserProductBrochurePrinter, createCloudflareBrowserProductBrochurePrinterFromEnv, } from "./brochure-printers.js";
1
+ export { brochureBodyToHtml, brochureBodyToHtmlFragment, createBasicPdfProductBrochurePrinter, createCloudflareBrowserProductBrochurePrinter, createCloudflareBrowserProductBrochurePrinterFromEnv, isBasicPdfProductBrochurePrinter, } from "./brochure-printers.js";
2
2
  export { createDefaultProductBrochureTemplate, loadProductBrochureTemplateContext, renderProductBrochureTemplate, } from "./brochure-templates.js";
3
+ export { createThemedBrochurePrinter, createThemedProductBrochurePrinter, defaultThemedBrochureSections, renderThemedBrochureHtml, } from "./brochure-themed.js";
3
4
  export { generateAndStoreProductBrochure, } from "./brochures.js";
4
5
  export { generateProductPdf } from "./generate-pdf.js";
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@voyant-travel/inventory",
3
- "version": "0.3.9",
3
+ "version": "0.4.0",
4
4
  "license": "Apache-2.0",
5
5
  "type": "module",
6
6
  "exports": {
@@ -163,16 +163,16 @@
163
163
  "sanitize-html": "^2.17.4",
164
164
  "zod": "^4.3.6",
165
165
  "@voyant-travel/action-ledger": "^0.105.3",
166
- "@voyant-travel/catalog": "^0.125.0",
166
+ "@voyant-travel/catalog": "^0.126.0",
167
167
  "@voyant-travel/core": "^0.110.0",
168
168
  "@voyant-travel/db": "^0.108.4",
169
169
  "@voyant-travel/extras-contracts": "^0.104.2",
170
170
  "@voyant-travel/hono": "^0.112.2",
171
- "@voyant-travel/commerce": "^0.9.0",
171
+ "@voyant-travel/commerce": "^0.10.0",
172
172
  "@voyant-travel/products-contracts": "^0.105.7",
173
173
  "@voyant-travel/storage": "^0.105.0",
174
174
  "@voyant-travel/utils": "^0.105.2",
175
- "@voyant-travel/operations": "^0.2.0"
175
+ "@voyant-travel/operations": "^0.2.1"
176
176
  },
177
177
  "devDependencies": {
178
178
  "@types/sanitize-html": "^2.16.1",
@@ -206,7 +206,7 @@
206
206
  "test": "vitest run",
207
207
  "build": "tsc -p tsconfig.json",
208
208
  "clean": "rm -rf dist tsconfig.tsbuildinfo",
209
- "db:generate": "drizzle-kit generate --config=drizzle.migrations.config.ts --name=inventory_baseline && node ../../scripts/d2/guard-create-type.mjs ./migrations && node ../../scripts/d2/ensure-extensions.mjs ./migrations"
209
+ "db:generate": "drizzle-kit generate --config=drizzle.migrations.config.ts --name=inventory_baseline && node ../../scripts/migrations/guard-create-type.mjs ./migrations && node ../../scripts/migrations/ensure-extensions.mjs ./migrations"
210
210
  },
211
211
  "main": "./dist/index.js",
212
212
  "types": "./dist/index.d.ts"