@dereekb/dbx-cli 13.15.0 → 13.17.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (46) hide show
  1. package/eslint/index.cjs.default.js +1 -0
  2. package/eslint/index.cjs.js +1050 -0
  3. package/eslint/index.cjs.mjs +2 -0
  4. package/eslint/index.d.ts +1 -0
  5. package/eslint/index.esm.js +1046 -0
  6. package/eslint/package.json +25 -0
  7. package/eslint/rollup.alias-internal.config.d.ts +11 -0
  8. package/eslint/src/index.d.ts +1 -0
  9. package/eslint/src/lib/index.d.ts +2 -0
  10. package/eslint/src/lib/plugin.d.ts +22 -0
  11. package/eslint/src/lib/valid-dbx-route-model-tags.rule.d.ts +59 -0
  12. package/firebase-api-manifest/main.js +318 -228
  13. package/firebase-api-manifest/package.json +3 -3
  14. package/generate-firestore-indexes/main.js +37 -24
  15. package/generate-firestore-indexes/package.json +2 -2
  16. package/generate-mcp-manifest/main.js +57 -39
  17. package/generate-mcp-manifest/package.json +3 -3
  18. package/generate-route-manifest/main.js +1137 -0
  19. package/generate-route-manifest/package.json +10 -0
  20. package/index.cjs.js +4847 -1953
  21. package/index.esm.js +4827 -1954
  22. package/lint-cache/package.json +2 -2
  23. package/manifest-extract/index.cjs.js +175 -240
  24. package/manifest-extract/index.esm.js +174 -239
  25. package/manifest-extract/package.json +9 -4
  26. package/package.json +16 -6
  27. package/src/lib/index.d.ts +2 -0
  28. package/src/lib/manifest/types.d.ts +53 -0
  29. package/src/lib/mcp-scan/manifest/package-root.d.ts +17 -0
  30. package/src/lib/mcp-scan/manifest/tokens-schema.d.ts +5 -4
  31. package/src/lib/mcp-scan/scan/extract-models/assemble.d.ts +17 -0
  32. package/src/lib/route/component-resolve.d.ts +48 -0
  33. package/src/lib/route/index.d.ts +18 -0
  34. package/src/lib/route/route-build-tree.d.ts +31 -0
  35. package/src/lib/route/route-extract.d.ts +46 -0
  36. package/src/lib/route/route-load-tree.d.ts +17 -0
  37. package/src/lib/route/route-manifest.d.ts +132 -0
  38. package/src/lib/route/route-model-tag.d.ts +89 -0
  39. package/src/lib/route/route-models-extract.d.ts +22 -0
  40. package/src/lib/route/route-resolve-sources.d.ts +39 -0
  41. package/src/lib/route/route-types.d.ts +136 -0
  42. package/src/lib/route/url-match.d.ts +116 -0
  43. package/src/lib/scan-helpers/firestore-model-extract-utils.d.ts +43 -0
  44. package/test/index.cjs.js +1 -1
  45. package/test/index.esm.js +1 -1
  46. package/test/package.json +9 -9
@@ -1,14 +1,14 @@
1
1
  {
2
2
  "name": "@dereekb/dbx-cli-firebase-api-manifest",
3
- "version": "13.15.0",
3
+ "version": "13.17.0",
4
4
  "private": true,
5
5
  "type": "module",
6
6
  "devDependencies": {
7
7
  "ts-morph": "^21.0.0"
8
8
  },
9
9
  "peerDependencies": {
10
- "@dereekb/dbx-cli": "13.15.0",
11
- "@dereekb/util": "13.15.0",
10
+ "@dereekb/dbx-cli": "13.17.0",
11
+ "@dereekb/util": "13.17.0",
12
12
  "prettier": "3.8.3"
13
13
  }
14
14
  }
@@ -5,14 +5,14 @@ const require = __createRequire(import.meta.url);
5
5
  // packages/dbx-cli/generate-firestore-indexes/package.json
