itamatrix 0.1.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.
Files changed (78) hide show
  1. package/DESIGN.md +247 -0
  2. package/LICENSE +21 -0
  3. package/README.md +101 -0
  4. package/dist/browser/batch.d.ts +8 -0
  5. package/dist/browser/batch.js +87 -0
  6. package/dist/browser/batch.js.map +1 -0
  7. package/dist/browser/batch.test.d.ts +1 -0
  8. package/dist/browser/batch.test.js +38 -0
  9. package/dist/browser/batch.test.js.map +1 -0
  10. package/dist/browser/forms.d.ts +26 -0
  11. package/dist/browser/forms.js +233 -0
  12. package/dist/browser/forms.js.map +1 -0
  13. package/dist/browser/session.d.ts +20 -0
  14. package/dist/browser/session.js +126 -0
  15. package/dist/browser/session.js.map +1 -0
  16. package/dist/cache.d.ts +21 -0
  17. package/dist/cache.js +71 -0
  18. package/dist/cache.js.map +1 -0
  19. package/dist/cache.test.d.ts +1 -0
  20. package/dist/cache.test.js +79 -0
  21. package/dist/cache.test.js.map +1 -0
  22. package/dist/cli.d.ts +3 -0
  23. package/dist/cli.js +154 -0
  24. package/dist/cli.js.map +1 -0
  25. package/dist/commands/calendar.d.ts +19 -0
  26. package/dist/commands/calendar.js +47 -0
  27. package/dist/commands/calendar.js.map +1 -0
  28. package/dist/commands/calendar.test.d.ts +1 -0
  29. package/dist/commands/calendar.test.js +58 -0
  30. package/dist/commands/calendar.test.js.map +1 -0
  31. package/dist/commands/multicity.d.ts +17 -0
  32. package/dist/commands/multicity.js +50 -0
  33. package/dist/commands/multicity.js.map +1 -0
  34. package/dist/commands/multicity.test.d.ts +1 -0
  35. package/dist/commands/multicity.test.js +54 -0
  36. package/dist/commands/multicity.test.js.map +1 -0
  37. package/dist/commands/search.d.ts +20 -0
  38. package/dist/commands/search.js +43 -0
  39. package/dist/commands/search.js.map +1 -0
  40. package/dist/commands/search.test.d.ts +1 -0
  41. package/dist/commands/search.test.js +124 -0
  42. package/dist/commands/search.test.js.map +1 -0
  43. package/dist/commands/shared.d.ts +44 -0
  44. package/dist/commands/shared.js +77 -0
  45. package/dist/commands/shared.js.map +1 -0
  46. package/dist/model/spec.d.ts +63 -0
  47. package/dist/model/spec.js +29 -0
  48. package/dist/model/spec.js.map +1 -0
  49. package/dist/model/spec.test.d.ts +1 -0
  50. package/dist/model/spec.test.js +55 -0
  51. package/dist/model/spec.test.js.map +1 -0
  52. package/dist/model/types.d.ts +22080 -0
  53. package/dist/model/types.js +100 -0
  54. package/dist/model/types.js.map +1 -0
  55. package/dist/model/types.test.d.ts +1 -0
  56. package/dist/model/types.test.js +35 -0
  57. package/dist/model/types.test.js.map +1 -0
  58. package/dist/render/calendar.d.ts +21 -0
  59. package/dist/render/calendar.js +89 -0
  60. package/dist/render/calendar.js.map +1 -0
  61. package/dist/render/calendar.render.test.d.ts +1 -0
  62. package/dist/render/calendar.render.test.js +66 -0
  63. package/dist/render/calendar.render.test.js.map +1 -0
  64. package/dist/render/json.d.ts +2 -0
  65. package/dist/render/json.js +4 -0
  66. package/dist/render/json.js.map +1 -0
  67. package/dist/render/normalize.d.ts +32 -0
  68. package/dist/render/normalize.js +44 -0
  69. package/dist/render/normalize.js.map +1 -0
  70. package/dist/render/render.test.d.ts +1 -0
  71. package/dist/render/render.test.js +129 -0
  72. package/dist/render/render.test.js.map +1 -0
  73. package/dist/render/table.d.ts +2 -0
  74. package/dist/render/table.js +49 -0
  75. package/dist/render/table.js.map +1 -0
  76. package/docs/ROUTING_CODES.md +137 -0
  77. package/package.json +68 -0
  78. package/skills/itamatrix/SKILL.md +173 -0
