@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,238 @@
|
|
|
1
|
+
import type { CarrierFetchedEvent } from './carrier-adapter'
|
|
2
|
+
|
|
3
|
+
export interface InferredRoute {
|
|
4
|
+
originUnlocode: string | null
|
|
5
|
+
destinationUnlocode: string | null
|
|
6
|
+
confidence: {
|
|
7
|
+
origin: 'high' | 'medium' | 'low' | null
|
|
8
|
+
destination: 'high' | 'medium' | 'low' | null
|
|
9
|
+
}
|
|
10
|
+
}
|
|
11
|
+
|
|
12
|
+
/**
|
|
13
|
+
* Checks if the facility type represents a port or intermodal terminal.
|
|
14
|
+
* Used to filter out client locations (CLOC) and depots (DEPO) when inferring ports.
|
|
15
|
+
*
|
|
16
|
+
* Valid facility types for port inference:
|
|
17
|
+
* - POTE: Port Terminal
|
|
18
|
+
* - INTE: Intermodal Terminal (rail/truck interchange)
|
|
19
|
+
*/
|
|
20
|
+
function isPortOrTerminal(facilityTypeCode: string | null | undefined): boolean {
|
|
21
|
+
if (!facilityTypeCode) return false
|
|
22
|
+
return facilityTypeCode === 'POTE' || facilityTypeCode === 'INTE'
|
|
23
|
+
}
|
|
24
|
+
|
|
25
|
+
/**
|
|
26
|
+
* Checks if the facility type is a known non-port location (client site or depot).
|
|
27
|
+
* Used to filter out these locations when inferring destination ports.
|
|
28
|
+
*/
|
|
29
|
+
function isNonPortLocation(facilityTypeCode: string | null | undefined): boolean {
|
|
30
|
+
if (!facilityTypeCode) return false
|
|
31
|
+
// CLOC = Client Location, DEPO = Depot - these are inland delivery points
|
|
32
|
+
return facilityTypeCode === 'CLOC' || facilityTypeCode === 'DEPO'
|
|
33
|
+
}
|
|
34
|
+
|
|
35
|
+
/**
|
|
36
|
+
* Infers origin and destination ports from DCSA tracking events.
|
|
37
|
+
*
|
|
38
|
+
* Strategy for DESTINATION:
|
|
39
|
+
* 1. Last EST/PLN ARRI event at a port terminal (POTE/INTE) → high confidence
|
|
40
|
+
* 2. Last ACT ARRI on VESSEL at a port terminal → high confidence
|
|
41
|
+
* 3. Last DISC event at a port terminal → medium confidence
|
|
42
|
+
* 4. Fallback: Last ARRI/DISC at any location (when no port data available)
|
|
43
|
+
*
|
|
44
|
+
* IMPORTANT: We filter by facilityTypeCode to exclude client locations (CLOC)
|
|
45
|
+
* and depots (DEPO) which are inland delivery points, not ports.
|
|
46
|
+
*
|
|
47
|
+
* Strategy for ORIGIN:
|
|
48
|
+
* 1. First ACT DEPA on VESSEL with exportVoyageNumber → high confidence (departure from load port)
|
|
49
|
+
* 2. First LOAD event on VESSEL → high confidence (where cargo was first loaded)
|
|
50
|
+
* 3. First GTIN (LADEN) event → medium confidence (gate-in at origin)
|
|
51
|
+
*
|
|
52
|
+
* Key insight:
|
|
53
|
+
* - exportVoyageNumber indicates outbound leg (origin side)
|
|
54
|
+
* - importVoyageNumber indicates inbound leg (destination side)
|
|
55
|
+
*/
|
|
56
|
+
export function inferRouteFromEvents(events: CarrierFetchedEvent[]): InferredRoute {
|
|
57
|
+
const result: InferredRoute = {
|
|
58
|
+
originUnlocode: null,
|
|
59
|
+
destinationUnlocode: null,
|
|
60
|
+
confidence: {
|
|
61
|
+
origin: null,
|
|
62
|
+
destination: null,
|
|
63
|
+
},
|
|
64
|
+
}
|
|
65
|
+
|
|
66
|
+
if (!events || events.length === 0) {
|
|
67
|
+
return result
|
|
68
|
+
}
|
|
69
|
+
|
|
70
|
+
// Sort events by datetime for chronological processing
|
|
71
|
+
const sortedEvents = [...events].sort(
|
|
72
|
+
(a, b) => a.eventDateTime.getTime() - b.eventDateTime.getTime()
|
|
73
|
+
)
|
|
74
|
+
|
|
75
|
+
// ─── DESTINATION INFERENCE ─────────────────────────────────────────
|
|
76
|
+
// For transshipment voyages, there are multiple ARRI events (one per port).
|
|
77
|
+
// The LAST arrival event at a PORT TERMINAL is the final destination.
|
|
78
|
+
// We explicitly filter out client locations (CLOC) and depots (DEPO).
|
|
79
|
+
|
|
80
|
+
// Strategy 1: Look for LAST ARRI event (EST or PLN), excluding known non-port locations
|
|
81
|
+
// PLN (planned) and EST (estimated) both indicate scheduled arrivals.
|
|
82
|
+
// We take the LAST one as the final destination, filtering out client locations/depots.
|
|
83
|
+
// Note: We use "exclude non-port" rather than "include only port" to handle cases
|
|
84
|
+
// where facilityTypeCode is not provided (common in many carriers' responses).
|
|
85
|
+
const plannedArriEvents = sortedEvents.filter(
|
|
86
|
+
(e) =>
|
|
87
|
+
e.eventType === 'TRANSPORT' &&
|
|
88
|
+
e.eventCode === 'ARRI' &&
|
|
89
|
+
(e.eventClassifierCode === 'EST' || e.eventClassifierCode === 'PLN') &&
|
|
90
|
+
e.locationUnlocode &&
|
|
91
|
+
!isNonPortLocation(e.facilityTypeCode)
|
|
92
|
+
)
|
|
93
|
+
|
|
94
|
+
if (plannedArriEvents.length > 0) {
|
|
95
|
+
// Take the last planned arrival - this is the final port destination
|
|
96
|
+
const lastPlannedArri = plannedArriEvents[plannedArriEvents.length - 1]
|
|
97
|
+
result.destinationUnlocode = lastPlannedArri.locationUnlocode!
|
|
98
|
+
// High confidence if we have facility type info confirming it's a port, otherwise still high for PLN/EST
|
|
99
|
+
result.confidence.destination = 'high'
|
|
100
|
+
}
|
|
101
|
+
|
|
102
|
+
// Strategy 2: Look for last ACT ARRI on VESSEL, excluding known non-port locations
|
|
103
|
+
if (!result.destinationUnlocode) {
|
|
104
|
+
const actArriEvents = sortedEvents.filter(
|
|
105
|
+
(e) =>
|
|
106
|
+
e.eventType === 'TRANSPORT' &&
|
|
107
|
+
e.eventCode === 'ARRI' &&
|
|
108
|
+
e.eventClassifierCode === 'ACT' &&
|
|
109
|
+
e.modeOfTransport === 'VESSEL' &&
|
|
110
|
+
e.locationUnlocode &&
|
|
111
|
+
!isNonPortLocation(e.facilityTypeCode)
|
|
112
|
+
)
|
|
113
|
+
|
|
114
|
+
if (actArriEvents.length > 0) {
|
|
115
|
+
const lastArri = actArriEvents[actArriEvents.length - 1]
|
|
116
|
+
result.destinationUnlocode = lastArri.locationUnlocode!
|
|
117
|
+
result.confidence.destination = 'high'
|
|
118
|
+
}
|
|
119
|
+
}
|
|
120
|
+
|
|
121
|
+
// Strategy 3: Look for last DISC event, excluding known non-port locations
|
|
122
|
+
if (!result.destinationUnlocode) {
|
|
123
|
+
const discEvents = sortedEvents.filter(
|
|
124
|
+
(e) =>
|
|
125
|
+
e.eventType === 'EQUIPMENT' &&
|
|
126
|
+
e.eventCode === 'DISC' &&
|
|
127
|
+
e.locationUnlocode &&
|
|
128
|
+
!isNonPortLocation(e.facilityTypeCode)
|
|
129
|
+
)
|
|
130
|
+
|
|
131
|
+
if (discEvents.length > 0) {
|
|
132
|
+
const lastDisc = discEvents[discEvents.length - 1]
|
|
133
|
+
result.destinationUnlocode = lastDisc.locationUnlocode!
|
|
134
|
+
result.confidence.destination = 'medium'
|
|
135
|
+
}
|
|
136
|
+
}
|
|
137
|
+
|
|
138
|
+
// ─── ORIGIN INFERENCE ──────────────────────────────────────────────
|
|
139
|
+
|
|
140
|
+
// Strategy 1: First ACT DEPA on VESSEL with exportVoyageNumber
|
|
141
|
+
const firstVesselDepa = sortedEvents.find(
|
|
142
|
+
(e) =>
|
|
143
|
+
e.eventType === 'TRANSPORT' &&
|
|
144
|
+
e.eventCode === 'DEPA' &&
|
|
145
|
+
e.eventClassifierCode === 'ACT' &&
|
|
146
|
+
e.modeOfTransport === 'VESSEL' &&
|
|
147
|
+
e.carrierExportVoyageNumber &&
|
|
148
|
+
e.locationUnlocode
|
|
149
|
+
)
|
|
150
|
+
|
|
151
|
+
if (firstVesselDepa?.locationUnlocode) {
|
|
152
|
+
result.originUnlocode = firstVesselDepa.locationUnlocode
|
|
153
|
+
result.confidence.origin = 'high'
|
|
154
|
+
}
|
|
155
|
+
|
|
156
|
+
// Strategy 2: First LOAD event on VESSEL
|
|
157
|
+
if (!result.originUnlocode) {
|
|
158
|
+
const firstLoad = sortedEvents.find(
|
|
159
|
+
(e) =>
|
|
160
|
+
e.eventType === 'EQUIPMENT' &&
|
|
161
|
+
e.eventCode === 'LOAD' &&
|
|
162
|
+
e.eventClassifierCode === 'ACT' &&
|
|
163
|
+
e.modeOfTransport === 'VESSEL' &&
|
|
164
|
+
e.locationUnlocode
|
|
165
|
+
)
|
|
166
|
+
|
|
167
|
+
if (firstLoad?.locationUnlocode) {
|
|
168
|
+
result.originUnlocode = firstLoad.locationUnlocode
|
|
169
|
+
result.confidence.origin = 'high'
|
|
170
|
+
}
|
|
171
|
+
}
|
|
172
|
+
|
|
173
|
+
// Strategy 3: First GTIN (LADEN) event at a port terminal (fallback)
|
|
174
|
+
// Only consider events at port terminals (POTE), not inland depots (DEPO)
|
|
175
|
+
if (!result.originUnlocode) {
|
|
176
|
+
const firstGtinLaden = sortedEvents.find(
|
|
177
|
+
(e) =>
|
|
178
|
+
e.eventType === 'EQUIPMENT' &&
|
|
179
|
+
e.eventCode === 'GTIN' &&
|
|
180
|
+
e.emptyIndicatorCode === 'LADEN' &&
|
|
181
|
+
e.eventClassifierCode === 'ACT' &&
|
|
182
|
+
e.locationUnlocode &&
|
|
183
|
+
e.facilityTypeCode === 'POTE' // Port Terminal only
|
|
184
|
+
)
|
|
185
|
+
|
|
186
|
+
if (firstGtinLaden?.locationUnlocode) {
|
|
187
|
+
result.originUnlocode = firstGtinLaden.locationUnlocode
|
|
188
|
+
result.confidence.origin = 'medium'
|
|
189
|
+
}
|
|
190
|
+
}
|
|
191
|
+
|
|
192
|
+
// Strategy 4: First EST/PLN DEPA on VESSEL (for pre-departure tracking)
|
|
193
|
+
// When no actual events exist yet, use planned departure to infer origin
|
|
194
|
+
if (!result.originUnlocode) {
|
|
195
|
+
const firstPlannedDepa = sortedEvents.find(
|
|
196
|
+
(e) =>
|
|
197
|
+
e.eventType === 'TRANSPORT' &&
|
|
198
|
+
e.eventCode === 'DEPA' &&
|
|
199
|
+
(e.eventClassifierCode === 'EST' || e.eventClassifierCode === 'PLN') &&
|
|
200
|
+
e.modeOfTransport === 'VESSEL' &&
|
|
201
|
+
e.locationUnlocode
|
|
202
|
+
)
|
|
203
|
+
|
|
204
|
+
if (firstPlannedDepa?.locationUnlocode) {
|
|
205
|
+
result.originUnlocode = firstPlannedDepa.locationUnlocode
|
|
206
|
+
result.confidence.origin = 'medium' // Lower confidence for planned events
|
|
207
|
+
}
|
|
208
|
+
}
|
|
209
|
+
|
|
210
|
+
// Strategy 5: First PLN LOAD event on VESSEL (for pre-departure tracking)
|
|
211
|
+
if (!result.originUnlocode) {
|
|
212
|
+
const firstPlannedLoad = sortedEvents.find(
|
|
213
|
+
(e) =>
|
|
214
|
+
e.eventType === 'EQUIPMENT' &&
|
|
215
|
+
e.eventCode === 'LOAD' &&
|
|
216
|
+
(e.eventClassifierCode === 'EST' || e.eventClassifierCode === 'PLN') &&
|
|
217
|
+
e.modeOfTransport === 'VESSEL' &&
|
|
218
|
+
e.locationUnlocode
|
|
219
|
+
)
|
|
220
|
+
|
|
221
|
+
if (firstPlannedLoad?.locationUnlocode) {
|
|
222
|
+
result.originUnlocode = firstPlannedLoad.locationUnlocode
|
|
223
|
+
result.confidence.origin = 'medium'
|
|
224
|
+
}
|
|
225
|
+
}
|
|
226
|
+
|
|
227
|
+
return result
|
|
228
|
+
}
|
|
229
|
+
|
|
230
|
+
/**
|
|
231
|
+
* Validates a UN/LOCODE format.
|
|
232
|
+
* Format: 2 letters (country) + 3 alphanumeric (location)
|
|
233
|
+
* Example: CNYTN (China, Yantian), PLGDN (Poland, Gdansk)
|
|
234
|
+
*/
|
|
235
|
+
export function isValidUnlocode(code: string | null | undefined): boolean {
|
|
236
|
+
if (!code) return false
|
|
237
|
+
return /^[A-Z]{2}[A-Z0-9]{3}$/.test(code.toUpperCase())
|
|
238
|
+
}
|
|
@@ -0,0 +1,92 @@
|
|
|
1
|
+
const DEFAULT_WINDOW_DAYS = 120 // 4 months
|
|
2
|
+
const JITTER_RANGE_HOURS = 4
|
|
3
|
+
|
|
4
|
+
function addDays(date: Date, days: number): Date {
|
|
5
|
+
const result = new Date(date)
|
|
6
|
+
result.setDate(result.getDate() + days)
|
|
7
|
+
return result
|
|
8
|
+
}
|
|
9
|
+
|
|
10
|
+
function addHours(date: Date, hours: number): Date {
|
|
11
|
+
const result = new Date(date)
|
|
12
|
+
result.setTime(result.getTime() + hours * 60 * 60 * 1000)
|
|
13
|
+
return result
|
|
14
|
+
}
|
|
15
|
+
|
|
16
|
+
function randomJitter(): number {
|
|
17
|
+
return Math.random() * JITTER_RANGE_HOURS - JITTER_RANGE_HOURS / 2
|
|
18
|
+
}
|
|
19
|
+
|
|
20
|
+
/**
|
|
21
|
+
* Generates a list of daily poll dates with random time jitter.
|
|
22
|
+
*
|
|
23
|
+
* - If ETD and ETA are provided, generates dates from ETD - 7 days to ETA + 14 days.
|
|
24
|
+
* - If only ETD is provided, generates from ETD - 7 days for the default window.
|
|
25
|
+
* - If nothing is provided, starts from now for the default window.
|
|
26
|
+
* - Jitter is +/- 2 hours to spread API load.
|
|
27
|
+
*/
|
|
28
|
+
export function generatePollSchedule(input?: {
|
|
29
|
+
etd?: Date | null
|
|
30
|
+
eta?: Date | null
|
|
31
|
+
atd?: Date | null
|
|
32
|
+
ata?: Date | null
|
|
33
|
+
}): string[] {
|
|
34
|
+
const now = new Date()
|
|
35
|
+
|
|
36
|
+
// If already delivered (ATA present), no more polling needed
|
|
37
|
+
if (input?.ata) {
|
|
38
|
+
return []
|
|
39
|
+
}
|
|
40
|
+
|
|
41
|
+
let startDate: Date
|
|
42
|
+
let endDate: Date
|
|
43
|
+
|
|
44
|
+
if (input?.etd) {
|
|
45
|
+
startDate = addDays(input.etd, -7)
|
|
46
|
+
} else if (input?.atd) {
|
|
47
|
+
startDate = input.atd
|
|
48
|
+
} else {
|
|
49
|
+
startDate = now
|
|
50
|
+
}
|
|
51
|
+
|
|
52
|
+
if (input?.eta) {
|
|
53
|
+
endDate = addDays(input.eta, 14)
|
|
54
|
+
} else {
|
|
55
|
+
endDate = addDays(startDate, DEFAULT_WINDOW_DAYS)
|
|
56
|
+
}
|
|
57
|
+
|
|
58
|
+
// Ensure start is not in the past
|
|
59
|
+
if (startDate < now) {
|
|
60
|
+
startDate = now
|
|
61
|
+
}
|
|
62
|
+
|
|
63
|
+
const dates: string[] = []
|
|
64
|
+
let current = new Date(startDate)
|
|
65
|
+
|
|
66
|
+
while (current <= endDate) {
|
|
67
|
+
const withJitter = addHours(current, randomJitter())
|
|
68
|
+
// Only include future dates
|
|
69
|
+
if (withJitter > now) {
|
|
70
|
+
dates.push(withJitter.toISOString())
|
|
71
|
+
}
|
|
72
|
+
current = addDays(current, 1)
|
|
73
|
+
}
|
|
74
|
+
|
|
75
|
+
return dates
|
|
76
|
+
}
|
|
77
|
+
|
|
78
|
+
/**
|
|
79
|
+
* Given a schedule (array of ISO date strings), returns the next poll date after now.
|
|
80
|
+
*/
|
|
81
|
+
export function getNextPollDate(schedule: string[]): Date | null {
|
|
82
|
+
const now = Date.now()
|
|
83
|
+
|
|
84
|
+
for (const dateStr of schedule) {
|
|
85
|
+
const date = new Date(dateStr)
|
|
86
|
+
if (date.getTime() > now) {
|
|
87
|
+
return date
|
|
88
|
+
}
|
|
89
|
+
}
|
|
90
|
+
|
|
91
|
+
return null
|
|
92
|
+
}
|
|
@@ -0,0 +1,350 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* ShipsGo API v2 client (ocean + air).
|
|
3
|
+
*
|
|
4
|
+
* ShipsGo is a register-then-poll provider: you POST a shipment (spending a
|
|
5
|
+
* credit) and receive a ShipsGo shipment id, then GET that id for movements.
|
|
6
|
+
* Registration is idempotent on `reference` — a duplicate returns 409 with the
|
|
7
|
+
* existing shipment, so we treat 200 and 409 the same (both yield an id).
|
|
8
|
+
*
|
|
9
|
+
* Raw `fetch` is correct here (this is a server-side lib, not UI — `apiCall`
|
|
10
|
+
* is the frontend helper). Auth is the `X-Shipsgo-User-Token` header.
|
|
11
|
+
*
|
|
12
|
+
* Docs: https://api.shipsgo.com/v2
|
|
13
|
+
*/
|
|
14
|
+
|
|
15
|
+
const TOKEN_HEADER = 'X-Shipsgo-User-Token'
|
|
16
|
+
|
|
17
|
+
/**
|
|
18
|
+
* Per-request timeout. ShipsGo polls run inside a sequential job loop, so a hung
|
|
19
|
+
* endpoint would otherwise stall the whole worker. Aborted requests surface as a
|
|
20
|
+
* normal fetch error → recorded as a transient job error.
|
|
21
|
+
*/
|
|
22
|
+
const REQUEST_TIMEOUT_MS = 20_000
|
|
23
|
+
|
|
24
|
+
/**
|
|
25
|
+
* Standardizes an AWB to ShipsGo's `NNN-NNNNNNNN` shape (3 digits, hyphen, 8
|
|
26
|
+
* digits) so a value typed with spaces/other separators still validates against
|
|
27
|
+
* ShipsGo's `^[0-9]{3}(-)?[0-9]{8}$` pattern. Returns the trimmed input
|
|
28
|
+
* unchanged when it isn't 11 digits (let ShipsGo return the authoritative error).
|
|
29
|
+
*/
|
|
30
|
+
export function normalizeAwbNumber(raw: string): string {
|
|
31
|
+
const digits = raw.replace(/\D/g, '')
|
|
32
|
+
if (digits.length === 11) return `${digits.slice(0, 3)}-${digits.slice(3)}`
|
|
33
|
+
return raw.trim()
|
|
34
|
+
}
|
|
35
|
+
|
|
36
|
+
export type ShipsGoClientConfig = {
|
|
37
|
+
apiToken: string
|
|
38
|
+
baseUrl: string
|
|
39
|
+
}
|
|
40
|
+
|
|
41
|
+
/** Thrown on HTTP 402 — the ShipsGo account has no remaining credits. */
|
|
42
|
+
export class ShipsGoCreditsExhaustedError extends Error {
|
|
43
|
+
constructor(message = 'ShipsGo credits exhausted (HTTP 402)') {
|
|
44
|
+
super(message)
|
|
45
|
+
this.name = 'ShipsGoCreditsExhaustedError'
|
|
46
|
+
}
|
|
47
|
+
}
|
|
48
|
+
|
|
49
|
+
/**
|
|
50
|
+
* Thrown on HTTP 403 — the token is missing/invalid/expired. This is an
|
|
51
|
+
* account-level condition (like 402), not a per-job fault, so callers back the
|
|
52
|
+
* job off instead of burning its retry budget.
|
|
53
|
+
*/
|
|
54
|
+
export class ShipsGoAuthError extends Error {
|
|
55
|
+
constructor(message = 'ShipsGo authentication failed (HTTP 403)') {
|
|
56
|
+
super(message)
|
|
57
|
+
this.name = 'ShipsGoAuthError'
|
|
58
|
+
}
|
|
59
|
+
}
|
|
60
|
+
|
|
61
|
+
/**
|
|
62
|
+
* Thrown on HTTP 429 — the ShipsGo rate limit (100 req/min, collective across
|
|
63
|
+
* the account) was exceeded. `retryAfterSeconds` is derived from the
|
|
64
|
+
* `RateLimit-Reset` (UTC epoch) or `Retry-After` header when present. Transient,
|
|
65
|
+
* so callers back the job off without consuming its retry budget.
|
|
66
|
+
*/
|
|
67
|
+
export class ShipsGoRateLimitedError extends Error {
|
|
68
|
+
constructor(
|
|
69
|
+
message = 'ShipsGo rate limit exceeded (HTTP 429)',
|
|
70
|
+
public readonly retryAfterSeconds?: number,
|
|
71
|
+
) {
|
|
72
|
+
super(message)
|
|
73
|
+
this.name = 'ShipsGoRateLimitedError'
|
|
74
|
+
}
|
|
75
|
+
}
|
|
76
|
+
|
|
77
|
+
/** Thrown for any other non-2xx (and non-409) ShipsGo response. */
|
|
78
|
+
export class ShipsGoApiError extends Error {
|
|
79
|
+
constructor(
|
|
80
|
+
public readonly status: number,
|
|
81
|
+
message: string,
|
|
82
|
+
) {
|
|
83
|
+
super(message)
|
|
84
|
+
this.name = 'ShipsGoApiError'
|
|
85
|
+
}
|
|
86
|
+
}
|
|
87
|
+
|
|
88
|
+
export type ShipsGoRegisterResult = {
|
|
89
|
+
/** ShipsGo shipment id (stringified integer). */
|
|
90
|
+
id: string
|
|
91
|
+
/** True when the shipment already existed (HTTP 409) — no credit spent. */
|
|
92
|
+
alreadyExisted: boolean
|
|
93
|
+
}
|
|
94
|
+
|
|
95
|
+
// ─── Response types (only the fields we consume) ─────────────────────────────
|
|
96
|
+
|
|
97
|
+
export type ShipsGoOceanLocation = {
|
|
98
|
+
code?: string | null
|
|
99
|
+
name?: string | null
|
|
100
|
+
timezone?: string | null
|
|
101
|
+
country?: { code?: string | null; name?: string | null } | null
|
|
102
|
+
}
|
|
103
|
+
|
|
104
|
+
export type ShipsGoOceanMovement = {
|
|
105
|
+
event: string // EMSH | GTIN | LOAD | DEPA | ARRV | DISC | GTOT | EMRT
|
|
106
|
+
status: 'EST' | 'ACT'
|
|
107
|
+
location?: ShipsGoOceanLocation | null
|
|
108
|
+
vessel?: { imo?: number | null; name?: string | null } | null
|
|
109
|
+
voyage?: string | null
|
|
110
|
+
timestamp: string
|
|
111
|
+
}
|
|
112
|
+
|
|
113
|
+
export type ShipsGoOceanContainer = {
|
|
114
|
+
number: string
|
|
115
|
+
status?: string | null
|
|
116
|
+
size?: number | null
|
|
117
|
+
type?: string | null
|
|
118
|
+
movements?: ShipsGoOceanMovement[] | null
|
|
119
|
+
}
|
|
120
|
+
|
|
121
|
+
/** ShipsGo route summary (shipment-level; POL/POD, transit, emissions). */
|
|
122
|
+
export type ShipsGoOceanRoutePort = {
|
|
123
|
+
location?: ShipsGoOceanLocation | null
|
|
124
|
+
date_of_loading?: string | null
|
|
125
|
+
date_of_discharge?: string | null
|
|
126
|
+
date_of_discharge_predicted?: string | null
|
|
127
|
+
}
|
|
128
|
+
|
|
129
|
+
export type ShipsGoOceanRoute = {
|
|
130
|
+
port_of_loading?: ShipsGoOceanRoutePort | null
|
|
131
|
+
port_of_discharge?: ShipsGoOceanRoutePort | null
|
|
132
|
+
transit_time?: number | null
|
|
133
|
+
transit_percentage?: number | null
|
|
134
|
+
co2_emission?: number | null
|
|
135
|
+
ts_count?: number | null
|
|
136
|
+
}
|
|
137
|
+
|
|
138
|
+
export type ShipsGoOceanShipment = {
|
|
139
|
+
id: number
|
|
140
|
+
reference?: string | null
|
|
141
|
+
booking_number?: string | null
|
|
142
|
+
container_number?: string | null
|
|
143
|
+
carrier?: { scac?: string | null; name?: string | null; status?: string | null } | null
|
|
144
|
+
status?: string | null
|
|
145
|
+
route?: ShipsGoOceanRoute | null
|
|
146
|
+
containers?: ShipsGoOceanContainer[] | null
|
|
147
|
+
}
|
|
148
|
+
|
|
149
|
+
// ─── Air response types (only the fields we consume) ─────────────────────────
|
|
150
|
+
|
|
151
|
+
export type ShipsGoAirLocation = {
|
|
152
|
+
name?: string | null
|
|
153
|
+
iata?: string | null
|
|
154
|
+
timezone?: string | null
|
|
155
|
+
country?: { code?: string | null; name?: string | null } | null
|
|
156
|
+
}
|
|
157
|
+
|
|
158
|
+
export type ShipsGoAirMovement = {
|
|
159
|
+
event: string // RCS | MAN | DEP | ARR | RCF | DLV
|
|
160
|
+
status: 'EST' | 'ACT'
|
|
161
|
+
location?: ShipsGoAirLocation | null
|
|
162
|
+
flight?: string | null
|
|
163
|
+
timestamp: string
|
|
164
|
+
}
|
|
165
|
+
|
|
166
|
+
/** ShipsGo air cargo summary (pieces / weight / volume). */
|
|
167
|
+
export type ShipsGoAirCargo = {
|
|
168
|
+
pieces?: number | null
|
|
169
|
+
weight?: number | null
|
|
170
|
+
weight_unit?: string | null
|
|
171
|
+
volume?: number | null
|
|
172
|
+
volume_unit?: string | null
|
|
173
|
+
}
|
|
174
|
+
|
|
175
|
+
/** ShipsGo air route summary (origin/destination airports + transit/emissions). */
|
|
176
|
+
export type ShipsGoAirRoutePort = {
|
|
177
|
+
location?: ShipsGoAirLocation | null
|
|
178
|
+
date_of_dep?: string | null
|
|
179
|
+
date_of_rcf?: string | null
|
|
180
|
+
}
|
|
181
|
+
|
|
182
|
+
export type ShipsGoAirRoute = {
|
|
183
|
+
origin?: ShipsGoAirRoutePort | null
|
|
184
|
+
destination?: ShipsGoAirRoutePort | null
|
|
185
|
+
transit_time?: number | null
|
|
186
|
+
transit_percentage?: number | null
|
|
187
|
+
co2_emission?: number | null
|
|
188
|
+
ts_count?: number | null
|
|
189
|
+
}
|
|
190
|
+
|
|
191
|
+
export type ShipsGoAirShipment = {
|
|
192
|
+
id: number
|
|
193
|
+
reference?: string | null
|
|
194
|
+
awb_number?: string | null
|
|
195
|
+
airline?: { iata?: string | null; name?: string | null } | null
|
|
196
|
+
status?: string | null // NEW | INPROGRESS | BOOKED | EN_ROUTE | LANDED | DELIVERED | UNTRACKED
|
|
197
|
+
status_extended?: unknown | null
|
|
198
|
+
cargo?: ShipsGoAirCargo | null
|
|
199
|
+
route?: ShipsGoAirRoute | null
|
|
200
|
+
movements?: ShipsGoAirMovement[] | null
|
|
201
|
+
}
|
|
202
|
+
|
|
203
|
+
type ShipmentEnvelope<T> = { message?: string; shipment?: T }
|
|
204
|
+
|
|
205
|
+
// ─── Internal fetch helper ───────────────────────────────────────────────────
|
|
206
|
+
|
|
207
|
+
async function shipsGoFetch(
|
|
208
|
+
config: ShipsGoClientConfig,
|
|
209
|
+
path: string,
|
|
210
|
+
init: RequestInit,
|
|
211
|
+
): Promise<Response> {
|
|
212
|
+
const url = `${config.baseUrl.replace(/\/$/, '')}${path}`
|
|
213
|
+
return fetch(url, {
|
|
214
|
+
...init,
|
|
215
|
+
signal: init.signal ?? AbortSignal.timeout(REQUEST_TIMEOUT_MS),
|
|
216
|
+
headers: {
|
|
217
|
+
[TOKEN_HEADER]: config.apiToken,
|
|
218
|
+
'Content-Type': 'application/json',
|
|
219
|
+
Accept: 'application/json',
|
|
220
|
+
...(init.headers ?? {}),
|
|
221
|
+
},
|
|
222
|
+
})
|
|
223
|
+
}
|
|
224
|
+
|
|
225
|
+
/**
|
|
226
|
+
* Maps account-level HTTP statuses (402/403/429) to their typed errors so every
|
|
227
|
+
* caller (register + GET) handles them uniformly. Returns without throwing for
|
|
228
|
+
* anything else; the caller then handles 409 / generic failures itself.
|
|
229
|
+
*/
|
|
230
|
+
function throwForAccountLevelStatus(response: Response): void {
|
|
231
|
+
if (response.status === 402) throw new ShipsGoCreditsExhaustedError()
|
|
232
|
+
if (response.status === 403) throw new ShipsGoAuthError()
|
|
233
|
+
if (response.status === 429) throw new ShipsGoRateLimitedError(undefined, retryAfterSeconds(response))
|
|
234
|
+
}
|
|
235
|
+
|
|
236
|
+
/** Seconds until the rate-limit window resets, from `RateLimit-Reset` (UTC epoch) or `Retry-After`. */
|
|
237
|
+
function retryAfterSeconds(response: Response): number | undefined {
|
|
238
|
+
const reset = response.headers.get('RateLimit-Reset') ?? response.headers.get('X-RateLimit-Reset')
|
|
239
|
+
if (reset) {
|
|
240
|
+
const resetEpoch = Number(reset)
|
|
241
|
+
if (Number.isFinite(resetEpoch)) return Math.max(0, Math.ceil(resetEpoch - Date.now() / 1000))
|
|
242
|
+
}
|
|
243
|
+
const retryAfter = response.headers.get('Retry-After')
|
|
244
|
+
if (retryAfter) {
|
|
245
|
+
const secs = Number(retryAfter)
|
|
246
|
+
if (Number.isFinite(secs)) return Math.max(0, Math.ceil(secs))
|
|
247
|
+
}
|
|
248
|
+
return undefined
|
|
249
|
+
}
|
|
250
|
+
|
|
251
|
+
function toId(shipment: { id?: number } | undefined): string {
|
|
252
|
+
if (!shipment || shipment.id == null) {
|
|
253
|
+
throw new ShipsGoApiError(500, 'ShipsGo response missing shipment id')
|
|
254
|
+
}
|
|
255
|
+
return String(shipment.id)
|
|
256
|
+
}
|
|
257
|
+
|
|
258
|
+
async function handleRegister(response: Response): Promise<ShipsGoRegisterResult> {
|
|
259
|
+
throwForAccountLevelStatus(response)
|
|
260
|
+
if (response.status === 409) {
|
|
261
|
+
// Already exists — free; extract the existing shipment id.
|
|
262
|
+
const body = (await response.json().catch(() => ({}))) as ShipmentEnvelope<{ id: number }>
|
|
263
|
+
return { id: toId(body.shipment), alreadyExisted: true }
|
|
264
|
+
}
|
|
265
|
+
if (!response.ok) {
|
|
266
|
+
const text = await response.text().catch(() => 'unknown')
|
|
267
|
+
throw new ShipsGoApiError(response.status, `ShipsGo register failed (${response.status}): ${text}`)
|
|
268
|
+
}
|
|
269
|
+
const body = (await response.json()) as ShipmentEnvelope<{ id: number }>
|
|
270
|
+
return { id: toId(body.shipment), alreadyExisted: false }
|
|
271
|
+
}
|
|
272
|
+
|
|
273
|
+
// ─── Ocean ───────────────────────────────────────────────────────────────────
|
|
274
|
+
|
|
275
|
+
export async function registerOceanShipment(
|
|
276
|
+
config: ShipsGoClientConfig,
|
|
277
|
+
input: {
|
|
278
|
+
reference: string
|
|
279
|
+
bookingNumber?: string | null
|
|
280
|
+
containerNumber?: string | null
|
|
281
|
+
/** Optional SCAC hint — ShipsGo auto-detects the line when omitted. */
|
|
282
|
+
carrier?: string | null
|
|
283
|
+
},
|
|
284
|
+
): Promise<ShipsGoRegisterResult> {
|
|
285
|
+
const payload: Record<string, unknown> = { reference: input.reference }
|
|
286
|
+
if (input.bookingNumber) payload.booking_number = input.bookingNumber
|
|
287
|
+
else if (input.containerNumber) payload.container_number = input.containerNumber
|
|
288
|
+
if (input.carrier) payload.carrier = input.carrier
|
|
289
|
+
|
|
290
|
+
const response = await shipsGoFetch(config, '/ocean/shipments', {
|
|
291
|
+
method: 'POST',
|
|
292
|
+
body: JSON.stringify(payload),
|
|
293
|
+
})
|
|
294
|
+
return handleRegister(response)
|
|
295
|
+
}
|
|
296
|
+
|
|
297
|
+
export async function getOceanShipment(
|
|
298
|
+
config: ShipsGoClientConfig,
|
|
299
|
+
id: string,
|
|
300
|
+
): Promise<ShipsGoOceanShipment> {
|
|
301
|
+
const response = await shipsGoFetch(config, `/ocean/shipments/${encodeURIComponent(id)}`, {
|
|
302
|
+
method: 'GET',
|
|
303
|
+
})
|
|
304
|
+
throwForAccountLevelStatus(response)
|
|
305
|
+
if (!response.ok) {
|
|
306
|
+
const text = await response.text().catch(() => 'unknown')
|
|
307
|
+
throw new ShipsGoApiError(response.status, `ShipsGo get ocean shipment failed (${response.status}): ${text}`)
|
|
308
|
+
}
|
|
309
|
+
const body = (await response.json()) as ShipmentEnvelope<ShipsGoOceanShipment>
|
|
310
|
+
if (!body.shipment) {
|
|
311
|
+
throw new ShipsGoApiError(500, 'ShipsGo ocean response missing shipment')
|
|
312
|
+
}
|
|
313
|
+
return body.shipment
|
|
314
|
+
}
|
|
315
|
+
|
|
316
|
+
// ─── Air ───────────────────────────────────────────────────────────────────────
|
|
317
|
+
|
|
318
|
+
export async function registerAirShipment(
|
|
319
|
+
config: ShipsGoClientConfig,
|
|
320
|
+
input: {
|
|
321
|
+
reference: string
|
|
322
|
+
/** Air Waybill number — ShipsGo standardizes to `333-88888888`. */
|
|
323
|
+
awbNumber: string
|
|
324
|
+
},
|
|
325
|
+
): Promise<ShipsGoRegisterResult> {
|
|
326
|
+
const response = await shipsGoFetch(config, '/air/shipments', {
|
|
327
|
+
method: 'POST',
|
|
328
|
+
body: JSON.stringify({ reference: input.reference, awb_number: normalizeAwbNumber(input.awbNumber) }),
|
|
329
|
+
})
|
|
330
|
+
return handleRegister(response)
|
|
331
|
+
}
|
|
332
|
+
|
|
333
|
+
export async function getAirShipment(
|
|
334
|
+
config: ShipsGoClientConfig,
|
|
335
|
+
id: string,
|
|
336
|
+
): Promise<ShipsGoAirShipment> {
|
|
337
|
+
const response = await shipsGoFetch(config, `/air/shipments/${encodeURIComponent(id)}`, {
|
|
338
|
+
method: 'GET',
|
|
339
|
+
})
|
|
340
|
+
throwForAccountLevelStatus(response)
|
|
341
|
+
if (!response.ok) {
|
|
342
|
+
const text = await response.text().catch(() => 'unknown')
|
|
343
|
+
throw new ShipsGoApiError(response.status, `ShipsGo get air shipment failed (${response.status}): ${text}`)
|
|
344
|
+
}
|
|
345
|
+
const body = (await response.json()) as ShipmentEnvelope<ShipsGoAirShipment>
|
|
346
|
+
if (!body.shipment) {
|
|
347
|
+
throw new ShipsGoApiError(500, 'ShipsGo air response missing shipment')
|
|
348
|
+
}
|
|
349
|
+
return body.shipment
|
|
350
|
+
}
|