DDS-TS 1.0 — Spec Coverage

Source: documentation/specs/dds-ts-1.0/main.tex (zerodds/v1.0, ZeroDDS vendor spec).

Audit item-by-item against the TeX source.

Implementation:

  • crates/lang-rules/ · docs.rs — the TypeScript rules, materialiser and emitter; the runtime under runtime/typescript/src/.

The mechanism is a different one than when this spec was written. Until commit fa2ed8b0a9c this was the crate idl-ts: a codegen per language feeding a descriptor runtime (@zerodds/types with a registry, type guards, DdsAny) that was reflected against at run time. Today a shared walk (crates/lang-rules/src/emit/{encode,decode}.rs) emits reader and writer code directly, and the language file supplies only the language decisions. The generated TypeScript imports Reader, Writer and the framing policies from runtime/typescript/src/.

The descriptor runtime stands again, as the second half beside the codec rather than in its place: runtime/typescript/src/{types,branded,dds_any,registry,equal}.ts carries the Ch8 symbols, wasm.ts and test_backend.ts the Annex C profile, and crates/lang-rules/src/adapter/typescript.rs::ts_descriptors emits the side table, the type guard and the registerType call for every IDL type. The generated code still imports Reader and Writer for the encoding; the descriptor describes the type, it does not encode it.

Module mapping:

Module Spec area
crates/lang-rules/src/rule/typescript.rs Ch7 naming, reserved words, type mapping
crates/lang-rules/src/adapter/typescript.rs Ch7 declaring the generated types; §7.4.1–§7.8.1 descriptor side tables
crates/lang-rules/src/emit/typescript.rs Ch7 rendering; the shared walk’s language decisions
crates/lang-rules/src/default_typescript.rs §7.10 default values
crates/lang-rules/src/emit/{encode,decode}.rs Ch9 wire format — shared across languages
runtime/typescript/src/{reader,writer,endianness,policy,float128,error,index}.ts the XCDR runtime
runtime/typescript/src/{types,branded,dds_any,registry,equal}.ts Ch8 descriptor runtime
runtime/typescript/src/{wasm,test_backend}.ts Annex C WASM bindings
tools/idlc/ Ch6 toolchain

Test run: cargo test -p zerodds-lang-rules typescript and runtime/typescript/run-tests.sh.


Status of Document

Front matter

Spec: Status of Document — informative; explains vendor status and source binding to tsc 5.x.

Repo: —

Tests: —

Status: n/a (informative) — status of the document and reading of the keywords; the only requirement (generated code compiles under tsc --strict with typescript 5.x) is in item Ch2 IDL-Codegen Profile, point 4.


Ch1 Scope

§1.1 Purpose

Spec: Ch1 §Purpose — defines a PSM for IDL 4.2 → TypeScript.

Repo: tools/idlc/, crates/lang-rules/src/{rule,adapter,emit}/typescript.rs

Tests: crates/lang-rules/tests/typescript_codec.rs::a_structure_of_primitives_becomes_a_typescript_encoder, crates/lang-rules/tests/typescript_reader_generates.rs::both_directions, crates/lang-rules/tests/typescript_names_its_runtime.rs::the_backend_names_nothing_the_runtime_does_not_export

Status: n/a (informative) — purpose of the spec.

§1.2 Out of Scope

Spec: Ch1 §Out of Scope — lists non-redefined areas (XCDR, JS runtime, DDS-RPC wire).

Repo: —

Tests: —

Status: n/a (informative) — scope of the spec (it does not redefine XCDR, a JavaScript runtime or the DDS-RPC wire protocol); no requirement on an implementation.


Ch2 Conformance

§2.1 IDL-Codegen Profile

Spec: Ch2 §IDL-Codegen Profile — five normative mandatory items (quoted from TeX §sec:profile-codegen):

  1. Type mapping from Ch7 (ch:type-mapping) for every primitive/ constructed/template type of OMG IDL 4.2.
  2. Annotation mapping from §7.12 (sec:annotation-mapping) for every X-Types-1.3 annotation.
  3. Type descriptor + type-guard function per emitted type (Ch8 + §7.4 struct mapping).
  4. Output is valid TypeScript-≥-5.0 source, compilable with tsc --strict without experimentalDecorators / emitDecoratorMetadata.
  5. Annex-B §B.1 conformance tests passed.

Repo: crates/lang-rules/src/rule/typescript.rs (naming, reserved words), crates/lang-rules/src/adapter/typescript.rs (ts_items for the types, ts_descriptors for the side tables, ts_items_with_descriptors for both in one namespace), crates/lang-rules/src/emit/typescript.rs (rendering), crates/lang-rules/src/generate.rs (the driver path, which writes both halves plus descriptor_import).

Points 1, 2 and 4 hold: the type mapping covers the construct set, the X-Types annotations take effect through the shared walk, and the generated source compiles under tsc --strict without decorator options — measured in crates/lang-rules/tests/typescript_descriptors.rs::the_generated_types_type_check over nine shapes. Point 3 holds: ts_descriptors writes <Type>Type, is<Type> and registerType(<Type>Type) for every type.

Point 5 holds: every one of the sixteen Annex B §B.1 marks has a test, the class-freedom clause of B.1.2 included — pair writes interface P beside function newP(): P and no class.

Tests: crates/lang-rules/tests/typescript_descriptors.rs::the_generated_types_type_check, ::b_1_1_every_primitive_round_trips, ::b_1_2_a_struct_gets_an_interface_a_descriptor_and_a_guard; crates/lang-rules/tests/typescript_codec.rs::a_structure_of_primitives_becomes_a_typescript_encoder, crates/lang-rules/tests/typescript_reader_generates.rs::both_directions, crates/lang-rules/tests/typescript_names_its_runtime.rs::the_backend_names_nothing_the_runtime_does_not_export.

Status: done

§2.2 Descriptor-Runtime Profile

Spec: Ch2 §Descriptor-Runtime Profile — mandatory surface: @zerodds/types library exports DdsTypeDescriptor, DdsMemberDescriptor, DdsTypeRef, ExtensibilityKind, DdsAny, DdsException, Char, WChar, makeChar, makeWChar, plus a reflection API.

Repo: runtime/typescript/src/types.ts (DdsTypeDescriptor, DdsMemberDescriptor, DdsTypeRef, ExtensibilityKind, DescriptorKind, PrimitiveName, DdsAny, DdsException, GuardedTypeOf), runtime/typescript/src/branded.ts (Char, WChar, makeChar, makeWChar, LongDouble, makeLongDouble), runtime/typescript/src/registry.ts (registerType, lookupType, getKey, getTopic, withDefaults, isOneOf), runtime/typescript/src/dds_any.ts (boxAny, unboxAny), runtime/typescript/src/equal.ts (equalKey, and the structural equivalence registerType needs); all re-exported from runtime/typescript/src/index.ts.

The import path is runtime/typescript/src/ rather than @zerodds/types. Ch8 calls the package name vendor-specific in as many words and binds only the symbol names; those are unchanged and unprefixed.

Tests: runtime/typescript/tests/descriptor-runtime.ts — the nine marks "B.2.1", "B.2.2", "B.2.3", "B.2.4", "B.2.5", "B.2.6", "B.2.7", "B.2.8", "B.2.9", driven by runtime/typescript/run-tests.sh.

Status: done

§2.3 Operations Profile

Spec: Ch2 §Operations Profile — mandatory surface: mapping from Ch11 (interface → client/handler pair, ServiceDescriptor), plus operations descriptor types (ServiceDescriptor, OperationDescriptor, OperationParameterDescriptor, AttributeDescriptor, ParameterMode, isOneOf).

Repo: crates/lang-rules/src/emit/interface_typescript.rs::ts_interface_items (a client/handler pair per interface), runtime/typescript/src/operations.ts (ServiceDescriptor, OperationDescriptor, OperationParameterDescriptor, AttributeDescriptor, ParameterMode, hasAggregatedResult)

Tests: crates/lang-rules/tests/interface_bindings.rs::typescript_maps_an_interface_to_a_client_a_handler_and_a_descriptor, crates/lang-rules/tests/typescript_operations_profile.rs::the_generated_operations_profile_type_checks

Status: done

§2.4 WASM-Bindings Profile

Spec: Ch2 §WASM-Bindings Profile — codegen + descriptor runtime + handle types/sample types/DCPS operations from Annex C.

Repo: runtime/typescript/src/wasm.ts (ParticipantHandle, TopicHandle, PublisherHandle, SubscriberHandle, DataWriterHandle, DataReaderHandle, DdsGuid, makeDdsGuid, SampleInfo, Sample, DataAvailableCallback, WasmBackend, DdsBindings, bindWasmBackend), runtime/typescript/src/test_backend.ts (createTestBackend).

The codegen and descriptor-runtime profiles §2.4 sits on are §2.1 and §2.2. The XCDR2 carrier across the boundary is Uint8Array and nothing else: writeSample takes octets, takeSamples returns octets.

Tests: runtime/typescript/tests/wasm-bindings.ts — "C.5.1", "C.5.2", "C.5.3", "C.5.4", driven by runtime/typescript/run-tests.sh.

Status: done


Ch3 Normative References

Spec: Ch3 — table of referenced standards.

Repo: —

None of the handle types, DdsGuid, SampleInfo, Sample, the WasmBackend trait, the DCPS operations or bindWasmBackend remain. The profile sits on the descriptor runtime and went with it.

Tests: —

Status: n/a (informative)


Ch4 Terms and Definitions

Spec: Ch4 — glossary (IDL, codegen, type/service descriptor, ESM).

Repo: —

None of the handle types, DdsGuid, SampleInfo, Sample, the WasmBackend trait, the DCPS operations or bindWasmBackend remain. The profile sits on the descriptor runtime and went with it.

Tests: —

Status: n/a (informative)


Ch5 Symbols and Abbreviations

Spec: Ch5 — abbreviation list.

Repo: —

None of the handle types, DdsGuid, SampleInfo, Sample, the WasmBackend trait, the DCPS operations or bindWasmBackend remain. The profile sits on the descriptor runtime and went with it.

Tests: —

Status: n/a (informative)


Ch6 Overview

§6.1 Mapping Philosophy

Spec: Ch6 §Mapping Philosophy — informative; structural typing, uniform interface, type descriptor, service descriptor.

Repo: —

Tests: —

Status: n/a (informative) — chapter “Overview (Informative)”.

§6.2 Runtime Reflection Strategy

Spec: Ch6 §Runtime Reflection Strategy — informative; side-table rationale with an OMG X-Types cross-reference.

Repo: —

Tests: —

Status: n/a (informative) — chapter “Overview (Informative)”; the side tables are items in Ch7 and Ch8.

§6.3 Toolchain

Spec: Ch6 §Toolchain — tsc ≥ 5.0, ESM output, codegen tools/idlc (the spec names the crate idl-ts, which existed until fa2ed8b0a9c).

Repo: tools/idlc/

Tests: crates/lang-rules/tests/typescript_codec.rs::a_structure_of_primitives_becomes_a_typescript_encoder, crates/lang-rules/tests/typescript_reader_generates.rs::both_directions, crates/lang-rules/tests/typescript_names_its_runtime.rs::the_backend_names_nothing_the_runtime_does_not_export

Status: n/a (informative) — chapter “Overview (Informative)”; the tsc requirement is in item Ch2 IDL-Codegen Profile.

§6.4 Code-Style Toggle

Spec: Ch6 §Code-Style Toggle — informative note on --camelcase.

Repo: —

Tests: —

Status: n/a (informative) — chapter “Overview (Informative)”; the toggle is described in Ch10.


Ch7 Type Mapping (Normative)

§7.1 Module Mapping

Spec: Ch7 §Module Mapping — IDL module M { … } SHALL → export namespace M { … }. Nested with declaration merging. Qualified refs as dotted paths. Optional --module-per-file.

Repo: crates/lang-rules/src/adapter/typescript.rs::{module_chain, module_handle, scope_decls} (module as export namespace, nested along the chain), crates/lang-rules/src/emit/typescript.rs::module_prefix (qualified names as dotted paths)

Tests: crates/lang-rules/tests/typescript_codec.rs::a_structure_of_primitives_becomes_a_typescript_encoder, crates/lang-rules/tests/typescript_reader_generates.rs::both_directions, crates/lang-rules/tests/typescript_names_its_runtime.rs::the_backend_names_nothing_the_runtime_does_not_export

Status: partial — export namespace with nesting and declaration merging and qualified names as a dotted path are implemented; the optional output of one file per top-level module (--module-per-file) is missing, own item.

§7.1 Option --module-per-file

Spec: Ch7 §Module Mapping — “Implementations MAY additionally produce one ECMAScript module file per top-level IDL module under a --module-per-file configuration option; the namespace structure inside each file SHALL remain as specified above.”

Repo: —

Tests: —

Status: open — neither tools/idlc nor crates/lang-rules knows the option.

§7.2 Constant Mapping

Spec: Ch7 §Constant Mapping — const T name = expr; SHALL → export const name: T' = expr';. Const evaluation, BigInt suffix for 64-bit, decimal/hex/octal/binary, cyclic-ref reject.

Repo: crates/lang-rules/src/adapter/typescript.rs::{const_object, declare}, crates/lang-rules/src/emit/typescript.rs::{constant, literal, wide_int}

Tests: crates/lang-rules/tests/typescript_codec.rs::a_structure_of_primitives_becomes_a_typescript_encoder, crates/lang-rules/tests/typescript_reader_generates.rs::both_directions, crates/lang-rules/tests/typescript_names_its_runtime.rs::the_backend_names_nothing_the_runtime_does_not_export

Status: done

§7.3 Primitive Types

Spec: Ch7 §Primitive Types — table with 14 IDL types → TS mapping. char/wchar → Char/WChar (branded), long double → LongDouble, any → DdsAny.

Repo: crates/lang-rules/src/emit/typescript.rs::{scalar, int, unsigned_int, wide_int}, crates/lang-rules/src/adapter/typescript.rs::ts_member_type

Tests: crates/lang-rules/tests/typescript_codec.rs::a_structure_of_primitives_becomes_a_typescript_encoder, crates/lang-rules/tests/typescript_reader_generates.rs::both_directions, crates/lang-rules/tests/typescript_names_its_runtime.rs::the_backend_names_nothing_the_runtime_does_not_export

Status: done

§7.3.1 Bounded Strings

Spec: Ch7 §Bounded Strings — string<N>/wstring<N> SHALL additionally emit an exported constant <Container>_<Field>_BOUND carrying N; a standalone bounded-string typedef emits <Alias>_BOUND.

Repo: crates/lang-rules/src/adapter/typescript.rs::{bound_constants, side_constant}, called from declare (the structure, union and alias arms) and from ts_pending (exception members). No alias is followed: a member declared with a named typedef takes that typedef’s own constant.

Tests: crates/lang-rules/tests/typescript_side_tables.rs::s_7_3_1_a_bounded_string_states_its_bound, ::the_side_tables_and_their_codecs_type_check (case side-strings)

Status: done

§7.3.2 Branded Character Types

Spec: Ch7 §Branded Character Types — Char/WChar as branded string with literal tag __dds_brand: "char"|"wchar"; makeChar (ISO 8859-1 range), makeWChar (code-point counting, surrogate reject).

Repo: crates/lang-rules/src/emit/typescript.rs::code_point

Tests: crates/lang-rules/tests/typescript_codec.rs::a_structure_of_primitives_becomes_a_typescript_encoder, crates/lang-rules/tests/typescript_reader_generates.rs::both_directions, crates/lang-rules/tests/typescript_names_its_runtime.rs::the_backend_names_nothing_the_runtime_does_not_export

