sbb-mcp 0.4.2 → 0.5.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 (74) hide show
  1. package/LICENSE +50 -57
  2. package/README.md +26 -296
  3. package/dist/index.js +47 -17
  4. package/package.json +10 -29
  5. package/dist/auth.d.ts +0 -2
  6. package/dist/auth.js +0 -44
  7. package/dist/auth.js.map +0 -1
  8. package/dist/cache.d.ts +0 -12
  9. package/dist/cache.js +0 -58
  10. package/dist/cache.js.map +0 -1
  11. package/dist/client.d.ts +0 -17
  12. package/dist/client.js +0 -70
  13. package/dist/client.js.map +0 -1
  14. package/dist/formatters.d.ts +0 -22
  15. package/dist/formatters.js +0 -210
  16. package/dist/formatters.js.map +0 -1
  17. package/dist/http.d.ts +0 -2
  18. package/dist/http.js +0 -61
  19. package/dist/http.js.map +0 -1
  20. package/dist/i18n.d.ts +0 -22
  21. package/dist/i18n.js +0 -36
  22. package/dist/i18n.js.map +0 -1
  23. package/dist/index.d.ts +0 -2
  24. package/dist/index.js.map +0 -1
  25. package/dist/journey.d.ts +0 -5
  26. package/dist/journey.js +0 -67
  27. package/dist/journey.js.map +0 -1
  28. package/dist/prices.d.ts +0 -3
  29. package/dist/prices.js +0 -51
  30. package/dist/prices.js.map +0 -1
  31. package/dist/profile.d.ts +0 -16
  32. package/dist/profile.js +0 -77
  33. package/dist/profile.js.map +0 -1
  34. package/dist/rate-limit.d.ts +0 -5
  35. package/dist/rate-limit.js +0 -44
  36. package/dist/rate-limit.js.map +0 -1
  37. package/dist/structured.d.ts +0 -119
  38. package/dist/structured.js +0 -133
  39. package/dist/structured.js.map +0 -1
  40. package/dist/swisstrip.d.ts +0 -41
  41. package/dist/swisstrip.js +0 -119
  42. package/dist/swisstrip.js.map +0 -1
  43. package/dist/tools.d.ts +0 -2
  44. package/dist/tools.js +0 -464
  45. package/dist/tools.js.map +0 -1
  46. package/dist/transport/index.d.ts +0 -9
  47. package/dist/transport/index.js +0 -9
  48. package/dist/transport/index.js.map +0 -1
  49. package/dist/transport/smapi-auth.d.ts +0 -2
  50. package/dist/transport/smapi-auth.js +0 -39
  51. package/dist/transport/smapi-auth.js.map +0 -1
  52. package/dist/transport/smapi-client.d.ts +0 -22
  53. package/dist/transport/smapi-client.js +0 -83
  54. package/dist/transport/smapi-client.js.map +0 -1
  55. package/dist/transport/smapi-journey.d.ts +0 -23
  56. package/dist/transport/smapi-journey.js +0 -82
  57. package/dist/transport/smapi-journey.js.map +0 -1
  58. package/dist/transport/smapi-mock.d.ts +0 -9
  59. package/dist/transport/smapi-mock.js +0 -151
  60. package/dist/transport/smapi-mock.js.map +0 -1
  61. package/dist/transport/smapi-prices.d.ts +0 -14
  62. package/dist/transport/smapi-prices.js +0 -73
  63. package/dist/transport/smapi-prices.js.map +0 -1
  64. package/dist/transport/smapi-types.d.ts +0 -153
  65. package/dist/transport/smapi-types.js +0 -2
  66. package/dist/transport/smapi-types.js.map +0 -1
  67. package/dist/types.d.ts +0 -139
  68. package/dist/types.js +0 -3
  69. package/dist/types.js.map +0 -1
  70. package/dist/widgets.d.ts +0 -33
  71. package/dist/widgets.js +0 -120
  72. package/dist/widgets.js.map +0 -1
  73. package/web/dist/widgets.css +0 -1
  74. package/web/dist/widgets.js +0 -1
