@dusted/anqst 1.5.0 → 1.6.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (84) hide show
  1. package/README.md +45 -4
  2. package/dist/src/app.js +78 -24
  3. package/dist/src/base93.js +0 -72
  4. package/dist/src/boundary-codec-analysis.js +468 -0
  5. package/dist/src/boundary-codec-leaves.js +602 -0
  6. package/dist/src/boundary-codec-model.js +77 -0
  7. package/dist/src/boundary-codec-plan.js +522 -0
  8. package/dist/src/boundary-codec-render.js +1738 -0
  9. package/dist/src/boundary-codecs.js +174 -0
  10. package/dist/src/emit.js +1960 -207
  11. package/dist/src/layout.js +9 -3
  12. package/dist/src/program.js +1 -1
  13. package/dist/src/project.js +3 -3
  14. package/package.json +2 -2
  15. package/spec/AnQst-Spec-DSL.d.ts +22 -24
  16. package/dist/src/codecgenerators/basecodecemitters/bigint-qint64/decoder.js +0 -35
  17. package/dist/src/codecgenerators/basecodecemitters/bigint-qint64/encoder.js +0 -36
  18. package/dist/src/codecgenerators/basecodecemitters/bigint-quint64/decoder.js +0 -26
  19. package/dist/src/codecgenerators/basecodecemitters/bigint-quint64/encoder.js +0 -38
  20. package/dist/src/codecgenerators/basecodecemitters/binary-blob/decoder.js +0 -28
  21. package/dist/src/codecgenerators/basecodecemitters/binary-blob/encoder.js +0 -34
  22. package/dist/src/codecgenerators/basecodecemitters/binary-buffer/decoder.js +0 -29
  23. package/dist/src/codecgenerators/basecodecemitters/binary-buffer/encoder.js +0 -36
  24. package/dist/src/codecgenerators/basecodecemitters/binary-float32Array/decoder.js +0 -46
  25. package/dist/src/codecgenerators/basecodecemitters/binary-float32Array/encoder.js +0 -49
  26. package/dist/src/codecgenerators/basecodecemitters/binary-float64Array/decoder.js +0 -46
  27. package/dist/src/codecgenerators/basecodecemitters/binary-float64Array/encoder.js +0 -47
  28. package/dist/src/codecgenerators/basecodecemitters/binary-int16Array/decoder.js +0 -46
  29. package/dist/src/codecgenerators/basecodecemitters/binary-int16Array/encoder.js +0 -49
  30. package/dist/src/codecgenerators/basecodecemitters/binary-int32Array/decoder.js +0 -50
  31. package/dist/src/codecgenerators/basecodecemitters/binary-int32Array/encoder.js +0 -52
  32. package/dist/src/codecgenerators/basecodecemitters/binary-int8Array/decoder.js +0 -38
  33. package/dist/src/codecgenerators/basecodecemitters/binary-int8Array/encoder.js +0 -44
  34. package/dist/src/codecgenerators/basecodecemitters/binary-typedArray/decoder.js +0 -33
  35. package/dist/src/codecgenerators/basecodecemitters/binary-typedArray/encoder.js +0 -34
  36. package/dist/src/codecgenerators/basecodecemitters/binary-uint16Array/decoder.js +0 -46
  37. package/dist/src/codecgenerators/basecodecemitters/binary-uint16Array/encoder.js +0 -49
  38. package/dist/src/codecgenerators/basecodecemitters/binary-uint32Array/decoder.js +0 -46
  39. package/dist/src/codecgenerators/basecodecemitters/binary-uint32Array/encoder.js +0 -49
  40. package/dist/src/codecgenerators/basecodecemitters/binary-uint8Array/decoder.js +0 -28
  41. package/dist/src/codecgenerators/basecodecemitters/binary-uint8Array/encoder.js +0 -34
  42. package/dist/src/codecgenerators/basecodecemitters/boolean/decoder.js +0 -34
  43. package/dist/src/codecgenerators/basecodecemitters/boolean/encoder.js +0 -40
  44. package/dist/src/codecgenerators/basecodecemitters/dynamic-json/decoder.js +0 -43
  45. package/dist/src/codecgenerators/basecodecemitters/dynamic-json/encoder.js +0 -45
  46. package/dist/src/codecgenerators/basecodecemitters/dynamic-object/decoder.js +0 -44
  47. package/dist/src/codecgenerators/basecodecemitters/dynamic-object/encoder.js +0 -46
  48. package/dist/src/codecgenerators/basecodecemitters/integer-int16/decoder.js +0 -32
  49. package/dist/src/codecgenerators/basecodecemitters/integer-int16/encoder.js +0 -43
  50. package/dist/src/codecgenerators/basecodecemitters/integer-int32/decoder.js +0 -26
  51. package/dist/src/codecgenerators/basecodecemitters/integer-int32/encoder.js +0 -37
  52. package/dist/src/codecgenerators/basecodecemitters/integer-int8/decoder.js +0 -26
  53. package/dist/src/codecgenerators/basecodecemitters/integer-int8/encoder.js +0 -37
  54. package/dist/src/codecgenerators/basecodecemitters/integer-qint16/decoder.js +0 -36
  55. package/dist/src/codecgenerators/basecodecemitters/integer-qint16/encoder.js +0 -36
  56. package/dist/src/codecgenerators/basecodecemitters/integer-qint32/decoder.js +0 -25
  57. package/dist/src/codecgenerators/basecodecemitters/integer-qint32/encoder.js +0 -36
  58. package/dist/src/codecgenerators/basecodecemitters/integer-qint8/decoder.js +0 -36
  59. package/dist/src/codecgenerators/basecodecemitters/integer-qint8/encoder.js +0 -36
  60. package/dist/src/codecgenerators/basecodecemitters/integer-quint16/decoder.js +0 -26
  61. package/dist/src/codecgenerators/basecodecemitters/integer-quint16/encoder.js +0 -38
  62. package/dist/src/codecgenerators/basecodecemitters/integer-quint32/decoder.js +0 -27
  63. package/dist/src/codecgenerators/basecodecemitters/integer-quint32/encoder.js +0 -39
  64. package/dist/src/codecgenerators/basecodecemitters/integer-quint8/decoder.js +0 -26
  65. package/dist/src/codecgenerators/basecodecemitters/integer-quint8/encoder.js +0 -38
  66. package/dist/src/codecgenerators/basecodecemitters/integer-uint16/decoder.js +0 -30
  67. package/dist/src/codecgenerators/basecodecemitters/integer-uint16/encoder.js +0 -42
  68. package/dist/src/codecgenerators/basecodecemitters/integer-uint32/decoder.js +0 -31
  69. package/dist/src/codecgenerators/basecodecemitters/integer-uint32/encoder.js +0 -43
  70. package/dist/src/codecgenerators/basecodecemitters/integer-uint8/decoder.js +0 -30
  71. package/dist/src/codecgenerators/basecodecemitters/integer-uint8/encoder.js +0 -40
  72. package/dist/src/codecgenerators/basecodecemitters/number/decoder.js +0 -26
  73. package/dist/src/codecgenerators/basecodecemitters/number/encoder.js +0 -38
  74. package/dist/src/codecgenerators/basecodecemitters/shared/comments.js +0 -13
  75. package/dist/src/codecgenerators/basecodecemitters/shared/contracts.js +0 -2
  76. package/dist/src/codecgenerators/basecodecemitters/shared/fixedwidth.js +0 -53
  77. package/dist/src/codecgenerators/basecodecemitters/shared/index.js +0 -21
  78. package/dist/src/codecgenerators/basecodecemitters/shared/positionalBase93.js +0 -48
  79. package/dist/src/codecgenerators/basecodecemitters/shared/rawbytes.js +0 -30
  80. package/dist/src/codecgenerators/basecodecemitters/string/decoder.js +0 -43
  81. package/dist/src/codecgenerators/basecodecemitters/string/encoder.js +0 -43
  82. package/dist/src/codecgenerators/basecodecemitters/stringArray/decoder.js +0 -80
  83. package/dist/src/codecgenerators/basecodecemitters/stringArray/encoder.js +0 -57
  84. package/dist/src/structured-top-level-codecs.js +0 -1305