Status: done

§7.3.3 Long Double Handling

Spec: Ch7 §long double Handling — long double SHALL → LongDouble { __dds_brand: "long_double"; bytes: Uint8Array(16) }. Default emit, makeLongDouble factory, byte order implementation-defined. Diagnostic DDS-TS-I001.

Repo: crates/lang-rules/src/emit/typescript.rs and runtime/typescript/src/float128.ts

Tests: crates/lang-rules/tests/typescript_codec.rs::a_structure_of_primitives_becomes_a_typescript_encoder, crates/lang-rules/tests/typescript_reader_generates.rs::both_directions, crates/lang-rules/tests/typescript_names_its_runtime.rs::the_backend_names_nothing_the_runtime_does_not_export

Status: done

§7.3.4 any Mapping

Spec: Ch7 §any Mapping — any SHALL → DdsAny { typeId; value }. boxAny/unboxAny helpers. Static-unknowability rule.

Repo: crates/lang-rules/src/adapter/typescript.rs (ANY_BOX; prelude declares DdsAny in the generated file, type_at maps TypeKind::Any onto it, and type_ref_expr writes { kind: "any" }), runtime/typescript/src/types.ts (DdsAny), runtime/typescript/src/dds_any.ts (boxAny, unboxAny).

prelude declares DdsAny rather than importing it — TypeScript is structural, the declaration and the runtime’s are one type, and a generated file without descriptors stays compilable with no import.

Tests: crates/lang-rules/tests/typescript_descriptors.rs::b_1_10_an_any_member_is_a_box_and_never_any_or_unknown, crates/lang-rules/src/adapter/typescript.rs::tests::an_any_member_takes_the_box_and_not_a_typescript_keyword; runtime/typescript/tests/descriptor-runtime.ts (B.2.4, B.2.5).

Status: done

§7.4 Struct Mapping — Uniform Interface

Spec: Ch7 §Struct Mapping §Uniform Interface — IDL struct SHALL → TS interface, not a class. Class promotion explicitly forbidden.

Repo: crates/lang-rules/src/adapter/typescript.rs::{ts_items, declare, pair, FACTORY_PREFIX, factory_path} (struct as interface beside function new<Name>(): <Name>; annotations reach the side table and never the shape of the interface)

Both clauses hold. The shape of the interface depends on no annotation, and no IDL data type produces a TypeScript class: what a value needs to start from comes from a factory function returning an object literal with every required member at its Table 9 value. The read path’s defaulter (crates/lang-rules/src/default_typescript.rs::new_local) calls it through factory_path. An @optional member gets no entry — §7.2.2.4.4.4.8.

Tests: crates/lang-rules/tests/typescript_descriptors.rs::b_1_2_no_class_reaches_the_output_for_any_idl_data_type, ::b_1_11_annotations_change_the_side_table_and_not_the_interface, crates/lang-rules/src/adapter/typescript.rs::tests::a_struct_is_an_interface_and_a_factory_and_never_a_class, crates/lang-rules/tests/typescript_reader_generates.rs::the_generated_reader_type_checks

Status: done

§7.4.1 Type Descriptor Side-Table

Spec: Ch7 §Type Descriptor Side-Table — per struct SHALL emit: <Type>Type: DdsTypeDescriptor<T> constant, is<Type> type guard, registerType(<Type>Type).

Repo: crates/lang-rules/src/adapter/typescript.rs::{ts_descriptors, ts_items_with_descriptors, push_descriptor, descriptor_object, field_object, guard_expr}; crates/lang-rules/src/generate.rs (the driver path, which writes both halves plus descriptor_import)

The guard checks the presence and the type of every required member, does not require an @optional one, and checks neither @unit, @min nor @max. The registration is a module-scope statement (zerodds_lang_cst::typescript::Decl::Statement), after the constant it names.

Tests: crates/lang-rules/tests/typescript_descriptors.rs::b_1_2_a_struct_gets_an_interface_a_descriptor_and_a_guard, ::b_1_11_annotations_change_the_side_table_and_not_the_interface, ::the_generated_types_type_check

Status: done

§7.4.2 Exception Mapping

Spec: Ch7 §Exception Mapping — IDL exception SHALL → TS interface extends DdsException, descriptor with kind: "exception".

Repo: crates/lang-rules/src/adapter/typescript.rs::declare, runtime/typescript/src/error.ts

Tests: crates/lang-rules/tests/typescript_codec.rs::a_structure_of_primitives_becomes_a_typescript_encoder, crates/lang-rules/tests/typescript_reader_generates.rs::both_directions, crates/lang-rules/tests/typescript_names_its_runtime.rs::the_backend_names_nothing_the_runtime_does_not_export

Status: done

§7.5 Union Mapping

Spec: Ch7 §Union Mapping — discriminated union with literal-narrowed discriminator (enum/boolean) resp. number (integer disc); default case via Exclude<…> for enum/boolean, runtime-only for integer; multiple labels per case.

Repo: crates/lang-rules/src/adapter/typescript.rs::{declare, discriminant_type, discriminant_type_of, discriminant_expr, excluding, explicit_labels} and the constant DISCRIMINANT_PROPERTY (= discriminator); crates/lang-rules/src/emit/typescript.rs::{discriminant, case_label} through the seams crates/lang-rules/src/emit/mod.rs::CodecTarget::{discriminant, case_label}.

The property is named discriminator. The default: arm carries Exclude<DiscType, ExplicitLabels>; for an enum or boolean discriminator Exclude really removes the labels, and for an integer discriminator the type stays the number — which is what the paragraph “Compile-time exclusivity is partial” says. An enum discriminator narrows to typeof Tag.TAG_A.

The interop suite’s canonical JSON dialect still spells a discriminant _d; that is crates/wire-probe/src/canonical.rs and not this mapping. The octets written are unchanged (measured, see the closing note).

Tests: crates/lang-rules/tests/typescript_side_tables.rs::s_7_5_a_union_arm_names_its_discriminator, ::s_7_5_an_enumeration_discriminator_narrows_on_the_enumerator, ::the_side_tables_and_their_codecs_type_check (cases side-union-integer, side-enum-union), crates/lang-rules/src/adapter/typescript.rs::tests::{a_union_is_a_discriminated_union_narrowed_on_a_literal, a_union_discriminant_takes_the_discriminators_own_literal_form}

Status: done

§7.5.1 Union Descriptor Side-Table

Spec: Ch7 §Union Descriptor — kind: "union" descriptor with synthetic discriminator field (id: 0xFFFFFFFF), labels property per case member, hasDefault/hasImplicitDefault flags.

Repo: crates/lang-rules/src/adapter/typescript.rs::{type_spec, DISCRIMINATOR_ID, DISCRIMINANT_PROPERTY, exhausts_discriminator}

The synthetic discriminator member comes first and carries id: 4294967295. Its name is the property the arms actually have (discriminator, §7.5), so a consumer reads the side table instead of knowing the name twice. hasImplicitDefault is computed from the coverage for boolean and enum discriminators; an integer discriminator never counts as covered.

Tests: crates/lang-rules/tests/typescript_descriptors.rs::b_1_3_a_union_carries_its_labels_and_its_default_flags, ::b_1_3_a_union_without_a_default_and_without_coverage_says_so, ::b_1_3_an_enum_discriminator_the_cases_exhaust_has_no_implicit_default

Status: done

§7.6 Enum Mapping

Spec: Ch7 §Enum Mapping — IDL enum SHALL → as const object + string-literal-union type alias. No TS enum keyword. @value(N) companion object <Enum>Ordinal.

Repo: crates/lang-rules/src/adapter/typescript.rs::{declare, enum_alias_type, enum_ordinals, ordinal_path, enum_default_literal, enumerator_path}; crates/lang-rules/src/emit/typescript.rs::enumerator_ordinal through the seam crates/lang-rules/src/emit/mod.rs::CodecTarget::enumerator_ordinal.

What is emitted is export const Colour = { RED: "RED", … } as const;, export type Colour = (typeof Colour)[keyof typeof Colour];, export const ColourOrdinal: Readonly<Record<Colour, number>> and export const ColourFromOrdinal: ReadonlyMap<number, Colour>. The ordinal table is emitted without @value(N) too, with the implicit values. @bit_bound(N) stays a descriptor field and does not touch the surface.

The codec reads the value through <Enum>Ordinal, because there is no number in the variable any more. Without that seam enum-sparse-values wrote the octets 70 79 87 — the first code unit of FAIL, OK and WARN — where the ledger holds 65535 1 100.

Tests: crates/lang-rules/tests/typescript_side_tables.rs::s_7_6_an_enumeration_is_a_string_literal_union_with_its_ordinals, ::s_7_6_implicit_ordinals_are_emitted_too, ::the_side_tables_and_their_codecs_type_check (case side-enum), crates/lang-rules/tests/typescript_descriptors.rs::b_1_4_an_enum_gets_a_const_object_and_a_descriptor, crates/lang-rules/src/adapter/typescript.rs::tests::an_enumeration_is_an_as_const_object_beside_a_string_literal_union

Status: done

§7.6.1 Enum Descriptor Side-Table

Spec: Ch7 §Enum Descriptor — kind: "enum" descriptor with bitBound field, members as DdsMemberDescriptor with ordinal default.

Repo: crates/lang-rules/src/adapter/typescript.rs::type_spec (the TypeKind::Enumeration arm)

Every enumerator becomes a DdsMemberDescriptor with type: { kind: "primitive", name: "int32" } — the 32 bits of X-Types 1.3 §7.4.3.5.3 — and its ordinal as default. bitBound carries the effective bit bound, 32 by default.

Tests: crates/lang-rules/tests/typescript_descriptors.rs::b_1_4_an_enum_gets_a_const_object_and_a_descriptor, ::b_1_4_an_explicit_value_reaches_the_descriptor

Status: done

§7.7 Bitset Mapping

Spec: Ch7 §Bitset Mapping — IDL bitset SHALL → TS interface + <Bitset>_<Field>_BITS constants. Width ≤ 32 → number, 33–64 → bigint. Total > 64 fatal error.

Repo: crates/lang-rules/src/adapter/typescript.rs::declare (the TypeKind::Bitset arm, one side_constant per named bitfield) and unsigned_holder for the anchor; the 64-bit limit is refused by the parser. An anonymous bitfield is padding and gets no constant.

Tests: crates/lang-rules/tests/typescript_side_tables.rs::s_7_7_a_bitset_states_the_width_of_every_bitfield, ::the_side_tables_and_their_codecs_type_check (case side-bits)

Status: done

§7.7.1 Bitset Descriptor Side-Table

Spec: Ch7 §Bitset Descriptor — kind: "bitset" descriptor with type: { kind: "bitfield"; width: W } per member.

Repo: crates/lang-rules/src/adapter/typescript.rs::{type_spec, bitfield_ref} (the TypeKind::Bitset arm)

Every bitfield becomes a DdsMemberDescriptor with type: { kind: "bitfield", width: W }; bitBound carries the sum of every width, anonymous padding fields included. The 64-bit limit is the parser’s to refuse.

Tests: crates/lang-rules/tests/typescript_descriptors.rs::b_1_5a_a_bitset_widths_pick_their_holders_and_the_descriptor_totals_them

Status: done

§7.8 Bitmask Mapping

Spec: Ch7 §Bitmask Mapping — IDL bitmask SHALL → as const object with shift expressions. bit_bound ≤ 32 → number + (1 << K) >>> 0; 33–64 → bigint + 1n << Kn. <Bitmask>_BIT_BOUND constant.

Repo: crates/lang-rules/src/adapter/typescript.rs::declare (the TypeKind::Bitmask arm; side_constant from ast.effective_bit_bound), bit_literal for the anchor-typed value, crates/lang-rules/src/emit/typescript.rs::bitmask_value for the codec.

Tests: crates/lang-rules/tests/typescript_side_tables.rs::s_7_8_a_bitmask_states_its_bit_bound, ::the_side_tables_and_their_codecs_type_check (case side-bits), crates/lang-rules/src/adapter/typescript.rs::tests::a_bitmask_writes_its_bit_as_the_number_and_not_as_a_shift

Status: done

§7.8.1 Bitmask Descriptor Side-Table

Spec: Ch7 §Bitmask Descriptor — kind: "bitmask" descriptor.

Repo: crates/lang-rules/src/adapter/typescript.rs::type_spec (the TypeKind::Bitmask arm)

Every bit value becomes a DdsMemberDescriptor with type: { kind: "bitfield", width: 1 } and the bit position as default; @position(P) reaches it because effective_bit_position has already resolved it. bitBound carries the effective bound.

Tests: crates/lang-rules/tests/typescript_descriptors.rs::b_1_5b_a_bitmask_carries_its_positions_and_its_bound, ::b_1_5b_a_wide_bitmask_and_an_explicit_position

Status: done

§7.8.2 Position Annotation

Spec: Ch7 §Position Annotation — @position(P) SHALL override the bit position; conflicting positions parser-reject.

Repo: crates/lang-rules/src/emit/typescript.rs::bitmask_value

Tests: crates/lang-rules/tests/typescript_codec.rs::a_structure_of_primitives_becomes_a_typescript_encoder, crates/lang-rules/tests/typescript_reader_generates.rs::both_directions, crates/lang-rules/tests/typescript_names_its_runtime.rs::the_backend_names_nothing_the_runtime_does_not_export

Status: done

§7.9 Sequence and Array Mapping

Spec: Ch7 §Sequence and Array Mapping — bounded/unbounded → Array<T'>; bounded with <Container>_<Field>_BOUND. Fixed-size arrays with _LENGTH. Multi-dim with _LENGTH_DIM<N>. Element mutability (ReadonlyArray opt-in). Empty sequences round-trip as [].

Repo: crates/lang-rules/src/adapter/typescript.rs::{array_of, type_at} for T[], bound_constants for _BOUND, _LENGTH and _LENGTH_DIM1…_LENGTH_DIM<k> in declaration order; default_expr gives a sequence [] and a fixed-length member Array.from({ length: n }, …).

ReadonlyArray is a MAY of the clause (“An implementation MAY emit ReadonlyArray … when configured with --readonly-collections”) and at the same time a SHALL NOT without that configuration; what is emitted is Array.

Tests: crates/lang-rules/tests/typescript_side_tables.rs::s_7_9_a_bounded_sequence_and_an_array_state_their_sizes, ::the_side_tables_and_their_codecs_type_check (case side-collections), crates/lang-rules/src/adapter/typescript.rs::tests::a_sequence_and_an_array_are_both_typescript_arrays, crates/lang-rules/tests/typescript_codec.rs::a_sequence_counts_and_loops

Status: done

§7.9.1 Maps

Spec: Ch7 §Maps — IDL map<K,V> SHALL → ReadonlyMap<K',V'>. <Container>_<Field>_BOUND for bounded; keyEqualityHazard flag for non-value-equality keys; --mutable-collections flag.

Repo: crates/lang-rules/src/adapter/typescript.rs::type_at (the TypeKind::Map arm → ReadonlyMap<K', V'>), default_expr (new Map()), guard_check_at (instanceof Map), bound_constants for _BOUND, map_key_hazard for keyEqualityHazard; crates/lang-rules/src/emit/typescript.rs::{value_type_of, routine, reading_map_body, entries_of_map, map_of_entries}.

Never a Record<…>: it loses key uniqueness and commits to an enumeration order. The codec still works on the entry sequence XTypes 1.3 §7.4.1.1.4 puts on the wire — a Map has no half-built entry object for the read direction to fill key and value into one after the other — and the conversion sits at the two ends of the generated function. The octets written are unchanged (measured, see the closing note).

