@dereekb/dbx-cli 13.40.0 → 13.42.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 (35) hide show
  1. package/eslint/package.json +3 -3
  2. package/firebase-api-manifest/main.js +156 -18
  3. package/firebase-api-manifest/package.json +3 -3
  4. package/firestore-indexes/src/generate-firestore-indexes-cli.d.ts +12 -4
  5. package/firestore-indexes/src/model-firebase-index-analyze.d.ts +12 -2
  6. package/firestore-query-manifest/main.js +306 -12
  7. package/firestore-query-manifest/package.json +3 -3
  8. package/generate-firestore-indexes/main.js +118 -20
  9. package/generate-firestore-indexes/package.json +2 -2
  10. package/generate-mcp-manifest/main.js +5 -2
  11. package/generate-mcp-manifest/package.json +3 -3
  12. package/generate-route-manifest/main.js +1 -1
  13. package/generate-route-manifest/package.json +2 -2
  14. package/index.esm.js +1389 -933
  15. package/lint-cache/package.json +2 -2
  16. package/manifest-extract/index.esm.js +148 -2
  17. package/manifest-extract/package.json +2 -2
  18. package/manifest-extract/src/lib/extract-models.d.ts +4 -2
  19. package/manifest-extract/src/lib/types.d.ts +65 -0
  20. package/model-test/index.esm.js +4 -54
  21. package/model-test/package.json +2 -2
  22. package/package.json +6 -6
  23. package/route/package.json +7 -7
  24. package/src/lib/doctor/firestore-session.check.d.ts +23 -2
  25. package/src/lib/firestore/firestore.collection.d.ts +29 -0
  26. package/src/lib/firestore/firestore.query.d.ts +1 -1
  27. package/src/lib/firestore/index.d.ts +1 -0
  28. package/src/lib/firestore/query-info-utils.d.ts +6 -1
  29. package/src/lib/firestore/query-mode.d.ts +120 -0
  30. package/src/lib/manifest/types.d.ts +161 -2
  31. package/src/lib/mcp-scan/manifest/css-utilities-schema.d.ts +2 -2
  32. package/src/lib/mcp-scan/manifest/tokens-schema.d.ts +2 -2
  33. package/test/package.json +9 -9
  34. package/validate/index.js +12 -7
  35. package/validate/package.json +3 -3
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@dereekb/dbx-cli-lint-cache",
3
- "version": "13.40.0",
3
+ "version": "13.42.0",
4
4
  "private": true,
5
5
  "type": "module",
6
6
  "devDependencies": {
@@ -8,7 +8,7 @@
8
8
  "eslint": "10.4.0"
9
9
  },
10
10
  "peerDependencies": {
11
- "@dereekb/util": "13.40.0",
11
+ "@dereekb/util": "13.42.0",
12
12
  "yargs": "^18.0.0"
13
13
  }
14
14
  }
@@ -1,4 +1,4 @@
1
- import { Project, Node } from 'ts-morph';
1
+ import { Project, Node, SyntaxKind } from 'ts-morph';
2
2
  import { parseFirestoreModelIdentityArgs, resolveExtendsName } from '@dereekb/dbx-cli';
3
3
 
