@dereekb/dbx-components-mcp 13.11.1 → 13.11.2

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (2) hide show
  1. package/dbx-components-mcp.js +194 -122
  2. package/package.json +11 -10
@@ -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.1",
8
+ version: "13.11.2",
9
9
  type: "module",
10
10
  bin: {
11
11
  "dbx-components-mcp": "dbx-components-mcp.js"
@@ -20,19 +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-core": "13.11.1",
24
- "@dereekb/dbx-firebase": "13.11.1",
25
- "@dereekb/dbx-form": "13.11.1",
26
- "@dereekb/dbx-web": "13.11.1",
27
- "@dereekb/firebase": "13.11.1",
28
- "@dereekb/firebase-server": "13.11.1",
29
- "@dereekb/rxjs": "13.11.1",
30
- "@dereekb/util": "13.11.1",
23
+ "@dereekb/dbx-cli": "13.11.2",
24
+ "@dereekb/dbx-core": "13.11.2",
25
+ "@dereekb/dbx-firebase": "13.11.2",
26
+ "@dereekb/dbx-form": "13.11.2",
27
+ "@dereekb/dbx-web": "13.11.2",
28
+ "@dereekb/firebase": "13.11.2",
29
+ "@dereekb/firebase-server": "13.11.2",
30
+ "@dereekb/rxjs": "13.11.2",
31
+ "@dereekb/util": "13.11.2",
31
32
  "@uirouter/angular": "21.0.0",
32
33
  rxjs: "^7.8.2"
33
34
  },
34
35
  devDependencies: {
35
- "@dereekb/model": "13.11.1"
36
+ "@dereekb/model": "13.11.2"
36
37
  }
37
38
  };
38
39
 
@@ -6954,12 +6955,17 @@ var DASH_CHARACTER_PREFIX_INSTANCE = characterPrefixSuffixInstance({
6954
6955
  prefix: "-"
6955
6956
  });
6956
6957
 
6957
- // packages/firebase/src/lib/model/storagefile/storagefile.upload.claims.ts
6958
- var STORAGE_FILE_UPLOAD_USER_ROLE = "uploads";
6958
+ // packages/firebase/src/lib/common/auth/oidc/oidc.error.ts
6959
+ var CALL_MODEL_MISSING_OIDC_SCOPE_ERROR_CODE = "CALL_MODEL_MISSING_OIDC_SCOPE";
6959
6960
 
6960
- // packages/dbx-components-mcp/src/registry/auth-builtin.ts
6961
+ // packages/firebase/src/lib/common/auth/oidc/oidc.ts
6961
6962
  var CALL_MODEL_OIDC_SCOPE_PREFIX = "model.";
