@dusted/anqst 1.0.1 → 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.
package/README.md CHANGED
@@ -75,6 +75,8 @@ Settings file (`./AnQst/<WidgetName>.settings.json`) owns project-local AnQst co
75
75
  - If `QWidget` is enabled and `angular.json` exists:
76
76
  - runs production Angular build
77
77
  - embeds built web assets into generated Qt widget `webapp/`.
78
+ - Generated Qt integration CMake consumes the existing `./AnQst/generated` widget tree and fails fast if the required generated files are missing.
79
+ - Downstream CMake no longer invokes `npm`, `npx`, or `anqst`; run `anqst build` first, then build C++ against the generated tree.
78
80
  - If `--designerplugin` is enabled:
79
81
  - requires `ANQST_WEBBASE_DIR`
80
82
  - emits plugin sources in `./AnQst/generated/backend/cpp/qt/<WidgetName>_widget/designerPlugin`
@@ -140,3 +142,16 @@ code AnQst/BurgerConstructor.AnQst.d.ts
140
142
  npx @dusted/anqst test
141
143
  npx @dusted/anqst build
142
144
  ```
145
+
146
+ ## Two-stage workflow
147
+
148
+ ```bash
149
+ # Stage 1: Node/Angular/generation environment
150
+ npx @dusted/anqst build
151
+
152
+ # Stage 2: pure Qt/CMake environment, consuming the generated tree
153
+ cmake -S . -B build
154
+ cmake --build build
155
+ ```
156
+
157
+ Both stages must use the exact outputs from the same prior `anqst build` invocation.
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();
@@ -261,6 +267,7 @@ function runBuild(cwd, designerPlugin = false) {
261
267
  detailLines.push(" Target QWidget:");
262
268
  detailLines.push(` - Qt integration CMake: ${(0, layout_1.toProjectRelative)(cwd, node_path_1.default.join(layout.cppCmakeRoot, "CMakeLists.txt"))}`);
263
269
  detailLines.push(` - Widget output root: ${(0, layout_1.toProjectRelative)(cwd, layout.cppQtWidgetRoot)}`);
270
+ detailLines.push(" - C++ handoff: downstream CMake consumes this generated tree directly");
264
271
  detailLines.push(" - Embedded web assets refreshed from Angular build");
265
272
  }
266
273
  if (generationTargets.emitNodeExpressWs) {
@@ -295,12 +302,12 @@ function parseSpecCommandArg(commandName, specArg, extraArgs) {
295
302
  const positional = [];
296
303
  for (const arg of allArgs) {
297
304
  if (arg.startsWith("-")) {
298
- throw new Error(`Unknown ${commandName} flag '${arg}'. ${usageFor(commandName)}`);
305
+ throw new CliUsageError(`Unknown ${commandName} flag '${arg}'. ${usageFor(commandName)}`);
299
306
  }
300
307
  positional.push(arg);
301
308
  }
302
309
  if (positional.length !== 1) {
303
- throw new Error(usageFor(commandName));
310
+ throw new CliUsageError(usageFor(commandName));
304
311
  }
305
312
  return positional[0];
306
313
  }
@@ -327,12 +334,12 @@ function parseBuildCommandArgs(specArg, extraArgs) {
327
334
  continue;
328
335
  }
329
336
  if (arg.startsWith("-")) {
330
- throw new Error(`Unknown build flag '${arg}'. ${usageFor("build")}`);
337
+ throw new CliUsageError(`Unknown build flag '${arg}'. ${usageFor("build")}`);
331
338
  }
332
339
  positional.push(arg);
333
340
  }
334
341
  if (positional.length > 0) {
335
- throw new Error(`Unexpected extra argument '${positional[0]}'. ${usageFor("build")}`);
342
+ throw new CliUsageError(`Unexpected extra argument '${positional[0]}'. ${usageFor("build")}`);
336
343
  }
337
344
  return { designerPlugin };
338
345
  }
@@ -346,15 +353,15 @@ function parseCleanCommandArgs(specArg, extraArgs) {
346
353
  continue;
347
354
  }
348
355
  if (arg.startsWith("-")) {
349
- throw new Error(`Unknown clean flag '${arg}'. Use -f or --force.`);
356
+ throw new CliUsageError(`Unknown clean flag '${arg}'. Use -f or --force.`);
350
357
  }
351
358
  if (targetPathArg !== null) {
352
- 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]`);
353
360
  }
354
361
  targetPathArg = arg;
355
362
  }
356
363
  if (targetPathArg === null) {
357
- throw new Error("Usage: anqst clean <path> [-f|--force]");
364
+ throw new CliUsageError("Usage: anqst clean <path> [-f|--force]");
358
365
  }
