@freighttech/shipment-tracking 0.6.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 (456) hide show
  1. package/LICENSE +21 -0
  2. package/NOTICE +25 -0
  3. package/dist/generated/entities/bic_config/index.js +21 -0
  4. package/dist/generated/entities/bic_config/index.js.map +7 -0
  5. package/dist/generated/entities/carrier_config/index.js +27 -0
  6. package/dist/generated/entities/carrier_config/index.js.map +7 -0
  7. package/dist/generated/entities/location_override/index.js +33 -0
  8. package/dist/generated/entities/location_override/index.js.map +7 -0
  9. package/dist/generated/entities/shipment/index.js +73 -0
  10. package/dist/generated/entities/shipment/index.js.map +7 -0
  11. package/dist/generated/entities/ships_go_config/index.js +31 -0
  12. package/dist/generated/entities/ships_go_config/index.js.map +7 -0
  13. package/dist/generated/entities/tracking_event/index.js +101 -0
  14. package/dist/generated/entities/tracking_event/index.js.map +7 -0
  15. package/dist/generated/entities/tracking_job/index.js +47 -0
  16. package/dist/generated/entities/tracking_job/index.js.map +7 -0
  17. package/dist/generated/entities/webhook/index.js +25 -0
  18. package/dist/generated/entities/webhook/index.js.map +7 -0
  19. package/dist/generated/entities/webhook_delivery/index.js +25 -0
  20. package/dist/generated/entities/webhook_delivery/index.js.map +7 -0
  21. package/dist/generated/entities.ids.generated.js +21 -0
  22. package/dist/generated/entities.ids.generated.js.map +7 -0
  23. package/dist/index.js +41 -0
  24. package/dist/index.js.map +7 -0
  25. package/dist/modules/shipment_tracking/acl.js +22 -0
  26. package/dist/modules/shipment_tracking/acl.js.map +7 -0
  27. package/dist/modules/shipment_tracking/api/bic-configs/route.js +176 -0
  28. package/dist/modules/shipment_tracking/api/bic-configs/route.js.map +7 -0
  29. package/dist/modules/shipment_tracking/api/bic-configs/test/route.js +95 -0
  30. package/dist/modules/shipment_tracking/api/bic-configs/test/route.js.map +7 -0
  31. package/dist/modules/shipment_tracking/api/cargo-events/route.js +93 -0
  32. package/dist/modules/shipment_tracking/api/cargo-events/route.js.map +7 -0
  33. package/dist/modules/shipment_tracking/api/carrier-configs/route.js +139 -0
  34. package/dist/modules/shipment_tracking/api/carrier-configs/route.js.map +7 -0
  35. package/dist/modules/shipment_tracking/api/dashboard/ships-map/route.js +177 -0
  36. package/dist/modules/shipment_tracking/api/dashboard/ships-map/route.js.map +7 -0
  37. package/dist/modules/shipment_tracking/api/location-overrides/route.js +179 -0
  38. package/dist/modules/shipment_tracking/api/location-overrides/route.js.map +7 -0
  39. package/dist/modules/shipment_tracking/api/openapi.js +27 -0
  40. package/dist/modules/shipment_tracking/api/openapi.js.map +7 -0
  41. package/dist/modules/shipment_tracking/api/shipments/route.js +192 -0
  42. package/dist/modules/shipment_tracking/api/shipments/route.js.map +7 -0
  43. package/dist/modules/shipment_tracking/api/shipsgo-configs/route.js +185 -0
  44. package/dist/modules/shipment_tracking/api/shipsgo-configs/route.js.map +7 -0
  45. package/dist/modules/shipment_tracking/api/tracking-jobs/route.js +144 -0
  46. package/dist/modules/shipment_tracking/api/tracking-jobs/route.js.map +7 -0
  47. package/dist/modules/shipment_tracking/api/tracking-jobs/sync/route.js +107 -0
  48. package/dist/modules/shipment_tracking/api/tracking-jobs/sync/route.js.map +7 -0
  49. package/dist/modules/shipment_tracking/api/utils.js +12 -0
  50. package/dist/modules/shipment_tracking/api/utils.js.map +7 -0
  51. package/dist/modules/shipment_tracking/api/webhooks/deliveries/route.js +76 -0
  52. package/dist/modules/shipment_tracking/api/webhooks/deliveries/route.js.map +7 -0
  53. package/dist/modules/shipment_tracking/api/webhooks/route.js +135 -0
  54. package/dist/modules/shipment_tracking/api/webhooks/route.js.map +7 -0
  55. package/dist/modules/shipment_tracking/api/webhooks/test/route.js +71 -0
  56. package/dist/modules/shipment_tracking/api/webhooks/test/route.js.map +7 -0
  57. package/dist/modules/shipment_tracking/assets/carriers/cma_cgm.png +0 -0
  58. package/dist/modules/shipment_tracking/assets/carriers/cosco.png +0 -0
  59. package/dist/modules/shipment_tracking/assets/carriers/evergreen.png +0 -0
  60. package/dist/modules/shipment_tracking/assets/carriers/hapag_lloyd.png +0 -0
  61. package/dist/modules/shipment_tracking/assets/carriers/maersk.png +0 -0
  62. package/dist/modules/shipment_tracking/assets/carriers/msc.png +0 -0
  63. package/dist/modules/shipment_tracking/assets/carriers/one.png +0 -0
  64. package/dist/modules/shipment_tracking/assets/carriers/oocl.png +0 -0
  65. package/dist/modules/shipment_tracking/assets/carriers/yang_ming.png +0 -0
  66. package/dist/modules/shipment_tracking/assets/carriers/zim.png +0 -0
  67. package/dist/modules/shipment_tracking/assets/index.js +52 -0
  68. package/dist/modules/shipment_tracking/assets/index.js.map +7 -0
  69. package/dist/modules/shipment_tracking/backend/carrier-configs/page.js +621 -0
  70. package/dist/modules/shipment_tracking/backend/carrier-configs/page.js.map +7 -0
  71. package/dist/modules/shipment_tracking/backend/carrier-configs/page.meta.js +19 -0
  72. package/dist/modules/shipment_tracking/backend/carrier-configs/page.meta.js.map +7 -0
  73. package/dist/modules/shipment_tracking/backend/location-overrides/page.js +198 -0
  74. package/dist/modules/shipment_tracking/backend/location-overrides/page.js.map +7 -0
  75. package/dist/modules/shipment_tracking/backend/location-overrides/page.meta.js +19 -0
  76. package/dist/modules/shipment_tracking/backend/location-overrides/page.meta.js.map +7 -0
  77. package/dist/modules/shipment_tracking/backend/shipment-tracking/page.js +279 -0
  78. package/dist/modules/shipment_tracking/backend/shipment-tracking/page.js.map +7 -0
  79. package/dist/modules/shipment_tracking/backend/shipment-tracking/page.meta.js +19 -0
  80. package/dist/modules/shipment_tracking/backend/shipment-tracking/page.meta.js.map +7 -0
  81. package/dist/modules/shipment_tracking/backend/tracking-jobs/page.js +10 -0
  82. package/dist/modules/shipment_tracking/backend/tracking-jobs/page.js.map +7 -0
  83. package/dist/modules/shipment_tracking/backend/tracking-jobs/page.meta.js +19 -0
  84. package/dist/modules/shipment_tracking/backend/tracking-jobs/page.meta.js.map +7 -0
  85. package/dist/modules/shipment_tracking/backend/webhooks/page.js +198 -0
  86. package/dist/modules/shipment_tracking/backend/webhooks/page.js.map +7 -0
  87. package/dist/modules/shipment_tracking/backend/webhooks/page.meta.js +19 -0
  88. package/dist/modules/shipment_tracking/backend/webhooks/page.meta.js.map +7 -0
  89. package/dist/modules/shipment_tracking/ce.js +52 -0
  90. package/dist/modules/shipment_tracking/ce.js.map +7 -0
  91. package/dist/modules/shipment_tracking/cli.js +229 -0
  92. package/dist/modules/shipment_tracking/cli.js.map +7 -0
  93. package/dist/modules/shipment_tracking/commands/bic-configs.js +66 -0
  94. package/dist/modules/shipment_tracking/commands/bic-configs.js.map +7 -0
  95. package/dist/modules/shipment_tracking/commands/carrier-configs.js +65 -0
  96. package/dist/modules/shipment_tracking/commands/carrier-configs.js.map +7 -0
  97. package/dist/modules/shipment_tracking/commands/index.js +9 -0
  98. package/dist/modules/shipment_tracking/commands/index.js.map +7 -0
  99. package/dist/modules/shipment_tracking/commands/location-overrides.js +93 -0
  100. package/dist/modules/shipment_tracking/commands/location-overrides.js.map +7 -0
  101. package/dist/modules/shipment_tracking/commands/shipments.js +200 -0
  102. package/dist/modules/shipment_tracking/commands/shipments.js.map +7 -0
  103. package/dist/modules/shipment_tracking/commands/shipsgo-configs.js +75 -0
  104. package/dist/modules/shipment_tracking/commands/shipsgo-configs.js.map +7 -0
  105. package/dist/modules/shipment_tracking/commands/tracking-jobs.js +120 -0
  106. package/dist/modules/shipment_tracking/commands/tracking-jobs.js.map +7 -0
  107. package/dist/modules/shipment_tracking/commands/tracking.js +140 -0
  108. package/dist/modules/shipment_tracking/commands/tracking.js.map +7 -0
  109. package/dist/modules/shipment_tracking/commands/webhooks.js +115 -0
  110. package/dist/modules/shipment_tracking/commands/webhooks.js.map +7 -0
  111. package/dist/modules/shipment_tracking/components/CombinedTimestampCell.js +168 -0
  112. package/dist/modules/shipment_tracking/components/CombinedTimestampCell.js.map +7 -0
  113. package/dist/modules/shipment_tracking/components/FacilityLocationInput.js +202 -0
  114. package/dist/modules/shipment_tracking/components/FacilityLocationInput.js.map +7 -0
  115. package/dist/modules/shipment_tracking/components/LocationOverrideDrawer.js +501 -0
  116. package/dist/modules/shipment_tracking/components/LocationOverrideDrawer.js.map +7 -0
  117. package/dist/modules/shipment_tracking/components/ShipmentDetailsDrawer.js +818 -0
  118. package/dist/modules/shipment_tracking/components/ShipmentDetailsDrawer.js.map +7 -0
  119. package/dist/modules/shipment_tracking/components/ShipmentDrawer.js +404 -0
  120. package/dist/modules/shipment_tracking/components/ShipmentDrawer.js.map +7 -0
  121. package/dist/modules/shipment_tracking/components/ShipsGoConfigSection.js +200 -0
  122. package/dist/modules/shipment_tracking/components/ShipsGoConfigSection.js.map +7 -0
  123. package/dist/modules/shipment_tracking/components/TimestampCell.js +108 -0
  124. package/dist/modules/shipment_tracking/components/TimestampCell.js.map +7 -0
  125. package/dist/modules/shipment_tracking/components/TrackingJobDrawer.js +343 -0
  126. package/dist/modules/shipment_tracking/components/TrackingJobDrawer.js.map +7 -0
  127. package/dist/modules/shipment_tracking/components/VesselTrackingMap.js +496 -0
  128. package/dist/modules/shipment_tracking/components/VesselTrackingMap.js.map +7 -0
  129. package/dist/modules/shipment_tracking/components/WebhookDrawer.js +332 -0
  130. package/dist/modules/shipment_tracking/components/WebhookDrawer.js.map +7 -0
  131. package/dist/modules/shipment_tracking/data/entities.js +686 -0
  132. package/dist/modules/shipment_tracking/data/entities.js.map +7 -0
  133. package/dist/modules/shipment_tracking/data/validators.js +434 -0
  134. package/dist/modules/shipment_tracking/data/validators.js.map +7 -0
  135. package/dist/modules/shipment_tracking/di.js +60 -0
  136. package/dist/modules/shipment_tracking/di.js.map +7 -0
  137. package/dist/modules/shipment_tracking/encryption.js +12 -0
  138. package/dist/modules/shipment_tracking/encryption.js.map +7 -0
  139. package/dist/modules/shipment_tracking/events.js +87 -0
  140. package/dist/modules/shipment_tracking/events.js.map +7 -0
  141. package/dist/modules/shipment_tracking/hooks/useVesselTracking.js +137 -0
  142. package/dist/modules/shipment_tracking/hooks/useVesselTracking.js.map +7 -0
  143. package/dist/modules/shipment_tracking/i18n/de.json +327 -0
  144. package/dist/modules/shipment_tracking/i18n/en.json +327 -0
  145. package/dist/modules/shipment_tracking/i18n/es.json +327 -0
  146. package/dist/modules/shipment_tracking/i18n/pl.json +327 -0
  147. package/dist/modules/shipment_tracking/index.js +15 -0
  148. package/dist/modules/shipment_tracking/index.js.map +7 -0
  149. package/dist/modules/shipment_tracking/lib/adapters/cma-cgm.js +66 -0
  150. package/dist/modules/shipment_tracking/lib/adapters/cma-cgm.js.map +7 -0
  151. package/dist/modules/shipment_tracking/lib/adapters/cosco-types.js +1 -0
  152. package/dist/modules/shipment_tracking/lib/adapters/cosco-types.js.map +7 -0
  153. package/dist/modules/shipment_tracking/lib/adapters/cosco.js +236 -0
  154. package/dist/modules/shipment_tracking/lib/adapters/cosco.js.map +7 -0
  155. package/dist/modules/shipment_tracking/lib/adapters/evergreen.js +87 -0
  156. package/dist/modules/shipment_tracking/lib/adapters/evergreen.js.map +7 -0
  157. package/dist/modules/shipment_tracking/lib/adapters/hapag-lloyd.js +68 -0
  158. package/dist/modules/shipment_tracking/lib/adapters/hapag-lloyd.js.map +7 -0
  159. package/dist/modules/shipment_tracking/lib/adapters/index.js +20 -0
  160. package/dist/modules/shipment_tracking/lib/adapters/index.js.map +7 -0
  161. package/dist/modules/shipment_tracking/lib/adapters/maersk.js +84 -0
  162. package/dist/modules/shipment_tracking/lib/adapters/maersk.js.map +7 -0
  163. package/dist/modules/shipment_tracking/lib/adapters/msc.js +170 -0
  164. package/dist/modules/shipment_tracking/lib/adapters/msc.js.map +7 -0
  165. package/dist/modules/shipment_tracking/lib/adapters/zim.js +85 -0
  166. package/dist/modules/shipment_tracking/lib/adapters/zim.js.map +7 -0
  167. package/dist/modules/shipment_tracking/lib/audit-helpers.js +20 -0
  168. package/dist/modules/shipment_tracking/lib/audit-helpers.js.map +7 -0
  169. package/dist/modules/shipment_tracking/lib/auth/base64url.js +8 -0
  170. package/dist/modules/shipment_tracking/lib/auth/base64url.js.map +7 -0
  171. package/dist/modules/shipment_tracking/lib/auth/oauth-client.js +38 -0
  172. package/dist/modules/shipment_tracking/lib/auth/oauth-client.js.map +7 -0
  173. package/dist/modules/shipment_tracking/lib/background.js +15 -0
  174. package/dist/modules/shipment_tracking/lib/background.js.map +7 -0
  175. package/dist/modules/shipment_tracking/lib/bic-api-client.js +106 -0
  176. package/dist/modules/shipment_tracking/lib/bic-api-client.js.map +7 -0
  177. package/dist/modules/shipment_tracking/lib/carrier-adapter.js +17 -0
  178. package/dist/modules/shipment_tracking/lib/carrier-adapter.js.map +7 -0
  179. package/dist/modules/shipment_tracking/lib/current-vessel.js +149 -0
  180. package/dist/modules/shipment_tracking/lib/current-vessel.js.map +7 -0
  181. package/dist/modules/shipment_tracking/lib/dcsa-event-mapping.js +124 -0
  182. package/dist/modules/shipment_tracking/lib/dcsa-event-mapping.js.map +7 -0
  183. package/dist/modules/shipment_tracking/lib/dcsa-event-parser.js +112 -0
  184. package/dist/modules/shipment_tracking/lib/dcsa-event-parser.js.map +7 -0
  185. package/dist/modules/shipment_tracking/lib/dcsa-params.js +16 -0
  186. package/dist/modules/shipment_tracking/lib/dcsa-params.js.map +7 -0
  187. package/dist/modules/shipment_tracking/lib/empty-return.js +48 -0
  188. package/dist/modules/shipment_tracking/lib/empty-return.js.map +7 -0
  189. package/dist/modules/shipment_tracking/lib/location-overrides.js +85 -0
  190. package/dist/modules/shipment_tracking/lib/location-overrides.js.map +7 -0
  191. package/dist/modules/shipment_tracking/lib/location-types.js +59 -0
  192. package/dist/modules/shipment_tracking/lib/location-types.js.map +7 -0
  193. package/dist/modules/shipment_tracking/lib/logger.js +113 -0
  194. package/dist/modules/shipment_tracking/lib/logger.js.map +7 -0
  195. package/dist/modules/shipment_tracking/lib/poi-event-processor.js +183 -0
  196. package/dist/modules/shipment_tracking/lib/poi-event-processor.js.map +7 -0
  197. package/dist/modules/shipment_tracking/lib/poi-nats-config.js +52 -0
  198. package/dist/modules/shipment_tracking/lib/poi-nats-config.js.map +7 -0
  199. package/dist/modules/shipment_tracking/lib/poi-types.js +43 -0
  200. package/dist/modules/shipment_tracking/lib/poi-types.js.map +7 -0
  201. package/dist/modules/shipment_tracking/lib/rate-limiter.js +31 -0
  202. package/dist/modules/shipment_tracking/lib/rate-limiter.js.map +7 -0
  203. package/dist/modules/shipment_tracking/lib/route-extraction.js +187 -0
  204. package/dist/modules/shipment_tracking/lib/route-extraction.js.map +7 -0
  205. package/dist/modules/shipment_tracking/lib/route-inference.js +106 -0
  206. package/dist/modules/shipment_tracking/lib/route-inference.js.map +7 -0
  207. package/dist/modules/shipment_tracking/lib/schedule-generator.js +63 -0
  208. package/dist/modules/shipment_tracking/lib/schedule-generator.js.map +7 -0
  209. package/dist/modules/shipment_tracking/lib/shipsgo-client.js +143 -0
  210. package/dist/modules/shipment_tracking/lib/shipsgo-client.js.map +7 -0
  211. package/dist/modules/shipment_tracking/lib/shipsgo-mapper.js +204 -0
  212. package/dist/modules/shipment_tracking/lib/shipsgo-mapper.js.map +7 -0
  213. package/dist/modules/shipment_tracking/lib/status-machine.js +131 -0
  214. package/dist/modules/shipment_tracking/lib/status-machine.js.map +7 -0
  215. package/dist/modules/shipment_tracking/lib/time-extraction.js +41 -0
  216. package/dist/modules/shipment_tracking/lib/time-extraction.js.map +7 -0
  217. package/dist/modules/shipment_tracking/lib/timestamp-utils.js +100 -0
  218. package/dist/modules/shipment_tracking/lib/timestamp-utils.js.map +7 -0
  219. package/dist/modules/shipment_tracking/lib/vessel-api.js +111 -0
  220. package/dist/modules/shipment_tracking/lib/vessel-api.js.map +7 -0
  221. package/dist/modules/shipment_tracking/lib/vessel-cache.js +50 -0
  222. package/dist/modules/shipment_tracking/lib/vessel-cache.js.map +7 -0
  223. package/dist/modules/shipment_tracking/lib/vessel-extraction.js +13 -0
  224. package/dist/modules/shipment_tracking/lib/vessel-extraction.js.map +7 -0
  225. package/dist/modules/shipment_tracking/lib/webhook-dispatcher.js +62 -0
  226. package/dist/modules/shipment_tracking/lib/webhook-dispatcher.js.map +7 -0
  227. package/dist/modules/shipment_tracking/migrations/Migration20260422161208_shipment_tracking.js +48 -0
  228. package/dist/modules/shipment_tracking/migrations/Migration20260422161208_shipment_tracking.js.map +7 -0
  229. package/dist/modules/shipment_tracking/migrations/Migration20260529120000_shipment_tracking_poi_region_names.js +15 -0
  230. package/dist/modules/shipment_tracking/migrations/Migration20260529120000_shipment_tracking_poi_region_names.js.map +7 -0
  231. package/dist/modules/shipment_tracking/migrations/Migration20260821120000_shipment_tracking_shipsgo.js +47 -0
  232. package/dist/modules/shipment_tracking/migrations/Migration20260821120000_shipment_tracking_shipsgo.js.map +7 -0
  233. package/dist/modules/shipment_tracking/notifications.js +145 -0
  234. package/dist/modules/shipment_tracking/notifications.js.map +7 -0
  235. package/dist/modules/shipment_tracking/services/carrierRegistry.js +24 -0
  236. package/dist/modules/shipment_tracking/services/carrierRegistry.js.map +7 -0
  237. package/dist/modules/shipment_tracking/services/poiNatsService.js +397 -0
  238. package/dist/modules/shipment_tracking/services/poiNatsService.js.map +7 -0
  239. package/dist/modules/shipment_tracking/services/shipsGoProvider.js +163 -0
  240. package/dist/modules/shipment_tracking/services/shipsGoProvider.js.map +7 -0
  241. package/dist/modules/shipment_tracking/services/trackingService.js +1283 -0
  242. package/dist/modules/shipment_tracking/services/trackingService.js.map +7 -0
  243. package/dist/modules/shipment_tracking/services/webhookService.js +263 -0
  244. package/dist/modules/shipment_tracking/services/webhookService.js.map +7 -0
  245. package/dist/modules/shipment_tracking/setup.js +72 -0
  246. package/dist/modules/shipment_tracking/setup.js.map +7 -0
  247. package/dist/modules/shipment_tracking/subscribers/cargo-event-created.js +60 -0
  248. package/dist/modules/shipment_tracking/subscribers/cargo-event-created.js.map +7 -0
  249. package/dist/modules/shipment_tracking/subscribers/dcsa-event-webhook-dispatch.js +58 -0
  250. package/dist/modules/shipment_tracking/subscribers/dcsa-event-webhook-dispatch.js.map +7 -0
  251. package/dist/modules/shipment_tracking/subscribers/poi-event-notification.js +82 -0
  252. package/dist/modules/shipment_tracking/subscribers/poi-event-notification.js.map +7 -0
  253. package/dist/modules/shipment_tracking/subscribers/poi-event-webhook-dispatch.js +53 -0
  254. package/dist/modules/shipment_tracking/subscribers/poi-event-webhook-dispatch.js.map +7 -0
  255. package/dist/modules/shipment_tracking/subscribers/shipment-status-changed.js +28 -0
  256. package/dist/modules/shipment_tracking/subscribers/shipment-status-changed.js.map +7 -0
  257. package/dist/modules/shipment_tracking/subscribers/terminal-event-ingest.js +67 -0
  258. package/dist/modules/shipment_tracking/subscribers/terminal-event-ingest.js.map +7 -0
  259. package/dist/modules/shipment_tracking/subscribers/tracking-job-initial-poll.js +30 -0
  260. package/dist/modules/shipment_tracking/subscribers/tracking-job-initial-poll.js.map +7 -0
  261. package/dist/modules/shipment_tracking/subscribers/tracking-job-webhook-dispatch.js +19 -0
  262. package/dist/modules/shipment_tracking/subscribers/tracking-job-webhook-dispatch.js.map +7 -0
  263. package/dist/modules/shipment_tracking/tables/index.js +6 -0
  264. package/dist/modules/shipment_tracking/tables/index.js.map +7 -0
  265. package/dist/modules/shipment_tracking/tables/tracking_job/table.client.js +284 -0
  266. package/dist/modules/shipment_tracking/tables/tracking_job/table.client.js.map +7 -0
  267. package/dist/modules/shipment_tracking/tables/tracking_job/table.js +36 -0
  268. package/dist/modules/shipment_tracking/tables/tracking_job/table.js.map +7 -0
  269. package/dist/modules/shipment_tracking/widgets/dashboard/ships-map/config.js +25 -0
  270. package/dist/modules/shipment_tracking/widgets/dashboard/ships-map/config.js.map +7 -0
  271. package/dist/modules/shipment_tracking/widgets/dashboard/ships-map/widget.client.js +355 -0
  272. package/dist/modules/shipment_tracking/widgets/dashboard/ships-map/widget.client.js.map +7 -0
  273. package/dist/modules/shipment_tracking/widgets/dashboard/ships-map/widget.js +26 -0
  274. package/dist/modules/shipment_tracking/widgets/dashboard/ships-map/widget.js.map +7 -0
  275. package/dist/types/assets.d.js +1 -0
  276. package/dist/types/assets.d.js.map +7 -0
  277. package/generated/entities/bic_config/index.ts +20 -0
  278. package/generated/entities/carrier_config/index.ts +23 -0
  279. package/generated/entities/location_override/index.ts +26 -0
  280. package/generated/entities/shipment/index.ts +46 -0
  281. package/generated/entities/ships_go_config/index.ts +25 -0
  282. package/generated/entities/tracking_event/index.ts +60 -0
  283. package/generated/entities/tracking_job/index.ts +33 -0
  284. package/generated/entities/webhook/index.ts +22 -0
  285. package/generated/entities/webhook_delivery/index.ts +22 -0
  286. package/generated/entities.ids.generated.ts +47 -0
  287. package/package.json +81 -0
  288. package/src/index.ts +39 -0
  289. package/src/modules/shipment_tracking/__integration__/TC-TRACK-410-shipsgo-config-api.spec.ts +133 -0
  290. package/src/modules/shipment_tracking/__integration__/meta.ts +4 -0
  291. package/src/modules/shipment_tracking/__tests__/empty-return-stop.test.ts +231 -0
  292. package/src/modules/shipment_tracking/__tests__/fixtures/hapag-lloyd-transship-container.json +702 -0
  293. package/src/modules/shipment_tracking/__tests__/fixtures/index.ts +434 -0
  294. package/src/modules/shipment_tracking/__tests__/fixtures/maersk-multi-transship-completed.json +1518 -0
  295. package/src/modules/shipment_tracking/__tests__/fixtures/maersk-transship-intransit.json +558 -0
  296. package/src/modules/shipment_tracking/__tests__/fixtures/msc-direct.json +222 -0
  297. package/src/modules/shipment_tracking/__tests__/fixtures/msc-multi-transship-completed.json +974 -0
  298. package/src/modules/shipment_tracking/__tests__/fixtures/msc-multi.json +4846 -0
  299. package/src/modules/shipment_tracking/__tests__/fixtures/msc-transship.json +409 -0
  300. package/src/modules/shipment_tracking/__tests__/fixtures/poi-events.json +240 -0
  301. package/src/modules/shipment_tracking/__tests__/fixtures/synthetic/index.ts +138 -0
  302. package/src/modules/shipment_tracking/__tests__/fixtures/synthetic-eta-updates.json +288 -0
  303. package/src/modules/shipment_tracking/__tests__/fixtures/synthetic-route-inference.json +201 -0
  304. package/src/modules/shipment_tracking/__tests__/fixtures/synthetic-voyage-progression.json +410 -0
  305. package/src/modules/shipment_tracking/__tests__/shipsgo-air-mapper.test.ts +157 -0
  306. package/src/modules/shipment_tracking/__tests__/shipsgo-client.test.ts +155 -0
  307. package/src/modules/shipment_tracking/__tests__/shipsgo-mapper.test.ts +201 -0
  308. package/src/modules/shipment_tracking/__tests__/shipsgo-poll.test.ts +357 -0
  309. package/src/modules/shipment_tracking/__tests__/shipsgo-provider.test.ts +364 -0
  310. package/src/modules/shipment_tracking/__tests__/trackingService.integration.test.ts +2248 -0
  311. package/src/modules/shipment_tracking/__tests__/webhookPayload.test.ts +151 -0
  312. package/src/modules/shipment_tracking/acl.ts +18 -0
  313. package/src/modules/shipment_tracking/api/bic-configs/route.ts +206 -0
  314. package/src/modules/shipment_tracking/api/bic-configs/test/route.ts +108 -0
  315. package/src/modules/shipment_tracking/api/cargo-events/route.ts +101 -0
  316. package/src/modules/shipment_tracking/api/carrier-configs/route.ts +142 -0
  317. package/src/modules/shipment_tracking/api/dashboard/ships-map/route.ts +246 -0
  318. package/src/modules/shipment_tracking/api/location-overrides/route.ts +185 -0
  319. package/src/modules/shipment_tracking/api/openapi.ts +28 -0
  320. package/src/modules/shipment_tracking/api/shipments/route.ts +197 -0
  321. package/src/modules/shipment_tracking/api/shipsgo-configs/route.ts +215 -0
  322. package/src/modules/shipment_tracking/api/tracking-jobs/route.ts +149 -0
  323. package/src/modules/shipment_tracking/api/tracking-jobs/sync/route.ts +116 -0
  324. package/src/modules/shipment_tracking/api/utils.ts +10 -0
  325. package/src/modules/shipment_tracking/api/webhooks/deliveries/route.ts +82 -0
  326. package/src/modules/shipment_tracking/api/webhooks/route.ts +138 -0
  327. package/src/modules/shipment_tracking/api/webhooks/test/route.ts +84 -0
  328. package/src/modules/shipment_tracking/assets/carriers/cma_cgm.png +0 -0
  329. package/src/modules/shipment_tracking/assets/carriers/cosco.png +0 -0
  330. package/src/modules/shipment_tracking/assets/carriers/evergreen.png +0 -0
  331. package/src/modules/shipment_tracking/assets/carriers/hapag_lloyd.png +0 -0
  332. package/src/modules/shipment_tracking/assets/carriers/maersk.png +0 -0
  333. package/src/modules/shipment_tracking/assets/carriers/msc.png +0 -0
  334. package/src/modules/shipment_tracking/assets/carriers/one.png +0 -0
  335. package/src/modules/shipment_tracking/assets/carriers/oocl.png +0 -0
  336. package/src/modules/shipment_tracking/assets/carriers/yang_ming.png +0 -0
  337. package/src/modules/shipment_tracking/assets/carriers/zim.png +0 -0
  338. package/src/modules/shipment_tracking/assets/index.ts +66 -0
  339. package/src/modules/shipment_tracking/backend/carrier-configs/page.meta.ts +16 -0
  340. package/src/modules/shipment_tracking/backend/carrier-configs/page.tsx +782 -0
  341. package/src/modules/shipment_tracking/backend/location-overrides/page.meta.ts +16 -0
  342. package/src/modules/shipment_tracking/backend/location-overrides/page.tsx +241 -0
  343. package/src/modules/shipment_tracking/backend/shipment-tracking/page.meta.ts +16 -0
  344. package/src/modules/shipment_tracking/backend/shipment-tracking/page.tsx +340 -0
  345. package/src/modules/shipment_tracking/backend/tracking-jobs/page.meta.ts +16 -0
  346. package/src/modules/shipment_tracking/backend/tracking-jobs/page.tsx +16 -0
  347. package/src/modules/shipment_tracking/backend/webhooks/page.meta.ts +16 -0
  348. package/src/modules/shipment_tracking/backend/webhooks/page.tsx +228 -0
  349. package/src/modules/shipment_tracking/ce.ts +48 -0
  350. package/src/modules/shipment_tracking/cli.ts +321 -0
  351. package/src/modules/shipment_tracking/commands/bic-configs.ts +94 -0
  352. package/src/modules/shipment_tracking/commands/carrier-configs.ts +89 -0
  353. package/src/modules/shipment_tracking/commands/index.ts +8 -0
  354. package/src/modules/shipment_tracking/commands/location-overrides.ts +119 -0
  355. package/src/modules/shipment_tracking/commands/shipments.ts +247 -0
  356. package/src/modules/shipment_tracking/commands/shipsgo-configs.ts +101 -0
  357. package/src/modules/shipment_tracking/commands/tracking-jobs.ts +158 -0
  358. package/src/modules/shipment_tracking/commands/tracking.ts +230 -0
  359. package/src/modules/shipment_tracking/commands/webhooks.ts +159 -0
  360. package/src/modules/shipment_tracking/components/CombinedTimestampCell.tsx +236 -0
  361. package/src/modules/shipment_tracking/components/FacilityLocationInput.tsx +235 -0
  362. package/src/modules/shipment_tracking/components/LocationOverrideDrawer.tsx +586 -0
  363. package/src/modules/shipment_tracking/components/ShipmentDetailsDrawer.tsx +1364 -0
  364. package/src/modules/shipment_tracking/components/ShipmentDrawer.tsx +498 -0
  365. package/src/modules/shipment_tracking/components/ShipsGoConfigSection.tsx +266 -0
  366. package/src/modules/shipment_tracking/components/TimestampCell.tsx +146 -0
  367. package/src/modules/shipment_tracking/components/TrackingJobDrawer.tsx +467 -0
  368. package/src/modules/shipment_tracking/components/VesselTrackingMap.tsx +761 -0
  369. package/src/modules/shipment_tracking/components/WebhookDrawer.tsx +434 -0
  370. package/src/modules/shipment_tracking/data/entities.ts +748 -0
  371. package/src/modules/shipment_tracking/data/validators.ts +523 -0
  372. package/src/modules/shipment_tracking/di.ts +73 -0
  373. package/src/modules/shipment_tracking/encryption.ts +17 -0
  374. package/src/modules/shipment_tracking/events.ts +122 -0
  375. package/src/modules/shipment_tracking/hooks/useVesselTracking.ts +297 -0
  376. package/src/modules/shipment_tracking/i18n/de.json +327 -0
  377. package/src/modules/shipment_tracking/i18n/en.json +327 -0
  378. package/src/modules/shipment_tracking/i18n/es.json +327 -0
  379. package/src/modules/shipment_tracking/i18n/pl.json +327 -0
  380. package/src/modules/shipment_tracking/index.ts +13 -0
  381. package/src/modules/shipment_tracking/lib/__tests__/dcsa-event-mapping.test.ts +385 -0
  382. package/src/modules/shipment_tracking/lib/__tests__/dcsa-event-parser.test.ts +398 -0
  383. package/src/modules/shipment_tracking/lib/__tests__/empty-return.test.ts +174 -0
  384. package/src/modules/shipment_tracking/lib/__tests__/poi-event-processor.test.ts +699 -0
  385. package/src/modules/shipment_tracking/lib/__tests__/route-inference.test.ts +402 -0
  386. package/src/modules/shipment_tracking/lib/__tests__/status-machine.test.ts +447 -0
  387. package/src/modules/shipment_tracking/lib/__tests__/time-extraction.test.ts +331 -0
  388. package/src/modules/shipment_tracking/lib/__tests__/timestamp-utils.test.ts +260 -0
  389. package/src/modules/shipment_tracking/lib/__tests__/vessel-api.test.ts +125 -0
  390. package/src/modules/shipment_tracking/lib/__tests__/vessel-extraction.test.ts +108 -0
  391. package/src/modules/shipment_tracking/lib/adapters/cma-cgm.ts +84 -0
  392. package/src/modules/shipment_tracking/lib/adapters/cosco-types.ts +145 -0
  393. package/src/modules/shipment_tracking/lib/adapters/cosco.ts +314 -0
  394. package/src/modules/shipment_tracking/lib/adapters/evergreen.ts +107 -0
  395. package/src/modules/shipment_tracking/lib/adapters/hapag-lloyd.ts +88 -0
  396. package/src/modules/shipment_tracking/lib/adapters/index.ts +18 -0
  397. package/src/modules/shipment_tracking/lib/adapters/maersk.ts +104 -0
  398. package/src/modules/shipment_tracking/lib/adapters/msc.ts +223 -0
  399. package/src/modules/shipment_tracking/lib/adapters/zim.ts +106 -0
  400. package/src/modules/shipment_tracking/lib/audit-helpers.ts +29 -0
  401. package/src/modules/shipment_tracking/lib/auth/base64url.ts +4 -0
  402. package/src/modules/shipment_tracking/lib/auth/oauth-client.ts +53 -0
  403. package/src/modules/shipment_tracking/lib/background.ts +24 -0
  404. package/src/modules/shipment_tracking/lib/bic-api-client.ts +199 -0
  405. package/src/modules/shipment_tracking/lib/carrier-adapter.ts +136 -0
  406. package/src/modules/shipment_tracking/lib/current-vessel.ts +274 -0
  407. package/src/modules/shipment_tracking/lib/dcsa-event-mapping.ts +202 -0
  408. package/src/modules/shipment_tracking/lib/dcsa-event-parser.ts +163 -0
  409. package/src/modules/shipment_tracking/lib/dcsa-params.ts +14 -0
  410. package/src/modules/shipment_tracking/lib/empty-return.ts +122 -0
  411. package/src/modules/shipment_tracking/lib/location-overrides.ts +150 -0
  412. package/src/modules/shipment_tracking/lib/location-types.ts +154 -0
  413. package/src/modules/shipment_tracking/lib/logger.ts +195 -0
  414. package/src/modules/shipment_tracking/lib/poi-event-processor.ts +302 -0
  415. package/src/modules/shipment_tracking/lib/poi-nats-config.ts +134 -0
  416. package/src/modules/shipment_tracking/lib/poi-types.ts +198 -0
  417. package/src/modules/shipment_tracking/lib/rate-limiter.ts +58 -0
  418. package/src/modules/shipment_tracking/lib/route-extraction.ts +439 -0
  419. package/src/modules/shipment_tracking/lib/route-inference.ts +238 -0
  420. package/src/modules/shipment_tracking/lib/schedule-generator.ts +92 -0
  421. package/src/modules/shipment_tracking/lib/shipsgo-client.ts +350 -0
  422. package/src/modules/shipment_tracking/lib/shipsgo-mapper.ts +296 -0
  423. package/src/modules/shipment_tracking/lib/status-machine.ts +230 -0
  424. package/src/modules/shipment_tracking/lib/time-extraction.ts +85 -0
  425. package/src/modules/shipment_tracking/lib/timestamp-utils.ts +244 -0
  426. package/src/modules/shipment_tracking/lib/vessel-api.ts +299 -0
  427. package/src/modules/shipment_tracking/lib/vessel-cache.ts +102 -0
  428. package/src/modules/shipment_tracking/lib/vessel-extraction.ts +39 -0
  429. package/src/modules/shipment_tracking/lib/webhook-dispatcher.ts +93 -0
  430. package/src/modules/shipment_tracking/migrations/.snapshot-open-mercato.json +2914 -0
  431. package/src/modules/shipment_tracking/migrations/Migration20260422161208_shipment_tracking.ts +60 -0
  432. package/src/modules/shipment_tracking/migrations/Migration20260529120000_shipment_tracking_poi_region_names.ts +22 -0
  433. package/src/modules/shipment_tracking/migrations/Migration20260821120000_shipment_tracking_shipsgo.ts +65 -0
  434. package/src/modules/shipment_tracking/notifications.ts +151 -0
  435. package/src/modules/shipment_tracking/services/carrierRegistry.ts +29 -0
  436. package/src/modules/shipment_tracking/services/poiNatsService.ts +526 -0
  437. package/src/modules/shipment_tracking/services/shipsGoProvider.ts +231 -0
  438. package/src/modules/shipment_tracking/services/trackingService.ts +1777 -0
  439. package/src/modules/shipment_tracking/services/webhookService.ts +347 -0
  440. package/src/modules/shipment_tracking/setup.ts +99 -0
  441. package/src/modules/shipment_tracking/subscribers/__tests__/terminal-event-ingest.test.ts +141 -0
  442. package/src/modules/shipment_tracking/subscribers/cargo-event-created.ts +115 -0
  443. package/src/modules/shipment_tracking/subscribers/dcsa-event-webhook-dispatch.ts +115 -0
  444. package/src/modules/shipment_tracking/subscribers/poi-event-notification.ts +144 -0
  445. package/src/modules/shipment_tracking/subscribers/poi-event-webhook-dispatch.ts +94 -0
  446. package/src/modules/shipment_tracking/subscribers/shipment-status-changed.ts +41 -0
  447. package/src/modules/shipment_tracking/subscribers/terminal-event-ingest.ts +109 -0
  448. package/src/modules/shipment_tracking/subscribers/tracking-job-initial-poll.ts +53 -0
  449. package/src/modules/shipment_tracking/subscribers/tracking-job-webhook-dispatch.ts +50 -0
  450. package/src/modules/shipment_tracking/tables/index.ts +4 -0
  451. package/src/modules/shipment_tracking/tables/tracking_job/table.client.tsx +337 -0
  452. package/src/modules/shipment_tracking/tables/tracking_job/table.ts +34 -0
  453. package/src/modules/shipment_tracking/widgets/dashboard/ships-map/config.ts +32 -0
  454. package/src/modules/shipment_tracking/widgets/dashboard/ships-map/widget.client.tsx +448 -0
  455. package/src/modules/shipment_tracking/widgets/dashboard/ships-map/widget.ts +25 -0
  456. package/src/types/assets.d.ts +5 -0