--mutable-collections is a MAY of the clause and explicitly non-default; what is emitted is ReadonlyMap.

Tests: crates/lang-rules/tests/typescript_side_tables.rs::s_7_9_1_a_map_is_a_readonly_map_and_states_its_bound, ::the_side_tables_and_their_codecs_type_check (cases side-map, side-map-struct-key, side-map-in-sequence), crates/lang-rules/src/adapter/typescript.rs::tests::a_map_member_is_a_readonly_map_over_a_staged_entry_type, crates/lang-rules/tests/typescript_descriptors.rs::b_1_16_a_bounded_map_carries_its_bound_and_a_struct_key_its_hazard

Status: done

§7.10 Typedef Mapping

Spec: Ch7 §Typedef Mapping — simple/sequence/array aliases, recursive resolution, forward references.

Repo: crates/lang-rules/src/adapter/typescript.rs::{module_chain, module_handle, scope_decls} (module as export namespace, nested along the chain), crates/lang-rules/src/emit/typescript.rs::module_prefix (qualified names as dotted paths)

Tests: crates/lang-rules/tests/typescript_codec.rs::a_structure_of_primitives_becomes_a_typescript_encoder, crates/lang-rules/tests/typescript_reader_generates.rs::both_directions, crates/lang-rules/tests/typescript_names_its_runtime.rs::the_backend_names_nothing_the_runtime_does_not_export

Status: done — TS export type aliases are module-scope- order-free (verified via forward-ref test). Cycle reject is a matter of the IDL front end (zerodds-idl-lower / zerodds-idl-compose, not here).

§7.10.1 Typedef Descriptor Side-Table

Spec: Ch7 §Typedef Descriptor — kind: "alias" descriptor with a single member named value; the type guard delegates to the referenced type’s guard.

Repo: crates/lang-rules/src/adapter/typescript.rs::{type_spec, GuardShape} (the TypeKind::Alias arm; GuardShape::Delegate carries the target’s guard)

Tests: crates/lang-rules/tests/typescript_descriptors.rs::b_1_13_a_typedef_is_an_alias_with_one_member_named_value, ::b_1_13_a_wide_bit_bound_switches_the_alias_to_bigint

Status: done

§7.10.2 @bit_bound on Integer Typedefs

Spec: Ch7 §@bit_bound on Integer Typedefs — N ≤ 32 → number, 33–64 → bigint switch.

Repo: crates/lang-rules/src/adapter/typescript.rs::{module_chain, module_handle, scope_decls} (module as export namespace, nested along the chain), crates/lang-rules/src/emit/typescript.rs::module_prefix (qualified names as dotted paths)

Tests: crates/lang-rules/tests/typescript_codec.rs::a_structure_of_primitives_becomes_a_typescript_encoder, crates/lang-rules/tests/typescript_reader_generates.rs::both_directions, crates/lang-rules/tests/typescript_names_its_runtime.rs::the_backend_names_nothing_the_runtime_does_not_export

Status: done

§7.11 Optional and Default Values

Spec: Ch7 §Optional and Default Values — @optional SHALL → ?: T | undefined; @default(V) SHALL → side-table <Type>_<Field>_DEFAULT constant + descriptor default field.

Repo: crates/lang-rules/src/adapter/typescript.rs::{pair, default_constant, member_field}; crates/lang-rules/src/default_typescript.rs::{explicit_default, member_default}.

An @optional member carries both halves: the ?: suffix and the | undefined arm — under exactOptionalPropertyTypes retry?: number and retry?: number | undefined are different declarations. A @default(V) produces export const <Type>_<Field>_DEFAULT: T' = V; from the same function the defaulter takes the value from, and the default property of the DdsMemberDescriptor beside it.

Tests: crates/lang-rules/tests/typescript_side_tables.rs::s_7_11_a_default_reaches_a_side_table_constant, ::s_7_11_an_optional_member_is_a_question_mark_and_undefined, ::the_side_tables_and_their_codecs_type_check (case side-defaults), crates/lang-rules/src/adapter/typescript.rs::tests::an_optional_member_is_a_question_mark_and_not_a_null

Status: done

§7.12 Annotation Mapping

Spec: Ch7 §Annotation Mapping — table of ~15 annotations (@id, @key, @optional, @default, @final, @appendable, @mutable, @nested, @topic, @must_understand, @unit, @min, @max, @hashid, @autoid, @verbatim, @bit_bound) → descriptor field or TSDoc tag. Resolution order, TSDoc rendering. Strict-annotations mode (--strict-annotations → E004).

Repo: crates/lang-rules/src/adapter/typescript.rs::{module_chain, module_handle, scope_decls} (module as export namespace, nested along the chain), crates/lang-rules/src/emit/typescript.rs::module_prefix (qualified names as dotted paths)

Tests: crates/lang-rules/tests/typescript_codec.rs::a_structure_of_primitives_becomes_a_typescript_encoder, crates/lang-rules/tests/typescript_reader_generates.rs::both_directions, crates/lang-rules/tests/typescript_names_its_runtime.rs::the_backend_names_nothing_the_runtime_does_not_export

Status: done


Ch8 Descriptor Runtime (Normative)

§8.1 Extensibility Kind

Spec: Ch8 §Extensibility Kind — ExtensibilityKind = "final" | "appendable" | "mutable" type.

Repo: runtime/typescript/src/types.ts (ExtensibilityKind)

The three values in lower case, because the descriptor is JSON-roundtrippable and these are its values; they correspond one for one to FINAL, APPENDABLE and MUTABLE of X-Types 1.3 §7.2.2.4.4.

Tests: runtime/typescript/tests/descriptor-runtime.ts (B.2.1 — the type list is a tuple tsc checks), crates/lang-rules/tests/typescript_descriptors.rs::b_1_11_an_unannotated_struct_is_appendable_and_carries_no_topic

Status: done

§8.2 Type References (DdsTypeRef)

Spec: Ch8 §Type References — DdsTypeRef discriminated union with kinds: primitive, bitfield, string, sequence, array, map, ref (by name), any. Plus PrimitiveName type.

Repo: runtime/typescript/src/types.ts (DdsTypeRef, PrimitiveName); crates/lang-rules/src/adapter/typescript.rs::{type_ref_expr, primitive_ref, bitfield_ref, named_ref, primitive_ref_name} (the producing side)

All eight kinds. ref carries the qualified IDL name and not a descriptor reference — that keeps the value graph acyclic and JSON-roundtrippable and is what mutually recursive types need.

PrimitiveName carries one name past the list in §8.2: int8. IDL 4.2 §7.4.1.4.4.2 has int8 alongside uint8, the list names octet — which is uint8 — and nothing at all for the signed one. Additive: every value §8.2 names still means what §8.2 says.

Tests: crates/lang-rules/tests/typescript_descriptors.rs::b_1_1_every_primitive_round_trips, ::b_1_16_a_map_member_is_described_as_a_map, ::b_1_8_char_and_wchar_reach_the_descriptor_as_themselves, ::the_generated_types_type_check; runtime/typescript/tests/descriptor-runtime.ts (B.2.8)

Status: done

§8.3 Type and Member Descriptors

Spec: Ch8 §Type and Member Descriptors — DdsMemberDescriptor, DescriptorKind, DdsTypeDescriptor<T>.

Repo: runtime/typescript/src/types.ts (DdsMemberDescriptor, DescriptorKind, DdsTypeDescriptor)

labels additionally admits bigint: a union U switch (long long) is valid IDL 4.2, its case labels reach past 2^53, and a label list that could not carry one would disagree with the type it describes. Additive, like int8 in §8.2.

Tests: runtime/typescript/tests/descriptor-runtime.ts (B.2.1 through B.2.6), crates/lang-rules/tests/typescript_descriptors.rs::the_generated_types_type_check

Status: done

§8.3.1 Exception Marker

Spec: Ch8 §Exception Marker — DdsException interface with __dds_exception: true.

Repo: runtime/typescript/src/types.ts (DdsException)

A property and not a class: TypeScript’s structural typing has nothing else that separates an exception payload from an ordinary struct at compile time.

Tests: runtime/typescript/tests/descriptor-runtime.ts — "B.2.1" (the export is in the list) and "B.2.9" (NotFound extends DdsException).

Status: done

§8.4 Boxed any (DdsAny)

Spec: Ch8 §Boxed any — DdsAny { typeId; value }.

Repo: runtime/typescript/src/types.ts (DdsAny), runtime/typescript/src/dds_any.ts (boxAny, unboxAny)

Both functions check the descriptor’s type guard — on the way in and on the way out. A box whose typeId does not describe its value is worse than no box, because every consumer downstream trusts it.

Tests: runtime/typescript/tests/descriptor-runtime.ts — "B.2.4" (boxAny) and "B.2.5" (unboxAny).

Status: done

§8.5 Branded Character Helpers

Spec: Ch8 §Branded Character Helpers — Char/WChar aliases + makeChar/makeWChar factories per §7.3.2.

Repo: runtime/typescript/src/branded.ts (Char, WChar, makeChar, makeWChar)

makeChar takes exactly one code unit in U+0000..U+00FF; makeWChar counts code points rather than code units, so it admits the astral plane and rejects unpaired surrogates.

Tests: runtime/typescript/tests/descriptor-runtime.ts — "B.2.7".

Status: done

§8.6 LongDouble

Spec: Ch8 §LongDouble — LongDouble type + makeLongDouble per §7.3.3.

Repo: runtime/typescript/src/branded.ts (LongDouble, makeLongDouble)

The length check is the whole check: a binary128 has no invalid bit pattern, and a factory that inspected the exponent would refuse values IEEE 754 defines. The conversion from a decimal to those sixteen octets is in runtime/typescript/src/float128.ts.

Tests: runtime/typescript/tests/descriptor-runtime.ts (B.2.7), crates/lang-rules/tests/typescript_descriptors.rs::b_1_9_a_long_double_is_never_a_number

Status: done

§8.7 Reflection API

Spec: Ch8 §Reflection API — exports: registerType, lookupType, getKey, getTopic, withDefaults, boxAny, unboxAny, equalKey, isOneOf. Plus GuardedTypeOf helper. Structural equivalence for registerType defined (deep-equal of all fields except typeGuard).

Repo: runtime/typescript/src/registry.ts (registerType, lookupType, getKey, getTopic, withDefaults, isOneOf), runtime/typescript/src/dds_any.ts (boxAny, unboxAny), runtime/typescript/src/equal.ts (equalKey, descriptorsEqual), runtime/typescript/src/types.ts (GuardedTypeOf)

registerType is silent on a structurally equivalent re-registration and throws on a structurally different one; descriptorsEqual compares every field deeply and typeGuard by reference identity. equalKey follows a ref through lookupType and compares member by member, NaN equal to itself and +0 equal to -0; a map, a sequence and an array are not IDL key types and throw rather than answer. isOneOf never throws.

Tests: runtime/typescript/tests/descriptor-runtime.ts — "B.2.1" through "B.2.9"; the reflection itself in "B.2.2" (lookupType) and "B.2.3" (getKey).

Status: done


Ch9 Verbatim Code-Generation Hook (Normative)

§9.1 Annotation Form

Spec: Ch9 §Annotation Form — @verbatim(language, placement, text) annotation; language="ts"|"*" recognition.

Repo: crates/lang-rules/src/verbatim.rs::{language_matches, declaration_slots, file_slots}

Tests: crates/lang-rules/tests/typescript_codec.rs::a_structure_of_primitives_becomes_a_typescript_encoder, crates/lang-rules/tests/typescript_reader_generates.rs::both_directions, crates/lang-rules/tests/typescript_names_its_runtime.rs::the_backend_names_nothing_the_runtime_does_not_export

Status: done

§9.2 Placement Kinds

Spec: Ch9 §Placement Kinds — all 6 PlacementKinds (BEGIN_FILE, BEFORE_DECLARATION, BEGIN_DECLARATION, END_DECLARATION, AFTER_DECLARATION, END_FILE).

Repo: crates/lang-rules/src/verbatim.rs::{declaration_slots, file_slots}

Tests: crates/lang-rules/tests/typescript_codec.rs::a_structure_of_primitives_becomes_a_typescript_encoder, crates/lang-rules/tests/typescript_reader_generates.rs::both_directions, crates/lang-rules/tests/typescript_names_its_runtime.rs::the_backend_names_nothing_the_runtime_does_not_export

Status: done

§9.3 Quoting and Escaping

Spec: Ch9 §Quoting and Escaping — IDL-string-escape resolution before emission.

Repo: crates/lang-rules/src/verbatim.rs::gather

Tests: crates/lang-rules/tests/typescript_codec.rs::a_structure_of_primitives_becomes_a_typescript_encoder, crates/lang-rules/tests/typescript_reader_generates.rs::both_directions, crates/lang-rules/tests/typescript_names_its_runtime.rs::the_backend_names_nothing_the_runtime_does_not_export

Status: done

§9.4 Multi-Line Text + Multiple Annotations

Spec: Ch9 §Multi-Line + Multiple — newlines, source order, no coalescing.

Repo: crates/lang-rules/src/verbatim.rs::gather

Tests: crates/lang-rules/tests/typescript_codec.rs::a_structure_of_primitives_becomes_a_typescript_encoder, crates/lang-rules/tests/typescript_reader_generates.rs::both_directions, crates/lang-rules/tests/typescript_names_its_runtime.rs::the_backend_names_nothing_the_runtime_does_not_export

Status: done


Ch10 Code Style (Informative)

Spec: Ch10 — identifier casing, output-format recommendations, file-layout options.

Repo: crates/lang-rules/src/verbatim.rs::gather

Tests: crates/lang-rules/tests/typescript_codec.rs::a_structure_of_primitives_becomes_a_typescript_encoder, crates/lang-rules/tests/typescript_reader_generates.rs::both_directions, crates/lang-rules/tests/typescript_names_its_runtime.rs::the_backend_names_nothing_the_runtime_does_not_export

Status: n/a (informative)


Ch11 Interface and Operation Mapping (Normative)

§11.1 Mapping Surface

Spec: Ch11 §Mapping Surface — IDL interface I SHALL → IClient + IHandler + IService: ServiceDescriptor. No async keyword on interface method signatures.

Repo: crates/lang-rules/src/emit/interface_typescript.rs::{ts_interface_items, decls_of} — one <I>Client, one <I>Handler and the descriptor per interface

Tests: crates/lang-rules/tests/interface_bindings.rs::typescript_maps_an_interface_to_a_client_a_handler_and_a_descriptor

Status: done

§11.2 Operation Mapping

Spec: Ch11 §Operation Mapping — return → Promise<R>; in/inout → method params; out/inout → result-object properties; oneway → Promise<void>.

Repo: crates/lang-rules/src/emit/interface_typescript.rs::{signatures, operation_signature} — out and inout parameters move into the resolved value of the Promise

Tests: crates/lang-rules/tests/interface_bindings.rs::typescript_aggregates_out_parameters_into_the_resolved_value

Status: done

§11.3 Attribute Mapping

Spec: Ch11 §Attribute Mapping — attribute T name SHALL → get_name(): Promise<T> + set_name(v: T): Promise<void>. readonly attribute → no setter. getraises/setraises.

Repo: crates/lang-rules/src/emit/interface_typescript.rs::attribute_signatures — get_/set_; a readonly attribute gets only the reader

Tests: crates/lang-rules/tests/interface_bindings.rs::typescript_maps_an_attribute_to_a_getter_and_a_setter

