@wandelbots/nova-js 3.12.0 → 3.13.0-pr.307.0c31cef

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,104 @@
1
+ Object.defineProperty(exports, Symbol.toStringTag, { value: "Module" });
2
+ let _nats_io_nats_core = require("@nats-io/nats-core");
3
+ //#region src/lib/experimental/nats/buildSubject.ts
4
+ /**
5
+ * Builds a NATS subject from an AsyncAPI-style channel address template
6
+ * (e.g. `"{instance}.v2.cells.{cell}"`) by substituting each `{param}`
7
+ * placeholder with the corresponding value from `params`.
8
+ */
9
+ function isValidSubjectChar(char) {
10
+ const code = char.charCodeAt(0);
11
+ return code >= 48 && code <= 57 || code >= 65 && code <= 90 || code >= 97 && code <= 122 || char === "-" || char === "_";
12
+ }
13
+ function isValidSubjectValue(value) {
14
+ if (value.length === 0) return false;
15
+ for (const char of value) if (!isValidSubjectChar(char)) return false;
16
+ return true;
17
+ }
18
+ function buildSubject(template, params) {
19
+ let result = "";
20
+ let cursor = 0;
21
+ while (cursor < template.length) {
22
+ const openIndex = template.indexOf("{", cursor);
23
+ if (openIndex === -1) {
24
+ result += template.slice(cursor);
25
+ break;
26
+ }
27
+ const closeIndex = template.indexOf("}", openIndex + 1);
28
+ if (closeIndex === -1) {
29
+ result += template.slice(cursor);
30
+ break;
31
+ }
32
+ result += template.slice(cursor, openIndex);
33
+ const paramName = template.slice(openIndex + 1, closeIndex);
34
+ const value = params[paramName];
35
+ if (value === void 0) throw new Error(`Missing value for subject parameter "${paramName}" in template "${template}"`);
36
+ if (!isValidSubjectValue(value)) throw new Error(`Invalid value for subject parameter "${paramName}": "${value}" (must be non-empty and contain only letters, digits, "-", and "_")`);
37
+ result += value;
38
+ cursor = closeIndex + 1;
39
+ }
40
+ return result;
41
+ }
42
+ //#endregion
43
+ //#region src/lib/experimental/nats/NovaNatsClient.ts
44
+ /**
45
+ * Typed NATS client for the Wandelbots NOVA messaging API, generated from
46
+ * src/asyncapi.yaml (see scripts/generate-nats-client.ts).
47
+ *
48
+ * Connects over WebSocket via `@nats-io/nats-core`'s `wsconnect`.
49
+ */
50
+ var NovaNatsClient = class {
51
+ config;
52
+ connection = null;
53
+ constructor(config) {
54
+ this.config = config;
55
+ }
56
+ /** Connects to NATS if not already connected, and returns the connection. */
57
+ async connect() {
58
+ if (!this.connection) this.connection = await (0, _nats_io_nats_core.wsconnect)(this.config);
59
+ return this.connection;
60
+ }
61
+ /** Closes the underlying NATS connection, if open. */
62
+ async close() {
63
+ await this.connection?.close();
64
+ this.connection = null;
65
+ }
66
+ /**
67
+ * Subscribes to a NATS subject published by the server, invoking `handler`
68
+ * with the JSON-decoded payload of every message received.
69
+ *
70
+ * `subject` is the subject template as it appears on the wire, e.g.
71
+ * `"nova.v2.cells.{cell}"`, with `{param}` placeholders filled in from
72
+ * `params`.
73
+ *
74
+ * Returns a function that unsubscribes when called.
75
+ */
76
+ async subscribe(subject, params, handler) {
77
+ const nc = await this.connect();
78
+ const resolvedSubject = buildSubject(subject, params);
79
+ const sub = nc.subscribe(resolvedSubject);
80
+ (async () => {
81
+ for await (const msg of sub) handler(msg.json(), msg);
82
+ })().catch((err) => {
83
+ console.error(`Error handling NATS subscription for "${resolvedSubject}"`, err);
84
+ });
85
+ return () => sub.unsubscribe();
86
+ }
87
+ /**
88
+ * Sends a request payload for a NATS subject the server receives, and
89
+ * waits for the JSON-decoded reply.
90
+ *
91
+ * `subject` is the subject template as it appears on the wire, e.g.
92
+ * `"nova.v2.cells.{cell}.bus-ios.ios.set"`, with `{param}` placeholders
93
+ * filled in from `params`.
94
+ */
95
+ async request(subject, params, payload, opts = {}) {
96
+ const nc = await this.connect();
97
+ const resolvedSubject = buildSubject(subject, params);
98
+ return (await nc.request(resolvedSubject, JSON.stringify(payload), { timeout: opts.timeout ?? 5e3 })).json();
99
+ }
100
+ };
101
+ //#endregion
102
+ exports.NovaNatsClient = NovaNatsClient;
103
+
104
+ //# sourceMappingURL=index.cjs.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"index.cjs","names":[],"sources":["../../../src/lib/experimental/nats/buildSubject.ts","../../../src/lib/experimental/nats/NovaNatsClient.ts"],"sourcesContent":["/**\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 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 connection: NatsConnection | null = null\n\n constructor(config: NovaNatsClientConfig) {\n this.config = config\n }\n\n /** Connects to NATS if not already connected, and returns the connection. */\n async connect(): Promise<NatsConnection> {\n if (!this.connection) {\n this.connection = await wsconnect(this.config)\n }\n return this.connection\n }\n\n /** Closes the underlying NATS connection, if open. */\n async close(): Promise<void> {\n await this.connection?.close()\n this.connection = null\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":";;;;;;;;AAMA,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;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;;;;;;;;;AC3CA,IAAa,iBAAb,MAA4B;CAC1B;CACA,aAAoC;CAEpC,YAAY,QAA8B;EACxC,KAAK,SAAS;CAChB;;CAGA,MAAM,UAAmC;EACvC,IAAI,CAAC,KAAK,YACR,KAAK,aAAa,OAAA,GAAA,mBAAA,WAAgB,KAAK,MAAM;EAE/C,OAAO,KAAK;CACd;;CAGA,MAAM,QAAuB;EAC3B,MAAM,KAAK,YAAY,MAAM;EAC7B,KAAK,aAAa;CACpB;;;;;;;;;;;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"}