@dronedeploy/rocos-js-sdk 3.1.5-rc1 → 3.1.6

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.
@@ -43,6 +43,44 @@ export declare class CallerService extends BaseStreamService<ICallerStream> {
43
43
  ack$: Observable<IRocosCallerMessageResponseAck>;
44
44
  cancel: () => Promise<void>;
45
45
  };
46
+ /** Invoke a long-running action (ROS 2 action-server semantics) and expose its lifecycle safely.
47
+ *
48
+ * Use this instead of `call` when the request is *accepted* (ack) long before it
49
+ * *completes* (result) — e.g. a navigation goal or a mission. The semantics are:
50
+ * fire the request exactly once, hold the underlying stream open after the ack,
51
+ * and tear it down only when the action itself completes/errors.
52
+ *
53
+ * This differs from `call` in one crucial way: `call` builds `ack$`/`result$` on a
54
+ * refcounted shared source, so awaiting `ack$` (dropping the subscriber count to
55
+ * zero) and *then* subscribing to `result$` re-issues the goal. `callAction` pins the
56
+ * source with a single SDK-owned subscription for the action's lifetime, so:
57
+ * - the request fires once and is never re-issued by subscriber churn,
58
+ * - `ack` is a Promise (inherently replayable — await it in any order),
59
+ * - `result$` replays the one-shot result, so a late or out-of-order subscriber
60
+ * still receives it.
61
+ *
62
+ * Lifecycle:
63
+ * - `ack` resolves with the first acknowledgement; rejects if the action
64
+ * errors (or completes) before any ack arrives.
65
+ * - `result$` emits the single result and completes when the action ends. Replayed,
66
+ * so subscription order relative to awaiting `ack` does not matter.
67
+ * - `return$` streams live feedback/return payloads (decoded as `T`). Not replayed —
68
+ * subscribe before the action emits feedback to avoid missing it.
69
+ * - `cancel` asks the server to cancel; the action then emits its result and closes.
70
+ * - `close` stops tracking the action locally *without* cancelling it on the robot,
71
+ * releasing the underlying stream. For teardown paths (e.g. UI unmount)
72
+ * where the action should keep running but the result is no longer needed.
73
+ *
74
+ * @see call
75
+ * @see callRaw
76
+ */
77
+ callAction<T = unknown>(params: ICallerCallParams): {
78
+ ack: Promise<IRocosCallerMessageResponseAck>;
79
+ result$: Observable<IRocosCallerMessageResponseResult>;
80
+ return$: Observable<T>;
81
+ cancel: () => Promise<void>;
82
+ close: () => void;
83
+ };
46
84
  protected createStream(): Promise<ICallerStream>;
47
85
  protected getStream(config: IStreamConfig): ICallerStream;
48
86
  }
@@ -104,6 +104,62 @@ class CallerService extends BaseStreamService_1.BaseStreamService {
104
104
  cancel,
105
105
  };
106
106
  }
