@dereekb/dbx-cli 13.37.0 → 13.39.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/package.json +8 -8
- package/firebase-api-manifest/main.js +191 -166
- package/firebase-api-manifest/package.json +3 -3
- package/firestore-query-manifest/main.js +2063 -0
- package/firestore-query-manifest/package.json +13 -0
- package/firestore-rules/src/firestore-rules-scan.d.ts +89 -0
- package/firestore-rules/src/index.d.ts +1 -0
- package/generate-firestore-indexes/main.js +2 -2
- package/generate-firestore-indexes/package.json +2 -2
- package/generate-mcp-manifest/main.js +1 -0
- package/generate-mcp-manifest/package.json +3 -3
- package/generate-route-manifest/package.json +2 -2
- package/index.esm.js +12615 -6863
- package/lint-cache/package.json +2 -2
- package/manifest-extract/index.esm.js +4 -1
- package/manifest-extract/package.json +7 -7
- package/manifest-extract/src/lib/types.d.ts +6 -0
- package/model-test/LICENSE +21 -0
- package/model-test/index.d.ts +1 -0
- package/model-test/index.esm.js +5984 -0
- package/model-test/package.json +21 -0
- package/model-test/src/index.d.ts +41 -0
- package/model-test/src/lib/fixture/archetype.d.ts +33 -0
- package/model-test/src/lib/fixture/extract.d.ts +37 -0
- package/model-test/src/lib/fixture/format.json.d.ts +24 -0
- package/model-test/src/lib/fixture/format.markdown.d.ts +23 -0
- package/model-test/src/lib/fixture/forward.d.ts +48 -0
- package/model-test/src/lib/fixture/framework-fixtures.d.ts +70 -0
- package/model-test/src/lib/fixture/inspect.d.ts +22 -0
- package/model-test/src/lib/fixture/scaffold.d.ts +74 -0
- package/model-test/src/lib/fixture/types.d.ts +175 -0
- package/model-test/src/lib/test/discover.d.ts +81 -0
- package/model-test/src/lib/test/extract.d.ts +49 -0
- package/model-test/src/lib/test/format.hotspots.d.ts +23 -0
- package/model-test/src/lib/test/format.json.d.ts +25 -0
- package/model-test/src/lib/test/format.list-app.d.ts +30 -0
- package/model-test/src/lib/test/format.markdown.d.ts +25 -0
- package/model-test/src/lib/test/hotspots.d.ts +98 -0
- package/model-test/src/lib/test/inspect.d.ts +32 -0
- package/model-test/src/lib/test/search.d.ts +34 -0
- package/model-test/src/lib/test/types.d.ts +179 -0
- package/package.json +23 -22
- package/route/package.json +12 -12
- package/src/lib/api/firestore-session.client.d.ts +54 -0
- package/src/lib/api/get-args.helper.d.ts +4 -1
- package/src/lib/api/get-many.command.d.ts +8 -3
- package/src/lib/api/get.command.d.ts +13 -5
- package/src/lib/api/index.d.ts +1 -0
- package/src/lib/config/env.d.ts +106 -0
- package/src/lib/config/firestore-session.cache.d.ts +86 -0
- package/src/lib/config/index.d.ts +1 -0
- package/src/lib/config/paths.d.ts +3 -1
- package/src/lib/context/cli.context.d.ts +87 -3
- package/src/lib/doctor/firestore-session.check.d.ts +113 -0
- package/src/lib/doctor/index.d.ts +1 -0
- package/src/lib/firestore/firestore-get.command.d.ts +28 -0
- package/src/lib/firestore/firestore-queries.command.d.ts +26 -0
- package/src/lib/firestore/firestore-query.command.d.ts +25 -0
- package/src/lib/firestore/firestore.accessor.d.ts +85 -0
- package/src/lib/firestore/firestore.collection.d.ts +44 -0
- package/src/lib/firestore/firestore.error.d.ts +14 -0
- package/src/lib/firestore/firestore.models.d.ts +223 -0
- package/src/lib/firestore/firestore.query-params.d.ts +34 -0
- package/src/lib/firestore/firestore.query.d.ts +79 -0
- package/src/lib/firestore/firestore.read.d.ts +180 -0
- package/src/lib/firestore/firestore.session.d.ts +97 -0
- package/src/lib/firestore/index.d.ts +13 -0
- package/src/lib/firestore/query-info-utils.d.ts +48 -0
- package/src/lib/firestore/query-registry.d.ts +32 -0
- package/src/lib/index.d.ts +3 -0
- package/src/lib/manifest/types.d.ts +117 -0
- package/src/lib/mcp-scan/manifest/dbx-docs-ui-examples-schema.d.ts +2 -2
- package/src/lib/mcp-scan/manifest/model-snapshot-fields-schema.d.ts +2 -2
- package/src/lib/mcp-scan/manifest/pipes-schema.d.ts +2 -2
- package/src/lib/mcp-scan/manifest/ui-components-schema.d.ts +2 -2
- package/src/lib/mcp-scan/manifest/utils-schema.d.ts +2 -2
- package/src/lib/mcp-scan/scan/dbx-docs-ui-examples-extract.d.ts +1 -1
- package/src/lib/mcp-scan/scan/extract-models/types.d.ts +5 -0
- package/src/lib/mcp-scan/scan/ui-components-extract.d.ts +1 -1
- package/src/lib/middleware/auth.middleware.d.ts +6 -0
- package/src/lib/runner/run.d.ts +28 -1
- package/src/lib/scan-helpers/emit-generated-ts.d.ts +76 -0
- package/src/lib/scan-helpers/exported-from-package.d.ts +29 -0
- package/src/lib/util/index.d.ts +1 -0
- package/src/lib/util/output.d.ts +10 -0
- package/src/lib/util/stdin.d.ts +39 -2
- package/src/lib/util/table.d.ts +40 -0
- package/test/index.esm.js +4 -2
- package/test/package.json +14 -14
- package/test/src/lib/cli-test.d.ts +12 -2
- package/validate/LICENSE +21 -0
- package/validate/index.js +16641 -0
- package/validate/package.json +11 -0
- package/eslint/index.cjs.default.js +0 -1
- package/eslint/index.cjs.js +0 -1112
- package/eslint/index.cjs.mjs +0 -2
- package/index.cjs.js +0 -59813
- package/manifest-extract/index.cjs.default.js +0 -1
- package/manifest-extract/index.cjs.js +0 -1592
- package/manifest-extract/index.cjs.mjs +0 -2
- package/route/index.cjs.default.js +0 -1
- package/route/index.cjs.js +0 -18
- package/route/index.cjs.mjs +0 -2
- package/test/index.cjs.default.js +0 -1
- package/test/index.cjs.js +0 -381
- package/test/index.cjs.mjs +0 -2
|
@@ -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[];
|
|
@@ -0,0 +1,79 @@
|
|
|
1
|
+
import { type FirestoreModelKey, type FirestoreQueryConstraint } from '@dereekb/firebase';
|
|
2
|
+
import { type CliFirestoreQueryManifestEntry } from '../manifest/types';
|
|
3
|
+
import { type CliFirestoreModels } from './firestore.models';
|
|
4
|
+
/**
|
|
5
|
+
* One row returned by {@link runCliFirestoreQuery}.
|
|
6
|
+
*/
|
|
7
|
+
export interface CliFirestoreQueryRow {
|
|
8
|
+
readonly key: FirestoreModelKey;
|
|
9
|
+
readonly id: string;
|
|
10
|
+
readonly data: unknown;
|
|
11
|
+
}
|
|
12
|
+
/**
|
|
13
|
+
* The result envelope of a `firestore-query` run.
|
|
14
|
+
*/
|
|
15
|
+
export interface CliFirestoreQueryResult {
|
|
16
|
+
readonly slug: string;
|
|
17
|
+
readonly model: string;
|
|
18
|
+
readonly collection: string;
|
|
19
|
+
readonly scope: string;
|
|
20
|
+
readonly parent?: FirestoreModelKey;
|
|
21
|
+
/**
|
|
22
|
+
* Present unless `--count` was passed.
|
|
23
|
+
*/
|
|
24
|
+
readonly rows?: readonly CliFirestoreQueryRow[];
|
|
25
|
+
/**
|
|
26
|
+
* Row count — the length of {@link rows}, or the `countDocs()` result under `--count`.
|
|
27
|
+
*/
|
|
28
|
+
readonly count: number;
|
|
29
|
+
readonly source: 'firestore';
|
|
30
|
+
}
|
|
31
|
+
/**
|
|
32
|
+
* Input for {@link runCliFirestoreQuery}.
|
|
33
|
+
*/
|
|
34
|
+
export interface RunCliFirestoreQueryInput {
|
|
35
|
+
readonly models: CliFirestoreModels;
|
|
36
|
+
readonly entry: CliFirestoreQueryManifestEntry;
|
|
37
|
+
readonly params?: string;
|
|
38
|
+
readonly rawParams?: boolean;
|
|
39
|
+
readonly parent?: FirestoreModelKey;
|
|
40
|
+
readonly limit?: number;
|
|
41
|
+
/**
|
|
42
|
+
* Return only the count, with no rows.
|
|
43
|
+
*/
|
|
44
|
+
readonly count?: boolean;
|
|
45
|
+
}
|
|
46
|
+
/**
|
|
47
|
+
* Resolves the constraint list a catalog entry produces for the supplied `--params`.
|
|
48
|
+
*
|
|
49
|
+
* Split out from {@link runCliFirestoreQuery} so the constraints can be inspected (or executed
|
|
50
|
+
* against a different sink, e.g. a future `--watch`) without running the query.
|
|
51
|
+
*
|
|
52
|
+
* @param input - The constraint inputs.
|
|
53
|
+
* @param input.entry - The catalog entry whose factory is called.
|
|
54
|
+
* @param input.params - The raw `--params` string, if supplied.
|
|
55
|
+
* @param input.rawParams - When true, disables date coercion on the params.
|
|
56
|
+
* @param input.limit - Replaces the factory's own `limit()` when supplied.
|
|
57
|
+
* @returns The resolved constraints.
|
|
58
|
+
* @throws {CliError} When the entry is not invocable, or the params do not fit its signature.
|
|
59
|
+
*/
|
|
60
|
+
export declare function cliFirestoreQueryConstraints(input: {
|
|
61
|
+
readonly entry: CliFirestoreQueryManifestEntry;
|
|
62
|
+
readonly params?: string;
|
|
63
|
+
readonly rawParams?: boolean;
|
|
64
|
+
readonly limit?: number;
|
|
65
|
+
}): FirestoreQueryConstraint[];
|
|
66
|
+
/**
|
|
67
|
+
* Executes a catalog entry against Firestore over the direct session.
|
|
68
|
+
*
|
|
69
|
+
* Rows come from `getDocSnapshotDataPairs()` + the per-document converter, which costs ONE read per
|
|
70
|
+
* row. `getDocs()` would cost two: `queryLike` is converter-less on both root collections and
|
|
71
|
+
* groups, so it re-loads every matched document from its ref purely to apply the converter. Going
|
|
72
|
+
* through `pair.document.converter` (rather than `collection.queryLike.withConverter(...)`) also
|
|
73
|
+
* honours per-ref converters, such as a stored-data map keyed by document id.
|
|
74
|
+
*
|
|
75
|
+
* @param input - The models view, the entry, and the run options.
|
|
76
|
+
* @returns The query result envelope.
|
|
77
|
+
* @throws {CliError} On an argument, scoping, or invocability failure. Firestore's own errors (rules, missing index) surface via `cliFirestoreErrorMapper`.
|
|
78
|
+
*/
|
|
79
|
+
export declare function runCliFirestoreQuery(input: RunCliFirestoreQueryInput): Promise<CliFirestoreQueryResult>;
|