@voyantjs/bookings 0.119.2 → 0.119.3

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (37) hide show
  1. package/dist/pricing-assignment/age.d.ts +33 -0
  2. package/dist/pricing-assignment/age.d.ts.map +1 -0
  3. package/dist/pricing-assignment/age.js +109 -0
  4. package/dist/pricing-assignment/draft.d.ts +69 -0
  5. package/dist/pricing-assignment/draft.d.ts.map +1 -0
  6. package/dist/pricing-assignment/draft.js +284 -0
  7. package/dist/pricing-assignment/types.d.ts +113 -0
  8. package/dist/pricing-assignment/types.d.ts.map +1 -0
  9. package/dist/pricing-assignment/types.js +30 -0
  10. package/dist/pricing-assignment/unit-helpers.d.ts +11 -0
  11. package/dist/pricing-assignment/unit-helpers.d.ts.map +1 -0
  12. package/dist/pricing-assignment/unit-helpers.js +19 -0
  13. package/dist/pricing-assignment/verify.d.ts +67 -0
  14. package/dist/pricing-assignment/verify.d.ts.map +1 -0
  15. package/dist/pricing-assignment/verify.js +121 -0
  16. package/dist/pricing-assignment.d.ts +4 -275
  17. package/dist/pricing-assignment.d.ts.map +1 -1
  18. package/dist/pricing-assignment.js +4 -559
  19. package/dist/routes-admin.d.ts +2894 -0
  20. package/dist/routes-admin.d.ts.map +1 -0
  21. package/dist/routes-admin.js +2111 -0
  22. package/dist/routes.d.ts +1 -2893
  23. package/dist/routes.d.ts.map +1 -1
  24. package/dist/routes.js +1 -2111
  25. package/dist/service-core.d.ts +6240 -0
  26. package/dist/service-core.d.ts.map +1 -0
  27. package/dist/service-core.js +4350 -0
  28. package/dist/service-public-core.d.ts +907 -0
  29. package/dist/service-public-core.d.ts.map +1 -0
  30. package/dist/service-public-core.js +1176 -0
  31. package/dist/service-public.d.ts +1 -906
  32. package/dist/service-public.d.ts.map +1 -1
  33. package/dist/service-public.js +1 -1176
  34. package/dist/service.d.ts +1 -6239
  35. package/dist/service.d.ts.map +1 -1
  36. package/dist/service.js +1 -4350
  37. package/package.json +5 -5
