@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.
- package/eslint/index.cjs.default.js +1 -0
- package/eslint/index.cjs.js +1050 -0
- package/eslint/index.cjs.mjs +2 -0
- package/eslint/index.d.ts +1 -0
- package/eslint/index.esm.js +1046 -0
- package/eslint/package.json +25 -0
- package/eslint/rollup.alias-internal.config.d.ts +11 -0
- package/eslint/src/index.d.ts +1 -0
- package/eslint/src/lib/index.d.ts +2 -0
- package/eslint/src/lib/plugin.d.ts +22 -0
- package/eslint/src/lib/valid-dbx-route-model-tags.rule.d.ts +59 -0
- package/firebase-api-manifest/main.js +318 -228
- package/firebase-api-manifest/package.json +3 -3
- package/generate-firestore-indexes/main.js +37 -24
- package/generate-firestore-indexes/package.json +2 -2
- package/generate-mcp-manifest/main.js +57 -39
- package/generate-mcp-manifest/package.json +3 -3
- package/generate-route-manifest/main.js +1137 -0
- package/generate-route-manifest/package.json +10 -0
- package/index.cjs.js +4847 -1953
- package/index.esm.js +4827 -1954
- package/lint-cache/package.json +2 -2
- package/manifest-extract/index.cjs.js +175 -240
- package/manifest-extract/index.esm.js +174 -239
- package/manifest-extract/package.json +9 -4
- package/package.json +16 -6
- package/src/lib/index.d.ts +2 -0
- package/src/lib/manifest/types.d.ts +53 -0
- package/src/lib/mcp-scan/manifest/package-root.d.ts +17 -0
- package/src/lib/mcp-scan/manifest/tokens-schema.d.ts +5 -4
- package/src/lib/mcp-scan/scan/extract-models/assemble.d.ts +17 -0
- package/src/lib/route/component-resolve.d.ts +48 -0
- package/src/lib/route/index.d.ts +18 -0
- package/src/lib/route/route-build-tree.d.ts +31 -0
- package/src/lib/route/route-extract.d.ts +46 -0
- package/src/lib/route/route-load-tree.d.ts +17 -0
- package/src/lib/route/route-manifest.d.ts +132 -0
- package/src/lib/route/route-model-tag.d.ts +89 -0
- package/src/lib/route/route-models-extract.d.ts +22 -0
- package/src/lib/route/route-resolve-sources.d.ts +39 -0
- package/src/lib/route/route-types.d.ts +136 -0
- package/src/lib/route/url-match.d.ts +116 -0
- package/src/lib/scan-helpers/firestore-model-extract-utils.d.ts +43 -0
- package/test/index.cjs.js +1 -1
- package/test/index.esm.js +1 -1
- package/test/package.json +9 -9
|
@@ -284,29 +284,20 @@ function findFunctionsClassName(sourceFile) {
|
|
|
284
284
|
return result;
|
|
285
285
|
}
|
|
286
286
|
function inferGroupName(sourceFile) {
|
|
287
|
+
return findTypeAliasStem(sourceFile, "ModelCrudFunctionsConfig") ?? findTypeAliasStem(sourceFile, "FunctionTypeMap");
|
|
288
|
+
}
|
|
289
|
+
function findTypeAliasStem(sourceFile, ending) {
|
|
287
290
|
let result;
|
|
288
291
|
for (const alias of sourceFile.getTypeAliases()) {
|
|
289
292
|
const name = alias.getName();
|
|
290
|
-
if (name.endsWith(
|
|
291
|
-
const stem = name.slice(0, -
|
|
293
|
+
if (name.endsWith(ending)) {
|
|
294
|
+
const stem = name.slice(0, -ending.length);
|
|
292
295
|
if (stem.length > 0) {
|
|
293
296
|
result = stem;
|
|
294
297
|
break;
|
|
295
298
|
}
|
|
296
299
|
}
|
|
297
300
|
}
|
|
298
|
-
if (result === void 0) {
|
|
299
|
-
for (const alias of sourceFile.getTypeAliases()) {
|
|
300
|
-
const name = alias.getName();
|
|
301
|
-
if (name.endsWith("FunctionTypeMap")) {
|
|
302
|
-
const stem = name.slice(0, -"FunctionTypeMap".length);
|
|
303
|
-
if (stem.length > 0) {
|
|
304
|
-
result = stem;
|
|
305
|
-
break;
|
|
306
|
-
}
|
|
307
|
-
}
|
|
308
|
-
}
|
|
309
|
-
}
|
|
310
301
|
return result;
|
|
311
302
|
}
|
|
312
303
|
function isNullLiteralType(node) {
|
|
@@ -402,44 +393,44 @@ function typeNodeName(node) {
|
|
|
402
393
|
return result;
|
|
403
394
|
}
|
|
404
395
|
function readTypeDocs(sourceFile, typeName) {
|
|
405
|
-
let result;
|
|
406
396
|
const interfaceDecl = sourceFile.getInterface(typeName);
|
|
397
|
+
let result;
|
|
407
398
|
if (interfaceDecl) {
|
|
408
|
-
|
|
409
|
-
const hasApiParamsTag = hasJsDocFlag(interfaceDecl, "dbxModelApiParams");
|
|
410
|
-
const fields = [];
|
|
411
|
-
for (const property of interfaceDecl.getProperties()) {
|
|
412
|
-
const fieldName = property.getName();
|
|
413
|
-
const description = readJsDocSummary(property);
|
|
414
|
-
const typeNode = property.getTypeNode();
|
|
415
|
-
const typeText = typeNode?.getText().trim() ?? "";
|
|
416
|
-
const adminOnly = hasJsDocFlag(property, "dbxModelApiAdminOnly");
|
|
417
|
-
const field = {
|
|
418
|
-
name: fieldName,
|
|
419
|
-
typeText,
|
|
420
|
-
...description ? { description } : {},
|
|
421
|
-
...adminOnly ? { accessLevel: "adminOnly" } : {}
|
|
422
|
-
};
|
|
423
|
-
fields.push(field);
|
|
424
|
-
}
|
|
425
|
-
if (typeDescription || fields.length > 0 || hasApiParamsTag) {
|
|
426
|
-
result = {
|
|
427
|
-
...typeDescription ? { typeDescription } : {},
|
|
428
|
-
...fields.length > 0 ? { fields } : {},
|
|
429
|
-
hasApiParamsTag
|
|
430
|
-
};
|
|
431
|
-
}
|
|
399
|
+
result = readInterfaceTypeDocs(interfaceDecl);
|
|
432
400
|
} else {
|
|
433
401
|
const typeAlias = sourceFile.getTypeAlias(typeName);
|
|
434
|
-
|
|
435
|
-
|
|
436
|
-
|
|
437
|
-
|
|
438
|
-
|
|
439
|
-
|
|
402
|
+
result = typeAlias ? readTypeAliasDocs(typeAlias) : void 0;
|
|
403
|
+
}
|
|
404
|
+
return result;
|
|
405
|
+
}
|
|
406
|
+
function readInterfaceTypeDocs(interfaceDecl) {
|
|
407
|
+
const typeDescription = readJsDocSummary(interfaceDecl);
|
|
408
|
+
const hasApiParamsTag = hasJsDocFlag(interfaceDecl, "dbxModelApiParams");
|
|
409
|
+
const fields = interfaceDecl.getProperties().map((property) => readInterfaceField(property));
|
|
410
|
+
let result;
|
|
411
|
+
if (typeDescription || fields.length > 0 || hasApiParamsTag) {
|
|
412
|
+
result = {
|
|
413
|
+
...typeDescription ? { typeDescription } : {},
|
|
414
|
+
...fields.length > 0 ? { fields } : {},
|
|
415
|
+
hasApiParamsTag
|
|
416
|
+
};
|
|
440
417
|
}
|
|
441
418
|
return result;
|
|
442
419
|
}
|
|
420
|
+
function readInterfaceField(property) {
|
|
421
|
+
const description = readJsDocSummary(property);
|
|
422
|
+
const adminOnly = hasJsDocFlag(property, "dbxModelApiAdminOnly");
|
|
423
|
+
return {
|
|
424
|
+
name: property.getName(),
|
|
425
|
+
typeText: property.getTypeNode()?.getText().trim() ?? "",
|
|
426
|
+
...description ? { description } : {},
|
|
427
|
+
...adminOnly ? { accessLevel: "adminOnly" } : {}
|
|
428
|
+
};
|
|
429
|
+
}
|
|
430
|
+
function readTypeAliasDocs(typeAlias) {
|
|
431
|
+
const typeDescription = readJsDocSummary(typeAlias);
|
|
432
|
+
return typeDescription ? { typeDescription } : void 0;
|
|
433
|
+
}
|
|
443
434
|
function hasJsDocFlag(node, tagName) {
|
|
444
435
|
let result = false;
|
|
445
436
|
for (const doc of node.getJsDocs()) {
|
|
@@ -468,8 +459,8 @@ function readJsDocTagValue(node, tagName) {
|
|
|
468
459
|
function readJsDocSummary(node) {
|
|
469
460
|
let result;
|
|
470
461
|
const docs = node.getJsDocs();
|
|
471
|
-
|
|
472
|
-
|
|
462
|
+
const last = docs.at(-1);
|
|
463
|
+
if (last) {
|
|
473
464
|
const description = last.getDescription().trim();
|
|
474
465
|
if (description.length > 0) {
|
|
475
466
|
result = description;
|
|
@@ -478,14 +469,92 @@ function readJsDocSummary(node) {
|
|
|
478
469
|
return result;
|
|
479
470
|
}
|
|
480
471
|
|
|
472
|
+
// packages/util/src/lib/string/sort.ts
|
|
473
|
+
var compareStrings = (a, b) => a.localeCompare(b);
|
|
474
|
+
|
|
475
|
+
// packages/dbx-cli/src/lib/scan-helpers/firestore-model-extract-utils.ts
|
|
476
|
+
import { Node as Node3 } from "ts-morph";
|
|
477
|
+
var PASSTHROUGH_TYPE_WRAPPERS = /* @__PURE__ */ new Set(["Partial", "Required", "Readonly", "NonNullable", "MaybeMap", "Pick", "Omit"]);
|
|
478
|
+
function parseFirestoreModelIdentityArgs(args) {
|
|
479
|
+
let result;
|
|
480
|
+
if (args.length === 1) {
|
|
481
|
+
const modelType = stringLiteralValue(args[0]);
|
|
482
|
+
if (modelType !== void 0) {
|
|
483
|
+
result = { modelType, collectionPrefix: void 0, parentIdentityConst: void 0 };
|
|
484
|
+
}
|
|
485
|
+
} else if (args.length === 2) {
|
|
486
|
+
const first = stringLiteralValue(args[0]);
|
|
487
|
+
if (first === void 0) {
|
|
488
|
+
const modelType = stringLiteralValue(args[1]);
|
|
489
|
+
if (modelType !== void 0) {
|
|
490
|
+
result = { modelType, collectionPrefix: void 0, parentIdentityConst: identifierName(args[0]) };
|
|
491
|
+
}
|
|
492
|
+
} else {
|
|
493
|
+
result = { modelType: first, collectionPrefix: stringLiteralValue(args[1]), parentIdentityConst: void 0 };
|
|
494
|
+
}
|
|
495
|
+
} else if (args.length >= 3) {
|
|
496
|
+
const modelType = stringLiteralValue(args[1]);
|
|
497
|
+
if (modelType !== void 0) {
|
|
498
|
+
result = { modelType, collectionPrefix: stringLiteralValue(args[2]), parentIdentityConst: identifierName(args[0]) };
|
|
499
|
+
}
|
|
500
|
+
}
|
|
501
|
+
return result;
|
|
502
|
+
}
|
|
503
|
+
function resolveExtendsName(expr) {
|
|
504
|
+
const head = expr.getExpression().getText();
|
|
505
|
+
let result = head;
|
|
506
|
+
if (PASSTHROUGH_TYPE_WRAPPERS.has(head)) {
|
|
507
|
+
const typeArgs = expr.getTypeArguments();
|
|
508
|
+
if (typeArgs.length > 0) {
|
|
509
|
+
const peeled = peelTypeNode(typeArgs[0]);
|
|
510
|
+
if (peeled !== void 0) {
|
|
511
|
+
result = peeled;
|
|
512
|
+
}
|
|
513
|
+
}
|
|
514
|
+
}
|
|
515
|
+
return result;
|
|
516
|
+
}
|
|
517
|
+
function peelTypeNode(node) {
|
|
518
|
+
let current = node;
|
|
519
|
+
while (Node3.isParenthesizedTypeNode(current)) {
|
|
520
|
+
current = current.getTypeNode();
|
|
521
|
+
}
|
|
522
|
+
let result;
|
|
523
|
+
if (Node3.isTypeReference(current)) {
|
|
524
|
+
const name = current.getTypeName().getText();
|
|
525
|
+
if (PASSTHROUGH_TYPE_WRAPPERS.has(name)) {
|
|
526
|
+
const inner = current.getTypeArguments();
|
|
527
|
+
if (inner.length > 0) {
|
|
528
|
+
result = peelTypeNode(inner[0]);
|
|
529
|
+
}
|
|
530
|
+
} else {
|
|
531
|
+
result = name;
|
|
532
|
+
}
|
|
533
|
+
}
|
|
534
|
+
return result;
|
|
535
|
+
}
|
|
536
|
+
function stringLiteralValue(node) {
|
|
537
|
+
let result;
|
|
538
|
+
if (Node3.isStringLiteral(node) || Node3.isNoSubstitutionTemplateLiteral(node)) {
|
|
539
|
+
result = node.getLiteralText();
|
|
540
|
+
}
|
|
541
|
+
return result;
|
|
542
|
+
}
|
|
543
|
+
function identifierName(node) {
|
|
544
|
+
let result;
|
|
545
|
+
if (Node3.isIdentifier(node)) {
|
|
546
|
+
result = node.getText();
|
|
547
|
+
}
|
|
548
|
+
return result;
|
|
549
|
+
}
|
|
550
|
+
|
|
481
551
|
// packages/dbx-cli/manifest-extract/src/lib/extract-models.ts
|
|
482
|
-
import { Node as
|
|
552
|
+
import { Node as Node4, Project as Project3 } from "ts-morph";
|
|
483
553
|
var READ_LEVEL_VALUES = /* @__PURE__ */ new Set(["system", "owner", "admin-only", "permissions"]);
|
|
484
554
|
var SERVICE_FACTORY_TAG = "dbxModelServiceFactory";
|
|
485
555
|
var MCP_TOOL_NAME_SEGMENT_TAG = "dbxModelMcpToolNameSegment";
|
|
486
556
|
var MODEL_TYPE_VALUE_PATTERN = /^[a-z][A-Za-z0-9_$]*$/;
|
|
487
557
|
var TOOL_NAME_SEGMENT_PATTERN = /^[A-Za-z][A-Za-z0-9_$]*$/;
|
|
488
|
-
var PASSTHROUGH_TYPE_WRAPPERS = /* @__PURE__ */ new Set(["Partial", "Required", "Readonly", "NonNullable", "MaybeMap", "Pick", "Omit"]);
|
|
489
558
|
var IDENTITY_FN = "firestoreModelIdentity";
|
|
490
559
|
var CONVERTER_FN_NAMES = ["snapshotConverterFunctions", "firestoreSubObject", "firestoreObjectArray"];
|
|
491
560
|
var SUB_OBJECT_FN = "firestoreSubObject";
|
|
@@ -493,6 +562,7 @@ var OBJECT_ARRAY_FN = "firestoreObjectArray";
|
|
|
493
562
|
var SNAPSHOT_FN = "snapshotConverterFunctions";
|
|
494
563
|
var FIELDS_LITERAL_KEY = "fields";
|
|
495
564
|
var OBJECT_FIELD_KEY = "objectField";
|
|
565
|
+
var FIRESTORE_FIELD_KEY = "firestoreField";
|
|
496
566
|
function extractModelsFromSource(input) {
|
|
497
567
|
const project = new Project3({ useInMemoryFileSystem: true, skipAddingFilesFromTsConfig: true });
|
|
498
568
|
const sourceFile = project.createSourceFile(input.name, input.text, { overwrite: true });
|
|
@@ -510,9 +580,9 @@ function readIdentities(sourceFile) {
|
|
|
510
580
|
if (!statement.isExported()) continue;
|
|
511
581
|
for (const decl of statement.getDeclarations()) {
|
|
512
582
|
const initializer = decl.getInitializer();
|
|
513
|
-
if (!initializer || !
|
|
583
|
+
if (!initializer || !Node4.isCallExpression(initializer)) continue;
|
|
514
584
|
if (initializer.getExpression().getText() !== IDENTITY_FN) continue;
|
|
515
|
-
const parsed =
|
|
585
|
+
const parsed = parseFirestoreModelIdentityArgs(initializer.getArguments());
|
|
516
586
|
if (parsed) {
|
|
517
587
|
out.push({ identityConst: decl.getName(), ...parsed });
|
|
518
588
|
}
|
|
@@ -520,32 +590,6 @@ function readIdentities(sourceFile) {
|
|
|
520
590
|
}
|
|
521
591
|
return out;
|
|
522
592
|
}
|
|
523
|
-
function parseIdentityArgs(call) {
|
|
524
|
-
const args = call.getArguments();
|
|
525
|
-
let result;
|
|
526
|
-
if (args.length === 1) {
|
|
527
|
-
const modelType = stringLiteralValue(args[0]);
|
|
528
|
-
if (modelType !== void 0) {
|
|
529
|
-
result = { modelType, collectionPrefix: void 0, parentIdentityConst: void 0 };
|
|
530
|
-
}
|
|
531
|
-
} else if (args.length === 2) {
|
|
532
|
-
const first = stringLiteralValue(args[0]);
|
|
533
|
-
if (first === void 0) {
|
|
534
|
-
const modelType = stringLiteralValue(args[1]);
|
|
535
|
-
if (modelType !== void 0) {
|
|
536
|
-
result = { modelType, collectionPrefix: void 0, parentIdentityConst: identifierName(args[0]) };
|
|
537
|
-
}
|
|
538
|
-
} else {
|
|
539
|
-
result = { modelType: first, collectionPrefix: stringLiteralValue(args[1]), parentIdentityConst: void 0 };
|
|
540
|
-
}
|
|
541
|
-
} else if (args.length >= 3) {
|
|
542
|
-
const modelType = stringLiteralValue(args[1]);
|
|
543
|
-
if (modelType !== void 0) {
|
|
544
|
-
result = { modelType, collectionPrefix: stringLiteralValue(args[2]), parentIdentityConst: identifierName(args[0]) };
|
|
545
|
-
}
|
|
546
|
-
}
|
|
547
|
-
return result;
|
|
548
|
-
}
|
|
549
593
|
function readInterfaces(sourceFile) {
|
|
550
594
|
const out = [];
|
|
551
595
|
for (const decl of sourceFile.getInterfaces()) {
|
|
@@ -646,46 +690,13 @@ function readServiceFactoryModelType(jsDocs) {
|
|
|
646
690
|
}
|
|
647
691
|
return result;
|
|
648
692
|
}
|
|
649
|
-
function resolveExtendsName(expr) {
|
|
650
|
-
const head = expr.getExpression().getText();
|
|
651
|
-
let result = head;
|
|
652
|
-
if (PASSTHROUGH_TYPE_WRAPPERS.has(head)) {
|
|
653
|
-
const typeArgs = expr.getTypeArguments();
|
|
654
|
-
if (typeArgs.length > 0) {
|
|
655
|
-
const peeled = peelTypeNode(typeArgs[0]);
|
|
656
|
-
if (peeled !== void 0) {
|
|
657
|
-
result = peeled;
|
|
658
|
-
}
|
|
659
|
-
}
|
|
660
|
-
}
|
|
661
|
-
return result;
|
|
662
|
-
}
|
|
663
|
-
function peelTypeNode(node) {
|
|
664
|
-
let current = node;
|
|
665
|
-
while (Node3.isParenthesizedTypeNode(current)) {
|
|
666
|
-
current = current.getTypeNode();
|
|
667
|
-
}
|
|
668
|
-
let result;
|
|
669
|
-
if (Node3.isTypeReference(current)) {
|
|
670
|
-
const name = current.getTypeName().getText();
|
|
671
|
-
if (PASSTHROUGH_TYPE_WRAPPERS.has(name)) {
|
|
672
|
-
const inner = current.getTypeArguments();
|
|
673
|
-
if (inner.length > 0) {
|
|
674
|
-
result = peelTypeNode(inner[0]);
|
|
675
|
-
}
|
|
676
|
-
} else {
|
|
677
|
-
result = name;
|
|
678
|
-
}
|
|
679
|
-
}
|
|
680
|
-
return result;
|
|
681
|
-
}
|
|
682
693
|
function readConverters(sourceFile) {
|
|
683
694
|
const out = [];
|
|
684
695
|
for (const statement of sourceFile.getVariableStatements()) {
|
|
685
696
|
if (!statement.isExported()) continue;
|
|
686
697
|
for (const decl of statement.getDeclarations()) {
|
|
687
698
|
const initializer = decl.getInitializer();
|
|
688
|
-
if (!initializer || !
|
|
699
|
+
if (!initializer || !Node4.isCallExpression(initializer)) continue;
|
|
689
700
|
const factory = initializer.getExpression().getText();
|
|
690
701
|
if (!isConverterFactoryName(factory)) continue;
|
|
691
702
|
const interfaceName = readGenericInterfaceName(initializer);
|
|
@@ -719,13 +730,13 @@ function readConverterFields(call) {
|
|
|
719
730
|
let result;
|
|
720
731
|
if (args.length > 0) {
|
|
721
732
|
const config = args[0];
|
|
722
|
-
if (
|
|
733
|
+
if (Node4.isObjectLiteralExpression(config)) {
|
|
723
734
|
let fieldsLiteral;
|
|
724
735
|
if (fnName === SNAPSHOT_FN) {
|
|
725
736
|
fieldsLiteral = readObjectProperty(config, FIELDS_LITERAL_KEY);
|
|
726
737
|
} else {
|
|
727
738
|
const objectField = readPropertyValue(config, OBJECT_FIELD_KEY);
|
|
728
|
-
if (objectField &&
|
|
739
|
+
if (objectField && Node4.isObjectLiteralExpression(objectField)) {
|
|
729
740
|
fieldsLiteral = readObjectProperty(objectField, FIELDS_LITERAL_KEY);
|
|
730
741
|
}
|
|
731
742
|
}
|
|
@@ -739,7 +750,7 @@ function readConverterFields(call) {
|
|
|
739
750
|
function readFieldEntries(fields) {
|
|
740
751
|
const out = [];
|
|
741
752
|
for (const property of fields.getProperties()) {
|
|
742
|
-
if (
|
|
753
|
+
if (Node4.isPropertyAssignment(property)) {
|
|
743
754
|
const initializer = property.getInitializer();
|
|
744
755
|
const converterText = initializer ? initializer.getText().replaceAll(/\s+/g, " ").trim() : "";
|
|
745
756
|
const nested = initializer ? readNestedFromExpression(initializer) : void 0;
|
|
@@ -750,7 +761,7 @@ function readFieldEntries(fields) {
|
|
|
750
761
|
nestedConverterInline: nested?.inline,
|
|
751
762
|
nestedIsArray: nested?.isArray
|
|
752
763
|
});
|
|
753
|
-
} else if (
|
|
764
|
+
} else if (Node4.isShorthandPropertyAssignment(property)) {
|
|
754
765
|
const name = property.getName();
|
|
755
766
|
out.push({ key: name, converter: name });
|
|
756
767
|
}
|
|
@@ -759,54 +770,69 @@ function readFieldEntries(fields) {
|
|
|
759
770
|
}
|
|
760
771
|
function readNestedFromExpression(expr) {
|
|
761
772
|
let result;
|
|
762
|
-
if (
|
|
773
|
+
if (Node4.isCallExpression(expr)) {
|
|
763
774
|
const fnName = expr.getExpression().getText();
|
|
764
775
|
if (fnName === SUB_OBJECT_FN || fnName === OBJECT_ARRAY_FN) {
|
|
765
|
-
|
|
766
|
-
|
|
767
|
-
|
|
768
|
-
|
|
769
|
-
|
|
770
|
-
|
|
771
|
-
|
|
772
|
-
|
|
773
|
-
|
|
774
|
-
|
|
775
|
-
|
|
776
|
-
|
|
777
|
-
|
|
778
|
-
|
|
779
|
-
inline: {
|
|
780
|
-
converterConst: void 0,
|
|
781
|
-
factory: fnName,
|
|
782
|
-
interfaceName: readGenericInterfaceName(expr),
|
|
783
|
-
fields: inlineFields,
|
|
784
|
-
line: expr.getStartLineNumber()
|
|
785
|
-
},
|
|
786
|
-
isArray
|
|
787
|
-
};
|
|
788
|
-
}
|
|
789
|
-
}
|
|
790
|
-
}
|
|
791
|
-
}
|
|
776
|
+
result = readNestedConverterCall(expr, fnName);
|
|
777
|
+
}
|
|
778
|
+
}
|
|
779
|
+
return result;
|
|
780
|
+
}
|
|
781
|
+
function readNestedConverterCall(call, fnName) {
|
|
782
|
+
let result;
|
|
783
|
+
const args = call.getArguments();
|
|
784
|
+
if (args.length > 0) {
|
|
785
|
+
const config = args[0];
|
|
786
|
+
if (Node4.isObjectLiteralExpression(config)) {
|
|
787
|
+
const valueNode = readPropertyValue(config, OBJECT_FIELD_KEY) ?? readPropertyValue(config, FIRESTORE_FIELD_KEY);
|
|
788
|
+
if (valueNode) {
|
|
789
|
+
result = buildNestedConverterMatch({ objectField: valueNode, call, fnName });
|
|
792
790
|
}
|
|
793
791
|
}
|
|
794
792
|
}
|
|
795
793
|
return result;
|
|
796
794
|
}
|
|
795
|
+
function buildNestedConverterMatch(input) {
|
|
796
|
+
const { objectField, call, fnName } = input;
|
|
797
|
+
const isArray = fnName === OBJECT_ARRAY_FN;
|
|
798
|
+
let result;
|
|
799
|
+
if (Node4.isIdentifier(objectField)) {
|
|
800
|
+
result = { ref: objectField.getText(), isArray };
|
|
801
|
+
} else if (Node4.isObjectLiteralExpression(objectField)) {
|
|
802
|
+
const fieldsLiteral = readObjectProperty(objectField, FIELDS_LITERAL_KEY);
|
|
803
|
+
if (fieldsLiteral) {
|
|
804
|
+
result = {
|
|
805
|
+
inline: {
|
|
806
|
+
converterConst: void 0,
|
|
807
|
+
factory: fnName,
|
|
808
|
+
interfaceName: readGenericInterfaceName(call),
|
|
809
|
+
fields: readFieldEntries(fieldsLiteral),
|
|
810
|
+
line: call.getStartLineNumber()
|
|
811
|
+
},
|
|
812
|
+
isArray
|
|
813
|
+
};
|
|
814
|
+
}
|
|
815
|
+
} else if (Node4.isCallExpression(objectField)) {
|
|
816
|
+
const nested = readNestedFromExpression(objectField);
|
|
817
|
+
if (nested) {
|
|
818
|
+
result = { ...nested, isArray };
|
|
819
|
+
}
|
|
820
|
+
}
|
|
821
|
+
return result;
|
|
822
|
+
}
|
|
797
823
|
function readPropertyValue(literal, key) {
|
|
798
824
|
const property = literal.getProperty(key);
|
|
799
825
|
let result;
|
|
800
|
-
if (property &&
|
|
826
|
+
if (property && Node4.isPropertyAssignment(property)) {
|
|
801
827
|
result = property.getInitializer();
|
|
802
|
-
} else if (property &&
|
|
828
|
+
} else if (property && Node4.isShorthandPropertyAssignment(property)) {
|
|
803
829
|
result = property.getNameNode();
|
|
804
830
|
}
|
|
805
831
|
return result;
|
|
806
832
|
}
|
|
807
833
|
function readObjectProperty(literal, key) {
|
|
808
834
|
const value = readPropertyValue(literal, key);
|
|
809
|
-
return value &&
|
|
835
|
+
return value && Node4.isObjectLiteralExpression(value) ? value : void 0;
|
|
810
836
|
}
|
|
811
837
|
function readEnums(sourceFile) {
|
|
812
838
|
const out = [];
|
|
@@ -905,20 +931,6 @@ function firstParagraph(text) {
|
|
|
905
931
|
}
|
|
906
932
|
return collected.join(" ").trim();
|
|
907
933
|
}
|
|
908
|
-
function stringLiteralValue(node) {
|
|
909
|
-
let result;
|
|
910
|
-
if (Node3.isStringLiteral(node) || Node3.isNoSubstitutionTemplateLiteral(node)) {
|
|
911
|
-
result = node.getLiteralText();
|
|
912
|
-
}
|
|
913
|
-
return result;
|
|
914
|
-
}
|
|
915
|
-
function identifierName(node) {
|
|
916
|
-
let result;
|
|
917
|
-
if (Node3.isIdentifier(node)) {
|
|
918
|
-
result = node.getText();
|
|
919
|
-
}
|
|
920
|
-
return result;
|
|
921
|
-
}
|
|
922
934
|
|
|
923
935
|
// packages/dbx-cli/firebase-api-manifest/src/generate-api-manifest/find-api-files.ts
|
|
924
936
|
function findApiFiles(packageRoot) {
|
|
@@ -1021,6 +1033,47 @@ function assembleModels(input) {
|
|
|
1021
1033
|
accumulator.entries.sort((a, b) => a.modelType.localeCompare(b.modelType));
|
|
1022
1034
|
return accumulator.entries;
|
|
1023
1035
|
}
|
|
1036
|
+
function collectModelEnums(input) {
|
|
1037
|
+
const registry = buildEnumRegistry(input.extractions);
|
|
1038
|
+
const referenced = collectReferencedEnumNames(input.models);
|
|
1039
|
+
const out = {};
|
|
1040
|
+
for (const name of [...referenced].sort((a, b) => a.localeCompare(b))) {
|
|
1041
|
+
const extracted = registry.get(name);
|
|
1042
|
+
if (extracted) {
|
|
1043
|
+
out[name] = buildModelEnumEntry(extracted);
|
|
1044
|
+
}
|
|
1045
|
+
}
|
|
1046
|
+
return out;
|
|
1047
|
+
}
|
|
1048
|
+
function buildEnumRegistry(extractions) {
|
|
1049
|
+
const registry = /* @__PURE__ */ new Map();
|
|
1050
|
+
for (const { extraction } of extractions) {
|
|
1051
|
+
for (const e of extraction.enums) {
|
|
1052
|
+
if (!registry.has(e.name)) registry.set(e.name, e);
|
|
1053
|
+
}
|
|
1054
|
+
}
|
|
1055
|
+
return registry;
|
|
1056
|
+
}
|
|
1057
|
+
function collectReferencedEnumNames(models) {
|
|
1058
|
+
const referenced = /* @__PURE__ */ new Set();
|
|
1059
|
+
for (const model of models) {
|
|
1060
|
+
addReferencedEnumNames(model.fields, referenced);
|
|
1061
|
+
}
|
|
1062
|
+
return referenced;
|
|
1063
|
+
}
|
|
1064
|
+
function addReferencedEnumNames(fields, referenced) {
|
|
1065
|
+
for (const field of fields) {
|
|
1066
|
+
if (field.enumRef) referenced.add(field.enumRef);
|
|
1067
|
+
if (field.nestedFields) addReferencedEnumNames(field.nestedFields, referenced);
|
|
1068
|
+
}
|
|
1069
|
+
}
|
|
1070
|
+
function buildModelEnumEntry(e) {
|
|
1071
|
+
return {
|
|
1072
|
+
name: e.name,
|
|
1073
|
+
values: e.values.map((v) => v.description ? { name: v.name, value: v.value, description: v.description } : { name: v.name, value: v.value }),
|
|
1074
|
+
...e.description ? { description: e.description } : {}
|
|
1075
|
+
};
|
|
1076
|
+
}
|
|
1024
1077
|
function buildGlobalRegistries(extractions) {
|
|
1025
1078
|
const converterRegistry = /* @__PURE__ */ new Map();
|
|
1026
1079
|
const interfaceRegistry = /* @__PURE__ */ new Map();
|
|
@@ -1086,28 +1139,32 @@ function buildEntryForIdentity(input) {
|
|
|
1086
1139
|
depth: 0,
|
|
1087
1140
|
visitedConverters: /* @__PURE__ */ new Set()
|
|
1088
1141
|
});
|
|
1089
|
-
|
|
1090
|
-
const serviceFactory = registries.serviceFactoryByModelType.get(identity.modelType);
|
|
1091
|
-
result = {
|
|
1092
|
-
modelType: identity.modelType,
|
|
1093
|
-
modelName,
|
|
1094
|
-
...modelGroup ? { modelGroup } : {},
|
|
1095
|
-
identityConst: identity.identityConst,
|
|
1096
|
-
collectionPrefix: identity.collectionPrefix,
|
|
1097
|
-
...identity.parentIdentityConst ? { parentIdentityConst: identity.parentIdentityConst } : {},
|
|
1098
|
-
...iface.description ? { description: iface.description } : {},
|
|
1099
|
-
sourcePackage: source.sourcePackage,
|
|
1100
|
-
sourceFile: source.sourceFile,
|
|
1101
|
-
fields,
|
|
1102
|
-
...iface.mcpToolNameSegment ? { mcpToolNameSegment: iface.mcpToolNameSegment } : {},
|
|
1103
|
-
...iface.dbxModelRead ? { read: iface.dbxModelRead } : {},
|
|
1104
|
-
...serviceFactory ? { serviceFactory } : {}
|
|
1105
|
-
};
|
|
1142
|
+
result = buildManifestEntry({ identity, modelName, collectionPrefix: identity.collectionPrefix, iface, fields, source, registries });
|
|
1106
1143
|
}
|
|
1107
1144
|
}
|
|
1108
1145
|
}
|
|
1109
1146
|
return result;
|
|
1110
1147
|
}
|
|
1148
|
+
function buildManifestEntry(input) {
|
|
1149
|
+
const { identity, modelName, collectionPrefix, iface, fields, source, registries } = input;
|
|
1150
|
+
const modelGroup = registries.groupByModelName.get(modelName);
|
|
1151
|
+
const serviceFactory = registries.serviceFactoryByModelType.get(identity.modelType);
|
|
1152
|
+
return {
|
|
1153
|
+
modelType: identity.modelType,
|
|
1154
|
+
modelName,
|
|
1155
|
+
...modelGroup ? { modelGroup } : {},
|
|
1156
|
+
identityConst: identity.identityConst,
|
|
1157
|
+
collectionPrefix,
|
|
1158
|
+
...identity.parentIdentityConst ? { parentIdentityConst: identity.parentIdentityConst } : {},
|
|
1159
|
+
...iface.description ? { description: iface.description } : {},
|
|
1160
|
+
sourcePackage: source.sourcePackage,
|
|
1161
|
+
sourceFile: source.sourceFile,
|
|
1162
|
+
fields,
|
|
1163
|
+
...iface.mcpToolNameSegment ? { mcpToolNameSegment: iface.mcpToolNameSegment } : {},
|
|
1164
|
+
...iface.dbxModelRead ? { read: iface.dbxModelRead } : {},
|
|
1165
|
+
...serviceFactory ? { serviceFactory } : {}
|
|
1166
|
+
};
|
|
1167
|
+
}
|
|
1111
1168
|
function findConverterForInterface(extraction, interfaceName) {
|
|
1112
1169
|
return extraction.converters.find((c) => c.interfaceName === interfaceName);
|
|
1113
1170
|
}
|
|
@@ -1158,21 +1215,10 @@ function buildField(input) {
|
|
|
1158
1215
|
return out;
|
|
1159
1216
|
}
|
|
1160
1217
|
function resolveNestedFields(input) {
|
|
1161
|
-
const { field } = input;
|
|
1162
1218
|
let result;
|
|
1163
1219
|
if (input.depth < MAX_NESTED_DEPTH) {
|
|
1164
|
-
|
|
1165
|
-
|
|
1166
|
-
if (field.nestedConverterInline) {
|
|
1167
|
-
nestedConverter = field.nestedConverterInline;
|
|
1168
|
-
} else if (field.nestedConverterRef) {
|
|
1169
|
-
if (input.visitedConverters.has(field.nestedConverterRef)) {
|
|
1170
|
-
aborted = true;
|
|
1171
|
-
} else {
|
|
1172
|
-
nestedConverter = input.converterRegistry.get(field.nestedConverterRef);
|
|
1173
|
-
}
|
|
1174
|
-
}
|
|
1175
|
-
if (!aborted && nestedConverter) {
|
|
1220
|
+
const nestedConverter = selectNestedConverter(input);
|
|
1221
|
+
if (nestedConverter) {
|
|
1176
1222
|
const nextVisited = new Set(input.visitedConverters);
|
|
1177
1223
|
if (nestedConverter.converterConst) nextVisited.add(nestedConverter.converterConst);
|
|
1178
1224
|
const nestedIface = nestedConverter.interfaceName ? input.interfaceRegistry.get(nestedConverter.interfaceName) : void 0;
|
|
@@ -1185,11 +1231,21 @@ function resolveNestedFields(input) {
|
|
|
1185
1231
|
depth: input.depth + 1,
|
|
1186
1232
|
visitedConverters: nextVisited
|
|
1187
1233
|
});
|
|
1188
|
-
result = { fields, isArray: field.nestedIsArray ?? false };
|
|
1234
|
+
result = { fields, isArray: input.field.nestedIsArray ?? false };
|
|
1189
1235
|
}
|
|
1190
1236
|
}
|
|
1191
1237
|
return result;
|
|
1192
1238
|
}
|
|
1239
|
+
function selectNestedConverter(input) {
|
|
1240
|
+
const { field } = input;
|
|
1241
|
+
let result;
|
|
1242
|
+
if (field.nestedConverterInline) {
|
|
1243
|
+
result = field.nestedConverterInline;
|
|
1244
|
+
} else if (field.nestedConverterRef && !input.visitedConverters.has(field.nestedConverterRef)) {
|
|
1245
|
+
result = input.converterRegistry.get(field.nestedConverterRef);
|
|
1246
|
+
}
|
|
1247
|
+
return result;
|
|
1248
|
+
}
|
|
1193
1249
|
function collectAncestors(iface, registry) {
|
|
1194
1250
|
const out = [];
|
|
1195
1251
|
const visited = /* @__PURE__ */ new Set([iface.name]);
|
|
@@ -1261,31 +1317,42 @@ function findIdentifierInBarrelChain(filePath, identifier, visited) {
|
|
|
1261
1317
|
let result = false;
|
|
1262
1318
|
if (!visited.has(filePath)) {
|
|
1263
1319
|
visited.add(filePath);
|
|
1264
|
-
|
|
1265
|
-
try {
|
|
1266
|
-
text = readFileSync5(filePath, "utf8");
|
|
1267
|
-
} catch {
|
|
1268
|
-
text = void 0;
|
|
1269
|
-
}
|
|
1320
|
+
const text = readFileTextSafe(filePath);
|
|
1270
1321
|
if (text !== void 0) {
|
|
1271
|
-
|
|
1272
|
-
|
|
1273
|
-
|
|
1274
|
-
|
|
1275
|
-
|
|
1276
|
-
|
|
1277
|
-
|
|
1278
|
-
|
|
1279
|
-
|
|
1280
|
-
|
|
1281
|
-
|
|
1282
|
-
|
|
1283
|
-
|
|
1284
|
-
|
|
1285
|
-
|
|
1286
|
-
|
|
1287
|
-
|
|
1288
|
-
|
|
1322
|
+
result = barrelTextDeclaresIdentifier(text, identifier) || reExportChainHasIdentifier({ fromFile: filePath, text, identifier, visited });
|
|
1323
|
+
}
|
|
1324
|
+
}
|
|
1325
|
+
return result;
|
|
1326
|
+
}
|
|
1327
|
+
function readFileTextSafe(filePath) {
|
|
1328
|
+
let text;
|
|
1329
|
+
try {
|
|
1330
|
+
text = readFileSync5(filePath, "utf8");
|
|
1331
|
+
} catch {
|
|
1332
|
+
text = void 0;
|
|
1333
|
+
}
|
|
1334
|
+
return text;
|
|
1335
|
+
}
|
|
1336
|
+
function barrelTextDeclaresIdentifier(text, identifier) {
|
|
1337
|
+
let result = false;
|
|
1338
|
+
for (const pattern of EXPORT_DECL_PATTERNS) {
|
|
1339
|
+
const re = new RegExp(pattern.source.replace("IDENT", escapeRegExp(identifier)));
|
|
1340
|
+
if (re.test(text)) {
|
|
1341
|
+
result = true;
|
|
1342
|
+
break;
|
|
1343
|
+
}
|
|
1344
|
+
}
|
|
1345
|
+
return result;
|
|
1346
|
+
}
|
|
1347
|
+
function reExportChainHasIdentifier(input) {
|
|
1348
|
+
const { fromFile, text, identifier, visited } = input;
|
|
1349
|
+
let result = false;
|
|
1350
|
+
const dir = dirname2(fromFile);
|
|
1351
|
+
for (const reExportTarget of collectReExportTargets(text)) {
|
|
1352
|
+
const resolved = resolveReExport(dir, reExportTarget);
|
|
1353
|
+
if (resolved && findIdentifierInBarrelChain(resolved, identifier, visited)) {
|
|
1354
|
+
result = true;
|
|
1355
|
+
break;
|
|
1289
1356
|
}
|
|
1290
1357
|
}
|
|
1291
1358
|
return result;
|
|
@@ -1343,13 +1410,8 @@ function escapeRegExp(value) {
|
|
|
1343
1410
|
|
|
1344
1411
|
// packages/dbx-cli/firebase-api-manifest/src/generate-api-manifest/emit.ts
|
|
1345
1412
|
import { format, resolveConfig } from "prettier";
|
|
1346
|
-
|
|
1347
|
-
// packages/util/src/lib/string/sort.ts
|
|
1348
|
-
var compareStrings = (a, b) => a.localeCompare(b);
|
|
1349
|
-
|
|
1350
|
-
// packages/dbx-cli/firebase-api-manifest/src/generate-api-manifest/emit.ts
|
|
1351
1413
|
async function renderManifest(input) {
|
|
1352
|
-
const { outputFile, entries, projectName, namespace, modelEntries, modelNamespace, emitConverters = false } = input;
|
|
1414
|
+
const { outputFile, entries, projectName, namespace, modelEntries, modelNamespace, enumEntries, enumNamespace, emitConverters = false } = input;
|
|
1353
1415
|
const importsByPackage = /* @__PURE__ */ new Map();
|
|
1354
1416
|
for (const entry of entries) {
|
|
1355
1417
|
if (!entry.packageName || !entry.validatorName) continue;
|
|
@@ -1363,12 +1425,18 @@ async function renderManifest(input) {
|
|
|
1363
1425
|
});
|
|
1364
1426
|
const entryLines = entries.map((e) => renderEntry(e));
|
|
1365
1427
|
const emitModels = Boolean(modelEntries && modelEntries.length > 0 && modelNamespace);
|
|
1366
|
-
const
|
|
1428
|
+
const emitEnums = Boolean(enumEntries && Object.keys(enumEntries).length > 0 && enumNamespace);
|
|
1429
|
+
const dbxCliTypeNames = ["type CliApiManifest", ...emitModels ? ["type CliModelManifest"] : [], ...emitEnums ? ["type CliEnumManifest"] : []];
|
|
1430
|
+
const dbxCliTypeImports = `import { ${dbxCliTypeNames.join(", ")} } from '@dereekb/dbx-cli';`;
|
|
1367
1431
|
const modelSection = emitModels ? `
|
|
1368
1432
|
|
|
1369
1433
|
export const ${modelNamespace}: CliModelManifest = [
|
|
1370
1434
|
${(modelEntries ?? []).map((m) => renderModelEntry(m, emitConverters)).join(",\n")}
|
|
1371
1435
|
];
|
|
1436
|
+
` : "";
|
|
1437
|
+
const enumSection = emitEnums ? `
|
|
1438
|
+
|
|
1439
|
+
export const ${enumNamespace}: CliEnumManifest = ${renderEnumManifest(enumEntries ?? {})};
|
|
1372
1440
|
` : "";
|
|
1373
1441
|
const source = `/* eslint-disable @nx/enforce-module-boundaries */
|
|
1374
1442
|
// AUTO-GENERATED \u2014 DO NOT EDIT.
|
|
@@ -1379,7 +1447,7 @@ ${dbxCliTypeImports}
|
|
|
1379
1447
|
|
|
1380
1448
|
export const ${namespace}: CliApiManifest = [
|
|
1381
1449
|
${entryLines.join(",\n")}
|
|
1382
|
-
];${modelSection}
|
|
1450
|
+
];${modelSection}${enumSection}
|
|
1383
1451
|
`;
|
|
1384
1452
|
return formatWithPrettier(source, outputFile);
|
|
1385
1453
|
}
|
|
@@ -1402,7 +1470,7 @@ function renderEntry({ entry, validatorName }) {
|
|
|
1402
1470
|
entry.mcpResultTypeDescription ? `mcpResultTypeDescription: ${JSON.stringify(entry.mcpResultTypeDescription)}` : void 0,
|
|
1403
1471
|
entry.mcpResultFields && entry.mcpResultFields.length > 0 ? `mcpResultFields: ${renderDocFields(entry.mcpResultFields)}` : void 0
|
|
1404
1472
|
];
|
|
1405
|
-
return ` { ${fields.filter(
|
|
1473
|
+
return ` { ${fields.filter(Boolean).join(", ")} }`;
|
|
1406
1474
|
}
|
|
1407
1475
|
function renderDocFields(fields) {
|
|
1408
1476
|
const items = fields.map((field) => {
|
|
@@ -1432,7 +1500,7 @@ function renderModelEntry(entry, emitConverters) {
|
|
|
1432
1500
|
entry.read ? `read: ${JSON.stringify(entry.read)}` : void 0,
|
|
1433
1501
|
entry.serviceFactory ? `serviceFactory: { exportName: ${JSON.stringify(entry.serviceFactory.exportName)}, sourceFile: ${JSON.stringify(entry.serviceFactory.sourceFile)} }` : void 0
|
|
1434
1502
|
];
|
|
1435
|
-
return ` { ${fields.filter(
|
|
1503
|
+
return ` { ${fields.filter(Boolean).join(", ")} }`;
|
|
1436
1504
|
}
|
|
1437
1505
|
function renderModelFields(fields, emitConverters) {
|
|
1438
1506
|
let result;
|
|
@@ -1444,6 +1512,23 @@ function renderModelFields(fields, emitConverters) {
|
|
|
1444
1512
|
}
|
|
1445
1513
|
return result;
|
|
1446
1514
|
}
|
|
1515
|
+
function renderEnumManifest(enums) {
|
|
1516
|
+
const names = Object.keys(enums).sort((a, b) => a.localeCompare(b));
|
|
1517
|
+
const items = names.map((name) => `${JSON.stringify(name)}: ${renderEnumEntry(enums[name])}`);
|
|
1518
|
+
return names.length === 0 ? "{}" : `{ ${items.join(", ")} }`;
|
|
1519
|
+
}
|
|
1520
|
+
function renderEnumEntry(entry) {
|
|
1521
|
+
const parts = [`name: ${JSON.stringify(entry.name)}`, `values: ${renderEnumValues(entry.values)}`, entry.description ? `description: ${JSON.stringify(entry.description)}` : void 0];
|
|
1522
|
+
return `{ ${parts.filter(Boolean).join(", ")} }`;
|
|
1523
|
+
}
|
|
1524
|
+
function renderEnumValues(values) {
|
|
1525
|
+
const items = values.map((value) => renderEnumValue(value));
|
|
1526
|
+
return `[${items.join(", ")}]`;
|
|
1527
|
+
}
|
|
1528
|
+
function renderEnumValue(value) {
|
|
1529
|
+
const parts = [`name: ${JSON.stringify(value.name)}`, `value: ${JSON.stringify(value.value)}`, value.description ? `description: ${JSON.stringify(value.description)}` : void 0];
|
|
1530
|
+
return `{ ${parts.filter(Boolean).join(", ")} }`;
|
|
1531
|
+
}
|
|
1447
1532
|
function renderModelField(field, emitConverters) {
|
|
1448
1533
|
const nestedIsArrayLiteral = field.nestedIsArray ? "true" : "false";
|
|
1449
1534
|
const parts = [
|
|
@@ -1458,7 +1543,7 @@ function renderModelField(field, emitConverters) {
|
|
|
1458
1543
|
field.nestedFields ? `nestedFields: ${renderModelFields(field.nestedFields, emitConverters)}` : void 0,
|
|
1459
1544
|
field.nestedFields ? `nestedIsArray: ${nestedIsArrayLiteral}` : void 0
|
|
1460
1545
|
];
|
|
1461
|
-
return `{ ${parts.filter(
|
|
1546
|
+
return `{ ${parts.filter(Boolean).join(", ")} }`;
|
|
1462
1547
|
}
|
|
1463
1548
|
|
|
1464
1549
|
// packages/dbx-cli/firebase-api-manifest/src/generate-api-manifest/main.ts
|
|
@@ -1551,8 +1636,9 @@ async function main() {
|
|
|
1551
1636
|
collected.sort(compareEntries);
|
|
1552
1637
|
const modelEntries = flags.emitModels ? assembleModels({ extractions: modelSources }) : [];
|
|
1553
1638
|
const filteredModelEntries = flags.only ? modelEntries.filter((m) => flags.only?.has(m.modelType)) : modelEntries;
|
|
1639
|
+
const enumEntries = flags.emitModels ? collectModelEnums({ extractions: modelSources, models: filteredModelEntries }) : {};
|
|
1554
1640
|
ensureOutputDir(outputDir);
|
|
1555
|
-
const formatted = await renderManifest({ outputFile, entries: collected, projectName, namespace, modelEntries: filteredModelEntries, modelNamespace: deriveModelNamespace(flags.project), emitConverters: flags.emitModelConverters });
|
|
1641
|
+
const formatted = await renderManifest({ outputFile, entries: collected, projectName, namespace, modelEntries: filteredModelEntries, modelNamespace: deriveModelNamespace(flags.project), enumEntries, enumNamespace: deriveEnumNamespace(flags.project), emitConverters: flags.emitModelConverters });
|
|
1556
1642
|
if (existsSync3(outputFile) && readFileSync6(outputFile, "utf8") === formatted) {
|
|
1557
1643
|
console.log(`[unchanged] ${relative2(WORKSPACE_ROOT, outputFile)}`);
|
|
1558
1644
|
} else {
|
|
@@ -1560,7 +1646,7 @@ async function main() {
|
|
|
1560
1646
|
console.log(`[wrote] ${relative2(WORKSPACE_ROOT, outputFile)}`);
|
|
1561
1647
|
}
|
|
1562
1648
|
const groupCount = packageCache.size === 0 ? 0 : new Set(collected.map((c) => c.entry.groupName)).size;
|
|
1563
|
-
const modelSummary = flags.emitModels ? ` \xB7 ${filteredModelEntries.length} models` : "";
|
|
1649
|
+
const modelSummary = flags.emitModels ? ` \xB7 ${filteredModelEntries.length} models \xB7 ${Object.keys(enumEntries).length} enums` : "";
|
|
1564
1650
|
console.log(`Summary: ${groupCount} groups \xB7 ${collected.length} entries \xB7 ${collected.length - missingValidators} validators bound \xB7 ${missingValidators} missing \xB7 ${skippedGroups} skipped${modelSummary}`);
|
|
1565
1651
|
if (flags.strict && missingValidators > 0) {
|
|
1566
1652
|
console.error(`[strict] ${missingValidators} validator(s) missing \u2014 failing build.`);
|
|
@@ -1592,6 +1678,10 @@ function deriveModelNamespace(projectName) {
|
|
|
1592
1678
|
const base = (projectName ?? "cli").replaceAll(/[^a-zA-Z0-9]+/g, "_");
|
|
1593
1679
|
return `${base.toUpperCase()}_MODEL_MANIFEST`;
|
|
1594
1680
|
}
|
|
1681
|
+
function deriveEnumNamespace(projectName) {
|
|
1682
|
+
const base = (projectName ?? "cli").replaceAll(/[^a-zA-Z0-9]+/g, "_");
|
|
1683
|
+
return `${base.toUpperCase()}_ENUM_MANIFEST`;
|
|
1684
|
+
}
|
|
1595
1685
|
function parseFlags(argv) {
|
|
1596
1686
|
let only;
|
|
1597
1687
|
let strict = false;
|