107
+ /** Invoke a long-running action (ROS 2 action-server semantics) and expose its lifecycle safely.
108
+ *
109
+ * Use this instead of `call` when the request is *accepted* (ack) long before it
110
+ * *completes* (result) — e.g. a navigation goal or a mission. The semantics are:
111
+ * fire the request exactly once, hold the underlying stream open after the ack,
112
+ * and tear it down only when the action itself completes/errors.
113
+ *
114
+ * This differs from `call` in one crucial way: `call` builds `ack$`/`result$` on a
115
+ * refcounted shared source, so awaiting `ack$` (dropping the subscriber count to
116
+ * zero) and *then* subscribing to `result$` re-issues the goal. `callAction` pins the
117
+ * source with a single SDK-owned subscription for the action's lifetime, so:
118
+ * - the request fires once and is never re-issued by subscriber churn,
119
+ * - `ack` is a Promise (inherently replayable — await it in any order),
120
+ * - `result$` replays the one-shot result, so a late or out-of-order subscriber
121
+ * still receives it.
122
+ *
123
+ * Lifecycle:
124
+ * - `ack` resolves with the first acknowledgement; rejects if the action
125
+ * errors (or completes) before any ack arrives.
126
+ * - `result$` emits the single result and completes when the action ends. Replayed,
127
+ * so subscription order relative to awaiting `ack` does not matter.
128
+ * - `return$` streams live feedback/return payloads (decoded as `T`). Not replayed —
129
+ * subscribe before the action emits feedback to avoid missing it.
130
+ * - `cancel` asks the server to cancel; the action then emits its result and closes.
131
+ * - `close` stops tracking the action locally *without* cancelling it on the robot,
132
+ * releasing the underlying stream. For teardown paths (e.g. UI unmount)
133
+ * where the action should keep running but the result is no longer needed.
134
+ *
135
+ * @see call
136
+ * @see callRaw
137
+ */
138
+ callAction(params) {
139
+ const stream = this.call(params);
140
+ // Replay the one-shot result so subscription order never matters and a late
141
+ // subscriber still receives it.
142
+ const result = new rxjs_1.ReplaySubject(1);
143
+ // One SDK-owned subscription pins the shared source alive for the whole action.
144
+ // It fires the request once (first subscriber) and forwards the result into the
145
+ // replay subject, completing/erroring it when the action ends. Because this
146
+ // subscription outlives any `firstValueFrom(ack)` teardown, subscriber churn can
147
+ // never reset the source and re-issue the goal.
148
+ const pin = stream.result$.subscribe(result);
149
+ // `ack` rejects if the action errors or completes before any ack arrives. Since
150
+ // callAction is meant to be consumed via result$/return$ too, a caller may never
151
+ // touch `ack` — without a handler that rejection would surface as an unhandled
152
+ // promise rejection. Awaiting `action.ack` still rejects for callers who do care.
153
+ const ack = (0, rxjs_1.firstValueFrom)(stream.ack$);
154
+ ack.catch(() => { });
155
+ return {
156
+ ack,
157
+ result$: result.asObservable(),
158
+ return$: stream.return$,
159
+ cancel: stream.cancel,
160
+ close: () => pin.unsubscribe(),
161
+ };
162
+ }
107
163
  async createStream() {
108
164
  return (await this.createStreamFromConfig(identifier_1.IDENTIFIER_NAME_CALLER, {
109
165
  url: this.config.url,
@@ -43,6 +43,44 @@ export declare class CallerService extends BaseStreamService<ICallerStream> {
43
43
  ack$: Observable<IRocosCallerMessageResponseAck>;
44
44
  cancel: () => Promise<void>;
45
45
  };
46
+ /** Invoke a long-running action (ROS 2 action-server semantics) and expose its lifecycle safely.
47
+ *
48
+ * Use this instead of `call` when the request is *accepted* (ack) long before it
49
+ * *completes* (result) — e.g. a navigation goal or a mission. The semantics are:
50
+ * fire the request exactly once, hold the underlying stream open after the ack,
51
+ * and tear it down only when the action itself completes/errors.
52
+ *
53
+ * This differs from `call` in one crucial way: `call` builds `ack$`/`result$` on a
54
+ * refcounted shared source, so awaiting `ack$` (dropping the subscriber count to
55
+ * zero) and *then* subscribing to `result$` re-issues the goal. `callAction` pins the
56
+ * source with a single SDK-owned subscription for the action's lifetime, so:
57
+ * - the request fires once and is never re-issued by subscriber churn,
58
+ * - `ack` is a Promise (inherently replayable — await it in any order),
59
+ * - `result$` replays the one-shot result, so a late or out-of-order subscriber
60
+ * still receives it.
61
+ *
62
+ * Lifecycle:
63
+ * - `ack` resolves with the first acknowledgement; rejects if the action
64
+ * errors (or completes) before any ack arrives.
65
+ * - `result$` emits the single result and completes when the action ends. Replayed,
66
+ * so subscription order relative to awaiting `ack` does not matter.
67
+ * - `return$` streams live feedback/return payloads (decoded as `T`). Not replayed —
68
+ * subscribe before the action emits feedback to avoid missing it.
69
+ * - `cancel` asks the server to cancel; the action then emits its result and closes.
70
+ * - `close` stops tracking the action locally *without* cancelling it on the robot,
71
+ * releasing the underlying stream. For teardown paths (e.g. UI unmount)
72
+ * where the action should keep running but the result is no longer needed.
73
+ *
74
+ * @see call
75
+ * @see callRaw
76
+ */
77
+ callAction<T = unknown>(params: ICallerCallParams): {
78
+ ack: Promise<IRocosCallerMessageResponseAck>;
79
+ result$: Observable<IRocosCallerMessageResponseResult>;
80
+ return$: Observable<T>;
81
+ cancel: () => Promise<void>;
82
+ close: () => void;
83
+ };
46
84
  protected createStream(): Promise<ICallerStream>;
47
85
  protected getStream(config: IStreamConfig): ICallerStream;
48
86
  }
@@ -1,5 +1,5 @@
1
1
  import { ResultStatus, RocosResponseLevel, } from '../models';
2
- import { from, lastValueFrom, map, mergeMap, share, switchMap, take, takeUntil, throwError } from 'rxjs';
2
+ import { ReplaySubject, firstValueFrom, from, lastValueFrom, map, mergeMap, share, switchMap, take, takeUntil, throwError, } from 'rxjs';
3
3
  import { catchError, filter } from 'rxjs/operators';
4
4
  import { getResponses, handleChunkedMessages } from '../helpers/callerMessageHelpers';
5
5
  import { BaseStreamService } from './BaseStreamService';
@@ -101,6 +101,62 @@ export class CallerService extends BaseStreamService {
101
101
  cancel,
102
102
  };
103
103
  }
104
+ /** Invoke a long-running action (ROS 2 action-server semantics) and expose its lifecycle safely.
105
+ *
106
+ * Use this instead of `call` when the request is *accepted* (ack) long before it
107
+ * *completes* (result) — e.g. a navigation goal or a mission. The semantics are:
108
+ * fire the request exactly once, hold the underlying stream open after the ack,
109
+ * and tear it down only when the action itself completes/errors.
110
+ *
111
+ * This differs from `call` in one crucial way: `call` builds `ack$`/`result$` on a
112
+ * refcounted shared source, so awaiting `ack$` (dropping the subscriber count to
113
+ * zero) and *then* subscribing to `result$` re-issues the goal. `callAction` pins the
114
+ * source with a single SDK-owned subscription for the action's lifetime, so:
115
+ * - the request fires once and is never re-issued by subscriber churn,
116
+ * - `ack` is a Promise (inherently replayable — await it in any order),
117
+ * - `result$` replays the one-shot result, so a late or out-of-order subscriber
118
+ * still receives it.
119
+ *
120
+ * Lifecycle:
121
+ * - `ack` resolves with the first acknowledgement; rejects if the action
122
+ * errors (or completes) before any ack arrives.
123
+ * - `result$` emits the single result and completes when the action ends. Replayed,
124
+ * so subscription order relative to awaiting `ack` does not matter.
125
+ * - `return$` streams live feedback/return payloads (decoded as `T`). Not replayed —
126
+ * subscribe before the action emits feedback to avoid missing it.
127
+ * - `cancel` asks the server to cancel; the action then emits its result and closes.
128
+ * - `close` stops tracking the action locally *without* cancelling it on the robot,
129
+ * releasing the underlying stream. For teardown paths (e.g. UI unmount)
130
+ * where the action should keep running but the result is no longer needed.
131
+ *
132
+ * @see call
133
+ * @see callRaw
134
+ */
135
+ callAction(params) {
136
+ const stream = this.call(params);
137
+ // Replay the one-shot result so subscription order never matters and a late
138
+ // subscriber still receives it.
139
+ const result = new ReplaySubject(1);
140
+ // One SDK-owned subscription pins the shared source alive for the whole action.
141
+ // It fires the request once (first subscriber) and forwards the result into the
142
+ // replay subject, completing/erroring it when the action ends. Because this
143
+ // subscription outlives any `firstValueFrom(ack)` teardown, subscriber churn can
144
+ // never reset the source and re-issue the goal.
145
+ const pin = stream.result$.subscribe(result);
146
+ // `ack` rejects if the action errors or completes before any ack arrives. Since
147
+ // callAction is meant to be consumed via result$/return$ too, a caller may never
148
+ // touch `ack` — without a handler that rejection would surface as an unhandled
149
+ // promise rejection. Awaiting `action.ack` still rejects for callers who do care.
150
+ const ack = firstValueFrom(stream.ack$);
151
+ ack.catch(() => { });
152
+ return {
153
+ ack,
154
+ result$: result.asObservable(),
155
+ return$: stream.return$,
156
+ cancel: stream.cancel,
157
+ close: () => pin.unsubscribe(),
158
+ };
159
+ }
104
160
  async createStream() {
105
161
  return (await this.createStreamFromConfig(IDENTIFIER_NAME_CALLER, {
106
162
  url: this.config.url,
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@dronedeploy/rocos-js-sdk",
3
- "version": "3.1.5-rc1",
3
+ "version": "3.1.6",
4
4
  "description": "Javascript SDK for rocos",
5
5
  "main": "cjs/index.js",
6
6
  "module": "esm/index.js",