@dereekb/dbx-components-mcp 13.19.0 → 13.21.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.19.0",
13
+ version: "13.21.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.19.0",
25
- "@dereekb/util": "13.19.0"
24
+ "@dereekb/dbx-cli": "13.21.0",
25
+ "@dereekb/util": "13.21.0"
26
26
  }
27
27
  };
28
28
 
@@ -15658,6 +15658,153 @@ function collectRelativeImports(sourceFile, fileName) {
15658
15658
  return out;
15659
15659
  }
15660
15660
 
15661
+ // packages/dbx-cli/src/lib/route/url-match.ts
15662
+ function normalizePathname(pathname) {
15663
+ let result;
15664
+ if (pathname.length === 0) {
15665
+ result = "/";
15666
+ } else if (pathname.length > 1 && pathname.endsWith("/")) {
15667
+ result = pathname.slice(0, -1);
15668
+ } else {
15669
+ result = pathname;
15670
+ }
15671
+ return result;
15672
+ }
15673
+ function stripUrlQueryAndHash(url) {
15674
+ const hashStripped = url.split("#", 1)[0];
15675
+ return hashStripped.split("?", 1)[0];
15676
+ }
15677
+ function splitSegments(path) {
15678
+ const normalized = path.startsWith("/") ? path.slice(1) : path;
15679
+ return normalized.length === 0 ? [] : normalized.split("/");
15680
+ }
15681
+ function hasParamSegment(path) {
15682
+ return path.includes(":") || path.includes("{");
15683
+ }
15684
+ function tryMatchSegments(route, input) {
15685
+ if (route.length !== input.length) {
15686
+ return void 0;
15687
+ }
15688
+ const params = {};
15689
+ let result = params;
15690
+ for (const [i, r] of route.entries()) {
15691
+ const v = input[i];
15692
+ if (r.startsWith(":")) {
15693
+ const key = r.slice(1);
15694
+ params[key] = decodeURIComponent(v);
15695
+ } else if (r.startsWith("{") && r.endsWith("}")) {
15696
+ const inner = r.slice(1, -1);
15697
+ const colonIdx = inner.indexOf(":");
15698
+ const key = colonIdx >= 0 ? inner.slice(0, colonIdx) : inner;
15699
+ params[key] = decodeURIComponent(v);
15700
+ } else if (r !== v) {
15701
+ result = void 0;
15702
+ break;
15703
+ }
15704
+ }
15705
+ return result;
15706
+ }
15707
+ function extractUrlParamKeys(fullUrl) {
15708
+ if (fullUrl === void 0 || fullUrl.length === 0) {
15709
+ return [];
15710
+ }
15711
+ const seen = /* @__PURE__ */ new Set();
15712
+ const keys = [];
15713
+ for (const segment of fullUrl.split("/")) {
15714
+ const key = extractParamKeyFromSegment(segment);
15715
+ if (key !== void 0 && !seen.has(key)) {
15716
+ seen.add(key);
15717
+ keys.push(key);
15718
+ }
15719
+ }
15720
+ return keys;
15721
+ }
15722
+ function extractParamKeyFromSegment(rawSegment) {
15723
+ const segment = stripUrlQueryAndHash(rawSegment);
15724
+ if (segment.startsWith(":")) {
15725
+ const key = segment.slice(1);
15726
+ return key.length > 0 ? key : void 0;
15727
+ }
15728
+ if (segment.startsWith("{") && segment.endsWith("}")) {
15729
+ const inner = segment.slice(1, -1);
15730
+ const colonIdx = inner.indexOf(":");
15731
+ const rawKey = colonIdx >= 0 ? inner.slice(0, colonIdx) : inner;
15732
+ const key = rawKey.trim();
15733
+ return key.length > 0 ? key : void 0;
15734
+ }
15735
+ return void 0;
15736
+ }
15737
+ function matchUrlAgainstEntries(input) {
15738
+ const pathname = normalizePathname(input.pathname);
15739
+ const literal = matchLiteral(input.entries, pathname);
15740
+ let result;
15741
+ if (literal.length === 1) {
15742
+ result = { kind: "match", via: "literal", value: literal[0].value, matchedFullUrl: normalizePathname(literal[0].fullUrl ?? pathname), params: {} };
15743
+ } else if (literal.length > 1) {
15744
+ result = { kind: "ambiguous", values: literal.map((e) => e.value) };
15745
+ } else {
15746
+ result = matchParamOrNone(input.entries, pathname);
15747
+ }
15748
+ return result;
15749
+ }
15750
+ function matchLiteral(entries, pathname) {
15751
+ const out = [];
15752
+ for (const entry of entries) {
15753
+ if (entry.fullUrl !== void 0 && normalizePathname(entry.fullUrl) === pathname) {
15754
+ out.push(entry);
15755
+ }
15756
+ }
15757
+ return out;
15758
+ }
15759
+ function matchParamOrNone(entries, pathname) {
15760
+ const inputSegments = splitSegments(pathname);
15761
+ const hits = [];
15762
+ for (const entry of entries) {
15763
+ if (entry.fullUrl === void 0 || !hasParamSegment(entry.fullUrl)) {
15764
+ continue;
15765
+ }
15766
+ const params = tryMatchSegments(splitSegments(entry.fullUrl), inputSegments);
15767
+ if (params) {
15768
+ hits.push({ entry, params });
15769
+ }
15770
+ }
15771
+ let result;
15772
+ if (hits.length === 1) {
15773
+ result = { kind: "match", via: "param", value: hits[0].entry.value, matchedFullUrl: normalizePathname(hits[0].entry.fullUrl ?? pathname), params: hits[0].params };
15774
+ } else if (hits.length > 1) {
15775
+ result = { kind: "ambiguous", values: hits.map((h) => h.entry.value) };
15776
+ } else {
15777
+ result = { kind: "none", candidates: scoreCandidates(entries, pathname) };
15778
+ }
15779
+ return result;
15780
+ }
15781
+ function scoreCandidates(entries, pathname) {
15782
+ const segments = splitSegments(normalizePathname(pathname));
15783
+ const scored = [];
15784
+ for (const entry of entries) {
15785
+ if (entry.fullUrl === void 0) {
15786
+ continue;
15787
+ }
15788
+ const candidateSegments = splitSegments(entry.fullUrl);
15789
+ let score = 0;
15790
+ const maxIndex = Math.min(segments.length, candidateSegments.length);
15791
+ for (let i = 0; i < maxIndex; i += 1) {
15792
+ if (segments[i] === candidateSegments[i]) {
15793
+ score += 2;
15794
+ } else if (candidateSegments[i].startsWith(":") || candidateSegments[i].startsWith("{")) {
15795
+ score += 1;
15796
+ } else {
15797
+ break;
15798
+ }
15799
+ }
15800
+ if (score > 0) {
15801
+ scored.push({ value: entry.value, score });
15802
+ }
15803
+ }
15804
+ scored.sort((a, b) => b.score - a.score);
15805
+ return scored.slice(0, 5).map((s) => s.value);
15806
+ }
15807
+
15661
15808
  // packages/dbx-cli/src/lib/route/route-build-tree.ts
