@dereekb/dbx-cli 13.11.18 → 13.12.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/firebase-api-manifest/main.js +70 -9
- package/firebase-api-manifest/package.json +1 -1
- package/firestore-indexes/src/firestore-indexes-generate.d.ts +125 -0
- package/firestore-indexes/src/firestore-model-identity-resolver.d.ts +93 -0
- package/firestore-indexes/src/firestore-query-helpers.d.ts +108 -0
- package/firestore-indexes/src/generate-firestore-indexes-cli.d.ts +94 -0
- package/firestore-indexes/src/index.d.ts +7 -0
- package/firestore-indexes/src/model-firebase-index-analyze.d.ts +68 -0
- package/firestore-indexes/src/model-firebase-index-build-manifest.d.ts +123 -0
- package/firestore-indexes/src/model-firebase-index-extract.d.ts +246 -0
- package/firestore-indexes/src/model-firebase-index-runtime.d.ts +126 -0
- package/firestore-indexes/src/model-firebase-index-scan-config-schema.d.ts +58 -0
- package/firestore-indexes/src/model-firebase-index-schema.d.ts +366 -0
- package/generate-firestore-indexes/main.js +1 -1
- package/generate-firestore-indexes/package.json +1 -1
- package/generate-mcp-manifest/src/generate-mcp-manifest/main.d.ts +26 -0
- package/generate-mcp-manifest/src/generate-mcp-manifest/render.d.ts +38 -0
- package/generated/firebase-models.generated.d.ts +3 -0
- package/index.cjs.js +45234 -640
- package/index.esm.js +44941 -643
- package/lint-cache/main.js +19 -19
- package/lint-cache/package.json +2 -2
- package/manifest-extract/index.cjs.js +169 -4
- package/manifest-extract/index.esm.js +169 -4
- package/manifest-extract/package.json +1 -1
- package/manifest-extract/src/lib/types.d.ts +26 -1
- package/package.json +14 -10
- package/src/lib/index.d.ts +3 -0
- package/src/lib/manifest/types.d.ts +155 -0
- package/src/lib/mcp-scan/config/config-schema.d.ts +226 -0
- package/src/lib/mcp-scan/config/load-config.d.ts +63 -0
- package/src/lib/mcp-scan/index.d.ts +16 -0
- package/src/lib/mcp-scan/manifest/actions-loader.d.ts +49 -0
- package/src/lib/mcp-scan/manifest/actions-schema.d.ts +328 -0
- package/src/lib/mcp-scan/manifest/core-topics.d.ts +38 -0
- package/src/lib/mcp-scan/manifest/css-utilities-loader.d.ts +55 -0
- package/src/lib/mcp-scan/manifest/css-utilities-schema.d.ts +168 -0
- package/src/lib/mcp-scan/manifest/dbx-docs-ui-examples-loader.d.ts +33 -0
- package/src/lib/mcp-scan/manifest/dbx-docs-ui-examples-schema.d.ts +133 -0
- package/src/lib/mcp-scan/manifest/filters-loader.d.ts +61 -0
- package/src/lib/mcp-scan/manifest/filters-schema.d.ts +190 -0
- package/src/lib/mcp-scan/manifest/forge-fields-loader.d.ts +53 -0
- package/src/lib/mcp-scan/manifest/forge-fields-schema.d.ts +170 -0
- package/src/lib/mcp-scan/manifest/index.d.ts +43 -0
- package/src/lib/mcp-scan/manifest/load-actions-registry.d.ts +38 -0
- package/src/lib/mcp-scan/manifest/load-auth-registry.d.ts +82 -0
- package/src/lib/mcp-scan/manifest/load-css-utilities-registry.d.ts +67 -0
- package/src/lib/mcp-scan/manifest/load-dbx-docs-ui-examples-registry.d.ts +45 -0
- package/src/lib/mcp-scan/manifest/load-filters-registry.d.ts +69 -0
- package/src/lib/mcp-scan/manifest/load-forge-fields-registry.d.ts +70 -0
- package/src/lib/mcp-scan/manifest/load-model-firebase-index-registry.d.ts +61 -0
- package/src/lib/mcp-scan/manifest/load-model-snapshot-fields-registry.d.ts +74 -0
- package/src/lib/mcp-scan/manifest/load-pipes-registry.d.ts +69 -0
- package/src/lib/mcp-scan/manifest/load-registry.d.ts +76 -0
- package/src/lib/mcp-scan/manifest/load-tokens-registry.d.ts +69 -0
- package/src/lib/mcp-scan/manifest/load-ui-components-registry.d.ts +70 -0
- package/src/lib/mcp-scan/manifest/load-utils-registry.d.ts +73 -0
- package/src/lib/mcp-scan/manifest/loader.d.ts +120 -0
- package/src/lib/mcp-scan/manifest/manifest-loader-base.d.ts +130 -0
- package/src/lib/mcp-scan/manifest/model-firebase-index-loader.d.ts +53 -0
- package/src/lib/mcp-scan/manifest/model-snapshot-fields-loader.d.ts +54 -0
- package/src/lib/mcp-scan/manifest/model-snapshot-fields-schema.d.ts +127 -0
- package/src/lib/mcp-scan/manifest/pipes-loader.d.ts +54 -0
- package/src/lib/mcp-scan/manifest/pipes-schema.d.ts +125 -0
- package/src/lib/mcp-scan/manifest/semantic-types-schema.d.ts +108 -0
- package/src/lib/mcp-scan/manifest/tokens-loader.d.ts +55 -0
- package/src/lib/mcp-scan/manifest/tokens-schema.d.ts +116 -0
- package/src/lib/mcp-scan/manifest/ui-components-loader.d.ts +54 -0
- package/src/lib/mcp-scan/manifest/ui-components-schema.d.ts +149 -0
- package/src/lib/mcp-scan/manifest/utils-loader.d.ts +54 -0
- package/src/lib/mcp-scan/manifest/utils-schema.d.ts +120 -0
- package/src/lib/mcp-scan/registry/actions-runtime.d.ts +173 -0
- package/src/lib/mcp-scan/registry/archetypes.d.ts +235 -0
- package/src/lib/mcp-scan/registry/auth-builtin.d.ts +59 -0
- package/src/lib/mcp-scan/registry/auth-runtime.d.ts +343 -0
- package/src/lib/mcp-scan/registry/css-utilities-runtime.d.ts +133 -0
- package/src/lib/mcp-scan/registry/dbx-docs-ui-examples-runtime.d.ts +58 -0
- package/src/lib/mcp-scan/registry/downstream-models-runtime.d.ts +93 -0
- package/src/lib/mcp-scan/registry/filters-runtime.d.ts +128 -0
- package/src/lib/mcp-scan/registry/firebase-models.d.ts +387 -0
- package/src/lib/mcp-scan/registry/forge-fields.d.ts +101 -0
- package/src/lib/mcp-scan/registry/form-fields.d.ts +203 -0
- package/src/lib/mcp-scan/registry/index.d.ts +165 -0
- package/src/lib/mcp-scan/registry/model-snapshot-fields-runtime.d.ts +138 -0
- package/src/lib/mcp-scan/registry/pipes-runtime.d.ts +136 -0
- package/src/lib/mcp-scan/registry/reserved-model-folders.d.ts +29 -0
- package/src/lib/mcp-scan/registry/semantic-types.d.ts +81 -0
- package/src/lib/mcp-scan/registry/tokens-runtime.d.ts +96 -0
- package/src/lib/mcp-scan/registry/ui-components-runtime.d.ts +90 -0
- package/src/lib/mcp-scan/registry/utils-runtime.d.ts +136 -0
- package/src/lib/mcp-scan/scan/_jsdoc-tagged-export/extract-base.d.ts +245 -0
- package/src/lib/mcp-scan/scan/actions-build-manifest.d.ts +58 -0
- package/src/lib/mcp-scan/scan/actions-cli.d.ts +38 -0
- package/src/lib/mcp-scan/scan/actions-extract.d.ts +99 -0
- package/src/lib/mcp-scan/scan/actions-scan-config-schema.d.ts +42 -0
- package/src/lib/mcp-scan/scan/auth-extract.d.ts +120 -0
- package/src/lib/mcp-scan/scan/build-manifest.d.ts +76 -0
- package/src/lib/mcp-scan/scan/cli.d.ts +60 -0
- package/src/lib/mcp-scan/scan/css-utilities-build-manifest.d.ts +76 -0
- package/src/lib/mcp-scan/scan/css-utilities-cli.d.ts +36 -0
- package/src/lib/mcp-scan/scan/css-utilities-extract.d.ts +187 -0
- package/src/lib/mcp-scan/scan/css-utilities-scan-config-schema.d.ts +57 -0
- package/src/lib/mcp-scan/scan/dbx-docs-ui-examples-build-manifest.d.ts +68 -0
- package/src/lib/mcp-scan/scan/dbx-docs-ui-examples-cli.d.ts +20 -0
- package/src/lib/mcp-scan/scan/dbx-docs-ui-examples-extract.d.ts +160 -0
- package/src/lib/mcp-scan/scan/dbx-docs-ui-examples-scan-config-schema.d.ts +56 -0
- package/src/lib/mcp-scan/scan/discover-downstream-packages.d.ts +76 -0
- package/src/lib/mcp-scan/scan/discover-firebase-packages.d.ts +58 -0
- package/src/lib/mcp-scan/scan/extract-models/assemble.d.ts +105 -0
- package/src/lib/mcp-scan/scan/extract-models/collect-inherited.d.ts +22 -0
- package/src/lib/mcp-scan/scan/extract-models/find-converters.d.ts +19 -0
- package/src/lib/mcp-scan/scan/extract-models/find-enums.d.ts +19 -0
- package/src/lib/mcp-scan/scan/extract-models/find-identities.d.ts +25 -0
- package/src/lib/mcp-scan/scan/extract-models/find-interfaces.d.ts +31 -0
- package/src/lib/mcp-scan/scan/extract-models/find-model-groups.d.ts +21 -0
- package/src/lib/mcp-scan/scan/extract-models/find-service-factories.d.ts +19 -0
- package/src/lib/mcp-scan/scan/extract-models/find-sub-object-consts.d.ts +20 -0
- package/src/lib/mcp-scan/scan/extract-models/index.d.ts +74 -0
- package/src/lib/mcp-scan/scan/extract-models/infer-collection-kind.d.ts +22 -0
- package/src/lib/mcp-scan/scan/extract-models/service-factory-constants.d.ts +6 -0
- package/src/lib/mcp-scan/scan/extract-models/types.d.ts +171 -0
- package/src/lib/mcp-scan/scan/extract.d.ts +82 -0
- package/src/lib/mcp-scan/scan/filters-build-manifest.d.ts +78 -0
- package/src/lib/mcp-scan/scan/filters-cli.d.ts +37 -0
- package/src/lib/mcp-scan/scan/filters-extract.d.ts +101 -0
- package/src/lib/mcp-scan/scan/filters-scan-config-schema.d.ts +56 -0
- package/src/lib/mcp-scan/scan/forge-fields-build-manifest.d.ts +78 -0
- package/src/lib/mcp-scan/scan/forge-fields-cli.d.ts +37 -0
- package/src/lib/mcp-scan/scan/forge-fields-extract.d.ts +165 -0
- package/src/lib/mcp-scan/scan/forge-fields-scan-config-schema.d.ts +61 -0
- package/src/lib/mcp-scan/scan/index.d.ts +60 -0
- package/src/lib/mcp-scan/scan/model-firebase-index-cli.d.ts +22 -0
- package/src/lib/mcp-scan/scan/model-firebase-index-dispatcher-credit.d.ts +47 -0
- package/src/lib/mcp-scan/scan/model-firebase-index-reference-scan.d.ts +100 -0
- package/src/lib/mcp-scan/scan/model-snapshot-fields-build-manifest.d.ts +79 -0
- package/src/lib/mcp-scan/scan/model-snapshot-fields-cli.d.ts +37 -0
- package/src/lib/mcp-scan/scan/model-snapshot-fields-extract.d.ts +115 -0
- package/src/lib/mcp-scan/scan/model-snapshot-fields-scan-config-schema.d.ts +59 -0
- package/src/lib/mcp-scan/scan/pipes-build-manifest.d.ts +78 -0
- package/src/lib/mcp-scan/scan/pipes-cli.d.ts +37 -0
- package/src/lib/mcp-scan/scan/pipes-extract.d.ts +90 -0
- package/src/lib/mcp-scan/scan/pipes-scan-config-schema.d.ts +56 -0
- package/src/lib/mcp-scan/scan/scan-angular-io.d.ts +89 -0
- package/src/lib/mcp-scan/scan/scan-cli-base.d.ts +162 -0
- package/src/lib/mcp-scan/scan/scan-config-schema.d.ts +44 -0
- package/src/lib/mcp-scan/scan/ui-components-build-manifest.d.ts +78 -0
- package/src/lib/mcp-scan/scan/ui-components-cli.d.ts +37 -0
- package/src/lib/mcp-scan/scan/ui-components-extract.d.ts +124 -0
- package/src/lib/mcp-scan/scan/ui-components-scan-config-schema.d.ts +62 -0
- package/src/lib/mcp-scan/scan/utils-build-manifest.d.ts +78 -0
- package/src/lib/mcp-scan/scan/utils-cli.d.ts +37 -0
- package/src/lib/mcp-scan/scan/utils-extract.d.ts +103 -0
- package/src/lib/mcp-scan/scan/utils-scan-config-schema.d.ts +57 -0
- package/test/package.json +9 -9
- package/index.cjs.default.js +0 -1
- package/index.cjs.mjs +0 -2
|
@@ -161,7 +161,7 @@ import { join as join2 } from "node:path";
|
|
|
161
161
|
|
|
162
162
|
// packages/dbx-cli/manifest-extract/src/lib/extract-crud.ts
|
|
163
163
|
import { Node as Node2, Project as Project2 } from "ts-morph";
|
|
164
|
-
var SUPPORTED_VERBS = /* @__PURE__ */ new Set(["create", "read", "update", "delete", "query"]);
|
|
164
|
+
var SUPPORTED_VERBS = /* @__PURE__ */ new Set(["create", "read", "update", "delete", "query", "invoke"]);
|
|
165
165
|
function extractCrudEntries(source) {
|
|
166
166
|
const project = new Project2({ useInMemoryFileSystem: true, skipAddingFilesFromTsConfig: true });
|
|
167
167
|
const sourceFile = project.createSourceFile(source.name, source.text, { overwrite: true });
|
|
@@ -426,6 +426,9 @@ function readJsDocSummary(node) {
|
|
|
426
426
|
|
|
427
427
|
// packages/dbx-cli/manifest-extract/src/lib/extract-models.ts
|
|
428
428
|
import { Node as Node3, Project as Project3 } from "ts-morph";
|
|
429
|
+
var READ_LEVEL_VALUES = /* @__PURE__ */ new Set(["system", "owner", "admin-only", "permissions"]);
|
|
430
|
+
var SERVICE_FACTORY_TAG = "dbxModelServiceFactory";
|
|
431
|
+
var MODEL_TYPE_VALUE_PATTERN = /^[a-z][A-Za-z0-9_$]*$/;
|
|
429
432
|
var PASSTHROUGH_TYPE_WRAPPERS = /* @__PURE__ */ new Set(["Partial", "Required", "Readonly", "NonNullable", "MaybeMap", "Pick", "Omit"]);
|
|
430
433
|
var IDENTITY_FN = "firestoreModelIdentity";
|
|
431
434
|
var CONVERTER_FN_NAMES = ["snapshotConverterFunctions", "firestoreSubObject", "firestoreObjectArray"];
|
|
@@ -442,7 +445,8 @@ function extractModelsFromSource(input) {
|
|
|
442
445
|
const converters = readConverters(sourceFile);
|
|
443
446
|
const enums = readEnums(sourceFile);
|
|
444
447
|
const modelGroups = readModelGroups(sourceFile);
|
|
445
|
-
|
|
448
|
+
const serviceFactories = readServiceFactories(sourceFile);
|
|
449
|
+
return { identities, interfaces, converters, enums, modelGroups, serviceFactories };
|
|
446
450
|
}
|
|
447
451
|
function readIdentities(sourceFile) {
|
|
448
452
|
const out = [];
|
|
@@ -497,6 +501,7 @@ function readInterfaces(sourceFile) {
|
|
|
497
501
|
function buildInterface(decl) {
|
|
498
502
|
const jsDocs = decl.getJsDocs();
|
|
499
503
|
const hasDbxModelTag = jsDocsHaveTag(jsDocs, "dbxModel");
|
|
504
|
+
const dbxModelRead = readDbxModelReadTag(jsDocs);
|
|
500
505
|
const extendsNames = decl.getExtends().map(resolveExtendsName);
|
|
501
506
|
const props = [];
|
|
502
507
|
for (const prop of decl.getProperties()) {
|
|
@@ -519,9 +524,54 @@ function buildInterface(decl) {
|
|
|
519
524
|
description: readJsDocDescription(jsDocs),
|
|
520
525
|
hasDbxModelTag,
|
|
521
526
|
extendsNames,
|
|
522
|
-
props
|
|
527
|
+
props,
|
|
528
|
+
...dbxModelRead === void 0 ? {} : { dbxModelRead }
|
|
523
529
|
};
|
|
524
530
|
}
|
|
531
|
+
function readDbxModelReadTag(jsDocs) {
|
|
532
|
+
let result;
|
|
533
|
+
for (const doc of jsDocs) {
|
|
534
|
+
for (const tag of doc.getTags()) {
|
|
535
|
+
if (tag.getTagName() !== "dbxModelRead") continue;
|
|
536
|
+
if (result !== void 0) continue;
|
|
537
|
+
const raw = tag.getCommentText()?.trim();
|
|
538
|
+
if (raw === void 0 || raw.length === 0) continue;
|
|
539
|
+
const firstToken = raw.split(/\s+/)[0];
|
|
540
|
+
if (READ_LEVEL_VALUES.has(firstToken)) {
|
|
541
|
+
result = firstToken;
|
|
542
|
+
}
|
|
543
|
+
}
|
|
544
|
+
}
|
|
545
|
+
return result;
|
|
546
|
+
}
|
|
547
|
+
function readServiceFactories(sourceFile) {
|
|
548
|
+
const out = [];
|
|
549
|
+
for (const statement of sourceFile.getVariableStatements()) {
|
|
550
|
+
if (!statement.isExported()) continue;
|
|
551
|
+
const modelType = readServiceFactoryModelType(statement.getJsDocs());
|
|
552
|
+
if (modelType === void 0) continue;
|
|
553
|
+
for (const decl of statement.getDeclarations()) {
|
|
554
|
+
out.push({ modelType, exportName: decl.getName() });
|
|
555
|
+
}
|
|
556
|
+
}
|
|
557
|
+
return out;
|
|
558
|
+
}
|
|
559
|
+
function readServiceFactoryModelType(jsDocs) {
|
|
560
|
+
let result;
|
|
561
|
+
for (const doc of jsDocs) {
|
|
562
|
+
for (const tag of doc.getTags()) {
|
|
563
|
+
if (tag.getTagName() !== SERVICE_FACTORY_TAG) continue;
|
|
564
|
+
if (result !== void 0) continue;
|
|
565
|
+
const raw = tag.getCommentText()?.trim();
|
|
566
|
+
if (raw === void 0 || raw.length === 0) continue;
|
|
567
|
+
const firstToken = raw.split(/\s+/)[0];
|
|
568
|
+
if (MODEL_TYPE_VALUE_PATTERN.test(firstToken)) {
|
|
569
|
+
result = firstToken;
|
|
570
|
+
}
|
|
571
|
+
}
|
|
572
|
+
}
|
|
573
|
+
return result;
|
|
574
|
+
}
|
|
525
575
|
function resolveExtendsName(expr) {
|
|
526
576
|
const head = expr.getExpression().getText();
|
|
527
577
|
let result = head;
|
|
@@ -850,14 +900,14 @@ function findModelFiles(packageRoot) {
|
|
|
850
900
|
const text = readFileSync4(filePath, "utf8");
|
|
851
901
|
if (!textHasModelMarker(text)) continue;
|
|
852
902
|
const extraction = extractModelsFromSource({ name: filePath, text });
|
|
853
|
-
if (extraction.identities.length === 0 && extraction.modelGroups.length === 0 && extraction.converters.length === 0) continue;
|
|
903
|
+
if (extraction.identities.length === 0 && extraction.modelGroups.length === 0 && extraction.converters.length === 0 && extraction.serviceFactories.length === 0) continue;
|
|
854
904
|
out.push({ filePath, extraction });
|
|
855
905
|
}
|
|
856
906
|
}
|
|
857
907
|
return out;
|
|
858
908
|
}
|
|
859
909
|
function textHasModelMarker(text) {
|
|
860
|
-
return text.includes("firestoreModelIdentity(") || text.includes("@dbxModelGroup") || text.includes("snapshotConverterFunctions") || text.includes("firestoreSubObject") || text.includes("firestoreObjectArray");
|
|
910
|
+
return text.includes("firestoreModelIdentity(") || text.includes("@dbxModelGroup") || text.includes("snapshotConverterFunctions") || text.includes("firestoreSubObject") || text.includes("firestoreObjectArray") || text.includes("@dbxModelServiceFactory");
|
|
861
911
|
}
|
|
862
912
|
function* walkSourceFiles(dir) {
|
|
863
913
|
for (const entry of readdirSync2(dir).sort()) {
|
|
@@ -901,12 +951,18 @@ function buildGlobalRegistries(extractions) {
|
|
|
901
951
|
const converterRegistry = /* @__PURE__ */ new Map();
|
|
902
952
|
const interfaceRegistry = /* @__PURE__ */ new Map();
|
|
903
953
|
const groupByModelName = /* @__PURE__ */ new Map();
|
|
904
|
-
|
|
954
|
+
const serviceFactoryByModelType = /* @__PURE__ */ new Map();
|
|
955
|
+
for (const { extraction, sourceFile } of extractions) {
|
|
905
956
|
registerConverters(extraction.converters, converterRegistry);
|
|
906
957
|
registerInterfaces(extraction.interfaces, interfaceRegistry);
|
|
907
958
|
registerModelGroups(extraction.modelGroups, groupByModelName);
|
|
959
|
+
for (const factory of extraction.serviceFactories) {
|
|
960
|
+
if (!serviceFactoryByModelType.has(factory.modelType)) {
|
|
961
|
+
serviceFactoryByModelType.set(factory.modelType, { exportName: factory.exportName, sourceFile });
|
|
962
|
+
}
|
|
963
|
+
}
|
|
908
964
|
}
|
|
909
|
-
return { converterRegistry, interfaceRegistry, groupByModelName };
|
|
965
|
+
return { converterRegistry, interfaceRegistry, groupByModelName, serviceFactoryByModelType };
|
|
910
966
|
}
|
|
911
967
|
function registerConverters(converters, registry) {
|
|
912
968
|
for (const converter of converters) {
|
|
@@ -957,6 +1013,7 @@ function buildEntryForIdentity(input) {
|
|
|
957
1013
|
visitedConverters: /* @__PURE__ */ new Set()
|
|
958
1014
|
});
|
|
959
1015
|
const modelGroup = registries.groupByModelName.get(modelName);
|
|
1016
|
+
const serviceFactory = registries.serviceFactoryByModelType.get(identity.modelType);
|
|
960
1017
|
result = {
|
|
961
1018
|
modelType: identity.modelType,
|
|
962
1019
|
modelName,
|
|
@@ -967,7 +1024,9 @@ function buildEntryForIdentity(input) {
|
|
|
967
1024
|
...iface.description ? { description: iface.description } : {},
|
|
968
1025
|
sourcePackage: source.sourcePackage,
|
|
969
1026
|
sourceFile: source.sourceFile,
|
|
970
|
-
fields
|
|
1027
|
+
fields,
|
|
1028
|
+
...iface.dbxModelRead ? { read: iface.dbxModelRead } : {},
|
|
1029
|
+
...serviceFactory ? { serviceFactory } : {}
|
|
971
1030
|
};
|
|
972
1031
|
}
|
|
973
1032
|
}
|
|
@@ -1290,7 +1349,9 @@ function renderModelEntry(entry, emitConverters) {
|
|
|
1290
1349
|
entry.description ? `description: ${JSON.stringify(entry.description)}` : void 0,
|
|
1291
1350
|
`sourcePackage: ${JSON.stringify(entry.sourcePackage)}`,
|
|
1292
1351
|
`sourceFile: ${JSON.stringify(entry.sourceFile)}`,
|
|
1293
|
-
`fields: ${renderModelFields(entry.fields, emitConverters)}
|
|
1352
|
+
`fields: ${renderModelFields(entry.fields, emitConverters)}`,
|
|
1353
|
+
entry.read ? `read: ${JSON.stringify(entry.read)}` : void 0,
|
|
1354
|
+
entry.serviceFactory ? `serviceFactory: { exportName: ${JSON.stringify(entry.serviceFactory.exportName)}, sourceFile: ${JSON.stringify(entry.serviceFactory.sourceFile)} }` : void 0
|
|
1294
1355
|
];
|
|
1295
1356
|
return ` { ${fields.filter((v) => Boolean(v)).join(", ")} }`;
|
|
1296
1357
|
}
|
|
@@ -0,0 +1,125 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* `firestore.indexes.json` generator.
|
|
3
|
+
*
|
|
4
|
+
* Consumes the {@link ModelFirebaseIndexRegistry} and produces the
|
|
5
|
+
* canonical `firestore.indexes.json` payload (composites + fieldOverrides)
|
|
6
|
+
* Firebase deploys against. Encodes the live-deploy shape observed in
|
|
7
|
+
* HelloSubs:
|
|
8
|
+
*
|
|
9
|
+
* - Every composite ends with a `__name__` tiebreaker whose direction
|
|
10
|
+
* matches the last orderBy in the entry.
|
|
11
|
+
* - Every composite carries `density: "SPARSE_ALL"`.
|
|
12
|
+
* - Every COLLECTION_GROUP single-field variant in a `fieldOverrides`
|
|
13
|
+
* entry is emitted alongside the standard COLLECTION quartet
|
|
14
|
+
* (ASCENDING/DESCENDING/CONTAINS) so the auto-indexes Firebase would
|
|
15
|
+
* have created at COLLECTION scope are explicitly preserved.
|
|
16
|
+
*
|
|
17
|
+
* Preserves user-authored content that the analyzer can't produce:
|
|
18
|
+
*
|
|
19
|
+
* - `fieldOverrides[]` entries whose `(collection, fieldPath)` no
|
|
20
|
+
* analyzed factory touches (TTL flags, hand-trimmed overrides like
|
|
21
|
+
* `sjs.adat`, vector indexes, etc.).
|
|
22
|
+
* - `indexes[]` entries tied to slugs that carry the
|
|
23
|
+
* `@dbxModelFirebaseIndexManual` JSDoc tag — round-tripped untouched.
|
|
24
|
+
*
|
|
25
|
+
* Returns the new JSON + a structured diff (`added`, `removed`,
|
|
26
|
+
* `unchanged`) so the CLI / MCP tool can render a clear CI report.
|
|
27
|
+
*/
|
|
28
|
+
import type { ModelFirebaseIndexEntryInfo } from './model-firebase-index-runtime.js';
|
|
29
|
+
/**
|
|
30
|
+
* `firestore.indexes.json` top-level shape.
|
|
31
|
+
*/
|
|
32
|
+
export interface FirestoreIndexesJson {
|
|
33
|
+
readonly indexes: readonly FirestoreIndexJsonEntry[];
|
|
34
|
+
readonly fieldOverrides: readonly FirestoreFieldOverrideJsonEntry[];
|
|
35
|
+
}
|
|
36
|
+
/**
|
|
37
|
+
* One `indexes[]` entry. Mirrors the Firestore Admin API schema with the
|
|
38
|
+
* `density` field included (Firebase CLI emits this on every export).
|
|
39
|
+
*/
|
|
40
|
+
export interface FirestoreIndexJsonEntry {
|
|
41
|
+
readonly collectionGroup: string;
|
|
42
|
+
readonly queryScope: 'COLLECTION' | 'COLLECTION_GROUP';
|
|
43
|
+
readonly fields: readonly FirestoreIndexJsonField[];
|
|
44
|
+
readonly density?: 'SPARSE_ALL' | 'SPARSE_ANY' | 'DENSE';
|
|
45
|
+
}
|
|
46
|
+
/**
|
|
47
|
+
* One `fields[]` member of an `indexes[]` entry. Includes the `__name__`
|
|
48
|
+
* tiebreaker the generator appends at emission time.
|
|
49
|
+
*/
|
|
50
|
+
export interface FirestoreIndexJsonField {
|
|
51
|
+
readonly fieldPath: string;
|
|
52
|
+
readonly order?: 'ASCENDING' | 'DESCENDING';
|
|
53
|
+
readonly arrayConfig?: 'CONTAINS';
|
|
54
|
+
}
|
|
55
|
+
/**
|
|
56
|
+
* One `fieldOverrides[]` entry.
|
|
57
|
+
*/
|
|
58
|
+
export interface FirestoreFieldOverrideJsonEntry {
|
|
59
|
+
readonly collectionGroup: string;
|
|
60
|
+
readonly fieldPath: string;
|
|
61
|
+
readonly ttl?: boolean;
|
|
62
|
+
readonly indexes: readonly FirestoreFieldOverrideJsonVariant[];
|
|
63
|
+
}
|
|
64
|
+
/**
|
|
65
|
+
* One variant inside a `fieldOverrides[].indexes` array.
|
|
66
|
+
*/
|
|
67
|
+
export interface FirestoreFieldOverrideJsonVariant {
|
|
68
|
+
readonly queryScope: 'COLLECTION' | 'COLLECTION_GROUP';
|
|
69
|
+
readonly order?: 'ASCENDING' | 'DESCENDING';
|
|
70
|
+
readonly arrayConfig?: 'CONTAINS';
|
|
71
|
+
}
|
|
72
|
+
/**
|
|
73
|
+
* Input to {@link generateFirestoreIndexesJson}.
|
|
74
|
+
*/
|
|
75
|
+
export interface GenerateFirestoreIndexesJsonInput {
|
|
76
|
+
/**
|
|
77
|
+
* Every entry the registry knows about. Entries with `skip = true` or
|
|
78
|
+
* `manual = true` are filtered out by the generator — manual entries
|
|
79
|
+
* are preserved via {@link existingJson}.
|
|
80
|
+
*/
|
|
81
|
+
readonly entries: readonly ModelFirebaseIndexEntryInfo[];
|
|
82
|
+
/**
|
|
83
|
+
* The current on-disk `firestore.indexes.json` if any, used to preserve
|
|
84
|
+
* user-authored content the generator can't reproduce.
|
|
85
|
+
*/
|
|
86
|
+
readonly existingJson?: FirestoreIndexesJson;
|
|
87
|
+
}
|
|
88
|
+
/**
|
|
89
|
+
* Diff shape returned alongside the new JSON. Each list carries the
|
|
90
|
+
* canonical-key form of the index (or override) for inclusion in CI
|
|
91
|
+
* reports.
|
|
92
|
+
*/
|
|
93
|
+
export interface FirestoreIndexesDiff {
|
|
94
|
+
readonly added: readonly string[];
|
|
95
|
+
readonly removed: readonly string[];
|
|
96
|
+
readonly unchanged: readonly string[];
|
|
97
|
+
readonly fieldOverridesAdded: readonly string[];
|
|
98
|
+
readonly fieldOverridesRemoved: readonly string[];
|
|
99
|
+
readonly fieldOverridesUnchanged: readonly string[];
|
|
100
|
+
}
|
|
101
|
+
/**
|
|
102
|
+
* Output of {@link generateFirestoreIndexesJson}.
|
|
103
|
+
*/
|
|
104
|
+
export interface GenerateFirestoreIndexesJsonResult {
|
|
105
|
+
readonly json: FirestoreIndexesJson;
|
|
106
|
+
readonly diff: FirestoreIndexesDiff;
|
|
107
|
+
}
|
|
108
|
+
/**
|
|
109
|
+
* Generates a canonical `firestore.indexes.json` payload from the
|
|
110
|
+
* registry, preserving user-authored content from any prior on-disk
|
|
111
|
+
* version.
|
|
112
|
+
*
|
|
113
|
+
* @param input - The entries and (optionally) the existing JSON to merge against.
|
|
114
|
+
* @returns The new JSON payload plus a diff against the existing version.
|
|
115
|
+
*/
|
|
116
|
+
export declare function generateFirestoreIndexesJson(input: GenerateFirestoreIndexesJsonInput): GenerateFirestoreIndexesJsonResult;
|
|
117
|
+
/**
|
|
118
|
+
* Serializes `firestore.indexes.json` with stable key ordering and a
|
|
119
|
+
* trailing newline so `--check` mode can byte-compare against the
|
|
120
|
+
* committed file without false-positive whitespace diffs.
|
|
121
|
+
*
|
|
122
|
+
* @param json - The indexes payload to serialise.
|
|
123
|
+
* @returns The canonical string form.
|
|
124
|
+
*/
|
|
125
|
+
export declare function serializeFirestoreIndexesJson(json: FirestoreIndexesJson): string;
|
|
@@ -0,0 +1,93 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Build-time resolver from a TS type name (`JobLocationWeek`) to the
|
|
3
|
+
* model's Firestore short collection name (`'jlw'`) and nested flag.
|
|
4
|
+
*
|
|
5
|
+
* The model-firebase-index extractor needs to map the
|
|
6
|
+
* `@dbxModelFirebaseIndexModel <TypeName>` tag value onto the
|
|
7
|
+
* `collectionPrefix` Firestore uses for the model — that's what becomes
|
|
8
|
+
* the `collectionGroup` field in `firestore.indexes.json`. The resolver
|
|
9
|
+
* builds a per-project map by walking the supplied ts-morph project for
|
|
10
|
+
* `firestoreModelIdentity(...)` calls, then exposes lookup-by-type-name
|
|
11
|
+
* (preferred), lookup-by-modelType (the first-string-arg), and
|
|
12
|
+
* lookup-by-identity-const (e.g. `jobLocationWeekIdentity`).
|
|
13
|
+
*
|
|
14
|
+
* For runtime use (the `dbx_model_firebase_index_*` MCP tools) the
|
|
15
|
+
* upstream `FIREBASE_MODELS` registry + downstream catalog provides the
|
|
16
|
+
* same data — see {@link buildIdentityResolverFromRegistries} below.
|
|
17
|
+
*/
|
|
18
|
+
import { type Project } from 'ts-morph';
|
|
19
|
+
/**
|
|
20
|
+
* Resolution result for one model. `collection` is the short collection
|
|
21
|
+
* prefix used by Firestore (and the `collectionGroup` field in
|
|
22
|
+
* `firestore.indexes.json`). `isNested` is `true` when the identity
|
|
23
|
+
* declares a parent — these models default to `COLLECTION_GROUP` scope.
|
|
24
|
+
*/
|
|
25
|
+
export interface ResolvedFirestoreModelIdentity {
|
|
26
|
+
readonly modelType: string;
|
|
27
|
+
readonly collection: string;
|
|
28
|
+
readonly isNested: boolean;
|
|
29
|
+
readonly identityConstName: string;
|
|
30
|
+
}
|
|
31
|
+
/**
|
|
32
|
+
* Build-time identity resolver. Methods accept either the TS type name
|
|
33
|
+
* (`'JobLocationWeek'`), the modelType string (`'jobLocationWeek'`), or
|
|
34
|
+
* the identity-const name (`'jobLocationWeekIdentity'`) — extractors
|
|
35
|
+
* commonly have access to whichever surface lines up with how the tagged
|
|
36
|
+
* factory was written.
|
|
37
|
+
*/
|
|
38
|
+
export interface FirestoreModelIdentityResolver {
|
|
39
|
+
/**
|
|
40
|
+
* Lookup by the TS type / interface name (e.g. `JobLocationWeek`). The
|
|
41
|
+
* resolver tries the literal name, then the camelCase form, then the
|
|
42
|
+
* derived `<lowerCamelCase>Identity` const name.
|
|
43
|
+
*/
|
|
44
|
+
readonly lookupByTypeName: (typeName: string) => ResolvedFirestoreModelIdentity | undefined;
|
|
45
|
+
/**
|
|
46
|
+
* Lookup by the first-arg `modelType` string passed to
|
|
47
|
+
* `firestoreModelIdentity(...)` — exact match.
|
|
48
|
+
*/
|
|
49
|
+
readonly lookupByModelType: (modelType: string) => ResolvedFirestoreModelIdentity | undefined;
|
|
50
|
+
/**
|
|
51
|
+
* Lookup by the identity const name (e.g. `jobLocationWeekIdentity`) —
|
|
52
|
+
* exact match.
|
|
53
|
+
*/
|
|
54
|
+
readonly lookupByIdentityConst: (identityConstName: string) => ResolvedFirestoreModelIdentity | undefined;
|
|
55
|
+
/**
|
|
56
|
+
* Every resolved record, useful for diagnostics.
|
|
57
|
+
*/
|
|
58
|
+
readonly all: () => readonly ResolvedFirestoreModelIdentity[];
|
|
59
|
+
}
|
|
60
|
+
/**
|
|
61
|
+
* Walks the supplied ts-morph project looking for
|
|
62
|
+
* `<name>Identity = firestoreModelIdentity(...)` declarations and assembles
|
|
63
|
+
* a {@link FirestoreModelIdentityResolver}.
|
|
64
|
+
*
|
|
65
|
+
* Identifies three call shapes:
|
|
66
|
+
* 1. `firestoreModelIdentity('modelType', 'prefix')` — root identity.
|
|
67
|
+
* 2. `firestoreModelIdentity(parentIdentity, 'modelType', 'prefix')` — nested identity.
|
|
68
|
+
* 3. `firestoreModelIdentity('modelType')` — root identity with prefix-defaulted-to-modelType (rare; collection equals modelType).
|
|
69
|
+
*
|
|
70
|
+
* Calls that don't match any of these shapes are skipped silently — the
|
|
71
|
+
* extractor only consumes successful matches.
|
|
72
|
+
*
|
|
73
|
+
* @param project - The ts-morph project whose source files to scan.
|
|
74
|
+
* @returns The resolver.
|
|
75
|
+
*/
|
|
76
|
+
export declare function buildIdentityResolverFromProject(project: Project): FirestoreModelIdentityResolver;
|
|
77
|
+
/**
|
|
78
|
+
* Builds a resolver from a pre-resolved list of records — used when the
|
|
79
|
+
* MCP tool runtime has access to {@link FIREBASE_MODELS} + the downstream
|
|
80
|
+
* catalog and doesn't need to rescan source files.
|
|
81
|
+
*
|
|
82
|
+
* @param records - The resolved identity records.
|
|
83
|
+
* @returns The resolver.
|
|
84
|
+
*/
|
|
85
|
+
export declare function buildIdentityResolverFromRecords(records: readonly ResolvedFirestoreModelIdentity[]): FirestoreModelIdentityResolver;
|
|
86
|
+
/**
|
|
87
|
+
* Converts `JobLocationWeek` → `jobLocationWeek`. Pass-through when the
|
|
88
|
+
* input is already camelCase.
|
|
89
|
+
*
|
|
90
|
+
* @param typeName - The TypeScript type name.
|
|
91
|
+
* @returns The lowerCamelCase identifier.
|
|
92
|
+
*/
|
|
93
|
+
export declare function toCamelCase(typeName: string): string;
|
|
@@ -0,0 +1,108 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Constraint-helper expansion registry used by the model-firebase-index
|
|
3
|
+
* extractor.
|
|
4
|
+
*
|
|
5
|
+
* The query factories under scan compose `@dereekb/firebase` helpers
|
|
6
|
+
* (`whereDateIsBeforeWithSort`, `whereDateIsBetween`,
|
|
7
|
+
* `allChildDocumentsUnderRelativePath`, …) that internally fan out to one
|
|
8
|
+
* or more base `where` / `orderBy` constraints. Static AST walking only
|
|
9
|
+
* sees the helper call — to derive the actual Firestore index requirements
|
|
10
|
+
* the extractor needs to know which base constraints the helper produces.
|
|
11
|
+
* This file is the source of truth for that mapping.
|
|
12
|
+
*
|
|
13
|
+
* Each entry declares:
|
|
14
|
+
* - which positional argument carries the field path
|
|
15
|
+
* - which positional argument carries the `OrderByDirection` (if any)
|
|
16
|
+
* - the ordered base constraints the helper emits, with operators or
|
|
17
|
+
* orderBy directions resolved from the call site when possible
|
|
18
|
+
*
|
|
19
|
+
* The extractor reads the call site, plugs the resolved field path and
|
|
20
|
+
* direction into the descriptor, and emits {@link ConstraintSequenceEntry}
|
|
21
|
+
* rows tagged with `fromHelper: <helperName>` so diagnostics remain
|
|
22
|
+
* faithful to the source factory.
|
|
23
|
+
*/
|
|
24
|
+
import type { ConstraintSequenceEntry, FirestoreWhereOperator } from './model-firebase-index-schema.js';
|
|
25
|
+
/**
|
|
26
|
+
* Description of one base constraint a helper expands into. `direction`
|
|
27
|
+
* resolves at call-site time from the helper's direction argument when
|
|
28
|
+
* `useCallSiteDirection` is set; otherwise it falls back to the literal
|
|
29
|
+
* `direction` here. `operator` is required when `kind === 'where'`.
|
|
30
|
+
*/
|
|
31
|
+
export interface FirestoreQueryHelperExpansionPart {
|
|
32
|
+
readonly kind: 'where' | 'orderBy';
|
|
33
|
+
readonly operator?: FirestoreWhereOperator;
|
|
34
|
+
readonly direction?: 'asc' | 'desc';
|
|
35
|
+
/**
|
|
36
|
+
* When `true` and the helper resolves a direction from its call-site
|
|
37
|
+
* argument, override the literal `direction` above.
|
|
38
|
+
*/
|
|
39
|
+
readonly useCallSiteDirection?: boolean;
|
|
40
|
+
}
|
|
41
|
+
/**
|
|
42
|
+
* One entry in the helper expansion registry.
|
|
43
|
+
*/
|
|
44
|
+
export interface FirestoreQueryHelperDescriptor {
|
|
45
|
+
/**
|
|
46
|
+
* Helper function name as it appears in source (e.g. `whereDateIsBetween`).
|
|
47
|
+
*/
|
|
48
|
+
readonly name: string;
|
|
49
|
+
/**
|
|
50
|
+
* Index of the call argument carrying the field path. The extractor reads
|
|
51
|
+
* the string literal at this position (or the identifier when the call
|
|
52
|
+
* passes a `FieldPath` constant — currently treated as opaque, dropping
|
|
53
|
+
* the entry with a warning).
|
|
54
|
+
*/
|
|
55
|
+
readonly fieldArgIndex: number;
|
|
56
|
+
/**
|
|
57
|
+
* Index of the call argument carrying the `OrderByDirection`. Undefined
|
|
58
|
+
* when the helper does not accept one (e.g. `whereDateIsBefore`).
|
|
59
|
+
*/
|
|
60
|
+
readonly directionArgIndex?: number;
|
|
61
|
+
/**
|
|
62
|
+
* Default direction when the call-site direction argument is absent.
|
|
63
|
+
*/
|
|
64
|
+
readonly defaultDirection?: 'asc' | 'desc';
|
|
65
|
+
/**
|
|
66
|
+
* Ordered list of the base constraints the helper expands into. Field
|
|
67
|
+
* path is the same for every part (helpers always operate on one field).
|
|
68
|
+
*/
|
|
69
|
+
readonly parts: readonly FirestoreQueryHelperExpansionPart[];
|
|
70
|
+
}
|
|
71
|
+
/**
|
|
72
|
+
* Built-in helper expansions. Mirrors the upstream
|
|
73
|
+
* `@dereekb/firebase/constraint.template.ts` implementations as of writing.
|
|
74
|
+
* Helpers not listed here are extracted as a single opaque `where` call so
|
|
75
|
+
* the entry still surfaces during validation — the extractor emits a
|
|
76
|
+
* warning so a missing helper can be added here.
|
|
77
|
+
*
|
|
78
|
+
* Order within `parts` is significant: it mirrors the source helper's
|
|
79
|
+
* emitted constraint array order, which the analyzer treats as the user's
|
|
80
|
+
* intended Firestore index field order.
|
|
81
|
+
*/
|
|
82
|
+
export declare const FIRESTORE_QUERY_HELPERS: readonly FirestoreQueryHelperDescriptor[];
|
|
83
|
+
/**
|
|
84
|
+
* Lookup helper by name. Returns `undefined` when the helper isn't in the
|
|
85
|
+
* registry — the extractor treats that as an opaque call (no constraints
|
|
86
|
+
* emitted) and surfaces a warning so the registry can be extended.
|
|
87
|
+
*
|
|
88
|
+
* @param name - The helper's source-level identifier.
|
|
89
|
+
* @returns The descriptor or undefined.
|
|
90
|
+
*/
|
|
91
|
+
export declare function getFirestoreQueryHelperDescriptor(name: string): FirestoreQueryHelperDescriptor | undefined;
|
|
92
|
+
/**
|
|
93
|
+
* Resolves the constraint entries a helper call produces given the
|
|
94
|
+
* resolved field path and (optional) call-site direction. The extractor
|
|
95
|
+
* supplies the parsed values; this function applies the descriptor's
|
|
96
|
+
* `parts` template.
|
|
97
|
+
*
|
|
98
|
+
* @param input - The resolved descriptor + call-site values.
|
|
99
|
+
* @param input.descriptor - The looked-up helper descriptor.
|
|
100
|
+
* @param input.fieldPath - The resolved field path for the constraints.
|
|
101
|
+
* @param input.direction - Optional call-site direction override for orderBy parts.
|
|
102
|
+
* @returns The ordered constraint entries the helper emits.
|
|
103
|
+
*/
|
|
104
|
+
export declare function expandFirestoreQueryHelper(input: {
|
|
105
|
+
readonly descriptor: FirestoreQueryHelperDescriptor;
|
|
106
|
+
readonly fieldPath: string;
|
|
107
|
+
readonly direction?: 'asc' | 'desc';
|
|
108
|
+
}): readonly ConstraintSequenceEntry[];
|
|
@@ -0,0 +1,94 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* `generate-firestore-indexes` subcommand entry point.
|
|
3
|
+
*
|
|
4
|
+
* Walks a downstream `-firebase` component for `@dbxModelFirebaseIndex`-
|
|
5
|
+
* tagged factories (the same pipeline `scan-model-firebase-indexes` uses),
|
|
6
|
+
* runs the analyzer, and emits a canonical `firestore.indexes.json`
|
|
7
|
+
* payload via {@link generateFirestoreIndexesJson}.
|
|
8
|
+
*
|
|
9
|
+
* Two modes:
|
|
10
|
+
*
|
|
11
|
+
* - **write** (default) — write the generated JSON to `--output` (defaults to
|
|
12
|
+
* `firestore.indexes.json` at the workspace root).
|
|
13
|
+
* - **`--check`** — compare the generated JSON to the file on disk and exit
|
|
14
|
+
* 1 on drift. Used in CI to fail PRs that change query factories without
|
|
15
|
+
* regenerating the indexes file.
|
|
16
|
+
*
|
|
17
|
+
* Preserves user-authored content that the analyzer can't reproduce: TTL
|
|
18
|
+
* fieldOverrides, vector indexes, hand-tuned single-field overrides like
|
|
19
|
+
* `sjs.adat`, and any composite tied to a `@dbxModelFirebaseIndexManual`
|
|
20
|
+
* factory (round-tripped verbatim from the existing file).
|
|
21
|
+
*/
|
|
22
|
+
/**
|
|
23
|
+
* Function shape used by the CLI to read existing files (the on-disk
|
|
24
|
+
* `firestore.indexes.json`). Defaults to `node:fs/promises.readFile`.
|
|
25
|
+
*/
|
|
26
|
+
export type GenerateFirestoreIndexesCliReadFile = (absolutePath: string) => Promise<string>;
|
|
27
|
+
/**
|
|
28
|
+
* Function shape used by the CLI to write the produced file. Defaults to
|
|
29
|
+
* `node:fs/promises.writeFile`.
|
|
30
|
+
*/
|
|
31
|
+
export type GenerateFirestoreIndexesCliWriteFile = (absolutePath: string, data: string) => Promise<void>;
|
|
32
|
+
/**
|
|
33
|
+
* Console sink for stdout and stderr lines.
|
|
34
|
+
*/
|
|
35
|
+
export type GenerateFirestoreIndexesCliLogger = (message: string) => void;
|
|
36
|
+
/**
|
|
37
|
+
* Result of one CLI invocation.
|
|
38
|
+
*/
|
|
39
|
+
export interface RunGenerateFirestoreIndexesCliResult {
|
|
40
|
+
readonly exitCode: number;
|
|
41
|
+
}
|
|
42
|
+
/**
|
|
43
|
+
* Input to {@link runGenerateFirestoreIndexesCli}.
|
|
44
|
+
*/
|
|
45
|
+
export interface RunGenerateFirestoreIndexesCliInput {
|
|
46
|
+
/**
|
|
47
|
+
* Subcommand argv slice — everything after `generate-firestore-indexes`.
|
|
48
|
+
*/
|
|
49
|
+
readonly argv: readonly string[];
|
|
50
|
+
/**
|
|
51
|
+
* Working directory the CLI resolves relative paths against.
|
|
52
|
+
*/
|
|
53
|
+
readonly cwd: string;
|
|
54
|
+
/**
|
|
55
|
+
* Generator string written into the manifest source field (used for
|
|
56
|
+
* diagnostics only — the indexes file does not record this).
|
|
57
|
+
*/
|
|
58
|
+
readonly generator: string;
|
|
59
|
+
/**
|
|
60
|
+
* Optional binary name printed in the `--help` usage banner so embedders
|
|
61
|
+
* (e.g. the `dbx-components-mcp generate-firestore-indexes` subcommand)
|
|
62
|
+
* can advertise the right invocation to their users. Defaults to
|
|
63
|
+
* `dbx-cli-generate-firestore-indexes`.
|
|
64
|
+
*/
|
|
65
|
+
readonly binName?: string;
|
|
66
|
+
/**
|
|
67
|
+
* Optional now() override. Unused at present; reserved so callers can
|
|
68
|
+
* inject a stable clock when adding wall-time metadata in the future.
|
|
69
|
+
*/
|
|
70
|
+
readonly now?: () => Date;
|
|
71
|
+
/**
|
|
72
|
+
* Optional file-reader override (e.g. tests that inject an in-memory FS).
|
|
73
|
+
*/
|
|
74
|
+
readonly readFile?: GenerateFirestoreIndexesCliReadFile;
|
|
75
|
+
/**
|
|
76
|
+
* Optional file-writer override (e.g. tests that capture output without
|
|
77
|
+
* touching disk).
|
|
78
|
+
*/
|
|
79
|
+
readonly writeFile?: GenerateFirestoreIndexesCliWriteFile;
|
|
80
|
+
/**
|
|
81
|
+
* Optional logger override. Defaults to console.log / console.error.
|
|
82
|
+
*/
|
|
83
|
+
readonly stdout?: GenerateFirestoreIndexesCliLogger;
|
|
84
|
+
readonly stderr?: GenerateFirestoreIndexesCliLogger;
|
|
85
|
+
}
|
|
86
|
+
/**
|
|
87
|
+
* Runs one invocation of `generate-firestore-indexes`. Never throws on
|
|
88
|
+
* user errors — every failure path returns a structured exit code so
|
|
89
|
+
* callers can wire this into `process.exit` without try/catch.
|
|
90
|
+
*
|
|
91
|
+
* @param input - Argv plus injectable I/O hooks.
|
|
92
|
+
* @returns The CLI's exit code (0 on success / no drift, 1 on drift / failure, 2 on usage error)
|
|
93
|
+
*/
|
|
94
|
+
export declare function runGenerateFirestoreIndexesCli(input: RunGenerateFirestoreIndexesCliInput): Promise<RunGenerateFirestoreIndexesCliResult>;
|
|
@@ -0,0 +1,7 @@
|
|
|
1
|
+
export * from './firestore-indexes-generate';
|
|
2
|
+
export * from './generate-firestore-indexes-cli';
|
|
3
|
+
export * from './model-firebase-index-build-manifest';
|
|
4
|
+
export * from './model-firebase-index-extract';
|
|
5
|
+
export * from './model-firebase-index-runtime';
|
|
6
|
+
export * from './model-firebase-index-scan-config-schema';
|
|
7
|
+
export * from './model-firebase-index-schema';
|
|
@@ -0,0 +1,68 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Index analyzer.
|
|
3
|
+
*
|
|
4
|
+
* Consumes the {@link ExtractedModelFirebaseIndexEntry}s produced by the
|
|
5
|
+
* extractor and decides, per `(collection, scope, constraintSequence)`,
|
|
6
|
+
* whether the query requires a composite index, a `fieldOverrides[]`
|
|
7
|
+
* variant, or neither (because Firestore's automatic single-field
|
|
8
|
+
* `COLLECTION`-scope index already covers the query). Encodes Firestore's
|
|
9
|
+
* field-order rule for composites:
|
|
10
|
+
*
|
|
11
|
+
* 1. Equality (`==`, `in`) fields first, in source order.
|
|
12
|
+
* 2. A single range/inequality field next (direction taken from any
|
|
13
|
+
* explicit `orderBy` on the same field, else ASCENDING).
|
|
14
|
+
* 3. An optional `array-contains` field.
|
|
15
|
+
* 4. Remaining `orderBy` fields in source order, with their declared
|
|
16
|
+
* direction.
|
|
17
|
+
*
|
|
18
|
+
* The `__name__` tiebreaker is NOT appended here — the generator does that
|
|
19
|
+
* at emission time, picking the direction to match the last orderBy.
|
|
20
|
+
*
|
|
21
|
+
* Returns one `derivedComposites` + one `derivedFieldOverrides` set per
|
|
22
|
+
* entry. The generator merges these across entries when building
|
|
23
|
+
* `firestore.indexes.json` (dedupe, canonical sort, COLLECTION-quartet
|
|
24
|
+
* companion fieldOverrides).
|
|
25
|
+
*/
|
|
26
|
+
import { type DerivedComposite, type DerivedFieldOverride } from './model-firebase-index-schema.js';
|
|
27
|
+
import type { ExtractedModelFirebaseIndexEntry } from './model-firebase-index-extract.js';
|
|
28
|
+
/**
|
|
29
|
+
* Per-factory analyzer output. The entry order matches the extractor's
|
|
30
|
+
* input order so warnings + diagnostics can point back to the factory.
|
|
31
|
+
*/
|
|
32
|
+
export interface AnalyzedEntry {
|
|
33
|
+
readonly extractedEntry: ExtractedModelFirebaseIndexEntry;
|
|
34
|
+
readonly derivedComposites: readonly DerivedComposite[];
|
|
35
|
+
readonly derivedFieldOverrides: readonly DerivedFieldOverride[];
|
|
36
|
+
readonly warnings: readonly AnalyzerWarning[];
|
|
37
|
+
}
|
|
38
|
+
/**
|
|
39
|
+
* Discriminated union of non-fatal events emitted during analysis.
|
|
40
|
+
*/
|
|
41
|
+
export type AnalyzerWarning = {
|
|
42
|
+
readonly kind: 'multiple-range-fields';
|
|
43
|
+
readonly factoryName: string;
|
|
44
|
+
readonly fields: readonly string[];
|
|
45
|
+
} | {
|
|
46
|
+
readonly kind: 'orderby-conflict';
|
|
47
|
+
readonly factoryName: string;
|
|
48
|
+
readonly field: string;
|
|
49
|
+
readonly directions: readonly string[];
|
|
50
|
+
} | {
|
|
51
|
+
readonly kind: 'unsupported-array-contains-any';
|
|
52
|
+
readonly factoryName: string;
|
|
53
|
+
readonly field: string;
|
|
54
|
+
};
|
|
55
|
+
/**
|
|
56
|
+
* Runs the analyzer over every extracted entry.
|
|
57
|
+
*
|
|
58
|
+
* @param entries - The extracted entries to analyze.
|
|
59
|
+
* @returns One analyzed result per input entry.
|
|
60
|
+
*/
|
|
61
|
+
export declare function analyzeModelFirebaseIndexEntries(entries: readonly ExtractedModelFirebaseIndexEntry[]): readonly AnalyzedEntry[];
|
|
62
|
+
/**
|
|
63
|
+
* Runs the analyzer over a single extracted entry.
|
|
64
|
+
*
|
|
65
|
+
* @param entry - The extracted entry to analyze.
|
|
66
|
+
* @returns The analyzed result.
|
|
67
|
+
*/
|
|
68
|
+
export declare function analyzeEntry(entry: ExtractedModelFirebaseIndexEntry): AnalyzedEntry;
|