atriusmaps-node-sdk 3.3.31 → 3.3.225

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.
Files changed (122) hide show
  1. package/README.md +19 -1
  2. package/dist/cjs/_virtual/_empty_module_placeholder.js +2 -2
  3. package/dist/cjs/deploy/prepareSDKConfig.js +152 -2
  4. package/dist/cjs/nodesdk/nodeEntry.js +109 -0
  5. package/dist/cjs/package.json.js +190 -7
  6. package/dist/cjs/plugins/clientAPI/src/clientAPI.js +11 -2
  7. package/dist/cjs/plugins/dynamicPois/src/dynamicPois.js +218 -21
  8. package/dist/cjs/plugins/poiDataManager/src/poiDataManager.js +292 -21
  9. package/dist/cjs/plugins/sdkServer/src/sdkHeadless.js +99 -20
  10. package/dist/cjs/plugins/sdkServer/src/sdkServer.js +219 -2
  11. package/dist/cjs/plugins/sdkServer/src/util.js +16 -3
  12. package/dist/cjs/plugins/searchService/src/poiSearch.js +57 -19
  13. package/dist/cjs/plugins/searchService/src/searchService.js +246 -21
  14. package/dist/cjs/plugins/searchService/src/searchTypeahead.js +60 -3
  15. package/dist/cjs/plugins/searchService/src/utils.js +23 -4
  16. package/dist/cjs/plugins/venueDataLoader/src/venueDataLoader.js +472 -23
  17. package/dist/cjs/plugins/venueDataLoader/src/venueLoadingUtils.js +191 -23
  18. package/dist/cjs/plugins/wayfinder/src/findRoute.js +147 -19
  19. package/dist/cjs/plugins/wayfinder/src/minPriorityQueue.js +88 -2
  20. package/dist/cjs/plugins/wayfinder/src/navGraph.js +393 -5
  21. package/dist/cjs/plugins/wayfinder/src/navGraphDebug.js +110 -20
  22. package/dist/cjs/plugins/wayfinder/src/segmentBadges.js +28 -2
  23. package/dist/cjs/plugins/wayfinder/src/segmentBuilder.js +257 -19
  24. package/dist/cjs/plugins/wayfinder/src/segmentCategories.js +29 -2
  25. package/dist/cjs/plugins/wayfinder/src/stepBuilder.js +238 -3
  26. package/dist/cjs/plugins/wayfinder/src/wayfinder.js +597 -22
  27. package/dist/cjs/src/app.js +191 -25
  28. package/dist/cjs/src/configs/postproc-mol-url-parms.js +58 -2
  29. package/dist/cjs/src/configs/postproc-stateTracking.js +53 -19
  30. package/dist/cjs/src/controller.js +43 -4
  31. package/dist/cjs/src/debugTools.js +128 -23
  32. package/dist/cjs/src/env.js +17 -2
  33. package/dist/cjs/src/extModules/bustle.js +128 -4
  34. package/dist/cjs/src/extModules/flexapi/src/help.js +23 -4
  35. package/dist/cjs/src/extModules/flexapi/src/index.js +65 -4
  36. package/dist/cjs/src/extModules/flexapi/src/validate.js +133 -5
  37. package/dist/cjs/src/extModules/geohasher.js +90 -3
  38. package/dist/cjs/src/extModules/log.js +69 -2
  39. package/dist/cjs/src/historyManager.js +29 -2
  40. package/dist/cjs/src/utils/bounds.js +22 -4
  41. package/dist/cjs/src/utils/buildStructureLookup.js +31 -19
  42. package/dist/cjs/src/utils/configUtils.js +71 -3
  43. package/dist/cjs/src/utils/dom.js +48 -5
  44. package/dist/cjs/src/utils/funcs.js +30 -7
  45. package/dist/cjs/src/utils/geodesy.js +35 -3
  46. package/dist/cjs/src/utils/geom.js +212 -25
  47. package/dist/cjs/src/utils/i18n.js +69 -5
  48. package/dist/cjs/src/utils/observable.js +73 -2
  49. package/dist/cjs/src/utils/rand.js +82 -3
  50. package/dist/nodesdk/nodeEntry.js +1 -0
  51. package/dist/package.json.js +1 -0
  52. package/dist/plugins/dynamicPois/src/dynamicPois.js +1 -0
  53. package/dist/plugins/sdkServer/src/sdkHeadless.js +1 -0
  54. package/{lib → dist}/plugins/sdkServer/src/sdkServer.js +1 -1
  55. package/dist/plugins/searchService/src/poiSearch.js +1 -0
  56. package/dist/plugins/searchService/src/searchService.js +1 -0
  57. package/dist/plugins/venueDataLoader/src/venueDataLoader.js +1 -0
  58. package/dist/plugins/venueDataLoader/src/venueLoadingUtils.js +1 -0
  59. package/dist/plugins/wayfinder/src/navGraph.js +1 -0
  60. package/{lib → dist}/plugins/wayfinder/src/segmentBuilder.js +1 -1
  61. package/dist/plugins/wayfinder/src/stepBuilder.js +1 -0
  62. package/dist/plugins/wayfinder/src/wayfinder.js +1 -0
  63. package/dist/src/app.js +1 -0
  64. package/dist/src/configs/postproc-mol-url-parms.js +1 -0
  65. package/{lib → dist}/src/configs/sdkHeadless.json.js +1 -1
  66. package/dist/src/extModules/bustle.js +1 -0
  67. package/dist/src/extModules/log.js +1 -0
  68. package/dist/src/utils/funcs.js +1 -0
  69. package/dist/src/utils/geom.js +1 -0
  70. package/dist/src/utils/i18n.js +1 -0
  71. package/package.json +17 -9
  72. package/config/rollup.config.cjs.js +0 -31
  73. package/dist/cjs/deploy/nodeEntry.js +0 -15
  74. package/dist/cjs/src/auth/Auth.js +0 -23
  75. package/lib/deploy/nodeEntry.js +0 -1
  76. package/lib/package.json.js +0 -1
  77. package/lib/plugins/dynamicPois/src/dynamicPois.js +0 -1
  78. package/lib/plugins/sdkServer/src/sdkHeadless.js +0 -1
  79. package/lib/plugins/searchService/src/poiSearch.js +0 -1
  80. package/lib/plugins/searchService/src/searchService.js +0 -1
  81. package/lib/plugins/venueDataLoader/src/venueDataLoader.js +0 -1
  82. package/lib/plugins/venueDataLoader/src/venueLoadingUtils.js +0 -1
  83. package/lib/plugins/wayfinder/src/navGraph.js +0 -1
  84. package/lib/plugins/wayfinder/src/stepBuilder.js +0 -1
  85. package/lib/plugins/wayfinder/src/wayfinder.js +0 -1
  86. package/lib/src/app.js +0 -1
  87. package/lib/src/auth/Auth.js +0 -1
  88. package/lib/src/configs/postproc-mol-url-parms.js +0 -1
  89. package/lib/src/configs/sdkHeadless.json +0 -28
  90. package/lib/src/extModules/bustle.js +0 -1
  91. package/lib/src/extModules/log.js +0 -1
  92. package/lib/src/utils/funcs.js +0 -1
  93. package/lib/src/utils/geom.js +0 -1
  94. package/lib/src/utils/i18n.js +0 -1
  95. /package/{lib → dist}/_virtual/_empty_module_placeholder.js +0 -0
  96. /package/{lib → dist}/deploy/prepareSDKConfig.js +0 -0
  97. /package/{lib → dist}/plugins/clientAPI/src/clientAPI.js +0 -0
  98. /package/{lib → dist}/plugins/poiDataManager/src/poiDataManager.js +0 -0
  99. /package/{lib → dist}/plugins/sdkServer/src/util.js +0 -0
  100. /package/{lib → dist}/plugins/searchService/src/searchTypeahead.js +0 -0
  101. /package/{lib → dist}/plugins/searchService/src/utils.js +0 -0
  102. /package/{lib → dist}/plugins/wayfinder/src/findRoute.js +0 -0
  103. /package/{lib → dist}/plugins/wayfinder/src/minPriorityQueue.js +0 -0
  104. /package/{lib → dist}/plugins/wayfinder/src/navGraphDebug.js +0 -0
  105. /package/{lib → dist}/plugins/wayfinder/src/segmentBadges.js +0 -0
  106. /package/{lib → dist}/plugins/wayfinder/src/segmentCategories.js +0 -0
  107. /package/{lib → dist}/src/configs/postproc-stateTracking.js +0 -0
  108. /package/{lib → dist}/src/controller.js +0 -0
  109. /package/{lib → dist}/src/debugTools.js +0 -0
  110. /package/{lib → dist}/src/env.js +0 -0
  111. /package/{lib → dist}/src/extModules/flexapi/src/help.js +0 -0
  112. /package/{lib → dist}/src/extModules/flexapi/src/index.js +0 -0
  113. /package/{lib → dist}/src/extModules/flexapi/src/validate.js +0 -0
  114. /package/{lib → dist}/src/extModules/geohasher.js +0 -0
  115. /package/{lib → dist}/src/historyManager.js +0 -0
  116. /package/{lib → dist}/src/utils/bounds.js +0 -0
  117. /package/{lib → dist}/src/utils/buildStructureLookup.js +0 -0
  118. /package/{lib → dist}/src/utils/configUtils.js +0 -0
  119. /package/{lib → dist}/src/utils/dom.js +0 -0
  120. /package/{lib → dist}/src/utils/geodesy.js +0 -0
  121. /package/{lib → dist}/src/utils/observable.js +0 -0
  122. /package/{lib → dist}/src/utils/rand.js +0 -0
