@dereekb/dbx-components-mcp 13.11.6 → 13.11.8

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.
@@ -5,7 +5,7 @@ const require = __createRequire(import.meta.url);
5
5
  // packages/dbx-components-mcp/package.json
6
6
  var package_default = {
7
7
  name: "@dereekb/dbx-components-mcp",
8
- version: "13.11.6",
8
+ version: "13.11.8",
9
9
  type: "module",
10
10
  bin: {
11
11
  "dbx-components-mcp": "dbx-components-mcp.js"
@@ -20,20 +20,20 @@ var package_default = {
20
20
  "@angular/core": "21.2.11",
21
21
  "@angular/forms": "21.2.11",
22
22
  "@angular/material": "^21.2.9",
23
- "@dereekb/dbx-cli": "13.11.6",
24
- "@dereekb/dbx-core": "13.11.6",
25
- "@dereekb/dbx-firebase": "13.11.6",
26
- "@dereekb/dbx-form": "13.11.6",
27
- "@dereekb/dbx-web": "13.11.6",
28
- "@dereekb/firebase": "13.11.6",
29
- "@dereekb/firebase-server": "13.11.6",
30
- "@dereekb/rxjs": "13.11.6",
31
- "@dereekb/util": "13.11.6",
23
+ "@dereekb/dbx-cli": "13.11.8",
24
+ "@dereekb/dbx-core": "13.11.8",
25
+ "@dereekb/dbx-firebase": "13.11.8",
26
+ "@dereekb/dbx-form": "13.11.8",
27
+ "@dereekb/dbx-web": "13.11.8",
28
+ "@dereekb/firebase": "13.11.8",
29
+ "@dereekb/firebase-server": "13.11.8",
30
+ "@dereekb/rxjs": "13.11.8",
31
+ "@dereekb/util": "13.11.8",
32
32
  "@uirouter/angular": "21.0.0",
33
33
  rxjs: "^7.8.2"
34
34
  },
35
35
  devDependencies: {
36
- "@dereekb/model": "13.11.6"
36
+ "@dereekb/model": "13.11.8"
37
37
  }
38
38
  };
39
39
 
@@ -3253,7 +3253,8 @@ var DbxMcpConfig = type16({
3253
3253
  version: "1",
3254
3254
  "modelValidate?": {
3255
3255
  "maxFieldNameLength?": "number>=1",
3256
- "ignoredFieldNames?": "string[]"
3256
+ "ignoredFieldNames?": "string[]",
3257
+ "ignoredExternalParents?": "string[]"
3257
3258
  },
3258
3259
  "semanticTypes?": ClusterSection,
3259
3260
  "uiComponents?": ClusterSection,
@@ -8066,7 +8067,9 @@ function assembleFile(input) {
8066
8067
  const fields = buildFields({
8067
8068
  converter,
8068
8069
  inheritedProps,
8069
- enumNames
8070
+ enumNames,
8071
+ subObjectConstIndex: input.subObjectConstIndex,
8072
+ subObjectInterfaceIndex: input.subObjectInterfaceIndex
8070
8073
  });
8071
8074
  const referencedEnums = new Set(fields.flatMap((f) => f.enumRef ? [f.enumRef] : []));
8072
8075
  const relevantEnums = input.enums.filter((e) => referencedEnums.has(e.name));
@@ -8119,6 +8122,12 @@ function buildFields(input) {
8119
8122
  const enumRef = enumFromType ?? enumFromConverter;
8120
8123
  const optional = prop?.optional ?? f.converter.startsWith("optionalFirestore");
8121
8124
  const longName = resolveLongName(f.key, prop?.longName);
8125
+ const subObject = resolveFieldSubObject({
8126
+ converter: f.converter,
8127
+ subObjectConstIndex: input.subObjectConstIndex,
8128
+ subObjectInterfaceIndex: input.subObjectInterfaceIndex,
8129
+ enumNames: input.enumNames
8130
+ });
8122
8131
  const field = {
8123
8132
  name: f.key,
8124
8133
  longName,
@@ -8127,12 +8136,126 @@ function buildFields(input) {
8127
8136
  optional,
8128
8137
  ...prop?.description ? { description: prop.description } : {},
8129
8138
  ...enumRef ? { enumRef } : {},
8130
- ...prop?.syncFlag ? { syncFlag: prop.syncFlag } : {}
8139
+ ...prop?.syncFlag ? { syncFlag: prop.syncFlag } : {},
8140
+ ...subObject ? { subObject } : {}
8131
8141
  };
8132
8142
  out.push(field);
8133
8143
  }
8134
8144
  return out;
8135
8145
  }
8146
+ function resolveFieldSubObject(input) {
8147
+ const { converter, subObjectConstIndex, subObjectInterfaceIndex, enumNames } = input;
8148
+ if (!subObjectConstIndex || !subObjectInterfaceIndex) {
8149
+ return void 0;
8150
+ }
8151
+ const resolved = resolveSubObjectReference(converter, subObjectConstIndex);
8152
+ if (!resolved) {
8153
+ return void 0;
8154
+ }
8155
+ const iface = subObjectInterfaceIndex.get(resolved.interfaceName);
8156
+ if (!iface) {
8157
+ return void 0;
8158
+ }
8159
+ const fields = buildSubObjectFields({ iface, subObjectConstIndex, subObjectInterfaceIndex, enumNames, visited: /* @__PURE__ */ new Set([resolved.interfaceName]) });
8160
+ return { interfaceName: resolved.interfaceName, factoryKind: resolved.factoryKind, fields };
8161
+ }
8162
+ var INLINE_SUB_OBJECT_RE = /^(firestoreSubObject|firestoreObjectArray|firestoreMap)\s*<\s*([A-Za-z_$][A-Za-z0-9_$]*)\s*>/;
8163
+ var WRAPPED_OBJECT_FIELD_RE = /^(firestoreObjectArray|firestoreMap)\s*\(\s*\{\s*objectField:\s*([A-Za-z_$][A-Za-z0-9_$]*)/;
8164
+ function resolveSubObjectReference(converter, index) {
8165
+ let result;
8166
+ const trimmed = converter.trim();
8167
+ const inline = INLINE_SUB_OBJECT_RE.exec(trimmed);
8168
+ if (inline) {
8169
+ result = { interfaceName: inline[2], factoryKind: FACTORY_KIND_BY_NAME[inline[1]] };
8170
+ } else {
8171
+ const wrapped = WRAPPED_OBJECT_FIELD_RE.exec(trimmed);
8172
+ if (wrapped) {
8173
+ const entry = index.get(wrapped[2]);
8174
+ if (entry) {
8175
+ const outerKind = FACTORY_KIND_BY_NAME[wrapped[1]];
8176
+ result = { interfaceName: entry.interfaceName, factoryKind: outerKind };
8177
+ }
8178
+ } else {
8179
+ const entry = index.get(trimmed);
8180
+ if (entry) {
8181
+ result = { interfaceName: entry.interfaceName, factoryKind: entry.factoryKind };
8182
+ }
8183
+ }
8184
+ }
8185
+ return result;
8186
+ }
8187
+ var FACTORY_KIND_BY_NAME = {
8188
+ firestoreSubObject: "object",
8189
+ firestoreObjectArray: "array",
8190
+ firestoreMap: "map"
8191
+ };
8192
+ function buildSubObjectFields(input) {
8193
+ const out = [];
8194
+ for (const prop of input.iface.props) {
8195
+ out.push(buildSubObjectField({ prop, ...input }));
8196
+ }
8197
+ return out;
8198
+ }
8199
+ function buildSubObjectField(input) {
8200
+ const { prop, subObjectConstIndex, subObjectInterfaceIndex, enumNames, visited } = input;
8201
+ const enumRef = prop.tsType ? findEnumInType(prop.tsType, enumNames) : void 0;
8202
+ const longName = resolveLongName(prop.name, prop.longName);
8203
+ const nested = resolveNestedSubObjectFromType({ tsType: prop.tsType, subObjectConstIndex, subObjectInterfaceIndex, enumNames, visited });
8204
+ const field = {
8205
+ name: prop.name,
8206
+ longName,
8207
+ converter: "",
8208
+ ...prop.tsType ? { tsType: prop.tsType } : {},
8209
+ optional: prop.optional,
8210
+ ...prop.description ? { description: prop.description } : {},
8211
+ ...enumRef ? { enumRef } : {},
8212
+ ...prop.syncFlag ? { syncFlag: prop.syncFlag } : {},
8213
+ ...nested ? { subObject: nested } : {}
8214
+ };
8215
+ return field;
8216
+ }
8217
+ function resolveNestedSubObjectFromType(input) {
8218
+ const { tsType, subObjectInterfaceIndex, visited } = input;
8219
+ if (!tsType) return void 0;
8220
+ const peeled = peelArrayAndMaybe(tsType.trim());
8221
+ if (!peeled) return void 0;
8222
+ if (visited.has(peeled.name)) return void 0;
8223
+ const iface = subObjectInterfaceIndex.get(peeled.name);
8224
+ if (!iface) return void 0;
8225
+ const nestedVisited = new Set(visited);
8226
+ nestedVisited.add(peeled.name);
8227
+ const fields = buildSubObjectFields({
8228
+ iface,
8229
+ subObjectConstIndex: input.subObjectConstIndex,
8230
+ subObjectInterfaceIndex,
8231
+ enumNames: input.enumNames,
8232
+ visited: nestedVisited
8233
+ });
8234
+ return { interfaceName: peeled.name, factoryKind: peeled.array ? "array" : "object", fields };
8235
+ }
8236
+ function peelArrayAndMaybe(raw) {
8237
+ let current = raw;
8238
+ let array = false;
8239
+ const maybeMatch = /^Maybe<\s*(.+)\s*>$/.exec(current);
8240
+ if (maybeMatch) {
8241
+ current = maybeMatch[1].trim();
8242
+ }
8243
+ const readonlyArrayMatch = /^ReadonlyArray<\s*(.+)\s*>$/.exec(current);
8244
+ if (readonlyArrayMatch) {
8245
+ current = readonlyArrayMatch[1].trim();
8246
+ array = true;
8247
+ } else {
8248
+ const arrayMatch = /^(?:readonly\s+)?(.+?)(\s*\[\s*\])$/.exec(current);
8249
+ if (arrayMatch) {
8250
+ current = arrayMatch[1].trim();
8251
+ array = true;
8252
+ }
8253
+ }
8254
+ if (!/^[A-Z][A-Za-z0-9_$]*$/.test(current)) {
8255
+ return void 0;
8256
+ }
8257
+ return { name: current, array };
8258
+ }
8136
8259
  function resolveLongName(fieldName, propLongName) {
8137
8260
  let result;
8138
8261
  if (propLongName && LONG_NAME_RE.test(propLongName)) {
@@ -8314,14 +8437,18 @@ function peelTypeNode(node) {
8314
8437
  }
8315
8438
  function readInterfaceTags(jsDocs) {
8316
8439
  let dbxModel = false;
8440
+ let dbxModelSubObject = false;
8317
8441
  for (const jsDoc of jsDocs) {
8318
8442
  for (const tag of jsDoc.getTags()) {
8319
- if (tag.getTagName() === "dbxModel") {
8443
+ const tagName = tag.getTagName();
8444
+ if (tagName === "dbxModel") {
8320
8445
  dbxModel = true;
8446
+ } else if (tagName === "dbxModelSubObject") {
8447
+ dbxModelSubObject = true;
8321
8448
  }
8322
8449
  }
8323
8450
  }
8324
- return { dbxModel };
8451
+ return { dbxModel, dbxModelSubObject };
8325
8452
  }
8326
8453
  function readPropertyTags2(jsDocs) {
8327
8454
  let dbxModelVariable;
@@ -8526,6 +8653,40 @@ function extractGroupModelNames(text) {
8526
8653
  return [...seen].sort((a, b) => a.localeCompare(b));
8527
8654
  }
8528
8655
 
8656
+ // packages/dbx-components-mcp/src/scan/extract-models/find-sub-object-consts.ts
8657
+ import { Node as Node11 } from "ts-morph";
8658
+ var SUB_OBJECT_FACTORY_NAMES = /* @__PURE__ */ new Set(["firestoreSubObject", "firestoreObjectArray", "firestoreMap"]);
8659
+ function findSubObjectConsts(sf) {
8660
+ const out = [];
8661
+ for (const statement of sf.getVariableStatements()) {
8662
+ for (const decl of statement.getDeclarations()) {
8663
+ const entry = readSubObjectConst(decl);
8664
+ if (entry) out.push(entry);
8665
+ }
8666
+ }
8667
+ return out;
8668
+ }
8669
+ function readSubObjectConst(decl) {
8670
+ const initializer = decl.getInitializer();
8671
+ if (!initializer || !Node11.isCallExpression(initializer)) return void 0;
8672
+ const expr = initializer.getExpression();
8673
+ if (!Node11.isIdentifier(expr)) return void 0;
8674
+ const factoryName = expr.getText();
8675
+ if (!SUB_OBJECT_FACTORY_NAMES.has(factoryName)) return void 0;
8676
+ const interfaceName = readInterfaceTypeArg2(initializer);
8677
+ if (!interfaceName) return void 0;
8678
+ return { constName: decl.getName(), interfaceName, factoryName };
8679
+ }
8680
+ function readInterfaceTypeArg2(call) {
8681
+ const typeArgs = call.getTypeArguments();
8682
+ if (typeArgs.length === 0) return void 0;
8683
+ const first = typeArgs[0];
8684
+ if (!Node11.isTypeReference(first)) return void 0;
8685
+ const nameNode = first.getTypeName();
8686
+ if (!Node11.isIdentifier(nameNode)) return void 0;
8687
+ return nameNode.getText();
8688
+ }
8689
+
8529
8690
  // packages/dbx-components-mcp/src/scan/extract-models/infer-collection-kind.ts
8530
8691
  var COLLECTION_CALL_RE = /\.(firestoreCollection|rootSingleItemFirestoreCollection|firestoreCollectionWithParent|singleItemFirestoreCollection)\s*\(/g;
8531
8692
  var KIND_BY_METHOD = {
@@ -8571,14 +8732,37 @@ async function extractModels(input) {
8571
8732
  const models = [];
8572
8733
  const modelGroups = [];
8573
8734
  const errors = [];
8735
+ const subObjectConstIndex = /* @__PURE__ */ new Map();
8736
+ const subObjectInterfaceIndex = /* @__PURE__ */ new Map();
8737
+ const parsedFiles = [];
8574
8738
  for (const filePath of files) {
8575
8739
  const sourceFileRel = relative3(baseDir, filePath).split(sep3).join(posix.sep);
8576
8740
  try {
8577
8741
  const text = await readFile2(filePath, "utf8");
8578
- if (!text.includes("firestoreModelIdentity(") && !text.includes("@dbxModelGroup")) {
8742
+ const hasModelMarker = text.includes("firestoreModelIdentity(") || text.includes("@dbxModelGroup");
8743
+ const hasSubObjectMarker = text.includes("@dbxModelSubObject") || text.includes("firestoreSubObject") || text.includes("firestoreObjectArray") || text.includes("firestoreMap");
8744
+ if (!hasModelMarker && !hasSubObjectMarker) {
8579
8745
  continue;
8580
8746
  }
8581
- const sf = project.createSourceFile(`/scan/${basename2(filePath)}-${models.length}-${modelGroups.length}.ts`, text, { overwrite: true });
8747
+ const sf = project.createSourceFile(`/scan/${basename2(filePath)}-${parsedFiles.length}.ts`, text, { overwrite: true });
8748
+ parsedFiles.push({ filePath, sourceFileRel, sf, hasModelMarker });
8749
+ for (const c of findSubObjectConsts(sf)) {
8750
+ if (!subObjectConstIndex.has(c.constName)) {
8751
+ subObjectConstIndex.set(c.constName, { interfaceName: c.interfaceName, factoryKind: c.factoryName === "firestoreSubObject" ? "object" : c.factoryName === "firestoreObjectArray" ? "array" : "map" });
8752
+ }
8753
+ }
8754
+ for (const iface of findInterfaces(sf)) {
8755
+ if (iface.tags.dbxModelSubObject && !subObjectInterfaceIndex.has(iface.name)) {
8756
+ subObjectInterfaceIndex.set(iface.name, iface);
8757
+ }
8758
+ }
8759
+ } catch (error) {
8760
+ errors.push({ sourceFile: sourceFileRel, message: error instanceof Error ? error.message : String(error) });
8761
+ }
8762
+ }
8763
+ for (const { sourceFileRel, sf, hasModelMarker } of parsedFiles) {
8764
+ if (!hasModelMarker) continue;
8765
+ try {
8582
8766
  const assembled = assembleFile({
8583
8767
  sourcePackage,
8584
8768
  sourceFile: sourceFileRel,
@@ -8587,7 +8771,9 @@ async function extractModels(input) {
8587
8771
  converters: findConverters(sf),
8588
8772
  enums: findEnums(sf),
8589
8773
  modelGroups: findModelGroups(sf),
8590
- factoryKinds: findCollectionFactoryCalls(sf)
8774
+ factoryKinds: findCollectionFactoryCalls(sf),
8775
+ subObjectConstIndex,
8776
+ subObjectInterfaceIndex
8591
8777
  });
8592
8778
  for (const m of assembled.models) models.push(m);
8593
8779
  for (const g of assembled.modelGroups) modelGroups.push(g);
@@ -13041,15 +13227,44 @@ function applyFieldsFilter(model, filter) {
13041
13227
  for (const field of model.fields) {
13042
13228
  const nameLower = field.name.toLowerCase();
13043
13229
  const longNameLower = field.longName.toLowerCase();
13044
- if (filterSet.has(nameLower) || filterSet.has(longNameLower)) {
13230
+ let keep = false;
13231
+ if (filterSet.has(nameLower)) {
13232
+ matched.add(nameLower);
13233
+ keep = true;
13234
+ }
13235
+ if (filterSet.has(longNameLower)) {
13236
+ matched.add(longNameLower);
13237
+ keep = true;
13238
+ }
13239
+ if (field.subObject !== void 0 && subObjectMatches(field.subObject, filterSet, matched)) {
13240
+ keep = true;
13241
+ }
13242
+ if (keep) {
13045
13243
  kept.push(field);
13046
- if (filterSet.has(nameLower)) matched.add(nameLower);
13047
- if (filterSet.has(longNameLower)) matched.add(longNameLower);
13048
13244
  }
13049
13245
  }
13050
13246
  const unmatchedFilters = filter.filter((entry) => !matched.has(entry));
13051
13247
  return { kept, unmatchedFilters };
13052
13248
  }
13249
+ function subObjectMatches(subObject, filterSet, matched) {
13250
+ let any = false;
13251
+ for (const field of subObject.fields) {
13252
+ const nameLower = field.name.toLowerCase();
13253
+ const longNameLower = field.longName.toLowerCase();
13254
+ if (filterSet.has(nameLower)) {
13255
+ matched.add(nameLower);
13256
+ any = true;
13257
+ }
13258
+ if (filterSet.has(longNameLower)) {
13259
+ matched.add(longNameLower);
13260
+ any = true;
13261
+ }
13262
+ if (field.subObject !== void 0 && subObjectMatches(field.subObject, filterSet, matched)) {
13263
+ any = true;
13264
+ }
13265
+ }
13266
+ return any;
13267
+ }
13053
13268
  function firebaseModelStoreShape(model) {
13054
13269
  const result = model.parentIdentityConst ? "sub-collection" : "root";
13055
13270
  return result;
@@ -13098,18 +13313,66 @@ function appendFieldsTable(lines, fields, depth) {
13098
13313
  lines.push("| Field | Description |", "|-------|-------------|");
13099
13314
  for (const field of fields) {
13100
13315
  const desc = describeField(field);
13101
- lines.push(`| \`${field.name}\` | ${desc} |`);
13316
+ lines.push(`| \`${formatFieldLabel(field)}\` | ${desc} |`);
13102
13317
  }
13103
13318
  } else {
13104
13319
  lines.push("| Field | Description | Type | Converter |", "|-------|-------------|------|-----------|");
13105
13320
  for (const field of fields) {
13106
13321
  const desc = describeField(field);
13107
13322
  const ts = field.tsType ? `\`${field.tsType}\`` : "\u2013";
13108
- const conv = `\`${field.converter}\``;
13109
- lines.push(`| \`${field.name}\` | ${desc} | ${ts} | ${conv} |`);
13323
+ const conv = field.converter.length > 0 ? `\`${field.converter}\`` : "\u2013";
13324
+ lines.push(`| \`${formatFieldLabel(field)}\` | ${desc} | ${ts} | ${conv} |`);
13110
13325
  }
13111
13326
  }
13327
+ appendSubObjectSections(lines, fields, depth);
13328
+ }
13329
+ function formatFieldLabel(field) {
13330
+ if (field.longName && field.longName !== field.name) {
13331
+ return `${field.name} (${field.longName})`;
13332
+ }
13333
+ return field.name;
13334
+ }
13335
+ function appendSubObjectSections(lines, fields, depth) {
13336
+ for (const field of fields) {
13337
+ if (field.subObject === void 0) continue;
13338
+ appendSubObjectSection({ lines, parentLabel: formatFieldLabel(field), subObject: field.subObject, depth, headingLevel: 3 });
13339
+ }
13112
13340
  }
13341
+ function appendSubObjectSection(input) {
13342
+ const { lines, parentLabel, subObject, depth, headingLevel } = input;
13343
+ const heading = "#".repeat(Math.min(Math.max(headingLevel, 2), 6));
13344
+ const kindLabel = SUB_OBJECT_KIND_LABEL[subObject.factoryKind];
13345
+ lines.push("", `${heading} Sub-object: \`${parentLabel}\` \u2192 \`${subObject.interfaceName}\` (${kindLabel})`, "");
13346
+ if (depth === "brief") {
13347
+ lines.push("| Field | Description |", "|-------|-------------|");
13348
+ for (const field of subObject.fields) {
13349
+ const desc = describeField(field);
13350
+ lines.push(`| \`${formatFieldLabel(field)}\` | ${desc} |`);
13351
+ }
13352
+ } else {
13353
+ lines.push("| Field | Description | Type |", "|-------|-------------|------|");
13354
+ for (const field of subObject.fields) {
13355
+ const desc = describeField(field);
13356
+ const ts = field.tsType ? `\`${field.tsType}\`` : "\u2013";
13357
+ lines.push(`| \`${formatFieldLabel(field)}\` | ${desc} | ${ts} |`);
13358
+ }
13359
+ }
13360
+ for (const field of subObject.fields) {
13361
+ if (field.subObject === void 0) continue;
13362
+ appendSubObjectSection({
13363
+ lines,
13364
+ parentLabel: `${parentLabel}.${formatFieldLabel(field)}`,
13365
+ subObject: field.subObject,
13366
+ depth,
13367
+ headingLevel: headingLevel + 1
13368
+ });
13369
+ }
13370
+ }
13371
+ var SUB_OBJECT_KIND_LABEL = {
13372
+ object: "embedded object",
13373
+ array: "embedded array",
13374
+ map: "embedded map"
13375
+ };
13113
13376
  function describeField(field) {
13114
13377
  return (field.description ?? "\u2013").replaceAll("|", String.raw`\|`).replaceAll("\n", " ");
13115
13378
  }
@@ -13407,7 +13670,7 @@ var DBX_MODEL_LOOKUP_TOOL = {
13407
13670
  "Optional inputs:",
13408
13671
  ' \u2022 `scope`: `"all"` (default), `"upstream"` (only `@dereekb/firebase`), or `"downstream"` (only component packages).',
13409
13672
  " \u2022 `componentDirs`: explicit downstream package directories \u2014 overrides the default `components/*-firebase` discovery.",
13410
- ' \u2022 `fields`: when the topic resolves to a single model, restrict the rendered fields table to those whose persisted name (e.g. `"fs"`) or longName (e.g. `"fileState"`) appears in the list (case-insensitive). Enums prune to only those referenced by the kept fields. Ignored on catalog/shapes/not-found responses.'
13673
+ ' \u2022 `fields`: when the topic resolves to a single model, restrict the rendered fields table to those whose persisted name (e.g. `"fs"`) or longName (e.g. `"fileState"`) appears in the list (case-insensitive). Filters also match against `@dbxModelSubObject` field names / longNames reachable through a parent field\'s `firestoreSubObject<T>` / `firestoreObjectArray<T>` / `firestoreMap<T>` converter \u2014 the parent field is kept whenever any of its nested sub-object fields match. Enums prune to only those referenced by the kept fields. Ignored on catalog/shapes/not-found responses.'
13411
13674
  ].join("\n"),
13412
13675
  inputSchema: {
13413
13676
  type: "object",
@@ -13950,29 +14213,35 @@ function matchPrefix(value, prefixes) {
13950
14213
  var DBX_MODEL_DECODE_TOOL = {
13951
14214
  name: "dbx_model_decode",
13952
14215
  description: [
13953
- "Decode a raw @dereekb/firebase Firestore document \u2014 identify the model, expand abbreviated field names to their descriptions, decode enum integer values, and surface foreign-key relationships to other models.",
14216
+ "Decode a @dereekb/firebase Firestore document or model key.",
13954
14217
  "",
13955
- 'Pass `data` as either a JSON string (copied straight from the Firestore console) or an already-parsed object. Pass `model` to skip detection and target a specific model by interface name (`"StorageFile"`), identity constant (`"storageFileIdentity"`), `modelType` (`"storageFile"`), or collection prefix (`"sf"`).',
14218
+ 'Document mode \u2014 pass `data` as a JSON string (copied straight from the Firestore console) or an already-parsed object to identify the model, expand abbreviated field names, decode enum integer values, and surface foreign-key relationships. Optionally pass `model` to skip detection and target a specific model by interface name (`"StorageFile"`), identity constant (`"storageFileIdentity"`), `modelType` (`"storageFile"`), or collection prefix (`"sf"`).',
13956
14219
  "",
13957
- "Auto-detection uses the document's key field (if any), then falls back to scoring each registered model by how many of its distinctive fields are present."
14220
+ 'Key mode \u2014 pass just `key` (e.g. `"jwr/hkzQa9W6MpaP99RTlQJcImbWdZm2"`) to resolve the model identity and id from the collection prefix alone, no document required. Subcollection paths like `"nb/abc/nbn/def"` are walked end-to-end so the leaf model is reported alongside its parent chain.',
14221
+ "",
14222
+ "At least one of `data` or `key` is required."
13958
14223
  ].join("\n"),
13959
14224
  inputSchema: {
13960
14225
  type: "object",
13961
14226
  properties: {
13962
14227
  data: {
13963
- description: "Raw Firestore document \u2014 JSON string or already-parsed object.",
14228
+ description: "Raw Firestore document \u2014 JSON string or already-parsed object. Required for document mode.",
13964
14229
  oneOf: [{ type: "string" }, { type: "object" }]
13965
14230
  },
14231
+ key: {
14232
+ type: "string",
14233
+ description: 'Firestore model key (e.g. `"sf/abc123"` or subcollection path `"nb/abc/nbn/def"`). Used as the sole input in key mode, or as an extra detection hint when paired with `data`.'
14234
+ },
13966
14235
  model: {
13967
14236
  type: "string",
13968
14237
  description: "Optional model hint \u2014 interface name, identity const, model type, or collection prefix."
13969
14238
  }
13970
- },
13971
- required: ["data"]
14239
+ }
13972
14240
  }
13973
14241
  };
13974
14242
  var DecodeArgsType = type36({
13975
- data: "string | object",
14243
+ "data?": "string | object",
14244
+ "key?": "string",
13976
14245
  "model?": "string"
13977
14246
  });
13978
14247
  function parseDecodeArgs(raw) {
@@ -13982,6 +14251,7 @@ function parseDecodeArgs(raw) {
13982
14251
  }
13983
14252
  const result = {
13984
14253
  data: parsed.data,
14254
+ key: parsed.key,
13985
14255
  model: parsed.model
13986
14256
  };
13987
14257
  return result;
@@ -14055,6 +14325,78 @@ function buildPrefixMap() {
14055
14325
  }
14056
14326
  return out;
14057
14327
  }
14328
+ function decodeKeyPath(key, hint) {
14329
+ const trimmed = key.trim();
14330
+ if (trimmed.length === 0) {
14331
+ return { error: "Key is empty. Provide a Firestore key like `sf/abc123`." };
14332
+ }
14333
+ const segments = trimmed.split("/").filter((s) => s.length > 0);
14334
+ if (segments.length < 2 || segments.length % 2 !== 0) {
14335
+ return {
14336
+ error: `Invalid Firestore key '${trimmed}'. Expected an even number of segments (\`prefix/id\` pairs). Got ${segments.length} segment(s).`
14337
+ };
14338
+ }
14339
+ const decoded = [];
14340
+ const unresolved = [];
14341
+ for (let i = 0; i < segments.length; i += 2) {
14342
+ const prefix = segments[i];
14343
+ const id = segments[i + 1];
14344
+ const model = getFirebaseModelByPrefix(prefix);
14345
+ if (!model) {
14346
+ unresolved.push(prefix);
14347
+ }
14348
+ decoded.push({ prefix, id, model });
14349
+ }
14350
+ let leaf = decoded[decoded.length - 1];
14351
+ if (!leaf.model && hint) {
14352
+ const fromHint = getFirebaseModel(hint) ?? getFirebaseModelByPrefix(hint);
14353
+ if (fromHint) {
14354
+ leaf = { prefix: leaf.prefix, id: leaf.id, model: fromHint };
14355
+ }
14356
+ }
14357
+ const ancestors2 = decoded.slice(0, -1);
14358
+ return { leaf, ancestors: ancestors2, unresolvedPrefixes: unresolved };
14359
+ }
14360
+ function formatKeyDecode(input, rawKey) {
14361
+ const lines = [];
14362
+ lines.push("_Model decoded from key prefix._", "");
14363
+ const leaf = input.leaf;
14364
+ if (leaf.model) {
14365
+ lines.push(`**Leaf:** ${leaf.model.name}`);
14366
+ lines.push(`- identityConst: \`${leaf.model.identityConst}\``);
14367
+ lines.push(`- modelType: \`${leaf.model.modelType}\``);
14368
+ lines.push(`- prefix: \`${leaf.prefix}\``);
14369
+ lines.push(`- id: \`${leaf.id}\``);
14370
+ if (leaf.model.modelGroup) {
14371
+ lines.push(`- modelGroup: ${leaf.model.modelGroup}`);
14372
+ }
14373
+ if (leaf.model.parentIdentityConst) {
14374
+ lines.push(`- parentIdentityConst: \`${leaf.model.parentIdentityConst}\``);
14375
+ }
14376
+ lines.push(`- source: \`${leaf.model.sourcePackage}\` (${leaf.model.sourceFile})`);
14377
+ } else {
14378
+ lines.push(`**Leaf:** _unknown_ \u2014 no model registered for prefix \`${leaf.prefix}\``);
14379
+ lines.push(`- prefix: \`${leaf.prefix}\``);
14380
+ lines.push(`- id: \`${leaf.id}\``);
14381
+ }
14382
+ if (input.ancestors.length > 0) {
14383
+ lines.push("", "**Parent chain:**");
14384
+ for (const ancestor of input.ancestors) {
14385
+ if (ancestor.model) {
14386
+ lines.push(`- ${ancestor.model.name} \u2014 prefix \`${ancestor.prefix}\`, id \`${ancestor.id}\``);
14387
+ } else {
14388
+ lines.push(`- _unknown_ \u2014 prefix \`${ancestor.prefix}\` (id \`${ancestor.id}\`)`);
14389
+ }
14390
+ }
14391
+ }
14392
+ if (input.unresolvedPrefixes.length > 0) {
14393
+ const label = input.unresolvedPrefixes.length === 1 ? "prefix" : "prefixes";
14394
+ const list = input.unresolvedPrefixes.map((p) => `\`${p}\``).join(", ");
14395
+ lines.push("", `_Unresolved ${label}: ${list}. Run \`dbx_model_lookup\` to browse known models._`);
14396
+ }
14397
+ lines.push("", `_Key:_ \`${rawKey}\``);
14398
+ return lines.join("\n");
14399
+ }
14058
14400
  function runModelDecode(rawArgs) {
14059
14401
  let args;
14060
14402
  try {
@@ -14063,6 +14405,18 @@ function runModelDecode(rawArgs) {
14063
14405
  const message = err instanceof Error ? err.message : String(err);
14064
14406
  return toolError(message);
14065
14407
  }
14408
+ if (args.data === void 0 && args.key === void 0) {
14409
+ return toolError("Provide either `data` (a Firestore document) or `key` (a Firestore model key).");
14410
+ }
14411
+ if (args.data === void 0 && typeof args.key === "string") {
14412
+ const decoded = decodeKeyPath(args.key, args.model);
14413
+ if ("error" in decoded) {
14414
+ return toolError(decoded.error);
14415
+ }
14416
+ const text2 = formatKeyDecode(decoded, args.key);
14417
+ const result2 = { content: [{ type: "text", text: text2 }] };
14418
+ return result2;
14419
+ }
14066
14420
  let document;
14067
14421
  try {
14068
14422
  document = coerceToDocument(args.data);
@@ -14070,15 +14424,16 @@ function runModelDecode(rawArgs) {
14070
14424
  const message = err instanceof Error ? err.message : String(err);
14071
14425
  return toolError(message);
14072
14426
  }
14073
- const detection = detectModel(document.doc, document.extraKey, args.model);
14427
+ const extraKey = args.key ?? document.extraKey;
14428
+ const detection = detectModel(document.doc, extraKey, args.model);
14074
14429
  if (!detection) {
14075
- const message = buildUnmatchedMessage(args.model, document);
14430
+ const message = buildUnmatchedMessage(args.model, { ...document, extraKey });
14076
14431
  const result2 = { content: [{ type: "text", text: message }] };
14077
14432
  return result2;
14078
14433
  }
14079
14434
  const prefixes = buildPrefixMap();
14080
14435
  const header = buildDetectionHeader(detection);
14081
- const body = formatDecode({ model: detection.model, doc: document.doc, prefixes, extraKey: document.extraKey });
14436
+ const body = formatDecode({ model: detection.model, doc: document.doc, prefixes, extraKey });
14082
14437
  const text = `${header}
14083
14438
 
14084
14439
  ${body}`;
@@ -14279,7 +14634,15 @@ function createListAppTool(config) {
14279
14634
  }
14280
14635
 
14281
14636
  // packages/dbx-components-mcp/src/tools/model-validate/extract.ts
14282
- import { Node as Node11, Project as Project4, SyntaxKind as SyntaxKind4 } from "ts-morph";
14637
+ import { Node as Node12, Project as Project4, SyntaxKind as SyntaxKind4 } from "ts-morph";
14638
+
14639
+ // packages/dbx-components-mcp/src/tools/model-validate/types.ts
14640
+ var MAX_FIELD_NAME_LENGTH = 5;
14641
+ var ROOT_MODEL_ORDER = ["identity", "interface", "rolesType", "documentClass", "converter", "referenceFn", "collectionType", "collectionFn"];
14642
+ var SUBCOLLECTION_MODEL_ORDER = ["identity", "interface", "rolesType", "documentClass", "converter", "referenceFn", "collectionType", "collectionFactoryType", "collectionFn", "referenceGroupFn", "collectionGroupType", "collectionGroupFn"];
14643
+ var SUB_OBJECT_FACTORY_NAMES2 = ["firestoreSubObject", "firestoreObjectArray", "firestoreMap"];
14644
+
14645
+ // packages/dbx-components-mcp/src/tools/model-validate/extract.ts
14283
14646
  function extractFile(source) {
14284
14647
  const project = new Project4({ useInMemoryFileSystem: true, skipAddingFilesFromTsConfig: true });
14285
14648
  const sourceFile = project.createSourceFile(source.name, source.text, { overwrite: true });
@@ -14289,16 +14652,56 @@ function extractFile(source) {
14289
14652
  const firstModelLine = identities.length > 0 ? Math.min(...identities.map((i) => i.line)) : void 0;
14290
14653
  const models = identities.map((identity) => extractModel(sourceFile, identity));
14291
14654
  const dataInterfaces = findDataInterfaces(sourceFile, groupInterface);
14655
+ const subObjectCalls = findSubObjectFactoryCalls(sourceFile);
14292
14656
  const result = {
14293
14657
  name: source.name,
14294
14658
  groupInterface,
14295
14659
  groupTypes,
14296
14660
  firstModelLine,
14297
14661
  models,
14298
- dataInterfaces
14662
+ dataInterfaces,
14663
+ subObjectCalls
14299
14664
  };
14300
14665
  return result;
14301
14666
  }
14667
+ function findSubObjectFactoryCalls(sourceFile) {
14668
+ const out = [];
14669
+ const factorySet = new Set(SUB_OBJECT_FACTORY_NAMES2);
14670
+ for (const call of sourceFile.getDescendantsOfKind(SyntaxKind4.CallExpression)) {
14671
+ const expr = call.getExpression();
14672
+ if (!Node12.isIdentifier(expr)) {
14673
+ continue;
14674
+ }
14675
+ const factoryName = expr.getText();
14676
+ if (!factorySet.has(factoryName)) {
14677
+ continue;
14678
+ }
14679
+ const typeArgs = call.getTypeArguments();
14680
+ if (typeArgs.length === 0) {
14681
+ continue;
14682
+ }
14683
+ const typeArgName = readTypeReferenceIdentifier(typeArgs[0]);
14684
+ if (!typeArgName) {
14685
+ continue;
14686
+ }
14687
+ out.push({
14688
+ factoryName,
14689
+ typeArgName,
14690
+ line: call.getStartLineNumber()
14691
+ });
14692
+ }
14693
+ return out;
14694
+ }
14695
+ function readTypeReferenceIdentifier(typeNode) {
14696
+ if (!Node12.isTypeReference(typeNode)) {
14697
+ return void 0;
14698
+ }
14699
+ const nameNode = typeNode.getTypeName();
14700
+ if (!Node12.isIdentifier(nameNode)) {
14701
+ return void 0;
14702
+ }
14703
+ return nameNode.getText();
14704
+ }
14302
14705
  function findDataInterfaces(sourceFile, groupInterface) {
14303
14706
  const out = [];
14304
14707
  const groupName = groupInterface?.name;
@@ -14309,10 +14712,50 @@ function findDataInterfaces(sourceFile, groupInterface) {
14309
14712
  }
14310
14713
  const fields = extractInterfaceFields(iface);
14311
14714
  const dbxModelTag = readDbxModelTag(iface.getJsDocs());
14312
- out.push({ name, line: iface.getStartLineNumber(), dbxModelTag, fields });
14715
+ const dbxModelSubObjectTag = readDbxModelSubObjectTag(iface.getJsDocs());
14716
+ const extendsNames = readExtendsNames(iface);
14717
+ out.push({ name, line: iface.getStartLineNumber(), dbxModelTag, dbxModelSubObjectTag, extendsNames, fields });
14718
+ }
14719
+ return out;
14720
+ }
14721
+ function readExtendsNames(iface) {
14722
+ const out = [];
14723
+ for (const expr of iface.getExtends()) {
14724
+ const head = expr.getExpression().getText();
14725
+ let resolved = head;
14726
+ if (PASSTHROUGH_TYPE_WRAPPERS2.has(head)) {
14727
+ const typeArgs = expr.getTypeArguments();
14728
+ if (typeArgs.length > 0) {
14729
+ const peeled = peelExtendsTypeNode(typeArgs[0]);
14730
+ if (peeled !== void 0) {
14731
+ resolved = peeled;
14732
+ }
14733
+ }
14734
+ }
14735
+ out.push(resolved);
14313
14736
  }
14314
14737
  return out;
14315
14738
  }
14739
+ var PASSTHROUGH_TYPE_WRAPPERS2 = /* @__PURE__ */ new Set(["Partial", "Required", "Readonly", "NonNullable", "MaybeMap", "Pick", "Omit"]);
14740
+ function peelExtendsTypeNode(node) {
14741
+ let current = node;
14742
+ while (Node12.isParenthesizedTypeNode(current)) {
14743
+ current = current.getTypeNode();
14744
+ }
14745
+ let result;
14746
+ if (Node12.isTypeReference(current)) {
14747
+ const name = current.getTypeName().getText();
14748
+ if (PASSTHROUGH_TYPE_WRAPPERS2.has(name)) {
14749
+ const inner = current.getTypeArguments();
14750
+ if (inner.length > 0) {
14751
+ result = peelExtendsTypeNode(inner[0]);
14752
+ }
14753
+ } else {
14754
+ result = name;
14755
+ }
14756
+ }
14757
+ return result;
14758
+ }
14316
14759
  function extractInterfaceFields(iface) {
14317
14760
  const fields = [];
14318
14761
  for (const prop of iface.getProperties()) {
@@ -14379,6 +14822,17 @@ function readDbxModelTag(jsDocs) {
14379
14822
  }
14380
14823
  return result;
14381
14824
  }
14825
+ function readDbxModelSubObjectTag(jsDocs) {
14826
+ let result = false;
14827
+ for (const jsDoc of jsDocs) {
14828
+ for (const tag of jsDoc.getTags()) {
14829
+ if (tag.getTagName() === "dbxModelSubObject") {
14830
+ result = true;
14831
+ }
14832
+ }
14833
+ }
14834
+ return result;
14835
+ }
14382
14836
  function readDbxModelVariableTag(jsDocs) {
14383
14837
  let result;
14384
14838
  for (const jsDoc of jsDocs) {
@@ -14418,7 +14872,7 @@ function extractIdentityRefs(alias) {
14418
14872
  if (!typeNode) {
14419
14873
  return refs;
14420
14874
  }
14421
- if (Node11.isTypeQuery(typeNode)) {
14875
+ if (Node12.isTypeQuery(typeNode)) {
14422
14876
  refs.push(typeNode.getExprName().getText());
14423
14877
  }
14424
14878
  const typeofNodes = typeNode.getDescendantsOfKind(SyntaxKind4.TypeQuery);
@@ -14432,7 +14886,7 @@ function findIdentities2(sourceFile) {
14432
14886
  for (const statement of sourceFile.getVariableStatements()) {
14433
14887
  for (const decl of statement.getDeclarations()) {
14434
14888
  const initializer = decl.getInitializer();
14435
- if (!initializer || !Node11.isCallExpression(initializer)) {
14889
+ if (!initializer || !Node12.isCallExpression(initializer)) {
14436
14890
  continue;
14437
14891
  }
14438
14892
  if (initializer.getExpression().getText() !== "firestoreModelIdentity") {
@@ -14468,7 +14922,7 @@ function parseIdentityArgs(args) {
14468
14922
  }
14469
14923
  if (args.length === 3) {
14470
14924
  const [parentNode, nameNode, prefixNode] = args;
14471
- const parentRef = Node11.isIdentifier(parentNode) ? parentNode.getText() : void 0;
14925
+ const parentRef = Node12.isIdentifier(parentNode) ? parentNode.getText() : void 0;
14472
14926
  const result2 = {
14473
14927
  parentIdentityRef: parentRef,
14474
14928
  collectionName: stringLiteralValue2(nameNode),
@@ -14483,7 +14937,7 @@ function stringLiteralValue2(node) {
14483
14937
  if (!node) {
14484
14938
  return void 0;
14485
14939
  }
14486
- if (Node11.isStringLiteral(node) || Node11.isNoSubstitutionTemplateLiteral(node)) {
14940
+ if (Node12.isStringLiteral(node) || Node12.isNoSubstitutionTemplateLiteral(node)) {
14487
14941
  return node.getLiteralText();
14488
14942
  }
14489
14943
  return void 0;
@@ -14544,7 +14998,7 @@ function detectFactoryCallKind(sourceFile, factoryFnName) {
14544
14998
  const calls = fn.getDescendantsOfKind(SyntaxKind4.CallExpression);
14545
14999
  for (const call of calls) {
14546
15000
  const expr = call.getExpression();
14547
- if (!Node11.isPropertyAccessExpression(expr)) {
15001
+ if (!Node12.isPropertyAccessExpression(expr)) {
14548
15002
  continue;
14549
15003
  }
14550
15004
  const name = expr.getName();
@@ -15141,7 +15595,7 @@ var RULE_CATALOG = [
15141
15595
  severity: "warning",
15142
15596
  title: "A persisted field's `@dbxModelVariable <longName>` tag value matches the field's short name verbatim, defeating the tag's purpose (the long name should be the field's unabbreviated camelCase variable name)",
15143
15597
  whatItFlags: "A persisted field's `@dbxModelVariable <longName>` tag value matches the field's short name verbatim, defeating the tag's purpose (the long name should be the field's unabbreviated camelCase variable name).",
15144
- whenItApplies: "Every field on a `@dbxModel` interface where `@dbxModelVariable` is present and its value equals the field's name (e.g. field `h` tagged `@dbxModelVariable h`, field `ub` tagged `@dbxModelVariable ub`). The tag must always carry the unabbreviated form so the catalog and decoder surface a meaningful long name.",
15598
+ whenItApplies: "Every field on a `@dbxModel` interface or on a `@dbxModelSubObject`-tagged embedded sub-object interface where `@dbxModelVariable` is present and its value equals the field's name (e.g. field `h` tagged `@dbxModelVariable h`, field `ub` tagged `@dbxModelVariable ub`). The tag must always carry the unabbreviated form so the catalog and decoder surface a meaningful long name.",
15145
15599
  whenItDoesNotApply: "Fields whose name is already the unabbreviated camelCase form (e.g. `name`, `email`, `id`, `at`) \u2014 add those to `dbx-mcp.config.json` `modelValidate.ignoredFieldNames` to silence the warning workspace-wide.",
15146
15600
  canonicalFix: "Replace the tag value with the field's unabbreviated camelCase variable name (e.g. `@dbxModelVariable h` \u2192 `@dbxModelVariable hours`, `@dbxModelVariable ub` \u2192 `@dbxModelVariable usedBudget`). To exempt fields whose long name legitimately equals the short name, add them to `modelValidate.ignoredFieldNames`."
15147
15601
  },
@@ -15161,8 +15615,8 @@ var RULE_CATALOG = [
15161
15615
  severity: "warning",
15162
15616
  title: "A persisted field is missing its `@dbxModelVariable <name>` JSDoc tag",
15163
15617
  whatItFlags: "A persisted field is missing its `@dbxModelVariable <name>` JSDoc tag.",
15164
- whenItApplies: "Every field on a `@dbxModel` interface \u2014 the tag carries the human-readable long name the catalog and decoder use when surfacing the field to operators. The long name is the variable name the field would have unabbreviated, written in camelCase (e.g. `uid` \u2192 `userUid`, `n` \u2192 `name`, `crAt` \u2192 `createdAt`).",
15165
- whenItDoesNotApply: "Fields on embedded sub-object interfaces (no `@dbxModel` parent) and fields the project deliberately leaves untagged (rare).",
15618
+ whenItApplies: "Every field on a `@dbxModel` interface or on a `@dbxModelSubObject`-tagged embedded sub-object interface \u2014 the tag carries the human-readable long name the catalog and decoder use when surfacing the field to operators. The long name is the variable name the field would have unabbreviated, written in camelCase (e.g. `uid` \u2192 `userUid`, `n` \u2192 `name`, `crAt` \u2192 `createdAt`).",
15619
+ whenItDoesNotApply: "Fields on embedded sub-object interfaces that are not tagged with `@dbxModelSubObject` (the tag is opt-in) and fields the project deliberately leaves untagged (rare).",
15166
15620
  canonicalFix: "Append `@dbxModelVariable <longName>` to the field's JSDoc block, where `<longName>` is the field's unabbreviated camelCase variable name."
15167
15621
  },
15168
15622
  {
@@ -15349,6 +15803,48 @@ var RULE_CATALOG = [
15349
15803
  whenItDoesNotApply: "When intentionally module-private.",
15350
15804
  canonicalFix: "Add `export` to the alias."
15351
15805
  },
15806
+ {
15807
+ code: "MODEL_SUBOBJECT_NOT_TAGGED",
15808
+ source: "dbx_model_validate",
15809
+ severity: "warning",
15810
+ title: "An interface is referenced as the first generic type-argument of a sub-object factory call (`firestoreSubObject<T>(...)`, `firestoreObjectArray<T>(...)`, or `firestoreMap<T>(...)`) but its declaration carries neither `@dbxModel` nor `@dbxModelSubObject`. The interface therefore opts out of the workspace's `@dbxModelVariable` long-name conventions even though its fields are persisted to Firestore",
15811
+ whatItFlags: "An interface is referenced as the first generic type-argument of a sub-object factory call (`firestoreSubObject<T>(...)`, `firestoreObjectArray<T>(...)`, or `firestoreMap<T>(...)`) but its declaration carries neither `@dbxModel` nor `@dbxModelSubObject`. The interface therefore opts out of the workspace's `@dbxModelVariable` long-name conventions even though its fields are persisted to Firestore.",
15812
+ whenItApplies: "Any interface declared in the validated source set that is the type-arg of at least one sub-object factory call and lacks both `@dbxModel` and `@dbxModelSubObject`. The warning is emitted once per interface (de-duplicated across all call sites) and anchored at the interface declaration line. Unresolved type-args \u2014 interfaces declared outside the validated source set, generic parameters, inline types \u2014 are silently skipped to avoid false positives.",
15813
+ whenItDoesNotApply: "Interfaces already tagged with `@dbxModel` (top-level models) or `@dbxModelSubObject` (embedded sub-objects), and interfaces referenced only from outside the supplied source set (different package, etc.).",
15814
+ canonicalFix: "Add `@dbxModelSubObject` to the interface's JSDoc block so its fields are subject to `@dbxModelVariable` long-name checks. If the interface is actually a top-level Firestore model, declare a matching `firestoreModelIdentity` and tag it `@dbxModel` instead.",
15815
+ seeAlso: [
15816
+ {
15817
+ kind: "artifact",
15818
+ target: "firestore-model"
15819
+ }
15820
+ ]
15821
+ },
15822
+ {
15823
+ code: "MODEL_SUBOBJECT_PARENT_NOT_TAGGED",
15824
+ source: "dbx_model_validate",
15825
+ severity: "warning",
15826
+ title: "A `@dbxModelSubObject` interface extends a parent interface that is not itself tagged with `@dbxModel` or `@dbxModelSubObject`. The parent's persisted fields flow through the sub-object's converter but never reach the workspace's `@dbxModelVariable` long-name checks. Whether the agent acts depends on whether the parent is owned by this package, but the validator surfaces both classifications so the consuming agent (or developer) can make an informed call",
15827
+ whatItFlags: "A `@dbxModelSubObject` interface extends a parent interface that is not itself tagged with `@dbxModel` or `@dbxModelSubObject`. The parent's persisted fields flow through the sub-object's converter but never reach the workspace's `@dbxModelVariable` long-name checks. Whether the agent acts depends on whether the parent is owned by this package, but the validator surfaces both classifications so the consuming agent (or developer) can make an informed call.",
15828
+ whenItApplies: "Every `@dbxModelSubObject` interface whose `extends` heritage names an untagged parent. The parent is classified `in-package` when its declaration is found in the validated source set, otherwise `external`. Anchored at the sub-object interface line so the caller sees both the child and the parent in one place.",
15829
+ whenItDoesNotApply: "Parents already tagged with `@dbxModel` or `@dbxModelSubObject`; parents whose names appear in `modelValidate.ignoredExternalParents` (suppresses external-only warnings \u2014 in-package parents are never suppressed). Generic parameters, inline types, and TS utility wrappers (`Partial<T>`, `Pick<T, K>`, `Omit<T, K>`, `Readonly<T>`, `MaybeMap<T>`) are unwrapped to the underlying parent name before resolution.",
15830
+ canonicalFix: "In-package parent: add `@dbxModelSubObject` to the parent interface's JSDoc and tag each persisted field with `@dbxModelVariable <longName>`. Alternative: redeclare the inherited fields directly on the child with their own JSDoc + tag (useful when the parent is a shared shape that shouldn't commit to a single longName). External parent: redeclare the inherited fields on the child with `@dbxModelVariable <longName>` tags when surface long-names are needed, or add the parent name to `modelValidate.ignoredExternalParents` in `dbx-mcp.config.json` when the inherited fields are framework plumbing (e.g. `IndexRef.i`, `DateRange.start/end`).",
15831
+ seeAlso: [
15832
+ {
15833
+ kind: "artifact",
15834
+ target: "firestore-model"
15835
+ }
15836
+ ]
15837
+ },
15838
+ {
15839
+ code: "MODEL_SUBOBJECT_TAG_CONFLICT",
15840
+ source: "dbx_model_validate",
15841
+ severity: "error",
15842
+ title: "An interface declaration carries both `@dbxModel` and `@dbxModelSubObject` JSDoc tags. The two tags model mutually exclusive concepts \u2014 `@dbxModel` marks a top-level Firestore model registered in the catalog via `firestoreModelIdentity`, while `@dbxModelSubObject` marks an embedded sub-object interface persisted only as part of a parent model's converter \u2014 so an interface must carry at most one",
15843
+ whatItFlags: "An interface declaration carries both `@dbxModel` and `@dbxModelSubObject` JSDoc tags. The two tags model mutually exclusive concepts \u2014 `@dbxModel` marks a top-level Firestore model registered in the catalog via `firestoreModelIdentity`, while `@dbxModelSubObject` marks an embedded sub-object interface persisted only as part of a parent model's converter \u2014 so an interface must carry at most one.",
15844
+ whenItApplies: "Any interface JSDoc block where both `@dbxModel` and `@dbxModelSubObject` are present.",
15845
+ whenItDoesNotApply: "Interfaces carrying only one of the two tags (or neither).",
15846
+ canonicalFix: "Remove whichever tag does not describe the interface. If the interface has a matching `firestoreModelIdentity` call, keep `@dbxModel`; if it is only embedded inside another model via `firestoreSubObject<T>()`, keep `@dbxModelSubObject`."
15847
+ },
15352
15848
  {
15353
15849
  code: "SUB_MISSING_COLLECTION_FACTORY_FN",
15354
15850
  source: "dbx_model_validate",
@@ -16975,33 +17471,34 @@ function formatSeeAlso(ref) {
16975
17471
  return `Doc: ${ref.target}${tail}`;
16976
17472
  }
16977
17473
 
16978
- // packages/dbx-components-mcp/src/tools/model-validate/types.ts
16979
- var MAX_FIELD_NAME_LENGTH = 5;
16980
- var ROOT_MODEL_ORDER = ["identity", "interface", "rolesType", "documentClass", "converter", "referenceFn", "collectionType", "collectionFn"];
16981
- var SUBCOLLECTION_MODEL_ORDER = ["identity", "interface", "rolesType", "documentClass", "converter", "referenceFn", "collectionType", "collectionFactoryType", "collectionFn", "referenceGroupFn", "collectionGroupType", "collectionGroupFn"];
16982
-
16983
17474
  // packages/dbx-components-mcp/src/tools/model-validate/rules.ts
16984
- function runRules(file, options) {
17475
+ function runRules(file, options, context) {
16985
17476
  const violations = [];
16986
- if (file.models.length === 0) {
16987
- return violations;
16988
- }
16989
- checkFileLevel(file, violations);
16990
- for (const model of file.models) {
16991
- checkIdentity(file, model, violations);
16992
- checkCoreDeclarations(file, model, violations);
16993
- if (model.variant === "subcollection") {
16994
- checkSubcollectionExtras(file, model, violations);
16995
- }
16996
- checkDeclarationOrder(file, model, violations);
16997
- }
16998
- checkFieldNameLengths(file, violations, options);
17477
+ const hasModels = file.models.length > 0;
17478
+ if (hasModels) {
17479
+ checkFileLevel(file, violations);
17480
+ for (const model of file.models) {
17481
+ checkIdentity(file, model, violations);
17482
+ checkCoreDeclarations(file, model, violations);
17483
+ if (model.variant === "subcollection") {
17484
+ checkSubcollectionExtras(file, model, violations);
17485
+ }
17486
+ checkDeclarationOrder(file, model, violations);
17487
+ }
17488
+ }
17489
+ checkFieldNameLengths(file, violations, options, { restrictToTaggedInterfaces: !hasModels });
17490
+ checkSubObjectInterfaceTags(file, violations);
17491
+ checkSubObjectFactoryCallSites(file, violations, context);
17492
+ checkSubObjectParentNotTagged(file, violations, context, options);
16999
17493
  return violations;
17000
17494
  }
17001
- function checkFieldNameLengths(file, violations, options) {
17495
+ function checkFieldNameLengths(file, violations, options, scope) {
17002
17496
  const limit = options?.maxFieldNameLength ?? MAX_FIELD_NAME_LENGTH;
17003
17497
  const ignored = options?.ignoredFieldNames;
17004
17498
  for (const iface of file.dataInterfaces) {
17499
+ if (scope.restrictToTaggedInterfaces && !(iface.dbxModelTag || iface.dbxModelSubObjectTag)) {
17500
+ continue;
17501
+ }
17005
17502
  for (const field of iface.fields) {
17006
17503
  if (ignored?.has(field.name)) {
17007
17504
  continue;
@@ -17019,11 +17516,14 @@ function checkFieldNameLengths(file, violations, options) {
17019
17516
  });
17020
17517
  }
17021
17518
  }
17022
- checkFieldJsDocs(file, violations, options);
17519
+ checkFieldJsDocs(file, violations, options, scope);
17023
17520
  }
17024
- function checkFieldJsDocs(file, violations, options) {
17521
+ function checkFieldJsDocs(file, violations, options, scope) {
17025
17522
  const ignored = options?.ignoredFieldNames;
17026
17523
  for (const iface of file.dataInterfaces) {
17524
+ if (scope.restrictToTaggedInterfaces && !(iface.dbxModelTag || iface.dbxModelSubObjectTag)) {
17525
+ continue;
17526
+ }
17027
17527
  for (const field of iface.fields) {
17028
17528
  if (!field.jsDocFirstLine) {
17029
17529
  pushViolation(violations, {
@@ -17035,7 +17535,8 @@ function checkFieldJsDocs(file, violations, options) {
17035
17535
  model: iface.name
17036
17536
  });
17037
17537
  }
17038
- if (iface.dbxModelTag && field.dbxModelVariableTag === void 0) {
17538
+ const fieldRulesApply = iface.dbxModelTag || iface.dbxModelSubObjectTag;
17539
+ if (fieldRulesApply && field.dbxModelVariableTag === void 0) {
17039
17540
  pushViolation(violations, {
17040
17541
  code: "MODEL_FIELD_MISSING_VARIABLE_TAG",
17041
17542
  severity: "warning",
@@ -17045,7 +17546,7 @@ function checkFieldJsDocs(file, violations, options) {
17045
17546
  model: iface.name
17046
17547
  });
17047
17548
  }
17048
- if (iface.dbxModelTag && field.dbxModelVariableTag !== void 0 && field.dbxModelVariableTag === field.name && !ignored?.has(field.name)) {
17549
+ if (fieldRulesApply && field.dbxModelVariableTag !== void 0 && field.dbxModelVariableTag === field.name && !ignored?.has(field.name)) {
17049
17550
  pushViolation(violations, {
17050
17551
  code: "MODEL_FIELD_LONG_NAME_EQUALS_NAME",
17051
17552
  severity: "warning",
@@ -17058,6 +17559,95 @@ function checkFieldJsDocs(file, violations, options) {
17058
17559
  }
17059
17560
  }
17060
17561
  }
17562
+ function checkSubObjectInterfaceTags(file, violations) {
17563
+ for (const iface of file.dataInterfaces) {
17564
+ if (iface.dbxModelTag && iface.dbxModelSubObjectTag) {
17565
+ pushViolation(violations, {
17566
+ code: "MODEL_SUBOBJECT_TAG_CONFLICT",
17567
+ message: `Interface \`${iface.name}\` carries both \`@dbxModel\` and \`@dbxModelSubObject\`. Use only \`@dbxModel\` for top-level Firestore models; use \`@dbxModelSubObject\` only for embedded sub-objects without a \`firestoreModelIdentity\`.`,
17568
+ file: file.name,
17569
+ line: iface.line,
17570
+ model: iface.name
17571
+ });
17572
+ }
17573
+ }
17574
+ }
17575
+ function checkSubObjectFactoryCallSites(file, violations, context) {
17576
+ if (!context) {
17577
+ return;
17578
+ }
17579
+ if (file.subObjectCalls.length === 0) {
17580
+ return;
17581
+ }
17582
+ for (const call of file.subObjectCalls) {
17583
+ const entry = context.interfacesByName.get(call.typeArgName);
17584
+ if (!entry) {
17585
+ continue;
17586
+ }
17587
+ const { iface, file: declFile } = entry;
17588
+ if (iface.dbxModelTag || iface.dbxModelSubObjectTag) {
17589
+ continue;
17590
+ }
17591
+ if (context.emittedSubObjectInterfaces.has(call.typeArgName)) {
17592
+ continue;
17593
+ }
17594
+ context.emittedSubObjectInterfaces.add(call.typeArgName);
17595
+ pushViolation(violations, {
17596
+ code: "MODEL_SUBOBJECT_NOT_TAGGED",
17597
+ severity: "warning",
17598
+ message: `Interface \`${iface.name}\` is referenced by \`${call.factoryName}<${iface.name}>(...)\` at ${file.name}:${call.line} but carries neither \`@dbxModel\` nor \`@dbxModelSubObject\`. Tag the interface with \`@dbxModelSubObject\` so its persisted fields are subject to the same \`@dbxModelVariable\` long-name checks as a top-level model.`,
17599
+ file: declFile,
17600
+ line: iface.line,
17601
+ model: iface.name
17602
+ });
17603
+ }
17604
+ }
17605
+ function checkSubObjectParentNotTagged(file, violations, context, options) {
17606
+ if (!context) {
17607
+ return;
17608
+ }
17609
+ const ignoredExternal = options?.ignoredExternalParents;
17610
+ for (const iface of file.dataInterfaces) {
17611
+ if (!iface.dbxModelSubObjectTag) {
17612
+ continue;
17613
+ }
17614
+ for (const parentName of iface.extendsNames) {
17615
+ const dedupKey = `${iface.name}<-${parentName}`;
17616
+ if (context.emittedSubObjectInterfaces.has(dedupKey)) {
17617
+ continue;
17618
+ }
17619
+ const entry = context.interfacesByName.get(parentName);
17620
+ if (entry) {
17621
+ const parent = entry.iface;
17622
+ if (parent.dbxModelTag || parent.dbxModelSubObjectTag) {
17623
+ continue;
17624
+ }
17625
+ context.emittedSubObjectInterfaces.add(dedupKey);
17626
+ pushViolation(violations, {
17627
+ code: "MODEL_SUBOBJECT_PARENT_NOT_TAGGED",
17628
+ severity: "warning",
17629
+ message: `Interface \`${parentName}\` is extended by \`@dbxModelSubObject\` \`${iface.name}\`. \`${parentName}\` is declared in the same package (${entry.file}) and its fields are persisted via \`${iface.name}\`'s converter but are not validated. Fix (preferred): add \`@dbxModelSubObject\` to \`${parentName}\`'s JSDoc and tag each persisted field with \`@dbxModelVariable <longName>\`. Alternative: redeclare the inherited fields directly on \`${iface.name}\` with their own JSDoc + tag (useful when \`${parentName}\` is a shared shape that you do not want to commit to a single longName).`,
17630
+ file: entry.file,
17631
+ line: parent.line,
17632
+ model: parentName
17633
+ });
17634
+ } else {
17635
+ if (ignoredExternal?.has(parentName)) {
17636
+ continue;
17637
+ }
17638
+ context.emittedSubObjectInterfaces.add(dedupKey);
17639
+ pushViolation(violations, {
17640
+ code: "MODEL_SUBOBJECT_PARENT_NOT_TAGGED",
17641
+ severity: "warning",
17642
+ message: `Interface \`${parentName}\` is extended by \`@dbxModelSubObject\` \`${iface.name}\`, but \`${parentName}\` is declared outside this package (unresolved in the validated source set). Its persisted fields cannot be tagged from here. Decide: (1) if the inherited fields need explicit longNames in this catalog, redeclare them on \`${iface.name}\` with a JSDoc block carrying \`@dbxModelVariable <longName>\` (the redeclaration is structurally compatible \u2014 TypeScript treats it as a field narrowing, not a new property); (2) if the inherited fields are framework plumbing (e.g. \`IndexRef.i\`, \`DateRange.start/end\`) and surface longNames are not needed, suppress this specific warning by adding \`${parentName}\` to \`modelValidate.ignoredExternalParents\` in \`dbx-mcp.config.json\`.`,
17643
+ file: file.name,
17644
+ line: iface.line,
17645
+ model: iface.name
17646
+ });
17647
+ }
17648
+ }
17649
+ }
17650
+ }
17061
17651
  function checkFileLevel(file, violations) {
17062
17652
  const { groupInterface, groupTypes, firstModelLine, models, name } = file;
17063
17653
  if (groupInterface) {
@@ -17759,10 +18349,15 @@ function validateFirebaseModelSources(sources, options) {
17759
18349
  let modelsChecked = 0;
17760
18350
  let errorCount = 0;
17761
18351
  let warningCount = 0;
18352
+ const extracteds = [];
17762
18353
  for (const source of sources) {
17763
18354
  const extracted = extractFile(source);
18355
+ extracteds.push(extracted);
17764
18356
  modelsChecked += extracted.models.length;
17765
- const fileViolations = runRules(extracted, options);
18357
+ }
18358
+ const context = buildCrossFileRuleContext(extracteds);
18359
+ for (const extracted of extracteds) {
18360
+ const fileViolations = runRules(extracted, options, context);
17766
18361
  for (const v of fileViolations) {
17767
18362
  violations.push(v);
17768
18363
  if (v.severity === "error") {
@@ -17781,6 +18376,22 @@ function validateFirebaseModelSources(sources, options) {
17781
18376
  };
17782
18377
  return result;
17783
18378
  }
18379
+ function buildCrossFileRuleContext(extracteds) {
18380
+ const interfacesByName = /* @__PURE__ */ new Map();
18381
+ for (const file of extracteds) {
18382
+ for (const iface of file.dataInterfaces) {
18383
+ if (interfacesByName.has(iface.name)) {
18384
+ continue;
18385
+ }
18386
+ interfacesByName.set(iface.name, { file: file.name, iface });
18387
+ }
18388
+ }
18389
+ const context = {
18390
+ interfacesByName,
18391
+ emittedSubObjectInterfaces: /* @__PURE__ */ new Set()
18392
+ };
18393
+ return context;
18394
+ }
17784
18395
 
17785
18396
  // packages/dbx-components-mcp/src/tools/model-validate.tool.ts
17786
18397
  var DBX_MODEL_VALIDATE_TOOL = {
@@ -17835,7 +18446,7 @@ var modelValidateTool = /* @__PURE__ */ createModelValidateTool();
17835
18446
  var runModelValidate = modelValidateTool.run;
17836
18447
 
17837
18448
  // packages/dbx-components-mcp/src/tools/model-validate-api/extract.ts
17838
- import { Node as Node12, Project as Project5, SyntaxKind as SyntaxKind5 } from "ts-morph";
18449
+ import { Node as Node13, Project as Project5, SyntaxKind as SyntaxKind5 } from "ts-morph";
17839
18450
  function extractFile2(source) {
17840
18451
  const project = new Project5({ useInMemoryFileSystem: true, skipAddingFilesFromTsConfig: true });
17841
18452
  const sourceFile = project.createSourceFile(source.name, source.text, { overwrite: true });
@@ -18001,7 +18612,7 @@ function findCrudConfigType(sourceFile) {
18001
18612
  continue;
18002
18613
  }
18003
18614
  const typeNode = alias.getTypeNode();
18004
- const summary = typeNode && Node12.isTypeLiteral(typeNode) ? summarizeCrudConfigType(typeNode) : { keys: [], nonNullKeys: [], bareLeafParamsNames: [] };
18615
+ const summary = typeNode && Node13.isTypeLiteral(typeNode) ? summarizeCrudConfigType(typeNode) : { keys: [], nonNullKeys: [], bareLeafParamsNames: [] };
18005
18616
  const result = {
18006
18617
  name: alias.getName(),
18007
18618
  exported: alias.isExported(),
@@ -18015,14 +18626,14 @@ function findCrudConfigType(sourceFile) {
18015
18626
  return void 0;
18016
18627
  }
18017
18628
  function summarizeCrudConfigType(typeLiteral) {
18018
- if (!Node12.isTypeLiteral(typeLiteral)) {
18629
+ if (!Node13.isTypeLiteral(typeLiteral)) {
18019
18630
  return { keys: [], nonNullKeys: [], bareLeafParamsNames: [] };
18020
18631
  }
18021
18632
  const keys = [];
18022
18633
  const nonNullKeys = [];
18023
18634
  const bareLeafParamsNames = [];
18024
18635
  for (const member of typeLiteral.getMembers()) {
18025
- if (!Node12.isPropertySignature(member)) {
18636
+ if (!Node13.isPropertySignature(member)) {
18026
18637
  continue;
18027
18638
  }
18028
18639
  const name = member.getName();
@@ -18040,18 +18651,18 @@ function summarizeCrudConfigType(typeLiteral) {
18040
18651
  return { keys, nonNullKeys, bareLeafParamsNames };
18041
18652
  }
18042
18653
  function isNullLiteralType(node) {
18043
- if (Node12.isLiteralTypeNode(node)) {
18654
+ if (Node13.isLiteralTypeNode(node)) {
18044
18655
  const literal = node.getLiteral();
18045
- if (Node12.isNullLiteral(literal)) {
18656
+ if (Node13.isNullLiteral(literal)) {
18046
18657
  return true;
18047
18658
  }
18048
18659
  }
18049
18660
  return false;
18050
18661
  }
18051
18662
  function collectBareParamsLeaves(node, out) {
18052
- if (Node12.isTypeLiteral(node)) {
18663
+ if (Node13.isTypeLiteral(node)) {
18053
18664
  for (const member of node.getMembers()) {
18054
- if (!Node12.isPropertySignature(member)) {
18665
+ if (!Node13.isPropertySignature(member)) {
18055
18666
  continue;
18056
18667
  }
18057
18668
  const valueNode = member.getTypeNode();
@@ -18061,10 +18672,10 @@ function collectBareParamsLeaves(node, out) {
18061
18672
  }
18062
18673
  return;
18063
18674
  }
18064
- if (Node12.isTupleTypeNode(node)) {
18675
+ if (Node13.isTupleTypeNode(node)) {
18065
18676
  return;
18066
18677
  }
18067
- if (Node12.isTypeReference(node)) {
18678
+ if (Node13.isTypeReference(node)) {
18068
18679
  const name = node.getTypeName().getText();
18069
18680
  if (name.endsWith("Params")) {
18070
18681
  out.push(name);
@@ -18081,12 +18692,12 @@ function findCrudConfigConst(sourceFile) {
18081
18692
  const typeNode = decl.getTypeNode();
18082
18693
  const initializer = decl.getInitializer();
18083
18694
  const runtimeKeys = [];
18084
- if (initializer && Node12.isAsExpression(initializer)) {
18695
+ if (initializer && Node13.isAsExpression(initializer)) {
18085
18696
  const inner = initializer.getExpression();
18086
- if (Node12.isObjectLiteralExpression(inner)) {
18697
+ if (Node13.isObjectLiteralExpression(inner)) {
18087
18698
  runtimeKeys.push(...collectObjectLiteralKeys(inner));
18088
18699
  }
18089
- } else if (initializer && Node12.isObjectLiteralExpression(initializer)) {
18700
+ } else if (initializer && Node13.isObjectLiteralExpression(initializer)) {
18090
18701
  runtimeKeys.push(...collectObjectLiteralKeys(initializer));
18091
18702
  }
18092
18703
  const result = {
@@ -18104,7 +18715,7 @@ function findCrudConfigConst(sourceFile) {
18104
18715
  function collectObjectLiteralKeys(obj) {
18105
18716
  const out = [];
18106
18717
  for (const prop of obj.getProperties()) {
18107
- if (Node12.isPropertyAssignment(prop) || Node12.isShorthandPropertyAssignment(prop)) {
18718
+ if (Node13.isPropertyAssignment(prop) || Node13.isShorthandPropertyAssignment(prop)) {
18108
18719
  out.push(prop.getName());
18109
18720
  }
18110
18721
  }
@@ -18120,7 +18731,7 @@ function findFunctionMap(sourceFile) {
18120
18731
  const initializer = decl.getInitializer();
18121
18732
  let callsFactory = false;
18122
18733
  const factoryArgs = [];
18123
- if (initializer && Node12.isCallExpression(initializer)) {
18734
+ if (initializer && Node13.isCallExpression(initializer)) {
18124
18735
  const exprText = initializer.getExpression().getText();
18125
18736
  if (exprText === "callModelFirebaseFunctionMapFactory") {
18126
18737
  callsFactory = true;
@@ -18258,18 +18869,18 @@ function extractCastTargetName(node) {
18258
18869
  return void 0;
18259
18870
  }
18260
18871
  let current = node;
18261
- while (current && Node12.isAsExpression(current)) {
18872
+ while (current && Node13.isAsExpression(current)) {
18262
18873
  const typeNode = current.getTypeNode();
18263
18874
  if (!typeNode) {
18264
18875
  break;
18265
18876
  }
18266
- if (Node12.isTypeReference(typeNode)) {
18877
+ if (Node13.isTypeReference(typeNode)) {
18267
18878
  const refName = typeNode.getTypeName().getText();
18268
18879
  if (refName === "Type") {
18269
18880
  const args = typeNode.getTypeArguments();
18270
18881
  if (args.length === 1) {
18271
18882
  const arg = args[0];
18272
- if (Node12.isTypeReference(arg)) {
18883
+ if (Node13.isTypeReference(arg)) {
18273
18884
  return arg.getTypeName().getText();
18274
18885
  }
18275
18886
  return arg.getText();
@@ -18288,7 +18899,7 @@ function extractValidatorProperties(initializer) {
18288
18899
  }
18289
18900
  const out = [];
18290
18901
  for (const prop of obj.getProperties()) {
18291
- if (!Node12.isPropertyAssignment(prop)) {
18902
+ if (!Node13.isPropertyAssignment(prop)) {
18292
18903
  continue;
18293
18904
  }
18294
18905
  const propAssignment = prop;
@@ -18298,7 +18909,7 @@ function extractValidatorProperties(initializer) {
18298
18909
  const clean = optional ? stripped.slice(0, -1) : stripped;
18299
18910
  const valueNode = propAssignment.getInitializer();
18300
18911
  let hasClearable = false;
18301
- if (valueNode && Node12.isCallExpression(valueNode)) {
18912
+ if (valueNode && Node13.isCallExpression(valueNode)) {
18302
18913
  const exprText = valueNode.getExpression().getText();
18303
18914
  if (exprText === "clearable") {
18304
18915
  hasClearable = true;
@@ -18310,25 +18921,25 @@ function extractValidatorProperties(initializer) {
18310
18921
  }
18311
18922
  function findArktypeObjectLiteral(initializer) {
18312
18923
  let current = initializer;
18313
- while (current && Node12.isAsExpression(current)) {
18924
+ while (current && Node13.isAsExpression(current)) {
18314
18925
  current = current.getExpression();
18315
18926
  }
18316
- if (!current || !Node12.isCallExpression(current)) {
18927
+ if (!current || !Node13.isCallExpression(current)) {
18317
18928
  return void 0;
18318
18929
  }
18319
18930
  const expr = current.getExpression();
18320
- if (Node12.isIdentifier(expr) && expr.getText() === "type") {
18931
+ if (Node13.isIdentifier(expr) && expr.getText() === "type") {
18321
18932
  const args = current.getArguments();
18322
18933
  const arg = args[0];
18323
- if (arg && Node12.isObjectLiteralExpression(arg)) {
18934
+ if (arg && Node13.isObjectLiteralExpression(arg)) {
18324
18935
  return arg;
18325
18936
  }
18326
18937
  return void 0;
18327
18938
  }
18328
- if (Node12.isPropertyAccessExpression(expr) && expr.getName() === "merge") {
18939
+ if (Node13.isPropertyAccessExpression(expr) && expr.getName() === "merge") {
18329
18940
  const args = current.getArguments();
18330
18941
  const arg = args[0];
18331
- if (arg && Node12.isObjectLiteralExpression(arg)) {
18942
+ if (arg && Node13.isObjectLiteralExpression(arg)) {
18332
18943
  return arg;
18333
18944
  }
18334
18945
  }
@@ -19013,7 +19624,7 @@ import { readdir as readdir2, readFile as readFile4, stat as stat4 } from "node:
19013
19624
  import { join as join4, relative as relative4, sep as sep5 } from "node:path";
19014
19625
 
19015
19626
  // packages/dbx-cli/manifest-extract/src/lib/extract-crud.ts
19016
- import { Node as Node13, Project as Project6 } from "ts-morph";
19627
+ import { Node as Node14, Project as Project6 } from "ts-morph";
19017
19628
  var SUPPORTED_VERBS = /* @__PURE__ */ new Set(["create", "read", "update", "delete", "query"]);
19018
19629
  function extractCrudEntries(source) {
19019
19630
  const project = new Project6({ useInMemoryFileSystem: true, skipAddingFilesFromTsConfig: true });
@@ -19039,9 +19650,9 @@ function extractCrudEntries(source) {
19039
19650
  };
19040
19651
  if (crudConfigType) {
19041
19652
  const literal = crudConfigType.getTypeNode();
19042
- if (literal && Node13.isTypeLiteral(literal)) {
19653
+ if (literal && Node14.isTypeLiteral(literal)) {
19043
19654
  for (const member of literal.getMembers()) {
19044
- if (!Node13.isPropertySignature(member)) {
19655
+ if (!Node14.isPropertySignature(member)) {
19045
19656
  continue;
19046
19657
  }
19047
19658
  const modelName = member.getName();
@@ -19053,9 +19664,9 @@ function extractCrudEntries(source) {
19053
19664
  if (isNullLiteralType2(valueNode)) {
19054
19665
  continue;
19055
19666
  }
19056
- if (Node13.isTypeLiteral(valueNode)) {
19667
+ if (Node14.isTypeLiteral(valueNode)) {
19057
19668
  for (const verbMember of valueNode.getMembers()) {
19058
- if (!Node13.isPropertySignature(verbMember)) {
19669
+ if (!Node14.isPropertySignature(verbMember)) {
19059
19670
  continue;
19060
19671
  }
19061
19672
  const verbName = verbMember.getName();
@@ -19076,9 +19687,9 @@ function extractCrudEntries(source) {
19076
19687
  const functionTypeMap = findTypeAliasByEnding(sourceFile, "FunctionTypeMap");
19077
19688
  if (functionTypeMap) {
19078
19689
  const literal = functionTypeMap.getTypeNode();
19079
- if (literal && Node13.isTypeLiteral(literal)) {
19690
+ if (literal && Node14.isTypeLiteral(literal)) {
19080
19691
  for (const member of literal.getMembers()) {
19081
- if (!Node13.isPropertySignature(member)) {
19692
+ if (!Node14.isPropertySignature(member)) {
19082
19693
  continue;
19083
19694
  }
19084
19695
  const key = member.getName();
@@ -19146,9 +19757,9 @@ function inferGroupName2(sourceFile) {
19146
19757
  return void 0;
19147
19758
  }
19148
19759
  function isNullLiteralType2(node) {
19149
- if (Node13.isLiteralTypeNode(node)) {
19760
+ if (Node14.isLiteralTypeNode(node)) {
19150
19761
  const literal = node.getLiteral();
19151
- if (Node13.isNullLiteral(literal)) {
19762
+ if (Node14.isNullLiteral(literal)) {
19152
19763
  return true;
19153
19764
  }
19154
19765
  }
@@ -19156,9 +19767,9 @@ function isNullLiteralType2(node) {
19156
19767
  }
19157
19768
  function collectVerbEntries(input) {
19158
19769
  const { modelName, verb, valueNode, entries, fallbackDescription, resolveTypeDocs } = input;
19159
- if (Node13.isTypeLiteral(valueNode)) {
19770
+ if (Node14.isTypeLiteral(valueNode)) {
19160
19771
  for (const specMember of valueNode.getMembers()) {
19161
- if (!Node13.isPropertySignature(specMember)) {
19772
+ if (!Node14.isPropertySignature(specMember)) {
19162
19773
  continue;
19163
19774
  }
19164
19775
  const specifier = specMember.getName();
@@ -19200,7 +19811,7 @@ function collectVerbEntries(input) {
19200
19811
  });
19201
19812
  }
19202
19813
  function readTupleParamsResult(node) {
19203
- if (!Node13.isTupleTypeNode(node)) {
19814
+ if (!Node14.isTupleTypeNode(node)) {
19204
19815
  return void 0;
19205
19816
  }
19206
19817
  const elements = node.getElements();
@@ -19219,7 +19830,7 @@ function readBareParams(node) {
19219
19830
  return { params, result: void 0 };
19220
19831
  }
19221
19832
  function typeNodeName(node) {
19222
- if (Node13.isTypeReference(node)) {
19833
+ if (Node14.isTypeReference(node)) {
19223
19834
  return node.getTypeName().getText();
19224
19835
  }
19225
19836
  const text = node.getText().trim();
@@ -20058,7 +20669,7 @@ async function collectApiFiles2(rootAbs) {
20058
20669
  // packages/dbx-components-mcp/src/tools/model-api-validate-app/extract-handlers.ts
20059
20670
  import { readFile as readFile8 } from "node:fs/promises";
20060
20671
  import { join as join8 } from "node:path";
20061
- import { Node as Node14, Project as Project8 } from "ts-morph";
20672
+ import { Node as Node15, Project as Project8 } from "ts-morph";
20062
20673
  var HANDLER_MAP_REL = "src/app/function/model/crud.functions.ts";
20063
20674
  var VERB_TOKENS = [
20064
20675
  { token: "Create", verb: "create" },
@@ -20086,7 +20697,7 @@ async function extractHandlerEntries(apiAbs, apiDir) {
20086
20697
  const verb = matchVerbFromConstName(name);
20087
20698
  if (!verb) continue;
20088
20699
  const initializer = decl.getInitializer();
20089
- if (!initializer || !Node14.isObjectLiteralExpression(initializer)) continue;
20700
+ if (!initializer || !Node15.isObjectLiteralExpression(initializer)) continue;
20090
20701
  verbsFound.push(verb);
20091
20702
  collectVerbMap({ verb, mapLiteral: initializer, sourceFileRel, entries });
20092
20703
  }
@@ -20104,7 +20715,7 @@ function matchVerbFromConstName(name) {
20104
20715
  function collectVerbMap(input) {
20105
20716
  const { verb, mapLiteral, sourceFileRel, entries } = input;
20106
20717
  for (const prop of mapLiteral.getProperties()) {
20107
- if (!Node14.isPropertyAssignment(prop)) continue;
20718
+ if (!Node15.isPropertyAssignment(prop)) continue;
20108
20719
  const model = prop.getName();
20109
20720
  const value = prop.getInitializer();
20110
20721
  if (!value) continue;
@@ -20123,25 +20734,25 @@ function collectVerbMap(input) {
20123
20734
  }
20124
20735
  }
20125
20736
  function readSpecifierHandler(node) {
20126
- if (!Node14.isCallExpression(node)) return void 0;
20737
+ if (!Node15.isCallExpression(node)) return void 0;
20127
20738
  const expr = node.getExpression();
20128
20739
  if (!expr.getText().endsWith("onCallSpecifierHandler")) return void 0;
20129
20740
  const args = node.getArguments();
20130
20741
  if (args.length === 0) return void 0;
20131
20742
  const firstArg = args[0];
20132
- if (!Node14.isObjectLiteralExpression(firstArg)) return void 0;
20743
+ if (!Node15.isObjectLiteralExpression(firstArg)) return void 0;
20133
20744
  const out = /* @__PURE__ */ new Map();
20134
20745
  for (const prop of firstArg.getProperties()) {
20135
- if (!Node14.isPropertyAssignment(prop) && !Node14.isShorthandPropertyAssignment(prop)) continue;
20746
+ if (!Node15.isPropertyAssignment(prop) && !Node15.isShorthandPropertyAssignment(prop)) continue;
20136
20747
  const specifier = prop.getName();
20137
- const handlerName = readBareHandlerName(Node14.isPropertyAssignment(prop) ? prop.getInitializerOrThrow() : prop) ?? "(unresolved)";
20748
+ const handlerName = readBareHandlerName(Node15.isPropertyAssignment(prop) ? prop.getInitializerOrThrow() : prop) ?? "(unresolved)";
20138
20749
  out.set(specifier, handlerName);
20139
20750
  }
20140
20751
  return out;
20141
20752
  }
20142
20753
  function readBareHandlerName(node) {
20143
- if (Node14.isIdentifier(node)) return node.getText();
20144
- if (Node14.isShorthandPropertyAssignment(node)) return node.getName();
20754
+ if (Node15.isIdentifier(node)) return node.getText();
20755
+ if (Node15.isShorthandPropertyAssignment(node)) return node.getName();
20145
20756
  const text = node.getText().split(/\s|\(/)[0];
20146
20757
  if (/^[A-Za-z_]\w*$/.test(text)) return text;
20147
20758
  return void 0;
@@ -20639,7 +21250,7 @@ async function readModelSources(folderPath, files) {
20639
21250
  }
20640
21251
 
20641
21252
  // packages/dbx-components-mcp/src/tools/model-validate-folder/rules.ts
20642
- function runRules3(inspection) {
21253
+ function runRules3(inspection, options) {
20643
21254
  const violations = [];
20644
21255
  if (inspection.status === "not-found") {
20645
21256
  pushViolation3(violations, {
@@ -20672,16 +21283,16 @@ function runRules3(inspection) {
20672
21283
  }
20673
21284
  checkRequiredFiles(inspection, violations);
20674
21285
  checkStrayFiles(inspection, violations);
20675
- checkSourceContents(inspection, violations);
21286
+ checkSourceContents(inspection, violations, options);
20676
21287
  return violations;
20677
21288
  }
20678
- function checkSourceContents(inspection, violations) {
21289
+ function checkSourceContents(inspection, violations, options) {
20679
21290
  const sources = inspection.sources;
20680
21291
  if (!sources || sources.length === 0) {
20681
21292
  return;
20682
21293
  }
20683
21294
  const validatorSources = sources.map((s) => ({ name: s.filename, text: s.text }));
20684
- const result = validateFirebaseModelSources(validatorSources);
21295
+ const result = validateFirebaseModelSources(validatorSources, options);
20685
21296
  for (const v of result.violations) {
20686
21297
  pushViolation3(violations, {
20687
21298
  code: v.code,
@@ -20742,8 +21353,8 @@ function pushViolation3(buffer, violation) {
20742
21353
  }
20743
21354
 
20744
21355
  // packages/dbx-components-mcp/src/tools/model-validate-folder/validate.ts
20745
- function validateModelFolders(inspections) {
20746
- return aggregateFolderRules({ inspections, runRules: runRules3 });
21356
+ function validateModelFolders(inspections, options) {
21357
+ return aggregateFolderRules({ inspections, runRules: (inspection) => runRules3(inspection, options) });
20747
21358
  }
20748
21359
 
20749
21360
  // packages/dbx-components-mcp/src/tools/model-validate-folder.tool.ts
@@ -20777,12 +21388,16 @@ var DBX_MODEL_VALIDATE_FOLDER_TOOL = {
20777
21388
  }
20778
21389
  }
20779
21390
  };
20780
- var modelValidateFolderTool = createFolderValidateTool({
20781
- definition: DBX_MODEL_VALIDATE_FOLDER_TOOL,
20782
- inspectFolder,
20783
- validate: validateModelFolders,
20784
- format: formatResult3
20785
- });
21391
+ // @__NO_SIDE_EFFECTS__
21392
+ function createModelValidateFolderTool(options = {}) {
21393
+ const { ruleOptions } = options;
21394
+ return createFolderValidateTool({
21395
+ definition: DBX_MODEL_VALIDATE_FOLDER_TOOL,
21396
+ inspectFolder,
21397
+ validate: (inspections) => validateModelFolders(inspections, ruleOptions),
21398
+ format: formatResult3
21399
+ });
21400
+ }
20786
21401
 
20787
21402
  // packages/dbx-components-mcp/src/tools/model-store-scaffold.tool.ts
20788
21403
  import { type as type41 } from "arktype";
@@ -21343,7 +21958,7 @@ function paramsHasFixtureDependencies(params) {
21343
21958
  }
21344
21959
 
21345
21960
  // packages/dbx-components-mcp/src/tools/model-fixture-shared/extract.ts
21346
- import { Node as Node15, Project as Project9, SyntaxKind as SyntaxKind7 } from "ts-morph";
21961
+ import { Node as Node16, Project as Project9, SyntaxKind as SyntaxKind7 } from "ts-morph";
21347
21962
 
21348
21963
  // packages/dbx-components-mcp/src/tools/model-fixture-shared/framework-fixtures.ts
21349
21964
  var KNOWN_NON_MODEL_FIXTURE_FAMILIES = [
@@ -21455,7 +22070,7 @@ function extendsByName(cls, baseName) {
21455
22070
  const extendsClause = cls.getExtends();
21456
22071
  if (!extendsClause) return false;
21457
22072
  const expr = extendsClause.getExpression();
21458
- if (Node15.isIdentifier(expr)) {
22073
+ if (Node16.isIdentifier(expr)) {
21459
22074
  return expr.getText() === baseName;
21460
22075
  }
21461
22076
  return extendsClause.getText().startsWith(baseName);
@@ -21583,14 +22198,14 @@ function readContextFactory(decl) {
21583
22198
  const initializer = decl.getInitializer();
21584
22199
  if (!initializer) return void 0;
21585
22200
  let bodyExpr;
21586
- if (Node15.isArrowFunction(initializer) || Node15.isFunctionExpression(initializer)) {
22201
+ if (Node16.isArrowFunction(initializer) || Node16.isFunctionExpression(initializer)) {
21587
22202
  bodyExpr = readSingleReturnExpression(initializer);
21588
22203
  } else {
21589
22204
  bodyExpr = initializer;
21590
22205
  }
21591
- if (!bodyExpr || !Node15.isCallExpression(bodyExpr)) return void 0;
22206
+ if (!bodyExpr || !Node16.isCallExpression(bodyExpr)) return void 0;
21592
22207
  const callee = bodyExpr.getExpression();
21593
- if (!Node15.isIdentifier(callee)) return void 0;
22208
+ if (!Node16.isIdentifier(callee)) return void 0;
21594
22209
  const calleeName = callee.getText();
21595
22210
  let family;
21596
22211
  if (calleeName !== MODEL_FACTORY_NAME) {
@@ -21605,9 +22220,9 @@ function readContextFactory(decl) {
21605
22220
  let parentFixtureFieldFromGetCollection;
21606
22221
  if (args.length > 0) {
21607
22222
  const first = args[0];
21608
- if (Node15.isObjectLiteralExpression(first)) {
22223
+ if (Node16.isObjectLiteralExpression(first)) {
21609
22224
  const getCollection = first.getProperty("getCollection");
21610
- if (getCollection && Node15.isPropertyAssignment(getCollection)) {
22225
+ if (getCollection && Node16.isPropertyAssignment(getCollection)) {
21611
22226
  const fnInit = getCollection.getInitializer();
21612
22227
  const arity = readArrowParameterCount(fnInit);
21613
22228
  if (arity >= 2) {
@@ -21658,7 +22273,7 @@ function familyFromExtends(cls) {
21658
22273
  if (!ext) return void 0;
21659
22274
  const expr = ext.getExpression();
21660
22275
  let baseName;
21661
- if (Node15.isIdentifier(expr)) {
22276
+ if (Node16.isIdentifier(expr)) {
21662
22277
  baseName = expr.getText();
21663
22278
  } else {
21664
22279
  const text = ext.getText();
@@ -21671,9 +22286,9 @@ function familyFromExtends(cls) {
21671
22286
  function readSingleReturnExpression(fn) {
21672
22287
  const body = fn.getBody();
21673
22288
  if (!body) return void 0;
21674
- if (Node15.isBlock(body)) {
22289
+ if (Node16.isBlock(body)) {
21675
22290
  for (const stmt of body.getStatements()) {
21676
- if (Node15.isReturnStatement(stmt)) {
22291
+ if (Node16.isReturnStatement(stmt)) {
21677
22292
  return stmt.getExpression();
21678
22293
  }
21679
22294
  }
@@ -21683,19 +22298,19 @@ function readSingleReturnExpression(fn) {
21683
22298
  }
21684
22299
  function readArrowParameterCount(fn) {
21685
22300
  if (!fn) return 0;
21686
- if (Node15.isArrowFunction(fn) || Node15.isFunctionExpression(fn) || Node15.isFunctionDeclaration(fn) || Node15.isMethodDeclaration(fn)) {
22301
+ if (Node16.isArrowFunction(fn) || Node16.isFunctionExpression(fn) || Node16.isFunctionDeclaration(fn) || Node16.isMethodDeclaration(fn)) {
21687
22302
  return fn.getParameters().length;
21688
22303
  }
21689
22304
  return 0;
21690
22305
  }
21691
22306
  function readParamsAccessFieldName(fn) {
21692
22307
  if (!fn) return void 0;
21693
- if (!Node15.isArrowFunction(fn) && !Node15.isFunctionExpression(fn)) return void 0;
22308
+ if (!Node16.isArrowFunction(fn) && !Node16.isFunctionExpression(fn)) return void 0;
21694
22309
  const body = fn.getBody();
21695
22310
  if (!body) return void 0;
21696
22311
  for (const access2 of body.getDescendantsOfKind(SyntaxKind7.PropertyAccessExpression)) {
21697
22312
  const expr = access2.getExpression();
21698
- if (Node15.isIdentifier(expr) && expr.getText() === "params") {
22313
+ if (Node16.isIdentifier(expr) && expr.getText() === "params") {
21699
22314
  return access2.getName();
21700
22315
  }
21701
22316
  }
@@ -21736,9 +22351,9 @@ function collectSingletonsByFactory(sourceFile) {
21736
22351
  for (const stmt of sourceFile.getVariableStatements()) {
21737
22352
  for (const decl of stmt.getDeclarations()) {
21738
22353
  const init = decl.getInitializer();
21739
- if (!init || !Node15.isCallExpression(init)) continue;
22354
+ if (!init || !Node16.isCallExpression(init)) continue;
21740
22355
  const callee = init.getExpression();
21741
- if (!Node15.isIdentifier(callee)) continue;
22356
+ if (!Node16.isIdentifier(callee)) continue;
21742
22357
  out.set(callee.getText(), decl.getName());
21743
22358
  }
21744
22359
  }
@@ -22986,7 +23601,7 @@ import { resolve as resolve32 } from "node:path";
22986
23601
  import { type as type47 } from "arktype";
22987
23602
 
22988
23603
  // packages/dbx-components-mcp/src/tools/model-test-shared/extract.ts
22989
- import { Node as Node16, Project as Project11, SyntaxKind as SyntaxKind8 } from "ts-morph";
23604
+ import { Node as Node17, Project as Project11, SyntaxKind as SyntaxKind8 } from "ts-morph";
22990
23605
  var SPEC_FILE_VIRTUAL_PATH = "__spec__/spec.ts";
22991
23606
  var FIXTURE_CONTEXT_SUFFIX = "Context";
22992
23607
  var FIXTURE_FACTORY_SUFFIX = "ContextFactory";
@@ -23007,9 +23622,9 @@ function extractSpecTreeFromText(input) {
23007
23622
  const counts = { describe: 0, it: 0, fixture: 0 };
23008
23623
  const rootChildren = [];
23009
23624
  for (const stmt of sourceFile.getStatements()) {
23010
- if (!Node16.isExpressionStatement(stmt)) continue;
23625
+ if (!Node17.isExpressionStatement(stmt)) continue;
23011
23626
  const expr = stmt.getExpression();
23012
- if (!Node16.isCallExpression(expr)) continue;
23627
+ if (!Node17.isCallExpression(expr)) continue;
23013
23628
  const node = walkCall(expr, { prefix, knownFixtureNames, helperNames, counts });
23014
23629
  if (node !== void 0) {
23015
23630
  rootChildren.push(node);
@@ -23175,7 +23790,7 @@ function fixtureNode(input) {
23175
23790
  const { call, calleeName, ctx, line, endLine } = input;
23176
23791
  const args = call.getArguments();
23177
23792
  const argsObject = args[0];
23178
- const parentVars = Node16.isObjectLiteralExpression(argsObject) ? readParentVars(argsObject) : [];
23793
+ const parentVars = Node17.isObjectLiteralExpression(argsObject) ? readParentVars(argsObject) : [];
23179
23794
  const callback = findCallback(call);
23180
23795
  const varName = callback === void 0 ? void 0 : readCallbackVarName(callback);
23181
23796
  const model = stripFixturePrefix(calleeName, ctx.prefix);
@@ -23213,11 +23828,11 @@ function stripFixturePrefix(name, prefix) {
23213
23828
  }
23214
23829
  function readTitle(arg) {
23215
23830
  if (arg === void 0) return { title: void 0, titleIsTemplate: false };
23216
- if (Node16.isStringLiteral(arg) || Node16.isNoSubstitutionTemplateLiteral(arg)) {
23831
+ if (Node17.isStringLiteral(arg) || Node17.isNoSubstitutionTemplateLiteral(arg)) {
23217
23832
  const result2 = { title: arg.getLiteralText(), titleIsTemplate: false };
23218
23833
  return result2;
23219
23834
  }
23220
- if (Node16.isTemplateExpression(arg)) {
23835
+ if (Node17.isTemplateExpression(arg)) {
23221
23836
  const result2 = { title: arg.getText(), titleIsTemplate: true };
23222
23837
  return result2;
23223
23838
  }
@@ -23228,7 +23843,7 @@ function findCallback(call) {
23228
23843
  const args = call.getArguments();
23229
23844
  for (let i = args.length - 1; i >= 0; i--) {
23230
23845
  const a = args[i];
23231
- if (Node16.isArrowFunction(a) || Node16.isFunctionExpression(a)) {
23846
+ if (Node17.isArrowFunction(a) || Node17.isFunctionExpression(a)) {
23232
23847
  return a;
23233
23848
  }
23234
23849
  }
@@ -23239,7 +23854,7 @@ function readCallbackVarName(callback) {
23239
23854
  if (params.length === 0) return void 0;
23240
23855
  const first = params[0];
23241
23856
  const nameNode = first.getNameNode();
23242
- if (Node16.isIdentifier(nameNode)) {
23857
+ if (Node17.isIdentifier(nameNode)) {
23243
23858
  return nameNode.getText();
23244
23859
  }
23245
23860
  return void 0;
@@ -23247,13 +23862,13 @@ function readCallbackVarName(callback) {
23247
23862
  function readParentVars(obj) {
23248
23863
  const out = [];
23249
23864
  for (const prop of obj.getProperties()) {
23250
- if (Node16.isShorthandPropertyAssignment(prop)) {
23865
+ if (Node17.isShorthandPropertyAssignment(prop)) {
23251
23866
  out.push(prop.getName());
23252
23867
  continue;
23253
23868
  }
23254
- if (Node16.isPropertyAssignment(prop)) {
23869
+ if (Node17.isPropertyAssignment(prop)) {
23255
23870
  const init = prop.getInitializer();
23256
- if (init !== void 0 && Node16.isIdentifier(init)) {
23871
+ if (init !== void 0 && Node17.isIdentifier(init)) {
23257
23872
  out.push(init.getText());
23258
23873
  }
23259
23874
  }
@@ -23262,10 +23877,10 @@ function readParentVars(obj) {
23262
23877
  }
23263
23878
  function walkCallbackBody(callback, ctx) {
23264
23879
  const body = callback.getBody();
23265
- if (Node16.isBlock(body)) {
23880
+ if (Node17.isBlock(body)) {
23266
23881
  return walkBlock(body, ctx);
23267
23882
  }
23268
- if (Node16.isCallExpression(body)) {
23883
+ if (Node17.isCallExpression(body)) {
23269
23884
  const child = walkCall(body, ctx);
23270
23885
  return child === void 0 ? [] : [child];
23271
23886
  }
@@ -23274,9 +23889,9 @@ function walkCallbackBody(callback, ctx) {
23274
23889
  function walkBlock(block, ctx) {
23275
23890
  const children = [];
23276
23891
  for (const stmt of block.getStatements()) {
23277
- if (!Node16.isExpressionStatement(stmt)) continue;
23892
+ if (!Node17.isExpressionStatement(stmt)) continue;
23278
23893
  const expr = stmt.getExpression();
23279
- if (!Node16.isCallExpression(expr)) continue;
23894
+ if (!Node17.isCallExpression(expr)) continue;
23280
23895
  const child = walkCall(expr, ctx);
23281
23896
  if (child !== void 0) {
23282
23897
  children.push(child);
@@ -23286,7 +23901,7 @@ function walkBlock(block, ctx) {
23286
23901
  }
23287
23902
  function getCalleeName(call) {
23288
23903
  const expr = call.getExpression();
23289
- if (Node16.isIdentifier(expr)) return expr.getText();
23904
+ if (Node17.isIdentifier(expr)) return expr.getText();
23290
23905
  return void 0;
23291
23906
  }
23292
23907
  function collectHelpers(sourceFile) {
@@ -23302,7 +23917,7 @@ function collectHelpers(sourceFile) {
23302
23917
  for (const decl of v.getDeclarations()) {
23303
23918
  const init = decl.getInitializer();
23304
23919
  if (init === void 0) continue;
23305
- if (!Node16.isArrowFunction(init) && !Node16.isFunctionExpression(init)) continue;
23920
+ if (!Node17.isArrowFunction(init) && !Node17.isFunctionExpression(init)) continue;
23306
23921
  const usage = scanForDescribeOrIt(init);
23307
23922
  if (!usage.emitsDescribe && !usage.emitsIt) continue;
23308
23923
  out.push({ name: decl.getName(), line: decl.getStartLineNumber(), endLine: decl.getEndLineNumber(), emitsDescribe: usage.emitsDescribe, emitsIt: usage.emitsIt });
@@ -23316,7 +23931,7 @@ function scanForDescribeOrIt(node) {
23316
23931
  if (descendant.getKind() !== SyntaxKind8.CallExpression) return;
23317
23932
  const call = descendant;
23318
23933
  const expr = call.getExpression();
23319
- if (!Node16.isIdentifier(expr)) return;
23934
+ if (!Node17.isIdentifier(expr)) return;
23320
23935
  const name = expr.getText();
23321
23936
  if (DESCRIBE_NAMES.has(name)) usage.emitsDescribe = true;
23322
23937
  if (isItName(name)) usage.emitsIt = true;
@@ -24089,10 +24704,10 @@ var modelHierarchyTool = {
24089
24704
  };
24090
24705
 
24091
24706
  // packages/dbx-components-mcp/src/tools/storagefile-m-validate-app/extract.ts
24092
- import { Node as Node18, SyntaxKind as SyntaxKind10 } from "ts-morph";
24707
+ import { Node as Node19, SyntaxKind as SyntaxKind10 } from "ts-morph";
24093
24708
 
24094
24709
  // packages/dbx-components-mcp/src/tools/_validate/ast.ts
24095
- import { Node as Node17, Project as Project12, SyntaxKind as SyntaxKind9 } from "ts-morph";
24710
+ import { Node as Node18, Project as Project12, SyntaxKind as SyntaxKind9 } from "ts-morph";
24096
24711
  var COMPONENT_VIRTUAL_PREFIX = "__component__/";
24097
24712
  var API_VIRTUAL_PREFIX = "__api__/";
24098
24713
  // @__NO_SIDE_EFFECTS__
@@ -24117,28 +24732,28 @@ function componentRelPath(sourceFile) {
24117
24732
  }
24118
24733
  function unwrapAsExpressions(node) {
24119
24734
  let current = node;
24120
- while (current && Node17.isAsExpression(current)) {
24735
+ while (current && Node18.isAsExpression(current)) {
24121
24736
  current = current.getExpression();
24122
24737
  }
24123
24738
  return current;
24124
24739
  }
24125
24740
  function asObjectLiteral(node) {
24126
24741
  const inner = unwrapAsExpressions(node);
24127
- if (inner && Node17.isObjectLiteralExpression(inner)) {
24742
+ if (inner && Node18.isObjectLiteralExpression(inner)) {
24128
24743
  return inner;
24129
24744
  }
24130
24745
  return void 0;
24131
24746
  }
24132
24747
  function asArrayLiteral(node) {
24133
24748
  const inner = unwrapAsExpressions(node);
24134
- if (inner && Node17.isArrayLiteralExpression(inner)) {
24749
+ if (inner && Node18.isArrayLiteralExpression(inner)) {
24135
24750
  return inner;
24136
24751
  }
24137
24752
  return void 0;
24138
24753
  }
24139
24754
  function readStringLiteralInitializer(decl) {
24140
24755
  const initializer = unwrapAsExpressions(decl.getInitializer());
24141
- if (initializer && Node17.isStringLiteral(initializer)) {
24756
+ if (initializer && Node18.isStringLiteral(initializer)) {
24142
24757
  return initializer.getLiteralText();
24143
24758
  }
24144
24759
  return void 0;
@@ -24150,37 +24765,37 @@ function typeAnnotationText(node) {
24150
24765
  function getPropertyInitializer(obj, name) {
24151
24766
  const prop = obj.getProperty(name);
24152
24767
  if (!prop) return void 0;
24153
- if (Node17.isPropertyAssignment(prop)) {
24768
+ if (Node18.isPropertyAssignment(prop)) {
24154
24769
  return prop.getInitializer();
24155
24770
  }
24156
- if (Node17.isShorthandPropertyAssignment(prop)) {
24771
+ if (Node18.isShorthandPropertyAssignment(prop)) {
24157
24772
  return prop.getNameNode();
24158
24773
  }
24159
24774
  return void 0;
24160
24775
  }
24161
24776
  function readStringProperty2(obj, name) {
24162
24777
  const init = unwrapAsExpressions(getPropertyInitializer(obj, name));
24163
- if (init && Node17.isStringLiteral(init)) {
24778
+ if (init && Node18.isStringLiteral(init)) {
24164
24779
  return init.getLiteralText();
24165
24780
  }
24166
24781
  return void 0;
24167
24782
  }
24168
24783
  function readIdentifierProperty(obj, name) {
24169
24784
  const init = unwrapAsExpressions(getPropertyInitializer(obj, name));
24170
- if (init && Node17.isIdentifier(init)) {
24785
+ if (init && Node18.isIdentifier(init)) {
24171
24786
  return init.getText();
24172
24787
  }
24173
24788
  return void 0;
24174
24789
  }
24175
24790
  function findReturnExpression(fn) {
24176
- if (!Node17.isArrowFunction(fn) && !Node17.isFunctionDeclaration(fn) && !Node17.isFunctionExpression(fn)) {
24791
+ if (!Node18.isArrowFunction(fn) && !Node18.isFunctionDeclaration(fn) && !Node18.isFunctionExpression(fn)) {
24177
24792
  return void 0;
24178
24793
  }
24179
24794
  const body = fn.getBody();
24180
24795
  if (!body) return void 0;
24181
- if (Node17.isBlock(body)) {
24796
+ if (Node18.isBlock(body)) {
24182
24797
  for (const stmt of body.getStatements()) {
24183
- if (Node17.isReturnStatement(stmt)) {
24798
+ if (Node18.isReturnStatement(stmt)) {
24184
24799
  return stmt.getExpression();
24185
24800
  }
24186
24801
  }
@@ -24200,7 +24815,7 @@ function findLocalVariable(sf, name) {
24200
24815
  return void 0;
24201
24816
  }
24202
24817
  function collectTypeofReferences(node, out) {
24203
- if (Node17.isTypeQuery(node)) {
24818
+ if (Node18.isTypeQuery(node)) {
24204
24819
  out.push(node.getExprName().getText());
24205
24820
  return;
24206
24821
  }
@@ -24402,7 +25017,7 @@ function buildApiFunctionIndex(sources) {
24402
25017
  for (const decl of stmt.getDeclarations()) {
24403
25018
  const initializer = unwrapAsExpressions(decl.getInitializer());
24404
25019
  if (!initializer) continue;
24405
- if (Node18.isArrowFunction(initializer) || Node18.isFunctionExpression(initializer)) {
25020
+ if (Node19.isArrowFunction(initializer) || Node19.isFunctionExpression(initializer)) {
24406
25021
  map.set(decl.getName(), { node: initializer, sourceFile: sf });
24407
25022
  }
24408
25023
  }
@@ -24415,34 +25030,34 @@ function collectInitializerBindingsFromValue(options) {
24415
25030
  const { node, sf, index, resolved, unresolved, visited } = options;
24416
25031
  const inner = unwrapAsExpressions(node);
24417
25032
  if (!inner) return false;
24418
- if (Node18.isArrayLiteralExpression(inner)) {
25033
+ if (Node19.isArrayLiteralExpression(inner)) {
24419
25034
  for (const el of inner.getElements()) {
24420
- if (Node18.isSpreadElement(el)) {
25035
+ if (Node19.isSpreadElement(el)) {
24421
25036
  const spreadInner = el.getExpression();
24422
- if (Node18.isIdentifier(spreadInner)) {
25037
+ if (Node19.isIdentifier(spreadInner)) {
24423
25038
  collectInitializerBindingsFromIdentifier({ name: spreadInner.getText(), sf, index, resolved, unresolved, visited });
24424
- } else if (Node18.isCallExpression(spreadInner)) {
25039
+ } else if (Node19.isCallExpression(spreadInner)) {
24425
25040
  const callee = spreadInner.getExpression();
24426
- if (Node18.isIdentifier(callee)) {
25041
+ if (Node19.isIdentifier(callee)) {
24427
25042
  collectInitializerBindingsFromIdentifier({ name: callee.getText(), sf, index, resolved, unresolved, visited });
24428
25043
  }
24429
25044
  }
24430
25045
  continue;
24431
25046
  }
24432
25047
  const elementInner = unwrapAsExpressions(el);
24433
- if (elementInner && Node18.isIdentifier(elementInner)) {
25048
+ if (elementInner && Node19.isIdentifier(elementInner)) {
24434
25049
  resolved.push(elementInner.getText());
24435
25050
  }
24436
25051
  }
24437
25052
  return true;
24438
25053
  }
24439
- if (Node18.isCallExpression(inner)) {
25054
+ if (Node19.isCallExpression(inner)) {
24440
25055
  const callee = inner.getExpression();
24441
- if (Node18.isIdentifier(callee)) {
25056
+ if (Node19.isIdentifier(callee)) {
24442
25057
  return collectInitializerBindingsFromIdentifier({ name: callee.getText(), sf, index, resolved, unresolved, visited });
24443
25058
  }
24444
25059
  }
24445
- if (Node18.isIdentifier(inner)) {
25060
+ if (Node19.isIdentifier(inner)) {
24446
25061
  return collectInitializerBindingsFromIdentifier({ name: inner.getText(), sf, index, resolved, unresolved, visited });
24447
25062
  }
24448
25063
  return false;
@@ -24481,15 +25096,15 @@ function collectUploadInitializerArray(options) {
24481
25096
  const { initializerArr, sf, index, direct, spreads, resolved, unresolved } = options;
24482
25097
  const visited = /* @__PURE__ */ new Set();
24483
25098
  for (const el of initializerArr.getElements()) {
24484
- if (Node18.isSpreadElement(el)) {
25099
+ if (Node19.isSpreadElement(el)) {
24485
25100
  const inner2 = el.getExpression();
24486
- if (Node18.isIdentifier(inner2)) {
25101
+ if (Node19.isIdentifier(inner2)) {
24487
25102
  const name = inner2.getText();
24488
25103
  spreads.push(name);
24489
25104
  collectInitializerBindingsFromIdentifier({ name, sf, index, resolved, unresolved, visited });
24490
- } else if (Node18.isCallExpression(inner2)) {
25105
+ } else if (Node19.isCallExpression(inner2)) {
24491
25106
  const callee = inner2.getExpression();
24492
- if (Node18.isIdentifier(callee)) {
25107
+ if (Node19.isIdentifier(callee)) {
24493
25108
  const name = callee.getText();
24494
25109
  spreads.push(name);
24495
25110
  collectInitializerBindingsFromIdentifier({ name, sf, index, resolved, unresolved, visited });
@@ -24498,7 +25113,7 @@ function collectUploadInitializerArray(options) {
24498
25113
  continue;
24499
25114
  }
24500
25115
  const inner = unwrapAsExpressions(el);
24501
- if (inner && Node18.isIdentifier(inner)) {
25116
+ if (inner && Node19.isIdentifier(inner)) {
24502
25117
  const name = inner.getText();
24503
25118
  direct.push(name);
24504
25119
  resolved.push(name);
@@ -24511,7 +25126,7 @@ function extractUploadServiceCalls(sources, index) {
24511
25126
  const rel = apiRelPath(sf);
24512
25127
  for (const call of sf.getDescendantsOfKind(SyntaxKind10.CallExpression)) {
24513
25128
  const expr = call.getExpression();
24514
- if (!Node18.isIdentifier(expr) || expr.getText() !== UPLOAD_SERVICE_FACTORY) continue;
25129
+ if (!Node19.isIdentifier(expr) || expr.getText() !== UPLOAD_SERVICE_FACTORY) continue;
24515
25130
  const args = call.getArguments();
24516
25131
  const first = args[0] ? resolveObjectArg(args[0], sf) : void 0;
24517
25132
  if (!first) continue;
@@ -24539,12 +25154,12 @@ function extractUploadServiceCalls(sources, index) {
24539
25154
  function resolveObjectArg(node, sf) {
24540
25155
  const inner = unwrapAsExpressions(node);
24541
25156
  if (!inner) return void 0;
24542
- if (Node18.isObjectLiteralExpression(inner)) return inner;
24543
- if (Node18.isIdentifier(inner)) {
25157
+ if (Node19.isObjectLiteralExpression(inner)) return inner;
25158
+ if (Node19.isIdentifier(inner)) {
24544
25159
  const declNode = findLocalVariable(sf, inner.getText());
24545
25160
  if (declNode) {
24546
25161
  const declInit = unwrapAsExpressions(declNode.getInitializer());
24547
- if (declInit && Node18.isObjectLiteralExpression(declInit)) {
25162
+ if (declInit && Node19.isObjectLiteralExpression(declInit)) {
24548
25163
  return declInit;
24549
25164
  }
24550
25165
  }
@@ -24554,12 +25169,12 @@ function resolveObjectArg(node, sf) {
24554
25169
  function resolveArrayFromProperty(obj, name, sf) {
24555
25170
  const init = unwrapAsExpressions(getPropertyInitializer(obj, name));
24556
25171
  if (!init) return void 0;
24557
- if (Node18.isArrayLiteralExpression(init)) return init;
24558
- if (Node18.isIdentifier(init)) {
25172
+ if (Node19.isArrayLiteralExpression(init)) return init;
25173
+ if (Node19.isIdentifier(init)) {
24559
25174
  const declNode = findLocalVariable(sf, init.getText());
24560
25175
  if (declNode) {
24561
25176
  const declInit = unwrapAsExpressions(declNode.getInitializer());
24562
- if (declInit && Node18.isArrayLiteralExpression(declInit)) {
25177
+ if (declInit && Node19.isArrayLiteralExpression(declInit)) {
24563
25178
  return declInit;
24564
25179
  }
24565
25180
  }
@@ -24570,11 +25185,11 @@ function enclosingFactoryName(node) {
24570
25185
  let current = node.getParent();
24571
25186
  let result;
24572
25187
  while (current) {
24573
- if (Node18.isFunctionDeclaration(current)) {
25188
+ if (Node19.isFunctionDeclaration(current)) {
24574
25189
  result = current.getName() ?? void 0;
24575
25190
  break;
24576
25191
  }
24577
- if (Node18.isVariableDeclaration(current)) {
25192
+ if (Node19.isVariableDeclaration(current)) {
24578
25193
  result = current.getName();
24579
25194
  break;
24580
25195
  }
@@ -24628,10 +25243,10 @@ function extractProcessingConfigFromDecl(decl, rel) {
24628
25243
  function readFlowSubtasks(obj) {
24629
25244
  const flowInit = unwrapAsExpressions(getPropertyInitializer(obj, "flow"));
24630
25245
  const subtasks = [];
24631
- if (flowInit && Node18.isArrayLiteralExpression(flowInit)) {
25246
+ if (flowInit && Node19.isArrayLiteralExpression(flowInit)) {
24632
25247
  for (const el of flowInit.getElements()) {
24633
25248
  const inner = unwrapAsExpressions(el);
24634
- if (inner && Node18.isObjectLiteralExpression(inner)) {
25249
+ if (inner && Node19.isObjectLiteralExpression(inner)) {
24635
25250
  const subtask = readIdentifierProperty(inner, "subtask");
24636
25251
  if (subtask) subtasks.push(subtask);
24637
25252
  }
@@ -24645,7 +25260,7 @@ function extractProcessingHandlerCalls(sources) {
24645
25260
  const rel = apiRelPath(sf);
24646
25261
  for (const call of sf.getDescendantsOfKind(SyntaxKind10.CallExpression)) {
24647
25262
  const expr = call.getExpression();
24648
- if (!Node18.isIdentifier(expr) || expr.getText() !== PROCESSING_HANDLER_FACTORY) continue;
25263
+ if (!Node19.isIdentifier(expr) || expr.getText() !== PROCESSING_HANDLER_FACTORY) continue;
24649
25264
  const args = call.getArguments();
24650
25265
  const first = args[0] ? resolveObjectArg(args[0], sf) : void 0;
24651
25266
  if (!first) continue;
@@ -24666,7 +25281,7 @@ function extractProcessingHandlerCalls(sources) {
24666
25281
  }
24667
25282
  function collectProcessorArrayElements(processorsArr, direct, spreads) {
24668
25283
  for (const el of processorsArr.getElements()) {
24669
- if (Node18.isSpreadElement(el)) {
25284
+ if (Node19.isSpreadElement(el)) {
24670
25285
  addProcessorIdentifierName(el.getExpression(), spreads);
24671
25286
  continue;
24672
25287
  }
@@ -24678,13 +25293,13 @@ function collectProcessorArrayElements(processorsArr, direct, spreads) {
24678
25293
  }
24679
25294
  function addProcessorIdentifierName(node, bucket) {
24680
25295
  if (!node) return;
24681
- if (Node18.isIdentifier(node)) {
25296
+ if (Node19.isIdentifier(node)) {
24682
25297
  bucket.push(node.getText());
24683
25298
  return;
24684
25299
  }
24685
- if (Node18.isCallExpression(node)) {
25300
+ if (Node19.isCallExpression(node)) {
24686
25301
  const callee = node.getExpression();
24687
- if (Node18.isIdentifier(callee)) {
25302
+ if (Node19.isIdentifier(callee)) {
24688
25303
  bucket.push(callee.getText());
24689
25304
  }
24690
25305
  }
@@ -25676,7 +26291,7 @@ var storageFileMValidateFolderTool = createTwoSideValidateTool({
25676
26291
  });
25677
26292
 
25678
26293
  // packages/dbx-components-mcp/src/tools/notification-m-validate-app/extract.ts
25679
- import { Node as Node19, SyntaxKind as SyntaxKind11 } from "ts-morph";
26294
+ import { Node as Node20, SyntaxKind as SyntaxKind11 } from "ts-morph";
25680
26295
  var NOTIF_TEMPLATE_TYPE = "NotificationTemplateType";
25681
26296
  var NOTIF_TEMPLATE_TYPE_INFO = "NotificationTemplateTypeInfo";
25682
26297
  var NOTIF_TEMPLATE_TYPE_INFO_ARRAY = "NotificationTemplateTypeInfo[]";
@@ -25797,7 +26412,7 @@ function extractTemplateInfoAggregates(sources) {
25797
26412
  const ids = [];
25798
26413
  for (const el of arr.getElements()) {
25799
26414
  const inner = unwrapAsExpressions(el);
25800
- if (inner && Node19.isIdentifier(inner)) {
26415
+ if (inner && Node20.isIdentifier(inner)) {
25801
26416
  ids.push(inner.getText());
25802
26417
  }
25803
26418
  }
@@ -25843,7 +26458,7 @@ function extractTaskAllTypesAggregates(sources) {
25843
26458
  const ids = [];
25844
26459
  for (const el of arr.getElements()) {
25845
26460
  const inner = unwrapAsExpressions(el);
25846
- if (inner && Node19.isIdentifier(inner)) {
26461
+ if (inner && Node20.isIdentifier(inner)) {
25847
26462
  ids.push(inner.getText());
25848
26463
  }
25849
26464
  }
@@ -25871,14 +26486,14 @@ function extractTaskCheckpointAliases(sources) {
25871
26486
  return out;
25872
26487
  }
25873
26488
  function collectStringLiterals(node, out) {
25874
- if (Node19.isLiteralTypeNode(node)) {
26489
+ if (Node20.isLiteralTypeNode(node)) {
25875
26490
  const lit = node.getLiteral();
25876
- if (Node19.isStringLiteral(lit)) {
26491
+ if (Node20.isStringLiteral(lit)) {
25877
26492
  out.push(lit.getLiteralText());
25878
26493
  }
25879
26494
  return;
25880
26495
  }
25881
- if (Node19.isUnionTypeNode(node)) {
26496
+ if (Node20.isUnionTypeNode(node)) {
25882
26497
  for (const child of node.getTypeNodes()) {
25883
26498
  collectStringLiterals(child, out);
25884
26499
  }
@@ -25932,7 +26547,7 @@ function extractTemplateInfoRecord(sources, index) {
25932
26547
  }
25933
26548
  function tryReadTemplateInfoRecord(decl, rel, index) {
25934
26549
  const initializer = unwrapAsExpressions(decl.getInitializer());
25935
- if (!initializer || !Node19.isCallExpression(initializer)) return void 0;
26550
+ if (!initializer || !Node20.isCallExpression(initializer)) return void 0;
25936
26551
  if (initializer.getExpression().getText() !== TEMPLATE_INFO_RECORD_FACTORY) return void 0;
25937
26552
  const args = initializer.getArguments();
25938
26553
  const arr = args[0] ? asArrayLiteral(args[0]) : void 0;
@@ -25953,15 +26568,15 @@ function tryReadTemplateInfoRecord(decl, rel, index) {
25953
26568
  }
25954
26569
  function classifyInfoRecordArrayElements(arr, direct, spreads) {
25955
26570
  for (const el of arr.getElements()) {
25956
- if (Node19.isSpreadElement(el)) {
26571
+ if (Node20.isSpreadElement(el)) {
25957
26572
  const inner2 = el.getExpression();
25958
- if (Node19.isIdentifier(inner2)) {
26573
+ if (Node20.isIdentifier(inner2)) {
25959
26574
  spreads.push(inner2.getText());
25960
26575
  }
25961
26576
  continue;
25962
26577
  }
25963
26578
  const inner = unwrapAsExpressions(el);
25964
- if (inner && Node19.isIdentifier(inner)) {
26579
+ if (inner && Node20.isIdentifier(inner)) {
25965
26580
  direct.push(inner.getText());
25966
26581
  }
25967
26582
  }
@@ -25988,7 +26603,7 @@ function extractTemplateInfoRecordWiring(sources) {
25988
26603
  const first = args[0];
25989
26604
  if (!first) continue;
25990
26605
  const inner = unwrapAsExpressions(first);
25991
- if (!inner || !Node19.isIdentifier(inner)) continue;
26606
+ if (!inner || !Node20.isIdentifier(inner)) continue;
25992
26607
  const result = {
25993
26608
  recordIdentifier: inner.getText(),
25994
26609
  sourceFile: rel
@@ -26026,7 +26641,7 @@ function buildApiFunctionIndex2(sources) {
26026
26641
  for (const decl of stmt.getDeclarations()) {
26027
26642
  const initializer = unwrapAsExpressions(decl.getInitializer());
26028
26643
  if (!initializer) continue;
26029
- if (Node19.isArrowFunction(initializer) || Node19.isFunctionExpression(initializer)) {
26644
+ if (Node20.isArrowFunction(initializer) || Node20.isFunctionExpression(initializer)) {
26030
26645
  map.set(decl.getName(), { node: initializer, sourceFile: sf, relPath: rel });
26031
26646
  }
26032
26647
  }
@@ -26062,11 +26677,11 @@ function extractTemplateConfigsArrayFactory(sources, index) {
26062
26677
  const arr = returnExpr ? asArrayLiteral(returnExpr) : void 0;
26063
26678
  if (arr) {
26064
26679
  for (const el of arr.getElements()) {
26065
- if (Node19.isSpreadElement(el)) {
26680
+ if (Node20.isSpreadElement(el)) {
26066
26681
  const inner2 = el.getExpression();
26067
- if (Node19.isCallExpression(inner2)) {
26682
+ if (Node20.isCallExpression(inner2)) {
26068
26683
  const callee = inner2.getExpression();
26069
- if (Node19.isIdentifier(callee)) {
26684
+ if (Node20.isIdentifier(callee)) {
26070
26685
  const callName = callee.getText();
26071
26686
  spreadCalls.push(callName);
26072
26687
  if (!index.functionsByName.has(callName)) {
@@ -26077,9 +26692,9 @@ function extractTemplateConfigsArrayFactory(sources, index) {
26077
26692
  continue;
26078
26693
  }
26079
26694
  const inner = unwrapAsExpressions(el);
26080
- if (inner && Node19.isCallExpression(inner)) {
26695
+ if (inner && Node20.isCallExpression(inner)) {
26081
26696
  const callee = inner.getExpression();
26082
- if (Node19.isIdentifier(callee)) {
26697
+ if (Node20.isIdentifier(callee)) {
26083
26698
  directCalls.push(callee.getText());
26084
26699
  }
26085
26700
  }
@@ -26128,11 +26743,11 @@ function collectFromArrayEntryFactory(name, out, index) {
26128
26743
  const arr = asArrayLiteral(returnExpr);
26129
26744
  if (!arr) return;
26130
26745
  for (const el of arr.getElements()) {
26131
- if (Node19.isSpreadElement(el)) {
26746
+ if (Node20.isSpreadElement(el)) {
26132
26747
  const inner2 = el.getExpression();
26133
- if (Node19.isCallExpression(inner2)) {
26748
+ if (Node20.isCallExpression(inner2)) {
26134
26749
  const callee = inner2.getExpression();
26135
- if (Node19.isIdentifier(callee)) {
26750
+ if (Node20.isIdentifier(callee)) {
26136
26751
  collectFromArrayEntryFactory(callee.getText(), out, index);
26137
26752
  }
26138
26753
  }
@@ -26140,9 +26755,9 @@ function collectFromArrayEntryFactory(name, out, index) {
26140
26755
  }
26141
26756
  const inner = unwrapAsExpressions(el);
26142
26757
  if (!inner) continue;
26143
- if (Node19.isCallExpression(inner)) {
26758
+ if (Node20.isCallExpression(inner)) {
26144
26759
  const callee = inner.getExpression();
26145
- if (Node19.isIdentifier(callee)) {
26760
+ if (Node20.isIdentifier(callee)) {
26146
26761
  collectFromSingleEntryFactory(callee.getText(), out, index);
26147
26762
  }
26148
26763
  continue;
@@ -26162,7 +26777,7 @@ function extractTaskServiceCalls(sources, index, trustedExternal) {
26162
26777
  const rel = apiRelPath(sf);
26163
26778
  for (const call of sf.getDescendantsOfKind(SyntaxKind11.CallExpression)) {
26164
26779
  const expr = call.getExpression();
26165
- if (!Node19.isIdentifier(expr) || expr.getText() !== TASK_SERVICE_FACTORY) continue;
26780
+ if (!Node20.isIdentifier(expr) || expr.getText() !== TASK_SERVICE_FACTORY) continue;
26166
26781
  const args = call.getArguments();
26167
26782
  const first = args[0] ? asObjectLiteral(args[0]) : void 0;
26168
26783
  if (!first) continue;
@@ -26188,13 +26803,13 @@ function collectValidateArrayElements(validateArr) {
26188
26803
  const spreads = [];
26189
26804
  if (!validateArr) return { ids, spreads };
26190
26805
  for (const el of validateArr.getElements()) {
26191
- if (Node19.isSpreadElement(el)) {
26806
+ if (Node20.isSpreadElement(el)) {
26192
26807
  const inner2 = el.getExpression();
26193
- if (Node19.isIdentifier(inner2)) spreads.push(inner2.getText());
26808
+ if (Node20.isIdentifier(inner2)) spreads.push(inner2.getText());
26194
26809
  continue;
26195
26810
  }
26196
26811
  const inner = unwrapAsExpressions(el);
26197
- if (inner && Node19.isIdentifier(inner)) ids.push(inner.getText());
26812
+ if (inner && Node20.isIdentifier(inner)) ids.push(inner.getText());
26198
26813
  }
26199
26814
  return { ids, spreads };
26200
26815
  }
@@ -26207,17 +26822,17 @@ function collectHandlersArrayElements(options) {
26207
26822
  if (!handlersArr) return { handlerTypes, unresolvedSpreads, resolvedBindings, unresolvedBindings };
26208
26823
  const visited = /* @__PURE__ */ new Set();
26209
26824
  for (const el of handlersArr.getElements()) {
26210
- if (Node19.isSpreadElement(el)) {
26825
+ if (Node20.isSpreadElement(el)) {
26211
26826
  consumeHandlersSpreadElement({ inner: el.getExpression(), sf, index, trustedExternal, unresolvedSpreads, resolvedBindings, unresolvedBindings, visited });
26212
26827
  continue;
26213
26828
  }
26214
26829
  const inner = unwrapAsExpressions(el);
26215
26830
  if (!inner) continue;
26216
- if (Node19.isIdentifier(inner)) {
26831
+ if (Node20.isIdentifier(inner)) {
26217
26832
  collectHandlerBindingsFromIdentifier({ name: inner.getText(), sf, index, trustedExternal, resolved: resolvedBindings, unresolved: unresolvedBindings, visited });
26218
- } else if (Node19.isCallExpression(inner)) {
26833
+ } else if (Node20.isCallExpression(inner)) {
26219
26834
  const callee = inner.getExpression();
26220
- if (Node19.isIdentifier(callee)) {
26835
+ if (Node20.isIdentifier(callee)) {
26221
26836
  collectHandlerBindingsFromIdentifier({ name: callee.getText(), sf, index, trustedExternal, resolved: resolvedBindings, unresolved: unresolvedBindings, visited });
26222
26837
  }
26223
26838
  }
@@ -26226,16 +26841,16 @@ function collectHandlersArrayElements(options) {
26226
26841
  }
26227
26842
  function consumeHandlersSpreadElement(options) {
26228
26843
  const { inner, sf, index, trustedExternal, unresolvedSpreads, resolvedBindings, unresolvedBindings, visited } = options;
26229
- if (Node19.isCallExpression(inner)) {
26844
+ if (Node20.isCallExpression(inner)) {
26230
26845
  const callee = inner.getExpression();
26231
- if (Node19.isIdentifier(callee)) {
26846
+ if (Node20.isIdentifier(callee)) {
26232
26847
  const name = callee.getText();
26233
26848
  unresolvedSpreads.push(name);
26234
26849
  collectHandlerBindingsFromIdentifier({ name, sf, index, trustedExternal, resolved: resolvedBindings, unresolved: unresolvedBindings, visited });
26235
26850
  }
26236
26851
  return;
26237
26852
  }
26238
- if (Node19.isIdentifier(inner)) {
26853
+ if (Node20.isIdentifier(inner)) {
26239
26854
  const name = inner.getText();
26240
26855
  unresolvedSpreads.push(name);
26241
26856
  collectHandlerBindingsFromIdentifier({ name, sf, index, trustedExternal, resolved: resolvedBindings, unresolved: unresolvedBindings, visited });
@@ -26245,15 +26860,31 @@ function collectHandlerBindingsFromValue(options) {
26245
26860
  const { node, sf, index, trustedExternal, resolved, unresolved, visited } = options;
26246
26861
  const inner = unwrapAsExpressions(node);
26247
26862
  if (!inner) return false;
26248
- if (Node19.isCallExpression(inner)) {
26863
+ if (Node20.isCallExpression(inner)) {
26249
26864
  const callee = inner.getExpression();
26250
- if (Node19.isIdentifier(callee)) {
26865
+ if (Node20.isIdentifier(callee)) {
26251
26866
  return collectHandlerBindingsFromIdentifier({ name: callee.getText(), sf, index, trustedExternal, resolved, unresolved, visited });
26252
26867
  }
26868
+ return false;
26253
26869
  }
26254
- if (Node19.isIdentifier(inner)) {
26870
+ if (Node20.isIdentifier(inner)) {
26255
26871
  return collectHandlerBindingsFromIdentifier({ name: inner.getText(), sf, index, trustedExternal, resolved, unresolved, visited });
26256
26872
  }
26873
+ if (Node20.isArrayLiteralExpression(inner)) {
26874
+ let any = false;
26875
+ for (const el of inner.getElements()) {
26876
+ if (Node20.isSpreadElement(el)) {
26877
+ if (collectHandlerBindingsFromValue({ node: el.getExpression(), sf, index, trustedExternal, resolved, unresolved, visited })) {
26878
+ any = true;
26879
+ }
26880
+ continue;
26881
+ }
26882
+ if (collectHandlerBindingsFromValue({ node: el, sf, index, trustedExternal, resolved, unresolved, visited })) {
26883
+ any = true;
26884
+ }
26885
+ }
26886
+ return any;
26887
+ }
26257
26888
  return false;
26258
26889
  }
26259
26890
  function collectHandlerBindingsFromIdentifier(options) {
@@ -26268,7 +26899,7 @@ function collectHandlerBindingsFromIdentifier(options) {
26268
26899
  const typeText = typeNode ? typeNode.getText() : void 0;
26269
26900
  const isHandlerConfigTyped = typeText !== void 0 && (typeText === NOTIF_TASK_SERVICE_HANDLER_CONFIG || typeText.startsWith(`${NOTIF_TASK_SERVICE_HANDLER_CONFIG}<`));
26270
26901
  const declInit = unwrapAsExpressions(localDecl.getInitializer());
26271
- const isObjectLit = declInit !== void 0 && Node19.isObjectLiteralExpression(declInit);
26902
+ const isObjectLit = declInit !== void 0 && Node20.isObjectLiteralExpression(declInit);
26272
26903
  if (isHandlerConfigTyped && isObjectLit) {
26273
26904
  resolved.push(localDecl.getName());
26274
26905
  resolvedAny = true;
@@ -26299,12 +26930,12 @@ function collectHandlerBindingsFromIdentifier(options) {
26299
26930
  function resolveArrayFromProperty2(obj, name, sf) {
26300
26931
  const init = unwrapAsExpressions(getPropertyInitializer(obj, name));
26301
26932
  if (!init) return void 0;
26302
- if (Node19.isArrayLiteralExpression(init)) return init;
26303
- if (Node19.isIdentifier(init)) {
26933
+ if (Node20.isArrayLiteralExpression(init)) return init;
26934
+ if (Node20.isIdentifier(init)) {
26304
26935
  const declNode = findLocalVariable(sf, init.getText());
26305
26936
  if (declNode) {
26306
26937
  const declInit = unwrapAsExpressions(declNode.getInitializer());
26307
- if (declInit && Node19.isArrayLiteralExpression(declInit)) {
26938
+ if (declInit && Node20.isArrayLiteralExpression(declInit)) {
26308
26939
  return declInit;
26309
26940
  }
26310
26941
  }
@@ -26325,12 +26956,12 @@ function extractTaskHandlerEntries(sources) {
26325
26956
  if (!typeIdent) return;
26326
26957
  const flowInit = unwrapAsExpressions(getPropertyInitializer(obj, "flow"));
26327
26958
  let flowCount;
26328
- if (flowInit && Node19.isArrayLiteralExpression(flowInit)) {
26959
+ if (flowInit && Node20.isArrayLiteralExpression(flowInit)) {
26329
26960
  flowCount = flowInit.getElements().length;
26330
26961
  }
26331
26962
  let dataArg;
26332
26963
  let checkpointArg;
26333
- if (Node19.isTypeReference(typeNode)) {
26964
+ if (Node20.isTypeReference(typeNode)) {
26334
26965
  const tArgs = typeNode.getTypeArguments();
26335
26966
  if (tArgs.length >= 1) dataArg = tArgs[0].getText();
26336
26967
  if (tArgs.length >= 2) checkpointArg = tArgs[1].getText();
@@ -27223,7 +27854,7 @@ async function inspectFolder2(path) {
27223
27854
  }
27224
27855
 
27225
27856
  // packages/dbx-components-mcp/src/tools/system-m-validate-folder/extract.ts
27226
- import { Node as Node20, Project as Project13 } from "ts-morph";
27857
+ import { Node as Node21, Project as Project13 } from "ts-morph";
27227
27858
  var TYPE_CONSTANT_SUFFIX = "_SYSTEM_STATE_TYPE";
27228
27859
  var DATA_INTERFACE_SUFFIX = "SystemData";
27229
27860
  var CONVERTER_CONFIG_TYPE = "SystemStateStoredDataFieldConverterConfig";
@@ -27287,7 +27918,7 @@ function findDataInterfaces2(sourceFile) {
27287
27918
  return out;
27288
27919
  }
27289
27920
  function extendsSystemStateStoredData(iface) {
27290
- if (!Node20.isInterfaceDeclaration(iface)) return false;
27921
+ if (!Node21.isInterfaceDeclaration(iface)) return false;
27291
27922
  for (const clause of iface.getExtends()) {
27292
27923
  const text = clause.getText();
27293
27924
  if (text === SYSTEM_STATE_STORED_DATA || text.startsWith(`${SYSTEM_STATE_STORED_DATA}<`) || text === `${SYSTEM_STATE_STORED_DATA}, ${SYSTEM_STATE_STORED_DATA}`) {
@@ -27307,14 +27938,14 @@ function findConverters2(sourceFile) {
27307
27938
  for (const decl of stmt.getDeclarations()) {
27308
27939
  const typeNode = decl.getTypeNode();
27309
27940
  if (!typeNode) continue;
27310
- if (!Node20.isTypeReference(typeNode)) continue;
27941
+ if (!Node21.isTypeReference(typeNode)) continue;
27311
27942
  const typeName = typeNode.getTypeName().getText();
27312
27943
  if (typeName !== CONVERTER_CONFIG_TYPE) continue;
27313
27944
  const typeArgs = typeNode.getTypeArguments();
27314
27945
  let dataTypeArgument;
27315
27946
  if (typeArgs.length >= 1) {
27316
27947
  const arg = typeArgs[0];
27317
- if (Node20.isTypeReference(arg)) {
27948
+ if (Node21.isTypeReference(arg)) {
27318
27949
  dataTypeArgument = arg.getTypeName().getText();
27319
27950
  } else {
27320
27951
  dataTypeArgument = arg.getText();
@@ -27361,19 +27992,19 @@ function collectConverterMapKeys(decl) {
27361
27992
  if (!obj) return [];
27362
27993
  const out = [];
27363
27994
  for (const prop of obj.getProperties()) {
27364
- if (!Node20.isPropertyAssignment(prop)) continue;
27995
+ if (!Node21.isPropertyAssignment(prop)) continue;
27365
27996
  const nameNode = prop.getNameNode();
27366
27997
  const line = prop.getStartLineNumber();
27367
- if (Node20.isComputedPropertyName(nameNode)) {
27998
+ if (Node21.isComputedPropertyName(nameNode)) {
27368
27999
  const inner = nameNode.getExpression();
27369
28000
  out.push({ raw: inner.getText(), kind: "identifier", line });
27370
28001
  continue;
27371
28002
  }
27372
- if (Node20.isStringLiteral(nameNode)) {
28003
+ if (Node21.isStringLiteral(nameNode)) {
27373
28004
  out.push({ raw: nameNode.getLiteralText(), kind: "string", line });
27374
28005
  continue;
27375
28006
  }
27376
- if (Node20.isIdentifier(nameNode)) {
28007
+ if (Node21.isIdentifier(nameNode)) {
27377
28008
  out.push({ raw: nameNode.getText(), kind: "string", line });
27378
28009
  continue;
27379
28010
  }
@@ -27383,10 +28014,10 @@ function collectConverterMapKeys(decl) {
27383
28014
  }
27384
28015
  function unwrapObjectLiteral(node) {
27385
28016
  let current = node;
27386
- while (current && Node20.isAsExpression(current)) {
28017
+ while (current && Node21.isAsExpression(current)) {
27387
28018
  current = current.getExpression();
27388
28019
  }
27389
- if (current && Node20.isObjectLiteralExpression(current)) {
28020
+ if (current && Node21.isObjectLiteralExpression(current)) {
27390
28021
  return current;
27391
28022
  }
27392
28023
  return void 0;
@@ -27420,10 +28051,10 @@ function findLastTopLevelExportLine(sourceFile) {
27420
28051
  return last;
27421
28052
  }
27422
28053
  function isExportedTopLevel(stmt) {
27423
- if (Node20.isVariableStatement(stmt) || Node20.isFunctionDeclaration(stmt) || Node20.isClassDeclaration(stmt) || Node20.isInterfaceDeclaration(stmt) || Node20.isTypeAliasDeclaration(stmt) || Node20.isEnumDeclaration(stmt)) {
28054
+ if (Node21.isVariableStatement(stmt) || Node21.isFunctionDeclaration(stmt) || Node21.isClassDeclaration(stmt) || Node21.isInterfaceDeclaration(stmt) || Node21.isTypeAliasDeclaration(stmt) || Node21.isEnumDeclaration(stmt)) {
27424
28055
  return stmt.isExported();
27425
28056
  }
27426
- if (Node20.isExportDeclaration(stmt) || Node20.isExportAssignment(stmt)) {
28057
+ if (Node21.isExportDeclaration(stmt) || Node21.isExportAssignment(stmt)) {
27427
28058
  return true;
27428
28059
  }
27429
28060
  return false;
@@ -27734,7 +28365,7 @@ var systemMValidateFolderTool = createFolderValidateTool({
27734
28365
  });
27735
28366
 
27736
28367
  // packages/dbx-components-mcp/src/tools/dbx-asset-validate-app/extract.ts
27737
- import { Node as Node21, Project as Project14 } from "ts-morph";
28368
+ import { Node as Node22, Project as Project14 } from "ts-morph";
27738
28369
  var ASSETS_RELPATH = "src/lib/assets.ts";
27739
28370
  var BARREL_RELPATH = "src/lib/index.ts";
27740
28371
  var LOCAL_ASSET_BUILDER = "localAsset";
@@ -27793,11 +28424,11 @@ function extractFromAssetsSource(ctx, sf) {
27793
28424
  const initializer = unwrapAsExpressions(decl.getInitializer());
27794
28425
  if (!initializer) continue;
27795
28426
  const site = { isExported, symbolName: decl.getName(), line: decl.getStartLineNumber(), rel };
27796
- if (Node21.isCallExpression(initializer)) {
28427
+ if (Node22.isCallExpression(initializer)) {
27797
28428
  handleCallInitializer(ctx, site, initializer);
27798
28429
  continue;
27799
28430
  }
27800
- if (isExported && Node21.isArrayLiteralExpression(initializer) && typeAnnotationText(decl) === ASSET_PATH_REF_ARRAY_TYPE) {
28431
+ if (isExported && Node22.isArrayLiteralExpression(initializer) && typeAnnotationText(decl) === ASSET_PATH_REF_ARRAY_TYPE) {
27801
28432
  ctx.aggregatorExports.push({ symbolName: site.symbolName, memberNames: collectArrayIdentifiers(initializer.getElements()), sourceFile: rel, line: site.line });
27802
28433
  }
27803
28434
  }
@@ -27807,7 +28438,7 @@ function collectArrayIdentifiers(elements) {
27807
28438
  const out = [];
27808
28439
  for (const el of elements) {
27809
28440
  const inner = unwrapAsExpressions(el);
27810
- if (inner && Node21.isIdentifier(inner)) {
28441
+ if (inner && Node22.isIdentifier(inner)) {
27811
28442
  out.push(inner.getText());
27812
28443
  }
27813
28444
  }
@@ -27815,11 +28446,11 @@ function collectArrayIdentifiers(elements) {
27815
28446
  }
27816
28447
  function handleCallInitializer(ctx, site, callExpr) {
27817
28448
  const callee = callExpr.getExpression();
27818
- if (Node21.isIdentifier(callee)) {
28449
+ if (Node22.isIdentifier(callee)) {
27819
28450
  handleIdentifierCallee({ ctx, site, callExpr, calleeName: callee.getText() });
27820
28451
  return;
27821
28452
  }
27822
- if (Node21.isPropertyAccessExpression(callee)) {
28453
+ if (Node22.isPropertyAccessExpression(callee)) {
27823
28454
  handleFluentMemberCall(ctx, site, callExpr);
27824
28455
  }
27825
28456
  }
@@ -27877,9 +28508,9 @@ function handleRemoteBaseBuilder(ctx, site, arg) {
27877
28508
  }
27878
28509
  function handleFluentMemberCall(ctx, site, callExpr) {
27879
28510
  const callee = callExpr.getExpression();
27880
- if (!Node21.isPropertyAccessExpression(callee)) return;
28511
+ if (!Node22.isPropertyAccessExpression(callee)) return;
27881
28512
  const identNode = callee.getExpression();
27882
- if (!Node21.isIdentifier(identNode)) return;
28513
+ if (!Node22.isIdentifier(identNode)) return;
27883
28514
  const memberName = callee.getName();
27884
28515
  if (memberName !== "asset" && memberName !== "assets") return;
27885
28516
  const builderName = identNode.getText();
@@ -27904,7 +28535,7 @@ function handleFluentMemberCall(ctx, site, callExpr) {
27904
28535
  return;
27905
28536
  }
27906
28537
  const arrayInner = unwrapAsExpressions(args[0]);
27907
- if (!arrayInner || !Node21.isArrayLiteralExpression(arrayInner)) return;
28538
+ if (!arrayInner || !Node22.isArrayLiteralExpression(arrayInner)) return;
27908
28539
  const resolvedPaths = collectFluentArrayPaths({ elements: arrayInner.getElements(), sourceType, folder, remoteBase });
27909
28540
  if (site.isExported) {
27910
28541
  ctx.assetConstants.push({ symbolName: site.symbolName, sourceType, helper, resolved: void 0, resolvedPaths, sourceFile: site.rel, line: site.line });
@@ -27914,7 +28545,7 @@ function collectFluentArrayPaths(input) {
27914
28545
  const out = [];
27915
28546
  for (const el of input.elements) {
27916
28547
  const inner = unwrapAsExpressions(el);
27917
- if (inner && Node21.isStringLiteral(inner)) {
28548
+ if (inner && Node22.isStringLiteral(inner)) {
27918
28549
  const joined = joinFluent({ sourceType: input.sourceType, folder: input.folder, remoteBase: input.remoteBase, child: inner.getLiteralText() });
27919
28550
  if (joined !== void 0) out.push(joined);
27920
28551
  }
@@ -27947,7 +28578,7 @@ function readStringArg(call) {
27947
28578
  }
27948
28579
  function readStringArgFromNode(node) {
27949
28580
  const inner = unwrapAsExpressions(node);
27950
- if (inner && Node21.isStringLiteral(inner)) {
28581
+ if (inner && Node22.isStringLiteral(inner)) {
27951
28582
  return inner.getLiteralText();
27952
28583
  }
27953
28584
  return void 0;
@@ -29862,7 +30493,7 @@ import { glob as fsGlob5, readFile as readFile18, stat as stat14 } from "node:fs
29862
30493
  import { dirname as dirname13, isAbsolute as isAbsolute13, resolve as resolve34, sep as sep12 } from "node:path";
29863
30494
 
29864
30495
  // packages/dbx-components-mcp/src/tools/route/extract.ts
29865
- import { Node as Node22, Project as Project15, SyntaxKind as SyntaxKind12 } from "ts-morph";
30496
+ import { Node as Node23, Project as Project15, SyntaxKind as SyntaxKind12 } from "ts-morph";
29866
30497
  function extractFile3(source) {
29867
30498
  const project = new Project15({ useInMemoryFileSystem: true, skipAddingFilesFromTsConfig: true });
29868
30499
  const sourceFile = project.createSourceFile(source.name, source.text, { overwrite: true });
@@ -29877,9 +30508,9 @@ function extractFile3(source) {
29877
30508
  }
29878
30509
  for (const arrayLit of findStateArrayLiterals(sourceFile)) {
29879
30510
  for (const element of arrayLit.getElements()) {
29880
- if (Node22.isObjectLiteralExpression(element)) {
30511
+ if (Node23.isObjectLiteralExpression(element)) {
29881
30512
  addNode({ file: source.name, literal: element, fallbackConstName: void 0, into: collected, issues });
29882
- } else if (Node22.isIdentifier(element)) {
30513
+ } else if (Node23.isIdentifier(element)) {
29883
30514
  const ref = objectLiteralsByName.get(element.getText());
29884
30515
  if (ref) {
29885
30516
  addNode({ file: source.name, literal: ref.literal, fallbackConstName: element.getText(), into: collected, issues });
@@ -29900,7 +30531,7 @@ function collectStateConsts(sourceFile) {
29900
30531
  for (const stmt of sourceFile.getVariableStatements()) {
29901
30532
  for (const decl of stmt.getDeclarations()) {
29902
30533
  const initializer = decl.getInitializer();
29903
- if (!initializer || !Node22.isObjectLiteralExpression(initializer)) {
30534
+ if (!initializer || !Node23.isObjectLiteralExpression(initializer)) {
29904
30535
  continue;
29905
30536
  }
29906
30537
  const typeNode = decl.getTypeNode();
@@ -29915,7 +30546,7 @@ function findStateArrayLiterals(sourceFile) {
29915
30546
  for (const stmt of sourceFile.getVariableStatements()) {
29916
30547
  for (const decl of stmt.getDeclarations()) {
29917
30548
  const initializer = decl.getInitializer();
29918
- if (initializer && Node22.isArrayLiteralExpression(initializer) && declarationIsStateArray(stmt, decl.getName(), initializer)) {
30549
+ if (initializer && Node23.isArrayLiteralExpression(initializer) && declarationIsStateArray(stmt, decl.getName(), initializer)) {
29919
30550
  arrays.push(initializer);
29920
30551
  }
29921
30552
  }
@@ -29929,17 +30560,17 @@ function findStateArrayLiterals(sourceFile) {
29929
30560
  continue;
29930
30561
  }
29931
30562
  for (const arg of call.getArguments()) {
29932
- if (!Node22.isObjectLiteralExpression(arg)) {
30563
+ if (!Node23.isObjectLiteralExpression(arg)) {
29933
30564
  continue;
29934
30565
  }
29935
30566
  const statesProp = arg.getProperty("states");
29936
- if (!statesProp || !Node22.isPropertyAssignment(statesProp)) {
30567
+ if (!statesProp || !Node23.isPropertyAssignment(statesProp)) {
29937
30568
  continue;
29938
30569
  }
29939
30570
  const initializer = statesProp.getInitializer();
29940
- if (initializer && Node22.isArrayLiteralExpression(initializer)) {
30571
+ if (initializer && Node23.isArrayLiteralExpression(initializer)) {
29941
30572
  arrays.push(initializer);
29942
- } else if (initializer && Node22.isIdentifier(initializer)) {
30573
+ } else if (initializer && Node23.isIdentifier(initializer)) {
29943
30574
  const referenced = findArrayLiteralForIdentifier(sourceFile, initializer);
29944
30575
  if (referenced) {
29945
30576
  arrays.push(referenced);
@@ -29956,10 +30587,10 @@ function declarationIsStateArray(stmt, name, literal) {
29956
30587
  return true;
29957
30588
  }
29958
30589
  for (const element of literal.getElements()) {
29959
- if (Node22.isObjectLiteralExpression(element) && hasStringNameProperty(element)) {
30590
+ if (Node23.isObjectLiteralExpression(element) && hasStringNameProperty(element)) {
29960
30591
  return true;
29961
30592
  }
29962
- if (Node22.isIdentifier(element)) {
30593
+ if (Node23.isIdentifier(element)) {
29963
30594
  return true;
29964
30595
  }
29965
30596
  }
@@ -29967,11 +30598,11 @@ function declarationIsStateArray(stmt, name, literal) {
29967
30598
  }
29968
30599
  function hasStringNameProperty(literal) {
29969
30600
  const nameProp = literal.getProperty("name");
29970
- if (!nameProp || !Node22.isPropertyAssignment(nameProp)) {
30601
+ if (!nameProp || !Node23.isPropertyAssignment(nameProp)) {
29971
30602
  return false;
29972
30603
  }
29973
30604
  const initializer = nameProp.getInitializer();
29974
- return initializer !== void 0 && (Node22.isStringLiteral(initializer) || Node22.isNoSubstitutionTemplateLiteral(initializer));
30605
+ return initializer !== void 0 && (Node23.isStringLiteral(initializer) || Node23.isNoSubstitutionTemplateLiteral(initializer));
29975
30606
  }
29976
30607
  function findArrayLiteralForIdentifier(sourceFile, identifier) {
29977
30608
  const name = identifier.getText();
@@ -29981,7 +30612,7 @@ function findArrayLiteralForIdentifier(sourceFile, identifier) {
29981
30612
  continue;
29982
30613
  }
29983
30614
  const initializer = decl.getInitializer();
29984
- if (initializer && Node22.isArrayLiteralExpression(initializer)) {
30615
+ if (initializer && Node23.isArrayLiteralExpression(initializer)) {
29985
30616
  return initializer;
29986
30617
  }
29987
30618
  }
@@ -30031,21 +30662,21 @@ function addNode(options) {
30031
30662
  }
30032
30663
  function stringPropertyValue(literal, key) {
30033
30664
  const prop = literal.getProperty(key);
30034
- if (!prop || !Node22.isPropertyAssignment(prop)) {
30665
+ if (!prop || !Node23.isPropertyAssignment(prop)) {
30035
30666
  return void 0;
30036
30667
  }
30037
30668
  const initializer = prop.getInitializer();
30038
30669
  if (!initializer) {
30039
30670
  return void 0;
30040
30671
  }
30041
- if (Node22.isStringLiteral(initializer) || Node22.isNoSubstitutionTemplateLiteral(initializer)) {
30672
+ if (Node23.isStringLiteral(initializer) || Node23.isNoSubstitutionTemplateLiteral(initializer)) {
30042
30673
  return initializer.getLiteralText();
30043
30674
  }
30044
30675
  return void 0;
30045
30676
  }
30046
30677
  function booleanPropertyValue(literal, key) {
30047
30678
  const prop = literal.getProperty(key);
30048
- if (!prop || !Node22.isPropertyAssignment(prop)) {
30679
+ if (!prop || !Node23.isPropertyAssignment(prop)) {
30049
30680
  return void 0;
30050
30681
  }
30051
30682
  const initializer = prop.getInitializer();
@@ -30062,33 +30693,33 @@ function booleanPropertyValue(literal, key) {
30062
30693
  }
30063
30694
  function identifierPropertyValue(literal, key) {
30064
30695
  const prop = literal.getProperty(key);
30065
- if (!prop || !Node22.isPropertyAssignment(prop)) {
30696
+ if (!prop || !Node23.isPropertyAssignment(prop)) {
30066
30697
  return void 0;
30067
30698
  }
30068
30699
  const initializer = prop.getInitializer();
30069
30700
  if (!initializer) {
30070
30701
  return void 0;
30071
30702
  }
30072
- if (Node22.isIdentifier(initializer)) {
30703
+ if (Node23.isIdentifier(initializer)) {
30073
30704
  return initializer.getText();
30074
30705
  }
30075
- if (Node22.isPropertyAccessExpression(initializer)) {
30706
+ if (Node23.isPropertyAccessExpression(initializer)) {
30076
30707
  return initializer.getText();
30077
30708
  }
30078
30709
  return void 0;
30079
30710
  }
30080
30711
  function objectPropertyKeys(literal, key) {
30081
30712
  const prop = literal.getProperty(key);
30082
- if (!prop || !Node22.isPropertyAssignment(prop)) {
30713
+ if (!prop || !Node23.isPropertyAssignment(prop)) {
30083
30714
  return [];
30084
30715
  }
30085
30716
  const initializer = prop.getInitializer();
30086
- if (!initializer || !Node22.isObjectLiteralExpression(initializer)) {
30717
+ if (!initializer || !Node23.isObjectLiteralExpression(initializer)) {
30087
30718
  return [];
30088
30719
  }
30089
30720
  const out = [];
30090
30721
  for (const member of initializer.getProperties()) {
30091
- if (Node22.isPropertyAssignment(member) || Node22.isShorthandPropertyAssignment(member) || Node22.isMethodDeclaration(member)) {
30722
+ if (Node23.isPropertyAssignment(member) || Node23.isShorthandPropertyAssignment(member) || Node23.isMethodDeclaration(member)) {
30092
30723
  const nameNode = member.getNameNode();
30093
30724
  if (nameNode) {
30094
30725
  out.push(nameNode.getText());
@@ -30099,7 +30730,7 @@ function objectPropertyKeys(literal, key) {
30099
30730
  }
30100
30731
  function collectResolveKeys(literal) {
30101
30732
  const prop = literal.getProperty("resolve");
30102
- if (!prop || !Node22.isPropertyAssignment(prop)) {
30733
+ if (!prop || !Node23.isPropertyAssignment(prop)) {
30103
30734
  return [];
30104
30735
  }
30105
30736
  const initializer = prop.getInitializer();
@@ -30107,22 +30738,22 @@ function collectResolveKeys(literal) {
30107
30738
  return [];
30108
30739
  }
30109
30740
  const keys = [];
30110
- if (Node22.isObjectLiteralExpression(initializer)) {
30741
+ if (Node23.isObjectLiteralExpression(initializer)) {
30111
30742
  for (const member of initializer.getProperties()) {
30112
- if (Node22.isPropertyAssignment(member) || Node22.isShorthandPropertyAssignment(member) || Node22.isMethodDeclaration(member)) {
30743
+ if (Node23.isPropertyAssignment(member) || Node23.isShorthandPropertyAssignment(member) || Node23.isMethodDeclaration(member)) {
30113
30744
  const nameNode = member.getNameNode();
30114
30745
  if (nameNode) {
30115
30746
  keys.push(nameNode.getText());
30116
30747
  }
30117
30748
  }
30118
30749
  }
30119
- } else if (Node22.isArrayLiteralExpression(initializer)) {
30750
+ } else if (Node23.isArrayLiteralExpression(initializer)) {
30120
30751
  for (const element of initializer.getElements()) {
30121
- if (Node22.isObjectLiteralExpression(element)) {
30752
+ if (Node23.isObjectLiteralExpression(element)) {
30122
30753
  const tokenProp = element.getProperty("token");
30123
- if (tokenProp && Node22.isPropertyAssignment(tokenProp)) {
30754
+ if (tokenProp && Node23.isPropertyAssignment(tokenProp)) {
30124
30755
  const tokenInit = tokenProp.getInitializer();
30125
- if (tokenInit && (Node22.isStringLiteral(tokenInit) || Node22.isIdentifier(tokenInit))) {
30756
+ if (tokenInit && (Node23.isStringLiteral(tokenInit) || Node23.isIdentifier(tokenInit))) {
30126
30757
  keys.push(tokenInit.getText().replaceAll(/^['"]|['"]$/g, ""));
30127
30758
  }
30128
30759
  }
@@ -32191,7 +32822,7 @@ function createSearchModelSnapshotFieldTool(config) {
32191
32822
  import { glob, readFile as readFile19 } from "node:fs/promises";
32192
32823
  import { resolve as resolve35 } from "node:path";
32193
32824
  import { type as type59 } from "arktype";
32194
- import { Node as Node23, Project as Project16 } from "ts-morph";
32825
+ import { Node as Node24, Project as Project16 } from "ts-morph";
32195
32826
  var ListAppArgsType2 = type59({
32196
32827
  componentDir: "string",
32197
32828
  "apiDir?": "string",
@@ -32321,7 +32952,7 @@ async function buildListAppReport(input) {
32321
32952
  function findSnapshotConverterCalls(sourceFile) {
32322
32953
  const out = [];
32323
32954
  sourceFile.forEachDescendant((node) => {
32324
- if (Node23.isCallExpression(node)) {
32955
+ if (Node24.isCallExpression(node)) {
32325
32956
  const expr = node.getExpression();
32326
32957
  const exprText = expr.getText();
32327
32958
  const head = exprText.replace(/<[^>]*>$/, "");
@@ -32336,17 +32967,17 @@ function readFieldsObject(call) {
32336
32967
  const args = call.getArguments();
32337
32968
  if (args.length === 0) return void 0;
32338
32969
  const first = args[0];
32339
- if (!Node23.isObjectLiteralExpression(first)) return void 0;
32970
+ if (!Node24.isObjectLiteralExpression(first)) return void 0;
32340
32971
  const fieldsProp = first.getProperty("fields");
32341
- if (fieldsProp === void 0 || !Node23.isPropertyAssignment(fieldsProp)) return void 0;
32972
+ if (fieldsProp === void 0 || !Node24.isPropertyAssignment(fieldsProp)) return void 0;
32342
32973
  const initializer = fieldsProp.getInitializer();
32343
- if (initializer === void 0 || !Node23.isObjectLiteralExpression(initializer)) return void 0;
32974
+ if (initializer === void 0 || !Node24.isObjectLiteralExpression(initializer)) return void 0;
32344
32975
  return initializer;
32345
32976
  }
32346
32977
  function resolveModelName(call) {
32347
32978
  let current = call.getParent();
32348
32979
  while (current !== void 0) {
32349
- if (Node23.isVariableDeclaration(current)) {
32980
+ if (Node24.isVariableDeclaration(current)) {
32350
32981
  return current.getName();
32351
32982
  }
32352
32983
  current = current.getParent();
@@ -32356,10 +32987,10 @@ function resolveModelName(call) {
32356
32987
  function extractFieldUsages(fieldsObj, registry) {
32357
32988
  const out = [];
32358
32989
  for (const property of fieldsObj.getProperties()) {
32359
- if (!Node23.isPropertyAssignment(property) && !Node23.isShorthandPropertyAssignment(property)) continue;
32990
+ if (!Node24.isPropertyAssignment(property) && !Node24.isShorthandPropertyAssignment(property)) continue;
32360
32991
  const fieldName = property.getName();
32361
32992
  let converterText;
32362
- if (Node23.isShorthandPropertyAssignment(property)) {
32993
+ if (Node24.isShorthandPropertyAssignment(property)) {
32363
32994
  converterText = property.getName();
32364
32995
  } else {
32365
32996
  const initializer = property.getInitializer();
@@ -33882,7 +34513,7 @@ import { type as type64 } from "arktype";
33882
34513
  // packages/dbx-components-mcp/src/tools/model-list-component/extract.ts
33883
34514
  import { readdir as readdir12, readFile as readFile20, stat as stat16 } from "node:fs/promises";
33884
34515
  import { basename as basename6, join as join19, relative as relative11, sep as sep14 } from "node:path";
33885
- import { Node as Node24, Project as Project17 } from "ts-morph";
34516
+ import { Node as Node25, Project as Project17 } from "ts-morph";
33886
34517
  var MODEL_SUBPATH3 = "src/lib/model";
33887
34518
  var MODEL_SUBPATH_PREFIX = `${MODEL_SUBPATH3}/`;
33888
34519
  var RESERVED_NAMES = RESERVED_MODEL_FOLDERS.map((r) => r.name);
@@ -34023,7 +34654,7 @@ function findIdentityIn(sourceFile) {
34023
34654
  for (const statement of sourceFile.getVariableStatements()) {
34024
34655
  for (const decl of statement.getDeclarations()) {
34025
34656
  const initializer = decl.getInitializer();
34026
- if (!initializer || !Node24.isCallExpression(initializer)) continue;
34657
+ if (!initializer || !Node25.isCallExpression(initializer)) continue;
34027
34658
  if (initializer.getExpression().getText() !== "firestoreModelIdentity") continue;
34028
34659
  const args = initializer.getArguments();
34029
34660
  const parsed = parseIdentityArgs2(args);
@@ -34043,13 +34674,13 @@ function parseIdentityArgs2(args) {
34043
34674
  return { parentIdentityRef: void 0, collectionName: stringLiteralValue3(args[0]), collectionPrefix: stringLiteralValue3(args[1]) };
34044
34675
  }
34045
34676
  if (args.length === 3) {
34046
- const parent = Node24.isIdentifier(args[0]) ? args[0].getText() : void 0;
34677
+ const parent = Node25.isIdentifier(args[0]) ? args[0].getText() : void 0;
34047
34678
  return { parentIdentityRef: parent, collectionName: stringLiteralValue3(args[1]), collectionPrefix: stringLiteralValue3(args[2]) };
34048
34679
  }
34049
34680
  return { parentIdentityRef: void 0, collectionName: void 0, collectionPrefix: void 0 };
34050
34681
  }
34051
34682
  function stringLiteralValue3(node) {
34052
- if (Node24.isStringLiteral(node) || Node24.isNoSubstitutionTemplateLiteral(node)) {
34683
+ if (Node25.isStringLiteral(node) || Node25.isNoSubstitutionTemplateLiteral(node)) {
34053
34684
  return node.getLiteralText();
34054
34685
  }
34055
34686
  return void 0;
@@ -34211,7 +34842,7 @@ import { type as type65 } from "arktype";
34211
34842
  // packages/dbx-components-mcp/src/tools/server-actions-list-app/extract.ts
34212
34843
  import { readdir as readdir13, readFile as readFile21, stat as stat17 } from "node:fs/promises";
34213
34844
  import { dirname as dirname14, join as join20, relative as relative12, sep as sep15 } from "node:path";
34214
- import { Node as Node25, Project as Project18 } from "ts-morph";
34845
+ import { Node as Node26, Project as Project18 } from "ts-morph";
34215
34846
  var ACTION_SERVER_SUFFIX2 = ".action.server.ts";
34216
34847
  var COMMON_MODEL_SUBPATH2 = "src/app/common/model";
34217
34848
  var COMMON_BARREL_REL = "src/app/common/index.ts";
@@ -34348,7 +34979,7 @@ function inspectModuleText(input) {
34348
34979
  for (const decorator of cls.getDecorators()) {
34349
34980
  if (decorator.getName() !== "Module") continue;
34350
34981
  const args = decorator.getArguments();
34351
- if (args.length === 0 || !Node25.isObjectLiteralExpression(args[0])) continue;
34982
+ if (args.length === 0 || !Node26.isObjectLiteralExpression(args[0])) continue;
34352
34983
  const obj = args[0];
34353
34984
  providedByModule ||= moduleArrayContains(obj, "providers", input.className);
34354
34985
  exportedByModule ||= moduleArrayContains(obj, "exports", input.className);
@@ -34358,16 +34989,16 @@ function inspectModuleText(input) {
34358
34989
  }
34359
34990
  function moduleArrayContains(decoratorArg, propertyName, className) {
34360
34991
  const property = decoratorArg.getProperty(propertyName);
34361
- if (!property || !Node25.isPropertyAssignment(property)) return false;
34992
+ if (!property || !Node26.isPropertyAssignment(property)) return false;
34362
34993
  const initializer = property.getInitializer();
34363
- if (!initializer || !Node25.isArrayLiteralExpression(initializer)) return false;
34994
+ if (!initializer || !Node26.isArrayLiteralExpression(initializer)) return false;
34364
34995
  for (const element of initializer.getElements()) {
34365
- if (Node25.isIdentifier(element) && element.getText() === className) return true;
34366
- if (Node25.isObjectLiteralExpression(element)) {
34996
+ if (Node26.isIdentifier(element) && element.getText() === className) return true;
34997
+ if (Node26.isObjectLiteralExpression(element)) {
34367
34998
  const provideProp = element.getProperty("provide");
34368
- if (provideProp && Node25.isPropertyAssignment(provideProp)) {
34999
+ if (provideProp && Node26.isPropertyAssignment(provideProp)) {
34369
35000
  const provideInit = provideProp.getInitializer();
34370
- if (provideInit && Node25.isIdentifier(provideInit) && provideInit.getText() === className) return true;
35001
+ if (provideInit && Node26.isIdentifier(provideInit) && provideInit.getText() === className) return true;
34371
35002
  }
34372
35003
  }
34373
35004
  }
@@ -34421,11 +35052,11 @@ function collectInterfaceGetters(sourceFile) {
34421
35052
  const out = /* @__PURE__ */ new Map();
34422
35053
  for (const iface of sourceFile.getInterfaces()) {
34423
35054
  for (const member of iface.getMembers()) {
34424
- if (Node25.isGetAccessorDeclaration(member)) {
35055
+ if (Node26.isGetAccessorDeclaration(member)) {
34425
35056
  out.set(member.getName(), iface.getName());
34426
35057
  continue;
34427
35058
  }
34428
- if (Node25.isPropertySignature(member)) {
35059
+ if (Node26.isPropertySignature(member)) {
34429
35060
  out.set(member.getName(), iface.getName());
34430
35061
  }
34431
35062
  }
@@ -34437,7 +35068,7 @@ function collectClassGetters(sourceFile) {
34437
35068
  for (const cls of sourceFile.getClasses()) {
34438
35069
  const set = /* @__PURE__ */ new Set();
34439
35070
  for (const member of cls.getMembers()) {
34440
- if (Node25.isGetAccessorDeclaration(member)) set.add(member.getName());
35071
+ if (Node26.isGetAccessorDeclaration(member)) set.add(member.getName());
34441
35072
  }
34442
35073
  if (set.size > 0) out.set(cls.getName() ?? "<anon>", set);
34443
35074
  }
@@ -34949,7 +35580,7 @@ import { resolve as resolve43 } from "node:path";
34949
35580
  import { type as type67 } from "arktype";
34950
35581
 
34951
35582
  // packages/dbx-components-mcp/src/scan/actions-extract.ts
34952
- import { Node as Node26, SyntaxKind as SyntaxKind13 } from "ts-morph";
35583
+ import { Node as Node27, SyntaxKind as SyntaxKind13 } from "ts-morph";
34953
35584
  var ACTION_MARKER = "dbxAction";
34954
35585
  var ACTION_SLUG_TAG = "dbxActionSlug";
34955
35586
  var ACTION_ROLE_TAG = "dbxActionRole";
@@ -35157,7 +35788,7 @@ function filterStates(values) {
35157
35788
  }
35158
35789
  function readEnumMemberLiteral(member) {
35159
35790
  const initializer = member.getInitializer();
35160
- if (initializer !== void 0 && (Node26.isStringLiteral(initializer) || Node26.isNoSubstitutionTemplateLiteral(initializer))) {
35791
+ if (initializer !== void 0 && (Node27.isStringLiteral(initializer) || Node27.isNoSubstitutionTemplateLiteral(initializer))) {
35161
35792
  return initializer.getLiteralText();
35162
35793
  }
35163
35794
  return member.getName();
@@ -35777,7 +36408,7 @@ import { resolve as resolve45 } from "node:path";
35777
36408
  import { type as type71 } from "arktype";
35778
36409
 
35779
36410
  // packages/dbx-components-mcp/src/scan/pipes-extract.ts
35780
- import { Node as Node27 } from "ts-morph";
36411
+ import { Node as Node28 } from "ts-morph";
35781
36412
  var PIPE_MARKER = "dbxPipe";
35782
36413
  var PIPE_SLUG_TAG = "dbxPipeSlug";
35783
36414
  var PIPE_CATEGORY_TAG = "dbxPipeCategory";
@@ -35970,7 +36601,7 @@ function readPipeDecoratorConfig(decorator) {
35970
36601
  return void 0;
35971
36602
  }
35972
36603
  const firstArg = args[0];
35973
- if (!Node27.isObjectLiteralExpression(firstArg)) {
36604
+ if (!Node28.isObjectLiteralExpression(firstArg)) {
35974
36605
  return void 0;
35975
36606
  }
35976
36607
  const name = readStringProperty(firstArg, "name");
@@ -35983,7 +36614,7 @@ function readPipeDecoratorConfig(decorator) {
35983
36614
  }
35984
36615
  function readBooleanProperty(obj, propName) {
35985
36616
  const prop = obj.getProperty(propName);
35986
- if (prop === void 0 || !Node27.isPropertyAssignment(prop)) {
36617
+ if (prop === void 0 || !Node28.isPropertyAssignment(prop)) {
35987
36618
  return void 0;
35988
36619
  }
35989
36620
  const initializer = prop.getInitializer();
@@ -39781,7 +40412,6 @@ var DBX_TOOLS = [
39781
40412
  modelApiListAppTool,
39782
40413
  modelApiLookupTool,
39783
40414
  modelApiValidateAppTool,
39784
- modelValidateFolderTool,
39785
40415
  modelStoreScaffoldTool,
39786
40416
  modelFixtureListAppTool,
39787
40417
  modelFixtureLookupTool,
@@ -39833,7 +40463,7 @@ function createAuthClusterTools(registry) {
39833
40463
  function registerTools(server, options = {}) {
39834
40464
  const underlyingServer = server.server;
39835
40465
  const tools = [...DBX_TOOLS];
39836
- tools.push(createUiExamplesTool({ examplesRegistry: options.dbxDocsUiExamplesRegistry }), createModelValidateTool({ ruleOptions: options.modelValidateRuleOptions }), createModelFixtureValidateAppTool({ getRegistry: () => options.fixtureModelRegistry }));
40466
+ tools.push(createUiExamplesTool({ examplesRegistry: options.dbxDocsUiExamplesRegistry }), createModelValidateTool({ ruleOptions: options.modelValidateRuleOptions }), createModelValidateFolderTool({ ruleOptions: options.modelValidateRuleOptions }), createModelFixtureValidateAppTool({ getRegistry: () => options.fixtureModelRegistry }));
39837
40467
  if (options.forgeFieldRegistry !== void 0) {
39838
40468
  tools.push(createLookupFormTool({ registry: options.forgeFieldRegistry }), createSearchFormTool({ registry: options.forgeFieldRegistry }), createFormScaffoldTool({ registry: options.forgeFieldRegistry }));
39839
40469
  }
@@ -40158,9 +40788,11 @@ function resolveModelValidateRuleOptions(config) {
40158
40788
  return void 0;
40159
40789
  }
40160
40790
  const ignoredFieldNames = block.ignoredFieldNames === void 0 ? void 0 : new Set(block.ignoredFieldNames);
40791
+ const ignoredExternalParents = block.ignoredExternalParents === void 0 ? void 0 : new Set(block.ignoredExternalParents);
40161
40792
  const result = {
40162
40793
  maxFieldNameLength: block.maxFieldNameLength,
40163
- ignoredFieldNames
40794
+ ignoredFieldNames,
40795
+ ignoredExternalParents
40164
40796
  };
40165
40797
  return result;
40166
40798
  }