@fibril/can-core 0.1.2 → 0.3.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/dist/index.d.ts CHANGED
@@ -1,12 +1,7 @@
1
1
  import { CanFrameTx, ITransport, Unsubscribe } from "@fibril/can-transport";
2
2
 
3
3
  //#region src/ack-tracker.d.ts
4
- /**
5
- * In-flight ACK/NAK ledger keyed by `seq`. Mirrors the per-node `AckEntry`
6
- * map in `fibril_can_bridge/include/fibril_can_bridge/node_agent.hpp` but
7
- * pared down for the single-threaded TS master — no deadlines list, the
8
- * caller polls via {@link sweep}.
9
- */
4
+ /** In-flight ACK/NAK ledger keyed by `seq`. Caller polls via {@link sweep}. */
10
5
  type AckOutcome = {
11
6
  kind: 'acked';
12
7
  } | {
@@ -120,13 +115,8 @@ declare function extSeg(id: number): number;
120
115
  //#endregion
121
116
  //#region src/config-plane.d.ts
122
117
  /**
123
- * Config-plane wire codec. Mirrors
124
- * `fibril_can_bridge/include/fibril_can_bridge/config_plane_codec.hpp`
125
- * and `fibril_can_bridge/src/config_plane_codec.cpp`.
126
- *
127
- * All integers are little-endian. Decoders return `null` on malformed input
128
- * (wrong length etc.); encoders produce CAN frames padded to the next DLC
129
- * step, matching what the slave sees on the bus.
118
+ * Config-plane wire codec. All integers little-endian; decoders return `null`
119
+ * on malformed input; encoders produce CAN-FD frames padded to the next DLC step.
130
120
  */
131
121
  /** ANNOUNCE payload (S→M, §5.4). */
132
122
  interface AnnounceMsg {
@@ -173,13 +163,9 @@ declare function encodeFrameDefine(nodeId: number, seq: number, stdId: number, p
173
163
  declare function encodeStart(nodeId: number, seq: number): CanFrameTx;
174
164
  declare function encodeStop(nodeId: number, seq: number): CanFrameTx;
175
165
  /**
176
- * Accumulator for SCHEMA_DATA chunks. The slave DLC-pads each response (§4.3)
177
- * so `chunk.bytes.length` is typically larger than what the master requested;
178
- * the accumulator clips against `expectedChunkLen` and the remaining blob
179
- * length to drop those padding bytes (parity with
180
- * `append_schema_chunk` in `fibril_can_bridge/src/config_plane_codec.cpp`).
181
- *
182
- * Out-of-order chunks are rejected (returns 0 from {@link append}).
166
+ * SCHEMA_DATA reassembly. Clips each chunk against `expectedChunkLen` and
167
+ * the remaining blob length so DLC padding is discarded. Out-of-order
168
+ * chunks are rejected ({@link append} returns 0).
183
169
  */
184
170
  declare class SchemaAccumulator {
185
171
  #private;
@@ -197,12 +183,8 @@ declare class SchemaAccumulator {
197
183
  //#endregion
198
184
  //#region src/scalar-codec.d.ts
199
185
  /**
200
- * Scalar type enumeration and little-endian wire codecs. Mirrors
201
- * `fibril_can_core/include/fibril_can_core/scalar_type.hpp` and
202
- * `fibril_can_core/src/scalar_type.cpp`.
203
- *
204
- * Type ordinal values are part of the protobuf schema blob's enum, so they
205
- * must not be renumbered.
186
+ * Scalar type enum and little-endian wire codecs. The ordinal values are
187
+ * part of the protobuf schema blob and must not be renumbered.
206
188
  */
207
189
  declare enum ScalarType {
208
190
  Bool = 0,
@@ -223,14 +205,9 @@ declare function scalarSize(type: ScalarType): number;
223
205
  declare function scalarName(type: ScalarType): string | undefined;
224
206
  declare function scalarFromName(name: string): ScalarType | undefined;
225
207
  /**
226
- * Pack an integer / bool value as little-endian bytes into `out` at `offset`.
227
- * Writes scalarSize(type) bytes — pass a wide-enough `out`.
228
- *
229
- * Pass `bigint` for 64-bit types (U64/I64). `number` is accepted for
230
- * everything (it is internally widened to bigint so signed callers get the
231
- * same sign-extension semantics as the C++ `pack_scalar_int_le(uint64_t)`).
232
- * Signed callers pass the negative number directly; the function performs the
233
- * 64-bit two's-complement cast.
208
+ * Pack an int/bool as little-endian into `out[offset..offset+scalarSize(type))`.
209
+ * `number` is widened to bigint so signed negatives sign-extend to 64-bit
210
+ * two's complement before truncation, matching C++ `pack_scalar_int_le`.
234
211
  */
235
212
  declare function packScalarIntLe(type: ScalarType, value: number | bigint, out: Uint8Array, offset?: number): void;
236
213
  /** Bit-cast a float32 to its IEEE-754 little-endian representation. */
@@ -332,61 +309,33 @@ declare function fieldWireSize(field: SchemaField): number;
332
309
  //#endregion
333
310
  //#region src/dynamic-codec.d.ts
334
311
  /**
335
- * Field-driven dict ⇄ Uint8Array codec. The runtime equivalent of what
336
- * codegen would otherwise emit per message — encode/decode is a pure
337
- * function of the field list ({@link SchemaField}[]) and the JS dict.
338
- *
339
- * Conventions (matches CLAUDE.md "値の表現規約"):
340
- * - dict keys are the schema field name verbatim
341
- * - scalar ↔ JS `number`, except U64/I64 ↔ `bigint`
342
- * - bool ↔ JS `boolean`
343
- * - fixed-length array (`arrayLen > 0`) ↔ regular JS array
344
- * (`number[]` / `bigint[]` / `boolean[]`) of exactly `arrayLen` elements
345
- *
346
- * Throws on missing / wrong-shape inputs. Decode tolerates extra trailing
347
- * bytes (mirrors C++ side: the master ignores trailing slop so that future
348
- * schema additions don't break older decoders).
312
+ * Field-driven dict ⇄ Uint8Array codec. Throws on shape mismatch; decode
313
+ * tolerates trailing bytes so future schema additions don't break decoders.
349
314
  */
350
315
  declare class DynamicCodecError extends Error {
351
316
  constructor(message: string);
352
317
  }
353
- /** Tag union of accepted JS value shapes for one field. */
354
318
  type FieldValue = number | bigint | boolean | number[] | bigint[] | boolean[];
355
- /** Dict shape — string-keyed values, one entry per field. */
356
319
  type Dict = Record<string, FieldValue>;
357
320
  /**
358
321
  * Sum the wire size of a field list. The result is the byte count that
359
322
  * {@link encodeFields} will produce.
360
323
  */
361
324
  declare function fieldsWireSize(fields: readonly SchemaField[]): number;
362
- /**
363
- * Encode a dict into the canonical little-endian wire bytes for `fields`.
364
- * Returns a fresh `Uint8Array`. Throws {@link DynamicCodecError} on a
365
- * missing or wrong-shape field.
366
- */
325
+ /** Encode a dict into little-endian wire bytes. Throws on missing / wrong-shape fields. */
367
326
  declare function encodeFields(fields: readonly SchemaField[], value: Dict): Uint8Array;
368
- /**
369
- * Decode wire bytes into a dict keyed by field name. Extra trailing bytes
370
- * are ignored. Throws {@link DynamicCodecError} only if the payload is
371
- * shorter than the field list demands.
372
- */
327
+ /** Decode wire bytes into a dict. Trailing bytes are ignored; throws only if too short. */
373
328
  declare function decodeFields(fields: readonly SchemaField[], bytes: Uint8Array): Dict;
374
329
  //#endregion
375
330
  //#region src/numbering.d.ts
376
331
  /**
377
- * Flat-numbering tables for topics, services, and parameters (§4.2).
378
- *
379
- * Port of `fibril_can_core/src/numbering.cpp`. Numbering is a pure function
380
- * of (schema, instance-count-vector). The same tables are derived
381
- * independently by codegen (with max_count for static checks), the slave
382
- * runtime (with actual counts at boot) and the master (with counts from the
383
- * ANNOUNCE frame) — keeping this in one place is what guarantees they agree.
332
+ * Flat-numbering tables for topics, services, and parameters — a pure
333
+ * function of (schema, instance-count-vector). Codegen, slave runtime, and
334
+ * master all derive these tables through the same function.
384
335
  */
385
336
  declare const SERVICE_INDEX_CANCEL = 255;
386
337
  declare const SERVICE_INDEX_PARAM_SET = 253;
387
- /** Maximum first-class service_index produced by numbering (inclusive). */
388
338
  declare const SERVICE_INDEX_MAX = 252;
389
- /** Number of available first-class service slots. */
390
339
  declare const SERVICE_INDEX_CAPACITY: number;
391
340
  declare enum NumberingError {
392
341
  Ok = "ok",
@@ -396,23 +345,15 @@ declare enum NumberingError {
396
345
  InstanceCountMismatch = "actual_counts length != #instances",
397
346
  BlockTypeOutOfRange = "BlockArray::blockTypeIndex out of range"
398
347
  }
399
- /** Concrete location of one (instance, endpoint) within a NodeSchema. */
400
348
  interface EndpointKey {
401
- /** Index into NodeSchema.instances. */
402
349
  blockArray: number;
403
- /** 0..count-1 */
404
350
  instance: number;
405
- /** 0..nEndpoints(type)-1 */
406
351
  endpoint: number;
407
352
  }
408
- /** Forward + inverse maps for a single endpoint kind. */
409
353
  interface NumberingTable {
410
- /**
411
- * size = Σ_b(actualCounts[b] × nEndpoints(types[instances[b].blockTypeIndex]));
412
- * the array index IS the flat numbering.
413
- */
354
+ /** Array index is the flat numbering. */
414
355
  byIndex: EndpointKey[];
415
- /** Per-block-array base offsets (size = #blockArrays + 1; last is total). */
356
+ /** Per-block-array base offsets. Length = #blockArrays + 1; last is total. */
416
357
  bases: number[];
417
358
  }
418
359
  interface NumberingResult {
@@ -422,12 +363,8 @@ interface NumberingResult {
422
363
  status: NumberingError;
423
364
  }
424
365
  /**
425
- * Compute the three numbering tables. `actualCounts` must have the same
426
- * length as `schema.instances`. The result has `status !== Ok` if input is
427
- * malformed or service_index would overflow into the reserved range
428
- * (§4.2 §9.4).
429
- *
430
- * On error the returned tables are empty but still safe to inspect.
366
+ * `actualCounts.length` must equal `schema.instances.length`. On any error
367
+ * the returned tables are empty and `status !== Ok`.
431
368
  */
432
369
  declare function computeNumbering(schema: NodeSchema, actualCounts: readonly number[]): NumberingResult;
433
370
  //#endregion
@@ -466,10 +403,6 @@ interface ParamDescriptor {
466
403
  readonly maxValue: Uint8Array;
467
404
  }
468
405
  type EndpointDescriptor = TopicDescriptor | ServiceDescriptor | ParamDescriptor;
469
- /**
470
- * Path → descriptor index for one slave. Built once from
471
- * (schema, numbering) and reused for the lifetime of a snapshot.
472
- */
473
406
  declare class SchemaIndex {
474
407
  #private;
475
408
  constructor(schema: NodeSchema, numbering: NumberingResult, instanceCounts: readonly number[]);
@@ -517,13 +450,8 @@ declare class NoCallSlotError extends ServiceCallError {
517
450
  constructor(serviceIndex: number);
518
451
  }
519
452
  /**
520
- * Single-slave master service-call client. Port of NodeAgent's
521
- * begin_service_call / on_service_response / on_service_segment flow but
522
- * Promise-based and AbortSignal-aware.
523
- *
524
- * One {@link ServiceClient} attaches to the transport and demuxes incoming
525
- * SVC_RESPONSE frames for a single `nodeId`. NodeAgent owns one per slave
526
- * and exposes it on the running snapshot.
453
+ * Promise-based, AbortSignal-aware service-call client for one slave.
454
+ * NodeAgent owns one per slave and exposes it on the running snapshot.
527
455
  */
528
456
  declare class ServiceClient {
529
457
  #private;
@@ -550,24 +478,8 @@ declare class ServiceClient {
550
478
  //#endregion
551
479
  //#region src/typed-param.d.ts
552
480
  /**
553
- * Schema-driven typed wrapper around PARAM_SET (SPEC §8.2, reserved
554
- * service_index 0xFD).
555
- *
556
- * Per SPEC §8.1 PARAM is M→S one-way: the master holds the source of truth
557
- * and re-injects defaults at provisioning time, so this wrapper exposes
558
- * `.set()` only — there's no `.get()` that queries the slave. The
559
- * descriptor's decoded default is available via `defaultValue()` for
560
- * reference / hydration of master-side state.
561
- *
562
- * The wire shape for one PARAM_SET request is:
563
- * [0] count = 1
564
- * [1..2] u16 param_index (LE)
565
- * [3..] value (scalar little-endian or fixed-array contiguous)
566
- *
567
- * Response is `[0] status [1..2] failed_param_index (when status != OK)`.
568
- * `set()` resolves with the status code; non-OK includes `failedIndex` so
569
- * the caller can correlate with a multi-param batch (single-param sets
570
- * always blame this param).
481
+ * Typed wrapper around PARAM_SET (service_index 0xFD). PARAM is M→S
482
+ * one-way, so this exposes `.set()` and `.defaultValue()` only.
571
483
  */
572
484
  declare class ParamCodecError extends Error {
573
485
  constructor(message: string);
@@ -577,13 +489,9 @@ interface TypedParamSetOptions {
577
489
  signal?: AbortSignal;
578
490
  }
579
491
  interface TypedParamSetResult {
580
- /** SPEC §7.2 status (0 = SVC_OK). */
492
+ /** SVC_OK = 0; non-zero = slave error code. */
581
493
  readonly status: number;
582
- /**
583
- * On `status != SVC_OK`, the slave-reported param_index that failed.
584
- * For a single-param call this is always `descriptor.paramIndex` if the
585
- * slave fills the field, else `null` (slave omitted the field).
586
- */
494
+ /** Slave-reported failing param_index (non-OK only); null if omitted. */
587
495
  readonly failedIndex: number | null;
588
496
  }
589
497
  /** Build a PARAM_SET request payload for a single (descriptor, value) pair. */
@@ -593,11 +501,7 @@ declare class TypedParam<T> {
593
501
  readonly path: string;
594
502
  readonly descriptor: ParamDescriptor;
595
503
  constructor(client: ServiceClient, descriptor: ParamDescriptor);
596
- /**
597
- * Decode the schema-embedded default value (SPEC §9.1 `default`). Useful
598
- * to seed the master-side persistence store. Returns `null` if the
599
- * descriptor carries no default payload.
600
- */
504
+ /** Decoded schema default; `null` if the descriptor carries no default. */
601
505
  defaultValue(): T | null;
602
506
  /** Send a PARAM_SET with a single entry for this param. */
603
507
  set(value: T, opts?: TypedParamSetOptions): Promise<TypedParamSetResult>;
@@ -605,20 +509,9 @@ declare class TypedParam<T> {
605
509
  //#endregion
606
510
  //#region src/typed-service.d.ts
607
511
  /**
608
- * Schema-driven typed wrapper around {@link ServiceClient.call}.
609
- *
610
- * The generic parameters are compile-time only: at runtime, encoding is
611
- * driven entirely by the {@link ServiceDescriptor}'s field list. Callers
612
- * supply `Req` / `Resp` interfaces matching the schema fields by name —
613
- * the runtime trusts the dict layout and packs by field iteration order.
614
- *
615
- * Returned on success: `{ status, data }` where `status` is the SPEC §7.2
616
- * service status (0 = OK) and `data` is the decoded response dict typed as
617
- * `Resp`. Non-OK responses still return a `data` object — the slave is
618
- * required to fill the response payload even on failure (§7.2), and the
619
- * decoder will best-effort attempt it; if the body is shorter than the
620
- * schema demands, `data` is `null` instead of throwing (so callers can
621
- * surface the status to the user without losing the whole response).
512
+ * Result of a {@link TypedService.call}. `status` follows SPEC §7.2 (0 = OK);
513
+ * `data` is best-effort even on non-OK, and `null` if the response body is
514
+ * shorter than the schema demands.
622
515
  */
623
516
  interface TypedServiceResult<Resp extends Dict> {
624
517
  readonly status: number;
@@ -638,35 +531,20 @@ declare class TypedService<Req extends Dict, Resp extends Dict> {
638
531
  }
639
532
  //#endregion
640
533
  //#region src/node-agent.d.ts
641
- /**
642
- * One planned data-plane CAN frame, supplied by the caller (typically a
643
- * frame planner — P6) and consumed by {@link NodeAgent} when it issues
644
- * FRAME_DEFINE during bring-up. Mirrors `FrameSpec` in
645
- * `fibril_can_bridge/include/fibril_can_bridge/frame_planner.hpp`.
646
- */
534
+ /** One planned data-plane CAN frame consumed by FRAME_DEFINE during bring-up. */
647
535
  interface FramePlan {
648
536
  stdId: number;
649
537
  periodUs: number;
650
538
  dir: Direction;
651
539
  entries: readonly FrameDefineEntry[];
652
540
  }
653
- /**
654
- * Per-bring-up context handed to a {@link FramePlanFactory} callback so the
655
- * caller can branch on the specific slave being provisioned (e.g. read an
656
- * external per-nodeId field-enable override map). The same factory is
657
- * shared across every {@link NodeAgent} the controller spawns, so this is
658
- * the only way for the factory to discriminate slaves.
659
- */
541
+ /** Context handed to {@link FramePlanFactory} so it can branch on the slave. */
660
542
  interface FramePlanContext {
661
- /** The slave being provisioned (1..0x7E). */
662
543
  readonly nodeId: number;
663
544
  }
664
545
  /**
665
- * Builds the FRAME_DEFINE plan for a slave once its schema is decoded and
666
- * numbered. The optional `ctx` argument carries the nodeId so callers that
667
- * keep per-slave overrides (e.g. UI-controlled S→M field toggles) can fork
668
- * on it without threading state through {@link NodeAgentOptions}. The
669
- * 2-arg form is still accepted — extra parameters are simply ignored.
546
+ * Builds the FRAME_DEFINE plan once schema and numbering are known.
547
+ * `ctx.nodeId` lets a shared factory fork on the specific slave.
670
548
  */
671
549
  type FramePlanFactory = ((schema: NodeSchema, numbering: NumberingResult, ctx: FramePlanContext) => readonly FramePlan[]) | readonly FramePlan[];
672
550
  /** Phase machine — public for observers. */
@@ -683,18 +561,11 @@ type NodeProvisionState = {
683
561
  } | {
684
562
  kind: 'provisioning';
685
563
  pendingDefines: number;
686
- }
687
- /**
688
- * FRAME_DEFINE completed successfully but START has been withheld by
689
- * the {@link NodeAgentOptions.autoStart} gate. `startNow()` moves the
690
- * agent forward. `reprovision()` / a fresh ANNOUNCE re-enter the flow
691
- * from bring-up.
692
- */
693
- | {
564
+ } /** FRAME_DEFINE done, autoStart blocked START. `startNow()` moves forward. */ | {
694
565
  kind: 'awaiting-start';
695
566
  } | {
696
567
  kind: 'running';
697
- } /** Slave reported node_state = SUSPENDED. Reverts to running on the next non-SUSPENDED HEARTBEAT. */ | {
568
+ } /** Slave reported SUSPENDED. Reverts to running on the next non-SUSPENDED HEARTBEAT. */ | {
698
569
  kind: 'suspended';
699
570
  } | {
700
571
  kind: 'lost';
@@ -717,22 +588,11 @@ interface NodeRunningSnapshot {
717
588
  stdIdToTopicIndex: Map<number, number>;
718
589
  /** §7 master-side RPC client. Stays valid for the lifetime of the snapshot. */
719
590
  services: ServiceClient;
720
- /**
721
- * Path → IR descriptor lookup. Drives the typed accessors below.
722
- * Exposed for sniffer-style introspection (`index.topicPaths()` etc.).
723
- */
591
+ /** Path → IR descriptor lookup; drives `service()` / `param()` below. */
724
592
  index: SchemaIndex;
725
- /**
726
- * Schema-driven typed RPC wrapper. Throws if `path` is unknown. Generic
727
- * `Req` / `Resp` are compile-time only — runtime encoding is driven by
728
- * the schema's field list.
729
- */
593
+ /** Typed RPC wrapper. Throws if `path` is unknown. */
730
594
  service<Req extends Dict, Resp extends Dict>(path: string): TypedService<Req, Resp>;
731
- /**
732
- * Schema-driven PARAM_SET wrapper. Throws if `path` is unknown.
733
- * `T` is `number` for scalar params (except U64/I64 → `bigint`),
734
- * `boolean` for bool, or the array form for fixed-length array params.
735
- */
595
+ /** Typed PARAM_SET wrapper. Throws if `path` is unknown. */
736
596
  param<T = number | bigint | boolean | number[] | bigint[] | boolean[]>(path: string): TypedParam<T>;
737
597
  }
738
598
  interface NodeAgentOptions {
@@ -742,59 +602,34 @@ interface NodeAgentOptions {
742
602
  transport: ITransport;
743
603
  /** Strategy for building the FRAME_DEFINE plan. */
744
604
  framePlan: FramePlanFactory;
745
- /**
746
- * Per-config-command ACK timeout in ms. Slaves must ACK within this
747
- * window or the agent abandons the bring-up attempt and waits for the
748
- * next ANNOUNCE. Default 250 ms (matches bridge's
749
- * `bring_up_ack_timeout_`).
750
- */
605
+ /** Per-config-command ACK timeout in ms. Default 250. */
751
606
  ackTimeoutMs?: number;
752
- /**
753
- * SCHEMA_READ chunk byte limit. SPEC §4.3 caps a single response at the
754
- * SINGLE-frame payload (≤59 data bytes after the 4-byte offset header).
755
- * Default 59.
756
- */
607
+ /** SCHEMA_READ chunk byte limit — caps at the SINGLE-frame payload. Default 59. */
757
608
  schemaChunkSize?: number;
758
- /**
759
- * Multiplier on `ackTimeoutMs` used as the schema-read retry budget.
760
- * Default 3 (one initial + two retries).
761
- */
609
+ /** SCHEMA_READ retry budget. Default 3. */
762
610
  schemaReadRetries?: number;
763
- /**
764
- * Heartbeat-loss budget. The agent expects a HEARTBEAT every 100 ms
765
- * (§14); after this much silence it transitions to `lost`. Default 350.
766
- */
611
+ /** HEARTBEAT-loss budget in ms. Default 350. */
767
612
  heartbeatTimeoutMs?: number;
768
613
  /** Per-call default timeout for `services.call()`. Default `ackTimeoutMs`. */
769
614
  serviceTimeoutMs?: number;
770
615
  /**
771
- * Gate consulted right before START is issued. When it returns `false`,
772
- * the agent stops in {@link NodeProvisionState} `awaiting-start` after
773
- * FRAME_DEFINE ACKs come back; the caller commits with `startNow()`.
774
- * Read fresh at each decision point so callers can flip the setting at
775
- * runtime without reconstructing the agent. Default `() => true`.
616
+ * Gate consulted before each START. Returning `false` parks in
617
+ * `awaiting-start`; caller commits with `startNow()`. Read fresh each
618
+ * time so the flag can flip at runtime. Default `() => true`.
776
619
  */
777
620
  autoStart?: () => boolean;
778
621
  /** Optional state-change observer. */
779
622
  onState?: (state: NodeProvisionState) => void;
780
- /** Optional non-fatal error observer (decode warnings, hash mismatch). */
623
+ /** Optional non-fatal error observer. */
781
624
  onError?: (err: Error) => void;
782
- /**
783
- * Override for the wall clock. Tests pass a deterministic counter.
784
- * Default `() => Date.now()`.
785
- */
625
+ /** Wall-clock override for tests. Default `() => Date.now()`. */
786
626
  now?: () => number;
787
627
  }
788
628
  /**
789
- * Per-slave provisioning state machine. Mirrors
790
- * `fibril_can_bridge::NodeAgent` lifecycle entry points but trimmed for the
791
- * single-threaded TS master — no ROS entity creation, no PARAM_SET
792
- * re-injection (P5), no plan computation (P6).
793
- *
794
- * Lifecycle: `idle` → `awaiting-announce` → `schema-reading` (skipped if
795
- * blob already cached on this agent instance) → `numbering` →
796
- * `provisioning` → `running`. HEARTBEAT loss drops to `lost`; the next
797
- * ANNOUNCE restarts the flow.
629
+ * Per-slave provisioning state machine.
630
+ * `idle` → `awaiting-announce` → `schema-reading` (skipped on cache hit) →
631
+ * `numbering` → `provisioning` → `running`. HEARTBEAT loss drops to `lost`;
632
+ * the next ANNOUNCE restarts the flow.
798
633
  */
799
634
  declare class NodeAgent {
800
635
  #private;
@@ -805,59 +640,32 @@ declare class NodeAgent {
805
640
  get state(): NodeProvisionState;
806
641
  get snapshot(): NodeRunningSnapshot | null;
807
642
  /**
808
- * Attach to the transport and wait for ANNOUNCE. Resolves immediately —
809
- * progress flows via {@link state} / {@link snapshot} / `onState`. Call
810
- * `stop()` to detach.
643
+ * Attach and wait for ANNOUNCE. Resolves immediately; progress flows via
644
+ * {@link state} / {@link snapshot} / `onState`.
811
645
  */
812
646
  start(): Promise<void>;
813
647
  /**
814
- * Re-run the FRAME_DEFINE / START handshake against the live slave using
815
- * the most recent ANNOUNCE + cached schema. The {@link FramePlanFactory}
816
- * is invoked again — callers that key off an external mutable state
817
- * (e.g. per-field TX-enable overrides) will see the latest values.
818
- *
819
- * The agent must have observed an ANNOUNCE already; `reprovision()`
820
- * before that throws. RUNNING is required so that the slave is known to
821
- * be reachable — calling during `lost` would race the heartbeat
822
- * watchdog. Pending service calls are failed and the current snapshot is
823
- * torn down before the new bring-up begins, mirroring what happens on a
824
- * boot_id change.
648
+ * Re-run FRAME_DEFINE / START against the live slave with the most recent
649
+ * ANNOUNCE + cached schema. Invokes {@link FramePlanFactory} again so
650
+ * per-slave overrides pick up their latest values. Requires a prior
651
+ * ANNOUNCE. Tears down the current snapshot and fails pending service
652
+ * calls, mirroring a boot_id change.
825
653
  */
826
654
  reprovision(): Promise<void>;
827
655
  /** Detach, send STOP if currently RUNNING, clear all in-flight state. */
828
656
  stop(): Promise<void>;
829
657
  /**
830
- * Commit the deferred START when {@link NodeAgentOptions.autoStart}
831
- * gated the previous bring-up. Only legal in `awaiting-start`; any
832
- * other state throws so callers spot logic bugs instead of racing
833
- * with a fresh ANNOUNCE.
658
+ * Commit the deferred START. Only legal in `awaiting-start`; throws
659
+ * elsewhere so callers spot logic bugs instead of racing a fresh ANNOUNCE.
834
660
  */
835
661
  startNow(): Promise<void>;
836
662
  }
837
663
  //#endregion
838
664
  //#region src/data-plane.d.ts
839
665
  /**
840
- * Master-side decoder for S→M data-plane frames.
841
- *
842
- * Listens on the transport for standard-ID frames whose IDs match one of
843
- * the std_ids configured in any attached slave's {@link FramePlan}. For
844
- * each match, unpacks the topic entries packed into the frame's payload
845
- * (in FRAME_DEFINE order) using the schema field bitmask and publishes
846
- * a decoded {@link Dict} per topic to subscribers.
847
- *
848
- * Lifecycle:
849
- * - `new DataPlaneReceiver({ transport })` registers an `onFrame` tap.
850
- * - `attachSlave(nodeId, framePlan, schemaIndex)` claims a slave's std_ids.
851
- * - `detachSlave(nodeId)` releases them (call on RUNNING → not-RUNNING).
852
- * - `dispose()` unsubscribes and clears all state.
853
- *
854
- * Two slaves cannot publish on the same std_id — attaching the second
855
- * throws. This mirrors the bus-level reality: identical std_ids would
856
- * collide on the wire.
857
- *
858
- * M→S frames (master output, dir `Direction.M2S`) are skipped during
859
- * attach so the receiver doesn't try to decode echo or self-loopback
860
- * frames as if they were slave-published.
666
+ * Master-side decoder for S→M data-plane frames. `attachSlave` claims a
667
+ * slave's std_ids; two slaves cannot share a std_id (throws). M→S frames
668
+ * are skipped so echo / self-loopback isn't decoded as slave-published.
861
669
  */
862
670
  interface DataPlaneReceiverOptions {
863
671
  transport: ITransport;
@@ -872,23 +680,16 @@ interface TopicSample {
872
680
  readonly tsMs: number;
873
681
  }
874
682
  interface TopicStats {
875
- /** Frames ever decoded into this topic since the slave attached. */
876
683
  readonly count: number;
877
684
  /** `now()` of the most recent frame. */
878
685
  readonly lastRxMs: number;
879
- /**
880
- * Rolling rate over the last few samples (Hz). 0 until at least
881
- * two frames have been seen.
882
- */
686
+ /** Rolling rate in Hz over the last {@link RATE_WINDOW} samples; 0 with <2 samples. */
883
687
  readonly ratePerSec: number;
884
688
  }
885
689
  declare class DataPlaneReceiver {
886
690
  #private;
887
691
  constructor(opts: DataPlaneReceiverOptions);
888
- /**
889
- * Claim a slave's S→M std_ids. Re-attaching the same nodeId atomically
890
- * detaches the prior plan first (re-provisioning case).
891
- */
692
+ /** Claim a slave's S→M std_ids. Re-attach atomically detaches the prior plan. */
892
693
  attachSlave(nodeId: number, framePlan: readonly FramePlan[], index: SchemaIndex): void;
893
694
  /** Release a slave's std_ids and drop its cached topic samples. */
894
695
  detachSlave(nodeId: number): void;
@@ -904,19 +705,10 @@ declare class DataPlaneReceiver {
904
705
  //#endregion
905
706
  //#region src/frame-planner.d.ts
906
707
  /**
907
- * Frame planner — turns a list of "I want field X at period Y" requests into
908
- * the concrete FRAME_DEFINE bodies the master ships to the slave (§6.3).
909
- *
910
- * Port of `fibril_can_core/src/frame_planner.cpp` — same bucketing rules:
911
- * - Group requests by (direction, period_us); never combine across either.
912
- * - Inside a bucket, bin-pack whole topics into ≤64-byte frames.
913
- * - A single topic whose declared fields exceed 64 bytes is the sole
914
- * field-split exception (`emitOversizedTopic`).
915
- *
916
- * Determinism is load-bearing: codegen, bridge, and this TS master must
917
- * produce identical std_id assignments for the same input. Both the
918
- * bucket map and the per-bucket topic map iterate in ascending key order
919
- * (matching `std::map` in the C++ reference).
708
+ * Field-requests → FRAME_DEFINE bodies. Groups by (dir, period_us) and
709
+ * bin-packs whole topics into ≤64-byte frames; a topic that exceeds 64 B
710
+ * is the sole field-split exception. Iteration order is ascending on
711
+ * every level so codegen / bridge / master produce identical std_ids.
920
712
  */
921
713
  interface FieldRequest {
922
714
  topicIndex: number;
@@ -936,9 +728,8 @@ interface FrameSpec {
936
728
  entries: FrameDefineEntry[];
937
729
  }
938
730
  /**
939
- * 11-bit Standard ID allocator with a free pool for IDs released after a
940
- * grace window (§11.4.1). `next()` returns `null` on exhaustion rather
941
- * than wrapping — wrapping past 0x7FF would alias on the wire.
731
+ * 11-bit std_id allocator with a delayed free pool. `next()` returns `null`
732
+ * on exhaustion — wrapping past 0x7FF would alias on the wire.
942
733
  */
943
734
  declare class StdIdAllocator {
944
735
  #private;
@@ -947,26 +738,16 @@ declare class StdIdAllocator {
947
738
  reset(start?: number): void;
948
739
  }
949
740
  /**
950
- * Plan a slave's frame layout. Returns `null` if the StdIdAllocator runs
951
- * out of 11-bit IDs mid-plan — the caller MUST surface that; a partial
952
- * plan would silently drop fields.
741
+ * Plan a slave's frame layout. Returns `null` on std_id exhaustion — a
742
+ * partial plan would silently drop fields.
953
743
  */
954
744
  declare function planFrames(requests: readonly FieldRequest[], alloc: StdIdAllocator): FrameSpec[] | null;
955
745
  //#endregion
956
746
  //#region src/default-plan.d.ts
957
747
  /**
958
- * Per-field predicate handed to {@link buildDefaultS2MRequests} (and via
959
- * it, {@link defaultS2MFramePlan}) so callers can apply a runtime mask on
960
- * top of the schema-declared `txEnabled`.
961
- *
962
- * Contract:
963
- * - Returning `false` skips the field entirely; the slave will not pack
964
- * it into any S→M frame. Returning `true` keeps it.
965
- * - Called only for S→M fields whose schema declares `txEnabled !==
966
- * false` (schema-disabled fields are filtered out unconditionally).
967
- * - `topicPath` is the same `'<expanded_ns>/<topic_name>'` string the
968
- * master uses elsewhere (mirrors {@link SchemaIndex.topicPaths}), so
969
- * UI override maps can key on the same identifier.
748
+ * Predicate for masking S→M fields on top of the schema-declared `txEnabled`.
749
+ * Only called for fields with `txEnabled !== false`. Return `false` to drop
750
+ * the field. `topicPath` mirrors {@link SchemaIndex.topicPaths}.
970
751
  */
971
752
  type S2MFieldFilter = (info: {
972
753
  topicPath: string;
@@ -976,84 +757,37 @@ type S2MFieldFilter = (info: {
976
757
  field: SchemaField;
977
758
  }) => boolean;
978
759
  /**
979
- * Synthesize the default {@link FieldRequest} list from a decoded schema +
980
- * numbering table — every S2M topic field at its declared period
981
- * (`field.periodUsOverride` if present, else the owning topic's
982
- * `defaultPeriodUs`), skipping `txEnabled === false` fields.
983
- *
984
- * Pass `filter` to layer a runtime per-field mask on top (UI toggles,
985
- * per-slave overrides). Schema-disabled fields are unconditionally
986
- * dropped before `filter` is consulted, so callers don't have to repeat
987
- * the `txEnabled` check.
988
- *
989
- * This is the natural input to {@link planFrames} when the master just
990
- * wants to "observe everything the slave publishes" — i.e. the
991
- * sniffer-style auto-provision path.
760
+ * Every schema-enabled S→M field at its declared period
761
+ * (`field.periodUsOverride` ?? topic's `defaultPeriodUs`), optionally masked
762
+ * further by `filter`. Feeds {@link planFrames} for the observe-everything
763
+ * auto-provision path.
992
764
  */
993
765
  declare function buildDefaultS2MRequests(schema: NodeSchema, numbering: NumberingResult, filter?: S2MFieldFilter): FieldRequest[];
994
766
  /**
995
- * One-line FramePlanFactory that subscribes to every S2M field at its
996
- * declared period using a freshly-allocated {@link StdIdAllocator}.
997
- *
998
- * Pass `filter` to apply a runtime per-field mask (e.g. a UI-controlled
999
- * field-tx-enable map). With no filter, every schema-enabled S→M field is
1000
- * subscribed.
1001
- *
1002
- * Returns `[]` when planning fails (e.g. std_id exhaustion); the caller's
1003
- * NodeAgent will then leave the slave in `provisioning` → `awaiting-announce`
1004
- * with no frames defined, which is the right outcome — partial plans would
1005
- * silently drop fields.
767
+ * FramePlanFactory subscribing to every schema-enabled S→M field (optionally
768
+ * masked by `filter`) with a fresh {@link StdIdAllocator}. Returns `[]` on
769
+ * planning failure so NodeAgent stalls in `awaiting-announce` — a partial
770
+ * plan would silently drop fields.
1006
771
  */
1007
772
  declare function defaultS2MFramePlan(schema: NodeSchema, numbering: NumberingResult, filter?: S2MFieldFilter): FramePlan[];
1008
773
  //#endregion
1009
774
  //#region src/dlc.d.ts
1010
- /**
1011
- * CAN FD valid payload sizes in ascending order (§4.3).
1012
- * Mirrors `fibril_can_core/include/fibril_can_core/packing.hpp::kCanFdDlcSteps`
1013
- * and `fibril_can_runtime/src/fcan_wire.c::fcan_quantize_dlc`.
1014
- */
775
+ /** Valid CAN FD payload sizes, ascending. */
1015
776
  declare const FD_DLC_STEPS: readonly number[];
1016
- /**
1017
- * Round `bytes` up to the next valid CAN FD payload step. Returns 64 for any
1018
- * input ≥ 64.
1019
- */
777
+ /** Round `bytes` up to the next valid CAN FD payload step (caps at 64). */
1020
778
  declare function quantizeDlc(bytes: number): number;
1021
779
  /**
1022
- * Grow `data` to the next DLC step by appending zero bytes. Returns a new
1023
- * Uint8Array. Never shrinks: if `data` already exceeds 64 bytes (which is
1024
- * a self-inconsistent frame the slave would misparse), the original is
1025
- * returned unchanged so the backend rejects it loudly (parity with
1026
- * `pad_to_dlc` in `fibril_can_bridge/src/config_plane_codec.cpp`).
780
+ * Pad `data` to the next DLC step with zeros. Over-length input (>64 B, which
781
+ * the slave would misparse) is returned unchanged so the backend rejects it.
1027
782
  */
1028
783
  declare function padToDlc(data: Uint8Array): Uint8Array;
1029
784
  //#endregion
1030
785
  //#region src/master-controller.d.ts
1031
786
  /**
1032
- * Multi-slave coordinator.
1033
- *
1034
- * One {@link MasterController} owns:
1035
- * - a single transport (the bus)
1036
- * - one {@link MasterHeartbeatBroadcaster} (shared across slaves)
1037
- * - a single broadcast DISCOVER at start() to flush every slave's frame
1038
- * table and pull fresh ANNOUNCEs onto the bus
1039
- * - a `Map<nodeId, NodeAgent>` of provisioned slaves — auto-spawned on
1040
- * the first ANNOUNCE / HEARTBEAT from a previously-unseen nodeId
1041
- *
1042
- * Periodic broadcast DISCOVER is intentionally NOT done. Broadcast
1043
- * DISCOVER is destructive (§5.11): every receiving slave clears its frame
1044
- * table and drops to UNPROVISIONED. Late-joining slaves are covered by
1045
- * their own spontaneous ANNOUNCE × 3 at boot (§5.4); the user-initiated
1046
- * "Discover" button (`discoverNow()`) is the escape hatch for the rare
1047
- * case where the master came up first and missed the burst.
1048
- *
1049
- * Provisioning per slave still goes through {@link NodeAgent}. The
1050
- * controller is the right place to add cross-slave concerns later (PARAM
1051
- * persistence, name ownership, lab-mode multiplexers) without bloating
1052
- * `NodeAgent`.
1053
- *
1054
- * The controller does NOT replace `NodeAgent` for single-slave embedded
1055
- * use cases (e.g. tests, a fixed-topology setup); it sits one layer above
1056
- * and is the natural entrypoint for the Web UI.
787
+ * Multi-slave coordinator. Owns the transport, one MASTER_HEARTBEAT
788
+ * broadcaster, and a `Map<nodeId, NodeAgent>` populated on first sight of
789
+ * ANNOUNCE / HEARTBEAT. Broadcast DISCOVER is destructive so it is fired
790
+ * only once at start(); use `discoverNow()` on demand.
1057
791
  */
1058
792
  /** Default policy hooks applied to every spawned NodeAgent. */
1059
793
  interface MasterControllerOptions {
@@ -1064,20 +798,11 @@ interface MasterControllerOptions {
1064
798
  /** MASTER_HEARTBEAT broadcast period in ms. Default 100 (§5.11). */
1065
799
  heartbeatPeriodMs?: number;
1066
800
  /**
1067
- * Broadcast DISCOVER period in ms. Default 0 (disabled). Setting >0 is
1068
- * destructive — every periodic broadcast DISCOVER wipes every slave's
1069
- * frame table and forces re-provisioning, breaking S→M topic flow until
1070
- * each NodeAgent re-pushes its FRAME_DEFINEs. Useful only for very
1071
- * niche reconnect-storm scenarios; prefer `discoverNow()` for on-demand
1072
- * rediscovery and rely on slave spontaneous ANNOUNCE × 3 (§5.4) for
1073
- * normal late-join.
801
+ * Periodic broadcast DISCOVER period in ms. Default 0 (disabled). Each
802
+ * firing wipes every slave's frame table — prefer `discoverNow()`.
1074
803
  */
1075
804
  discoverPeriodMs?: number;
1076
- /**
1077
- * Send a single broadcast DISCOVER inside `start()` to flush stale
1078
- * slave state from a previous master session. Default true. Set false
1079
- * in tests that drive ANNOUNCE manually and need a quiet bus.
1080
- */
805
+ /** One broadcast DISCOVER inside `start()` to flush stale slaves. Default true. */
1081
806
  initialDiscover?: boolean;
1082
807
  /** Per-NodeAgent ACK timeout in ms. Default 250 (matches NodeAgent). */
1083
808
  ackTimeoutMs?: number;
@@ -1085,12 +810,7 @@ interface MasterControllerOptions {
1085
810
  heartbeatTimeoutMs?: number;
1086
811
  /** Per-service-call default timeout in ms. Default `ackTimeoutMs`. */
1087
812
  serviceTimeoutMs?: number;
1088
- /**
1089
- * Read-through gate applied to every spawned NodeAgent right before it
1090
- * issues START. Callers pass a getter (not a boolean) so the flag can
1091
- * be flipped at runtime — see `NodeAgentOptions.autoStart`. Default
1092
- * `() => true`.
1093
- */
813
+ /** Getter (not a boolean) gating START on every spawned NodeAgent. Default `() => true`. */
1094
814
  autoStart?: () => boolean;
1095
815
  /** Optional clock override (tests). Default `() => Date.now()`. */
1096
816
  now?: () => number;
@@ -1115,48 +835,32 @@ declare class MasterController {
1115
835
  #private;
1116
836
  constructor(opts: MasterControllerOptions);
1117
837
  /**
1118
- * Master-side decoder for S→M topic frames. Auto-attached / detached as
1119
- * each slave transitions in and out of `running`. Subscribe with
1120
- * `dataPlane.onTopic(nodeId, path, listener)`; read latest values with
1121
- * `dataPlane.latest(nodeId, path)`.
838
+ * S→M topic decoder. Auto-attaches / detaches on each slave's `running`
839
+ * transition; subscribe via `dataPlane.onTopic` / read via `dataPlane.latest`.
1122
840
  */
1123
841
  get dataPlane(): DataPlaneReceiver;
1124
- /**
1125
- * Attach to the transport, start MASTER_HEARTBEAT, fire one initial
1126
- * broadcast DISCOVER to flush stale slave state from a previous master
1127
- * session. New slaves auto-spawn a NodeAgent on first sight.
1128
- */
1129
842
  start(): void;
1130
843
  /** Stop all internals, stop every NodeAgent, detach from the transport. */
1131
844
  stop(): Promise<void>;
1132
845
  /** Snapshot of all known slaves. Stable identity per call. */
1133
846
  snapshots(): ControllerSlaveSnapshot[];
1134
- /** Subscribe to slave-set / state-change updates. Fires on every change. */
847
+ /** Subscribe to slave-set / state-change updates. Fires immediately with the current snapshot, then on every change. */
1135
848
  onChange(cb: ControllerListener): Unsubscribe;
1136
- /**
1137
- * Access the NodeAgent for one slave, if it exists. Returns null until
1138
- * the slave has been observed. Useful for service / param calls from
1139
- * code that already knows the nodeId.
1140
- */
849
+ /** NodeAgent for one slave, or null if not yet observed. */
1141
850
  agent(nodeId: number): NodeAgent | null;
1142
- /** Issue an ad-hoc broadcast DISCOVER (between periodic firings). */
851
+ /** Ad-hoc broadcast DISCOVER. */
1143
852
  discoverNow(): Promise<void>;
1144
853
  /**
1145
- * Commit the deferred START on one slave that is parked in
1146
- * `awaiting-start` because the autoStart gate blocked its bring-up.
1147
- * Returns `false` if the slave is unknown or not currently waiting
1148
- * for a manual start; otherwise resolves once START has been queued
1149
- * on the wire (the RUNNING transition arrives later via `onChange`).
854
+ * Commit the deferred START on a slave parked in `awaiting-start`.
855
+ * Returns `false` if the slave is unknown or not waiting; the RUNNING
856
+ * transition arrives later via `onChange`.
1150
857
  */
1151
858
  startNode(nodeId: number): Promise<boolean>;
1152
859
  /**
1153
- * Re-run the FRAME_DEFINE / START handshake for one slave so the
1154
- * {@link FramePlanFactory} is consulted again. Use when external state
1155
- * the factory closes over has changed (e.g. per-field TX-enable
1156
- * toggles). Returns `false` when the slave is unknown or has never
1157
- * ANNOUNCEd; otherwise resolves once the new bring-up has been kicked
1158
- * off (it does not await the next RUNNING transition — listen via
1159
- * {@link onChange} instead).
860
+ * Re-run FRAME_DEFINE / START so the {@link FramePlanFactory} is consulted
861
+ * again (e.g. after a UI-driven field-tx-enable change). Returns `false`
862
+ * if unknown / never ANNOUNCEd; resolves once bring-up is kicked off, not
863
+ * when RUNNING is reached — listen via {@link onChange}.
1160
864
  */
1161
865
  reprovision(nodeId: number): Promise<boolean>;
1162
866
  }
@@ -1188,7 +892,7 @@ declare class ProtoReader {
1188
892
  constructor(buf: Uint8Array, end?: number);
1189
893
  get done(): boolean;
1190
894
  get position(): number;
1191
- /** Read a base-128 varint as a JS number (overflows ≥ 2^53 wrap silently). */
895
+ /** Read a base-128 varint as a JS number (schema uses uint32 only). */
1192
896
  readVarint(): number;
1193
897
  /** Read a length-delimited byte slice (no copy). */
1194
898
  readBytes(): Uint8Array;
@@ -1196,11 +900,7 @@ declare class ProtoReader {
1196
900
  readString(): string;
1197
901
  /** Read a length-delimited sub-message and return a new reader scoped to it. */
1198
902
  readMessage(): ProtoReader;
1199
- /**
1200
- * Read the next tag and return both the field number and wire type. The
1201
- * caller is expected to dispatch on field number; unknown fields can be
1202
- * passed to {@link skipValue}.
1203
- */
903
+ /** Read a tag as `{field, wire}`. Unknown fields dispatch to {@link skipValue}. */
1204
904
  readTag(): {
1205
905
  field: number;
1206
906
  wire: number;
@@ -1213,75 +913,40 @@ declare class ProtoError extends Error {
1213
913
  }
1214
914
  //#endregion
1215
915
  //#region src/schema-hash.d.ts
1216
- /**
1217
- * SHA-256 helpers for the schema blob (§5.4, §9.3).
1218
- *
1219
- * Both codegen (C++) and the bridge (C++) hash the entire schema blob with
1220
- * SHA-256 and pack the first 8 bytes as little-endian u64 into the ANNOUNCE
1221
- * frame. This module is the master-side equivalent: hash the blob we
1222
- * received over SCHEMA_DATA and compare with the schema_hash the slave
1223
- * advertised in ANNOUNCE.
1224
- *
1225
- * Uses Web Crypto (`crypto.subtle`) — available in modern browsers and Node
1226
- * ≥ 19 / Deno / Bun, no polyfill needed for our target environments.
1227
- */
1228
916
  declare function sha256(bytes: Uint8Array): Promise<Uint8Array>;
1229
- /**
1230
- * Compute the ANNOUNCE-shape schema hash: the first 8 bytes of SHA-256(blob),
1231
- * interpreted as a little-endian u64. Mirrors
1232
- * `fibril_can_core/sha256::hash_prefix_u64`.
1233
- */
917
+ /** First 8 bytes of SHA-256(blob) as little-endian u64 — the ANNOUNCE shape. */
1234
918
  declare function schemaHashPrefix(blob: Uint8Array): Promise<bigint>;
1235
919
  //#endregion
1236
920
  //#region src/segmented-transfer.d.ts
1237
921
  /**
1238
- * Build the §7.4 SVC_REQUEST frame stream for a master→slave call. Mirrors
1239
- * `make_svc_request_frames` in
1240
- * `fibril_can_bridge/include/fibril_can_bridge/svc_request.hpp`.
1241
- *
1242
- * - ≤64 byte payload → one SINGLE frame (seg=0).
1243
- * - Larger → seq=0 carries `[0]=0 [1..2]=total_len + ≤61B data`, subsequent
1244
- * seqs carry `[0]=seq + ≤63B data`. Throws on payloads >1024B.
922
+ * SVC_REQUEST frames for a master→slave call. ≤64 B → one SINGLE frame;
923
+ * larger → seq 0 carries `[0]=0 [1..2]=total + ≤61 B data`, later seqs
924
+ * carry `[0]=seq + ≤63 B data`. Throws on payloads >1024 B.
1245
925
  */
1246
926
  declare function makeSvcRequestFrames(nodeId: number, svcIdx: number, callId: number, payload: Uint8Array): CanFrameTx[];
1247
927
  /**
1248
- * Build §7.4 response segments. Used by mock slaves and any TS slave runtime.
1249
- * `channel` is the channel field (typically `CHAN_SVC_RESPONSE`); the payload
1250
- * is `[status, ...response]` so the caller bakes status into the buffer.
928
+ * Response segments (for mock slaves / TS slave runtime). `payload` must
929
+ * already carry `[status, ...body]`; `channel` is typically CHAN_SVC_RESPONSE.
1251
930
  */
1252
931
  declare function makeSvcSegmentedFrames(channel: number, nodeId: number, svcIdx: number, callId: number, payload: Uint8Array): CanFrameTx[];
1253
- /**
1254
- * Bitmask of segment seqs required to reassemble a `totalLen`-byte payload.
1255
- * Identical formula to `expected_mask` in
1256
- * `fibril_can_bridge/src/node_agent.cpp` and the slave's `seg_expected_mask`.
1257
- */
932
+ /** Bitmask of seg-seqs required to reassemble a `totalLen`-byte payload. */
1258
933
  declare function segExpectedMask(totalLen: number): number;
1259
- /**
1260
- * Per-(svc, call_id) reassembly slot. The caller drives one of these per
1261
- * outstanding incoming segmented transfer.
1262
- */
934
+ /** Per-(svc, call_id) reassembly slot for one segmented transfer. */
1263
935
  declare class SegmentReassembler {
1264
936
  #private;
1265
937
  totalLen: number;
1266
938
  rxBitmap: number;
1267
939
  lastRxMs: number;
1268
940
  /**
1269
- * Ingest one seg=1 frame. `data` is the frame payload (with the seq /
1270
- * total_len header still attached). Returns the fully-assembled payload
1271
- * once every required segment has arrived; otherwise null. Returns null
1272
- * on protocol violations and resets the slot.
941
+ * Ingest one seg=1 frame (payload with seq / total_len header attached).
942
+ * Returns the assembled payload once complete, else null. Resets the slot
943
+ * on protocol violation.
1273
944
  */
1274
945
  ingest(data: Uint8Array, nowMs: number): Uint8Array | null;
1275
946
  }
1276
947
  //#endregion
1277
948
  //#region src/seq-allocator.d.ts
1278
- /**
1279
- * Sequential u8 allocator used for the config-plane `seq` field shared by
1280
- * every command this master has issued to a given slave. Wraps at 256,
1281
- * matching `BridgeNode::alloc_seq` in
1282
- * `fibril_can_bridge/src/bridge_node.cpp` (the bridge wraps on the same
1283
- * boundary; the AckTracker indexes by the wrapped value).
1284
- */
949
+ /** Wrapping u8 counter for the config-plane `seq` byte. */
1285
950
  declare class SeqAllocator {
1286
951
  #private;
1287
952
  next(): number;