@@ -2,32 +2,229 @@
2
2
 
3
3
  Object.defineProperty(exports, '__esModule', { value: true });
4
4
 
5
- var e = require('ramda');
6
- var a$1 = require('zousan');
5
+ var R = require('ramda');
6
+ var Zousan = require('zousan');
7
7
 
8
8
  function _interopDefaultLegacy (e) { return e && typeof e === 'object' && 'default' in e ? e : { 'default': e }; }
9
9
 
10
10
  function _interopNamespace(e) {
11
- if (e && e.__esModule) return e;
12
- var n = Object.create(null);
13
- if (e) {
14
- Object.keys(e).forEach(function (k) {
15
- if (k !== 'default') {
16
- var d = Object.getOwnPropertyDescriptor(e, k);
17
- Object.defineProperty(n, k, d.get ? d : {
18
- enumerable: true,
19
- get: function () { return e[k]; }
20
- });
21
- }
22
- });
23
- }
24
- n["default"] = e;
25
- return Object.freeze(n);
11
+ if (e && e.__esModule) return e;
12
+ var n = Object.create(null);
13
+ if (e) {
14
+ Object.keys(e).forEach(function (k) {
15
+ if (k !== 'default') {
16
+ var d = Object.getOwnPropertyDescriptor(e, k);
17
+ Object.defineProperty(n, k, d.get ? d : {
18
+ enumerable: true,
19
+ get: function () { return e[k]; }
20
+ });
21
+ }
22
+ });
23
+ }
24
+ n["default"] = e;
25
+ return Object.freeze(n);
26
26
  }
27
27
 
28
- var e__namespace = /*#__PURE__*/_interopNamespace(e);
29
- var a__default = /*#__PURE__*/_interopDefaultLegacy(a$1);
28
+ var R__namespace = /*#__PURE__*/_interopNamespace(R);
29
+ var Zousan__default = /*#__PURE__*/_interopDefaultLegacy(Zousan);
30
30
 
