sbb-mcp 0.4.3 → 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 (83) hide show
  1. package/LICENSE +50 -57
  2. package/README.md +25 -214
  3. package/dist/index.js +47 -19
  4. package/package.json +10 -33
  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 -14
  9. package/dist/cache.js +0 -62
  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 -35
  15. package/dist/formatters.js +0 -285
  16. package/dist/formatters.js.map +0 -1
  17. package/dist/http.d.ts +0 -2
  18. package/dist/http.js +0 -117
  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/look2book.d.ts +0 -98
  29. package/dist/look2book.js +0 -212
  30. package/dist/look2book.js.map +0 -1
  31. package/dist/prices.d.ts +0 -3
  32. package/dist/prices.js +0 -51
  33. package/dist/prices.js.map +0 -1
  34. package/dist/profile.d.ts +0 -16
  35. package/dist/profile.js +0 -84
  36. package/dist/profile.js.map +0 -1
  37. package/dist/rate-limit.d.ts +0 -5
  38. package/dist/rate-limit.js +0 -44
  39. package/dist/rate-limit.js.map +0 -1
  40. package/dist/shortlink.d.ts +0 -60
  41. package/dist/shortlink.js +0 -122
  42. package/dist/shortlink.js.map +0 -1
  43. package/dist/structured.d.ts +0 -125
  44. package/dist/structured.js +0 -134
  45. package/dist/structured.js.map +0 -1
  46. package/dist/swisstrip.d.ts +0 -41
  47. package/dist/swisstrip.js +0 -135
  48. package/dist/swisstrip.js.map +0 -1
  49. package/dist/tools.d.ts +0 -40
  50. package/dist/tools.js +0 -509
  51. package/dist/tools.js.map +0 -1
  52. package/dist/transport/index.d.ts +0 -10
  53. package/dist/transport/index.js +0 -13
  54. package/dist/transport/index.js.map +0 -1
  55. package/dist/transport/setup.d.ts +0 -1
  56. package/dist/transport/setup.js +0 -59
  57. package/dist/transport/setup.js.map +0 -1
  58. package/dist/transport/smapi-auth.d.ts +0 -14
  59. package/dist/transport/smapi-auth.js +0 -89
  60. package/dist/transport/smapi-auth.js.map +0 -1
  61. package/dist/transport/smapi-client.d.ts +0 -46
  62. package/dist/transport/smapi-client.js +0 -186
  63. package/dist/transport/smapi-client.js.map +0 -1
  64. package/dist/transport/smapi-journey.d.ts +0 -29
  65. package/dist/transport/smapi-journey.js +0 -91
  66. package/dist/transport/smapi-journey.js.map +0 -1
  67. package/dist/transport/smapi-mock.d.ts +0 -9
  68. package/dist/transport/smapi-mock.js +0 -151
  69. package/dist/transport/smapi-mock.js.map +0 -1
  70. package/dist/transport/smapi-prices.d.ts +0 -48
  71. package/dist/transport/smapi-prices.js +0 -144
  72. package/dist/transport/smapi-prices.js.map +0 -1
  73. package/dist/transport/smapi-types.d.ts +0 -181
  74. package/dist/transport/smapi-types.js +0 -2
  75. package/dist/transport/smapi-types.js.map +0 -1
  76. package/dist/types.d.ts +0 -139
  77. package/dist/types.js +0 -3
  78. package/dist/types.js.map +0 -1
  79. package/dist/widgets.d.ts +0 -60
  80. package/dist/widgets.js +0 -184
  81. package/dist/widgets.js.map +0 -1
  82. package/web/dist/widgets.css +0 -1
  83. package/web/dist/widgets.js +0 -1
