atriusmaps-node-sdk 3.3.1161 → 3.3.1165
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/dist/cjs/package.json.js +1 -1
- package/dist/cjs/plugins/venueDataLoader/src/constants.js +12 -0
- package/dist/cjs/plugins/venueDataLoader/src/interactiveAuthGate.js +53 -0
- package/dist/cjs/plugins/venueDataLoader/src/loadInteractiveVenue.js +81 -0
- package/dist/cjs/plugins/venueDataLoader/src/resolveAuth.js +22 -0
- package/dist/cjs/plugins/venueDataLoader/src/venueDataLoader.js +55 -8
- package/dist/cjs/plugins/venueDataLoader/src/venueLoadingUtils.js +4 -2
- package/dist/package.json.js +1 -1
- package/dist/plugins/venueDataLoader/src/constants.js +1 -0
- package/dist/plugins/venueDataLoader/src/interactiveAuthGate.js +1 -0
- package/dist/plugins/venueDataLoader/src/loadInteractiveVenue.js +1 -0
- package/dist/plugins/venueDataLoader/src/resolveAuth.js +1 -0
- package/dist/plugins/venueDataLoader/src/venueDataLoader.js +1 -1
- package/dist/plugins/venueDataLoader/src/venueLoadingUtils.js +1 -1
- package/dist/src/utils/bounds.js +1 -1
- package/dist/types/src/types/bus.d.ts +14 -0
- package/package.json +1 -1
package/dist/cjs/package.json.js
CHANGED
|
@@ -0,0 +1,12 @@
|
|
|
1
|
+
'use strict';
|
|
2
|
+
|
|
3
|
+
const LOAD_FAILURE_REASON = {
|
|
4
|
+
MISCONFIGURED: "misconfigured",
|
|
5
|
+
SESSION_EXPIRED: "sessionExpired",
|
|
6
|
+
ACCESS_DENIED: "accessDenied",
|
|
7
|
+
UNAVAILABLE: "unavailable",
|
|
8
|
+
LINK_EXPIRED: "linkExpired",
|
|
9
|
+
SERVICE_ERROR: "serviceError"
|
|
10
|
+
};
|
|
11
|
+
|
|
12
|
+
exports.LOAD_FAILURE_REASON = LOAD_FAILURE_REASON;
|
|
@@ -0,0 +1,53 @@
|
|
|
1
|
+
'use strict';
|
|
2
|
+
|
|
3
|
+
var constants = require('./constants.js');
|
|
4
|
+
|
|
5
|
+
const isAuthState = (value) => typeof value === "object" && value !== null && typeof value.status === "string";
|
|
6
|
+
const AUTH_ERROR_TO_REASON = {
|
|
7
|
+
misconfigured: constants.LOAD_FAILURE_REASON.MISCONFIGURED,
|
|
8
|
+
unavailable: constants.LOAD_FAILURE_REASON.UNAVAILABLE,
|
|
9
|
+
callbackFailed: constants.LOAD_FAILURE_REASON.SERVICE_ERROR,
|
|
10
|
+
signOutFailed: constants.LOAD_FAILURE_REASON.SERVICE_ERROR
|
|
11
|
+
};
|
|
12
|
+
const authErrorToReason = (reason) => AUTH_ERROR_TO_REASON[reason] ?? constants.LOAD_FAILURE_REASON.SERVICE_ERROR;
|
|
13
|
+
const awaitAuthentication = (bus, onUnauthenticated) => new Promise((resolve) => {
|
|
14
|
+
let settled = false;
|
|
15
|
+
let signInRequested = false;
|
|
16
|
+
const settle = (result) => {
|
|
17
|
+
if (!settled) {
|
|
18
|
+
settled = true;
|
|
19
|
+
unsubscribe();
|
|
20
|
+
resolve(result);
|
|
21
|
+
}
|
|
22
|
+
};
|
|
23
|
+
const handleState = (state) => {
|
|
24
|
+
if (state.status === "authenticated") {
|
|
25
|
+
settle({ ok: true });
|
|
26
|
+
} else if (state.status === "error") {
|
|
27
|
+
settle({ ok: false, reason: authErrorToReason(state.reason) });
|
|
28
|
+
} else if (state.status === "unauthenticated" && !settled && !signInRequested) {
|
|
29
|
+
signInRequested = true;
|
|
30
|
+
try {
|
|
31
|
+
onUnauthenticated?.();
|
|
32
|
+
} catch {
|
|
33
|
+
settle({ ok: false, reason: constants.LOAD_FAILURE_REASON.SERVICE_ERROR });
|
|
34
|
+
}
|
|
35
|
+
}
|
|
36
|
+
};
|
|
37
|
+
const unsubscribe = bus.on("auth/stateChanged", (payload) => {
|
|
38
|
+
if (isAuthState(payload)) {
|
|
39
|
+
handleState(payload);
|
|
40
|
+
}
|
|
41
|
+
});
|
|
42
|
+
bus.getFirst("auth/getState").then((current) => {
|
|
43
|
+
if (current === void 0) {
|
|
44
|
+
settle({ ok: false, reason: constants.LOAD_FAILURE_REASON.MISCONFIGURED });
|
|
45
|
+
} else if (isAuthState(current)) {
|
|
46
|
+
handleState(current);
|
|
47
|
+
} else {
|
|
48
|
+
settle({ ok: false, reason: constants.LOAD_FAILURE_REASON.SERVICE_ERROR });
|
|
49
|
+
}
|
|
50
|
+
}).catch(() => settle({ ok: false, reason: constants.LOAD_FAILURE_REASON.SERVICE_ERROR }));
|
|
51
|
+
});
|
|
52
|
+
|
|
53
|
+
exports.awaitAuthentication = awaitAuthentication;
|
|
@@ -0,0 +1,81 @@
|
|
|
1
|
+
'use strict';
|
|
2
|
+
|
|
3
|
+
var constants = require('./constants.js');
|
|
4
|
+
var interactiveAuthGate = require('./interactiveAuthGate.js');
|
|
5
|
+
|
|
6
|
+
class VenueLoadFailedError extends Error {
|
|
7
|
+
reason;
|
|
8
|
+
constructor(reason, options) {
|
|
9
|
+
super(`Venue load failed: ${reason}`, options);
|
|
10
|
+
this.name = "VenueLoadFailedError";
|
|
11
|
+
this.reason = reason;
|
|
12
|
+
}
|
|
13
|
+
}
|
|
14
|
+
const MANIFEST_BASE_URL = "https://assets-api.locuslabs.com/prod";
|
|
15
|
+
const buildInteractiveManifestEndpoint = ({ accountId, venueId, assetStage }) => {
|
|
16
|
+
const base = MANIFEST_BASE_URL;
|
|
17
|
+
const path = `${base}/asset-manifest/accounts/${encodeURIComponent(accountId)}/venue/${encodeURIComponent(venueId)}`;
|
|
18
|
+
return assetStage ? `${path}?assetStage=${encodeURIComponent(assetStage)}&mode=interactive` : path;
|
|
19
|
+
};
|
|
20
|
+
const hop1StatusToReason = (status) => {
|
|
21
|
+
switch (status) {
|
|
22
|
+
case 401:
|
|
23
|
+
return constants.LOAD_FAILURE_REASON.SESSION_EXPIRED;
|
|
24
|
+
case 403:
|
|
25
|
+
return constants.LOAD_FAILURE_REASON.ACCESS_DENIED;
|
|
26
|
+
default:
|
|
27
|
+
return constants.LOAD_FAILURE_REASON.SERVICE_ERROR;
|
|
28
|
+
}
|
|
29
|
+
};
|
|
30
|
+
const hop2StatusToReason = (status) => {
|
|
31
|
+
switch (status) {
|
|
32
|
+
case 403:
|
|
33
|
+
return constants.LOAD_FAILURE_REASON.LINK_EXPIRED;
|
|
34
|
+
default:
|
|
35
|
+
return constants.LOAD_FAILURE_REASON.SERVICE_ERROR;
|
|
36
|
+
}
|
|
37
|
+
};
|
|
38
|
+
const loadInteractiveManifest = async ({
|
|
39
|
+
bus,
|
|
40
|
+
fetch,
|
|
41
|
+
manifestEndpoint
|
|
42
|
+
}) => {
|
|
43
|
+
const failClosed = async (reason) => {
|
|
44
|
+
await bus.send("venueData/loadFailed", { reason });
|
|
45
|
+
throw new VenueLoadFailedError(reason);
|
|
46
|
+
};
|
|
47
|
+
const gate = await interactiveAuthGate.awaitAuthentication(bus, () => bus.send("auth/signIn"));
|
|
48
|
+
if (!gate.ok) {
|
|
49
|
+
return failClosed(gate.reason);
|
|
50
|
+
}
|
|
51
|
+
const attempt = async (op) => {
|
|
52
|
+
try {
|
|
53
|
+
return await op();
|
|
54
|
+
} catch {
|
|
55
|
+
return failClosed(constants.LOAD_FAILURE_REASON.SERVICE_ERROR);
|
|
56
|
+
}
|
|
57
|
+
};
|
|
58
|
+
const token = await attempt(() => bus.getFirst("auth/getAccessToken", { forceRefresh: true }));
|
|
59
|
+
if (typeof token !== "string" || token === "") {
|
|
60
|
+
return failClosed(constants.LOAD_FAILURE_REASON.SESSION_EXPIRED);
|
|
61
|
+
}
|
|
62
|
+
const hop1 = await attempt(() => fetch(manifestEndpoint, { headers: { "x-api-key": token }, redirect: "error" }));
|
|
63
|
+
if (hop1.status !== 200) {
|
|
64
|
+
return failClosed(hop1StatusToReason(hop1.status));
|
|
65
|
+
}
|
|
66
|
+
const body = await attempt(() => hop1.json());
|
|
67
|
+
const signedManifestUrl = body?.data?.url;
|
|
68
|
+
if (typeof signedManifestUrl !== "string" || signedManifestUrl === "") {
|
|
69
|
+
return failClosed(constants.LOAD_FAILURE_REASON.SERVICE_ERROR);
|
|
70
|
+
}
|
|
71
|
+
const hop2 = await attempt(() => fetch(signedManifestUrl));
|
|
72
|
+
if (hop2.status !== 200) {
|
|
73
|
+
return failClosed(hop2StatusToReason(hop2.status));
|
|
74
|
+
}
|
|
75
|
+
return attempt(() => hop2.json());
|
|
76
|
+
};
|
|
77
|
+
|
|
78
|
+
exports.MANIFEST_BASE_URL = MANIFEST_BASE_URL;
|
|
79
|
+
exports.VenueLoadFailedError = VenueLoadFailedError;
|
|
80
|
+
exports.buildInteractiveManifestEndpoint = buildInteractiveManifestEndpoint;
|
|
81
|
+
exports.loadInteractiveManifest = loadInteractiveManifest;
|
|
@@ -0,0 +1,22 @@
|
|
|
1
|
+
'use strict';
|
|
2
|
+
|
|
3
|
+
const isRecord = (value) => typeof value === "object" && value !== null && !Array.isArray(value);
|
|
4
|
+
const resolveAuth = (auth) => {
|
|
5
|
+
if (auth === void 0) {
|
|
6
|
+
return { mode: "public" };
|
|
7
|
+
}
|
|
8
|
+
if (!isRecord(auth)) {
|
|
9
|
+
return { mode: "invalid" };
|
|
10
|
+
}
|
|
11
|
+
const keyCount = Object.keys(auth).length;
|
|
12
|
+
if (auth.type === "kiosk") {
|
|
13
|
+
const { signedManifestUrl } = auth;
|
|
14
|
+
return keyCount === 2 && typeof signedManifestUrl === "string" && signedManifestUrl.trim() !== "" ? { mode: "kiosk", signedManifestUrl } : { mode: "invalid" };
|
|
15
|
+
}
|
|
16
|
+
if (auth.type === "interactive") {
|
|
17
|
+
return keyCount === 1 ? { mode: "interactive" } : { mode: "invalid" };
|
|
18
|
+
}
|
|
19
|
+
return { mode: "invalid" };
|
|
20
|
+
};
|
|
21
|
+
|
|
22
|
+
exports.resolveAuth = resolveAuth;
|
|
@@ -2,7 +2,10 @@
|
|
|
2
2
|
|
|
3
3
|
var R = require('ramda');
|
|
4
4
|
var Zousan = require('zousan');
|
|
5
|
+
var constants = require('./constants.js');
|
|
5
6
|
var fixedOffsetZones = require('./fixedOffsetZones.js');
|
|
7
|
+
var loadInteractiveVenue = require('./loadInteractiveVenue.js');
|
|
8
|
+
var resolveAuth = require('./resolveAuth.js');
|
|
6
9
|
var venueLoadingUtils = require('./venueLoadingUtils.js');
|
|
7
10
|
|
|
8
11
|
function _interopNamespaceDefault(e) {
|
|
@@ -36,12 +39,53 @@ async function create(app, config) {
|
|
|
36
39
|
const getVenueDefForLang = (venueData, lang) => Object.values(venueData.venueList).filter((venueDef) => venueDef.locale === lang).sort((a, b) => countMatchedPrefix(b.id, venueData.id) - countMatchedPrefix(a.id, venueData.id)).at(0);
|
|
37
40
|
const calculateBaseVenueId = (venueData) => getVenueDefForLang(venueData, "en")?.id;
|
|
38
41
|
async function loadVenueData(vConfig, languagesToTry) {
|
|
42
|
+
const thisVenueDataLoaded = venueDataLoaded;
|
|
43
|
+
const thisMapDataLoaded = mapDataLoaded;
|
|
44
|
+
const auth = resolveAuth.resolveAuth(vConfig.auth);
|
|
45
|
+
try {
|
|
46
|
+
const venueData = await fetchVenueData(vConfig, languagesToTry, auth);
|
|
47
|
+
thisVenueDataLoaded.resolve(venueData);
|
|
48
|
+
return thisVenueDataLoaded;
|
|
49
|
+
} catch (err) {
|
|
50
|
+
if (!(err instanceof loadInteractiveVenue.VenueLoadFailedError)) {
|
|
51
|
+
app.bus.send("venueData/loadFailed", { reason: constants.LOAD_FAILURE_REASON.SERVICE_ERROR });
|
|
52
|
+
}
|
|
53
|
+
thisVenueDataLoaded.reject(err);
|
|
54
|
+
thisMapDataLoaded.reject(err);
|
|
55
|
+
throw err;
|
|
56
|
+
}
|
|
57
|
+
}
|
|
58
|
+
function createKioskManifestFetcher(authConfig) {
|
|
59
|
+
let forbidden = false;
|
|
60
|
+
const fetchManifest = venueLoadingUtils.createFetchManifestJson(authConfig, async (status) => {
|
|
61
|
+
forbidden = true;
|
|
62
|
+
await app.bus.send("venueData/authError", { reason: "signedManifestForbidden", status });
|
|
63
|
+
await app.bus.send("venueData/loadFailed", { reason: "linkExpired" });
|
|
64
|
+
});
|
|
65
|
+
return async (url) => {
|
|
66
|
+
try {
|
|
67
|
+
return await fetchManifest(url);
|
|
68
|
+
} catch (err) {
|
|
69
|
+
throw forbidden ? new loadInteractiveVenue.VenueLoadFailedError("linkExpired", { cause: err }) : err;
|
|
70
|
+
}
|
|
71
|
+
};
|
|
72
|
+
}
|
|
73
|
+
async function fetchVenueData(vConfig, languagesToTry, auth) {
|
|
74
|
+
if (auth.mode === "invalid") {
|
|
75
|
+
app.bus.send("venueData/loadFailed", { reason: "misconfigured" });
|
|
76
|
+
throw new loadInteractiveVenue.VenueLoadFailedError("misconfigured");
|
|
77
|
+
}
|
|
39
78
|
const fetchJson = venueLoadingUtils.createFetchJson();
|
|
40
79
|
const fetchText = venueLoadingUtils.createFetchText();
|
|
41
|
-
const fetchManifestJson =
|
|
42
|
-
|
|
43
|
-
|
|
44
|
-
|
|
80
|
+
const fetchManifestJson = auth.mode === "interactive" ? () => loadInteractiveVenue.loadInteractiveManifest({
|
|
81
|
+
bus: app.bus,
|
|
82
|
+
fetch: globalThis.fetch,
|
|
83
|
+
manifestEndpoint: loadInteractiveVenue.buildInteractiveManifestEndpoint({
|
|
84
|
+
accountId: vConfig.accountId,
|
|
85
|
+
venueId: vConfig.venueId,
|
|
86
|
+
assetStage: vConfig.assetStage
|
|
87
|
+
})
|
|
88
|
+
}) : auth.mode === "kiosk" ? createKioskManifestFetcher(vConfig.auth) : venueLoadingUtils.createFetchManifestJson(vConfig.auth);
|
|
45
89
|
const venueData = await venueLoadingUtils.getVenueDataFromUrls(vConfig, fetchJson, languagesToTry, fetchManifestJson);
|
|
46
90
|
const { accountId, venueId } = vConfig;
|
|
47
91
|
venueData.assetStage = vConfig.assetStage;
|
|
@@ -64,8 +108,7 @@ async function create(app, config) {
|
|
|
64
108
|
})();
|
|
65
109
|
}
|
|
66
110
|
app.bus.send("venueData/venueDataLoaded", { venueData });
|
|
67
|
-
|
|
68
|
-
return venueDataLoaded;
|
|
111
|
+
return venueData;
|
|
69
112
|
}
|
|
70
113
|
function getDefaultOrdinal(venueData) {
|
|
71
114
|
const defaultStructureId = getDefaultStructureId(venueData);
|
|
@@ -162,6 +205,7 @@ async function create(app, config) {
|
|
|
162
205
|
return features.map(enrichFeature);
|
|
163
206
|
};
|
|
164
207
|
app.bus.on("venueData/loadMap", async () => {
|
|
208
|
+
const thisMapDataLoaded = mapDataLoaded;
|
|
165
209
|
venueDataLoaded.then(async (venueData) => {
|
|
166
210
|
const badgesSpriteUrl = venueData.files.spritesheet;
|
|
167
211
|
const mapGlyphsUrl = venueData.files.glyphs;
|
|
@@ -194,8 +238,11 @@ async function create(app, config) {
|
|
|
194
238
|
tileServerAuthInfo: venueData.tileServerAuthInfo
|
|
195
239
|
// only defined for tile maps
|
|
196
240
|
};
|
|
197
|
-
|
|
241
|
+
thisMapDataLoaded.resolve(mapData);
|
|
198
242
|
app.bus.send("venueData/mapDataLoaded", mapData);
|
|
243
|
+
}).catch((err) => {
|
|
244
|
+
thisMapDataLoaded.reject(err);
|
|
245
|
+
log.error("Map data load failed", err);
|
|
199
246
|
});
|
|
200
247
|
});
|
|
201
248
|
const shouldDisplayPredicate = (building) => building.shouldDisplay == null || building.shouldDisplay;
|
|
@@ -296,7 +343,7 @@ async function create(app, config) {
|
|
|
296
343
|
mapDataLoaded.reject(new Error("loadNewVenue called - previous loading ignored"));
|
|
297
344
|
venueDataLoaded = new Zousan();
|
|
298
345
|
mapDataLoaded = new Zousan();
|
|
299
|
-
loadVenueData({ ...config, venueId, accountId, assetStage }, []).then(notifyState);
|
|
346
|
+
loadVenueData({ ...config, venueId, accountId, assetStage }, []).then(notifyState).catch((err) => log.error(err));
|
|
300
347
|
});
|
|
301
348
|
app.bus.on("venueData/getPoiImageUrl", ({ imageName, size }) => {
|
|
302
349
|
return `https://img.locuslabs.com/resize/${config.accountId}/png/transparent/${size}contain/poi/${imageName}`;
|
|
@@ -117,10 +117,12 @@ const getVenueDataFromUrls = async (vconfig, fetchJson, languagesToTry, fetchMan
|
|
|
117
117
|
const accountUrl = `https://${stageUrl}/accounts/${accountId}`;
|
|
118
118
|
const assetFormat = formatVersion || "v5";
|
|
119
119
|
const usesKioskAuth = vconfig.auth?.type === "kiosk";
|
|
120
|
+
const usesInteractiveAuth = vconfig.auth?.type === "interactive";
|
|
121
|
+
const usesSecureManifest = usesKioskAuth || usesInteractiveAuth;
|
|
120
122
|
if (usesKioskAuth && (typeof vconfig.auth.signedManifestUrl !== "string" || !vconfig.auth.signedManifestUrl.trim())) {
|
|
121
123
|
throw new Error("Kiosk auth requires a non-empty signedManifestUrl");
|
|
122
124
|
}
|
|
123
|
-
const venueList = !
|
|
125
|
+
const venueList = !usesSecureManifest && vconfig.dataFetch && globalThis[vconfig.dataFetch] && globalThis[vconfig.dataFetch].getFiles ? await globalThis[vconfig.dataFetch].getFiles(vconfig) : await fetchManifestJson(usesKioskAuth ? vconfig.auth.signedManifestUrl : `${accountUrl}/${assetFormat}.json`);
|
|
124
126
|
const venueCode = venueList[venueId] ? venueList[venueId].venueCode : null;
|
|
125
127
|
if (venueCode) {
|
|
126
128
|
const venuesWithSameVenueCode = Object.values(venueList).filter((v) => v.venueCode === venueCode);
|
|
@@ -138,7 +140,7 @@ const getVenueDataFromUrls = async (vconfig, fetchJson, languagesToTry, fetchMan
|
|
|
138
140
|
throw Error(`Attempt to access venue ${venueId} which is not within venue list: ${Object.keys(venueList)}`);
|
|
139
141
|
}
|
|
140
142
|
const files = venueList[venueId].files;
|
|
141
|
-
const fetchedData = !
|
|
143
|
+
const fetchedData = !usesSecureManifest && vconfig.dataFetch && globalThis[vconfig.dataFetch] && globalThis[vconfig.dataFetch].getVenueData ? await globalThis[vconfig.dataFetch].getVenueData(vconfig) : await fetchJson(files.venueData);
|
|
142
144
|
const venueData = fetchedData[venueId];
|
|
143
145
|
if (venueData.tileServerAuthInfo) {
|
|
144
146
|
embellishTilemapVenueData(venueData);
|
package/dist/package.json.js
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
var a="3.3.
|
|
1
|
+
var a="3.3.1165",e={version:a};export{e as default,a as version};
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
const E={MISCONFIGURED:"misconfigured",SESSION_EXPIRED:"sessionExpired",ACCESS_DENIED:"accessDenied",UNAVAILABLE:"unavailable",LINK_EXPIRED:"linkExpired",SERVICE_ERROR:"serviceError"};export{E as LOAD_FAILURE_REASON};
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
import{LOAD_FAILURE_REASON as t}from"./constants.js";const e=t=>"object"==typeof t&&null!==t&&"string"==typeof t.status,a={misconfigured:t.MISCONFIGURED,unavailable:t.UNAVAILABLE,callbackFailed:t.SERVICE_ERROR,signOutFailed:t.SERVICE_ERROR},s=(s,o)=>new Promise(R=>{let n=!1,E=!1;const r=t=>{n||(n=!0,c(),R(t))},i=e=>{if("authenticated"===e.status)r({ok:!0});else if("error"===e.status)r({ok:!1,reason:(s=e.reason,a[s]??t.SERVICE_ERROR)});else if("unauthenticated"===e.status&&!n&&!E){E=!0;try{o?.()}catch{r({ok:!1,reason:t.SERVICE_ERROR})}}var s},c=s.on("auth/stateChanged",t=>{e(t)&&i(t)});s.getFirst("auth/getState").then(a=>{void 0===a?r({ok:!1,reason:t.MISCONFIGURED}):e(a)?i(a):r({ok:!1,reason:t.SERVICE_ERROR})}).catch(()=>r({ok:!1,reason:t.SERVICE_ERROR}))});export{s as awaitAuthentication};
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
import{LOAD_FAILURE_REASON as t}from"./constants.js";import{awaitAuthentication as e}from"./interactiveAuthGate.js";class s extends Error{reason;constructor(t,e){super(`Venue load failed: ${t}`,e),this.name="VenueLoadFailedError",this.reason=t}}const n="https://assets-api.locuslabs.com/prod",a=({accountId:t,venueId:e,assetStage:s})=>{const a=`${n}/asset-manifest/accounts/${encodeURIComponent(t)}/venue/${encodeURIComponent(e)}`;return s?`${a}?assetStage=${encodeURIComponent(s)}&mode=interactive`:a},r=async({bus:n,fetch:a,manifestEndpoint:r})=>{const o=async t=>{throw await n.send("venueData/loadFailed",{reason:t}),new s(t)},c=await e(n,()=>n.send("auth/signIn"));if(!c.ok)return o(c.reason);const i=async e=>{try{return await e()}catch{return o(t.SERVICE_ERROR)}},u=await i(()=>n.getFirst("auth/getAccessToken",{forceRefresh:!0}));if("string"!=typeof u||""===u)return o(t.SESSION_EXPIRED);const E=await i(()=>a(r,{headers:{"x-api-key":u},redirect:"error"}));if(200!==E.status)return o((e=>{switch(e){case 401:return t.SESSION_EXPIRED;case 403:return t.ACCESS_DENIED;default:return t.SERVICE_ERROR}})(E.status));const R=await i(()=>E.json()),d=R?.data?.url;if("string"!=typeof d||""===d)return o(t.SERVICE_ERROR);const I=await i(()=>a(d));return 200!==I.status?o(403===I.status?t.LINK_EXPIRED:t.SERVICE_ERROR):i(()=>I.json())};export{n as MANIFEST_BASE_URL,s as VenueLoadFailedError,a as buildInteractiveManifestEndpoint,r as loadInteractiveManifest};
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
const e=e=>{if(void 0===e)return{mode:"public"};if("object"!=typeof(i=e)||null===i||Array.isArray(i))return{mode:"invalid"};var i;const t=Object.keys(e).length;if("kiosk"===e.type){const{signedManifestUrl:i}=e;return 2===t&&"string"==typeof i&&""!==i.trim()?{mode:"kiosk",signedManifestUrl:i}:{mode:"invalid"}}return"interactive"===e.type&&1===t?{mode:"interactive"}:{mode:"invalid"}};export{e as resolveAuth};
|
|
@@ -1 +1 @@
|
|
|
1
|
-
import*as e from"ramda";import t from"zousan";import{
|
|
1
|
+
import*as e from"ramda";import t from"zousan";import{LOAD_FAILURE_REASON as a}from"./constants.js";import{resolveVenueTimezone as n}from"./fixedOffsetZones.js";import{VenueLoadFailedError as s,loadInteractiveManifest as r,buildInteractiveManifestEndpoint as o}from"./loadInteractiveVenue.js";import{resolveAuth as u}from"./resolveAuth.js";import{fetchStyleDoc as i,normalizeCoords as d,createFetchManifestJson as c,getVenueDataFromUrls as l,buildStructures as p,createFetchJson as v,createFetchText as g}from"./venueLoadingUtils.js";async function f(f,m){const b=f.log.sublog("venueDataLoader");let h=new t,y=new t;const D=new t;f.bus.on("system/readywhenyouare",()=>D);const I=t=>t.defaultStructureId||e.path(["structureOrder",0],t)||e.path(["selectorOrder",0],t)||e.pipe(e.prop("structures"),Object.values,e.path([0,"id"]))(t),w=(e,t)=>e.split("").reduce((a,n,s)=>e.substring(0,s+1)===t.substring(0,s+1)?s:a,-1)+1,S=e=>((e,t)=>Object.values(e.venueList).filter(e=>e.locale===t).sort((t,a)=>w(a.id,e.id)-w(t.id,e.id)).at(0))(e,"en")?.id;async function O(t,n){const i=h,d=y,m=u(t.auth);try{const a=await async function(t,a,n){if("invalid"===n.mode)throw f.bus.send("venueData/loadFailed",{reason:"misconfigured"}),new s("misconfigured");const u=v(),i=g(),d="interactive"===n.mode?()=>r({bus:f.bus,fetch:globalThis.fetch,manifestEndpoint:o({accountId:t.accountId,venueId:t.venueId,assetStage:t.assetStage})}):"kiosk"===n.mode?function(e){let t=!1;const a=c(e,async e=>{t=!0,await f.bus.send("venueData/authError",{reason:"signedManifestForbidden",status:e}),await f.bus.send("venueData/loadFailed",{reason:"linkExpired"})});return async e=>{try{return await a(e)}catch(e){throw t?new s("linkExpired",{cause:e}):e}}}(t.auth):c(t.auth),m=await l(t,u,a,d),{accountId:b,venueId:h}=t;m.assetStage=t.assetStage,m.defaultOrdinal=m.defaultOrdinal||function(t){const a=I(t),n=Object.values(t.structures).find(e.propEq(a,"id"));return n.levels[n.defaultLevelId].ordinal}(m),m.structures=p(m),m.baseVenueId=S(m),m.getTranslatedContentPath=e=>`https://content.locuslabs.com/${m.category}/${e}/${h}/${b}`,m.fetchJson=u,m.fetchText=i,f.config.debug&&f.env.isBrowser&&(window._venueData=m);m.queueTypes&&(m.securityQueueTypes=(()=>{const e=m.queueTypes.find(e=>"SecurityLane"===e.id);return e?e.subtypes.map(e=>e.id):[]})());return f.bus.send("venueData/venueDataLoaded",{venueData:m}),m}(t,n,m);return i.resolve(a),i}catch(e){throw e instanceof s||f.bus.send("venueData/loadFailed",{reason:a.SERVICE_ERROR}),i.reject(e),d.reject(e),e}}function T(e){const t={id:"venueDataLoader"};return e.id!==m.venueId&&(t.vid=e.id),t.lang=f.i18n().language,"prod"!==e.assetStage&&(t.stage=e.assetStage),f.bus.send("deepLinking/notifyState",t),e}f.bus.on("debugTools/fileDrop",async({file:e,content:t})=>{if("application/json"===e.type){const e=JSON.parse(t);if(e.basemap&&e["basemap.venue"])return n=JSON.parse(t),void f.bus.send("map/replaceTheme",{theme:n});if(e.metadata&&e.metadata["mapbox:type"])return a=t,void f.bus.send("map/replaceStyle",{styleSrc:a})}var a,n});async function j(e){const t={...e},a=await f.bus.get("poi/getAll");return Object.values(t).forEach(e=>e.filter(e=>"poi"===e.properties.aiLayer&&"Point"===e.geometry.type).forEach(e=>{const t=a[e.properties.id];t?t.mapLabel?e.properties.text=t.mapLabel:!1!==m.copyPOINamesToMap&&(e.properties.text=function(e){let t=e.name;return m.poiMapNameXForm?(Object.keys(m.poiMapNameXForm).filter(t=>((e,t)=>e===t||0===e.indexOf(t+"."))(e.category,t)).forEach(e=>{m.poiMapNameXForm[e].forEach(e=>t=t.replace(new RegExp(e.replace),e.with))}),t):t}(t)):b.warn(`Unknown poi in style: ${e.properties.id}`)})),t}async function P(a){return e.pipe(e.prop("structures"),e.map(e.prop("levels")),e.chain(e.keys),e.prepend(a.id),e.map(e=>a.files.geoJson.replace("${geoJsonId}",e)),e.map(a.fetchJson),e.map(e.andThen(e=>[e.id,E(e.id,e.features,a.id,a.structures)])),e=>t.all(e),e.andThen(e.fromPairs))(a)}const E=(t,a,n,s)=>{const r=t.replace(/-[^-]*$/,""),o=r===n?"landscape-background":`ordinal: ${s.find(e.hasPath(["levels",t])).levels[t].ordinal}`;return a.map(a=>{var s,u,i;return s="properties",u={venueId:n,structureId:r,ordinalId:o,levelId:t},i=a,a=e.over(e.lensProp(s),t=>e.mergeRight(t||{},u),i),a=e.assoc("id",a.properties.subid,a)})};f.bus.on("venueData/loadMap",async()=>{const e=y;h.then(async t=>{const a=t.files.spritesheet,n=t.files.glyphs,{id:s,bounds:r,structures:o,venueCenter:u,venueRadius:d,defaultOrdinal:c}=t,[l,p,v]=await Promise.all([i(t.baseVenueId,t.category),t.fetchJson(t.files.theme),t.venueList[s].mapTokens?Promise.resolve({[s]:[]}):P(t).then(j)]),g={mapFeatures:v,mapStyleSource:l,mapTheme:p,badgesSpriteUrl:a,mapGlyphsUrl:n,structures:o,defaultOrdinal:c,venueBounds:{n:r.ne.lat,s:r.sw.lat,e:r.ne.lng,w:r.sw.lng},venueId:s,venueCenter:u,venueRadius:d,accountId:m.accountId,secure:void 0!==m.auth,tileServerAuthInfo:t.tileServerAuthInfo};e.resolve(g),f.bus.send("venueData/mapDataLoaded",g)}).catch(t=>{e.reject(t),b.error("Map data load failed",t)})});const L=e=>null==e.shouldDisplay||e.shouldDisplay;f.bus.on("venueData/loadBuildingSelectorData",()=>h.then(async t=>{const a={buildings:t.structures.filter(L).map(e.evolve({levels:e.pipe(e.values,e.sortWith([e.descend(e.prop("ordinal"))]))})),structureOrder:t.structureOrder,selectorOrder:t.selectorOrder};return f.bus.send("venueData/buildingSelectorDataLoaded",a),a})),f.bus.on("venueData/normalizeCoords",({coords:e})=>h.then(t=>d(e,t.bounds)));const V={edges:[],nodes:[]};f.bus.on("venueData/loadNavGraph",async()=>h.then(async e=>{const t={navGraphData:e.files.nav?await e.fetchJson(e.files.nav):V,structures:e.structures};return f.bus.send("venueData/navGraphLoaded",t),t})),f.bus.on("venueData/loadPoiData",async()=>h.then(async e=>{const t=m.useOldDataModel?e.files.poisOld||e.files.pois:e.files.pois||e.files.poisOld;if(t){const a=await e.fetchJson(t);f.bus.send("venueData/poiDataLoaded",{pois:a,structures:e.structures})}})),f.bus.on("venueData/getVenueCenter",async()=>h.then(async e=>({lat:e.venueCenter[0],lng:e.venueCenter[1],ordinal:0}))),f.bus.on("venueData/getContentUrl",({type:e,name:t=""})=>h.then(a=>a.files[e]+t)),f.bus.on("venueData/getFloorIdToNameMap",()=>h.then(e.pipe(e.prop("structures"),e.map(e.prop("levels")),e.chain(e.values),e.map(e.props(["id","name"])),e.fromPairs))),f.bus.on("venueData/getFloorIdName",({floorId:t})=>h.then(async a=>{const n=e.pipe(e.values,e.find(e.hasPath(["levels",t])))(a.structures);return n?{structureId:n.id,structureName:n.name,floorName:n.levels[t].name}:null}));const N=(t,a)=>()=>h.then(e.pipe(e.prop(t),e.defaultTo(a)));function $(e,t){const a=C(t);if(a.includes(e))return e;const n=e.split("-").slice(1).join("-");return a.find(e=>new RegExp(`${t.baseVenueId}.?.?-${n}`).test(e))}f.bus.on("venueData/getVenueData",()=>h),f.bus.on("venueData/getVenueName",N("name")),f.bus.on("venueData/getVenueCategory",N("category")),f.bus.on("venueData/getVenueTimezone",()=>h.then(e=>n(e.tz))),f.bus.on("venueData/getAccountId",()=>m.accountId),f.bus.on("venueData/getVenueId",N("id")),f.bus.on("venueData/getPositioningSupported",N("positioningSupported")),f.bus.on("venueData/getStructures",N("structures")),f.bus.on("venueData/loadNewVenue",async({venueId:e,accountId:a,assetStage:n=m.assetStage})=>{h.reject(new Error("loadNewVenue called - previous loading ignored")),y.reject(new Error("loadNewVenue called - previous loading ignored")),h=new t,y=new t,O({...m,venueId:e,accountId:a,assetStage:n},[]).then(T).catch(e=>b.error(e))}),f.bus.on("venueData/getPoiImageUrl",({imageName:e,size:t})=>`https://img.locuslabs.com/resize/${m.accountId}/png/transparent/${t}contain/poi/${e}`),f.bus.on("venueData/getDistributionId",()=>h.then(e=>`${e.baseVenueId}-${m.accountId}`)),f.bus.on("venueData/getCustomKeywords",()=>h.then(e=>{const t=m.useOldDataModel&&e.files.searchOld||e.files.search;return e.fetchJson(t)})),f.bus.on("venueData/isGrabEnabled",N("enableGrab")),f.bus.on("venueData/getGrabPoiIds",N("grabPoiIds",[])),f.bus.on("venueData/getAssetsTimestamp",N("version")),f.bus.on("venueData/getTranslatedFloorId",async({floorId:e})=>h.then(t=>$(e,t)));const C=e=>e.structures.reduce((e,t)=>e.concat(Object.keys(t.levels)),[]);f.bus.on("venueData/getQueueTypes",()=>h.then(e=>{const t=["tsapre","clear","globalEntry"];return e.queueTypes?e.queueTypes.reduce((e,a)=>{const{id:n,subtypes:s}=a,r=s.map(e=>{const a=t.includes(e.id)&&`security-logo-${e.id.toLowerCase()}`;return{...e,imageId:a}});return e[n]=r,e},{}):{}}));return{init:async()=>{const e=m.deepLinkProps||{},t="undefined"==typeof window?null:new URLSearchParams(window.location.search).get("lang"),a=e.lang??t,n="undefined"==typeof window?[]:[...navigator.languages],s=a?[]:n,r=e.vid||m.venueId,o=m.useDynamicUrlParams&&e.stage?e.stage:m.assetStage,u=e.accountId||("alpha"===o?"A1VPTJKREFJWX5":m.accountId);O({...m,venueId:r,accountId:u,assetStage:o},s).then(T).catch(e=>b.error("Initial venue load failed",e)).finally(()=>D.resolve(!0))},runTest:async({testRoutine:e,reset:a=!1,venueData:n=null})=>{let s,r;return(a||n)&&(h=new t,y=new t),n&&(h=t.resolve(n)),await e(),h.v&&(s=await h),y.v&&(r=await y),{venueDataObj:s,mapDataObj:r}},internal:{calculateBaseVenueId:S,getDefaultStructureId:I,getTranslatedFloorId:$,setConfigProperty:(e,t)=>{m[e]=t}}}}export{f as create};
|
|
@@ -1 +1 @@
|
|
|
1
|
-
import*as e from"ramda";import{findBoundsOfCoordinates as t}from"../../../src/utils/bounds.js";const n=async(e,t)=>e?fetch(t,{headers:{Authorization:e}}):fetch(t),s=e=>t=>n(e,t).then(e=>e.json()),a=e=>t=>n(e,t).then(e=>e.text()),o=(e,t)=>async n=>{const s=await fetch(n);if("kiosk"===e?.type&&403===s.status)throw await(t?.(s.status)),new Error(`Kiosk signed manifest request was forbidden (${s.status}); the signed URL may have expired.`);return s.json()},r=(e,t)=>n(null,`https://style.maps.atrius.com/v2/style?venueId=${e}&category=${t}`).then(e=>e.json()).then(e=>e.data),
|
|
1
|
+
import*as e from"ramda";import{findBoundsOfCoordinates as t}from"../../../src/utils/bounds.js";const n=async(e,t)=>e?fetch(t,{headers:{Authorization:e}}):fetch(t),s=e=>t=>n(e,t).then(e=>e.json()),a=e=>t=>n(e,t).then(e=>e.text()),o=(e,t)=>async n=>{const s=await fetch(n);if("kiosk"===e?.type&&403===s.status)throw await(t?.(s.status)),new Error(`Kiosk signed manifest request was forbidden (${s.status}); the signed URL may have expired.`);return s.json()},r=(e,t)=>n(null,`https://style.maps.atrius.com/v2/style?venueId=${e}&category=${t}`).then(e=>e.json()).then(e=>e.data),i=e=>`https://api.content.locuslabs.com/${e}`,l=e=>{if("string"!=typeof e)return!1;try{const t=new URL(e).searchParams;return["Expires","Signature","Key-Pair-Id"].every(e=>t.has(e))}catch{return!1}},u=(t,n,s,a,o)=>e.mapObjIndexed((e,n)=>((e,t,n,s,a,o)=>l(e)?e:"theme"===o||"style"===o?`${i(n)}/${t}/${o}/${a}/${s}/${o}.json`:e.replace(/https:\/\/content.locuslabs.com/gi,i(n)))(e,t,s,a,o,n),n),c=[["zh-CN","zh-SG","zh-Hans"],["zh-TW","zh-HK","zh-MO","zh-Hant"]],d=e=>{const t=(e=>{const t=c.find(t=>t.includes(e));return t?t.filter(t=>t!==e):[]})(e),n=e.slice(0,2);return[t=>t===e,e=>t.includes(e),e=>e.slice(0,2)===n]},h=(e,t,n=null)=>{for(const s of d(t)){if(n&&s(n.locale))return n;const t=e.find(e=>s(e.locale));if(t)return t}return null},g=async(e,t,n,s=t)=>{const a={alpha:"alpha-a.locuslabs.com",beta:"beta-a.locuslabs.com",gamma:"gamma-a.locuslabs.com",prod:"a.locuslabs.com"},{assetStage:o,accountId:r,formatVersion:i}=e;let{venueId:l}=e;const c=`https://${a[o]||a.prod}/accounts/${r}`,d=i||"v5",g="kiosk"===e.auth?.type,f="interactive"===e.auth?.type,m=g||f;if(g&&("string"!=typeof e.auth.signedManifestUrl||!e.auth.signedManifestUrl.trim()))throw new Error("Kiosk auth requires a non-empty signedManifestUrl");const p=!m&&e.dataFetch&&globalThis[e.dataFetch]&&globalThis[e.dataFetch].getFiles?await globalThis[e.dataFetch].getFiles(e):await s(g?e.auth.signedManifestUrl:`${c}/${d}.json`),b=p[l]?p[l].venueCode:null;if(b){const t=Object.values(p).filter(e=>e.venueCode===b),s=p[l];for(const a of n){const n=h(t,a,s);if(n){l=n.id,e.venueId=`${l}`;break}}}if(!p[l])throw Error(`Attempt to access venue ${l} which is not within venue list: ${Object.keys(p)}`);const v=p[l].files,y=(!m&&e.dataFetch&&globalThis[e.dataFetch]&&globalThis[e.dataFetch].getVenueData?await globalThis[e.dataFetch].getVenueData(e):await t(v.venueData))[l];y.tileServerAuthInfo&&function(e){const t={defaultOrdinal:0,defaultStructureId:"singleBuilding",formatVersion:"v5",structures:{singleBuilding:{name:"singleBuilding",boundsPolygon:[],defaultLevelId:"singleLevel",id:"singleBuilding",levels:{singleLevel:{boundsPolygon:[],clfloor:0,details:"",id:"singleLevel",name:"singleLevel",ordinal:0}}}},structureOrder:["singleBuilding"]};for(const n in t)e[n]=t[n]}(y),y.venueList=p;const $=(e=>{const t=e.deepLinkProps?e.deepLinkProps.contentStage:null;return"alpha"===t||"beta"===t||"prod"===t?t:null})(e);return y.files=$?u(y.category,v,$,r,l):v,y},f=e=>{const{structureOrder:n,structures:s}=e;return n.map(e=>{const n=s[e];Object.values(n.levels).forEach(e=>e.bounds=t(e.boundsPolygon));const a=t(n.boundsPolygon);return{...n,bounds:a}})};const m=([e,t,...n])=>[t,e,...n],p=(e,t)=>{return!e||!Array.isArray(e)||e.length<1?e:(n=e[0][0],s=t.ne.lng,a=t.sw.lng,n>=Math.min(s,a)&&n<=Math.max(s,a)?e:e.map(m));var n,s,a};export{f as buildStructures,s as createFetchJson,o as createFetchManifestJson,a as createFetchText,r as fetchStyleDoc,h as findVenueForLocale,g as getVenueDataFromUrls,p as normalizeCoords};
|
package/dist/src/utils/bounds.js
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
import{
|
|
1
|
+
import{path as t,identity as n,prop as i}from"ramda";function r(i){return m(i.map(t(["position","lat"])).filter(n),i.map(t(["position","lng"])).filter(n))}function a(t){return m(t.map(i(0)).filter(n),t.map(i(1)).filter(n))}function m(t,n){return{n:Math.max(...t),s:Math.min(...t),e:Math.max(...n),w:Math.min(...n)}}export{a as findBoundsOfCoordinates,r as findBoundsOfWaypoints};
|
|
@@ -181,6 +181,19 @@ export type BusAuthState = {
|
|
|
181
181
|
status: 'error';
|
|
182
182
|
reason: BusAuthErrorReason;
|
|
183
183
|
};
|
|
184
|
+
export type BusAuthFormErrorCode = 'invalidCredentials' | 'invalidCode' | 'invalidPassword' | 'rateLimited' | 'unsupportedChallenge' | 'unavailable';
|
|
185
|
+
export type BusAuthFormResult = {
|
|
186
|
+
ok: true;
|
|
187
|
+
} | {
|
|
188
|
+
ok: false;
|
|
189
|
+
error: BusAuthFormErrorCode;
|
|
190
|
+
};
|
|
191
|
+
export type BusAuthCredentialController = {
|
|
192
|
+
signInWithRedirect: () => Promise<BusAuthFormResult>;
|
|
193
|
+
signIn: (email: string, password: string) => Promise<BusAuthFormResult>;
|
|
194
|
+
resetPassword: (email: string) => Promise<BusAuthFormResult>;
|
|
195
|
+
confirmNewPassword: (email: string, code: string, newPassword: string) => Promise<BusAuthFormResult>;
|
|
196
|
+
};
|
|
184
197
|
export type BusVenueLoadFailureReason = 'misconfigured' | 'sessionExpired' | 'accessDenied' | 'unavailable' | 'linkExpired' | 'serviceError';
|
|
185
198
|
export interface BusEventMap {
|
|
186
199
|
'map/updateRoutePreviewInsets': BusEventDefinition<{
|
|
@@ -426,6 +439,7 @@ export interface BusEventMap {
|
|
|
426
439
|
'auth/stateChanged': BusEventDefinition<BusAuthState, unknown>;
|
|
427
440
|
'auth/signIn': BusEventDefinition<undefined, unknown>;
|
|
428
441
|
'auth/signOut': BusEventDefinition<undefined, unknown>;
|
|
442
|
+
'auth/getCredentialController': BusEventDefinition<undefined, BusAuthCredentialController>;
|
|
429
443
|
'auth/getAccessToken': BusEventDefinition<{
|
|
430
444
|
forceRefresh?: boolean;
|
|
431
445
|
} | undefined, string | null>;
|
package/package.json
CHANGED