4
4
  function _define_property$1(obj, key, value) {
@@ -702,6 +702,14 @@ var MCP_TOOL_NAME_SEGMENT_TAG = 'dbxModelMcpToolNameSegment';
702
702
  var MODEL_TYPE_VALUE_PATTERN = /^[a-z][A-Za-z0-9_$]*$/;
703
703
  var TOOL_NAME_SEGMENT_PATTERN = /^[A-Za-z][A-Za-z0-9_$]*$/;
704
704
  var IDENTITY_FN = 'firestoreModelIdentity';
705
+ var SINGLE_ITEM_COLLECTION_FN = 'singleItemFirestoreCollection';
706
+ var ROOT_SINGLE_ITEM_COLLECTION_FN = 'rootSingleItemFirestoreCollection';
707
+ var SINGLE_ITEM_COLLECTION_FN_NAMES = [
708
+ SINGLE_ITEM_COLLECTION_FN,
709
+ ROOT_SINGLE_ITEM_COLLECTION_FN
710
+ ];
711
+ var MODEL_IDENTITY_KEY = 'modelIdentity';
712
+ var SINGLE_ITEM_IDENTIFIER_KEY = 'singleItemIdentifier';
705
713
  var CONVERTER_FN_NAMES = [
706
714
  'snapshotConverterFunctions',
707
715
  'firestoreSubObject',
@@ -736,13 +744,17 @@ var FIRESTORE_FIELD_KEY = 'firestoreField';
736
744
  var enums = readEnums(sourceFile);
737
745
  var modelGroups = readModelGroups(sourceFile);
738
746
  var serviceFactories = readServiceFactories(sourceFile);
747
+ var singleItemCollections = readSingleItemCollections(sourceFile);
748
+ var stringConstants = readStringConstants(sourceFile);
739
749
  return {
740
750
  identities: identities,
741
751
  interfaces: interfaces,
742
752
  converters: converters,
743
753
  enums: enums,
744
754
  modelGroups: modelGroups,
745
- serviceFactories: serviceFactories
755
+ serviceFactories: serviceFactories,
756
+ singleItemCollections: singleItemCollections,
757
+ stringConstants: stringConstants
746
758
  };
747
759
  }
748
760
  function readIdentities(sourceFile) {
@@ -1080,6 +1092,140 @@ function readServiceFactoryModelType(jsDocs) {
1080
1092
  }
1081
1093
  return result;
1082
1094
  }
1095
+ /**
1096
+ * Finds every `singleItemFirestoreCollection` / `rootSingleItemFirestoreCollection` call in the
1097
+ * file. Unlike the other artifacts these are not top-level declarations — they sit inside the
1098
+ * `<model>FirestoreCollectionFactory` function bodies — so this needs a full descendant walk,
1099
+ * guarded on the raw text so files declaring no single-item collection skip it entirely.
1100
+ *
1101
+ * @param sourceFile - The parsed source file.
1102
+ * @returns One entry per resolvable call, in source order.
1103
+ */ function readSingleItemCollections(sourceFile) {
1104
+ var out = [];
1105
+ var text = sourceFile.getFullText();
1106
+ if (SINGLE_ITEM_COLLECTION_FN_NAMES.some(function(name) {
1107
+ return text.includes(name);
1108
+ })) {
1109
+ var _iteratorNormalCompletion = true, _didIteratorError = false, _iteratorError = undefined;
1110
+ try {
1111
+ for(var _iterator = sourceFile.getDescendantsOfKind(SyntaxKind.CallExpression)[Symbol.iterator](), _step; !(_iteratorNormalCompletion = (_step = _iterator.next()).done); _iteratorNormalCompletion = true){
1112
+ var call = _step.value;
1113
+ var fnName = readCalleeName(call);
1114
+ if (fnName !== SINGLE_ITEM_COLLECTION_FN && fnName !== ROOT_SINGLE_ITEM_COLLECTION_FN) continue;
1115
+ var entry = buildSingleItemCollection(call, fnName === ROOT_SINGLE_ITEM_COLLECTION_FN);
1116
+ if (entry) out.push(entry);
1117
+ }
1118
+ } catch (err) {
1119
+ _didIteratorError = true;
1120
+ _iteratorError = err;
1121
+ } finally{
1122
+ try {
1123
+ if (!_iteratorNormalCompletion && _iterator.return != null) {
1124
+ _iterator.return();
1125
+ }
1126
+ } finally{
1127
+ if (_didIteratorError) {
1128
+ throw _iteratorError;
1129
+ }
1130
+ }
1131
+ }
1132
+ }
1133
+ return out;
1134
+ }
1135
+ /**
1136
+ * Resolves a call's function name for both the bare (`singleItemFirestoreCollection(...)`) and the
1137
+ * method (`firestoreContext.singleItemFirestoreCollection(...)`) forms.
1138
+ *
1139
+ * @param call - The call expression.
1140
+ * @returns The called function's name, or the verbatim callee text when it is neither shape.
1141
+ */ function readCalleeName(call) {
1142
+ var expression = call.getExpression();
1143
+ return Node.isPropertyAccessExpression(expression) ? expression.getName() : expression.getText();
1144
+ }
1145
+ function buildSingleItemCollection(call, root) {
1146
+ var result;
1147
+ var args = call.getArguments();
1148
+ if (args.length > 0) {
1149
+ var config = args[0];
1150
+ if (Node.isObjectLiteralExpression(config)) {
1151
+ var identityNode = readPropertyValue(config, MODEL_IDENTITY_KEY);
1152
+ if (identityNode && Node.isIdentifier(identityNode)) {
1153
+ var identifierNode = readPropertyValue(config, SINGLE_ITEM_IDENTIFIER_KEY);
1154
+ result = {
1155
+ identityConst: identityNode.getText(),
1156
+ root: root,
1157
+ documentId: readStringLiteralText(identifierNode),
1158
+ documentIdConstRef: identifierNode && Node.isIdentifier(identifierNode) ? identifierNode.getText() : undefined,
1159
+ explicitIdentifier: config.getProperty(SINGLE_ITEM_IDENTIFIER_KEY) !== undefined,
1160
+ line: call.getStartLineNumber()
1161
+ };
1162
+ }
1163
+ }
1164
+ }
1165
+ return result;
1166
+ }
1167
+ function readStringConstants(sourceFile) {
1168
+ var out = [];
1169
+ var _iteratorNormalCompletion = true, _didIteratorError = false, _iteratorError = undefined;
1170
+ try {
1171
+ for(var _iterator = sourceFile.getVariableStatements()[Symbol.iterator](), _step; !(_iteratorNormalCompletion = (_step = _iterator.next()).done); _iteratorNormalCompletion = true){
1172
+ var statement = _step.value;
1173
+ if (!statement.isExported()) continue;
1174
+ var _iteratorNormalCompletion1 = true, _didIteratorError1 = false, _iteratorError1 = undefined;
1175
+ try {
1176
+ for(var _iterator1 = statement.getDeclarations()[Symbol.iterator](), _step1; !(_iteratorNormalCompletion1 = (_step1 = _iterator1.next()).done); _iteratorNormalCompletion1 = true){
1177
+ var decl = _step1.value;
1178
+ var value = readStringLiteralText(decl.getInitializer());
1179
+ if (value !== undefined) {
1180
+ out.push({
1181
+ name: decl.getName(),
1182
+ value: value
1183
+ });
1184
+ }
1185
+ }
1186
+ } catch (err) {
1187
+ _didIteratorError1 = true;
1188
+ _iteratorError1 = err;
1189
+ } finally{
1190
+ try {
1191
+ if (!_iteratorNormalCompletion1 && _iterator1.return != null) {
1192
+ _iterator1.return();
1193
+ }
1194
+ } finally{
1195
+ if (_didIteratorError1) {
1196
+ throw _iteratorError1;
1197
+ }
1198
+ }
1199
+ }
1200
+ }
1201
+ } catch (err) {
1202
+ _didIteratorError = true;
1203
+ _iteratorError = err;
1204
+ } finally{
1205
+ try {
1206
+ if (!_iteratorNormalCompletion && _iterator.return != null) {
1207
+ _iterator.return();
1208
+ }
1209
+ } finally{
1210
+ if (_didIteratorError) {
1211
+ throw _iteratorError;
1212
+ }
1213
+ }
1214
+ }
1215
+ return out;
1216
+ }
1217
+ /**
1218
+ * Reads a node's string-literal text, peeling any `as const` / `as Foo` assertion.
1219
+ *
1220
+ * @param node - The candidate node, or `undefined`.
1221
+ * @returns The literal text, or `undefined` when the node is not a string literal.
1222
+ */ function readStringLiteralText(node) {
1223
+ var current = node;
1224
+ while(current !== undefined && Node.isAsExpression(current)){
1225
+ current = current.getExpression();
1226
+ }
1227
+ return current !== undefined && (Node.isStringLiteral(current) || Node.isNoSubstitutionTemplateLiteral(current)) ? current.getLiteralText() : undefined;
1228
+ }
1083
1229
  function readConverters(sourceFile) {
1084
1230
  var out = [];
1085
1231
  var _iteratorNormalCompletion = true, _didIteratorError = false, _iteratorError = undefined;
@@ -1,11 +1,11 @@
1
1
  {
2
2
  "name": "@dereekb/dbx-cli/manifest-extract",
3
- "version": "13.40.0",
3
+ "version": "13.42.0",
4
4
  "type": "module",
5
5
  "sideEffects": false,
6
6
  "peerDependencies": {
7
7
  "@dereekb/date": "13.15.0",
8
- "@dereekb/dbx-cli": "13.40.0",
8
+ "@dereekb/dbx-cli": "13.42.0",
9
9
  "@dereekb/firebase": "13.15.0",
10
10
  "@dereekb/model": "13.15.0",
11
11
  "@dereekb/nestjs": "13.15.0",
@@ -2,8 +2,10 @@
2
2
  * Per-source-file walker that pulls Firestore-model metadata out of a `.ts`
3
3
  * file: identities, `@dbxModel`-tagged interfaces with their property JSDocs,
4
4
  * snapshot converters (top-level `snapshotConverterFunctions`,
5
- * `firestoreSubObject`, and `firestoreObjectArray` consts), enums, and
6
- * `@dbxModelGroup`-tagged collection containers.
5
+ * `firestoreSubObject`, and `firestoreObjectArray` consts), enums,
6
+ * `@dbxModelGroup`-tagged collection containers, single-item collection
7
+ * declarations (with the fixed document id they pin), and the exported
8
+ * string constants such an id may be declared as.
7
9
  *
8
10
  * Mirrors the per-file outputs of `dbx-components-mcp`'s `extractModels()`
9
11
  * but lives inside `@dereekb/dbx-cli/manifest-extract` so the `dbx-cli`
@@ -279,6 +279,20 @@ export interface ModelExtraction {
279
279
  * manifest can surface the implementing factory alongside each model.
280
280
  */
281
281
  readonly serviceFactories: readonly ModelExtractionServiceFactory[];
282
+ /**
283
+ * `singleItemFirestoreCollection(...)` / `rootSingleItemFirestoreCollection(...)` calls found
284
+ * anywhere in this file (they live inside collection-factory function bodies, not at the top
285
+ * level). The orchestrator joins these onto matching model entries by `identityConst` so the
286
+ * runtime manifest can publish each singleton model's fixed document id.
287
+ */
288
+ readonly singleItemCollections: readonly ModelExtractionSingleItemCollection[];
289
+ /**
290
+ * Exported `const <NAME> = '<literal>'` string constants declared in this file. Aggregated by
291
+ * the orchestrator into a cross-file registry so a
292
+ * {@link ModelExtractionSingleItemCollection.documentIdConstRef} declared in a sibling file
293
+ * still resolves.
294
+ */
295
+ readonly stringConstants: readonly ModelExtractionStringConstant[];
282
296
  }
283
297
  /**
284
298
  * One `@dbxModelServiceFactory <modelType>`-tagged variable export.
@@ -294,3 +308,54 @@ export interface ModelExtractionServiceFactory {
294
308
  */
295
309
  readonly exportName: string;
296
310
  }
311
+ /**
312
+ * One `firestoreContext.singleItemFirestoreCollection({ ... })` or
313
+ * `firestoreContext.rootSingleItemFirestoreCollection({ ... })` call — the declaration that a
314
+ * model's collection holds exactly one document, at a fixed id.
315
+ *
316
+ * The id is what a caller needs to build a document key and what the manifest could not express
317
+ * before: it defaults to `DEFAULT_SINGLE_ITEM_FIRESTORE_COLLECTION_DOCUMENT_IDENTIFIER` (`'0'`)
318
+ * but is frequently overridden, so `'/0'` cannot be inferred.
319
+ */
320
+ export interface ModelExtractionSingleItemCollection {
321
+ /**
322
+ * Identity const named by the call's `modelIdentity` property (e.g.
323
+ * `billingGroupInvoiceSummaryIdentity`) — the join key back to the model entry.
324
+ */
325
+ readonly identityConst: string;
326
+ /**
327
+ * `true` for the `rootSingleItemFirestoreCollection` variant (a root collection holding one
328
+ * document), `false` for the `singleItemFirestoreCollection` subcollection variant.
329
+ */
330
+ readonly root: boolean;
331
+ /**
332
+ * Document id when `singleItemIdentifier` is a string literal. `undefined` when the property is
333
+ * absent, names a const (see {@link documentIdConstRef}), or is an unresolvable expression.
334
+ */
335
+ readonly documentId: string | undefined;
336
+ /**
337
+ * Const name when `singleItemIdentifier` is an identifier reference (e.g.
338
+ * `BILLING_GROUP_SUMMARY_BILLING_SINGLE_IDENTIFIER`), resolved by the orchestrator against the
339
+ * cross-file string-constant registry.
340
+ */
341
+ readonly documentIdConstRef: string | undefined;
342
+ /**
343
+ * `true` when the config object declares a `singleItemIdentifier` property at all. `false` means
344
+ * the runtime default applies; `true` with neither {@link documentId} nor
345
+ * {@link documentIdConstRef} means the value is an expression the walker could not read, and the
346
+ * id is genuinely unknown rather than defaulted.
347
+ */
348
+ readonly explicitIdentifier: boolean;
349
+ /**
350
+ * Source-line number of the call (1-based) for diagnostics.
351
+ */
352
+ readonly line: number;
353
+ }
354
+ /**
355
+ * One exported `const <NAME> = '<literal>'` string constant, captured so cross-file
356
+ * `singleItemIdentifier: SOME_CONST` references resolve without type-checker access.
357
+ */
358
+ export interface ModelExtractionStringConstant {
359
+ readonly name: string;
360
+ readonly value: string;
361
+ }
@@ -438,31 +438,6 @@ function toPosix(p) {
438
438
  return p.split(sep).join('/');
439
439
  }
440
440
 
441
- function _array_like_to_array$6(arr, len) {
442
- if (len == null || len > arr.length) len = arr.length;
443
- for(var i = 0, arr2 = new Array(len); i < len; i++)arr2[i] = arr[i];
444
- return arr2;
445
- }
446
- function _array_without_holes$6(arr) {
447
- if (Array.isArray(arr)) return _array_like_to_array$6(arr);
448
- }
449
- function _iterable_to_array$6(iter) {
450
- if (typeof Symbol !== "undefined" && iter[Symbol.iterator] != null || iter["@@iterator"] != null) return Array.from(iter);
451
- }
452
- function _non_iterable_spread$6() {
453
- throw new TypeError("Invalid attempt to spread non-iterable instance.\\nIn order to be iterable, non-array objects must have a [Symbol.iterator]() method.");
454
- }
455
- function _to_consumable_array$6(arr) {
456
- return _array_without_holes$6(arr) || _iterable_to_array$6(arr) || _unsupported_iterable_to_array$6(arr) || _non_iterable_spread$6();
457
- }
458
- function _unsupported_iterable_to_array$6(o, minLen) {
459
- if (!o) return;
460
- if (typeof o === "string") return _array_like_to_array$6(o, minLen);
461
- var n = Object.prototype.toString.call(o).slice(8, -1);
462
- if (n === "Object" && o.constructor) n = o.constructor.name;
463
- if (n === "Map" || n === "Set") return Array.from(n);
464
- if (n === "Arguments" || /^(?:Ui|I)nt(?:8|16|32)(?:Clamped)?Array$/.test(n)) return _array_like_to_array$6(o, minLen);
465
- }
466
441
  var SPEC_FILE_VIRTUAL_PATH = '__spec__/spec.ts';
467
442
  var FIXTURE_CONTEXT_SUFFIX = 'Context';
468
443
  var FIXTURE_FACTORY_SUFFIX = 'ContextFactory';
@@ -670,7 +645,7 @@ function isItName(name) {
670
645
  }
671
646
  }
672
647
  }
