@voyantjs/storefront 0.43.0 → 0.45.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/README.md ADDED
@@ -0,0 +1,74 @@
1
+ # @voyantjs/storefront
2
+
3
+ Public storefront routes and service helpers for checkout-adjacent product, departure,
4
+ offer, and eligibility flows.
5
+
6
+ ## Transport Eligibility
7
+
8
+ Storefronts can check whether traveler document facts satisfy departure-level
9
+ transport rules before checkout confirmation.
10
+
11
+ ```ts
12
+ import { createStorefrontPublicRoutes } from "@voyantjs/storefront"
13
+
14
+ createStorefrontPublicRoutes({
15
+ transportEligibilityRules: [
16
+ {
17
+ id: "egypt-passport-validity",
18
+ label: "Egypt passport validity",
19
+ productId: "prod_123",
20
+ destinationCountries: ["EG"],
21
+ nationalityCountries: ["RO"],
22
+ requiredDocumentType: "passport",
23
+ minValidityDaysAfterReturn: 180,
24
+ visaRequired: true,
25
+ },
26
+ ],
27
+ })
28
+ ```
29
+
30
+ Schemas and the standalone evaluator are exported from
31
+ `@voyantjs/storefront/transport-eligibility`.
32
+
33
+ The public API is available at:
34
+
35
+ - `POST /departures/:departureId/eligibility`
36
+ - `POST /products/:productId/departures/:departureId/eligibility`
37
+
38
+ Request bodies contain only eligibility facts:
39
+
40
+ ```json
41
+ {
42
+ "travelStartsOn": "2026-08-01",
43
+ "travelEndsOn": "2026-08-08",
44
+ "travelers": [
45
+ {
46
+ "travelerRef": "traveler_1",
47
+ "nationalityCountry": "RO",
48
+ "dateOfBirth": "1990-01-01",
49
+ "documents": [
50
+ {
51
+ "type": "passport",
52
+ "issuingCountry": "RO",
53
+ "expiresOn": "2027-03-01"
54
+ }
55
+ ],
56
+ "hasVisa": true
57
+ }
58
+ ]
59
+ }
60
+ ```
61
+
62
+ Responses return a top-level `eligible` flag plus traveler-specific blocking
63
+ issues and warnings. Supported rule dimensions are product, departure,
64
+ destination country, nationality country, required document type, minimum
65
+ validity after return, age range, visa requirement, minor consent requirement,
66
+ and warning/blocking severity.
67
+
68
+ Do not send or store raw passport numbers through this surface. The route and
69
+ schemas only model document type, issuing country, expiry date, nationality,
70
+ birth date, and boolean evidence such as `hasVisa`.
71
+
72
+ This first slice is API-only. Admin CRUD and operator UI for managing transport
73
+ rules should persist normalized rule objects and can call the same public
74
+ eligibility service once added.
package/dist/index.d.ts CHANGED
@@ -5,8 +5,11 @@ export type { StorefrontPublicRoutes } from "./routes-public.js";
5
5
  export { createStorefrontPublicRoutes } from "./routes-public.js";
6
6
  export type { StorefrontOfferResolvers, StorefrontRequestContext, StorefrontServiceOptions, } from "./service.js";
7
7
  export { createStorefrontService, resolveStorefrontSettings } from "./service.js";
8
+ export { evaluateStorefrontTransportEligibility } from "./service-transport-eligibility.js";
8
9
  export type { StorefrontAppliedOffer, StorefrontDepartureListQuery, StorefrontFormField, StorefrontFormFieldInput, StorefrontOfferApplyInput, StorefrontOfferMutationResult, StorefrontOfferRedeemInput, StorefrontPaymentMethod, StorefrontPaymentMethodCode, StorefrontPaymentMethodInput, StorefrontProductAvailabilitySummaryQuery, StorefrontPromotionalOffer, StorefrontSettings, StorefrontSettingsInput, } from "./validation.js";
9
10
  export { storefrontAppliedOfferSchema, storefrontDepartureItinerarySchema, storefrontDepartureListQuerySchema, storefrontDepartureListResponseSchema, storefrontDeparturePricePreviewInputSchema, storefrontDeparturePricePreviewSchema, storefrontDepartureSchema, storefrontFormFieldInputSchema, storefrontFormFieldOptionSchema, storefrontFormFieldSchema, storefrontFormFieldTypeSchema, storefrontOfferApplyInputSchema, storefrontOfferAudienceSchema, storefrontOfferConflictSchema, storefrontOfferMutationReasonSchema, storefrontOfferMutationResponseSchema, storefrontOfferMutationResultSchema, storefrontOfferMutationStatusSchema, storefrontOfferRedeemInputSchema, storefrontPaymentMethodCodeSchema, storefrontPaymentMethodInputSchema, storefrontPaymentMethodSchema, storefrontProductAvailabilitySlotSchema, storefrontProductAvailabilityStateSchema, storefrontProductAvailabilitySummaryQuerySchema, storefrontProductAvailabilitySummaryResponseSchema, storefrontProductAvailabilitySummarySchema, storefrontProductExtensionsQuerySchema, storefrontProductExtensionsResponseSchema, storefrontPromotionalOfferListQuerySchema, storefrontPromotionalOfferListResponseSchema, storefrontPromotionalOfferResponseSchema, storefrontPromotionalOfferSchema, storefrontSettingsInputSchema, storefrontSettingsSchema, } from "./validation.js";
11
+ export type { StorefrontTransportEligibilityInput, StorefrontTransportEligibilityIssue, StorefrontTransportEligibilityResult, StorefrontTransportEligibilityRule, StorefrontTransportEligibilityRuleInput, } from "./validation-transport-eligibility.js";
12
+ export { storefrontRequiredDocumentTypeSchema, storefrontTransportEligibilityDocumentInputSchema, storefrontTransportEligibilityInputSchema, storefrontTransportEligibilityIssueCodeSchema, storefrontTransportEligibilityIssueSchema, storefrontTransportEligibilityResultSchema, storefrontTransportEligibilityRuleSchema, storefrontTransportEligibilitySeveritySchema, storefrontTransportEligibilityTravelerInputSchema, storefrontTransportEligibilityTravelerResultSchema, storefrontTravelDocumentTypeSchema, } from "./validation-transport-eligibility.js";
10
13
  export declare const storefrontModule: Module;
11
14
  export declare function createStorefrontHonoModule(options?: Parameters<typeof createStorefrontPublicRoutes>[0]): HonoModule;
12
15
  //# sourceMappingURL=index.d.ts.map