Status: done

§11.4 Exception Mapping

Spec: Ch11 §Exception Mapping — raises (E) → reject with Error + cause: E. isOneOf helper.

Repo: crates/lang-rules/src/emit/interface_typescript.rs (lines 395 and 420–424: the raises list per operation and per attribute direction in the descriptor), runtime/typescript/src/operations.ts (isOneOf inspects the caught rejection)

Tests: crates/lang-rules/tests/interface_bindings.rs::typescript_writes_the_service_descriptor_of_eleven_five

Status: done

§11.5 Service Descriptor

Spec: Ch11 §Service Descriptor — ServiceDescriptor<TC,TH>, OperationDescriptor, OperationParameterDescriptor, AttributeDescriptor, ParameterMode types.

Repo: crates/lang-rules/src/emit/interface_typescript.rs::{descriptor, operation_descriptor}, runtime/typescript/src/operations.ts::ServiceDescriptor

Tests: crates/lang-rules/tests/interface_bindings.rs::typescript_writes_the_service_descriptor_of_eleven_five

Status: done

§11.6 Inheritance

Spec: Ch11 §Inheritance — interface B : A SHALL → BClient extends AClient + BHandler extends AHandler + BService.inherits = [AService]. Multi-inheritance allowed.

Repo: crates/lang-rules/src/emit/interface_typescript.rs (lines 141 and 156: extends on the client and the handler; line 313: the descriptor’s inherits array carries every parent in declaration order)

Tests: crates/lang-rules/tests/interface_bindings.rs::typescript_maps_an_interface_to_a_client_a_handler_and_a_descriptor

Status: done

§11.7 Forward Declarations

Spec: Ch11 §Forward Declarations — <interface_forward_dcl> SHALL NOT produce a separate declaration. Orphan → DDS-TS-E002.

Repo: crates/lang-rules/src/emit/interface_typescript.rs — a forward declaration without a definition is a fatal finding carrying the code DDS-TS-E002

Tests: crates/lang-rules/tests/interface_bindings.rs::a_forward_declaration_without_a_definition_is_e002

Status: done


Annex A — IDL Examples (Informative)

Spec: Annex A — three examples (bounded sequence, mutable type, bitset).

Repo: crates/lang-rules/src/emit/interface_typescript.rs — a forward declaration without a definition is a fatal finding carrying the code DDS-TS-E002

Tests: crates/lang-rules/tests/interface_bindings.rs::a_forward_declaration_without_a_definition_is_e002

Status: n/a (informative)


Annex B — Compliance Test Suite (Normative)

§B.1 Codegen-Profile Tests

Spec: Annex B §B.1 — 16 mandatory tests (B.1.1–B.1.16) against codegen output.

Repo: crates/lang-rules/tests/typescript_descriptors.rs — 26 tests named after the marks (b_1_1_… through b_1_16_…), plus the_generated_types_type_check, which puts nine shapes through tsc --strict --noEmit against the real runtime.

All sixteen marks hold. B.1.2 has two halves and both are measured: the side table (<Type>Type, is<Type>, three typed members) and the clause “No class keyword MUST appear in the generator output for any IDL data type” — checked over eleven shapes with the comments stripped, because a comment about the word is not a class.

Tests: crates/lang-rules/tests/typescript_descriptors.rs::b_1_1_every_primitive_round_trips, ::b_1_2_a_struct_gets_an_interface_a_descriptor_and_a_guard, ::b_1_2_no_class_reaches_the_output_for_any_idl_data_type, ::b_1_3_a_union_carries_its_labels_and_its_default_flags, ::b_1_4_an_enum_gets_a_const_object_and_a_descriptor, ::b_1_5a_a_bitset_widths_pick_their_holders_and_the_descriptor_totals_them, ::b_1_5b_a_bitmask_carries_its_positions_and_its_bound, ::b_1_6_a_module_nests_its_declarations_and_its_descriptors, ::b_1_7_free_standing_constants_keep_their_types, ::b_1_8_char_and_wchar_reach_the_descriptor_as_themselves, ::b_1_9_a_long_double_is_never_a_number, ::b_1_10_an_any_member_is_a_box_and_never_any_or_unknown, ::b_1_11_annotations_change_the_side_table_and_not_the_interface, ::b_1_12_an_exception_is_described_as_one, ::b_1_13_a_typedef_is_an_alias_with_one_member_named_value, ::b_1_14_a_mixed_module_produces_every_side_table, ::b_1_15_an_unrecognised_annotation_does_not_stop_the_generator, ::b_1_16_a_map_member_is_described_as_a_map, ::the_generated_types_type_check

Status: done

§B.2 Descriptor-Runtime Tests

Spec: Annex B §B.2 — 9 mandatory tests against the @zerodds/types library surface.

Repo: runtime/typescript/tests/descriptor-runtime.ts — B.2.1 through B.2.9, one block per mark. B.2.1 has two halves: the type list is a tuple tsc checks, and the function list is checked at run time, because a name that is exported and undefined compiles perfectly well.

Tests: runtime/typescript/tests/descriptor-runtime.ts — 9 of 9 marks: "B.2.1", "B.2.2", "B.2.3", "B.2.4", "B.2.5", "B.2.6", "B.2.7", "B.2.8", "B.2.9"; run via runtime/typescript/run-tests.sh.

Status: done

§B.3 Operations-Profile Tests

Spec: Annex B §B.3 — 9 mandatory tests against operations-profile output.

Repo: crates/lang-rules/tests/interface_bindings.rs (the six typescript_* markers) and crates/lang-rules/tests/typescript_operations_profile.rs (tsc --strict over the generated profile code)

Tests: crates/lang-rules/tests/typescript_operations_profile.rs::the_generated_operations_profile_type_checks

Status: done

§B.4 Reference Test-Harness

Spec: Annex B §B.4 — test-harness definition (tsc verify on generated output, Node.js runner for descriptor helpers).

Repo: crates/lang-rules/tests/typescript_descriptors.rs (the tsc half: --noEmit --strict over the generated output), runtime/typescript/run-tests.sh (the Node.js half: the descriptor helpers and the WASM bindings under node).

The spec names the former codegen crate as the place; it is gone and both halves of the harness are not. What §B.4 asks for — Rust tests against the codegen output, tsc --noEmit --strict over it, and the runtime helpers in a Node.js runner — stands in full.

Tests: crates/lang-rules/tests/typescript_descriptors.rs::the_generated_types_type_check, runtime/typescript/tests/descriptor-runtime.ts, runtime/typescript/tests/wasm-bindings.ts

Status: done


Annex C — WASM-Bindings Profile (Normative)

§C.1 Handle and Sample Types

Spec: Annex C §C.1 — branded handle types (Participant/Topic/ Publisher/Subscriber/Writer/Reader with string-literal brands), SampleInfo, Sample, DdsGuid (12+4 byte) with makeDdsGuid factory, DataAvailableCallback.

Repo: runtime/typescript/src/wasm.ts (ParticipantHandle, TopicHandle, PublisherHandle, SubscriberHandle, DataWriterHandle, DataReaderHandle, DdsGuid, makeDdsGuid, SampleInfo, Sample, DataAvailableCallback)

The brands are string literals and not unique symbols: a symbol brand is nominal across module boundaries, and a handle from one vendor’s binding would then not fit another’s parameter. publicationHandle carries the full sixteen octets of the RTPS GUID; truncating to 64 bits would lose the vendor and host components.

Tests: runtime/typescript/tests/wasm-bindings.ts — "C.5.1" checks prefix.length === 12 and sequenceNumber.

Status: done

§C.2 Required Operations

Spec: Annex C §C.2 — DCPS data-path operations (createParticipant/Topic/Publisher/Subscriber/DataWriter/ DataReader/writeSample/takeSamples/setDataAvailableListener).

Repo: runtime/typescript/src/wasm.ts (DdsBindings names all fourteen operations of §C.2.1 through §C.2.4; WasmBackend is the same surface with bare numbers, as a Wasm export table has it; bindWasmBackend joins the two)

What bindWasmBackend enforces on top is not in the type system: handle 0 as the sentinel, liveness, the cascade on delete, borrowed buffers, and listener delivery on the host event loop.

Tests: runtime/typescript/tests/wasm-bindings.ts — "C.5.1", "C.5.2", "C.5.3", "C.5.4".

Status: done

§C.3 Wire-Format Boundary

Spec: Annex C §C.3 — XCDR2 bytes, memory ownership, DdsAny boundary rule. --xcdr-endianness flag.

Repo: runtime/typescript/src/wasm.ts (writeSample and takeSamples take and return Uint8Array; borrowed copies what goes across and takeSamples copies what comes back), tools/idlc/src/main.rs (--xcdr-endianness <BE|LE>, default LE)

No JavaScript object graph crosses the boundary: the payload is the XCDR2 octet sequence and nothing else. A DdsAny is no exception — boxAny on the producing side, the generated serialiser, the octets, the generated deserialiser, unboxAny on the consuming side.

Tests: runtime/typescript/tests/wasm-bindings.ts (C.5.1 checks that an array changed after the write does not reach the reader; C.5.4 the DdsAny path), tools/idlc/src/main.rs::tests::generate_takes_the_dds_ts_spelling_of_the_endianness_flag

Status: done

§C.4 Browser vs Node.js Bindings

Spec: Annex C §C.4 — browser via wasm-bindgen, Node.js via N-API (optional).

Repo: runtime/typescript/src/wasm.ts (WasmBackend, bindWasmBackend)

One surface for both profiles. The browser supplies a .wasm module, Node.js may additionally supply a .node module through N-API; both satisfy WasmBackend, and bindWasmBackend returns the same DdsBindings either way. An application changes its import line and nothing else. The runtime itself depends on nothing from Node: it has to run in a browser, and an @types/node among its dependencies would say otherwise.

Tests: runtime/typescript/tests/wasm-bindings.ts — "C.5.1" binds against a backend that is neither the browser nor the Node module; what is measured is the binding layer, not either module.

Status: done

§C.5 Conformance Tests

Spec: Annex C §C.5 — 4 mandatory round-trip tests.

Repo: runtime/typescript/tests/wasm-bindings.ts (C.5.1 publish/subscribe round trip, C.5.2 listener, C.5.3 resource cleanup, C.5.4 DdsAny round trip), runtime/typescript/src/test_backend.ts (the in-memory data path they run against)

The reference backend is deliberately synchronous: it calls the listener inside writeSample, which is exactly what §C.2.4 forbids a binding from doing — and that is what makes the deferral in bindWasmBackend measurable.

Tests: runtime/typescript/tests/wasm-bindings.ts — 4 of 4: "C.5.1", "C.5.2", "C.5.3", "C.5.4", via runtime/typescript/run-tests.sh.

Status: done

§C.6 Reservation of Future Extensions

Spec: Annex C §C.6 — “The following identifiers are reserved for future revisions of this Profile and SHALL NOT be re-used by Beta implementations for unrelated purposes: createGuardCondition, createWaitSet, createQueryCondition, getInstance.”

Repo: —

Tests: —

Status: open — fulfilled by the code: crates/ts-wasm/ and runtime/typescript/src/ use none of the four identifiers; crates/ts-node/src/dds.ts uses getInstance for the DomainParticipantFactory (same purpose). A test that checks the exports of the WASM profile against the list is missing.


Annex D — Diagnostic Codes (Normative)

§D.1 Diagnostic Code Registry

Spec: Annex D — codes E001 (reserved), E002 (forward-decl orphan), E003 (annotation conflict), E004 (strict-annotations), W001 (reserved), W002 (unknown annotation), W003 (map key hazard), W004 (implicit default), I001 (long double info).

Repo: —

Tests: —

Status: open — the tree carries a stable code set, but a different one: crates/idl-parse/src/diagnostic.rs::DiagnosticCode assigns IDL0001 through IDL0018. The Annex D codes E001-E004, W001-W004 and I001 do not occur in the tree, and no test covers them.


Audit status

57 done / 1 partial / 3 open / 0 blocked / 12 n/a (informative).

Test run: cargo test -p zerodds-lang-rules — 50 test binaries, 1024 tests green, 0 failed; among them --test typescript_side_tables (12), --test typescript_descriptors (26), --test typescript_reader_generates (16), --test typescript_codec (3), --test typescript_names_its_runtime (2) and --test typescript_operations_profile (1). runtime/typescript/run-tests.sh — 61 marks green (16 octet cases, 32 value cases, 9 descriptor-runtime marks, 4 WASM conformance tests), 0 failed.

Wire measurement for the surfaces of §7.5, §7.6 and §7.9.1: 119 cases of the cell matrix/zerodds-typescript/cyclone-xcdr2-le re-run through tools/wireprobe/wireprobe-zerodds-typescript, octets and read-back values held against the recorded artefacts — 244 of 244 comparisons equal, 0 different.

Open items: dds-ts-1.0.open.en.md.

DDS-TS 1.0 — Spec-Coverage

Quelle: documentation/specs/dds-ts-1.0/main.tex (zerodds/v1.0, ZeroDDS Vendor-Spec).

Audit Item-für-Item gegen die TeX-Quelle.

Implementation:

Der Mechanismus ist ein anderer als beim Verfassen dieser Spec. Bis Commit fa2ed8b0a9c gab es dafür die Crate idl-ts: ein Codegen je Sprache, der eine Descriptor-Runtime (@zerodds/types mit Registry, Type-Guards, DdsAny) belieferte, gegen die zur Laufzeit reflektiert wurde. Heute erzeugt ein geteilter Walk (crates/lang-rules/src/emit/{encode,decode}.rs) direkt Reader- und Writer-Code, und die Sprachdatei liefert nur noch die Sprachentscheidungen. Der erzeugte TypeScript importiert Reader, Writer und die Rahmungs-Politiken aus runtime/typescript/src/.

Die Descriptor-Runtime steht wieder, als zweite Hälfte neben dem Codec und nicht an dessen Stelle: runtime/typescript/src/{types,branded,dds_any, registry,equal}.ts trägt die Ch8-Symbole, wasm.ts und test_backend.ts das Annex-C-Profil, und crates/lang-rules/src/adapter/typescript.rs::ts_descriptors erzeugt je IDL-Typ die Seitentabelle, den Type-Guard und den registerType-Aufruf. Der erzeugte Code importiert weiterhin Reader und Writer für die Kodierung; der Deskriptor beschreibt den Typ, er kodiert ihn nicht.

Modul-Mapping:

Modul Spec-Bereich
crates/lang-rules/src/rule/typescript.rs Ch7 Namensgebung, reservierte Wörter, Typabbildung
crates/lang-rules/src/adapter/typescript.rs Ch7 Deklaration der erzeugten Typen; §7.4.1–§7.8.1 Deskriptor-Seitentabellen
crates/lang-rules/src/emit/typescript.rs Ch7 Rendern; Sprachentscheidungen des geteilten Walks
crates/lang-rules/src/default_typescript.rs §7.10 Default-Werte
crates/lang-rules/src/emit/{encode,decode}.rs Ch9 Wire-Format — geteilt über alle Sprachen
runtime/typescript/src/{reader,writer,endianness,policy,float128,error,index}.ts XCDR-Laufzeit
runtime/typescript/src/{types,branded,dds_any,registry,equal}.ts Ch8 Descriptor-Runtime
runtime/typescript/src/{wasm,test_backend}.ts Annex C WASM-Bindungen
tools/idlc/ Ch6 Toolchain

Test-Lauf: cargo test -p zerodds-lang-rules typescript und runtime/typescript/run-tests.sh.


