@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,195 @@
|
|
|
1
|
+
import { AsyncLocalStorage } from 'node:async_hooks'
|
|
2
|
+
import { trace, SpanStatusCode, type Span } from '@opentelemetry/api'
|
|
3
|
+
|
|
4
|
+
const MODULE_NAME = 'shipment-tracking'
|
|
5
|
+
|
|
6
|
+
// ── Inlined structured logger + tracing ──────────────────────────────
|
|
7
|
+
// Previously provided by `@open-mercato/logger`, which is not published to the
|
|
8
|
+
// public npm registry. Reimplemented locally over `@opentelemetry/api` (already
|
|
9
|
+
// a dependency) and `node:async_hooks` so this package installs from public
|
|
10
|
+
// registries with no private dependency. Behaviour parity: JSON structured
|
|
11
|
+
// logs that carry any active async-local context, and an OTel active span
|
|
12
|
+
// wrapper that records exceptions and always ends the span.
|
|
13
|
+
|
|
14
|
+
type LogContext = Record<string, unknown>
|
|
15
|
+
type LogLevel = 'debug' | 'info' | 'warn' | 'error'
|
|
16
|
+
|
|
17
|
+
const logContext = new AsyncLocalStorage<LogContext>()
|
|
18
|
+
|
|
19
|
+
function emit(level: LogLevel, message: string, fields?: LogContext): void {
|
|
20
|
+
const record: LogContext = {
|
|
21
|
+
level,
|
|
22
|
+
time: new Date().toISOString(),
|
|
23
|
+
message,
|
|
24
|
+
...logContext.getStore(),
|
|
25
|
+
...fields,
|
|
26
|
+
}
|
|
27
|
+
const line = JSON.stringify(record)
|
|
28
|
+
if (level === 'error') console.error(line)
|
|
29
|
+
else if (level === 'warn') console.warn(line)
|
|
30
|
+
else console.log(line)
|
|
31
|
+
}
|
|
32
|
+
|
|
33
|
+
const logger = {
|
|
34
|
+
debug: (message: string, fields?: LogContext) => emit('debug', message, fields),
|
|
35
|
+
info: (message: string, fields?: LogContext) => emit('info', message, fields),
|
|
36
|
+
warn: (message: string, fields?: LogContext) => emit('warn', message, fields),
|
|
37
|
+
error: (message: string, fields?: LogContext) => emit('error', message, fields),
|
|
38
|
+
}
|
|
39
|
+
|
|
40
|
+
function runWithLogContext<T>(ctx: LogContext, fn: () => T | Promise<T>): T | Promise<T> {
|
|
41
|
+
return logContext.run({ ...logContext.getStore(), ...ctx }, fn)
|
|
42
|
+
}
|
|
43
|
+
|
|
44
|
+
const tracer = trace.getTracer(MODULE_NAME)
|
|
45
|
+
|
|
46
|
+
function withSpan<T>(
|
|
47
|
+
opts: { name: string; attributes?: Record<string, string | number | boolean | undefined> },
|
|
48
|
+
fn: (span: Span) => Promise<T>,
|
|
49
|
+
): Promise<T> {
|
|
50
|
+
const attributes = Object.fromEntries(
|
|
51
|
+
Object.entries(opts.attributes ?? {}).filter(([, v]) => v !== undefined),
|
|
52
|
+
) as Record<string, string | number | boolean>
|
|
53
|
+
return tracer.startActiveSpan(opts.name, { attributes }, async (span) => {
|
|
54
|
+
try {
|
|
55
|
+
return await fn(span)
|
|
56
|
+
} catch (error) {
|
|
57
|
+
span.recordException(error as Error)
|
|
58
|
+
span.setStatus({ code: SpanStatusCode.ERROR })
|
|
59
|
+
throw error
|
|
60
|
+
} finally {
|
|
61
|
+
span.end()
|
|
62
|
+
}
|
|
63
|
+
})
|
|
64
|
+
}
|
|
65
|
+
|
|
66
|
+
// ──────────────────────────────────────────────────────────────────────
|
|
67
|
+
|
|
68
|
+
export type CarrierOperation = 'authenticate' | 'fetchEvents'
|
|
69
|
+
|
|
70
|
+
export interface CarrierApiContext {
|
|
71
|
+
carrierCode: string
|
|
72
|
+
operation: CarrierOperation
|
|
73
|
+
referenceType?: string
|
|
74
|
+
referenceValue?: string
|
|
75
|
+
}
|
|
76
|
+
|
|
77
|
+
export interface CarrierLogContext {
|
|
78
|
+
carrierCode: string
|
|
79
|
+
shipmentId?: string
|
|
80
|
+
trackingJobId?: string
|
|
81
|
+
}
|
|
82
|
+
|
|
83
|
+
/**
|
|
84
|
+
* Run a function with carrier context in logs.
|
|
85
|
+
* All logs within the function will include the carrier context fields.
|
|
86
|
+
*/
|
|
87
|
+
export function withCarrierContext<T>(
|
|
88
|
+
ctx: CarrierLogContext,
|
|
89
|
+
fn: () => T | Promise<T>,
|
|
90
|
+
): T | Promise<T> {
|
|
91
|
+
return runWithLogContext({ module: MODULE_NAME, ...ctx }, fn)
|
|
92
|
+
}
|
|
93
|
+
|
|
94
|
+
/**
|
|
95
|
+
* Run a carrier API call with OpenTelemetry span and structured logging.
|
|
96
|
+
*
|
|
97
|
+
* Creates a span named `carrier.{carrierCode}.{operation}` with relevant attributes.
|
|
98
|
+
* Logs debug on start, info on success, error on failure.
|
|
99
|
+
*
|
|
100
|
+
* @example
|
|
101
|
+
* ```ts
|
|
102
|
+
* const events = await withCarrierApiSpan(
|
|
103
|
+
* { carrierCode: 'maersk', operation: 'fetchEvents', referenceType: 'container' },
|
|
104
|
+
* async (span) => {
|
|
105
|
+
* const response = await fetch(url, options)
|
|
106
|
+
* span.setAttribute('http.status_code', response.status)
|
|
107
|
+
* return response.json()
|
|
108
|
+
* }
|
|
109
|
+
* )
|
|
110
|
+
* ```
|
|
111
|
+
*/
|
|
112
|
+
export async function withCarrierApiSpan<T>(
|
|
113
|
+
ctx: CarrierApiContext,
|
|
114
|
+
fn: (span: Span) => Promise<T>,
|
|
115
|
+
): Promise<T> {
|
|
116
|
+
const spanName = `carrier.${ctx.carrierCode}.${ctx.operation}`
|
|
117
|
+
|
|
118
|
+
logger.debug(`Starting ${ctx.operation}`, {
|
|
119
|
+
module: MODULE_NAME,
|
|
120
|
+
carrierCode: ctx.carrierCode,
|
|
121
|
+
operation: ctx.operation,
|
|
122
|
+
referenceType: ctx.referenceType,
|
|
123
|
+
})
|
|
124
|
+
|
|
125
|
+
const startMs = Date.now()
|
|
126
|
+
|
|
127
|
+
return withSpan(
|
|
128
|
+
{
|
|
129
|
+
name: spanName,
|
|
130
|
+
attributes: {
|
|
131
|
+
'carrier.code': ctx.carrierCode,
|
|
132
|
+
'carrier.operation': ctx.operation,
|
|
133
|
+
module: MODULE_NAME,
|
|
134
|
+
...(ctx.referenceType && { 'tracking.reference_type': ctx.referenceType }),
|
|
135
|
+
},
|
|
136
|
+
},
|
|
137
|
+
async (span) => {
|
|
138
|
+
try {
|
|
139
|
+
const result = await fn(span)
|
|
140
|
+
const durationMs = Date.now() - startMs
|
|
141
|
+
|
|
142
|
+
logger.info(`Completed ${ctx.operation}`, {
|
|
143
|
+
module: MODULE_NAME,
|
|
144
|
+
carrierCode: ctx.carrierCode,
|
|
145
|
+
operation: ctx.operation,
|
|
146
|
+
durationMs,
|
|
147
|
+
success: true,
|
|
148
|
+
})
|
|
149
|
+
|
|
150
|
+
span.setAttribute('duration_ms', durationMs)
|
|
151
|
+
return result
|
|
152
|
+
} catch (error) {
|
|
153
|
+
const durationMs = Date.now() - startMs
|
|
154
|
+
const errorMessage = error instanceof Error ? error.message : 'Unknown error'
|
|
155
|
+
|
|
156
|
+
logger.error(`Failed ${ctx.operation}`, {
|
|
157
|
+
module: MODULE_NAME,
|
|
158
|
+
carrierCode: ctx.carrierCode,
|
|
159
|
+
operation: ctx.operation,
|
|
160
|
+
durationMs,
|
|
161
|
+
success: false,
|
|
162
|
+
error: errorMessage,
|
|
163
|
+
})
|
|
164
|
+
|
|
165
|
+
span.setAttribute('duration_ms', durationMs)
|
|
166
|
+
throw error
|
|
167
|
+
}
|
|
168
|
+
},
|
|
169
|
+
)
|
|
170
|
+
}
|
|
171
|
+
|
|
172
|
+
/**
|
|
173
|
+
* Log rate limit events with structured fields.
|
|
174
|
+
*/
|
|
175
|
+
export function logRateLimit(ctx: {
|
|
176
|
+
carrierCode: string
|
|
177
|
+
tenantId: string
|
|
178
|
+
allowed: boolean
|
|
179
|
+
retryAfterSeconds?: number
|
|
180
|
+
}): void {
|
|
181
|
+
const level = ctx.allowed ? 'debug' : 'info'
|
|
182
|
+
logger[level]('Rate limit check', {
|
|
183
|
+
module: MODULE_NAME,
|
|
184
|
+
carrierCode: ctx.carrierCode,
|
|
185
|
+
tenantId: ctx.tenantId,
|
|
186
|
+
allowed: ctx.allowed,
|
|
187
|
+
retryAfterSeconds: ctx.retryAfterSeconds,
|
|
188
|
+
})
|
|
189
|
+
}
|
|
190
|
+
|
|
191
|
+
/**
|
|
192
|
+
* The shipment tracking logger instance.
|
|
193
|
+
* Use this for general logging within the shipment-tracking module.
|
|
194
|
+
*/
|
|
195
|
+
export const trackingLogger = logger
|
|
@@ -0,0 +1,302 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* POI Event Processor
|
|
3
|
+
*
|
|
4
|
+
* Processes POI proximity events from AIS system and matches them
|
|
5
|
+
* to tracked shipments for notification and event creation.
|
|
6
|
+
*/
|
|
7
|
+
|
|
8
|
+
import type { EntityManager } from '@mikro-orm/postgresql'
|
|
9
|
+
import { Shipment, TrackingEvent, TrackingJob } from '../data/entities'
|
|
10
|
+
import type { PoiProximityEvent, ProcessedPoiEvent, ProximityEventType } from './poi-types'
|
|
11
|
+
import { PROXIMITY_EVENT_CODES } from './poi-types'
|
|
12
|
+
import { trackingLogger } from './logger'
|
|
13
|
+
|
|
14
|
+
const LOG_COMPONENT = 'poi-processor'
|
|
15
|
+
|
|
16
|
+
// ─── Shipment Matching ───────────────────────────────────────
|
|
17
|
+
|
|
18
|
+
/**
|
|
19
|
+
* Find active shipments that match the given vessel IMO.
|
|
20
|
+
*
|
|
21
|
+
* A shipment matches if:
|
|
22
|
+
* - It has the same vesselImo
|
|
23
|
+
* - It's active (not deleted, not delivered)
|
|
24
|
+
* - It has an associated tracking job (tracking is enabled)
|
|
25
|
+
*/
|
|
26
|
+
export async function findShipmentsByVesselImo(
|
|
27
|
+
em: EntityManager,
|
|
28
|
+
vesselImo: string
|
|
29
|
+
): Promise<Shipment[]> {
|
|
30
|
+
return em.find(Shipment, {
|
|
31
|
+
vesselImo,
|
|
32
|
+
isActive: true,
|
|
33
|
+
deletedAt: null,
|
|
34
|
+
status: { $nin: ['DELIVERED'] },
|
|
35
|
+
trackingJob: { $ne: null },
|
|
36
|
+
})
|
|
37
|
+
}
|
|
38
|
+
|
|
39
|
+
// ─── Event Processing ────────────────────────────────────────
|
|
40
|
+
|
|
41
|
+
/**
|
|
42
|
+
* Generate a deterministic source event ID for deduplication.
|
|
43
|
+
*
|
|
44
|
+
* Must be stable across redeliveries (nak / max_deliver retries / consumer replay),
|
|
45
|
+
* so it derives only from payload fields — never the wall clock. Arrival/departure
|
|
46
|
+
* events anchor on ata/atd; timeless events (e.g. WAYPOINT_REACHED) anchor on the POI code.
|
|
47
|
+
*
|
|
48
|
+
* Caveat: two distinct position-update batches that emit the same type + sequenceOrder
|
|
49
|
+
* for the same vessel with no ata/atd and no POI code collapse to one event. Acceptable
|
|
50
|
+
* for waypoints; revisit with a producer-supplied event id or JetStream msg.seq if real
|
|
51
|
+
* duplicates surface.
|
|
52
|
+
*/
|
|
53
|
+
export function generateSourceEventId(event: PoiProximityEvent): string {
|
|
54
|
+
const anchor = event.ata || event.atd || getPoiCode(event) || 'unknown'
|
|
55
|
+
return `poi-${event.mmsi}-${event.type}-${anchor}-${event.sequenceOrder}`
|
|
56
|
+
}
|
|
57
|
+
|
|
58
|
+
/**
|
|
59
|
+
* Extract event datetime from POI event.
|
|
60
|
+
*/
|
|
61
|
+
export function extractEventDateTime(event: PoiProximityEvent): Date {
|
|
62
|
+
if (event.ata) {
|
|
63
|
+
return new Date(event.ata)
|
|
64
|
+
}
|
|
65
|
+
if (event.atd) {
|
|
66
|
+
return new Date(event.atd)
|
|
67
|
+
}
|
|
68
|
+
return new Date()
|
|
69
|
+
}
|
|
70
|
+
|
|
71
|
+
/**
|
|
72
|
+
* Get POI code from event (arrival or departure).
|
|
73
|
+
*/
|
|
74
|
+
export function getPoiCode(event: PoiProximityEvent): string | null {
|
|
75
|
+
return event.arrivedPoiCode || event.departedPoiCode || null
|
|
76
|
+
}
|
|
77
|
+
|
|
78
|
+
/**
|
|
79
|
+
* Resolve the human-friendly location name for a POI event, following the producer's
|
|
80
|
+
* per-type identity fallback:
|
|
81
|
+
* - terminal events → terminalName → portName → city
|
|
82
|
+
* - port events → city → portName
|
|
83
|
+
* - waypoint events → null (no port/terminal/city; the localized region name is used instead,
|
|
84
|
+
* stored separately in regionNamePl/regionNameEn and chosen at render time)
|
|
85
|
+
*
|
|
86
|
+
* Returns null when nothing usable is present, so callers fall back to the raw POI code.
|
|
87
|
+
*/
|
|
88
|
+
export function resolvePoiLocationName(event: PoiProximityEvent): string | null {
|
|
89
|
+
if (event.type.startsWith('TERMINAL')) {
|
|
90
|
+
return event.terminalName || event.portName || event.city || null
|
|
91
|
+
}
|
|
92
|
+
if (event.type.startsWith('PORT')) {
|
|
93
|
+
return event.city || event.portName || null
|
|
94
|
+
}
|
|
95
|
+
return null
|
|
96
|
+
}
|
|
97
|
+
|
|
98
|
+
/**
|
|
99
|
+
* Map POI event type to Open Mercato event ID.
|
|
100
|
+
*/
|
|
101
|
+
export function mapToOpenMercatoEventId(type: ProximityEventType): string {
|
|
102
|
+
const eventMap: Record<ProximityEventType, string> = {
|
|
103
|
+
PORT_ARRIVAL: 'shipment_tracking.poi.port_arrival',
|
|
104
|
+
PORT_PROXIMITY_ARRIVAL: 'shipment_tracking.poi.port_proximity_arrival',
|
|
105
|
+
PORT_PROXIMITY_DEPARTURE: 'shipment_tracking.poi.port_departure',
|
|
106
|
+
TERMINAL_ARRIVAL: 'shipment_tracking.poi.terminal_arrival',
|
|
107
|
+
TERMINAL_PROXIMITY_ARRIVAL: 'shipment_tracking.poi.terminal_proximity_arrival',
|
|
108
|
+
TERMINAL_PROXIMITY_DEPARTURE: 'shipment_tracking.poi.terminal_departure',
|
|
109
|
+
WAYPOINT_REACHED: 'shipment_tracking.poi.waypoint_reached',
|
|
110
|
+
}
|
|
111
|
+
return eventMap[type]
|
|
112
|
+
}
|
|
113
|
+
|
|
114
|
+
/**
|
|
115
|
+
* Process a POI event and match it to shipments.
|
|
116
|
+
*
|
|
117
|
+
* @param em - Entity manager
|
|
118
|
+
* @param event - Raw POI proximity event from NATS
|
|
119
|
+
* @returns Processed event with matched shipments, or null if no matches
|
|
120
|
+
*/
|
|
121
|
+
export async function processPoiEvent(
|
|
122
|
+
em: EntityManager,
|
|
123
|
+
event: PoiProximityEvent
|
|
124
|
+
): Promise<ProcessedPoiEvent | null> {
|
|
125
|
+
// The AIS detector sets `shipId` from `ship.id`, which is the vessel IMO.
|
|
126
|
+
// Match directly against the IMO we already store on the shipment — no lookup needed.
|
|
127
|
+
const vesselImo = String(event.shipId)
|
|
128
|
+
const shipments = await findShipmentsByVesselImo(em, vesselImo)
|
|
129
|
+
|
|
130
|
+
if (shipments.length === 0) {
|
|
131
|
+
trackingLogger.debug('No active shipments found for vessel IMO', {
|
|
132
|
+
component: LOG_COMPONENT,
|
|
133
|
+
mmsi: event.mmsi,
|
|
134
|
+
vesselImo,
|
|
135
|
+
eventType: event.type,
|
|
136
|
+
})
|
|
137
|
+
return null
|
|
138
|
+
}
|
|
139
|
+
|
|
140
|
+
return {
|
|
141
|
+
poiEvent: event,
|
|
142
|
+
vesselImo,
|
|
143
|
+
shipmentIds: shipments.map((s) => s.id),
|
|
144
|
+
eventDateTime: extractEventDateTime(event),
|
|
145
|
+
poiCode: getPoiCode(event),
|
|
146
|
+
eventCode: PROXIMITY_EVENT_CODES[event.type],
|
|
147
|
+
sourceEventId: generateSourceEventId(event),
|
|
148
|
+
}
|
|
149
|
+
}
|
|
150
|
+
|
|
151
|
+
// ─── TrackingEvent Creation ──────────────────────────────────
|
|
152
|
+
|
|
153
|
+
/**
|
|
154
|
+
* Check if a POI event has already been processed (deduplication).
|
|
155
|
+
*/
|
|
156
|
+
export async function isEventAlreadyProcessed(
|
|
157
|
+
em: EntityManager,
|
|
158
|
+
trackingJobId: string,
|
|
159
|
+
sourceEventId: string
|
|
160
|
+
): Promise<boolean> {
|
|
161
|
+
const existing = await em.findOne(TrackingEvent, {
|
|
162
|
+
trackingJob: trackingJobId,
|
|
163
|
+
source: 'ais',
|
|
164
|
+
sourceEventId,
|
|
165
|
+
})
|
|
166
|
+
return existing !== null
|
|
167
|
+
}
|
|
168
|
+
|
|
169
|
+
/**
|
|
170
|
+
* Create a TrackingEvent record from a processed POI event.
|
|
171
|
+
*/
|
|
172
|
+
export function createTrackingEventFromPoi(
|
|
173
|
+
processedEvent: ProcessedPoiEvent,
|
|
174
|
+
trackingJob: TrackingJob
|
|
175
|
+
): TrackingEvent {
|
|
176
|
+
const { poiEvent, eventDateTime, eventCode, sourceEventId, poiCode, vesselImo } = processedEvent
|
|
177
|
+
|
|
178
|
+
const event = new TrackingEvent()
|
|
179
|
+
event.organizationId = trackingJob.organizationId
|
|
180
|
+
event.tenantId = trackingJob.tenantId
|
|
181
|
+
event.trackingJob = trackingJob
|
|
182
|
+
event.source = 'ais'
|
|
183
|
+
event.sourceEventId = sourceEventId
|
|
184
|
+
event.eventType = 'TRANSPORT'
|
|
185
|
+
event.eventCode = eventCode
|
|
186
|
+
event.eventClassifierCode = 'ACT' // Actual event
|
|
187
|
+
event.eventDateTime = eventDateTime
|
|
188
|
+
event.description = null // Labels resolved via i18n: event_codes.<CODE>
|
|
189
|
+
event.latitude = poiEvent.lat
|
|
190
|
+
event.longitude = poiEvent.lng
|
|
191
|
+
event.vesselName = poiEvent.shipName
|
|
192
|
+
// The POI producer sets shipId to the vessel IMO (that's how we matched the shipment),
|
|
193
|
+
// so persist it — route legs/maps need the IMO, not just the name.
|
|
194
|
+
event.vesselImo = vesselImo
|
|
195
|
+
event.locationUnlocode = poiCode // raw POI code (grid/LOCODE), kept as the dedup reference
|
|
196
|
+
// Friendly port/terminal name (null for waypoints, which use the localized region name below).
|
|
197
|
+
// Populating locationName also gives route extraction a real name instead of the grid code.
|
|
198
|
+
event.locationName = resolvePoiLocationName(poiEvent)
|
|
199
|
+
event.regionNamePl = poiEvent.regionNamePl ?? null
|
|
200
|
+
event.regionNameEn = poiEvent.regionNameEn ?? null
|
|
201
|
+
|
|
202
|
+
// Store raw event data for debugging
|
|
203
|
+
event.rawData = {
|
|
204
|
+
poiEventType: poiEvent.type,
|
|
205
|
+
mmsi: poiEvent.mmsi,
|
|
206
|
+
shipId: poiEvent.shipId,
|
|
207
|
+
arrivedPoiCode: poiEvent.arrivedPoiCode,
|
|
208
|
+
departedPoiCode: poiEvent.departedPoiCode,
|
|
209
|
+
distanceToPoiMeters: poiEvent.distanceToPoiMeters,
|
|
210
|
+
sequenceOrder: poiEvent.sequenceOrder,
|
|
211
|
+
// POI identity fields without dedicated columns — kept here for debugging. The resolved
|
|
212
|
+
// name lands in locationName; region names have their own columns.
|
|
213
|
+
portName: poiEvent.portName,
|
|
214
|
+
terminalName: poiEvent.terminalName,
|
|
215
|
+
city: poiEvent.city,
|
|
216
|
+
}
|
|
217
|
+
|
|
218
|
+
return event
|
|
219
|
+
}
|
|
220
|
+
|
|
221
|
+
// ─── Batch Processing ────────────────────────────────────────
|
|
222
|
+
|
|
223
|
+
export type PoiProcessingResult = {
|
|
224
|
+
processed: number
|
|
225
|
+
matched: number
|
|
226
|
+
eventsCreated: number
|
|
227
|
+
errors: number
|
|
228
|
+
}
|
|
229
|
+
|
|
230
|
+
/**
|
|
231
|
+
* Process a batch of POI events.
|
|
232
|
+
*/
|
|
233
|
+
export async function processPoiEventBatch(
|
|
234
|
+
em: EntityManager,
|
|
235
|
+
events: PoiProximityEvent[]
|
|
236
|
+
): Promise<PoiProcessingResult> {
|
|
237
|
+
const result: PoiProcessingResult = {
|
|
238
|
+
processed: 0,
|
|
239
|
+
matched: 0,
|
|
240
|
+
eventsCreated: 0,
|
|
241
|
+
errors: 0,
|
|
242
|
+
}
|
|
243
|
+
|
|
244
|
+
for (const event of events) {
|
|
245
|
+
result.processed++
|
|
246
|
+
|
|
247
|
+
try {
|
|
248
|
+
const processed = await processPoiEvent(em, event)
|
|
249
|
+
|
|
250
|
+
if (!processed) {
|
|
251
|
+
continue
|
|
252
|
+
}
|
|
253
|
+
|
|
254
|
+
result.matched++
|
|
255
|
+
|
|
256
|
+
// For each matched shipment, check if we need to create events
|
|
257
|
+
for (const shipmentId of processed.shipmentIds) {
|
|
258
|
+
const shipment = await em.findOne(Shipment, { id: shipmentId }, { populate: ['trackingJob'] })
|
|
259
|
+
|
|
260
|
+
if (!shipment?.trackingJob) {
|
|
261
|
+
continue
|
|
262
|
+
}
|
|
263
|
+
|
|
264
|
+
// Check for duplicates
|
|
265
|
+
const alreadyProcessed = await isEventAlreadyProcessed(
|
|
266
|
+
em,
|
|
267
|
+
shipment.trackingJob.id,
|
|
268
|
+
processed.sourceEventId
|
|
269
|
+
)
|
|
270
|
+
|
|
271
|
+
if (alreadyProcessed) {
|
|
272
|
+
trackingLogger.debug('Event already processed', {
|
|
273
|
+
component: LOG_COMPONENT,
|
|
274
|
+
sourceEventId: processed.sourceEventId,
|
|
275
|
+
trackingJobId: shipment.trackingJob.id,
|
|
276
|
+
})
|
|
277
|
+
continue
|
|
278
|
+
}
|
|
279
|
+
|
|
280
|
+
// Create TrackingEvent
|
|
281
|
+
const trackingEvent = createTrackingEventFromPoi(processed, shipment.trackingJob)
|
|
282
|
+
em.persist(trackingEvent)
|
|
283
|
+
result.eventsCreated++
|
|
284
|
+
}
|
|
285
|
+
} catch (error) {
|
|
286
|
+
trackingLogger.error('Error processing POI event', {
|
|
287
|
+
component: LOG_COMPONENT,
|
|
288
|
+
mmsi: event.mmsi,
|
|
289
|
+
eventType: event.type,
|
|
290
|
+
error: error instanceof Error ? error.message : String(error),
|
|
291
|
+
stack: error instanceof Error ? error.stack : undefined,
|
|
292
|
+
})
|
|
293
|
+
result.errors++
|
|
294
|
+
}
|
|
295
|
+
}
|
|
296
|
+
|
|
297
|
+
if (result.eventsCreated > 0) {
|
|
298
|
+
await em.flush()
|
|
299
|
+
}
|
|
300
|
+
|
|
301
|
+
return result
|
|
302
|
+
}
|
|
@@ -0,0 +1,134 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* NATS Configuration for POI Proximity Events
|
|
3
|
+
*
|
|
4
|
+
* Configuration for connecting to NATS server and subscribing to
|
|
5
|
+
* POI proximity events from the AIS system.
|
|
6
|
+
*/
|
|
7
|
+
|
|
8
|
+
import { createHash, randomUUID } from 'crypto'
|
|
9
|
+
|
|
10
|
+
// ─── Configuration Interface ─────────────────────────────────
|
|
11
|
+
|
|
12
|
+
export interface PoiNatsConfig {
|
|
13
|
+
/** NATS server URL (e.g., nats://localhost:4222) */
|
|
14
|
+
serverUrl: string
|
|
15
|
+
|
|
16
|
+
/** JetStream stream name containing POI events */
|
|
17
|
+
streamName: string
|
|
18
|
+
|
|
19
|
+
/** NATS subject for POI proximity events (filter within stream) */
|
|
20
|
+
subject: string
|
|
21
|
+
|
|
22
|
+
/** Unique instance identifier (derived from JWT_SECRET or explicit) */
|
|
23
|
+
instanceId: string
|
|
24
|
+
|
|
25
|
+
/** Durable consumer name for reliable message delivery (includes instanceId) */
|
|
26
|
+
consumerName: string
|
|
27
|
+
|
|
28
|
+
/** Connection name for debugging */
|
|
29
|
+
connectionName: string
|
|
30
|
+
|
|
31
|
+
/** Reconnect settings */
|
|
32
|
+
reconnect: {
|
|
33
|
+
/** Maximum reconnect attempts (-1 for infinite) */
|
|
34
|
+
maxAttempts: number
|
|
35
|
+
/** Initial delay between reconnects in ms */
|
|
36
|
+
initialDelayMs: number
|
|
37
|
+
/** Maximum delay between reconnects in ms */
|
|
38
|
+
maxDelayMs: number
|
|
39
|
+
}
|
|
40
|
+
}
|
|
41
|
+
|
|
42
|
+
// ─── Instance ID Generation ──────────────────────────────────
|
|
43
|
+
|
|
44
|
+
/**
|
|
45
|
+
* Generate a unique instance ID for this deployment.
|
|
46
|
+
*
|
|
47
|
+
* Priority:
|
|
48
|
+
* 1. POI_NATS_INSTANCE_ID env var (explicit override)
|
|
49
|
+
* 2. Hash of JWT_SECRET (stable, unique per installation)
|
|
50
|
+
* 3. Random UUID prefix (fallback for dev/testing)
|
|
51
|
+
*/
|
|
52
|
+
function generateInstanceId(): string {
|
|
53
|
+
// 1. Explicit override
|
|
54
|
+
if (process.env.POI_NATS_INSTANCE_ID) {
|
|
55
|
+
return process.env.POI_NATS_INSTANCE_ID
|
|
56
|
+
}
|
|
57
|
+
|
|
58
|
+
// 2. Derive from JWT_SECRET (stable across restarts)
|
|
59
|
+
const jwtSecret = process.env.JWT_SECRET
|
|
60
|
+
if (jwtSecret) {
|
|
61
|
+
const hash = createHash('sha256').update(jwtSecret).digest('hex')
|
|
62
|
+
return hash.substring(0, 8)
|
|
63
|
+
}
|
|
64
|
+
|
|
65
|
+
// 3. Fallback to random (dev/testing only)
|
|
66
|
+
console.warn('[poi-nats] JWT_SECRET not set, using random instance ID (not stable across restarts)')
|
|
67
|
+
return randomUUID().split('-')[0]
|
|
68
|
+
}
|
|
69
|
+
|
|
70
|
+
// ─── Default Configuration ───────────────────────────────────
|
|
71
|
+
|
|
72
|
+
const DEFAULT_RECONNECT = {
|
|
73
|
+
maxAttempts: -1, // Infinite reconnect
|
|
74
|
+
initialDelayMs: 250,
|
|
75
|
+
maxDelayMs: 5000,
|
|
76
|
+
}
|
|
77
|
+
|
|
78
|
+
// ─── Configuration Loader ────────────────────────────────────
|
|
79
|
+
|
|
80
|
+
/**
|
|
81
|
+
* Load POI NATS configuration from environment variables.
|
|
82
|
+
*
|
|
83
|
+
* Environment variables:
|
|
84
|
+
* - POI_NATS_URL: NATS server URL (required to enable)
|
|
85
|
+
* - POI_NATS_STREAM: JetStream stream name (default: AIS_STREAM)
|
|
86
|
+
* - POI_NATS_SUBJECT: Subject filter for POI events (default: ais.ship.proximity.events)
|
|
87
|
+
* - POI_NATS_INSTANCE_ID: Unique instance identifier (default: derived from JWT_SECRET)
|
|
88
|
+
*
|
|
89
|
+
* The consumer name is auto-generated as: shipment-tracking-poi-{instanceId}
|
|
90
|
+
* This allows multiple self-hosted instances to each receive all messages independently.
|
|
91
|
+
*/
|
|
92
|
+
export function loadPoiNatsConfig(): PoiNatsConfig {
|
|
93
|
+
const instanceId = generateInstanceId()
|
|
94
|
+
const consumerName = `shipment-tracking-poi-${instanceId}`
|
|
95
|
+
|
|
96
|
+
return {
|
|
97
|
+
serverUrl: process.env.POI_NATS_URL || 'nats://localhost:4222',
|
|
98
|
+
streamName: process.env.POI_NATS_STREAM || 'AIS_STREAM',
|
|
99
|
+
subject: process.env.POI_NATS_SUBJECT || 'ais.ship.proximity.events',
|
|
100
|
+
instanceId,
|
|
101
|
+
consumerName,
|
|
102
|
+
connectionName: `shipment-tracking-poi-${instanceId}`,
|
|
103
|
+
reconnect: DEFAULT_RECONNECT,
|
|
104
|
+
}
|
|
105
|
+
}
|
|
106
|
+
|
|
107
|
+
/**
|
|
108
|
+
* Check if POI NATS integration is configured.
|
|
109
|
+
*
|
|
110
|
+
* Returns true only if POI_NATS_URL is explicitly set.
|
|
111
|
+
* When not configured, the POI subscriber will be skipped.
|
|
112
|
+
*/
|
|
113
|
+
export function isPoiNatsConfigured(): boolean {
|
|
114
|
+
return !!process.env.POI_NATS_URL
|
|
115
|
+
}
|
|
116
|
+
|
|
117
|
+
/**
|
|
118
|
+
* Validate POI NATS configuration.
|
|
119
|
+
*
|
|
120
|
+
* @throws Error if configuration is invalid
|
|
121
|
+
*/
|
|
122
|
+
export function validatePoiNatsConfig(config: PoiNatsConfig): void {
|
|
123
|
+
if (!config.serverUrl) {
|
|
124
|
+
throw new Error('POI_NATS_URL is required')
|
|
125
|
+
}
|
|
126
|
+
|
|
127
|
+
if (!config.serverUrl.startsWith('nats://') && !config.serverUrl.startsWith('nats+tls://')) {
|
|
128
|
+
throw new Error('POI_NATS_URL must start with nats:// or nats+tls://')
|
|
129
|
+
}
|
|
130
|
+
|
|
131
|
+
if (!config.subject) {
|
|
132
|
+
throw new Error('POI_NATS_SUBJECT is required')
|
|
133
|
+
}
|
|
134
|
+
}
|