@dusted/anqst 1.5.0 → 1.5.1

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 (80) hide show
  1. package/dist/src/app.js +31 -9
  2. package/dist/src/base93.js +0 -72
  3. package/dist/src/boundary-codec-analysis.js +468 -0
  4. package/dist/src/boundary-codec-leaves.js +602 -0
  5. package/dist/src/boundary-codec-model.js +77 -0
  6. package/dist/src/boundary-codec-plan.js +522 -0
  7. package/dist/src/boundary-codec-render.js +1738 -0
  8. package/dist/src/boundary-codecs.js +174 -0
  9. package/dist/src/emit.js +580 -90
  10. package/dist/src/program.js +1 -1
  11. package/package.json +2 -2
  12. package/dist/src/codecgenerators/basecodecemitters/bigint-qint64/decoder.js +0 -35
  13. package/dist/src/codecgenerators/basecodecemitters/bigint-qint64/encoder.js +0 -36
  14. package/dist/src/codecgenerators/basecodecemitters/bigint-quint64/decoder.js +0 -26
  15. package/dist/src/codecgenerators/basecodecemitters/bigint-quint64/encoder.js +0 -38
  16. package/dist/src/codecgenerators/basecodecemitters/binary-blob/decoder.js +0 -28
  17. package/dist/src/codecgenerators/basecodecemitters/binary-blob/encoder.js +0 -34
  18. package/dist/src/codecgenerators/basecodecemitters/binary-buffer/decoder.js +0 -29
  19. package/dist/src/codecgenerators/basecodecemitters/binary-buffer/encoder.js +0 -36
  20. package/dist/src/codecgenerators/basecodecemitters/binary-float32Array/decoder.js +0 -46
  21. package/dist/src/codecgenerators/basecodecemitters/binary-float32Array/encoder.js +0 -49
  22. package/dist/src/codecgenerators/basecodecemitters/binary-float64Array/decoder.js +0 -46
  23. package/dist/src/codecgenerators/basecodecemitters/binary-float64Array/encoder.js +0 -47
  24. package/dist/src/codecgenerators/basecodecemitters/binary-int16Array/decoder.js +0 -46
  25. package/dist/src/codecgenerators/basecodecemitters/binary-int16Array/encoder.js +0 -49
  26. package/dist/src/codecgenerators/basecodecemitters/binary-int32Array/decoder.js +0 -50
  27. package/dist/src/codecgenerators/basecodecemitters/binary-int32Array/encoder.js +0 -52
  28. package/dist/src/codecgenerators/basecodecemitters/binary-int8Array/decoder.js +0 -38
  29. package/dist/src/codecgenerators/basecodecemitters/binary-int8Array/encoder.js +0 -44
  30. package/dist/src/codecgenerators/basecodecemitters/binary-typedArray/decoder.js +0 -33
  31. package/dist/src/codecgenerators/basecodecemitters/binary-typedArray/encoder.js +0 -34
  32. package/dist/src/codecgenerators/basecodecemitters/binary-uint16Array/decoder.js +0 -46
  33. package/dist/src/codecgenerators/basecodecemitters/binary-uint16Array/encoder.js +0 -49
  34. package/dist/src/codecgenerators/basecodecemitters/binary-uint32Array/decoder.js +0 -46
  35. package/dist/src/codecgenerators/basecodecemitters/binary-uint32Array/encoder.js +0 -49
  36. package/dist/src/codecgenerators/basecodecemitters/binary-uint8Array/decoder.js +0 -28
  37. package/dist/src/codecgenerators/basecodecemitters/binary-uint8Array/encoder.js +0 -34
  38. package/dist/src/codecgenerators/basecodecemitters/boolean/decoder.js +0 -34
  39. package/dist/src/codecgenerators/basecodecemitters/boolean/encoder.js +0 -40
  40. package/dist/src/codecgenerators/basecodecemitters/dynamic-json/decoder.js +0 -43
  41. package/dist/src/codecgenerators/basecodecemitters/dynamic-json/encoder.js +0 -45
  42. package/dist/src/codecgenerators/basecodecemitters/dynamic-object/decoder.js +0 -44
  43. package/dist/src/codecgenerators/basecodecemitters/dynamic-object/encoder.js +0 -46
  44. package/dist/src/codecgenerators/basecodecemitters/integer-int16/decoder.js +0 -32
  45. package/dist/src/codecgenerators/basecodecemitters/integer-int16/encoder.js +0 -43
  46. package/dist/src/codecgenerators/basecodecemitters/integer-int32/decoder.js +0 -26
  47. package/dist/src/codecgenerators/basecodecemitters/integer-int32/encoder.js +0 -37
  48. package/dist/src/codecgenerators/basecodecemitters/integer-int8/decoder.js +0 -26
  49. package/dist/src/codecgenerators/basecodecemitters/integer-int8/encoder.js +0 -37
  50. package/dist/src/codecgenerators/basecodecemitters/integer-qint16/decoder.js +0 -36
  51. package/dist/src/codecgenerators/basecodecemitters/integer-qint16/encoder.js +0 -36
  52. package/dist/src/codecgenerators/basecodecemitters/integer-qint32/decoder.js +0 -25
  53. package/dist/src/codecgenerators/basecodecemitters/integer-qint32/encoder.js +0 -36
  54. package/dist/src/codecgenerators/basecodecemitters/integer-qint8/decoder.js +0 -36
  55. package/dist/src/codecgenerators/basecodecemitters/integer-qint8/encoder.js +0 -36
  56. package/dist/src/codecgenerators/basecodecemitters/integer-quint16/decoder.js +0 -26
  57. package/dist/src/codecgenerators/basecodecemitters/integer-quint16/encoder.js +0 -38
  58. package/dist/src/codecgenerators/basecodecemitters/integer-quint32/decoder.js +0 -27
  59. package/dist/src/codecgenerators/basecodecemitters/integer-quint32/encoder.js +0 -39
  60. package/dist/src/codecgenerators/basecodecemitters/integer-quint8/decoder.js +0 -26
  61. package/dist/src/codecgenerators/basecodecemitters/integer-quint8/encoder.js +0 -38
  62. package/dist/src/codecgenerators/basecodecemitters/integer-uint16/decoder.js +0 -30
  63. package/dist/src/codecgenerators/basecodecemitters/integer-uint16/encoder.js +0 -42
  64. package/dist/src/codecgenerators/basecodecemitters/integer-uint32/decoder.js +0 -31
  65. package/dist/src/codecgenerators/basecodecemitters/integer-uint32/encoder.js +0 -43
  66. package/dist/src/codecgenerators/basecodecemitters/integer-uint8/decoder.js +0 -30
  67. package/dist/src/codecgenerators/basecodecemitters/integer-uint8/encoder.js +0 -40
  68. package/dist/src/codecgenerators/basecodecemitters/number/decoder.js +0 -26
  69. package/dist/src/codecgenerators/basecodecemitters/number/encoder.js +0 -38
  70. package/dist/src/codecgenerators/basecodecemitters/shared/comments.js +0 -13
  71. package/dist/src/codecgenerators/basecodecemitters/shared/contracts.js +0 -2
  72. package/dist/src/codecgenerators/basecodecemitters/shared/fixedwidth.js +0 -53
  73. package/dist/src/codecgenerators/basecodecemitters/shared/index.js +0 -21
  74. package/dist/src/codecgenerators/basecodecemitters/shared/positionalBase93.js +0 -48
  75. package/dist/src/codecgenerators/basecodecemitters/shared/rawbytes.js +0 -30
  76. package/dist/src/codecgenerators/basecodecemitters/string/decoder.js +0 -43
  77. package/dist/src/codecgenerators/basecodecemitters/string/encoder.js +0 -43
  78. package/dist/src/codecgenerators/basecodecemitters/stringArray/decoder.js +0 -80
  79. package/dist/src/codecgenerators/basecodecemitters/stringArray/encoder.js +0 -57
  80. package/dist/src/structured-top-level-codecs.js +0 -1305