15662
15809
  // @__NO_SIDE_EFFECTS__
15663
15810
  function buildRouteTree(nodes, extractIssues) {
@@ -15817,7 +15964,7 @@ function composeFullUrl(node) {
15817
15964
  return void 0;
15818
15965
  }
15819
15966
  const joined = segments.join("");
15820
- const collapsed = joined.replaceAll(/\/{2,}/g, "/");
15967
+ const collapsed = stripUrlQueryAndHash(joined).replaceAll(/\/{2,}/g, "/");
15821
15968
  return collapsed.length === 0 ? "/" : collapsed;
15822
15969
  }
15823
15970
  function compareByName(a, b) {
@@ -15873,148 +16020,6 @@ function loadRouteTree(args) {
15873
16020
  return result;
15874
16021
  }
15875
16022
 
15876
- // packages/dbx-cli/src/lib/route/url-match.ts
15877
- function normalizePathname(pathname) {
15878
- let result;
15879
- if (pathname.length === 0) {
15880
- result = "/";
15881
- } else if (pathname.length > 1 && pathname.endsWith("/")) {
15882
- result = pathname.slice(0, -1);
15883
- } else {
15884
- result = pathname;
15885
- }
15886
- return result;
15887
- }
15888
- function splitSegments(path) {
15889
- const normalized = path.startsWith("/") ? path.slice(1) : path;
15890
- return normalized.length === 0 ? [] : normalized.split("/");
15891
- }
15892
- function hasParamSegment(path) {
15893
- return path.includes(":") || path.includes("{");
15894
- }
15895
- function tryMatchSegments(route, input) {
15896
- if (route.length !== input.length) {
15897
- return void 0;
15898
- }
15899
- const params = {};
15900
- let result = params;
15901
- for (const [i, r] of route.entries()) {
15902
- const v = input[i];
15903
- if (r.startsWith(":")) {
15904
- const key = r.slice(1);
15905
- params[key] = decodeURIComponent(v);
15906
- } else if (r.startsWith("{") && r.endsWith("}")) {
15907
- const inner = r.slice(1, -1);
15908
- const colonIdx = inner.indexOf(":");
15909
- const key = colonIdx >= 0 ? inner.slice(0, colonIdx) : inner;
15910
- params[key] = decodeURIComponent(v);
15911
- } else if (r !== v) {
15912
- result = void 0;
15913
- break;
15914
- }
15915
- }
15916
- return result;
15917
- }
15918
- function extractUrlParamKeys(fullUrl) {
15919
- if (fullUrl === void 0 || fullUrl.length === 0) {
15920
- return [];
15921
- }
15922
- const seen = /* @__PURE__ */ new Set();
15923
- const keys = [];
15924
- for (const segment of fullUrl.split("/")) {
15925
- const key = extractParamKeyFromSegment(segment);
15926
- if (key !== void 0 && !seen.has(key)) {
15927
- seen.add(key);
15928
- keys.push(key);
15929
- }
15930
- }
15931
- return keys;
15932
- }
15933
- function extractParamKeyFromSegment(segment) {
15934
- if (segment.startsWith(":")) {
15935
- const key = segment.slice(1);
15936
- return key.length > 0 ? key : void 0;
15937
- }
15938
- if (segment.startsWith("{") && segment.endsWith("}")) {
15939
- const inner = segment.slice(1, -1);
15940
- const colonIdx = inner.indexOf(":");
15941
- const rawKey = colonIdx >= 0 ? inner.slice(0, colonIdx) : inner;
15942
- const key = rawKey.trim();
15943
- return key.length > 0 ? key : void 0;
15944
- }
15945
- return void 0;
15946
- }
15947
- function matchUrlAgainstEntries(input) {
15948
- const pathname = normalizePathname(input.pathname);
15949
- const literal = matchLiteral(input.entries, pathname);
15950
- let result;
15951
- if (literal.length === 1) {
15952
- result = { kind: "match", via: "literal", value: literal[0].value, matchedFullUrl: normalizePathname(literal[0].fullUrl ?? pathname), params: {} };
15953
- } else if (literal.length > 1) {
15954
- result = { kind: "ambiguous", values: literal.map((e) => e.value) };
15955
- } else {
15956
- result = matchParamOrNone(input.entries, pathname);
15957
- }
15958
- return result;
15959
- }
15960
- function matchLiteral(entries, pathname) {
15961
- const out = [];
15962
- for (const entry of entries) {
15963
- if (entry.fullUrl !== void 0 && normalizePathname(entry.fullUrl) === pathname) {
15964
- out.push(entry);
15965
- }
15966
- }
15967
- return out;
15968
- }
15969
- function matchParamOrNone(entries, pathname) {
15970
- const inputSegments = splitSegments(pathname);
15971
- const hits = [];
15972
- for (const entry of entries) {
15973
- if (entry.fullUrl === void 0 || !hasParamSegment(entry.fullUrl)) {
15974
- continue;
15975
- }
15976
- const params = tryMatchSegments(splitSegments(entry.fullUrl), inputSegments);
15977
- if (params) {
15978
- hits.push({ entry, params });
15979
- }
15980
- }
15981
- let result;
15982
- if (hits.length === 1) {
15983
- result = { kind: "match", via: "param", value: hits[0].entry.value, matchedFullUrl: normalizePathname(hits[0].entry.fullUrl ?? pathname), params: hits[0].params };
15984
- } else if (hits.length > 1) {
15985
- result = { kind: "ambiguous", values: hits.map((h) => h.entry.value) };
15986
- } else {
15987
- result = { kind: "none", candidates: scoreCandidates(entries, pathname) };
15988
- }
15989
- return result;
15990
- }
15991
- function scoreCandidates(entries, pathname) {
15992
- const segments = splitSegments(normalizePathname(pathname));
15993
- const scored = [];
15994
- for (const entry of entries) {
15995
- if (entry.fullUrl === void 0) {
15996
- continue;
15997
- }
15998
- const candidateSegments = splitSegments(entry.fullUrl);
15999
- let score = 0;
16000
- const maxIndex = Math.min(segments.length, candidateSegments.length);
16001
- for (let i = 0; i < maxIndex; i += 1) {
16002
- if (segments[i] === candidateSegments[i]) {
16003
- score += 2;
16004
- } else if (candidateSegments[i].startsWith(":") || candidateSegments[i].startsWith("{")) {
16005
- score += 1;
16006
- } else {
16007
- break;
16008
- }
16009
- }
16010
- if (score > 0) {
16011
- scored.push({ value: entry.value, score });
16012
- }
16013
- }
16014
- scored.sort((a, b) => b.score - a.score);
16015
- return scored.slice(0, 5).map((s) => s.value);
16016
- }
16017
-
16018
16023
  // packages/dbx-cli/src/lib/route/component-resolve.ts
16019
16024
  import { dirname as dirname15, join as join5, normalize } from "node:path/posix";
16020
16025
  var TRY_EXTENSIONS = [".ts", ".tsx", "/index.ts", "/index.tsx"];
@@ -16089,6 +16094,8 @@ function containsImportedSymbol(importStatement, symbol) {
16089
16094
  var MODEL_TYPE_RE = /^[a-zA-Z][a-zA-Z0-9]*$/u;
16090
16095
  var LITERAL_SEGMENT_RE = /^[a-zA-Z0-9][a-zA-Z0-9_-]*$/u;
16091
16096
  var AUTH_UID_PLACEHOLDER = "{authUid}";
16097
+ var CONST_TOKEN_RE = /^\{const:([a-zA-Z0-9][a-zA-Z0-9_-]*)\}$/u;
16098
+ var FLAT_KEY_TOKEN_RE = /^\{flatKey:([a-zA-Z_][a-zA-Z0-9_]*)\}$/u;
16092
16099
  var ROUTE_MODEL_TAG = "dbxRouteModel";
16093
16100
  var ROUTE_MODEL_LIST_TAG = "dbxRouteModelList";
16094
16101
  function parseRouteModelTag(tag) {
@@ -16124,7 +16131,7 @@ function parseModelTag(tokens, description) {
16124
16131
  } else if (MODEL_TYPE_RE.test(tokens[0])) {
16125
16132
  const parsedKey = parseKeyTemplate(tokens[1]);
16126
16133
  if (parsedKey.ok) {
16127
- result = { ok: true, model: { modelType: tokens[0], kind: parsedKey.kind, keyTemplate: tokens[1], description, routeParams: parsedKey.routeParams } };
16134
+ result = { ok: true, model: { modelType: tokens[0], kind: parsedKey.kind, keyTemplate: parsedKey.keyTemplate, description, routeParams: parsedKey.routeParams } };
16128
16135
  } else {
16129
16136
  result = { ok: false, message: parsedKey.message };
16130
16137
  }
@@ -16146,17 +16153,21 @@ function parseKeyTemplate(keyTemplate) {
16146
16153
  return result;
16147
16154
  }
16148
16155
  function parseSingleSegmentKey(segment, keyTemplate) {
16156
+ const flatKeyParam = flatKeyTokenParam(segment);
16149
16157
  const placeholder = placeholderParam(segment);
16150
16158
  let result;
16151
- if (placeholder === void 0) {
16152
- result = { ok: false, message: `Single-segment key template \`${keyTemplate}\` must be a placeholder (\`:param\` or \`${AUTH_UID_PLACEHOLDER}\`).` };
16159
+ if (flatKeyParam !== void 0) {
16160
+ result = { ok: true, kind: "flatKey", keyTemplate, routeParams: [flatKeyParam] };
16161
+ } else if (placeholder === void 0) {
16162
+ result = { ok: false, message: `Single-segment key template \`${keyTemplate}\` must be a placeholder (\`:param\` or \`${AUTH_UID_PLACEHOLDER}\`) or a flattened-key token (\`{flatKey:<param>}\`).` };
16153
16163
  } else {
16154
- result = { ok: true, kind: "id", routeParams: placeholder.routeParam === void 0 ? [] : [placeholder.routeParam] };
16164
+ result = { ok: true, kind: "id", keyTemplate, routeParams: placeholder.routeParam === void 0 ? [] : [placeholder.routeParam] };
16155
16165
  }
16156
16166
  return result;
16157
16167
  }
16158
16168
  function parseAlternatingKey(segments, keyTemplate) {
16159
16169
  const routeParams = [];
16170
+ const normalizedSegments = [];
16160
16171
  let message;
16161
16172
  for (const [i, segment] of segments.entries()) {
16162
16173
  if (i % 2 === 0) {
@@ -16164,18 +16175,24 @@ function parseAlternatingKey(segments, keyTemplate) {
16164
16175
  message = `Key template \`${keyTemplate}\` segment \`${segment}\` must be a literal collection name.`;
16165
16176
  break;
16166
16177
  }
16178
+ normalizedSegments.push(segment);
16167
16179
  } else {
16168
16180
  const placeholder = placeholderParam(segment);
16169
- if (placeholder === void 0) {
16170
- message = `Key template \`${keyTemplate}\` segment \`${segment}\` must be a placeholder (\`:param\` or \`${AUTH_UID_PLACEHOLDER}\`).`;
16181
+ const constId = constTokenId(segment);
16182
+ if (placeholder !== void 0) {
16183
+ if (placeholder.routeParam !== void 0) {
16184
+ routeParams.push(placeholder.routeParam);
16185
+ }
16186
+ normalizedSegments.push(segment);
16187
+ } else if (constId === void 0) {
16188
+ message = `Key template \`${keyTemplate}\` segment \`${segment}\` must be a placeholder (\`:param\` or \`${AUTH_UID_PLACEHOLDER}\`) or a fixed id (\`{const:<id>}\`).`;
16171
16189
  break;
16172
- }
16173
- if (placeholder.routeParam !== void 0) {
16174
- routeParams.push(placeholder.routeParam);
16190
+ } else {
16191
+ normalizedSegments.push(constId);
16175
16192
  }
16176
16193
  }
16177
16194
  }
16178
- return message === void 0 ? { ok: true, kind: "key", routeParams } : { ok: false, message };
16195
+ return message === void 0 ? { ok: true, kind: "key", keyTemplate: normalizedSegments.join("/"), routeParams } : { ok: false, message };
16179
16196
  }
16180
16197
  function placeholderParam(segment) {
16181
16198
  let result;
@@ -16188,6 +16205,14 @@ function placeholderParam(segment) {
16188
16205
  }
16189
16206
  return result;
16190
16207
  }
16208
+ function constTokenId(segment) {
16209
+ const match = CONST_TOKEN_RE.exec(segment);
16210
+ return match === null ? void 0 : match[1];
16211
+ }
16212
+ function flatKeyTokenParam(segment) {
16213
+ const match = FLAT_KEY_TOKEN_RE.exec(segment);
16214
+ return match === null ? void 0 : match[1];
16215
+ }
16191
16216
 
16192
16217
  // packages/dbx-cli/src/lib/route/route-models-extract.ts
16193
16218
  function extractComponentRouteModelTags(sourceFile, component) {
@@ -16209,7 +16234,7 @@ function collectRouteModelTags2(jsDocs) {
16209
16234
 
16210
16235
  // packages/dbx-cli/src/lib/route/route-manifest.ts
16211
16236
  import { Project as Project5 } from "ts-morph";
16212
- var ROUTE_MANIFEST_VERSION = 1;
16237
+ var ROUTE_MANIFEST_VERSION = 2;
16213
16238
  function buildRouteManifest(input, now = /* @__PURE__ */ new Date()) {
16214
16239
  const warnings = [];
16215
16240
  const tree = loadRouteTree({ sources: input.sources });