@odata-effect/odata-effect-generator 0.1.2 → 0.3.0

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 (74) hide show
  1. package/README.md +232 -5
  2. package/dist/cjs/Cli.js +5 -1
  3. package/dist/cjs/Cli.js.map +1 -1
  4. package/dist/cjs/generator/Generator.js +35 -25
  5. package/dist/cjs/generator/Generator.js.map +1 -1
  6. package/dist/cjs/generator/IndexGenerator.js +29 -80
  7. package/dist/cjs/generator/IndexGenerator.js.map +1 -1
  8. package/dist/cjs/generator/ModelsGenerator.js +2 -5
  9. package/dist/cjs/generator/ModelsGenerator.js.map +1 -1
  10. package/dist/cjs/generator/NamingHelper.js +4 -1
  11. package/dist/cjs/generator/NamingHelper.js.map +1 -1
  12. package/dist/cjs/generator/NavigationGenerator.js +370 -0
  13. package/dist/cjs/generator/NavigationGenerator.js.map +1 -0
  14. package/dist/cjs/generator/OperationsGenerator.js +384 -0
  15. package/dist/cjs/generator/OperationsGenerator.js.map +1 -0
  16. package/dist/cjs/generator/QueryModelsGenerator.js +2 -2
  17. package/dist/cjs/generator/QueryModelsGenerator.js.map +1 -1
  18. package/dist/cjs/generator/ServiceFnGenerator.js +102 -224
  19. package/dist/cjs/generator/ServiceFnGenerator.js.map +1 -1
  20. package/dist/cjs/index.js +3 -3
  21. package/dist/dts/Cli.d.ts.map +1 -1
  22. package/dist/dts/generator/Generator.d.ts +2 -0
  23. package/dist/dts/generator/Generator.d.ts.map +1 -1
  24. package/dist/dts/generator/IndexGenerator.d.ts.map +1 -1
  25. package/dist/dts/generator/NamingHelper.d.ts.map +1 -1
  26. package/dist/dts/generator/NavigationGenerator.d.ts +55 -0
  27. package/dist/dts/generator/NavigationGenerator.d.ts.map +1 -0
  28. package/dist/dts/generator/OperationsGenerator.d.ts +50 -0
  29. package/dist/dts/generator/OperationsGenerator.d.ts.map +1 -0
  30. package/dist/dts/generator/ServiceFnGenerator.d.ts +10 -11
  31. package/dist/dts/generator/ServiceFnGenerator.d.ts.map +1 -1
  32. package/dist/dts/index.d.ts +4 -4
  33. package/dist/dts/index.d.ts.map +1 -1
  34. package/dist/esm/Cli.js +5 -1
  35. package/dist/esm/Cli.js.map +1 -1
  36. package/dist/esm/generator/Generator.js +35 -25
  37. package/dist/esm/generator/Generator.js.map +1 -1
  38. package/dist/esm/generator/IndexGenerator.js +29 -80
  39. package/dist/esm/generator/IndexGenerator.js.map +1 -1
  40. package/dist/esm/generator/ModelsGenerator.js +2 -5
  41. package/dist/esm/generator/ModelsGenerator.js.map +1 -1
  42. package/dist/esm/generator/NamingHelper.js +4 -1
  43. package/dist/esm/generator/NamingHelper.js.map +1 -1
  44. package/dist/esm/generator/NavigationGenerator.js +362 -0
  45. package/dist/esm/generator/NavigationGenerator.js.map +1 -0
  46. package/dist/esm/generator/OperationsGenerator.js +375 -0
  47. package/dist/esm/generator/OperationsGenerator.js.map +1 -0
  48. package/dist/esm/generator/QueryModelsGenerator.js +2 -2
  49. package/dist/esm/generator/QueryModelsGenerator.js.map +1 -1
  50. package/dist/esm/generator/ServiceFnGenerator.js +102 -224
  51. package/dist/esm/generator/ServiceFnGenerator.js.map +1 -1
  52. package/dist/esm/index.js +4 -4
  53. package/dist/esm/index.js.map +1 -1
  54. package/generator/NavigationGenerator/package.json +6 -0
  55. package/generator/OperationsGenerator/package.json +6 -0
  56. package/package.json +17 -9
  57. package/src/Cli.ts +15 -2
  58. package/src/generator/Generator.ts +44 -23
  59. package/src/generator/IndexGenerator.ts +29 -80
  60. package/src/generator/ModelsGenerator.ts +2 -5
  61. package/src/generator/NamingHelper.ts +8 -1
  62. package/src/generator/NavigationGenerator.ts +485 -0
  63. package/src/generator/OperationsGenerator.ts +507 -0
  64. package/src/generator/QueryModelsGenerator.ts +2 -2
  65. package/src/generator/ServiceFnGenerator.ts +117 -265
  66. package/src/index.ts +4 -4
  67. package/dist/cjs/generator/ServiceFnPromiseGenerator.js +0 -183
  68. package/dist/cjs/generator/ServiceFnPromiseGenerator.js.map +0 -1
  69. package/dist/dts/generator/ServiceFnPromiseGenerator.d.ts +0 -40
  70. package/dist/dts/generator/ServiceFnPromiseGenerator.d.ts.map +0 -1
  71. package/dist/esm/generator/ServiceFnPromiseGenerator.js +0 -175
  72. package/dist/esm/generator/ServiceFnPromiseGenerator.js.map +0 -1
  73. package/generator/ServiceFnPromiseGenerator/package.json +0 -6
  74. package/src/generator/ServiceFnPromiseGenerator.ts +0 -243
