@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,85 @@
1
+ import { LocationOverride } from "../data/entities.js";
2
+ async function findLocationOverride(em, match, scope) {
3
+ if (!match.unlocode || !match.facilityCode || !match.facilityCodeListProvider) {
4
+ return null;
5
+ }
6
+ const conditions = {
7
+ organizationId: scope.organizationId,
8
+ tenantId: scope.tenantId,
9
+ unlocode: match.unlocode.toUpperCase(),
10
+ facilityCode: match.facilityCode,
11
+ facilityCodeListProvider: match.facilityCodeListProvider,
12
+ isActive: true,
13
+ deletedAt: null
14
+ };
15
+ if (match.carrierCode) {
16
+ const carrierSpecific = await em.findOne(LocationOverride, {
17
+ ...conditions,
18
+ carrierCode: match.carrierCode.toUpperCase()
19
+ });
20
+ if (carrierSpecific) {
21
+ return carrierSpecific;
22
+ }
23
+ }
24
+ const globalOverride = await em.findOne(LocationOverride, {
25
+ ...conditions,
26
+ carrierCode: null
27
+ });
28
+ return globalOverride;
29
+ }
30
+ function applyLocationOverride(location, overrideData) {
31
+ const result = { ...location };
32
+ if (overrideData.name != null) {
33
+ result.name = overrideData.name;
34
+ }
35
+ if (overrideData.address !== void 0) {
36
+ result.address = overrideData.address;
37
+ }
38
+ if (overrideData.operatorName !== void 0) {
39
+ result.operatorName = overrideData.operatorName;
40
+ }
41
+ if (overrideData.countryCode !== void 0) {
42
+ result.countryCode = overrideData.countryCode;
43
+ }
44
+ if (overrideData.facilityTypeCode !== void 0) {
45
+ result.facilityTypeCode = overrideData.facilityTypeCode;
46
+ }
47
+ if (overrideData.coords !== void 0) {
48
+ result.coords = overrideData.coords;
49
+ }
50
+ if (overrideData.unlocode !== void 0) {
51
+ result.unlocode = overrideData.unlocode;
52
+ }
53
+ if (overrideData.facilityCode !== void 0) {
54
+ result.facilityCode = overrideData.facilityCode;
55
+ }
56
+ if (overrideData.facilityCodeListProvider !== void 0) {
57
+ result.facilityCodeListProvider = overrideData.facilityCodeListProvider;
58
+ }
59
+ result.source = "manual";
60
+ return result;
61
+ }
62
+ async function applyLocationOverrideIfExists(em, location, carrierCode, scope) {
63
+ if (!location) {
64
+ return null;
65
+ }
66
+ if (!location.unlocode || !location.facilityCode || !location.facilityCodeListProvider) {
67
+ return location;
68
+ }
69
+ const override = await findLocationOverride(em, {
70
+ unlocode: location.unlocode,
71
+ facilityCode: location.facilityCode,
72
+ facilityCodeListProvider: location.facilityCodeListProvider,
73
+ carrierCode
74
+ }, scope);
75
+ if (override) {
76
+ return applyLocationOverride(location, override.overrideData);
77
+ }
78
+ return location;
79
+ }
80
+ export {
81
+ applyLocationOverride,
82
+ applyLocationOverrideIfExists,
83
+ findLocationOverride
84
+ };
85
+ //# sourceMappingURL=location-overrides.js.map
@@ -0,0 +1,7 @@
1
+ {
2
+ "version": 3,
3
+ "sources": ["../../../../src/modules/shipment_tracking/lib/location-overrides.ts"],
4
+ "sourcesContent": ["/**\n * Location Override Helpers\n *\n * Provides lookup and application of location overrides for BIC/SMDG facility data.\n * Overrides allow correcting incorrect terminal/facility information from BIC API.\n */\n\nimport type { EntityManager } from '@mikro-orm/postgresql'\nimport { LocationOverride, type FacilityLocation, type FacilityCodeListProvider } from '../data/entities'\n\nexport interface LocationOverrideMatch {\n unlocode: string\n facilityCode: string\n facilityCodeListProvider: FacilityCodeListProvider\n carrierCode?: string | null\n}\n\nexport interface LocationOverrideScope {\n organizationId: string\n tenantId: string\n}\n\n/**\n * Finds the most specific location override for a given facility.\n *\n * Priority order:\n * 1. Carrier-specific override (carrierCode matches)\n * 2. Global override (carrierCode is null)\n *\n * @returns The override to apply, or null if no override exists\n */\nexport async function findLocationOverride(\n em: EntityManager,\n match: LocationOverrideMatch,\n scope: LocationOverrideScope\n): Promise<LocationOverride | null> {\n if (!match.unlocode || !match.facilityCode || !match.facilityCodeListProvider) {\n return null\n }\n\n // Build query conditions - carrier-specific first, then global\n const conditions: Record<string, unknown> = {\n organizationId: scope.organizationId,\n tenantId: scope.tenantId,\n unlocode: match.unlocode.toUpperCase(),\n facilityCode: match.facilityCode,\n facilityCodeListProvider: match.facilityCodeListProvider,\n isActive: true,\n deletedAt: null,\n }\n\n // First try carrier-specific override (normalize to uppercase)\n if (match.carrierCode) {\n const carrierSpecific = await em.findOne(LocationOverride, {\n ...conditions,\n carrierCode: match.carrierCode.toUpperCase(),\n })\n if (carrierSpecific) {\n return carrierSpecific\n }\n }\n\n // Fall back to global override (carrierCode is null)\n const globalOverride = await em.findOne(LocationOverride, {\n ...conditions,\n carrierCode: null,\n })\n\n return globalOverride\n}\n\n/**\n * Applies override data to a FacilityLocation.\n * Only non-null fields from the override are applied.\n * Sets source to 'manual' to indicate the data was manually overridden.\n */\nexport function applyLocationOverride(\n location: FacilityLocation,\n overrideData: Partial<FacilityLocation>\n): FacilityLocation {\n const result: FacilityLocation = { ...location }\n\n // Apply each override field if it's defined and not null\n if (overrideData.name != null) {\n result.name = overrideData.name\n }\n if (overrideData.address !== undefined) {\n result.address = overrideData.address\n }\n if (overrideData.operatorName !== undefined) {\n result.operatorName = overrideData.operatorName\n }\n if (overrideData.countryCode !== undefined) {\n result.countryCode = overrideData.countryCode\n }\n if (overrideData.facilityTypeCode !== undefined) {\n result.facilityTypeCode = overrideData.facilityTypeCode\n }\n if (overrideData.coords !== undefined) {\n result.coords = overrideData.coords\n }\n // Can also override unlocode/facilityCode if needed (rare)\n if (overrideData.unlocode !== undefined) {\n result.unlocode = overrideData.unlocode\n }\n if (overrideData.facilityCode !== undefined) {\n result.facilityCode = overrideData.facilityCode\n }\n if (overrideData.facilityCodeListProvider !== undefined) {\n result.facilityCodeListProvider = overrideData.facilityCodeListProvider\n }\n\n // Mark as manually overridden\n result.source = 'manual'\n\n return result\n}\n\n/**\n * Finds and applies location override to a FacilityLocation in one step.\n * Returns the original location if no override exists.\n */\nexport async function applyLocationOverrideIfExists(\n em: EntityManager,\n location: FacilityLocation | null | undefined,\n carrierCode: string | null | undefined,\n scope: LocationOverrideScope\n): Promise<FacilityLocation | null> {\n if (!location) {\n return null\n }\n\n // Skip if location doesn't have required match fields\n if (!location.unlocode || !location.facilityCode || !location.facilityCodeListProvider) {\n return location\n }\n\n const override = await findLocationOverride(em, {\n unlocode: location.unlocode,\n facilityCode: location.facilityCode,\n facilityCodeListProvider: location.facilityCodeListProvider,\n carrierCode,\n }, scope)\n\n if (override) {\n return applyLocationOverride(location, override.overrideData)\n }\n\n return location\n}\n"],
5
+ "mappings": "AAQA,SAAS,wBAA8E;AAuBvF,eAAsB,qBACpB,IACA,OACA,OACkC;AAClC,MAAI,CAAC,MAAM,YAAY,CAAC,MAAM,gBAAgB,CAAC,MAAM,0BAA0B;AAC7E,WAAO;AAAA,EACT;AAGA,QAAM,aAAsC;AAAA,IAC1C,gBAAgB,MAAM;AAAA,IACtB,UAAU,MAAM;AAAA,IAChB,UAAU,MAAM,SAAS,YAAY;AAAA,IACrC,cAAc,MAAM;AAAA,IACpB,0BAA0B,MAAM;AAAA,IAChC,UAAU;AAAA,IACV,WAAW;AAAA,EACb;AAGA,MAAI,MAAM,aAAa;AACrB,UAAM,kBAAkB,MAAM,GAAG,QAAQ,kBAAkB;AAAA,MACzD,GAAG;AAAA,MACH,aAAa,MAAM,YAAY,YAAY;AAAA,IAC7C,CAAC;AACD,QAAI,iBAAiB;AACnB,aAAO;AAAA,IACT;AAAA,EACF;AAGA,QAAM,iBAAiB,MAAM,GAAG,QAAQ,kBAAkB;AAAA,IACxD,GAAG;AAAA,IACH,aAAa;AAAA,EACf,CAAC;AAED,SAAO;AACT;AAOO,SAAS,sBACd,UACA,cACkB;AAClB,QAAM,SAA2B,EAAE,GAAG,SAAS;AAG/C,MAAI,aAAa,QAAQ,MAAM;AAC7B,WAAO,OAAO,aAAa;AAAA,EAC7B;AACA,MAAI,aAAa,YAAY,QAAW;AACtC,WAAO,UAAU,aAAa;AAAA,EAChC;AACA,MAAI,aAAa,iBAAiB,QAAW;AAC3C,WAAO,eAAe,aAAa;AAAA,EACrC;AACA,MAAI,aAAa,gBAAgB,QAAW;AAC1C,WAAO,cAAc,aAAa;AAAA,EACpC;AACA,MAAI,aAAa,qBAAqB,QAAW;AAC/C,WAAO,mBAAmB,aAAa;AAAA,EACzC;AACA,MAAI,aAAa,WAAW,QAAW;AACrC,WAAO,SAAS,aAAa;AAAA,EAC/B;AAEA,MAAI,aAAa,aAAa,QAAW;AACvC,WAAO,WAAW,aAAa;AAAA,EACjC;AACA,MAAI,aAAa,iBAAiB,QAAW;AAC3C,WAAO,eAAe,aAAa;AAAA,EACrC;AACA,MAAI,aAAa,6BAA6B,QAAW;AACvD,WAAO,2BAA2B,aAAa;AAAA,EACjD;AAGA,SAAO,SAAS;AAEhB,SAAO;AACT;AAMA,eAAsB,8BACpB,IACA,UACA,aACA,OACkC;AAClC,MAAI,CAAC,UAAU;AACb,WAAO;AAAA,EACT;AAGA,MAAI,CAAC,SAAS,YAAY,CAAC,SAAS,gBAAgB,CAAC,SAAS,0BAA0B;AACtF,WAAO;AAAA,EACT;AAEA,QAAM,WAAW,MAAM,qBAAqB,IAAI;AAAA,IAC9C,UAAU,SAAS;AAAA,IACnB,cAAc,SAAS;AAAA,IACvB,0BAA0B,SAAS;AAAA,IACnC;AAAA,EACF,GAAG,KAAK;AAER,MAAI,UAAU;AACZ,WAAO,sBAAsB,UAAU,SAAS,YAAY;AAAA,EAC9D;AAEA,SAAO;AACT;",
6
+ "names": []
7
+ }
@@ -0,0 +1,59 @@
1
+ function isLocationComplete(loc) {
2
+ if (!loc) return false;
3
+ const hasName = !!loc.name && loc.name !== "Unknown";
4
+ const hasCoords = loc.coords?.latitude != null && loc.coords?.longitude != null;
5
+ const hasAddress = !!loc.address;
6
+ return hasName && (hasCoords || hasAddress);
7
+ }
8
+ function buildLocationFromEvent(event) {
9
+ const countryCode = event.locationCountry || (event.locationUnlocode?.length === 5 ? event.locationUnlocode.slice(0, 2) : null);
10
+ return {
11
+ name: event.locationName || event.locationUnlocode || "Unknown",
12
+ unlocode: event.locationUnlocode || null,
13
+ countryCode,
14
+ facilityCode: event.facilityCode || null,
15
+ facilityCodeListProvider: event.facilityCodeListProvider || null,
16
+ facilityTypeCode: event.facilityTypeCode || null,
17
+ address: event.facilityAddress || null,
18
+ coords: event.latitude != null && event.longitude != null ? { latitude: event.latitude, longitude: event.longitude } : null,
19
+ operatorName: null,
20
+ // Only populated by BIC enrichment
21
+ source: "dcsa"
22
+ };
23
+ }
24
+ function mergeLocationWithBicData(location, bicData) {
25
+ const enrichedName = bicData.name || location.name;
26
+ return {
27
+ ...location,
28
+ name: enrichedName,
29
+ address: location.address || bicData.address || null,
30
+ coords: location.coords || bicData.coords || null,
31
+ operatorName: location.operatorName || bicData.operatorName || null,
32
+ facilityCode: location.facilityCode || bicData.facilityCode || null,
33
+ facilityCodeListProvider: location.facilityCodeListProvider || bicData.facilityCodeListProvider || null,
34
+ source: bicData.name || bicData.address || bicData.coords ? "bic" : location.source
35
+ };
36
+ }
37
+ function createBasicLocation(nameOrUnlocode, unlocode) {
38
+ const actualUnlocode = unlocode || (nameOrUnlocode.length === 5 ? nameOrUnlocode : null);
39
+ const countryCode = actualUnlocode?.slice(0, 2) || null;
40
+ return {
41
+ name: nameOrUnlocode,
42
+ unlocode: actualUnlocode,
43
+ countryCode,
44
+ facilityCode: null,
45
+ facilityCodeListProvider: null,
46
+ facilityTypeCode: null,
47
+ address: null,
48
+ coords: null,
49
+ operatorName: null,
50
+ source: "manual"
51
+ };
52
+ }
53
+ export {
54
+ buildLocationFromEvent,
55
+ createBasicLocation,
56
+ isLocationComplete,
57
+ mergeLocationWithBicData
58
+ };
59
+ //# sourceMappingURL=location-types.js.map
@@ -0,0 +1,7 @@
1
+ {
2
+ "version": 3,
3
+ "sources": ["../../../../src/modules/shipment_tracking/lib/location-types.ts"],
4
+ "sourcesContent": ["/**\n * Location Types for Shipment Tracking\n * \n * Rich location data combining port (UN/LOCODE) and facility/terminal details.\n * Used for Shipment origin/destination and RouteStopEntry.\n */\n\n// \u2500\u2500\u2500 Types \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\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\n\n/**\n * Rich location data for origin, destination, or transshipment points.\n * Combines port-level (UN/LOCODE) with facility-level (terminal) details.\n */\nexport interface FacilityLocation {\n // Display name (terminal or port name)\n name: string\n\n // Port-level identifiers\n unlocode: string | null // UN/LOCODE (e.g., \"PLGDN\")\n countryCode: string | null // ISO 3166-1 alpha-2 (e.g., \"PL\")\n\n // Facility/terminal identifiers (separate fields)\n facilityCode: string | null // SMDG/BIC code (e.g., \"DCT\")\n facilityCodeListProvider: 'BIC' | 'SMDG' | null\n facilityTypeCode: string | null // POTE (port terminal), DEPO (depot), etc.\n\n // Address (from DCSA otherFacility or BIC API)\n address: string | null // Full address string\n\n // Coordinates\n coords: {\n latitude: number\n longitude: number\n } | null\n\n // Operator (from BIC API enrichment only)\n operatorName: string | null\n\n // Source of the data\n source: 'dcsa' | 'bic' | 'manual'\n}\n\n/**\n * Input type for building FacilityLocation from TrackingEvent data.\n */\nexport interface LocationEventInput {\n locationName?: string | null\n locationUnlocode?: string | null\n locationCountry?: string | null\n facilityCode?: string | null\n facilityCodeListProvider?: 'BIC' | 'SMDG' | null\n facilityTypeCode?: string | null\n facilityAddress?: string | null\n latitude?: number | null\n longitude?: number | null\n}\n\n// \u2500\u2500\u2500 Helpers \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\u2500\u2500\u2500\u2500\u2500\u2500\u2500\n\n/**\n * Determines if location data is \"complete enough\" to skip BIC enrichment.\n * Complete = has name AND (coords OR address)\n */\nexport function isLocationComplete(loc: Partial<FacilityLocation> | null | undefined): boolean {\n if (!loc) return false\n const hasName = !!loc.name && loc.name !== 'Unknown'\n const hasCoords = loc.coords?.latitude != null && loc.coords?.longitude != null\n const hasAddress = !!loc.address\n\n // Complete if we have name AND (coords OR address)\n return hasName && (hasCoords || hasAddress)\n}\n\n/**\n * Builds a FacilityLocation from TrackingEvent data.\n * Extracts country code from UN/LOCODE if not provided.\n */\nexport function buildLocationFromEvent(event: LocationEventInput): FacilityLocation {\n // Extract country code from UN/LOCODE (first 2 chars) if not provided\n const countryCode = event.locationCountry ||\n (event.locationUnlocode?.length === 5 ? event.locationUnlocode.slice(0, 2) : null)\n\n return {\n name: event.locationName || event.locationUnlocode || 'Unknown',\n unlocode: event.locationUnlocode || null,\n countryCode,\n facilityCode: event.facilityCode || null,\n facilityCodeListProvider: event.facilityCodeListProvider || null,\n facilityTypeCode: event.facilityTypeCode || null,\n address: event.facilityAddress || null,\n coords: (event.latitude != null && event.longitude != null)\n ? { latitude: event.latitude, longitude: event.longitude }\n : null,\n operatorName: null, // Only populated by BIC enrichment\n source: 'dcsa',\n }\n}\n\n/**\n * Merges BIC enrichment data into an existing FacilityLocation.\n * BIC provides more detailed facility names (e.g., \"GDYNIA CONTAINER TERMINAL S.A.\")\n * so we prefer BIC name over generic port names from DCSA.\n */\nexport function mergeLocationWithBicData(\n location: FacilityLocation,\n bicData: {\n name?: string\n address?: string\n coords?: { latitude: number; longitude: number }\n operatorName?: string\n facilityCode?: string\n facilityCodeListProvider?: 'BIC' | 'SMDG'\n }\n): FacilityLocation {\n // BIC provides more descriptive terminal names, so prefer BIC name when available\n // Only keep DCSA name if BIC doesn't provide one\n const enrichedName = bicData.name || location.name\n\n return {\n ...location,\n name: enrichedName,\n address: location.address || bicData.address || null,\n coords: location.coords || bicData.coords || null,\n operatorName: location.operatorName || bicData.operatorName || null,\n facilityCode: location.facilityCode || bicData.facilityCode || null,\n facilityCodeListProvider: location.facilityCodeListProvider || bicData.facilityCodeListProvider || null,\n source: bicData.name || bicData.address || bicData.coords ? 'bic' : location.source,\n }\n}\n\n/**\n * Creates an empty/unknown FacilityLocation with just a name/unlocode.\n * Used when no event data is available but we need a location object.\n */\nexport function createBasicLocation(\n nameOrUnlocode: string,\n unlocode?: string | null\n): FacilityLocation {\n const actualUnlocode = unlocode || (nameOrUnlocode.length === 5 ? nameOrUnlocode : null)\n const countryCode = actualUnlocode?.slice(0, 2) || null\n\n return {\n name: nameOrUnlocode,\n unlocode: actualUnlocode,\n countryCode,\n facilityCode: null,\n facilityCodeListProvider: null,\n facilityTypeCode: null,\n address: null,\n coords: null,\n operatorName: null,\n source: 'manual',\n }\n}\n"],
5
+ "mappings": "AA+DO,SAAS,mBAAmB,KAA4D;AAC7F,MAAI,CAAC,IAAK,QAAO;AACjB,QAAM,UAAU,CAAC,CAAC,IAAI,QAAQ,IAAI,SAAS;AAC3C,QAAM,YAAY,IAAI,QAAQ,YAAY,QAAQ,IAAI,QAAQ,aAAa;AAC3E,QAAM,aAAa,CAAC,CAAC,IAAI;AAGzB,SAAO,YAAY,aAAa;AAClC;AAMO,SAAS,uBAAuB,OAA6C;AAElF,QAAM,cAAc,MAAM,oBACvB,MAAM,kBAAkB,WAAW,IAAI,MAAM,iBAAiB,MAAM,GAAG,CAAC,IAAI;AAE/E,SAAO;AAAA,IACL,MAAM,MAAM,gBAAgB,MAAM,oBAAoB;AAAA,IACtD,UAAU,MAAM,oBAAoB;AAAA,IACpC;AAAA,IACA,cAAc,MAAM,gBAAgB;AAAA,IACpC,0BAA0B,MAAM,4BAA4B;AAAA,IAC5D,kBAAkB,MAAM,oBAAoB;AAAA,IAC5C,SAAS,MAAM,mBAAmB;AAAA,IAClC,QAAS,MAAM,YAAY,QAAQ,MAAM,aAAa,OAClD,EAAE,UAAU,MAAM,UAAU,WAAW,MAAM,UAAU,IACvD;AAAA,IACJ,cAAc;AAAA;AAAA,IACd,QAAQ;AAAA,EACV;AACF;AAOO,SAAS,yBACd,UACA,SAQkB;AAGlB,QAAM,eAAe,QAAQ,QAAQ,SAAS;AAE9C,SAAO;AAAA,IACL,GAAG;AAAA,IACH,MAAM;AAAA,IACN,SAAS,SAAS,WAAW,QAAQ,WAAW;AAAA,IAChD,QAAQ,SAAS,UAAU,QAAQ,UAAU;AAAA,IAC7C,cAAc,SAAS,gBAAgB,QAAQ,gBAAgB;AAAA,IAC/D,cAAc,SAAS,gBAAgB,QAAQ,gBAAgB;AAAA,IAC/D,0BAA0B,SAAS,4BAA4B,QAAQ,4BAA4B;AAAA,IACnG,QAAQ,QAAQ,QAAQ,QAAQ,WAAW,QAAQ,SAAS,QAAQ,SAAS;AAAA,EAC/E;AACF;AAMO,SAAS,oBACd,gBACA,UACkB;AAClB,QAAM,iBAAiB,aAAa,eAAe,WAAW,IAAI,iBAAiB;AACnF,QAAM,cAAc,gBAAgB,MAAM,GAAG,CAAC,KAAK;AAEnD,SAAO;AAAA,IACL,MAAM;AAAA,IACN,UAAU;AAAA,IACV;AAAA,IACA,cAAc;AAAA,IACd,0BAA0B;AAAA,IAC1B,kBAAkB;AAAA,IAClB,SAAS;AAAA,IACT,QAAQ;AAAA,IACR,cAAc;AAAA,IACd,QAAQ;AAAA,EACV;AACF;",
6
+ "names": []
7
+ }
@@ -0,0 +1,113 @@
1
+ import { AsyncLocalStorage } from "node:async_hooks";
2
+ import { trace, SpanStatusCode } from "@opentelemetry/api";
3
+ const MODULE_NAME = "shipment-tracking";
4
+ const logContext = new AsyncLocalStorage();
5
+ function emit(level, message, fields) {
6
+ const record = {
7
+ level,
8
+ time: (/* @__PURE__ */ new Date()).toISOString(),
9
+ message,
10
+ ...logContext.getStore(),
11
+ ...fields
12
+ };
13
+ const line = JSON.stringify(record);
14
+ if (level === "error") console.error(line);
15
+ else if (level === "warn") console.warn(line);
16
+ else console.log(line);
17
+ }
18
+ const logger = {
19
+ debug: (message, fields) => emit("debug", message, fields),
20
+ info: (message, fields) => emit("info", message, fields),
21
+ warn: (message, fields) => emit("warn", message, fields),
22
+ error: (message, fields) => emit("error", message, fields)
23
+ };
24
+ function runWithLogContext(ctx, fn) {
25
+ return logContext.run({ ...logContext.getStore(), ...ctx }, fn);
26
+ }
27
+ const tracer = trace.getTracer(MODULE_NAME);
28
+ function withSpan(opts, fn) {
29
+ const attributes = Object.fromEntries(
30
+ Object.entries(opts.attributes ?? {}).filter(([, v]) => v !== void 0)
31
+ );
32
+ return tracer.startActiveSpan(opts.name, { attributes }, async (span) => {
33
+ try {
34
+ return await fn(span);
35
+ } catch (error) {
36
+ span.recordException(error);
37
+ span.setStatus({ code: SpanStatusCode.ERROR });
38
+ throw error;
39
+ } finally {
40
+ span.end();
41
+ }
42
+ });
43
+ }
44
+ function withCarrierContext(ctx, fn) {
45
+ return runWithLogContext({ module: MODULE_NAME, ...ctx }, fn);
46
+ }
47
+ async function withCarrierApiSpan(ctx, fn) {
48
+ const spanName = `carrier.${ctx.carrierCode}.${ctx.operation}`;
49
+ logger.debug(`Starting ${ctx.operation}`, {
50
+ module: MODULE_NAME,
51
+ carrierCode: ctx.carrierCode,
52
+ operation: ctx.operation,
53
+ referenceType: ctx.referenceType
54
+ });
55
+ const startMs = Date.now();
56
+ return withSpan(
57
+ {
58
+ name: spanName,
59
+ attributes: {
60
+ "carrier.code": ctx.carrierCode,
61
+ "carrier.operation": ctx.operation,
62
+ module: MODULE_NAME,
63
+ ...ctx.referenceType && { "tracking.reference_type": ctx.referenceType }
64
+ }
65
+ },
66
+ async (span) => {
67
+ try {
68
+ const result = await fn(span);
69
+ const durationMs = Date.now() - startMs;
70
+ logger.info(`Completed ${ctx.operation}`, {
71
+ module: MODULE_NAME,
72
+ carrierCode: ctx.carrierCode,
73
+ operation: ctx.operation,
74
+ durationMs,
75
+ success: true
76
+ });
77
+ span.setAttribute("duration_ms", durationMs);
78
+ return result;
79
+ } catch (error) {
80
+ const durationMs = Date.now() - startMs;
81
+ const errorMessage = error instanceof Error ? error.message : "Unknown error";
82
+ logger.error(`Failed ${ctx.operation}`, {
83
+ module: MODULE_NAME,
84
+ carrierCode: ctx.carrierCode,
85
+ operation: ctx.operation,
86
+ durationMs,
87
+ success: false,
88
+ error: errorMessage
89
+ });
90
+ span.setAttribute("duration_ms", durationMs);
91
+ throw error;
92
+ }
93
+ }
94
+ );
95
+ }
96
+ function logRateLimit(ctx) {
97
+ const level = ctx.allowed ? "debug" : "info";
98
+ logger[level]("Rate limit check", {
99
+ module: MODULE_NAME,
100
+ carrierCode: ctx.carrierCode,
101
+ tenantId: ctx.tenantId,
102
+ allowed: ctx.allowed,
103
+ retryAfterSeconds: ctx.retryAfterSeconds
104
+ });
105
+ }
106
+ const trackingLogger = logger;
107
+ export {
108
+ logRateLimit,
109
+ trackingLogger,
110
+ withCarrierApiSpan,
111
+ withCarrierContext
112
+ };
113
+ //# sourceMappingURL=logger.js.map
@@ -0,0 +1,7 @@
1
+ {
2
+ "version": 3,
3
+ "sources": ["../../../../src/modules/shipment_tracking/lib/logger.ts"],
4
+ "sourcesContent": ["import { AsyncLocalStorage } from 'node:async_hooks'\nimport { trace, SpanStatusCode, type Span } from '@opentelemetry/api'\n\nconst MODULE_NAME = 'shipment-tracking'\n\n// \u2500\u2500 Inlined structured logger + tracing \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// Previously provided by `@open-mercato/logger`, which is not published to the\n// public npm registry. Reimplemented locally over `@opentelemetry/api` (already\n// a dependency) and `node:async_hooks` so this package installs from public\n// registries with no private dependency. Behaviour parity: JSON structured\n// logs that carry any active async-local context, and an OTel active span\n// wrapper that records exceptions and always ends the span.\n\ntype LogContext = Record<string, unknown>\ntype LogLevel = 'debug' | 'info' | 'warn' | 'error'\n\nconst logContext = new AsyncLocalStorage<LogContext>()\n\nfunction emit(level: LogLevel, message: string, fields?: LogContext): void {\n const record: LogContext = {\n level,\n time: new Date().toISOString(),\n message,\n ...logContext.getStore(),\n ...fields,\n }\n const line = JSON.stringify(record)\n if (level === 'error') console.error(line)\n else if (level === 'warn') console.warn(line)\n else console.log(line)\n}\n\nconst logger = {\n debug: (message: string, fields?: LogContext) => emit('debug', message, fields),\n info: (message: string, fields?: LogContext) => emit('info', message, fields),\n warn: (message: string, fields?: LogContext) => emit('warn', message, fields),\n error: (message: string, fields?: LogContext) => emit('error', message, fields),\n}\n\nfunction runWithLogContext<T>(ctx: LogContext, fn: () => T | Promise<T>): T | Promise<T> {\n return logContext.run({ ...logContext.getStore(), ...ctx }, fn)\n}\n\nconst tracer = trace.getTracer(MODULE_NAME)\n\nfunction withSpan<T>(\n opts: { name: string; attributes?: Record<string, string | number | boolean | undefined> },\n fn: (span: Span) => Promise<T>,\n): Promise<T> {\n const attributes = Object.fromEntries(\n Object.entries(opts.attributes ?? {}).filter(([, v]) => v !== undefined),\n ) as Record<string, string | number | boolean>\n return tracer.startActiveSpan(opts.name, { attributes }, async (span) => {\n try {\n return await fn(span)\n } catch (error) {\n span.recordException(error as Error)\n span.setStatus({ code: SpanStatusCode.ERROR })\n throw error\n } finally {\n span.end()\n }\n })\n}\n\n// \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\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\nexport type CarrierOperation = 'authenticate' | 'fetchEvents'\n\nexport interface CarrierApiContext {\n carrierCode: string\n operation: CarrierOperation\n referenceType?: string\n referenceValue?: string\n}\n\nexport interface CarrierLogContext {\n carrierCode: string\n shipmentId?: string\n trackingJobId?: string\n}\n\n/**\n * Run a function with carrier context in logs.\n * All logs within the function will include the carrier context fields.\n */\nexport function withCarrierContext<T>(\n ctx: CarrierLogContext,\n fn: () => T | Promise<T>,\n): T | Promise<T> {\n return runWithLogContext({ module: MODULE_NAME, ...ctx }, fn)\n}\n\n/**\n * Run a carrier API call with OpenTelemetry span and structured logging.\n *\n * Creates a span named `carrier.{carrierCode}.{operation}` with relevant attributes.\n * Logs debug on start, info on success, error on failure.\n *\n * @example\n * ```ts\n * const events = await withCarrierApiSpan(\n * { carrierCode: 'maersk', operation: 'fetchEvents', referenceType: 'container' },\n * async (span) => {\n * const response = await fetch(url, options)\n * span.setAttribute('http.status_code', response.status)\n * return response.json()\n * }\n * )\n * ```\n */\nexport async function withCarrierApiSpan<T>(\n ctx: CarrierApiContext,\n fn: (span: Span) => Promise<T>,\n): Promise<T> {\n const spanName = `carrier.${ctx.carrierCode}.${ctx.operation}`\n\n logger.debug(`Starting ${ctx.operation}`, {\n module: MODULE_NAME,\n carrierCode: ctx.carrierCode,\n operation: ctx.operation,\n referenceType: ctx.referenceType,\n })\n\n const startMs = Date.now()\n\n return withSpan(\n {\n name: spanName,\n attributes: {\n 'carrier.code': ctx.carrierCode,\n 'carrier.operation': ctx.operation,\n module: MODULE_NAME,\n ...(ctx.referenceType && { 'tracking.reference_type': ctx.referenceType }),\n },\n },\n async (span) => {\n try {\n const result = await fn(span)\n const durationMs = Date.now() - startMs\n\n logger.info(`Completed ${ctx.operation}`, {\n module: MODULE_NAME,\n carrierCode: ctx.carrierCode,\n operation: ctx.operation,\n durationMs,\n success: true,\n })\n\n span.setAttribute('duration_ms', durationMs)\n return result\n } catch (error) {\n const durationMs = Date.now() - startMs\n const errorMessage = error instanceof Error ? error.message : 'Unknown error'\n\n logger.error(`Failed ${ctx.operation}`, {\n module: MODULE_NAME,\n carrierCode: ctx.carrierCode,\n operation: ctx.operation,\n durationMs,\n success: false,\n error: errorMessage,\n })\n\n span.setAttribute('duration_ms', durationMs)\n throw error\n }\n },\n )\n}\n\n/**\n * Log rate limit events with structured fields.\n */\nexport function logRateLimit(ctx: {\n carrierCode: string\n tenantId: string\n allowed: boolean\n retryAfterSeconds?: number\n}): void {\n const level = ctx.allowed ? 'debug' : 'info'\n logger[level]('Rate limit check', {\n module: MODULE_NAME,\n carrierCode: ctx.carrierCode,\n tenantId: ctx.tenantId,\n allowed: ctx.allowed,\n retryAfterSeconds: ctx.retryAfterSeconds,\n })\n}\n\n/**\n * The shipment tracking logger instance.\n * Use this for general logging within the shipment-tracking module.\n */\nexport const trackingLogger = logger\n"],
5
+ "mappings": "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",
6
+ "names": []
7
+ }
@@ -0,0 +1,183 @@
1
+ import { Shipment, TrackingEvent } from "../data/entities.js";
2
+ import { PROXIMITY_EVENT_CODES } from "./poi-types.js";
3
+ import { trackingLogger } from "./logger.js";
4
+ const LOG_COMPONENT = "poi-processor";
5
+ async function findShipmentsByVesselImo(em, vesselImo) {
6
+ return em.find(Shipment, {
7
+ vesselImo,
8
+ isActive: true,
9
+ deletedAt: null,
10
+ status: { $nin: ["DELIVERED"] },
11
+ trackingJob: { $ne: null }
12
+ });
13
+ }
14
+ function generateSourceEventId(event) {
15
+ const anchor = event.ata || event.atd || getPoiCode(event) || "unknown";
16
+ return `poi-${event.mmsi}-${event.type}-${anchor}-${event.sequenceOrder}`;
17
+ }
18
+ function extractEventDateTime(event) {
19
+ if (event.ata) {
20
+ return new Date(event.ata);
21
+ }
22
+ if (event.atd) {
23
+ return new Date(event.atd);
24
+ }
25
+ return /* @__PURE__ */ new Date();
26
+ }
27
+ function getPoiCode(event) {
28
+ return event.arrivedPoiCode || event.departedPoiCode || null;
29
+ }
30
+ function resolvePoiLocationName(event) {
31
+ if (event.type.startsWith("TERMINAL")) {
32
+ return event.terminalName || event.portName || event.city || null;
33
+ }
34
+ if (event.type.startsWith("PORT")) {
35
+ return event.city || event.portName || null;
36
+ }
37
+ return null;
38
+ }
39
+ function mapToOpenMercatoEventId(type) {
40
+ const eventMap = {
41
+ PORT_ARRIVAL: "shipment_tracking.poi.port_arrival",
42
+ PORT_PROXIMITY_ARRIVAL: "shipment_tracking.poi.port_proximity_arrival",
43
+ PORT_PROXIMITY_DEPARTURE: "shipment_tracking.poi.port_departure",
44
+ TERMINAL_ARRIVAL: "shipment_tracking.poi.terminal_arrival",
45
+ TERMINAL_PROXIMITY_ARRIVAL: "shipment_tracking.poi.terminal_proximity_arrival",
46
+ TERMINAL_PROXIMITY_DEPARTURE: "shipment_tracking.poi.terminal_departure",
47
+ WAYPOINT_REACHED: "shipment_tracking.poi.waypoint_reached"
48
+ };
49
+ return eventMap[type];
50
+ }
51
+ async function processPoiEvent(em, event) {
52
+ const vesselImo = String(event.shipId);
53
+ const shipments = await findShipmentsByVesselImo(em, vesselImo);
54
+ if (shipments.length === 0) {
55
+ trackingLogger.debug("No active shipments found for vessel IMO", {
56
+ component: LOG_COMPONENT,
57
+ mmsi: event.mmsi,
58
+ vesselImo,
59
+ eventType: event.type
60
+ });
61
+ return null;
62
+ }
63
+ return {
64
+ poiEvent: event,
65
+ vesselImo,
66
+ shipmentIds: shipments.map((s) => s.id),
67
+ eventDateTime: extractEventDateTime(event),
68
+ poiCode: getPoiCode(event),
69
+ eventCode: PROXIMITY_EVENT_CODES[event.type],
70
+ sourceEventId: generateSourceEventId(event)
71
+ };
72
+ }
73
+ async function isEventAlreadyProcessed(em, trackingJobId, sourceEventId) {
74
+ const existing = await em.findOne(TrackingEvent, {
75
+ trackingJob: trackingJobId,
76
+ source: "ais",
77
+ sourceEventId
78
+ });
79
+ return existing !== null;
80
+ }
81
+ function createTrackingEventFromPoi(processedEvent, trackingJob) {
82
+ const { poiEvent, eventDateTime, eventCode, sourceEventId, poiCode, vesselImo } = processedEvent;
83
+ const event = new TrackingEvent();
84
+ event.organizationId = trackingJob.organizationId;
85
+ event.tenantId = trackingJob.tenantId;
86
+ event.trackingJob = trackingJob;
87
+ event.source = "ais";
88
+ event.sourceEventId = sourceEventId;
89
+ event.eventType = "TRANSPORT";
90
+ event.eventCode = eventCode;
91
+ event.eventClassifierCode = "ACT";
92
+ event.eventDateTime = eventDateTime;
93
+ event.description = null;
94
+ event.latitude = poiEvent.lat;
95
+ event.longitude = poiEvent.lng;
96
+ event.vesselName = poiEvent.shipName;
97
+ event.vesselImo = vesselImo;
98
+ event.locationUnlocode = poiCode;
99
+ event.locationName = resolvePoiLocationName(poiEvent);
100
+ event.regionNamePl = poiEvent.regionNamePl ?? null;
101
+ event.regionNameEn = poiEvent.regionNameEn ?? null;
102
+ event.rawData = {
103
+ poiEventType: poiEvent.type,
104
+ mmsi: poiEvent.mmsi,
105
+ shipId: poiEvent.shipId,
106
+ arrivedPoiCode: poiEvent.arrivedPoiCode,
107
+ departedPoiCode: poiEvent.departedPoiCode,
108
+ distanceToPoiMeters: poiEvent.distanceToPoiMeters,
109
+ sequenceOrder: poiEvent.sequenceOrder,
110
+ // POI identity fields without dedicated columns — kept here for debugging. The resolved
111
+ // name lands in locationName; region names have their own columns.
112
+ portName: poiEvent.portName,
113
+ terminalName: poiEvent.terminalName,
114
+ city: poiEvent.city
115
+ };
116
+ return event;
117
+ }
118
+ async function processPoiEventBatch(em, events) {
119
+ const result = {
120
+ processed: 0,
121
+ matched: 0,
122
+ eventsCreated: 0,
123
+ errors: 0
124
+ };
125
+ for (const event of events) {
126
+ result.processed++;
127
+ try {
128
+ const processed = await processPoiEvent(em, event);
129
+ if (!processed) {
130
+ continue;
131
+ }
132
+ result.matched++;
133
+ for (const shipmentId of processed.shipmentIds) {
134
+ const shipment = await em.findOne(Shipment, { id: shipmentId }, { populate: ["trackingJob"] });
135
+ if (!shipment?.trackingJob) {
136
+ continue;
137
+ }
138
+ const alreadyProcessed = await isEventAlreadyProcessed(
139
+ em,
140
+ shipment.trackingJob.id,
141
+ processed.sourceEventId
142
+ );
143
+ if (alreadyProcessed) {
144
+ trackingLogger.debug("Event already processed", {
145
+ component: LOG_COMPONENT,
146
+ sourceEventId: processed.sourceEventId,
147
+ trackingJobId: shipment.trackingJob.id
148
+ });
149
+ continue;
150
+ }
151
+ const trackingEvent = createTrackingEventFromPoi(processed, shipment.trackingJob);
152
+ em.persist(trackingEvent);
153
+ result.eventsCreated++;
154
+ }
155
+ } catch (error) {
156
+ trackingLogger.error("Error processing POI event", {
157
+ component: LOG_COMPONENT,
158
+ mmsi: event.mmsi,
159
+ eventType: event.type,
160
+ error: error instanceof Error ? error.message : String(error),
161
+ stack: error instanceof Error ? error.stack : void 0
162
+ });
163
+ result.errors++;
164
+ }
165
+ }
166
+ if (result.eventsCreated > 0) {
167
+ await em.flush();
168
+ }
169
+ return result;
170
+ }
171
+ export {
172
+ createTrackingEventFromPoi,
173
+ extractEventDateTime,
174
+ findShipmentsByVesselImo,
175
+ generateSourceEventId,
176
+ getPoiCode,
177
+ isEventAlreadyProcessed,
178
+ mapToOpenMercatoEventId,
179
+ processPoiEvent,
180
+ processPoiEventBatch,
181
+ resolvePoiLocationName
182
+ };
183
+ //# sourceMappingURL=poi-event-processor.js.map
@@ -0,0 +1,7 @@
1
+ {
2
+ "version": 3,
3
+ "sources": ["../../../../src/modules/shipment_tracking/lib/poi-event-processor.ts"],
4
+ "sourcesContent": ["/**\n * POI Event Processor\n *\n * Processes POI proximity events from AIS system and matches them\n * to tracked shipments for notification and event creation.\n */\n\nimport type { EntityManager } from '@mikro-orm/postgresql'\nimport { Shipment, TrackingEvent, TrackingJob } from '../data/entities'\nimport type { PoiProximityEvent, ProcessedPoiEvent, ProximityEventType } from './poi-types'\nimport { PROXIMITY_EVENT_CODES } from './poi-types'\nimport { trackingLogger } from './logger'\n\nconst LOG_COMPONENT = 'poi-processor'\n\n// \u2500\u2500\u2500 Shipment Matching \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\n\n/**\n * Find active shipments that match the given vessel IMO.\n *\n * A shipment matches if:\n * - It has the same vesselImo\n * - It's active (not deleted, not delivered)\n * - It has an associated tracking job (tracking is enabled)\n */\nexport async function findShipmentsByVesselImo(\n em: EntityManager,\n vesselImo: string\n): Promise<Shipment[]> {\n return em.find(Shipment, {\n vesselImo,\n isActive: true,\n deletedAt: null,\n status: { $nin: ['DELIVERED'] },\n trackingJob: { $ne: null },\n })\n}\n\n// \u2500\u2500\u2500 Event Processing \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\n\n/**\n * Generate a deterministic source event ID for deduplication.\n *\n * Must be stable across redeliveries (nak / max_deliver retries / consumer replay),\n * so it derives only from payload fields \u2014 never the wall clock. Arrival/departure\n * events anchor on ata/atd; timeless events (e.g. WAYPOINT_REACHED) anchor on the POI code.\n *\n * Caveat: two distinct position-update batches that emit the same type + sequenceOrder\n * for the same vessel with no ata/atd and no POI code collapse to one event. Acceptable\n * for waypoints; revisit with a producer-supplied event id or JetStream msg.seq if real\n * duplicates surface.\n */\nexport function generateSourceEventId(event: PoiProximityEvent): string {\n const anchor = event.ata || event.atd || getPoiCode(event) || 'unknown'\n return `poi-${event.mmsi}-${event.type}-${anchor}-${event.sequenceOrder}`\n}\n\n/**\n * Extract event datetime from POI event.\n */\nexport function extractEventDateTime(event: PoiProximityEvent): Date {\n if (event.ata) {\n return new Date(event.ata)\n }\n if (event.atd) {\n return new Date(event.atd)\n }\n return new Date()\n}\n\n/**\n * Get POI code from event (arrival or departure).\n */\nexport function getPoiCode(event: PoiProximityEvent): string | null {\n return event.arrivedPoiCode || event.departedPoiCode || null\n}\n\n/**\n * Resolve the human-friendly location name for a POI event, following the producer's\n * per-type identity fallback:\n * - terminal events \u2192 terminalName \u2192 portName \u2192 city\n * - port events \u2192 city \u2192 portName\n * - waypoint events \u2192 null (no port/terminal/city; the localized region name is used instead,\n * stored separately in regionNamePl/regionNameEn and chosen at render time)\n *\n * Returns null when nothing usable is present, so callers fall back to the raw POI code.\n */\nexport function resolvePoiLocationName(event: PoiProximityEvent): string | null {\n if (event.type.startsWith('TERMINAL')) {\n return event.terminalName || event.portName || event.city || null\n }\n if (event.type.startsWith('PORT')) {\n return event.city || event.portName || null\n }\n return null\n}\n\n/**\n * Map POI event type to Open Mercato event ID.\n */\nexport function mapToOpenMercatoEventId(type: ProximityEventType): string {\n const eventMap: Record<ProximityEventType, string> = {\n PORT_ARRIVAL: 'shipment_tracking.poi.port_arrival',\n PORT_PROXIMITY_ARRIVAL: 'shipment_tracking.poi.port_proximity_arrival',\n PORT_PROXIMITY_DEPARTURE: 'shipment_tracking.poi.port_departure',\n TERMINAL_ARRIVAL: 'shipment_tracking.poi.terminal_arrival',\n TERMINAL_PROXIMITY_ARRIVAL: 'shipment_tracking.poi.terminal_proximity_arrival',\n TERMINAL_PROXIMITY_DEPARTURE: 'shipment_tracking.poi.terminal_departure',\n WAYPOINT_REACHED: 'shipment_tracking.poi.waypoint_reached',\n }\n return eventMap[type]\n}\n\n/**\n * Process a POI event and match it to shipments.\n *\n * @param em - Entity manager\n * @param event - Raw POI proximity event from NATS\n * @returns Processed event with matched shipments, or null if no matches\n */\nexport async function processPoiEvent(\n em: EntityManager,\n event: PoiProximityEvent\n): Promise<ProcessedPoiEvent | null> {\n // The AIS detector sets `shipId` from `ship.id`, which is the vessel IMO.\n // Match directly against the IMO we already store on the shipment \u2014 no lookup needed.\n const vesselImo = String(event.shipId)\n const shipments = await findShipmentsByVesselImo(em, vesselImo)\n\n if (shipments.length === 0) {\n trackingLogger.debug('No active shipments found for vessel IMO', {\n component: LOG_COMPONENT,\n mmsi: event.mmsi,\n vesselImo,\n eventType: event.type,\n })\n return null\n }\n\n return {\n poiEvent: event,\n vesselImo,\n shipmentIds: shipments.map((s) => s.id),\n eventDateTime: extractEventDateTime(event),\n poiCode: getPoiCode(event),\n eventCode: PROXIMITY_EVENT_CODES[event.type],\n sourceEventId: generateSourceEventId(event),\n }\n}\n\n// \u2500\u2500\u2500 TrackingEvent Creation \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\n\n/**\n * Check if a POI event has already been processed (deduplication).\n */\nexport async function isEventAlreadyProcessed(\n em: EntityManager,\n trackingJobId: string,\n sourceEventId: string\n): Promise<boolean> {\n const existing = await em.findOne(TrackingEvent, {\n trackingJob: trackingJobId,\n source: 'ais',\n sourceEventId,\n })\n return existing !== null\n}\n\n/**\n * Create a TrackingEvent record from a processed POI event.\n */\nexport function createTrackingEventFromPoi(\n processedEvent: ProcessedPoiEvent,\n trackingJob: TrackingJob\n): TrackingEvent {\n const { poiEvent, eventDateTime, eventCode, sourceEventId, poiCode, vesselImo } = processedEvent\n\n const event = new TrackingEvent()\n event.organizationId = trackingJob.organizationId\n event.tenantId = trackingJob.tenantId\n event.trackingJob = trackingJob\n event.source = 'ais'\n event.sourceEventId = sourceEventId\n event.eventType = 'TRANSPORT'\n event.eventCode = eventCode\n event.eventClassifierCode = 'ACT' // Actual event\n event.eventDateTime = eventDateTime\n event.description = null // Labels resolved via i18n: event_codes.<CODE>\n event.latitude = poiEvent.lat\n event.longitude = poiEvent.lng\n event.vesselName = poiEvent.shipName\n // The POI producer sets shipId to the vessel IMO (that's how we matched the shipment),\n // so persist it \u2014 route legs/maps need the IMO, not just the name.\n event.vesselImo = vesselImo\n event.locationUnlocode = poiCode // raw POI code (grid/LOCODE), kept as the dedup reference\n // Friendly port/terminal name (null for waypoints, which use the localized region name below).\n // Populating locationName also gives route extraction a real name instead of the grid code.\n event.locationName = resolvePoiLocationName(poiEvent)\n event.regionNamePl = poiEvent.regionNamePl ?? null\n event.regionNameEn = poiEvent.regionNameEn ?? null\n\n // Store raw event data for debugging\n event.rawData = {\n poiEventType: poiEvent.type,\n mmsi: poiEvent.mmsi,\n shipId: poiEvent.shipId,\n arrivedPoiCode: poiEvent.arrivedPoiCode,\n departedPoiCode: poiEvent.departedPoiCode,\n distanceToPoiMeters: poiEvent.distanceToPoiMeters,\n sequenceOrder: poiEvent.sequenceOrder,\n // POI identity fields without dedicated columns \u2014 kept here for debugging. The resolved\n // name lands in locationName; region names have their own columns.\n portName: poiEvent.portName,\n terminalName: poiEvent.terminalName,\n city: poiEvent.city,\n }\n\n return event\n}\n\n// \u2500\u2500\u2500 Batch Processing \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\n\nexport type PoiProcessingResult = {\n processed: number\n matched: number\n eventsCreated: number\n errors: number\n}\n\n/**\n * Process a batch of POI events.\n */\nexport async function processPoiEventBatch(\n em: EntityManager,\n events: PoiProximityEvent[]\n): Promise<PoiProcessingResult> {\n const result: PoiProcessingResult = {\n processed: 0,\n matched: 0,\n eventsCreated: 0,\n errors: 0,\n }\n\n for (const event of events) {\n result.processed++\n\n try {\n const processed = await processPoiEvent(em, event)\n\n if (!processed) {\n continue\n }\n\n result.matched++\n\n // For each matched shipment, check if we need to create events\n for (const shipmentId of processed.shipmentIds) {\n const shipment = await em.findOne(Shipment, { id: shipmentId }, { populate: ['trackingJob'] })\n\n if (!shipment?.trackingJob) {\n continue\n }\n\n // Check for duplicates\n const alreadyProcessed = await isEventAlreadyProcessed(\n em,\n shipment.trackingJob.id,\n processed.sourceEventId\n )\n\n if (alreadyProcessed) {\n trackingLogger.debug('Event already processed', {\n component: LOG_COMPONENT,\n sourceEventId: processed.sourceEventId,\n trackingJobId: shipment.trackingJob.id,\n })\n continue\n }\n\n // Create TrackingEvent\n const trackingEvent = createTrackingEventFromPoi(processed, shipment.trackingJob)\n em.persist(trackingEvent)\n result.eventsCreated++\n }\n } catch (error) {\n trackingLogger.error('Error processing POI event', {\n component: LOG_COMPONENT,\n mmsi: event.mmsi,\n eventType: event.type,\n error: error instanceof Error ? error.message : String(error),\n stack: error instanceof Error ? error.stack : undefined,\n })\n result.errors++\n }\n }\n\n if (result.eventsCreated > 0) {\n await em.flush()\n }\n\n return result\n}\n"],
5
+ "mappings": "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",
6
+ "names": []
7
+ }
@@ -0,0 +1,52 @@
1
+ import { createHash, randomUUID } from "crypto";
2
+ function generateInstanceId() {
3
+ if (process.env.POI_NATS_INSTANCE_ID) {
4
+ return process.env.POI_NATS_INSTANCE_ID;
5
+ }
6
+ const jwtSecret = process.env.JWT_SECRET;
7
+ if (jwtSecret) {
8
+ const hash = createHash("sha256").update(jwtSecret).digest("hex");
9
+ return hash.substring(0, 8);
10
+ }
11
+ console.warn("[poi-nats] JWT_SECRET not set, using random instance ID (not stable across restarts)");
12
+ return randomUUID().split("-")[0];
13
+ }
14
+ const DEFAULT_RECONNECT = {
15
+ maxAttempts: -1,
16
+ // Infinite reconnect
17
+ initialDelayMs: 250,
18
+ maxDelayMs: 5e3
19
+ };
20
+ function loadPoiNatsConfig() {
21
+ const instanceId = generateInstanceId();
22
+ const consumerName = `shipment-tracking-poi-${instanceId}`;
23
+ return {
24
+ serverUrl: process.env.POI_NATS_URL || "nats://localhost:4222",
25
+ streamName: process.env.POI_NATS_STREAM || "AIS_STREAM",
26
+ subject: process.env.POI_NATS_SUBJECT || "ais.ship.proximity.events",
27
+ instanceId,
28
+ consumerName,
29
+ connectionName: `shipment-tracking-poi-${instanceId}`,
30
+ reconnect: DEFAULT_RECONNECT
31
+ };
32
+ }
33
+ function isPoiNatsConfigured() {
34
+ return !!process.env.POI_NATS_URL;
35
+ }
36
+ function validatePoiNatsConfig(config) {
37
+ if (!config.serverUrl) {
38
+ throw new Error("POI_NATS_URL is required");
39
+ }
40
+ if (!config.serverUrl.startsWith("nats://") && !config.serverUrl.startsWith("nats+tls://")) {
41
+ throw new Error("POI_NATS_URL must start with nats:// or nats+tls://");
42
+ }
43
+ if (!config.subject) {
44
+ throw new Error("POI_NATS_SUBJECT is required");
45
+ }
46
+ }
47
+ export {
48
+ isPoiNatsConfigured,
49
+ loadPoiNatsConfig,
50
+ validatePoiNatsConfig
51
+ };
52
+ //# sourceMappingURL=poi-nats-config.js.map
@@ -0,0 +1,7 @@
1
+ {
2
+ "version": 3,
3
+ "sources": ["../../../../src/modules/shipment_tracking/lib/poi-nats-config.ts"],
4
+ "sourcesContent": ["/**\n * NATS Configuration for POI Proximity Events\n *\n * Configuration for connecting to NATS server and subscribing to\n * POI proximity events from the AIS system.\n */\n\nimport { createHash, randomUUID } from 'crypto'\n\n// \u2500\u2500\u2500 Configuration Interface \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\n\nexport interface PoiNatsConfig {\n /** NATS server URL (e.g., nats://localhost:4222) */\n serverUrl: string\n\n /** JetStream stream name containing POI events */\n streamName: string\n\n /** NATS subject for POI proximity events (filter within stream) */\n subject: string\n\n /** Unique instance identifier (derived from JWT_SECRET or explicit) */\n instanceId: string\n\n /** Durable consumer name for reliable message delivery (includes instanceId) */\n consumerName: string\n\n /** Connection name for debugging */\n connectionName: string\n\n /** Reconnect settings */\n reconnect: {\n /** Maximum reconnect attempts (-1 for infinite) */\n maxAttempts: number\n /** Initial delay between reconnects in ms */\n initialDelayMs: number\n /** Maximum delay between reconnects in ms */\n maxDelayMs: number\n }\n}\n\n// \u2500\u2500\u2500 Instance ID Generation \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\n\n/**\n * Generate a unique instance ID for this deployment.\n *\n * Priority:\n * 1. POI_NATS_INSTANCE_ID env var (explicit override)\n * 2. Hash of JWT_SECRET (stable, unique per installation)\n * 3. Random UUID prefix (fallback for dev/testing)\n */\nfunction generateInstanceId(): string {\n // 1. Explicit override\n if (process.env.POI_NATS_INSTANCE_ID) {\n return process.env.POI_NATS_INSTANCE_ID\n }\n\n // 2. Derive from JWT_SECRET (stable across restarts)\n const jwtSecret = process.env.JWT_SECRET\n if (jwtSecret) {\n const hash = createHash('sha256').update(jwtSecret).digest('hex')\n return hash.substring(0, 8)\n }\n\n // 3. Fallback to random (dev/testing only)\n console.warn('[poi-nats] JWT_SECRET not set, using random instance ID (not stable across restarts)')\n return randomUUID().split('-')[0]\n}\n\n// \u2500\u2500\u2500 Default Configuration \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\n\nconst DEFAULT_RECONNECT = {\n maxAttempts: -1, // Infinite reconnect\n initialDelayMs: 250,\n maxDelayMs: 5000,\n}\n\n// \u2500\u2500\u2500 Configuration Loader \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\n\n/**\n * Load POI NATS configuration from environment variables.\n *\n * Environment variables:\n * - POI_NATS_URL: NATS server URL (required to enable)\n * - POI_NATS_STREAM: JetStream stream name (default: AIS_STREAM)\n * - POI_NATS_SUBJECT: Subject filter for POI events (default: ais.ship.proximity.events)\n * - POI_NATS_INSTANCE_ID: Unique instance identifier (default: derived from JWT_SECRET)\n *\n * The consumer name is auto-generated as: shipment-tracking-poi-{instanceId}\n * This allows multiple self-hosted instances to each receive all messages independently.\n */\nexport function loadPoiNatsConfig(): PoiNatsConfig {\n const instanceId = generateInstanceId()\n const consumerName = `shipment-tracking-poi-${instanceId}`\n\n return {\n serverUrl: process.env.POI_NATS_URL || 'nats://localhost:4222',\n streamName: process.env.POI_NATS_STREAM || 'AIS_STREAM',\n subject: process.env.POI_NATS_SUBJECT || 'ais.ship.proximity.events',\n instanceId,\n consumerName,\n connectionName: `shipment-tracking-poi-${instanceId}`,\n reconnect: DEFAULT_RECONNECT,\n }\n}\n\n/**\n * Check if POI NATS integration is configured.\n *\n * Returns true only if POI_NATS_URL is explicitly set.\n * When not configured, the POI subscriber will be skipped.\n */\nexport function isPoiNatsConfigured(): boolean {\n return !!process.env.POI_NATS_URL\n}\n\n/**\n * Validate POI NATS configuration.\n *\n * @throws Error if configuration is invalid\n */\nexport function validatePoiNatsConfig(config: PoiNatsConfig): void {\n if (!config.serverUrl) {\n throw new Error('POI_NATS_URL is required')\n }\n\n if (!config.serverUrl.startsWith('nats://') && !config.serverUrl.startsWith('nats+tls://')) {\n throw new Error('POI_NATS_URL must start with nats:// or nats+tls://')\n }\n\n if (!config.subject) {\n throw new Error('POI_NATS_SUBJECT is required')\n }\n}\n"],
5
+ "mappings": "AAOA,SAAS,YAAY,kBAAkB;AA4CvC,SAAS,qBAA6B;AAEpC,MAAI,QAAQ,IAAI,sBAAsB;AACpC,WAAO,QAAQ,IAAI;AAAA,EACrB;AAGA,QAAM,YAAY,QAAQ,IAAI;AAC9B,MAAI,WAAW;AACb,UAAM,OAAO,WAAW,QAAQ,EAAE,OAAO,SAAS,EAAE,OAAO,KAAK;AAChE,WAAO,KAAK,UAAU,GAAG,CAAC;AAAA,EAC5B;AAGA,UAAQ,KAAK,sFAAsF;AACnG,SAAO,WAAW,EAAE,MAAM,GAAG,EAAE,CAAC;AAClC;AAIA,MAAM,oBAAoB;AAAA,EACxB,aAAa;AAAA;AAAA,EACb,gBAAgB;AAAA,EAChB,YAAY;AACd;AAgBO,SAAS,oBAAmC;AACjD,QAAM,aAAa,mBAAmB;AACtC,QAAM,eAAe,yBAAyB,UAAU;AAExD,SAAO;AAAA,IACL,WAAW,QAAQ,IAAI,gBAAgB;AAAA,IACvC,YAAY,QAAQ,IAAI,mBAAmB;AAAA,IAC3C,SAAS,QAAQ,IAAI,oBAAoB;AAAA,IACzC;AAAA,IACA;AAAA,IACA,gBAAgB,yBAAyB,UAAU;AAAA,IACnD,WAAW;AAAA,EACb;AACF;AAQO,SAAS,sBAA+B;AAC7C,SAAO,CAAC,CAAC,QAAQ,IAAI;AACvB;AAOO,SAAS,sBAAsB,QAA6B;AACjE,MAAI,CAAC,OAAO,WAAW;AACrB,UAAM,IAAI,MAAM,0BAA0B;AAAA,EAC5C;AAEA,MAAI,CAAC,OAAO,UAAU,WAAW,SAAS,KAAK,CAAC,OAAO,UAAU,WAAW,aAAa,GAAG;AAC1F,UAAM,IAAI,MAAM,qDAAqD;AAAA,EACvE;AAEA,MAAI,CAAC,OAAO,SAAS;AACnB,UAAM,IAAI,MAAM,8BAA8B;AAAA,EAChD;AACF;",
6
+ "names": []
7
+ }