@dereekb/dbx-cli 13.15.0 → 13.17.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 (46) hide show
  1. package/eslint/index.cjs.default.js +1 -0
  2. package/eslint/index.cjs.js +1050 -0
  3. package/eslint/index.cjs.mjs +2 -0
  4. package/eslint/index.d.ts +1 -0
  5. package/eslint/index.esm.js +1046 -0
  6. package/eslint/package.json +25 -0
  7. package/eslint/rollup.alias-internal.config.d.ts +11 -0
  8. package/eslint/src/index.d.ts +1 -0
  9. package/eslint/src/lib/index.d.ts +2 -0
  10. package/eslint/src/lib/plugin.d.ts +22 -0
  11. package/eslint/src/lib/valid-dbx-route-model-tags.rule.d.ts +59 -0
  12. package/firebase-api-manifest/main.js +318 -228
  13. package/firebase-api-manifest/package.json +3 -3
  14. package/generate-firestore-indexes/main.js +37 -24
  15. package/generate-firestore-indexes/package.json +2 -2
  16. package/generate-mcp-manifest/main.js +57 -39
  17. package/generate-mcp-manifest/package.json +3 -3
  18. package/generate-route-manifest/main.js +1137 -0
  19. package/generate-route-manifest/package.json +10 -0
  20. package/index.cjs.js +4847 -1953
  21. package/index.esm.js +4827 -1954
  22. package/lint-cache/package.json +2 -2
  23. package/manifest-extract/index.cjs.js +175 -240
  24. package/manifest-extract/index.esm.js +174 -239
  25. package/manifest-extract/package.json +9 -4
  26. package/package.json +16 -6
  27. package/src/lib/index.d.ts +2 -0
  28. package/src/lib/manifest/types.d.ts +53 -0
  29. package/src/lib/mcp-scan/manifest/package-root.d.ts +17 -0
  30. package/src/lib/mcp-scan/manifest/tokens-schema.d.ts +5 -4
  31. package/src/lib/mcp-scan/scan/extract-models/assemble.d.ts +17 -0
  32. package/src/lib/route/component-resolve.d.ts +48 -0
  33. package/src/lib/route/index.d.ts +18 -0
  34. package/src/lib/route/route-build-tree.d.ts +31 -0
  35. package/src/lib/route/route-extract.d.ts +46 -0
  36. package/src/lib/route/route-load-tree.d.ts +17 -0
  37. package/src/lib/route/route-manifest.d.ts +132 -0
  38. package/src/lib/route/route-model-tag.d.ts +89 -0
  39. package/src/lib/route/route-models-extract.d.ts +22 -0
  40. package/src/lib/route/route-resolve-sources.d.ts +39 -0
  41. package/src/lib/route/route-types.d.ts +136 -0
  42. package/src/lib/route/url-match.d.ts +116 -0
  43. package/src/lib/scan-helpers/firestore-model-extract-utils.d.ts +43 -0
  44. package/test/index.cjs.js +1 -1
  45. package/test/index.esm.js +1 -1
  46. package/test/package.json +9 -9
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@dereekb/dbx-cli-lint-cache",
3
- "version": "13.15.0",
3
+ "version": "13.17.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.15.0",
11
+ "@dereekb/util": "13.17.0",
12
12
  "yargs": "^18.0.0"
13
13
  }
14
14
  }
@@ -1,6 +1,7 @@
1
1
  'use strict';
2
2
 
3
3
  var tsMorph = require('ts-morph');
4
+ var dbxCli = require('@dereekb/dbx-cli');
4
5
 
