@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.
- package/eslint/index.cjs.default.js +1 -0
- package/eslint/index.cjs.js +1050 -0
- package/eslint/index.cjs.mjs +2 -0
- package/eslint/index.d.ts +1 -0
- package/eslint/index.esm.js +1046 -0
- package/eslint/package.json +25 -0
- package/eslint/rollup.alias-internal.config.d.ts +11 -0
- package/eslint/src/index.d.ts +1 -0
- package/eslint/src/lib/index.d.ts +2 -0
- package/eslint/src/lib/plugin.d.ts +22 -0
- package/eslint/src/lib/valid-dbx-route-model-tags.rule.d.ts +59 -0
- package/firebase-api-manifest/main.js +318 -228
- package/firebase-api-manifest/package.json +3 -3
- package/generate-firestore-indexes/main.js +37 -24
- package/generate-firestore-indexes/package.json +2 -2
- package/generate-mcp-manifest/main.js +57 -39
- package/generate-mcp-manifest/package.json +3 -3
- package/generate-route-manifest/main.js +1137 -0
- package/generate-route-manifest/package.json +10 -0
- package/index.cjs.js +4847 -1953
- package/index.esm.js +4827 -1954
- package/lint-cache/package.json +2 -2
- package/manifest-extract/index.cjs.js +175 -240
- package/manifest-extract/index.esm.js +174 -239
- package/manifest-extract/package.json +9 -4
- package/package.json +16 -6
- package/src/lib/index.d.ts +2 -0
- package/src/lib/manifest/types.d.ts +53 -0
- package/src/lib/mcp-scan/manifest/package-root.d.ts +17 -0
- package/src/lib/mcp-scan/manifest/tokens-schema.d.ts +5 -4
- package/src/lib/mcp-scan/scan/extract-models/assemble.d.ts +17 -0
- package/src/lib/route/component-resolve.d.ts +48 -0
- package/src/lib/route/index.d.ts +18 -0
- package/src/lib/route/route-build-tree.d.ts +31 -0
- package/src/lib/route/route-extract.d.ts +46 -0
- package/src/lib/route/route-load-tree.d.ts +17 -0
- package/src/lib/route/route-manifest.d.ts +132 -0
- package/src/lib/route/route-model-tag.d.ts +89 -0
- package/src/lib/route/route-models-extract.d.ts +22 -0
- package/src/lib/route/route-resolve-sources.d.ts +39 -0
- package/src/lib/route/route-types.d.ts +136 -0
- package/src/lib/route/url-match.d.ts +116 -0
- package/src/lib/scan-helpers/firestore-model-extract-utils.d.ts +43 -0
- package/test/index.cjs.js +1 -1
- package/test/index.esm.js +1 -1
- 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;
|
|
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[];
|