@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.
- package/LICENSE +21 -0
- package/NOTICE +25 -0
- package/dist/generated/entities/bic_config/index.js +21 -0
- package/dist/generated/entities/bic_config/index.js.map +7 -0
- package/dist/generated/entities/carrier_config/index.js +27 -0
- package/dist/generated/entities/carrier_config/index.js.map +7 -0
- package/dist/generated/entities/location_override/index.js +33 -0
- package/dist/generated/entities/location_override/index.js.map +7 -0
- package/dist/generated/entities/shipment/index.js +73 -0
- package/dist/generated/entities/shipment/index.js.map +7 -0
- package/dist/generated/entities/ships_go_config/index.js +31 -0
- package/dist/generated/entities/ships_go_config/index.js.map +7 -0
- package/dist/generated/entities/tracking_event/index.js +101 -0
- package/dist/generated/entities/tracking_event/index.js.map +7 -0
- package/dist/generated/entities/tracking_job/index.js +47 -0
- package/dist/generated/entities/tracking_job/index.js.map +7 -0
- package/dist/generated/entities/webhook/index.js +25 -0
- package/dist/generated/entities/webhook/index.js.map +7 -0
- package/dist/generated/entities/webhook_delivery/index.js +25 -0
- package/dist/generated/entities/webhook_delivery/index.js.map +7 -0
- package/dist/generated/entities.ids.generated.js +21 -0
- package/dist/generated/entities.ids.generated.js.map +7 -0
- package/dist/index.js +41 -0
- package/dist/index.js.map +7 -0
- package/dist/modules/shipment_tracking/acl.js +22 -0
- package/dist/modules/shipment_tracking/acl.js.map +7 -0
- package/dist/modules/shipment_tracking/api/bic-configs/route.js +176 -0
- package/dist/modules/shipment_tracking/api/bic-configs/route.js.map +7 -0
- package/dist/modules/shipment_tracking/api/bic-configs/test/route.js +95 -0
- package/dist/modules/shipment_tracking/api/bic-configs/test/route.js.map +7 -0
- package/dist/modules/shipment_tracking/api/cargo-events/route.js +93 -0
- package/dist/modules/shipment_tracking/api/cargo-events/route.js.map +7 -0
- package/dist/modules/shipment_tracking/api/carrier-configs/route.js +139 -0
- package/dist/modules/shipment_tracking/api/carrier-configs/route.js.map +7 -0
- package/dist/modules/shipment_tracking/api/dashboard/ships-map/route.js +177 -0
- package/dist/modules/shipment_tracking/api/dashboard/ships-map/route.js.map +7 -0
- package/dist/modules/shipment_tracking/api/location-overrides/route.js +179 -0
- package/dist/modules/shipment_tracking/api/location-overrides/route.js.map +7 -0
- package/dist/modules/shipment_tracking/api/openapi.js +27 -0
- package/dist/modules/shipment_tracking/api/openapi.js.map +7 -0
- package/dist/modules/shipment_tracking/api/shipments/route.js +192 -0
- package/dist/modules/shipment_tracking/api/shipments/route.js.map +7 -0
- package/dist/modules/shipment_tracking/api/shipsgo-configs/route.js +185 -0
- package/dist/modules/shipment_tracking/api/shipsgo-configs/route.js.map +7 -0
- package/dist/modules/shipment_tracking/api/tracking-jobs/route.js +144 -0
- package/dist/modules/shipment_tracking/api/tracking-jobs/route.js.map +7 -0
- package/dist/modules/shipment_tracking/api/tracking-jobs/sync/route.js +107 -0
- package/dist/modules/shipment_tracking/api/tracking-jobs/sync/route.js.map +7 -0
- package/dist/modules/shipment_tracking/api/utils.js +12 -0
- package/dist/modules/shipment_tracking/api/utils.js.map +7 -0
- package/dist/modules/shipment_tracking/api/webhooks/deliveries/route.js +76 -0
- package/dist/modules/shipment_tracking/api/webhooks/deliveries/route.js.map +7 -0
- package/dist/modules/shipment_tracking/api/webhooks/route.js +135 -0
- package/dist/modules/shipment_tracking/api/webhooks/route.js.map +7 -0
- package/dist/modules/shipment_tracking/api/webhooks/test/route.js +71 -0
- package/dist/modules/shipment_tracking/api/webhooks/test/route.js.map +7 -0
- package/dist/modules/shipment_tracking/assets/carriers/cma_cgm.png +0 -0
- package/dist/modules/shipment_tracking/assets/carriers/cosco.png +0 -0
- package/dist/modules/shipment_tracking/assets/carriers/evergreen.png +0 -0
- package/dist/modules/shipment_tracking/assets/carriers/hapag_lloyd.png +0 -0
- package/dist/modules/shipment_tracking/assets/carriers/maersk.png +0 -0
- package/dist/modules/shipment_tracking/assets/carriers/msc.png +0 -0
- package/dist/modules/shipment_tracking/assets/carriers/one.png +0 -0
- package/dist/modules/shipment_tracking/assets/carriers/oocl.png +0 -0
- package/dist/modules/shipment_tracking/assets/carriers/yang_ming.png +0 -0
- package/dist/modules/shipment_tracking/assets/carriers/zim.png +0 -0
- package/dist/modules/shipment_tracking/assets/index.js +52 -0
- package/dist/modules/shipment_tracking/assets/index.js.map +7 -0
- package/dist/modules/shipment_tracking/backend/carrier-configs/page.js +621 -0
- package/dist/modules/shipment_tracking/backend/carrier-configs/page.js.map +7 -0
- package/dist/modules/shipment_tracking/backend/carrier-configs/page.meta.js +19 -0
- package/dist/modules/shipment_tracking/backend/carrier-configs/page.meta.js.map +7 -0
- package/dist/modules/shipment_tracking/backend/location-overrides/page.js +198 -0
- package/dist/modules/shipment_tracking/backend/location-overrides/page.js.map +7 -0
- package/dist/modules/shipment_tracking/backend/location-overrides/page.meta.js +19 -0
- package/dist/modules/shipment_tracking/backend/location-overrides/page.meta.js.map +7 -0
- package/dist/modules/shipment_tracking/backend/shipment-tracking/page.js +279 -0
- package/dist/modules/shipment_tracking/backend/shipment-tracking/page.js.map +7 -0
- package/dist/modules/shipment_tracking/backend/shipment-tracking/page.meta.js +19 -0
- package/dist/modules/shipment_tracking/backend/shipment-tracking/page.meta.js.map +7 -0
- package/dist/modules/shipment_tracking/backend/tracking-jobs/page.js +10 -0
- package/dist/modules/shipment_tracking/backend/tracking-jobs/page.js.map +7 -0
- package/dist/modules/shipment_tracking/backend/tracking-jobs/page.meta.js +19 -0
- package/dist/modules/shipment_tracking/backend/tracking-jobs/page.meta.js.map +7 -0
- package/dist/modules/shipment_tracking/backend/webhooks/page.js +198 -0
- package/dist/modules/shipment_tracking/backend/webhooks/page.js.map +7 -0
- package/dist/modules/shipment_tracking/backend/webhooks/page.meta.js +19 -0
- package/dist/modules/shipment_tracking/backend/webhooks/page.meta.js.map +7 -0
- package/dist/modules/shipment_tracking/ce.js +52 -0
- package/dist/modules/shipment_tracking/ce.js.map +7 -0
- package/dist/modules/shipment_tracking/cli.js +229 -0
- package/dist/modules/shipment_tracking/cli.js.map +7 -0
- package/dist/modules/shipment_tracking/commands/bic-configs.js +66 -0
- package/dist/modules/shipment_tracking/commands/bic-configs.js.map +7 -0
- package/dist/modules/shipment_tracking/commands/carrier-configs.js +65 -0
- package/dist/modules/shipment_tracking/commands/carrier-configs.js.map +7 -0
- package/dist/modules/shipment_tracking/commands/index.js +9 -0
- package/dist/modules/shipment_tracking/commands/index.js.map +7 -0
- package/dist/modules/shipment_tracking/commands/location-overrides.js +93 -0
- package/dist/modules/shipment_tracking/commands/location-overrides.js.map +7 -0
- package/dist/modules/shipment_tracking/commands/shipments.js +200 -0
- package/dist/modules/shipment_tracking/commands/shipments.js.map +7 -0
- package/dist/modules/shipment_tracking/commands/shipsgo-configs.js +75 -0
- package/dist/modules/shipment_tracking/commands/shipsgo-configs.js.map +7 -0
- package/dist/modules/shipment_tracking/commands/tracking-jobs.js +120 -0
- package/dist/modules/shipment_tracking/commands/tracking-jobs.js.map +7 -0
- package/dist/modules/shipment_tracking/commands/tracking.js +140 -0
- package/dist/modules/shipment_tracking/commands/tracking.js.map +7 -0
- package/dist/modules/shipment_tracking/commands/webhooks.js +115 -0
- package/dist/modules/shipment_tracking/commands/webhooks.js.map +7 -0
- package/dist/modules/shipment_tracking/components/CombinedTimestampCell.js +168 -0
- package/dist/modules/shipment_tracking/components/CombinedTimestampCell.js.map +7 -0
- package/dist/modules/shipment_tracking/components/FacilityLocationInput.js +202 -0
- package/dist/modules/shipment_tracking/components/FacilityLocationInput.js.map +7 -0
- package/dist/modules/shipment_tracking/components/LocationOverrideDrawer.js +501 -0
- package/dist/modules/shipment_tracking/components/LocationOverrideDrawer.js.map +7 -0
- package/dist/modules/shipment_tracking/components/ShipmentDetailsDrawer.js +818 -0
- package/dist/modules/shipment_tracking/components/ShipmentDetailsDrawer.js.map +7 -0
- package/dist/modules/shipment_tracking/components/ShipmentDrawer.js +404 -0
- package/dist/modules/shipment_tracking/components/ShipmentDrawer.js.map +7 -0
- package/dist/modules/shipment_tracking/components/ShipsGoConfigSection.js +200 -0
- package/dist/modules/shipment_tracking/components/ShipsGoConfigSection.js.map +7 -0
- package/dist/modules/shipment_tracking/components/TimestampCell.js +108 -0
- package/dist/modules/shipment_tracking/components/TimestampCell.js.map +7 -0
- package/dist/modules/shipment_tracking/components/TrackingJobDrawer.js +343 -0
- package/dist/modules/shipment_tracking/components/TrackingJobDrawer.js.map +7 -0
- package/dist/modules/shipment_tracking/components/VesselTrackingMap.js +496 -0
- package/dist/modules/shipment_tracking/components/VesselTrackingMap.js.map +7 -0
- package/dist/modules/shipment_tracking/components/WebhookDrawer.js +332 -0
- package/dist/modules/shipment_tracking/components/WebhookDrawer.js.map +7 -0
- package/dist/modules/shipment_tracking/data/entities.js +686 -0
- package/dist/modules/shipment_tracking/data/entities.js.map +7 -0
- package/dist/modules/shipment_tracking/data/validators.js +434 -0
- package/dist/modules/shipment_tracking/data/validators.js.map +7 -0
- package/dist/modules/shipment_tracking/di.js +60 -0
- package/dist/modules/shipment_tracking/di.js.map +7 -0
- package/dist/modules/shipment_tracking/encryption.js +12 -0
- package/dist/modules/shipment_tracking/encryption.js.map +7 -0
- package/dist/modules/shipment_tracking/events.js +87 -0
- package/dist/modules/shipment_tracking/events.js.map +7 -0
- package/dist/modules/shipment_tracking/hooks/useVesselTracking.js +137 -0
- package/dist/modules/shipment_tracking/hooks/useVesselTracking.js.map +7 -0
- package/dist/modules/shipment_tracking/i18n/de.json +327 -0
- package/dist/modules/shipment_tracking/i18n/en.json +327 -0
- package/dist/modules/shipment_tracking/i18n/es.json +327 -0
- package/dist/modules/shipment_tracking/i18n/pl.json +327 -0
- package/dist/modules/shipment_tracking/index.js +15 -0
- package/dist/modules/shipment_tracking/index.js.map +7 -0
- package/dist/modules/shipment_tracking/lib/adapters/cma-cgm.js +66 -0
- package/dist/modules/shipment_tracking/lib/adapters/cma-cgm.js.map +7 -0
- package/dist/modules/shipment_tracking/lib/adapters/cosco-types.js +1 -0
- package/dist/modules/shipment_tracking/lib/adapters/cosco-types.js.map +7 -0
- package/dist/modules/shipment_tracking/lib/adapters/cosco.js +236 -0
- package/dist/modules/shipment_tracking/lib/adapters/cosco.js.map +7 -0
- package/dist/modules/shipment_tracking/lib/adapters/evergreen.js +87 -0
- package/dist/modules/shipment_tracking/lib/adapters/evergreen.js.map +7 -0
- package/dist/modules/shipment_tracking/lib/adapters/hapag-lloyd.js +68 -0
- package/dist/modules/shipment_tracking/lib/adapters/hapag-lloyd.js.map +7 -0
- package/dist/modules/shipment_tracking/lib/adapters/index.js +20 -0
- package/dist/modules/shipment_tracking/lib/adapters/index.js.map +7 -0
- package/dist/modules/shipment_tracking/lib/adapters/maersk.js +84 -0
- package/dist/modules/shipment_tracking/lib/adapters/maersk.js.map +7 -0
- package/dist/modules/shipment_tracking/lib/adapters/msc.js +170 -0
- package/dist/modules/shipment_tracking/lib/adapters/msc.js.map +7 -0
- package/dist/modules/shipment_tracking/lib/adapters/zim.js +85 -0
- package/dist/modules/shipment_tracking/lib/adapters/zim.js.map +7 -0
- package/dist/modules/shipment_tracking/lib/audit-helpers.js +20 -0
- package/dist/modules/shipment_tracking/lib/audit-helpers.js.map +7 -0
- package/dist/modules/shipment_tracking/lib/auth/base64url.js +8 -0
- package/dist/modules/shipment_tracking/lib/auth/base64url.js.map +7 -0
- package/dist/modules/shipment_tracking/lib/auth/oauth-client.js +38 -0
- package/dist/modules/shipment_tracking/lib/auth/oauth-client.js.map +7 -0
- package/dist/modules/shipment_tracking/lib/background.js +15 -0
- package/dist/modules/shipment_tracking/lib/background.js.map +7 -0
- package/dist/modules/shipment_tracking/lib/bic-api-client.js +106 -0
- package/dist/modules/shipment_tracking/lib/bic-api-client.js.map +7 -0
- package/dist/modules/shipment_tracking/lib/carrier-adapter.js +17 -0
- package/dist/modules/shipment_tracking/lib/carrier-adapter.js.map +7 -0
- package/dist/modules/shipment_tracking/lib/current-vessel.js +149 -0
- package/dist/modules/shipment_tracking/lib/current-vessel.js.map +7 -0
- package/dist/modules/shipment_tracking/lib/dcsa-event-mapping.js +124 -0
- package/dist/modules/shipment_tracking/lib/dcsa-event-mapping.js.map +7 -0
- package/dist/modules/shipment_tracking/lib/dcsa-event-parser.js +112 -0
- package/dist/modules/shipment_tracking/lib/dcsa-event-parser.js.map +7 -0
- package/dist/modules/shipment_tracking/lib/dcsa-params.js +16 -0
- package/dist/modules/shipment_tracking/lib/dcsa-params.js.map +7 -0
- package/dist/modules/shipment_tracking/lib/empty-return.js +48 -0
- package/dist/modules/shipment_tracking/lib/empty-return.js.map +7 -0
- package/dist/modules/shipment_tracking/lib/location-overrides.js +85 -0
- package/dist/modules/shipment_tracking/lib/location-overrides.js.map +7 -0
- package/dist/modules/shipment_tracking/lib/location-types.js +59 -0
- package/dist/modules/shipment_tracking/lib/location-types.js.map +7 -0
- package/dist/modules/shipment_tracking/lib/logger.js +113 -0
- package/dist/modules/shipment_tracking/lib/logger.js.map +7 -0
- package/dist/modules/shipment_tracking/lib/poi-event-processor.js +183 -0
- package/dist/modules/shipment_tracking/lib/poi-event-processor.js.map +7 -0
- package/dist/modules/shipment_tracking/lib/poi-nats-config.js +52 -0
- package/dist/modules/shipment_tracking/lib/poi-nats-config.js.map +7 -0
- package/dist/modules/shipment_tracking/lib/poi-types.js +43 -0
- package/dist/modules/shipment_tracking/lib/poi-types.js.map +7 -0
- package/dist/modules/shipment_tracking/lib/rate-limiter.js +31 -0
- package/dist/modules/shipment_tracking/lib/rate-limiter.js.map +7 -0
- package/dist/modules/shipment_tracking/lib/route-extraction.js +187 -0
- package/dist/modules/shipment_tracking/lib/route-extraction.js.map +7 -0
- package/dist/modules/shipment_tracking/lib/route-inference.js +106 -0
- package/dist/modules/shipment_tracking/lib/route-inference.js.map +7 -0
- package/dist/modules/shipment_tracking/lib/schedule-generator.js +63 -0
- package/dist/modules/shipment_tracking/lib/schedule-generator.js.map +7 -0
- package/dist/modules/shipment_tracking/lib/shipsgo-client.js +143 -0
- package/dist/modules/shipment_tracking/lib/shipsgo-client.js.map +7 -0
- package/dist/modules/shipment_tracking/lib/shipsgo-mapper.js +204 -0
- package/dist/modules/shipment_tracking/lib/shipsgo-mapper.js.map +7 -0
- package/dist/modules/shipment_tracking/lib/status-machine.js +131 -0
- package/dist/modules/shipment_tracking/lib/status-machine.js.map +7 -0
- package/dist/modules/shipment_tracking/lib/time-extraction.js +41 -0
- package/dist/modules/shipment_tracking/lib/time-extraction.js.map +7 -0
- package/dist/modules/shipment_tracking/lib/timestamp-utils.js +100 -0
- package/dist/modules/shipment_tracking/lib/timestamp-utils.js.map +7 -0
- package/dist/modules/shipment_tracking/lib/vessel-api.js +111 -0
- package/dist/modules/shipment_tracking/lib/vessel-api.js.map +7 -0
- package/dist/modules/shipment_tracking/lib/vessel-cache.js +50 -0
- package/dist/modules/shipment_tracking/lib/vessel-cache.js.map +7 -0
- package/dist/modules/shipment_tracking/lib/vessel-extraction.js +13 -0
- package/dist/modules/shipment_tracking/lib/vessel-extraction.js.map +7 -0
- package/dist/modules/shipment_tracking/lib/webhook-dispatcher.js +62 -0
- package/dist/modules/shipment_tracking/lib/webhook-dispatcher.js.map +7 -0
- package/dist/modules/shipment_tracking/migrations/Migration20260422161208_shipment_tracking.js +48 -0
- package/dist/modules/shipment_tracking/migrations/Migration20260422161208_shipment_tracking.js.map +7 -0
- package/dist/modules/shipment_tracking/migrations/Migration20260529120000_shipment_tracking_poi_region_names.js +15 -0
- package/dist/modules/shipment_tracking/migrations/Migration20260529120000_shipment_tracking_poi_region_names.js.map +7 -0
- package/dist/modules/shipment_tracking/migrations/Migration20260821120000_shipment_tracking_shipsgo.js +47 -0
- package/dist/modules/shipment_tracking/migrations/Migration20260821120000_shipment_tracking_shipsgo.js.map +7 -0
- package/dist/modules/shipment_tracking/notifications.js +145 -0
- package/dist/modules/shipment_tracking/notifications.js.map +7 -0
- package/dist/modules/shipment_tracking/services/carrierRegistry.js +24 -0
- package/dist/modules/shipment_tracking/services/carrierRegistry.js.map +7 -0
- package/dist/modules/shipment_tracking/services/poiNatsService.js +397 -0
- package/dist/modules/shipment_tracking/services/poiNatsService.js.map +7 -0
- package/dist/modules/shipment_tracking/services/shipsGoProvider.js +163 -0
- package/dist/modules/shipment_tracking/services/shipsGoProvider.js.map +7 -0
- package/dist/modules/shipment_tracking/services/trackingService.js +1283 -0
- package/dist/modules/shipment_tracking/services/trackingService.js.map +7 -0
- package/dist/modules/shipment_tracking/services/webhookService.js +263 -0
- package/dist/modules/shipment_tracking/services/webhookService.js.map +7 -0
- package/dist/modules/shipment_tracking/setup.js +72 -0
- package/dist/modules/shipment_tracking/setup.js.map +7 -0
- package/dist/modules/shipment_tracking/subscribers/cargo-event-created.js +60 -0
- package/dist/modules/shipment_tracking/subscribers/cargo-event-created.js.map +7 -0
- package/dist/modules/shipment_tracking/subscribers/dcsa-event-webhook-dispatch.js +58 -0
- package/dist/modules/shipment_tracking/subscribers/dcsa-event-webhook-dispatch.js.map +7 -0
- package/dist/modules/shipment_tracking/subscribers/poi-event-notification.js +82 -0
- package/dist/modules/shipment_tracking/subscribers/poi-event-notification.js.map +7 -0
- package/dist/modules/shipment_tracking/subscribers/poi-event-webhook-dispatch.js +53 -0
- package/dist/modules/shipment_tracking/subscribers/poi-event-webhook-dispatch.js.map +7 -0
- package/dist/modules/shipment_tracking/subscribers/shipment-status-changed.js +28 -0
- package/dist/modules/shipment_tracking/subscribers/shipment-status-changed.js.map +7 -0
- package/dist/modules/shipment_tracking/subscribers/terminal-event-ingest.js +67 -0
- package/dist/modules/shipment_tracking/subscribers/terminal-event-ingest.js.map +7 -0
- package/dist/modules/shipment_tracking/subscribers/tracking-job-initial-poll.js +30 -0
- package/dist/modules/shipment_tracking/subscribers/tracking-job-initial-poll.js.map +7 -0
- package/dist/modules/shipment_tracking/subscribers/tracking-job-webhook-dispatch.js +19 -0
- package/dist/modules/shipment_tracking/subscribers/tracking-job-webhook-dispatch.js.map +7 -0
- package/dist/modules/shipment_tracking/tables/index.js +6 -0
- package/dist/modules/shipment_tracking/tables/index.js.map +7 -0
- package/dist/modules/shipment_tracking/tables/tracking_job/table.client.js +284 -0
- package/dist/modules/shipment_tracking/tables/tracking_job/table.client.js.map +7 -0
- package/dist/modules/shipment_tracking/tables/tracking_job/table.js +36 -0
- package/dist/modules/shipment_tracking/tables/tracking_job/table.js.map +7 -0
- package/dist/modules/shipment_tracking/widgets/dashboard/ships-map/config.js +25 -0
- package/dist/modules/shipment_tracking/widgets/dashboard/ships-map/config.js.map +7 -0
- package/dist/modules/shipment_tracking/widgets/dashboard/ships-map/widget.client.js +355 -0
- package/dist/modules/shipment_tracking/widgets/dashboard/ships-map/widget.client.js.map +7 -0
- package/dist/modules/shipment_tracking/widgets/dashboard/ships-map/widget.js +26 -0
- package/dist/modules/shipment_tracking/widgets/dashboard/ships-map/widget.js.map +7 -0
- package/dist/types/assets.d.js +1 -0
- package/dist/types/assets.d.js.map +7 -0
- package/generated/entities/bic_config/index.ts +20 -0
- package/generated/entities/carrier_config/index.ts +23 -0
- package/generated/entities/location_override/index.ts +26 -0
- package/generated/entities/shipment/index.ts +46 -0
- package/generated/entities/ships_go_config/index.ts +25 -0
- package/generated/entities/tracking_event/index.ts +60 -0
- package/generated/entities/tracking_job/index.ts +33 -0
- package/generated/entities/webhook/index.ts +22 -0
- package/generated/entities/webhook_delivery/index.ts +22 -0
- package/generated/entities.ids.generated.ts +47 -0
- package/package.json +81 -0
- package/src/index.ts +39 -0
- package/src/modules/shipment_tracking/__integration__/TC-TRACK-410-shipsgo-config-api.spec.ts +133 -0
- package/src/modules/shipment_tracking/__integration__/meta.ts +4 -0
- package/src/modules/shipment_tracking/__tests__/empty-return-stop.test.ts +231 -0
- package/src/modules/shipment_tracking/__tests__/fixtures/hapag-lloyd-transship-container.json +702 -0
- package/src/modules/shipment_tracking/__tests__/fixtures/index.ts +434 -0
- package/src/modules/shipment_tracking/__tests__/fixtures/maersk-multi-transship-completed.json +1518 -0
- package/src/modules/shipment_tracking/__tests__/fixtures/maersk-transship-intransit.json +558 -0
- package/src/modules/shipment_tracking/__tests__/fixtures/msc-direct.json +222 -0
- package/src/modules/shipment_tracking/__tests__/fixtures/msc-multi-transship-completed.json +974 -0
- package/src/modules/shipment_tracking/__tests__/fixtures/msc-multi.json +4846 -0
- package/src/modules/shipment_tracking/__tests__/fixtures/msc-transship.json +409 -0
- package/src/modules/shipment_tracking/__tests__/fixtures/poi-events.json +240 -0
- package/src/modules/shipment_tracking/__tests__/fixtures/synthetic/index.ts +138 -0
- package/src/modules/shipment_tracking/__tests__/fixtures/synthetic-eta-updates.json +288 -0
- package/src/modules/shipment_tracking/__tests__/fixtures/synthetic-route-inference.json +201 -0
- package/src/modules/shipment_tracking/__tests__/fixtures/synthetic-voyage-progression.json +410 -0
- package/src/modules/shipment_tracking/__tests__/shipsgo-air-mapper.test.ts +157 -0
- package/src/modules/shipment_tracking/__tests__/shipsgo-client.test.ts +155 -0
- package/src/modules/shipment_tracking/__tests__/shipsgo-mapper.test.ts +201 -0
- package/src/modules/shipment_tracking/__tests__/shipsgo-poll.test.ts +357 -0
- package/src/modules/shipment_tracking/__tests__/shipsgo-provider.test.ts +364 -0
- package/src/modules/shipment_tracking/__tests__/trackingService.integration.test.ts +2248 -0
- package/src/modules/shipment_tracking/__tests__/webhookPayload.test.ts +151 -0
- package/src/modules/shipment_tracking/acl.ts +18 -0
- package/src/modules/shipment_tracking/api/bic-configs/route.ts +206 -0
- package/src/modules/shipment_tracking/api/bic-configs/test/route.ts +108 -0
- package/src/modules/shipment_tracking/api/cargo-events/route.ts +101 -0
- package/src/modules/shipment_tracking/api/carrier-configs/route.ts +142 -0
- package/src/modules/shipment_tracking/api/dashboard/ships-map/route.ts +246 -0
- package/src/modules/shipment_tracking/api/location-overrides/route.ts +185 -0
- package/src/modules/shipment_tracking/api/openapi.ts +28 -0
- package/src/modules/shipment_tracking/api/shipments/route.ts +197 -0
- package/src/modules/shipment_tracking/api/shipsgo-configs/route.ts +215 -0
- package/src/modules/shipment_tracking/api/tracking-jobs/route.ts +149 -0
- package/src/modules/shipment_tracking/api/tracking-jobs/sync/route.ts +116 -0
- package/src/modules/shipment_tracking/api/utils.ts +10 -0
- package/src/modules/shipment_tracking/api/webhooks/deliveries/route.ts +82 -0
- package/src/modules/shipment_tracking/api/webhooks/route.ts +138 -0
- package/src/modules/shipment_tracking/api/webhooks/test/route.ts +84 -0
- package/src/modules/shipment_tracking/assets/carriers/cma_cgm.png +0 -0
- package/src/modules/shipment_tracking/assets/carriers/cosco.png +0 -0
- package/src/modules/shipment_tracking/assets/carriers/evergreen.png +0 -0
- package/src/modules/shipment_tracking/assets/carriers/hapag_lloyd.png +0 -0
- package/src/modules/shipment_tracking/assets/carriers/maersk.png +0 -0
- package/src/modules/shipment_tracking/assets/carriers/msc.png +0 -0
- package/src/modules/shipment_tracking/assets/carriers/one.png +0 -0
- package/src/modules/shipment_tracking/assets/carriers/oocl.png +0 -0
- package/src/modules/shipment_tracking/assets/carriers/yang_ming.png +0 -0
- package/src/modules/shipment_tracking/assets/carriers/zim.png +0 -0
- package/src/modules/shipment_tracking/assets/index.ts +66 -0
- package/src/modules/shipment_tracking/backend/carrier-configs/page.meta.ts +16 -0
- package/src/modules/shipment_tracking/backend/carrier-configs/page.tsx +782 -0
- package/src/modules/shipment_tracking/backend/location-overrides/page.meta.ts +16 -0
- package/src/modules/shipment_tracking/backend/location-overrides/page.tsx +241 -0
- package/src/modules/shipment_tracking/backend/shipment-tracking/page.meta.ts +16 -0
- package/src/modules/shipment_tracking/backend/shipment-tracking/page.tsx +340 -0
- package/src/modules/shipment_tracking/backend/tracking-jobs/page.meta.ts +16 -0
- package/src/modules/shipment_tracking/backend/tracking-jobs/page.tsx +16 -0
- package/src/modules/shipment_tracking/backend/webhooks/page.meta.ts +16 -0
- package/src/modules/shipment_tracking/backend/webhooks/page.tsx +228 -0
- package/src/modules/shipment_tracking/ce.ts +48 -0
- package/src/modules/shipment_tracking/cli.ts +321 -0
- package/src/modules/shipment_tracking/commands/bic-configs.ts +94 -0
- package/src/modules/shipment_tracking/commands/carrier-configs.ts +89 -0
- package/src/modules/shipment_tracking/commands/index.ts +8 -0
- package/src/modules/shipment_tracking/commands/location-overrides.ts +119 -0
- package/src/modules/shipment_tracking/commands/shipments.ts +247 -0
- package/src/modules/shipment_tracking/commands/shipsgo-configs.ts +101 -0
- package/src/modules/shipment_tracking/commands/tracking-jobs.ts +158 -0
- package/src/modules/shipment_tracking/commands/tracking.ts +230 -0
- package/src/modules/shipment_tracking/commands/webhooks.ts +159 -0
- package/src/modules/shipment_tracking/components/CombinedTimestampCell.tsx +236 -0
- package/src/modules/shipment_tracking/components/FacilityLocationInput.tsx +235 -0
- package/src/modules/shipment_tracking/components/LocationOverrideDrawer.tsx +586 -0
- package/src/modules/shipment_tracking/components/ShipmentDetailsDrawer.tsx +1364 -0
- package/src/modules/shipment_tracking/components/ShipmentDrawer.tsx +498 -0
- package/src/modules/shipment_tracking/components/ShipsGoConfigSection.tsx +266 -0
- package/src/modules/shipment_tracking/components/TimestampCell.tsx +146 -0
- package/src/modules/shipment_tracking/components/TrackingJobDrawer.tsx +467 -0
- package/src/modules/shipment_tracking/components/VesselTrackingMap.tsx +761 -0
- package/src/modules/shipment_tracking/components/WebhookDrawer.tsx +434 -0
- package/src/modules/shipment_tracking/data/entities.ts +748 -0
- package/src/modules/shipment_tracking/data/validators.ts +523 -0
- package/src/modules/shipment_tracking/di.ts +73 -0
- package/src/modules/shipment_tracking/encryption.ts +17 -0
- package/src/modules/shipment_tracking/events.ts +122 -0
- package/src/modules/shipment_tracking/hooks/useVesselTracking.ts +297 -0
- package/src/modules/shipment_tracking/i18n/de.json +327 -0
- package/src/modules/shipment_tracking/i18n/en.json +327 -0
- package/src/modules/shipment_tracking/i18n/es.json +327 -0
- package/src/modules/shipment_tracking/i18n/pl.json +327 -0
- package/src/modules/shipment_tracking/index.ts +13 -0
- package/src/modules/shipment_tracking/lib/__tests__/dcsa-event-mapping.test.ts +385 -0
- package/src/modules/shipment_tracking/lib/__tests__/dcsa-event-parser.test.ts +398 -0
- package/src/modules/shipment_tracking/lib/__tests__/empty-return.test.ts +174 -0
- package/src/modules/shipment_tracking/lib/__tests__/poi-event-processor.test.ts +699 -0
- package/src/modules/shipment_tracking/lib/__tests__/route-inference.test.ts +402 -0
- package/src/modules/shipment_tracking/lib/__tests__/status-machine.test.ts +447 -0
- package/src/modules/shipment_tracking/lib/__tests__/time-extraction.test.ts +331 -0
- package/src/modules/shipment_tracking/lib/__tests__/timestamp-utils.test.ts +260 -0
- package/src/modules/shipment_tracking/lib/__tests__/vessel-api.test.ts +125 -0
- package/src/modules/shipment_tracking/lib/__tests__/vessel-extraction.test.ts +108 -0
- package/src/modules/shipment_tracking/lib/adapters/cma-cgm.ts +84 -0
- package/src/modules/shipment_tracking/lib/adapters/cosco-types.ts +145 -0
- package/src/modules/shipment_tracking/lib/adapters/cosco.ts +314 -0
- package/src/modules/shipment_tracking/lib/adapters/evergreen.ts +107 -0
- package/src/modules/shipment_tracking/lib/adapters/hapag-lloyd.ts +88 -0
- package/src/modules/shipment_tracking/lib/adapters/index.ts +18 -0
- package/src/modules/shipment_tracking/lib/adapters/maersk.ts +104 -0
- package/src/modules/shipment_tracking/lib/adapters/msc.ts +223 -0
- package/src/modules/shipment_tracking/lib/adapters/zim.ts +106 -0
- package/src/modules/shipment_tracking/lib/audit-helpers.ts +29 -0
- package/src/modules/shipment_tracking/lib/auth/base64url.ts +4 -0
- package/src/modules/shipment_tracking/lib/auth/oauth-client.ts +53 -0
- package/src/modules/shipment_tracking/lib/background.ts +24 -0
- package/src/modules/shipment_tracking/lib/bic-api-client.ts +199 -0
- package/src/modules/shipment_tracking/lib/carrier-adapter.ts +136 -0
- package/src/modules/shipment_tracking/lib/current-vessel.ts +274 -0
- package/src/modules/shipment_tracking/lib/dcsa-event-mapping.ts +202 -0
- package/src/modules/shipment_tracking/lib/dcsa-event-parser.ts +163 -0
- package/src/modules/shipment_tracking/lib/dcsa-params.ts +14 -0
- package/src/modules/shipment_tracking/lib/empty-return.ts +122 -0
- package/src/modules/shipment_tracking/lib/location-overrides.ts +150 -0
- package/src/modules/shipment_tracking/lib/location-types.ts +154 -0
- package/src/modules/shipment_tracking/lib/logger.ts +195 -0
- package/src/modules/shipment_tracking/lib/poi-event-processor.ts +302 -0
- package/src/modules/shipment_tracking/lib/poi-nats-config.ts +134 -0
- package/src/modules/shipment_tracking/lib/poi-types.ts +198 -0
- package/src/modules/shipment_tracking/lib/rate-limiter.ts +58 -0
- package/src/modules/shipment_tracking/lib/route-extraction.ts +439 -0
- package/src/modules/shipment_tracking/lib/route-inference.ts +238 -0
- package/src/modules/shipment_tracking/lib/schedule-generator.ts +92 -0
- package/src/modules/shipment_tracking/lib/shipsgo-client.ts +350 -0
- package/src/modules/shipment_tracking/lib/shipsgo-mapper.ts +296 -0
- package/src/modules/shipment_tracking/lib/status-machine.ts +230 -0
- package/src/modules/shipment_tracking/lib/time-extraction.ts +85 -0
- package/src/modules/shipment_tracking/lib/timestamp-utils.ts +244 -0
- package/src/modules/shipment_tracking/lib/vessel-api.ts +299 -0
- package/src/modules/shipment_tracking/lib/vessel-cache.ts +102 -0
- package/src/modules/shipment_tracking/lib/vessel-extraction.ts +39 -0
- package/src/modules/shipment_tracking/lib/webhook-dispatcher.ts +93 -0
- package/src/modules/shipment_tracking/migrations/.snapshot-open-mercato.json +2914 -0
- package/src/modules/shipment_tracking/migrations/Migration20260422161208_shipment_tracking.ts +60 -0
- package/src/modules/shipment_tracking/migrations/Migration20260529120000_shipment_tracking_poi_region_names.ts +22 -0
- package/src/modules/shipment_tracking/migrations/Migration20260821120000_shipment_tracking_shipsgo.ts +65 -0
- package/src/modules/shipment_tracking/notifications.ts +151 -0
- package/src/modules/shipment_tracking/services/carrierRegistry.ts +29 -0
- package/src/modules/shipment_tracking/services/poiNatsService.ts +526 -0
- package/src/modules/shipment_tracking/services/shipsGoProvider.ts +231 -0
- package/src/modules/shipment_tracking/services/trackingService.ts +1777 -0
- package/src/modules/shipment_tracking/services/webhookService.ts +347 -0
- package/src/modules/shipment_tracking/setup.ts +99 -0
- package/src/modules/shipment_tracking/subscribers/__tests__/terminal-event-ingest.test.ts +141 -0
- package/src/modules/shipment_tracking/subscribers/cargo-event-created.ts +115 -0
- package/src/modules/shipment_tracking/subscribers/dcsa-event-webhook-dispatch.ts +115 -0
- package/src/modules/shipment_tracking/subscribers/poi-event-notification.ts +144 -0
- package/src/modules/shipment_tracking/subscribers/poi-event-webhook-dispatch.ts +94 -0
- package/src/modules/shipment_tracking/subscribers/shipment-status-changed.ts +41 -0
- package/src/modules/shipment_tracking/subscribers/terminal-event-ingest.ts +109 -0
- package/src/modules/shipment_tracking/subscribers/tracking-job-initial-poll.ts +53 -0
- package/src/modules/shipment_tracking/subscribers/tracking-job-webhook-dispatch.ts +50 -0
- package/src/modules/shipment_tracking/tables/index.ts +4 -0
- package/src/modules/shipment_tracking/tables/tracking_job/table.client.tsx +337 -0
- package/src/modules/shipment_tracking/tables/tracking_job/table.ts +34 -0
- package/src/modules/shipment_tracking/widgets/dashboard/ships-map/config.ts +32 -0
- package/src/modules/shipment_tracking/widgets/dashboard/ships-map/widget.client.tsx +448 -0
- package/src/modules/shipment_tracking/widgets/dashboard/ships-map/widget.ts +25 -0
- package/src/types/assets.d.ts +5 -0
|
@@ -0,0 +1,198 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* POI Proximity Event Types
|
|
3
|
+
*
|
|
4
|
+
* Type definitions for AIS POI proximity events received from the
|
|
5
|
+
* poi-proximity-detector service via NATS.
|
|
6
|
+
*/
|
|
7
|
+
|
|
8
|
+
// ─── Enums ───────────────────────────────────────────────────
|
|
9
|
+
|
|
10
|
+
/**
|
|
11
|
+
* Types of proximity events emitted by the POI detector.
|
|
12
|
+
*/
|
|
13
|
+
export type ProximityEventType =
|
|
14
|
+
| 'PORT_ARRIVAL'
|
|
15
|
+
| 'PORT_PROXIMITY_ARRIVAL'
|
|
16
|
+
| 'PORT_PROXIMITY_DEPARTURE'
|
|
17
|
+
| 'TERMINAL_ARRIVAL'
|
|
18
|
+
| 'TERMINAL_PROXIMITY_ARRIVAL'
|
|
19
|
+
| 'TERMINAL_PROXIMITY_DEPARTURE'
|
|
20
|
+
| 'WAYPOINT_REACHED'
|
|
21
|
+
|
|
22
|
+
/**
|
|
23
|
+
* Map POI event types to distinct event codes for TrackingEvent records.
|
|
24
|
+
* These codes are AIS-specific and don't conflict with standard DCSA codes.
|
|
25
|
+
* Labels are resolved via i18n: shipment_tracking.event_codes.<CODE>
|
|
26
|
+
*/
|
|
27
|
+
export const PROXIMITY_EVENT_CODES: Record<ProximityEventType, string> = {
|
|
28
|
+
PORT_ARRIVAL: 'PARR',
|
|
29
|
+
PORT_PROXIMITY_ARRIVAL: 'PPRA',
|
|
30
|
+
PORT_PROXIMITY_DEPARTURE: 'PPRD',
|
|
31
|
+
TERMINAL_ARRIVAL: 'TARR',
|
|
32
|
+
TERMINAL_PROXIMITY_ARRIVAL: 'TPRA',
|
|
33
|
+
TERMINAL_PROXIMITY_DEPARTURE: 'TPRD',
|
|
34
|
+
WAYPOINT_REACHED: 'WAYR',
|
|
35
|
+
}
|
|
36
|
+
|
|
37
|
+
/**
|
|
38
|
+
* Categorize events as arrival, departure, or proximity.
|
|
39
|
+
*/
|
|
40
|
+
export type ProximityEventCategory = 'arrival' | 'departure' | 'proximity' | 'waypoint'
|
|
41
|
+
|
|
42
|
+
export const PROXIMITY_EVENT_CATEGORY: Record<ProximityEventType, ProximityEventCategory> = {
|
|
43
|
+
PORT_ARRIVAL: 'arrival',
|
|
44
|
+
PORT_PROXIMITY_ARRIVAL: 'proximity',
|
|
45
|
+
PORT_PROXIMITY_DEPARTURE: 'departure',
|
|
46
|
+
TERMINAL_ARRIVAL: 'arrival',
|
|
47
|
+
TERMINAL_PROXIMITY_ARRIVAL: 'proximity',
|
|
48
|
+
TERMINAL_PROXIMITY_DEPARTURE: 'departure',
|
|
49
|
+
WAYPOINT_REACHED: 'waypoint',
|
|
50
|
+
}
|
|
51
|
+
|
|
52
|
+
// ─── Event Payload ───────────────────────────────────────────
|
|
53
|
+
|
|
54
|
+
/**
|
|
55
|
+
* POI proximity event payload received from the AIS POI detector.
|
|
56
|
+
*
|
|
57
|
+
* This matches the ShipPoiProximityData interface from the poi-proximity-detector service.
|
|
58
|
+
*/
|
|
59
|
+
export interface PoiProximityEvent {
|
|
60
|
+
/**
|
|
61
|
+
* Vessel IMO number. In the AIS system `ships.id` IS the IMO, and the detector
|
|
62
|
+
* sets this field from `ship.id` — so this is the real IMO, not an internal id.
|
|
63
|
+
* Used to match shipments directly by `vesselImo` (no MMSI→IMO lookup).
|
|
64
|
+
*/
|
|
65
|
+
shipId: number
|
|
66
|
+
|
|
67
|
+
/** Ship name */
|
|
68
|
+
shipName: string
|
|
69
|
+
|
|
70
|
+
/** Maritime Mobile Service Identity (9 digits) */
|
|
71
|
+
mmsi: number
|
|
72
|
+
|
|
73
|
+
/** Latitude of ship position */
|
|
74
|
+
lat: number
|
|
75
|
+
|
|
76
|
+
/** Longitude of ship position */
|
|
77
|
+
lng: number
|
|
78
|
+
|
|
79
|
+
/** Actual Time of Arrival (ISO 8601) - present for arrival events */
|
|
80
|
+
ata?: string
|
|
81
|
+
|
|
82
|
+
/** Actual Time of Departure (ISO 8601) - present for departure events */
|
|
83
|
+
atd?: string
|
|
84
|
+
|
|
85
|
+
/** Type of proximity event */
|
|
86
|
+
type: ProximityEventType
|
|
87
|
+
|
|
88
|
+
/** POI code for arrival events (e.g., port/terminal LOCODE or custom code) */
|
|
89
|
+
arrivedPoiCode?: string
|
|
90
|
+
|
|
91
|
+
/** POI code for departure events */
|
|
92
|
+
departedPoiCode?: string
|
|
93
|
+
|
|
94
|
+
/** Distance to POI in meters (null if not applicable) */
|
|
95
|
+
distanceToPoiMeters: number | null
|
|
96
|
+
|
|
97
|
+
/** Sequence order for multiple events in same message batch */
|
|
98
|
+
sequenceOrder: number
|
|
99
|
+
|
|
100
|
+
/**
|
|
101
|
+
* Human-friendly POI identity for arrival/proximity events at a matched port or terminal.
|
|
102
|
+
* All nullable — which fields are populated depends on the event type and how rich the
|
|
103
|
+
* matched POI is (e.g. a PORT_ARRIVAL from a port's geometric center may only have `city`).
|
|
104
|
+
* Resolved for display via {@link resolvePoiLocationName}.
|
|
105
|
+
*/
|
|
106
|
+
portName?: string | null
|
|
107
|
+
terminalName?: string | null
|
|
108
|
+
city?: string | null
|
|
109
|
+
|
|
110
|
+
/**
|
|
111
|
+
* Human-friendly region name in Polish (e.g. "Morze Północne"), set for open-water
|
|
112
|
+
* WAYPOINT_REACHED events that have no port/terminal/city. Null for port/terminal events.
|
|
113
|
+
*/
|
|
114
|
+
regionNamePl?: string | null
|
|
115
|
+
|
|
116
|
+
/** Human-friendly region name in English (e.g. "North Sea"). Null for port/terminal events — see {@link regionNamePl}. */
|
|
117
|
+
regionNameEn?: string | null
|
|
118
|
+
}
|
|
119
|
+
|
|
120
|
+
// ─── Processed Event ─────────────────────────────────────────
|
|
121
|
+
|
|
122
|
+
/**
|
|
123
|
+
* Processed POI event enriched with shipment context.
|
|
124
|
+
*/
|
|
125
|
+
export interface ProcessedPoiEvent {
|
|
126
|
+
/** Original POI event */
|
|
127
|
+
poiEvent: PoiProximityEvent
|
|
128
|
+
|
|
129
|
+
/** IMO number (resolved from MMSI if needed) */
|
|
130
|
+
vesselImo: string | null
|
|
131
|
+
|
|
132
|
+
/** Matched shipment IDs */
|
|
133
|
+
shipmentIds: string[]
|
|
134
|
+
|
|
135
|
+
/** Event timestamp (from ata/atd or current time) */
|
|
136
|
+
eventDateTime: Date
|
|
137
|
+
|
|
138
|
+
/** POI code (arrived or departed) */
|
|
139
|
+
poiCode: string | null
|
|
140
|
+
|
|
141
|
+
/** Mapped event code for TrackingEvent */
|
|
142
|
+
eventCode: string
|
|
143
|
+
|
|
144
|
+
/** Source event ID for deduplication */
|
|
145
|
+
sourceEventId: string
|
|
146
|
+
}
|
|
147
|
+
|
|
148
|
+
// ─── Event ID Mapping ────────────────────────────────────────
|
|
149
|
+
|
|
150
|
+
/**
|
|
151
|
+
* POI event type string used in event IDs.
|
|
152
|
+
* Maps ProximityEventType to the underscore-separated format used in event declarations.
|
|
153
|
+
*/
|
|
154
|
+
export type PoiEventTypeString =
|
|
155
|
+
| 'port_arrival'
|
|
156
|
+
| 'port_proximity_arrival'
|
|
157
|
+
| 'port_departure'
|
|
158
|
+
| 'terminal_arrival'
|
|
159
|
+
| 'terminal_proximity_arrival'
|
|
160
|
+
| 'terminal_departure'
|
|
161
|
+
| 'waypoint_reached'
|
|
162
|
+
|
|
163
|
+
/**
|
|
164
|
+
* Map ProximityEventType to the event type string used in event IDs.
|
|
165
|
+
*/
|
|
166
|
+
export const PROXIMITY_EVENT_TO_STRING: Record<ProximityEventType, PoiEventTypeString> = {
|
|
167
|
+
PORT_ARRIVAL: 'port_arrival',
|
|
168
|
+
PORT_PROXIMITY_ARRIVAL: 'port_proximity_arrival',
|
|
169
|
+
PORT_PROXIMITY_DEPARTURE: 'port_departure',
|
|
170
|
+
TERMINAL_ARRIVAL: 'terminal_arrival',
|
|
171
|
+
TERMINAL_PROXIMITY_ARRIVAL: 'terminal_proximity_arrival',
|
|
172
|
+
TERMINAL_PROXIMITY_DEPARTURE: 'terminal_departure',
|
|
173
|
+
WAYPOINT_REACHED: 'waypoint_reached',
|
|
174
|
+
}
|
|
175
|
+
|
|
176
|
+
/**
|
|
177
|
+
* Get the per-tracking-job event ID for a POI event type.
|
|
178
|
+
* Used for notifications to avoid duplicates for multi-container bookings.
|
|
179
|
+
*
|
|
180
|
+
* @example
|
|
181
|
+
* getJobPoiEventId('PORT_ARRIVAL') // 'shipment_tracking.tracking_job.poi.port_arrival'
|
|
182
|
+
*/
|
|
183
|
+
export function getJobPoiEventId(eventType: ProximityEventType): string {
|
|
184
|
+
const typeString = PROXIMITY_EVENT_TO_STRING[eventType]
|
|
185
|
+
return `shipment_tracking.tracking_job.poi.${typeString}`
|
|
186
|
+
}
|
|
187
|
+
|
|
188
|
+
/**
|
|
189
|
+
* Get the per-shipment event ID for a POI event type.
|
|
190
|
+
* Used for webhooks and external integrations.
|
|
191
|
+
*
|
|
192
|
+
* @example
|
|
193
|
+
* getShipmentPoiEventId('PORT_ARRIVAL') // 'shipment_tracking.poi.port_arrival'
|
|
194
|
+
*/
|
|
195
|
+
export function getShipmentPoiEventId(eventType: ProximityEventType): string {
|
|
196
|
+
const typeString = PROXIMITY_EVENT_TO_STRING[eventType]
|
|
197
|
+
return `shipment_tracking.poi.${typeString}`
|
|
198
|
+
}
|
|
@@ -0,0 +1,58 @@
|
|
|
1
|
+
import { logRateLimit } from './logger'
|
|
2
|
+
|
|
3
|
+
export type RateLimitResult = {
|
|
4
|
+
allowed: boolean
|
|
5
|
+
retryAfterSeconds?: number
|
|
6
|
+
}
|
|
7
|
+
|
|
8
|
+
export type CacheService = {
|
|
9
|
+
get(key: string): Promise<string | null>
|
|
10
|
+
set(key: string, value: string, ttlSeconds?: number): Promise<void>
|
|
11
|
+
}
|
|
12
|
+
|
|
13
|
+
/**
|
|
14
|
+
* Token bucket rate limiter using a cache backend.
|
|
15
|
+
*
|
|
16
|
+
* Key: `st:ratelimit:{tenantId}:{carrier}`
|
|
17
|
+
*
|
|
18
|
+
* Stores the current token count and last refill timestamp in the cache.
|
|
19
|
+
* Refills tokens proportionally based on elapsed time.
|
|
20
|
+
*/
|
|
21
|
+
export async function checkRateLimit(
|
|
22
|
+
cache: CacheService,
|
|
23
|
+
tenantId: string,
|
|
24
|
+
carrierCode: string,
|
|
25
|
+
maxRequests: number,
|
|
26
|
+
windowSeconds: number,
|
|
27
|
+
): Promise<RateLimitResult> {
|
|
28
|
+
const key = `st:ratelimit:${tenantId}:${carrierCode}`
|
|
29
|
+
const now = Date.now()
|
|
30
|
+
|
|
31
|
+
const raw = await cache.get(key)
|
|
32
|
+
let tokens = maxRequests
|
|
33
|
+
let lastRefill = now
|
|
34
|
+
|
|
35
|
+
if (raw) {
|
|
36
|
+
try {
|
|
37
|
+
const state = JSON.parse(raw) as { tokens: number; lastRefill: number }
|
|
38
|
+
const elapsed = (now - state.lastRefill) / 1000
|
|
39
|
+
const refilled = Math.floor((elapsed / windowSeconds) * maxRequests)
|
|
40
|
+
tokens = Math.min(maxRequests, state.tokens + refilled)
|
|
41
|
+
lastRefill = refilled > 0 ? now : state.lastRefill
|
|
42
|
+
} catch {
|
|
43
|
+
// Corrupted cache entry, reset
|
|
44
|
+
}
|
|
45
|
+
}
|
|
46
|
+
|
|
47
|
+
if (tokens <= 0) {
|
|
48
|
+
const retryAfterSeconds = Math.ceil(windowSeconds / maxRequests)
|
|
49
|
+
logRateLimit({ carrierCode, tenantId, allowed: false, retryAfterSeconds })
|
|
50
|
+
return { allowed: false, retryAfterSeconds }
|
|
51
|
+
}
|
|
52
|
+
|
|
53
|
+
tokens -= 1
|
|
54
|
+
await cache.set(key, JSON.stringify({ tokens, lastRefill }), windowSeconds * 2)
|
|
55
|
+
|
|
56
|
+
logRateLimit({ carrierCode, tenantId, allowed: true })
|
|
57
|
+
return { allowed: true }
|
|
58
|
+
}
|
|
@@ -0,0 +1,439 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Route extraction utilities for shipment tracking.
|
|
3
|
+
*
|
|
4
|
+
* Extracts route stops from tracking events for display in the shipment details drawer.
|
|
5
|
+
* This logic is shared between:
|
|
6
|
+
* - TrackingService (when populating denormalized data on shipment)
|
|
7
|
+
* - ShipmentDetailsDrawer (as types reference)
|
|
8
|
+
*/
|
|
9
|
+
|
|
10
|
+
// ─── Types ───────────────────────────────────────────────────
|
|
11
|
+
|
|
12
|
+
/**
|
|
13
|
+
* Seal information for a container.
|
|
14
|
+
*/
|
|
15
|
+
export interface SealInfo {
|
|
16
|
+
number: string
|
|
17
|
+
source?: string | null // CAR (carrier), SHI (shipper), TER (terminal), CUS (customs)
|
|
18
|
+
type?: string | null
|
|
19
|
+
}
|
|
20
|
+
|
|
21
|
+
/**
|
|
22
|
+
* A stop along the shipment's route (origin, transshipment, or destination).
|
|
23
|
+
* Stored as JSONB on the Shipment entity.
|
|
24
|
+
*
|
|
25
|
+
* Includes facility/terminal details from DCSA events for rich location display.
|
|
26
|
+
*/
|
|
27
|
+
export interface RouteStopEntry {
|
|
28
|
+
// Core fields
|
|
29
|
+
location: string // Terminal or port name
|
|
30
|
+
unlocode?: string | null // UN/LOCODE (e.g., "PLGDN")
|
|
31
|
+
// 'transshipment' = cargo changes vessels here; 'port_call' = same vessel calls en route
|
|
32
|
+
type: 'origin' | 'transshipment' | 'port_call' | 'destination'
|
|
33
|
+
vesselName?: string | null // Vessel name for this leg
|
|
34
|
+
vesselImo?: string | null // Vessel IMO number for vessel tracking
|
|
35
|
+
|
|
36
|
+
// Timestamps
|
|
37
|
+
ata?: string | null // Actual arrival - ISO datetime string
|
|
38
|
+
atd?: string | null // Actual departure - ISO datetime string
|
|
39
|
+
eta?: string | null // Estimated arrival - ISO datetime string (for delay calculation)
|
|
40
|
+
etd?: string | null // Estimated departure - ISO datetime string (for delay calculation)
|
|
41
|
+
|
|
42
|
+
// Facility/terminal details (from DCSA events)
|
|
43
|
+
countryCode?: string | null // ISO 3166-1 alpha-2 (e.g., "PL")
|
|
44
|
+
facilityCode?: string | null // SMDG/BIC code (e.g., "DCT")
|
|
45
|
+
facilityCodeListProvider?: 'BIC' | 'SMDG' | null
|
|
46
|
+
facilityTypeCode?: string | null // POTE (port terminal), DEPO (depot), etc.
|
|
47
|
+
facilityAddress?: string | null // Full address string from DCSA otherFacility
|
|
48
|
+
coords?: {
|
|
49
|
+
latitude: number
|
|
50
|
+
longitude: number
|
|
51
|
+
} | null
|
|
52
|
+
|
|
53
|
+
// From BIC enrichment (optional)
|
|
54
|
+
operatorName?: string | null
|
|
55
|
+
}
|
|
56
|
+
|
|
57
|
+
/**
|
|
58
|
+
* A cargo/tracking event entry for a specific container.
|
|
59
|
+
* Stored as JSONB on the Shipment entity.
|
|
60
|
+
*
|
|
61
|
+
* Includes facility details from DCSA events for route extraction.
|
|
62
|
+
*/
|
|
63
|
+
export interface CargoEventEntry {
|
|
64
|
+
// Core fields
|
|
65
|
+
id: string
|
|
66
|
+
eventType: string
|
|
67
|
+
eventCode: string
|
|
68
|
+
eventClassifierCode?: 'ACT' | 'PLN' | 'EST' | null
|
|
69
|
+
eventDateTime: string // ISO datetime string
|
|
70
|
+
description?: string | null
|
|
71
|
+
|
|
72
|
+
// Location fields
|
|
73
|
+
locationName?: string | null
|
|
74
|
+
locationUnlocode?: string | null
|
|
75
|
+
|
|
76
|
+
// Human-friendly region names for AIS POI events (localized at render). Null for carrier events.
|
|
77
|
+
regionNamePl?: string | null
|
|
78
|
+
regionNameEn?: string | null
|
|
79
|
+
|
|
80
|
+
// Vessel/voyage fields
|
|
81
|
+
vesselName?: string | null
|
|
82
|
+
vesselImo?: string | null
|
|
83
|
+
voyageNumber?: string | null
|
|
84
|
+
isTransshipmentMove?: boolean | null
|
|
85
|
+
|
|
86
|
+
emptyIndicatorCode?: 'EMPTY' | 'LADEN' | null
|
|
87
|
+
|
|
88
|
+
// Facility/terminal details (from DCSA events)
|
|
89
|
+
facilityCode?: string | null // SMDG/BIC code (e.g., "DCT")
|
|
90
|
+
facilityCodeListProvider?: 'BIC' | 'SMDG' | null
|
|
91
|
+
facilityTypeCode?: string | null // POTE, DEPO, etc.
|
|
92
|
+
facilityAddress?: string | null // Full address string
|
|
93
|
+
latitude?: number | null
|
|
94
|
+
longitude?: number | null
|
|
95
|
+
|
|
96
|
+
// Seal information (from DCSA events)
|
|
97
|
+
seals?: SealInfo[] | null
|
|
98
|
+
}
|
|
99
|
+
|
|
100
|
+
/**
|
|
101
|
+
* Context for route extraction - provides origin/destination hints.
|
|
102
|
+
*/
|
|
103
|
+
export interface RouteExtractionContext {
|
|
104
|
+
originUnlocode?: string | null
|
|
105
|
+
destinationUnlocode?: string | null
|
|
106
|
+
}
|
|
107
|
+
|
|
108
|
+
// ─── Route Extraction ────────────────────────────────────────
|
|
109
|
+
|
|
110
|
+
/**
|
|
111
|
+
* AIS POI event codes that are too fine-grained for Route Details and are
|
|
112
|
+
* skipped during route extraction:
|
|
113
|
+
* - WAYR (waypoint reached) — open-water grid codes (e.g. "N051E001-00773"), no real place
|
|
114
|
+
* - TARR / TPRA / TPRD (terminal arrival / proximity arrival / proximity departure)
|
|
115
|
+
*
|
|
116
|
+
* Port-level AIS events (PARR/PPRA/PPRD) and all carrier/DCSA milestones
|
|
117
|
+
* (ARRI/DEPA/GTIN/GTOT/LOAD/DISC, …) are kept — they define the route.
|
|
118
|
+
* Excluded events remain in the Journey Timeline (cargoEvents).
|
|
119
|
+
*/
|
|
120
|
+
const ROUTE_DETAIL_EXCLUDED_EVENT_CODES = new Set(['WAYR', 'TARR', 'TPRA', 'TPRD'])
|
|
121
|
+
|
|
122
|
+
// ISO datetime strings are UTC (toISOString), so lexicographic compare == chronological.
|
|
123
|
+
function earliestIso(a?: string | null, b?: string | null): string | null {
|
|
124
|
+
if (!a) return b ?? null
|
|
125
|
+
if (!b) return a
|
|
126
|
+
return a < b ? a : b
|
|
127
|
+
}
|
|
128
|
+
function latestIso(a?: string | null, b?: string | null): string | null {
|
|
129
|
+
if (!a) return b ?? null
|
|
130
|
+
if (!b) return a
|
|
131
|
+
return a > b ? a : b
|
|
132
|
+
}
|
|
133
|
+
|
|
134
|
+
/**
|
|
135
|
+
* Extracts route stops from a list of cargo events.
|
|
136
|
+
*
|
|
137
|
+
* The algorithm:
|
|
138
|
+
* 1. Sorts events chronologically
|
|
139
|
+
* 2. Groups events by location (unlocode or name)
|
|
140
|
+
* 3. Tracks vessels, arrival/departure times, and transshipment indicators
|
|
141
|
+
* 4. Assigns type (origin/transshipment/destination) based on position and context
|
|
142
|
+
*
|
|
143
|
+
* @param events - Array of cargo events (already filtered for specific container)
|
|
144
|
+
* @param context - Optional context with origin/destination hints
|
|
145
|
+
* @returns Array of route stops in chronological order
|
|
146
|
+
*/
|
|
147
|
+
export function extractRouteFromEvents(
|
|
148
|
+
events: CargoEventEntry[],
|
|
149
|
+
context?: RouteExtractionContext
|
|
150
|
+
): RouteStopEntry[] {
|
|
151
|
+
if (!events || events.length === 0) {
|
|
152
|
+
return []
|
|
153
|
+
}
|
|
154
|
+
|
|
155
|
+
// Sort events by datetime
|
|
156
|
+
const sortedEvents = [...events].sort(
|
|
157
|
+
(a, b) => new Date(a.eventDateTime).getTime() - new Date(b.eventDateTime).getTime()
|
|
158
|
+
)
|
|
159
|
+
|
|
160
|
+
// Build location map with timestamps and facility data
|
|
161
|
+
// vessels is a Map of vesselName -> vesselImo (to track IMO alongside name)
|
|
162
|
+
const locationMap = new Map<string, {
|
|
163
|
+
name: string
|
|
164
|
+
unlocode?: string | null
|
|
165
|
+
countryCode?: string | null
|
|
166
|
+
vessels: Map<string, string | null> // vesselName -> vesselImo
|
|
167
|
+
ata?: string
|
|
168
|
+
atd?: string
|
|
169
|
+
eta?: string
|
|
170
|
+
etd?: string
|
|
171
|
+
isTransshipment: boolean
|
|
172
|
+
// Facility data from events
|
|
173
|
+
facilityCode?: string | null
|
|
174
|
+
facilityCodeListProvider?: 'BIC' | 'SMDG' | null
|
|
175
|
+
facilityTypeCode?: string | null
|
|
176
|
+
facilityAddress?: string | null
|
|
177
|
+
coords?: { latitude: number; longitude: number } | null
|
|
178
|
+
}>()
|
|
179
|
+
|
|
180
|
+
for (const event of sortedEvents) {
|
|
181
|
+
// Skip fine-grained AIS POI events (waypoints + terminals) — Route Details
|
|
182
|
+
// shows port-level stops only. See ROUTE_DETAIL_EXCLUDED_EVENT_CODES.
|
|
183
|
+
if (ROUTE_DETAIL_EXCLUDED_EVENT_CODES.has(event.eventCode)) continue
|
|
184
|
+
|
|
185
|
+
const loc = event.locationUnlocode || event.locationName
|
|
186
|
+
if (!loc) continue
|
|
187
|
+
|
|
188
|
+
if (!locationMap.has(loc)) {
|
|
189
|
+
// Extract country code from UN/LOCODE (first 2 chars)
|
|
190
|
+
const countryCode = event.locationUnlocode?.length === 5
|
|
191
|
+
? event.locationUnlocode.slice(0, 2)
|
|
192
|
+
: null
|
|
193
|
+
|
|
194
|
+
locationMap.set(loc, {
|
|
195
|
+
name: event.locationName || loc,
|
|
196
|
+
unlocode: event.locationUnlocode || null,
|
|
197
|
+
countryCode,
|
|
198
|
+
vessels: new Map<string, string | null>(),
|
|
199
|
+
isTransshipment: false,
|
|
200
|
+
// Initialize facility fields
|
|
201
|
+
facilityCode: null,
|
|
202
|
+
facilityCodeListProvider: null,
|
|
203
|
+
facilityTypeCode: null,
|
|
204
|
+
facilityAddress: null,
|
|
205
|
+
coords: null,
|
|
206
|
+
})
|
|
207
|
+
}
|
|
208
|
+
|
|
209
|
+
const entry = locationMap.get(loc)!
|
|
210
|
+
|
|
211
|
+
// Update facility data if this event has more complete info
|
|
212
|
+
// Prefer events with facility codes over those without
|
|
213
|
+
if (event.facilityCode && !entry.facilityCode) {
|
|
214
|
+
entry.facilityCode = event.facilityCode
|
|
215
|
+
entry.facilityCodeListProvider = event.facilityCodeListProvider || null
|
|
216
|
+
}
|
|
217
|
+
if (event.facilityTypeCode && !entry.facilityTypeCode) {
|
|
218
|
+
entry.facilityTypeCode = event.facilityTypeCode
|
|
219
|
+
}
|
|
220
|
+
if (event.facilityAddress && !entry.facilityAddress) {
|
|
221
|
+
entry.facilityAddress = event.facilityAddress
|
|
222
|
+
}
|
|
223
|
+
if (event.latitude != null && event.longitude != null && !entry.coords) {
|
|
224
|
+
entry.coords = { latitude: event.latitude, longitude: event.longitude }
|
|
225
|
+
}
|
|
226
|
+
|
|
227
|
+
if (event.vesselName) {
|
|
228
|
+
// Store vessel name with its IMO (may be null)
|
|
229
|
+
// If we already have this vessel, only update if the new event has an IMO
|
|
230
|
+
const existingImo = entry.vessels.get(event.vesselName)
|
|
231
|
+
if (!existingImo && event.vesselImo) {
|
|
232
|
+
entry.vessels.set(event.vesselName, event.vesselImo)
|
|
233
|
+
} else if (!entry.vessels.has(event.vesselName)) {
|
|
234
|
+
entry.vessels.set(event.vesselName, event.vesselImo || null)
|
|
235
|
+
}
|
|
236
|
+
}
|
|
237
|
+
|
|
238
|
+
// Track actual arrivals/departures
|
|
239
|
+
// ARRI = vessel arrival at port
|
|
240
|
+
// GTIN = gate-in (container enters terminal via truck/rail - indicates arrival for inland segments)
|
|
241
|
+
// For the first segment (depot to port), GTIN at the port indicates the container arrived there
|
|
242
|
+
if (event.eventCode === 'ARRI' && event.eventClassifierCode === 'ACT') {
|
|
243
|
+
entry.ata = event.eventDateTime
|
|
244
|
+
}
|
|
245
|
+
// Use GTIN as arrival indicator for port terminals when no ARRI event exists
|
|
246
|
+
// This handles the depot-to-port segment where container arrives by truck
|
|
247
|
+
if (event.eventCode === 'GTIN' && event.eventClassifierCode === 'ACT' && !entry.ata) {
|
|
248
|
+
// Only use GTIN as arrival if it's at a port terminal (POTE) or intermodal (INTE)
|
|
249
|
+
if (event.facilityTypeCode === 'POTE' || event.facilityTypeCode === 'INTE') {
|
|
250
|
+
entry.ata = event.eventDateTime
|
|
251
|
+
}
|
|
252
|
+
}
|
|
253
|
+
if (event.eventCode === 'DEPA' && event.eventClassifierCode === 'ACT') {
|
|
254
|
+
entry.atd = event.eventDateTime
|
|
255
|
+
}
|
|
256
|
+
// Use GTOT (gate-out) as departure indicator for depots/terminals
|
|
257
|
+
// This handles the first segment where container departs depot by truck
|
|
258
|
+
if (event.eventCode === 'GTOT' && event.eventClassifierCode === 'ACT' && !entry.atd) {
|
|
259
|
+
entry.atd = event.eventDateTime
|
|
260
|
+
}
|
|
261
|
+
|
|
262
|
+
// AIS port proximity events. Only ports survive into Route Details (waypoints +
|
|
263
|
+
// terminals are filtered out above), so these are the arrival/departure signals for
|
|
264
|
+
// AIS-tracked port calls — without them an AIS-only stop (e.g. a transshipment port
|
|
265
|
+
// not in the DCSA feed) has no ATA/ATD and its segment bars never turn green.
|
|
266
|
+
// Applied only when no carrier (DCSA) timestamp exists, so DCSA ATA/ATD always win.
|
|
267
|
+
// PARR = port arrival, PPRA = port proximity arrival, PPRD = port proximity departure.
|
|
268
|
+
if (
|
|
269
|
+
(event.eventCode === 'PARR' || event.eventCode === 'PPRA') &&
|
|
270
|
+
event.eventClassifierCode === 'ACT' &&
|
|
271
|
+
!entry.ata
|
|
272
|
+
) {
|
|
273
|
+
entry.ata = event.eventDateTime
|
|
274
|
+
}
|
|
275
|
+
if (event.eventCode === 'PPRD' && event.eventClassifierCode === 'ACT' && !entry.atd) {
|
|
276
|
+
entry.atd = event.eventDateTime
|
|
277
|
+
}
|
|
278
|
+
|
|
279
|
+
// Track estimated/planned arrivals/departures (for delay calculation)
|
|
280
|
+
// Only store the first estimate (original plan) to compare against actual
|
|
281
|
+
if (event.eventCode === 'ARRI' && (event.eventClassifierCode === 'PLN' || event.eventClassifierCode === 'EST')) {
|
|
282
|
+
if (!entry.eta) entry.eta = event.eventDateTime
|
|
283
|
+
}
|
|
284
|
+
if (event.eventCode === 'DEPA' && (event.eventClassifierCode === 'PLN' || event.eventClassifierCode === 'EST')) {
|
|
285
|
+
if (!entry.etd) entry.etd = event.eventDateTime
|
|
286
|
+
}
|
|
287
|
+
|
|
288
|
+
// Mark transshipment if container was discharged and loaded
|
|
289
|
+
if (event.isTransshipmentMove ||
|
|
290
|
+
(event.eventCode === 'DISC' || event.eventCode === 'LOAD')) {
|
|
291
|
+
entry.isTransshipment = true
|
|
292
|
+
}
|
|
293
|
+
}
|
|
294
|
+
|
|
295
|
+
// Convert to route stops
|
|
296
|
+
const stops: RouteStopEntry[] = []
|
|
297
|
+
const locations = Array.from(locationMap.entries())
|
|
298
|
+
|
|
299
|
+
for (let i = 0; i < locations.length; i++) {
|
|
300
|
+
const [key, data] = locations[i]
|
|
301
|
+
const isFirst = i === 0
|
|
302
|
+
const isLast = i === locations.length - 1
|
|
303
|
+
|
|
304
|
+
// Determine type based on position and context.
|
|
305
|
+
// An intermediate stop is a real 'transshipment' only when the cargo actually
|
|
306
|
+
// changed vessels here (isTransshipmentMove / DISC+LOAD); otherwise it's a plain
|
|
307
|
+
// 'port_call' — the same vessel calling en route (e.g. AIS port proximity events).
|
|
308
|
+
let type: 'origin' | 'transshipment' | 'port_call' | 'destination' =
|
|
309
|
+
data.isTransshipment ? 'transshipment' : 'port_call'
|
|
310
|
+
|
|
311
|
+
if (isFirst || key === context?.originUnlocode) {
|
|
312
|
+
type = 'origin'
|
|
313
|
+
} else if (isLast || key === context?.destinationUnlocode) {
|
|
314
|
+
type = 'destination'
|
|
315
|
+
}
|
|
316
|
+
|
|
317
|
+
// Get the last vessel entry (most recent vessel at this stop)
|
|
318
|
+
const vesselEntries = Array.from(data.vessels.entries())
|
|
319
|
+
const lastVessel = vesselEntries.length > 0 ? vesselEntries[vesselEntries.length - 1] : null
|
|
320
|
+
const vesselName = lastVessel ? lastVessel[0] : null
|
|
321
|
+
const vesselImo = lastVessel ? lastVessel[1] : null
|
|
322
|
+
|
|
323
|
+
stops.push({
|
|
324
|
+
location: data.name,
|
|
325
|
+
unlocode: data.unlocode,
|
|
326
|
+
type,
|
|
327
|
+
vesselName,
|
|
328
|
+
vesselImo,
|
|
329
|
+
ata: data.ata || null,
|
|
330
|
+
atd: data.atd || null,
|
|
331
|
+
eta: data.eta || null,
|
|
332
|
+
etd: data.etd || null,
|
|
333
|
+
// Facility details
|
|
334
|
+
countryCode: data.countryCode || null,
|
|
335
|
+
facilityCode: data.facilityCode || null,
|
|
336
|
+
facilityCodeListProvider: data.facilityCodeListProvider || null,
|
|
337
|
+
facilityTypeCode: data.facilityTypeCode || null,
|
|
338
|
+
facilityAddress: data.facilityAddress || null,
|
|
339
|
+
coords: data.coords || null,
|
|
340
|
+
operatorName: null, // Will be populated by BIC enrichment if needed
|
|
341
|
+
})
|
|
342
|
+
}
|
|
343
|
+
|
|
344
|
+
// Collapse consecutive stops at the same place into one port call. AIS port events for a
|
|
345
|
+
// single city can arrive under several POI codes (proximity vs actual arrival, different
|
|
346
|
+
// berths), which would otherwise render as duplicate rows (e.g. two "Le Havre") with split
|
|
347
|
+
// timestamps — breaking segment colors. Merge by location name, keeping the earliest
|
|
348
|
+
// arrival and latest departure so segment status is computed correctly.
|
|
349
|
+
const merged: RouteStopEntry[] = []
|
|
350
|
+
for (const stop of stops) {
|
|
351
|
+
const prev = merged[merged.length - 1]
|
|
352
|
+
if (prev && prev.location === stop.location) {
|
|
353
|
+
prev.ata = earliestIso(prev.ata, stop.ata)
|
|
354
|
+
prev.atd = latestIso(prev.atd, stop.atd)
|
|
355
|
+
prev.eta = earliestIso(prev.eta, stop.eta)
|
|
356
|
+
prev.etd = latestIso(prev.etd, stop.etd)
|
|
357
|
+
prev.vesselName = prev.vesselName ?? stop.vesselName
|
|
358
|
+
prev.vesselImo = prev.vesselImo ?? stop.vesselImo
|
|
359
|
+
// Prefer a real UN/LOCODE over a raw AIS POI grid code
|
|
360
|
+
if (prev.unlocode?.length !== 5 && stop.unlocode?.length === 5) prev.unlocode = stop.unlocode
|
|
361
|
+
prev.countryCode = prev.countryCode ?? stop.countryCode
|
|
362
|
+
prev.facilityCode = prev.facilityCode ?? stop.facilityCode
|
|
363
|
+
prev.facilityCodeListProvider = prev.facilityCodeListProvider ?? stop.facilityCodeListProvider
|
|
364
|
+
prev.facilityTypeCode = prev.facilityTypeCode ?? stop.facilityTypeCode
|
|
365
|
+
prev.facilityAddress = prev.facilityAddress ?? stop.facilityAddress
|
|
366
|
+
prev.coords = prev.coords ?? stop.coords
|
|
367
|
+
// Promote type if a merged member is an endpoint (origin/destination beats transshipment)
|
|
368
|
+
if (stop.type === 'origin') prev.type = 'origin'
|
|
369
|
+
else if (stop.type === 'destination' && prev.type !== 'origin') prev.type = 'destination'
|
|
370
|
+
else if (stop.type === 'transshipment' && prev.type === 'port_call') prev.type = 'transshipment'
|
|
371
|
+
continue
|
|
372
|
+
}
|
|
373
|
+
merged.push({ ...stop })
|
|
374
|
+
}
|
|
375
|
+
|
|
376
|
+
return merged
|
|
377
|
+
}
|
|
378
|
+
|
|
379
|
+
// ─── Event Mapping ───────────────────────────────────────────
|
|
380
|
+
|
|
381
|
+
/**
|
|
382
|
+
* Maps a TrackingEvent entity to a CargoEventEntry for storage.
|
|
383
|
+
*
|
|
384
|
+
* @param event - The tracking event entity
|
|
385
|
+
* @returns A simplified event entry suitable for JSONB storage
|
|
386
|
+
*/
|
|
387
|
+
export function mapTrackingEventToEntry(event: {
|
|
388
|
+
id: string
|
|
389
|
+
eventType: string
|
|
390
|
+
eventCode: string
|
|
391
|
+
eventClassifierCode?: string | null
|
|
392
|
+
eventDateTime: Date
|
|
393
|
+
description?: string | null
|
|
394
|
+
locationName?: string | null
|
|
395
|
+
locationUnlocode?: string | null
|
|
396
|
+
regionNamePl?: string | null
|
|
397
|
+
regionNameEn?: string | null
|
|
398
|
+
vesselName?: string | null
|
|
399
|
+
vesselImo?: string | null
|
|
400
|
+
voyageNumber?: string | null
|
|
401
|
+
isTransshipmentMove?: boolean | null
|
|
402
|
+
emptyIndicatorCode?: 'EMPTY' | 'LADEN' | null
|
|
403
|
+
// Facility fields
|
|
404
|
+
facilityCode?: string | null
|
|
405
|
+
facilityCodeListProvider?: 'BIC' | 'SMDG' | null
|
|
406
|
+
facilityTypeCode?: string | null
|
|
407
|
+
facilityAddress?: string | null
|
|
408
|
+
latitude?: number | null
|
|
409
|
+
longitude?: number | null
|
|
410
|
+
// Seal fields
|
|
411
|
+
seals?: SealInfo[] | null
|
|
412
|
+
}): CargoEventEntry {
|
|
413
|
+
return {
|
|
414
|
+
id: event.id,
|
|
415
|
+
eventType: event.eventType,
|
|
416
|
+
eventCode: event.eventCode,
|
|
417
|
+
eventClassifierCode: event.eventClassifierCode as 'ACT' | 'PLN' | 'EST' | null,
|
|
418
|
+
eventDateTime: event.eventDateTime.toISOString(),
|
|
419
|
+
description: event.description || null,
|
|
420
|
+
locationName: event.locationName || null,
|
|
421
|
+
locationUnlocode: event.locationUnlocode || null,
|
|
422
|
+
regionNamePl: event.regionNamePl || null,
|
|
423
|
+
regionNameEn: event.regionNameEn || null,
|
|
424
|
+
vesselName: event.vesselName || null,
|
|
425
|
+
vesselImo: event.vesselImo || null,
|
|
426
|
+
voyageNumber: event.voyageNumber || null,
|
|
427
|
+
isTransshipmentMove: event.isTransshipmentMove ?? null,
|
|
428
|
+
emptyIndicatorCode: event.emptyIndicatorCode ?? null,
|
|
429
|
+
// Facility fields
|
|
430
|
+
facilityCode: event.facilityCode || null,
|
|
431
|
+
facilityCodeListProvider: event.facilityCodeListProvider || null,
|
|
432
|
+
facilityTypeCode: event.facilityTypeCode || null,
|
|
433
|
+
facilityAddress: event.facilityAddress || null,
|
|
434
|
+
latitude: event.latitude ?? null,
|
|
435
|
+
longitude: event.longitude ?? null,
|
|
436
|
+
// Seal fields
|
|
437
|
+
seals: event.seals ?? null,
|
|
438
|
+
}
|
|
439
|
+
}
|