@fibril/can-core 0.1.1 → 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.js CHANGED
@@ -67,7 +67,7 @@ var AckTracker = class {
67
67
  * Standard ID (11 bit, §3.1) is dynamically allocated by the master and
68
68
  * has no fixed bitfield meaning — kept here only as the wire mask.
69
69
  */
70
- const PROTOCOL_VERSION = 1;
70
+ const PROTOCOL_VERSION = 2;
71
71
  const MAX_FRAME_PAYLOAD = 64;
72
72
  const CHAN_CONFIG = 0;
73
73
  const CHAN_SVC_REQUEST = 1;
@@ -102,6 +102,18 @@ const STATE_UNPROVISIONED = 0;
102
102
  const STATE_PROVISIONED = 1;
103
103
  const STATE_RUNNING = 2;
104
104
  const STATE_FAULT = 3;
105
+ const STATE_SUSPENDED = 4;
106
+ /** Human-readable name for a node_state byte. Unknown values → `unknown(N)`. */
107
+ function stateName(state) {
108
+ switch (state) {
109
+ case 0: return "UNPROVISIONED";
110
+ case 1: return "PROVISIONED";
111
+ case 2: return "RUNNING";
112
+ case 3: return "FAULT";
113
+ case 4: return "SUSPENDED";
114
+ default: return `unknown(${state})`;
115
+ }
116
+ }
105
117
  const FAULT_NONE = 0;
106
118
  const FAULT_NOMEM = 1;
107
119
  const FAULT_INDEX_OVERFLOW = 2;
@@ -148,11 +160,7 @@ function extSeg(id) {
148
160
  }
149
161
  //#endregion
150
162
  //#region src/dlc.ts
151
- /**
152
- * CAN FD valid payload sizes in ascending order (§4.3).
153
- * Mirrors `fibril_can_core/include/fibril_can_core/packing.hpp::kCanFdDlcSteps`
154
- * and `fibril_can_runtime/src/fcan_wire.c::fcan_quantize_dlc`.
155
- */
163
+ /** Valid CAN FD payload sizes, ascending. */
156
164
  const FD_DLC_STEPS = Object.freeze([
157
165
  0,
158
166
  1,
@@ -171,21 +179,15 @@ const FD_DLC_STEPS = Object.freeze([
171
179
  48,
172
180
  64
173
181
  ]);
174
- /**
175
- * Round `bytes` up to the next valid CAN FD payload step. Returns 64 for any
176
- * input ≥ 64.
177
- */
182
+ /** Round `bytes` up to the next valid CAN FD payload step (caps at 64). */
178
183
  function quantizeDlc(bytes) {
179
184
  if (bytes >= 64) return 64;
180
185
  for (const step of FD_DLC_STEPS) if (step >= bytes) return step;
181
186
  return 64;
182
187
  }
183
188
  /**
184
- * Grow `data` to the next DLC step by appending zero bytes. Returns a new
185
- * Uint8Array. Never shrinks: if `data` already exceeds 64 bytes (which is
186
- * a self-inconsistent frame the slave would misparse), the original is
187
- * returned unchanged so the backend rejects it loudly (parity with
188
- * `pad_to_dlc` in `fibril_can_bridge/src/config_plane_codec.cpp`).
189
+ * Pad `data` to the next DLC step with zeros. Over-length input (>64 B, which
190
+ * the slave would misparse) is returned unchanged so the backend rejects it.
189
191
  */
190
192
  function padToDlc(data) {
191
193
  if (data.length >= 64) return data;
@@ -288,13 +290,9 @@ function encodeStop(nodeId, seq) {
288
290
  return configFrame(nodeId, 33, seq, /* @__PURE__ */ new Uint8Array(0));
289
291
  }
290
292
  /**
291
- * Accumulator for SCHEMA_DATA chunks. The slave DLC-pads each response (§4.3)
292
- * so `chunk.bytes.length` is typically larger than what the master requested;
293
- * the accumulator clips against `expectedChunkLen` and the remaining blob
294
- * length to drop those padding bytes (parity with
295
- * `append_schema_chunk` in `fibril_can_bridge/src/config_plane_codec.cpp`).
296
- *
297
- * Out-of-order chunks are rejected (returns 0 from {@link append}).
293
+ * SCHEMA_DATA reassembly. Clips each chunk against `expectedChunkLen` and
294
+ * the remaining blob length so DLC padding is discarded. Out-of-order
295
+ * chunks are rejected ({@link append} returns 0).
298
296
  */
299
297
  var SchemaAccumulator = class {
300
298
  #buf = [];
@@ -330,12 +328,8 @@ var SchemaAccumulator = class {
330
328
  //#endregion
331
329
  //#region src/scalar-codec.ts
332
330
  /**
333
- * Scalar type enumeration and little-endian wire codecs. Mirrors
334
- * `fibril_can_core/include/fibril_can_core/scalar_type.hpp` and
335
- * `fibril_can_core/src/scalar_type.cpp`.
336
- *
337
- * Type ordinal values are part of the protobuf schema blob's enum, so they
338
- * must not be renumbered.
331
+ * Scalar type enum and little-endian wire codecs. The ordinal values are
332
+ * part of the protobuf schema blob and must not be renumbered.
339
333
  */
340
334
  let ScalarType = /* @__PURE__ */ function(ScalarType) {
341
335
  ScalarType[ScalarType["Bool"] = 0] = "Bool";
@@ -388,14 +382,9 @@ function scalarFromName(name) {
388
382
  return idx >= 0 ? idx : void 0;
389
383
  }
390
384
  /**
391
- * Pack an integer / bool value as little-endian bytes into `out` at `offset`.
392
- * Writes scalarSize(type) bytes — pass a wide-enough `out`.
393
- *
394
- * Pass `bigint` for 64-bit types (U64/I64). `number` is accepted for
395
- * everything (it is internally widened to bigint so signed callers get the
396
- * same sign-extension semantics as the C++ `pack_scalar_int_le(uint64_t)`).
397
- * Signed callers pass the negative number directly; the function performs the
398
- * 64-bit two's-complement cast.
385
+ * Pack an int/bool as little-endian into `out[offset..offset+scalarSize(type))`.
386
+ * `number` is widened to bigint so signed negatives sign-extend to 64-bit
387
+ * two's complement before truncation, matching C++ `pack_scalar_int_le`.
399
388
  */
400
389
  function packScalarIntLe(type, value, out, offset = 0) {
401
390
  const bytes = scalarSize(type);
@@ -451,7 +440,7 @@ var ProtoReader = class ProtoReader {
451
440
  get position() {
452
441
  return this.pos;
453
442
  }
454
- /** Read a base-128 varint as a JS number (overflows ≥ 2^53 wrap silently). */
443
+ /** Read a base-128 varint as a JS number (schema uses uint32 only). */
455
444
  readVarint() {
456
445
  let result = 0;
457
446
  let shift = 0;
@@ -489,11 +478,7 @@ var ProtoReader = class ProtoReader {
489
478
  readMessage() {
490
479
  return new ProtoReader(this.readBytes());
491
480
  }
492
- /**
493
- * Read the next tag and return both the field number and wire type. The
494
- * caller is expected to dispatch on field number; unknown fields can be
495
- * passed to {@link skipValue}.
496
- */
481
+ /** Read a tag as `{field, wire}`. Unknown fields dispatch to {@link skipValue}. */
497
482
  readTag() {
498
483
  const tag = this.readVarint();
499
484
  return {
@@ -531,23 +516,13 @@ var ProtoError = class extends Error {
531
516
  //#endregion
532
517
  //#region src/schema.ts
533
518
  /**
534
- * NodeSchema IR + protobuf decoder for the schema blob (§9).
535
- *
536
- * Mirrors `fibril_can_core/include/fibril_can_core/ir.hpp` and
537
- * `fibril_can_core/src/schema_blob.cpp::parse_schema`. Field IDs come from
538
- * `fibril_can_core/proto/fibril_can.proto`.
539
- *
540
- * Master-side notes:
541
- * - The proto carries `topic_id` / `service_id` / `param_id` / BlockType
542
- * `type_id` for codegen stability; the master derives equivalent indices
543
- * from {@link computeNumbering} instead, so these are accepted but
544
- * discarded on parse.
545
- * - The proto wire field is `tx_disabled` while the IR holds `tx_enabled`
546
- * (inverted, matches the C++ side).
519
+ * NodeSchema IR + protobuf decoder for the schema blob. Master-side notes:
520
+ * - Proto `topic_id` / `service_id` / `param_id` / BlockType `type_id`
521
+ * are accepted but discarded — the master derives indices from
522
+ * {@link computeNumbering}.
523
+ * - Proto wire `tx_disabled` inverts to IR `tx_enabled`.
547
524
  * - `BlockArray.type_id` (proto) → `BlockArray.blockTypeIndex` (IR).
548
- *
549
- * The reader skips unknown fields silently so future schema additions do
550
- * not break old masters.
525
+ * Unknown fields are skipped so future schema additions don't break masters.
551
526
  */
552
527
  const FIELD = {
553
528
  Field: {
@@ -832,13 +807,11 @@ function decodeLimits(r) {
832
807
  return l;
833
808
  }
834
809
  /**
835
- * Parse a schema blob (proto3 binary) into the master-side IR.
836
- *
837
- * Throws {@link ProtoError} on malformed input or on
838
- * `protocolVersion !== 1` (the only version this codec understands —
839
- * mirrors C++ side's UnsupportedProtocolVersion behaviour).
810
+ * Parse a schema blob into the master-side IR. Throws {@link ProtoError}
811
+ * on malformed input or `protocolVersion === 0` (proto3 leaves an unset
812
+ * uint32 at 0, so zero means it was never populated).
840
813
  */
841
- function decodeSchema(blob) {
814
+ function decodeSchemaWithMeta(blob) {
842
815
  const schema = {
843
816
  protocolVersion: 0,
844
817
  nodeName: "",
@@ -868,9 +841,16 @@ function decodeSchema(blob) {
868
841
  default: r.skipValue(wire);
869
842
  }
870
843
  }
871
- if (schema.protocolVersion !== 1) throw new ProtoError(`unsupported schema protocol_version: ${schema.protocolVersion}`);
844
+ if (schema.protocolVersion === 0) throw new ProtoError(`schema protocol_version is 0 (unset)`);
872
845
  for (const arr of schema.instances) if (arr.blockTypeIndex >= schema.types.length) throw new ProtoError(`BlockArray.type_id ${arr.blockTypeIndex} out of range (#types=${schema.types.length})`);
873
- return schema;
846
+ return {
847
+ schema,
848
+ versionAheadOfMaster: schema.protocolVersion > 2
849
+ };
850
+ }
851
+ /** Shim for callers that don't need the metadata — prefer {@link decodeSchemaWithMeta}. */
852
+ function decodeSchema(blob) {
853
+ return decodeSchemaWithMeta(blob).schema;
874
854
  }
875
855
  /** Wire size in bytes for a single field (scalar or fixed array). */
876
856
  function fieldWireSize(field) {
@@ -879,20 +859,8 @@ function fieldWireSize(field) {
879
859
  //#endregion
880
860
  //#region src/dynamic-codec.ts
881
861
  /**
882
- * Field-driven dict ⇄ Uint8Array codec. The runtime equivalent of what
883
- * codegen would otherwise emit per message — encode/decode is a pure
884
- * function of the field list ({@link SchemaField}[]) and the JS dict.
885
- *
886
- * Conventions (matches CLAUDE.md "値の表現規約"):
887
- * - dict keys are the schema field name verbatim
888
- * - scalar ↔ JS `number`, except U64/I64 ↔ `bigint`
889
- * - bool ↔ JS `boolean`
890
- * - fixed-length array (`arrayLen > 0`) ↔ regular JS array
891
- * (`number[]` / `bigint[]` / `boolean[]`) of exactly `arrayLen` elements
892
- *
893
- * Throws on missing / wrong-shape inputs. Decode tolerates extra trailing
894
- * bytes (mirrors C++ side: the master ignores trailing slop so that future
895
- * schema additions don't break older decoders).
862
+ * Field-driven dict ⇄ Uint8Array codec. Throws on shape mismatch; decode
863
+ * tolerates trailing bytes so future schema additions don't break decoders.
896
864
  */
897
865
  var DynamicCodecError = class extends Error {
898
866
  constructor(message) {
@@ -952,11 +920,7 @@ function fieldsWireSize(fields) {
952
920
  for (const f of fields) n += fieldWireSize(f);
953
921
  return n;
954
922
  }
955
- /**
956
- * Encode a dict into the canonical little-endian wire bytes for `fields`.
957
- * Returns a fresh `Uint8Array`. Throws {@link DynamicCodecError} on a
958
- * missing or wrong-shape field.
959
- */
923
+ /** Encode a dict into little-endian wire bytes. Throws on missing / wrong-shape fields. */
960
924
  function encodeFields(fields, value) {
961
925
  const totalSize = fieldsWireSize(fields);
962
926
  const out = new Uint8Array(totalSize);
@@ -983,11 +947,7 @@ function encodeFields(fields, value) {
983
947
  }
984
948
  return out;
985
949
  }
986
- /**
987
- * Decode wire bytes into a dict keyed by field name. Extra trailing bytes
988
- * are ignored. Throws {@link DynamicCodecError} only if the payload is
989
- * shorter than the field list demands.
990
- */
950
+ /** Decode wire bytes into a dict. Trailing bytes are ignored; throws only if too short. */
991
951
  function decodeFields(fields, bytes) {
992
952
  const expected = fieldsWireSize(fields);
993
953
  if (bytes.length < expected) throw new DynamicCodecError(`decodeFields: payload too short — need ${expected} bytes, got ${bytes.length}`);
@@ -1080,10 +1040,7 @@ var DataPlaneReceiver = class {
1080
1040
  this.#now = opts.now ?? (() => Date.now());
1081
1041
  this.#unsubFrame = opts.transport.onFrame((f) => this.#onFrame(f));
1082
1042
  }
1083
- /**
1084
- * Claim a slave's S→M std_ids. Re-attaching the same nodeId atomically
1085
- * detaches the prior plan first (re-provisioning case).
1086
- */
1043
+ /** Claim a slave's S→M std_ids. Re-attach atomically detaches the prior plan. */
1087
1044
  attachSlave(nodeId, framePlan, index) {
1088
1045
  this.detachSlave(nodeId);
1089
1046
  const owned = /* @__PURE__ */ new Set();
@@ -1218,9 +1175,8 @@ var DataPlaneReceiver = class {
1218
1175
  //#endregion
1219
1176
  //#region src/frame-planner.ts
1220
1177
  /**
1221
- * 11-bit Standard ID allocator with a free pool for IDs released after a
1222
- * grace window (§11.4.1). `next()` returns `null` on exhaustion rather
1223
- * than wrapping — wrapping past 0x7FF would alias on the wire.
1178
+ * 11-bit std_id allocator with a delayed free pool. `next()` returns `null`
1179
+ * on exhaustion — wrapping past 0x7FF would alias on the wire.
1224
1180
  */
1225
1181
  var StdIdAllocator = class {
1226
1182
  #next = 256;
@@ -1312,9 +1268,8 @@ function emitOversizedTopic(fields, dir, periodUs, alloc, out) {
1312
1268
  return flush();
1313
1269
  }
1314
1270
  /**
1315
- * Plan a slave's frame layout. Returns `null` if the StdIdAllocator runs
1316
- * out of 11-bit IDs mid-plan — the caller MUST surface that; a partial
1317
- * plan would silently drop fields.
1271
+ * Plan a slave's frame layout. Returns `null` on std_id exhaustion — a
1272
+ * partial plan would silently drop fields.
1318
1273
  */
1319
1274
  function planFrames(requests, alloc) {
1320
1275
  const buckets = /* @__PURE__ */ new Map();
@@ -1402,19 +1357,10 @@ function joinPath$1(ns, name) {
1402
1357
  return `${ns}/${name}`;
1403
1358
  }
1404
1359
  /**
1405
- * Synthesize the default {@link FieldRequest} list from a decoded schema +
1406
- * numbering table — every S2M topic field at its declared period
1407
- * (`field.periodUsOverride` if present, else the owning topic's
1408
- * `defaultPeriodUs`), skipping `txEnabled === false` fields.
1409
- *
1410
- * Pass `filter` to layer a runtime per-field mask on top (UI toggles,
1411
- * per-slave overrides). Schema-disabled fields are unconditionally
1412
- * dropped before `filter` is consulted, so callers don't have to repeat
1413
- * the `txEnabled` check.
1414
- *
1415
- * This is the natural input to {@link planFrames} when the master just
1416
- * wants to "observe everything the slave publishes" — i.e. the
1417
- * sniffer-style auto-provision path.
1360
+ * Every schema-enabled S→M field at its declared period
1361
+ * (`field.periodUsOverride` ?? topic's `defaultPeriodUs`), optionally masked
1362
+ * further by `filter`. Feeds {@link planFrames} for the observe-everything
1363
+ * auto-provision path.
1418
1364
  */
1419
1365
  function buildDefaultS2MRequests(schema, numbering, filter) {
1420
1366
  const out = [];
@@ -1451,17 +1397,10 @@ function buildDefaultS2MRequests(schema, numbering, filter) {
1451
1397
  return out;
1452
1398
  }
1453
1399
  /**
1454
- * One-line FramePlanFactory that subscribes to every S2M field at its
1455
- * declared period using a freshly-allocated {@link StdIdAllocator}.
1456
- *
1457
- * Pass `filter` to apply a runtime per-field mask (e.g. a UI-controlled
1458
- * field-tx-enable map). With no filter, every schema-enabled S→M field is
1459
- * subscribed.
1460
- *
1461
- * Returns `[]` when planning fails (e.g. std_id exhaustion); the caller's
1462
- * NodeAgent will then leave the slave in `provisioning` → `awaiting-announce`
1463
- * with no frames defined, which is the right outcome — partial plans would
1464
- * silently drop fields.
1400
+ * FramePlanFactory subscribing to every schema-enabled S→M field (optionally
1401
+ * masked by `filter`) with a fresh {@link StdIdAllocator}. Returns `[]` on
1402
+ * planning failure so NodeAgent stalls in `awaiting-announce` — a partial
1403
+ * plan would silently drop fields.
1465
1404
  */
1466
1405
  function defaultS2MFramePlan(schema, numbering, filter) {
1467
1406
  const planned = planFrames(buildDefaultS2MRequests(schema, numbering, filter), new StdIdAllocator());
@@ -1512,19 +1451,13 @@ var MasterHeartbeatBroadcaster = class {
1512
1451
  //#endregion
1513
1452
  //#region src/numbering.ts
1514
1453
  /**
1515
- * Flat-numbering tables for topics, services, and parameters (§4.2).
1516
- *
1517
- * Port of `fibril_can_core/src/numbering.cpp`. Numbering is a pure function
1518
- * of (schema, instance-count-vector). The same tables are derived
1519
- * independently by codegen (with max_count for static checks), the slave
1520
- * runtime (with actual counts at boot) and the master (with counts from the
1521
- * ANNOUNCE frame) — keeping this in one place is what guarantees they agree.
1454
+ * Flat-numbering tables for topics, services, and parameters — a pure
1455
+ * function of (schema, instance-count-vector). Codegen, slave runtime, and
1456
+ * master all derive these tables through the same function.
1522
1457
  */
1523
1458
  const SERVICE_INDEX_CANCEL = 255;
1524
1459
  const SERVICE_INDEX_PARAM_SET = 253;
1525
- /** Maximum first-class service_index produced by numbering (inclusive). */
1526
1460
  const SERVICE_INDEX_MAX = 252;
1527
- /** Number of available first-class service slots. */
1528
1461
  const SERVICE_INDEX_CAPACITY = 253;
1529
1462
  let NumberingError = /* @__PURE__ */ function(NumberingError) {
1530
1463
  NumberingError["Ok"] = "ok";
@@ -1564,12 +1497,8 @@ function buildTable(schema, counts, pick) {
1564
1497
  };
1565
1498
  }
1566
1499
  /**
1567
- * Compute the three numbering tables. `actualCounts` must have the same
1568
- * length as `schema.instances`. The result has `status !== Ok` if input is
1569
- * malformed or service_index would overflow into the reserved range
1570
- * (§4.2 §9.4).
1571
- *
1572
- * On error the returned tables are empty but still safe to inspect.
1500
+ * `actualCounts.length` must equal `schema.instances.length`. On any error
1501
+ * the returned tables are empty and `status !== Ok`.
1573
1502
  */
1574
1503
  function computeNumbering(schema, actualCounts) {
1575
1504
  const empty = {
@@ -1614,29 +1543,13 @@ function computeNumbering(schema, actualCounts) {
1614
1543
  }
1615
1544
  //#endregion
1616
1545
  //#region src/schema-hash.ts
1617
- /**
1618
- * SHA-256 helpers for the schema blob (§5.4, §9.3).
1619
- *
1620
- * Both codegen (C++) and the bridge (C++) hash the entire schema blob with
1621
- * SHA-256 and pack the first 8 bytes as little-endian u64 into the ANNOUNCE
1622
- * frame. This module is the master-side equivalent: hash the blob we
1623
- * received over SCHEMA_DATA and compare with the schema_hash the slave
1624
- * advertised in ANNOUNCE.
1625
- *
1626
- * Uses Web Crypto (`crypto.subtle`) — available in modern browsers and Node
1627
- * ≥ 19 / Deno / Bun, no polyfill needed for our target environments.
1628
- */
1629
1546
  async function sha256(bytes) {
1630
1547
  if (typeof crypto === "undefined" || !crypto.subtle) throw new Error("crypto.subtle is not available in this environment");
1631
1548
  const owned = new Uint8Array(bytes);
1632
1549
  const digest = await crypto.subtle.digest("SHA-256", owned);
1633
1550
  return new Uint8Array(digest);
1634
1551
  }
1635
- /**
1636
- * Compute the ANNOUNCE-shape schema hash: the first 8 bytes of SHA-256(blob),
1637
- * interpreted as a little-endian u64. Mirrors
1638
- * `fibril_can_core/sha256::hash_prefix_u64`.
1639
- */
1552
+ /** First 8 bytes of SHA-256(blob) as little-endian u64 — the ANNOUNCE shape. */
1640
1553
  async function schemaHashPrefix(blob) {
1641
1554
  const digest = await sha256(blob);
1642
1555
  let result = 0n;
@@ -1646,18 +1559,9 @@ async function schemaHashPrefix(blob) {
1646
1559
  //#endregion
1647
1560
  //#region src/schema-index.ts
1648
1561
  /**
1649
- * Schema-driven path lookup. Resolves `'<expanded_ns>/<endpoint_name>'`
1650
- * style paths to the flat (topic|service|param) index plus the field IR
1651
- * needed to drive {@link ./dynamic-codec.ts}.
1652
- *
1653
- * Paths are local to a single slave (one {@link NodeAgent}). The block-array
1654
- * `ros_namespace` is expanded by substituting `{i}` with the instance index
1655
- * (mirrors `expand_i_token` in `fibril_can_bridge::node_agent_internal.hpp`).
1656
- *
1657
- * Two endpoints of the same kind sharing a path is a schema error — the
1658
- * index throws on construction. Same path across different kinds (e.g. a
1659
- * topic and a service named `motor0/foo`) is allowed because the caller
1660
- * selects the kind explicitly via `topic()` / `service()` / `param()`.
1562
+ * Path (`<expanded_ns>/<endpoint_name>`) → descriptor lookup for one slave.
1563
+ * Duplicate path within the same kind throws; the same path across kinds
1564
+ * is allowed (caller selects via `topic()` / `service()` / `param()`).
1661
1565
  */
1662
1566
  const I_TOKEN = "{i}";
1663
1567
  function expandIToken(s, value) {
@@ -1674,10 +1578,6 @@ function sumFieldsWireSize(fields) {
1674
1578
  for (const f of fields) n += fieldWireSize(f);
1675
1579
  return n;
1676
1580
  }
1677
- /**
1678
- * Path → descriptor index for one slave. Built once from
1679
- * (schema, numbering) and reused for the lifetime of a snapshot.
1680
- */
1681
1581
  var SchemaIndex = class {
1682
1582
  #topicsByPath = /* @__PURE__ */ new Map();
1683
1583
  #servicesByPath = /* @__PURE__ */ new Map();
@@ -1795,13 +1695,7 @@ var SchemaIndex = class {
1795
1695
  };
1796
1696
  //#endregion
1797
1697
  //#region src/seq-allocator.ts
1798
- /**
1799
- * Sequential u8 allocator used for the config-plane `seq` field shared by
1800
- * every command this master has issued to a given slave. Wraps at 256,
1801
- * matching `BridgeNode::alloc_seq` in
1802
- * `fibril_can_bridge/src/bridge_node.cpp` (the bridge wraps on the same
1803
- * boundary; the AckTracker indexes by the wrapped value).
1804
- */
1698
+ /** Wrapping u8 counter for the config-plane `seq` byte. */
1805
1699
  var SeqAllocator = class {
1806
1700
  #next = 0;
1807
1701
  next() {
@@ -1816,13 +1710,9 @@ var SeqAllocator = class {
1816
1710
  //#endregion
1817
1711
  //#region src/segmented-transfer.ts
1818
1712
  /**
1819
- * Build the §7.4 SVC_REQUEST frame stream for a master→slave call. Mirrors
1820
- * `make_svc_request_frames` in
1821
- * `fibril_can_bridge/include/fibril_can_bridge/svc_request.hpp`.
1822
- *
1823
- * - ≤64 byte payload → one SINGLE frame (seg=0).
1824
- * - Larger → seq=0 carries `[0]=0 [1..2]=total_len + ≤61B data`, subsequent
1825
- * seqs carry `[0]=seq + ≤63B data`. Throws on payloads >1024B.
1713
+ * SVC_REQUEST frames for a master→slave call. ≤64 B → one SINGLE frame;
1714
+ * larger → seq 0 carries `[0]=0 [1..2]=total + ≤61 B data`, later seqs
1715
+ * carry `[0]=seq + ≤63 B data`. Throws on payloads >1024 B.
1826
1716
  */
1827
1717
  function makeSvcRequestFrames(nodeId, svcIdx, callId, payload) {
1828
1718
  if (payload.length > 1024) throw new RangeError(`service request payload ${payload.length} exceeds SEG_MAX_TOTAL (${SEG_MAX_TOTAL})`);
@@ -1863,9 +1753,8 @@ function makeSvcRequestFrames(nodeId, svcIdx, callId, payload) {
1863
1753
  return out;
1864
1754
  }
1865
1755
  /**
1866
- * Build §7.4 response segments. Used by mock slaves and any TS slave runtime.
1867
- * `channel` is the channel field (typically `CHAN_SVC_RESPONSE`); the payload
1868
- * is `[status, ...response]` so the caller bakes status into the buffer.
1756
+ * Response segments (for mock slaves / TS slave runtime). `payload` must
1757
+ * already carry `[status, ...body]`; `channel` is typically CHAN_SVC_RESPONSE.
1869
1758
  */
1870
1759
  function makeSvcSegmentedFrames(channel, nodeId, svcIdx, callId, payload) {
1871
1760
  if (payload.length > 1024) throw new RangeError(`service response payload ${payload.length} exceeds SEG_MAX_TOTAL (${SEG_MAX_TOTAL})`);
@@ -1905,11 +1794,7 @@ function makeSvcSegmentedFrames(channel, nodeId, svcIdx, callId, payload) {
1905
1794
  }
1906
1795
  return out;
1907
1796
  }
1908
- /**
1909
- * Bitmask of segment seqs required to reassemble a `totalLen`-byte payload.
1910
- * Identical formula to `expected_mask` in
1911
- * `fibril_can_bridge/src/node_agent.cpp` and the slave's `seg_expected_mask`.
1912
- */
1797
+ /** Bitmask of seg-seqs required to reassemble a `totalLen`-byte payload. */
1913
1798
  function segExpectedMask(totalLen) {
1914
1799
  if (totalLen <= 0) return 0;
1915
1800
  if (totalLen <= 61) return 1;
@@ -1918,20 +1803,16 @@ function segExpectedMask(totalLen) {
1918
1803
  if (nSegs >= 32) return 4294967295;
1919
1804
  return (1 << nSegs) - 1;
1920
1805
  }
1921
- /**
1922
- * Per-(svc, call_id) reassembly slot. The caller drives one of these per
1923
- * outstanding incoming segmented transfer.
1924
- */
1806
+ /** Per-(svc, call_id) reassembly slot for one segmented transfer. */
1925
1807
  var SegmentReassembler = class {
1926
1808
  totalLen = 0;
1927
1809
  rxBitmap = 0;
1928
1810
  lastRxMs = 0;
1929
1811
  #buffer = /* @__PURE__ */ new Uint8Array(0);
1930
1812
  /**
1931
- * Ingest one seg=1 frame. `data` is the frame payload (with the seq /
1932
- * total_len header still attached). Returns the fully-assembled payload
1933
- * once every required segment has arrived; otherwise null. Returns null
1934
- * on protocol violations and resets the slot.
1813
+ * Ingest one seg=1 frame (payload with seq / total_len header attached).
1814
+ * Returns the assembled payload once complete, else null. Resets the slot
1815
+ * on protocol violation.
1935
1816
  */
1936
1817
  ingest(data, nowMs) {
1937
1818
  if (data.length === 0) return this.#reset();
@@ -2018,13 +1899,8 @@ const REASM_MAX_CONCURRENT = 16;
2018
1899
  /** §7.4 reassembly TTL. */
2019
1900
  const SEG_TIMEOUT_MS = 100;
2020
1901
  /**
2021
- * Single-slave master service-call client. Port of NodeAgent's
2022
- * begin_service_call / on_service_response / on_service_segment flow but
2023
- * Promise-based and AbortSignal-aware.
2024
- *
2025
- * One {@link ServiceClient} attaches to the transport and demuxes incoming
2026
- * SVC_RESPONSE frames for a single `nodeId`. NodeAgent owns one per slave
2027
- * and exposes it on the running snapshot.
1902
+ * Promise-based, AbortSignal-aware service-call client for one slave.
1903
+ * NodeAgent owns one per slave and exposes it on the running snapshot.
2028
1904
  */
2029
1905
  var ServiceClient = class {
2030
1906
  nodeId;
@@ -2189,24 +2065,8 @@ var ServiceClient = class {
2189
2065
  //#endregion
2190
2066
  //#region src/typed-param.ts
2191
2067
  /**
2192
- * Schema-driven typed wrapper around PARAM_SET (SPEC §8.2, reserved
2193
- * service_index 0xFD).
2194
- *
2195
- * Per SPEC §8.1 PARAM is M→S one-way: the master holds the source of truth
2196
- * and re-injects defaults at provisioning time, so this wrapper exposes
2197
- * `.set()` only — there's no `.get()` that queries the slave. The
2198
- * descriptor's decoded default is available via `defaultValue()` for
2199
- * reference / hydration of master-side state.
2200
- *
2201
- * The wire shape for one PARAM_SET request is:
2202
- * [0] count = 1
2203
- * [1..2] u16 param_index (LE)
2204
- * [3..] value (scalar little-endian or fixed-array contiguous)
2205
- *
2206
- * Response is `[0] status [1..2] failed_param_index (when status != OK)`.
2207
- * `set()` resolves with the status code; non-OK includes `failedIndex` so
2208
- * the caller can correlate with a multi-param batch (single-param sets
2209
- * always blame this param).
2068
+ * Typed wrapper around PARAM_SET (service_index 0xFD). PARAM is M→S
2069
+ * one-way, so this exposes `.set()` and `.defaultValue()` only.
2210
2070
  */
2211
2071
  var ParamCodecError = class extends Error {
2212
2072
  constructor(message) {
@@ -2289,11 +2149,7 @@ var TypedParam = class {
2289
2149
  this.descriptor = descriptor;
2290
2150
  this.path = descriptor.path;
2291
2151
  }
2292
- /**
2293
- * Decode the schema-embedded default value (SPEC §9.1 `default`). Useful
2294
- * to seed the master-side persistence store. Returns `null` if the
2295
- * descriptor carries no default payload.
2296
- */
2152
+ /** Decoded schema default; `null` if the descriptor carries no default. */
2297
2153
  defaultValue() {
2298
2154
  if (this.descriptor.defaultValue.length === 0) return null;
2299
2155
  return unpackValue(this.descriptor, this.descriptor.defaultValue);
@@ -2351,15 +2207,10 @@ const DEFAULT_OPTS = {
2351
2207
  heartbeatTimeoutMs: 350
2352
2208
  };
2353
2209
  /**
2354
- * Per-slave provisioning state machine. Mirrors
2355
- * `fibril_can_bridge::NodeAgent` lifecycle entry points but trimmed for the
2356
- * single-threaded TS master — no ROS entity creation, no PARAM_SET
2357
- * re-injection (P5), no plan computation (P6).
2358
- *
2359
- * Lifecycle: `idle` → `awaiting-announce` → `schema-reading` (skipped if
2360
- * blob already cached on this agent instance) → `numbering` →
2361
- * `provisioning` → `running`. HEARTBEAT loss drops to `lost`; the next
2362
- * ANNOUNCE restarts the flow.
2210
+ * Per-slave provisioning state machine.
2211
+ * `idle` → `awaiting-announce` → `schema-reading` (skipped on cache hit) →
2212
+ * `numbering` → `provisioning` → `running`. HEARTBEAT loss drops to `lost`;
2213
+ * the next ANNOUNCE restarts the flow.
2363
2214
  */
2364
2215
  var NodeAgent = class {
2365
2216
  nodeId;
@@ -2420,9 +2271,8 @@ var NodeAgent = class {
2420
2271
  return this.#snapshot;
2421
2272
  }
2422
2273
  /**
2423
- * Attach to the transport and wait for ANNOUNCE. Resolves immediately —
2424
- * progress flows via {@link state} / {@link snapshot} / `onState`. Call
2425
- * `stop()` to detach.
2274
+ * Attach and wait for ANNOUNCE. Resolves immediately; progress flows via
2275
+ * {@link state} / {@link snapshot} / `onState`.
2426
2276
  */
2427
2277
  async start() {
2428
2278
  if (this.#stopped) throw new Error("NodeAgent.start() after stop() is not supported — construct a new agent");
@@ -2433,17 +2283,11 @@ var NodeAgent = class {
2433
2283
  this.#setState({ kind: "awaiting-announce" });
2434
2284
  }
2435
2285
  /**
2436
- * Re-run the FRAME_DEFINE / START handshake against the live slave using
2437
- * the most recent ANNOUNCE + cached schema. The {@link FramePlanFactory}
2438
- * is invoked again — callers that key off an external mutable state
2439
- * (e.g. per-field TX-enable overrides) will see the latest values.
2440
- *
2441
- * The agent must have observed an ANNOUNCE already; `reprovision()`
2442
- * before that throws. RUNNING is required so that the slave is known to
2443
- * be reachable — calling during `lost` would race the heartbeat
2444
- * watchdog. Pending service calls are failed and the current snapshot is
2445
- * torn down before the new bring-up begins, mirroring what happens on a
2446
- * boot_id change.
2286
+ * Re-run FRAME_DEFINE / START against the live slave with the most recent
2287
+ * ANNOUNCE + cached schema. Invokes {@link FramePlanFactory} again so
2288
+ * per-slave overrides pick up their latest values. Requires a prior
2289
+ * ANNOUNCE. Tears down the current snapshot and fails pending service
2290
+ * calls, mirroring a boot_id change.
2447
2291
  */
2448
2292
  async reprovision() {
2449
2293
  if (this.#stopped) throw new Error("NodeAgent.reprovision() after stop()");
@@ -2567,6 +2411,11 @@ var NodeAgent = class {
2567
2411
  if (this.#snapshot !== null) this.#setState({ kind: "running" });
2568
2412
  else if (this.#pendingStart !== null) this.#setState({ kind: "awaiting-start" });
2569
2413
  }
2414
+ if (this.#snapshot !== null) {
2415
+ const isSuspended = h.state === 4;
2416
+ if (isSuspended && this.#state.kind === "running") this.#setState({ kind: "suspended" });
2417
+ else if (!isSuspended && this.#state.kind === "suspended") this.#setState({ kind: "running" });
2418
+ }
2570
2419
  }
2571
2420
  #startSchemaRead(a) {
2572
2421
  const progress = {
@@ -2648,13 +2497,15 @@ var NodeAgent = class {
2648
2497
  return;
2649
2498
  }
2650
2499
  let schema;
2500
+ let versionAheadOfMaster;
2651
2501
  try {
2652
- schema = decodeSchema(blob);
2502
+ ({schema, versionAheadOfMaster} = decodeSchemaWithMeta(blob));
2653
2503
  } catch (e) {
2654
2504
  this.#error(e);
2655
2505
  this.#setState({ kind: "awaiting-announce" });
2656
2506
  return;
2657
2507
  }
2508
+ if (versionAheadOfMaster) this.#error(/* @__PURE__ */ new Error(`node ${this.nodeId}: schema protocol_version=${schema.protocolVersion} exceeds master-known — proceeding with best-effort`));
2658
2509
  this.#blobCache.set(a.schemaHash, {
2659
2510
  blob,
2660
2511
  schema
@@ -2738,10 +2589,8 @@ var NodeAgent = class {
2738
2589
  this.#maybeIssueStart(a, schema, blob, numbering, gen, p);
2739
2590
  }
2740
2591
  /**
2741
- * FRAME_DEFINE has succeeded (or been skipped). Consult the autoStart
2742
- * gate and either fire START immediately or park in `awaiting-start`,
2743
- * remembering enough context that `startNow()` can resume without
2744
- * re-running FRAME_DEFINE.
2592
+ * Fire START if autoStart allows, else park in `awaiting-start` with
2593
+ * enough context that `startNow()` can resume without re-running FRAME_DEFINE.
2745
2594
  */
2746
2595
  #maybeIssueStart(a, schema, blob, numbering, gen, progress) {
2747
2596
  if (this.#autoStart()) {
@@ -2759,10 +2608,8 @@ var NodeAgent = class {
2759
2608
  this.#setState({ kind: "awaiting-start" });
2760
2609
  }
2761
2610
  /**
2762
- * Commit the deferred START when {@link NodeAgentOptions.autoStart}
2763
- * gated the previous bring-up. Only legal in `awaiting-start`; any
2764
- * other state throws so callers spot logic bugs instead of racing
2765
- * with a fresh ANNOUNCE.
2611
+ * Commit the deferred START. Only legal in `awaiting-start`; throws
2612
+ * elsewhere so callers spot logic bugs instead of racing a fresh ANNOUNCE.
2766
2613
  */
2767
2614
  async startNow() {
2768
2615
  if (this.#stopped) throw new Error("NodeAgent.startNow() after stop()");
@@ -2886,19 +2733,12 @@ var MasterController = class {
2886
2733
  this.#onError = opts.onError;
2887
2734
  }
2888
2735
  /**
2889
- * Master-side decoder for S→M topic frames. Auto-attached / detached as
2890
- * each slave transitions in and out of `running`. Subscribe with
2891
- * `dataPlane.onTopic(nodeId, path, listener)`; read latest values with
2892
- * `dataPlane.latest(nodeId, path)`.
2736
+ * S→M topic decoder. Auto-attaches / detaches on each slave's `running`
2737
+ * transition; subscribe via `dataPlane.onTopic` / read via `dataPlane.latest`.
2893
2738
  */
2894
2739
  get dataPlane() {
2895
2740
  return this.#dataPlane;
2896
2741
  }
2897
- /**
2898
- * Attach to the transport, start MASTER_HEARTBEAT, fire one initial
2899
- * broadcast DISCOVER to flush stale slave state from a previous master
2900
- * session. New slaves auto-spawn a NodeAgent on first sight.
2901
- */
2902
2742
  start() {
2903
2743
  if (this.#started) return;
2904
2744
  this.#started = true;
@@ -2939,7 +2779,7 @@ var MasterController = class {
2939
2779
  schemaHash: r.schemaHash
2940
2780
  })).sort((a, b) => a.nodeId - b.nodeId);
2941
2781
  }
2942
- /** Subscribe to slave-set / state-change updates. Fires on every change. */
2782
+ /** Subscribe to slave-set / state-change updates. Fires immediately with the current snapshot, then on every change. */
2943
2783
  onChange(cb) {
2944
2784
  this.#listeners.add(cb);
2945
2785
  cb(this.snapshots());
@@ -2947,24 +2787,18 @@ var MasterController = class {
2947
2787
  this.#listeners.delete(cb);
2948
2788
  };
2949
2789
  }
2950
- /**
2951
- * Access the NodeAgent for one slave, if it exists. Returns null until
2952
- * the slave has been observed. Useful for service / param calls from
2953
- * code that already knows the nodeId.
2954
- */
2790
+ /** NodeAgent for one slave, or null if not yet observed. */
2955
2791
  agent(nodeId) {
2956
2792
  return this.#slaves.get(nodeId)?.agent ?? null;
2957
2793
  }
2958
- /** Issue an ad-hoc broadcast DISCOVER (between periodic firings). */
2794
+ /** Ad-hoc broadcast DISCOVER. */
2959
2795
  async discoverNow() {
2960
2796
  await this.#transport.send(encodeDiscover());
2961
2797
  }
2962
2798
  /**
2963
- * Commit the deferred START on one slave that is parked in
2964
- * `awaiting-start` because the autoStart gate blocked its bring-up.
2965
- * Returns `false` if the slave is unknown or not currently waiting
2966
- * for a manual start; otherwise resolves once START has been queued
2967
- * on the wire (the RUNNING transition arrives later via `onChange`).
2799
+ * Commit the deferred START on a slave parked in `awaiting-start`.
2800
+ * Returns `false` if the slave is unknown or not waiting; the RUNNING
2801
+ * transition arrives later via `onChange`.
2968
2802
  */
2969
2803
  async startNode(nodeId) {
2970
2804
  const rec = this.#slaves.get(nodeId);
@@ -2979,13 +2813,10 @@ var MasterController = class {
2979
2813
  }
2980
2814
  }
2981
2815
  /**
2982
- * Re-run the FRAME_DEFINE / START handshake for one slave so the
2983
- * {@link FramePlanFactory} is consulted again. Use when external state
2984
- * the factory closes over has changed (e.g. per-field TX-enable
2985
- * toggles). Returns `false` when the slave is unknown or has never
2986
- * ANNOUNCEd; otherwise resolves once the new bring-up has been kicked
2987
- * off (it does not await the next RUNNING transition — listen via
2988
- * {@link onChange} instead).
2816
+ * Re-run FRAME_DEFINE / START so the {@link FramePlanFactory} is consulted
2817
+ * again (e.g. after a UI-driven field-tx-enable change). Returns `false`
2818
+ * if unknown / never ANNOUNCEd; resolves once bring-up is kicked off, not
2819
+ * when RUNNING is reached — listen via {@link onChange}.
2989
2820
  */
2990
2821
  async reprovision(nodeId) {
2991
2822
  const rec = this.#slaves.get(nodeId);
@@ -3072,6 +2903,6 @@ var MasterController = class {
3072
2903
  }
3073
2904
  };
3074
2905
  //#endregion
3075
- export { AckTracker, CHAN_CONFIG, CHAN_SVC_REQUEST, CHAN_SVC_RESPONSE, CMD_ACK, CMD_ANNOUNCE, CMD_DISCOVER, CMD_FRAME_DEFINE, CMD_HB_PERIOD, CMD_HEARTBEAT, CMD_MASTER_HEARTBEAT, CMD_NAK, CMD_SCHEMA_DATA, CMD_SCHEMA_READ, CMD_START, CMD_STOP, DIR_RX_M2S, DIR_TX_S2M, DataPlaneReceiver, Direction, DynamicCodecError, FAULT_ID_CONFLICT, FAULT_INDEX_OVERFLOW, FAULT_NOMEM, FAULT_NONE, FD_DLC_STEPS, MAX_FRAME_PAYLOAD, MasterController, MasterHeartbeatBroadcaster, NAK_BAD_LENGTH, NAK_BAD_PARAM, NAK_BAD_STATE, NAK_DIR_MISMATCH, NAK_ID_CONFLICT, NAK_INDEX_RANGE, NAK_NO_RESOURCE, NAK_SIZE_OVERFLOW, NAK_UNKNOWN_CMD, NODE_ID_BROADCAST, NoCallSlotError, NodeAgent, NumberingError, PROTOCOL_VERSION, ParamCodecError, ProtoError, ProtoReader, SCALAR_NAMES, SEG_FIRST_DATA, SEG_MAX_SEQ, SEG_MAX_TOTAL, SEG_REST_DATA, SERVICE_INDEX_CANCEL, SERVICE_INDEX_CAPACITY, SERVICE_INDEX_MAX, SERVICE_INDEX_PARAM_SET, STATE_FAULT, STATE_PROVISIONED, STATE_RUNNING, STATE_UNPROVISIONED, STD_ID_MASK, SVC_ACCEPTED, SVC_APP_ERROR, SVC_BAD_INDEX, SVC_BAD_LENGTH, SVC_BAD_VALUE, SVC_BUSY, SVC_CANCEL, SVC_MAX_FIRST_CLASS, SVC_OK, SVC_PARAM_SET, SVC_UNAVAIL, ScalarType, SchemaAccumulator, SchemaIndex, SegmentReassembler, SeqAllocator, ServiceCallError, ServiceCancelledError, ServiceClient, ServiceTimeoutError, ServiceUnavailableError, StdIdAllocator, TypedParam, TypedService, buildDefaultS2MRequests, computeNumbering, decodeAnnounce, decodeFields, decodeHeartbeat, decodeNak, decodeSchema, decodeSchemaData, defaultS2MFramePlan, encodeDiscover, encodeDiscoverTo, encodeFields, encodeFrameDefine, encodeMasterHeartbeat, encodeParamSetSingle, encodeSchemaRead, encodeStart, encodeStop, extChannel, extIndex, extNodeId, extSeg, extSeq, fieldWireSize, fieldsWireSize, makeExtId, makeSvcRequestFrames, makeSvcSegmentedFrames, packScalarF32Le, packScalarF64Le, packScalarIntLe, padToDlc, planFrames, quantizeDlc, scalarFromName, scalarName, scalarSize, schemaHashPrefix, segExpectedMask, segOffset, sha256, signExtend, unpackScalarF32Le, unpackScalarF64Le, unpackScalarIntLe };
2906
+ export { AckTracker, CHAN_CONFIG, CHAN_SVC_REQUEST, CHAN_SVC_RESPONSE, CMD_ACK, CMD_ANNOUNCE, CMD_DISCOVER, CMD_FRAME_DEFINE, CMD_HB_PERIOD, CMD_HEARTBEAT, CMD_MASTER_HEARTBEAT, CMD_NAK, CMD_SCHEMA_DATA, CMD_SCHEMA_READ, CMD_START, CMD_STOP, DIR_RX_M2S, DIR_TX_S2M, DataPlaneReceiver, Direction, DynamicCodecError, FAULT_ID_CONFLICT, FAULT_INDEX_OVERFLOW, FAULT_NOMEM, FAULT_NONE, FD_DLC_STEPS, MAX_FRAME_PAYLOAD, MasterController, MasterHeartbeatBroadcaster, NAK_BAD_LENGTH, NAK_BAD_PARAM, NAK_BAD_STATE, NAK_DIR_MISMATCH, NAK_ID_CONFLICT, NAK_INDEX_RANGE, NAK_NO_RESOURCE, NAK_SIZE_OVERFLOW, NAK_UNKNOWN_CMD, NODE_ID_BROADCAST, NoCallSlotError, NodeAgent, NumberingError, PROTOCOL_VERSION, ParamCodecError, ProtoError, ProtoReader, SCALAR_NAMES, SEG_FIRST_DATA, SEG_MAX_SEQ, SEG_MAX_TOTAL, SEG_REST_DATA, SERVICE_INDEX_CANCEL, SERVICE_INDEX_CAPACITY, SERVICE_INDEX_MAX, SERVICE_INDEX_PARAM_SET, STATE_FAULT, STATE_PROVISIONED, STATE_RUNNING, STATE_SUSPENDED, STATE_UNPROVISIONED, STD_ID_MASK, SVC_ACCEPTED, SVC_APP_ERROR, SVC_BAD_INDEX, SVC_BAD_LENGTH, SVC_BAD_VALUE, SVC_BUSY, SVC_CANCEL, SVC_MAX_FIRST_CLASS, SVC_OK, SVC_PARAM_SET, SVC_UNAVAIL, ScalarType, SchemaAccumulator, SchemaIndex, SegmentReassembler, SeqAllocator, ServiceCallError, ServiceCancelledError, ServiceClient, ServiceTimeoutError, ServiceUnavailableError, StdIdAllocator, TypedParam, TypedService, buildDefaultS2MRequests, computeNumbering, decodeAnnounce, decodeFields, decodeHeartbeat, decodeNak, decodeSchema, decodeSchemaData, decodeSchemaWithMeta, defaultS2MFramePlan, encodeDiscover, encodeDiscoverTo, encodeFields, encodeFrameDefine, encodeMasterHeartbeat, encodeParamSetSingle, encodeSchemaRead, encodeStart, encodeStop, extChannel, extIndex, extNodeId, extSeg, extSeq, fieldWireSize, fieldsWireSize, makeExtId, makeSvcRequestFrames, makeSvcSegmentedFrames, packScalarF32Le, packScalarF64Le, packScalarIntLe, padToDlc, planFrames, quantizeDlc, scalarFromName, scalarName, scalarSize, schemaHashPrefix, segExpectedMask, segOffset, sha256, signExtend, stateName, unpackScalarF32Le, unpackScalarF64Le, unpackScalarIntLe };
3076
2907
 
3077
2908
  //# sourceMappingURL=index.js.map