package/dist/src/app.js CHANGED
@@ -19,6 +19,12 @@ const project_1 = require("./project");
19
19
  const layout_1 = require("./layout");
20
20
  const parser_1 = require("./parser");
21
21
  const verify_1 = require("./verify");
22
+ class CliUsageError extends Error {
23
+ constructor(message) {
24
+ super(message);
25
+ this.name = "CliUsageError";
26
+ }
27
+ }
22
28
  const ANQSTGEN_ACTIVE_STAMP_FILE = ".anqstgen-version-active.json";
23
29
  function renderHelp() {
24
30
  const version = readActiveBuildStamp();
@@ -296,12 +302,12 @@ function parseSpecCommandArg(commandName, specArg, extraArgs) {
296
302
  const positional = [];
297
303
  for (const arg of allArgs) {
298
304
  if (arg.startsWith("-")) {
299
- throw new Error(`Unknown ${commandName} flag '${arg}'. ${usageFor(commandName)}`);
305
+ throw new CliUsageError(`Unknown ${commandName} flag '${arg}'. ${usageFor(commandName)}`);
300
306
  }
301
307
  positional.push(arg);
302
308
  }
303
309
  if (positional.length !== 1) {
304
- throw new Error(usageFor(commandName));
310
+ throw new CliUsageError(usageFor(commandName));
305
311
  }
306
312
  return positional[0];
307
313
  }
@@ -328,12 +334,12 @@ function parseBuildCommandArgs(specArg, extraArgs) {
328
334
  continue;
329
335
  }
330
336
  if (arg.startsWith("-")) {
331
- throw new Error(`Unknown build flag '${arg}'. ${usageFor("build")}`);
337
+ throw new CliUsageError(`Unknown build flag '${arg}'. ${usageFor("build")}`);
332
338
  }
333
339
  positional.push(arg);
334
340
  }
