@vertz/compiler 0.2.14 → 0.2.16

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 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";
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" | "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;
@@ -39,9 +39,32 @@ interface AppIR {
39
39
  schemas: SchemaIR[];
40
40
  entities: EntityIR[];
41
41
  databases: DatabaseIR[];
42
+ access?: AccessIR;
42
43
  dependencyGraph: DependencyGraphIR;
43
44
  diagnostics: Diagnostic[];
44
45
  }
46
+ interface AccessIR extends SourceLocation {
47
+ entities: AccessEntityIR[];
48
+ entitlements: string[];
49
+ whereClauses: AccessWhereClauseIR[];
50
+ }
51
+ interface AccessEntityIR {
52
+ name: string;
53
+ roles: string[];
54
+ }
55
+ interface AccessWhereClauseIR {
56
+ entitlement: string;
57
+ conditions: AccessWhereCondition[];
58
+ }
59
+ type AccessWhereCondition = {
60
+ kind: "marker";
61
+ column: string;
62
+ marker: "user.id" | "user.tenantId";
63
+ } | {
64
+ kind: "literal";
65
+ column: string;
66
+ value: string | number | boolean;
67
+ };
45
68
  interface AppDefinition extends SourceLocation {
46
69
  basePath: string;
47
70
  version?: string;
@@ -174,12 +197,16 @@ interface EntityIR extends SourceLocation {
174
197
  hooks: EntityHooksIR;
175
198
  actions: EntityActionIR[];
176
199
  relations: EntityRelationIR[];
200
+ tenantScoped?: boolean;
201
+ table?: string;
177
202
  }
178
203
  interface EntityModelRef {
179
204
  variableName: string;
180
205
  importSource?: string;
181
206
  tableName?: string;
182
207
  schemaRefs: EntityModelSchemaRefs;
208
+ primaryKey?: string;
209
+ hiddenFields?: string[];
183
210
  }
184
211
  interface EntityModelSchemaRefs {
185
212
  response?: SchemaRef;
@@ -215,6 +242,9 @@ interface EntityRelationIR {
215
242
  type?: "one" | "many";
216
243
  entity?: string;
217
244
  selection: "all" | string[];
245
+ allowWhere?: string[];
246
+ allowOrderBy?: string[];
247
+ maxLimit?: number;
218
248
  }
219
249
  interface ModuleDefContext {
220
250
  moduleDefVariables: Map<string, string>;
@@ -287,6 +317,21 @@ declare abstract class BaseAnalyzer<T> implements Analyzer<T> {
287
317
  protected addDiagnostic(diagnostic: Diagnostic): void;
288
318
  getDiagnostics(): Diagnostic[];
289
319
  }
320
+ interface AccessAnalyzerResult {
321
+ access?: AccessIR;
322
+ }
323
+ declare class AccessAnalyzer extends BaseAnalyzer<AccessAnalyzerResult> {
324
+ analyze(): Promise<AccessAnalyzerResult>;
325
+ private findDefineAccessCalls;
326
+ private extractAccess;
327
+ private extractWhereClauses;
328
+ /** Find all .where() calls within a node — handles both r.where() and rules.where() */
329
+ private findWhereCalls;
330
+ /** Extract conditions from a where({ column: value }) call argument */
331
+ private extractWhereConditions;
332
+ /** Resolve a condition value to a marker or literal */
333
+ private extractConditionValue;
334
+ }
290
335
  interface AppAnalyzerResult {
291
336
  app: AppDefinition;
292
337
  }
@@ -379,6 +424,11 @@ declare class EntityAnalyzer extends BaseAnalyzer<EntityAnalyzerResult> {
379
424
  private resolveSchemaFromExpression;
380
425
  private extractRelations;
381
426
  /**
427
+ * Extract primaryKey and hiddenFields from the model's table type.
428
+ * Navigates ModelDef.table._columns to inspect column metadata.
429
+ */
430
+ private extractModelTableMetadata;
431
+ /**
382
432
  * Resolve relation types from the model's TypeScript type.
383
433
  * Navigates ModelDef.relations to extract _type ('one'/'many') for each relation.
384
434
  */
@@ -474,6 +524,7 @@ interface CompilerDependencies {
474
524
  app: Analyzer<AppAnalyzerResult>;
475
525
  entity: Analyzer<EntityAnalyzerResult>;
476
526
  database: Analyzer<DatabaseAnalyzerResult>;
527
+ access: Analyzer<AccessAnalyzerResult>;
477
528
  dependencyGraph: Analyzer<DependencyGraphResult>;
478
529
  };
479
530
  validators: Validator[];
@@ -860,4 +911,4 @@ declare class PlacementValidator implements Validator {
860
911
  private checkFileLocation;
861
912
  private checkMixedExports;
862
913
  }
863
- export { typecheckWatch, typecheck, resolveImportPath, resolveIdentifier, resolveExport, resolveConfig, renderSchemaRegistryFile, renderRouteTableFile, renderBootFile, parseWatchBlock, parseTscOutput, parseSchemaName2 as parseSchemaName, parseInjectRefs, parseImports, mergeIR, mergeDiagnostics, isSchemaFile, isSchemaExpression, isFromImport, injectEntityRoutes, hasErrors, getVariableNameForCall, getStringValue, getSourceLocation, getPropertyValue, getProperties, getNumberValue, getBooleanValue, getArrayElements, findMethodCallsOnVariable, findCallExpressions, findAffectedModules, filterBySeverity, extractSchemaId, extractObjectLiteral, extractMethodSignatures, extractIdentifierNames, detectRouteCollisions, defineConfig, createSchemaExecutor, createNamedSchemaRef, createInlineSchemaRef, createEmptyDependencyGraph, createEmptyAppIR, createDiagnosticFromLocation, createDiagnostic, createCompiler, categorizeChanges, buildSchemaRegistry, buildRouteTable, buildManifest, buildBootManifest, addDiagnosticsToIR, VertzConfig, Validator, ValidationConfig, ValidPart, ValidOperation, TypecheckWatchOptions, TypecheckResult, TypecheckOptions, TypecheckDiagnostic, SourceLocation, SourceContext, ServiceMethodParam, ServiceMethodIR, ServiceIR, ServiceAnalyzerResult, ServiceAnalyzer, SchemaRegistryManifest, SchemaRegistryGenerator, SchemaRegistryEntry, SchemaRef, SchemaNameParts, SchemaIR, SchemaExecutor, SchemaExecutionResult, SchemaConfig, SchemaAnalyzerResult, SchemaAnalyzer, RouterIR, RouteTableSchemas, RouteTableManifest, RouteTableGenerator, RouteTableEntry, RouteIR, RouteAnalyzerResult, RouteAnalyzer, ResolvedImport, ResolvedConfig, PlacementValidator, ParsedSchemaName, OpenAPITag, OpenAPIServer, OpenAPIResponse, OpenAPIRequestBody, OpenAPIPathItem, OpenAPIParameter, OpenAPIOperation, OpenAPIInfo, OpenAPIGenerator, OpenAPIDocument, OpenAPIConfig, NamingValidator, NamedSchemaRef, ModuleValidator, ModuleRegistration, ModuleIR, ModuleDefContext, ModuleAnalyzerResult, ModuleAnalyzer, MiddlewareRef, MiddlewareIR, MiddlewareAnalyzerResult, MiddlewareAnalyzer, ManifestRoute, ManifestModule, ManifestMiddleware, ManifestGenerator, ManifestDiagnostic, ManifestDependencyEdge, JSONSchemaObject, InlineSchemaRef, InjectRef, IncrementalResult, IncrementalCompiler, ImportRef, HttpMethod, Generator, FileChange, FileCategory, EnvVariableIR, EnvIR, EnvAnalyzerResult, EnvAnalyzer, EntityRelationIR, EntityModelSchemaRefs, EntityModelRef, EntityIR, EntityHooksIR, EntityAnalyzerResult, EntityAnalyzer, EntityActionIR, EntityAccessRuleKind, EntityAccessIR, DiagnosticSeverity, DiagnosticCode, Diagnostic, DependencyNodeKind, DependencyNode, DependencyGraphResult, DependencyGraphInput, DependencyGraphIR, DependencyGraphAnalyzer, DependencyEdgeKind, DependencyEdge, DatabaseIR, DatabaseAnalyzerResult, DatabaseAnalyzer, CreateDiagnosticOptions, CompletenessValidator, CompilerDependencies, CompilerConfig, Compiler, CompileResult, CategorizedChanges, CategorizeOptions, BootModuleEntry, BootMiddlewareEntry, BootManifest, BootGenerator, BaseGenerator, BaseAnalyzer, AppManifest, AppIR, AppDefinition, AppAnalyzerResult, AppAnalyzer, Analyzer };
914
+ export { typecheckWatch, typecheck, resolveImportPath, resolveIdentifier, resolveExport, resolveConfig, renderSchemaRegistryFile, renderRouteTableFile, renderBootFile, parseWatchBlock, parseTscOutput, parseSchemaName2 as parseSchemaName, parseInjectRefs, parseImports, mergeIR, mergeDiagnostics, isSchemaFile, isSchemaExpression, isFromImport, injectEntityRoutes, hasErrors, getVariableNameForCall, getStringValue, getSourceLocation, getPropertyValue, getProperties, getNumberValue, getBooleanValue, getArrayElements, findMethodCallsOnVariable, findCallExpressions, findAffectedModules, filterBySeverity, extractSchemaId, extractObjectLiteral, extractMethodSignatures, extractIdentifierNames, detectRouteCollisions, defineConfig, createSchemaExecutor, createNamedSchemaRef, createInlineSchemaRef, createEmptyDependencyGraph, createEmptyAppIR, createDiagnosticFromLocation, createDiagnostic, createCompiler, categorizeChanges, buildSchemaRegistry, buildRouteTable, buildManifest, buildBootManifest, addDiagnosticsToIR, VertzConfig, Validator, ValidationConfig, ValidPart, ValidOperation, TypecheckWatchOptions, TypecheckResult, TypecheckOptions, TypecheckDiagnostic, SourceLocation, SourceContext, ServiceMethodParam, ServiceMethodIR, ServiceIR, ServiceAnalyzerResult, ServiceAnalyzer, SchemaRegistryManifest, SchemaRegistryGenerator, SchemaRegistryEntry, SchemaRef, SchemaNameParts, SchemaIR, SchemaExecutor, SchemaExecutionResult, SchemaConfig, SchemaAnalyzerResult, SchemaAnalyzer, RouterIR, RouteTableSchemas, RouteTableManifest, RouteTableGenerator, RouteTableEntry, RouteIR, RouteAnalyzerResult, RouteAnalyzer, ResolvedImport, ResolvedConfig, PlacementValidator, ParsedSchemaName, OpenAPITag, OpenAPIServer, OpenAPIResponse, OpenAPIRequestBody, OpenAPIPathItem, OpenAPIParameter, OpenAPIOperation, OpenAPIInfo, OpenAPIGenerator, OpenAPIDocument, OpenAPIConfig, NamingValidator, NamedSchemaRef, ModuleValidator, ModuleRegistration, ModuleIR, ModuleDefContext, ModuleAnalyzerResult, ModuleAnalyzer, MiddlewareRef, MiddlewareIR, MiddlewareAnalyzerResult, MiddlewareAnalyzer, ManifestRoute, ManifestModule, ManifestMiddleware, ManifestGenerator, ManifestDiagnostic, ManifestDependencyEdge, JSONSchemaObject, InlineSchemaRef, InjectRef, IncrementalResult, IncrementalCompiler, ImportRef, HttpMethod, Generator, FileChange, FileCategory, EnvVariableIR, EnvIR, EnvAnalyzerResult, EnvAnalyzer, EntityRelationIR, EntityModelSchemaRefs, EntityModelRef, EntityIR, EntityHooksIR, EntityAnalyzerResult, EntityAnalyzer, EntityActionIR, EntityAccessRuleKind, EntityAccessIR, DiagnosticSeverity, DiagnosticCode, Diagnostic, DependencyNodeKind, DependencyNode, DependencyGraphResult, DependencyGraphInput, DependencyGraphIR, DependencyGraphAnalyzer, DependencyEdgeKind, DependencyEdge, DatabaseIR, DatabaseAnalyzerResult, DatabaseAnalyzer, CreateDiagnosticOptions, CompletenessValidator, CompilerDependencies, CompilerConfig, Compiler, CompileResult, CategorizedChanges, CategorizeOptions, BootModuleEntry, BootMiddlewareEntry, BootManifest, BootGenerator, BaseGenerator, BaseAnalyzer, AppManifest, AppIR, AppDefinition, AppAnalyzerResult, AppAnalyzer, Analyzer, AccessWhereCondition, AccessWhereClauseIR, AccessIR, AccessEntityIR, AccessAnalyzerResult, AccessAnalyzer };
package/dist/index.js CHANGED
@@ -1,28 +1,6 @@
1
- // src/analyzers/app-analyzer.ts
1
+ // src/analyzers/access-analyzer.ts
2
2
  import { SyntaxKind as SyntaxKind2 } from "ts-morph";
3
3
 
4
- // src/errors.ts
5
- function createDiagnostic(options) {
6
- return { ...options };
7
- }
8
- function createDiagnosticFromLocation(location, options) {
9
- return {
10
- ...options,
11
- file: location.sourceFile,
12
- line: location.sourceLine,
13
- column: location.sourceColumn
14
- };
15
- }
16
- function hasErrors(diagnostics) {
17
- return diagnostics.some((d) => d.severity === "error");
18
- }
19
- function filterBySeverity(diagnostics, severity) {
20
- return diagnostics.filter((d) => d.severity === severity);
21
- }
22
- function mergeDiagnostics(a, b) {
23
- return [...a, ...b];
24
- }
25
-
26
4
  // src/utils/ast-helpers.ts
27
5
  import {
28
6
  SyntaxKind
@@ -210,6 +188,254 @@ class BaseAnalyzer {
210
188
  }
211
189
  }
212
190
 
191
+ // src/analyzers/access-analyzer.ts
192
+ class AccessAnalyzer extends BaseAnalyzer {
193
+ async analyze() {
194
+ const files = this.project.getSourceFiles();
195
+ const calls = [];
196
+ for (const file of files) {
197
+ const found = this.findDefineAccessCalls(file);
198
+ for (const call2 of found) {
199
+ calls.push({ call: call2, file });
200
+ }
201
+ }
202
+ if (calls.length === 0) {
203
+ return { access: undefined };
204
+ }
205
+ if (calls.length > 1) {
206
+ for (const { call: call2 } of calls.slice(1)) {
207
+ this.addDiagnostic({
208
+ code: "ACCESS_MULTIPLE_DEFINITIONS",
209
+ severity: "error",
210
+ message: "Only one defineAccess() call is allowed per application",
211
+ ...getSourceLocation(call2)
212
+ });
213
+ }
214
+ }
215
+ const first = calls[0];
216
+ if (!first)
217
+ return { access: undefined };
218
+ const { call } = first;
219
+ const access = this.extractAccess(call);
220
+ return { access };
221
+ }
222
+ findDefineAccessCalls(file) {
223
+ const validCalls = [];
224
+ for (const call of file.getDescendantsOfKind(SyntaxKind2.CallExpression)) {
225
+ const expr = call.getExpression();
226
+ if (expr.isKind(SyntaxKind2.Identifier)) {
227
+ if (isFromImport(expr, "@vertz/server")) {
228
+ const name = expr.getText();
229
+ const sourceFile = expr.getSourceFile();
230
+ const importDecls = sourceFile.getImportDeclarations();
231
+ for (const decl of importDecls) {
232
+ if (decl.getModuleSpecifierValue() !== "@vertz/server")
233
+ continue;
234
+ for (const specifier of decl.getNamedImports()) {
235
+ const importedName = specifier.getName();
236
+ const alias = specifier.getAliasNode()?.getText();
237
+ if (importedName === "defineAccess" && (alias === name || !alias && importedName === name)) {
238
+ validCalls.push(call);
239
+ }
240
+ }
241
+ }
242
+ }
243
+ continue;
244
+ }
245
+ if (expr.isKind(SyntaxKind2.PropertyAccessExpression)) {
246
+ const propName = expr.getName();
247
+ if (propName !== "defineAccess")
248
+ continue;
249
+ const obj = expr.getExpression();
250
+ if (!obj.isKind(SyntaxKind2.Identifier))
251
+ continue;
252
+ const sourceFile = obj.getSourceFile();
253
+ const importDecl = sourceFile.getImportDeclarations().find((d) => d.getModuleSpecifierValue() === "@vertz/server" && d.getNamespaceImport()?.getText() === obj.getText());
254
+ if (importDecl) {
255
+ validCalls.push(call);
256
+ }
257
+ }
258
+ }
259
+ return validCalls;
260
+ }
261
+ extractAccess(call) {
262
+ const configObj = extractObjectLiteral(call, 0);
263
+ if (!configObj)
264
+ return;
265
+ const loc = getSourceLocation(call);
266
+ const entities = [];
267
+ const entitiesExpr = getPropertyValue(configObj, "entities");
268
+ if (entitiesExpr?.isKind(SyntaxKind2.ObjectLiteralExpression)) {
269
+ for (const prop of getProperties(entitiesExpr)) {
270
+ const roles = [];
271
+ if (prop.value.isKind(SyntaxKind2.ObjectLiteralExpression)) {
272
+ const rolesExpr = getPropertyValue(prop.value, "roles");
273
+ if (rolesExpr) {
274
+ for (const el of getArrayElements(rolesExpr)) {
275
+ const role = getStringValue(el);
276
+ if (role !== null) {
277
+ roles.push(role);
278
+ } else {
279
+ this.addDiagnostic({
280
+ code: "ACCESS_NON_LITERAL_ROLE",
281
+ severity: "warning",
282
+ message: `Role in entity "${prop.name}" must be a string literal`,
283
+ ...getSourceLocation(el)
284
+ });
285
+ }
286
+ }
287
+ }
288
+ }
289
+ entities.push({ name: prop.name, roles });
290
+ }
291
+ }
292
+ const entitlements = [];
293
+ const seenEntitlements = new Set;
294
+ const entitlementsExpr = getPropertyValue(configObj, "entitlements");
295
+ if (entitlementsExpr?.isKind(SyntaxKind2.ObjectLiteralExpression)) {
296
+ for (const prop of entitlementsExpr.getProperties()) {
297
+ if (prop.isKind(SyntaxKind2.PropertyAssignment) || prop.isKind(SyntaxKind2.MethodDeclaration)) {
298
+ const name = prop.getName();
299
+ const cleanName = name.replace(/^['"]|['"]$/g, "");
300
+ if (seenEntitlements.has(cleanName)) {
301
+ this.addDiagnostic({
302
+ code: "ACCESS_DUPLICATE_ENTITLEMENT",
303
+ severity: "warning",
304
+ message: `Duplicate entitlement "${cleanName}" — only the first occurrence is used`,
305
+ ...getSourceLocation(prop)
306
+ });
307
+ } else {
308
+ seenEntitlements.add(cleanName);
309
+ entitlements.push(cleanName);
310
+ }
311
+ } else if (prop.isKind(SyntaxKind2.SpreadAssignment)) {
312
+ this.addDiagnostic({
313
+ code: "ACCESS_NON_LITERAL_KEY",
314
+ severity: "warning",
315
+ message: "Spread in entitlements object cannot be statically analyzed — these entitlements will not be type-checked",
316
+ ...getSourceLocation(prop)
317
+ });
318
+ }
319
+ }
320
+ }
321
+ const whereClauses = this.extractWhereClauses(configObj);
322
+ return {
323
+ entities,
324
+ entitlements,
325
+ whereClauses,
326
+ sourceFile: loc.sourceFile,
327
+ sourceLine: loc.sourceLine,
328
+ sourceColumn: loc.sourceColumn
329
+ };
330
+ }
331
+ extractWhereClauses(configObj) {
332
+ const clauses = [];
333
+ const entitlementsExpr = getPropertyValue(configObj, "entitlements");
334
+ if (!entitlementsExpr?.isKind(SyntaxKind2.ObjectLiteralExpression))
335
+ return clauses;
336
+ for (const prop of entitlementsExpr.getProperties()) {
337
+ if (!prop.isKind(SyntaxKind2.PropertyAssignment) && !prop.isKind(SyntaxKind2.MethodDeclaration)) {
338
+ continue;
339
+ }
340
+ const entName = prop.getName().replace(/^['"]|['"]$/g, "");
341
+ const whereCalls = this.findWhereCalls(prop);
342
+ const allConditions = [];
343
+ for (const whereCall of whereCalls) {
344
+ const conditions = this.extractWhereConditions(whereCall);
345
+ allConditions.push(...conditions);
346
+ }
347
+ if (allConditions.length > 0) {
348
+ clauses.push({ entitlement: entName, conditions: allConditions });
349
+ }
350
+ }
351
+ return clauses;
352
+ }
353
+ findWhereCalls(expr) {
354
+ const calls = [];
355
+ for (const call of expr.getDescendantsOfKind(SyntaxKind2.CallExpression)) {
356
+ const callExpr = call.getExpression();
357
+ if (callExpr.isKind(SyntaxKind2.PropertyAccessExpression) && callExpr.getName() === "where") {
358
+ calls.push(call);
359
+ }
360
+ }
361
+ return calls;
362
+ }
363
+ extractWhereConditions(call) {
364
+ const conditions = [];
365
+ const arg = call.getArguments()[0];
366
+ if (!arg?.isKind(SyntaxKind2.ObjectLiteralExpression))
367
+ return conditions;
368
+ for (const prop of arg.getProperties()) {
369
+ if (!prop.isKind(SyntaxKind2.PropertyAssignment))
370
+ continue;
371
+ const column = prop.getName().replace(/^['"]|['"]$/g, "");
372
+ const init = prop.getInitializer();
373
+ if (!init)
374
+ continue;
375
+ const condition = this.extractConditionValue(column, init);
376
+ if (condition) {
377
+ conditions.push(condition);
378
+ } else {
379
+ this.addDiagnostic({
380
+ code: "ACCESS_WHERE_NOT_TRANSLATABLE",
381
+ severity: "warning",
382
+ message: `Where condition for column "${column}" cannot be statically analyzed — no RLS policy will be generated`,
383
+ ...getSourceLocation(init)
384
+ });
385
+ }
386
+ }
387
+ return conditions;
388
+ }
389
+ extractConditionValue(column, expr) {
390
+ if (expr.isKind(SyntaxKind2.PropertyAccessExpression)) {
391
+ const text = expr.getText();
392
+ const markerMatch = text.match(/\.\buser\.(id|tenantId)$/);
393
+ if (markerMatch) {
394
+ const marker = `user.${markerMatch[1]}`;
395
+ return { kind: "marker", column, marker };
396
+ }
397
+ }
398
+ const strVal = getStringValue(expr);
399
+ if (strVal !== null) {
400
+ return { kind: "literal", column, value: strVal };
401
+ }
402
+ if (expr.isKind(SyntaxKind2.TrueKeyword)) {
403
+ return { kind: "literal", column, value: true };
404
+ }
405
+ if (expr.isKind(SyntaxKind2.FalseKeyword)) {
406
+ return { kind: "literal", column, value: false };
407
+ }
408
+ if (expr.isKind(SyntaxKind2.NumericLiteral)) {
409
+ return { kind: "literal", column, value: Number(expr.getText()) };
410
+ }
411
+ return;
412
+ }
413
+ }
414
+ // src/analyzers/app-analyzer.ts
415
+ import { SyntaxKind as SyntaxKind3 } from "ts-morph";
416
+
417
+ // src/errors.ts
418
+ function createDiagnostic(options) {
419
+ return { ...options };
420
+ }
421
+ function createDiagnosticFromLocation(location, options) {
422
+ return {
423
+ ...options,
424
+ file: location.sourceFile,
425
+ line: location.sourceLine,
426
+ column: location.sourceColumn
427
+ };
428
+ }
429
+ function hasErrors(diagnostics) {
430
+ return diagnostics.some((d) => d.severity === "error");
431
+ }
432
+ function filterBySeverity(diagnostics, severity) {
433
+ return diagnostics.filter((d) => d.severity === severity);
434
+ }
435
+ function mergeDiagnostics(a, b) {
436
+ return [...a, ...b];
437
+ }
438
+
213
439
  // src/analyzers/app-analyzer.ts
214
440
  class AppAnalyzer extends BaseAnalyzer {
215
441
  async analyze() {
@@ -278,13 +504,13 @@ class AppAnalyzer extends BaseAnalyzer {
278
504
  collectChainedCalls(appCall) {
279
505
  const results = [];
280
506
  let current = appCall;
281
- while (current.getParent()?.isKind(SyntaxKind2.PropertyAccessExpression)) {
507
+ while (current.getParent()?.isKind(SyntaxKind3.PropertyAccessExpression)) {
282
508
  const propAccess = current.getParentOrThrow();
283
- if (!propAccess.isKind(SyntaxKind2.PropertyAccessExpression))
509
+ if (!propAccess.isKind(SyntaxKind3.PropertyAccessExpression))
284
510
  break;
285
511
  const methodName = propAccess.getName();
286
512
  const parentCall = propAccess.getParent();
287
- if (!parentCall?.isKind(SyntaxKind2.CallExpression))
513
+ if (!parentCall?.isKind(SyntaxKind3.CallExpression))
288
514
  break;
289
515
  results.push({ methodName, call: parentCall });
290
516
  current = parentCall;
@@ -301,7 +527,7 @@ class AppAnalyzer extends BaseAnalyzer {
301
527
  continue;
302
528
  const elements = getArrayElements(arrArg);
303
529
  for (const el of elements) {
304
- if (el.isKind(SyntaxKind2.Identifier)) {
530
+ if (el.isKind(SyntaxKind3.Identifier)) {
305
531
  const resolved = resolveIdentifier(el, this.project);
306
532
  middleware.push({
307
533
  name: el.getText(),
@@ -321,7 +547,7 @@ class AppAnalyzer extends BaseAnalyzer {
321
547
  const moduleArg = args.at(0);
322
548
  if (!moduleArg)
323
549
  continue;
324
- const moduleName = moduleArg.isKind(SyntaxKind2.Identifier) ? moduleArg.getText() : undefined;
550
+ const moduleName = moduleArg.isKind(SyntaxKind3.Identifier) ? moduleArg.getText() : undefined;
325
551
  if (!moduleName) {
326
552
  this.addDiagnostic(createDiagnosticFromLocation(getSourceLocation(call), {
327
553
  severity: "warning",
@@ -339,20 +565,20 @@ class AppAnalyzer extends BaseAnalyzer {
339
565
  extractOptions(obj) {
340
566
  const result = {};
341
567
  for (const prop of obj.getProperties()) {
342
- if (!prop.isKind(SyntaxKind2.PropertyAssignment))
568
+ if (!prop.isKind(SyntaxKind3.PropertyAssignment))
343
569
  continue;
344
570
  const name = prop.getName();
345
571
  const init = prop.getInitializerOrThrow();
346
572
  const strValue = getStringValue(init);
347
573
  if (strValue !== null) {
348
574
  result[name] = strValue;
349
- } else if (init.isKind(SyntaxKind2.TrueKeyword)) {
575
+ } else if (init.isKind(SyntaxKind3.TrueKeyword)) {
350
576
  result[name] = true;
351
- } else if (init.isKind(SyntaxKind2.FalseKeyword)) {
577
+ } else if (init.isKind(SyntaxKind3.FalseKeyword)) {
352
578
  result[name] = false;
353
- } else if (init.isKind(SyntaxKind2.NumericLiteral)) {
579
+ } else if (init.isKind(SyntaxKind3.NumericLiteral)) {
354
580
  result[name] = Number(init.getText());
355
- } else if (init.isKind(SyntaxKind2.ObjectLiteralExpression)) {
581
+ } else if (init.isKind(SyntaxKind3.ObjectLiteralExpression)) {
356
582
  result[name] = this.extractOptions(init);
357
583
  }
358
584
  }
@@ -360,7 +586,7 @@ class AppAnalyzer extends BaseAnalyzer {
360
586
  }
361
587
  }
362
588
  // src/analyzers/database-analyzer.ts
363
- import { SyntaxKind as SyntaxKind3 } from "ts-morph";
589
+ import { SyntaxKind as SyntaxKind4 } from "ts-morph";
364
590
  class DatabaseAnalyzer extends BaseAnalyzer {
365
591
  async analyze() {
366
592
  const databases = [];
@@ -376,20 +602,20 @@ class DatabaseAnalyzer extends BaseAnalyzer {
376
602
  }
377
603
  findCreateDbCalls(file) {
378
604
  const validCalls = [];
379
- for (const call of file.getDescendantsOfKind(SyntaxKind3.CallExpression)) {
605
+ for (const call of file.getDescendantsOfKind(SyntaxKind4.CallExpression)) {
380
606
  const expr = call.getExpression();
381
- if (expr.isKind(SyntaxKind3.Identifier)) {
607
+ if (expr.isKind(SyntaxKind4.Identifier)) {
382
608
  if (isFromImport(expr, "@vertz/db")) {
383
609
  validCalls.push(call);
384
610
  }
385
611
  continue;
386
612
  }
387
- if (expr.isKind(SyntaxKind3.PropertyAccessExpression)) {
613
+ if (expr.isKind(SyntaxKind4.PropertyAccessExpression)) {
388
614
  const propName = expr.getName();
389
615
  if (propName !== "createDb")
390
616
  continue;
391
617
  const obj = expr.getExpression();
392
- if (!obj.isKind(SyntaxKind3.Identifier))
618
+ if (!obj.isKind(SyntaxKind4.Identifier))
393
619
  continue;
394
620
  const sourceFile = obj.getSourceFile();
395
621
  const importDecl = sourceFile.getImportDeclarations().find((d) => d.getModuleSpecifierValue() === "@vertz/db" && d.getNamespaceImport()?.getText() === obj.getText());
@@ -415,14 +641,14 @@ class DatabaseAnalyzer extends BaseAnalyzer {
415
641
  return { modelKeys, ...loc };
416
642
  }
417
643
  resolveObjectLiteral(expr) {
418
- if (expr.isKind(SyntaxKind3.ObjectLiteralExpression))
644
+ if (expr.isKind(SyntaxKind4.ObjectLiteralExpression))
419
645
  return expr;
420
- if (expr.isKind(SyntaxKind3.Identifier)) {
646
+ if (expr.isKind(SyntaxKind4.Identifier)) {
421
647
  const defs = expr.getDefinitionNodes();
422
648
  for (const def of defs) {
423
- if (def.isKind(SyntaxKind3.VariableDeclaration)) {
649
+ if (def.isKind(SyntaxKind4.VariableDeclaration)) {
424
650
  const init = def.getInitializer();
425
- if (init?.isKind(SyntaxKind3.ObjectLiteralExpression))
651
+ if (init?.isKind(SyntaxKind4.ObjectLiteralExpression))
426
652
  return init;
427
653
  }
428
654
  }
@@ -432,8 +658,8 @@ class DatabaseAnalyzer extends BaseAnalyzer {
432
658
  extractModelKeys(obj, loc) {
433
659
  const keys = [];
434
660
  for (const prop of obj.getProperties()) {
435
- if (prop.isKind(SyntaxKind3.PropertyAssignment)) {
436
- if (prop.getNameNode().isKind(SyntaxKind3.ComputedPropertyName)) {
661
+ if (prop.isKind(SyntaxKind4.PropertyAssignment)) {
662
+ if (prop.getNameNode().isKind(SyntaxKind4.ComputedPropertyName)) {
437
663
  this.addDiagnostic({
438
664
  severity: "warning",
439
665
  code: "ENTITY_MODEL_NOT_REGISTERED",
@@ -443,9 +669,9 @@ class DatabaseAnalyzer extends BaseAnalyzer {
443
669
  } else {
444
670
  keys.push(prop.getName());
445
671
  }
446
- } else if (prop.isKind(SyntaxKind3.ShorthandPropertyAssignment)) {
672
+ } else if (prop.isKind(SyntaxKind4.ShorthandPropertyAssignment)) {
447
673
  keys.push(prop.getName());
448
- } else if (prop.isKind(SyntaxKind3.SpreadAssignment)) {
674
+ } else if (prop.isKind(SyntaxKind4.SpreadAssignment)) {
449
675
  this.addDiagnostic({
450
676
  severity: "warning",
451
677
  code: "ENTITY_MODEL_NOT_REGISTERED",
@@ -706,7 +932,7 @@ class DependencyGraphAnalyzer extends BaseAnalyzer {
706
932
  }
707
933
  }
708
934
  // src/analyzers/entity-analyzer.ts
709
- import { SyntaxKind as SyntaxKind4 } from "ts-morph";
935
+ import { SyntaxKind as SyntaxKind5 } from "ts-morph";
710
936
  var ENTITY_NAME_PATTERN = /^[a-z][a-z0-9-]*$/;
711
937
  var CRUD_OPS = ["list", "get", "create", "update", "delete"];
712
938
 
@@ -763,9 +989,9 @@ class EntityAnalyzer extends BaseAnalyzer {
763
989
  }
764
990
  findEntityCalls(file) {
765
991
  const validCalls = [];
766
- for (const call of file.getDescendantsOfKind(SyntaxKind4.CallExpression)) {
992
+ for (const call of file.getDescendantsOfKind(SyntaxKind5.CallExpression)) {
767
993
  const expr = call.getExpression();
768
- if (expr.isKind(SyntaxKind4.Identifier)) {
994
+ if (expr.isKind(SyntaxKind5.Identifier)) {
769
995
  const isValid = isFromImport(expr, "@vertz/server");
770
996
  if (!isValid && expr.getText() === "entity") {
771
997
  this.addDiagnostic({
@@ -781,12 +1007,12 @@ class EntityAnalyzer extends BaseAnalyzer {
781
1007
  }
782
1008
  continue;
783
1009
  }
784
- if (expr.isKind(SyntaxKind4.PropertyAccessExpression)) {
1010
+ if (expr.isKind(SyntaxKind5.PropertyAccessExpression)) {
785
1011
  const propName = expr.getName();
786
1012
  if (propName !== "entity")
787
1013
  continue;
788
1014
  const obj = expr.getExpression();
789
- if (!obj.isKind(SyntaxKind4.Identifier))
1015
+ if (!obj.isKind(SyntaxKind5.Identifier))
790
1016
  continue;
791
1017
  const sourceFile = obj.getSourceFile();
792
1018
  const importDecl = sourceFile.getImportDeclarations().find((d) => d.getModuleSpecifierValue() === "@vertz/server" && d.getNamespaceImport()?.getText() === obj.getText());
@@ -846,6 +1072,13 @@ class EntityAnalyzer extends BaseAnalyzer {
846
1072
  const actions = this.extractActions(configObj);
847
1073
  const modelExpr = getPropertyValue(configObj, "model");
848
1074
  const relations = this.extractRelations(configObj, modelExpr ?? undefined);
1075
+ const tenantScopedExpr = getPropertyValue(configObj, "tenantScoped");
1076
+ const tenantScoped = tenantScopedExpr ? getBooleanValue(tenantScopedExpr) : undefined;
1077
+ const tableExpr = getPropertyValue(configObj, "table");
1078
+ const table = tableExpr ? getStringValue(tableExpr) : undefined;
1079
+ if (modelExpr) {
1080
+ this.extractModelTableMetadata(modelExpr, modelRef);
1081
+ }
849
1082
  for (const action of actions) {
850
1083
  if (CRUD_OPS.includes(action.name)) {
851
1084
  this.addDiagnostic({
@@ -867,7 +1100,17 @@ class EntityAnalyzer extends BaseAnalyzer {
867
1100
  }
868
1101
  }
869
1102
  return {
870
- entity: { name, modelRef, access, hooks, actions, relations, ...loc },
1103
+ entity: {
1104
+ name,
1105
+ modelRef,
1106
+ access,
1107
+ hooks,
1108
+ actions,
1109
+ relations,
1110
+ ...tenantScoped !== undefined && tenantScoped !== null ? { tenantScoped } : {},
1111
+ ...table !== undefined && table !== null ? { table } : {},
1112
+ ...loc
1113
+ },
871
1114
  modelExpr: modelExpr ?? undefined
872
1115
  };
873
1116
  }
@@ -882,9 +1125,9 @@ class EntityAnalyzer extends BaseAnalyzer {
882
1125
  });
883
1126
  return null;
884
1127
  }
885
- const variableName = modelExpr.isKind(SyntaxKind4.Identifier) ? modelExpr.getText() : modelExpr.getText();
1128
+ const variableName = modelExpr.isKind(SyntaxKind5.Identifier) ? modelExpr.getText() : modelExpr.getText();
886
1129
  let importSource;
887
- if (modelExpr.isKind(SyntaxKind4.Identifier)) {
1130
+ if (modelExpr.isKind(SyntaxKind5.Identifier)) {
888
1131
  const importInfo = this.findImportForIdentifier(modelExpr);
889
1132
  if (importInfo) {
890
1133
  importSource = importInfo.importDecl.getModuleSpecifierValue();
@@ -1053,7 +1296,7 @@ class EntityAnalyzer extends BaseAnalyzer {
1053
1296
  custom: {}
1054
1297
  };
1055
1298
  const accessExpr = getPropertyValue(configObj, "access");
1056
- if (!accessExpr || !accessExpr.isKind(SyntaxKind4.ObjectLiteralExpression))
1299
+ if (!accessExpr || !accessExpr.isKind(SyntaxKind5.ObjectLiteralExpression))
1057
1300
  return defaults;
1058
1301
  const result = { ...defaults };
1059
1302
  const knownOps = new Set([...CRUD_OPS]);
@@ -1078,14 +1321,14 @@ class EntityAnalyzer extends BaseAnalyzer {
1078
1321
  extractHooks(configObj) {
1079
1322
  const hooks = { before: [], after: [] };
1080
1323
  const beforeExpr = getPropertyValue(configObj, "before");
1081
- if (beforeExpr?.isKind(SyntaxKind4.ObjectLiteralExpression)) {
1324
+ if (beforeExpr?.isKind(SyntaxKind5.ObjectLiteralExpression)) {
1082
1325
  for (const { name } of getProperties(beforeExpr)) {
1083
1326
  if (name === "create" || name === "update")
1084
1327
  hooks.before.push(name);
1085
1328
  }
1086
1329
  }
1087
1330
  const afterExpr = getPropertyValue(configObj, "after");
1088
- if (afterExpr?.isKind(SyntaxKind4.ObjectLiteralExpression)) {
1331
+ if (afterExpr?.isKind(SyntaxKind5.ObjectLiteralExpression)) {
1089
1332
  for (const { name } of getProperties(afterExpr)) {
1090
1333
  if (name === "create" || name === "update" || name === "delete")
1091
1334
  hooks.after.push(name);
@@ -1095,10 +1338,10 @@ class EntityAnalyzer extends BaseAnalyzer {
1095
1338
  }
1096
1339
  extractActions(configObj) {
1097
1340
  const actionsExpr = getPropertyValue(configObj, "actions");
1098
- if (!actionsExpr?.isKind(SyntaxKind4.ObjectLiteralExpression))
1341
+ if (!actionsExpr?.isKind(SyntaxKind5.ObjectLiteralExpression))
1099
1342
  return [];
1100
1343
  return getProperties(actionsExpr).map(({ name, value }) => {
1101
- const actionObj = value.isKind(SyntaxKind4.ObjectLiteralExpression) ? value : null;
1344
+ const actionObj = value.isKind(SyntaxKind5.ObjectLiteralExpression) ? value : null;
1102
1345
  const loc = getSourceLocation(value);
1103
1346
  const bodyExpr = actionObj ? getPropertyValue(actionObj, "body") : null;
1104
1347
  const responseExpr = actionObj ? getPropertyValue(actionObj, "response") : null;
@@ -1158,7 +1401,7 @@ class EntityAnalyzer extends BaseAnalyzer {
1158
1401
  });
1159
1402
  }
1160
1403
  resolveSchemaFromExpression(expr, loc) {
1161
- if (expr.isKind(SyntaxKind4.Identifier)) {
1404
+ if (expr.isKind(SyntaxKind5.Identifier)) {
1162
1405
  const varName = expr.getText();
1163
1406
  return { kind: "named", schemaName: varName, sourceFile: loc.sourceFile };
1164
1407
  }
@@ -1171,7 +1414,7 @@ class EntityAnalyzer extends BaseAnalyzer {
1171
1414
  }
1172
1415
  extractRelations(configObj, modelExpr) {
1173
1416
  const relExpr = getPropertyValue(configObj, "relations");
1174
- if (!relExpr?.isKind(SyntaxKind4.ObjectLiteralExpression))
1417
+ if (!relExpr?.isKind(SyntaxKind5.ObjectLiteralExpression))
1175
1418
  return [];
1176
1419
  const modelRelTypes = modelExpr ? this.resolveModelRelationTypes(modelExpr) : new Map;
1177
1420
  return getProperties(relExpr).filter(({ value }) => {
@@ -1179,16 +1422,74 @@ class EntityAnalyzer extends BaseAnalyzer {
1179
1422
  return boolVal !== false;
1180
1423
  }).map(({ name, value }) => {
1181
1424
  const boolVal = getBooleanValue(value);
1182
- const selection = boolVal === true ? "all" : value.isKind(SyntaxKind4.ObjectLiteralExpression) ? getProperties(value).map((p) => p.name) : "all";
1183
1425
  const relType = modelRelTypes.get(name);
1426
+ if (boolVal === true || !value.isKind(SyntaxKind5.ObjectLiteralExpression)) {
1427
+ return { name, type: relType?.type, entity: relType?.entity, selection: "all" };
1428
+ }
1429
+ const selectExpr = getPropertyValue(value, "select");
1430
+ const selection = selectExpr?.isKind(SyntaxKind5.ObjectLiteralExpression) ? getProperties(selectExpr).map((p) => p.name) : "all";
1431
+ const allowWhereExpr = getPropertyValue(value, "allowWhere");
1432
+ const allowWhere = allowWhereExpr ? getArrayElements(allowWhereExpr).map((el) => getStringValue(el)).filter((s) => s !== null) : undefined;
1433
+ const allowOrderByExpr = getPropertyValue(value, "allowOrderBy");
1434
+ const allowOrderBy = allowOrderByExpr ? getArrayElements(allowOrderByExpr).map((el) => getStringValue(el)).filter((s) => s !== null) : undefined;
1435
+ const maxLimitExpr = getPropertyValue(value, "maxLimit");
1436
+ const maxLimit = maxLimitExpr ? getNumberValue(maxLimitExpr) ?? undefined : undefined;
1184
1437
  return {
1185
1438
  name,
1186
1439
  type: relType?.type,
1187
1440
  entity: relType?.entity,
1188
- selection
1441
+ selection,
1442
+ ...allowWhere ? { allowWhere } : {},
1443
+ ...allowOrderBy ? { allowOrderBy } : {},
1444
+ ...maxLimit !== undefined ? { maxLimit } : {}
1189
1445
  };
1190
1446
  });
1191
1447
  }
1448
+ extractModelTableMetadata(modelExpr, modelRef) {
1449
+ try {
1450
+ const modelType = modelExpr.getType();
1451
+ const tableProp = modelType.getProperty("table");
1452
+ if (!tableProp)
1453
+ return;
1454
+ const tableType = tableProp.getTypeAtLocation(modelExpr);
1455
+ const columnsProp = tableType.getProperty("_columns");
1456
+ if (!columnsProp)
1457
+ return;
1458
+ const columnsType = columnsProp.getTypeAtLocation(modelExpr);
1459
+ const hiddenFields = [];
1460
+ for (const colProp of columnsType.getProperties()) {
1461
+ const colName = colProp.getName();
1462
+ const colType = colProp.getTypeAtLocation(modelExpr);
1463
+ const metaProp = colType.getProperty("_meta");
1464
+ if (!metaProp)
1465
+ continue;
1466
+ const metaType = metaProp.getTypeAtLocation(modelExpr);
1467
+ const primaryProp = metaType.getProperty("primary");
1468
+ if (primaryProp) {
1469
+ const primaryType = primaryProp.getTypeAtLocation(modelExpr);
1470
+ if (primaryType.getText() === "true") {
1471
+ modelRef.primaryKey = colName;
1472
+ }
1473
+ }
1474
+ const annotationsProp = metaType.getProperty("_annotations");
1475
+ if (annotationsProp) {
1476
+ const annotationsType = annotationsProp.getTypeAtLocation(modelExpr);
1477
+ const hiddenProp = annotationsType.getProperty("hidden");
1478
+ if (hiddenProp) {
1479
+ const hiddenType = hiddenProp.getTypeAtLocation(modelExpr);
1480
+ if (hiddenType.getText() === "true") {
1481
+ hiddenFields.push(colName);
1482
+ }
1483
+ }
1484
+ }
1485
+ }
1486
+ if (hiddenFields.length > 0 || columnsType.getProperties().length > 0) {
1487
+ modelRef.hiddenFields = hiddenFields;
1488
+ }
1489
+ } catch (e) {
1490
+ this.debug(`extractModelTableMetadata failed: ${e instanceof Error ? e.message : String(e)}`);
1491
+ }
1492
+ }
1192
1493
  resolveModelRelationTypes(modelExpr) {
1193
1494
  const result = new Map;
1194
1495
  try {
@@ -1266,10 +1567,10 @@ class EntityAnalyzer extends BaseAnalyzer {
1266
1567
  }
1267
1568
  }
1268
1569
  // src/analyzers/env-analyzer.ts
1269
- import { SyntaxKind as SyntaxKind6 } from "ts-morph";
1570
+ import { SyntaxKind as SyntaxKind7 } from "ts-morph";
1270
1571
 
1271
1572
  // src/analyzers/schema-analyzer.ts
1272
- import { SyntaxKind as SyntaxKind5 } from "ts-morph";
1573
+ import { SyntaxKind as SyntaxKind6 } from "ts-morph";
1273
1574
  class SchemaAnalyzer extends BaseAnalyzer {
1274
1575
  async analyze() {
1275
1576
  const schemas = [];
@@ -1279,7 +1580,7 @@ class SchemaAnalyzer extends BaseAnalyzer {
1279
1580
  for (const exportSymbol of file.getExportSymbols()) {
1280
1581
  const declarations = exportSymbol.getDeclarations();
1281
1582
  for (const decl of declarations) {
1282
- if (!decl.isKind(SyntaxKind5.VariableDeclaration))
1583
+ if (!decl.isKind(SyntaxKind6.VariableDeclaration))
1283
1584
  continue;
1284
1585
  const initializer = decl.getInitializer();
1285
1586
  if (!initializer)
@@ -1334,9 +1635,9 @@ function isSchemaExpression(_file, expr) {
1334
1635
  }
1335
1636
  function extractSchemaId(expr) {
1336
1637
  let current = expr;
1337
- while (current.isKind(SyntaxKind5.CallExpression)) {
1638
+ while (current.isKind(SyntaxKind6.CallExpression)) {
1338
1639
  const access = current.getExpression();
1339
- if (access.isKind(SyntaxKind5.PropertyAccessExpression) && access.getName() === "id") {
1640
+ if (access.isKind(SyntaxKind6.PropertyAccessExpression) && access.getName() === "id") {
1340
1641
  const args = current.getArguments();
1341
1642
  if (args.length === 1) {
1342
1643
  const firstArg = args.at(0);
@@ -1345,7 +1646,7 @@ function extractSchemaId(expr) {
1345
1646
  return value;
1346
1647
  }
1347
1648
  }
1348
- if (access.isKind(SyntaxKind5.PropertyAccessExpression)) {
1649
+ if (access.isKind(SyntaxKind6.PropertyAccessExpression)) {
1349
1650
  current = access.getExpression();
1350
1651
  } else {
1351
1652
  break;
@@ -1363,13 +1664,13 @@ function createInlineSchemaRef(sourceFile) {
1363
1664
  return { kind: "inline", sourceFile };
1364
1665
  }
1365
1666
  function findRootIdentifier(expr) {
1366
- if (expr.isKind(SyntaxKind5.CallExpression)) {
1667
+ if (expr.isKind(SyntaxKind6.CallExpression)) {
1367
1668
  return findRootIdentifier(expr.getExpression());
1368
1669
  }
1369
- if (expr.isKind(SyntaxKind5.PropertyAccessExpression)) {
1670
+ if (expr.isKind(SyntaxKind6.PropertyAccessExpression)) {
1370
1671
  return findRootIdentifier(expr.getExpression());
1371
1672
  }
1372
- if (expr.isKind(SyntaxKind5.Identifier)) {
1673
+ if (expr.isKind(SyntaxKind6.Identifier)) {
1373
1674
  return expr;
1374
1675
  }
1375
1676
  return null;
@@ -1398,7 +1699,7 @@ class EnvAnalyzer extends BaseAnalyzer {
1398
1699
  const loadFiles = loadExpr ? getArrayElements(loadExpr).map((e) => getStringValue(e)).filter((v) => v !== null) : [];
1399
1700
  const schemaExpr = getPropertyValue(obj, "schema");
1400
1701
  let schema;
1401
- if (schemaExpr?.isKind(SyntaxKind6.Identifier)) {
1702
+ if (schemaExpr?.isKind(SyntaxKind7.Identifier)) {
1402
1703
  schema = createNamedSchemaRef(schemaExpr.getText(), file.getFilePath());
1403
1704
  }
1404
1705
  env = {
@@ -1413,10 +1714,10 @@ class EnvAnalyzer extends BaseAnalyzer {
1413
1714
  }
1414
1715
  }
1415
1716
  // src/analyzers/middleware-analyzer.ts
1416
- import { SyntaxKind as SyntaxKind8 } from "ts-morph";
1717
+ import { SyntaxKind as SyntaxKind9 } from "ts-morph";
1417
1718
 
1418
1719
  // src/analyzers/service-analyzer.ts
1419
- import { SyntaxKind as SyntaxKind7 } from "ts-morph";
1720
+ import { SyntaxKind as SyntaxKind8 } from "ts-morph";
1420
1721
  class ServiceAnalyzer extends BaseAnalyzer {
1421
1722
  async analyze() {
1422
1723
  return { services: [] };
@@ -1447,7 +1748,7 @@ class ServiceAnalyzer extends BaseAnalyzer {
1447
1748
  }
1448
1749
  function parseInjectFromObj(obj) {
1449
1750
  const injectExpr = getPropertyValue(obj, "inject");
1450
- if (!injectExpr?.isKind(SyntaxKind7.ObjectLiteralExpression))
1751
+ if (!injectExpr?.isKind(SyntaxKind8.ObjectLiteralExpression))
1451
1752
  return [];
1452
1753
  return parseInjectRefs(injectExpr);
1453
1754
  }
@@ -1459,27 +1760,27 @@ function parseMethodsFromObj(obj) {
1459
1760
  }
1460
1761
  function parseInjectRefs(obj) {
1461
1762
  return getProperties(obj).map(({ name, value }) => {
1462
- const resolvedToken = value.isKind(SyntaxKind7.Identifier) ? value.getText() : name;
1763
+ const resolvedToken = value.isKind(SyntaxKind8.Identifier) ? value.getText() : name;
1463
1764
  return { localName: name, resolvedToken };
1464
1765
  });
1465
1766
  }
1466
1767
  function extractMethodSignatures(expr) {
1467
- if (!expr.isKind(SyntaxKind7.ArrowFunction) && !expr.isKind(SyntaxKind7.FunctionExpression)) {
1768
+ if (!expr.isKind(SyntaxKind8.ArrowFunction) && !expr.isKind(SyntaxKind8.FunctionExpression)) {
1468
1769
  return [];
1469
1770
  }
1470
1771
  const body = expr.getBody();
1471
1772
  let returnObj = null;
1472
- if (body.isKind(SyntaxKind7.ObjectLiteralExpression)) {
1773
+ if (body.isKind(SyntaxKind8.ObjectLiteralExpression)) {
1473
1774
  returnObj = body;
1474
- } else if (body.isKind(SyntaxKind7.ParenthesizedExpression)) {
1775
+ } else if (body.isKind(SyntaxKind8.ParenthesizedExpression)) {
1475
1776
  const inner = body.getExpression();
1476
- if (inner.isKind(SyntaxKind7.ObjectLiteralExpression)) {
1777
+ if (inner.isKind(SyntaxKind8.ObjectLiteralExpression)) {
1477
1778
  returnObj = inner;
1478
1779
  }
1479
- } else if (body.isKind(SyntaxKind7.Block)) {
1480
- const returnStmt = body.getStatements().find((s) => s.isKind(SyntaxKind7.ReturnStatement));
1481
- const retExpr = returnStmt?.asKind(SyntaxKind7.ReturnStatement)?.getExpression();
1482
- if (retExpr?.isKind(SyntaxKind7.ObjectLiteralExpression)) {
1780
+ } else if (body.isKind(SyntaxKind8.Block)) {
1781
+ const returnStmt = body.getStatements().find((s) => s.isKind(SyntaxKind8.ReturnStatement));
1782
+ const retExpr = returnStmt?.asKind(SyntaxKind8.ReturnStatement)?.getExpression();
1783
+ if (retExpr?.isKind(SyntaxKind8.ObjectLiteralExpression)) {
1483
1784
  returnObj = retExpr;
1484
1785
  }
1485
1786
  }
@@ -1487,7 +1788,7 @@ function extractMethodSignatures(expr) {
1487
1788
  return [];
1488
1789
  const methods = [];
1489
1790
  for (const prop of returnObj.getProperties()) {
1490
- if (!prop.isKind(SyntaxKind7.PropertyAssignment))
1791
+ if (!prop.isKind(SyntaxKind8.PropertyAssignment))
1491
1792
  continue;
1492
1793
  const methodName = prop.getName();
1493
1794
  const init = prop.getInitializer();
@@ -1504,7 +1805,7 @@ function extractMethodSignatures(expr) {
1504
1805
  return methods;
1505
1806
  }
1506
1807
  function extractFunctionParams(expr) {
1507
- if (!expr.isKind(SyntaxKind7.ArrowFunction) && !expr.isKind(SyntaxKind7.FunctionExpression)) {
1808
+ if (!expr.isKind(SyntaxKind8.ArrowFunction) && !expr.isKind(SyntaxKind8.FunctionExpression)) {
1508
1809
  return [];
1509
1810
  }
1510
1811
  return expr.getParameters().map((p) => ({
@@ -1513,7 +1814,7 @@ function extractFunctionParams(expr) {
1513
1814
  }));
1514
1815
  }
1515
1816
  function inferReturnType(expr) {
1516
- if (expr.isKind(SyntaxKind7.ArrowFunction) || expr.isKind(SyntaxKind7.FunctionExpression)) {
1817
+ if (expr.isKind(SyntaxKind8.ArrowFunction) || expr.isKind(SyntaxKind8.FunctionExpression)) {
1517
1818
  const retType = expr.getReturnType();
1518
1819
  return retType.getText(expr);
1519
1820
  }
@@ -1570,7 +1871,7 @@ class MiddlewareAnalyzer extends BaseAnalyzer {
1570
1871
  continue;
1571
1872
  }
1572
1873
  const injectExpr = getPropertyValue(obj, "inject");
1573
- const inject = injectExpr?.isKind(SyntaxKind8.ObjectLiteralExpression) ? parseInjectRefs(injectExpr) : [];
1874
+ const inject = injectExpr?.isKind(SyntaxKind9.ObjectLiteralExpression) ? parseInjectRefs(injectExpr) : [];
1574
1875
  const filePath = file.getFilePath();
1575
1876
  const headers = this.resolveSchemaRef(obj, "headers", filePath);
1576
1877
  const params = this.resolveSchemaRef(obj, "params", filePath);
@@ -1597,7 +1898,7 @@ class MiddlewareAnalyzer extends BaseAnalyzer {
1597
1898
  const expr = getPropertyValue(obj, prop);
1598
1899
  if (!expr)
1599
1900
  return;
1600
- if (expr.isKind(SyntaxKind8.Identifier)) {
1901
+ if (expr.isKind(SyntaxKind9.Identifier)) {
1601
1902
  const resolved = resolveIdentifier(expr, this.project);
1602
1903
  const resolvedPath = resolved ? resolved.sourceFile.getFilePath() : filePath;
1603
1904
  return createNamedSchemaRef(expr.getText(), resolvedPath);
@@ -1609,7 +1910,7 @@ class MiddlewareAnalyzer extends BaseAnalyzer {
1609
1910
  }
1610
1911
  }
1611
1912
  // src/analyzers/module-analyzer.ts
1612
- import { SyntaxKind as SyntaxKind9 } from "ts-morph";
1913
+ import { SyntaxKind as SyntaxKind10 } from "ts-morph";
1613
1914
  class ModuleAnalyzer extends BaseAnalyzer {
1614
1915
  async analyze() {
1615
1916
  const modules = [];
@@ -1632,10 +1933,10 @@ class ModuleAnalyzer extends BaseAnalyzer {
1632
1933
  }
1633
1934
  const varName = getVariableNameForCall(call);
1634
1935
  const importsExpr = getPropertyValue(obj, "imports");
1635
- const imports = importsExpr?.isKind(SyntaxKind9.ObjectLiteralExpression) ? parseImports(importsExpr) : [];
1936
+ const imports = importsExpr?.isKind(SyntaxKind10.ObjectLiteralExpression) ? parseImports(importsExpr) : [];
1636
1937
  const optionsExpr = getPropertyValue(obj, "options");
1637
1938
  let options;
1638
- if (optionsExpr?.isKind(SyntaxKind9.Identifier)) {
1939
+ if (optionsExpr?.isKind(SyntaxKind10.Identifier)) {
1639
1940
  options = createNamedSchemaRef(optionsExpr.getText(), file.getFilePath());
1640
1941
  }
1641
1942
  const loc = getSourceLocation(call);
@@ -1661,7 +1962,7 @@ class ModuleAnalyzer extends BaseAnalyzer {
1661
1962
  if (args.length < 2)
1662
1963
  continue;
1663
1964
  const defArg = args.at(0);
1664
- if (!defArg?.isKind(SyntaxKind9.Identifier))
1965
+ if (!defArg?.isKind(SyntaxKind10.Identifier))
1665
1966
  continue;
1666
1967
  const defVarName = defArg.getText();
1667
1968
  const idx = defVarToIndex.get(defVarName);
@@ -1689,12 +1990,12 @@ function parseImports(obj) {
1689
1990
  }));
1690
1991
  }
1691
1992
  function extractIdentifierNames(expr) {
1692
- if (!expr.isKind(SyntaxKind9.ArrayLiteralExpression))
1993
+ if (!expr.isKind(SyntaxKind10.ArrayLiteralExpression))
1693
1994
  return [];
1694
- return expr.getElements().filter((e) => e.isKind(SyntaxKind9.Identifier)).map((e) => e.getText());
1995
+ return expr.getElements().filter((e) => e.isKind(SyntaxKind10.Identifier)).map((e) => e.getText());
1695
1996
  }
1696
1997
  // src/analyzers/route-analyzer.ts
1697
- import { SyntaxKind as SyntaxKind10 } from "ts-morph";
1998
+ import { SyntaxKind as SyntaxKind11 } from "ts-morph";
1698
1999
  var HTTP_METHODS = {
1699
2000
  get: "GET",
1700
2001
  post: "POST",
@@ -1731,7 +2032,7 @@ class RouteAnalyzer extends BaseAnalyzer {
1731
2032
  }
1732
2033
  const loc = getSourceLocation(call);
1733
2034
  const injectExpr = obj ? getPropertyValue(obj, "inject") : null;
1734
- const inject = injectExpr?.isKind(SyntaxKind10.ObjectLiteralExpression) ? parseInjectRefs(injectExpr) : [];
2035
+ const inject = injectExpr?.isKind(SyntaxKind11.ObjectLiteralExpression) ? parseInjectRefs(injectExpr) : [];
1735
2036
  const routes = this.extractRoutes(file, varName, prefix, moduleName);
1736
2037
  routers.push({
1737
2038
  name: varName,
@@ -1748,15 +2049,15 @@ class RouteAnalyzer extends BaseAnalyzer {
1748
2049
  return { routers };
1749
2050
  }
1750
2051
  detectUnknownRouterCalls(file, knownModuleDefVars) {
1751
- const allCalls = file.getDescendantsOfKind(SyntaxKind10.CallExpression);
2052
+ const allCalls = file.getDescendantsOfKind(SyntaxKind11.CallExpression);
1752
2053
  for (const call of allCalls) {
1753
2054
  const expr = call.getExpression();
1754
- if (!expr.isKind(SyntaxKind10.PropertyAccessExpression))
2055
+ if (!expr.isKind(SyntaxKind11.PropertyAccessExpression))
1755
2056
  continue;
1756
2057
  if (expr.getName() !== "router")
1757
2058
  continue;
1758
2059
  const obj = expr.getExpression();
1759
- if (!obj.isKind(SyntaxKind10.Identifier))
2060
+ if (!obj.isKind(SyntaxKind11.Identifier))
1760
2061
  continue;
1761
2062
  if (knownModuleDefVars.has(obj.getText()))
1762
2063
  continue;
@@ -1790,25 +2091,25 @@ class RouteAnalyzer extends BaseAnalyzer {
1790
2091
  return routes;
1791
2092
  }
1792
2093
  findChainedHttpCalls(file, routerVarName, methodName) {
1793
- return file.getDescendantsOfKind(SyntaxKind10.CallExpression).filter((call) => {
2094
+ return file.getDescendantsOfKind(SyntaxKind11.CallExpression).filter((call) => {
1794
2095
  const expr = call.getExpression();
1795
- if (!expr.isKind(SyntaxKind10.PropertyAccessExpression))
2096
+ if (!expr.isKind(SyntaxKind11.PropertyAccessExpression))
1796
2097
  return false;
1797
2098
  if (expr.getName() !== methodName)
1798
2099
  return false;
1799
2100
  const obj = expr.getExpression();
1800
- if (!obj.isKind(SyntaxKind10.CallExpression))
2101
+ if (!obj.isKind(SyntaxKind11.CallExpression))
1801
2102
  return false;
1802
2103
  return this.chainResolvesToVariable(obj, routerVarName);
1803
2104
  });
1804
2105
  }
1805
2106
  chainResolvesToVariable(expr, varName) {
1806
- if (expr.isKind(SyntaxKind10.Identifier)) {
2107
+ if (expr.isKind(SyntaxKind11.Identifier)) {
1807
2108
  return expr.getText() === varName;
1808
2109
  }
1809
- if (expr.isKind(SyntaxKind10.CallExpression)) {
2110
+ if (expr.isKind(SyntaxKind11.CallExpression)) {
1810
2111
  const inner = expr.getExpression();
1811
- if (inner.isKind(SyntaxKind10.PropertyAccessExpression)) {
2112
+ if (inner.isKind(SyntaxKind11.PropertyAccessExpression)) {
1812
2113
  return this.chainResolvesToVariable(inner.getExpression(), varName);
1813
2114
  }
1814
2115
  }
@@ -1882,7 +2183,7 @@ class RouteAnalyzer extends BaseAnalyzer {
1882
2183
  const expr = getPropertyValue(obj, prop);
1883
2184
  if (!expr)
1884
2185
  return;
1885
- if (expr.isKind(SyntaxKind10.Identifier)) {
2186
+ if (expr.isKind(SyntaxKind11.Identifier)) {
1886
2187
  const resolved = resolveIdentifier(expr, this.project);
1887
2188
  const resolvedPath = resolved ? resolved.sourceFile.getFilePath() : filePath;
1888
2189
  return createNamedSchemaRef(expr.getText(), resolvedPath);
@@ -1897,7 +2198,7 @@ class RouteAnalyzer extends BaseAnalyzer {
1897
2198
  if (!expr)
1898
2199
  return [];
1899
2200
  const elements = getArrayElements(expr);
1900
- return elements.filter((el) => el.isKind(SyntaxKind10.Identifier)).map((el) => {
2201
+ return elements.filter((el) => el.isKind(SyntaxKind11.Identifier)).map((el) => {
1901
2202
  const resolved = resolveIdentifier(el, this.project);
1902
2203
  return {
1903
2204
  name: el.getText(),
@@ -1907,9 +2208,9 @@ class RouteAnalyzer extends BaseAnalyzer {
1907
2208
  }
1908
2209
  generateOperationId(moduleName, method, path, handlerExpr, usedIds) {
1909
2210
  let handlerName = null;
1910
- if (handlerExpr?.isKind(SyntaxKind10.Identifier)) {
2211
+ if (handlerExpr?.isKind(SyntaxKind11.Identifier)) {
1911
2212
  handlerName = handlerExpr.getText();
1912
- } else if (handlerExpr?.isKind(SyntaxKind10.PropertyAccessExpression)) {
2213
+ } else if (handlerExpr?.isKind(SyntaxKind11.PropertyAccessExpression)) {
1913
2214
  handlerName = handlerExpr.getName();
1914
2215
  }
1915
2216
  const id = handlerName ? `${moduleName}_${handlerName}` : `${moduleName}_${method.toLowerCase()}_${sanitizePath(path)}`;
@@ -3270,6 +3571,7 @@ class Compiler {
3270
3571
  const appResult = await analyzers.app.analyze();
3271
3572
  const entityResult = await analyzers.entity.analyze();
3272
3573
  const databaseResult = await analyzers.database.analyze();
3574
+ const accessResult = await analyzers.access.analyze();
3273
3575
  const depGraphResult = await analyzers.dependencyGraph.analyze();
3274
3576
  ir.env = envResult.env;
3275
3577
  ir.schemas = schemaResult.schemas;
@@ -3278,8 +3580,9 @@ class Compiler {
3278
3580
  ir.app = appResult.app;
3279
3581
  ir.entities = entityResult.entities;
3280
3582
  ir.databases = databaseResult.databases;
3583
+ ir.access = accessResult.access;
3281
3584
  ir.dependencyGraph = depGraphResult.graph;
3282
- ir.diagnostics.push(...analyzers.env.getDiagnostics(), ...analyzers.schema.getDiagnostics(), ...analyzers.middleware.getDiagnostics(), ...analyzers.module.getDiagnostics(), ...analyzers.app.getDiagnostics(), ...analyzers.entity.getDiagnostics(), ...analyzers.database.getDiagnostics(), ...analyzers.dependencyGraph.getDiagnostics());
3585
+ ir.diagnostics.push(...analyzers.env.getDiagnostics(), ...analyzers.schema.getDiagnostics(), ...analyzers.middleware.getDiagnostics(), ...analyzers.module.getDiagnostics(), ...analyzers.app.getDiagnostics(), ...analyzers.entity.getDiagnostics(), ...analyzers.database.getDiagnostics(), ...analyzers.access.getDiagnostics(), ...analyzers.dependencyGraph.getDiagnostics());
3283
3586
  injectEntityRoutes(ir);
3284
3587
  const collisionDiags = detectRouteCollisions(ir);
3285
3588
  ir.diagnostics.push(...collisionDiags);
@@ -3324,6 +3627,7 @@ function createCompiler(config) {
3324
3627
  app: new AppAnalyzer(project, resolved),
3325
3628
  entity: new EntityAnalyzer(project, resolved),
3326
3629
  database: new DatabaseAnalyzer(project, resolved),
3630
+ access: new AccessAnalyzer(project, resolved),
3327
3631
  dependencyGraph: new DependencyGraphAnalyzer(project, resolved)
3328
3632
  },
3329
3633
  validators: [
@@ -3693,5 +3997,6 @@ export {
3693
3997
  BootGenerator,
3694
3998
  BaseGenerator,
3695
3999
  BaseAnalyzer,
3696
- AppAnalyzer
4000
+ AppAnalyzer,
4001
+ AccessAnalyzer
3697
4002
  };
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@vertz/compiler",
3
- "version": "0.2.14",
3
+ "version": "0.2.16",
4
4
  "type": "module",
5
5
  "license": "MIT",
6
6
  "description": "Vertz compiler — internal, no stability guarantee",