673
- return _to_consumable_array$6(seen);
648
+ return Array.from(seen);
674
649
  }
675
650
  /**
676
651
  * `true` when the import specifier points at the conventional fixture module
@@ -730,7 +705,7 @@ function sharedPrefix(a, b) {
730
705
  return s;
731
706
  }
732
707
  function dedupedSorted(names) {
733
- return _to_consumable_array$6(new Set(names)).sort(function(a, b) {
708
+ return Array.from(new Set(names)).sort(function(a, b) {
734
709
  return a.localeCompare(b);
735
710
  });
736
711
  }
@@ -1294,31 +1269,6 @@ var KNOWN_NON_MODEL_FIXTURE_FAMILIES = [
1294
1269
  });
1295
1270
  }
1296
1271
 
1297
- function _array_like_to_array$5(arr, len) {
1298
- if (len == null || len > arr.length) len = arr.length;
1299
- for(var i = 0, arr2 = new Array(len); i < len; i++)arr2[i] = arr[i];
1300
- return arr2;
1301
- }
1302
- function _array_without_holes$5(arr) {
1303
- if (Array.isArray(arr)) return _array_like_to_array$5(arr);
1304
- }
1305
- function _iterable_to_array$5(iter) {
1306
- if (typeof Symbol !== "undefined" && iter[Symbol.iterator] != null || iter["@@iterator"] != null) return Array.from(iter);
1307
- }
1308
- function _non_iterable_spread$5() {
1309
- throw new TypeError("Invalid attempt to spread non-iterable instance.\\nIn order to be iterable, non-array objects must have a [Symbol.iterator]() method.");
1310
- }
1311
- function _to_consumable_array$5(arr) {
1312
- return _array_without_holes$5(arr) || _iterable_to_array$5(arr) || _unsupported_iterable_to_array$5(arr) || _non_iterable_spread$5();
1313
- }
1314
- function _unsupported_iterable_to_array$5(o, minLen) {
1315
- if (!o) return;
1316
- if (typeof o === "string") return _array_like_to_array$5(o, minLen);
1317
- var n = Object.prototype.toString.call(o).slice(8, -1);
1318
- if (n === "Object" && o.constructor) n = o.constructor.name;
1319
- if (n === "Map" || n === "Set") return Array.from(n);
1320
- if (n === "Arguments" || /^(?:Ui|I)nt(?:8|16|32)(?:Clamped)?Array$/.test(n)) return _array_like_to_array$5(o, minLen);
1321
- }
1322
1272
  var FIXTURE_SUFFIX = 'TestContextFixture';
1323
1273
  var INSTANCE_SUFFIX = 'TestContextInstance';
1324
1274
  var PARAMS_SUFFIX = 'TestContextParams';
@@ -2239,7 +2189,7 @@ function readFactoryArgInfo(args) {
2239
2189
  }
2240
2190
  }
2241
2191
  }
2242
- var list = _to_consumable_array$5(all);
2192
+ var list = Array.from(all);
2243
2193
  list.sort(function(a, b) {
2244
2194
  var _ref, _fixtures_get, _ref1, _fixtures_get1;
2245
2195
  var _this, _this1;
@@ -2388,7 +2338,7 @@ function readFactoryArgInfo(args) {
2388
2338
  }
2389
2339
  }
2390
2340
  }
2391
- var list = _to_consumable_array$5(out);
2341
+ var list = Array.from(out);
2392
2342
  list.sort(function(a, b) {
2393
2343
  return a.localeCompare(b);
2394
2344
  });
@@ -1,10 +1,10 @@
1
1
  {
2
2
  "name": "@dereekb/dbx-cli/model-test",
3
- "version": "13.40.0",
3
+ "version": "13.42.0",
4
4
  "type": "module",
5
5
  "sideEffects": false,
6
6
  "peerDependencies": {
7
- "@dereekb/util": "13.40.0",
7
+ "@dereekb/util": "13.42.0",
8
8
  "ts-morph": "^21.0.0"
9
9
  },
10
10
  "exports": {
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@dereekb/dbx-cli",
3
- "version": "13.40.0",
3
+ "version": "13.42.0",
4
4
  "type": "module",
5
5
  "sideEffects": false,
6
6
  "bin": {
@@ -66,11 +66,11 @@
66
66
  }
67
67
  },
68
68
  "peerDependencies": {
69
- "@dereekb/date": "13.40.0",
70
- "@dereekb/firebase": "13.40.0",
71
- "@dereekb/model": "13.40.0",
72
- "@dereekb/nestjs": "13.40.0",
73
- "@dereekb/util": "13.40.0",
69
+ "@dereekb/date": "13.42.0",
70
+ "@dereekb/firebase": "13.42.0",
71
+ "@dereekb/model": "13.42.0",
72
+ "@dereekb/nestjs": "13.42.0",
73
+ "@dereekb/util": "13.42.0",
74
74
  "@nestjs/common": "^11.1.19",
75
75
  "arktype": "^2.2.0",
76
76
  "firebase": "^12.12.1",
@@ -1,15 +1,15 @@
1
1
  {
2
2
  "name": "@dereekb/dbx-cli/route",
3
- "version": "13.40.0",
3
+ "version": "13.42.0",
4
4
  "type": "module",
5
5
  "sideEffects": false,
6
6
  "peerDependencies": {
7
- "@dereekb/date": "13.40.0",
8
- "@dereekb/dbx-cli": "13.40.0",
9
- "@dereekb/firebase": "13.40.0",
10
- "@dereekb/model": "13.40.0",
11
- "@dereekb/nestjs": "13.40.0",
12
- "@dereekb/util": "13.40.0"
7
+ "@dereekb/date": "13.42.0",
8
+ "@dereekb/dbx-cli": "13.42.0",
9
+ "@dereekb/firebase": "13.42.0",
10
+ "@dereekb/model": "13.42.0",
11
+ "@dereekb/nestjs": "13.42.0",
12
+ "@dereekb/util": "13.42.0"
13
13
  },
14
14
  "exports": {
15
15
  "./package.json": "./package.json",
@@ -57,11 +57,32 @@ export interface FirestoreSessionDoctorReadRouting {
57
57
  readonly readPreference: 'firestore' | 'api';
58
58
  readonly reason: CliReadSourceReason;
59
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`.
60
+ * Query-catalog entries `firestore-query` can actually run: the factory bound to a real runtime
61
+ * export AND `firestore.rules` does not refuse the query at every scope.
62
+ *
63
+ * `unavailable` entries are deliberately EXCLUDED. Counting them was the bug this number had:
64
+ * an entry with a bound factory over a collection with no `/{path=**}/` rule reported as invocable
65
+ * and then failed with `permission-denied` on every call, so `invocableQueryEntries: 128/128` was
66
+ * a claim the CLI could not honour for about a third of the catalog.
62
67
  */
