@dereekb/dbx-cli 13.36.0 → 13.38.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 (69) hide show
  1. package/eslint/package.json +3 -3
  2. package/firebase-api-manifest/main.js +191 -166
  3. package/firebase-api-manifest/package.json +3 -3
  4. package/firestore-query-manifest/main.js +2063 -0
  5. package/firestore-query-manifest/package.json +13 -0
  6. package/firestore-rules/src/firestore-rules-scan.d.ts +89 -0
  7. package/firestore-rules/src/index.d.ts +1 -0
  8. package/generate-firestore-indexes/main.js +2 -2
  9. package/generate-firestore-indexes/package.json +2 -2
  10. package/generate-mcp-manifest/main.js +1 -0
  11. package/generate-mcp-manifest/package.json +3 -3
  12. package/generate-route-manifest/package.json +2 -2
  13. package/index.cjs.js +12675 -6864
  14. package/index.esm.js +12620 -6868
  15. package/lint-cache/package.json +2 -2
  16. package/manifest-extract/index.cjs.js +4 -1
  17. package/manifest-extract/index.esm.js +4 -1
  18. package/manifest-extract/package.json +2 -2
  19. package/manifest-extract/src/lib/types.d.ts +6 -0
  20. package/package.json +11 -6
  21. package/route/package.json +7 -7
  22. package/src/lib/api/firestore-session.client.d.ts +54 -0
  23. package/src/lib/api/get-args.helper.d.ts +4 -1
  24. package/src/lib/api/get-many.command.d.ts +8 -3
  25. package/src/lib/api/get.command.d.ts +13 -5
  26. package/src/lib/api/index.d.ts +1 -0
  27. package/src/lib/config/env.d.ts +106 -0
  28. package/src/lib/config/firestore-session.cache.d.ts +86 -0
  29. package/src/lib/config/index.d.ts +1 -0
  30. package/src/lib/config/paths.d.ts +3 -1
  31. package/src/lib/context/cli.context.d.ts +87 -3
  32. package/src/lib/doctor/firestore-session.check.d.ts +113 -0
  33. package/src/lib/doctor/index.d.ts +1 -0
  34. package/src/lib/firestore/firestore-get.command.d.ts +28 -0
  35. package/src/lib/firestore/firestore-queries.command.d.ts +26 -0
  36. package/src/lib/firestore/firestore-query.command.d.ts +25 -0
  37. package/src/lib/firestore/firestore.accessor.d.ts +85 -0
  38. package/src/lib/firestore/firestore.collection.d.ts +44 -0
  39. package/src/lib/firestore/firestore.error.d.ts +14 -0
  40. package/src/lib/firestore/firestore.models.d.ts +223 -0
  41. package/src/lib/firestore/firestore.query-params.d.ts +34 -0
  42. package/src/lib/firestore/firestore.query.d.ts +79 -0
  43. package/src/lib/firestore/firestore.read.d.ts +180 -0
  44. package/src/lib/firestore/firestore.session.d.ts +97 -0
  45. package/src/lib/firestore/index.d.ts +13 -0
  46. package/src/lib/firestore/query-info-utils.d.ts +48 -0
  47. package/src/lib/firestore/query-registry.d.ts +32 -0
  48. package/src/lib/index.d.ts +3 -0
  49. package/src/lib/manifest/types.d.ts +117 -0
  50. package/src/lib/mcp-scan/manifest/dbx-docs-ui-examples-schema.d.ts +2 -2
  51. package/src/lib/mcp-scan/manifest/model-snapshot-fields-schema.d.ts +2 -2
  52. package/src/lib/mcp-scan/manifest/pipes-schema.d.ts +2 -2
  53. package/src/lib/mcp-scan/manifest/ui-components-schema.d.ts +2 -2
  54. package/src/lib/mcp-scan/manifest/utils-schema.d.ts +2 -2
  55. package/src/lib/mcp-scan/scan/dbx-docs-ui-examples-extract.d.ts +1 -1
  56. package/src/lib/mcp-scan/scan/extract-models/types.d.ts +5 -0
  57. package/src/lib/mcp-scan/scan/ui-components-extract.d.ts +1 -1
  58. package/src/lib/middleware/auth.middleware.d.ts +6 -0
  59. package/src/lib/runner/run.d.ts +28 -1
  60. package/src/lib/scan-helpers/emit-generated-ts.d.ts +76 -0
  61. package/src/lib/scan-helpers/exported-from-package.d.ts +29 -0
  62. package/src/lib/util/index.d.ts +1 -0
  63. package/src/lib/util/output.d.ts +10 -0
  64. package/src/lib/util/stdin.d.ts +39 -2
  65. package/src/lib/util/table.d.ts +40 -0
  66. package/test/index.cjs.js +4 -2
  67. package/test/index.esm.js +4 -2
  68. package/test/package.json +9 -9
  69. package/test/src/lib/cli-test.d.ts +12 -2