31
- function a(a,s){let n=new a__default["default"];const i=new a__default["default"];function o(...e){r(...e),p(...e);}function r(t,s){const n=e__namespace.pipe(e__namespace.filter((e=>"parking"===e.category)),e__namespace.map((a=>{const s=a.dynamicAttributes;if(!s)throw Error(`No dynamicAttributes defined for parking POI ${a.poiId}`);return {...(t-a.timestamp)/1e3<s["parking.timeToLive"]?e__namespace.pick(["lotStatus","rateDay","rateHour","timeIsReal","timeToTerminal1","timeToTerminal2"],a):{lotStatus:s["parking.default"],rateDay:"$ -",rateHour:"$ -",timeIsReal:!1},lastUpdated:a.timestamp,lotName:a.lotName}})))(s);a.bus.send("poi/setDynamicData",{plugin:"parking",idValuesMap:n});}function u(t,s){const n=e__namespace.pipe(e__namespace.map((e=>[e.poiId,l(t,e)])),e__namespace.fromPairs)(s);a.bus.send("poi/setDynamicData",{plugin:"security",idValuesMap:n});}const l=(e,t)=>({queueTime:t.queueTime,isTemporarilyClosed:t.isTemporarilyClosed,timeIsReal:!t.isQueueTimeDefault&&t.expiration>e,lastUpdated:e});function p(t,s){const n=["dynamicData","openClosed"],i=e__namespace.filter(e__namespace.hasPath(n),s),o=e__namespace.map(e__namespace.path(n),i);if(!e__namespace.all(e__namespace.both(e__namespace.has("isOpen"),e__namespace.has("expiration")),e__namespace.values(o)))throw Error("Open Closed poi status is malformed.");{const s=e__namespace.pipe(e__namespace.prop("expiration"),e__namespace.lt(t)),n=e__namespace.filter(s,o);a.bus.send("poi/setDynamicData",{plugin:"open-closed-status",idValuesMap:n});}}return a.bus.on("venueData/venueDataLoaded",(({venueData:e})=>{n.v?n=a__default["default"].resolve(e):n.resolve(e);})),a.bus.on("sdk/readyWhenYouAre",(()=>i)),{init:async()=>{const e=s.urlBase||"https://rest.locuslabs.com/v3",t=s.urlBaseV1||s.urlBase||"https://rest.locuslabs.com/v1",r=a.config.plugins.venueDataLoader.accountId;async function l(){return n.then((e=>{let a=`${t}/venue/${e.id}/account/${r}/get-all-dynamic-pois/`;return (t.startsWith("./")||t.endsWith(".json"))&&(a=t),a}))}const p=async()=>n.then((t=>e.startsWith("./")||e.endsWith(".json")?e:`${e}/venueId/${t.id}/accountId/${r}/get-dynamic-queue-times/`)),c=async()=>{Promise.all([l().then(fetch).then((e=>e.json())).then((e=>o(Date.now(),e))),p().then(fetch).then((e=>e.json())).then((e=>u(Date.now(),e)))]).then((()=>i.resolve(!0))).catch((e=>{console.error(e),i.resolve(!0);}));},m=await a.bus.get("venueData/getQueueTypes");m.SecurityLane&&m.SecurityLane.length?n.then(c).then((()=>setInterval(c,3e4))):i.resolve(!0);},internal:{processSecurityWaitTimes:u,processParkingPOIS:r,processOpenClosedPois:p,processDynamicPois:o}}}
31
+ /*
32
+ This service obtains dynamic POI data from our own backend REST API service.
33
+ Currently it drives dynamic data for Security Wait Times and Parking Status.
32
34
 
33
- exports.create = a;
35
+ Configuration Properties Recognized:
36
+ urlBase : to override the REST url base. i.e. for alpha use 'https://rest-alpha.locuslabs.com/v1'. You can also reference
37
+ a local file, such as './testDynamicPois.json'
38
+
39
+ Values defined by 'parking':
40
+ lotName: Name of the lot for this POI, such as 'East'
41
+ lotStatus: returns either 'Closed' or 'Open'
42
+ timeIsReal: If true, the time here is live and valid, else its a default fallback
43
+ rateDay: Daily Rate expressed in English: '$17 per day'
44
+ rateHour: Hourly Rate expressed in English: '$5 per hour'
45
+ timeToTerminal1: Time in walking or by shuttle to Terminal 1 : 'Shuttle 5-7'
46
+ timeToTerminal2: Time in nwalking or by shuttle to Terminal 2 : 'Walking 5-10'
47
+ lastUpdated: timestamp of last update (as sent by server)
48
+
49
+ Values defined by 'security'
50
+ queueTime: Estimated Time in queue (in minutes) : i.e. 45
51
+ isTemporarilyClosed: If true, this line is closed - else it is open
52
+ timeIsReal: If true, the time here is live and valid, else its a default fallback
53
+ lastUpdated: timestamp of last update (as sent by server)
54
+ */
55
+
56
+ const REFRESH_FREQUENCY = 1000 * 30; // every 30 seconds
57
+
58
+ function create (app, config) {
59
+ let dataLoadedProm = new Zousan__default["default"]();
60
+ const dynamicDataNotPending = new Zousan__default["default"]();
61
+
62
+ const init = async () => {
63
+ const urlBaseNew = config.urlBase || 'https://rest.locuslabs.com/v3';
64
+ const urlBase = config.urlBaseV1 || config.urlBase || 'https://rest.locuslabs.com/v1';
65
+ const accountId = app.config.plugins.venueDataLoader.accountId;
66
+
67
+ async function getURL () {
68
+ return dataLoadedProm
69
+ .then(venueData => {
70
+ let url = `${urlBase}/venue/${venueData.id}/account/${accountId}/get-all-dynamic-pois/`;
71
+ if (urlBase.startsWith('./') || urlBase.endsWith('.json'))
72
+ url = urlBase;
73
+ return url
74
+ })
75
+ }
76
+
77
+ /*
78
+ API URL: https://gitlab.com/locuslabs/core-data-team/rest-api/-/blob/develop/v3/docs/REST%20API%20v3.postman_collection.json
79
+ */
80
+ const getWaitTimesUrl = async () => dataLoadedProm.then(venueData =>
81
+ (urlBaseNew.startsWith('./') || urlBaseNew.endsWith('.json'))
82
+ ? urlBaseNew
83
+ : `${urlBaseNew}/venueId/${venueData.id}/accountId/${accountId}/get-dynamic-queue-times/`);
84
+
85
+ const updateFromAPI = async () => {
86
+ Promise.all([
87
+ getURL()
88
+ .then(fetch)
89
+ .then(r => r.json())
90
+ .then(poiMap => processDynamicPois(Date.now(), poiMap)),
91
+ getWaitTimesUrl()
92
+ .then(fetch)
93
+ .then(r => r.json())
94
+ .then(waitTimes => processSecurityWaitTimes(Date.now(), waitTimes))
95
+ ]).then(() => dynamicDataNotPending.resolve(true))
96
+ .catch(err => {
97
+ console.error(err);
98
+ dynamicDataNotPending.resolve(true);
99
+ });
100
+ };
101
+
102
+ // Currently, the only way to know if a venue has dynamic POIs is if they have security wait times
103
+ // and the only way we know that is if they have queueTypes.
104
+ // I know this sounds "fragile" - but Jessica said it is our current truth.
105
+ // This will certainly need to change at some point.
106
+ const queueTypes = await app.bus.get('venueData/getQueueTypes');
107
+ if (queueTypes.SecurityLane && queueTypes.SecurityLane.length) {
108
+ dataLoadedProm
109
+ .then(updateFromAPI)
110
+ .then(() => setInterval(updateFromAPI, REFRESH_FREQUENCY));
111
+ } else
112
+ dynamicDataNotPending.resolve(true); // no need to wait for this since there is no dynamic data
113
+ };
114
+
115
+ function processDynamicPois (...args) {
116
+ processParkingPOIS(...args);
117
+ processOpenClosedPois(...args);
118
+ }
119
+
120
+ function processParkingPOIS (timeNowMs, poiMap) {
121
+ const idValuesMap =
122
+ R__namespace.pipe(
123
+ R__namespace.filter(poi => poi.category === 'parking'),
124
+ R__namespace.map(poi => {
125
+ const da = poi.dynamicAttributes;
126
+ if (!da)
127
+ throw Error(`No dynamicAttributes defined for parking POI ${poi.poiId}`)
128
+ const age = (timeNowMs - poi.timestamp) / 1000; // how long ago this was updated by backend (in seconds)
129
+ const props = (age < da['parking.timeToLive']) // if this update is recent enough, consider it "valid"
130
+ ? R__namespace.pick(['lotStatus', 'rateDay', 'rateHour', 'timeIsReal', 'timeToTerminal1', 'timeToTerminal2'], poi)
131
+ : { lotStatus: da['parking.default'], rateDay: '$ -', rateHour: '$ -', timeIsReal: false };
132
+
133
+ return { ...props, lastUpdated: poi.timestamp, lotName: poi.lotName }
134
+ }))(poiMap);
135
+
136
+ app.bus.send('poi/setDynamicData', { plugin: 'parking', idValuesMap });
137
+ }
138
+
139
+ const getPOILabels = async idArray => {
140
+ const nameMap = { };
141
+ for (const poiId of idArray) {
142
+ const poi = await app.bus.get('poi/getById', { id: poiId });
143
+ if (poi)
144
+ nameMap[poiId] = poi.name;
145
+ }
146
+ return nameMap
147
+ };
148
+
149
+ /**
150
+ *
151
+ * @param {Record<Number, DynamicQueueTime} queueTimes A map of POI Ids to dynamic queue times
152
+ * @param {Record<Number, String>} labels A map of POI Ids to map labels
153
+ * @param {*} feature A GeoJson feature
154
+ * @returns feature
155
+ */
156
+ const mutateSecurityCheckpointLabel = (queueTimes, labels) => feature => {
157
+ const id = feature.properties.id;
158
+ const dynamicData = queueTimes[id];
159
+ const label = labels[id];
160
+ if (dynamicData) { // should we only show this message when "timeIsReal" is true?
161
+ const { queueTime, isTemporarilyClosed } = dynamicData;
162
+ const secondaryText = isTemporarilyClosed ? '(closed)' : `(${queueTime} minute wait)`;
163
+ feature.properties.text = `${label}\n${secondaryText}`;
164
+ }
165
+ return feature
166
+ };
167
+
168
+ /*
169
+ API response: https://gitlab.com/locuslabs/core-data-team/json-schemas/-/blob/develop/src/api-marketplace/dynamic-queue-data.json
170
+ */
171
+ async function processSecurityWaitTimes (timeNowMs, waitTimes) {
172
+ const idValuesMap = R__namespace.pipe(
173
+ R__namespace.map(waitTime => [waitTime.poiId, toDynamicWaitTime(timeNowMs, waitTime)]),
174
+ R__namespace.fromPairs
175
+ )(waitTimes);
176
+
177
+ app.bus.send('poi/setDynamicData', { plugin: 'security', idValuesMap });
178
+
179
+ const labels = await getPOILabels(Object.keys(idValuesMap)); // get a map of labels for the POIs - used in wait time labeling
180
+
181
+ app.bus.send('map/mutateFeature', {
182
+ functor: mutateSecurityCheckpointLabel(idValuesMap, labels)
183
+ });
184
+ }
185
+
186
+ const toDynamicWaitTime = (timeNowMs, waitTime) => ({
187
+ queueTime: waitTime.queueTime,
188
+ isTemporarilyClosed: waitTime.isTemporarilyClosed,
189
+ timeIsReal: !waitTime.isQueueTimeDefault && waitTime.expiration > timeNowMs,
190
+ lastUpdated: timeNowMs
191
+ });
192
+
193
+ function processOpenClosedPois (timeNowMs, poiMap) {
194
+ const pathToOpenClosedData = ['dynamicData', 'openClosed'];
195
+ const openClosedPois = R__namespace.filter(R__namespace.hasPath(pathToOpenClosedData), poiMap);
196
+ const openClosedStatuses = R__namespace.map(R__namespace.path(pathToOpenClosedData), openClosedPois);
197
+
198
+ if (R__namespace.all(R__namespace.both(R__namespace.has('isOpen'), R__namespace.has('expiration')), R__namespace.values(openClosedStatuses))) {
199
+ const isExpired = R__namespace.pipe(R__namespace.prop('expiration'), R__namespace.lt(timeNowMs));
200
+ const idValuesMap = R__namespace.filter(isExpired, openClosedStatuses);
201
+
202
+ app.bus.send('poi/setDynamicData', { plugin: 'open-closed-status', idValuesMap });
203
+ } else {
204
+ throw Error('Open Closed poi status is malformed.')
205
+ }
206
+ }
207
+
208
+ app.bus.on('venueData/venueDataLoaded', ({ venueData }) => {
209
+ if (dataLoadedProm.v) // non-standard - indicates promise has been resolved...
210
+ dataLoadedProm = Zousan__default["default"].resolve(venueData);
211
+ else
212
+ dataLoadedProm.resolve(venueData);
213
+ });
214
+
215
+ // by returning this dynamicDataLoaded promise, we hold sdkReady event until this is resolved
216
+ app.bus.on('system/readywhenyouare', () => dynamicDataNotPending);
217
+
218
+ return {
219
+ init,
220
+ internal: {
221
+ mutateSecurityCheckpointLabel,
222
+ processSecurityWaitTimes,
223
+ processParkingPOIS,
224
+ processOpenClosedPois,
225
+ processDynamicPois
226
+ }
227
+ }
228
+ }
229
+
230
+ exports.create = create;
@@ -2,8 +2,8 @@
2
2
 
