@dronedeploy/rocos-js-sdk 4.4.2 → 4.4.3
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/cjs/api/StreamRegister.js +9 -3
- package/cjs/services/BaseStreamService.d.ts +1 -1
- package/cjs/services/BaseStreamService.js +21 -2
- package/cjs/services/TelemetryService.js +21 -14
- package/esm/api/StreamRegister.js +9 -3
- package/esm/services/BaseStreamService.d.ts +1 -1
- package/esm/services/BaseStreamService.js +21 -2
- package/esm/services/TelemetryService.js +22 -15
- package/package.json +1 -1
|
@@ -19,8 +19,10 @@ class StreamRegister {
|
|
|
19
19
|
}
|
|
20
20
|
addStream(stream) {
|
|
21
21
|
try {
|
|
22
|
-
|
|
23
|
-
|
|
22
|
+
// Stop any existing occupant of this key before overwriting it.
|
|
23
|
+
const existing = this.teleStreams.get(stream.identifier);
|
|
24
|
+
if (existing && existing !== stream) {
|
|
25
|
+
existing.stopStream();
|
|
24
26
|
}
|
|
25
27
|
this.teleStreams.set(stream.identifier, stream);
|
|
26
28
|
}
|
|
@@ -40,7 +42,11 @@ class StreamRegister {
|
|
|
40
42
|
removeStream(stream) {
|
|
41
43
|
try {
|
|
42
44
|
stream.stopStream();
|
|
43
|
-
this
|
|
45
|
+
// Only clear the entry if this instance still owns the key: a replacement may already hold it,
|
|
46
|
+
// and evicting that would leave subscribers forking duplicate streams for the scope.
|
|
47
|
+
if (this.teleStreams.get(stream.identifier) === stream) {
|
|
48
|
+
this.teleStreams.delete(stream.identifier);
|
|
49
|
+
}
|
|
44
50
|
}
|
|
45
51
|
catch (e) {
|
|
46
52
|
this.logger.error(`Failed to remove stream from list. identifier: ${stream.identifier}`, e);
|
|
@@ -10,7 +10,7 @@ export declare abstract class BaseStreamService<T extends IBaseStream, C extends
|
|
|
10
10
|
protected constructor(name: string, config: IRocosSDKConfig);
|
|
11
11
|
getStatus(): boolean;
|
|
12
12
|
protected initStream(stream: T): Promise<void>;
|
|
13
|
-
protected createStreamFromConfig(identifier: string, config: C): Promise<{
|
|
13
|
+
protected createStreamFromConfig(identifier: string, config: C, onAcquire?: (stream: T) => void): Promise<{
|
|
14
14
|
stream: T;
|
|
15
15
|
isNew: boolean;
|
|
16
16
|
}>;
|
|
@@ -32,7 +32,7 @@ class BaseStreamService {
|
|
|
32
32
|
}
|
|
33
33
|
authService.startTokenRefreshChecker();
|
|
34
34
|
}
|
|
35
|
-
async createStreamFromConfig(identifier, config) {
|
|
35
|
+
async createStreamFromConfig(identifier, config, onAcquire) {
|
|
36
36
|
const identifierWithScope = StreamRegister_1.StreamRegister.getIdentifier(identifier, config.scope);
|
|
37
37
|
const streamRegister = StreamRegister_1.StreamRegister.getInstance();
|
|
38
38
|
let stream = streamRegister.getStream(identifierWithScope);
|
|
@@ -45,7 +45,26 @@ class BaseStreamService {
|
|
|
45
45
|
this.status$.next(msg);
|
|
46
46
|
});
|
|
47
47
|
streamRegister.addStream(stream);
|
|
48
|
-
|
|
48
|
+
}
|
|
49
|
+
// Acquire synchronously with the lookup/registration above, before any await. A stream is
|
|
50
|
+
// stopped and deregistered the instant its refcount hits zero, so a deferred acquire can land on
|
|
51
|
+
// a dead instance — including a freshly-registered stream torn down during the init await below.
|
|
52
|
+
onAcquire?.(stream);
|
|
53
|
+
if (isNew) {
|
|
54
|
+
try {
|
|
55
|
+
await this.initStream(stream);
|
|
56
|
+
}
|
|
57
|
+
catch (e) {
|
|
58
|
+
// Evict eagerly, before the error reaches the subscriber, so a synchronous resubscribe
|
|
59
|
+
// can't re-acquire this uninitialised instance.
|
|
60
|
+
streamRegister.removeStream(stream);
|
|
61
|
+
throw e;
|
|
62
|
+
}
|
|
63
|
+
// If a concurrent teardown deregistered this stream during init, init() has opened a receiver
|
|
64
|
+
// on an instance nothing can reach; close it.
|
|
65
|
+
if (streamRegister.getStream(identifierWithScope) !== stream) {
|
|
66
|
+
stream.stopStream();
|
|
67
|
+
}
|
|
49
68
|
}
|
|
50
69
|
return { stream, isNew };
|
|
51
70
|
}
|
|
@@ -61,19 +61,26 @@ class TelemetryService extends BaseStreamService_1.BaseStreamService {
|
|
|
61
61
|
callsigns: callsignsLookup?.lookupType === models_1.CallsignsLookupType.List ? callsignsLookup.lookupValue : [],
|
|
62
62
|
sources,
|
|
63
63
|
};
|
|
64
|
-
|
|
65
|
-
|
|
66
|
-
|
|
67
|
-
|
|
68
|
-
|
|
69
|
-
|
|
70
|
-
|
|
71
|
-
}
|
|
72
|
-
|
|
64
|
+
// `defer` acquires the stream only when subscribed; `finalize` releases it on unsubscribe, error
|
|
65
|
+
// and complete — so acquire and release stay balanced on every path.
|
|
66
|
+
return (0, rxjs_1.defer)(() => {
|
|
67
|
+
let acquired;
|
|
68
|
+
const stream$ = this.createStream(params.projectId, callsignsLookup, sources, scope, (stream) => {
|
|
69
|
+
acquired = stream;
|
|
70
|
+
stream.addSubscription(subscriptionParams);
|
|
71
|
+
}).then((stream) => {
|
|
72
|
+
if (!this.statusSubscription) {
|
|
73
|
+
this.statusSubscription = stream.statusStream$.subscribe((msg) => {
|
|
74
|
+
this.status = msg === models_1.SubscriberStatusEnum.STOPPED || msg === models_1.SubscriberStatusEnum.ALIVE;
|
|
75
|
+
this.status$.next(msg);
|
|
76
|
+
});
|
|
77
|
+
}
|
|
78
|
+
return (0, rxjs_1.from)(stream.messageStream$);
|
|
79
|
+
});
|
|
80
|
+
return (0, rxjs_1.from)(stream$).pipe((0, rxjs_1.mergeAll)(), (0, operators_1.filter)((message) => {
|
|
81
|
+
return callsigns.includes(message.callsign) && sources.includes(message.source);
|
|
82
|
+
}), (0, operators_1.finalize)(() => acquired?.removeSubscription(subscriptionParams, params.terminateReceiverGroupOnUnsubscribe)));
|
|
73
83
|
});
|
|
74
|
-
return (0, rxjs_1.from)(stream).pipe((0, rxjs_1.mergeAll)(), (0, operators_1.filter)((message) => {
|
|
75
|
-
return callsigns.includes(message.callsign) && sources.includes(message.source);
|
|
76
|
-
}));
|
|
77
84
|
}
|
|
78
85
|
/**
|
|
79
86
|
* A method to keep track of the current subscriptions
|
|
@@ -152,7 +159,7 @@ class TelemetryService extends BaseStreamService_1.BaseStreamService {
|
|
|
152
159
|
return `${params.projectId}-default-${(0, flattenCallsignsLookup_1.flattenCallsignsLookup)(callsignsLookup).join()}`;
|
|
153
160
|
return `${params.projectId}-default`;
|
|
154
161
|
}
|
|
155
|
-
async createStream(projectId, callsignsLookup, sources, scope) {
|
|
162
|
+
async createStream(projectId, callsignsLookup, sources, scope, onAcquire) {
|
|
156
163
|
const newStream = await this.createStreamFromConfig(identifier_1.IDENTIFIER_NAME_TELEMETRY, {
|
|
157
164
|
url: this.config.url,
|
|
158
165
|
projectId,
|
|
@@ -165,7 +172,7 @@ class TelemetryService extends BaseStreamService_1.BaseStreamService {
|
|
|
165
172
|
insecure: this.config.insecure,
|
|
166
173
|
transport: this.config.transport,
|
|
167
174
|
fetch: this.config.fetch,
|
|
168
|
-
});
|
|
175
|
+
}, onAcquire);
|
|
169
176
|
if (!newStream.isNew) {
|
|
170
177
|
newStream.stream.statusStream$.subscribe((msg) => {
|
|
171
178
|
this.streamStatusSubject$.next({
|
|
@@ -16,8 +16,10 @@ export class StreamRegister {
|
|
|
16
16
|
}
|
|
17
17
|
addStream(stream) {
|
|
18
18
|
try {
|
|
19
|
-
|
|
20
|
-
|
|
19
|
+
// Stop any existing occupant of this key before overwriting it.
|
|
20
|
+
const existing = this.teleStreams.get(stream.identifier);
|
|
21
|
+
if (existing && existing !== stream) {
|
|
22
|
+
existing.stopStream();
|
|
21
23
|
}
|
|
22
24
|
this.teleStreams.set(stream.identifier, stream);
|
|
23
25
|
}
|
|
@@ -37,7 +39,11 @@ export class StreamRegister {
|
|
|
37
39
|
removeStream(stream) {
|
|
38
40
|
try {
|
|
39
41
|
stream.stopStream();
|
|
40
|
-
this
|
|
42
|
+
// Only clear the entry if this instance still owns the key: a replacement may already hold it,
|
|
43
|
+
// and evicting that would leave subscribers forking duplicate streams for the scope.
|
|
44
|
+
if (this.teleStreams.get(stream.identifier) === stream) {
|
|
45
|
+
this.teleStreams.delete(stream.identifier);
|
|
46
|
+
}
|
|
41
47
|
}
|
|
42
48
|
catch (e) {
|
|
43
49
|
this.logger.error(`Failed to remove stream from list. identifier: ${stream.identifier}`, e);
|
|
@@ -10,7 +10,7 @@ export declare abstract class BaseStreamService<T extends IBaseStream, C extends
|
|
|
10
10
|
protected constructor(name: string, config: IRocosSDKConfig);
|
|
11
11
|
getStatus(): boolean;
|
|
12
12
|
protected initStream(stream: T): Promise<void>;
|
|
13
|
-
protected createStreamFromConfig(identifier: string, config: C): Promise<{
|
|
13
|
+
protected createStreamFromConfig(identifier: string, config: C, onAcquire?: (stream: T) => void): Promise<{
|
|
14
14
|
stream: T;
|
|
15
15
|
isNew: boolean;
|
|
16
16
|
}>;
|
|
@@ -29,7 +29,7 @@ export class BaseStreamService {
|
|
|
29
29
|
}
|
|
30
30
|
authService.startTokenRefreshChecker();
|
|
31
31
|
}
|
|
32
|
-
async createStreamFromConfig(identifier, config) {
|
|
32
|
+
async createStreamFromConfig(identifier, config, onAcquire) {
|
|
33
33
|
const identifierWithScope = StreamRegister.getIdentifier(identifier, config.scope);
|
|
34
34
|
const streamRegister = StreamRegister.getInstance();
|
|
35
35
|
let stream = streamRegister.getStream(identifierWithScope);
|
|
@@ -42,7 +42,26 @@ export class BaseStreamService {
|
|
|
42
42
|
this.status$.next(msg);
|
|
43
43
|
});
|
|
44
44
|
streamRegister.addStream(stream);
|
|
45
|
-
|
|
45
|
+
}
|
|
46
|
+
// Acquire synchronously with the lookup/registration above, before any await. A stream is
|
|
47
|
+
// stopped and deregistered the instant its refcount hits zero, so a deferred acquire can land on
|
|
48
|
+
// a dead instance — including a freshly-registered stream torn down during the init await below.
|
|
49
|
+
onAcquire?.(stream);
|
|
50
|
+
if (isNew) {
|
|
51
|
+
try {
|
|
52
|
+
await this.initStream(stream);
|
|
53
|
+
}
|
|
54
|
+
catch (e) {
|
|
55
|
+
// Evict eagerly, before the error reaches the subscriber, so a synchronous resubscribe
|
|
56
|
+
// can't re-acquire this uninitialised instance.
|
|
57
|
+
streamRegister.removeStream(stream);
|
|
58
|
+
throw e;
|
|
59
|
+
}
|
|
60
|
+
// If a concurrent teardown deregistered this stream during init, init() has opened a receiver
|
|
61
|
+
// on an instance nothing can reach; close it.
|
|
62
|
+
if (streamRegister.getStream(identifierWithScope) !== stream) {
|
|
63
|
+
stream.stopStream();
|
|
64
|
+
}
|
|
46
65
|
}
|
|
47
66
|
return { stream, isNew };
|
|
48
67
|
}
|
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
import { BehaviorSubject, catchError, combineLatest, distinctUntilChanged, from, interval, map, mergeAll, of, startWith, } from 'rxjs';
|
|
1
|
+
import { BehaviorSubject, catchError, combineLatest, defer, distinctUntilChanged, from, interval, map, mergeAll, of, startWith, } from 'rxjs';
|
|
2
2
|
import { CallsignStatus, CallsignsLookup, CallsignsLookupType, RocosError, SubscriberStatusEnum, TelemetryMonitorStatus, errorCodes, } from '../models';
|
|
3
3
|
import { filter, finalize } from 'rxjs/operators';
|
|
4
4
|
import { BaseStreamService } from './BaseStreamService';
|
|
@@ -58,19 +58,26 @@ export class TelemetryService extends BaseStreamService {
|
|
|
58
58
|
callsigns: callsignsLookup?.lookupType === CallsignsLookupType.List ? callsignsLookup.lookupValue : [],
|
|
59
59
|
sources,
|
|
60
60
|
};
|
|
61
|
-
|
|
62
|
-
|
|
63
|
-
|
|
64
|
-
|
|
65
|
-
|
|
66
|
-
|
|
67
|
-
|
|
68
|
-
}
|
|
69
|
-
|
|
61
|
+
// `defer` acquires the stream only when subscribed; `finalize` releases it on unsubscribe, error
|
|
62
|
+
// and complete — so acquire and release stay balanced on every path.
|
|
63
|
+
return defer(() => {
|
|
64
|
+
let acquired;
|
|
65
|
+
const stream$ = this.createStream(params.projectId, callsignsLookup, sources, scope, (stream) => {
|
|
66
|
+
acquired = stream;
|
|
67
|
+
stream.addSubscription(subscriptionParams);
|
|
68
|
+
}).then((stream) => {
|
|
69
|
+
if (!this.statusSubscription) {
|
|
70
|
+
this.statusSubscription = stream.statusStream$.subscribe((msg) => {
|
|
71
|
+
this.status = msg === SubscriberStatusEnum.STOPPED || msg === SubscriberStatusEnum.ALIVE;
|
|
72
|
+
this.status$.next(msg);
|
|
73
|
+
});
|
|
74
|
+
}
|
|
75
|
+
return from(stream.messageStream$);
|
|
76
|
+
});
|
|
77
|
+
return from(stream$).pipe(mergeAll(), filter((message) => {
|
|
78
|
+
return callsigns.includes(message.callsign) && sources.includes(message.source);
|
|
79
|
+
}), finalize(() => acquired?.removeSubscription(subscriptionParams, params.terminateReceiverGroupOnUnsubscribe)));
|
|
70
80
|
});
|
|
71
|
-
return from(stream).pipe(mergeAll(), filter((message) => {
|
|
72
|
-
return callsigns.includes(message.callsign) && sources.includes(message.source);
|
|
73
|
-
}));
|
|
74
81
|
}
|
|
75
82
|
/**
|
|
76
83
|
* A method to keep track of the current subscriptions
|
|
@@ -149,7 +156,7 @@ export class TelemetryService extends BaseStreamService {
|
|
|
149
156
|
return `${params.projectId}-default-${flattenCallsignsLookup(callsignsLookup).join()}`;
|
|
150
157
|
return `${params.projectId}-default`;
|
|
151
158
|
}
|
|
152
|
-
async createStream(projectId, callsignsLookup, sources, scope) {
|
|
159
|
+
async createStream(projectId, callsignsLookup, sources, scope, onAcquire) {
|
|
153
160
|
const newStream = await this.createStreamFromConfig(IDENTIFIER_NAME_TELEMETRY, {
|
|
154
161
|
url: this.config.url,
|
|
155
162
|
projectId,
|
|
@@ -162,7 +169,7 @@ export class TelemetryService extends BaseStreamService {
|
|
|
162
169
|
insecure: this.config.insecure,
|
|
163
170
|
transport: this.config.transport,
|
|
164
171
|
fetch: this.config.fetch,
|
|
165
|
-
});
|
|
172
|
+
}, onAcquire);
|
|
166
173
|
if (!newStream.isNew) {
|
|
167
174
|
newStream.stream.statusStream$.subscribe((msg) => {
|
|
168
175
|
this.streamStatusSubject$.next({
|