@fluidframework/tree-agent 2.73.0 → 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.
Files changed (68) hide show
  1. package/api-report/tree-agent.alpha.api.md +1 -3
  2. package/dist/agent.d.ts.map +1 -1
  3. package/dist/agent.js +2 -0
  4. package/dist/agent.js.map +1 -1
  5. package/dist/methodBinding.d.ts +7 -4
  6. package/dist/methodBinding.d.ts.map +1 -1
  7. package/dist/methodBinding.js +14 -3
  8. package/dist/methodBinding.js.map +1 -1
  9. package/dist/prompt.d.ts.map +1 -1
  10. package/dist/prompt.js +2 -12
  11. package/dist/prompt.js.map +1 -1
  12. package/dist/propertyBinding.d.ts.map +1 -1
  13. package/dist/propertyBinding.js +2 -2
  14. package/dist/propertyBinding.js.map +1 -1
  15. package/dist/renderSchemaTypeScript.d.ts +18 -0
  16. package/dist/renderSchemaTypeScript.d.ts.map +1 -0
  17. package/dist/renderSchemaTypeScript.js +399 -0
  18. package/dist/renderSchemaTypeScript.js.map +1 -0
  19. package/dist/renderZodTypeScript.d.ts +21 -0
  20. package/dist/renderZodTypeScript.d.ts.map +1 -0
  21. package/dist/renderZodTypeScript.js +272 -0
  22. package/dist/renderZodTypeScript.js.map +1 -0
  23. package/dist/typeGeneration.d.ts +3 -5
  24. package/dist/typeGeneration.d.ts.map +1 -1
  25. package/dist/typeGeneration.js +21 -254
  26. package/dist/typeGeneration.js.map +1 -1
  27. package/dist/utils.d.ts +10 -27
  28. package/dist/utils.d.ts.map +1 -1
  29. package/dist/utils.js +20 -364
  30. package/dist/utils.js.map +1 -1
  31. package/lib/agent.d.ts.map +1 -1
  32. package/lib/agent.js +2 -0
  33. package/lib/agent.js.map +1 -1
  34. package/lib/methodBinding.d.ts +7 -4
  35. package/lib/methodBinding.d.ts.map +1 -1
  36. package/lib/methodBinding.js +11 -1
  37. package/lib/methodBinding.js.map +1 -1
  38. package/lib/prompt.d.ts.map +1 -1
  39. package/lib/prompt.js +3 -13
  40. package/lib/prompt.js.map +1 -1
  41. package/lib/propertyBinding.d.ts.map +1 -1
  42. package/lib/propertyBinding.js +1 -1
  43. package/lib/propertyBinding.js.map +1 -1
  44. package/lib/renderSchemaTypeScript.d.ts +18 -0
  45. package/lib/renderSchemaTypeScript.d.ts.map +1 -0
  46. package/lib/renderSchemaTypeScript.js +395 -0
  47. package/lib/renderSchemaTypeScript.js.map +1 -0
  48. package/lib/renderZodTypeScript.d.ts +21 -0
  49. package/lib/renderZodTypeScript.d.ts.map +1 -0
  50. package/lib/renderZodTypeScript.js +267 -0
  51. package/lib/renderZodTypeScript.js.map +1 -0
  52. package/lib/typeGeneration.d.ts +3 -5
  53. package/lib/typeGeneration.d.ts.map +1 -1
  54. package/lib/typeGeneration.js +24 -257
  55. package/lib/typeGeneration.js.map +1 -1
  56. package/lib/utils.d.ts +10 -27
  57. package/lib/utils.d.ts.map +1 -1
  58. package/lib/utils.js +20 -362
  59. package/lib/utils.js.map +1 -1
  60. package/package.json +10 -10
  61. package/src/agent.ts +2 -0
  62. package/src/methodBinding.ts +17 -5
  63. package/src/prompt.ts +6 -22
  64. package/src/propertyBinding.ts +2 -1
  65. package/src/renderSchemaTypeScript.ts +523 -0
  66. package/src/renderZodTypeScript.ts +314 -0
  67. package/src/typeGeneration.ts +32 -414
  68. package/src/utils.ts +20 -414
