@vertz/compiler 0.2.19 → 0.2.20
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/index.d.ts +9 -1
- package/dist/index.js +36 -0
- package/package.json +1 -1
package/dist/index.d.ts
CHANGED
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
type DiagnosticSeverity = "error" | "warning" | "info";
|
|
2
|
-
type DiagnosticCode = "VERTZ_SCHEMA_NAMING" | "VERTZ_SCHEMA_PLACEMENT" | "VERTZ_SCHEMA_EXECUTION" | "VERTZ_SCHEMA_MISSING_ID" | "VERTZ_SCHEMA_DYNAMIC_NAME" | "VERTZ_MODULE_CIRCULAR" | "VERTZ_MODULE_EXPORT_INVALID" | "VERTZ_MODULE_IMPORT_MISSING" | "VERTZ_MODULE_DUPLICATE_NAME" | "VERTZ_MODULE_DYNAMIC_NAME" | "VERTZ_MODULE_OPTIONS_INVALID" | "VERTZ_MODULE_WRONG_OWNERSHIP" | "VERTZ_SERVICE_INJECT_MISSING" | "VERTZ_SERVICE_UNUSED" | "VERTZ_SERVICE_DYNAMIC_NAME" | "VERTZ_ENV_MISSING_DEFAULT" | "VERTZ_ENV_DUPLICATE" | "VERTZ_ENV_DYNAMIC_CONFIG" | "VERTZ_MW_MISSING_NAME" | "VERTZ_MW_MISSING_HANDLER" | "VERTZ_MW_DYNAMIC_NAME" | "VERTZ_MW_NON_OBJECT_CONFIG" | "VERTZ_MW_REQUIRES_UNSATISFIED" | "VERTZ_MW_PROVIDES_COLLISION" | "VERTZ_MW_ORDER_INVALID" | "VERTZ_RT_UNKNOWN_MODULE_DEF" | "VERTZ_RT_DYNAMIC_PATH" | "VERTZ_RT_MISSING_HANDLER" | "VERTZ_RT_MISSING_PREFIX" | "VERTZ_RT_DYNAMIC_CONFIG" | "VERTZ_RT_INVALID_PATH" | "VERTZ_ROUTE_DUPLICATE" | "VERTZ_ROUTE_PARAM_MISMATCH" | "VERTZ_ROUTE_MISSING_RESPONSE" | "VERTZ_APP_MISSING" | "VERTZ_APP_NOT_FOUND" | "VERTZ_APP_DUPLICATE" | "VERTZ_APP_BASEPATH_FORMAT" | "VERTZ_APP_INLINE_MODULE" | "VERTZ_DEP_CYCLE" | "VERTZ_DEP_CIRCULAR" | "VERTZ_DEP_UNRESOLVED_INJECT" | "VERTZ_DEP_INIT_ORDER" | "VERTZ_CTX_COLLISION" | "VERTZ_DEAD_CODE" | "ENTITY_MISSING_ARGS" | "ENTITY_NON_LITERAL_NAME" | "ENTITY_INVALID_NAME" | "ENTITY_DUPLICATE_NAME" | "ENTITY_CONFIG_NOT_OBJECT" | "ENTITY_MISSING_MODEL" | "ENTITY_MODEL_UNRESOLVABLE" | "ENTITY_ACTION_NAME_COLLISION" | "ENTITY_ACTION_MISSING_SCHEMA" | "ENTITY_ACTION_INVALID_METHOD" | "ENTITY_UNKNOWN_ACCESS_OP" | "ENTITY_UNRESOLVED_IMPORT" | "ENTITY_ROUTE_COLLISION" | "ENTITY_NO_ROUTES" | "ENTITY_MODEL_NOT_REGISTERED" | "ENTITY_EXPOSE_EMPTY_SELECT" | "ACCESS_MULTIPLE_DEFINITIONS" | "ACCESS_NON_LITERAL_KEY" | "ACCESS_NON_LITERAL_ROLE" | "ACCESS_DUPLICATE_ENTITLEMENT" | "ACCESS_WHERE_NOT_TRANSLATABLE";
|
|
2
|
+
type DiagnosticCode = "VERTZ_SCHEMA_NAMING" | "VERTZ_SCHEMA_PLACEMENT" | "VERTZ_SCHEMA_EXECUTION" | "VERTZ_SCHEMA_MISSING_ID" | "VERTZ_SCHEMA_DYNAMIC_NAME" | "VERTZ_MODULE_CIRCULAR" | "VERTZ_MODULE_EXPORT_INVALID" | "VERTZ_MODULE_IMPORT_MISSING" | "VERTZ_MODULE_DUPLICATE_NAME" | "VERTZ_MODULE_DYNAMIC_NAME" | "VERTZ_MODULE_OPTIONS_INVALID" | "VERTZ_MODULE_WRONG_OWNERSHIP" | "VERTZ_SERVICE_INJECT_MISSING" | "VERTZ_SERVICE_UNUSED" | "VERTZ_SERVICE_DYNAMIC_NAME" | "VERTZ_ENV_MISSING_DEFAULT" | "VERTZ_ENV_DUPLICATE" | "VERTZ_ENV_DYNAMIC_CONFIG" | "VERTZ_MW_MISSING_NAME" | "VERTZ_MW_MISSING_HANDLER" | "VERTZ_MW_DYNAMIC_NAME" | "VERTZ_MW_NON_OBJECT_CONFIG" | "VERTZ_MW_REQUIRES_UNSATISFIED" | "VERTZ_MW_PROVIDES_COLLISION" | "VERTZ_MW_ORDER_INVALID" | "VERTZ_RT_UNKNOWN_MODULE_DEF" | "VERTZ_RT_DYNAMIC_PATH" | "VERTZ_RT_MISSING_HANDLER" | "VERTZ_RT_MISSING_PREFIX" | "VERTZ_RT_DYNAMIC_CONFIG" | "VERTZ_RT_INVALID_PATH" | "VERTZ_ROUTE_DUPLICATE" | "VERTZ_ROUTE_PARAM_MISMATCH" | "VERTZ_ROUTE_MISSING_RESPONSE" | "VERTZ_APP_MISSING" | "VERTZ_APP_NOT_FOUND" | "VERTZ_APP_DUPLICATE" | "VERTZ_APP_BASEPATH_FORMAT" | "VERTZ_APP_INLINE_MODULE" | "VERTZ_DEP_CYCLE" | "VERTZ_DEP_CIRCULAR" | "VERTZ_DEP_UNRESOLVED_INJECT" | "VERTZ_DEP_INIT_ORDER" | "VERTZ_CTX_COLLISION" | "VERTZ_DEAD_CODE" | "ENTITY_MISSING_ARGS" | "ENTITY_NON_LITERAL_NAME" | "ENTITY_INVALID_NAME" | "ENTITY_DUPLICATE_NAME" | "ENTITY_CONFIG_NOT_OBJECT" | "ENTITY_MISSING_MODEL" | "ENTITY_MODEL_UNRESOLVABLE" | "ENTITY_ACTION_NAME_COLLISION" | "ENTITY_ACTION_MISSING_SCHEMA" | "ENTITY_ACTION_INVALID_METHOD" | "ENTITY_UNKNOWN_ACCESS_OP" | "ENTITY_UNRESOLVED_IMPORT" | "ENTITY_ROUTE_COLLISION" | "ENTITY_NO_ROUTES" | "ENTITY_MODEL_NOT_REGISTERED" | "ENTITY_EXPOSE_EMPTY_SELECT" | "ENTITY_EXPOSE_NON_LITERAL" | "ENTITY_EXPOSE_RELATION_UNRESOLVED" | "ACCESS_MULTIPLE_DEFINITIONS" | "ACCESS_NON_LITERAL_KEY" | "ACCESS_NON_LITERAL_ROLE" | "ACCESS_DUPLICATE_ENTITLEMENT" | "ACCESS_WHERE_NOT_TRANSLATABLE";
|
|
3
3
|
interface SourceContext {
|
|
4
4
|
lines: {
|
|
5
5
|
number: number;
|
|
@@ -440,6 +440,7 @@ declare class EntityAnalyzer extends BaseAnalyzer<EntityAnalyzerResult> {
|
|
|
440
440
|
private extractRelations;
|
|
441
441
|
private extractExpose;
|
|
442
442
|
private extractExposeFields;
|
|
443
|
+
private checkExposeNonLiteral;
|
|
443
444
|
/**
|
|
444
445
|
* Extract primaryKey and hiddenFields from the model's table type.
|
|
445
446
|
* Navigates ModelDef.table._columns to inspect column metadata.
|
|
@@ -451,6 +452,13 @@ declare class EntityAnalyzer extends BaseAnalyzer<EntityAnalyzerResult> {
|
|
|
451
452
|
*/
|
|
452
453
|
private resolveModelRelationTypes;
|
|
453
454
|
/**
|
|
455
|
+
* Post-processing: validate expose.include relations after entity resolution.
|
|
456
|
+
* Emits ENTITY_EXPOSE_RELATION_UNRESOLVED for relations whose target entity
|
|
457
|
+
* could not be resolved. The IR preserves the entries; the ir-adapter handles
|
|
458
|
+
* omission from codegen output.
|
|
459
|
+
*/
|
|
460
|
+
private validateExposeRelations;
|
|
461
|
+
/**
|
|
454
462
|
* Post-processing: resolve relation `entity` fields by matching
|
|
455
463
|
* each relation's _target return type to the table type of known entities.
|
|
456
464
|
*/
|
package/dist/index.js
CHANGED
|
@@ -985,6 +985,7 @@ class EntityAnalyzer extends BaseAnalyzer {
|
|
|
985
985
|
}
|
|
986
986
|
}
|
|
987
987
|
this.resolveRelationEntities(entities, modelExprs);
|
|
988
|
+
this.validateExposeRelations(entities);
|
|
988
989
|
return { entities };
|
|
989
990
|
}
|
|
990
991
|
findEntityCalls(file) {
|
|
@@ -1454,6 +1455,7 @@ class EntityAnalyzer extends BaseAnalyzer {
|
|
|
1454
1455
|
const selectExpr = getPropertyValue(exposeExpr, "select");
|
|
1455
1456
|
if (!selectExpr || !selectExpr.isKind(SyntaxKind5.ObjectLiteralExpression))
|
|
1456
1457
|
return;
|
|
1458
|
+
this.checkExposeNonLiteral(selectExpr, "expose.select", loc);
|
|
1457
1459
|
const select = this.extractExposeFields(selectExpr);
|
|
1458
1460
|
if (select.length === 0) {
|
|
1459
1461
|
this.addDiagnostic({
|
|
@@ -1466,6 +1468,7 @@ class EntityAnalyzer extends BaseAnalyzer {
|
|
|
1466
1468
|
const includeExpr = getPropertyValue(exposeExpr, "include");
|
|
1467
1469
|
let include;
|
|
1468
1470
|
if (includeExpr?.isKind(SyntaxKind5.ObjectLiteralExpression)) {
|
|
1471
|
+
this.checkExposeNonLiteral(includeExpr, "expose.include", loc);
|
|
1469
1472
|
include = getProperties(includeExpr).filter(({ value }) => getBooleanValue(value) !== false).map(({ name, value }) => {
|
|
1470
1473
|
const boolVal = getBooleanValue(value);
|
|
1471
1474
|
if (boolVal === true || !value.isKind(SyntaxKind5.ObjectLiteralExpression)) {
|
|
@@ -1491,6 +1494,19 @@ class EntityAnalyzer extends BaseAnalyzer {
|
|
|
1491
1494
|
conditional: getBooleanValue(value) !== true
|
|
1492
1495
|
}));
|
|
1493
1496
|
}
|
|
1497
|
+
checkExposeNonLiteral(obj, context, loc) {
|
|
1498
|
+
for (const prop of obj.getProperties()) {
|
|
1499
|
+
if (prop.isKind(SyntaxKind5.SpreadAssignment) || prop.isKind(SyntaxKind5.PropertyAssignment) && prop.getNameNode().isKind(SyntaxKind5.ComputedPropertyName)) {
|
|
1500
|
+
this.addDiagnostic({
|
|
1501
|
+
code: "ENTITY_EXPOSE_NON_LITERAL",
|
|
1502
|
+
severity: "warning",
|
|
1503
|
+
message: `${context} contains non-literal properties (spread or computed) that can't be statically analyzed`,
|
|
1504
|
+
...loc
|
|
1505
|
+
});
|
|
1506
|
+
return;
|
|
1507
|
+
}
|
|
1508
|
+
}
|
|
1509
|
+
}
|
|
1494
1510
|
extractModelTableMetadata(modelExpr, modelRef) {
|
|
1495
1511
|
try {
|
|
1496
1512
|
const modelType = modelExpr.getType();
|
|
@@ -1561,6 +1577,26 @@ class EntityAnalyzer extends BaseAnalyzer {
|
|
|
1561
1577
|
} catch {}
|
|
1562
1578
|
return result;
|
|
1563
1579
|
}
|
|
1580
|
+
validateExposeRelations(entities) {
|
|
1581
|
+
for (const entity of entities) {
|
|
1582
|
+
if (!entity.expose?.include)
|
|
1583
|
+
continue;
|
|
1584
|
+
const relationMap = new Map(entity.relations.map((r) => [r.name, r]));
|
|
1585
|
+
for (const rel of entity.expose.include) {
|
|
1586
|
+
const matchedRelation = relationMap.get(rel.name);
|
|
1587
|
+
if (!matchedRelation || !matchedRelation.entity) {
|
|
1588
|
+
this.addDiagnostic({
|
|
1589
|
+
code: "ENTITY_EXPOSE_RELATION_UNRESOLVED",
|
|
1590
|
+
severity: "warning",
|
|
1591
|
+
message: `expose.include references relation "${rel.name}" whose target entity could not be resolved`,
|
|
1592
|
+
file: entity.sourceFile,
|
|
1593
|
+
line: entity.sourceLine,
|
|
1594
|
+
column: entity.sourceColumn
|
|
1595
|
+
});
|
|
1596
|
+
}
|
|
1597
|
+
}
|
|
1598
|
+
}
|
|
1599
|
+
}
|
|
1564
1600
|
resolveRelationEntities(entities, modelExprs) {
|
|
1565
1601
|
try {
|
|
1566
1602
|
const tableToEntity = new Map;
|