@cloudparse/up-miniapps-sdk 0.1.5 β†’ 0.2.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/README.md CHANGED
@@ -13,12 +13,22 @@ npm i @cloudparse/up-miniapps-sdk
13
13
  ### Initialization
14
14
 
15
15
  ```typescript
16
- import { sdk } from 'up-miniapps-sdk';
16
+ import {sdk} from 'up-miniapps-sdk';
17
17
 
18
18
  // Or if using UMD/CDN
19
19
  // const sdk = window.UPSDK;
20
20
  ```
21
21
 
22
+ #### Global Context Sync
23
+
24
+ Mini-Apps can synchronize state (like theme preferences) with the native shell.
25
+
26
+ - `sdk.updateContext(context: Record<string, unknown>): Promise<void>` - Merges local state into the shell's global context.
27
+
28
+ ```typescript
29
+ await sdk.updateContext({isDark: true, lastViewedTab: 'home'});
30
+ ```
31
+
22
32
  ---
23
33
 
24
34
  ## πŸ›  API Reference
@@ -29,15 +39,18 @@ import { sdk } from 'up-miniapps-sdk';
29
39
  Local SQLite Storage and Hardware-level Secure Storage (Keychain/Keystore). Data is isolated per Mini-App.
30
40
 
31
41
  #### Standard Storage (`sdk.storage`)
42
+
32
43
  - `sdk.storage.get(key: string): Promise<string | null>` - Retrieves a value by key.
33
44
  - `sdk.storage.getAll(): Promise<Record<string, string>>` - Retrieves all key-value pairs.
34
45
  - `sdk.storage.set(key: string, value: string): Promise<void>` - Sets a value for a specific key.
35
46
 
36
47
  #### Secure Storage (`sdk.secureStore`)
48
+
37
49
  - `sdk.secureStore.get(key: string): Promise<string | null>` - Retrieves a value from the device's secure enclave.
38
50
  - `sdk.secureStore.set(key: string, value: string): Promise<void>` - Stores hardware-encrypted data.
39
51
 
40
52
  #### Database Storage (`sdk.database`)
53
+
41
54
  - `sdk.database.getUserData<T = unknown>(): Promise<T>` - Retrieves user-app specific data from the backend database.
42
55
  - `sdk.database.setUserData<T = unknown>(data: T): Promise<void>` - Updates user-app specific data in the backend database.
43
56
 
@@ -46,7 +59,24 @@ Local SQLite Storage and Hardware-level Secure Storage (Keychain/Keystore). Data
46
59
  <details>
47
60
  <summary><b>πŸ“ Location</b></summary>
48
61
 
49
- - `sdk.location.getCurrentPosition(): Promise<LocationData>` - Retrieves the current GPS position of the device.
62
+ - `sdk.location.getCurrentPosition(): Promise<LocationData>` - Retrieves the current GPS position of the device (One-shot).
63
+ - `sdk.location.startStream(config?: LocationStreamConfig): Promise<void>` - Starts the continuous, high-frequency location and sensor stream from the native shell.
64
+ - `sdk.location.stopStream(): Promise<void>` - Stops the location stream to save battery.
65
+
66
+ #### Events
67
+
68
+ - `GeolocationEvent.LOCATION_STREAM_UPDATE` - Fired continuously while the stream is active. Includes GPS, Heading (compass), and Accelerometer data.
69
+
70
+ ```typescript
71
+ import {sdk, GeolocationEvent} from '@cloudparse/up-miniapps-sdk';
72
+
73
+ sdk.location.startStream({distanceFilter: 1, headingThreshold: 2});
74
+
75
+ sdk.on(GeolocationEvent.LOCATION_STREAM_UPDATE, (payload) => {
76
+ console.log("Location:", payload.location.coords);
77
+ console.log("Compass:", payload.heading.trueHeading);
78
+ });
79
+ ```
50
80
 
51
81
  </details>
52
82
 
@@ -105,6 +135,125 @@ Integration with the Fidelity program.
105
135
  - `sdk.fidelity.getFidelityBalance(): Promise<FidelityBalance>` - Returns current fidelity balance.
106
136
  - `sdk.fidelity.getFidelityHistory(params?: FidelityHistoryParams): Promise<FidelityTransaction[]>` - Retrieves transaction history.
