@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.
- package/dist/common/helpers/define-entity.d.ts +29 -0
- package/dist/common/helpers/define-entity.d.ts.map +1 -1
- package/dist/common/helpers/define-entity.js +42 -1
- package/dist/common/helpers/define-entity.js.map +1 -1
- package/dist/common/interfaces/entity.descriptor.interface.d.ts +2 -2
- package/dist/common/interfaces/entity.descriptor.interface.d.ts.map +1 -1
- package/dist/common/interfaces/entity.descriptor.interface.js +2 -1
- package/dist/common/interfaces/entity.descriptor.interface.js.map +1 -1
- package/dist/common/interfaces/entity.schema.interface.d.ts +19 -1
- package/dist/common/interfaces/entity.schema.interface.d.ts.map +1 -1
- package/dist/core/email/templates/en/paymentFailure.hbs +56 -0
- package/dist/core/email/templates/en/paymentSuccessAdmin.hbs +31 -0
- package/dist/core/email/templates/en/paymentSuccessCustomer.hbs +51 -0
- package/dist/core/email/templates/en/subscriptionStatusChange.hbs +48 -0
- package/dist/core/jsonapi/serialisers/descriptor.based.serialiser.d.ts.map +1 -1
- package/dist/core/jsonapi/serialisers/descriptor.based.serialiser.js +13 -0
- package/dist/core/jsonapi/serialisers/descriptor.based.serialiser.js.map +1 -1
- package/dist/core/jsonapi/serialisers/jsonapi.paginator.d.ts +3 -0
- package/dist/core/jsonapi/serialisers/jsonapi.paginator.d.ts.map +1 -1
- package/dist/core/jsonapi/serialisers/jsonapi.paginator.js +6 -0
- package/dist/core/jsonapi/serialisers/jsonapi.paginator.js.map +1 -1
- package/dist/core/jsonapi/services/jsonapi.service.d.ts.map +1 -1
- package/dist/core/jsonapi/services/jsonapi.service.js +6 -0
- package/dist/core/jsonapi/services/jsonapi.service.js.map +1 -1
- package/dist/core/neo4j/abstracts/abstract.service.d.ts +2 -0
- package/dist/core/neo4j/abstracts/abstract.service.d.ts.map +1 -1
- package/dist/core/neo4j/abstracts/abstract.service.js +8 -4
- package/dist/core/neo4j/abstracts/abstract.service.js.map +1 -1
- package/dist/core/neo4j/services/neo4j.service.d.ts +9 -0
- package/dist/core/neo4j/services/neo4j.service.d.ts.map +1 -1
- package/dist/core/neo4j/services/neo4j.service.js +95 -1
- package/dist/core/neo4j/services/neo4j.service.js.map +1 -1
- package/dist/foundations/audit/entities/audit.entity.d.ts +1 -1
- package/dist/foundations/audit/entities/audit.entity.d.ts.map +1 -1
- package/dist/foundations/audit/serialisers/audit.serialiser.js +2 -2
- package/dist/foundations/audit/serialisers/audit.serialiser.js.map +1 -1
- package/dist/foundations/auth/entities/auth.entity.d.ts +1 -1
- package/dist/foundations/auth/entities/auth.entity.d.ts.map +1 -1
- package/dist/foundations/auth/repositories/auth.repository.d.ts +1 -1
- package/dist/foundations/auth/repositories/auth.repository.d.ts.map +1 -1
- package/dist/foundations/auth/repositories/auth.repository.js +31 -13
- package/dist/foundations/auth/repositories/auth.repository.js.map +1 -1
- package/dist/foundations/auth/serialisers/auth.serialiser.js +2 -2
- package/dist/foundations/auth/serialisers/auth.serialiser.js.map +1 -1
- package/dist/foundations/auth/services/auth.service.d.ts +1 -1
- package/dist/foundations/auth/services/auth.service.d.ts.map +1 -1
- package/dist/foundations/company/company.module.d.ts.map +1 -1
- package/dist/foundations/company/company.module.js +9 -5
- package/dist/foundations/company/company.module.js.map +1 -1
- package/dist/foundations/company/entities/company.d.ts +44 -0
- package/dist/foundations/company/entities/company.d.ts.map +1 -0
- package/dist/foundations/company/entities/company.js +47 -0
- package/dist/foundations/company/entities/company.js.map +1 -0
- package/dist/foundations/company/entities/company.meta.d.ts +1 -1
- package/dist/foundations/company/entities/company.meta.d.ts.map +1 -1
- package/dist/foundations/company/index.d.ts +1 -2
- package/dist/foundations/company/index.d.ts.map +1 -1
- package/dist/foundations/company/index.js +3 -3
- package/dist/foundations/company/index.js.map +1 -1
- package/dist/foundations/company/repositories/company.repository.d.ts +26 -1
- package/dist/foundations/company/repositories/company.repository.d.ts.map +1 -1
- package/dist/foundations/company/repositories/company.repository.js +67 -11
- package/dist/foundations/company/repositories/company.repository.js.map +1 -1
- package/dist/foundations/company/services/company.service.d.ts +4 -1
- package/dist/foundations/company/services/company.service.d.ts.map +1 -1
- package/dist/foundations/company/services/company.service.js +11 -6
- package/dist/foundations/company/services/company.service.js.map +1 -1
- package/dist/foundations/content/entities/content.entity.d.ts +1 -1
- package/dist/foundations/content/entities/content.entity.d.ts.map +1 -1
- package/dist/foundations/content/serialisers/content.serialiser.js +3 -3
- package/dist/foundations/content/serialisers/content.serialiser.js.map +1 -1
- package/dist/foundations/module/queries/feature.module.query.d.ts +3 -1
- package/dist/foundations/module/queries/feature.module.query.d.ts.map +1 -1
- package/dist/foundations/module/queries/feature.module.query.js +27 -15
- package/dist/foundations/module/queries/feature.module.query.js.map +1 -1
- package/dist/foundations/notification/entities/notification.entity.d.ts +1 -1
- package/dist/foundations/notification/entities/notification.entity.d.ts.map +1 -1
- package/dist/foundations/notification/serialisers/notifications.serialiser.js +2 -2
- package/dist/foundations/notification/serialisers/notifications.serialiser.js.map +1 -1
- package/dist/foundations/oauth/entities/oauth.client.entity.d.ts +2 -2
- package/dist/foundations/oauth/entities/oauth.client.entity.d.ts.map +1 -1
- package/dist/foundations/relevancy/repositories/relevancy.repository.d.ts.map +1 -1
- package/dist/foundations/relevancy/repositories/relevancy.repository.js +1 -1
- package/dist/foundations/relevancy/repositories/relevancy.repository.js.map +1 -1
- package/dist/foundations/relevancy/services/relevancy.service.js +1 -1
- package/dist/foundations/relevancy/services/relevancy.service.js.map +1 -1
- package/dist/foundations/role/entities/role.d.ts +28 -0
- package/dist/foundations/role/entities/role.d.ts.map +1 -0
- package/dist/foundations/role/entities/role.js +33 -0
- package/dist/foundations/role/entities/role.js.map +1 -0
- package/dist/foundations/role/entities/role.meta.d.ts +1 -1
- package/dist/foundations/role/entities/role.meta.d.ts.map +1 -1
- package/dist/foundations/role/repositories/role.repository.d.ts +1 -1
- package/dist/foundations/role/repositories/role.repository.d.ts.map +1 -1
- package/dist/foundations/role/repositories/role.repository.js +7 -7
- package/dist/foundations/role/repositories/role.repository.js.map +1 -1
- package/dist/foundations/role/role.module.d.ts.map +1 -1
- package/dist/foundations/role/role.module.js +3 -4
- package/dist/foundations/role/role.module.js.map +1 -1
- package/dist/foundations/role/services/role.service.js +5 -5
- package/dist/foundations/role/services/role.service.js.map +1 -1
- package/dist/foundations/stripe-customer/entities/stripe-customer.entity.d.ts +1 -1
- package/dist/foundations/stripe-customer/entities/stripe-customer.entity.d.ts.map +1 -1
- package/dist/foundations/stripe-price/dtos/stripe-price.dto.d.ts +12 -0
- package/dist/foundations/stripe-price/dtos/stripe-price.dto.d.ts.map +1 -1
- package/dist/foundations/stripe-price/dtos/stripe-price.dto.js +47 -1
- package/dist/foundations/stripe-price/dtos/stripe-price.dto.js.map +1 -1
- package/dist/foundations/stripe-price/entities/stripe-price.entity.d.ts +2 -0
- package/dist/foundations/stripe-price/entities/stripe-price.entity.d.ts.map +1 -1
- package/dist/foundations/stripe-price/entities/stripe-price.map.d.ts.map +1 -1
- package/dist/foundations/stripe-price/entities/stripe-price.map.js +1 -0
- package/dist/foundations/stripe-price/entities/stripe-price.map.js.map +1 -1
- package/dist/foundations/stripe-price/entities/stripe-price.model.d.ts.map +1 -1
- package/dist/foundations/stripe-price/entities/stripe-price.model.js +2 -1
- package/dist/foundations/stripe-price/entities/stripe-price.model.js.map +1 -1
- package/dist/foundations/stripe-price/repositories/stripe-price.repository.d.ts +3 -0
- package/dist/foundations/stripe-price/repositories/stripe-price.repository.d.ts.map +1 -1
- package/dist/foundations/stripe-price/repositories/stripe-price.repository.js +64 -4
- package/dist/foundations/stripe-price/repositories/stripe-price.repository.js.map +1 -1
- package/dist/foundations/stripe-price/serialisers/stripe-price.serialiser.d.ts.map +1 -1
- package/dist/foundations/stripe-price/serialisers/stripe-price.serialiser.js +5 -0
- package/dist/foundations/stripe-price/serialisers/stripe-price.serialiser.js.map +1 -1
- package/dist/foundations/stripe-price/services/stripe-price-admin.service.d.ts.map +1 -1
- package/dist/foundations/stripe-price/services/stripe-price-admin.service.js +10 -0
- package/dist/foundations/stripe-price/services/stripe-price-admin.service.js.map +1 -1
- package/dist/foundations/stripe-subscription/repositories/stripe-subscription.repository.d.ts +11 -0
- package/dist/foundations/stripe-subscription/repositories/stripe-subscription.repository.d.ts.map +1 -1
- package/dist/foundations/stripe-subscription/repositories/stripe-subscription.repository.js +22 -0
- package/dist/foundations/stripe-subscription/repositories/stripe-subscription.repository.js.map +1 -1
- package/dist/foundations/stripe-subscription/services/feature-sync.service.d.ts +33 -0
- package/dist/foundations/stripe-subscription/services/feature-sync.service.d.ts.map +1 -0
- package/dist/foundations/stripe-subscription/services/feature-sync.service.js +155 -0
- package/dist/foundations/stripe-subscription/services/feature-sync.service.js.map +1 -0
- package/dist/foundations/stripe-subscription/services/stripe-subscription-admin.service.d.ts.map +1 -1
- package/dist/foundations/stripe-subscription/services/stripe-subscription-admin.service.js +11 -0
- package/dist/foundations/stripe-subscription/services/stripe-subscription-admin.service.js.map +1 -1
- package/dist/foundations/stripe-subscription/stripe-subscription.module.d.ts.map +1 -1
- package/dist/foundations/stripe-subscription/stripe-subscription.module.js +3 -0
- package/dist/foundations/stripe-subscription/stripe-subscription.module.js.map +1 -1
- package/dist/foundations/stripe-webhook/processors/stripe-webhook.processor.d.ts +3 -1
- package/dist/foundations/stripe-webhook/processors/stripe-webhook.processor.d.ts.map +1 -1
- package/dist/foundations/stripe-webhook/processors/stripe-webhook.processor.js +79 -9
- package/dist/foundations/stripe-webhook/processors/stripe-webhook.processor.js.map +1 -1
- package/dist/foundations/stripe-webhook/services/stripe-webhook-notification.service.d.ts +26 -1
- package/dist/foundations/stripe-webhook/services/stripe-webhook-notification.service.d.ts.map +1 -1
- package/dist/foundations/stripe-webhook/services/stripe-webhook-notification.service.js +124 -2
- package/dist/foundations/stripe-webhook/services/stripe-webhook-notification.service.js.map +1 -1
- package/dist/foundations/stripe-webhook/stripe-webhook.module.d.ts.map +1 -1
- package/dist/foundations/stripe-webhook/stripe-webhook.module.js +2 -0
- package/dist/foundations/stripe-webhook/stripe-webhook.module.js.map +1 -1
- package/dist/foundations/tokenusage/entities/tokenusage.entity.d.ts +1 -1
- package/dist/foundations/tokenusage/entities/tokenusage.entity.d.ts.map +1 -1
- package/dist/foundations/user/controllers/user.controller.d.ts +1 -1
- package/dist/foundations/user/controllers/user.controller.d.ts.map +1 -1
- package/dist/foundations/user/entities/user.d.ts +124 -0
- package/dist/foundations/user/entities/user.d.ts.map +1 -0
- package/dist/foundations/user/entities/user.js +81 -0
- package/dist/foundations/user/entities/user.js.map +1 -0
- package/dist/foundations/user/entities/user.meta.d.ts +1 -1
- package/dist/foundations/user/entities/user.meta.d.ts.map +1 -1
- package/dist/foundations/user/index.d.ts +1 -4
- package/dist/foundations/user/index.d.ts.map +1 -1
- package/dist/foundations/user/index.js +6 -23
- package/dist/foundations/user/index.js.map +1 -1
- package/dist/foundations/user/repositories/user.repository.d.ts +13 -1
- package/dist/foundations/user/repositories/user.repository.d.ts.map +1 -1
- package/dist/foundations/user/repositories/user.repository.js +85 -21
- package/dist/foundations/user/repositories/user.repository.js.map +1 -1
- package/dist/foundations/user/services/user.service.d.ts.map +1 -1
- package/dist/foundations/user/services/user.service.js +12 -12
- package/dist/foundations/user/services/user.service.js.map +1 -1
- package/dist/foundations/user/user.module.d.ts.map +1 -1
- package/dist/foundations/user/user.module.js +7 -8
- package/dist/foundations/user/user.module.js.map +1 -1
- package/dist/openapi/factories/entity-schema.factory.js +12 -0
- package/dist/openapi/factories/entity-schema.factory.js.map +1 -1
- package/dist/openapi/schemas/jsonapi-base.schemas.d.ts +1 -0
- package/dist/openapi/schemas/jsonapi-base.schemas.d.ts.map +1 -1
- package/dist/openapi/schemas/jsonapi-base.schemas.js.map +1 -1
- package/dist/tools/migrate-entity/__tests__/ast-parser.spec.js +331 -0
- package/dist/tools/migrate-entity/__tests__/descriptor-generator.spec.js +482 -0
- package/dist/tools/migrate-entity/__tests__/file-discovery.spec.js +190 -0
- package/dist/tools/migrate-entity/__tests__/integration.spec.js +261 -0
- package/dist/tools/migrate-entity/index.js +2 -2
- package/dist/tools/migrate-entity/lib/ast-parser.js +255 -10
- package/dist/tools/migrate-entity/lib/descriptor-generator.js +384 -55
- package/dist/tools/migrate-entity/lib/entity-migrator.js +112 -9
- package/dist/tools/migrate-entity/lib/module-updater.js +25 -9
- package/dist/tools/migrate-entity/lib/reference-updater.js +110 -24
- package/package.json +2 -2
- package/dist/foundations/company/entities/company.entity.d.ts +0 -18
- package/dist/foundations/company/entities/company.entity.d.ts.map +0 -1
- package/dist/foundations/company/entities/company.entity.js +0 -3
- package/dist/foundations/company/entities/company.entity.js.map +0 -1
- package/dist/foundations/company/entities/company.map.d.ts +0 -8
- package/dist/foundations/company/entities/company.map.d.ts.map +0 -1
- package/dist/foundations/company/entities/company.map.js +0 -23
- package/dist/foundations/company/entities/company.map.js.map +0 -1
- package/dist/foundations/company/entities/company.model.d.ts +0 -4
- package/dist/foundations/company/entities/company.model.d.ts.map +0 -1
- package/dist/foundations/company/entities/company.model.js +0 -17
- package/dist/foundations/company/entities/company.model.js.map +0 -1
- package/dist/foundations/company/serialisers/company.serialiser.d.ts +0 -14
- package/dist/foundations/company/serialisers/company.serialiser.d.ts.map +0 -1
- package/dist/foundations/company/serialisers/company.serialiser.js +0 -76
- package/dist/foundations/company/serialisers/company.serialiser.js.map +0 -1
- package/dist/foundations/role/entities/role.entity.d.ts +0 -9
- package/dist/foundations/role/entities/role.entity.d.ts.map +0 -1
- package/dist/foundations/role/entities/role.entity.js +0 -3
- package/dist/foundations/role/entities/role.entity.js.map +0 -1
- package/dist/foundations/role/entities/role.map.d.ts +0 -8
- package/dist/foundations/role/entities/role.map.d.ts.map +0 -1
- package/dist/foundations/role/entities/role.map.js +0 -15
- package/dist/foundations/role/entities/role.map.js.map +0 -1
- package/dist/foundations/role/entities/role.model.d.ts +0 -4
- package/dist/foundations/role/entities/role.model.d.ts.map +0 -1
- package/dist/foundations/role/entities/role.model.js +0 -15
- package/dist/foundations/role/entities/role.model.js.map +0 -1
- package/dist/foundations/role/serialisers/role.serialiser.d.ts +0 -12
- package/dist/foundations/role/serialisers/role.serialiser.d.ts.map +0 -1
- package/dist/foundations/role/serialisers/role.serialiser.js +0 -46
- package/dist/foundations/role/serialisers/role.serialiser.js.map +0 -1
- package/dist/foundations/user/entities/user.entity.d.ts +0 -26
- package/dist/foundations/user/entities/user.entity.d.ts.map +0 -1
- package/dist/foundations/user/entities/user.entity.js +0 -3
- package/dist/foundations/user/entities/user.entity.js.map +0 -1
- package/dist/foundations/user/entities/user.map.d.ts +0 -8
- package/dist/foundations/user/entities/user.map.d.ts.map +0 -1
- package/dist/foundations/user/entities/user.map.js +0 -27
- package/dist/foundations/user/entities/user.map.js.map +0 -1
- package/dist/foundations/user/entities/user.model.d.ts +0 -7
- package/dist/foundations/user/entities/user.model.d.ts.map +0 -1
- package/dist/foundations/user/entities/user.model.js +0 -30
- package/dist/foundations/user/entities/user.model.js.map +0 -1
- package/dist/foundations/user/serialisers/user.serialiser.d.ts +0 -14
- package/dist/foundations/user/serialisers/user.serialiser.d.ts.map +0 -1
- package/dist/foundations/user/serialisers/user.serialiser.js +0 -80
- 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
|
-
//
|
|
29
|
-
const
|
|
30
|
-
// Build
|
|
31
|
-
const
|
|
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
|
-
*
|
|
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
|
-
|
|
82
|
-
|
|
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
|
-
|
|
86
|
-
|
|
87
|
-
|
|
88
|
-
|
|
89
|
-
|
|
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
|
-
|
|
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: "
|
|
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
|
|
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
|
-
//
|
|
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
|
|
220
|
-
|
|
221
|
-
|
|
222
|
-
|
|
223
|
-
|
|
224
|
-
|
|
225
|
-
|
|
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,
|
|
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
|
-
//
|
|
242
|
-
if ([
|
|
243
|
-
|
|
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 "
|
|
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:
|
|
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
|
-
|
|
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 === "
|
|
307
|
-
return `import { ${metaName} } from "
|
|
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
|
-
|
|
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
|
-
|
|
335
|
-
|
|
336
|
-
code +=
|
|
337
|
-
|
|
338
|
-
|
|
339
|
-
|
|
340
|
-
|
|
341
|
-
|
|
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
|
-
|
|
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
|
*/
|