@freighttech/terminal-tracking 0.7.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.
Files changed (236) hide show
  1. package/LICENSE +21 -0
  2. package/README.md +121 -0
  3. package/dist/generated/entities/terminal_config/index.js +53 -0
  4. package/dist/generated/entities/terminal_config/index.js.map +7 -0
  5. package/dist/generated/entities/terminal_event/index.js +59 -0
  6. package/dist/generated/entities/terminal_event/index.js.map +7 -0
  7. package/dist/generated/entities/terminal_tracking_job/index.js +37 -0
  8. package/dist/generated/entities/terminal_tracking_job/index.js.map +7 -0
  9. package/dist/generated/entities/terminal_vessel_visit/index.js +41 -0
  10. package/dist/generated/entities/terminal_vessel_visit/index.js.map +7 -0
  11. package/dist/generated/entities.ids.generated.js +26 -0
  12. package/dist/generated/entities.ids.generated.js.map +7 -0
  13. package/dist/index.js +20 -0
  14. package/dist/index.js.map +7 -0
  15. package/dist/modules/terminal_tracking/acl.js +14 -0
  16. package/dist/modules/terminal_tracking/acl.js.map +7 -0
  17. package/dist/modules/terminal_tracking/api/openapi.js +27 -0
  18. package/dist/modules/terminal_tracking/api/openapi.js.map +7 -0
  19. package/dist/modules/terminal_tracking/api/terminal-configs/route.js +140 -0
  20. package/dist/modules/terminal_tracking/api/terminal-configs/route.js.map +7 -0
  21. package/dist/modules/terminal_tracking/api/terminal-configs/test/route.js +58 -0
  22. package/dist/modules/terminal_tracking/api/terminal-configs/test/route.js.map +7 -0
  23. package/dist/modules/terminal_tracking/api/tracking-jobs/details/route.js +109 -0
  24. package/dist/modules/terminal_tracking/api/tracking-jobs/details/route.js.map +7 -0
  25. package/dist/modules/terminal_tracking/api/tracking-jobs/poll/route.js +58 -0
  26. package/dist/modules/terminal_tracking/api/tracking-jobs/poll/route.js.map +7 -0
  27. package/dist/modules/terminal_tracking/api/tracking-jobs/route.js +189 -0
  28. package/dist/modules/terminal_tracking/api/tracking-jobs/route.js.map +7 -0
  29. package/dist/modules/terminal_tracking/api/utils.js +12 -0
  30. package/dist/modules/terminal_tracking/api/utils.js.map +7 -0
  31. package/dist/modules/terminal_tracking/api/vessel-visits/route.js +83 -0
  32. package/dist/modules/terminal_tracking/api/vessel-visits/route.js.map +7 -0
  33. package/dist/modules/terminal_tracking/backend/terminal-configs/page.js +669 -0
  34. package/dist/modules/terminal_tracking/backend/terminal-configs/page.js.map +7 -0
  35. package/dist/modules/terminal_tracking/backend/terminal-configs/page.meta.js +19 -0
  36. package/dist/modules/terminal_tracking/backend/terminal-configs/page.meta.js.map +7 -0
  37. package/dist/modules/terminal_tracking/backend/terminal-containers/page.js +275 -0
  38. package/dist/modules/terminal_tracking/backend/terminal-containers/page.js.map +7 -0
  39. package/dist/modules/terminal_tracking/backend/terminal-containers/page.meta.js +19 -0
  40. package/dist/modules/terminal_tracking/backend/terminal-containers/page.meta.js.map +7 -0
  41. package/dist/modules/terminal_tracking/ce.js +35 -0
  42. package/dist/modules/terminal_tracking/ce.js.map +7 -0
  43. package/dist/modules/terminal_tracking/commands/index.js +4 -0
  44. package/dist/modules/terminal_tracking/commands/index.js.map +7 -0
  45. package/dist/modules/terminal_tracking/commands/terminal-configs.js +107 -0
  46. package/dist/modules/terminal_tracking/commands/terminal-configs.js.map +7 -0
  47. package/dist/modules/terminal_tracking/commands/terminal-tracking.js +27 -0
  48. package/dist/modules/terminal_tracking/commands/terminal-tracking.js.map +7 -0
  49. package/dist/modules/terminal_tracking/commands/tracking-jobs.js +56 -0
  50. package/dist/modules/terminal_tracking/commands/tracking-jobs.js.map +7 -0
  51. package/dist/modules/terminal_tracking/components/ContainerDetailsDrawer.js +371 -0
  52. package/dist/modules/terminal_tracking/components/ContainerDetailsDrawer.js.map +7 -0
  53. package/dist/modules/terminal_tracking/data/entities.js +337 -0
  54. package/dist/modules/terminal_tracking/data/entities.js.map +7 -0
  55. package/dist/modules/terminal_tracking/data/validators.js +98 -0
  56. package/dist/modules/terminal_tracking/data/validators.js.map +7 -0
  57. package/dist/modules/terminal_tracking/di.js +39 -0
  58. package/dist/modules/terminal_tracking/di.js.map +7 -0
  59. package/dist/modules/terminal_tracking/encryption.js +14 -0
  60. package/dist/modules/terminal_tracking/encryption.js.map +7 -0
  61. package/dist/modules/terminal_tracking/events.js +45 -0
  62. package/dist/modules/terminal_tracking/events.js.map +7 -0
  63. package/dist/modules/terminal_tracking/i18n/de.json +175 -0
  64. package/dist/modules/terminal_tracking/i18n/en.json +178 -0
  65. package/dist/modules/terminal_tracking/i18n/es.json +175 -0
  66. package/dist/modules/terminal_tracking/i18n/pl.json +206 -0
  67. package/dist/modules/terminal_tracking/index.js +15 -0
  68. package/dist/modules/terminal_tracking/index.js.map +7 -0
  69. package/dist/modules/terminal_tracking/lib/adapters/bct/adapter.js +164 -0
  70. package/dist/modules/terminal_tracking/lib/adapters/bct/adapter.js.map +7 -0
  71. package/dist/modules/terminal_tracking/lib/adapters/bct/auth/basic.js +23 -0
  72. package/dist/modules/terminal_tracking/lib/adapters/bct/auth/basic.js.map +7 -0
  73. package/dist/modules/terminal_tracking/lib/adapters/bct/incos-semantics.js +146 -0
  74. package/dist/modules/terminal_tracking/lib/adapters/bct/incos-semantics.js.map +7 -0
  75. package/dist/modules/terminal_tracking/lib/adapters/bct/types.js +1 -0
  76. package/dist/modules/terminal_tracking/lib/adapters/bct/types.js.map +7 -0
  77. package/dist/modules/terminal_tracking/lib/adapters/gct/adapter.js +106 -0
  78. package/dist/modules/terminal_tracking/lib/adapters/gct/adapter.js.map +7 -0
  79. package/dist/modules/terminal_tracking/lib/adapters/gct/auth/token.js +120 -0
  80. package/dist/modules/terminal_tracking/lib/adapters/gct/auth/token.js.map +7 -0
  81. package/dist/modules/terminal_tracking/lib/adapters/gct/gct-semantics.js +77 -0
  82. package/dist/modules/terminal_tracking/lib/adapters/gct/gct-semantics.js.map +7 -0
  83. package/dist/modules/terminal_tracking/lib/adapters/gct/types.js +1 -0
  84. package/dist/modules/terminal_tracking/lib/adapters/gct/types.js.map +7 -0
  85. package/dist/modules/terminal_tracking/lib/adapters/index.js +12 -0
  86. package/dist/modules/terminal_tracking/lib/adapters/index.js.map +7 -0
  87. package/dist/modules/terminal_tracking/lib/adapters/n4/adapter.js +121 -0
  88. package/dist/modules/terminal_tracking/lib/adapters/n4/adapter.js.map +7 -0
  89. package/dist/modules/terminal_tracking/lib/adapters/n4/auth/ropc.js +81 -0
  90. package/dist/modules/terminal_tracking/lib/adapters/n4/auth/ropc.js.map +7 -0
  91. package/dist/modules/terminal_tracking/lib/adapters/n4/http.js +66 -0
  92. package/dist/modules/terminal_tracking/lib/adapters/n4/http.js.map +7 -0
  93. package/dist/modules/terminal_tracking/lib/adapters/n4/n4-semantics.js +95 -0
  94. package/dist/modules/terminal_tracking/lib/adapters/n4/n4-semantics.js.map +7 -0
  95. package/dist/modules/terminal_tracking/lib/adapters/n4/parsers/data-table.js +70 -0
  96. package/dist/modules/terminal_tracking/lib/adapters/n4/parsers/data-table.js.map +7 -0
  97. package/dist/modules/terminal_tracking/lib/adapters/n4/parsers/vessel-data-table.js +44 -0
  98. package/dist/modules/terminal_tracking/lib/adapters/n4/parsers/vessel-data-table.js.map +7 -0
  99. package/dist/modules/terminal_tracking/lib/adapters/n4/proxy.js +15 -0
  100. package/dist/modules/terminal_tracking/lib/adapters/n4/proxy.js.map +7 -0
  101. package/dist/modules/terminal_tracking/lib/adapters/n4/types.js +1 -0
  102. package/dist/modules/terminal_tracking/lib/adapters/n4/types.js.map +7 -0
  103. package/dist/modules/terminal_tracking/lib/adapters/zoned-time.js +27 -0
  104. package/dist/modules/terminal_tracking/lib/adapters/zoned-time.js.map +7 -0
  105. package/dist/modules/terminal_tracking/lib/availability.js +40 -0
  106. package/dist/modules/terminal_tracking/lib/availability.js.map +7 -0
  107. package/dist/modules/terminal_tracking/lib/ensure-schedule.js +51 -0
  108. package/dist/modules/terminal_tracking/lib/ensure-schedule.js.map +7 -0
  109. package/dist/modules/terminal_tracking/lib/holds.js +240 -0
  110. package/dist/modules/terminal_tracking/lib/holds.js.map +7 -0
  111. package/dist/modules/terminal_tracking/lib/logger.js +19 -0
  112. package/dist/modules/terminal_tracking/lib/logger.js.map +7 -0
  113. package/dist/modules/terminal_tracking/lib/rate-limiter.js +30 -0
  114. package/dist/modules/terminal_tracking/lib/rate-limiter.js.map +7 -0
  115. package/dist/modules/terminal_tracking/lib/stops.js +48 -0
  116. package/dist/modules/terminal_tracking/lib/stops.js.map +7 -0
  117. package/dist/modules/terminal_tracking/lib/terminal-adapter.js +1 -0
  118. package/dist/modules/terminal_tracking/lib/terminal-adapter.js.map +7 -0
  119. package/dist/modules/terminal_tracking/lib/totp.js +41 -0
  120. package/dist/modules/terminal_tracking/lib/totp.js.map +7 -0
  121. package/dist/modules/terminal_tracking/migrations/Migration20260609120000_terminal_tracking.js +101 -0
  122. package/dist/modules/terminal_tracking/migrations/Migration20260609120000_terminal_tracking.js.map +7 -0
  123. package/dist/modules/terminal_tracking/migrations/Migration20260610120000_terminal_vessel_visits.js +39 -0
  124. package/dist/modules/terminal_tracking/migrations/Migration20260610120000_terminal_vessel_visits.js.map +7 -0
  125. package/dist/modules/terminal_tracking/migrations/Migration20260611120000_terminal_config_proxy.js +13 -0
  126. package/dist/modules/terminal_tracking/migrations/Migration20260611120000_terminal_config_proxy.js.map +7 -0
  127. package/dist/modules/terminal_tracking/migrations/Migration20260707120000_terminal_job_availability_markers.js +15 -0
  128. package/dist/modules/terminal_tracking/migrations/Migration20260707120000_terminal_job_availability_markers.js.map +7 -0
  129. package/dist/modules/terminal_tracking/migrations/Migration20260805120000_terminal_event_loaded_at.js +13 -0
  130. package/dist/modules/terminal_tracking/migrations/Migration20260805120000_terminal_event_loaded_at.js.map +7 -0
  131. package/dist/modules/terminal_tracking/services/terminalMatcherService.js +61 -0
  132. package/dist/modules/terminal_tracking/services/terminalMatcherService.js.map +7 -0
  133. package/dist/modules/terminal_tracking/services/terminalRegistry.js +24 -0
  134. package/dist/modules/terminal_tracking/services/terminalRegistry.js.map +7 -0
  135. package/dist/modules/terminal_tracking/services/terminalTrackingService.js +687 -0
  136. package/dist/modules/terminal_tracking/services/terminalTrackingService.js.map +7 -0
  137. package/dist/modules/terminal_tracking/setup.js +23 -0
  138. package/dist/modules/terminal_tracking/setup.js.map +7 -0
  139. package/dist/modules/terminal_tracking/subscribers/terminal-tracking-job-initial-poll.js +22 -0
  140. package/dist/modules/terminal_tracking/subscribers/terminal-tracking-job-initial-poll.js.map +7 -0
  141. package/generated/entities/terminal_config/index.ts +36 -0
  142. package/generated/entities/terminal_event/index.ts +39 -0
  143. package/generated/entities/terminal_tracking_job/index.ts +28 -0
  144. package/generated/entities/terminal_vessel_visit/index.ts +30 -0
  145. package/generated/entities.ids.generated.ts +52 -0
  146. package/package.json +80 -0
  147. package/src/index.ts +44 -0
  148. package/src/modules/terminal_tracking/__integration__/TC-TRACK-301-gct-config-test-connection.spec.ts +66 -0
  149. package/src/modules/terminal_tracking/__integration__/TC-TRACK-317-bct-config-test-connection.spec.ts +68 -0
  150. package/src/modules/terminal_tracking/__integration__/helpers.ts +148 -0
  151. package/src/modules/terminal_tracking/__integration__/meta.ts +4 -0
  152. package/src/modules/terminal_tracking/acl.ts +10 -0
  153. package/src/modules/terminal_tracking/api/openapi.ts +28 -0
  154. package/src/modules/terminal_tracking/api/terminal-configs/route.ts +140 -0
  155. package/src/modules/terminal_tracking/api/terminal-configs/test/route.ts +64 -0
  156. package/src/modules/terminal_tracking/api/tracking-jobs/details/route.ts +116 -0
  157. package/src/modules/terminal_tracking/api/tracking-jobs/poll/route.ts +64 -0
  158. package/src/modules/terminal_tracking/api/tracking-jobs/route.ts +243 -0
  159. package/src/modules/terminal_tracking/api/utils.ts +10 -0
  160. package/src/modules/terminal_tracking/api/vessel-visits/route.ts +95 -0
  161. package/src/modules/terminal_tracking/backend/terminal-configs/page.meta.ts +16 -0
  162. package/src/modules/terminal_tracking/backend/terminal-configs/page.tsx +728 -0
  163. package/src/modules/terminal_tracking/backend/terminal-containers/page.meta.ts +16 -0
  164. package/src/modules/terminal_tracking/backend/terminal-containers/page.tsx +328 -0
  165. package/src/modules/terminal_tracking/ce.ts +31 -0
  166. package/src/modules/terminal_tracking/commands/index.ts +3 -0
  167. package/src/modules/terminal_tracking/commands/terminal-configs.ts +132 -0
  168. package/src/modules/terminal_tracking/commands/terminal-tracking.ts +34 -0
  169. package/src/modules/terminal_tracking/commands/tracking-jobs.ts +69 -0
  170. package/src/modules/terminal_tracking/components/ContainerDetailsDrawer.tsx +540 -0
  171. package/src/modules/terminal_tracking/data/entities.ts +386 -0
  172. package/src/modules/terminal_tracking/data/validators.ts +113 -0
  173. package/src/modules/terminal_tracking/di.ts +46 -0
  174. package/src/modules/terminal_tracking/encryption.ts +12 -0
  175. package/src/modules/terminal_tracking/events.ts +48 -0
  176. package/src/modules/terminal_tracking/i18n/de.json +175 -0
  177. package/src/modules/terminal_tracking/i18n/en.json +178 -0
  178. package/src/modules/terminal_tracking/i18n/es.json +175 -0
  179. package/src/modules/terminal_tracking/i18n/pl.json +206 -0
  180. package/src/modules/terminal_tracking/index.ts +13 -0
  181. package/src/modules/terminal_tracking/lib/__tests__/availability.test.ts +136 -0
  182. package/src/modules/terminal_tracking/lib/__tests__/ensure-schedule.test.ts +67 -0
  183. package/src/modules/terminal_tracking/lib/__tests__/holds.test.ts +73 -0
  184. package/src/modules/terminal_tracking/lib/__tests__/stops.test.ts +80 -0
  185. package/src/modules/terminal_tracking/lib/__tests__/totp.test.ts +38 -0
  186. package/src/modules/terminal_tracking/lib/adapters/bct/__tests__/adapter.test.ts +196 -0
  187. package/src/modules/terminal_tracking/lib/adapters/bct/__tests__/incos-semantics.test.ts +303 -0
  188. package/src/modules/terminal_tracking/lib/adapters/bct/adapter.ts +219 -0
  189. package/src/modules/terminal_tracking/lib/adapters/bct/auth/basic.ts +34 -0
  190. package/src/modules/terminal_tracking/lib/adapters/bct/incos-semantics.ts +291 -0
  191. package/src/modules/terminal_tracking/lib/adapters/bct/types.ts +113 -0
  192. package/src/modules/terminal_tracking/lib/adapters/gct/__tests__/adapter.test.ts +238 -0
  193. package/src/modules/terminal_tracking/lib/adapters/gct/__tests__/gct-semantics.test.ts +133 -0
  194. package/src/modules/terminal_tracking/lib/adapters/gct/adapter.ts +152 -0
  195. package/src/modules/terminal_tracking/lib/adapters/gct/auth/token.ts +219 -0
  196. package/src/modules/terminal_tracking/lib/adapters/gct/gct-semantics.ts +136 -0
  197. package/src/modules/terminal_tracking/lib/adapters/gct/types.ts +58 -0
  198. package/src/modules/terminal_tracking/lib/adapters/index.ts +16 -0
  199. package/src/modules/terminal_tracking/lib/adapters/n4/__tests__/adapter-proxy.test.ts +69 -0
  200. package/src/modules/terminal_tracking/lib/adapters/n4/__tests__/data-table.test.ts +179 -0
  201. package/src/modules/terminal_tracking/lib/adapters/n4/__tests__/http.test.ts +98 -0
  202. package/src/modules/terminal_tracking/lib/adapters/n4/__tests__/n4-semantics.test.ts +140 -0
  203. package/src/modules/terminal_tracking/lib/adapters/n4/__tests__/proxy.test.ts +19 -0
  204. package/src/modules/terminal_tracking/lib/adapters/n4/__tests__/vessel-data-table.test.ts +67 -0
  205. package/src/modules/terminal_tracking/lib/adapters/n4/adapter.ts +163 -0
  206. package/src/modules/terminal_tracking/lib/adapters/n4/auth/ropc.ts +104 -0
  207. package/src/modules/terminal_tracking/lib/adapters/n4/http.ts +127 -0
  208. package/src/modules/terminal_tracking/lib/adapters/n4/n4-semantics.ts +156 -0
  209. package/src/modules/terminal_tracking/lib/adapters/n4/parsers/data-table.ts +102 -0
  210. package/src/modules/terminal_tracking/lib/adapters/n4/parsers/vessel-data-table.ts +60 -0
  211. package/src/modules/terminal_tracking/lib/adapters/n4/proxy.ts +24 -0
  212. package/src/modules/terminal_tracking/lib/adapters/n4/types.ts +56 -0
  213. package/src/modules/terminal_tracking/lib/adapters/zoned-time.ts +65 -0
  214. package/src/modules/terminal_tracking/lib/availability.ts +119 -0
  215. package/src/modules/terminal_tracking/lib/ensure-schedule.ts +83 -0
  216. package/src/modules/terminal_tracking/lib/holds.ts +344 -0
  217. package/src/modules/terminal_tracking/lib/logger.ts +21 -0
  218. package/src/modules/terminal_tracking/lib/rate-limiter.ts +52 -0
  219. package/src/modules/terminal_tracking/lib/stops.ts +124 -0
  220. package/src/modules/terminal_tracking/lib/terminal-adapter.ts +136 -0
  221. package/src/modules/terminal_tracking/lib/totp.ts +63 -0
  222. package/src/modules/terminal_tracking/migrations/Migration20260609120000_terminal_tracking.ts +104 -0
  223. package/src/modules/terminal_tracking/migrations/Migration20260610120000_terminal_vessel_visits.ts +40 -0
  224. package/src/modules/terminal_tracking/migrations/Migration20260611120000_terminal_config_proxy.ts +11 -0
  225. package/src/modules/terminal_tracking/migrations/Migration20260707120000_terminal_job_availability_markers.ts +13 -0
  226. package/src/modules/terminal_tracking/migrations/Migration20260805120000_terminal_event_loaded_at.ts +16 -0
  227. package/src/modules/terminal_tracking/services/__tests__/availabilityEmit.test.ts +277 -0
  228. package/src/modules/terminal_tracking/services/__tests__/createJob.test.ts +59 -0
  229. package/src/modules/terminal_tracking/services/__tests__/pollAllBatch.test.ts +147 -0
  230. package/src/modules/terminal_tracking/services/__tests__/terminalMatcher.test.ts +70 -0
  231. package/src/modules/terminal_tracking/services/__tests__/vesselResolution.test.ts +453 -0
  232. package/src/modules/terminal_tracking/services/terminalMatcherService.ts +97 -0
  233. package/src/modules/terminal_tracking/services/terminalRegistry.ts +30 -0
  234. package/src/modules/terminal_tracking/services/terminalTrackingService.ts +912 -0
  235. package/src/modules/terminal_tracking/setup.ts +24 -0
  236. package/src/modules/terminal_tracking/subscribers/terminal-tracking-job-initial-poll.ts +32 -0
