sen-ether-client 0.1.0

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/lib/bus.js ADDED
@@ -0,0 +1,740 @@
1
+ import { SenBinaryReader, SenBinaryWriter } from './codec.js';
2
+ import { crc32 } from './crc32.js';
3
+ import { eventHash, methodHash, propertyHash } from './hash32.js';
4
+ import {
5
+ BASIC_TYPE,
6
+ BUILT_IN_TYPE,
7
+ CUSTOM_TYPE_DATA,
8
+ INTEGRAL_TYPE,
9
+ KERNEL_CONTROL_MESSAGE_KEY,
10
+ METHOD_CONSTNESS,
11
+ NUMERIC_TYPE,
12
+ PROPERTY_CATEGORY,
13
+ PROPERTY_RELATION,
14
+ REAL_TYPE,
15
+ TRANSPORT_MODE,
16
+ TYPE_SPEC_RESPONSE,
17
+ UNIT_CATEGORY
18
+ } from './protocol/generated.js';
19
+
20
+ export { KERNEL_CONTROL_MESSAGE_KEY };
21
+
22
+ export const BUS_MESSAGE_CATEGORY = Object.freeze({
23
+ controlMessage: 0,
24
+ runtimeObjectUpdate: 1,
25
+ runtimeMethodCallBestEffort: 2,
26
+ runtimeMethodCallConfirmed: 3,
27
+ runtimeMethodResponse: 4,
28
+ runtimeEvents: 5
29
+ });
30
+
31
+ const REMOTE_CALL_RESULT = [
32
+ 'success',
33
+ 'objectNotFound',
34
+ 'runtimeError',
35
+ 'logicError',
36
+ 'unknownException'
37
+ ];
38
+
39
+ function enumName(values, code, label) {
40
+ const name = values[code];
41
+ if (name === undefined) {
42
+ throw new RangeError(`unknown SEN ${label} value: ${code}`);
43
+ }
44
+ return name;
45
+ }
46
+
47
+ function kernelControlTypeFromKey(key) {
48
+ for (const [type, value] of Object.entries(KERNEL_CONTROL_MESSAGE_KEY)) {
49
+ if (value === key) {
50
+ return type;
51
+ }
52
+ }
53
+ return undefined;
54
+ }
55
+
56
+ function readSequence(reader, readItem) {
57
+ const count = reader.readUInt32();
58
+ const values = [];
59
+ for (let i = 0; i < count; i += 1) {
60
+ values.push(readItem(reader));
61
+ }
62
+ return values;
63
+ }
64
+
65
+ function readU32List(reader) {
66
+ return readSequence(reader, itemReader => itemReader.readUInt32());
67
+ }
68
+
69
+ function writeU32List(writer, values = []) {
70
+ writer.writeUInt32(values.length);
71
+ for (const value of values) {
72
+ writer.writeUInt32(value);
73
+ }
74
+ }
75
+
76
+ function readStringList(reader) {
77
+ return readSequence(reader, itemReader => itemReader.readString());
78
+ }
79
+
80
+ function readOptional(reader, readValue) {
81
+ return reader.readBool() ? readValue(reader) : null;
82
+ }
83
+
84
+ function readUInt64Json(reader) {
85
+ const value = reader.readUInt64();
86
+ return value <= BigInt(Number.MAX_SAFE_INTEGER) ? Number(value) : value;
87
+ }
88
+
89
+ function readIntegralType(reader) {
90
+ return enumName(INTEGRAL_TYPE, reader.readUInt8(), 'IntegralType');
91
+ }
92
+
93
+ function readRealType(reader) {
94
+ return enumName(REAL_TYPE, reader.readUInt8(), 'RealType');
95
+ }
96
+
97
+ function readNumericType(reader) {
98
+ const key = reader.readUInt32();
99
+ const type = enumName(NUMERIC_TYPE, key, 'NumericType');
100
+ switch (type) {
101
+ case 'IntegralType':
102
+ return { type: 'IntegralType', value: readIntegralType(reader) };
103
+ case 'RealType':
104
+ return { type: 'RealType', value: readRealType(reader) };
105
+ default:
106
+ throw new TypeError(`unhandled SEN NumericType alternative: ${type}`);
107
+ }
108
+ }
109
+
110
+ function readBasicType(reader) {
111
+ return enumName(BASIC_TYPE, reader.readUInt8(), 'BasicType');
112
+ }
113
+
114
+ function readBuiltInType(reader) {
115
+ const key = reader.readUInt32();
116
+ const type = enumName(BUILT_IN_TYPE, key, 'BuiltInType');
117
+ switch (type) {
118
+ case 'NumericType':
119
+ return { type: 'NumericType', value: readNumericType(reader) };
120
+ case 'BasicType':
121
+ return { type: 'BasicType', value: readBasicType(reader) };
122
+ default:
123
+ throw new TypeError(`unhandled SEN BuiltInType alternative: ${type}`);
124
+ }
125
+ }
126
+
127
+ function readUnitInfo(reader) {
128
+ return {
129
+ name: reader.readString(),
130
+ abbreviation: reader.readString(),
131
+ category: enumName(UNIT_CATEGORY, reader.readUInt8(), 'UnitCat')
132
+ };
133
+ }
134
+
135
+ function readEnumeratorSpec(reader) {
136
+ return {
137
+ name: reader.readString(),
138
+ key: reader.readUInt32(),
139
+ description: reader.readString()
140
+ };
141
+ }
142
+
143
+ function readEnumTypeSpec(reader) {
144
+ return {
145
+ enums: readSequence(reader, readEnumeratorSpec),
146
+ storageType: readIntegralType(reader)
147
+ };
148
+ }
149
+
150
+ function readQuantityTypeSpec(reader) {
151
+ return {
152
+ elementType: readNumericType(reader),
153
+ unit: readUnitInfo(reader),
154
+ minValue: readOptional(reader, itemReader => itemReader.readFloat64()),
155
+ maxValue: readOptional(reader, itemReader => itemReader.readFloat64())
156
+ };
157
+ }
158
+
159
+ function readSequenceTypeSpec(reader) {
160
+ return {
161
+ elementType: reader.readString(),
162
+ maxSize: readOptional(reader, readUInt64Json),
163
+ fixedSize: reader.readBool()
164
+ };
165
+ }
166
+
167
+ function readStructTypeFieldSpec(reader) {
168
+ return {
169
+ name: reader.readString(),
170
+ description: reader.readString(),
171
+ type: reader.readString()
172
+ };
173
+ }
174
+
175
+ function readStructTypeSpec(reader) {
176
+ return {
177
+ fields: readSequence(reader, readStructTypeFieldSpec),
178
+ parent: reader.readString()
179
+ };
180
+ }
181
+
182
+ function readVariantTypeFieldSpec(reader) {
183
+ return {
184
+ key: reader.readUInt32(),
185
+ description: reader.readString(),
186
+ type: reader.readString()
187
+ };
188
+ }
189
+
190
+ function readVariantTypeSpec(reader) {
191
+ return {
192
+ fields: readSequence(reader, readVariantTypeFieldSpec)
193
+ };
194
+ }
195
+
196
+ function readAliasTypeSpec(reader) {
197
+ return {
198
+ aliasedType: reader.readString()
199
+ };
200
+ }
201
+
202
+ function readOptionalTypeSpec(reader) {
203
+ return {
204
+ type: reader.readString()
205
+ };
206
+ }
207
+
208
+ function readArgSpec(reader) {
209
+ return {
210
+ name: reader.readString(),
211
+ description: reader.readString(),
212
+ type: reader.readString()
213
+ };
214
+ }
215
+
216
+ function readEventSpec(reader) {
217
+ const name = reader.readString();
218
+ return {
219
+ id: eventHash(name),
220
+ name,
221
+ description: reader.readString(),
222
+ args: readSequence(reader, readArgSpec),
223
+ transportMode: enumName(TRANSPORT_MODE, reader.readUInt8(), 'TransportModeSpec')
224
+ };
225
+ }
226
+
227
+ function readMethodSpec(reader) {
228
+ const name = reader.readString();
229
+ return {
230
+ id: methodHash(name),
231
+ name,
232
+ description: reader.readString(),
233
+ args: readSequence(reader, readArgSpec),
234
+ transportMode: enumName(TRANSPORT_MODE, reader.readUInt8(), 'TransportModeSpec'),
235
+ constness: enumName(METHOD_CONSTNESS, reader.readUInt8(), 'MethodConstnessSpec'),
236
+ deferred: reader.readBool(),
237
+ returnType: reader.readString(),
238
+ propertyRelation: enumName(PROPERTY_RELATION, reader.readUInt8(), 'PropertyRelationSpec'),
239
+ localOnly: reader.readBool()
240
+ };
241
+ }
242
+
243
+ function readPropertySpec(reader) {
244
+ const name = reader.readString();
245
+ return {
246
+ id: propertyHash(name),
247
+ name,
248
+ description: reader.readString(),
249
+ category: enumName(PROPERTY_CATEGORY, reader.readUInt8(), 'PropertyCategorySpec'),
250
+ type: reader.readString(),
251
+ transportMode: enumName(TRANSPORT_MODE, reader.readUInt8(), 'TransportModeSpec'),
252
+ tags: readStringList(reader),
253
+ checkedSet: reader.readBool()
254
+ };
255
+ }
256
+
257
+ function readClassTypeSpec(reader) {
258
+ return {
259
+ properties: readSequence(reader, readPropertySpec),
260
+ methods: readSequence(reader, readMethodSpec),
261
+ events: readSequence(reader, readEventSpec),
262
+ constructor: readMethodSpec(reader),
263
+ parents: readStringList(reader),
264
+ isInterface: reader.readBool()
265
+ };
266
+ }
267
+
268
+ function readCustomTypeData(reader) {
269
+ const key = reader.readUInt32();
270
+ const type = enumName(CUSTOM_TYPE_DATA, key, 'CustomTypeData');
271
+ let value;
272
+
273
+ switch (type) {
274
+ case 'EnumTypeSpec':
275
+ value = readEnumTypeSpec(reader);
276
+ break;
277
+ case 'QuantityTypeSpec':
278
+ value = readQuantityTypeSpec(reader);
279
+ break;
280
+ case 'SequenceTypeSpec':
281
+ value = readSequenceTypeSpec(reader);
282
+ break;
283
+ case 'StructTypeSpec':
284
+ value = readStructTypeSpec(reader);
285
+ break;
286
+ case 'VariantTypeSpec':
287
+ value = readVariantTypeSpec(reader);
288
+ break;
289
+ case 'AliasTypeSpec':
290
+ value = readAliasTypeSpec(reader);
291
+ break;
292
+ case 'OptionalTypeSpec':
293
+ value = readOptionalTypeSpec(reader);
294
+ break;
295
+ case 'ClassTypeSpec':
296
+ value = readClassTypeSpec(reader);
297
+ break;
298
+ default:
299
+ throw new TypeError(`unhandled SEN CustomTypeData alternative: ${type}`);
300
+ }
301
+
302
+ return { type, value };
303
+ }
304
+
305
+ function readCustomTypeSpec(reader) {
306
+ return {
307
+ name: reader.readString(),
308
+ qualifiedName: reader.readString(),
309
+ description: reader.readString(),
310
+ data: readCustomTypeData(reader)
311
+ };
312
+ }
313
+
314
+ function readTypeSpecResponse(reader) {
315
+ const key = reader.readUInt32();
316
+ const type = enumName(TYPE_SPEC_RESPONSE, key, 'TypeSpecResponse');
317
+
318
+ switch (type) {
319
+ case 'ClassSpecResponse':
320
+ return {
321
+ type,
322
+ classHash: reader.readUInt32(),
323
+ spec: readCustomTypeSpec(reader),
324
+ dependentTypes: readU32List(reader)
325
+ };
326
+ case 'NonClassSpecResponse':
327
+ return {
328
+ type,
329
+ spec: readCustomTypeSpec(reader)
330
+ };
331
+ default:
332
+ throw new TypeError(`unhandled SEN TypeSpecResponse alternative: ${type}`);
333
+ }
334
+ }
335
+
336
+ function readTypesInfoResponse(reader) {
337
+ return {
338
+ ownerId: reader.readUInt32(),
339
+ types: readSequence(reader, readTypeSpecResponse)
340
+ };
341
+ }
342
+
343
+ function readTypesInfoRejection(reader) {
344
+ return {
345
+ ownerId: reader.readUInt32(),
346
+ rejections: readStringList(reader)
347
+ };
348
+ }
349
+
350
+ function readObjectAdded(reader) {
351
+ return {
352
+ className: reader.readString(),
353
+ typeHash: reader.readUInt32(),
354
+ name: reader.readString(),
355
+ id: reader.readUInt32(),
356
+ state: reader.readBuffer(),
357
+ time: reader.readInt64()
358
+ };
359
+ }
360
+
361
+ function readInterestDiscovery(reader) {
362
+ const interestId = reader.readUInt32();
363
+ const objectCount = reader.readUInt32();
364
+ const objects = [];
365
+ for (let i = 0; i < objectCount; i += 1) {
366
+ objects.push(readObjectAdded(reader));
367
+ }
368
+ return { interestId, objects };
369
+ }
370
+
371
+ function readObjectsPublished(reader) {
372
+ const ownerId = reader.readUInt32();
373
+ const discoveryCount = reader.readUInt32();
374
+ const discoveries = [];
375
+ for (let i = 0; i < discoveryCount; i += 1) {
376
+ discoveries.push(readInterestDiscovery(reader));
377
+ }
378
+ return { ownerId, discoveries };
379
+ }
380
+
381
+ function readObjectsRemoved(reader) {
382
+ const removalCount = reader.readUInt32();
383
+ const removals = [];
384
+ for (let i = 0; i < removalCount; i += 1) {
385
+ removals.push({
386
+ interestId: reader.readUInt32(),
387
+ ids: readU32List(reader)
388
+ });
389
+ }
390
+ return { removals };
391
+ }
392
+
393
+ function readObjectIdsByInterestList(reader) {
394
+ const count = reader.readUInt32();
395
+ const requests = [];
396
+ for (let i = 0; i < count; i += 1) {
397
+ requests.push({
398
+ interestId: reader.readUInt32(),
399
+ objectIds: readU32List(reader)
400
+ });
401
+ }
402
+ return requests;
403
+ }
404
+
405
+ function writeObjectIdsByInterestList(writer, requests = []) {
406
+ writer.writeUInt32(requests.length);
407
+ for (const request of requests) {
408
+ writer.writeUInt32(request.interestId);
409
+ writeU32List(writer, request.objectIds);
410
+ }
411
+ }
412
+
413
+ function readObjectState(reader) {
414
+ return {
415
+ id: reader.readUInt32(),
416
+ timestamp: reader.readInt64(),
417
+ state: reader.readBuffer()
418
+ };
419
+ }
420
+
421
+ function readObjectsStateResponse(reader) {
422
+ const ownerId = reader.readUInt32();
423
+ const groupCount = reader.readUInt32();
424
+ const responses = [];
425
+ for (let i = 0; i < groupCount; i += 1) {
426
+ const interestId = reader.readUInt32();
427
+ const objectStateCount = reader.readUInt32();
428
+ const objectStates = [];
429
+ for (let j = 0; j < objectStateCount; j += 1) {
430
+ objectStates.push(readObjectState(reader));
431
+ }
432
+ responses.push({ interestId, objectStates });
433
+ }
434
+ return { ownerId, responses };
435
+ }
436
+
437
+ export function decodePropertyUpdateBuffer(buffer) {
438
+ const reader = new SenBinaryReader(buffer);
439
+ const updates = [];
440
+
441
+ while (reader.remaining() > 0) {
442
+ const id = reader.readUInt32();
443
+ const size = reader.readUInt32();
444
+ reader.ensure(size);
445
+ const value = reader.buffer.subarray(reader.offset, reader.offset + size);
446
+ reader.offset += size;
447
+ updates.push({ id, size, value });
448
+ }
449
+
450
+ return updates;
451
+ }
452
+
453
+ function readRuntimeObjectUpdate(reader) {
454
+ const objectId = reader.readUInt32();
455
+ const time = reader.readInt64();
456
+ const propertiesSize = reader.readUInt32();
457
+ reader.ensure(propertiesSize);
458
+ const properties = reader.buffer.subarray(reader.offset, reader.offset + propertiesSize);
459
+ reader.offset += propertiesSize;
460
+ return {
461
+ objectId,
462
+ time,
463
+ propertiesSize,
464
+ properties,
465
+ propertyUpdates: decodePropertyUpdateBuffer(properties)
466
+ };
467
+ }
468
+
469
+ function readRuntimeMethodResponse(reader) {
470
+ const resultCode = reader.readUInt8();
471
+ const result = enumName(REMOTE_CALL_RESULT, resultCode, 'RemoteCallResult');
472
+ const objectId = reader.readUInt32();
473
+ const ticketId = reader.readUInt32();
474
+
475
+ if (result === 'success') {
476
+ const returnSize = reader.readUInt32();
477
+ reader.ensure(returnSize);
478
+ const returnValue = reader.buffer.subarray(reader.offset, reader.offset + returnSize);
479
+ reader.offset += returnSize;
480
+ return {
481
+ resultCode,
482
+ result,
483
+ objectId,
484
+ ticketId,
485
+ returnValue
486
+ };
487
+ }
488
+
489
+ const error = result === 'runtimeError' || result === 'logicError'
490
+ ? reader.readString()
491
+ : result;
492
+
493
+ return {
494
+ resultCode,
495
+ result,
496
+ objectId,
497
+ ticketId,
498
+ error
499
+ };
500
+ }
501
+
502
+ function readRuntimeEvents(reader) {
503
+ const events = [];
504
+
505
+ while (reader.remaining() > 0) {
506
+ const producerId = reader.readUInt32();
507
+ const eventId = reader.readUInt32();
508
+ const creationTime = reader.readInt64();
509
+ const argumentsSize = reader.readUInt32();
510
+ reader.ensure(argumentsSize);
511
+ const argumentsBuffer = reader.buffer.subarray(reader.offset, reader.offset + argumentsSize);
512
+ reader.offset += argumentsSize;
513
+ events.push({
514
+ producerId,
515
+ eventId,
516
+ creationTime,
517
+ argumentsSize,
518
+ argumentsBuffer
519
+ });
520
+ }
521
+
522
+ return events;
523
+ }
524
+
525
+ /**
526
+ * Decode process-level bus payload from ProcessHandler TCP category `busMessage`.
527
+ *
528
+ * Payload layout:
529
+ * u32 to; u32 busId; bytes busMessage
530
+ *
531
+ * @param {Buffer | Uint8Array | ArrayBuffer} buffer
532
+ */
533
+ export function decodeConfirmedBusFrame(buffer) {
534
+ const reader = new SenBinaryReader(buffer);
535
+ const to = reader.readUInt32();
536
+ const busId = reader.readUInt32();
537
+ const message = reader.buffer.subarray(reader.offset);
538
+ return { to, busId, message };
539
+ }
540
+
541
+ /**
542
+ * Encode a confirmed process-level bus payload.
543
+ *
544
+ * @param {object} frame
545
+ * @param {number} frame.to
546
+ * @param {number} frame.busId
547
+ * @param {Buffer | Uint8Array | ArrayBuffer} frame.message
548
+ */
549
+ export function encodeConfirmedBusFrame(frame) {
550
+ const writer = new SenBinaryWriter();
551
+ writer.writeUInt32(frame.to);
552
+ writer.writeUInt32(frame.busId);
553
+ writer.chunks.push(Buffer.from(frame.message ?? []));
554
+ return writer.toBuffer();
555
+ }
556
+
557
+ /**
558
+ * Decode sen::kernel::impl bus message envelope.
559
+ *
560
+ * @param {Buffer | Uint8Array | ArrayBuffer} buffer
561
+ */
562
+ export function decodeBusMessage(buffer) {
563
+ const reader = new SenBinaryReader(buffer);
564
+ const category = reader.readUInt8();
565
+ const payload = reader.buffer.subarray(reader.offset);
566
+
567
+ if (category === BUS_MESSAGE_CATEGORY.controlMessage) {
568
+ return {
569
+ category,
570
+ categoryName: 'controlMessage',
571
+ control: decodeKernelControlMessage(payload)
572
+ };
573
+ }
574
+
575
+ if (category === BUS_MESSAGE_CATEGORY.runtimeObjectUpdate) {
576
+ return {
577
+ category,
578
+ categoryName: 'runtimeObjectUpdate',
579
+ update: readRuntimeObjectUpdate(reader)
580
+ };
581
+ }
582
+
583
+ if (category === BUS_MESSAGE_CATEGORY.runtimeMethodResponse) {
584
+ return {
585
+ category,
586
+ categoryName: 'runtimeMethodResponse',
587
+ response: readRuntimeMethodResponse(reader)
588
+ };
589
+ }
590
+
591
+ if (category === BUS_MESSAGE_CATEGORY.runtimeEvents) {
592
+ return {
593
+ category,
594
+ categoryName: 'runtimeEvents',
595
+ events: readRuntimeEvents(reader),
596
+ payload
597
+ };
598
+ }
599
+
600
+ return {
601
+ category,
602
+ categoryName: Object.entries(BUS_MESSAGE_CATEGORY).find(([, value]) => value === category)?.[0] ?? `unknown:${category}`,
603
+ payload
604
+ };
605
+ }
606
+
607
+ /**
608
+ * @param {{ type: string, value?: object }} message
609
+ */
610
+ export function encodeKernelControlMessage(message) {
611
+ const type = message.type;
612
+ const value = message.value ?? {};
613
+
614
+ if (!(type in KERNEL_CONTROL_MESSAGE_KEY)) {
615
+ throw new TypeError(`unknown SEN kernel ControlMessage: ${type}`);
616
+ }
617
+
618
+ const writer = new SenBinaryWriter();
619
+ writer.writeUInt32(KERNEL_CONTROL_MESSAGE_KEY[type]);
620
+
621
+ switch (type) {
622
+ case 'RemoteParticipantReady':
623
+ writer.writeUInt32(value.id);
624
+ break;
625
+ case 'InterestStarted':
626
+ writer.writeString(value.query);
627
+ writer.writeUInt32(value.id ?? crc32(value.query));
628
+ break;
629
+ case 'InterestStopped':
630
+ writer.writeUInt32(value.id);
631
+ break;
632
+ case 'ObjectsStateRequest':
633
+ writer.writeUInt32(value.ownerId);
634
+ writeObjectIdsByInterestList(writer, value.requests);
635
+ break;
636
+ case 'TypesInfoRequest':
637
+ writer.writeUInt32(value.ownerId);
638
+ writeU32List(writer, value.requests);
639
+ break;
640
+ default:
641
+ throw new TypeError(`encoding SEN kernel ControlMessage ${type} is not implemented`);
642
+ }
643
+
644
+ return writer.toBuffer();
645
+ }
646
+
647
+ /**
648
+ * @param {Buffer | Uint8Array | ArrayBuffer} buffer
649
+ */
650
+ export function decodeKernelControlMessage(buffer) {
651
+ const reader = new SenBinaryReader(buffer);
652
+ const key = reader.readUInt32();
653
+ const type = kernelControlTypeFromKey(key);
654
+
655
+ if (!type) {
656
+ throw new RangeError(`unknown SEN kernel ControlMessage key: ${key}`);
657
+ }
658
+
659
+ let value = {};
660
+ switch (type) {
661
+ case 'RemoteParticipantReady':
662
+ value = { id: reader.readUInt32() };
663
+ break;
664
+ case 'InterestStarted':
665
+ value = { query: reader.readString(), id: reader.readUInt32() };
666
+ break;
667
+ case 'InterestStopped':
668
+ value = { id: reader.readUInt32() };
669
+ break;
670
+ case 'ObjectsPublished':
671
+ value = readObjectsPublished(reader);
672
+ break;
673
+ case 'ObjectsRemoved':
674
+ value = readObjectsRemoved(reader);
675
+ break;
676
+ case 'ObjectsStateRequest':
677
+ value = { ownerId: reader.readUInt32(), requests: readObjectIdsByInterestList(reader) };
678
+ break;
679
+ case 'ObjectsStateResponse':
680
+ value = readObjectsStateResponse(reader);
681
+ break;
682
+ case 'TypesInfoRequest':
683
+ value = { ownerId: reader.readUInt32(), requests: readU32List(reader) };
684
+ break;
685
+ case 'TypesInfoResponse':
686
+ value = readTypesInfoResponse(reader);
687
+ break;
688
+ case 'TypesInfoRejection':
689
+ value = readTypesInfoRejection(reader);
690
+ break;
691
+ default:
692
+ value = { raw: reader.buffer.subarray(reader.offset) };
693
+ reader.offset = reader.buffer.length;
694
+ break;
695
+ }
696
+
697
+ return {
698
+ type,
699
+ value,
700
+ bytesRead: reader.offset
701
+ };
702
+ }
703
+
704
+ export function encodeBusControlMessage(message) {
705
+ const writer = new SenBinaryWriter();
706
+ writer.writeUInt8(BUS_MESSAGE_CATEGORY.controlMessage);
707
+ writer.chunks.push(encodeKernelControlMessage(message));
708
+ return writer.toBuffer();
709
+ }
710
+
711
+ /**
712
+ * Encode a SEN runtime method call bus message.
713
+ *
714
+ * Source implementation:
715
+ * libs/kernel/src/bus/remote_participant.cpp::makeMethodCallHeader
716
+ *
717
+ * @param {object} call
718
+ * @param {boolean} [call.confirmed]
719
+ * @param {number} call.ownerId Local participant id that should receive the response.
720
+ * @param {number} call.objectId Remote object id.
721
+ * @param {number} call.methodId SEN method member hash.
722
+ * @param {number} call.ticketId Local call id.
723
+ * @param {Buffer | Uint8Array | ArrayBuffer} [call.argumentsBuffer]
724
+ */
725
+ export function encodeRuntimeMethodCall(call) {
726
+ const args = Buffer.isBuffer(call.argumentsBuffer)
727
+ ? call.argumentsBuffer
728
+ : Buffer.from(call.argumentsBuffer ?? []);
729
+ const writer = new SenBinaryWriter();
730
+ writer.writeUInt8(call.confirmed ? BUS_MESSAGE_CATEGORY.runtimeMethodCallConfirmed : BUS_MESSAGE_CATEGORY.runtimeMethodCallBestEffort);
731
+ writer.writeUInt32(call.ownerId);
732
+ writer.writeUInt32(call.objectId);
733
+ writer.writeUInt32(call.methodId);
734
+ writer.writeUInt32(call.ticketId);
735
+ writer.writeUInt32(args.length);
736
+ if (args.length) {
737
+ writer.chunks.push(args);
738
+ }
739
+ return writer.toBuffer();
740
+ }