@supacloud/compiler 0.4.1 → 0.6.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.
@@ -0,0 +1,21 @@
1
+ import type { ApplicationGraph, Diagnostic } from "./types";
2
+ export interface DoctorResult {
3
+ checks: Array<{
4
+ name: string;
5
+ ok: boolean;
6
+ detail: string;
7
+ }>;
8
+ diagnostics: ApplicationGraph["diagnostics"];
9
+ errors: number;
10
+ }
11
+ export declare function formatGraph(graph: ApplicationGraph): string;
12
+ export declare function explainGraph(graph: ApplicationGraph, subject: string): string;
13
+ export declare function doctorProject(rootDir: string, outDir: string, graph: ApplicationGraph, upToDate: boolean, diagnostics?: Diagnostic[]): DoctorResult;
14
+ /**
15
+ * Exports the application module architecture as a Mermaid graph diagram.
16
+ */
17
+ export declare function exportGraphMermaid(graph: ApplicationGraph): string;
18
+ /**
19
+ * Exports the application module architecture as a Graphviz DOT script.
20
+ */
21
+ export declare function exportGraphDot(graph: ApplicationGraph): string;
@@ -0,0 +1,30 @@
1
+ import * as ts from "@typescript/typescript6";
2
+ import { type TraitRecord } from "./traits";
3
+ export interface IncrementalProgramSession {
4
+ /** The current TypeScript program used as the semantic compilation boundary. */
5
+ getProgram(): ts.Program;
6
+ /** The current TypeScript type checker. */
7
+ getTypeChecker(): ts.TypeChecker;
8
+ /** Update the program while preserving TypeScript's incremental state. */
9
+ update(rootNames: string[], changedPaths?: string[]): ProgramUpdate;
10
+ /** Local Angular-style metadata records compiled from the current Program. */
11
+ getTraits(): readonly TraitRecord[];
12
+ /** TypeScript syntactic diagnostics for the current Program. */
13
+ getDiagnostics(): readonly ts.Diagnostic[];
14
+ /** Emit the current TypeScript program using the builder's incremental state. */
15
+ emit(): ts.EmitResult;
16
+ /** Drop the retained builder and source versions. */
17
+ reset(): void;
18
+ }
19
+ export interface ProgramUpdate {
20
+ changedFiles: string[];
21
+ reusedFiles: string[];
22
+ program: ts.Program;
23
+ }
24
+ /**
25
+ * Angular-style semantic boundary around TypeScript's incremental program.
26
+ *
27
+ * This deliberately owns only TypeScript program state. SupaCloud metadata
28
+ * handlers and ApplicationGraph linking remain separate layers.
29
+ */
30
+ export declare function createIncrementalProgramSession(projectRoot: string): IncrementalProgramSession;
@@ -0,0 +1,32 @@
1
+ import * as ts from "@typescript/typescript6";
2
+ export type TraitKind = "module" | "injectable" | "controller" | "command" | "query" | "defineModule" | "injectionToken";
3
+ export interface TraitRecord {
4
+ kind: TraitKind;
5
+ name: string;
6
+ file: string;
7
+ start: number;
8
+ end: number;
9
+ fingerprint: string;
10
+ }
11
+ export interface TraitCompilation {
12
+ byFile: Map<string, TraitRecord[]>;
13
+ all: TraitRecord[];
14
+ }
15
+ export interface TraitHandler {
16
+ readonly kind: TraitKind;
17
+ detect(node: ts.Node): string | undefined;
18
+ }
19
+ export declare class TraitCompiler {
20
+ private readonly handlers;
21
+ constructor(handlers?: readonly TraitHandler[]);
22
+ /**
23
+ * Angular-style local metadata compiler.
24
+ *
25
+ * This pass is intentionally syntax-only. It discovers candidate declarations
26
+ * cheaply; handlers that need symbols or types run later against the Program's
27
+ * TypeChecker.
28
+ */
29
+ compile(program: ts.Program, previous: TraitCompilation | undefined, changedFiles: Set<string>): TraitCompilation;
30
+ private compileSourceFile;
31
+ }
32
+ export declare function compileTraits(program: ts.Program, previous: TraitCompilation | undefined, changedFiles: Set<string>): TraitCompilation;
@@ -0,0 +1,9 @@
1
+ import type { Diagnostic, TypeSafetyOptions } from "./types";
2
+ export interface TypeSafetyScanOptions extends TypeSafetyOptions {
3
+ rootDir: string;
4
+ include?: string[];
5
+ outDir?: string;
6
+ strict?: boolean;
7
+ }
8
+ export declare function scanGeneratedArtifacts(artifacts: Record<string, string | undefined>, strict?: boolean): Diagnostic[];
9
+ export declare function scanProductionSource(options: TypeSafetyScanOptions): Diagnostic[];
package/dist/types.d.ts CHANGED
@@ -2,15 +2,46 @@
2
2
  * ApplicationGraph: Static application graph built by the compiler from source AST.
