@dereekb/dbx-cli 13.41.0 → 13.43.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 (38) hide show
  1. package/eslint/package.json +3 -3
  2. package/firebase-api-manifest/main.js +177 -21
  3. package/firebase-api-manifest/package.json +3 -3
  4. package/firestore-indexes/src/generate-firestore-indexes-cli.d.ts +12 -4
  5. package/firestore-indexes/src/model-firebase-index-analyze.d.ts +12 -2
  6. package/firestore-query-manifest/main.js +26 -14
  7. package/firestore-query-manifest/package.json +3 -3
  8. package/generate-firestore-indexes/main.js +118 -20
  9. package/generate-firestore-indexes/package.json +2 -2
  10. package/generate-mcp-manifest/main.js +5 -2
  11. package/generate-mcp-manifest/package.json +3 -3
  12. package/generate-route-manifest/main.js +1 -1
  13. package/generate-route-manifest/package.json +2 -2
  14. package/index.esm.js +2651 -1387
  15. package/lint-cache/package.json +2 -2
  16. package/manifest-extract/index.esm.js +148 -2
  17. package/manifest-extract/package.json +2 -2
  18. package/manifest-extract/src/lib/extract-models.d.ts +4 -2
  19. package/manifest-extract/src/lib/types.d.ts +65 -0
  20. package/model-test/index.esm.js +4 -54
  21. package/model-test/package.json +2 -2
  22. package/package.json +6 -6
  23. package/route/package.json +7 -7
  24. package/src/lib/doctor/build-drift.check.d.ts +21 -0
  25. package/src/lib/doctor/doctor.command.factory.d.ts +21 -2
  26. package/src/lib/doctor/firestore-session.check.d.ts +7 -1
  27. package/src/lib/doctor/index.d.ts +1 -0
  28. package/src/lib/firestore/firestore.sdk-identity.d.ts +193 -0
  29. package/src/lib/firestore/index.d.ts +1 -0
  30. package/src/lib/manifest/types.d.ts +73 -2
  31. package/src/lib/mcp-scan/manifest/css-utilities-schema.d.ts +2 -2
  32. package/src/lib/mcp-scan/manifest/tokens-schema.d.ts +2 -2
  33. package/src/lib/runner/build-stamp.d.ts +138 -0
  34. package/src/lib/runner/index.d.ts +1 -0
  35. package/src/lib/runner/run.d.ts +13 -0
  36. package/test/package.json +9 -9
  37. package/validate/index.js +17 -7
  38. package/validate/package.json +3 -3
