@carlonicora/nestjs-neo4jsonapi 1.31.2 → 1.32.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 (238) hide show
  1. package/dist/common/helpers/define-entity.d.ts +29 -0
  2. package/dist/common/helpers/define-entity.d.ts.map +1 -1
  3. package/dist/common/helpers/define-entity.js +42 -1
  4. package/dist/common/helpers/define-entity.js.map +1 -1
  5. package/dist/common/interfaces/entity.descriptor.interface.d.ts +2 -2
  6. package/dist/common/interfaces/entity.descriptor.interface.d.ts.map +1 -1
  7. package/dist/common/interfaces/entity.descriptor.interface.js +2 -1
  8. package/dist/common/interfaces/entity.descriptor.interface.js.map +1 -1
  9. package/dist/common/interfaces/entity.schema.interface.d.ts +19 -1
  10. package/dist/common/interfaces/entity.schema.interface.d.ts.map +1 -1
  11. package/dist/core/email/templates/en/paymentFailure.hbs +56 -0
  12. package/dist/core/email/templates/en/paymentSuccessAdmin.hbs +31 -0
  13. package/dist/core/email/templates/en/paymentSuccessCustomer.hbs +51 -0
  14. package/dist/core/email/templates/en/subscriptionStatusChange.hbs +48 -0
  15. package/dist/core/jsonapi/serialisers/descriptor.based.serialiser.d.ts.map +1 -1
  16. package/dist/core/jsonapi/serialisers/descriptor.based.serialiser.js +13 -0
  17. package/dist/core/jsonapi/serialisers/descriptor.based.serialiser.js.map +1 -1
  18. package/dist/core/jsonapi/serialisers/jsonapi.paginator.d.ts +3 -0
  19. package/dist/core/jsonapi/serialisers/jsonapi.paginator.d.ts.map +1 -1
  20. package/dist/core/jsonapi/serialisers/jsonapi.paginator.js +6 -0
  21. package/dist/core/jsonapi/serialisers/jsonapi.paginator.js.map +1 -1
  22. package/dist/core/jsonapi/services/jsonapi.service.d.ts.map +1 -1
  23. package/dist/core/jsonapi/services/jsonapi.service.js +6 -0
  24. package/dist/core/jsonapi/services/jsonapi.service.js.map +1 -1
  25. package/dist/core/neo4j/abstracts/abstract.service.d.ts +2 -0
  26. package/dist/core/neo4j/abstracts/abstract.service.d.ts.map +1 -1
  27. package/dist/core/neo4j/abstracts/abstract.service.js +8 -4
  28. package/dist/core/neo4j/abstracts/abstract.service.js.map +1 -1
  29. package/dist/core/neo4j/services/neo4j.service.d.ts +9 -0
  30. package/dist/core/neo4j/services/neo4j.service.d.ts.map +1 -1
  31. package/dist/core/neo4j/services/neo4j.service.js +95 -1
  32. package/dist/core/neo4j/services/neo4j.service.js.map +1 -1
  33. package/dist/foundations/audit/entities/audit.entity.d.ts +1 -1
  34. package/dist/foundations/audit/entities/audit.entity.d.ts.map +1 -1
  35. package/dist/foundations/audit/serialisers/audit.serialiser.js +2 -2
  36. package/dist/foundations/audit/serialisers/audit.serialiser.js.map +1 -1
  37. package/dist/foundations/auth/entities/auth.entity.d.ts +1 -1
  38. package/dist/foundations/auth/entities/auth.entity.d.ts.map +1 -1
  39. package/dist/foundations/auth/repositories/auth.repository.d.ts +1 -1
  40. package/dist/foundations/auth/repositories/auth.repository.d.ts.map +1 -1
  41. package/dist/foundations/auth/repositories/auth.repository.js +31 -13
  42. package/dist/foundations/auth/repositories/auth.repository.js.map +1 -1
  43. package/dist/foundations/auth/serialisers/auth.serialiser.js +2 -2
  44. package/dist/foundations/auth/serialisers/auth.serialiser.js.map +1 -1
  45. package/dist/foundations/auth/services/auth.service.d.ts +1 -1
  46. package/dist/foundations/auth/services/auth.service.d.ts.map +1 -1
  47. package/dist/foundations/company/company.module.d.ts.map +1 -1
  48. package/dist/foundations/company/company.module.js +9 -5
  49. package/dist/foundations/company/company.module.js.map +1 -1
  50. package/dist/foundations/company/entities/company.d.ts +44 -0
  51. package/dist/foundations/company/entities/company.d.ts.map +1 -0
  52. package/dist/foundations/company/entities/company.js +47 -0
  53. package/dist/foundations/company/entities/company.js.map +1 -0
  54. package/dist/foundations/company/entities/company.meta.d.ts +1 -1
  55. package/dist/foundations/company/entities/company.meta.d.ts.map +1 -1
  56. package/dist/foundations/company/index.d.ts +1 -2
  57. package/dist/foundations/company/index.d.ts.map +1 -1
  58. package/dist/foundations/company/index.js +3 -3
  59. package/dist/foundations/company/index.js.map +1 -1
  60. package/dist/foundations/company/repositories/company.repository.d.ts +26 -1
  61. package/dist/foundations/company/repositories/company.repository.d.ts.map +1 -1
  62. package/dist/foundations/company/repositories/company.repository.js +67 -11
  63. package/dist/foundations/company/repositories/company.repository.js.map +1 -1
  64. package/dist/foundations/company/services/company.service.d.ts +4 -1
  65. package/dist/foundations/company/services/company.service.d.ts.map +1 -1
  66. package/dist/foundations/company/services/company.service.js +11 -6
  67. package/dist/foundations/company/services/company.service.js.map +1 -1
  68. package/dist/foundations/content/entities/content.entity.d.ts +1 -1
  69. package/dist/foundations/content/entities/content.entity.d.ts.map +1 -1
  70. package/dist/foundations/content/serialisers/content.serialiser.js +3 -3
  71. package/dist/foundations/content/serialisers/content.serialiser.js.map +1 -1
  72. package/dist/foundations/module/queries/feature.module.query.d.ts +3 -1
  73. package/dist/foundations/module/queries/feature.module.query.d.ts.map +1 -1
  74. package/dist/foundations/module/queries/feature.module.query.js +27 -15
  75. package/dist/foundations/module/queries/feature.module.query.js.map +1 -1
  76. package/dist/foundations/notification/entities/notification.entity.d.ts +1 -1
  77. package/dist/foundations/notification/entities/notification.entity.d.ts.map +1 -1
  78. package/dist/foundations/notification/serialisers/notifications.serialiser.js +2 -2
  79. package/dist/foundations/notification/serialisers/notifications.serialiser.js.map +1 -1
  80. package/dist/foundations/oauth/entities/oauth.client.entity.d.ts +2 -2
  81. package/dist/foundations/oauth/entities/oauth.client.entity.d.ts.map +1 -1
  82. package/dist/foundations/relevancy/repositories/relevancy.repository.d.ts.map +1 -1
  83. package/dist/foundations/relevancy/repositories/relevancy.repository.js +1 -1
  84. package/dist/foundations/relevancy/repositories/relevancy.repository.js.map +1 -1
  85. package/dist/foundations/relevancy/services/relevancy.service.js +1 -1
  86. package/dist/foundations/relevancy/services/relevancy.service.js.map +1 -1
  87. package/dist/foundations/role/entities/role.d.ts +28 -0
  88. package/dist/foundations/role/entities/role.d.ts.map +1 -0
  89. package/dist/foundations/role/entities/role.js +33 -0
  90. package/dist/foundations/role/entities/role.js.map +1 -0
  91. package/dist/foundations/role/entities/role.meta.d.ts +1 -1
  92. package/dist/foundations/role/entities/role.meta.d.ts.map +1 -1
  93. package/dist/foundations/role/repositories/role.repository.d.ts +1 -1
  94. package/dist/foundations/role/repositories/role.repository.d.ts.map +1 -1
  95. package/dist/foundations/role/repositories/role.repository.js +7 -7
  96. package/dist/foundations/role/repositories/role.repository.js.map +1 -1
  97. package/dist/foundations/role/role.module.d.ts.map +1 -1
  98. package/dist/foundations/role/role.module.js +3 -4
  99. package/dist/foundations/role/role.module.js.map +1 -1
  100. package/dist/foundations/role/services/role.service.js +5 -5
  101. package/dist/foundations/role/services/role.service.js.map +1 -1
  102. package/dist/foundations/stripe-customer/entities/stripe-customer.entity.d.ts +1 -1
  103. package/dist/foundations/stripe-customer/entities/stripe-customer.entity.d.ts.map +1 -1
  104. package/dist/foundations/stripe-price/dtos/stripe-price.dto.d.ts +12 -0
  105. package/dist/foundations/stripe-price/dtos/stripe-price.dto.d.ts.map +1 -1
  106. package/dist/foundations/stripe-price/dtos/stripe-price.dto.js +47 -1
  107. package/dist/foundations/stripe-price/dtos/stripe-price.dto.js.map +1 -1
  108. package/dist/foundations/stripe-price/entities/stripe-price.entity.d.ts +2 -0
  109. package/dist/foundations/stripe-price/entities/stripe-price.entity.d.ts.map +1 -1
  110. package/dist/foundations/stripe-price/entities/stripe-price.map.d.ts.map +1 -1
  111. package/dist/foundations/stripe-price/entities/stripe-price.map.js +1 -0
  112. package/dist/foundations/stripe-price/entities/stripe-price.map.js.map +1 -1
  113. package/dist/foundations/stripe-price/entities/stripe-price.model.d.ts.map +1 -1
  114. package/dist/foundations/stripe-price/entities/stripe-price.model.js +2 -1
  115. package/dist/foundations/stripe-price/entities/stripe-price.model.js.map +1 -1
  116. package/dist/foundations/stripe-price/repositories/stripe-price.repository.d.ts +3 -0
  117. package/dist/foundations/stripe-price/repositories/stripe-price.repository.d.ts.map +1 -1
  118. package/dist/foundations/stripe-price/repositories/stripe-price.repository.js +64 -4
  119. package/dist/foundations/stripe-price/repositories/stripe-price.repository.js.map +1 -1
  120. package/dist/foundations/stripe-price/serialisers/stripe-price.serialiser.d.ts.map +1 -1
  121. package/dist/foundations/stripe-price/serialisers/stripe-price.serialiser.js +5 -0
  122. package/dist/foundations/stripe-price/serialisers/stripe-price.serialiser.js.map +1 -1
  123. package/dist/foundations/stripe-price/services/stripe-price-admin.service.d.ts.map +1 -1
  124. package/dist/foundations/stripe-price/services/stripe-price-admin.service.js +10 -0
  125. package/dist/foundations/stripe-price/services/stripe-price-admin.service.js.map +1 -1
  126. package/dist/foundations/stripe-subscription/repositories/stripe-subscription.repository.d.ts +11 -0
  127. package/dist/foundations/stripe-subscription/repositories/stripe-subscription.repository.d.ts.map +1 -1
  128. package/dist/foundations/stripe-subscription/repositories/stripe-subscription.repository.js +22 -0
  129. package/dist/foundations/stripe-subscription/repositories/stripe-subscription.repository.js.map +1 -1
  130. package/dist/foundations/stripe-subscription/services/feature-sync.service.d.ts +33 -0
  131. package/dist/foundations/stripe-subscription/services/feature-sync.service.d.ts.map +1 -0
  132. package/dist/foundations/stripe-subscription/services/feature-sync.service.js +155 -0
  133. package/dist/foundations/stripe-subscription/services/feature-sync.service.js.map +1 -0
  134. package/dist/foundations/stripe-subscription/services/stripe-subscription-admin.service.d.ts.map +1 -1
  135. package/dist/foundations/stripe-subscription/services/stripe-subscription-admin.service.js +11 -0
  136. package/dist/foundations/stripe-subscription/services/stripe-subscription-admin.service.js.map +1 -1
  137. package/dist/foundations/stripe-subscription/stripe-subscription.module.d.ts.map +1 -1
  138. package/dist/foundations/stripe-subscription/stripe-subscription.module.js +3 -0
  139. package/dist/foundations/stripe-subscription/stripe-subscription.module.js.map +1 -1
  140. package/dist/foundations/stripe-webhook/processors/stripe-webhook.processor.d.ts +3 -1
  141. package/dist/foundations/stripe-webhook/processors/stripe-webhook.processor.d.ts.map +1 -1
  142. package/dist/foundations/stripe-webhook/processors/stripe-webhook.processor.js +79 -9
  143. package/dist/foundations/stripe-webhook/processors/stripe-webhook.processor.js.map +1 -1
  144. package/dist/foundations/stripe-webhook/services/stripe-webhook-notification.service.d.ts +26 -1
  145. package/dist/foundations/stripe-webhook/services/stripe-webhook-notification.service.d.ts.map +1 -1
  146. package/dist/foundations/stripe-webhook/services/stripe-webhook-notification.service.js +124 -2
  147. package/dist/foundations/stripe-webhook/services/stripe-webhook-notification.service.js.map +1 -1
  148. package/dist/foundations/stripe-webhook/stripe-webhook.module.d.ts.map +1 -1
  149. package/dist/foundations/stripe-webhook/stripe-webhook.module.js +2 -0
  150. package/dist/foundations/stripe-webhook/stripe-webhook.module.js.map +1 -1
  151. package/dist/foundations/tokenusage/entities/tokenusage.entity.d.ts +1 -1
  152. package/dist/foundations/tokenusage/entities/tokenusage.entity.d.ts.map +1 -1
  153. package/dist/foundations/user/controllers/user.controller.d.ts +1 -1
  154. package/dist/foundations/user/controllers/user.controller.d.ts.map +1 -1
  155. package/dist/foundations/user/entities/user.d.ts +124 -0
  156. package/dist/foundations/user/entities/user.d.ts.map +1 -0
  157. package/dist/foundations/user/entities/user.js +81 -0
  158. package/dist/foundations/user/entities/user.js.map +1 -0
  159. package/dist/foundations/user/entities/user.meta.d.ts +1 -1
  160. package/dist/foundations/user/entities/user.meta.d.ts.map +1 -1
  161. package/dist/foundations/user/index.d.ts +1 -4
  162. package/dist/foundations/user/index.d.ts.map +1 -1
  163. package/dist/foundations/user/index.js +6 -23
  164. package/dist/foundations/user/index.js.map +1 -1
  165. package/dist/foundations/user/repositories/user.repository.d.ts +13 -1
  166. package/dist/foundations/user/repositories/user.repository.d.ts.map +1 -1
  167. package/dist/foundations/user/repositories/user.repository.js +85 -21
  168. package/dist/foundations/user/repositories/user.repository.js.map +1 -1
  169. package/dist/foundations/user/services/user.service.d.ts.map +1 -1
  170. package/dist/foundations/user/services/user.service.js +12 -12
  171. package/dist/foundations/user/services/user.service.js.map +1 -1
  172. package/dist/foundations/user/user.module.d.ts.map +1 -1
  173. package/dist/foundations/user/user.module.js +7 -8
  174. package/dist/foundations/user/user.module.js.map +1 -1
  175. package/dist/openapi/factories/entity-schema.factory.js +12 -0
  176. package/dist/openapi/factories/entity-schema.factory.js.map +1 -1
  177. package/dist/openapi/schemas/jsonapi-base.schemas.d.ts +1 -0
  178. package/dist/openapi/schemas/jsonapi-base.schemas.d.ts.map +1 -1
  179. package/dist/openapi/schemas/jsonapi-base.schemas.js.map +1 -1
  180. package/dist/tools/migrate-entity/__tests__/ast-parser.spec.js +331 -0
  181. package/dist/tools/migrate-entity/__tests__/descriptor-generator.spec.js +482 -0
  182. package/dist/tools/migrate-entity/__tests__/file-discovery.spec.js +190 -0
  183. package/dist/tools/migrate-entity/__tests__/integration.spec.js +261 -0
  184. package/dist/tools/migrate-entity/index.js +2 -2
  185. package/dist/tools/migrate-entity/lib/ast-parser.js +255 -10
  186. package/dist/tools/migrate-entity/lib/descriptor-generator.js +384 -55
  187. package/dist/tools/migrate-entity/lib/entity-migrator.js +112 -9
  188. package/dist/tools/migrate-entity/lib/module-updater.js +25 -9
  189. package/dist/tools/migrate-entity/lib/reference-updater.js +110 -24
  190. package/package.json +2 -2
  191. package/dist/foundations/company/entities/company.entity.d.ts +0 -18
  192. package/dist/foundations/company/entities/company.entity.d.ts.map +0 -1
  193. package/dist/foundations/company/entities/company.entity.js +0 -3
  194. package/dist/foundations/company/entities/company.entity.js.map +0 -1
  195. package/dist/foundations/company/entities/company.map.d.ts +0 -8
  196. package/dist/foundations/company/entities/company.map.d.ts.map +0 -1
  197. package/dist/foundations/company/entities/company.map.js +0 -23
  198. package/dist/foundations/company/entities/company.map.js.map +0 -1
  199. package/dist/foundations/company/entities/company.model.d.ts +0 -4
  200. package/dist/foundations/company/entities/company.model.d.ts.map +0 -1
  201. package/dist/foundations/company/entities/company.model.js +0 -17
  202. package/dist/foundations/company/entities/company.model.js.map +0 -1
  203. package/dist/foundations/company/serialisers/company.serialiser.d.ts +0 -14
  204. package/dist/foundations/company/serialisers/company.serialiser.d.ts.map +0 -1
  205. package/dist/foundations/company/serialisers/company.serialiser.js +0 -76
  206. package/dist/foundations/company/serialisers/company.serialiser.js.map +0 -1
  207. package/dist/foundations/role/entities/role.entity.d.ts +0 -9
  208. package/dist/foundations/role/entities/role.entity.d.ts.map +0 -1
  209. package/dist/foundations/role/entities/role.entity.js +0 -3
  210. package/dist/foundations/role/entities/role.entity.js.map +0 -1
  211. package/dist/foundations/role/entities/role.map.d.ts +0 -8
  212. package/dist/foundations/role/entities/role.map.d.ts.map +0 -1
  213. package/dist/foundations/role/entities/role.map.js +0 -15
  214. package/dist/foundations/role/entities/role.map.js.map +0 -1
  215. package/dist/foundations/role/entities/role.model.d.ts +0 -4
  216. package/dist/foundations/role/entities/role.model.d.ts.map +0 -1
  217. package/dist/foundations/role/entities/role.model.js +0 -15
  218. package/dist/foundations/role/entities/role.model.js.map +0 -1
  219. package/dist/foundations/role/serialisers/role.serialiser.d.ts +0 -12
  220. package/dist/foundations/role/serialisers/role.serialiser.d.ts.map +0 -1
  221. package/dist/foundations/role/serialisers/role.serialiser.js +0 -46
  222. package/dist/foundations/role/serialisers/role.serialiser.js.map +0 -1
  223. package/dist/foundations/user/entities/user.entity.d.ts +0 -26
  224. package/dist/foundations/user/entities/user.entity.d.ts.map +0 -1
  225. package/dist/foundations/user/entities/user.entity.js +0 -3
  226. package/dist/foundations/user/entities/user.entity.js.map +0 -1
  227. package/dist/foundations/user/entities/user.map.d.ts +0 -8
  228. package/dist/foundations/user/entities/user.map.d.ts.map +0 -1
  229. package/dist/foundations/user/entities/user.map.js +0 -27
  230. package/dist/foundations/user/entities/user.map.js.map +0 -1
  231. package/dist/foundations/user/entities/user.model.d.ts +0 -7
  232. package/dist/foundations/user/entities/user.model.d.ts.map +0 -1
  233. package/dist/foundations/user/entities/user.model.js +0 -30
  234. package/dist/foundations/user/entities/user.model.js.map +0 -1
  235. package/dist/foundations/user/serialisers/user.serialiser.d.ts +0 -14
  236. package/dist/foundations/user/serialisers/user.serialiser.d.ts.map +0 -1
  237. package/dist/foundations/user/serialisers/user.serialiser.js +0 -80
  238. package/dist/foundations/user/serialisers/user.serialiser.js.map +0 -1