335
341
  if (positional.length > 0) {
336
- throw new Error(`Unexpected extra argument '${positional[0]}'. ${usageFor("build")}`);
342
+ throw new CliUsageError(`Unexpected extra argument '${positional[0]}'. ${usageFor("build")}`);
337
343
  }
338
344
  return { designerPlugin };
339
345
  }
@@ -347,15 +353,15 @@ function parseCleanCommandArgs(specArg, extraArgs) {
347
353
  continue;
348
354
  }
349
355
  if (arg.startsWith("-")) {
350
- throw new Error(`Unknown clean flag '${arg}'. Use -f or --force.`);
356
+ throw new CliUsageError(`Unknown clean flag '${arg}'. Use -f or --force.`);
351
357
  }
352
358
  if (targetPathArg !== null) {
353
- throw new Error(`Unexpected extra argument '${arg}'. Usage: anqst clean <path> [-f|--force]`);
359
+ throw new CliUsageError(`Unexpected extra argument '${arg}'. Usage: anqst clean <path> [-f|--force]`);
354
360
  }
355
361
  targetPathArg = arg;
356
362
  }
357
363
  if (targetPathArg === null) {
358
- throw new Error("Usage: anqst clean <path> [-f|--force]");
364
+ throw new CliUsageError("Usage: anqst clean <path> [-f|--force]");
359
365
  }
360
366
  return { targetPathArg, force };
361
367
  }
@@ -476,6 +482,19 @@ function renderInstallAliasMessage() {
476
482
  return text;
477
483
  return `\x1b[38;5;214m${text}\x1b[0m`;
478
484
  }