@@ -0,0 +1,485 @@
1
+ /**
2
+ * Generator for type-safe, tree-shakable navigation path builders.
3
+ *
4
+ * Generates branded path types and navigation functions that can be composed
5
+ * with pipe() for type-safe OData path construction.
6
+ *
7
+ * @example
8
+ * ```typescript
9
+ * import { pipe } from "effect"
10
+ * import { People, byKey, trips, planItems, asFlight } from "./PathBuilders"
11
+ *
12
+ * const path = pipe(
13
+ * People,
14
+ * byKey("russellwhyte"),
15
+ * trips,
16
+ * byKey(0),
17
+ * planItems,
18
+ * asFlight
19
+ * )
20
+ * ```
21
+ *
22
+ * @since 1.0.0
23
+ */
24
+ import type { DataModel, EntityTypeModel } from "../model/DataModel.js"
25
+ import type { ODataVersion } from "../parser/EdmxSchema.js"
26
+ import { getClassName, toCamelCase } from "./NamingHelper.js"
27
+
28
+ /**
29
+ * Version-specific configuration.
30
+ */
31
+ interface VersionConfig {
32
+ readonly odataNamespace: string
33
+ readonly clientModule: string
34
+ readonly queryOptionsType: string
35
+ }
36
+
37
+ const V2_CONFIG: VersionConfig = {
38
+ odataNamespace: "OData",
39
+ clientModule: "ODataClient",
40
+ queryOptionsType: "ODataQueryOptions"
41
+ }
42
+
43
+ const V4_CONFIG: VersionConfig = {
44
+ odataNamespace: "ODataV4",
45
+ clientModule: "ODataV4Client",
46
+ queryOptionsType: "ODataV4QueryOptions"
47
+ }
48
+
49
+ const getVersionConfig = (version: ODataVersion): VersionConfig => version === "V4" ? V4_CONFIG : V2_CONFIG
50
+
51
+ /**
52
+ * Get the path builders module name.
53
+ */
54
+ export const getPathBuildersModuleName = (): string => "PathBuilders"
55
+
56
+ /**
57
+ * Generated navigation file.
58
+ *
59
+ * @since 1.0.0
60
+ * @category types
61
+ */
62
+ export interface GeneratedNavigationFile {
63
+ readonly fileName: string
64
+ readonly content: string
65
+ }
66
+
67
+ /**
68
+ * Result of navigation generation.
69
+ *
70
+ * @since 1.0.0
71
+ * @category types
72
+ */
73
+ export interface NavigationGenerationResult {
74
+ readonly navigationFiles: ReadonlyArray<GeneratedNavigationFile>
75
+ }
76
+
77
+ /**
78
+ * Track navigation property info for collision detection.
79
+ */
80
+ interface NavPropertyInfo {
81
+ readonly propertyName: string
82
+ readonly odataName: string
83
+ readonly sourceEntityName: string
84
+ readonly targetEntityName: string
85
+ readonly isCollection: boolean
86
+ }
87
+
88
+ /**
89
+ * Collect all navigation properties across all entity types.
90
+ */
91
+ const collectAllNavigationProperties = (dataModel: DataModel): ReadonlyArray<NavPropertyInfo> => {
92
+ const navProps: Array<NavPropertyInfo> = []
93
+
94
+ for (const entityType of dataModel.entityTypes.values()) {
95
+ for (const navProp of entityType.navigationProperties) {
96
+ const targetTypeName = getClassName(navProp.targetType)
97
+ navProps.push({
98
+ propertyName: navProp.name,
99
+ odataName: navProp.odataName,
100
+ sourceEntityName: entityType.name,
101
+ targetEntityName: targetTypeName,
102
+ isCollection: navProp.isCollection
103
+ })
104
+ }
105
+ }
106
+
107
+ return navProps
108
+ }
109
+
110
+ /**
111
+ * Check if a navigation property name has collisions (same name, different source entities).
112
+ */
113
+ const findCollisions = (navProps: ReadonlyArray<NavPropertyInfo>): Set<string> => {
114
+ const collisions = new Set<string>()
115
+ const seen = new Map<string, NavPropertyInfo>()
116
+
117
+ for (const prop of navProps) {
118
+ const existing = seen.get(prop.propertyName)
119
+ if (existing) {
120
+ // Collision if same name but different source entity
121
+ if (existing.sourceEntityName !== prop.sourceEntityName) {
122
+ collisions.add(prop.propertyName)
123
+ }
124
+ } else {
125
+ seen.set(prop.propertyName, prop)
126
+ }
127
+ }
128
+
129
+ return collisions
130
+ }
131
+
132
+ /**
133
+ * Get the function name for a navigation property.
134
+ */
135
+ const getNavFunctionName = (
136
+ prop: NavPropertyInfo,
137
+ hasCollision: boolean
138
+ ): string => {
139
+ if (hasCollision) {
140
+ // Qualified name: personTrips, orderTrips
141
+ return toCamelCase(`${prop.sourceEntityName}_${prop.propertyName}`)
142
+ }
143
+ return toCamelCase(prop.propertyName)
144
+ }
145
+
146
+ /**
147
+ * Collect derived types for an entity type (for type casting).
148
+ */
149
+ const getDerivedTypes = (
150
+ baseTypeFqName: string,
151
+ dataModel: DataModel
152
+ ): ReadonlyArray<EntityTypeModel> => {
153
+ const derived: Array<EntityTypeModel> = []
154
+
155
+ for (const entityType of dataModel.entityTypes.values()) {
156
+ if (entityType.baseType === baseTypeFqName) {
157
+ derived.push(entityType)
158
+ // Recursively get further derived types
159
+ const furtherDerived = getDerivedTypes(entityType.fqName, dataModel)
160
+ for (const d of furtherDerived) {
161
+ derived.push(d)
162
+ }
163
+ }
164
+ }
165
+
166
+ return derived
167
+ }
168
+
169
+ /**
170
+ * Generate navigation builders.
171
+ *
172
+ * @since 1.0.0
173
+ * @category generation
174
+ */
175
+ export const generateNavigations = (dataModel: DataModel): NavigationGenerationResult => {
176
+ const moduleName = getPathBuildersModuleName()
177
+ const content = generatePathBuildersFile(dataModel)
178
+
179
+ return {
180
+ navigationFiles: [{
181
+ fileName: `${moduleName}.ts`,
182
+ content
183
+ }]
184
+ }
185
+ }
186
+
187
+ /**
188
+ * Generate the PathBuilders.ts file.
189
+ */
190
+ const generatePathBuildersFile = (dataModel: DataModel): string => {
191
+ const lines: Array<string> = []
192
+ const versionConfig = getVersionConfig(dataModel.version)
193
+
194
+ // Collect all nav properties and find collisions
195
+ const allNavProps = collectAllNavigationProperties(dataModel)
196
+ const collisions = findCollisions(allNavProps)
197
+
198
+ // Collect all entity type names that need to be referenced
199
+ const referencedTypes = collectReferencedTypes(dataModel)
200
+
201
+ // Header
202
+ lines.push(`/**`)
203
+ lines.push(` * Type-safe, tree-shakable path builders for OData navigation.`)
204
+ lines.push(` * Generated by odata-effect-gen.`)
205
+ lines.push(` *`)
206
+ lines.push(` * @example`)
207
+ lines.push(` * \`\`\`typescript`)
208
+ lines.push(` * import { pipe } from "effect"`)
209
+ lines.push(` * import { People, byKey, trips, planItems } from "./PathBuilders"`)
210
+ lines.push(` *`)
211
+ lines.push(` * const path = pipe(`)
212
+ lines.push(` * People,`)
213
+ lines.push(` * byKey("russellwhyte"),`)
214
+ lines.push(` * trips,`)
215
+ lines.push(` * byKey(0),`)
216
+ lines.push(` * planItems`)
217
+ lines.push(` * )`)
218
+ lines.push(` * \`\`\``)
219
+ lines.push(` *`)
220
+ lines.push(` * @since 1.0.0`)
221
+ lines.push(` */`)
222
+ lines.push(``)
223
+
224
+ // Imports
225
+ lines.push(
226
+ `import { ${versionConfig.odataNamespace}, ${versionConfig.clientModule}} from "@odata-effect/odata-effect"`
227
+ )
228
+ const runtimeType = dataModel.version === "V4" ? "ODataV4Runtime" : "ODataRuntime"
229
+ lines.push(`import type { Runtime } from "@odata-effect/odata-effect-promise"`)
230
+ lines.push(`import type { Effect, Schema } from "effect"`)
231
+ lines.push(``)
232
+
233
+ // Import model types with Model suffix to avoid collision with entity set names
234
+ if (referencedTypes.size > 0) {
235
+ lines.push(`import type {`)
236
+ const typesList = Array.from(referencedTypes).sort()
237
+ for (let i = 0; i < typesList.length; i++) {
238
+ const isLast = i === typesList.length - 1
239
+ lines.push(` ${typesList[i]} as ${typesList[i]}Model${isLast ? "" : ","}`)
240
+ }
241
+ lines.push(`} from "./Models"`)
242
+ lines.push(``)
243
+ }
244
+
245
+ // Path branded type
246
+ lines.push(`// ============================================================================`)
247
+ lines.push(`// Path Types`)
248
+ lines.push(`// ============================================================================`)
249
+ lines.push(``)
250
+ lines.push(`/**`)
251
+ lines.push(` * Branded path type that tracks the entity type and whether it's a collection.`)
252
+ lines.push(` * This enables type-safe navigation - you can only navigate to properties`)
253
+ lines.push(` * that exist on the current entity type.`)
254
+ lines.push(` *`)
255
+ lines.push(` * @since 1.0.0`)
256
+ lines.push(` * @category types`)
257
+ lines.push(` */`)
258
+ lines.push(`export type Path<TEntity, IsCollection extends boolean = false> = string & {`)
259
+ lines.push(` readonly _entity: TEntity`)
260
+ lines.push(` readonly _collection: IsCollection`)
261
+ lines.push(`}`)
262
+ lines.push(``)
263
+
264
+ // Entity set roots (PascalCase, types use Model suffix to avoid collision)
265
+ lines.push(`// ============================================================================`)
266
+ lines.push(`// Entity Set Roots`)
267
+ lines.push(`// ============================================================================`)
268
+ lines.push(``)
269
+ for (const entitySet of dataModel.entitySets.values()) {
270
+ const entityType = dataModel.entityTypes.get(entitySet.entityTypeFqName)
271
+ if (entityType) {
272
+ lines.push(`/**`)
273
+ lines.push(` * Root path for ${entitySet.name} entity set.`)
274
+ lines.push(` *`)
275
+ lines.push(` * @since 1.0.0`)
276
+ lines.push(` * @category entity-sets`)
277
+ lines.push(` */`)
278
+ lines.push(
279
+ `export const ${entitySet.name}: Path<${entityType.name}Model, true> = "${entitySet.name}" as Path<${entityType.name}Model, true>`
280
+ )
281
+ lines.push(``)
282
+ }
283
+ }
284
+
285
+ // byKey function
286
+ lines.push(`// ============================================================================`)
287
+ lines.push(`// Key Access`)
288
+ lines.push(`// ============================================================================`)
289
+ lines.push(``)
290
+ lines.push(`/**`)
291
+ lines.push(` * Navigate to a specific entity by key.`)
292
+ lines.push(` * Works on any collection path.`)
293
+ lines.push(` *`)
294
+ lines.push(` * @example`)
295
+ lines.push(` * \`\`\`typescript`)
296
+ lines.push(` * pipe(People, byKey("russellwhyte")) // Path<PersonModel, false>`)
297
+ lines.push(` * pipe(Airports, byKey("KSFO")) // Path<AirportModel, false>`)
298
+ lines.push(` * \`\`\``)
299
+ lines.push(` *`)
300
+ lines.push(` * @since 1.0.0`)
301
+ lines.push(` * @category navigation`)
302
+ lines.push(` */`)
303
+ lines.push(`export const byKey = <T>(key: string | number) =>`)
304
+ lines.push(` (base: Path<T, true>): Path<T, false> =>`)
305
+ lines.push(` \`\${base}(\${typeof key === "string" ? \`'\${key}'\` : key})\` as Path<T, false>`)
306
+ lines.push(``)
307
+
308
+ // Navigation property functions
309
+ lines.push(`// ============================================================================`)
310
+ lines.push(`// Navigation Properties`)
311
+ lines.push(`// ============================================================================`)
312
+ lines.push(``)
313
+
314
+ // Group nav props by source entity for better organization
315
+ const navPropsBySource = new Map<string, Array<NavPropertyInfo>>()
316
+ for (const prop of allNavProps) {
317
+ const existing = navPropsBySource.get(prop.sourceEntityName) || []
318
+ existing.push(prop)
319
+ navPropsBySource.set(prop.sourceEntityName, existing)
320
+ }
321
+
322
+ for (const [sourceEntity, props] of navPropsBySource) {
323
+ lines.push(`// From ${sourceEntity}`)
324
+ for (const prop of props) {
325
+ const hasCollision = collisions.has(prop.propertyName)
326
+ const fnName = getNavFunctionName(prop, hasCollision)
327
+ const targetType = prop.targetEntityName
328
+
329
+ lines.push(`/**`)
330
+ lines.push(` * Navigate to ${prop.odataName}${prop.isCollection ? " collection" : ""}.`)
331
+ lines.push(` *`)
332
+ lines.push(` * @since 1.0.0`)
333
+ lines.push(` * @category navigation`)
334
+ lines.push(` */`)
335
+ lines.push(
336
+ `export const ${fnName} = (base: Path<${sourceEntity}Model, false>): Path<${targetType}Model, ${prop.isCollection}> =>`
337
+ )
338
+ lines.push(` \`\${base}/${prop.odataName}\` as Path<${targetType}Model, ${prop.isCollection}>`)
339
+ lines.push(``)
340
+ }
341
+ }
342
+
343
+ // Type casting functions for derived types
344
+ const castFunctions = generateTypeCastFunctions(dataModel)
345
+ if (castFunctions.length > 0) {
346
+ lines.push(`// ============================================================================`)
347
+ lines.push(`// Type Casting (for entity inheritance)`)
348
+ lines.push(`// ============================================================================`)
349
+ lines.push(``)
350
+ for (const castFunction of castFunctions) {
351
+ lines.push(castFunction)
352
+ }
353
+ }
354
+
355
+ // Terminal operations (Effect-based)
356
+ lines.push(`// ============================================================================`)
357
+ lines.push(`// Terminal Operations (Effect-based)`)
358
+ lines.push(`// ============================================================================`)
359
+ lines.push(``)
360
+ lines.push(`/**`)
361
+ lines.push(` * Fetch a collection of entities at the given path (Effect-based).`)
362
+ lines.push(` *`)
363
+ lines.push(` * @example`)
364
+ lines.push(` * \`\`\`typescript`)
365
+ lines.push(` * const allPeople = yield* pipe(People, fetchCollection(Person))`)
366
+ lines.push(` * \`\`\``)
367
+ lines.push(` *`)
368
+ lines.push(` * @since 1.0.0`)
369
+ lines.push(` * @category operations`)
370
+ lines.push(` */`)
371
+ lines.push(`export const fetchCollection = <T, I>(schema: Schema.Schema<T, I>) =>`)
372
+ lines.push(` (path: Path<T, true>, options?: ${versionConfig.clientModule}.${versionConfig.queryOptionsType}) =>`)
373
+ lines.push(` ${versionConfig.odataNamespace}.getCollection(path, schema, options)`)
374
+ lines.push(``)
375
+ lines.push(`/**`)
376
+ lines.push(` * Fetch a single entity at the given path (Effect-based).`)
377
+ lines.push(` *`)
378
+ lines.push(` * @example`)
379
+ lines.push(` * \`\`\`typescript`)
380
+ lines.push(` * const person = yield* pipe(People, byKey("russell"), fetchOne(Person))`)
381
+ lines.push(` * \`\`\``)
382
+ lines.push(` *`)
383
+ lines.push(` * @since 1.0.0`)
384
+ lines.push(` * @category operations`)
385
+ lines.push(` */`)
386
+ lines.push(`export const fetchOne = <T, I>(schema: Schema.Schema<T, I>) =>`)
387
+ lines.push(` (path: Path<T, false>, options?: ${versionConfig.clientModule}.${versionConfig.queryOptionsType}) =>`)
388
+ lines.push(` ${versionConfig.odataNamespace}.get(path, schema, options)`)
389
+ lines.push(``)
390
+
391
+ // Promise conversion (pipe-friendly)
392
+ lines.push(`// ============================================================================`)
393
+ lines.push(`// Promise Conversion`)
394
+ lines.push(`// ============================================================================`)
395
+ lines.push(``)
396
+ lines.push(`/**`)
397
+ lines.push(` * Convert an Effect to a Promise. Use at the end of a pipe chain.`)
398
+ lines.push(` *`)
399
+ lines.push(` * @example`)
400
+ lines.push(` * \`\`\`typescript`)
401
+ lines.push(` * const myTrips = await pipe(`)
402
+ lines.push(` * People,`)
403
+ lines.push(` * byKey("russell"),`)
404
+ lines.push(` * trips,`)
405
+ lines.push(` * fetchCollection(Trip),`)
406
+ lines.push(` * toPromise(runtime)`)
407
+ lines.push(` * )`)
408
+ lines.push(` * \`\`\``)
409
+ lines.push(` *`)
410
+ lines.push(` * @since 1.0.0`)
411
+ lines.push(` * @category operations`)
412
+ lines.push(` */`)
413
+ lines.push(`export const toPromise = (runtime: Runtime.${runtimeType}) =>`)
414
+ lines.push(` <A, E, R>(effect: Effect.Effect<A, E, R>): Promise<A> =>`)
415
+ lines.push(` runtime.runPromise(effect as any)`)
416
+ lines.push(``)
417
+
418
+ return lines.join("\n")
419
+ }
420
+
421
+ /**
422
+ * Generate type casting functions for derived types.
423
+ */
424
+ const generateTypeCastFunctions = (dataModel: DataModel): Array<string> => {
425
+ const lines: Array<string> = []
426
+ const processed = new Set<string>()
427
+
428
+ for (const entityType of dataModel.entityTypes.values()) {
429
+ const derivedTypes = getDerivedTypes(entityType.fqName, dataModel)
430
+
431
+ for (const derived of derivedTypes) {
432
+ const fnName = `as${derived.name}`
433
+ if (processed.has(fnName)) continue
434
+ processed.add(fnName)
435
+
436
+ const castPath = `${dataModel.namespace}.${derived.odataName}`
437
+
438
+ lines.push(`/**`)
439
+ lines.push(` * Cast collection to ${derived.name} (filters to derived type).`)
440
+ lines.push(` *`)
441
+ lines.push(` * @since 1.0.0`)
442
+ lines.push(` * @category casting`)
443
+ lines.push(` */`)
444
+ lines.push(
445
+ `export const ${fnName} = (base: Path<${entityType.name}Model, true>): Path<${derived.name}Model, true> =>`
446
+ )
447
+ lines.push(` \`\${base}/${castPath}\` as Path<${derived.name}Model, true>`)
448
+ lines.push(``)
449
+ }
450
+ }
451
+
452
+ return lines
453
+ }
454
+
455
+ /**
456
+ * Collect all entity type names referenced in paths.
457
+ */
458
+ const collectReferencedTypes = (dataModel: DataModel): Set<string> => {
459
+ const types = new Set<string>()
460
+
461
+ // Entity set entity types
462
+ for (const entitySet of dataModel.entitySets.values()) {
463
+ const entityType = dataModel.entityTypes.get(entitySet.entityTypeFqName)
464
+ if (entityType) {
465
+ types.add(entityType.name)
466
+ }
467
+ }
468
+
469
+ // Navigation target types
470
+ for (const entityType of dataModel.entityTypes.values()) {
471
+ for (const navProp of entityType.navigationProperties) {
472
+ types.add(getClassName(navProp.targetType))
473
+ }
474
+ }
475
+
476
+ // Derived types
477
+ for (const entityType of dataModel.entityTypes.values()) {
478
+ const derivedTypes = getDerivedTypes(entityType.fqName, dataModel)
479
+ for (const derived of derivedTypes) {
480
+ types.add(derived.name)
481
+ }
482
+ }
483
+
484
+ return types
485
+ }