@voyantjs/products 0.52.0 → 0.52.2

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.
@@ -1 +1 @@
1
- {"version":3,"file":"catalog-policy.d.ts","sourceRoot":"","sources":["../src/catalog-policy.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;;;;GAkBG;AAEH,OAAO,EAAqB,KAAK,gBAAgB,EAAE,MAAM,4BAA4B,CAAA;AAErF;;;GAGG;AACH,QAAA,MAAM,oBAAoB,EAAE,gBAAgB,EAshB3C,CAAA;AAED;;;;GAIG;AACH,eAAO,MAAM,oBAAoB,2CAA0C,CAAA;AAE3E,OAAO,EAAE,oBAAoB,EAAE,CAAA"}
1
+ {"version":3,"file":"catalog-policy.d.ts","sourceRoot":"","sources":["../src/catalog-policy.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;;;;GAkBG;AAEH,OAAO,EAAqB,KAAK,gBAAgB,EAAE,MAAM,4BAA4B,CAAA;AAErF;;;GAGG;AACH,QAAA,MAAM,oBAAoB,EAAE,gBAAgB,EAwiB3C,CAAA;AAED;;;;GAIG;AACH,eAAO,MAAM,oBAAoB,2CAA0C,CAAA;AAE3E,OAAO,EAAE,oBAAoB,EAAE,CAAA"}
@@ -377,6 +377,24 @@ const PRODUCT_FIELD_POLICY = [
377
377
  overrideFriction: "none",
378
378
  sourceFreshness: "sync",
379
379
  },
380
+ {
381
+ path: "availableDeparturesCount",
382
+ class: "merchandisable",
383
+ merge: "source-only",
384
+ // High-drift because the count moves whenever availability_slots are
385
+ // added/removed or close-outs change; reindex-on-entry keeps it warm
386
+ // enough for the operator catalog table without being load-bearing
387
+ // for downstream booking flows.
388
+ drift: "high",
389
+ reindex: "facet-affecting",
390
+ snapshot: "never",
391
+ query: "indexed-column",
392
+ localized: false,
393
+ visibility: ["staff", "customer", "partner"],
394
+ editRole: "none",
395
+ overrideFriction: "none",
396
+ sourceFreshness: "sync",
397
+ },
380
398
  {
381
399
  path: "durationDays",
382
400
  class: "structural",
@@ -77,6 +77,7 @@ export declare const productDaySchema: z.ZodObject<{
77
77
  title: z.ZodOptional<z.ZodNullable<z.ZodString>>;
78
78
  description: z.ZodOptional<z.ZodNullable<z.ZodString>>;
79
79
  location: z.ZodOptional<z.ZodNullable<z.ZodString>>;
80
+ hero_image_url: z.ZodOptional<z.ZodNullable<z.ZodString>>;
80
81
  services: z.ZodDefault<z.ZodOptional<z.ZodArray<z.ZodString>>>;
81
82
  }, z.core.$strip>;
82
83
  export declare const productPolicySchema: z.ZodObject<{
@@ -150,6 +151,7 @@ export declare const productContentSchema: z.ZodObject<{
150
151
  title: z.ZodOptional<z.ZodNullable<z.ZodString>>;
151
152
  description: z.ZodOptional<z.ZodNullable<z.ZodString>>;
152
153
  location: z.ZodOptional<z.ZodNullable<z.ZodString>>;
154
+ hero_image_url: z.ZodOptional<z.ZodNullable<z.ZodString>>;
153
155
  services: z.ZodDefault<z.ZodOptional<z.ZodArray<z.ZodString>>>;
154
156
  }, z.core.$strip>>>;
155
157
  media: z.ZodDefault<z.ZodArray<z.ZodObject<{
@@ -1 +1 @@
1
- {"version":3,"file":"content-shape.d.ts","sourceRoot":"","sources":["../src/content-shape.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;GAeG;AAEH,OAAO,EACL,KAAK,cAAc,EACnB,KAAK,oBAAoB,EAE1B,MAAM,mBAAmB,CAAA;AAC1B,OAAO,EAAE,CAAC,EAAE,MAAM,KAAK,CAAA;AAEvB;;;GAGG;AACH,eAAO,MAAM,+BAA+B,gBAAgB,CAAA;AAE5D;;;;GAIG;AACH,eAAO,MAAM,oBAAoB;;;;;;;;;;;;;;;iBAe/B,CAAA;AAEF,eAAO,MAAM,sBAAsB;;;;;;;;;iBAKjC,CAAA;AAEF,eAAO,MAAM,uBAAuB;;;;;;iBAMlC,CAAA;AAEF,eAAO,MAAM,mBAAmB;;;;;;;;;;;;iBAM9B,CAAA;AAEF,eAAO,MAAM,gBAAgB;;;;;;iBAM3B,CAAA;AAEF,eAAO,MAAM,mBAAmB;;;;;;;;;iBAK9B,CAAA;AAEF;;;;;;;;;GASG;AACH,eAAO,MAAM,sBAAsB;;;;;;;;;;iBAejC,CAAA;AAEF;;;;GAIG;AACH,eAAO,MAAM,oBAAoB;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;iBAO/B,CAAA;AAEF,MAAM,MAAM,cAAc,GAAG,CAAC,CAAC,KAAK,CAAC,OAAO,oBAAoB,CAAC,CAAA;AACjE,MAAM,MAAM,cAAc,GAAG,CAAC,CAAC,KAAK,CAAC,OAAO,oBAAoB,CAAC,CAAA;AACjE,MAAM,MAAM,gBAAgB,GAAG,CAAC,CAAC,KAAK,CAAC,OAAO,sBAAsB,CAAC,CAAA;AACrE,MAAM,MAAM,aAAa,GAAG,CAAC,CAAC,KAAK,CAAC,OAAO,mBAAmB,CAAC,CAAA;AAC/D,MAAM,MAAM,gBAAgB,GAAG,CAAC,CAAC,KAAK,CAAC,OAAO,sBAAsB,CAAC,CAAA;AACrE,MAAM,MAAM,UAAU,GAAG,CAAC,CAAC,KAAK,CAAC,OAAO,gBAAgB,CAAC,CAAA;AACzD,MAAM,MAAM,aAAa,GAAG,CAAC,CAAC,KAAK,CAAC,OAAO,mBAAmB,CAAC,CAAA;AAE/D;;;;GAIG;AACH,wBAAgB,sBAAsB,CACpC,OAAO,EAAE,OAAO,GACf;IAAE,KAAK,EAAE,IAAI,CAAC;IAAC,OAAO,EAAE,cAAc,CAAA;CAAE,GAAG;IAAE,KAAK,EAAE,KAAK,CAAC;IAAC,MAAM,EAAE,MAAM,CAAA;CAAE,CAY7E;AAED;;;;;;;;;GASG;AACH,wBAAgB,+BAA+B,CAC7C,OAAO,EAAE,cAAc,EACvB,QAAQ,EAAE,aAAa,CAAC,cAAc,CAAC,EACvC,OAAO,GAAE,IAAI,CAAC,oBAAoB,EAAE,gBAAgB,CAAM,GACzD,cAAc,CAchB"}
1
+ {"version":3,"file":"content-shape.d.ts","sourceRoot":"","sources":["../src/content-shape.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;GAeG;AAEH,OAAO,EACL,KAAK,cAAc,EACnB,KAAK,oBAAoB,EAE1B,MAAM,mBAAmB,CAAA;AAC1B,OAAO,EAAE,CAAC,EAAE,MAAM,KAAK,CAAA;AAEvB;;;GAGG;AACH,eAAO,MAAM,+BAA+B,gBAAgB,CAAA;AAE5D;;;;GAIG;AACH,eAAO,MAAM,oBAAoB;;;;;;;;;;;;;;;iBAe/B,CAAA;AAEF,eAAO,MAAM,sBAAsB;;;;;;;;;iBAKjC,CAAA;AAEF,eAAO,MAAM,uBAAuB;;;;;;iBAMlC,CAAA;AAEF,eAAO,MAAM,mBAAmB;;;;;;;;;;;;iBAM9B,CAAA;AAEF,eAAO,MAAM,gBAAgB;;;;;;;iBAQ3B,CAAA;AAEF,eAAO,MAAM,mBAAmB;;;;;;;;;iBAK9B,CAAA;AAEF;;;;;;;;;GASG;AACH,eAAO,MAAM,sBAAsB;;;;;;;;;;iBAejC,CAAA;AAEF;;;;GAIG;AACH,eAAO,MAAM,oBAAoB;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;iBAO/B,CAAA;AAEF,MAAM,MAAM,cAAc,GAAG,CAAC,CAAC,KAAK,CAAC,OAAO,oBAAoB,CAAC,CAAA;AACjE,MAAM,MAAM,cAAc,GAAG,CAAC,CAAC,KAAK,CAAC,OAAO,oBAAoB,CAAC,CAAA;AACjE,MAAM,MAAM,gBAAgB,GAAG,CAAC,CAAC,KAAK,CAAC,OAAO,sBAAsB,CAAC,CAAA;AACrE,MAAM,MAAM,aAAa,GAAG,CAAC,CAAC,KAAK,CAAC,OAAO,mBAAmB,CAAC,CAAA;AAC/D,MAAM,MAAM,gBAAgB,GAAG,CAAC,CAAC,KAAK,CAAC,OAAO,sBAAsB,CAAC,CAAA;AACrE,MAAM,MAAM,UAAU,GAAG,CAAC,CAAC,KAAK,CAAC,OAAO,gBAAgB,CAAC,CAAA;AACzD,MAAM,MAAM,aAAa,GAAG,CAAC,CAAC,KAAK,CAAC,OAAO,mBAAmB,CAAC,CAAA;AAE/D;;;;GAIG;AACH,wBAAgB,sBAAsB,CACpC,OAAO,EAAE,OAAO,GACf;IAAE,KAAK,EAAE,IAAI,CAAC;IAAC,OAAO,EAAE,cAAc,CAAA;CAAE,GAAG;IAAE,KAAK,EAAE,KAAK,CAAC;IAAC,MAAM,EAAE,MAAM,CAAA;CAAE,CAY7E;AAED;;;;;;;;;GASG;AACH,wBAAgB,+BAA+B,CAC7C,OAAO,EAAE,cAAc,EACvB,QAAQ,EAAE,aAAa,CAAC,cAAc,CAAC,EACvC,OAAO,GAAE,IAAI,CAAC,oBAAoB,EAAE,gBAAgB,CAAM,GACzD,cAAc,CAchB"}
@@ -67,6 +67,8 @@ export const productDaySchema = z.object({
67
67
  title: z.string().nullable().optional(),
68
68
  description: z.string().nullable().optional(),
69
69
  location: z.string().nullable().optional(),
70
+ /** Day-level hero image (catalog detail sheet thumbnail). */
71
+ hero_image_url: z.string().nullable().optional(),
70
72
  services: z.array(z.string()).optional().default([]),
71
73
  });
72
74
  export const productPolicySchema = z.object({
@@ -1 +1 @@
1
- {"version":3,"file":"service-catalog-plane.d.ts","sourceRoot":"","sources":["../src/service-catalog-plane.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;;;;;GAmBG;AAEH,OAAO,EAGL,KAAK,oBAAoB,EAEzB,KAAK,eAAe,EACpB,KAAK,eAAe,EACpB,KAAK,WAAW,EAChB,KAAK,mBAAmB,EACxB,KAAK,eAAe,EACpB,KAAK,YAAY,EACjB,KAAK,YAAY,EACjB,KAAK,UAAU,EACf,KAAK,YAAY,EACjB,KAAK,aAAa,EAElB,KAAK,UAAU,EAChB,MAAM,mBAAmB,CAAA;AAC1B,OAAO,KAAK,EAAE,YAAY,EAAE,MAAM,cAAc,CAAA;AAIhD,OAAO,EAAE,QAAQ,EAAE,MAAM,kBAAkB,CAAA;AAgB3C;;;;;;;;;;GAUG;AACH,wBAAgB,sBAAsB,CACpC,GAAG,EAAE,OAAO,QAAQ,CAAC,YAAY,EACjC,OAAO,EAAE;IAAE,gBAAgB,EAAE,MAAM,CAAA;CAAE,GACpC,WAAW,CAAC,MAAM,EAAE,OAAO,CAAC,CA0C9B;AAED;;;;;GAKG;AACH,wBAAgB,iBAAiB,CAC/B,IAAI,EAAE,OAAO,QAAQ,CAAC,YAAY,EAClC,QAAQ,EAAE;IAAE,gBAAgB,EAAE,MAAM,CAAA;CAAE,GACrC,UAAU,CAKZ;AAED,8EAA8E;AAC9E,MAAM,WAAW,qBAAqB;IACpC,iFAAiF;IACjF,gBAAgB,EAAE,MAAM,CAAA;IACxB,qCAAqC;IACrC,KAAK,EAAE,aAAa,CAAA;CACrB;AAED;;;;;;;;GAQG;AACH,wBAAsB,sBAAsB,CAC1C,EAAE,EAAE,YAAY,EAChB,EAAE,EAAE,MAAM,EACV,OAAO,EAAE,qBAAqB,GAC7B,OAAO,CAAC,YAAY,GAAG,IAAI,CAAC,CAS9B;AAED;;;;;;;;;;;;;;;;;;GAkBG;AACH,wBAAsB,oBAAoB,CACxC,EAAE,EAAE,YAAY,EAChB,IAAI,EAAE,aAAa,CAAC,OAAO,QAAQ,CAAC,YAAY,CAAC,EACjD,OAAO,EAAE,qBAAqB,GAC7B,OAAO,CAAC,YAAY,EAAE,CAAC,CAkBzB;AAED;;;;;;;;;;;;;;GAcG;AACH,wBAAsB,yBAAyB,CAC7C,EAAE,EAAE,YAAY,EAChB,SAAS,EAAE,MAAM,EACjB,OAAO,EAAE,qBAAqB,GAAG;IAAE,YAAY,CAAC,EAAE,YAAY,CAAA;CAAE,GAC/D,OAAO,CAAC,IAAI,CAAC,oBAAoB,EAAE,WAAW,CAAC,GAAG,IAAI,CAAC,CASzD;AAMD;;;;;;;;;;;;;;GAcG;AACH,MAAM,WAAW,0BAA0B;IACzC,0DAA0D;IAC1D,QAAQ,CAAC,IAAI,EAAE,MAAM,CAAA;IACrB;;;;OAIG;IACH,OAAO,CACL,EAAE,EAAE,YAAY,EAChB,SAAS,EAAE,MAAM,EACjB,KAAK,EAAE,YAAY,GAClB,OAAO,CAAC,WAAW,CAAC,MAAM,EAAE,OAAO,CAAC,CAAC,CAAA;CACzC;AAED;;;;GAIG;AACH,wBAAgB,sBAAsB,CACpC,GAAG,iBAAiB,EAAE,aAAa,CAAC,aAAa,CAAC,WAAW,CAAC,CAAC,GAC9D,mBAAmB,CAOrB;AAED;;;;;;;;;;;GAWG;AACH,wBAAgB,4BAA4B,CAAC,OAAO,EAAE;IACpD,gBAAgB,EAAE,MAAM,CAAA;IACxB,QAAQ,CAAC,EAAE,mBAAmB,CAAA;CAC/B,GAAG,eAAe,CAAC,OAAO,QAAQ,CAAC,YAAY,CAAC,CAWhD;AAiBD;;;;;;;;;;;;;;;GAeG;AACH,wBAAgB,4BAA4B,CAC1C,EAAE,EAAE,YAAY,EAChB,OAAO,EAAE;IACP,gBAAgB,EAAE,MAAM,CAAA;IACxB,UAAU,CAAC,EAAE,aAAa,CAAC,0BAA0B,CAAC,CAAA;IACtD,QAAQ,CAAC,EAAE,mBAAmB,CAAA;CAC/B,GACA,eAAe,CA2BjB;AAED;;;;GAIG;AACH,wBAAgB,8CAA8C,IAAI,0BAA0B,CAuE3F;AAsCD;;GAEG;AACH,YAAY,EACV,oBAAoB,EACpB,eAAe,EACf,eAAe,EACf,eAAe,EACf,YAAY,EACZ,YAAY,EACZ,UAAU,EACV,YAAY,EACZ,aAAa,EACb,UAAU,GACX,CAAA"}
1
+ {"version":3,"file":"service-catalog-plane.d.ts","sourceRoot":"","sources":["../src/service-catalog-plane.ts"],"names":[],"mappings":"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"}
@@ -19,7 +19,7 @@
19
19
  * in their own service-catalog-plane.ts files).
20
20
  */
21
21
  import { buildIndexerDocument, buildSnapshotInputFromView, createFieldPolicyRegistry, resolveEntityView, } from "@voyantjs/catalog";
22
- import { asc, eq } from "drizzle-orm";
22
+ import { asc, eq, sql } from "drizzle-orm";
23
23
  import { productCatalogPolicy } from "./catalog-policy.js";
24
24
  import { products } from "./schema-core.js";
25
25
  import { productDays, productItineraries, productMedia } from "./schema-itinerary.js";
@@ -214,6 +214,12 @@ function isPublicStorefrontProduct(row) {
214
214
  return row.status === "active" && row.activated === true && row.visibility === "public";
215
215
  }
216
216
  function shouldEmitForSlice(row, slice) {
217
+ // The catalog is a "bookable now" surface — draft and archived
218
+ // products don't belong there, regardless of audience. Operators
219
+ // browsing /catalog get the same active-only set the storefront sees,
220
+ // just with staff-visible attribute columns.
221
+ if (row.status !== "active")
222
+ return false;
217
223
  if (slice.audience === "customer" ||
218
224
  slice.audience === "partner" ||
219
225
  slice.audience === "supplier") {
@@ -320,6 +326,7 @@ export function createProductStorefrontCardProjectionExtension() {
320
326
  const durationDays = defaultItinerary
321
327
  ? await estimateItineraryDurationDays(db, defaultItinerary.id)
322
328
  : null;
329
+ const availableDeparturesCount = await countAvailableDepartures(db, productId);
323
330
  const out = new Map([
324
331
  ["slug", translation?.slug ?? null],
325
332
  ["shortDescription", translation?.shortDescription ?? null],
@@ -327,6 +334,7 @@ export function createProductStorefrontCardProjectionExtension() {
327
334
  ["thumbnailUrl", primaryMedia?.url ?? null],
328
335
  ["coverMediaUrl", primaryMedia?.url ?? null],
329
336
  ["durationDays", durationDays],
337
+ ["availableDeparturesCount", availableDeparturesCount],
330
338
  ["latitude", coordinateLocation?.latitude ?? null],
331
339
  ["longitude", coordinateLocation?.longitude ?? null],
332
340
  ]);
@@ -344,6 +352,45 @@ function pickTranslation(rows, locale) {
344
352
  rows[0] ??
345
353
  null);
346
354
  }
355
+ /**
356
+ * Counts future, open availability slots for a product — surfaces in the
357
+ * catalog index as `availableDeparturesCount` so the operator catalog
358
+ * table can show booking-ready stock at a glance without a separate
359
+ * round-trip. Owned-products only; sourced products carry departures via
360
+ * the upstream feed and don't write to `availability_slots`.
361
+ *
362
+ * Cross-package boundary: queries `availability_slots` by raw table name
363
+ * via `sql` so the products module doesn't take a hard dependency on the
364
+ * `@voyantjs/availability` schema.
365
+ */
366
+ async function countAvailableDepartures(db, productId) {
367
+ try {
368
+ const result = await db.execute(sql `
369
+ SELECT COUNT(*)::int AS count
370
+ FROM availability_slots
371
+ WHERE product_id = ${productId}
372
+ AND status = 'open'
373
+ AND starts_at >= NOW()
374
+ `);
375
+ // postgres-js + neon-serverless both return `{ count }` on the first row;
376
+ // shape-defensive read in case a driver wraps it differently.
377
+ const rows = Array.isArray(result) ? result : (result.rows ?? []);
378
+ const row = rows[0];
379
+ const value = row?.count;
380
+ if (typeof value === "number")
381
+ return value;
382
+ if (typeof value === "string") {
383
+ const n = Number(value);
384
+ return Number.isFinite(n) ? n : 0;
385
+ }
386
+ return 0;
387
+ }
388
+ catch {
389
+ // availability_slots may not exist in slim test fixtures; treat as 0
390
+ // so reindex doesn't fail.
391
+ return 0;
392
+ }
393
+ }
347
394
  async function estimateItineraryDurationDays(db, itineraryId) {
348
395
  const rows = await db
349
396
  .select({ dayNumber: productDays.dayNumber })
@@ -1 +1 @@
1
- {"version":3,"file":"service-content-owned.d.ts","sourceRoot":"","sources":["../src/service-content-owned.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;GA+BG;AAEH,OAAO,KAAK,EAAE,sBAAsB,EAAE,MAAM,mBAAmB,CAAA;AAE/D,OAAO,KAAK,EAAE,YAAY,EAAE,MAAM,cAAc,CAAA;AAGhD,OAAO,EACL,KAAK,cAAc,EAGpB,MAAM,oBAAoB,CAAA;AAW3B,MAAM,WAAW,+BAA+B;IAC9C;;;;;OAKG;IACH,gBAAgB,EAAE,aAAa,CAAC,MAAM,CAAC,CAAA;CACxC;AAED,MAAM,WAAW,8BAA8B;IAC7C,qDAAqD;IACrD,OAAO,EAAE,cAAc,CAAA;IACvB;;;;;OAKG;IACH,YAAY,EAAE,MAAM,CAAA;IACpB;;;;OAIG;IACH,SAAS,EAAE,sBAAsB,CAAA;CAClC;AAED;;;;GAIG;AACH,wBAAsB,wBAAwB,CAC5C,EAAE,EAAE,YAAY,EAChB,QAAQ,EAAE,MAAM,EAChB,OAAO,EAAE,+BAA+B,GACvC,OAAO,CAAC,8BAA8B,GAAG,IAAI,CAAC,CAmIhD"}
1
+ {"version":3,"file":"service-content-owned.d.ts","sourceRoot":"","sources":["../src/service-content-owned.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;GA+BG;AAEH,OAAO,KAAK,EAAE,sBAAsB,EAAE,MAAM,mBAAmB,CAAA;AAE/D,OAAO,KAAK,EAAE,YAAY,EAAE,MAAM,cAAc,CAAA;AAGhD,OAAO,EACL,KAAK,cAAc,EAGpB,MAAM,oBAAoB,CAAA;AAW3B,MAAM,WAAW,+BAA+B;IAC9C;;;;;OAKG;IACH,gBAAgB,EAAE,aAAa,CAAC,MAAM,CAAC,CAAA;CACxC;AAED,MAAM,WAAW,8BAA8B;IAC7C,qDAAqD;IACrD,OAAO,EAAE,cAAc,CAAA;IACvB;;;;;OAKG;IACH,YAAY,EAAE,MAAM,CAAA;IACpB;;;;OAIG;IACH,SAAS,EAAE,sBAAsB,CAAA;CAClC;AAED;;;;GAIG;AACH,wBAAsB,wBAAwB,CAC5C,EAAE,EAAE,YAAY,EAChB,QAAQ,EAAE,MAAM,EAChB,OAAO,EAAE,+BAA+B,GACvC,OAAO,CAAC,8BAA8B,GAAG,IAAI,CAAC,CAwIhD"}
@@ -31,7 +31,7 @@
31
31
  * same way.
32
32
  */
33
33
  import { pickBestCachedLocale } from "@voyantjs/catalog";
34
- import { and, asc, eq, inArray } from "drizzle-orm";
34
+ import { and, asc, eq, inArray, sql } from "drizzle-orm";
35
35
  import { productContentSchema, validateProductContent, } from "./content-shape.js";
36
36
  import { productDays, productItineraries, productMedia, productOptions, productOptionTranslations, products, productTranslations, } from "./schema.js";
37
37
  /**
@@ -130,6 +130,9 @@ export async function buildOwnedProductContent(db, entityId, options) {
130
130
  title: d.title ?? null,
131
131
  description: d.description ?? null,
132
132
  location: d.location ?? null,
133
+ // Per-day hero — prefer the cover, fall back to the first sorted
134
+ // image attached to this day in `product_media`.
135
+ hero_image_url: pickDayHeroImage(mediaRows, d.id),
133
136
  services: [],
134
137
  })),
135
138
  media: mediaRows
@@ -141,9 +144,11 @@ export async function buildOwnedProductContent(db, entityId, options) {
141
144
  alt: m.altText ?? null,
142
145
  })),
143
146
  policies: [],
144
- // Owned products have no scheduled-departure table in v1; sourced
145
- // products carry departures via the upstream's getContent payload.
146
- departures: [],
147
+ // Owned products derive departures from `availability_slots`. Pull
148
+ // future-or-current slots only so the catalog sheet doesn't drown
149
+ // operators in expired departures; ordering is chronological so the
150
+ // UI can group consecutive months without sorting client-side.
151
+ departures: await readOwnedProductDepartures(db, entityId, productRow.sellCurrency),
147
152
  });
148
153
  const validation = validateProductContent(content);
149
154
  if (!validation.valid) {
@@ -155,6 +160,95 @@ export async function buildOwnedProductContent(db, entityId, options) {
155
160
  matchKind: productMatchKind,
156
161
  };
157
162
  }
163
+ /**
164
+ * Pick the best image to surface on an itinerary day card. Filters
165
+ * media rows to entries with a matching `dayId`, ignores brochures and
166
+ * non-images, then prefers `isCover === true` before falling back to
167
+ * the first sorted entry. `media-rows` is the same list the parent
168
+ * projection already pulled; no extra round-trip.
169
+ */
170
+ function pickDayHeroImage(mediaRows, dayId) {
171
+ const dayImages = mediaRows.filter((m) => m.dayId === dayId && m.mediaType === "image" && !m.isBrochure);
172
+ if (dayImages.length === 0)
173
+ return null;
174
+ const cover = dayImages.find((m) => m.isCover);
175
+ return (cover ?? dayImages[0])?.url ?? null;
176
+ }
177
+ /**
178
+ * Map future availability_slots → ProductDeparture[]. Raw SQL keeps the
179
+ * products module from depending on `@voyantjs/availability` (cross-
180
+ * module schema coupling is avoided per the workspace's separation).
181
+ */
182
+ async function readOwnedProductDepartures(db, productId, sellCurrency) {
183
+ try {
184
+ const result = await db.execute(sql `
185
+ SELECT
186
+ id,
187
+ starts_at,
188
+ ends_at,
189
+ status,
190
+ initial_pax,
191
+ remaining_pax
192
+ FROM availability_slots
193
+ WHERE product_id = ${productId}
194
+ AND starts_at >= NOW()
195
+ ORDER BY starts_at ASC
196
+ LIMIT 365
197
+ `);
198
+ const rows = Array.isArray(result) ? result : (result.rows ?? []);
199
+ return rows
200
+ .map((raw) => {
201
+ const row = raw;
202
+ const id = typeof row.id === "string" ? row.id : null;
203
+ const startsAt = isoOrNull(row.starts_at);
204
+ if (!id || !startsAt)
205
+ return null;
206
+ const status = typeof row.status === "string" ? row.status : null;
207
+ const capacity = numberOrNull(row.initial_pax);
208
+ const remaining = numberOrNull(row.remaining_pax);
209
+ return {
210
+ id,
211
+ starts_at: startsAt,
212
+ ends_at: isoOrNull(row.ends_at),
213
+ status,
214
+ capacity,
215
+ remaining,
216
+ // Lowest price hint is for display only; the live engine
217
+ // resolves the actual quote. Fall back to the product's base
218
+ // sell_amount via the content shape's parent currency.
219
+ lowest_price_cents: null,
220
+ currency: sellCurrency,
221
+ note: null,
222
+ };
223
+ })
224
+ .filter((row) => row !== null);
225
+ }
226
+ catch {
227
+ // availability_slots may be absent in trimmed test fixtures —
228
+ // empty list is a safe default (matches "on-request" behavior).
229
+ return [];
230
+ }
231
+ }
232
+ function isoOrNull(value) {
233
+ if (value instanceof Date) {
234
+ const ms = value.getTime();
235
+ return Number.isFinite(ms) ? value.toISOString() : null;
236
+ }
237
+ if (typeof value === "string" && value.length > 0) {
238
+ const d = new Date(value);
239
+ return Number.isFinite(d.getTime()) ? d.toISOString() : null;
240
+ }
241
+ return null;
242
+ }
243
+ function numberOrNull(value) {
244
+ if (typeof value === "number" && Number.isFinite(value))
245
+ return value;
246
+ if (typeof value === "string") {
247
+ const n = Number(value);
248
+ return Number.isFinite(n) ? n : null;
249
+ }
250
+ return null;
251
+ }
158
252
  function pickBestProductTranslation(rows, preferred) {
159
253
  if (rows.length === 0)
160
254
  return null;
@@ -58,33 +58,8 @@ export declare const insertProductSchema: z.ZodObject<{
58
58
  }, z.core.$strip>;
59
59
  export declare const updateProductSchema: z.ZodObject<{
60
60
  name: z.ZodOptional<z.ZodString>;
61
- status: z.ZodOptional<z.ZodDefault<z.ZodEnum<{
62
- active: "active";
63
- draft: "draft";
64
- archived: "archived";
65
- }>>>;
66
61
  description: z.ZodOptional<z.ZodNullable<z.ZodOptional<z.ZodString>>>;
67
- bookingMode: z.ZodOptional<z.ZodDefault<z.ZodEnum<{
68
- date: "date";
69
- other: "other";
70
- date_time: "date_time";
71
- open: "open";
72
- stay: "stay";
73
- transfer: "transfer";
74
- itinerary: "itinerary";
75
- }>>>;
76
- capacityMode: z.ZodOptional<z.ZodDefault<z.ZodEnum<{
77
- free_sale: "free_sale";
78
- limited: "limited";
79
- on_request: "on_request";
80
- }>>>;
81
62
  timezone: z.ZodOptional<z.ZodNullable<z.ZodOptional<z.ZodString>>>;
82
- visibility: z.ZodOptional<z.ZodDefault<z.ZodEnum<{
83
- public: "public";
84
- private: "private";
85
- hidden: "hidden";
86
- }>>>;
87
- activated: z.ZodOptional<z.ZodDefault<z.ZodBoolean>>;
88
63
  reservationTimeoutMinutes: z.ZodOptional<z.ZodNullable<z.ZodOptional<z.ZodNumber>>>;
89
64
  sellCurrency: z.ZodOptional<z.ZodString>;
90
65
  facilityId: z.ZodOptional<z.ZodNullable<z.ZodOptional<z.ZodString>>>;
@@ -108,7 +83,32 @@ export declare const updateProductSchema: z.ZodObject<{
108
83
  balanceDueDaysBeforeDeparture: z.ZodNumber;
109
84
  balanceDueMinDaysFromNow: z.ZodNumber;
110
85
  }, z.core.$strip>>>>;
111
- tags: z.ZodOptional<z.ZodDefault<z.ZodArray<z.ZodString>>>;
86
+ status: z.ZodOptional<z.ZodEnum<{
87
+ active: "active";
88
+ draft: "draft";
89
+ archived: "archived";
90
+ }>>;
91
+ bookingMode: z.ZodOptional<z.ZodEnum<{
92
+ date: "date";
93
+ other: "other";
94
+ date_time: "date_time";
95
+ open: "open";
96
+ stay: "stay";
97
+ transfer: "transfer";
98
+ itinerary: "itinerary";
99
+ }>>;
100
+ capacityMode: z.ZodOptional<z.ZodEnum<{
101
+ free_sale: "free_sale";
102
+ limited: "limited";
103
+ on_request: "on_request";
104
+ }>>;
105
+ visibility: z.ZodOptional<z.ZodEnum<{
106
+ public: "public";
107
+ private: "private";
108
+ hidden: "hidden";
109
+ }>>;
110
+ activated: z.ZodOptional<z.ZodBoolean>;
111
+ tags: z.ZodOptional<z.ZodArray<z.ZodString>>;
112
112
  sellAmountCents: z.ZodNullable<z.ZodOptional<z.ZodNumber>>;
113
113
  costAmountCents: z.ZodNullable<z.ZodOptional<z.ZodNumber>>;
114
114
  marginPercent: z.ZodNullable<z.ZodOptional<z.ZodNumber>>;
@@ -1 +1 @@
1
- {"version":3,"file":"validation-core.d.ts","sourceRoot":"","sources":["../src/validation-core.ts"],"names":[],"mappings":"AAAA,OAAO,EASL,CAAC,EACF,MAAM,wBAAwB,CAAA;AA2C/B,eAAO,MAAM,mBAAmB;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;iBAAiD,CAAA;AACjF,eAAO,MAAM,mBAAmB;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;iBAA2D,CAAA;AAC3F,eAAO,MAAM,mBAAmB;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;iBAO9B,CAAA;AACF,eAAO,MAAM,0BAA0B;;;;;;;;EAQrC,CAAA;AAEF,eAAO,MAAM,wBAAwB;;;EAA0B,CAAA;AAE/D,eAAO,MAAM,sBAAsB;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;iBAsBjC,CAAA;AAEF,eAAO,MAAM,4BAA4B;;;iBAGvC,CAAA;AACF,MAAM,MAAM,aAAa,GAAG,CAAC,CAAC,KAAK,CAAC,OAAO,mBAAmB,CAAC,CAAA;AAC/D,MAAM,MAAM,aAAa,GAAG,CAAC,CAAC,KAAK,CAAC,OAAO,mBAAmB,CAAC,CAAA;AAC/D,MAAM,MAAM,aAAa,GAAG,CAAC,CAAC,KAAK,CAAC,OAAO,mBAAmB,CAAC,CAAA;AAY/D,eAAO,MAAM,yBAAyB;;;;;;;;;;;;;iBAA0B,CAAA;AAChE,eAAO,MAAM,yBAAyB;;;;;;;;;;;;;iBAAoC,CAAA;AAC1E,eAAO,MAAM,4BAA4B;;;;;;;;;iBAKvC,CAAA;AACF,MAAM,MAAM,mBAAmB,GAAG,CAAC,CAAC,KAAK,CAAC,OAAO,yBAAyB,CAAC,CAAA;AAC3E,MAAM,MAAM,mBAAmB,GAAG,CAAC,CAAC,KAAK,CAAC,OAAO,yBAAyB,CAAC,CAAA;AAwB3E,KAAK,cAAc,GAAG;IACpB,QAAQ,CAAC,EAAE,MAAM,GAAG,IAAI,GAAG,SAAS,CAAA;IACpC,YAAY,CAAC,EAAE,MAAM,GAAG,IAAI,GAAG,SAAS,CAAA;IACxC,YAAY,CAAC,EAAE,MAAM,GAAG,IAAI,GAAG,SAAS,CAAA;CACzC,CAAA;AAED,KAAK,cAAc,GAAG;IAAE,IAAI,EAAE,MAAM,EAAE,CAAC;IAAC,OAAO,EAAE,MAAM,CAAA;CAAE,CAAA;AA2BzD,eAAO,MAAM,sBAAsB;;;;;;;;;;;;;;;;;;;;;iBAIjC,CAAA;AAMF,eAAO,MAAM,sBAAsB;;;;;;;;;;;;;;;;;;;;;iBAYjC,CAAA;AAEF;;;;;;;GAOG;AACH,wBAAgB,wBAAwB,CACtC,MAAM,EAAE,cAAc,GACrB;IAAE,EAAE,EAAE,IAAI,CAAA;CAAE,GAAG;IAAE,EAAE,EAAE,KAAK,CAAC;IAAC,MAAM,EAAE,cAAc,EAAE,CAAA;CAAE,CAGxD;AACD,eAAO,MAAM,yBAAyB;;;;;;;;;;;;iBAKpC,CAAA;AACF,MAAM,MAAM,gBAAgB,GAAG,CAAC,CAAC,KAAK,CAAC,OAAO,sBAAsB,CAAC,CAAA;AACrE,MAAM,MAAM,gBAAgB,GAAG,CAAC,CAAC,KAAK,CAAC,OAAO,sBAAsB,CAAC,CAAA;AAErE,eAAO,MAAM,mBAAmB;;iBAE9B,CAAA;AACF,MAAM,MAAM,aAAa,GAAG,CAAC,CAAC,KAAK,CAAC,OAAO,mBAAmB,CAAC,CAAA;AAE/D,eAAO,MAAM,uBAAuB;;iBAElC,CAAA"}
1
+ {"version":3,"file":"validation-core.d.ts","sourceRoot":"","sources":["../src/validation-core.ts"],"names":[],"mappings":"AAAA,OAAO,EASL,CAAC,EACF,MAAM,wBAAwB,CAAA;AA2C/B,eAAO,MAAM,mBAAmB;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;iBAAiD,CAAA;AAOjF,eAAO,MAAM,mBAAmB;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;iBAUD,CAAA;AAC/B,eAAO,MAAM,mBAAmB;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;iBAO9B,CAAA;AACF,eAAO,MAAM,0BAA0B;;;;;;;;EAQrC,CAAA;AAEF,eAAO,MAAM,wBAAwB;;;EAA0B,CAAA;AAE/D,eAAO,MAAM,sBAAsB;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;iBAsBjC,CAAA;AAEF,eAAO,MAAM,4BAA4B;;;iBAGvC,CAAA;AACF,MAAM,MAAM,aAAa,GAAG,CAAC,CAAC,KAAK,CAAC,OAAO,mBAAmB,CAAC,CAAA;AAC/D,MAAM,MAAM,aAAa,GAAG,CAAC,CAAC,KAAK,CAAC,OAAO,mBAAmB,CAAC,CAAA;AAC/D,MAAM,MAAM,aAAa,GAAG,CAAC,CAAC,KAAK,CAAC,OAAO,mBAAmB,CAAC,CAAA;AAY/D,eAAO,MAAM,yBAAyB;;;;;;;;;;;;;iBAA0B,CAAA;AAChE,eAAO,MAAM,yBAAyB;;;;;;;;;;;;;iBAAoC,CAAA;AAC1E,eAAO,MAAM,4BAA4B;;;;;;;;;iBAKvC,CAAA;AACF,MAAM,MAAM,mBAAmB,GAAG,CAAC,CAAC,KAAK,CAAC,OAAO,yBAAyB,CAAC,CAAA;AAC3E,MAAM,MAAM,mBAAmB,GAAG,CAAC,CAAC,KAAK,CAAC,OAAO,yBAAyB,CAAC,CAAA;AAwB3E,KAAK,cAAc,GAAG;IACpB,QAAQ,CAAC,EAAE,MAAM,GAAG,IAAI,GAAG,SAAS,CAAA;IACpC,YAAY,CAAC,EAAE,MAAM,GAAG,IAAI,GAAG,SAAS,CAAA;IACxC,YAAY,CAAC,EAAE,MAAM,GAAG,IAAI,GAAG,SAAS,CAAA;CACzC,CAAA;AAED,KAAK,cAAc,GAAG;IAAE,IAAI,EAAE,MAAM,EAAE,CAAC;IAAC,OAAO,EAAE,MAAM,CAAA;CAAE,CAAA;AA2BzD,eAAO,MAAM,sBAAsB;;;;;;;;;;;;;;;;;;;;;iBAIjC,CAAA;AAMF,eAAO,MAAM,sBAAsB;;;;;;;;;;;;;;;;;;;;;iBAYjC,CAAA;AAEF;;;;;;;GAOG;AACH,wBAAgB,wBAAwB,CACtC,MAAM,EAAE,cAAc,GACrB;IAAE,EAAE,EAAE,IAAI,CAAA;CAAE,GAAG;IAAE,EAAE,EAAE,KAAK,CAAC;IAAC,MAAM,EAAE,cAAc,EAAE,CAAA;CAAE,CAGxD;AACD,eAAO,MAAM,yBAAyB;;;;;;;;;;;;iBAKpC,CAAA;AACF,MAAM,MAAM,gBAAgB,GAAG,CAAC,CAAC,KAAK,CAAC,OAAO,sBAAsB,CAAC,CAAA;AACrE,MAAM,MAAM,gBAAgB,GAAG,CAAC,CAAC,KAAK,CAAC,OAAO,sBAAsB,CAAC,CAAA;AAErE,eAAO,MAAM,mBAAmB;;iBAE9B,CAAA;AACF,MAAM,MAAM,aAAa,GAAG,CAAC,CAAC,KAAK,CAAC,OAAO,mBAAmB,CAAC,CAAA;AAE/D,eAAO,MAAM,uBAAuB;;iBAElC,CAAA"}
@@ -37,7 +37,23 @@ const productPricingFields = {
37
37
  marginPercent: z.number().int().optional().nullable(),
38
38
  };
39
39
  export const insertProductSchema = productCoreSchema.extend(productPricingFields);
40
- export const updateProductSchema = productCoreSchema.partial().extend(productPricingFields);
40
+ // `productCoreSchema` carries `.default(...)` on enum-ish fields so
41
+ // insert can omit them. zod's `.partial()` does NOT strip those
42
+ // defaults — every PATCH would synthesize `status: "draft"`,
43
+ // `visibility: "private"`, etc., and overwrite the row. Re-declare the
44
+ // defaulted fields without defaults before partial-ising so PATCH only
45
+ // touches the keys the client actually sent.
46
+ export const updateProductSchema = productCoreSchema
47
+ .extend({
48
+ status: productStatusSchema,
49
+ bookingMode: productBookingModeSchema,
50
+ capacityMode: productCapacityModeSchema,
51
+ visibility: productVisibilitySchema,
52
+ activated: z.boolean(),
53
+ tags: z.array(z.string()),
54
+ })
55
+ .partial()
56
+ .extend(productPricingFields);
41
57
  export const selectProductSchema = productCoreSchema.extend({
42
58
  id: typeIdSchema("products"),
43
59
  sellAmountCents: z.number().int().nullable(),
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@voyantjs/products",
3
- "version": "0.52.0",
3
+ "version": "0.52.2",
4
4
  "license": "Apache-2.0",
5
5
  "type": "module",
6
6
  "exports": {
@@ -125,12 +125,12 @@
125
125
  "hono": "^4.12.10",
126
126
  "pdf-lib": "^1.17.1",
127
127
  "zod": "^4.3.6",
128
- "@voyantjs/core": "0.52.0",
129
- "@voyantjs/db": "0.52.0",
130
- "@voyantjs/hono": "0.52.0",
131
- "@voyantjs/utils": "0.52.0",
132
- "@voyantjs/catalog": "0.52.0",
133
- "@voyantjs/storage": "0.52.0"
128
+ "@voyantjs/core": "0.52.2",
129
+ "@voyantjs/db": "0.52.2",
130
+ "@voyantjs/hono": "0.52.2",
131
+ "@voyantjs/utils": "0.52.2",
132
+ "@voyantjs/catalog": "0.52.2",
133
+ "@voyantjs/storage": "0.52.2"
134
134
  },
135
135
  "devDependencies": {
136
136
  "typescript": "^6.0.2",