@wandelbots/nova-js 3.13.0-pr.307.64cc6ba → 3.13.0-pr.307.720787b

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.
@@ -0,0 +1,85 @@
1
+ import { n as MockNovaInstance, t as AutoReconnectingWebsocket } from "./AutoReconnectingWebsocket-1K2J1NA4.mjs";
2
+ import { AxiosInstance } from "axios";
3
+ import { ApplicationApi, BUSInputsOutputsApi, CellApi, Configuration, ControllerApi, ControllerInputsOutputsApi, JoggingApi, KinematicsApi, LicenseApi, MotionGroupApi, MotionGroupModelsApi, NOVACloudApi, ProgramApi, RobotConfigurationsApi, SessionApi, StoreCollisionComponentsApi, StoreCollisionSetupsApi, StoreObjectApi, SystemApi, TrajectoryCachingApi, TrajectoryExecutionApi, TrajectoryPlanningApi, VersionApi, VirtualControllerApi, VirtualControllerBehaviorApi, VirtualControllerInputsOutputsApi } from "@wandelbots/nova-api/v2";
4
+
5
+ //#region src/lib/NovaAPIClient.d.ts
6
+ type UnwrapAxiosResponseReturn<T> = T extends ((...a: any[]) => any) ? (...a: Parameters<T>) => Promise<Awaited<ReturnType<T>> extends {
7
+ data: infer D;
8
+ } ? D : never> : never;
9
+ type WithUnwrappedAxiosResponse<T> = { [P in keyof T]: UnwrapAxiosResponseReturn<T[P]> };
10
+ type NovaAPIClientOpts = Configuration & {
11
+ axiosInstance?: AxiosInstance;
12
+ mock?: boolean;
13
+ };
14
+ /**
15
+ * API client providing type-safe access to all the endpoints of a NOVA
16
+ * instance.
17
+ */
18
+ declare class NovaAPIClient {
19
+ readonly opts: NovaAPIClientOpts;
20
+ readonly application: WithUnwrappedAxiosResponse<ApplicationApi>;
21
+ readonly busIOs: WithUnwrappedAxiosResponse<BUSInputsOutputsApi>;
22
+ readonly cell: WithUnwrappedAxiosResponse<CellApi>;
23
+ readonly controller: WithUnwrappedAxiosResponse<ControllerApi>;
24
+ readonly controllerIOs: WithUnwrappedAxiosResponse<ControllerInputsOutputsApi>;
25
+ readonly jogging: WithUnwrappedAxiosResponse<JoggingApi>;
26
+ readonly kinematics: WithUnwrappedAxiosResponse<KinematicsApi>;
27
+ readonly license: WithUnwrappedAxiosResponse<LicenseApi>;
28
+ readonly motionGroup: WithUnwrappedAxiosResponse<MotionGroupApi>;
29
+ readonly motionGroupModels: WithUnwrappedAxiosResponse<MotionGroupModelsApi>;
30
+ readonly novaCloud: WithUnwrappedAxiosResponse<NOVACloudApi>;
31
+ readonly program: WithUnwrappedAxiosResponse<ProgramApi>;
32
+ readonly robotConfigurations: WithUnwrappedAxiosResponse<RobotConfigurationsApi>;
33
+ readonly session: WithUnwrappedAxiosResponse<SessionApi>;
34
+ readonly storeCollisionComponents: WithUnwrappedAxiosResponse<StoreCollisionComponentsApi>;
35
+ readonly storeCollisionSetups: WithUnwrappedAxiosResponse<StoreCollisionSetupsApi>;
36
+ readonly storeObject: WithUnwrappedAxiosResponse<StoreObjectApi>;
37
+ readonly system: WithUnwrappedAxiosResponse<SystemApi>;
38
+ readonly trajectoryCaching: WithUnwrappedAxiosResponse<TrajectoryCachingApi>;
39
+ readonly trajectoryExecution: WithUnwrappedAxiosResponse<TrajectoryExecutionApi>;
40
+ readonly trajectoryPlanning: WithUnwrappedAxiosResponse<TrajectoryPlanningApi>;
41
+ readonly version: WithUnwrappedAxiosResponse<VersionApi>;
42
+ readonly virtualController: WithUnwrappedAxiosResponse<VirtualControllerApi>;
43
+ readonly virtualControllerBehavior: WithUnwrappedAxiosResponse<VirtualControllerBehaviorApi>;
44
+ readonly virtualControllerIOs: WithUnwrappedAxiosResponse<VirtualControllerInputsOutputsApi>;
45
+ constructor(opts: NovaAPIClientOpts);
46
+ }
47
+ //#endregion
48
+ //#region src/lib/Nova.d.ts
49
+ type NovaConfig = {
50
+ /**
51
+ * Url of the deployed NOVA instance to connect to
52
+ * e.g. https://saeattii.instance.wandelbots.io
53
+ */
54
+ instanceUrl: string;
55
+ /**
56
+ * Access token for Bearer authentication.
57
+ * If running on a NOVA instance, this can be automatically retrieved from
58
+ * the current session when omitted.
59
+ */
60
+ accessToken?: string;
61
+ } & Omit<Configuration, "isJsonMime" | "basePath">;
62
+ /**
63
+ *
64
+ * Client for connecting to a NOVA instance and controlling robots.
65
+ */
66
+ declare class Nova {
67
+ readonly api: NovaAPIClient;
68
+ readonly config: NovaConfig;
69
+ readonly mock?: MockNovaInstance;
70
+ readonly instanceUrl: URL;
71
+ authPromise: Promise<string | null> | null;
72
+ accessToken: string | null;
73
+ constructor(config: NovaConfig);
74
+ renewAuthentication(): Promise<void>;
75
+ makeWebsocketURL(path: string): string;
76
+ /**
77
+ * Retrieve an AutoReconnectingWebsocket to the given path on the Nova instance.
78
+ * If you explicitly want to reconnect an existing websocket, call `reconnect`
79
+ * on the returned object.
80
+ */
81
+ openReconnectingWebsocket(path: string): AutoReconnectingWebsocket;
82
+ }
83
+ //#endregion
84
+ export { NovaConfig as n, NovaAPIClient as r, Nova as t };
85
+ //# sourceMappingURL=Nova--8gozkLR.d.mts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"Nova--8gozkLR.d.mts","names":[],"sources":["../src/lib/NovaAPIClient.ts","../src/lib/Nova.ts"],"mappings":";;;;;KAmCK,yBAAA,MAA+B,CAAA,cAAc,CAAA,uBAEzC,CAAA,EAAG,UAAA,CAAW,CAAA,MACd,OAAA,CAAQ,OAAA,CAAQ,UAAA,CAAW,CAAA;EAAc,IAAA;AAAA,IAAkB,CAAA;AAAA,KAG/D,0BAAA,oBACS,CAAA,GAAI,yBAAA,CAA0B,CAAA,CAAE,CAAA;AAAA,KAGzC,iBAAA,GAAoB,aAAA;EACvB,aAAA,GAAgB,aAAa;EAC7B,IAAA;AAAA;;;;;cAuCW,aAAA;EAAA,SACF,IAAA,EAAM,iBAAA;EAAA,SAEN,WAAA,EAAa,0BAAA,CAA2B,cAAA;EAAA,SACxC,MAAA,EAAQ,0BAAA,CAA2B,mBAAA;EAAA,SACnC,IAAA,EAAM,0BAAA,CAA2B,OAAA;EAAA,SACjC,UAAA,EAAY,0BAAA,CAA2B,aAAA;EAAA,SACvC,aAAA,EAAe,0BAAA,CAA2B,0BAAA;EAAA,SAC1C,OAAA,EAAS,0BAAA,CAA2B,UAAA;EAAA,SACpC,UAAA,EAAY,0BAAA,CAA2B,aAAA;EAAA,SACvC,OAAA,EAAS,0BAAA,CAA2B,UAAA;EAAA,SACpC,WAAA,EAAa,0BAAA,CAA2B,cAAA;EAAA,SACxC,iBAAA,EAAmB,0BAAA,CAA2B,oBAAA;EAAA,SAC9C,SAAA,EAAW,0BAAA,CAA2B,YAAA;EAAA,SACtC,OAAA,EAAS,0BAAA,CAA2B,UAAA;EAAA,SACpC,mBAAA,EAAqB,0BAAA,CAA2B,sBAAA;EAAA,SAChD,OAAA,EAAS,0BAAA,CAA2B,UAAA;EAAA,SACpC,wBAAA,EAA0B,0BAAA,CAA2B,2BAAA;EAAA,SACrD,oBAAA,EAAsB,0BAAA,CAA2B,uBAAA;EAAA,SACjD,WAAA,EAAa,0BAAA,CAA2B,cAAA;EAAA,SACxC,MAAA,EAAQ,0BAAA,CAA2B,SAAA;EAAA,SACnC,iBAAA,EAAmB,0BAAA,CAA2B,oBAAA;EAAA,SAC9C,mBAAA,EAAqB,0BAAA,CAA2B,sBAAA;EAAA,SAChD,kBAAA,EAAoB,0BAAA,CAA2B,qBAAA;EAAA,SAC/C,OAAA,EAAS,0BAAA,CAA2B,UAAA;EAAA,SACpC,iBAAA,EAAmB,0BAAA,CAA2B,oBAAA;EAAA,SAC9C,yBAAA,EAA2B,0BAAA,CAA2B,4BAAA;EAAA,SACtD,oBAAA,EAAsB,0BAAA,CAA2B,iCAAA;cAE9C,IAAA,EAAM,iBAAA;AAAA;;;KCvGR,UAAA;EDuBP;;;;EClBH,WAAA;EDoBU;;;;;ECbV,WAAA;AAAA,IACE,IAAI,CAAC,aAAA;;;;;cAMI,IAAA;EAAA,SACF,GAAA,EAAK,aAAA;EAAA,SACL,MAAA,EAAQ,UAAA;EAAA,SACR,IAAA,GAAO,gBAAA;EAAA,SACP,WAAA,EAAa,GAAA;EACtB,WAAA,EAAa,OAAA;EACb,WAAA;cAEY,MAAA,EAAQ,UAAA;EAqId,mBAAA,IAAuB,OAAA;EA4B7B,gBAAA,CAAiB,IAAA;EDlKkD;AAAA;;;;ECmLnE,yBAAA,CAA0B,IAAA,WAAY,yBAAA;AAAA"}
@@ -0,0 +1,85 @@
1
+ import { n as MockNovaInstance, t as AutoReconnectingWebsocket } from "./AutoReconnectingWebsocket-1K2J1NA4.cjs";
2
+ import { ApplicationApi, BUSInputsOutputsApi, CellApi, Configuration, ControllerApi, ControllerInputsOutputsApi, JoggingApi, KinematicsApi, LicenseApi, MotionGroupApi, MotionGroupModelsApi, NOVACloudApi, ProgramApi, RobotConfigurationsApi, SessionApi, StoreCollisionComponentsApi, StoreCollisionSetupsApi, StoreObjectApi, SystemApi, TrajectoryCachingApi, TrajectoryExecutionApi, TrajectoryPlanningApi, VersionApi, VirtualControllerApi, VirtualControllerBehaviorApi, VirtualControllerInputsOutputsApi } from "@wandelbots/nova-api/v2";
3
+ import { AxiosInstance } from "axios";
4
+
5
+ //#region src/lib/NovaAPIClient.d.ts
6
+ type UnwrapAxiosResponseReturn<T> = T extends ((...a: any[]) => any) ? (...a: Parameters<T>) => Promise<Awaited<ReturnType<T>> extends {
7
+ data: infer D;
8
+ } ? D : never> : never;
9
+ type WithUnwrappedAxiosResponse<T> = { [P in keyof T]: UnwrapAxiosResponseReturn<T[P]> };
10
+ type NovaAPIClientOpts = Configuration & {
11
+ axiosInstance?: AxiosInstance;
12
+ mock?: boolean;
13
+ };
14
+ /**
15
+ * API client providing type-safe access to all the endpoints of a NOVA
16
+ * instance.
17
+ */
18
+ declare class NovaAPIClient {
19
+ readonly opts: NovaAPIClientOpts;
20
+ readonly application: WithUnwrappedAxiosResponse<ApplicationApi>;
21
+ readonly busIOs: WithUnwrappedAxiosResponse<BUSInputsOutputsApi>;
22
+ readonly cell: WithUnwrappedAxiosResponse<CellApi>;
23
+ readonly controller: WithUnwrappedAxiosResponse<ControllerApi>;
24
+ readonly controllerIOs: WithUnwrappedAxiosResponse<ControllerInputsOutputsApi>;
25
+ readonly jogging: WithUnwrappedAxiosResponse<JoggingApi>;
26
+ readonly kinematics: WithUnwrappedAxiosResponse<KinematicsApi>;
27
+ readonly license: WithUnwrappedAxiosResponse<LicenseApi>;
28
+ readonly motionGroup: WithUnwrappedAxiosResponse<MotionGroupApi>;
29
+ readonly motionGroupModels: WithUnwrappedAxiosResponse<MotionGroupModelsApi>;
30
+ readonly novaCloud: WithUnwrappedAxiosResponse<NOVACloudApi>;
31
+ readonly program: WithUnwrappedAxiosResponse<ProgramApi>;
32
+ readonly robotConfigurations: WithUnwrappedAxiosResponse<RobotConfigurationsApi>;
33
+ readonly session: WithUnwrappedAxiosResponse<SessionApi>;
34
+ readonly storeCollisionComponents: WithUnwrappedAxiosResponse<StoreCollisionComponentsApi>;
35
+ readonly storeCollisionSetups: WithUnwrappedAxiosResponse<StoreCollisionSetupsApi>;
36
+ readonly storeObject: WithUnwrappedAxiosResponse<StoreObjectApi>;
37
+ readonly system: WithUnwrappedAxiosResponse<SystemApi>;
38
+ readonly trajectoryCaching: WithUnwrappedAxiosResponse<TrajectoryCachingApi>;
39
+ readonly trajectoryExecution: WithUnwrappedAxiosResponse<TrajectoryExecutionApi>;
40
+ readonly trajectoryPlanning: WithUnwrappedAxiosResponse<TrajectoryPlanningApi>;
41
+ readonly version: WithUnwrappedAxiosResponse<VersionApi>;
42
+ readonly virtualController: WithUnwrappedAxiosResponse<VirtualControllerApi>;
43
+ readonly virtualControllerBehavior: WithUnwrappedAxiosResponse<VirtualControllerBehaviorApi>;
44
+ readonly virtualControllerIOs: WithUnwrappedAxiosResponse<VirtualControllerInputsOutputsApi>;
45
+ constructor(opts: NovaAPIClientOpts);
46
+ }
47
+ //#endregion
48
+ //#region src/lib/Nova.d.ts
49
+ type NovaConfig = {
50
+ /**
51
+ * Url of the deployed NOVA instance to connect to
52
+ * e.g. https://saeattii.instance.wandelbots.io
53
+ */
54
+ instanceUrl: string;
55
+ /**
56
+ * Access token for Bearer authentication.
57
+ * If running on a NOVA instance, this can be automatically retrieved from
58
+ * the current session when omitted.
59
+ */
60
+ accessToken?: string;
61
+ } & Omit<Configuration, "isJsonMime" | "basePath">;
62
+ /**
63
+ *
64
+ * Client for connecting to a NOVA instance and controlling robots.
65
+ */
66
+ declare class Nova {
67
+ readonly api: NovaAPIClient;
68
+ readonly config: NovaConfig;
69
+ readonly mock?: MockNovaInstance;
70
+ readonly instanceUrl: URL;
71
+ authPromise: Promise<string | null> | null;
72
+ accessToken: string | null;
73
+ constructor(config: NovaConfig);
74
+ renewAuthentication(): Promise<void>;
75
+ makeWebsocketURL(path: string): string;
76
+ /**
77
+ * Retrieve an AutoReconnectingWebsocket to the given path on the Nova instance.
78
+ * If you explicitly want to reconnect an existing websocket, call `reconnect`
79
+ * on the returned object.
80
+ */
81
+ openReconnectingWebsocket(path: string): AutoReconnectingWebsocket;
82
+ }
83
+ //#endregion
84
+ export { NovaConfig as n, NovaAPIClient as r, Nova as t };
85
+ //# sourceMappingURL=Nova-B1o5iTQ6.d.cts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"Nova-B1o5iTQ6.d.cts","names":[],"sources":["../src/lib/NovaAPIClient.ts","../src/lib/Nova.ts"],"mappings":";;;;;KAmCK,yBAAA,MAA+B,CAAA,cAAc,CAAA,uBAEzC,CAAA,EAAG,UAAA,CAAW,CAAA,MACd,OAAA,CAAQ,OAAA,CAAQ,UAAA,CAAW,CAAA;EAAc,IAAA;AAAA,IAAkB,CAAA;AAAA,KAG/D,0BAAA,oBACS,CAAA,GAAI,yBAAA,CAA0B,CAAA,CAAE,CAAA;AAAA,KAGzC,iBAAA,GAAoB,aAAA;EACvB,aAAA,GAAgB,aAAa;EAC7B,IAAA;AAAA;;;;;cAuCW,aAAA;EAAA,SACF,IAAA,EAAM,iBAAA;EAAA,SAEN,WAAA,EAAa,0BAAA,CAA2B,cAAA;EAAA,SACxC,MAAA,EAAQ,0BAAA,CAA2B,mBAAA;EAAA,SACnC,IAAA,EAAM,0BAAA,CAA2B,OAAA;EAAA,SACjC,UAAA,EAAY,0BAAA,CAA2B,aAAA;EAAA,SACvC,aAAA,EAAe,0BAAA,CAA2B,0BAAA;EAAA,SAC1C,OAAA,EAAS,0BAAA,CAA2B,UAAA;EAAA,SACpC,UAAA,EAAY,0BAAA,CAA2B,aAAA;EAAA,SACvC,OAAA,EAAS,0BAAA,CAA2B,UAAA;EAAA,SACpC,WAAA,EAAa,0BAAA,CAA2B,cAAA;EAAA,SACxC,iBAAA,EAAmB,0BAAA,CAA2B,oBAAA;EAAA,SAC9C,SAAA,EAAW,0BAAA,CAA2B,YAAA;EAAA,SACtC,OAAA,EAAS,0BAAA,CAA2B,UAAA;EAAA,SACpC,mBAAA,EAAqB,0BAAA,CAA2B,sBAAA;EAAA,SAChD,OAAA,EAAS,0BAAA,CAA2B,UAAA;EAAA,SACpC,wBAAA,EAA0B,0BAAA,CAA2B,2BAAA;EAAA,SACrD,oBAAA,EAAsB,0BAAA,CAA2B,uBAAA;EAAA,SACjD,WAAA,EAAa,0BAAA,CAA2B,cAAA;EAAA,SACxC,MAAA,EAAQ,0BAAA,CAA2B,SAAA;EAAA,SACnC,iBAAA,EAAmB,0BAAA,CAA2B,oBAAA;EAAA,SAC9C,mBAAA,EAAqB,0BAAA,CAA2B,sBAAA;EAAA,SAChD,kBAAA,EAAoB,0BAAA,CAA2B,qBAAA;EAAA,SAC/C,OAAA,EAAS,0BAAA,CAA2B,UAAA;EAAA,SACpC,iBAAA,EAAmB,0BAAA,CAA2B,oBAAA;EAAA,SAC9C,yBAAA,EAA2B,0BAAA,CAA2B,4BAAA;EAAA,SACtD,oBAAA,EAAsB,0BAAA,CAA2B,iCAAA;cAE9C,IAAA,EAAM,iBAAA;AAAA;;;KCvGR,UAAA;EDuBP;;;;EClBH,WAAA;EDoBU;;;;;ECbV,WAAA;AAAA,IACE,IAAI,CAAC,aAAA;;;;;cAMI,IAAA;EAAA,SACF,GAAA,EAAK,aAAA;EAAA,SACL,MAAA,EAAQ,UAAA;EAAA,SACR,IAAA,GAAO,gBAAA;EAAA,SACP,WAAA,EAAa,GAAA;EACtB,WAAA,EAAa,OAAA;EACb,WAAA;cAEY,MAAA,EAAQ,UAAA;EAqId,mBAAA,IAAuB,OAAA;EA4B7B,gBAAA,CAAiB,IAAA;EDlKkD;AAAA;;;;ECmLnE,yBAAA,CAA0B,IAAA,WAAY,yBAAA;AAAA"}
@@ -65,8 +65,11 @@ function buildSubject(template, params) {
65
65
  var NovaNatsClient = class {
66
66
  config;
67
67
  connectionPromise = null;
68
- constructor(config) {
69
- this.config = config;
68
+ constructor(nova, config = {}) {
69
+ this.config = {
70
+ servers: buildNatsServerUrl(nova.instanceUrl.href),
71
+ ...config
72
+ };
70
73
  }
71
74
  /**
72
75
  * Connects to NATS if not already connected or connecting, and returns the
@@ -99,7 +102,8 @@ var NovaNatsClient = class {
99
102
  *
100
103
  * Returns a function that unsubscribes when called.
101
104
  */
102
- async subscribe(subject, params, handler) {
105
+ async subscribe(subject, ...args) {
106
+ const [params, handler] = args.length === 1 ? [{}, args[0]] : [args[0], args[1]];
103
107
  const nc = await this.connect();
104
108
  const resolvedSubject = buildSubject(subject, params);
105
109
  const sub = nc.subscribe(resolvedSubject);
@@ -123,6 +127,19 @@ var NovaNatsClient = class {
123
127
  const resolvedSubject = buildSubject(subject, params);
124
128
  return (await nc.request(resolvedSubject, JSON.stringify(payload), { timeout: opts.timeout ?? 5e3 })).json();
125
129
  }
130
+ /**
131
+ * Publishes a JSON payload to any NATS subject defined in the spec,
132
+ * without waiting for a reply.
133
+ *
134
+ * `subject` is the subject template as it appears on the wire, e.g.
135
+ * `"nova.v2.cells.{cell}.bus-ios.ios.set"`, with `{param}` placeholders
136
+ * filled in from `params`.
137
+ */
138
+ async publish(subject, params, payload) {
139
+ const nc = await this.connect();
140
+ const resolvedSubject = buildSubject(subject, params);
141
+ nc.publish(resolvedSubject, JSON.stringify(payload));
142
+ }
126
143
  };
127
144
  //#endregion
128
145
  exports.NovaNatsClient = NovaNatsClient;
@@ -1 +1 @@
1
- {"version":3,"file":"index.cjs","names":["parseNovaInstanceUrl"],"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 char === \"*\"\n )\n}\n\nfunction isValidSubjectValue(value: string): boolean {\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 {\n type ConnectionOptions,\n type Msg,\n type NatsConnection,\n wsconnect,\n} from \"@nats-io/nats-core\"\nimport { buildSubject } from \"./buildSubject.ts\"\nimport type {\n NatsOperationParams,\n NatsReplyPayloads,\n NatsRequestPayloads,\n NatsRequestSubject,\n NatsSubscribePayloads,\n NatsSubscribeSubject,\n} from \"./generated/operations.ts\"\n\nexport type NovaNatsClientConfig = ConnectionOptions\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(config: NovaNatsClientConfig) {\n this.config = config\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 * Returns a function that unsubscribes when called.\n */\n async subscribe<K extends NatsSubscribeSubject>(\n subject: K,\n params: NatsOperationParams[K],\n handler: (payload: NatsSubscribePayloads[K], msg: Msg) => void,\n ): Promise<() => void> {\n const nc = await this.connect()\n const resolvedSubject = buildSubject(subject, params)\n const sub = nc.subscribe(resolvedSubject)\n\n ;(async () => {\n for await (const msg of sub) {\n handler(msg.json<NatsSubscribePayloads[K]>(), msg)\n }\n })().catch((err: unknown) => {\n console.error(\n `Error handling NATS subscription for \"${resolvedSubject}\"`,\n err,\n )\n })\n\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"],"mappings":";;;;;;;;;;;;AAUA,SAAgB,mBAAmB,aAA6B;CAC9D,MAAM,MAAMA,mBAAAA,qBAAqB,WAAW;CAE5C,OAAO,GADU,IAAI,aAAa,WAAW,SAAS,MACnC,IAAI,IAAI,KAAK;AAClC;;;;;;;;ACRA,SAAS,mBAAmB,MAAuB;CACjD,MAAM,OAAO,KAAK,WAAW,CAAC;CAC9B,OACG,QAAQ,MAAM,QAAQ,MACtB,QAAQ,MAAM,QAAQ,MACtB,QAAQ,MAAM,QAAQ,OACvB,SAAS,OACT,SAAS,OACT,SAAS;AAEb;AAEA,SAAS,oBAAoB,OAAwB;CACnD,IAAI,MAAM,WAAW,GAAG,OAAO;CAC/B,KAAK,MAAM,QAAQ,OACjB,IAAI,CAAC,mBAAmB,IAAI,GAAG,OAAO;CAExC,OAAO;AACT;AAEA,SAAgB,aACd,UACA,QACQ;CAIR,IAAI,SAAS;CACb,IAAI,SAAS;CAEb,OAAO,SAAS,SAAS,QAAQ;EAC/B,MAAM,YAAY,SAAS,QAAQ,KAAK,MAAM;EAC9C,IAAI,cAAc,IAAI;GACpB,UAAU,SAAS,MAAM,MAAM;GAC/B;EACF;EAEA,MAAM,aAAa,SAAS,QAAQ,KAAK,YAAY,CAAC;EACtD,IAAI,eAAe,IAAI;GACrB,UAAU,SAAS,MAAM,MAAM;GAC/B;EACF;EAEA,UAAU,SAAS,MAAM,QAAQ,SAAS;EAC1C,MAAM,YAAY,SAAS,MAAM,YAAY,GAAG,UAAU;EAC1D,MAAM,QAAQ,OAAO;EACrB,IAAI,UAAU,KAAA,GACZ,MAAM,IAAI,MACR,wCAAwC,UAAU,iBAAiB,SAAS,EAC9E;EAEF,IAAI,CAAC,oBAAoB,KAAK,GAC5B,MAAM,IAAI,MACR,wCAAwC,UAAU,MAAM,MAAM,qEAChE;EAEF,UAAU;EAEV,SAAS,aAAa;CACxB;CAEA,OAAO;AACT;;;;;;;;;AC5CA,IAAa,iBAAb,MAA4B;CAC1B;CACA,oBAA4D;CAE5D,YAAY,QAA8B;EACxC,KAAK,SAAS;CAChB;;;;;;CAOA,UAAmC;EACjC,IAAI,CAAC,KAAK,mBACR,KAAK,qBAAA,GAAA,mBAAA,WAA8B,KAAK,MAAM,EAAE,OAAO,QAAiB;GAEtE,KAAK,oBAAoB;GACzB,MAAM;EACR,CAAC;EAEH,OAAO,KAAK;CACd;;CAGA,MAAM,QAAuB;EAC3B,MAAM,oBAAoB,KAAK;EAC/B,KAAK,oBAAoB;EACzB,IAAI,CAAC,mBAAmB;EACxB,IAAI;GAEF,OAAM,MADW,mBACR,MAAM;EACjB,QAAQ,CAER;CACF;;;;;;;;;;;CAYA,MAAM,UACJ,SACA,QACA,SACqB;EACrB,MAAM,KAAK,MAAM,KAAK,QAAQ;EAC9B,MAAM,kBAAkB,aAAa,SAAS,MAAM;EACpD,MAAM,MAAM,GAAG,UAAU,eAAe;EAEvC,CAAC,YAAY;GACZ,WAAW,MAAM,OAAO,KACtB,QAAQ,IAAI,KAA+B,GAAG,GAAG;EAErD,GAAG,EAAE,OAAO,QAAiB;GAC3B,QAAQ,MACN,yCAAyC,gBAAgB,IACzD,GACF;EACF,CAAC;EAED,aAAa,IAAI,YAAY;CAC/B;;;;;;;;;CAUA,MAAM,QACJ,SACA,QACA,SACA,OAA6B,CAAC,GACC;EAC/B,MAAM,KAAK,MAAM,KAAK,QAAQ;EAC9B,MAAM,kBAAkB,aAAa,SAAS,MAAM;EAIpD,QAAO,MAHW,GAAG,QAAQ,iBAAiB,KAAK,UAAU,OAAO,GAAG,EACrE,SAAS,KAAK,WAAW,IAC3B,CAAC,GACU,KAA2B;CACxC;AACF"}
1
+ {"version":3,"file":"index.cjs","names":["parseNovaInstanceUrl"],"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 char === \"*\"\n )\n}\n\nfunction isValidSubjectValue(value: string): boolean {\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 {\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 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\ntype NatsMessageHandler<K extends NatsSubscribeSubject> = (\n payload: NatsSubscribePayloads[K],\n msg: Msg,\n) => void\n\ntype SubscribeArgs<K extends NatsSubscribeSubject> =\n keyof NatsOperationParams[K] extends never\n ? [handler: NatsMessageHandler<K>]\n : [params: NatsOperationParams[K], handler: NatsMessageHandler<K>]\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 ...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 * 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] =\n args.length === 1\n ? ([{}, args[0]] as const)\n : ([args[0], args[1]] as const)\n\n const nc = await this.connect()\n const resolvedSubject = buildSubject(subject, params)\n const sub = nc.subscribe(resolvedSubject)\n\n ;(async () => {\n for await (const msg of sub) {\n handler(msg.json<NatsSubscribePayloads[K]>(), msg)\n }\n })().catch((err: unknown) => {\n console.error(\n `Error handling NATS subscription for \"${resolvedSubject}\"`,\n err,\n )\n })\n\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":";;;;;;;;;;;;AAUA,SAAgB,mBAAmB,aAA6B;CAC9D,MAAM,MAAMA,mBAAAA,qBAAqB,WAAW;CAE5C,OAAO,GADU,IAAI,aAAa,WAAW,SAAS,MACnC,IAAI,IAAI,KAAK;AAClC;;;;;;;;ACRA,SAAS,mBAAmB,MAAuB;CACjD,MAAM,OAAO,KAAK,WAAW,CAAC;CAC9B,OACG,QAAQ,MAAM,QAAQ,MACtB,QAAQ,MAAM,QAAQ,MACtB,QAAQ,MAAM,QAAQ,OACvB,SAAS,OACT,SAAS,OACT,SAAS;AAEb;AAEA,SAAS,oBAAoB,OAAwB;CACnD,IAAI,MAAM,WAAW,GAAG,OAAO;CAC/B,KAAK,MAAM,QAAQ,OACjB,IAAI,CAAC,mBAAmB,IAAI,GAAG,OAAO;CAExC,OAAO;AACT;AAEA,SAAgB,aACd,UACA,QACQ;CAIR,IAAI,SAAS;CACb,IAAI,SAAS;CAEb,OAAO,SAAS,SAAS,QAAQ;EAC/B,MAAM,YAAY,SAAS,QAAQ,KAAK,MAAM;EAC9C,IAAI,cAAc,IAAI;GACpB,UAAU,SAAS,MAAM,MAAM;GAC/B;EACF;EAEA,MAAM,aAAa,SAAS,QAAQ,KAAK,YAAY,CAAC;EACtD,IAAI,eAAe,IAAI;GACrB,UAAU,SAAS,MAAM,MAAM;GAC/B;EACF;EAEA,UAAU,SAAS,MAAM,QAAQ,SAAS;EAC1C,MAAM,YAAY,SAAS,MAAM,YAAY,GAAG,UAAU;EAC1D,MAAM,QAAQ,OAAO;EACrB,IAAI,UAAU,KAAA,GACZ,MAAM,IAAI,MACR,wCAAwC,UAAU,iBAAiB,SAAS,EAC9E;EAEF,IAAI,CAAC,oBAAoB,KAAK,GAC5B,MAAM,IAAI,MACR,wCAAwC,UAAU,MAAM,MAAM,qEAChE;EAEF,UAAU;EAEV,SAAS,aAAa;CACxB;CAEA,OAAO;AACT;;;;;;;;;AC9BA,IAAa,iBAAb,MAA4B;CAC1B;CACA,oBAA4D;CAE5D,YAAY,MAAY,SAA+B,CAAC,GAAG;EACzD,KAAK,SAAS;GACZ,SAAS,mBAAmB,KAAK,YAAY,IAAI;GACjD,GAAG;EACL;CACF;;;;;;CAOA,UAAmC;EACjC,IAAI,CAAC,KAAK,mBACR,KAAK,qBAAA,GAAA,mBAAA,WAA8B,KAAK,MAAM,EAAE,OAAO,QAAiB;GAEtE,KAAK,oBAAoB;GACzB,MAAM;EACR,CAAC;EAEH,OAAO,KAAK;CACd;;CAGA,MAAM,QAAuB;EAC3B,MAAM,oBAAoB,KAAK;EAC/B,KAAK,oBAAoB;EACzB,IAAI,CAAC,mBAAmB;EACxB,IAAI;GAEF,OAAM,MADW,mBACR,MAAM;EACjB,QAAQ,CAER;CACF;;;;;;;;;;;CAYA,MAAM,UACJ,SACA,GAAG,MACkB;EACrB,MAAM,CAAC,QAAQ,WACb,KAAK,WAAW,IACX,CAAC,CAAC,GAAG,KAAK,EAAE,IACZ,CAAC,KAAK,IAAI,KAAK,EAAE;EAExB,MAAM,KAAK,MAAM,KAAK,QAAQ;EAC9B,MAAM,kBAAkB,aAAa,SAAS,MAAM;EACpD,MAAM,MAAM,GAAG,UAAU,eAAe;EAEvC,CAAC,YAAY;GACZ,WAAW,MAAM,OAAO,KACtB,QAAQ,IAAI,KAA+B,GAAG,GAAG;EAErD,GAAG,EAAE,OAAO,QAAiB;GAC3B,QAAQ,MACN,yCAAyC,gBAAgB,IACzD,GACF;EACF,CAAC;EAED,aAAa,IAAI,YAAY;CAC/B;;;;;;;;;CAUA,MAAM,QACJ,SACA,QACA,SACA,OAA6B,CAAC,GACC;EAC/B,MAAM,KAAK,MAAM,KAAK,QAAQ;EAC9B,MAAM,kBAAkB,aAAa,SAAS,MAAM;EAIpD,QAAO,MAHW,GAAG,QAAQ,iBAAiB,KAAK,UAAU,OAAO,GAAG,EACrE,SAAS,KAAK,WAAW,IAC3B,CAAC,GACU,KAA2B;CACxC;;;;;;;;;CAUA,MAAM,QACJ,SACA,QACA,SACe;EACf,MAAM,KAAK,MAAM,KAAK,QAAQ;EAC9B,MAAM,kBAAkB,aAAa,SAAS,MAAM;EACpD,GAAG,QAAQ,iBAAiB,KAAK,UAAU,OAAO,CAAC;CACrD;AACF"}