107
137
  - `sdk.fidelity.spendFPoints(intentUuid: string): Promise<{ success: boolean; transactionId: string }>` - Authorize spending points via Two-Phase Authorization.
138
+ - `sdk.fidelity.showHistory(): Promise<void>` - Triggers the native fidelity history view.
139
+
140
+ </details>
141
+
142
+ <details>
143
+ <summary><b>πŸ—ΊοΈ Map & Navigation</b></summary>
144
+
145
+ A fully-featured, MapLibre-based interactive map component with built-in POI rendering, Live Routing, and customizable user tracking.
146
+
147
+ ### Basic Usage
148
+
149
+ ```tsx
150
+ import {Map, useLiveRouting, useRoute} from '@cloudparse/up-miniapps-sdk/components/Map';
151
+
152
+ // 1. (Option A) Fetch live routing data (automatically tracks user location)
153
+ const navigationData = useLiveRouting({
154
+ destination: [6.1400, 45.9100], // [lng, lat]
155
+ enabled: true
156
+ });
157
+
158
+ // 1. (Option B) Simple one-off route (no live tracking)
159
+ const {route, isCalculating, error} = useRoute(
160
+ [6.1258, 45.9058], // origin
161
+ [6.1400, 45.9100] // destination
162
+ );
163
+
164
+ // 2. Render the Map
165
+ <Map
166
+ initialViewState={{latitude: 45.906, longitude: 6.13, zoom: 16}}
167
+ mapStyle="https://tiles.cloudparse.fr/style/up_dark_matter" // Optional custom style
168
+
169
+ // POI Layer
170
+ pois={[
171
+ {id: '1', name: 'Le Pub', longitude: 6.128, latitude: 45.908, emoji: '🍻'}
172
+ ]}
173
+ onPoiClick={(poi) => console.log('Clicked:', poi)}
174
+
175
+ // Navigation & User Location
176
+ showUserLocation={true}
177
+ showUserLocationButton={true} // Built-in "Center on User" button
178
+ route={navigationData.route?.geometry}
179
+ showNavigationUI={true}
180
+ navigationData={navigationData}
181
+ onNavigationAbort={() => console.log("Route cancelled")}
182
+ route={navigationData.route?.geometry || route?.geometry}
183
+ // Customization
184
+ renderUserLocation={({coords, heading}) => (
185
+ <MyCustomPulsatingMarker coords={coords} heading={heading}/>
186
+ )}
187
+ />
188
+ ```
189
+
190
+ ### Integrated Navigation
191
+
192
+ The `Map` component can internally handle the entire navigation lifecycle (requesting routes, drawing lines, following user location).
193
+
194
+ #### Option A: Component-driven (via props)
195
+
196
+ ```tsx
197
+ <Map
198
+ navigation={{
199
+ destination: [6.1400, 45.9100],
200
+ profile: 'walking'
201
+ }}
202
+ />
203
+ ```
204
+
205
+ #### Option B: Programmatic-driven (Google Maps style)
206
+
207
+ You can trigger a journey from anywhere in your code. Any mounted `Map` component will automatically pick it up and show the route.
208
+
209
+ ```typescript
210
+ import {sdk} from '@cloudparse/up-miniapps-sdk';
211
+
212
+ // 1. Show route preview
213
+ sdk.navigation.createJourney({
214
+ destination: [6.1400, 45.9100],
215
+ profile: 'driving'
216
+ });
217
+
218
+ // 2. Start active navigation (can also be done via "Start" button in Map UI)
219
+ sdk.navigation.start();
220
+
221
+ // 3. Stop/Clear
222
+ sdk.navigation.abort();
223
+ ```
224
+
225
+ ---
226
+
227
+ ### Custom Camera Controls
228
+
229
+ If you want to build your own custom "Center on User" button outside of the Map component, wrap your view in `SDKMapProvider` and use the `useMapControls` hook.
230
+
231
+ ```tsx
232
+ import {Map, SDKMapProvider, useMapControls} from '@cloudparse/up-miniapps-sdk/components/Map';
233
+
234
+ const CustomRecenterButton = () => {
235
+ // Access camera state and the flyTo trigger
236
+ const {isFollowing, centerOnUser} = useMapControls();
237
+
238
+ return (
239
+ <button
240
+ onClick={centerOnUser}
241
+ style={{color: isFollowing ? 'blue' : 'gray'}}
242
+ >
243
+ Center Map
244
+ </button>
245
+ );
246
+ };
247
+
248
+ export const AppView = () => (
249
+ <SDKMapProvider>
250
+ <div style={{position: 'relative'}}>
251
+ <Map showUserLocation={true} showUserLocationButton={false}/>
252
+ <CustomRecenterButton/>
253
+ </div>
254
+ </SDKMapProvider>
255
+ );
256
+ ```
108
257
 
