@fluidframework/tree-agent 2.74.0-365691 → 2.74.0-368706
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/methodBinding.d.ts +6 -1
- package/dist/methodBinding.d.ts.map +1 -1
- package/dist/methodBinding.js +14 -3
- package/dist/methodBinding.js.map +1 -1
- package/dist/prompt.d.ts.map +1 -1
- package/dist/prompt.js +2 -12
- package/dist/prompt.js.map +1 -1
- package/dist/propertyBinding.js +2 -2
- package/dist/propertyBinding.js.map +1 -1
- package/dist/renderSchemaTypeScript.d.ts +18 -0
- package/dist/renderSchemaTypeScript.d.ts.map +1 -0
- package/dist/renderSchemaTypeScript.js +399 -0
- package/dist/renderSchemaTypeScript.js.map +1 -0
- package/dist/renderZodTypeScript.d.ts +21 -0
- package/dist/renderZodTypeScript.d.ts.map +1 -0
- package/dist/renderZodTypeScript.js +272 -0
- package/dist/renderZodTypeScript.js.map +1 -0
- package/dist/typeGeneration.d.ts +3 -5
- package/dist/typeGeneration.d.ts.map +1 -1
- package/dist/typeGeneration.js +21 -254
- package/dist/typeGeneration.js.map +1 -1
- package/dist/utils.d.ts +10 -27
- package/dist/utils.d.ts.map +1 -1
- package/dist/utils.js +18 -362
- package/dist/utils.js.map +1 -1
- package/lib/methodBinding.d.ts +6 -1
- package/lib/methodBinding.d.ts.map +1 -1
- package/lib/methodBinding.js +11 -1
- package/lib/methodBinding.js.map +1 -1
- package/lib/prompt.d.ts.map +1 -1
- package/lib/prompt.js +3 -13
- package/lib/prompt.js.map +1 -1
- package/lib/propertyBinding.js +1 -1
- package/lib/propertyBinding.js.map +1 -1
- package/lib/renderSchemaTypeScript.d.ts +18 -0
- package/lib/renderSchemaTypeScript.d.ts.map +1 -0
- package/lib/renderSchemaTypeScript.js +395 -0
- package/lib/renderSchemaTypeScript.js.map +1 -0
- package/lib/renderZodTypeScript.d.ts +21 -0
- package/lib/renderZodTypeScript.d.ts.map +1 -0
- package/lib/renderZodTypeScript.js +267 -0
- package/lib/renderZodTypeScript.js.map +1 -0
- package/lib/typeGeneration.d.ts +3 -5
- package/lib/typeGeneration.d.ts.map +1 -1
- package/lib/typeGeneration.js +24 -257
- package/lib/typeGeneration.js.map +1 -1
- package/lib/utils.d.ts +10 -27
- package/lib/utils.d.ts.map +1 -1
- package/lib/utils.js +18 -360
- package/lib/utils.js.map +1 -1
- package/package.json +10 -10
- package/src/methodBinding.ts +15 -3
- package/src/prompt.ts +6 -22
- package/src/propertyBinding.ts +1 -1
- package/src/renderSchemaTypeScript.ts +523 -0
- package/src/renderZodTypeScript.ts +314 -0
- package/src/typeGeneration.ts +32 -414
- package/src/utils.ts +18 -412
|
@@ -0,0 +1,523 @@
|
|
|
1
|
+
/*!
|
|
2
|
+
* Copyright (c) Microsoft Corporation and contributors. All rights reserved.
|
|
3
|
+
* Licensed under the MIT License.
|
|
4
|
+
*/
|
|
5
|
+
|
|
6
|
+
import { UsageError } from "@fluidframework/telemetry-utils/internal";
|
|
7
|
+
import { FieldKind, NodeKind, ValueSchema } from "@fluidframework/tree/internal";
|
|
8
|
+
import type {
|
|
9
|
+
SimpleArrayNodeSchema,
|
|
10
|
+
SimpleFieldSchema,
|
|
11
|
+
SimpleMapNodeSchema,
|
|
12
|
+
SimpleNodeSchema,
|
|
13
|
+
SimpleObjectNodeSchema,
|
|
14
|
+
SimpleRecordNodeSchema,
|
|
15
|
+
} from "@fluidframework/tree/internal";
|
|
16
|
+
import { z } from "zod";
|
|
17
|
+
|
|
18
|
+
import type { BindableSchema, FunctionWrapper } from "./methodBinding.js";
|
|
19
|
+
import { getExposedMethods } from "./methodBinding.js";
|
|
20
|
+
import { getExposedProperties, type PropertyDef } from "./propertyBinding.js";
|
|
21
|
+
import { getFriendlyName, isNamedSchema, llmDefault, unqualifySchema } from "./utils.js";
|
|
22
|
+
import { instanceOfs, renderZodTypeScript } from "./renderZodTypeScript.js";
|
|
23
|
+
|
|
24
|
+
interface BoundMembers {
|
|
25
|
+
methods: Record<string, FunctionWrapper>;
|
|
26
|
+
properties: Record<string, PropertyDef>;
|
|
27
|
+
}
|
|
28
|
+
|
|
29
|
+
interface RenderResult {
|
|
30
|
+
declaration: string;
|
|
31
|
+
description?: string;
|
|
32
|
+
}
|
|
33
|
+
|
|
34
|
+
interface BindingIntersectionResult {
|
|
35
|
+
hasMethods: boolean;
|
|
36
|
+
hasProperties: boolean;
|
|
37
|
+
suffix: string;
|
|
38
|
+
}
|
|
39
|
+
|
|
40
|
+
interface TypeExpression {
|
|
41
|
+
precedence: TypePrecedence;
|
|
42
|
+
text: string;
|
|
43
|
+
}
|
|
44
|
+
|
|
45
|
+
const enum TypePrecedence {
|
|
46
|
+
Union = 0,
|
|
47
|
+
Intersection = 1,
|
|
48
|
+
Object = 2,
|
|
49
|
+
}
|
|
50
|
+
|
|
51
|
+
/**
|
|
52
|
+
* Output of rendering schema metadata into TypeScript declaration text.
|
|
53
|
+
*/
|
|
54
|
+
export interface SchemaTypeScriptRenderResult {
|
|
55
|
+
schemaText: string;
|
|
56
|
+
hasHelperMethods: boolean;
|
|
57
|
+
}
|
|
58
|
+
|
|
59
|
+
/**
|
|
60
|
+
* Converts schema metadata into TypeScript declarations suitable for prompt inclusion.
|
|
61
|
+
*/
|
|
62
|
+
export function renderSchemaTypeScript(
|
|
63
|
+
definitions: ReadonlyMap<string, SimpleNodeSchema>,
|
|
64
|
+
bindableSchemas: Map<string, BindableSchema>,
|
|
65
|
+
): SchemaTypeScriptRenderResult {
|
|
66
|
+
const friendlyNames = new Map<string, string>();
|
|
67
|
+
let hasHelperMethods = false;
|
|
68
|
+
|
|
69
|
+
for (const identifier of definitions.keys()) {
|
|
70
|
+
if (isNamedSchema(identifier)) {
|
|
71
|
+
friendlyNames.set(identifier, unqualifySchema(identifier));
|
|
72
|
+
}
|
|
73
|
+
}
|
|
74
|
+
|
|
75
|
+
const declarations: string[] = [];
|
|
76
|
+
for (const [identifier, schema] of definitions) {
|
|
77
|
+
if (!isNamedSchema(identifier)) {
|
|
78
|
+
continue;
|
|
79
|
+
}
|
|
80
|
+
const friendlyName = friendlyNames.get(identifier) ?? unqualifySchema(identifier);
|
|
81
|
+
const rendered = renderNamedSchema(identifier, friendlyName, schema);
|
|
82
|
+
if (rendered === undefined) {
|
|
83
|
+
continue;
|
|
84
|
+
}
|
|
85
|
+
|
|
86
|
+
const lines: string[] = [];
|
|
87
|
+
if (rendered.description !== undefined && rendered.description !== "") {
|
|
88
|
+
for (const comment of rendered.description.split("\n")) {
|
|
89
|
+
lines.push(`// ${comment}`);
|
|
90
|
+
}
|
|
91
|
+
}
|
|
92
|
+
lines.push(rendered.declaration);
|
|
93
|
+
declarations.push(lines.join("\n"));
|
|
94
|
+
}
|
|
95
|
+
|
|
96
|
+
const schemaText = declarations.join("\n\n");
|
|
97
|
+
return {
|
|
98
|
+
schemaText: schemaText === "" ? "" : `${schemaText}\n`,
|
|
99
|
+
hasHelperMethods,
|
|
100
|
+
};
|
|
101
|
+
|
|
102
|
+
function renderNamedSchema(
|
|
103
|
+
identifier: string,
|
|
104
|
+
friendlyName: string,
|
|
105
|
+
schema: SimpleNodeSchema,
|
|
106
|
+
): RenderResult | undefined {
|
|
107
|
+
switch (schema.kind) {
|
|
108
|
+
case NodeKind.Object: {
|
|
109
|
+
return renderObjectDeclaration(identifier, friendlyName, schema);
|
|
110
|
+
}
|
|
111
|
+
case NodeKind.Array: {
|
|
112
|
+
return renderArrayDeclaration(identifier, friendlyName, schema);
|
|
113
|
+
}
|
|
114
|
+
case NodeKind.Map: {
|
|
115
|
+
return renderMapDeclaration(identifier, friendlyName, schema);
|
|
116
|
+
}
|
|
117
|
+
case NodeKind.Record: {
|
|
118
|
+
return renderRecordDeclaration(identifier, friendlyName, schema);
|
|
119
|
+
}
|
|
120
|
+
case NodeKind.Leaf: {
|
|
121
|
+
return {
|
|
122
|
+
declaration: `type ${friendlyName} = ${renderLeaf(schema.leafKind)};`,
|
|
123
|
+
description: schema.metadata?.description,
|
|
124
|
+
};
|
|
125
|
+
}
|
|
126
|
+
default: {
|
|
127
|
+
return undefined;
|
|
128
|
+
}
|
|
129
|
+
}
|
|
130
|
+
}
|
|
131
|
+
|
|
132
|
+
function renderObjectDeclaration(
|
|
133
|
+
definition: string,
|
|
134
|
+
name: string,
|
|
135
|
+
schema: SimpleObjectNodeSchema,
|
|
136
|
+
): RenderResult {
|
|
137
|
+
const fieldLines: string[] = [];
|
|
138
|
+
const fieldNames = new Set<string>();
|
|
139
|
+
|
|
140
|
+
for (const [fieldName, fieldSchema] of schema.fields) {
|
|
141
|
+
fieldNames.add(fieldName);
|
|
142
|
+
fieldLines.push(...renderFieldLine(fieldName, fieldSchema));
|
|
143
|
+
}
|
|
144
|
+
|
|
145
|
+
const { methods, properties } = getBoundMembers(definition);
|
|
146
|
+
ensureNoMemberConflicts(definition, fieldNames, methods, properties);
|
|
147
|
+
fieldLines.push(...renderPropertyLines(properties));
|
|
148
|
+
fieldLines.push(...renderMethodLines(methods));
|
|
149
|
+
|
|
150
|
+
const members = fieldLines.map((line) => ` ${line}`).join("\n");
|
|
151
|
+
const body = members === "" ? "" : `\n${members}`;
|
|
152
|
+
return {
|
|
153
|
+
declaration: `interface ${name} {${body}\n}`,
|
|
154
|
+
description: schema.metadata?.description,
|
|
155
|
+
};
|
|
156
|
+
}
|
|
157
|
+
|
|
158
|
+
function renderArrayDeclaration(
|
|
159
|
+
definition: string,
|
|
160
|
+
name: string,
|
|
161
|
+
schema: SimpleArrayNodeSchema,
|
|
162
|
+
): RenderResult {
|
|
163
|
+
const elementTypes = renderAllowedTypes(schema.simpleAllowedTypes.keys());
|
|
164
|
+
const base = `${formatExpression(elementTypes)}[]`;
|
|
165
|
+
const binding = renderBindingIntersection(definition);
|
|
166
|
+
return {
|
|
167
|
+
declaration: `type ${name} = ${base}${binding.suffix};`,
|
|
168
|
+
description: describeBinding(schema.metadata?.description, "array", binding),
|
|
169
|
+
};
|
|
170
|
+
}
|
|
171
|
+
|
|
172
|
+
function renderMapDeclaration(
|
|
173
|
+
definition: string,
|
|
174
|
+
name: string,
|
|
175
|
+
schema: SimpleMapNodeSchema,
|
|
176
|
+
): RenderResult {
|
|
177
|
+
const valueType = renderAllowedTypes(schema.simpleAllowedTypes.keys());
|
|
178
|
+
const base = `Map<string, ${valueType.text}>`;
|
|
179
|
+
const binding = renderBindingIntersection(definition);
|
|
180
|
+
return {
|
|
181
|
+
declaration: `type ${name} = ${base}${binding.suffix};`,
|
|
182
|
+
description: describeBinding(schema.metadata?.description, "map", binding),
|
|
183
|
+
};
|
|
184
|
+
}
|
|
185
|
+
|
|
186
|
+
function renderRecordDeclaration(
|
|
187
|
+
definition: string,
|
|
188
|
+
name: string,
|
|
189
|
+
schema: SimpleRecordNodeSchema,
|
|
190
|
+
): RenderResult {
|
|
191
|
+
const valueType = renderAllowedTypes(schema.simpleAllowedTypes.keys());
|
|
192
|
+
const base = `Record<string, ${valueType.text}>`;
|
|
193
|
+
const binding = renderBindingIntersection(definition);
|
|
194
|
+
return {
|
|
195
|
+
declaration: `type ${name} = ${base}${binding.suffix};`,
|
|
196
|
+
description: describeBinding(schema.metadata?.description, "record", binding),
|
|
197
|
+
};
|
|
198
|
+
}
|
|
199
|
+
|
|
200
|
+
function renderFieldLine(name: string, field: SimpleFieldSchema): string[] {
|
|
201
|
+
const { comment, optional, type } = describeField(field);
|
|
202
|
+
const lines: string[] = [];
|
|
203
|
+
if (comment !== undefined && comment !== "") {
|
|
204
|
+
for (const note of comment.split("\n")) {
|
|
205
|
+
lines.push(`// ${note}`);
|
|
206
|
+
}
|
|
207
|
+
}
|
|
208
|
+
lines.push(`${name}${optional ? "?" : ""}: ${type};`);
|
|
209
|
+
return lines;
|
|
210
|
+
}
|
|
211
|
+
|
|
212
|
+
function describeField(field: SimpleFieldSchema): {
|
|
213
|
+
comment?: string;
|
|
214
|
+
optional: boolean;
|
|
215
|
+
type: string;
|
|
216
|
+
} {
|
|
217
|
+
const allowedTypes = renderAllowedTypes(field.simpleAllowedTypes.keys());
|
|
218
|
+
const type = formatExpression(allowedTypes);
|
|
219
|
+
const optional = field.kind !== FieldKind.Required;
|
|
220
|
+
const customMetadata = field.metadata.custom as
|
|
221
|
+
| Record<string | symbol, unknown>
|
|
222
|
+
| undefined;
|
|
223
|
+
const getDefault = customMetadata?.[llmDefault];
|
|
224
|
+
|
|
225
|
+
if (getDefault !== undefined) {
|
|
226
|
+
if (typeof getDefault !== "function") {
|
|
227
|
+
throw new UsageError(
|
|
228
|
+
`Expected value of ${llmDefault.description} property to be a function, but got ${typeof getDefault}`,
|
|
229
|
+
);
|
|
230
|
+
}
|
|
231
|
+
if (field.kind !== FieldKind.Optional) {
|
|
232
|
+
throw new UsageError(
|
|
233
|
+
`The ${llmDefault.description} property is only permitted on optional fields.`,
|
|
234
|
+
);
|
|
235
|
+
}
|
|
236
|
+
return {
|
|
237
|
+
optional,
|
|
238
|
+
type,
|
|
239
|
+
comment:
|
|
240
|
+
"Do not populate this field. It will be automatically supplied by the system after insertion.",
|
|
241
|
+
};
|
|
242
|
+
}
|
|
243
|
+
|
|
244
|
+
if (field.kind === FieldKind.Identifier) {
|
|
245
|
+
return {
|
|
246
|
+
optional: true,
|
|
247
|
+
type,
|
|
248
|
+
comment:
|
|
249
|
+
"This is an ID automatically generated by the system. Do not supply it when constructing a new object.",
|
|
250
|
+
};
|
|
251
|
+
}
|
|
252
|
+
|
|
253
|
+
const description = field.metadata?.description;
|
|
254
|
+
return {
|
|
255
|
+
optional,
|
|
256
|
+
type,
|
|
257
|
+
comment: description === undefined || description === "" ? undefined : description,
|
|
258
|
+
};
|
|
259
|
+
}
|
|
260
|
+
|
|
261
|
+
function renderBindingIntersection(definition: string): BindingIntersectionResult {
|
|
262
|
+
const { methods, properties } = getBoundMembers(definition);
|
|
263
|
+
const propertyLines = renderPropertyLines(properties);
|
|
264
|
+
const methodLines = renderMethodLines(methods);
|
|
265
|
+
|
|
266
|
+
if (propertyLines.length === 0 && methodLines.length === 0) {
|
|
267
|
+
return { hasMethods: false, hasProperties: false, suffix: "" };
|
|
268
|
+
}
|
|
269
|
+
|
|
270
|
+
const lines = [...propertyLines, ...methodLines].map((line) => ` ${line}`);
|
|
271
|
+
const suffix = ` & {\n${lines.join("\n")}\n}`;
|
|
272
|
+
return {
|
|
273
|
+
hasMethods: methodLines.length > 0,
|
|
274
|
+
hasProperties: propertyLines.length > 0,
|
|
275
|
+
suffix,
|
|
276
|
+
};
|
|
277
|
+
}
|
|
278
|
+
|
|
279
|
+
function renderMethodLines(methods: Record<string, FunctionWrapper>): string[] {
|
|
280
|
+
const lines: string[] = [];
|
|
281
|
+
for (const [name, method] of Object.entries(methods)) {
|
|
282
|
+
if (method.description !== undefined && method.description !== "") {
|
|
283
|
+
for (const note of method.description.split("\n")) {
|
|
284
|
+
lines.push(`// ${note}`);
|
|
285
|
+
}
|
|
286
|
+
}
|
|
287
|
+
lines.push(formatMethod(name, method));
|
|
288
|
+
}
|
|
289
|
+
if (lines.length > 0) {
|
|
290
|
+
hasHelperMethods = true;
|
|
291
|
+
}
|
|
292
|
+
return lines;
|
|
293
|
+
}
|
|
294
|
+
|
|
295
|
+
function getBoundMembers(definition: string): BoundMembers {
|
|
296
|
+
const schemaClass = bindableSchemas.get(definition);
|
|
297
|
+
if (schemaClass === undefined) {
|
|
298
|
+
return { methods: {}, properties: {} };
|
|
299
|
+
}
|
|
300
|
+
return {
|
|
301
|
+
methods: getExposedMethods(schemaClass).methods,
|
|
302
|
+
properties: getExposedProperties(schemaClass).properties,
|
|
303
|
+
};
|
|
304
|
+
}
|
|
305
|
+
|
|
306
|
+
function renderAllowedTypes(allowedTypes: Iterable<string>): TypeExpression {
|
|
307
|
+
const expressions: TypeExpression[] = [];
|
|
308
|
+
for (const identifier of allowedTypes) {
|
|
309
|
+
expressions.push(renderTypeReference(identifier));
|
|
310
|
+
}
|
|
311
|
+
if (expressions.length === 0) {
|
|
312
|
+
return { precedence: TypePrecedence.Object, text: "never" };
|
|
313
|
+
}
|
|
314
|
+
if (expressions.length === 1) {
|
|
315
|
+
return expressions[0] ?? { precedence: TypePrecedence.Object, text: "never" };
|
|
316
|
+
}
|
|
317
|
+
return {
|
|
318
|
+
precedence: TypePrecedence.Union,
|
|
319
|
+
text: expressions
|
|
320
|
+
.map((expr) => formatExpression(expr, TypePrecedence.Union))
|
|
321
|
+
.join(" | "),
|
|
322
|
+
};
|
|
323
|
+
}
|
|
324
|
+
|
|
325
|
+
function renderTypeReference(identifier: string): TypeExpression {
|
|
326
|
+
const schema = definitions.get(identifier);
|
|
327
|
+
if (schema === undefined) {
|
|
328
|
+
return {
|
|
329
|
+
precedence: TypePrecedence.Object,
|
|
330
|
+
text: friendlyNames.get(identifier) ?? unqualifySchema(identifier),
|
|
331
|
+
};
|
|
332
|
+
}
|
|
333
|
+
if (isNamedSchema(identifier)) {
|
|
334
|
+
return {
|
|
335
|
+
precedence: TypePrecedence.Object,
|
|
336
|
+
text: friendlyNames.get(identifier) ?? unqualifySchema(identifier),
|
|
337
|
+
};
|
|
338
|
+
}
|
|
339
|
+
return renderInlineSchema(schema);
|
|
340
|
+
}
|
|
341
|
+
|
|
342
|
+
function renderInlineSchema(schema: SimpleNodeSchema): TypeExpression {
|
|
343
|
+
switch (schema.kind) {
|
|
344
|
+
case NodeKind.Object: {
|
|
345
|
+
return renderInlineObject(schema);
|
|
346
|
+
}
|
|
347
|
+
case NodeKind.Array: {
|
|
348
|
+
return renderInlineArray(schema);
|
|
349
|
+
}
|
|
350
|
+
case NodeKind.Map: {
|
|
351
|
+
return renderInlineMap(schema);
|
|
352
|
+
}
|
|
353
|
+
case NodeKind.Record: {
|
|
354
|
+
return renderInlineRecord(schema);
|
|
355
|
+
}
|
|
356
|
+
case NodeKind.Leaf: {
|
|
357
|
+
return { precedence: TypePrecedence.Object, text: renderLeaf(schema.leafKind) };
|
|
358
|
+
}
|
|
359
|
+
default: {
|
|
360
|
+
return { precedence: TypePrecedence.Object, text: "unknown" };
|
|
361
|
+
}
|
|
362
|
+
}
|
|
363
|
+
}
|
|
364
|
+
|
|
365
|
+
function renderInlineObject(schema: SimpleObjectNodeSchema): TypeExpression {
|
|
366
|
+
const fieldLines: string[] = [];
|
|
367
|
+
for (const [fieldName, fieldSchema] of schema.fields) {
|
|
368
|
+
fieldLines.push(...renderFieldLine(fieldName, fieldSchema));
|
|
369
|
+
}
|
|
370
|
+
const members = fieldLines.map((line) => ` ${line}`).join("\n");
|
|
371
|
+
const text =
|
|
372
|
+
members === ""
|
|
373
|
+
? "{\n}"
|
|
374
|
+
: `{
|
|
375
|
+
${members}
|
|
376
|
+
}`;
|
|
377
|
+
return { precedence: TypePrecedence.Object, text };
|
|
378
|
+
}
|
|
379
|
+
|
|
380
|
+
function renderInlineArray(schema: SimpleArrayNodeSchema): TypeExpression {
|
|
381
|
+
const elementTypes = renderAllowedTypes(schema.simpleAllowedTypes.keys());
|
|
382
|
+
return {
|
|
383
|
+
precedence: TypePrecedence.Object,
|
|
384
|
+
text: `${formatExpression(elementTypes)}[]`,
|
|
385
|
+
};
|
|
386
|
+
}
|
|
387
|
+
|
|
388
|
+
function renderInlineMap(schema: SimpleMapNodeSchema): TypeExpression {
|
|
389
|
+
const valueType = renderAllowedTypes(schema.simpleAllowedTypes.keys());
|
|
390
|
+
return {
|
|
391
|
+
precedence: TypePrecedence.Object,
|
|
392
|
+
text: `Map<string, ${valueType.text}>`,
|
|
393
|
+
};
|
|
394
|
+
}
|
|
395
|
+
|
|
396
|
+
function renderInlineRecord(schema: SimpleRecordNodeSchema): TypeExpression {
|
|
397
|
+
const valueType = renderAllowedTypes(schema.simpleAllowedTypes.keys());
|
|
398
|
+
return {
|
|
399
|
+
precedence: TypePrecedence.Object,
|
|
400
|
+
text: `Record<string, ${valueType.text}>`,
|
|
401
|
+
};
|
|
402
|
+
}
|
|
403
|
+
}
|
|
404
|
+
|
|
405
|
+
function describeBinding(
|
|
406
|
+
description: string | undefined,
|
|
407
|
+
typeLabel: "array" | "map" | "record",
|
|
408
|
+
binding: BindingIntersectionResult,
|
|
409
|
+
): string | undefined {
|
|
410
|
+
let note = "";
|
|
411
|
+
if (binding.hasMethods && binding.hasProperties) {
|
|
412
|
+
note = `Note: this ${typeLabel} has custom user-defined methods and properties directly on it.`;
|
|
413
|
+
} else if (binding.hasMethods) {
|
|
414
|
+
note = `Note: this ${typeLabel} has custom user-defined methods directly on it.`;
|
|
415
|
+
} else if (binding.hasProperties) {
|
|
416
|
+
note = `Note: this ${typeLabel} has custom user-defined properties directly on it.`;
|
|
417
|
+
}
|
|
418
|
+
|
|
419
|
+
if (note === "") {
|
|
420
|
+
return description === undefined || description === "" ? undefined : description;
|
|
421
|
+
}
|
|
422
|
+
if (description === undefined || description === "") {
|
|
423
|
+
return note;
|
|
424
|
+
}
|
|
425
|
+
return `${description} - ${note}`;
|
|
426
|
+
}
|
|
427
|
+
|
|
428
|
+
function renderPropertyLines(properties: Record<string, PropertyDef>): string[] {
|
|
429
|
+
const lines: string[] = [];
|
|
430
|
+
for (const [name, property] of Object.entries(properties)) {
|
|
431
|
+
if (property.description !== undefined && property.description !== "") {
|
|
432
|
+
for (const note of property.description.split("\n")) {
|
|
433
|
+
lines.push(`// ${note}`);
|
|
434
|
+
}
|
|
435
|
+
}
|
|
436
|
+
const modifier = property.readOnly ? "readonly " : "";
|
|
437
|
+
lines.push(`${modifier}${name}: ${renderZodType(property.schema)};`);
|
|
438
|
+
}
|
|
439
|
+
return lines;
|
|
440
|
+
}
|
|
441
|
+
|
|
442
|
+
function formatMethod(name: string, method: FunctionWrapper): string {
|
|
443
|
+
const args: string[] = [];
|
|
444
|
+
for (const [argName, argType] of method.args) {
|
|
445
|
+
const { innerType, optional } = unwrapOptional(argType);
|
|
446
|
+
const renderedType = renderZodType(innerType);
|
|
447
|
+
args.push(`${argName}${optional ? "?" : ""}: ${renderedType}`);
|
|
448
|
+
}
|
|
449
|
+
if (method.rest !== null) {
|
|
450
|
+
args.push(`...rest: ${renderZodType(method.rest)}[]`);
|
|
451
|
+
}
|
|
452
|
+
return `${name}(${args.join(", ")}): ${renderZodType(method.returns)};`;
|
|
453
|
+
}
|
|
454
|
+
|
|
455
|
+
function renderLeaf(leafKind: ValueSchema): string {
|
|
456
|
+
switch (leafKind) {
|
|
457
|
+
case ValueSchema.Boolean: {
|
|
458
|
+
return "boolean";
|
|
459
|
+
}
|
|
460
|
+
case ValueSchema.Number: {
|
|
461
|
+
return "number";
|
|
462
|
+
}
|
|
463
|
+
case ValueSchema.String: {
|
|
464
|
+
return "string";
|
|
465
|
+
}
|
|
466
|
+
case ValueSchema.Null: {
|
|
467
|
+
return "null";
|
|
468
|
+
}
|
|
469
|
+
default: {
|
|
470
|
+
throw new Error(`Unsupported leaf kind ${NodeKind[leafKind]}.`);
|
|
471
|
+
}
|
|
472
|
+
}
|
|
473
|
+
}
|
|
474
|
+
|
|
475
|
+
function formatExpression(
|
|
476
|
+
expression: TypeExpression,
|
|
477
|
+
minPrecedence: TypePrecedence = TypePrecedence.Object,
|
|
478
|
+
): string {
|
|
479
|
+
return expression.precedence < minPrecedence ? `(${expression.text})` : expression.text;
|
|
480
|
+
}
|
|
481
|
+
|
|
482
|
+
/**
|
|
483
|
+
* Detects optional zod wrappers so argument lists can keep TypeScript optional markers in sync.
|
|
484
|
+
*/
|
|
485
|
+
function unwrapOptional(type: z.ZodTypeAny): { innerType: z.ZodTypeAny; optional: boolean } {
|
|
486
|
+
if (type instanceof z.ZodOptional) {
|
|
487
|
+
const inner = type.unwrap() as z.ZodTypeAny;
|
|
488
|
+
return { innerType: inner, optional: true };
|
|
489
|
+
}
|
|
490
|
+
return { innerType: type, optional: false };
|
|
491
|
+
}
|
|
492
|
+
|
|
493
|
+
/**
|
|
494
|
+
* Verifies that helper members do not clobber structural fields and fails fast if they do.
|
|
495
|
+
*/
|
|
496
|
+
function ensureNoMemberConflicts(
|
|
497
|
+
definition: string,
|
|
498
|
+
fieldNames: ReadonlySet<string>,
|
|
499
|
+
methods: Record<string, FunctionWrapper>,
|
|
500
|
+
properties: Record<string, PropertyDef>,
|
|
501
|
+
): void {
|
|
502
|
+
for (const name of Object.keys(methods)) {
|
|
503
|
+
if (fieldNames.has(name)) {
|
|
504
|
+
throw new UsageError(
|
|
505
|
+
`Method ${name} conflicts with field of the same name in schema ${definition}`,
|
|
506
|
+
);
|
|
507
|
+
}
|
|
508
|
+
}
|
|
509
|
+
for (const name of Object.keys(properties)) {
|
|
510
|
+
if (fieldNames.has(name)) {
|
|
511
|
+
throw new UsageError(
|
|
512
|
+
`Property ${name} conflicts with field of the same name in schema ${definition}`,
|
|
513
|
+
);
|
|
514
|
+
}
|
|
515
|
+
}
|
|
516
|
+
}
|
|
517
|
+
|
|
518
|
+
/**
|
|
519
|
+
* Converts schema metadata into TypeScript declarations suitable for prompt inclusion.
|
|
520
|
+
*/
|
|
521
|
+
function renderZodType(type: z.ZodTypeAny): string {
|
|
522
|
+
return renderZodTypeScript(type, getFriendlyName, instanceOfs);
|
|
523
|
+
}
|