@@ -0,0 +1,113 @@
1
+ import { type CliFirestoreBinding } from '../firestore/firestore.models';
2
+ import { type CliReadSourceReason } from '../firestore/firestore.read';
3
+ import { type CliFirestoreSessionContext } from '../firestore/firestore.session';
4
+ import { type CliFirestoreQueryManifest, type CliModelManifest } from '../manifest/types';
5
+ import { type DoctorCheck } from './doctor.command.factory';
6
+ /**
7
+ * Name reported by the check {@link createFirestoreSessionDoctorCheck} builds.
8
+ */
9
+ export declare const FIRESTORE_SESSION_DOCTOR_CHECK_NAME = "firestore-session";
10
+ /**
11
+ * An app-supplied read that proves the direct Firestore connection works end to end.
12
+ *
13
+ * The framework cannot know which collection is rules-protected in a given app, so the last hop —
14
+ * "App Check accepted and a rules-protected read succeeded" — is delegated. Point it at a collection
15
+ * an admin may list and a non-admin may not (e.g. `getDocs(query(collection(firestore, 'wk'), limit(1)))`).
16
+ *
17
+ * Return anything JSON-serializable; it is surfaced on the check's `detail.probe`.
18
+ */
19
+ export type FirestoreSessionDoctorProbe = (context: CliFirestoreSessionContext) => Promise<unknown>;
20
+ export interface CreateFirestoreSessionDoctorCheckInput {
21
+ /**
22
+ * The rules-protected read that proves the session is genuinely usable. Strongly recommended —
23
+ * without it the check stops after sign-in and cannot tell whether App Check was accepted.
24
+ */
25
+ readonly probe?: FirestoreSessionDoctorProbe;
26
+ /**
27
+ * Human-readable label for what the probe reads, surfaced in the check detail (e.g. `list /wk`).
28
+ */
29
+ readonly probeName?: string;
30
+ /**
31
+ * The same `firestore` binding passed to `runCli`. Doctor checks run PRE-AUTH with no `CliContext`,
32
+ * so the binding cannot be discovered — pass it here and the check reports whether
33
+ * `CliContext.getFirestoreModels` would exist, and therefore whether `--via auto` can ever go direct.
34
+ */
35
+ readonly firestore?: CliFirestoreBinding;
36
+ /**
37
+ * The same `modelManifest` passed to `runCli`, for the server-only model count.
38
+ */
39
+ readonly modelManifest?: CliModelManifest;
40
+ /**
41
+ * The same `firestoreQueryManifest` passed to `runCli`, for the invocable query-entry count.
42
+ */
43
+ readonly firestoreQueryManifest?: CliFirestoreQueryManifest;
44
+ }
45
+ /**
46
+ * The read-routing summary the check reports alongside the session handshake — the `--via auto`
47
+ * decision an operator would otherwise have to infer from three separate facts.
48
+ */
49
+ export interface FirestoreSessionDoctorReadRouting {
50
+ /**
51
+ * Whether `CliContext.getFirestoreModels` exists — i.e. `runCli` was given a `firestore` binding.
52
+ */
53
+ readonly getFirestoreModels: boolean;
54
+ /**
55
+ * What `--via auto` would choose given the state this check just observed.
56
+ */
57
+ readonly readPreference: 'firestore' | 'api';
58
+ readonly reason: CliReadSourceReason;
59
+ /**
60
+ * Query-catalog entries whose factory bound to a real runtime export, and are therefore runnable
61
+ * by `firestore-query`. A non-invocable entry is listed by `firestore-queries` with `INVOCABLE = no`.
62
+ */
63
+ readonly invocableQueryEntries: number;
64
+ readonly totalQueryEntries: number;
65
+ /**
66
+ * Models the manifest marks `@dbxModelServerOnly` — refused on every `--via` value.
67
+ */
68
+ readonly serverOnlyModels: number;
69
+ }
70
+ /**
71
+ * Summarizes the `--via auto` routing decision from what doctor can observe.
72
+ *
73
+ * @param input - The wired bindings/manifests plus whether the session actually opened.
74
+ * @param input.firestore - The `firestore` binding, when supplied.
75
+ * @param input.modelManifest - The model manifest, when supplied.
76
+ * @param input.firestoreQueryManifest - The query manifest, when supplied.
77
+ * @param input.firebaseConfigComplete - Whether the env carries a complete Firebase client config.
78
+ * @param input.sessionOpened - Whether the session handshake succeeded in this run.
79
+ * @returns The routing summary.
80
+ *
81
+ * @__NO_SIDE_EFFECTS__
82
+ */
83
+ export declare function buildFirestoreSessionDoctorReadRouting(input: {
84
+ readonly firestore?: CliFirestoreBinding;
85
+ readonly modelManifest?: CliModelManifest;
86
+ readonly firestoreQueryManifest?: CliFirestoreQueryManifest;
87
+ readonly firebaseConfigComplete: boolean;
88
+ readonly sessionOpened: boolean;
89
+ }): FirestoreSessionDoctorReadRouting;
90
+ /**
91
+ * Builds the doctor check for the direct-Firestore session path.
92
+ *
93
+ * This is the **fail-loudly surface** for the feature: `CliContext.getFirestoreContext()` throws
94
+ * rather than falling back to the HTTP model API, so `doctor` is where an operator finds out which
95
+ * hop broke. It walks the whole chain in order:
96
+ *
97
+ * 1. the env carries a complete Firebase client config;
98
+ * 2. a usable access token is available (cached, env-supplied, or refreshed);
99
+ * 3. `GET /session/firestore` is reachable and mints a custom token (+ an App Check token when the
100
+ * API is configured with a web `appId`);
101
+ * 4. `signInWithCustomToken` succeeds against the configured project;
102
+ * 5. the app-supplied {@link FirestoreSessionDoctorProbe} performs one rules-protected read.
103
+ *
104
+ * Doctor checks run PRE-AUTH — `DoctorCheckInput` is only `{ cliName, envName, env, config }`, with no
105
+ * token and no `CliContext` — so this loads credentials itself via `buildCliPaths` +
106
+ * `createCliTokenCacheStore`, matching the built-in `token-cache-fresh` / `token-refresh-round-trip`
107
+ * checks.
108
+ *
109
+ * @param input - Optional probe configuration.
110
+ * @returns A {@link DoctorCheck} to append to a CLI's `doctor` check list.
111
+ * @__NO_SIDE_EFFECTS__
112
+ */
113
+ export declare function createFirestoreSessionDoctorCheck(input?: CreateFirestoreSessionDoctorCheckInput): DoctorCheck;
@@ -1 +1,2 @@
1
1
  export * from './doctor.command.factory';