3
3
  Object.defineProperty(exports, '__esModule', { value: true });
4
4
 
5
- var e = require('ramda');
6
- var a = require('zousan');
5
+ var R = require('ramda');
6
+ var Zousan = require('zousan');
7
7
  var buildStructureLookup = require('../../../src/utils/buildStructureLookup.js');
8
8
  var configUtils = require('../../../src/utils/configUtils.js');
9
9
  var i18n = require('../../../src/utils/i18n.js');
@@ -11,26 +11,297 @@ var i18n = require('../../../src/utils/i18n.js');
11
11
  function _interopDefaultLegacy (e) { return e && typeof e === 'object' && 'default' in e ? e : { 'default': e }; }
12
12
 
13
13
  function _interopNamespace(e) {
14
- if (e && e.__esModule) return e;
15
- var n = Object.create(null);
16
- if (e) {
17
- Object.keys(e).forEach(function (k) {
18
- if (k !== 'default') {
19
- var d = Object.getOwnPropertyDescriptor(e, k);
20
- Object.defineProperty(n, k, d.get ? d : {
21
- enumerable: true,
22
- get: function () { return e[k]; }
23
- });
24
- }
25
- });
26
- }
27
- n["default"] = e;
28
- return Object.freeze(n);
14
+ if (e && e.__esModule) return e;
15
+ var n = Object.create(null);
16
+ if (e) {
17
+ Object.keys(e).forEach(function (k) {
18
+ if (k !== 'default') {
19
+ var d = Object.getOwnPropertyDescriptor(e, k);
20
+ Object.defineProperty(n, k, d.get ? d : {
21
+ enumerable: true,
22
+ get: function () { return e[k]; }
23
+ });
24
+ }
25
+ });
26
+ }
27
+ n["default"] = e;
28
+ return Object.freeze(n);
29
29
  }