63
68
  readonly invocableQueryEntries: number;
64
69
  readonly totalQueryEntries: number;
70
+ /**
71
+ * Entries `firestore.rules` refuses at every scope (`queryMode: 'unavailable'`) — the query-level
72
+ * analogue of {@link serverOnlyModels}, surfaced the same way because it has the same consequence.
73
+ */
74
+ readonly unavailableQueryEntries: number;
75
+ /**
76
+ * Entries that address ONE parent document's subcollection (`queryMode: 'parent-child'`), so they
77
+ * run only when scoped with `--parent`.
78
+ */
79
+ readonly parentChildQueryEntries: number;
80
+ /**
81
+ * Whether the query manifest carries invocation modes at all — false when its generator ran
82
+ * without `--rules`, in which case the two counts above are structurally `0` rather than
83
+ * genuinely clean.
84
+ */
85
+ readonly queryModesScanned: boolean;
65
86
  /**
66
87
  * Models the manifest marks `@dbxModelServerOnly` — refused on every `--via` value.
67
88
  */
@@ -20,6 +20,12 @@ export interface CliFirestoreCollectionForQueryInput {
20
20
  * The parent document key to narrow to, from `--parent`.
21
21
  */
22
22
  readonly parentKey?: FirestoreModelKey;
23
+ /**
24
+ * The parent-document path templates the rules declare for this collection, from
25
+ * `CliFirestoreQueryReachability.parentPaths` (e.g. `jl/{jobLocation}/jlj/{job}`). When supplied,
26
+ * a `--parent` whose collection chain does not match one of them is rejected locally.
27
+ */
28
+ readonly parentPaths?: readonly string[];
23
29
  }
