@multisetai/vps 1.0.6 → 1.0.7-beta.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
@@ -24,11 +24,13 @@ npm install @multisetai/vps three
24
24
 
25
25
  ### Runtime Requirements
26
26
 
27
- - **HTTPS**: WebXR requires a secure context. Use HTTPS in production or `https://localhost` for local development.
28
- - **WebXR-capable device**: Android device with ARCore or iOS device with ARKit (via WebXR Viewer or Safari)
29
- - **Modern browser**: Chrome/Edge (Android) or Safari (iOS 15+)
27
+ - **HTTPS**: WebXR requires a secure context. Use HTTPS in production or `http://localhost` for local development.
28
+ - **WebXR-capable device**: Android device with ARCore
29
+ - **Modern browser**: Chrome or Edge on Android
30
30
  - **Three.js**: Version 0.176.0 or higher (peer dependency)
31
31
 
32
+ > **iOS is not supported.** This SDK requires the `camera-access` WebXR feature to capture frames for localization. Safari on iOS does not implement `camera-access`, so the AR session will fail to start on any iOS browser.
33
+
32
34
  ### Development Requirements
33
35
 
34
36
  - Node.js 16+ and npm
@@ -55,7 +57,7 @@ Since this SDK makes direct API calls to MultiSet servers from browser-based app
55
57
  Add entries for both your local development and production environments:
56
58
 
57
59
  **Local Development:**
58
- - **Format:** `http://localhost:PORT` (e.g., `http://localhost:3000`, `http://localhost:5173`)
60
+ - **Format:** `https://localhost:PORT` (e.g., `https://localhost:3000`, `https://localhost:5173`)
59
61
  - **Note:** Use the exact port your development server runs on
60
62
 
61
63
  **Production:**
