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.
- package/LICENSE +50 -57
- package/README.md +26 -296
- package/dist/index.js +47 -17
- package/package.json +10 -29
- package/dist/auth.d.ts +0 -2
- package/dist/auth.js +0 -44
- package/dist/auth.js.map +0 -1
- package/dist/cache.d.ts +0 -12
- package/dist/cache.js +0 -58
- package/dist/cache.js.map +0 -1
- package/dist/client.d.ts +0 -17
- package/dist/client.js +0 -70
- package/dist/client.js.map +0 -1
- package/dist/formatters.d.ts +0 -22
- package/dist/formatters.js +0 -210
- package/dist/formatters.js.map +0 -1
- package/dist/http.d.ts +0 -2
- package/dist/http.js +0 -61
- package/dist/http.js.map +0 -1
- package/dist/i18n.d.ts +0 -22
- package/dist/i18n.js +0 -36
- package/dist/i18n.js.map +0 -1
- package/dist/index.d.ts +0 -2
- package/dist/index.js.map +0 -1
- package/dist/journey.d.ts +0 -5
- package/dist/journey.js +0 -67
- package/dist/journey.js.map +0 -1
- package/dist/prices.d.ts +0 -3
- package/dist/prices.js +0 -51
- package/dist/prices.js.map +0 -1
- package/dist/profile.d.ts +0 -16
- package/dist/profile.js +0 -77
- package/dist/profile.js.map +0 -1
- package/dist/rate-limit.d.ts +0 -5
- package/dist/rate-limit.js +0 -44
- package/dist/rate-limit.js.map +0 -1
- package/dist/structured.d.ts +0 -119
- package/dist/structured.js +0 -133
- package/dist/structured.js.map +0 -1
- package/dist/swisstrip.d.ts +0 -41
- package/dist/swisstrip.js +0 -119
- package/dist/swisstrip.js.map +0 -1
- package/dist/tools.d.ts +0 -2
- package/dist/tools.js +0 -464
- package/dist/tools.js.map +0 -1
- package/dist/transport/index.d.ts +0 -9
- package/dist/transport/index.js +0 -9
- package/dist/transport/index.js.map +0 -1
- package/dist/transport/smapi-auth.d.ts +0 -2
- package/dist/transport/smapi-auth.js +0 -39
- package/dist/transport/smapi-auth.js.map +0 -1
- package/dist/transport/smapi-client.d.ts +0 -22
- package/dist/transport/smapi-client.js +0 -83
- package/dist/transport/smapi-client.js.map +0 -1
- package/dist/transport/smapi-journey.d.ts +0 -23
- package/dist/transport/smapi-journey.js +0 -82
- package/dist/transport/smapi-journey.js.map +0 -1
- package/dist/transport/smapi-mock.d.ts +0 -9
- package/dist/transport/smapi-mock.js +0 -151
- package/dist/transport/smapi-mock.js.map +0 -1
- package/dist/transport/smapi-prices.d.ts +0 -14
- package/dist/transport/smapi-prices.js +0 -73
- package/dist/transport/smapi-prices.js.map +0 -1
- package/dist/transport/smapi-types.d.ts +0 -153
- package/dist/transport/smapi-types.js +0 -2
- package/dist/transport/smapi-types.js.map +0 -1
- package/dist/types.d.ts +0 -139
- package/dist/types.js +0 -3
- package/dist/types.js.map +0 -1
- package/dist/widgets.d.ts +0 -33
- package/dist/widgets.js +0 -120
- package/dist/widgets.js.map +0 -1
- package/web/dist/widgets.css +0 -1
- package/web/dist/widgets.js +0 -1
package/dist/transport/index.js
DELETED
|
@@ -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,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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"}
|