@@ -0,0 +1,193 @@
1
+ import { type FirestoreModelKey, type FirestoreModelType } from '@dereekb/firebase';
2
+ import { type Maybe } from '@dereekb/util';
3
+ /**
4
+ * The `drivers.firestoreDriverIdentifier` a client-SDK `FirestoreContext` reports.
5
+ *
6
+ * `clientFirebaseFirestoreContextFactory` stamps this; the server's
7
+ * `googleCloudFirestoreContextFactory` stamps `@google-cloud/firestore` instead. Comparing against
8
+ * it is how {@link inspectCliFirestoreSdkIdentity} tells an admin-SDK context handed to the CLI apart
9
+ * from a genuinely broken client one.
10
+ */
11
+ export declare const CLI_CLIENT_FIRESTORE_DRIVER_IDENTIFIER = "@firebase/firestore";
12
+ /**
13
+ * The doctor `detail.stage` reported when the session's Firestore handle is not a usable client-SDK
14
+ * `Firestore`.
15
+ *
16
+ * A distinct stage from `rules-protected-read` on purpose: the read never ran, so reporting it under
17
+ * the read's stage is what made this failure look like a rules/App Check problem.
18
+ */
19
+ export declare const FIRESTORE_SDK_IDENTITY_STAGE = "firestore-sdk-identity";
20
+ /**
21
+ * `CliError` code for a Firestore handle the client SDK does not recognize as its own.
22
+ */
23
+ export declare const FIRESTORE_SDK_INSTANCE_MISMATCH_CODE = "FIRESTORE_SDK_INSTANCE_MISMATCH";
24
+ /**
25
+ * The distinct ways a session's Firestore handle can be unusable, in the order
26
+ * {@link inspectCliFirestoreSdkIdentity} tests them — most specific diagnosis first.
27
+ */
28
+ export type CliFirestoreSdkIdentityProblem =
29
+ /**
30
+ * `firestoreContext` is absent, or its `firestore` is null/undefined. Every `collection()` call
31
+ * built off it throws `Expected first argument to collection() to be a CollectionReference, a
32
+ * DocumentReference or FirebaseFirestore` — the SDK's message for ANY non-Firestore first
33
+ * argument, which is why it never named this.
34
+ */
35
+ 'no-firestore-handle'
36
+ /**
37
+ * The context reports a driver other than {@link CLI_CLIENT_FIRESTORE_DRIVER_IDENTIFIER} — an
38
+ * admin/`@google-cloud/firestore` context reached the client-SDK read path.
39
+ */
40
+ | 'unexpected-driver'
41
+ /**
42
+ * Two copies of `@firebase/firestore` are loaded, so the handle minted by one fails the other's
43
+ * brand check.
44
+ */
45
+ | 'duplicated-firestore-sdk'
46
+ /**
47
+ * The handle is present and the driver looks right, but it is not an instance of THIS copy of the
48
+ * SDK's `Firestore` class and no duplicate install explains it.
49
+ */
50
+ | 'foreign-firestore-instance';
51
+ /**
52
+ * Where one consumer resolved `@firebase/firestore` to, and at which version.
53
+ *
54
+ * Reported for both `@dereekb/dbx-cli` and `@dereekb/firebase` because the whole point of the
55
+ * duplicated-SDK hypothesis is that those two answers can differ.
56
+ */
57
+ export interface CliFirestoreSdkModuleIdentity {
58
+ /**
59
+ * The resolved package directory, or `undefined` when resolution failed.
60
+ */
61
+ readonly packageDir?: Maybe<string>;
62
+ readonly version?: Maybe<string>;
63
+ /**
64
+ * Why resolution failed, when it did.
65
+ */
66
+ readonly error?: Maybe<string>;
67
+ }
68
+ /**
69
+ * The provenance + brand-check report {@link inspectCliFirestoreSdkIdentity} produces.
70
+ *
71
+ * Everything here is reported whether or not the check passed: a `sdkDuplicated: true` alongside
72
+ * `ok: true` is a latent hazard worth seeing before it becomes an outage.
73
+ */
74
+ export interface CliFirestoreSdkIdentityReport {
75
+ readonly ok: boolean;
76
+ readonly problem?: CliFirestoreSdkIdentityProblem;
77
+ readonly firestorePresent: boolean;
78
+ /**
79
+ * Whether the handle passes `instanceof Firestore` against the copy of `@firebase/firestore` THIS
80
+ * package loaded.
81
+ */
82
+ readonly firestoreIsSdkInstance: boolean;
83
+ /**
84
+ * The handle's constructor name, which distinguishes a duplicate `Firestore` (same name, different
85
+ * class) from a genuinely foreign object.
86
+ */
87
+ readonly firestoreConstructor?: Maybe<string>;
88
+ readonly firestoreDriverIdentifier?: Maybe<string>;
89
+ /**
90
+ * The version of the `firebase` umbrella package resolved at runtime.
91
+ */
92
+ readonly firebaseVersion?: Maybe<string>;
93
+ readonly sdkFromDbxCli: CliFirestoreSdkModuleIdentity;
94
+ readonly sdkFromDbxFirebase: CliFirestoreSdkModuleIdentity;
95
+ /**
96
+ * True when the two resolutions above name DIFFERENT package directories.
97
+ */
98
+ readonly sdkDuplicated: boolean;
99
+ }
100
+ /**
101
+ * The slice of a session context {@link inspectCliFirestoreSdkIdentity} reads.
102
+ *
103
+ * Deliberately looser than `FirestoreContext`: this check exists precisely for the case where the
104
+ * object is not the shape its declared type claims, so narrowing it here would assume away the
105
+ * failure. A real `CliFirestoreSessionContext['firestoreContext']` is structurally assignable.
106
+ */
107
+ export interface CliFirestoreSdkIdentityContext {
108
+ readonly firestore?: unknown;
109
+ readonly drivers?: {
110
+ readonly firestoreDriverIdentifier?: unknown;
111
+ };
112
+ }
113
+ /**
114
+ * Input for {@link inspectCliFirestoreSdkIdentity}.
115
+ */
116
+ export interface InspectCliFirestoreSdkIdentityInput {
117
+ readonly firestoreContext?: Maybe<CliFirestoreSdkIdentityContext>;
118
+ }
119
+ /**
120
+ * Checks that a session's Firestore handle is one THIS copy of the client SDK will accept, and
121
+ * reports where every consumer resolved the SDK from.
122
+ *
123
+ * Exists because `collection()` refuses any non-`Firestore` first argument with one message —
124
+ * `Expected first argument to collection() to be a CollectionReference, a DocumentReference or
125
+ * FirebaseFirestore` — that names neither the model, nor the collection, nor which of the three
126
+ * distinct causes ({@link CliFirestoreSdkIdentityProblem}) produced it. Every downstream CLI built on
127
+ * `@dereekb/dbx-cli` shares that hazard, so the diagnosis belongs here rather than in an app.
128
+ *
129
+ * The `instanceof` test is the load-bearing one and it is deliberately NOT structural: an identity
130
+ * check across the package boundary is the only thing that can detect a duplicated
131
+ * `@firebase/firestore`, which is exactly what a structural check would hide. (Contrast
132
+ * `firestore.error.ts`, which narrows a thrown `FirebaseError` structurally — there the goal is to
133
+ * classify an error even when copies differ, so the tradeoff runs the other way.)
134
+ *
135
+ * @param input - The session's `firestoreContext`, when one was built.
136
+ * @returns The provenance + brand-check report.
137
+ */
138
+ export declare function inspectCliFirestoreSdkIdentity(input: InspectCliFirestoreSdkIdentityInput): CliFirestoreSdkIdentityReport;
139
+ /**
140
+ * The actionable next step for a failed {@link inspectCliFirestoreSdkIdentity}, keyed on which
141
+ * problem was found.
142
+ *
143
+ * Each branch names the fix rather than the symptom — a raw SDK sentence sends an operator looking at
144
+ * security rules and App Check, neither of which is ever the cause here.
145
+ *
146
+ * @param report - The report to describe.
147
+ * @returns The suggestion, or `undefined` when the report passed.
148
+ *
149
+ * @__NO_SIDE_EFFECTS__
150
+ */
151
+ export declare function cliFirestoreSdkIdentitySuggestion(report: CliFirestoreSdkIdentityReport): Maybe<string>;
152
+ /**
153
+ * `CliError` code for a collection/document reference the SDK refused to build for a reason the
154
+ * wiring identity check cannot explain — a bad `--parent`, or an app collection whose own
155
+ * construction is broken.
156
+ */
157
+ export declare const FIRESTORE_COLLECTION_UNRESOLVABLE_CODE = "FIRESTORE_COLLECTION_UNRESOLVABLE";
158
+ /**
159
+ * Input for {@link cliFirestoreWiringError}.
160
+ */
161
+ export interface CliFirestoreWiringErrorInput {
162
+ /**
163
+ * The value the SDK threw.
164
+ */
165
+ readonly error: unknown;
166
+ /**
167
+ * What the CLI was doing, phrased to complete `Could not <operation>` — e.g.
168
+ * `build the app's Firestore collections`.
169
+ */
170
+ readonly operation: string;
171
+ readonly modelType?: FirestoreModelType;
172
+ readonly collectionName?: string;
173
+ /**
174
+ * The `--parent` in play, when one was supplied. Reported either way: "no `--parent`" rules out the
175
+ * subcollection route, which is otherwise the first thing an operator has to go and check.
176
+ */
177
+ readonly parentKey?: FirestoreModelKey;
178
+ readonly firestoreContext?: Maybe<CliFirestoreSdkIdentityContext>;
179
+ }
180
+ /**
181
+ * Re-raises a reference-construction failure as a `CliError` that names WHAT was being built and, when
182
+ * the SDK handle is at fault, WHY.
183
+ *
184
+ * The failure this replaces is the least readable in the direct-Firestore path: `collection()` and
185
+ * `doc()` reject a bad first argument with a message that names neither the model, the collection, the
186
+ * `--parent`, nor the handle — and every one of those is known at the call site. A `CliError` already
187
+ * raised upstream (an unknown model type, a malformed `--parent`) is passed through untouched, so this
188
+ * can be applied unconditionally in a `catch`.
189
+ *
190
+ * @param input - The thrown value plus everything the call site knows about the reference it was building.
191
+ * @returns The `CliError` to throw, or the original `CliError` when the failure was already named.
192
+ */
193
+ export declare function cliFirestoreWiringError(input: CliFirestoreWiringErrorInput): Error;
@@ -8,6 +8,7 @@ export * from './firestore.models';
8
8
  export * from './firestore.query';