3
3
  * No runtime reflection or container; all metadata is explicitly represented here and in generated code.
4
4
  */
5
+ import type { IncrementalProgramSession } from "./program";
5
6
  export type Scope = "application" | "request" | "job";
6
7
  export type ProviderKind = "class" | "value" | "factory" | "existing";
7
8
  export type TokenKind = "injection-token" | "class";
9
+ export interface FunctionalInjectNode {
10
+ /** Logical token name used by the application graph. */
11
+ token: string;
12
+ /** Source expression used to identify the runtime token. */
13
+ expression: string;
14
+ /** Relative source module path for the token expression. */
15
+ importPath?: string;
16
+ /** Package module specifier for a library token. */
17
+ importModule?: string;
18
+ optional?: boolean;
19
+ self?: boolean;
20
+ skipSelf?: boolean;
21
+ host?: boolean;
22
+ }
23
+ export interface AspectRefNode {
24
+ /** Exported symbol name used in the generated static import. */
25
+ name: string;
26
+ /** Source expression retained for diagnostics and manifest inspection. */
27
+ expression: string;
28
+ /** Relative source module path for a project-local aspect. */
29
+ importPath?: string;
30
+ /** Package module specifier for a library aspect. */
31
+ importModule?: string;
32
+ }
8
33
  export interface Diagnostic {
9
34
  severity: "error" | "warn";
10
35
  code: string;
11
36
  message: string;
12
37
  file?: string;
13
38
  line?: number;
39
+ /** Actionable Angular Ivy-style remediation hint. */
40
+ suggestion?: string;
41
+ /** Standardized compiler diagnostic code (e.g. SC1001) modeled after Angular ngtsc error codes. */
42
+ errorCode?: string;
43
+ /** Documentation URL for this diagnostic. */
44
+ docsUrl?: string;
14
45
  }