package/src/utils.ts CHANGED
@@ -3,13 +3,10 @@
3
3
  * Licensed under the MIT License.
4
4
  */
5
5
 
6
- /* eslint-disable @typescript-eslint/no-unsafe-argument */
7
- /* eslint-disable @typescript-eslint/explicit-function-return-type */
8
-
9
6
  import { assert } from "@fluidframework/core-utils/internal";
10
7
  import { isFluidHandle } from "@fluidframework/runtime-utils";
11
8
  import { UsageError } from "@fluidframework/telemetry-utils/internal";
12
- import type { ImplicitFieldSchema, TreeNodeSchemaClass } from "@fluidframework/tree";
9
+ import type { ImplicitFieldSchema } from "@fluidframework/tree";
13
10
  import type {
14
11
  InsertableContent,
15
12
  TreeNode,
@@ -19,15 +16,10 @@ import type {
19
16
  import {
20
17
  ArrayNodeSchema,
21
18
  MapNodeSchema,
22
- ObjectNodeSchema,
23
19
  RecordNodeSchema,
24
20
  TreeAlpha,
25
21
  } from "@fluidframework/tree/alpha";
26
22
  import { NodeKind, normalizeFieldSchema } from "@fluidframework/tree/internal";
27
- import { z } from "zod";
28
-
29
- import { FunctionWrapper } from "./methodBinding.js";
30
- import { PropertyDef } from "./propertyBinding.js";
31
23
 
32
24
  /**
33
25
  * Subset of Map interface.
@@ -180,423 +172,37 @@ export function isNamedSchema(schemaIdentifier: string): boolean {
180
172
  return false;
181
173
  }
182
174
 
183
- return schemaIdentifier.match(/(?:Array|Map|Record)<\["(.*)"]>/) === null;
175
+ return /(?:Array|Map|Record)<\["(.*)"]>/.exec(schemaIdentifier) === null;
184
176
  }
185
177
 
186
178
  /**
187
- * Returns the unqualified name of a schema (e.g. `"my.scope.MyNode"` returns `"MyNode"`).
188
- * @remarks This works by removing all characters before the last dot in the schema name.
189
- * If there is a dot in a user's schema name, this might produce unexpected results.
179
+ * Returns the unqualified, sanitized Typescript-safe name of a schema
180
+ * Examples:
181
+ * - `"my.scope.MyNode"` returns `"MyNode"`
182
+ * - `"my.scope.MyNode-2"` returns `"MyNode_2"`
183
+ * - `"my.scope.MyNode!"` returns `"MyNode_"`
184
+ * @remarks
185
+ * - Removes all characters before the last dot in the schema name.
186
+ * - Sanitizes the remainder into a valid Typescript identifier
187
+ * - If there is a dot in a user's schema name, this might produce unexpected results.
190
188
  */
191
189
  export function unqualifySchema(schemaIdentifier: string): string {
192
190
  // Get the unqualified name by removing the scope (everything before the last dot).
193
- const matches = schemaIdentifier.match(/[^.]+$/);
194
- if (matches === null) {
195
- return schemaIdentifier; // Return the original name if it is unscoped.
196
- }
197
- return matches[0];
198
- }
199
-
200
- /**
201
- * Details about the properties of a TypeScript schema represented as Zod.
202
- */
203
- export interface SchemaDetails {
204
- hasHelperMethods: boolean;
205
- }
206
-
207
- // TODO: yuck, this entire file has too many statics. we should rewrite it as a generic zod schema walk.
208
- let detailsI: SchemaDetails = {
209
- hasHelperMethods: false,
210
- };
211
-
212
- /**
213
- * Returns the TypeScript source code corresponding to a Zod schema. The schema is supplied as an object where each
214
- * property provides a name for an associated Zod type. The return value is a string containing the TypeScript source
215
- * code corresponding to the schema. Each property of the schema object is emitted as a named `interface` or `type`
216
- * declaration for the associated type and is referenced by that name in the emitted type declarations. Other types
217
- * referenced in the schema are emitted in their structural form.
218
- * @param schema - A schema object where each property provides a name for an associated Zod type.
219
- * @param details - Optional details about the schema. The fields will be set according to the details in the given schema.
220
- * @returns The TypeScript source code corresponding to the schema.
221
- */
222
- export function getZodSchemaAsTypeScript(
223
- schema: Record<string, z.ZodType>,
224
- details?: SchemaDetails,
225
- ): string {
226
- detailsI = details ?? { hasHelperMethods: false };
227
- let result = "";
228
- let startOfLine = true;
229
- let indent = 0;
230
- const entries = [...Object.entries(schema)];
231
- const namedTypes = new Map<object, string>(
232
- entries.map(([name, type]) => [getTypeIdentity(type), name]),
233
- );
234
- for (const [name, type] of entries) {
235
- if (result) {
236
- appendNewLine();
237
- }
238
- const description = type._def.description;
239
- if (description !== undefined && description !== "") {
240
- for (const comment of description.split("\n")) {
241
- append(`// ${comment}`);
242
- appendNewLine();
243
- }
244
- }
245
- if (getTypeKind(type) === z.ZodFirstPartyTypeKind.ZodObject) {
246
- append(`interface ${name} `);
247
- appendObjectType(type as z.ZodObject<z.ZodRawShape>);
248
- } else {
249
- append(`type ${name} = `);
250
- appendTypeDefinition(type);
251
- append(";");
252
- }
253
- appendNewLine();
254
- }
255
- return result;
256
-
257
- function append(s: string) {
258
- if (startOfLine) {
259
- result += " ".repeat(indent);
260
- startOfLine = false;
261
- }
262
- result += s;
263
- }
264
-
265
- function appendNewLine() {
266
- append("\n");
267
- startOfLine = true;
268
- }
269
-
270
- function appendType(type: z.ZodType, minPrecedence = TypePrecedence.Object) {
271
- const name = namedTypes.get(getTypeIdentity(type));
272
- if (name === undefined) {
273
- const parenthesize = getTypePrecendece(type) < minPrecedence;
274
- if (parenthesize) append("(");
275
- appendTypeDefinition(type);
276
- if (parenthesize) append(")");
277
- } else {
278
- append(name);
279
- }
280
- }
281
-
282
- function appendTypeDefinition(type: z.ZodType) {
283
- switch (getTypeKind(type)) {
284
- case z.ZodFirstPartyTypeKind.ZodString: {
285
- return append("string");
286
- }
287
- case z.ZodFirstPartyTypeKind.ZodNumber: {
288
- return append("number");
289
- }
290
- case z.ZodFirstPartyTypeKind.ZodBoolean: {
291
- return append("boolean");
292
- }
293
- case z.ZodFirstPartyTypeKind.ZodDate: {
294
- return append("Date");
295
- }
296
- case z.ZodFirstPartyTypeKind.ZodUndefined: {
297
- return append("undefined");
298
- }
299
- case z.ZodFirstPartyTypeKind.ZodNull: {
300
- return append("null");
301
- }
302
- case z.ZodFirstPartyTypeKind.ZodUnknown: {
303
- return append("unknown");
304
- }
305
- case z.ZodFirstPartyTypeKind.ZodArray: {
306
- return appendArrayType(type);
307
- }
308
- case z.ZodFirstPartyTypeKind.ZodObject: {
309
- return appendObjectType(type);
310
- }
311
- case z.ZodFirstPartyTypeKind.ZodUnion: {
312
- return appendUnionOrIntersectionTypes(
313
- (type._def as z.ZodUnionDef).options,
314
- TypePrecedence.Union,
315
- );
316
- }
317
- case z.ZodFirstPartyTypeKind.ZodDiscriminatedUnion: {
318
- return appendUnionOrIntersectionTypes(
319
- [...(type._def as z.ZodDiscriminatedUnionDef<string>).options.values()],
320
- TypePrecedence.Union,
321
- );
322
- }
323
- case z.ZodFirstPartyTypeKind.ZodIntersection: {
324
- return appendUnionOrIntersectionTypes(
325
- [
326
- (type._def as z.ZodIntersectionDef).left,
327
- (type._def as z.ZodIntersectionDef).right,
328
- ],
329
- TypePrecedence.Intersection,
330
- );
331
- }
332
- case z.ZodFirstPartyTypeKind.ZodTuple: {
333
- return appendTupleType(type);
334
- }
335
- case z.ZodFirstPartyTypeKind.ZodRecord: {
336
- return appendRecordType(type);
337
- }
338
- case z.ZodFirstPartyTypeKind.ZodMap: {
339
- return appendMapType(type);
340
- }
341
- case z.ZodFirstPartyTypeKind.ZodLiteral: {
342
- return appendLiteral((type._def as z.ZodLiteralDef).value);
343
- }
344
- case z.ZodFirstPartyTypeKind.ZodEnum: {
345
- return append(
346
- (type._def as z.ZodEnumDef).values.map((value) => JSON.stringify(value)).join(" | "),
347
- );
348
- }
349
- case z.ZodFirstPartyTypeKind.ZodOptional: {
350
- return appendUnionOrIntersectionTypes(
351
- [(type._def as z.ZodOptionalDef).innerType, z.undefined()],
352
- TypePrecedence.Union,
353
- );
354
- }
355
- case z.ZodFirstPartyTypeKind.ZodReadonly: {
356
- return appendReadonlyType(type);
357
- }
358
- case z.ZodFirstPartyTypeKind.ZodEffects: {
359
- // Currently, this only handles schema class instances, but there are other cases in which a ZodEffects could theoretically be used.
360
- if (instanceOfs.has(type)) {
361
- const objectNodeSchema = instanceOfs.get(type);
362
- if (objectNodeSchema === undefined) {
363
- throw new UsageError(
364
- `Unsupported zod effects type when transforming class method: ${getTypeKind(type)}`,
365
- );
366
- }
367
- return append(getFriendlyName(objectNodeSchema));
368
- }
369
- throw new Error(
370
- "Unsupported zod effects type. Did you use z.instanceOf? Use ExposedMethods.instanceOf function to reference schema classes in methods.",
371
- );
372
- }
373
- case z.ZodFirstPartyTypeKind.ZodVoid: {
374
- return append("void");
375
- }
376
- case z.ZodFirstPartyTypeKind.ZodLazy: {
377
- return appendType((type._def as z.ZodLazyDef).getter());
378
- }
379
- default: {
380
- throw new UsageError(
381
- `Unsupported type when transforming class method: ${getTypeKind(type)}`,
382
- );
383
- }
384
- }
385
- }
386
-
387
- function appendBoundMethods(boundType: z.ZodType): void {
388
- // eslint-disable-next-line prefer-const
389
- for (let [name, type] of Object.entries((boundType._def as z.ZodObjectDef).shape())) {
390
- // Special handling of methods on objects
391
- const method = (type as unknown as { method: object | undefined }).method;
392
- if (method !== undefined && method instanceof FunctionWrapper) {
393
- detailsI.hasHelperMethods = true;
394
- append(name);
395
- append("(");
396
- let first = true;
397
- for (const [argName, argType] of method.args) {
398
- if (!first) append(", ");
399
- if (getTypeKind(argType) === z.ZodFirstPartyTypeKind.ZodOptional) {
400
- append(`${argName}?: `);
401
- appendType((argType._def as z.ZodOptionalDef).innerType, TypePrecedence.Object);
402
- } else {
403
- append(`${argName}: `);
404
- appendType(argType);
405
- }
406
- first = false;
407
- }
408
- if (method.rest !== null) {
409
- if (!first) append(", ");
410
- append("...rest: ");
411
- appendType(method.rest, TypePrecedence.Object);
412
- append("[]");
413
- }
414
- append(`): `);
415
- appendType(method.returns, TypePrecedence.Object);
416
- append(";");
417
- if (method.description !== undefined) {
418
- append(` // ${method.description}`);
419
- }
420
- appendNewLine();
421
- }
422
- }
423
- }
424
-
425
- function appendBoundProperties(type: z.ZodType): void {
426
- const property = (type as unknown as { property?: PropertyDef }).property;
427
-
428
- if (!(property instanceof PropertyDef)) {
429
- if (type.description !== undefined && type.description !== "") {
430
- append(` // ${type.description}`);
431
- }
432
- return;
433
- }
434
-
435
- if (property.readOnly === true) {
436
- append(" // readonly");
437
- }
438
- if (property.description !== undefined && property.description !== "") {
439
- append(` - ${property.description}`);
440
- }
441
- }
442
-
443
- function appendArrayType(arrayType: z.ZodType) {
444
- appendType((arrayType._def as z.ZodArrayDef).type, TypePrecedence.Object);
445
- append("[]");
446
- }
447
-
448
- function appendObjectType(objectType: z.ZodType) {
449
- append("{");
450
- appendNewLine();
451
- indent++;
452
- // eslint-disable-next-line prefer-const
453
- for (let [name, type] of Object.entries((objectType._def as z.ZodObjectDef).shape())) {
454
- const method = (type as unknown as { method: object | undefined }).method;
455
-
456
- if (method === undefined || !(method instanceof FunctionWrapper)) {
457
- append(name);
458
- if (getTypeKind(type) === z.ZodFirstPartyTypeKind.ZodOptional) {
459
- append("?");
460
- type = (type._def as z.ZodOptionalDef).innerType;
461
- }
462
- append(": ");
463
- appendType(type);
464
- append(";");
465
- appendBoundProperties(type);
466
- appendNewLine();
467
- }
468
- }
469
- appendBoundMethods(objectType);
470
- indent--;
471
- append("}");
472
- }
473
-
474
- function appendUnionOrIntersectionTypes(
475
- types: readonly z.ZodType[],
476
- minPrecedence: TypePrecedence,
477
- ) {
478
- let first = true;
479
- for (const type of types) {
480
- if (!first) append(minPrecedence === TypePrecedence.Intersection ? " & " : " | ");
481
- appendType(type, minPrecedence);
482
- first = false;
483
- }
484
- }
485
-
486
- function appendTupleType(tupleType: z.ZodType) {
487
- append("[");
488
- let first = true;
489
- for (const type of (tupleType._def as z.ZodTupleDef<z.ZodTupleItems, z.ZodType>).items) {
490
- if (!first) append(", ");
491
- if (getTypeKind(type) === z.ZodFirstPartyTypeKind.ZodOptional) {
492
- appendType((type._def as z.ZodOptionalDef).innerType, TypePrecedence.Object);
493
- append("?");
494
- } else {
495
- appendType(type);
496
- }
497
- first = false;
498
- }
499
- const rest = (tupleType._def as z.ZodTupleDef<z.ZodTupleItems, z.ZodType | null>).rest;
500
- if (rest !== null) {
501
- if (!first) append(", ");
502
- append("...");
503
- appendType(rest, TypePrecedence.Object);
504
- append("[]");
505
- }
506
- append("]");
507
- }
508
-
509
- function appendRecordType(recordType: z.ZodType) {
510
- append("Record<");
511
- appendType((recordType._def as z.ZodRecordDef).keyType);
512
- append(", ");
513
- appendType((recordType._def as z.ZodRecordDef).valueType);
514
- append(">");
515
- }
516
-
517
- function appendMapType(mapType: z.ZodType) {
518
- append("Map<");
519
- appendType((mapType._def as z.ZodMapDef).keyType);
520
- append(", ");
521
- appendType((mapType._def as z.ZodMapDef).valueType);
522
- append(">");
523
- }
191
+ const matches = /[^.]+$/.exec(schemaIdentifier);
192
+ const unqualifiedName = matches === null ? schemaIdentifier : matches[0];
524
193
 
525
- function appendLiteral(value: unknown) {
526
- append(
527
- typeof value === "string" || typeof value === "number" || typeof value === "boolean"
528
- ? JSON.stringify(value)
529
- : "any",
530
- );
531
- }
532
-
533
- function appendReadonlyType(readonlyType: z.ZodType) {
534
- append("Readonly<");
535
- appendType((readonlyType._def as z.ZodReadonlyDef).innerType);
536
- append(">");
537
- }
538
- }
194
+ let sanitizedName = unqualifiedName;
539
195
 
540
- function getTypeKind(type: z.ZodType): z.ZodFirstPartyTypeKind {
541
- return (type._def as z.ZodTypeDef & { typeName: z.ZodFirstPartyTypeKind }).typeName;
542
- }
196
+ // Replace invalid characters with "_".
197
+ sanitizedName = sanitizedName.replace(/[^\w$]/g, "_");
543
198
 
544
- function getTypeIdentity(type: z.ZodType): object {
545
- switch (getTypeKind(type)) {
546
- case z.ZodFirstPartyTypeKind.ZodObject: {
547
- return (type._def as z.ZodObjectDef).shape();
548
- }
549
- case z.ZodFirstPartyTypeKind.ZodEnum: {
550
- return (type._def as z.ZodEnumDef).values;
551
- }
552
- case z.ZodFirstPartyTypeKind.ZodUnion: {
553
- return (type._def as z.ZodUnionDef).options;
554
- }
555
- default: {
556
- return type;
557
- }
199
+ // If the first character is a number, prefix it with "_".
200
+ if (!/^[$A-Z_a-z]/.test(sanitizedName)) {
201
+ sanitizedName = `_${sanitizedName}`;
558
202
  }
203
+ return sanitizedName;
559
204
  }
560
205
 
561
- const enum TypePrecedence {
562
- Union = 0,
563
- Intersection = 1,
564
- Object = 2,
565
- }
566
-
567
- function getTypePrecendece(type: z.ZodType): TypePrecedence {
568
- switch (getTypeKind(type)) {
569
- case z.ZodFirstPartyTypeKind.ZodEnum:
570
- case z.ZodFirstPartyTypeKind.ZodUnion:
571
- case z.ZodFirstPartyTypeKind.ZodDiscriminatedUnion: {
572
- return TypePrecedence.Union;
573
- }
574
- case z.ZodFirstPartyTypeKind.ZodIntersection: {
575
- return TypePrecedence.Intersection;
576
- }
577
- default: {
578
- return TypePrecedence.Object;
579
- }
580
- }
581
- }
582
-
583
- /**
584
- * Create a Zod schema for a SharedTree schema class.
585
- * @alpha
586
- */
587
- export function instanceOf<T extends TreeNodeSchemaClass>(
588
- schema: T,
589
- ): z.ZodType<InstanceType<T>, z.ZodTypeDef, InstanceType<T>> {
590
- if (!(schema instanceof ObjectNodeSchema)) {
591
- throw new UsageError(`${schema.identifier} must be an instance of ObjectNodeSchema.`);
592
- }
593
- const effect = z.instanceof(schema);
594
- instanceOfs.set(effect, schema);
595
- return effect;
596
- }
597
-
598
- const instanceOfs = new WeakMap<z.ZodTypeAny, ObjectNodeSchema>();
599
-
600
206
  /**
601
207
  * Adds all (optionally filtered) schemas reachable from the given schema to the given set.
602
208
  * @returns The set of schemas added (same as the `schemas` parameter, if supplied).