24
30
  /**
25
31
  * Resolves the collection a catalog entry should execute against, applying `--parent` scoping.
@@ -42,3 +48,26 @@ export interface CliFirestoreCollectionForQueryInput {
42
48
  * @throws {CliError} When `--parent` is missing where required, supplied where rejected, or the registered collection cannot be scoped.
43
49
  */
44
50
  export declare function cliFirestoreCollectionForQuery(input: CliFirestoreCollectionForQueryInput): FirestoreCollectionLike<unknown>;
51
+ /**
52
+ * Validates a `--parent` value before it is turned into a document reference.
53
+ *
54
+ * Both checks exist because the failure they prevent is otherwise unreadable. An odd-segment
55
+ * `--parent` (a COLLECTION path, e.g. `jl/abc/jlj`) reaches `docAtPath` and comes back as a raw
56
+ * Firestore path assertion; a well-formed key for the WRONG ancestor chain (e.g. `gb/abc` for a
57
+ * collection that lives under `jl/{loc}/jlj/{job}`) silently produces an empty result set, which is
58
+ * indistinguishable from "no matching documents".
59
+ *
60
+ * The chain comparison is on COLLECTION NAMES only — document ids are the caller's data, and the
61
+ * rules templates carry `{wildcard}` placeholders there.
62
+ *
63
+ * @param input - The model type, the supplied parent key, and the rules-declared parent templates.
64
+ * @param input.modelType - The model being queried, for the message.
65
+ * @param input.parentKey - The raw `--parent` value.
66
+ * @param input.parentPaths - The rules-declared parent path templates, when known.
67
+ * @throws {CliError} `INVALID_ARGUMENT` when the key is not a document path, or names a chain the rules do not declare.
68
+ */
69
+ export declare function assertCliFirestoreQueryParentKey(input: {
70
+ readonly modelType: FirestoreModelType;
71
+ readonly parentKey: FirestoreModelKey;
72
+ readonly parentPaths?: readonly string[];
73
+ }): void;
@@ -74,6 +74,6 @@ export declare function cliFirestoreQueryConstraints(input: {
74
74
  *
75
75
  * @param input - The models view, the entry, and the run options.
76
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`.
77
+ * @throws {CliError} On an argument, scoping, mode, or invocability failure. Firestore's own errors (rules, missing index) surface via `cliFirestoreErrorMapper`.
78
78
  */
79
79
  export declare function runCliFirestoreQuery(input: RunCliFirestoreQueryInput): Promise<CliFirestoreQueryResult>;
@@ -10,4 +10,5 @@ export * from './firestore.query-params';
10
10
  export * from './firestore.read';
11
11
  export * from './firestore.session';
12
12
  export * from './query-info-utils';
13
+ export * from './query-mode';
13
14
  export * from './query-registry';
@@ -17,9 +17,14 @@ export interface CliFirestoreQueryListFilter {
17
17
  readonly model?: string;
18
18
  readonly category?: string;
19
19
  readonly tag?: string;
20
+ /**
21
+ * Drop the entries this CLI cannot run — an unbound factory, or a query `firestore.rules` refuses
22
+ * at every scope. A `parent-child` entry is KEPT: `--parent` runs it.
23
+ */
24
+ readonly invocableOnly?: boolean;
20
25
  }
21
26
  /**
22
- * Applies the `--model` / `--category` / `--tag` filters to the catalog.
27
+ * Applies the `--model` / `--category` / `--tag` / `--invocable-only` filters to the catalog.
23
28
  *
24
29
  * @param registry - The query catalog.
25
30
  * @param filter - The active filters.