15
46
  export interface ProviderNode {
16
47
  /** Token name (InjectionToken variable name or class name). */
@@ -24,11 +55,36 @@ export interface ProviderNode {
24
55
  scope: Scope;
25
56
  /** Token names in constructor/factory parameter order. */
26
57
  deps: string[];
58
+ /** Parameter tokens that are marked @Optional() (receive undefined if unresolved). */
59
+ optionalDeps?: string[];
60
+ /** Parameter tokens marked @Self() (must be resolved from current module's own providers). */
61
+ selfDeps?: string[];
62
+ /** Parameter tokens marked @SkipSelf() (must NOT be resolved from current module's own providers). */
63
+ skipSelfDeps?: string[];
64
+ /** Parameter tokens marked @Host(). */
65
+ hostDeps?: string[];
66
+ /** Property-level Angular functional inject() calls compiled into a static context. */
67
+ functionalInjects?: FunctionalInjectNode[];
68
+ /** When true, multiple providers can contribute to this token as an array of instances (Angular multi-providers). */
69
+ multi?: boolean;
70
+ /** Automatically provided in the root injector context without manual module declaration (Angular-style). */
71
+ providedIn?: "root";
72
+ /** Class implements OnDestroy interface or onDestroy method. */
73
+ hasOnDestroy?: boolean;
27
74
  exported: boolean;
28
75
  file: string;
29
76
  line: number;
30
77
  /** Relative module path of useClass/useFactory/useValue symbol (for import generation). */
31
78
  importPath?: string;
79
+ /** Package module specifier for providers supplied by a library. */
80
+ importModule?: string;
81
+ }
82
+ export interface HandlerParamNode {
83
+ name: string;
84
+ kind: "param" | "query" | "body" | "headers" | "context" | "unknown";
85
+ bindingName?: string;
86
+ transform?: "number" | "boolean" | "string";
87
+ default?: unknown;
32
88
  }
33
89
  export interface RouteNode {
34
90
  method: "GET" | "POST" | "PUT" | "PATCH" | "DELETE" | "HEAD" | "OPTIONS";
@@ -40,12 +96,60 @@ export interface RouteNode {
40
96
  response?: string;
41
97
  /** @Command-decorated class explicitly bound by the route. */
42
98
  command?: string;
99
+ /** Route guards executed before handler (Angular CanActivateFn style). */
100
+ guards?: string[];
101
+ /** Route matching guards executed before route activation (Angular CanMatchFn style). */
102
+ canMatch?: string[];
103
+ /** Route deactivation guards executed before leaving route (Angular CanDeactivateFn style). */
104
+ canDeactivate?: string[];
105
+ /** Route resolvers executed before handler (Angular ResolveFn style). */
106
+ resolvers?: Record<string, string>;
107
+ /** Route redirect target (Angular Router style). */
108
+ redirectTo?: string;
109
+ /** Route redirect matching rule (Angular Router style). */
110
+ pathMatch?: "full" | "prefix";
111
+ /** Path parameters parsed from route path (e.g. :id -> 'id'). */
112
+ pathParams?: string[];
113
+ /** Handler method parameter bindings declared via @Param('name'). */
114
+ paramBindings?: string[];
115
+ /** Handler method query bindings declared via @Query('name'). */
116
+ queryBindings?: string[];
117
+ /** Handler method has @Body() binding. */
118
+ hasBodyBinding?: boolean;
119
+ /** Parameter transforms declared via @Param({ transform: ... }) */
120
+ paramTransforms?: Record<string, "number" | "boolean" | "string">;
121
+ /** Parameter defaults declared via @Param({ default: ... }) */
122
+ paramDefaults?: Record<string, unknown>;
123
+ /** Query transforms declared via @Query({ transform: ... }) */
124
+ queryTransforms?: Record<string, "number" | "boolean" | "string">;
125
+ /** Query defaults declared via @Query({ default: ... }) */
126
+ queryDefaults?: Record<string, unknown>;
127
+ /** Route title (Angular Route.title style). */
128
+ title?: string;
129
+ /** Route static metadata dictionary (Angular Route.data style). */
130
+ data?: Record<string, unknown>;
131
+ /** Detailed method parameter metadata for compile-time typed invoker generation. */
132
+ handlerParams?: HandlerParamNode[];
133
+ /** Explicit aspects applied around this route. */
134
+ aspects?: AspectRefNode[];
43
135
  }
44
136
  export interface ControllerNode {
45
137
  className: string;
46
138
  path: string;
47
139
  scope: Scope;
48
140
  deps: string[];
141
+ /** Class implements OnDestroy interface or onDestroy method. */
142
+ hasOnDestroy?: boolean;
143
+ /** Parameter tokens marked @Optional(). */
144
+ optionalDeps?: string[];
145
+ selfDeps?: string[];
146
+ skipSelfDeps?: string[];
147
+ /** Parameter tokens marked @Host(). */
148
+ hostDeps?: string[];
149
+ /** Property-level Angular functional inject() calls compiled into a static context. */
150
+ functionalInjects?: FunctionalInjectNode[];
151
+ /** Automatically registered without manual module declaration (Angular standalone controller style). */
152
+ standalone?: boolean;
49
153
  routes: RouteNode[];
50
154
  file: string;
51
155
  importPath: string;
@@ -59,6 +163,16 @@ export interface CommandNode {
59
163
  transaction: "required" | "none";
60
164
  audit?: string;
61
165
  idempotency: "required" | "none";
166
+ /** Automatically registered without manual module declaration. */
167
+ standalone?: boolean;
168
+ /** Explicit aspects applied around this command. */
169
+ aspects?: AspectRefNode[];
170
+ }
171
+ export interface JobNode {
172
+ className: string;
173
+ name: string;
174
+ scope: Scope;
175
+ aspects?: AspectRefNode[];
62
176
  }
63
177
  export interface QueryNode {
64
178
  className: string;
@@ -77,7 +191,10 @@ export interface ModuleNode {
77
191
  providers: ProviderNode[];
78
192
  controllers: ControllerNode[];
79
193
  commands: CommandNode[];
194
+ jobs?: JobNode[];
80
195
  queries: QueryNode[];
196
+ /** Explicit aspects applied to all routes and commands in this module. */
197
+ aspects?: AspectRefNode[];
81
198
  /** Exported token names. */
82
199
  exports: string[];
83
200
  }
@@ -96,6 +213,11 @@ export interface ApplicationGraph {
96
213
  * Omitted from app.manifest.json.
97
214
  */
98
215
  tokenNames?: Record<string, string>;
216
+ /** Incremental cache statistics (modules reused vs reanalyzed). */
217
+ cacheStats?: {
218
+ reusedModules: string[];
219
+ reanalyzedModules: string[];
220
+ };
99
221
  }
100
222
  export interface CompileOptions {
101
223
  /** Project root directory (containing tsconfig). */
@@ -118,6 +240,31 @@ export interface CompileOptions {
118
240
  disallowControllerDirectDb?: boolean;
119
241
  /** Detect modules declared in the project that are unreachable from any root module. */
120
242
  detectOrphanModules?: boolean;
243
+ /** Write generated artifacts even when error-level diagnostics exist (default: true). */
244
+ writeOnError?: boolean;
245
+ /** Generate typed API client in client.ts (default: false). */
246
+ generateClient?: boolean;
247
+ /** Generate typed permissions registry in permissions.ts (default: false). */
248
+ generatePermissions?: boolean;
249
+ /** Prune unused root providers from compiled output (Angular Ivy AOT tree-shaking). */
250
+ treeShakeUnusedProviders?: boolean;
251
+ /** Incremental dependency graph cache. */
252
+ cache?: DependencyGraphCache;
253
+ /**
254
+ * Changed source paths supplied by the watch/incremental driver.
255
+ * This is an implementation hint and does not change the public graph shape.
256
+ */
257
+ changedPaths?: string[];
258
+ /** Type-safety gates for generated artifacts and production source. */
259
+ typeSafety?: TypeSafetyOptions;
260
+ }
261
+ export interface TypeSafetyOptions {
262
+ /** Reject the `any` keyword in generated TypeScript artifacts. */
263
+ noAnyInGenerated?: boolean;
264
+ /** Scan non-test production source for unsafe type escapes and widening. */
265
+ scanProductionSource?: boolean;
266
+ /** Additional relative glob patterns excluded from the production-source scan. */
267
+ exclude?: string[];
121
268
  }
122
269
  export interface ModuleBoundaryRule {
123
270
  /** Source module tag pattern or tag (for example, 'type:ui', 'scope:case', or '*'). */
@@ -167,6 +314,14 @@ export interface CompileResult {
167
314
  diagnostics: Diagnostic[];
168
315
  graph: ApplicationGraph;
169
316
  written: string[];
317
+ stats?: CompileStats;
318
+ }
319
+ export interface CompileStats {
320
+ cacheHit: boolean;
321
+ changedFiles: string[];
322
+ affectedModules: string[];
323
+ reanalyzedModules?: string[];
324
+ reusedModules?: string[];
170
325
  }
171
326
  export interface CheckProjectResult {
172
327
  /** Whether generated artifacts exactly match the files on disk. */
@@ -176,3 +331,49 @@ export interface CheckProjectResult {
176
331
  diagnostics: Diagnostic[];
177
332
  graph: ApplicationGraph;
178
333
  }
334
+ export interface WatchEvent {
335
+ type: "compile-start" | "compiled" | "compile-error";
336
+ initial: boolean;
337
+ durationMs: number;
338
+ diagnostics: Diagnostic[];
339
+ written: string[];
340
+ stats?: CompileStats;
341
+ }
342
+ export interface WatchOptions extends CompileOptions {
343
+ /** Debounce source changes before starting a compile (default: 100ms). */
344
+ debounceMs?: number;
345
+ onEvent?: (event: WatchEvent) => void;
346
+ }
347
+ export interface WatchHandle {
348
+ /** Resolves after the initial compile has completed. */
349
+ ready: Promise<WatchEvent>;
350
+ close(): Promise<void>;
351
+ }
352
+ export interface CachedModuleEntry {
353
+ module: ModuleNode;
354
+ /** Files owned by this module (normalized relative paths). */
355
+ ownedFiles: string[];
356
+ /** File hash mapping for owned files. */
357
+ fileHashes: Record<string, string>;
358
+ /** Diagnostics captured during this module's analysis. */
359
+ diagnostics?: Diagnostic[];
360
+ }
361
+ export interface DependencyGraphCache {
362
+ /** Cached modules by module name. */
363
+ modules: Map<string, CachedModuleEntry>;
364
+ /** Global file hashes by relative file path. */
365
+ fileHashes: Map<string, string>;
366
+ /** Retained native TypeScript BuilderProgram session. */
367
+ programSession?: IncrementalProgramSession;
368
+ /** Retained module dependency graph tracking imports and reverse dependents. */
369
+ dependencyGraph?: DependencyGraphIndex;
370
+ /** Content hashes for generated artifacts, used by the incremental emitter. */
371
+ generatedHashes?: Map<string, string>;
372
+ lastStats?: {
373
+ reusedModules: string[];
374
+ reanalyzedModules: string[];
375
+ };
376
+ }
377
+ export interface DependencyGraphIndex {
378
+ getAffectedModules(changedFiles: string[]): string[];
379
+ }
package/dist/util.d.ts CHANGED
@@ -13,3 +13,7 @@ export declare const JOB_CONTEXT_TOKEN_NAME = "supacloud.job-context";
13
13
  export declare function isRequestContextToken(token: string, tokenNames?: Record<string, string>): boolean;
14
14
  /** Determines whether token is built-in job context. */
15
15
  export declare function isJobContextToken(token: string, tokenNames?: Record<string, string>): boolean;
16
+ /** Normalizes and combines controller path and route path. */
17
+ export declare function joinRoutePaths(prefix: string, path: string): string;
18
+ /** Finds the closest match from a list of candidate strings (case/delimiter-insensitive). */
19
+ export declare function findClosestMatch(target: string, candidates: string[]): string | undefined;
@@ -1,4 +1,8 @@
1
1
  import type { ApplicationGraph, Diagnostic, ValidateOptions } from "./types";
2
+ export declare const COMPILER_DIAGNOSTIC_CODES: Record<string, {
3
+ code: string;
4
+ docsUrl: string;
5
+ }>;
2
6
  /**
3
7
  * Validates ApplicationGraph and produces diagnostics list.
4
8
  * In strict mode, warn-level diagnostics are promoted to error.
@@ -0,0 +1,3 @@
1
+ import type { WatchHandle, WatchOptions } from "./types";
2
+ /** Watch a project and keep the last successful generated artifacts active on errors. */
3
+ export declare function watchProject(options: WatchOptions): WatchHandle;
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@supacloud/compiler",
3
- "version": "0.4.1",
3
+ "version": "0.6.0",
4
4
  "description": "Static compiler for @supacloud/app metadata: builds the application graph from AST, validates it, and generates reflection-free factory code",
5
5
  "type": "module",
6
6
  "main": "./dist/index.js",
@@ -22,7 +22,7 @@
22
22
  ],
23
23
  "scripts": {
24
24
  "build": "bun run clean && bun run build:js && bun run build:types",
25
- "build:js": "bun build src/index.ts src/cli.ts --outdir dist --target node --external ts-morph",
25
+ "build:js": "bun build src/index.ts src/cli.ts --outdir dist --target node --external @typescript/typescript6",
26
26
  "build:types": "tsc -p tsconfig.json --emitDeclarationOnly",
27
27
  "clean": "rm -rf dist",
28
28
  "prepublishOnly": "bun run build",
@@ -44,7 +44,7 @@
44
44
  "directory": "packages/compiler"
45
45
  },
46
46
  "dependencies": {
47
- "ts-morph": "^28.0.0"
47
+ "@typescript/typescript6": "^6.0.2"
48
48
  },
49
49
  "devDependencies": {
50
50
  "@types/bun": "^1.4.0",