485
+ function formatUnexpectedError(error) {
486
+ if (!(error instanceof Error)) {
487
+ return `[AnQst] ${String(error)}`;
488
+ }
489
+ const lines = [`[AnQst] ${error.message}`];
490
+ const stack = typeof error.stack === "string" ? error.stack.trim() : "";
491
+ if (stack.length > 0) {
492
+ lines.push("");
493
+ lines.push("Stack trace:");
494
+ lines.push(stack);
495
+ }
496
+ return lines.join("\n");
497
+ }
479
498
  function runCommand(command, specArg, extraArgs = []) {
480
499
  try {
481
500
  if (!command) {
@@ -545,8 +564,11 @@ function runCommand(command, specArg, extraArgs = []) {
545
564
  console.error((0, errors_1.formatVerifyError)(error));
546
565
  return 1;
547
566
  }
548
- const message = error instanceof Error ? error.message : String(error);
549
- console.error(`[AnQst] ${message}`);
567
+ if (error instanceof CliUsageError) {
568
+ console.error(error.message);
569
+ return 1;
570
+ }
571
+ console.error(formatUnexpectedError(error));
550
572
  return 1;
551
573
  }
552
574
  }
@@ -3,7 +3,6 @@ Object.defineProperty(exports, "__esModule", { value: true });
3
3
  exports.BASE93_ALPHABET = void 0;
4
4
  exports.emitBase93Encoder = emitBase93Encoder;
5
5
  exports.emitBase93Decoder = emitBase93Decoder;
6
- exports.emitBase93CppFunctions = emitBase93CppFunctions;
7
6
  exports.BASE93_ALPHABET = Array.from({ length: 95 }, (_, i) => String.fromCharCode(0x20 + i))
8
7
  .filter(c => c !== '"' && c !== '\\')
9
8
  .join('');
@@ -51,74 +50,3 @@ for (i = r - 2; i >= 0; i--) { o[p + i] = v & 255; v = (v / 256) | 0; }
51
50
  return o;
52
51
  }`;
53
52
  }
54
- function emitBase93CppFunctions() {
55
- return `inline int base93AlphabetIndex(char c) {
56
- const unsigned char uc = static_cast<unsigned char>(c);
57
- return static_cast<int>(uc) - 32 - (uc > 34) - (uc > 92);
58
- }
59
-
60
- inline std::string base93Encode(const std::vector<std::uint8_t>& d) {
61
- static constexpr char A[] = "${exports.BASE93_ALPHABET}";
62
- const std::size_t n = d.size();
63
- const std::size_t f = n >> 2;
64
- const std::size_t r = n & 3;
65
- std::string o(f * 5 + (r ? r + 1 : 0), '\\0');
66
- std::size_t p = 0;
67
- for (std::size_t i = 0; i < f; ++i) {
68
- const std::size_t b = i << 2;
69
- std::uint32_t v =
70
- (static_cast<std::uint32_t>(d[b]) << 24) |
71
- (static_cast<std::uint32_t>(d[b + 1]) << 16) |
72
- (static_cast<std::uint32_t>(d[b + 2]) << 8) |
73
- static_cast<std::uint32_t>(d[b + 3]);
74
- o[p + 4] = A[v % 93u]; v /= 93u;
75
- o[p + 3] = A[v % 93u]; v /= 93u;
76
- o[p + 2] = A[v % 93u]; v /= 93u;
77
- o[p + 1] = A[v % 93u];
78
- o[p] = A[v / 93u];
79
- p += 5;
80
- }
81
- if (r) {
82
- const std::size_t b = f << 2;
83
- std::uint32_t v = 0;
84
- for (std::size_t j = 0; j < r; ++j) v = (v << 8) | d[b + j];
85
- for (std::size_t j = r + 1; j-- > 0;) {
86
- o[p + j] = A[v % 93u];
87
- v /= 93u;
88
- }
89
- }
90
- return o;
91
- }
92
-
93
- inline std::vector<std::uint8_t> base93Decode(const std::string& s) {
94
- const std::size_t n = s.size();
95
- const std::size_t f = n / 5;
96
- const std::size_t r = n - f * 5;
97
- std::vector<std::uint8_t> o(f * 4 + (r ? r - 1 : 0));
98
- std::size_t p = 0;
99
- for (std::size_t i = 0; i < f; ++i) {
100
- const std::size_t b = i * 5;
101
- std::uint32_t v = static_cast<std::uint32_t>(base93AlphabetIndex(s[b]));
102
- v = v * 93u + static_cast<std::uint32_t>(base93AlphabetIndex(s[b + 1]));
103
- v = v * 93u + static_cast<std::uint32_t>(base93AlphabetIndex(s[b + 2]));
104
- v = v * 93u + static_cast<std::uint32_t>(base93AlphabetIndex(s[b + 3]));
105
- v = v * 93u + static_cast<std::uint32_t>(base93AlphabetIndex(s[b + 4]));
106
- o[p] = static_cast<std::uint8_t>(v >> 24);
107
- o[p + 1] = static_cast<std::uint8_t>((v >> 16) & 255u);
108
- o[p + 2] = static_cast<std::uint8_t>((v >> 8) & 255u);
109
- o[p + 3] = static_cast<std::uint8_t>(v & 255u);
110
- p += 4;
111
- }
112
- if (r) {
113
- std::uint32_t v = 0;
114
- for (std::size_t i = 0; i < r; ++i) {
115
- v = v * 93u + static_cast<std::uint32_t>(base93AlphabetIndex(s[f * 5 + i]));
116
- }
117
- for (std::size_t i = r - 1; i-- > 0;) {
118
- o[p + i] = static_cast<std::uint8_t>(v & 255u);
119
- v /= 256u;
120
- }
121
- }
122
- return o;
123
- }`;
124
- }
@@ -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
+ }