@@ -25,22 +25,52 @@ function generateDescriptor(parsed, entityDir, options = {}) {
25
25
  }
26
26
  }
27
27
  }
28
- // Build field configs
29
- const fields = buildFieldConfigs(entityType, serialiser, mapper);
30
- // Build computed configs
31
- const computed = buildComputedConfigs(mapper, serialiser);
28
+ // Get S3 transforms from serialiser
29
+ const s3Transforms = serialiser?.s3Transforms || [];
30
+ // Build field configs with S3 transforms applied
31
+ const fields = buildFieldConfigs(entityType, serialiser, mapper, s3Transforms);
32
+ // Build computed configs and virtual fields
33
+ const { computed, virtualFields } = buildComputedConfigs(mapper, serialiser, entityType);
32
34
  // Build relationship configs using Cypher relationships when available
33
35
  const relationships = buildRelationshipConfigs(serialiser, cypherRelationships, options.verbose);
36
+ // Determine which services to include (only include S3Service if transforms are used)
37
+ const rawServices = serialiser?.services || [];
38
+ const services = s3Transforms.length > 0 ? rawServices : rawServices.filter((s) => s !== "S3Service");
39
+ // Log S3 transforms if verbose
40
+ if (options.verbose && s3Transforms.length > 0) {
41
+ console.log(` Auto-generated ${s3Transforms.length} S3 transform(s):`);
42
+ for (const transform of s3Transforms) {
43
+ console.log(` - ${transform.fieldName}: ${transform.isArray ? "array" : "single"} URL signing`);
44
+ }
45
+ }
46
+ // Warn about custom methods that may need manual migration (skip S3-related methods if transforms were generated)
47
+ const s3TransformFields = new Set(s3Transforms.map((t) => t.fieldName));
48
+ const s3RelatedMethods = ["getSignedUrl", "getSignedUrls", "generateSignedUrl", "signUrl", "signUrls"];
49
+ if (serialiser?.customMethods && serialiser.customMethods.length > 0) {
50
+ const unmigratableMethods = serialiser.customMethods.filter((method) => {
51
+ // Skip S3-related methods if we have transforms for URL fields
52
+ if (s3Transforms.length > 0 && s3RelatedMethods.some((s3m) => method.toLowerCase().includes(s3m.toLowerCase()))) {
53
+ return false;
54
+ }
55
+ return true;
56
+ });
57
+ if (unmigratableMethods.length > 0) {
58
+ console.warn(` ⚠️ Serialiser has custom methods that may need manual migration:`);
59
+ for (const method of unmigratableMethods) {
60
+ console.warn(` - ${method}() → Consider adding field transform`);
61
+ }
62
+ }
63
+ }
34
64
  // Generate imports (pass entityName for correct file path in self-meta import)