@@ -0,0 +1,468 @@
1
+ "use strict";
2
+ var __importDefault = (this && this.__importDefault) || function (mod) {
3
+ return (mod && mod.__esModule) ? mod : { "default": mod };
4
+ };
5
+ Object.defineProperty(exports, "__esModule", { value: true });
6
+ exports.BoundaryTransportAnalyzer = void 0;
7
+ const typescript_1 = __importDefault(require("typescript"));
8
+ const errors_1 = require("./errors");
9
+ const boundary_codec_model_1 = require("./boundary-codec-model");
10
+ const boundary_codec_leaves_1 = require("./boundary-codec-leaves");
11
+ const BUILTIN_TYPE_REFS = new Set(["Array", "ReadonlyArray", "Record", "Map", "Partial", "Promise"]);
12
+ function isStringLikeUnion(node) {
13
+ return node.types.every((part) => {
14
+ if (typescript_1.default.isLiteralTypeNode(part) && typescript_1.default.isStringLiteral(part.literal))
15
+ return true;
16
+ return part.kind === typescript_1.default.SyntaxKind.StringKeyword;
17
+ });
18
+ }
19
+ function isBooleanLikeUnion(node) {
20
+ return node.types.every((part) => {
21
+ if (typescript_1.default.isLiteralTypeNode(part)) {
22
+ return part.literal.kind === typescript_1.default.SyntaxKind.TrueKeyword || part.literal.kind === typescript_1.default.SyntaxKind.FalseKeyword;
23
+ }
24
+ return part.kind === typescript_1.default.SyntaxKind.BooleanKeyword;
25
+ });
26
+ }
27
+ function isNumberLikeUnion(node) {
28
+ return node.types.every((part) => {
29
+ if (typescript_1.default.isLiteralTypeNode(part) && typescript_1.default.isNumericLiteral(part.literal))
30
+ return true;
31
+ return part.kind === typescript_1.default.SyntaxKind.NumberKeyword || part.kind === typescript_1.default.SyntaxKind.BigIntKeyword;
32
+ });
33
+ }
34
+ function filterNullishUnionParts(types) {
35
+ return types.filter((part) => part.kind !== typescript_1.default.SyntaxKind.NullKeyword && part.kind !== typescript_1.default.SyntaxKind.UndefinedKeyword);
36
+ }
37
+ function collectFiniteStringLiterals(node) {
38
+ const values = [];
39
+ for (const part of node.types) {
40
+ if (!typescript_1.default.isLiteralTypeNode(part) || !typescript_1.default.isStringLiteral(part.literal))
41
+ return null;
42
+ values.push(part.literal.text);
43
+ }
44
+ return values;
45
+ }
46
+ function collectFiniteBooleanLiterals(node) {
47
+ const values = [];
48
+ for (const part of node.types) {
49
+ if (!typescript_1.default.isLiteralTypeNode(part))
50
+ return null;
51
+ if (part.literal.kind === typescript_1.default.SyntaxKind.TrueKeyword) {
52
+ values.push(true);
53
+ continue;
54
+ }
55
+ if (part.literal.kind === typescript_1.default.SyntaxKind.FalseKeyword) {
56
+ values.push(false);
57
+ continue;
58
+ }
59
+ return null;
60
+ }
61
+ return values;
62
+ }
63
+ function collectFiniteNumberLiterals(node) {
64
+ const values = [];
65
+ for (const part of node.types) {
66
+ if (!typescript_1.default.isLiteralTypeNode(part) || !typescript_1.default.isNumericLiteral(part.literal))
67
+ return null;
68
+ values.push(Number(part.literal.text));
69
+ }
70
+ return values;
71
+ }
72
+ function finiteDomainSymbolForValue(value) {
73
+ if (typeof value === "boolean")
74
+ return value ? "True" : "False";
75
+ if (typeof value === "number") {
76
+ const text = Number.isInteger(value) ? `${value}` : `${value}`.replace(/\./g, "_");
77
+ return (0, boundary_codec_model_1.sanitizeIdentifier)(`Value_${text.replace(/-/g, "neg_")}`);
78
+ }
79
+ const trimmed = value.trim();
80
+ if (trimmed.length === 0)
81
+ return "Empty";
82
+ const direct = (0, boundary_codec_model_1.sanitizeIdentifier)(trimmed);
83
+ return direct.length > 0 ? direct : "Value";
84
+ }
85
+ function buildFiniteDomain(primitive, values) {
86
+ const seen = new Set();
87
+ const variants = [];
88
+ for (const value of values) {
89
+ const key = `${typeof value}:${String(value)}`;
90
+ if (seen.has(key))
91
+ continue;
92
+ seen.add(key);
93
+ variants.push({
94
+ code: variants.length,
95
+ symbolicName: finiteDomainSymbolForValue(value),
96
+ tsLiteralText: typeof value === "string" ? JSON.stringify(value) : `${value}`,
97
+ value
98
+ });
99
+ }
100
+ return { primitive, variants };
101
+ }
102
+ function unsupported(path, typeText, reason) {
103
+ throw new errors_1.VerifyError(`Boundary codec planning failed for '${path.join(".") || typeText}': ${reason} (${typeText}).`);
104
+ }
105
+ class BoundaryTransportAnalyzer {
106
+ constructor(spec) {
107
+ this.spec = spec;
108
+ this.declNodes = new Map();
109
+ this.namedNodes = new Map();
110
+ for (const decl of this.collectDecls()) {
111
+ const node = (0, boundary_codec_model_1.parseTypeDeclNode)(decl.nodeText);
112
+ if (node) {
113
+ this.declNodes.set(decl.name, node);
114
+ }
115
+ }
116
+ }
117
+ analyzeTypeText(typeText, path) {
118
+ const rootNode = (0, boundary_codec_model_1.parseTypeNodeFromText)(typeText);
119
+ const root = this.resolveTypeNode(rootNode, typeText, path, [], this.defaultIdentityParts(rootNode, typeText, path));
120
+ return {
121
+ typeText,
122
+ tsTypeText: (0, boundary_codec_model_1.stripAnQstType)(typeText),
123
+ root,
124
+ summary: summarizeAnalysis(root)
125
+ };
126
+ }
127
+ collectDecls() {
128
+ const out = new Map();
129
+ for (const decl of this.spec.namespaceTypeDecls)
130
+ out.set(decl.name, decl);
131
+ for (const decl of this.spec.importedTypeDecls.values())
132
+ out.set(decl.name, decl);
133
+ return [...out.values()];
134
+ }
135
+ nodeMeta(typeText, path, identityParts) {
136
+ const normalizedParts = identityParts.filter((part) => part.trim().length > 0);
137
+ return {
138
+ typeText,
139
+ path,
140
+ typeIdentityKey: normalizedParts.join("::") || (0, boundary_codec_model_1.stripAnQstType)(typeText).trim(),
141
+ cppNameHintParts: normalizedParts.length > 0 ? normalizedParts : (path.length > 0 ? path : [(0, boundary_codec_model_1.stripAnQstType)(typeText).trim() || "AnonymousType"])
142
+ };
143
+ }
144
+ defaultIdentityParts(node, typeText, path) {
145
+ if (typescript_1.default.isTypeReferenceNode(node)) {
146
+ const name = (0, boundary_codec_model_1.qNameText)(node.typeName);
147
+ if (!name.startsWith("AnQst.Type.") && !BUILTIN_TYPE_REFS.has(name)) {
148
+ return [name];
149
+ }
150
+ }
151
+ return path.length > 0 ? [...path] : [(0, boundary_codec_model_1.stripAnQstType)(typeText).trim() || "AnonymousType"];
152
+ }
153
+ createFiniteDomainAnalysis(typeText, path, identityParts, domain) {
154
+ return {
155
+ nodeKind: "finite-domain",
156
+ ...this.nodeMeta(typeText, path, identityParts),
157
+ domain
158
+ };
159
+ }
160
+ createStructAnalysis(typeText, path, members, stack, identityParts) {
161
+ const fields = members
162
+ .filter((member) => {
163
+ return typescript_1.default.isPropertySignature(member) && !!member.type && typescript_1.default.isIdentifier(member.name);
164
+ })
165
+ .map((member) => {
166
+ const fieldPath = [...path, member.name.text];
167
+ const fieldIdentityParts = [...identityParts, member.name.text];
168
+ const child = this.resolveTypeNode(member.type, member.type.getText(), fieldPath, stack, fieldIdentityParts);
169
+ return {
170
+ name: member.name.text,
171
+ optional: !!member.questionToken,
172
+ typeText: member.type.getText(),
173
+ path: fieldPath,
174
+ typeIdentityKey: child.typeIdentityKey,
175
+ cppNameHintParts: child.cppNameHintParts,
176
+ reconstructionKey: member.name.text,
177
+ node: child
178
+ };
179
+ });
180
+ return {
181
+ nodeKind: "struct",
182
+ ...this.nodeMeta(typeText, path, identityParts),
183
+ fields,
184
+ reconstruction: "object"
185
+ };
186
+ }
187
+ resolveNamedReference(name, decl) {
188
+ const existing = this.namedNodes.get(name);
189
+ if (existing) {
190
+ return existing;
191
+ }
192
+ const placeholder = {
193
+ nodeKind: "named",
194
+ ...this.nodeMeta(name, [name], [name]),
195
+ name,
196
+ target: null
197
+ };
198
+ this.namedNodes.set(name, placeholder);
199
+ placeholder.target = typescript_1.default.isInterfaceDeclaration(decl)
200
+ ? this.createStructAnalysis(name, [name], decl.members, [name], [name])
201
+ : this.resolveTypeNode(decl.type, name, [name], [name], [name]);
202
+ return placeholder;
203
+ }
204
+ resolveTypeNode(node, typeText, path, stack, identityParts) {
205
+ if (typescript_1.default.isParenthesizedTypeNode(node)) {
206
+ return this.resolveTypeNode(node.type, typeText, path, stack, identityParts);
207
+ }
208
+ if (typescript_1.default.isTypeLiteralNode(node)) {
209
+ return this.createStructAnalysis(typeText, path, node.members, stack, identityParts);
210
+ }
211
+ if (typescript_1.default.isArrayTypeNode(node)) {
212
+ return {
213
+ nodeKind: "array",
214
+ ...this.nodeMeta(typeText, path, identityParts),
215
+ elementTypeText: node.elementType.getText(),
216
+ element: this.resolveTypeNode(node.elementType, node.elementType.getText(), [...path, "Item"], stack, [...identityParts, "Item"]),
217
+ requiresCountMetadata: true,
218
+ reconstruction: "array"
219
+ };
220
+ }
221
+ if (typescript_1.default.isTupleTypeNode(node)) {
222
+ unsupported(path, typeText, "tuple transport is not supported by the whole-boundary planner");
223
+ }
224
+ if (typescript_1.default.isLiteralTypeNode(node)) {
225
+ if (typescript_1.default.isStringLiteral(node.literal)) {
226
+ return this.createFiniteDomainAnalysis(typeText, path, identityParts, buildFiniteDomain("string", [node.literal.text]));
227
+ }
228
+ if (typescript_1.default.isNumericLiteral(node.literal)) {
229
+ return this.createFiniteDomainAnalysis(typeText, path, identityParts, buildFiniteDomain("number", [Number(node.literal.text)]));
230
+ }
231
+ if (node.literal.kind === typescript_1.default.SyntaxKind.TrueKeyword || node.literal.kind === typescript_1.default.SyntaxKind.FalseKeyword) {
232
+ return this.createFiniteDomainAnalysis(typeText, path, identityParts, buildFiniteDomain("boolean", [node.literal.kind === typescript_1.default.SyntaxKind.TrueKeyword]));
233
+ }
234
+ }
235
+ if (typescript_1.default.isUnionTypeNode(node)) {
236
+ const filtered = filterNullishUnionParts(node.types);
237
+ if (filtered.length !== node.types.length) {
238
+ unsupported(path, typeText, "nullish unions are not supported; use explicit optional members instead");
239
+ }
240
+ const finiteStringVariants = collectFiniteStringLiterals(node);
241
+ if (finiteStringVariants) {
242
+ return this.createFiniteDomainAnalysis(typeText, path, identityParts, buildFiniteDomain("string", finiteStringVariants));
243
+ }
244
+ const finiteBooleanVariants = collectFiniteBooleanLiterals(node);
245
+ if (finiteBooleanVariants) {
246
+ return this.createFiniteDomainAnalysis(typeText, path, identityParts, buildFiniteDomain("boolean", finiteBooleanVariants));
247
+ }
248
+ const finiteNumberVariants = collectFiniteNumberLiterals(node);
249
+ if (finiteNumberVariants) {
250
+ return this.createFiniteDomainAnalysis(typeText, path, identityParts, buildFiniteDomain("number", finiteNumberVariants));
251
+ }
252
+ if (isStringLikeUnion(node)) {
253
+ return {
254
+ nodeKind: "leaf",
255
+ ...this.nodeMeta(typeText, path, identityParts),
256
+ leaf: (0, boundary_codec_leaves_1.resolveLeafCapability)("string", "string"),
257
+ fixedWidth: false
258
+ };
259
+ }
260
+ if (isBooleanLikeUnion(node)) {
261
+ return {
262
+ nodeKind: "leaf",
263
+ ...this.nodeMeta(typeText, path, identityParts),
264
+ leaf: (0, boundary_codec_leaves_1.resolveLeafCapability)("boolean", "boolean"),
265
+ fixedWidth: true
266
+ };
267
+ }
268
+ if (isNumberLikeUnion(node)) {
269
+ return {
270
+ nodeKind: "leaf",
271
+ ...this.nodeMeta(typeText, path, identityParts),
272
+ leaf: (0, boundary_codec_leaves_1.resolveLeafCapability)("number", "number"),
273
+ fixedWidth: true
274
+ };
275
+ }
276
+ unsupported(path, typeText, "union transport is only supported for string, boolean, or number-like unions");
277
+ }
278
+ if (typescript_1.default.isTypeReferenceNode(node)) {
279
+ const name = (0, boundary_codec_model_1.qNameText)(node.typeName);
280
+ const rawText = node.getText();
281
+ if (name === "Array" || name === "ReadonlyArray") {
282
+ const arg = node.typeArguments?.[0];
283
+ if (!arg) {
284
+ unsupported(path, rawText, "array type is missing its element type");
285
+ }
286
+ return {
287
+ nodeKind: "array",
288
+ ...this.nodeMeta(typeText, path, identityParts),
289
+ elementTypeText: arg.getText(),
290
+ element: this.resolveTypeNode(arg, arg.getText(), [...path, "Item"], stack, [...identityParts, "Item"]),
291
+ requiresCountMetadata: true,
292
+ reconstruction: "array"
293
+ };
294
+ }
295
+ if (name === "Record" || name === "Map") {
296
+ const leaf = (0, boundary_codec_leaves_1.resolveLeafCapability)("object", "object");
297
+ return {
298
+ nodeKind: "leaf",
299
+ ...this.nodeMeta(typeText, path, identityParts),
300
+ leaf: leaf,
301
+ fixedWidth: false
302
+ };
303
+ }
304
+ if (name === "Partial") {
305
+ unsupported(path, rawText, "generic Partial<T> transport is not supported");
306
+ }
307
+ if (name === "Promise") {
308
+ unsupported(path, rawText, "Promise transport is not supported");
309
+ }
310
+ if (rawText.trim() === "AnQst.Type.stringArray") {
311
+ const leaf = (0, boundary_codec_leaves_1.resolveLeafCapability)("string", "string");
312
+ return {
313
+ nodeKind: "array",
314
+ ...this.nodeMeta(typeText, path, identityParts),
315
+ elementTypeText: "string",
316
+ element: {
317
+ nodeKind: "leaf",
318
+ ...this.nodeMeta("string", [...path, "Item"], [...identityParts, "Item"]),
319
+ leaf: leaf,
320
+ fixedWidth: false
321
+ },
322
+ requiresCountMetadata: true,
323
+ reconstruction: "array"
324
+ };
325
+ }
326
+ const leaf = (0, boundary_codec_leaves_1.resolveLeafCapability)(rawText, name);
327
+ if (leaf) {
328
+ return {
329
+ nodeKind: "leaf",
330
+ ...this.nodeMeta(typeText, path, identityParts),
331
+ leaf,
332
+ fixedWidth: leaf.fixedByteWidth !== null
333
+ };
334
+ }
335
+ const decl = this.declNodes.get(name);
336
+ if (decl) {
337
+ return this.resolveNamedReference(name, decl);
338
+ }
339
+ }
340
+ switch (node.kind) {
341
+ case typescript_1.default.SyntaxKind.StringKeyword:
342
+ return {
343
+ nodeKind: "leaf",
344
+ ...this.nodeMeta(typeText, path, identityParts),
345
+ leaf: (0, boundary_codec_leaves_1.resolveLeafCapability)("string", "string"),
346
+ fixedWidth: false
347
+ };
348
+ case typescript_1.default.SyntaxKind.BooleanKeyword:
349
+ return {
350
+ nodeKind: "leaf",
351
+ ...this.nodeMeta(typeText, path, identityParts),
352
+ leaf: (0, boundary_codec_leaves_1.resolveLeafCapability)("boolean", "boolean"),
353
+ fixedWidth: true
354
+ };
355
+ case typescript_1.default.SyntaxKind.NumberKeyword:
356
+ return {
357
+ nodeKind: "leaf",
358
+ ...this.nodeMeta(typeText, path, identityParts),
359
+ leaf: (0, boundary_codec_leaves_1.resolveLeafCapability)("number", "number"),
360
+ fixedWidth: true
361
+ };
362
+ case typescript_1.default.SyntaxKind.BigIntKeyword:
363
+ return {
364
+ nodeKind: "leaf",
365
+ ...this.nodeMeta(typeText, path, identityParts),
366
+ leaf: (0, boundary_codec_leaves_1.resolveLeafCapability)("bigint", "bigint"),
367
+ fixedWidth: true
368
+ };
369
+ case typescript_1.default.SyntaxKind.ObjectKeyword:
370
+ return {
371
+ nodeKind: "leaf",
372
+ ...this.nodeMeta(typeText, path, identityParts),
373
+ leaf: (0, boundary_codec_leaves_1.resolveLeafCapability)("object", "object"),
374
+ fixedWidth: false
375
+ };
376
+ default:
377
+ unsupported(path, typeText, "no transport analysis rule exists for this type");
378
+ }
379
+ }
380
+ }
381
+ exports.BoundaryTransportAnalyzer = BoundaryTransportAnalyzer;
382
+ function summarizeAnalysis(node) {
383
+ const used = new Set();
384
+ const visitedNamed = new Set();
385
+ const visit = (current) => {
386
+ switch (current.nodeKind) {
387
+ case "leaf":
388
+ used.add(current.leaf.key);
389
+ return {
390
+ hasBlobLeaves: current.leaf.region === "blob",
391
+ hasStringLeaves: current.leaf.region === "string",
392
+ hasBinaryLeaves: current.leaf.region === "binary",
393
+ hasDynamicLeaves: current.leaf.region === "dynamic",
394
+ hasRepeatedStructures: false,
395
+ hasOptionalPresence: false,
396
+ hasFiniteDomains: false,
397
+ usedLeafCapabilities: []
398
+ };
399
+ case "named":
400
+ if (visitedNamed.has(current.name)) {
401
+ return {
402
+ hasBlobLeaves: false,
403
+ hasStringLeaves: false,
404
+ hasBinaryLeaves: false,
405
+ hasDynamicLeaves: false,
406
+ hasRepeatedStructures: false,
407
+ hasOptionalPresence: false,
408
+ hasFiniteDomains: false,
409
+ usedLeafCapabilities: []
410
+ };
411
+ }
412
+ visitedNamed.add(current.name);
413
+ return visit(current.target);
414
+ case "finite-domain":
415
+ return {
416
+ hasBlobLeaves: false,
417
+ hasStringLeaves: false,
418
+ hasBinaryLeaves: false,
419
+ hasDynamicLeaves: false,
420
+ hasRepeatedStructures: false,
421
+ hasOptionalPresence: false,
422
+ hasFiniteDomains: true,
423
+ usedLeafCapabilities: []
424
+ };
425
+ case "array": {
426
+ const inner = visit(current.element);
427
+ return {
428
+ hasBlobLeaves: inner.hasBlobLeaves,
429
+ hasStringLeaves: inner.hasStringLeaves,
430
+ hasBinaryLeaves: inner.hasBinaryLeaves,
431
+ hasDynamicLeaves: inner.hasDynamicLeaves,
432
+ hasRepeatedStructures: true,
433
+ hasOptionalPresence: inner.hasOptionalPresence,
434
+ hasFiniteDomains: inner.hasFiniteDomains,
435
+ usedLeafCapabilities: []
436
+ };
437
+ }
438
+ case "struct":
439
+ return current.fields.reduce((acc, field) => {
440
+ const next = visit(field.node);
441
+ return {
442
+ hasBlobLeaves: acc.hasBlobLeaves || next.hasBlobLeaves,
443
+ hasStringLeaves: acc.hasStringLeaves || next.hasStringLeaves,
444
+ hasBinaryLeaves: acc.hasBinaryLeaves || next.hasBinaryLeaves,
445
+ hasDynamicLeaves: acc.hasDynamicLeaves || next.hasDynamicLeaves,
446
+ hasRepeatedStructures: acc.hasRepeatedStructures || next.hasRepeatedStructures,
447
+ hasOptionalPresence: acc.hasOptionalPresence || field.optional || next.hasOptionalPresence,
448
+ hasFiniteDomains: acc.hasFiniteDomains || next.hasFiniteDomains,
449
+ usedLeafCapabilities: []
450
+ };
451
+ }, {
452
+ hasBlobLeaves: false,
453
+ hasStringLeaves: false,
454
+ hasBinaryLeaves: false,
455
+ hasDynamicLeaves: false,
456
+ hasRepeatedStructures: false,
457
+ hasOptionalPresence: false,
458
+ hasFiniteDomains: false,
459
+ usedLeafCapabilities: []
460
+ });
461
+ }
462
+ };
463
+ const summary = visit(node);
464
+ return {
465
+ ...summary,
466
+ usedLeafCapabilities: [...used]
467
+ };
468
+ }