@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.
- package/dist/src/app.js +31 -9
- package/dist/src/base93.js +0 -72
- package/dist/src/boundary-codec-analysis.js +468 -0
- package/dist/src/boundary-codec-leaves.js +602 -0
- package/dist/src/boundary-codec-model.js +77 -0
- package/dist/src/boundary-codec-plan.js +522 -0
- package/dist/src/boundary-codec-render.js +1738 -0
- package/dist/src/boundary-codecs.js +174 -0
- package/dist/src/emit.js +580 -90
- package/dist/src/program.js +1 -1
- package/package.json +2 -2
- package/dist/src/codecgenerators/basecodecemitters/bigint-qint64/decoder.js +0 -35
- package/dist/src/codecgenerators/basecodecemitters/bigint-qint64/encoder.js +0 -36
- package/dist/src/codecgenerators/basecodecemitters/bigint-quint64/decoder.js +0 -26
- package/dist/src/codecgenerators/basecodecemitters/bigint-quint64/encoder.js +0 -38
- package/dist/src/codecgenerators/basecodecemitters/binary-blob/decoder.js +0 -28
- package/dist/src/codecgenerators/basecodecemitters/binary-blob/encoder.js +0 -34
- package/dist/src/codecgenerators/basecodecemitters/binary-buffer/decoder.js +0 -29
- package/dist/src/codecgenerators/basecodecemitters/binary-buffer/encoder.js +0 -36
- package/dist/src/codecgenerators/basecodecemitters/binary-float32Array/decoder.js +0 -46
- package/dist/src/codecgenerators/basecodecemitters/binary-float32Array/encoder.js +0 -49
- package/dist/src/codecgenerators/basecodecemitters/binary-float64Array/decoder.js +0 -46
- package/dist/src/codecgenerators/basecodecemitters/binary-float64Array/encoder.js +0 -47
- package/dist/src/codecgenerators/basecodecemitters/binary-int16Array/decoder.js +0 -46
- package/dist/src/codecgenerators/basecodecemitters/binary-int16Array/encoder.js +0 -49
- package/dist/src/codecgenerators/basecodecemitters/binary-int32Array/decoder.js +0 -50
- package/dist/src/codecgenerators/basecodecemitters/binary-int32Array/encoder.js +0 -52
- package/dist/src/codecgenerators/basecodecemitters/binary-int8Array/decoder.js +0 -38
- package/dist/src/codecgenerators/basecodecemitters/binary-int8Array/encoder.js +0 -44
- package/dist/src/codecgenerators/basecodecemitters/binary-typedArray/decoder.js +0 -33
- package/dist/src/codecgenerators/basecodecemitters/binary-typedArray/encoder.js +0 -34
- package/dist/src/codecgenerators/basecodecemitters/binary-uint16Array/decoder.js +0 -46
- package/dist/src/codecgenerators/basecodecemitters/binary-uint16Array/encoder.js +0 -49
- package/dist/src/codecgenerators/basecodecemitters/binary-uint32Array/decoder.js +0 -46
- package/dist/src/codecgenerators/basecodecemitters/binary-uint32Array/encoder.js +0 -49
- package/dist/src/codecgenerators/basecodecemitters/binary-uint8Array/decoder.js +0 -28
- package/dist/src/codecgenerators/basecodecemitters/binary-uint8Array/encoder.js +0 -34
- package/dist/src/codecgenerators/basecodecemitters/boolean/decoder.js +0 -34
- package/dist/src/codecgenerators/basecodecemitters/boolean/encoder.js +0 -40
- package/dist/src/codecgenerators/basecodecemitters/dynamic-json/decoder.js +0 -43
- package/dist/src/codecgenerators/basecodecemitters/dynamic-json/encoder.js +0 -45
- package/dist/src/codecgenerators/basecodecemitters/dynamic-object/decoder.js +0 -44
- package/dist/src/codecgenerators/basecodecemitters/dynamic-object/encoder.js +0 -46
- package/dist/src/codecgenerators/basecodecemitters/integer-int16/decoder.js +0 -32
- package/dist/src/codecgenerators/basecodecemitters/integer-int16/encoder.js +0 -43
- package/dist/src/codecgenerators/basecodecemitters/integer-int32/decoder.js +0 -26
- package/dist/src/codecgenerators/basecodecemitters/integer-int32/encoder.js +0 -37
- package/dist/src/codecgenerators/basecodecemitters/integer-int8/decoder.js +0 -26
- package/dist/src/codecgenerators/basecodecemitters/integer-int8/encoder.js +0 -37
- package/dist/src/codecgenerators/basecodecemitters/integer-qint16/decoder.js +0 -36
- package/dist/src/codecgenerators/basecodecemitters/integer-qint16/encoder.js +0 -36
- package/dist/src/codecgenerators/basecodecemitters/integer-qint32/decoder.js +0 -25
- package/dist/src/codecgenerators/basecodecemitters/integer-qint32/encoder.js +0 -36
- package/dist/src/codecgenerators/basecodecemitters/integer-qint8/decoder.js +0 -36
- package/dist/src/codecgenerators/basecodecemitters/integer-qint8/encoder.js +0 -36
- package/dist/src/codecgenerators/basecodecemitters/integer-quint16/decoder.js +0 -26
- package/dist/src/codecgenerators/basecodecemitters/integer-quint16/encoder.js +0 -38
- package/dist/src/codecgenerators/basecodecemitters/integer-quint32/decoder.js +0 -27
- package/dist/src/codecgenerators/basecodecemitters/integer-quint32/encoder.js +0 -39
- package/dist/src/codecgenerators/basecodecemitters/integer-quint8/decoder.js +0 -26
- package/dist/src/codecgenerators/basecodecemitters/integer-quint8/encoder.js +0 -38
- package/dist/src/codecgenerators/basecodecemitters/integer-uint16/decoder.js +0 -30
- package/dist/src/codecgenerators/basecodecemitters/integer-uint16/encoder.js +0 -42
- package/dist/src/codecgenerators/basecodecemitters/integer-uint32/decoder.js +0 -31
- package/dist/src/codecgenerators/basecodecemitters/integer-uint32/encoder.js +0 -43
- package/dist/src/codecgenerators/basecodecemitters/integer-uint8/decoder.js +0 -30
- package/dist/src/codecgenerators/basecodecemitters/integer-uint8/encoder.js +0 -40
- package/dist/src/codecgenerators/basecodecemitters/number/decoder.js +0 -26
- package/dist/src/codecgenerators/basecodecemitters/number/encoder.js +0 -38
- package/dist/src/codecgenerators/basecodecemitters/shared/comments.js +0 -13
- package/dist/src/codecgenerators/basecodecemitters/shared/contracts.js +0 -2
- package/dist/src/codecgenerators/basecodecemitters/shared/fixedwidth.js +0 -53
- package/dist/src/codecgenerators/basecodecemitters/shared/index.js +0 -21
- package/dist/src/codecgenerators/basecodecemitters/shared/positionalBase93.js +0 -48
- package/dist/src/codecgenerators/basecodecemitters/shared/rawbytes.js +0 -30
- package/dist/src/codecgenerators/basecodecemitters/string/decoder.js +0 -43
- package/dist/src/codecgenerators/basecodecemitters/string/encoder.js +0 -43
- package/dist/src/codecgenerators/basecodecemitters/stringArray/decoder.js +0 -80
- package/dist/src/codecgenerators/basecodecemitters/stringArray/encoder.js +0 -57
- package/dist/src/structured-top-level-codecs.js +0 -1305
package/dist/src/emit.js
CHANGED
|
@@ -13,7 +13,7 @@ const node_path_1 = __importDefault(require("node:path"));
|
|
|
13
13
|
const typescript_1 = __importDefault(require("typescript"));
|
|
14
14
|
const pngjs_1 = require("pngjs");
|
|
15
15
|
const layout_1 = require("./layout");
|
|
16
|
-
const
|
|
16
|
+
const boundary_codecs_1 = require("./boundary-codecs");
|
|
17
17
|
function stripAnQstType(typeText) {
|
|
18
18
|
return typeText
|
|
19
19
|
.replace(/\bAnQst\.Type\.stringArray\b/g, "string[]")
|
|
@@ -83,6 +83,99 @@ function isNumberLikeUnionTypeNode(node) {
|
|
|
83
83
|
return false;
|
|
84
84
|
});
|
|
85
85
|
}
|
|
86
|
+
function collectFiniteStringLiteralsTypeNode(node) {
|
|
87
|
+
const values = [];
|
|
88
|
+
for (const part of node.types) {
|
|
89
|
+
if (!typescript_1.default.isLiteralTypeNode(part) || !typescript_1.default.isStringLiteral(part.literal))
|
|
90
|
+
return null;
|
|
91
|
+
values.push(part.literal.text);
|
|
92
|
+
}
|
|
93
|
+
return values;
|
|
94
|
+
}
|
|
95
|
+
function collectFiniteBooleanLiteralsTypeNode(node) {
|
|
96
|
+
const values = [];
|
|
97
|
+
for (const part of node.types) {
|
|
98
|
+
if (!typescript_1.default.isLiteralTypeNode(part))
|
|
99
|
+
return null;
|
|
100
|
+
if (part.literal.kind === typescript_1.default.SyntaxKind.TrueKeyword) {
|
|
101
|
+
values.push(true);
|
|
102
|
+
continue;
|
|
103
|
+
}
|
|
104
|
+
if (part.literal.kind === typescript_1.default.SyntaxKind.FalseKeyword) {
|
|
105
|
+
values.push(false);
|
|
106
|
+
continue;
|
|
107
|
+
}
|
|
108
|
+
return null;
|
|
109
|
+
}
|
|
110
|
+
return values;
|
|
111
|
+
}
|
|
112
|
+
function collectFiniteNumberLiteralsTypeNode(node) {
|
|
113
|
+
const values = [];
|
|
114
|
+
for (const part of node.types) {
|
|
115
|
+
if (!typescript_1.default.isLiteralTypeNode(part) || !typescript_1.default.isNumericLiteral(part.literal))
|
|
116
|
+
return null;
|
|
117
|
+
values.push(Number(part.literal.text));
|
|
118
|
+
}
|
|
119
|
+
return values;
|
|
120
|
+
}
|
|
121
|
+
function finiteDomainSymbolForCpp(value) {
|
|
122
|
+
if (typeof value === "boolean")
|
|
123
|
+
return value ? "True" : "False";
|
|
124
|
+
if (typeof value === "number") {
|
|
125
|
+
const text = Number.isInteger(value) ? `${value}` : `${value}`.replace(/\./g, "_");
|
|
126
|
+
return sanitizeIdentifier(`Value_${text.replace(/-/g, "neg_")}`);
|
|
127
|
+
}
|
|
128
|
+
const direct = sanitizeIdentifier(value.trim());
|
|
129
|
+
return direct.length > 0 ? direct : "Value";
|
|
130
|
+
}
|
|
131
|
+
function buildCppFiniteDomain(primitive, values) {
|
|
132
|
+
const seen = new Set();
|
|
133
|
+
const variants = [];
|
|
134
|
+
for (const value of values) {
|
|
135
|
+
const key = `${typeof value}:${String(value)}`;
|
|
136
|
+
if (seen.has(key))
|
|
137
|
+
continue;
|
|
138
|
+
seen.add(key);
|
|
139
|
+
variants.push({
|
|
140
|
+
code: variants.length,
|
|
141
|
+
symbolicName: finiteDomainSymbolForCpp(value),
|
|
142
|
+
value
|
|
143
|
+
});
|
|
144
|
+
}
|
|
145
|
+
return { primitive, variants };
|
|
146
|
+
}
|
|
147
|
+
function collectFiniteDomainFromTypeNode(typeNode) {
|
|
148
|
+
if (typescript_1.default.isParenthesizedTypeNode(typeNode)) {
|
|
149
|
+
return collectFiniteDomainFromTypeNode(typeNode.type);
|
|
150
|
+
}
|
|
151
|
+
if (typescript_1.default.isLiteralTypeNode(typeNode)) {
|
|
152
|
+
if (typescript_1.default.isStringLiteral(typeNode.literal)) {
|
|
153
|
+
return buildCppFiniteDomain("string", [typeNode.literal.text]);
|
|
154
|
+
}
|
|
155
|
+
if (typescript_1.default.isNumericLiteral(typeNode.literal)) {
|
|
156
|
+
return buildCppFiniteDomain("number", [Number(typeNode.literal.text)]);
|
|
157
|
+
}
|
|
158
|
+
if (typeNode.literal.kind === typescript_1.default.SyntaxKind.TrueKeyword || typeNode.literal.kind === typescript_1.default.SyntaxKind.FalseKeyword) {
|
|
159
|
+
return buildCppFiniteDomain("boolean", [typeNode.literal.kind === typescript_1.default.SyntaxKind.TrueKeyword]);
|
|
160
|
+
}
|
|
161
|
+
return null;
|
|
162
|
+
}
|
|
163
|
+
if (!typescript_1.default.isUnionTypeNode(typeNode))
|
|
164
|
+
return null;
|
|
165
|
+
const filtered = filterNullishUnionTypeNodes(typeNode.types);
|
|
166
|
+
if (filtered.length !== typeNode.types.length)
|
|
167
|
+
return null;
|
|
168
|
+
const finiteStrings = collectFiniteStringLiteralsTypeNode(typeNode);
|
|
169
|
+
if (finiteStrings)
|
|
170
|
+
return buildCppFiniteDomain("string", finiteStrings);
|
|
171
|
+
const finiteBooleans = collectFiniteBooleanLiteralsTypeNode(typeNode);
|
|
172
|
+
if (finiteBooleans)
|
|
173
|
+
return buildCppFiniteDomain("boolean", finiteBooleans);
|
|
174
|
+
const finiteNumbers = collectFiniteNumberLiteralsTypeNode(typeNode);
|
|
175
|
+
if (finiteNumbers)
|
|
176
|
+
return buildCppFiniteDomain("number", finiteNumbers);
|
|
177
|
+
return null;
|
|
178
|
+
}
|
|
86
179
|
function mapTsTypeToCpp(typeText) {
|
|
87
180
|
const raw = typeText.trim();
|
|
88
181
|
if (/\bAnQst\.Type\.qint64\b/.test(raw))
|
|
@@ -179,6 +272,11 @@ function callbackName(memberName) {
|
|
|
179
272
|
function pascalCase(value) {
|
|
180
273
|
return value.length === 0 ? value : `${value.charAt(0).toUpperCase()}${value.slice(1)}`;
|
|
181
274
|
}
|
|
275
|
+
function sanitizeIdentifier(value) {
|
|
276
|
+
const trimmed = value.replace(/[^A-Za-z0-9_]+/g, "_").replace(/_+/g, "_").replace(/^_+|_+$/g, "");
|
|
277
|
+
const withFallback = trimmed.length > 0 ? trimmed : "Codec";
|
|
278
|
+
return /^[0-9]/.test(withFallback) ? `T_${withFallback}` : withFallback;
|
|
279
|
+
}
|
|
182
280
|
function variantToCppExpression(cppType, expr) {
|
|
183
281
|
if (cppType === "QString")
|
|
184
282
|
return `${expr}.toString()`;
|
|
@@ -471,6 +569,7 @@ class CppTypeNormalizer {
|
|
|
471
569
|
this.allKnownNames = new Set();
|
|
472
570
|
this.usedNames = new Set();
|
|
473
571
|
this.syntheticNameByKey = new Map();
|
|
572
|
+
this.finiteDomainNameByKey = new Map();
|
|
474
573
|
for (const decl of collectStructDecls(spec)) {
|
|
475
574
|
this.allKnownNames.add(decl.name);
|
|
476
575
|
this.usedNames.add(decl.name);
|
|
@@ -494,9 +593,11 @@ class CppTypeNormalizer {
|
|
|
494
593
|
const order = this.topologicalOrder();
|
|
495
594
|
const orderedDecls = order.map((name) => this.declMap.get(name)).filter((x) => !!x);
|
|
496
595
|
const structNames = orderedDecls.filter((d) => d.kind === "struct").map((d) => d.name);
|
|
596
|
+
const metatypeNames = orderedDecls.filter((d) => d.kind === "struct" || d.kind === "enum").map((d) => d.name);
|
|
497
597
|
return {
|
|
498
598
|
orderedDecls,
|
|
499
599
|
structNames,
|
|
600
|
+
metatypeNames,
|
|
500
601
|
mapTypeText: (typeText, nameHintParts) => this.mapTypeText(typeText, nameHintParts)
|
|
501
602
|
};
|
|
502
603
|
}
|
|
@@ -514,7 +615,19 @@ class CppTypeNormalizer {
|
|
|
514
615
|
optional: !!member.questionToken
|
|
515
616
|
});
|
|
516
617
|
}
|
|
517
|
-
return { name, kind: "struct", fields, aliasType: null, deps, isUnionAlias: false };
|
|
618
|
+
return { name, kind: "struct", fields, aliasType: null, deps, isUnionAlias: false, finiteDomain: null };
|
|
619
|
+
}
|
|
620
|
+
const finiteDomain = collectFiniteDomainFromTypeNode(node.type);
|
|
621
|
+
if (finiteDomain) {
|
|
622
|
+
return {
|
|
623
|
+
name,
|
|
624
|
+
kind: "enum",
|
|
625
|
+
fields: [],
|
|
626
|
+
aliasType: null,
|
|
627
|
+
deps: new Set(),
|
|
628
|
+
isUnionAlias: false,
|
|
629
|
+
finiteDomain
|
|
630
|
+
};
|
|
518
631
|
}
|
|
519
632
|
const deps = new Set();
|
|
520
633
|
const aliasType = this.mapTypeNode(node.type, [name], deps);
|
|
@@ -524,13 +637,18 @@ class CppTypeNormalizer {
|
|
|
524
637
|
fields: [],
|
|
525
638
|
aliasType,
|
|
526
639
|
deps,
|
|
527
|
-
isUnionAlias: node.type.getText().includes("|")
|
|
640
|
+
isUnionAlias: node.type.getText().includes("|"),
|
|
641
|
+
finiteDomain: null
|
|
528
642
|
};
|
|
529
643
|
}
|
|
530
644
|
mapTypeNode(typeNode, nameHintParts, deps) {
|
|
531
645
|
if (typescript_1.default.isParenthesizedTypeNode(typeNode)) {
|
|
532
646
|
return this.mapTypeNode(typeNode.type, nameHintParts, deps);
|
|
533
647
|
}
|
|
648
|
+
const finiteDomain = collectFiniteDomainFromTypeNode(typeNode);
|
|
649
|
+
if (finiteDomain) {
|
|
650
|
+
return this.ensureFiniteDomainType(finiteDomain, nameHintParts, deps);
|
|
651
|
+
}
|
|
534
652
|
if (typescript_1.default.isUnionTypeNode(typeNode)) {
|
|
535
653
|
const filtered = filterNullishUnionTypeNodes(typeNode.types);
|
|
536
654
|
if (filtered.length === 1) {
|
|
@@ -587,6 +705,34 @@ class CppTypeNormalizer {
|
|
|
587
705
|
this.collectKnownTypeDeps(mapped, deps);
|
|
588
706
|
return mapped;
|
|
589
707
|
}
|
|
708
|
+
ensureFiniteDomainType(domain, nameHintParts, deps) {
|
|
709
|
+
const baseName = this.makeSyntheticBaseName(nameHintParts);
|
|
710
|
+
const domainKey = `${baseName}::finite::${domain.primitive}::${domain.variants.map((variant) => `${variant.symbolicName}=${String(variant.value)}`).join("|")}`;
|
|
711
|
+
const existingName = this.finiteDomainNameByKey.get(domainKey);
|
|
712
|
+
if (existingName) {
|
|
713
|
+
deps.add(existingName);
|
|
714
|
+
return existingName;
|
|
715
|
+
}
|
|
716
|
+
const synthesizedName = this.allocateUniqueName(baseName);
|
|
717
|
+
this.finiteDomainNameByKey.set(domainKey, synthesizedName);
|
|
718
|
+
if (this.declMap.has(synthesizedName)) {
|
|
719
|
+
deps.add(synthesizedName);
|
|
720
|
+
return synthesizedName;
|
|
721
|
+
}
|
|
722
|
+
this.allKnownNames.add(synthesizedName);
|
|
723
|
+
this.declMap.set(synthesizedName, {
|
|
724
|
+
name: synthesizedName,
|
|
725
|
+
kind: "enum",
|
|
726
|
+
fields: [],
|
|
727
|
+
aliasType: null,
|
|
728
|
+
deps: new Set(),
|
|
729
|
+
isUnionAlias: false,
|
|
730
|
+
finiteDomain: domain
|
|
731
|
+
});
|
|
732
|
+
this.seedOrder.push(synthesizedName);
|
|
733
|
+
deps.add(synthesizedName);
|
|
734
|
+
return synthesizedName;
|
|
735
|
+
}
|
|
590
736
|
ensureSyntheticStruct(typeNode, nameHintParts, deps) {
|
|
591
737
|
const baseName = this.makeSyntheticBaseName(nameHintParts);
|
|
592
738
|
const syntheticKey = `${baseName}::${typeNode.getText()}`;
|
|
@@ -620,7 +766,8 @@ class CppTypeNormalizer {
|
|
|
620
766
|
fields,
|
|
621
767
|
aliasType: null,
|
|
622
768
|
deps: localDeps,
|
|
623
|
-
isUnionAlias: false
|
|
769
|
+
isUnionAlias: false,
|
|
770
|
+
finiteDomain: null
|
|
624
771
|
});
|
|
625
772
|
this.seedOrder.push(synthesizedName);
|
|
626
773
|
deps.add(synthesizedName);
|
|
@@ -682,6 +829,17 @@ class CppTypeNormalizer {
|
|
|
682
829
|
}
|
|
683
830
|
}
|
|
684
831
|
function renderCppDecl(decl) {
|
|
832
|
+
if (decl.kind === "enum") {
|
|
833
|
+
const variants = decl.finiteDomain?.variants ?? [];
|
|
834
|
+
const underlyingType = variants.length <= 0xff ? "std::uint8_t" : variants.length <= 0xffff ? "std::uint16_t" : "std::uint32_t";
|
|
835
|
+
const lines = [];
|
|
836
|
+
lines.push(`enum class ${decl.name} : ${underlyingType} {`);
|
|
837
|
+
for (const variant of variants) {
|
|
838
|
+
lines.push(` ${variant.symbolicName} = ${variant.code},`);
|
|
839
|
+
}
|
|
840
|
+
lines.push("};");
|
|
841
|
+
return lines.join("\n");
|
|
842
|
+
}
|
|
685
843
|
if (decl.kind === "alias") {
|
|
686
844
|
if (decl.isUnionAlias && decl.aliasType === "QString") {
|
|
687
845
|
return `using ${decl.name} = QString; // union mapped conservatively`;
|
|
@@ -737,6 +895,146 @@ function collectDragDropMimeConstants(spec) {
|
|
|
737
895
|
}
|
|
738
896
|
return constants;
|
|
739
897
|
}
|
|
898
|
+
function createCarrierSummary(counts, singleKinds = [], mayBlob = false, mustBlob = false) {
|
|
899
|
+
return {
|
|
900
|
+
counts: new Set(counts),
|
|
901
|
+
singleKinds: new Set(singleKinds),
|
|
902
|
+
mayBlob,
|
|
903
|
+
mustBlob
|
|
904
|
+
};
|
|
905
|
+
}
|
|
906
|
+
function addOptionalAbsence(summary) {
|
|
907
|
+
const counts = new Set(summary.counts);
|
|
908
|
+
counts.add(0);
|
|
909
|
+
return {
|
|
910
|
+
counts,
|
|
911
|
+
singleKinds: new Set(summary.singleKinds),
|
|
912
|
+
mayBlob: summary.mayBlob,
|
|
913
|
+
mustBlob: summary.mustBlob
|
|
914
|
+
};
|
|
915
|
+
}
|
|
916
|
+
function saturatingItemCountAdd(left, right) {
|
|
917
|
+
if (left === 2 || right === 2)
|
|
918
|
+
return 2;
|
|
919
|
+
const total = left + right;
|
|
920
|
+
return total >= 2 ? 2 : total;
|
|
921
|
+
}
|
|
922
|
+
function mergeCarrierSummaries(left, right) {
|
|
923
|
+
const counts = new Set();
|
|
924
|
+
const singleKinds = new Set();
|
|
925
|
+
for (const leftCount of left.counts) {
|
|
926
|
+
for (const rightCount of right.counts) {
|
|
927
|
+
const total = saturatingItemCountAdd(leftCount, rightCount);
|
|
928
|
+
counts.add(total);
|
|
929
|
+
if (total !== 1)
|
|
930
|
+
continue;
|
|
931
|
+
if (leftCount === 1 && rightCount === 0) {
|
|
932
|
+
for (const kind of left.singleKinds)
|
|
933
|
+
singleKinds.add(kind);
|
|
934
|
+
}
|
|
935
|
+
if (leftCount === 0 && rightCount === 1) {
|
|
936
|
+
for (const kind of right.singleKinds)
|
|
937
|
+
singleKinds.add(kind);
|
|
938
|
+
}
|
|
939
|
+
}
|
|
940
|
+
}
|
|
941
|
+
return {
|
|
942
|
+
counts,
|
|
943
|
+
singleKinds,
|
|
944
|
+
mayBlob: left.mayBlob || right.mayBlob,
|
|
945
|
+
mustBlob: left.mustBlob || right.mustBlob
|
|
946
|
+
};
|
|
947
|
+
}
|
|
948
|
+
function summarizePlanCarrier(node) {
|
|
949
|
+
switch (node.nodeKind) {
|
|
950
|
+
case "leaf":
|
|
951
|
+
if (node.blobEntryId)
|
|
952
|
+
return createCarrierSummary([0], [], true, true);
|
|
953
|
+
if (node.itemEntryId) {
|
|
954
|
+
return createCarrierSummary([1], [node.leaf.region === "dynamic" ? "object" : "string"]);
|
|
955
|
+
}
|
|
956
|
+
return createCarrierSummary([0]);
|
|
957
|
+
case "named":
|
|
958
|
+
return summarizePlanCarrier(node.target);
|
|
959
|
+
case "finite-domain":
|
|
960
|
+
if (node.blobEntryId)
|
|
961
|
+
return createCarrierSummary([0], [], true, true);
|
|
962
|
+
if (node.itemEntryId)
|
|
963
|
+
return createCarrierSummary([1], ["string"]);
|
|
964
|
+
return createCarrierSummary([0]);
|
|
965
|
+
case "array": {
|
|
966
|
+
if (node.extentStrategy === "blob-tail") {
|
|
967
|
+
return createCarrierSummary([0], [], true, false);
|
|
968
|
+
}
|
|
969
|
+
const elementSummary = summarizePlanCarrier(node.element);
|
|
970
|
+
const counts = new Set([0]);
|
|
971
|
+
const singleKinds = new Set();
|
|
972
|
+
if (elementSummary.counts.has(1)) {
|
|
973
|
+
counts.add(1);
|
|
974
|
+
for (const kind of elementSummary.singleKinds)
|
|
975
|
+
singleKinds.add(kind);
|
|
976
|
+
}
|
|
977
|
+
if (elementSummary.counts.has(1) || elementSummary.counts.has(2)) {
|
|
978
|
+
counts.add(2);
|
|
979
|
+
}
|
|
980
|
+
return {
|
|
981
|
+
counts,
|
|
982
|
+
singleKinds,
|
|
983
|
+
mayBlob: true,
|
|
984
|
+
mustBlob: true
|
|
985
|
+
};
|
|
986
|
+
}
|
|
987
|
+
case "struct": {
|
|
988
|
+
let summary = createCarrierSummary([0]);
|
|
989
|
+
for (const field of node.fields) {
|
|
990
|
+
let fieldSummary = summarizePlanCarrier(field.node);
|
|
991
|
+
if (field.optional) {
|
|
992
|
+
fieldSummary = addOptionalAbsence(fieldSummary);
|
|
993
|
+
}
|
|
994
|
+
if (field.presenceStrategy) {
|
|
995
|
+
fieldSummary = {
|
|
996
|
+
counts: new Set(fieldSummary.counts),
|
|
997
|
+
singleKinds: new Set(fieldSummary.singleKinds),
|
|
998
|
+
mayBlob: true,
|
|
999
|
+
mustBlob: true
|
|
1000
|
+
};
|
|
1001
|
+
}
|
|
1002
|
+
summary = mergeCarrierSummaries(summary, fieldSummary);
|
|
1003
|
+
}
|
|
1004
|
+
return summary;
|
|
1005
|
+
}
|
|
1006
|
+
}
|
|
1007
|
+
}
|
|
1008
|
+
function inferDragDropCarrierKinds(plan) {
|
|
1009
|
+
const summary = summarizePlanCarrier(plan.root);
|
|
1010
|
+
const carriers = new Set();
|
|
1011
|
+
const addNoBlobCarriers = () => {
|
|
1012
|
+
if (summary.counts.has(0))
|
|
1013
|
+
carriers.add("array");
|
|
1014
|
+
if (summary.counts.has(1)) {
|
|
1015
|
+
for (const kind of summary.singleKinds)
|
|
1016
|
+
carriers.add(kind);
|
|
1017
|
+
}
|
|
1018
|
+
if (summary.counts.has(2))
|
|
1019
|
+
carriers.add("array");
|
|
1020
|
+
};
|
|
1021
|
+
if (summary.mustBlob) {
|
|
1022
|
+
if (summary.counts.has(0))
|
|
1023
|
+
carriers.add("string");
|
|
1024
|
+
if (summary.counts.has(1) || summary.counts.has(2))
|
|
1025
|
+
carriers.add("array");
|
|
1026
|
+
}
|
|
1027
|
+
else {
|
|
1028
|
+
addNoBlobCarriers();
|
|
1029
|
+
if (summary.mayBlob) {
|
|
1030
|
+
if (summary.counts.has(0))
|
|
1031
|
+
carriers.add("string");
|
|
1032
|
+
if (summary.counts.has(1) || summary.counts.has(2))
|
|
1033
|
+
carriers.add("array");
|
|
1034
|
+
}
|
|
1035
|
+
}
|
|
1036
|
+
return ["string", "array", "object"].filter((kind) => carriers.has(kind));
|
|
1037
|
+
}
|
|
740
1038
|
function collectDragDropPayloadHelpers(spec, cppTypes, cppCodecCatalog) {
|
|
741
1039
|
const seen = new Set();
|
|
742
1040
|
const helpers = [];
|
|
@@ -748,21 +1046,96 @@ function collectDragDropPayloadHelpers(spec, cppTypes, cppCodecCatalog) {
|
|
|
748
1046
|
if (seen.has(typeName))
|
|
749
1047
|
continue;
|
|
750
1048
|
seen.add(typeName);
|
|
751
|
-
const payloadSite = (0,
|
|
1049
|
+
const payloadSite = (0, boundary_codecs_1.getBoundaryPayloadSite)(cppCodecCatalog, service.name, member.name);
|
|
752
1050
|
if (!payloadSite)
|
|
753
1051
|
continue;
|
|
1052
|
+
const plan = cppCodecCatalog.plansByCodecId.get(payloadSite.codecId);
|
|
1053
|
+
if (!plan)
|
|
1054
|
+
continue;
|
|
754
1055
|
helpers.push({
|
|
755
1056
|
typeName,
|
|
756
1057
|
cppType: cppTypes.mapTypeText(member.payloadTypeText, [service.name, member.name, "Payload"]),
|
|
757
|
-
codecId: payloadSite.codecId
|
|
1058
|
+
codecId: payloadSite.codecId,
|
|
1059
|
+
carriers: inferDragDropCarrierKinds(plan)
|
|
758
1060
|
});
|
|
759
1061
|
}
|
|
760
1062
|
}
|
|
761
1063
|
return helpers;
|
|
762
1064
|
}
|
|
1065
|
+
function collectTsDragDropPayloadHelpers(spec, codecCatalog) {
|
|
1066
|
+
const seen = new Set();
|
|
1067
|
+
const helpers = [];
|
|
1068
|
+
for (const service of spec.services) {
|
|
1069
|
+
for (const member of service.members) {
|
|
1070
|
+
if ((member.kind !== "DropTarget" && member.kind !== "HoverTarget") || !member.payloadTypeText)
|
|
1071
|
+
continue;
|
|
1072
|
+
const typeName = member.payloadTypeText.replace(/\s/g, "");
|
|
1073
|
+
if (seen.has(typeName))
|
|
1074
|
+
continue;
|
|
1075
|
+
seen.add(typeName);
|
|
1076
|
+
const payloadSite = (0, boundary_codecs_1.getBoundaryPayloadSite)(codecCatalog, service.name, member.name);
|
|
1077
|
+
if (!payloadSite)
|
|
1078
|
+
continue;
|
|
1079
|
+
const plan = codecCatalog.plansByCodecId.get(payloadSite.codecId);
|
|
1080
|
+
if (!plan)
|
|
1081
|
+
continue;
|
|
1082
|
+
helpers.push({
|
|
1083
|
+
typeName,
|
|
1084
|
+
tsType: mapTypeTextToTs(member.payloadTypeText),
|
|
1085
|
+
codecId: payloadSite.codecId,
|
|
1086
|
+
carriers: inferDragDropCarrierKinds(plan)
|
|
1087
|
+
});
|
|
1088
|
+
}
|
|
1089
|
+
}
|
|
1090
|
+
return helpers;
|
|
1091
|
+
}
|
|
1092
|
+
function renderTsDragDropPayloadHelpers(spec, codecCatalog) {
|
|
1093
|
+
const helpers = collectTsDragDropPayloadHelpers(spec, codecCatalog);
|
|
1094
|
+
return helpers
|
|
1095
|
+
.map((helper) => {
|
|
1096
|
+
const lines = [
|
|
1097
|
+
`function decodeDragDropPayload_${helper.typeName}(rawPayload: unknown): ${helper.tsType} {`,
|
|
1098
|
+
` if (typeof rawPayload !== "string") {`,
|
|
1099
|
+
` throw new Error("Drag/drop payload must be tagged text.");`,
|
|
1100
|
+
` }`,
|
|
1101
|
+
` if (rawPayload.length === 0) {`,
|
|
1102
|
+
` throw new Error("Drag/drop payload is empty.");`,
|
|
1103
|
+
` }`,
|
|
1104
|
+
` const transportTag = rawPayload[0];`,
|
|
1105
|
+
` const payloadText = rawPayload.slice(1);`
|
|
1106
|
+
];
|
|
1107
|
+
if (helper.carriers.includes("string")) {
|
|
1108
|
+
lines.push(` if (transportTag === "S") {`);
|
|
1109
|
+
lines.push(` return decode${helper.codecId}(payloadText);`);
|
|
1110
|
+
lines.push(` }`);
|
|
1111
|
+
}
|
|
1112
|
+
if (helper.carriers.includes("array")) {
|
|
1113
|
+
lines.push(` if (transportTag === "A") {`);
|
|
1114
|
+
lines.push(` const parsed = JSON.parse(payloadText) as unknown;`);
|
|
1115
|
+
lines.push(` if (!Array.isArray(parsed)) {`);
|
|
1116
|
+
lines.push(` throw new Error("Drag/drop payload must be a JSON array.");`);
|
|
1117
|
+
lines.push(` }`);
|
|
1118
|
+
lines.push(` return decode${helper.codecId}(parsed);`);
|
|
1119
|
+
lines.push(` }`);
|
|
1120
|
+
}
|
|
1121
|
+
if (helper.carriers.includes("object")) {
|
|
1122
|
+
lines.push(` if (transportTag === "O") {`);
|
|
1123
|
+
lines.push(` const parsed = JSON.parse(payloadText) as unknown;`);
|
|
1124
|
+
lines.push(` if (parsed === null || Array.isArray(parsed) || typeof parsed !== "object") {`);
|
|
1125
|
+
lines.push(` throw new Error("Drag/drop payload must be a JSON object.");`);
|
|
1126
|
+
lines.push(` }`);
|
|
1127
|
+
lines.push(` return decode${helper.codecId}(parsed);`);
|
|
1128
|
+
lines.push(` }`);
|
|
1129
|
+
}
|
|
1130
|
+
lines.push(` throw new Error(\`Drag/drop payload has an unknown transport tag: \${transportTag}\`);`);
|
|
1131
|
+
lines.push(`}`);
|
|
1132
|
+
return lines.join("\n");
|
|
1133
|
+
})
|
|
1134
|
+
.join("\n\n");
|
|
1135
|
+
}
|
|
763
1136
|
function renderTypesHeader(spec, cppTypes) {
|
|
764
1137
|
const decls = cppTypes.orderedDecls.map(renderCppDecl).join("\n\n");
|
|
765
|
-
const metatypes = cppTypes.
|
|
1138
|
+
const metatypes = cppTypes.metatypeNames
|
|
766
1139
|
.flatMap((name) => [
|
|
767
1140
|
`Q_DECLARE_METATYPE(${spec.widgetName}::${name})`,
|
|
768
1141
|
`Q_DECLARE_METATYPE(QList<${spec.widgetName}::${name}>)`
|
|
@@ -794,14 +1167,12 @@ ${metatypes}
|
|
|
794
1167
|
}
|
|
795
1168
|
function renderWidgetUmbrellaHeader(spec) {
|
|
796
1169
|
return `#pragma once
|
|
797
|
-
// Built by <AnQst_version>
|
|
798
1170
|
#include "${spec.widgetName}Widget.h"
|
|
799
1171
|
#include "${spec.widgetName}Types.h"
|
|
800
1172
|
`;
|
|
801
1173
|
}
|
|
802
|
-
function renderWidgetHeader(spec, cppTypes) {
|
|
1174
|
+
function renderWidgetHeader(spec, cppTypes, cppCodecCatalog) {
|
|
803
1175
|
const widgetClassName = `${spec.widgetName}Widget`;
|
|
804
|
-
const cppCodecCatalog = (0, structured_top_level_codecs_1.buildStructuredCodecCatalog)(spec);
|
|
805
1176
|
const dragDropPayloadHelpers = collectDragDropPayloadHelpers(spec, cppTypes, cppCodecCatalog);
|
|
806
1177
|
const callbackAliases = [];
|
|
807
1178
|
const publicMethods = [];
|
|
@@ -816,10 +1187,8 @@ function renderWidgetHeader(spec, cppTypes) {
|
|
|
816
1187
|
`static QByteArray encodeDragDropPayload_${helper.typeName}(const ${helper.cppType}& payload);`,
|
|
817
1188
|
`static std::optional<${helper.cppType}> decodeDragDropPayload_${helper.typeName}(const QByteArray& rawPayload);`
|
|
818
1189
|
]);
|
|
819
|
-
const bindings = [];
|
|
820
1190
|
for (const service of spec.services) {
|
|
821
1191
|
for (const member of service.members) {
|
|
822
|
-
bindings.push({ service: service.name, member: member.name, kind: member.kind });
|
|
823
1192
|
const memberPascal = pascalCase(member.name);
|
|
824
1193
|
if (member.kind === "Call" && member.payloadTypeText) {
|
|
825
1194
|
const cppType = cppTypes.mapTypeText(member.payloadTypeText, [service.name, member.name, "Payload"]);
|
|
@@ -929,13 +1298,6 @@ private:
|
|
|
929
1298
|
QVariantList args;
|
|
930
1299
|
QDateTime enqueuedAt;
|
|
931
1300
|
};
|
|
932
|
-
struct BridgeBindingRow {
|
|
933
|
-
const char* service;
|
|
934
|
-
const char* member;
|
|
935
|
-
const char* kind;
|
|
936
|
-
};
|
|
937
|
-
static const BridgeBindingRow kBridgeBindings[];
|
|
938
|
-
static constexpr int kBridgeBindingsCount = ${bindings.length};
|
|
939
1301
|
static QString makeBindingKey(const QString& service, const QString& member);
|
|
940
1302
|
void installBridgeBindings();
|
|
941
1303
|
bool hasEmitterListeners(const QString& service, const QString& member) const;
|
|
@@ -957,18 +1319,19 @@ ${fields.map((f) => ` ${f}`).join("\n")}
|
|
|
957
1319
|
};
|
|
958
1320
|
`;
|
|
959
1321
|
}
|
|
960
|
-
function renderCppStub(spec, cppTypes) {
|
|
1322
|
+
function renderCppStub(spec, cppTypes, cppCodecCatalog) {
|
|
961
1323
|
const widgetClassName = `${spec.widgetName}Widget`;
|
|
962
|
-
const cppCodecCatalog = (0, structured_top_level_codecs_1.buildStructuredCodecCatalog)(spec);
|
|
963
1324
|
const dragDropPayloadHelpers = collectDragDropPayloadHelpers(spec, cppTypes, cppCodecCatalog);
|
|
964
|
-
const cppCodecHelpers = (0,
|
|
1325
|
+
const cppCodecHelpers = (0, boundary_codecs_1.renderCppBoundaryCodecHelpers)(cppCodecCatalog, (typeText, pathHintParts) => cppTypes.mapTypeText(typeText, pathHintParts)).trim();
|
|
965
1326
|
const lines = [];
|
|
966
1327
|
lines.push(`#include "include/${spec.widgetName}Widget.h"`);
|
|
1328
|
+
lines.push(`#include "AnQstBase93.h"`);
|
|
967
1329
|
lines.push(`#include <QDebug>`);
|
|
968
1330
|
lines.push(`#include <QElapsedTimer>`);
|
|
969
1331
|
lines.push(`#include <QEventLoop>`);
|
|
970
1332
|
lines.push(`#include <QJsonArray>`);
|
|
971
1333
|
lines.push(`#include <QJsonDocument>`);
|
|
1334
|
+
lines.push(`#include <QJsonObject>`);
|
|
972
1335
|
lines.push(`#include <QMetaType>`);
|
|
973
1336
|
lines.push(`#include <QTimer>`);
|
|
974
1337
|
lines.push(`#include <cstring>`);
|
|
@@ -984,7 +1347,7 @@ function renderCppStub(spec, cppTypes) {
|
|
|
984
1347
|
lines.push("namespace {");
|
|
985
1348
|
lines.push("void registerGeneratedMetaTypes() {");
|
|
986
1349
|
lines.push(" static const bool registered = []() {");
|
|
987
|
-
for (const typeName of cppTypes.
|
|
1350
|
+
for (const typeName of cppTypes.metatypeNames) {
|
|
988
1351
|
lines.push(` qRegisterMetaType<${spec.widgetName}::${typeName}>("${spec.widgetName}::${typeName}");`);
|
|
989
1352
|
lines.push(` qRegisterMetaType<QList<${spec.widgetName}::${typeName}>>("QList<${spec.widgetName}::${typeName}>");`);
|
|
990
1353
|
}
|
|
@@ -1000,20 +1363,78 @@ function renderCppStub(spec, cppTypes) {
|
|
|
1000
1363
|
lines.push("");
|
|
1001
1364
|
for (const helper of dragDropPayloadHelpers) {
|
|
1002
1365
|
lines.push(`QByteArray ${widgetClassName}::encodeDragDropPayload_${helper.typeName}(const ${helper.cppType}& payload) {`);
|
|
1003
|
-
lines.push(`
|
|
1366
|
+
lines.push(` const QVariant wire = encode${helper.codecId}(payload);`);
|
|
1367
|
+
if (helper.carriers.includes("string")) {
|
|
1368
|
+
lines.push(` if (wire.type() == QVariant::String) {`);
|
|
1369
|
+
lines.push(` QByteArray out;`);
|
|
1370
|
+
lines.push(` out.append('S');`);
|
|
1371
|
+
lines.push(` out.append(wire.toString().toUtf8());`);
|
|
1372
|
+
lines.push(` return out;`);
|
|
1373
|
+
lines.push(` }`);
|
|
1374
|
+
}
|
|
1375
|
+
if (helper.carriers.includes("array")) {
|
|
1376
|
+
lines.push(` if (wire.type() == QVariant::List) {`);
|
|
1377
|
+
lines.push(` QByteArray out;`);
|
|
1378
|
+
lines.push(` out.append('A');`);
|
|
1379
|
+
lines.push(` out.append(QJsonDocument(QJsonArray::fromVariantList(wire.toList())).toJson(QJsonDocument::Compact));`);
|
|
1380
|
+
lines.push(` return out;`);
|
|
1381
|
+
lines.push(` }`);
|
|
1382
|
+
}
|
|
1383
|
+
if (helper.carriers.includes("object")) {
|
|
1384
|
+
lines.push(` if (wire.type() == QVariant::Map) {`);
|
|
1385
|
+
lines.push(` QByteArray out;`);
|
|
1386
|
+
lines.push(` out.append('O');`);
|
|
1387
|
+
lines.push(` out.append(QJsonDocument(QJsonObject::fromVariantMap(wire.toMap())).toJson(QJsonDocument::Compact));`);
|
|
1388
|
+
lines.push(` return out;`);
|
|
1389
|
+
lines.push(` }`);
|
|
1390
|
+
}
|
|
1391
|
+
lines.push(` throw std::runtime_error("AnQst drag/drop payload codec emitted an unsupported top-level carrier.");`);
|
|
1004
1392
|
lines.push(`}`);
|
|
1005
1393
|
lines.push("");
|
|
1006
1394
|
lines.push(`std::optional<${helper.cppType}> ${widgetClassName}::decodeDragDropPayload_${helper.typeName}(const QByteArray& rawPayload) {`);
|
|
1007
|
-
lines.push(`
|
|
1008
|
-
lines.push(` const QJsonDocument document = QJsonDocument::fromJson(rawPayload, &parseError);`);
|
|
1009
|
-
lines.push(` if (parseError.error != QJsonParseError::NoError || !document.isArray()) {`);
|
|
1010
|
-
lines.push(` return std::nullopt;`);
|
|
1011
|
-
lines.push(` }`);
|
|
1012
|
-
lines.push(` try {`);
|
|
1013
|
-
lines.push(` return decode${helper.codecId}(document.array().toVariantList());`);
|
|
1014
|
-
lines.push(` } catch (...) {`);
|
|
1395
|
+
lines.push(` if (rawPayload.isEmpty()) {`);
|
|
1015
1396
|
lines.push(` return std::nullopt;`);
|
|
1016
1397
|
lines.push(` }`);
|
|
1398
|
+
lines.push(` const char transportTag = rawPayload.at(0);`);
|
|
1399
|
+
lines.push(` const QByteArray payloadBytes = rawPayload.mid(1);`);
|
|
1400
|
+
if (helper.carriers.includes("string")) {
|
|
1401
|
+
lines.push(` if (transportTag == 'S') {`);
|
|
1402
|
+
lines.push(` try {`);
|
|
1403
|
+
lines.push(` return decode${helper.codecId}(QString::fromUtf8(payloadBytes));`);
|
|
1404
|
+
lines.push(` } catch (...) {`);
|
|
1405
|
+
lines.push(` return std::nullopt;`);
|
|
1406
|
+
lines.push(` }`);
|
|
1407
|
+
lines.push(` }`);
|
|
1408
|
+
}
|
|
1409
|
+
if (helper.carriers.includes("array")) {
|
|
1410
|
+
lines.push(` if (transportTag == 'A') {`);
|
|
1411
|
+
lines.push(` QJsonParseError parseError;`);
|
|
1412
|
+
lines.push(` const QJsonDocument document = QJsonDocument::fromJson(payloadBytes, &parseError);`);
|
|
1413
|
+
lines.push(` if (parseError.error != QJsonParseError::NoError || !document.isArray()) {`);
|
|
1414
|
+
lines.push(` return std::nullopt;`);
|
|
1415
|
+
lines.push(` }`);
|
|
1416
|
+
lines.push(` try {`);
|
|
1417
|
+
lines.push(` return decode${helper.codecId}(QVariant(document.array().toVariantList()));`);
|
|
1418
|
+
lines.push(` } catch (...) {`);
|
|
1419
|
+
lines.push(` return std::nullopt;`);
|
|
1420
|
+
lines.push(` }`);
|
|
1421
|
+
lines.push(` }`);
|
|
1422
|
+
}
|
|
1423
|
+
if (helper.carriers.includes("object")) {
|
|
1424
|
+
lines.push(` if (transportTag == 'O') {`);
|
|
1425
|
+
lines.push(` QJsonParseError parseError;`);
|
|
1426
|
+
lines.push(` const QJsonDocument document = QJsonDocument::fromJson(payloadBytes, &parseError);`);
|
|
1427
|
+
lines.push(` if (parseError.error != QJsonParseError::NoError || !document.isObject()) {`);
|
|
1428
|
+
lines.push(` return std::nullopt;`);
|
|
1429
|
+
lines.push(` }`);
|
|
1430
|
+
lines.push(` try {`);
|
|
1431
|
+
lines.push(` return decode${helper.codecId}(QVariant(document.object().toVariantMap()));`);
|
|
1432
|
+
lines.push(` } catch (...) {`);
|
|
1433
|
+
lines.push(` return std::nullopt;`);
|
|
1434
|
+
lines.push(` }`);
|
|
1435
|
+
lines.push(` }`);
|
|
1436
|
+
}
|
|
1437
|
+
lines.push(` return std::nullopt;`);
|
|
1017
1438
|
lines.push(`}`);
|
|
1018
1439
|
lines.push("");
|
|
1019
1440
|
}
|
|
@@ -1033,14 +1454,6 @@ function renderCppStub(spec, cppTypes) {
|
|
|
1033
1454
|
lines.push("");
|
|
1034
1455
|
}
|
|
1035
1456
|
}
|
|
1036
|
-
lines.push(`const ${widgetClassName}::BridgeBindingRow ${widgetClassName}::kBridgeBindings[] = {`);
|
|
1037
|
-
for (const service of spec.services) {
|
|
1038
|
-
for (const member of service.members) {
|
|
1039
|
-
lines.push(` {"${service.name}", "${member.name}", "${member.kind}"},`);
|
|
1040
|
-
}
|
|
1041
|
-
}
|
|
1042
|
-
lines.push(`};`);
|
|
1043
|
-
lines.push("");
|
|
1044
1457
|
lines.push(`${widgetClassName}::${widgetClassName}(QWidget* parent) : AnQstWebHostBase(parent), handle(this) {`);
|
|
1045
1458
|
lines.push(` static const bool kResourcesInitialized = []() {`);
|
|
1046
1459
|
lines.push(` ::qInitResources_${spec.widgetName}();`);
|
|
@@ -1067,19 +1480,89 @@ function renderCppStub(spec, cppTypes) {
|
|
|
1067
1480
|
for (const member of service.members) {
|
|
1068
1481
|
if (member.kind === "DropTarget" && member.payloadTypeText) {
|
|
1069
1482
|
const cppType = cppTypes.mapTypeText(member.payloadTypeText, [service.name, member.name, "Payload"]);
|
|
1070
|
-
const payloadSite = (0,
|
|
1483
|
+
const payloadSite = (0, boundary_codecs_1.getBoundaryPayloadSite)(cppCodecCatalog, service.name, member.name);
|
|
1484
|
+
const typeName = member.payloadTypeText.replace(/\s/g, "");
|
|
1071
1485
|
lines.push(` QObject::connect(this, &AnQstWebHostBase::anQstBridge_dropReceived, this, [this](const QString& service, const QString& member, const QVariant& payload, double x, double y) {`);
|
|
1072
1486
|
lines.push(` if (service == QStringLiteral("${service.name}") && member == QStringLiteral("${member.name}")) {`);
|
|
1073
|
-
|
|
1487
|
+
if (payloadSite) {
|
|
1488
|
+
lines.push(` if (payload.type() != QVariant::String) {`);
|
|
1489
|
+
lines.push(` emitHostError(`);
|
|
1490
|
+
lines.push(` QStringLiteral("DeserializationError"),`);
|
|
1491
|
+
lines.push(` QStringLiteral("bridge"),`);
|
|
1492
|
+
lines.push(` QStringLiteral("error"),`);
|
|
1493
|
+
lines.push(` true,`);
|
|
1494
|
+
lines.push(` QStringLiteral("Failed to deserialize DropTarget ${service.name}.${member.name}."),`);
|
|
1495
|
+
lines.push(` {`);
|
|
1496
|
+
lines.push(` {QStringLiteral("service"), QStringLiteral("${service.name}")},`);
|
|
1497
|
+
lines.push(` {QStringLiteral("member"), QStringLiteral("${member.name}")},`);
|
|
1498
|
+
lines.push(` {QStringLiteral("detail"), QStringLiteral("Host did not provide tagged drag/drop payload text.")},`);
|
|
1499
|
+
lines.push(` });`);
|
|
1500
|
+
lines.push(` return;`);
|
|
1501
|
+
lines.push(` }`);
|
|
1502
|
+
lines.push(` const auto decodedPayload = decodeDragDropPayload_${typeName}(payload.toString().toUtf8());`);
|
|
1503
|
+
lines.push(` if (!decodedPayload.has_value()) {`);
|
|
1504
|
+
lines.push(` emitHostError(`);
|
|
1505
|
+
lines.push(` QStringLiteral("DeserializationError"),`);
|
|
1506
|
+
lines.push(` QStringLiteral("bridge"),`);
|
|
1507
|
+
lines.push(` QStringLiteral("error"),`);
|
|
1508
|
+
lines.push(` true,`);
|
|
1509
|
+
lines.push(` QStringLiteral("Failed to deserialize DropTarget ${service.name}.${member.name}."),`);
|
|
1510
|
+
lines.push(` {`);
|
|
1511
|
+
lines.push(` {QStringLiteral("service"), QStringLiteral("${service.name}")},`);
|
|
1512
|
+
lines.push(` {QStringLiteral("member"), QStringLiteral("${member.name}")},`);
|
|
1513
|
+
lines.push(` {QStringLiteral("detail"), QStringLiteral("Tagged drag/drop payload did not match the planned boundary carrier.")},`);
|
|
1514
|
+
lines.push(` });`);
|
|
1515
|
+
lines.push(` return;`);
|
|
1516
|
+
lines.push(` }`);
|
|
1517
|
+
lines.push(` emit ${member.name}(*decodedPayload, x, y);`);
|
|
1518
|
+
}
|
|
1519
|
+
else {
|
|
1520
|
+
lines.push(` emit ${member.name}(payload.value<${cppType}>(), x, y);`);
|
|
1521
|
+
}
|
|
1074
1522
|
lines.push(` }`);
|
|
1075
1523
|
lines.push(` });`);
|
|
1076
1524
|
}
|
|
1077
1525
|
else if (member.kind === "HoverTarget" && member.payloadTypeText) {
|
|
1078
1526
|
const cppType = cppTypes.mapTypeText(member.payloadTypeText, [service.name, member.name, "Payload"]);
|
|
1079
|
-
const payloadSite = (0,
|
|
1527
|
+
const payloadSite = (0, boundary_codecs_1.getBoundaryPayloadSite)(cppCodecCatalog, service.name, member.name);
|
|
1528
|
+
const typeName = member.payloadTypeText.replace(/\s/g, "");
|
|
1080
1529
|
lines.push(` QObject::connect(this, &AnQstWebHostBase::anQstBridge_hoverUpdated, this, [this](const QString& service, const QString& member, const QVariant& payload, double x, double y) {`);
|
|
1081
1530
|
lines.push(` if (service == QStringLiteral("${service.name}") && member == QStringLiteral("${member.name}")) {`);
|
|
1082
|
-
|
|
1531
|
+
if (payloadSite) {
|
|
1532
|
+
lines.push(` if (payload.type() != QVariant::String) {`);
|
|
1533
|
+
lines.push(` emitHostError(`);
|
|
1534
|
+
lines.push(` QStringLiteral("DeserializationError"),`);
|
|
1535
|
+
lines.push(` QStringLiteral("bridge"),`);
|
|
1536
|
+
lines.push(` QStringLiteral("error"),`);
|
|
1537
|
+
lines.push(` true,`);
|
|
1538
|
+
lines.push(` QStringLiteral("Failed to deserialize HoverTarget ${service.name}.${member.name}."),`);
|
|
1539
|
+
lines.push(` {`);
|
|
1540
|
+
lines.push(` {QStringLiteral("service"), QStringLiteral("${service.name}")},`);
|
|
1541
|
+
lines.push(` {QStringLiteral("member"), QStringLiteral("${member.name}")},`);
|
|
1542
|
+
lines.push(` {QStringLiteral("detail"), QStringLiteral("Host did not provide tagged drag/drop payload text.")},`);
|
|
1543
|
+
lines.push(` });`);
|
|
1544
|
+
lines.push(` return;`);
|
|
1545
|
+
lines.push(` }`);
|
|
1546
|
+
lines.push(` const auto decodedPayload = decodeDragDropPayload_${typeName}(payload.toString().toUtf8());`);
|
|
1547
|
+
lines.push(` if (!decodedPayload.has_value()) {`);
|
|
1548
|
+
lines.push(` emitHostError(`);
|
|
1549
|
+
lines.push(` QStringLiteral("DeserializationError"),`);
|
|
1550
|
+
lines.push(` QStringLiteral("bridge"),`);
|
|
1551
|
+
lines.push(` QStringLiteral("error"),`);
|
|
1552
|
+
lines.push(` true,`);
|
|
1553
|
+
lines.push(` QStringLiteral("Failed to deserialize HoverTarget ${service.name}.${member.name}."),`);
|
|
1554
|
+
lines.push(` {`);
|
|
1555
|
+
lines.push(` {QStringLiteral("service"), QStringLiteral("${service.name}")},`);
|
|
1556
|
+
lines.push(` {QStringLiteral("member"), QStringLiteral("${member.name}")},`);
|
|
1557
|
+
lines.push(` {QStringLiteral("detail"), QStringLiteral("Tagged drag/drop payload did not match the planned boundary carrier.")},`);
|
|
1558
|
+
lines.push(` });`);
|
|
1559
|
+
lines.push(` return;`);
|
|
1560
|
+
lines.push(` }`);
|
|
1561
|
+
lines.push(` emit ${member.name}(*decodedPayload, x, y);`);
|
|
1562
|
+
}
|
|
1563
|
+
else {
|
|
1564
|
+
lines.push(` emit ${member.name}(payload.value<${cppType}>(), x, y);`);
|
|
1565
|
+
}
|
|
1083
1566
|
lines.push(` }`);
|
|
1084
1567
|
lines.push(` });`);
|
|
1085
1568
|
lines.push(` QObject::connect(this, &AnQstWebHostBase::anQstBridge_hoverLeft, this, [this](const QString& service, const QString& member) {`);
|
|
@@ -1185,12 +1668,12 @@ function renderCppStub(spec, cppTypes) {
|
|
|
1185
1668
|
continue;
|
|
1186
1669
|
const timeoutMs = member.timeoutMs;
|
|
1187
1670
|
const cppType = cppTypes.mapTypeText(member.payloadTypeText, [service.name, member.name, "Payload"]);
|
|
1188
|
-
const payloadSite = (0,
|
|
1671
|
+
const payloadSite = (0, boundary_codecs_1.getBoundaryPayloadSite)(cppCodecCatalog, service.name, member.name);
|
|
1189
1672
|
lines.push(` if (service == QStringLiteral("${service.name}") && member == QStringLiteral("${member.name}")) {`);
|
|
1190
1673
|
for (let i = 0; i < member.parameters.length; i++) {
|
|
1191
1674
|
const p = member.parameters[i];
|
|
1192
1675
|
const pType = cppTypes.mapTypeText(p.typeText, [service.name, member.name, p.name]);
|
|
1193
|
-
const paramSite = (0,
|
|
1676
|
+
const paramSite = (0, boundary_codecs_1.getBoundaryParameterSite)(cppCodecCatalog, service.name, member.name, p.name);
|
|
1194
1677
|
lines.push(` const ${pType} ${p.name} = ${paramSite ? `decode${paramSite.codecId}(args.value(${i}))` : variantToCppExpression(pType, `args.value(${i})`)};`);
|
|
1195
1678
|
}
|
|
1196
1679
|
lines.push(` const QString requestId = QStringLiteral("call-%1").arg(++m_callRequestCounter);`);
|
|
@@ -1260,7 +1743,7 @@ function renderCppStub(spec, cppTypes) {
|
|
|
1260
1743
|
for (let i = 0; i < member.parameters.length; i++) {
|
|
1261
1744
|
const p = member.parameters[i];
|
|
1262
1745
|
const pType = cppTypes.mapTypeText(p.typeText, [service.name, member.name, p.name]);
|
|
1263
|
-
const paramSite = (0,
|
|
1746
|
+
const paramSite = (0, boundary_codecs_1.getBoundaryParameterSite)(cppCodecCatalog, service.name, member.name, p.name);
|
|
1264
1747
|
lines.push(` const ${pType} ${p.name} = ${paramSite ? `decode${paramSite.codecId}(args.value(${i}))` : variantToCppExpression(pType, `args.value(${i})`)};`);
|
|
1265
1748
|
}
|
|
1266
1749
|
const argNames = member.parameters.map((p) => p.name).join(", ");
|
|
@@ -1277,7 +1760,7 @@ function renderCppStub(spec, cppTypes) {
|
|
|
1277
1760
|
if (member.kind !== "Input" || !member.payloadTypeText)
|
|
1278
1761
|
continue;
|
|
1279
1762
|
const cppType = cppTypes.mapTypeText(member.payloadTypeText, [service.name, member.name, "Payload"]);
|
|
1280
|
-
const payloadSite = (0,
|
|
1763
|
+
const payloadSite = (0, boundary_codecs_1.getBoundaryPayloadSite)(cppCodecCatalog, service.name, member.name);
|
|
1281
1764
|
lines.push(` if (service == QStringLiteral("${service.name}") && member == QStringLiteral("${member.name}")) {`);
|
|
1282
1765
|
lines.push(` const ${cppType} typedValue = ${payloadSite ? `decode${payloadSite.codecId}(value)` : variantToCppExpression(cppType, "value")};`);
|
|
1283
1766
|
lines.push(` set${pascalCase(member.name)}(typedValue);`);
|
|
@@ -1299,13 +1782,13 @@ function renderCppStub(spec, cppTypes) {
|
|
|
1299
1782
|
}
|
|
1300
1783
|
if (member.kind === "Slot") {
|
|
1301
1784
|
const ret = member.payloadTypeText ? cppTypes.mapTypeText(member.payloadTypeText, [service.name, member.name, "Payload"]) : "void";
|
|
1302
|
-
const payloadSite = member.payloadTypeText ? (0,
|
|
1785
|
+
const payloadSite = member.payloadTypeText ? (0, boundary_codecs_1.getBoundaryPayloadSite)(cppCodecCatalog, service.name, member.name) : undefined;
|
|
1303
1786
|
const args = member.parameters.map((p) => `${cppTypes.mapTypeText(p.typeText, [service.name, member.name, p.name])} ${p.name}`).join(", ");
|
|
1304
1787
|
lines.push(`${ret} ${widgetClassName}::slot_${member.name}(${args}) {`);
|
|
1305
1788
|
lines.push(` QVariantList invokeArgs;`);
|
|
1306
1789
|
for (const p of member.parameters) {
|
|
1307
1790
|
const pType = mapTsTypeToCpp(p.typeText);
|
|
1308
|
-
const paramSite = (0,
|
|
1791
|
+
const paramSite = (0, boundary_codecs_1.getBoundaryParameterSite)(cppCodecCatalog, service.name, member.name, p.name);
|
|
1309
1792
|
lines.push(` invokeArgs.push_back(${paramSite ? `encode${paramSite.codecId}(${p.name})` : cppToVariantExpression(pType, p.name)});`);
|
|
1310
1793
|
}
|
|
1311
1794
|
lines.push(` QVariant result;`);
|
|
@@ -1330,7 +1813,7 @@ function renderCppStub(spec, cppTypes) {
|
|
|
1330
1813
|
}
|
|
1331
1814
|
else if ((member.kind === "Input" || member.kind === "Output") && member.payloadTypeText) {
|
|
1332
1815
|
const cppType = cppTypes.mapTypeText(member.payloadTypeText, [service.name, member.name, "Payload"]);
|
|
1333
|
-
const payloadSite = (0,
|
|
1816
|
+
const payloadSite = (0, boundary_codecs_1.getBoundaryPayloadSite)(cppCodecCatalog, service.name, member.name);
|
|
1334
1817
|
const cap = member.name.charAt(0).toUpperCase() + member.name.slice(1);
|
|
1335
1818
|
lines.push(`${cppType} ${widgetClassName}::${member.name}() const {`);
|
|
1336
1819
|
lines.push(` return m_${member.name};`);
|
|
@@ -1567,11 +2050,11 @@ function renderTsService(spec, serviceName, codecCatalog) {
|
|
|
1567
2050
|
const constructorBodyLines = [];
|
|
1568
2051
|
for (const m of members) {
|
|
1569
2052
|
const args = m.parameters.map((p) => `${p.name}: ${mapTypeTextToTs(p.typeText)}`).join(", ");
|
|
1570
|
-
const paramSites = m.parameters.map((p) => (0,
|
|
2053
|
+
const paramSites = m.parameters.map((p) => (0, boundary_codecs_1.getBoundaryParameterSite)(codecCatalog, serviceName, m.name, p.name));
|
|
1571
2054
|
const encodedValueArray = paramSites.length > 0
|
|
1572
2055
|
? `[${m.parameters.map((p, index) => `${paramSites[index] ? `encode${paramSites[index].codecId}(${p.name})` : p.name}`).join(", ")}]`
|
|
1573
2056
|
: "[]";
|
|
1574
|
-
const payloadSite = (0,
|
|
2057
|
+
const payloadSite = (0, boundary_codecs_1.getBoundaryPayloadSite)(codecCatalog, serviceName, m.name);
|
|
1575
2058
|
if (m.kind === "Call") {
|
|
1576
2059
|
const ret = mapTypeTextToTs(m.payloadTypeText ?? "void");
|
|
1577
2060
|
if (payloadSite) {
|
|
@@ -1649,30 +2132,18 @@ function renderTsService(spec, serviceName, codecCatalog) {
|
|
|
1649
2132
|
}
|
|
1650
2133
|
if (m.kind === "Output") {
|
|
1651
2134
|
constructorBodyLines.push(` this._bridge.onOutput("${serviceName}", "${m.name}", (value) => {`);
|
|
1652
|
-
constructorBodyLines.push(`
|
|
1653
|
-
constructorBodyLines.push(` this._${m.name}.set(${payloadSite ? `decode${payloadSite.codecId}(value)` : `value as ${tsType}`});`);
|
|
1654
|
-
constructorBodyLines.push(` } catch (error) {`);
|
|
1655
|
-
constructorBodyLines.push(` this._bridge.reportFrontendDiagnostic({`);
|
|
1656
|
-
constructorBodyLines.push(` code: "DeserializationError",`);
|
|
1657
|
-
constructorBodyLines.push(` severity: "error",`);
|
|
1658
|
-
constructorBodyLines.push(` category: "bridge",`);
|
|
1659
|
-
constructorBodyLines.push(` recoverable: true,`);
|
|
1660
|
-
constructorBodyLines.push(` message: \`Failed to deserialize Output ${serviceName}.${m.name}: \${errorMessage(error)}\`,`);
|
|
1661
|
-
constructorBodyLines.push(` service: "${serviceName}",`);
|
|
1662
|
-
constructorBodyLines.push(` member: "${m.name}",`);
|
|
1663
|
-
constructorBodyLines.push(` context: { interaction: "Output" }`);
|
|
1664
|
-
constructorBodyLines.push(` });`);
|
|
1665
|
-
constructorBodyLines.push(` }`);
|
|
2135
|
+
constructorBodyLines.push(` this._${m.name}.set(${payloadSite ? `decode${payloadSite.codecId}(value)` : `value as ${tsType}`});`);
|
|
1666
2136
|
constructorBodyLines.push(` });`);
|
|
1667
2137
|
}
|
|
1668
2138
|
}
|
|
1669
2139
|
if (m.kind === "DropTarget" && m.payloadTypeText) {
|
|
1670
2140
|
const tsType = mapTypeTextToTs(m.payloadTypeText);
|
|
2141
|
+
const typeName = m.payloadTypeText.replace(/\s/g, "");
|
|
1671
2142
|
fieldLines.push(` private readonly _${m.name} = signal<{ payload: ${tsType}; x: number; y: number } | null>(null);`);
|
|
1672
2143
|
methodLines.push(` ${m.name}(): { payload: ${tsType}; x: number; y: number } | null { return this._${m.name}(); }`);
|
|
1673
2144
|
constructorBodyLines.push(` this._bridge.onDrop("${serviceName}", "${m.name}", (payload, x, y) => {`);
|
|
1674
2145
|
constructorBodyLines.push(` try {`);
|
|
1675
|
-
constructorBodyLines.push(` this._${m.name}.set({ payload: ${payloadSite ? `
|
|
2146
|
+
constructorBodyLines.push(` this._${m.name}.set({ payload: ${payloadSite ? `decodeDragDropPayload_${typeName}(payload)` : `payload as ${tsType}`}, x, y });`);
|
|
1676
2147
|
constructorBodyLines.push(` } catch (error) {`);
|
|
1677
2148
|
constructorBodyLines.push(` this._bridge.reportFrontendDiagnostic({`);
|
|
1678
2149
|
constructorBodyLines.push(` code: "DeserializationError",`);
|
|
@@ -1689,11 +2160,12 @@ function renderTsService(spec, serviceName, codecCatalog) {
|
|
|
1689
2160
|
}
|
|
1690
2161
|
if (m.kind === "HoverTarget" && m.payloadTypeText) {
|
|
1691
2162
|
const tsType = mapTypeTextToTs(m.payloadTypeText);
|
|
2163
|
+
const typeName = m.payloadTypeText.replace(/\s/g, "");
|
|
1692
2164
|
fieldLines.push(` private readonly _${m.name} = signal<{ payload: ${tsType}; x: number; y: number } | null>(null);`);
|
|
1693
2165
|
methodLines.push(` ${m.name}(): { payload: ${tsType}; x: number; y: number } | null { return this._${m.name}(); }`);
|
|
1694
2166
|
constructorBodyLines.push(` this._bridge.onHover("${serviceName}", "${m.name}", (payload, x, y) => {`);
|
|
1695
2167
|
constructorBodyLines.push(` try {`);
|
|
1696
|
-
constructorBodyLines.push(` this._${m.name}.set({ payload: ${payloadSite ? `
|
|
2168
|
+
constructorBodyLines.push(` this._${m.name}.set({ payload: ${payloadSite ? `decodeDragDropPayload_${typeName}(payload)` : `payload as ${tsType}`}, x, y });`);
|
|
1697
2169
|
constructorBodyLines.push(` } catch (error) {`);
|
|
1698
2170
|
constructorBodyLines.push(` this._bridge.reportFrontendDiagnostic({`);
|
|
1699
2171
|
constructorBodyLines.push(` code: "DeserializationError",`);
|
|
@@ -1785,18 +2257,20 @@ export declare class ${serviceName} {
|
|
|
1785
2257
|
readonly onSlot: ${onSlotInterfaceName};${classMemberBlock}
|
|
1786
2258
|
}`;
|
|
1787
2259
|
}
|
|
1788
|
-
function renderTsServices(spec) {
|
|
1789
|
-
const codecCatalog = (0, structured_top_level_codecs_1.buildStructuredCodecCatalog)(spec);
|
|
2260
|
+
function renderTsServices(spec, codecCatalog) {
|
|
1790
2261
|
const serviceClasses = spec.services.map((s) => renderTsService(spec, s.name, codecCatalog)).join("\n");
|
|
1791
2262
|
const externalTypeImports = renderRequiredTypeImports(spec, `frontend/${(0, layout_1.generatedFrontendDirName)(spec.widgetName)}/services.ts`).trim();
|
|
1792
2263
|
const localTypeImports = renderLocalTypeImports(spec).trim();
|
|
1793
2264
|
const typeImports = [externalTypeImports, localTypeImports].filter((s) => s.length > 0).join("\n");
|
|
1794
2265
|
const typeImportsBlock = typeImports.length > 0 ? `${typeImports}\n\n` : "";
|
|
2266
|
+
const dragDropHelperBlock = renderTsDragDropPayloadHelpers(spec, codecCatalog).trim();
|
|
2267
|
+
const dragDropHelpers = dragDropHelperBlock.length > 0 ? `\n// Drag/drop payload helpers\n${dragDropHelperBlock}\n` : "";
|
|
1795
2268
|
return `import { Injectable, inject, signal } from "@angular/core";
|
|
1796
2269
|
${typeImportsBlock}
|
|
1797
2270
|
|
|
1798
|
-
//
|
|
1799
|
-
${(0,
|
|
2271
|
+
// Boundary codec plan helpers
|
|
2272
|
+
${(0, boundary_codecs_1.renderTsBoundaryCodecHelpers)(codecCatalog)}
|
|
2273
|
+
${dragDropHelpers}
|
|
1800
2274
|
|
|
1801
2275
|
type SlotHandler = (...args: unknown[]) => unknown;
|
|
1802
2276
|
type OutputHandler = (value: unknown) => void;
|
|
@@ -2717,8 +3191,7 @@ function renderNodeExpressWsTypes(spec) {
|
|
|
2717
3191
|
const sections = [typeImports, typeDecls].filter((s) => s.length > 0);
|
|
2718
3192
|
return sections.length > 0 ? `${sections.join("\n\n")}\n` : "";
|
|
2719
3193
|
}
|
|
2720
|
-
function renderNodeExpressWsIndex(spec) {
|
|
2721
|
-
const codecCatalog = (0, structured_top_level_codecs_1.buildStructuredCodecCatalog)(spec);
|
|
3194
|
+
function renderNodeExpressWsIndex(spec, codecCatalog) {
|
|
2722
3195
|
const typeImports = renderRequiredTypeImports(spec, `backend/node/express/${generatedNodeExpressWsDirName(spec.widgetName)}/index.ts`);
|
|
2723
3196
|
const typeDecls = renderTypeDeclarations(spec, true);
|
|
2724
3197
|
const handlerBridgeTypeName = `${spec.widgetName}HandlerBridge`;
|
|
@@ -2750,8 +3223,8 @@ function renderNodeExpressWsIndex(spec) {
|
|
|
2750
3223
|
.map((member) => {
|
|
2751
3224
|
const ret = mapTypeTextToTs(member.payloadTypeText ?? "void");
|
|
2752
3225
|
const args = nodeParamArgs(member);
|
|
2753
|
-
const paramSites = member.parameters.map((p) => (0,
|
|
2754
|
-
const payloadSite = (0,
|
|
3226
|
+
const paramSites = member.parameters.map((p) => (0, boundary_codecs_1.getBoundaryParameterSite)(codecCatalog, service.name, member.name, p.name));
|
|
3227
|
+
const payloadSite = (0, boundary_codecs_1.getBoundaryPayloadSite)(codecCatalog, service.name, member.name);
|
|
2755
3228
|
const encodedArgs = member.parameters.length > 0
|
|
2756
3229
|
? `[${member.parameters.map((p, index) => `${paramSites[index] ? `encode${paramSites[index].codecId}(${p.name})` : p.name}`).join(", ")}]`
|
|
2757
3230
|
: "[]";
|
|
@@ -2765,7 +3238,7 @@ function renderNodeExpressWsIndex(spec) {
|
|
|
2765
3238
|
.filter((member) => member.kind === "Output" && member.payloadTypeText)
|
|
2766
3239
|
.map((member) => {
|
|
2767
3240
|
const typeText = mapTypeTextToTs(member.payloadTypeText);
|
|
2768
|
-
const payloadSite = (0,
|
|
3241
|
+
const payloadSite = (0, boundary_codecs_1.getBoundaryPayloadSite)(codecCatalog, service.name, member.name);
|
|
2769
3242
|
return ` set${service.name}_${nodeCap(member.name)}(value: ${typeText}): void {
|
|
2770
3243
|
this.setOutputValue("${service.name}", "${member.name}", ${payloadSite ? `encode${payloadSite.codecId}(value)` : "value"});
|
|
2771
3244
|
}`;
|
|
@@ -2820,7 +3293,7 @@ function renderNodeExpressWsIndex(spec) {
|
|
|
2820
3293
|
.filter((member) => (member.kind === "Input" || member.kind === "Output") && member.payloadTypeText)
|
|
2821
3294
|
.map((member) => {
|
|
2822
3295
|
const typeText = mapTypeTextToTs(member.payloadTypeText);
|
|
2823
|
-
const payloadSite = (0,
|
|
3296
|
+
const payloadSite = (0, boundary_codecs_1.getBoundaryPayloadSite)(codecCatalog, service.name, member.name);
|
|
2824
3297
|
if (member.kind === "Input") {
|
|
2825
3298
|
return ` ${member.name}: {\n get: () => session.readInput("${service.name}", "${member.name}").then((value) => ${payloadSite ? `decode${payloadSite.codecId}(value)` : `value as ${typeText}`}),\n on: (handler: (value: ${typeText}) => void) => session.onInput("${service.name}", "${member.name}", (value) => handler(${payloadSite ? `decode${payloadSite.codecId}(value)` : `value as ${typeText}`}))\n },`;
|
|
2826
3299
|
}
|
|
@@ -2834,8 +3307,8 @@ function renderNodeExpressWsIndex(spec) {
|
|
|
2834
3307
|
.flatMap((service) => service.members
|
|
2835
3308
|
.filter((member) => member.kind === "Call" && member.payloadTypeText)
|
|
2836
3309
|
.map((member) => {
|
|
2837
|
-
const paramSites = member.parameters.map((p) => (0,
|
|
2838
|
-
const payloadSite = (0,
|
|
3310
|
+
const paramSites = member.parameters.map((p) => (0, boundary_codecs_1.getBoundaryParameterSite)(codecCatalog, service.name, member.name, p.name));
|
|
3311
|
+
const payloadSite = (0, boundary_codecs_1.getBoundaryPayloadSite)(codecCatalog, service.name, member.name);
|
|
2839
3312
|
const decodedArgs = member.parameters.length > 0
|
|
2840
3313
|
? member.parameters.map((p, index) => `${paramSites[index] ? `decode${paramSites[index].codecId}(args[${index}])` : `args[${index}] as ${mapTypeTextToTs(p.typeText)}`}`).join(", ")
|
|
2841
3314
|
: "";
|
|
@@ -2890,7 +3363,7 @@ function renderNodeExpressWsIndex(spec) {
|
|
|
2890
3363
|
.flatMap((service) => service.members
|
|
2891
3364
|
.filter((member) => member.kind === "Emitter")
|
|
2892
3365
|
.map((member) => {
|
|
2893
|
-
const paramSites = member.parameters.map((p) => (0,
|
|
3366
|
+
const paramSites = member.parameters.map((p) => (0, boundary_codecs_1.getBoundaryParameterSite)(codecCatalog, service.name, member.name, p.name));
|
|
2894
3367
|
const decodedArgs = member.parameters.length > 0
|
|
2895
3368
|
? member.parameters.map((p, index) => `${paramSites[index] ? `decode${paramSites[index].codecId}(args[${index}])` : `args[${index}] as ${mapTypeTextToTs(p.typeText)}`}`).join(", ")
|
|
2896
3369
|
: "";
|
|
@@ -2935,7 +3408,7 @@ function renderNodeExpressWsIndex(spec) {
|
|
|
2935
3408
|
.flatMap((service) => service.members
|
|
2936
3409
|
.filter((member) => member.kind === "Input" && member.payloadTypeText)
|
|
2937
3410
|
.map((member) => {
|
|
2938
|
-
const payloadSite = (0,
|
|
3411
|
+
const payloadSite = (0, boundary_codecs_1.getBoundaryPayloadSite)(codecCatalog, service.name, member.name);
|
|
2939
3412
|
return ` if (service === "${service.name}" && member === "${member.name}") {
|
|
2940
3413
|
const handler = implementation.${service.name}.${member.name};
|
|
2941
3414
|
if (typeof handler !== "function") {
|
|
@@ -2976,8 +3449,8 @@ import type { WebSocket, WebSocketServer } from "ws";
|
|
|
2976
3449
|
${typeImports}
|
|
2977
3450
|
${typeDecls}
|
|
2978
3451
|
|
|
2979
|
-
//
|
|
2980
|
-
${(0,
|
|
3452
|
+
// Boundary codec plan helpers
|
|
3453
|
+
${(0, boundary_codecs_1.renderTsBoundaryCodecHelpers)(codecCatalog)}
|
|
2981
3454
|
|
|
2982
3455
|
${handlerInterfaces}
|
|
2983
3456
|
|
|
@@ -3439,7 +3912,23 @@ function renderTypeRootIndexDts(spec) {
|
|
|
3439
3912
|
const typeDecls = renderTypeTypesDts(spec).trim();
|
|
3440
3913
|
const serviceDecls = renderTypeServicesDts(spec).trim();
|
|
3441
3914
|
const sections = [indexDecls, typeDecls, serviceDecls].filter((s) => s.length > 0);
|
|
3442
|
-
|
|
3915
|
+
if (sections.length === 0)
|
|
3916
|
+
return "";
|
|
3917
|
+
const dedupedLines = [];
|
|
3918
|
+
const seenImportLines = new Set();
|
|
3919
|
+
for (const line of sections.join("\n\n").split("\n")) {
|
|
3920
|
+
const trimmed = line.trim();
|
|
3921
|
+
if (!trimmed.startsWith("import type ")) {
|
|
3922
|
+
dedupedLines.push(line);
|
|
3923
|
+
continue;
|
|
3924
|
+
}
|
|
3925
|
+
if (seenImportLines.has(trimmed))
|
|
3926
|
+
continue;
|
|
3927
|
+
seenImportLines.add(trimmed);
|
|
3928
|
+
dedupedLines.push(line);
|
|
3929
|
+
}
|
|
3930
|
+
const deduped = dedupedLines.join("\n").replace(/\n{3,}/g, "\n\n");
|
|
3931
|
+
return `${deduped}\n`;
|
|
3443
3932
|
}
|
|
3444
3933
|
function generatedCppLibraryDirName(widgetName) {
|
|
3445
3934
|
return (0, layout_1.generatedQtWidgetDirName)(widgetName);
|
|
@@ -3452,10 +3941,11 @@ function generateOutputs(spec, options = { emitQWidget: true, emitAngularService
|
|
|
3452
3941
|
const cppDir = `backend/cpp/qt/${generatedCppLibraryDirName(spec.widgetName)}`;
|
|
3453
3942
|
const nodeDir = `backend/node/express/${generatedNodeExpressWsDirName(spec.widgetName)}`;
|
|
3454
3943
|
const outputs = {};
|
|
3944
|
+
const codecCatalog = (0, boundary_codecs_1.buildBoundaryCodecCatalog)(spec);
|
|
3455
3945
|
if (options.emitAngularService) {
|
|
3456
3946
|
outputs[`${frontendDir}/package.json`] = renderNpmPackage(spec);
|
|
3457
3947
|
outputs[`${frontendDir}/index.ts`] = renderTsIndex();
|
|
3458
|
-
outputs[`${frontendDir}/services.ts`] = renderTsServices(spec);
|
|
3948
|
+
outputs[`${frontendDir}/services.ts`] = renderTsServices(spec, codecCatalog);
|
|
3459
3949
|
outputs[`${frontendDir}/types.ts`] = renderTsTypes(spec);
|
|
3460
3950
|
outputs[`${frontendDir}/index.js`] = renderJsIndex();
|
|
3461
3951
|
outputs[`${frontendDir}/services.js`] = renderJsServices();
|
|
@@ -3469,13 +3959,13 @@ function generateOutputs(spec, options = { emitQWidget: true, emitAngularService
|
|
|
3469
3959
|
outputs[`${cppDir}/CMakeLists.txt`] = renderCMake(spec);
|
|
3470
3960
|
outputs[`${cppDir}/${spec.widgetName}.qrc`] = renderEmbeddedQrc(spec.widgetName, []);
|
|
3471
3961
|
outputs[`${cppDir}/include/${spec.widgetName}.h`] = renderWidgetUmbrellaHeader(spec);
|
|
3472
|
-
outputs[`${cppDir}/include/${spec.widgetName}Widget.h`] = renderWidgetHeader(spec, cppTypes);
|
|
3962
|
+
outputs[`${cppDir}/include/${spec.widgetName}Widget.h`] = renderWidgetHeader(spec, cppTypes, codecCatalog);
|
|
3473
3963
|
outputs[`${cppDir}/include/${spec.widgetName}Types.h`] = renderTypesHeader(spec, cppTypes);
|
|
3474
|
-
outputs[`${cppDir}/${spec.widgetName}.cpp`] = renderCppStub(spec, cppTypes);
|
|
3964
|
+
outputs[`${cppDir}/${spec.widgetName}.cpp`] = renderCppStub(spec, cppTypes, codecCatalog);
|
|
3475
3965
|
}
|
|
3476
3966
|
if (options.emitNodeExpressWs) {
|
|
3477
3967
|
outputs[`${nodeDir}/package.json`] = renderNodeExpressWsPackage(spec);
|
|
3478
|
-
outputs[`${nodeDir}/index.ts`] = renderNodeExpressWsIndex(spec);
|
|
3968
|
+
outputs[`${nodeDir}/index.ts`] = renderNodeExpressWsIndex(spec, codecCatalog);
|
|
3479
3969
|
outputs[`${nodeDir}/types/index.d.ts`] = renderNodeExpressWsTypes(spec);
|
|
3480
3970
|
}
|
|
3481
3971
|
return outputs;
|