6
6
  var package_default = {
7
7
  name: "@dereekb/dbx-cli-generate-firestore-indexes",
8
- version: "13.15.0",
8
+ version: "13.17.0",
9
9
  private: true,
10
10
  type: "module",
11
11
  devDependencies: {
12
12
  eslint: "10.4.0"
13
13
  },
14
14
  peerDependencies: {
15
- "@dereekb/dbx-cli": "13.15.0"
15
+ "@dereekb/dbx-cli": "13.17.0"
16
16
  }
17
17
  };
18
18
 
@@ -2109,38 +2109,51 @@ function parseArgv(argv) {
2109
2109
  return { component, output, check, json, help, error };
2110
2110
  }
2111
2111
  async function readExistingIndexes(input) {
2112
+ const text = await readExistingIndexesText(input);
2113
+ let result2;
2114
+ if (text !== void 0) {
2115
+ result2 = parseExistingIndexesJson(text, input.outputAbs, input.stderr);
2116
+ }
2117
+ return result2;
2118
+ }
2119
+ async function readExistingIndexesText(input) {
2112
2120
  const { outputAbs, readFile, stderr } = input;
2113
- let text = null;
2114
- let readFailed = false;
2121
+ let text;
2115
2122
  try {
2116
2123
  text = await readFile(outputAbs);
2117
2124
  } catch (err) {
2118
- readFailed = true;
2119
2125
  const code = err.code;
2120
2126
  if (code !== "ENOENT") {
2121
2127
  stderr(`generate-firestore-indexes: could not read existing ${outputAbs}: ${err instanceof Error ? err.message : String(err)}`);
2122
2128
  }
2129
+ text = void 0;
2123
2130
  }
2131
+ return text;
2132
+ }
2133
+ function parseExistingIndexesJson(text, outputAbs, stderr) {
2124
2134
  let result2;
2125
- if (!readFailed && text !== null) {
2126
- let parsed;
2127
- let parseFailed = false;
2128
- try {
2129
- parsed = JSON.parse(text);
2130
- } catch (err) {
2131
- parseFailed = true;
2132
- stderr(`generate-firestore-indexes: existing ${outputAbs} is not valid JSON: ${err instanceof Error ? err.message : String(err)}`);
2133
- }
2134
- if (!parseFailed) {
2135
- if (parsed === null || typeof parsed !== "object") {
2136
- stderr(`generate-firestore-indexes: existing ${outputAbs} top-level value is not an object`);
2137
- } else {
2138
- const raw = parsed;
2139
- const indexes = Array.isArray(raw.indexes) ? raw.indexes : [];
2140
- const fieldOverrides = Array.isArray(raw.fieldOverrides) ? raw.fieldOverrides : [];
2141
- result2 = { indexes, fieldOverrides };
2142
- }
2143
- }
2135
+ let parsed;
2136
+ let parseFailed = false;
2137
+ try {
2138
+ parsed = JSON.parse(text);
2139
+ } catch (err) {
2140
+ parseFailed = true;
2141
+ stderr(`generate-firestore-indexes: existing ${outputAbs} is not valid JSON: ${err instanceof Error ? err.message : String(err)}`);
2142
+ }
2143
+ if (!parseFailed) {
2144
+ result2 = coerceFirestoreIndexesJson(parsed, outputAbs, stderr);
2145
+ }
2146
+ return result2;
2147
+ }
2148
+ function coerceFirestoreIndexesJson(parsed, outputAbs, stderr) {
2149
+ let result2;
2150
+ if (parsed === null || typeof parsed !== "object") {
2151
+ stderr(`generate-firestore-indexes: existing ${outputAbs} top-level value is not an object`);
2152
+ } else {
2153
+ const raw = parsed;
2154
+ const indexes = Array.isArray(raw.indexes) ? raw.indexes : [];
2155
+ const fieldOverrides = Array.isArray(raw.fieldOverrides) ? raw.fieldOverrides : [];
2156
+ result2 = { indexes, fieldOverrides };
2144
2157
  }
2145
2158
  return result2;
2146
2159
  }
@@ -1,12 +1,12 @@
1
1
  {
2
2
  "name": "@dereekb/dbx-cli-generate-firestore-indexes",
3
- "version": "13.15.0",
3
+ "version": "13.17.0",
4
4
  "private": true,
5
5
  "type": "module",
6
6
  "devDependencies": {
7
7
  "eslint": "10.4.0"
8
8
  },
9
9
  "peerDependencies": {
10
- "@dereekb/dbx-cli": "13.15.0"
10
+ "@dereekb/dbx-cli": "13.17.0"
11
11
  }
12
12
  }
@@ -725,9 +725,7 @@ var EMPTY_ROLE_CONST_RESOLUTION = { roles: [], unresolved: [] };
725
725
  function resolveRoleConstByName(name, ctx) {
726
726
  let result;
727
727
  const scalar = ctx.knownRoles.get(name);
728
- if (scalar !== void 0) {
729
- result = { roles: [scalar], unresolved: [] };
730
- } else {
728
+ if (scalar === void 0) {
731
729
  const arrayExpr = ctx.localArrayRoles.get(name);
732
730
  if (arrayExpr === void 0) {
733
731
  result = { roles: [], unresolved: [name] };
@@ -738,6 +736,8 @@ function resolveRoleConstByName(name, ctx) {
738
736
  nextSeen.add(name);
739
737
  result = resolveRoleArrayLiteral(arrayExpr, { ...ctx, seen: nextSeen });
740
738
  }
739
+ } else {
740
+ result = { roles: [scalar], unresolved: [] };
741
741
  }
742
742
  return result;
743
743
  }
@@ -1015,53 +1015,67 @@ function renderMcpManifest(input, now = /* @__PURE__ */ new Date()) {
1015
1015
  const warnings = [];
1016
1016
  const errors = [];
1017
1017
  const seenNames = /* @__PURE__ */ new Map();
1018
+ const segments = buildSegmentMap(input.modelManifest);
1019
+ const toolEntries = input.apiManifest.filter((entry) => entry.verb !== "standalone");
1020
+ const nameCounts = countToolNames(toolEntries, segments);
1021
+ for (const entry of toolEntries) {
1022
+ registerToolEntry({ entry, segments, nameCounts, tools, seenNames, warnings, errors });
1023
+ }
1024
+ const models = input.modelManifest != null && input.modelManifest.length > 0 ? input.modelManifest.map(projectModelEntry) : void 0;
1025
+ const enums = input.enumManifest != null && Object.keys(input.enumManifest).length > 0 ? input.enumManifest : void 0;
1026
+ const auth = input.auth == null ? void 0 : projectAuthSection(input.auth.registry, input.auth.app);
1027
+ const base = { version: MCP_MANIFEST_VERSION, generatedAt: now.toISOString(), tools };
1028
+ const manifest = {
1029
+ ...base,
1030
+ ...models == null ? {} : { models },
1031
+ ...enums == null ? {} : { enums },
1032
+ ...auth == null ? {} : { auth }
1033
+ };
1034
+ return { manifest, warnings, errors };
1035
+ }
1036
+ function buildSegmentMap(modelManifest) {
1018
1037
  const segments = /* @__PURE__ */ new Map();
1019
- if (input.modelManifest != null) {
1020
- for (const model of input.modelManifest) {
1038
+ if (modelManifest != null) {
1039
+ for (const model of modelManifest) {
1021
1040
  if (model.mcpToolNameSegment != null && model.mcpToolNameSegment.length > 0) {
1022
1041
  segments.set(model.modelType, model.mcpToolNameSegment);
1023
1042
  }
1024
1043
  }
1025
1044
  }
1026
- const toolEntries = input.apiManifest.filter((entry) => entry.verb !== "standalone");
1045
+ return segments;
1046
+ }
1047
+ function countToolNames(toolEntries, segments) {
1027
1048
  const nameCounts = /* @__PURE__ */ new Map();
1028
1049
  for (const entry of toolEntries) {
1029
1050
  const segment = segments.get(entry.model) ?? entry.model;
1030
1051
  const baseName = buildMcpToolName(segment, entry.verb, entry.specifier);
1031
1052
  nameCounts.set(baseName, (nameCounts.get(baseName) ?? 0) + 1);
1032
1053
  }
1033
- for (const entry of toolEntries) {
1034
- const key = mcpManifestKey(entry.model, entry.verb, entry.specifier);
1035
- tools[key] = buildToolEntry(entry);
1036
- const segment = segments.get(entry.model) ?? entry.model;
1037
- const baseName = buildMcpToolName(segment, entry.verb, entry.specifier);
1038
- const clashes = (nameCounts.get(baseName) ?? 0) > 1;
1039
- const toolName = clashes ? buildDisambiguatedMcpToolName(segment, entry.verb, entry.specifier) : baseName;
1040
- if (toolName !== baseName) {
1041
- warnings.push(`Tool name "${baseName}" is produced by more than one entry; re-derived as "${toolName}" (${key}) with the abbreviated call type to disambiguate.`);
1042
- }
1043
- const validation = validateMcpToolName(toolName);
1044
- if (validation.level === "error") {
1045
- errors.push(`Tool name "${toolName}" is ${validation.length} chars, over the ${MCP_TOOL_NAME_MAX_LENGTH}-char MCP cap (${key}). Shorten the model/specifier, set a per-model mcpToolNameSegment, or hide the tool.`);
1046
- } else if (validation.level === "warn") {
1047
- warnings.push(`Tool name "${toolName}" is ${validation.length} chars, over the ${MCP_TOOL_NAME_WARN_LENGTH}-char soft limit (${key}).`);
1048
- }
1049
- const priorKey = seenNames.get(toolName);
1050
- if (priorKey != null) {
1051
- warnings.push(`Tool name "${toolName}" is still produced by more than one entry (${priorKey} and ${key}) after disambiguation; one shadows the other unless hidden or renamed at runtime.`);
1052
- } else {
1053
- seenNames.set(toolName, key);
1054
- }
1054
+ return nameCounts;
1055
+ }
1056
+ function registerToolEntry(input) {
1057
+ const { entry, segments, nameCounts, tools, seenNames, warnings, errors } = input;
1058
+ const key = mcpManifestKey(entry.model, entry.verb, entry.specifier);
1059
+ tools[key] = buildToolEntry(entry);
1060
+ const segment = segments.get(entry.model) ?? entry.model;
1061
+ const baseName = buildMcpToolName(segment, entry.verb, entry.specifier);
1062
+ const clashes = (nameCounts.get(baseName) ?? 0) > 1;
1063
+ const toolName = clashes ? buildDisambiguatedMcpToolName(segment, entry.verb, entry.specifier) : baseName;
1064
+ if (toolName !== baseName) {
1065
+ warnings.push(`Tool name "${baseName}" is produced by more than one entry; re-derived as "${toolName}" (${key}) with the abbreviated call type to disambiguate.`);
1066
+ }
1067
+ const validation = validateMcpToolName(toolName);
1068
+ if (validation.level === "error") {
1069
+ errors.push(`Tool name "${toolName}" is ${validation.length} chars, over the ${MCP_TOOL_NAME_MAX_LENGTH}-char MCP cap (${key}). Shorten the model/specifier, set a per-model mcpToolNameSegment, or hide the tool.`);
1070
+ } else if (validation.level === "warn") {
1071
+ warnings.push(`Tool name "${toolName}" is ${validation.length} chars, over the ${MCP_TOOL_NAME_WARN_LENGTH}-char soft limit (${key}).`);
1072
+ }
1073
+ const priorKey = seenNames.get(toolName);
1074
+ if (priorKey == null) {
1075
+ seenNames.set(toolName, key);
1076
+ } else {
1077
+ warnings.push(`Tool name "${toolName}" is still produced by more than one entry (${priorKey} and ${key}) after disambiguation; one shadows the other unless hidden or renamed at runtime.`);
1055
1078
  }
1056
- const models = input.modelManifest != null && input.modelManifest.length > 0 ? input.modelManifest.map(projectModelEntry) : void 0;
1057
- const auth = input.auth == null ? void 0 : projectAuthSection(input.auth.registry, input.auth.app);
1058
- const base = { version: MCP_MANIFEST_VERSION, generatedAt: now.toISOString(), tools };
1059
- const manifest = {
1060
- ...base,
1061
- ...models == null ? {} : { models },
1062
- ...auth == null ? {} : { auth }
1063
- };
1064
- return { manifest, warnings, errors };
1065
1079
  }
1066
1080
  function projectAuthSection(registry, appSlug) {
1067
1081
  const primary = registry.findApp(appSlug);
@@ -1300,8 +1314,9 @@ Expected output of \`nx run <cli>:generate-api-manifest\`.`);
1300
1314
  writeFileSync(tmpPath, serialized);
1301
1315
  renameSync(tmpPath, outputPath);
1302
1316
  const modelCount = manifest.models?.length ?? 0;
1317
+ const enumCount = manifest.enums == null ? 0 : Object.keys(manifest.enums).length;
1303
1318
  const authCount = manifest.auth?.claims.length ?? 0;
1304
- console.log(`[wrote] ${relative2(WORKSPACE_ROOT, outputPath)} \u2014 ${Object.keys(manifest.tools).length} tools, ${modelCount} models, ${authCount} auth claims`);
1319
+ console.log(`[wrote] ${relative2(WORKSPACE_ROOT, outputPath)} \u2014 ${Object.keys(manifest.tools).length} tools, ${modelCount} models, ${enumCount} enums, ${authCount} auth claims`);
1305
1320
  }
1306
1321
  async function maybeLoadAuth(flags) {
1307
1322
  if (flags.app == null || flags.claimsInputs.length === 0) {
@@ -1330,7 +1345,10 @@ async function loadManifest(path) {
1330
1345
  const namedModel = Object.entries(loaded).find(([key]) => key.endsWith("_MODEL_MANIFEST"));
1331
1346
  const modelManifestValue = namedModel?.[1];
1332
1347
  const modelManifest = Array.isArray(modelManifestValue) ? modelManifestValue : void 0;
1333
- return { apiManifest, modelManifest };
1348
+ const namedEnum = Object.entries(loaded).find(([key]) => key.endsWith("_ENUM_MANIFEST"));
1349
+ const enumManifestValue = namedEnum?.[1];
1350
+ const enumManifest = enumManifestValue != null && typeof enumManifestValue === "object" && !Array.isArray(enumManifestValue) ? enumManifestValue : void 0;
1351
+ return { apiManifest, modelManifest, enumManifest };
1334
1352
  }
1335
1353
  function loadTsconfigPathAliases() {
1336
1354
  const tsconfigPath = resolve(WORKSPACE_ROOT, "tsconfig.base.json");
@@ -1,11 +1,11 @@
1
1
  {
2
2
  "name": "@dereekb/dbx-cli-generate-mcp-manifest",
3
- "version": "13.15.0",
3
+ "version": "13.17.0",
4
4
  "private": true,
5
5
  "type": "module",
6
6
  "peerDependencies": {
7
- "@dereekb/dbx-cli": "13.15.0",
8
- "@dereekb/model": "13.15.0",
7
+ "@dereekb/dbx-cli": "13.17.0",
8
+ "@dereekb/model": "13.17.0",
9
9
  "arktype": "^2.2.0",
10
10
  "jiti": "2.6.1"
11
11
  }