35
- const imports = generateImports(parsed, relationships, entityDir, options.entityName);
65
+ const imports = generateImports(parsed, relationships, entityDir, options.entityName, services);
36
66
  // Generate the descriptor code
37
- const code = generateDescriptorCode(meta, entityType.name, fields, computed, relationships);
67
+ const code = generateDescriptorCode(meta, entityType.name, fields, computed, virtualFields, relationships, services);
38
68
  return { code, imports };
39
69
  }
40
70
  /**
41
71
  * Builds field configurations from entity type, serialiser, and mapper.
42
72
  */
43
- function buildFieldConfigs(entityType, serialiser, mapper) {
73
+ function buildFieldConfigs(entityType, serialiser, mapper, s3Transforms = []) {
44
74
  const fields = [];
45
75
  // Get attribute names from serialiser (these are JSON:API attributes)
46
76
  const attributeNames = new Set(serialiser?.attributes.map((a) => a.name) || []);
@@ -48,6 +78,8 @@ function buildFieldConfigs(entityType, serialiser, mapper) {
48
78
  const metaNames = new Set(serialiser?.meta.map((m) => m.name) || []);
49
79
  // Get mapper field names for mapping
50
80
  const mapperFields = new Map(mapper?.fields.filter((f) => !f.isComputed).map((f) => [f.name, f]) || []);
81
+ // Create a map of S3 transforms by field name
82
+ const s3TransformMap = new Map(s3Transforms.map((t) => [t.fieldName, t]));
51
83
  // Process entity type fields
52
84
  for (const field of entityType.fields) {
53
85
  // Skip if this is a computed field (handled separately)
@@ -63,33 +95,98 @@ function buildFieldConfigs(entityType, serialiser, mapper) {
63
95
  const cypherType = mapToCypherType(field.type);
64
96
  // Check for default values (e.g., aiStatus)
65
97
  const defaultValue = getDefaultValue(field.name, mapper);
98
+ // Check for S3 transforms
99
+ const s3Transform = s3TransformMap.get(field.name);
100
+ const transform = s3Transform ? generateS3TransformCode(field.name, s3Transform.isArray) : undefined;
66
101
  fields.push({
67
102
  name: field.name,
68
103
  type: cypherType,
69
104
  required: isRequired,
70
105
  default: defaultValue,
71
106
  meta: isMeta,
107
+ transform,
72
108
  });
73
109
  }
74
110
  return fields;
75
111
  }
76
112
  /**
77
- * Builds computed field configurations from mapper.
113
+ * Generates S3 transform function code for a field.
114
+ *
115
+ * Handles the `~` prefix convention: URLs starting with `~` are already
116
+ * signed or external URLs that should be returned as-is (without the prefix).
117
+ */
118
+ function generateS3TransformCode(fieldName, isArray) {
119
+ if (isArray) {
120
+ // Array URL transform with ~ prefix handling
121
+ return `async (data, services) => {
122
+ if (!data.${fieldName}?.length) return [];
123
+ return Promise.all(
124
+ data.${fieldName}.map(async (url: string) => {
125
+ if (url.startsWith("~")) return url.substring(1);
126
+ return services.S3Service.generateSignedUrl({ key: url, isPublic: true });
127
+ }),
128
+ );
129
+ }`;
130
+ }
131
+ else {
132
+ // Single URL transform with ~ prefix handling
133
+ return `async (data, services) => {
134
+ if (!data.${fieldName}) return undefined;
135
+ if (data.${fieldName}.startsWith("~")) return data.${fieldName}.substring(1);
136
+ return await services.S3Service.generateSignedUrl({ key: data.${fieldName}, isPublic: true });
137
+ }`;
138
+ }
139
+ }
140
+ /**
141
+ * Builds computed field configurations and virtual fields from mapper and serialiser.
142
+ *
143
+ * Includes:
144
+ * 1. Mapper-based computed fields (e.g., params.record.has patterns)
145
+ * 2. Virtual fields: Serialiser attributes where name differs from mapping (e.g., avatarUrl -> avatar)
146
+ * These are output-only fields that don't exist in the entity type.
78
147
  */
79
- function buildComputedConfigs(mapper, serialiser) {
148
+ function buildComputedConfigs(mapper, serialiser, entityType) {
80
149
  const computed = [];
81
- if (!mapper)
82
- return computed;
150
+ const virtualFields = [];
151
+ const addedNames = new Set();
83
152
  // Get meta names from serialiser
84
153
  const metaNames = new Set(serialiser?.meta.map((m) => m.name) || []);
85
- for (const field of mapper.fields.filter((f) => f.isComputed)) {
86
- computed.push({
87
- name: field.name,
88
- compute: field.mapping,
89
- meta: metaNames.has(field.name),
90
- });
154
+ // Get entity field names (to distinguish computed-only fields)
155
+ const entityFieldNames = new Set(entityType.fields.map((f) => f.name));
156
+ // 1. Add mapper-based computed fields
157
+ if (mapper) {
158
+ for (const field of mapper.fields.filter((f) => f.isComputed)) {
159
+ computed.push({
160
+ name: field.name,
161
+ compute: field.mapping,
162
+ meta: metaNames.has(field.name),
163
+ });
164
+ addedNames.add(field.name);
165
+ }
91
166
  }
92
- return computed;
167
+ // 2. Serialiser attributes where name != mapping AND name not in entity type
168
+ // These are output-only fields like `avatarUrl` that return `data.avatar`.
169
+ // We generate them as virtualFields (new feature in defineEntity).
170
+ if (serialiser) {
171
+ for (const attr of serialiser.attributes) {
172
+ // Skip if already added from mapper
173
+ if (addedNames.has(attr.name))
174
+ continue;
175
+ // Skip if this field exists in entity type (it's a regular field, not computed)
176
+ if (entityFieldNames.has(attr.name))
177
+ continue;
178
+ // If the serialiser attribute name differs from its mapping, it's a virtual field
179
+ if (attr.name !== attr.mapping) {
180
+ virtualFields.push({
181
+ name: attr.name,
182
+ compute: `params.data.${attr.mapping}`,
183
+ meta: metaNames.has(attr.name),
184
+ });
185
+ addedNames.add(attr.name);
186
+ }
187
+ }
188
+ }
189
+ return { computed, virtualFields };
93
190
  }
94
191
  /**
95
192
  * Builds relationship configurations from serialiser, using Cypher relationships when available.
@@ -147,13 +244,27 @@ function buildRelationshipConfigs(serialiser, cypherRelationships = [], verbose)
147
244
  }
148
245
  /**
149
246
  * Maps TypeScript type to Cypher type.
247
+ * Handles both scalar types and array types.
150
248
  */
151
249
  function mapToCypherType(tsType) {
250
+ // Handle array types first (e.g., string[], number[])
251
+ if (tsType.endsWith("[]")) {
252
+ const baseType = tsType.slice(0, -2);
253
+ const mappedBase = mapScalarToCypherType(baseType);
254
+ return `${mappedBase}[]`;
255
+ }
256
+ return mapScalarToCypherType(tsType);
257
+ }
258
+ /**
259
+ * Maps a scalar TypeScript type to Cypher type.
260
+ */
261
+ function mapScalarToCypherType(tsType) {
152
262
  const typeMap = {
153
263
  string: "string",
154
264
  number: "number",
155
265
  boolean: "boolean",
156
- Date: "date",
266
+ Date: "datetime",
267
+ object: "json",
157
268
  };
158
269
  return typeMap[tsType] || "string";
159
270
  }
@@ -168,15 +279,20 @@ function getDefaultValue(fieldName, mapper) {
168
279
  return knownDefaults[fieldName];
169
280
  }
170
281
  /**
171
- * Converts Model import name to meta variable name.
282
+ * Converts Model/Descriptor import name to model reference.
283
+ * Returns either a meta variable name or a Descriptor.model reference.
172
284
  */
173
285
  function modelToMetaName(relName, modelImport) {
174
- // Special cases
286
+ // Special cases for framework-provided relationships
175
287
  if (relName === "author")
176
288
  return "authorMeta";
177
289
  if (relName === "user" || relName === "editors")
178
290
  return "userMeta";
179
- // General case: UserModel -> userMeta
291
+ // Already migrated entity: CompanyDescriptor -> CompanyDescriptor.model
292
+ if (modelImport.endsWith("Descriptor")) {
293
+ return `${modelImport}.model`;
294
+ }
295
+ // Old pattern: UserModel -> userMeta
180
296
  const baseName = modelImport.replace("Model", "").toLowerCase();
181
297
  return `${baseName}Meta`;
182
298
  }
@@ -203,10 +319,23 @@ function inferRelationshipDetails(relName) {
203
319
  * - Group 3: Entity-specific imports (src/features/...)
204
320
  * - Group 4: Self-meta import (relative path)
205
321
  */
206
- function generateImports(parsed, relationships, entityDir, entityName) {
322
+ function generateImports(parsed, relationships, entityDir, entityName, services = []) {
207
323
  const { meta } = parsed;
208
324
  // Collect all framework imports into a single barrel import
209
325
  const frameworkImports = new Set(["defineEntity", "Entity"]);
326
+ // Add service imports
327
+ // Note: S3Service is NOT exported from common, it has its own foundation module
328
+ const serviceImports = [];
329
+ for (const service of services) {
330
+ if (service === "S3Service") {
331
+ // S3Service comes from its own module
332
+ serviceImports.push(`import { S3Service } from "../../s3";`);
333
+ }
334
+ else {
335
+ // Other services come from common
336
+ frameworkImports.add(service);
337
+ }
338
+ }
210
339
  // Check if AiStatus is needed
211
340
  const hasAiStatus = parsed.entityType.fields.some((f) => f.name === "aiStatus");
212
341
  if (hasAiStatus) {
@@ -216,38 +345,85 @@ function generateImports(parsed, relationships, entityDir, entityName) {
216
345
  const externalImports = [];
217
346
  const featureImports = [];
218
347
  for (const rel of relationships) {
219
- const metaName = rel.model;
220
- const baseName = metaName.replace("Meta", "");
221
- // Framework-provided metas (user, author, owner, company)
222
- if (["user", "author", "owner", "company"].includes(baseName)) {
223
- frameworkImports.add(metaName);
224
- // Also add the type if it's a relationship field type
225
- const typeName = baseName.charAt(0).toUpperCase() + baseName.slice(1);
226
- if (parsed.entityType.relationshipFields.some((f) => f.type.includes(typeName))) {
227
- frameworkImports.add(typeName);
348
+ const modelRef = rel.model;
349
+ // Already-migrated entity: CompanyDescriptor.model -> need to import CompanyDescriptor
350
+ if (modelRef.endsWith(".model")) {
351
+ const descriptorName = modelRef.replace(".model", "");
352
+ const descriptorImport = findDescriptorImport(parsed, descriptorName);
353
+ if (descriptorImport) {
354
+ featureImports.push(descriptorImport);
228
355
  }
356
+ continue;
357
+ }
358
+ // Meta-based reference: xxxMeta
359
+ const baseName = modelRef.replace("Meta", "");
360
+ // Known foundation metas and their import paths
361
+ const foundationMetaPaths = {
362
+ user: "../../user/entities/user.meta",
363
+ author: "../../user/entities/user.meta",
364
+ owner: "../../user/entities/user.meta",
365
+ assignee: "../../user/entities/user.meta",
366
+ company: "../../company/entities/company.meta",
367
+ };
368
+ if (foundationMetaPaths[baseName]) {
369
+ // Foundation-provided metas: import from their meta files
370
+ featureImports.push(`import { ${modelRef} } from "${foundationMetaPaths[baseName]}";`);
229
371
  }
230
372
  else {
231
373
  // Feature-specific metas need separate imports
232
- const metaImport = findMetaImport(parsed, metaName);
374
+ const metaImport = findMetaImport(parsed, modelRef);
233
375
  if (metaImport) {
234
376
  featureImports.push(metaImport);
235
377
  }
236
378
  }
237
379
  }
238
380
  // Add relationship type imports from entity type definition
381
+ // Collect types that need imports from original entity or foundation paths
382
+ const relationshipTypeImports = [];
383
+ // Known foundation entity types and their import paths
384
+ const foundationEntityPaths = {
385
+ Company: "../../company/entities/company",
386
+ User: "../../user/entities/user",
387
+ // Alias types map to User
388
+ Author: "../../user/entities/user",
389
+ Owner: "../../user/entities/user",
390
+ Assignee: "../../user/entities/user",
391
+ };
239
392
  for (const field of parsed.entityType.relationshipFields) {
240
393
  const typeName = field.type.replace("[]", "");
241
- // Add framework types (Company, User)
242
- if (["Company", "User"].includes(typeName)) {
243
- frameworkImports.add(typeName);
394
+ // For known foundation types, generate type-only imports
395
+ if (foundationEntityPaths[typeName]) {
396
+ const importPath = foundationEntityPaths[typeName];
397
+ relationshipTypeImports.push(`import type { ${typeName} } from "${importPath}";`);
398
+ continue;
399
+ }
400
+ // For all other relationship types, find the original import from entity
401
+ // Use type-only imports to avoid circular dependencies
402
+ for (const imp of parsed.entityType.imports) {
403
+ // Skip imports from the package barrel - these should use internal paths
404
+ if (imp.includes("@carlonicora/nestjs-neo4jsonapi")) {
405
+ continue;
406
+ }
407
+ // Match imports like "import { Feature } from ..." or "import { Module } from ..."
408
+ if (imp.includes(`{ ${typeName} }`) || imp.includes(`{ ${typeName},`) || imp.includes(`, ${typeName} }`)) {
409
+ // Convert to type-only import
410
+ const typeImport = imp.replace(/^import\s+\{/, "import type {");
411
+ relationshipTypeImports.push(typeImport);
412
+ break;
413
+ }
244
414
  }
245
415
  }
246
416
  // Build the imports array
247
417
  const imports = [];
248
418
  // Group 1: Single barrel import from framework
419
+ // Use internal imports (../../../common) to avoid circular dependency when the package
420
+ // barrel re-exports from ./foundations which tries to load this entity again
249
421
  const frameworkImportList = Array.from(frameworkImports).sort();
250
- imports.push(`import {\n ${frameworkImportList.join(",\n ")},\n} from "@carlonicora/nestjs-neo4jsonapi";`);
422
+ imports.push(`import {\n ${frameworkImportList.join(",\n ")},\n} from "../../../common";`);
423
+ // Group 1b: Service imports that have their own modules (e.g., S3Service)
424
+ if (serviceImports.length > 0) {
425
+ imports.push(...serviceImports);
426
+ }
251
427
  // Group 2: External type imports (from original entity imports that aren't framework)
252
428
  for (const imp of parsed.entityType.imports) {
253
429
  if (imp.includes("@only35/shared") || (imp.includes("@") && !imp.includes("@carlonicora/nestjs-neo4jsonapi"))) {
@@ -257,7 +433,13 @@ function generateImports(parsed, relationships, entityDir, entityName) {
257
433
  if (externalImports.length > 0) {
258
434
  imports.push(...[...new Set(externalImports)]);
259
435
  }
260
- // Group 3: Entity-specific imports (src/features/...)
436
+ // Group 3: Relationship type imports (Feature, Module, etc.) from original entity
437
+ // Use "import type" to avoid runtime circular dependencies - these are only needed for TypeScript types
438
+ if (relationshipTypeImports.length > 0) {
439
+ const typeOnlyImports = [...new Set(relationshipTypeImports)].map((imp) => imp.startsWith("import type ") ? imp : imp.replace(/^import\s+/, "import type "));
440
+ imports.push(...typeOnlyImports);
441
+ }
442
+ // Group 4: Entity-specific imports (src/features/...)
261
443
  for (const imp of parsed.entityType.imports) {
262
444
  if (imp.includes("src/features/") && !imp.includes(".meta")) {
263
445
  featureImports.push(imp);
@@ -274,9 +456,10 @@ function generateImports(parsed, relationships, entityDir, entityName) {
274
456
  }
275
457
  /**
276
458
  * Finds the meta import statement for a given meta name.
459
+ * Derives the path from serialiser model imports by converting .model to .meta
277
460
  */
278
461
  function findMetaImport(parsed, metaName) {
279
- // Check serialiser imports
462
+ // Check if serialiser already imports this meta directly
280
463
  if (parsed.serialiser) {
281
464
  for (const imp of parsed.serialiser.imports) {
282
465
  if (imp.includes(`.meta"`) || imp.includes(`.meta'`)) {
@@ -292,26 +475,77 @@ function findMetaImport(parsed, metaName) {
292
475
  }
293
476
  }
294
477
  }
295
- // Fallback: construct import from meta name
296
478
  const baseName = metaName.replace("Meta", "");
297
- if (baseName === "author" || baseName === "user") {
479
+ // Framework-provided metas
480
+ if (["author", "user", "company"].includes(baseName)) {
298
481
  return `import { ${metaName} } from "@carlonicora/nestjs-neo4jsonapi";`;
299
482
  }
483
+ // Try to derive meta import from serialiser model imports
484
+ // e.g., FeatureModel from ".../feature.model" -> featureMeta from ".../feature.meta"
485
+ if (parsed.serialiser) {
486
+ const modelName = baseName.charAt(0).toUpperCase() + baseName.slice(1) + "Model";
487
+ for (const imp of parsed.serialiser.imports) {
488
+ if (imp.includes(modelName)) {
489
+ // Extract path and convert to meta path
490
+ const pathMatch = imp.match(/from\s+["']([^"']+)["']/);
491
+ if (pathMatch) {
492
+ const modelPath = pathMatch[1];
493
+ const metaPath = modelPath.replace(/\.model$/, ".meta");
494
+ return `import { ${metaName} } from "${metaPath}";`;
495
+ }
496
+ }
497
+ }
498
+ }
499
+ // Last resort: construct path heuristically from known foundations structure
300
500
  if (baseName === "topic") {
301
501
  return `import { ${metaName} } from "src/features/topic/entities/topic.meta";`;
302
502
  }
303
503
  if (baseName === "expertise") {
304
504
  return `import { ${metaName} } from "src/features/expertise/entities/expertise.meta";`;
305
505
  }
306
- if (baseName === "company") {
307
- return `import { ${metaName} } from "@carlonicora/nestjs-neo4jsonapi";`;
506
+ if (baseName === "feature") {
507
+ return `import { ${metaName} } from "../../feature/entities/feature.meta";`;
508
+ }
509
+ if (baseName === "module") {
510
+ return `import { ${metaName} } from "../../module/entities/module.meta";`;
511
+ }
512
+ if (baseName === "role") {
513
+ return `import { ${metaName} } from "../../role/entities/role.meta";`;
514
+ }
515
+ if (baseName === "configuration") {
516
+ return `import { ${metaName} } from "../../configuration/entities/configuration.meta";`;
517
+ }
518
+ if (baseName === "referencerole") {
519
+ return `import { ${metaName} } from "../../reference-role/entities/reference.role.meta";`;
520
+ }
521
+ // Generic fallback: assume foundation-style path
522
+ return `import { ${metaName} } from "../../${baseName}/entities/${baseName}.meta";`;
523
+ }
524
+ /**
525
+ * Finds the descriptor import statement for an already-migrated entity.
526
+ * Looks for existing imports like: import { CompanyDescriptor } from "../../company/entities/company";
527
+ */
528
+ function findDescriptorImport(parsed, descriptorName) {
529
+ // Check if serialiser already imports this descriptor
530
+ if (parsed.serialiser) {
531
+ for (const imp of parsed.serialiser.imports) {
532
+ if (imp.includes(descriptorName)) {
533
+ // Extract the path and return just this descriptor import
534
+ const pathMatch = imp.match(/from\s+["']([^"']+)["']/);
535
+ if (pathMatch) {
536
+ return `import { ${descriptorName} } from "${pathMatch[1]}";`;
537
+ }
538
+ }
539
+ }
308
540
  }
309
- return null;
541
+ // Derive path from descriptor name (e.g., CompanyDescriptor -> ../../company/entities/company)
542
+ const baseName = descriptorName.replace("Descriptor", "").toLowerCase();
543
+ return `import { ${descriptorName} } from "../../${baseName}/entities/${baseName}";`;
310
544
  }
311
545
  /**
312
546
  * Generates the complete descriptor code.
313
547
  */
314
- function generateDescriptorCode(meta, entityName, fields, computed, relationships) {
548
+ function generateDescriptorCode(meta, entityName, fields, computed, virtualFields, relationships, services = []) {
315
549
  const descriptorName = `${meta.labelName}Descriptor`;
316
550
  const metaName = `${meta.nodeName}Meta`;
317
551
  let code = `
@@ -324,6 +558,12 @@ function generateDescriptorCode(meta, entityName, fields, computed, relationship
324
558
  export const ${descriptorName} = defineEntity<${entityName}>()({
325
559
  ...${metaName},
326
560
  `;
561
+ // Add injectServices if services exist
562
+ if (services.length > 0) {
563
+ code += `
564
+ injectServices: [${services.join(", ")}],
565
+ `;
566
+ }
327
567
  // Add fields
328
568
  if (fields.length > 0) {
329
569
  code += `
@@ -331,14 +571,30 @@ export const ${descriptorName} = defineEntity<${entityName}>()({
331
571
  fields: {
332
572
  `;
333
573
  for (const field of fields) {
334
- code += ` ${field.name}: { type: "${field.type}"`;
335
- if (field.required)
336
- code += `, required: true`;
337
- if (field.default)
338
- code += `, default: ${field.default}`;
339
- if (field.meta)
340
- code += `, meta: true`;
341
- code += ` },\n`;
574
+ if (field.transform) {
575
+ // Multi-line format for fields with transforms
576
+ code += ` ${field.name}: {\n`;
577
+ code += ` type: "${field.type}",\n`;
578
+ if (field.required)
579
+ code += ` required: true,\n`;
580
+ if (field.default)
581
+ code += ` default: ${field.default},\n`;
582
+ if (field.meta)
583
+ code += ` meta: true,\n`;
584
+ code += ` transform: ${field.transform},\n`;
585
+ code += ` },\n`;
586
+ }
587
+ else {
588
+ // Single-line format for simple fields
589
+ code += ` ${field.name}: { type: "${field.type}"`;
590
+ if (field.required)
591
+ code += `, required: true`;
592
+ if (field.default)
593
+ code += `, default: ${field.default}`;
594
+ if (field.meta)
595
+ code += `, meta: true`;
596
+ code += ` },\n`;
597
+ }
342
598
  }
343
599
  code += ` },
344
600
  `;
@@ -357,6 +613,22 @@ export const ${descriptorName} = defineEntity<${entityName}>()({
357
613
  code += ` },\n`;
358
614
  }
359
615
  code += ` },
616
+ `;
617
+ }
618
+ // Add virtual fields (output-only computed values with arbitrary names)
619
+ if (virtualFields.length > 0) {
620
+ code += `
621
+ // Virtual fields (output-only, not in entity type)
622
+ virtualFields: {
623
+ `;
624
+ for (const vf of virtualFields) {
625
+ code += ` ${vf.name}: {\n`;
626
+ code += ` compute: (params) => ${vf.compute},\n`;
627
+ if (vf.meta)
628
+ code += ` meta: true,\n`;
629
+ code += ` },\n`;
630
+ }
631
+ code += ` },
360
632
  `;
361
633
  }
362
634
  // Add relationships
@@ -371,10 +643,23 @@ export const ${descriptorName} = defineEntity<${entityName}>()({
371
643
  code += ` direction: "${rel.direction}",\n`;
372
644
  code += ` relationship: "${rel.relationship}",\n`;
373
645
  code += ` cardinality: "${rel.cardinality}",\n`;
646
+ if (rel.required === false)
647
+ code += ` required: false,\n`;
374
648
  if (rel.contextKey)
375
649
  code += ` contextKey: "${rel.contextKey}",\n`;
376
650
  if (rel.dtoKey)
377
651
  code += ` dtoKey: "${rel.dtoKey}",\n`;
652
+ // Add edge fields if present
653
+ if (rel.fields && rel.fields.length > 0) {
654
+ code += ` fields: [\n`;
655
+ for (const field of rel.fields) {
656
+ code += ` { name: "${field.name}", type: "${field.type}"`;
657
+ if (field.required)
658
+ code += `, required: true`;
659
+ code += ` },\n`;
660
+ }
661
+ code += ` ],\n`;
662
+ }
378
663
  code += ` },\n`;
379
664
  }
380
665
  code += ` },
@@ -413,15 +698,59 @@ export const ${metaName}: DataMeta = {
413
698
  */
414
699
  function generateEntityFile(parsed, entityDir, options = {}) {
415
700
  const descriptor = generateDescriptor(parsed, entityDir, options);
416
- // Start with imports
417
- let content = descriptor.imports.join("\n") + "\n\n";
701
+ // Start with imports - extend with alias-related imports
702
+ const imports = [...descriptor.imports];
703
+ if (parsed.aliasModels.length > 0) {
704
+ // Add defineEntityAlias to the framework imports (already in first import line)
705
+ const firstImport = imports[0];
706
+ if (firstImport && firstImport.includes("defineEntity")) {
707
+ imports[0] = firstImport.replace("defineEntity,", "defineEntity,\n defineEntityAlias,");
708
+ }
709
+ // Add alias meta imports to the self-meta import line
710
+ // The self-meta import is the one that imports from "./{entityName}.meta" (the last import added by generateImports)
711
+ const selfMetaPattern = new RegExp(`from\\s+["']\\.\\/[^"']+\\.meta["']`);
712
+ const metaImportIndex = imports.findIndex((imp) => selfMetaPattern.test(imp));
713
+ if (metaImportIndex !== -1) {
714
+ const existingMetaImport = imports[metaImportIndex];
715
+ const aliasMetaNames = parsed.aliasModels.map((a) => a.metaName);
716
+ const baseMeta = `${parsed.meta.nodeName}Meta`;
717
+ // Reconstruct meta import with all metas
718
+ const allMetas = [baseMeta, ...aliasMetaNames];
719
+ const pathMatch = existingMetaImport.match(/from\s+["']([^"']+)["']/);
720
+ if (pathMatch) {
721
+ imports[metaImportIndex] = `import { ${allMetas.join(", ")} } from "${pathMatch[1]}";`;
722
+ }
723
+ }
724
+ }
725
+ let content = imports.join("\n") + "\n\n";
418
726
  // Add the entity type definition (keep from original)
419
727
  content += generateEntityTypeDefinition(parsed);
420
728
  content += "\n";
421
729
  // Add the descriptor
422
730
  content += descriptor.code;
731
+ // Add alias descriptors if any
732
+ if (parsed.aliasModels.length > 0) {
733
+ content += generateAliasDescriptors(parsed);
734
+ }
423
735
  return content;
424
736
  }
737
+ /**
738
+ * Generates alias descriptor exports for entities with alias models.
739
+ *
740
+ * Example output:
741
+ * ```typescript
742
+ * export const OwnerDescriptor = defineEntityAlias(UserDescriptor, ownerMeta);
743
+ * export const AuthorDescriptor = defineEntityAlias(UserDescriptor, authorMeta);
744
+ * ```
745
+ */
746
+ function generateAliasDescriptors(parsed) {
747
+ const baseDescriptorName = `${parsed.meta.labelName}Descriptor`;
748
+ let code = "\n// Alias descriptors for relationship endpoints\n";
749
+ for (const alias of parsed.aliasModels) {
750
+ code += `export const ${alias.descriptorName} = defineEntityAlias(${baseDescriptorName}, ${alias.metaName});\n`;
751
+ }
752
+ return code;
753
+ }
425
754
  /**
426
755
  * Generates the entity type definition from parsed data.
427
756
  */