sbb-mcp 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.
@@ -0,0 +1 @@
1
+ {"version":3,"file":"smapi-auth.js","sourceRoot":"","sources":["../../src/transport/smapi-auth.ts"],"names":[],"mappings":"AAAA,kDAAkD;AAClD,qCAAqC;AACrC,iFAAiF;AAEjF,IAAI,WAAW,GAAgD,IAAI,CAAA;AAEnE,MAAM,UAAU,iBAAiB;IAC/B,OAAO,CAAC,CAAC,CACP,OAAO,CAAC,GAAG,CAAC,eAAe;QAC3B,OAAO,CAAC,GAAG,CAAC,mBAAmB;QAC/B,OAAO,CAAC,GAAG,CAAC,WAAW,CACxB,CAAA;AACH,CAAC;AAED,MAAM,CAAC,KAAK,UAAU,cAAc;IAClC,kDAAkD;IAClD,IAAI,WAAW,IAAI,IAAI,CAAC,GAAG,EAAE,GAAG,WAAW,CAAC,SAAS,EAAE,CAAC;QACtD,OAAO,WAAW,CAAC,KAAK,CAAA;IAC1B,CAAC;IAED,MAAM,QAAQ,GAAG,OAAO,CAAC,GAAG,CAAC,eAAe,IAAI,sCAAsC,CAAA;IACtF,MAAM,QAAQ,GAAG,qCAAqC,QAAQ,oBAAoB,CAAA;IAElF,MAAM,IAAI,GAAG,IAAI,eAAe,CAAC;QAC/B,UAAU,EAAE,oBAAoB;QAChC,SAAS,EAAE,OAAO,CAAC,GAAG,CAAC,eAAgB;QACvC,aAAa,EAAE,OAAO,CAAC,GAAG,CAAC,mBAAoB;QAC/C,KAAK,EAAE,OAAO,CAAC,GAAG,CAAC,WAAY;KAChC,CAAC,CAAA;IAEF,MAAM,GAAG,GAAG,MAAM,KAAK,CAAC,QAAQ,EAAE;QAChC,MAAM,EAAE,MAAM;QACd,OAAO,EAAE,EAAE,cAAc,EAAE,mCAAmC,EAAE;QAChE,IAAI,EAAE,IAAI,CAAC,QAAQ,EAAE;KACtB,CAAC,CAAA;IAEF,IAAI,CAAC,GAAG,CAAC,EAAE,EAAE,CAAC;QACZ,MAAM,IAAI,GAAG,MAAM,GAAG,CAAC,IAAI,EAAE,CAAA;QAC7B,MAAM,IAAI,KAAK,CAAC,+BAA+B,GAAG,CAAC,MAAM,IAAI,IAAI,EAAE,CAAC,CAAA;IACtE,CAAC;IAED,MAAM,IAAI,GAAG,MAAM,GAAG,CAAC,IAAI,EAAE,CAAA;IAE7B,WAAW,GAAG;QACZ,KAAK,EAAE,IAAI,CAAC,YAAY;QACxB,SAAS,EAAE,IAAI,CAAC,GAAG,EAAE,GAAG,EAAE,GAAG,EAAE,GAAG,IAAI,EAAE,aAAa;KACtD,CAAA;IAED,OAAO,WAAW,CAAC,KAAK,CAAA;AAC1B,CAAC"}
@@ -0,0 +1,22 @@
1
+ import type { SmapiProblem } from './smapi-types.js';
2
+ export declare function getJourneyBaseUrl(): string;
3
+ export declare function getTicketingBaseUrl(): string;
4
+ export declare class SmapiError extends Error {
5
+ problem?: SmapiProblem;
6
+ status: number;
7
+ constructor(message: string, status: number, problem?: SmapiProblem);
8
+ /** User-facing error messages (translated by SBB per Accept-Language) */
9
+ get displayMessages(): string[];
10
+ }
11
+ type SmapiRequestOptions = {
12
+ method?: 'GET' | 'POST';
13
+ body?: unknown;
14
+ conversationId?: string;
15
+ acceptLanguage?: string;
16
+ };
17
+ /**
18
+ * Make an authenticated request to an SMAPI endpoint.
19
+ * Attaches OAuth token, contract ID, and conversation ID headers.
20
+ */
21
+ export declare function smapiRequest<T>(baseUrl: string, path: string, options?: SmapiRequestOptions): Promise<T>;
22
+ export {};
@@ -0,0 +1,82 @@
1
+ import { getAccessToken } from './smapi-auth.js';
2
+ // Base URLs per environment
3
+ const JOURNEY_URLS = {
4
+ int: 'https://smapi-osdm-journey-int.api.sbb.ch',
5
+ prod: 'https://smapi-osdm-journey.api.sbb.ch',
6
+ };
7
+ const TICKETING_URLS = {
8
+ int: 'https://b2p-int.api.sbb.ch',
9
+ prod: 'https://b2p.api.sbb.ch',
10
+ };
11
+ function getEnv() {
12
+ return process.env.SMAPI_ENV || 'int';
13
+ }
14
+ export function getJourneyBaseUrl() {
15
+ return JOURNEY_URLS[getEnv()];
16
+ }
17
+ export function getTicketingBaseUrl() {
18
+ return TICKETING_URLS[getEnv()];
19
+ }
20
+ export class SmapiError extends Error {
21
+ problem;
22
+ status;
23
+ constructor(message, status, problem) {
24
+ super(message);
25
+ this.name = 'SmapiError';
26
+ this.status = status;
27
+ this.problem = problem;
28
+ }
29
+ /** User-facing error messages (translated by SBB per Accept-Language) */
30
+ get displayMessages() {
31
+ return this.problem?.displayMessages ?? [];
32
+ }
33
+ }
34
+ /**
35
+ * Make an authenticated request to an SMAPI endpoint.
36
+ * Attaches OAuth token, contract ID, and conversation ID headers.
37
+ */
38
+ export async function smapiRequest(baseUrl, path, options = {}) {
39
+ const { method = 'GET', body, conversationId, acceptLanguage = 'en' } = options;
40
+ const token = await getAccessToken();
41
+ const contractId = process.env.SMAPI_CONTRACT_ID || '';
42
+ const headers = {
43
+ Authorization: `Bearer ${token}`,
44
+ 'x-contract-id': contractId,
45
+ 'x-conversation-id': conversationId || crypto.randomUUID(),
46
+ Accept: 'application/json',
47
+ 'Accept-Language': acceptLanguage,
48
+ 'Cache-Control': 'no-cache',
49
+ };
50
+ if (body) {
51
+ headers['Content-Type'] = 'application/json';
52
+ }
53
+ const res = await fetch(`${baseUrl}${path}`, {
54
+ method,
55
+ headers,
56
+ body: body ? JSON.stringify(body) : undefined,
57
+ });
58
+ // Handle RFC 7807 problem responses
59
+ if (!res.ok) {
60
+ let problem;
61
+ try {
62
+ const contentType = res.headers.get('content-type') || '';
63
+ if (contentType.includes('json')) {
64
+ problem = await res.json();
65
+ }
66
+ }
67
+ catch {
68
+ // ignore parse errors
69
+ }
70
+ // Log server-side details (never expose to users)
71
+ if (problem) {
72
+ console.error(`[smapi] ${res.status} ${problem.type}: ${problem.title}`, problem.detail);
73
+ }
74
+ throw new SmapiError(problem?.title || `SMAPI request failed: ${res.status}`, res.status, problem);
75
+ }
76
+ // 204 No Content
77
+ if (res.status === 204) {
78
+ return undefined;
79
+ }
80
+ return res.json();
81
+ }
82
+ //# sourceMappingURL=smapi-client.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"smapi-client.js","sourceRoot":"","sources":["../../src/transport/smapi-client.ts"],"names":[],"mappings":"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"}
@@ -0,0 +1,18 @@
1
+ import type { SmapiPlace, SmapiTrip, SmapiTripsCollection, SmapiTripSearchParams, SmapiPlaceSearchParams } from './smapi-types.js';
2
+ /**
3
+ * Search for stations, addresses, or points of interest by name.
4
+ */
5
+ export declare function searchPlaces(params: SmapiPlaceSearchParams): Promise<SmapiPlace[]>;
6
+ /**
7
+ * Search for trip connections between two stations.
8
+ * Returns a collection with tripIds needed for pricing/booking.
9
+ */
10
+ export declare function searchTrips(params: SmapiTripSearchParams): Promise<SmapiTripsCollection>;
11
+ /**
12
+ * Retrieve a previously found trip by its ID.
13
+ */
14
+ export declare function getTrip(tripId: string, stopBehavior?: 'ORIGIN_DESTINATION_ONLY' | 'REAL_BOARDING_ALIGHTING'): Promise<SmapiTrip>;
15
+ /**
16
+ * Paginate through a trips collection (next/previous departures).
17
+ */
18
+ export declare function paginateTrips(collectionId: string, direction: 'next' | 'previous'): Promise<SmapiTripsCollection>;
@@ -0,0 +1,81 @@
1
+ import { smapiRequest, getJourneyBaseUrl } from './smapi-client.js';
2
+ /**
3
+ * Search for stations, addresses, or points of interest by name.
4
+ */
5
+ export async function searchPlaces(params) {
6
+ const body = {
7
+ placeInput: { name: params.name },
8
+ restrictions: {
9
+ ...(params.type && { type: params.type }),
10
+ numberOfResults: params.numberOfResults ?? 10,
11
+ },
12
+ };
13
+ const result = await smapiRequest(getJourneyBaseUrl(), '/v1/places', { method: 'POST', body });
14
+ return result.places ?? [];
15
+ }
16
+ /**
17
+ * Search for trip connections between two stations.
18
+ * Returns a collection with tripIds needed for pricing/booking.
19
+ */
20
+ export async function searchTrips(params) {
21
+ const body = {
22
+ origin: {
23
+ objectType: 'StopPlaceRef',
24
+ stopPlaceRef: params.origin,
25
+ },
26
+ destination: {
27
+ objectType: 'StopPlaceRef',
28
+ stopPlaceRef: params.destination,
29
+ },
30
+ };
31
+ // departureTime and arrivalTime are mutually exclusive
32
+ if (params.departureTime) {
33
+ body.departureTime = params.departureTime;
34
+ }
35
+ else if (params.arrivalTime) {
36
+ body.arrivalTime = params.arrivalTime;
37
+ }
38
+ else {
39
+ body.departureTime = new Date().toISOString();
40
+ }
41
+ if (params.transferLimit !== undefined) {
42
+ body.parameters = { transferLimit: params.transferLimit };
43
+ }
44
+ if (params.vias?.length) {
45
+ body.vias = params.vias.map((via) => ({
46
+ viaPlace: {
47
+ objectType: 'StopPlaceRef',
48
+ stopPlaceRef: via.stopPlaceRef,
49
+ },
50
+ ...(via.dwellTime && { dwellTime: via.dwellTime }),
51
+ }));
52
+ }
53
+ if (params.ptModeFilter) {
54
+ const parameters = body.parameters || {};
55
+ parameters.dataFilter = {
56
+ ptModeFilter: {
57
+ exclude: params.ptModeFilter.exclude,
58
+ transportModes: params.ptModeFilter.transportModes.map((mode) => ({
59
+ ptMode: mode,
60
+ })),
61
+ },
62
+ };
63
+ body.parameters = parameters;
64
+ }
65
+ return smapiRequest(getJourneyBaseUrl(), '/v1/trips-collection', { method: 'POST', body });
66
+ }
67
+ /**
68
+ * Retrieve a previously found trip by its ID.
69
+ */
70
+ export async function getTrip(tripId, stopBehavior = 'ORIGIN_DESTINATION_ONLY') {
71
+ const params = new URLSearchParams({ stopBehavior });
72
+ return smapiRequest(getJourneyBaseUrl(), `/v1/trips/${encodeURIComponent(tripId)}?${params}`);
73
+ }
74
+ /**
75
+ * Paginate through a trips collection (next/previous departures).
76
+ */
77
+ export async function paginateTrips(collectionId, direction) {
78
+ const params = new URLSearchParams({ page: direction });
79
+ return smapiRequest(getJourneyBaseUrl(), `/v1/trips-collections/${encodeURIComponent(collectionId)}?${params}`);
80
+ }
81
+ //# sourceMappingURL=smapi-journey.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"smapi-journey.js","sourceRoot":"","sources":["../../src/transport/smapi-journey.ts"],"names":[],"mappings":"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"}
@@ -0,0 +1,5 @@
1
+ import type { SmapiPlace, SmapiTripsCollection, SmapiTripSearchParams, SmapiPlaceSearchParams, SmapiPriceResult, SmapiOfferResult, SmapiTraveler } from './smapi-types.js';
2
+ export declare function mockSearchPlaces(params: SmapiPlaceSearchParams): Promise<SmapiPlace[]>;
3
+ export declare function mockSearchTrips(params: SmapiTripSearchParams): Promise<SmapiTripsCollection>;
4
+ export declare function mockGetTripPrices(tripIds: string[]): Promise<SmapiPriceResult[]>;
5
+ export declare function mockGetTripOffers(tripId: string, _travelers: SmapiTraveler[]): Promise<SmapiOfferResult>;
@@ -0,0 +1,144 @@
1
+ // Realistic Swiss station data
2
+ const MOCK_STATIONS = {
3
+ '8503000': {
4
+ objectType: 'StopPlace',
5
+ id: '8503000',
6
+ name: 'Zürich HB',
7
+ geoPosition: { longitude: 8.540192, latitude: 47.378177 },
8
+ ref: { objectType: 'StopPlaceRef', stopPlaceRef: '8503000' },
9
+ },
10
+ '8507000': {
11
+ objectType: 'StopPlace',
12
+ id: '8507000',
13
+ name: 'Bern',
14
+ geoPosition: { longitude: 7.439122, latitude: 46.948825 },
15
+ ref: { objectType: 'StopPlaceRef', stopPlaceRef: '8507000' },
16
+ },
17
+ '8507493': {
18
+ objectType: 'StopPlace',
19
+ id: '8507493',
20
+ name: 'Interlaken Ost',
21
+ geoPosition: { longitude: 7.869161, latitude: 46.690578 },
22
+ ref: { objectType: 'StopPlaceRef', stopPlaceRef: '8507493' },
23
+ },
24
+ '8505000': {
25
+ objectType: 'StopPlace',
26
+ id: '8505000',
27
+ name: 'Luzern',
28
+ geoPosition: { longitude: 7.616425, latitude: 47.062224 },
29
+ ref: { objectType: 'StopPlaceRef', stopPlaceRef: '8505000' },
30
+ },
31
+ '8501120': {
32
+ objectType: 'StopPlace',
33
+ id: '8501120',
34
+ name: 'Genève',
35
+ geoPosition: { longitude: 6.142455, latitude: 46.210207 },
36
+ ref: { objectType: 'StopPlaceRef', stopPlaceRef: '8501120' },
37
+ },
38
+ '8506302': {
39
+ objectType: 'StopPlace',
40
+ id: '8506302',
41
+ name: 'Zermatt',
42
+ geoPosition: { longitude: 7.748040, latitude: 46.024076 },
43
+ ref: { objectType: 'StopPlaceRef', stopPlaceRef: '8506302' },
44
+ },
45
+ };
46
+ // Mock prices (2nd class, adult with Halbtax, in CHF)
47
+ const MOCK_PRICES = {
48
+ 'Zürich HB→Bern': 25,
49
+ 'Bern→Zürich HB': 25,
50
+ 'Zürich HB→Luzern': 12.50,
51
+ 'Luzern→Zürich HB': 12.50,
52
+ 'Bern→Interlaken Ost': 15,
53
+ 'Interlaken Ost→Bern': 15,
54
+ 'Zürich HB→Interlaken Ost': 36,
55
+ 'Interlaken Ost→Zürich HB': 36,
56
+ 'Zürich HB→Genève': 44,
57
+ 'Genève→Zürich HB': 44,
58
+ 'Zürich HB→Zermatt': 64,
59
+ 'Zermatt→Zürich HB': 64,
60
+ 'Bern→Zermatt': 42,
61
+ 'Zermatt→Bern': 42,
62
+ 'Luzern→Interlaken Ost': 16,
63
+ 'Interlaken Ost→Luzern': 16,
64
+ };
65
+ function getMockPrice(from, to) {
66
+ return MOCK_PRICES[`${from}→${to}`] ?? 30; // default CHF 30
67
+ }
68
+ function buildMockTripsCollection(params) {
69
+ const origin = Object.values(MOCK_STATIONS).find((s) => s.id === params.origin || s.name.toLowerCase().includes(params.origin.toLowerCase())) ?? { objectType: 'StopPlace', id: params.origin, name: params.origin };
70
+ const dest = Object.values(MOCK_STATIONS).find((s) => s.id === params.destination || s.name.toLowerCase().includes(params.destination.toLowerCase())) ?? { objectType: 'StopPlace', id: params.destination, name: params.destination };
71
+ const baseTime = params.departureTime
72
+ ? new Date(params.departureTime)
73
+ : new Date();
74
+ // Generate 5 mock connections at 30-min intervals
75
+ const trips = Array.from({ length: 5 }, (_, i) => {
76
+ const dep = new Date(baseTime.getTime() + i * 30 * 60 * 1000);
77
+ const durationMin = 55 + Math.floor(Math.random() * 30); // 55-85 minutes
78
+ const arr = new Date(dep.getTime() + durationMin * 60 * 1000);
79
+ const transfers = i % 3 === 0 ? 0 : i % 3 === 1 ? 1 : 2;
80
+ return {
81
+ id: `mock-trip-${i}-${dep.getTime()}`,
82
+ origin,
83
+ destination: dest,
84
+ startTime: dep.toISOString(),
85
+ endTime: arr.toISOString(),
86
+ duration: `PT${Math.floor(durationMin / 60)}H${durationMin % 60}M`,
87
+ transfers,
88
+ tripStatus: 'PLANNED',
89
+ legs: [
90
+ {
91
+ type: 'timed',
92
+ board: {
93
+ stopPlace: origin,
94
+ departureTime: dep.toISOString(),
95
+ platform: String(Math.floor(Math.random() * 16) + 1),
96
+ },
97
+ alight: {
98
+ stopPlace: dest,
99
+ arrivalTime: arr.toISOString(),
100
+ },
101
+ service: {
102
+ publishedLineName: ['IC 1', 'IC 5', 'IR 15', 'IC 8', 'IR 37'][i],
103
+ operatorName: 'SBB',
104
+ },
105
+ duration: `PT${durationMin}M`,
106
+ },
107
+ ],
108
+ };
109
+ });
110
+ return {
111
+ id: `mock-collection-${Date.now()}`,
112
+ trips,
113
+ tripSummaries: trips.map(({ id, origin: o, destination: d, startTime, endTime, duration, transfers }) => ({
114
+ id, origin: o, destination: d, startTime, endTime, duration, transfers,
115
+ })),
116
+ };
117
+ }
118
+ // ─── Mock API functions ─────────────────────────────────────────────────────
119
+ export async function mockSearchPlaces(params) {
120
+ const query = params.name.toLowerCase();
121
+ return Object.values(MOCK_STATIONS).filter((s) => s.name.toLowerCase().includes(query));
122
+ }
123
+ export async function mockSearchTrips(params) {
124
+ return buildMockTripsCollection(params);
125
+ }
126
+ export async function mockGetTripPrices(tripIds) {
127
+ // Parse origin/destination from mock trip IDs isn't possible,
128
+ // so return a default price range
129
+ return tripIds.map((tripId) => ({
130
+ tripId,
131
+ prices: [
132
+ { amount: 25 + Math.floor(Math.random() * 40), currency: 'CHF', class: '2' },
133
+ { amount: 45 + Math.floor(Math.random() * 60), currency: 'CHF', class: '1' },
134
+ ],
135
+ }));
136
+ }
137
+ export async function mockGetTripOffers(tripId, _travelers) {
138
+ return {
139
+ tripId,
140
+ containers: [],
141
+ affiliateDeepLink: 'https://www.sbb.ch/en/buying/pages/fahrplan/fahrplan.xhtml',
142
+ };
143
+ }
144
+ //# sourceMappingURL=smapi-mock.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"smapi-mock.js","sourceRoot":"","sources":["../../src/transport/smapi-mock.ts"],"names":[],"mappings":"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"}
@@ -0,0 +1,13 @@
1
+ import type { SmapiPriceResult, SmapiOfferResult, SmapiTraveler } from './smapi-types.js';
2
+ /**
3
+ * Get non-bookable price estimates for one or more trips.
4
+ * Default: adult traveler with Halbtax (Half-Fare card).
5
+ * These prices are for display only — they cannot be used for prebooking.
6
+ */
7
+ export declare function getTripPrices(tripIds: string[], travelers?: SmapiTraveler[]): Promise<SmapiPriceResult[]>;
8
+ /**
9
+ * Get trip offers including affiliate deep links to SBB webshop.
10
+ * Call this when the user clicks "Buy on SBB" to get the specific purchase link.
11
+ * Note: This counts toward the look2book ratio — only call when user intends to buy.
12
+ */
13
+ export declare function getTripOffers(tripId: string, travelers: SmapiTraveler[]): Promise<SmapiOfferResult>;
@@ -0,0 +1,71 @@
1
+ import { smapiRequest, getTicketingBaseUrl } from './smapi-client.js';
2
+ /**
3
+ * Get non-bookable price estimates for one or more trips.
4
+ * Default: adult traveler with Halbtax (Half-Fare card).
5
+ * These prices are for display only — they cannot be used for prebooking.
6
+ */
7
+ export async function getTripPrices(tripIds, travelers) {
8
+ // The prices endpoint accepts tripId as query parameter
9
+ // For multiple trips, we make parallel requests
10
+ const results = await Promise.all(tripIds.map(async (tripId) => {
11
+ const params = new URLSearchParams({ tripId });
12
+ // V2 supports traveler details
13
+ const path = travelers?.length
14
+ ? `/api/v2/prices?${params}`
15
+ : `/api/prices?${params}`;
16
+ try {
17
+ const data = await smapiRequest(getTicketingBaseUrl(), path, {
18
+ method: travelers?.length ? 'POST' : 'GET',
19
+ ...(travelers?.length && { body: { travelers } }),
20
+ });
21
+ // Normalize response to SmapiPriceResult
22
+ const prices = data.prices ?? (data.price ? [{
23
+ amount: data.price.amount,
24
+ currency: data.price.currency,
25
+ class: '2',
26
+ }] : []);
27
+ return {
28
+ tripId,
29
+ prices: prices.map((p) => ({
30
+ amount: p.amount,
31
+ currency: p.currency || 'CHF',
32
+ class: (p.class || '2'),
33
+ reductionCard: p.reductionCard,
34
+ })),
35
+ };
36
+ }
37
+ catch (err) {
38
+ console.warn(`[smapi-prices] Failed to get price for trip ${tripId}:`, err);
39
+ return { tripId, prices: [] };
40
+ }
41
+ }));
42
+ return results;
43
+ }
44
+ /**
45
+ * Get trip offers including affiliate deep links to SBB webshop.
46
+ * Call this when the user clicks "Buy on SBB" to get the specific purchase link.
47
+ * Note: This counts toward the look2book ratio — only call when user intends to buy.
48
+ */
49
+ export async function getTripOffers(tripId, travelers) {
50
+ const body = {
51
+ tripId,
52
+ travelers: travelers.map((t) => ({
53
+ externalId: t.id,
54
+ type: t.type,
55
+ ...(t.dateOfBirth && { dateOfBirth: t.dateOfBirth }),
56
+ ...(t.reductionCard && t.reductionCard !== 'NONE' && {
57
+ cards: [{ type: t.reductionCard }],
58
+ }),
59
+ })),
60
+ };
61
+ const data = await smapiRequest(getTicketingBaseUrl(), '/api/trip-offers', { method: 'POST', body });
62
+ // Extract affiliate deep link from response links
63
+ const links = (data.links ?? []);
64
+ const affiliateLink = links.find((l) => l.rel === 'online-offers')?.href;
65
+ return {
66
+ tripId,
67
+ containers: [], // Full offer parsing not needed for deep-link flow
68
+ affiliateDeepLink: affiliateLink,
69
+ };
70
+ }
71
+ //# sourceMappingURL=smapi-prices.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"smapi-prices.js","sourceRoot":"","sources":["../../src/transport/smapi-prices.ts"],"names":[],"mappings":"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"}
@@ -0,0 +1,153 @@
1
+ export type SmapiPlaceType = 'StopPlace' | 'Address' | 'PointOfInterest';
2
+ export type SmapiGeoPosition = {
3
+ longitude: number;
4
+ latitude: number;
5
+ };
6
+ export type SmapiPlaceRef = {
7
+ objectType: 'StopPlaceRef' | 'AddressRef' | 'PointOfInterestRef';
8
+ stopPlaceRef?: string;
9
+ };
10
+ export type SmapiPlace = {
11
+ objectType: SmapiPlaceType;
12
+ id: string;
13
+ name: string;
14
+ geoPosition?: SmapiGeoPosition;
15
+ ref?: SmapiPlaceRef;
16
+ };
17
+ export type SmapiTripStatus = 'PLANNED' | 'CONFIRMED' | 'CHANGED' | 'CANCELLED';
18
+ export type SmapiTimedLeg = {
19
+ type: 'timed';
20
+ board: {
21
+ stopPlace: SmapiPlace;
22
+ departureTime: string;
23
+ platform?: string;
24
+ };
25
+ alight: {
26
+ stopPlace: SmapiPlace;
27
+ arrivalTime: string;
28
+ platform?: string;
29
+ };
30
+ service: {
31
+ publishedLineName?: string;
32
+ operatorName?: string;
33
+ };
34
+ duration: string;
35
+ intermediateStops?: Array<{
36
+ stopPlace: SmapiPlace;
37
+ arrivalTime?: string;
38
+ departureTime?: string;
39
+ }>;
40
+ occupancy?: {
41
+ firstClass?: 'LOW' | 'MEDIUM' | 'HIGH' | 'UNKNOWN';
42
+ secondClass?: 'LOW' | 'MEDIUM' | 'HIGH' | 'UNKNOWN';
43
+ };
44
+ };
45
+ export type SmapiTransferLeg = {
46
+ type: 'transfer';
47
+ start: SmapiPlaceRef;
48
+ end: SmapiPlaceRef;
49
+ duration: string;
50
+ transferMode: string;
51
+ };
52
+ export type SmapiContinuousLeg = {
53
+ type: 'continuous';
54
+ service: {
55
+ providerName?: string;
56
+ };
57
+ duration: string;
58
+ };
59
+ export type SmapiTripLeg = SmapiTimedLeg | SmapiTransferLeg | SmapiContinuousLeg;
60
+ export type SmapiTrip = {
61
+ id: string;
62
+ origin: SmapiPlace;
63
+ destination: SmapiPlace;
64
+ startTime: string;
65
+ endTime: string;
66
+ duration: string;
67
+ transfers: number;
68
+ legs: SmapiTripLeg[];
69
+ tripStatus: SmapiTripStatus;
70
+ };
71
+ export type SmapiTripSummary = {
72
+ id: string;
73
+ origin: SmapiPlace;
74
+ destination: SmapiPlace;
75
+ startTime: string;
76
+ endTime: string;
77
+ duration: string;
78
+ transfers: number;
79
+ };
80
+ export type SmapiTripsCollection = {
81
+ id: string;
82
+ trips: SmapiTrip[];
83
+ tripSummaries: SmapiTripSummary[];
84
+ links?: {
85
+ next?: string;
86
+ previous?: string;
87
+ };
88
+ warnings?: string[];
89
+ };
90
+ export type SmapiTransportMode = 'HIGH_SPEED_TRAIN' | 'HISTORIC_TRAIN' | 'INTERCITY' | 'REGIONAL' | 'INTERREGIONAL' | 'URBAN' | 'TRAIN' | 'TRAM' | 'UNDERGROUND' | 'NIGHT_TRAIN' | 'SHARED_TAXI' | 'MOTOR_RAIL' | 'MOUNTAIN_TRAIN' | 'PLANE' | 'COACH_GROUP' | 'SHIP' | 'BUS';
91
+ export type SmapiTripSearchParams = {
92
+ origin: string;
93
+ destination: string;
94
+ departureTime?: string;
95
+ arrivalTime?: string;
96
+ transferLimit?: number;
97
+ vias?: Array<{
98
+ stopPlaceRef: string;
99
+ dwellTime?: string;
100
+ }>;
101
+ ptModeFilter?: {
102
+ exclude: boolean;
103
+ transportModes: SmapiTransportMode[];
104
+ };
105
+ };
106
+ export type SmapiPlaceSearchParams = {
107
+ name: string;
108
+ type?: 'STOP' | 'ADDRESS' | 'POI';
109
+ numberOfResults?: number;
110
+ };
111
+ export type SmapiReductionCard = 'HALF_FARE' | 'GA' | 'NONE';
112
+ export type SmapiTraveler = {
113
+ id: string;
114
+ type: 'ADULT' | 'CHILD';
115
+ dateOfBirth?: string;
116
+ reductionCard?: SmapiReductionCard;
117
+ firstName?: string;
118
+ lastName?: string;
119
+ };
120
+ export type SmapiPrice = {
121
+ amount: number;
122
+ currency: string;
123
+ class: '1' | '2';
124
+ reductionCard?: SmapiReductionCard;
125
+ };
126
+ export type SmapiPriceResult = {
127
+ tripId: string;
128
+ prices: SmapiPrice[];
129
+ };
130
+ export type SmapiOfferCategory = 'TRANSPORT' | 'RESERVATION' | 'ANCILLARY';
131
+ export type SmapiOffer = {
132
+ offerId: string;
133
+ category: SmapiOfferCategory;
134
+ price: SmapiPrice;
135
+ validUntil: string;
136
+ affiliateLink?: string;
137
+ };
138
+ export type SmapiOfferContainer = {
139
+ offers: SmapiOffer[];
140
+ totalPrice: SmapiPrice;
141
+ };
142
+ export type SmapiOfferResult = {
143
+ tripId: string;
144
+ containers: SmapiOfferContainer[];
145
+ affiliateDeepLink?: string;
146
+ };
147
+ export type SmapiProblem = {
148
+ type: string;
149
+ title?: string;
150
+ status?: number;
151
+ detail?: string;
152
+ displayMessages?: string[];
153
+ };
@@ -0,0 +1,2 @@
1
+ export {};
2
+ //# sourceMappingURL=smapi-types.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"smapi-types.js","sourceRoot":"","sources":["../../src/transport/smapi-types.ts"],"names":[],"mappings":""}