@@ -25,14 +25,58 @@ type AisCache = {
2525 cachedAt : string ;
2626} ;
2727
28+ type RawAisMessage = {
29+ Message ?: {
30+ PositionReport ?: {
31+ Latitude ?: unknown ;
32+ Longitude ?: unknown ;
33+ UserID ?: unknown ;
34+ MMSI ?: unknown ;
35+ Sog ?: unknown ;
36+ Cog ?: unknown ;
37+ TrueHeading ?: unknown ;
38+ NavigationalStatus ?: unknown ;
39+ } ;
40+ } ;
41+ MetaData ?: {
42+ MMSI ?: unknown ;
43+ time_utc ?: string ;
44+ time ?: string ;
45+ ShipName ?: string ;
46+ CallSign ?: string ;
47+ } ;
48+ } ;
49+
50+ type WsLike = {
51+ on : ( event : string , listener : ( ...args : unknown [ ] ) => void ) => void ;
52+ send : ( data : string ) => void ;
53+ close : ( ) => void ;
54+ } ;
55+
56+ type WsConstructor = new (
57+ url : string ,
58+ options : { handshakeTimeout : number } ,
59+ ) => WsLike ;
60+
2861let cache : AisCache | null = null ;
2962
3063const REQUEST_TIMEOUT_MS = Number ( process . env . AISSTREAM_TIMEOUT_MS ?? 12000 ) ;
3164const MAX_MESSAGES = Number ( process . env . AISSTREAM_MAX_MESSAGES ?? 200 ) ;
3265
33- function normalizeAisMessage ( raw : any ) : AisVessel | null {
34- const position = raw ?. Message ?. PositionReport ;
35- const metadata = raw ?. MetaData ;
66+ function rawDataToString ( data : unknown ) : string {
67+ if ( typeof data === "string" ) return data ;
68+ if ( data instanceof Buffer ) return data . toString ( "utf8" ) ;
69+ if ( data instanceof ArrayBuffer ) return Buffer . from ( data ) . toString ( "utf8" ) ;
70+ if ( Array . isArray ( data ) && data . every ( ( item ) => item instanceof Buffer ) ) {
71+ return Buffer . concat ( data ) . toString ( "utf8" ) ;
72+ }
73+ return String ( data ) ;
74+ }
75+
76+ function normalizeAisMessage ( raw : unknown ) : AisVessel | null {
77+ const message = raw as RawAisMessage ;
78+ const position = message ?. Message ?. PositionReport ;
79+ const metadata = message ?. MetaData ;
3680 if ( ! position ) return null ;
3781
3882 const lat = Number ( position . Latitude ) ;
@@ -61,8 +105,8 @@ async function fetchSnapshotFromWs(apiKey: string): Promise<AisPayload> {
61105 process . env . WS_NO_BUFFER_UTIL = "1" ;
62106 process . env . WS_NO_UTF_8_VALIDATE = "1" ;
63107
64- const wsModule : any = await import ( "ws" ) ;
65- const WebSocket = wsModule . default ?? wsModule ;
108+ const wsModule = ( await import ( "ws" ) ) as { default ?: WsConstructor } ;
109+ const WebSocket = ( wsModule . default ?? ( wsModule as unknown as WsConstructor ) ) ;
66110
67111 return new Promise ( ( resolve , reject ) => {
68112 const vessels = new Map < number , AisVessel > ( ) ;
@@ -97,9 +141,9 @@ async function fetchSnapshotFromWs(apiKey: string): Promise<AisPayload> {
97141 ) ;
98142 } ) ;
99143
100- ws . on ( "message" , ( data : any ) => {
144+ ws . on ( "message" , ( data : unknown ) => {
101145 try {
102- const parsed = JSON . parse ( data . toString ( ) ) ;
146+ const parsed = JSON . parse ( rawDataToString ( data ) ) ;
103147 const vessel = normalizeAisMessage ( parsed ) ;
104148 if ( vessel ) vessels . set ( vessel . mmsi , vessel ) ;
105149
0 commit comments