sbb-mcp 0.4.1 → 0.4.3
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 +57 -57
- package/README.md +258 -314
- package/dist/cache.d.ts +2 -0
- package/dist/cache.js +4 -0
- package/dist/cache.js.map +1 -1
- package/dist/formatters.d.ts +13 -0
- package/dist/formatters.js +94 -19
- package/dist/formatters.js.map +1 -1
- package/dist/http.js +59 -3
- package/dist/http.js.map +1 -1
- package/dist/index.js +2 -0
- package/dist/index.js.map +1 -1
- package/dist/look2book.d.ts +98 -0
- package/dist/look2book.js +212 -0
- package/dist/look2book.js.map +1 -0
- package/dist/profile.js +15 -8
- package/dist/profile.js.map +1 -1
- package/dist/shortlink.d.ts +60 -0
- package/dist/shortlink.js +122 -0
- package/dist/shortlink.js.map +1 -0
- package/dist/structured.d.ts +7 -1
- package/dist/structured.js +2 -1
- package/dist/structured.js.map +1 -1
- package/dist/swisstrip.js +21 -5
- package/dist/swisstrip.js.map +1 -1
- package/dist/tools.d.ts +38 -0
- package/dist/tools.js +122 -77
- package/dist/tools.js.map +1 -1
- package/dist/transport/index.d.ts +1 -0
- package/dist/transport/index.js +4 -0
- package/dist/transport/index.js.map +1 -1
- package/dist/transport/setup.d.ts +1 -0
- package/dist/transport/setup.js +59 -0
- package/dist/transport/setup.js.map +1 -0
- package/dist/transport/smapi-auth.d.ts +14 -2
- package/dist/transport/smapi-auth.js +67 -17
- package/dist/transport/smapi-auth.js.map +1 -1
- package/dist/transport/smapi-client.d.ts +25 -1
- package/dist/transport/smapi-client.js +112 -9
- package/dist/transport/smapi-client.js.map +1 -1
- package/dist/transport/smapi-journey.d.ts +11 -5
- package/dist/transport/smapi-journey.js +16 -7
- package/dist/transport/smapi-journey.js.map +1 -1
- package/dist/transport/smapi-mock.js.map +1 -1
- package/dist/transport/smapi-prices.d.ts +43 -9
- package/dist/transport/smapi-prices.js +112 -41
- package/dist/transport/smapi-prices.js.map +1 -1
- package/dist/transport/smapi-types.d.ts +28 -0
- package/dist/widgets.d.ts +30 -3
- package/dist/widgets.js +78 -14
- package/dist/widgets.js.map +1 -1
- package/package.json +79 -73
- package/web/dist/widgets.css +1 -1
- package/web/dist/widgets.js +1 -1
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"smapi-auth.js","sourceRoot":"","sources":["../../src/transport/smapi-auth.ts"],"names":[],"mappings":"AAAA,kDAAkD;AAClD,
|
|
1
|
+
{"version":3,"file":"smapi-auth.js","sourceRoot":"","sources":["../../src/transport/smapi-auth.ts"],"names":[],"mappings":"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"}
|
|
@@ -1,6 +1,21 @@
|
|
|
1
1
|
import type { SmapiProblem } from './smapi-types.js';
|
|
2
2
|
export declare function getJourneyBaseUrl(): string;
|
|
3
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;
|
|
4
19
|
export declare class SmapiError extends Error {
|
|
5
20
|
problem?: SmapiProblem;
|
|
6
21
|
status: number;
|
|
@@ -16,7 +31,16 @@ type SmapiRequestOptions = {
|
|
|
16
31
|
};
|
|
17
32
|
/**
|
|
18
33
|
* Make an authenticated request to an SMAPI endpoint.
|
|
19
|
-
*
|
|
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.
|
|
20
44
|
*/
|
|
21
45
|
export declare function smapiRequest<T>(baseUrl: string, path: string, options?: SmapiRequestOptions): Promise<T>;
|
|
22
46
|
export {};
|
|
@@ -1,8 +1,14 @@
|
|
|
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.
|
|
1
4
|
import { getAccessToken } from './smapi-auth.js';
|
|
2
|
-
// Base URLs per environment
|
|
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.
|
|
3
8
|
const JOURNEY_URLS = {
|
|
4
9
|
int: 'https://smapi-osdm-journey-int.api.sbb.ch',
|
|
5
10
|
prod: 'https://smapi-osdm-journey.api.sbb.ch',
|
|
11
|
+
mock: 'https://smapi-osdm-journey-mock.app.sbb.ch',
|
|
6
12
|
};
|
|
7
13
|
const TICKETING_URLS = {
|
|
8
14
|
int: 'https://b2p-int.api.sbb.ch',
|
|
@@ -12,10 +18,59 @@ function getEnv() {
|
|
|
12
18
|
return process.env.SMAPI_ENV || 'int';
|
|
13
19
|
}
|
|
14
20
|
export function getJourneyBaseUrl() {
|
|
15
|
-
|
|
21
|
+
const env = getEnv();
|
|
22
|
+
return JOURNEY_URLS[env] ?? JOURNEY_URLS.int;
|
|
16
23
|
}
|
|
17
24
|
export function getTicketingBaseUrl() {
|
|
18
|
-
|
|
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`;
|
|
19
74
|
}
|
|
20
75
|
export class SmapiError extends Error {
|
|
21
76
|
problem;
|
|
@@ -33,11 +88,21 @@ export class SmapiError extends Error {
|
|
|
33
88
|
}
|
|
34
89
|
/**
|
|
35
90
|
* Make an authenticated request to an SMAPI endpoint.
|
|
36
|
-
*
|
|
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.
|
|
37
101
|
*/
|
|
38
102
|
export async function smapiRequest(baseUrl, path, options = {}) {
|
|
39
103
|
const { method = 'GET', body, conversationId, acceptLanguage = 'en' } = options;
|
|
40
|
-
const
|
|
104
|
+
const family = classifyApiFamily(baseUrl);
|
|
105
|
+
const token = await getAccessToken(authFamilyFor(family));
|
|
41
106
|
const contractId = process.env.SMAPI_CONTRACT_ID || '';
|
|
42
107
|
const headers = {
|
|
43
108
|
Authorization: `Bearer ${token}`,
|
|
@@ -48,6 +113,17 @@ export async function smapiRequest(baseUrl, path, options = {}) {
|
|
|
48
113
|
'Cache-Control': 'no-cache',
|
|
49
114
|
'User-Agent': 'SwissTrip/1.0 (https://swisstrip.app)',
|
|
50
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
|
+
}
|
|
51
127
|
if (body) {
|
|
52
128
|
headers['Content-Type'] = 'application/json';
|
|
53
129
|
}
|
|
@@ -56,24 +132,51 @@ export async function smapiRequest(baseUrl, path, options = {}) {
|
|
|
56
132
|
headers,
|
|
57
133
|
body: body ? JSON.stringify(body) : undefined,
|
|
58
134
|
});
|
|
59
|
-
//
|
|
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[].
|
|
60
138
|
if (!res.ok) {
|
|
61
139
|
let problem;
|
|
62
140
|
try {
|
|
63
141
|
const contentType = res.headers.get('content-type') || '';
|
|
64
142
|
if (contentType.includes('json')) {
|
|
65
|
-
problem = await res.json();
|
|
143
|
+
problem = (await res.json());
|
|
66
144
|
}
|
|
67
145
|
}
|
|
68
146
|
catch {
|
|
69
147
|
// ignore parse errors
|
|
70
148
|
}
|
|
71
|
-
//
|
|
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.
|
|
72
152
|
if (problem) {
|
|
73
|
-
console.error(`[smapi] ${res.status} ${problem.type}: ${problem.title}`, problem.detail);
|
|
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
|
+
}
|
|
74
163
|
}
|
|
75
164
|
throw new SmapiError(problem?.title || `SMAPI request failed: ${res.status}`, res.status, problem);
|
|
76
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
|
+
}
|
|
77
180
|
// 204 No Content
|
|
78
181
|
if (res.status === 204) {
|
|
79
182
|
return undefined;
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"smapi-client.js","sourceRoot":"","sources":["../../src/transport/smapi-client.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,cAAc,EAAE,MAAM,iBAAiB,CAAA;
|
|
1
|
+
{"version":3,"file":"smapi-client.js","sourceRoot":"","sources":["../../src/transport/smapi-client.ts"],"names":[],"mappings":"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"}
|
|
@@ -1,23 +1,29 @@
|
|
|
1
1
|
import type { SmapiPlace, SmapiTrip, SmapiTripsCollection, SmapiTripSearchParams, SmapiPlaceSearchParams } from './smapi-types.js';
|
|
2
|
-
import type { Lang } from '../i18n.js';
|
|
3
2
|
/**
|
|
4
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.
|
|
5
6
|
*/
|
|
6
7
|
export declare function searchPlaces(params: SmapiPlaceSearchParams & {
|
|
7
|
-
|
|
8
|
+
acceptLanguage?: string;
|
|
8
9
|
}): Promise<SmapiPlace[]>;
|
|
9
10
|
/**
|
|
10
11
|
* Search for trip connections between two stations.
|
|
11
12
|
* Returns a collection with tripIds needed for pricing/booking.
|
|
12
13
|
*/
|
|
13
14
|
export declare function searchTrips(params: SmapiTripSearchParams & {
|
|
14
|
-
|
|
15
|
+
acceptLanguage?: string;
|
|
15
16
|
}): Promise<SmapiTripsCollection>;
|
|
16
17
|
/**
|
|
17
18
|
* Retrieve a previously found trip by its ID.
|
|
18
19
|
*/
|
|
19
|
-
export declare function getTrip(tripId: string, stopBehavior?: 'ORIGIN_DESTINATION_ONLY' | 'REAL_BOARDING_ALIGHTING',
|
|
20
|
+
export declare function getTrip(tripId: string, stopBehavior?: 'ORIGIN_DESTINATION_ONLY' | 'REAL_BOARDING_ALIGHTING', acceptLanguage?: string): Promise<SmapiTrip>;
|
|
20
21
|
/**
|
|
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.
|
|
22
28
|
*/
|
|
23
|
-
export declare function paginateTrips(collectionId: string, direction: 'next' | 'previous',
|
|
29
|
+
export declare function paginateTrips(collectionId: string, direction: 'next' | 'previous', acceptLanguage?: string): Promise<SmapiTripsCollection>;
|
|
@@ -1,7 +1,11 @@
|
|
|
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.
|
|
1
4
|
import { smapiRequest, getJourneyBaseUrl } from './smapi-client.js';
|
|
2
|
-
import { toLocale } from '../i18n.js';
|
|
3
5
|
/**
|
|
4
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.
|
|
5
9
|
*/
|
|
6
10
|
export async function searchPlaces(params) {
|
|
7
11
|
const body = {
|
|
@@ -11,7 +15,7 @@ export async function searchPlaces(params) {
|
|
|
11
15
|
numberOfResults: params.numberOfResults ?? 10,
|
|
12
16
|
},
|
|
13
17
|
};
|
|
14
|
-
const result = await smapiRequest(getJourneyBaseUrl(), '/v1/places', { method: 'POST', body, acceptLanguage: params.
|
|
18
|
+
const result = await smapiRequest(getJourneyBaseUrl(), '/v1/places', { method: 'POST', body, acceptLanguage: params.acceptLanguage });
|
|
15
19
|
return result.places ?? [];
|
|
16
20
|
}
|
|
17
21
|
/**
|
|
@@ -63,20 +67,25 @@ export async function searchTrips(params) {
|
|
|
63
67
|
};
|
|
64
68
|
body.parameters = parameters;
|
|
65
69
|
}
|
|
66
|
-
return smapiRequest(getJourneyBaseUrl(), '/v1/trips-collection', { method: 'POST', body, acceptLanguage: params.
|
|
70
|
+
return smapiRequest(getJourneyBaseUrl(), '/v1/trips-collection', { method: 'POST', body, acceptLanguage: params.acceptLanguage });
|
|
67
71
|
}
|
|
68
72
|
/**
|
|
69
73
|
* Retrieve a previously found trip by its ID.
|
|
70
74
|
*/
|
|
71
|
-
export async function getTrip(tripId, stopBehavior = 'ORIGIN_DESTINATION_ONLY',
|
|
75
|
+
export async function getTrip(tripId, stopBehavior = 'ORIGIN_DESTINATION_ONLY', acceptLanguage) {
|
|
72
76
|
const params = new URLSearchParams({ stopBehavior });
|
|
73
|
-
return smapiRequest(getJourneyBaseUrl(), `/v1/trips/${encodeURIComponent(tripId)}?${params}`, { acceptLanguage
|
|
77
|
+
return smapiRequest(getJourneyBaseUrl(), `/v1/trips/${encodeURIComponent(tripId)}?${params}`, { acceptLanguage });
|
|
74
78
|
}
|
|
75
79
|
/**
|
|
76
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.
|
|
77
86
|
*/
|
|
78
|
-
export async function paginateTrips(collectionId, direction,
|
|
87
|
+
export async function paginateTrips(collectionId, direction, acceptLanguage) {
|
|
79
88
|
const params = new URLSearchParams({ page: direction });
|
|
80
|
-
return smapiRequest(getJourneyBaseUrl(), `/v1/trips-collections/${encodeURIComponent(collectionId)}?${params}`, { acceptLanguage
|
|
89
|
+
return smapiRequest(getJourneyBaseUrl(), `/v1/trips-collections/${encodeURIComponent(collectionId)}?${params}`, { acceptLanguage });
|
|
81
90
|
}
|
|
82
91
|
//# sourceMappingURL=smapi-journey.js.map
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"smapi-journey.js","sourceRoot":"","sources":["../../src/transport/smapi-journey.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,YAAY,EAAE,iBAAiB,EAAE,MAAM,mBAAmB,CAAA;AASnE
|
|
1
|
+
{"version":3,"file":"smapi-journey.js","sourceRoot":"","sources":["../../src/transport/smapi-journey.ts"],"names":[],"mappings":"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"}
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"smapi-mock.js","sourceRoot":"","sources":["../../src/transport/smapi-mock.ts"],"names":[],"mappings":"
|
|
1
|
+
{"version":3,"file":"smapi-mock.js","sourceRoot":"","sources":["../../src/transport/smapi-mock.ts"],"names":[],"mappings":"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"}
|
|
@@ -1,14 +1,48 @@
|
|
|
1
1
|
import type { SmapiPriceResult, SmapiOfferResult, SmapiTraveler } from './smapi-types.js';
|
|
2
|
-
import type { Lang } from '../i18n.js';
|
|
3
2
|
/**
|
|
4
|
-
*
|
|
5
|
-
*
|
|
6
|
-
*
|
|
3
|
+
* ## Endpoint inventory (observed against b2p-int.api.sbb.ch on 2026-04-23)
|
|
4
|
+
*
|
|
5
|
+
* GET /api/networks — known working ✓
|
|
6
|
+
* GET /api/trip-offers?tripId=…&… — 400 "At least one of passengers
|
|
7
|
+
* or dogs or bikes must be set"
|
|
8
|
+
* (contract + auth ok; schema below)
|
|
9
|
+
* POST /api/v2/prices?tripId=… — 400 "Failed to read request"
|
|
10
|
+
* (contract + auth ok; need right
|
|
11
|
+
* body shape)
|
|
12
|
+
* /api/prices (v1) — 403 NOT FOUND at this gateway
|
|
13
|
+
*
|
|
14
|
+
* The legacy fallback to `/api/prices` for traveler-less lookups has been
|
|
15
|
+
* removed — that endpoint doesn't exist in the current Ticketing API. Both
|
|
16
|
+
* exported functions now always hit their v2 / correct paths.
|
|
17
|
+
*
|
|
18
|
+
* The exact query-param / request-body shapes for `/api/trip-offers` and
|
|
19
|
+
* `/api/v2/prices` still need live-tripId verification once Journey API is
|
|
20
|
+
* approved and we can fetch a real tripId to probe against. The payload
|
|
21
|
+
* schemas in this file are inferred from SBB's RFC 9457 error messages and
|
|
22
|
+
* may need refinement — tracked in docs/sbb-train-tickets-implementation.md
|
|
23
|
+
* under "Known limits".
|
|
7
24
|
*/
|
|
8
|
-
export declare function getTripPrices(tripIds: string[], travelers?: SmapiTraveler[], lang?: Lang): Promise<SmapiPriceResult[]>;
|
|
9
25
|
/**
|
|
10
|
-
*
|
|
11
|
-
*
|
|
12
|
-
*
|
|
26
|
+
* Fetch price points for a single trip via `POST /api/v2/prices?tripId=…`.
|
|
27
|
+
*
|
|
28
|
+
* SBB's /api/prices (v1) endpoint was removed — the current Ticketing API
|
|
29
|
+
* only has v2. Always requires passenger details (id, age, reduction).
|
|
30
|
+
*
|
|
31
|
+
* Returns [] on any error so one bad trip doesn't blow up a batch lookup.
|
|
13
32
|
*/
|
|
14
|
-
export declare function
|
|
33
|
+
export declare function getTripPrices(tripIds: string[], travelers?: SmapiTraveler[], acceptLanguage?: string): Promise<SmapiPriceResult[]>;
|
|
34
|
+
/**
|
|
35
|
+
* Fetch trip offers + affiliate deep link via `GET /api/trip-offers?tripId=…`.
|
|
36
|
+
*
|
|
37
|
+
* The legacy `POST /api/trip-offers` with JSON body is obsolete — the current
|
|
38
|
+
* contract is GET with query parameters. The `passengers` param is required
|
|
39
|
+
* (at least one of passengers / dogs / bikes).
|
|
40
|
+
*
|
|
41
|
+
* We send passengers as a URL-encoded JSON array because simple form encoding
|
|
42
|
+
* drops structured fields (id/age/reduction). This mirrors how most b2p
|
|
43
|
+
* partner integrations pass structured collections to their GET endpoints.
|
|
44
|
+
*
|
|
45
|
+
* Note: this call burns the Look2Book offers ratio — only invoke when the
|
|
46
|
+
* user actually intends to see prices / buy.
|
|
47
|
+
*/
|
|
48
|
+
export declare function getTripOffers(tripId: string, travelers: SmapiTraveler[], acceptLanguage?: string): Promise<SmapiOfferResult>;
|
|
@@ -1,31 +1,51 @@
|
|
|
1
|
+
// AUTO-GENERATED from packages/sbb-transport/src/smapi-prices.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.
|
|
1
4
|
import { smapiRequest, getTicketingBaseUrl } from './smapi-client.js';
|
|
2
|
-
import { toLocale } from '../i18n.js';
|
|
3
5
|
/**
|
|
4
|
-
*
|
|
5
|
-
*
|
|
6
|
-
*
|
|
6
|
+
* ## Endpoint inventory (observed against b2p-int.api.sbb.ch on 2026-04-23)
|
|
7
|
+
*
|
|
8
|
+
* GET /api/networks — known working ✓
|
|
9
|
+
* GET /api/trip-offers?tripId=…&… — 400 "At least one of passengers
|
|
10
|
+
* or dogs or bikes must be set"
|
|
11
|
+
* (contract + auth ok; schema below)
|
|
12
|
+
* POST /api/v2/prices?tripId=… — 400 "Failed to read request"
|
|
13
|
+
* (contract + auth ok; need right
|
|
14
|
+
* body shape)
|
|
15
|
+
* /api/prices (v1) — 403 NOT FOUND at this gateway
|
|
16
|
+
*
|
|
17
|
+
* The legacy fallback to `/api/prices` for traveler-less lookups has been
|
|
18
|
+
* removed — that endpoint doesn't exist in the current Ticketing API. Both
|
|
19
|
+
* exported functions now always hit their v2 / correct paths.
|
|
20
|
+
*
|
|
21
|
+
* The exact query-param / request-body shapes for `/api/trip-offers` and
|
|
22
|
+
* `/api/v2/prices` still need live-tripId verification once Journey API is
|
|
23
|
+
* approved and we can fetch a real tripId to probe against. The payload
|
|
24
|
+
* schemas in this file are inferred from SBB's RFC 9457 error messages and
|
|
25
|
+
* may need refinement — tracked in docs/sbb-train-tickets-implementation.md
|
|
26
|
+
* under "Known limits".
|
|
7
27
|
*/
|
|
8
|
-
|
|
9
|
-
|
|
10
|
-
|
|
11
|
-
|
|
28
|
+
/**
|
|
29
|
+
* Fetch price points for a single trip via `POST /api/v2/prices?tripId=…`.
|
|
30
|
+
*
|
|
31
|
+
* SBB's /api/prices (v1) endpoint was removed — the current Ticketing API
|
|
32
|
+
* only has v2. Always requires passenger details (id, age, reduction).
|
|
33
|
+
*
|
|
34
|
+
* Returns [] on any error so one bad trip doesn't blow up a batch lookup.
|
|
35
|
+
*/
|
|
36
|
+
export async function getTripPrices(tripIds, travelers, acceptLanguage) {
|
|
37
|
+
const effectiveTravelers = travelers && travelers.length > 0
|
|
38
|
+
? travelers
|
|
39
|
+
: [{ id: 'p0', type: 'ADULT' }];
|
|
40
|
+
return Promise.all(tripIds.map(async (tripId) => {
|
|
12
41
|
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
42
|
try {
|
|
18
|
-
const data = await smapiRequest(getTicketingBaseUrl(),
|
|
19
|
-
method:
|
|
20
|
-
|
|
21
|
-
acceptLanguage
|
|
43
|
+
const data = await smapiRequest(getTicketingBaseUrl(), `/api/v2/prices?${params}`, {
|
|
44
|
+
method: 'POST',
|
|
45
|
+
body: { passengers: effectiveTravelers.map(toPassengerPayload) },
|
|
46
|
+
acceptLanguage,
|
|
22
47
|
});
|
|
23
|
-
|
|
24
|
-
const prices = data.prices ?? (data.price ? [{
|
|
25
|
-
amount: data.price.amount,
|
|
26
|
-
currency: data.price.currency,
|
|
27
|
-
class: '2',
|
|
28
|
-
}] : []);
|
|
48
|
+
const prices = data.prices ?? [];
|
|
29
49
|
return {
|
|
30
50
|
tripId,
|
|
31
51
|
prices: prices.map((p) => ({
|
|
@@ -41,33 +61,84 @@ export async function getTripPrices(tripIds, travelers, lang) {
|
|
|
41
61
|
return { tripId, prices: [] };
|
|
42
62
|
}
|
|
43
63
|
}));
|
|
44
|
-
return results;
|
|
45
64
|
}
|
|
46
65
|
/**
|
|
47
|
-
*
|
|
48
|
-
*
|
|
49
|
-
*
|
|
66
|
+
* Fetch trip offers + affiliate deep link via `GET /api/trip-offers?tripId=…`.
|
|
67
|
+
*
|
|
68
|
+
* The legacy `POST /api/trip-offers` with JSON body is obsolete — the current
|
|
69
|
+
* contract is GET with query parameters. The `passengers` param is required
|
|
70
|
+
* (at least one of passengers / dogs / bikes).
|
|
71
|
+
*
|
|
72
|
+
* We send passengers as a URL-encoded JSON array because simple form encoding
|
|
73
|
+
* drops structured fields (id/age/reduction). This mirrors how most b2p
|
|
74
|
+
* partner integrations pass structured collections to their GET endpoints.
|
|
75
|
+
*
|
|
76
|
+
* Note: this call burns the Look2Book offers ratio — only invoke when the
|
|
77
|
+
* user actually intends to see prices / buy.
|
|
50
78
|
*/
|
|
51
|
-
export async function getTripOffers(tripId, travelers,
|
|
52
|
-
const
|
|
79
|
+
export async function getTripOffers(tripId, travelers, acceptLanguage) {
|
|
80
|
+
const passengers = (travelers.length > 0 ? travelers : [{ id: 'p0', type: 'ADULT' }])
|
|
81
|
+
.map(toPassengerPayload);
|
|
82
|
+
const params = new URLSearchParams({
|
|
53
83
|
tripId,
|
|
54
|
-
|
|
55
|
-
|
|
56
|
-
|
|
57
|
-
|
|
58
|
-
|
|
59
|
-
|
|
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;
|
|
84
|
+
passengers: JSON.stringify(passengers),
|
|
85
|
+
});
|
|
86
|
+
const data = await smapiRequest(getTicketingBaseUrl(), `/api/trip-offers?${params}`, { method: 'GET', acceptLanguage });
|
|
87
|
+
// Extract affiliate deep link from HATEOAS `_links` (OSDM convention) or
|
|
88
|
+
// top-level `links` (legacy shape) — we accept either to stay resilient
|
|
89
|
+
// across spec revisions.
|
|
90
|
+
const hateoas = ((data._links ?? data.links) ?? []);
|
|
91
|
+
const affiliateLink = hateoas.find((l) => l.rel === 'online-offers')?.href;
|
|
67
92
|
return {
|
|
68
93
|
tripId,
|
|
69
94
|
containers: [], // Full offer parsing not needed for deep-link flow
|
|
70
95
|
affiliateDeepLink: affiliateLink,
|
|
71
96
|
};
|
|
72
97
|
}
|
|
98
|
+
/**
|
|
99
|
+
* Map our internal SmapiTraveler → SBB's b2p passenger payload shape.
|
|
100
|
+
*
|
|
101
|
+
* SBB's /api/trip-offers expects `{ id, age, reduction }` per passenger
|
|
102
|
+
* (observed from the RFC 9457 error: "Passenger data has to contain id,
|
|
103
|
+
* age and reduction."). Our SmapiTraveler uses {id, type, dateOfBirth,
|
|
104
|
+
* reductionCard}; we translate.
|
|
105
|
+
*
|
|
106
|
+
* - age: derived from dateOfBirth when present; else default 30 for ADULT,
|
|
107
|
+
* 10 for CHILD (b2p classifies by age bracket, not by type)
|
|
108
|
+
* - reduction: mapped from reductionCard; NONE → 'NO_REDUCTION' by default
|
|
109
|
+
*/
|
|
110
|
+
function toPassengerPayload(t) {
|
|
111
|
+
return {
|
|
112
|
+
id: t.id || 'p0',
|
|
113
|
+
age: deriveAge(t),
|
|
114
|
+
reduction: mapReduction(t.reductionCard),
|
|
115
|
+
};
|
|
116
|
+
}
|
|
117
|
+
function deriveAge(t) {
|
|
118
|
+
if (t.dateOfBirth) {
|
|
119
|
+
const dob = new Date(t.dateOfBirth);
|
|
120
|
+
if (!Number.isNaN(dob.getTime())) {
|
|
121
|
+
const now = new Date();
|
|
122
|
+
let age = now.getFullYear() - dob.getFullYear();
|
|
123
|
+
const m = now.getMonth() - dob.getMonth();
|
|
124
|
+
if (m < 0 || (m === 0 && now.getDate() < dob.getDate()))
|
|
125
|
+
age--;
|
|
126
|
+
return age;
|
|
127
|
+
}
|
|
128
|
+
}
|
|
129
|
+
return t.type === 'CHILD' ? 10 : 30;
|
|
130
|
+
}
|
|
131
|
+
function mapReduction(card) {
|
|
132
|
+
switch (card) {
|
|
133
|
+
case 'HALF_FARE':
|
|
134
|
+
return 'HALFFARE';
|
|
135
|
+
case 'GA':
|
|
136
|
+
return 'GA';
|
|
137
|
+
case 'NONE':
|
|
138
|
+
case undefined:
|
|
139
|
+
return 'NO_REDUCTION';
|
|
140
|
+
default:
|
|
141
|
+
return 'NO_REDUCTION';
|
|
142
|
+
}
|
|
143
|
+
}
|
|
73
144
|
//# sourceMappingURL=smapi-prices.js.map
|