@auxilium/datalynk-client 1.3.22 → 1.4.1
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/dist/api.d.ts +5 -0
- package/dist/api.d.ts.map +1 -1
- package/dist/gps.d.ts +156 -0
- package/dist/gps.d.ts.map +1 -0
- package/dist/index.cjs +399 -1
- package/dist/index.d.ts +1 -0
- package/dist/index.d.ts.map +1 -1
- package/dist/index.mjs +399 -1
- package/package.json +1 -1
package/dist/api.d.ts
CHANGED
|
@@ -9,6 +9,7 @@ import { ApiCall, Slice } from './slice';
|
|
|
9
9
|
import { Socket } from './socket';
|
|
10
10
|
import { Superuser } from './superuser';
|
|
11
11
|
import { WebRtc } from './webrtc';
|
|
12
|
+
import { Gps, GpsOptions } from './gps';
|
|
12
13
|
export type JwtPayload = {
|
|
13
14
|
aud: string;
|
|
14
15
|
email: string;
|
|
@@ -45,6 +46,8 @@ export type ApiOptions = {
|
|
|
45
46
|
serviceWorker?: string;
|
|
46
47
|
/** Disable sockets with false or override socket URL */
|
|
47
48
|
socket?: false | string;
|
|
49
|
+
/** GPS live tracking over socket-server/Redis */
|
|
50
|
+
gps?: GpsOptions;
|
|
48
51
|
/** WebRTC TURN server URL */
|
|
49
52
|
webrtc?: {
|
|
50
53
|
/** Additional ICE servers */
|
|
@@ -122,6 +125,8 @@ export declare class Api {
|
|
|
122
125
|
readonly pwa: PWA;
|
|
123
126
|
/** Socket */
|
|
124
127
|
readonly socket: Socket;
|
|
128
|
+
/** GPS */
|
|
129
|
+
readonly gps: Gps;
|
|
125
130
|
/** Superuser */
|
|
126
131
|
readonly superuser: Superuser;
|
|
127
132
|
/** WebRTC */
|
package/dist/api.d.ts.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"api.d.ts","sourceRoot":"","sources":["../src/api.ts"],"names":[],"mappings":"AAAA,OAAO,EAAoD,QAAQ,EAAQ,MAAM,gBAAgB,CAAC;AAClG,OAAO,EAAC,eAAe,EAAuB,MAAM,MAAM,CAAC;AAC3D,OAAO,EAAC,IAAI,EAAC,MAAM,QAAQ,CAAC;AAE5B,OAAO,EAAC,GAAG,EAAC,MAAM,OAAO,CAAC;AAC1B,OAAO,EAAC,KAAK,EAAC,MAAM,SAAS,CAAC;AAC9B,OAAO,EAAC,IAAI,EAAC,MAAM,QAAQ,CAAC;AAC5B,OAAO,EAAC,GAAG,EAAC,MAAM,OAAO,CAAC;AAE1B,OAAO,EAAC,OAAO,EAAE,KAAK,EAAC,MAAM,SAAS,CAAC;AACvC,OAAO,EAAC,MAAM,EAAC,MAAM,UAAU,CAAC;AAChC,OAAO,EAAC,SAAS,EAAC,MAAM,aAAa,CAAC;AAEtC,OAAO,EAAC,MAAM,EAAC,MAAM,UAAU,CAAC;
|
|
1
|
+
{"version":3,"file":"api.d.ts","sourceRoot":"","sources":["../src/api.ts"],"names":[],"mappings":"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"}
|
package/dist/gps.d.ts
ADDED
|
@@ -0,0 +1,156 @@
|
|
|
1
|
+
import { Api } from './api';
|
|
2
|
+
import { Unsubscribe } from './socket';
|
|
3
|
+
/** GPS connection options */
|
|
4
|
+
export type GpsOptions = false | {
|
|
5
|
+
/**
|
|
6
|
+
* Start browser GPS automatically after a token is available.
|
|
7
|
+
* Set false when an app already owns GPS collection and only wants api.gps.send(...).
|
|
8
|
+
*/
|
|
9
|
+
autoStart?: boolean;
|
|
10
|
+
/** Dedicated GPS socket URL override. Defaults to the standard socket-server URL */
|
|
11
|
+
socketUrl?: string;
|
|
12
|
+
/** Slice used by socket-server GPS storage */
|
|
13
|
+
slice: number | string;
|
|
14
|
+
/** Field key used by socket-server GPS storage. Default is coords */
|
|
15
|
+
field?: string;
|
|
16
|
+
/**
|
|
17
|
+
* Stable tracking ID for the object/person/vehicle being tracked.
|
|
18
|
+
*
|
|
19
|
+
* If omitted, datalynk-client generates a unique tracker ID for this Gps
|
|
20
|
+
* instance. The default tracking ID is intentionally not based on sessionId,
|
|
21
|
+
* because one logged-in session/device may start more than one active tracker.
|
|
22
|
+
*/
|
|
23
|
+
trackingId?: string;
|
|
24
|
+
/** Stable device ID. Default is stored in localStorage */
|
|
25
|
+
deviceId?: string;
|
|
26
|
+
/** Human-readable label passed through to listeners */
|
|
27
|
+
label?: string;
|
|
28
|
+
/** Source/app label passed through to listeners */
|
|
29
|
+
source?: string;
|
|
30
|
+
/** Send a heartbeat even when the GPS position has not changed. Default is 5000 */
|
|
31
|
+
heartbeatMs?: number;
|
|
32
|
+
/** Minimum movement before sending a non-heartbeat update. Default is 3 */
|
|
33
|
+
minDistanceMeters?: number;
|
|
34
|
+
/** Do not send non-heartbeat positions worse than this accuracy. Default is 200 */
|
|
35
|
+
maxAccuracyMeters?: number;
|
|
36
|
+
/** Use browser high-accuracy GPS. Default is true */
|
|
37
|
+
highAccuracy?: boolean;
|
|
38
|
+
/** Browser geolocation timeout. Default is 10000 */
|
|
39
|
+
timeoutMs?: number;
|
|
40
|
+
/** Reject GPS fixes older than this. Default is 4000 */
|
|
41
|
+
maxStaleMs?: number;
|
|
42
|
+
/** Restart watchPosition if no fresh fix arrives. Default is 60000 */
|
|
43
|
+
watchdogMs?: number;
|
|
44
|
+
/** Wait for socket-server ack on manual send(). Default is false */
|
|
45
|
+
waitForAck?: boolean;
|
|
46
|
+
/** Ack wait timeout when waitForAck is true. Default is 15000 */
|
|
47
|
+
ackTimeoutMs?: number;
|
|
48
|
+
};
|
|
49
|
+
export type GpsPositionPayload = {
|
|
50
|
+
lat: number;
|
|
51
|
+
lng: number;
|
|
52
|
+
accuracy: number | null;
|
|
53
|
+
altitude: number | null;
|
|
54
|
+
altitudeAccuracy: number | null;
|
|
55
|
+
heading: number | null;
|
|
56
|
+
speed: number | null;
|
|
57
|
+
timestamp: string;
|
|
58
|
+
timestampMs: number;
|
|
59
|
+
};
|
|
60
|
+
export type GpsManualPositionPayload = Omit<Partial<GpsPositionPayload>, 'lat' | 'lng' | 'timestampMs'> & {
|
|
61
|
+
lat?: number | string;
|
|
62
|
+
lng?: number | string;
|
|
63
|
+
lon?: number | string;
|
|
64
|
+
latitude?: number | string;
|
|
65
|
+
longitude?: number | string;
|
|
66
|
+
timestampMs?: number | string;
|
|
67
|
+
};
|
|
68
|
+
export type GpsListenOptions = {
|
|
69
|
+
slice?: number | string;
|
|
70
|
+
field?: string;
|
|
71
|
+
includeTrails?: boolean;
|
|
72
|
+
trailLimit?: number;
|
|
73
|
+
};
|
|
74
|
+
export type GpsListener = (event: any) => any;
|
|
75
|
+
/** Server response body inside `{ gps: ... }` */
|
|
76
|
+
export type GpsAck = {
|
|
77
|
+
ok: boolean;
|
|
78
|
+
seq?: number | null;
|
|
79
|
+
error?: string;
|
|
80
|
+
stage?: string;
|
|
81
|
+
slice?: number | string;
|
|
82
|
+
field?: string;
|
|
83
|
+
trackingId?: string;
|
|
84
|
+
liveKey?: string;
|
|
85
|
+
dirtyKey?: string;
|
|
86
|
+
activeKey?: string;
|
|
87
|
+
trailKey?: string;
|
|
88
|
+
sessionId?: string | null;
|
|
89
|
+
deviceId?: string | null;
|
|
90
|
+
pending?: boolean;
|
|
91
|
+
};
|
|
92
|
+
export type GpsSendOptions = {
|
|
93
|
+
/** When true, wait for matching socket-server ack (matched by seq). Default false */
|
|
94
|
+
waitForAck?: boolean;
|
|
95
|
+
};
|
|
96
|
+
/** Browser/manual GPS sender backed by socket-server Redis GPS storage */
|
|
97
|
+
export declare class Gps {
|
|
98
|
+
private readonly api;
|
|
99
|
+
private static readonly GPS_ACK_TIMEOUT_MS;
|
|
100
|
+
private ackResolvers;
|
|
101
|
+
private deviceId;
|
|
102
|
+
private heartbeat?;
|
|
103
|
+
private lastFixAt;
|
|
104
|
+
private lastPosition?;
|
|
105
|
+
private lastSentAt;
|
|
106
|
+
private lastSentPosition?;
|
|
107
|
+
private pendingPayloads;
|
|
108
|
+
private retryWatchdog?;
|
|
109
|
+
private seq;
|
|
110
|
+
private sessionId;
|
|
111
|
+
private socket?;
|
|
112
|
+
private trackerId;
|
|
113
|
+
private started;
|
|
114
|
+
private unsubs;
|
|
115
|
+
private watchId?;
|
|
116
|
+
private watchStartedAt;
|
|
117
|
+
readonly options?: Exclude<GpsOptions, false>;
|
|
118
|
+
constructor(api: Api, options?: GpsOptions);
|
|
119
|
+
/** Start sharing browser GPS to socket-server */
|
|
120
|
+
start(): void;
|
|
121
|
+
/** Stop sharing GPS and close the dedicated GPS socket */
|
|
122
|
+
stop(): void;
|
|
123
|
+
/**
|
|
124
|
+
* Send a position from an app-owned GPS engine.
|
|
125
|
+
* By default returns after the message is queued/sent; set `waitForAck` to await server confirmation.
|
|
126
|
+
*/
|
|
127
|
+
send(position: GpsManualPositionPayload, extra?: any, options?: GpsSendOptions): Promise<GpsAck>;
|
|
128
|
+
/** Listen to GPS updates for the configured slice/field */
|
|
129
|
+
listen(callback: GpsListener, options?: GpsListenOptions): Unsubscribe;
|
|
130
|
+
private bindResumeEvents;
|
|
131
|
+
private clearAckResolvers;
|
|
132
|
+
private connectSocket;
|
|
133
|
+
private onSocketMessage;
|
|
134
|
+
private createId;
|
|
135
|
+
private flushPendingPayloads;
|
|
136
|
+
private getClientDetails;
|
|
137
|
+
private getOrCreateDeviceId;
|
|
138
|
+
private getTrackingId;
|
|
139
|
+
private isFresh;
|
|
140
|
+
private metersBetween;
|
|
141
|
+
private normalizeNullableNumber;
|
|
142
|
+
private normalizeOptionalNumber;
|
|
143
|
+
private normalizePosition;
|
|
144
|
+
private onPosition;
|
|
145
|
+
private onPositionError;
|
|
146
|
+
private restartWatch;
|
|
147
|
+
private sendPayload;
|
|
148
|
+
private sendPosition;
|
|
149
|
+
private serializePosition;
|
|
150
|
+
private shouldSendPosition;
|
|
151
|
+
private startHeartbeat;
|
|
152
|
+
private startWatch;
|
|
153
|
+
private startWatchdog;
|
|
154
|
+
private startWhenAuthenticated;
|
|
155
|
+
}
|
|
156
|
+
//# sourceMappingURL=gps.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"gps.d.ts","sourceRoot":"","sources":["../src/gps.ts"],"names":[],"mappings":"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"}
|
package/dist/index.cjs
CHANGED
|
@@ -3774,7 +3774,7 @@ var __publicField = (obj, key, value) => __defNormalProp(obj, typeof key !== "sy
|
|
|
3774
3774
|
} });
|
|
3775
3775
|
}
|
|
3776
3776
|
}
|
|
3777
|
-
const version = "1.
|
|
3777
|
+
const version = "1.4.1";
|
|
3778
3778
|
class WebRtc {
|
|
3779
3779
|
constructor(api) {
|
|
3780
3780
|
__publicField(this, "ice");
|
|
@@ -3895,6 +3895,400 @@ var __publicField = (obj, key, value) => __defNormalProp(obj, typeof key !== "sy
|
|
|
3895
3895
|
return session;
|
|
3896
3896
|
}
|
|
3897
3897
|
}
|
|
3898
|
+
const _Gps = class _Gps {
|
|
3899
|
+
constructor(api, options) {
|
|
3900
|
+
__publicField(this, "ackResolvers", /* @__PURE__ */ new Map());
|
|
3901
|
+
__publicField(this, "deviceId");
|
|
3902
|
+
__publicField(this, "heartbeat");
|
|
3903
|
+
__publicField(this, "lastFixAt", 0);
|
|
3904
|
+
__publicField(this, "lastPosition");
|
|
3905
|
+
__publicField(this, "lastSentAt", 0);
|
|
3906
|
+
__publicField(this, "lastSentPosition");
|
|
3907
|
+
__publicField(this, "pendingPayloads", []);
|
|
3908
|
+
__publicField(this, "retryWatchdog");
|
|
3909
|
+
__publicField(this, "seq", 0);
|
|
3910
|
+
__publicField(this, "sessionId");
|
|
3911
|
+
__publicField(this, "socket");
|
|
3912
|
+
__publicField(this, "trackerId");
|
|
3913
|
+
__publicField(this, "started", false);
|
|
3914
|
+
__publicField(this, "unsubs", []);
|
|
3915
|
+
__publicField(this, "watchId");
|
|
3916
|
+
__publicField(this, "watchStartedAt", 0);
|
|
3917
|
+
__publicField(this, "options");
|
|
3918
|
+
__publicField(this, "onSocketMessage", (event) => {
|
|
3919
|
+
const ack = event == null ? void 0 : event.gps;
|
|
3920
|
+
if (!ack || ack.seq == null) return;
|
|
3921
|
+
const pending = this.ackResolvers.get(Number(ack.seq));
|
|
3922
|
+
if (!pending) return;
|
|
3923
|
+
clearTimeout(pending.timeout);
|
|
3924
|
+
this.ackResolvers.delete(Number(ack.seq));
|
|
3925
|
+
pending.resolve(ack);
|
|
3926
|
+
});
|
|
3927
|
+
__publicField(this, "onPosition", (position) => {
|
|
3928
|
+
if (!this.options) return;
|
|
3929
|
+
if (!this.isFresh(position)) return;
|
|
3930
|
+
this.lastFixAt = Date.now();
|
|
3931
|
+
this.lastPosition = position;
|
|
3932
|
+
if (this.shouldSendPosition(position, false)) this.sendPosition(position, false);
|
|
3933
|
+
});
|
|
3934
|
+
__publicField(this, "onPositionError", (error) => {
|
|
3935
|
+
console.warn("Datalynk GPS error:", error.message);
|
|
3936
|
+
});
|
|
3937
|
+
this.api = api;
|
|
3938
|
+
if (options === false || options == null) return;
|
|
3939
|
+
this.options = {
|
|
3940
|
+
autoStart: true,
|
|
3941
|
+
field: "coords",
|
|
3942
|
+
heartbeatMs: 5e3,
|
|
3943
|
+
minDistanceMeters: 3,
|
|
3944
|
+
maxAccuracyMeters: 200,
|
|
3945
|
+
highAccuracy: true,
|
|
3946
|
+
timeoutMs: 1e4,
|
|
3947
|
+
maxStaleMs: 4e3,
|
|
3948
|
+
watchdogMs: 6e4,
|
|
3949
|
+
...options
|
|
3950
|
+
};
|
|
3951
|
+
this.deviceId = this.options.deviceId || this.getOrCreateDeviceId();
|
|
3952
|
+
this.sessionId = this.createId();
|
|
3953
|
+
this.trackerId = this.options.trackingId || this.createId();
|
|
3954
|
+
if (this.options.autoStart !== false) this.startWhenAuthenticated();
|
|
3955
|
+
}
|
|
3956
|
+
/** Start sharing browser GPS to socket-server */
|
|
3957
|
+
start() {
|
|
3958
|
+
if (!this.options) throw new Error("Datalynk GPS is not configured");
|
|
3959
|
+
if (this.started) return;
|
|
3960
|
+
if (!this.api.token) throw new Error("Datalynk GPS cannot start before login");
|
|
3961
|
+
if (typeof navigator == "undefined" || !navigator.geolocation) throw new Error("Browser geolocation is not available");
|
|
3962
|
+
this.started = true;
|
|
3963
|
+
this.connectSocket();
|
|
3964
|
+
this.startWatch();
|
|
3965
|
+
this.startHeartbeat();
|
|
3966
|
+
this.startWatchdog();
|
|
3967
|
+
this.bindResumeEvents();
|
|
3968
|
+
}
|
|
3969
|
+
/** Stop sharing GPS and close the dedicated GPS socket */
|
|
3970
|
+
stop() {
|
|
3971
|
+
var _a;
|
|
3972
|
+
this.started = false;
|
|
3973
|
+
if (this.watchId != null && typeof navigator != "undefined") navigator.geolocation.clearWatch(this.watchId);
|
|
3974
|
+
this.watchId = void 0;
|
|
3975
|
+
if (this.heartbeat) clearInterval(this.heartbeat);
|
|
3976
|
+
this.heartbeat = void 0;
|
|
3977
|
+
if (this.retryWatchdog) clearInterval(this.retryWatchdog);
|
|
3978
|
+
this.retryWatchdog = void 0;
|
|
3979
|
+
this.unsubs.forEach((unsub) => unsub());
|
|
3980
|
+
this.unsubs = [];
|
|
3981
|
+
this.pendingPayloads = [];
|
|
3982
|
+
this.clearAckResolvers("GPS stopped");
|
|
3983
|
+
(_a = this.socket) == null ? void 0 : _a.close();
|
|
3984
|
+
this.socket = void 0;
|
|
3985
|
+
}
|
|
3986
|
+
/**
|
|
3987
|
+
* Send a position from an app-owned GPS engine.
|
|
3988
|
+
* By default returns after the message is queued/sent; set `waitForAck` to await server confirmation.
|
|
3989
|
+
*/
|
|
3990
|
+
send(position, extra = {}, options = {}) {
|
|
3991
|
+
if (!this.options) throw new Error("Datalynk GPS is not configured");
|
|
3992
|
+
if (!this.api.token) throw new Error("Datalynk GPS cannot send before login");
|
|
3993
|
+
this.connectSocket();
|
|
3994
|
+
const waitForAck = options.waitForAck ?? this.options.waitForAck ?? false;
|
|
3995
|
+
return this.sendPayload({
|
|
3996
|
+
...extra,
|
|
3997
|
+
position: this.normalizePosition(position)
|
|
3998
|
+
}, waitForAck);
|
|
3999
|
+
}
|
|
4000
|
+
/** Listen to GPS updates for the configured slice/field */
|
|
4001
|
+
listen(callback, options = {}) {
|
|
4002
|
+
if (!this.options) throw new Error("Datalynk GPS is not configured");
|
|
4003
|
+
if (!this.api.token) throw new Error("Datalynk GPS cannot listen before login");
|
|
4004
|
+
this.connectSocket();
|
|
4005
|
+
const request = {
|
|
4006
|
+
gpsListen: {
|
|
4007
|
+
slice: options.slice || this.options.slice,
|
|
4008
|
+
field: options.field || this.options.field || "coords",
|
|
4009
|
+
includeTrails: options.includeTrails ?? false,
|
|
4010
|
+
trailLimit: options.trailLimit ?? 0
|
|
4011
|
+
}
|
|
4012
|
+
};
|
|
4013
|
+
return this.socket.addListener((event) => {
|
|
4014
|
+
var _a, _b;
|
|
4015
|
+
const payload = event;
|
|
4016
|
+
const gps = ((_a = payload == null ? void 0 : payload.data) == null ? void 0 : _a.gps) || (payload == null ? void 0 : payload.gps);
|
|
4017
|
+
if ((payload == null ? void 0 : payload.type) == "sliceEvents" && ((_b = payload == null ? void 0 : payload.data) == null ? void 0 : _b.type) == "gps.position" && gps) callback(gps);
|
|
4018
|
+
else if ((payload == null ? void 0 : payload.type) == "gps.position" && gps) callback(gps);
|
|
4019
|
+
else if ((payload == null ? void 0 : payload.type) == "gpsListen" || (payload == null ? void 0 : payload.gpsListen)) callback(payload);
|
|
4020
|
+
}, () => this.socket.send(request));
|
|
4021
|
+
}
|
|
4022
|
+
bindResumeEvents() {
|
|
4023
|
+
if (typeof window == "undefined" || typeof document == "undefined") return;
|
|
4024
|
+
const restart = () => {
|
|
4025
|
+
if (!this.started) return;
|
|
4026
|
+
if (document.visibilityState && document.visibilityState !== "visible") return;
|
|
4027
|
+
this.restartWatch();
|
|
4028
|
+
};
|
|
4029
|
+
window.addEventListener("focus", restart);
|
|
4030
|
+
window.addEventListener("pageshow", restart);
|
|
4031
|
+
document.addEventListener("visibilitychange", restart);
|
|
4032
|
+
this.unsubs.push(() => {
|
|
4033
|
+
window.removeEventListener("focus", restart);
|
|
4034
|
+
window.removeEventListener("pageshow", restart);
|
|
4035
|
+
document.removeEventListener("visibilitychange", restart);
|
|
4036
|
+
});
|
|
4037
|
+
}
|
|
4038
|
+
clearAckResolvers(reason) {
|
|
4039
|
+
this.ackResolvers.forEach(({ timeout, resolve }) => {
|
|
4040
|
+
clearTimeout(timeout);
|
|
4041
|
+
resolve({ ok: false, error: reason });
|
|
4042
|
+
});
|
|
4043
|
+
this.ackResolvers.clear();
|
|
4044
|
+
}
|
|
4045
|
+
connectSocket() {
|
|
4046
|
+
if (!this.options || this.socket) return;
|
|
4047
|
+
if (!this.api.token) throw new Error("Datalynk GPS socket cannot connect before login");
|
|
4048
|
+
this.socket = new Socket(this.api, { url: this.options.socketUrl });
|
|
4049
|
+
this.unsubs.push(this.socket.addListener(this.onSocketMessage, () => this.flushPendingPayloads()));
|
|
4050
|
+
}
|
|
4051
|
+
createId() {
|
|
4052
|
+
if (typeof crypto != "undefined" && crypto.randomUUID) return crypto.randomUUID();
|
|
4053
|
+
return `${Date.now()}-${Math.random().toString(16).slice(2)}`;
|
|
4054
|
+
}
|
|
4055
|
+
flushPendingPayloads() {
|
|
4056
|
+
var _a;
|
|
4057
|
+
if (!((_a = this.socket) == null ? void 0 : _a.open)) return;
|
|
4058
|
+
while (this.pendingPayloads.length) {
|
|
4059
|
+
this.socket.send(this.pendingPayloads.shift());
|
|
4060
|
+
}
|
|
4061
|
+
}
|
|
4062
|
+
getClientDetails() {
|
|
4063
|
+
if (typeof navigator == "undefined") return {};
|
|
4064
|
+
return {
|
|
4065
|
+
userAgent: navigator.userAgent,
|
|
4066
|
+
language: navigator.language,
|
|
4067
|
+
platform: navigator.platform,
|
|
4068
|
+
vendor: navigator.vendor,
|
|
4069
|
+
screen: typeof screen == "undefined" ? void 0 : {
|
|
4070
|
+
width: screen.width,
|
|
4071
|
+
height: screen.height,
|
|
4072
|
+
pixelRatio: typeof devicePixelRatio == "undefined" ? void 0 : devicePixelRatio
|
|
4073
|
+
},
|
|
4074
|
+
timezone: Intl.DateTimeFormat().resolvedOptions().timeZone
|
|
4075
|
+
};
|
|
4076
|
+
}
|
|
4077
|
+
getOrCreateDeviceId() {
|
|
4078
|
+
const key = "datalynk-gps-device-id";
|
|
4079
|
+
if (typeof localStorage != "undefined") {
|
|
4080
|
+
const existing = localStorage.getItem(key);
|
|
4081
|
+
if (existing) return existing;
|
|
4082
|
+
const id = this.createId();
|
|
4083
|
+
localStorage.setItem(key, id);
|
|
4084
|
+
return id;
|
|
4085
|
+
}
|
|
4086
|
+
return this.createId();
|
|
4087
|
+
}
|
|
4088
|
+
getTrackingId() {
|
|
4089
|
+
var _a;
|
|
4090
|
+
if (!this.options) return this.trackerId || this.createId();
|
|
4091
|
+
if (this.options.trackingId) return this.options.trackingId;
|
|
4092
|
+
const uid = (_a = this.api.jwtPayload) == null ? void 0 : _a.uid;
|
|
4093
|
+
return uid ? `user:${uid}:tracker:${this.trackerId}` : `device:${this.deviceId}:tracker:${this.trackerId}`;
|
|
4094
|
+
}
|
|
4095
|
+
isFresh(position) {
|
|
4096
|
+
if (!this.options) return false;
|
|
4097
|
+
if (this.lastPosition && position.timestamp <= this.lastPosition.timestamp) return false;
|
|
4098
|
+
return Date.now() - position.timestamp <= (this.options.maxStaleMs || 4e3);
|
|
4099
|
+
}
|
|
4100
|
+
metersBetween(a, b) {
|
|
4101
|
+
const earthRadius = 6371e3;
|
|
4102
|
+
const lat1 = a.coords.latitude * Math.PI / 180;
|
|
4103
|
+
const lat2 = b.coords.latitude * Math.PI / 180;
|
|
4104
|
+
const dLat = (b.coords.latitude - a.coords.latitude) * Math.PI / 180;
|
|
4105
|
+
const dLng = (b.coords.longitude - a.coords.longitude) * Math.PI / 180;
|
|
4106
|
+
const x = Math.sin(dLat / 2) * Math.sin(dLat / 2) + Math.cos(lat1) * Math.cos(lat2) * Math.sin(dLng / 2) * Math.sin(dLng / 2);
|
|
4107
|
+
return earthRadius * 2 * Math.atan2(Math.sqrt(x), Math.sqrt(1 - x));
|
|
4108
|
+
}
|
|
4109
|
+
normalizeNullableNumber(value, field) {
|
|
4110
|
+
if (value == null || value === "") return null;
|
|
4111
|
+
const number = Number(value);
|
|
4112
|
+
if (!Number.isFinite(number)) throw new Error(`GPS ${field} must be a valid number`);
|
|
4113
|
+
return number;
|
|
4114
|
+
}
|
|
4115
|
+
normalizeOptionalNumber(value, field) {
|
|
4116
|
+
if (value == null || value === "") return void 0;
|
|
4117
|
+
const number = Number(value);
|
|
4118
|
+
if (!Number.isFinite(number)) throw new Error(`GPS ${field} must be a valid number`);
|
|
4119
|
+
return number;
|
|
4120
|
+
}
|
|
4121
|
+
normalizePosition(position) {
|
|
4122
|
+
const lat = Number(position.lat ?? position.latitude);
|
|
4123
|
+
const lng = Number(position.lng ?? position.lon ?? position.longitude);
|
|
4124
|
+
if (!Number.isFinite(lat) || lat < -90 || lat > 90) {
|
|
4125
|
+
throw new Error("GPS position must include a valid lat/latitude between -90 and 90");
|
|
4126
|
+
}
|
|
4127
|
+
if (!Number.isFinite(lng) || lng < -180 || lng > 180) {
|
|
4128
|
+
throw new Error("GPS position must include a valid lng/lon/longitude between -180 and 180");
|
|
4129
|
+
}
|
|
4130
|
+
const accuracy = this.normalizeNullableNumber(position.accuracy, "accuracy");
|
|
4131
|
+
if (accuracy != null && accuracy < 0) throw new Error("GPS accuracy must be zero or greater");
|
|
4132
|
+
const altitude = this.normalizeNullableNumber(position.altitude, "altitude");
|
|
4133
|
+
const altitudeAccuracy = this.normalizeNullableNumber(position.altitudeAccuracy, "altitudeAccuracy");
|
|
4134
|
+
if (altitudeAccuracy != null && altitudeAccuracy < 0) throw new Error("GPS altitudeAccuracy must be zero or greater");
|
|
4135
|
+
const heading = this.normalizeNullableNumber(position.heading, "heading");
|
|
4136
|
+
if (heading != null && (heading < 0 || heading >= 360)) throw new Error("GPS heading must be between 0 and 360");
|
|
4137
|
+
const speed = this.normalizeNullableNumber(position.speed, "speed");
|
|
4138
|
+
if (speed != null && speed < 0) throw new Error("GPS speed must be zero or greater");
|
|
4139
|
+
const timestampMs = this.normalizeOptionalNumber(position.timestampMs, "timestampMs") ?? Date.now();
|
|
4140
|
+
if (timestampMs <= 0) throw new Error("GPS timestampMs must be greater than zero");
|
|
4141
|
+
let timestamp = position.timestamp;
|
|
4142
|
+
if (timestamp) {
|
|
4143
|
+
const parsed = Date.parse(timestamp);
|
|
4144
|
+
if (!Number.isFinite(parsed)) throw new Error("GPS timestamp must be a valid date string");
|
|
4145
|
+
} else {
|
|
4146
|
+
timestamp = new Date(timestampMs).toISOString();
|
|
4147
|
+
}
|
|
4148
|
+
return {
|
|
4149
|
+
lat,
|
|
4150
|
+
lng,
|
|
4151
|
+
accuracy,
|
|
4152
|
+
altitude,
|
|
4153
|
+
altitudeAccuracy,
|
|
4154
|
+
heading,
|
|
4155
|
+
speed,
|
|
4156
|
+
timestamp,
|
|
4157
|
+
timestampMs
|
|
4158
|
+
};
|
|
4159
|
+
}
|
|
4160
|
+
restartWatch() {
|
|
4161
|
+
if (!this.options || !this.started || typeof navigator == "undefined" || !navigator.geolocation) return;
|
|
4162
|
+
if (this.watchId != null) navigator.geolocation.clearWatch(this.watchId);
|
|
4163
|
+
this.watchId = void 0;
|
|
4164
|
+
this.startWatch();
|
|
4165
|
+
}
|
|
4166
|
+
sendPayload(payload, waitForAck = false) {
|
|
4167
|
+
var _a, _b;
|
|
4168
|
+
if (!this.options) return Promise.resolve({ ok: false, error: "Datalynk GPS is not configured" });
|
|
4169
|
+
this.connectSocket();
|
|
4170
|
+
const seq = ++this.seq;
|
|
4171
|
+
const message = {
|
|
4172
|
+
gps: {
|
|
4173
|
+
slice: this.options.slice,
|
|
4174
|
+
field: this.options.field || "coords",
|
|
4175
|
+
trackingId: this.getTrackingId(),
|
|
4176
|
+
trackerId: this.trackerId,
|
|
4177
|
+
sessionId: this.sessionId,
|
|
4178
|
+
deviceId: this.deviceId,
|
|
4179
|
+
seq,
|
|
4180
|
+
client: this.getClientDetails(),
|
|
4181
|
+
sentAt: (/* @__PURE__ */ new Date()).toISOString(),
|
|
4182
|
+
gpsTimestamp: ((_a = payload.position) == null ? void 0 : _a.timestamp) || null,
|
|
4183
|
+
label: this.options.label,
|
|
4184
|
+
source: this.options.source,
|
|
4185
|
+
...payload
|
|
4186
|
+
}
|
|
4187
|
+
};
|
|
4188
|
+
let ackPromise;
|
|
4189
|
+
if (waitForAck) {
|
|
4190
|
+
const timeoutMs = this.options.ackTimeoutMs ?? _Gps.GPS_ACK_TIMEOUT_MS;
|
|
4191
|
+
ackPromise = new Promise((resolve) => {
|
|
4192
|
+
const timeout = setTimeout(() => {
|
|
4193
|
+
this.ackResolvers.delete(seq);
|
|
4194
|
+
resolve({ ok: false, error: "GPS ack timeout", seq });
|
|
4195
|
+
}, timeoutMs);
|
|
4196
|
+
this.ackResolvers.set(seq, { resolve, timeout });
|
|
4197
|
+
});
|
|
4198
|
+
}
|
|
4199
|
+
if (!((_b = this.socket) == null ? void 0 : _b.open)) {
|
|
4200
|
+
this.pendingPayloads.push(message);
|
|
4201
|
+
this.pendingPayloads = this.pendingPayloads.slice(-25);
|
|
4202
|
+
return ackPromise ?? Promise.resolve({ ok: true, seq, pending: true });
|
|
4203
|
+
}
|
|
4204
|
+
this.socket.send(message);
|
|
4205
|
+
return ackPromise ?? Promise.resolve({ ok: true, seq, pending: false });
|
|
4206
|
+
}
|
|
4207
|
+
sendPosition(position, force) {
|
|
4208
|
+
this.sendPayload({
|
|
4209
|
+
force,
|
|
4210
|
+
position: this.serializePosition(position)
|
|
4211
|
+
});
|
|
4212
|
+
this.lastSentPosition = position;
|
|
4213
|
+
this.lastSentAt = Date.now();
|
|
4214
|
+
}
|
|
4215
|
+
serializePosition(position) {
|
|
4216
|
+
return {
|
|
4217
|
+
lat: position.coords.latitude,
|
|
4218
|
+
lng: position.coords.longitude,
|
|
4219
|
+
accuracy: position.coords.accuracy,
|
|
4220
|
+
altitude: position.coords.altitude,
|
|
4221
|
+
altitudeAccuracy: position.coords.altitudeAccuracy,
|
|
4222
|
+
heading: position.coords.heading,
|
|
4223
|
+
speed: position.coords.speed,
|
|
4224
|
+
timestamp: new Date(position.timestamp).toISOString(),
|
|
4225
|
+
timestampMs: position.timestamp
|
|
4226
|
+
};
|
|
4227
|
+
}
|
|
4228
|
+
shouldSendPosition(position, force) {
|
|
4229
|
+
if (!this.options) return false;
|
|
4230
|
+
if (force) return true;
|
|
4231
|
+
const accuracy = Number(position.coords.accuracy || 0);
|
|
4232
|
+
if (accuracy && accuracy > (this.options.maxAccuracyMeters || 200)) return false;
|
|
4233
|
+
if (!this.lastSentPosition) return true;
|
|
4234
|
+
const movedMeters = this.metersBetween(this.lastSentPosition, position);
|
|
4235
|
+
const accuracyImproved = Number(position.coords.accuracy || 999999) < Number(this.lastSentPosition.coords.accuracy || 999999);
|
|
4236
|
+
const heartbeatDue = Date.now() - this.lastSentAt >= (this.options.heartbeatMs || 5e3);
|
|
4237
|
+
return movedMeters >= (this.options.minDistanceMeters || 3) || accuracyImproved || heartbeatDue;
|
|
4238
|
+
}
|
|
4239
|
+
startHeartbeat() {
|
|
4240
|
+
if (!this.options || this.heartbeat) return;
|
|
4241
|
+
this.heartbeat = setInterval(() => {
|
|
4242
|
+
if (!this.started || !this.lastPosition) return;
|
|
4243
|
+
this.sendPosition(this.lastPosition, true);
|
|
4244
|
+
}, this.options.heartbeatMs || 5e3);
|
|
4245
|
+
}
|
|
4246
|
+
startWatch() {
|
|
4247
|
+
if (!this.options || this.watchId != null) return;
|
|
4248
|
+
this.watchStartedAt = Date.now();
|
|
4249
|
+
this.watchId = navigator.geolocation.watchPosition(this.onPosition, this.onPositionError, {
|
|
4250
|
+
enableHighAccuracy: this.options.highAccuracy !== false,
|
|
4251
|
+
maximumAge: 0,
|
|
4252
|
+
timeout: this.options.timeoutMs || 1e4
|
|
4253
|
+
});
|
|
4254
|
+
}
|
|
4255
|
+
startWatchdog() {
|
|
4256
|
+
if (!this.options || this.retryWatchdog) return;
|
|
4257
|
+
this.retryWatchdog = setInterval(() => {
|
|
4258
|
+
if (!this.started) return;
|
|
4259
|
+
const lastActivity = this.lastFixAt || this.watchStartedAt;
|
|
4260
|
+
if (lastActivity && Date.now() - lastActivity >= (this.options.watchdogMs || 6e4)) this.restartWatch();
|
|
4261
|
+
}, Math.min(this.options.watchdogMs || 6e4, 15e3));
|
|
4262
|
+
}
|
|
4263
|
+
startWhenAuthenticated() {
|
|
4264
|
+
if (!this.options) return;
|
|
4265
|
+
if (this.api.token) {
|
|
4266
|
+
setTimeout(() => {
|
|
4267
|
+
try {
|
|
4268
|
+
this.start();
|
|
4269
|
+
} catch (error) {
|
|
4270
|
+
console.warn("Datalynk GPS auto-start failed:", error);
|
|
4271
|
+
}
|
|
4272
|
+
}, 0);
|
|
4273
|
+
return;
|
|
4274
|
+
}
|
|
4275
|
+
const sub = this.api.token$.subscribe((token) => {
|
|
4276
|
+
if (!token) {
|
|
4277
|
+
if (this.started) this.stop();
|
|
4278
|
+
return;
|
|
4279
|
+
}
|
|
4280
|
+
if (this.started || !this.options || this.options.autoStart === false) return;
|
|
4281
|
+
try {
|
|
4282
|
+
this.start();
|
|
4283
|
+
} catch (error) {
|
|
4284
|
+
console.warn("Datalynk GPS auto-start failed:", error);
|
|
4285
|
+
}
|
|
4286
|
+
});
|
|
4287
|
+
this.unsubs.push(() => sub.unsubscribe());
|
|
4288
|
+
}
|
|
4289
|
+
};
|
|
4290
|
+
__publicField(_Gps, "GPS_ACK_TIMEOUT_MS", 15e3);
|
|
4291
|
+
let Gps = _Gps;
|
|
3898
4292
|
const _Api = class _Api {
|
|
3899
4293
|
/**
|
|
3900
4294
|
* Connect to Datalynk & send requests
|
|
@@ -3933,6 +4327,8 @@ var __publicField = (obj, key, value) => __defNormalProp(obj, typeof key !== "sy
|
|
|
3933
4327
|
__publicField(this, "pwa");
|
|
3934
4328
|
/** Socket */
|
|
3935
4329
|
__publicField(this, "socket");
|
|
4330
|
+
/** GPS */
|
|
4331
|
+
__publicField(this, "gps");
|
|
3936
4332
|
/** Superuser */
|
|
3937
4333
|
__publicField(this, "superuser");
|
|
3938
4334
|
/** WebRTC */
|
|
@@ -3978,6 +4374,7 @@ var __publicField = (obj, key, value) => __defNormalProp(obj, typeof key !== "sy
|
|
|
3978
4374
|
});
|
|
3979
4375
|
}
|
|
3980
4376
|
this.socket = new Socket(this, { url: options.socket });
|
|
4377
|
+
this.gps = new Gps(this, options.gps);
|
|
3981
4378
|
this.auth = new Auth(this);
|
|
3982
4379
|
this.files = new Files(this);
|
|
3983
4380
|
this.pdf = new Pdf(this);
|
|
@@ -4283,6 +4680,7 @@ var __publicField = (obj, key, value) => __defNormalProp(obj, typeof key !== "sy
|
|
|
4283
4680
|
exports2.ApiCall = ApiCall;
|
|
4284
4681
|
exports2.Auth = Auth;
|
|
4285
4682
|
exports2.Files = Files;
|
|
4683
|
+
exports2.Gps = Gps;
|
|
4286
4684
|
exports2.LoginPrompt = LoginPrompt;
|
|
4287
4685
|
exports2.PWA = PWA;
|
|
4288
4686
|
exports2.Pdf = Pdf;
|
package/dist/index.d.ts
CHANGED
package/dist/index.d.ts.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":"AAAA,cAAc,OAAO,CAAC;AACtB,cAAc,QAAQ,CAAC;AACvB,cAAc,SAAS,CAAC;AACxB,cAAc,gBAAgB,CAAC;AAC/B,cAAc,QAAQ,CAAC;AACvB,cAAc,OAAO,CAAC;AACtB,cAAc,OAAO,CAAC;AACtB,cAAc,cAAc,CAAC;AAC7B,cAAc,SAAS,CAAC;AACxB,cAAc,UAAU,CAAC;AACzB,cAAc,aAAa,CAAC;AAC5B,cAAc,UAAU,CAAC"}
|
|
1
|
+
{"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":"AAAA,cAAc,OAAO,CAAC;AACtB,cAAc,QAAQ,CAAC;AACvB,cAAc,SAAS,CAAC;AACxB,cAAc,OAAO,CAAC;AACtB,cAAc,gBAAgB,CAAC;AAC/B,cAAc,QAAQ,CAAC;AACvB,cAAc,OAAO,CAAC;AACtB,cAAc,OAAO,CAAC;AACtB,cAAc,cAAc,CAAC;AAC7B,cAAc,SAAS,CAAC;AACxB,cAAc,UAAU,CAAC;AACzB,cAAc,aAAa,CAAC;AAC5B,cAAc,UAAU,CAAC"}
|
package/dist/index.mjs
CHANGED
|
@@ -3770,7 +3770,7 @@ class Superuser {
|
|
|
3770
3770
|
} });
|
|
3771
3771
|
}
|
|
3772
3772
|
}
|
|
3773
|
-
const version = "1.
|
|
3773
|
+
const version = "1.4.1";
|
|
3774
3774
|
class WebRtc {
|
|
3775
3775
|
constructor(api) {
|
|
3776
3776
|
__publicField(this, "ice");
|
|
@@ -3891,6 +3891,400 @@ class WebRtc {
|
|
|
3891
3891
|
return session;
|
|
3892
3892
|
}
|
|
3893
3893
|
}
|
|
3894
|
+
const _Gps = class _Gps {
|
|
3895
|
+
constructor(api, options) {
|
|
3896
|
+
__publicField(this, "ackResolvers", /* @__PURE__ */ new Map());
|
|
3897
|
+
__publicField(this, "deviceId");
|
|
3898
|
+
__publicField(this, "heartbeat");
|
|
3899
|
+
__publicField(this, "lastFixAt", 0);
|
|
3900
|
+
__publicField(this, "lastPosition");
|
|
3901
|
+
__publicField(this, "lastSentAt", 0);
|
|
3902
|
+
__publicField(this, "lastSentPosition");
|
|
3903
|
+
__publicField(this, "pendingPayloads", []);
|
|
3904
|
+
__publicField(this, "retryWatchdog");
|
|
3905
|
+
__publicField(this, "seq", 0);
|
|
3906
|
+
__publicField(this, "sessionId");
|
|
3907
|
+
__publicField(this, "socket");
|
|
3908
|
+
__publicField(this, "trackerId");
|
|
3909
|
+
__publicField(this, "started", false);
|
|
3910
|
+
__publicField(this, "unsubs", []);
|
|
3911
|
+
__publicField(this, "watchId");
|
|
3912
|
+
__publicField(this, "watchStartedAt", 0);
|
|
3913
|
+
__publicField(this, "options");
|
|
3914
|
+
__publicField(this, "onSocketMessage", (event) => {
|
|
3915
|
+
const ack = event == null ? void 0 : event.gps;
|
|
3916
|
+
if (!ack || ack.seq == null) return;
|
|
3917
|
+
const pending = this.ackResolvers.get(Number(ack.seq));
|
|
3918
|
+
if (!pending) return;
|
|
3919
|
+
clearTimeout(pending.timeout);
|
|
3920
|
+
this.ackResolvers.delete(Number(ack.seq));
|
|
3921
|
+
pending.resolve(ack);
|
|
3922
|
+
});
|
|
3923
|
+
__publicField(this, "onPosition", (position) => {
|
|
3924
|
+
if (!this.options) return;
|
|
3925
|
+
if (!this.isFresh(position)) return;
|
|
3926
|
+
this.lastFixAt = Date.now();
|
|
3927
|
+
this.lastPosition = position;
|
|
3928
|
+
if (this.shouldSendPosition(position, false)) this.sendPosition(position, false);
|
|
3929
|
+
});
|
|
3930
|
+
__publicField(this, "onPositionError", (error) => {
|
|
3931
|
+
console.warn("Datalynk GPS error:", error.message);
|
|
3932
|
+
});
|
|
3933
|
+
this.api = api;
|
|
3934
|
+
if (options === false || options == null) return;
|
|
3935
|
+
this.options = {
|
|
3936
|
+
autoStart: true,
|
|
3937
|
+
field: "coords",
|
|
3938
|
+
heartbeatMs: 5e3,
|
|
3939
|
+
minDistanceMeters: 3,
|
|
3940
|
+
maxAccuracyMeters: 200,
|
|
3941
|
+
highAccuracy: true,
|
|
3942
|
+
timeoutMs: 1e4,
|
|
3943
|
+
maxStaleMs: 4e3,
|
|
3944
|
+
watchdogMs: 6e4,
|
|
3945
|
+
...options
|
|
3946
|
+
};
|
|
3947
|
+
this.deviceId = this.options.deviceId || this.getOrCreateDeviceId();
|
|
3948
|
+
this.sessionId = this.createId();
|
|
3949
|
+
this.trackerId = this.options.trackingId || this.createId();
|
|
3950
|
+
if (this.options.autoStart !== false) this.startWhenAuthenticated();
|
|
3951
|
+
}
|
|
3952
|
+
/** Start sharing browser GPS to socket-server */
|
|
3953
|
+
start() {
|
|
3954
|
+
if (!this.options) throw new Error("Datalynk GPS is not configured");
|
|
3955
|
+
if (this.started) return;
|
|
3956
|
+
if (!this.api.token) throw new Error("Datalynk GPS cannot start before login");
|
|
3957
|
+
if (typeof navigator == "undefined" || !navigator.geolocation) throw new Error("Browser geolocation is not available");
|
|
3958
|
+
this.started = true;
|
|
3959
|
+
this.connectSocket();
|
|
3960
|
+
this.startWatch();
|
|
3961
|
+
this.startHeartbeat();
|
|
3962
|
+
this.startWatchdog();
|
|
3963
|
+
this.bindResumeEvents();
|
|
3964
|
+
}
|
|
3965
|
+
/** Stop sharing GPS and close the dedicated GPS socket */
|
|
3966
|
+
stop() {
|
|
3967
|
+
var _a;
|
|
3968
|
+
this.started = false;
|
|
3969
|
+
if (this.watchId != null && typeof navigator != "undefined") navigator.geolocation.clearWatch(this.watchId);
|
|
3970
|
+
this.watchId = void 0;
|
|
3971
|
+
if (this.heartbeat) clearInterval(this.heartbeat);
|
|
3972
|
+
this.heartbeat = void 0;
|
|
3973
|
+
if (this.retryWatchdog) clearInterval(this.retryWatchdog);
|
|
3974
|
+
this.retryWatchdog = void 0;
|
|
3975
|
+
this.unsubs.forEach((unsub) => unsub());
|
|
3976
|
+
this.unsubs = [];
|
|
3977
|
+
this.pendingPayloads = [];
|
|
3978
|
+
this.clearAckResolvers("GPS stopped");
|
|
3979
|
+
(_a = this.socket) == null ? void 0 : _a.close();
|
|
3980
|
+
this.socket = void 0;
|
|
3981
|
+
}
|
|
3982
|
+
/**
|
|
3983
|
+
* Send a position from an app-owned GPS engine.
|
|
3984
|
+
* By default returns after the message is queued/sent; set `waitForAck` to await server confirmation.
|
|
3985
|
+
*/
|
|
3986
|
+
send(position, extra = {}, options = {}) {
|
|
3987
|
+
if (!this.options) throw new Error("Datalynk GPS is not configured");
|
|
3988
|
+
if (!this.api.token) throw new Error("Datalynk GPS cannot send before login");
|
|
3989
|
+
this.connectSocket();
|
|
3990
|
+
const waitForAck = options.waitForAck ?? this.options.waitForAck ?? false;
|
|
3991
|
+
return this.sendPayload({
|
|
3992
|
+
...extra,
|
|
3993
|
+
position: this.normalizePosition(position)
|
|
3994
|
+
}, waitForAck);
|
|
3995
|
+
}
|
|
3996
|
+
/** Listen to GPS updates for the configured slice/field */
|
|
3997
|
+
listen(callback, options = {}) {
|
|
3998
|
+
if (!this.options) throw new Error("Datalynk GPS is not configured");
|
|
3999
|
+
if (!this.api.token) throw new Error("Datalynk GPS cannot listen before login");
|
|
4000
|
+
this.connectSocket();
|
|
4001
|
+
const request = {
|
|
4002
|
+
gpsListen: {
|
|
4003
|
+
slice: options.slice || this.options.slice,
|
|
4004
|
+
field: options.field || this.options.field || "coords",
|
|
4005
|
+
includeTrails: options.includeTrails ?? false,
|
|
4006
|
+
trailLimit: options.trailLimit ?? 0
|
|
4007
|
+
}
|
|
4008
|
+
};
|
|
4009
|
+
return this.socket.addListener((event) => {
|
|
4010
|
+
var _a, _b;
|
|
4011
|
+
const payload = event;
|
|
4012
|
+
const gps = ((_a = payload == null ? void 0 : payload.data) == null ? void 0 : _a.gps) || (payload == null ? void 0 : payload.gps);
|
|
4013
|
+
if ((payload == null ? void 0 : payload.type) == "sliceEvents" && ((_b = payload == null ? void 0 : payload.data) == null ? void 0 : _b.type) == "gps.position" && gps) callback(gps);
|
|
4014
|
+
else if ((payload == null ? void 0 : payload.type) == "gps.position" && gps) callback(gps);
|
|
4015
|
+
else if ((payload == null ? void 0 : payload.type) == "gpsListen" || (payload == null ? void 0 : payload.gpsListen)) callback(payload);
|
|
4016
|
+
}, () => this.socket.send(request));
|
|
4017
|
+
}
|
|
4018
|
+
bindResumeEvents() {
|
|
4019
|
+
if (typeof window == "undefined" || typeof document == "undefined") return;
|
|
4020
|
+
const restart = () => {
|
|
4021
|
+
if (!this.started) return;
|
|
4022
|
+
if (document.visibilityState && document.visibilityState !== "visible") return;
|
|
4023
|
+
this.restartWatch();
|
|
4024
|
+
};
|
|
4025
|
+
window.addEventListener("focus", restart);
|
|
4026
|
+
window.addEventListener("pageshow", restart);
|
|
4027
|
+
document.addEventListener("visibilitychange", restart);
|
|
4028
|
+
this.unsubs.push(() => {
|
|
4029
|
+
window.removeEventListener("focus", restart);
|
|
4030
|
+
window.removeEventListener("pageshow", restart);
|
|
4031
|
+
document.removeEventListener("visibilitychange", restart);
|
|
4032
|
+
});
|
|
4033
|
+
}
|
|
4034
|
+
clearAckResolvers(reason) {
|
|
4035
|
+
this.ackResolvers.forEach(({ timeout, resolve }) => {
|
|
4036
|
+
clearTimeout(timeout);
|
|
4037
|
+
resolve({ ok: false, error: reason });
|
|
4038
|
+
});
|
|
4039
|
+
this.ackResolvers.clear();
|
|
4040
|
+
}
|
|
4041
|
+
connectSocket() {
|
|
4042
|
+
if (!this.options || this.socket) return;
|
|
4043
|
+
if (!this.api.token) throw new Error("Datalynk GPS socket cannot connect before login");
|
|
4044
|
+
this.socket = new Socket(this.api, { url: this.options.socketUrl });
|
|
4045
|
+
this.unsubs.push(this.socket.addListener(this.onSocketMessage, () => this.flushPendingPayloads()));
|
|
4046
|
+
}
|
|
4047
|
+
createId() {
|
|
4048
|
+
if (typeof crypto != "undefined" && crypto.randomUUID) return crypto.randomUUID();
|
|
4049
|
+
return `${Date.now()}-${Math.random().toString(16).slice(2)}`;
|
|
4050
|
+
}
|
|
4051
|
+
flushPendingPayloads() {
|
|
4052
|
+
var _a;
|
|
4053
|
+
if (!((_a = this.socket) == null ? void 0 : _a.open)) return;
|
|
4054
|
+
while (this.pendingPayloads.length) {
|
|
4055
|
+
this.socket.send(this.pendingPayloads.shift());
|
|
4056
|
+
}
|
|
4057
|
+
}
|
|
4058
|
+
getClientDetails() {
|
|
4059
|
+
if (typeof navigator == "undefined") return {};
|
|
4060
|
+
return {
|
|
4061
|
+
userAgent: navigator.userAgent,
|
|
4062
|
+
language: navigator.language,
|
|
4063
|
+
platform: navigator.platform,
|
|
4064
|
+
vendor: navigator.vendor,
|
|
4065
|
+
screen: typeof screen == "undefined" ? void 0 : {
|
|
4066
|
+
width: screen.width,
|
|
4067
|
+
height: screen.height,
|
|
4068
|
+
pixelRatio: typeof devicePixelRatio == "undefined" ? void 0 : devicePixelRatio
|
|
4069
|
+
},
|
|
4070
|
+
timezone: Intl.DateTimeFormat().resolvedOptions().timeZone
|
|
4071
|
+
};
|
|
4072
|
+
}
|
|
4073
|
+
getOrCreateDeviceId() {
|
|
4074
|
+
const key = "datalynk-gps-device-id";
|
|
4075
|
+
if (typeof localStorage != "undefined") {
|
|
4076
|
+
const existing = localStorage.getItem(key);
|
|
4077
|
+
if (existing) return existing;
|
|
4078
|
+
const id = this.createId();
|
|
4079
|
+
localStorage.setItem(key, id);
|
|
4080
|
+
return id;
|
|
4081
|
+
}
|
|
4082
|
+
return this.createId();
|
|
4083
|
+
}
|
|
4084
|
+
getTrackingId() {
|
|
4085
|
+
var _a;
|
|
4086
|
+
if (!this.options) return this.trackerId || this.createId();
|
|
4087
|
+
if (this.options.trackingId) return this.options.trackingId;
|
|
4088
|
+
const uid = (_a = this.api.jwtPayload) == null ? void 0 : _a.uid;
|
|
4089
|
+
return uid ? `user:${uid}:tracker:${this.trackerId}` : `device:${this.deviceId}:tracker:${this.trackerId}`;
|
|
4090
|
+
}
|
|
4091
|
+
isFresh(position) {
|
|
4092
|
+
if (!this.options) return false;
|
|
4093
|
+
if (this.lastPosition && position.timestamp <= this.lastPosition.timestamp) return false;
|
|
4094
|
+
return Date.now() - position.timestamp <= (this.options.maxStaleMs || 4e3);
|
|
4095
|
+
}
|
|
4096
|
+
metersBetween(a, b) {
|
|
4097
|
+
const earthRadius = 6371e3;
|
|
4098
|
+
const lat1 = a.coords.latitude * Math.PI / 180;
|
|
4099
|
+
const lat2 = b.coords.latitude * Math.PI / 180;
|
|
4100
|
+
const dLat = (b.coords.latitude - a.coords.latitude) * Math.PI / 180;
|
|
4101
|
+
const dLng = (b.coords.longitude - a.coords.longitude) * Math.PI / 180;
|
|
4102
|
+
const x = Math.sin(dLat / 2) * Math.sin(dLat / 2) + Math.cos(lat1) * Math.cos(lat2) * Math.sin(dLng / 2) * Math.sin(dLng / 2);
|
|
4103
|
+
return earthRadius * 2 * Math.atan2(Math.sqrt(x), Math.sqrt(1 - x));
|
|
4104
|
+
}
|
|
4105
|
+
normalizeNullableNumber(value, field) {
|
|
4106
|
+
if (value == null || value === "") return null;
|
|
4107
|
+
const number = Number(value);
|
|
4108
|
+
if (!Number.isFinite(number)) throw new Error(`GPS ${field} must be a valid number`);
|
|
4109
|
+
return number;
|
|
4110
|
+
}
|
|
4111
|
+
normalizeOptionalNumber(value, field) {
|
|
4112
|
+
if (value == null || value === "") return void 0;
|
|
4113
|
+
const number = Number(value);
|
|
4114
|
+
if (!Number.isFinite(number)) throw new Error(`GPS ${field} must be a valid number`);
|
|
4115
|
+
return number;
|
|
4116
|
+
}
|
|
4117
|
+
normalizePosition(position) {
|
|
4118
|
+
const lat = Number(position.lat ?? position.latitude);
|
|
4119
|
+
const lng = Number(position.lng ?? position.lon ?? position.longitude);
|
|
4120
|
+
if (!Number.isFinite(lat) || lat < -90 || lat > 90) {
|
|
4121
|
+
throw new Error("GPS position must include a valid lat/latitude between -90 and 90");
|
|
4122
|
+
}
|
|
4123
|
+
if (!Number.isFinite(lng) || lng < -180 || lng > 180) {
|
|
4124
|
+
throw new Error("GPS position must include a valid lng/lon/longitude between -180 and 180");
|
|
4125
|
+
}
|
|
4126
|
+
const accuracy = this.normalizeNullableNumber(position.accuracy, "accuracy");
|
|
4127
|
+
if (accuracy != null && accuracy < 0) throw new Error("GPS accuracy must be zero or greater");
|
|
4128
|
+
const altitude = this.normalizeNullableNumber(position.altitude, "altitude");
|
|
4129
|
+
const altitudeAccuracy = this.normalizeNullableNumber(position.altitudeAccuracy, "altitudeAccuracy");
|
|
4130
|
+
if (altitudeAccuracy != null && altitudeAccuracy < 0) throw new Error("GPS altitudeAccuracy must be zero or greater");
|
|
4131
|
+
const heading = this.normalizeNullableNumber(position.heading, "heading");
|
|
4132
|
+
if (heading != null && (heading < 0 || heading >= 360)) throw new Error("GPS heading must be between 0 and 360");
|
|
4133
|
+
const speed = this.normalizeNullableNumber(position.speed, "speed");
|
|
4134
|
+
if (speed != null && speed < 0) throw new Error("GPS speed must be zero or greater");
|
|
4135
|
+
const timestampMs = this.normalizeOptionalNumber(position.timestampMs, "timestampMs") ?? Date.now();
|
|
4136
|
+
if (timestampMs <= 0) throw new Error("GPS timestampMs must be greater than zero");
|
|
4137
|
+
let timestamp = position.timestamp;
|
|
4138
|
+
if (timestamp) {
|
|
4139
|
+
const parsed = Date.parse(timestamp);
|
|
4140
|
+
if (!Number.isFinite(parsed)) throw new Error("GPS timestamp must be a valid date string");
|
|
4141
|
+
} else {
|
|
4142
|
+
timestamp = new Date(timestampMs).toISOString();
|
|
4143
|
+
}
|
|
4144
|
+
return {
|
|
4145
|
+
lat,
|
|
4146
|
+
lng,
|
|
4147
|
+
accuracy,
|
|
4148
|
+
altitude,
|
|
4149
|
+
altitudeAccuracy,
|
|
4150
|
+
heading,
|
|
4151
|
+
speed,
|
|
4152
|
+
timestamp,
|
|
4153
|
+
timestampMs
|
|
4154
|
+
};
|
|
4155
|
+
}
|
|
4156
|
+
restartWatch() {
|
|
4157
|
+
if (!this.options || !this.started || typeof navigator == "undefined" || !navigator.geolocation) return;
|
|
4158
|
+
if (this.watchId != null) navigator.geolocation.clearWatch(this.watchId);
|
|
4159
|
+
this.watchId = void 0;
|
|
4160
|
+
this.startWatch();
|
|
4161
|
+
}
|
|
4162
|
+
sendPayload(payload, waitForAck = false) {
|
|
4163
|
+
var _a, _b;
|
|
4164
|
+
if (!this.options) return Promise.resolve({ ok: false, error: "Datalynk GPS is not configured" });
|
|
4165
|
+
this.connectSocket();
|
|
4166
|
+
const seq = ++this.seq;
|
|
4167
|
+
const message = {
|
|
4168
|
+
gps: {
|
|
4169
|
+
slice: this.options.slice,
|
|
4170
|
+
field: this.options.field || "coords",
|
|
4171
|
+
trackingId: this.getTrackingId(),
|
|
4172
|
+
trackerId: this.trackerId,
|
|
4173
|
+
sessionId: this.sessionId,
|
|
4174
|
+
deviceId: this.deviceId,
|
|
4175
|
+
seq,
|
|
4176
|
+
client: this.getClientDetails(),
|
|
4177
|
+
sentAt: (/* @__PURE__ */ new Date()).toISOString(),
|
|
4178
|
+
gpsTimestamp: ((_a = payload.position) == null ? void 0 : _a.timestamp) || null,
|
|
4179
|
+
label: this.options.label,
|
|
4180
|
+
source: this.options.source,
|
|
4181
|
+
...payload
|
|
4182
|
+
}
|
|
4183
|
+
};
|
|
4184
|
+
let ackPromise;
|
|
4185
|
+
if (waitForAck) {
|
|
4186
|
+
const timeoutMs = this.options.ackTimeoutMs ?? _Gps.GPS_ACK_TIMEOUT_MS;
|
|
4187
|
+
ackPromise = new Promise((resolve) => {
|
|
4188
|
+
const timeout = setTimeout(() => {
|
|
4189
|
+
this.ackResolvers.delete(seq);
|
|
4190
|
+
resolve({ ok: false, error: "GPS ack timeout", seq });
|
|
4191
|
+
}, timeoutMs);
|
|
4192
|
+
this.ackResolvers.set(seq, { resolve, timeout });
|
|
4193
|
+
});
|
|
4194
|
+
}
|
|
4195
|
+
if (!((_b = this.socket) == null ? void 0 : _b.open)) {
|
|
4196
|
+
this.pendingPayloads.push(message);
|
|
4197
|
+
this.pendingPayloads = this.pendingPayloads.slice(-25);
|
|
4198
|
+
return ackPromise ?? Promise.resolve({ ok: true, seq, pending: true });
|
|
4199
|
+
}
|
|
4200
|
+
this.socket.send(message);
|
|
4201
|
+
return ackPromise ?? Promise.resolve({ ok: true, seq, pending: false });
|
|
4202
|
+
}
|
|
4203
|
+
sendPosition(position, force) {
|
|
4204
|
+
this.sendPayload({
|
|
4205
|
+
force,
|
|
4206
|
+
position: this.serializePosition(position)
|
|
4207
|
+
});
|
|
4208
|
+
this.lastSentPosition = position;
|
|
4209
|
+
this.lastSentAt = Date.now();
|
|
4210
|
+
}
|
|
4211
|
+
serializePosition(position) {
|
|
4212
|
+
return {
|
|
4213
|
+
lat: position.coords.latitude,
|
|
4214
|
+
lng: position.coords.longitude,
|
|
4215
|
+
accuracy: position.coords.accuracy,
|
|
4216
|
+
altitude: position.coords.altitude,
|
|
4217
|
+
altitudeAccuracy: position.coords.altitudeAccuracy,
|
|
4218
|
+
heading: position.coords.heading,
|
|
4219
|
+
speed: position.coords.speed,
|
|
4220
|
+
timestamp: new Date(position.timestamp).toISOString(),
|
|
4221
|
+
timestampMs: position.timestamp
|
|
4222
|
+
};
|
|
4223
|
+
}
|
|
4224
|
+
shouldSendPosition(position, force) {
|
|
4225
|
+
if (!this.options) return false;
|
|
4226
|
+
if (force) return true;
|
|
4227
|
+
const accuracy = Number(position.coords.accuracy || 0);
|
|
4228
|
+
if (accuracy && accuracy > (this.options.maxAccuracyMeters || 200)) return false;
|
|
4229
|
+
if (!this.lastSentPosition) return true;
|
|
4230
|
+
const movedMeters = this.metersBetween(this.lastSentPosition, position);
|
|
4231
|
+
const accuracyImproved = Number(position.coords.accuracy || 999999) < Number(this.lastSentPosition.coords.accuracy || 999999);
|
|
4232
|
+
const heartbeatDue = Date.now() - this.lastSentAt >= (this.options.heartbeatMs || 5e3);
|
|
4233
|
+
return movedMeters >= (this.options.minDistanceMeters || 3) || accuracyImproved || heartbeatDue;
|
|
4234
|
+
}
|
|
4235
|
+
startHeartbeat() {
|
|
4236
|
+
if (!this.options || this.heartbeat) return;
|
|
4237
|
+
this.heartbeat = setInterval(() => {
|
|
4238
|
+
if (!this.started || !this.lastPosition) return;
|
|
4239
|
+
this.sendPosition(this.lastPosition, true);
|
|
4240
|
+
}, this.options.heartbeatMs || 5e3);
|
|
4241
|
+
}
|
|
4242
|
+
startWatch() {
|
|
4243
|
+
if (!this.options || this.watchId != null) return;
|
|
4244
|
+
this.watchStartedAt = Date.now();
|
|
4245
|
+
this.watchId = navigator.geolocation.watchPosition(this.onPosition, this.onPositionError, {
|
|
4246
|
+
enableHighAccuracy: this.options.highAccuracy !== false,
|
|
4247
|
+
maximumAge: 0,
|
|
4248
|
+
timeout: this.options.timeoutMs || 1e4
|
|
4249
|
+
});
|
|
4250
|
+
}
|
|
4251
|
+
startWatchdog() {
|
|
4252
|
+
if (!this.options || this.retryWatchdog) return;
|
|
4253
|
+
this.retryWatchdog = setInterval(() => {
|
|
4254
|
+
if (!this.started) return;
|
|
4255
|
+
const lastActivity = this.lastFixAt || this.watchStartedAt;
|
|
4256
|
+
if (lastActivity && Date.now() - lastActivity >= (this.options.watchdogMs || 6e4)) this.restartWatch();
|
|
4257
|
+
}, Math.min(this.options.watchdogMs || 6e4, 15e3));
|
|
4258
|
+
}
|
|
4259
|
+
startWhenAuthenticated() {
|
|
4260
|
+
if (!this.options) return;
|
|
4261
|
+
if (this.api.token) {
|
|
4262
|
+
setTimeout(() => {
|
|
4263
|
+
try {
|
|
4264
|
+
this.start();
|
|
4265
|
+
} catch (error) {
|
|
4266
|
+
console.warn("Datalynk GPS auto-start failed:", error);
|
|
4267
|
+
}
|
|
4268
|
+
}, 0);
|
|
4269
|
+
return;
|
|
4270
|
+
}
|
|
4271
|
+
const sub = this.api.token$.subscribe((token) => {
|
|
4272
|
+
if (!token) {
|
|
4273
|
+
if (this.started) this.stop();
|
|
4274
|
+
return;
|
|
4275
|
+
}
|
|
4276
|
+
if (this.started || !this.options || this.options.autoStart === false) return;
|
|
4277
|
+
try {
|
|
4278
|
+
this.start();
|
|
4279
|
+
} catch (error) {
|
|
4280
|
+
console.warn("Datalynk GPS auto-start failed:", error);
|
|
4281
|
+
}
|
|
4282
|
+
});
|
|
4283
|
+
this.unsubs.push(() => sub.unsubscribe());
|
|
4284
|
+
}
|
|
4285
|
+
};
|
|
4286
|
+
__publicField(_Gps, "GPS_ACK_TIMEOUT_MS", 15e3);
|
|
4287
|
+
let Gps = _Gps;
|
|
3894
4288
|
const _Api = class _Api {
|
|
3895
4289
|
/**
|
|
3896
4290
|
* Connect to Datalynk & send requests
|
|
@@ -3929,6 +4323,8 @@ const _Api = class _Api {
|
|
|
3929
4323
|
__publicField(this, "pwa");
|
|
3930
4324
|
/** Socket */
|
|
3931
4325
|
__publicField(this, "socket");
|
|
4326
|
+
/** GPS */
|
|
4327
|
+
__publicField(this, "gps");
|
|
3932
4328
|
/** Superuser */
|
|
3933
4329
|
__publicField(this, "superuser");
|
|
3934
4330
|
/** WebRTC */
|
|
@@ -3974,6 +4370,7 @@ const _Api = class _Api {
|
|
|
3974
4370
|
});
|
|
3975
4371
|
}
|
|
3976
4372
|
this.socket = new Socket(this, { url: options.socket });
|
|
4373
|
+
this.gps = new Gps(this, options.gps);
|
|
3977
4374
|
this.auth = new Auth(this);
|
|
3978
4375
|
this.files = new Files(this);
|
|
3979
4376
|
this.pdf = new Pdf(this);
|
|
@@ -4280,6 +4677,7 @@ export {
|
|
|
4280
4677
|
ApiCall,
|
|
4281
4678
|
Auth,
|
|
4282
4679
|
Files,
|
|
4680
|
+
Gps,
|
|
4283
4681
|
LoginPrompt,
|
|
4284
4682
|
PWA,
|
|
4285
4683
|
Pdf,
|
package/package.json
CHANGED
|
@@ -2,7 +2,7 @@
|
|
|
2
2
|
"name": "@auxilium/datalynk-client",
|
|
3
3
|
"description": "Datalynk client library",
|
|
4
4
|
"repository": "https://gitlab.auxiliumgroup.com/auxilium/datalynk/datalynk-client",
|
|
5
|
-
"version": "1.
|
|
5
|
+
"version": "1.4.1",
|
|
6
6
|
"author": "Zak Timson <zaktimson@gmail.com>",
|
|
7
7
|
"private": false,
|
|
8
8
|
"main": "./dist/index.cjs",
|