6962
- var CALL_MODEL_OIDC_SCOPES = ["model.create", "model.read", "model.update", "model.delete", "model.query"];
6963
+ var CREATE_MODEL_OIDC_SCOPE = `${CALL_MODEL_OIDC_SCOPE_PREFIX}create`;
6964
+ var READ_MODEL_OIDC_SCOPE = `${CALL_MODEL_OIDC_SCOPE_PREFIX}read`;
6965
+ var UPDATE_MODEL_OIDC_SCOPE = `${CALL_MODEL_OIDC_SCOPE_PREFIX}update`;
6966
+ var DELETE_MODEL_OIDC_SCOPE = `${CALL_MODEL_OIDC_SCOPE_PREFIX}delete`;
6967
+ var QUERY_MODEL_OIDC_SCOPE = `${CALL_MODEL_OIDC_SCOPE_PREFIX}query`;
6968
+ var CALL_MODEL_OIDC_SCOPES = [CREATE_MODEL_OIDC_SCOPE, READ_MODEL_OIDC_SCOPE, UPDATE_MODEL_OIDC_SCOPE, DELETE_MODEL_OIDC_SCOPE, QUERY_MODEL_OIDC_SCOPE];
6963
6969
  var CALL_MODEL_OIDC_SCOPE_FOR_CALL_TYPE = {
6964
6970
  create: "model.create",
6965
6971
  read: "model.read",
@@ -6967,10 +6973,15 @@ var CALL_MODEL_OIDC_SCOPE_FOR_CALL_TYPE = {
6967
6973
  delete: "model.delete",
6968
6974
  query: "model.query"
6969
6975
  };
6970
- var CALL_MODEL_MISSING_OIDC_SCOPE_ERROR_CODE = "CALL_MODEL_MISSING_OIDC_SCOPE";
6976
+
6977
+ // packages/firebase/src/lib/model/storagefile/storagefile.upload.claims.ts
6978
+ var STORAGE_FILE_UPLOAD_USER_ROLE = "uploads";
6979
+
6980
+ // packages/dbx-components-mcp/src/registry/auth-builtin.ts
6971
6981
  var UTIL_ROLE_PATH = "packages/util/src/lib/auth/auth.role.ts";
6972
6982
  var STORAGE_CLAIM_PATH = "packages/firebase/src/lib/model/storagefile/storagefile.upload.claims.ts";
6973
- var OIDC_SCOPE_PATH = "packages/firebase-server/oidc/src/lib/scope.ts";
6983
+ var OIDC_SCOPE_PATH = "packages/firebase/src/lib/common/auth/oidc/oidc.ts";
6984
+ var OIDC_SCOPE_PRE_ASSERT_PATH = "packages/firebase-server/oidc/src/lib/scope.ts";
6974
6985
  var DEMO_CLAIMS_PATH = "components/demo-firebase/src/lib/auth/claims.ts";
6975
6986
  var BUILTIN_AUTH_ROLES = [
6976
6987
  {
@@ -7046,15 +7057,15 @@ var BUILTIN_AUTH_SCOPES = CALL_MODEL_OIDC_SCOPES.map((scope) => {
7046
7057
  description: `Required OIDC scope for the \`callModel\` ${callType ?? "CRUD"} verb.`,
7047
7058
  enforcedAt: [
7048
7059
  {
7049
- path: OIDC_SCOPE_PATH,
7050
- line: 76,
7060
+ path: OIDC_SCOPE_PRE_ASSERT_PATH,
7061
+ line: 21,
7051
7062
  description: "`oidcCallModelScopePreAssert` rejects requests that lack this scope."
7052
7063
  }
7053
7064
  ],
7054
7065
  errorCode: CALL_MODEL_MISSING_OIDC_SCOPE_ERROR_CODE,
7055
7066
  source: "system",
7056
7067
  sourcePath: OIDC_SCOPE_PATH,
7057
- sourceLine: 20,
7068
+ sourceLine: 30,
7058
7069
  apps: []
7059
7070
  };
7060
7071
  return result;
@@ -7589,7 +7600,7 @@ function composeRegistry(input) {
7589
7600
  for (const app of extractResult.apps) {
7590
7601
  apps.push(toAuthAppInfo({ extracted: app, cwd, inheritedClaimsByInterface, allBuiltinScopes: builtinScopes }));
7591
7602
  }
7592
- const loadedSources = ["builtin:@dereekb/util", "builtin:@dereekb/firebase", "builtin:@dereekb/firebase-server/oidc", ...scannedRelFiles.map((rel) => `workspace:${rel}`)];
7603
+ const loadedSources = ["builtin:@dereekb/util", "builtin:@dereekb/firebase", ...scannedRelFiles.map((rel) => `workspace:${rel}`)];
7593
7604
  return createAuthRegistryFromEntries({
7594
7605
  roles: builtinRoles,
7595
7606
  claims,
@@ -18482,7 +18493,7 @@ import { type as type37 } from "arktype";
18482
18493
  import { readdir as readdir2, readFile as readFile4, stat as stat4 } from "node:fs/promises";
18483
18494
  import { join as join4, relative as relative4, sep as sep5 } from "node:path";
18484
18495
 
18485
- // packages/dbx-components-mcp/src/tools/model-api-shared/extract-crud.ts
18496
+ // packages/dbx-cli/manifest-extract/src/lib/extract-crud.ts
18486
18497
  import { Node as Node12, Project as Project6 } from "ts-morph";
18487
18498
  var SUPPORTED_VERBS = /* @__PURE__ */ new Set(["create", "read", "update", "delete", "query"]);
18488
18499
  function extractCrudEntries(source) {
@@ -18490,11 +18501,26 @@ function extractCrudEntries(source) {
18490
18501
  const sourceFile = project.createSourceFile(source.name, source.text, { overwrite: true });
18491
18502
  const entries = [];
18492
18503
  const modelKeys = [];
18493
- const crudConfigType = findCrudConfigType2(sourceFile);
18504
+ const crudConfigType = findTypeAliasByEnding(sourceFile, "ModelCrudFunctionsConfig");
18494
18505
  const groupName = inferGroupName2(sourceFile);
18506
+ const functionsClassName = findFunctionsClassName(sourceFile);
18507
+ const typeDocsCache = /* @__PURE__ */ new Map();
18508
+ const resolveTypeDocs = (typeName) => {
18509
+ if (!typeName) {
18510
+ return void 0;
18511
+ }
18512
+ if (typeDocsCache.has(typeName)) {
18513
+ return typeDocsCache.get(typeName);
18514
+ }
18515
+ const docs = readTypeDocs(sourceFile, typeName);
18516
+ if (docs) {
18517
+ typeDocsCache.set(typeName, docs);
18518
+ }
18519
+ return docs;
18520
+ };
18495
18521
  if (crudConfigType) {
18496
- const literal = crudConfigType.typeNode;
18497
- if (Node12.isTypeLiteral(literal)) {
18522
+ const literal = crudConfigType.getTypeNode();
18523
+ if (literal && Node12.isTypeLiteral(literal)) {
18498
18524
  for (const member of literal.getMembers()) {
18499
18525
  if (!Node12.isPropertySignature(member)) {
18500
18526
  continue;
@@ -18522,16 +18548,16 @@ function extractCrudEntries(source) {
18522
18548
  if (!verbValueNode) {
18523
18549
  continue;
18524
18550
  }
18525
- collectVerbEntries({ modelName, verb, valueNode: verbValueNode, entries });
18551
+ collectVerbEntries({ modelName, verb, valueNode: verbValueNode, entries, fallbackDescription: readJsDocSummary(verbMember), resolveTypeDocs });
18526
18552
  }
18527
18553
  }
18528
18554
  }
18529
18555
  }
18530
18556
  }
18531
- const functionTypeMap = findFunctionTypeMap2(sourceFile);
18557
+ const functionTypeMap = findTypeAliasByEnding(sourceFile, "FunctionTypeMap");
18532
18558
  if (functionTypeMap) {
18533
- const literal = functionTypeMap.typeNode;
18534
- if (Node12.isTypeLiteral(literal)) {
18559
+ const literal = functionTypeMap.getTypeNode();
18560
+ if (literal && Node12.isTypeLiteral(literal)) {
18535
18561
  for (const member of literal.getMembers()) {
18536
18562
  if (!Node12.isPropertySignature(member)) {
18537
18563
  continue;
@@ -18539,37 +18565,42 @@ function extractCrudEntries(source) {
18539
18565
  const key = member.getName();
18540
18566
  const valueNode = member.getTypeNode();
18541
18567
  const tuple = valueNode ? readTupleParamsResult(valueNode) : void 0;
18568
+ const paramsDocs = resolveTypeDocs(tuple?.params);
18569
+ const resultDocs = resolveTypeDocs(tuple?.result);
18542
18570
  entries.push({
18543
18571
  model: key,
18544
18572
  verb: "standalone",
18545
18573
  specifier: void 0,
18546
18574
  paramsTypeName: tuple?.params,
18547
18575
  resultTypeName: tuple?.result,
18548
- line: member.getStartLineNumber()
18576
+ line: member.getStartLineNumber(),
18577
+ description: readJsDocSummary(member),
18578
+ paramsTypeDescription: paramsDocs?.typeDescription,
18579
+ paramsFields: paramsDocs?.fields,
18580
+ resultTypeDescription: resultDocs?.typeDescription,
18581
+ resultFields: resultDocs?.fields
18549
18582
  });
18550
18583
  }
18551
18584
  }
18552
18585
  }
18553
- return { groupName, modelKeys, entries };
18586
+ return { groupName, modelKeys, entries, functionsClassName };
18554
18587
  }
18555
- function findCrudConfigType2(sourceFile) {
18588
+ function findTypeAliasByEnding(sourceFile, ending) {
18556
18589
  for (const alias of sourceFile.getTypeAliases()) {
18557
- if (alias.getName().endsWith("ModelCrudFunctionsConfig")) {
18558
- const typeNode = alias.getTypeNode();
18559
- if (typeNode) {
18560
- return { typeNode };
18561
- }
18590
+ if (alias.getName().endsWith(ending) && alias.getTypeNode()) {
18591
+ return alias;
18562
18592
  }
18563
18593
  }
18564
18594
  return void 0;
18565
18595
  }
18566
- function findFunctionTypeMap2(sourceFile) {
18567
- for (const alias of sourceFile.getTypeAliases()) {
18568
- if (alias.getName().endsWith("FunctionTypeMap")) {
18569
- const typeNode = alias.getTypeNode();
18570
- if (typeNode) {
18571
- return { typeNode };
18572
- }
18596
+ function findFunctionsClassName(sourceFile) {
18597
+ for (const cls of sourceFile.getClasses()) {
18598
+ if (!cls.isAbstract()) {
18599
+ continue;
18600
+ }
18601
+ const name = cls.getName();
18602
+ if (name?.endsWith("Functions")) {
18603
+ return name;
18573
18604
  }
18574
18605
  }
18575
18606
  return void 0;
@@ -18605,7 +18636,7 @@ function isNullLiteralType2(node) {
18605
18636
  return false;
18606
18637
  }
18607
18638
  function collectVerbEntries(input) {
18608
- const { modelName, verb, valueNode, entries } = input;
18639
+ const { modelName, verb, valueNode, entries, fallbackDescription, resolveTypeDocs } = input;
18609
18640
  if (Node12.isTypeLiteral(valueNode)) {
18610
18641
  for (const specMember of valueNode.getMembers()) {
18611
18642
  if (!Node12.isPropertySignature(specMember)) {
@@ -18614,25 +18645,39 @@ function collectVerbEntries(input) {
18614
18645
  const specifier = specMember.getName();
18615
18646
  const leafNode = specMember.getTypeNode();
18616
18647
  const leaf2 = leafNode ? readTupleParamsResult(leafNode) ?? readBareParams(leafNode) : void 0;
18648
+ const paramsDocs2 = resolveTypeDocs(leaf2?.params);
18649
+ const resultDocs2 = resolveTypeDocs(leaf2?.result);
18617
18650
  entries.push({
18618
18651
  model: modelName,
18619
18652
  verb,
18620
18653
  specifier,
18621
18654
  paramsTypeName: leaf2?.params,
18622
18655
  resultTypeName: leaf2?.result,
18623
- line: specMember.getStartLineNumber()
18656
+ line: specMember.getStartLineNumber(),
18657
+ description: readJsDocSummary(specMember),
18658
+ paramsTypeDescription: paramsDocs2?.typeDescription,
18659
+ paramsFields: paramsDocs2?.fields,
18660
+ resultTypeDescription: resultDocs2?.typeDescription,
18661
+ resultFields: resultDocs2?.fields
18624
18662
  });
18625
18663
  }
18626
18664
  return;
18627
18665
  }
18628
18666
  const leaf = readTupleParamsResult(valueNode) ?? readBareParams(valueNode);
18667
+ const paramsDocs = resolveTypeDocs(leaf?.params);
18668
+ const resultDocs = resolveTypeDocs(leaf?.result);
18629
18669
  entries.push({
18630
18670
  model: modelName,
18631
18671
  verb,
18632
18672
  specifier: void 0,
18633
18673
  paramsTypeName: leaf?.params,
18634
18674
  resultTypeName: leaf?.result,
18635
- line: valueNode.getStartLineNumber()
18675
+ line: valueNode.getStartLineNumber(),
18676
+ description: fallbackDescription,
18677
+ paramsTypeDescription: paramsDocs?.typeDescription,
18678
+ paramsFields: paramsDocs?.fields,
18679
+ resultTypeDescription: resultDocs?.typeDescription,
18680
+ resultFields: resultDocs?.fields
18636
18681
  });
18637
18682
  }
18638
18683
  function readTupleParamsResult(node) {
@@ -18661,6 +18706,40 @@ function typeNodeName(node) {
18661
18706
  const text = node.getText().trim();
18662
18707
  return text.length > 0 ? text : void 0;
18663
18708
  }
18709
+ function readTypeDocs(sourceFile, typeName) {
18710
+ const interfaceDecl = sourceFile.getInterface(typeName);
18711
+ if (interfaceDecl) {
18712
+ const typeDescription = readJsDocSummary(interfaceDecl);
18713
+ const fields = [];
18714
+ for (const property of interfaceDecl.getProperties()) {
18715
+ const fieldName = property.getName();
18716
+ const description = readJsDocSummary(property);
18717
+ const typeNode = property.getTypeNode();
18718
+ const typeText = typeNode?.getText().trim() ?? "";
18719
+ const field = description ? { name: fieldName, typeText, description } : { name: fieldName, typeText };
18720
+ fields.push(field);
18721
+ }
18722
+ if (!typeDescription && fields.length === 0) {
18723
+ return void 0;
18724
+ }
18725
+ return { typeDescription, fields: fields.length > 0 ? fields : void 0 };
18726
+ }
18727
+ const typeAlias = sourceFile.getTypeAlias(typeName);
18728
+ if (typeAlias) {
18729
+ const typeDescription = readJsDocSummary(typeAlias);
18730
+ return typeDescription ? { typeDescription } : void 0;
18731
+ }
18732
+ return void 0;
18733
+ }
18734
+ function readJsDocSummary(node) {
18735
+ const docs = node.getJsDocs();
18736
+ if (docs.length === 0) {
18737
+ return void 0;
18738
+ }
18739
+ const last = docs[docs.length - 1];
18740
+ const description = last.getDescription().trim();
18741
+ return description.length > 0 ? description : void 0;
18742
+ }
18664
18743
 
18665
18744
  // packages/dbx-components-mcp/src/tools/model-api-list-app/extract.ts
18666
18745
  var API_SUFFIX = ".api.ts";
@@ -18781,6 +18860,12 @@ function formatReportAsMarkdown(report) {
18781
18860
  lines.push(formatEntryRow(entry));
18782
18861
  }
18783
18862
  lines.push("");
18863
+ const described = fileEntries.filter((entry) => entry.description !== void 0 && entry.description.length > 0);
18864
+ if (described.length > 0) {
18865
+ for (const entry of described) {
18866
+ lines.push(formatEntryDescription(entry), "");
18867
+ }
18868
+ }
18784
18869
  }
18785
18870
  return lines.join("\n");
18786
18871
  }
@@ -18804,6 +18889,14 @@ function formatEntryRow(entry) {
18804
18889
  const result = entry.resultTypeName ? `\`${entry.resultTypeName}\`` : "`void`";
18805
18890
  return `| \`${entry.model}\` | \`${entry.verb}\` | ${specifier} | ${params} | ${result} | ${entry.line} |`;
18806
18891
  }
18892
+ function formatEntryDescription(entry) {
18893
+ const heading = entry.specifier !== void 0 ? `${entry.model}.${entry.verb}.${entry.specifier}` : `${entry.model}.${entry.verb}`;
18894
+ const description = entry.description ?? "";
18895
+ const quoted = description.split("\n").map((line) => `> ${line}`).join("\n");
18896
+ return `**${heading}**
18897
+
18898
+ ${quoted}`;
18899
+ }
18807
18900
 
18808
18901
  // packages/dbx-components-mcp/src/tools/model-api-list-app/index.ts
18809
18902
  async function listAppModelApi(componentAbs, options) {
@@ -18838,6 +18931,8 @@ var TOOL = {
18838
18931
  "- `result` \u2014 the result type name when the leaf is a `[Params, Result]` tuple, else `void`.",
18839
18932
  "- `line` \u2014 source line of the leaf in the `.api.ts` file.",
18840
18933
  "",
18934
+ "When the maintainer added a JSDoc summary to a CRUD property (e.g. on `update.username:` inside `<Group>ModelCrudFunctionsConfig` or on a `<Group>FunctionTypeMap` key), the markdown view renders that description as a block-quote under the table. JSON output exposes the same string on `entry.description`.",
18935
+ "",
18841
18936
  "Inputs:",
18842
18937
  "- `componentDir`: relative path to the firebase-component package (e.g. `components/demo-firebase`).",
18843
18938
  "- `model` (optional): bare model name, e.g. `Profile` \u2014 case-insensitive match against group / model keys.",
@@ -18892,7 +18987,6 @@ import { type as type38 } from "arktype";
18892
18987
  // packages/dbx-components-mcp/src/tools/model-api-lookup/extract.ts
18893
18988
  import { readdir as readdir4, readFile as readFile6, stat as stat6 } from "node:fs/promises";
18894
18989
  import { join as join6, relative as relative6, sep as sep7 } from "node:path";
18895
- import { Project as Project8 } from "ts-morph";
18896
18990
 
18897
18991
  // packages/dbx-components-mcp/src/tools/model-api-lookup/extract-actions.ts
18898
18992
  import { readdir as readdir3, readFile as readFile5, stat as stat5 } from "node:fs/promises";
@@ -19055,9 +19149,6 @@ async function extractApiLookup(input) {
19055
19149
  }
19056
19150
  const crudExtraction = extractCrudEntries({ name: matched.fileRel, text: matched.text });
19057
19151
  const filteredEntries = crudExtraction.entries.filter((entry) => entryMatchesFilter(entry.model, crudExtraction.groupName, input.modelFilter));
19058
- const project = new Project8({ useInMemoryFileSystem: true, skipAddingFilesFromTsConfig: true });
19059
- const sourceFile = project.createSourceFile(matched.fileRel, matched.text, { overwrite: true });
19060
- const interfaces = collectInterfacesByName(sourceFile);
19061
19152
  let actionLookup;
19062
19153
  let actionLookupStatus;
19063
19154
  if (input.apiAbs) {
@@ -19070,22 +19161,7 @@ async function extractApiLookup(input) {
19070
19161
  } else {
19071
19162
  actionLookupStatus = { kind: "skipped", reason: "apiDir not provided \u2014 action JSDoc skipped." };
19072
19163
  }
19073
- const enriched = filteredEntries.map((entry) => {
19074
- const paramsInterface = entry.paramsTypeName ? interfaces.get(entry.paramsTypeName) : void 0;
19075
- const resultInterface = entry.resultTypeName ? interfaces.get(entry.resultTypeName) : void 0;
19076
- const action = actionLookup && entry.paramsTypeName ? actionLookup.methodsByParams.get(entry.paramsTypeName) : void 0;
19077
- const factory = actionLookup && entry.paramsTypeName ? actionLookup.factoriesByParams.get(entry.paramsTypeName) : void 0;
19078
- return {
19079
- ...entry,
19080
- sourceFile: matched.fileRel,
19081
- paramsJsDoc: paramsInterface ? readInterfaceJsDoc(paramsInterface) : void 0,
19082
- paramsFields: paramsInterface ? extractInterfaceFields3(paramsInterface) : [],
19083
- resultJsDoc: resultInterface ? readInterfaceJsDoc(resultInterface) : void 0,
19084
- resultFields: resultInterface ? extractInterfaceFields3(resultInterface) : [],
19085
- action,
19086
- factory
19087
- };
19088
- });
19164
+ const enriched = filteredEntries.map((entry) => mapEntry(entry, matched.fileRel, actionLookup));
19089
19165
  return {
19090
19166
  componentDir: input.componentDir,
19091
19167
  apiDir: input.apiDir,
@@ -19097,6 +19173,30 @@ async function extractApiLookup(input) {
19097
19173
  actionLookupStatus
19098
19174
  };
19099
19175
  }
19176
+ function mapEntry(entry, sourceFile, actionLookup) {
19177
+ const action = actionLookup && entry.paramsTypeName ? actionLookup.methodsByParams.get(entry.paramsTypeName) : void 0;
19178
+ const factory = actionLookup && entry.paramsTypeName ? actionLookup.factoriesByParams.get(entry.paramsTypeName) : void 0;
19179
+ return {
19180
+ ...entry,
19181
+ sourceFile,
19182
+ paramsJsDoc: entry.paramsTypeDescription,
19183
+ paramsFields: mapDocFields(entry.paramsFields),
19184
+ resultJsDoc: entry.resultTypeDescription,
19185
+ resultFields: mapDocFields(entry.resultFields),
19186
+ action,
19187
+ factory
19188
+ };
19189
+ }
19190
+ function mapDocFields(fields) {
19191
+ if (!fields) {
19192
+ return [];
19193
+ }
19194
+ return fields.map((field) => ({
19195
+ name: field.name,
19196
+ typeText: field.typeText,
19197
+ jsDoc: field.description
19198
+ }));
19199
+ }
19100
19200
  async function collectApiSources(componentAbs) {
19101
19201
  const root = join6(componentAbs, COMPONENT_LIB_SUBPATH);
19102
19202
  const files = [];
@@ -19164,39 +19264,6 @@ function entryMatchesFilter(model, groupName, filter) {
19164
19264
  function normalize2(value) {
19165
19265
  return value.replace(/Identity$/i, "").toLowerCase();
19166
19266
  }
19167
- function collectInterfacesByName(sourceFile) {
19168
- const out = /* @__PURE__ */ new Map();
19169
- for (const iface of sourceFile.getInterfaces()) {
19170
- out.set(iface.getName(), iface);
19171
- }
19172
- return out;
19173
- }
19174
- function readInterfaceJsDoc(iface) {
19175
- const docs = iface.getJsDocs();
19176
- if (docs.length === 0) return void 0;
19177
- const last = docs[docs.length - 1];
19178
- const description = last.getDescription().trim();
19179
- return description.length > 0 ? description : void 0;
19180
- }
19181
- function extractInterfaceFields3(iface) {
19182
- const fields = [];
19183
- for (const prop of iface.getProperties()) {
19184
- const typeNode = prop.getTypeNode();
19185
- const docs = prop.getJsDocs();
19186
- let jsDoc;
19187
- if (docs.length > 0) {
19188
- const last = docs[docs.length - 1];
19189
- const description = last.getDescription().trim();
19190
- jsDoc = description.length > 0 ? description : void 0;
19191
- }
19192
- fields.push({
19193
- name: prop.getName(),
19194
- typeText: typeNode ? typeNode.getText() : "",
19195
- jsDoc
19196
- });
19197
- }
19198
- return fields;
19199
- }
19200
19267
 
19201
19268
  // packages/dbx-components-mcp/src/tools/model-api-lookup/format.ts
19202
19269
  function formatLookupAsMarkdown(report) {
@@ -19240,7 +19307,11 @@ function formatEntry2(entry) {
19240
19307
  const heading = entry.specifier !== void 0 ? `${entry.model}.${entry.verb}.${entry.specifier}` : `${entry.model}.${entry.verb}`;
19241
19308
  const paramsLabel = entry.paramsTypeName ? `\`${entry.paramsTypeName}\`` : "_unresolved_";
19242
19309
  const resultLabel = entry.resultTypeName ? `\`${entry.resultTypeName}\`` : "`void`";
19243
- const lines = [`## ${heading}`, "", `- Wire key: \`${wireKey}\``, `- Params: ${paramsLabel}`, `- Result: ${resultLabel}`, `- Source: \`${entry.sourceFile}:${entry.line}\``, ""];
19310
+ const lines = [`## ${heading}`, ""];
19311
+ if (entry.description) {
19312
+ lines.push(entry.description, "");
19313
+ }
19314
+ lines.push(`- Wire key: \`${wireKey}\``, `- Params: ${paramsLabel}`, `- Result: ${resultLabel}`, `- Source: \`${entry.sourceFile}:${entry.line}\``, "");
19244
19315
  if (entry.paramsJsDoc || entry.paramsFields.length > 0) {
19245
19316
  lines.push("### Params");
19246
19317
  if (entry.paramsJsDoc) {
@@ -19325,6 +19396,7 @@ var TOOL2 = {
19325
19396
  "Deep lookup for one model declared in a firebase-component package. Returns every CRUD / standalone entry under that model with rich metadata.",
19326
19397
  "",
19327
19398
  "For each entry the tool surfaces:",
19399
+ "- Per-call summary read from the JSDoc on the property in `<Group>ModelCrudFunctionsConfig` / `<Group>FunctionTypeMap`.",
19328
19400
  "- Params + result type names, params/result interface JSDoc, per-field type and doc.",
19329
19401
  "- Action method on the matching `*ServerActions` abstract class (when `apiDir` is provided).",
19330
19402
  "- Action factory function (`<X>Factory`) with its richer JSDoc \u2014 usually the most semantic source.",
@@ -19460,7 +19532,7 @@ async function extractDeclaredEntries(componentAbs) {
19460
19532
  // packages/dbx-components-mcp/src/tools/model-api-validate-app/extract-handlers.ts
19461
19533
  import { readFile as readFile8 } from "node:fs/promises";
19462
19534
  import { join as join8 } from "node:path";
19463
- import { Node as Node13, Project as Project9 } from "ts-morph";
19535
+ import { Node as Node13, Project as Project8 } from "ts-morph";
19464
19536
  var HANDLER_MAP_REL = "src/app/function/model/crud.functions.ts";
19465
19537
  var VERB_TOKENS = [
19466
19538
  { token: "Create", verb: "create" },
@@ -19478,7 +19550,7 @@ async function extractHandlerEntries(apiAbs, apiDir) {
19478
19550
  } catch {
19479
19551
  return { path: fullPath, status: { kind: "missing", path: HANDLER_MAP_REL }, entries: [] };
19480
19552
  }
19481
- const project = new Project9({ useInMemoryFileSystem: true, skipAddingFilesFromTsConfig: true });
19553
+ const project = new Project8({ useInMemoryFileSystem: true, skipAddingFilesFromTsConfig: true });
19482
19554
  const sourceFile = project.createSourceFile(sourceFileRel, text, { overwrite: true });
19483
19555
  const entries = [];
19484
19556
  const verbsFound = [];
@@ -20534,7 +20606,7 @@ function paramsHasFixtureDependencies(params) {
20534
20606
  }
20535
20607
 
20536
20608
  // packages/dbx-components-mcp/src/tools/model-fixture-shared/extract.ts
20537
- import { Node as Node14, Project as Project10, SyntaxKind as SyntaxKind7 } from "ts-morph";
20609
+ import { Node as Node14, Project as Project9, SyntaxKind as SyntaxKind7 } from "ts-morph";
20538
20610
 
20539
20611
  // packages/dbx-components-mcp/src/tools/model-fixture-shared/framework-fixtures.ts
20540
20612
  var KNOWN_NON_MODEL_FIXTURE_FAMILIES = [
@@ -20564,7 +20636,7 @@ var BASE_CONTEXT_FIXTURE_BASE = "FirebaseAdminNestTestContextFixture";
20564
20636
  var MODEL_FACTORY_NAME = "modelTestContextFactory";
20565
20637
  function extractAppFixturesFromText(input) {
20566
20638
  const { text, fixturePath } = input;
20567
- const project = new Project10({ useInMemoryFileSystem: true, skipAddingFilesFromTsConfig: true });
20639
+ const project = new Project9({ useInMemoryFileSystem: true, skipAddingFilesFromTsConfig: true });
20568
20640
  const sourceFile = project.createSourceFile(FIXTURE_FILE_VIRTUAL_PATH, text, { overwrite: true });
20569
20641
  const prefix = detectPrefix(sourceFile);
20570
20642
  const fixtureClasses = /* @__PURE__ */ new Map();
@@ -22062,7 +22134,7 @@ var modelFixtureScaffoldTool = { definition: TOOL7, run: run6 };
22062
22134
  // packages/dbx-components-mcp/src/tools/model-fixture-forward.tool.ts
22063
22135
  import { join as join12, resolve as resolve29 } from "node:path";
22064
22136
  import { type as type45 } from "arktype";
22065
- import { Project as Project11 } from "ts-morph";
22137
+ import { Project as Project10 } from "ts-morph";
22066
22138
  var ForwardArgsType = type45({
22067
22139
  apiDir: "string",
22068
22140
  model: "string",
@@ -22123,7 +22195,7 @@ function toErrorMessage(err) {
22123
22195
  return err instanceof Error ? err.message : String(err);
22124
22196
  }
22125
22197
  async function applyForwardersToFile(absolutePath, fixtureClassName, rendered) {
22126
- const project = new Project11({ skipAddingFilesFromTsConfig: true });
22198
+ const project = new Project10({ skipAddingFilesFromTsConfig: true });
22127
22199
  let sourceFile;
22128
22200
  try {
22129
22201
  sourceFile = project.addSourceFileAtPath(absolutePath);
@@ -22174,7 +22246,7 @@ import { resolve as resolve30 } from "node:path";
22174
22246
  import { type as type46 } from "arktype";
22175
22247
 
22176
22248
  // packages/dbx-components-mcp/src/tools/model-test-shared/extract.ts
22177
- import { Node as Node15, Project as Project12, SyntaxKind as SyntaxKind8 } from "ts-morph";
22249
+ import { Node as Node15, Project as Project11, SyntaxKind as SyntaxKind8 } from "ts-morph";
22178
22250
  var SPEC_FILE_VIRTUAL_PATH = "__spec__/spec.ts";
22179
22251
  var FIXTURE_CONTEXT_SUFFIX = "Context";
22180
22252
  var FIXTURE_FACTORY_SUFFIX = "ContextFactory";
@@ -22187,7 +22259,7 @@ function isItName(name) {
22187
22259
  }
22188
22260
  function extractSpecTreeFromText(input) {
22189
22261
  const { text, specPath } = input;
22190
- const project = new Project12({ useInMemoryFileSystem: true, skipAddingFilesFromTsConfig: true });
22262
+ const project = new Project11({ useInMemoryFileSystem: true, skipAddingFilesFromTsConfig: true });
22191
22263
  const sourceFile = project.createSourceFile(SPEC_FILE_VIRTUAL_PATH, text, { overwrite: true });
22192
22264
  const { prefix, prefixSource, knownFixtureNames } = resolvePrefixAndFixtures(sourceFile, input);
22193
22265
  const helpers = collectHelpers(sourceFile);
@@ -23280,11 +23352,11 @@ var modelHierarchyTool = {
23280
23352
  import { Node as Node17, SyntaxKind as SyntaxKind10 } from "ts-morph";
23281
23353
 
23282
23354
  // packages/dbx-components-mcp/src/tools/_validate/ast.ts
23283
- import { Node as Node16, Project as Project13, SyntaxKind as SyntaxKind9 } from "ts-morph";
23355
+ import { Node as Node16, Project as Project12, SyntaxKind as SyntaxKind9 } from "ts-morph";
23284
23356
  var COMPONENT_VIRTUAL_PREFIX = "__component__/";
23285
23357
  var API_VIRTUAL_PREFIX = "__api__/";
23286
23358
  function buildInMemoryProject(input) {
23287
- const project = new Project13({ useInMemoryFileSystem: true, skipAddingFilesFromTsConfig: true });
23359
+ const project = new Project12({ useInMemoryFileSystem: true, skipAddingFilesFromTsConfig: true });
23288
23360
  const componentSources = [];
23289
23361
  const apiSources = [];
23290
23362
  for (const file of input.component.files) {
@@ -26409,14 +26481,14 @@ async function inspectFolder2(path) {
26409
26481
  }
26410
26482
 
26411
26483
  // packages/dbx-components-mcp/src/tools/system-m-validate-folder/extract.ts
26412
- import { Node as Node19, Project as Project14 } from "ts-morph";
26484
+ import { Node as Node19, Project as Project13 } from "ts-morph";
26413
26485
  var TYPE_CONSTANT_SUFFIX = "_SYSTEM_STATE_TYPE";
26414
26486
  var DATA_INTERFACE_SUFFIX = "SystemData";
26415
26487
  var CONVERTER_CONFIG_TYPE = "SystemStateStoredDataFieldConverterConfig";
26416
26488
  var CONVERTER_MAP_TYPE = "SystemStateStoredDataConverterMap";
26417
26489
  var SYSTEM_STATE_STORED_DATA = "SystemStateStoredData";
26418
26490
  function extractSystemFile(name, text) {
26419
- const project = new Project14({ useInMemoryFileSystem: true, skipAddingFilesFromTsConfig: true });
26491
+ const project = new Project13({ useInMemoryFileSystem: true, skipAddingFilesFromTsConfig: true });
26420
26492
  const sourceFile = project.createSourceFile(name, text, { overwrite: true });
26421
26493
  const typeConstants = findTypeConstants(sourceFile);
26422
26494
  const dataInterfaces = findDataInterfaces2(sourceFile);
@@ -26920,7 +26992,7 @@ var systemMValidateFolderTool = createFolderValidateTool({
26920
26992
  });
26921
26993
 
26922
26994
  // packages/dbx-components-mcp/src/tools/dbx-asset-validate-app/extract.ts
26923
- import { Node as Node20, Project as Project15 } from "ts-morph";
26995
+ import { Node as Node20, Project as Project14 } from "ts-morph";
26924
26996
  var ASSETS_RELPATH = "src/lib/assets.ts";
26925
26997
  var BARREL_RELPATH = "src/lib/index.ts";
26926
26998
  var LOCAL_ASSET_BUILDER = "localAsset";
@@ -26932,7 +27004,7 @@ var KNOWN_FLUENT_BUILDERS = /* @__PURE__ */ new Set([ASSET_FOLDER_BUILDER, REMOT
26932
27004
  var ASSET_PATH_REF_ARRAY_TYPE = "AssetPathRef[]";
26933
27005
  var BARREL_RE_EXPORT_RE = /export\s*\*\s*from\s*['"]\.\/assets['"]/;
26934
27006
  function extractAppAssets(inspection) {
26935
- const project = new Project15({ useInMemoryFileSystem: true, skipAddingFilesFromTsConfig: true });
27007
+ const project = new Project14({ useInMemoryFileSystem: true, skipAddingFilesFromTsConfig: true });
26936
27008
  const sources = [];
26937
27009
  let assetsFileExists = false;
26938
27010
  let barrelText;
@@ -28966,9 +29038,9 @@ import { glob as fsGlob5, readFile as readFile17, stat as stat13 } from "node:fs
28966
29038
  import { dirname as dirname11, isAbsolute as isAbsolute11, resolve as resolve32, sep as sep11 } from "node:path";
28967
29039
 
28968
29040
  // packages/dbx-components-mcp/src/tools/route/extract.ts
28969
- import { Node as Node21, Project as Project16, SyntaxKind as SyntaxKind12 } from "ts-morph";
29041
+ import { Node as Node21, Project as Project15, SyntaxKind as SyntaxKind12 } from "ts-morph";
28970
29042
  function extractFile3(source) {
28971
- const project = new Project16({ useInMemoryFileSystem: true, skipAddingFilesFromTsConfig: true });
29043
+ const project = new Project15({ useInMemoryFileSystem: true, skipAddingFilesFromTsConfig: true });
28972
29044
  const sourceFile = project.createSourceFile(source.name, source.text, { overwrite: true });
28973
29045
  const issues = [];
28974
29046
  const objectLiteralsByName = collectStateConsts(sourceFile);
@@ -32343,7 +32415,7 @@ import { type as type62 } from "arktype";
32343
32415
  // packages/dbx-components-mcp/src/tools/model-list-component/extract.ts
32344
32416
  import { readdir as readdir12, readFile as readFile18, stat as stat15 } from "node:fs/promises";
32345
32417
  import { basename as basename6, join as join18, relative as relative10, sep as sep13 } from "node:path";
32346
- import { Node as Node22, Project as Project17 } from "ts-morph";
32418
+ import { Node as Node22, Project as Project16 } from "ts-morph";
32347
32419
  var MODEL_SUBPATH3 = "src/lib/model";
32348
32420
  var MODEL_SUBPATH_PREFIX = `${MODEL_SUBPATH3}/`;
32349
32421
  var RESERVED_NAMES = RESERVED_MODEL_FOLDERS.map((r) => r.name);
@@ -32372,7 +32444,7 @@ async function extractComponentModels(componentAbs) {
32372
32444
  skipReservedFolders: RESERVED_NAMES
32373
32445
  });
32374
32446
  const richByFolder = groupRichByFolder(richResult.models);
32375
- const fallbackProject = new Project17({ useInMemoryFileSystem: true });
32447
+ const fallbackProject = new Project16({ useInMemoryFileSystem: true });
32376
32448
  const models = [];
32377
32449
  const unidentified = [];
32378
32450
  for (const folder of targetFolders) {
@@ -32672,7 +32744,7 @@ import { type as type63 } from "arktype";
32672
32744
  // packages/dbx-components-mcp/src/tools/server-actions-list-app/extract.ts
32673
32745
  import { readdir as readdir13, readFile as readFile19, stat as stat16 } from "node:fs/promises";
32674
32746
  import { dirname as dirname12, join as join19, relative as relative11, sep as sep14 } from "node:path";
32675
- import { Node as Node23, Project as Project18 } from "ts-morph";
32747
+ import { Node as Node23, Project as Project17 } from "ts-morph";
32676
32748
  var ACTION_SERVER_SUFFIX2 = ".action.server.ts";
32677
32749
  var COMMON_MODEL_SUBPATH2 = "src/app/common/model";
32678
32750
  var COMMON_BARREL_REL = "src/app/common/index.ts";
@@ -32682,7 +32754,7 @@ async function extractServerActions(apiAbs, apiRel) {
32682
32754
  if (actionFiles.length === 0) {
32683
32755
  return { modelRoot, entries: [], fixtureStatus: "ok" };
32684
32756
  }
32685
- const project = new Project18({ useInMemoryFileSystem: true });
32757
+ const project = new Project17({ useInMemoryFileSystem: true });
32686
32758
  const commonBarrelExports = await readCommonBarrelExports(apiAbs);
32687
32759
  const fixtureCoverageMap = /* @__PURE__ */ new Map();
32688
32760
  let fixtureStatus = "ok";
@@ -32801,7 +32873,7 @@ async function safeRead(path) {
32801
32873
  }
32802
32874
  }
32803
32875
  function inspectModuleText(input) {
32804
- const project = new Project18({ useInMemoryFileSystem: true });
32876
+ const project = new Project17({ useInMemoryFileSystem: true });
32805
32877
  const sourceFile = project.createSourceFile("module.ts", input.text, { overwrite: true });
32806
32878
  let providedByModule = false;
32807
32879
  let exportedByModule = false;
@@ -32838,7 +32910,7 @@ async function readCommonBarrelExports(apiAbs) {
32838
32910
  const path = join19(apiAbs, COMMON_BARREL_REL);
32839
32911
  const text = await safeRead(path);
32840
32912
  if (text === void 0) return /* @__PURE__ */ new Set();
32841
- const project = new Project18({ useInMemoryFileSystem: true });
32913
+ const project = new Project17({ useInMemoryFileSystem: true });
32842
32914
  const sourceFile = project.createSourceFile(path, text, { overwrite: true });
32843
32915
  const exported = /* @__PURE__ */ new Set();
32844
32916
  for (const decl of sourceFile.getExportDeclarations()) {
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@dereekb/dbx-components-mcp",
3
- "version": "13.11.1",
3
+ "version": "13.11.2",
4
4
  "type": "module",
5
5
  "bin": {
6
6
  "dbx-components-mcp": "dbx-components-mcp.js"
@@ -15,18 +15,19 @@
15
15
  "@angular/core": "21.2.11",
16
16
  "@angular/forms": "21.2.11",
17
17
  "@angular/material": "^21.2.9",
18
- "@dereekb/dbx-core": "13.11.1",
19
- "@dereekb/dbx-firebase": "13.11.1",
20
- "@dereekb/dbx-form": "13.11.1",
21
- "@dereekb/dbx-web": "13.11.1",
22
- "@dereekb/firebase": "13.11.1",
23
- "@dereekb/firebase-server": "13.11.1",
24
- "@dereekb/rxjs": "13.11.1",
25
- "@dereekb/util": "13.11.1",
18
+ "@dereekb/dbx-cli": "13.11.2",
19
+ "@dereekb/dbx-core": "13.11.2",
20
+ "@dereekb/dbx-firebase": "13.11.2",
21
+ "@dereekb/dbx-form": "13.11.2",
22
+ "@dereekb/dbx-web": "13.11.2",
23
+ "@dereekb/firebase": "13.11.2",
24
+ "@dereekb/firebase-server": "13.11.2",
25
+ "@dereekb/rxjs": "13.11.2",
26
+ "@dereekb/util": "13.11.2",
26
27
  "@uirouter/angular": "21.0.0",
27
28
  "rxjs": "^7.8.2"
28
29
  },
29
30
  "devDependencies": {
30
- "@dereekb/model": "13.11.1"
31
+ "@dereekb/model": "13.11.2"
31
32
  }
32
33
  }