@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,100 @@
|
|
|
1
|
+
function getLatestTimestamp(entries) {
|
|
2
|
+
if (!entries?.length) return null;
|
|
3
|
+
const latest = entries.reduce(
|
|
4
|
+
(best, entry) => entry.updatedAt >= best.updatedAt ? entry : best
|
|
5
|
+
);
|
|
6
|
+
return {
|
|
7
|
+
value: new Date(latest.value),
|
|
8
|
+
offset: latest.offset,
|
|
9
|
+
source: latest.source,
|
|
10
|
+
updatedAt: new Date(latest.updatedAt),
|
|
11
|
+
sourceEventId: latest.sourceEventId
|
|
12
|
+
};
|
|
13
|
+
}
|
|
14
|
+
function getPrimaryTimestampValue(entries) {
|
|
15
|
+
return getLatestTimestamp(entries)?.value ?? null;
|
|
16
|
+
}
|
|
17
|
+
function getPrimaryTimestampOffset(entries) {
|
|
18
|
+
return getLatestTimestamp(entries)?.offset ?? null;
|
|
19
|
+
}
|
|
20
|
+
function createTimestampEntry(value, offset, source, sourceEventId) {
|
|
21
|
+
const dateValue = typeof value === "string" ? new Date(value) : value;
|
|
22
|
+
return {
|
|
23
|
+
value: dateValue.toISOString(),
|
|
24
|
+
offset,
|
|
25
|
+
source,
|
|
26
|
+
updatedAt: (/* @__PURE__ */ new Date()).toISOString(),
|
|
27
|
+
sourceEventId: sourceEventId ?? null
|
|
28
|
+
};
|
|
29
|
+
}
|
|
30
|
+
function addTimestampEntry(entries, newEntry) {
|
|
31
|
+
const result = entries ? [...entries] : [];
|
|
32
|
+
const isDuplicate = result.some(
|
|
33
|
+
(e) => e.value === newEntry.value && e.source === newEntry.source
|
|
34
|
+
);
|
|
35
|
+
if (!isDuplicate) {
|
|
36
|
+
result.push(newEntry);
|
|
37
|
+
}
|
|
38
|
+
return result;
|
|
39
|
+
}
|
|
40
|
+
function mergeExtractedTimestamps(current, extracted, source, sourceEventId) {
|
|
41
|
+
let changed = false;
|
|
42
|
+
let etdTimestamps = current.etdTimestamps ?? null;
|
|
43
|
+
let etaTimestamps = current.etaTimestamps ?? null;
|
|
44
|
+
let atdTimestamps = current.atdTimestamps ?? null;
|
|
45
|
+
let ataTimestamps = current.ataTimestamps ?? null;
|
|
46
|
+
if (extracted.etd) {
|
|
47
|
+
const entry = createTimestampEntry(extracted.etd, extracted.etdOffset ?? null, source, sourceEventId);
|
|
48
|
+
const before = etdTimestamps?.length ?? 0;
|
|
49
|
+
etdTimestamps = addTimestampEntry(etdTimestamps, entry);
|
|
50
|
+
if ((etdTimestamps?.length ?? 0) > before) changed = true;
|
|
51
|
+
}
|
|
52
|
+
if (extracted.eta) {
|
|
53
|
+
const entry = createTimestampEntry(extracted.eta, extracted.etaOffset ?? null, source, sourceEventId);
|
|
54
|
+
const before = etaTimestamps?.length ?? 0;
|
|
55
|
+
etaTimestamps = addTimestampEntry(etaTimestamps, entry);
|
|
56
|
+
if ((etaTimestamps?.length ?? 0) > before) changed = true;
|
|
57
|
+
}
|
|
58
|
+
if (extracted.atd) {
|
|
59
|
+
const entry = createTimestampEntry(extracted.atd, extracted.atdOffset ?? null, source, sourceEventId);
|
|
60
|
+
const before = atdTimestamps?.length ?? 0;
|
|
61
|
+
atdTimestamps = addTimestampEntry(atdTimestamps, entry);
|
|
62
|
+
if ((atdTimestamps?.length ?? 0) > before) changed = true;
|
|
63
|
+
}
|
|
64
|
+
if (extracted.ata) {
|
|
65
|
+
const entry = createTimestampEntry(extracted.ata, extracted.ataOffset ?? null, source, sourceEventId);
|
|
66
|
+
const before = ataTimestamps?.length ?? 0;
|
|
67
|
+
ataTimestamps = addTimestampEntry(ataTimestamps, entry);
|
|
68
|
+
if ((ataTimestamps?.length ?? 0) > before) changed = true;
|
|
69
|
+
}
|
|
70
|
+
return {
|
|
71
|
+
etdTimestamps,
|
|
72
|
+
etaTimestamps,
|
|
73
|
+
atdTimestamps,
|
|
74
|
+
ataTimestamps,
|
|
75
|
+
changed
|
|
76
|
+
};
|
|
77
|
+
}
|
|
78
|
+
function hasTimestamps(entries) {
|
|
79
|
+
return !!entries && entries.length > 0;
|
|
80
|
+
}
|
|
81
|
+
function getTimestampsBySource(entries, source) {
|
|
82
|
+
if (!entries) return [];
|
|
83
|
+
return entries.filter((e) => e.source === source);
|
|
84
|
+
}
|
|
85
|
+
function getTimestampHistory(entries) {
|
|
86
|
+
if (!entries) return [];
|
|
87
|
+
return [...entries].sort((a, b) => a.updatedAt.localeCompare(b.updatedAt));
|
|
88
|
+
}
|
|
89
|
+
export {
|
|
90
|
+
addTimestampEntry,
|
|
91
|
+
createTimestampEntry,
|
|
92
|
+
getLatestTimestamp,
|
|
93
|
+
getPrimaryTimestampOffset,
|
|
94
|
+
getPrimaryTimestampValue,
|
|
95
|
+
getTimestampHistory,
|
|
96
|
+
getTimestampsBySource,
|
|
97
|
+
hasTimestamps,
|
|
98
|
+
mergeExtractedTimestamps
|
|
99
|
+
};
|
|
100
|
+
//# sourceMappingURL=timestamp-utils.js.map
|
|
@@ -0,0 +1,7 @@
|
|
|
1
|
+
{
|
|
2
|
+
"version": 3,
|
|
3
|
+
"sources": ["../../../../src/modules/shipment_tracking/lib/timestamp-utils.ts"],
|
|
4
|
+
"sourcesContent": ["/**\n * Multi-source timestamp utilities for shipment tracking.\n *\n * Supports tracking ETD/ETA/ATD/ATA from multiple sources with full history (SCD pattern).\n * Primary value is computed using \"latest update wins\" strategy.\n */\n\nexport type TimestampSource = 'carrier_api' | 'manual' | 'ais' | 'port' | 'edi'\n\nexport type TimestampType = 'ETD' | 'ETA' | 'ATD' | 'ATA'\n\n/**\n * A single timestamp entry tracking value, source, and when it was recorded.\n * Multiple entries per source are allowed (SCD - Slowly Changing Dimension pattern).\n */\nexport type ShipmentTimestampEntry = {\n /** ISO 8601 datetime value (e.g., \"2026-02-24T15:30:00\") */\n value: string\n /** Original timezone offset (e.g., \"+08:00\", \"-05:00\", \"Z\") */\n offset: string | null\n /** Source system that provided this timestamp */\n source: TimestampSource\n /** When this entry was recorded/updated (ISO 8601) */\n updatedAt: string\n /** Optional reference to the source tracking event ID */\n sourceEventId?: string | null\n}\n\n/**\n * Computed primary timestamp with its metadata.\n */\nexport type ComputedTimestamp = {\n value: Date\n offset: string | null\n source: TimestampSource\n updatedAt: Date\n sourceEventId?: string | null\n}\n\n/**\n * Gets the latest (primary) timestamp from an array of entries.\n * Uses \"latest update wins\" strategy - the entry with most recent updatedAt is primary.\n *\n * @param entries Array of timestamp entries or null/undefined\n * @returns The latest entry or null if no entries\n */\nexport function getLatestTimestamp(\n entries: ShipmentTimestampEntry[] | null | undefined,\n): ComputedTimestamp | null {\n if (!entries?.length) return null\n\n // Use >= to prefer later entries when updatedAt is equal.\n // This handles the case where entries are added in the same millisecond.\n const latest = entries.reduce((best, entry) =>\n entry.updatedAt >= best.updatedAt ? entry : best,\n )\n\n return {\n value: new Date(latest.value),\n offset: latest.offset,\n source: latest.source,\n updatedAt: new Date(latest.updatedAt),\n sourceEventId: latest.sourceEventId,\n }\n}\n\n/**\n * Gets the primary value as a Date, or null if no entries.\n * Convenience wrapper around getLatestTimestamp.\n */\nexport function getPrimaryTimestampValue(\n entries: ShipmentTimestampEntry[] | null | undefined,\n): Date | null {\n return getLatestTimestamp(entries)?.value ?? null\n}\n\n/**\n * Gets the primary offset, or null if no entries.\n */\nexport function getPrimaryTimestampOffset(\n entries: ShipmentTimestampEntry[] | null | undefined,\n): string | null {\n return getLatestTimestamp(entries)?.offset ?? null\n}\n\n/**\n * Creates a new timestamp entry.\n *\n * @param value The timestamp value (Date or ISO string)\n * @param offset Timezone offset string (e.g., \"+08:00\") or null\n * @param source Source system\n * @param sourceEventId Optional source event ID for traceability\n * @returns A new ShipmentTimestampEntry\n */\nexport function createTimestampEntry(\n value: Date | string,\n offset: string | null,\n source: TimestampSource,\n sourceEventId?: string | null,\n): ShipmentTimestampEntry {\n const dateValue = typeof value === 'string' ? new Date(value) : value\n\n return {\n value: dateValue.toISOString(),\n offset,\n source,\n updatedAt: new Date().toISOString(),\n sourceEventId: sourceEventId ?? null,\n }\n}\n\n/**\n * Adds a timestamp entry to an array with time-based deduplication.\n * Deduplication: if an entry with the same value+source exists, it won't be added.\n * Multiple entries per source with different values ARE allowed (SCD pattern).\n *\n * @param entries Existing entries array (will be mutated) or null\n * @param newEntry Entry to add\n * @returns The updated array (creates new array if input was null)\n */\nexport function addTimestampEntry(\n entries: ShipmentTimestampEntry[] | null | undefined,\n newEntry: ShipmentTimestampEntry,\n): ShipmentTimestampEntry[] {\n const result = entries ? [...entries] : []\n\n // Dedupe: check if exact same value+source combo already exists\n const isDuplicate = result.some(\n (e) => e.value === newEntry.value && e.source === newEntry.source,\n )\n\n if (!isDuplicate) {\n result.push(newEntry)\n }\n\n return result\n}\n\n/**\n * Merges extracted times into existing timestamp arrays.\n * Used when processing tracking events to update shipment timestamps.\n *\n * @param current Current timestamp arrays from shipment\n * @param extracted Newly extracted timestamps from events\n * @param source Source of the extracted timestamps\n * @param sourceEventId Optional source event ID\n * @returns Updated timestamp arrays\n */\nexport function mergeExtractedTimestamps(\n current: {\n etdTimestamps?: ShipmentTimestampEntry[] | null\n etaTimestamps?: ShipmentTimestampEntry[] | null\n atdTimestamps?: ShipmentTimestampEntry[] | null\n ataTimestamps?: ShipmentTimestampEntry[] | null\n },\n extracted: {\n etd?: Date | null\n etdOffset?: string | null\n eta?: Date | null\n etaOffset?: string | null\n atd?: Date | null\n atdOffset?: string | null\n ata?: Date | null\n ataOffset?: string | null\n },\n source: TimestampSource,\n sourceEventId?: string | null,\n): {\n etdTimestamps: ShipmentTimestampEntry[] | null\n etaTimestamps: ShipmentTimestampEntry[] | null\n atdTimestamps: ShipmentTimestampEntry[] | null\n ataTimestamps: ShipmentTimestampEntry[] | null\n changed: boolean\n} {\n let changed = false\n let etdTimestamps = current.etdTimestamps ?? null\n let etaTimestamps = current.etaTimestamps ?? null\n let atdTimestamps = current.atdTimestamps ?? null\n let ataTimestamps = current.ataTimestamps ?? null\n\n if (extracted.etd) {\n const entry = createTimestampEntry(extracted.etd, extracted.etdOffset ?? null, source, sourceEventId)\n const before = etdTimestamps?.length ?? 0\n etdTimestamps = addTimestampEntry(etdTimestamps, entry)\n if ((etdTimestamps?.length ?? 0) > before) changed = true\n }\n\n if (extracted.eta) {\n const entry = createTimestampEntry(extracted.eta, extracted.etaOffset ?? null, source, sourceEventId)\n const before = etaTimestamps?.length ?? 0\n etaTimestamps = addTimestampEntry(etaTimestamps, entry)\n if ((etaTimestamps?.length ?? 0) > before) changed = true\n }\n\n if (extracted.atd) {\n const entry = createTimestampEntry(extracted.atd, extracted.atdOffset ?? null, source, sourceEventId)\n const before = atdTimestamps?.length ?? 0\n atdTimestamps = addTimestampEntry(atdTimestamps, entry)\n if ((atdTimestamps?.length ?? 0) > before) changed = true\n }\n\n if (extracted.ata) {\n const entry = createTimestampEntry(extracted.ata, extracted.ataOffset ?? null, source, sourceEventId)\n const before = ataTimestamps?.length ?? 0\n ataTimestamps = addTimestampEntry(ataTimestamps, entry)\n if ((ataTimestamps?.length ?? 0) > before) changed = true\n }\n\n return {\n etdTimestamps,\n etaTimestamps,\n atdTimestamps,\n ataTimestamps,\n changed,\n }\n}\n\n/**\n * Checks if a timestamp array has any entries.\n */\nexport function hasTimestamps(entries: ShipmentTimestampEntry[] | null | undefined): boolean {\n return !!entries && entries.length > 0\n}\n\n/**\n * Gets all timestamps from a specific source.\n */\nexport function getTimestampsBySource(\n entries: ShipmentTimestampEntry[] | null | undefined,\n source: TimestampSource,\n): ShipmentTimestampEntry[] {\n if (!entries) return []\n return entries.filter((e) => e.source === source)\n}\n\n/**\n * Gets the history of timestamp changes, sorted by updatedAt (oldest first).\n */\nexport function getTimestampHistory(\n entries: ShipmentTimestampEntry[] | null | undefined,\n): ShipmentTimestampEntry[] {\n if (!entries) return []\n return [...entries].sort((a, b) => a.updatedAt.localeCompare(b.updatedAt))\n}\n"],
|
|
5
|
+
"mappings": "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",
|
|
6
|
+
"names": []
|
|
7
|
+
}
|
|
@@ -0,0 +1,111 @@
|
|
|
1
|
+
function vesselApiBaseUrl() {
|
|
2
|
+
const baseUrl = process.env.NEXT_PUBLIC_VESSEL_API_URL || "";
|
|
3
|
+
if (!baseUrl) {
|
|
4
|
+
throw new Error(
|
|
5
|
+
"Vessel API is not configured \u2014 set NEXT_PUBLIC_VESSEL_API_URL to your vessel-tracking API base URL"
|
|
6
|
+
);
|
|
7
|
+
}
|
|
8
|
+
return baseUrl.replace(/\/+$/, "");
|
|
9
|
+
}
|
|
10
|
+
async function fetchVessel(imoOrMmsi) {
|
|
11
|
+
const id = typeof imoOrMmsi === "string" ? imoOrMmsi : String(imoOrMmsi);
|
|
12
|
+
try {
|
|
13
|
+
const response = await fetch(`${vesselApiBaseUrl()}/vessel/${encodeURIComponent(id)}`);
|
|
14
|
+
if (response.status === 404) {
|
|
15
|
+
return null;
|
|
16
|
+
}
|
|
17
|
+
if (!response.ok) {
|
|
18
|
+
const error = await response.json().catch(() => ({ error: "Unknown error" }));
|
|
19
|
+
throw new Error(error.error || `HTTP ${response.status}`);
|
|
20
|
+
}
|
|
21
|
+
const data = await response.json();
|
|
22
|
+
return data.vessel;
|
|
23
|
+
} catch (error) {
|
|
24
|
+
console.error("[vessel-api] Failed to fetch vessel:", error);
|
|
25
|
+
throw error;
|
|
26
|
+
}
|
|
27
|
+
}
|
|
28
|
+
async function fetchVesselTrace(imoOrMmsi, bounds, limit, timeRange) {
|
|
29
|
+
const id = typeof imoOrMmsi === "string" ? imoOrMmsi : String(imoOrMmsi);
|
|
30
|
+
const params = new URLSearchParams();
|
|
31
|
+
if (bounds) {
|
|
32
|
+
params.set("north", String(bounds.north));
|
|
33
|
+
params.set("south", String(bounds.south));
|
|
34
|
+
params.set("east", String(bounds.east));
|
|
35
|
+
params.set("west", String(bounds.west));
|
|
36
|
+
}
|
|
37
|
+
if (limit) {
|
|
38
|
+
params.set("limit", String(limit));
|
|
39
|
+
}
|
|
40
|
+
if (timeRange?.from) {
|
|
41
|
+
params.set("from", timeRange.from);
|
|
42
|
+
}
|
|
43
|
+
if (timeRange?.to) {
|
|
44
|
+
params.set("to", timeRange.to);
|
|
45
|
+
}
|
|
46
|
+
const queryString = params.toString();
|
|
47
|
+
const url = `${vesselApiBaseUrl()}/vessel/${encodeURIComponent(id)}/trace${queryString ? `?${queryString}` : ""}`;
|
|
48
|
+
try {
|
|
49
|
+
const response = await fetch(url);
|
|
50
|
+
if (response.status === 404) {
|
|
51
|
+
return {
|
|
52
|
+
vessel: { imo: 0, mmsi: null, name: "Unknown" },
|
|
53
|
+
trace: [],
|
|
54
|
+
count: 0,
|
|
55
|
+
limit: limit || 5e3
|
|
56
|
+
};
|
|
57
|
+
}
|
|
58
|
+
if (!response.ok) {
|
|
59
|
+
const error = await response.json().catch(() => ({ error: "Unknown error" }));
|
|
60
|
+
throw new Error(error.error || `HTTP ${response.status}`);
|
|
61
|
+
}
|
|
62
|
+
const data = await response.json();
|
|
63
|
+
return data;
|
|
64
|
+
} catch (error) {
|
|
65
|
+
console.error("[vessel-api] Failed to fetch vessel trace:", error);
|
|
66
|
+
throw error;
|
|
67
|
+
}
|
|
68
|
+
}
|
|
69
|
+
async function fetchPois(bounds, options) {
|
|
70
|
+
const params = new URLSearchParams();
|
|
71
|
+
if (bounds) {
|
|
72
|
+
params.set("north", String(bounds.north));
|
|
73
|
+
params.set("south", String(bounds.south));
|
|
74
|
+
params.set("east", String(bounds.east));
|
|
75
|
+
params.set("west", String(bounds.west));
|
|
76
|
+
}
|
|
77
|
+
if (options?.type) {
|
|
78
|
+
params.set("type", options.type);
|
|
79
|
+
}
|
|
80
|
+
if (options?.limit) {
|
|
81
|
+
params.set("limit", String(options.limit));
|
|
82
|
+
}
|
|
83
|
+
const queryString = params.toString();
|
|
84
|
+
const url = `${vesselApiBaseUrl()}/poi${queryString ? `?${queryString}` : ""}`;
|
|
85
|
+
try {
|
|
86
|
+
const response = await fetch(url);
|
|
87
|
+
if (response.status === 404) {
|
|
88
|
+
return [];
|
|
89
|
+
}
|
|
90
|
+
if (!response.ok) {
|
|
91
|
+
const error = await response.json().catch(() => ({ error: "Unknown error" }));
|
|
92
|
+
throw new Error(error.error || `HTTP ${response.status}`);
|
|
93
|
+
}
|
|
94
|
+
const data = await response.json();
|
|
95
|
+
const seen = /* @__PURE__ */ new Set();
|
|
96
|
+
return (data.pois ?? []).filter((poi) => {
|
|
97
|
+
if (seen.has(poi.code)) return false;
|
|
98
|
+
seen.add(poi.code);
|
|
99
|
+
return true;
|
|
100
|
+
});
|
|
101
|
+
} catch (error) {
|
|
102
|
+
console.error("[vessel-api] Failed to fetch POIs:", error);
|
|
103
|
+
throw error;
|
|
104
|
+
}
|
|
105
|
+
}
|
|
106
|
+
export {
|
|
107
|
+
fetchPois,
|
|
108
|
+
fetchVessel,
|
|
109
|
+
fetchVesselTrace
|
|
110
|
+
};
|
|
111
|
+
//# sourceMappingURL=vessel-api.js.map
|
|
@@ -0,0 +1,7 @@
|
|
|
1
|
+
{
|
|
2
|
+
"version": 3,
|
|
3
|
+
"sources": ["../../../../src/modules/shipment_tracking/lib/vessel-api.ts"],
|
|
4
|
+
"sourcesContent": ["/**\n * Vessel API client for fetching real-time vessel position and trace data.\n *\n * The base URL is deployment-specific and MUST be provided via the\n * `NEXT_PUBLIC_VESSEL_API_URL` environment variable. There is no default: when\n * it is unset the vessel/trace/POI calls throw a clear \"not configured\" error\n * and the map features that depend on them are simply unavailable.\n */\n\n// \u2500\u2500\u2500 Types \u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\n\nexport interface VesselDimensions {\n a: number\n b: number\n c: number\n d: number\n}\n\nexport interface PortInfo {\n locode: string\n name: string\n city: string\n country: string\n}\n\nexport interface Position {\n lat: number\n lng: number\n}\n\nexport type VesselStatus = 'AT_SEA' | 'IN_PORT'\nexport type PoiType = 'PORT' | 'TERMINAL' | 'WAYPOINT' | 'PORT_GEOMETRIC_CENTER'\n\nexport interface VesselInfo {\n imo: number\n mmsi: number | null\n name: string\n callsign: string | null\n shipType: string | null\n operatorName: string | null\n owner: string | null\n dimensions: VesselDimensions | null\n status: VesselStatus | null\n lastPosition: Position | null\n lastHeading: number | null\n lastDestination: string | null\n lastDestinationPort: PortInfo | null\n currentPort: PortInfo | null\n currentPoiCode: string | null\n currentPoiType: PoiType | null\n isStationary: boolean\n updatedAt: string\n}\n\nexport type TraceEventType =\n | 'POSITION_UPDATE'\n | 'PORT_ARRIVAL'\n | 'PORT_DEPARTURE'\n | 'TERMINAL_ARRIVAL'\n | 'TERMINAL_DEPARTURE'\n | 'DESTINATION_CHANGE'\n\nexport interface TracePoint {\n lat: number\n lng: number\n speed: number | null\n heading: number | null\n eventType: TraceEventType\n destination: string | null\n timestamp: string\n}\n\nexport interface BoundingBox {\n north: number\n south: number\n east: number\n west: number\n}\n\n/**\n * A point of interest (port, terminal, waypoint, or port geometric center) returned by the\n * vessel-api `/poi` endpoint, modeled as a circle (center + radius) ready to draw on a map.\n */\nexport interface PoiInfo {\n code: string\n locode: string\n type: PoiType\n center: Position\n radiusMeters: number\n portName: string | null\n terminalName: string | null\n city: string | null\n country: string | null\n /** Maritime region name in Polish (waypoints; may fall back to English). */\n regionNamePl: string | null\n /** Maritime region name in English. */\n regionNameEn: string | null\n}\n\nexport interface PoiListResponse {\n pois: PoiInfo[]\n count: number\n limit: number\n /** Present only when a bounding-box filter was applied. */\n bounds?: BoundingBox\n /** Present only when a type filter was applied. */\n type?: PoiType\n}\n\nexport interface TimeRange {\n from?: string // ISO 8601 date\n to?: string // ISO 8601 date\n}\n\nexport interface VesselResponse {\n vessel: VesselInfo\n}\n\nexport interface TraceResponse {\n vessel: {\n imo: number\n mmsi: number | null\n name: string\n }\n trace: TracePoint[]\n count: number\n limit: number\n bounds?: BoundingBox\n timeRange?: TimeRange\n}\n\n// \u2500\u2500\u2500 Configuration \u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\n\nfunction vesselApiBaseUrl(): string {\n const baseUrl = process.env.NEXT_PUBLIC_VESSEL_API_URL || ''\n if (!baseUrl) {\n throw new Error(\n 'Vessel API is not configured \u2014 set NEXT_PUBLIC_VESSEL_API_URL to your vessel-tracking API base URL'\n )\n }\n return baseUrl.replace(/\\/+$/, '')\n}\n\n// \u2500\u2500\u2500 API Functions \u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\n\n/**\n * Fetch vessel information by IMO number or MMSI.\n *\n * @param imoOrMmsi - IMO number (7 digits) or MMSI (9 digits)\n * @returns Vessel info or null if not found\n */\nexport async function fetchVessel(imoOrMmsi: number | string): Promise<VesselInfo | null> {\n const id = typeof imoOrMmsi === 'string' ? imoOrMmsi : String(imoOrMmsi)\n\n try {\n const response = await fetch(`${vesselApiBaseUrl()}/vessel/${encodeURIComponent(id)}`)\n\n if (response.status === 404) {\n return null\n }\n\n if (!response.ok) {\n const error = await response.json().catch(() => ({ error: 'Unknown error' }))\n throw new Error(error.error || `HTTP ${response.status}`)\n }\n\n const data: VesselResponse = await response.json()\n return data.vessel\n } catch (error) {\n console.error('[vessel-api] Failed to fetch vessel:', error)\n throw error\n }\n}\n\n/**\n * Fetch vessel trace (position history) with optional bounding box and time filters.\n *\n * @param imoOrMmsi - IMO number or MMSI\n * @param bounds - Optional bounding box to filter trace points\n * @param limit - Maximum number of points to return (default: 5000)\n * @param timeRange - Optional time range filter (from/to in ISO 8601)\n * @returns Trace data with array of position points\n */\nexport async function fetchVesselTrace(\n imoOrMmsi: number | string,\n bounds?: BoundingBox,\n limit?: number,\n timeRange?: TimeRange\n): Promise<TraceResponse> {\n const id = typeof imoOrMmsi === 'string' ? imoOrMmsi : String(imoOrMmsi)\n\n const params = new URLSearchParams()\n\n if (bounds) {\n params.set('north', String(bounds.north))\n params.set('south', String(bounds.south))\n params.set('east', String(bounds.east))\n params.set('west', String(bounds.west))\n }\n\n if (limit) {\n params.set('limit', String(limit))\n }\n\n if (timeRange?.from) {\n params.set('from', timeRange.from)\n }\n\n if (timeRange?.to) {\n params.set('to', timeRange.to)\n }\n\n const queryString = params.toString()\n const url = `${vesselApiBaseUrl()}/vessel/${encodeURIComponent(id)}/trace${queryString ? `?${queryString}` : ''}`\n\n try {\n const response = await fetch(url)\n\n if (response.status === 404) {\n return {\n vessel: { imo: 0, mmsi: null, name: 'Unknown' },\n trace: [],\n count: 0,\n limit: limit || 5000,\n }\n }\n\n if (!response.ok) {\n const error = await response.json().catch(() => ({ error: 'Unknown error' }))\n throw new Error(error.error || `HTTP ${response.status}`)\n }\n\n const data: TraceResponse = await response.json()\n return data\n } catch (error) {\n console.error('[vessel-api] Failed to fetch vessel trace:', error)\n throw error\n }\n}\n\n/**\n * Fetch points of interest (ports, terminals, waypoints) as map circles, optionally\n * constrained to a viewport bounding box and/or a single POI type.\n *\n * @param bounds - Optional bounding box to fetch only POIs in the current viewport\n * @param options - Optional `type` filter and `limit` (API default/cap is 5000)\n * @returns Array of POIs (empty on 404)\n */\nexport async function fetchPois(\n bounds?: BoundingBox,\n options?: { type?: PoiType; limit?: number }\n): Promise<PoiInfo[]> {\n const params = new URLSearchParams()\n\n if (bounds) {\n params.set('north', String(bounds.north))\n params.set('south', String(bounds.south))\n params.set('east', String(bounds.east))\n params.set('west', String(bounds.west))\n }\n\n if (options?.type) {\n params.set('type', options.type)\n }\n\n if (options?.limit) {\n params.set('limit', String(options.limit))\n }\n\n const queryString = params.toString()\n const url = `${vesselApiBaseUrl()}/poi${queryString ? `?${queryString}` : ''}`\n\n try {\n const response = await fetch(url)\n\n if (response.status === 404) {\n return []\n }\n\n if (!response.ok) {\n const error = await response.json().catch(() => ({ error: 'Unknown error' }))\n throw new Error(error.error || `HTTP ${response.status}`)\n }\n\n const data: PoiListResponse = await response.json()\n // The API contract says `code` is unique, but the live endpoint can return the same code\n // more than once. Dedupe by code so downstream React keys stay unique and we don't draw\n // overlapping duplicate circles.\n const seen = new Set<string>()\n return (data.pois ?? []).filter((poi) => {\n if (seen.has(poi.code)) return false\n seen.add(poi.code)\n return true\n })\n } catch (error) {\n console.error('[vessel-api] Failed to fetch POIs:', error)\n throw error\n }\n}\n"],
|
|
5
|
+
"mappings": "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",
|
|
6
|
+
"names": []
|
|
7
|
+
}
|
|
@@ -0,0 +1,50 @@
|
|
|
1
|
+
import { fetchVessel } from "./vessel-api.js";
|
|
2
|
+
const TTL_MS = 5 * 60 * 1e3;
|
|
3
|
+
const MAX_ENTRIES = 500;
|
|
4
|
+
const DEFAULT_CONCURRENCY = 8;
|
|
5
|
+
const cache = /* @__PURE__ */ new Map();
|
|
6
|
+
function evictIfNeeded(now) {
|
|
7
|
+
for (const [key, entry] of cache) {
|
|
8
|
+
if (entry.exp <= now) cache.delete(key);
|
|
9
|
+
}
|
|
10
|
+
while (cache.size >= MAX_ENTRIES) {
|
|
11
|
+
const oldest = cache.keys().next().value;
|
|
12
|
+
if (oldest === void 0) break;
|
|
13
|
+
cache.delete(oldest);
|
|
14
|
+
}
|
|
15
|
+
}
|
|
16
|
+
async function getVesselCached(imo) {
|
|
17
|
+
const now = Date.now();
|
|
18
|
+
const hit = cache.get(imo);
|
|
19
|
+
if (hit && hit.exp > now) return hit.v;
|
|
20
|
+
const vessel = await fetchVessel(imo);
|
|
21
|
+
evictIfNeeded(Date.now());
|
|
22
|
+
cache.set(imo, { v: vessel, exp: Date.now() + TTL_MS });
|
|
23
|
+
return vessel;
|
|
24
|
+
}
|
|
25
|
+
function __clearVesselCache() {
|
|
26
|
+
cache.clear();
|
|
27
|
+
}
|
|
28
|
+
async function mapWithConcurrency(items, task, concurrency = DEFAULT_CONCURRENCY) {
|
|
29
|
+
const results = new Array(items.length);
|
|
30
|
+
let cursor = 0;
|
|
31
|
+
async function worker() {
|
|
32
|
+
while (cursor < items.length) {
|
|
33
|
+
const index = cursor++;
|
|
34
|
+
try {
|
|
35
|
+
results[index] = { status: "fulfilled", value: await task(items[index]) };
|
|
36
|
+
} catch (reason) {
|
|
37
|
+
results[index] = { status: "rejected", reason };
|
|
38
|
+
}
|
|
39
|
+
}
|
|
40
|
+
}
|
|
41
|
+
const workers = Array.from({ length: Math.min(concurrency, items.length) }, () => worker());
|
|
42
|
+
await Promise.all(workers);
|
|
43
|
+
return results;
|
|
44
|
+
}
|
|
45
|
+
export {
|
|
46
|
+
__clearVesselCache,
|
|
47
|
+
getVesselCached,
|
|
48
|
+
mapWithConcurrency
|
|
49
|
+
};
|
|
50
|
+
//# sourceMappingURL=vessel-cache.js.map
|
|
@@ -0,0 +1,7 @@
|
|
|
1
|
+
{
|
|
2
|
+
"version": 3,
|
|
3
|
+
"sources": ["../../../../src/modules/shipment_tracking/lib/vessel-cache.ts"],
|
|
4
|
+
"sourcesContent": ["/**\n * Process-global, bounded, short-TTL cache for public AIS vessel data, plus a small\n * concurrency limiter for fanning out `fetchVessel` calls.\n *\n * Why this exists: the ships-map dashboard widget resolves many shipments to a handful\n * of distinct vessels (a vessel carries several of our containers), then fetches each\n * vessel's live position from the external `vessel-api`. That service has no batch\n * endpoint, so we make one HTTP call per distinct IMO. To keep latency and upstream\n * quota in check we:\n * - cache the public `VesselInfo` keyed by IMO (TTL ~5 min),\n * - bound the cache so it can't grow over the process lifetime,\n * - cap concurrency on the fan-out.\n *\n * The cache stores ONLY public maritime data (`VesselInfo` keyed by IMO) \u2014 never\n * tenant-scoped rows \u2014 so it is safe to share across tenants. The tenant-scoped\n * `Shipment` query and the shipment\u2192vessel grouping are computed per request and\n * never cached here.\n */\n\nimport { fetchVessel, type VesselInfo } from './vessel-api'\n\n// \u2500\u2500\u2500 Configuration \u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\n\nconst TTL_MS = 5 * 60 * 1000 // 5 minutes\nconst MAX_ENTRIES = 500 // hard cap so the cache can't grow unbounded over process life\nconst DEFAULT_CONCURRENCY = 8\n\ntype CacheEntry = { v: VesselInfo | null; exp: number }\n\n// Module-level (process-global) cache. Keyed by IMO string.\nconst cache = new Map<string, CacheEntry>()\n\n// \u2500\u2500\u2500 Cache \u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\n\n/**\n * Evict expired entries; if still at capacity, drop the oldest insertion (Map preserves\n * insertion order) so the cache stays bounded.\n */\nfunction evictIfNeeded(now: number): void {\n for (const [key, entry] of cache) {\n if (entry.exp <= now) cache.delete(key)\n }\n while (cache.size >= MAX_ENTRIES) {\n const oldest = cache.keys().next().value\n if (oldest === undefined) break\n cache.delete(oldest)\n }\n}\n\n/**\n * Returns the cached `VesselInfo` for an IMO, fetching from `vessel-api` on a miss.\n *\n * A fetch failure is NOT cached \u2014 it propagates to the caller so the request handler can\n * fold the IMO into `unresolvedCount` (via `Promise.allSettled`). A successful fetch that\n * yields `null` (404 / unknown vessel) IS cached to avoid re-hitting a known-miss.\n */\nexport async function getVesselCached(imo: string): Promise<VesselInfo | null> {\n const now = Date.now()\n const hit = cache.get(imo)\n if (hit && hit.exp > now) return hit.v\n\n const vessel = await fetchVessel(imo)\n evictIfNeeded(Date.now())\n cache.set(imo, { v: vessel, exp: Date.now() + TTL_MS })\n return vessel\n}\n\n/** Test-only: clear the process-global cache. */\nexport function __clearVesselCache(): void {\n cache.clear()\n}\n\n// \u2500\u2500\u2500 Concurrency-capped fan-out \u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\n\n/**\n * Run `task` over `items` with at most `concurrency` in flight, returning results in the\n * same order as `items`. Mirrors `Promise.allSettled` semantics: never rejects \u2014 each slot\n * resolves to `{ status: 'fulfilled', value }` or `{ status: 'rejected', reason }`.\n */\nexport async function mapWithConcurrency<T, R>(\n items: readonly T[],\n task: (item: T) => Promise<R>,\n concurrency: number = DEFAULT_CONCURRENCY,\n): Promise<PromiseSettledResult<R>[]> {\n const results: PromiseSettledResult<R>[] = new Array(items.length)\n let cursor = 0\n\n async function worker(): Promise<void> {\n while (cursor < items.length) {\n const index = cursor++\n try {\n results[index] = { status: 'fulfilled', value: await task(items[index]) }\n } catch (reason) {\n results[index] = { status: 'rejected', reason }\n }\n }\n }\n\n const workers = Array.from({ length: Math.min(concurrency, items.length) }, () => worker())\n await Promise.all(workers)\n return results\n}\n"],
|
|
5
|
+
"mappings": "AAmBA,SAAS,mBAAoC;AAI7C,MAAM,SAAS,IAAI,KAAK;AACxB,MAAM,cAAc;AACpB,MAAM,sBAAsB;AAK5B,MAAM,QAAQ,oBAAI,IAAwB;AAQ1C,SAAS,cAAc,KAAmB;AACxC,aAAW,CAAC,KAAK,KAAK,KAAK,OAAO;AAChC,QAAI,MAAM,OAAO,IAAK,OAAM,OAAO,GAAG;AAAA,EACxC;AACA,SAAO,MAAM,QAAQ,aAAa;AAChC,UAAM,SAAS,MAAM,KAAK,EAAE,KAAK,EAAE;AACnC,QAAI,WAAW,OAAW;AAC1B,UAAM,OAAO,MAAM;AAAA,EACrB;AACF;AASA,eAAsB,gBAAgB,KAAyC;AAC7E,QAAM,MAAM,KAAK,IAAI;AACrB,QAAM,MAAM,MAAM,IAAI,GAAG;AACzB,MAAI,OAAO,IAAI,MAAM,IAAK,QAAO,IAAI;AAErC,QAAM,SAAS,MAAM,YAAY,GAAG;AACpC,gBAAc,KAAK,IAAI,CAAC;AACxB,QAAM,IAAI,KAAK,EAAE,GAAG,QAAQ,KAAK,KAAK,IAAI,IAAI,OAAO,CAAC;AACtD,SAAO;AACT;AAGO,SAAS,qBAA2B;AACzC,QAAM,MAAM;AACd;AASA,eAAsB,mBACpB,OACA,MACA,cAAsB,qBACc;AACpC,QAAM,UAAqC,IAAI,MAAM,MAAM,MAAM;AACjE,MAAI,SAAS;AAEb,iBAAe,SAAwB;AACrC,WAAO,SAAS,MAAM,QAAQ;AAC5B,YAAM,QAAQ;AACd,UAAI;AACF,gBAAQ,KAAK,IAAI,EAAE,QAAQ,aAAa,OAAO,MAAM,KAAK,MAAM,KAAK,CAAC,EAAE;AAAA,MAC1E,SAAS,QAAQ;AACf,gBAAQ,KAAK,IAAI,EAAE,QAAQ,YAAY,OAAO;AAAA,MAChD;AAAA,IACF;AAAA,EACF;AAEA,QAAM,UAAU,MAAM,KAAK,EAAE,QAAQ,KAAK,IAAI,aAAa,MAAM,MAAM,EAAE,GAAG,MAAM,OAAO,CAAC;AAC1F,QAAM,QAAQ,IAAI,OAAO;AACzB,SAAO;AACT;",
|
|
6
|
+
"names": []
|
|
7
|
+
}
|
|
@@ -0,0 +1,13 @@
|
|
|
1
|
+
function findLatestVesselInfo(events) {
|
|
2
|
+
const eventWithVessel = [...events].reverse().find((e) => e.vesselName || e.vesselImo || e.voyageNumber);
|
|
3
|
+
if (!eventWithVessel) return null;
|
|
4
|
+
return {
|
|
5
|
+
vesselName: eventWithVessel.vesselName ?? null,
|
|
6
|
+
vesselImo: eventWithVessel.vesselImo ?? null,
|
|
7
|
+
voyageNumber: eventWithVessel.voyageNumber ?? null
|
|
8
|
+
};
|
|
9
|
+
}
|
|
10
|
+
export {
|
|
11
|
+
findLatestVesselInfo
|
|
12
|
+
};
|
|
13
|
+
//# sourceMappingURL=vessel-extraction.js.map
|
|
@@ -0,0 +1,7 @@
|
|
|
1
|
+
{
|
|
2
|
+
"version": 3,
|
|
3
|
+
"sources": ["../../../../src/modules/shipment_tracking/lib/vessel-extraction.ts"],
|
|
4
|
+
"sourcesContent": ["/**\n * Vessel info extraction utilities for shipment tracking.\n */\n\nexport type VesselInfo = {\n vesselName: string | null\n vesselImo: string | null\n voyageNumber: string | null\n}\n\ntype EventWithVesselFields = {\n vesselName?: string | null\n vesselImo?: string | null\n voyageNumber?: string | null\n}\n\n/**\n * Finds the latest vessel info from a list of tracking events.\n *\n * For completed voyages, the last event may be a gate operation (GTOT/GTIN)\n * that doesn't have vessel data. This function finds the latest event\n * that has any vessel info (vesselName, vesselImo, or voyageNumber).\n *\n * @param events - Array of tracking events sorted by eventDateTime\n * @returns VesselInfo from the latest event with vessel data, or null if none found\n */\nexport function findLatestVesselInfo(events: EventWithVesselFields[]): VesselInfo | null {\n const eventWithVessel = [...events]\n .reverse()\n .find((e) => e.vesselName || e.vesselImo || e.voyageNumber)\n\n if (!eventWithVessel) return null\n\n return {\n vesselName: eventWithVessel.vesselName ?? null,\n vesselImo: eventWithVessel.vesselImo ?? null,\n voyageNumber: eventWithVessel.voyageNumber ?? null,\n }\n}\n"],
|
|
5
|
+
"mappings": "AA0BO,SAAS,qBAAqB,QAAoD;AACvF,QAAM,kBAAkB,CAAC,GAAG,MAAM,EAC/B,QAAQ,EACR,KAAK,CAAC,MAAM,EAAE,cAAc,EAAE,aAAa,EAAE,YAAY;AAE5D,MAAI,CAAC,gBAAiB,QAAO;AAE7B,SAAO;AAAA,IACL,YAAY,gBAAgB,cAAc;AAAA,IAC1C,WAAW,gBAAgB,aAAa;AAAA,IACxC,cAAc,gBAAgB,gBAAgB;AAAA,EAChD;AACF;",
|
|
6
|
+
"names": []
|
|
7
|
+
}
|
|
@@ -0,0 +1,62 @@
|
|
|
1
|
+
import { createHmac } from "node:crypto";
|
|
2
|
+
function signPayload(payload, secret) {
|
|
3
|
+
return createHmac("sha256", secret).update(payload).digest("hex");
|
|
4
|
+
}
|
|
5
|
+
async function dispatchWebhook(input) {
|
|
6
|
+
const { url, payload, hmacSecret, timeoutMs = 1e4 } = input;
|
|
7
|
+
const body = JSON.stringify(payload);
|
|
8
|
+
const timestamp = Math.floor(Date.now() / 1e3).toString();
|
|
9
|
+
const headers = {
|
|
10
|
+
"Content-Type": "application/json",
|
|
11
|
+
"X-Webhook-Timestamp": timestamp
|
|
12
|
+
};
|
|
13
|
+
if (hmacSecret) {
|
|
14
|
+
const signaturePayload = `${timestamp}.${body}`;
|
|
15
|
+
headers["X-Webhook-Signature"] = signPayload(signaturePayload, hmacSecret);
|
|
16
|
+
}
|
|
17
|
+
const startTime = Date.now();
|
|
18
|
+
try {
|
|
19
|
+
const controller = new AbortController();
|
|
20
|
+
const timeout = setTimeout(() => controller.abort(), timeoutMs);
|
|
21
|
+
const response = await fetch(url, {
|
|
22
|
+
method: "POST",
|
|
23
|
+
headers,
|
|
24
|
+
body,
|
|
25
|
+
signal: controller.signal
|
|
26
|
+
});
|
|
27
|
+
clearTimeout(timeout);
|
|
28
|
+
const durationMs = Date.now() - startTime;
|
|
29
|
+
let responseBody;
|
|
30
|
+
try {
|
|
31
|
+
responseBody = await response.text();
|
|
32
|
+
} catch {
|
|
33
|
+
}
|
|
34
|
+
if (response.ok) {
|
|
35
|
+
return {
|
|
36
|
+
success: true,
|
|
37
|
+
responseStatus: response.status,
|
|
38
|
+
responseBody,
|
|
39
|
+
durationMs
|
|
40
|
+
};
|
|
41
|
+
}
|
|
42
|
+
return {
|
|
43
|
+
success: false,
|
|
44
|
+
responseStatus: response.status,
|
|
45
|
+
responseBody,
|
|
46
|
+
errorMessage: `HTTP ${response.status}: ${response.statusText}`,
|
|
47
|
+
durationMs
|
|
48
|
+
};
|
|
49
|
+
} catch (error) {
|
|
50
|
+
const durationMs = Date.now() - startTime;
|
|
51
|
+
const message = error instanceof Error ? error.message : "Unknown error";
|
|
52
|
+
return {
|
|
53
|
+
success: false,
|
|
54
|
+
errorMessage: message,
|
|
55
|
+
durationMs
|
|
56
|
+
};
|
|
57
|
+
}
|
|
58
|
+
}
|
|
59
|
+
export {
|
|
60
|
+
dispatchWebhook
|
|
61
|
+
};
|
|
62
|
+
//# sourceMappingURL=webhook-dispatcher.js.map
|
|
@@ -0,0 +1,7 @@
|
|
|
1
|
+
{
|
|
2
|
+
"version": 3,
|
|
3
|
+
"sources": ["../../../../src/modules/shipment_tracking/lib/webhook-dispatcher.ts"],
|
|
4
|
+
"sourcesContent": ["import { createHmac } from 'node:crypto'\n\nexport type WebhookDispatchResult = {\n success: boolean\n responseStatus?: number\n responseBody?: string\n errorMessage?: string\n durationMs: number\n}\n\nfunction signPayload(payload: string, secret: string): string {\n return createHmac('sha256', secret).update(payload).digest('hex')\n}\n\n/**\n * Dispatches a webhook HTTP POST with HMAC-SHA256 signing.\n *\n * Headers sent:\n * - Content-Type: application/json\n * - X-Webhook-Timestamp: Unix timestamp in seconds\n * - X-Webhook-Signature: HMAC-SHA256 of `{timestamp}.{body}` (only if hmacSecret provided)\n */\nexport async function dispatchWebhook(input: {\n url: string\n payload: Record<string, unknown>\n hmacSecret?: string | null\n timeoutMs?: number\n}): Promise<WebhookDispatchResult> {\n const { url, payload, hmacSecret, timeoutMs = 10000 } = input\n const body = JSON.stringify(payload)\n const timestamp = Math.floor(Date.now() / 1000).toString()\n\n const headers: Record<string, string> = {\n 'Content-Type': 'application/json',\n 'X-Webhook-Timestamp': timestamp,\n }\n\n if (hmacSecret) {\n const signaturePayload = `${timestamp}.${body}`\n headers['X-Webhook-Signature'] = signPayload(signaturePayload, hmacSecret)\n }\n\n const startTime = Date.now()\n\n try {\n const controller = new AbortController()\n const timeout = setTimeout(() => controller.abort(), timeoutMs)\n\n const response = await fetch(url, {\n method: 'POST',\n headers,\n body,\n signal: controller.signal,\n })\n\n clearTimeout(timeout)\n\n const durationMs = Date.now() - startTime\n let responseBody: string | undefined\n\n try {\n responseBody = await response.text()\n } catch {\n // Body read failure is non-critical\n }\n\n if (response.ok) {\n return {\n success: true,\n responseStatus: response.status,\n responseBody,\n durationMs,\n }\n }\n\n return {\n success: false,\n responseStatus: response.status,\n responseBody,\n errorMessage: `HTTP ${response.status}: ${response.statusText}`,\n durationMs,\n }\n } catch (error) {\n const durationMs = Date.now() - startTime\n const message = error instanceof Error ? error.message : 'Unknown error'\n\n return {\n success: false,\n errorMessage: message,\n durationMs,\n }\n }\n}\n"],
|
|
5
|
+
"mappings": "AAAA,SAAS,kBAAkB;AAU3B,SAAS,YAAY,SAAiB,QAAwB;AAC5D,SAAO,WAAW,UAAU,MAAM,EAAE,OAAO,OAAO,EAAE,OAAO,KAAK;AAClE;AAUA,eAAsB,gBAAgB,OAKH;AACjC,QAAM,EAAE,KAAK,SAAS,YAAY,YAAY,IAAM,IAAI;AACxD,QAAM,OAAO,KAAK,UAAU,OAAO;AACnC,QAAM,YAAY,KAAK,MAAM,KAAK,IAAI,IAAI,GAAI,EAAE,SAAS;AAEzD,QAAM,UAAkC;AAAA,IACtC,gBAAgB;AAAA,IAChB,uBAAuB;AAAA,EACzB;AAEA,MAAI,YAAY;AACd,UAAM,mBAAmB,GAAG,SAAS,IAAI,IAAI;AAC7C,YAAQ,qBAAqB,IAAI,YAAY,kBAAkB,UAAU;AAAA,EAC3E;AAEA,QAAM,YAAY,KAAK,IAAI;AAE3B,MAAI;AACF,UAAM,aAAa,IAAI,gBAAgB;AACvC,UAAM,UAAU,WAAW,MAAM,WAAW,MAAM,GAAG,SAAS;AAE9D,UAAM,WAAW,MAAM,MAAM,KAAK;AAAA,MAChC,QAAQ;AAAA,MACR;AAAA,MACA;AAAA,MACA,QAAQ,WAAW;AAAA,IACrB,CAAC;AAED,iBAAa,OAAO;AAEpB,UAAM,aAAa,KAAK,IAAI,IAAI;AAChC,QAAI;AAEJ,QAAI;AACF,qBAAe,MAAM,SAAS,KAAK;AAAA,IACrC,QAAQ;AAAA,IAER;AAEA,QAAI,SAAS,IAAI;AACf,aAAO;AAAA,QACL,SAAS;AAAA,QACT,gBAAgB,SAAS;AAAA,QACzB;AAAA,QACA;AAAA,MACF;AAAA,IACF;AAEA,WAAO;AAAA,MACL,SAAS;AAAA,MACT,gBAAgB,SAAS;AAAA,MACzB;AAAA,MACA,cAAc,QAAQ,SAAS,MAAM,KAAK,SAAS,UAAU;AAAA,MAC7D;AAAA,IACF;AAAA,EACF,SAAS,OAAO;AACd,UAAM,aAAa,KAAK,IAAI,IAAI;AAChC,UAAM,UAAU,iBAAiB,QAAQ,MAAM,UAAU;AAEzD,WAAO;AAAA,MACL,SAAS;AAAA,MACT,cAAc;AAAA,MACd;AAAA,IACF;AAAA,EACF;AACF;",
|
|
6
|
+
"names": []
|
|
7
|
+
}
|
package/dist/modules/shipment_tracking/migrations/Migration20260422161208_shipment_tracking.js
ADDED
|
@@ -0,0 +1,48 @@
|
|
|
1
|
+
import { Migration } from "@mikro-orm/migrations";
|
|
2
|
+
class Migration20260422161208_shipment_tracking extends Migration {
|
|
3
|
+
async up() {
|
|
4
|
+
this.addSql(`create table if not exists "shipment_tracking_bic_configs" ("id" uuid not null default gen_random_uuid(), "organization_id" uuid not null, "tenant_id" uuid not null, "is_enabled" boolean not null default false, "username" text not null, "password" text not null, "base_url" text not null default 'https://api.bic-code.org', "created_at" timestamptz not null default now(), "updated_at" timestamptz not null default now(), constraint "shipment_tracking_bic_configs_pkey" primary key ("id"));`);
|
|
5
|
+
this.addSql(`create index if not exists "st_bic_configs_org_tenant_idx" on "shipment_tracking_bic_configs" ("organization_id", "tenant_id");`);
|
|
6
|
+
this.addSql(`DO $$ BEGIN alter table "shipment_tracking_bic_configs" add constraint "st_bic_configs_scope_unique" unique ("organization_id", "tenant_id"); EXCEPTION WHEN duplicate_object THEN NULL; WHEN duplicate_table THEN NULL; END $$;`);
|
|
7
|
+
this.addSql(`create table if not exists "shipment_tracking_carrier_configs" ("id" uuid not null default gen_random_uuid(), "organization_id" uuid not null, "tenant_id" uuid not null, "carrier_code" text not null, "api_endpoint" text null, "auth_config" jsonb null, "rate_limit_requests" int not null default 60, "rate_limit_window_seconds" int not null default 60, "is_active" boolean not null default true, "created_at" timestamptz not null default now(), "updated_at" timestamptz not null default now(), "deleted_at" timestamptz null, constraint "shipment_tracking_carrier_configs_pkey" primary key ("id"));`);
|
|
8
|
+
this.addSql(`create index if not exists "st_carrier_configs_org_tenant_idx" on "shipment_tracking_carrier_configs" ("organization_id", "tenant_id");`);
|
|
9
|
+
this.addSql(`DO $$ BEGIN alter table "shipment_tracking_carrier_configs" add constraint "st_carrier_configs_code_uniq" unique ("organization_id", "tenant_id", "carrier_code"); EXCEPTION WHEN duplicate_object THEN NULL; WHEN duplicate_table THEN NULL; END $$;`);
|
|
10
|
+
this.addSql(`create table if not exists "shipment_tracking_location_overrides" ("id" uuid not null default gen_random_uuid(), "organization_id" uuid not null, "tenant_id" uuid not null, "carrier_code" text null, "unlocode" text not null, "facility_code" text not null, "facility_code_list_provider" text not null, "override_data" jsonb not null, "description" text null, "is_active" boolean not null default true, "created_at" timestamptz not null default now(), "updated_at" timestamptz not null default now(), "deleted_at" timestamptz null, "created_by_user_id" uuid null, "updated_by_user_id" uuid null, constraint "shipment_tracking_location_overrides_pkey" primary key ("id"));`);
|
|
11
|
+
this.addSql(`create index if not exists "st_location_overrides_lookup_idx" on "shipment_tracking_location_overrides" ("unlocode", "facility_code", "facility_code_list_provider");`);
|
|
12
|
+
this.addSql(`create index if not exists "st_location_overrides_org_tenant_idx" on "shipment_tracking_location_overrides" ("organization_id", "tenant_id");`);
|
|
13
|
+
this.addSql(`DO $$ BEGIN alter table "shipment_tracking_location_overrides" add constraint "st_location_overrides_match_uniq" unique ("organization_id", "tenant_id", "carrier_code", "unlocode", "facility_code", "facility_code_list_provider"); EXCEPTION WHEN duplicate_object THEN NULL; WHEN duplicate_table THEN NULL; END $$;`);
|
|
14
|
+
this.addSql(`create table if not exists "shipment_tracking_jobs" ("id" uuid not null default gen_random_uuid(), "organization_id" uuid not null, "tenant_id" uuid not null, "carrier_code" text not null, "reference_type" text not null, "reference_value" text not null, "origin_unlocode" text null, "destination_unlocode" text null, "status" text not null default 'active', "schedule" jsonb null, "next_poll_at" timestamptz null, "last_poll_at" timestamptz null, "retry_count" int not null default 0, "error_history" jsonb null, "created_at" timestamptz not null default now(), "updated_at" timestamptz not null default now(), "deleted_at" timestamptz null, constraint "shipment_tracking_jobs_pkey" primary key ("id"));`);
|
|
15
|
+
this.addSql(`create index if not exists "st_jobs_next_poll_idx" on "shipment_tracking_jobs" ("next_poll_at");`);
|
|
16
|
+
this.addSql(`create index if not exists "st_jobs_status_idx" on "shipment_tracking_jobs" ("status");`);
|
|
17
|
+
this.addSql(`create index if not exists "st_jobs_org_tenant_idx" on "shipment_tracking_jobs" ("organization_id", "tenant_id");`);
|
|
18
|
+
this.addSql(`create table if not exists "shipment_tracking_events" ("id" uuid not null default gen_random_uuid(), "organization_id" uuid not null, "tenant_id" uuid not null, "tracking_job_id" uuid not null, "source" text not null default 'dcsa', "source_event_id" text null, "event_type" text not null, "event_code" text not null, "event_classifier_code" text null, "event_date_time" timestamptz not null, "event_date_time_offset" text null, "description" text null, "raw_data" jsonb null, "equipment_reference" text null, "iso_equipment_code" text null, "empty_indicator_code" text null, "is_transshipment_move" boolean null, "location_name" text null, "location_unlocode" text null, "location_country" text null, "facility_code" text null, "facility_code_list_provider" text null, "facility_type_code" text null, "facility_address" text null, "latitude" real null, "longitude" real null, "transport_call_reference" text null, "mode_of_transport" text null, "vessel_name" text null, "vessel_imo" text null, "voyage_number" text null, "carrier_service_code" text null, "carrier_export_voyage_number" text null, "carrier_import_voyage_number" text null, "universal_service_reference" text null, "universal_export_voyage_reference" text null, "universal_import_voyage_reference" text null, "port_visit_reference" text null, "related_document_references" jsonb null, "event_created_date_time" timestamptz null, "retracted_event_id" text null, "publisher_name" text null, "publisher_role" text null, "delay_reason_code" text null, "change_remark" text null, "seals" jsonb null, "created_at" timestamptz not null default now(), constraint "shipment_tracking_events_pkey" primary key ("id"));`);
|
|
19
|
+
this.addSql(`create index if not exists "st_events_event_date_time_idx" on "shipment_tracking_events" ("event_date_time");`);
|
|
20
|
+
this.addSql(`create index if not exists "st_events_equipment_ref_idx" on "shipment_tracking_events" ("equipment_reference");`);
|
|
21
|
+
this.addSql(`create index if not exists "st_events_tracking_job_idx" on "shipment_tracking_events" ("tracking_job_id");`);
|
|
22
|
+
this.addSql(`create index if not exists "st_events_org_tenant_idx" on "shipment_tracking_events" ("organization_id", "tenant_id");`);
|
|
23
|
+
this.addSql(`DO $$ BEGIN alter table "shipment_tracking_events" add constraint "st_events_source_event_uniq" unique ("tracking_job_id", "source", "source_event_id"); EXCEPTION WHEN duplicate_object THEN NULL; WHEN duplicate_table THEN NULL; END $$;`);
|
|
24
|
+
this.addSql(`create table if not exists "shipment_tracking_shipments" ("id" uuid not null default gen_random_uuid(), "organization_id" uuid not null, "tenant_id" uuid not null, "status" text not null default 'PENDING', "carrier_code" text null, "container_number" text null, "iso_equipment_code" text null, "booking_number" text null, "bol_number" text null, "etd_timestamps" jsonb null, "eta_timestamps" jsonb null, "atd_timestamps" jsonb null, "ata_timestamps" jsonb null, "origin_location" jsonb null, "destination_location" jsonb null, "vessel_name" text null, "vessel_imo" text null, "voyage_number" text null, "event_count" int not null default 0, "last_event_at" timestamptz null, "extra" jsonb null, "route_stops" jsonb null, "cargo_events" jsonb null, "seals" jsonb null, "is_active" boolean not null default true, "created_at" timestamptz not null default now(), "updated_at" timestamptz not null default now(), "deleted_at" timestamptz null, "created_by_user_id" uuid null, "tracking_job_id" uuid null, constraint "shipment_tracking_shipments_pkey" primary key ("id"));`);
|
|
25
|
+
this.addSql(`create index if not exists "st_shipments_tracking_job_idx" on "shipment_tracking_shipments" ("tracking_job_id");`);
|
|
26
|
+
this.addSql(`create index if not exists "st_shipments_carrier_idx" on "shipment_tracking_shipments" ("carrier_code");`);
|
|
27
|
+
this.addSql(`create index if not exists "st_shipments_status_idx" on "shipment_tracking_shipments" ("status");`);
|
|
28
|
+
this.addSql(`create index if not exists "st_shipments_org_tenant_idx" on "shipment_tracking_shipments" ("organization_id", "tenant_id");`);
|
|
29
|
+
this.addSql(`create table if not exists "shipment_tracking_webhooks" ("id" uuid not null default gen_random_uuid(), "organization_id" uuid not null, "tenant_id" uuid not null, "url" text not null, "events_subscribed" jsonb not null, "hmac_secret" text null, "is_active" boolean not null default true, "created_at" timestamptz not null default now(), "updated_at" timestamptz not null default now(), "deleted_at" timestamptz null, constraint "shipment_tracking_webhooks_pkey" primary key ("id"));`);
|
|
30
|
+
this.addSql(`create index if not exists "st_webhooks_org_tenant_idx" on "shipment_tracking_webhooks" ("organization_id", "tenant_id");`);
|
|
31
|
+
this.addSql(`create table if not exists "shipment_tracking_webhook_deliveries" ("id" uuid not null default gen_random_uuid(), "webhook_id" uuid not null, "event_type" text not null, "status" text not null default 'pending', "retry_count" int not null default 0, "next_retry_at" timestamptz null, "payload" jsonb not null, "response_status" int null, "response_body" text null, "error_message" text null, "created_at" timestamptz not null default now(), constraint "shipment_tracking_webhook_deliveries_pkey" primary key ("id"));`);
|
|
32
|
+
this.addSql(`create index if not exists "st_webhook_deliveries_next_retry_idx" on "shipment_tracking_webhook_deliveries" ("next_retry_at");`);
|
|
33
|
+
this.addSql(`create index if not exists "st_webhook_deliveries_status_idx" on "shipment_tracking_webhook_deliveries" ("status");`);
|
|
34
|
+
this.addSql(`create index if not exists "st_webhook_deliveries_webhook_idx" on "shipment_tracking_webhook_deliveries" ("webhook_id");`);
|
|
35
|
+
this.addSql(`DO $$ BEGIN alter table "shipment_tracking_events" add constraint "shipment_tracking_events_tracking_job_id_foreign" foreign key ("tracking_job_id") references "shipment_tracking_jobs" ("id") on update cascade; EXCEPTION WHEN duplicate_object THEN NULL; WHEN duplicate_table THEN NULL; END $$;`);
|
|
36
|
+
this.addSql(`DO $$ BEGIN alter table "shipment_tracking_shipments" add constraint "shipment_tracking_shipments_tracking_job_id_foreign" foreign key ("tracking_job_id") references "shipment_tracking_jobs" ("id") on update cascade on delete set null; EXCEPTION WHEN duplicate_object THEN NULL; WHEN duplicate_table THEN NULL; END $$;`);
|
|
37
|
+
this.addSql(`DO $$ BEGIN alter table "shipment_tracking_webhook_deliveries" add constraint "shipment_tracking_webhook_deliveries_webhook_id_foreign" foreign key ("webhook_id") references "shipment_tracking_webhooks" ("id") on update cascade; EXCEPTION WHEN duplicate_object THEN NULL; WHEN duplicate_table THEN NULL; END $$;`);
|
|
38
|
+
}
|
|
39
|
+
async down() {
|
|
40
|
+
this.addSql(`alter table "shipment_tracking_events" drop constraint if exists "shipment_tracking_events_tracking_job_id_foreign";`);
|
|
41
|
+
this.addSql(`alter table "shipment_tracking_shipments" drop constraint if exists "shipment_tracking_shipments_tracking_job_id_foreign";`);
|
|
42
|
+
this.addSql(`alter table "shipment_tracking_webhook_deliveries" drop constraint if exists "shipment_tracking_webhook_deliveries_webhook_id_foreign";`);
|
|
43
|
+
}
|
|
44
|
+
}
|
|
45
|
+
export {
|
|
46
|
+
Migration20260422161208_shipment_tracking
|
|
47
|
+
};
|
|
48
|
+
//# sourceMappingURL=Migration20260422161208_shipment_tracking.js.map
|
package/dist/modules/shipment_tracking/migrations/Migration20260422161208_shipment_tracking.js.map
ADDED
|
@@ -0,0 +1,7 @@
|
|
|
1
|
+
{
|
|
2
|
+
"version": 3,
|
|
3
|
+
"sources": ["../../../../src/modules/shipment_tracking/migrations/Migration20260422161208_shipment_tracking.ts"],
|
|
4
|
+
"sourcesContent": ["import { Migration } from '@mikro-orm/migrations';\n\nexport class Migration20260422161208_shipment_tracking extends Migration {\n\n override async up(): Promise<void> {\n this.addSql(`create table if not exists \"shipment_tracking_bic_configs\" (\"id\" uuid not null default gen_random_uuid(), \"organization_id\" uuid not null, \"tenant_id\" uuid not null, \"is_enabled\" boolean not null default false, \"username\" text not null, \"password\" text not null, \"base_url\" text not null default 'https://api.bic-code.org', \"created_at\" timestamptz not null default now(), \"updated_at\" timestamptz not null default now(), constraint \"shipment_tracking_bic_configs_pkey\" primary key (\"id\"));`);\n this.addSql(`create index if not exists \"st_bic_configs_org_tenant_idx\" on \"shipment_tracking_bic_configs\" (\"organization_id\", \"tenant_id\");`);\n this.addSql(`DO $$ BEGIN alter table \"shipment_tracking_bic_configs\" add constraint \"st_bic_configs_scope_unique\" unique (\"organization_id\", \"tenant_id\"); EXCEPTION WHEN duplicate_object THEN NULL; WHEN duplicate_table THEN NULL; END $$;`);\n\n this.addSql(`create table if not exists \"shipment_tracking_carrier_configs\" (\"id\" uuid not null default gen_random_uuid(), \"organization_id\" uuid not null, \"tenant_id\" uuid not null, \"carrier_code\" text not null, \"api_endpoint\" text null, \"auth_config\" jsonb null, \"rate_limit_requests\" int not null default 60, \"rate_limit_window_seconds\" int not null default 60, \"is_active\" boolean not null default true, \"created_at\" timestamptz not null default now(), \"updated_at\" timestamptz not null default now(), \"deleted_at\" timestamptz null, constraint \"shipment_tracking_carrier_configs_pkey\" primary key (\"id\"));`);\n this.addSql(`create index if not exists \"st_carrier_configs_org_tenant_idx\" on \"shipment_tracking_carrier_configs\" (\"organization_id\", \"tenant_id\");`);\n this.addSql(`DO $$ BEGIN alter table \"shipment_tracking_carrier_configs\" add constraint \"st_carrier_configs_code_uniq\" unique (\"organization_id\", \"tenant_id\", \"carrier_code\"); EXCEPTION WHEN duplicate_object THEN NULL; WHEN duplicate_table THEN NULL; END $$;`);\n\n this.addSql(`create table if not exists \"shipment_tracking_location_overrides\" (\"id\" uuid not null default gen_random_uuid(), \"organization_id\" uuid not null, \"tenant_id\" uuid not null, \"carrier_code\" text null, \"unlocode\" text not null, \"facility_code\" text not null, \"facility_code_list_provider\" text not null, \"override_data\" jsonb not null, \"description\" text null, \"is_active\" boolean not null default true, \"created_at\" timestamptz not null default now(), \"updated_at\" timestamptz not null default now(), \"deleted_at\" timestamptz null, \"created_by_user_id\" uuid null, \"updated_by_user_id\" uuid null, constraint \"shipment_tracking_location_overrides_pkey\" primary key (\"id\"));`);\n this.addSql(`create index if not exists \"st_location_overrides_lookup_idx\" on \"shipment_tracking_location_overrides\" (\"unlocode\", \"facility_code\", \"facility_code_list_provider\");`);\n this.addSql(`create index if not exists \"st_location_overrides_org_tenant_idx\" on \"shipment_tracking_location_overrides\" (\"organization_id\", \"tenant_id\");`);\n this.addSql(`DO $$ BEGIN alter table \"shipment_tracking_location_overrides\" add constraint \"st_location_overrides_match_uniq\" unique (\"organization_id\", \"tenant_id\", \"carrier_code\", \"unlocode\", \"facility_code\", \"facility_code_list_provider\"); EXCEPTION WHEN duplicate_object THEN NULL; WHEN duplicate_table THEN NULL; END $$;`);\n\n this.addSql(`create table if not exists \"shipment_tracking_jobs\" (\"id\" uuid not null default gen_random_uuid(), \"organization_id\" uuid not null, \"tenant_id\" uuid not null, \"carrier_code\" text not null, \"reference_type\" text not null, \"reference_value\" text not null, \"origin_unlocode\" text null, \"destination_unlocode\" text null, \"status\" text not null default 'active', \"schedule\" jsonb null, \"next_poll_at\" timestamptz null, \"last_poll_at\" timestamptz null, \"retry_count\" int not null default 0, \"error_history\" jsonb null, \"created_at\" timestamptz not null default now(), \"updated_at\" timestamptz not null default now(), \"deleted_at\" timestamptz null, constraint \"shipment_tracking_jobs_pkey\" primary key (\"id\"));`);\n this.addSql(`create index if not exists \"st_jobs_next_poll_idx\" on \"shipment_tracking_jobs\" (\"next_poll_at\");`);\n this.addSql(`create index if not exists \"st_jobs_status_idx\" on \"shipment_tracking_jobs\" (\"status\");`);\n this.addSql(`create index if not exists \"st_jobs_org_tenant_idx\" on \"shipment_tracking_jobs\" (\"organization_id\", \"tenant_id\");`);\n\n this.addSql(`create table if not exists \"shipment_tracking_events\" (\"id\" uuid not null default gen_random_uuid(), \"organization_id\" uuid not null, \"tenant_id\" uuid not null, \"tracking_job_id\" uuid not null, \"source\" text not null default 'dcsa', \"source_event_id\" text null, \"event_type\" text not null, \"event_code\" text not null, \"event_classifier_code\" text null, \"event_date_time\" timestamptz not null, \"event_date_time_offset\" text null, \"description\" text null, \"raw_data\" jsonb null, \"equipment_reference\" text null, \"iso_equipment_code\" text null, \"empty_indicator_code\" text null, \"is_transshipment_move\" boolean null, \"location_name\" text null, \"location_unlocode\" text null, \"location_country\" text null, \"facility_code\" text null, \"facility_code_list_provider\" text null, \"facility_type_code\" text null, \"facility_address\" text null, \"latitude\" real null, \"longitude\" real null, \"transport_call_reference\" text null, \"mode_of_transport\" text null, \"vessel_name\" text null, \"vessel_imo\" text null, \"voyage_number\" text null, \"carrier_service_code\" text null, \"carrier_export_voyage_number\" text null, \"carrier_import_voyage_number\" text null, \"universal_service_reference\" text null, \"universal_export_voyage_reference\" text null, \"universal_import_voyage_reference\" text null, \"port_visit_reference\" text null, \"related_document_references\" jsonb null, \"event_created_date_time\" timestamptz null, \"retracted_event_id\" text null, \"publisher_name\" text null, \"publisher_role\" text null, \"delay_reason_code\" text null, \"change_remark\" text null, \"seals\" jsonb null, \"created_at\" timestamptz not null default now(), constraint \"shipment_tracking_events_pkey\" primary key (\"id\"));`);\n this.addSql(`create index if not exists \"st_events_event_date_time_idx\" on \"shipment_tracking_events\" (\"event_date_time\");`);\n this.addSql(`create index if not exists \"st_events_equipment_ref_idx\" on \"shipment_tracking_events\" (\"equipment_reference\");`);\n this.addSql(`create index if not exists \"st_events_tracking_job_idx\" on \"shipment_tracking_events\" (\"tracking_job_id\");`);\n this.addSql(`create index if not exists \"st_events_org_tenant_idx\" on \"shipment_tracking_events\" (\"organization_id\", \"tenant_id\");`);\n this.addSql(`DO $$ BEGIN alter table \"shipment_tracking_events\" add constraint \"st_events_source_event_uniq\" unique (\"tracking_job_id\", \"source\", \"source_event_id\"); EXCEPTION WHEN duplicate_object THEN NULL; WHEN duplicate_table THEN NULL; END $$;`);\n\n this.addSql(`create table if not exists \"shipment_tracking_shipments\" (\"id\" uuid not null default gen_random_uuid(), \"organization_id\" uuid not null, \"tenant_id\" uuid not null, \"status\" text not null default 'PENDING', \"carrier_code\" text null, \"container_number\" text null, \"iso_equipment_code\" text null, \"booking_number\" text null, \"bol_number\" text null, \"etd_timestamps\" jsonb null, \"eta_timestamps\" jsonb null, \"atd_timestamps\" jsonb null, \"ata_timestamps\" jsonb null, \"origin_location\" jsonb null, \"destination_location\" jsonb null, \"vessel_name\" text null, \"vessel_imo\" text null, \"voyage_number\" text null, \"event_count\" int not null default 0, \"last_event_at\" timestamptz null, \"extra\" jsonb null, \"route_stops\" jsonb null, \"cargo_events\" jsonb null, \"seals\" jsonb null, \"is_active\" boolean not null default true, \"created_at\" timestamptz not null default now(), \"updated_at\" timestamptz not null default now(), \"deleted_at\" timestamptz null, \"created_by_user_id\" uuid null, \"tracking_job_id\" uuid null, constraint \"shipment_tracking_shipments_pkey\" primary key (\"id\"));`);\n this.addSql(`create index if not exists \"st_shipments_tracking_job_idx\" on \"shipment_tracking_shipments\" (\"tracking_job_id\");`);\n this.addSql(`create index if not exists \"st_shipments_carrier_idx\" on \"shipment_tracking_shipments\" (\"carrier_code\");`);\n this.addSql(`create index if not exists \"st_shipments_status_idx\" on \"shipment_tracking_shipments\" (\"status\");`);\n this.addSql(`create index if not exists \"st_shipments_org_tenant_idx\" on \"shipment_tracking_shipments\" (\"organization_id\", \"tenant_id\");`);\n\n this.addSql(`create table if not exists \"shipment_tracking_webhooks\" (\"id\" uuid not null default gen_random_uuid(), \"organization_id\" uuid not null, \"tenant_id\" uuid not null, \"url\" text not null, \"events_subscribed\" jsonb not null, \"hmac_secret\" text null, \"is_active\" boolean not null default true, \"created_at\" timestamptz not null default now(), \"updated_at\" timestamptz not null default now(), \"deleted_at\" timestamptz null, constraint \"shipment_tracking_webhooks_pkey\" primary key (\"id\"));`);\n this.addSql(`create index if not exists \"st_webhooks_org_tenant_idx\" on \"shipment_tracking_webhooks\" (\"organization_id\", \"tenant_id\");`);\n\n this.addSql(`create table if not exists \"shipment_tracking_webhook_deliveries\" (\"id\" uuid not null default gen_random_uuid(), \"webhook_id\" uuid not null, \"event_type\" text not null, \"status\" text not null default 'pending', \"retry_count\" int not null default 0, \"next_retry_at\" timestamptz null, \"payload\" jsonb not null, \"response_status\" int null, \"response_body\" text null, \"error_message\" text null, \"created_at\" timestamptz not null default now(), constraint \"shipment_tracking_webhook_deliveries_pkey\" primary key (\"id\"));`);\n this.addSql(`create index if not exists \"st_webhook_deliveries_next_retry_idx\" on \"shipment_tracking_webhook_deliveries\" (\"next_retry_at\");`);\n this.addSql(`create index if not exists \"st_webhook_deliveries_status_idx\" on \"shipment_tracking_webhook_deliveries\" (\"status\");`);\n this.addSql(`create index if not exists \"st_webhook_deliveries_webhook_idx\" on \"shipment_tracking_webhook_deliveries\" (\"webhook_id\");`);\n\n this.addSql(`DO $$ BEGIN alter table \"shipment_tracking_events\" add constraint \"shipment_tracking_events_tracking_job_id_foreign\" foreign key (\"tracking_job_id\") references \"shipment_tracking_jobs\" (\"id\") on update cascade; EXCEPTION WHEN duplicate_object THEN NULL; WHEN duplicate_table THEN NULL; END $$;`);\n\n this.addSql(`DO $$ BEGIN alter table \"shipment_tracking_shipments\" add constraint \"shipment_tracking_shipments_tracking_job_id_foreign\" foreign key (\"tracking_job_id\") references \"shipment_tracking_jobs\" (\"id\") on update cascade on delete set null; EXCEPTION WHEN duplicate_object THEN NULL; WHEN duplicate_table THEN NULL; END $$;`);\n\n this.addSql(`DO $$ BEGIN alter table \"shipment_tracking_webhook_deliveries\" add constraint \"shipment_tracking_webhook_deliveries_webhook_id_foreign\" foreign key (\"webhook_id\") references \"shipment_tracking_webhooks\" (\"id\") on update cascade; EXCEPTION WHEN duplicate_object THEN NULL; WHEN duplicate_table THEN NULL; END $$;`);\n }\n\n override async down(): Promise<void> {\n this.addSql(`alter table \"shipment_tracking_events\" drop constraint if exists \"shipment_tracking_events_tracking_job_id_foreign\";`);\n\n this.addSql(`alter table \"shipment_tracking_shipments\" drop constraint if exists \"shipment_tracking_shipments_tracking_job_id_foreign\";`);\n\n this.addSql(`alter table \"shipment_tracking_webhook_deliveries\" drop constraint if exists \"shipment_tracking_webhook_deliveries_webhook_id_foreign\";`);\n }\n\n}\n"],
|
|
5
|
+
"mappings": "AAAA,SAAS,iBAAiB;AAEnB,MAAM,kDAAkD,UAAU;AAAA,EAEvE,MAAe,KAAoB;AACjC,SAAK,OAAO,4eAA4e;AACxf,SAAK,OAAO,iIAAiI;AAC7I,SAAK,OAAO,kOAAkO;AAE9O,SAAK,OAAO,slBAAslB;AAClmB,SAAK,OAAO,yIAAyI;AACrJ,SAAK,OAAO,uPAAuP;AAEnQ,SAAK,OAAO,+pBAA+pB;AAC3qB,SAAK,OAAO,uKAAuK;AACnL,SAAK,OAAO,+IAA+I;AAC3J,SAAK,OAAO,0TAA0T;AAEtU,SAAK,OAAO,isBAAisB;AAC7sB,SAAK,OAAO,kGAAkG;AAC9G,SAAK,OAAO,yFAAyF;AACrG,SAAK,OAAO,mHAAmH;AAE/H,SAAK,OAAO,2oDAA2oD;AACvpD,SAAK,OAAO,+GAA+G;AAC3H,SAAK,OAAO,iHAAiH;AAC7H,SAAK,OAAO,4GAA4G;AACxH,SAAK,OAAO,uHAAuH;AACnI,SAAK,OAAO,6OAA6O;AAEzP,SAAK,OAAO,6iCAA6iC;AACzjC,SAAK,OAAO,kHAAkH;AAC9H,SAAK,OAAO,0GAA0G;AACtH,SAAK,OAAO,mGAAmG;AAC/G,SAAK,OAAO,6HAA6H;AAEzI,SAAK,OAAO,oeAAoe;AAChf,SAAK,OAAO,2HAA2H;AAEvI,SAAK,OAAO,qgBAAqgB;AACjhB,SAAK,OAAO,gIAAgI;AAC5I,SAAK,OAAO,qHAAqH;AACjI,SAAK,OAAO,0HAA0H;AAEtI,SAAK,OAAO,uSAAuS;AAEnT,SAAK,OAAO,gUAAgU;AAE5U,SAAK,OAAO,yTAAyT;AAAA,EACvU;AAAA,EAEA,MAAe,OAAsB;AACnC,SAAK,OAAO,sHAAsH;AAElI,SAAK,OAAO,4HAA4H;AAExI,SAAK,OAAO,yIAAyI;AAAA,EACvJ;AAEF;",
|
|
6
|
+
"names": []
|
|
7
|
+
}
|
|
@@ -0,0 +1,15 @@
|
|
|
1
|
+
import { Migration } from "@mikro-orm/migrations";
|
|
2
|
+
class Migration20260529120000_shipment_tracking_poi_region_names extends Migration {
|
|
3
|
+
async up() {
|
|
4
|
+
this.addSql(`alter table "shipment_tracking_events" add column if not exists "region_name_pl" text null;`);
|
|
5
|
+
this.addSql(`alter table "shipment_tracking_events" add column if not exists "region_name_en" text null;`);
|
|
6
|
+
}
|
|
7
|
+
async down() {
|
|
8
|
+
this.addSql(`alter table "shipment_tracking_events" drop column if exists "region_name_pl";`);
|
|
9
|
+
this.addSql(`alter table "shipment_tracking_events" drop column if exists "region_name_en";`);
|
|
10
|
+
}
|
|
11
|
+
}
|
|
12
|
+
export {
|
|
13
|
+
Migration20260529120000_shipment_tracking_poi_region_names
|
|
14
|
+
};
|
|
15
|
+
//# sourceMappingURL=Migration20260529120000_shipment_tracking_poi_region_names.js.map
|
|
@@ -0,0 +1,7 @@
|
|
|
1
|
+
{
|
|
2
|
+
"version": 3,
|
|
3
|
+
"sources": ["../../../../src/modules/shipment_tracking/migrations/Migration20260529120000_shipment_tracking_poi_region_names.ts"],
|
|
4
|
+
"sourcesContent": ["import { Migration } from '@mikro-orm/migrations';\n\n/**\n * Additive: human-friendly region names for AIS POI events.\n *\n * The poi-proximity-detector now emits `regionNamePl` / `regionNameEn` alongside the raw\n * POI code so the Journey Timeline can show e.g. \"Morze P\u00F3\u0142nocne\" instead of \"N053W001-03738\".\n * Both columns are nullable (carrier/DCSA events and unnamed POIs leave them null).\n */\nexport class Migration20260529120000_shipment_tracking_poi_region_names extends Migration {\n\n override async up(): Promise<void> {\n this.addSql(`alter table \"shipment_tracking_events\" add column if not exists \"region_name_pl\" text null;`);\n this.addSql(`alter table \"shipment_tracking_events\" add column if not exists \"region_name_en\" text null;`);\n }\n\n override async down(): Promise<void> {\n this.addSql(`alter table \"shipment_tracking_events\" drop column if exists \"region_name_pl\";`);\n this.addSql(`alter table \"shipment_tracking_events\" drop column if exists \"region_name_en\";`);\n }\n\n}\n"],
|
|
5
|
+
"mappings": "AAAA,SAAS,iBAAiB;AASnB,MAAM,mEAAmE,UAAU;AAAA,EAExF,MAAe,KAAoB;AACjC,SAAK,OAAO,6FAA6F;AACzG,SAAK,OAAO,6FAA6F;AAAA,EAC3G;AAAA,EAEA,MAAe,OAAsB;AACnC,SAAK,OAAO,gFAAgF;AAC5F,SAAK,OAAO,gFAAgF;AAAA,EAC9F;AAEF;",
|
|
6
|
+
"names": []
|
|
7
|
+
}
|