@@ -0,0 +1,124 @@
1
+ import { describe, it, expect, vi } from "vitest";
2
+ import { resolveRouting, runSearchCommand } from "./search.js";
3
+ const captured = [];
4
+ vi.mock("../browser/session.js", () => ({
5
+ runSearch: (spec) => {
6
+ captured.push(spec);
7
+ return Promise.resolve({ solutionList: { solutions: [] } });
8
+ },
9
+ }));
10
+ const opts = (over = {}) => ({
11
+ depart: "2026-08-10",
12
+ adults: 1,
13
+ limit: 25,
14
+ format: "json",
15
+ cache: false,
16
+ ...over,
17
+ });
18
+ // These all fail validation before any browser launch, so they stay deterministic.
19
+ describe("runSearchCommand validation", () => {
20
+ it("rejects a missing origin/destination", async () => {
21
+ await expect(runSearchCommand("", "LAX", opts())).rejects.toThrow(/required/);
22
+ });
23
+ it("rejects a malformed --depart", async () => {
24
+ await expect(runSearchCommand("BOS", "LAX", opts({ depart: "08-10-2026" }))).rejects.toThrow(/--depart must be a valid date/);
25
+ });
26
+ it("rejects a malformed --return", async () => {
27
+ await expect(runSearchCommand("BOS", "LAX", opts({ return: "2026/08/17" }))).rejects.toThrow(/--return must be a valid date/);
28
+ });
29
+ it("rejects an impossible calendar date", async () => {
30
+ await expect(runSearchCommand("BOS", "LAX", opts({ depart: "2026-13-99" }))).rejects.toThrow(/--depart must be a valid date/);
31
+ });
32
+ it("rejects a --return earlier than --depart", async () => {
33
+ await expect(runSearchCommand("BOS", "LAX", opts({ return: "2026-08-01" }))).rejects.toThrow(/--return must be on or after --depart/);
34
+ });
35
+ it("rejects adults < 1", async () => {
36
+ await expect(runSearchCommand("BOS", "LAX", opts({ adults: 0 }))).rejects.toThrow(/--adults must be an integer >= 1/);
37
+ });
38
+ it("rejects a non-numeric --adults (NaN) before launching the browser", async () => {
39
+ await expect(runSearchCommand("BOS", "LAX", opts({ adults: NaN }))).rejects.toThrow(/--adults must be an integer >= 1/);
40
+ });
41
+ it("rejects an invalid --limit", async () => {
42
+ await expect(runSearchCommand("BOS", "LAX", opts({ limit: 0 }))).rejects.toThrow(/--limit must be an integer >= 1/);
43
+ });
44
+ it("rejects a --limit above the single-page maximum", async () => {
45
+ await expect(runSearchCommand("BOS", "LAX", opts({ limit: 50 }))).rejects.toThrow(/--limit cannot exceed 25/);
46
+ });
47
+ it("ignores a malformed --return when --one-way is set", async () => {
48
+ const out = await runSearchCommand("BOS", "LAX", opts({ return: "not-a-date", oneWay: true }));
49
+ expect(out).toContain("\"solutions\"");
50
+ });
51
+ it("rejects an unknown --cabin", async () => {
52
+ await expect(runSearchCommand("BOS", "LAX", opts({ cabin: "economy" }))).rejects.toThrow(/--cabin must be one of/);
53
+ });
54
+ it("rejects an unknown --stops", async () => {
55
+ await expect(runSearchCommand("BOS", "LAX", opts({ stops: "3" }))).rejects.toThrow(/--stops must be one of/);
56
+ });
57
+ it("rejects an unknown --extra-stops", async () => {
58
+ await expect(runSearchCommand("BOS", "LAX", opts({ extraStops: "lots" }))).rejects.toThrow(/--extra-stops must be one of/);
59
+ });
60
+ });
61
+ describe("resolveRouting (--carriers sugar)", () => {
62
+ it("turns multiple --carriers into a grouped OR'd routing code", () => {
63
+ expect(resolveRouting({ carriers: "ua,aa" })).toBe("(UA,AA)+");
64
+ });
65
+ it("does not group a single carrier", () => {
66
+ expect(resolveRouting({ carriers: "ua" })).toBe("UA+");
67
+ });
68
+ it("lets explicit --routing win over --carriers", () => {
69
+ expect(resolveRouting({ carriers: "UA,AA", routing: "X:DFW" })).toBe("X:DFW");
70
+ });
71
+ it("trims whitespace and drops empties between carriers", () => {
72
+ expect(resolveRouting({ carriers: " ua , , aa " })).toBe("(UA,AA)+");
73
+ });
74
+ it("is undefined for an all-empty --carriers", () => {
75
+ expect(resolveRouting({ carriers: " , " })).toBeUndefined();
76
+ });
77
+ it("is undefined when neither is given", () => {
78
+ expect(resolveRouting({})).toBeUndefined();
79
+ });
80
+ });
81
+ describe("runSearchCommand spec wiring", () => {
82
+ const opts = (over = {}) => ({
83
+ depart: "2026-08-10",
84
+ adults: 1,
85
+ limit: 25,
86
+ format: "json",
87
+ cache: false,
88
+ ...over,
89
+ });
90
+ it("passes advanced controls and uppercases airports", async () => {
91
+ captured.length = 0;
92
+ await runSearchCommand("bos", "lax", opts({ cabin: "business", stops: "1", ext: "-REDEYES" }));
93
+ expect(captured[0]).toMatchObject({
94
+ origin: "BOS",
95
+ dest: "LAX",
96
+ cabin: "business",
97
+ stops: "1",
98
+ ext: "-REDEYES",
99
+ });
100
+ });
101
+ it("folds --carriers into the routing field", async () => {
102
+ captured.length = 0;
103
+ await runSearchCommand("BOS", "LAX", opts({ carriers: "UA,AA" }));
104
+ expect(captured[0].routing).toBe("(UA,AA)+");
105
+ });
106
+ it("drops the return date when --one-way is set", async () => {
107
+ captured.length = 0;
108
+ await runSearchCommand("BOS", "LAX", opts({ return: "2026-08-17", oneWay: true }));
109
+ expect(captured[0].returnDate).toBeUndefined();
110
+ });
111
+ it("leaves advanced controls undefined when unset", async () => {
112
+ captured.length = 0;
113
+ await runSearchCommand("BOS", "LAX", opts());
114
+ const s = captured[0];
115
+ expect([s.cabin, s.stops, s.extraStops, s.routing, s.ext]).toEqual([
116
+ undefined,
117
+ undefined,
118
+ undefined,
119
+ undefined,
120
+ undefined,
121
+ ]);
122
+ });
123
+ });
124
+ //# sourceMappingURL=search.test.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"search.test.js","sourceRoot":"","sources":["../../src/commands/search.test.ts"],"names":[],"mappings":"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"}
@@ -0,0 +1,44 @@
1
+ import { type Cabin, type StopLimit } from "../model/spec.js";
2
+ import { type CacheOptions } from "../cache.js";
3
+ export type OutputFormat = "table" | "json";
4
+ /** Cache controls shared by every command (`--no-cache`, `--cache-ttl`). */
5
+ export interface CacheControlOptions {
6
+ /** Default true; `--no-cache` sets it false. */
7
+ cache?: boolean;
8
+ /** TTL in minutes; defaults to {@link DEFAULT_CACHE_TTL_MINUTES}. */
9
+ cacheTtlMinutes?: number;
10
+ }
11
+ export declare function resolveCacheOptions(opts: CacheControlOptions): CacheOptions;
12
+ export declare const DATE_RE: RegExp;
13
+ /**
14
+ * The driver reads a single Matrix results page; pagination isn't driven, so a
15
+ * `--limit` above the page size can't be honored. Cap it instead of silently
16
+ * returning fewer results than asked. (Multi-page fetch is future work.)
17
+ */
18
+ export declare const MAX_LIMIT = 25;
19
+ export declare const CABINS: Cabin[];
20
+ export declare const STOPS: StopLimit[];
21
+ /** Shared cabin/stops/extra-stops/adults/limit validation across all commands. */
22
+ export interface TripControlOptions {
23
+ adults: number;
24
+ limit: number;
25
+ cabin?: string;
26
+ stops?: string;
27
+ extraStops?: string;
28
+ }
29
+ export declare function validateTripControls(opts: TripControlOptions): void;
30
+ /**
31
+ * Reject a page-size `--limit` above {@link MAX_LIMIT}. Used by the page-based
32
+ * commands (search, multicity); calendar's `--limit` is a display cap, not a page
33
+ * size, so it does not call this.
34
+ */
35
+ export declare function requirePageLimit(limit: number): void;
36
+ /**
37
+ * `--carriers UA,AA` → `(UA,AA)+`: one or more segments on *any* listed carrier.
38
+ * The alternatives must be grouped before the `+` repeat operator, otherwise ITA
39
+ * reads `UA,AA+` as "a single UA segment, or one-or-more AA segments", which
40
+ * drops valid connecting itineraries (e.g. all-UA multi-leg). Returns undefined
41
+ * when no carriers are given.
42
+ */
43
+ export declare function carriersToRouting(carriers?: string): string | undefined;
44
+ export declare function requireIsoDate(value: string, flag: string): void;
@@ -0,0 +1,77 @@
1
+ import { CABIN_LABELS, STOPS_LABELS, } from "../model/spec.js";
2
+ import { DEFAULT_CACHE_TTL_MINUTES } from "../cache.js";
3
+ export function resolveCacheOptions(opts) {
4
+ const minutes = opts.cacheTtlMinutes ?? DEFAULT_CACHE_TTL_MINUTES;
5
+ if (!Number.isFinite(minutes) || minutes < 0) {
6
+ throw new Error("--cache-ttl must be a number >= 0 (minutes)");
7
+ }
8
+ return { enabled: opts.cache !== false, ttlMs: minutes * 60_000 };
9
+ }
10
+ export const DATE_RE = /^\d{4}-\d{2}-\d{2}$/;
11
+ /**
12
+ * The driver reads a single Matrix results page; pagination isn't driven, so a
13
+ * `--limit` above the page size can't be honored. Cap it instead of silently
14
+ * returning fewer results than asked. (Multi-page fetch is future work.)
15
+ */
16
+ export const MAX_LIMIT = 25;
17
+ export const CABINS = Object.keys(CABIN_LABELS);
18
+ export const STOPS = Object.keys(STOPS_LABELS);
19
+ export function validateTripControls(opts) {
20
+ if (!Number.isInteger(opts.adults) || opts.adults < 1) {
21
+ throw new Error("--adults must be an integer >= 1");
22
+ }
23
+ if (!Number.isInteger(opts.limit) || opts.limit < 1) {
24
+ throw new Error("--limit must be an integer >= 1");
25
+ }
26
+ if (opts.cabin && !CABINS.includes(opts.cabin)) {
27
+ throw new Error(`--cabin must be one of ${CABINS.join(", ")}, got "${opts.cabin}"`);
28
+ }
29
+ if (opts.stops && !STOPS.includes(opts.stops)) {
30
+ throw new Error(`--stops must be one of ${STOPS.join(", ")}, got "${opts.stops}"`);
31
+ }
32
+ if (opts.extraStops && !STOPS.includes(opts.extraStops)) {
33
+ throw new Error(`--extra-stops must be one of ${STOPS.join(", ")}, got "${opts.extraStops}"`);
34
+ }
35
+ }
36
+ /**
37
+ * Reject a page-size `--limit` above {@link MAX_LIMIT}. Used by the page-based
38
+ * commands (search, multicity); calendar's `--limit` is a display cap, not a page
39
+ * size, so it does not call this.
40
+ */
41
+ export function requirePageLimit(limit) {
42
+ if (limit > MAX_LIMIT) {
43
+ throw new Error(`--limit cannot exceed ${MAX_LIMIT} (one Matrix results page; pagination is not yet supported)`);
44
+ }
45
+ }
46
+ /**
47
+ * `--carriers UA,AA` → `(UA,AA)+`: one or more segments on *any* listed carrier.
48
+ * The alternatives must be grouped before the `+` repeat operator, otherwise ITA
49
+ * reads `UA,AA+` as "a single UA segment, or one-or-more AA segments", which
50
+ * drops valid connecting itineraries (e.g. all-UA multi-leg). Returns undefined
51
+ * when no carriers are given.
52
+ */
53
+ export function carriersToRouting(carriers) {
54
+ if (!carriers)
55
+ return undefined;
56
+ const codes = carriers
57
+ .split(",")
58
+ .map((c) => c.trim().toUpperCase())
59
+ .filter(Boolean);
60
+ if (!codes.length)
61
+ return undefined;
62
+ return codes.length === 1 ? `${codes[0]}+` : `(${codes.join(",")})+`;
63
+ }
64
+ export function requireIsoDate(value, flag) {
65
+ if (!DATE_RE.test(value) || !isRealCalendarDate(value)) {
66
+ throw new Error(`${flag} must be a valid date (YYYY-MM-DD), got "${value}"`);
67
+ }
68
+ }
69
+ /** True only for an existing calendar date — rejects e.g. 2026-13-99, 2026-02-31. */
70
+ function isRealCalendarDate(value) {
71
+ const [y, m, d] = value.split("-").map(Number);
72
+ const date = new Date(Date.UTC(y, m - 1, d));
73
+ return (date.getUTCFullYear() === y &&
74
+ date.getUTCMonth() === m - 1 &&
75
+ date.getUTCDate() === d);
76
+ }
77
+ //# sourceMappingURL=shared.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"shared.js","sourceRoot":"","sources":["../../src/commands/shared.ts"],"names":[],"mappings":"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"}
@@ -0,0 +1,63 @@
1
+ /** Normalized search request the CLI builds and the browser driver consumes. */
2
+ export interface SearchSpec {
3
+ origin: string;
4
+ dest: string;
5
+ departDate: string;
6
+ returnDate?: string;
7
+ adults: number;
8
+ /** Matrix page size; defaults to 25 (Matrix default). */
9
+ limit: number;
10
+ /** Advanced controls (P2). Omitted fields keep Matrix defaults. */
11
+ cabin?: Cabin;
12
+ stops?: StopLimit;
13
+ extraStops?: StopLimit;
14
+ /** ITA routing codes (the flight path) — see docs/ROUTING_CODES.md. */
15
+ routing?: string;
16
+ /** ITA extension codes (faring & filters) — see docs/ROUTING_CODES.md. */
17
+ ext?: string;
18
+ }
19
+ /** One leg of a multi-city trip. Routing/ext are per-slice in Matrix. */
20
+ export interface Slice {
21
+ origin: string;
22
+ dest: string;
23
+ departDate: string;
24
+ routing?: string;
25
+ ext?: string;
26
+ }
27
+ /** Shared advanced controls applied to a whole trip (P3 multicity/calendar). */
28
+ export interface TripOptions {
29
+ adults: number;
30
+ limit: number;
31
+ cabin?: Cabin;
32
+ stops?: StopLimit;
33
+ extraStops?: StopLimit;
34
+ }
35
+ /** Multi-city request: N independent legs (DESIGN P3). */
36
+ export interface MultiCitySpec extends TripOptions {
37
+ slices: Slice[];
38
+ }
39
+ /**
40
+ * Price-calendar request: lowest fare per departure date across a range, for a
41
+ * fixed trip length (DESIGN P3, "See calendar of lowest fares").
42
+ */
43
+ export interface CalendarSpec extends TripOptions {
44
+ origin: string;
45
+ dest: string;
46
+ departFrom: string;
47
+ departTo: string;
48
+ /** Nights between departure and return; omit for one-way calendar. */
49
+ tripLength?: number;
50
+ routing?: string;
51
+ ext?: string;
52
+ }
53
+ export type Cabin = "cheapest" | "premium-economy" | "business" | "first";
54
+ export type StopLimit = "any" | "none" | "1" | "2";
55
+ /** Matrix Cabin dropdown option labels, keyed by CLI value. */
56
+ export declare const CABIN_LABELS: Record<Cabin, string>;
57
+ /** Matrix Stops dropdown option labels, keyed by CLI value. */
58
+ export declare const STOPS_LABELS: Record<StopLimit, string>;
59
+ /** Matrix Extra stops dropdown option labels, keyed by CLI value. */
60
+ export declare const EXTRA_STOPS_LABELS: Record<StopLimit, string>;
61
+ export declare function isRoundTrip(spec: SearchSpec): boolean;
62
+ /** True when any advanced control is set and the panel must be expanded. */
63
+ export declare function hasAdvancedControls(spec: SearchSpec): boolean;
@@ -0,0 +1,29 @@
1
+ /** Matrix Cabin dropdown option labels, keyed by CLI value. */
2
+ export const CABIN_LABELS = {
3
+ cheapest: "Cheapest available",
4
+ "premium-economy": "Premium Economy",
5
+ business: "Business class or higher",
6
+ first: "First class",
7
+ };
8
+ /** Matrix Stops dropdown option labels, keyed by CLI value. */
9
+ export const STOPS_LABELS = {
10
+ any: "No limit",
11
+ none: "Nonstop only",
12
+ "1": "Up to 1 stop",
13
+ "2": "Up to 2 stops",
14
+ };
15
+ /** Matrix Extra stops dropdown option labels, keyed by CLI value. */
16
+ export const EXTRA_STOPS_LABELS = {
17
+ any: "No limit",
18
+ none: "No extra stops",
19
+ "1": "Up to 1 extra stop",
20
+ "2": "Up to 2 extra stops",
21
+ };
22
+ export function isRoundTrip(spec) {
23
+ return Boolean(spec.returnDate);
24
+ }
25
+ /** True when any advanced control is set and the panel must be expanded. */
26
+ export function hasAdvancedControls(spec) {
27
+ return Boolean(spec.cabin || spec.stops || spec.extraStops || spec.routing || spec.ext);
28
+ }
29
+ //# sourceMappingURL=spec.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"spec.js","sourceRoot":"","sources":["../../src/model/spec.ts"],"names":[],"mappings":"AA4DA,+DAA+D;AAC/D,MAAM,CAAC,MAAM,YAAY,GAA0B;IACjD,QAAQ,EAAE,oBAAoB;IAC9B,iBAAiB,EAAE,iBAAiB;IACpC,QAAQ,EAAE,0BAA0B;IACpC,KAAK,EAAE,aAAa;CACrB,CAAC;AAEF,+DAA+D;AAC/D,MAAM,CAAC,MAAM,YAAY,GAA8B;IACrD,GAAG,EAAE,UAAU;IACf,IAAI,EAAE,cAAc;IACpB,GAAG,EAAE,cAAc;IACnB,GAAG,EAAE,eAAe;CACrB,CAAC;AAEF,qEAAqE;AACrE,MAAM,CAAC,MAAM,kBAAkB,GAA8B;IAC3D,GAAG,EAAE,UAAU;IACf,IAAI,EAAE,gBAAgB;IACtB,GAAG,EAAE,oBAAoB;IACzB,GAAG,EAAE,qBAAqB;CAC3B,CAAC;AAEF,MAAM,UAAU,WAAW,CAAC,IAAgB;IAC1C,OAAO,OAAO,CAAC,IAAI,CAAC,UAAU,CAAC,CAAC;AAClC,CAAC;AAED,4EAA4E;AAC5E,MAAM,UAAU,mBAAmB,CAAC,IAAgB;IAClD,OAAO,OAAO,CAAC,IAAI,CAAC,KAAK,IAAI,IAAI,CAAC,KAAK,IAAI,IAAI,CAAC,UAAU,IAAI,IAAI,CAAC,OAAO,IAAI,IAAI,CAAC,GAAG,CAAC,CAAC;AAC1F,CAAC"}
@@ -0,0 +1 @@
1
+ export {};
@@ -0,0 +1,55 @@
1
+ import { describe, it, expect } from "vitest";
2
+ import { CABIN_LABELS, EXTRA_STOPS_LABELS, STOPS_LABELS, hasAdvancedControls, isRoundTrip, } from "./spec.js";
3
+ const base = {
4
+ origin: "BOS",
5
+ dest: "LAX",
6
+ departDate: "2026-08-10",
7
+ adults: 1,
8
+ limit: 25,
9
+ };
10
+ describe("isRoundTrip", () => {
11
+ it("is false without a return date", () => {
12
+ expect(isRoundTrip(base)).toBe(false);
13
+ });
14
+ it("is true with a return date", () => {
15
+ expect(isRoundTrip({ ...base, returnDate: "2026-08-17" })).toBe(true);
16
+ });
17
+ });
18
+ describe("hasAdvancedControls", () => {
19
+ it("is false for a bare spec", () => {
20
+ expect(hasAdvancedControls(base)).toBe(false);
21
+ });
22
+ it("is true when any advanced control is set", () => {
23
+ expect(hasAdvancedControls({ ...base, cabin: "business" })).toBe(true);
24
+ expect(hasAdvancedControls({ ...base, stops: "none" })).toBe(true);
25
+ expect(hasAdvancedControls({ ...base, routing: "UA+" })).toBe(true);
26
+ expect(hasAdvancedControls({ ...base, ext: "-OVERNIGHTS" })).toBe(true);
27
+ });
28
+ });
29
+ describe("Matrix dropdown label maps", () => {
30
+ it("covers every cabin value with the live option label", () => {
31
+ expect(CABIN_LABELS).toEqual({
32
+ cheapest: "Cheapest available",
33
+ "premium-economy": "Premium Economy",
34
+ business: "Business class or higher",
35
+ first: "First class",
36
+ });
37
+ });
38
+ it("covers every stops value", () => {
39
+ expect(STOPS_LABELS).toEqual({
40
+ any: "No limit",
41
+ none: "Nonstop only",
42
+ "1": "Up to 1 stop",
43
+ "2": "Up to 2 stops",
44
+ });
45
+ });
46
+ it("covers every extra-stops value", () => {
47
+ expect(EXTRA_STOPS_LABELS).toEqual({
48
+ any: "No limit",
49
+ none: "No extra stops",
50
+ "1": "Up to 1 extra stop",
51
+ "2": "Up to 2 extra stops",
52
+ });
53
+ });
54
+ });
55
+ //# sourceMappingURL=spec.test.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"spec.test.js","sourceRoot":"","sources":["../../src/model/spec.test.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,QAAQ,EAAE,EAAE,EAAE,MAAM,EAAE,MAAM,QAAQ,CAAC;AAC9C,OAAO,EACL,YAAY,EACZ,kBAAkB,EAClB,YAAY,EACZ,mBAAmB,EACnB,WAAW,GAEZ,MAAM,WAAW,CAAC;AAEnB,MAAM,IAAI,GAAe;IACvB,MAAM,EAAE,KAAK;IACb,IAAI,EAAE,KAAK;IACX,UAAU,EAAE,YAAY;IACxB,MAAM,EAAE,CAAC;IACT,KAAK,EAAE,EAAE;CACV,CAAC;AAEF,QAAQ,CAAC,aAAa,EAAE,GAAG,EAAE;IAC3B,EAAE,CAAC,gCAAgC,EAAE,GAAG,EAAE;QACxC,MAAM,CAAC,WAAW,CAAC,IAAI,CAAC,CAAC,CAAC,IAAI,CAAC,KAAK,CAAC,CAAC;IACxC,CAAC,CAAC,CAAC;IAEH,EAAE,CAAC,4BAA4B,EAAE,GAAG,EAAE;QACpC,MAAM,CAAC,WAAW,CAAC,EAAE,GAAG,IAAI,EAAE,UAAU,EAAE,YAAY,EAAE,CAAC,CAAC,CAAC,IAAI,CAAC,IAAI,CAAC,CAAC;IACxE,CAAC,CAAC,CAAC;AACL,CAAC,CAAC,CAAC;AAEH,QAAQ,CAAC,qBAAqB,EAAE,GAAG,EAAE;IACnC,EAAE,CAAC,0BAA0B,EAAE,GAAG,EAAE;QAClC,MAAM,CAAC,mBAAmB,CAAC,IAAI,CAAC,CAAC,CAAC,IAAI,CAAC,KAAK,CAAC,CAAC;IAChD,CAAC,CAAC,CAAC;IAEH,EAAE,CAAC,0CAA0C,EAAE,GAAG,EAAE;QAClD,MAAM,CAAC,mBAAmB,CAAC,EAAE,GAAG,IAAI,EAAE,KAAK,EAAE,UAAU,EAAE,CAAC,CAAC,CAAC,IAAI,CAAC,IAAI,CAAC,CAAC;QACvE,MAAM,CAAC,mBAAmB,CAAC,EAAE,GAAG,IAAI,EAAE,KAAK,EAAE,MAAM,EAAE,CAAC,CAAC,CAAC,IAAI,CAAC,IAAI,CAAC,CAAC;QACnE,MAAM,CAAC,mBAAmB,CAAC,EAAE,GAAG,IAAI,EAAE,OAAO,EAAE,KAAK,EAAE,CAAC,CAAC,CAAC,IAAI,CAAC,IAAI,CAAC,CAAC;QACpE,MAAM,CAAC,mBAAmB,CAAC,EAAE,GAAG,IAAI,EAAE,GAAG,EAAE,aAAa,EAAE,CAAC,CAAC,CAAC,IAAI,CAAC,IAAI,CAAC,CAAC;IAC1E,CAAC,CAAC,CAAC;AACL,CAAC,CAAC,CAAC;AAEH,QAAQ,CAAC,4BAA4B,EAAE,GAAG,EAAE;IAC1C,EAAE,CAAC,qDAAqD,EAAE,GAAG,EAAE;QAC7D,MAAM,CAAC,YAAY,CAAC,CAAC,OAAO,CAAC;YAC3B,QAAQ,EAAE,oBAAoB;YAC9B,iBAAiB,EAAE,iBAAiB;YACpC,QAAQ,EAAE,0BAA0B;YACpC,KAAK,EAAE,aAAa;SACrB,CAAC,CAAC;IACL,CAAC,CAAC,CAAC;IAEH,EAAE,CAAC,0BAA0B,EAAE,GAAG,EAAE;QAClC,MAAM,CAAC,YAAY,CAAC,CAAC,OAAO,CAAC;YAC3B,GAAG,EAAE,UAAU;YACf,IAAI,EAAE,cAAc;YACpB,GAAG,EAAE,cAAc;YACnB,GAAG,EAAE,eAAe;SACrB,CAAC,CAAC;IACL,CAAC,CAAC,CAAC;IAEH,EAAE,CAAC,gCAAgC,EAAE,GAAG,EAAE;QACxC,MAAM,CAAC,kBAAkB,CAAC,CAAC,OAAO,CAAC;YACjC,GAAG,EAAE,UAAU;YACf,IAAI,EAAE,gBAAgB;YACtB,GAAG,EAAE,oBAAoB;YACzB,GAAG,EAAE,qBAAqB;SAC3B,CAAC,CAAC;IACL,CAAC,CAAC,CAAC;AACL,CAAC,CAAC,CAAC"}