@happyvertical/smrt-scanner 0.51.32 → 0.51.34

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,2061 +1,29 @@
1
- /** The declared agent-addressable surface found by one scan. */
2
- export declare interface AgentSurface {
3
- intents: AgentSurfaceIntent[];
4
- playbooks: AgentSurfacePlaybook[];
5
- diagnostics: AgentSurfaceDiagnostic[];
6
- }
7
-
8
- /** A fully resolved classification (#2587's fail-closed rule already applied). */
9
- export declare interface AgentSurfaceCapability {
10
- effect: AgentSurfaceEffect;
11
- idempotent: boolean;
12
- openWorld: boolean;
13
- }
14
-
15
- /**
16
- * Something a reader of the emitted surface has to know about a declaration.
17
- *
18
- * Two kinds, and the difference is whether the entry survived. Every code but
19
- * `tool-name-collision` reports a declaration that is NOT emitted — never a
20
- * silent omission, so each of those messages names `useWebMcpTool`, the escape
21
- * hatch for a genuinely computed tool set. A `tool-name-collision` is
22
- * advisory: the declaration IS emitted, and the diagnostic records that its
23
- * derived WebMCP tool name is already spoken for by something outside the
24
- * declared surface.
25
- */
26
- export declare interface AgentSurfaceDiagnostic {
27
- code: AgentSurfaceDiagnosticCode;
28
- helper: AgentSurfaceHelper;
29
- message: string;
30
- filePath: string;
31
- line?: number;
32
- column?: number;
33
- }
34
-
35
- /** Why a recognized declaration could not be emitted. */
36
- export declare type AgentSurfaceDiagnosticCode = 'non-literal-argument' | 'not-module-scope' | 'argument-count' | 'incomplete-declaration' | 'invalid-identity' | 'svelte-declaration' | 'duplicate-identity' | 'tool-name-collision';
37
-
38
- /**
39
- * Effect classification, mirroring `CapabilityEffect` in
40
- * `@happyvertical/smrt-types`. Mirrored rather than imported because this
41
- * package carries no `@happyvertical/*` dependency — core depends on it, and
42
- * importing back would close the cycle.
43
- */
44
- export declare type AgentSurfaceEffect = 'read' | 'write' | 'destructive';
45
-
46
- /** The two module-scope helpers the agent-surface matcher recognizes. */
47
- export declare type AgentSurfaceHelper = 'defineIntent' | 'definePlaybook';
48
-
49
- /** One emitted view intent (#2588). */
50
- export declare interface AgentSurfaceIntent {
51
- kind: 'intent';
52
- /** Declared, dot-namespaced identity. This is the entry's stable identity. */
53
- id: string;
54
- description: string;
55
- capability: AgentSurfaceCapability;
56
- target: Record<string, unknown>;
57
- /** Whether the declaration carried an `inputSchema` object. */
58
- hasInputSchema: boolean;
59
- /**
60
- * Always exactly `['browser']`. An intent moves mounted browser state; a
61
- * server-side agent reaches one only through the #2446 command/ack bridge,
62
- * which the referencing PLAYBOOK declares. Typed as the literal tuple rather
63
- * than the open plane list so the contract cannot be read as wider than it is.
64
- */
65
- planes: ['browser'];
66
- /** Declaring module, relativized by the emitting caller. */
67
- filePath: string;
68
- }
69
-
70
- /** Plane a declaration is valid on. */
71
- export declare type AgentSurfacePlane = 'browser' | 'server';
72
-
73
- /** One emitted playbook (#2589). */
74
- export declare interface AgentSurfacePlaybook {
75
- kind: 'playbook';
76
- /** Declared registry key. This is the entry's stable identity. */
77
- key: string;
78
- title: string;
79
- description: string;
80
- steps: AgentSurfacePlaybookStep[];
81
- planes: AgentSurfacePlane[];
82
- /** False when `planes` was derived from the step kinds rather than declared. */
83
- planesDeclared: boolean;
84
- onStepFailure: 'abort' | 'continue';
85
- enabled: boolean;
86
- /** Declaring module, relativized by the emitting caller. */
87
- filePath: string;
88
- }
89
-
90
- /** One step of an emitted playbook (#2589). Playbooks cannot nest. */
91
- export declare type AgentSurfacePlaybookStep = {
92
- kind: 'operation';
93
- model: string;
94
- action: string;
95
- } | {
96
- kind: 'intent';
97
- id: string;
98
- };
99
-
100
- export declare interface AgentSurfaceToolNameOptions {
101
- /**
102
- * Generated model tool names for the SAME manifest, already filtered by the
103
- * exposure policy.
104
- *
105
- * Filtered, not enumerated: comparing against every verb a class COULD
106
- * expose would report collisions with tools that are never registered.
107
- * `buildWebMcpToolDefinitions` in `@happyvertical/smrt-core/vite-plugin` is
108
- * the function that emits the runtime definitions, so its output is the only
109
- * honest input here.
110
- */
111
- generatedToolNames?: readonly GeneratedModelToolName[];
112
- /**
113
- * Prefixes the fixed UI tools are mounted under, defaulting to the
114
- * registrar's own `smrt_ui_`.
115
- *
116
- * Supplied for the same reason `generatedToolNames` is: a name is compared
117
- * against tools that will really register. Quantifying over every prefix an
118
- * app COULD have configured — deriving the would-be prefix from the name —
119
- * looks like more coverage and is the opposite. Under the default it has no
120
- * true positive at all, because an id flattening to `smrt_ui_*` is rejected
121
- * by {@link intentIdentityProblem} before it can become an entry, so every
122
- * diagnostic such a rule emits for a default-configured app is false. A
123
- * warning that is always wrong where it fires is worse than silence: it is
124
- * persisted into the knowledge artifact, counted by `smrt doctor`, and
125
- * clearable only by renaming an intent that was correct.
126
- *
127
- * There is no build-time source for this today — `ui.prefix` is a runtime
128
- * `<Provider webmcp={{ ui: { prefix } }}>` prop — so the default leaves this
129
- * half dormant. That is the honest state of it, and the seam is ready for
130
- * the caller that can fill it.
131
- */
132
- uiToolPrefixes?: readonly string[];
133
- }
134
-
135
- declare interface ArrayExpression extends BaseNode {
136
- type: 'ArrayExpression';
137
- elements: (Expression | SpreadElement | null)[];
138
- }
139
-
140
- declare interface ArrayPattern extends BaseNode {
141
- type: 'ArrayPattern';
142
- elements: (Pattern | null)[];
143
- typeAnnotation?: TSTypeAnnotation;
144
- }
145
-
146
- declare interface AssignmentPattern extends BaseNode {
147
- type: 'AssignmentPattern';
148
- left: Pattern;
149
- right: Expression;
150
- }
151
-
152
- declare interface BaseNode {
153
- type: string;
154
- loc?: SourceLocation;
155
- range?: [number, number];
156
- start?: number;
157
- end?: number;
158
- }
159
-
160
- declare interface BlockStatement extends BaseNode {
161
- type: 'BlockStatement';
162
- body: Statement[];
163
- }
164
-
165
- declare interface CallExpression extends BaseNode {
166
- type: 'CallExpression';
167
- callee: Expression;
168
- arguments: Expression[];
169
- typeParameters?: TSTypeParameterInstantiation;
170
- }
171
-
172
- /**
173
- * Report an emitted intent whose derived WebMCP tool name is already spoken
174
- * for by something OUTSIDE the declared surface (#2725).
175
- *
176
- * `mergeAgentSurfaces` resolves the collisions it can see on its own —
177
- * intent vs intent, where `intentToolName` is not injective. It cannot see the
178
- * two sources that live outside the declaration set:
179
- *
180
- * - **generated model tools**, `${className.toLowerCase()}_${action}`, so
181
- * `defineIntent({ id: 'product.list' })` lands exactly on `Product.list`;
182
- * - **the six fixed UI tools** under a CUSTOM `webmcp.ui.prefix`. The default
183
- * `smrt_ui_` is already a hard rejection in {@link intentIdentityProblem},
184
- * mirroring `defineIntent`; a custom prefix is not, because `defineIntent`
185
- * accepts such an id and the intent really does register.
186
- *
187
- * ### These are warnings, not drops — unlike intent vs intent
188
- *
189
- * The asymmetry is real and it is the whole reason this is a separate pass.
190
- * Two colliding intents are a closed question: `defineIntent` REJECTS the
191
- * second declaration, so only one can exist and emitting both would overstate
192
- * the surface by an entry that cannot be. Neither collision here is closed:
193
- *
194
- * - `defineIntent` accepts the intent, so the declaration is real and the
195
- * entry belongs in the artifact. Dropping it would make the emitted surface
196
- * disagree with the source, which is the failure this module exists to
197
- * prevent;
198
- * - which registration survives is decided at mount by the document-global
199
- * tool-name lock (#2613), which rejects the second with a
200
- * `WebMcpToolNameCollisionError` naming the owner. That is a runtime answer
201
- * to a question the build can already see coming, and it costs whoever loses
202
- * its tool; the build-time notice is the earlier, cheaper one;
203
- * - and whether it happens at all depends on runtime values no artifact
204
- * records: a WebMCP `namespace` moves every generated tool out of the way,
205
- * an `effects` policy can exclude the action, and a page need not mount
206
- * both. A build-time drop would be a guess about all three.
207
- *
208
- * So the entry stays and the report is advisory. The message names both sides,
209
- * the consequence, and — because those runtime values are unknowable here and
210
- * a `namespace` genuinely dissolves the collision — the precondition under
211
- * which it holds. Without that last part a namespaced app gets a notice that
212
- * is always wrong where it fires, recommending the very remedy it already
213
- * applied, with renaming a correct intent as the only clearing action. That is
214
- * the standard `uiToolPrefixes` exists to meet; the generated half has to meet
215
- * it too, and it does so in the message because the caller CAN supply
216
- * namespaced names when it knows them.
217
- *
218
- * @param surface - A MERGED surface; intent-vs-intent losers are already gone.
219
- * @returns Diagnostics only. The caller appends them; nothing is removed.
220
- */
221
- export declare function checkAgentSurfaceToolNames(surface: AgentSurface, options?: AgentSurfaceToolNameOptions): AgentSurfaceDiagnostic[];
222
-
223
- declare interface ClassBody extends BaseNode {
224
- type: 'ClassBody';
225
- body: ClassElement[];
226
- }
227
-
228
- declare interface ClassDeclaration extends BaseNode {
229
- type: 'ClassDeclaration';
230
- id: Identifier | null;
231
- superClass: Expression | null;
232
- superTypeParameters?: TSTypeParameterInstantiation;
233
- superTypeArguments?: TSTypeParameterInstantiation;
234
- body: ClassBody;
235
- decorators?: Decorator[];
236
- }
237
-
238
- declare type ClassElement = PropertyDefinition | MethodDefinition_2;
239
-
240
- /**
241
- * Classify an identifier as money, a rate, or neither.
242
- *
243
- * The head noun is the last word, or the last word before a trailing qualifier
244
- * such as `Paid`. A rate word anywhere in the name wins outright: `taxRate` is
245
- * a rate even though `tax` is money, and that precedence is what stops the two
246
- * vocabularies from fighting over the same field.
247
- */
248
- export declare function classifyNumericFieldName(name: string): NumericPrecisionKind | undefined;
249
-
250
- declare interface Decorator extends BaseNode {
251
- type: 'Decorator';
252
- expression: Expression;
253
- }
254
-
255
- /**
256
- * Discover authored source files with the bounded policy shared by every
257
- * scanner entry point, including ManifestBuilder's preflight (#2275).
258
- */
259
- export declare function discoverSourceFiles(options: SourceDiscoveryOptions): Promise<string[]>;
260
-
261
- /** An empty surface, for callers that skipped the scan. */
262
- export declare function emptyAgentSurface(): AgentSurface;
263
-
264
- declare interface ExportDefaultDeclaration extends BaseNode {
265
- type: 'ExportDefaultDeclaration';
266
- declaration: Statement | Expression;
267
- }
268
-
269
- declare interface ExportNamedDeclaration extends BaseNode {
270
- type: 'ExportNamedDeclaration';
271
- declaration: Statement | null;
272
- specifiers: ExportSpecifier[];
273
- }
274
-
275
- declare interface ExportSpecifier extends BaseNode {
276
- type: 'ExportSpecifier';
277
- local: Identifier;
278
- exported: Identifier;
279
- }
280
-
281
- declare type Expression = Identifier | Literal | CallExpression | MemberExpression | ObjectExpression | ArrayExpression | NewExpression | UnaryExpression;
282
-
283
- /**
284
- * Describes a pre-loaded manifest from an installed SMRT package, used by
285
- * {@link InheritanceResolver} to resolve base classes that originate outside
286
- * the local project source.
287
- *
288
- * Build tooling (e.g. the SMRT CLI / vitest plugin) loads each installed
289
- * `@happyvertical/smrt-*` package's `manifest.json` and converts it into an
290
- * `ExternalManifest` before passing it to the scanner.
291
- *
292
- * @see {@link OxcScannerOptions.externalManifests}
293
- * @see {@link InheritanceResolver.addExternalManifest}
294
- */
295
- export declare interface ExternalManifest {
296
- /** npm package name, e.g. `'@happyvertical/smrt-profiles'`. */
297
- packageName: string;
298
- /** SemVer version string of the installed package. */
299
- packageVersion: string;
300
- /** All class definitions exported by the package, keyed by class name. */
301
- classes: Map<string, RawClassDefinition>;
302
- }
303
-
304
- /**
305
- * Match `defineIntent(...)` / `definePlaybook(...)` declarations in one parsed
306
- * module.
307
- *
308
- * @returns Emittable entries plus a diagnostic for every recognized call that
309
- * is not emittable. A recognized call always produces exactly one of the two.
310
- */
311
- export declare function extractAgentSurface(options: ExtractAgentSurfaceOptions): AgentSurface;
312
-
313
- export declare interface ExtractAgentSurfaceOptions {
314
- /** Program body from an OXC parse. */
315
- body: readonly unknown[];
316
- /** Full source text, used for diagnostic line/column. */
317
- sourceText: string;
318
- /** Path recorded on every emitted entry and diagnostic. */
319
- filePath: string;
320
- }
321
-
322
- /* Excluded from this release type: extractSmrtImports */
323
-
324
- declare interface FieldDefinition {
325
- type: 'text' | 'decimal' | 'boolean' | 'integer' | 'datetime' | 'json' | 'foreignKey' | 'crossPackageRef' | 'oneToMany' | 'manyToMany' | 'meta';
326
- required?: boolean;
327
- default?: unknown;
328
- min?: number;
329
- max?: number;
330
- maxLength?: number;
331
- minLength?: number;
332
- related?: string;
333
- description?: string;
334
- _meta?: Record<string, unknown>;
335
- transient?: boolean;
336
- /** Sensitive value — excluded from public serialization + where filtering. */
337
- sensitive?: boolean;
338
- /** Read-only over generated write surfaces — stripped from create/update bodies. */
339
- readonly?: boolean;
340
- /** Permission slug required before the field is included in public reads. */
341
- readPermission?: string;
342
- }
343
-
344
- /**
345
- * Result produced by {@link ManifestAdapter.inferFieldType} for a single field.
346
- *
347
- * In addition to the inferred `type`, carries the `source` of the inference
348
- * so callers can distinguish authoritative decorator-driven results from
349
- * heuristic guesses and provide better diagnostics.
350
- *
351
- * @see {@link InferredFieldType} for valid `type` values.
352
- * @see {@link ManifestAdapter.inferFieldType} for inference priority rules.
353
- */
354
- export declare interface FieldTypeInference {
355
- /** Inferred SMRT type */
356
- type: InferredFieldType;
357
- /** Related class for relationship types */
358
- related?: string;
359
- /** Default value if extractable */
360
- defaultValue?: unknown;
361
- /** Whether field is required */
362
- required: boolean;
363
- /** Inference source for debugging */
364
- source: 'helper' | 'decorator' | 'annotation' | 'heuristic' | 'default';
365
- /** Underlying type for meta fields (e.g., 'string' inside Meta<string>) */
366
- underlyingType?: InferredFieldType;
367
- /**
368
- * Decorator-derived metadata that should be merged into the manifest
369
- * field's `_meta` object. Used by `@crossPackageRef`, `@manyToMany`,
370
- * `@meta` to carry options (`validate`, `through`, `indexed`, `idType`,
371
- * etc.) that don't fit on the top-level FieldDefinition.
372
- */
373
- _meta?: Record<string, unknown>;
374
- }
375
-
376
- /**
377
- * Result from scanning a single file
378
- */
379
- export declare interface FileScanResult {
380
- /** Source file path */
381
- filePath: string;
382
- /** Classes found in file */
383
- classes: RawClassDefinition[];
384
- /** Scan errors */
385
- errors: ScanError[];
386
- /** Parse time in milliseconds */
387
- parseTimeMs: number;
388
- /** Type alias declarations found in file (name → resolved type string) */
389
- typeAliases: Record<string, string>;
390
- /** SMRT package imports found in file (package name → Set of imported class names) */
391
- smrtImports?: Map<string, Set<string>>;
392
- /**
393
- * Declared view intents and playbooks found in the file, plus a diagnostic
394
- * for every recognized declaration that is not statically emittable (#2591).
395
- * Omitted when the file declares none.
396
- */
397
- agentSurface?: AgentSurface;
398
- }
399
-
400
- declare interface FunctionExpression extends BaseNode {
401
- type: 'FunctionExpression';
402
- async: boolean;
403
- params: Pattern[];
404
- returnType?: TSTypeAnnotation;
405
- body: BlockStatement;
406
- }
407
-
408
- /**
409
- * One WebMCP tool name the build will register for a generated model action,
410
- * supplied by the caller that knows the exposure policy.
411
- */
412
- export declare interface GeneratedModelToolName {
413
- /**
414
- * The tool name EXACTLY as it will be registered.
415
- *
416
- * This pass never derives a generated name and never applies a namespace of
417
- * its own: it compares the strings it is handed. That is deliberate. The
418
- * WebMCP `namespace` is a runtime `<Provider webmcp={{ namespace }}>` value
419
- * that no build artifact records, so inventing a build-time declaration of
420
- * it here would create a second place to say what the provider already says,
421
- * free to disagree with it silently. Taking names instead means a caller
422
- * that ever does know the namespace qualifies them itself and nothing in
423
- * this module changes.
424
- */
425
- name: string;
426
- /** What owns that name, for the message — e.g. `Product.list`. */
427
- declaredBy?: string;
428
- }
429
-
430
- declare interface Identifier extends BaseNode {
431
- type: 'Identifier';
432
- name: string;
433
- typeAnnotation?: TSTypeAnnotation;
434
- optional?: boolean;
435
- }
436
-
437
- declare interface ImportDeclaration extends BaseNode {
438
- type: 'ImportDeclaration';
439
- specifiers?: ImportSpecifierLike[];
440
- source: Literal;
441
- }
442
-
443
- declare interface ImportDefaultSpecifier extends BaseNode {
444
- type: 'ImportDefaultSpecifier';
445
- local: Identifier;
446
- }
447
-
448
- declare interface ImportNamespaceSpecifier extends BaseNode {
449
- type: 'ImportNamespaceSpecifier';
450
- local: Identifier;
451
- }
452
-
453
- declare interface ImportSpecifier extends BaseNode {
454
- type: 'ImportSpecifier';
455
- imported: Identifier;
456
- local: Identifier;
457
- }
458
-
459
- declare type ImportSpecifierLike = ImportSpecifier | ImportNamespaceSpecifier | ImportDefaultSpecifier;
460
-
461
- /**
462
- * The set of SMRT column types that the scanner can infer for a field.
463
- *
464
- * | Value | DB column type | Notes |
465
- * |---|---|---|
466
- * | `text` | `TEXT` / `VARCHAR` | Default for `string` and unknown types |
467
- * | `integer` | `INTEGER` | `number` with `= 0` initialiser |
468
- * | `decimal` | `DECIMAL` | `number` with `= 0.0` initialiser |
469
- * | `boolean` | `BOOLEAN` | `boolean` annotation or literal initialiser |
470
- * | `datetime` | `DATETIME` | `Date` annotation |
471
- * | `json` | `JSON` / `TEXT` | Arrays, `Record<>`, object types |
472
- * | `foreignKey` | `UUID` by default (FK column) | `@foreignKey(Class)` decorator |
473
- * | `crossPackageRef` | `UUID` by default (no FK constraint) | `@crossPackageRef('@pkg:Class')` decorator |
474
- * | `oneToMany` | — (virtual) | `@oneToMany(Class)` decorator |
475
- * | `manyToMany` | — (virtual) | `@manyToMany(Class)` decorator |
476
- * | `meta` | Stored in `_meta_data` | STI child field wrapped in `Meta<T>` |
477
- * | `unknown` | — | Could not be determined |
478
- *
479
- * @see {@link FieldTypeInference} for the full inference result shape.
480
- */
481
- export declare type InferredFieldType = 'text' | 'integer' | 'decimal' | 'boolean' | 'datetime' | 'json' | 'foreignKey' | 'crossPackageRef' | 'oneToMany' | 'manyToMany' | 'meta' | 'unknown';
482
-
483
- /**
484
- * Resolves class inheritance chains and STI hierarchies from raw OXC scan output.
485
- *
486
- * After {@link OxcScanner} parses files, `InheritanceResolver` builds a
487
- * class map from the raw definitions and walks each class's `extends` chain
488
- * to produce fully-resolved {@link ResolvedClassDefinition} objects.
489
- *
490
- * Key responsibilities:
491
- * - Walking extends chains across local classes and external package manifests.
492
- * - Detecting which classes participate in STI (Single Table Inheritance).
493
- * - Merging fields from ancestor classes for STI subclasses (base fields first).
494
- * - Caching resolved chains to avoid repeated traversals.
495
- *
496
- * Framework base classes (`SmrtObject`, `SmrtClass`, `SmrtCollection`) are
497
- * recognized without needing to appear in source files.
498
- *
499
- * @example
500
- * ```typescript
501
- * import { InheritanceResolver } from '@happyvertical/smrt-scanner';
502
- *
503
- * const resolver = new InheritanceResolver({ baseClasses: ['MyBaseClass'] });
504
- * resolver.addClasses(rawClasses);
505
- * const resolved = resolver.resolveAll();
506
- * ```
507
- *
508
- * @see {@link OxcScanner} which owns and drives this resolver internally.
509
- */
510
- export declare class InheritanceResolver {
511
- /** Map of className -> RawClassDefinition */
512
- private classMap;
513
- /** External package manifests for cross-package resolution */
514
- private externalManifests;
515
- /** Known base classes (user-provided) */
516
- private knownBaseClasses;
517
- /** Cache of resolved inheritance chains */
518
- private chainCache;
519
- /**
520
- * Create a new `InheritanceResolver`.
521
- *
522
- * @param options.baseClasses - Additional class names to treat as known
523
- * framework base classes (beyond the built-in `SmrtObject`, `SmrtClass`,
524
- * and `SmrtCollection`).
525
- * @param options.externalManifests - Pre-loaded external package manifests
526
- * keyed by package name, used for cross-package parent class resolution.
527
- */
528
- constructor(options?: {
529
- baseClasses?: string[];
530
- externalManifests?: Map<string, ExternalManifest>;
531
- });
532
- /**
533
- * Register raw class definitions from a scan pass.
534
- *
535
- * Adds each class to the internal class map by `className`. Calling this
536
- * clears the inheritance chain cache so subsequent calls to
537
- * {@link resolveAll} or {@link resolveInheritanceChain} reflect the new
538
- * classes.
539
- *
540
- * @param classes - Array of {@link RawClassDefinition} objects from
541
- * {@link ScanResults.classes}.
542
- */
543
- addClasses(classes: RawClassDefinition[]): void;
544
- /**
545
- * Register an external package manifest for cross-package base class resolution.
546
- *
547
- * Clears the chain cache after registration so re-resolution picks up the
548
- * new definitions.
549
- *
550
- * @param manifest - External package manifest providing class definitions
551
- * that may appear as base classes in the local project.
552
- *
553
- * @see {@link ExternalManifest}
554
- */
555
- addExternalManifest(manifest: ExternalManifest): void;
556
- /**
557
- * Resolve all registered classes and return fully-resolved definitions.
558
- *
559
- * A class is included in the output if it either:
560
- * 1. Has an `@smrt()` decorator, or
561
- * 2. Directly or transitively extends a framework base class
562
- * (`SmrtObject`, `SmrtClass`, `SmrtCollection`) — this captures
563
- * collection classes such as `class MeetingCollection extends
564
- * SmrtCollection<Meeting>` that do not carry `@smrt()` themselves.
565
- *
566
- * @returns An array of {@link ResolvedClassDefinition} — one entry per
567
- * eligible class, with inheritance chain, STI metadata, and merged fields
568
- * populated.
569
- *
570
- * @see {@link resolve} to resolve a single class definition.
571
- */
572
- resolveAll(): ResolvedClassDefinition[];
573
- /**
574
- * Check if a class extends a framework base class
575
- * (SmrtObject, SmrtClass, or SmrtCollection)
576
- */
577
- private extendsFrameworkBase;
578
- /**
579
- * Resolve a single raw class definition into a fully-resolved definition.
580
- *
581
- * Computes the inheritance chain, determines the effective table strategy,
582
- * detects STI membership, and merges ancestor fields for STI classes.
583
- *
584
- * @param classDef - The raw class definition to resolve.
585
- * @returns A {@link ResolvedClassDefinition} with all inherited metadata
586
- * applied. The `packageName` field is left as `null` and must be set by
587
- * the caller (e.g. {@link ManifestAdapter}).
588
- *
589
- * @see {@link resolveAll} to resolve every registered class at once.
590
- */
591
- resolve(classDef: RawClassDefinition): ResolvedClassDefinition;
592
- /**
593
- * Resolve the full inheritance chain for a named class, from the root base
594
- * class down to the named class itself.
595
- *
596
- * Results are memoised in an internal cache that is cleared whenever
597
- * {@link addClasses} or {@link addExternalManifest} is called.
598
- *
599
- * @param className - Name of the class to resolve.
600
- * @returns An ordered array of class names starting from the furthest
601
- * ancestor and ending with `className`.
602
- *
603
- * @example
604
- * ```typescript
605
- * // Given: class Article extends Content, class Content extends SmrtObject
606
- * resolver.resolveInheritanceChain('Article');
607
- * // => ['SmrtObject', 'Content', 'Article']
608
- * ```
609
- */
610
- resolveInheritanceChain(className: string): string[];
611
- /**
612
- * Look up a class definition by name, searching in priority order:
613
- * 1. Local classes added via {@link addClasses}.
614
- * 2. External package manifests added via {@link addExternalManifest}.
615
- * 3. Built-in framework base classes (`SmrtObject`, `SmrtClass`,
616
- * `SmrtCollection`) — returns a minimal stub definition so chain walking
617
- * can terminate cleanly.
618
- *
619
- * @param className - Class name to look up.
620
- * @returns The {@link RawClassDefinition} if found, or `null` if the class
621
- * is unknown to the resolver.
622
- */
623
- findClassDefinition(className: string): RawClassDefinition | null;
624
- /**
625
- * Find the STI root class in a resolved inheritance chain.
626
- *
627
- * Walks the chain from base to leaf and returns the name of the first class
628
- * whose `@smrt()` decorator explicitly declares `tableStrategy: 'sti'`.
629
- *
630
- * @param chain - Ordered inheritance chain (base → leaf) as returned by
631
- * {@link resolveInheritanceChain}.
632
- * @returns The class name of the STI root, or `null` if no class in the
633
- * chain uses `tableStrategy: 'sti'`.
634
- */
635
- findSTIBase(chain: string[]): string | null;
636
- /**
637
- * Determine the effective table strategy (`'sti'` or `'cti'`) for a class.
638
- *
639
- * Resolution order:
640
- * 1. The class's own `@smrt({ tableStrategy })` declaration, if present.
641
- * 2. The nearest ancestor that declares `tableStrategy: 'sti'` — STI is
642
- * inherited automatically by all subclasses.
643
- * 3. Defaults to `'cti'` if no STI ancestor is found.
644
- *
645
- * @param classDef - Raw class definition whose strategy is being determined.
646
- * @param chain - Pre-resolved inheritance chain for `classDef` (base → leaf).
647
- * @returns `'sti'` or `'cti'`.
648
- */
649
- determineTableStrategy(classDef: RawClassDefinition, chain: string[]): 'sti' | 'cti';
650
- /**
651
- * Merge fields from all classes in an STI inheritance chain.
652
- *
653
- * Iterates from the root base class to the leaf class so that base class
654
- * fields appear first in the returned array. If a field name is declared in
655
- * both an ancestor and a descendant, the ancestor's definition takes
656
- * precedence (first-seen wins), preserving the base-class column layout.
657
- *
658
- * @param chain - Ordered inheritance chain (base → leaf) as returned by
659
- * {@link resolveInheritanceChain}.
660
- * @returns A deduplicated, ordered array of {@link RawFieldDefinition}
661
- * covering every field in the STI hierarchy.
662
- */
663
- mergeFieldsForSTI(chain: string[]): RawFieldDefinition[];
664
- /**
665
- * Return all known descendants of a class.
666
- *
667
- * Useful for STI schema generation where the base table must accommodate
668
- * columns from every subclass.
669
- *
670
- * @param className - The ancestor class name to search from.
671
- * @returns An array of class names (local classes only) whose resolved
672
- * inheritance chain includes `className`. Does not include `className`
673
- * itself.
674
- */
675
- getDescendants(className: string): string[];
676
- /**
677
- * Check whether a class participates in an STI hierarchy.
678
- *
679
- * @param className - Name of the class to check.
680
- * @returns `true` if any class in the resolved inheritance chain declares
681
- * `tableStrategy: 'sti'`, `false` otherwise.
682
- */
683
- isSTIClass(className: string): boolean;
684
- /**
685
- * Return aggregate statistics about the classes registered with this resolver.
686
- *
687
- * @returns An object with:
688
- * - `totalClasses` — total number of classes in the class map.
689
- * - `smrtClasses` — classes that carry `@smrt()`.
690
- * - `stiClasses` — `@smrt()` classes in an STI hierarchy.
691
- * - `maxInheritanceDepth` — length of the deepest inheritance chain among
692
- * `@smrt()` classes.
693
- */
694
- getStats(): {
695
- totalClasses: number;
696
- smrtClasses: number;
697
- stiClasses: number;
698
- maxInheritanceDepth: number;
699
- };
700
- }
701
-
702
- /**
703
- * Whether the agent-surface emitter reads this file. **This is the one
704
- * authority on that question.**
705
- *
706
- * Both the scanner's declaration pass and `dev:knowledge-check`'s freshness
707
- * re-scan call it, because the two answering differently is not a cosmetic
708
- * inconsistency: a file the emitter reads but the checker skips is reported as
709
- * "no longer present in source" on every run, and a file the checker reads but
710
- * the emitter skips is reported as "missing from smrt-knowledge.json" — both
711
- * unclearable by any rebuild. It is a path predicate rather than a glob so the
712
- * two sides cannot drift through differing glob semantics either.
713
- *
714
- * Callers still pass globs to prune the WALK for speed; this decides what
715
- * counts.
716
- */
717
- export declare function isAgentSurfaceSourcePath(filePath: string, rootDir?: string): boolean;
718
-
719
- /**
720
- * Whether a path lies in a directory declarations are never read from,
721
- * measured **relative to the project root**.
722
- *
723
- * `rootDir` is not optional in spirit. Matching these segments against an
724
- * absolute path would disable the entire feature for a checkout that merely
725
- * LIVES under one — a container with `WORKDIR /build`, or a clone in
726
- * `~/build/…` — and it would do so with no diagnostic at all, because the
727
- * freshness check applies the same predicate and would agree that nothing is
728
- * declared. That is the silent drop this module exists to prevent, so the same
729
- * care `discovery.ts` takes to rewrite globs relative to `cwd` applies here.
730
- *
731
- * Exported so the `.svelte` pass and `dev:knowledge-check`'s walk prune
732
- * identically; a `.svelte` file cannot go through
733
- * {@link isAgentSurfaceSourcePath}, which rejects it on extension.
734
- */
735
- export declare function isPrunedAgentSurfacePath(filePath: string, rootDir?: string): boolean;
736
-
737
- /**
738
- * Report every persisted `number` field whose declared precision contradicts
739
- * its name — money declared decimal, or a rate declared integer.
740
- *
741
- * @param classes - Raw class definitions from `parseFile` / `OxcScanner`.
742
- * @param sourceText - Optional contents of the scanned file, used only to
743
- * recover declaration line numbers the AST does not carry.
744
- * @returns One finding per offending field, in declaration order.
745
- */
746
- export declare function lintNumericPrecision(classes: RawClassDefinition[], sourceText?: string): NumericPrecisionFinding[];
747
-
748
- declare interface Literal extends BaseNode {
749
- type: 'Literal';
750
- value: string | number | boolean | null | RegExp | bigint;
751
- raw?: string;
752
- }
753
-
754
- /**
755
- * Converts OXC scanner output into the smrt-core `SmartObjectManifest` format
756
- * consumed by code generators, the Vitest plugin, and the SMRT CLI.
757
- *
758
- * The adapter handles field type inference (applying the `0` vs `0.0` integer /
759
- * decimal heuristic), decorator interpretation (`@foreignKey`, `@oneToMany`,
760
- * `@manyToMany`, `@field`), type alias resolution, STI `Meta<T>` unwrapping,
761
- * static property capture (`uiSlots`, `adminRoutes`), and qualified name
762
- * generation for namespace isolation across packages.
763
- *
764
- * @example
765
- * ```typescript
766
- * import { OxcScanner, ManifestAdapter } from '@happyvertical/smrt-scanner';
767
- *
768
- * const scanner = new OxcScanner({ cwd: process.cwd() });
769
- * const { results, resolved } = await scanner.scanAndResolve();
770
- *
771
- * const adapter = new ManifestAdapter();
772
- * const manifest = adapter.toManifest(resolved, {
773
- * packageName: '@my-org/my-package',
774
- * packageVersion: '1.0.0',
775
- * typeAliases: results.typeAliases,
776
- * });
777
- * ```
778
- *
779
- * @see {@link OxcScanner} for producing the `ResolvedClassDefinition[]` input.
780
- * @see {@link ResolvedClassDefinition} for the shape of each input element.
781
- */
782
- export declare class ManifestAdapter {
783
- private typeAliases;
784
- private _aliasDepth?;
785
- /**
786
- * Convert an array of resolved class definitions into a `SmartObjectManifest`.
787
- *
788
- * Each class is converted to a `SmartObjectDefinition` via
789
- * {@link toSmartObjectDefinition} and stored under its qualified name key
790
- * (e.g. `@my-org/my-package:MyClass`) when `packageName` is provided, or
791
- * under its lowercased class name otherwise.
792
- *
793
- * @param resolved - Resolved class definitions from {@link OxcScanner.resolve}
794
- * or {@link OxcScanner.scanAndResolve}.
795
- * @param options.packageName - npm package name used to generate qualified
796
- * class names for namespace isolation across multi-package projects.
797
- * @param options.packageVersion - Package version recorded in the manifest
798
- * metadata.
799
- * @param options.typeAliases - Map of type alias names to their resolved type
800
- * strings (from {@link ScanResults.typeAliases}). Used to resolve custom
801
- * types like `type Status = 'active' | 'inactive'` during field inference.
802
- * @returns A complete `SmartObjectManifest` ready for serialisation.
803
- *
804
- * @example
805
- * ```typescript
806
- * const manifest = adapter.toManifest(resolved, {
807
- * packageName: '@my-org/my-package',
808
- * packageVersion: '1.0.0',
809
- * typeAliases: results.typeAliases,
810
- * });
811
- * fs.writeFileSync('manifest.json', JSON.stringify(manifest, null, 2));
812
- * ```
813
- */
814
- toManifest(resolved: ResolvedClassDefinition[], options?: {
815
- packageName?: string;
816
- packageVersion?: string;
817
- typeAliases?: Record<string, string>;
818
- }): SmartObjectManifest;
819
- /**
820
- * Convert a single resolved class definition to a `SmartObjectDefinition`.
821
- *
822
- * Handles:
823
- * - Static property capture (`uiSlots`, `adminRoutes`) with child-wins
824
- * semantics for overridden statics.
825
- * - Field conversion (non-static public fields only) via {@link convertField}.
826
- * - Method conversion (public instance/static methods) via {@link convertMethod}.
827
- * - Collection name pluralisation.
828
- * - Qualified name generation when `packageName` is supplied.
829
- *
830
- * @param classDef - A fully-resolved class definition.
831
- * @param options.packageName - Package name used to build the qualified class
832
- * name (`@pkg:ClassName`).
833
- * @param options.packageVersion - Package version (informational, stored in
834
- * the definition).
835
- * @returns A `SmartObjectDefinition` ready to be stored in a manifest.
836
- *
837
- * @see {@link toManifest} for the bulk conversion entry point.
838
- */
839
- toSmartObjectDefinition(classDef: ResolvedClassDefinition, options?: {
840
- packageName?: string;
841
- packageVersion?: string;
842
- }): SmartObjectDefinition;
843
- /**
844
- * Framework internal fields that should NOT be included in manifests
845
- * These are SmrtObject internals used by the framework, not user-defined fields
846
- */
847
- private static readonly FRAMEWORK_INTERNAL_FIELDS;
848
- /**
849
- * Convert a single raw field definition to a manifest `FieldDefinition`.
850
- *
851
- * Returns `null` for fields that should be omitted from the manifest:
852
- * - `private` or `protected` fields.
853
- * - Framework-internal fields (`_tableName`, `_db`, `_ai`, etc.).
854
- *
855
- * Delegates type inference to {@link inferFieldType} and applies additional
856
- * post-processing:
857
- * - Marks fields with `Function` type annotation as `transient`.
858
- * - Marks fields with `@field({ transient: true })` decorator as `transient`.
859
- * - Populates `_meta.underlyingType` for STI `Meta<T>` fields.
860
- *
861
- * @param field - Raw field definition from a scanned class.
862
- * @returns A `FieldDefinition` for the manifest, or `null` if the field
863
- * should be excluded.
864
- *
865
- * @see {@link inferFieldType} for the type inference logic.
866
- */
867
- convertField(field: RawFieldDefinition): FieldDefinition | null;
868
- /**
869
- * Infer the SMRT field type and required flag from a raw field definition.
870
- *
871
- * Inference is attempted in the following priority order:
872
- * 1. **Field helper call in initializer** — currently always returns `null`
873
- * (field helpers removed); reserved for future use.
874
- * 2. **Decorator** — `@foreignKey`, `@oneToMany`, `@manyToMany`, `@field({ type })`.
875
- * 3. **Type annotation** — `string` → `text`, `number` with `0` vs `0.0`
876
- * heuristic → `integer` / `decimal`, `boolean`, `Date` → `datetime`,
877
- * arrays → `json`, `Record<>` / `object` → `json`, union types with
878
- * `null`, inline string/number literal unions, `Meta<T>` wrapper,
879
- * and type alias resolution (up to depth 5).
880
- * 4. **Numeric literal without annotation** — `version = 1` → `integer`.
881
- * 5. **Boolean literal without annotation** — `isRead = false` → `boolean`.
882
- * 6. **Default** — falls back to `text`.
883
- *
884
- * @param field - The raw field definition to analyse.
885
- * @returns A {@link FieldTypeInference} describing the inferred type,
886
- * required flag, default value, related class name (for relationships),
887
- * and the inference source for debugging.
888
- *
889
- * @see {@link FieldTypeInference} for the result shape.
890
- * @see {@link InferredFieldType} for valid type values.
891
- */
892
- inferFieldType(field: RawFieldDefinition): FieldTypeInference;
893
- /**
894
- * Infer type from field helper call (removed)
895
- *
896
- * Field helpers have been removed in favor of decorators and TypeScript types:
897
- * - Use TypeScript types: name: string = '', price: number = 0.0
898
- * - Use @field() decorator for constraints: @field({ required: true })
899
- * - Use @foreignKey(), @oneToMany(), @manyToMany() decorators for relationships
900
- */
901
- private inferFromHelper;
902
- /**
903
- * Infer type from field decorator
904
- */
905
- private inferFromDecorator;
906
- private extractFieldDecoratorOptions;
907
- private parseReportFieldDecorator;
908
- private parseFieldDecoratorOptions;
909
- private normalizeFieldType;
910
- /**
911
- * Infer type from TypeScript type annotation
912
- */
913
- private inferFromAnnotation;
914
- /**
915
- * Parse default value from initializer string
916
- */
917
- private parseDefaultValue;
918
- /**
919
- * Convert a raw method definition to a manifest `MethodDefinition`.
920
- *
921
- * Returns `null` for `private` or `protected` methods, which are excluded
922
- * from the manifest. Parameters are mapped to the manifest parameter shape
923
- * and default values are parsed via `parseDefaultValue`.
924
- *
925
- * @param method - Raw method definition from a scanned class.
926
- * @returns A manifest-compatible `MethodDefinition`, or `null` if the method
927
- * should be excluded.
928
- */
929
- convertMethod(method: RawMethodDefinition): MethodDefinition | null;
930
- /**
931
- * Simple pluralization for collection names.
932
- *
933
- * This produces the manifest's `collection` label only; the authoritative DDL
934
- * table name is derived independently by core (`classnameToTablename` →
935
- * the `pluralize` library), so this needs to stay self-consistent rather than
936
- * cover every irregular plural. Note the `y → ies` rule fires only after a
937
- * consonant, so vowel+y words pluralise correctly (`Day` → `days`, not
938
- * `daies`).
939
- */
940
- private pluralize;
941
- }
942
-
943
- declare interface MemberExpression extends BaseNode {
944
- type: 'MemberExpression';
945
- object: Expression;
946
- property: Expression;
947
- computed: boolean;
948
- }
949
-
950
- /**
951
- * Merge per-file results into ONE deterministic surface.
952
- *
953
- * Emission must not depend on the order the file system happened to hand files
954
- * to the scanner — a cross-profile parity snapshot that churns on directory
955
- * order proves nothing. So identity is total and content-derived:
956
- *
957
- * - an intent is identified by its `id`, a playbook by its `key`;
958
- * - entries sort by that identity, then by the recorded source path;
959
- * - when two files declare the same identity, the entry from the
960
- * lexicographically smaller path wins and the other is reported as a
961
- * `duplicate-identity` diagnostic — a rule that gives the same answer for
962
- * every input order, which "first one scanned wins" does not;
963
- * - diagnostics sort by path, then line, column, code, and message.
964
- *
965
- * `relativize` maps an absolute scan path to the stable path recorded in the
966
- * artifact; callers pass the package root's relativizer so a checked-in
967
- * artifact never carries a machine-specific absolute path.
968
- */
969
- export declare function mergeAgentSurfaces(surfaces: readonly AgentSurface[], relativize?: (filePath: string) => string): AgentSurface;
970
-
971
- declare interface MethodDefinition {
972
- name: string;
973
- async: boolean;
974
- parameters: Array<{
975
- name: string;
976
- type: string;
977
- optional: boolean;
978
- default?: unknown;
979
- /** See `RawParameterDefinition.typeUnresolved` (#2686). */
980
- typeUnresolved?: boolean;
981
- /** See `RawParameterDefinition.memberTypes` (#2686). */
982
- memberTypes?: string[];
983
- /** See `RawParameterDefinition.unionBranches` (#2686). */
984
- unionBranches?: ParameterTypeBranch[];
985
- }>;
986
- returnType: string;
987
- description?: string;
988
- isStatic: boolean;
989
- isPublic: boolean;
990
- /** Config of an `@method()` decorator on this method (#2686). */
991
- decoratorConfig?: Record<string, unknown>;
992
- }
993
-
994
- declare interface MethodDefinition_2 extends BaseNode {
995
- type: 'MethodDefinition';
996
- key: Expression;
997
- value: FunctionExpression;
998
- kind: 'constructor' | 'method' | 'get' | 'set';
999
- computed: boolean;
1000
- static: boolean;
1001
- accessibility?: 'public' | 'private' | 'protected';
1002
- decorators?: Decorator[];
1003
- }
1004
-
1005
- declare interface NewExpression extends BaseNode {
1006
- type: 'NewExpression';
1007
- callee: Expression;
1008
- arguments: Expression[];
1009
- }
1010
-
1011
- export declare function normalizeGlobSeparators(pattern: string, pathSeparator?: "\\" | "/"): string;
1012
-
1013
- /** One field whose declared precision contradicts its name. */
1014
- export declare interface NumericPrecisionFinding {
1015
- /** `money` wants INTEGER minor units; `rate` wants DECIMAL. */
1016
- kind: NumericPrecisionKind;
1017
- /** Declaring class, e.g. `Invoice`. */
1018
- className: string;
1019
- /** Field name, e.g. `totalAmount`. */
1020
- fieldName: string;
1021
- /** File the class was scanned from. */
1022
- filePath: string;
1023
- /**
1024
- * 1-based line of the field declaration, or `0` when it could not be
1025
- * resolved. The OXC AST nodes this scanner consumes do not carry `loc`, so
1026
- * the line is recovered from the source text when a caller passes it to
1027
- * {@link lintNumericPrecision}.
1028
- */
1029
- line: number;
1030
- /** The initializer that triggered the finding. */
1031
- initializer: string;
1032
- /** Human-readable explanation naming the rule. */
1033
- message: string;
1034
- /** The accepted fixes. */
1035
- remedy: string;
1036
- }
1037
-
1038
- /** Which rule a field falls under, if any. */
1039
- export declare type NumericPrecisionKind = 'money' | 'rate';
1040
-
1041
- declare interface ObjectExpression extends BaseNode {
1042
- type: 'ObjectExpression';
1043
- properties: (Property | SpreadElement)[];
1044
- }
1045
-
1046
- declare interface ObjectPattern extends BaseNode {
1047
- type: 'ObjectPattern';
1048
- properties: (Property | RestElement)[];
1049
- typeAnnotation?: TSTypeAnnotation;
1050
- }
1051
-
1052
- /**
1053
- * Prunes that always apply, on top of whatever `exclude` a caller passes.
1054
- *
1055
- * `exclude` replaces {@link DEFAULT_EXCLUDE} wholesale, so every caller that
1056
- * narrowed the excludes also silently reopened `node_modules` — and installed
1057
- * dependencies are never a project's own `@smrt()` sources. Dot directories are
1058
- * generated or tool state (`.git`, `.svelte-kit`, `.turbo`, `.vercel`, agent
1059
- * scratch) and are pruned for the same reason: nothing authored lives there.
1060
- * `**\/.*` keeps hidden FILES out of the result too, so turning `dot` on to
1061
- * make these prunes work does not quietly widen what gets scanned.
1062
- *
1063
- * These are load-bearing for termination, not just for speed. See
1064
- * {@link OxcScanner.discoverFiles}.
1065
- */
1066
- /**
1067
- * High-performance TypeScript scanner that discovers `@smrt()`-decorated
1068
- * classes in a project's source files.
1069
- *
1070
- * Orchestrates the two-phase scan pipeline:
1071
- * 1. **Phase 1 — Parse** (`scan()`): uses OXC (Rust) to parse TypeScript files
1072
- * in parallel and extract raw class, field, method, and decorator metadata.
1073
- * 2. **Phase 2 — Resolve** (`resolve()`): walks inheritance chains, detects STI
1074
- * hierarchies, and merges fields from ancestor classes.
1075
- *
1076
- * The common path is {@link scanAndResolve} which runs both phases in sequence.
1077
- *
1078
- * @example
1079
- * ```typescript
1080
- * import { OxcScanner } from '@happyvertical/smrt-scanner';
1081
- *
1082
- * const scanner = new OxcScanner({
1083
- * cwd: process.cwd(),
1084
- * include: ['src/**\/*.ts'],
1085
- * exclude: ['**\/*.test.ts'],
1086
- * });
1087
- *
1088
- * const { results, resolved } = await scanner.scanAndResolve();
1089
- * console.log(`Found ${resolved.length} SMRT classes in ${results.fileCount} files`);
1090
- * ```
1091
- *
1092
- * @see {@link OxcScannerOptions} for all available configuration options.
1093
- * @see {@link scanDirectory} for a one-liner convenience wrapper.
1094
- */
1095
- export declare class OxcScanner {
1096
- private options;
1097
- private resolver;
1098
- private scanResults;
1099
- /**
1100
- * Create a new `OxcScanner` with the given options.
1101
- *
1102
- * All options are optional. By default the scanner targets every `.ts` and
1103
- * `.tsx` file under `process.cwd()`, excluding `node_modules`, `dist`,
1104
- * `build`, declaration files, and test files.
1105
- *
1106
- * @param options - Scanner configuration. See {@link OxcScannerOptions}.
1107
- */
1108
- constructor(options?: OxcScannerOptions);
1109
- /**
1110
- * Phase 1 — Discover and parse TypeScript files using OXC.
1111
- *
1112
- * Uses `fast-glob` to enumerate matching files and then parses them in
1113
- * parallel with OXC (Rust). The raw class definitions are registered with
1114
- * the internal {@link InheritanceResolver} for use in the subsequent
1115
- * {@link resolve} call.
1116
- *
1117
- * @returns A {@link ScanResults} object containing all classes found, any
1118
- * parse errors, accumulated type aliases, SMRT import metadata, and
1119
- * aggregate timing information.
1120
- *
1121
- * @example
1122
- * ```typescript
1123
- * const scanner = new OxcScanner({ cwd: '/project' });
1124
- * const results = await scanner.scan();
1125
- * console.log(`Parsed ${results.fileCount} files in ${results.totalParseTimeMs.toFixed(1)}ms`);
1126
- * ```
1127
- */
1128
- scan(): Promise<ScanResults>;
1129
- /**
1130
- * Phase 2 — Resolve inheritance chains for all scanned classes.
1131
- *
1132
- * Must be called after {@link scan}. Walks each class's extends chain,
1133
- * detects STI hierarchies, merges ancestor fields for STI subclasses, and
1134
- * marks framework base classes.
1135
- *
1136
- * @returns An array of {@link ResolvedClassDefinition} objects — one for
1137
- * every class that either carries `@smrt()` or extends a framework base
1138
- * class (`SmrtObject`, `SmrtClass`, `SmrtCollection`).
1139
- *
1140
- * @throws {Error} If called before {@link scan}.
1141
- *
1142
- * @see {@link scanAndResolve} to run both phases in one call.
1143
- */
1144
- resolve(): ResolvedClassDefinition[];
1145
- /**
1146
- * Run both scan phases in a single call.
1147
- *
1148
- * Equivalent to calling `await scanner.scan()` followed by
1149
- * `scanner.resolve()`. This is the most common entry point for callers
1150
- * that want the fully-resolved manifest-ready data in one step.
1151
- *
1152
- * @returns An object with:
1153
- * - `results` — raw {@link ScanResults} from Phase 1.
1154
- * - `resolved` — array of {@link ResolvedClassDefinition} from Phase 2.
1155
- *
1156
- * @example
1157
- * ```typescript
1158
- * const scanner = new OxcScanner({ cwd: '/project/src' });
1159
- * const { results, resolved } = await scanner.scanAndResolve();
1160
- * // resolved is ready to pass to ManifestAdapter.toManifest()
1161
- * ```
1162
- *
1163
- * @see {@link ManifestAdapter} to convert `resolved` into a manifest JSON.
1164
- */
1165
- scanAndResolve(): Promise<{
1166
- results: ScanResults;
1167
- resolved: ResolvedClassDefinition[];
1168
- }>;
1169
- /**
1170
- * Register an external package manifest for cross-package base class resolution.
1171
- *
1172
- * When a project class extends a class defined in an installed SMRT package,
1173
- * the resolver needs access to that package's class definitions to walk the
1174
- * full inheritance chain. Call this method with each external package's
1175
- * {@link ExternalManifest} before calling {@link scan} or {@link resolve}.
1176
- *
1177
- * @param manifest - The external manifest to register, including `packageName`,
1178
- * `packageVersion`, and a `classes` map keyed by class name.
1179
- *
1180
- * @see {@link ExternalManifest}
1181
- */
1182
- addExternalManifest(manifest: ExternalManifest): void;
1183
- /**
1184
- * Scan all discovered files for @happyvertical/smrt-* imports.
1185
- * Returns a map of package name → Set of imported class names.
1186
- *
1187
- * Used for tree-shaking: only external objects that are actually imported
1188
- * in the project's source files will be included in the manifest.
1189
- *
1190
- * Must be called after scan() or as part of scanAndResolve().
1191
- *
1192
- * @example
1193
- * ```typescript
1194
- * const scanner = new OxcScanner({ cwd: process.cwd() });
1195
- * await scanner.scan();
1196
- * const imports = scanner.scanSmrtImports();
1197
- * // Map { '@happyvertical/smrt-profiles' => Set { 'Person', 'Organization' } }
1198
- * ```
1199
- */
1200
- scanSmrtImports(): Map<string, Set<string>>;
1201
- /**
1202
- * Return aggregate statistics about the last scan.
1203
- *
1204
- * Can be called after {@link scan} has completed. Returns counts useful for
1205
- * diagnostics and the `--stats` CLI flag.
1206
- *
1207
- * @returns An object with:
1208
- * - `totalClasses` — total class declarations seen (including non-SMRT).
1209
- * - `smrtClasses` — classes with `@smrt()` decorator.
1210
- * - `stiClasses` — SMRT classes participating in an STI hierarchy.
1211
- * - `maxInheritanceDepth` — length of the deepest inheritance chain.
1212
- * - `fileCount` — number of files scanned.
1213
- * - `parseTimeMs` — total wall-clock parse time in milliseconds.
1214
- */
1215
- getStats(): {
1216
- totalClasses: number;
1217
- smrtClasses: number;
1218
- stiClasses: number;
1219
- maxInheritanceDepth: number;
1220
- fileCount: number;
1221
- parseTimeMs: number;
1222
- };
1223
- /**
1224
- * Discover files to scan using fast-glob.
1225
- *
1226
- * Two settings here decide whether discovery terminates at all when the
1227
- * scanner is pointed at an application root rather than a package `src/`:
1228
- *
1229
- * - `dot: true`. Without it a `**` in an ignore pattern cannot cross a
1230
- * dot segment, so `**\/node_modules/**` prunes `node_modules` at the root
1231
- * but NOT `.svelte-kit/…/node_modules` or any other `node_modules` under a
1232
- * dot directory. Those subtrees were then walked in full and every entry
1233
- * discarded — unbounded work that could never produce a match.
1234
- * - `followSymbolicLinks: false`. pnpm materializes `node_modules` as a
1235
- * symlink graph with cycles, so a link-following walk revisits the same
1236
- * real directories once per path that reaches them.
1237
- *
1238
- * Together they were enough to exhaust a 4 GB heap on a consumer app that
1239
- * installs the published packages (#2275).
1240
- *
1241
- * Patterns are rewritten relative to `cwd` first. fast-glob matches `ignore`
1242
- * in whatever space the patterns use, so an absolute pattern would hand
1243
- * `**\/.*\/**` the project's own ancestors — a checkout under `~/.worktrees`
1244
- * or `~/.cache` would then match nothing at all, silently.
1245
- */
1246
- private discoverFiles;
1247
- /**
1248
- * Find declarations in files the CLASS scan did not cover.
1249
- *
1250
- * The class `include` is routinely narrowed to where models live, but an
1251
- * intent sidecar lives beside its component. Without this pass those
1252
- * declarations would be missing from every artifact with no diagnostic — a
1253
- * silent omission, and in the shipped SvelteKit template's own layout at
1254
- * that. Files already parsed by the class scan are skipped so a declaration
1255
- * is never counted twice and cannot collide with itself.
1256
- *
1257
- * @param alreadyScanned - Absolute paths the class scan already parsed.
1258
- */
1259
- private scanDeclarationsOutsideClassGlob;
1260
- /**
1261
- * Search `.svelte` files for declarations the scanner can never read.
1262
- *
1263
- * A `.svelte` file is not a TypeScript program and is never parsed here, so
1264
- * this pass produces diagnostics only — never an emitted entry. It exists
1265
- * because the alternative is silence: an intent declared inline in a
1266
- * component simply would not appear anywhere, with nothing to explain why.
1267
- */
1268
- private scanSvelteDeclarations;
1269
- /**
1270
- * Record a declaring module as a `cwd`-relative POSIX path.
1271
- *
1272
- * Emitted entries land in checked-in artifacts, so an absolute path would
1273
- * make them machine-specific and a Windows separator would make them
1274
- * platform-specific — either one churns a snapshot that is supposed to prove
1275
- * two builds agree.
1276
- */
1277
- private relativizeSourcePath;
1278
- /**
1279
- * Parse a single file with timing
1280
- */
1281
- private parseFileWithTiming;
1282
- }
1283
-
1284
- /**
1285
- * Configuration options for {@link OxcScanner}.
1286
- *
1287
- * All fields are optional; reasonable defaults are applied when omitted.
1288
- *
1289
- * @see {@link OxcScanner}
1290
- */
1291
- export declare interface OxcScannerOptions {
1292
- /** Glob patterns to include */
1293
- include?: string[];
1294
- /** Glob patterns to exclude */
1295
- exclude?: string[];
1296
- /** Base directory for scanning */
1297
- cwd?: string;
1298
- /** Path to tsconfig.json for module resolution */
1299
- tsconfig?: string;
1300
- /** Whether to follow imports to find base classes */
1301
- followImports?: boolean;
1302
- /** Known base classes (avoid resolution) */
1303
- baseClasses?: string[];
1304
- /** Include private methods in output */
1305
- includePrivateMethods?: boolean;
1306
- /** Include static methods in output */
1307
- includeStaticMethods?: boolean;
1308
- /** External package manifests for base class resolution */
1309
- externalManifests?: Map<string, ExternalManifest>;
1310
- /**
1311
- * Follow symbolic links while discovering files. Defaults to `false`.
1312
- *
1313
- * A package's own sources are real files, while a pnpm `node_modules` is a
1314
- * symlink graph with cycles: every store entry links back out to its
1315
- * siblings, so a link-following walk reaches the same real directory once per
1316
- * path that leads to it and never terminates in practice. Discovery therefore
1317
- * stays on real directories unless a caller explicitly opts back in.
1318
- */
1319
- followSymbolicLinks?: boolean;
1320
- /**
1321
- * Match module-scope `defineIntent()` / `definePlaybook()` declarations and
1322
- * report `.svelte` declarations the scanner can never read (#2591). Defaults
1323
- * to `true`.
1324
- */
1325
- agentSurface?: boolean;
1326
- /**
1327
- * Glob patterns searched for `.svelte` declarations the scanner cannot read.
1328
- * Only used when {@link OxcScannerOptions.agentSurface} is enabled.
1329
- */
1330
- svelteInclude?: string[];
1331
- /**
1332
- * Glob patterns searched for `defineIntent` / `definePlaybook` declarations,
1333
- * independently of {@link OxcScannerOptions.include}.
1334
- *
1335
- * A model scan is routinely narrowed to where models live, but an intent
1336
- * sidecar lives beside the component that uses it; binding declaration
1337
- * discovery to the class glob would silently drop those. Only used when
1338
- * {@link OxcScannerOptions.agentSurface} is enabled.
1339
- */
1340
- agentSurfaceInclude?: string[];
1341
- }
1342
-
1343
- /** One branch of a top-level union parameter type (#2686). */
1344
- export declare interface ParameterTypeBranch {
1345
- /** The branch's own type name, e.g. `string` or `object`. */
1346
- type: string;
1347
- /** Inline object members declared by THIS branch only. */
1348
- memberTypes?: string[];
1349
- }
1350
-
1351
- /**
1352
- * Read one file for agent-surface declarations ONLY (#2591).
1353
- *
1354
- * Exists because the agent surface must not be confined to the class scan's
1355
- * `include` glob: an application that scans `src/lib/objects/**` for its models
1356
- * — the shipped template does exactly that — would otherwise never see a
1357
- * `src/lib/agent/Foo.intents.ts` sidecar, and the declaration would vanish from
1358
- * every artifact with no diagnostic. Silent omission is the one failure this
1359
- * matcher exists to prevent, so declarations are discovered on their own terms.
1360
- *
1361
- * The token pre-filter runs before the parse, so a file that declares nothing
1362
- * costs one read and one `String.includes`.
1363
- *
1364
- * @returns The file's surface, or `undefined` when it declares nothing or
1365
- * cannot be read.
1366
- */
1367
- export declare function parseAgentSurfaceFile(filePath: string): AgentSurface | undefined;
1368
-
1369
- /**
1370
- * Parse a single TypeScript file and extract SMRT class definitions.
1371
- *
1372
- * Reads the file from disk, runs oxc-parser on it, and returns all class
1373
- * definitions found, any parse errors, accumulated type aliases, and
1374
- * `@happyvertical/smrt-*` import metadata.
1375
- *
1376
- * @param filePath - Absolute path to the `.ts` or `.tsx` file to parse.
1377
- * @returns A {@link FileScanResult} containing classes, errors, type aliases,
1378
- * SMRT imports, and timing information for the file.
1379
- *
1380
- * @example
1381
- * ```typescript
1382
- * import { parseFile } from '@happyvertical/smrt-scanner';
1383
- *
1384
- * const result = parseFile('/project/src/models/Product.ts');
1385
- * console.log(result.classes.map((c) => c.className));
1386
- * // ['Product', 'ProductCollection']
1387
- * ```
1388
- *
1389
- * @see {@link parseSource} to parse a source string directly (e.g. in tests).
1390
- */
1391
- export declare function parseFile(filePath: string): FileScanResult;
1392
-
1393
- /**
1394
- * Parse TypeScript source text directly and extract SMRT class definitions.
1395
- *
1396
- * Identical to {@link parseFile} but accepts a source string instead of a
1397
- * file path. Primarily used in tests and tooling that constructs source
1398
- * programmatically.
1399
- *
1400
- * @param sourceText - Raw TypeScript source code to parse.
1401
- * @param filename - Virtual filename used to determine the parser language
1402
- * mode (`.ts`, `.tsx`, `.js`, `.jsx`) and to populate `filePath` fields in
1403
- * the result. Defaults to `'test.ts'`.
1404
- * @returns A {@link FileScanResult} containing classes, errors, type aliases,
1405
- * and SMRT import metadata extracted from the source text.
1406
- *
1407
- * @example
1408
- * ```typescript
1409
- * import { parseSource } from '@happyvertical/smrt-scanner';
1410
- *
1411
- * const src = `
1412
- * import { smrt } from '@happyvertical/smrt-core';
1413
- * @smrt()
1414
- * export class Widget extends SmrtObject {
1415
- * label: string = '';
1416
- * }
1417
- * `;
1418
- * const result = parseSource(src, 'Widget.ts');
1419
- * console.log(result.classes[0].className); // 'Widget'
1420
- * ```
1421
- *
1422
- * @see {@link parseFile} to parse a file from disk.
1423
- */
1424
- export declare function parseSource(sourceText: string, filename?: string): FileScanResult;
1425
-
1426
- declare type Pattern = Identifier | AssignmentPattern | RestElement | ObjectPattern | ArrayPattern;
1427
-
1428
- declare interface Position {
1429
- line: number;
1430
- column: number;
1431
- }
1432
-
1433
- declare interface Property extends BaseNode {
1434
- type: 'Property';
1435
- key: Expression;
1436
- value: Expression | Pattern;
1437
- kind: 'init' | 'get' | 'set';
1438
- method: boolean;
1439
- shorthand: boolean;
1440
- computed: boolean;
1441
- }
1442
-
1443
- declare interface PropertyDefinition extends BaseNode {
1444
- type: 'PropertyDefinition';
1445
- key: Expression;
1446
- value: Expression | null;
1447
- computed: boolean;
1448
- static: boolean;
1449
- readonly?: boolean;
1450
- optional?: boolean;
1451
- accessibility?: 'public' | 'private' | 'protected';
1452
- typeAnnotation?: TSTypeAnnotation;
1453
- decorators?: Decorator[];
1454
- }
1455
-
1456
- /**
1457
- * Qualified class name format: "@package/name:ClassName"
1458
- * Uniquely identifies classes across packages.
1459
- */
1460
- declare type QualifiedClassName = `${string}:${string}`;
1461
-
1462
- /**
1463
- * Type definitions for OXC-based SMRT scanner
1464
- *
1465
- * This module defines:
1466
- * 1. Raw types - Intermediate representation from OXC parsing (Phase 1)
1467
- * 2. Resolved types - After inheritance resolution (Phase 2)
1468
- * 3. Re-exports of smrt-core types for compatibility
1469
- */
1470
- /**
1471
- * Raw class definition extracted from OXC AST
1472
- * Contains only syntactic information, no semantic resolution
1473
- */
1474
- export declare interface RawClassDefinition {
1475
- /** Class name as declared in source */
1476
- className: string;
1477
- /** Absolute path to source file */
1478
- filePath: string;
1479
- /** Parent class name from extends clause (null if none) */
1480
- extendsClause: string | null;
1481
- /** Generic type argument from extends (e.g., "Meeting" from SmrtCollection<Meeting>) */
1482
- extendsTypeArg: string | null;
1483
- /** Parsed @smrt() decorator configuration object */
1484
- decoratorConfig: RawDecoratorConfig | null;
1485
- /** Has @smrt() decorator */
1486
- hasSmartDecorator: boolean;
1487
- /** Class properties/fields */
1488
- fields: RawFieldDefinition[];
1489
- /** Class methods */
1490
- methods: RawMethodDefinition[];
1491
- /** Start line in source file */
1492
- startLine: number;
1493
- /** End line in source file */
1494
- endLine: number;
1495
- }
1496
-
1497
- /**
1498
- * Raw decorator information
1499
- */
1500
- export declare interface RawDecorator {
1501
- /** Decorator name (e.g., "field", "foreignKey") */
1502
- name: string;
1503
- /** Raw arguments as strings */
1504
- arguments: string[];
1505
- }
1506
-
1507
- /**
1508
- * Raw @smrt() decorator configuration
1509
- */
1510
- export declare interface RawDecoratorConfig {
1511
- /** Table strategy: 'sti' | 'cti' */
1512
- tableStrategy?: 'sti' | 'cti';
1513
- /** Storage type for the generated id primary key */
1514
- idType?: 'uuid' | 'text';
1515
- /** Code-owned feature toggle declarations */
1516
- features?: Record<string, {
1517
- defaultEnabled: boolean;
1518
- label?: string;
1519
- description?: string;
1520
- metadata?: Record<string, unknown>;
1521
- }>;
1522
- /** API configuration */
1523
- api?: {
1524
- include?: string[];
1525
- exclude?: string[];
1526
- };
1527
- /** CLI configuration */
1528
- cli?: boolean | {
1529
- include?: string[];
1530
- exclude?: string[];
1531
- skipApiCheck?: boolean | string[];
1532
- http?: boolean;
1533
- };
1534
- /** MCP configuration */
1535
- mcp?: {
1536
- include?: string[];
1537
- exclude?: string[];
1538
- };
1539
- /** Raw config object for unknown properties */
1540
- [key: string]: unknown;
1541
- }
1542
-
1543
- /**
1544
- * Raw field definition from OXC AST
1545
- */
1546
- export declare interface RawFieldDefinition {
1547
- /** Field name */
1548
- name: string;
1549
- /** TypeScript type annotation as string (e.g., "string", "number", "Date") */
1550
- typeAnnotation: string | null;
1551
- /** Raw initializer expression as string */
1552
- initializer: string | null;
1553
- /** For numeric literals: whether it contains a decimal point */
1554
- hasDecimalPoint: boolean;
1555
- /** For numeric literals: the actual numeric value */
1556
- numericValue: number | null;
1557
- /** Decorators applied to this field */
1558
- decorators: RawDecorator[];
1559
- /** Whether field is optional (has ?) */
1560
- optional: boolean;
1561
- /** Whether field is static */
1562
- isStatic: boolean;
1563
- /** Whether field is readonly */
1564
- readonly: boolean;
1565
- /** Whether field is private/protected */
1566
- accessibility: 'public' | 'private' | 'protected';
1567
- /** Start line in source */
1568
- line: number;
1569
- }
1570
-
1571
- /**
1572
- * Raw method definition from OXC AST
1573
- */
1574
- export declare interface RawMethodDefinition {
1575
- /** Method name */
1576
- name: string;
1577
- /** Whether method is async */
1578
- async: boolean;
1579
- /** Whether method is static */
1580
- isStatic: boolean;
1581
- /** Accessibility modifier */
1582
- accessibility: 'public' | 'private' | 'protected';
1583
- /** Method parameters */
1584
- parameters: RawParameterDefinition[];
1585
- /** Return type annotation as string */
1586
- returnType: string | null;
1587
- /** JSDoc description if present */
1588
- description: string | null;
1589
- /**
1590
- * Config object of an `@method()` decorator on this method, when one is
1591
- * present. `{}` for a bare `@method()`; `undefined` when the method carries
1592
- * no `@method()` decorator at all — the two are distinct, because an
1593
- * explicit bare decorator still marks the method as deliberately reviewed.
1594
- *
1595
- * Values are extracted with the same literal-only rules the class-level
1596
- * `@smrt()` config uses, so an expression the scanner cannot resolve becomes
1597
- * a scan error rather than a silently dropped `expose: false`.
1598
- */
1599
- decoratorConfig?: Record<string, unknown>;
1600
- /** Start line in source */
1601
- line: number;
1602
- }
1603
-
1604
- /**
1605
- * Raw parameter definition
1606
- */
1607
- export declare interface RawParameterDefinition {
1608
- /** Parameter name */
1609
- name: string;
1610
- /** Type annotation as string */
1611
- type: string | null;
1612
- /** Whether parameter is optional */
1613
- optional: boolean;
1614
- /** Default value as string */
1615
- defaultValue: string | null;
1616
- /**
1617
- * True when the parameter carries a type annotation the scanner could not
1618
- * express as a string — an intersection, tuple, conditional, mapped,
1619
- * `typeof`, or indexed-access type — or when an inline object literal
1620
- * contains such a member.
1621
- *
1622
- * This is the provenance that separates "the author wrote `any`" from "the
1623
- * scanner gave up": both previously reached the manifest as the string
1624
- * `'any'`. Consumers that must fail closed on an uncertain type (the API
1625
- * wire-ability gate) read this rather than trusting `type`.
1626
- *
1627
- * `type: null` with `typeUnresolved` absent means the parameter simply has
1628
- * no annotation, which is an implicit — and genuinely authored — `any`.
1629
- */
1630
- typeUnresolved?: boolean;
1631
- /**
1632
- * Resolved member types of an INLINE object-literal annotation
1633
- * (`{ onDone: () => void; target: Content }` → `['Function', 'Content']`),
1634
- * flattened across nested literals, arrays, and unions.
1635
- *
1636
- * `extractTypeName` collapses every inline literal to the single string
1637
- * `'object'`, which erases exactly the members a caller needs to judge
1638
- * whether the bag can cross a wire. NAMED bags (an interface, type alias,
1639
- * `Partial<>`/`Pick<>`) are deliberately NOT expanded — resolving them needs
1640
- * cross-file type resolution this AST layer does not do, and they are
1641
- * accepted heuristically by the consumers that care.
1642
- */
1643
- memberTypes?: string[];
1644
- /**
1645
- * For a top-level UNION, each branch with the inline members IT declared.
1646
- *
1647
- * `memberTypes` above flattens across branches, which lets one branch veto
1648
- * another: `{ callback: () => void } | string` is wire-able through its
1649
- * string branch, but the flattened `Function` rejected the whole parameter.
1650
- * A consumer that understands this field MUST prefer it over `memberTypes`
1651
- * for unions. Absent on a non-union parameter and on manifests generated
1652
- * before #2686.
1653
- */
1654
- unionBranches?: ParameterTypeBranch[];
1655
- }
1656
-
1657
- /** Preserve relative glob escapes; normalize separators only after rewriting. */
1658
- export declare function relativeGlobToCwd(pattern: string, cwd: string): string;
1659
-
1660
- /**
1661
- * Resolved class definition with inheritance chain
1662
- */
1663
- export declare interface ResolvedClassDefinition extends RawClassDefinition {
1664
- /** Full inheritance chain from base to this class */
1665
- inheritanceChain: string[];
1666
- /** STI base class name (if part of STI hierarchy) */
1667
- stiBase: string | null;
1668
- /** Effective table strategy (inherited or declared) */
1669
- effectiveTableStrategy: 'sti' | 'cti';
1670
- /** Whether this class uses STI (convenience boolean) */
1671
- isSTI: boolean;
1672
- /** Whether this class is a framework base class */
1673
- isFrameworkBase: boolean;
1674
- /** All fields including inherited (for STI) */
1675
- allFields: RawFieldDefinition[];
1676
- /** Package this class belongs to (if external) */
1677
- packageName: string | null;
1678
- }
1679
-
1680
- declare interface RestElement extends BaseNode {
1681
- type: 'RestElement';
1682
- argument: Pattern;
1683
- typeAnnotation?: TSTypeAnnotation;
1684
- }
1685
-
1686
- /**
1687
- * Scan error
1688
- */
1689
- export declare interface ScanError {
1690
- /** Error message */
1691
- message: string;
1692
- /** Source file */
1693
- filePath: string;
1694
- /** Line number (1-based) */
1695
- line?: number;
1696
- /** Column number (1-based) */
1697
- column?: number;
1698
- /** Error severity */
1699
- severity: 'error' | 'warning';
1700
- }
1701
-
1702
- /**
1703
- * Result from scanning multiple files
1704
- */
1705
- export declare interface ScanResults {
1706
- /** All scanned files */
1707
- files: FileScanResult[];
1708
- /** All classes found (flattened) */
1709
- classes: RawClassDefinition[];
1710
- /** All errors (flattened) */
1711
- errors: ScanError[];
1712
- /** Total parse time in milliseconds */
1713
- totalParseTimeMs: number;
1714
- /** Number of files scanned */
1715
- fileCount: number;
1716
- /** Accumulated type aliases across all files */
1717
- typeAliases: Record<string, string>;
1718
- /** Accumulated SMRT package imports across all files (package name → Set of imported class names) */
1719
- smrtImports?: Map<string, Set<string>>;
1720
- /**
1721
- * The project's declared agent-addressable surface, merged across files with
1722
- * deterministic identity so emission never depends on file order (#2591).
1723
- */
1724
- agentSurface: AgentSurface;
1725
- }
1726
-
1727
- export declare function scanSvelteAgentSurface(filePath: string, sourceText?: string): AgentSurfaceDiagnostic[];
1728
-
1729
- declare interface SmartObjectConfig {
1730
- tableStrategy?: 'sti' | 'cti';
1731
- idType?: 'uuid' | 'text';
1732
- features?: Record<string, {
1733
- defaultEnabled: boolean;
1734
- label?: string;
1735
- description?: string;
1736
- metadata?: Record<string, unknown>;
1737
- }>;
1738
- api?: {
1739
- include?: string[];
1740
- exclude?: string[];
1741
- };
1742
- cli?: boolean | {
1743
- include?: string[];
1744
- exclude?: string[];
1745
- skipApiCheck?: boolean | string[];
1746
- http?: boolean;
1747
- };
1748
- mcp?: {
1749
- include?: string[];
1750
- exclude?: string[];
1751
- };
1752
- [key: string]: unknown;
1753
- }
1754
-
1755
- declare interface SmartObjectDefinition {
1756
- name: string;
1757
- className: string;
1758
- qualifiedName?: QualifiedClassName;
1759
- collection: string;
1760
- filePath: string;
1761
- packageName?: string;
1762
- packageVersion?: string;
1763
- importPath?: string;
1764
- modulePath?: string;
1765
- exportName?: string;
1766
- collectionExportName?: string;
1767
- fields: Record<string, FieldDefinition>;
1768
- methods: Record<string, MethodDefinition>;
1769
- decoratorConfig: SmartObjectConfig;
1770
- extends?: string;
1771
- extendsTypeArg?: string;
1772
- staticProperties?: Record<string, unknown>;
1773
- }
1774
-
1775
- declare interface SmartObjectManifest {
1776
- version: string;
1777
- /** Always {@link MANIFEST_TIMESTAMP}: build output must be reproducible. */
1778
- timestamp: number;
1779
- packageName?: string;
1780
- packageVersion?: string;
1781
- objects: Record<string, SmartObjectDefinition>;
1782
- moduleType?: string;
1783
- smrtDependencies?: string[];
1784
- }
1785
-
1786
- export declare interface SourceDiscoveryOptions {
1787
- cwd: string;
1788
- include: string[];
1789
- exclude: string[];
1790
- followSymbolicLinks?: boolean;
1791
- }
1792
-
1793
- declare interface SourceLocation {
1794
- start: Position;
1795
- end: Position;
1796
- }
1797
-
1798
- /**
1799
- * Cheap pre-filter so callers can skip parsing files that cannot produce a
1800
- * finding.
1801
- *
1802
- * Conservative by construction: every condition here is textually implied by a
1803
- * finding. A reported field lives in a class carrying `@smrt` or
1804
- * `extends Smrt…`, is initialized to a number, and has a money or rate word at
1805
- * one of the boundaries the head-noun splitter cuts on. It may still return
1806
- * `true` for a file the AST pass then clears — that is the intended direction.
1807
- *
1808
- * @param source - Full file contents.
1809
- * @returns `false` only when the file provably cannot produce a finding.
1810
- */
1811
- export declare function sourceMayContainNumericPrecisionIssue(source: string): boolean;
1812
-
1813
- /**
1814
- * Fast pre-check: a file that names neither helper cannot declare either, so
1815
- * the AST walk is skipped entirely. Callers use it to keep the matcher off the
1816
- * hot path of a large scan.
1817
- */
1818
- export declare function sourceMayDeclareAgentSurface(sourceText: string): boolean;
1819
-
1820
- /**
1821
- * Split an identifier into lowercase words on camelCase, PascalCase, digits,
1822
- * and underscores. `totalAmountCents` → `['total', 'amount', 'cents']`.
1823
- */
1824
- export declare function splitIdentifierWords(name: string): string[];
1825
-
1826
- declare interface SpreadElement extends BaseNode {
1827
- type: 'SpreadElement';
1828
- argument: Expression;
1829
- }
1830
-
1831
- declare type Statement = ClassDeclaration | ExportNamedDeclaration | ExportDefaultDeclaration | ImportDeclaration | TSTypeAliasDeclaration | TSEnumDeclaration | VariableDeclaration;
1832
-
1833
- declare interface TSAnyKeyword extends BaseNode {
1834
- type: 'TSAnyKeyword';
1835
- }
1836
-
1837
- declare interface TSArrayType extends BaseNode {
1838
- type: 'TSArrayType';
1839
- elementType: TSType;
1840
- }
1841
-
1842
- declare interface TSBigIntKeyword extends BaseNode {
1843
- type: 'TSBigIntKeyword';
1844
- }
1845
-
1846
- declare interface TSBooleanKeyword extends BaseNode {
1847
- type: 'TSBooleanKeyword';
1848
- }
1849
-
1850
- declare interface TSEnumBody extends BaseNode {
1851
- type: 'TSEnumBody';
1852
- members: TSEnumMember[];
1853
- }
1854
-
1855
- declare interface TSEnumDeclaration extends BaseNode {
1856
- type: 'TSEnumDeclaration';
1857
- id: Identifier;
1858
- body?: TSEnumBody;
1859
- members?: TSEnumMember[];
1860
- }
1861
-
1862
- declare interface TSEnumMember extends BaseNode {
1863
- type: 'TSEnumMember';
1864
- initializer?: Expression;
1865
- }
1866
-
1867
- declare interface TSFunctionType extends BaseNode {
1868
- type: 'TSFunctionType';
1869
- }
1870
-
1871
- declare interface TSIndexSignature extends BaseNode {
1872
- type: 'TSIndexSignature';
1873
- parameters: Identifier[];
1874
- typeAnnotation?: TSTypeAnnotation;
1875
- }
1876
-
1877
- declare interface TSLiteralType extends BaseNode {
1878
- type: 'TSLiteralType';
1879
- literal?: Literal;
1880
- }
1881
-
1882
- declare interface TSMethodSignature extends BaseNode {
1883
- type: 'TSMethodSignature';
1884
- key: Expression;
1885
- params: Pattern[];
1886
- returnType?: TSTypeAnnotation;
1887
- }
1888
-
1889
- declare interface TSNeverKeyword extends BaseNode {
1890
- type: 'TSNeverKeyword';
1891
- }
1892
-
1893
- declare interface TSNullKeyword extends BaseNode {
1894
- type: 'TSNullKeyword';
1895
- }
1896
-
1897
- declare interface TSNumberKeyword extends BaseNode {
1898
- type: 'TSNumberKeyword';
1899
- }
1900
-
1901
- declare interface TSObjectKeyword extends BaseNode {
1902
- type: 'TSObjectKeyword';
1903
- }
1904
-
1905
- declare interface TSPropertySignature extends BaseNode {
1906
- type: 'TSPropertySignature';
1907
- key: Expression;
1908
- typeAnnotation?: TSTypeAnnotation;
1909
- optional?: boolean;
1910
- readonly?: boolean;
1911
- }
1912
-
1913
- declare interface TSQualifiedName extends BaseNode {
1914
- type: 'TSQualifiedName';
1915
- left: Identifier | TSQualifiedName;
1916
- right: Identifier;
1917
- }
1918
-
1919
- declare interface TSStringKeyword extends BaseNode {
1920
- type: 'TSStringKeyword';
1921
- }
1922
-
1923
- declare interface TSSymbolKeyword extends BaseNode {
1924
- type: 'TSSymbolKeyword';
1925
- }
1926
-
1927
- declare interface TSThisType extends BaseNode {
1928
- type: 'TSThisType';
1929
- }
1930
-
1931
- declare type TSType = TSStringKeyword | TSNumberKeyword | TSBooleanKeyword | TSAnyKeyword | TSUnknownKeyword | TSNeverKeyword | TSObjectKeyword | TSBigIntKeyword | TSSymbolKeyword | TSVoidKeyword | TSNullKeyword | TSUndefinedKeyword | TSThisType | TSTypeReference | TSArrayType | TSUnionType | TSTypeLiteral | TSLiteralType | TSFunctionType;
1932
-
1933
- declare interface TSTypeAliasDeclaration extends BaseNode {
1934
- type: 'TSTypeAliasDeclaration';
1935
- id: Identifier;
1936
- typeAnnotation: TSType;
1937
- }
1938
-
1939
- declare interface TSTypeAnnotation extends BaseNode {
1940
- type: 'TSTypeAnnotation';
1941
- typeAnnotation: TSType;
1942
- }
1943
-
1944
- declare type TSTypeElement = TSPropertySignature | TSMethodSignature | TSIndexSignature;
1945
-
1946
- declare interface TSTypeLiteral extends BaseNode {
1947
- type: 'TSTypeLiteral';
1948
- members: TSTypeElement[];
1949
- }
1950
-
1951
- declare interface TSTypeParameterInstantiation extends BaseNode {
1952
- type: 'TSTypeParameterInstantiation';
1953
- params: TSType[];
1954
- }
1955
-
1956
- declare interface TSTypeReference extends BaseNode {
1957
- type: 'TSTypeReference';
1958
- typeName: Identifier | TSQualifiedName;
1959
- typeParameters?: TSTypeParameterInstantiation;
1960
- typeArguments?: TSTypeParameterInstantiation;
1961
- }
1962
-
1963
- declare interface TSUndefinedKeyword extends BaseNode {
1964
- type: 'TSUndefinedKeyword';
1965
- }
1966
-
1967
- declare interface TSUnionType extends BaseNode {
1968
- type: 'TSUnionType';
1969
- types: TSType[];
1970
- }
1971
-
1972
- declare interface TSUnknownKeyword extends BaseNode {
1973
- type: 'TSUnknownKeyword';
1974
- }
1975
-
1976
- declare interface TSVoidKeyword extends BaseNode {
1977
- type: 'TSVoidKeyword';
1978
- }
1979
-
1980
- declare interface UnaryExpression extends BaseNode {
1981
- type: 'UnaryExpression';
1982
- operator: string;
1983
- argument: Expression;
1984
- }
1985
-
1986
- declare interface VariableDeclaration extends BaseNode {
1987
- type: 'VariableDeclaration';
1988
- kind: 'const' | 'let' | 'var';
1989
- declarations: VariableDeclarator[];
1990
- }
1991
-
1992
- declare interface VariableDeclarator extends BaseNode {
1993
- type: 'VariableDeclarator';
1994
- id: Identifier | Pattern;
1995
- init: Expression | null;
1996
- }
1997
-
1998
- /**
1999
- * Verify that `dist/manifest.json` contains every `@smrt()` object declared in
2000
- * the package source. See module docs for the rationale and guarantees.
2001
- */
2002
- export declare function verifyManifestCompleteness(options: VerifyManifestCompletenessOptions): Promise<VerifyManifestCompletenessResult>;
2003
-
2004
- export declare interface VerifyManifestCompletenessOptions {
2005
- /** Absolute or relative path to the package directory to verify. */
2006
- packageDir: string;
2007
- /** Source include globs (default mirrors the build). */
2008
- include?: string[];
2009
- /** Source exclude globs (default mirrors the build). */
2010
- exclude?: string[];
2011
- /** Package directory basenames to skip (default: framework infrastructure). */
2012
- skipPackages?: string[];
2013
- }
2014
-
2015
- export declare interface VerifyManifestCompletenessResult {
2016
- status: VerifyManifestStatus;
2017
- packageName?: string;
2018
- manifestPath?: string;
2019
- /** Qualified object keys present in source but missing from the manifest. */
2020
- missing: string[];
2021
- /** Number of objects the source is expected to contribute. */
2022
- expectedCount: number;
2023
- /** Number of objects present in the published manifest. */
2024
- distCount: number;
2025
- /** Human-readable explanation for `skipped` / `missing-manifest`. */
2026
- reason?: string;
2027
- }
2028
-
2029
- /**
2030
- * Manifest completeness verification.
2031
- *
2032
- * Re-scans a package's `src/` with the same OXC scanner the build uses and
2033
- * asserts that every `@smrt()`-decorated object (and its collection) is present
2034
- * in the package's published `dist/manifest.json`. This is a publish-time guard:
2035
- * downstream schema migration is manifest-driven, so a stale manifest that omits
2036
- * an object means consumers can never create its table via `smrt db:migrate`.
2037
- *
2038
- * Root cause it guards against (issue #1483): `@happyvertical/smrt-jobs@0.27.41`
2039
- * shipped a `dist/manifest.json` generated before `SmrtWorker` existed. The
2040
- * source, exports, and `__smrt-register__` path were all correct, but the
2041
- * published manifest had only 4 of 6 objects, so `_smrt_workers` was never
2042
- * created and `TaskRunner.start()` threw on every consumer that upgraded.
2043
- *
2044
- * The check compares object-key SETS only. The downstream manifest-enrichment
2045
- * passes (schema/validation/agent generation) mutate object entries but never
2046
- * add or remove object keys, so the scanner + adapter object set is the source
2047
- * of truth for "which objects the published manifest must contain". The
2048
- * comparison is `expected ⊆ dist`: enrichment passes that add keys to `dist`
2049
- * (e.g. STI children) never trigger a false failure.
2050
- *
2051
- * Parse errors are handled before the set comparison: a syntactically broken
2052
- * source file drops its objects from `expected` AND `dist` symmetrically, which
2053
- * a naive `expected ⊆ dist` would wave through as `ok`. The scan's
2054
- * `severity:'error'` entries therefore short-circuit to a distinct `scan-error`
2055
- * status so a broken source can never masquerade as a complete manifest.
2056
- *
2057
- * @see https://github.com/happyvertical/smrt/issues/1483
2058
- */
2059
- export declare type VerifyManifestStatus = 'ok' | 'incomplete' | 'missing-manifest' | 'scan-error' | 'skipped';
2060
-
2061
- export { }
1
+ /**
2
+ * @happyvertical/smrt-scanner
3
+ *
4
+ * High-performance TypeScript scanner for SMRT manifest generation.
5
+ * Uses OXC (Rust-based parser) for 2-3x faster scanning than TypeScript compiler.
6
+ *
7
+ * @example
8
+ * ```typescript
9
+ * import { OxcScanner } from '@happyvertical/smrt-scanner';
10
+ *
11
+ * const scanner = new OxcScanner({
12
+ * include: ['src/** /*.ts'],
13
+ * exclude: ['** /*.test.ts'],
14
+ * });
15
+ *
16
+ * const results = await scanner.scan();
17
+ * console.log(`Found ${results.classes.length} SMRT classes`);
18
+ * ```
19
+ */
20
+ export { type AgentSurfaceToolNameOptions, checkAgentSurfaceToolNames, type ExtractAgentSurfaceOptions, emptyAgentSurface, extractAgentSurface, type GeneratedModelToolName, isAgentSurfaceSourcePath, isPrunedAgentSurfacePath, mergeAgentSurfaces, scanSvelteAgentSurface, sourceMayDeclareAgentSurface, } from './agent-surface.js';
21
+ export { discoverSourceFiles, normalizeGlobSeparators, relativeGlobToCwd, type SourceDiscoveryOptions, } from './discovery.js';
22
+ export { InheritanceResolver } from './inheritance-resolver.js';
23
+ export { ManifestAdapter } from './manifest-adapter.js';
24
+ export { classifyNumericFieldName, lintNumericPrecision, type NumericPrecisionFinding, type NumericPrecisionKind, sourceMayContainNumericPrecisionIssue, splitIdentifierWords, } from './numeric-precision-lint.js';
25
+ export { extractSmrtImports, parseAgentSurfaceFile, parseFile, parseSource, } from './oxc-parser.js';
26
+ export { OxcScanner } from './scanner.js';
27
+ export * from './types.js';
28
+ export { type VerifyManifestCompletenessOptions, type VerifyManifestCompletenessResult, type VerifyManifestStatus, verifyManifestCompleteness, } from './verify-completeness.js';
29
+ //# sourceMappingURL=index.d.ts.map