@@ -0,0 +1,41 @@
1
+ import crypto from "crypto";
2
+ const BASE32_ALPHABET = "ABCDEFGHIJKLMNOPQRSTUVWXYZ234567";
3
+ function base32Decode(input) {
4
+ const clean = input.replace(/=+$/g, "").replace(/\s+/g, "").toUpperCase();
5
+ let bits = 0;
6
+ let value = 0;
7
+ const bytes = [];
8
+ for (const ch of clean) {
9
+ const idx = BASE32_ALPHABET.indexOf(ch);
10
+ if (idx === -1) throw new Error("totp: invalid base32 character in secret");
11
+ value = value << 5 | idx;
12
+ bits += 5;
13
+ if (bits >= 8) {
14
+ bytes.push(value >>> bits - 8 & 255);
15
+ bits -= 8;
16
+ }
17
+ }
18
+ return Buffer.from(bytes);
19
+ }
20
+ const TOTP_PERIOD_SECONDS = 30;
21
+ const TOTP_DIGITS = 6;
22
+ function hotp(secret, counter) {
23
+ const buf = Buffer.alloc(8);
24
+ buf.writeUInt32BE(Math.floor(counter / 4294967296), 0);
25
+ buf.writeUInt32BE(counter >>> 0, 4);
26
+ const hmac = crypto.createHmac("sha1", secret).update(buf).digest();
27
+ const offset = hmac[hmac.length - 1] & 15;
28
+ const binary = (hmac[offset] & 127) << 24 | (hmac[offset + 1] & 255) << 16 | (hmac[offset + 2] & 255) << 8 | hmac[offset + 3] & 255;
29
+ return (binary % 10 ** TOTP_DIGITS).toString().padStart(TOTP_DIGITS, "0");
30
+ }
31
+ function generateTotp(base32Secret, nowMs = Date.now()) {
32
+ const counter = Math.floor(nowMs / 1e3 / TOTP_PERIOD_SECONDS);
33
+ return hotp(base32Decode(base32Secret), counter);
34
+ }
35
+ export {
36
+ TOTP_DIGITS,
37
+ TOTP_PERIOD_SECONDS,
38
+ base32Decode,
39
+ generateTotp
40
+ };
41
+ //# sourceMappingURL=totp.js.map
@@ -0,0 +1,7 @@
1
+ {
2
+ "version": 3,
3
+ "sources": ["../../../../src/modules/terminal_tracking/lib/totp.ts"],
4
+ "sourcesContent": ["/**\n * TOTP (RFC 6238) code generation for terminal adapters whose login expects an\n * authenticator one-time code in place of a static password (e.g. GCT).\n *\n * This is a deliberate, self-contained COPY of the generation half of\n * `packages/two-factor/src/modules/two_factor/lib/totp.ts` \u2014 kept in sync by\n * hand. We copy rather than depend on `@freighttech/two-factor` so this backend\n * tracking module stays dependency-light (only `fuse.js` + `undici`) and free of\n * that package's Next/React/mikro-orm peer deps. The algorithm is pure Node\n * `crypto`; only the consume-side pieces are copied (base32 decode + HOTP +\n * `generateTotp`) \u2014 enrollment helpers (secret/QR generation, verification) live\n * in the two-factor package and are not needed here.\n *\n * Parameters match a standard authenticator app: SHA1, 6 digits, 30s period.\n */\nimport crypto from 'crypto'\n\n// \u2500\u2500 Base32 (RFC 4648, no padding) \u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\nconst BASE32_ALPHABET = 'ABCDEFGHIJKLMNOPQRSTUVWXYZ234567'\n\n/** Decode a base32 authenticator secret into its raw key bytes. */\nexport function base32Decode(input: string): Buffer {\n const clean = input.replace(/=+$/g, '').replace(/\\s+/g, '').toUpperCase()\n let bits = 0\n let value = 0\n const bytes: number[] = []\n for (const ch of clean) {\n const idx = BASE32_ALPHABET.indexOf(ch)\n if (idx === -1) throw new Error('totp: invalid base32 character in secret')\n value = (value << 5) | idx\n bits += 5\n if (bits >= 8) {\n bytes.push((value >>> (bits - 8)) & 0xff)\n bits -= 8\n }\n }\n return Buffer.from(bytes)\n}\n\n// \u2500\u2500 TOTP (RFC 6238) over HOTP (RFC 4226) \u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\nexport const TOTP_PERIOD_SECONDS = 30\nexport const TOTP_DIGITS = 6\n\nfunction hotp(secret: Buffer, counter: number): string {\n const buf = Buffer.alloc(8)\n // 64-bit big-endian counter (JS bitops are 32-bit, so split hi/lo).\n buf.writeUInt32BE(Math.floor(counter / 0x100000000), 0)\n buf.writeUInt32BE(counter >>> 0, 4)\n const hmac = crypto.createHmac('sha1', secret).update(buf).digest()\n const offset = hmac[hmac.length - 1] & 0xf\n const binary =\n ((hmac[offset] & 0x7f) << 24) |\n ((hmac[offset + 1] & 0xff) << 16) |\n ((hmac[offset + 2] & 0xff) << 8) |\n (hmac[offset + 3] & 0xff)\n return (binary % 10 ** TOTP_DIGITS).toString().padStart(TOTP_DIGITS, '0')\n}\n\n/** Compute the current TOTP code for a base32 secret (default time: now). */\nexport function generateTotp(base32Secret: string, nowMs: number = Date.now()): string {\n const counter = Math.floor(nowMs / 1000 / TOTP_PERIOD_SECONDS)\n return hotp(base32Decode(base32Secret), counter)\n}\n"],
5
+ "mappings": "AAeA,OAAO,YAAY;AAGnB,MAAM,kBAAkB;AAGjB,SAAS,aAAa,OAAuB;AAClD,QAAM,QAAQ,MAAM,QAAQ,QAAQ,EAAE,EAAE,QAAQ,QAAQ,EAAE,EAAE,YAAY;AACxE,MAAI,OAAO;AACX,MAAI,QAAQ;AACZ,QAAM,QAAkB,CAAC;AACzB,aAAW,MAAM,OAAO;AACtB,UAAM,MAAM,gBAAgB,QAAQ,EAAE;AACtC,QAAI,QAAQ,GAAI,OAAM,IAAI,MAAM,0CAA0C;AAC1E,YAAS,SAAS,IAAK;AACvB,YAAQ;AACR,QAAI,QAAQ,GAAG;AACb,YAAM,KAAM,UAAW,OAAO,IAAM,GAAI;AACxC,cAAQ;AAAA,IACV;AAAA,EACF;AACA,SAAO,OAAO,KAAK,KAAK;AAC1B;AAGO,MAAM,sBAAsB;AAC5B,MAAM,cAAc;AAE3B,SAAS,KAAK,QAAgB,SAAyB;AACrD,QAAM,MAAM,OAAO,MAAM,CAAC;AAE1B,MAAI,cAAc,KAAK,MAAM,UAAU,UAAW,GAAG,CAAC;AACtD,MAAI,cAAc,YAAY,GAAG,CAAC;AAClC,QAAM,OAAO,OAAO,WAAW,QAAQ,MAAM,EAAE,OAAO,GAAG,EAAE,OAAO;AAClE,QAAM,SAAS,KAAK,KAAK,SAAS,CAAC,IAAI;AACvC,QAAM,UACF,KAAK,MAAM,IAAI,QAAS,MACxB,KAAK,SAAS,CAAC,IAAI,QAAS,MAC5B,KAAK,SAAS,CAAC,IAAI,QAAS,IAC7B,KAAK,SAAS,CAAC,IAAI;AACtB,UAAQ,SAAS,MAAM,aAAa,SAAS,EAAE,SAAS,aAAa,GAAG;AAC1E;AAGO,SAAS,aAAa,cAAsB,QAAgB,KAAK,IAAI,GAAW;AACrF,QAAM,UAAU,KAAK,MAAM,QAAQ,MAAO,mBAAmB;AAC7D,SAAO,KAAK,aAAa,YAAY,GAAG,OAAO;AACjD;",
6
+ "names": []
7
+ }
@@ -0,0 +1,101 @@
1
+ import { Migration } from "@mikro-orm/migrations";
2
+ class Migration20260609120000_terminal_tracking extends Migration {
3
+ async up() {
4
+ this.addSql(`create table if not exists "terminal_tracking_terminal_configs" (
5
+ "id" uuid not null default gen_random_uuid(),
6
+ "organization_id" uuid not null,
7
+ "tenant_id" uuid not null,
8
+ "terminal_code" text not null,
9
+ "adapter_type" text not null,
10
+ "display_name" text not null,
11
+ "base_url" text not null,
12
+ "endpoints" jsonb not null,
13
+ "auth_type" text not null,
14
+ "token_url" text null,
15
+ "scope" text null,
16
+ "client_id" text null,
17
+ "auth_config" jsonb null,
18
+ "rate_limit_requests" integer not null default 200,
19
+ "rate_limit_window_seconds" integer not null default 60,
20
+ "unlocode" varchar(5) null,
21
+ "bic_codes" jsonb null,
22
+ "smdg_codes" jsonb null,
23
+ "name_aliases" jsonb null,
24
+ "is_active" boolean not null default true,
25
+ "created_at" timestamptz not null default now(),
26
+ "updated_at" timestamptz not null default now(),
27
+ "deleted_at" timestamptz null,
28
+ constraint "terminal_tracking_terminal_configs_pkey" primary key ("id")
29
+ );`);
30
+ this.addSql(`create index if not exists "tt_terminal_configs_org_tenant_idx" on "terminal_tracking_terminal_configs" ("organization_id", "tenant_id");`);
31
+ this.addSql(`create unique index if not exists "tt_terminal_configs_code_uniq" on "terminal_tracking_terminal_configs" ("organization_id", "tenant_id", "terminal_code");`);
32
+ this.addSql(`create table if not exists "terminal_tracking_jobs" (
33
+ "id" uuid not null default gen_random_uuid(),
34
+ "organization_id" uuid not null,
35
+ "tenant_id" uuid not null,
36
+ "terminal_code" text not null,
37
+ "container_number" text not null,
38
+ "status" text not null default 'active',
39
+ "schedule" jsonb null,
40
+ "next_poll_at" timestamptz null,
41
+ "last_poll_at" timestamptz null,
42
+ "retry_count" integer not null default 0,
43
+ "error_history" jsonb null,
44
+ "created_at" timestamptz not null default now(),
45
+ "updated_at" timestamptz not null default now(),
46
+ "deleted_at" timestamptz null,
47
+ constraint "terminal_tracking_jobs_pkey" primary key ("id")
48
+ );`);
49
+ this.addSql(`create index if not exists "tt_jobs_org_tenant_idx" on "terminal_tracking_jobs" ("organization_id", "tenant_id");`);
50
+ this.addSql(`create index if not exists "tt_jobs_status_idx" on "terminal_tracking_jobs" ("status");`);
51
+ this.addSql(`create index if not exists "tt_jobs_next_poll_idx" on "terminal_tracking_jobs" ("next_poll_at");`);
52
+ this.addSql(`create index if not exists "tt_jobs_container_idx" on "terminal_tracking_jobs" ("container_number");`);
53
+ this.addSql(`create table if not exists "terminal_tracking_events" (
54
+ "id" uuid not null default gen_random_uuid(),
55
+ "organization_id" uuid not null,
56
+ "tenant_id" uuid not null,
57
+ "job_id" uuid not null,
58
+ "source" text not null default 'terminal',
59
+ "source_event_id" text not null,
60
+ "event_type" text not null,
61
+ "event_code" text not null,
62
+ "event_classifier_code" text null,
63
+ "event_date_time" timestamptz not null,
64
+ "container_number" text not null,
65
+ "ufv_gkey" text not null,
66
+ "transit_state" text null,
67
+ "visit_state" text null,
68
+ "facility_code" text null,
69
+ "facility_code_list_provider" text null,
70
+ "unlocode" varchar(5) null,
71
+ "visit_ref_in" text null,
72
+ "visit_ref_out" text null,
73
+ "vessel_name" text null,
74
+ "voyage_number" text null,
75
+ "mode_of_transport" text null,
76
+ "seals" jsonb null,
77
+ "vgm_weight_kg" double precision null,
78
+ "impediments" jsonb null,
79
+ "raw_data" jsonb null,
80
+ "created_at" timestamptz not null default now(),
81
+ constraint "terminal_tracking_events_pkey" primary key ("id")
82
+ );`);
83
+ this.addSql(`do $$ begin alter table "terminal_tracking_events"
84
+ add constraint "terminal_tracking_events_job_id_foreign"
85
+ foreign key ("job_id") references "terminal_tracking_jobs" ("id") on update cascade on delete cascade; exception when duplicate_object then null; when duplicate_table then null; end $$;`);
86
+ this.addSql(`create index if not exists "tt_events_org_tenant_idx" on "terminal_tracking_events" ("organization_id", "tenant_id");`);
87
+ this.addSql(`create index if not exists "tt_events_job_idx" on "terminal_tracking_events" ("job_id");`);
88
+ this.addSql(`create index if not exists "tt_events_container_idx" on "terminal_tracking_events" ("container_number");`);
89
+ this.addSql(`create index if not exists "tt_events_event_date_idx" on "terminal_tracking_events" ("event_date_time");`);
90
+ this.addSql(`create unique index if not exists "tt_events_source_event_uniq" on "terminal_tracking_events" ("job_id", "source", "source_event_id");`);
91
+ }
92
+ async down() {
93
+ this.addSql(`drop table if exists "terminal_tracking_events" cascade;`);
94
+ this.addSql(`drop table if exists "terminal_tracking_jobs" cascade;`);
95
+ this.addSql(`drop table if exists "terminal_tracking_terminal_configs" cascade;`);
96
+ }
97
+ }
98
+ export {
99
+ Migration20260609120000_terminal_tracking
100
+ };
101
+ //# sourceMappingURL=Migration20260609120000_terminal_tracking.js.map
@@ -0,0 +1,7 @@
1
+ {
2
+ "version": 3,
3
+ "sources": ["../../../../src/modules/terminal_tracking/migrations/Migration20260609120000_terminal_tracking.ts"],
4
+ "sourcesContent": ["import { Migration } from '@mikro-orm/migrations'\n\nexport class Migration20260609120000_terminal_tracking extends Migration {\n override async up(): Promise<void> {\n // \u2500\u2500\u2500 terminal_tracking_terminal_configs \u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\n this.addSql(`create table if not exists \"terminal_tracking_terminal_configs\" (\n \"id\" uuid not null default gen_random_uuid(),\n \"organization_id\" uuid not null,\n \"tenant_id\" uuid not null,\n \"terminal_code\" text not null,\n \"adapter_type\" text not null,\n \"display_name\" text not null,\n \"base_url\" text not null,\n \"endpoints\" jsonb not null,\n \"auth_type\" text not null,\n \"token_url\" text null,\n \"scope\" text null,\n \"client_id\" text null,\n \"auth_config\" jsonb null,\n \"rate_limit_requests\" integer not null default 200,\n \"rate_limit_window_seconds\" integer not null default 60,\n \"unlocode\" varchar(5) null,\n \"bic_codes\" jsonb null,\n \"smdg_codes\" jsonb null,\n \"name_aliases\" jsonb null,\n \"is_active\" boolean not null default true,\n \"created_at\" timestamptz not null default now(),\n \"updated_at\" timestamptz not null default now(),\n \"deleted_at\" timestamptz null,\n constraint \"terminal_tracking_terminal_configs_pkey\" primary key (\"id\")\n );`)\n this.addSql(`create index if not exists \"tt_terminal_configs_org_tenant_idx\" on \"terminal_tracking_terminal_configs\" (\"organization_id\", \"tenant_id\");`)\n this.addSql(`create unique index if not exists \"tt_terminal_configs_code_uniq\" on \"terminal_tracking_terminal_configs\" (\"organization_id\", \"tenant_id\", \"terminal_code\");`)\n\n // \u2500\u2500\u2500 terminal_tracking_jobs \u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\n this.addSql(`create table if not exists \"terminal_tracking_jobs\" (\n \"id\" uuid not null default gen_random_uuid(),\n \"organization_id\" uuid not null,\n \"tenant_id\" uuid not null,\n \"terminal_code\" text not null,\n \"container_number\" text not null,\n \"status\" text not null default 'active',\n \"schedule\" jsonb null,\n \"next_poll_at\" timestamptz null,\n \"last_poll_at\" timestamptz null,\n \"retry_count\" integer not null default 0,\n \"error_history\" jsonb null,\n \"created_at\" timestamptz not null default now(),\n \"updated_at\" timestamptz not null default now(),\n \"deleted_at\" timestamptz null,\n constraint \"terminal_tracking_jobs_pkey\" primary key (\"id\")\n );`)\n this.addSql(`create index if not exists \"tt_jobs_org_tenant_idx\" on \"terminal_tracking_jobs\" (\"organization_id\", \"tenant_id\");`)\n this.addSql(`create index if not exists \"tt_jobs_status_idx\" on \"terminal_tracking_jobs\" (\"status\");`)\n this.addSql(`create index if not exists \"tt_jobs_next_poll_idx\" on \"terminal_tracking_jobs\" (\"next_poll_at\");`)\n this.addSql(`create index if not exists \"tt_jobs_container_idx\" on \"terminal_tracking_jobs\" (\"container_number\");`)\n\n // \u2500\u2500\u2500 terminal_tracking_events \u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\n this.addSql(`create table if not exists \"terminal_tracking_events\" (\n \"id\" uuid not null default gen_random_uuid(),\n \"organization_id\" uuid not null,\n \"tenant_id\" uuid not null,\n \"job_id\" uuid not null,\n \"source\" text not null default 'terminal',\n \"source_event_id\" text not null,\n \"event_type\" text not null,\n \"event_code\" text not null,\n \"event_classifier_code\" text null,\n \"event_date_time\" timestamptz not null,\n \"container_number\" text not null,\n \"ufv_gkey\" text not null,\n \"transit_state\" text null,\n \"visit_state\" text null,\n \"facility_code\" text null,\n \"facility_code_list_provider\" text null,\n \"unlocode\" varchar(5) null,\n \"visit_ref_in\" text null,\n \"visit_ref_out\" text null,\n \"vessel_name\" text null,\n \"voyage_number\" text null,\n \"mode_of_transport\" text null,\n \"seals\" jsonb null,\n \"vgm_weight_kg\" double precision null,\n \"impediments\" jsonb null,\n \"raw_data\" jsonb null,\n \"created_at\" timestamptz not null default now(),\n constraint \"terminal_tracking_events_pkey\" primary key (\"id\")\n );`)\n this.addSql(`do $$ begin alter table \"terminal_tracking_events\"\n add constraint \"terminal_tracking_events_job_id_foreign\"\n foreign key (\"job_id\") references \"terminal_tracking_jobs\" (\"id\") on update cascade on delete cascade; exception when duplicate_object then null; when duplicate_table then null; end $$;`)\n this.addSql(`create index if not exists \"tt_events_org_tenant_idx\" on \"terminal_tracking_events\" (\"organization_id\", \"tenant_id\");`)\n this.addSql(`create index if not exists \"tt_events_job_idx\" on \"terminal_tracking_events\" (\"job_id\");`)\n this.addSql(`create index if not exists \"tt_events_container_idx\" on \"terminal_tracking_events\" (\"container_number\");`)\n this.addSql(`create index if not exists \"tt_events_event_date_idx\" on \"terminal_tracking_events\" (\"event_date_time\");`)\n this.addSql(`create unique index if not exists \"tt_events_source_event_uniq\" on \"terminal_tracking_events\" (\"job_id\", \"source\", \"source_event_id\");`)\n }\n\n override async down(): Promise<void> {\n this.addSql(`drop table if exists \"terminal_tracking_events\" cascade;`)\n this.addSql(`drop table if exists \"terminal_tracking_jobs\" cascade;`)\n this.addSql(`drop table if exists \"terminal_tracking_terminal_configs\" cascade;`)\n }\n}\n"],
5
+ "mappings": "AAAA,SAAS,iBAAiB;AAEnB,MAAM,kDAAkD,UAAU;AAAA,EACvE,MAAe,KAAoB;AAEjC,SAAK,OAAO;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA,OAyBT;AACH,SAAK,OAAO,2IAA2I;AACvJ,SAAK,OAAO,8JAA8J;AAG1K,SAAK,OAAO;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA,OAgBT;AACH,SAAK,OAAO,mHAAmH;AAC/H,SAAK,OAAO,yFAAyF;AACrG,SAAK,OAAO,kGAAkG;AAC9G,SAAK,OAAO,sGAAsG;AAGlH,SAAK,OAAO;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA,OA6BT;AACH,SAAK,OAAO;AAAA;AAAA,gMAEgL;AAC5L,SAAK,OAAO,uHAAuH;AACnI,SAAK,OAAO,0FAA0F;AACtG,SAAK,OAAO,0GAA0G;AACtH,SAAK,OAAO,0GAA0G;AACtH,SAAK,OAAO,wIAAwI;AAAA,EACtJ;AAAA,EAEA,MAAe,OAAsB;AACnC,SAAK,OAAO,0DAA0D;AACtE,SAAK,OAAO,wDAAwD;AACpE,SAAK,OAAO,oEAAoE;AAAA,EAClF;AACF;",
6
+ "names": []
7
+ }
@@ -0,0 +1,39 @@
1
+ import { Migration } from "@mikro-orm/migrations";
2
+ class Migration20260610120000_terminal_vessel_visits extends Migration {
3
+ async up() {
4
+ this.addSql(`create table if not exists "terminal_tracking_vessel_visits" (
5
+ "id" uuid not null default gen_random_uuid(),
6
+ "organization_id" uuid not null,
7
+ "tenant_id" uuid not null,
8
+ "terminal_code" text not null,
9
+ "visit_ref" text not null,
10
+ "vessel_name" text null,
11
+ "ib_voyage" text null,
12
+ "ob_voyage" text null,
13
+ "line" text null,
14
+ "phase" text null,
15
+ "eta" timestamptz null,
16
+ "etd" timestamptz null,
17
+ "ata" timestamptz null,
18
+ "atd" timestamptz null,
19
+ "begin_receive" timestamptz null,
20
+ "dry_cutoff" timestamptz null,
21
+ "raw_data" jsonb null,
22
+ "created_at" timestamptz not null default now(),
23
+ "updated_at" timestamptz not null default now(),
24
+ constraint "terminal_tracking_vessel_visits_pkey" primary key ("id")
25
+ );`);
26
+ this.addSql(`create index if not exists "tt_vessel_visits_org_tenant_idx" on "terminal_tracking_vessel_visits" ("organization_id", "tenant_id");`);
27
+ this.addSql(`create index if not exists "tt_vessel_visits_visit_ref_idx" on "terminal_tracking_vessel_visits" ("visit_ref");`);
28
+ this.addSql(`create unique index if not exists "tt_vessel_visits_uniq" on "terminal_tracking_vessel_visits" ("organization_id", "tenant_id", "terminal_code", "visit_ref");`);
29
+ this.addSql(`alter table "terminal_tracking_terminal_configs" add column if not exists "vessel_cache_ttl_seconds" integer null;`);
30
+ }
31
+ async down() {
32
+ this.addSql(`alter table "terminal_tracking_terminal_configs" drop column if exists "vessel_cache_ttl_seconds";`);
33
+ this.addSql(`drop table if exists "terminal_tracking_vessel_visits" cascade;`);
34
+ }
35
+ }
36
+ export {
37
+ Migration20260610120000_terminal_vessel_visits
38
+ };
39
+ //# sourceMappingURL=Migration20260610120000_terminal_vessel_visits.js.map
@@ -0,0 +1,7 @@
1
+ {
2
+ "version": 3,
3
+ "sources": ["../../../../src/modules/terminal_tracking/migrations/Migration20260610120000_terminal_vessel_visits.ts"],
4
+ "sourcesContent": ["import { Migration } from '@mikro-orm/migrations'\n\nexport class Migration20260610120000_terminal_vessel_visits extends Migration {\n override async up(): Promise<void> {\n // \u2500\u2500\u2500 terminal_tracking_vessel_visits \u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\n this.addSql(`create table if not exists \"terminal_tracking_vessel_visits\" (\n \"id\" uuid not null default gen_random_uuid(),\n \"organization_id\" uuid not null,\n \"tenant_id\" uuid not null,\n \"terminal_code\" text not null,\n \"visit_ref\" text not null,\n \"vessel_name\" text null,\n \"ib_voyage\" text null,\n \"ob_voyage\" text null,\n \"line\" text null,\n \"phase\" text null,\n \"eta\" timestamptz null,\n \"etd\" timestamptz null,\n \"ata\" timestamptz null,\n \"atd\" timestamptz null,\n \"begin_receive\" timestamptz null,\n \"dry_cutoff\" timestamptz null,\n \"raw_data\" jsonb null,\n \"created_at\" timestamptz not null default now(),\n \"updated_at\" timestamptz not null default now(),\n constraint \"terminal_tracking_vessel_visits_pkey\" primary key (\"id\")\n );`)\n this.addSql(`create index if not exists \"tt_vessel_visits_org_tenant_idx\" on \"terminal_tracking_vessel_visits\" (\"organization_id\", \"tenant_id\");`)\n this.addSql(`create index if not exists \"tt_vessel_visits_visit_ref_idx\" on \"terminal_tracking_vessel_visits\" (\"visit_ref\");`)\n this.addSql(`create unique index if not exists \"tt_vessel_visits_uniq\" on \"terminal_tracking_vessel_visits\" (\"organization_id\", \"tenant_id\", \"terminal_code\", \"visit_ref\");`)\n\n // \u2500\u2500\u2500 terminal_tracking_terminal_configs: vessel cache TTL \u2500\n this.addSql(`alter table \"terminal_tracking_terminal_configs\" add column if not exists \"vessel_cache_ttl_seconds\" integer null;`)\n }\n\n override async down(): Promise<void> {\n this.addSql(`alter table \"terminal_tracking_terminal_configs\" drop column if exists \"vessel_cache_ttl_seconds\";`)\n this.addSql(`drop table if exists \"terminal_tracking_vessel_visits\" cascade;`)\n }\n}\n"],
5
+ "mappings": "AAAA,SAAS,iBAAiB;AAEnB,MAAM,uDAAuD,UAAU;AAAA,EAC5E,MAAe,KAAoB;AAEjC,SAAK,OAAO;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA,OAqBT;AACH,SAAK,OAAO,qIAAqI;AACjJ,SAAK,OAAO,iHAAiH;AAC7H,SAAK,OAAO,gKAAgK;AAG5K,SAAK,OAAO,oHAAoH;AAAA,EAClI;AAAA,EAEA,MAAe,OAAsB;AACnC,SAAK,OAAO,oGAAoG;AAChH,SAAK,OAAO,iEAAiE;AAAA,EAC/E;AACF;",
6
+ "names": []
7
+ }
@@ -0,0 +1,13 @@
1
+ import { Migration } from "@mikro-orm/migrations";
2
+ class Migration20260611120000_terminal_config_proxy extends Migration {
3
+ async up() {
4
+ this.addSql(`alter table "terminal_tracking_terminal_configs" add column if not exists "proxy_url" text null;`);
5
+ }
6
+ async down() {
7
+ this.addSql(`alter table "terminal_tracking_terminal_configs" drop column if exists "proxy_url";`);
8
+ }
9
+ }
10
+ export {
11
+ Migration20260611120000_terminal_config_proxy
12
+ };
13
+ //# sourceMappingURL=Migration20260611120000_terminal_config_proxy.js.map
@@ -0,0 +1,7 @@
1
+ {
2
+ "version": 3,
3
+ "sources": ["../../../../src/modules/terminal_tracking/migrations/Migration20260611120000_terminal_config_proxy.ts"],
4
+ "sourcesContent": ["import { Migration } from '@mikro-orm/migrations'\n\nexport class Migration20260611120000_terminal_config_proxy extends Migration {\n override async up(): Promise<void> {\n this.addSql(`alter table \"terminal_tracking_terminal_configs\" add column if not exists \"proxy_url\" text null;`)\n }\n\n override async down(): Promise<void> {\n this.addSql(`alter table \"terminal_tracking_terminal_configs\" drop column if exists \"proxy_url\";`)\n }\n}\n"],
5
+ "mappings": "AAAA,SAAS,iBAAiB;AAEnB,MAAM,sDAAsD,UAAU;AAAA,EAC3E,MAAe,KAAoB;AACjC,SAAK,OAAO,kGAAkG;AAAA,EAChH;AAAA,EAEA,MAAe,OAAsB;AACnC,SAAK,OAAO,qFAAqF;AAAA,EACnG;AACF;",
6
+ "names": []
7
+ }
@@ -0,0 +1,15 @@
1
+ import { Migration } from "@mikro-orm/migrations";
2
+ class Migration20260707120000_terminal_job_availability_markers extends Migration {
3
+ async up() {
4
+ this.addSql(`alter table "terminal_tracking_jobs" add column if not exists "empty_ready_at" timestamptz null;`);
5
+ this.addSql(`alter table "terminal_tracking_jobs" add column if not exists "holds_cleared_at" timestamptz null;`);
6
+ }
7
+ async down() {
8
+ this.addSql(`alter table "terminal_tracking_jobs" drop column if exists "empty_ready_at";`);
9
+ this.addSql(`alter table "terminal_tracking_jobs" drop column if exists "holds_cleared_at";`);
10
+ }
11
+ }
12
+ export {
13
+ Migration20260707120000_terminal_job_availability_markers
14
+ };
15
+ //# sourceMappingURL=Migration20260707120000_terminal_job_availability_markers.js.map
@@ -0,0 +1,7 @@
1
+ {
2
+ "version": 3,
3
+ "sources": ["../../../../src/modules/terminal_tracking/migrations/Migration20260707120000_terminal_job_availability_markers.ts"],
4
+ "sourcesContent": ["import { Migration } from '@mikro-orm/migrations'\n\nexport class Migration20260707120000_terminal_job_availability_markers extends Migration {\n override async up(): Promise<void> {\n this.addSql(`alter table \"terminal_tracking_jobs\" add column if not exists \"empty_ready_at\" timestamptz null;`)\n this.addSql(`alter table \"terminal_tracking_jobs\" add column if not exists \"holds_cleared_at\" timestamptz null;`)\n }\n\n override async down(): Promise<void> {\n this.addSql(`alter table \"terminal_tracking_jobs\" drop column if exists \"empty_ready_at\";`)\n this.addSql(`alter table \"terminal_tracking_jobs\" drop column if exists \"holds_cleared_at\";`)\n }\n}\n"],
5
+ "mappings": "AAAA,SAAS,iBAAiB;AAEnB,MAAM,kEAAkE,UAAU;AAAA,EACvF,MAAe,KAAoB;AACjC,SAAK,OAAO,kGAAkG;AAC9G,SAAK,OAAO,oGAAoG;AAAA,EAClH;AAAA,EAEA,MAAe,OAAsB;AACnC,SAAK,OAAO,8EAA8E;AAC1F,SAAK,OAAO,gFAAgF;AAAA,EAC9F;AACF;",
6
+ "names": []
7
+ }
@@ -0,0 +1,13 @@
1
+ import { Migration } from "@mikro-orm/migrations";
2
+ class Migration20260805120000_terminal_event_loaded_at extends Migration {
3
+ async up() {
4
+ this.addSql(`alter table "terminal_tracking_events" add column if not exists "loaded_at" timestamptz null;`);
5
+ }
6
+ async down() {
7
+ this.addSql(`alter table "terminal_tracking_events" drop column if exists "loaded_at";`);
8
+ }
9
+ }
10
+ export {
11
+ Migration20260805120000_terminal_event_loaded_at
12
+ };
13
+ //# sourceMappingURL=Migration20260805120000_terminal_event_loaded_at.js.map
@@ -0,0 +1,7 @@
1
+ {
2
+ "version": 3,
3
+ "sources": ["../../../../src/modules/terminal_tracking/migrations/Migration20260805120000_terminal_event_loaded_at.ts"],
4
+ "sourcesContent": ["import { Migration } from '@mikro-orm/migrations'\n\n/**\n * Additive-only: a nullable `loaded_at` timestamp on terminal_tracking_events.\n * The N4 `Loaded` column parsed into a typed datetime, captured on every event\n * regardless of the container's current transit state.\n */\nexport class Migration20260805120000_terminal_event_loaded_at extends Migration {\n override async up(): Promise<void> {\n this.addSql(`alter table \"terminal_tracking_events\" add column if not exists \"loaded_at\" timestamptz null;`)\n }\n\n override async down(): Promise<void> {\n this.addSql(`alter table \"terminal_tracking_events\" drop column if exists \"loaded_at\";`)\n }\n}\n"],
5
+ "mappings": "AAAA,SAAS,iBAAiB;AAOnB,MAAM,yDAAyD,UAAU;AAAA,EAC9E,MAAe,KAAoB;AACjC,SAAK,OAAO,+FAA+F;AAAA,EAC7G;AAAA,EAEA,MAAe,OAAsB;AACnC,SAAK,OAAO,2EAA2E;AAAA,EACzF;AACF;",
6
+ "names": []
7
+ }
@@ -0,0 +1,61 @@
1
+ import Fuse from "fuse.js";
2
+ import { TerminalConfig } from "../data/entities.js";
3
+ class TerminalMatcherService {
4
+ constructor(deps) {
5
+ this.deps = deps;
6
+ }
7
+ async candidates(scope) {
8
+ const em = this.deps.em().fork();
9
+ return em.find(TerminalConfig, {
10
+ organizationId: scope.organizationId,
11
+ tenantId: scope.tenantId,
12
+ isActive: true,
13
+ deletedAt: null
14
+ });
15
+ }
16
+ async resolveFacility(input, scope) {
17
+ const configs = await this.candidates(scope);
18
+ if (configs.length === 0) return null;
19
+ const norm = (s) => s.trim().toUpperCase();
20
+ if (input.bic) {
21
+ const bic = norm(input.bic);
22
+ const hit = configs.find((c) => (c.bicCodes ?? []).some((x) => norm(x) === bic));
23
+ if (hit) return { terminalCode: hit.terminalCode, displayName: hit.displayName, matchedBy: "bic" };
24
+ }
25
+ if (input.smdg) {
26
+ const smdg = norm(input.smdg);
27
+ const hit = configs.find((c) => (c.smdgCodes ?? []).some((x) => norm(x) === smdg));
28
+ if (hit) return { terminalCode: hit.terminalCode, displayName: hit.displayName, matchedBy: "smdg" };
29
+ }
30
+ if (input.unlocode) {
31
+ const unlocode = norm(input.unlocode);
32
+ const hit = configs.find((c) => c.unlocode && norm(c.unlocode) === unlocode);
33
+ if (hit) return { terminalCode: hit.terminalCode, displayName: hit.displayName, matchedBy: "unlocode" };
34
+ }
35
+ if (input.name && input.name.trim()) {
36
+ const docs = configs.flatMap(
37
+ (c) => [c.displayName, ...c.nameAliases ?? []].map((alias) => ({ config: c, alias }))
38
+ );
39
+ const fuse = new Fuse(docs, {
40
+ keys: ["alias"],
41
+ includeScore: true,
42
+ threshold: 0.3,
43
+ ignoreLocation: true
44
+ });
45
+ const [best] = fuse.search(input.name.trim());
46
+ if (best) {
47
+ return {
48
+ terminalCode: best.item.config.terminalCode,
49
+ displayName: best.item.config.displayName,
50
+ matchedBy: "name",
51
+ score: best.score
52
+ };
53
+ }
54
+ }
55
+ return null;
56
+ }
57
+ }
58
+ export {
59
+ TerminalMatcherService
60
+ };
61
+ //# sourceMappingURL=terminalMatcherService.js.map
@@ -0,0 +1,7 @@
1
+ {
2
+ "version": 3,
3
+ "sources": ["../../../../src/modules/terminal_tracking/services/terminalMatcherService.ts"],
4
+ "sourcesContent": ["import Fuse from 'fuse.js'\nimport type { EntityManager } from '@mikro-orm/postgresql'\nimport { TerminalConfig } from '../data/entities'\n\nexport type FacilityMatchInput = {\n name?: string | null\n bic?: string | null\n smdg?: string | null\n unlocode?: string | null\n}\n\nexport type MatchScope = {\n organizationId: string\n tenantId: string\n}\n\nexport type TerminalMatch = {\n terminalCode: string\n displayName: string\n matchedBy: 'bic' | 'smdg' | 'unlocode' | 'name'\n score?: number\n}\n\ntype Deps = {\n em: () => EntityManager\n}\n\n/**\n * Resolves a facility (by exact BIC / SMDG / UNLOCODE or fuzzy name) to one of\n * the tenant's configured terminals. Matching fields are not encrypted, so a\n * plain `em.find` is sufficient.\n */\nexport class TerminalMatcherService {\n constructor(private readonly deps: Deps) {}\n\n private async candidates(scope: MatchScope): Promise<TerminalConfig[]> {\n const em = this.deps.em().fork()\n return em.find(TerminalConfig, {\n organizationId: scope.organizationId,\n tenantId: scope.tenantId,\n isActive: true,\n deletedAt: null,\n })\n }\n\n async resolveFacility(input: FacilityMatchInput, scope: MatchScope): Promise<TerminalMatch | null> {\n const configs = await this.candidates(scope)\n if (configs.length === 0) return null\n\n const norm = (s: string) => s.trim().toUpperCase()\n\n // 1) Exact BIC\n if (input.bic) {\n const bic = norm(input.bic)\n const hit = configs.find((c) => (c.bicCodes ?? []).some((x) => norm(x) === bic))\n if (hit) return { terminalCode: hit.terminalCode, displayName: hit.displayName, matchedBy: 'bic' }\n }\n\n // 2) Exact SMDG\n if (input.smdg) {\n const smdg = norm(input.smdg)\n const hit = configs.find((c) => (c.smdgCodes ?? []).some((x) => norm(x) === smdg))\n if (hit) return { terminalCode: hit.terminalCode, displayName: hit.displayName, matchedBy: 'smdg' }\n }\n\n // 3) Exact UNLOCODE\n if (input.unlocode) {\n const unlocode = norm(input.unlocode)\n const hit = configs.find((c) => c.unlocode && norm(c.unlocode) === unlocode)\n if (hit) return { terminalCode: hit.terminalCode, displayName: hit.displayName, matchedBy: 'unlocode' }\n }\n\n // 4) Fuzzy name (fuse.js)\n if (input.name && input.name.trim()) {\n const docs = configs.flatMap((c) =>\n [c.displayName, ...(c.nameAliases ?? [])].map((alias) => ({ config: c, alias })),\n )\n const fuse = new Fuse(docs, {\n keys: ['alias'],\n includeScore: true,\n threshold: 0.3,\n ignoreLocation: true,\n })\n const [best] = fuse.search(input.name.trim())\n if (best) {\n return {\n terminalCode: best.item.config.terminalCode,\n displayName: best.item.config.displayName,\n matchedBy: 'name',\n score: best.score,\n }\n }\n }\n\n return null\n }\n}\n"],
5
+ "mappings": "AAAA,OAAO,UAAU;AAEjB,SAAS,sBAAsB;AA8BxB,MAAM,uBAAuB;AAAA,EAClC,YAA6B,MAAY;AAAZ;AAAA,EAAa;AAAA,EAE1C,MAAc,WAAW,OAA8C;AACrE,UAAM,KAAK,KAAK,KAAK,GAAG,EAAE,KAAK;AAC/B,WAAO,GAAG,KAAK,gBAAgB;AAAA,MAC7B,gBAAgB,MAAM;AAAA,MACtB,UAAU,MAAM;AAAA,MAChB,UAAU;AAAA,MACV,WAAW;AAAA,IACb,CAAC;AAAA,EACH;AAAA,EAEA,MAAM,gBAAgB,OAA2B,OAAkD;AACjG,UAAM,UAAU,MAAM,KAAK,WAAW,KAAK;AAC3C,QAAI,QAAQ,WAAW,EAAG,QAAO;AAEjC,UAAM,OAAO,CAAC,MAAc,EAAE,KAAK,EAAE,YAAY;AAGjD,QAAI,MAAM,KAAK;AACb,YAAM,MAAM,KAAK,MAAM,GAAG;AAC1B,YAAM,MAAM,QAAQ,KAAK,CAAC,OAAO,EAAE,YAAY,CAAC,GAAG,KAAK,CAAC,MAAM,KAAK,CAAC,MAAM,GAAG,CAAC;AAC/E,UAAI,IAAK,QAAO,EAAE,cAAc,IAAI,cAAc,aAAa,IAAI,aAAa,WAAW,MAAM;AAAA,IACnG;AAGA,QAAI,MAAM,MAAM;AACd,YAAM,OAAO,KAAK,MAAM,IAAI;AAC5B,YAAM,MAAM,QAAQ,KAAK,CAAC,OAAO,EAAE,aAAa,CAAC,GAAG,KAAK,CAAC,MAAM,KAAK,CAAC,MAAM,IAAI,CAAC;AACjF,UAAI,IAAK,QAAO,EAAE,cAAc,IAAI,cAAc,aAAa,IAAI,aAAa,WAAW,OAAO;AAAA,IACpG;AAGA,QAAI,MAAM,UAAU;AAClB,YAAM,WAAW,KAAK,MAAM,QAAQ;AACpC,YAAM,MAAM,QAAQ,KAAK,CAAC,MAAM,EAAE,YAAY,KAAK,EAAE,QAAQ,MAAM,QAAQ;AAC3E,UAAI,IAAK,QAAO,EAAE,cAAc,IAAI,cAAc,aAAa,IAAI,aAAa,WAAW,WAAW;AAAA,IACxG;AAGA,QAAI,MAAM,QAAQ,MAAM,KAAK,KAAK,GAAG;AACnC,YAAM,OAAO,QAAQ;AAAA,QAAQ,CAAC,MAC5B,CAAC,EAAE,aAAa,GAAI,EAAE,eAAe,CAAC,CAAE,EAAE,IAAI,CAAC,WAAW,EAAE,QAAQ,GAAG,MAAM,EAAE;AAAA,MACjF;AACA,YAAM,OAAO,IAAI,KAAK,MAAM;AAAA,QAC1B,MAAM,CAAC,OAAO;AAAA,QACd,cAAc;AAAA,QACd,WAAW;AAAA,QACX,gBAAgB;AAAA,MAClB,CAAC;AACD,YAAM,CAAC,IAAI,IAAI,KAAK,OAAO,MAAM,KAAK,KAAK,CAAC;AAC5C,UAAI,MAAM;AACR,eAAO;AAAA,UACL,cAAc,KAAK,KAAK,OAAO;AAAA,UAC/B,aAAa,KAAK,KAAK,OAAO;AAAA,UAC9B,WAAW;AAAA,UACX,OAAO,KAAK;AAAA,QACd;AAAA,MACF;AAAA,IACF;AAEA,WAAO;AAAA,EACT;AACF;",
6
+ "names": []
7
+ }
@@ -0,0 +1,24 @@
1
+ class TerminalRegistry {
2
+ constructor() {
3
+ this.adapters = /* @__PURE__ */ new Map();
4
+ }
5
+ register(adapter) {
6
+ this.adapters.set(adapter.adapterType.toLowerCase(), adapter);
7
+ }
8
+ get(adapterType) {
9
+ return this.adapters.get(adapterType.toLowerCase());
10
+ }
11
+ has(adapterType) {
12
+ return this.adapters.has(adapterType.toLowerCase());
13
+ }
14
+ list() {
15
+ return Array.from(this.adapters.values());
16
+ }
17
+ types() {
18
+ return Array.from(this.adapters.keys());
19
+ }
20
+ }
21
+ export {
22
+ TerminalRegistry
23
+ };
24
+ //# sourceMappingURL=terminalRegistry.js.map
@@ -0,0 +1,7 @@
1
+ {
2
+ "version": 3,
3
+ "sources": ["../../../../src/modules/terminal_tracking/services/terminalRegistry.ts"],
4
+ "sourcesContent": ["import type { TerminalAdapter } from '../lib/terminal-adapter'\n\n/**\n * Singleton registry for terminal adapters, keyed by `adapterType` (e.g. 'n4').\n * One adapter serves all terminals of that type; per-terminal config lives in\n * TerminalConfig rows.\n */\nexport class TerminalRegistry {\n private adapters = new Map<string, TerminalAdapter>()\n\n register(adapter: TerminalAdapter): void {\n this.adapters.set(adapter.adapterType.toLowerCase(), adapter)\n }\n\n get(adapterType: string): TerminalAdapter | undefined {\n return this.adapters.get(adapterType.toLowerCase())\n }\n\n has(adapterType: string): boolean {\n return this.adapters.has(adapterType.toLowerCase())\n }\n\n list(): TerminalAdapter[] {\n return Array.from(this.adapters.values())\n }\n\n types(): string[] {\n return Array.from(this.adapters.keys())\n }\n}\n"],
5
+ "mappings": "AAOO,MAAM,iBAAiB;AAAA,EAAvB;AACL,SAAQ,WAAW,oBAAI,IAA6B;AAAA;AAAA,EAEpD,SAAS,SAAgC;AACvC,SAAK,SAAS,IAAI,QAAQ,YAAY,YAAY,GAAG,OAAO;AAAA,EAC9D;AAAA,EAEA,IAAI,aAAkD;AACpD,WAAO,KAAK,SAAS,IAAI,YAAY,YAAY,CAAC;AAAA,EACpD;AAAA,EAEA,IAAI,aAA8B;AAChC,WAAO,KAAK,SAAS,IAAI,YAAY,YAAY,CAAC;AAAA,EACpD;AAAA,EAEA,OAA0B;AACxB,WAAO,MAAM,KAAK,KAAK,SAAS,OAAO,CAAC;AAAA,EAC1C;AAAA,EAEA,QAAkB;AAChB,WAAO,MAAM,KAAK,KAAK,SAAS,KAAK,CAAC;AAAA,EACxC;AACF;",
6
+ "names": []
7
+ }