@dereekb/dbx-cli 13.15.0 → 13.17.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.
Files changed (46) hide show
  1. package/eslint/index.cjs.default.js +1 -0
  2. package/eslint/index.cjs.js +1050 -0
  3. package/eslint/index.cjs.mjs +2 -0
  4. package/eslint/index.d.ts +1 -0
  5. package/eslint/index.esm.js +1046 -0
  6. package/eslint/package.json +25 -0
  7. package/eslint/rollup.alias-internal.config.d.ts +11 -0
  8. package/eslint/src/index.d.ts +1 -0
  9. package/eslint/src/lib/index.d.ts +2 -0
  10. package/eslint/src/lib/plugin.d.ts +22 -0
  11. package/eslint/src/lib/valid-dbx-route-model-tags.rule.d.ts +59 -0
  12. package/firebase-api-manifest/main.js +318 -228
  13. package/firebase-api-manifest/package.json +3 -3
  14. package/generate-firestore-indexes/main.js +37 -24
  15. package/generate-firestore-indexes/package.json +2 -2
  16. package/generate-mcp-manifest/main.js +57 -39
  17. package/generate-mcp-manifest/package.json +3 -3
  18. package/generate-route-manifest/main.js +1137 -0
  19. package/generate-route-manifest/package.json +10 -0
  20. package/index.cjs.js +4847 -1953
  21. package/index.esm.js +4827 -1954
  22. package/lint-cache/package.json +2 -2
  23. package/manifest-extract/index.cjs.js +175 -240
  24. package/manifest-extract/index.esm.js +174 -239
  25. package/manifest-extract/package.json +9 -4
  26. package/package.json +16 -6
  27. package/src/lib/index.d.ts +2 -0
  28. package/src/lib/manifest/types.d.ts +53 -0
  29. package/src/lib/mcp-scan/manifest/package-root.d.ts +17 -0
  30. package/src/lib/mcp-scan/manifest/tokens-schema.d.ts +5 -4
  31. package/src/lib/mcp-scan/scan/extract-models/assemble.d.ts +17 -0
  32. package/src/lib/route/component-resolve.d.ts +48 -0
  33. package/src/lib/route/index.d.ts +18 -0
  34. package/src/lib/route/route-build-tree.d.ts +31 -0
  35. package/src/lib/route/route-extract.d.ts +46 -0
  36. package/src/lib/route/route-load-tree.d.ts +17 -0
  37. package/src/lib/route/route-manifest.d.ts +132 -0
  38. package/src/lib/route/route-model-tag.d.ts +89 -0
  39. package/src/lib/route/route-models-extract.d.ts +22 -0
  40. package/src/lib/route/route-resolve-sources.d.ts +39 -0
  41. package/src/lib/route/route-types.d.ts +136 -0
  42. package/src/lib/route/url-match.d.ts +116 -0
  43. package/src/lib/scan-helpers/firestore-model-extract-utils.d.ts +43 -0
  44. package/test/index.cjs.js +1 -1
  45. package/test/index.esm.js +1 -1
  46. package/test/package.json +9 -9