@@ -1,14 +0,0 @@
1
- /** Look2Book family — also selects which credential set we use for OAuth. */
2
- export type AuthFamily = 'journey' | 'ticketing';
3
- /**
4
- * True if at least one API family is configured (journey OR ticketing).
5
- * Pass `family` to check that specific family only.
6
- *
7
- * Consumers picking between mock vs. live code paths should prefer the
8
- * family-specific form: e.g. stations/connections need 'journey' to be live,
9
- * prices/offers need 'ticketing'. See sbb-services for examples.
10
- */
11
- export declare function isSmapiConfigured(family?: AuthFamily): boolean;
12
- export declare function getAccessToken(family?: AuthFamily): Promise<string>;
13
- /** Test hook: clear cached OAuth tokens for all families. */
14
- export declare function __resetTokenCacheForTesting(): void;
@@ -1,89 +0,0 @@
1
- // AUTO-GENERATED from packages/sbb-transport/src/smapi-auth.ts
2
- // Run `npm run sync:transport` in packages/sbb-mcp to refresh.
3
- // Do not edit this file directly — edit the sbb-transport source instead.
4
- // OAuth 2.0 Client Credentials flow for SBB SMAPI
5
- // Token endpoint: Microsoft Azure AD (SBB's tenant)
6
- // Tokens cached in-memory per API family with 55-min TTL (5-min safety margin).
7
- //
8
- // ## Per-API credentials
9
- // SBB issues a separate Azure AD application per API family (Journey and
10
- // Ticketing). Each app has its own Client ID, Secret, and Scope. Both live
11
- // under SBB's shared tenant and share the same token endpoint — only the
12
- // three credential fields differ.
13
- //
14
- // Env var lookup order (each family independently):
15
- // 1. Family-specific: SMAPI_<FAMILY>_CLIENT_ID / _CLIENT_SECRET / _SCOPE
16
- // 2. Generic fallback: SMAPI_CLIENT_ID / _CLIENT_SECRET / _SCOPE
17
- //
18
- // The generic fallback keeps single-app setups (e.g. when only one API is
19
- // approved) working without env duplication — set the generic vars and all
20
- // families use them. Once the second API is approved, add the family-specific
21
- // overrides.
22
- /**
23
- * Resolve credentials for the given family.
24
- * Returns null if neither family-specific NOR generic fallback is complete.
25
- */
26
- function getCreds(family) {
27
- const prefix = family === 'journey' ? 'SMAPI_JOURNEY_' : 'SMAPI_TICKETING_';
28
- const clientId = process.env[`${prefix}CLIENT_ID`] || process.env.SMAPI_CLIENT_ID;
29
- const clientSecret = process.env[`${prefix}CLIENT_SECRET`] || process.env.SMAPI_CLIENT_SECRET;
30
- const scope = process.env[`${prefix}SCOPE`] || process.env.SMAPI_SCOPE;
31
- if (clientId && clientSecret && scope) {
32
- return { clientId, clientSecret, scope };
33
- }
34
- return null;
35
- }
36
- /**
37
- * True if at least one API family is configured (journey OR ticketing).
38
- * Pass `family` to check that specific family only.
39
- *
40
- * Consumers picking between mock vs. live code paths should prefer the
41
- * family-specific form: e.g. stations/connections need 'journey' to be live,
42
- * prices/offers need 'ticketing'. See sbb-services for examples.
43
- */
44
- export function isSmapiConfigured(family) {
45
- if (family)
46
- return getCreds(family) !== null;
47
- return getCreds('journey') !== null || getCreds('ticketing') !== null;
48
- }
49
- const tokenCache = {};
50
- export async function getAccessToken(family = 'journey') {
51
- // Return cached token if still valid (55-min TTL)
52
- const cached = tokenCache[family];
53
- if (cached && Date.now() < cached.expiresAt) {
54
- return cached.token;
55
- }
56
- const creds = getCreds(family);
57
- if (!creds) {
58
- throw new Error(`SMAPI credentials not configured for ${family} API. Set SMAPI_${family.toUpperCase()}_CLIENT_ID/SECRET/SCOPE or the generic SMAPI_CLIENT_ID/SECRET/SCOPE fallback.`);
59
- }
60
- const tenantId = process.env.SMAPI_TENANT_ID || '2cda5d11-f0ac-46b3-967d-af1b2e1bd01a';
61
- const tokenUrl = `https://login.microsoftonline.com/${tenantId}/oauth2/v2.0/token`;
62
- const body = new URLSearchParams({
63
- grant_type: 'client_credentials',
64
- client_id: creds.clientId,
65
- client_secret: creds.clientSecret,
66
- scope: creds.scope,
67
- });
68
- const res = await fetch(tokenUrl, {
69
- method: 'POST',
70
- headers: { 'Content-Type': 'application/x-www-form-urlencoded' },
71
- body: body.toString(),
72
- });
73
- if (!res.ok) {
74
- const text = await res.text();
75
- throw new Error(`SMAPI token request failed (${family}): ${res.status} ${text}`);
76
- }
77
- const data = await res.json();
78
- tokenCache[family] = {
79
- token: data.access_token,
80
- expiresAt: Date.now() + 55 * 60 * 1000,
81
- };
82
- return data.access_token;
83
- }
84
- /** Test hook: clear cached OAuth tokens for all families. */
85
- export function __resetTokenCacheForTesting() {
86
- tokenCache.journey = undefined;
87
- tokenCache.ticketing = undefined;
88
- }
89
- //# sourceMappingURL=smapi-auth.js.map
@@ -1 +0,0 @@
1
- {"version":3,"file":"smapi-auth.js","sourceRoot":"","sources":["../../src/transport/smapi-auth.ts"],"names":[],"mappings":"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"}
@@ -1,46 +0,0 @@
1
- import type { SmapiProblem } from './smapi-types.js';
2
- export declare function getJourneyBaseUrl(): string;
3
- export declare function getTicketingBaseUrl(): string;
4
- /**
5
- * Look2Book ratio family — what the contract counts a request against.
6
- * Journey API calls bucket as `trips`; Ticketing API calls bucket as `offers`.
7
- */
8
- export type ApiFamily = 'trips' | 'offers';
9
- /**
10
- * Pluggable API-call recorder. Default no-op so this package is consumable
11
- * without any side-effect setup; consumers (sbb-mcp, swisstrip web) wire a
12
- * real recorder at module-init via `setApiCallRecorder`.
13
- *
14
- * Recorders MUST be cheap and non-throwing — a recorder failure must never
15
- * affect the caller. Async work should be fire-and-forget.
16
- */
17
- export type ApiCallRecorder = (family: ApiFamily, path: string) => void;
18
- export declare function setApiCallRecorder(fn: ApiCallRecorder): void;
19
- export declare class SmapiError extends Error {
20
- problem?: SmapiProblem;
21
- status: number;
22
- constructor(message: string, status: number, problem?: SmapiProblem);
23
- /** User-facing error messages (translated by SBB per Accept-Language) */
24
- get displayMessages(): string[];
25
- }
26
- type SmapiRequestOptions = {
27
- method?: 'GET' | 'POST';
28
- body?: unknown;
29
- conversationId?: string;
30
- acceptLanguage?: string;
31
- };
32
- /**
33
- * Make an authenticated request to an SMAPI endpoint.
34
- *
35
- * Header strategy:
36
- * - Always attach: OAuth bearer, x-contract-id, x-conversation-id,
37
- * Accept, Accept-Language, Cache-Control, User-Agent.
38
- * These are SBB's platform-wide headers documented in the SMAPI
39
- * cookbook overview (accepted by both API families).
40
- * - Journey API only: also attach Requestor (OSDM partner identity) and
41
- * traceparent (W3C trace context). These are documented in the Journey
42
- * OpenAPI v1.3.17 spec on top of the platform headers — they let SBB
43
- * trace our requests through their backend if we file a support ticket.
44
- */
45
- export declare function smapiRequest<T>(baseUrl: string, path: string, options?: SmapiRequestOptions): Promise<T>;
46
- export {};
@@ -1,186 +0,0 @@
1
- // AUTO-GENERATED from packages/sbb-transport/src/smapi-client.ts
2
- // Run `npm run sync:transport` in packages/sbb-mcp to refresh.
3
- // Do not edit this file directly — edit the sbb-transport source instead.
4
- import { getAccessToken } from './smapi-auth.js';
5
- // Base URLs per environment.
6
- // Mock URL exposed for early auth/integration testing before INT credentials
7
- // arrive — see SBB Journey OpenAPI spec v1.3.17 server list.
8
- const JOURNEY_URLS = {
9
- int: 'https://smapi-osdm-journey-int.api.sbb.ch',
10
- prod: 'https://smapi-osdm-journey.api.sbb.ch',
11
- mock: 'https://smapi-osdm-journey-mock.app.sbb.ch',
12
- };
13
- const TICKETING_URLS = {
14
- int: 'https://b2p-int.api.sbb.ch',
15
- prod: 'https://b2p.api.sbb.ch',
16
- };
17
- function getEnv() {
18
- return process.env.SMAPI_ENV || 'int';
19
- }
20
- export function getJourneyBaseUrl() {
21
- const env = getEnv();
22
- return JOURNEY_URLS[env] ?? JOURNEY_URLS.int;
23
- }
24
- export function getTicketingBaseUrl() {
25
- const env = getEnv();
26
- // Ticketing API has no published mock host; fall back to INT for that family.
27
- return env === 'mock' ? TICKETING_URLS.int : TICKETING_URLS[env];
28
- }
29
- function classifyApiFamily(baseUrl) {
30
- if (baseUrl === JOURNEY_URLS.int ||
31
- baseUrl === JOURNEY_URLS.prod ||
32
- baseUrl === JOURNEY_URLS.mock) {
33
- return 'journey';
34
- }
35
- if (baseUrl === TICKETING_URLS.int || baseUrl === TICKETING_URLS.prod) {
36
- return 'ticketing';
37
- }
38
- return null;
39
- }
40
- function look2bookFamily(family) {
41
- if (family === 'journey')
42
- return 'trips';
43
- if (family === 'ticketing')
44
- return 'offers';
45
- return null;
46
- }
47
- /** Map base-URL classification to the OAuth credential family. */
48
- function authFamilyFor(family) {
49
- // Default to 'journey' for unclassified URLs (e.g. one-off Requestor-only
50
- // calls in tests). `getAccessToken` will throw if that family isn't
51
- // configured, which is the right failure mode.
52
- return family === 'ticketing' ? 'ticketing' : 'journey';
53
- }
54
- let apiCallRecorder = () => {
55
- /* no-op */
56
- };
57
- export function setApiCallRecorder(fn) {
58
- apiCallRecorder = fn;
59
- }
60
- /**
61
- * Generate a W3C traceparent header value. Format:
62
- * 00-<32 hex trace-id>-<16 hex span-id>-01
63
- * The trailing "01" flag = sampled. See https://www.w3.org/TR/trace-context/
64
- *
65
- * Used on Journey API calls (per SMAPI Journey OpenAPI v1.3.17). Lets SBB
66
- * correlate our request through their backend trace graph if we open a
67
- * support ticket.
68
- */
69
- function generateTraceparent() {
70
- const hex = (bytes) => Array.from(crypto.getRandomValues(new Uint8Array(bytes)))
71
- .map((b) => b.toString(16).padStart(2, '0'))
72
- .join('');
73
- return `00-${hex(16)}-${hex(8)}-01`;
74
- }
75
- export class SmapiError extends Error {
76
- problem;
77
- status;
78
- constructor(message, status, problem) {
79
- super(message);
80
- this.name = 'SmapiError';
81
- this.status = status;
82
- this.problem = problem;
83
- }
84
- /** User-facing error messages (translated by SBB per Accept-Language) */
85
- get displayMessages() {
86
- return this.problem?.displayMessages ?? [];
87
- }
88
- }
89
- /**
90
- * Make an authenticated request to an SMAPI endpoint.
91
- *
92
- * Header strategy:
93
- * - Always attach: OAuth bearer, x-contract-id, x-conversation-id,
94
- * Accept, Accept-Language, Cache-Control, User-Agent.
95
- * These are SBB's platform-wide headers documented in the SMAPI
96
- * cookbook overview (accepted by both API families).
97
- * - Journey API only: also attach Requestor (OSDM partner identity) and
98
- * traceparent (W3C trace context). These are documented in the Journey
99
- * OpenAPI v1.3.17 spec on top of the platform headers — they let SBB
100
- * trace our requests through their backend if we file a support ticket.
101
- */
102
- export async function smapiRequest(baseUrl, path, options = {}) {
103
- const { method = 'GET', body, conversationId, acceptLanguage = 'en' } = options;
104
- const family = classifyApiFamily(baseUrl);
105
- const token = await getAccessToken(authFamilyFor(family));
106
- const contractId = process.env.SMAPI_CONTRACT_ID || '';
107
- const headers = {
108
- Authorization: `Bearer ${token}`,
109
- 'x-contract-id': contractId,
110
- 'x-conversation-id': conversationId || crypto.randomUUID(),
111
- Accept: 'application/json',
112
- 'Accept-Language': acceptLanguage,
113
- 'Cache-Control': 'no-cache',
114
- 'User-Agent': 'SwissTrip/1.0 (https://swisstrip.app)',
115
- };
116
- // Journey API: add OSDM-standard identity + tracing headers per the
117
- // Journey OpenAPI spec. We use SMAPI_CONTRACT_ID as the Requestor value
118
- // — the spec describes Requestor as a string whose contents are
119
- // bilateral between provider and partner, and SBB correlates us by the
120
- // contract id we received during onboarding.
121
- if (family === 'journey') {
122
- if (contractId) {
123
- headers.Requestor = contractId;
124
- }
125
- headers.traceparent = generateTraceparent();
126
- }
127
- if (body) {
128
- headers['Content-Type'] = 'application/json';
129
- }
130
- const res = await fetch(`${baseUrl}${path}`, {
131
- method,
132
- headers,
133
- body: body ? JSON.stringify(body) : undefined,
134
- });
135
- // Parse RFC 9457 problem+json (back-compatible with RFC 7807). The
136
- // cookbook still cites 7807 but the Journey OpenAPI declares 9457; the
137
- // shape is identical for the fields we already use, plus pointers[].
138
- if (!res.ok) {
139
- let problem;
140
- try {
141
- const contentType = res.headers.get('content-type') || '';
142
- if (contentType.includes('json')) {
143
- problem = (await res.json());
144
- }
145
- }
146
- catch {
147
- // ignore parse errors
148
- }
149
- // Server-side log only — never expose problem details to end users.
150
- // Surfaces the RFC 9457 pointer array when present so support tickets
151
- // can pinpoint exactly which request/response attribute SBB rejected.
152
- if (problem) {
153
- console.error(`[smapi] ${res.status} ${problem.code ?? problem.type}: ${problem.title}`, problem.detail);
154
- if (problem.pointers && problem.pointers.length > 0) {
155
- for (const p of problem.pointers) {
156
- console.error(`[smapi] pointer: ${p.code ?? p.type ?? 'unknown'}` +
157
- (p.requestPointer ? ` request=${p.requestPointer}` : '') +
158
- (p.responsePointer ? ` response=${p.responsePointer}` : '') +
159
- (p.originator ? ` originator=${p.originator}` : '') +
160
- (p.detail ? ` — ${p.detail}` : ''));
161
- }
162
- }
163
- }
164
- throw new SmapiError(problem?.title || `SMAPI request failed: ${res.status}`, res.status, problem);
165
- }
166
- // Count successful calls against the Look2Book ratio. Recorded AFTER the
167
- // ok-check so failed requests don't pollute the contract metric — only
168
- // data-returning calls should count. Token-refresh URLs return null
169
- // family and are correctly skipped. Recorder is a swallow-all wrapper to
170
- // ensure recorder errors never break the caller.
171
- const ratioFamily = look2bookFamily(family);
172
- if (ratioFamily) {
173
- try {
174
- apiCallRecorder(ratioFamily, path);
175
- }
176
- catch (err) {
177
- console.error('[smapi] apiCallRecorder threw:', err);
178
- }
179
- }
180
- // 204 No Content
181
- if (res.status === 204) {
182
- return undefined;
183
- }
184
- return res.json();
185
- }
186
- //# 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,29 +0,0 @@
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
- * `acceptLanguage` is a BCP-47 locale tag (e.g. "de-CH", "fr", "it"); SBB
5
- * honors it for localized stop names.
6
- */
7
- export declare function searchPlaces(params: SmapiPlaceSearchParams & {
8
- acceptLanguage?: string;
9
- }): Promise<SmapiPlace[]>;
10
- /**
11
- * Search for trip connections between two stations.
12
- * Returns a collection with tripIds needed for pricing/booking.
13
- */
14
- export declare function searchTrips(params: SmapiTripSearchParams & {
15
- acceptLanguage?: string;
16
- }): Promise<SmapiTripsCollection>;
17
- /**
18
- * Retrieve a previously found trip by its ID.
19
- */
20
- export declare function getTrip(tripId: string, stopBehavior?: 'ORIGIN_DESTINATION_ONLY' | 'REAL_BOARDING_ALIGHTING', acceptLanguage?: string): Promise<SmapiTrip>;
21
- /**
22
- * Paginate through a trips collection (next/previous departures).
23
- * Note: per Journey OpenAPI v1.3.17 the `?page=` query param is deprecated —
24
- * the spec mandates HATEOAS `_links[]` on the response (next/previous) for
25
- * pagination. We keep the legacy parameter for backwards compatibility until
26
- * INT confirms the link-based path; the response shape (`SmapiTripsCollection.links`)
27
- * is already wired for either approach.
28
- */
29
- export declare function paginateTrips(collectionId: string, direction: 'next' | 'previous', acceptLanguage?: string): Promise<SmapiTripsCollection>;
@@ -1,91 +0,0 @@
1
- // AUTO-GENERATED from packages/sbb-transport/src/smapi-journey.ts
2
- // Run `npm run sync:transport` in packages/sbb-mcp to refresh.
3
- // Do not edit this file directly — edit the sbb-transport source instead.
4
- import { smapiRequest, getJourneyBaseUrl } from './smapi-client.js';
5
- /**
6
- * Search for stations, addresses, or points of interest by name.
7
- * `acceptLanguage` is a BCP-47 locale tag (e.g. "de-CH", "fr", "it"); SBB
8
- * honors it for localized stop names.
9
- */
10
- export async function searchPlaces(params) {
11
- const body = {
12
- placeInput: { name: params.name },
13
- restrictions: {
14
- ...(params.type && { type: params.type }),
15
- numberOfResults: params.numberOfResults ?? 10,
16
- },
17
- };
18
- const result = await smapiRequest(getJourneyBaseUrl(), '/v1/places', { method: 'POST', body, acceptLanguage: params.acceptLanguage });
19
- return result.places ?? [];
20
- }
21
- /**
22
- * Search for trip connections between two stations.
23
- * Returns a collection with tripIds needed for pricing/booking.
24
- */
25
- export async function searchTrips(params) {
26
- const body = {
27
- origin: {
28
- objectType: 'StopPlaceRef',
29
- stopPlaceRef: params.origin,
30
- },
31
- destination: {
32
- objectType: 'StopPlaceRef',
33
- stopPlaceRef: params.destination,
34
- },
35
- };
36
- // departureTime and arrivalTime are mutually exclusive
37
- if (params.departureTime) {
38
- body.departureTime = params.departureTime;
39
- }
40
- else if (params.arrivalTime) {
41
- body.arrivalTime = params.arrivalTime;
42
- }
43
- else {
44
- body.departureTime = new Date().toISOString();
45
- }
46
- if (params.transferLimit !== undefined) {
47
- body.parameters = { transferLimit: params.transferLimit };
48
- }
49
- if (params.vias?.length) {
50
- body.vias = params.vias.map((via) => ({
51
- viaPlace: {
52
- objectType: 'StopPlaceRef',
53
- stopPlaceRef: via.stopPlaceRef,
54
- },
55
- ...(via.dwellTime && { dwellTime: via.dwellTime }),
56
- }));
57
- }
58
- if (params.ptModeFilter) {
59
- const parameters = body.parameters || {};
60
- parameters.dataFilter = {
61
- ptModeFilter: {
62
- exclude: params.ptModeFilter.exclude,
63
- transportModes: params.ptModeFilter.transportModes.map((mode) => ({
64
- ptMode: mode,
65
- })),
66
- },
67
- };
68
- body.parameters = parameters;
69
- }
70
- return smapiRequest(getJourneyBaseUrl(), '/v1/trips-collection', { method: 'POST', body, acceptLanguage: params.acceptLanguage });
71
- }
72
- /**
73
- * Retrieve a previously found trip by its ID.
74
- */
75
- export async function getTrip(tripId, stopBehavior = 'ORIGIN_DESTINATION_ONLY', acceptLanguage) {
76
- const params = new URLSearchParams({ stopBehavior });
77
- return smapiRequest(getJourneyBaseUrl(), `/v1/trips/${encodeURIComponent(tripId)}?${params}`, { acceptLanguage });
78
- }
79
- /**
80
- * Paginate through a trips collection (next/previous departures).
81
- * Note: per Journey OpenAPI v1.3.17 the `?page=` query param is deprecated —
82
- * the spec mandates HATEOAS `_links[]` on the response (next/previous) for
83
- * pagination. We keep the legacy parameter for backwards compatibility until
84
- * INT confirms the link-based path; the response shape (`SmapiTripsCollection.links`)
85
- * is already wired for either approach.
86
- */
87
- export async function paginateTrips(collectionId, direction, acceptLanguage) {
88
- const params = new URLSearchParams({ page: direction });
89
- return smapiRequest(getJourneyBaseUrl(), `/v1/trips-collections/${encodeURIComponent(collectionId)}?${params}`, { acceptLanguage });
90
- }
91
- //# 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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"}