Status of Document

Front-Matter

Spec: Status of Document — informativ; erklärt Vendor-Status und Quellen-Bindung an tsc 5.x.

Repo: —

Tests: —

Status: n/a (informative) — Status des Dokuments und Lesart der Schlüsselwörter; die einzige Anforderung (erzeugter Code übersetzt unter tsc --strict mit typescript 5.x) steht im Item Ch2 IDL-Codegen Profile, Punkt 4.


Ch1 Scope

§1.1 Purpose

Spec: Ch1 §Purpose — definiert PSM für IDL 4.2 → TypeScript.

Repo: tools/idlc/ (Treiber), crates/lang-rules/src/{rule,adapter,emit}/typescript.rs

Tests: —

Status: n/a (informative) — Zweck der Spec.

§1.2 Out of Scope

Spec: Ch1 §Out of Scope — listet nicht-redefinierte Bereiche (XCDR, JS-Runtime, DDS-RPC-Wire).

Repo: —

Tests: —

Status: n/a (informative) — Abgrenzung der Spec (sie definiert XCDR, eine JavaScript-Laufzeit und das DDS-RPC-Protokoll nicht neu); keine Anforderung an eine Implementierung.


Ch2 Conformance

§2.1 IDL-Codegen Profile

Spec: Ch2 §IDL-Codegen Profile — fünf normative Pflicht-Items (zitiert aus TeX §sec:profile-codegen):

  1. Type-Mapping aus Ch7 (ch:type-mapping) für jeden Primitive/ Constructed/Template-Type von OMG IDL 4.2.
  2. Annotation-Mapping aus §7.12 (sec:annotation-mapping) für jede X-Types-1.3-Annotation.
  3. Type Descriptor + Type-Guard-Funktion pro emittierten Typ (Ch8 + §7.4 Struct-Mapping).
  4. Output ist gültiger TypeScript-≥-5.0-Source, kompilierbar mit tsc --strict ohne experimentalDecorators / emitDecoratorMetadata.
  5. Annex-B-§B.1-Conformance-Tests bestanden.

Repo: crates/lang-rules/src/rule/typescript.rs (Namensgebung, reservierte Wörter), crates/lang-rules/src/adapter/typescript.rs (ts_items für die Typen, ts_descriptors für die Seitentabellen, ts_items_with_descriptors für beides in einem Namensraum), crates/lang-rules/src/emit/typescript.rs (Rendern), crates/lang-rules/src/generate.rs (der Treiber-Pfad, der beide Hälften plus descriptor_import schreibt).

Punkte 1, 2 und 4 sind erfüllt: das Type-Mapping deckt den Konstruktvorrat, die X-Types-Annotationen wirken über den geteilten Walk, und der erzeugte Source übersetzt unter tsc --strict ohne Decorator-Optionen — gemessen in crates/lang-rules/tests/typescript_descriptors.rs::the_generated_types_type_check über neun Formen. Punkt 3 ist erfüllt: ts_descriptors schreibt je Typ <Type>Type, is<Type> und registerType(<Type>Type).

Punkt 5 ist erfüllt: alle sechzehn Marken aus Annex B §B.1 haben einen Test. Die Class-Freedom-Klausel aus B.1.2 eingeschlossen — pair schreibt heute interface P neben function newP(): P und keine Klasse.

Tests: crates/lang-rules/tests/typescript_descriptors.rs::the_generated_types_type_check, ::b_1_1_every_primitive_round_trips, ::b_1_2_a_struct_gets_an_interface_a_descriptor_and_a_guard; crates/lang-rules/tests/typescript_codec.rs::a_structure_of_primitives_becomes_a_typescript_encoder, ::a_sequence_counts_and_loops, ::a_wide_integer_is_a_bigint_literal; crates/lang-rules/tests/typescript_reader_generates.rs::a_final_struct_gets_a_reader, ::an_appendable_struct_gets_a_reader, ::a_mutable_struct_gets_a_reader, ::an_optional_member_gets_a_reader; crates/lang-rules/tests/typescript_names_its_runtime.rs::the_backend_names_nothing_the_runtime_does_not_export.

Status: done

§2.2 Descriptor-Runtime Profile

Spec: Ch2 §Descriptor-Runtime Profile — Pflicht-Surface: @zerodds/types-Library exportiert DdsTypeDescriptor, DdsMemberDescriptor, DdsTypeRef, ExtensibilityKind, DdsAny, DdsException, Char, WChar, makeChar, makeWChar, plus Reflection-API.

Repo: runtime/typescript/src/types.ts (DdsTypeDescriptor, DdsMemberDescriptor, DdsTypeRef, ExtensibilityKind, DescriptorKind, PrimitiveName, DdsAny, DdsException, GuardedTypeOf), runtime/typescript/src/branded.ts (Char, WChar, makeChar, makeWChar, LongDouble, makeLongDouble), runtime/typescript/src/registry.ts (registerType, lookupType, getKey, getTopic, withDefaults, isOneOf), runtime/typescript/src/dds_any.ts (boxAny, unboxAny), runtime/typescript/src/equal.ts (equalKey, strukturelle Äquivalenz für registerType); alle über runtime/typescript/src/index.ts exportiert.

Der Import-Pfad ist runtime/typescript/src/ statt @zerodds/types. Ch8 nennt den Paketnamen ausdrücklich vendor-spezifisch und bindet nur die Symbolnamen; die sind unverändert und unpräfixiert.

Tests: runtime/typescript/tests/descriptor-runtime.ts — die neun Marken "B.2.1", "B.2.2", "B.2.3", "B.2.4", "B.2.5", "B.2.6", "B.2.7", "B.2.8", "B.2.9", gefahren über runtime/typescript/run-tests.sh.

Status: done

§2.3 Operations Profile

Spec: Ch2 §Operations Profile — Pflicht-Surface: Mapping aus Ch11 (interface → Client/Handler-Pair, ServiceDescriptor), plus Operations-Descriptor-Types (ServiceDescriptor, OperationDescriptor, OperationParameterDescriptor, AttributeDescriptor, ParameterMode, isOneOf).

Repo: crates/lang-rules/src/emit/interface_typescript.rs::ts_interface_items (Client/Handler-Paar je Interface), runtime/typescript/src/operations.ts (ServiceDescriptor, OperationDescriptor, OperationParameterDescriptor, AttributeDescriptor, ParameterMode, hasAggregatedResult)

Tests: crates/lang-rules/tests/interface_bindings.rs::typescript_maps_an_interface_to_a_client_a_handler_and_a_descriptor, crates/lang-rules/tests/typescript_operations_profile.rs::the_generated_operations_profile_type_checks

Status: done

§2.4 WASM-Bindings Profile

Spec: Ch2 §WASM-Bindings Profile — Codegen+Descriptor-Runtime + Handle-Types/Sample-Types/DCPS-Operations aus Annex C.

Repo: runtime/typescript/src/wasm.ts (ParticipantHandle, TopicHandle, PublisherHandle, SubscriberHandle, DataWriterHandle, DataReaderHandle, DdsGuid, makeDdsGuid, SampleInfo, Sample, DataAvailableCallback, WasmBackend, DdsBindings, bindWasmBackend), runtime/typescript/src/test_backend.ts (createTestBackend).

Die Codegen- und Descriptor-Runtime-Profile, auf denen §2.4 aufsetzt, sind §2.1 und §2.2. Der XCDR2-Träger über die Grenze ist Uint8Array und nichts anderes: writeSample nimmt Oktette, takeSamples gibt Oktette.

Tests: runtime/typescript/tests/wasm-bindings.ts — "C.5.1", "C.5.2", "C.5.3", "C.5.4", gefahren über runtime/typescript/run-tests.sh.

Status: done


Ch3 Normative References

Spec: Ch3 — Tabelle der referenzierten Standards.

Repo: —

Tests: —

Status: n/a (informative)


Ch4 Terms and Definitions

Spec: Ch4 — Glossar (IDL, Codegen, Type/Service Descriptor, ESM).

Repo: —

Tests: —

Status: n/a (informative)


Ch5 Symbols and Abbreviations

Spec: Ch5 — Abkürzungs-Liste.

Repo: —

Tests: —

Status: n/a (informative)


Ch6 Overview

§6.1 Mapping Philosophy

Spec: Ch6 §Mapping Philosophy — informativ; structural typing, uniform interface, Type Descriptor, Service Descriptor.

Repo: —

Tests: —

Status: n/a (informative) — Kapitel „Overview (Informative)“.

§6.2 Runtime Reflection Strategy

Spec: Ch6 §Runtime Reflection Strategy — informativ; Side-Table-Begründung mit OMG-X-Types-Cross-Reference.

Repo: —

Tests: —

Status: n/a (informative) — Kapitel „Overview (Informative)“; die Seitentabellen sind in Ch7 und Ch8 geführt.

§6.3 Toolchain

Spec: Ch6 §Toolchain — tsc ≥ 5.0, ESM-Output, Codegen tools/idlc (die Spec nennt die Crate idl-ts, die es bis fa2ed8b0a9c gab).

Repo: tools/idlc/ — Aufruf, Optionen und Ausgabe des Codegens

Tests: —

Status: n/a (informative) — Kapitel „Overview (Informative)“; die Anforderung an tsc steht im Item Ch2 IDL-Codegen Profile.

§6.4 Code-Style Toggle

Spec: Ch6 §Code-Style Toggle — informativer Hinweis auf --camelcase.

Repo: —

Tests: —

Status: n/a (informative) — Kapitel „Overview (Informative)“; der Schalter ist in Ch10 beschrieben.


Ch7 Type Mapping (Normative)

§7.1 Module Mapping

Spec: Ch7 §Module Mapping — IDL module M { … } SHALL → export namespace M { … }. Nested mit declaration-merging. Qualifizierte Refs als dotted paths. Optional --module-per-file.

Repo: crates/lang-rules/src/adapter/typescript.rs::{module_chain, module_handle, scope_decls} (Modul als export namespace, verschachtelt über die Kette), crates/lang-rules/src/emit/typescript.rs::module_prefix (qualifizierte Namen als Punktpfad)

Tests: crates/lang-rules/tests/typescript_codec.rs::a_structure_of_primitives_becomes_a_typescript_encoder, crates/lang-rules/tests/typescript_reader_generates.rs::both_directions, crates/lang-rules/tests/typescript_names_its_runtime.rs::the_backend_names_nothing_the_runtime_does_not_export

Status: partial — export namespace mit Verschachtelung und Declaration Merging sowie qualifizierte Namen als Punktpfad sind umgesetzt; es fehlt die optionale Ausgabe einer Datei je Modul der obersten Ebene (--module-per-file), eigenes Item.

§7.1 Option --module-per-file

Spec: Ch7 §Module Mapping — “Implementations MAY additionally produce one ECMAScript module file per top-level IDL module under a --module-per-file configuration option; the namespace structure inside each file SHALL remain as specified above.”

Repo: —

Tests: —

Status: open — weder tools/idlc noch crates/lang-rules kennen die Option.

§7.2 Constant Mapping

Spec: Ch7 §Constant Mapping — const T name = expr; SHALL → export const name: T' = expr';. Const-Evaluation, BigInt- Suffix für 64-bit, Decimal/Hex/Octal/Binary, cyclic-ref-reject.

Repo: crates/lang-rules/src/adapter/typescript.rs::{const_object, declare} (Konstanten-Deklaration), crates/lang-rules/src/emit/typescript.rs::{constant, literal, wide_int} (typisiertes Literal, n-Suffix ab 64 Bit)

Tests: crates/lang-rules/tests/typescript_codec.rs::a_structure_of_primitives_becomes_a_typescript_encoder, crates/lang-rules/tests/typescript_reader_generates.rs::both_directions, crates/lang-rules/tests/typescript_names_its_runtime.rs::the_backend_names_nothing_the_runtime_does_not_export

Status: done

§7.3 Primitive Types

Spec: Ch7 §Primitive Types — Tabelle mit 14 IDL-Typen → TS-Mapping. char/wchar → Char/WChar (Branded), long double → LongDouble, any → DdsAny.

Repo: crates/lang-rules/src/emit/typescript.rs::{scalar, int, unsigned_int, wide_int} (Primitive auf number bzw. bigint), crates/lang-rules/src/adapter/typescript.rs::ts_member_type

Tests: crates/lang-rules/tests/typescript_codec.rs::a_structure_of_primitives_becomes_a_typescript_encoder, crates/lang-rules/tests/typescript_reader_generates.rs::both_directions, crates/lang-rules/tests/typescript_names_its_runtime.rs::the_backend_names_nothing_the_runtime_does_not_export

Status: done

§7.3.1 Bounded Strings

Spec: Ch7 §Bounded Strings — string<N>/wstring<N> SHALL zusätzlich eine exportierte Konstante <Container>_<Field>_BOUND mit N emittieren; ein eigenständiger bounded-string-Typedef <Alias>_BOUND.

Repo: crates/lang-rules/src/adapter/typescript.rs::{bound_constants, side_constant}, gerufen in declare (Struktur-, Union- und Alias-Arm) und in ts_pending (Exception-Glieder). Aliase werden nicht verfolgt: ein Glied, das einen benannten Typedef nennt, nimmt dessen Konstante.

Tests: crates/lang-rules/tests/typescript_side_tables.rs::s_7_3_1_a_bounded_string_states_its_bound, ::the_side_tables_and_their_codecs_type_check (Fall side-strings)

Status: done

§7.3.2 Branded Character Types

Spec: Ch7 §Branded Character Types — Char/WChar als branded string mit literal-tag __dds_brand: "char"|"wchar"; makeChar (ISO 8859-1 range), makeWChar (code-point counting, surrogate-reject).

Repo: crates/lang-rules/src/emit/typescript.rs::code_point (Zeichen als Codepunkt, Breite und Rahmung aus der Politik der Laufzeit)

Tests: crates/lang-rules/tests/typescript_codec.rs::a_structure_of_primitives_becomes_a_typescript_encoder, crates/lang-rules/tests/typescript_reader_generates.rs::both_directions, crates/lang-rules/tests/typescript_names_its_runtime.rs::the_backend_names_nothing_the_runtime_does_not_export

Status: done

§7.3.3 Long Double Handling

Spec: Ch7 §long double Handling — long double SHALL → LongDouble { __dds_brand: "long_double"; bytes: Uint8Array(16) }. Default-emit, makeLongDouble-Factory, byte-order implementation-defined. Diagnostic DDS-TS-I001.

Repo: crates/lang-rules/src/emit/typescript.rs (Zeile 145) und runtime/typescript/src/float128.ts (Träger für binary128)

Tests: crates/lang-rules/tests/typescript_codec.rs::a_structure_of_primitives_becomes_a_typescript_encoder, crates/lang-rules/tests/typescript_reader_generates.rs::both_directions, crates/lang-rules/tests/typescript_names_its_runtime.rs::the_backend_names_nothing_the_runtime_does_not_export

Status: done

§7.3.4 any Mapping

Spec: Ch7 §any Mapping — any SHALL → DdsAny { typeId; value }. boxAny/unboxAny-Helpers. Static-unknowability rule.

Repo: crates/lang-rules/src/adapter/typescript.rs (ANY_BOX, prelude deklariert DdsAny im erzeugten File, type_at bildet TypeKind::Any darauf ab, type_ref_expr schreibt { kind: "any" }), runtime/typescript/src/types.ts (DdsAny), runtime/typescript/src/dds_any.ts (boxAny, unboxAny).

