@eyr1n/ros-cdr-client 0.0.1

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/LICENSE ADDED
@@ -0,0 +1,21 @@
1
+ MIT License
2
+
3
+ Copyright (c) 2026 Rin Iwai
4
+
5
+ Permission is hereby granted, free of charge, to any person obtaining a copy
6
+ of this software and associated documentation files (the "Software"), to deal
7
+ in the Software without restriction, including without limitation the rights
8
+ to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
9
+ copies of the Software, and to permit persons to whom the Software is
10
+ furnished to do so, subject to the following conditions:
11
+
12
+ The above copyright notice and this permission notice shall be included in all
13
+ copies or substantial portions of the Software.
14
+
15
+ THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
16
+ IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
17
+ FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
18
+ AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
19
+ LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
20
+ OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
21
+ SOFTWARE.
@@ -0,0 +1,19 @@
1
+ declare const brand: unique symbol;
2
+ type Branded<T, Brand> = T & {
3
+ [brand]: Brand;
4
+ };
5
+ export type PublisherId = Branded<number, 'PublisherId'>;
6
+ export type SubscriptionId = Branded<number, 'SubscriptionId'>;
7
+ export type ServiceClientId = Branded<number, 'ServiceClientId'>;
8
+ export declare class RosCdrClient {
9
+ #private;
10
+ constructor(ws: WebSocket);
11
+ createPublisher(name: string, type: string): Promise<PublisherId>;
12
+ createSubscription(name: string, type: string, callback: (message: DataView) => void): Promise<SubscriptionId>;
13
+ createServiceClient(name: string, type: string): Promise<ServiceClientId>;
14
+ publish(id: PublisherId, message: Uint8Array): void;
15
+ callService(id: ServiceClientId, callId: number, request: Uint8Array): Promise<DataView>;
16
+ destroy(id: PublisherId | SubscriptionId | ServiceClientId): void;
17
+ }
18
+ export {};
19
+ //# sourceMappingURL=index.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../../src/index.ts"],"names":[],"mappings":"AAAA,QAAA,MAAM,KAAK,eAAW,CAAC;AACvB,KAAK,OAAO,CAAC,CAAC,EAAE,KAAK,IAAI,CAAC,GAAG;IAAE,CAAC,KAAK,CAAC,EAAE,KAAK,CAAA;CAAE,CAAC;AAgBhD,MAAM,MAAM,WAAW,GAAG,OAAO,CAAC,MAAM,EAAE,aAAa,CAAC,CAAC;AACzD,MAAM,MAAM,cAAc,GAAG,OAAO,CAAC,MAAM,EAAE,gBAAgB,CAAC,CAAC;AAC/D,MAAM,MAAM,eAAe,GAAG,OAAO,CAAC,MAAM,EAAE,iBAAiB,CAAC,CAAC;AAwGjE,qBAAa,YAAY;;gBAUX,EAAE,EAAE,SAAS;IAMnB,eAAe,CAAC,IAAI,EAAE,MAAM,EAAE,IAAI,EAAE,MAAM,GAAG,OAAO,CAAC,WAAW,CAAC;IAKjE,kBAAkB,CACtB,IAAI,EAAE,MAAM,EACZ,IAAI,EAAE,MAAM,EACZ,QAAQ,EAAE,CAAC,OAAO,EAAE,QAAQ,KAAK,IAAI,GACpC,OAAO,CAAC,cAAc,CAAC;IASpB,mBAAmB,CACvB,IAAI,EAAE,MAAM,EACZ,IAAI,EAAE,MAAM,GACX,OAAO,CAAC,eAAe,CAAC;IAK3B,OAAO,CAAC,EAAE,EAAE,WAAW,EAAE,OAAO,EAAE,UAAU;IAS5C,WAAW,CACT,EAAE,EAAE,eAAe,EACnB,MAAM,EAAE,MAAM,EACd,OAAO,EAAE,UAAU,GAClB,OAAO,CAAC,QAAQ,CAAC;IAepB,OAAO,CAAC,EAAE,EAAE,WAAW,GAAG,cAAc,GAAG,eAAe;CAmE3D"}
@@ -0,0 +1,174 @@
1
+ const brand = Symbol();
2
+ const OP_CREATE_SUBSCRIPTION = 'create_subscription';
3
+ const OP_CREATE_PUBLISHER = 'create_publisher';
4
+ const OP_CREATE_SERVICE_CLIENT = 'create_service_client';
5
+ const OP_DESTROY = 'destroy';
6
+ const OP_TOPIC = 1;
7
+ const OP_SERVICE_REQUEST = 2;
8
+ const OP_SERVICE_RESPONSE = 3;
9
+ function isObject(value) {
10
+ return value !== null && typeof value === 'object';
11
+ }
12
+ function isCreateOp(value) {
13
+ return (value === OP_CREATE_PUBLISHER ||
14
+ value === OP_CREATE_SUBSCRIPTION ||
15
+ value === OP_CREATE_SERVICE_CLIENT);
16
+ }
17
+ function parseTextPayload(payload) {
18
+ let response;
19
+ try {
20
+ response = JSON.parse(payload);
21
+ }
22
+ catch {
23
+ return null;
24
+ }
25
+ if (!isObject(response)) {
26
+ return null;
27
+ }
28
+ if (!('op' in response) || !isCreateOp(response.op)) {
29
+ return null;
30
+ }
31
+ if (!('name' in response) || typeof response.name !== 'string') {
32
+ return null;
33
+ }
34
+ if (!('id' in response) || typeof response.id !== 'number') {
35
+ return null;
36
+ }
37
+ return { op: response.op, name: response.name, id: response.id };
38
+ }
39
+ function parseBinaryPayload(buffer) {
40
+ const view = new DataView(buffer);
41
+ if (view.byteLength < 5) {
42
+ return null;
43
+ }
44
+ const opcode = view.getUint8(0);
45
+ if (opcode === OP_TOPIC) {
46
+ const id = view.getUint32(1, true);
47
+ return { opcode: OP_TOPIC, id, message: new DataView(buffer, 5) };
48
+ }
49
+ if (opcode === OP_SERVICE_RESPONSE) {
50
+ if (view.byteLength < 9) {
51
+ return null;
52
+ }
53
+ const id = view.getUint32(1, true);
54
+ const callId = view.getUint32(5, true);
55
+ return {
56
+ opcode: OP_SERVICE_RESPONSE,
57
+ id,
58
+ callId,
59
+ message: new DataView(buffer, 9),
60
+ };
61
+ }
62
+ return null;
63
+ }
64
+ function pendingCreateKey(op, name) {
65
+ return `${op}:${name}`;
66
+ }
67
+ function serviceResponseKey(id, callId) {
68
+ return `${id}:${callId}`;
69
+ }
70
+ export class RosCdrClient {
71
+ #ws;
72
+ #pendingCreates = new Map();
73
+ #subscriptions = new Map();
74
+ #serviceResponses = new Map();
75
+ constructor(ws) {
76
+ this.#ws = ws;
77
+ this.#ws.binaryType = 'arraybuffer';
78
+ this.#ws.onmessage = this.#onMessage.bind(this);
79
+ }
80
+ async createPublisher(name, type) {
81
+ const request = { op: OP_CREATE_PUBLISHER, name, type };
82
+ return this.#sendCreateRequest(request);
83
+ }
84
+ async createSubscription(name, type, callback) {
85
+ const request = { op: OP_CREATE_SUBSCRIPTION, name, type };
86
+ const id = await this.#sendCreateRequest(request);
87
+ this.#subscriptions.set(id, callback);
88
+ return id;
89
+ }
90
+ async createServiceClient(name, type) {
91
+ const request = { op: OP_CREATE_SERVICE_CLIENT, name, type };
92
+ return this.#sendCreateRequest(request);
93
+ }
94
+ publish(id, message) {
95
+ const payload = new Uint8Array(1 + 4 + message.length);
96
+ payload[0] = OP_TOPIC;
97
+ const view = new DataView(payload.buffer);
98
+ view.setUint32(1, id, true);
99
+ payload.set(message, 5);
100
+ this.#sendBinaryPayload(payload);
101
+ }
102
+ callService(id, callId, request) {
103
+ const payload = new Uint8Array(1 + 4 + 4 + request.length);
104
+ payload[0] = OP_SERVICE_REQUEST;
105
+ const view = new DataView(payload.buffer);
106
+ view.setUint32(1, id, true);
107
+ view.setUint32(5, callId, true);
108
+ payload.set(request, 9);
109
+ const key = serviceResponseKey(id, callId);
110
+ return new Promise((resolve) => {
111
+ this.#serviceResponses.set(key, resolve);
112
+ this.#sendBinaryPayload(payload);
113
+ });
114
+ }
115
+ destroy(id) {
116
+ const request = { op: OP_DESTROY, id };
117
+ this.#sendTextPayload(request);
118
+ this.#subscriptions.delete(id);
119
+ }
120
+ #sendCreateRequest(request) {
121
+ return new Promise((resolve) => {
122
+ this.#pendingCreates.set(pendingCreateKey(request.op, request.name), resolve);
123
+ this.#sendTextPayload(request);
124
+ });
125
+ }
126
+ #sendTextPayload(payload) {
127
+ this.#ws.send(JSON.stringify(payload));
128
+ }
129
+ #sendBinaryPayload(payload) {
130
+ this.#ws.send(payload);
131
+ }
132
+ #onMessage(event) {
133
+ if (typeof event.data === 'string') {
134
+ this.#onTextPayload(event.data);
135
+ return;
136
+ }
137
+ if (event.data instanceof ArrayBuffer) {
138
+ this.#onBinaryPayload(event.data);
139
+ return;
140
+ }
141
+ }
142
+ #onTextPayload(payload) {
143
+ const response = parseTextPayload(payload);
144
+ if (!response) {
145
+ return;
146
+ }
147
+ const key = pendingCreateKey(response.op, response.name);
148
+ const callback = this.#pendingCreates.get(key);
149
+ if (callback) {
150
+ this.#pendingCreates.delete(key);
151
+ callback(response.id);
152
+ }
153
+ }
154
+ #onBinaryPayload(payload) {
155
+ const message = parseBinaryPayload(payload);
156
+ if (!message) {
157
+ return;
158
+ }
159
+ if (message.opcode === OP_TOPIC) {
160
+ this.#subscriptions.get(message.id)?.(message.message);
161
+ return;
162
+ }
163
+ if (message.opcode === OP_SERVICE_RESPONSE) {
164
+ const key = serviceResponseKey(message.id, message.callId);
165
+ const callback = this.#serviceResponses.get(key);
166
+ if (callback) {
167
+ this.#serviceResponses.delete(key);
168
+ callback(message.message);
169
+ }
170
+ return;
171
+ }
172
+ }
173
+ }
174
+ //# sourceMappingURL=index.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"index.js","sourceRoot":"","sources":["../../src/index.ts"],"names":[],"mappings":"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"}
@@ -0,0 +1 @@
1
+ {"fileNames":["../../../node_modules/.pnpm/typescript@5.9.3/node_modules/typescript/lib/lib.es5.d.ts","../../../node_modules/.pnpm/typescript@5.9.3/node_modules/typescript/lib/lib.es2015.d.ts","../../../node_modules/.pnpm/typescript@5.9.3/node_modules/typescript/lib/lib.es2016.d.ts","../../../node_modules/.pnpm/typescript@5.9.3/node_modules/typescript/lib/lib.es2017.d.ts","../../../node_modules/.pnpm/typescript@5.9.3/node_modules/typescript/lib/lib.es2018.d.ts","../../../node_modules/.pnpm/typescript@5.9.3/node_modules/typescript/lib/lib.es2019.d.ts","../../../node_modules/.pnpm/typescript@5.9.3/node_modules/typescript/lib/lib.es2020.d.ts","../../../node_modules/.pnpm/typescript@5.9.3/node_modules/typescript/lib/lib.es2021.d.ts","../../../node_modules/.pnpm/typescript@5.9.3/node_modules/typescript/lib/lib.es2022.d.ts","../../../node_modules/.pnpm/typescript@5.9.3/node_modules/typescript/lib/lib.es2023.d.ts","../../../node_modules/.pnpm/typescript@5.9.3/node_modules/typescript/lib/lib.dom.d.ts","../../../node_modules/.pnpm/typescript@5.9.3/node_modules/typescript/lib/lib.dom.iterable.d.ts","../../../node_modules/.pnpm/typescript@5.9.3/node_modules/typescript/lib/lib.dom.asynciterable.d.ts","../../../node_modules/.pnpm/typescript@5.9.3/node_modules/typescript/lib/lib.webworker.importscripts.d.ts","../../../node_modules/.pnpm/typescript@5.9.3/node_modules/typescript/lib/lib.scripthost.d.ts","../../../node_modules/.pnpm/typescript@5.9.3/node_modules/typescript/lib/lib.es2015.core.d.ts","../../../node_modules/.pnpm/typescript@5.9.3/node_modules/typescript/lib/lib.es2015.collection.d.ts","../../../node_modules/.pnpm/typescript@5.9.3/node_modules/typescript/lib/lib.es2015.generator.d.ts","../../../node_modules/.pnpm/typescript@5.9.3/node_modules/typescript/lib/lib.es2015.iterable.d.ts","../../../node_modules/.pnpm/typescript@5.9.3/node_modules/typescript/lib/lib.es2015.promise.d.ts","../../../node_modules/.pnpm/typescript@5.9.3/node_modules/typescript/lib/lib.es2015.proxy.d.ts","../../../node_modules/.pnpm/typescript@5.9.3/node_modules/typescript/lib/lib.es2015.reflect.d.ts","../../../node_modules/.pnpm/typescript@5.9.3/node_modules/typescript/lib/lib.es2015.symbol.d.ts","../../../node_modules/.pnpm/typescript@5.9.3/node_modules/typescript/lib/lib.es2015.symbol.wellknown.d.ts","../../../node_modules/.pnpm/typescript@5.9.3/node_modules/typescript/lib/lib.es2016.array.include.d.ts","../../../node_modules/.pnpm/typescript@5.9.3/node_modules/typescript/lib/lib.es2016.intl.d.ts","../../../node_modules/.pnpm/typescript@5.9.3/node_modules/typescript/lib/lib.es2017.arraybuffer.d.ts","../../../node_modules/.pnpm/typescript@5.9.3/node_modules/typescript/lib/lib.es2017.date.d.ts","../../../node_modules/.pnpm/typescript@5.9.3/node_modules/typescript/lib/lib.es2017.object.d.ts","../../../node_modules/.pnpm/typescript@5.9.3/node_modules/typescript/lib/lib.es2017.sharedmemory.d.ts","../../../node_modules/.pnpm/typescript@5.9.3/node_modules/typescript/lib/lib.es2017.string.d.ts","../../../node_modules/.pnpm/typescript@5.9.3/node_modules/typescript/lib/lib.es2017.intl.d.ts","../../../node_modules/.pnpm/typescript@5.9.3/node_modules/typescript/lib/lib.es2017.typedarrays.d.ts","../../../node_modules/.pnpm/typescript@5.9.3/node_modules/typescript/lib/lib.es2018.asyncgenerator.d.ts","../../../node_modules/.pnpm/typescript@5.9.3/node_modules/typescript/lib/lib.es2018.asynciterable.d.ts","../../../node_modules/.pnpm/typescript@5.9.3/node_modules/typescript/lib/lib.es2018.intl.d.ts","../../../node_modules/.pnpm/typescript@5.9.3/node_modules/typescript/lib/lib.es2018.promise.d.ts","../../../node_modules/.pnpm/typescript@5.9.3/node_modules/typescript/lib/lib.es2018.regexp.d.ts","../../../node_modules/.pnpm/typescript@5.9.3/node_modules/typescript/lib/lib.es2019.array.d.ts","../../../node_modules/.pnpm/typescript@5.9.3/node_modules/typescript/lib/lib.es2019.object.d.ts","../../../node_modules/.pnpm/typescript@5.9.3/node_modules/typescript/lib/lib.es2019.string.d.ts","../../../node_modules/.pnpm/typescript@5.9.3/node_modules/typescript/lib/lib.es2019.symbol.d.ts","../../../node_modules/.pnpm/typescript@5.9.3/node_modules/typescript/lib/lib.es2019.intl.d.ts","../../../node_modules/.pnpm/typescript@5.9.3/node_modules/typescript/lib/lib.es2020.bigint.d.ts","../../../node_modules/.pnpm/typescript@5.9.3/node_modules/typescript/lib/lib.es2020.date.d.ts","../../../node_modules/.pnpm/typescript@5.9.3/node_modules/typescript/lib/lib.es2020.promise.d.ts","../../../node_modules/.pnpm/typescript@5.9.3/node_modules/typescript/lib/lib.es2020.sharedmemory.d.ts","../../../node_modules/.pnpm/typescript@5.9.3/node_modules/typescript/lib/lib.es2020.string.d.ts","../../../node_modules/.pnpm/typescript@5.9.3/node_modules/typescript/lib/lib.es2020.symbol.wellknown.d.ts","../../../node_modules/.pnpm/typescript@5.9.3/node_modules/typescript/lib/lib.es2020.intl.d.ts","../../../node_modules/.pnpm/typescript@5.9.3/node_modules/typescript/lib/lib.es2020.number.d.ts","../../../node_modules/.pnpm/typescript@5.9.3/node_modules/typescript/lib/lib.es2021.promise.d.ts","../../../node_modules/.pnpm/typescript@5.9.3/node_modules/typescript/lib/lib.es2021.string.d.ts","../../../node_modules/.pnpm/typescript@5.9.3/node_modules/typescript/lib/lib.es2021.weakref.d.ts","../../../node_modules/.pnpm/typescript@5.9.3/node_modules/typescript/lib/lib.es2021.intl.d.ts","../../../node_modules/.pnpm/typescript@5.9.3/node_modules/typescript/lib/lib.es2022.array.d.ts","../../../node_modules/.pnpm/typescript@5.9.3/node_modules/typescript/lib/lib.es2022.error.d.ts","../../../node_modules/.pnpm/typescript@5.9.3/node_modules/typescript/lib/lib.es2022.intl.d.ts","../../../node_modules/.pnpm/typescript@5.9.3/node_modules/typescript/lib/lib.es2022.object.d.ts","../../../node_modules/.pnpm/typescript@5.9.3/node_modules/typescript/lib/lib.es2022.string.d.ts","../../../node_modules/.pnpm/typescript@5.9.3/node_modules/typescript/lib/lib.es2022.regexp.d.ts","../../../node_modules/.pnpm/typescript@5.9.3/node_modules/typescript/lib/lib.es2023.array.d.ts","../../../node_modules/.pnpm/typescript@5.9.3/node_modules/typescript/lib/lib.es2023.collection.d.ts","../../../node_modules/.pnpm/typescript@5.9.3/node_modules/typescript/lib/lib.es2023.intl.d.ts","../../../node_modules/.pnpm/typescript@5.9.3/node_modules/typescript/lib/lib.decorators.d.ts","../../../node_modules/.pnpm/typescript@5.9.3/node_modules/typescript/lib/lib.decorators.legacy.d.ts","../../../node_modules/.pnpm/typescript@5.9.3/node_modules/typescript/lib/lib.es2023.full.d.ts","../src/index.ts"],"fileInfos":[{"version":"c430d44666289dae81f30fa7b2edebf186ecc91a2d4c71266ea6ae76388792e1","affectsGlobalScope":true,"impliedFormat":1},{"version":"45b7ab580deca34ae9729e97c13cfd999df04416a79116c3bfb483804f85ded4","impliedFormat":1},{"version":"3facaf05f0c5fc569c5649dd359892c98a85557e3e0c847964caeb67076f4d75","impliedFormat":1},{"version":"e44bb8bbac7f10ecc786703fe0a6a4b952189f908707980ba8f3c8975a760962","impliedFormat":1},{"version":"5e1c4c362065a6b95ff952c0eab010f04dcd2c3494e813b493ecfd4fcb9fc0d8","impliedFormat":1},{"version":"68d73b4a11549f9c0b7d352d10e91e5dca8faa3322bfb77b661839c42b1ddec7","impliedFormat":1},{"version":"5efce4fc3c29ea84e8928f97adec086e3dc876365e0982cc8479a07954a3efd4","impliedFormat":1},{"version":"feecb1be483ed332fad555aff858affd90a48ab19ba7272ee084704eb7167569","impliedFormat":1},{"version":"ee7bad0c15b58988daa84371e0b89d313b762ab83cb5b31b8a2d1162e8eb41c2","impliedFormat":1},{"version":"27bdc30a0e32783366a5abeda841bc22757c1797de8681bbe81fbc735eeb1c10","impliedFormat":1},{"version":"080941d9f9ff9307f7e27a83bcd888b7c8270716c39af943532438932ec1d0b9","affectsGlobalScope":true,"impliedFormat":1},{"version":"2e80ee7a49e8ac312cc11b77f1475804bee36b3b2bc896bead8b6e1266befb43","affectsGlobalScope":true,"impliedFormat":1},{"version":"d7a3c8b952931daebdfc7a2897c53c0a1c73624593fa070e46bd537e64dcd20a","affectsGlobalScope":true,"impliedFormat":1},{"version":"80e18897e5884b6723488d4f5652167e7bb5024f946743134ecc4aa4ee731f89","affectsGlobalScope":true,"impliedFormat":1},{"version":"cd034f499c6cdca722b60c04b5b1b78e058487a7085a8e0d6fb50809947ee573","affectsGlobalScope":true,"impliedFormat":1},{"version":"c57796738e7f83dbc4b8e65132f11a377649c00dd3eee333f672b8f0a6bea671","affectsGlobalScope":true,"impliedFormat":1},{"version":"dc2df20b1bcdc8c2d34af4926e2c3ab15ffe1160a63e58b7e09833f616efff44","affectsGlobalScope":true,"impliedFormat":1},{"version":"515d0b7b9bea2e31ea4ec968e9edd2c39d3eebf4a2d5cbd04e88639819ae3b71","affectsGlobalScope":true,"impliedFormat":1},{"version":"0559b1f683ac7505ae451f9a96ce4c3c92bdc71411651ca6ddb0e88baaaad6a3","affectsGlobalScope":true,"impliedFormat":1},{"version":"0dc1e7ceda9b8b9b455c3a2d67b0412feab00bd2f66656cd8850e8831b08b537","affectsGlobalScope":true,"impliedFormat":1},{"version":"ce691fb9e5c64efb9547083e4a34091bcbe5bdb41027e310ebba8f7d96a98671","affectsGlobalScope":true,"impliedFormat":1},{"version":"8d697a2a929a5fcb38b7a65594020fcef05ec1630804a33748829c5ff53640d0","affectsGlobalScope":true,"impliedFormat":1},{"version":"4ff2a353abf8a80ee399af572debb8faab2d33ad38c4b4474cff7f26e7653b8d","affectsGlobalScope":true,"impliedFormat":1},{"version":"fb0f136d372979348d59b3f5020b4cdb81b5504192b1cacff5d1fbba29378aa1","affectsGlobalScope":true,"impliedFormat":1},{"version":"d15bea3d62cbbdb9797079416b8ac375ae99162a7fba5de2c6c505446486ac0a","affectsGlobalScope":true,"impliedFormat":1},{"version":"68d18b664c9d32a7336a70235958b8997ebc1c3b8505f4f1ae2b7e7753b87618","affectsGlobalScope":true,"impliedFormat":1},{"version":"eb3d66c8327153d8fa7dd03f9c58d351107fe824c79e9b56b462935176cdf12a","affectsGlobalScope":true,"impliedFormat":1},{"version":"38f0219c9e23c915ef9790ab1d680440d95419ad264816fa15009a8851e79119","affectsGlobalScope":true,"impliedFormat":1},{"version":"69ab18c3b76cd9b1be3d188eaf8bba06112ebbe2f47f6c322b5105a6fbc45a2e","affectsGlobalScope":true,"impliedFormat":1},{"version":"a680117f487a4d2f30ea46f1b4b7f58bef1480456e18ba53ee85c2746eeca012","affectsGlobalScope":true,"impliedFormat":1},{"version":"2f11ff796926e0832f9ae148008138ad583bd181899ab7dd768a2666700b1893","affectsGlobalScope":true,"impliedFormat":1},{"version":"4de680d5bb41c17f7f68e0419412ca23c98d5749dcaaea1896172f06435891fc","affectsGlobalScope":true,"impliedFormat":1},{"version":"954296b30da6d508a104a3a0b5d96b76495c709785c1d11610908e63481ee667","affectsGlobalScope":true,"impliedFormat":1},{"version":"ac9538681b19688c8eae65811b329d3744af679e0bdfa5d842d0e32524c73e1c","affectsGlobalScope":true,"impliedFormat":1},{"version":"0a969edff4bd52585473d24995c5ef223f6652d6ef46193309b3921d65dd4376","affectsGlobalScope":true,"impliedFormat":1},{"version":"9e9fbd7030c440b33d021da145d3232984c8bb7916f277e8ffd3dc2e3eae2bdb","affectsGlobalScope":true,"impliedFormat":1},{"version":"811ec78f7fefcabbda4bfa93b3eb67d9ae166ef95f9bff989d964061cbf81a0c","affectsGlobalScope":true,"impliedFormat":1},{"version":"717937616a17072082152a2ef351cb51f98802fb4b2fdabd32399843875974ca","affectsGlobalScope":true,"impliedFormat":1},{"version":"d7e7d9b7b50e5f22c915b525acc5a49a7a6584cf8f62d0569e557c5cfc4b2ac2","affectsGlobalScope":true,"impliedFormat":1},{"version":"71c37f4c9543f31dfced6c7840e068c5a5aacb7b89111a4364b1d5276b852557","affectsGlobalScope":true,"impliedFormat":1},{"version":"576711e016cf4f1804676043e6a0a5414252560eb57de9faceee34d79798c850","affectsGlobalScope":true,"impliedFormat":1},{"version":"89c1b1281ba7b8a96efc676b11b264de7a8374c5ea1e6617f11880a13fc56dc6","affectsGlobalScope":true,"impliedFormat":1},{"version":"74f7fa2d027d5b33eb0471c8e82a6c87216223181ec31247c357a3e8e2fddc5b","affectsGlobalScope":true,"impliedFormat":1},{"version":"d6d7ae4d1f1f3772e2a3cde568ed08991a8ae34a080ff1151af28b7f798e22ca","affectsGlobalScope":true,"impliedFormat":1},{"version":"063600664504610fe3e99b717a1223f8b1900087fab0b4cad1496a114744f8df","affectsGlobalScope":true,"impliedFormat":1},{"version":"934019d7e3c81950f9a8426d093458b65d5aff2c7c1511233c0fd5b941e608ab","affectsGlobalScope":true,"impliedFormat":1},{"version":"52ada8e0b6e0482b728070b7639ee42e83a9b1c22d205992756fe020fd9f4a47","affectsGlobalScope":true,"impliedFormat":1},{"version":"3bdefe1bfd4d6dee0e26f928f93ccc128f1b64d5d501ff4a8cf3c6371200e5e6","affectsGlobalScope":true,"impliedFormat":1},{"version":"59fb2c069260b4ba00b5643b907ef5d5341b167e7d1dbf58dfd895658bda2867","affectsGlobalScope":true,"impliedFormat":1},{"version":"639e512c0dfc3fad96a84caad71b8834d66329a1f28dc95e3946c9b58176c73a","affectsGlobalScope":true,"impliedFormat":1},{"version":"368af93f74c9c932edd84c58883e736c9e3d53cec1fe24c0b0ff451f529ceab1","affectsGlobalScope":true,"impliedFormat":1},{"version":"af3dd424cf267428f30ccfc376f47a2c0114546b55c44d8c0f1d57d841e28d74","affectsGlobalScope":true,"impliedFormat":1},{"version":"995c005ab91a498455ea8dfb63aa9f83fa2ea793c3d8aa344be4a1678d06d399","affectsGlobalScope":true,"impliedFormat":1},{"version":"959d36cddf5e7d572a65045b876f2956c973a586da58e5d26cde519184fd9b8a","affectsGlobalScope":true,"impliedFormat":1},{"version":"965f36eae237dd74e6cca203a43e9ca801ce38824ead814728a2807b1910117d","affectsGlobalScope":true,"impliedFormat":1},{"version":"3925a6c820dcb1a06506c90b1577db1fdbf7705d65b62b99dce4be75c637e26b","affectsGlobalScope":true,"impliedFormat":1},{"version":"0a3d63ef2b853447ec4f749d3f368ce642264246e02911fcb1590d8c161b8005","affectsGlobalScope":true,"impliedFormat":1},{"version":"8cdf8847677ac7d20486e54dd3fcf09eda95812ac8ace44b4418da1bbbab6eb8","affectsGlobalScope":true,"impliedFormat":1},{"version":"8444af78980e3b20b49324f4a16ba35024fef3ee069a0eb67616ea6ca821c47a","affectsGlobalScope":true,"impliedFormat":1},{"version":"3287d9d085fbd618c3971944b65b4be57859f5415f495b33a6adc994edd2f004","affectsGlobalScope":true,"impliedFormat":1},{"version":"b4b67b1a91182421f5df999988c690f14d813b9850b40acd06ed44691f6727ad","affectsGlobalScope":true,"impliedFormat":1},{"version":"df83c2a6c73228b625b0beb6669c7ee2a09c914637e2d35170723ad49c0f5cd4","affectsGlobalScope":true,"impliedFormat":1},{"version":"436aaf437562f276ec2ddbee2f2cdedac7664c1e4c1d2c36839ddd582eeb3d0a","affectsGlobalScope":true,"impliedFormat":1},{"version":"8e3c06ea092138bf9fa5e874a1fdbc9d54805d074bee1de31b99a11e2fec239d","affectsGlobalScope":true,"impliedFormat":1},{"version":"8e7f8264d0fb4c5339605a15daadb037bf238c10b654bb3eee14208f860a32ea","affectsGlobalScope":true,"impliedFormat":1},{"version":"782dec38049b92d4e85c1585fbea5474a219c6984a35b004963b00beb1aab538","affectsGlobalScope":true,"impliedFormat":1},{"version":"785921608325fa246b450f05b238f4b3ed659f1099af278ce9ebbc9416a13f1d","impliedFormat":1},{"version":"198675eb0b5219336c2e6caf052ee50da3b4193d244fa514083ddf835b1c588c","signature":"2e02609ee5dc68e0537f67b5713a819779697beb3c5796fbabde9fa354f534b7","impliedFormat":99}],"root":[68],"options":{"composite":true,"declaration":true,"declarationMap":true,"module":100,"outDir":"./","skipLibCheck":true,"sourceMap":true,"strict":true,"target":10,"verbatimModuleSyntax":true},"latestChangedDtsFile":"./src/index.d.ts","version":"5.9.3"}
package/package.json ADDED
@@ -0,0 +1,28 @@
1
+ {
2
+ "name": "@eyr1n/ros-cdr-client",
3
+ "version": "0.0.1",
4
+ "type": "module",
5
+ "main": "dist/src/index.js",
6
+ "types": "dist/src/index.d.ts",
7
+ "exports": {
8
+ ".": {
9
+ "types": "./dist/src/index.d.ts",
10
+ "import": "./dist/src/index.js"
11
+ }
12
+ },
13
+ "files": [
14
+ "dist",
15
+ "src"
16
+ ],
17
+ "license": "MIT",
18
+ "publishConfig": {
19
+ "registry": "https://registry.npmjs.org",
20
+ "access": "public"
21
+ },
22
+ "devDependencies": {
23
+ "typescript": "^5.9.3"
24
+ },
25
+ "scripts": {
26
+ "build": "tsc --build"
27
+ }
28
+ }
package/src/index.ts ADDED
@@ -0,0 +1,261 @@
1
+ const brand = Symbol();
2
+ type Branded<T, Brand> = T & { [brand]: Brand };
3
+
4
+ const OP_CREATE_SUBSCRIPTION = 'create_subscription' as const;
5
+ const OP_CREATE_PUBLISHER = 'create_publisher' as const;
6
+ const OP_CREATE_SERVICE_CLIENT = 'create_service_client' as const;
7
+ const OP_DESTROY = 'destroy' as const;
8
+
9
+ const OP_TOPIC = 1 as const;
10
+ const OP_SERVICE_REQUEST = 2 as const;
11
+ const OP_SERVICE_RESPONSE = 3 as const;
12
+
13
+ type CreateOp =
14
+ | typeof OP_CREATE_PUBLISHER
15
+ | typeof OP_CREATE_SUBSCRIPTION
16
+ | typeof OP_CREATE_SERVICE_CLIENT;
17
+
18
+ export type PublisherId = Branded<number, 'PublisherId'>;
19
+ export type SubscriptionId = Branded<number, 'SubscriptionId'>;
20
+ export type ServiceClientId = Branded<number, 'ServiceClientId'>;
21
+
22
+ interface CreateRequest {
23
+ op: CreateOp;
24
+ name: string;
25
+ type: string;
26
+ }
27
+
28
+ interface CreateResponse {
29
+ op: CreateOp;
30
+ name: string;
31
+ id: number;
32
+ }
33
+
34
+ interface DestroyRequest {
35
+ op: typeof OP_DESTROY;
36
+ id: PublisherId | SubscriptionId | ServiceClientId;
37
+ }
38
+
39
+ interface SubscriptionMessage {
40
+ opcode: typeof OP_TOPIC;
41
+ id: number;
42
+ message: DataView;
43
+ }
44
+
45
+ interface ServiceClientResponse {
46
+ opcode: typeof OP_SERVICE_RESPONSE;
47
+ id: number;
48
+ callId: number;
49
+ message: DataView;
50
+ }
51
+
52
+ function isObject(value: unknown): value is object {
53
+ return value !== null && typeof value === 'object';
54
+ }
55
+
56
+ function isCreateOp(value: unknown): value is CreateOp {
57
+ return (
58
+ value === OP_CREATE_PUBLISHER ||
59
+ value === OP_CREATE_SUBSCRIPTION ||
60
+ value === OP_CREATE_SERVICE_CLIENT
61
+ );
62
+ }
63
+
64
+ function parseTextPayload(payload: string): CreateResponse | null {
65
+ let response: unknown;
66
+ try {
67
+ response = JSON.parse(payload);
68
+ } catch {
69
+ return null;
70
+ }
71
+ if (!isObject(response)) {
72
+ return null;
73
+ }
74
+ if (!('op' in response) || !isCreateOp(response.op)) {
75
+ return null;
76
+ }
77
+ if (!('name' in response) || typeof response.name !== 'string') {
78
+ return null;
79
+ }
80
+ if (!('id' in response) || typeof response.id !== 'number') {
81
+ return null;
82
+ }
83
+ return { op: response.op, name: response.name, id: response.id };
84
+ }
85
+
86
+ function parseBinaryPayload(
87
+ buffer: ArrayBuffer,
88
+ ): SubscriptionMessage | ServiceClientResponse | null {
89
+ const view = new DataView(buffer);
90
+ if (view.byteLength < 5) {
91
+ return null;
92
+ }
93
+
94
+ const opcode = view.getUint8(0);
95
+
96
+ if (opcode === OP_TOPIC) {
97
+ const id = view.getUint32(1, true);
98
+ return { opcode: OP_TOPIC, id, message: new DataView(buffer, 5) };
99
+ }
100
+ if (opcode === OP_SERVICE_RESPONSE) {
101
+ if (view.byteLength < 9) {
102
+ return null;
103
+ }
104
+ const id = view.getUint32(1, true);
105
+ const callId = view.getUint32(5, true);
106
+ return {
107
+ opcode: OP_SERVICE_RESPONSE,
108
+ id,
109
+ callId,
110
+ message: new DataView(buffer, 9),
111
+ };
112
+ }
113
+ return null;
114
+ }
115
+
116
+ function pendingCreateKey(op: CreateOp, name: string): string {
117
+ return `${op}:${name}`;
118
+ }
119
+
120
+ function serviceResponseKey(id: number, callId: number): string {
121
+ return `${id}:${callId}`;
122
+ }
123
+
124
+ export class RosCdrClient {
125
+ readonly #ws: WebSocket;
126
+
127
+ readonly #pendingCreates = new Map<string, (id: number) => void>();
128
+ readonly #subscriptions = new Map<
129
+ SubscriptionId,
130
+ (message: DataView) => void
131
+ >();
132
+ readonly #serviceResponses = new Map<string, (response: DataView) => void>();
133
+
134
+ constructor(ws: WebSocket) {
135
+ this.#ws = ws;
136
+ this.#ws.binaryType = 'arraybuffer';
137
+ this.#ws.onmessage = this.#onMessage.bind(this);
138
+ }
139
+
140
+ async createPublisher(name: string, type: string): Promise<PublisherId> {
141
+ const request: CreateRequest = { op: OP_CREATE_PUBLISHER, name, type };
142
+ return this.#sendCreateRequest(request) as Promise<PublisherId>;
143
+ }
144
+
145
+ async createSubscription(
146
+ name: string,
147
+ type: string,
148
+ callback: (message: DataView) => void,
149
+ ): Promise<SubscriptionId> {
150
+ const request: CreateRequest = { op: OP_CREATE_SUBSCRIPTION, name, type };
151
+ const id = await (this.#sendCreateRequest(
152
+ request,
153
+ ) as Promise<SubscriptionId>);
154
+ this.#subscriptions.set(id, callback);
155
+ return id;
156
+ }
157
+
158
+ async createServiceClient(
159
+ name: string,
160
+ type: string,
161
+ ): Promise<ServiceClientId> {
162
+ const request: CreateRequest = { op: OP_CREATE_SERVICE_CLIENT, name, type };
163
+ return this.#sendCreateRequest(request) as Promise<ServiceClientId>;
164
+ }
165
+
166
+ publish(id: PublisherId, message: Uint8Array) {
167
+ const payload = new Uint8Array(1 + 4 + message.length);
168
+ payload[0] = OP_TOPIC;
169
+ const view = new DataView(payload.buffer);
170
+ view.setUint32(1, id, true);
171
+ payload.set(message, 5);
172
+ this.#sendBinaryPayload(payload);
173
+ }
174
+
175
+ callService(
176
+ id: ServiceClientId,
177
+ callId: number,
178
+ request: Uint8Array,
179
+ ): Promise<DataView> {
180
+ const payload = new Uint8Array(1 + 4 + 4 + request.length);
181
+ payload[0] = OP_SERVICE_REQUEST;
182
+ const view = new DataView(payload.buffer);
183
+ view.setUint32(1, id, true);
184
+ view.setUint32(5, callId, true);
185
+ payload.set(request, 9);
186
+
187
+ const key = serviceResponseKey(id, callId);
188
+ return new Promise((resolve) => {
189
+ this.#serviceResponses.set(key, resolve);
190
+ this.#sendBinaryPayload(payload);
191
+ });
192
+ }
193
+
194
+ destroy(id: PublisherId | SubscriptionId | ServiceClientId) {
195
+ const request: DestroyRequest = { op: OP_DESTROY, id };
196
+ this.#sendTextPayload(request);
197
+ this.#subscriptions.delete(id as SubscriptionId);
198
+ }
199
+
200
+ #sendCreateRequest(request: CreateRequest): Promise<number> {
201
+ return new Promise((resolve) => {
202
+ this.#pendingCreates.set(
203
+ pendingCreateKey(request.op, request.name),
204
+ resolve,
205
+ );
206
+ this.#sendTextPayload(request);
207
+ });
208
+ }
209
+
210
+ #sendTextPayload(payload: CreateRequest | DestroyRequest) {
211
+ this.#ws.send(JSON.stringify(payload));
212
+ }
213
+
214
+ #sendBinaryPayload(payload: Uint8Array) {
215
+ this.#ws.send(payload);
216
+ }
217
+
218
+ #onMessage(event: MessageEvent) {
219
+ if (typeof event.data === 'string') {
220
+ this.#onTextPayload(event.data);
221
+ return;
222
+ }
223
+ if (event.data instanceof ArrayBuffer) {
224
+ this.#onBinaryPayload(event.data);
225
+ return;
226
+ }
227
+ }
228
+
229
+ #onTextPayload(payload: string) {
230
+ const response = parseTextPayload(payload);
231
+ if (!response) {
232
+ return;
233
+ }
234
+ const key = pendingCreateKey(response.op, response.name);
235
+ const callback = this.#pendingCreates.get(key);
236
+ if (callback) {
237
+ this.#pendingCreates.delete(key);
238
+ callback(response.id);
239
+ }
240
+ }
241
+
242
+ #onBinaryPayload(payload: ArrayBuffer) {
243
+ const message = parseBinaryPayload(payload);
244
+ if (!message) {
245
+ return;
246
+ }
247
+ if (message.opcode === OP_TOPIC) {
248
+ this.#subscriptions.get(message.id as SubscriptionId)?.(message.message);
249
+ return;
250
+ }
251
+ if (message.opcode === OP_SERVICE_RESPONSE) {
252
+ const key = serviceResponseKey(message.id, message.callId);
253
+ const callback = this.#serviceResponses.get(key);
254
+ if (callback) {
255
+ this.#serviceResponses.delete(key);
256
+ callback(message.message);
257
+ }
258
+ return;
259
+ }
260
+ }
261
+ }