@dereekb/dbx-components-mcp 13.15.0 → 13.16.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
@@ -10,7 +10,7 @@ var __export = (target, all) => {
10
10
  // packages/dbx-components-mcp/package.json
11
11
  var package_default = {
12
12
  name: "@dereekb/dbx-components-mcp",
13
- version: "13.15.0",
13
+ version: "13.16.0",
14
14
  type: "module",
15
15
  bin: {
16
16
  "dbx-components-mcp": "dbx-components-mcp.js"
@@ -21,8 +21,8 @@ var package_default = {
21
21
  "ts-morph": "^21.0.0"
22
22
  },
23
23
  peerDependencies: {
24
- "@dereekb/dbx-cli": "13.15.0",
25
- "@dereekb/util": "13.15.0"
24
+ "@dereekb/dbx-cli": "13.16.0",
25
+ "@dereekb/util": "13.16.0"
26
26
  }
27
27
  };
28
28
 
@@ -3515,14 +3515,25 @@ function buildModelEntry(args) {
3515
3515
  sourcePackage: input.sourcePackage,
3516
3516
  sourceFile: input.sourceFile,
3517
3517
  fields,
3518
- enums: relevantEnums.map((e) => ({
3519
- name: e.name,
3520
- values: e.values.map((v) => v.description ? { name: v.name, value: v.value, description: v.description } : { name: v.name, value: v.value }),
3521
- ...e.description ? { description: e.description } : {}
3522
- })),
3518
+ enums: relevantEnums.map((e) => buildModelEnumEntry(e)),
3523
3519
  detectionHints,
3524
3520
  ...groupName ? { modelGroup: groupName } : {},
3525
3521
  ...collectionKind ? { collectionKind } : {},
3522
+ ...buildModelMarkerFlags(markers),
3523
+ ...buildModelArchetypeFields(archetypeInfo),
3524
+ ...compositeKey2 ? { compositeKey: compositeKey2 } : {},
3525
+ ...iface.tags.dbxModelRead ? { read: iface.tags.dbxModelRead } : {}
3526
+ };
3527
+ }
3528
+ function buildModelEnumEntry(e) {
3529
+ return {
3530
+ name: e.name,
3531
+ values: e.values.map((v) => v.description ? { name: v.name, value: v.value, description: v.description } : { name: v.name, value: v.value }),
3532
+ ...e.description ? { description: e.description } : {}
3533
+ };
3534
+ }
3535
+ function buildModelMarkerFlags(markers) {
3536
+ return {
3526
3537
  ...markers.userKeyedById ? { userKeyedById: true } : {},
3527
3538
  ...markers.hasUserUidField ? { hasUserUidField: true } : {},
3528
3539
  ...markers.regionKeyedById ? { regionKeyedById: true } : {},
@@ -3530,11 +3541,13 @@ function buildModelEntry(args) {
3530
3541
  ...markers.externalIdKeyedById ? { externalIdKeyedById: true } : {},
3531
3542
  ...markers.bucketKeyedById ? { bucketKeyedById: true } : {},
3532
3543
  ...markers.organizationalGroupRoot ? { organizationalGroupRoot: true } : {},
3533
- ...markers.aggregatesFromNonEmpty ? { aggregatesFrom: markers.aggregatesFrom } : {},
3544
+ ...markers.aggregatesFromNonEmpty ? { aggregatesFrom: markers.aggregatesFrom } : {}
3545
+ };
3546
+ }
3547
+ function buildModelArchetypeFields(archetypeInfo) {
3548
+ return {
3534
3549
  ...archetypeInfo.archetypes.length > 0 ? { archetypes: archetypeInfo.archetypes } : {},
3535
- ...archetypeInfo.archetypeAxesBySlug ? { archetypeAxesBySlug: archetypeInfo.archetypeAxesBySlug } : {},
3536
- ...compositeKey2 ? { compositeKey: compositeKey2 } : {},
3537
- ...iface.tags.dbxModelRead ? { read: iface.tags.dbxModelRead } : {}
3550
+ ...archetypeInfo.archetypeAxesBySlug ? { archetypeAxesBySlug: archetypeInfo.archetypeAxesBySlug } : {}
3538
3551
  };
3539
3552
  }
3540
3553
  function filterRelevantEnums(fields, enums) {
@@ -4070,20 +4083,22 @@ function parseCompositeKeyTagValue(value) {
4070
4083
  const key = token.slice(0, eq).trim();
4071
4084
  const v = token.slice(eq + 1).trim();
4072
4085
  if (key === "from") {
4073
- if (v === "*") {
4074
- fromValue = "*";
4075
- } else if (v.length > 0) {
4076
- const parts = v.split(",").map((p) => p.trim()).filter((p) => p.length > 0);
4077
- if (parts.includes("*")) {
4078
- fromValue = parts;
4079
- } else {
4080
- fromValue = parts.filter((p) => COMPOSITE_KEY_MODEL_NAME_RE.test(p));
4081
- }
4082
- }
4086
+ const parsedFrom = parseCompositeKeyFromValue(v);
4087
+ if (parsedFrom !== void 0) fromValue = parsedFrom;
4083
4088
  } else if (key === "encoding" && (v === "two-way" || v === "one-way")) encoding = v;
4084
4089
  }
4085
4090
  return { from: fromValue, encoding };
4086
4091
  }
4092
+ function parseCompositeKeyFromValue(v) {
4093
+ let result;
4094
+ if (v === "*") {
4095
+ result = "*";
4096
+ } else if (v.length > 0) {
4097
+ const parts = v.split(",").map((p) => p.trim()).filter((p) => p.length > 0);
4098
+ result = parts.includes("*") ? parts : parts.filter((p) => COMPOSITE_KEY_MODEL_NAME_RE.test(p));
4099
+ }
4100
+ return result;
4101
+ }
4087
4102
  function takeFirstTagText(current, tag) {
4088
4103
  if (current !== void 0) return current;
4089
4104
  const text = tag.getCommentText()?.trim();
@@ -7458,9 +7473,7 @@ var EMPTY_ROLE_CONST_RESOLUTION = { roles: [], unresolved: [] };
7458
7473
  function resolveRoleConstByName(name, ctx) {
7459
7474
  let result;
7460
7475
  const scalar = ctx.knownRoles.get(name);
7461
- if (scalar !== void 0) {
7462
- result = { roles: [scalar], unresolved: [] };
7463
- } else {
7476
+ if (scalar === void 0) {
7464
7477
  const arrayExpr = ctx.localArrayRoles.get(name);
7465
7478
  if (arrayExpr === void 0) {
7466
7479
  result = { roles: [], unresolved: [name] };
@@ -7471,6 +7484,8 @@ function resolveRoleConstByName(name, ctx) {
7471
7484
  nextSeen.add(name);
7472
7485
  result = resolveRoleArrayLiteral(arrayExpr, { ...ctx, seen: nextSeen });
7473
7486
  }
7487
+ } else {
7488
+ result = { roles: [scalar], unresolved: [] };
7474
7489
  }
7475
7490
  return result;
7476
7491
  }
@@ -11561,30 +11576,37 @@ async function scanFactoryReferences(input) {
11561
11576
  const filePaths = await globber({ projectRoot, include, exclude: [...exclude] });
11562
11577
  const combinedPattern = buildCombinedRegex(Array.from(namesBySlug.values()));
11563
11578
  if (combinedPattern !== void 0) {
11564
- const sitesBySlug = /* @__PURE__ */ new Map();
11565
- for (const entry of entries) {
11566
- sitesBySlug.set(entry.slug, []);
11567
- }
11568
- for (const relPath of filePaths) {
11569
- const absolutePath = resolvePath2(projectRoot, relPath);
11570
- if (declFilePaths.has(absolutePath)) {
11571
- continue;
11572
- }
11573
- await scanOneFile({ absolutePath, projectRoot, readFile: readFile30, combinedPattern, namesBySlug, sitesBySlug });
11574
- }
11579
+ const sitesBySlug = await collectReferenceSites({ entries, filePaths, projectRoot, declFilePaths, readFile: readFile30, combinedPattern, namesBySlug });
11575
11580
  for (const [slug, sites] of sitesBySlug) {
11576
- let specCount = 0;
11577
- for (const site of sites) {
11578
- if (site.isSpec) {
11579
- specCount += 1;
11580
- }
11581
- }
11582
- result.set(slug, { count: sites.length, productionCount: sites.length - specCount, specCount, referencedBy: sites });
11581
+ result.set(slug, tallyReferenceSites(sites));
11583
11582
  }
11584
11583
  }
11585
11584
  }
11586
11585
  return result;
11587
11586
  }
11587
+ async function collectReferenceSites(input) {
11588
+ const { entries, filePaths, projectRoot, declFilePaths, readFile: readFile30, combinedPattern, namesBySlug } = input;
11589
+ const sitesBySlug = /* @__PURE__ */ new Map();
11590
+ for (const entry of entries) {
11591
+ sitesBySlug.set(entry.slug, []);
11592
+ }
11593
+ for (const relPath of filePaths) {
11594
+ const absolutePath = resolvePath2(projectRoot, relPath);
11595
+ if (!declFilePaths.has(absolutePath)) {
11596
+ await scanOneFile({ absolutePath, projectRoot, readFile: readFile30, combinedPattern, namesBySlug, sitesBySlug });
11597
+ }
11598
+ }
11599
+ return sitesBySlug;
11600
+ }
11601
+ function tallyReferenceSites(sites) {
11602
+ let specCount = 0;
11603
+ for (const site of sites) {
11604
+ if (site.isSpec) {
11605
+ specCount += 1;
11606
+ }
11607
+ }
11608
+ return { count: sites.length, productionCount: sites.length - specCount, specCount, referencedBy: sites };
11609
+ }
11588
11610
  function isSpecSubpath(subpath) {
11589
11611
  return subpath.endsWith(".spec.ts") || subpath.endsWith(".spec.tsx");
11590
11612
  }
@@ -11645,7 +11667,7 @@ function buildCombinedRegex(names) {
11645
11667
  return result;
11646
11668
  }
11647
11669
  function escapeRegex(value) {
11648
- return value.replace(/[.*+?^${}()|[\]\\]/g, String.raw`\$&`);
11670
+ return value.replaceAll(/[.*+?^${}()|[\]\\]/g, String.raw`\$&`);
11649
11671
  }
11650
11672
  function computeLineOffsets(contents) {
11651
11673
  const offsets = [0];
@@ -14257,19 +14279,20 @@ function collectExternalCssUtilitySources(configResult) {
14257
14279
  const baseDir = dirname4(configResult.configPath);
14258
14280
  const cluster = configResult.config.cssUtilities;
14259
14281
  for (const source of cluster?.sources ?? []) {
14260
- const absolute = isAbsolute4(source) ? source : resolve21(baseDir, source);
14261
- externalSources.push({ origin: "external", path: absolute });
14282
+ externalSources.push({ origin: "external", path: toAbsolutePath(baseDir, source) });
14262
14283
  }
14263
14284
  for (const scan of cluster?.scan ?? []) {
14264
14285
  const out = scan.out;
14265
14286
  if (typeof out === "string" && out.length > 0) {
14266
- const absolute = isAbsolute4(out) ? out : resolve21(baseDir, out);
14267
- externalSources.push({ origin: "external", path: absolute });
14287
+ externalSources.push({ origin: "external", path: toAbsolutePath(baseDir, out) });
14268
14288
  }
14269
14289
  }
14270
14290
  }
14271
14291
  return externalSources;
14272
14292
  }
14293
+ function toAbsolutePath(baseDir, value) {
14294
+ return isAbsolute4(value) ? value : resolve21(baseDir, value);
14295
+ }
14273
14296
 
14274
14297
  // packages/dbx-cli/src/lib/mcp-scan/manifest/load-dbx-docs-ui-examples-registry.ts
14275
14298
  import { existsSync as existsSync4 } from "node:fs";
@@ -14929,38 +14952,51 @@ function parseArgv(argv) {
14929
14952
  return { component, output, check, json, help, error };
14930
14953
  }
14931
14954
  async function readExistingIndexes(input) {
14955
+ const text = await readExistingIndexesText(input);
14956
+ let result;
14957
+ if (text !== void 0) {
14958
+ result = parseExistingIndexesJson(text, input.outputAbs, input.stderr);
14959
+ }
14960
+ return result;
14961
+ }
14962
+ async function readExistingIndexesText(input) {
14932
14963
  const { outputAbs, readFile: readFile30, stderr } = input;
14933
- let text = null;
14934
- let readFailed = false;
14964
+ let text;
14935
14965
  try {
14936
14966
  text = await readFile30(outputAbs);
14937
14967
  } catch (err) {
14938
- readFailed = true;
14939
14968
  const code17 = err.code;
14940
14969
  if (code17 !== "ENOENT") {
14941
14970
  stderr(`generate-firestore-indexes: could not read existing ${outputAbs}: ${err instanceof Error ? err.message : String(err)}`);
14942
14971
  }
14972
+ text = void 0;
14943
14973
  }
14974
+ return text;
14975
+ }
14976
+ function parseExistingIndexesJson(text, outputAbs, stderr) {
14944
14977
  let result;
14945
- if (!readFailed && text !== null) {
14946
- let parsed;
14947
- let parseFailed = false;
14948
- try {
14949
- parsed = JSON.parse(text);
14950
- } catch (err) {
14951
- parseFailed = true;
14952
- stderr(`generate-firestore-indexes: existing ${outputAbs} is not valid JSON: ${err instanceof Error ? err.message : String(err)}`);
14953
- }
14954
- if (!parseFailed) {
14955
- if (parsed === null || typeof parsed !== "object") {
14956
- stderr(`generate-firestore-indexes: existing ${outputAbs} top-level value is not an object`);
14957
- } else {
14958
- const raw = parsed;
14959
- const indexes = Array.isArray(raw.indexes) ? raw.indexes : [];
14960
- const fieldOverrides = Array.isArray(raw.fieldOverrides) ? raw.fieldOverrides : [];
14961
- result = { indexes, fieldOverrides };
14962
- }
14963
- }
14978
+ let parsed;
14979
+ let parseFailed = false;
14980
+ try {
14981
+ parsed = JSON.parse(text);
14982
+ } catch (err) {
14983
+ parseFailed = true;
14984
+ stderr(`generate-firestore-indexes: existing ${outputAbs} is not valid JSON: ${err instanceof Error ? err.message : String(err)}`);
14985
+ }
14986
+ if (!parseFailed) {
14987
+ result = coerceFirestoreIndexesJson(parsed, outputAbs, stderr);
14988
+ }
14989
+ return result;
14990
+ }
14991
+ function coerceFirestoreIndexesJson(parsed, outputAbs, stderr) {
14992
+ let result;
14993
+ if (parsed === null || typeof parsed !== "object") {
14994
+ stderr(`generate-firestore-indexes: existing ${outputAbs} top-level value is not an object`);
14995
+ } else {
14996
+ const raw = parsed;
14997
+ const indexes = Array.isArray(raw.indexes) ? raw.indexes : [];
14998
+ const fieldOverrides = Array.isArray(raw.fieldOverrides) ? raw.fieldOverrides : [];
14999
+ result = { indexes, fieldOverrides };
14964
15000
  }
14965
15001
  return result;
14966
15002
  }
@@ -15326,19 +15362,20 @@ function collectExternalTokenSources(configResult) {
15326
15362
  const baseDir = dirname11(configResult.configPath);
15327
15363
  const tokensCluster = configResult.config.tokens;
15328
15364
  for (const source of tokensCluster?.sources ?? []) {
15329
- const absolute = isAbsolute11(source) ? source : resolve29(baseDir, source);
15330
- externalSources.push({ origin: "external", path: absolute });
15365
+ externalSources.push({ origin: "external", path: toAbsolutePath2(baseDir, source) });
15331
15366
  }
15332
15367
  for (const scan of tokensCluster?.scan ?? []) {
15333
15368
  const out = scan.out;
15334
15369
  if (typeof out === "string" && out.length > 0) {
15335
- const absolute = isAbsolute11(out) ? out : resolve29(baseDir, out);
15336
- externalSources.push({ origin: "external", path: absolute });
15370
+ externalSources.push({ origin: "external", path: toAbsolutePath2(baseDir, out) });
15337
15371
  }
15338
15372
  }
15339
15373
  }
15340
15374
  return externalSources;
15341
15375
  }
15376
+ function toAbsolutePath2(baseDir, value) {
15377
+ return isAbsolute11(value) ? value : resolve29(baseDir, value);
15378
+ }
15342
15379
 
15343
15380
  // packages/dbx-cli/src/lib/mcp-scan/manifest/load-ui-components-registry.ts
15344
15381
  import { existsSync as existsSync11 } from "node:fs";
@@ -24326,14 +24363,14 @@ function parseCompositeKeyTag(tag) {
24326
24363
  const key = token.slice(0, eq).trim();
24327
24364
  const v = token.slice(eq + 1).trim();
24328
24365
  if (key === "from") {
24329
- fromValue = parseCompositeKeyFromValue(v);
24366
+ fromValue = parseCompositeKeyFromValue2(v);
24330
24367
  } else if (key === "encoding" && (v === "two-way" || v === "one-way")) {
24331
24368
  encoding = v;
24332
24369
  }
24333
24370
  }
24334
24371
  return { from: fromValue, encoding, line: tag.getStartLineNumber() };
24335
24372
  }
24336
- function parseCompositeKeyFromValue(v) {
24373
+ function parseCompositeKeyFromValue2(v) {
24337
24374
  if (v === "*") return "*";
24338
24375
  if (v.length === 0) return [];
24339
24376
  const parts = v.split(",").map((p) => p.trim()).filter((p) => p.length > 0);
@@ -26928,8 +26965,8 @@ __export(model_api_validate_app_exports, {
26928
26965
  });
26929
26966
 
26930
26967
  // packages/dbx-cli/validate/src/lib/model-api-validate-app/extract-declarations.ts
26931
- import { readdir as readdir4, readFile as readFile7, stat as stat7 } from "node:fs/promises";
26932
- import { join as join7, relative as relative8, sep as sep6 } from "node:path";
26968
+ import { readFile as readFile7 } from "node:fs/promises";
26969
+ import { relative as relative8, sep as sep6 } from "node:path";
26933
26970
 
26934
26971
  // packages/dbx-cli/manifest-extract/src/lib/extract-crud.ts
26935
26972
  import { Node as Node22, Project as Project6 } from "ts-morph";
@@ -27056,29 +27093,20 @@ function findFunctionsClassName(sourceFile) {
27056
27093
  return result;
27057
27094
  }
27058
27095
  function inferGroupName2(sourceFile) {
27096
+ return findTypeAliasStem(sourceFile, "ModelCrudFunctionsConfig") ?? findTypeAliasStem(sourceFile, "FunctionTypeMap");
27097
+ }
27098
+ function findTypeAliasStem(sourceFile, ending) {
27059
27099
  let result;
27060
27100
  for (const alias of sourceFile.getTypeAliases()) {
27061
27101
  const name = alias.getName();
27062
- if (name.endsWith("ModelCrudFunctionsConfig")) {
27063
- const stem = name.slice(0, -"ModelCrudFunctionsConfig".length);
27102
+ if (name.endsWith(ending)) {
27103
+ const stem = name.slice(0, -ending.length);
27064
27104
  if (stem.length > 0) {
27065
27105
  result = stem;
27066
27106
  break;
27067
27107
  }
27068
27108
  }
27069
27109
  }
27070
- if (result === void 0) {
27071
- for (const alias of sourceFile.getTypeAliases()) {
27072
- const name = alias.getName();
27073
- if (name.endsWith("FunctionTypeMap")) {
27074
- const stem = name.slice(0, -"FunctionTypeMap".length);
27075
- if (stem.length > 0) {
27076
- result = stem;
27077
- break;
27078
- }
27079
- }
27080
- }
27081
- }
27082
27110
  return result;
27083
27111
  }
27084
27112
  function isNullLiteralType2(node) {
@@ -27174,44 +27202,44 @@ function typeNodeName(node) {
27174
27202
  return result;
27175
27203
  }
27176
27204
  function readTypeDocs(sourceFile, typeName) {
27177
- let result;
27178
27205
  const interfaceDecl = sourceFile.getInterface(typeName);
27206
+ let result;
27179
27207
  if (interfaceDecl) {
27180
- const typeDescription = readJsDocSummary(interfaceDecl);
27181
- const hasApiParamsTag = hasJsDocFlag(interfaceDecl, "dbxModelApiParams");
27182
- const fields = [];
27183
- for (const property of interfaceDecl.getProperties()) {
27184
- const fieldName = property.getName();
27185
- const description = readJsDocSummary(property);
27186
- const typeNode = property.getTypeNode();
27187
- const typeText = typeNode?.getText().trim() ?? "";
27188
- const adminOnly = hasJsDocFlag(property, "dbxModelApiAdminOnly");
27189
- const field = {
27190
- name: fieldName,
27191
- typeText,
27192
- ...description ? { description } : {},
27193
- ...adminOnly ? { accessLevel: "adminOnly" } : {}
27194
- };
27195
- fields.push(field);
27196
- }
27197
- if (typeDescription || fields.length > 0 || hasApiParamsTag) {
27198
- result = {
27199
- ...typeDescription ? { typeDescription } : {},
27200
- ...fields.length > 0 ? { fields } : {},
27201
- hasApiParamsTag
27202
- };
27203
- }
27208
+ result = readInterfaceTypeDocs(interfaceDecl);
27204
27209
  } else {
27205
27210
  const typeAlias = sourceFile.getTypeAlias(typeName);
27206
- if (typeAlias) {
27207
- const typeDescription = readJsDocSummary(typeAlias);
27208
- if (typeDescription) {
27209
- result = { typeDescription };
27210
- }
27211
- }
27211
+ result = typeAlias ? readTypeAliasDocs(typeAlias) : void 0;
27212
27212
  }
27213
27213
  return result;
27214
27214
  }
27215
+ function readInterfaceTypeDocs(interfaceDecl) {
27216
+ const typeDescription = readJsDocSummary(interfaceDecl);
27217
+ const hasApiParamsTag = hasJsDocFlag(interfaceDecl, "dbxModelApiParams");
27218
+ const fields = interfaceDecl.getProperties().map((property) => readInterfaceField(property));
27219
+ let result;
27220
+ if (typeDescription || fields.length > 0 || hasApiParamsTag) {
27221
+ result = {
27222
+ ...typeDescription ? { typeDescription } : {},
27223
+ ...fields.length > 0 ? { fields } : {},
27224
+ hasApiParamsTag
27225
+ };
27226
+ }
27227
+ return result;
27228
+ }
27229
+ function readInterfaceField(property) {
27230
+ const description = readJsDocSummary(property);
27231
+ const adminOnly = hasJsDocFlag(property, "dbxModelApiAdminOnly");
27232
+ return {
27233
+ name: property.getName(),
27234
+ typeText: property.getTypeNode()?.getText().trim() ?? "",
27235
+ ...description ? { description } : {},
27236
+ ...adminOnly ? { accessLevel: "adminOnly" } : {}
27237
+ };
27238
+ }
27239
+ function readTypeAliasDocs(typeAlias) {
27240
+ const typeDescription = readJsDocSummary(typeAlias);
27241
+ return typeDescription ? { typeDescription } : void 0;
27242
+ }
27215
27243
  function hasJsDocFlag(node, tagName) {
27216
27244
  let result = false;
27217
27245
  for (const doc of node.getJsDocs()) {
@@ -27240,8 +27268,8 @@ function readJsDocTagValue(node, tagName) {
27240
27268
  function readJsDocSummary(node) {
27241
27269
  let result;
27242
27270
  const docs = node.getJsDocs();
27243
- if (docs.length > 0) {
27244
- const last = docs[docs.length - 1];
27271
+ const last = docs.at(-1);
27272
+ if (last) {
27245
27273
  const description = last.getDescription().trim();
27246
27274
  if (description.length > 0) {
27247
27275
  result = description;
@@ -27250,6 +27278,59 @@ function readJsDocSummary(node) {
27250
27278
  return result;
27251
27279
  }
27252
27280
 
27281
+ // packages/dbx-cli/validate/src/lib/_core/collect-files.ts
27282
+ import { readdir as readdir4, stat as stat7 } from "node:fs/promises";
27283
+ import { join as join7 } from "node:path";
27284
+ var SPEC_SUFFIX2 = ".spec.ts";
27285
+ async function collectFilesWithSuffix(rootAbs, suffix) {
27286
+ const out = [];
27287
+ if (await isDirectoryPath(rootAbs)) {
27288
+ const stack = [rootAbs];
27289
+ while (stack.length > 0) {
27290
+ const current = stack.pop();
27291
+ const entries = await readDirEntriesSafe(current);
27292
+ const partition = partitionDirEntries({ entries, current, suffix });
27293
+ stack.push(...partition.dirs);
27294
+ out.push(...partition.files);
27295
+ }
27296
+ out.sort((a, b) => a.localeCompare(b));
27297
+ }
27298
+ return out;
27299
+ }
27300
+ function partitionDirEntries(input) {
27301
+ const { entries, current, suffix } = input;
27302
+ const dirs = [];
27303
+ const files = [];
27304
+ for (const entry of entries) {
27305
+ const full = join7(current, entry.name);
27306
+ if (entry.isDirectory()) {
27307
+ dirs.push(full);
27308
+ } else if (entry.isFile() && entry.name.endsWith(suffix) && !entry.name.endsWith(SPEC_SUFFIX2)) {
27309
+ files.push(full);
27310
+ }
27311
+ }
27312
+ return { dirs, files };
27313
+ }
27314
+ async function isDirectoryPath(path) {
27315
+ let result = false;
27316
+ try {
27317
+ const stats = await stat7(path);
27318
+ result = stats.isDirectory();
27319
+ } catch {
27320
+ result = false;
27321
+ }
27322
+ return result;
27323
+ }
27324
+ async function readDirEntriesSafe(path) {
27325
+ let result;
27326
+ try {
27327
+ result = await readdir4(path, { withFileTypes: true });
27328
+ } catch {
27329
+ result = [];
27330
+ }
27331
+ return result;
27332
+ }
27333
+
27253
27334
  // packages/dbx-cli/validate/src/lib/model-api-validate-app/extract-declarations.ts
27254
27335
  var API_SUFFIX = ".api.ts";
27255
27336
  async function extractDeclaredEntriesFromFile(fileAbs, root) {
@@ -27276,7 +27357,7 @@ async function extractDeclaredEntries(roots) {
27276
27357
  const seen = /* @__PURE__ */ new Set();
27277
27358
  const out = [];
27278
27359
  for (const root of roots) {
27279
- const files = await collectApiFiles(root.absDir);
27360
+ const files = await collectFilesWithSuffix(root.absDir, API_SUFFIX);
27280
27361
  for (const fileAbs of files) {
27281
27362
  if (seen.has(fileAbs)) continue;
27282
27363
  seen.add(fileAbs);
@@ -27286,45 +27367,6 @@ async function extractDeclaredEntries(roots) {
27286
27367
  }
27287
27368
  return out;
27288
27369
  }
27289
- async function readDirSafe(path) {
27290
- let result;
27291
- try {
27292
- result = await readdir4(path, { withFileTypes: true });
27293
- } catch {
27294
- result = [];
27295
- }
27296
- return result;
27297
- }
27298
- async function collectApiFiles(rootAbs) {
27299
- const files = [];
27300
- const stack = [];
27301
- let isDir = false;
27302
- try {
27303
- const stats = await stat7(rootAbs);
27304
- isDir = stats.isDirectory();
27305
- } catch {
27306
- isDir = false;
27307
- }
27308
- if (isDir) {
27309
- stack.push(rootAbs);
27310
- while (stack.length > 0) {
27311
- const current = stack.pop();
27312
- const entries = await readDirSafe(current);
27313
- for (const entry of entries) {
27314
- const full = join7(current, entry.name);
27315
- if (entry.isDirectory()) {
27316
- stack.push(full);
27317
- continue;
27318
- }
27319
- if (entry.isFile() && entry.name.endsWith(API_SUFFIX) && !entry.name.endsWith(".spec.ts")) {
27320
- files.push(full);
27321
- }
27322
- }
27323
- }
27324
- files.sort((a, b) => a.localeCompare(b));
27325
- }
27326
- return files;
27327
- }
27328
27370
 
27329
27371
  // packages/dbx-cli/validate/src/lib/model-api-validate-app/extract-handlers.ts
27330
27372
  import { readFile as readFile8 } from "node:fs/promises";
@@ -27354,23 +27396,27 @@ async function extractHandlerEntries(apiAbs, apiDir) {
27354
27396
  } else {
27355
27397
  const project = new Project7({ useInMemoryFileSystem: true, skipAddingFilesFromTsConfig: true });
27356
27398
  const sourceFile = project.createSourceFile(sourceFileRel, text, { overwrite: true });
27357
- const entries = [];
27358
- const verbsFound = [];
27359
- for (const stmt of sourceFile.getVariableStatements()) {
27360
- for (const decl of stmt.getDeclarations()) {
27361
- const name = decl.getName();
27362
- const verb = matchVerbFromConstName(name);
27363
- if (!verb) continue;
27364
- const initializer = decl.getInitializer();
27365
- if (!initializer || !Node23.isObjectLiteralExpression(initializer)) continue;
27366
- verbsFound.push(verb);
27367
- collectVerbMap({ verb, mapLiteral: initializer, sourceFileRel, entries });
27368
- }
27369
- }
27399
+ const { entries, verbsFound } = collectHandlerMap(sourceFile, sourceFileRel);
27370
27400
  result = { path: fullPath, status: { kind: "ok", verbsFound }, entries };
27371
27401
  }
27372
27402
  return result;
27373
27403
  }
27404
+ function collectHandlerMap(sourceFile, sourceFileRel) {
27405
+ const entries = [];
27406
+ const verbsFound = [];
27407
+ for (const stmt of sourceFile.getVariableStatements()) {
27408
+ for (const decl of stmt.getDeclarations()) {
27409
+ const name = decl.getName();
27410
+ const verb = matchVerbFromConstName(name);
27411
+ if (!verb) continue;
27412
+ const initializer = decl.getInitializer();
27413
+ if (!initializer || !Node23.isObjectLiteralExpression(initializer)) continue;
27414
+ verbsFound.push(verb);
27415
+ collectVerbMap({ verb, mapLiteral: initializer, sourceFileRel, entries });
27416
+ }
27417
+ }
27418
+ return { entries, verbsFound };
27419
+ }
27374
27420
  function matchVerbFromConstName(name) {
27375
27421
  for (const { token, verb } of VERB_TOKENS) {
27376
27422
  if (name.endsWith(`${token}ModelMap`) || name.endsWith(`_${token.toUpperCase()}_MODEL_MAP`)) {
@@ -27551,6 +27597,7 @@ var TSCONFIG_BASE = "tsconfig.base.json";
27551
27597
  var COMPONENT_PACKAGE_JSON = "package.json";
27552
27598
  var SRC_LIB_SUBPATH = "src/lib";
27553
27599
  var DBX_COMPONENTS_BASE_SCOPE = "@dereekb/";
27600
+ var DEPENDENCY_FIELDS = ["dependencies", "devDependencies", "peerDependencies"];
27554
27601
  async function resolveApiSourceRoots(input) {
27555
27602
  const workspaceRoot = input.workspaceRoot ?? await findWorkspaceRoot(input.componentAbs);
27556
27603
  const componentLib = join9(input.componentAbs, SRC_LIB_SUBPATH);
@@ -27603,49 +27650,42 @@ async function resolveUpstreamRoots(input) {
27603
27650
  return out;
27604
27651
  }
27605
27652
  async function readComponentDereekbDeps(componentAbs) {
27606
- const pkgPath = join9(componentAbs, COMPONENT_PACKAGE_JSON);
27607
- let raw;
27608
- try {
27609
- raw = await readFile9(pkgPath, "utf8");
27610
- } catch {
27611
- raw = void 0;
27612
- }
27613
- let json;
27614
- if (raw !== void 0) {
27615
- try {
27616
- json = JSON.parse(raw);
27617
- } catch {
27618
- json = void 0;
27619
- }
27620
- }
27653
+ const json = await readJsonFileSafe(join9(componentAbs, COMPONENT_PACKAGE_JSON));
27621
27654
  const out = /* @__PURE__ */ new Set();
27622
27655
  if (isRecord(json)) {
27623
- for (const field of ["dependencies", "devDependencies", "peerDependencies"]) {
27624
- const map = json[field];
27625
- if (!isRecord(map)) continue;
27626
- for (const name of Object.keys(map)) {
27627
- if (name.startsWith(DBX_COMPONENTS_BASE_SCOPE)) out.add(name);
27628
- }
27656
+ for (const field of DEPENDENCY_FIELDS) {
27657
+ addScopedDependencyNames(json[field], out);
27629
27658
  }
27630
27659
  }
27631
27660
  return [...out];
27632
27661
  }
27633
- async function readTsconfigPaths(workspaceRoot) {
27634
- const tsconfigPath = join9(workspaceRoot, TSCONFIG_BASE);
27662
+ function addScopedDependencyNames(field, out) {
27663
+ if (isRecord(field)) {
27664
+ for (const name of Object.keys(field)) {
27665
+ if (name.startsWith(DBX_COMPONENTS_BASE_SCOPE)) out.add(name);
27666
+ }
27667
+ }
27668
+ }
27669
+ async function readJsonFileSafe(path, preprocess) {
27635
27670
  let raw;
27636
27671
  try {
27637
- raw = await readFile9(tsconfigPath, "utf8");
27672
+ raw = await readFile9(path, "utf8");
27638
27673
  } catch {
27639
27674
  raw = void 0;
27640
27675
  }
27641
27676
  let json;
27642
27677
  if (raw !== void 0) {
27643
27678
  try {
27644
- json = JSON.parse(stripJsonComments(raw));
27679
+ json = JSON.parse(preprocess ? preprocess(raw) : raw);
27645
27680
  } catch {
27646
27681
  json = void 0;
27647
27682
  }
27648
27683
  }
27684
+ return json;
27685
+ }
27686
+ async function readTsconfigPaths(workspaceRoot) {
27687
+ const tsconfigPath = join9(workspaceRoot, TSCONFIG_BASE);
27688
+ const json = await readJsonFileSafe(tsconfigPath, stripJsonComments);
27649
27689
  let result;
27650
27690
  if (isRecord(json) && isRecord(json.compilerOptions) && isRecord(json.compilerOptions.paths)) {
27651
27691
  const entries = /* @__PURE__ */ new Map();
@@ -31937,7 +31977,7 @@ async function discoverSpecFilesByGroup(config) {
31937
31977
  let totalSpecFiles = 0;
31938
31978
  let totalDriftFiles = 0;
31939
31979
  if (exists) {
31940
- const entries = await readDirSafe2(functionDirAbs);
31980
+ const entries = await readDirSafe(functionDirAbs);
31941
31981
  const groupFolders = entries.filter((entry) => entry.isDirectory()).map((entry) => entry.name).sort();
31942
31982
  for (const folderName of groupFolders) {
31943
31983
  const groupAbs = join13(functionDirAbs, folderName);
@@ -31965,7 +32005,7 @@ async function collectSpecFiles(config) {
31965
32005
  const stack = [{ dirAbs: folderAbs, relativeDirName: parentFolderName }];
31966
32006
  while (stack.length > 0) {
31967
32007
  const current = stack.pop();
31968
- const entries = await readDirSafe2(current.dirAbs);
32008
+ const entries = await readDirSafe(current.dirAbs);
31969
32009
  for (const entry of entries) {
31970
32010
  const fullAbs = join13(current.dirAbs, entry.name);
31971
32011
  if (entry.isDirectory()) {
@@ -31982,7 +32022,7 @@ async function collectSpecFiles(config) {
31982
32022
  collected.sort((a, b) => a.fileRel.localeCompare(b.fileRel));
31983
32023
  return collected;
31984
32024
  }
31985
- async function readDirSafe2(path) {
32025
+ async function readDirSafe(path) {
31986
32026
  let result;
31987
32027
  try {
31988
32028
  result = await readdir8(path, { withFileTypes: true });
@@ -33045,7 +33085,7 @@ function resolveGroup(config) {
33045
33085
  const existing = new Set(catalog.groups.map((group) => group.group.toLowerCase()));
33046
33086
  const candidates = [model, ...parentModels].map((name) => name.toLowerCase());
33047
33087
  const matched = candidates.find((candidate) => existing.has(candidate));
33048
- return matched ?? candidates[candidates.length - 1] ?? model.toLowerCase();
33088
+ return matched ?? candidates.at(-1) ?? model.toLowerCase();
33049
33089
  }
33050
33090
  function bucketForKind(kind) {
33051
33091
  let result = "other";
@@ -33532,8 +33572,7 @@ function formatHotspotsAsJson(result) {
33532
33572
  }
33533
33573
  function formatHotspotsAsMarkdown(result) {
33534
33574
  const lines = [];
33535
- lines.push(`# Model-test hotspots \u2014 \`${result.model}\` (${result.apiRel})`, "");
33536
- lines.push(fixtureLine(result));
33575
+ lines.push(`# Model-test hotspots \u2014 \`${result.model}\` (${result.apiRel})`, "", fixtureLine(result));
33537
33576
  if (result.hotspots.length > 0) {
33538
33577
  appendHotspotSections(lines, result);
33539
33578
  } else {
@@ -34313,13 +34352,13 @@ __export(model_api_list_app_exports, {
34313
34352
  });
34314
34353
 
34315
34354
  // packages/dbx-cli/validate/src/lib/model-api-list-app/extract.ts
34316
- import { readdir as readdir10, readFile as readFile17, stat as stat14 } from "node:fs/promises";
34355
+ import { readFile as readFile17 } from "node:fs/promises";
34317
34356
  import { join as join17, relative as relative14, sep as sep12 } from "node:path";
34318
34357
  var API_SUFFIX2 = ".api.ts";
34319
34358
  var MODEL_SUBPATH3 = "src/lib";
34320
34359
  async function extractApiList(input) {
34321
34360
  const modelRoot = join17(input.componentAbs, MODEL_SUBPATH3);
34322
- const apiFiles = await collectApiFiles2(modelRoot);
34361
+ const apiFiles = await collectFilesWithSuffix(modelRoot, API_SUFFIX2);
34323
34362
  const entries = [];
34324
34363
  const files = [];
34325
34364
  for (const filePath of apiFiles) {
@@ -34350,45 +34389,6 @@ async function extractApiList(input) {
34350
34389
  }
34351
34390
  return { modelRoot, entries, files };
34352
34391
  }
34353
- async function readDirSafe3(path) {
34354
- let result;
34355
- try {
34356
- result = await readdir10(path, { withFileTypes: true });
34357
- } catch {
34358
- result = [];
34359
- }
34360
- return result;
34361
- }
34362
- async function collectApiFiles2(root) {
34363
- const out = [];
34364
- const stack = [];
34365
- let isDir = false;
34366
- try {
34367
- const stats = await stat14(root);
34368
- isDir = stats.isDirectory();
34369
- } catch {
34370
- isDir = false;
34371
- }
34372
- if (isDir) {
34373
- stack.push(root);
34374
- while (stack.length > 0) {
34375
- const current = stack.pop();
34376
- const entries = await readDirSafe3(current);
34377
- for (const entry of entries) {
34378
- const full = join17(current, entry.name);
34379
- if (entry.isDirectory()) {
34380
- stack.push(full);
34381
- continue;
34382
- }
34383
- if (entry.isFile() && entry.name.endsWith(API_SUFFIX2) && !entry.name.endsWith(".spec.ts")) {
34384
- out.push(full);
34385
- }
34386
- }
34387
- }
34388
- out.sort((a, b) => a.localeCompare(b));
34389
- }
34390
- return out;
34391
- }
34392
34392
  function countVerbs(entries) {
34393
34393
  const counts = { create: 0, read: 0, update: 0, delete: 0, query: 0, invoke: 0, standalone: 0 };
34394
34394
  for (const entry of entries) {
@@ -34497,18 +34497,18 @@ __export(model_api_lookup_exports, {
34497
34497
  });
34498
34498
 
34499
34499
  // packages/dbx-cli/validate/src/lib/model-api-lookup/extract.ts
34500
- import { readdir as readdir12, readFile as readFile19, stat as stat16 } from "node:fs/promises";
34500
+ import { readFile as readFile19 } from "node:fs/promises";
34501
34501
  import { join as join19, relative as relative16, sep as sep14 } from "node:path";
34502
34502
 
34503
34503
  // packages/dbx-cli/validate/src/lib/model-api-lookup/extract-actions.ts
34504
- import { readdir as readdir11, readFile as readFile18, stat as stat15 } from "node:fs/promises";
34504
+ import { readFile as readFile18 } from "node:fs/promises";
34505
34505
  import { join as join18, relative as relative15, sep as sep13 } from "node:path";
34506
34506
  import { Project as Project14, SyntaxKind as SyntaxKind14 } from "ts-morph";
34507
34507
  var ACTION_SERVER_SUFFIX = ".action.server.ts";
34508
34508
  var COMMON_MODEL_SUBPATH = "src/app/common/model";
34509
34509
  async function buildActionLookup(apiAbs) {
34510
34510
  const modelRoot = join18(apiAbs, COMMON_MODEL_SUBPATH);
34511
- const files = await collectActionFiles(modelRoot);
34511
+ const files = await collectFilesWithSuffix(modelRoot, ACTION_SERVER_SUFFIX);
34512
34512
  const methodsByParams = /* @__PURE__ */ new Map();
34513
34513
  const factoriesByParams = /* @__PURE__ */ new Map();
34514
34514
  if (files.length === 0) {
@@ -34524,45 +34524,6 @@ async function buildActionLookup(apiAbs) {
34524
34524
  }
34525
34525
  return { methodsByParams, factoriesByParams, filesScanned: files.length };
34526
34526
  }
34527
- async function readDirSafe4(path) {
34528
- let result;
34529
- try {
34530
- result = await readdir11(path, { withFileTypes: true });
34531
- } catch {
34532
- result = [];
34533
- }
34534
- return result;
34535
- }
34536
- async function collectActionFiles(root) {
34537
- const out = [];
34538
- const stack = [];
34539
- let isDir = false;
34540
- try {
34541
- const stats = await stat15(root);
34542
- isDir = stats.isDirectory();
34543
- } catch {
34544
- isDir = false;
34545
- }
34546
- if (isDir) {
34547
- stack.push(root);
34548
- while (stack.length > 0) {
34549
- const current = stack.pop();
34550
- const entries = await readDirSafe4(current);
34551
- for (const entry of entries) {
34552
- const full = join18(current, entry.name);
34553
- if (entry.isDirectory()) {
34554
- stack.push(full);
34555
- continue;
34556
- }
34557
- if (entry.isFile() && entry.name.endsWith(ACTION_SERVER_SUFFIX) && !entry.name.endsWith(".spec.ts")) {
34558
- out.push(full);
34559
- }
34560
- }
34561
- }
34562
- out.sort((a, b) => a.localeCompare(b));
34563
- }
34564
- return out;
34565
- }
34566
34527
  function collectAbstractMethods(input) {
34567
34528
  const { sourceFile, sourceFileRel, methodsByParams } = input;
34568
34529
  for (const cls of sourceFile.getClasses()) {
@@ -34640,10 +34601,10 @@ function stripParamsTypeSuffix(validatorName) {
34640
34601
  return stem.charAt(0).toUpperCase() + stem.slice(1);
34641
34602
  }
34642
34603
  function readJsDoc(jsDocs) {
34643
- if (jsDocs.length === 0) {
34604
+ const last = jsDocs.at(-1);
34605
+ if (!last) {
34644
34606
  return void 0;
34645
34607
  }
34646
- const last = jsDocs[jsDocs.length - 1];
34647
34608
  const description = last.getDescription().trim();
34648
34609
  return description.length > 0 ? description : void 0;
34649
34610
  }
@@ -34718,8 +34679,7 @@ function mapDocFields(fields) {
34718
34679
  }
34719
34680
  async function collectApiSources(componentAbs) {
34720
34681
  const root = join19(componentAbs, COMPONENT_LIB_SUBPATH2);
34721
- const files = await listApiFiles(root);
34722
- files.sort((a, b) => a.localeCompare(b));
34682
+ const files = await collectFilesWithSuffix(root, API_SUFFIX3);
34723
34683
  const sources = [];
34724
34684
  for (const fileAbs of files) {
34725
34685
  const text = await readFile19(fileAbs, "utf8");
@@ -34729,43 +34689,6 @@ async function collectApiSources(componentAbs) {
34729
34689
  }
34730
34690
  return sources;
34731
34691
  }
34732
- async function listApiFiles(root) {
34733
- const files = [];
34734
- let isDir = false;
34735
- try {
34736
- const stats = await stat16(root);
34737
- isDir = stats.isDirectory();
34738
- } catch {
34739
- isDir = false;
34740
- }
34741
- if (isDir) {
34742
- const stack = [root];
34743
- while (stack.length > 0) {
34744
- const current = stack.pop();
34745
- const entries = await readDirEntries(current);
34746
- for (const entry of entries) {
34747
- const full = join19(current, entry.name);
34748
- if (entry.isDirectory()) {
34749
- stack.push(full);
34750
- continue;
34751
- }
34752
- if (entry.isFile() && entry.name.endsWith(API_SUFFIX3) && !entry.name.endsWith(".spec.ts")) {
34753
- files.push(full);
34754
- }
34755
- }
34756
- }
34757
- }
34758
- return files;
34759
- }
34760
- async function readDirEntries(dir) {
34761
- let result;
34762
- try {
34763
- result = await readdir12(dir, { withFileTypes: true });
34764
- } catch {
34765
- result = [];
34766
- }
34767
- return result;
34768
- }
34769
34692
  function pickMatchingSource(sources, modelFilter) {
34770
34693
  const wanted = normalize4(modelFilter);
34771
34694
  for (const source of sources) {
@@ -34909,7 +34832,7 @@ __export(server_actions_list_app_exports, {
34909
34832
  });
34910
34833
 
34911
34834
  // packages/dbx-cli/validate/src/lib/server-actions-list-app/extract.ts
34912
- import { readdir as readdir13, readFile as readFile20, stat as stat17 } from "node:fs/promises";
34835
+ import { readdir as readdir10, readFile as readFile20 } from "node:fs/promises";
34913
34836
  import { dirname as dirname15, join as join20, relative as relative17, sep as sep15 } from "node:path";
34914
34837
  import { Node as Node32, Project as Project15 } from "ts-morph";
34915
34838
  var ACTION_SERVER_SUFFIX2 = ".action.server.ts";
@@ -34917,7 +34840,7 @@ var COMMON_MODEL_SUBPATH2 = "src/app/common/model";
34917
34840
  var COMMON_BARREL_REL = "src/app/common/index.ts";
34918
34841
  async function extractServerActions(apiAbs, apiRel) {
34919
34842
  const modelRoot = join20(apiAbs, COMMON_MODEL_SUBPATH2);
34920
- const actionFiles = await collectActionFiles2(modelRoot);
34843
+ const actionFiles = await collectFilesWithSuffix(modelRoot, ACTION_SERVER_SUFFIX2);
34921
34844
  if (actionFiles.length === 0) {
34922
34845
  return { modelRoot, entries: [], fixtureStatus: "ok" };
34923
34846
  }
@@ -34957,45 +34880,6 @@ async function extractServerActions(apiAbs, apiRel) {
34957
34880
  entries.sort((a, b) => a.className.localeCompare(b.className));
34958
34881
  return { modelRoot, entries, fixtureStatus };
34959
34882
  }
34960
- async function readDirSafe5(path) {
34961
- let result;
34962
- try {
34963
- result = await readdir13(path, { withFileTypes: true });
34964
- } catch {
34965
- result = [];
34966
- }
34967
- return result;
34968
- }
34969
- async function collectActionFiles2(root) {
34970
- const out = [];
34971
- const stack = [];
34972
- let isDir = false;
34973
- try {
34974
- const stats = await stat17(root);
34975
- isDir = stats.isDirectory();
34976
- } catch {
34977
- isDir = false;
34978
- }
34979
- if (isDir) {
34980
- stack.push(root);
34981
- while (stack.length > 0) {
34982
- const current = stack.pop();
34983
- const entries = await readDirSafe5(current);
34984
- for (const entry of entries) {
34985
- const full = join20(current, entry.name);
34986
- if (entry.isDirectory()) {
34987
- stack.push(full);
34988
- continue;
34989
- }
34990
- if (entry.isFile() && entry.name.endsWith(ACTION_SERVER_SUFFIX2) && !entry.name.endsWith(".spec.ts")) {
34991
- out.push(full);
34992
- }
34993
- }
34994
- }
34995
- out.sort((a, b) => a.localeCompare(b));
34996
- }
34997
- return out;
34998
- }
34999
34883
  function findServerActionClasses(sourceFile) {
35000
34884
  const out = [];
35001
34885
  for (const cls of sourceFile.getClasses()) {
@@ -35027,7 +34911,7 @@ async function findModuleCandidates(actionFilePath) {
35027
34911
  const dir = dirname15(actionFilePath);
35028
34912
  const out = [];
35029
34913
  try {
35030
- const entries = await readdir13(dir, { withFileTypes: true });
34914
+ const entries = await readdir10(dir, { withFileTypes: true });
35031
34915
  for (const entry of entries) {
35032
34916
  if (!entry.isFile()) continue;
35033
34917
  if (!entry.name.endsWith(".module.ts")) continue;
@@ -35877,14 +35761,14 @@ async function collectCandidatePaths(apiAbs) {
35877
35761
  return [...explicit, ...scanned];
35878
35762
  }
35879
35763
  async function scanProviderFiles(srcAbs) {
35880
- const { readdir: readdir16 } = await import("node:fs/promises");
35764
+ const { readdir: readdir13 } = await import("node:fs/promises");
35881
35765
  const out = [];
35882
35766
  const stack = [srcAbs];
35883
35767
  while (stack.length > 0) {
35884
35768
  const current = stack.pop();
35885
35769
  let entries;
35886
35770
  try {
35887
- entries = await readdir16(current, { withFileTypes: true });
35771
+ entries = await readdir13(current, { withFileTypes: true });
35888
35772
  } catch {
35889
35773
  continue;
35890
35774
  }
@@ -36277,7 +36161,7 @@ function isInColorPosition(obj) {
36277
36161
  return false;
36278
36162
  }
36279
36163
  function collapseSnippet(text) {
36280
- return text.replace(/\s+/g, " ").trim();
36164
+ return text.replaceAll(/\s+/g, " ").trim();
36281
36165
  }
36282
36166
  var HTML_ATTR_RE = /\[(dbxColor|dbxTextColor|color)\]="([^"]+)"/g;
36283
36167
  function extractHtmlLiterals(filePath, text) {
@@ -36301,7 +36185,7 @@ function parseHtmlObjectValue(input) {
36301
36185
  if (parsed.hasTemplate) return void 0;
36302
36186
  if (!parsed.hasKnownField) return void 0;
36303
36187
  const position = positionAt(text, matchIndex);
36304
- const snippet = `[dbxColor]="${value}"`.replace(/\s+/g, " ").trim();
36188
+ const snippet = `[dbxColor]="${value}"`.replaceAll(/\s+/g, " ").trim();
36305
36189
  const literal = {
36306
36190
  file: filePath,
36307
36191
  line: position.line,
@@ -36517,7 +36401,7 @@ function proposeTemplateKey(normalized) {
36517
36401
  if (normalized.color !== void 0) {
36518
36402
  const hex = /^#([0-9a-f]{6})$/i.exec(normalized.color);
36519
36403
  if (hex === null) {
36520
- const sanitized = normalized.color.toLowerCase().replace(/[^a-z0-9]+/g, "-").replace(/^-+|-+$/g, "");
36404
+ const sanitized = normalized.color.toLowerCase().replaceAll(/[^a-z0-9]+/g, "-").replaceAll(/^-+|-+$/g, "");
36521
36405
  if (sanitized.length > 0) result = `brand-${sanitized}`.slice(0, 40);
36522
36406
  } else {
36523
36407
  result = `brand-${hex[1].toLowerCase()}`;
@@ -39667,9 +39551,7 @@ async function run5(rawArgs) {
39667
39551
  const cwd = process.cwd();
39668
39552
  const ensureError = runEnsurePathInsideCwd(parsed.apiDir, cwd);
39669
39553
  let result;
39670
- if (ensureError !== void 0) {
39671
- result = toolError(ensureError);
39672
- } else {
39554
+ if (ensureError === void 0) {
39673
39555
  const apiAbs = resolve39(cwd, parsed.apiDir);
39674
39556
  const inspection = await inspectFixturesSafe(apiAbs, parsed.apiDir);
39675
39557
  if (inspection.kind === "error") {
@@ -39682,6 +39564,8 @@ async function run5(rawArgs) {
39682
39564
  format: parsed.format
39683
39565
  });
39684
39566
  }
39567
+ } else {
39568
+ result = toolError(ensureError);
39685
39569
  }
39686
39570
  return result;
39687
39571
  }
@@ -43790,7 +43674,7 @@ var ACTION_SCAFFOLD_TOOL = {
43790
43674
  import { type as type80 } from "arktype";
43791
43675
 
43792
43676
  // packages/dbx-components-mcp/src/tools/route/load-sources.ts
43793
- import { glob as fsGlob6, readFile as readFile23, stat as stat18 } from "node:fs/promises";
43677
+ import { glob as fsGlob6, readFile as readFile23, stat as stat14 } from "node:fs/promises";
43794
43678
  import { dirname as dirname16, isAbsolute as isAbsolute15, resolve as resolve51, sep as sep17 } from "node:path";
43795
43679
 
43796
43680
  // packages/dbx-components-mcp/src/tools/route/extract.ts
@@ -44244,7 +44128,7 @@ async function resolveRelativeImport(fromFile, specifier, cwd) {
44244
44128
  async function fileExists2(path) {
44245
44129
  let result;
44246
44130
  try {
44247
- const info = await stat18(path);
44131
+ const info = await stat14(path);
44248
44132
  result = info.isFile();
44249
44133
  } catch {
44250
44134
  result = false;
@@ -45069,7 +44953,7 @@ var ROUTE_SEARCH_TOOL = {
45069
44953
  import { type as type83 } from "arktype";
45070
44954
 
45071
44955
  // packages/dbx-components-mcp/src/tools/route/resolve-url.ts
45072
- import { readFile as readFile25, stat as stat19 } from "node:fs/promises";
44956
+ import { readFile as readFile25, stat as stat15 } from "node:fs/promises";
45073
44957
  import { dirname as dirname18, isAbsolute as isAbsolute16, resolve as resolve53, sep as sep19 } from "node:path";
45074
44958
 
45075
44959
  // packages/dbx-components-mcp/src/tools/route/app-port-map.ts
@@ -45547,7 +45431,7 @@ function containsImportedSymbol(importStatement, symbol) {
45547
45431
  async function fileExists3(path) {
45548
45432
  let result;
45549
45433
  try {
45550
- const info = await stat19(path);
45434
+ const info = await stat15(path);
45551
45435
  result = info.isFile();
45552
45436
  } catch {
45553
45437
  result = false;
@@ -50154,7 +50038,7 @@ var EXPLAIN_RULE_TOOL = { definition: TOOL16, run: run17 };
50154
50038
 
50155
50039
  // packages/dbx-components-mcp/src/tools/app-validate.tool.ts
50156
50040
  import { resolve as resolve58, join as join24 } from "node:path";
50157
- import { readdir as readdir14, readFile as readFile28, stat as stat20 } from "node:fs/promises";
50041
+ import { readdir as readdir11, readFile as readFile28, stat as stat16 } from "node:fs/promises";
50158
50042
  import { type as type92 } from "arktype";
50159
50043
  var { inspectAppStorageFiles: inspectAppStorageFiles4, validateAppStorageFiles: validateAppStorageFiles3 } = storagefile_m_validate_app_exports;
50160
50044
  var { inspectAppNotifications: inspectAppNotifications4, validateAppNotifications: validateAppNotifications3 } = notification_m_validate_app_exports;
@@ -50344,7 +50228,7 @@ async function runModelFolders(ctx) {
50344
50228
  const reservedNames = new Set(RESERVED_MODEL_FOLDERS.map((r) => r.name));
50345
50229
  let entries;
50346
50230
  try {
50347
- entries = await readdir14(modelRoot, { withFileTypes: true });
50231
+ entries = await readdir11(modelRoot, { withFileTypes: true });
50348
50232
  } catch (err) {
50349
50233
  const code17 = err.code;
50350
50234
  if (code17 === "ENOENT" || code17 === "ENOTDIR") {
@@ -50397,7 +50281,7 @@ async function runSystemFolder(ctx) {
50397
50281
  try {
50398
50282
  let exists = true;
50399
50283
  try {
50400
- const stats = await stat20(systemPath);
50284
+ const stats = await stat16(systemPath);
50401
50285
  if (!stats.isDirectory()) exists = false;
50402
50286
  } catch (err) {
50403
50287
  const code17 = err.code;
@@ -50731,7 +50615,7 @@ import { resolve as resolve63 } from "node:path";
50731
50615
 
50732
50616
  // packages/dbx-components-mcp/src/tools/mcp-config/snapshot.ts
50733
50617
  import { dirname as dirname19, isAbsolute as isAbsolute17, relative as relative19, resolve as resolve61 } from "node:path";
50734
- import { stat as stat21 } from "node:fs/promises";
50618
+ import { stat as stat17 } from "node:fs/promises";
50735
50619
  async function buildSnapshot(input) {
50736
50620
  const { workspaceRoot, explicitDirs } = input;
50737
50621
  const configResult = await findAndLoadConfig({ cwd: workspaceRoot });
@@ -50781,7 +50665,7 @@ function clustersRegisteredInPackage(pkg, sources) {
50781
50665
  async function fileExists5(path) {
50782
50666
  let result;
50783
50667
  try {
50784
- const stats = await stat21(path);
50668
+ const stats = await stat17(path);
50785
50669
  result = stats.isFile();
50786
50670
  } catch {
50787
50671
  result = false;
@@ -51344,7 +51228,7 @@ import { type as type96 } from "arktype";
51344
51228
  import { basename as basename10 } from "node:path";
51345
51229
 
51346
51230
  // packages/dbx-components-mcp/src/tools/log-search/discover-logs.ts
51347
- import { readdir as readdir15, stat as stat22 } from "node:fs/promises";
51231
+ import { readdir as readdir12, stat as stat18 } from "node:fs/promises";
51348
51232
  import { basename as basename9, join as join25, resolve as resolve64 } from "node:path";
51349
51233
  var DBX_LOG_PATH_ENV_VAR = "DBX_LOG_PATH";
51350
51234
  function resolveLogBasePath(input) {
@@ -51397,7 +51281,7 @@ async function discoverLogs(input) {
51397
51281
  async function pathIsDir(absPath) {
51398
51282
  let isDir = false;
51399
51283
  try {
51400
- const info = await stat22(absPath);
51284
+ const info = await stat18(absPath);
51401
51285
  isDir = info.isDirectory();
51402
51286
  } catch {
51403
51287
  isDir = false;
@@ -51407,7 +51291,7 @@ async function pathIsDir(absPath) {
51407
51291
  async function listChildDirs(absPath) {
51408
51292
  let dirs = [];
51409
51293
  try {
51410
- const entries = await readdir15(absPath, { withFileTypes: true });
51294
+ const entries = await readdir12(absPath, { withFileTypes: true });
51411
51295
  dirs = entries.filter((e) => e.isDirectory()).map((e) => e.name).sort();
51412
51296
  } catch {
51413
51297
  dirs = [];
@@ -51438,7 +51322,7 @@ async function collectProjectLogs(input) {
51438
51322
  async function readMarkdownEntries(projectDir) {
51439
51323
  let entries = [];
51440
51324
  try {
51441
- const dirents = await readdir15(projectDir, { withFileTypes: true });
51325
+ const dirents = await readdir12(projectDir, { withFileTypes: true });
51442
51326
  entries = dirents.filter((d) => (d.isFile() || d.isSymbolicLink()) && d.name.endsWith(".md")).map((d) => d.name);
51443
51327
  } catch {
51444
51328
  entries = [];
@@ -51449,7 +51333,7 @@ async function buildLogRefIfRecent(input) {
51449
51333
  const absolutePath = join25(input.projectDir, input.fileName);
51450
51334
  let info;
51451
51335
  try {
51452
- info = await stat22(absolutePath);
51336
+ info = await stat18(absolutePath);
51453
51337
  } catch {
51454
51338
  info = void 0;
51455
51339
  }
@@ -51512,7 +51396,7 @@ function formatKeywordResults(header, hits) {
51512
51396
  return lines.join("\n").trimEnd();
51513
51397
  }
51514
51398
  function buildHeading(header) {
51515
- const queryPart = header.query !== void 0 ? ` \u2014 \`${header.query}\`` : "";
51399
+ const queryPart = header.query === void 0 ? "" : ` \u2014 \`${header.query}\``;
51516
51400
  return `# Log search${queryPart} (mode: ${header.mode}, scope: ${header.scope}, window: ${header.windowLabel})`;
51517
51401
  }
51518
51402
  function renderEntry(entry, matchedSuffix, maxSnippetLen) {
@@ -51771,7 +51655,7 @@ function parseArgs16(input) {
51771
51655
  }
51772
51656
  const rawQuery = parsed.query?.trim();
51773
51657
  const query = rawQuery !== void 0 && rawQuery.length > 0 ? rawQuery : void 0;
51774
- const mode = parsed.mode ?? (query !== void 0 ? "fuzzy" : "list");
51658
+ const mode = parsed.mode ?? (query === void 0 ? "list" : "fuzzy");
51775
51659
  const limit = clamp(parsed.limit ?? DEFAULT_LIMIT8, 1, MAX_LIMIT7);
51776
51660
  const days = clamp(parsed.days ?? DEFAULT_DAYS, 1, MAX_DAYS);
51777
51661
  const cwd = parsed.cwd ?? process.cwd();