prelude deklariert DdsAny statt es zu importieren — TypeScript ist strukturell, die Deklaration und die der Laufzeit sind ein Typ, und ein erzeugtes File ohne Deskriptoren bleibt so ohne Import übersetzbar.

Tests: crates/lang-rules/tests/typescript_descriptors.rs::b_1_10_an_any_member_is_a_box_and_never_any_or_unknown, crates/lang-rules/src/adapter/typescript.rs::tests::an_any_member_takes_the_box_and_not_a_typescript_keyword; runtime/typescript/tests/descriptor-runtime.ts (B.2.4, B.2.5).

Status: done

§7.4 Struct Mapping — Uniform Interface

Spec: Ch7 §Struct Mapping §Uniform Interface — IDL struct SHALL → TS interface, kein class. Class-Promotion explizit verboten.

Repo: crates/lang-rules/src/adapter/typescript.rs::{ts_items, declare, pair, FACTORY_PREFIX, factory_path} (Struct als interface neben function new<Name>(): <Name>; Annotationen wirken auf die Seitentabelle, nie auf die Form des Interface)

Beide Klauseln stehen. Die Form des Interface hängt an keiner Annotation, und für keinen IDL-Datentyp entsteht eine TypeScript-Klasse: Was ein Wert zum Anfangen braucht, liefert eine Fabrik-Funktion, die ein Objektliteral mit jedem Pflicht-Glied auf seinem Tabelle-9-Wert zurückgibt. Der Defaulter des Lesepfads (crates/lang-rules/src/default_typescript.rs::new_local) ruft sie über factory_path. Ein @optional-Glied bekommt keinen Eintrag — §7.2.2.4.4.4.8.

Tests: crates/lang-rules/tests/typescript_descriptors.rs::b_1_2_no_class_reaches_the_output_for_any_idl_data_type, ::b_1_11_annotations_change_the_side_table_and_not_the_interface, crates/lang-rules/src/adapter/typescript.rs::tests::a_struct_is_an_interface_and_a_factory_and_never_a_class, crates/lang-rules/tests/typescript_reader_generates.rs::the_generated_reader_type_checks

Status: done

§7.4.1 Type Descriptor Side-Table

Spec: Ch7 §Type Descriptor Side-Table — pro Struct SHALL emittieren: <Type>Type: DdsTypeDescriptor<T>-Konstante, is<Type>-Type-Guard, registerType(<Type>Type).

Repo: crates/lang-rules/src/adapter/typescript.rs::{ts_descriptors, ts_items_with_descriptors, push_descriptor, descriptor_object, field_object, guard_expr}; crates/lang-rules/src/generate.rs (Treiber-Pfad, schreibt beide Hälften plus descriptor_import)

Der Guard prüft Vorhandensein und Typ jedes Pflicht-Glieds, fordert kein @optional-Glied und prüft @unit, @min, @max nicht. Die Registrierung steht als Anweisung im Modul-Scope (zerodds_lang_cst::typescript::Decl::Statement), hinter der Konstante, die sie nennt.

Tests: crates/lang-rules/tests/typescript_descriptors.rs::b_1_2_a_struct_gets_an_interface_a_descriptor_and_a_guard, ::b_1_11_annotations_change_the_side_table_and_not_the_interface, ::the_generated_types_type_check

Status: done

§7.4.2 Exception Mapping

Spec: Ch7 §Exception Mapping — IDL exception SHALL → TS interface extends DdsException, descriptor mit kind: "exception".

Repo: crates/lang-rules/src/adapter/typescript.rs::declare (Exception als eigener Typ), runtime/typescript/src/error.ts (Fehlerträger der Laufzeit)

Tests: crates/lang-rules/tests/typescript_codec.rs::a_structure_of_primitives_becomes_a_typescript_encoder, crates/lang-rules/tests/typescript_reader_generates.rs::both_directions, crates/lang-rules/tests/typescript_names_its_runtime.rs::the_backend_names_nothing_the_runtime_does_not_export

Status: done

§7.5 Union Mapping

Spec: Ch7 §Union Mapping — discriminated-union mit literal-narrowed discriminator (enum/boolean) bzw. number (integer-disc); default-case via Exclude<…> für enum/boolean, runtime-only für integer; multiple-labels-per-case.

Repo: crates/lang-rules/src/adapter/typescript.rs::{declare, discriminant_type, discriminant_type_of, discriminant_expr, excluding, explicit_labels} und die Konstante DISCRIMINANT_PROPERTY (= discriminator); crates/lang-rules/src/emit/typescript.rs::{discriminant, case_label} über die Nähte crates/lang-rules/src/emit/mod.rs::CodecTarget::{discriminant, case_label}.

Die Eigenschaft heißt discriminator. Der default:-Arm trägt Exclude<DiscType, ExplicitLabels>; bei einem Enum- oder boolean-Diskriminanten nimmt Exclude die Labels wirklich heraus, bei einem Ganzzahl-Diskriminanten bleibt der Typ die Zahl — was der Absatz „Compile-time exclusivity is partial” sagt. Ein Enum-Diskriminant narrowt auf typeof Tag.TAG_A.

Der kanonische JSON-Dialekt der Interop-Suite schreibt einen Diskriminanten weiter _d; das ist crates/wire-probe/src/canonical.rs und nicht dieses Mapping. Die geschriebenen Oktette sind unverändert (gemessen, siehe Abschluss-Bemerkung).

Tests: crates/lang-rules/tests/typescript_side_tables.rs::s_7_5_a_union_arm_names_its_discriminator, ::s_7_5_an_enumeration_discriminator_narrows_on_the_enumerator, ::the_side_tables_and_their_codecs_type_check (Fälle side-union-integer, side-enum-union), crates/lang-rules/src/adapter/typescript.rs::tests::{a_union_is_a_discriminated_union_narrowed_on_a_literal, a_union_discriminant_takes_the_discriminators_own_literal_form}

Status: done

§7.5.1 Union Descriptor Side-Table

Spec: Ch7 §Union Descriptor — kind: "union" Descriptor mit synthetic discriminator-field (id: 0xFFFFFFFF), labels-Property pro case-member, hasDefault/hasImplicitDefault-Flags.

Repo: crates/lang-rules/src/adapter/typescript.rs::{type_spec, DISCRIMINATOR_ID, DISCRIMINANT_PROPERTY, exhausts_discriminator}

Das synthetische Diskriminanten-Glied steht als erstes und trägt id: 4294967295. Sein name ist die Eigenschaft, die die Arme tatsächlich tragen (discriminator, §7.5), damit ein Konsument die Seitentabelle liest statt den Namen zweimal zu wissen. hasImplicitDefault wird für boolean- und Enum-Diskriminanten aus der Abdeckung berechnet; ein Ganzzahl-Diskriminant gilt nie als abgedeckt.

Tests: crates/lang-rules/tests/typescript_descriptors.rs::b_1_3_a_union_carries_its_labels_and_its_default_flags, ::b_1_3_a_union_without_a_default_and_without_coverage_says_so, ::b_1_3_an_enum_discriminator_the_cases_exhaust_has_no_implicit_default

Status: done

§7.6 Enum Mapping

Spec: Ch7 §Enum Mapping — IDL enum SHALL → as const-Object + string-literal-union type alias. Kein TS-enum keyword. @value(N)-Companion-Object <Enum>Ordinal.

Repo: crates/lang-rules/src/adapter/typescript.rs::{declare, enum_alias_type, enum_ordinals, ordinal_path, enum_default_literal, enumerator_path}; crates/lang-rules/src/emit/typescript.rs::enumerator_ordinal über die Naht crates/lang-rules/src/emit/mod.rs::CodecTarget::enumerator_ordinal.

Erzeugt werden export const Colour = { RED: "RED", … } as const;, export type Colour = (typeof Colour)[keyof typeof Colour];, export const ColourOrdinal: Readonly<Record<Colour, number>> und export const ColourFromOrdinal: ReadonlyMap<number, Colour>. Die Ordinal-Tabelle wird auch ohne @value(N) emittiert, mit den impliziten Werten. @bit_bound(N) bleibt Descriptor-Feld und berührt die Oberfläche nicht.

Der Codec liest den Wert durch <Enum>Ordinal, weil in der Variablen keine Zahl mehr steht. Ohne diese Naht schrieb enum-sparse-values die Oktette 70 79 87 — die erste Codeeinheit von FAIL, OK, WARN — wo das Ledger 65535 1 100 hält.

Tests: crates/lang-rules/tests/typescript_side_tables.rs::s_7_6_an_enumeration_is_a_string_literal_union_with_its_ordinals, ::s_7_6_implicit_ordinals_are_emitted_too, ::the_side_tables_and_their_codecs_type_check (Fall side-enum), crates/lang-rules/tests/typescript_descriptors.rs::b_1_4_an_enum_gets_a_const_object_and_a_descriptor, crates/lang-rules/src/adapter/typescript.rs::tests::an_enumeration_is_an_as_const_object_beside_a_string_literal_union

Status: done

§7.6.1 Enum Descriptor Side-Table

Spec: Ch7 §Enum Descriptor — kind: "enum" Descriptor mit bitBound-Field, Member als DdsMemberDescriptor mit ordinal-Default.

Repo: crates/lang-rules/src/adapter/typescript.rs::type_spec (Arm TypeKind::Enumeration)

Jeder Enumerator wird ein DdsMemberDescriptor mit type: { kind: "primitive", name: "int32" } — die 32 Bit aus XTypes 1.3 §7.4.3.5.3 — und seinem Ordinalwert als default. bitBound trägt den effektiven Bit-Bound, Vorgabe 32.

Tests: crates/lang-rules/tests/typescript_descriptors.rs::b_1_4_an_enum_gets_a_const_object_and_a_descriptor, ::b_1_4_an_explicit_value_reaches_the_descriptor

Status: done

§7.7 Bitset Mapping

Spec: Ch7 §Bitset Mapping — IDL bitset SHALL → TS interface + <Bitset>_<Field>_BITS-Constants. Width ≤ 32 → number, 33–64 → bigint. Total > 64 fatal-error.

Repo: crates/lang-rules/src/adapter/typescript.rs::declare (Arm TypeKind::Bitset, ein side_constant je benanntem Bitfeld) und unsigned_holder für den Anker; die 64-Bit-Grenze weist der Parser ab. Ein anonymes Bitfeld ist Padding und bekommt keine Konstante.

Tests: crates/lang-rules/tests/typescript_side_tables.rs::s_7_7_a_bitset_states_the_width_of_every_bitfield, ::the_side_tables_and_their_codecs_type_check (Fall side-bits)

Status: done

§7.7.1 Bitset Descriptor Side-Table

Spec: Ch7 §Bitset Descriptor — kind: "bitset" Descriptor mit type: { kind: "bitfield"; width: W } pro Member.

Repo: crates/lang-rules/src/adapter/typescript.rs::{type_spec, bitfield_ref} (Arm TypeKind::Bitset)

Jedes Bitfeld wird ein DdsMemberDescriptor mit type: { kind: "bitfield", width: W }; bitBound trägt die Summe aller Breiten, anonyme Padding-Felder eingerechnet. Die 64-Bit-Grenze weist der Parser ab.

Tests: crates/lang-rules/tests/typescript_descriptors.rs::b_1_5a_a_bitset_widths_pick_their_holders_and_the_descriptor_totals_them

Status: done

§7.8 Bitmask Mapping

Spec: Ch7 §Bitmask Mapping — IDL bitmask SHALL → as const- Object mit shift-Expressions. bit_bound ≤ 32 → number + (1 << K) >>> 0; 33–64 → bigint + 1n << Kn. <Bitmask>_BIT_BOUND-Constant.

Repo: crates/lang-rules/src/adapter/typescript.rs::declare (Arm TypeKind::Bitmask; side_constant mit ast.effective_bit_bound), bit_literal für den Wert des Ankertyps, crates/lang-rules/src/emit/typescript.rs::bitmask_value für den Codec.

Tests: crates/lang-rules/tests/typescript_side_tables.rs::s_7_8_a_bitmask_states_its_bit_bound, ::the_side_tables_and_their_codecs_type_check (Fall side-bits), crates/lang-rules/src/adapter/typescript.rs::tests::a_bitmask_writes_its_bit_as_the_number_and_not_as_a_shift

Status: done

§7.8.1 Bitmask Descriptor Side-Table

Spec: Ch7 §Bitmask Descriptor — kind: "bitmask" Descriptor.

Repo: crates/lang-rules/src/adapter/typescript.rs::type_spec (Arm TypeKind::Bitmask)

Jeder Bit-Wert wird ein DdsMemberDescriptor mit type: { kind: "bitfield", width: 1 } und der Bit-Position als default; @position(P) schlägt dabei durch, weil effective_bit_position sie bereits aufgelöst hat. bitBound trägt den effektiven Bound.

Tests: crates/lang-rules/tests/typescript_descriptors.rs::b_1_5b_a_bitmask_carries_its_positions_and_its_bound, ::b_1_5b_a_wide_bitmask_and_an_explicit_position

Status: done

§7.8.2 Position Annotation

Spec: Ch7 §Position Annotation — @position(P) SHALL bit position überschreiben; conflicting positions parser-reject.

Repo: crates/lang-rules/src/emit/typescript.rs::bitmask_value (@position bestimmt das gesetzte Bit)

Tests: crates/lang-rules/tests/typescript_codec.rs::a_wide_integer_is_a_bigint_literal

Status: done

§7.9 Sequence and Array Mapping

Spec: Ch7 §Sequence and Array Mapping — bounded/unbounded → Array<T'>; Bounded mit <Container>_<Field>_BOUND. Fixed-size arrays mit _LENGTH. Multi-Dim mit _LENGTH_DIM<N>. Element-Mutability (ReadonlyArray opt-in). Empty-Sequences round-trip als [].

Repo: crates/lang-rules/src/adapter/typescript.rs::{array_of, type_at} für T[], bound_constants für _BOUND, _LENGTH und _LENGTH_DIM1…_LENGTH_DIM<k> in Deklarationsreihenfolge; default_expr gibt einer Folge [] und einem Feld fester Länge Array.from({ length: n }, …).

ReadonlyArray ist ein MAY der Klausel („An implementation MAY emit ReadonlyArray … when configured with --readonly-collections“) und zugleich ein SHALL NOT ohne diese Konfiguration; emittiert wird Array.

Tests: crates/lang-rules/tests/typescript_side_tables.rs::s_7_9_a_bounded_sequence_and_an_array_state_their_sizes, ::the_side_tables_and_their_codecs_type_check (Fall side-collections), crates/lang-rules/src/adapter/typescript.rs::tests::a_sequence_and_an_array_are_both_typescript_arrays, crates/lang-rules/tests/typescript_codec.rs::a_sequence_counts_and_loops

Status: done

§7.9.1 Maps

Spec: Ch7 §Maps — IDL map<K,V> SHALL → ReadonlyMap<K',V'>. <Container>_<Field>_BOUND für bounded; keyEqualityHazard-Flag für non-value-equality keys; --mutable-collections-Flag.

Repo: crates/lang-rules/src/adapter/typescript.rs::type_at (Arm TypeKind::Map → ReadonlyMap<K', V'>), default_expr (new Map()), guard_check_at (instanceof Map), bound_constants für _BOUND, map_key_hazard für keyEqualityHazard; crates/lang-rules/src/emit/typescript.rs::{value_type_of, routine, reading_map_body, entries_of_map, map_of_entries}.

