hk-bus-eta 3.5.0 → 3.5.2

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.
@@ -10,6 +10,7 @@ var __awaiter = (this && this.__awaiter) || function (thisArg, _arguments, P, ge
10
10
  };
11
11
  Object.defineProperty(exports, "__esModule", { value: true });
12
12
  exports.fetchEstJourneyTime = void 0;
13
+ const __HK_BUS_ETA_JT_CACHE__ = {};
13
14
  /**
14
15
  * Fetch Journey time in minute for a route
15
16
  * @param {Object}
@@ -26,49 +27,79 @@ function fetchEstJourneyTime({ route, stopList, startSeq, endSeq, batchSize = 4,
26
27
  throw new Error("endSeq should be < number of stops");
27
28
  if (startSeq >= endSeq)
28
29
  throw new Error("startSeq should be < endSeq");
30
+ let ts = Date.now();
29
31
  let ret = 0;
30
32
  let payloads = [];
31
33
  for (let i = startSeq; i < endSeq; ++i) {
32
- payloads.push(JSON.stringify({
33
- start: {
34
- lat: stopList[stops[i]].location.lat,
35
- long: stopList[stops[i]].location.lng,
36
- },
37
- end: {
38
- lat: stopList[stops[i + 1]].location.lat,
39
- long: stopList[stops[i + 1]].location.lng,
40
- },
41
- departIn: Math.round(ret / 15) * 15
42
- }));
34
+ payloads.push([
35
+ `${stops[i]}-${stops[i + 1]}`,
36
+ JSON.stringify({
37
+ start: {
38
+ lat: stopList[stops[i]].location.lat,
39
+ long: stopList[stops[i]].location.lng,
40
+ },
41
+ end: {
42
+ lat: stopList[stops[i + 1]].location.lat,
43
+ long: stopList[stops[i + 1]].location.lng,
44
+ },
45
+ departIn: Math.round(ret / 15) * 15
46
+ })
47
+ ]);
43
48
  if (payloads.length < batchSize && i !== endSeq - 1) {
44
49
  // skip fetching until whole batch filled
45
50
  continue;
46
51
  }
47
- const minutes = yield Promise.all(payloads.map(payload =>
48
- //TODO: update to use gov API
49
- // fetch('https://tdas.hkbus.app/tdas/api/route', {
50
- fetch('https://tdas.hkbus.app/tdas/api/route', {
51
- method: "POST",
52
- headers: {
53
- 'Content-Type': "application/json",
54
- },
55
- body: payload,
56
- signal,
57
- })
58
- .then(r => r.json())
59
- .then(({ eta }) => {
60
- const [hh, mm] = eta.split(":").map((v) => parseInt(v, 10));
61
- return hh * 60 + mm + 1;
62
- })
63
- .catch(() => {
64
- // for any error, assume 5 minutes travel time blindly
65
- return 5;
66
- })));
52
+ const minutes = yield Promise.all(payloads.map(([key, payload]) => {
53
+ // load from cache if it is query within 15 minutes
54
+ if (key in __HK_BUS_ETA_JT_CACHE__ && __HK_BUS_ETA_JT_CACHE__[key].ts + 15 * 60 * 1000 >= ts) {
55
+ return Promise.resolve(__HK_BUS_ETA_JT_CACHE__[key].m);
56
+ }
57
+ return (
58
+ //TODO: update to use gov API
59
+ // fetch('https://tdas.hkbus.app/tdas/api/route', {
60
+ fetch('https://tdas.hkbus.app/tdas/api/route', {
61
+ method: "POST",
62
+ headers: {
63
+ 'Content-Type': "application/json",
64
+ },
65
+ body: payload,
66
+ signal,
67
+ })
68
+ .then(r => r.json())
69
+ .then(({ eta, distM, jSpeed }) => {
70
+ var _a;
71
+ const speedStr = (_a = /(\d+)公里\/小時/g.exec(jSpeed)) === null || _a === void 0 ? void 0 : _a[1];
72
+ if (speedStr !== null &&
73
+ speedStr !== undefined &&
74
+ !isNaN(parseInt(speedStr, 10))) {
75
+ // Set the speed limit as 70 km/h
76
+ return (distM / Math.min(parseInt(speedStr, 10), 70) / 1000) * 60;
77
+ }
78
+ console.warn("Unable to parse TDAS response for more precise journey time. Falling back.");
79
+ const [hh, mm] = eta.split(":").map((v) => parseInt(v, 10));
80
+ return hh * 60 + mm;
81
+ })
82
+ .catch(() => {
83
+ // for any error, assume 4 minutes journey time blindly
84
+ return 4;
85
+ })
86
+ .then(m => {
87
+ __HK_BUS_ETA_JT_CACHE__[key] = { m: m + 1, ts };
88
+ // margin for car accelerating, decelerating, and passenger picking up and dropping off
89
+ return m + 1;
90
+ }));
91
+ }));
67
92
  minutes.forEach(m => {
68
93
  ret += m;
69
94
  });
70
95
  payloads.length = 0;
71
96
  }
97
+ for (const k in __HK_BUS_ETA_JT_CACHE__) {
98
+ // clean up cache
99
+ if (__HK_BUS_ETA_JT_CACHE__[k].ts + 15 * 60 * 1000 < ts) {
100
+ delete __HK_BUS_ETA_JT_CACHE__[k];
101
+ }
102
+ }
72
103
  return ret;
73
104
  });
74
105
  }
@@ -1 +1 @@
1
- {"version":3,"file":"journeyTime.js","sourceRoot":"","sources":["../src/journeyTime.ts"],"names":[],"mappings":"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"}
1
+ {"version":3,"file":"journeyTime.js","sourceRoot":"","sources":["../src/journeyTime.ts"],"names":[],"mappings":"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"}
@@ -1,3 +1,4 @@
1
+ const __HK_BUS_ETA_JT_CACHE__ = {};
1
2
  /**
2
3
  * Fetch Journey time in minute for a route
3
4
  * @param {Object}
@@ -13,49 +14,79 @@ export async function fetchEstJourneyTime({ route, stopList, startSeq, endSeq, b
13
14
  throw new Error("endSeq should be < number of stops");
14
15
  if (startSeq >= endSeq)
15
16
  throw new Error("startSeq should be < endSeq");
17
+ let ts = Date.now();
16
18
  let ret = 0;
17
19
  let payloads = [];
18
20
  for (let i = startSeq; i < endSeq; ++i) {
19
- payloads.push(JSON.stringify({
20
- start: {
21
- lat: stopList[stops[i]].location.lat,
22
- long: stopList[stops[i]].location.lng,
23
- },
24
- end: {
25
- lat: stopList[stops[i + 1]].location.lat,
26
- long: stopList[stops[i + 1]].location.lng,
27
- },
28
- departIn: Math.round(ret / 15) * 15
29
- }));
21
+ payloads.push([
22
+ `${stops[i]}-${stops[i + 1]}`,
23
+ JSON.stringify({
24
+ start: {
25
+ lat: stopList[stops[i]].location.lat,
26
+ long: stopList[stops[i]].location.lng,
27
+ },
28
+ end: {
29
+ lat: stopList[stops[i + 1]].location.lat,
30
+ long: stopList[stops[i + 1]].location.lng,
31
+ },
32
+ departIn: Math.round(ret / 15) * 15
33
+ })
34
+ ]);
30
35
  if (payloads.length < batchSize && i !== endSeq - 1) {
31
36
  // skip fetching until whole batch filled
32
37
  continue;
33
38
  }
34
- const minutes = await Promise.all(payloads.map(payload =>
35
- //TODO: update to use gov API
36
- // fetch('https://tdas.hkbus.app/tdas/api/route', {
37
- fetch('https://tdas.hkbus.app/tdas/api/route', {
38
- method: "POST",
39
- headers: {
40
- 'Content-Type': "application/json",
41
- },
42
- body: payload,
43
- signal,
44
- })
45
- .then(r => r.json())
46
- .then(({ eta }) => {
47
- const [hh, mm] = eta.split(":").map((v) => parseInt(v, 10));
48
- return hh * 60 + mm + 1;
49
- })
50
- .catch(() => {
51
- // for any error, assume 5 minutes travel time blindly
52
- return 5;
53
- })));
39
+ const minutes = await Promise.all(payloads.map(([key, payload]) => {
40
+ // load from cache if it is query within 15 minutes
41
+ if (key in __HK_BUS_ETA_JT_CACHE__ && __HK_BUS_ETA_JT_CACHE__[key].ts + 15 * 60 * 1000 >= ts) {
42
+ return Promise.resolve(__HK_BUS_ETA_JT_CACHE__[key].m);
43
+ }
44
+ return (
45
+ //TODO: update to use gov API
46
+ // fetch('https://tdas.hkbus.app/tdas/api/route', {
47
+ fetch('https://tdas.hkbus.app/tdas/api/route', {
48
+ method: "POST",
49
+ headers: {
50
+ 'Content-Type': "application/json",
51
+ },
52
+ body: payload,
53
+ signal,
54
+ })
55
+ .then(r => r.json())
56
+ .then(({ eta, distM, jSpeed }) => {
57
+ var _a;
58
+ const speedStr = (_a = /(\d+)公里\/小時/g.exec(jSpeed)) === null || _a === void 0 ? void 0 : _a[1];
59
+ if (speedStr !== null &&
60
+ speedStr !== undefined &&
61
+ !isNaN(parseInt(speedStr, 10))) {
62
+ // Set the speed limit as 70 km/h
63
+ return (distM / Math.min(parseInt(speedStr, 10), 70) / 1000) * 60;
64
+ }
65
+ console.warn("Unable to parse TDAS response for more precise journey time. Falling back.");
66
+ const [hh, mm] = eta.split(":").map((v) => parseInt(v, 10));
67
+ return hh * 60 + mm;
68
+ })
69
+ .catch(() => {
70
+ // for any error, assume 4 minutes journey time blindly
71
+ return 4;
72
+ })
73
+ .then(m => {
74
+ __HK_BUS_ETA_JT_CACHE__[key] = { m: m + 1, ts };
75
+ // margin for car accelerating, decelerating, and passenger picking up and dropping off
76
+ return m + 1;
77
+ }));
78
+ }));
54
79
  minutes.forEach(m => {
55
80
  ret += m;
56
81
  });
57
82
  payloads.length = 0;
58
83
  }
84
+ for (const k in __HK_BUS_ETA_JT_CACHE__) {
85
+ // clean up cache
86
+ if (__HK_BUS_ETA_JT_CACHE__[k].ts + 15 * 60 * 1000 < ts) {
87
+ delete __HK_BUS_ETA_JT_CACHE__[k];
88
+ }
89
+ }
59
90
  return ret;
60
91
  }
61
92
  //# sourceMappingURL=journeyTime.js.map
@@ -1 +1 @@
1
- {"version":3,"file":"journeyTime.js","sourceRoot":"","sources":["../src/journeyTime.ts"],"names":[],"mappings":"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"}
1
+ {"version":3,"file":"journeyTime.js","sourceRoot":"","sources":["../src/journeyTime.ts"],"names":[],"mappings":"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"}
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "hk-bus-eta",
3
- "version": "3.5.0",
3
+ "version": "3.5.2",
4
4
  "description": "Query the ETA (Estimated Time of Arrival) of HK Bus/Minibus/MTR/Lightrail",
5
5
  "main": "dist/index.js",
6
6
  "module": "esm/index.js",
@@ -1,5 +1,12 @@
1
1
  import { RouteListEntry, StopList } from "./type";
2
2
 
3
+ type JT_CACHE = Record<string, {
4
+ m: number;
5
+ ts: number;
6
+ }>
7
+
8
+ const __HK_BUS_ETA_JT_CACHE__: JT_CACHE = {}
9
+
3
10
  /**
4
11
  * Fetch Journey time in minute for a route
5
12
  * @param {Object}
@@ -17,10 +24,12 @@ export async function fetchEstJourneyTime({
17
24
  if ( endSeq >= stops.length ) throw new Error("endSeq should be < number of stops")
18
25
  if ( startSeq >= endSeq ) throw new Error("startSeq should be < endSeq")
19
26
 
27
+ let ts = Date.now()
20
28
  let ret = 0
21
29
  let payloads = []
22
30
  for ( let i = startSeq; i < endSeq; ++i ) {
23
- payloads.push(
31
+ payloads.push([
32
+ `${stops[i]}-${stops[i+1]}`,
24
33
  JSON.stringify({
25
34
  start: {
26
35
  lat: stopList[stops[i]].location.lat,
@@ -32,36 +41,67 @@ export async function fetchEstJourneyTime({
32
41
  },
33
42
  departIn: Math.round(ret / 15) * 15
34
43
  })
35
- )
44
+ ])
36
45
  if ( payloads.length < batchSize && i !== endSeq - 1 ) {
37
46
  // skip fetching until whole batch filled
38
47
  continue
39
48
  }
40
- const minutes = await Promise.all(payloads.map(payload =>
41
- //TODO: update to use gov API
42
- // fetch('https://tdas.hkbus.app/tdas/api/route', {
43
- fetch('https://tdas.hkbus.app/tdas/api/route', {
44
- method: "POST",
45
- headers: {
46
- 'Content-Type': "application/json",
47
- },
48
- body: payload,
49
- signal,
50
- })
51
- .then(r => r.json())
52
- .then(({eta}) => {
53
- const [hh, mm] = eta.split(":").map((v: string) => parseInt(v, 10))
54
- return hh * 60 + mm + 1;
55
- })
56
- .catch(() => {
57
- // for any error, assume 5 minutes travel time blindly
58
- return 5;
49
+ const minutes = await Promise.all(payloads.map(([key, payload]) => {
50
+ // load from cache if it is query within 15 minutes
51
+ if ( key in __HK_BUS_ETA_JT_CACHE__ && __HK_BUS_ETA_JT_CACHE__[key].ts + 15 * 60 * 1000 >= ts ) {
52
+ return Promise.resolve(__HK_BUS_ETA_JT_CACHE__[key].m)
53
+ }
54
+
55
+ return (
56
+ //TODO: update to use gov API
57
+ // fetch('https://tdas.hkbus.app/tdas/api/route', {
58
+ fetch('https://tdas.hkbus.app/tdas/api/route', {
59
+ method: "POST",
60
+ headers: {
61
+ 'Content-Type': "application/json",
62
+ },
63
+ body: payload,
64
+ signal,
59
65
  })
66
+ .then(r => r.json())
67
+ .then(({eta, distM, jSpeed}) => {
68
+ const speedStr = /(\d+)公里\/小時/g.exec(jSpeed)?.[1];
69
+ if (
70
+ speedStr !== null &&
71
+ speedStr !== undefined &&
72
+ !isNaN(parseInt(speedStr, 10))
73
+ ) {
74
+ // Set the speed limit as 70 km/h
75
+ return (distM / Math.min(parseInt(speedStr, 10), 70) / 1000) * 60;
76
+ }
77
+ console.warn(
78
+ "Unable to parse TDAS response for more precise journey time. Falling back."
79
+ );
80
+ const [hh, mm] = eta.split(":").map((v: string) => parseInt(v, 10))
81
+ return hh * 60 + mm;
82
+ })
83
+ .catch(() => {
84
+ // for any error, assume 4 minutes journey time blindly
85
+ return 4;
86
+ })
87
+ .then(m => {
88
+ __HK_BUS_ETA_JT_CACHE__[key] = { m: m + 1, ts }
89
+ // margin for car accelerating, decelerating, and passenger picking up and dropping off
90
+ return m + 1
91
+ })
92
+ )
93
+ }
60
94
  ))
61
95
  minutes.forEach(m => {
62
96
  ret += m
63
97
  })
64
98
  payloads.length = 0
65
99
  }
100
+ for ( const k in __HK_BUS_ETA_JT_CACHE__ ) {
101
+ // clean up cache
102
+ if ( __HK_BUS_ETA_JT_CACHE__[k].ts + 15 * 60 * 1000 < ts ) {
103
+ delete __HK_BUS_ETA_JT_CACHE__[k];
104
+ }
105
+ }
66
106
  return ret
67
107
  }