5
6
  function _define_property$1(obj, key, value) {
6
7
  if (key in obj) {
@@ -30,7 +31,7 @@ function _object_spread$1(target) {
30
31
  }
31
32
  return target;
32
33
  }
33
- function ownKeys(object, enumerableOnly) {
34
+ function ownKeys$1(object, enumerableOnly) {
34
35
  var keys = Object.keys(object);
35
36
  if (Object.getOwnPropertySymbols) {
36
37
  var symbols = Object.getOwnPropertySymbols(object);
@@ -38,12 +39,12 @@ function ownKeys(object, enumerableOnly) {
38
39
  }
39
40
  return keys;
40
41
  }
41
- function _object_spread_props(target, source) {
42
+ function _object_spread_props$1(target, source) {
42
43
  source = source != null ? source : {};
43
44
  if (Object.getOwnPropertyDescriptors) {
44
45
  Object.defineProperties(target, Object.getOwnPropertyDescriptors(source));
45
46
  } else {
46
- ownKeys(Object(source)).forEach(function(key) {
47
+ ownKeys$1(Object(source)).forEach(function(key) {
47
48
  Object.defineProperty(target, key, Object.getOwnPropertyDescriptor(source, key));
48
49
  });
49
50
  }
@@ -295,14 +296,25 @@ function findFunctionsClassName(sourceFile) {
295
296
  return result;
296
297
  }
297
298
  function inferGroupName(sourceFile) {
299
+ var _findTypeAliasStem;
300
+ return (_findTypeAliasStem = findTypeAliasStem(sourceFile, 'ModelCrudFunctionsConfig')) !== null && _findTypeAliasStem !== void 0 ? _findTypeAliasStem : findTypeAliasStem(sourceFile, 'FunctionTypeMap');
301
+ }
302
+ /**
303
+ * Finds the first type alias whose name ends with `ending` and returns the
304
+ * non-empty stem (the name with `ending` stripped).
305
+ *
306
+ * @param sourceFile - The parsed `<model>.api.ts` source.
307
+ * @param ending - The type-alias name suffix to match.
308
+ * @returns The stem preceding `ending`, or `undefined` when no alias matches with a non-empty stem.
309
+ */ function findTypeAliasStem(sourceFile, ending) {
298
310
  var result;
299
311
  var _iteratorNormalCompletion = true, _didIteratorError = false, _iteratorError = undefined;
300
312
  try {
301
313
  for(var _iterator = sourceFile.getTypeAliases()[Symbol.iterator](), _step; !(_iteratorNormalCompletion = (_step = _iterator.next()).done); _iteratorNormalCompletion = true){
302
314
  var alias = _step.value;
303
315
  var name = alias.getName();
304
- if (name.endsWith('ModelCrudFunctionsConfig')) {
305
- var stem = name.slice(0, -'ModelCrudFunctionsConfig'.length);
316
+ if (name.endsWith(ending)) {
317
+ var stem = name.slice(0, -ending.length);
306
318
  if (stem.length > 0) {
307
319
  result = stem;
308
320
  break;
@@ -323,35 +335,6 @@ function inferGroupName(sourceFile) {
323
335
  }
324
336
  }
325
337
  }
326
- if (result === undefined) {
327
- var _iteratorNormalCompletion1 = true, _didIteratorError1 = false, _iteratorError1 = undefined;
328
- try {
329
- for(var _iterator1 = sourceFile.getTypeAliases()[Symbol.iterator](), _step1; !(_iteratorNormalCompletion1 = (_step1 = _iterator1.next()).done); _iteratorNormalCompletion1 = true){
330
- var alias1 = _step1.value;
331
- var name1 = alias1.getName();
332
- if (name1.endsWith('FunctionTypeMap')) {
333
- var stem1 = name1.slice(0, -'FunctionTypeMap'.length);
334
- if (stem1.length > 0) {
335
- result = stem1;
336
- break;
337
- }
338
- }
339
- }
340
- } catch (err) {
341
- _didIteratorError1 = true;
342
- _iteratorError1 = err;
343
- } finally{
344
- try {
345
- if (!_iteratorNormalCompletion1 && _iterator1.return != null) {
346
- _iterator1.return();
347
- }
348
- } finally{
349
- if (_didIteratorError1) {
350
- throw _iteratorError1;
351
- }
352
- }
353
- }
354
- }
355
338
  return result;
356
339
  }
357
340
  function isNullLiteralType(node) {
@@ -473,68 +456,71 @@ function typeNodeName(node) {
473
456
  return result;
474
457
  }
475
458
  function readTypeDocs(sourceFile, typeName) {
476
- var result;
477
459
  var interfaceDecl = sourceFile.getInterface(typeName);
460
+ var result;
478
461
  if (interfaceDecl) {
479
- var typeDescription = readJsDocSummary(interfaceDecl);
480
- var hasApiParamsTag = hasJsDocFlag(interfaceDecl, 'dbxModelApiParams');
481
- var fields = [];
482
- var _iteratorNormalCompletion = true, _didIteratorError = false, _iteratorError = undefined;
483
- try {
484
- for(var _iterator = interfaceDecl.getProperties()[Symbol.iterator](), _step; !(_iteratorNormalCompletion = (_step = _iterator.next()).done); _iteratorNormalCompletion = true){
485
- var property = _step.value;
486
- var _ref;
487
- var fieldName = property.getName();
488
- var description = readJsDocSummary(property);
489
- var typeNode = property.getTypeNode();
490
- var typeText = (_ref = typeNode === null || typeNode === void 0 ? void 0 : typeNode.getText().trim()) !== null && _ref !== void 0 ? _ref : '';
491
- var adminOnly = hasJsDocFlag(property, 'dbxModelApiAdminOnly');
492
- var field = _object_spread$1({
493
- name: fieldName,
494
- typeText: typeText
495
- }, description ? {
496
- description: description
497
- } : {}, adminOnly ? {
498
- accessLevel: 'adminOnly'
499
- } : {});
500
- fields.push(field);
501
- }
502
- } catch (err) {
503
- _didIteratorError = true;
504
- _iteratorError = err;
505
- } finally{
506
- try {
507
- if (!_iteratorNormalCompletion && _iterator.return != null) {
508
- _iterator.return();
509
- }
510
- } finally{
511
- if (_didIteratorError) {
512
- throw _iteratorError;
513
- }
514
- }
515
- }
516
- if (typeDescription || fields.length > 0 || hasApiParamsTag) {
517
- result = _object_spread_props(_object_spread$1({}, typeDescription ? {
518
- typeDescription: typeDescription
519
- } : {}, fields.length > 0 ? {
520
- fields: fields
521
- } : {}), {
522
- hasApiParamsTag: hasApiParamsTag
523
- });
524
- }
462
+ result = readInterfaceTypeDocs(interfaceDecl);
525
463
  } else {
526
464
  var typeAlias = sourceFile.getTypeAlias(typeName);
527
- if (typeAlias) {
528
- var typeDescription1 = readJsDocSummary(typeAlias);
529
- if (typeDescription1) {
530
- result = {
531
- typeDescription: typeDescription1
532
- };
533
- }
534
- }
465
+ result = typeAlias ? readTypeAliasDocs(typeAlias) : undefined;
466
+ }
467
+ return result;
468
+ }
469
+ /**
470
+ * Reads the type-level JSDoc (summary, fields, `@dbxModelApiParams` flag) of a
471
+ * resolved interface declaration.
472
+ *
473
+ * @param interfaceDecl - The interface naming a params/result type.
474
+ * @returns The collected docs, or `undefined` when the interface has no description, fields, or marker.
475
+ */ function readInterfaceTypeDocs(interfaceDecl) {
476
+ var typeDescription = readJsDocSummary(interfaceDecl);
477
+ var hasApiParamsTag = hasJsDocFlag(interfaceDecl, 'dbxModelApiParams');
478
+ var fields = interfaceDecl.getProperties().map(function(property) {
479
+ return readInterfaceField(property);
480
+ });
481
+ var result;
482
+ if (typeDescription || fields.length > 0 || hasApiParamsTag) {
483
+ result = _object_spread_props$1(_object_spread$1({}, typeDescription ? {
484
+ typeDescription: typeDescription
485
+ } : {}, fields.length > 0 ? {
486
+ fields: fields
487
+ } : {}), {
488
+ hasApiParamsTag: hasApiParamsTag
489
+ });
535
490
  }
536
491
  return result;
537
492
  }
493
+ /**
494
+ * Reads one interface property into a {@link CrudEntryDocField}, capturing its
495
+ * type text, JSDoc summary, and `@dbxModelApiAdminOnly` access level.
496
+ *
497
+ * @param property - The interface property signature.
498
+ * @returns The doc field for the property.
499
+ */ function readInterfaceField(property) {
500
+ var _ref;
501
+ var _property_getTypeNode;
502
+ var description = readJsDocSummary(property);
503
+ var adminOnly = hasJsDocFlag(property, 'dbxModelApiAdminOnly');
504
+ return _object_spread$1({
505
+ name: property.getName(),
506
+ typeText: (_ref = (_property_getTypeNode = property.getTypeNode()) === null || _property_getTypeNode === void 0 ? void 0 : _property_getTypeNode.getText().trim()) !== null && _ref !== void 0 ? _ref : ''
507
+ }, description ? {
508
+ description: description
509
+ } : {}, adminOnly ? {
510
+ accessLevel: 'adminOnly'
511
+ } : {});
512
+ }
513
+ /**
514
+ * Reads the type-level JSDoc summary of a resolved type alias.
515
+ *
516
+ * @param typeAlias - The type alias naming a params/result type.
517
+ * @returns The collected docs, or `undefined` when the alias has no description.
518
+ */ function readTypeAliasDocs(typeAlias) {
519
+ var typeDescription = readJsDocSummary(typeAlias);
520
+ return typeDescription ? {
521
+ typeDescription: typeDescription
522
+ } : undefined;
523
+ }
538
524
  /**
539
525
  * Returns `true` when any JSDoc block on `node` carries the bare `@<tagName>` flag.
540
526
  *
@@ -650,8 +636,8 @@ function readTypeDocs(sourceFile, typeName) {
650
636
  function readJsDocSummary(node) {
651
637
  var result;
652
638
  var docs = node.getJsDocs();
653
- if (docs.length > 0) {
654
- var last = docs[docs.length - 1];
639
+ var last = docs.at(-1);
640
+ if (last) {
655
641
  var description = last.getDescription().trim();
656
642
  if (description.length > 0) {
657
643
  result = description;
@@ -688,6 +674,25 @@ function _object_spread(target) {
688
674
  }
689
675
  return target;
690
676
  }
677
+ function ownKeys(object, enumerableOnly) {
678
+ var keys = Object.keys(object);
679
+ if (Object.getOwnPropertySymbols) {
680
+ var symbols = Object.getOwnPropertySymbols(object);
681
+ keys.push.apply(keys, symbols);
682
+ }
683
+ return keys;
684
+ }
685
+ function _object_spread_props(target, source) {
686
+ source = source != null ? source : {};
687
+ if (Object.getOwnPropertyDescriptors) {
688
+ Object.defineProperties(target, Object.getOwnPropertyDescriptors(source));
689
+ } else {
690
+ ownKeys(Object(source)).forEach(function(key) {
691
+ Object.defineProperty(target, key, Object.getOwnPropertyDescriptor(source, key));
692
+ });
693
+ }
694
+ return target;
695
+ }
691
696
  var READ_LEVEL_VALUES = new Set([
692
697
  'system',
693
698
  'owner',
@@ -698,25 +703,6 @@ var SERVICE_FACTORY_TAG = 'dbxModelServiceFactory';
698
703
  var MCP_TOOL_NAME_SEGMENT_TAG = 'dbxModelMcpToolNameSegment';
699
704
  var MODEL_TYPE_VALUE_PATTERN = /^[a-z][A-Za-z0-9_$]*$/;
700
705
  var TOOL_NAME_SEGMENT_PATTERN = /^[A-Za-z][A-Za-z0-9_$]*$/;
701
- /**
702
- * TS utility/structural wrappers that don't change the field surface for
703
- * inheritance walks — `Partial<T>`, `Required<T>`, `Readonly<T>`,
704
- * `NonNullable<T>` preserve every property, and `Pick<T, K>` / `Omit<T, K>`
705
- * leave the original `T` reachable for long-name resolution. `MaybeMap<T>` is
706
- * the workspace's own pass-through that decorates each prop with `Maybe<…>`
707
- * without renaming. `extends` walks need to see through these to find the
708
- * concrete ancestor interface — `getExpression()` alone returns just the
709
- * leftmost identifier (`Partial`, `Pick`, …) and silently drops the inner
710
- * model, leaving every inherited `@dbxModelVariable` tag unreachable.
711
- */ var PASSTHROUGH_TYPE_WRAPPERS = new Set([
712
- 'Partial',
713
- 'Required',
714
- 'Readonly',
715
- 'NonNullable',
716
- 'MaybeMap',
717
- 'Pick',
718
- 'Omit'
719
- ]);
720
706
  var IDENTITY_FN = 'firestoreModelIdentity';
721
707
  var CONVERTER_FN_NAMES = [
722
708
  'snapshotConverterFunctions',
@@ -728,6 +714,7 @@ var OBJECT_ARRAY_FN = 'firestoreObjectArray';
728
714
  var SNAPSHOT_FN = 'snapshotConverterFunctions';
729
715
  var FIELDS_LITERAL_KEY = 'fields';
730
716
  var OBJECT_FIELD_KEY = 'objectField';
717
+ var FIRESTORE_FIELD_KEY = 'firestoreField';
731
718
  /**
732
719
  * Walks a single source file and reports raw model-extraction artifacts.
733
720
  * Best-effort: a malformed call shape leaves the corresponding entry out
@@ -774,7 +761,7 @@ function readIdentities(sourceFile) {
774
761
  var initializer = decl.getInitializer();
775
762
  if (!initializer || !tsMorph.Node.isCallExpression(initializer)) continue;
776
763
  if (initializer.getExpression().getText() !== IDENTITY_FN) continue;
777
- var parsed = parseIdentityArgs(initializer);
764
+ var parsed = dbxCli.parseFirestoreModelIdentityArgs(initializer.getArguments());
778
765
  if (parsed) {
779
766
  out.push(_object_spread({
780
767
  identityConst: decl.getName()
@@ -812,48 +799,6 @@ function readIdentities(sourceFile) {
812
799
  }
813
800
  return out;
814
801
  }
815
- function parseIdentityArgs(call) {
816
- var args = call.getArguments();
817
- var result;
818
- if (args.length === 1) {
819
- var modelType = stringLiteralValue(args[0]);
820
- if (modelType !== undefined) {
821
- result = {
822
- modelType: modelType,
823
- collectionPrefix: undefined,
824
- parentIdentityConst: undefined
825
- };
826
- }
827
- } else if (args.length === 2) {
828
- var first = stringLiteralValue(args[0]);
829
- if (first === undefined) {
830
- var modelType1 = stringLiteralValue(args[1]);
831
- if (modelType1 !== undefined) {
832
- result = {
833
- modelType: modelType1,
834
- collectionPrefix: undefined,
835
- parentIdentityConst: identifierName(args[0])
836
- };
837
- }
838
- } else {
839
- result = {
840
- modelType: first,
841
- collectionPrefix: stringLiteralValue(args[1]),
842
- parentIdentityConst: undefined
843
- };
844
- }
845
- } else if (args.length >= 3) {
846
- var modelType2 = stringLiteralValue(args[1]);
847
- if (modelType2 !== undefined) {
848
- result = {
849
- modelType: modelType2,
850
- collectionPrefix: stringLiteralValue(args[2]),
851
- parentIdentityConst: identifierName(args[0])
852
- };
853
- }
854
- }
855
- return result;
856
- }
857
802
  function readInterfaces(sourceFile) {
858
803
  var out = [];
859
804
  var _iteratorNormalCompletion = true, _didIteratorError = false, _iteratorError = undefined;
@@ -884,7 +829,7 @@ function buildInterface(decl) {
884
829
  var hasDbxModelTag = jsDocsHaveTag(jsDocs, 'dbxModel');
885
830
  var dbxModelRead = readDbxModelReadTag(jsDocs);
886
831
  var mcpToolNameSegment = readMcpToolNameSegmentTag(jsDocs);
887
- var extendsNames = decl.getExtends().map(resolveExtendsName);
832
+ var extendsNames = decl.getExtends().map(dbxCli.resolveExtendsName);
888
833
  var props = [];
889
834
  var _iteratorNormalCompletion = true, _didIteratorError = false, _iteratorError = undefined;
890
835
  try {
@@ -1134,48 +1079,6 @@ function readServiceFactoryModelType(jsDocs) {
1134
1079
  }
1135
1080
  return result;
1136
1081
  }
1137
- /**
1138
- * Resolves an `extends` clause to the concrete ancestor interface name,
1139
- * peeling any leading {@link PASSTHROUGH_TYPE_WRAPPERS}. Returns the leftmost
1140
- * identifier of the unwrapped expression so the inheritance walker can chain
1141
- * through utility-wrapped declarations like
1142
- * `extends Partial<MaybeMap<Omit<Base, '…'>>>`.
1143
- *
1144
- * @param expr - The `ExpressionWithTypeArguments` produced by `getExtends()`
1145
- * @returns The resolved interface name, or the original leftmost identifier when no inner reference is reachable.
1146
- */ function resolveExtendsName(expr) {
1147
- var head = expr.getExpression().getText();
1148
- var result = head;
1149
- if (PASSTHROUGH_TYPE_WRAPPERS.has(head)) {
1150
- var typeArgs = expr.getTypeArguments();
1151
- if (typeArgs.length > 0) {
1152
- var peeled = peelTypeNode(typeArgs[0]);
1153
- if (peeled !== undefined) {
1154
- result = peeled;
1155
- }
1156
- }
1157
- }
1158
- return result;
1159
- }
1160
- function peelTypeNode(node) {
1161
- var current = node;
1162
- while(tsMorph.Node.isParenthesizedTypeNode(current)){
1163
- current = current.getTypeNode();
1164
- }
1165
- var result;
1166
- if (tsMorph.Node.isTypeReference(current)) {
1167
- var name = current.getTypeName().getText();
1168
- if (PASSTHROUGH_TYPE_WRAPPERS.has(name)) {
1169
- var inner = current.getTypeArguments();
1170
- if (inner.length > 0) {
1171
- result = peelTypeNode(inner[0]);
1172
- }
1173
- } else {
1174
- result = name;
1175
- }
1176
- }
1177
- return result;
1178
- }
1179
1082
  function readConverters(sourceFile) {
1180
1083
  var out = [];
1181
1084
  var _iteratorNormalCompletion = true, _didIteratorError = false, _iteratorError = undefined;
@@ -1313,41 +1216,87 @@ function readNestedFromExpression(expr) {
1313
1216
  if (tsMorph.Node.isCallExpression(expr)) {
1314
1217
  var fnName = expr.getExpression().getText();
1315
1218
  if (fnName === SUB_OBJECT_FN || fnName === OBJECT_ARRAY_FN) {
1316
- var args = expr.getArguments();
1317
- if (args.length > 0) {
1318
- var config = args[0];
1319
- if (tsMorph.Node.isObjectLiteralExpression(config)) {
1320
- var objectField = readPropertyValue(config, OBJECT_FIELD_KEY);
1321
- if (objectField) {
1322
- var isArray = fnName === OBJECT_ARRAY_FN;
1323
- if (tsMorph.Node.isIdentifier(objectField)) {
1324
- result = {
1325
- ref: objectField.getText(),
1326
- isArray: isArray
1327
- };
1328
- } else if (tsMorph.Node.isObjectLiteralExpression(objectField)) {
1329
- var fieldsLiteral = readObjectProperty(objectField, FIELDS_LITERAL_KEY);
1330
- if (fieldsLiteral) {
1331
- var inlineFields = readFieldEntries(fieldsLiteral);
1332
- result = {
1333
- inline: {
1334
- converterConst: undefined,
1335
- factory: fnName,
1336
- interfaceName: readGenericInterfaceName(expr),
1337
- fields: inlineFields,
1338
- line: expr.getStartLineNumber()
1339
- },
1340
- isArray: isArray
1341
- };
1342
- }
1343
- }
1344
- }
1345
- }
1219
+ result = readNestedConverterCall(expr, fnName);
1220
+ }
1221
+ }
1222
+ return result;
1223
+ }
1224
+ /**
1225
+ * Reads the `objectField` (or `firestoreField`) argument of a
1226
+ * `firestoreSubObject` / `firestoreObjectArray` call into a
1227
+ * {@link NestedConverterMatch}.
1228
+ *
1229
+ * `firestoreObjectArray`'s config is a union: `{ objectField }` describes the
1230
+ * element shape directly, while `{ firestoreField }` points the element decode
1231
+ * at another field converter (a sub-object const or an inline
1232
+ * `firestoreSubObject(...)`). Both carriers resolve through the same nested path
1233
+ * — the `firestoreField` variant is what the timesheet day-level form uses.
1234
+ *
1235
+ * @param call - The nested-converter call expression.
1236
+ * @param fnName - The resolved factory name (`firestoreSubObject` or `firestoreObjectArray`).
1237
+ * @returns The nested-converter match, or `undefined` when the call shape is malformed.
1238
+ */ function readNestedConverterCall(call, fnName) {
1239
+ var result;
1240
+ var args = call.getArguments();
1241
+ if (args.length > 0) {
1242
+ var config = args[0];
1243
+ if (tsMorph.Node.isObjectLiteralExpression(config)) {
1244
+ var _readPropertyValue;
1245
+ var valueNode = (_readPropertyValue = readPropertyValue(config, OBJECT_FIELD_KEY)) !== null && _readPropertyValue !== void 0 ? _readPropertyValue : readPropertyValue(config, FIRESTORE_FIELD_KEY);
1246
+ if (valueNode) {
1247
+ result = buildNestedConverterMatch({
1248
+ objectField: valueNode,
1249
+ call: call,
1250
+ fnName: fnName
1251
+ });
1346
1252
  }
1347
1253
  }
1348
1254
  }
1349
1255
  return result;
1350
1256
  }
1257
+ /**
1258
+ * Builds a {@link NestedConverterMatch} from a resolved value node:
1259
+ * an identifier yields a converter `ref`, an object literal yields an `inline`
1260
+ * converter parsed from its `fields`, and a nested
1261
+ * `firestoreSubObject(...)` / `firestoreObjectArray(...)` call (the inline
1262
+ * `firestoreField: firestoreSubObject<T>({...})` form) is resolved recursively —
1263
+ * the outer factory still decides array-ness.
1264
+ *
1265
+ * @param input - The resolved value node, owning call expression, and factory name.
1266
+ * @returns The nested-converter match, or `undefined` when no shape applies.
1267
+ */ function buildNestedConverterMatch(input) {
1268
+ var objectField = input.objectField, call = input.call, fnName = input.fnName;
1269
+ var isArray = fnName === OBJECT_ARRAY_FN;
1270
+ var result;
1271
+ if (tsMorph.Node.isIdentifier(objectField)) {
1272
+ result = {
1273
+ ref: objectField.getText(),
1274
+ isArray: isArray
1275
+ };
1276
+ } else if (tsMorph.Node.isObjectLiteralExpression(objectField)) {
1277
+ var fieldsLiteral = readObjectProperty(objectField, FIELDS_LITERAL_KEY);
1278
+ if (fieldsLiteral) {
1279
+ result = {
1280
+ inline: {
1281
+ converterConst: undefined,
1282
+ factory: fnName,
1283
+ interfaceName: readGenericInterfaceName(call),
1284
+ fields: readFieldEntries(fieldsLiteral),
1285
+ line: call.getStartLineNumber()
1286
+ },
1287
+ isArray: isArray
1288
+ };
1289
+ }
1290
+ } else if (tsMorph.Node.isCallExpression(objectField)) {
1291
+ var nested = readNestedFromExpression(objectField);
1292
+ if (nested) {
1293
+ result = _object_spread_props(_object_spread({}, nested), {
1294
+ isArray: isArray
1295
+ });
1296
+ }
1297
+ }
1298
+ return result;
1299
+ }
1351
1300
  function readPropertyValue(literal, key) {
1352
1301
  var property = literal.getProperty(key);
1353
1302
  var result;
@@ -1638,20 +1587,6 @@ function firstParagraph(text) {
1638
1587
  }
1639
1588
  return collected.join(' ').trim();
1640
1589
  }
1641
- function stringLiteralValue(node) {
1642
- var result;
1643
- if (tsMorph.Node.isStringLiteral(node) || tsMorph.Node.isNoSubstitutionTemplateLiteral(node)) {
1644
- result = node.getLiteralText();
1645
- }
1646
- return result;
1647
- }
1648
- function identifierName(node) {
1649
- var result;
1650
- if (tsMorph.Node.isIdentifier(node)) {
1651
- result = node.getText();
1652
- }
1653
- return result;
1654
- }
1655
1590
 
1656
1591
  exports.extractCrudEntries = extractCrudEntries;
1657
1592
  exports.extractModelsFromSource = extractModelsFromSource;