@dereekb/dbx-cli 13.11.5 → 13.11.7

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.
@@ -386,6 +386,7 @@ function readJsDocSummary(node) {
386
386
 
387
387
  // packages/dbx-cli/manifest-extract/src/lib/extract-models.ts
388
388
  import { Node as Node3, Project as Project3 } from "ts-morph";
389
+ var PASSTHROUGH_TYPE_WRAPPERS = /* @__PURE__ */ new Set(["Partial", "Required", "Readonly", "NonNullable", "MaybeMap", "Pick", "Omit"]);
389
390
  var IDENTITY_FN = "firestoreModelIdentity";
390
391
  var CONVERTER_FN_NAMES = ["snapshotConverterFunctions", "firestoreSubObject", "firestoreObjectArray"];
391
392
  var SUB_OBJECT_FN = "firestoreSubObject";
@@ -456,7 +457,7 @@ function readInterfaces(sourceFile) {
456
457
  function buildInterface(decl) {
457
458
  const jsDocs = decl.getJsDocs();
458
459
  const hasDbxModelTag = jsDocsHaveTag(jsDocs, "dbxModel");
459
- const extendsNames = decl.getExtends().map((e) => e.getExpression().getText());
460
+ const extendsNames = decl.getExtends().map(resolveExtendsName);
460
461
  const props = [];
461
462
  for (const prop of decl.getProperties()) {
462
463
  const propJsDocs = prop.getJsDocs();
@@ -481,6 +482,39 @@ function buildInterface(decl) {
481
482
  props
482
483
  };
483
484
  }
485
+ function resolveExtendsName(expr) {
486
+ const head = expr.getExpression().getText();
487
+ let result = head;
488
+ if (PASSTHROUGH_TYPE_WRAPPERS.has(head)) {
489
+ const typeArgs = expr.getTypeArguments();
490
+ if (typeArgs.length > 0) {
491
+ const peeled = peelTypeNode(typeArgs[0]);
492
+ if (peeled !== void 0) {
493
+ result = peeled;
494
+ }
495
+ }
496
+ }
497
+ return result;
498
+ }
499
+ function peelTypeNode(node) {
500
+ let current = node;
501
+ while (Node3.isParenthesizedTypeNode(current)) {
502
+ current = current.getTypeNode();
503
+ }
504
+ let result;
505
+ if (Node3.isTypeReference(current)) {
506
+ const name = current.getTypeName().getText();
507
+ if (PASSTHROUGH_TYPE_WRAPPERS.has(name)) {
508
+ const inner = current.getTypeArguments();
509
+ if (inner.length > 0) {
510
+ result = peelTypeNode(inner[0]);
511
+ }
512
+ } else {
513
+ result = name;
514
+ }
515
+ }
516
+ return result;
517
+ }
484
518
  function readConverters(sourceFile) {
485
519
  const out = [];
486
520
  for (const statement of sourceFile.getVariableStatements()) {
@@ -1088,7 +1122,7 @@ function escapeRegExp(value) {
1088
1122
  // packages/dbx-cli/firebase-api-manifest/src/generate-api-manifest/emit.ts
1089
1123
  import { format, resolveConfig } from "prettier";
1090
1124
  async function renderManifest(input) {
1091
- const { outputFile, entries, projectName, namespace, modelEntries, modelNamespace } = input;
1125
+ const { outputFile, entries, projectName, namespace, modelEntries, modelNamespace, emitConverters = false } = input;
1092
1126
  const importsByPackage = /* @__PURE__ */ new Map();
1093
1127
  for (const entry of entries) {
1094
1128
  if (!entry.packageName || !entry.validatorName) continue;
@@ -1106,7 +1140,7 @@ async function renderManifest(input) {
1106
1140
  const modelSection = emitModels ? `
1107
1141
 
1108
1142
  export const ${modelNamespace}: CliModelManifest = [
1109
- ${(modelEntries ?? []).map((m) => renderModelEntry(m)).join(",\n")}
1143
+ ${(modelEntries ?? []).map((m) => renderModelEntry(m, emitConverters)).join(",\n")}
1110
1144
  ];
1111
1145
  ` : "";
1112
1146
  const source = `/* eslint-disable @nx/enforce-module-boundaries */
@@ -1152,7 +1186,7 @@ async function formatWithPrettier(source, outputFile) {
1152
1186
  const config = await resolveConfig(outputFile);
1153
1187
  return format(source, { ...config, filepath: outputFile });
1154
1188
  }
1155
- function renderModelEntry(entry) {
1189
+ function renderModelEntry(entry, emitConverters) {
1156
1190
  const fields = [
1157
1191
  `modelType: ${JSON.stringify(entry.modelType)}`,
1158
1192
  `modelName: ${JSON.stringify(entry.modelName)}`,
@@ -1163,26 +1197,26 @@ function renderModelEntry(entry) {
1163
1197
  entry.description ? `description: ${JSON.stringify(entry.description)}` : void 0,
1164
1198
  `sourcePackage: ${JSON.stringify(entry.sourcePackage)}`,
1165
1199
  `sourceFile: ${JSON.stringify(entry.sourceFile)}`,
1166
- `fields: ${renderModelFields(entry.fields)}`
1200
+ `fields: ${renderModelFields(entry.fields, emitConverters)}`
1167
1201
  ];
1168
1202
  return ` { ${fields.filter((v) => Boolean(v)).join(", ")} }`;
1169
1203
  }
1170
- function renderModelFields(fields) {
1204
+ function renderModelFields(fields, emitConverters) {
1171
1205
  if (fields.length === 0) return "[]";
1172
- const items = fields.map((field) => renderModelField(field));
1206
+ const items = fields.map((field) => renderModelField(field, emitConverters));
1173
1207
  return `[${items.join(", ")}]`;
1174
1208
  }
1175
- function renderModelField(field) {
1209
+ function renderModelField(field, emitConverters) {
1176
1210
  const parts = [
1177
1211
  `name: ${JSON.stringify(field.name)}`,
1178
1212
  `longName: ${JSON.stringify(field.longName)}`,
1179
- `converter: ${JSON.stringify(field.converter)}`,
1213
+ emitConverters && field.converter !== void 0 ? `converter: ${JSON.stringify(field.converter)}` : void 0,
1180
1214
  field.tsType ? `tsType: ${JSON.stringify(field.tsType)}` : void 0,
1181
1215
  `optional: ${field.optional ? "true" : "false"}`,
1182
1216
  field.description ? `description: ${JSON.stringify(field.description)}` : void 0,
1183
1217
  field.enumRef ? `enumRef: ${JSON.stringify(field.enumRef)}` : void 0,
1184
1218
  field.syncFlag ? `syncFlag: ${JSON.stringify(field.syncFlag)}` : void 0,
1185
- field.nestedFields ? `nestedFields: ${renderModelFields(field.nestedFields)}` : void 0,
1219
+ field.nestedFields ? `nestedFields: ${renderModelFields(field.nestedFields, emitConverters)}` : void 0,
1186
1220
  field.nestedFields ? `nestedIsArray: ${field.nestedIsArray ? "true" : "false"}` : void 0
1187
1221
  ];
1188
1222
  return `{ ${parts.filter((v) => Boolean(v)).join(", ")} }`;
@@ -1271,7 +1305,7 @@ async function main() {
1271
1305
  const modelEntries = flags.emitModels ? assembleModels({ extractions: modelSources }) : [];
1272
1306
  const filteredModelEntries = flags.only ? modelEntries.filter((m) => flags.only?.has(m.modelType)) : modelEntries;
1273
1307
  ensureOutputDir(outputDir);
1274
- const formatted = await renderManifest({ outputFile, entries: collected, projectName, namespace, modelEntries: filteredModelEntries, modelNamespace: deriveModelNamespace(flags.project) });
1308
+ const formatted = await renderManifest({ outputFile, entries: collected, projectName, namespace, modelEntries: filteredModelEntries, modelNamespace: deriveModelNamespace(flags.project), emitConverters: flags.emitModelConverters });
1275
1309
  if (existsSync3(outputFile) && readFileSync6(outputFile, "utf8") === formatted) {
1276
1310
  console.log(`[unchanged] ${relative2(WORKSPACE_ROOT, outputFile)}`);
1277
1311
  } else {
@@ -1312,11 +1346,14 @@ function parseFlags(argv) {
1312
1346
  let output;
1313
1347
  let project;
1314
1348
  let emitModels = false;
1349
+ let emitModelConverters = false;
1315
1350
  for (const arg of argv) {
1316
1351
  if (arg === "--strict") {
1317
1352
  strict = true;
1318
1353
  } else if (arg === "--emit-models") {
1319
1354
  emitModels = true;
1355
+ } else if (arg === "--emit-model-converters") {
1356
+ emitModelConverters = true;
1320
1357
  } else if (arg.startsWith("--only=")) {
1321
1358
  const list = arg.slice("--only=".length).split(",").map((s) => s.trim()).filter(Boolean);
1322
1359
  if (list.length > 0) only = new Set(list);
@@ -1328,7 +1365,7 @@ function parseFlags(argv) {
1328
1365
  project = arg.slice("--project=".length);
1329
1366
  }
1330
1367
  }
1331
- return { only, strict, functionsConfig, output, project, emitModels };
1368
+ return { only, strict, functionsConfig, output, project, emitModels, emitModelConverters };
1332
1369
  }
1333
1370
  function printUsageAndExit() {
1334
1371
  console.error(String.raw`generate-api-manifest
@@ -1350,7 +1387,10 @@ Optional:
1350
1387
  --strict Fail when any validator binding is missing.
1351
1388
  --emit-models Opt in to emitting <NAMESPACE>_MODEL_MANIFEST. Off by default —
1352
1389
  pair with the runtime \`modelManifest\` option on \`runCli\` to
1353
- enable the built-in \`model-info\` command.`);
1390
+ enable the built-in \`model-info\` command.
1391
+ --emit-model-converters Opt in to including each field's converter expression text inside
1392
+ the model manifest. Off by default — useful for downstream tooling
1393
+ (e.g. dbx-components MCP) but unused by the CLI.`);
1354
1394
  process.exit(1);
1355
1395
  }
1356
1396
  try {
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@dereekb/dbx-cli-firebase-api-manifest",
3
- "version": "13.11.5",
3
+ "version": "13.11.7",
4
4
  "private": true,
5
5
  "type": "module",
6
6
  "devDependencies": {
package/index.cjs.js CHANGED
@@ -1868,6 +1868,11 @@ function mergeOutputCommandsConfig(existing, updates) {
1868
1868
  'model.update',
1869
1869
  'model.delete'
1870
1870
  ];
1871
+ /**
1872
+ * The default redirect URI used by the CLI.
1873
+ *
1874
+ * Opens up to nothing in the browser so the user can copy/paste the resulting token url back into the CLI.
1875
+ */ var DEFAULT_CLI_REDIRECT_URI = 'http://127.0.0.1:0/callback';
1871
1876
  /**
1872
1877
  * Returns the input scope string with the `model.create`, `model.update`, and `model.delete`
1873
1878
  * scopes removed, preserving every other scope (including `model.read` and `model.query`).
@@ -3457,11 +3462,11 @@ function maskEnv$1(env) {
3457
3462
  resolve({
3458
3463
  argvValue: argv.redirectUri,
3459
3464
  existingValue: existing === null || existing === void 0 ? void 0 : existing.redirectUri,
3460
- prompt: "Redirect URI [".concat((_ref4 = existing === null || existing === void 0 ? void 0 : existing.redirectUri) !== null && _ref4 !== void 0 ? _ref4 : 'http://127.0.0.1:0/callback', "]: ")
3465
+ prompt: "Redirect URI [".concat((_ref4 = existing === null || existing === void 0 ? void 0 : existing.redirectUri) !== null && _ref4 !== void 0 ? _ref4 : DEFAULT_CLI_REDIRECT_URI, "]: ")
3461
3466
  })
3462
3467
  ];
3463
3468
  case 9:
3464
- redirectUri = (_ref3 = _state.sent()) !== null && _ref3 !== void 0 ? _ref3 : 'http://127.0.0.1:0/callback';
3469
+ redirectUri = (_ref3 = _state.sent()) !== null && _ref3 !== void 0 ? _ref3 : DEFAULT_CLI_REDIRECT_URI;
3465
3470
  scopes = (_argv_scopes = argv.scopes) !== null && _argv_scopes !== void 0 ? _argv_scopes : existing === null || existing === void 0 ? void 0 : existing.scopes;
3466
3471
  if (!apiBaseUrl || !oidcIssuer || !clientId || !clientSecret) {
3467
3472
  throw new CliError({
@@ -5616,27 +5621,37 @@ function _unsupported_iterable_to_array$2(o, minLen) {
5616
5621
  }
5617
5622
  function renderFieldsTree(fields, indent) {
5618
5623
  var out = [];
5624
+ var includeConverter = fields.some(function(f) {
5625
+ return f.converter !== undefined;
5626
+ });
5627
+ var header = includeConverter ? [
5628
+ 'NAME',
5629
+ 'LONG NAME',
5630
+ 'TYPE',
5631
+ 'OPTIONAL',
5632
+ 'CONVERTER'
5633
+ ] : [
5634
+ 'NAME',
5635
+ 'LONG NAME',
5636
+ 'TYPE',
5637
+ 'OPTIONAL'
5638
+ ];
5619
5639
  var rows = [
5620
- [
5621
- 'NAME',
5622
- 'LONG NAME',
5623
- 'TYPE',
5624
- 'OPTIONAL',
5625
- 'CONVERTER'
5626
- ]
5640
+ header
5627
5641
  ];
5628
5642
  var _iteratorNormalCompletion = true, _didIteratorError = false, _iteratorError = undefined;
5629
5643
  try {
5630
5644
  for(var _iterator = fields[Symbol.iterator](), _step; !(_iteratorNormalCompletion = (_step = _iterator.next()).done); _iteratorNormalCompletion = true){
5631
5645
  var field = _step.value;
5632
5646
  var _field_tsType;
5633
- rows.push([
5647
+ var row = [
5634
5648
  field.name,
5635
5649
  field.longName,
5636
5650
  (_field_tsType = field.tsType) !== null && _field_tsType !== void 0 ? _field_tsType : '',
5637
- field.optional ? 'yes' : 'no',
5638
- truncate(field.converter, 60)
5639
- ]);
5651
+ field.optional ? 'yes' : 'no'
5652
+ ];
5653
+ if (includeConverter) row.push(field.converter ? truncate(field.converter, 60) : '');
5654
+ rows.push(row);
5640
5655
  }
5641
5656
  } catch (err) {
5642
5657
  _didIteratorError = true;
@@ -8826,6 +8841,7 @@ Object.defineProperty(exports, "CALL_MODEL_APP_FUNCTION_KEY", {
8826
8841
  exports.CALL_MODEL_API_PATH = CALL_MODEL_API_PATH;
8827
8842
  exports.CliError = CliError;
8828
8843
  exports.DEFAULT_CLI_OIDC_SCOPES = DEFAULT_CLI_OIDC_SCOPES;
8844
+ exports.DEFAULT_CLI_REDIRECT_URI = DEFAULT_CLI_REDIRECT_URI;
8829
8845
  exports.DEFAULT_CLI_SECRET_PATTERNS = DEFAULT_CLI_SECRET_PATTERNS;
8830
8846
  exports.DEFAULT_MANIFEST_HELP_DATA_FORMAT = DEFAULT_MANIFEST_HELP_DATA_FORMAT;
8831
8847
  exports.DEFAULT_MANIFEST_HELP_MODE = DEFAULT_MANIFEST_HELP_MODE;
package/index.esm.js CHANGED
@@ -1866,6 +1866,11 @@ function mergeOutputCommandsConfig(existing, updates) {
1866
1866
  'model.update',
1867
1867
  'model.delete'
1868
1868
  ];
1869
+ /**
1870
+ * The default redirect URI used by the CLI.
1871
+ *
1872
+ * Opens up to nothing in the browser so the user can copy/paste the resulting token url back into the CLI.
1873
+ */ var DEFAULT_CLI_REDIRECT_URI = 'http://127.0.0.1:0/callback';
1869
1874
  /**
1870
1875
  * Returns the input scope string with the `model.create`, `model.update`, and `model.delete`
1871
1876
  * scopes removed, preserving every other scope (including `model.read` and `model.query`).
@@ -3455,11 +3460,11 @@ function maskEnv$1(env) {
3455
3460
  resolve({
3456
3461
  argvValue: argv.redirectUri,
3457
3462
  existingValue: existing === null || existing === void 0 ? void 0 : existing.redirectUri,
3458
- prompt: "Redirect URI [".concat((_ref4 = existing === null || existing === void 0 ? void 0 : existing.redirectUri) !== null && _ref4 !== void 0 ? _ref4 : 'http://127.0.0.1:0/callback', "]: ")
3463
+ prompt: "Redirect URI [".concat((_ref4 = existing === null || existing === void 0 ? void 0 : existing.redirectUri) !== null && _ref4 !== void 0 ? _ref4 : DEFAULT_CLI_REDIRECT_URI, "]: ")
3459
3464
  })
3460
3465
  ];
3461
3466
  case 9:
3462
- redirectUri = (_ref3 = _state.sent()) !== null && _ref3 !== void 0 ? _ref3 : 'http://127.0.0.1:0/callback';
3467
+ redirectUri = (_ref3 = _state.sent()) !== null && _ref3 !== void 0 ? _ref3 : DEFAULT_CLI_REDIRECT_URI;
3463
3468
  scopes = (_argv_scopes = argv.scopes) !== null && _argv_scopes !== void 0 ? _argv_scopes : existing === null || existing === void 0 ? void 0 : existing.scopes;
3464
3469
  if (!apiBaseUrl || !oidcIssuer || !clientId || !clientSecret) {
3465
3470
  throw new CliError({
@@ -5614,27 +5619,37 @@ function _unsupported_iterable_to_array$2(o, minLen) {
5614
5619
  }
5615
5620
  function renderFieldsTree(fields, indent) {
5616
5621
  var out = [];
5622
+ var includeConverter = fields.some(function(f) {
5623
+ return f.converter !== undefined;
5624
+ });
5625
+ var header = includeConverter ? [
5626
+ 'NAME',
5627
+ 'LONG NAME',
5628
+ 'TYPE',
5629
+ 'OPTIONAL',
5630
+ 'CONVERTER'
5631
+ ] : [
5632
+ 'NAME',
5633
+ 'LONG NAME',
5634
+ 'TYPE',
5635
+ 'OPTIONAL'
5636
+ ];
5617
5637
  var rows = [
5618
- [
5619
- 'NAME',
5620
- 'LONG NAME',
5621
- 'TYPE',
5622
- 'OPTIONAL',
5623
- 'CONVERTER'
5624
- ]
5638
+ header
5625
5639
  ];
5626
5640
  var _iteratorNormalCompletion = true, _didIteratorError = false, _iteratorError = undefined;
5627
5641
  try {
5628
5642
  for(var _iterator = fields[Symbol.iterator](), _step; !(_iteratorNormalCompletion = (_step = _iterator.next()).done); _iteratorNormalCompletion = true){
5629
5643
  var field = _step.value;
5630
5644
  var _field_tsType;
5631
- rows.push([
5645
+ var row = [
5632
5646
  field.name,
5633
5647
  field.longName,
5634
5648
  (_field_tsType = field.tsType) !== null && _field_tsType !== void 0 ? _field_tsType : '',
5635
- field.optional ? 'yes' : 'no',
5636
- truncate(field.converter, 60)
5637
- ]);
5649
+ field.optional ? 'yes' : 'no'
5650
+ ];
5651
+ if (includeConverter) row.push(field.converter ? truncate(field.converter, 60) : '');
5652
+ rows.push(row);
5638
5653
  }
5639
5654
  } catch (err) {
5640
5655
  _didIteratorError = true;
@@ -8817,4 +8832,4 @@ function printPaginatedOutput(input) {
8817
8832
  })();
8818
8833
  }
8819
8834
 
8820
- export { CALL_MODEL_API_PATH, CliError, DEFAULT_CLI_OIDC_SCOPES, DEFAULT_CLI_SECRET_PATTERNS, DEFAULT_MANIFEST_HELP_DATA_FORMAT, DEFAULT_MANIFEST_HELP_MODE, DEFAULT_MANIFEST_MODEL_COMMAND_NAME, DEFAULT_MODEL_INFO_COMMAND_NAME, DUMP_MERGE_MODES, DUMP_OUTPUT_MODES, MODEL_WRITE_OIDC_SCOPES, MULTIPLE_PAGES_OUTPUT_MODES, PROMPT_CANCELLED_ERROR_CODE, STANDARD_GLOBAL_OPTION_NAMES, applyEnvVarOverrides, buildAuthorizationUrl, buildCliPaths, buildDumpFilePath, buildErrorOutput, buildManifestCommands, buildModelInfoCommand, callModelOverHttp, callPassthroughCommand, configureCliErrorMapper, configureCliSecretPatterns, configureOutputOptions, createAuthCommand, createAuthMiddleware, createCallModelCommand, createCli, createCliContext, createCliTokenCacheStore, createContextSlot, createDoctorCommand, createEnvCommand, createOutputCommand, createOutputMiddleware, defaultDoctorChecks, detectDataHelpFormat, detectHelpMode, discoverOidcMetadata, dumpTimestamp, exchangeAuthorizationCode, expandModelKeys, fetchUserInfo, filterReadOnlyModelScopes, findCliEnvDefault, findCliModelManifestEntry, generateOAuthState, generatePkceMaterial, getCliContext, getOutputOptions, isCliEnvConfigComplete, isTokenExpired, loadCliConfig, maskSecret, mergeCliConfig, mergeCliEnvWithDefault, mergeOutputConfig, openStreamingDump, outputError, outputResult, parsePastedRedirect, pickFields, promptLine, refreshAccessToken, renderModelManifestEntry, renderModelManifestFields, renderModelManifestList, requireCliContext, resolveActiveEnvName, resolveCliModel, resolveOutputConfig, revokeToken, runCli, runPaginatedList, sanitizeString, saveCliConfig, setCliContext, withCallModelArgs, withEnv, withMultiplePages, withOutput, wrapCommandHandler };
8835
+ export { CALL_MODEL_API_PATH, CliError, DEFAULT_CLI_OIDC_SCOPES, DEFAULT_CLI_REDIRECT_URI, DEFAULT_CLI_SECRET_PATTERNS, DEFAULT_MANIFEST_HELP_DATA_FORMAT, DEFAULT_MANIFEST_HELP_MODE, DEFAULT_MANIFEST_MODEL_COMMAND_NAME, DEFAULT_MODEL_INFO_COMMAND_NAME, DUMP_MERGE_MODES, DUMP_OUTPUT_MODES, MODEL_WRITE_OIDC_SCOPES, MULTIPLE_PAGES_OUTPUT_MODES, PROMPT_CANCELLED_ERROR_CODE, STANDARD_GLOBAL_OPTION_NAMES, applyEnvVarOverrides, buildAuthorizationUrl, buildCliPaths, buildDumpFilePath, buildErrorOutput, buildManifestCommands, buildModelInfoCommand, callModelOverHttp, callPassthroughCommand, configureCliErrorMapper, configureCliSecretPatterns, configureOutputOptions, createAuthCommand, createAuthMiddleware, createCallModelCommand, createCli, createCliContext, createCliTokenCacheStore, createContextSlot, createDoctorCommand, createEnvCommand, createOutputCommand, createOutputMiddleware, defaultDoctorChecks, detectDataHelpFormat, detectHelpMode, discoverOidcMetadata, dumpTimestamp, exchangeAuthorizationCode, expandModelKeys, fetchUserInfo, filterReadOnlyModelScopes, findCliEnvDefault, findCliModelManifestEntry, generateOAuthState, generatePkceMaterial, getCliContext, getOutputOptions, isCliEnvConfigComplete, isTokenExpired, loadCliConfig, maskSecret, mergeCliConfig, mergeCliEnvWithDefault, mergeOutputConfig, openStreamingDump, outputError, outputResult, parsePastedRedirect, pickFields, promptLine, refreshAccessToken, renderModelManifestEntry, renderModelManifestFields, renderModelManifestList, requireCliContext, resolveActiveEnvName, resolveCliModel, resolveOutputConfig, revokeToken, runCli, runPaginatedList, sanitizeString, saveCliConfig, setCliContext, withCallModelArgs, withEnv, withMultiplePages, withOutput, wrapCommandHandler };
@@ -484,6 +484,25 @@ function _object_spread(target) {
484
484
  }
485
485
  return target;
486
486
  }
487
+ /**
488
+ * TS utility/structural wrappers that don't change the field surface for
489
+ * inheritance walks — `Partial<T>`, `Required<T>`, `Readonly<T>`,
490
+ * `NonNullable<T>` preserve every property, and `Pick<T, K>` / `Omit<T, K>`
491
+ * leave the original `T` reachable for long-name resolution. `MaybeMap<T>` is
492
+ * the workspace's own pass-through that decorates each prop with `Maybe<…>`
493
+ * without renaming. `extends` walks need to see through these to find the
494
+ * concrete ancestor interface — `getExpression()` alone returns just the
495
+ * leftmost identifier (`Partial`, `Pick`, …) and silently drops the inner
496
+ * model, leaving every inherited `@dbxModelVariable` tag unreachable.
497
+ */ var PASSTHROUGH_TYPE_WRAPPERS = new Set([
498
+ 'Partial',
499
+ 'Required',
500
+ 'Readonly',
501
+ 'NonNullable',
502
+ 'MaybeMap',
503
+ 'Pick',
504
+ 'Omit'
505
+ ]);
487
506
  var IDENTITY_FN = 'firestoreModelIdentity';
488
507
  var CONVERTER_FN_NAMES = [
489
508
  'snapshotConverterFunctions',
@@ -647,9 +666,7 @@ function readInterfaces(sourceFile) {
647
666
  function buildInterface(decl) {
648
667
  var jsDocs = decl.getJsDocs();
649
668
  var hasDbxModelTag = jsDocsHaveTag(jsDocs, 'dbxModel');
650
- var extendsNames = decl.getExtends().map(function(e) {
651
- return e.getExpression().getText();
652
- });
669
+ var extendsNames = decl.getExtends().map(resolveExtendsName);
653
670
  var props = [];
654
671
  var _iteratorNormalCompletion = true, _didIteratorError = false, _iteratorError = undefined;
655
672
  try {
@@ -693,6 +710,48 @@ function buildInterface(decl) {
693
710
  props: props
694
711
  };
695
712
  }
713
+ /**
714
+ * Resolves an `extends` clause to the concrete ancestor interface name,
715
+ * peeling any leading {@link PASSTHROUGH_TYPE_WRAPPERS}. Returns the leftmost
716
+ * identifier of the unwrapped expression so the inheritance walker can chain
717
+ * through utility-wrapped declarations like
718
+ * `extends Partial<MaybeMap<Omit<Base, '…'>>>`.
719
+ *
720
+ * @param expr - the `ExpressionWithTypeArguments` produced by `getExtends()`
721
+ * @returns the resolved interface name, or the original leftmost identifier when no inner reference is reachable
722
+ */ function resolveExtendsName(expr) {
723
+ var head = expr.getExpression().getText();
724
+ var result = head;
725
+ if (PASSTHROUGH_TYPE_WRAPPERS.has(head)) {
726
+ var typeArgs = expr.getTypeArguments();
727
+ if (typeArgs.length > 0) {
728
+ var peeled = peelTypeNode(typeArgs[0]);
729
+ if (peeled !== undefined) {
730
+ result = peeled;
731
+ }
732
+ }
733
+ }
734
+ return result;
735
+ }
736
+ function peelTypeNode(node) {
737
+ var current = node;
738
+ while(tsMorph.Node.isParenthesizedTypeNode(current)){
739
+ current = current.getTypeNode();
740
+ }
741
+ var result;
742
+ if (tsMorph.Node.isTypeReference(current)) {
743
+ var name = current.getTypeName().getText();
744
+ if (PASSTHROUGH_TYPE_WRAPPERS.has(name)) {
745
+ var inner = current.getTypeArguments();
746
+ if (inner.length > 0) {
747
+ result = peelTypeNode(inner[0]);
748
+ }
749
+ } else {
750
+ result = name;
751
+ }
752
+ }
753
+ return result;
754
+ }
696
755
  function readConverters(sourceFile) {
697
756
  var out = [];
698
757
  var _iteratorNormalCompletion = true, _didIteratorError = false, _iteratorError = undefined;
@@ -482,6 +482,25 @@ function _object_spread(target) {
482
482
  }
483
483
  return target;
484
484
  }
485
+ /**
486
+ * TS utility/structural wrappers that don't change the field surface for
487
+ * inheritance walks — `Partial<T>`, `Required<T>`, `Readonly<T>`,
488
+ * `NonNullable<T>` preserve every property, and `Pick<T, K>` / `Omit<T, K>`
489
+ * leave the original `T` reachable for long-name resolution. `MaybeMap<T>` is
490
+ * the workspace's own pass-through that decorates each prop with `Maybe<…>`
491
+ * without renaming. `extends` walks need to see through these to find the
492
+ * concrete ancestor interface — `getExpression()` alone returns just the
493
+ * leftmost identifier (`Partial`, `Pick`, …) and silently drops the inner
494
+ * model, leaving every inherited `@dbxModelVariable` tag unreachable.
495
+ */ var PASSTHROUGH_TYPE_WRAPPERS = new Set([
496
+ 'Partial',
497
+ 'Required',
498
+ 'Readonly',
499
+ 'NonNullable',
500
+ 'MaybeMap',
501
+ 'Pick',
502
+ 'Omit'
503
+ ]);
485
504
  var IDENTITY_FN = 'firestoreModelIdentity';
486
505
  var CONVERTER_FN_NAMES = [
487
506
  'snapshotConverterFunctions',
@@ -645,9 +664,7 @@ function readInterfaces(sourceFile) {
645
664
  function buildInterface(decl) {
646
665
  var jsDocs = decl.getJsDocs();
647
666
  var hasDbxModelTag = jsDocsHaveTag(jsDocs, 'dbxModel');
648
- var extendsNames = decl.getExtends().map(function(e) {
649
- return e.getExpression().getText();
650
- });
667
+ var extendsNames = decl.getExtends().map(resolveExtendsName);
651
668
  var props = [];
652
669
  var _iteratorNormalCompletion = true, _didIteratorError = false, _iteratorError = undefined;
653
670
  try {
@@ -691,6 +708,48 @@ function buildInterface(decl) {
691
708
  props: props
692
709
  };
693
710
  }
711
+ /**
712
+ * Resolves an `extends` clause to the concrete ancestor interface name,
713
+ * peeling any leading {@link PASSTHROUGH_TYPE_WRAPPERS}. Returns the leftmost
714
+ * identifier of the unwrapped expression so the inheritance walker can chain
715
+ * through utility-wrapped declarations like
716
+ * `extends Partial<MaybeMap<Omit<Base, '…'>>>`.
717
+ *
718
+ * @param expr - the `ExpressionWithTypeArguments` produced by `getExtends()`
719
+ * @returns the resolved interface name, or the original leftmost identifier when no inner reference is reachable
720
+ */ function resolveExtendsName(expr) {
721
+ var head = expr.getExpression().getText();
722
+ var result = head;
723
+ if (PASSTHROUGH_TYPE_WRAPPERS.has(head)) {
724
+ var typeArgs = expr.getTypeArguments();
725
+ if (typeArgs.length > 0) {
726
+ var peeled = peelTypeNode(typeArgs[0]);
727
+ if (peeled !== undefined) {
728
+ result = peeled;
729
+ }
730
+ }
731
+ }
732
+ return result;
733
+ }
734
+ function peelTypeNode(node) {
735
+ var current = node;
736
+ while(Node.isParenthesizedTypeNode(current)){
737
+ current = current.getTypeNode();
738
+ }
739
+ var result;
740
+ if (Node.isTypeReference(current)) {
741
+ var name = current.getTypeName().getText();
742
+ if (PASSTHROUGH_TYPE_WRAPPERS.has(name)) {
743
+ var inner = current.getTypeArguments();
744
+ if (inner.length > 0) {
745
+ result = peelTypeNode(inner[0]);
746
+ }
747
+ } else {
748
+ result = name;
749
+ }
750
+ }
751
+ return result;
752
+ }
694
753
  function readConverters(sourceFile) {
695
754
  var out = [];
696
755
  var _iteratorNormalCompletion = true, _didIteratorError = false, _iteratorError = undefined;
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@dereekb/dbx-cli/manifest-extract",
3
- "version": "13.11.5",
3
+ "version": "13.11.7",
4
4
  "sideEffects": false,
5
5
  "peerDependencies": {
6
6
  "ts-morph": "^21.0.0"
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@dereekb/dbx-cli",
3
- "version": "13.11.5",
3
+ "version": "13.11.7",
4
4
  "sideEffects": false,
5
5
  "bin": {
6
6
  "dbx-cli-generate-firebase-api-manifest": "firebase-api-manifest/main.js"
@@ -24,10 +24,10 @@
24
24
  }
25
25
  },
26
26
  "peerDependencies": {
27
- "@dereekb/date": "13.11.5",
28
- "@dereekb/firebase": "13.11.5",
29
- "@dereekb/nestjs": "13.11.5",
30
- "@dereekb/util": "13.11.5",
27
+ "@dereekb/date": "13.11.7",
28
+ "@dereekb/firebase": "13.11.7",
29
+ "@dereekb/nestjs": "13.11.7",
30
+ "@dereekb/util": "13.11.7",
31
31
  "arktype": "^2.2.0",
32
32
  "yargs": "^18.0.0"
33
33
  },
@@ -11,6 +11,12 @@ export declare const DEFAULT_CLI_OIDC_SCOPES = "openid profile email";
11
11
  * CLI doesn't take a server-side dependency just to know the names.
12
12
  */
13
13
  export declare const MODEL_WRITE_OIDC_SCOPES: readonly ["model.create", "model.update", "model.delete"];
14
+ /**
15
+ * The default redirect URI used by the CLI.
16
+ *
17
+ * Opens up to nothing in the browser so the user can copy/paste the resulting token url back into the CLI.
18
+ */
19
+ export declare const DEFAULT_CLI_REDIRECT_URI = "http://127.0.0.1:0/callback";
14
20
  /**
15
21
  * Returns the input scope string with the `model.create`, `model.update`, and `model.delete`
16
22
  * scopes removed, preserving every other scope (including `model.read` and `model.query`).
@@ -119,6 +125,8 @@ export interface CliEnvConfig {
119
125
  * The redirect URI registered with the OAuth client. The CLI does not bind a server — it parses
120
126
  * the URL the user pastes back, so this can be any value the OIDC provider accepts as a
121
127
  * registered redirect URI (e.g. `http://127.0.0.1:0/callback` or another loopback/placeholder URL).
128
+ *
129
+ * Defaults to {@link DEFAULT_CLI_REDIRECT_URI}.
122
130
  */
123
131
  readonly redirectUri?: string;
124
132
  /**
@@ -22,8 +22,12 @@ export interface CliModelField {
22
22
  /**
23
23
  * Verbatim converter expression text from the converter's `fields` literal
24
24
  * (e.g. `firestoreDate()`, `firestoreObjectArray({ objectField: foo })`).
25
+ *
26
+ * Opt-in. Only emitted when the manifest is generated with
27
+ * `--emit-model-converters`; downstream tooling (e.g. the dbx-components MCP)
28
+ * uses this text, while the CLI itself does not need it.
25
29
  */
26
- readonly converter: string;
30
+ readonly converter?: string;
27
31
  /**
28
32
  * TypeScript type text from the interface property declaration, when the
29
33
  * field's interface property could be located (e.g. `Date`, `Maybe<NotificationUserState>`).