2
+ export * from './firestore-session.check';
@@ -0,0 +1,28 @@
1
+ import type { CommandModule } from 'yargs';
2
+ /**
3
+ * Default command name for the direct-Firestore single-document read.
4
+ */
5
+ export declare const DEFAULT_FIRESTORE_GET_COMMAND_NAME = "firestore-get";
6
+ /**
7
+ * Options accepted by {@link buildFirestoreGetCommand}.
8
+ */
9
+ export interface BuildFirestoreGetCommandOptions {
10
+ readonly commandName?: string;
11
+ }
12
+ /**
13
+ * Builds the top-level `firestore-get <modelOrKey> [key]` command.
14
+ *
15
+ * Reads one document over the direct Firestore connection, through security rules. Positional
16
+ * parsing goes through the same {@link parseGetArgs} the API-backed `get` uses, so inferred-model
17
+ * resolution behaves identically, and the emitted `{ key, data }` is byte-identical to
18
+ * `GetModelOverHttpResult`.
19
+ *
20
+ * There is no `--via` here — the command name IS the transport selection. `get --via firestore` is
21
+ * the routed equivalent for callers that want a fallback.
22
+ *
23
+ * @param options - Optional command-name override.
24
+ * @returns A yargs `CommandModule` for `runCli({ apiCommands })`.
25
+ *
26
+ * @__NO_SIDE_EFFECTS__
27
+ */
28
+ export declare function buildFirestoreGetCommand(options?: BuildFirestoreGetCommandOptions): CommandModule;
@@ -0,0 +1,26 @@
1
+ import type { CommandModule } from 'yargs';
2
+ import { type CliFirestoreQueryManifest } from '../manifest/types';
3
+ /**
4
+ * Default command name for the Firestore query catalog command.
5
+ */
6
+ export declare const DEFAULT_FIRESTORE_QUERIES_COMMAND_NAME = "firestore-queries";
7
+ /**
8
+ * Options accepted by {@link buildFirestoreQueriesCommand}.
9
+ */
10
+ export interface BuildFirestoreQueriesCommandOptions {
11
+ readonly commandName?: string;
12
+ }
13
+ /**
14
+ * Builds the top-level `firestore-queries [query]` catalog command.
15
+ *
16
+ * Registered as a CONFIG command, not an API command — browsing the catalog is a documentation
17
+ * read, and demanding a login to see what queries exist would be the wrong trade. This mirrors the
18
+ * existing `model-info` (config, auth-bypassed) vs `model <m> get` (api, post-auth) split.
19
+ *
20
+ * @param manifest - The generated Firestore query manifest.
21
+ * @param options - Optional command-name override.
22
+ * @returns A yargs `CommandModule` for `runCli({ configCommands })`.
23
+ *
24
+ * @__NO_SIDE_EFFECTS__
25
+ */
26
+ export declare function buildFirestoreQueriesCommand(manifest: CliFirestoreQueryManifest, options?: BuildFirestoreQueriesCommandOptions): CommandModule;
@@ -0,0 +1,25 @@
1
+ import type { CommandModule } from 'yargs';
2
+ import { type CliFirestoreQueryManifest } from '../manifest/types';
3
+ /**
4
+ * Default command name for the Firestore query execution command.
5
+ */
6
+ export declare const DEFAULT_FIRESTORE_QUERY_COMMAND_NAME = "firestore-query";
7
+ /**
8
+ * Options accepted by {@link buildFirestoreQueryCommand}.
9
+ */
10
+ export interface BuildFirestoreQueryCommandOptions {
11
+ readonly commandName?: string;
12
+ }
13
+ /**
14
+ * Builds the top-level `firestore-query <query>` command.
15
+ *
16
+ * Executes a catalog entry over the direct Firestore connection, through the app's security rules
17
+ * as the authenticated user.
18
+ *
19
+ * @param manifest - The generated Firestore query manifest.
20
+ * @param options - Optional command-name override.
21
+ * @returns A yargs `CommandModule` for `runCli({ apiCommands })`.
22
+ *
23
+ * @__NO_SIDE_EFFECTS__
24
+ */
25
+ export declare function buildFirestoreQueryCommand(manifest: CliFirestoreQueryManifest, options?: BuildFirestoreQueryCommandOptions): CommandModule;
@@ -0,0 +1,85 @@
1
+ import { type FirebaseAppModelContext, type FirebaseModelsService } from '@dereekb/firebase';
2
+ import { type Maybe } from '@dereekb/util';
3
+ import { type GetModelOverHttpResult, type GetMultipleModelsOverHttpResult } from '../api/call-model.client';
4
+ import { type CliContext } from '../context/cli.context';
5
+ import { type CliFirestoreBinding, type CliFirestoreBindingInput, type CliFirestoreModels } from './firestore.models';
6
+ /**
7
+ * A callable that resolves the app's TYPED direct-Firestore view for the current invocation, plus the
8
+ * binding to wire the CLI with.
9
+ *
10
+ * The one place an app names its `<X>FirestoreCollections`: `cliFirestoreAccessorFactory` captures
11
+ * both `C` and `Y` at the app site, and hands them back on every call — so an action reads through
12
+ * `collections.guestbookCollection` and `serviceFor('guestbook')` at the app's real types instead of
13
+ * rebuilding the collections itself to recover them.
14
+ *
15
+ * @template C - The app's collections type, e.g. `DemoFirestoreCollections`.
16
+ * @template Y - The app's `<app>FirebaseModelServices` type.
17
+ */
18
+ export type CliFirestoreAccessorFactory<C extends object, Y extends FirebaseModelsService<any, any>> = ((context: CliContext) => Promise<CliFirestoreAccessor<C, Y>>) & {
19
+ /**
20
+ * The binding to hand `runCli({ firestore })`, `buildTestCliContext({ firestore })`, and
21
+ * `createFirestoreSessionDoctorCheck({ firestore })`.
22
+ *
23
+ * Sharing THIS object — rather than calling `cliFirestoreBinding` again with the same arguments —
24
+ * is what lets the accessor reuse the context's memoized collections instead of building a second
25
+ * copy: the reuse branch is an identity check, and two separate `cliFirestoreBinding` calls produce
26
+ * two non-identical bindings.
27
+ */
28
+ readonly binding: CliFirestoreBinding<C>;
29
+ };
30
+ /**
31
+ * The typed direct-Firestore view an app's actions read through.
32
+ *
33
+ * A {@link CliFirestoreModels} carrying the app's real `C` / `Y`, plus the two read helpers, so an
34
+ * action never has to import `getModelOverFirestore` alongside it.
35
+ *
36
+ * @template C - The app's collections type.
37
+ * @template Y - The app's `<app>FirebaseModelServices` type.
38
+ */
39
+ export type CliFirestoreAccessor<C extends object, Y extends FirebaseModelsService<any, any>> = CliFirestoreModels<C, Y> & {
40
+ /**
41
+ * Reads one document by model key, returning the same `{ key, data }` envelope
42
+ * `GET /model/<type>/get` returns.
43
+ */
44
+ readonly readModel: <T = unknown>(modelType: string, key: string) => Promise<GetModelOverHttpResult<Maybe<T>>>;
45
+ /**
46
+ * Batch-reads documents by model key, returning the same `{ results, errors }` envelope the model
47
+ * API's `getMany` returns.
48
+ */
49
+ readonly readMultipleModels: <T = unknown>(modelType: string, keys: ReadonlyArray<string>) => Promise<GetMultipleModelsOverHttpResult<Maybe<T>>>;
50
+ };
51
+ /**
52
+ * Registers an app's collections factory + model services once, and returns the accessor its actions
53
+ * read through.
54
+ *
55
+ * Resolution order on each call, three cases:
56
+ *
57
+ * 1. the context was wired with THIS binding (`models.binding === binding`) → reuse its memoized
58
+ * view. The identity check is what makes the `object` → `C` narrowing sound: `collections`
59
+ * provably came from `binding.collections`, which returns `C`.
60
+ * 2. the context has NO binding → build a view from `requireCliFirestoreSession(context)`, so an app
61
+ * can use the accessor in its actions without also passing `runCli({ firestore })`.
62
+ * 3. the context was wired with a DIFFERENT binding → build a second view and `verboseLog` about it,
63
+ * rather than silently asserting that someone else's `collections` is a `C`.
64
+ *
65
+ * @param input - The app's collections factory, model services, and optional per-model override.
66
+ * @returns The callable accessor, carrying {@link CliFirestoreAccessorFactory.binding}.
67
+ *
68
+ * @example
69
+ * ```ts
70
+ * export const demoCliFirestore = cliFirestoreAccessorFactory({
71
+ * collections: makeDemoFirestoreCollections,
72
+ * models: demoFirebaseModelServices
73
+ * });
74
+ *
75
+ * // wiring
76
+ * runCli({ firestore: demoCliFirestore.binding });
77
+ *
78
+ * // in an action
79
+ * const { collections, session } = await demoCliFirestore(input.context);
80
+ * const entries = await collections.guestbookCollection.queryDocument(...).getDocs();
81
+ * ```
82
+ *
83
+ * @__NO_SIDE_EFFECTS__
84
+ */
85
+ export declare function cliFirestoreAccessorFactory<C extends object, Y extends FirebaseModelsService<any, FirebaseAppModelContext<C>>>(input: CliFirestoreBindingInput<C, Y>): CliFirestoreAccessorFactory<C, Y>;
@@ -0,0 +1,44 @@
1
+ import { type FirestoreCollectionLike, type FirestoreModelKey, type FirestoreModelType } from '@dereekb/firebase';
2
+ import { type CliFirestoreQueryScope } from '../manifest/types';
3
+ import { type CliFirestoreModels } from './firestore.models';
4
+ /**
5
+ * Input for {@link cliFirestoreCollectionForQuery}.
6
+ */
7
+ export interface CliFirestoreCollectionForQueryInput {
8
+ readonly models: CliFirestoreModels;
9
+ readonly modelType: FirestoreModelType;
10
+ /**
11
+ * The entry's declared scope. Together with {@link isNested} it decides whether `--parent` is
12
+ * optional, required, or rejected.
13
+ */
14
+ readonly scope: CliFirestoreQueryScope;
15
+ /**
16
+ * Whether the model is a subcollection of some parent.
17
+ */
18
+ readonly isNested: boolean;
19
+ /**
20
+ * The parent document key to narrow to, from `--parent`.
21
+ */
22
+ readonly parentKey?: FirestoreModelKey;
23
+ }
24
+ /**
25
+ * Resolves the collection a catalog entry should execute against, applying `--parent` scoping.
26
+ *
27
+ * The `--parent` rules, and why:
28
+ *
29
+ * | entry | `--parent` |
30
+ * | --- | --- |
31
+ * | `COLLECTION_GROUP` + nested | optional — narrows the group to one parent |
32
+ * | `COLLECTION` + nested | **required** — the COLLECTION-scope composite index may not exist at group scope, so silently widening would turn a working query into a `FAILED_PRECONDITION` |
33
+ * | not nested | rejected — a root collection has no parent to scope to |
34
+ *
35
+ * Narrowing is derived generically rather than per-model: `FirestoreCollectionGroup.config` is
36
+ * public and `makeFirestoreCollectionGroup` accepts any `queryLike`, so re-pointing the group's
37
+ * `queryLike` at `firestoreContext.subcollection(parentRef, collectionName)` scopes it to one
38
+ * parent. `docAtPath` needs no parent modelType — the parent key IS the path.
39
+ *
40
+ * @param input - The models view, the entry's model/scope/nesting, and the optional parent key.
41
+ * @returns The collection to query.
42
+ * @throws {CliError} When `--parent` is missing where required, supplied where rejected, or the registered collection cannot be scoped.
43
+ */
44
+ export declare function cliFirestoreCollectionForQuery(input: CliFirestoreCollectionForQueryInput): FirestoreCollectionLike<unknown>;
@@ -0,0 +1,14 @@
1
+ import { type CliErrorMapper } from '../util/output';
2
+ /**
3
+ * Maps a client-SDK `FirebaseError` onto the CLI's error envelope.
4
+ *
5
+ * Without this a rules rejection falls through `buildErrorOutput` to a bare
6
+ * `{ ok: false, code: 'ERROR' }` — indistinguishable from any other failure, which is exactly the
7
+ * wrong answer for the one error the direct path exists to surface honestly.
8
+ *
9
+ * @param error - The thrown value.
10
+ * @returns The mapped envelope, or `undefined` to defer to the built-in branches.
11
+ *
12
+ * @__NO_SIDE_EFFECTS__
13
+ */
14
+ export declare const cliFirestoreErrorMapper: CliErrorMapper;
@@ -0,0 +1,223 @@
1
+ import { type FirebaseAppModelContext, type FirebaseModelServiceGetter, type FirebaseModelsService, type FirebaseModelsServiceTypes, type FirestoreCollectionLike, type FirestoreContext, type FirestoreDocument, type FirestoreModelKey, type FirestoreModelType, type InContextFirebaseModelCollectionLoader, type InContextFirebaseModelLoader } from '@dereekb/firebase';
2
+ import { type Maybe } from '@dereekb/util';
3
+ import { type CliContext } from '../context/cli.context';
4
+ import { type CliFirestoreSessionContext } from './firestore.session';
5
+ /**
6
+ * The slice of a `FirebaseModelService` the CLI's generic direct-Firestore reads need.
7
+ *
8
+ * Only the two members that never touch `roleMapForModel`: `dbx-cli` reads through Firestore
9
+ * security rules, not through the app's role map, so the permission half of the service is
10
+ * deliberately unused here.
11
+ */
12
+ export interface CliFirestoreModelService {
13
+ readonly loadModelForKey: (key: FirestoreModelKey) => FirestoreDocument<unknown>;
14
+ readonly getFirestoreCollection: () => FirestoreCollectionLike<unknown>;
15
+ }
16
+ /**
17
+ * The callable shape of an app's `<app>FirebaseModelServices`, erased to what the CLI needs.
18
+ */
19
+ export type CliFirestoreModelsService = ((modelType: FirestoreModelType, context: {
20
+ readonly app: unknown;
21
+ }) => CliFirestoreModelService) & {
22
+ allTypes(): FirestoreModelType[];
23
+ };
24
+ /**
25
+ * Input for a {@link CliFirestoreBinding.collectionForModel} override.
26
+ */
27
+ export interface CliFirestoreCollectionForModelInput {
28
+ readonly collections: object;
29
+ readonly modelType: FirestoreModelType;
30
+ readonly parentKey?: FirestoreModelKey;
31
+ }
32
+ /**
33
+ * The one app-supplied hook that makes generic direct-Firestore commands possible.
34
+ *
35
+ * `dbx-cli` cannot import an app's collections factory — that is the whole reason there is no
36
+ * generic direct command without this. Supplying it wires `firestore-get` / `firestore-query`
37
+ * for EVERY registered model at once; there is no per-model codegen.
38
+ *
39
+ * `C` appears in exactly one position — the RETURN of {@link collections} — and that is deliberate.
40
+ * Because the use is purely covariant, `CliFirestoreBinding<DemoFirestoreCollections>` is assignable
41
+ * to `CliFirestoreBinding<object>`, which is what lets `CreateCliInput.firestore` keep accepting a
42
+ * typed binding with no cast and keeps generics out of `CliContext` / `runCli` entirely.
43
+ *
44
+ * Adding `C` (or the models generic `Y`) to {@link models} or to
45
+ * {@link CliFirestoreCollectionForModelInput} would put it in a PARAMETER position, and under
46
+ * `strictFunctionTypes` that destroys the assignability above — cascading generics through
47
+ * `CreateCliInput` → `createAuthMiddleware` → `createCliContext` → `CliContext`. Don't.
48
+ *
49
+ * @template C - The app's collections type, e.g. `DemoFirestoreCollections`.
50
+ */
51
+ export interface CliFirestoreBinding<C extends object = object> {
52
+ /**
53
+ * The app's `make<App>FirestoreCollections`.
54
+ */
55
+ readonly collections: (firestoreContext: FirestoreContext) => C;
56
+ /**
57
+ * The app's `<app>FirebaseModelServices`.
58
+ */
59
+ readonly models: CliFirestoreModelsService;
60
+ /**
61
+ * Escape hatch for models whose registered collection cannot be parent-scoped generically
62
+ * (e.g. a paged-items collection). Return `undefined` to fall through to the derived scoping.
63
+ */
64
+ readonly collectionForModel?: (input: CliFirestoreCollectionForModelInput) => Maybe<FirestoreCollectionLike<unknown>>;
65
+ }
66
+ /**
67
+ * Input for {@link cliFirestoreBinding} and {@link cliFirestoreAccessorFactory}.
68
+ *
69
+ * `Y` sits in a PROPERTY position here, which is safe precisely because this is the INPUT type and
70
+ * never the binding: nothing assigns a `CliFirestoreBindingInput` to `CreateCliInput.firestore`, so
71
+ * the variance rule on {@link CliFirestoreBinding} does not apply. Capturing it is what lets
72
+ * `cliFirestoreAccessorFactory` hand back the app's real model types.
73
+ *
74
+ * @template C - The app's collections type, e.g. `DemoFirestoreCollections`.
75
+ * @template Y - The app's `<app>FirebaseModelServices` type.
76
+ */
77
+ export interface CliFirestoreBindingInput<C extends object, Y extends FirebaseModelsService<any, FirebaseAppModelContext<C>> = FirebaseModelsService<any, FirebaseAppModelContext<C>>> {
78
+ readonly collections: (firestoreContext: FirestoreContext) => C;
79
+ readonly models: Y;
80
+ readonly collectionForModel?: (input: CliFirestoreCollectionForModelInput) => Maybe<FirestoreCollectionLike<unknown>>;
81
+ }
82
+ /**
83
+ * Builds a {@link CliFirestoreBinding} from an app's collections factory and model services.
84
+ *
85
+ * Typed so the app site needs no cast — `C` is carried through on {@link CliFirestoreBinding.collections},
86
+ * and the one erasure the CLI requires (the app's model-service union → {@link CliFirestoreModelService})
87
+ * is absorbed here rather than at every call site.
88
+ *
89
+ * The `{ app: collections }` context handed to the model service is COMPLETE for the two members
90
+ * the CLI uses: `FirebasePermissionContext`, `FirebasePermissionErrorContext` and
91
+ * `FirebaseAuthContext` are all-optional, `firebaseModelsService` self-injects `service`, and
92
+ * neither `loadModelForKey` nor `getFirestoreCollection` reads `auth`.
93
+ *
94
+ * Most apps should reach for {@link cliFirestoreAccessorFactory} instead, which calls this and ALSO
95
+ * hands back a typed accessor for the app's own actions. Use this directly only when the binding is
96
+ * all you need.
97
+ *
98
+ * @param input - The app's collections factory, model services, and optional per-model override.
99
+ * @returns The binding `runCli` accepts, still carrying `C`.
100
+ *
101
+ * @example
102
+ * ```ts
103
+ * runCli({
104
+ * firestore: cliFirestoreBinding({ collections: makeDemoFirestoreCollections, models: demoFirebaseModelServices })
105
+ * });
106
+ * ```
107
+ *
108
+ * @__NO_SIDE_EFFECTS__
109
+ */
110
+ export declare function cliFirestoreBinding<C extends object>(input: CliFirestoreBindingInput<C>): CliFirestoreBinding<C>;
111
+ /**
112
+ * The default `Y` for {@link CliFirestoreModels}: a registry admitting ANY `FirestoreModelType`, with
113
+ * every model erased to `unknown`.
114
+ *
115
+ * Chosen so the defaulted generic form reproduces the pre-`Y` interface member-for-member — which is
116
+ * why `CliContext.getFirestoreModels?: () => Promise<CliFirestoreModels>` needs no change, and why
117
+ * every string-dispatched caller inside `dbx-cli` still compiles.
118
+ */
119
+ export type CliErasedFirebaseModelsService = FirebaseModelsService<Record<FirestoreModelType, FirebaseModelServiceGetter<any, unknown>>, any>;
120
+ /**
121
+ * Resolves the app's REAL `T` / `D` for one registered model type out of its `<app>FirebaseModelServices`.
122
+ *
123
+ * Deliberately only the loader half — {@link CliFirestoreModelService} explains why the permission
124
+ * half (`roleMapForModel`, `requireRole`, `use`) is excluded: `dbx-cli` authorizes through
125
+ * `firestore.rules`, not the app's role map, and the `{ app: collections }` context it hands the
126
+ * service carries no `auth` for a role map to read.
127
+ *
128
+ * @template Y - The app's `<app>FirebaseModelServices` type.
129
+ * @template K - The registered model type to resolve.
130
+ */
131
+ export type CliFirestoreModelServiceForType<Y extends FirebaseModelsService<any, any>, K extends FirebaseModelsServiceTypes<Y>> = Y extends FirebaseModelsService<infer X, infer C> ? (K extends keyof X ? (X[K] extends FirebaseModelServiceGetter<C, infer T, infer D, any> ? InContextFirebaseModelLoader<T, D> & InContextFirebaseModelCollectionLoader<T, D> : never) : never) : never;
132
+ /**
133
+ * The per-invocation, session-bound view of an app's models over a direct Firestore connection.
134
+ *
135
+ * @template C - The app's collections type, e.g. `DemoFirestoreCollections`.
136
+ * @template Y - The app's `<app>FirebaseModelServices` type. Defaults to
137
+ * {@link CliErasedFirebaseModelsService}, which reproduces the erased pre-`Y` interface exactly.
138
+ */
139
+ export interface CliFirestoreModels<C extends object = object, Y extends FirebaseModelsService<any, any> = CliErasedFirebaseModelsService> {
140
+ readonly session: CliFirestoreSessionContext;
141
+ /**
142
+ * The app's collections object, built against the session's `FirestoreContext`.
143
+ */
144
+ readonly collections: C;
145
+ /**
146
+ * The binding the app supplied, for consumers that need `collectionForModel`.
147
+ *
148
+ * Typed `CliFirestoreBinding<C>`, NOT `CliFirestoreBinding<C, Y>` — `Y` must never reach the
149
+ * binding, or the binding stops being assignable to `CliFirestoreBinding<object>`. See the
150
+ * variance note on {@link CliFirestoreBinding}.
151
+ */
152
+ readonly binding: CliFirestoreBinding<C>;
153
+ /**
154
+ * The app's `<app>FirebaseModelServices`, at its real type.
155
+ *
156
+ * Runtime-identical to `binding.models` — the same object — but honestly typed, so callers that
157
+ * hold a typed `CliFirestoreModels` can reach the full service (permissions included) when they
158
+ * genuinely want it, rather than the CLI-erased slice {@link serviceFor} hands back.
159
+ */
160
+ readonly models: Y;
161
+ readonly allTypes: () => FirebaseModelsServiceTypes<Y>[];
162
+ /**
163
+ * Resolves the model service for `modelType`, validating against {@link allTypes} FIRST.
164
+ *
165
+ * The validation is load-bearing, not defensive: `firebaseModelsService` indexes its factory map
166
+ * and calls the result immediately, so an unregistered type surfaces as a bare `TypeError`
167
+ * instead of a `CliError` the CLI can render.
168
+ *
169
+ * An OVERLOAD PAIR, specific-first, and the order is what keeps this non-breaking. A literal
170
+ * (`serviceFor('guestbook')`) satisfies `K extends FirebaseModelsServiceTypes<Y>` and gets the app's
171
+ * real `GuestbookDocument`; a runtime `string` off argv does not, and falls through to the erased
172
+ * signature — which is why every string-dispatched caller in `dbx-cli` compiles untouched. Under
173
+ * the default erased `Y`, `FirebaseModelsServiceTypes<Y>` widens to `FirestoreModelType` and
174
+ * signature 1 collapses into signature 2.
175
+ */
176
+ readonly serviceFor: {
177
+ <K extends FirebaseModelsServiceTypes<Y>>(modelType: K): CliFirestoreModelServiceForType<Y, K>;
178
+ (modelType: FirestoreModelType): CliFirestoreModelService;
179
+ };
180
+ /**
181
+ * Resolves a SHORT COLLECTION NAME (`gb`, `gbe`) to the `modelType` the app registered its service
182
+ * under (`guestbook`, `guestbookEntry`).
183
+ *
184
+ * The query catalog records the collection name, not the model type — that is what
185
+ * `firestore.indexes.json` keys on and what `CliModelManifestEntry.collectionPrefix` joins to — but
186
+ * `<app>FirebaseModelServices` is keyed by model type. The join is derived from the registered
187
+ * collections' own `modelIdentity` rather than from a manifest, so it works for every app with no
188
+ * extra wiring.
189
+ *
190
+ * Memoized: resolving walks `allTypes()` building each service's collection until it matches.
191
+ */
192
+ readonly modelTypeForCollection: (collectionName: string) => FirebaseModelsServiceTypes<Y>;
193
+ }
194
+ /**
195
+ * Resolves the direct-Firestore model view for the current invocation, opening the session on
196
+ * first use.
197
+ *
198
+ * @param context - The live CLI context.
199
+ * @returns The session-bound {@link CliFirestoreModels}.
200
+ * @throws {CliError} When the CLI was not configured with a `firestore` binding, or the session cannot be opened.
201
+ */
202
+ export declare function requireCliFirestoreModels(context: CliContext): Promise<CliFirestoreModels>;
203
+ /**
204
+ * Input for {@link createCliFirestoreModels}.
205
+ */
206
+ export interface CreateCliFirestoreModelsInput<C extends object = object> {
207
+ readonly binding: CliFirestoreBinding<C>;
208
+ readonly session: CliFirestoreSessionContext;
209
+ }
210
+ /**
211
+ * Binds an app's {@link CliFirestoreBinding} to an open session.
212
+ *
213
+ * `Y` is inferred from the caller's annotation rather than from `input` — the binding erased its
214
+ * models to {@link CliFirestoreModelsService} on the way in (it must, for variance), so there is
215
+ * nothing left here to infer it from. {@link cliFirestoreAccessorFactory} is what re-attaches the
216
+ * app's real `Y`, having captured it at the app site.
217
+ *
218
+ * @param input - The binding and the open session.
219
+ * @returns The {@link CliFirestoreModels} view.
220
+ *
221
+ * @__NO_SIDE_EFFECTS__
222
+ */
223
+ export declare function createCliFirestoreModels<C extends object = object, Y extends FirebaseModelsService<any, any> = CliErasedFirebaseModelsService>(input: CreateCliFirestoreModelsInput<C>): CliFirestoreModels<C, Y>;
@@ -0,0 +1,34 @@
1
+ import { type CliFirestoreQueryManifestEntry } from '../manifest/types';
2
+ /**
3
+ * Input for {@link resolveCliFirestoreQueryArgs}.
4
+ */
5
+ export interface ResolveCliFirestoreQueryArgsInput {
6
+ readonly entry: CliFirestoreQueryManifestEntry;
7
+ /**
8
+ * The raw `--params` string, if supplied.
9
+ */
10
+ readonly params?: string;
11
+ /**
12
+ * When true, no date coercion is applied at any level.
13
+ */
14
+ readonly rawParams?: boolean;
15
+ }
16
+ /**
17
+ * Resolves the positional argument list to call a catalog entry's factory with.
18
+ *
19
+ * The contract is POSITIONAL-first, because the single-params-object convention is not universal:
20
+ * the workspace's `*.query.ts` files include zero-arg, single-scalar, two-positional,
21
+ * defaulted-positional, `Maybe<Date>` and `ArrayOrValue<T>` shapes.
22
+ *
23
+ * 1. **omitted** → call with no args; valid only when every param is optional.
24
+ * 2. **JSON array** → spread positionally. Works for every shape.
25
+ * 3. **JSON object** → when the factory takes exactly one param and the key set is not exactly that
26
+ * param's name, the whole object is passed as arg 0 (the single-params-object ergonomic path).
27
+ * Otherwise keys are mapped by param name into positional order.
28
+ * 4. **unparseable** → a `CliError` quoting the parse error and the received string.
29
+ *
30
+ * @param input - The catalog entry and the raw `--params` string.
31
+ * @returns The positional arguments to spread into the factory.
32
+ * @throws {CliError} On any arity, naming, JSON, or date-coercion failure.
33
+ */
34
+ export declare function resolveCliFirestoreQueryArgs(input: ResolveCliFirestoreQueryArgsInput): unknown[];