@zerodds/wire 0.0.0-stage → 1.0.0-rc.7
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +13 -2
- package/dist/branded.d.ts +98 -0
- package/dist/branded.js +64 -0
- package/dist/dds_any.d.ts +47 -0
- package/dist/dds_any.js +41 -0
- package/dist/endianness.d.ts +22 -0
- package/dist/endianness.js +35 -0
- package/dist/equal.d.ts +46 -0
- package/dist/equal.js +273 -0
- package/dist/error.d.ts +52 -0
- package/dist/error.js +78 -0
- package/dist/float128.d.ts +15 -0
- package/dist/float128.js +181 -0
- package/dist/index.d.ts +31 -0
- package/dist/index.js +33 -0
- package/dist/operations.d.ts +180 -0
- package/dist/operations.js +74 -0
- package/dist/policy.d.ts +133 -0
- package/dist/policy.js +184 -0
- package/dist/reader.d.ts +605 -0
- package/dist/reader.js +1345 -0
- package/dist/registry.d.ts +84 -0
- package/dist/registry.js +166 -0
- package/dist/test_backend.d.ts +9 -0
- package/dist/test_backend.js +173 -0
- package/dist/types.d.ts +246 -0
- package/dist/types.js +3 -0
- package/dist/wasm.d.ts +203 -0
- package/dist/wasm.js +194 -0
- package/dist/writer.d.ts +662 -0
- package/dist/writer.js +1333 -0
- package/package.json +27 -4
|
@@ -0,0 +1,84 @@
|
|
|
1
|
+
import type { DdsTypeDescriptor, GuardedTypeOf } from "./types.js";
|
|
2
|
+
/**
|
|
3
|
+
* Add a descriptor to the registry.
|
|
4
|
+
*
|
|
5
|
+
* DDS-TS 1.0 §8.7. Registering the same type twice is silent — a module
|
|
6
|
+
* imported from two places runs its registration twice and that is not
|
|
7
|
+
* an error. Registering a **different** type under a name already taken
|
|
8
|
+
* throws, and it has to: the second registration would otherwise decide
|
|
9
|
+
* what every `{ kind: "ref" }` to that name resolves to, and every
|
|
10
|
+
* consumer downstream would read one type's samples through another
|
|
11
|
+
* type's members with nothing reporting it.
|
|
12
|
+
*
|
|
13
|
+
* @throws TypeError when the name is taken by a structurally different
|
|
14
|
+
* descriptor.
|
|
15
|
+
*/
|
|
16
|
+
export declare function registerType(d: DdsTypeDescriptor): void;
|
|
17
|
+
/**
|
|
18
|
+
* The descriptor registered under a qualified IDL name, or `undefined`.
|
|
19
|
+
*
|
|
20
|
+
* DDS-TS 1.0 §8.7. `undefined` and not a throw: a consumer that reached
|
|
21
|
+
* a `ref` to a type whose module was never imported is asking a question
|
|
22
|
+
* this can answer, and the caller decides whether that is a failure.
|
|
23
|
+
*/
|
|
24
|
+
export declare function lookupType(qualifiedName: string): DdsTypeDescriptor | undefined;
|
|
25
|
+
/**
|
|
26
|
+
* The values of a sample's key members, in declaration order.
|
|
27
|
+
*
|
|
28
|
+
* DDS-TS 1.0 §8.7. Declaration order is the order the descriptor lists
|
|
29
|
+
* them, which is the order the IDL declared them and the order X-Types
|
|
30
|
+
* 1.3 §7.6.8 hashes them in — so the array can be fed to a key hash
|
|
31
|
+
* without being sorted first.
|
|
32
|
+
*
|
|
33
|
+
* The sample's shape is **not** checked. A caller that does not trust
|
|
34
|
+
* where the sample came from runs `descriptor.typeGuard` first; one that
|
|
35
|
+
* just decoded it through the generated codec already knows.
|
|
36
|
+
*
|
|
37
|
+
* A type with no `@key` member returns `[]`, which is the whole of the
|
|
38
|
+
* DDS single-instance case and not an error.
|
|
39
|
+
*/
|
|
40
|
+
export declare function getKey<T>(sample: T, descriptor: DdsTypeDescriptor<T>): ReadonlyArray<unknown>;
|
|
41
|
+
/**
|
|
42
|
+
* The topic name the type is bound to, or `undefined`.
|
|
43
|
+
*
|
|
44
|
+
* DDS-TS 1.0 §8.7. `descriptor.topic` unchanged — a function rather than
|
|
45
|
+
* a property access so that a consumer holding only the reflection API
|
|
46
|
+
* has one, and so that the `@topic` binding has a name to be looked up
|
|
47
|
+
* by.
|
|
48
|
+
*/
|
|
49
|
+
export declare function getTopic<T>(descriptor: DdsTypeDescriptor<T>): string | undefined;
|
|
50
|
+
/**
|
|
51
|
+
* A partial value, completed from the descriptor's `@default` values.
|
|
52
|
+
*
|
|
53
|
+
* DDS-TS 1.0 §8.7. Every member the descriptor records a
|
|
54
|
+
* non-`undefined` `default` for and `partial` does not carry is filled
|
|
55
|
+
* from that default. A member with neither stays **absent** — not
|
|
56
|
+
* `undefined`, absent — because a `@optional` member that is present and
|
|
57
|
+
* undefined is a different sample from one that is not there at all, and
|
|
58
|
+
* under XCDR2 the two are different octets.
|
|
59
|
+
*/
|
|
60
|
+
export declare function withDefaults<T>(descriptor: DdsTypeDescriptor<T>, partial: Partial<T>): T;
|
|
61
|
+
/**
|
|
62
|
+
* Whether a rejection carries one of a set of IDL exceptions.
|
|
63
|
+
*
|
|
64
|
+
* DDS-TS 1.0 §8.7. A generated Operations-Profile client rejects with an
|
|
65
|
+
* `Error` whose `cause` is the decoded IDL exception payload, so a
|
|
66
|
+
* catch-handler asks this and gets the union of the listed types back as
|
|
67
|
+
* a narrowing:
|
|
68
|
+
*
|
|
69
|
+
* ```ts
|
|
70
|
+
* catch (err) {
|
|
71
|
+
* if (isOneOf(err, [NotFoundType, DeniedType])) {
|
|
72
|
+
* err.cause; // NotFound | Denied
|
|
73
|
+
* }
|
|
74
|
+
* }
|
|
75
|
+
* ```
|
|
76
|
+
*
|
|
77
|
+
* Never throws. A `catch` block is the one place where a helper that
|
|
78
|
+
* threw would replace the error being handled with its own, so anything
|
|
79
|
+
* that is not an `Error` — a thrown string, a rejected `undefined` — is
|
|
80
|
+
* simply `false`.
|
|
81
|
+
*/
|
|
82
|
+
export declare function isOneOf<DS extends ReadonlyArray<DdsTypeDescriptor>>(err: unknown, descriptors: DS): err is Error & {
|
|
83
|
+
cause: GuardedTypeOf<DS[number]>;
|
|
84
|
+
};
|
package/dist/registry.js
ADDED
|
@@ -0,0 +1,166 @@
|
|
|
1
|
+
// SPDX-License-Identifier: Apache-2.0
|
|
2
|
+
// Copyright 2026 ZeroDDS Contributors
|
|
3
|
+
/**
|
|
4
|
+
* The reflection half of the descriptor runtime.
|
|
5
|
+
*
|
|
6
|
+
* DDS-TS 1.0 §8.7. A registry keyed by qualified IDL name, and the five
|
|
7
|
+
* functions that read a descriptor: the key members out of a sample, the
|
|
8
|
+
* topic name off the type, the defaults into a partial value, and — in
|
|
9
|
+
* `./dds_any.js` and `./equal.js` — the box and the key comparison.
|
|
10
|
+
*
|
|
11
|
+
* # Why there is a registry at all
|
|
12
|
+
*
|
|
13
|
+
* Because `DdsTypeRef.kind === "ref"` carries a **name** and not a
|
|
14
|
+
* descriptor (§8.2). That is what makes a descriptor graph acyclic and
|
|
15
|
+
* therefore JSON-roundtrippable, and it is what lets two structs refer to
|
|
16
|
+
* each other when neither can be initialised first. The cost is that a
|
|
17
|
+
* consumer walking the graph has to resolve those names somewhere, and
|
|
18
|
+
* this is the somewhere: every generated module ends with
|
|
19
|
+
* `registerType(<Type>Type)`, and {@link lookupType} answers.
|
|
20
|
+
*/
|
|
21
|
+
import { descriptorsEqual } from "./equal.js";
|
|
22
|
+
/**
|
|
23
|
+
* Every descriptor registered so far, by qualified IDL name.
|
|
24
|
+
*
|
|
25
|
+
* One map for the module graph. Two bundles that each carry their own
|
|
26
|
+
* copy of this runtime have two registries and each sees its own
|
|
27
|
+
* registrations, which §8.7 names as out of scope for the equivalence
|
|
28
|
+
* check.
|
|
29
|
+
*/
|
|
30
|
+
const registry = new Map();
|
|
31
|
+
/**
|
|
32
|
+
* Add a descriptor to the registry.
|
|
33
|
+
*
|
|
34
|
+
* DDS-TS 1.0 §8.7. Registering the same type twice is silent — a module
|
|
35
|
+
* imported from two places runs its registration twice and that is not
|
|
36
|
+
* an error. Registering a **different** type under a name already taken
|
|
37
|
+
* throws, and it has to: the second registration would otherwise decide
|
|
38
|
+
* what every `{ kind: "ref" }` to that name resolves to, and every
|
|
39
|
+
* consumer downstream would read one type's samples through another
|
|
40
|
+
* type's members with nothing reporting it.
|
|
41
|
+
*
|
|
42
|
+
* @throws TypeError when the name is taken by a structurally different
|
|
43
|
+
* descriptor.
|
|
44
|
+
*/
|
|
45
|
+
export function registerType(d) {
|
|
46
|
+
const seen = registry.get(d.name);
|
|
47
|
+
if (seen === undefined) {
|
|
48
|
+
registry.set(d.name, d);
|
|
49
|
+
return;
|
|
50
|
+
}
|
|
51
|
+
if (descriptorsEqual(seen, d)) {
|
|
52
|
+
return;
|
|
53
|
+
}
|
|
54
|
+
throw new TypeError(`registerType: \`${d.name}\` is already registered as a different type`);
|
|
55
|
+
}
|
|
56
|
+
/**
|
|
57
|
+
* The descriptor registered under a qualified IDL name, or `undefined`.
|
|
58
|
+
*
|
|
59
|
+
* DDS-TS 1.0 §8.7. `undefined` and not a throw: a consumer that reached
|
|
60
|
+
* a `ref` to a type whose module was never imported is asking a question
|
|
61
|
+
* this can answer, and the caller decides whether that is a failure.
|
|
62
|
+
*/
|
|
63
|
+
export function lookupType(qualifiedName) {
|
|
64
|
+
return registry.get(qualifiedName);
|
|
65
|
+
}
|
|
66
|
+
/**
|
|
67
|
+
* The values of a sample's key members, in declaration order.
|
|
68
|
+
*
|
|
69
|
+
* DDS-TS 1.0 §8.7. Declaration order is the order the descriptor lists
|
|
70
|
+
* them, which is the order the IDL declared them and the order X-Types
|
|
71
|
+
* 1.3 §7.6.8 hashes them in — so the array can be fed to a key hash
|
|
72
|
+
* without being sorted first.
|
|
73
|
+
*
|
|
74
|
+
* The sample's shape is **not** checked. A caller that does not trust
|
|
75
|
+
* where the sample came from runs `descriptor.typeGuard` first; one that
|
|
76
|
+
* just decoded it through the generated codec already knows.
|
|
77
|
+
*
|
|
78
|
+
* A type with no `@key` member returns `[]`, which is the whole of the
|
|
79
|
+
* DDS single-instance case and not an error.
|
|
80
|
+
*/
|
|
81
|
+
export function getKey(sample, descriptor) {
|
|
82
|
+
const bag = sample;
|
|
83
|
+
const out = [];
|
|
84
|
+
for (const member of descriptor.fields) {
|
|
85
|
+
if (member.base === true && member.type.kind === "ref") {
|
|
86
|
+
// The keys of the base come first, as they are written first (§7.4.1.1.5.1).
|
|
87
|
+
const base = lookupType(member.type.name);
|
|
88
|
+
const held = bag[member.name];
|
|
89
|
+
if (base !== undefined && held !== undefined) {
|
|
90
|
+
out.push(...getKey(held, base));
|
|
91
|
+
}
|
|
92
|
+
continue;
|
|
93
|
+
}
|
|
94
|
+
if (member.key) {
|
|
95
|
+
out.push(bag[member.name]);
|
|
96
|
+
}
|
|
97
|
+
}
|
|
98
|
+
return out;
|
|
99
|
+
}
|
|
100
|
+
/**
|
|
101
|
+
* The topic name the type is bound to, or `undefined`.
|
|
102
|
+
*
|
|
103
|
+
* DDS-TS 1.0 §8.7. `descriptor.topic` unchanged — a function rather than
|
|
104
|
+
* a property access so that a consumer holding only the reflection API
|
|
105
|
+
* has one, and so that the `@topic` binding has a name to be looked up
|
|
106
|
+
* by.
|
|
107
|
+
*/
|
|
108
|
+
export function getTopic(descriptor) {
|
|
109
|
+
return descriptor.topic;
|
|
110
|
+
}
|
|
111
|
+
/**
|
|
112
|
+
* A partial value, completed from the descriptor's `@default` values.
|
|
113
|
+
*
|
|
114
|
+
* DDS-TS 1.0 §8.7. Every member the descriptor records a
|
|
115
|
+
* non-`undefined` `default` for and `partial` does not carry is filled
|
|
116
|
+
* from that default. A member with neither stays **absent** — not
|
|
117
|
+
* `undefined`, absent — because a `@optional` member that is present and
|
|
118
|
+
* undefined is a different sample from one that is not there at all, and
|
|
119
|
+
* under XCDR2 the two are different octets.
|
|
120
|
+
*/
|
|
121
|
+
export function withDefaults(descriptor, partial) {
|
|
122
|
+
const out = { ...partial };
|
|
123
|
+
for (const member of descriptor.fields) {
|
|
124
|
+
if (member.base === true && member.type.kind === "ref") {
|
|
125
|
+
const base = lookupType(member.type.name);
|
|
126
|
+
if (base !== undefined) {
|
|
127
|
+
const held = (out[member.name] ?? {});
|
|
128
|
+
out[member.name] = withDefaults(base, held);
|
|
129
|
+
}
|
|
130
|
+
continue;
|
|
131
|
+
}
|
|
132
|
+
if (member.default === undefined || member.name in out) {
|
|
133
|
+
continue;
|
|
134
|
+
}
|
|
135
|
+
out[member.name] = member.default;
|
|
136
|
+
}
|
|
137
|
+
return out;
|
|
138
|
+
}
|
|
139
|
+
/**
|
|
140
|
+
* Whether a rejection carries one of a set of IDL exceptions.
|
|
141
|
+
*
|
|
142
|
+
* DDS-TS 1.0 §8.7. A generated Operations-Profile client rejects with an
|
|
143
|
+
* `Error` whose `cause` is the decoded IDL exception payload, so a
|
|
144
|
+
* catch-handler asks this and gets the union of the listed types back as
|
|
145
|
+
* a narrowing:
|
|
146
|
+
*
|
|
147
|
+
* ```ts
|
|
148
|
+
* catch (err) {
|
|
149
|
+
* if (isOneOf(err, [NotFoundType, DeniedType])) {
|
|
150
|
+
* err.cause; // NotFound | Denied
|
|
151
|
+
* }
|
|
152
|
+
* }
|
|
153
|
+
* ```
|
|
154
|
+
*
|
|
155
|
+
* Never throws. A `catch` block is the one place where a helper that
|
|
156
|
+
* threw would replace the error being handled with its own, so anything
|
|
157
|
+
* that is not an `Error` — a thrown string, a rejected `undefined` — is
|
|
158
|
+
* simply `false`.
|
|
159
|
+
*/
|
|
160
|
+
export function isOneOf(err, descriptors) {
|
|
161
|
+
if (!(err instanceof Error)) {
|
|
162
|
+
return false;
|
|
163
|
+
}
|
|
164
|
+
const cause = err.cause;
|
|
165
|
+
return descriptors.some((descriptor) => descriptor.typeGuard(cause));
|
|
166
|
+
}
|
|
@@ -0,0 +1,9 @@
|
|
|
1
|
+
import type { WasmBackend } from "./wasm.js";
|
|
2
|
+
/**
|
|
3
|
+
* An in-memory {@link WasmBackend}.
|
|
4
|
+
*
|
|
5
|
+
* Handles start at 1 and are never reused, so a handle that has been
|
|
6
|
+
* deleted names nothing else afterwards — which is what lets the
|
|
7
|
+
* binding layer's `RangeError` say which handle it was.
|
|
8
|
+
*/
|
|
9
|
+
export declare function createTestBackend(): WasmBackend;
|
|
@@ -0,0 +1,173 @@
|
|
|
1
|
+
// SPDX-License-Identifier: Apache-2.0
|
|
2
|
+
// Copyright 2026 ZeroDDS Contributors
|
|
3
|
+
/**
|
|
4
|
+
* A DDS data path in TypeScript, for measuring the binding layer against.
|
|
5
|
+
*
|
|
6
|
+
* DDS-TS 1.0 §C.5 requires a conformant WASM binding to pass four tests:
|
|
7
|
+
* a publish/subscribe round trip, a listener that fires, a delete that
|
|
8
|
+
* cascades, and a boxed `any` that survives the trip. Those are tests of
|
|
9
|
+
* {@link bindWasmBackend} — the handle checks, the cascade, the copies,
|
|
10
|
+
* the deferral — and running them against a real `.wasm` module would
|
|
11
|
+
* measure the module as well, in a test that needs a build of it to fail
|
|
12
|
+
* or pass.
|
|
13
|
+
*
|
|
14
|
+
* So this: a {@link WasmBackend} that keeps its samples in a `Map`.
|
|
15
|
+
* Every rule Annex C states about the *binding* is measurable against
|
|
16
|
+
* it, and a failure is the binding's, because there is no wire and no
|
|
17
|
+
* module.
|
|
18
|
+
*
|
|
19
|
+
* What it is not: a DDS implementation. There is no discovery, no
|
|
20
|
+
* reliability, no history depth, no QoS at all. Matching is by domain
|
|
21
|
+
* and topic name, delivery is immediate and in order, and the sample
|
|
22
|
+
* states it reports are the ones a fresh take produces. A test that
|
|
23
|
+
* needs any of the rest belongs against the Rust core.
|
|
24
|
+
*
|
|
25
|
+
* It is deliberately synchronous — `writeSample` runs the listener
|
|
26
|
+
* straight through, in the writer's own turn. That is precisely what
|
|
27
|
+
* §C.2.4 forbids a *binding* from doing, and it is what makes the
|
|
28
|
+
* deferral in {@link bindWasmBackend} measurable: if the wrapper ever
|
|
29
|
+
* stopped queueing, this backend would deliver inside `writeSample` and
|
|
30
|
+
* the C.5.2 test would see it.
|
|
31
|
+
*/
|
|
32
|
+
import { makeDdsGuid } from "./wasm.js";
|
|
33
|
+
/** The twelve-octet prefix every writer of one backend instance shares. */
|
|
34
|
+
const GUID_PREFIX_LENGTH = 12;
|
|
35
|
+
/**
|
|
36
|
+
* An in-memory {@link WasmBackend}.
|
|
37
|
+
*
|
|
38
|
+
* Handles start at 1 and are never reused, so a handle that has been
|
|
39
|
+
* deleted names nothing else afterwards — which is what lets the
|
|
40
|
+
* binding layer's `RangeError` say which handle it was.
|
|
41
|
+
*/
|
|
42
|
+
export function createTestBackend() {
|
|
43
|
+
const participants = new Map();
|
|
44
|
+
const topics = new Map();
|
|
45
|
+
const publishers = new Map();
|
|
46
|
+
const subscribers = new Map();
|
|
47
|
+
const writers = new Map();
|
|
48
|
+
const readers = new Map();
|
|
49
|
+
let nextHandle = 1;
|
|
50
|
+
/** The next unused handle. */
|
|
51
|
+
function fresh() {
|
|
52
|
+
const handle = nextHandle;
|
|
53
|
+
nextHandle += 1;
|
|
54
|
+
return handle;
|
|
55
|
+
}
|
|
56
|
+
/** The domain a topic sits in. */
|
|
57
|
+
function domainOf(topic) {
|
|
58
|
+
return participants.get(topic.participant)?.domainId ?? -1;
|
|
59
|
+
}
|
|
60
|
+
/** A GUID that names one writer and no other. */
|
|
61
|
+
function guidOf(writer) {
|
|
62
|
+
const prefix = new Uint8Array(GUID_PREFIX_LENGTH);
|
|
63
|
+
// Octets 0..1 are the vendor id in DDSI-RTPS 2.5 §8.2.4.1; the
|
|
64
|
+
// rest of the prefix is this backend's own numbering, which is all
|
|
65
|
+
// an in-memory data path has to tell its writers apart with.
|
|
66
|
+
prefix[0] = 0x01;
|
|
67
|
+
prefix[1] = 0x20;
|
|
68
|
+
prefix[8] = (writer >>> 24) & 0xff;
|
|
69
|
+
prefix[9] = (writer >>> 16) & 0xff;
|
|
70
|
+
prefix[10] = (writer >>> 8) & 0xff;
|
|
71
|
+
prefix[11] = writer & 0xff;
|
|
72
|
+
// The entity kind of a user-defined writer with key: DDSI-RTPS 2.5
|
|
73
|
+
// §9.3.1.2, `0x02` in the low octet.
|
|
74
|
+
return makeDdsGuid(prefix, ((writer << 8) >>> 0) | 0x02);
|
|
75
|
+
}
|
|
76
|
+
return {
|
|
77
|
+
createParticipant(domainId) {
|
|
78
|
+
const handle = fresh();
|
|
79
|
+
participants.set(handle, { domainId });
|
|
80
|
+
return handle;
|
|
81
|
+
},
|
|
82
|
+
deleteParticipant(p) {
|
|
83
|
+
participants.delete(p);
|
|
84
|
+
},
|
|
85
|
+
createTopic(p, name, typeName) {
|
|
86
|
+
const handle = fresh();
|
|
87
|
+
topics.set(handle, { participant: p, name, typeName });
|
|
88
|
+
return handle;
|
|
89
|
+
},
|
|
90
|
+
deleteTopic(t) {
|
|
91
|
+
topics.delete(t);
|
|
92
|
+
},
|
|
93
|
+
createPublisher(p) {
|
|
94
|
+
const handle = fresh();
|
|
95
|
+
publishers.set(handle, { participant: p });
|
|
96
|
+
return handle;
|
|
97
|
+
},
|
|
98
|
+
createSubscriber(p) {
|
|
99
|
+
const handle = fresh();
|
|
100
|
+
subscribers.set(handle, { participant: p });
|
|
101
|
+
return handle;
|
|
102
|
+
},
|
|
103
|
+
deletePublisher(pub) {
|
|
104
|
+
publishers.delete(pub);
|
|
105
|
+
},
|
|
106
|
+
deleteSubscriber(sub) {
|
|
107
|
+
subscribers.delete(sub);
|
|
108
|
+
},
|
|
109
|
+
createDataWriter(pub, topic) {
|
|
110
|
+
const handle = fresh();
|
|
111
|
+
writers.set(handle, { publisher: pub, topic, next: 1n });
|
|
112
|
+
return handle;
|
|
113
|
+
},
|
|
114
|
+
createDataReader(sub, topic) {
|
|
115
|
+
const handle = fresh();
|
|
116
|
+
readers.set(handle, { subscriber: sub, topic, queue: [], listener: null });
|
|
117
|
+
return handle;
|
|
118
|
+
},
|
|
119
|
+
writeSample(w, xcdr2) {
|
|
120
|
+
const writer = writers.get(w);
|
|
121
|
+
const topic = writer === undefined ? undefined : topics.get(writer.topic);
|
|
122
|
+
if (writer === undefined || topic === undefined) {
|
|
123
|
+
throw new RangeError(`writer ${w} has no topic`);
|
|
124
|
+
}
|
|
125
|
+
const sequenceNumber = writer.next;
|
|
126
|
+
writer.next += 1n;
|
|
127
|
+
const info = {
|
|
128
|
+
validData: true,
|
|
129
|
+
sampleState: "not_read",
|
|
130
|
+
viewState: "new",
|
|
131
|
+
instanceState: "alive",
|
|
132
|
+
sourceTimestampNs: BigInt(Date.now()) * 1000000n,
|
|
133
|
+
sequenceNumber,
|
|
134
|
+
publicationHandle: guidOf(w),
|
|
135
|
+
instanceHandle: BigInt(w),
|
|
136
|
+
};
|
|
137
|
+
for (const [handle, reader] of readers) {
|
|
138
|
+
const other = topics.get(reader.topic);
|
|
139
|
+
if (other === undefined ||
|
|
140
|
+
other.name !== topic.name ||
|
|
141
|
+
domainOf(other) !== domainOf(topic)) {
|
|
142
|
+
continue;
|
|
143
|
+
}
|
|
144
|
+
// A copy per reader: two readers that were handed one array
|
|
145
|
+
// would see each other's writes into it.
|
|
146
|
+
reader.queue.push({ bytes: xcdr2.slice(), info });
|
|
147
|
+
// Straight through, in the writer's turn. See the file comment:
|
|
148
|
+
// the deferral is the binding layer's and this is what proves
|
|
149
|
+
// it happens.
|
|
150
|
+
reader.listener?.(handle);
|
|
151
|
+
}
|
|
152
|
+
},
|
|
153
|
+
takeSamples(r, max) {
|
|
154
|
+
const reader = readers.get(r);
|
|
155
|
+
if (reader === undefined) {
|
|
156
|
+
return [];
|
|
157
|
+
}
|
|
158
|
+
return reader.queue.splice(0, max);
|
|
159
|
+
},
|
|
160
|
+
deleteDataWriter(w) {
|
|
161
|
+
writers.delete(w);
|
|
162
|
+
},
|
|
163
|
+
deleteDataReader(r) {
|
|
164
|
+
readers.delete(r);
|
|
165
|
+
},
|
|
166
|
+
setDataAvailableListener(r, cb) {
|
|
167
|
+
const reader = readers.get(r);
|
|
168
|
+
if (reader !== undefined) {
|
|
169
|
+
reader.listener = cb;
|
|
170
|
+
}
|
|
171
|
+
},
|
|
172
|
+
};
|
|
173
|
+
}
|
package/dist/types.d.ts
ADDED
|
@@ -0,0 +1,246 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* The descriptor side-table, as the values a TypeScript program can read.
|
|
3
|
+
*
|
|
4
|
+
* DDS-TS 1.0 chapter 8. A TypeScript `interface` is erased at compile
|
|
5
|
+
* time and a plain object literal carries no run-time type identity, so
|
|
6
|
+
* the identity travels beside the data as a first-class value: one
|
|
7
|
+
* {@link DdsTypeDescriptor} per IDL type, emitted by the code generator
|
|
8
|
+
* and registered with the process-wide registry
|
|
9
|
+
* (`registerType` in `./registry.js`).
|
|
10
|
+
*
|
|
11
|
+
* The content set is deliberately close to the `TypeObject` /
|
|
12
|
+
* `MinimalTypeObject` structures of OMG X-Types 1.3 §7.3 — extensibility,
|
|
13
|
+
* member ids, key membership, type references — so that a descriptor can
|
|
14
|
+
* be turned into a `TypeObject` and back without losing anything
|
|
15
|
+
* (DDS-TS 1.0 §6.2).
|
|
16
|
+
*
|
|
17
|
+
* Nothing in this file has a run-time representation: it is types only.
|
|
18
|
+
* The functions that read these shapes live in `./registry.js`,
|
|
19
|
+
* `./dds_any.js` and `./equal.js`.
|
|
20
|
+
*/
|
|
21
|
+
/**
|
|
22
|
+
* The three extensibility kinds of OMG X-Types 1.3 §7.2.2.4.4.
|
|
23
|
+
*
|
|
24
|
+
* DDS-TS 1.0 §8.1. Spelled in lower case rather than as the
|
|
25
|
+
* `FINAL`/`APPENDABLE`/`MUTABLE` of the IDL annotation, because the
|
|
26
|
+
* descriptor is JSON-roundtrippable and these are its values.
|
|
27
|
+
*/
|
|
28
|
+
export type ExtensibilityKind = "final" | "appendable" | "mutable";
|
|
29
|
+
/**
|
|
30
|
+
* The IDL primitive types, under the names the descriptor uses.
|
|
31
|
+
*
|
|
32
|
+
* DDS-TS 1.0 §8.2. `int16` and not `short`: the IDL 4.2 §7.4.1.4.4.2
|
|
33
|
+
* explicit-width spellings are unambiguous, and a descriptor that said
|
|
34
|
+
* `long` would be read differently by a C consumer than by a Java one.
|
|
35
|
+
* `longDouble` is the sixteen-octet carrier of §8.6, not a `number`.
|
|
36
|
+
*
|
|
37
|
+
* `int8` is one name past the list §8.2 gives. IDL 4.2 §7.4.1.4.4.2
|
|
38
|
+
* declares `int8` alongside `uint8`, the list names `octet` — which is
|
|
39
|
+
* `uint8` — and names nothing for the signed one. A generator meeting
|
|
40
|
+
* `int8 v;` has three choices: write `octet`, which is the wrong
|
|
41
|
+
* signedness; refuse the member, which drops a type IDL admits; or
|
|
42
|
+
* carry a name the list does not have. The third is the only one that
|
|
43
|
+
* does not lose something, and it is additive: every value §8.2 lists
|
|
44
|
+
* still means what §8.2 says.
|
|
45
|
+
*/
|
|
46
|
+
export type PrimitiveName = "boolean" | "octet" | "int8" | "int16" | "uint16" | "int32" | "uint32" | "int64" | "uint64" | "float" | "double" | "longDouble" | "char" | "wchar";
|
|
47
|
+
/**
|
|
48
|
+
* The type of one member, in side-table form.
|
|
49
|
+
*
|
|
50
|
+
* DDS-TS 1.0 §8.2. A discriminated union over `kind`, which is what lets
|
|
51
|
+
* a consumer walk a descriptor graph with the compiler checking every
|
|
52
|
+
* arm.
|
|
53
|
+
*
|
|
54
|
+
* The `ref` kind carries the **qualified IDL name** of the referenced
|
|
55
|
+
* type and not a descriptor reference. That rule is normative and it
|
|
56
|
+
* buys three things: mutually-recursive struct and union references that
|
|
57
|
+
* no source-line order could initialise, a value graph with no cycles in
|
|
58
|
+
* it (so `JSON.stringify` round-trips a descriptor), and the same shape
|
|
59
|
+
* OMG X-Types 1.3 uses for `TypeIdentifier` lookup. Resolution happens at
|
|
60
|
+
* use time, through `lookupType(name)`.
|
|
61
|
+
*
|
|
62
|
+
* The `any` kind carries no name at all: the run-time identity of an IDL
|
|
63
|
+
* `any` is by construction not known statically, and a consumer that
|
|
64
|
+
* reaches one defers to the boxed value's `typeId` (DDS-TS 1.0 §7.3.4).
|
|
65
|
+
*/
|
|
66
|
+
export type DdsTypeRef = {
|
|
67
|
+
kind: "primitive";
|
|
68
|
+
name: PrimitiveName;
|
|
69
|
+
}
|
|
70
|
+
/** A bitset member or a bitmask bit value; `width` is 1..64. */
|
|
71
|
+
| {
|
|
72
|
+
kind: "bitfield";
|
|
73
|
+
width: number;
|
|
74
|
+
} | {
|
|
75
|
+
kind: "string";
|
|
76
|
+
bound?: number | undefined;
|
|
77
|
+
wide: boolean;
|
|
78
|
+
} | {
|
|
79
|
+
kind: "sequence";
|
|
80
|
+
element: DdsTypeRef;
|
|
81
|
+
bound?: number | undefined;
|
|
82
|
+
} | {
|
|
83
|
+
kind: "array";
|
|
84
|
+
element: DdsTypeRef;
|
|
85
|
+
lengths: ReadonlyArray<number>;
|
|
86
|
+
} | {
|
|
87
|
+
kind: "map";
|
|
88
|
+
key: DdsTypeRef;
|
|
89
|
+
value: DdsTypeRef;
|
|
90
|
+
bound?: number | undefined;
|
|
91
|
+
}
|
|
92
|
+
/** A qualified IDL name; resolve through `lookupType`. */
|
|
93
|
+
| {
|
|
94
|
+
kind: "ref";
|
|
95
|
+
name: string;
|
|
96
|
+
} | {
|
|
97
|
+
kind: "any";
|
|
98
|
+
};
|
|
99
|
+
/**
|
|
100
|
+
* One member of a type, with the annotation metadata that governs it.
|
|
101
|
+
*
|
|
102
|
+
* DDS-TS 1.0 §8.3. `unit`, `min` and `max` are carried for a consumer
|
|
103
|
+
* that wants them and are **not** enforced by the type guard: a guard
|
|
104
|
+
* that rejected an out-of-range value would refuse a sample the wire
|
|
105
|
+
* accepted, which is a different contract from the one the IDL wrote.
|
|
106
|
+
*/
|
|
107
|
+
export interface DdsMemberDescriptor {
|
|
108
|
+
/** The IDL member name, as declared. */
|
|
109
|
+
readonly name: string;
|
|
110
|
+
/** The effective member id — `@id`, `@autoid`, or the position. */
|
|
111
|
+
readonly id: number;
|
|
112
|
+
/** What the member holds. */
|
|
113
|
+
readonly type: DdsTypeRef;
|
|
114
|
+
/** `@key`. */
|
|
115
|
+
readonly key: boolean;
|
|
116
|
+
/** `@optional`. */
|
|
117
|
+
readonly optional: boolean;
|
|
118
|
+
/** `@must_understand`. */
|
|
119
|
+
readonly mustUnderstand: boolean;
|
|
120
|
+
/** `@default(V)`, or an enumerator's ordinal, or a bit position. */
|
|
121
|
+
readonly default?: unknown;
|
|
122
|
+
/** `@unit("…")` — documentation, never validated. */
|
|
123
|
+
readonly unit?: string | undefined;
|
|
124
|
+
/** `@min(V)` — documentation, never validated. */
|
|
125
|
+
readonly min?: unknown;
|
|
126
|
+
/** `@max(V)` — documentation, never validated. */
|
|
127
|
+
readonly max?: unknown;
|
|
128
|
+
/** `@hashid("…")`, the string the id was hashed from. */
|
|
129
|
+
readonly hashid?: string | undefined;
|
|
130
|
+
/**
|
|
131
|
+
* The case labels that select this member. Union descriptors only
|
|
132
|
+
* (DDS-TS 1.0 §7.5.1); `undefined` marks the `default:` case.
|
|
133
|
+
*
|
|
134
|
+
* `bigint` is one member past the element type §8.3 gives. A
|
|
135
|
+
* `union U switch (long long)` is legal IDL 4.2 and its case labels
|
|
136
|
+
* reach past 2^53, where a `number` holds two of them as one value;
|
|
137
|
+
* the arm's `discriminator` property is a `bigint` for that reason
|
|
138
|
+
* (§7.5), and a label list that could not carry one would disagree
|
|
139
|
+
* with the type it describes. Additive: every label §8.3 admits is
|
|
140
|
+
* still admitted.
|
|
141
|
+
*/
|
|
142
|
+
readonly labels?: ReadonlyArray<number | string | boolean | bigint> | undefined;
|
|
143
|
+
/**
|
|
144
|
+
* A map key whose JavaScript equality is not value equality — a
|
|
145
|
+
* struct, an array, a sequence (DDS-TS 1.0 §7.9.1, diagnostic
|
|
146
|
+
* `DDS-TS-W003`). A `Map` keyed by one compares its keys by
|
|
147
|
+
* reference; the flag is what tells a consumer to compare with
|
|
148
|
+
* `equalKey` instead.
|
|
149
|
+
*/
|
|
150
|
+
readonly keyEqualityHazard?: boolean | undefined;
|
|
151
|
+
/**
|
|
152
|
+
* The member is the base structure, the first member `parent` (XTypes
|
|
153
|
+
* 1.3 §7.5.1.4.1.3): its type is the `ref` to the base, and the members
|
|
154
|
+
* of that type are members of this one for `getKey` and `withDefaults`.
|
|
155
|
+
*/
|
|
156
|
+
readonly base?: boolean | undefined;
|
|
157
|
+
}
|
|
158
|
+
/**
|
|
159
|
+
* Which IDL declaration a descriptor came from.
|
|
160
|
+
*
|
|
161
|
+
* DDS-TS 1.0 §8.3. `exception` and not a flag on `struct`: an exception
|
|
162
|
+
* maps to the same shape but crosses an Operations-Profile boundary
|
|
163
|
+
* differently, and a consumer switching on `kind` should see the
|
|
164
|
+
* difference.
|
|
165
|
+
*/
|
|
166
|
+
export type DescriptorKind = "struct" | "union" | "enum" | "bitset" | "bitmask" | "exception" | "alias" | "interface";
|
|
167
|
+
/**
|
|
168
|
+
* One IDL type, as the value that carries its metadata.
|
|
169
|
+
*
|
|
170
|
+
* DDS-TS 1.0 §8.3. The generated `export const <Type>Type` constant.
|
|
171
|
+
*
|
|
172
|
+
* `typeGuard` is the one member that is not data: a function the
|
|
173
|
+
* generator emits beside the descriptor, checking the presence and type
|
|
174
|
+
* of every **required** member. It does not check `@unit`, `@min` or
|
|
175
|
+
* `@max`, and it does not require optional members.
|
|
176
|
+
*/
|
|
177
|
+
export interface DdsTypeDescriptor<T = unknown> {
|
|
178
|
+
/** Which IDL declaration this is. */
|
|
179
|
+
readonly kind: DescriptorKind;
|
|
180
|
+
/** The qualified IDL name — `robot::Pose`, not `Pose`. */
|
|
181
|
+
readonly name: string;
|
|
182
|
+
/** `@final` / `@appendable` / `@mutable`, resolved. */
|
|
183
|
+
readonly extensibility: ExtensibilityKind;
|
|
184
|
+
/** `@nested` — whether the type may stand as a topic type. */
|
|
185
|
+
readonly nested: boolean;
|
|
186
|
+
/** `@topic("…")`, where the IDL bound one. */
|
|
187
|
+
readonly topic?: string | undefined;
|
|
188
|
+
/** `@autoid(SEQUENTIAL|HASH)`, where the IDL set one. */
|
|
189
|
+
readonly autoid?: "sequential" | "hash" | undefined;
|
|
190
|
+
/**
|
|
191
|
+
* `@bit_bound(N)`. An enumeration's effective bound (default 32), a
|
|
192
|
+
* bitmask's, or a bitset's total width.
|
|
193
|
+
*/
|
|
194
|
+
readonly bitBound?: number | undefined;
|
|
195
|
+
/** A union with a `default:` case (DDS-TS 1.0 §7.5.1). */
|
|
196
|
+
readonly hasDefault?: boolean | undefined;
|
|
197
|
+
/**
|
|
198
|
+
* A union with no `default:` case whose listed cases do not exhaust
|
|
199
|
+
* the discriminator (DDS-TS 1.0 §7.5.2, diagnostic `DDS-TS-W004`).
|
|
200
|
+
*/
|
|
201
|
+
readonly hasImplicitDefault?: boolean | undefined;
|
|
202
|
+
/**
|
|
203
|
+
* The members, in declaration order. Empty for an enumeration's
|
|
204
|
+
* holder-only forms and for an `interface`, whose operations live in
|
|
205
|
+
* a Service Descriptor instead.
|
|
206
|
+
*/
|
|
207
|
+
readonly fields: ReadonlyArray<DdsMemberDescriptor>;
|
|
208
|
+
/** The emitted `is<Type>` predicate. */
|
|
209
|
+
readonly typeGuard: (v: unknown) => v is T;
|
|
210
|
+
}
|
|
211
|
+
/**
|
|
212
|
+
* The marker every generated IDL `exception` interface extends.
|
|
213
|
+
*
|
|
214
|
+
* DDS-TS 1.0 §8.3.1. A property and not a class: TypeScript's structural
|
|
215
|
+
* typing has nothing else that separates an exception payload from an
|
|
216
|
+
* ordinary struct at compile time, and this PSM emits no classes.
|
|
217
|
+
*/
|
|
218
|
+
export interface DdsException {
|
|
219
|
+
readonly __dds_exception: true;
|
|
220
|
+
}
|
|
221
|
+
/**
|
|
222
|
+
* A boxed IDL `any`: a value together with the identity of its type.
|
|
223
|
+
*
|
|
224
|
+
* DDS-TS 1.0 §8.4 and §7.3.4. `typeId` is the qualified IDL name and
|
|
225
|
+
* indexes the registry, so `lookupType(box.typeId)` answers with the
|
|
226
|
+
* descriptor that describes `value`.
|
|
227
|
+
*
|
|
228
|
+
* Construct with `boxAny`, narrow with `unboxAny` — both in
|
|
229
|
+
* `./dds_any.js`. Reading `value` directly is legal and gives back
|
|
230
|
+
* `unknown`, which is the point: an IDL `any` mapped to TypeScript `any`
|
|
231
|
+
* would switch the checking off rather than carry the identity.
|
|
232
|
+
*/
|
|
233
|
+
export interface DdsAny {
|
|
234
|
+
/** The qualified IDL name of the boxed value's type. */
|
|
235
|
+
readonly typeId: string;
|
|
236
|
+
/** The payload. `unknown`, and narrowed through the descriptor. */
|
|
237
|
+
readonly value: unknown;
|
|
238
|
+
}
|
|
239
|
+
/**
|
|
240
|
+
* The type a descriptor guards.
|
|
241
|
+
*
|
|
242
|
+
* DDS-TS 1.0 §8.7. `GuardedTypeOf<typeof PoseType>` is `Pose`, which is
|
|
243
|
+
* what lets `isOneOf` state the union of a list of exception
|
|
244
|
+
* descriptors as one narrowed type.
|
|
245
|
+
*/
|
|
246
|
+
export type GuardedTypeOf<D> = D extends DdsTypeDescriptor<infer T> ? T : never;
|
package/dist/types.js
ADDED