9
9
  export * from './firestore.query-params';
10
10
  export * from './firestore.read';
11
+ export * from './firestore.sdk-identity';
11
12
  export * from './firestore.session';
12
13
  export * from './query-info-utils';
13
14
  export * from './query-mode';
@@ -46,8 +46,9 @@ export interface CliModelField {
46
46
  readonly description?: string;
47
47
  /**
48
48
  * Enum name referenced by either the interface property's TS type or the
49
- * converter's `firestoreEnum<Enum>()` generic argument, when an enum is
50
- * present in the same source file.
49
+ * converter's `firestoreEnum<Enum>()` generic argument, resolved against
50
+ * every enum in the scan — the declaring file does not have to be the
51
+ * converter's own.
51
52
  */
52
53
  readonly enumRef?: string;
53
54
  /**
@@ -107,6 +108,38 @@ export interface CliModelManifestEntry {
107
108
  * Parent identity const name when the model is a subcollection.
108
109
  */
109
110
  readonly parentIdentityConst?: string;
111
+ /**
112
+ * `true` when the model's collection holds exactly one document — built by
113
+ * `singleItemFirestoreCollection` (one per parent) or
114
+ * `rootSingleItemFirestoreCollection` (one, full stop). Absent for ordinary
115
+ * multi-document collections.
116
+ */
117
+ readonly singleton?: boolean;
118
+ /**
119
+ * Fixed document id of a {@link singleton} model's one document: the value passed as
120
+ * `singleItemIdentifier`, or `'0'`
121
+ * (`DEFAULT_SINGLE_ITEM_FIRESTORE_COLLECTION_DOCUMENT_IDENTIFIER`) when the collection
122
+ * factory omits it.
123
+ *
124
+ * This cannot be inferred from the rest of the entry — plenty of single-item collections
125
+ * override the default (`bgbs`, `bgsu`, …) — so a caller building a document key must read
126
+ * it here rather than assume `/0`. Absent when {@link singleton} is `true` but the id was
127
+ * declared as an expression the generator could not resolve (a build-time warning is
128
+ * printed for that case).
129
+ */
130
+ readonly singleItemIdentifier?: string;
131
+ /**
132
+ * A ready-to-use example document key for this model, with the parent chain resolved from
133
+ * the identity graph and `<…Id>` placeholders standing in for the ids a caller supplies
134
+ * (e.g. `p/<profileId>`, `bg/<billingGroupId>/bgi/<billingGroupInvoiceId>`). A
135
+ * {@link singleton} model's own segment is its literal {@link singleItemIdentifier}, so the
136
+ * key is exact past the last placeholder (e.g. `bg/<billingGroupId>/bgis/bgbs`).
137
+ *
138
+ * Key *shape* — parent chain plus fixed document id — is what consumers of the manifest
139
+ * actually get wrong, so it is published pre-assembled. Absent only when some ancestor
140
+ * identity could not be resolved.
141
+ */
142
+ readonly exampleKey?: string;
110
143
  /**
111
144
  * First paragraph of the source interface's JSDoc, when present.
112
145
  */
@@ -327,6 +360,23 @@ export interface McpManifestModelEntry {
327
360
  readonly identityConst: string;
328
361
  readonly collectionPrefix: string;
329
362
  readonly parentIdentityConst?: string;
363
+ /**
364
+ * `true` when the model's collection holds exactly one document, at the fixed
365
+ * {@link singleItemIdentifier} (mirror of {@link CliModelManifestEntry.singleton}).
366
+ */
367
+ readonly singleton?: boolean;
368
+ /**
369
+ * Fixed document id of a {@link singleton} model's one document — `'0'` by default but
370
+ * frequently overridden, so it cannot be inferred (mirror of
371
+ * {@link CliModelManifestEntry.singleItemIdentifier}).
372
+ */
373
+ readonly singleItemIdentifier?: string;
374
+ /**
375
+ * Example document key with the parent chain resolved and `<…Id>` placeholders for the ids a
376
+ * caller supplies (mirror of {@link CliModelManifestEntry.exampleKey}). Surfaced by
377
+ * `model-info` so a `model-get` caller can build a valid key without reading source.
378
+ */
379
+ readonly exampleKey?: string;
330
380
  readonly description?: string;
331
381
  readonly sourcePackage: string;
332
382
  readonly sourceFile: string;
@@ -738,3 +788,24 @@ export interface CliFirestoreQueryManifestEntry {
738
788
  * The generated per-model Firestore query catalog.
739
789
  */
740
790
  export type CliFirestoreQueryManifest = readonly CliFirestoreQueryManifestEntry[];
791
+ /**
792
+ * The generator provenance a generated manifest module carries alongside its catalog.
793
+ *
794
+ * Emitted as `<NAMESPACE>_STAMP` by the manifest generators and wired into `runCli` as
795
+ * `manifestGeneratorVersion`. Exists because a generated manifest is the one build output that can go
796
+ * stale WITHOUT breaking the build: it is committed TypeScript, so a manifest emitted by an older
797
+ * generator still compiles and still type-checks against a newer runtime — it just quietly lacks the
798
+ * fields that runtime reads. `queryMode` / `rules` were exactly that, and the resulting
799
+ * `queryModesScanned: false` sat in `doctor` output unread while the direct-Firestore path was being
800
+ * debugged from the wrong end.
801
+ *
802
+ * Version only, deliberately no timestamp: the generators skip a write when the bytes are unchanged
803
+ * so incremental builds preserve mtimes, and a timestamp would rewrite the file — and its git
804
+ * history — on every build.
805
+ */
806
+ export interface CliGeneratedManifestStamp {
807
+ /**
808
+ * The `@dereekb/dbx-cli` version of the generator that emitted the module.
809
+ */
810
+ readonly generatorVersion: string;
811
+ }
@@ -106,7 +106,7 @@ export declare const CssUtilityEntry: import("arktype/internal/variants/object.t
106
106
  property: string;
107
107
  value: string;
108
108
  }[];
109
- role?: "text" | "state" | "size" | "layout" | "misc" | "flex" | "spacing" | "interaction" | "color" | "text-color" | "surface" | "radius" | undefined;
109
+ role?: "color" | "state" | "text" | "size" | "layout" | "misc" | "flex" | "spacing" | "interaction" | "text-color" | "surface" | "radius" | undefined;
110
110
  intent?: string | undefined;
111
111
  seeAlso?: string[] | undefined;
112
112
  antiUse?: string | undefined;
@@ -149,7 +149,7 @@ export declare const CssUtilityManifest: import("arktype/internal/variants/objec
149
149
  property: string;
150
150
  value: string;
151
151
  }[];
152
- role?: "text" | "state" | "size" | "layout" | "misc" | "flex" | "spacing" | "interaction" | "color" | "text-color" | "surface" | "radius" | undefined;
152
+ role?: "color" | "state" | "text" | "size" | "layout" | "misc" | "flex" | "spacing" | "interaction" | "text-color" | "surface" | "radius" | undefined;
153
153
  intent?: string | undefined;
154
154
  seeAlso?: string[] | undefined;
155
155
  antiUse?: string | undefined;
@@ -58,7 +58,7 @@ export type TokenDefaults = typeof TokenDefaults.infer;
58
58
  export declare const TokenEntry: import("arktype/internal/variants/object.ts").ObjectType<{
59
59
  cssVariable: string;
60
60
  source: "app" | "dbx-web" | "dbx-form" | "mat-sys" | "mdc";
61
- role: "breakpoint" | "size" | "misc" | "spacing" | "color" | "text-color" | "surface" | "radius" | "elevation" | "shadow" | "typography" | "motion" | "state-layer";
61
+ role: "color" | "breakpoint" | "size" | "misc" | "spacing" | "text-color" | "surface" | "radius" | "elevation" | "shadow" | "typography" | "motion" | "state-layer";
62
62
  intents: string[];
63
63
  description: string;
64
64
  defaults: {
@@ -96,7 +96,7 @@ export declare const TokenManifest: import("arktype/internal/variants/object.ts"
96
96
  entries: {
97
97
  cssVariable: string;
98
98
  source: "app" | "dbx-web" | "dbx-form" | "mat-sys" | "mdc";
99
- role: "breakpoint" | "size" | "misc" | "spacing" | "color" | "text-color" | "surface" | "radius" | "elevation" | "shadow" | "typography" | "motion" | "state-layer";
99
+ role: "color" | "breakpoint" | "size" | "misc" | "spacing" | "text-color" | "surface" | "radius" | "elevation" | "shadow" | "typography" | "motion" | "state-layer";
100
100
  intents: string[];
101
101
  description: string;
102
102
  defaults: {
@@ -0,0 +1,138 @@
1
+ import { type Maybe } from '@dereekb/util';
2
+ /**
3
+ * The `@dereekb/*` package prefix the drift check compares. Only framework packages are checked —
4
+ * a third-party range like `firebase: ^12.12.1` is deliberately out of scope.
5
+ */
6
+ export declare const CLI_FRAMEWORK_PACKAGE_PREFIX = "@dereekb/";
7
+ /**
8
+ * The artifact `package.json` a built CLI ships beside its entry file.
9
+ *
10
+ * `@nx/esbuild` copies the project's `package.json` verbatim into the output directory, so its
11
+ * exact-pinned `@dereekb/*` peer versions ARE the build stamp — the dependency tree the bundle was
12
+ * compiled against — with no extra build-time codegen to keep in sync.
13
+ */
14
+ export interface CliArtifactPackage {
15
+ /**
16
+ * Absolute path the stamp was read from, reported so an operator can see WHICH artifact is running.
17
+ */
18
+ readonly path: string;
19
+ readonly name?: Maybe<string>;
20
+ readonly version?: Maybe<string>;
21
+ /**
22
+ * Every exact-pinned `@dereekb/*` version the artifact declares, from `peerDependencies` and
23
+ * `dependencies` combined.
24
+ */
25
+ readonly frameworkVersions: Readonly<Record<string, string>>;
26
+ }
27
+ /**
28
+ * One framework package whose built-against version differs from the version resolved at run time.
29
+ */
30
+ export interface CliFrameworkVersionDrift {
31
+ readonly packageName: string;
32
+ readonly builtAgainst: string;
33
+ /**
34
+ * The version resolved from `node_modules` now, or `undefined` when the package is not installed.
35
+ */
36
+ readonly resolved?: Maybe<string>;
37
+ }
38
+ /**
39
+ * Why {@link inspectCliBuildDrift} could not compare anything. A report with a `reason` is `ok`: an
40
+ * undetectable stamp is not evidence of drift.
41
+ */
42
+ export type CliBuildDriftUndeterminedReason = 'no-entry-path' | 'no-artifact-package' | 'no-framework-versions-declared'
43
+ /**
44
+ * The artifact declares framework versions but NOT ONE of them resolves from `node_modules` — the
45
+ * signature of running from SOURCE through a workspace's tsconfig paths (a monorepo dev run), where
46
+ * there is no installed framework to drift from. Reporting every package as "not installed" here
47
+ * would make the check red on every dev run, and a check that is red by default gets ignored.
48
+ */
49
+ | 'framework-not-resolvable';
50
+ /**
51
+ * The verdict on whether the running artifact matches the dependency tree it was built against.
52
+ */
53
+ export interface CliBuildDriftReport {
54
+ readonly ok: boolean;
55
+ readonly reason?: Maybe<CliBuildDriftUndeterminedReason>;
56
+ readonly artifactPackagePath?: Maybe<string>;
57
+ readonly artifactVersion?: Maybe<string>;
58
+ /**
59
+ * How many framework packages were actually comparable.
60
+ */
61
+ readonly checked: number;
62
+ readonly drift: readonly CliFrameworkVersionDrift[];
63
+ /**
64
+ * The `@dereekb/dbx-cli` version that emitted the CLI's generated manifests, when the app passed
65
+ * one through.
66
+ */
67
+ readonly manifestGeneratorVersion?: Maybe<string>;
68
+ /**
69
+ * The `@dereekb/dbx-cli` version resolved at run time.
70
+ */
71
+ readonly resolvedDbxCliVersion?: Maybe<string>;
72
+ /**
73
+ * True when the manifests were generated by a DIFFERENT `@dereekb/dbx-cli` than the one now
74
+ * running them. This is the half the peer-version comparison cannot see: bumping the app's declared
75
+ * peers without re-running the generators leaves the committed manifests behind, which is how a
76
+ * manifest silently lost fields the runtime reads (`queryModesScanned: false` was this, unread).
77
+ */
78
+ readonly manifestGeneratorDrift: boolean;
79
+ }
80
+ /**
81
+ * Input for {@link inspectCliBuildDrift}.
82
+ */
83
+ export interface InspectCliBuildDriftInput {
84
+ /**
85
+ * The executed entry file. Defaults to `process.argv[1]`.
86
+ */
87
+ readonly entryPath?: Maybe<string>;
88
+ /**
89
+ * The generator version stamped into the app's generated manifests
90
+ * (`<NAMESPACE>_STAMP.generatorVersion`), when the app wires one.
91
+ */
92
+ readonly manifestGeneratorVersion?: Maybe<string>;
93
+ /**
94
+ * Test seam: use this artifact stamp instead of discovering one from {@link entryPath}.
95
+ */
96
+ readonly artifactPackage?: Maybe<CliArtifactPackage>;
97
+ /**
98
+ * Test seam: resolves a package's installed version. Defaults to resolving each package's
99
+ * `package.json` through `createRequire`.
100
+ */
101
+ readonly resolveVersion?: (packageName: string) => Maybe<string>;
102
+ }
103
+ /**
104
+ * Compares the `@dereekb/*` versions the running artifact was BUILT against with the versions
105
+ * resolved from `node_modules` now.
106
+ *
107
+ * This is the check whose absence turns a routine `@dereekb/*` bump into an unreadable runtime
108
+ * failure. A CLI built with `thirdParty: false` inlines its app code and resolves the framework at
109
+ * RUN time, so the artifact is only valid against the tree it was compiled against — and nothing
110
+ * else in the toolchain notices when that stops being true. The symptom is arbitrary: an SDK
111
+ * assertion, a missing manifest field, a silently-empty result.
112
+ *
113
+ * Deliberately conservative. Only exact-pinned `@dereekb/*` versions are compared, and every way the
114
+ * comparison can fail to apply produces `ok: true` with a {@link CliBuildDriftUndeterminedReason} —
115
+ * a check that cried wolf on every source-run CLI would be turned off, and then it would be useless
116
+ * on the day it mattered.
117
+ *
118
+ * @param input - The entry path, the manifest generator stamp, and the test seams.
119
+ * @returns The drift verdict.
120
+ */
121
+ export declare function inspectCliBuildDrift(input?: InspectCliBuildDriftInput): CliBuildDriftReport;
122
+ /**
123
+ * Renders a {@link CliBuildDriftReport} as one operator-facing line, for `--version` and for a
124
+ * doctor suggestion.
125
+ *
126
+ * @param report - The report to describe.
127
+ * @returns The description, or `undefined` when there is nothing to say.
128
+ *
129
+ * @__NO_SIDE_EFFECTS__
130
+ */
131
+ export declare function cliBuildDriftDescription(report: CliBuildDriftReport): Maybe<string>;
132
+ /**
133
+ * Finds and parses the artifact `package.json` nearest the executed entry file.
134
+ *
135
+ * @param entryPath - The executed entry file (`process.argv[1]`).
136
+ * @returns The parsed stamp, or `undefined` when none could be read.
137
+ */
138
+ export declare function readCliArtifactPackage(entryPath: string): Maybe<CliArtifactPackage>;
@@ -1,2 +1,3 @@
1
+ export * from './build-stamp';
1
2
  export * from './lifecycle';
2
3
  export * from './run';
@@ -146,6 +146,17 @@ export interface CreateCliInput extends CliLifecycleHooks {
146
146
  * bundler `define` or a generated module).
147
147
  */
148
148
  readonly version?: string;
149
+ /**
150
+ * The `@dereekb/dbx-cli` version that emitted this CLI's generated manifests — pass
151
+ * `<NAMESPACE>_STAMP.generatorVersion` from a generated manifest module.
152
+ *
153
+ * Wiring it turns the built-in `cli-build-not-stale` doctor check from "the artifact's declared
154
+ * peers match `node_modules`" into "…and the committed manifests were generated by that same
155
+ * version", which is the half the peer comparison cannot see: bumping `@dereekb/*` and rebuilding
156
+ * without re-running the generators leaves the manifests behind, and a manifest missing a field the
157
+ * runtime reads degrades silently rather than failing.
158
+ */
159
+ readonly manifestGeneratorVersion?: string;
149
160
  /**
150
161
  * The lifecycle runner guarding this invocation's {@link CliLifecycleHooks}.
151
162
  *
@@ -188,6 +199,8 @@ export interface CreateCliInput extends CliLifecycleHooks {
188
199
  * if {@link CreateCliInput.modelManifest} is provided.
189
200
  * @param input.firestore - The app-supplied direct-Firestore binding; enables `firestore-get` / `firestore-query`.
190
201
  * @param input.firestoreQueryManifest - The generated Firestore query catalog; enables `firestore-queries`.
202
+ * @param input.manifestGeneratorVersion - The `@dereekb/dbx-cli` version that emitted the app's generated
203
+ * manifests, for the built-in `cli-build-not-stale` doctor check.
191
204
  * @param input.setup - App hook run once before the command's handler; a throw aborts the command.
192
205
  * @param input.teardown - App hook run once after the parser settles, before the Firestore session closes.
193
206
  * Only {@link runCli} runs it — a caller that drives `createCli().parse()` itself owns its own teardown.
package/test/package.json CHANGED
@@ -1,16 +1,16 @@
1
1
  {
2
2
  "name": "@dereekb/dbx-cli/test",
3
- "version": "13.41.0",
3
+ "version": "13.43.0",
4
4
  "type": "module",
5
5
  "peerDependencies": {
6
- "@dereekb/date": "13.41.0",
7
- "@dereekb/dbx-cli": "13.41.0",
8
- "@dereekb/firebase": "13.41.0",
9
- "@dereekb/firebase-server/test": "13.41.0",
10
- "@dereekb/model": "13.41.0",
11
- "@dereekb/nestjs": "13.41.0",
12
- "@dereekb/rxjs": "13.41.0",
13
- "@dereekb/util": "13.41.0",
6
+ "@dereekb/date": "13.43.0",
7
+ "@dereekb/dbx-cli": "13.43.0",
8
+ "@dereekb/firebase": "13.43.0",
9
+ "@dereekb/firebase-server/test": "13.43.0",
10
+ "@dereekb/model": "13.43.0",
11
+ "@dereekb/nestjs": "13.43.0",
12
+ "@dereekb/rxjs": "13.43.0",
13
+ "@dereekb/util": "13.43.0",
14
14
  "@nestjs/common": "^11.1.19",
15
15
  "arktype": "^2.2.0",
16
16
  "vitest": "4.1.5",
package/validate/index.js CHANGED
@@ -6334,6 +6334,16 @@ var RESERVED_MODEL_FOLDERS = [
6334
6334
  name: "storagefile",
6335
6335
  reason: "StorageFile is a canonical group from `@dereekb/firebase` with a richer layout than the base 5 files (group, upload, file, etc.); downstream projects extend rather than redeclare it.",
6336
6336
  recommendedTool: "dbx_storagefile_m_validate_folder"
6337
+ },
6338
+ {
6339
+ name: "formspace",
6340
+ reason: "FormSpace is a canonical group from `@dereekb/firebase` with a richer layout than the base 5 files (type, upload, task, util, etc.); a downstream project only registers its own types and does not redeclare the model.",
6341
+ recommendedTool: "dbx_model_validate_folder"
6342
+ },
6343
+ {
6344
+ name: "calendar",
6345
+ reason: "Calendar is a canonical group from `@dereekb/firebase` with a richer layout than the base 5 files (type, expand, ics, processing, etc.); downstream projects extend rather than redeclare it.",
6346
+ recommendedTool: "dbx_model_validate_folder"
6337
6347
  }
6338
6348
  ];
6339
6349
 
@@ -7254,7 +7264,7 @@ function extractGroupModelNames(text) {
7254
7264
  while ((match = MODEL_NAME_RE.exec(text)) !== null) {
7255
7265
  seen.add(match[1]);
7256
7266
  }
7257
- return [...seen].sort((a, b) => a.localeCompare(b));
7267
+ return Array.from(seen).sort((a, b) => a.localeCompare(b));
7258
7268
  }
7259
7269
 
7260
7270
  // packages/dbx-cli/src/lib/mcp-scan/scan/_jsdoc-tagged-export/extract-base.ts
@@ -8355,7 +8365,7 @@ function buildSummaries(entries, issues) {
8355
8365
  const summary = byModel.get(issue.model);
8356
8366
  if (summary) summary.errorCount += 1;
8357
8367
  }
8358
- return [...byModel.entries()].map(([model, s]) => ({ model, ...s })).sort((a, b) => a.model.localeCompare(b.model));
8368
+ return Array.from(byModel.entries()).map(([model, s]) => ({ model, ...s })).sort((a, b) => a.model.localeCompare(b.model));
8359
8369
  }
8360
8370
  function compareEntries(a, b) {
8361
8371
  return a.model.localeCompare(b.model) || a.verb.localeCompare(b.verb) || (a.specifier ?? "").localeCompare(b.specifier ?? "");
@@ -8431,7 +8441,7 @@ async function readComponentDereekbDeps(componentAbs) {
8431
8441
  addScopedDependencyNames(json[field], out);
8432
8442
  }
8433
8443
  }
8434
- return [...out];
8444
+ return Array.from(out);
8435
8445
  }
8436
8446
  function addScopedDependencyNames(field, out) {
8437
8447
  if (isRecord(field)) {
@@ -9864,7 +9874,7 @@ function tryReadTemplateInfoRecord(decl, rel, index) {
9864
9874
  symbolName: decl.getName(),
9865
9875
  directInfoIdentifiers: direct,
9866
9876
  spreadAggregateIdentifiers: spreads,
9867
- resolvedInfoIdentifiers: [...resolved],
9877
+ resolvedInfoIdentifiers: Array.from(resolved),
9868
9878
  unresolvedSpreadIdentifiers: unresolved,
9869
9879
  sourceFile: rel
9870
9880
  };
@@ -11644,7 +11654,7 @@ function flagUploadServiceWiring(extracted, violations) {
11644
11654
  if (!anyWired) {
11645
11655
  pushViolation9(violations, {
11646
11656
  code: "STORAGEFILE_UPLOAD_SERVICE_NOT_WIRED",
11647
- message: `No NestJS provider with \`provide: StorageFileInitializeFromUploadService, useFactory: ${factoryNames.size > 0 ? [...factoryNames].join(" | ") : "<storage-file-upload-factory>"}\` found. Bind the factory function in a NestJS module.`,
11657
+ message: `No NestJS provider with \`provide: StorageFileInitializeFromUploadService, useFactory: ${factoryNames.size > 0 ? Array.from(factoryNames).join(" | ") : "<storage-file-upload-factory>"}\` found. Bind the factory function in a NestJS module.`,
11648
11658
  side: "api",
11649
11659
  file: void 0
11650
11660
  });
@@ -13175,7 +13185,7 @@ function collectAllModels(fixtures, instances) {
13175
13185
  const all = /* @__PURE__ */ new Set();
13176
13186
  for (const k of fixtures.keys()) all.add(k);
13177
13187
  for (const k of instances.keys()) all.add(k);
13178
- const list = [...all];
13188
+ const list = Array.from(all);
13179
13189
  list.sort((a, b) => {
13180
13190
  const aLine = (fixtures.get(a) ?? instances.get(a))?.getStartLineNumber() ?? 0;
13181
13191
  const bLine = (fixtures.get(b) ?? instances.get(b))?.getStartLineNumber() ?? 0;
@@ -13229,7 +13239,7 @@ function collectIdentityImports(sourceFile) {
13229
13239
  }
13230
13240
  }
13231
13241
  }
13232
- const list = [...out];
13242
+ const list = Array.from(out);
13233
13243
  list.sort((a, b) => a.localeCompare(b));
13234
13244
  return list;
13235
13245
  }
@@ -1,11 +1,11 @@
1
1
  {
2
2
  "name": "@dereekb/dbx-cli/validate",
3
- "version": "13.41.0",
3
+ "version": "13.43.0",
4
4
  "type": "module",
5
5
  "sideEffects": false,
6
6
  "peerDependencies": {
7
- "@dereekb/dbx-cli": "13.41.0",
8
- "@dereekb/util": "13.41.0",
7
+ "@dereekb/dbx-cli": "13.43.0",
8
+ "@dereekb/util": "13.43.0",
9
9
  "ts-morph": "^21.0.0"
10
10
  }
11
11
  }