@@ -81,7 +83,7 @@ import { WebxrController } from '@multisetai/vps/webxr';
81
83
  const client = new MultisetClient({
82
84
  clientId: 'CLIENT_ID',
83
85
  clientSecret: 'CLIENT_SECRET',
84
- code: 'MAP_OUR_MAPSET_CODE',
86
+ code: 'MAP_OR_MAPSET_CODE',
85
87
  mapType: 'map', // or 'map-set'
86
88
  endpoints: DEFAULT_ENDPOINTS,
87
89
  onAuthorize: (token) => console.log('Authorized:', token),
@@ -131,11 +133,42 @@ new MultisetClient(config: IMultisetSdkConfig)
131
133
 
132
134
  ```typescript
133
135
  interface IMultisetSdkConfig {
136
+ // Core
134
137
  clientId: string; // Your MultiSet client ID
135
138
  clientSecret: string; // Your MultiSet client secret
136
- code: string; // Map code (e.g., 'MAP_XXXXX')
139
+ code: string; // Map or map-set code (e.g., 'MAP_XXXXX')
137
140
  mapType: 'map' | 'map-set'; // Type of map to use
138
141
  endpoints?: Partial<IMultisetSdkEndpoints>; // Optional custom endpoints
142
+ mapId?: string; // Optional map ID (overrides code-based lookup)
143
+ mapSetId?: string; // Optional map-set ID (overrides code-based lookup)
144
+
145
+ // Localization behavior
146
+ /** If true, automatically start a localization run when the AR session starts. */
147
+ autoLocalize?: boolean;
148
+ /** If true, automatically re-localize when tracking is lost and then recovered. */
149
+ relocalization?: boolean;
150
+ /** When enabled, only accept a localization result if confidence >= confidenceThreshold. */
151
+ confidenceCheck?: boolean;
152
+ /** Minimum confidence (0.2–0.8) required when confidenceCheck is enabled. */
153
+ confidenceThreshold?: number;
154
+ /** Total single-frame attempts per localization run (1–5). */
155
+ requestAttempts?: number;
156
+ /** Time in seconds between attempts in a localization run (1–5). */
157
+ localizationInterval?: number;
158
+ /** Include device geo pose as a hint in localization requests. */
159
+ passGeoPose?: boolean;
160
+ /** Request geo coordinates in the localization response (if supported by backend). */
161
+ geoCoordinatesInResponse?: boolean;
162
+
163
+ // Localization lifecycle callbacks
164
+ /** Invoked at the start of a localization run. */
165
+ onLocalizationInit?: () => void;
166
+ /** Invoked after a successful localization that meets confidence criteria (if enabled). */
167
+ onLocalizationSuccess?: (result: ILocalizeAndMapDetails) => void;
168
+ /** Invoked when all attempts fail or the best result is below the confidence threshold. */
169
+ onLocalizationFailure?: (reason?: string) => void;
170
+
171
+ // Core callbacks
139
172
  onAuthorize?: (token: string) => void;
140
173
  onFrameCaptured?: (payload: IFrameCaptureEvent) => void;
141
174
  onCameraIntrinsics?: (intrinsics: ICameraIntrinsicsEvent) => void;
@@ -156,15 +189,28 @@ const token = await client.authorize();
156
189
 
157
190
  **Returns**: The access token as a string.
158
191
 
159
- #### Events
192
+ #### Events and callbacks
160
193
 
161
- The client emits events through callback functions:
194
+ The client and controller emit events through callback functions:
162
195
 
163
196
  - **`onAuthorize`**: Called when authorization succeeds with the access token
164
197
  - **`onFrameCaptured`**: Called when a camera frame is captured for localization
165
198
  - **`onCameraIntrinsics`**: Called with camera intrinsic parameters
166
199
  - **`onPoseResult`**: Called with localization results (pose found/not found)
167
- - **`onError`**: Called when any error occurs
200
+ - **`onLocalizationInit`**: Called at the start of a localization run
201
+ - **`onLocalizationSuccess`**: Called when a localization run succeeds (optionally after confidence checks)
202
+ - **`onLocalizationFailure`**: Called when a localization run fails or the best result is below the confidence threshold
203
+ - **`onError`**: Called when any error occurs during authorization or localization
204
+
205
+ ### Advanced localization behavior
206
+
207
+ `MultisetClient` together with `WebxrController` supports a higher-level localization flow similar to the Unity SingleFrameLocalizationManager:
208
+
209
+ - **Multiple attempts per run**: `requestAttempts` and `localizationInterval` control how many single-frame attempts are made and how long to wait between them.
210
+ - **Confidence-based acceptance**: When `confidenceCheck` is `true`, only results with `confidence >= confidenceThreshold` (0.2–0.8) are treated as successful.
211
+ - **Auto-localize**: When `autoLocalize` is `true`, a localization run is started automatically when the AR session starts.
212
+ - **Re-localize on tracking loss**: When `relocalization` is `true`, the controller automatically tries to re-localize after tracking has been lost for a short period.
213
+ - **Geo hint**: When `passGeoPose` is `true`, the SDK uses the browser Geolocation API to compute a `geoHint` string `"lat,lon,alt"` and sends it with the localization request. When `geoCoordinatesInResponse` is `true`, the backend may include geo coordinates in the response (if supported).
168
214
 
169
215
  ## WebXR Controller API
170
216
 
@@ -216,7 +262,26 @@ if (result?.localizeData?.poseFound) {
216
262
  }
217
263
  ```
218
264
 
219
- **Returns**: Object with `localizeData` and optional `mapDetails`, or `null` if capture fails.
265
+ **Returns**: Object with `localizeData` and optional `mapDetails`, or `null` if no pose is found or capture fails.
266
+
267
+ ##### `localizeFrame(): Promise<ILocalizeAndMapDetails | null>`
268
+
269
+ Runs a full localization cycle using the configured `requestAttempts`, `localizationInterval`,
270
+ `confidenceCheck`, and `confidenceThreshold`:
271
+
272
+ ```typescript
273
+ const result = await controller.localizeFrame();
274
+ if (result?.localizeData?.poseFound) {
275
+ console.log('Localized at:', result.localizeData.position);
276
+ }
277
+ ```
278
+
279
+ This method:
280
+
281
+ - Fires `onLocalizationInit` at the start of the run
282
+ - Performs multiple attempts and picks the best result by confidence
283
+ - Applies the confidence threshold when `confidenceCheck` is enabled
284
+ - Calls `onLocalizationSuccess` or `onLocalizationFailure` accordingly
220
285
 
221
286
  ##### `getScene(): THREE.Scene`
222
287
 
@@ -308,6 +373,15 @@ const client = new MultisetClient({
308
373
  code: 'MAP_OR_MAPSET_CODE',
309
374
  mapType: 'map',
310
375
  endpoints: DEFAULT_ENDPOINTS,
376
+ // Optional advanced behavior
377
+ autoLocalize: true,
378
+ relocalization: true,
379
+ confidenceCheck: true,
380
+ confidenceThreshold: 0.5,
381
+ requestAttempts: 3,
382
+ localizationInterval: 2,
383
+ passGeoPose: true,
384
+ geoCoordinatesInResponse: true,
311
385
  onAuthorize: (token) => console.log('Authorized:', token),
312
386
  onError: (error) => console.error('Error:', error),
313
387
  });
@@ -128,15 +128,46 @@ interface ILocalizeAndMapDetails {
128
128
  }
129
129
 
130
130
  interface IMultisetSdkConfig {
131
+ mapId?: string;
132
+ mapSetId?: string;
131
133
  clientId: string;
132
134
  clientSecret: string;
135
+ /** Map or map-set code used for localization. */
133
136
  code: string;
137
+ /** Map or map-set type ('map' or 'map-set'). */
134
138
  mapType: MapType;
135
139
  endpoints?: Partial<IMultisetSdkEndpoints>;
140
+ /** If true, automatically start a localization run when the AR session starts. */
141
+ autoLocalize?: boolean;
142
+ /** If true, automatically re-localize when tracking is lost and then recovered. */
143
+ relocalization?: boolean;
144
+ /** When enabled, only accept a localization result if confidence >= confidenceThreshold. */
145
+ confidenceCheck?: boolean;
146
+ /** Minimum confidence (0.2–0.8) required when confidenceCheck is enabled. */
147
+ confidenceThreshold?: number;
148
+ /** Total single-frame attempts per localization run (1–5). */
149
+ requestAttempts?: number;
150
+ /** Time in seconds between attempts in a localization run (1–5). */
151
+ localizationInterval?: number;
152
+ /** Include device geo pose as a hint in localization requests. */
153
+ passGeoPose?: boolean;
154
+ /** Request geo coordinates in the localization response (if supported by backend). */
155
+ geoCoordinatesInResponse?: boolean;
156
+ /** Invoked at the start of a localization run. */
157
+ onLocalizationInit?: () => void;
158
+ /** Invoked after a successful localization that meets confidence criteria (if enabled). */
159
+ onLocalizationSuccess?: (result: ILocalizeAndMapDetails) => void;
160
+ /** Invoked when all attempts fail or the best result is below the confidence threshold. */
161
+ onLocalizationFailure?: (reason?: string) => void;
162
+ /** Called after a successful authorization with the access token. */
136
163
  onAuthorize?: (token: string) => void;
164
+ /** Called whenever a camera frame is captured and sent for localization. */
137
165
  onFrameCaptured?: (payload: IFrameCaptureEvent) => void;
166
+ /** Called with the camera intrinsics used for a localization request. */
138
167
  onCameraIntrinsics?: (intrinsics: ICameraIntrinsicsEvent) => void;
168
+ /** Called with the raw pose/localization result returned by the backend. */
139
169
  onPoseResult?: (payload: IPoseResultEvent) => void;
170
+ /** Called when any error occurs during authorization or localization. */
140
171
  onError?: (error: unknown) => void;
141
172
  }
142
173
  interface IMultisetSdkEndpoints {
@@ -181,18 +212,18 @@ interface ILocalizeResultEvent {
181
212
  response: ILocalizeAndMapDetails | null;
182
213
  }
183
214
  declare const DEFAULT_ENDPOINTS: IMultisetSdkEndpoints;
184
- /**
185
- * Placeholder class to be implemented by porting logic from multiset-webxr-sdk.
186
- */
215
+
187
216
  declare class MultisetClient {
188
217
  private readonly config;
189
218
  private readonly endpoints;
190
219
  private accessToken;
191
220
  constructor(config: IMultisetSdkConfig);
192
221
  get token(): string | null;
222
+ getConfig(): IMultisetSdkConfig;
193
223
  authorize(): Promise<string>;
194
224
  private handleError;
195
225
  localizeWithFrame(frame: IFrameCaptureEvent, intrinsics: ICameraIntrinsicsEvent): Promise<ILocalizeAndMapDetails | null>;
226
+ private getGeoPoseComponents;
196
227
  private queryLocalization;
197
228
  private fetchMapDetails;
198
229
  private fetchMapSetDetails;
@@ -1,6 +1,6 @@
1
1
  import axios from 'axios';
2
2
 
3
- // src/lib/core/index.ts
3
+ // src/lib/core/config.ts
4
4
  var DEFAULT_ENDPOINTS = {
5
5
  authUrl: "https://api.multiset.ai/v1/m2m/token",
6
6
  queryUrl: "https://api.multiset.ai/v1/vps/map/query-form",
@@ -21,6 +21,9 @@ var MultisetClient = class {
21
21
  get token() {
22
22
  return this.accessToken;
23
23
  }
24
+ getConfig() {
25
+ return this.config;
26
+ }
24
27
  async authorize() {
25
28
  var _a, _b, _c, _d, _e;
26
29
  try {
@@ -68,22 +71,54 @@ var MultisetClient = class {
68
71
  }
69
72
  return queryResult;
70
73
  }
74
+ async getGeoPoseComponents() {
75
+ if (typeof navigator === "undefined" || !("geolocation" in navigator) || !navigator.geolocation) {
76
+ return null;
77
+ }
78
+ try {
79
+ const position = await new Promise((resolve, reject) => {
80
+ navigator.geolocation.getCurrentPosition(resolve, reject, {
81
+ enableHighAccuracy: true,
82
+ timeout: 1e4,
83
+ maximumAge: 0
84
+ });
85
+ });
86
+ const { latitude, longitude, altitude } = position.coords;
87
+ const safeAltitude = typeof altitude === "number" && !Number.isNaN(altitude) ? altitude : 0;
88
+ return [latitude, longitude, safeAltitude];
89
+ } catch {
90
+ return null;
91
+ }
92
+ }
71
93
  async queryLocalization(frame, intrinsics) {
72
- var _a;
94
+ var _a, _b, _c;
73
95
  const formData = new FormData();
74
- formData.append("isRightHanded", "true");
75
- formData.append("width", `${frame.width}`);
76
- formData.append("height", `${frame.height}`);
77
- formData.append("px", `${intrinsics.px}`);
78
- formData.append("py", `${intrinsics.py}`);
79
- formData.append("fx", `${intrinsics.fx}`);
80
- formData.append("fy", `${intrinsics.fy}`);
81
- formData.append("queryImage", frame.blob);
96
+ formData.append("mapId", (_a = this.config.mapId) != null ? _a : "");
97
+ formData.append("mapSetId", (_b = this.config.mapSetId) != null ? _b : "");
82
98
  if (this.config.mapType === "map") {
83
99
  formData.append("mapCode", this.config.code);
84
100
  } else {
85
101
  formData.append("mapSetCode", this.config.code);
86
102
  }
103
+ formData.append("isRightHanded", "true");
104
+ formData.append("fx", `${intrinsics.fx}`);
105
+ formData.append("fy", `${intrinsics.fy}`);
106
+ formData.append("px", `${intrinsics.px}`);
107
+ formData.append("py", `${intrinsics.py}`);
108
+ formData.append("width", `${frame.width}`);
109
+ formData.append("height", `${frame.height}`);
110
+ formData.append("queryImage", frame.blob);
111
+ if (this.config.geoCoordinatesInResponse) {
112
+ formData.append("geoCoordinatesInResponse", "true");
113
+ }
114
+ if (this.config.passGeoPose) {
115
+ const components = await this.getGeoPoseComponents();
116
+ if (components) {
117
+ const [latitude, longitude, altitude] = components;
118
+ const geoHint = `${latitude},${longitude},${altitude}`;
119
+ formData.append("geoHint", geoHint);
120
+ }
121
+ }
87
122
  try {
88
123
  const response = await axios.post(
89
124
  this.endpoints.queryUrl,
@@ -101,7 +136,7 @@ var MultisetClient = class {
101
136
  const result = {
102
137
  localizeData: data
103
138
  };
104
- if ((_a = data.mapIds) == null ? void 0 : _a.length) {
139
+ if ((_c = data.mapIds) == null ? void 0 : _c.length) {
105
140
  const mapDetails = await this.fetchMapDetails(data.mapIds[0]);
106
141
  if (mapDetails) {
107
142
  result.mapDetails = mapDetails;
@@ -1 +1 @@
1
- {"version":3,"sources":["../../src/lib/core/index.ts"],"names":[],"mappings":"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","file":"index.js","sourcesContent":["import axios, { AxiosError } from 'axios';\nimport type {\n ILocalizeAndMapDetails,\n ILocalizeResponse,\n MapType,\n IGetMapsDetailsResponse,\n IMapSetMapsResponse,\n} from './types';\n\nexport interface IMultisetSdkConfig {\n clientId: string;\n clientSecret: string;\n code: string;\n mapType: MapType;\n endpoints?: Partial<IMultisetSdkEndpoints>;\n onAuthorize?: (token: string) => void;\n onFrameCaptured?: (payload: IFrameCaptureEvent) => void;\n onCameraIntrinsics?: (intrinsics: ICameraIntrinsicsEvent) => void;\n onPoseResult?: (payload: IPoseResultEvent) => void;\n onError?: (error: unknown) => void;\n}\n\nexport interface IMultisetSdkEndpoints {\n authUrl: string;\n queryUrl: string;\n mapDetailsUrl: string;\n fileDownloadUrl: string;\n mapSetDetailsUrl: string;\n}\n\nexport interface IFrameCaptureEvent {\n blob: Blob;\n width: number;\n height: number;\n}\n\nexport interface ICameraIntrinsicsEvent {\n fx: number;\n fy: number;\n px: number;\n py: number;\n width: number;\n height: number;\n}\n\nexport interface IPoseResultEvent {\n poseFound: boolean;\n position: {\n x: number;\n y: number;\n z: number;\n };\n rotation: {\n x: number;\n y: number;\n z: number;\n w: number;\n };\n mapIds: string[];\n confidence?: number;\n}\n\nexport interface ILocalizeResultEvent {\n frame: IFrameCaptureEvent;\n intrinsics: ICameraIntrinsicsEvent;\n response: ILocalizeAndMapDetails | null;\n}\n\nexport const DEFAULT_ENDPOINTS: IMultisetSdkEndpoints = {\n authUrl: 'https://api.multiset.ai/v1/m2m/token',\n queryUrl: 'https://api.multiset.ai/v1/vps/map/query-form',\n mapDetailsUrl: 'https://api.multiset.ai/v1/vps/map/',\n mapSetDetailsUrl: 'https://api.multiset.ai/v1/vps/map-set/',\n fileDownloadUrl: 'https://api.multiset.ai/v1/file',\n};\n\n/**\n * Placeholder class to be implemented by porting logic from multiset-webxr-sdk.\n */\nexport class MultisetClient {\n private readonly endpoints: IMultisetSdkEndpoints;\n private accessToken: string | null = null;\n\n constructor(private readonly config: IMultisetSdkConfig) {\n this.config = config;\n this.endpoints = {\n ...DEFAULT_ENDPOINTS,\n ...config.endpoints,\n };\n }\n\n get token(): string | null {\n return this.accessToken;\n }\n\n async authorize(): Promise<string> {\n try {\n const response = await axios.post(\n this.endpoints.authUrl,\n {},\n {\n auth: {\n username: this.config.clientId,\n password: this.config.clientSecret,\n },\n }\n );\n\n const token: string | undefined =\n response.data?.token ?? response.data?.access_token;\n\n if (!token) {\n throw new Error('Authorization succeeded but no token was returned.');\n }\n\n this.accessToken = token;\n this.config.onAuthorize?.(token);\n return token;\n } catch (error) {\n this.handleError(error);\n throw error;\n }\n }\n\n private handleError(error: unknown): void {\n if (axios.isAxiosError(error)) {\n const axiosError = error as AxiosError;\n this.config.onError?.(axiosError);\n } else {\n this.config.onError?.(error);\n }\n }\n\n async localizeWithFrame(\n frame: IFrameCaptureEvent,\n intrinsics: ICameraIntrinsicsEvent\n ): Promise<ILocalizeAndMapDetails | null> {\n if (!this.accessToken) {\n throw new Error('Access token is missing. Call authorize() first.');\n }\n\n this.config.onFrameCaptured?.(frame);\n this.config.onCameraIntrinsics?.(intrinsics);\n\n const queryResult = await this.queryLocalization(frame, intrinsics);\n\n if (queryResult?.localizeData?.poseFound) {\n this.config.onPoseResult?.(queryResult.localizeData);\n }\n\n return queryResult;\n }\n\n private async queryLocalization(\n frame: IFrameCaptureEvent,\n intrinsics: ICameraIntrinsicsEvent\n ): Promise<ILocalizeAndMapDetails | null> {\n const formData = new FormData();\n formData.append('isRightHanded', 'true');\n formData.append('width', `${frame.width}`);\n formData.append('height', `${frame.height}`);\n formData.append('px', `${intrinsics.px}`);\n formData.append('py', `${intrinsics.py}`);\n formData.append('fx', `${intrinsics.fx}`);\n formData.append('fy', `${intrinsics.fy}`);\n formData.append('queryImage', frame.blob);\n\n if (this.config.mapType === 'map') {\n formData.append('mapCode', this.config.code);\n } else {\n formData.append('mapSetCode', this.config.code);\n }\n\n try {\n const response = await axios.post<ILocalizeResponse>(\n this.endpoints.queryUrl,\n formData,\n {\n headers: {\n Authorization: `Bearer ${this.accessToken}`,\n },\n }\n );\n\n const data = response.data;\n if (!data.poseFound) {\n return null;\n }\n\n const result: ILocalizeAndMapDetails = {\n localizeData: data,\n };\n\n if (data.mapIds?.length) {\n const mapDetails = await this.fetchMapDetails(data.mapIds[0]);\n if (mapDetails) {\n result.mapDetails = mapDetails;\n }\n }\n\n return result;\n } catch (error) {\n this.handleError(error);\n return null;\n }\n }\n\n private async fetchMapDetails(mapId: string): Promise<IGetMapsDetailsResponse | null> {\n try {\n const response = await axios.get<IGetMapsDetailsResponse>(\n `${this.endpoints.mapDetailsUrl}${mapId}`,\n {\n headers: {\n Authorization: `Bearer ${this.accessToken}`,\n },\n }\n );\n return response.data;\n } catch (error) {\n this.handleError(error);\n return null;\n }\n }\n\n private async fetchMapSetDetails(mapSetId: string): Promise<IMapSetMapsResponse | null> {\n try {\n const response = await axios.get<IMapSetMapsResponse>(\n `${this.endpoints.mapSetDetailsUrl}${mapSetId}`,\n {\n headers: {\n Authorization: `Bearer ${this.accessToken}`,\n },\n }\n );\n return response.data;\n } catch (error) {\n this.handleError(error);\n return null;\n }\n }\n}\n\nexport type { IMultisetSdkConfig as IMultisetClientOptions };\nexport type {\n MapType,\n ILocalizeResponse,\n ILocalizeAndMapDetails,\n IGetMapsDetailsResponse,\n IMapSetMapsResponse,\n} from './types';\n\n"]}
1
+ {"version":3,"sources":["../../src/lib/core/config.ts","../../src/lib/core/client.ts"],"names":[],"mappings":"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","file":"index.js","sourcesContent":["import type { ILocalizeAndMapDetails, MapType } from './types';\n\nexport interface IMultisetSdkConfig {\n mapId?: string;\n mapSetId?: string;\n clientId: string;\n clientSecret: string;\n /** Map or map-set code used for localization. */\n code: string;\n /** Map or map-set type ('map' or 'map-set'). */\n mapType: MapType;\n endpoints?: Partial<IMultisetSdkEndpoints>;\n\n /** If true, automatically start a localization run when the AR session starts. */\n autoLocalize?: boolean;\n /** If true, automatically re-localize when tracking is lost and then recovered. */\n relocalization?: boolean;\n /** When enabled, only accept a localization result if confidence >= confidenceThreshold. */\n confidenceCheck?: boolean;\n /** Minimum confidence (0.2–0.8) required when confidenceCheck is enabled. */\n confidenceThreshold?: number;\n /** Total single-frame attempts per localization run (1–5). */\n requestAttempts?: number;\n /** Time in seconds between attempts in a localization run (1–5). */\n localizationInterval?: number;\n /** Include device geo pose as a hint in localization requests. */\n passGeoPose?: boolean;\n /** Request geo coordinates in the localization response (if supported by backend). */\n geoCoordinatesInResponse?: boolean;\n\n /** Invoked at the start of a localization run. */\n onLocalizationInit?: () => void;\n /** Invoked after a successful localization that meets confidence criteria (if enabled). */\n onLocalizationSuccess?: (result: ILocalizeAndMapDetails) => void;\n /** Invoked when all attempts fail or the best result is below the confidence threshold. */\n onLocalizationFailure?: (reason?: string) => void;\n\n /** Called after a successful authorization with the access token. */\n onAuthorize?: (token: string) => void;\n /** Called whenever a camera frame is captured and sent for localization. */\n onFrameCaptured?: (payload: IFrameCaptureEvent) => void;\n /** Called with the camera intrinsics used for a localization request. */\n onCameraIntrinsics?: (intrinsics: ICameraIntrinsicsEvent) => void;\n /** Called with the raw pose/localization result returned by the backend. */\n onPoseResult?: (payload: IPoseResultEvent) => void;\n /** Called when any error occurs during authorization or localization. */\n onError?: (error: unknown) => void;\n}\n\nexport interface IMultisetSdkEndpoints {\n authUrl: string;\n queryUrl: string;\n mapDetailsUrl: string;\n fileDownloadUrl: string;\n mapSetDetailsUrl: string;\n}\n\nexport interface IFrameCaptureEvent {\n blob: Blob;\n width: number;\n height: number;\n}\n\nexport interface ICameraIntrinsicsEvent {\n fx: number;\n fy: number;\n px: number;\n py: number;\n width: number;\n height: number;\n}\n\nexport interface IPoseResultEvent {\n poseFound: boolean;\n position: {\n x: number;\n y: number;\n z: number;\n };\n rotation: {\n x: number;\n y: number;\n z: number;\n w: number;\n };\n mapIds: string[];\n confidence?: number;\n}\n\nexport interface ILocalizeResultEvent {\n frame: IFrameCaptureEvent;\n intrinsics: ICameraIntrinsicsEvent;\n response: ILocalizeAndMapDetails | null;\n}\n\nexport const DEFAULT_ENDPOINTS: IMultisetSdkEndpoints = {\n authUrl: 'https://api.multiset.ai/v1/m2m/token',\n queryUrl: 'https://api.multiset.ai/v1/vps/map/query-form',\n mapDetailsUrl: 'https://api.multiset.ai/v1/vps/map/',\n mapSetDetailsUrl: 'https://api.multiset.ai/v1/vps/map-set/',\n fileDownloadUrl: 'https://api.multiset.ai/v1/file',\n};\n","import axios, { AxiosError } from 'axios';\nimport type {\n IMultisetSdkConfig,\n IMultisetSdkEndpoints,\n IFrameCaptureEvent,\n ICameraIntrinsicsEvent,\n} from './config';\nimport { DEFAULT_ENDPOINTS } from './config';\nimport type {\n ILocalizeAndMapDetails,\n ILocalizeResponse,\n IGetMapsDetailsResponse,\n IMapSetMapsResponse,\n} from './types';\n\nexport class MultisetClient {\n private readonly endpoints: IMultisetSdkEndpoints;\n private accessToken: string | null = null;\n\n constructor(private readonly config: IMultisetSdkConfig) {\n this.config = config;\n this.endpoints = {\n ...DEFAULT_ENDPOINTS,\n ...config.endpoints,\n };\n }\n\n get token(): string | null {\n return this.accessToken;\n }\n\n getConfig(): IMultisetSdkConfig {\n return this.config;\n }\n\n async authorize(): Promise<string> {\n try {\n const response = await axios.post(\n this.endpoints.authUrl,\n {},\n {\n auth: {\n username: this.config.clientId,\n password: this.config.clientSecret,\n },\n }\n );\n\n const token: string | undefined =\n response.data?.token ?? response.data?.access_token;\n\n if (!token) {\n throw new Error('Authorization succeeded but no token was returned.');\n }\n\n this.accessToken = token;\n this.config.onAuthorize?.(token);\n return token;\n } catch (error) {\n this.handleError(error);\n throw error;\n }\n }\n\n private handleError(error: unknown): void {\n if (axios.isAxiosError(error)) {\n const axiosError = error as AxiosError;\n this.config.onError?.(axiosError);\n } else {\n this.config.onError?.(error);\n }\n }\n\n async localizeWithFrame(\n frame: IFrameCaptureEvent,\n intrinsics: ICameraIntrinsicsEvent\n ): Promise<ILocalizeAndMapDetails | null> {\n if (!this.accessToken) {\n throw new Error('Access token is missing. Call authorize() first.');\n }\n\n this.config.onFrameCaptured?.(frame);\n this.config.onCameraIntrinsics?.(intrinsics);\n\n const queryResult = await this.queryLocalization(frame, intrinsics);\n\n if (queryResult?.localizeData?.poseFound) {\n this.config.onPoseResult?.(queryResult.localizeData);\n }\n\n return queryResult;\n }\n\n private async getGeoPoseComponents(): Promise<\n [latitude: number, longitude: number, altitude: number] | null\n > {\n if (\n typeof navigator === 'undefined' ||\n !('geolocation' in navigator) ||\n !navigator.geolocation\n ) {\n return null;\n }\n\n try {\n const position = await new Promise<GeolocationPosition>((resolve, reject) => {\n navigator.geolocation.getCurrentPosition(resolve, reject, {\n enableHighAccuracy: true,\n timeout: 10000,\n maximumAge: 0,\n });\n });\n\n const { latitude, longitude, altitude } = position.coords;\n const safeAltitude =\n typeof altitude === 'number' && !Number.isNaN(altitude) ? altitude : 0.0;\n\n return [latitude, longitude, safeAltitude];\n } catch {\n return null;\n }\n }\n\n private async queryLocalization(\n frame: IFrameCaptureEvent,\n intrinsics: ICameraIntrinsicsEvent\n ): Promise<ILocalizeAndMapDetails | null> {\n const formData = new FormData();\n\n formData.append('mapId', this.config.mapId ?? '');\n formData.append('mapSetId', this.config.mapSetId ?? '');\n if (this.config.mapType === 'map') {\n formData.append('mapCode', this.config.code);\n } else {\n formData.append('mapSetCode', this.config.code);\n }\n formData.append('isRightHanded', 'true');\n formData.append('fx', `${intrinsics.fx}`);\n formData.append('fy', `${intrinsics.fy}`);\n formData.append('px', `${intrinsics.px}`);\n formData.append('py', `${intrinsics.py}`);\n formData.append('width', `${frame.width}`);\n formData.append('height', `${frame.height}`);\n formData.append('queryImage', frame.blob);\n if (this.config.geoCoordinatesInResponse) {\n formData.append('geoCoordinatesInResponse', 'true');\n }\n if (this.config.passGeoPose) {\n const components = await this.getGeoPoseComponents();\n if (components) {\n const [latitude, longitude, altitude] = components;\n const geoHint = `${latitude},${longitude},${altitude}`;\n formData.append('geoHint', geoHint);\n }\n }\n\n try {\n const response = await axios.post<ILocalizeResponse>(\n this.endpoints.queryUrl,\n formData,\n {\n headers: {\n Authorization: `Bearer ${this.accessToken}`,\n },\n }\n );\n\n const data = response.data;\n if (!data.poseFound) {\n return null;\n }\n\n const result: ILocalizeAndMapDetails = {\n localizeData: data,\n };\n\n if (data.mapIds?.length) {\n const mapDetails = await this.fetchMapDetails(data.mapIds[0]);\n if (mapDetails) {\n result.mapDetails = mapDetails;\n }\n }\n\n return result;\n } catch (error) {\n this.handleError(error);\n return null;\n }\n }\n\n private async fetchMapDetails(mapId: string): Promise<IGetMapsDetailsResponse | null> {\n try {\n const response = await axios.get<IGetMapsDetailsResponse>(\n `${this.endpoints.mapDetailsUrl}${mapId}`,\n {\n headers: {\n Authorization: `Bearer ${this.accessToken}`,\n },\n }\n );\n return response.data;\n } catch (error) {\n this.handleError(error);\n return null;\n }\n }\n\n private async fetchMapSetDetails(mapSetId: string): Promise<IMapSetMapsResponse | null> {\n try {\n const response = await axios.get<IMapSetMapsResponse>(\n `${this.endpoints.mapSetDetailsUrl}${mapSetId}`,\n {\n headers: {\n Authorization: `Bearer ${this.accessToken}`,\n },\n }\n );\n return response.data;\n } catch (error) {\n this.handleError(error);\n return null;\n }\n }\n}\n"]}
package/dist/index.d.ts CHANGED
@@ -1,3 +1,3 @@
1
- export { DEFAULT_ENDPOINTS, ICameraIntrinsicsEvent, IFrameCaptureEvent, IGetMapsDetailsResponse, ILocalizeAndMapDetails, ILocalizeResponse, ILocalizeResultEvent, IMapSetMapsResponse, IMultisetClientOptions, IMultisetClientOptions as IMultisetSdkConfig, IMultisetSdkEndpoints, IPoseResultEvent, MapType, MultisetClient } from './core/index.js';
1
+ export { DEFAULT_ENDPOINTS, ICameraIntrinsicsEvent, IFrameCaptureEvent, IGetMapsDetailsResponse, ILocalizeAndMapDetails, ILocalizeResponse, ILocalizeResultEvent, IMapSetMapsResponse, IMultisetSdkConfig as IMultisetClientOptions, IMultisetSdkConfig, IMultisetSdkEndpoints, IPoseResultEvent, MapType, MultisetClient } from './core/index.js';
2
2
  export { IWebxrControllerOptions, WebxrController } from './webxr/index.js';
3
3
  import 'three';