@@ -150,6 +150,56 @@ export interface CliModelManifestEntry {
150
150
  * `<NAMESPACE>_MODEL_MANIFEST` of this type.
151
151
  */
152
152
  export type CliModelManifest = readonly CliModelManifestEntry[];
153
+ /**
154
+ * One enum value with its persisted literal and the leading JSDoc paragraph.
155
+ *
156
+ * Mirrors the dbx-components MCP `FirebaseEnumValue` shape so the runtime MCP
157
+ * manifest carries the same value→label tables the design-time catalog builds.
158
+ */
159
+ export interface CliModelEnumValue {
160
+ /**
161
+ * Enum member name (e.g. `ACTIVE`).
162
+ */
163
+ readonly name: string;
164
+ /**
165
+ * Raw persisted literal stored in Firestore (e.g. `1`, `'a'`).
166
+ */
167
+ readonly value: number | string;
168
+ /**
169
+ * First paragraph of the member's JSDoc, when present.
170
+ */
171
+ readonly description?: string;
172
+ }
173
+ /**
174
+ * One TypeScript enum referenced by some model field's `enumRef`, captured so
175
+ * the runtime `model-info` / `enum-info` tools can decode raw integer/string
176
+ * values without dropping into source.
177
+ *
178
+ * Mirrors the dbx-components MCP `FirebaseEnum` shape.
179
+ */
180
+ export interface CliModelEnum {
181
+ /**
182
+ * Enum declaration name (e.g. `JobWorkerTimesheetState`).
183
+ */
184
+ readonly name: string;
185
+ /**
186
+ * Value table in source order.
187
+ */
188
+ readonly values: readonly CliModelEnumValue[];
189
+ /**
190
+ * First paragraph of the enum declaration's JSDoc, when present.
191
+ */
192
+ readonly description?: string;
193
+ }
194
+ /**
195
+ * Generated map of {@link CliModelEnum} keyed by enum name, scoped to enums
196
+ * referenced by some emitted model field's `enumRef`. Each downstream CLI app
197
+ * exports its own `<NAMESPACE>_ENUM_MANIFEST` of this type; keying by name lets
198
+ * the runtime do O(1) lookups for both `model-info` and `enum-info`.
199
+ */
200
+ export type CliEnumManifest = {
201
+ readonly [enumName: string]: CliModelEnum;
202
+ };
153
203
  export type CliApiVerb = 'create' | 'read' | 'update' | 'delete' | 'query' | 'invoke' | 'standalone';
154
204
  export interface CliApiManifestField {
155
205
  readonly name: string;
@@ -329,6 +379,8 @@ export interface McpManifestAuth {
329
379
  *
330
380
  * `tools` is keyed by {@link mcpManifestKey} so the runtime can do O(1) lookups per registered tool.
331
381
  * `models` is optional — the runtime skips the catalog-introspection tools when missing.
382
+ * `enums` is optional — keyed by enum name, it carries the value→label tables `model-info` /
383
+ * `enum-info` decode. Scoped to enums referenced by some model field's `enumRef`.
332
384
  * `auth` is optional — drives the runtime `whoami` tool.
333
385
  */
334
386
  export interface McpManifest {
@@ -341,6 +393,7 @@ export interface McpManifest {
341
393
  readonly [key: string]: McpManifestToolEntry | undefined;
342
394
  };
343
395
  readonly models?: readonly McpManifestModelEntry[];
396
+ readonly enums?: CliEnumManifest;
344
397
  readonly auth?: McpManifestAuth;
345
398
  }
346
399
  /**
@@ -0,0 +1,17 @@
1
+ /**
2
+ * Shared package-root resolver for the MCP-scan registry loaders.
3
+ *
4
+ * Every `load-*-registry.ts` resolves the bundled manifest it ships in its
5
+ * package's `generated/` directory by walking up from the module's own URL
6
+ * until a `package.json` appears. Centralizing the walk here keeps each
7
+ * loader free of the duplicated traversal.
8
+ */
9
+ /**
10
+ * Walks up from the directory of `startUrl` until it finds a directory that
11
+ * contains a `package.json`, returning that directory.
12
+ *
13
+ * @param startUrl - An `import.meta.url` from a module inside the package.
14
+ * @returns The absolute path of the nearest ancestor directory holding a `package.json`.
15
+ * @throws {Error} If no `package.json` is found before reaching the filesystem root.
16
+ */
17
+ export declare function findPackageRoot(startUrl: string): string;
@@ -25,9 +25,10 @@ export type TokenRoleValue = (typeof TOKEN_ROLES)[number];
25
25
  /**
26
26
  * Origin of a token — used for filtering (`category=mat-sys`) and to indicate
27
27
  * authoritative source. `dbx-web` aliases live alongside `mat-sys` system
28
- * values; downstream apps surface as `app`.
28
+ * values; `dbx-form` carries the form package's component-scoped tokens;
29
+ * downstream apps surface as `app`.
29
30
  */
30
- export declare const TOKEN_SOURCES: readonly ["dbx-web", "mat-sys", "mdc", "app"];
31
+ export declare const TOKEN_SOURCES: readonly ["dbx-web", "dbx-form", "mat-sys", "mdc", "app"];
31
32
  /**
32
33
  * Static type for the closed token-source vocabulary.
33
34
  */
@@ -56,7 +57,7 @@ export type TokenDefaults = typeof TokenDefaults.infer;
56
57
  */
57
58
  export declare const TokenEntry: import("arktype/internal/variants/object.ts").ObjectType<{
58
59
  cssVariable: string;
59
- source: "app" | "dbx-web" | "mat-sys" | "mdc";
60
+ source: "app" | "dbx-web" | "dbx-form" | "mat-sys" | "mdc";
60
61
  role: "breakpoint" | "size" | "misc" | "spacing" | "color" | "text-color" | "surface" | "radius" | "elevation" | "shadow" | "typography" | "motion" | "state-layer";
61
62
  intents: string[];
62
63
  description: string;
@@ -94,7 +95,7 @@ export declare const TokenManifest: import("arktype/internal/variants/object.ts"
94
95
  generator: string;
95
96
  entries: {
96
97
  cssVariable: string;
97
- source: "app" | "dbx-web" | "mat-sys" | "mdc";
98
+ source: "app" | "dbx-web" | "dbx-form" | "mat-sys" | "mdc";
98
99
  role: "breakpoint" | "size" | "misc" | "spacing" | "color" | "text-color" | "surface" | "radius" | "elevation" | "shadow" | "typography" | "motion" | "state-layer";
99
100
  intents: string[];
100
101
  description: string;
@@ -103,3 +103,20 @@ export interface SubObjectConstEntry {
103
103
  * @returns The assembled models and groups for the file.
104
104
  */
105
105
  export declare function assembleFile(input: AssembleFileInput): AssembledFile;
106
+ interface SubObjectResolution {
107
+ readonly interfaceName: string;
108
+ readonly factoryKind: FirebaseSubObject['factoryKind'];
109
+ }
110
+ /**
111
+ * Resolves a field's (whitespace-collapsed) converter expression to the sub-object interface +
112
+ * factory kind it expands into. Recognises the inline generic form, the wrapped
113
+ * `firestoreObjectArray({ ... objectField|firestoreField: <const> })` form (the `firestoreField`
114
+ * carrier may sit behind sibling props), the inline `firestoreField: firestoreSubObject<T>({...})`
115
+ * form, and a bare sub-object const reference. Exported for focused unit testing.
116
+ *
117
+ * @param converter - The canonical converter expression text.
118
+ * @param index - The cross-file sub-object const index.
119
+ * @returns The resolved interface + factory kind, or `undefined` when the converter references no known sub-object.
120
+ */
121
+ export declare function resolveSubObjectReference(converter: string, index: ReadonlyMap<string, SubObjectConstEntry>): SubObjectResolution | undefined;
122
+ export {};
@@ -0,0 +1,48 @@
1
+ /**
2
+ * Pure component-source resolution: given a router file's text and a rendered
3
+ * component class name, find the source file that declares the component among
4
+ * an already-loaded set of {@link RouteSource} records.
5
+ *
6
+ * Unlike the dev-server tool's fs-backed resolver, this works entirely against
7
+ * the in-memory source set the route-manifest builder already gathered, so it
8
+ * performs no disk I/O. Path arithmetic uses POSIX semantics because source
9
+ * names are workspace-relative, slash-delimited paths.
10
+ */
11
+ import type { RouteSource } from './route-types.js';
12
+ /**
13
+ * Resolved component source location. `path` is the matched source name when
14
+ * the import resolved inside the source set, or the raw module specifier when
15
+ * it could not be resolved (e.g. a node-module / barrel import).
16
+ */
17
+ export interface ComponentSourceResult {
18
+ readonly path: string;
19
+ readonly moduleSpecifier: string;
20
+ }
21
+ /**
22
+ * Input to {@link resolveComponentSourceFromSources}.
23
+ */
24
+ export interface ResolveComponentSourceInput {
25
+ /**
26
+ * Source name of the file that declares the state (and imports the component).
27
+ */
28
+ readonly routerFile: string;
29
+ /**
30
+ * Component class name rendered by the state.
31
+ */
32
+ readonly component: string;
33
+ /**
34
+ * The full in-memory source set the manifest builder gathered.
35
+ */
36
+ readonly sources: readonly RouteSource[];
37
+ }
38
+ /**
39
+ * Resolves the source file that declares `component`, traced from its import in
40
+ * the router file. Returns `undefined` when the router file isn't in the set or
41
+ * the component isn't imported there; returns the raw specifier as `path` when
42
+ * the import is non-relative (node module / path alias) or can't be matched to
43
+ * a loaded source.
44
+ *
45
+ * @param input - The router file name, component class, and source set.
46
+ * @returns The resolved component source location, or `undefined`.
47
+ */
48
+ export declare function resolveComponentSourceFromSources(input: ResolveComponentSourceInput): ComponentSourceResult | undefined;
@@ -0,0 +1,18 @@
1
+ /**
2
+ * UIRouter route-extraction core, shared by the dev MCP server
3
+ * (`@dereekb/dbx-components-mcp`'s `dbx_route_*` tools) and the build-time
4
+ * `dbx-cli-generate-route-manifest` binary.
5
+ *
6
+ * The fs/glob I/O layer lives in each caller — this core operates on in-memory
7
+ * {@link RouteSource} records and produces a normalized {@link RouteTree}, plus
8
+ * a pure URL matcher and component-source resolver.
9
+ */
10
+ export * from './route-types.js';
11
+ export * from './route-extract.js';
12
+ export * from './route-build-tree.js';
13
+ export * from './route-resolve-sources.js';
14
+ export * from './route-load-tree.js';
15
+ export * from './url-match.js';
16
+ export * from './component-resolve.js';
17
+ export * from './route-models-extract.js';
18
+ export * from './route-manifest.js';
@@ -0,0 +1,31 @@
1
+ /**
2
+ * Builds a parent-linked {@link RouteTree} from a flat list of {@link RouteNode}s.
3
+ *
4
+ * Linkage strategy:
5
+ *
6
+ * 1. Use the explicit `parent` field if present.
7
+ * 2. Otherwise derive parent from the dot-prefix of `name`. State `a.b.c`
8
+ * becomes a child of `a.b`. Trailing `.**` is stripped before lookup.
9
+ * 3. Nodes whose parent doesn't resolve are reported as orphan warnings and
10
+ * placed at the tree root.
11
+ *
12
+ * Issues surfaced:
13
+ *
14
+ * - `DUPLICATE_STATE_NAME` — error; second declaration is dropped.
15
+ * - `CYCLE_DETECTED` — error; the cycle is broken by reverting the tail to
16
+ * the root list.
17
+ * - `ORPHAN_STATE` — warning.
18
+ */
19
+ import type { RouteIssue, RouteNode, RouteTree } from './route-types.js';
20
+ /**
21
+ * Builds the parent-child UIRouter tree from a flat node list, computing each
22
+ * state's full URL while preserving any extraction issues so callers see one
23
+ * combined diagnostics view.
24
+ *
25
+ * @param nodes - The flat route nodes extracted from sources.
26
+ * @param extractIssues - Issues already discovered during extraction to forward.
27
+ * @returns The constructed tree alongside the merged issue list.
28
+ *
29
+ * @__NO_SIDE_EFFECTS__
30
+ */
31
+ export declare function buildRouteTree(nodes: readonly RouteNode[], extractIssues: readonly RouteIssue[]): RouteTree;
@@ -0,0 +1,46 @@
1
+ /**
2
+ * AST extraction for the route core.
3
+ *
4
+ * Parses one TypeScript source file with ts-morph and emits a list of
5
+ * {@link RouteNode}s plus any issues surfaced during extraction.
6
+ *
7
+ * Recognized shapes (all syntactic — no type resolution):
8
+ *
9
+ * 1. Top-level `const x: Ng2StateDeclaration = { ... }` — single-state.
10
+ * 2. Top-level array literals of state objects (`const STATES = [a, b]` or
11
+ * `const STATES: Ng2StateDeclaration[] = [...]`) — every element is
12
+ * either an inline object literal or an identifier referencing one of
13
+ * the single-state consts above. Identifier refs are resolved within the
14
+ * same file.
15
+ * 3. `provideStates({ states: [...] })` — element shape matches (2).
16
+ * 4. `UIRouterModule.forChild({ states: [...] })` — same.
17
+ *
18
+ * `@dbxRouteModel*` JSDoc tags on a typed state const's `export const` are
19
+ * captured onto {@link RouteNode.jsDocTags}; the route-manifest builder reads
20
+ * them to augment / override the component-level model annotations.
21
+ *
22
+ * Pure ts-morph in-memory project, no tsconfig, no fs reads. The caller is
23
+ * responsible for funnelling files in.
24
+ */
25
+ import type { RouteIssue, RouteNode, RouteSource } from './route-types.js';
26
+ export interface ExtractFileResult {
27
+ readonly nodes: readonly RouteNode[];
28
+ readonly issues: readonly RouteIssue[];
29
+ /**
30
+ * Identifiers that this file imports — used by `route-resolve-sources.ts` to walk transitively.
31
+ */
32
+ readonly importedFromRelative: readonly RelativeImport[];
33
+ }
34
+ export interface RelativeImport {
35
+ readonly moduleSpecifier: string;
36
+ readonly file: string;
37
+ }
38
+ /**
39
+ * Extracts every UIRouter state declaration from a single source file plus any
40
+ * extraction-time diagnostics. Best-effort: malformed states surface as issues
41
+ * rather than throwing so the rest of the file still contributes nodes.
42
+ *
43
+ * @param source - The in-memory source name + text pair to extract.
44
+ * @returns The discovered route nodes alongside extraction issues.
45
+ */
46
+ export declare function extractFile(source: RouteSource): ExtractFileResult;
@@ -0,0 +1,17 @@
1
+ /**
2
+ * Pure tree-loading entry point. The fs/glob I/O layer lives in the caller —
3
+ * this module receives a list of {@link RouteSource} records and produces a
4
+ * normalized {@link RouteTree}.
5
+ */
6
+ import type { RouteSource, RouteTree } from './route-types.js';
7
+ export interface LoadRouteTreeArgs {
8
+ readonly sources: readonly RouteSource[];
9
+ }
10
+ /**
11
+ * Pure tree-loading entry point. Resolves the supplied source list and builds
12
+ * the parent/child tree in one step so callers can stay thin.
13
+ *
14
+ * @param args - The in-memory sources to process.
15
+ * @returns The constructed route tree with extraction issues attached.
16
+ */
17
+ export declare function loadRouteTree(args: LoadRouteTreeArgs): RouteTree;
@@ -0,0 +1,132 @@
1
+ /**
2
+ * Build-time route manifest: the authoritative schema + builder that the
3
+ * `dbx-cli-generate-route-manifest` binary serializes to `route.manifest.json`.
4
+ *
5
+ * The firebase-server/mcp runtime mirrors this schema in
6
+ * `service/mcp.route-manifest.ts` (it does NOT import this package); both bump
7
+ * {@link ROUTE_MANIFEST_VERSION} together when the shape changes.
8
+ *
9
+ * The builder runs the route extractor over the app sources, drops future-state
10
+ * (`.**`) nodes, resolves each rendered component's `@dbxRouteModel*` tags,
11
+ * merges them with the state const's own tags, pre-flattens ancestor
12
+ * inheritance (recording each inherited entry's `from`), and emits a flat,
13
+ * name-sorted state array. Warnings are collected but never throw — the
14
+ * generator writes the manifest and logs them.
15
+ */
16
+ import { type RouteModelKind } from './route-models-extract.js';
17
+ import type { RouteSource } from './route-types.js';
18
+ /**
19
+ * Version stamp embedded in `route.manifest.json`. Runtime loaders refuse
20
+ * manifests whose `version` does not match. Mirror in firebase-server/mcp's
21
+ * `ROUTE_MANIFEST_VERSION` — bump both together.
22
+ */
23
+ export declare const ROUTE_MANIFEST_VERSION: 1;
24
+ /**
25
+ * One model an app page renders, after tag parsing + inheritance flattening.
26
+ */
27
+ export interface RouteManifestModelEntry {
28
+ readonly modelType: string;
29
+ readonly kind: RouteModelKind;
30
+ /**
31
+ * Verbatim key template (`:uid`, `{authUid}`, `gb/:id/gbe/{authUid}`). Absent
32
+ * for `list` entries.
33
+ */
34
+ readonly keyTemplate?: string;
35
+ readonly description?: string;
36
+ /**
37
+ * When the entry was inherited from an ancestor state, that ancestor's name.
38
+ * Absent for a state's own (component + state-tag) models.
39
+ */
40
+ readonly from?: string;
41
+ }
42
+ /**
43
+ * One UIRouter state, with inheritance pre-flattened into `models`.
44
+ */
45
+ export interface RouteManifestStateEntry {
46
+ readonly name: string;
47
+ readonly url?: string;
48
+ readonly fullUrl?: string;
49
+ readonly parentName?: string;
50
+ readonly paramKeys: readonly string[];
51
+ readonly urlParamKeys: readonly string[];
52
+ readonly component?: string;
53
+ readonly componentFile?: string;
54
+ readonly abstract?: boolean;
55
+ readonly redirectTo?: string;
56
+ readonly models: readonly RouteManifestModelEntry[];
57
+ }
58
+ /**
59
+ * The full `route.manifest.json` shape consumed at runtime by the `url-models`
60
+ * tool. `states` is a flat array (inheritance pre-flattened at build).
61
+ */
62
+ export interface RouteManifest {
63
+ readonly version: typeof ROUTE_MANIFEST_VERSION;
64
+ readonly generatedAt: string;
65
+ readonly app: {
66
+ readonly name: string;
67
+ readonly baseUrl?: string;
68
+ };
69
+ readonly states: readonly RouteManifestStateEntry[];
70
+ }
71
+ /**
72
+ * Kinds of non-fatal finding surfaced while building the manifest.
73
+ */
74
+ export type RouteManifestWarningKind = 'malformed-tag' | 'unknown-route-param' | 'unknown-model-type' | 'duplicate-route-model' | 'dropped-future-state' | 'missing-route-model';
75
+ /**
76
+ * Severity of a {@link RouteManifestWarning}. `error`-severity findings fail
77
+ * manifest generation (and CI via the build dependency); `warning`-severity
78
+ * findings are logged but do not block.
79
+ */
80
+ export type RouteManifestSeverity = 'error' | 'warning';
81
+ export interface RouteManifestWarning {
82
+ readonly kind: RouteManifestWarningKind;
83
+ readonly severity: RouteManifestSeverity;
84
+ readonly message: string;
85
+ readonly stateName?: string;
86
+ readonly modelType?: string;
87
+ /**
88
+ * The id-like route param a `missing-route-model` finding refers to (`:id` →
89
+ * `id`). Absent for other warning kinds.
90
+ */
91
+ readonly param?: string;
92
+ }
93
+ /**
94
+ * App identity stamped onto the manifest.
95
+ */
96
+ export interface BuildRouteManifestApp {
97
+ readonly name: string;
98
+ readonly baseUrl?: string;
99
+ }
100
+ /**
101
+ * Input to {@link buildRouteManifest}.
102
+ */
103
+ export interface BuildRouteManifestInput {
104
+ readonly app: BuildRouteManifestApp;
105
+ readonly sources: readonly RouteSource[];
106
+ /**
107
+ * Known Firestore model types for `unknown-model-type` validation. When
108
+ * omitted (no `--models-input`), the check is skipped.
109
+ */
110
+ readonly modelTypes?: readonly string[];
111
+ }
112
+ /**
113
+ * Output of {@link buildRouteManifest}: the rendered manifest plus the
114
+ * accumulated warnings.
115
+ */
116
+ export interface BuildRouteManifestResult {
117
+ readonly manifest: RouteManifest;
118
+ readonly warnings: readonly RouteManifestWarning[];
119
+ }
120
+ /**
121
+ * Builds the route manifest from a set of app sources.
122
+ *
123
+ * @param input - The app identity, source set, and optional model-type catalog.
124
+ * @param now - Override for the `generatedAt` timestamp (tests pass a fixed value).
125
+ * @returns The rendered manifest and the collected warnings.
126
+ *
127
+ * @example
128
+ * ```ts
129
+ * const { manifest, warnings } = buildRouteManifest({ app: { name: 'demo' }, sources });
130
+ * ```
131
+ */
132
+ export declare function buildRouteManifest(input: BuildRouteManifestInput, now?: Date): BuildRouteManifestResult;
@@ -0,0 +1,89 @@
1
+ /**
2
+ * Pure parser for the `@dbxRouteModel` / `@dbxRouteModelList` JSDoc tag grammar
3
+ * that annotates which Firestore models a route renders.
4
+ *
5
+ * Grammar:
6
+ *
7
+ * ```
8
+ * @dbxRouteModel <modelType> <keyTemplate> [- <description>]
9
+ * @dbxRouteModelList <modelType> [- <description>]
10
+ * ```
11
+ *
12
+ * `keyTemplate` shapes:
13
+ * - `:param` / `{authUid}` — single placeholder → `kind: 'id'` (the value is
14
+ * promoted to `<collectionName>/<id>` at runtime via the model identity).
15
+ * - `gb/:id/gbe/{authUid}` — alternating literal / placeholder segments (even
16
+ * count) → `kind: 'key'` (a full FirestoreModelKey for a subcollection model).
17
+ * - (absent, list tag) → `kind: 'list'`.
18
+ *
19
+ * This module is deliberately runtime-dependency-free (no ts-morph): the same
20
+ * grammar is reused by the build-time manifest builder, the dev MCP route tools,
21
+ * and the `@dereekb/dbx-cli/eslint` rule so they can never disagree about what a
22
+ * valid tag is. The ts-morph consumer (`extractComponentRouteModelTags`) lives in
23
+ * `./route-models-extract.ts` and re-exports these symbols for existing importers.
24
+ */
25
+ /**
26
+ * Whether a route-model entry resolves to a promoted id, a full key, or a
27
+ * keyless list.
28
+ */
29
+ export type RouteModelKind = 'id' | 'key' | 'list';
30
+ /**
31
+ * A successfully parsed `@dbxRouteModel*` tag.
32
+ */
33
+ export interface ParsedRouteModel {
34
+ readonly modelType: string;
35
+ readonly kind: RouteModelKind;
36
+ /**
37
+ * The verbatim key template (`:uid`, `{authUid}`, `gb/:id/gbe/{authUid}`).
38
+ * Absent for `list` entries.
39
+ */
40
+ readonly keyTemplate?: string;
41
+ readonly description?: string;
42
+ /**
43
+ * Route param names (`:name` → `name`) referenced by the key template, for
44
+ * `unknown-route-param` validation against the state's URL params.
45
+ */
46
+ readonly routeParams: readonly string[];
47
+ }
48
+ /**
49
+ * Result of parsing one route-model tag: the parsed model or a malformed-tag
50
+ * message describing why it could not be parsed.
51
+ */
52
+ export type ParseRouteModelTagResult = {
53
+ readonly ok: true;
54
+ readonly model: ParsedRouteModel;
55
+ } | {
56
+ readonly ok: false;
57
+ readonly message: string;
58
+ };
59
+ /**
60
+ * One raw `@dbxRouteModel*` tag — the tag name (without `@`) plus its trimmed
61
+ * comment text.
62
+ */
63
+ export interface RawRouteModelTag {
64
+ readonly name: string;
65
+ readonly text: string;
66
+ }
67
+ /**
68
+ * The bare `@dbxRouteModel` tag name (without the leading `@`).
69
+ */
70
+ export declare const ROUTE_MODEL_TAG = "dbxRouteModel";
71
+ /**
72
+ * The `@dbxRouteModelList` tag name (without the leading `@`).
73
+ */
74
+ export declare const ROUTE_MODEL_LIST_TAG = "dbxRouteModelList";
75
+ /**
76
+ * Parses one `@dbxRouteModel` / `@dbxRouteModelList` tag into a structured
77
+ * model, or returns a malformed-tag message. The description (everything after
78
+ * the first ` - `) is split off first; the remaining head is tokenized.
79
+ *
80
+ * @param tag - The raw tag name + comment text.
81
+ * @returns The parsed model on success, else a malformed-tag message.
82
+ *
83
+ * @example
84
+ * ```ts
85
+ * parseRouteModelTag({ name: 'dbxRouteModel', text: 'profile :uid - The profile' });
86
+ * // => { ok: true, model: { modelType: 'profile', kind: 'id', keyTemplate: ':uid', description: 'The profile', routeParams: ['uid'] } }
87
+ * ```
88
+ */
89
+ export declare function parseRouteModelTag(tag: RawRouteModelTag): ParseRouteModelTagResult;
@@ -0,0 +1,22 @@
1
+ /**
2
+ * ts-morph consumer of the `@dbxRouteModel` / `@dbxRouteModelList` grammar.
3
+ *
4
+ * The pure grammar parser lives in `./route-model-tag.ts` (zero ts-morph) so it
5
+ * can be reused by the `@dereekb/dbx-cli/eslint` rule without dragging ts-morph
6
+ * into a bundled ESLint plugin. This module reads the tags off a ts-morph
7
+ * `SourceFile` and re-exports the grammar symbols so existing importers
8
+ * (`route-manifest.ts`, the route barrel) keep working unchanged.
9
+ */
10
+ import type { SourceFile } from 'ts-morph';
11
+ import { type RawRouteModelTag } from './route-model-tag.js';
12
+ export { parseRouteModelTag, ROUTE_MODEL_TAG, ROUTE_MODEL_LIST_TAG, type RawRouteModelTag, type ParseRouteModelTagResult, type ParsedRouteModel, type RouteModelKind } from './route-model-tag.js';
13
+ /**
14
+ * Extracts every `@dbxRouteModel*` tag declared on the named component class in
15
+ * a source file. Returns an empty list when the class is absent or carries no
16
+ * route-model tags.
17
+ *
18
+ * @param sourceFile - The ts-morph source file declaring the component.
19
+ * @param component - The component class name to read tags from.
20
+ * @returns The raw route-model tags found on the class.
21
+ */
22
+ export declare function extractComponentRouteModelTags(sourceFile: SourceFile, component: string): readonly RawRouteModelTag[];
@@ -0,0 +1,39 @@
1
+ /**
2
+ * Helpers for transitive resolution across multiple route sources.
3
+ *
4
+ * Two flavors:
5
+ *
6
+ * 1. {@link resolveRouteSources} — walks every supplied source and emits a
7
+ * flat node list. Used when the caller already gathered a glob/path set.
8
+ * 2. {@link computeRelativeSpecifiers} — given a starting source, returns
9
+ * the relative import specifiers it depends on. The off-disk loader uses
10
+ * this to walk the file tree.
11
+ *
12
+ * The pure core never touches the file system — the caller does the
13
+ * `readFile`-and-recurse loop, calling back into {@link extractFile} for each
14
+ * file it loads.
15
+ */
16
+ import type { RouteIssue, RouteNode, RouteSource } from './route-types.js';
17
+ export interface ResolvedSources {
18
+ readonly nodes: readonly RouteNode[];
19
+ readonly issues: readonly RouteIssue[];
20
+ readonly filesChecked: number;
21
+ }
22
+ /**
23
+ * Walks every supplied source through the extractor and emits a flat node and
24
+ * issue list, used when the caller has already gathered a complete glob/file
25
+ * set in memory.
26
+ *
27
+ * @param sources - The in-memory sources to extract from.
28
+ * @returns The merged extraction nodes, issues, and processed file count.
29
+ */
30
+ export declare function resolveRouteSources(sources: readonly RouteSource[]): ResolvedSources;
31
+ /**
32
+ * Returns the relative module specifiers imported by `source` — used to plan
33
+ * the next round of file reads in transitive walking. Specifiers are
34
+ * left untouched (no `.ts` resolution); the caller normalizes them.
35
+ *
36
+ * @param source - The in-memory source to inspect.
37
+ * @returns The relative specifiers in original-source order.
38
+ */
39
+ export declare function computeRelativeSpecifiers(source: RouteSource): readonly string[];