109
258
  </details>
110
259
 
@@ -113,6 +262,22 @@ Integration with the Fidelity program.
113
262
 
114
263
  - `sdk.ui.exit(): Promise<void>` - Closes the Mini-App and returns to the host shell.
115
264
 
265
+ #### Events
266
+
267
+ - `UIEvent.BACK_GESTURE` - Triggered when the user performs a native back gesture or presses the native back button. Use this to handle custom back navigation within your Mini App.
268
+
269
+ ```typescript
270
+ import {sdk, UIEvent} from '@cloudparse/up-miniapps-sdk';
271
+
272
+ sdk.on(UIEvent.BACK_GESTURE, () => {
273
+ if (myAppCanGoBack) {
274
+ myAppGoBack();
275
+ } else {
276
+ sdk.ui.exit();
277
+ }
278
+ });
279
+ ```
280
+
116
281
  </details>
117
282
 
118
283
  ---
@@ -0,0 +1,252 @@
1
+ // src/services/routingService.ts
2
+ var RoutingService = {
3
+ createErrorResponse(req, code, message) {
4
+ return {
5
+ error: { code, message },
6
+ requestId: req.requestId,
7
+ sdkVersion: req.sdkVersion,
8
+ success: false,
9
+ type: "SDK_ROUTING_GETROUTE_RESPONSE"
10
+ };
11
+ },
12
+ decodePolyline(str, precision) {
13
+ let index = 0, lat = 0, lng = 0;
14
+ const coordinates = [];
15
+ const shift = 10 ** precision;
16
+ while (index < str.length) {
17
+ let b, shiftNum = 0, result = 0;
18
+ do {
19
+ b = str.charCodeAt(index++) - 63;
20
+ result |= (b & 31) << shiftNum;
21
+ shiftNum += 5;
22
+ } while (b >= 32);
23
+ const dlat = result & 1 ? ~(result >> 1) : result >> 1;
24
+ lat += dlat;
25
+ shiftNum = 0;
26
+ result = 0;
27
+ do {
28
+ b = str.charCodeAt(index++) - 63;
29
+ result |= (b & 31) << shiftNum;
30
+ shiftNum += 5;
31
+ } while (b >= 32);
32
+ const dlng = result & 1 ? ~(result >> 1) : result >> 1;
33
+ lng += dlng;
34
+ coordinates.push([lng / shift, lat / shift]);
35
+ }
36
+ return coordinates;
37
+ },
38
+ async fetchOSRMRoute(req, coords, profile, baseUrl) {
39
+ const coordsString = coords.map((c) => `${c[0]},${c[1]}`).join(";");
40
+ const osrmProfile = profile === "walking" ? "foot" : profile === "driving" ? "car" : "bike";
41
+ const url = `${baseUrl}/route/v1/${osrmProfile}/${coordsString}?overview=full&geometries=geojson&steps=true`;
42
+ const response = await fetch(url);
43
+ if (!response.ok) {
44
+ throw new Error(`OSRM Error: ${response.statusText}`);
45
+ }
46
+ const data = await response.json();
47
+ if (data.code !== "Ok" || !data.routes || data.routes.length === 0) {
48
+ throw new Error("NO_ROUTE");
49
+ }
50
+ const route = data.routes[0];
51
+ const steps = route.legs?.flatMap(
52
+ (leg) => leg.steps?.map((step) => ({
53
+ distance: step.distance,
54
+ duration: step.duration,
55
+ instruction: step.maneuver?.instruction || "Continue"
56
+ }))
57
+ ) || [];
58
+ return {
59
+ data: {
60
+ distanceMeters: route.distance,
61
+ durationSeconds: route.duration,
62
+ geometry: route.geometry,
63
+ steps
64
+ },
65
+ requestId: req.requestId,
66
+ sdkVersion: req.sdkVersion,
67
+ success: true,
68
+ type: "SDK_ROUTING_GETROUTE_RESPONSE"
69
+ };
70
+ },
71
+ async fetchRoute(req, config) {
72
+ const { origin, destination, waypoints, profile, language } = req.payload;
73
+ if (!this.isValidCoordinate(origin) || !this.isValidCoordinate(destination)) {
74
+ return this.createErrorResponse(
75
+ req,
76
+ "INVALID_INPUT",
77
+ "Origin or destination coordinates are invalid."
78
+ );
79
+ }
80
+ if (waypoints && !waypoints.every(this.isValidCoordinate)) {
81
+ return this.createErrorResponse(
82
+ req,
83
+ "INVALID_INPUT",
84
+ "One or more waypoints are invalid."
85
+ );
86
+ }
87
+ const allCoords = [origin, ...waypoints || [], destination];
88
+ try {
89
+ if (config.backend === "osrm") {
90
+ return await this.fetchOSRMRoute(
91
+ req,
92
+ allCoords,
93
+ profile,
94
+ config.baseUrl
95
+ );
96
+ } else {
97
+ return await this.fetchValhallaRoute(
98
+ req,
99
+ allCoords,
100
+ profile,
101
+ language,
102
+ config.baseUrl
103
+ );
104
+ }
105
+ } catch (error) {
106
+ if (error instanceof Error && error.message.includes("NO_ROUTE")) {
107
+ return this.createErrorResponse(
108
+ req,
109
+ "NO_ROUTE",
110
+ "No route could be found for the given points."
111
+ );
112
+ }
113
+ if (error instanceof TypeError) {
114
+ return this.createErrorResponse(
115
+ req,
116
+ "NETWORK_ERROR",
117
+ "Failed to connect to the routing backend."
118
+ );
119
+ }
120
+ return this.createErrorResponse(
121
+ req,
122
+ "BACKEND_ERROR",
123
+ `Routing backend error: ${error instanceof Error ? error.message : "Unknown error"}`
124
+ );
125
+ }
126
+ },
127
+ async fetchValhallaRoute(req, coords, profile, language, baseUrl) {
128
+ const valhallaProfile = profile === "driving" ? "auto" : profile === "walking" ? "pedestrian" : "bicycle";
129
+ const body = {
130
+ costing: valhallaProfile,
131
+ directions_options: {
132
+ language: language || "en-US"
133
+ },
134
+ locations: coords.map((c) => ({ lat: c[1], lon: c[0] }))
135
+ };
136
+ const response = await fetch(`${baseUrl}/route`, {
137
+ body: JSON.stringify(body),
138
+ headers: { "Content-Type": "application/json" },
139
+ method: "POST"
140
+ });
141
+ if (!response.ok) {
142
+ throw new Error(`Valhalla Error: ${response.statusText}`);
143
+ }
144
+ const data = await response.json();
145
+ if (!data.trip || !data.trip.legs || data.trip.legs.length === 0) {
146
+ throw new Error("NO_ROUTE");
147
+ }
148
+ const trip = data.trip;
149
+ const decodedCoordinates = this.decodePolyline(trip.legs[0].shape, 6);
150
+ const steps = trip.legs.flatMap(
151
+ (leg) => leg.maneuvers?.map((maneuver) => ({
152
+ distance: maneuver.length * 1e3,
153
+ // Valhalla returns km
154
+ duration: maneuver.time,
155
+ instruction: maneuver.instruction || "Continue"
156
+ }))
157
+ ) || [];
158
+ return {
159
+ data: {
160
+ distanceMeters: trip.summary.length * 1e3,
161
+ // km to m
162
+ durationSeconds: trip.summary.time,
163
+ geometry: {
164
+ coordinates: decodedCoordinates,
165
+ type: "LineString"
166
+ },
167
+ steps
168
+ },
169
+ requestId: req.requestId,
170
+ sdkVersion: req.sdkVersion,
171
+ success: true,
172
+ type: "SDK_ROUTING_GETROUTE_RESPONSE"
173
+ };
174
+ },
175
+ isValidCoordinate(coord) {
176
+ const [lng, lat] = coord;
177
+ return lng >= -180 && lng <= 180 && lat >= -90 && lat <= 90;
178
+ }
179
+ };
180
+
181
+ // src/host/routingHandler.ts
182
+ async function handleRouteRequest(req, config) {
183
+ return RoutingService.fetchRoute(req, config);
184
+ }
185
+ function handleDrawRoute(map, miniAppId, req) {
186
+ const sourceId = `sdk-route-${miniAppId}`;
187
+ const casingLayerId = `sdk-route-casing-${miniAppId}`;
188
+ const lineLayerId = `sdk-route-line-${miniAppId}`;
189
+ const geojson = {
190
+ features: [
191
+ {
192
+ geometry: req.payload.routeData.geometry,
193
+ properties: {},
194
+ type: "Feature"
195
+ }
196
+ ],
197
+ type: "FeatureCollection"
198
+ };
199
+ const source = map.getSource(sourceId);
200
+ if (source) {
201
+ source.setData(geojson);
202
+ } else {
203
+ map.addSource(sourceId, {
204
+ data: geojson,
205
+ type: "geojson"
206
+ });
207
+ map.addLayer({
208
+ id: casingLayerId,
209
+ layout: {
210
+ "line-cap": "round",
211
+ "line-join": "round"
212
+ },
213
+ paint: {
214
+ "line-color": "#111111",
215
+ "line-opacity": 0.8,
216
+ "line-width": 8
217
+ },
218
+ source: sourceId,
219
+ type: "line"
220
+ });
221
+ map.addLayer({
222
+ id: lineLayerId,
223
+ layout: {
224
+ "line-cap": "round",
225
+ "line-join": "round"
226
+ },
227
+ paint: {
228
+ "line-color": "#3b82f6",
229
+ // Bright blue
230
+ "line-width": 4
231
+ },
232
+ source: sourceId,
233
+ type: "line"
234
+ });
235
+ }
236
+ }
237
+ function handleClearRoute(map, miniAppId) {
238
+ const sourceId = `sdk-route-${miniAppId}`;
239
+ const casingLayerId = `sdk-route-casing-${miniAppId}`;
240
+ const lineLayerId = `sdk-route-line-${miniAppId}`;
241
+ if (map.getLayer(lineLayerId)) map.removeLayer(lineLayerId);
242
+ if (map.getLayer(casingLayerId)) map.removeLayer(casingLayerId);
243
+ if (map.getSource(sourceId)) map.removeSource(sourceId);
244
+ }
245
+
246
+ export {
247
+ RoutingService,
248
+ handleRouteRequest,
249
+ handleDrawRoute,
250
+ handleClearRoute
251
+ };
252
+ //# sourceMappingURL=chunk-S4UHRBSM.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"sources":["../src/services/routingService.ts","../src/host/routingHandler.ts"],"sourcesContent":["import type { RouteRequest, RouteResponse } from \"@miniapps-sdk/types/routing\";\n\nexport interface RoutingBackendConfig {\n backend: \"osrm\" | \"valhalla\";\n baseUrl: string;\n}\n\n/**\n * Core routing service that can be used by both SDK (for direct fetching)\n * and Host (for handling bridge requests).\n */\nexport const RoutingService = {\n createErrorResponse(\n req: RouteRequest,\n code: NonNullable<RouteResponse[\"error\"]>[\"code\"],\n message: string,\n ): RouteResponse {\n return {\n error: { code, message },\n requestId: req.requestId,\n sdkVersion: req.sdkVersion,\n success: false,\n type: \"SDK_ROUTING_GETROUTE_RESPONSE\",\n };\n },\n\n decodePolyline(str: string, precision: number): [number, number][] {\n let index = 0,\n lat = 0,\n lng = 0;\n const coordinates: [number, number][] = [];\n const shift = 10 ** precision;\n\n while (index < str.length) {\n let b: any,\n shiftNum = 0,\n result = 0;\n do {\n b = str.charCodeAt(index++) - 63;\n result |= (b & 0x1f) << shiftNum;\n shiftNum += 5;\n } while (b >= 0x20);\n const dlat = result & 1 ? ~(result >> 1) : result >> 1;\n lat += dlat;\n\n shiftNum = 0;\n result = 0;\n do {\n b = str.charCodeAt(index++) - 63;\n result |= (b & 0x1f) << shiftNum;\n shiftNum += 5;\n } while (b >= 0x20);\n const dlng = result & 1 ? ~(result >> 1) : result >> 1;\n lng += dlng;\n\n coordinates.push([lng / shift, lat / shift]);\n }\n return coordinates;\n },\n\n async fetchOSRMRoute(\n req: RouteRequest,\n coords: [number, number][],\n profile: string,\n baseUrl: string,\n ): Promise<RouteResponse> {\n const coordsString = coords.map((c) => `${c[0]},${c[1]}`).join(\";\");\n const osrmProfile =\n profile === \"walking\" ? \"foot\" : profile === \"driving\" ? \"car\" : \"bike\";\n const url = `${baseUrl}/route/v1/${osrmProfile}/${coordsString}?overview=full&geometries=geojson&steps=true`;\n\n const response = await fetch(url);\n if (!response.ok) {\n throw new Error(`OSRM Error: ${response.statusText}`);\n }\n const data = await response.json();\n\n if (data.code !== \"Ok\" || !data.routes || data.routes.length === 0) {\n throw new Error(\"NO_ROUTE\");\n }\n\n const route = data.routes[0];\n const steps =\n route.legs?.flatMap((leg: any) =>\n leg.steps?.map((step: any) => ({\n distance: step.distance,\n duration: step.duration,\n instruction: step.maneuver?.instruction || \"Continue\",\n })),\n ) || [];\n\n return {\n data: {\n distanceMeters: route.distance,\n durationSeconds: route.duration,\n geometry: route.geometry,\n steps,\n },\n requestId: req.requestId,\n sdkVersion: req.sdkVersion,\n success: true,\n type: \"SDK_ROUTING_GETROUTE_RESPONSE\",\n };\n },\n async fetchRoute(\n req: RouteRequest,\n config: RoutingBackendConfig,\n ): Promise<RouteResponse> {\n const { origin, destination, waypoints, profile, language } = req.payload;\n\n if (\n !this.isValidCoordinate(origin) ||\n !this.isValidCoordinate(destination)\n ) {\n return this.createErrorResponse(\n req,\n \"INVALID_INPUT\",\n \"Origin or destination coordinates are invalid.\",\n );\n }\n if (waypoints && !waypoints.every(this.isValidCoordinate)) {\n return this.createErrorResponse(\n req,\n \"INVALID_INPUT\",\n \"One or more waypoints are invalid.\",\n );\n }\n\n const allCoords = [origin, ...(waypoints || []), destination];\n\n try {\n if (config.backend === \"osrm\") {\n return await this.fetchOSRMRoute(\n req,\n allCoords,\n profile,\n config.baseUrl,\n );\n } else {\n return await this.fetchValhallaRoute(\n req,\n allCoords,\n profile,\n language,\n config.baseUrl,\n );\n }\n } catch (error) {\n if (error instanceof Error && error.message.includes(\"NO_ROUTE\")) {\n return this.createErrorResponse(\n req,\n \"NO_ROUTE\",\n \"No route could be found for the given points.\",\n );\n }\n if (error instanceof TypeError) {\n return this.createErrorResponse(\n req,\n \"NETWORK_ERROR\",\n \"Failed to connect to the routing backend.\",\n );\n }\n return this.createErrorResponse(\n req,\n \"BACKEND_ERROR\",\n `Routing backend error: ${error instanceof Error ? error.message : \"Unknown error\"}`,\n );\n }\n },\n\n async fetchValhallaRoute(\n req: RouteRequest,\n coords: [number, number][],\n profile: string,\n language: string | undefined,\n baseUrl: string,\n ): Promise<RouteResponse> {\n const valhallaProfile =\n profile === \"driving\"\n ? \"auto\"\n : profile === \"walking\"\n ? \"pedestrian\"\n : \"bicycle\";\n const body = {\n costing: valhallaProfile,\n directions_options: {\n language: language || \"en-US\",\n },\n locations: coords.map((c) => ({ lat: c[1], lon: c[0] })),\n };\n\n const response = await fetch(`${baseUrl}/route`, {\n body: JSON.stringify(body),\n headers: { \"Content-Type\": \"application/json\" },\n method: \"POST\",\n });\n\n if (!response.ok) {\n throw new Error(`Valhalla Error: ${response.statusText}`);\n }\n const data = await response.json();\n\n if (!data.trip || !data.trip.legs || data.trip.legs.length === 0) {\n throw new Error(\"NO_ROUTE\");\n }\n\n const trip = data.trip;\n const decodedCoordinates = this.decodePolyline(trip.legs[0].shape, 6);\n\n const steps =\n trip.legs.flatMap((leg: any) =>\n leg.maneuvers?.map((maneuver: any) => ({\n distance: maneuver.length * 1000, // Valhalla returns km\n duration: maneuver.time,\n instruction: maneuver.instruction || \"Continue\",\n })),\n ) || [];\n\n return {\n data: {\n distanceMeters: trip.summary.length * 1000, // km to m\n durationSeconds: trip.summary.time,\n geometry: {\n coordinates: decodedCoordinates,\n type: \"LineString\",\n },\n steps,\n },\n requestId: req.requestId,\n sdkVersion: req.sdkVersion,\n success: true,\n type: \"SDK_ROUTING_GETROUTE_RESPONSE\",\n };\n },\n\n isValidCoordinate(coord: [number, number]): boolean {\n const [lng, lat] = coord;\n return lng >= -180 && lng <= 180 && lat >= -90 && lat <= 90;\n },\n};\n","import type { RoutingBackendConfig } from \"@miniapps-sdk/services/routingService\";\nimport { RoutingService } from \"@miniapps-sdk/services/routingService\";\nimport type {\n DrawRouteRequest,\n RouteRequest,\n RouteResponse,\n} from \"@miniapps-sdk/types/routing\";\nimport type { Map } from \"maplibre-gl\";\n\n/**\n * Handles a route request from the mini-app and fetches data from the configured backend.\n */\nexport async function handleRouteRequest(\n req: RouteRequest,\n config: RoutingBackendConfig,\n): Promise<RouteResponse> {\n return RoutingService.fetchRoute(req, config);\n}\n\n/**\n * Host-side map rendering logic for routes.\n */\nexport function handleDrawRoute(\n map: Map,\n miniAppId: string,\n req: DrawRouteRequest,\n) {\n const sourceId = `sdk-route-${miniAppId}`;\n const casingLayerId = `sdk-route-casing-${miniAppId}`;\n const lineLayerId = `sdk-route-line-${miniAppId}`;\n\n const geojson = {\n features: [\n {\n geometry: req.payload.routeData.geometry,\n properties: {},\n type: \"Feature\",\n },\n ],\n type: \"FeatureCollection\",\n };\n\n const source = map.getSource(sourceId);\n if (source) {\n // @ts-expect-error - maplibre-gl types might not expose setData nicely on generic source\n (source as maplibregl.GeoJSONSource).setData(geojson);\n } else {\n map.addSource(sourceId, {\n data: geojson as any,\n type: \"geojson\",\n });\n\n map.addLayer({\n id: casingLayerId,\n layout: {\n \"line-cap\": \"round\",\n \"line-join\": \"round\",\n },\n paint: {\n \"line-color\": \"#111111\",\n \"line-opacity\": 0.8,\n \"line-width\": 8,\n },\n source: sourceId,\n type: \"line\",\n });\n\n map.addLayer({\n id: lineLayerId,\n layout: {\n \"line-cap\": \"round\",\n \"line-join\": \"round\",\n },\n paint: {\n \"line-color\": \"#3b82f6\", // Bright blue\n \"line-width\": 4,\n },\n source: sourceId,\n type: \"line\",\n });\n }\n}\n\nexport function handleClearRoute(map: Map, miniAppId: string) {\n const sourceId = `sdk-route-${miniAppId}`;\n const casingLayerId = `sdk-route-casing-${miniAppId}`;\n const lineLayerId = `sdk-route-line-${miniAppId}`;\n\n if (map.getLayer(lineLayerId)) map.removeLayer(lineLayerId);\n if (map.getLayer(casingLayerId)) map.removeLayer(casingLayerId);\n if (map.getSource(sourceId)) map.removeSource(sourceId);\n}\n"],"mappings":"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","names":[]}
@@ -0,0 +1,2 @@
1
+ export { h as handleClearRoute, d as handleDrawRoute, e as handleRouteRequest } from '../index-B9dDciJq.cjs';
2
+ import 'maplibre-gl';
@@ -0,0 +1,2 @@
1
+ export { h as handleClearRoute, d as handleDrawRoute, e as handleRouteRequest } from '../index-B9dDciJq.js';
2
+ import 'maplibre-gl';