@@ -1,9 +0,0 @@
1
- export { isSmapiConfigured } from './smapi-auth.js';
2
- export { searchPlaces } from './smapi-journey.js';
3
- export { searchTrips } from './smapi-journey.js';
4
- export { getTrip } from './smapi-journey.js';
5
- export { paginateTrips } from './smapi-journey.js';
6
- export { getTripPrices } from './smapi-prices.js';
7
- export { getTripOffers } from './smapi-prices.js';
8
- export { mockSearchPlaces, mockSearchTrips, mockGetTripPrices, mockGetTripOffers, } from './smapi-mock.js';
9
- //# sourceMappingURL=index.js.map
@@ -1 +0,0 @@
1
- {"version":3,"file":"index.js","sourceRoot":"","sources":["../../src/transport/index.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,iBAAiB,EAAE,MAAM,iBAAiB,CAAA;AACnD,OAAO,EAAE,YAAY,EAAE,MAAM,oBAAoB,CAAA;AACjD,OAAO,EAAE,WAAW,EAAE,MAAM,oBAAoB,CAAA;AAChD,OAAO,EAAE,OAAO,EAAE,MAAM,oBAAoB,CAAA;AAC5C,OAAO,EAAE,aAAa,EAAE,MAAM,oBAAoB,CAAA;AAClD,OAAO,EAAE,aAAa,EAAE,MAAM,mBAAmB,CAAA;AACjD,OAAO,EAAE,aAAa,EAAE,MAAM,mBAAmB,CAAA;AACjD,OAAO,EACL,gBAAgB,EAChB,eAAe,EACf,iBAAiB,EACjB,iBAAiB,GAClB,MAAM,iBAAiB,CAAA"}
@@ -1,2 +0,0 @@
1
- export declare function isSmapiConfigured(): boolean;
2
- export declare function getAccessToken(): Promise<string>;
@@ -1,39 +0,0 @@
1
- // OAuth 2.0 Client Credentials flow for SBB SMAPI
2
- // Token endpoint: Microsoft Azure AD
3
- // Tokens cached in-memory with 55-min TTL (5-min safety margin on 60-min expiry)
4
- let cachedToken = null;
5
- export function isSmapiConfigured() {
6
- return !!(process.env.SMAPI_CLIENT_ID &&
7
- process.env.SMAPI_CLIENT_SECRET &&
8
- process.env.SMAPI_SCOPE);
9
- }
10
- export async function getAccessToken() {
11
- // Return cached token if still valid (55-min TTL)
12
- if (cachedToken && Date.now() < cachedToken.expiresAt) {
13
- return cachedToken.token;
14
- }
15
- const tenantId = process.env.SMAPI_TENANT_ID || '2cda5d11-f0ac-46b3-967d-af1b2e1bd01a';
16
- const tokenUrl = `https://login.microsoftonline.com/${tenantId}/oauth2/v2.0/token`;
17
- const body = new URLSearchParams({
18
- grant_type: 'client_credentials',
19
- client_id: process.env.SMAPI_CLIENT_ID,
20
- client_secret: process.env.SMAPI_CLIENT_SECRET,
21
- scope: process.env.SMAPI_SCOPE,
22
- });
23
- const res = await fetch(tokenUrl, {
24
- method: 'POST',
25
- headers: { 'Content-Type': 'application/x-www-form-urlencoded' },
26
- body: body.toString(),
27
- });
28
- if (!res.ok) {
29
- const text = await res.text();
30
- throw new Error(`SMAPI token request failed: ${res.status} ${text}`);
31
- }
32
- const data = await res.json();
33
- cachedToken = {
34
- token: data.access_token,
35
- expiresAt: Date.now() + 55 * 60 * 1000, // 55 minutes
36
- };
37
- return cachedToken.token;
38
- }
39
- //# sourceMappingURL=smapi-auth.js.map
@@ -1 +0,0 @@
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"}
@@ -1,22 +0,0 @@
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 {};
@@ -1,83 +0,0 @@
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
- 'User-Agent': 'SwissTrip/1.0 (https://swisstrip.app)',
50
- };
51
- if (body) {
52
- headers['Content-Type'] = 'application/json';
53
- }
54
- const res = await fetch(`${baseUrl}${path}`, {
55
- method,
56
- headers,
57
- body: body ? JSON.stringify(body) : undefined,
58
- });
59
- // Handle RFC 7807 problem responses
60
- if (!res.ok) {
61
- let problem;
62
- try {
63
- const contentType = res.headers.get('content-type') || '';
64
- if (contentType.includes('json')) {
65
- problem = await res.json();
66
- }
67
- }
68
- catch {
69
- // ignore parse errors
70
- }
71
- // Log server-side details (never expose to users)
72
- if (problem) {
73
- console.error(`[smapi] ${res.status} ${problem.type}: ${problem.title}`, problem.detail);
74
- }
75
- throw new SmapiError(problem?.title || `SMAPI request failed: ${res.status}`, res.status, problem);
76
- }
77
- // 204 No Content
78
- if (res.status === 204) {
79
- return undefined;
80
- }
81
- return res.json();
82
- }
83
- //# sourceMappingURL=smapi-client.js.map
@@ -1 +0,0 @@
1
- {"version":3,"file":"smapi-client.js","sourceRoot":"","sources":["../../src/transport/smapi-client.ts"],"names":[],"mappings":"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"}
@@ -1,23 +0,0 @@
1
- import type { SmapiPlace, SmapiTrip, SmapiTripsCollection, SmapiTripSearchParams, SmapiPlaceSearchParams } from './smapi-types.js';
2
- import type { Lang } from '../i18n.js';
3
- /**
4
- * Search for stations, addresses, or points of interest by name.
5
- */
6
- export declare function searchPlaces(params: SmapiPlaceSearchParams & {
7
- lang?: Lang;
8
- }): Promise<SmapiPlace[]>;
9
- /**
10
- * Search for trip connections between two stations.
11
- * Returns a collection with tripIds needed for pricing/booking.
12
- */
13
- export declare function searchTrips(params: SmapiTripSearchParams & {
14
- lang?: Lang;
15
- }): Promise<SmapiTripsCollection>;
16
- /**
17
- * Retrieve a previously found trip by its ID.
18
- */
19
- export declare function getTrip(tripId: string, stopBehavior?: 'ORIGIN_DESTINATION_ONLY' | 'REAL_BOARDING_ALIGHTING', lang?: Lang): Promise<SmapiTrip>;
20
- /**
21
- * Paginate through a trips collection (next/previous departures).
22
- */
23
- export declare function paginateTrips(collectionId: string, direction: 'next' | 'previous', lang?: Lang): Promise<SmapiTripsCollection>;
@@ -1,82 +0,0 @@
1
- import { smapiRequest, getJourneyBaseUrl } from './smapi-client.js';
2
- import { toLocale } from '../i18n.js';
3
- /**
4
- * Search for stations, addresses, or points of interest by name.
5
- */
6
- export async function searchPlaces(params) {
7
- const body = {
8
- placeInput: { name: params.name },
9
- restrictions: {
10
- ...(params.type && { type: params.type }),
11
- numberOfResults: params.numberOfResults ?? 10,
12
- },
13
- };
14
- const result = await smapiRequest(getJourneyBaseUrl(), '/v1/places', { method: 'POST', body, acceptLanguage: params.lang ? toLocale(params.lang) : undefined });
15
- return result.places ?? [];
16
- }
17
- /**
18
- * Search for trip connections between two stations.
19
- * Returns a collection with tripIds needed for pricing/booking.
20
- */
21
- export async function searchTrips(params) {
22
- const body = {
23
- origin: {
24
- objectType: 'StopPlaceRef',
25
- stopPlaceRef: params.origin,
26
- },
27
- destination: {
28
- objectType: 'StopPlaceRef',
29
- stopPlaceRef: params.destination,
30
- },
31
- };
32
- // departureTime and arrivalTime are mutually exclusive
33
- if (params.departureTime) {
34
- body.departureTime = params.departureTime;
35
- }
36
- else if (params.arrivalTime) {
37
- body.arrivalTime = params.arrivalTime;
38
- }
39
- else {
40
- body.departureTime = new Date().toISOString();
41
- }
42
- if (params.transferLimit !== undefined) {
43
- body.parameters = { transferLimit: params.transferLimit };
44
- }
45
- if (params.vias?.length) {
46
- body.vias = params.vias.map((via) => ({
47
- viaPlace: {
48
- objectType: 'StopPlaceRef',
49
- stopPlaceRef: via.stopPlaceRef,
50
- },
51
- ...(via.dwellTime && { dwellTime: via.dwellTime }),
52
- }));
53
- }
54
- if (params.ptModeFilter) {
55
- const parameters = body.parameters || {};
56
- parameters.dataFilter = {
57
- ptModeFilter: {
58
- exclude: params.ptModeFilter.exclude,
59
- transportModes: params.ptModeFilter.transportModes.map((mode) => ({
60
- ptMode: mode,
61
- })),
62
- },
63
- };
64
- body.parameters = parameters;
65
- }
66
- return smapiRequest(getJourneyBaseUrl(), '/v1/trips-collection', { method: 'POST', body, acceptLanguage: params.lang ? toLocale(params.lang) : undefined });
67
- }
68
- /**
69
- * Retrieve a previously found trip by its ID.
70
- */
71
- export async function getTrip(tripId, stopBehavior = 'ORIGIN_DESTINATION_ONLY', lang) {
72
- const params = new URLSearchParams({ stopBehavior });
73
- return smapiRequest(getJourneyBaseUrl(), `/v1/trips/${encodeURIComponent(tripId)}?${params}`, { acceptLanguage: lang ? toLocale(lang) : undefined });
74
- }
75
- /**
76
- * Paginate through a trips collection (next/previous departures).
77
- */
78
- export async function paginateTrips(collectionId, direction, lang) {
79
- const params = new URLSearchParams({ page: direction });
80
- return smapiRequest(getJourneyBaseUrl(), `/v1/trips-collections/${encodeURIComponent(collectionId)}?${params}`, { acceptLanguage: lang ? toLocale(lang) : undefined });
81
- }
82
- //# sourceMappingURL=smapi-journey.js.map
@@ -1 +0,0 @@
1
- {"version":3,"file":"smapi-journey.js","sourceRoot":"","sources":["../../src/transport/smapi-journey.ts"],"names":[],"mappings":"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"}
@@ -1,9 +0,0 @@
1
- import type { SmapiPlace, SmapiTripsCollection, SmapiTripSearchParams, SmapiPlaceSearchParams, SmapiPriceResult, SmapiOfferResult, SmapiTraveler } from './smapi-types.js';
2
- export declare function mockSearchPlaces(params: SmapiPlaceSearchParams & {
3
- lang?: string;
4
- }): Promise<SmapiPlace[]>;
5
- export declare function mockSearchTrips(params: SmapiTripSearchParams & {
6
- lang?: string;
7
- }): Promise<SmapiTripsCollection>;
8
- export declare function mockGetTripPrices(tripIds: string[]): Promise<SmapiPriceResult[]>;
9
- export declare function mockGetTripOffers(tripId: string, _travelers: SmapiTraveler[]): Promise<SmapiOfferResult>;
@@ -1,151 +0,0 @@
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
- /** Normalize Swiss chars for fuzzy matching: ü→u, ö→o, ä→a, è→e, é→e */
119
- function normalize(str) {
120
- return str
121
- .toLowerCase()
122
- .normalize('NFD')
123
- .replace(/[\u0300-\u036f]/g, '');
124
- }
125
- // ─── Mock API functions ─────────────────────────────────────────────────────
126
- export async function mockSearchPlaces(params) {
127
- const query = normalize(params.name);
128
- return Object.values(MOCK_STATIONS).filter((s) => normalize(s.name).includes(query));
129
- }
130
- export async function mockSearchTrips(params) {
131
- return buildMockTripsCollection(params);
132
- }
133
- export async function mockGetTripPrices(tripIds) {
134
- // Parse origin/destination from mock trip IDs isn't possible,
135
- // so return a default price range
136
- return tripIds.map((tripId) => ({
137
- tripId,
138
- prices: [
139
- { amount: 25 + Math.floor(Math.random() * 40), currency: 'CHF', class: '2' },
140
- { amount: 45 + Math.floor(Math.random() * 60), currency: 'CHF', class: '1' },
141
- ],
142
- }));
143
- }
144
- export async function mockGetTripOffers(tripId, _travelers) {
145
- return {
146
- tripId,
147
- containers: [],
148
- affiliateDeepLink: 'https://www.sbb.ch/en/buying/pages/fahrplan/fahrplan.xhtml',
149
- };
150
- }
151
- //# sourceMappingURL=smapi-mock.js.map
@@ -1 +0,0 @@
1
- {"version":3,"file":"smapi-mock.js","sourceRoot":"","sources":["../../src/transport/smapi-mock.ts"],"names":[],"mappings":"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"}
@@ -1,14 +0,0 @@
1
- import type { SmapiPriceResult, SmapiOfferResult, SmapiTraveler } from './smapi-types.js';
2
- import type { Lang } from '../i18n.js';
3
- /**
4
- * Get non-bookable price estimates for one or more trips.
5
- * Default: adult traveler with Halbtax (Half-Fare card).
6
- * These prices are for display only — they cannot be used for prebooking.
7
- */
8
- export declare function getTripPrices(tripIds: string[], travelers?: SmapiTraveler[], lang?: Lang): Promise<SmapiPriceResult[]>;
9
- /**
10
- * Get trip offers including affiliate deep links to SBB webshop.
11
- * Call this when the user clicks "Buy on SBB" to get the specific purchase link.
12
- * Note: This counts toward the look2book ratio — only call when user intends to buy.
13
- */
14
- export declare function getTripOffers(tripId: string, travelers: SmapiTraveler[], lang?: Lang): Promise<SmapiOfferResult>;
@@ -1,73 +0,0 @@
1
- import { smapiRequest, getTicketingBaseUrl } from './smapi-client.js';
2
- import { toLocale } from '../i18n.js';
3
- /**
4
- * Get non-bookable price estimates for one or more trips.
5
- * Default: adult traveler with Halbtax (Half-Fare card).
6
- * These prices are for display only — they cannot be used for prebooking.
7
- */
8
- export async function getTripPrices(tripIds, travelers, lang) {
9
- // The prices endpoint accepts tripId as query parameter
10
- // For multiple trips, we make parallel requests
11
- const results = await Promise.all(tripIds.map(async (tripId) => {
12
- const params = new URLSearchParams({ tripId });
13
- // V2 supports traveler details
14
- const path = travelers?.length
15
- ? `/api/v2/prices?${params}`
16
- : `/api/prices?${params}`;
17
- try {
18
- const data = await smapiRequest(getTicketingBaseUrl(), path, {
19
- method: travelers?.length ? 'POST' : 'GET',
20
- ...(travelers?.length && { body: { travelers } }),
21
- acceptLanguage: lang ? toLocale(lang) : undefined,
22
- });
23
- // Normalize response to SmapiPriceResult
24
- const prices = data.prices ?? (data.price ? [{
25
- amount: data.price.amount,
26
- currency: data.price.currency,
27
- class: '2',
28
- }] : []);
29
- return {
30
- tripId,
31
- prices: prices.map((p) => ({
32
- amount: p.amount,
33
- currency: p.currency || 'CHF',
34
- class: (p.class || '2'),
35
- reductionCard: p.reductionCard,
36
- })),
37
- };
38
- }
39
- catch (err) {
40
- console.warn(`[smapi-prices] Failed to get price for trip ${tripId}:`, err);
41
- return { tripId, prices: [] };
42
- }
43
- }));
44
- return results;
45
- }
46
- /**
47
- * Get trip offers including affiliate deep links to SBB webshop.
48
- * Call this when the user clicks "Buy on SBB" to get the specific purchase link.
49
- * Note: This counts toward the look2book ratio — only call when user intends to buy.
50
- */
51
- export async function getTripOffers(tripId, travelers, lang) {
52
- const body = {
53
- tripId,
54
- travelers: travelers.map((t) => ({
55
- externalId: t.id,
56
- type: t.type,
57
- ...(t.dateOfBirth && { dateOfBirth: t.dateOfBirth }),
58
- ...(t.reductionCard && t.reductionCard !== 'NONE' && {
59
- cards: [{ type: t.reductionCard }],
60
- }),
61
- })),
62
- };
63
- const data = await smapiRequest(getTicketingBaseUrl(), '/api/trip-offers', { method: 'POST', body, acceptLanguage: lang ? toLocale(lang) : undefined });
64
- // Extract affiliate deep link from response links
65
- const links = (data.links ?? []);
66
- const affiliateLink = links.find((l) => l.rel === 'online-offers')?.href;
67
- return {
68
- tripId,
69
- containers: [], // Full offer parsing not needed for deep-link flow
70
- affiliateDeepLink: affiliateLink,
71
- };
72
- }
73
- //# sourceMappingURL=smapi-prices.js.map
@@ -1 +0,0 @@
1
- {"version":3,"file":"smapi-prices.js","sourceRoot":"","sources":["../../src/transport/smapi-prices.ts"],"names":[],"mappings":"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"}