Nie ein Record<…>: das verliert die Schlüssel-Eindeutigkeit und legt eine Aufzählungsreihenfolge fest. Der Codec arbeitet weiter auf der Eintragsfolge, die XTypes 1.3 §7.4.1.1.4 auf die Leitung legt — ein Map hat kein halb gefülltes Eintragsobjekt, in das die Leserichtung Schlüssel und Wert nacheinander schreiben könnte —; die Umwandlung steht an den beiden Enden der erzeugten Funktion. Die geschriebenen Oktette sind unverändert (gemessen, siehe Abschluss-Bemerkung).

--mutable-collections ist ein MAY der Klausel und ausdrücklich non-default; emittiert wird ReadonlyMap.

Tests: crates/lang-rules/tests/typescript_side_tables.rs::s_7_9_1_a_map_is_a_readonly_map_and_states_its_bound, ::the_side_tables_and_their_codecs_type_check (Fälle side-map, side-map-struct-key, side-map-in-sequence), crates/lang-rules/src/adapter/typescript.rs::tests::a_map_member_is_a_readonly_map_over_a_staged_entry_type, crates/lang-rules/tests/typescript_descriptors.rs::b_1_16_a_bounded_map_carries_its_bound_and_a_struct_key_its_hazard

Status: done

§7.10 Typedef Mapping

Spec: Ch7 §Typedef Mapping — Simple/Sequence/Array-Aliases, Recursive Resolution, Forward References.

Repo: crates/lang-rules/src/adapter/typescript.rs::{module_chain, module_handle, scope_decls} (Modul als export namespace, verschachtelt über die Kette), crates/lang-rules/src/emit/typescript.rs::module_prefix (qualifizierte Namen als Punktpfad)

Tests: crates/lang-rules/tests/typescript_codec.rs::a_structure_of_primitives_becomes_a_typescript_encoder, crates/lang-rules/tests/typescript_reader_generates.rs::both_directions, crates/lang-rules/tests/typescript_names_its_runtime.rs::the_backend_names_nothing_the_runtime_does_not_export

Status: done — TS-export type-Aliases sind module-scope- order-frei (verifiziert via Forward-Ref-Test). Cycle-Reject ist Sache des IDL-Frontends (zerodds-idl-lower / zerodds-idl-compose, nicht hier).

§7.10.1 Typedef Descriptor Side-Table

Spec: Ch7 §Typedef Descriptor — kind: "alias" Descriptor mit einem Glied namens value; Type-Guard delegiert an den Guard des referenzierten Typs.

Repo: crates/lang-rules/src/adapter/typescript.rs::{type_spec, GuardShape} (Arm TypeKind::Alias; GuardShape::Delegate trägt den Guard des Ziels)

Tests: crates/lang-rules/tests/typescript_descriptors.rs::b_1_13_a_typedef_is_an_alias_with_one_member_named_value, ::b_1_13_a_wide_bit_bound_switches_the_alias_to_bigint

Status: done

§7.10.2 @bit_bound on Integer Typedefs

Spec: Ch7 §@bit_bound on Integer Typedefs — N ≤ 32 → number, 33–64 → bigint-Switch.

Repo: crates/lang-rules/src/adapter/typescript.rs::{module_chain, module_handle, scope_decls} (Modul als export namespace, verschachtelt über die Kette), crates/lang-rules/src/emit/typescript.rs::module_prefix (qualifizierte Namen als Punktpfad)

Tests: crates/lang-rules/tests/typescript_codec.rs::a_structure_of_primitives_becomes_a_typescript_encoder, crates/lang-rules/tests/typescript_reader_generates.rs::both_directions, crates/lang-rules/tests/typescript_names_its_runtime.rs::the_backend_names_nothing_the_runtime_does_not_export

Status: done

§7.11 Optional and Default Values

Spec: Ch7 §Optional and Default Values — @optional SHALL → ?: T | undefined; @default(V) SHALL → side-table <Type>_<Field>_DEFAULT-Constant + descriptor default-Field.

Repo: crates/lang-rules/src/adapter/typescript.rs::{pair, default_constant, member_field}; crates/lang-rules/src/default_typescript.rs::{explicit_default, member_default}.

Ein @optional-Glied trägt beide Hälften: den ?:-Suffix und den | undefined-Arm — unter exactOptionalPropertyTypes sind retry?: number und retry?: number | undefined verschiedene Deklarationen. Ein @default(V) erzeugt export const <Type>_<Field>_DEFAULT: T' = V; aus derselben Funktion, aus der der Defaulter den Wert nimmt, und dazu das default-Feld des DdsMemberDescriptor.

Tests: crates/lang-rules/tests/typescript_side_tables.rs::s_7_11_a_default_reaches_a_side_table_constant, ::s_7_11_an_optional_member_is_a_question_mark_and_undefined, ::the_side_tables_and_their_codecs_type_check (Fall side-defaults), crates/lang-rules/src/adapter/typescript.rs::tests::an_optional_member_is_a_question_mark_and_not_a_null

Status: done

§7.12 Annotation Mapping

Spec: Ch7 §Annotation Mapping — Tabelle mit ~15 Annotationen (@id, @key, @optional, @default, @final, @appendable, @mutable, @nested, @topic, @must_understand, @unit, @min, @max, @hashid, @autoid, @verbatim, @bit_bound) → Descriptor-Field oder TSDoc-Tag. Resolution-Order, TSDoc- Rendering. Strict-Annotations-Mode (--strict-annotations → E004).

Repo: crates/lang-rules/src/adapter/typescript.rs::{module_chain, module_handle, scope_decls} (Modul als export namespace, verschachtelt über die Kette), crates/lang-rules/src/emit/typescript.rs::module_prefix (qualifizierte Namen als Punktpfad)

Tests: crates/lang-rules/tests/typescript_codec.rs::a_structure_of_primitives_becomes_a_typescript_encoder, crates/lang-rules/tests/typescript_reader_generates.rs::both_directions, crates/lang-rules/tests/typescript_names_its_runtime.rs::the_backend_names_nothing_the_runtime_does_not_export

Status: done


Ch8 Descriptor Runtime (Normative)

§8.1 Extensibility Kind

Spec: Ch8 §Extensibility Kind — ExtensibilityKind = "final" | "appendable" | "mutable"-Type.

Repo: runtime/typescript/src/types.ts (ExtensibilityKind)

Die drei Werte in Kleinschreibung, weil der Deskriptor JSON-fähig ist und dies seine Werte sind; sie entsprechen FINAL, APPENDABLE und MUTABLE aus X-Types 1.3 §7.2.2.4.4 eins zu eins.

Tests: runtime/typescript/tests/descriptor-runtime.ts (B.2.1 — die Typ-Liste ist ein Tupel, das tsc prüft), crates/lang-rules/tests/typescript_descriptors.rs::b_1_11_an_unannotated_struct_is_appendable_and_carries_no_topic

Status: done

§8.2 Type References (DdsTypeRef)

Spec: Ch8 §Type References — DdsTypeRef discriminated-union mit Kinds: primitive, bitfield, string, sequence, array, map, ref (by name), any. Plus PrimitiveName-Type.

Repo: runtime/typescript/src/types.ts (DdsTypeRef, PrimitiveName); crates/lang-rules/src/adapter/typescript.rs::{type_ref_expr, primitive_ref, bitfield_ref, named_ref, primitive_ref_name} (die Erzeugerseite)

Alle acht Kinds. ref trägt den qualifizierten IDL-Namen und keine Deskriptor-Referenz — das hält den Wertgraphen zyklenfrei und JSON-fähig und ist Voraussetzung für wechselseitig rekursive Typen.

PrimitiveName trägt einen Namen mehr als die Liste in §8.2: int8. IDL 4.2 §7.4.1.4.4.2 kennt int8 neben uint8, die Liste nennt octet — das ist uint8 — und für den vorzeichenbehafteten Fall gar nichts. Additiv: jeder von §8.2 genannte Wert bedeutet unverändert das, was §8.2 sagt.

Tests: crates/lang-rules/tests/typescript_descriptors.rs::b_1_1_every_primitive_round_trips, ::b_1_16_a_map_member_is_described_as_a_map, ::b_1_8_char_and_wchar_reach_the_descriptor_as_themselves, ::the_generated_types_type_check; runtime/typescript/tests/descriptor-runtime.ts (B.2.8)

Status: done

§8.3 Type and Member Descriptors

Spec: Ch8 §Type and Member Descriptors — DdsMemberDescriptor, DescriptorKind, DdsTypeDescriptor<T>.

Repo: runtime/typescript/src/types.ts (DdsMemberDescriptor, DescriptorKind, DdsTypeDescriptor)

labels nimmt zusätzlich bigint auf: ein union U switch (long long) ist gültiges IDL 4.2, seine Case-Labels reichen über 2^53 hinaus, und eine Label-Liste, die keinen tragen kann, widerspräche dem Typ, den sie beschreibt. Additiv wie int8 in §8.2.

Tests: runtime/typescript/tests/descriptor-runtime.ts (B.2.1 bis B.2.6), crates/lang-rules/tests/typescript_descriptors.rs::the_generated_types_type_check

Status: done

§8.3.1 Exception Marker

Spec: Ch8 §Exception Marker — DdsException Interface mit __dds_exception: true.

Repo: runtime/typescript/src/types.ts (DdsException)

Eine Eigenschaft und keine Klasse: TypeScripts strukturelle Typisierung hat nichts anderes, was eine Exception-Nutzlast zur Übersetzungszeit von einem gewöhnlichen Struct trennt.

Tests: runtime/typescript/tests/descriptor-runtime.ts — "B.2.1" (der Export steht in der Liste) und "B.2.9" (NotFound extends DdsException).

Status: done

§8.4 Boxed any (DdsAny)

Spec: Ch8 §Boxed any — DdsAny { typeId; value }.

Repo: runtime/typescript/src/types.ts (DdsAny), runtime/typescript/src/dds_any.ts (boxAny, unboxAny)

Beide Funktionen prüfen den Type-Guard des Deskriptors — beim Einpacken und beim Auspacken. Eine Kiste, deren typeId ihren value nicht beschreibt, ist schlimmer als keine, weil jeder Konsument dahinter ihr glaubt.

Tests: runtime/typescript/tests/descriptor-runtime.ts — "B.2.4" (boxAny) und "B.2.5" (unboxAny).

Status: done

§8.5 Branded Character Helpers

Spec: Ch8 §Branded Character Helpers — Char/WChar-Aliases + makeChar/makeWChar-Factories per §7.3.2.

Repo: runtime/typescript/src/branded.ts (Char, WChar, makeChar, makeWChar)

makeChar nimmt genau eine Code-Einheit im Bereich U+0000..U+00FF; makeWChar zählt Code-Punkte statt Code-Einheiten, nimmt also die astrale Ebene an und weist ungepaarte Surrogate ab.

Tests: runtime/typescript/tests/descriptor-runtime.ts — "B.2.7".

Status: done

§8.6 LongDouble

Spec: Ch8 §LongDouble — LongDouble-Type + makeLongDouble per §7.3.3.

Repo: runtime/typescript/src/branded.ts (LongDouble, makeLongDouble)

Die Längenprüfung ist die ganze Prüfung: ein binary128 hat kein ungültiges Bitmuster, und eine Fabrik, die den Exponenten ansähe, würde Werte abweisen, die IEEE 754 definiert. Die Umrechnung einer Dezimalzahl in die sechzehn Oktette steht in runtime/typescript/src/float128.ts.

Tests: runtime/typescript/tests/descriptor-runtime.ts (B.2.7), crates/lang-rules/tests/typescript_descriptors.rs::b_1_9_a_long_double_is_never_a_number

Status: done

§8.7 Reflection API

Spec: Ch8 §Reflection API — exports: registerType, lookupType, getKey, getTopic, withDefaults, boxAny, unboxAny, equalKey, isOneOf. Plus GuardedTypeOf-Helper. Strukturelle Äquivalenz für registerType definiert (deep-equal aller Felder außer typeGuard).

Repo: runtime/typescript/src/registry.ts (registerType, lookupType, getKey, getTopic, withDefaults, isOneOf), runtime/typescript/src/dds_any.ts (boxAny, unboxAny), runtime/typescript/src/equal.ts (equalKey, descriptorsEqual), runtime/typescript/src/types.ts (GuardedTypeOf)

registerType schweigt bei strukturgleicher Wiederanmeldung und wirft bei einer strukturverschiedenen; descriptorsEqual vergleicht jedes Feld tief und typeGuard über Referenzidentität. equalKey folgt einem ref über lookupType und vergleicht Glied für Glied, NaN gleich sich selbst und +0 gleich -0; Map, Sequence und Array sind keine IDL-Schlüsseltypen und werfen statt zu antworten. isOneOf wirft nie.

Tests: runtime/typescript/tests/descriptor-runtime.ts — "B.2.1" bis "B.2.9"; die Reflexion selbst in "B.2.2" (lookupType) und "B.2.3" (getKey).

Status: done


Ch9 Verbatim Code-Generation Hook (Normative)

§9.1 Annotation Form

Spec: Ch9 §Annotation Form — @verbatim(language, placement, text)-Annotation; language="ts"|"*"-Recognition.

Repo: crates/lang-rules/src/verbatim.rs::{language_matches, declaration_slots, file_slots}

Tests: crates/lang-rules/tests/typescript_codec.rs::encoder

Status: done

§9.2 Placement Kinds

Spec: Ch9 §Placement Kinds — alle 6 PlacementKinds (BEGIN_FILE, BEFORE_DECLARATION, BEGIN_DECLARATION, END_DECLARATION, AFTER_DECLARATION, END_FILE).

Repo: crates/lang-rules/src/verbatim.rs::{declaration_slots, file_slots} (Platzierung an Deklaration oder Datei)

Tests: crates/lang-rules/tests/typescript_codec.rs::encoder

Status: done

§9.3 Quoting and Escaping

Spec: Ch9 §Quoting and Escaping — IDL-string-escape-Resolution vor Emission.

Repo: crates/lang-rules/src/verbatim.rs::gather (Auflösung der IDL-Maskierung)

Tests: crates/lang-rules/tests/typescript_codec.rs::encoder

Status: done

§9.4 Multi-Line Text + Multiple Annotations

Spec: Ch9 §Multi-Line + Multiple — Newlines, source-order, no-coalescing.

Repo: crates/lang-rules/src/verbatim.rs::gather (mehrzeiliger Text, mehrere Annotationen je Platz)

Tests: crates/lang-rules/tests/typescript_codec.rs::encoder

Status: done


Ch10 Code Style (Informative)

Spec: Ch10 — Identifier-Casing, Output-Format-Empfehlungen, File-Layout-Optionen.

Repo: crates/lang-rules/src/verbatim.rs::gather (mehrzeiliger Text, mehrere Annotationen je Platz)

Tests: crates/lang-rules/tests/typescript_codec.rs::encoder

Status: n/a (informative)


Ch11 Interface and Operation Mapping (Normative)

§11.1 Mapping Surface

Spec: Ch11 §Mapping Surface — IDL interface I SHALL → IClient + IHandler + IService: ServiceDescriptor. Kein async-Keyword auf interface-method-signatures.

Repo: crates/lang-rules/src/emit/interface_typescript.rs::{ts_interface_items, decls_of} — je Interface ein <I>Client, ein <I>Handler und der Deskriptor

Tests: crates/lang-rules/tests/interface_bindings.rs::typescript_maps_an_interface_to_a_client_a_handler_and_a_descriptor

Status: done

§11.2 Operation Mapping

Spec: Ch11 §Operation Mapping — return → Promise<R>; in/inout → method-params; out/inout → result-object-properties; oneway → Promise<void>.