@@ -0,0 +1,244 @@
1
+ /**
2
+ * Multi-source timestamp utilities for shipment tracking.
3
+ *
4
+ * Supports tracking ETD/ETA/ATD/ATA from multiple sources with full history (SCD pattern).
5
+ * Primary value is computed using "latest update wins" strategy.
6
+ */
7
+
8
+ export type TimestampSource = 'carrier_api' | 'manual' | 'ais' | 'port' | 'edi'
9
+
10
+ export type TimestampType = 'ETD' | 'ETA' | 'ATD' | 'ATA'
11
+
12
+ /**
13
+ * A single timestamp entry tracking value, source, and when it was recorded.
14
+ * Multiple entries per source are allowed (SCD - Slowly Changing Dimension pattern).
15
+ */
16
+ export type ShipmentTimestampEntry = {
17
+ /** ISO 8601 datetime value (e.g., "2026-02-24T15:30:00") */
18
+ value: string
19
+ /** Original timezone offset (e.g., "+08:00", "-05:00", "Z") */
20
+ offset: string | null
21
+ /** Source system that provided this timestamp */
22
+ source: TimestampSource
23
+ /** When this entry was recorded/updated (ISO 8601) */
24
+ updatedAt: string
25
+ /** Optional reference to the source tracking event ID */
26
+ sourceEventId?: string | null
27
+ }
28
+
29
+ /**
30
+ * Computed primary timestamp with its metadata.
31
+ */
32
+ export type ComputedTimestamp = {
33
+ value: Date
34
+ offset: string | null
35
+ source: TimestampSource
36
+ updatedAt: Date
37
+ sourceEventId?: string | null
38
+ }
39
+
40
+ /**
41
+ * Gets the latest (primary) timestamp from an array of entries.
42
+ * Uses "latest update wins" strategy - the entry with most recent updatedAt is primary.
43
+ *
44
+ * @param entries Array of timestamp entries or null/undefined
45
+ * @returns The latest entry or null if no entries
46
+ */
47
+ export function getLatestTimestamp(
48
+ entries: ShipmentTimestampEntry[] | null | undefined,
49
+ ): ComputedTimestamp | null {
50
+ if (!entries?.length) return null
51
+
52
+ // Use >= to prefer later entries when updatedAt is equal.
53
+ // This handles the case where entries are added in the same millisecond.
54
+ const latest = entries.reduce((best, entry) =>
55
+ entry.updatedAt >= best.updatedAt ? entry : best,
56
+ )
57
+
58
+ return {
59
+ value: new Date(latest.value),
60
+ offset: latest.offset,
61
+ source: latest.source,
62
+ updatedAt: new Date(latest.updatedAt),
63
+ sourceEventId: latest.sourceEventId,
64
+ }
65
+ }
66
+
67
+ /**
68
+ * Gets the primary value as a Date, or null if no entries.
69
+ * Convenience wrapper around getLatestTimestamp.
70
+ */
71
+ export function getPrimaryTimestampValue(
72
+ entries: ShipmentTimestampEntry[] | null | undefined,
73
+ ): Date | null {
74
+ return getLatestTimestamp(entries)?.value ?? null
75
+ }
76
+
77
+ /**
78
+ * Gets the primary offset, or null if no entries.
79
+ */
80
+ export function getPrimaryTimestampOffset(
81
+ entries: ShipmentTimestampEntry[] | null | undefined,
82
+ ): string | null {
83
+ return getLatestTimestamp(entries)?.offset ?? null
84
+ }
85
+
86
+ /**
87
+ * Creates a new timestamp entry.
88
+ *
89
+ * @param value The timestamp value (Date or ISO string)
90
+ * @param offset Timezone offset string (e.g., "+08:00") or null
91
+ * @param source Source system
92
+ * @param sourceEventId Optional source event ID for traceability
93
+ * @returns A new ShipmentTimestampEntry
94
+ */
95
+ export function createTimestampEntry(
96
+ value: Date | string,
97
+ offset: string | null,
98
+ source: TimestampSource,
99
+ sourceEventId?: string | null,
100
+ ): ShipmentTimestampEntry {
101
+ const dateValue = typeof value === 'string' ? new Date(value) : value
102
+
103
+ return {
104
+ value: dateValue.toISOString(),
105
+ offset,
106
+ source,
107
+ updatedAt: new Date().toISOString(),
108
+ sourceEventId: sourceEventId ?? null,
109
+ }
110
+ }
111
+
112
+ /**
113
+ * Adds a timestamp entry to an array with time-based deduplication.
114
+ * Deduplication: if an entry with the same value+source exists, it won't be added.
115
+ * Multiple entries per source with different values ARE allowed (SCD pattern).
116
+ *
117
+ * @param entries Existing entries array (will be mutated) or null
118
+ * @param newEntry Entry to add
119
+ * @returns The updated array (creates new array if input was null)
120
+ */
121
+ export function addTimestampEntry(
122
+ entries: ShipmentTimestampEntry[] | null | undefined,
123
+ newEntry: ShipmentTimestampEntry,
124
+ ): ShipmentTimestampEntry[] {
125
+ const result = entries ? [...entries] : []
126
+
127
+ // Dedupe: check if exact same value+source combo already exists
128
+ const isDuplicate = result.some(
129
+ (e) => e.value === newEntry.value && e.source === newEntry.source,
130
+ )
131
+
132
+ if (!isDuplicate) {
133
+ result.push(newEntry)
134
+ }
135
+
136
+ return result
137
+ }
138
+
139
+ /**
140
+ * Merges extracted times into existing timestamp arrays.
141
+ * Used when processing tracking events to update shipment timestamps.
142
+ *
143
+ * @param current Current timestamp arrays from shipment
144
+ * @param extracted Newly extracted timestamps from events
145
+ * @param source Source of the extracted timestamps
146
+ * @param sourceEventId Optional source event ID
147
+ * @returns Updated timestamp arrays
148
+ */
149
+ export function mergeExtractedTimestamps(
150
+ current: {
151
+ etdTimestamps?: ShipmentTimestampEntry[] | null
152
+ etaTimestamps?: ShipmentTimestampEntry[] | null
153
+ atdTimestamps?: ShipmentTimestampEntry[] | null
154
+ ataTimestamps?: ShipmentTimestampEntry[] | null
155
+ },
156
+ extracted: {
157
+ etd?: Date | null
158
+ etdOffset?: string | null
159
+ eta?: Date | null
160
+ etaOffset?: string | null
161
+ atd?: Date | null
162
+ atdOffset?: string | null
163
+ ata?: Date | null
164
+ ataOffset?: string | null
165
+ },
166
+ source: TimestampSource,
167
+ sourceEventId?: string | null,
168
+ ): {
169
+ etdTimestamps: ShipmentTimestampEntry[] | null
170
+ etaTimestamps: ShipmentTimestampEntry[] | null
171
+ atdTimestamps: ShipmentTimestampEntry[] | null
172
+ ataTimestamps: ShipmentTimestampEntry[] | null
173
+ changed: boolean
174
+ } {
175
+ let changed = false
176
+ let etdTimestamps = current.etdTimestamps ?? null
177
+ let etaTimestamps = current.etaTimestamps ?? null
178
+ let atdTimestamps = current.atdTimestamps ?? null
179
+ let ataTimestamps = current.ataTimestamps ?? null
180
+
181
+ if (extracted.etd) {
182
+ const entry = createTimestampEntry(extracted.etd, extracted.etdOffset ?? null, source, sourceEventId)
183
+ const before = etdTimestamps?.length ?? 0
184
+ etdTimestamps = addTimestampEntry(etdTimestamps, entry)
185
+ if ((etdTimestamps?.length ?? 0) > before) changed = true
186
+ }
187
+
188
+ if (extracted.eta) {
189
+ const entry = createTimestampEntry(extracted.eta, extracted.etaOffset ?? null, source, sourceEventId)
190
+ const before = etaTimestamps?.length ?? 0
191
+ etaTimestamps = addTimestampEntry(etaTimestamps, entry)
192
+ if ((etaTimestamps?.length ?? 0) > before) changed = true
193
+ }
194
+
195
+ if (extracted.atd) {
196
+ const entry = createTimestampEntry(extracted.atd, extracted.atdOffset ?? null, source, sourceEventId)
197
+ const before = atdTimestamps?.length ?? 0
198
+ atdTimestamps = addTimestampEntry(atdTimestamps, entry)
199
+ if ((atdTimestamps?.length ?? 0) > before) changed = true
200
+ }
201
+
202
+ if (extracted.ata) {
203
+ const entry = createTimestampEntry(extracted.ata, extracted.ataOffset ?? null, source, sourceEventId)
204
+ const before = ataTimestamps?.length ?? 0
205
+ ataTimestamps = addTimestampEntry(ataTimestamps, entry)
206
+ if ((ataTimestamps?.length ?? 0) > before) changed = true
207
+ }
208
+
209
+ return {
210
+ etdTimestamps,
211
+ etaTimestamps,
212
+ atdTimestamps,
213
+ ataTimestamps,
214
+ changed,
215
+ }
216
+ }
217
+
218
+ /**
219
+ * Checks if a timestamp array has any entries.
220
+ */
221
+ export function hasTimestamps(entries: ShipmentTimestampEntry[] | null | undefined): boolean {
222
+ return !!entries && entries.length > 0
223
+ }
224
+
225
+ /**
226
+ * Gets all timestamps from a specific source.
227
+ */
228
+ export function getTimestampsBySource(
229
+ entries: ShipmentTimestampEntry[] | null | undefined,
230
+ source: TimestampSource,
231
+ ): ShipmentTimestampEntry[] {
232
+ if (!entries) return []
233
+ return entries.filter((e) => e.source === source)
234
+ }
235
+
236
+ /**
237
+ * Gets the history of timestamp changes, sorted by updatedAt (oldest first).
238
+ */
239
+ export function getTimestampHistory(
240
+ entries: ShipmentTimestampEntry[] | null | undefined,
241
+ ): ShipmentTimestampEntry[] {
242
+ if (!entries) return []
243
+ return [...entries].sort((a, b) => a.updatedAt.localeCompare(b.updatedAt))
244
+ }
@@ -0,0 +1,299 @@
1
+ /**
2
+ * Vessel API client for fetching real-time vessel position and trace data.
3
+ *
4
+ * The base URL is deployment-specific and MUST be provided via the
5
+ * `NEXT_PUBLIC_VESSEL_API_URL` environment variable. There is no default: when
6
+ * it is unset the vessel/trace/POI calls throw a clear "not configured" error
7
+ * and the map features that depend on them are simply unavailable.
8
+ */
9
+
10
+ // ─── Types ───────────────────────────────────────────────────
11
+
12
+ export interface VesselDimensions {
13
+ a: number
14
+ b: number
15
+ c: number
16
+ d: number
17
+ }
18
+
19
+ export interface PortInfo {
20
+ locode: string
21
+ name: string
22
+ city: string
23
+ country: string
24
+ }
25
+
26
+ export interface Position {
27
+ lat: number
28
+ lng: number
29
+ }
30
+
31
+ export type VesselStatus = 'AT_SEA' | 'IN_PORT'
32
+ export type PoiType = 'PORT' | 'TERMINAL' | 'WAYPOINT' | 'PORT_GEOMETRIC_CENTER'
33
+
34
+ export interface VesselInfo {
35
+ imo: number
36
+ mmsi: number | null
37
+ name: string
38
+ callsign: string | null
39
+ shipType: string | null
40
+ operatorName: string | null
41
+ owner: string | null
42
+ dimensions: VesselDimensions | null
43
+ status: VesselStatus | null
44
+ lastPosition: Position | null
45
+ lastHeading: number | null
46
+ lastDestination: string | null
47
+ lastDestinationPort: PortInfo | null
48
+ currentPort: PortInfo | null
49
+ currentPoiCode: string | null
50
+ currentPoiType: PoiType | null
51
+ isStationary: boolean
52
+ updatedAt: string
53
+ }
54
+
55
+ export type TraceEventType =
56
+ | 'POSITION_UPDATE'
57
+ | 'PORT_ARRIVAL'
58
+ | 'PORT_DEPARTURE'
59
+ | 'TERMINAL_ARRIVAL'
60
+ | 'TERMINAL_DEPARTURE'
61
+ | 'DESTINATION_CHANGE'
62
+
63
+ export interface TracePoint {
64
+ lat: number
65
+ lng: number
66
+ speed: number | null
67
+ heading: number | null
68
+ eventType: TraceEventType
69
+ destination: string | null
70
+ timestamp: string
71
+ }
72
+
73
+ export interface BoundingBox {
74
+ north: number
75
+ south: number
76
+ east: number
77
+ west: number
78
+ }
79
+
80
+ /**
81
+ * A point of interest (port, terminal, waypoint, or port geometric center) returned by the
82
+ * vessel-api `/poi` endpoint, modeled as a circle (center + radius) ready to draw on a map.
83
+ */
84
+ export interface PoiInfo {
85
+ code: string
86
+ locode: string
87
+ type: PoiType
88
+ center: Position
89
+ radiusMeters: number
90
+ portName: string | null
91
+ terminalName: string | null
92
+ city: string | null
93
+ country: string | null
94
+ /** Maritime region name in Polish (waypoints; may fall back to English). */
95
+ regionNamePl: string | null
96
+ /** Maritime region name in English. */
97
+ regionNameEn: string | null
98
+ }
99
+
100
+ export interface PoiListResponse {
101
+ pois: PoiInfo[]
102
+ count: number
103
+ limit: number
104
+ /** Present only when a bounding-box filter was applied. */
105
+ bounds?: BoundingBox
106
+ /** Present only when a type filter was applied. */
107
+ type?: PoiType
108
+ }
109
+
110
+ export interface TimeRange {
111
+ from?: string // ISO 8601 date
112
+ to?: string // ISO 8601 date
113
+ }
114
+
115
+ export interface VesselResponse {
116
+ vessel: VesselInfo
117
+ }
118
+
119
+ export interface TraceResponse {
120
+ vessel: {
121
+ imo: number
122
+ mmsi: number | null
123
+ name: string
124
+ }
125
+ trace: TracePoint[]
126
+ count: number
127
+ limit: number
128
+ bounds?: BoundingBox
129
+ timeRange?: TimeRange
130
+ }
131
+
132
+ // ─── Configuration ───────────────────────────────────────────
133
+
134
+ function vesselApiBaseUrl(): string {
135
+ const baseUrl = process.env.NEXT_PUBLIC_VESSEL_API_URL || ''
136
+ if (!baseUrl) {
137
+ throw new Error(
138
+ 'Vessel API is not configured — set NEXT_PUBLIC_VESSEL_API_URL to your vessel-tracking API base URL'
139
+ )
140
+ }
141
+ return baseUrl.replace(/\/+$/, '')
142
+ }
143
+
144
+ // ─── API Functions ───────────────────────────────────────────
145
+
146
+ /**
147
+ * Fetch vessel information by IMO number or MMSI.
148
+ *
149
+ * @param imoOrMmsi - IMO number (7 digits) or MMSI (9 digits)
150
+ * @returns Vessel info or null if not found
151
+ */
152
+ export async function fetchVessel(imoOrMmsi: number | string): Promise<VesselInfo | null> {
153
+ const id = typeof imoOrMmsi === 'string' ? imoOrMmsi : String(imoOrMmsi)
154
+
155
+ try {
156
+ const response = await fetch(`${vesselApiBaseUrl()}/vessel/${encodeURIComponent(id)}`)
157
+
158
+ if (response.status === 404) {
159
+ return null
160
+ }
161
+
162
+ if (!response.ok) {
163
+ const error = await response.json().catch(() => ({ error: 'Unknown error' }))
164
+ throw new Error(error.error || `HTTP ${response.status}`)
165
+ }
166
+
167
+ const data: VesselResponse = await response.json()
168
+ return data.vessel
169
+ } catch (error) {
170
+ console.error('[vessel-api] Failed to fetch vessel:', error)
171
+ throw error
172
+ }
173
+ }
174
+
175
+ /**
176
+ * Fetch vessel trace (position history) with optional bounding box and time filters.
177
+ *
178
+ * @param imoOrMmsi - IMO number or MMSI
179
+ * @param bounds - Optional bounding box to filter trace points
180
+ * @param limit - Maximum number of points to return (default: 5000)
181
+ * @param timeRange - Optional time range filter (from/to in ISO 8601)
182
+ * @returns Trace data with array of position points
183
+ */
184
+ export async function fetchVesselTrace(
185
+ imoOrMmsi: number | string,
186
+ bounds?: BoundingBox,
187
+ limit?: number,
188
+ timeRange?: TimeRange
189
+ ): Promise<TraceResponse> {
190
+ const id = typeof imoOrMmsi === 'string' ? imoOrMmsi : String(imoOrMmsi)
191
+
192
+ const params = new URLSearchParams()
193
+
194
+ if (bounds) {
195
+ params.set('north', String(bounds.north))
196
+ params.set('south', String(bounds.south))
197
+ params.set('east', String(bounds.east))
198
+ params.set('west', String(bounds.west))
199
+ }
200
+
201
+ if (limit) {
202
+ params.set('limit', String(limit))
203
+ }
204
+
205
+ if (timeRange?.from) {
206
+ params.set('from', timeRange.from)
207
+ }
208
+
209
+ if (timeRange?.to) {
210
+ params.set('to', timeRange.to)
211
+ }
212
+
213
+ const queryString = params.toString()
214
+ const url = `${vesselApiBaseUrl()}/vessel/${encodeURIComponent(id)}/trace${queryString ? `?${queryString}` : ''}`
215
+
216
+ try {
217
+ const response = await fetch(url)
218
+
219
+ if (response.status === 404) {
220
+ return {
221
+ vessel: { imo: 0, mmsi: null, name: 'Unknown' },
222
+ trace: [],
223
+ count: 0,
224
+ limit: limit || 5000,
225
+ }
226
+ }
227
+
228
+ if (!response.ok) {
229
+ const error = await response.json().catch(() => ({ error: 'Unknown error' }))
230
+ throw new Error(error.error || `HTTP ${response.status}`)
231
+ }
232
+
233
+ const data: TraceResponse = await response.json()
234
+ return data
235
+ } catch (error) {
236
+ console.error('[vessel-api] Failed to fetch vessel trace:', error)
237
+ throw error
238
+ }
239
+ }
240
+
241
+ /**
242
+ * Fetch points of interest (ports, terminals, waypoints) as map circles, optionally
243
+ * constrained to a viewport bounding box and/or a single POI type.
244
+ *
245
+ * @param bounds - Optional bounding box to fetch only POIs in the current viewport
246
+ * @param options - Optional `type` filter and `limit` (API default/cap is 5000)
247
+ * @returns Array of POIs (empty on 404)
248
+ */
249
+ export async function fetchPois(
250
+ bounds?: BoundingBox,
251
+ options?: { type?: PoiType; limit?: number }
252
+ ): Promise<PoiInfo[]> {
253
+ const params = new URLSearchParams()
254
+
255
+ if (bounds) {
256
+ params.set('north', String(bounds.north))
257
+ params.set('south', String(bounds.south))
258
+ params.set('east', String(bounds.east))
259
+ params.set('west', String(bounds.west))
260
+ }
261
+
262
+ if (options?.type) {
263
+ params.set('type', options.type)
264
+ }
265
+
266
+ if (options?.limit) {
267
+ params.set('limit', String(options.limit))
268
+ }
269
+
270
+ const queryString = params.toString()
271
+ const url = `${vesselApiBaseUrl()}/poi${queryString ? `?${queryString}` : ''}`
272
+
273
+ try {
274
+ const response = await fetch(url)
275
+
276
+ if (response.status === 404) {
277
+ return []
278
+ }
279
+
280
+ if (!response.ok) {
281
+ const error = await response.json().catch(() => ({ error: 'Unknown error' }))
282
+ throw new Error(error.error || `HTTP ${response.status}`)
283
+ }
284
+
285
+ const data: PoiListResponse = await response.json()
286
+ // The API contract says `code` is unique, but the live endpoint can return the same code
287
+ // more than once. Dedupe by code so downstream React keys stay unique and we don't draw
288
+ // overlapping duplicate circles.
289
+ const seen = new Set<string>()
290
+ return (data.pois ?? []).filter((poi) => {
291
+ if (seen.has(poi.code)) return false
292
+ seen.add(poi.code)
293
+ return true
294
+ })
295
+ } catch (error) {
296
+ console.error('[vessel-api] Failed to fetch POIs:', error)
297
+ throw error
298
+ }
299
+ }
@@ -0,0 +1,102 @@
1
+ /**
2
+ * Process-global, bounded, short-TTL cache for public AIS vessel data, plus a small
3
+ * concurrency limiter for fanning out `fetchVessel` calls.
4
+ *
5
+ * Why this exists: the ships-map dashboard widget resolves many shipments to a handful
6
+ * of distinct vessels (a vessel carries several of our containers), then fetches each
7
+ * vessel's live position from the external `vessel-api`. That service has no batch
8
+ * endpoint, so we make one HTTP call per distinct IMO. To keep latency and upstream
9
+ * quota in check we:
10
+ * - cache the public `VesselInfo` keyed by IMO (TTL ~5 min),
11
+ * - bound the cache so it can't grow over the process lifetime,
12
+ * - cap concurrency on the fan-out.
13
+ *
14
+ * The cache stores ONLY public maritime data (`VesselInfo` keyed by IMO) — never
15
+ * tenant-scoped rows — so it is safe to share across tenants. The tenant-scoped
16
+ * `Shipment` query and the shipment→vessel grouping are computed per request and
17
+ * never cached here.
18
+ */
19
+
20
+ import { fetchVessel, type VesselInfo } from './vessel-api'
21
+
22
+ // ─── Configuration ───────────────────────────────────────────
23
+
24
+ const TTL_MS = 5 * 60 * 1000 // 5 minutes
25
+ const MAX_ENTRIES = 500 // hard cap so the cache can't grow unbounded over process life
26
+ const DEFAULT_CONCURRENCY = 8
27
+
28
+ type CacheEntry = { v: VesselInfo | null; exp: number }
29
+
30
+ // Module-level (process-global) cache. Keyed by IMO string.
31
+ const cache = new Map<string, CacheEntry>()
32
+
33
+ // ─── Cache ───────────────────────────────────────────────────
34
+
35
+ /**
36
+ * Evict expired entries; if still at capacity, drop the oldest insertion (Map preserves
37
+ * insertion order) so the cache stays bounded.
38
+ */
39
+ function evictIfNeeded(now: number): void {
40
+ for (const [key, entry] of cache) {
41
+ if (entry.exp <= now) cache.delete(key)
42
+ }
43
+ while (cache.size >= MAX_ENTRIES) {
44
+ const oldest = cache.keys().next().value
45
+ if (oldest === undefined) break
46
+ cache.delete(oldest)
47
+ }
48
+ }
49
+
50
+ /**
51
+ * Returns the cached `VesselInfo` for an IMO, fetching from `vessel-api` on a miss.
52
+ *
53
+ * A fetch failure is NOT cached — it propagates to the caller so the request handler can
54
+ * fold the IMO into `unresolvedCount` (via `Promise.allSettled`). A successful fetch that
55
+ * yields `null` (404 / unknown vessel) IS cached to avoid re-hitting a known-miss.
56
+ */
57
+ export async function getVesselCached(imo: string): Promise<VesselInfo | null> {
58
+ const now = Date.now()
59
+ const hit = cache.get(imo)
60
+ if (hit && hit.exp > now) return hit.v
61
+
62
+ const vessel = await fetchVessel(imo)
63
+ evictIfNeeded(Date.now())
64
+ cache.set(imo, { v: vessel, exp: Date.now() + TTL_MS })
65
+ return vessel
66
+ }
67
+
68
+ /** Test-only: clear the process-global cache. */
69
+ export function __clearVesselCache(): void {
70
+ cache.clear()
71
+ }
72
+
73
+ // ─── Concurrency-capped fan-out ──────────────────────────────
74
+
75
+ /**
76
+ * Run `task` over `items` with at most `concurrency` in flight, returning results in the
77
+ * same order as `items`. Mirrors `Promise.allSettled` semantics: never rejects — each slot
78
+ * resolves to `{ status: 'fulfilled', value }` or `{ status: 'rejected', reason }`.
79
+ */
80
+ export async function mapWithConcurrency<T, R>(
81
+ items: readonly T[],
82
+ task: (item: T) => Promise<R>,
83
+ concurrency: number = DEFAULT_CONCURRENCY,
84
+ ): Promise<PromiseSettledResult<R>[]> {
85
+ const results: PromiseSettledResult<R>[] = new Array(items.length)
86
+ let cursor = 0
87
+
88
+ async function worker(): Promise<void> {
89
+ while (cursor < items.length) {
90
+ const index = cursor++
91
+ try {
92
+ results[index] = { status: 'fulfilled', value: await task(items[index]) }
93
+ } catch (reason) {
94
+ results[index] = { status: 'rejected', reason }
95
+ }
96
+ }
97
+ }
98
+
99
+ const workers = Array.from({ length: Math.min(concurrency, items.length) }, () => worker())
100
+ await Promise.all(workers)
101
+ return results
102
+ }
@@ -0,0 +1,39 @@
1
+ /**
2
+ * Vessel info extraction utilities for shipment tracking.
3
+ */
4
+
5
+ export type VesselInfo = {
6
+ vesselName: string | null
7
+ vesselImo: string | null
8
+ voyageNumber: string | null
9
+ }
10
+
11
+ type EventWithVesselFields = {
12
+ vesselName?: string | null
13
+ vesselImo?: string | null
14
+ voyageNumber?: string | null
15
+ }
16
+
17
+ /**
18
+ * Finds the latest vessel info from a list of tracking events.
19
+ *
20
+ * For completed voyages, the last event may be a gate operation (GTOT/GTIN)
21
+ * that doesn't have vessel data. This function finds the latest event
22
+ * that has any vessel info (vesselName, vesselImo, or voyageNumber).
23
+ *
24
+ * @param events - Array of tracking events sorted by eventDateTime
25
+ * @returns VesselInfo from the latest event with vessel data, or null if none found
26
+ */
27
+ export function findLatestVesselInfo(events: EventWithVesselFields[]): VesselInfo | null {
28
+ const eventWithVessel = [...events]
29
+ .reverse()
30
+ .find((e) => e.vesselName || e.vesselImo || e.voyageNumber)
31
+
32
+ if (!eventWithVessel) return null
33
+
34
+ return {
35
+ vesselName: eventWithVessel.vesselName ?? null,
36
+ vesselImo: eventWithVessel.vesselImo ?? null,
37
+ voyageNumber: eventWithVessel.voyageNumber ?? null,
38
+ }
39
+ }