359
366
  return { targetPathArg, force };
360
367
  }
@@ -475,6 +482,19 @@ function renderInstallAliasMessage() {
475
482
  return text;
476
483
  return `\x1b[38;5;214m${text}\x1b[0m`;
477
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
+ }
478
498
  function runCommand(command, specArg, extraArgs = []) {
479
499
  try {
480
500
  if (!command) {
@@ -544,8 +564,11 @@ function runCommand(command, specArg, extraArgs = []) {
544
564
  console.error((0, errors_1.formatVerifyError)(error));
545
565
  return 1;
546
566
  }
547
- const message = error instanceof Error ? error.message : String(error);
548
- console.error(`[AnQst] ${message}`);
567
+ if (error instanceof CliUsageError) {
568
+ console.error(error.message);
569
+ return 1;
570
+ }
571
+ console.error(formatUnexpectedError(error));
549
572
  return 1;
550
573
  }
551
574
  }
@@ -0,0 +1,52 @@
1
+ "use strict";
2
+ Object.defineProperty(exports, "__esModule", { value: true });
3
+ exports.BASE93_ALPHABET = void 0;
4
+ exports.emitBase93Encoder = emitBase93Encoder;
5
+ exports.emitBase93Decoder = emitBase93Decoder;
6
+ exports.BASE93_ALPHABET = Array.from({ length: 95 }, (_, i) => String.fromCharCode(0x20 + i))
7
+ .filter(c => c !== '"' && c !== '\\')
8
+ .join('');
9
+ function emitBase93Encoder() {
10
+ return `function(d) {
11
+ var A = "${exports.BASE93_ALPHABET}", n = d.length, f = n >>> 2, r = n & 3,
12
+ o = new Array(f * 5 + (r ? r + 1 : 0)), p = 0, i, v, b, j;
13
+ for (i = 0; i < f; i++) {
14
+ b = i << 2;
15
+ v = ((d[b] << 24) | (d[b+1] << 16) | (d[b+2] << 8) | d[b+3]) >>> 0;
16
+ o[p+4] = A[v % 93]; v = (v / 93) | 0;
17
+ o[p+3] = A[v % 93]; v = (v / 93) | 0;
18
+ o[p+2] = A[v % 93]; v = (v / 93) | 0;
19
+ o[p+1] = A[v % 93];
20
+ o[p] = A[(v / 93) | 0];
21
+ p += 5;
22
+ }
23
+ if (r) {
24
+ b = f << 2; v = 0;
25
+ for (j = 0; j < r; j++) v = (v << 8) | d[b + j];
26
+ for (j = r; j >= 0; j--) { o[p + j] = A[v % 93]; v = (v / 93) | 0; }
27
+ }
28
+ return o.join("");
29
+ }`;
30
+ }
31
+ function emitBase93Decoder() {
32
+ return `function(s) {
33
+ var n = s.length, f = (n / 5) | 0, r = n - f * 5,
34
+ o = new Uint8Array(f * 4 + (r ? r - 1 : 0)), p = 0, i, v, c, b;
35
+ for (i = 0; i < f; i++) {
36
+ b = i * 5;
37
+ c = s.charCodeAt(b); v = c - 32 - ((c > 34) ? 1 : 0) - ((c > 92) ? 1 : 0);
38
+ c = s.charCodeAt(b + 1); v = v * 93 + c - 32 - ((c > 34) ? 1 : 0) - ((c > 92) ? 1 : 0);
39
+ c = s.charCodeAt(b + 2); v = v * 93 + c - 32 - ((c > 34) ? 1 : 0) - ((c > 92) ? 1 : 0);
40
+ c = s.charCodeAt(b + 3); v = v * 93 + c - 32 - ((c > 34) ? 1 : 0) - ((c > 92) ? 1 : 0);
41
+ c = s.charCodeAt(b + 4); v = v * 93 + c - 32 - ((c > 34) ? 1 : 0) - ((c > 92) ? 1 : 0);
42
+ o[p] = v >>> 24; o[p+1] = (v >>> 16) & 255; o[p+2] = (v >>> 8) & 255; o[p+3] = v & 255;
43
+ p += 4;
44
+ }
45
+ if (r) {
46
+ v = 0;
47
+ for (i = 0; i < r; i++) { c = s.charCodeAt(f * 5 + i); v = v * 93 + c - 32 - ((c > 34) ? 1 : 0) - ((c > 92) ? 1 : 0); }
48
+ for (i = r - 2; i >= 0; i--) { o[p + i] = v & 255; v = (v / 256) | 0; }
49
+ }
50
+ return o;
51
+ }`;
52
+ }
@@ -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
+ }