@dereekb/dbx-components-mcp 13.11.6 → 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.
- package/dbx-components-mcp.js +190 -25
- package/generated/rule-catalog.generated.json +29 -3
- package/package.json +11 -11
package/dbx-components-mcp.js
CHANGED
|
@@ -5,7 +5,7 @@ const require = __createRequire(import.meta.url);
|
|
|
5
5
|
// packages/dbx-components-mcp/package.json
|
|
6
6
|
var package_default = {
|
|
7
7
|
name: "@dereekb/dbx-components-mcp",
|
|
8
|
-
version: "13.11.
|
|
8
|
+
version: "13.11.7",
|
|
9
9
|
type: "module",
|
|
10
10
|
bin: {
|
|
11
11
|
"dbx-components-mcp": "dbx-components-mcp.js"
|
|
@@ -20,20 +20,20 @@ var package_default = {
|
|
|
20
20
|
"@angular/core": "21.2.11",
|
|
21
21
|
"@angular/forms": "21.2.11",
|
|
22
22
|
"@angular/material": "^21.2.9",
|
|
23
|
-
"@dereekb/dbx-cli": "13.11.
|
|
24
|
-
"@dereekb/dbx-core": "13.11.
|
|
25
|
-
"@dereekb/dbx-firebase": "13.11.
|
|
26
|
-
"@dereekb/dbx-form": "13.11.
|
|
27
|
-
"@dereekb/dbx-web": "13.11.
|
|
28
|
-
"@dereekb/firebase": "13.11.
|
|
29
|
-
"@dereekb/firebase-server": "13.11.
|
|
30
|
-
"@dereekb/rxjs": "13.11.
|
|
31
|
-
"@dereekb/util": "13.11.
|
|
23
|
+
"@dereekb/dbx-cli": "13.11.7",
|
|
24
|
+
"@dereekb/dbx-core": "13.11.7",
|
|
25
|
+
"@dereekb/dbx-firebase": "13.11.7",
|
|
26
|
+
"@dereekb/dbx-form": "13.11.7",
|
|
27
|
+
"@dereekb/dbx-web": "13.11.7",
|
|
28
|
+
"@dereekb/firebase": "13.11.7",
|
|
29
|
+
"@dereekb/firebase-server": "13.11.7",
|
|
30
|
+
"@dereekb/rxjs": "13.11.7",
|
|
31
|
+
"@dereekb/util": "13.11.7",
|
|
32
32
|
"@uirouter/angular": "21.0.0",
|
|
33
33
|
rxjs: "^7.8.2"
|
|
34
34
|
},
|
|
35
35
|
devDependencies: {
|
|
36
|
-
"@dereekb/model": "13.11.
|
|
36
|
+
"@dereekb/model": "13.11.7"
|
|
37
37
|
}
|
|
38
38
|
};
|
|
39
39
|
|
|
@@ -14280,6 +14280,14 @@ function createListAppTool(config) {
|
|
|
14280
14280
|
|
|
14281
14281
|
// packages/dbx-components-mcp/src/tools/model-validate/extract.ts
|
|
14282
14282
|
import { Node as Node11, Project as Project4, SyntaxKind as SyntaxKind4 } from "ts-morph";
|
|
14283
|
+
|
|
14284
|
+
// packages/dbx-components-mcp/src/tools/model-validate/types.ts
|
|
14285
|
+
var MAX_FIELD_NAME_LENGTH = 5;
|
|
14286
|
+
var ROOT_MODEL_ORDER = ["identity", "interface", "rolesType", "documentClass", "converter", "referenceFn", "collectionType", "collectionFn"];
|
|
14287
|
+
var SUBCOLLECTION_MODEL_ORDER = ["identity", "interface", "rolesType", "documentClass", "converter", "referenceFn", "collectionType", "collectionFactoryType", "collectionFn", "referenceGroupFn", "collectionGroupType", "collectionGroupFn"];
|
|
14288
|
+
var SUB_OBJECT_FACTORY_NAMES = ["firestoreSubObject", "firestoreObjectArray", "firestoreMap"];
|
|
14289
|
+
|
|
14290
|
+
// packages/dbx-components-mcp/src/tools/model-validate/extract.ts
|
|
14283
14291
|
function extractFile(source) {
|
|
14284
14292
|
const project = new Project4({ useInMemoryFileSystem: true, skipAddingFilesFromTsConfig: true });
|
|
14285
14293
|
const sourceFile = project.createSourceFile(source.name, source.text, { overwrite: true });
|
|
@@ -14289,16 +14297,56 @@ function extractFile(source) {
|
|
|
14289
14297
|
const firstModelLine = identities.length > 0 ? Math.min(...identities.map((i) => i.line)) : void 0;
|
|
14290
14298
|
const models = identities.map((identity) => extractModel(sourceFile, identity));
|
|
14291
14299
|
const dataInterfaces = findDataInterfaces(sourceFile, groupInterface);
|
|
14300
|
+
const subObjectCalls = findSubObjectFactoryCalls(sourceFile);
|
|
14292
14301
|
const result = {
|
|
14293
14302
|
name: source.name,
|
|
14294
14303
|
groupInterface,
|
|
14295
14304
|
groupTypes,
|
|
14296
14305
|
firstModelLine,
|
|
14297
14306
|
models,
|
|
14298
|
-
dataInterfaces
|
|
14307
|
+
dataInterfaces,
|
|
14308
|
+
subObjectCalls
|
|
14299
14309
|
};
|
|
14300
14310
|
return result;
|
|
14301
14311
|
}
|
|
14312
|
+
function findSubObjectFactoryCalls(sourceFile) {
|
|
14313
|
+
const out = [];
|
|
14314
|
+
const factorySet = new Set(SUB_OBJECT_FACTORY_NAMES);
|
|
14315
|
+
for (const call of sourceFile.getDescendantsOfKind(SyntaxKind4.CallExpression)) {
|
|
14316
|
+
const expr = call.getExpression();
|
|
14317
|
+
if (!Node11.isIdentifier(expr)) {
|
|
14318
|
+
continue;
|
|
14319
|
+
}
|
|
14320
|
+
const factoryName = expr.getText();
|
|
14321
|
+
if (!factorySet.has(factoryName)) {
|
|
14322
|
+
continue;
|
|
14323
|
+
}
|
|
14324
|
+
const typeArgs = call.getTypeArguments();
|
|
14325
|
+
if (typeArgs.length === 0) {
|
|
14326
|
+
continue;
|
|
14327
|
+
}
|
|
14328
|
+
const typeArgName = readTypeReferenceIdentifier(typeArgs[0]);
|
|
14329
|
+
if (!typeArgName) {
|
|
14330
|
+
continue;
|
|
14331
|
+
}
|
|
14332
|
+
out.push({
|
|
14333
|
+
factoryName,
|
|
14334
|
+
typeArgName,
|
|
14335
|
+
line: call.getStartLineNumber()
|
|
14336
|
+
});
|
|
14337
|
+
}
|
|
14338
|
+
return out;
|
|
14339
|
+
}
|
|
14340
|
+
function readTypeReferenceIdentifier(typeNode) {
|
|
14341
|
+
if (!Node11.isTypeReference(typeNode)) {
|
|
14342
|
+
return void 0;
|
|
14343
|
+
}
|
|
14344
|
+
const nameNode = typeNode.getTypeName();
|
|
14345
|
+
if (!Node11.isIdentifier(nameNode)) {
|
|
14346
|
+
return void 0;
|
|
14347
|
+
}
|
|
14348
|
+
return nameNode.getText();
|
|
14349
|
+
}
|
|
14302
14350
|
function findDataInterfaces(sourceFile, groupInterface) {
|
|
14303
14351
|
const out = [];
|
|
14304
14352
|
const groupName = groupInterface?.name;
|
|
@@ -14309,7 +14357,8 @@ function findDataInterfaces(sourceFile, groupInterface) {
|
|
|
14309
14357
|
}
|
|
14310
14358
|
const fields = extractInterfaceFields(iface);
|
|
14311
14359
|
const dbxModelTag = readDbxModelTag(iface.getJsDocs());
|
|
14312
|
-
|
|
14360
|
+
const dbxModelSubObjectTag = readDbxModelSubObjectTag(iface.getJsDocs());
|
|
14361
|
+
out.push({ name, line: iface.getStartLineNumber(), dbxModelTag, dbxModelSubObjectTag, fields });
|
|
14313
14362
|
}
|
|
14314
14363
|
return out;
|
|
14315
14364
|
}
|
|
@@ -14379,6 +14428,17 @@ function readDbxModelTag(jsDocs) {
|
|
|
14379
14428
|
}
|
|
14380
14429
|
return result;
|
|
14381
14430
|
}
|
|
14431
|
+
function readDbxModelSubObjectTag(jsDocs) {
|
|
14432
|
+
let result = false;
|
|
14433
|
+
for (const jsDoc of jsDocs) {
|
|
14434
|
+
for (const tag of jsDoc.getTags()) {
|
|
14435
|
+
if (tag.getTagName() === "dbxModelSubObject") {
|
|
14436
|
+
result = true;
|
|
14437
|
+
}
|
|
14438
|
+
}
|
|
14439
|
+
}
|
|
14440
|
+
return result;
|
|
14441
|
+
}
|
|
14382
14442
|
function readDbxModelVariableTag(jsDocs) {
|
|
14383
14443
|
let result;
|
|
14384
14444
|
for (const jsDoc of jsDocs) {
|
|
@@ -15141,7 +15201,7 @@ var RULE_CATALOG = [
|
|
|
15141
15201
|
severity: "warning",
|
|
15142
15202
|
title: "A persisted field's `@dbxModelVariable <longName>` tag value matches the field's short name verbatim, defeating the tag's purpose (the long name should be the field's unabbreviated camelCase variable name)",
|
|
15143
15203
|
whatItFlags: "A persisted field's `@dbxModelVariable <longName>` tag value matches the field's short name verbatim, defeating the tag's purpose (the long name should be the field's unabbreviated camelCase variable name).",
|
|
15144
|
-
whenItApplies: "Every field on a `@dbxModel` interface where `@dbxModelVariable` is present and its value equals the field's name (e.g. field `h` tagged `@dbxModelVariable h`, field `ub` tagged `@dbxModelVariable ub`). The tag must always carry the unabbreviated form so the catalog and decoder surface a meaningful long name.",
|
|
15204
|
+
whenItApplies: "Every field on a `@dbxModel` interface or on a `@dbxModelSubObject`-tagged embedded sub-object interface where `@dbxModelVariable` is present and its value equals the field's name (e.g. field `h` tagged `@dbxModelVariable h`, field `ub` tagged `@dbxModelVariable ub`). The tag must always carry the unabbreviated form so the catalog and decoder surface a meaningful long name.",
|
|
15145
15205
|
whenItDoesNotApply: "Fields whose name is already the unabbreviated camelCase form (e.g. `name`, `email`, `id`, `at`) \u2014 add those to `dbx-mcp.config.json` `modelValidate.ignoredFieldNames` to silence the warning workspace-wide.",
|
|
15146
15206
|
canonicalFix: "Replace the tag value with the field's unabbreviated camelCase variable name (e.g. `@dbxModelVariable h` \u2192 `@dbxModelVariable hours`, `@dbxModelVariable ub` \u2192 `@dbxModelVariable usedBudget`). To exempt fields whose long name legitimately equals the short name, add them to `modelValidate.ignoredFieldNames`."
|
|
15147
15207
|
},
|
|
@@ -15161,8 +15221,8 @@ var RULE_CATALOG = [
|
|
|
15161
15221
|
severity: "warning",
|
|
15162
15222
|
title: "A persisted field is missing its `@dbxModelVariable <name>` JSDoc tag",
|
|
15163
15223
|
whatItFlags: "A persisted field is missing its `@dbxModelVariable <name>` JSDoc tag.",
|
|
15164
|
-
whenItApplies: "Every field on a `@dbxModel` interface \u2014 the tag carries the human-readable long name the catalog and decoder use when surfacing the field to operators. The long name is the variable name the field would have unabbreviated, written in camelCase (e.g. `uid` \u2192 `userUid`, `n` \u2192 `name`, `crAt` \u2192 `createdAt`).",
|
|
15165
|
-
whenItDoesNotApply: "Fields on embedded sub-object interfaces
|
|
15224
|
+
whenItApplies: "Every field on a `@dbxModel` interface or on a `@dbxModelSubObject`-tagged embedded sub-object interface \u2014 the tag carries the human-readable long name the catalog and decoder use when surfacing the field to operators. The long name is the variable name the field would have unabbreviated, written in camelCase (e.g. `uid` \u2192 `userUid`, `n` \u2192 `name`, `crAt` \u2192 `createdAt`).",
|
|
15225
|
+
whenItDoesNotApply: "Fields on embedded sub-object interfaces that are not tagged with `@dbxModelSubObject` (the tag is opt-in) and fields the project deliberately leaves untagged (rare).",
|
|
15166
15226
|
canonicalFix: "Append `@dbxModelVariable <longName>` to the field's JSDoc block, where `<longName>` is the field's unabbreviated camelCase variable name."
|
|
15167
15227
|
},
|
|
15168
15228
|
{
|
|
@@ -15349,6 +15409,32 @@ var RULE_CATALOG = [
|
|
|
15349
15409
|
whenItDoesNotApply: "When intentionally module-private.",
|
|
15350
15410
|
canonicalFix: "Add `export` to the alias."
|
|
15351
15411
|
},
|
|
15412
|
+
{
|
|
15413
|
+
code: "MODEL_SUBOBJECT_NOT_TAGGED",
|
|
15414
|
+
source: "dbx_model_validate",
|
|
15415
|
+
severity: "warning",
|
|
15416
|
+
title: "An interface is referenced as the first generic type-argument of a sub-object factory call (`firestoreSubObject<T>(...)`, `firestoreObjectArray<T>(...)`, or `firestoreMap<T>(...)`) but its declaration carries neither `@dbxModel` nor `@dbxModelSubObject`. The interface therefore opts out of the workspace's `@dbxModelVariable` long-name conventions even though its fields are persisted to Firestore",
|
|
15417
|
+
whatItFlags: "An interface is referenced as the first generic type-argument of a sub-object factory call (`firestoreSubObject<T>(...)`, `firestoreObjectArray<T>(...)`, or `firestoreMap<T>(...)`) but its declaration carries neither `@dbxModel` nor `@dbxModelSubObject`. The interface therefore opts out of the workspace's `@dbxModelVariable` long-name conventions even though its fields are persisted to Firestore.",
|
|
15418
|
+
whenItApplies: "Any interface declared in the validated source set that is the type-arg of at least one sub-object factory call and lacks both `@dbxModel` and `@dbxModelSubObject`. The warning is emitted once per interface (de-duplicated across all call sites) and anchored at the interface declaration line. Unresolved type-args \u2014 interfaces declared outside the validated source set, generic parameters, inline types \u2014 are silently skipped to avoid false positives.",
|
|
15419
|
+
whenItDoesNotApply: "Interfaces already tagged with `@dbxModel` (top-level models) or `@dbxModelSubObject` (embedded sub-objects), and interfaces referenced only from outside the supplied source set (different package, etc.).",
|
|
15420
|
+
canonicalFix: "Add `@dbxModelSubObject` to the interface's JSDoc block so its fields are subject to `@dbxModelVariable` long-name checks. If the interface is actually a top-level Firestore model, declare a matching `firestoreModelIdentity` and tag it `@dbxModel` instead.",
|
|
15421
|
+
seeAlso: [
|
|
15422
|
+
{
|
|
15423
|
+
kind: "artifact",
|
|
15424
|
+
target: "firestore-model"
|
|
15425
|
+
}
|
|
15426
|
+
]
|
|
15427
|
+
},
|
|
15428
|
+
{
|
|
15429
|
+
code: "MODEL_SUBOBJECT_TAG_CONFLICT",
|
|
15430
|
+
source: "dbx_model_validate",
|
|
15431
|
+
severity: "error",
|
|
15432
|
+
title: "An interface declaration carries both `@dbxModel` and `@dbxModelSubObject` JSDoc tags. The two tags model mutually exclusive concepts \u2014 `@dbxModel` marks a top-level Firestore model registered in the catalog via `firestoreModelIdentity`, while `@dbxModelSubObject` marks an embedded sub-object interface persisted only as part of a parent model's converter \u2014 so an interface must carry at most one",
|
|
15433
|
+
whatItFlags: "An interface declaration carries both `@dbxModel` and `@dbxModelSubObject` JSDoc tags. The two tags model mutually exclusive concepts \u2014 `@dbxModel` marks a top-level Firestore model registered in the catalog via `firestoreModelIdentity`, while `@dbxModelSubObject` marks an embedded sub-object interface persisted only as part of a parent model's converter \u2014 so an interface must carry at most one.",
|
|
15434
|
+
whenItApplies: "Any interface JSDoc block where both `@dbxModel` and `@dbxModelSubObject` are present.",
|
|
15435
|
+
whenItDoesNotApply: "Interfaces carrying only one of the two tags (or neither).",
|
|
15436
|
+
canonicalFix: "Remove whichever tag does not describe the interface. If the interface has a matching `firestoreModelIdentity` call, keep `@dbxModel`; if it is only embedded inside another model via `firestoreSubObject<T>()`, keep `@dbxModelSubObject`."
|
|
15437
|
+
},
|
|
15352
15438
|
{
|
|
15353
15439
|
code: "SUB_MISSING_COLLECTION_FACTORY_FN",
|
|
15354
15440
|
source: "dbx_model_validate",
|
|
@@ -16975,15 +17061,11 @@ function formatSeeAlso(ref) {
|
|
|
16975
17061
|
return `Doc: ${ref.target}${tail}`;
|
|
16976
17062
|
}
|
|
16977
17063
|
|
|
16978
|
-
// packages/dbx-components-mcp/src/tools/model-validate/types.ts
|
|
16979
|
-
var MAX_FIELD_NAME_LENGTH = 5;
|
|
16980
|
-
var ROOT_MODEL_ORDER = ["identity", "interface", "rolesType", "documentClass", "converter", "referenceFn", "collectionType", "collectionFn"];
|
|
16981
|
-
var SUBCOLLECTION_MODEL_ORDER = ["identity", "interface", "rolesType", "documentClass", "converter", "referenceFn", "collectionType", "collectionFactoryType", "collectionFn", "referenceGroupFn", "collectionGroupType", "collectionGroupFn"];
|
|
16982
|
-
|
|
16983
17064
|
// packages/dbx-components-mcp/src/tools/model-validate/rules.ts
|
|
16984
|
-
function runRules(file, options) {
|
|
17065
|
+
function runRules(file, options, context) {
|
|
16985
17066
|
const violations = [];
|
|
16986
17067
|
if (file.models.length === 0) {
|
|
17068
|
+
checkSubObjectFactoryCallSites(file, violations, context);
|
|
16987
17069
|
return violations;
|
|
16988
17070
|
}
|
|
16989
17071
|
checkFileLevel(file, violations);
|
|
@@ -16996,6 +17078,8 @@ function runRules(file, options) {
|
|
|
16996
17078
|
checkDeclarationOrder(file, model, violations);
|
|
16997
17079
|
}
|
|
16998
17080
|
checkFieldNameLengths(file, violations, options);
|
|
17081
|
+
checkSubObjectInterfaceTags(file, violations);
|
|
17082
|
+
checkSubObjectFactoryCallSites(file, violations, context);
|
|
16999
17083
|
return violations;
|
|
17000
17084
|
}
|
|
17001
17085
|
function checkFieldNameLengths(file, violations, options) {
|
|
@@ -17035,7 +17119,8 @@ function checkFieldJsDocs(file, violations, options) {
|
|
|
17035
17119
|
model: iface.name
|
|
17036
17120
|
});
|
|
17037
17121
|
}
|
|
17038
|
-
|
|
17122
|
+
const fieldRulesApply = iface.dbxModelTag || iface.dbxModelSubObjectTag;
|
|
17123
|
+
if (fieldRulesApply && field.dbxModelVariableTag === void 0) {
|
|
17039
17124
|
pushViolation(violations, {
|
|
17040
17125
|
code: "MODEL_FIELD_MISSING_VARIABLE_TAG",
|
|
17041
17126
|
severity: "warning",
|
|
@@ -17045,7 +17130,7 @@ function checkFieldJsDocs(file, violations, options) {
|
|
|
17045
17130
|
model: iface.name
|
|
17046
17131
|
});
|
|
17047
17132
|
}
|
|
17048
|
-
if (
|
|
17133
|
+
if (fieldRulesApply && field.dbxModelVariableTag !== void 0 && field.dbxModelVariableTag === field.name && !ignored?.has(field.name)) {
|
|
17049
17134
|
pushViolation(violations, {
|
|
17050
17135
|
code: "MODEL_FIELD_LONG_NAME_EQUALS_NAME",
|
|
17051
17136
|
severity: "warning",
|
|
@@ -17058,6 +17143,49 @@ function checkFieldJsDocs(file, violations, options) {
|
|
|
17058
17143
|
}
|
|
17059
17144
|
}
|
|
17060
17145
|
}
|
|
17146
|
+
function checkSubObjectInterfaceTags(file, violations) {
|
|
17147
|
+
for (const iface of file.dataInterfaces) {
|
|
17148
|
+
if (iface.dbxModelTag && iface.dbxModelSubObjectTag) {
|
|
17149
|
+
pushViolation(violations, {
|
|
17150
|
+
code: "MODEL_SUBOBJECT_TAG_CONFLICT",
|
|
17151
|
+
message: `Interface \`${iface.name}\` carries both \`@dbxModel\` and \`@dbxModelSubObject\`. Use only \`@dbxModel\` for top-level Firestore models; use \`@dbxModelSubObject\` only for embedded sub-objects without a \`firestoreModelIdentity\`.`,
|
|
17152
|
+
file: file.name,
|
|
17153
|
+
line: iface.line,
|
|
17154
|
+
model: iface.name
|
|
17155
|
+
});
|
|
17156
|
+
}
|
|
17157
|
+
}
|
|
17158
|
+
}
|
|
17159
|
+
function checkSubObjectFactoryCallSites(file, violations, context) {
|
|
17160
|
+
if (!context) {
|
|
17161
|
+
return;
|
|
17162
|
+
}
|
|
17163
|
+
if (file.subObjectCalls.length === 0) {
|
|
17164
|
+
return;
|
|
17165
|
+
}
|
|
17166
|
+
for (const call of file.subObjectCalls) {
|
|
17167
|
+
const entry = context.interfacesByName.get(call.typeArgName);
|
|
17168
|
+
if (!entry) {
|
|
17169
|
+
continue;
|
|
17170
|
+
}
|
|
17171
|
+
const { iface, file: declFile } = entry;
|
|
17172
|
+
if (iface.dbxModelTag || iface.dbxModelSubObjectTag) {
|
|
17173
|
+
continue;
|
|
17174
|
+
}
|
|
17175
|
+
if (context.emittedSubObjectInterfaces.has(call.typeArgName)) {
|
|
17176
|
+
continue;
|
|
17177
|
+
}
|
|
17178
|
+
context.emittedSubObjectInterfaces.add(call.typeArgName);
|
|
17179
|
+
pushViolation(violations, {
|
|
17180
|
+
code: "MODEL_SUBOBJECT_NOT_TAGGED",
|
|
17181
|
+
severity: "warning",
|
|
17182
|
+
message: `Interface \`${iface.name}\` is referenced by \`${call.factoryName}<${iface.name}>(...)\` at ${file.name}:${call.line} but carries neither \`@dbxModel\` nor \`@dbxModelSubObject\`. Tag the interface with \`@dbxModelSubObject\` so its persisted fields are subject to the same \`@dbxModelVariable\` long-name checks as a top-level model.`,
|
|
17183
|
+
file: declFile,
|
|
17184
|
+
line: iface.line,
|
|
17185
|
+
model: iface.name
|
|
17186
|
+
});
|
|
17187
|
+
}
|
|
17188
|
+
}
|
|
17061
17189
|
function checkFileLevel(file, violations) {
|
|
17062
17190
|
const { groupInterface, groupTypes, firstModelLine, models, name } = file;
|
|
17063
17191
|
if (groupInterface) {
|
|
@@ -17759,10 +17887,15 @@ function validateFirebaseModelSources(sources, options) {
|
|
|
17759
17887
|
let modelsChecked = 0;
|
|
17760
17888
|
let errorCount = 0;
|
|
17761
17889
|
let warningCount = 0;
|
|
17890
|
+
const extracteds = [];
|
|
17762
17891
|
for (const source of sources) {
|
|
17763
17892
|
const extracted = extractFile(source);
|
|
17893
|
+
extracteds.push(extracted);
|
|
17764
17894
|
modelsChecked += extracted.models.length;
|
|
17765
|
-
|
|
17895
|
+
}
|
|
17896
|
+
const context = buildCrossFileRuleContext(extracteds);
|
|
17897
|
+
for (const extracted of extracteds) {
|
|
17898
|
+
const fileViolations = runRules(extracted, options, context);
|
|
17766
17899
|
for (const v of fileViolations) {
|
|
17767
17900
|
violations.push(v);
|
|
17768
17901
|
if (v.severity === "error") {
|
|
@@ -17781,6 +17914,22 @@ function validateFirebaseModelSources(sources, options) {
|
|
|
17781
17914
|
};
|
|
17782
17915
|
return result;
|
|
17783
17916
|
}
|
|
17917
|
+
function buildCrossFileRuleContext(extracteds) {
|
|
17918
|
+
const interfacesByName = /* @__PURE__ */ new Map();
|
|
17919
|
+
for (const file of extracteds) {
|
|
17920
|
+
for (const iface of file.dataInterfaces) {
|
|
17921
|
+
if (interfacesByName.has(iface.name)) {
|
|
17922
|
+
continue;
|
|
17923
|
+
}
|
|
17924
|
+
interfacesByName.set(iface.name, { file: file.name, iface });
|
|
17925
|
+
}
|
|
17926
|
+
}
|
|
17927
|
+
const context = {
|
|
17928
|
+
interfacesByName,
|
|
17929
|
+
emittedSubObjectInterfaces: /* @__PURE__ */ new Set()
|
|
17930
|
+
};
|
|
17931
|
+
return context;
|
|
17932
|
+
}
|
|
17784
17933
|
|
|
17785
17934
|
// packages/dbx-components-mcp/src/tools/model-validate.tool.ts
|
|
17786
17935
|
var DBX_MODEL_VALIDATE_TOOL = {
|
|
@@ -26250,10 +26399,26 @@ function collectHandlerBindingsFromValue(options) {
|
|
|
26250
26399
|
if (Node19.isIdentifier(callee)) {
|
|
26251
26400
|
return collectHandlerBindingsFromIdentifier({ name: callee.getText(), sf, index, trustedExternal, resolved, unresolved, visited });
|
|
26252
26401
|
}
|
|
26402
|
+
return false;
|
|
26253
26403
|
}
|
|
26254
26404
|
if (Node19.isIdentifier(inner)) {
|
|
26255
26405
|
return collectHandlerBindingsFromIdentifier({ name: inner.getText(), sf, index, trustedExternal, resolved, unresolved, visited });
|
|
26256
26406
|
}
|
|
26407
|
+
if (Node19.isArrayLiteralExpression(inner)) {
|
|
26408
|
+
let any = false;
|
|
26409
|
+
for (const el of inner.getElements()) {
|
|
26410
|
+
if (Node19.isSpreadElement(el)) {
|
|
26411
|
+
if (collectHandlerBindingsFromValue({ node: el.getExpression(), sf, index, trustedExternal, resolved, unresolved, visited })) {
|
|
26412
|
+
any = true;
|
|
26413
|
+
}
|
|
26414
|
+
continue;
|
|
26415
|
+
}
|
|
26416
|
+
if (collectHandlerBindingsFromValue({ node: el, sf, index, trustedExternal, resolved, unresolved, visited })) {
|
|
26417
|
+
any = true;
|
|
26418
|
+
}
|
|
26419
|
+
}
|
|
26420
|
+
return any;
|
|
26421
|
+
}
|
|
26257
26422
|
return false;
|
|
26258
26423
|
}
|
|
26259
26424
|
function collectHandlerBindingsFromIdentifier(options) {
|
|
@@ -479,7 +479,7 @@
|
|
|
479
479
|
"severity": "warning",
|
|
480
480
|
"title": "A persisted field's `@dbxModelVariable <longName>` tag value matches the field's short name verbatim, defeating the tag's purpose (the long name should be the field's unabbreviated camelCase variable name)",
|
|
481
481
|
"whatItFlags": "A persisted field's `@dbxModelVariable <longName>` tag value matches the field's short name verbatim, defeating the tag's purpose (the long name should be the field's unabbreviated camelCase variable name).",
|
|
482
|
-
"whenItApplies": "Every field on a `@dbxModel` interface where `@dbxModelVariable` is present and its value equals the field's name (e.g. field `h` tagged `@dbxModelVariable h`, field `ub` tagged `@dbxModelVariable ub`). The tag must always carry the unabbreviated form so the catalog and decoder surface a meaningful long name.",
|
|
482
|
+
"whenItApplies": "Every field on a `@dbxModel` interface or on a `@dbxModelSubObject`-tagged embedded sub-object interface where `@dbxModelVariable` is present and its value equals the field's name (e.g. field `h` tagged `@dbxModelVariable h`, field `ub` tagged `@dbxModelVariable ub`). The tag must always carry the unabbreviated form so the catalog and decoder surface a meaningful long name.",
|
|
483
483
|
"whenItDoesNotApply": "Fields whose name is already the unabbreviated camelCase form (e.g. `name`, `email`, `id`, `at`) — add those to `dbx-mcp.config.json` `modelValidate.ignoredFieldNames` to silence the warning workspace-wide.",
|
|
484
484
|
"canonicalFix": "Replace the tag value with the field's unabbreviated camelCase variable name (e.g. `@dbxModelVariable h` → `@dbxModelVariable hours`, `@dbxModelVariable ub` → `@dbxModelVariable usedBudget`). To exempt fields whose long name legitimately equals the short name, add them to `modelValidate.ignoredFieldNames`."
|
|
485
485
|
},
|
|
@@ -499,8 +499,8 @@
|
|
|
499
499
|
"severity": "warning",
|
|
500
500
|
"title": "A persisted field is missing its `@dbxModelVariable <name>` JSDoc tag",
|
|
501
501
|
"whatItFlags": "A persisted field is missing its `@dbxModelVariable <name>` JSDoc tag.",
|
|
502
|
-
"whenItApplies": "Every field on a `@dbxModel` interface — the tag carries the human-readable long name the catalog and decoder use when surfacing the field to operators. The long name is the variable name the field would have unabbreviated, written in camelCase (e.g. `uid` → `userUid`, `n` → `name`, `crAt` → `createdAt`).",
|
|
503
|
-
"whenItDoesNotApply": "Fields on embedded sub-object interfaces
|
|
502
|
+
"whenItApplies": "Every field on a `@dbxModel` interface or on a `@dbxModelSubObject`-tagged embedded sub-object interface — the tag carries the human-readable long name the catalog and decoder use when surfacing the field to operators. The long name is the variable name the field would have unabbreviated, written in camelCase (e.g. `uid` → `userUid`, `n` → `name`, `crAt` → `createdAt`).",
|
|
503
|
+
"whenItDoesNotApply": "Fields on embedded sub-object interfaces that are not tagged with `@dbxModelSubObject` (the tag is opt-in) and fields the project deliberately leaves untagged (rare).",
|
|
504
504
|
"canonicalFix": "Append `@dbxModelVariable <longName>` to the field's JSDoc block, where `<longName>` is the field's unabbreviated camelCase variable name."
|
|
505
505
|
},
|
|
506
506
|
{
|
|
@@ -687,6 +687,32 @@
|
|
|
687
687
|
"whenItDoesNotApply": "When intentionally module-private.",
|
|
688
688
|
"canonicalFix": "Add `export` to the alias."
|
|
689
689
|
},
|
|
690
|
+
{
|
|
691
|
+
"code": "MODEL_SUBOBJECT_NOT_TAGGED",
|
|
692
|
+
"source": "dbx_model_validate",
|
|
693
|
+
"severity": "warning",
|
|
694
|
+
"title": "An interface is referenced as the first generic type-argument of a sub-object factory call (`firestoreSubObject<T>(...)`, `firestoreObjectArray<T>(...)`, or `firestoreMap<T>(...)`) but its declaration carries neither `@dbxModel` nor `@dbxModelSubObject`. The interface therefore opts out of the workspace's `@dbxModelVariable` long-name conventions even though its fields are persisted to Firestore",
|
|
695
|
+
"whatItFlags": "An interface is referenced as the first generic type-argument of a sub-object factory call (`firestoreSubObject<T>(...)`, `firestoreObjectArray<T>(...)`, or `firestoreMap<T>(...)`) but its declaration carries neither `@dbxModel` nor `@dbxModelSubObject`. The interface therefore opts out of the workspace's `@dbxModelVariable` long-name conventions even though its fields are persisted to Firestore.",
|
|
696
|
+
"whenItApplies": "Any interface declared in the validated source set that is the type-arg of at least one sub-object factory call and lacks both `@dbxModel` and `@dbxModelSubObject`. The warning is emitted once per interface (de-duplicated across all call sites) and anchored at the interface declaration line. Unresolved type-args — interfaces declared outside the validated source set, generic parameters, inline types — are silently skipped to avoid false positives.",
|
|
697
|
+
"whenItDoesNotApply": "Interfaces already tagged with `@dbxModel` (top-level models) or `@dbxModelSubObject` (embedded sub-objects), and interfaces referenced only from outside the supplied source set (different package, etc.).",
|
|
698
|
+
"canonicalFix": "Add `@dbxModelSubObject` to the interface's JSDoc block so its fields are subject to `@dbxModelVariable` long-name checks. If the interface is actually a top-level Firestore model, declare a matching `firestoreModelIdentity` and tag it `@dbxModel` instead.",
|
|
699
|
+
"seeAlso": [
|
|
700
|
+
{
|
|
701
|
+
"kind": "artifact",
|
|
702
|
+
"target": "firestore-model"
|
|
703
|
+
}
|
|
704
|
+
]
|
|
705
|
+
},
|
|
706
|
+
{
|
|
707
|
+
"code": "MODEL_SUBOBJECT_TAG_CONFLICT",
|
|
708
|
+
"source": "dbx_model_validate",
|
|
709
|
+
"severity": "error",
|
|
710
|
+
"title": "An interface declaration carries both `@dbxModel` and `@dbxModelSubObject` JSDoc tags. The two tags model mutually exclusive concepts — `@dbxModel` marks a top-level Firestore model registered in the catalog via `firestoreModelIdentity`, while `@dbxModelSubObject` marks an embedded sub-object interface persisted only as part of a parent model's converter — so an interface must carry at most one",
|
|
711
|
+
"whatItFlags": "An interface declaration carries both `@dbxModel` and `@dbxModelSubObject` JSDoc tags. The two tags model mutually exclusive concepts — `@dbxModel` marks a top-level Firestore model registered in the catalog via `firestoreModelIdentity`, while `@dbxModelSubObject` marks an embedded sub-object interface persisted only as part of a parent model's converter — so an interface must carry at most one.",
|
|
712
|
+
"whenItApplies": "Any interface JSDoc block where both `@dbxModel` and `@dbxModelSubObject` are present.",
|
|
713
|
+
"whenItDoesNotApply": "Interfaces carrying only one of the two tags (or neither).",
|
|
714
|
+
"canonicalFix": "Remove whichever tag does not describe the interface. If the interface has a matching `firestoreModelIdentity` call, keep `@dbxModel`; if it is only embedded inside another model via `firestoreSubObject<T>()`, keep `@dbxModelSubObject`."
|
|
715
|
+
},
|
|
690
716
|
{
|
|
691
717
|
"code": "SUB_MISSING_COLLECTION_FACTORY_FN",
|
|
692
718
|
"source": "dbx_model_validate",
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@dereekb/dbx-components-mcp",
|
|
3
|
-
"version": "13.11.
|
|
3
|
+
"version": "13.11.7",
|
|
4
4
|
"type": "module",
|
|
5
5
|
"bin": {
|
|
6
6
|
"dbx-components-mcp": "dbx-components-mcp.js"
|
|
@@ -15,19 +15,19 @@
|
|
|
15
15
|
"@angular/core": "21.2.11",
|
|
16
16
|
"@angular/forms": "21.2.11",
|
|
17
17
|
"@angular/material": "^21.2.9",
|
|
18
|
-
"@dereekb/dbx-cli": "13.11.
|
|
19
|
-
"@dereekb/dbx-core": "13.11.
|
|
20
|
-
"@dereekb/dbx-firebase": "13.11.
|
|
21
|
-
"@dereekb/dbx-form": "13.11.
|
|
22
|
-
"@dereekb/dbx-web": "13.11.
|
|
23
|
-
"@dereekb/firebase": "13.11.
|
|
24
|
-
"@dereekb/firebase-server": "13.11.
|
|
25
|
-
"@dereekb/rxjs": "13.11.
|
|
26
|
-
"@dereekb/util": "13.11.
|
|
18
|
+
"@dereekb/dbx-cli": "13.11.7",
|
|
19
|
+
"@dereekb/dbx-core": "13.11.7",
|
|
20
|
+
"@dereekb/dbx-firebase": "13.11.7",
|
|
21
|
+
"@dereekb/dbx-form": "13.11.7",
|
|
22
|
+
"@dereekb/dbx-web": "13.11.7",
|
|
23
|
+
"@dereekb/firebase": "13.11.7",
|
|
24
|
+
"@dereekb/firebase-server": "13.11.7",
|
|
25
|
+
"@dereekb/rxjs": "13.11.7",
|
|
26
|
+
"@dereekb/util": "13.11.7",
|
|
27
27
|
"@uirouter/angular": "21.0.0",
|
|
28
28
|
"rxjs": "^7.8.2"
|
|
29
29
|
},
|
|
30
30
|
"devDependencies": {
|
|
31
|
-
"@dereekb/model": "13.11.
|
|
31
|
+
"@dereekb/model": "13.11.7"
|
|
32
32
|
}
|
|
33
33
|
}
|