Repo: crates/lang-rules/src/emit/interface_typescript.rs::{signatures, operation_signature} — out- und inout-Parameter wandern in den aufgelösten Wert des Promise

Tests: crates/lang-rules/tests/interface_bindings.rs::typescript_aggregates_out_parameters_into_the_resolved_value

Status: done

§11.3 Attribute Mapping

Spec: Ch11 §Attribute Mapping — attribute T name SHALL → get_name(): Promise<T> + set_name(v: T): Promise<void>. readonly attribute → kein Setter. getraises/setraises.

Repo: crates/lang-rules/src/emit/interface_typescript.rs::attribute_signatures — get_/set_; ein readonly attribute bekommt nur den Leser

Tests: crates/lang-rules/tests/interface_bindings.rs::typescript_maps_an_attribute_to_a_getter_and_a_setter

Status: done

§11.4 Exception Mapping

Spec: Ch11 §Exception Mapping — raises (E) → reject mit Error + cause: E. isOneOf-Helper.

Repo: crates/lang-rules/src/emit/interface_typescript.rs (Zeile 395 und 420–424: die raises-Liste je Operation und je Attributrichtung im Deskriptor), runtime/typescript/src/operations.ts (isOneOf prüft die abgefangene Ablehnung)

Tests: crates/lang-rules/tests/interface_bindings.rs::typescript_writes_the_service_descriptor_of_eleven_five

Status: done

§11.5 Service Descriptor

Spec: Ch11 §Service Descriptor — ServiceDescriptor<TC,TH>, OperationDescriptor, OperationParameterDescriptor, AttributeDescriptor, ParameterMode-Types.

Repo: crates/lang-rules/src/emit/interface_typescript.rs::{descriptor, operation_descriptor}, runtime/typescript/src/operations.ts::ServiceDescriptor

Tests: crates/lang-rules/tests/interface_bindings.rs::typescript_writes_the_service_descriptor_of_eleven_five

Status: done

§11.6 Inheritance

Spec: Ch11 §Inheritance — interface B : A SHALL → BClient extends AClient + BHandler extends AHandler + BService.inherits = [AService]. Multi-Inheritance erlaubt.

Repo: crates/lang-rules/src/emit/interface_typescript.rs (Zeile 141 und 156: extends am Client und am Handler; Zeile 313: das inherits-Feld des Deskriptors trägt jede Elternschaft in Deklarationsreihenfolge)

Tests: crates/lang-rules/tests/interface_bindings.rs::typescript_maps_an_interface_to_a_client_a_handler_and_a_descriptor

Status: done

§11.7 Forward Declarations

Spec: Ch11 §Forward Declarations — <interface_forward_dcl> SHALL NOT separate Declaration produzieren. Orphan → DDS-TS-E002.

Repo: crates/lang-rules/src/emit/interface_typescript.rs — eine Vorwärtsdeklaration ohne Definition ist ein fataler Befund und trägt den Code DDS-TS-E002

Tests: crates/lang-rules/tests/interface_bindings.rs::a_forward_declaration_without_a_definition_is_e002

Status: done


Annex A — IDL Examples (Informative)

Spec: Annex A — drei Beispiele (Bounded Sequence, Mutable Type, Bitset).

Repo: crates/lang-rules/src/emit/interface_typescript.rs — eine Vorwärtsdeklaration ohne Definition ist ein fataler Befund und trägt den Code DDS-TS-E002

Tests: crates/lang-rules/tests/interface_bindings.rs::a_forward_declaration_without_a_definition_is_e002

Status: n/a (informative)


Annex B — Compliance Test Suite (Normative)

§B.1 Codegen-Profile Tests

Spec: Annex B §B.1 — 16 Pflicht-Tests (B.1.1–B.1.16) gegen Codegen-Output.

Repo: crates/lang-rules/tests/typescript_descriptors.rs — 25 Tests, nach den Marken benannt (b_1_1_… bis b_1_16_…), plus the_generated_types_type_check, das neun Formen durch tsc --strict --noEmit gegen die echte Laufzeit schickt.

Alle sechzehn Marken stehen. B.1.2 hat zwei Hälften und beide werden gemessen: die Seitentabelle (<Type>Type, is<Type>, drei typisierte Glieder) und die Klausel „No class keyword MUST appear in the generator output for any IDL data type” — geprüft über elf Formen, mit entfernten Kommentaren, weil ein Kommentar über das Wort keine Klasse ist.

Tests: crates/lang-rules/tests/typescript_descriptors.rs::b_1_1_every_primitive_round_trips, ::b_1_2_a_struct_gets_an_interface_a_descriptor_and_a_guard, ::b_1_2_no_class_reaches_the_output_for_any_idl_data_type, ::b_1_3_a_union_carries_its_labels_and_its_default_flags, ::b_1_4_an_enum_gets_a_const_object_and_a_descriptor, ::b_1_5a_a_bitset_widths_pick_their_holders_and_the_descriptor_totals_them, ::b_1_5b_a_bitmask_carries_its_positions_and_its_bound, ::b_1_6_a_module_nests_its_declarations_and_its_descriptors, ::b_1_7_free_standing_constants_keep_their_types, ::b_1_8_char_and_wchar_reach_the_descriptor_as_themselves, ::b_1_9_a_long_double_is_never_a_number, ::b_1_10_an_any_member_is_a_box_and_never_any_or_unknown, ::b_1_11_annotations_change_the_side_table_and_not_the_interface, ::b_1_12_an_exception_is_described_as_one, ::b_1_13_a_typedef_is_an_alias_with_one_member_named_value, ::b_1_14_a_mixed_module_produces_every_side_table, ::b_1_15_an_unrecognised_annotation_does_not_stop_the_generator, ::b_1_16_a_map_member_is_described_as_a_map, ::the_generated_types_type_check

Status: done

§B.2 Descriptor-Runtime Tests

Spec: Annex B §B.2 — 9 Pflicht-Tests gegen @zerodds/types- Library-Surface.

Repo: runtime/typescript/tests/descriptor-runtime.ts — B.2.1 bis B.2.9, je Marke ein Block. B.2.1 hat zwei Hälften: die Typ-Liste steht als Tupel, das tsc prüft, und die Funktions-Liste wird zur Laufzeit geprüft, weil ein Name, der exportiert und undefined ist, anstandslos übersetzt.

Tests: runtime/typescript/tests/descriptor-runtime.ts — 9 von 9 Marken: "B.2.1", "B.2.2", "B.2.3", "B.2.4", "B.2.5", "B.2.6", "B.2.7", "B.2.8", "B.2.9"; Lauf über runtime/typescript/run-tests.sh.

Status: done

§B.3 Operations-Profile Tests

Spec: Annex B §B.3 — 9 Pflicht-Tests gegen Operations-Profile- Output.

Repo: crates/lang-rules/tests/interface_bindings.rs (die sechs typescript_*-Marken) und crates/lang-rules/tests/typescript_operations_profile.rs (tsc --strict über den erzeugten Profil-Code)

Tests: crates/lang-rules/tests/typescript_operations_profile.rs::the_generated_operations_profile_type_checks

Status: done

§B.4 Reference Test-Harness

Spec: Annex B §B.4 — Test-Harness-Definition (tsc-Verify auf generated Output, Node.js-Runner für Descriptor-Helpers).

Repo: crates/lang-rules/tests/typescript_descriptors.rs (der tsc-Teil: --noEmit --strict über den erzeugten Output), runtime/typescript/run-tests.sh (der Node.js-Teil: die Descriptor-Helfer und die WASM-Bindungen unter node).

Die Spec nennt das frühere Codegen-Crate als Ort; es gibt es nicht mehr, die beiden Hälften des Harness schon. Was §B.4 verlangt — Rust-Tests gegen den Codegen-Output, tsc --noEmit --strict darüber, und die Laufzeit-Helfer in einem Node.js-Runner — steht vollständig.

Tests: crates/lang-rules/tests/typescript_descriptors.rs::the_generated_types_type_check, runtime/typescript/tests/descriptor-runtime.ts, runtime/typescript/tests/wasm-bindings.ts

Status: done


Annex C — WASM-Bindings Profile (Normative)

§C.1 Handle and Sample Types

Spec: Annex C §C.1 — Branded Handle-Types (Participant/Topic/ Publisher/Subscriber/Writer/Reader mit string-literal brands), SampleInfo, Sample, DdsGuid (12+4 byte) mit makeDdsGuid-Factory, DataAvailableCallback.

Repo: runtime/typescript/src/wasm.ts (ParticipantHandle, TopicHandle, PublisherHandle, SubscriberHandle, DataWriterHandle, DataReaderHandle, DdsGuid, makeDdsGuid, SampleInfo, Sample, DataAvailableCallback)

Die Marken sind String-Literale und keine unique symbol: ein Symbol-Brand ist über Modulgrenzen nominal, und ein Handle aus der Bindung eines Anbieters passte dann nicht auf den Parameter eines anderen. publicationHandle trägt die vollen sechzehn Oktette des RTPS-GUID; eine Kürzung auf 64 Bit verlöre Vendor- und Host-Anteil.

Tests: runtime/typescript/tests/wasm-bindings.ts — "C.5.1" prüft prefix.length === 12 und sequenceNumber.

Status: done

§C.2 Required Operations

Spec: Annex C §C.2 — DCPS-Data-Path-Operations (createParticipant/Topic/Publisher/Subscriber/DataWriter/ DataReader/writeSample/takeSamples/setDataAvailableListener).

Repo: runtime/typescript/src/wasm.ts (DdsBindings nennt alle vierzehn Operationen aus §C.2.1 bis §C.2.4; WasmBackend ist dieselbe Fläche mit blanken Zahlen, wie sie eine Wasm-Exporttabelle hat; bindWasmBackend verbindet beide)

Was bindWasmBackend zusätzlich durchsetzt, steht nicht im Typsystem: Handle 0 als Sentinel, Lebendigkeit, die Kaskade beim Löschen, geliehene Puffer und die Zustellung des Listeners über die Ereignisschleife.

Tests: runtime/typescript/tests/wasm-bindings.ts — "C.5.1", "C.5.2", "C.5.3", "C.5.4".

Status: done

§C.3 Wire-Format Boundary

Spec: Annex C §C.3 — XCDR2-Bytes, Memory-Ownership, DdsAny-Boundary-Rule. --xcdr-endianness-Flag.

Repo: runtime/typescript/src/wasm.ts (writeSample und takeSamples nehmen und geben Uint8Array; borrowed kopiert, was hinübergereicht wird, und takeSamples kopiert, was zurückkommt), tools/idlc/src/main.rs (--xcdr-endianness <BE|LE>, Vorgabe LE)

Kein JavaScript-Objektgraph überquert die Grenze: die Nutzlast ist der XCDR2-Oktettstrom und sonst nichts. Eine DdsAny ist davon keine Ausnahme — boxAny auf der Erzeugerseite, der erzeugte Serialisierer, die Oktette, der erzeugte Deserialisierer, unboxAny auf der Konsumentenseite.

Tests: runtime/typescript/tests/wasm-bindings.ts (C.5.1 prüft, dass ein nach dem Schreiben verändertes Array den Empfänger nicht erreicht; C.5.4 den DdsAny-Weg), tools/idlc/src/main.rs::tests::generate_takes_the_dds_ts_spelling_of_the_endianness_flag

Status: done

§C.4 Browser vs Node.js Bindings

Spec: Annex C §C.4 — Browser via wasm-bindgen, Node.js via N-API (optional).

Repo: runtime/typescript/src/wasm.ts (WasmBackend, bindWasmBackend)

Eine Fläche für beide Profile. Der Browser liefert ein .wasm-Modul, Node.js darf zusätzlich ein .node-Modul über N-API liefern; beide erfüllen WasmBackend, und bindWasmBackend gibt beidemal dasselbe DdsBindings zurück. Eine Anwendung wechselt damit die Import-Zeile und sonst nichts. Die Laufzeit selbst hängt an nichts aus Node: sie muss im Browser laufen, und ein @types/node unter ihren Abhängigkeiten sagte etwas anderes.

Tests: runtime/typescript/tests/wasm-bindings.ts — "C.5.1" bindet gegen ein Backend, das weder das Browser- noch das Node-Modul ist; gemessen wird damit die Bindungsschicht, nicht das jeweilige Modul.

Status: done

§C.5 Conformance Tests

Spec: Annex C §C.5 — 4 Pflicht-Round-Trip-Tests.

Repo: runtime/typescript/tests/wasm-bindings.ts (C.5.1 Publish/Subscribe-Rundlauf, C.5.2 Listener, C.5.3 Ressourcen-Freigabe, C.5.4 DdsAny-Rundlauf), runtime/typescript/src/test_backend.ts (der In-Memory-Datenpfad, gegen den sie laufen)

Der Referenz-Backend ist absichtlich synchron: er ruft den Listener innerhalb von writeSample auf, was §C.2.4 einer Bindung verbietet — und genau das macht die Verzögerung in bindWasmBackend messbar.

Tests: runtime/typescript/tests/wasm-bindings.ts — 4 von 4: "C.5.1", "C.5.2", "C.5.3", "C.5.4", über runtime/typescript/run-tests.sh.

Status: done

§C.6 Reservation of Future Extensions

Spec: Annex C §C.6 — “The following identifiers are reserved for future revisions of this Profile and SHALL NOT be re-used by Beta implementations for unrelated purposes: createGuardCondition, createWaitSet, createQueryCondition, getInstance.”

Repo: —

Tests: —

Status: open — nach dem Code erfüllt: crates/ts-wasm/ und runtime/typescript/src/ verwenden keinen der vier Bezeichner; crates/ts-node/src/dds.ts nutzt getInstance für die DomainParticipantFactory (gleicher Zweck). Es fehlt ein Test, der die Exporte des WASM-Profils gegen die Liste prüft.


Annex D — Diagnostic Codes (Normative)

§D.1 Diagnostic Code Registry

Spec: Annex D — Codes E001 (reserved), E002 (forward-decl orphan), E003 (annotation conflict), E004 (strict-annotations), W001 (reserved), W002 (unknown annotation), W003 (map key hazard), W004 (implicit default), I001 (long double info).

Repo: —

Tests: —

Status: open — der Baum führt eine stabile Code-Menge, aber eine andere: crates/idl-parse/src/diagnostic.rs::DiagnosticCode vergibt IDL0001 bis IDL0018. Die Annex-D-Codes E001-E004, W001-W004 und I001 kommen im Baum nicht vor, und kein Test prüft sie.


Audit-Status

57 done / 1 partial / 3 open / 0 blocked / 12 n/a (informative).

Test-Lauf: cargo test -p zerodds-lang-rules — 50 Testbinaries, 1024 Tests grün, 0 failed; darunter --test typescript_side_tables (12), --test typescript_descriptors (26), --test typescript_reader_generates (16), --test typescript_codec (3), --test typescript_names_its_runtime (2) und --test typescript_operations_profile (1). runtime/typescript/run-tests.sh — 61 Marken grün (16 Oktett-Fälle, 32 Wert-Fälle, 9 Descriptor-Runtime-Marken, 4 WASM-Konformanztests), 0 failed.

Wire-Messung zur Oberfläche von §7.5, §7.6 und §7.9.1: 119 Fälle der Zelle matrix/zerodds-typescript/cyclone-xcdr2-le neu durch tools/wireprobe/wireprobe-zerodds-typescript gefahren und Oktette wie zurückgelesene Werte gegen die abgelegten Artefakte gehalten — 244 von 244 Vergleichen gleich, 0 verschieden.

Offene Punkte: dds-ts-1.0.open.md.