@wandelbots/nova-js 4.5.0-pr.322.aa66640 → 4.5.0-pr.324.1203649
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/experimental/nats/index.d.mts +33 -41
- package/dist/experimental/nats/index.d.mts.map +1 -1
- package/dist/experimental/nats/index.mjs +28 -46
- package/dist/experimental/nats/index.mjs.map +1 -1
- package/package.json +1 -1
- package/src/experimental/nats/index.ts +10 -11
- package/src/lib/experimental/nats/NovaNatsClient.ts +91 -95
- package/src/lib/experimental/nats/generated/operations.ts +0 -20
|
@@ -1,6 +1,5 @@
|
|
|
1
1
|
import { t as Nova } from "../../Nova-DdbUm145.mjs";
|
|
2
|
-
import {
|
|
3
|
-
import { ConnectionOptions, Msg, NatsConnection } from "@nats-io/nats-core";
|
|
2
|
+
import { ConnectionOptions, MsgHdrs, NatsConnection } from "@nats-io/nats-core";
|
|
4
3
|
//#region src/lib/experimental/nats/buildNatsServerUrl.d.ts
|
|
5
4
|
/**
|
|
6
5
|
* Builds the WebSocket URL for a NOVA instance's NATS gateway from its
|
|
@@ -2859,23 +2858,6 @@ interface NatsSubscribePayloads {
|
|
|
2859
2858
|
"nova.v2.events.cells.{cell}.controllers.{controller}.deleted": RobotControllerDeletedEvent;
|
|
2860
2859
|
}
|
|
2861
2860
|
type NatsSubscribeSubject = keyof NatsSubscribePayloads;
|
|
2862
|
-
/**
|
|
2863
|
-
* The JetStream stream backing each subject that retains only its latest
|
|
2864
|
-
* message (`max_msgs_per_subject: 1` in src/asyncapi.yaml), so that
|
|
2865
|
-
* message is the subject's current state.
|
|
2866
|
-
*/
|
|
2867
|
-
declare const natsStreamBySubject: {
|
|
2868
|
-
readonly "nova.v2.cells.{cell}": "system-state";
|
|
2869
|
-
readonly "nova.v2.cells.{cell}.apps.{app}": "system-state";
|
|
2870
|
-
readonly "nova.v2.cells.{cell}.controllers.{controller}": "system-state";
|
|
2871
|
-
readonly "nova.v2.cells.{cell}.status": "system-state";
|
|
2872
|
-
readonly "nova.v2.system.status": "system-state";
|
|
2873
|
-
readonly "nova.v2.cells.{cell}.collision.setups.{setup}": "system-state";
|
|
2874
|
-
readonly "nova.v2.cells.{cell}.bus-ios.status": "system-state";
|
|
2875
|
-
readonly "nova.v2.cells.{cell}.controllers.{controller}.motion-groups.{motion-group}.description": "system-state";
|
|
2876
|
-
};
|
|
2877
|
-
/** Subjects whose current value can be replayed via `{ replayLast: true }`. */
|
|
2878
|
-
type NatsPersistedSubject = keyof typeof natsStreamBySubject;
|
|
2879
2861
|
/** Request payload types for subjects the client sends requests to. */
|
|
2880
2862
|
interface NatsRequestPayloads {
|
|
2881
2863
|
/**
|
|
@@ -3191,6 +3173,24 @@ type NatsPublishSubject = keyof NatsPublishPayloads;
|
|
|
3191
3173
|
//#endregion
|
|
3192
3174
|
//#region src/lib/experimental/nats/NovaNatsClient.d.ts
|
|
3193
3175
|
type NovaNatsClientConfig = ConnectionOptions;
|
|
3176
|
+
/**
|
|
3177
|
+
* The fields of a received message that exist on both a core NATS `Msg` and
|
|
3178
|
+
* a JetStream `JsMsg` — a subscription delivers the former by default and
|
|
3179
|
+
* the latter with `lastMessage: true` (see {@link NatsSubscribeOptions}),
|
|
3180
|
+
* so handlers see only what both have in common.
|
|
3181
|
+
*/
|
|
3182
|
+
interface NatsReceivedMsg {
|
|
3183
|
+
/** The concrete subject the message was published to. */
|
|
3184
|
+
subject: string;
|
|
3185
|
+
/** The message's raw payload bytes. */
|
|
3186
|
+
data: Uint8Array;
|
|
3187
|
+
/** Headers set by the server or the publisher, if any. */
|
|
3188
|
+
headers?: MsgHdrs;
|
|
3189
|
+
/** The payload decoded as JSON. */
|
|
3190
|
+
json<T>(): T;
|
|
3191
|
+
/** The payload decoded as a string. */
|
|
3192
|
+
string(): string;
|
|
3193
|
+
}
|
|
3194
3194
|
/**
|
|
3195
3195
|
* A received message, annotated with the values of the subject template's
|
|
3196
3196
|
* `{param}` placeholders as extracted from the message's concrete subject.
|
|
@@ -3198,27 +3198,21 @@ type NovaNatsClientConfig = ConnectionOptions;
|
|
|
3198
3198
|
* a handler knows which entity a message belongs to. Typed per subject via
|
|
3199
3199
|
* the generated `NatsOperationParams`.
|
|
3200
3200
|
*/
|
|
3201
|
-
type NatsSubscribeMsg<K extends NatsSubscribeSubject> =
|
|
3201
|
+
type NatsSubscribeMsg<K extends NatsSubscribeSubject> = NatsReceivedMsg & {
|
|
3202
3202
|
subjectParams: NatsOperationParams[K];
|
|
3203
3203
|
};
|
|
3204
|
-
type
|
|
3205
|
-
/**
|
|
3206
|
-
* Extra options for {@link NovaNatsClient.subscribe}. `replayLast` is only
|
|
3207
|
-
* offered on subjects the spec marks as retaining their latest message, so
|
|
3208
|
-
* asking for a replay that could never arrive is a compile error.
|
|
3209
|
-
*/
|
|
3210
|
-
type NatsSubscribeOptions<K extends NatsSubscribeSubject> = K extends NatsPersistedSubject ? {
|
|
3204
|
+
type NatsSubscribeOptions = {
|
|
3211
3205
|
/**
|
|
3212
|
-
*
|
|
3213
|
-
*
|
|
3214
|
-
*
|
|
3215
|
-
*
|
|
3206
|
+
* When true, the subscription immediately replays the last JetStream
|
|
3207
|
+
* message persisted for each subject matching the subscription (one per
|
|
3208
|
+
* concrete subject when subscribing with wildcards), then continues with
|
|
3209
|
+
* live messages. Requires the server to have a JetStream stream capturing
|
|
3210
|
+
* the subscribed subject; subscribing fails with an error if none exists.
|
|
3216
3211
|
*/
|
|
3217
|
-
|
|
3218
|
-
} : {
|
|
3219
|
-
replayLast?: never;
|
|
3212
|
+
lastMessage?: boolean;
|
|
3220
3213
|
};
|
|
3221
|
-
type
|
|
3214
|
+
type NatsMessageHandler<K extends NatsSubscribeSubject> = (payload: NatsSubscribePayloads[K], msg: NatsSubscribeMsg<K>) => void | Promise<void>;
|
|
3215
|
+
type SubscribeArgs<K extends NatsSubscribeSubject> = keyof NatsOperationParams[K] extends never ? [handler: NatsMessageHandler<K>, options?: NatsSubscribeOptions] : [params: NatsOperationParams[K], handler: NatsMessageHandler<K>, options?: NatsSubscribeOptions];
|
|
3222
3216
|
/**
|
|
3223
3217
|
* Typed NATS client for the Wandelbots NOVA messaging API, generated from
|
|
3224
3218
|
* src/asyncapi.yaml (see scripts/generate-nats-client.ts).
|
|
@@ -3256,11 +3250,9 @@ declare class NovaNatsClient {
|
|
|
3256
3250
|
* nats.subscribe("nova.v2.cells.{cell}.status", { cell: "*" },
|
|
3257
3251
|
* (services, msg) => console.log(msg.subjectParams.cell, services))
|
|
3258
3252
|
*
|
|
3259
|
-
*
|
|
3260
|
-
*
|
|
3261
|
-
*
|
|
3262
|
-
*
|
|
3263
|
-
* nats.subscribe("nova.v2.system.status", onStatus, { replayLast: true })
|
|
3253
|
+
* With `{ lastMessage: true }` as the final argument, the last JetStream
|
|
3254
|
+
* message persisted for each matching subject is replayed to the handler
|
|
3255
|
+
* immediately, before live messages (see {@link NatsSubscribeOptions}).
|
|
3264
3256
|
*
|
|
3265
3257
|
* Returns a function that unsubscribes when called.
|
|
3266
3258
|
*/
|
|
@@ -3287,5 +3279,5 @@ declare class NovaNatsClient {
|
|
|
3287
3279
|
publish<K extends NatsPublishSubject>(subject: K, params: NatsOperationParams[K], payload: NatsPublishPayloads[K]): Promise<void>;
|
|
3288
3280
|
}
|
|
3289
3281
|
//#endregion
|
|
3290
|
-
export { type AbbController, type AddVirtualControllerMotionGroupRequest, type App, type AppCreatedEvent, type AppDeletedEvent, type AppEventData, type AppUpdatedEvent, type Box, type BusIOsState, type BusIOsStateEnum, type Capacity, type Capsule, type CartesianLimits, type Cell, type CellCreatedEvent, type CellCycleEvent, type CellDeletedEvent, type CellDescription, type CellEventData, type CellName, type CellUpdatedEvent, type CloudEvent, type Collider, type ColliderDictionary, type ColliderDictionary1, type ColliderDictionary2, type CollisionMotionGroupLink, type CollisionMotionGroupTool, type CollisionMotionGroupTool1, type CollisionSetup, type ContainerEnvironment, type ContainerImage, type ContainerResources, type ContainerStorage, type ControllerNetworkInterface, type ConvexHull, type Cylinder, type DHParameter, type Execute, type Execute1, type FanucController, type IOBooleanValue, type IOFloatValue, type IOIntegerValue, type IOValue, type ImageCredentials, type JoggingDetails, type JoggingPausedByUser, type JoggingPausedNearCollision, type JoggingPausedNearJointLimit, type JoggingPausedNearSingularity, type JoggingPausedOnIO, type JoggingRunning, type JointLimits, type JointTypeEnum, type Joints, type Joints1, type Joints2, type Joints3, type KukaController, type LimitRange, type LimitSet, type LinkChain, type LinkChain1, type ListIOValuesResponse, type Location, type Manufacturer, type MotionGroupDescription, type MotionGroupFromJSON, type MotionGroupFromModel, type MotionGroupModel, type MotionGroupState, type MotionGroupState1, type MotionGroupState_JointLimitReached, type MotionGroupState_JointLimitReached1, type NatsErrorPayload, type NatsOperationParams, type
|
|
3282
|
+
export { type AbbController, type AddVirtualControllerMotionGroupRequest, type App, type AppCreatedEvent, type AppDeletedEvent, type AppEventData, type AppUpdatedEvent, type Box, type BusIOsState, type BusIOsStateEnum, type Capacity, type Capsule, type CartesianLimits, type Cell, type CellCreatedEvent, type CellCycleEvent, type CellDeletedEvent, type CellDescription, type CellEventData, type CellName, type CellUpdatedEvent, type CloudEvent, type Collider, type ColliderDictionary, type ColliderDictionary1, type ColliderDictionary2, type CollisionMotionGroupLink, type CollisionMotionGroupTool, type CollisionMotionGroupTool1, type CollisionSetup, type ContainerEnvironment, type ContainerImage, type ContainerResources, type ContainerStorage, type ControllerNetworkInterface, type ConvexHull, type Cylinder, type DHParameter, type Execute, type Execute1, type FanucController, type IOBooleanValue, type IOFloatValue, type IOIntegerValue, type IOValue, type ImageCredentials, type JoggingDetails, type JoggingPausedByUser, type JoggingPausedNearCollision, type JoggingPausedNearJointLimit, type JoggingPausedNearSingularity, type JoggingPausedOnIO, type JoggingRunning, type JointLimits, type JointTypeEnum, type Joints, type Joints1, type Joints2, type Joints3, type KukaController, type LimitRange, type LimitSet, type LinkChain, type LinkChain1, type ListIOValuesResponse, type Location, type Manufacturer, type MotionGroupDescription, type MotionGroupFromJSON, type MotionGroupFromModel, type MotionGroupModel, type MotionGroupState, type MotionGroupState1, type MotionGroupState_JointLimitReached, type MotionGroupState_JointLimitReached1, type NatsErrorPayload, type NatsOperationParams, type NatsPublishPayloads, type NatsPublishSubject, type NatsReceivedMsg, type NatsReplyPayloads, type NatsRequestPayloads, type NatsRequestSubject, type NatsSubscribeMsg, type NatsSubscribeOptions, type NatsSubscribePayloads, type NatsSubscribeSubject, type NetworkState, type NetworkStatusChangedEvent, NovaNatsClient, type NovaNatsClientConfig, type OperatingState, type OperationLimits, type OperationMode, type Payload, type PayloadDictionary, type Plane, type Pose, type Pose1, type Pose2, type Pose3, type Pose4, type Pose5, type ProgramRunState, type ProgramRunState1, type ProgramStatus, type Rectangle, type RectangularCapsule, type RobotController, type RobotControllerCreatedEvent, type RobotControllerDeletedEvent, type RobotControllerEventData, type RobotControllerState, type RobotControllerUpdatedEvent, type RobotSystemMode, type RotationVector, type SafetyStateType, type SafetyToolColliders, type SelectIOs, type ServiceGroup, type ServiceStatus, type ServiceStatusList, type ServiceStatusPhase, type ServiceStatusSeverity, type Sphere, type StreamIOValuesResponse, type SystemUpdateCompletedEvent, type SystemUpdateStartedEvent, type TcpOffset, type TcpOffsetDictionary, type TrajectoryDetails, type TrajectoryEnded, type TrajectoryPausedByUser, type TrajectoryPausedOnIO, type TrajectoryRunning, type TrajectoryWaitForIO, type UniversalrobotsController, type Vector3D, type Vector3D1, type Vector3D2, type VirtualController, type YaskawaController, buildNatsServerUrl };
|
|
3291
3283
|
//# sourceMappingURL=index.d.mts.map
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"index.d.mts","names":[],"sources":["../../../src/lib/experimental/nats/buildNatsServerUrl.ts","../../../src/lib/experimental/nats/generated/types.ts","../../../src/lib/experimental/nats/generated/operations.ts","../../../src/lib/experimental/nats/NovaNatsClient.ts"],"mappings":"
|
|
1
|
+
{"version":3,"file":"index.d.mts","names":[],"sources":["../../../src/lib/experimental/nats/buildNatsServerUrl.ts","../../../src/lib/experimental/nats/generated/types.ts","../../../src/lib/experimental/nats/generated/operations.ts","../../../src/lib/experimental/nats/NovaNatsClient.ts"],"mappings":"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"}
|
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
import { a as parseNovaInstanceUrl } from "../../converters-DnG1fX23.mjs";
|
|
2
|
-
import { DeliverPolicy, jetstream } from "@nats-io/jetstream";
|
|
2
|
+
import { DeliverPolicy, jetstream, jetstreamManager } from "@nats-io/jetstream";
|
|
3
3
|
import { wsconnect } from "@nats-io/nats-core";
|
|
4
4
|
//#region src/lib/experimental/nats/buildNatsServerUrl.ts
|
|
5
5
|
/**
|
|
@@ -15,23 +15,6 @@ function buildNatsServerUrl(instanceUrl) {
|
|
|
15
15
|
return `${url.protocol === "https:" ? "wss:" : "ws:"}//${url.host}/api/nats`;
|
|
16
16
|
}
|
|
17
17
|
//#endregion
|
|
18
|
-
//#region src/lib/experimental/nats/generated/operations.ts
|
|
19
|
-
/**
|
|
20
|
-
* The JetStream stream backing each subject that retains only its latest
|
|
21
|
-
* message (`max_msgs_per_subject: 1` in src/asyncapi.yaml), so that
|
|
22
|
-
* message is the subject's current state.
|
|
23
|
-
*/
|
|
24
|
-
const natsStreamBySubject = {
|
|
25
|
-
"nova.v2.cells.{cell}": "system-state",
|
|
26
|
-
"nova.v2.cells.{cell}.apps.{app}": "system-state",
|
|
27
|
-
"nova.v2.cells.{cell}.controllers.{controller}": "system-state",
|
|
28
|
-
"nova.v2.cells.{cell}.status": "system-state",
|
|
29
|
-
"nova.v2.system.status": "system-state",
|
|
30
|
-
"nova.v2.cells.{cell}.collision.setups.{setup}": "system-state",
|
|
31
|
-
"nova.v2.cells.{cell}.bus-ios.status": "system-state",
|
|
32
|
-
"nova.v2.cells.{cell}.controllers.{controller}.motion-groups.{motion-group}.description": "system-state"
|
|
33
|
-
};
|
|
34
|
-
//#endregion
|
|
35
18
|
//#region src/lib/experimental/nats/buildSubject.ts
|
|
36
19
|
/**
|
|
37
20
|
* Builds a NATS subject from an AsyncAPI-style channel address template
|
|
@@ -130,51 +113,50 @@ var NovaNatsClient = class {
|
|
|
130
113
|
* nats.subscribe("nova.v2.cells.{cell}.status", { cell: "*" },
|
|
131
114
|
* (services, msg) => console.log(msg.subjectParams.cell, services))
|
|
132
115
|
*
|
|
133
|
-
*
|
|
134
|
-
*
|
|
135
|
-
*
|
|
136
|
-
*
|
|
137
|
-
* nats.subscribe("nova.v2.system.status", onStatus, { replayLast: true })
|
|
116
|
+
* With `{ lastMessage: true }` as the final argument, the last JetStream
|
|
117
|
+
* message persisted for each matching subject is replayed to the handler
|
|
118
|
+
* immediately, before live messages (see {@link NatsSubscribeOptions}).
|
|
138
119
|
*
|
|
139
120
|
* Returns a function that unsubscribes when called.
|
|
140
121
|
*/
|
|
141
122
|
async subscribe(subject, ...args) {
|
|
142
|
-
const
|
|
143
|
-
|
|
144
|
-
|
|
145
|
-
|
|
123
|
+
const [params, handler, options] = typeof args[0] === "function" ? [
|
|
124
|
+
{},
|
|
125
|
+
args[0],
|
|
126
|
+
args[1]
|
|
127
|
+
] : [
|
|
128
|
+
args[0],
|
|
129
|
+
args[1],
|
|
130
|
+
args[2]
|
|
131
|
+
];
|
|
146
132
|
const nc = await this.connect();
|
|
147
133
|
const resolvedSubject = buildSubject(subject, params);
|
|
148
134
|
const paramPositions = [];
|
|
149
135
|
for (const [index, token] of subject.split(".").entries()) if (token.startsWith("{") && token.endsWith("}")) paramPositions.push([token.slice(1, -1), index]);
|
|
150
|
-
const
|
|
151
|
-
try {
|
|
152
|
-
const subjectTokens = msg.subject.split(".");
|
|
153
|
-
const subjectParams = Object.fromEntries(paramPositions.map(([name, index]) => [name, subjectTokens[index] ?? ""]));
|
|
154
|
-
await handler(msg.json(), Object.assign(msg, { subjectParams }));
|
|
155
|
-
} catch (err) {
|
|
156
|
-
console.error(`Error handling NATS message on subject "${resolvedSubject}"`, err);
|
|
157
|
-
}
|
|
158
|
-
};
|
|
159
|
-
const deliverAll = (messages) => {
|
|
136
|
+
const dispatch = (messages) => {
|
|
160
137
|
(async () => {
|
|
161
|
-
for await (const msg of messages)
|
|
138
|
+
for await (const msg of messages) try {
|
|
139
|
+
const subjectTokens = msg.subject.split(".");
|
|
140
|
+
const subjectParams = Object.fromEntries(paramPositions.map(([name, index]) => [name, subjectTokens[index] ?? ""]));
|
|
141
|
+
await handler(msg.json(), Object.assign(msg, { subjectParams }));
|
|
142
|
+
} catch (err) {
|
|
143
|
+
console.error(`Error handling NATS message on subject "${resolvedSubject}"`, err);
|
|
144
|
+
}
|
|
162
145
|
})().catch((err) => {
|
|
163
146
|
console.error(`NATS subscription iterator failed for "${resolvedSubject}"`, err);
|
|
164
147
|
});
|
|
165
148
|
};
|
|
166
|
-
if (
|
|
167
|
-
const
|
|
149
|
+
if (options?.lastMessage) {
|
|
150
|
+
const streamName = await (await jetstreamManager(nc)).streams.find(resolvedSubject);
|
|
151
|
+
const messages = await (await jetstream(nc).consumers.get(streamName, {
|
|
168
152
|
filter_subjects: [resolvedSubject],
|
|
169
153
|
deliver_policy: DeliverPolicy.LastPerSubject
|
|
170
154
|
})).consume();
|
|
171
|
-
|
|
172
|
-
return () =>
|
|
173
|
-
messages.stop();
|
|
174
|
-
};
|
|
155
|
+
dispatch(messages);
|
|
156
|
+
return () => void messages.close();
|
|
175
157
|
}
|
|
176
158
|
const sub = nc.subscribe(resolvedSubject);
|
|
177
|
-
|
|
159
|
+
dispatch(sub);
|
|
178
160
|
return () => sub.unsubscribe();
|
|
179
161
|
}
|
|
180
162
|
/**
|
|
@@ -205,6 +187,6 @@ var NovaNatsClient = class {
|
|
|
205
187
|
}
|
|
206
188
|
};
|
|
207
189
|
//#endregion
|
|
208
|
-
export { NovaNatsClient, buildNatsServerUrl
|
|
190
|
+
export { NovaNatsClient, buildNatsServerUrl };
|
|
209
191
|
|
|
210
192
|
//# sourceMappingURL=index.mjs.map
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"index.mjs","names":[],"sources":["../../../src/lib/experimental/nats/buildNatsServerUrl.ts","../../../src/lib/experimental/nats/generated/operations.ts","../../../src/lib/experimental/nats/buildSubject.ts","../../../src/lib/experimental/nats/NovaNatsClient.ts"],"sourcesContent":["import { parseNovaInstanceUrl } from \"../../converters.ts\"\n\n/**\n * Builds the WebSocket URL for a NOVA instance's NATS gateway from its\n * instance URL, e.g. `https://foo.instance.wandelbots.io` becomes\n * `wss://foo.instance.wandelbots.io/api/nats`.\n *\n * Pass the result as `servers` in the `NovaNatsClientConfig` passed to\n * `NovaNatsClient`.\n */\nexport function buildNatsServerUrl(instanceUrl: string): string {\n const url = parseNovaInstanceUrl(instanceUrl)\n const protocol = url.protocol === \"https:\" ? \"wss:\" : \"ws:\"\n return `${protocol}//${url.host}/api/nats`\n}\n","/**\n * AUTO-GENERATED FILE - DO NOT EDIT.\n * Generated from src/asyncapi.yaml by scripts/generate-nats-client.ts.\n * Run `pnpm generate:nats` to regenerate.\n */\n\nimport type {\n App,\n AppCreatedEvent,\n AppDeletedEvent,\n AppUpdatedEvent,\n BusIOsState,\n Cell,\n CellCreatedEvent,\n CellCycleEvent,\n CellDeletedEvent,\n CellUpdatedEvent,\n CollisionSetup,\n ListIOValuesResponse,\n MotionGroupDescription,\n NatsErrorPayload,\n NetworkStatusChangedEvent,\n ProgramStatus,\n RobotController,\n RobotControllerCreatedEvent,\n RobotControllerDeletedEvent,\n RobotControllerState,\n RobotControllerUpdatedEvent,\n SelectIOs,\n ServiceStatusList,\n StreamIOValuesResponse,\n SystemUpdateCompletedEvent,\n SystemUpdateStartedEvent,\n} from \"./types.ts\"\n\n/** Subject parameters required by each NATS subject, e.g. \"nova.v2.cells.{cell}\". */\nexport interface NatsOperationParams {\n /**\n * Cell Configuration\n *\n * Publishes the configuration for a cell.\n *\n * The latest status message is persisted NATS JetStream, documented in `x-nats-jetstream-stream`\n *\n * @operationId publishCell\n */\n \"nova.v2.cells.{cell}\": {\n /**\n * Unique identifier addressing a cell in all API calls.\n */\n cell: string\n }\n /**\n * App Configuration\n *\n * Publishes the configuration for a GUI application in the cell.\n *\n * The latest status message is persisted NATS JetStream, documented in `x-nats-jetstream-stream`\n *\n * @operationId publishApp\n */\n \"nova.v2.cells.{cell}.apps.{app}\": {\n /**\n * Unique identifier addressing a cell in all API calls.\n */\n cell: string\n /**\n * Name of the provided application.\n * Must be unique within the cell and is used as an identifier for addressing the application in all API calls, e.g., when updating the application.\n */\n app: string\n }\n /**\n * Program Status\n *\n * Publishes status messages for programs running in an app within a cell.\n * The status messages provide information about the current state of a program run.\n *\n * @operationId publishProgramStatus\n */\n \"nova.v2.cells.{cell}.programs\": {\n /**\n * Unique identifier addressing a cell in all API calls.\n */\n cell: string\n }\n /**\n * Robot Controller Configuration\n *\n * Publishes the configuration of a robot controller.\n *\n * @operationId publishRobotController\n */\n \"nova.v2.cells.{cell}.controllers.{controller}\": {\n /**\n * Unique identifier addressing a cell in all API calls.\n */\n cell: string\n /**\n * Unique identifier to address a controller in the cell.\n */\n controller: string\n }\n /**\n * Service Status\n *\n * Publishes the status of all cell resources.\n *\n * The latest status message is persisted NATS JetStream, documented in `x-nats-jetstream-stream`\n *\n * @operationId publishCellStatus\n */\n \"nova.v2.cells.{cell}.status\": {\n /**\n * Unique identifier addressing a cell in all API calls.\n */\n cell: string\n }\n /**\n * Cell Cycle Event\n *\n * Publishes the cycle events for a cell.\n *\n * @operationId publishCellCycle\n */\n \"nova.v2.cells.{cell}.cycle\": {\n /**\n * Unique identifier addressing a cell in all API calls.\n */\n cell: string\n }\n /**\n * Wandelbots NOVA status\n *\n * Publishes the status of all system services.\n *\n * The latest status message is persisted NATS JetStream, documented in `x-nats-jetstream-stream`\n *\n * @operationId publishSystemStatus\n */\n \"nova.v2.system.status\": Record<never, never>\n /**\n * Collision Setup\n *\n * Publishes the stored collision setup.\n *\n * The latest status message is persisted NATS JetStream, documented in `x-nats-jetstream-stream`\n *\n * @operationId publishCollisionSetup\n */\n \"nova.v2.cells.{cell}.collision.setups.{setup}\": {\n /**\n * Unique identifier addressing a cell in all API calls.\n */\n cell: string\n /**\n * Unique identifier addressing a collision setup.\n */\n setup: string\n }\n /**\n * BUS Inputs/Outputs Service Status\n *\n * Publishes the status of BUS inputs/outputs service.\n *\n * The latest status message is persisted NATS JetStream, documented in `x-nats-jetstream-stream`.\n *\n * @operationId publishBUSIOStatus\n */\n \"nova.v2.cells.{cell}.bus-ios.status\": {\n /**\n * Unique identifier addressing a cell in all API calls.\n */\n cell: string\n }\n /**\n * BUS Input/Output Values\n *\n * Publishes updates of BUS input/output values.\n *\n * @operationId publishBUSIOsIOs\n */\n \"nova.v2.cells.{cell}.bus-ios.ios\": {\n /**\n * Unique identifier addressing a cell in all API calls.\n */\n cell: string\n }\n /**\n * Set Output Values\n *\n * Set output values published with the BUS inputs/outputs service.\n * If you're using a virtual service, you can set inputs as well.\n *\n * @operationId setBUSIOsIOs\n */\n \"nova.v2.cells.{cell}.bus-ios.ios.set\": {\n /**\n * Unique identifier addressing a cell in all API calls.\n */\n cell: string\n }\n /**\n * Select Input/Output Values\n *\n * Select input/output values published by the controller.\n *\n * @operationId selectRobotControllerIOs\n */\n \"nova.v2.cells.{cell}.controllers.{controller}.ios.select\": {\n /**\n * Unique identifier addressing a cell in all API calls.\n */\n cell: string\n /**\n * Unique identifier to address a controller in a cell.\n */\n controller: string\n }\n /**\n * Input/Output Values\n *\n * Publishes updates of input/output values.\n *\n * @operationId publishRobotControllerIOs\n */\n \"nova.v2.cells.{cell}.controllers.{controller}.ios\": {\n /**\n * Unique identifier addressing a cell in all API calls.\n */\n cell: string\n /**\n * Unique identifier to address a controller in the cell.\n */\n controller: string\n }\n /**\n * State of Robot Controller\n *\n * Publishes the current state of a robot controller.\n *\n * @operationId publishRobotControllersState\n */\n \"nova.v2.cells.{cell}.controllers.{controller}.state\": {\n /**\n * Unique identifier addressing a cell in all API calls.\n */\n cell: string\n /**\n * Unique identifier to address a controller in the cell.\n */\n controller: string\n }\n /**\n * Description of Motion Group\n *\n * Publishes the description of a motion group, including TCPs, mounting, safety zones, limits, etc.\n *\n * @operationId publishMotionGroupDescription\n */\n \"nova.v2.cells.{cell}.controllers.{controller}.motion-groups.{motion-group}.description\": {\n /**\n * Unique identifier addressing a cell in all API calls.\n */\n cell: string\n /**\n * Unique identifier addressing a controller in the cell.\n */\n controller: string\n /**\n * Motion group identifier.\n */\n \"motion-group\": string\n }\n /**\n * System Update Started\n *\n * Publishes an event when a system update process is initiated.\n *\n * This event is triggered once the service-manager begins a system update process,\n * providing details about the update metadata, trigger information, and pre-update checks.\n *\n * The event follows CloudEvents v1.0 specification and is persisted in NATS JetStream\n * for reliable delivery and event replay capabilities.\n *\n * @operationId eventSystemUpdateStarted\n */\n \"nova.v2.events.system.update.started\": Record<never, never>\n /**\n * System Update Completed\n *\n * Publishes an event when a system update process is completed.\n *\n * This event is triggered once the service-manager completes a system update process,\n * providing comprehensive results including success status, component outcomes,\n * error details, and post-update validation results.\n *\n * The event follows CloudEvents v1.0 specification and is persisted in NATS JetStream\n * for reliable delivery and event replay capabilities.\n *\n * @operationId eventSystemUpdateCompleted\n */\n \"nova.v2.events.system.update.completed\": Record<never, never>\n /**\n * System Network Status Changed\n *\n * Publishes an event when a system network status changes.\n *\n * This event is triggered once system-info service detects a change in the system network status,\n * providing details about the new network state and related information.\n *\n * The event follows CloudEvents v1.0 specification and is persisted in NATS JetStream\n * for reliable delivery and event replay capabilities.\n *\n * @operationId eventSystemNetworkStatusChanged\n */\n \"nova.v2.events.system.network.status.changed\": Record<never, never>\n /**\n * Cell Created\n *\n * Publishes an event when a cell foundation release is created.\n *\n * The event follows CloudEvents v1.0 and is persisted in NATS JetStream for reliable delivery and replay capabilities.\n *\n * @operationId eventCellCreated\n */\n \"nova.v2.events.cells.{cell}.created\": {\n /**\n * Unique identifier addressing a cell in all API calls.\n */\n cell: string\n }\n /**\n * Cell Updated\n *\n * Publishes an event when a cell foundation release is updated.\n *\n * The event follows CloudEvents v1.0 and is persisted in NATS JetStream for reliable delivery and replay capabilities.\n *\n * @operationId eventCellUpdated\n */\n \"nova.v2.events.cells.{cell}.updated\": {\n /**\n * Unique identifier addressing a cell in all API calls.\n */\n cell: string\n }\n /**\n * Cell Deleted\n *\n * Publishes an event when a cell foundation release is deleted.\n *\n * The event follows CloudEvents v1.0 and is persisted in NATS JetStream for reliable delivery and replay capabilities.\n *\n * @operationId eventCellDeleted\n */\n \"nova.v2.events.cells.{cell}.deleted\": {\n /**\n * Unique identifier addressing a cell in all API calls.\n */\n cell: string\n }\n /**\n * App Created\n *\n * Publishes an event when an app release is created in a cell.\n *\n * The event follows CloudEvents v1.0 and is persisted in NATS JetStream for reliable delivery and replay capabilities.\n *\n * @operationId eventAppCreated\n */\n \"nova.v2.events.cells.{cell}.apps.{app}.created\": {\n /**\n * Unique identifier addressing a cell in all API calls.\n */\n cell: string\n /**\n * Unique identifier addressing an app in the cell.\n */\n app: string\n }\n /**\n * App Updated\n *\n * Publishes an event when an app release is updated in a cell.\n *\n * The event follows CloudEvents v1.0 and is persisted in NATS JetStream for reliable delivery and replay capabilities.\n *\n * @operationId eventAppUpdated\n */\n \"nova.v2.events.cells.{cell}.apps.{app}.updated\": {\n /**\n * Unique identifier addressing a cell in all API calls.\n */\n cell: string\n /**\n * Unique identifier addressing an app in the cell.\n */\n app: string\n }\n /**\n * App Deleted\n *\n * Publishes an event when an app release is deleted from a cell.\n *\n * The event follows CloudEvents v1.0 and is persisted in NATS JetStream for reliable delivery and replay capabilities.\n *\n * @operationId eventAppDeleted\n */\n \"nova.v2.events.cells.{cell}.apps.{app}.deleted\": {\n /**\n * Unique identifier addressing a cell in all API calls.\n */\n cell: string\n /**\n * Unique identifier addressing an app in the cell.\n */\n app: string\n }\n /**\n * Robot Controller Created\n *\n * Publishes an event when a robot controller release is created in a cell.\n *\n * The event follows CloudEvents v1.0 and is persisted in NATS JetStream for reliable delivery and replay capabilities.\n *\n * @operationId eventRobotControllerCreated\n */\n \"nova.v2.events.cells.{cell}.controllers.{controller}.created\": {\n /**\n * Unique identifier addressing a cell in all API calls.\n */\n cell: string\n /**\n * Unique identifier to address a controller in the cell.\n */\n controller: string\n }\n /**\n * Robot Controller Updated\n *\n * Publishes an event when a robot controller release is updated in a cell.\n *\n * The event follows CloudEvents v1.0 and is persisted in NATS JetStream for reliable delivery and replay capabilities.\n *\n * @operationId eventRobotControllerUpdated\n */\n \"nova.v2.events.cells.{cell}.controllers.{controller}.updated\": {\n /**\n * Unique identifier addressing a cell in all API calls.\n */\n cell: string\n /**\n * Unique identifier to address a controller in the cell.\n */\n controller: string\n }\n /**\n * Robot Controller Deleted\n *\n * Publishes an event when a robot controller release is deleted from a cell.\n *\n * The event follows CloudEvents v1.0 and is persisted in NATS JetStream for reliable delivery and replay capabilities.\n *\n * @operationId eventRobotControllerDeleted\n */\n \"nova.v2.events.cells.{cell}.controllers.{controller}.deleted\": {\n /**\n * Unique identifier addressing a cell in all API calls.\n */\n cell: string\n /**\n * Unique identifier to address a controller in the cell.\n */\n controller: string\n }\n}\n\n/** Payload types for subjects the server publishes and the client subscribes to. */\nexport interface NatsSubscribePayloads {\n /**\n * Cell Configuration\n *\n * Publishes the configuration for a cell.\n *\n * The latest status message is persisted NATS JetStream, documented in `x-nats-jetstream-stream`\n *\n * @operationId publishCell\n */\n \"nova.v2.cells.{cell}\": Cell\n /**\n * App Configuration\n *\n * Publishes the configuration for a GUI application in the cell.\n *\n * The latest status message is persisted NATS JetStream, documented in `x-nats-jetstream-stream`\n *\n * @operationId publishApp\n */\n \"nova.v2.cells.{cell}.apps.{app}\": App\n /**\n * Program Status\n *\n * Publishes status messages for programs running in an app within a cell.\n * The status messages provide information about the current state of a program run.\n *\n * @operationId publishProgramStatus\n */\n \"nova.v2.cells.{cell}.programs\": ProgramStatus\n /**\n * Robot Controller Configuration\n *\n * Publishes the configuration of a robot controller.\n *\n * @operationId publishRobotController\n */\n \"nova.v2.cells.{cell}.controllers.{controller}\": RobotController\n /**\n * Service Status\n *\n * Publishes the status of all cell resources.\n *\n * The latest status message is persisted NATS JetStream, documented in `x-nats-jetstream-stream`\n *\n * @operationId publishCellStatus\n */\n \"nova.v2.cells.{cell}.status\": ServiceStatusList\n /**\n * Cell Cycle Event\n *\n * Publishes the cycle events for a cell.\n *\n * @operationId publishCellCycle\n */\n \"nova.v2.cells.{cell}.cycle\": CellCycleEvent\n /**\n * Wandelbots NOVA status\n *\n * Publishes the status of all system services.\n *\n * The latest status message is persisted NATS JetStream, documented in `x-nats-jetstream-stream`\n *\n * @operationId publishSystemStatus\n */\n \"nova.v2.system.status\": ServiceStatusList\n /**\n * Collision Setup\n *\n * Publishes the stored collision setup.\n *\n * The latest status message is persisted NATS JetStream, documented in `x-nats-jetstream-stream`\n *\n * @operationId publishCollisionSetup\n */\n \"nova.v2.cells.{cell}.collision.setups.{setup}\": CollisionSetup\n /**\n * BUS Inputs/Outputs Service Status\n *\n * Publishes the status of BUS inputs/outputs service.\n *\n * The latest status message is persisted NATS JetStream, documented in `x-nats-jetstream-stream`.\n *\n * @operationId publishBUSIOStatus\n */\n \"nova.v2.cells.{cell}.bus-ios.status\": BusIOsState\n /**\n * BUS Input/Output Values\n *\n * Publishes updates of BUS input/output values.\n *\n * @operationId publishBUSIOsIOs\n */\n \"nova.v2.cells.{cell}.bus-ios.ios\": ListIOValuesResponse\n /**\n * Input/Output Values\n *\n * Publishes updates of input/output values.\n *\n * @operationId publishRobotControllerIOs\n */\n \"nova.v2.cells.{cell}.controllers.{controller}.ios\": StreamIOValuesResponse\n /**\n * State of Robot Controller\n *\n * Publishes the current state of a robot controller.\n *\n * @operationId publishRobotControllersState\n */\n \"nova.v2.cells.{cell}.controllers.{controller}.state\": RobotControllerState\n /**\n * Description of Motion Group\n *\n * Publishes the description of a motion group, including TCPs, mounting, safety zones, limits, etc.\n *\n * @operationId publishMotionGroupDescription\n */\n \"nova.v2.cells.{cell}.controllers.{controller}.motion-groups.{motion-group}.description\": MotionGroupDescription\n /**\n * System Update Started\n *\n * Publishes an event when a system update process is initiated.\n *\n * This event is triggered once the service-manager begins a system update process,\n * providing details about the update metadata, trigger information, and pre-update checks.\n *\n * The event follows CloudEvents v1.0 specification and is persisted in NATS JetStream\n * for reliable delivery and event replay capabilities.\n *\n * @operationId eventSystemUpdateStarted\n */\n \"nova.v2.events.system.update.started\": SystemUpdateStartedEvent\n /**\n * System Update Completed\n *\n * Publishes an event when a system update process is completed.\n *\n * This event is triggered once the service-manager completes a system update process,\n * providing comprehensive results including success status, component outcomes,\n * error details, and post-update validation results.\n *\n * The event follows CloudEvents v1.0 specification and is persisted in NATS JetStream\n * for reliable delivery and event replay capabilities.\n *\n * @operationId eventSystemUpdateCompleted\n */\n \"nova.v2.events.system.update.completed\": SystemUpdateCompletedEvent\n /**\n * System Network Status Changed\n *\n * Publishes an event when a system network status changes.\n *\n * This event is triggered once system-info service detects a change in the system network status,\n * providing details about the new network state and related information.\n *\n * The event follows CloudEvents v1.0 specification and is persisted in NATS JetStream\n * for reliable delivery and event replay capabilities.\n *\n * @operationId eventSystemNetworkStatusChanged\n */\n \"nova.v2.events.system.network.status.changed\": NetworkStatusChangedEvent\n /**\n * Cell Created\n *\n * Publishes an event when a cell foundation release is created.\n *\n * The event follows CloudEvents v1.0 and is persisted in NATS JetStream for reliable delivery and replay capabilities.\n *\n * @operationId eventCellCreated\n */\n \"nova.v2.events.cells.{cell}.created\": CellCreatedEvent\n /**\n * Cell Updated\n *\n * Publishes an event when a cell foundation release is updated.\n *\n * The event follows CloudEvents v1.0 and is persisted in NATS JetStream for reliable delivery and replay capabilities.\n *\n * @operationId eventCellUpdated\n */\n \"nova.v2.events.cells.{cell}.updated\": CellUpdatedEvent\n /**\n * Cell Deleted\n *\n * Publishes an event when a cell foundation release is deleted.\n *\n * The event follows CloudEvents v1.0 and is persisted in NATS JetStream for reliable delivery and replay capabilities.\n *\n * @operationId eventCellDeleted\n */\n \"nova.v2.events.cells.{cell}.deleted\": CellDeletedEvent\n /**\n * App Created\n *\n * Publishes an event when an app release is created in a cell.\n *\n * The event follows CloudEvents v1.0 and is persisted in NATS JetStream for reliable delivery and replay capabilities.\n *\n * @operationId eventAppCreated\n */\n \"nova.v2.events.cells.{cell}.apps.{app}.created\": AppCreatedEvent\n /**\n * App Updated\n *\n * Publishes an event when an app release is updated in a cell.\n *\n * The event follows CloudEvents v1.0 and is persisted in NATS JetStream for reliable delivery and replay capabilities.\n *\n * @operationId eventAppUpdated\n */\n \"nova.v2.events.cells.{cell}.apps.{app}.updated\": AppUpdatedEvent\n /**\n * App Deleted\n *\n * Publishes an event when an app release is deleted from a cell.\n *\n * The event follows CloudEvents v1.0 and is persisted in NATS JetStream for reliable delivery and replay capabilities.\n *\n * @operationId eventAppDeleted\n */\n \"nova.v2.events.cells.{cell}.apps.{app}.deleted\": AppDeletedEvent\n /**\n * Robot Controller Created\n *\n * Publishes an event when a robot controller release is created in a cell.\n *\n * The event follows CloudEvents v1.0 and is persisted in NATS JetStream for reliable delivery and replay capabilities.\n *\n * @operationId eventRobotControllerCreated\n */\n \"nova.v2.events.cells.{cell}.controllers.{controller}.created\": RobotControllerCreatedEvent\n /**\n * Robot Controller Updated\n *\n * Publishes an event when a robot controller release is updated in a cell.\n *\n * The event follows CloudEvents v1.0 and is persisted in NATS JetStream for reliable delivery and replay capabilities.\n *\n * @operationId eventRobotControllerUpdated\n */\n \"nova.v2.events.cells.{cell}.controllers.{controller}.updated\": RobotControllerUpdatedEvent\n /**\n * Robot Controller Deleted\n *\n * Publishes an event when a robot controller release is deleted from a cell.\n *\n * The event follows CloudEvents v1.0 and is persisted in NATS JetStream for reliable delivery and replay capabilities.\n *\n * @operationId eventRobotControllerDeleted\n */\n \"nova.v2.events.cells.{cell}.controllers.{controller}.deleted\": RobotControllerDeletedEvent\n}\n\nexport type NatsSubscribeSubject = keyof NatsSubscribePayloads\n\n/**\n * The JetStream stream backing each subject that retains only its latest\n * message (`max_msgs_per_subject: 1` in src/asyncapi.yaml), so that\n * message is the subject's current state.\n */\nexport const natsStreamBySubject = {\n \"nova.v2.cells.{cell}\": \"system-state\",\n \"nova.v2.cells.{cell}.apps.{app}\": \"system-state\",\n \"nova.v2.cells.{cell}.controllers.{controller}\": \"system-state\",\n \"nova.v2.cells.{cell}.status\": \"system-state\",\n \"nova.v2.system.status\": \"system-state\",\n \"nova.v2.cells.{cell}.collision.setups.{setup}\": \"system-state\",\n \"nova.v2.cells.{cell}.bus-ios.status\": \"system-state\",\n \"nova.v2.cells.{cell}.controllers.{controller}.motion-groups.{motion-group}.description\":\n \"system-state\",\n} as const\n\n/** Subjects whose current value can be replayed via `{ replayLast: true }`. */\nexport type NatsPersistedSubject = keyof typeof natsStreamBySubject\n\n/** Request payload types for subjects the client sends requests to. */\nexport interface NatsRequestPayloads {\n /**\n * Set Output Values\n *\n * Set output values published with the BUS inputs/outputs service.\n * If you're using a virtual service, you can set inputs as well.\n *\n * @operationId setBUSIOsIOs\n */\n \"nova.v2.cells.{cell}.bus-ios.ios.set\": ListIOValuesResponse\n /**\n * Select Input/Output Values\n *\n * Select input/output values published by the controller.\n *\n * @operationId selectRobotControllerIOs\n */\n \"nova.v2.cells.{cell}.controllers.{controller}.ios.select\": SelectIOs\n}\n\n/** Reply payload types for request/reply subjects. */\nexport interface NatsReplyPayloads {\n /**\n * Set Output Values\n *\n * Set output values published with the BUS inputs/outputs service.\n * If you're using a virtual service, you can set inputs as well.\n *\n * @operationId setBUSIOsIOs\n */\n \"nova.v2.cells.{cell}.bus-ios.ios.set\": NatsErrorPayload\n /**\n * Select Input/Output Values\n *\n * Select input/output values published by the controller.\n *\n * @operationId selectRobotControllerIOs\n */\n \"nova.v2.cells.{cell}.controllers.{controller}.ios.select\": NatsErrorPayload\n}\n\nexport type NatsRequestSubject = keyof NatsRequestPayloads\n\n/** Payload types for every subject defined in the spec, publishable via NovaNatsClient#publish. */\nexport interface NatsPublishPayloads {\n /**\n * Cell Configuration\n *\n * Publishes the configuration for a cell.\n *\n * The latest status message is persisted NATS JetStream, documented in `x-nats-jetstream-stream`\n *\n * @operationId publishCell\n */\n \"nova.v2.cells.{cell}\": Cell\n /**\n * App Configuration\n *\n * Publishes the configuration for a GUI application in the cell.\n *\n * The latest status message is persisted NATS JetStream, documented in `x-nats-jetstream-stream`\n *\n * @operationId publishApp\n */\n \"nova.v2.cells.{cell}.apps.{app}\": App\n /**\n * Program Status\n *\n * Publishes status messages for programs running in an app within a cell.\n * The status messages provide information about the current state of a program run.\n *\n * @operationId publishProgramStatus\n */\n \"nova.v2.cells.{cell}.programs\": ProgramStatus\n /**\n * Robot Controller Configuration\n *\n * Publishes the configuration of a robot controller.\n *\n * @operationId publishRobotController\n */\n \"nova.v2.cells.{cell}.controllers.{controller}\": RobotController\n /**\n * Service Status\n *\n * Publishes the status of all cell resources.\n *\n * The latest status message is persisted NATS JetStream, documented in `x-nats-jetstream-stream`\n *\n * @operationId publishCellStatus\n */\n \"nova.v2.cells.{cell}.status\": ServiceStatusList\n /**\n * Cell Cycle Event\n *\n * Publishes the cycle events for a cell.\n *\n * @operationId publishCellCycle\n */\n \"nova.v2.cells.{cell}.cycle\": CellCycleEvent\n /**\n * Wandelbots NOVA status\n *\n * Publishes the status of all system services.\n *\n * The latest status message is persisted NATS JetStream, documented in `x-nats-jetstream-stream`\n *\n * @operationId publishSystemStatus\n */\n \"nova.v2.system.status\": ServiceStatusList\n /**\n * Collision Setup\n *\n * Publishes the stored collision setup.\n *\n * The latest status message is persisted NATS JetStream, documented in `x-nats-jetstream-stream`\n *\n * @operationId publishCollisionSetup\n */\n \"nova.v2.cells.{cell}.collision.setups.{setup}\": CollisionSetup\n /**\n * BUS Inputs/Outputs Service Status\n *\n * Publishes the status of BUS inputs/outputs service.\n *\n * The latest status message is persisted NATS JetStream, documented in `x-nats-jetstream-stream`.\n *\n * @operationId publishBUSIOStatus\n */\n \"nova.v2.cells.{cell}.bus-ios.status\": BusIOsState\n /**\n * BUS Input/Output Values\n *\n * Publishes updates of BUS input/output values.\n *\n * @operationId publishBUSIOsIOs\n */\n \"nova.v2.cells.{cell}.bus-ios.ios\": ListIOValuesResponse\n /**\n * Set Output Values\n *\n * Set output values published with the BUS inputs/outputs service.\n * If you're using a virtual service, you can set inputs as well.\n *\n * @operationId setBUSIOsIOs\n */\n \"nova.v2.cells.{cell}.bus-ios.ios.set\": ListIOValuesResponse\n /**\n * Select Input/Output Values\n *\n * Select input/output values published by the controller.\n *\n * @operationId selectRobotControllerIOs\n */\n \"nova.v2.cells.{cell}.controllers.{controller}.ios.select\": SelectIOs\n /**\n * Input/Output Values\n *\n * Publishes updates of input/output values.\n *\n * @operationId publishRobotControllerIOs\n */\n \"nova.v2.cells.{cell}.controllers.{controller}.ios\": StreamIOValuesResponse\n /**\n * State of Robot Controller\n *\n * Publishes the current state of a robot controller.\n *\n * @operationId publishRobotControllersState\n */\n \"nova.v2.cells.{cell}.controllers.{controller}.state\": RobotControllerState\n /**\n * Description of Motion Group\n *\n * Publishes the description of a motion group, including TCPs, mounting, safety zones, limits, etc.\n *\n * @operationId publishMotionGroupDescription\n */\n \"nova.v2.cells.{cell}.controllers.{controller}.motion-groups.{motion-group}.description\": MotionGroupDescription\n /**\n * System Update Started\n *\n * Publishes an event when a system update process is initiated.\n *\n * This event is triggered once the service-manager begins a system update process,\n * providing details about the update metadata, trigger information, and pre-update checks.\n *\n * The event follows CloudEvents v1.0 specification and is persisted in NATS JetStream\n * for reliable delivery and event replay capabilities.\n *\n * @operationId eventSystemUpdateStarted\n */\n \"nova.v2.events.system.update.started\": SystemUpdateStartedEvent\n /**\n * System Update Completed\n *\n * Publishes an event when a system update process is completed.\n *\n * This event is triggered once the service-manager completes a system update process,\n * providing comprehensive results including success status, component outcomes,\n * error details, and post-update validation results.\n *\n * The event follows CloudEvents v1.0 specification and is persisted in NATS JetStream\n * for reliable delivery and event replay capabilities.\n *\n * @operationId eventSystemUpdateCompleted\n */\n \"nova.v2.events.system.update.completed\": SystemUpdateCompletedEvent\n /**\n * System Network Status Changed\n *\n * Publishes an event when a system network status changes.\n *\n * This event is triggered once system-info service detects a change in the system network status,\n * providing details about the new network state and related information.\n *\n * The event follows CloudEvents v1.0 specification and is persisted in NATS JetStream\n * for reliable delivery and event replay capabilities.\n *\n * @operationId eventSystemNetworkStatusChanged\n */\n \"nova.v2.events.system.network.status.changed\": NetworkStatusChangedEvent\n /**\n * Cell Created\n *\n * Publishes an event when a cell foundation release is created.\n *\n * The event follows CloudEvents v1.0 and is persisted in NATS JetStream for reliable delivery and replay capabilities.\n *\n * @operationId eventCellCreated\n */\n \"nova.v2.events.cells.{cell}.created\": CellCreatedEvent\n /**\n * Cell Updated\n *\n * Publishes an event when a cell foundation release is updated.\n *\n * The event follows CloudEvents v1.0 and is persisted in NATS JetStream for reliable delivery and replay capabilities.\n *\n * @operationId eventCellUpdated\n */\n \"nova.v2.events.cells.{cell}.updated\": CellUpdatedEvent\n /**\n * Cell Deleted\n *\n * Publishes an event when a cell foundation release is deleted.\n *\n * The event follows CloudEvents v1.0 and is persisted in NATS JetStream for reliable delivery and replay capabilities.\n *\n * @operationId eventCellDeleted\n */\n \"nova.v2.events.cells.{cell}.deleted\": CellDeletedEvent\n /**\n * App Created\n *\n * Publishes an event when an app release is created in a cell.\n *\n * The event follows CloudEvents v1.0 and is persisted in NATS JetStream for reliable delivery and replay capabilities.\n *\n * @operationId eventAppCreated\n */\n \"nova.v2.events.cells.{cell}.apps.{app}.created\": AppCreatedEvent\n /**\n * App Updated\n *\n * Publishes an event when an app release is updated in a cell.\n *\n * The event follows CloudEvents v1.0 and is persisted in NATS JetStream for reliable delivery and replay capabilities.\n *\n * @operationId eventAppUpdated\n */\n \"nova.v2.events.cells.{cell}.apps.{app}.updated\": AppUpdatedEvent\n /**\n * App Deleted\n *\n * Publishes an event when an app release is deleted from a cell.\n *\n * The event follows CloudEvents v1.0 and is persisted in NATS JetStream for reliable delivery and replay capabilities.\n *\n * @operationId eventAppDeleted\n */\n \"nova.v2.events.cells.{cell}.apps.{app}.deleted\": AppDeletedEvent\n /**\n * Robot Controller Created\n *\n * Publishes an event when a robot controller release is created in a cell.\n *\n * The event follows CloudEvents v1.0 and is persisted in NATS JetStream for reliable delivery and replay capabilities.\n *\n * @operationId eventRobotControllerCreated\n */\n \"nova.v2.events.cells.{cell}.controllers.{controller}.created\": RobotControllerCreatedEvent\n /**\n * Robot Controller Updated\n *\n * Publishes an event when a robot controller release is updated in a cell.\n *\n * The event follows CloudEvents v1.0 and is persisted in NATS JetStream for reliable delivery and replay capabilities.\n *\n * @operationId eventRobotControllerUpdated\n */\n \"nova.v2.events.cells.{cell}.controllers.{controller}.updated\": RobotControllerUpdatedEvent\n /**\n * Robot Controller Deleted\n *\n * Publishes an event when a robot controller release is deleted from a cell.\n *\n * The event follows CloudEvents v1.0 and is persisted in NATS JetStream for reliable delivery and replay capabilities.\n *\n * @operationId eventRobotControllerDeleted\n */\n \"nova.v2.events.cells.{cell}.controllers.{controller}.deleted\": RobotControllerDeletedEvent\n}\n\nexport type NatsPublishSubject = keyof NatsPublishPayloads\n","/**\n * Builds a NATS subject from an AsyncAPI-style channel address template\n * (e.g. `\"{instance}.v2.cells.{cell}\"`) by substituting each `{param}`\n * placeholder with the corresponding value from `params`.\n */\n\nfunction isValidSubjectChar(char: string): boolean {\n const code = char.charCodeAt(0)\n return (\n (code >= 48 && code <= 57) || // 0-9\n (code >= 65 && code <= 90) || // A-Z\n (code >= 97 && code <= 122) || // a-z\n char === \"-\" ||\n char === \"_\"\n )\n}\n\nfunction isValidSubjectValue(value: string): boolean {\n // A bare \"*\" is the NATS single-token wildcard, allowed on its own (e.g.\n // subscribing to all cells with `{ cell: \"*\" }\") but not as part of a\n // larger value, since it wouldn't act as a wildcard there anyway.\n if (value === \"*\") return true\n if (value.length === 0) return false\n for (const char of value) {\n if (!isValidSubjectChar(char)) return false\n }\n return true\n}\n\nexport function buildSubject(\n template: string,\n params: Record<string, string>,\n): string {\n // Scanned manually (rather than with a regex like /\\{([^}]+)\\}/g) to avoid\n // a polynomial-time backtracking blowup on pathological input, e.g. a\n // template consisting of many \"{\" characters with no closing \"}\".\n let result = \"\"\n let cursor = 0\n\n while (cursor < template.length) {\n const openIndex = template.indexOf(\"{\", cursor)\n if (openIndex === -1) {\n result += template.slice(cursor)\n break\n }\n\n const closeIndex = template.indexOf(\"}\", openIndex + 1)\n if (closeIndex === -1) {\n result += template.slice(cursor)\n break\n }\n\n result += template.slice(cursor, openIndex)\n const paramName = template.slice(openIndex + 1, closeIndex)\n const value = params[paramName]\n if (value === undefined) {\n throw new Error(\n `Missing value for subject parameter \"${paramName}\" in template \"${template}\"`,\n )\n }\n if (!isValidSubjectValue(value)) {\n throw new Error(\n `Invalid value for subject parameter \"${paramName}\": \"${value}\" (must be non-empty and contain only letters, digits, \"-\", and \"_\")`,\n )\n }\n result += value\n\n cursor = closeIndex + 1\n }\n\n return result\n}\n","import { DeliverPolicy, jetstream, type JsMsg } from \"@nats-io/jetstream\"\nimport {\n type ConnectionOptions,\n type Msg,\n type NatsConnection,\n wsconnect,\n} from \"@nats-io/nats-core\"\nimport type { Nova } from \"../../Nova.ts\"\nimport { buildNatsServerUrl } from \"./buildNatsServerUrl.ts\"\nimport { buildSubject } from \"./buildSubject.ts\"\nimport {\n type NatsOperationParams,\n type NatsPersistedSubject,\n type NatsPublishPayloads,\n type NatsPublishSubject,\n type NatsReplyPayloads,\n type NatsRequestPayloads,\n type NatsRequestSubject,\n natsStreamBySubject,\n type NatsSubscribePayloads,\n type NatsSubscribeSubject,\n} from \"./generated/operations.ts\"\n\nexport type NovaNatsClientConfig = ConnectionOptions\n\n/**\n * A received message, annotated with the values of the subject template's\n * `{param}` placeholders as extracted from the message's concrete subject.\n * With a wildcard subscription (e.g. `{ cell: \"*\" }`), `subjectParams` is how\n * a handler knows which entity a message belongs to. Typed per subject via\n * the generated `NatsOperationParams`.\n */\nexport type NatsSubscribeMsg<K extends NatsSubscribeSubject> = (Msg | JsMsg) & {\n subjectParams: NatsOperationParams[K]\n}\n\ntype NatsMessageHandler<K extends NatsSubscribeSubject> = (\n payload: NatsSubscribePayloads[K],\n msg: NatsSubscribeMsg<K>,\n) => void | Promise<void>\n\n/**\n * Extra options for {@link NovaNatsClient.subscribe}. `replayLast` is only\n * offered on subjects the spec marks as retaining their latest message, so\n * asking for a replay that could never arrive is a compile error.\n */\nexport type NatsSubscribeOptions<K extends NatsSubscribeSubject> =\n K extends NatsPersistedSubject\n ? {\n /**\n * Deliver the subject's current value immediately on subscribe,\n * before any subsequent updates. With a wildcard subscription (e.g.\n * `{ cell: \"*\" }`) the current value of every matching subject is\n * delivered, not just one.\n */\n replayLast?: boolean\n }\n : // Not `Record<never, never>`: that behaves like `{}` and so accepts any\n // object, letting `replayLast` through unchecked. Typing the property as\n // `never` is what makes passing it an error.\n { replayLast?: never }\n\ntype SubscribeArgs<K extends NatsSubscribeSubject> =\n keyof NatsOperationParams[K] extends never\n ? [handler: NatsMessageHandler<K>, opts?: NatsSubscribeOptions<K>]\n : [\n params: NatsOperationParams[K],\n handler: NatsMessageHandler<K>,\n opts?: NatsSubscribeOptions<K>,\n ]\n\n/**\n * Typed NATS client for the Wandelbots NOVA messaging API, generated from\n * src/asyncapi.yaml (see scripts/generate-nats-client.ts).\n *\n * Connects over WebSocket via `@nats-io/nats-core`'s `wsconnect`.\n */\nexport class NovaNatsClient {\n readonly config: NovaNatsClientConfig\n private connectionPromise: Promise<NatsConnection> | null = null\n\n constructor(nova: Nova, config: NovaNatsClientConfig = {}) {\n this.config = {\n servers: buildNatsServerUrl(nova.instanceUrl.href),\n // Reuse the Nova instance's access token for NATS auth, if it has one\n // (e.g. from login or a passed-in config.accessToken). Explicit auth\n // options in `config` (token/user/pass/authenticator) still win.\n ...(nova.accessToken ? { token: nova.accessToken } : {}),\n ...config,\n }\n }\n\n /**\n * Connects to NATS if not already connected or connecting, and returns the\n * connection. Safe to call concurrently: all callers share the same\n * in-flight connection attempt instead of each starting their own.\n */\n connect(): Promise<NatsConnection> {\n if (!this.connectionPromise) {\n this.connectionPromise = wsconnect(this.config).catch((err: unknown) => {\n // Allow a subsequent connect() call to retry after a failed attempt.\n this.connectionPromise = null\n throw err\n })\n }\n return this.connectionPromise\n }\n\n /** Closes the underlying NATS connection, if open or connecting. */\n async close(): Promise<void> {\n const connectionPromise = this.connectionPromise\n this.connectionPromise = null\n if (!connectionPromise) return\n try {\n const nc = await connectionPromise\n await nc.close()\n } catch {\n // Connection never succeeded; nothing to close.\n }\n }\n\n /**\n * Subscribes to a NATS subject published by the server, invoking `handler`\n * with the JSON-decoded payload of every message received.\n *\n * `subject` is the subject template as it appears on the wire, e.g.\n * `\"nova.v2.cells.{cell}\"`, with `{param}` placeholders filled in from\n * `params`.\n *\n * Errors decoding a message or thrown/rejected by `handler` are caught and\n * logged per-message, so one bad message doesn't stop later messages on\n * the same subscription from being handled.\n *\n * Each message is annotated with `msg.subjectParams` — the template's\n * `{param}` values extracted from the message's concrete subject — so a\n * wildcard subscriber knows which entity a message belongs to:\n *\n * nats.subscribe(\"nova.v2.cells.{cell}.status\", { cell: \"*\" },\n * (services, msg) => console.log(msg.subjectParams.cell, services))\n *\n * On subjects that retain their latest message, pass `{ replayLast: true }`\n * to receive the current value immediately instead of waiting for the next\n * update — useful for a subscriber that starts after the last change:\n *\n * nats.subscribe(\"nova.v2.system.status\", onStatus, { replayLast: true })\n *\n * Returns a function that unsubscribes when called.\n */\n async subscribe<K extends NatsSubscribeSubject>(\n subject: K,\n ...args: SubscribeArgs<K>\n ): Promise<() => void> {\n // `params` is omitted for subjects with no {param} placeholders, so the\n // handler is the first argument in that case.\n const hasParams = typeof args[0] !== \"function\"\n const params = (hasParams ? args[0] : {}) as NatsOperationParams[K]\n const handler = (hasParams ? args[1] : args[0]) as NatsMessageHandler<K>\n const opts = (hasParams ? args[2] : args[1]) as\n | { replayLast?: boolean }\n | undefined\n\n const nc = await this.connect()\n const resolvedSubject = buildSubject(subject, params)\n\n // The token positions of the template's {param} placeholders, computed\n // once here so each message's subjectParams is a plain index pick: a\n // delivered message always matches the subscribed pattern token for\n // token, so its params sit at the same positions as in the template.\n const paramPositions: [name: string, index: number][] = []\n for (const [index, token] of subject.split(\".\").entries()) {\n if (token.startsWith(\"{\") && token.endsWith(\"}\")) {\n paramPositions.push([token.slice(1, -1), index])\n }\n }\n\n const deliver = async (msg: Msg | JsMsg) => {\n // Handled per-message: a bad payload or a throwing/rejecting handler\n // should not stop the subscription from processing later messages.\n try {\n const subjectTokens = msg.subject.split(\".\")\n const subjectParams = Object.fromEntries(\n paramPositions.map(([name, index]) => [\n name,\n subjectTokens[index] ?? \"\",\n ]),\n ) as NatsOperationParams[K]\n await handler(\n msg.json<NatsSubscribePayloads[K]>(),\n Object.assign(msg, { subjectParams }),\n )\n } catch (err) {\n console.error(\n `Error handling NATS message on subject \"${resolvedSubject}\"`,\n err,\n )\n }\n }\n\n const deliverAll = (messages: AsyncIterable<Msg | JsMsg>) => {\n ;(async () => {\n for await (const msg of messages) {\n await deliver(msg)\n }\n })().catch((err: unknown) => {\n console.error(\n `NATS subscription iterator failed for \"${resolvedSubject}\"`,\n err,\n )\n })\n }\n\n if (opts?.replayLast) {\n // Delivered by JetStream rather than core NATS: an ordered consumer\n // starting at `last_per_subject` yields each matching subject's retained\n // message and then continues with live ones on the same iterator, so\n // there is no gap — and no possible duplicate — between the replayed\n // value and the updates that follow it. Ordered consumers are ephemeral\n // and need no acking, so stopping the iterator is the whole teardown.\n const consumer = await jetstream(nc).consumers.get(\n natsStreamBySubject[subject as NatsPersistedSubject],\n {\n filter_subjects: [resolvedSubject],\n deliver_policy: DeliverPolicy.LastPerSubject,\n },\n )\n const messages = await consumer.consume()\n deliverAll(messages)\n return () => {\n messages.stop()\n }\n }\n\n const sub = nc.subscribe(resolvedSubject)\n deliverAll(sub)\n return () => sub.unsubscribe()\n }\n\n /**\n * Sends a request payload for a NATS subject the server receives, and\n * waits for the JSON-decoded reply.\n *\n * `subject` is the subject template as it appears on the wire, e.g.\n * `\"nova.v2.cells.{cell}.bus-ios.ios.set\"`, with `{param}` placeholders\n * filled in from `params`.\n */\n async request<K extends NatsRequestSubject>(\n subject: K,\n params: NatsOperationParams[K],\n payload: NatsRequestPayloads[K],\n opts: { timeout?: number } = {},\n ): Promise<NatsReplyPayloads[K]> {\n const nc = await this.connect()\n const resolvedSubject = buildSubject(subject, params)\n const msg = await nc.request(resolvedSubject, JSON.stringify(payload), {\n timeout: opts.timeout ?? 5000,\n })\n return msg.json<NatsReplyPayloads[K]>()\n }\n\n /**\n * Publishes a JSON payload to any NATS subject defined in the spec,\n * without waiting for a reply.\n *\n * `subject` is the subject template as it appears on the wire, e.g.\n * `\"nova.v2.cells.{cell}.bus-ios.ios.set\"`, with `{param}` placeholders\n * filled in from `params`.\n */\n async publish<K extends NatsPublishSubject>(\n subject: K,\n params: NatsOperationParams[K],\n payload: NatsPublishPayloads[K],\n ): Promise<void> {\n const nc = await this.connect()\n const resolvedSubject = buildSubject(subject, params)\n nc.publish(resolvedSubject, JSON.stringify(payload))\n }\n}\n"],"mappings":"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"}
|
|
1
|
+
{"version":3,"file":"index.mjs","names":[],"sources":["../../../src/lib/experimental/nats/buildNatsServerUrl.ts","../../../src/lib/experimental/nats/buildSubject.ts","../../../src/lib/experimental/nats/NovaNatsClient.ts"],"sourcesContent":["import { parseNovaInstanceUrl } from \"../../converters.ts\"\n\n/**\n * Builds the WebSocket URL for a NOVA instance's NATS gateway from its\n * instance URL, e.g. `https://foo.instance.wandelbots.io` becomes\n * `wss://foo.instance.wandelbots.io/api/nats`.\n *\n * Pass the result as `servers` in the `NovaNatsClientConfig` passed to\n * `NovaNatsClient`.\n */\nexport function buildNatsServerUrl(instanceUrl: string): string {\n const url = parseNovaInstanceUrl(instanceUrl)\n const protocol = url.protocol === \"https:\" ? \"wss:\" : \"ws:\"\n return `${protocol}//${url.host}/api/nats`\n}\n","/**\n * Builds a NATS subject from an AsyncAPI-style channel address template\n * (e.g. `\"{instance}.v2.cells.{cell}\"`) by substituting each `{param}`\n * placeholder with the corresponding value from `params`.\n */\n\nfunction isValidSubjectChar(char: string): boolean {\n const code = char.charCodeAt(0)\n return (\n (code >= 48 && code <= 57) || // 0-9\n (code >= 65 && code <= 90) || // A-Z\n (code >= 97 && code <= 122) || // a-z\n char === \"-\" ||\n char === \"_\"\n )\n}\n\nfunction isValidSubjectValue(value: string): boolean {\n // A bare \"*\" is the NATS single-token wildcard, allowed on its own (e.g.\n // subscribing to all cells with `{ cell: \"*\" }\") but not as part of a\n // larger value, since it wouldn't act as a wildcard there anyway.\n if (value === \"*\") return true\n if (value.length === 0) return false\n for (const char of value) {\n if (!isValidSubjectChar(char)) return false\n }\n return true\n}\n\nexport function buildSubject(\n template: string,\n params: Record<string, string>,\n): string {\n // Scanned manually (rather than with a regex like /\\{([^}]+)\\}/g) to avoid\n // a polynomial-time backtracking blowup on pathological input, e.g. a\n // template consisting of many \"{\" characters with no closing \"}\".\n let result = \"\"\n let cursor = 0\n\n while (cursor < template.length) {\n const openIndex = template.indexOf(\"{\", cursor)\n if (openIndex === -1) {\n result += template.slice(cursor)\n break\n }\n\n const closeIndex = template.indexOf(\"}\", openIndex + 1)\n if (closeIndex === -1) {\n result += template.slice(cursor)\n break\n }\n\n result += template.slice(cursor, openIndex)\n const paramName = template.slice(openIndex + 1, closeIndex)\n const value = params[paramName]\n if (value === undefined) {\n throw new Error(\n `Missing value for subject parameter \"${paramName}\" in template \"${template}\"`,\n )\n }\n if (!isValidSubjectValue(value)) {\n throw new Error(\n `Invalid value for subject parameter \"${paramName}\": \"${value}\" (must be non-empty and contain only letters, digits, \"-\", and \"_\")`,\n )\n }\n result += value\n\n cursor = closeIndex + 1\n }\n\n return result\n}\n","import { DeliverPolicy, jetstream, jetstreamManager } from \"@nats-io/jetstream\"\nimport {\n type ConnectionOptions,\n type MsgHdrs,\n type NatsConnection,\n wsconnect,\n} from \"@nats-io/nats-core\"\nimport type { Nova } from \"../../Nova.ts\"\nimport { buildNatsServerUrl } from \"./buildNatsServerUrl.ts\"\nimport { buildSubject } from \"./buildSubject.ts\"\nimport type {\n NatsOperationParams,\n NatsPublishPayloads,\n NatsPublishSubject,\n NatsReplyPayloads,\n NatsRequestPayloads,\n NatsRequestSubject,\n NatsSubscribePayloads,\n NatsSubscribeSubject,\n} from \"./generated/operations.ts\"\n\nexport type NovaNatsClientConfig = ConnectionOptions\n\n/**\n * The fields of a received message that exist on both a core NATS `Msg` and\n * a JetStream `JsMsg` — a subscription delivers the former by default and\n * the latter with `lastMessage: true` (see {@link NatsSubscribeOptions}),\n * so handlers see only what both have in common.\n */\nexport interface NatsReceivedMsg {\n /** The concrete subject the message was published to. */\n subject: string\n /** The message's raw payload bytes. */\n data: Uint8Array\n /** Headers set by the server or the publisher, if any. */\n headers?: MsgHdrs\n /** The payload decoded as JSON. */\n json<T>(): T\n /** The payload decoded as a string. */\n string(): string\n}\n\n/**\n * A received message, annotated with the values of the subject template's\n * `{param}` placeholders as extracted from the message's concrete subject.\n * With a wildcard subscription (e.g. `{ cell: \"*\" }`), `subjectParams` is how\n * a handler knows which entity a message belongs to. Typed per subject via\n * the generated `NatsOperationParams`.\n */\nexport type NatsSubscribeMsg<K extends NatsSubscribeSubject> =\n NatsReceivedMsg & {\n subjectParams: NatsOperationParams[K]\n }\n\nexport type NatsSubscribeOptions = {\n /**\n * When true, the subscription immediately replays the last JetStream\n * message persisted for each subject matching the subscription (one per\n * concrete subject when subscribing with wildcards), then continues with\n * live messages. Requires the server to have a JetStream stream capturing\n * the subscribed subject; subscribing fails with an error if none exists.\n */\n lastMessage?: boolean\n}\n\ntype NatsMessageHandler<K extends NatsSubscribeSubject> = (\n payload: NatsSubscribePayloads[K],\n msg: NatsSubscribeMsg<K>,\n) => void | Promise<void>\n\ntype SubscribeArgs<K extends NatsSubscribeSubject> =\n keyof NatsOperationParams[K] extends never\n ? [handler: NatsMessageHandler<K>, options?: NatsSubscribeOptions]\n : [\n params: NatsOperationParams[K],\n handler: NatsMessageHandler<K>,\n options?: NatsSubscribeOptions,\n ]\n\n/**\n * Typed NATS client for the Wandelbots NOVA messaging API, generated from\n * src/asyncapi.yaml (see scripts/generate-nats-client.ts).\n *\n * Connects over WebSocket via `@nats-io/nats-core`'s `wsconnect`.\n */\nexport class NovaNatsClient {\n readonly config: NovaNatsClientConfig\n private connectionPromise: Promise<NatsConnection> | null = null\n\n constructor(nova: Nova, config: NovaNatsClientConfig = {}) {\n this.config = {\n servers: buildNatsServerUrl(nova.instanceUrl.href),\n // Reuse the Nova instance's access token for NATS auth, if it has one\n // (e.g. from login or a passed-in config.accessToken). Explicit auth\n // options in `config` (token/user/pass/authenticator) still win.\n ...(nova.accessToken ? { token: nova.accessToken } : {}),\n ...config,\n }\n }\n\n /**\n * Connects to NATS if not already connected or connecting, and returns the\n * connection. Safe to call concurrently: all callers share the same\n * in-flight connection attempt instead of each starting their own.\n */\n connect(): Promise<NatsConnection> {\n if (!this.connectionPromise) {\n this.connectionPromise = wsconnect(this.config).catch((err: unknown) => {\n // Allow a subsequent connect() call to retry after a failed attempt.\n this.connectionPromise = null\n throw err\n })\n }\n return this.connectionPromise\n }\n\n /** Closes the underlying NATS connection, if open or connecting. */\n async close(): Promise<void> {\n const connectionPromise = this.connectionPromise\n this.connectionPromise = null\n if (!connectionPromise) return\n try {\n const nc = await connectionPromise\n await nc.close()\n } catch {\n // Connection never succeeded; nothing to close.\n }\n }\n\n /**\n * Subscribes to a NATS subject published by the server, invoking `handler`\n * with the JSON-decoded payload of every message received.\n *\n * `subject` is the subject template as it appears on the wire, e.g.\n * `\"nova.v2.cells.{cell}\"`, with `{param}` placeholders filled in from\n * `params`.\n *\n * Errors decoding a message or thrown/rejected by `handler` are caught and\n * logged per-message, so one bad message doesn't stop later messages on\n * the same subscription from being handled.\n *\n * Each message is annotated with `msg.subjectParams` — the template's\n * `{param}` values extracted from the message's concrete subject — so a\n * wildcard subscriber knows which entity a message belongs to:\n *\n * nats.subscribe(\"nova.v2.cells.{cell}.status\", { cell: \"*\" },\n * (services, msg) => console.log(msg.subjectParams.cell, services))\n *\n * With `{ lastMessage: true }` as the final argument, the last JetStream\n * message persisted for each matching subject is replayed to the handler\n * immediately, before live messages (see {@link NatsSubscribeOptions}).\n *\n * Returns a function that unsubscribes when called.\n */\n async subscribe<K extends NatsSubscribeSubject>(\n subject: K,\n ...args: SubscribeArgs<K>\n ): Promise<() => void> {\n const [params, handler, options] =\n typeof args[0] === \"function\"\n ? ([{}, args[0], args[1] as NatsSubscribeOptions | undefined] as const)\n : ([args[0], args[1] as NatsMessageHandler<K>, args[2]] as const)\n\n const nc = await this.connect()\n const resolvedSubject = buildSubject(subject, params)\n\n // The token positions of the template's {param} placeholders, computed\n // once here so each message's subjectParams is a plain index pick: a\n // delivered message always matches the subscribed pattern token for\n // token, so its params sit at the same positions as in the template.\n const paramPositions: [name: string, index: number][] = []\n for (const [index, token] of subject.split(\".\").entries()) {\n if (token.startsWith(\"{\") && token.endsWith(\"}\")) {\n paramPositions.push([token.slice(1, -1), index])\n }\n }\n\n const dispatch = (messages: AsyncIterable<NatsReceivedMsg>) => {\n ;(async () => {\n for await (const msg of messages) {\n // Handled per-message: a bad payload or a throwing/rejecting\n // handler should not stop the subscription from processing later\n // messages.\n try {\n const subjectTokens = msg.subject.split(\".\")\n const subjectParams = Object.fromEntries(\n paramPositions.map(([name, index]) => [\n name,\n subjectTokens[index] ?? \"\",\n ]),\n ) as NatsOperationParams[K]\n await handler(\n msg.json<NatsSubscribePayloads[K]>(),\n Object.assign(msg, { subjectParams }),\n )\n } catch (err) {\n console.error(\n `Error handling NATS message on subject \"${resolvedSubject}\"`,\n err,\n )\n }\n }\n })().catch((err: unknown) => {\n console.error(\n `NATS subscription iterator failed for \"${resolvedSubject}\"`,\n err,\n )\n })\n }\n\n if (options?.lastMessage) {\n // Delivered through JetStream instead of a core subscription: an\n // ordered (ephemeral, auto-acked, auto-cleaned-up) consumer with\n // \"last per subject\" delivery replays the last persisted message of\n // each matching subject, then seamlessly continues with live messages.\n const jsm = await jetstreamManager(nc)\n // Throws if no stream captures the subject, rather than silently\n // never delivering anything.\n const streamName = await jsm.streams.find(resolvedSubject)\n const consumer = await jetstream(nc).consumers.get(streamName, {\n filter_subjects: [resolvedSubject],\n deliver_policy: DeliverPolicy.LastPerSubject,\n })\n const messages = await consumer.consume()\n dispatch(messages)\n return () => void messages.close()\n }\n\n const sub = nc.subscribe(resolvedSubject)\n dispatch(sub)\n return () => sub.unsubscribe()\n }\n\n /**\n * Sends a request payload for a NATS subject the server receives, and\n * waits for the JSON-decoded reply.\n *\n * `subject` is the subject template as it appears on the wire, e.g.\n * `\"nova.v2.cells.{cell}.bus-ios.ios.set\"`, with `{param}` placeholders\n * filled in from `params`.\n */\n async request<K extends NatsRequestSubject>(\n subject: K,\n params: NatsOperationParams[K],\n payload: NatsRequestPayloads[K],\n opts: { timeout?: number } = {},\n ): Promise<NatsReplyPayloads[K]> {\n const nc = await this.connect()\n const resolvedSubject = buildSubject(subject, params)\n const msg = await nc.request(resolvedSubject, JSON.stringify(payload), {\n timeout: opts.timeout ?? 5000,\n })\n return msg.json<NatsReplyPayloads[K]>()\n }\n\n /**\n * Publishes a JSON payload to any NATS subject defined in the spec,\n * without waiting for a reply.\n *\n * `subject` is the subject template as it appears on the wire, e.g.\n * `\"nova.v2.cells.{cell}.bus-ios.ios.set\"`, with `{param}` placeholders\n * filled in from `params`.\n */\n async publish<K extends NatsPublishSubject>(\n subject: K,\n params: NatsOperationParams[K],\n payload: NatsPublishPayloads[K],\n ): Promise<void> {\n const nc = await this.connect()\n const resolvedSubject = buildSubject(subject, params)\n nc.publish(resolvedSubject, JSON.stringify(payload))\n }\n}\n"],"mappings":"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"}
|
package/package.json
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@wandelbots/nova-js",
|
|
3
3
|
"type": "module",
|
|
4
|
-
"version": "4.5.0-pr.
|
|
4
|
+
"version": "4.5.0-pr.324.1203649",
|
|
5
5
|
"description": "Official JS client for the Wandelbots API",
|
|
6
6
|
"sideEffects": false,
|
|
7
7
|
"packageManager": "pnpm@11.9.0+sha512.bd682d5d03fe525ef7c9fd6780c6884d1e756ac4c9c9fe00c538782824310dcf90e3ddc4f53835f06dfaebd5085e41855e0bcbb3b60de2ac5bbab89e5036f03b",
|
|
@@ -5,17 +5,15 @@
|
|
|
5
5
|
* This API is experimental and may change without a major version bump.
|
|
6
6
|
*/
|
|
7
7
|
export { buildNatsServerUrl } from "../../lib/experimental/nats/buildNatsServerUrl.ts"
|
|
8
|
-
export {
|
|
9
|
-
|
|
10
|
-
|
|
11
|
-
|
|
12
|
-
|
|
13
|
-
|
|
14
|
-
|
|
15
|
-
|
|
16
|
-
|
|
17
|
-
type NatsSubscribePayloads,
|
|
18
|
-
type NatsSubscribeSubject,
|
|
8
|
+
export type {
|
|
9
|
+
NatsOperationParams,
|
|
10
|
+
NatsPublishPayloads,
|
|
11
|
+
NatsPublishSubject,
|
|
12
|
+
NatsReplyPayloads,
|
|
13
|
+
NatsRequestPayloads,
|
|
14
|
+
NatsRequestSubject,
|
|
15
|
+
NatsSubscribePayloads,
|
|
16
|
+
NatsSubscribeSubject,
|
|
19
17
|
} from "../../lib/experimental/nats/generated/operations.ts"
|
|
20
18
|
// Message payload types for every schema in src/asyncapi.yaml, e.g. `Cell`,
|
|
21
19
|
// `App`, `ProgramStatus` (see NatsSubscribePayloads/NatsRequestPayloads for
|
|
@@ -23,6 +21,7 @@ export {
|
|
|
23
21
|
export type * from "../../lib/experimental/nats/generated/types.ts"
|
|
24
22
|
export { NovaNatsClient } from "../../lib/experimental/nats/NovaNatsClient.ts"
|
|
25
23
|
export type {
|
|
24
|
+
NatsReceivedMsg,
|
|
26
25
|
NatsSubscribeMsg,
|
|
27
26
|
NatsSubscribeOptions,
|
|
28
27
|
NovaNatsClientConfig,
|
|
@@ -1,28 +1,45 @@
|
|
|
1
|
-
import { DeliverPolicy, jetstream,
|
|
1
|
+
import { DeliverPolicy, jetstream, jetstreamManager } from "@nats-io/jetstream"
|
|
2
2
|
import {
|
|
3
3
|
type ConnectionOptions,
|
|
4
|
-
type
|
|
4
|
+
type MsgHdrs,
|
|
5
5
|
type NatsConnection,
|
|
6
6
|
wsconnect,
|
|
7
7
|
} from "@nats-io/nats-core"
|
|
8
8
|
import type { Nova } from "../../Nova.ts"
|
|
9
9
|
import { buildNatsServerUrl } from "./buildNatsServerUrl.ts"
|
|
10
10
|
import { buildSubject } from "./buildSubject.ts"
|
|
11
|
-
import {
|
|
12
|
-
|
|
13
|
-
|
|
14
|
-
|
|
15
|
-
|
|
16
|
-
|
|
17
|
-
|
|
18
|
-
|
|
19
|
-
|
|
20
|
-
type NatsSubscribePayloads,
|
|
21
|
-
type NatsSubscribeSubject,
|
|
11
|
+
import type {
|
|
12
|
+
NatsOperationParams,
|
|
13
|
+
NatsPublishPayloads,
|
|
14
|
+
NatsPublishSubject,
|
|
15
|
+
NatsReplyPayloads,
|
|
16
|
+
NatsRequestPayloads,
|
|
17
|
+
NatsRequestSubject,
|
|
18
|
+
NatsSubscribePayloads,
|
|
19
|
+
NatsSubscribeSubject,
|
|
22
20
|
} from "./generated/operations.ts"
|
|
23
21
|
|
|
24
22
|
export type NovaNatsClientConfig = ConnectionOptions
|
|
25
23
|
|
|
24
|
+
/**
|
|
25
|
+
* The fields of a received message that exist on both a core NATS `Msg` and
|
|
26
|
+
* a JetStream `JsMsg` — a subscription delivers the former by default and
|
|
27
|
+
* the latter with `lastMessage: true` (see {@link NatsSubscribeOptions}),
|
|
28
|
+
* so handlers see only what both have in common.
|
|
29
|
+
*/
|
|
30
|
+
export interface NatsReceivedMsg {
|
|
31
|
+
/** The concrete subject the message was published to. */
|
|
32
|
+
subject: string
|
|
33
|
+
/** The message's raw payload bytes. */
|
|
34
|
+
data: Uint8Array
|
|
35
|
+
/** Headers set by the server or the publisher, if any. */
|
|
36
|
+
headers?: MsgHdrs
|
|
37
|
+
/** The payload decoded as JSON. */
|
|
38
|
+
json<T>(): T
|
|
39
|
+
/** The payload decoded as a string. */
|
|
40
|
+
string(): string
|
|
41
|
+
}
|
|
42
|
+
|
|
26
43
|
/**
|
|
27
44
|
* A received message, annotated with the values of the subject template's
|
|
28
45
|
* `{param}` placeholders as extracted from the message's concrete subject.
|
|
@@ -30,8 +47,20 @@ export type NovaNatsClientConfig = ConnectionOptions
|
|
|
30
47
|
* a handler knows which entity a message belongs to. Typed per subject via
|
|
31
48
|
* the generated `NatsOperationParams`.
|
|
32
49
|
*/
|
|
33
|
-
export type NatsSubscribeMsg<K extends NatsSubscribeSubject> =
|
|
34
|
-
|
|
50
|
+
export type NatsSubscribeMsg<K extends NatsSubscribeSubject> =
|
|
51
|
+
NatsReceivedMsg & {
|
|
52
|
+
subjectParams: NatsOperationParams[K]
|
|
53
|
+
}
|
|
54
|
+
|
|
55
|
+
export type NatsSubscribeOptions = {
|
|
56
|
+
/**
|
|
57
|
+
* When true, the subscription immediately replays the last JetStream
|
|
58
|
+
* message persisted for each subject matching the subscription (one per
|
|
59
|
+
* concrete subject when subscribing with wildcards), then continues with
|
|
60
|
+
* live messages. Requires the server to have a JetStream stream capturing
|
|
61
|
+
* the subscribed subject; subscribing fails with an error if none exists.
|
|
62
|
+
*/
|
|
63
|
+
lastMessage?: boolean
|
|
35
64
|
}
|
|
36
65
|
|
|
37
66
|
type NatsMessageHandler<K extends NatsSubscribeSubject> = (
|
|
@@ -39,34 +68,13 @@ type NatsMessageHandler<K extends NatsSubscribeSubject> = (
|
|
|
39
68
|
msg: NatsSubscribeMsg<K>,
|
|
40
69
|
) => void | Promise<void>
|
|
41
70
|
|
|
42
|
-
/**
|
|
43
|
-
* Extra options for {@link NovaNatsClient.subscribe}. `replayLast` is only
|
|
44
|
-
* offered on subjects the spec marks as retaining their latest message, so
|
|
45
|
-
* asking for a replay that could never arrive is a compile error.
|
|
46
|
-
*/
|
|
47
|
-
export type NatsSubscribeOptions<K extends NatsSubscribeSubject> =
|
|
48
|
-
K extends NatsPersistedSubject
|
|
49
|
-
? {
|
|
50
|
-
/**
|
|
51
|
-
* Deliver the subject's current value immediately on subscribe,
|
|
52
|
-
* before any subsequent updates. With a wildcard subscription (e.g.
|
|
53
|
-
* `{ cell: "*" }`) the current value of every matching subject is
|
|
54
|
-
* delivered, not just one.
|
|
55
|
-
*/
|
|
56
|
-
replayLast?: boolean
|
|
57
|
-
}
|
|
58
|
-
: // Not `Record<never, never>`: that behaves like `{}` and so accepts any
|
|
59
|
-
// object, letting `replayLast` through unchecked. Typing the property as
|
|
60
|
-
// `never` is what makes passing it an error.
|
|
61
|
-
{ replayLast?: never }
|
|
62
|
-
|
|
63
71
|
type SubscribeArgs<K extends NatsSubscribeSubject> =
|
|
64
72
|
keyof NatsOperationParams[K] extends never
|
|
65
|
-
? [handler: NatsMessageHandler<K>,
|
|
73
|
+
? [handler: NatsMessageHandler<K>, options?: NatsSubscribeOptions]
|
|
66
74
|
: [
|
|
67
75
|
params: NatsOperationParams[K],
|
|
68
76
|
handler: NatsMessageHandler<K>,
|
|
69
|
-
|
|
77
|
+
options?: NatsSubscribeOptions,
|
|
70
78
|
]
|
|
71
79
|
|
|
72
80
|
/**
|
|
@@ -138,11 +146,9 @@ export class NovaNatsClient {
|
|
|
138
146
|
* nats.subscribe("nova.v2.cells.{cell}.status", { cell: "*" },
|
|
139
147
|
* (services, msg) => console.log(msg.subjectParams.cell, services))
|
|
140
148
|
*
|
|
141
|
-
*
|
|
142
|
-
*
|
|
143
|
-
*
|
|
144
|
-
*
|
|
145
|
-
* nats.subscribe("nova.v2.system.status", onStatus, { replayLast: true })
|
|
149
|
+
* With `{ lastMessage: true }` as the final argument, the last JetStream
|
|
150
|
+
* message persisted for each matching subject is replayed to the handler
|
|
151
|
+
* immediately, before live messages (see {@link NatsSubscribeOptions}).
|
|
146
152
|
*
|
|
147
153
|
* Returns a function that unsubscribes when called.
|
|
148
154
|
*/
|
|
@@ -150,14 +156,10 @@ export class NovaNatsClient {
|
|
|
150
156
|
subject: K,
|
|
151
157
|
...args: SubscribeArgs<K>
|
|
152
158
|
): Promise<() => void> {
|
|
153
|
-
|
|
154
|
-
|
|
155
|
-
|
|
156
|
-
|
|
157
|
-
const handler = (hasParams ? args[1] : args[0]) as NatsMessageHandler<K>
|
|
158
|
-
const opts = (hasParams ? args[2] : args[1]) as
|
|
159
|
-
| { replayLast?: boolean }
|
|
160
|
-
| undefined
|
|
159
|
+
const [params, handler, options] =
|
|
160
|
+
typeof args[0] === "function"
|
|
161
|
+
? ([{}, args[0], args[1] as NatsSubscribeOptions | undefined] as const)
|
|
162
|
+
: ([args[0], args[1] as NatsMessageHandler<K>, args[2]] as const)
|
|
161
163
|
|
|
162
164
|
const nc = await this.connect()
|
|
163
165
|
const resolvedSubject = buildSubject(subject, params)
|
|
@@ -173,33 +175,30 @@ export class NovaNatsClient {
|
|
|
173
175
|
}
|
|
174
176
|
}
|
|
175
177
|
|
|
176
|
-
const
|
|
177
|
-
// Handled per-message: a bad payload or a throwing/rejecting handler
|
|
178
|
-
// should not stop the subscription from processing later messages.
|
|
179
|
-
try {
|
|
180
|
-
const subjectTokens = msg.subject.split(".")
|
|
181
|
-
const subjectParams = Object.fromEntries(
|
|
182
|
-
paramPositions.map(([name, index]) => [
|
|
183
|
-
name,
|
|
184
|
-
subjectTokens[index] ?? "",
|
|
185
|
-
]),
|
|
186
|
-
) as NatsOperationParams[K]
|
|
187
|
-
await handler(
|
|
188
|
-
msg.json<NatsSubscribePayloads[K]>(),
|
|
189
|
-
Object.assign(msg, { subjectParams }),
|
|
190
|
-
)
|
|
191
|
-
} catch (err) {
|
|
192
|
-
console.error(
|
|
193
|
-
`Error handling NATS message on subject "${resolvedSubject}"`,
|
|
194
|
-
err,
|
|
195
|
-
)
|
|
196
|
-
}
|
|
197
|
-
}
|
|
198
|
-
|
|
199
|
-
const deliverAll = (messages: AsyncIterable<Msg | JsMsg>) => {
|
|
178
|
+
const dispatch = (messages: AsyncIterable<NatsReceivedMsg>) => {
|
|
200
179
|
;(async () => {
|
|
201
180
|
for await (const msg of messages) {
|
|
202
|
-
|
|
181
|
+
// Handled per-message: a bad payload or a throwing/rejecting
|
|
182
|
+
// handler should not stop the subscription from processing later
|
|
183
|
+
// messages.
|
|
184
|
+
try {
|
|
185
|
+
const subjectTokens = msg.subject.split(".")
|
|
186
|
+
const subjectParams = Object.fromEntries(
|
|
187
|
+
paramPositions.map(([name, index]) => [
|
|
188
|
+
name,
|
|
189
|
+
subjectTokens[index] ?? "",
|
|
190
|
+
]),
|
|
191
|
+
) as NatsOperationParams[K]
|
|
192
|
+
await handler(
|
|
193
|
+
msg.json<NatsSubscribePayloads[K]>(),
|
|
194
|
+
Object.assign(msg, { subjectParams }),
|
|
195
|
+
)
|
|
196
|
+
} catch (err) {
|
|
197
|
+
console.error(
|
|
198
|
+
`Error handling NATS message on subject "${resolvedSubject}"`,
|
|
199
|
+
err,
|
|
200
|
+
)
|
|
201
|
+
}
|
|
203
202
|
}
|
|
204
203
|
})().catch((err: unknown) => {
|
|
205
204
|
console.error(
|
|
@@ -209,29 +208,26 @@ export class NovaNatsClient {
|
|
|
209
208
|
})
|
|
210
209
|
}
|
|
211
210
|
|
|
212
|
-
if (
|
|
213
|
-
// Delivered
|
|
214
|
-
//
|
|
215
|
-
//
|
|
216
|
-
//
|
|
217
|
-
|
|
218
|
-
//
|
|
219
|
-
|
|
220
|
-
|
|
221
|
-
|
|
222
|
-
|
|
223
|
-
|
|
224
|
-
|
|
225
|
-
)
|
|
211
|
+
if (options?.lastMessage) {
|
|
212
|
+
// Delivered through JetStream instead of a core subscription: an
|
|
213
|
+
// ordered (ephemeral, auto-acked, auto-cleaned-up) consumer with
|
|
214
|
+
// "last per subject" delivery replays the last persisted message of
|
|
215
|
+
// each matching subject, then seamlessly continues with live messages.
|
|
216
|
+
const jsm = await jetstreamManager(nc)
|
|
217
|
+
// Throws if no stream captures the subject, rather than silently
|
|
218
|
+
// never delivering anything.
|
|
219
|
+
const streamName = await jsm.streams.find(resolvedSubject)
|
|
220
|
+
const consumer = await jetstream(nc).consumers.get(streamName, {
|
|
221
|
+
filter_subjects: [resolvedSubject],
|
|
222
|
+
deliver_policy: DeliverPolicy.LastPerSubject,
|
|
223
|
+
})
|
|
226
224
|
const messages = await consumer.consume()
|
|
227
|
-
|
|
228
|
-
return () =>
|
|
229
|
-
messages.stop()
|
|
230
|
-
}
|
|
225
|
+
dispatch(messages)
|
|
226
|
+
return () => void messages.close()
|
|
231
227
|
}
|
|
232
228
|
|
|
233
229
|
const sub = nc.subscribe(resolvedSubject)
|
|
234
|
-
|
|
230
|
+
dispatch(sub)
|
|
235
231
|
return () => sub.unsubscribe()
|
|
236
232
|
}
|
|
237
233
|
|
|
@@ -732,26 +732,6 @@ export interface NatsSubscribePayloads {
|
|
|
732
732
|
|
|
733
733
|
export type NatsSubscribeSubject = keyof NatsSubscribePayloads
|
|
734
734
|
|
|
735
|
-
/**
|
|
736
|
-
* The JetStream stream backing each subject that retains only its latest
|
|
737
|
-
* message (`max_msgs_per_subject: 1` in src/asyncapi.yaml), so that
|
|
738
|
-
* message is the subject's current state.
|
|
739
|
-
*/
|
|
740
|
-
export const natsStreamBySubject = {
|
|
741
|
-
"nova.v2.cells.{cell}": "system-state",
|
|
742
|
-
"nova.v2.cells.{cell}.apps.{app}": "system-state",
|
|
743
|
-
"nova.v2.cells.{cell}.controllers.{controller}": "system-state",
|
|
744
|
-
"nova.v2.cells.{cell}.status": "system-state",
|
|
745
|
-
"nova.v2.system.status": "system-state",
|
|
746
|
-
"nova.v2.cells.{cell}.collision.setups.{setup}": "system-state",
|
|
747
|
-
"nova.v2.cells.{cell}.bus-ios.status": "system-state",
|
|
748
|
-
"nova.v2.cells.{cell}.controllers.{controller}.motion-groups.{motion-group}.description":
|
|
749
|
-
"system-state",
|
|
750
|
-
} as const
|
|
751
|
-
|
|
752
|
-
/** Subjects whose current value can be replayed via `{ replayLast: true }`. */
|
|
753
|
-
export type NatsPersistedSubject = keyof typeof natsStreamBySubject
|
|
754
|
-
|
|
755
735
|
/** Request payload types for subjects the client sends requests to. */
|
|
756
736
|
export interface NatsRequestPayloads {
|
|
757
737
|
/**
|