@@ -0,0 +1 @@
1
+ {"version":3,"file":"unit-helpers.d.ts","sourceRoot":"","sources":["../../src/pricing-assignment/unit-helpers.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,kBAAkB,EAAE,oBAAoB,EAAE,qBAAqB,EAAE,MAAM,YAAY,CAAA;AAEjG;;;;GAIG;AACH,wBAAgB,SAAS,CAAC,IAAI,EAAE,qBAAqB,GAAG,MAAM,CAE7D;AAED,wBAAgB,YAAY,CAAC,IAAI,EAAE,qBAAqB,GAAG,OAAO,CAEjE;AAED,wBAAgB,eAAe,CAAC,IAAI,EAAE,qBAAqB,GAAG,OAAO,CAEpE;AAED,wBAAgB,mBAAmB,CAAC,QAAQ,EAAE,oBAAoB,GAAG,kBAAkB,GAAG,IAAI,CAI7F"}
@@ -0,0 +1,19 @@
1
+ /**
2
+ * Inventory units are finite containers a traveler is placed into.
3
+ * Rooms (hotels), vehicles (transfers), seats (where modeled
4
+ * explicitly). Distinct from pricing tiers (`person`).
5
+ */
6
+ export function optionKey(unit) {
7
+ return unit.optionId ?? unit.optionUnitId;
8
+ }
9
+ export function isPersonUnit(unit) {
10
+ return unit.unitType == null || unit.unitType === "person";
11
+ }
12
+ export function isInventoryUnit(unit) {
13
+ return unit.unitType === "room" || unit.unitType === "vehicle";
14
+ }
15
+ export function roleHintForTraveler(traveler) {
16
+ return traveler.role === "adult" || traveler.role === "child" || traveler.role === "infant"
17
+ ? traveler.role
18
+ : null;
19
+ }
@@ -0,0 +1,67 @@
1
+ import type { PricingAssignmentUnit } from "./types.js";
2
+ /**
3
+ * Minimal traveler shape `verifyBookingDraft` needs. Mirrors the
4
+ * wire-format `BookingCreateTravelerInput` from
5
+ * `@voyantjs/bookings-react`, defined here to avoid a cyclic
6
+ * dependency. The verifier doesn't care about most fields —
7
+ * `firstName` / `email` / etc. — only the role + traveler-category +
8
+ * primary flag that decide which pricing band each traveler
9
+ * implicitly maps to.
10
+ */
11
+ export interface VerifiableTraveler {
12
+ clientTravelerKey?: string | null;
13
+ isPrimary?: boolean | null;
14
+ travelerCategory?: "adult" | "child" | "infant" | "senior" | "other" | null;
15
+ }
16
+ /**
17
+ * Submitted itemLine shape `verifyBookingDraft` needs. Mirrors the
18
+ * wire-format `BookingCreateItemLineInput`.
19
+ */
20
+ export interface VerifiableItemLine {
21
+ optionUnitId: string;
22
+ quantity: number;
23
+ travelerKeys?: string[] | null;
24
+ travelerIndexes?: number[] | null;
25
+ }
26
+ export interface BookingDraftMismatch {
27
+ /** "qty" — submitted quantity differs from resolver-derived quantity for a unit. */
28
+ /** "missing" — resolver derived a unit not present in the submitted lines. */
29
+ /** "extra" — submitted line for a unit the resolver wouldn't have produced. */
30
+ kind: "qty" | "missing" | "extra";
31
+ optionUnitId: string;
32
+ submittedQuantity: number;
33
+ resolvedQuantity: number;
34
+ }
35
+ export interface VerifyBookingDraftResult {
36
+ ok: boolean;
37
+ mismatches: BookingDraftMismatch[];
38
+ }
39
+ /**
40
+ * Re-derive what `resolveBookingDraft` would have produced from the
41
+ * submitted wire-format payload and compare against the submitted
42
+ * itemLines. Used server-side as a sanity check on the client's
43
+ * draft resolution. Returns `ok: true` when the submitted lines
44
+ * match the resolver, else lists per-unit mismatches.
45
+ *
46
+ * Behavior of this helper is *non-rejecting* — callers decide
47
+ * whether to log, warn, or fail. The orchestrator currently logs;
48
+ * a follow-up will flip to rejection once observability confirms
49
+ * no legitimate clients trip the warning.
50
+ *
51
+ * Notes on the reconstruction:
52
+ * - The wire format doesn't carry split per-traveler assignment
53
+ * fields (the join-table model encodes them through
54
+ * `itemLines[].travelerKeys`), so we reconstruct the relevant
55
+ * pricing or inventory unit by walking item lines and matching
56
+ * them to traveler keys. Deprecated `travelerIndexes` remain a
57
+ * fallback for older clients.
58
+ * - DOB doesn't round-trip on the wire today either; we feed the
59
+ * resolver the `travelerCategory` as a role hint so age-banded
60
+ * options can still be re-derived.
61
+ */
62
+ export declare function verifyBookingDraft(input: {
63
+ travelers: ReadonlyArray<VerifiableTraveler>;
64
+ itemLines: ReadonlyArray<VerifiableItemLine>;
65
+ units: ReadonlyArray<PricingAssignmentUnit>;
66
+ }): VerifyBookingDraftResult;
67
+ //# sourceMappingURL=verify.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"verify.d.ts","sourceRoot":"","sources":["../../src/pricing-assignment/verify.ts"],"names":[],"mappings":"AACA,OAAO,KAAK,EAGV,qBAAqB,EAEtB,MAAM,YAAY,CAAA;AAGnB;;;;;;;;GAQG;AACH,MAAM,WAAW,kBAAkB;IACjC,iBAAiB,CAAC,EAAE,MAAM,GAAG,IAAI,CAAA;IACjC,SAAS,CAAC,EAAE,OAAO,GAAG,IAAI,CAAA;IAC1B,gBAAgB,CAAC,EAAE,OAAO,GAAG,OAAO,GAAG,QAAQ,GAAG,QAAQ,GAAG,OAAO,GAAG,IAAI,CAAA;CAC5E;AAED;;;GAGG;AACH,MAAM,WAAW,kBAAkB;IACjC,YAAY,EAAE,MAAM,CAAA;IACpB,QAAQ,EAAE,MAAM,CAAA;IAChB,YAAY,CAAC,EAAE,MAAM,EAAE,GAAG,IAAI,CAAA;IAC9B,eAAe,CAAC,EAAE,MAAM,EAAE,GAAG,IAAI,CAAA;CAClC;AAED,MAAM,WAAW,oBAAoB;IACnC,oFAAoF;IACpF,8EAA8E;IAC9E,+EAA+E;IAC/E,IAAI,EAAE,KAAK,GAAG,SAAS,GAAG,OAAO,CAAA;IACjC,YAAY,EAAE,MAAM,CAAA;IACpB,iBAAiB,EAAE,MAAM,CAAA;IACzB,gBAAgB,EAAE,MAAM,CAAA;CACzB;AAED,MAAM,WAAW,wBAAwB;IACvC,EAAE,EAAE,OAAO,CAAA;IACX,UAAU,EAAE,oBAAoB,EAAE,CAAA;CACnC;AAED;;;;;;;;;;;;;;;;;;;;;;GAsBG;AACH,wBAAgB,kBAAkB,CAAC,KAAK,EAAE;IACxC,SAAS,EAAE,aAAa,CAAC,kBAAkB,CAAC,CAAA;IAC5C,SAAS,EAAE,aAAa,CAAC,kBAAkB,CAAC,CAAA;IAC5C,KAAK,EAAE,aAAa,CAAC,qBAAqB,CAAC,CAAA;CAC5C,GAAG,wBAAwB,CAqG3B"}
@@ -0,0 +1,121 @@
1
+ import { resolveBookingDraft } from "./draft.js";
2
+ import { isInventoryUnit, isPersonUnit } from "./unit-helpers.js";
3
+ /**
4
+ * Re-derive what `resolveBookingDraft` would have produced from the
5
+ * submitted wire-format payload and compare against the submitted
6
+ * itemLines. Used server-side as a sanity check on the client's
7
+ * draft resolution. Returns `ok: true` when the submitted lines
8
+ * match the resolver, else lists per-unit mismatches.
9
+ *
10
+ * Behavior of this helper is *non-rejecting* — callers decide
11
+ * whether to log, warn, or fail. The orchestrator currently logs;
12
+ * a follow-up will flip to rejection once observability confirms
13
+ * no legitimate clients trip the warning.
14
+ *
15
+ * Notes on the reconstruction:
16
+ * - The wire format doesn't carry split per-traveler assignment
17
+ * fields (the join-table model encodes them through
18
+ * `itemLines[].travelerKeys`), so we reconstruct the relevant
19
+ * pricing or inventory unit by walking item lines and matching
20
+ * them to traveler keys. Deprecated `travelerIndexes` remain a
21
+ * fallback for older clients.
22
+ * - DOB doesn't round-trip on the wire today either; we feed the
23
+ * resolver the `travelerCategory` as a role hint so age-banded
24
+ * options can still be re-derived.
25
+ */
26
+ export function verifyBookingDraft(input) {
27
+ if (input.itemLines.length === 0 || input.units.length === 0) {
28
+ return { ok: true, mismatches: [] };
29
+ }
30
+ // Walk the submitted itemLines to recover the per-traveler unit
31
+ // assignment the client intended. Prefer stable `travelerKeys`
32
+ // when present; fall back to deprecated `travelerIndexes` for
33
+ // legacy clients. If neither is present, we can't verify
34
+ // per-traveler assignment — just compare aggregate quantities.
35
+ // Booking-create schema validation rejects unknown traveler keys
36
+ // before this verifier runs.
37
+ const unitById = new Map(input.units.map((unit) => [unit.optionUnitId, unit]));
38
+ const travelerIndexByKey = new Map();
39
+ for (const [index, traveler] of input.travelers.entries()) {
40
+ const key = traveler.clientTravelerKey?.trim();
41
+ if (key && !travelerIndexByKey.has(key))
42
+ travelerIndexByKey.set(key, index);
43
+ }
44
+ const unitByTravelerIndex = new Map();
45
+ for (const line of input.itemLines) {
46
+ const travelerKeys = (line.travelerKeys ?? []).filter((key) => key.trim().length > 0);
47
+ const keyedIndexes = travelerKeys
48
+ .map((key) => travelerIndexByKey.get(key.trim()))
49
+ .filter((index) => typeof index === "number");
50
+ const indexes = travelerKeys.length > 0 ? keyedIndexes : (line.travelerIndexes ?? []);
51
+ for (const idx of indexes) {
52
+ unitByTravelerIndex.set(idx, line.optionUnitId);
53
+ }
54
+ }
55
+ const travelerDrafts = input.travelers.map((t, i) => {
56
+ const cat = t.travelerCategory;
57
+ const role = t.isPrimary === true
58
+ ? "lead"
59
+ : cat === "child" || cat === "infant" || cat === "adult"
60
+ ? cat
61
+ : "adult";
62
+ const assigned = unitByTravelerIndex.get(i);
63
+ const assignedUnit = assigned ? unitById.get(assigned) : undefined;
64
+ return {
65
+ personId: null,
66
+ firstName: "",
67
+ lastName: "",
68
+ email: "",
69
+ phone: "",
70
+ preferredLanguage: "",
71
+ role,
72
+ dateOfBirth: null,
73
+ // We deliberately use `auto` (not `manual`) here even though
74
+ // the client effectively committed to an assignment when it
75
+ // serialized the line. The point of verification is to ask
76
+ // "would a fresh resolver, given the same role/category
77
+ // signals, produce these quantities?" — i.e. allow the
78
+ // resolver to re-derive instead of treating the submitted
79
+ // value as ground truth. Otherwise the day-tour bug shape
80
+ // (3 travelers all manually frozen to Adult) would round-trip
81
+ // through the verifier unchanged.
82
+ pricingUnitId: assigned && assignedUnit && isPersonUnit(assignedUnit) ? assigned : null,
83
+ inventoryUnitId: assigned && assignedUnit && isInventoryUnit(assignedUnit) ? assigned : null,
84
+ pricingUnitSource: "auto",
85
+ inventoryUnitSource: "auto",
86
+ };
87
+ });
88
+ // Aggregate submitted quantities for resolver input. For
89
+ // accommodation options the resolver passes these through
90
+ // unchanged; for person-priced options it rebuilds them from
91
+ // traveler assignments.
92
+ const submittedQuantities = {};
93
+ for (const line of input.itemLines) {
94
+ submittedQuantities[line.optionUnitId] =
95
+ (submittedQuantities[line.optionUnitId] ?? 0) + line.quantity;
96
+ }
97
+ const resolved = resolveBookingDraft({
98
+ quantities: submittedQuantities,
99
+ travelers: travelerDrafts,
100
+ units: input.units,
101
+ });
102
+ const mismatches = [];
103
+ const allUnitIds = new Set([
104
+ ...Object.keys(submittedQuantities),
105
+ ...Object.keys(resolved.quantities),
106
+ ]);
107
+ for (const unitId of allUnitIds) {
108
+ const submitted = submittedQuantities[unitId] ?? 0;
109
+ const resolvedQty = resolved.quantities[unitId] ?? 0;
110
+ if (submitted === resolvedQty)
111
+ continue;
112
+ const kind = submitted === 0 ? "missing" : resolvedQty === 0 ? "extra" : "qty";
113
+ mismatches.push({
114
+ kind,
115
+ optionUnitId: unitId,
116
+ submittedQuantity: submitted,
117
+ resolvedQuantity: resolvedQty,
118
+ });
119
+ }
120
+ return { ok: mismatches.length === 0, mismatches };
121
+ }
@@ -1,276 +1,5 @@
1
- /**
2
- * Pure, transport-agnostic logic for mapping travelers onto option_units
3
- * at booking-create time. Lives in `@voyantjs/bookings` so the
4
- * booking-create dialog (preview + submit) is the only call site today,
5
- * but the server can import the same module to validate or re-resolve
6
- * submit payloads in a follow-up — that wiring is not yet in place.
7
- *
8
- * Vocabulary:
9
- * - A **pricing tier** (`unit_type='person'`) is a per-pax price band
10
- * a traveler is billed as (Adult / Child 6-12 / Infant 0-5 / …).
11
- * - An **inventory unit** (`'room' | 'vehicle' | 'seat'`) is a finite
12
- * container a traveler is placed into (one DBL room holds 2 pax).
13
- * - "Person-priced options" are options that only have pricing tiers
14
- * (no inventory). Excursions. For these, line-item quantities
15
- * **derive from the traveler list** (1 adult + 1 child + 1 infant).
16
- * - "Accommodation options" have inventory units (and usually a
17
- * paired person unit for per-pax fees). For these, line-item
18
- * quantities **stay as the operator picked them** (1 DBL room is
19
- * still 1 line, not 2).
20
- *
21
- * The `pricingUnitSource` and `inventoryUnitSource` enums on the
22
- * traveler track operator intent so the resolver knows when to
23
- * re-derive ("auto") versus respect an explicit choice ("manual" /
24
- * "none" for No room).
25
- *
26
- * No React, no DB, no HTTP — just the assignment math.
27
- *
28
- * Tracking: voyantjs/voyant#1267.
29
- */
30
- export type TravelerRole = "lead" | "adult" | "child" | "infant";
31
- export type AssignmentRoleHint = "adult" | "child" | "infant";
32
- export type BookingDraftUnitAssignmentSource = "auto" | "manual" | "none";
33
- export interface BookingDraftTraveler {
34
- /** Stable client-side traveler key used by wire-format travelerKeys links. */
35
- clientTravelerKey?: string | null;
36
- personId: string | null;
37
- firstName: string;
38
- lastName: string;
39
- email: string;
40
- phone: string;
41
- preferredLanguage: string;
42
- role: TravelerRole;
43
- dateOfBirth: string | null;
44
- /** option_unit_id of the person pricing tier this traveler is billed as. */
45
- pricingUnitId: string | null;
46
- /** option_unit_id of the room/vehicle this traveler occupies, when applicable. */
47
- inventoryUnitId: string | null;
48
- /**
49
- * Tracks operator intent around `pricingUnitId`.
50
- *
51
- * - `auto`: derived from product shape, DOB, role hints, and
52
- * selected quantity. Eligible for re-derivation on every render.
53
- * - `manual`: operator explicitly clicked a category button. The
54
- * resolver respects the value when it's in the current unit set;
55
- * otherwise it re-derives.
56
- * - `none`: no pricing unit should be assigned.
57
- */
58
- pricingUnitSource?: BookingDraftUnitAssignmentSource;
59
- /**
60
- * Tracks operator intent around `inventoryUnitId`.
61
- *
62
- * - `auto`: derived from selected quantity and product shape.
63
- * - `manual`: operator explicitly clicked a room/vehicle control.
64
- * - `none`: operator explicitly picked "No room". Stays null.
65
- */
66
- inventoryUnitSource?: BookingDraftUnitAssignmentSource;
67
- }
68
- export interface PricingAssignmentUnit {
69
- /** Option the unit belongs to. Null for product-level units. */
70
- optionId?: string | null;
71
- /** Stable id of the unit (option_unit primary key). */
72
- optionUnitId: string;
73
- /** Display name (e.g. "Adult", "Child 6-12", "DBL room"). */
74
- unitName: string;
75
- /** Stable code from the products schema (`ADULT`, `child_6_12`, …) when present. */
76
- unitCode?: string | null;
77
- /** Inclusive lower age bound for this unit, when configured. */
78
- minAge?: number | null;
79
- /** Inclusive upper age bound for this unit, when configured. */
80
- maxAge?: number | null;
81
- /** Unit category — drives the pricing-tier vs inventory split. */
82
- unitType?: "person" | "group" | "room" | "vehicle" | "service" | "other" | null;
83
- }
84
- export type BookingDraftQuantities = Record<string, number>;
85
- export interface ResolvedBookingDraft<TTraveler extends BookingDraftTraveler> {
86
- quantities: BookingDraftQuantities;
87
- travelers: TTraveler[];
88
- /**
89
- * For each unit that ended up assigned, the indexes (into the input
90
- * traveler array) of travelers mapped to it. Used at submit time to
91
- * stamp stable `travelerKeys` on `booking_item` lines so the server
92
- * can link items to travelers through `booking_item_travelers`.
93
- */
94
- travelerIndexesByUnitId: Record<string, number[]>;
95
- }
96
- /**
97
- * Minimal structural shape `resolveBookingExtraLines` needs from an
98
- * extra line. Real callers (booking-create dialog) pass the full
99
- * `BookingCreateExtraLineInput` from `@voyantjs/bookings-react`; this
100
- * type avoids a cyclic dependency.
101
- */
102
- export interface ResolvableExtraLine {
103
- productExtraId: string;
104
- pricingMode?: string | null;
105
- pricedPerPerson?: boolean | null;
106
- quantity: number;
107
- unitSellAmountCents?: number | null;
108
- totalSellAmountCents?: number | null;
109
- clientLineKey?: string | null;
110
- travelerKeys?: string[] | null;
111
- travelerIndexes?: number[] | null;
112
- }
113
- /**
114
- * Compute integer age in full years from an ISO date-of-birth string.
115
- * Returns null when the DOB is missing or unparseable.
116
- */
117
- export declare function computeAgeYears(dob: string | null, now?: Date): number | null;
118
- /**
119
- * Derive the age-banded traveler category from DOB + role hint.
120
- * DOB-derived age wins; role hint is the fallback for travelers
121
- * added before the operator filled in a birthday.
122
- */
123
- export declare function deriveDraftPaxBand(traveler: Pick<BookingDraftTraveler, "dateOfBirth" | "role">, now?: Date): "adult" | "child" | "infant" | null;
124
- /**
125
- * Pick the unit for a traveler pricing band. Priorities:
126
- * 1. DOB-derived age that falls into a unit's `[minAge, maxAge]`
127
- * 2. Role hint mapped to a representative age (infant→1, child→8,
128
- * adult→30) matched against bands. Works for products coded
129
- * `child_0_5` / `child_6_12` (not just literal `INFANT`/`CHILD`).
130
- * 3. Code/name matching for legacy products with no min/max set.
131
- */
132
- export declare function pickUnitForAge<TUnit extends PricingAssignmentUnit>(units: ReadonlyArray<TUnit>, age: number | null, roleHint?: AssignmentRoleHint | null): TUnit | undefined;
133
- /**
134
- * Find the unit whose `[minAge, maxAge]` contains the DOB-derived
135
- * age. Used by the UI to pre-pick a unit when a traveler attaches.
136
- */
137
- export declare function matchUnitByDob<TUnit extends PricingAssignmentUnit>(units: ReadonlyArray<TUnit>, dob: string | null): string | null;
138
- /**
139
- * Find the unit matching a role hint when DOB is missing. Same
140
- * HINT_AGE mapping as `pickUnitForAge`. Returns null when the role
141
- * carries no age signal (e.g. `lead`).
142
- */
143
- export declare function matchUnitByRoleHint<TUnit extends PricingAssignmentUnit>(units: ReadonlyArray<TUnit>, role: TravelerRole | null): string | null;
144
- /**
145
- * Resolve a booking-create draft into the per-unit quantities and
146
- * per-traveler unit assignments the submit pipeline + price preview
147
- * both need. Single source of truth for traveler→unit mapping.
148
- *
149
- * Shape branching:
150
- * - **Person-priced options** (only `person` units, no `room`):
151
- * quantities are derived from traveler assignments (1 adult + 1
152
- * child + 1 infant = 3 line items, not "3 x Adult"). Auto-source
153
- * travelers are re-derived against the current option's bands.
154
- * - **Accommodation options** (any `room` unit): operator-picked
155
- * stepper quantities are preserved (1 DBL room is 1 line). Per-
156
- * traveler room assignments still update `travelerIndexesByUnitId`
157
- * so `booking_item_travelers` rows can be created.
158
- *
159
- * Respects assignment sources:
160
- * - `none` → the corresponding field stays null
161
- * - `manual` → the existing unit is kept when valid for the chosen option
162
- * - `auto` → re-derived against current options
163
- */
164
- export declare function resolveBookingDraft<TTraveler extends BookingDraftTraveler>(options: {
165
- quantities: BookingDraftQuantities;
166
- travelers: readonly TTraveler[];
167
- units: readonly PricingAssignmentUnit[];
168
- now?: Date;
169
- }): ResolvedBookingDraft<TTraveler>;
170
- /**
171
- * Normalize per-person extras to charged traveler quantity and
172
- * stamp traveler links + `clientLineKey` so the server can link
173
- * each extra line to the travelers it applies to via
174
- * `booking_item_travelers`.
175
- *
176
- * Per-person mode (`pricingMode === "per_person"` or
177
- * `pricedPerPerson === true`): quantity multiplied by travelerCount,
178
- * `travelerKeys` set to all travelers when stable keys are supplied;
179
- * otherwise `travelerIndexes` is set as a deprecated fallback.
180
- * Non-per-person lines pass through with `clientLineKey` only.
181
- */
182
- export declare function resolveBookingExtraLines<TLine extends ResolvableExtraLine>(options: {
183
- extraLines: readonly TLine[];
184
- travelerCount: number;
185
- travelerKeys?: readonly (string | null | undefined)[];
186
- }): TLine[];
187
- /**
188
- * Project a resolved draft's traveler list into the wire-format
189
- * `BookingCreateTravelerInput[]` shape the dialog submits. Derives
190
- * the `travelerCategory` from DOB / role.
191
- *
192
- * `roomUnitId` is a deprecated compatibility alias for the pricing tier
193
- * option unit. Inventory placement is expressed only by item lines and their
194
- * `travelerKeys`; the server accepts this field but does not persist it.
195
- */
196
- export declare function travelersToRows(value: {
197
- travelers: readonly BookingDraftTraveler[];
198
- }, now?: Date): Array<{
199
- clientTravelerKey: string | null;
200
- personId: string | null;
201
- firstName: string;
202
- lastName: string;
203
- email: string | null;
204
- phone: string | null;
205
- preferredLanguage: string | null;
206
- participantType: "traveler";
207
- travelerCategory: "adult" | "child" | "infant" | null;
208
- isPrimary: boolean;
209
- roomUnitId: string | null;
210
- }>;
211
- /**
212
- * Minimal traveler shape `verifyBookingDraft` needs. Mirrors the
213
- * wire-format `BookingCreateTravelerInput` from
214
- * `@voyantjs/bookings-react`, defined here to avoid a cyclic
215
- * dependency. The verifier doesn't care about most fields —
216
- * `firstName` / `email` / etc. — only the role + traveler-category +
217
- * primary flag that decide which pricing band each traveler
218
- * implicitly maps to.
219
- */
220
- export interface VerifiableTraveler {
221
- clientTravelerKey?: string | null;
222
- isPrimary?: boolean | null;
223
- travelerCategory?: "adult" | "child" | "infant" | "senior" | "other" | null;
224
- }
225
- /**
226
- * Submitted itemLine shape `verifyBookingDraft` needs. Mirrors the
227
- * wire-format `BookingCreateItemLineInput`.
228
- */
229
- export interface VerifiableItemLine {
230
- optionUnitId: string;
231
- quantity: number;
232
- travelerKeys?: string[] | null;
233
- travelerIndexes?: number[] | null;
234
- }
235
- export interface BookingDraftMismatch {
236
- /** "qty" — submitted quantity differs from resolver-derived quantity for a unit. */
237
- /** "missing" — resolver derived a unit not present in the submitted lines. */
238
- /** "extra" — submitted line for a unit the resolver wouldn't have produced. */
239
- kind: "qty" | "missing" | "extra";
240
- optionUnitId: string;
241
- submittedQuantity: number;
242
- resolvedQuantity: number;
243
- }
244
- export interface VerifyBookingDraftResult {
245
- ok: boolean;
246
- mismatches: BookingDraftMismatch[];
247
- }
248
- /**
249
- * Re-derive what `resolveBookingDraft` would have produced from the
250
- * submitted wire-format payload and compare against the submitted
251
- * itemLines. Used server-side as a sanity check on the client's
252
- * draft resolution. Returns `ok: true` when the submitted lines
253
- * match the resolver, else lists per-unit mismatches.
254
- *
255
- * Behavior of this helper is *non-rejecting* — callers decide
256
- * whether to log, warn, or fail. The orchestrator currently logs;
257
- * a follow-up will flip to rejection once observability confirms
258
- * no legitimate clients trip the warning.
259
- *
260
- * Notes on the reconstruction:
261
- * - The wire format doesn't carry split per-traveler assignment
262
- * fields (the join-table model encodes them through
263
- * `itemLines[].travelerKeys`), so we reconstruct the relevant
264
- * pricing or inventory unit by walking item lines and matching
265
- * them to traveler keys. Deprecated `travelerIndexes` remain a
266
- * fallback for older clients.
267
- * - DOB doesn't round-trip on the wire today either; we feed the
268
- * resolver the `travelerCategory` as a role hint so age-banded
269
- * options can still be re-derived.
270
- */
271
- export declare function verifyBookingDraft(input: {
272
- travelers: ReadonlyArray<VerifiableTraveler>;
273
- itemLines: ReadonlyArray<VerifiableItemLine>;
274
- units: ReadonlyArray<PricingAssignmentUnit>;
275
- }): VerifyBookingDraftResult;
1
+ export * from "./pricing-assignment/age.js";
2
+ export * from "./pricing-assignment/draft.js";
3
+ export * from "./pricing-assignment/types.js";
4
+ export * from "./pricing-assignment/verify.js";
276
5
  //# sourceMappingURL=pricing-assignment.d.ts.map
@@ -1 +1 @@
1
- {"version":3,"file":"pricing-assignment.d.ts","sourceRoot":"","sources":["../src/pricing-assignment.ts"],"names":[],"mappings":"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"}
1
+ {"version":3,"file":"pricing-assignment.d.ts","sourceRoot":"","sources":["../src/pricing-assignment.ts"],"names":[],"mappings":"AAAA,cAAc,6BAA6B,CAAA;AAC3C,cAAc,+BAA+B,CAAA;AAC7C,cAAc,+BAA+B,CAAA;AAC7C,cAAc,gCAAgC,CAAA"}