@@ -1 +1 @@
1
- {"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,MAAM,EAAE,MAAM,gBAAgB,CAAA;AAC5C,OAAO,KAAK,EAAE,UAAU,EAAE,MAAM,uBAAuB,CAAA;AAEvD,OAAO,EAAE,4BAA4B,EAAE,MAAM,oBAAoB,CAAA;AAEjE,YAAY,EAAE,sBAAsB,EAAE,MAAM,oBAAoB,CAAA;AAChE,OAAO,EAAE,4BAA4B,EAAE,MAAM,oBAAoB,CAAA;AACjE,YAAY,EACV,wBAAwB,EACxB,wBAAwB,EACxB,wBAAwB,GACzB,MAAM,cAAc,CAAA;AACrB,OAAO,EAAE,uBAAuB,EAAE,yBAAyB,EAAE,MAAM,cAAc,CAAA;AACjF,YAAY,EACV,sBAAsB,EACtB,4BAA4B,EAC5B,mBAAmB,EACnB,wBAAwB,EACxB,yBAAyB,EACzB,6BAA6B,EAC7B,0BAA0B,EAC1B,uBAAuB,EACvB,2BAA2B,EAC3B,4BAA4B,EAC5B,yCAAyC,EACzC,0BAA0B,EAC1B,kBAAkB,EAClB,uBAAuB,GACxB,MAAM,iBAAiB,CAAA;AACxB,OAAO,EACL,4BAA4B,EAC5B,kCAAkC,EAClC,kCAAkC,EAClC,qCAAqC,EACrC,0CAA0C,EAC1C,qCAAqC,EACrC,yBAAyB,EACzB,8BAA8B,EAC9B,+BAA+B,EAC/B,yBAAyB,EACzB,6BAA6B,EAC7B,+BAA+B,EAC/B,6BAA6B,EAC7B,6BAA6B,EAC7B,mCAAmC,EACnC,qCAAqC,EACrC,mCAAmC,EACnC,mCAAmC,EACnC,gCAAgC,EAChC,iCAAiC,EACjC,kCAAkC,EAClC,6BAA6B,EAC7B,uCAAuC,EACvC,wCAAwC,EACxC,+CAA+C,EAC/C,kDAAkD,EAClD,0CAA0C,EAC1C,sCAAsC,EACtC,yCAAyC,EACzC,yCAAyC,EACzC,4CAA4C,EAC5C,wCAAwC,EACxC,gCAAgC,EAChC,6BAA6B,EAC7B,wBAAwB,GACzB,MAAM,iBAAiB,CAAA;AAExB,eAAO,MAAM,gBAAgB,EAAE,MAE9B,CAAA;AAED,wBAAgB,0BAA0B,CACxC,OAAO,CAAC,EAAE,UAAU,CAAC,OAAO,4BAA4B,CAAC,CAAC,CAAC,CAAC,GAC3D,UAAU,CAMZ"}
1
+ {"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,MAAM,EAAE,MAAM,gBAAgB,CAAA;AAC5C,OAAO,KAAK,EAAE,UAAU,EAAE,MAAM,uBAAuB,CAAA;AAEvD,OAAO,EAAE,4BAA4B,EAAE,MAAM,oBAAoB,CAAA;AAEjE,YAAY,EAAE,sBAAsB,EAAE,MAAM,oBAAoB,CAAA;AAChE,OAAO,EAAE,4BAA4B,EAAE,MAAM,oBAAoB,CAAA;AACjE,YAAY,EACV,wBAAwB,EACxB,wBAAwB,EACxB,wBAAwB,GACzB,MAAM,cAAc,CAAA;AACrB,OAAO,EAAE,uBAAuB,EAAE,yBAAyB,EAAE,MAAM,cAAc,CAAA;AACjF,OAAO,EAAE,sCAAsC,EAAE,MAAM,oCAAoC,CAAA;AAC3F,YAAY,EACV,sBAAsB,EACtB,4BAA4B,EAC5B,mBAAmB,EACnB,wBAAwB,EACxB,yBAAyB,EACzB,6BAA6B,EAC7B,0BAA0B,EAC1B,uBAAuB,EACvB,2BAA2B,EAC3B,4BAA4B,EAC5B,yCAAyC,EACzC,0BAA0B,EAC1B,kBAAkB,EAClB,uBAAuB,GACxB,MAAM,iBAAiB,CAAA;AACxB,OAAO,EACL,4BAA4B,EAC5B,kCAAkC,EAClC,kCAAkC,EAClC,qCAAqC,EACrC,0CAA0C,EAC1C,qCAAqC,EACrC,yBAAyB,EACzB,8BAA8B,EAC9B,+BAA+B,EAC/B,yBAAyB,EACzB,6BAA6B,EAC7B,+BAA+B,EAC/B,6BAA6B,EAC7B,6BAA6B,EAC7B,mCAAmC,EACnC,qCAAqC,EACrC,mCAAmC,EACnC,mCAAmC,EACnC,gCAAgC,EAChC,iCAAiC,EACjC,kCAAkC,EAClC,6BAA6B,EAC7B,uCAAuC,EACvC,wCAAwC,EACxC,+CAA+C,EAC/C,kDAAkD,EAClD,0CAA0C,EAC1C,sCAAsC,EACtC,yCAAyC,EACzC,yCAAyC,EACzC,4CAA4C,EAC5C,wCAAwC,EACxC,gCAAgC,EAChC,6BAA6B,EAC7B,wBAAwB,GACzB,MAAM,iBAAiB,CAAA;AACxB,YAAY,EACV,mCAAmC,EACnC,mCAAmC,EACnC,oCAAoC,EACpC,kCAAkC,EAClC,uCAAuC,GACxC,MAAM,uCAAuC,CAAA;AAC9C,OAAO,EACL,oCAAoC,EACpC,iDAAiD,EACjD,yCAAyC,EACzC,6CAA6C,EAC7C,yCAAyC,EACzC,0CAA0C,EAC1C,wCAAwC,EACxC,4CAA4C,EAC5C,iDAAiD,EACjD,kDAAkD,EAClD,kCAAkC,GACnC,MAAM,uCAAuC,CAAA;AAE9C,eAAO,MAAM,gBAAgB,EAAE,MAE9B,CAAA;AAED,wBAAgB,0BAA0B,CACxC,OAAO,CAAC,EAAE,UAAU,CAAC,OAAO,4BAA4B,CAAC,CAAC,CAAC,CAAC,GAC3D,UAAU,CAMZ"}
package/dist/index.js CHANGED
@@ -1,7 +1,9 @@
1
1
  import { createStorefrontPublicRoutes } from "./routes-public.js";
2
2
  export { createStorefrontPublicRoutes } from "./routes-public.js";
3
3
  export { createStorefrontService, resolveStorefrontSettings } from "./service.js";
4
+ export { evaluateStorefrontTransportEligibility } from "./service-transport-eligibility.js";
4
5
  export { storefrontAppliedOfferSchema, storefrontDepartureItinerarySchema, storefrontDepartureListQuerySchema, storefrontDepartureListResponseSchema, storefrontDeparturePricePreviewInputSchema, storefrontDeparturePricePreviewSchema, storefrontDepartureSchema, storefrontFormFieldInputSchema, storefrontFormFieldOptionSchema, storefrontFormFieldSchema, storefrontFormFieldTypeSchema, storefrontOfferApplyInputSchema, storefrontOfferAudienceSchema, storefrontOfferConflictSchema, storefrontOfferMutationReasonSchema, storefrontOfferMutationResponseSchema, storefrontOfferMutationResultSchema, storefrontOfferMutationStatusSchema, storefrontOfferRedeemInputSchema, storefrontPaymentMethodCodeSchema, storefrontPaymentMethodInputSchema, storefrontPaymentMethodSchema, storefrontProductAvailabilitySlotSchema, storefrontProductAvailabilityStateSchema, storefrontProductAvailabilitySummaryQuerySchema, storefrontProductAvailabilitySummaryResponseSchema, storefrontProductAvailabilitySummarySchema, storefrontProductExtensionsQuerySchema, storefrontProductExtensionsResponseSchema, storefrontPromotionalOfferListQuerySchema, storefrontPromotionalOfferListResponseSchema, storefrontPromotionalOfferResponseSchema, storefrontPromotionalOfferSchema, storefrontSettingsInputSchema, storefrontSettingsSchema, } from "./validation.js";
6
+ export { storefrontRequiredDocumentTypeSchema, storefrontTransportEligibilityDocumentInputSchema, storefrontTransportEligibilityInputSchema, storefrontTransportEligibilityIssueCodeSchema, storefrontTransportEligibilityIssueSchema, storefrontTransportEligibilityResultSchema, storefrontTransportEligibilityRuleSchema, storefrontTransportEligibilitySeveritySchema, storefrontTransportEligibilityTravelerInputSchema, storefrontTransportEligibilityTravelerResultSchema, storefrontTravelDocumentTypeSchema, } from "./validation-transport-eligibility.js";
5
7
  export const storefrontModule = {
6
8
  name: "storefront",
7
9
  };
@@ -243,6 +243,132 @@ export declare function createStorefrontPublicRoutes(options?: StorefrontService
243
243
  status: 404;
244
244
  };
245
245
  };
