@naeemo/capnp 0.8.1 → 0.9.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/cli-DGBgLdGK.js +551 -0
- package/dist/cli-DGBgLdGK.js.map +1 -0
- package/dist/cli-DlZBZozW.js +251 -0
- package/dist/cli-DlZBZozW.js.map +1 -0
- package/dist/cli-audit-j58Eyd5d.js +380 -0
- package/dist/cli-audit-j58Eyd5d.js.map +1 -0
- package/dist/cli-bench-bkR1vGK8.js +4528 -0
- package/dist/cli-bench-bkR1vGK8.js.map +1 -0
- package/dist/cli-gen-aCkffW1Z.js +1325 -0
- package/dist/cli-gen-aCkffW1Z.js.map +1 -0
- package/dist/cli.js +88 -2257
- package/dist/cli.js.map +1 -1
- package/dist/core-DstLMMvA.js +2 -0
- package/dist/index.cjs +37 -2
- package/dist/index.cjs.map +1 -1
- package/dist/index.js +37 -2
- package/dist/index.js.map +1 -1
- package/dist/message-reader-_TCBf61G.js +712 -0
- package/dist/message-reader-_TCBf61G.js.map +1 -0
- package/dist/schema-types-GVRD1pwE.js +18 -0
- package/dist/schema-types-GVRD1pwE.js.map +1 -0
- package/dist/segment-yid_PYS5.js +176 -0
- package/dist/segment-yid_PYS5.js.map +1 -0
- package/package.json +7 -3
|
@@ -0,0 +1,1325 @@
|
|
|
1
|
+
#!/usr/bin/env node
|
|
2
|
+
import { r as ElementSize } from "./segment-yid_PYS5.js";
|
|
3
|
+
import { t as MessageReader } from "./message-reader-_TCBf61G.js";
|
|
4
|
+
import "./core-DstLMMvA.js";
|
|
5
|
+
import { parseArgs } from "node:util";
|
|
6
|
+
import { existsSync, mkdtempSync, readFileSync, rmSync, writeFileSync } from "node:fs";
|
|
7
|
+
import { tmpdir } from "node:os";
|
|
8
|
+
import { execSync } from "node:child_process";
|
|
9
|
+
import { join } from "node:path";
|
|
10
|
+
|
|
11
|
+
//#region src/codegen/generator.ts
|
|
12
|
+
const DEFAULT_OPTIONS = { runtimeImportPath: "@naeemo/capnp" };
|
|
13
|
+
/**
|
|
14
|
+
* 从 CodeGeneratorRequest 生成 TypeScript 代码
|
|
15
|
+
*/
|
|
16
|
+
function generateFromRequest(request, options) {
|
|
17
|
+
const opts = {
|
|
18
|
+
...DEFAULT_OPTIONS,
|
|
19
|
+
...options
|
|
20
|
+
};
|
|
21
|
+
const files = /* @__PURE__ */ new Map();
|
|
22
|
+
const nodes = request.nodes;
|
|
23
|
+
const requestedFiles = request.requestedFiles;
|
|
24
|
+
for (const file of requestedFiles) {
|
|
25
|
+
const filename = file.filename.replace(/\.capnp$/, ".ts");
|
|
26
|
+
const code = generateFile(file.id, nodes, opts);
|
|
27
|
+
files.set(filename, code);
|
|
28
|
+
}
|
|
29
|
+
return files;
|
|
30
|
+
}
|
|
31
|
+
/**
|
|
32
|
+
* 生成单个文件的 TypeScript 代码
|
|
33
|
+
*/
|
|
34
|
+
function generateFile(fileId, allNodes, options) {
|
|
35
|
+
const lines = [];
|
|
36
|
+
lines.push("// Generated by @naeemo/capnp");
|
|
37
|
+
lines.push("// DO NOT EDIT MANUALLY");
|
|
38
|
+
lines.push("");
|
|
39
|
+
lines.push(`import { MessageReader, MessageBuilder, StructReader, StructBuilder, createUnionReader, createUnionBuilder } from "${options.runtimeImportPath}";`);
|
|
40
|
+
lines.push("");
|
|
41
|
+
lines.push("// XOR helpers for default value encoding");
|
|
42
|
+
lines.push("function xorFloat32(a: number, b: number): number {");
|
|
43
|
+
lines.push(" const view = new DataView(new ArrayBuffer(4));");
|
|
44
|
+
lines.push(" view.setFloat32(0, a, true);");
|
|
45
|
+
lines.push(" const aBits = view.getUint32(0, true);");
|
|
46
|
+
lines.push(" view.setFloat32(0, b, true);");
|
|
47
|
+
lines.push(" const bBits = view.getUint32(0, true);");
|
|
48
|
+
lines.push(" view.setUint32(0, aBits ^ bBits, true);");
|
|
49
|
+
lines.push(" return view.getFloat32(0, true);");
|
|
50
|
+
lines.push("}");
|
|
51
|
+
lines.push("");
|
|
52
|
+
lines.push("function xorFloat64(a: number, b: number): number {");
|
|
53
|
+
lines.push(" const view = new DataView(new ArrayBuffer(8));");
|
|
54
|
+
lines.push(" view.setFloat64(0, a, true);");
|
|
55
|
+
lines.push(" const aBits = view.getBigUint64(0, true);");
|
|
56
|
+
lines.push(" view.setFloat64(0, b, true);");
|
|
57
|
+
lines.push(" const bBits = view.getBigUint64(0, true);");
|
|
58
|
+
lines.push(" view.setBigUint64(0, aBits ^ bBits, true);");
|
|
59
|
+
lines.push(" return view.getFloat64(0, true);");
|
|
60
|
+
lines.push("}");
|
|
61
|
+
lines.push("");
|
|
62
|
+
let filePrefix = "";
|
|
63
|
+
try {
|
|
64
|
+
filePrefix = allNodes.find((n) => n.id === fileId)?.displayName || "";
|
|
65
|
+
} catch (_e) {}
|
|
66
|
+
const fileNodes = allNodes.filter((n) => {
|
|
67
|
+
try {
|
|
68
|
+
if (n.scopeId === fileId) return true;
|
|
69
|
+
if (n.scopeId === 0n && filePrefix && n.displayName.startsWith(`${filePrefix}:`)) return n.displayName.substring(filePrefix.length + 1).includes("$");
|
|
70
|
+
} catch (_e) {}
|
|
71
|
+
return false;
|
|
72
|
+
});
|
|
73
|
+
for (const node of fileNodes) {
|
|
74
|
+
if (node.isStruct) lines.push(generateStruct(node, allNodes));
|
|
75
|
+
else if (node.isEnum) lines.push(generateEnum(node));
|
|
76
|
+
else if (node.isInterface) lines.push(generateInterface(node, allNodes));
|
|
77
|
+
lines.push("");
|
|
78
|
+
}
|
|
79
|
+
return lines.join("\n");
|
|
80
|
+
}
|
|
81
|
+
/**
|
|
82
|
+
* 生成 Struct 的 TypeScript 代码
|
|
83
|
+
*/
|
|
84
|
+
function generateStruct(node, allNodes) {
|
|
85
|
+
const lines = [];
|
|
86
|
+
const structName = getShortName(node.displayName);
|
|
87
|
+
let unionGroups = [];
|
|
88
|
+
let regularFields = [];
|
|
89
|
+
let groupFields = [];
|
|
90
|
+
try {
|
|
91
|
+
const analysis = analyzeFields(node, allNodes);
|
|
92
|
+
unionGroups = analysis.unionGroups;
|
|
93
|
+
regularFields = analysis.regularFields;
|
|
94
|
+
groupFields = analysis.groupFields;
|
|
95
|
+
} catch (_e) {
|
|
96
|
+
lines.push(`export interface ${structName} {`);
|
|
97
|
+
lines.push(" // Note: Could not parse struct fields");
|
|
98
|
+
lines.push("}");
|
|
99
|
+
lines.push("");
|
|
100
|
+
lines.push(`export class ${structName}Reader {`);
|
|
101
|
+
lines.push(" constructor(private reader: StructReader) {}");
|
|
102
|
+
lines.push("}");
|
|
103
|
+
lines.push("");
|
|
104
|
+
lines.push(`export class ${structName}Builder {`);
|
|
105
|
+
lines.push(" constructor(private builder: StructBuilder) {}");
|
|
106
|
+
lines.push("}");
|
|
107
|
+
return lines.join("\n");
|
|
108
|
+
}
|
|
109
|
+
lines.push(`export interface ${structName} {`);
|
|
110
|
+
for (const field of regularFields) {
|
|
111
|
+
if (!field.isSlot) continue;
|
|
112
|
+
const tsType = getTypeScriptType(field.slotType, allNodes);
|
|
113
|
+
lines.push(` ${field.name}: ${tsType};`);
|
|
114
|
+
}
|
|
115
|
+
for (const { field: groupField, groupNode } of groupFields) {
|
|
116
|
+
const groupName = groupField.name;
|
|
117
|
+
try {
|
|
118
|
+
for (const field of groupNode.structFields) {
|
|
119
|
+
if (!field.isSlot) continue;
|
|
120
|
+
const tsType = getTypeScriptType(field.slotType, allNodes);
|
|
121
|
+
lines.push(` ${groupName}${capitalize(field.name)}: ${tsType};`);
|
|
122
|
+
}
|
|
123
|
+
} catch (_e) {}
|
|
124
|
+
}
|
|
125
|
+
for (const [groupIndex, group] of unionGroups.entries()) {
|
|
126
|
+
const unionName = group.fields.length > 0 ? `${capitalize(group.fields[0].name)}Union` : `Union${groupIndex}`;
|
|
127
|
+
lines.push(` ${unionName}: ${generateUnionVariantType(group, allNodes)};`);
|
|
128
|
+
}
|
|
129
|
+
lines.push("}");
|
|
130
|
+
lines.push("");
|
|
131
|
+
lines.push(`export class ${structName}Reader {`);
|
|
132
|
+
lines.push(" constructor(private reader: StructReader) {}");
|
|
133
|
+
lines.push("");
|
|
134
|
+
for (const field of regularFields) {
|
|
135
|
+
if (!field.isSlot) continue;
|
|
136
|
+
const getter = generateFieldGetter(field);
|
|
137
|
+
lines.push(` ${getter}`);
|
|
138
|
+
lines.push("");
|
|
139
|
+
}
|
|
140
|
+
for (const { field: groupField, groupNode } of groupFields) {
|
|
141
|
+
const groupName = groupField.name;
|
|
142
|
+
try {
|
|
143
|
+
for (const field of groupNode.structFields) {
|
|
144
|
+
if (!field.isSlot) continue;
|
|
145
|
+
const getter = generateGroupFieldGetter(field, groupName);
|
|
146
|
+
lines.push(` ${getter}`);
|
|
147
|
+
lines.push("");
|
|
148
|
+
}
|
|
149
|
+
} catch (_e) {}
|
|
150
|
+
}
|
|
151
|
+
for (const [groupIndex, group] of unionGroups.entries()) {
|
|
152
|
+
const unionName = group.fields.length > 0 ? `${capitalize(group.fields[0].name)}Union` : `Union${groupIndex}`;
|
|
153
|
+
const variants = {};
|
|
154
|
+
for (const field of group.fields) variants[field.discriminantValue] = field.name;
|
|
155
|
+
const variantsStr = JSON.stringify(variants).replace(/"/g, "'");
|
|
156
|
+
lines.push(` get${unionName}Tag(): number {`);
|
|
157
|
+
lines.push(` return this.reader.getUint16(${group.discriminantOffset * 2});`);
|
|
158
|
+
lines.push(" }");
|
|
159
|
+
lines.push("");
|
|
160
|
+
lines.push(` get${unionName}Variant(): string | undefined {`);
|
|
161
|
+
lines.push(` const tag = this.get${unionName}Tag();`);
|
|
162
|
+
lines.push(` const variants = ${variantsStr};`);
|
|
163
|
+
lines.push(" return variants[tag];");
|
|
164
|
+
lines.push(" }");
|
|
165
|
+
lines.push("");
|
|
166
|
+
for (const field of group.fields) {
|
|
167
|
+
const getter = generateUnionFieldGetter(field, unionName, field.discriminantValue);
|
|
168
|
+
lines.push(` ${getter}`);
|
|
169
|
+
lines.push("");
|
|
170
|
+
}
|
|
171
|
+
for (const field of group.fields) {
|
|
172
|
+
const setter = generateUnionFieldSetter(field, unionName, field.discriminantValue, group.discriminantOffset * 2);
|
|
173
|
+
lines.push(` ${setter}`);
|
|
174
|
+
lines.push("");
|
|
175
|
+
}
|
|
176
|
+
}
|
|
177
|
+
lines.push("}");
|
|
178
|
+
lines.push("");
|
|
179
|
+
lines.push(`export class ${structName}Builder {`);
|
|
180
|
+
lines.push(" constructor(private builder: StructBuilder) {}");
|
|
181
|
+
lines.push("");
|
|
182
|
+
for (const field of regularFields) {
|
|
183
|
+
if (!field.isSlot) continue;
|
|
184
|
+
const setter = generateFieldSetter(field);
|
|
185
|
+
lines.push(` ${setter}`);
|
|
186
|
+
lines.push("");
|
|
187
|
+
}
|
|
188
|
+
for (const { field: groupField, groupNode } of groupFields) {
|
|
189
|
+
const groupName = groupField.name;
|
|
190
|
+
try {
|
|
191
|
+
for (const field of groupNode.structFields) {
|
|
192
|
+
if (!field.isSlot) continue;
|
|
193
|
+
const setter = generateGroupFieldSetter(field, groupName);
|
|
194
|
+
lines.push(` ${setter}`);
|
|
195
|
+
lines.push("");
|
|
196
|
+
}
|
|
197
|
+
} catch (_e) {}
|
|
198
|
+
}
|
|
199
|
+
for (const [groupIndex, group] of unionGroups.entries()) {
|
|
200
|
+
const unionName = group.fields.length > 0 ? `${capitalize(group.fields[0].name)}Union` : `Union${groupIndex}`;
|
|
201
|
+
for (const field of group.fields) {
|
|
202
|
+
const setter = generateUnionFieldSetter(field, unionName, field.discriminantValue, group.discriminantOffset * 2);
|
|
203
|
+
lines.push(` ${setter}`);
|
|
204
|
+
lines.push("");
|
|
205
|
+
}
|
|
206
|
+
}
|
|
207
|
+
lines.push("}");
|
|
208
|
+
return lines.join("\n");
|
|
209
|
+
}
|
|
210
|
+
/**
|
|
211
|
+
* 分析字段,分离 Union 字段和普通字段,展开 Group 字段
|
|
212
|
+
*/
|
|
213
|
+
function analyzeFields(node, allNodes) {
|
|
214
|
+
const fields = node.structFields;
|
|
215
|
+
const unionGroups = /* @__PURE__ */ new Map();
|
|
216
|
+
const regularFields = [];
|
|
217
|
+
const groupFields = [];
|
|
218
|
+
for (const field of fields) {
|
|
219
|
+
if (field.isGroup) {
|
|
220
|
+
const groupNode = allNodes.find((n) => n.id === field.groupTypeId);
|
|
221
|
+
if (groupNode?.isStruct) groupFields.push({
|
|
222
|
+
field,
|
|
223
|
+
groupNode
|
|
224
|
+
});
|
|
225
|
+
continue;
|
|
226
|
+
}
|
|
227
|
+
if (node.structDiscriminantCount > 0 && field.discriminantValue !== 65535 && field.discriminantValue !== 0) {
|
|
228
|
+
const discriminantOffset = node.structDiscriminantOffset;
|
|
229
|
+
if (!unionGroups.has(discriminantOffset)) unionGroups.set(discriminantOffset, {
|
|
230
|
+
discriminantOffset,
|
|
231
|
+
fields: []
|
|
232
|
+
});
|
|
233
|
+
unionGroups.get(discriminantOffset).fields.push(field);
|
|
234
|
+
} else regularFields.push(field);
|
|
235
|
+
}
|
|
236
|
+
return {
|
|
237
|
+
unionGroups: Array.from(unionGroups.values()),
|
|
238
|
+
regularFields,
|
|
239
|
+
groupFields
|
|
240
|
+
};
|
|
241
|
+
}
|
|
242
|
+
/**
|
|
243
|
+
* 生成 Union variant 类型
|
|
244
|
+
*/
|
|
245
|
+
function generateUnionVariantType(group, allNodes) {
|
|
246
|
+
return group.fields.map((f) => {
|
|
247
|
+
const tsType = f.isSlot ? getTypeScriptType(f.slotType, allNodes) : "unknown";
|
|
248
|
+
return `{ tag: ${f.discriminantValue}; variant: '${f.name}'; value: ${tsType} }`;
|
|
249
|
+
}).join(" | ");
|
|
250
|
+
}
|
|
251
|
+
/**
|
|
252
|
+
* 生成 Union 字段的 getter
|
|
253
|
+
*/
|
|
254
|
+
function generateUnionFieldGetter(field, unionName, discriminantValue) {
|
|
255
|
+
const name = field.name;
|
|
256
|
+
const type = field.slotType;
|
|
257
|
+
if (!type || !field.isSlot) return `get${capitalize(name)}(): unknown | undefined { return this.get${unionName}Tag() === ${discriminantValue} ? undefined : undefined; }`;
|
|
258
|
+
const returnType = getTypeScriptTypeForSetter(type);
|
|
259
|
+
let body;
|
|
260
|
+
switch (type.kind) {
|
|
261
|
+
case "void":
|
|
262
|
+
body = "return undefined;";
|
|
263
|
+
break;
|
|
264
|
+
case "bool":
|
|
265
|
+
body = `return this.reader.getBool(${field.slotOffset * 8});`;
|
|
266
|
+
break;
|
|
267
|
+
case "int8":
|
|
268
|
+
body = `return this.reader.getInt8(${field.slotOffset});`;
|
|
269
|
+
break;
|
|
270
|
+
case "int16":
|
|
271
|
+
body = `return this.reader.getInt16(${field.slotOffset * 2});`;
|
|
272
|
+
break;
|
|
273
|
+
case "int32":
|
|
274
|
+
body = `return this.reader.getInt32(${field.slotOffset * 4});`;
|
|
275
|
+
break;
|
|
276
|
+
case "int64":
|
|
277
|
+
body = `return this.reader.getInt64(${field.slotOffset * 8});`;
|
|
278
|
+
break;
|
|
279
|
+
case "uint8":
|
|
280
|
+
body = `return this.reader.getUint8(${field.slotOffset});`;
|
|
281
|
+
break;
|
|
282
|
+
case "uint16":
|
|
283
|
+
body = `return this.reader.getUint16(${field.slotOffset * 2});`;
|
|
284
|
+
break;
|
|
285
|
+
case "uint32":
|
|
286
|
+
body = `return this.reader.getUint32(${field.slotOffset * 4});`;
|
|
287
|
+
break;
|
|
288
|
+
case "uint64":
|
|
289
|
+
body = `return this.reader.getUint64(${field.slotOffset * 8});`;
|
|
290
|
+
break;
|
|
291
|
+
case "float32":
|
|
292
|
+
body = `return this.reader.getFloat32(${field.slotOffset * 4});`;
|
|
293
|
+
break;
|
|
294
|
+
case "float64":
|
|
295
|
+
body = `return this.reader.getFloat64(${field.slotOffset * 8});`;
|
|
296
|
+
break;
|
|
297
|
+
case "text":
|
|
298
|
+
body = `return this.reader.getText(${field.slotOffset});`;
|
|
299
|
+
break;
|
|
300
|
+
case "data":
|
|
301
|
+
body = `return this.reader.getData(${field.slotOffset});`;
|
|
302
|
+
break;
|
|
303
|
+
default: body = "return undefined;";
|
|
304
|
+
}
|
|
305
|
+
return `get${capitalize(name)}(): ${returnType} | undefined {
|
|
306
|
+
if (this.get${unionName}Tag() !== ${discriminantValue}) return undefined;
|
|
307
|
+
${body}
|
|
308
|
+
}`;
|
|
309
|
+
}
|
|
310
|
+
/**
|
|
311
|
+
* 生成 Group 字段的 getter
|
|
312
|
+
* Group 字段在父 struct 的 data/pointer section 中,但命名空间是 Group 的名字
|
|
313
|
+
*/
|
|
314
|
+
function generateGroupFieldGetter(field, groupName) {
|
|
315
|
+
const name = field.name;
|
|
316
|
+
const type = field.slotType;
|
|
317
|
+
if (!type) return `get${capitalize(groupName)}${capitalize(name)}(): unknown { return undefined; }`;
|
|
318
|
+
switch (type.kind) {
|
|
319
|
+
case "void": return `get${capitalize(groupName)}${capitalize(name)}(): void { return undefined; }`;
|
|
320
|
+
case "bool": return `get${capitalize(groupName)}${capitalize(name)}(): boolean { return this.reader.getBool(${field.slotOffset * 8}); }`;
|
|
321
|
+
case "int8": return `get${capitalize(groupName)}${capitalize(name)}(): number { return this.reader.getInt8(${field.slotOffset}); }`;
|
|
322
|
+
case "int16": return `get${capitalize(groupName)}${capitalize(name)}(): number { return this.reader.getInt16(${field.slotOffset * 2}); }`;
|
|
323
|
+
case "int32": return `get${capitalize(groupName)}${capitalize(name)}(): number { return this.reader.getInt32(${field.slotOffset * 4}); }`;
|
|
324
|
+
case "int64": return `get${capitalize(groupName)}${capitalize(name)}(): bigint { return this.reader.getInt64(${field.slotOffset * 8}); }`;
|
|
325
|
+
case "uint8": return `get${capitalize(groupName)}${capitalize(name)}(): number { return this.reader.getUint8(${field.slotOffset}); }`;
|
|
326
|
+
case "uint16": return `get${capitalize(groupName)}${capitalize(name)}(): number { return this.reader.getUint16(${field.slotOffset * 2}); }`;
|
|
327
|
+
case "uint32": return `get${capitalize(groupName)}${capitalize(name)}(): number { return this.reader.getUint32(${field.slotOffset * 4}); }`;
|
|
328
|
+
case "uint64": return `get${capitalize(groupName)}${capitalize(name)}(): bigint { return this.reader.getUint64(${field.slotOffset * 8}); }`;
|
|
329
|
+
case "float32": return `get${capitalize(groupName)}${capitalize(name)}(): number { return this.reader.getFloat32(${field.slotOffset * 4}); }`;
|
|
330
|
+
case "float64": return `get${capitalize(groupName)}${capitalize(name)}(): number { return this.reader.getFloat64(${field.slotOffset * 8}); }`;
|
|
331
|
+
case "text": return `get${capitalize(groupName)}${capitalize(name)}(): string { return this.reader.getText(${field.slotOffset}); }`;
|
|
332
|
+
case "data": return `get${capitalize(groupName)}${capitalize(name)}(): Uint8Array { return this.reader.getData(${field.slotOffset}); }`;
|
|
333
|
+
default: return `get${capitalize(groupName)}${capitalize(name)}(): unknown { return undefined; }`;
|
|
334
|
+
}
|
|
335
|
+
}
|
|
336
|
+
/**
|
|
337
|
+
* 生成 Group 字段的 setter
|
|
338
|
+
*/
|
|
339
|
+
function generateGroupFieldSetter(field, groupName) {
|
|
340
|
+
const name = field.name;
|
|
341
|
+
const type = field.slotType;
|
|
342
|
+
const paramType = getTypeScriptTypeForSetter(type);
|
|
343
|
+
if (!type || !field.isSlot) return `set${capitalize(groupName)}${capitalize(name)}(value: ${paramType}): void { /* TODO */ }`;
|
|
344
|
+
switch (type.kind) {
|
|
345
|
+
case "void": return `set${capitalize(groupName)}${capitalize(name)}(value: ${paramType}): void { }`;
|
|
346
|
+
case "bool": return `set${capitalize(groupName)}${capitalize(name)}(value: ${paramType}): void { this.builder.setBool(${field.slotOffset * 8}, value); }`;
|
|
347
|
+
case "int8": return `set${capitalize(groupName)}${capitalize(name)}(value: ${paramType}): void { this.builder.setInt8(${field.slotOffset}, value); }`;
|
|
348
|
+
case "int16": return `set${capitalize(groupName)}${capitalize(name)}(value: ${paramType}): void { this.builder.setInt16(${field.slotOffset * 2}, value); }`;
|
|
349
|
+
case "int32": return `set${capitalize(groupName)}${capitalize(name)}(value: ${paramType}): void { this.builder.setInt32(${field.slotOffset * 4}, value); }`;
|
|
350
|
+
case "int64": return `set${capitalize(groupName)}${capitalize(name)}(value: ${paramType}): void { this.builder.setInt64(${field.slotOffset * 8}, value); }`;
|
|
351
|
+
case "uint8": return `set${capitalize(groupName)}${capitalize(name)}(value: ${paramType}): void { this.builder.setUint8(${field.slotOffset}, value); }`;
|
|
352
|
+
case "uint16": return `set${capitalize(groupName)}${capitalize(name)}(value: ${paramType}): void { this.builder.setUint16(${field.slotOffset * 2}, value); }`;
|
|
353
|
+
case "uint32": return `set${capitalize(groupName)}${capitalize(name)}(value: ${paramType}): void { this.builder.setUint32(${field.slotOffset * 4}, value); }`;
|
|
354
|
+
case "uint64": return `set${capitalize(groupName)}${capitalize(name)}(value: ${paramType}): void { this.builder.setUint64(${field.slotOffset * 8}, value); }`;
|
|
355
|
+
case "float32": return `set${capitalize(groupName)}${capitalize(name)}(value: ${paramType}): void { this.builder.setFloat32(${field.slotOffset * 4}, value); }`;
|
|
356
|
+
case "float64": return `set${capitalize(groupName)}${capitalize(name)}(value: ${paramType}): void { this.builder.setFloat64(${field.slotOffset * 8}, value); }`;
|
|
357
|
+
case "text": return `set${capitalize(groupName)}${capitalize(name)}(value: ${paramType}): void { this.builder.setText(${field.slotOffset}, value); }`;
|
|
358
|
+
case "data": return `set${capitalize(groupName)}${capitalize(name)}(value: ${paramType}): void { this.builder.setData(${field.slotOffset}, value); }`;
|
|
359
|
+
default: return `set${capitalize(groupName)}${capitalize(name)}(value: ${paramType}): void { /* TODO */ }`;
|
|
360
|
+
}
|
|
361
|
+
}
|
|
362
|
+
/**
|
|
363
|
+
* 生成 Union 字段的 setter
|
|
364
|
+
*/
|
|
365
|
+
function generateUnionFieldSetter(field, _unionName, discriminantValue, discriminantOffset) {
|
|
366
|
+
const name = field.name;
|
|
367
|
+
const type = field.slotType;
|
|
368
|
+
const paramType = getTypeScriptTypeForSetter(type);
|
|
369
|
+
if (!type || !field.isSlot) return `set${capitalize(name)}(value: ${paramType}): void {
|
|
370
|
+
this.builder.setUint16(${discriminantOffset}, ${discriminantValue});
|
|
371
|
+
}`;
|
|
372
|
+
let body;
|
|
373
|
+
switch (type.kind) {
|
|
374
|
+
case "void":
|
|
375
|
+
body = "";
|
|
376
|
+
break;
|
|
377
|
+
case "bool":
|
|
378
|
+
body = `this.builder.setBool(${field.slotOffset * 8}, value);`;
|
|
379
|
+
break;
|
|
380
|
+
case "int8":
|
|
381
|
+
body = `this.builder.setInt8(${field.slotOffset}, value);`;
|
|
382
|
+
break;
|
|
383
|
+
case "int16":
|
|
384
|
+
body = `this.builder.setInt16(${field.slotOffset * 2}, value);`;
|
|
385
|
+
break;
|
|
386
|
+
case "int32":
|
|
387
|
+
body = `this.builder.setInt32(${field.slotOffset * 4}, value);`;
|
|
388
|
+
break;
|
|
389
|
+
case "int64":
|
|
390
|
+
body = `this.builder.setInt64(${field.slotOffset * 8}, value);`;
|
|
391
|
+
break;
|
|
392
|
+
case "uint8":
|
|
393
|
+
body = `this.builder.setUint8(${field.slotOffset}, value);`;
|
|
394
|
+
break;
|
|
395
|
+
case "uint16":
|
|
396
|
+
body = `this.builder.setUint16(${field.slotOffset * 2}, value);`;
|
|
397
|
+
break;
|
|
398
|
+
case "uint32":
|
|
399
|
+
body = `this.builder.setUint32(${field.slotOffset * 4}, value);`;
|
|
400
|
+
break;
|
|
401
|
+
case "uint64":
|
|
402
|
+
body = `this.builder.setUint64(${field.slotOffset * 8}, value);`;
|
|
403
|
+
break;
|
|
404
|
+
case "float32":
|
|
405
|
+
body = `this.builder.setFloat32(${field.slotOffset * 4}, value);`;
|
|
406
|
+
break;
|
|
407
|
+
case "float64":
|
|
408
|
+
body = `this.builder.setFloat64(${field.slotOffset * 8}, value);`;
|
|
409
|
+
break;
|
|
410
|
+
case "text":
|
|
411
|
+
body = `this.builder.setText(${field.slotOffset}, value);`;
|
|
412
|
+
break;
|
|
413
|
+
case "data":
|
|
414
|
+
body = `this.builder.setData(${field.slotOffset}, value);`;
|
|
415
|
+
break;
|
|
416
|
+
default: body = "";
|
|
417
|
+
}
|
|
418
|
+
return `set${capitalize(name)}(value: ${paramType}): void {
|
|
419
|
+
this.builder.setUint16(${discriminantOffset}, ${discriminantValue});
|
|
420
|
+
${body}
|
|
421
|
+
}`;
|
|
422
|
+
}
|
|
423
|
+
/**
|
|
424
|
+
* 从 ValueReader 提取默认值,用于代码生成
|
|
425
|
+
*/
|
|
426
|
+
function extractDefaultValue(field) {
|
|
427
|
+
const defaultValue = field.defaultValue;
|
|
428
|
+
if (!defaultValue) return void 0;
|
|
429
|
+
const type = field.slotType;
|
|
430
|
+
if (!type) return void 0;
|
|
431
|
+
switch (type.kind) {
|
|
432
|
+
case "bool":
|
|
433
|
+
if (defaultValue.isBool) return defaultValue.boolValue ? "true" : "false";
|
|
434
|
+
return;
|
|
435
|
+
case "int8":
|
|
436
|
+
if (defaultValue.isInt8) return `${defaultValue.int8Value}`;
|
|
437
|
+
return;
|
|
438
|
+
case "int16":
|
|
439
|
+
if (defaultValue.isInt16) return `${defaultValue.int16Value}`;
|
|
440
|
+
return;
|
|
441
|
+
case "int32":
|
|
442
|
+
if (defaultValue.isInt32) return `${defaultValue.int32Value}`;
|
|
443
|
+
return;
|
|
444
|
+
case "int64":
|
|
445
|
+
if (defaultValue.isInt64) return `${defaultValue.int64Value}n`;
|
|
446
|
+
return;
|
|
447
|
+
case "uint8":
|
|
448
|
+
if (defaultValue.isUint8) return `${defaultValue.uint8Value}`;
|
|
449
|
+
return;
|
|
450
|
+
case "uint16":
|
|
451
|
+
if (defaultValue.isUint16) return `${defaultValue.uint16Value}`;
|
|
452
|
+
return;
|
|
453
|
+
case "uint32":
|
|
454
|
+
if (defaultValue.isUint32) return `${defaultValue.uint32Value}`;
|
|
455
|
+
return;
|
|
456
|
+
case "uint64":
|
|
457
|
+
if (defaultValue.isUint64) return `${defaultValue.uint64Value}n`;
|
|
458
|
+
return;
|
|
459
|
+
case "float32":
|
|
460
|
+
if (defaultValue.isFloat32) {
|
|
461
|
+
const val = defaultValue.float32Value;
|
|
462
|
+
if (Number.isNaN(val)) return "NaN";
|
|
463
|
+
if (val === Number.POSITIVE_INFINITY) return "Infinity";
|
|
464
|
+
if (val === Number.NEGATIVE_INFINITY) return "-Infinity";
|
|
465
|
+
return `${val}`;
|
|
466
|
+
}
|
|
467
|
+
return;
|
|
468
|
+
case "float64":
|
|
469
|
+
if (defaultValue.isFloat64) {
|
|
470
|
+
const val = defaultValue.float64Value;
|
|
471
|
+
if (Number.isNaN(val)) return "NaN";
|
|
472
|
+
if (val === Number.POSITIVE_INFINITY) return "Infinity";
|
|
473
|
+
if (val === Number.NEGATIVE_INFINITY) return "-Infinity";
|
|
474
|
+
return `${val}`;
|
|
475
|
+
}
|
|
476
|
+
return;
|
|
477
|
+
case "enum":
|
|
478
|
+
if (defaultValue.isEnum) return `${defaultValue.enumValue}`;
|
|
479
|
+
return;
|
|
480
|
+
default: return;
|
|
481
|
+
}
|
|
482
|
+
}
|
|
483
|
+
/**
|
|
484
|
+
* 生成 XOR 解码表达式(用于 getter)
|
|
485
|
+
* stored ^ default = actual
|
|
486
|
+
*/
|
|
487
|
+
function generateXorDecode(expr, defaultValue, typeKind) {
|
|
488
|
+
switch (typeKind) {
|
|
489
|
+
case "bool": return `${expr} !== ${defaultValue}`;
|
|
490
|
+
case "int8":
|
|
491
|
+
case "int16":
|
|
492
|
+
case "int32": return `(${expr} ^ ${defaultValue}) | 0`;
|
|
493
|
+
case "uint8":
|
|
494
|
+
case "uint16":
|
|
495
|
+
case "uint32": return `${expr} ^ ${defaultValue}`;
|
|
496
|
+
case "int64":
|
|
497
|
+
case "uint64": return `${expr} ^ ${defaultValue}`;
|
|
498
|
+
case "float32": return `xorFloat32(${expr}, ${defaultValue})`;
|
|
499
|
+
case "float64": return `xorFloat64(${expr}, ${defaultValue})`;
|
|
500
|
+
case "enum": return `${expr} ^ ${defaultValue}`;
|
|
501
|
+
default: return expr;
|
|
502
|
+
}
|
|
503
|
+
}
|
|
504
|
+
/**
|
|
505
|
+
* 生成 XOR 编码表达式(用于 setter)
|
|
506
|
+
* actual ^ default = stored
|
|
507
|
+
*/
|
|
508
|
+
function generateXorEncode(valueExpr, defaultValue, typeKind) {
|
|
509
|
+
switch (typeKind) {
|
|
510
|
+
case "bool": return `${valueExpr} !== ${defaultValue}`;
|
|
511
|
+
case "int8":
|
|
512
|
+
case "int16":
|
|
513
|
+
case "int32": return `(${valueExpr} >>> 0) ^ ${defaultValue}`;
|
|
514
|
+
case "uint8":
|
|
515
|
+
case "uint16":
|
|
516
|
+
case "uint32": return `${valueExpr} ^ ${defaultValue}`;
|
|
517
|
+
case "int64":
|
|
518
|
+
case "uint64": return `${valueExpr} ^ ${defaultValue}`;
|
|
519
|
+
case "float32": return `xorFloat32(${valueExpr}, ${defaultValue})`;
|
|
520
|
+
case "float64": return `xorFloat64(${valueExpr}, ${defaultValue})`;
|
|
521
|
+
case "enum": return `${valueExpr} ^ ${defaultValue}`;
|
|
522
|
+
default: return valueExpr;
|
|
523
|
+
}
|
|
524
|
+
}
|
|
525
|
+
/**
|
|
526
|
+
* 生成字段 getter
|
|
527
|
+
*/
|
|
528
|
+
function generateFieldGetter(field) {
|
|
529
|
+
const name = field.name;
|
|
530
|
+
const type = field.slotType;
|
|
531
|
+
const defaultValue = extractDefaultValue(field);
|
|
532
|
+
if (!type) return `get ${name}(): unknown { return undefined; }`;
|
|
533
|
+
if (defaultValue !== void 0 && type.kind !== "text" && type.kind !== "data") {
|
|
534
|
+
const decodedExpr = generateXorDecode(`this.reader.get${capitalize(type.kind)}(${getByteOffset(field, type.kind)})`, defaultValue, type.kind);
|
|
535
|
+
return `get ${name}(): ${getTypeScriptTypeForSetter(type)} { return ${decodedExpr}; }`;
|
|
536
|
+
}
|
|
537
|
+
switch (type.kind) {
|
|
538
|
+
case "void": return `get ${name}(): void { return undefined; }`;
|
|
539
|
+
case "bool": return `get ${name}(): boolean { return this.reader.getBool(${field.slotOffset * 8}); }`;
|
|
540
|
+
case "int8": return `get ${name}(): number { return this.reader.getInt8(${field.slotOffset}); }`;
|
|
541
|
+
case "int16": return `get ${name}(): number { return this.reader.getInt16(${field.slotOffset * 2}); }`;
|
|
542
|
+
case "int32": return `get ${name}(): number { return this.reader.getInt32(${field.slotOffset * 4}); }`;
|
|
543
|
+
case "int64": return `get ${name}(): bigint { return this.reader.getInt64(${field.slotOffset * 8}); }`;
|
|
544
|
+
case "uint8": return `get ${name}(): number { return this.reader.getUint8(${field.slotOffset}); }`;
|
|
545
|
+
case "uint16": return `get ${name}(): number { return this.reader.getUint16(${field.slotOffset * 2}); }`;
|
|
546
|
+
case "uint32": return `get ${name}(): number { return this.reader.getUint32(${field.slotOffset * 4}); }`;
|
|
547
|
+
case "uint64": return `get ${name}(): bigint { return this.reader.getUint64(${field.slotOffset * 8}); }`;
|
|
548
|
+
case "float32": return `get ${name}(): number { return this.reader.getFloat32(${field.slotOffset * 4}); }`;
|
|
549
|
+
case "float64": return `get ${name}(): number { return this.reader.getFloat64(${field.slotOffset * 8}); }`;
|
|
550
|
+
case "text": return `get ${name}(): string { return this.reader.getText(${field.slotOffset}); }`;
|
|
551
|
+
case "data": return `get ${name}(): Uint8Array { return this.reader.getData(${field.slotOffset}); }`;
|
|
552
|
+
default: return `get ${name}(): unknown { return undefined; }`;
|
|
553
|
+
}
|
|
554
|
+
}
|
|
555
|
+
/**
|
|
556
|
+
* 获取字节偏移量
|
|
557
|
+
*/
|
|
558
|
+
function getByteOffset(field, typeKind) {
|
|
559
|
+
switch (typeKind) {
|
|
560
|
+
case "bool": return field.slotOffset * 8;
|
|
561
|
+
case "int8":
|
|
562
|
+
case "uint8": return field.slotOffset;
|
|
563
|
+
case "int16":
|
|
564
|
+
case "uint16":
|
|
565
|
+
case "enum": return field.slotOffset * 2;
|
|
566
|
+
case "int32":
|
|
567
|
+
case "uint32":
|
|
568
|
+
case "float32": return field.slotOffset * 4;
|
|
569
|
+
case "int64":
|
|
570
|
+
case "uint64":
|
|
571
|
+
case "float64": return field.slotOffset * 8;
|
|
572
|
+
default: return field.slotOffset;
|
|
573
|
+
}
|
|
574
|
+
}
|
|
575
|
+
/**
|
|
576
|
+
* 生成字段 setter
|
|
577
|
+
*/
|
|
578
|
+
function generateFieldSetter(field) {
|
|
579
|
+
const name = field.name;
|
|
580
|
+
const type = field.slotType;
|
|
581
|
+
const paramType = getTypeScriptTypeForSetter(type);
|
|
582
|
+
const defaultValue = extractDefaultValue(field);
|
|
583
|
+
if (!type) return `set${capitalize(name)}(value: unknown): void { /* TODO */ }`;
|
|
584
|
+
if (defaultValue !== void 0 && type.kind !== "text" && type.kind !== "data") {
|
|
585
|
+
const encodedExpr = generateXorEncode("value", defaultValue, type.kind);
|
|
586
|
+
const setterMethod = `set${capitalize(type.kind)}`;
|
|
587
|
+
return `set${capitalize(name)}(value: ${paramType}): void { this.builder.${setterMethod}(${getByteOffset(field, type.kind)}, ${encodedExpr}); }`;
|
|
588
|
+
}
|
|
589
|
+
switch (type.kind) {
|
|
590
|
+
case "void": return `set${capitalize(name)}(value: void): void { /* void */ }`;
|
|
591
|
+
case "bool": return `set${capitalize(name)}(value: ${paramType}): void { this.builder.setBool(${field.slotOffset * 8}, value); }`;
|
|
592
|
+
case "int8": return `set${capitalize(name)}(value: ${paramType}): void { this.builder.setInt8(${field.slotOffset}, value); }`;
|
|
593
|
+
case "int16": return `set${capitalize(name)}(value: ${paramType}): void { this.builder.setInt16(${field.slotOffset * 2}, value); }`;
|
|
594
|
+
case "int32": return `set${capitalize(name)}(value: ${paramType}): void { this.builder.setInt32(${field.slotOffset * 4}, value); }`;
|
|
595
|
+
case "int64": return `set${capitalize(name)}(value: ${paramType}): void { this.builder.setInt64(${field.slotOffset * 8}, value); }`;
|
|
596
|
+
case "uint8": return `set${capitalize(name)}(value: ${paramType}): void { this.builder.setUint8(${field.slotOffset}, value); }`;
|
|
597
|
+
case "uint16": return `set${capitalize(name)}(value: ${paramType}): void { this.builder.setUint16(${field.slotOffset * 2}, value); }`;
|
|
598
|
+
case "uint32": return `set${capitalize(name)}(value: ${paramType}): void { this.builder.setUint32(${field.slotOffset * 4}, value); }`;
|
|
599
|
+
case "uint64": return `set${capitalize(name)}(value: ${paramType}): void { this.builder.setUint64(${field.slotOffset * 8}, value); }`;
|
|
600
|
+
case "float32": return `set${capitalize(name)}(value: ${paramType}): void { this.builder.setFloat32(${field.slotOffset * 4}, value); }`;
|
|
601
|
+
case "float64": return `set${capitalize(name)}(value: ${paramType}): void { this.builder.setFloat64(${field.slotOffset * 8}, value); }`;
|
|
602
|
+
case "text": return `set${capitalize(name)}(value: ${paramType}): void { this.builder.setText(${field.slotOffset}, value); }`;
|
|
603
|
+
case "data": return `set${capitalize(name)}(value: ${paramType}): void { this.builder.setData(${field.slotOffset}, value); }`;
|
|
604
|
+
default: return `set${capitalize(name)}(value: ${paramType}): void { /* TODO */ }`;
|
|
605
|
+
}
|
|
606
|
+
}
|
|
607
|
+
/**
|
|
608
|
+
* 获取 TypeScript setter 参数类型
|
|
609
|
+
*/
|
|
610
|
+
function getTypeScriptTypeForSetter(type) {
|
|
611
|
+
if (!type) return "unknown";
|
|
612
|
+
switch (type.kind) {
|
|
613
|
+
case "void": return "void";
|
|
614
|
+
case "bool": return "boolean";
|
|
615
|
+
case "int8":
|
|
616
|
+
case "int16":
|
|
617
|
+
case "int32":
|
|
618
|
+
case "uint8":
|
|
619
|
+
case "uint16":
|
|
620
|
+
case "uint32":
|
|
621
|
+
case "float32":
|
|
622
|
+
case "float64": return "number";
|
|
623
|
+
case "int64":
|
|
624
|
+
case "uint64": return "bigint";
|
|
625
|
+
case "text": return "string";
|
|
626
|
+
case "data": return "Uint8Array";
|
|
627
|
+
default: return "unknown";
|
|
628
|
+
}
|
|
629
|
+
}
|
|
630
|
+
/**
|
|
631
|
+
* 首字母大写
|
|
632
|
+
*/
|
|
633
|
+
function capitalize(str) {
|
|
634
|
+
return str.charAt(0).toUpperCase() + str.slice(1);
|
|
635
|
+
}
|
|
636
|
+
/**
|
|
637
|
+
* 生成 Enum 的 TypeScript 代码
|
|
638
|
+
*/
|
|
639
|
+
function generateEnum(node) {
|
|
640
|
+
const lines = [];
|
|
641
|
+
const enumName = getShortName(node.displayName);
|
|
642
|
+
lines.push(`export enum ${enumName} {`);
|
|
643
|
+
for (const enumerant of node.enumEnumerants) lines.push(` ${enumerant.name} = ${enumerant.codeOrder},`);
|
|
644
|
+
lines.push("}");
|
|
645
|
+
return lines.join("\n");
|
|
646
|
+
}
|
|
647
|
+
/**
|
|
648
|
+
* 获取 TypeScript 类型字符串
|
|
649
|
+
*/
|
|
650
|
+
function getTypeScriptType(type, allNodes) {
|
|
651
|
+
if (!type) return "unknown";
|
|
652
|
+
switch (type.kind) {
|
|
653
|
+
case "void": return "void";
|
|
654
|
+
case "bool": return "boolean";
|
|
655
|
+
case "int8":
|
|
656
|
+
case "int16":
|
|
657
|
+
case "int32":
|
|
658
|
+
case "uint8":
|
|
659
|
+
case "uint16":
|
|
660
|
+
case "uint32":
|
|
661
|
+
case "float32":
|
|
662
|
+
case "float64": return "number";
|
|
663
|
+
case "int64":
|
|
664
|
+
case "uint64": return "bigint";
|
|
665
|
+
case "text": return "string";
|
|
666
|
+
case "data": return "Uint8Array";
|
|
667
|
+
case "list": {
|
|
668
|
+
const elementType = type.listElementType;
|
|
669
|
+
if (!elementType) return "unknown[]";
|
|
670
|
+
return `${getTypeScriptType(elementType, allNodes)}[]`;
|
|
671
|
+
}
|
|
672
|
+
case "struct": {
|
|
673
|
+
const structNode = allNodes.find((n) => n.id === type.typeId);
|
|
674
|
+
if (structNode) return getShortName(structNode.displayName);
|
|
675
|
+
return "unknown";
|
|
676
|
+
}
|
|
677
|
+
case "enum": {
|
|
678
|
+
const enumNode = allNodes.find((n) => n.id === type.typeId);
|
|
679
|
+
if (enumNode) return getShortName(enumNode.displayName);
|
|
680
|
+
return "unknown";
|
|
681
|
+
}
|
|
682
|
+
default: return "unknown";
|
|
683
|
+
}
|
|
684
|
+
}
|
|
685
|
+
/**
|
|
686
|
+
* 从 displayName 获取短名称
|
|
687
|
+
* 例如 "test-schema.capnp:Person" -> "Person"
|
|
688
|
+
* 处理 auto-generated 名称如 "Calculator.evaluate$Params" -> "EvaluateParams"
|
|
689
|
+
*/
|
|
690
|
+
function getShortName(displayName) {
|
|
691
|
+
if (!displayName) return "Unknown";
|
|
692
|
+
const colonIndex = displayName.lastIndexOf(":");
|
|
693
|
+
let name = colonIndex >= 0 ? displayName.substring(colonIndex + 1) : displayName;
|
|
694
|
+
if (name.includes(".")) {
|
|
695
|
+
const parts = name.split(".");
|
|
696
|
+
if (parts.length === 2 && parts[1].includes("$")) {
|
|
697
|
+
const methodPart = parts[1];
|
|
698
|
+
const dollarIndex = methodPart.indexOf("$");
|
|
699
|
+
if (dollarIndex > 0) {
|
|
700
|
+
const methodName = methodPart.substring(0, dollarIndex);
|
|
701
|
+
const suffix = methodPart.substring(dollarIndex + 1);
|
|
702
|
+
name = capitalize(methodName) + suffix;
|
|
703
|
+
} else name = capitalize(parts[1]);
|
|
704
|
+
} else name = parts[parts.length - 1];
|
|
705
|
+
}
|
|
706
|
+
name = name.replace(/[^a-zA-Z0-9_]/g, "_");
|
|
707
|
+
return name;
|
|
708
|
+
}
|
|
709
|
+
/**
|
|
710
|
+
* 生成 Interface 的 TypeScript 代码
|
|
711
|
+
* 包括:Method Constants、Client Class、Server Interface、Server Stub
|
|
712
|
+
*/
|
|
713
|
+
function generateInterface(node, allNodes) {
|
|
714
|
+
const lines = [];
|
|
715
|
+
const interfaceName = getShortName(node.displayName);
|
|
716
|
+
const methods = node.interfaceMethods;
|
|
717
|
+
if (methods.length === 0) return `// Interface ${interfaceName} has no methods`;
|
|
718
|
+
lines.push(`// ${interfaceName} Method IDs`);
|
|
719
|
+
lines.push(`export const ${interfaceName}InterfaceId = ${node.id}n;`);
|
|
720
|
+
lines.push(`export const ${interfaceName}MethodIds = {`);
|
|
721
|
+
for (const method of methods) lines.push(` ${method.name}: ${method.codeOrder},`);
|
|
722
|
+
lines.push("} as const;");
|
|
723
|
+
lines.push("");
|
|
724
|
+
lines.push(`// ${interfaceName} Server Interface`);
|
|
725
|
+
lines.push(`export interface ${interfaceName}Server {`);
|
|
726
|
+
for (const method of methods) {
|
|
727
|
+
const paramType = getTypeNameById(method.paramStructType, allNodes, "unknown");
|
|
728
|
+
const resultType = getTypeNameById(method.resultStructType, allNodes, "unknown");
|
|
729
|
+
lines.push(` ${method.name}(context: CallContext<${paramType}Reader, ${resultType}Builder>): Promise<void> | void;`);
|
|
730
|
+
}
|
|
731
|
+
lines.push("}");
|
|
732
|
+
lines.push("");
|
|
733
|
+
lines.push(`// ${interfaceName} Server Stub`);
|
|
734
|
+
lines.push(`export class ${interfaceName}Stub {`);
|
|
735
|
+
lines.push(` private server: ${interfaceName}Server;`);
|
|
736
|
+
lines.push("");
|
|
737
|
+
lines.push(` constructor(server: ${interfaceName}Server) {`);
|
|
738
|
+
lines.push(" this.server = server;");
|
|
739
|
+
lines.push(" }");
|
|
740
|
+
lines.push("");
|
|
741
|
+
lines.push(` static readonly interfaceId = ${node.id}n;`);
|
|
742
|
+
lines.push("");
|
|
743
|
+
lines.push(" /** Dispatch a method call to the appropriate handler */");
|
|
744
|
+
lines.push(" async dispatch(methodId: number, context: CallContext<unknown, unknown>): Promise<void> {");
|
|
745
|
+
lines.push(" switch (methodId) {");
|
|
746
|
+
for (const method of methods) {
|
|
747
|
+
const paramType = getTypeNameById(method.paramStructType, allNodes, "unknown");
|
|
748
|
+
const resultType = getTypeNameById(method.resultStructType, allNodes, "unknown");
|
|
749
|
+
lines.push(` case ${interfaceName}MethodIds.${method.name}:`);
|
|
750
|
+
lines.push(` return this.server.${method.name}(context as CallContext<${paramType}Reader, ${resultType}Builder>);`);
|
|
751
|
+
}
|
|
752
|
+
lines.push(" default:");
|
|
753
|
+
lines.push(" throw new Error(`Unknown method ID: ${methodId}`);");
|
|
754
|
+
lines.push(" }");
|
|
755
|
+
lines.push(" }");
|
|
756
|
+
lines.push("");
|
|
757
|
+
lines.push(" /** Check if a method ID is valid */");
|
|
758
|
+
lines.push(" isValidMethod(methodId: number): boolean {");
|
|
759
|
+
lines.push(" return [");
|
|
760
|
+
for (const method of methods) lines.push(` ${interfaceName}MethodIds.${method.name},`);
|
|
761
|
+
lines.push(" ].includes(methodId);");
|
|
762
|
+
lines.push(" }");
|
|
763
|
+
lines.push("}");
|
|
764
|
+
lines.push("");
|
|
765
|
+
lines.push(`// ${interfaceName} Client Class`);
|
|
766
|
+
lines.push(`export class ${interfaceName}Client extends BaseCapabilityClient {`);
|
|
767
|
+
lines.push(` static readonly interfaceId = ${node.id}n;`);
|
|
768
|
+
lines.push("");
|
|
769
|
+
for (const method of methods) {
|
|
770
|
+
const paramType = getTypeNameById(method.paramStructType, allNodes, "unknown");
|
|
771
|
+
const resultType = getTypeNameById(method.resultStructType, allNodes, "unknown");
|
|
772
|
+
lines.push(" /**");
|
|
773
|
+
lines.push(` * ${method.name}`);
|
|
774
|
+
lines.push(` * @param params - ${paramType}`);
|
|
775
|
+
lines.push(` * @returns PipelineClient<${resultType}Reader>`);
|
|
776
|
+
lines.push(" */");
|
|
777
|
+
lines.push(` ${method.name}(params: ${paramType}Builder): PipelineClient<${resultType}Reader> {`);
|
|
778
|
+
lines.push(" return this._call(");
|
|
779
|
+
lines.push(` ${interfaceName}Client.interfaceId,`);
|
|
780
|
+
lines.push(` ${interfaceName}MethodIds.${method.name},`);
|
|
781
|
+
lines.push(" params");
|
|
782
|
+
lines.push(` ) as PipelineClient<${resultType}Reader>;`);
|
|
783
|
+
lines.push(" }");
|
|
784
|
+
lines.push("");
|
|
785
|
+
}
|
|
786
|
+
lines.push("}");
|
|
787
|
+
return lines.join("\n");
|
|
788
|
+
}
|
|
789
|
+
/**
|
|
790
|
+
* 根据类型 ID 获取类型名称
|
|
791
|
+
*/
|
|
792
|
+
function getTypeNameById(id, allNodes, fallback) {
|
|
793
|
+
const node = allNodes.find((n) => n.id === id);
|
|
794
|
+
if (!node) return fallback;
|
|
795
|
+
return getShortName(node.displayName);
|
|
796
|
+
}
|
|
797
|
+
|
|
798
|
+
//#endregion
|
|
799
|
+
//#region src/schema/schema-reader.ts
|
|
800
|
+
/**
|
|
801
|
+
* Cap'n Proto 编译后 Schema 的 TypeScript Reader
|
|
802
|
+
* 基于官方 schema.capnp 手动编写
|
|
803
|
+
*
|
|
804
|
+
* 这是自举的基础:我们需要先能读取 schema 才能生成 schema 的代码
|
|
805
|
+
*
|
|
806
|
+
* 结构信息来自: capnp compile -ocapnp schema.capnp
|
|
807
|
+
*/
|
|
808
|
+
var NodeReader = class {
|
|
809
|
+
constructor(reader) {
|
|
810
|
+
this.reader = reader;
|
|
811
|
+
}
|
|
812
|
+
get id() {
|
|
813
|
+
return this.reader.getUint64(0);
|
|
814
|
+
}
|
|
815
|
+
get displayName() {
|
|
816
|
+
return this.reader.getText(0);
|
|
817
|
+
}
|
|
818
|
+
get displayNamePrefixLength() {
|
|
819
|
+
return this.reader.getUint32(8);
|
|
820
|
+
}
|
|
821
|
+
get scopeId() {
|
|
822
|
+
return this.reader.getUint64(16);
|
|
823
|
+
}
|
|
824
|
+
/** union tag 在 bits [96, 112) = bytes [12, 14) */
|
|
825
|
+
get unionTag() {
|
|
826
|
+
return this.reader.getUint16(12);
|
|
827
|
+
}
|
|
828
|
+
get isFile() {
|
|
829
|
+
return this.unionTag === 0;
|
|
830
|
+
}
|
|
831
|
+
get isStruct() {
|
|
832
|
+
return this.unionTag === 1;
|
|
833
|
+
}
|
|
834
|
+
get isEnum() {
|
|
835
|
+
return this.unionTag === 2;
|
|
836
|
+
}
|
|
837
|
+
get isInterface() {
|
|
838
|
+
return this.unionTag === 3;
|
|
839
|
+
}
|
|
840
|
+
get isConst() {
|
|
841
|
+
return this.unionTag === 4;
|
|
842
|
+
}
|
|
843
|
+
get isAnnotation() {
|
|
844
|
+
return this.unionTag === 5;
|
|
845
|
+
}
|
|
846
|
+
get structDataWordCount() {
|
|
847
|
+
return this.reader.getUint16(14);
|
|
848
|
+
}
|
|
849
|
+
get structPointerCount() {
|
|
850
|
+
return this.reader.getUint16(24);
|
|
851
|
+
}
|
|
852
|
+
get structPreferredListEncoding() {
|
|
853
|
+
return this.reader.getUint16(26);
|
|
854
|
+
}
|
|
855
|
+
get structIsGroup() {
|
|
856
|
+
return this.reader.getBool(224);
|
|
857
|
+
}
|
|
858
|
+
get structDiscriminantCount() {
|
|
859
|
+
return this.reader.getUint16(30);
|
|
860
|
+
}
|
|
861
|
+
get structDiscriminantOffset() {
|
|
862
|
+
return this.reader.getUint32(32);
|
|
863
|
+
}
|
|
864
|
+
get structFields() {
|
|
865
|
+
const listReader = this.reader.getList(3, ElementSize.INLINE_COMPOSITE, {
|
|
866
|
+
dataWords: 3,
|
|
867
|
+
pointerCount: 4
|
|
868
|
+
});
|
|
869
|
+
if (!listReader) return [];
|
|
870
|
+
const fields = [];
|
|
871
|
+
for (let i = 0; i < listReader.length; i++) {
|
|
872
|
+
const field = new FieldReader(listReader.getStruct(i));
|
|
873
|
+
if (!field.name && i > 0) break;
|
|
874
|
+
fields.push(field);
|
|
875
|
+
}
|
|
876
|
+
return fields;
|
|
877
|
+
}
|
|
878
|
+
get enumEnumerants() {
|
|
879
|
+
const listReader = this.reader.getList(3, ElementSize.INLINE_COMPOSITE, {
|
|
880
|
+
dataWords: 1,
|
|
881
|
+
pointerCount: 2
|
|
882
|
+
});
|
|
883
|
+
if (!listReader) return [];
|
|
884
|
+
const enumerants = [];
|
|
885
|
+
for (let i = 0; i < Math.min(listReader.length, 10); i++) try {
|
|
886
|
+
const enumerant = new EnumerantReader(listReader.getStruct(i));
|
|
887
|
+
if (!enumerant.name) break;
|
|
888
|
+
enumerants.push(enumerant);
|
|
889
|
+
} catch (_e) {
|
|
890
|
+
break;
|
|
891
|
+
}
|
|
892
|
+
return enumerants;
|
|
893
|
+
}
|
|
894
|
+
get interfaceMethods() {
|
|
895
|
+
const listReader = this.reader.getList(3, ElementSize.INLINE_COMPOSITE, {
|
|
896
|
+
dataWords: 5,
|
|
897
|
+
pointerCount: 2
|
|
898
|
+
});
|
|
899
|
+
if (!listReader) return [];
|
|
900
|
+
const methods = [];
|
|
901
|
+
for (let i = 0; i < listReader.length; i++) try {
|
|
902
|
+
const method = new MethodReader(listReader.getStruct(i));
|
|
903
|
+
if (!method.name && i > 0) break;
|
|
904
|
+
methods.push(method);
|
|
905
|
+
} catch (_e) {
|
|
906
|
+
break;
|
|
907
|
+
}
|
|
908
|
+
return methods;
|
|
909
|
+
}
|
|
910
|
+
get nestedNodes() {
|
|
911
|
+
const listReader = this.reader.getList(1, ElementSize.INLINE_COMPOSITE, {
|
|
912
|
+
dataWords: 1,
|
|
913
|
+
pointerCount: 1
|
|
914
|
+
});
|
|
915
|
+
if (!listReader) return [];
|
|
916
|
+
const nodes = [];
|
|
917
|
+
for (let i = 0; i < listReader.length; i++) {
|
|
918
|
+
const itemReader = listReader.getStruct(i);
|
|
919
|
+
nodes.push(new NestedNodeReader(itemReader));
|
|
920
|
+
}
|
|
921
|
+
return nodes;
|
|
922
|
+
}
|
|
923
|
+
};
|
|
924
|
+
var FieldReader = class {
|
|
925
|
+
constructor(reader) {
|
|
926
|
+
this.reader = reader;
|
|
927
|
+
}
|
|
928
|
+
get name() {
|
|
929
|
+
return this.reader.getText(0);
|
|
930
|
+
}
|
|
931
|
+
get codeOrder() {
|
|
932
|
+
return this.reader.getUint16(0);
|
|
933
|
+
}
|
|
934
|
+
get discriminantValue() {
|
|
935
|
+
return this.reader.getUint16(2);
|
|
936
|
+
}
|
|
937
|
+
/** union tag 在 bits [64, 80) = bytes [8, 10) */
|
|
938
|
+
get unionTag() {
|
|
939
|
+
return this.reader.getUint16(8);
|
|
940
|
+
}
|
|
941
|
+
get isSlot() {
|
|
942
|
+
return this.unionTag === 0;
|
|
943
|
+
}
|
|
944
|
+
get isGroup() {
|
|
945
|
+
return this.unionTag === 1;
|
|
946
|
+
}
|
|
947
|
+
get slotOffset() {
|
|
948
|
+
return this.reader.getUint32(4);
|
|
949
|
+
}
|
|
950
|
+
get slotType() {
|
|
951
|
+
const typeReader = this.reader.getStruct(2, 3, 1);
|
|
952
|
+
return typeReader ? new TypeReader(typeReader) : null;
|
|
953
|
+
}
|
|
954
|
+
get groupTypeId() {
|
|
955
|
+
return this.reader.getUint64(16);
|
|
956
|
+
}
|
|
957
|
+
get defaultValue() {
|
|
958
|
+
const valueReader = this.reader.getStruct(3, 2, 1);
|
|
959
|
+
return valueReader ? new ValueReader(valueReader) : null;
|
|
960
|
+
}
|
|
961
|
+
};
|
|
962
|
+
var TypeReader = class TypeReader {
|
|
963
|
+
constructor(reader) {
|
|
964
|
+
this.reader = reader;
|
|
965
|
+
}
|
|
966
|
+
get kind() {
|
|
967
|
+
return TYPE_KIND_MAP[this.reader.getUint16(0)] ?? "unknown";
|
|
968
|
+
}
|
|
969
|
+
get isPrimitive() {
|
|
970
|
+
const k = this.kind;
|
|
971
|
+
return k !== "list" && k !== "enum" && k !== "struct" && k !== "interface" && k !== "anyPointer";
|
|
972
|
+
}
|
|
973
|
+
get listElementType() {
|
|
974
|
+
if (this.kind !== "list") return null;
|
|
975
|
+
const elementReader = this.reader.getStruct(0, 3, 1);
|
|
976
|
+
return elementReader ? new TypeReader(elementReader) : null;
|
|
977
|
+
}
|
|
978
|
+
get typeId() {
|
|
979
|
+
const k = this.kind;
|
|
980
|
+
if (k === "enum" || k === "struct" || k === "interface") return this.reader.getUint64(8);
|
|
981
|
+
return null;
|
|
982
|
+
}
|
|
983
|
+
};
|
|
984
|
+
const TYPE_KIND_MAP = {
|
|
985
|
+
0: "void",
|
|
986
|
+
1: "bool",
|
|
987
|
+
2: "int8",
|
|
988
|
+
3: "int16",
|
|
989
|
+
4: "int32",
|
|
990
|
+
5: "int64",
|
|
991
|
+
6: "uint8",
|
|
992
|
+
7: "uint16",
|
|
993
|
+
8: "uint32",
|
|
994
|
+
9: "uint64",
|
|
995
|
+
10: "float32",
|
|
996
|
+
11: "float64",
|
|
997
|
+
12: "text",
|
|
998
|
+
13: "data",
|
|
999
|
+
14: "list",
|
|
1000
|
+
15: "enum",
|
|
1001
|
+
16: "struct",
|
|
1002
|
+
17: "interface",
|
|
1003
|
+
18: "anyPointer"
|
|
1004
|
+
};
|
|
1005
|
+
var ValueReader = class {
|
|
1006
|
+
constructor(reader) {
|
|
1007
|
+
this.reader = reader;
|
|
1008
|
+
}
|
|
1009
|
+
/** union tag 在 bits [0, 16) = bytes [0, 2) */
|
|
1010
|
+
get unionTag() {
|
|
1011
|
+
return this.reader.getUint16(0);
|
|
1012
|
+
}
|
|
1013
|
+
get isVoid() {
|
|
1014
|
+
return this.unionTag === 0;
|
|
1015
|
+
}
|
|
1016
|
+
get isBool() {
|
|
1017
|
+
return this.unionTag === 1;
|
|
1018
|
+
}
|
|
1019
|
+
get isInt8() {
|
|
1020
|
+
return this.unionTag === 2;
|
|
1021
|
+
}
|
|
1022
|
+
get isInt16() {
|
|
1023
|
+
return this.unionTag === 3;
|
|
1024
|
+
}
|
|
1025
|
+
get isInt32() {
|
|
1026
|
+
return this.unionTag === 4;
|
|
1027
|
+
}
|
|
1028
|
+
get isInt64() {
|
|
1029
|
+
return this.unionTag === 5;
|
|
1030
|
+
}
|
|
1031
|
+
get isUint8() {
|
|
1032
|
+
return this.unionTag === 6;
|
|
1033
|
+
}
|
|
1034
|
+
get isUint16() {
|
|
1035
|
+
return this.unionTag === 7;
|
|
1036
|
+
}
|
|
1037
|
+
get isUint32() {
|
|
1038
|
+
return this.unionTag === 8;
|
|
1039
|
+
}
|
|
1040
|
+
get isUint64() {
|
|
1041
|
+
return this.unionTag === 9;
|
|
1042
|
+
}
|
|
1043
|
+
get isFloat32() {
|
|
1044
|
+
return this.unionTag === 10;
|
|
1045
|
+
}
|
|
1046
|
+
get isFloat64() {
|
|
1047
|
+
return this.unionTag === 11;
|
|
1048
|
+
}
|
|
1049
|
+
get isText() {
|
|
1050
|
+
return this.unionTag === 12;
|
|
1051
|
+
}
|
|
1052
|
+
get isData() {
|
|
1053
|
+
return this.unionTag === 13;
|
|
1054
|
+
}
|
|
1055
|
+
get isList() {
|
|
1056
|
+
return this.unionTag === 14;
|
|
1057
|
+
}
|
|
1058
|
+
get isEnum() {
|
|
1059
|
+
return this.unionTag === 15;
|
|
1060
|
+
}
|
|
1061
|
+
get isStruct() {
|
|
1062
|
+
return this.unionTag === 16;
|
|
1063
|
+
}
|
|
1064
|
+
get isInterface() {
|
|
1065
|
+
return this.unionTag === 17;
|
|
1066
|
+
}
|
|
1067
|
+
get isAnyPointer() {
|
|
1068
|
+
return this.unionTag === 18;
|
|
1069
|
+
}
|
|
1070
|
+
get boolValue() {
|
|
1071
|
+
return this.reader.getBool(16);
|
|
1072
|
+
}
|
|
1073
|
+
get int8Value() {
|
|
1074
|
+
return this.reader.getInt8(2);
|
|
1075
|
+
}
|
|
1076
|
+
get int16Value() {
|
|
1077
|
+
return this.reader.getInt16(2);
|
|
1078
|
+
}
|
|
1079
|
+
get int32Value() {
|
|
1080
|
+
return this.reader.getInt32(4);
|
|
1081
|
+
}
|
|
1082
|
+
get int64Value() {
|
|
1083
|
+
return this.reader.getInt64(8);
|
|
1084
|
+
}
|
|
1085
|
+
get uint8Value() {
|
|
1086
|
+
return this.reader.getUint8(2);
|
|
1087
|
+
}
|
|
1088
|
+
get uint16Value() {
|
|
1089
|
+
return this.reader.getUint16(2);
|
|
1090
|
+
}
|
|
1091
|
+
get uint32Value() {
|
|
1092
|
+
return this.reader.getUint32(4);
|
|
1093
|
+
}
|
|
1094
|
+
get uint64Value() {
|
|
1095
|
+
return this.reader.getUint64(8);
|
|
1096
|
+
}
|
|
1097
|
+
get float32Value() {
|
|
1098
|
+
return this.reader.getFloat32(4);
|
|
1099
|
+
}
|
|
1100
|
+
get float64Value() {
|
|
1101
|
+
return this.reader.getFloat64(8);
|
|
1102
|
+
}
|
|
1103
|
+
get enumValue() {
|
|
1104
|
+
return this.reader.getUint16(2);
|
|
1105
|
+
}
|
|
1106
|
+
getAsNumber() {
|
|
1107
|
+
if (this.isInt8) return this.int8Value;
|
|
1108
|
+
if (this.isInt16) return this.int16Value;
|
|
1109
|
+
if (this.isInt32) return this.int32Value;
|
|
1110
|
+
if (this.isUint8) return this.uint8Value;
|
|
1111
|
+
if (this.isUint16) return this.uint16Value;
|
|
1112
|
+
if (this.isUint32) return this.uint32Value;
|
|
1113
|
+
if (this.isFloat32) return this.float32Value;
|
|
1114
|
+
if (this.isFloat64) return this.float64Value;
|
|
1115
|
+
if (this.isEnum) return this.enumValue;
|
|
1116
|
+
}
|
|
1117
|
+
getAsBigint() {
|
|
1118
|
+
if (this.isInt64) return this.int64Value;
|
|
1119
|
+
if (this.isUint64) return this.uint64Value;
|
|
1120
|
+
}
|
|
1121
|
+
getAsBoolean() {
|
|
1122
|
+
if (this.isBool) return this.boolValue;
|
|
1123
|
+
}
|
|
1124
|
+
};
|
|
1125
|
+
var MethodReader = class {
|
|
1126
|
+
constructor(reader) {
|
|
1127
|
+
this.reader = reader;
|
|
1128
|
+
}
|
|
1129
|
+
get name() {
|
|
1130
|
+
return this.reader.getText(0);
|
|
1131
|
+
}
|
|
1132
|
+
get codeOrder() {
|
|
1133
|
+
return this.reader.getUint16(0);
|
|
1134
|
+
}
|
|
1135
|
+
get paramStructType() {
|
|
1136
|
+
return this.reader.getUint64(8);
|
|
1137
|
+
}
|
|
1138
|
+
get resultStructType() {
|
|
1139
|
+
return this.reader.getUint64(16);
|
|
1140
|
+
}
|
|
1141
|
+
};
|
|
1142
|
+
var EnumerantReader = class {
|
|
1143
|
+
constructor(reader) {
|
|
1144
|
+
this.reader = reader;
|
|
1145
|
+
}
|
|
1146
|
+
get name() {
|
|
1147
|
+
return this.reader.getText(0);
|
|
1148
|
+
}
|
|
1149
|
+
get codeOrder() {
|
|
1150
|
+
return this.reader.getUint16(0);
|
|
1151
|
+
}
|
|
1152
|
+
};
|
|
1153
|
+
var NestedNodeReader = class {
|
|
1154
|
+
constructor(reader) {
|
|
1155
|
+
this.reader = reader;
|
|
1156
|
+
}
|
|
1157
|
+
get name() {
|
|
1158
|
+
return this.reader.getText(0);
|
|
1159
|
+
}
|
|
1160
|
+
get id() {
|
|
1161
|
+
return this.reader.getUint64(0);
|
|
1162
|
+
}
|
|
1163
|
+
};
|
|
1164
|
+
var CodeGeneratorRequestReader = class CodeGeneratorRequestReader {
|
|
1165
|
+
constructor(message) {
|
|
1166
|
+
this.message = message;
|
|
1167
|
+
}
|
|
1168
|
+
static fromBuffer(buffer) {
|
|
1169
|
+
return new CodeGeneratorRequestReader(new MessageReader(buffer));
|
|
1170
|
+
}
|
|
1171
|
+
get nodes() {
|
|
1172
|
+
const listReader = this.message.getRoot(0, 4).getList(0, ElementSize.INLINE_COMPOSITE, {
|
|
1173
|
+
dataWords: 6,
|
|
1174
|
+
pointerCount: 6
|
|
1175
|
+
});
|
|
1176
|
+
if (!listReader) return [];
|
|
1177
|
+
const nodes = [];
|
|
1178
|
+
for (let i = 0; i < listReader.length; i++) {
|
|
1179
|
+
const nodeReader = listReader.getStruct(i);
|
|
1180
|
+
nodes.push(new NodeReader(nodeReader));
|
|
1181
|
+
}
|
|
1182
|
+
return nodes;
|
|
1183
|
+
}
|
|
1184
|
+
get requestedFiles() {
|
|
1185
|
+
const listReader = this.message.getRoot(0, 4).getList(1, ElementSize.INLINE_COMPOSITE, {
|
|
1186
|
+
dataWords: 1,
|
|
1187
|
+
pointerCount: 2
|
|
1188
|
+
});
|
|
1189
|
+
if (!listReader) return [];
|
|
1190
|
+
const files = [];
|
|
1191
|
+
for (let i = 0; i < listReader.length; i++) try {
|
|
1192
|
+
const file = new RequestedFileReader(listReader.getStruct(i));
|
|
1193
|
+
if (!file.filename && files.length > 0) break;
|
|
1194
|
+
files.push(file);
|
|
1195
|
+
} catch (_e) {
|
|
1196
|
+
break;
|
|
1197
|
+
}
|
|
1198
|
+
return files;
|
|
1199
|
+
}
|
|
1200
|
+
};
|
|
1201
|
+
var RequestedFileReader = class {
|
|
1202
|
+
constructor(reader) {
|
|
1203
|
+
this.reader = reader;
|
|
1204
|
+
}
|
|
1205
|
+
get id() {
|
|
1206
|
+
return this.reader.getUint64(0);
|
|
1207
|
+
}
|
|
1208
|
+
get filename() {
|
|
1209
|
+
return this.reader.getText(0);
|
|
1210
|
+
}
|
|
1211
|
+
};
|
|
1212
|
+
|
|
1213
|
+
//#endregion
|
|
1214
|
+
//#region src/cli-gen.ts
|
|
1215
|
+
/**
|
|
1216
|
+
* Cap'n Proto Code Generator CLI
|
|
1217
|
+
*
|
|
1218
|
+
* Usage: capnp gen <schema.capnp> [options]
|
|
1219
|
+
*/
|
|
1220
|
+
const VERSION = "3.0.0";
|
|
1221
|
+
function printUsage() {
|
|
1222
|
+
console.log(`
|
|
1223
|
+
Cap'n Proto TypeScript Code Generator v${VERSION}
|
|
1224
|
+
|
|
1225
|
+
Usage: capnp gen <schema.capnp> [options]
|
|
1226
|
+
|
|
1227
|
+
Options:
|
|
1228
|
+
-o, --output Output file (default: stdout)
|
|
1229
|
+
-d, --outDir Output directory
|
|
1230
|
+
-r, --runtimePath Runtime library import path (default: @naeemo/capnp)
|
|
1231
|
+
-h, --help Show this help
|
|
1232
|
+
-v, --version Show version
|
|
1233
|
+
|
|
1234
|
+
Examples:
|
|
1235
|
+
capnp gen schema.capnp -o schema.ts
|
|
1236
|
+
capnp gen schema.capnp -d ./generated
|
|
1237
|
+
`);
|
|
1238
|
+
}
|
|
1239
|
+
function checkCapnpTool() {
|
|
1240
|
+
try {
|
|
1241
|
+
execSync("capnp --version", { stdio: "ignore" });
|
|
1242
|
+
return true;
|
|
1243
|
+
} catch {
|
|
1244
|
+
return false;
|
|
1245
|
+
}
|
|
1246
|
+
}
|
|
1247
|
+
async function run(args) {
|
|
1248
|
+
const { values, positionals } = parseArgs({
|
|
1249
|
+
args,
|
|
1250
|
+
options: {
|
|
1251
|
+
output: {
|
|
1252
|
+
type: "string",
|
|
1253
|
+
short: "o"
|
|
1254
|
+
},
|
|
1255
|
+
outDir: {
|
|
1256
|
+
type: "string",
|
|
1257
|
+
short: "d"
|
|
1258
|
+
},
|
|
1259
|
+
runtimePath: {
|
|
1260
|
+
type: "string",
|
|
1261
|
+
short: "r"
|
|
1262
|
+
},
|
|
1263
|
+
help: {
|
|
1264
|
+
type: "boolean",
|
|
1265
|
+
short: "h"
|
|
1266
|
+
},
|
|
1267
|
+
version: {
|
|
1268
|
+
type: "boolean",
|
|
1269
|
+
short: "v"
|
|
1270
|
+
}
|
|
1271
|
+
},
|
|
1272
|
+
allowPositionals: true
|
|
1273
|
+
});
|
|
1274
|
+
if (values.version) {
|
|
1275
|
+
console.log(VERSION);
|
|
1276
|
+
process.exit(0);
|
|
1277
|
+
}
|
|
1278
|
+
if (values.help || positionals.length === 0) {
|
|
1279
|
+
printUsage();
|
|
1280
|
+
process.exit(0);
|
|
1281
|
+
}
|
|
1282
|
+
const inputFile = positionals[0];
|
|
1283
|
+
if (!existsSync(inputFile)) {
|
|
1284
|
+
console.error(`Error: File not found: ${inputFile}`);
|
|
1285
|
+
process.exit(1);
|
|
1286
|
+
}
|
|
1287
|
+
if (!checkCapnpTool()) {
|
|
1288
|
+
console.error("Error: capnp tool not found. Please install Cap'n Proto.");
|
|
1289
|
+
process.exit(1);
|
|
1290
|
+
}
|
|
1291
|
+
const tmpDir = mkdtempSync(join(tmpdir(), "capnp-gen-"));
|
|
1292
|
+
const binFile = join(tmpDir, "schema.bin");
|
|
1293
|
+
try {
|
|
1294
|
+
const { exec } = await import("node:child_process");
|
|
1295
|
+
const { promisify } = await import("node:util");
|
|
1296
|
+
await promisify(exec)(`capnp compile -o- "${inputFile}" > "${binFile}"`);
|
|
1297
|
+
const buffer = readFileSync(binFile);
|
|
1298
|
+
const files = generateFromRequest(CodeGeneratorRequestReader.fromBuffer(buffer.buffer), { runtimeImportPath: typeof values.runtimePath === "string" ? values.runtimePath : "@naeemo/capnp" });
|
|
1299
|
+
if (values.outDir) for (const [filename, content] of files) {
|
|
1300
|
+
const outPath = join(values.outDir, filename);
|
|
1301
|
+
writeFileSync(outPath, content);
|
|
1302
|
+
console.log(`Generated: ${outPath}`);
|
|
1303
|
+
}
|
|
1304
|
+
else if (values.output) {
|
|
1305
|
+
const content = Array.from(files.values()).join("\n");
|
|
1306
|
+
writeFileSync(values.output, content);
|
|
1307
|
+
console.log(`Generated: ${values.output}`);
|
|
1308
|
+
} else {
|
|
1309
|
+
const content = Array.from(files.values()).join("\n");
|
|
1310
|
+
console.log(content);
|
|
1311
|
+
}
|
|
1312
|
+
} catch (err) {
|
|
1313
|
+
console.error("Error:", err.message);
|
|
1314
|
+
process.exit(1);
|
|
1315
|
+
} finally {
|
|
1316
|
+
rmSync(tmpDir, {
|
|
1317
|
+
recursive: true,
|
|
1318
|
+
force: true
|
|
1319
|
+
});
|
|
1320
|
+
}
|
|
1321
|
+
}
|
|
1322
|
+
|
|
1323
|
+
//#endregion
|
|
1324
|
+
export { run };
|
|
1325
|
+
//# sourceMappingURL=cli-gen-aCkffW1Z.js.map
|