30
30
 
31
- var e__namespace = /*#__PURE__*/_interopNamespace(e);
32
- var a__default = /*#__PURE__*/_interopDefaultLegacy(a);
31
+ var R__namespace = /*#__PURE__*/_interopNamespace(R);
32
+ var Zousan__default = /*#__PURE__*/_interopDefaultLegacy(Zousan);
33
33
 
34
- async function n(a,n){const s=a.log.sublog("poiDataManager"),u=()=>{a.bus.send("venueData/loadPoiData");};let p=new a__default["default"];const d=(o,e)=>{const{position:t}=o,i=e.floorIdToStructure(t.floorId);if(!i)return s.error(`No structure found for floorId: ${t.floorId} for POI ${o.poiId}`),{...o};const a=e.floorIdToFloor(t.floorId),n={...t,structureName:i.name,buildingId:i.id,floorName:a.name,floorOrdinal:a.ordinal};return {...o,position:n}},c=(o,e)=>{o.roomInfo||(o.roomInfo=[]),o.roomInfo.push(e);},l=e__namespace.pipe(e__namespace.propOr([],"externalIds"),e__namespace.find(e__namespace.propEq("type","roomId")),e__namespace.prop("id"),e__namespace.unless(e__namespace.isNil,e__namespace.tail));a.bus.on("venueData/poiDataLoaded",(async({pois:e,structures:n})=>{if(e=((e,t)=>e__namespace.pipe(e__namespace.values,e__namespace.map((o=>{o.distance=null,o.isNavigable=void 0===o.isNavigable||!0===o.isNavigable,o.capacity&&c(o,{name:`Seats ${o.capacity.join("-")}`,svgId:"number-of-seats"}),o.category.startsWith("meeting")&&c(o,{name:a.gt()("poiView:Conference Room"),svgId:"conference-room"});const e=l(o);return e&&(o.roomId=e),[o.poiId,d(o,t)]})),e__namespace.fromPairs)(e))(e,buildStructureLookup.buildStructuresLookup(n)),configUtils.debugIsTrue(a,"pseudoTransPois"))for(const o in e)e[o]=r(e[o],a.i18n().language);e=function(o){const e=[];return Object.values(o).forEach((o=>{try{const t=o.position;t?["buildingId","structureName","floorId","floorName","floorOrdinal","latitude","longitude"].forEach((i=>{null!==t[i]&&void 0!==t[i]||e.push({id:o.poiId,e:`invalid position property: ${i}: ${t[i]}`});})):e.push({poi:o,e:"No position information"});}catch(t){s.error(t),e.push({id:o.poiId,e:t.message});}})),e.length&&(s.warn("badPois:",e),e.forEach((e=>{delete o[e.id];}))),o}(e),await async function(o){for(const e of Object.values(o))await y(e);return o}(e),p.resolve(e),a.config.debug&&a.env.isBrowser&&(window._pois=e),a.config.debug&&async function(o){const e=Date.now(),t=[],i=await a.bus.get("wayfinder/_getNavGraph");Object.values(o).forEach((o=>{try{const e=o.position;i.findClosestNode(e.floorId,e.latitude,e.longitude)||t.push({id:o.poiId,e:"No closest Navgraph Node"});}catch(e){s.error(e),t.push({id:o.poiId,e:e.message});}})),t.length&&s.warn("badPois:",t),s(`Total time for navgraph POI check: ${Date.now()-e}ms`);}(e);})),a.bus.on("poi/getById",(async({id:o})=>p.then((e=>e[o])))),a.bus.on("poi/getByFloorId",(async({floorId:e})=>p.then(e__namespace.pickBy(e__namespace.pathEq(["position","floorId"],e))))),a.bus.on("poi/getByCategoryId",(async({categoryId:e})=>p.then(e__namespace.pickBy((o=>o.category===e||o.category.startsWith(e+".")))))),a.bus.on("poi/getAll",(async()=>p));const f=["queue","primaryQueueId"],m=(e,t,i)=>{const a=e__namespace.path(["queue","queueType"],t);if(!a)return null;const n=e[a],r=e__namespace.path(f,t);return i.filter(e__namespace.pathEq(f,r)).filter((o=>o.poiId!==t.poiId)).map((e=>{const t=e__namespace.path(["queue","queueSubtype"],e),i=g(t)(n);return {poiId:e.poiId,...i}}))},g=e=>{return e__namespace.pipe(e__namespace.find(e__namespace.propEq("id",e)),(t=`No queue found with ID: ${e}`,o=>{if(null!=o)return o;throw Error(t)}),e__namespace.pick(["displayText","imageId"]));var t;};async function y(t){if(!t)return;const i="undefined"==typeof window?1:window.devicePixelRatio||1;return e__namespace.length(t.images)?t.images[0].startsWith("https:")||(t.images=await a__default["default"].all(t.images.map((o=>a.bus.get("venueData/getPoiImageUrl",{imageName:o,size:`${Math.round(351*i)}x${Math.round(197*i)}`}))))):t.images=[],t}a.bus.on("poi/addOtherSecurityLanes",(({poi:e})=>(async e=>{if(!e__namespace.path(f,e))return e;const t=await a.bus.get("venueData/getQueueTypes"),i=await a.bus.get("poi/getByCategoryId",{categoryId:"security"}),n=Object.values(i);return e.queue.otherQueues=m(t,e,n),e})(e)));const h=e__namespace.memoizeWith(e__namespace.identity,e__namespace.pipe(e__namespace.pluck("category"),e__namespace.values,e__namespace.uniq));a.bus.on("poi/getAllCategories",(async()=>p.then(h))),a.bus.on("venueData/loadNewVenue",(()=>{p=new a__default["default"],u();})),a.bus.on("poi/setDynamicData",(({plugin:e,idValuesMap:t})=>{p.then((i=>{for(const a in t){const n=i[a].dynamicData||{};n[e]={...t[a]};const r=e__namespace.mergeRight(i[a],{dynamicData:n});i[a]=r;}}));}));return {init:u,runTest:async o=>(await o(),p),internal:{addImages:y,pseudoTransPoi:r}}}function r(o,e){return ["description","nearbyLandmark","name","phone","operationHours"].forEach((t=>{o[t]&&(o[t]=i18n.toLang(o[t],e));})),o.keywords&&(o.keywords=o.keywords.map((o=>(o.name=i18n.toLang(o.name,e),o)))),o.position.floorName&&(o.position.floorName=i18n.toLang(o.position.floorName,e)),o.position.structureName&&(o.position.structureName=i18n.toLang(o.position.structureName,e)),o}
34
+ async function create (app, config) {
35
+ const log = app.log.sublog('poiDataManager');
36
+ const init = () => {
37
+ app.bus.send('venueData/loadPoiData');
38
+ };
35
39
 
36
- exports.create = n;
40
+ let poisLoaded = new Zousan__default["default"]();
41
+
42
+ const fixPositionInfo = (poi, structuresLookup) => {
43
+ const { position } = poi;
44
+ const structure = structuresLookup.floorIdToStructure(position.floorId);
45
+ if (!structure) {
46
+ log.error(`No structure found for floorId: ${position.floorId} for POI ${poi.poiId}`);
47
+ return { ...poi }
48
+ }
49
+ const floor = structuresLookup.floorIdToFloor(position.floorId);
50
+ const detailedPosition = {
51
+ ...position,
52
+ structureName: structure.name,
53
+ buildingId: structure.id,
54
+ floorName: floor.name,
55
+ floorOrdinal: floor.ordinal
56
+ };
57
+ return { ...poi, position: detailedPosition }
58
+ };
59
+
60
+ // todo R.map may be enough to update dictionary values
61
+ const formatPois = (pois, structuresLookup) => {
62
+ return R__namespace.pipe(
63
+ R__namespace.values,
64
+ R__namespace.map(poi => {
65
+ poi.distance = null;
66
+ poi.isNavigable = poi.isNavigable === undefined || poi.isNavigable === true; // isNavigable is true as default, and is optional in the POI data
67
+ // poi.isNavigable = /^[a-mA-M]+/.test(poi.name) // uncomment for easy testing of isNavigable
68
+ if (poi.capacity)
69
+ addToRoomInfo(poi, { name: `Seats ${poi.capacity.join('-')}`, svgId: 'number-of-seats' });
70
+ if (poi.category.startsWith('meeting'))
71
+ addToRoomInfo(poi, { name: app.gt()('poiView:Conference Room'), svgId: 'conference-room' });
72
+
73
+ const roomId = getRoomId(poi);
74
+ if (roomId)
75
+ poi.roomId = roomId;
76
+
77
+ return [poi.poiId, fixPositionInfo(poi, structuresLookup)]
78
+ }),
79
+ R__namespace.fromPairs
80
+ )(pois)
81
+ };
82
+
83
+ const addToRoomInfo = (poi, value) => {
84
+ if (!poi.roomInfo) {
85
+ poi.roomInfo = [];
86
+ }
87
+ poi.roomInfo.push(value);
88
+ };
89
+
90
+ const getRoomId = R__namespace.pipe(
91
+ R__namespace.propOr([], 'externalIds'),
92
+ R__namespace.find(R__namespace.propEq('type', 'roomId')),
93
+ R__namespace.prop('id'),
94
+ R__namespace.unless(R__namespace.isNil, R__namespace.tail)
95
+ );
96
+
97
+ async function checkNavgraph (pois) {
98
+ const start = Date.now();
99
+ const badPois = [];
100
+ const navGraph = await app.bus.get('wayfinder/_getNavGraph');
101
+ Object.values(pois).forEach(poi => {
102
+ try {
103
+ const pos = poi.position;
104
+ const n = navGraph.findClosestNode(pos.floorId, pos.latitude, pos.longitude);
105
+ if (!n)
106
+ badPois.push({ id: poi.poiId, e: 'No closest Navgraph Node' });
107
+ } catch (e) {
108
+ log.error(e);
109
+ badPois.push({ id: poi.poiId, e: e.message });
110
+ }
111
+ });
112
+
113
+ if (badPois.length)
114
+ log.warn('badPois:', badPois);
115
+
116
+ log(`Total time for navgraph POI check: ${Date.now() - start}ms`);
117
+ }
118
+
119
+ function filterBadPois (pois) {
120
+ const badPois = [];
121
+ Object.values(pois).forEach(poi => {
122
+ try {
123
+ const pos = poi.position;
124
+ if (!pos)
125
+ badPois.push({ poi, e: 'No position information' });
126
+ else {
127
+ ;['buildingId', 'structureName', 'floorId', 'floorName', 'floorOrdinal', 'latitude', 'longitude'].forEach(k => {
128
+ if (pos[k] === null || pos[k] === undefined)
129
+ badPois.push({ id: poi.poiId, e: `invalid position property: ${k}: ${pos[k]}` });
130
+ });
131
+ }
132
+ } catch (e) {
133
+ log.error(e);
134
+ badPois.push({ id: poi.poiId, e: e.message });
135
+ }
136
+ });
137
+
138
+ if (badPois.length) {
139
+ log.warn('badPois:', badPois);
140
+ badPois.forEach(ob => { delete pois[ob.id]; });
141
+ }
142
+
143
+ return pois
144
+ }
145
+
146
+ async function enhanceImages (pois) {
147
+ for (const poi of Object.values(pois))
148
+ await addImages(poi);
149
+
150
+ return pois
151
+ }
152
+
153
+ app.bus.on('venueData/poiDataLoaded', async ({ pois, structures }) => {
154
+ const structuresLookup = buildStructureLookup.buildStructuresLookup(structures);
155
+ pois = formatPois(pois, structuresLookup);
156
+ if (configUtils.debugIsTrue(app, 'pseudoTransPois'))
157
+ for (const id in pois)
158
+ pois[id] = pseudoTransPoi(pois[id], app.i18n().language);
159
+
160
+ pois = filterBadPois(pois);
161
+
162
+ await enhanceImages(pois);
163
+ poisLoaded.resolve(pois);
164
+
165
+ if (app.config.debug && app.env.isBrowser)
166
+ window._pois = pois;
167
+
168
+ if (app.config.debug)
169
+ checkNavgraph(pois); // this doesn't change the pois, but warns in the console...
170
+ });
171
+
172
+ app.bus.on('poi/getById', async ({ id }) => {
173
+ return poisLoaded.then(pois => pois[id])
174
+ });
175
+
176
+ app.bus.on('poi/getByFloorId', async ({ floorId }) => poisLoaded.then(
177
+ R__namespace.pickBy(R__namespace.pathEq(['position', 'floorId'], floorId))));
178
+
179
+ app.bus.on('poi/getByCategoryId', async ({ categoryId }) => {
180
+ // returns true for exact category matches or parent category matches
181
+ const categoryMatch = (poi) => poi.category === categoryId || poi.category.startsWith(categoryId + '.');
182
+ return poisLoaded.then(R__namespace.pickBy(categoryMatch))
183
+ });
184
+
185
+ app.bus.on('poi/getAll', async () => poisLoaded);
186
+
187
+ const checkpointPath = ['queue', 'primaryQueueId'];
188
+ const addOtherSecurityLanesIfNeeded = async poi => {
189
+ const primaryCheckpointId = R__namespace.path(checkpointPath, poi);
190
+
191
+ if (!primaryCheckpointId) return poi
192
+
193
+ const queueTypes = await app.bus.get('venueData/getQueueTypes');
194
+ const securityPoisMap = await app.bus.get('poi/getByCategoryId', { categoryId: 'security' });
195
+ const securityPoisList = Object.values(securityPoisMap);
196
+
197
+ poi.queue.otherQueues = prepareOtherSecurityLanes(queueTypes, poi, securityPoisList);
198
+ return poi
199
+ };
200
+
201
+ const prepareOtherSecurityLanes = (queueTypes, currentPoi, securityPois) => {
202
+ const queueTypePath = ['queue', 'queueType'];
203
+ const queueType = R__namespace.path(queueTypePath, currentPoi);
204
+ if (!queueType)
205
+ return null
206
+
207
+ const queueSubtypes = queueTypes[queueType];
208
+ const primaryCheckpointId = R__namespace.path(checkpointPath, currentPoi);
209
+ return securityPois
210
+ .filter(R__namespace.pathEq(checkpointPath, primaryCheckpointId)) // filter only connected security checkpoints
211
+ .filter(poi => poi.poiId !== currentPoi.poiId) // skip current poi
212
+ .map(poi => {
213
+ const laneId = R__namespace.path(['queue', 'queueSubtype'], poi);
214
+ const lane = getLaneData(laneId)(queueSubtypes);
215
+ return { poiId: poi.poiId, ...lane }
216
+ })
217
+ };
218
+
219
+ // if the value passed is non-null, simply return it. Else throw the error with the message specified.
220
+ // This is useful to insert into pipelines.
221
+ const ensureDefined = errorMsg => value => {
222
+ if (value != null)
223
+ return value
224
+ throw Error(errorMsg)
225
+ };
226
+
227
+ const getLaneData = laneId => R__namespace.pipe(
228
+ R__namespace.find(R__namespace.propEq('id', laneId)),
229
+ ensureDefined(`No queue found with ID: ${laneId}`),
230
+ R__namespace.pick(['displayText', 'imageId'])
231
+ );
232
+
233
+ /**
234
+ * Updates security checkpoint POI with a list of related security checkpoints.
235
+ */
236
+ app.bus.on('poi/addOtherSecurityLanes', ({ poi }) => addOtherSecurityLanesIfNeeded(poi));
237
+
238
+ async function addImages (poi) {
239
+ if (!poi) return
240
+ const dpr = typeof window === 'undefined' ? 1 : (window.devicePixelRatio || 1);
241
+ if (!R__namespace.length(poi.images)) {
242
+ poi.images = [];
243
+ } else if (!poi.images[0].startsWith('https:')) { // then images are not yet transformed
244
+ poi.images = await Zousan__default["default"].all(
245
+ poi.images.map(imageName =>
246
+ app.bus.get('venueData/getPoiImageUrl', { imageName, size: `${Math.round(351 * dpr)}x${Math.round(197 * dpr)}` })));
247
+ }
248
+
249
+ return poi
250
+ }
251
+
252
+ const getUniqueCategories = R__namespace.memoizeWith(R__namespace.identity, R__namespace.pipe(R__namespace.pluck('category'), R__namespace.values, R__namespace.uniq));
253
+ app.bus.on('poi/getAllCategories', async () => poisLoaded.then(getUniqueCategories));
254
+
255
+ app.bus.on('venueData/loadNewVenue', () => {
256
+ poisLoaded = new Zousan__default["default"]();
257
+ init();
258
+ });
259
+
260
+ // See architectural document at https://docs.google.com/document/d/1NoBAboHR9BiX_vvLef-vp3ButrIQDWYofcTsdilEWvs/edit#
261
+ app.bus.on('poi/setDynamicData', ({ plugin, idValuesMap }) => {
262
+ poisLoaded
263
+ .then(pois => {
264
+ for (const poiId in idValuesMap) {
265
+ // const dd = { [plugin]: idValuesMap[poiId] }
266
+ const dynamicData = pois[poiId].dynamicData || {};
267
+ dynamicData[plugin] = { ...idValuesMap[poiId] };
268
+ const newPoi = R__namespace.mergeRight(pois[poiId], { dynamicData });
269
+ pois[poiId] = newPoi;
270
+ }
271
+ });
272
+ });
273
+
274
+ const runTest = async (testRoutine) => {
275
+ await testRoutine();
276
+ return poisLoaded
277
+ };
278
+
279
+ return {
280
+ init,
281
+ runTest,
282
+ internal: {
283
+ addImages,
284
+ pseudoTransPoi
285
+ }
286
+ }
287
+ }
288
+
289
+ function pseudoTransPoi (poi, lang) {
290
+ ['description', 'nearbyLandmark', 'name', 'phone', 'operationHours']
291
+ .forEach(p => {
292
+ if (poi[p])
293
+ poi[p] = i18n.toLang(poi[p], lang);
294
+ });
295
+ if (poi.keywords)
296
+ poi.keywords = poi.keywords.map(keyword => {
297
+ keyword.name = i18n.toLang(keyword.name, lang);
298
+ return keyword
299
+ });
300
+ if (poi.position.floorName)
301
+ poi.position.floorName = i18n.toLang(poi.position.floorName, lang);
302
+ if (poi.position.structureName)
303
+ poi.position.structureName = i18n.toLang(poi.position.structureName, lang);
304
+ return poi
305
+ }
306
+
307
+ exports.create = create;