246
+ } & {
247
+ "/departures/:departureId/eligibility": {
248
+ $post: {
249
+ input: {
250
+ param: {
251
+ departureId: string;
252
+ };
253
+ };
254
+ output: {
255
+ data: {
256
+ departureId: string;
257
+ productId: string | null;
258
+ travelStartsOn: string | null;
259
+ travelEndsOn: string | null;
260
+ eligible: boolean;
261
+ blockingIssues: {
262
+ code: "date_of_birth_required" | "document_required" | "document_expiry_required" | "document_validity" | "nationality_required" | "visa_required" | "minor_consent_required" | "travel_dates_required";
263
+ severity: "warning" | "blocking";
264
+ message: string;
265
+ travelerRef: string;
266
+ ruleId: string;
267
+ destinationCountries: string[];
268
+ requiredDocumentType: "passport" | "none" | "id_card" | "passport_or_id_card";
269
+ }[];
270
+ warnings: {
271
+ code: "date_of_birth_required" | "document_required" | "document_expiry_required" | "document_validity" | "nationality_required" | "visa_required" | "minor_consent_required" | "travel_dates_required";
272
+ severity: "warning" | "blocking";
273
+ message: string;
274
+ travelerRef: string;
275
+ ruleId: string;
276
+ destinationCountries: string[];
277
+ requiredDocumentType: "passport" | "none" | "id_card" | "passport_or_id_card";
278
+ }[];
279
+ travelers: {
280
+ travelerRef: string;
281
+ eligible: boolean;
282
+ matchedRuleIds: string[];
283
+ blockingIssues: {
284
+ code: "date_of_birth_required" | "document_required" | "document_expiry_required" | "document_validity" | "nationality_required" | "visa_required" | "minor_consent_required" | "travel_dates_required";
285
+ severity: "warning" | "blocking";
286
+ message: string;
287
+ travelerRef: string;
288
+ ruleId: string;
289
+ destinationCountries: string[];
290
+ requiredDocumentType: "passport" | "none" | "id_card" | "passport_or_id_card";
291
+ }[];
292
+ warnings: {
293
+ code: "date_of_birth_required" | "document_required" | "document_expiry_required" | "document_validity" | "nationality_required" | "visa_required" | "minor_consent_required" | "travel_dates_required";
294
+ severity: "warning" | "blocking";
295
+ message: string;
296
+ travelerRef: string;
297
+ ruleId: string;
298
+ destinationCountries: string[];
299
+ requiredDocumentType: "passport" | "none" | "id_card" | "passport_or_id_card";
300
+ }[];
301
+ }[];
302
+ };
303
+ };
304
+ outputFormat: "json";
305
+ status: import("hono/utils/http-status").ContentfulStatusCode;
306
+ };
307
+ };
308
+ } & {
309
+ "/products/:productId/departures/:departureId/eligibility": {
310
+ $post: {
311
+ input: {
312
+ param: {
313
+ departureId: string;
314
+ } & {
315
+ productId: string;
316
+ };
317
+ };
318
+ output: {
319
+ data: {
320
+ departureId: string;
321
+ productId: string | null;
322
+ travelStartsOn: string | null;
323
+ travelEndsOn: string | null;
324
+ eligible: boolean;
325
+ blockingIssues: {
326
+ code: "date_of_birth_required" | "document_required" | "document_expiry_required" | "document_validity" | "nationality_required" | "visa_required" | "minor_consent_required" | "travel_dates_required";
327
+ severity: "warning" | "blocking";
328
+ message: string;
329
+ travelerRef: string;
330
+ ruleId: string;
331
+ destinationCountries: string[];
332
+ requiredDocumentType: "passport" | "none" | "id_card" | "passport_or_id_card";
333
+ }[];
334
+ warnings: {
335
+ code: "date_of_birth_required" | "document_required" | "document_expiry_required" | "document_validity" | "nationality_required" | "visa_required" | "minor_consent_required" | "travel_dates_required";
336
+ severity: "warning" | "blocking";
337
+ message: string;
338
+ travelerRef: string;
339
+ ruleId: string;
340
+ destinationCountries: string[];
341
+ requiredDocumentType: "passport" | "none" | "id_card" | "passport_or_id_card";
342
+ }[];
343
+ travelers: {
344
+ travelerRef: string;
345
+ eligible: boolean;
346
+ matchedRuleIds: string[];
347
+ blockingIssues: {
348
+ code: "date_of_birth_required" | "document_required" | "document_expiry_required" | "document_validity" | "nationality_required" | "visa_required" | "minor_consent_required" | "travel_dates_required";
349
+ severity: "warning" | "blocking";
350
+ message: string;
351
+ travelerRef: string;
352
+ ruleId: string;
353
+ destinationCountries: string[];
354
+ requiredDocumentType: "passport" | "none" | "id_card" | "passport_or_id_card";
355
+ }[];
356
+ warnings: {
357
+ code: "date_of_birth_required" | "document_required" | "document_expiry_required" | "document_validity" | "nationality_required" | "visa_required" | "minor_consent_required" | "travel_dates_required";
358
+ severity: "warning" | "blocking";
359
+ message: string;
360
+ travelerRef: string;
361
+ ruleId: string;
362
+ destinationCountries: string[];
363
+ requiredDocumentType: "passport" | "none" | "id_card" | "passport_or_id_card";
364
+ }[];
365
+ }[];
366
+ };
367
+ };
368
+ outputFormat: "json";
369
+ status: import("hono/utils/http-status").ContentfulStatusCode;
370
+ };
371
+ };
246
372
  } & {
247
373
  "/products/:productId/extensions": {
248
374
  $get: {
@@ -1 +1 @@
1
- {"version":3,"file":"routes-public.d.ts","sourceRoot":"","sources":["../src/routes-public.ts"],"names":[],"mappings":"AAIA,OAAO,EAGL,KAAK,wBAAwB,EAC9B,MAAM,cAAc,CAAA;AAWrB,KAAK,GAAG,GAAG;IACT,SAAS,EAAE;QACT,EAAE,EAAE,OAAO,CAAA;KACZ,CAAA;CACF,CAAA;AAED,wBAAgB,4BAA4B,CAAC,OAAO,CAAC,EAAE,wBAAwB;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;0BAsH9E;AAED,MAAM,MAAM,sBAAsB,GAAG,UAAU,CAAC,OAAO,4BAA4B,CAAC,CAAA"}
1
+ {"version":3,"file":"routes-public.d.ts","sourceRoot":"","sources":["../src/routes-public.ts"],"names":[],"mappings":"AAIA,OAAO,EAGL,KAAK,wBAAwB,EAC9B,MAAM,cAAc,CAAA;AAYrB,KAAK,GAAG,GAAG;IACT,SAAS,EAAE;QACT,EAAE,EAAE,OAAO,CAAA;KACZ,CAAA;CACF,CAAA;AAED,wBAAgB,4BAA4B,CAAC,OAAO,CAAC,EAAE,wBAAwB;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;0BAyI9E;AAED,MAAM,MAAM,sBAAsB,GAAG,UAAU,CAAC,OAAO,4BAA4B,CAAC,CAAA"}
@@ -2,6 +2,7 @@ import { parseJsonBody, parseQuery } from "@voyantjs/hono";
2
2
  import { Hono } from "hono";
3
3
  import { createStorefrontService, } from "./service.js";
4
4
  import { storefrontDepartureListQuerySchema, storefrontDeparturePricePreviewInputSchema, storefrontOfferApplyInputSchema, storefrontOfferRedeemInputSchema, storefrontProductAvailabilitySummaryQuerySchema, storefrontProductExtensionsQuerySchema, storefrontPromotionalOfferListQuerySchema, } from "./validation.js";
5
+ import { storefrontTransportEligibilityInputSchema } from "./validation-transport-eligibility.js";
5
6
  export function createStorefrontPublicRoutes(options) {
6
7
  const storefrontService = createStorefrontService(options);
7
8
  function getRequestContext(c) {
@@ -29,6 +30,25 @@ export function createStorefrontPublicRoutes(options) {
29
30
  return preview
30
31
  ? c.json({ data: preview })
31
32
  : c.json({ error: "Storefront departure not found" }, 404);
33
+ })
34
+ .post("/departures/:departureId/eligibility", async (c) => {
35
+ return c.json({
36
+ data: await storefrontService.checkDepartureTransportEligibility({
37
+ departureId: c.req.param("departureId"),
38
+ body: await parseJsonBody(c, storefrontTransportEligibilityInputSchema),
39
+ context: getRequestContext(c),
40
+ }),
41
+ });
42
+ })
43
+ .post("/products/:productId/departures/:departureId/eligibility", async (c) => {
44
+ return c.json({
45
+ data: await storefrontService.checkDepartureTransportEligibility({
46
+ departureId: c.req.param("departureId"),
47
+ productId: c.req.param("productId"),
48
+ body: await parseJsonBody(c, storefrontTransportEligibilityInputSchema),
49
+ context: getRequestContext(c),
50
+ }),
51
+ });
32
52
  })
33
53
  .get("/products/:productId/extensions", async (c) => {
34
54
  const query = await parseQuery(c, storefrontProductExtensionsQuerySchema);
@@ -0,0 +1,10 @@
1
+ import type { StorefrontTransportEligibilityInput, StorefrontTransportEligibilityResult, StorefrontTransportEligibilityRuleInput } from "./validation-transport-eligibility.js";
2
+ export declare function evaluateStorefrontTransportEligibility(input: {
3
+ departureId: string;
4
+ productId?: string | null;
5
+ travelStartsOn?: string | null;
6
+ travelEndsOn?: string | null;
7
+ travelers: StorefrontTransportEligibilityInput["travelers"];
8
+ rules: StorefrontTransportEligibilityRuleInput[];
9
+ }): StorefrontTransportEligibilityResult;
10
+ //# sourceMappingURL=service-transport-eligibility.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"service-transport-eligibility.d.ts","sourceRoot":"","sources":["../src/service-transport-eligibility.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EACV,mCAAmC,EAEnC,oCAAoC,EAEpC,uCAAuC,EACxC,MAAM,uCAAuC,CAAA;AAoR9C,wBAAgB,sCAAsC,CAAC,KAAK,EAAE;IAC5D,WAAW,EAAE,MAAM,CAAA;IACnB,SAAS,CAAC,EAAE,MAAM,GAAG,IAAI,CAAA;IACzB,cAAc,CAAC,EAAE,MAAM,GAAG,IAAI,CAAA;IAC9B,YAAY,CAAC,EAAE,MAAM,GAAG,IAAI,CAAA;IAC5B,SAAS,EAAE,mCAAmC,CAAC,WAAW,CAAC,CAAA;IAC3D,KAAK,EAAE,uCAAuC,EAAE,CAAA;CACjD,GAAG,oCAAoC,CA0CvC"}
@@ -0,0 +1,198 @@
1
+ import { storefrontTransportEligibilityRuleSchema } from "./validation-transport-eligibility.js";
2
+ function parseDate(value) {
3
+ if (!value)
4
+ return null;
5
+ const date = new Date(`${value}T00:00:00.000Z`);
6
+ return Number.isNaN(date.getTime()) ? null : date;
7
+ }
8
+ function addDays(value, days) {
9
+ const date = parseDate(value);
10
+ if (!date)
11
+ return value;
12
+ date.setUTCDate(date.getUTCDate() + days);
13
+ return date.toISOString().slice(0, 10);
14
+ }
15
+ function getAgeOn(dateOfBirth, onDate) {
16
+ const birth = parseDate(dateOfBirth);
17
+ const reference = parseDate(onDate);
18
+ if (!birth || !reference)
19
+ return null;
20
+ let age = reference.getUTCFullYear() - birth.getUTCFullYear();
21
+ const birthdayThisYear = Date.UTC(reference.getUTCFullYear(), birth.getUTCMonth(), birth.getUTCDate());
22
+ if (reference.getTime() < birthdayThisYear) {
23
+ age -= 1;
24
+ }
25
+ return age;
26
+ }
27
+ function isBeforeDate(left, right) {
28
+ const leftDate = parseDate(left);
29
+ const rightDate = parseDate(right);
30
+ if (!leftDate || !rightDate)
31
+ return false;
32
+ return leftDate.getTime() < rightDate.getTime();
33
+ }
34
+ function findDocuments(traveler, rule) {
35
+ if (rule.requiredDocumentType === "none")
36
+ return [];
37
+ if (rule.requiredDocumentType === "passport_or_id_card") {
38
+ return traveler.documents.filter((document) => document.type === "passport" || document.type === "id_card");
39
+ }
40
+ return traveler.documents.filter((document) => document.type === rule.requiredDocumentType);
41
+ }
42
+ function documentTypeLabel(rule) {
43
+ switch (rule.requiredDocumentType) {
44
+ case "passport_or_id_card":
45
+ return "passport or ID card";
46
+ case "id_card":
47
+ return "ID card";
48
+ case "passport":
49
+ return "passport";
50
+ case "none":
51
+ return "travel document";
52
+ }
53
+ }
54
+ function buildIssue(code, traveler, rule, message) {
55
+ return {
56
+ code,
57
+ severity: rule.severity,
58
+ message: rule.message ?? message,
59
+ travelerRef: traveler.travelerRef,
60
+ ruleId: rule.id,
61
+ destinationCountries: rule.destinationCountries,
62
+ requiredDocumentType: rule.requiredDocumentType,
63
+ };
64
+ }
65
+ function pushIssue(target, issue) {
66
+ if (issue.severity === "warning") {
67
+ target.warnings.push(issue);
68
+ }
69
+ else {
70
+ target.blockingIssues.push(issue);
71
+ }
72
+ }
73
+ function appliesToTarget(rule, target) {
74
+ if (rule.productId && rule.productId !== target.productId)
75
+ return false;
76
+ if (rule.departureId && rule.departureId !== target.departureId)
77
+ return false;
78
+ return true;
79
+ }
80
+ function appliesToTraveler(rule, traveler, travelStartsOn) {
81
+ if (rule.nationalityCountries.length > 0) {
82
+ if (!traveler.nationalityCountry)
83
+ return "missing_nationality";
84
+ if (!rule.nationalityCountries.includes(traveler.nationalityCountry))
85
+ return false;
86
+ }
87
+ if (rule.minAge == null && rule.maxAge == null)
88
+ return true;
89
+ if (!traveler.dateOfBirth || !travelStartsOn)
90
+ return "missing_age_basis";
91
+ const age = getAgeOn(traveler.dateOfBirth, travelStartsOn);
92
+ if (age == null)
93
+ return "missing_age_basis";
94
+ if (rule.minAge != null && age < rule.minAge)
95
+ return false;
96
+ if (rule.maxAge != null && age > rule.maxAge)
97
+ return false;
98
+ return true;
99
+ }
100
+ function evaluateTravelerRule(traveler, rule, target) {
101
+ const blockingIssues = [];
102
+ const warnings = [];
103
+ const resultTarget = { blockingIssues, warnings };
104
+ const travelerApplicability = appliesToTraveler(rule, traveler, target.travelStartsOn);
105
+ if (travelerApplicability === false) {
106
+ return null;
107
+ }
108
+ if (travelerApplicability === "missing_nationality") {
109
+ pushIssue(resultTarget, buildIssue("nationality_required", traveler, rule, "Traveler nationality is required to evaluate destination document rules."));
110
+ }
111
+ if (travelerApplicability === "missing_age_basis") {
112
+ pushIssue(resultTarget, buildIssue("date_of_birth_required", traveler, rule, "Traveler date of birth and travel start date are required to evaluate age rules."));
113
+ }
114
+ const documents = findDocuments(traveler, rule);
115
+ if (rule.requiredDocumentType !== "none" && documents.length === 0) {
116
+ pushIssue(resultTarget, buildIssue("document_required", traveler, rule, `Traveler needs a ${documentTypeLabel(rule)} for this destination.`));
117
+ }
118
+ if (rule.minValidityDaysAfterReturn > 0) {
119
+ if (!target.travelEndsOn) {
120
+ pushIssue(resultTarget, buildIssue("travel_dates_required", traveler, rule, "Travel end date is required to evaluate document validity after return."));
121
+ }
122
+ else if (documents.length === 0) {
123
+ // Missing documents are already reported through document_required.
124
+ }
125
+ else {
126
+ const requiredValidUntil = addDays(target.travelEndsOn, rule.minValidityDaysAfterReturn);
127
+ const documentsWithExpiry = documents.filter((document) => typeof document.expiresOn === "string");
128
+ const hasValidDocument = documentsWithExpiry.some((document) => !isBeforeDate(document.expiresOn, requiredValidUntil));
129
+ if (hasValidDocument) {
130
+ // Any acceptable document can satisfy a passport-or-ID-card validity rule.
131
+ }
132
+ else if (documentsWithExpiry.length > 0) {
133
+ pushIssue(resultTarget, buildIssue("document_validity", traveler, rule, `Traveler ${documentTypeLabel(rule)} must be valid until at least ${requiredValidUntil}.`));
134
+ }
135
+ else {
136
+ pushIssue(resultTarget, buildIssue("document_expiry_required", traveler, rule, `Traveler ${documentTypeLabel(rule)} expiry date is required.`));
137
+ }
138
+ }
139
+ }
140
+ if (rule.visaRequired) {
141
+ const hasVisa = traveler.hasVisa || traveler.documents.some((document) => document.type === "visa");
142
+ if (!hasVisa) {
143
+ pushIssue(resultTarget, buildIssue("visa_required", traveler, rule, "Traveler needs a visa for this destination."));
144
+ }
145
+ }
146
+ if (rule.minorConsentRequired) {
147
+ const age = traveler.dateOfBirth && target.travelStartsOn
148
+ ? getAgeOn(traveler.dateOfBirth, target.travelStartsOn)
149
+ : null;
150
+ const isMinor = age == null ? true : age < 18;
151
+ const hasConsent = traveler.hasMinorConsent ||
152
+ traveler.travelingWithGuardian ||
153
+ traveler.documents.some((document) => document.type === "minor_consent");
154
+ if (isMinor && !hasConsent) {
155
+ pushIssue(resultTarget, buildIssue("minor_consent_required", traveler, rule, "Minor traveler needs guardian travel consent for this departure."));
156
+ }
157
+ }
158
+ return { blockingIssues, warnings };
159
+ }
160
+ export function evaluateStorefrontTransportEligibility(input) {
161
+ const travelStartsOn = input.travelStartsOn ?? null;
162
+ const travelEndsOn = input.travelEndsOn ?? null;
163
+ const rules = input.rules
164
+ .map((rule) => storefrontTransportEligibilityRuleSchema.parse(rule))
165
+ .filter((rule) => appliesToTarget(rule, input));
166
+ const travelerResults = input.travelers.map((traveler) => {
167
+ const blockingIssues = [];
168
+ const warnings = [];
169
+ const matchedRuleIds = [];
170
+ for (const rule of rules) {
171
+ const result = evaluateTravelerRule(traveler, rule, { travelStartsOn, travelEndsOn });
172
+ if (!result)
173
+ continue;
174
+ matchedRuleIds.push(rule.id);
175
+ blockingIssues.push(...result.blockingIssues);
176
+ warnings.push(...result.warnings);
177
+ }
178
+ return {
179
+ travelerRef: traveler.travelerRef,
180
+ eligible: blockingIssues.length === 0,
181
+ matchedRuleIds,
182
+ blockingIssues,
183
+ warnings,
184
+ };
185
+ });
186
+ const blockingIssues = travelerResults.flatMap((traveler) => traveler.blockingIssues);
187
+ const warnings = travelerResults.flatMap((traveler) => traveler.warnings);
188
+ return {
189
+ departureId: input.departureId,
190
+ productId: input.productId ?? null,
191
+ travelStartsOn,
192
+ travelEndsOn,
193
+ eligible: blockingIssues.length === 0,
194
+ blockingIssues,
195
+ warnings,
196
+ travelers: travelerResults,
197
+ };
198
+ }
package/dist/service.d.ts CHANGED
@@ -1,10 +1,18 @@
1
1
  import type { PostgresJsDatabase } from "drizzle-orm/postgres-js";
2
2
  import { type StorefrontDepartureListQuery, type StorefrontDeparturePricePreviewInput, type StorefrontOfferApplyInput, type StorefrontOfferMutationResult, type StorefrontOfferRedeemInput, type StorefrontProductAvailabilitySummaryQuery, type StorefrontPromotionalOffer, type StorefrontSettings, type StorefrontSettingsInput } from "./validation.js";
3
+ import type { StorefrontTransportEligibilityInput, StorefrontTransportEligibilityResult, StorefrontTransportEligibilityRuleInput } from "./validation-transport-eligibility.js";
3
4
  export interface StorefrontServiceOptions {
4
5
  settings?: StorefrontSettingsInput;
5
6
  resolveSettings?: (context: StorefrontRequestContext) => Promise<StorefrontSettingsInput> | StorefrontSettingsInput;
6
7
  offers?: StorefrontOfferResolvers;
7
8
  resolveOffers?: (context: StorefrontRequestContext) => Promise<StorefrontOfferResolvers | null | undefined> | StorefrontOfferResolvers | null | undefined;
9
+ transportEligibilityRules?: StorefrontTransportEligibilityRuleInput[];
10
+ resolveTransportEligibilityRules?: (input: {
11
+ departureId: string;
12
+ productId?: string | null;
13
+ travelStartsOn?: string | null;
14
+ travelEndsOn?: string | null;
15
+ } & StorefrontRequestContext) => Promise<StorefrontTransportEligibilityRuleInput[]> | StorefrontTransportEligibilityRuleInput[];
8
16
  }
9
17
  export interface StorefrontRequestContext {
10
18
  db?: PostgresJsDatabase;
@@ -296,6 +304,12 @@ export declare function createStorefrontService(options?: StorefrontServiceOptio
296
304
  }[];
297
305
  }[];
298
306
  } | null>;
307
+ checkDepartureTransportEligibility(input: {
308
+ departureId: string;
309
+ productId?: string | null;
310
+ body: StorefrontTransportEligibilityInput;
311
+ context?: StorefrontRequestContext;
312
+ }): Promise<StorefrontTransportEligibilityResult>;
299
313
  listApplicableOffers(input: {
300
314
  productId: string;
301
315
  departureId?: string;
@@ -1 +1 @@
1
- {"version":3,"file":"service.d.ts","sourceRoot":"","sources":["../src/service.ts"],"names":[],"mappings":"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"}
1
+ {"version":3,"file":"service.d.ts","sourceRoot":"","sources":["../src/service.ts"],"names":[],"mappings":"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"}
package/dist/service.js CHANGED
@@ -1,4 +1,5 @@
1
1
  import { getStorefrontDeparture, getStorefrontDepartureItinerary, getStorefrontProductAvailabilitySummary, getStorefrontProductExtensions, listStorefrontProductDepartures, previewStorefrontDeparturePrice, } from "./service-departures.js";
2
+ import { evaluateStorefrontTransportEligibility } from "./service-transport-eligibility.js";
2
3
  import { storefrontSettingsInputSchema, storefrontSettingsSchema, } from "./validation.js";
3
4
  const defaultPaymentLabels = {
4
5
  card: "Card",
@@ -68,6 +69,11 @@ export function createStorefrontService(options) {
68
69
  async function resolveOffers(context = {}) {
69
70
  return (await options?.resolveOffers?.(context)) ?? options?.offers;
70
71
  }
72
+ async function resolveTransportEligibilityRules(input) {
73
+ return ((await options?.resolveTransportEligibilityRules?.(input)) ??
74
+ options?.transportEligibilityRules ??
75
+ []);
76
+ }
71
77
  return {
72
78
  getSettings() {
73
79
  return settings;
@@ -91,6 +97,29 @@ export function createStorefrontService(options) {
91
97
  getDepartureItinerary(db, input) {
92
98
  return getStorefrontDepartureItinerary(db, input);
93
99
  },
100
+ async checkDepartureTransportEligibility(input) {
101
+ const { context, body, departureId } = input;
102
+ const needsDeparture = context?.db && (!input.productId || !body.travelStartsOn || !body.travelEndsOn);
103
+ const departure = needsDeparture && context?.db ? await getStorefrontDeparture(context.db, departureId) : null;
104
+ const productId = input.productId ?? departure?.productId ?? null;
105
+ const travelStartsOn = body.travelStartsOn ?? departure?.dateLocal ?? departure?.startAt?.slice(0, 10) ?? null;
106
+ const travelEndsOn = body.travelEndsOn ?? departure?.endAt?.slice(0, 10) ?? departure?.dateLocal ?? null;
107
+ const rules = await resolveTransportEligibilityRules({
108
+ ...(context ?? {}),
109
+ departureId,
110
+ productId,
111
+ travelStartsOn,
112
+ travelEndsOn,
113
+ });
114
+ return evaluateStorefrontTransportEligibility({
115
+ departureId,
116
+ productId,
117
+ travelStartsOn,
118
+ travelEndsOn,
119
+ travelers: body.travelers,
120
+ rules,
121
+ });
122
+ },
94
123
  async listApplicableOffers(input) {
95
124
  const { context, ...offerInput } = input;
96
125
  const offers = await resolveOffers(context)?.then((resolvers) => resolvers?.listApplicableOffers?.({ ...offerInput, ...(context ?? {}) }));
@@ -0,0 +1,4 @@
1
+ export { evaluateStorefrontTransportEligibility } from "./service-transport-eligibility.js";
2
+ export type { StorefrontTransportEligibilityInput, StorefrontTransportEligibilityIssue, StorefrontTransportEligibilityResult, StorefrontTransportEligibilityRule, StorefrontTransportEligibilityRuleInput, } from "./validation-transport-eligibility.js";
3
+ export { storefrontRequiredDocumentTypeSchema, storefrontTransportEligibilityDocumentInputSchema, storefrontTransportEligibilityInputSchema, storefrontTransportEligibilityIssueCodeSchema, storefrontTransportEligibilityIssueSchema, storefrontTransportEligibilityResultSchema, storefrontTransportEligibilityRuleSchema, storefrontTransportEligibilitySeveritySchema, storefrontTransportEligibilityTravelerInputSchema, storefrontTransportEligibilityTravelerResultSchema, storefrontTravelDocumentTypeSchema, } from "./validation-transport-eligibility.js";
4
+ //# sourceMappingURL=transport-eligibility.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"transport-eligibility.d.ts","sourceRoot":"","sources":["../src/transport-eligibility.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,sCAAsC,EAAE,MAAM,oCAAoC,CAAA;AAC3F,YAAY,EACV,mCAAmC,EACnC,mCAAmC,EACnC,oCAAoC,EACpC,kCAAkC,EAClC,uCAAuC,GACxC,MAAM,uCAAuC,CAAA;AAC9C,OAAO,EACL,oCAAoC,EACpC,iDAAiD,EACjD,yCAAyC,EACzC,6CAA6C,EAC7C,yCAAyC,EACzC,0CAA0C,EAC1C,wCAAwC,EACxC,4CAA4C,EAC5C,iDAAiD,EACjD,kDAAkD,EAClD,kCAAkC,GACnC,MAAM,uCAAuC,CAAA"}
@@ -0,0 +1,2 @@
1
+ export { evaluateStorefrontTransportEligibility } from "./service-transport-eligibility.js";
2
+ export { storefrontRequiredDocumentTypeSchema, storefrontTransportEligibilityDocumentInputSchema, storefrontTransportEligibilityInputSchema, storefrontTransportEligibilityIssueCodeSchema, storefrontTransportEligibilityIssueSchema, storefrontTransportEligibilityResultSchema, storefrontTransportEligibilityRuleSchema, storefrontTransportEligibilitySeveritySchema, storefrontTransportEligibilityTravelerInputSchema, storefrontTransportEligibilityTravelerResultSchema, storefrontTravelDocumentTypeSchema, } from "./validation-transport-eligibility.js";
@@ -0,0 +1,314 @@
1
+ import { z } from "zod";
2
+ export declare const storefrontTravelDocumentTypeSchema: z.ZodEnum<{
3
+ passport: "passport";
4
+ visa: "visa";
5
+ other: "other";
6
+ id_card: "id_card";
7
+ residence_permit: "residence_permit";
8
+ minor_consent: "minor_consent";
9
+ }>;
10
+ export declare const storefrontRequiredDocumentTypeSchema: z.ZodEnum<{
11
+ passport: "passport";
12
+ none: "none";
13
+ id_card: "id_card";
14
+ passport_or_id_card: "passport_or_id_card";
15
+ }>;
16
+ export declare const storefrontTransportEligibilitySeveritySchema: z.ZodEnum<{
17
+ warning: "warning";
18
+ blocking: "blocking";
19
+ }>;
20
+ export declare const storefrontTransportEligibilityRuleSchema: z.ZodObject<{
21
+ id: z.ZodString;
22
+ label: z.ZodString;
23
+ productId: z.ZodNullable<z.ZodOptional<z.ZodString>>;
24
+ departureId: z.ZodNullable<z.ZodOptional<z.ZodString>>;
25
+ destinationCountries: z.ZodArray<z.ZodPipe<z.ZodString, z.ZodTransform<string, string>>>;
26
+ nationalityCountries: z.ZodDefault<z.ZodArray<z.ZodPipe<z.ZodString, z.ZodTransform<string, string>>>>;
27
+ requiredDocumentType: z.ZodDefault<z.ZodEnum<{
28
+ passport: "passport";
29
+ none: "none";
30
+ id_card: "id_card";
31
+ passport_or_id_card: "passport_or_id_card";
32
+ }>>;
33
+ minValidityDaysAfterReturn: z.ZodDefault<z.ZodNumber>;
34
+ minAge: z.ZodNullable<z.ZodOptional<z.ZodNumber>>;
35
+ maxAge: z.ZodNullable<z.ZodOptional<z.ZodNumber>>;
36
+ visaRequired: z.ZodDefault<z.ZodBoolean>;
37
+ minorConsentRequired: z.ZodDefault<z.ZodBoolean>;
38
+ severity: z.ZodDefault<z.ZodEnum<{
39
+ warning: "warning";
40
+ blocking: "blocking";
41
+ }>>;
42
+ message: z.ZodNullable<z.ZodOptional<z.ZodString>>;
43
+ }, z.core.$strip>;
44
+ export declare const storefrontTransportEligibilityDocumentInputSchema: z.ZodObject<{
45
+ type: z.ZodEnum<{
46
+ passport: "passport";
47
+ visa: "visa";
48
+ other: "other";
49
+ id_card: "id_card";
50
+ residence_permit: "residence_permit";
51
+ minor_consent: "minor_consent";
52
+ }>;
53
+ issuingCountry: z.ZodNullable<z.ZodOptional<z.ZodPipe<z.ZodString, z.ZodTransform<string, string>>>>;
54
+ expiresOn: z.ZodNullable<z.ZodOptional<z.ZodString>>;
55
+ }, z.core.$strip>;
56
+ export declare const storefrontTransportEligibilityTravelerInputSchema: z.ZodObject<{
57
+ travelerRef: z.ZodString;
58
+ nationalityCountry: z.ZodNullable<z.ZodOptional<z.ZodPipe<z.ZodString, z.ZodTransform<string, string>>>>;
59
+ dateOfBirth: z.ZodNullable<z.ZodOptional<z.ZodString>>;
60
+ documents: z.ZodDefault<z.ZodArray<z.ZodObject<{
61
+ type: z.ZodEnum<{
62
+ passport: "passport";
63
+ visa: "visa";
64
+ other: "other";
65
+ id_card: "id_card";
66
+ residence_permit: "residence_permit";
67
+ minor_consent: "minor_consent";
68
+ }>;
69
+ issuingCountry: z.ZodNullable<z.ZodOptional<z.ZodPipe<z.ZodString, z.ZodTransform<string, string>>>>;
70
+ expiresOn: z.ZodNullable<z.ZodOptional<z.ZodString>>;
71
+ }, z.core.$strip>>>;
72
+ hasVisa: z.ZodDefault<z.ZodBoolean>;
73
+ travelingWithGuardian: z.ZodDefault<z.ZodBoolean>;
74
+ hasMinorConsent: z.ZodDefault<z.ZodBoolean>;
75
+ }, z.core.$strip>;
76
+ export declare const storefrontTransportEligibilityInputSchema: z.ZodObject<{
77
+ travelStartsOn: z.ZodNullable<z.ZodOptional<z.ZodString>>;
78
+ travelEndsOn: z.ZodNullable<z.ZodOptional<z.ZodString>>;
79
+ travelers: z.ZodArray<z.ZodObject<{
80
+ travelerRef: z.ZodString;
81
+ nationalityCountry: z.ZodNullable<z.ZodOptional<z.ZodPipe<z.ZodString, z.ZodTransform<string, string>>>>;
82
+ dateOfBirth: z.ZodNullable<z.ZodOptional<z.ZodString>>;
83
+ documents: z.ZodDefault<z.ZodArray<z.ZodObject<{
84
+ type: z.ZodEnum<{
85
+ passport: "passport";
86
+ visa: "visa";
87
+ other: "other";
88
+ id_card: "id_card";
89
+ residence_permit: "residence_permit";
90
+ minor_consent: "minor_consent";
91
+ }>;
92
+ issuingCountry: z.ZodNullable<z.ZodOptional<z.ZodPipe<z.ZodString, z.ZodTransform<string, string>>>>;
93
+ expiresOn: z.ZodNullable<z.ZodOptional<z.ZodString>>;
94
+ }, z.core.$strip>>>;
95
+ hasVisa: z.ZodDefault<z.ZodBoolean>;
96
+ travelingWithGuardian: z.ZodDefault<z.ZodBoolean>;
97
+ hasMinorConsent: z.ZodDefault<z.ZodBoolean>;
98
+ }, z.core.$strip>>;
99
+ }, z.core.$strip>;
100
+ export declare const storefrontTransportEligibilityIssueCodeSchema: z.ZodEnum<{
101
+ date_of_birth_required: "date_of_birth_required";
102
+ document_required: "document_required";
103
+ document_expiry_required: "document_expiry_required";
104
+ document_validity: "document_validity";
105
+ nationality_required: "nationality_required";
106
+ visa_required: "visa_required";
107
+ minor_consent_required: "minor_consent_required";
108
+ travel_dates_required: "travel_dates_required";
109
+ }>;
110
+ export declare const storefrontTransportEligibilityIssueSchema: z.ZodObject<{
111
+ code: z.ZodEnum<{
112
+ date_of_birth_required: "date_of_birth_required";
113
+ document_required: "document_required";
114
+ document_expiry_required: "document_expiry_required";
115
+ document_validity: "document_validity";
116
+ nationality_required: "nationality_required";
117
+ visa_required: "visa_required";
118
+ minor_consent_required: "minor_consent_required";
119
+ travel_dates_required: "travel_dates_required";
120
+ }>;
121
+ severity: z.ZodEnum<{
122
+ warning: "warning";
123
+ blocking: "blocking";
124
+ }>;
125
+ message: z.ZodString;
126
+ travelerRef: z.ZodString;
127
+ ruleId: z.ZodString;
128
+ destinationCountries: z.ZodArray<z.ZodPipe<z.ZodString, z.ZodTransform<string, string>>>;
129
+ requiredDocumentType: z.ZodEnum<{
130
+ passport: "passport";
131
+ none: "none";
132
+ id_card: "id_card";
133
+ passport_or_id_card: "passport_or_id_card";
134
+ }>;
135
+ }, z.core.$strip>;
136
+ export declare const storefrontTransportEligibilityTravelerResultSchema: z.ZodObject<{
137
+ travelerRef: z.ZodString;
138
+ eligible: z.ZodBoolean;
139
+ matchedRuleIds: z.ZodArray<z.ZodString>;
140
+ blockingIssues: z.ZodArray<z.ZodObject<{
141
+ code: z.ZodEnum<{
142
+ date_of_birth_required: "date_of_birth_required";
143
+ document_required: "document_required";
144
+ document_expiry_required: "document_expiry_required";
145
+ document_validity: "document_validity";
146
+ nationality_required: "nationality_required";
147
+ visa_required: "visa_required";
148
+ minor_consent_required: "minor_consent_required";
149
+ travel_dates_required: "travel_dates_required";
150
+ }>;
151
+ severity: z.ZodEnum<{
152
+ warning: "warning";
153
+ blocking: "blocking";
154
+ }>;
155
+ message: z.ZodString;
156
+ travelerRef: z.ZodString;
157
+ ruleId: z.ZodString;
158
+ destinationCountries: z.ZodArray<z.ZodPipe<z.ZodString, z.ZodTransform<string, string>>>;
159
+ requiredDocumentType: z.ZodEnum<{
160
+ passport: "passport";
161
+ none: "none";
162
+ id_card: "id_card";
163
+ passport_or_id_card: "passport_or_id_card";
164
+ }>;
165
+ }, z.core.$strip>>;
166
+ warnings: z.ZodArray<z.ZodObject<{
167
+ code: z.ZodEnum<{
168
+ date_of_birth_required: "date_of_birth_required";
169
+ document_required: "document_required";
170
+ document_expiry_required: "document_expiry_required";
171
+ document_validity: "document_validity";
172
+ nationality_required: "nationality_required";
173
+ visa_required: "visa_required";
174
+ minor_consent_required: "minor_consent_required";
175
+ travel_dates_required: "travel_dates_required";
176
+ }>;
177
+ severity: z.ZodEnum<{
178
+ warning: "warning";
179
+ blocking: "blocking";
180
+ }>;
181
+ message: z.ZodString;
182
+ travelerRef: z.ZodString;
183
+ ruleId: z.ZodString;
184
+ destinationCountries: z.ZodArray<z.ZodPipe<z.ZodString, z.ZodTransform<string, string>>>;
185
+ requiredDocumentType: z.ZodEnum<{
186
+ passport: "passport";
187
+ none: "none";
188
+ id_card: "id_card";
189
+ passport_or_id_card: "passport_or_id_card";
190
+ }>;
191
+ }, z.core.$strip>>;
192
+ }, z.core.$strip>;
193
+ export declare const storefrontTransportEligibilityResultSchema: z.ZodObject<{
194
+ departureId: z.ZodString;
195
+ productId: z.ZodNullable<z.ZodString>;
196
+ travelStartsOn: z.ZodNullable<z.ZodString>;
197
+ travelEndsOn: z.ZodNullable<z.ZodString>;
198
+ eligible: z.ZodBoolean;
199
+ blockingIssues: z.ZodArray<z.ZodObject<{
200
+ code: z.ZodEnum<{
201
+ date_of_birth_required: "date_of_birth_required";
202
+ document_required: "document_required";
203
+ document_expiry_required: "document_expiry_required";
204
+ document_validity: "document_validity";
205
+ nationality_required: "nationality_required";
206
+ visa_required: "visa_required";
207
+ minor_consent_required: "minor_consent_required";
208
+ travel_dates_required: "travel_dates_required";
209
+ }>;
210
+ severity: z.ZodEnum<{
211
+ warning: "warning";
212
+ blocking: "blocking";
213
+ }>;
214
+ message: z.ZodString;
215
+ travelerRef: z.ZodString;
216
+ ruleId: z.ZodString;
217
+ destinationCountries: z.ZodArray<z.ZodPipe<z.ZodString, z.ZodTransform<string, string>>>;
218
+ requiredDocumentType: z.ZodEnum<{
219
+ passport: "passport";
220
+ none: "none";
221
+ id_card: "id_card";
222
+ passport_or_id_card: "passport_or_id_card";
223
+ }>;
224
+ }, z.core.$strip>>;
225
+ warnings: z.ZodArray<z.ZodObject<{
226
+ code: z.ZodEnum<{
227
+ date_of_birth_required: "date_of_birth_required";
228
+ document_required: "document_required";
229
+ document_expiry_required: "document_expiry_required";
230
+ document_validity: "document_validity";
231
+ nationality_required: "nationality_required";
232
+ visa_required: "visa_required";
233
+ minor_consent_required: "minor_consent_required";
234
+ travel_dates_required: "travel_dates_required";
235
+ }>;
236
+ severity: z.ZodEnum<{
237
+ warning: "warning";
238
+ blocking: "blocking";
239
+ }>;
240
+ message: z.ZodString;
241
+ travelerRef: z.ZodString;
242
+ ruleId: z.ZodString;
243
+ destinationCountries: z.ZodArray<z.ZodPipe<z.ZodString, z.ZodTransform<string, string>>>;
244
+ requiredDocumentType: z.ZodEnum<{
245
+ passport: "passport";
246
+ none: "none";
247
+ id_card: "id_card";
248
+ passport_or_id_card: "passport_or_id_card";
249
+ }>;
250
+ }, z.core.$strip>>;
251
+ travelers: z.ZodArray<z.ZodObject<{
252
+ travelerRef: z.ZodString;
253
+ eligible: z.ZodBoolean;
254
+ matchedRuleIds: z.ZodArray<z.ZodString>;
255
+ blockingIssues: z.ZodArray<z.ZodObject<{
256
+ code: z.ZodEnum<{
257
+ date_of_birth_required: "date_of_birth_required";
258
+ document_required: "document_required";
259
+ document_expiry_required: "document_expiry_required";
260
+ document_validity: "document_validity";
261
+ nationality_required: "nationality_required";
262
+ visa_required: "visa_required";
263
+ minor_consent_required: "minor_consent_required";
264
+ travel_dates_required: "travel_dates_required";
265
+ }>;
266
+ severity: z.ZodEnum<{
267
+ warning: "warning";
268
+ blocking: "blocking";
269
+ }>;
270
+ message: z.ZodString;
271
+ travelerRef: z.ZodString;
272
+ ruleId: z.ZodString;
273
+ destinationCountries: z.ZodArray<z.ZodPipe<z.ZodString, z.ZodTransform<string, string>>>;
274
+ requiredDocumentType: z.ZodEnum<{
275
+ passport: "passport";
276
+ none: "none";
277
+ id_card: "id_card";
278
+ passport_or_id_card: "passport_or_id_card";
279
+ }>;
280
+ }, z.core.$strip>>;
281
+ warnings: z.ZodArray<z.ZodObject<{
282
+ code: z.ZodEnum<{
283
+ date_of_birth_required: "date_of_birth_required";
284
+ document_required: "document_required";
285
+ document_expiry_required: "document_expiry_required";
286
+ document_validity: "document_validity";
287
+ nationality_required: "nationality_required";
288
+ visa_required: "visa_required";
289
+ minor_consent_required: "minor_consent_required";
290
+ travel_dates_required: "travel_dates_required";
291
+ }>;
292
+ severity: z.ZodEnum<{
293
+ warning: "warning";
294
+ blocking: "blocking";
295
+ }>;
296
+ message: z.ZodString;
297
+ travelerRef: z.ZodString;
298
+ ruleId: z.ZodString;
299
+ destinationCountries: z.ZodArray<z.ZodPipe<z.ZodString, z.ZodTransform<string, string>>>;
300
+ requiredDocumentType: z.ZodEnum<{
301
+ passport: "passport";
302
+ none: "none";
303
+ id_card: "id_card";
304
+ passport_or_id_card: "passport_or_id_card";
305
+ }>;
306
+ }, z.core.$strip>>;
307
+ }, z.core.$strip>>;
308
+ }, z.core.$strip>;
309
+ export type StorefrontTransportEligibilityInput = z.infer<typeof storefrontTransportEligibilityInputSchema>;
310
+ export type StorefrontTransportEligibilityRule = z.infer<typeof storefrontTransportEligibilityRuleSchema>;
311
+ export type StorefrontTransportEligibilityRuleInput = z.input<typeof storefrontTransportEligibilityRuleSchema>;
312
+ export type StorefrontTransportEligibilityIssue = z.infer<typeof storefrontTransportEligibilityIssueSchema>;
313
+ export type StorefrontTransportEligibilityResult = z.infer<typeof storefrontTransportEligibilityResultSchema>;
314
+ //# sourceMappingURL=validation-transport-eligibility.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"validation-transport-eligibility.d.ts","sourceRoot":"","sources":["../src/validation-transport-eligibility.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,CAAC,EAAE,MAAM,KAAK,CAAA;AAQvB,eAAO,MAAM,kCAAkC;;;;;;;EAO7C,CAAA;AAEF,eAAO,MAAM,oCAAoC;;;;;EAK/C,CAAA;AAEF,eAAO,MAAM,4CAA4C;;;EAAkC,CAAA;AAE3F,eAAO,MAAM,wCAAwC;;;;;;;;;;;;;;;;;;;;;;;iBAoBjD,CAAA;AAEJ,eAAO,MAAM,iDAAiD;;;;;;;;;;;iBAI5D,CAAA;AAEF,eAAO,MAAM,iDAAiD;;;;;;;;;;;;;;;;;;;iBAQ5D,CAAA;AAEF,eAAO,MAAM,yCAAyC;;;;;;;;;;;;;;;;;;;;;;;iBAIpD,CAAA;AAEF,eAAO,MAAM,6CAA6C;;;;;;;;;EASxD,CAAA;AAEF,eAAO,MAAM,yCAAyC;;;;;;;;;;;;;;;;;;;;;;;;;iBAQpD,CAAA;AAEF,eAAO,MAAM,kDAAkD;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;iBAM7D,CAAA;AAEF,eAAO,MAAM,0CAA0C;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;iBASrD,CAAA;AAEF,MAAM,MAAM,mCAAmC,GAAG,CAAC,CAAC,KAAK,CACvD,OAAO,yCAAyC,CACjD,CAAA;AACD,MAAM,MAAM,kCAAkC,GAAG,CAAC,CAAC,KAAK,CACtD,OAAO,wCAAwC,CAChD,CAAA;AACD,MAAM,MAAM,uCAAuC,GAAG,CAAC,CAAC,KAAK,CAC3D,OAAO,wCAAwC,CAChD,CAAA;AACD,MAAM,MAAM,mCAAmC,GAAG,CAAC,CAAC,KAAK,CACvD,OAAO,yCAAyC,CACjD,CAAA;AACD,MAAM,MAAM,oCAAoC,GAAG,CAAC,CAAC,KAAK,CACxD,OAAO,0CAA0C,CAClD,CAAA"}
@@ -0,0 +1,97 @@
1
+ import { z } from "zod";
2
+ const countryCodeSchema = z
3
+ .string()
4
+ .trim()
5
+ .length(2)
6
+ .transform((value) => value.toUpperCase());
7
+ export const storefrontTravelDocumentTypeSchema = z.enum([
8
+ "passport",
9
+ "id_card",
10
+ "residence_permit",
11
+ "visa",
12
+ "minor_consent",
13
+ "other",
14
+ ]);
15
+ export const storefrontRequiredDocumentTypeSchema = z.enum([
16
+ "none",
17
+ "passport",
18
+ "id_card",
19
+ "passport_or_id_card",
20
+ ]);
21
+ export const storefrontTransportEligibilitySeveritySchema = z.enum(["blocking", "warning"]);
22
+ export const storefrontTransportEligibilityRuleSchema = z
23
+ .object({
24
+ id: z.string().trim().min(1),
25
+ label: z.string().trim().min(1),
26
+ productId: z.string().trim().min(1).optional().nullable(),
27
+ departureId: z.string().trim().min(1).optional().nullable(),
28
+ destinationCountries: z.array(countryCodeSchema).min(1),
29
+ nationalityCountries: z.array(countryCodeSchema).default([]),
30
+ requiredDocumentType: storefrontRequiredDocumentTypeSchema.default("passport"),
31
+ minValidityDaysAfterReturn: z.number().int().min(0).default(0),
32
+ minAge: z.number().int().min(0).optional().nullable(),
33
+ maxAge: z.number().int().min(0).optional().nullable(),
34
+ visaRequired: z.boolean().default(false),
35
+ minorConsentRequired: z.boolean().default(false),
36
+ severity: storefrontTransportEligibilitySeveritySchema.default("blocking"),
37
+ message: z.string().trim().min(1).optional().nullable(),
38
+ })
39
+ .refine((rule) => rule.minAge == null || rule.maxAge == null || rule.minAge <= rule.maxAge, {
40
+ message: "minAge must be less than or equal to maxAge",
41
+ path: ["maxAge"],
42
+ });
43
+ export const storefrontTransportEligibilityDocumentInputSchema = z.object({
44
+ type: storefrontTravelDocumentTypeSchema,
45
+ issuingCountry: countryCodeSchema.optional().nullable(),
46
+ expiresOn: z.string().date().optional().nullable(),
47
+ });
48
+ export const storefrontTransportEligibilityTravelerInputSchema = z.object({
49
+ travelerRef: z.string().trim().min(1),
50
+ nationalityCountry: countryCodeSchema.optional().nullable(),
51
+ dateOfBirth: z.string().date().optional().nullable(),
52
+ documents: z.array(storefrontTransportEligibilityDocumentInputSchema).default([]),
53
+ hasVisa: z.boolean().default(false),
54
+ travelingWithGuardian: z.boolean().default(false),
55
+ hasMinorConsent: z.boolean().default(false),
56
+ });
57
+ export const storefrontTransportEligibilityInputSchema = z.object({
58
+ travelStartsOn: z.string().date().optional().nullable(),
59
+ travelEndsOn: z.string().date().optional().nullable(),
60
+ travelers: z.array(storefrontTransportEligibilityTravelerInputSchema).min(1),
61
+ });
62
+ export const storefrontTransportEligibilityIssueCodeSchema = z.enum([
63
+ "date_of_birth_required",
64
+ "document_required",
65
+ "document_expiry_required",
66
+ "document_validity",
67
+ "nationality_required",
68
+ "visa_required",
69
+ "minor_consent_required",
70
+ "travel_dates_required",
71
+ ]);
72
+ export const storefrontTransportEligibilityIssueSchema = z.object({
73
+ code: storefrontTransportEligibilityIssueCodeSchema,
74
+ severity: storefrontTransportEligibilitySeveritySchema,
75
+ message: z.string(),
76
+ travelerRef: z.string(),
77
+ ruleId: z.string(),
78
+ destinationCountries: z.array(countryCodeSchema),
79
+ requiredDocumentType: storefrontRequiredDocumentTypeSchema,
80
+ });
81
+ export const storefrontTransportEligibilityTravelerResultSchema = z.object({
82
+ travelerRef: z.string(),
83
+ eligible: z.boolean(),
84
+ matchedRuleIds: z.array(z.string()),
85
+ blockingIssues: z.array(storefrontTransportEligibilityIssueSchema),
86
+ warnings: z.array(storefrontTransportEligibilityIssueSchema),
87
+ });
88
+ export const storefrontTransportEligibilityResultSchema = z.object({
89
+ departureId: z.string(),
90
+ productId: z.string().nullable(),
91
+ travelStartsOn: z.string().nullable(),
92
+ travelEndsOn: z.string().nullable(),
93
+ eligible: z.boolean(),
94
+ blockingIssues: z.array(storefrontTransportEligibilityIssueSchema),
95
+ warnings: z.array(storefrontTransportEligibilityIssueSchema),
96
+ travelers: z.array(storefrontTransportEligibilityTravelerResultSchema),
97
+ });
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@voyantjs/storefront",
3
- "version": "0.43.0",
3
+ "version": "0.45.0",
4
4
  "license": "Apache-2.0",
5
5
  "type": "module",
6
6
  "exports": {
@@ -19,6 +19,11 @@
19
19
  "import": "./dist/service.js",
20
20
  "default": "./dist/service.js"
21
21
  },
22
+ "./transport-eligibility": {
23
+ "types": "./dist/transport-eligibility.d.ts",
24
+ "import": "./dist/transport-eligibility.js",
25
+ "default": "./dist/transport-eligibility.js"
26
+ },
22
27
  "./validation": {
23
28
  "types": "./dist/validation.d.ts",
24
29
  "import": "./dist/validation.js",
@@ -29,18 +34,18 @@
29
34
  "drizzle-orm": "^0.45.2",
30
35
  "hono": "^4.12.10",
31
36
  "zod": "^4.3.6",
32
- "@voyantjs/availability": "0.43.0",
33
- "@voyantjs/core": "0.43.0",
34
- "@voyantjs/extras": "0.43.0",
35
- "@voyantjs/hono": "0.43.0",
36
- "@voyantjs/pricing": "0.43.0",
37
- "@voyantjs/products": "0.43.0",
38
- "@voyantjs/sellability": "0.43.0"
37
+ "@voyantjs/availability": "0.45.0",
38
+ "@voyantjs/core": "0.45.0",
39
+ "@voyantjs/extras": "0.45.0",
40
+ "@voyantjs/hono": "0.45.0",
41
+ "@voyantjs/pricing": "0.45.0",
42
+ "@voyantjs/products": "0.45.0",
43
+ "@voyantjs/sellability": "0.45.0"
39
44
  },
40
45
  "devDependencies": {
41
46
  "typescript": "^6.0.2",
42
47
  "vitest": "^4.1.2",
43
- "@voyantjs/db": "0.43.0",
48
+ "@voyantjs/db": "0.45.0",
44
49
  "@voyantjs/voyant-typescript-config": "0.1.0"
45
50
  },
46
51
  "files": [