@dereekb/dbx-components-mcp 13.11.6 → 13.11.8
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 +968 -336
- package/generated/rule-catalog.generated.json +45 -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.8",
|
|
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.8",
|
|
24
|
+
"@dereekb/dbx-core": "13.11.8",
|
|
25
|
+
"@dereekb/dbx-firebase": "13.11.8",
|
|
26
|
+
"@dereekb/dbx-form": "13.11.8",
|
|
27
|
+
"@dereekb/dbx-web": "13.11.8",
|
|
28
|
+
"@dereekb/firebase": "13.11.8",
|
|
29
|
+
"@dereekb/firebase-server": "13.11.8",
|
|
30
|
+
"@dereekb/rxjs": "13.11.8",
|
|
31
|
+
"@dereekb/util": "13.11.8",
|
|
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.8"
|
|
37
37
|
}
|
|
38
38
|
};
|
|
39
39
|
|
|
@@ -3253,7 +3253,8 @@ var DbxMcpConfig = type16({
|
|
|
3253
3253
|
version: "1",
|
|
3254
3254
|
"modelValidate?": {
|
|
3255
3255
|
"maxFieldNameLength?": "number>=1",
|
|
3256
|
-
"ignoredFieldNames?": "string[]"
|
|
3256
|
+
"ignoredFieldNames?": "string[]",
|
|
3257
|
+
"ignoredExternalParents?": "string[]"
|
|
3257
3258
|
},
|
|
3258
3259
|
"semanticTypes?": ClusterSection,
|
|
3259
3260
|
"uiComponents?": ClusterSection,
|
|
@@ -8066,7 +8067,9 @@ function assembleFile(input) {
|
|
|
8066
8067
|
const fields = buildFields({
|
|
8067
8068
|
converter,
|
|
8068
8069
|
inheritedProps,
|
|
8069
|
-
enumNames
|
|
8070
|
+
enumNames,
|
|
8071
|
+
subObjectConstIndex: input.subObjectConstIndex,
|
|
8072
|
+
subObjectInterfaceIndex: input.subObjectInterfaceIndex
|
|
8070
8073
|
});
|
|
8071
8074
|
const referencedEnums = new Set(fields.flatMap((f) => f.enumRef ? [f.enumRef] : []));
|
|
8072
8075
|
const relevantEnums = input.enums.filter((e) => referencedEnums.has(e.name));
|
|
@@ -8119,6 +8122,12 @@ function buildFields(input) {
|
|
|
8119
8122
|
const enumRef = enumFromType ?? enumFromConverter;
|
|
8120
8123
|
const optional = prop?.optional ?? f.converter.startsWith("optionalFirestore");
|
|
8121
8124
|
const longName = resolveLongName(f.key, prop?.longName);
|
|
8125
|
+
const subObject = resolveFieldSubObject({
|
|
8126
|
+
converter: f.converter,
|
|
8127
|
+
subObjectConstIndex: input.subObjectConstIndex,
|
|
8128
|
+
subObjectInterfaceIndex: input.subObjectInterfaceIndex,
|
|
8129
|
+
enumNames: input.enumNames
|
|
8130
|
+
});
|
|
8122
8131
|
const field = {
|
|
8123
8132
|
name: f.key,
|
|
8124
8133
|
longName,
|
|
@@ -8127,12 +8136,126 @@ function buildFields(input) {
|
|
|
8127
8136
|
optional,
|
|
8128
8137
|
...prop?.description ? { description: prop.description } : {},
|
|
8129
8138
|
...enumRef ? { enumRef } : {},
|
|
8130
|
-
...prop?.syncFlag ? { syncFlag: prop.syncFlag } : {}
|
|
8139
|
+
...prop?.syncFlag ? { syncFlag: prop.syncFlag } : {},
|
|
8140
|
+
...subObject ? { subObject } : {}
|
|
8131
8141
|
};
|
|
8132
8142
|
out.push(field);
|
|
8133
8143
|
}
|
|
8134
8144
|
return out;
|
|
8135
8145
|
}
|
|
8146
|
+
function resolveFieldSubObject(input) {
|
|
8147
|
+
const { converter, subObjectConstIndex, subObjectInterfaceIndex, enumNames } = input;
|
|
8148
|
+
if (!subObjectConstIndex || !subObjectInterfaceIndex) {
|
|
8149
|
+
return void 0;
|
|
8150
|
+
}
|
|
8151
|
+
const resolved = resolveSubObjectReference(converter, subObjectConstIndex);
|
|
8152
|
+
if (!resolved) {
|
|
8153
|
+
return void 0;
|
|
8154
|
+
}
|
|
8155
|
+
const iface = subObjectInterfaceIndex.get(resolved.interfaceName);
|
|
8156
|
+
if (!iface) {
|
|
8157
|
+
return void 0;
|
|
8158
|
+
}
|
|
8159
|
+
const fields = buildSubObjectFields({ iface, subObjectConstIndex, subObjectInterfaceIndex, enumNames, visited: /* @__PURE__ */ new Set([resolved.interfaceName]) });
|
|
8160
|
+
return { interfaceName: resolved.interfaceName, factoryKind: resolved.factoryKind, fields };
|
|
8161
|
+
}
|
|
8162
|
+
var INLINE_SUB_OBJECT_RE = /^(firestoreSubObject|firestoreObjectArray|firestoreMap)\s*<\s*([A-Za-z_$][A-Za-z0-9_$]*)\s*>/;
|
|
8163
|
+
var WRAPPED_OBJECT_FIELD_RE = /^(firestoreObjectArray|firestoreMap)\s*\(\s*\{\s*objectField:\s*([A-Za-z_$][A-Za-z0-9_$]*)/;
|
|
8164
|
+
function resolveSubObjectReference(converter, index) {
|
|
8165
|
+
let result;
|
|
8166
|
+
const trimmed = converter.trim();
|
|
8167
|
+
const inline = INLINE_SUB_OBJECT_RE.exec(trimmed);
|
|
8168
|
+
if (inline) {
|
|
8169
|
+
result = { interfaceName: inline[2], factoryKind: FACTORY_KIND_BY_NAME[inline[1]] };
|
|
8170
|
+
} else {
|
|
8171
|
+
const wrapped = WRAPPED_OBJECT_FIELD_RE.exec(trimmed);
|
|
8172
|
+
if (wrapped) {
|
|
8173
|
+
const entry = index.get(wrapped[2]);
|
|
8174
|
+
if (entry) {
|
|
8175
|
+
const outerKind = FACTORY_KIND_BY_NAME[wrapped[1]];
|
|
8176
|
+
result = { interfaceName: entry.interfaceName, factoryKind: outerKind };
|
|
8177
|
+
}
|
|
8178
|
+
} else {
|
|
8179
|
+
const entry = index.get(trimmed);
|
|
8180
|
+
if (entry) {
|
|
8181
|
+
result = { interfaceName: entry.interfaceName, factoryKind: entry.factoryKind };
|
|
8182
|
+
}
|
|
8183
|
+
}
|
|
8184
|
+
}
|
|
8185
|
+
return result;
|
|
8186
|
+
}
|
|
8187
|
+
var FACTORY_KIND_BY_NAME = {
|
|
8188
|
+
firestoreSubObject: "object",
|
|
8189
|
+
firestoreObjectArray: "array",
|
|
8190
|
+
firestoreMap: "map"
|
|
8191
|
+
};
|
|
8192
|
+
function buildSubObjectFields(input) {
|
|
8193
|
+
const out = [];
|
|
8194
|
+
for (const prop of input.iface.props) {
|
|
8195
|
+
out.push(buildSubObjectField({ prop, ...input }));
|
|
8196
|
+
}
|
|
8197
|
+
return out;
|
|
8198
|
+
}
|
|
8199
|
+
function buildSubObjectField(input) {
|
|
8200
|
+
const { prop, subObjectConstIndex, subObjectInterfaceIndex, enumNames, visited } = input;
|
|
8201
|
+
const enumRef = prop.tsType ? findEnumInType(prop.tsType, enumNames) : void 0;
|
|
8202
|
+
const longName = resolveLongName(prop.name, prop.longName);
|
|
8203
|
+
const nested = resolveNestedSubObjectFromType({ tsType: prop.tsType, subObjectConstIndex, subObjectInterfaceIndex, enumNames, visited });
|
|
8204
|
+
const field = {
|
|
8205
|
+
name: prop.name,
|
|
8206
|
+
longName,
|
|
8207
|
+
converter: "",
|
|
8208
|
+
...prop.tsType ? { tsType: prop.tsType } : {},
|
|
8209
|
+
optional: prop.optional,
|
|
8210
|
+
...prop.description ? { description: prop.description } : {},
|
|
8211
|
+
...enumRef ? { enumRef } : {},
|
|
8212
|
+
...prop.syncFlag ? { syncFlag: prop.syncFlag } : {},
|
|
8213
|
+
...nested ? { subObject: nested } : {}
|
|
8214
|
+
};
|
|
8215
|
+
return field;
|
|
8216
|
+
}
|
|
8217
|
+
function resolveNestedSubObjectFromType(input) {
|
|
8218
|
+
const { tsType, subObjectInterfaceIndex, visited } = input;
|
|
8219
|
+
if (!tsType) return void 0;
|
|
8220
|
+
const peeled = peelArrayAndMaybe(tsType.trim());
|
|
8221
|
+
if (!peeled) return void 0;
|
|
8222
|
+
if (visited.has(peeled.name)) return void 0;
|
|
8223
|
+
const iface = subObjectInterfaceIndex.get(peeled.name);
|
|
8224
|
+
if (!iface) return void 0;
|
|
8225
|
+
const nestedVisited = new Set(visited);
|
|
8226
|
+
nestedVisited.add(peeled.name);
|
|
8227
|
+
const fields = buildSubObjectFields({
|
|
8228
|
+
iface,
|
|
8229
|
+
subObjectConstIndex: input.subObjectConstIndex,
|
|
8230
|
+
subObjectInterfaceIndex,
|
|
8231
|
+
enumNames: input.enumNames,
|
|
8232
|
+
visited: nestedVisited
|
|
8233
|
+
});
|
|
8234
|
+
return { interfaceName: peeled.name, factoryKind: peeled.array ? "array" : "object", fields };
|
|
8235
|
+
}
|
|
8236
|
+
function peelArrayAndMaybe(raw) {
|
|
8237
|
+
let current = raw;
|
|
8238
|
+
let array = false;
|
|
8239
|
+
const maybeMatch = /^Maybe<\s*(.+)\s*>$/.exec(current);
|
|
8240
|
+
if (maybeMatch) {
|
|
8241
|
+
current = maybeMatch[1].trim();
|
|
8242
|
+
}
|
|
8243
|
+
const readonlyArrayMatch = /^ReadonlyArray<\s*(.+)\s*>$/.exec(current);
|
|
8244
|
+
if (readonlyArrayMatch) {
|
|
8245
|
+
current = readonlyArrayMatch[1].trim();
|
|
8246
|
+
array = true;
|
|
8247
|
+
} else {
|
|
8248
|
+
const arrayMatch = /^(?:readonly\s+)?(.+?)(\s*\[\s*\])$/.exec(current);
|
|
8249
|
+
if (arrayMatch) {
|
|
8250
|
+
current = arrayMatch[1].trim();
|
|
8251
|
+
array = true;
|
|
8252
|
+
}
|
|
8253
|
+
}
|
|
8254
|
+
if (!/^[A-Z][A-Za-z0-9_$]*$/.test(current)) {
|
|
8255
|
+
return void 0;
|
|
8256
|
+
}
|
|
8257
|
+
return { name: current, array };
|
|
8258
|
+
}
|
|
8136
8259
|
function resolveLongName(fieldName, propLongName) {
|
|
8137
8260
|
let result;
|
|
8138
8261
|
if (propLongName && LONG_NAME_RE.test(propLongName)) {
|
|
@@ -8314,14 +8437,18 @@ function peelTypeNode(node) {
|
|
|
8314
8437
|
}
|
|
8315
8438
|
function readInterfaceTags(jsDocs) {
|
|
8316
8439
|
let dbxModel = false;
|
|
8440
|
+
let dbxModelSubObject = false;
|
|
8317
8441
|
for (const jsDoc of jsDocs) {
|
|
8318
8442
|
for (const tag of jsDoc.getTags()) {
|
|
8319
|
-
|
|
8443
|
+
const tagName = tag.getTagName();
|
|
8444
|
+
if (tagName === "dbxModel") {
|
|
8320
8445
|
dbxModel = true;
|
|
8446
|
+
} else if (tagName === "dbxModelSubObject") {
|
|
8447
|
+
dbxModelSubObject = true;
|
|
8321
8448
|
}
|
|
8322
8449
|
}
|
|
8323
8450
|
}
|
|
8324
|
-
return { dbxModel };
|
|
8451
|
+
return { dbxModel, dbxModelSubObject };
|
|
8325
8452
|
}
|
|
8326
8453
|
function readPropertyTags2(jsDocs) {
|
|
8327
8454
|
let dbxModelVariable;
|
|
@@ -8526,6 +8653,40 @@ function extractGroupModelNames(text) {
|
|
|
8526
8653
|
return [...seen].sort((a, b) => a.localeCompare(b));
|
|
8527
8654
|
}
|
|
8528
8655
|
|
|
8656
|
+
// packages/dbx-components-mcp/src/scan/extract-models/find-sub-object-consts.ts
|
|
8657
|
+
import { Node as Node11 } from "ts-morph";
|
|
8658
|
+
var SUB_OBJECT_FACTORY_NAMES = /* @__PURE__ */ new Set(["firestoreSubObject", "firestoreObjectArray", "firestoreMap"]);
|
|
8659
|
+
function findSubObjectConsts(sf) {
|
|
8660
|
+
const out = [];
|
|
8661
|
+
for (const statement of sf.getVariableStatements()) {
|
|
8662
|
+
for (const decl of statement.getDeclarations()) {
|
|
8663
|
+
const entry = readSubObjectConst(decl);
|
|
8664
|
+
if (entry) out.push(entry);
|
|
8665
|
+
}
|
|
8666
|
+
}
|
|
8667
|
+
return out;
|
|
8668
|
+
}
|
|
8669
|
+
function readSubObjectConst(decl) {
|
|
8670
|
+
const initializer = decl.getInitializer();
|
|
8671
|
+
if (!initializer || !Node11.isCallExpression(initializer)) return void 0;
|
|
8672
|
+
const expr = initializer.getExpression();
|
|
8673
|
+
if (!Node11.isIdentifier(expr)) return void 0;
|
|
8674
|
+
const factoryName = expr.getText();
|
|
8675
|
+
if (!SUB_OBJECT_FACTORY_NAMES.has(factoryName)) return void 0;
|
|
8676
|
+
const interfaceName = readInterfaceTypeArg2(initializer);
|
|
8677
|
+
if (!interfaceName) return void 0;
|
|
8678
|
+
return { constName: decl.getName(), interfaceName, factoryName };
|
|
8679
|
+
}
|
|
8680
|
+
function readInterfaceTypeArg2(call) {
|
|
8681
|
+
const typeArgs = call.getTypeArguments();
|
|
8682
|
+
if (typeArgs.length === 0) return void 0;
|
|
8683
|
+
const first = typeArgs[0];
|
|
8684
|
+
if (!Node11.isTypeReference(first)) return void 0;
|
|
8685
|
+
const nameNode = first.getTypeName();
|
|
8686
|
+
if (!Node11.isIdentifier(nameNode)) return void 0;
|
|
8687
|
+
return nameNode.getText();
|
|
8688
|
+
}
|
|
8689
|
+
|
|
8529
8690
|
// packages/dbx-components-mcp/src/scan/extract-models/infer-collection-kind.ts
|
|
8530
8691
|
var COLLECTION_CALL_RE = /\.(firestoreCollection|rootSingleItemFirestoreCollection|firestoreCollectionWithParent|singleItemFirestoreCollection)\s*\(/g;
|
|
8531
8692
|
var KIND_BY_METHOD = {
|
|
@@ -8571,14 +8732,37 @@ async function extractModels(input) {
|
|
|
8571
8732
|
const models = [];
|
|
8572
8733
|
const modelGroups = [];
|
|
8573
8734
|
const errors = [];
|
|
8735
|
+
const subObjectConstIndex = /* @__PURE__ */ new Map();
|
|
8736
|
+
const subObjectInterfaceIndex = /* @__PURE__ */ new Map();
|
|
8737
|
+
const parsedFiles = [];
|
|
8574
8738
|
for (const filePath of files) {
|
|
8575
8739
|
const sourceFileRel = relative3(baseDir, filePath).split(sep3).join(posix.sep);
|
|
8576
8740
|
try {
|
|
8577
8741
|
const text = await readFile2(filePath, "utf8");
|
|
8578
|
-
|
|
8742
|
+
const hasModelMarker = text.includes("firestoreModelIdentity(") || text.includes("@dbxModelGroup");
|
|
8743
|
+
const hasSubObjectMarker = text.includes("@dbxModelSubObject") || text.includes("firestoreSubObject") || text.includes("firestoreObjectArray") || text.includes("firestoreMap");
|
|
8744
|
+
if (!hasModelMarker && !hasSubObjectMarker) {
|
|
8579
8745
|
continue;
|
|
8580
8746
|
}
|
|
8581
|
-
const sf = project.createSourceFile(`/scan/${basename2(filePath)}-${
|
|
8747
|
+
const sf = project.createSourceFile(`/scan/${basename2(filePath)}-${parsedFiles.length}.ts`, text, { overwrite: true });
|
|
8748
|
+
parsedFiles.push({ filePath, sourceFileRel, sf, hasModelMarker });
|
|
8749
|
+
for (const c of findSubObjectConsts(sf)) {
|
|
8750
|
+
if (!subObjectConstIndex.has(c.constName)) {
|
|
8751
|
+
subObjectConstIndex.set(c.constName, { interfaceName: c.interfaceName, factoryKind: c.factoryName === "firestoreSubObject" ? "object" : c.factoryName === "firestoreObjectArray" ? "array" : "map" });
|
|
8752
|
+
}
|
|
8753
|
+
}
|
|
8754
|
+
for (const iface of findInterfaces(sf)) {
|
|
8755
|
+
if (iface.tags.dbxModelSubObject && !subObjectInterfaceIndex.has(iface.name)) {
|
|
8756
|
+
subObjectInterfaceIndex.set(iface.name, iface);
|
|
8757
|
+
}
|
|
8758
|
+
}
|
|
8759
|
+
} catch (error) {
|
|
8760
|
+
errors.push({ sourceFile: sourceFileRel, message: error instanceof Error ? error.message : String(error) });
|
|
8761
|
+
}
|
|
8762
|
+
}
|
|
8763
|
+
for (const { sourceFileRel, sf, hasModelMarker } of parsedFiles) {
|
|
8764
|
+
if (!hasModelMarker) continue;
|
|
8765
|
+
try {
|
|
8582
8766
|
const assembled = assembleFile({
|
|
8583
8767
|
sourcePackage,
|
|
8584
8768
|
sourceFile: sourceFileRel,
|
|
@@ -8587,7 +8771,9 @@ async function extractModels(input) {
|
|
|
8587
8771
|
converters: findConverters(sf),
|
|
8588
8772
|
enums: findEnums(sf),
|
|
8589
8773
|
modelGroups: findModelGroups(sf),
|
|
8590
|
-
factoryKinds: findCollectionFactoryCalls(sf)
|
|
8774
|
+
factoryKinds: findCollectionFactoryCalls(sf),
|
|
8775
|
+
subObjectConstIndex,
|
|
8776
|
+
subObjectInterfaceIndex
|
|
8591
8777
|
});
|
|
8592
8778
|
for (const m of assembled.models) models.push(m);
|
|
8593
8779
|
for (const g of assembled.modelGroups) modelGroups.push(g);
|
|
@@ -13041,15 +13227,44 @@ function applyFieldsFilter(model, filter) {
|
|
|
13041
13227
|
for (const field of model.fields) {
|
|
13042
13228
|
const nameLower = field.name.toLowerCase();
|
|
13043
13229
|
const longNameLower = field.longName.toLowerCase();
|
|
13044
|
-
|
|
13230
|
+
let keep = false;
|
|
13231
|
+
if (filterSet.has(nameLower)) {
|
|
13232
|
+
matched.add(nameLower);
|
|
13233
|
+
keep = true;
|
|
13234
|
+
}
|
|
13235
|
+
if (filterSet.has(longNameLower)) {
|
|
13236
|
+
matched.add(longNameLower);
|
|
13237
|
+
keep = true;
|
|
13238
|
+
}
|
|
13239
|
+
if (field.subObject !== void 0 && subObjectMatches(field.subObject, filterSet, matched)) {
|
|
13240
|
+
keep = true;
|
|
13241
|
+
}
|
|
13242
|
+
if (keep) {
|
|
13045
13243
|
kept.push(field);
|
|
13046
|
-
if (filterSet.has(nameLower)) matched.add(nameLower);
|
|
13047
|
-
if (filterSet.has(longNameLower)) matched.add(longNameLower);
|
|
13048
13244
|
}
|
|
13049
13245
|
}
|
|
13050
13246
|
const unmatchedFilters = filter.filter((entry) => !matched.has(entry));
|
|
13051
13247
|
return { kept, unmatchedFilters };
|
|
13052
13248
|
}
|
|
13249
|
+
function subObjectMatches(subObject, filterSet, matched) {
|
|
13250
|
+
let any = false;
|
|
13251
|
+
for (const field of subObject.fields) {
|
|
13252
|
+
const nameLower = field.name.toLowerCase();
|
|
13253
|
+
const longNameLower = field.longName.toLowerCase();
|
|
13254
|
+
if (filterSet.has(nameLower)) {
|
|
13255
|
+
matched.add(nameLower);
|
|
13256
|
+
any = true;
|
|
13257
|
+
}
|
|
13258
|
+
if (filterSet.has(longNameLower)) {
|
|
13259
|
+
matched.add(longNameLower);
|
|
13260
|
+
any = true;
|
|
13261
|
+
}
|
|
13262
|
+
if (field.subObject !== void 0 && subObjectMatches(field.subObject, filterSet, matched)) {
|
|
13263
|
+
any = true;
|
|
13264
|
+
}
|
|
13265
|
+
}
|
|
13266
|
+
return any;
|
|
13267
|
+
}
|
|
13053
13268
|
function firebaseModelStoreShape(model) {
|
|
13054
13269
|
const result = model.parentIdentityConst ? "sub-collection" : "root";
|
|
13055
13270
|
return result;
|
|
@@ -13098,18 +13313,66 @@ function appendFieldsTable(lines, fields, depth) {
|
|
|
13098
13313
|
lines.push("| Field | Description |", "|-------|-------------|");
|
|
13099
13314
|
for (const field of fields) {
|
|
13100
13315
|
const desc = describeField(field);
|
|
13101
|
-
lines.push(`| \`${field
|
|
13316
|
+
lines.push(`| \`${formatFieldLabel(field)}\` | ${desc} |`);
|
|
13102
13317
|
}
|
|
13103
13318
|
} else {
|
|
13104
13319
|
lines.push("| Field | Description | Type | Converter |", "|-------|-------------|------|-----------|");
|
|
13105
13320
|
for (const field of fields) {
|
|
13106
13321
|
const desc = describeField(field);
|
|
13107
13322
|
const ts = field.tsType ? `\`${field.tsType}\`` : "\u2013";
|
|
13108
|
-
const conv = `\`${field.converter}
|
|
13109
|
-
lines.push(`| \`${field
|
|
13323
|
+
const conv = field.converter.length > 0 ? `\`${field.converter}\`` : "\u2013";
|
|
13324
|
+
lines.push(`| \`${formatFieldLabel(field)}\` | ${desc} | ${ts} | ${conv} |`);
|
|
13110
13325
|
}
|
|
13111
13326
|
}
|
|
13327
|
+
appendSubObjectSections(lines, fields, depth);
|
|
13328
|
+
}
|
|
13329
|
+
function formatFieldLabel(field) {
|
|
13330
|
+
if (field.longName && field.longName !== field.name) {
|
|
13331
|
+
return `${field.name} (${field.longName})`;
|
|
13332
|
+
}
|
|
13333
|
+
return field.name;
|
|
13334
|
+
}
|
|
13335
|
+
function appendSubObjectSections(lines, fields, depth) {
|
|
13336
|
+
for (const field of fields) {
|
|
13337
|
+
if (field.subObject === void 0) continue;
|
|
13338
|
+
appendSubObjectSection({ lines, parentLabel: formatFieldLabel(field), subObject: field.subObject, depth, headingLevel: 3 });
|
|
13339
|
+
}
|
|
13112
13340
|
}
|
|
13341
|
+
function appendSubObjectSection(input) {
|
|
13342
|
+
const { lines, parentLabel, subObject, depth, headingLevel } = input;
|
|
13343
|
+
const heading = "#".repeat(Math.min(Math.max(headingLevel, 2), 6));
|
|
13344
|
+
const kindLabel = SUB_OBJECT_KIND_LABEL[subObject.factoryKind];
|
|
13345
|
+
lines.push("", `${heading} Sub-object: \`${parentLabel}\` \u2192 \`${subObject.interfaceName}\` (${kindLabel})`, "");
|
|
13346
|
+
if (depth === "brief") {
|
|
13347
|
+
lines.push("| Field | Description |", "|-------|-------------|");
|
|
13348
|
+
for (const field of subObject.fields) {
|
|
13349
|
+
const desc = describeField(field);
|
|
13350
|
+
lines.push(`| \`${formatFieldLabel(field)}\` | ${desc} |`);
|
|
13351
|
+
}
|
|
13352
|
+
} else {
|
|
13353
|
+
lines.push("| Field | Description | Type |", "|-------|-------------|------|");
|
|
13354
|
+
for (const field of subObject.fields) {
|
|
13355
|
+
const desc = describeField(field);
|
|
13356
|
+
const ts = field.tsType ? `\`${field.tsType}\`` : "\u2013";
|
|
13357
|
+
lines.push(`| \`${formatFieldLabel(field)}\` | ${desc} | ${ts} |`);
|
|
13358
|
+
}
|
|
13359
|
+
}
|
|
13360
|
+
for (const field of subObject.fields) {
|
|
13361
|
+
if (field.subObject === void 0) continue;
|
|
13362
|
+
appendSubObjectSection({
|
|
13363
|
+
lines,
|
|
13364
|
+
parentLabel: `${parentLabel}.${formatFieldLabel(field)}`,
|
|
13365
|
+
subObject: field.subObject,
|
|
13366
|
+
depth,
|
|
13367
|
+
headingLevel: headingLevel + 1
|
|
13368
|
+
});
|
|
13369
|
+
}
|
|
13370
|
+
}
|
|
13371
|
+
var SUB_OBJECT_KIND_LABEL = {
|
|
13372
|
+
object: "embedded object",
|
|
13373
|
+
array: "embedded array",
|
|
13374
|
+
map: "embedded map"
|
|
13375
|
+
};
|
|
13113
13376
|
function describeField(field) {
|
|
13114
13377
|
return (field.description ?? "\u2013").replaceAll("|", String.raw`\|`).replaceAll("\n", " ");
|
|
13115
13378
|
}
|
|
@@ -13407,7 +13670,7 @@ var DBX_MODEL_LOOKUP_TOOL = {
|
|
|
13407
13670
|
"Optional inputs:",
|
|
13408
13671
|
' \u2022 `scope`: `"all"` (default), `"upstream"` (only `@dereekb/firebase`), or `"downstream"` (only component packages).',
|
|
13409
13672
|
" \u2022 `componentDirs`: explicit downstream package directories \u2014 overrides the default `components/*-firebase` discovery.",
|
|
13410
|
-
' \u2022 `fields`: when the topic resolves to a single model, restrict the rendered fields table to those whose persisted name (e.g. `"fs"`) or longName (e.g. `"fileState"`) appears in the list (case-insensitive). Enums prune to only those referenced by the kept fields. Ignored on catalog/shapes/not-found responses.'
|
|
13673
|
+
' \u2022 `fields`: when the topic resolves to a single model, restrict the rendered fields table to those whose persisted name (e.g. `"fs"`) or longName (e.g. `"fileState"`) appears in the list (case-insensitive). Filters also match against `@dbxModelSubObject` field names / longNames reachable through a parent field\'s `firestoreSubObject<T>` / `firestoreObjectArray<T>` / `firestoreMap<T>` converter \u2014 the parent field is kept whenever any of its nested sub-object fields match. Enums prune to only those referenced by the kept fields. Ignored on catalog/shapes/not-found responses.'
|
|
13411
13674
|
].join("\n"),
|
|
13412
13675
|
inputSchema: {
|
|
13413
13676
|
type: "object",
|
|
@@ -13950,29 +14213,35 @@ function matchPrefix(value, prefixes) {
|
|
|
13950
14213
|
var DBX_MODEL_DECODE_TOOL = {
|
|
13951
14214
|
name: "dbx_model_decode",
|
|
13952
14215
|
description: [
|
|
13953
|
-
"Decode a
|
|
14216
|
+
"Decode a @dereekb/firebase Firestore document or model key.",
|
|
13954
14217
|
"",
|
|
13955
|
-
'
|
|
14218
|
+
'Document mode \u2014 pass `data` as a JSON string (copied straight from the Firestore console) or an already-parsed object to identify the model, expand abbreviated field names, decode enum integer values, and surface foreign-key relationships. Optionally pass `model` to skip detection and target a specific model by interface name (`"StorageFile"`), identity constant (`"storageFileIdentity"`), `modelType` (`"storageFile"`), or collection prefix (`"sf"`).',
|
|
13956
14219
|
"",
|
|
13957
|
-
"
|
|
14220
|
+
'Key mode \u2014 pass just `key` (e.g. `"jwr/hkzQa9W6MpaP99RTlQJcImbWdZm2"`) to resolve the model identity and id from the collection prefix alone, no document required. Subcollection paths like `"nb/abc/nbn/def"` are walked end-to-end so the leaf model is reported alongside its parent chain.',
|
|
14221
|
+
"",
|
|
14222
|
+
"At least one of `data` or `key` is required."
|
|
13958
14223
|
].join("\n"),
|
|
13959
14224
|
inputSchema: {
|
|
13960
14225
|
type: "object",
|
|
13961
14226
|
properties: {
|
|
13962
14227
|
data: {
|
|
13963
|
-
description: "Raw Firestore document \u2014 JSON string or already-parsed object.",
|
|
14228
|
+
description: "Raw Firestore document \u2014 JSON string or already-parsed object. Required for document mode.",
|
|
13964
14229
|
oneOf: [{ type: "string" }, { type: "object" }]
|
|
13965
14230
|
},
|
|
14231
|
+
key: {
|
|
14232
|
+
type: "string",
|
|
14233
|
+
description: 'Firestore model key (e.g. `"sf/abc123"` or subcollection path `"nb/abc/nbn/def"`). Used as the sole input in key mode, or as an extra detection hint when paired with `data`.'
|
|
14234
|
+
},
|
|
13966
14235
|
model: {
|
|
13967
14236
|
type: "string",
|
|
13968
14237
|
description: "Optional model hint \u2014 interface name, identity const, model type, or collection prefix."
|
|
13969
14238
|
}
|
|
13970
|
-
}
|
|
13971
|
-
required: ["data"]
|
|
14239
|
+
}
|
|
13972
14240
|
}
|
|
13973
14241
|
};
|
|
13974
14242
|
var DecodeArgsType = type36({
|
|
13975
|
-
data: "string | object",
|
|
14243
|
+
"data?": "string | object",
|
|
14244
|
+
"key?": "string",
|
|
13976
14245
|
"model?": "string"
|
|
13977
14246
|
});
|
|
13978
14247
|
function parseDecodeArgs(raw) {
|
|
@@ -13982,6 +14251,7 @@ function parseDecodeArgs(raw) {
|
|
|
13982
14251
|
}
|
|
13983
14252
|
const result = {
|
|
13984
14253
|
data: parsed.data,
|
|
14254
|
+
key: parsed.key,
|
|
13985
14255
|
model: parsed.model
|
|
13986
14256
|
};
|
|
13987
14257
|
return result;
|
|
@@ -14055,6 +14325,78 @@ function buildPrefixMap() {
|
|
|
14055
14325
|
}
|
|
14056
14326
|
return out;
|
|
14057
14327
|
}
|
|
14328
|
+
function decodeKeyPath(key, hint) {
|
|
14329
|
+
const trimmed = key.trim();
|
|
14330
|
+
if (trimmed.length === 0) {
|
|
14331
|
+
return { error: "Key is empty. Provide a Firestore key like `sf/abc123`." };
|
|
14332
|
+
}
|
|
14333
|
+
const segments = trimmed.split("/").filter((s) => s.length > 0);
|
|
14334
|
+
if (segments.length < 2 || segments.length % 2 !== 0) {
|
|
14335
|
+
return {
|
|
14336
|
+
error: `Invalid Firestore key '${trimmed}'. Expected an even number of segments (\`prefix/id\` pairs). Got ${segments.length} segment(s).`
|
|
14337
|
+
};
|
|
14338
|
+
}
|
|
14339
|
+
const decoded = [];
|
|
14340
|
+
const unresolved = [];
|
|
14341
|
+
for (let i = 0; i < segments.length; i += 2) {
|
|
14342
|
+
const prefix = segments[i];
|
|
14343
|
+
const id = segments[i + 1];
|
|
14344
|
+
const model = getFirebaseModelByPrefix(prefix);
|
|
14345
|
+
if (!model) {
|
|
14346
|
+
unresolved.push(prefix);
|
|
14347
|
+
}
|
|
14348
|
+
decoded.push({ prefix, id, model });
|
|
14349
|
+
}
|
|
14350
|
+
let leaf = decoded[decoded.length - 1];
|
|
14351
|
+
if (!leaf.model && hint) {
|
|
14352
|
+
const fromHint = getFirebaseModel(hint) ?? getFirebaseModelByPrefix(hint);
|
|
14353
|
+
if (fromHint) {
|
|
14354
|
+
leaf = { prefix: leaf.prefix, id: leaf.id, model: fromHint };
|
|
14355
|
+
}
|
|
14356
|
+
}
|
|
14357
|
+
const ancestors2 = decoded.slice(0, -1);
|
|
14358
|
+
return { leaf, ancestors: ancestors2, unresolvedPrefixes: unresolved };
|
|
14359
|
+
}
|
|
14360
|
+
function formatKeyDecode(input, rawKey) {
|
|
14361
|
+
const lines = [];
|
|
14362
|
+
lines.push("_Model decoded from key prefix._", "");
|
|
14363
|
+
const leaf = input.leaf;
|
|
14364
|
+
if (leaf.model) {
|
|
14365
|
+
lines.push(`**Leaf:** ${leaf.model.name}`);
|
|
14366
|
+
lines.push(`- identityConst: \`${leaf.model.identityConst}\``);
|
|
14367
|
+
lines.push(`- modelType: \`${leaf.model.modelType}\``);
|
|
14368
|
+
lines.push(`- prefix: \`${leaf.prefix}\``);
|
|
14369
|
+
lines.push(`- id: \`${leaf.id}\``);
|
|
14370
|
+
if (leaf.model.modelGroup) {
|
|
14371
|
+
lines.push(`- modelGroup: ${leaf.model.modelGroup}`);
|
|
14372
|
+
}
|
|
14373
|
+
if (leaf.model.parentIdentityConst) {
|
|
14374
|
+
lines.push(`- parentIdentityConst: \`${leaf.model.parentIdentityConst}\``);
|
|
14375
|
+
}
|
|
14376
|
+
lines.push(`- source: \`${leaf.model.sourcePackage}\` (${leaf.model.sourceFile})`);
|
|
14377
|
+
} else {
|
|
14378
|
+
lines.push(`**Leaf:** _unknown_ \u2014 no model registered for prefix \`${leaf.prefix}\``);
|
|
14379
|
+
lines.push(`- prefix: \`${leaf.prefix}\``);
|
|
14380
|
+
lines.push(`- id: \`${leaf.id}\``);
|
|
14381
|
+
}
|
|
14382
|
+
if (input.ancestors.length > 0) {
|
|
14383
|
+
lines.push("", "**Parent chain:**");
|
|
14384
|
+
for (const ancestor of input.ancestors) {
|
|
14385
|
+
if (ancestor.model) {
|
|
14386
|
+
lines.push(`- ${ancestor.model.name} \u2014 prefix \`${ancestor.prefix}\`, id \`${ancestor.id}\``);
|
|
14387
|
+
} else {
|
|
14388
|
+
lines.push(`- _unknown_ \u2014 prefix \`${ancestor.prefix}\` (id \`${ancestor.id}\`)`);
|
|
14389
|
+
}
|
|
14390
|
+
}
|
|
14391
|
+
}
|
|
14392
|
+
if (input.unresolvedPrefixes.length > 0) {
|
|
14393
|
+
const label = input.unresolvedPrefixes.length === 1 ? "prefix" : "prefixes";
|
|
14394
|
+
const list = input.unresolvedPrefixes.map((p) => `\`${p}\``).join(", ");
|
|
14395
|
+
lines.push("", `_Unresolved ${label}: ${list}. Run \`dbx_model_lookup\` to browse known models._`);
|
|
14396
|
+
}
|
|
14397
|
+
lines.push("", `_Key:_ \`${rawKey}\``);
|
|
14398
|
+
return lines.join("\n");
|
|
14399
|
+
}
|
|
14058
14400
|
function runModelDecode(rawArgs) {
|
|
14059
14401
|
let args;
|
|
14060
14402
|
try {
|
|
@@ -14063,6 +14405,18 @@ function runModelDecode(rawArgs) {
|
|
|
14063
14405
|
const message = err instanceof Error ? err.message : String(err);
|
|
14064
14406
|
return toolError(message);
|
|
14065
14407
|
}
|
|
14408
|
+
if (args.data === void 0 && args.key === void 0) {
|
|
14409
|
+
return toolError("Provide either `data` (a Firestore document) or `key` (a Firestore model key).");
|
|
14410
|
+
}
|
|
14411
|
+
if (args.data === void 0 && typeof args.key === "string") {
|
|
14412
|
+
const decoded = decodeKeyPath(args.key, args.model);
|
|
14413
|
+
if ("error" in decoded) {
|
|
14414
|
+
return toolError(decoded.error);
|
|
14415
|
+
}
|
|
14416
|
+
const text2 = formatKeyDecode(decoded, args.key);
|
|
14417
|
+
const result2 = { content: [{ type: "text", text: text2 }] };
|
|
14418
|
+
return result2;
|
|
14419
|
+
}
|
|
14066
14420
|
let document;
|
|
14067
14421
|
try {
|
|
14068
14422
|
document = coerceToDocument(args.data);
|
|
@@ -14070,15 +14424,16 @@ function runModelDecode(rawArgs) {
|
|
|
14070
14424
|
const message = err instanceof Error ? err.message : String(err);
|
|
14071
14425
|
return toolError(message);
|
|
14072
14426
|
}
|
|
14073
|
-
const
|
|
14427
|
+
const extraKey = args.key ?? document.extraKey;
|
|
14428
|
+
const detection = detectModel(document.doc, extraKey, args.model);
|
|
14074
14429
|
if (!detection) {
|
|
14075
|
-
const message = buildUnmatchedMessage(args.model, document);
|
|
14430
|
+
const message = buildUnmatchedMessage(args.model, { ...document, extraKey });
|
|
14076
14431
|
const result2 = { content: [{ type: "text", text: message }] };
|
|
14077
14432
|
return result2;
|
|
14078
14433
|
}
|
|
14079
14434
|
const prefixes = buildPrefixMap();
|
|
14080
14435
|
const header = buildDetectionHeader(detection);
|
|
14081
|
-
const body = formatDecode({ model: detection.model, doc: document.doc, prefixes, extraKey
|
|
14436
|
+
const body = formatDecode({ model: detection.model, doc: document.doc, prefixes, extraKey });
|
|
14082
14437
|
const text = `${header}
|
|
14083
14438
|
|
|
14084
14439
|
${body}`;
|
|
@@ -14279,7 +14634,15 @@ function createListAppTool(config) {
|
|
|
14279
14634
|
}
|
|
14280
14635
|
|
|
14281
14636
|
// packages/dbx-components-mcp/src/tools/model-validate/extract.ts
|
|
14282
|
-
import { Node as
|
|
14637
|
+
import { Node as Node12, Project as Project4, SyntaxKind as SyntaxKind4 } from "ts-morph";
|
|
14638
|
+
|
|
14639
|
+
// packages/dbx-components-mcp/src/tools/model-validate/types.ts
|
|
14640
|
+
var MAX_FIELD_NAME_LENGTH = 5;
|
|
14641
|
+
var ROOT_MODEL_ORDER = ["identity", "interface", "rolesType", "documentClass", "converter", "referenceFn", "collectionType", "collectionFn"];
|
|
14642
|
+
var SUBCOLLECTION_MODEL_ORDER = ["identity", "interface", "rolesType", "documentClass", "converter", "referenceFn", "collectionType", "collectionFactoryType", "collectionFn", "referenceGroupFn", "collectionGroupType", "collectionGroupFn"];
|
|
14643
|
+
var SUB_OBJECT_FACTORY_NAMES2 = ["firestoreSubObject", "firestoreObjectArray", "firestoreMap"];
|
|
14644
|
+
|
|
14645
|
+
// packages/dbx-components-mcp/src/tools/model-validate/extract.ts
|
|
14283
14646
|
function extractFile(source) {
|
|
14284
14647
|
const project = new Project4({ useInMemoryFileSystem: true, skipAddingFilesFromTsConfig: true });
|
|
14285
14648
|
const sourceFile = project.createSourceFile(source.name, source.text, { overwrite: true });
|
|
@@ -14289,16 +14652,56 @@ function extractFile(source) {
|
|
|
14289
14652
|
const firstModelLine = identities.length > 0 ? Math.min(...identities.map((i) => i.line)) : void 0;
|
|
14290
14653
|
const models = identities.map((identity) => extractModel(sourceFile, identity));
|
|
14291
14654
|
const dataInterfaces = findDataInterfaces(sourceFile, groupInterface);
|
|
14655
|
+
const subObjectCalls = findSubObjectFactoryCalls(sourceFile);
|
|
14292
14656
|
const result = {
|
|
14293
14657
|
name: source.name,
|
|
14294
14658
|
groupInterface,
|
|
14295
14659
|
groupTypes,
|
|
14296
14660
|
firstModelLine,
|
|
14297
14661
|
models,
|
|
14298
|
-
dataInterfaces
|
|
14662
|
+
dataInterfaces,
|
|
14663
|
+
subObjectCalls
|
|
14299
14664
|
};
|
|
14300
14665
|
return result;
|
|
14301
14666
|
}
|
|
14667
|
+
function findSubObjectFactoryCalls(sourceFile) {
|
|
14668
|
+
const out = [];
|
|
14669
|
+
const factorySet = new Set(SUB_OBJECT_FACTORY_NAMES2);
|
|
14670
|
+
for (const call of sourceFile.getDescendantsOfKind(SyntaxKind4.CallExpression)) {
|
|
14671
|
+
const expr = call.getExpression();
|
|
14672
|
+
if (!Node12.isIdentifier(expr)) {
|
|
14673
|
+
continue;
|
|
14674
|
+
}
|
|
14675
|
+
const factoryName = expr.getText();
|
|
14676
|
+
if (!factorySet.has(factoryName)) {
|
|
14677
|
+
continue;
|
|
14678
|
+
}
|
|
14679
|
+
const typeArgs = call.getTypeArguments();
|
|
14680
|
+
if (typeArgs.length === 0) {
|
|
14681
|
+
continue;
|
|
14682
|
+
}
|
|
14683
|
+
const typeArgName = readTypeReferenceIdentifier(typeArgs[0]);
|
|
14684
|
+
if (!typeArgName) {
|
|
14685
|
+
continue;
|
|
14686
|
+
}
|
|
14687
|
+
out.push({
|
|
14688
|
+
factoryName,
|
|
14689
|
+
typeArgName,
|
|
14690
|
+
line: call.getStartLineNumber()
|
|
14691
|
+
});
|
|
14692
|
+
}
|
|
14693
|
+
return out;
|
|
14694
|
+
}
|
|
14695
|
+
function readTypeReferenceIdentifier(typeNode) {
|
|
14696
|
+
if (!Node12.isTypeReference(typeNode)) {
|
|
14697
|
+
return void 0;
|
|
14698
|
+
}
|
|
14699
|
+
const nameNode = typeNode.getTypeName();
|
|
14700
|
+
if (!Node12.isIdentifier(nameNode)) {
|
|
14701
|
+
return void 0;
|
|
14702
|
+
}
|
|
14703
|
+
return nameNode.getText();
|
|
14704
|
+
}
|
|
14302
14705
|
function findDataInterfaces(sourceFile, groupInterface) {
|
|
14303
14706
|
const out = [];
|
|
14304
14707
|
const groupName = groupInterface?.name;
|
|
@@ -14309,10 +14712,50 @@ function findDataInterfaces(sourceFile, groupInterface) {
|
|
|
14309
14712
|
}
|
|
14310
14713
|
const fields = extractInterfaceFields(iface);
|
|
14311
14714
|
const dbxModelTag = readDbxModelTag(iface.getJsDocs());
|
|
14312
|
-
|
|
14715
|
+
const dbxModelSubObjectTag = readDbxModelSubObjectTag(iface.getJsDocs());
|
|
14716
|
+
const extendsNames = readExtendsNames(iface);
|
|
14717
|
+
out.push({ name, line: iface.getStartLineNumber(), dbxModelTag, dbxModelSubObjectTag, extendsNames, fields });
|
|
14718
|
+
}
|
|
14719
|
+
return out;
|
|
14720
|
+
}
|
|
14721
|
+
function readExtendsNames(iface) {
|
|
14722
|
+
const out = [];
|
|
14723
|
+
for (const expr of iface.getExtends()) {
|
|
14724
|
+
const head = expr.getExpression().getText();
|
|
14725
|
+
let resolved = head;
|
|
14726
|
+
if (PASSTHROUGH_TYPE_WRAPPERS2.has(head)) {
|
|
14727
|
+
const typeArgs = expr.getTypeArguments();
|
|
14728
|
+
if (typeArgs.length > 0) {
|
|
14729
|
+
const peeled = peelExtendsTypeNode(typeArgs[0]);
|
|
14730
|
+
if (peeled !== void 0) {
|
|
14731
|
+
resolved = peeled;
|
|
14732
|
+
}
|
|
14733
|
+
}
|
|
14734
|
+
}
|
|
14735
|
+
out.push(resolved);
|
|
14313
14736
|
}
|
|
14314
14737
|
return out;
|
|
14315
14738
|
}
|
|
14739
|
+
var PASSTHROUGH_TYPE_WRAPPERS2 = /* @__PURE__ */ new Set(["Partial", "Required", "Readonly", "NonNullable", "MaybeMap", "Pick", "Omit"]);
|
|
14740
|
+
function peelExtendsTypeNode(node) {
|
|
14741
|
+
let current = node;
|
|
14742
|
+
while (Node12.isParenthesizedTypeNode(current)) {
|
|
14743
|
+
current = current.getTypeNode();
|
|
14744
|
+
}
|
|
14745
|
+
let result;
|
|
14746
|
+
if (Node12.isTypeReference(current)) {
|
|
14747
|
+
const name = current.getTypeName().getText();
|
|
14748
|
+
if (PASSTHROUGH_TYPE_WRAPPERS2.has(name)) {
|
|
14749
|
+
const inner = current.getTypeArguments();
|
|
14750
|
+
if (inner.length > 0) {
|
|
14751
|
+
result = peelExtendsTypeNode(inner[0]);
|
|
14752
|
+
}
|
|
14753
|
+
} else {
|
|
14754
|
+
result = name;
|
|
14755
|
+
}
|
|
14756
|
+
}
|
|
14757
|
+
return result;
|
|
14758
|
+
}
|
|
14316
14759
|
function extractInterfaceFields(iface) {
|
|
14317
14760
|
const fields = [];
|
|
14318
14761
|
for (const prop of iface.getProperties()) {
|
|
@@ -14379,6 +14822,17 @@ function readDbxModelTag(jsDocs) {
|
|
|
14379
14822
|
}
|
|
14380
14823
|
return result;
|
|
14381
14824
|
}
|
|
14825
|
+
function readDbxModelSubObjectTag(jsDocs) {
|
|
14826
|
+
let result = false;
|
|
14827
|
+
for (const jsDoc of jsDocs) {
|
|
14828
|
+
for (const tag of jsDoc.getTags()) {
|
|
14829
|
+
if (tag.getTagName() === "dbxModelSubObject") {
|
|
14830
|
+
result = true;
|
|
14831
|
+
}
|
|
14832
|
+
}
|
|
14833
|
+
}
|
|
14834
|
+
return result;
|
|
14835
|
+
}
|
|
14382
14836
|
function readDbxModelVariableTag(jsDocs) {
|
|
14383
14837
|
let result;
|
|
14384
14838
|
for (const jsDoc of jsDocs) {
|
|
@@ -14418,7 +14872,7 @@ function extractIdentityRefs(alias) {
|
|
|
14418
14872
|
if (!typeNode) {
|
|
14419
14873
|
return refs;
|
|
14420
14874
|
}
|
|
14421
|
-
if (
|
|
14875
|
+
if (Node12.isTypeQuery(typeNode)) {
|
|
14422
14876
|
refs.push(typeNode.getExprName().getText());
|
|
14423
14877
|
}
|
|
14424
14878
|
const typeofNodes = typeNode.getDescendantsOfKind(SyntaxKind4.TypeQuery);
|
|
@@ -14432,7 +14886,7 @@ function findIdentities2(sourceFile) {
|
|
|
14432
14886
|
for (const statement of sourceFile.getVariableStatements()) {
|
|
14433
14887
|
for (const decl of statement.getDeclarations()) {
|
|
14434
14888
|
const initializer = decl.getInitializer();
|
|
14435
|
-
if (!initializer || !
|
|
14889
|
+
if (!initializer || !Node12.isCallExpression(initializer)) {
|
|
14436
14890
|
continue;
|
|
14437
14891
|
}
|
|
14438
14892
|
if (initializer.getExpression().getText() !== "firestoreModelIdentity") {
|
|
@@ -14468,7 +14922,7 @@ function parseIdentityArgs(args) {
|
|
|
14468
14922
|
}
|
|
14469
14923
|
if (args.length === 3) {
|
|
14470
14924
|
const [parentNode, nameNode, prefixNode] = args;
|
|
14471
|
-
const parentRef =
|
|
14925
|
+
const parentRef = Node12.isIdentifier(parentNode) ? parentNode.getText() : void 0;
|
|
14472
14926
|
const result2 = {
|
|
14473
14927
|
parentIdentityRef: parentRef,
|
|
14474
14928
|
collectionName: stringLiteralValue2(nameNode),
|
|
@@ -14483,7 +14937,7 @@ function stringLiteralValue2(node) {
|
|
|
14483
14937
|
if (!node) {
|
|
14484
14938
|
return void 0;
|
|
14485
14939
|
}
|
|
14486
|
-
if (
|
|
14940
|
+
if (Node12.isStringLiteral(node) || Node12.isNoSubstitutionTemplateLiteral(node)) {
|
|
14487
14941
|
return node.getLiteralText();
|
|
14488
14942
|
}
|
|
14489
14943
|
return void 0;
|
|
@@ -14544,7 +14998,7 @@ function detectFactoryCallKind(sourceFile, factoryFnName) {
|
|
|
14544
14998
|
const calls = fn.getDescendantsOfKind(SyntaxKind4.CallExpression);
|
|
14545
14999
|
for (const call of calls) {
|
|
14546
15000
|
const expr = call.getExpression();
|
|
14547
|
-
if (!
|
|
15001
|
+
if (!Node12.isPropertyAccessExpression(expr)) {
|
|
14548
15002
|
continue;
|
|
14549
15003
|
}
|
|
14550
15004
|
const name = expr.getName();
|
|
@@ -15141,7 +15595,7 @@ var RULE_CATALOG = [
|
|
|
15141
15595
|
severity: "warning",
|
|
15142
15596
|
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
15597
|
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.",
|
|
15598
|
+
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
15599
|
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
15600
|
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
15601
|
},
|
|
@@ -15161,8 +15615,8 @@ var RULE_CATALOG = [
|
|
|
15161
15615
|
severity: "warning",
|
|
15162
15616
|
title: "A persisted field is missing its `@dbxModelVariable <name>` JSDoc tag",
|
|
15163
15617
|
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
|
|
15618
|
+
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`).",
|
|
15619
|
+
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
15620
|
canonicalFix: "Append `@dbxModelVariable <longName>` to the field's JSDoc block, where `<longName>` is the field's unabbreviated camelCase variable name."
|
|
15167
15621
|
},
|
|
15168
15622
|
{
|
|
@@ -15349,6 +15803,48 @@ var RULE_CATALOG = [
|
|
|
15349
15803
|
whenItDoesNotApply: "When intentionally module-private.",
|
|
15350
15804
|
canonicalFix: "Add `export` to the alias."
|
|
15351
15805
|
},
|
|
15806
|
+
{
|
|
15807
|
+
code: "MODEL_SUBOBJECT_NOT_TAGGED",
|
|
15808
|
+
source: "dbx_model_validate",
|
|
15809
|
+
severity: "warning",
|
|
15810
|
+
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",
|
|
15811
|
+
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.",
|
|
15812
|
+
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.",
|
|
15813
|
+
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.).",
|
|
15814
|
+
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.",
|
|
15815
|
+
seeAlso: [
|
|
15816
|
+
{
|
|
15817
|
+
kind: "artifact",
|
|
15818
|
+
target: "firestore-model"
|
|
15819
|
+
}
|
|
15820
|
+
]
|
|
15821
|
+
},
|
|
15822
|
+
{
|
|
15823
|
+
code: "MODEL_SUBOBJECT_PARENT_NOT_TAGGED",
|
|
15824
|
+
source: "dbx_model_validate",
|
|
15825
|
+
severity: "warning",
|
|
15826
|
+
title: "A `@dbxModelSubObject` interface extends a parent interface that is not itself tagged with `@dbxModel` or `@dbxModelSubObject`. The parent's persisted fields flow through the sub-object's converter but never reach the workspace's `@dbxModelVariable` long-name checks. Whether the agent acts depends on whether the parent is owned by this package, but the validator surfaces both classifications so the consuming agent (or developer) can make an informed call",
|
|
15827
|
+
whatItFlags: "A `@dbxModelSubObject` interface extends a parent interface that is not itself tagged with `@dbxModel` or `@dbxModelSubObject`. The parent's persisted fields flow through the sub-object's converter but never reach the workspace's `@dbxModelVariable` long-name checks. Whether the agent acts depends on whether the parent is owned by this package, but the validator surfaces both classifications so the consuming agent (or developer) can make an informed call.",
|
|
15828
|
+
whenItApplies: "Every `@dbxModelSubObject` interface whose `extends` heritage names an untagged parent. The parent is classified `in-package` when its declaration is found in the validated source set, otherwise `external`. Anchored at the sub-object interface line so the caller sees both the child and the parent in one place.",
|
|
15829
|
+
whenItDoesNotApply: "Parents already tagged with `@dbxModel` or `@dbxModelSubObject`; parents whose names appear in `modelValidate.ignoredExternalParents` (suppresses external-only warnings \u2014 in-package parents are never suppressed). Generic parameters, inline types, and TS utility wrappers (`Partial<T>`, `Pick<T, K>`, `Omit<T, K>`, `Readonly<T>`, `MaybeMap<T>`) are unwrapped to the underlying parent name before resolution.",
|
|
15830
|
+
canonicalFix: "In-package parent: add `@dbxModelSubObject` to the parent interface's JSDoc and tag each persisted field with `@dbxModelVariable <longName>`. Alternative: redeclare the inherited fields directly on the child with their own JSDoc + tag (useful when the parent is a shared shape that shouldn't commit to a single longName). External parent: redeclare the inherited fields on the child with `@dbxModelVariable <longName>` tags when surface long-names are needed, or add the parent name to `modelValidate.ignoredExternalParents` in `dbx-mcp.config.json` when the inherited fields are framework plumbing (e.g. `IndexRef.i`, `DateRange.start/end`).",
|
|
15831
|
+
seeAlso: [
|
|
15832
|
+
{
|
|
15833
|
+
kind: "artifact",
|
|
15834
|
+
target: "firestore-model"
|
|
15835
|
+
}
|
|
15836
|
+
]
|
|
15837
|
+
},
|
|
15838
|
+
{
|
|
15839
|
+
code: "MODEL_SUBOBJECT_TAG_CONFLICT",
|
|
15840
|
+
source: "dbx_model_validate",
|
|
15841
|
+
severity: "error",
|
|
15842
|
+
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",
|
|
15843
|
+
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.",
|
|
15844
|
+
whenItApplies: "Any interface JSDoc block where both `@dbxModel` and `@dbxModelSubObject` are present.",
|
|
15845
|
+
whenItDoesNotApply: "Interfaces carrying only one of the two tags (or neither).",
|
|
15846
|
+
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`."
|
|
15847
|
+
},
|
|
15352
15848
|
{
|
|
15353
15849
|
code: "SUB_MISSING_COLLECTION_FACTORY_FN",
|
|
15354
15850
|
source: "dbx_model_validate",
|
|
@@ -16975,33 +17471,34 @@ function formatSeeAlso(ref) {
|
|
|
16975
17471
|
return `Doc: ${ref.target}${tail}`;
|
|
16976
17472
|
}
|
|
16977
17473
|
|
|
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
17474
|
// packages/dbx-components-mcp/src/tools/model-validate/rules.ts
|
|
16984
|
-
function runRules(file, options) {
|
|
17475
|
+
function runRules(file, options, context) {
|
|
16985
17476
|
const violations = [];
|
|
16986
|
-
|
|
16987
|
-
|
|
16988
|
-
|
|
16989
|
-
|
|
16990
|
-
|
|
16991
|
-
|
|
16992
|
-
|
|
16993
|
-
|
|
16994
|
-
|
|
16995
|
-
|
|
16996
|
-
|
|
16997
|
-
}
|
|
16998
|
-
checkFieldNameLengths(file, violations, options);
|
|
17477
|
+
const hasModels = file.models.length > 0;
|
|
17478
|
+
if (hasModels) {
|
|
17479
|
+
checkFileLevel(file, violations);
|
|
17480
|
+
for (const model of file.models) {
|
|
17481
|
+
checkIdentity(file, model, violations);
|
|
17482
|
+
checkCoreDeclarations(file, model, violations);
|
|
17483
|
+
if (model.variant === "subcollection") {
|
|
17484
|
+
checkSubcollectionExtras(file, model, violations);
|
|
17485
|
+
}
|
|
17486
|
+
checkDeclarationOrder(file, model, violations);
|
|
17487
|
+
}
|
|
17488
|
+
}
|
|
17489
|
+
checkFieldNameLengths(file, violations, options, { restrictToTaggedInterfaces: !hasModels });
|
|
17490
|
+
checkSubObjectInterfaceTags(file, violations);
|
|
17491
|
+
checkSubObjectFactoryCallSites(file, violations, context);
|
|
17492
|
+
checkSubObjectParentNotTagged(file, violations, context, options);
|
|
16999
17493
|
return violations;
|
|
17000
17494
|
}
|
|
17001
|
-
function checkFieldNameLengths(file, violations, options) {
|
|
17495
|
+
function checkFieldNameLengths(file, violations, options, scope) {
|
|
17002
17496
|
const limit = options?.maxFieldNameLength ?? MAX_FIELD_NAME_LENGTH;
|
|
17003
17497
|
const ignored = options?.ignoredFieldNames;
|
|
17004
17498
|
for (const iface of file.dataInterfaces) {
|
|
17499
|
+
if (scope.restrictToTaggedInterfaces && !(iface.dbxModelTag || iface.dbxModelSubObjectTag)) {
|
|
17500
|
+
continue;
|
|
17501
|
+
}
|
|
17005
17502
|
for (const field of iface.fields) {
|
|
17006
17503
|
if (ignored?.has(field.name)) {
|
|
17007
17504
|
continue;
|
|
@@ -17019,11 +17516,14 @@ function checkFieldNameLengths(file, violations, options) {
|
|
|
17019
17516
|
});
|
|
17020
17517
|
}
|
|
17021
17518
|
}
|
|
17022
|
-
checkFieldJsDocs(file, violations, options);
|
|
17519
|
+
checkFieldJsDocs(file, violations, options, scope);
|
|
17023
17520
|
}
|
|
17024
|
-
function checkFieldJsDocs(file, violations, options) {
|
|
17521
|
+
function checkFieldJsDocs(file, violations, options, scope) {
|
|
17025
17522
|
const ignored = options?.ignoredFieldNames;
|
|
17026
17523
|
for (const iface of file.dataInterfaces) {
|
|
17524
|
+
if (scope.restrictToTaggedInterfaces && !(iface.dbxModelTag || iface.dbxModelSubObjectTag)) {
|
|
17525
|
+
continue;
|
|
17526
|
+
}
|
|
17027
17527
|
for (const field of iface.fields) {
|
|
17028
17528
|
if (!field.jsDocFirstLine) {
|
|
17029
17529
|
pushViolation(violations, {
|
|
@@ -17035,7 +17535,8 @@ function checkFieldJsDocs(file, violations, options) {
|
|
|
17035
17535
|
model: iface.name
|
|
17036
17536
|
});
|
|
17037
17537
|
}
|
|
17038
|
-
|
|
17538
|
+
const fieldRulesApply = iface.dbxModelTag || iface.dbxModelSubObjectTag;
|
|
17539
|
+
if (fieldRulesApply && field.dbxModelVariableTag === void 0) {
|
|
17039
17540
|
pushViolation(violations, {
|
|
17040
17541
|
code: "MODEL_FIELD_MISSING_VARIABLE_TAG",
|
|
17041
17542
|
severity: "warning",
|
|
@@ -17045,7 +17546,7 @@ function checkFieldJsDocs(file, violations, options) {
|
|
|
17045
17546
|
model: iface.name
|
|
17046
17547
|
});
|
|
17047
17548
|
}
|
|
17048
|
-
if (
|
|
17549
|
+
if (fieldRulesApply && field.dbxModelVariableTag !== void 0 && field.dbxModelVariableTag === field.name && !ignored?.has(field.name)) {
|
|
17049
17550
|
pushViolation(violations, {
|
|
17050
17551
|
code: "MODEL_FIELD_LONG_NAME_EQUALS_NAME",
|
|
17051
17552
|
severity: "warning",
|
|
@@ -17058,6 +17559,95 @@ function checkFieldJsDocs(file, violations, options) {
|
|
|
17058
17559
|
}
|
|
17059
17560
|
}
|
|
17060
17561
|
}
|
|
17562
|
+
function checkSubObjectInterfaceTags(file, violations) {
|
|
17563
|
+
for (const iface of file.dataInterfaces) {
|
|
17564
|
+
if (iface.dbxModelTag && iface.dbxModelSubObjectTag) {
|
|
17565
|
+
pushViolation(violations, {
|
|
17566
|
+
code: "MODEL_SUBOBJECT_TAG_CONFLICT",
|
|
17567
|
+
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\`.`,
|
|
17568
|
+
file: file.name,
|
|
17569
|
+
line: iface.line,
|
|
17570
|
+
model: iface.name
|
|
17571
|
+
});
|
|
17572
|
+
}
|
|
17573
|
+
}
|
|
17574
|
+
}
|
|
17575
|
+
function checkSubObjectFactoryCallSites(file, violations, context) {
|
|
17576
|
+
if (!context) {
|
|
17577
|
+
return;
|
|
17578
|
+
}
|
|
17579
|
+
if (file.subObjectCalls.length === 0) {
|
|
17580
|
+
return;
|
|
17581
|
+
}
|
|
17582
|
+
for (const call of file.subObjectCalls) {
|
|
17583
|
+
const entry = context.interfacesByName.get(call.typeArgName);
|
|
17584
|
+
if (!entry) {
|
|
17585
|
+
continue;
|
|
17586
|
+
}
|
|
17587
|
+
const { iface, file: declFile } = entry;
|
|
17588
|
+
if (iface.dbxModelTag || iface.dbxModelSubObjectTag) {
|
|
17589
|
+
continue;
|
|
17590
|
+
}
|
|
17591
|
+
if (context.emittedSubObjectInterfaces.has(call.typeArgName)) {
|
|
17592
|
+
continue;
|
|
17593
|
+
}
|
|
17594
|
+
context.emittedSubObjectInterfaces.add(call.typeArgName);
|
|
17595
|
+
pushViolation(violations, {
|
|
17596
|
+
code: "MODEL_SUBOBJECT_NOT_TAGGED",
|
|
17597
|
+
severity: "warning",
|
|
17598
|
+
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.`,
|
|
17599
|
+
file: declFile,
|
|
17600
|
+
line: iface.line,
|
|
17601
|
+
model: iface.name
|
|
17602
|
+
});
|
|
17603
|
+
}
|
|
17604
|
+
}
|
|
17605
|
+
function checkSubObjectParentNotTagged(file, violations, context, options) {
|
|
17606
|
+
if (!context) {
|
|
17607
|
+
return;
|
|
17608
|
+
}
|
|
17609
|
+
const ignoredExternal = options?.ignoredExternalParents;
|
|
17610
|
+
for (const iface of file.dataInterfaces) {
|
|
17611
|
+
if (!iface.dbxModelSubObjectTag) {
|
|
17612
|
+
continue;
|
|
17613
|
+
}
|
|
17614
|
+
for (const parentName of iface.extendsNames) {
|
|
17615
|
+
const dedupKey = `${iface.name}<-${parentName}`;
|
|
17616
|
+
if (context.emittedSubObjectInterfaces.has(dedupKey)) {
|
|
17617
|
+
continue;
|
|
17618
|
+
}
|
|
17619
|
+
const entry = context.interfacesByName.get(parentName);
|
|
17620
|
+
if (entry) {
|
|
17621
|
+
const parent = entry.iface;
|
|
17622
|
+
if (parent.dbxModelTag || parent.dbxModelSubObjectTag) {
|
|
17623
|
+
continue;
|
|
17624
|
+
}
|
|
17625
|
+
context.emittedSubObjectInterfaces.add(dedupKey);
|
|
17626
|
+
pushViolation(violations, {
|
|
17627
|
+
code: "MODEL_SUBOBJECT_PARENT_NOT_TAGGED",
|
|
17628
|
+
severity: "warning",
|
|
17629
|
+
message: `Interface \`${parentName}\` is extended by \`@dbxModelSubObject\` \`${iface.name}\`. \`${parentName}\` is declared in the same package (${entry.file}) and its fields are persisted via \`${iface.name}\`'s converter but are not validated. Fix (preferred): add \`@dbxModelSubObject\` to \`${parentName}\`'s JSDoc and tag each persisted field with \`@dbxModelVariable <longName>\`. Alternative: redeclare the inherited fields directly on \`${iface.name}\` with their own JSDoc + tag (useful when \`${parentName}\` is a shared shape that you do not want to commit to a single longName).`,
|
|
17630
|
+
file: entry.file,
|
|
17631
|
+
line: parent.line,
|
|
17632
|
+
model: parentName
|
|
17633
|
+
});
|
|
17634
|
+
} else {
|
|
17635
|
+
if (ignoredExternal?.has(parentName)) {
|
|
17636
|
+
continue;
|
|
17637
|
+
}
|
|
17638
|
+
context.emittedSubObjectInterfaces.add(dedupKey);
|
|
17639
|
+
pushViolation(violations, {
|
|
17640
|
+
code: "MODEL_SUBOBJECT_PARENT_NOT_TAGGED",
|
|
17641
|
+
severity: "warning",
|
|
17642
|
+
message: `Interface \`${parentName}\` is extended by \`@dbxModelSubObject\` \`${iface.name}\`, but \`${parentName}\` is declared outside this package (unresolved in the validated source set). Its persisted fields cannot be tagged from here. Decide: (1) if the inherited fields need explicit longNames in this catalog, redeclare them on \`${iface.name}\` with a JSDoc block carrying \`@dbxModelVariable <longName>\` (the redeclaration is structurally compatible \u2014 TypeScript treats it as a field narrowing, not a new property); (2) if the inherited fields are framework plumbing (e.g. \`IndexRef.i\`, \`DateRange.start/end\`) and surface longNames are not needed, suppress this specific warning by adding \`${parentName}\` to \`modelValidate.ignoredExternalParents\` in \`dbx-mcp.config.json\`.`,
|
|
17643
|
+
file: file.name,
|
|
17644
|
+
line: iface.line,
|
|
17645
|
+
model: iface.name
|
|
17646
|
+
});
|
|
17647
|
+
}
|
|
17648
|
+
}
|
|
17649
|
+
}
|
|
17650
|
+
}
|
|
17061
17651
|
function checkFileLevel(file, violations) {
|
|
17062
17652
|
const { groupInterface, groupTypes, firstModelLine, models, name } = file;
|
|
17063
17653
|
if (groupInterface) {
|
|
@@ -17759,10 +18349,15 @@ function validateFirebaseModelSources(sources, options) {
|
|
|
17759
18349
|
let modelsChecked = 0;
|
|
17760
18350
|
let errorCount = 0;
|
|
17761
18351
|
let warningCount = 0;
|
|
18352
|
+
const extracteds = [];
|
|
17762
18353
|
for (const source of sources) {
|
|
17763
18354
|
const extracted = extractFile(source);
|
|
18355
|
+
extracteds.push(extracted);
|
|
17764
18356
|
modelsChecked += extracted.models.length;
|
|
17765
|
-
|
|
18357
|
+
}
|
|
18358
|
+
const context = buildCrossFileRuleContext(extracteds);
|
|
18359
|
+
for (const extracted of extracteds) {
|
|
18360
|
+
const fileViolations = runRules(extracted, options, context);
|
|
17766
18361
|
for (const v of fileViolations) {
|
|
17767
18362
|
violations.push(v);
|
|
17768
18363
|
if (v.severity === "error") {
|
|
@@ -17781,6 +18376,22 @@ function validateFirebaseModelSources(sources, options) {
|
|
|
17781
18376
|
};
|
|
17782
18377
|
return result;
|
|
17783
18378
|
}
|
|
18379
|
+
function buildCrossFileRuleContext(extracteds) {
|
|
18380
|
+
const interfacesByName = /* @__PURE__ */ new Map();
|
|
18381
|
+
for (const file of extracteds) {
|
|
18382
|
+
for (const iface of file.dataInterfaces) {
|
|
18383
|
+
if (interfacesByName.has(iface.name)) {
|
|
18384
|
+
continue;
|
|
18385
|
+
}
|
|
18386
|
+
interfacesByName.set(iface.name, { file: file.name, iface });
|
|
18387
|
+
}
|
|
18388
|
+
}
|
|
18389
|
+
const context = {
|
|
18390
|
+
interfacesByName,
|
|
18391
|
+
emittedSubObjectInterfaces: /* @__PURE__ */ new Set()
|
|
18392
|
+
};
|
|
18393
|
+
return context;
|
|
18394
|
+
}
|
|
17784
18395
|
|
|
17785
18396
|
// packages/dbx-components-mcp/src/tools/model-validate.tool.ts
|
|
17786
18397
|
var DBX_MODEL_VALIDATE_TOOL = {
|
|
@@ -17835,7 +18446,7 @@ var modelValidateTool = /* @__PURE__ */ createModelValidateTool();
|
|
|
17835
18446
|
var runModelValidate = modelValidateTool.run;
|
|
17836
18447
|
|
|
17837
18448
|
// packages/dbx-components-mcp/src/tools/model-validate-api/extract.ts
|
|
17838
|
-
import { Node as
|
|
18449
|
+
import { Node as Node13, Project as Project5, SyntaxKind as SyntaxKind5 } from "ts-morph";
|
|
17839
18450
|
function extractFile2(source) {
|
|
17840
18451
|
const project = new Project5({ useInMemoryFileSystem: true, skipAddingFilesFromTsConfig: true });
|
|
17841
18452
|
const sourceFile = project.createSourceFile(source.name, source.text, { overwrite: true });
|
|
@@ -18001,7 +18612,7 @@ function findCrudConfigType(sourceFile) {
|
|
|
18001
18612
|
continue;
|
|
18002
18613
|
}
|
|
18003
18614
|
const typeNode = alias.getTypeNode();
|
|
18004
|
-
const summary = typeNode &&
|
|
18615
|
+
const summary = typeNode && Node13.isTypeLiteral(typeNode) ? summarizeCrudConfigType(typeNode) : { keys: [], nonNullKeys: [], bareLeafParamsNames: [] };
|
|
18005
18616
|
const result = {
|
|
18006
18617
|
name: alias.getName(),
|
|
18007
18618
|
exported: alias.isExported(),
|
|
@@ -18015,14 +18626,14 @@ function findCrudConfigType(sourceFile) {
|
|
|
18015
18626
|
return void 0;
|
|
18016
18627
|
}
|
|
18017
18628
|
function summarizeCrudConfigType(typeLiteral) {
|
|
18018
|
-
if (!
|
|
18629
|
+
if (!Node13.isTypeLiteral(typeLiteral)) {
|
|
18019
18630
|
return { keys: [], nonNullKeys: [], bareLeafParamsNames: [] };
|
|
18020
18631
|
}
|
|
18021
18632
|
const keys = [];
|
|
18022
18633
|
const nonNullKeys = [];
|
|
18023
18634
|
const bareLeafParamsNames = [];
|
|
18024
18635
|
for (const member of typeLiteral.getMembers()) {
|
|
18025
|
-
if (!
|
|
18636
|
+
if (!Node13.isPropertySignature(member)) {
|
|
18026
18637
|
continue;
|
|
18027
18638
|
}
|
|
18028
18639
|
const name = member.getName();
|
|
@@ -18040,18 +18651,18 @@ function summarizeCrudConfigType(typeLiteral) {
|
|
|
18040
18651
|
return { keys, nonNullKeys, bareLeafParamsNames };
|
|
18041
18652
|
}
|
|
18042
18653
|
function isNullLiteralType(node) {
|
|
18043
|
-
if (
|
|
18654
|
+
if (Node13.isLiteralTypeNode(node)) {
|
|
18044
18655
|
const literal = node.getLiteral();
|
|
18045
|
-
if (
|
|
18656
|
+
if (Node13.isNullLiteral(literal)) {
|
|
18046
18657
|
return true;
|
|
18047
18658
|
}
|
|
18048
18659
|
}
|
|
18049
18660
|
return false;
|
|
18050
18661
|
}
|
|
18051
18662
|
function collectBareParamsLeaves(node, out) {
|
|
18052
|
-
if (
|
|
18663
|
+
if (Node13.isTypeLiteral(node)) {
|
|
18053
18664
|
for (const member of node.getMembers()) {
|
|
18054
|
-
if (!
|
|
18665
|
+
if (!Node13.isPropertySignature(member)) {
|
|
18055
18666
|
continue;
|
|
18056
18667
|
}
|
|
18057
18668
|
const valueNode = member.getTypeNode();
|
|
@@ -18061,10 +18672,10 @@ function collectBareParamsLeaves(node, out) {
|
|
|
18061
18672
|
}
|
|
18062
18673
|
return;
|
|
18063
18674
|
}
|
|
18064
|
-
if (
|
|
18675
|
+
if (Node13.isTupleTypeNode(node)) {
|
|
18065
18676
|
return;
|
|
18066
18677
|
}
|
|
18067
|
-
if (
|
|
18678
|
+
if (Node13.isTypeReference(node)) {
|
|
18068
18679
|
const name = node.getTypeName().getText();
|
|
18069
18680
|
if (name.endsWith("Params")) {
|
|
18070
18681
|
out.push(name);
|
|
@@ -18081,12 +18692,12 @@ function findCrudConfigConst(sourceFile) {
|
|
|
18081
18692
|
const typeNode = decl.getTypeNode();
|
|
18082
18693
|
const initializer = decl.getInitializer();
|
|
18083
18694
|
const runtimeKeys = [];
|
|
18084
|
-
if (initializer &&
|
|
18695
|
+
if (initializer && Node13.isAsExpression(initializer)) {
|
|
18085
18696
|
const inner = initializer.getExpression();
|
|
18086
|
-
if (
|
|
18697
|
+
if (Node13.isObjectLiteralExpression(inner)) {
|
|
18087
18698
|
runtimeKeys.push(...collectObjectLiteralKeys(inner));
|
|
18088
18699
|
}
|
|
18089
|
-
} else if (initializer &&
|
|
18700
|
+
} else if (initializer && Node13.isObjectLiteralExpression(initializer)) {
|
|
18090
18701
|
runtimeKeys.push(...collectObjectLiteralKeys(initializer));
|
|
18091
18702
|
}
|
|
18092
18703
|
const result = {
|
|
@@ -18104,7 +18715,7 @@ function findCrudConfigConst(sourceFile) {
|
|
|
18104
18715
|
function collectObjectLiteralKeys(obj) {
|
|
18105
18716
|
const out = [];
|
|
18106
18717
|
for (const prop of obj.getProperties()) {
|
|
18107
|
-
if (
|
|
18718
|
+
if (Node13.isPropertyAssignment(prop) || Node13.isShorthandPropertyAssignment(prop)) {
|
|
18108
18719
|
out.push(prop.getName());
|
|
18109
18720
|
}
|
|
18110
18721
|
}
|
|
@@ -18120,7 +18731,7 @@ function findFunctionMap(sourceFile) {
|
|
|
18120
18731
|
const initializer = decl.getInitializer();
|
|
18121
18732
|
let callsFactory = false;
|
|
18122
18733
|
const factoryArgs = [];
|
|
18123
|
-
if (initializer &&
|
|
18734
|
+
if (initializer && Node13.isCallExpression(initializer)) {
|
|
18124
18735
|
const exprText = initializer.getExpression().getText();
|
|
18125
18736
|
if (exprText === "callModelFirebaseFunctionMapFactory") {
|
|
18126
18737
|
callsFactory = true;
|
|
@@ -18258,18 +18869,18 @@ function extractCastTargetName(node) {
|
|
|
18258
18869
|
return void 0;
|
|
18259
18870
|
}
|
|
18260
18871
|
let current = node;
|
|
18261
|
-
while (current &&
|
|
18872
|
+
while (current && Node13.isAsExpression(current)) {
|
|
18262
18873
|
const typeNode = current.getTypeNode();
|
|
18263
18874
|
if (!typeNode) {
|
|
18264
18875
|
break;
|
|
18265
18876
|
}
|
|
18266
|
-
if (
|
|
18877
|
+
if (Node13.isTypeReference(typeNode)) {
|
|
18267
18878
|
const refName = typeNode.getTypeName().getText();
|
|
18268
18879
|
if (refName === "Type") {
|
|
18269
18880
|
const args = typeNode.getTypeArguments();
|
|
18270
18881
|
if (args.length === 1) {
|
|
18271
18882
|
const arg = args[0];
|
|
18272
|
-
if (
|
|
18883
|
+
if (Node13.isTypeReference(arg)) {
|
|
18273
18884
|
return arg.getTypeName().getText();
|
|
18274
18885
|
}
|
|
18275
18886
|
return arg.getText();
|
|
@@ -18288,7 +18899,7 @@ function extractValidatorProperties(initializer) {
|
|
|
18288
18899
|
}
|
|
18289
18900
|
const out = [];
|
|
18290
18901
|
for (const prop of obj.getProperties()) {
|
|
18291
|
-
if (!
|
|
18902
|
+
if (!Node13.isPropertyAssignment(prop)) {
|
|
18292
18903
|
continue;
|
|
18293
18904
|
}
|
|
18294
18905
|
const propAssignment = prop;
|
|
@@ -18298,7 +18909,7 @@ function extractValidatorProperties(initializer) {
|
|
|
18298
18909
|
const clean = optional ? stripped.slice(0, -1) : stripped;
|
|
18299
18910
|
const valueNode = propAssignment.getInitializer();
|
|
18300
18911
|
let hasClearable = false;
|
|
18301
|
-
if (valueNode &&
|
|
18912
|
+
if (valueNode && Node13.isCallExpression(valueNode)) {
|
|
18302
18913
|
const exprText = valueNode.getExpression().getText();
|
|
18303
18914
|
if (exprText === "clearable") {
|
|
18304
18915
|
hasClearable = true;
|
|
@@ -18310,25 +18921,25 @@ function extractValidatorProperties(initializer) {
|
|
|
18310
18921
|
}
|
|
18311
18922
|
function findArktypeObjectLiteral(initializer) {
|
|
18312
18923
|
let current = initializer;
|
|
18313
|
-
while (current &&
|
|
18924
|
+
while (current && Node13.isAsExpression(current)) {
|
|
18314
18925
|
current = current.getExpression();
|
|
18315
18926
|
}
|
|
18316
|
-
if (!current || !
|
|
18927
|
+
if (!current || !Node13.isCallExpression(current)) {
|
|
18317
18928
|
return void 0;
|
|
18318
18929
|
}
|
|
18319
18930
|
const expr = current.getExpression();
|
|
18320
|
-
if (
|
|
18931
|
+
if (Node13.isIdentifier(expr) && expr.getText() === "type") {
|
|
18321
18932
|
const args = current.getArguments();
|
|
18322
18933
|
const arg = args[0];
|
|
18323
|
-
if (arg &&
|
|
18934
|
+
if (arg && Node13.isObjectLiteralExpression(arg)) {
|
|
18324
18935
|
return arg;
|
|
18325
18936
|
}
|
|
18326
18937
|
return void 0;
|
|
18327
18938
|
}
|
|
18328
|
-
if (
|
|
18939
|
+
if (Node13.isPropertyAccessExpression(expr) && expr.getName() === "merge") {
|
|
18329
18940
|
const args = current.getArguments();
|
|
18330
18941
|
const arg = args[0];
|
|
18331
|
-
if (arg &&
|
|
18942
|
+
if (arg && Node13.isObjectLiteralExpression(arg)) {
|
|
18332
18943
|
return arg;
|
|
18333
18944
|
}
|
|
18334
18945
|
}
|
|
@@ -19013,7 +19624,7 @@ import { readdir as readdir2, readFile as readFile4, stat as stat4 } from "node:
|
|
|
19013
19624
|
import { join as join4, relative as relative4, sep as sep5 } from "node:path";
|
|
19014
19625
|
|
|
19015
19626
|
// packages/dbx-cli/manifest-extract/src/lib/extract-crud.ts
|
|
19016
|
-
import { Node as
|
|
19627
|
+
import { Node as Node14, Project as Project6 } from "ts-morph";
|
|
19017
19628
|
var SUPPORTED_VERBS = /* @__PURE__ */ new Set(["create", "read", "update", "delete", "query"]);
|
|
19018
19629
|
function extractCrudEntries(source) {
|
|
19019
19630
|
const project = new Project6({ useInMemoryFileSystem: true, skipAddingFilesFromTsConfig: true });
|
|
@@ -19039,9 +19650,9 @@ function extractCrudEntries(source) {
|
|
|
19039
19650
|
};
|
|
19040
19651
|
if (crudConfigType) {
|
|
19041
19652
|
const literal = crudConfigType.getTypeNode();
|
|
19042
|
-
if (literal &&
|
|
19653
|
+
if (literal && Node14.isTypeLiteral(literal)) {
|
|
19043
19654
|
for (const member of literal.getMembers()) {
|
|
19044
|
-
if (!
|
|
19655
|
+
if (!Node14.isPropertySignature(member)) {
|
|
19045
19656
|
continue;
|
|
19046
19657
|
}
|
|
19047
19658
|
const modelName = member.getName();
|
|
@@ -19053,9 +19664,9 @@ function extractCrudEntries(source) {
|
|
|
19053
19664
|
if (isNullLiteralType2(valueNode)) {
|
|
19054
19665
|
continue;
|
|
19055
19666
|
}
|
|
19056
|
-
if (
|
|
19667
|
+
if (Node14.isTypeLiteral(valueNode)) {
|
|
19057
19668
|
for (const verbMember of valueNode.getMembers()) {
|
|
19058
|
-
if (!
|
|
19669
|
+
if (!Node14.isPropertySignature(verbMember)) {
|
|
19059
19670
|
continue;
|
|
19060
19671
|
}
|
|
19061
19672
|
const verbName = verbMember.getName();
|
|
@@ -19076,9 +19687,9 @@ function extractCrudEntries(source) {
|
|
|
19076
19687
|
const functionTypeMap = findTypeAliasByEnding(sourceFile, "FunctionTypeMap");
|
|
19077
19688
|
if (functionTypeMap) {
|
|
19078
19689
|
const literal = functionTypeMap.getTypeNode();
|
|
19079
|
-
if (literal &&
|
|
19690
|
+
if (literal && Node14.isTypeLiteral(literal)) {
|
|
19080
19691
|
for (const member of literal.getMembers()) {
|
|
19081
|
-
if (!
|
|
19692
|
+
if (!Node14.isPropertySignature(member)) {
|
|
19082
19693
|
continue;
|
|
19083
19694
|
}
|
|
19084
19695
|
const key = member.getName();
|
|
@@ -19146,9 +19757,9 @@ function inferGroupName2(sourceFile) {
|
|
|
19146
19757
|
return void 0;
|
|
19147
19758
|
}
|
|
19148
19759
|
function isNullLiteralType2(node) {
|
|
19149
|
-
if (
|
|
19760
|
+
if (Node14.isLiteralTypeNode(node)) {
|
|
19150
19761
|
const literal = node.getLiteral();
|
|
19151
|
-
if (
|
|
19762
|
+
if (Node14.isNullLiteral(literal)) {
|
|
19152
19763
|
return true;
|
|
19153
19764
|
}
|
|
19154
19765
|
}
|
|
@@ -19156,9 +19767,9 @@ function isNullLiteralType2(node) {
|
|
|
19156
19767
|
}
|
|
19157
19768
|
function collectVerbEntries(input) {
|
|
19158
19769
|
const { modelName, verb, valueNode, entries, fallbackDescription, resolveTypeDocs } = input;
|
|
19159
|
-
if (
|
|
19770
|
+
if (Node14.isTypeLiteral(valueNode)) {
|
|
19160
19771
|
for (const specMember of valueNode.getMembers()) {
|
|
19161
|
-
if (!
|
|
19772
|
+
if (!Node14.isPropertySignature(specMember)) {
|
|
19162
19773
|
continue;
|
|
19163
19774
|
}
|
|
19164
19775
|
const specifier = specMember.getName();
|
|
@@ -19200,7 +19811,7 @@ function collectVerbEntries(input) {
|
|
|
19200
19811
|
});
|
|
19201
19812
|
}
|
|
19202
19813
|
function readTupleParamsResult(node) {
|
|
19203
|
-
if (!
|
|
19814
|
+
if (!Node14.isTupleTypeNode(node)) {
|
|
19204
19815
|
return void 0;
|
|
19205
19816
|
}
|
|
19206
19817
|
const elements = node.getElements();
|
|
@@ -19219,7 +19830,7 @@ function readBareParams(node) {
|
|
|
19219
19830
|
return { params, result: void 0 };
|
|
19220
19831
|
}
|
|
19221
19832
|
function typeNodeName(node) {
|
|
19222
|
-
if (
|
|
19833
|
+
if (Node14.isTypeReference(node)) {
|
|
19223
19834
|
return node.getTypeName().getText();
|
|
19224
19835
|
}
|
|
19225
19836
|
const text = node.getText().trim();
|
|
@@ -20058,7 +20669,7 @@ async function collectApiFiles2(rootAbs) {
|
|
|
20058
20669
|
// packages/dbx-components-mcp/src/tools/model-api-validate-app/extract-handlers.ts
|
|
20059
20670
|
import { readFile as readFile8 } from "node:fs/promises";
|
|
20060
20671
|
import { join as join8 } from "node:path";
|
|
20061
|
-
import { Node as
|
|
20672
|
+
import { Node as Node15, Project as Project8 } from "ts-morph";
|
|
20062
20673
|
var HANDLER_MAP_REL = "src/app/function/model/crud.functions.ts";
|
|
20063
20674
|
var VERB_TOKENS = [
|
|
20064
20675
|
{ token: "Create", verb: "create" },
|
|
@@ -20086,7 +20697,7 @@ async function extractHandlerEntries(apiAbs, apiDir) {
|
|
|
20086
20697
|
const verb = matchVerbFromConstName(name);
|
|
20087
20698
|
if (!verb) continue;
|
|
20088
20699
|
const initializer = decl.getInitializer();
|
|
20089
|
-
if (!initializer || !
|
|
20700
|
+
if (!initializer || !Node15.isObjectLiteralExpression(initializer)) continue;
|
|
20090
20701
|
verbsFound.push(verb);
|
|
20091
20702
|
collectVerbMap({ verb, mapLiteral: initializer, sourceFileRel, entries });
|
|
20092
20703
|
}
|
|
@@ -20104,7 +20715,7 @@ function matchVerbFromConstName(name) {
|
|
|
20104
20715
|
function collectVerbMap(input) {
|
|
20105
20716
|
const { verb, mapLiteral, sourceFileRel, entries } = input;
|
|
20106
20717
|
for (const prop of mapLiteral.getProperties()) {
|
|
20107
|
-
if (!
|
|
20718
|
+
if (!Node15.isPropertyAssignment(prop)) continue;
|
|
20108
20719
|
const model = prop.getName();
|
|
20109
20720
|
const value = prop.getInitializer();
|
|
20110
20721
|
if (!value) continue;
|
|
@@ -20123,25 +20734,25 @@ function collectVerbMap(input) {
|
|
|
20123
20734
|
}
|
|
20124
20735
|
}
|
|
20125
20736
|
function readSpecifierHandler(node) {
|
|
20126
|
-
if (!
|
|
20737
|
+
if (!Node15.isCallExpression(node)) return void 0;
|
|
20127
20738
|
const expr = node.getExpression();
|
|
20128
20739
|
if (!expr.getText().endsWith("onCallSpecifierHandler")) return void 0;
|
|
20129
20740
|
const args = node.getArguments();
|
|
20130
20741
|
if (args.length === 0) return void 0;
|
|
20131
20742
|
const firstArg = args[0];
|
|
20132
|
-
if (!
|
|
20743
|
+
if (!Node15.isObjectLiteralExpression(firstArg)) return void 0;
|
|
20133
20744
|
const out = /* @__PURE__ */ new Map();
|
|
20134
20745
|
for (const prop of firstArg.getProperties()) {
|
|
20135
|
-
if (!
|
|
20746
|
+
if (!Node15.isPropertyAssignment(prop) && !Node15.isShorthandPropertyAssignment(prop)) continue;
|
|
20136
20747
|
const specifier = prop.getName();
|
|
20137
|
-
const handlerName = readBareHandlerName(
|
|
20748
|
+
const handlerName = readBareHandlerName(Node15.isPropertyAssignment(prop) ? prop.getInitializerOrThrow() : prop) ?? "(unresolved)";
|
|
20138
20749
|
out.set(specifier, handlerName);
|
|
20139
20750
|
}
|
|
20140
20751
|
return out;
|
|
20141
20752
|
}
|
|
20142
20753
|
function readBareHandlerName(node) {
|
|
20143
|
-
if (
|
|
20144
|
-
if (
|
|
20754
|
+
if (Node15.isIdentifier(node)) return node.getText();
|
|
20755
|
+
if (Node15.isShorthandPropertyAssignment(node)) return node.getName();
|
|
20145
20756
|
const text = node.getText().split(/\s|\(/)[0];
|
|
20146
20757
|
if (/^[A-Za-z_]\w*$/.test(text)) return text;
|
|
20147
20758
|
return void 0;
|
|
@@ -20639,7 +21250,7 @@ async function readModelSources(folderPath, files) {
|
|
|
20639
21250
|
}
|
|
20640
21251
|
|
|
20641
21252
|
// packages/dbx-components-mcp/src/tools/model-validate-folder/rules.ts
|
|
20642
|
-
function runRules3(inspection) {
|
|
21253
|
+
function runRules3(inspection, options) {
|
|
20643
21254
|
const violations = [];
|
|
20644
21255
|
if (inspection.status === "not-found") {
|
|
20645
21256
|
pushViolation3(violations, {
|
|
@@ -20672,16 +21283,16 @@ function runRules3(inspection) {
|
|
|
20672
21283
|
}
|
|
20673
21284
|
checkRequiredFiles(inspection, violations);
|
|
20674
21285
|
checkStrayFiles(inspection, violations);
|
|
20675
|
-
checkSourceContents(inspection, violations);
|
|
21286
|
+
checkSourceContents(inspection, violations, options);
|
|
20676
21287
|
return violations;
|
|
20677
21288
|
}
|
|
20678
|
-
function checkSourceContents(inspection, violations) {
|
|
21289
|
+
function checkSourceContents(inspection, violations, options) {
|
|
20679
21290
|
const sources = inspection.sources;
|
|
20680
21291
|
if (!sources || sources.length === 0) {
|
|
20681
21292
|
return;
|
|
20682
21293
|
}
|
|
20683
21294
|
const validatorSources = sources.map((s) => ({ name: s.filename, text: s.text }));
|
|
20684
|
-
const result = validateFirebaseModelSources(validatorSources);
|
|
21295
|
+
const result = validateFirebaseModelSources(validatorSources, options);
|
|
20685
21296
|
for (const v of result.violations) {
|
|
20686
21297
|
pushViolation3(violations, {
|
|
20687
21298
|
code: v.code,
|
|
@@ -20742,8 +21353,8 @@ function pushViolation3(buffer, violation) {
|
|
|
20742
21353
|
}
|
|
20743
21354
|
|
|
20744
21355
|
// packages/dbx-components-mcp/src/tools/model-validate-folder/validate.ts
|
|
20745
|
-
function validateModelFolders(inspections) {
|
|
20746
|
-
return aggregateFolderRules({ inspections, runRules: runRules3 });
|
|
21356
|
+
function validateModelFolders(inspections, options) {
|
|
21357
|
+
return aggregateFolderRules({ inspections, runRules: (inspection) => runRules3(inspection, options) });
|
|
20747
21358
|
}
|
|
20748
21359
|
|
|
20749
21360
|
// packages/dbx-components-mcp/src/tools/model-validate-folder.tool.ts
|
|
@@ -20777,12 +21388,16 @@ var DBX_MODEL_VALIDATE_FOLDER_TOOL = {
|
|
|
20777
21388
|
}
|
|
20778
21389
|
}
|
|
20779
21390
|
};
|
|
20780
|
-
|
|
20781
|
-
|
|
20782
|
-
|
|
20783
|
-
|
|
20784
|
-
|
|
20785
|
-
|
|
21391
|
+
// @__NO_SIDE_EFFECTS__
|
|
21392
|
+
function createModelValidateFolderTool(options = {}) {
|
|
21393
|
+
const { ruleOptions } = options;
|
|
21394
|
+
return createFolderValidateTool({
|
|
21395
|
+
definition: DBX_MODEL_VALIDATE_FOLDER_TOOL,
|
|
21396
|
+
inspectFolder,
|
|
21397
|
+
validate: (inspections) => validateModelFolders(inspections, ruleOptions),
|
|
21398
|
+
format: formatResult3
|
|
21399
|
+
});
|
|
21400
|
+
}
|
|
20786
21401
|
|
|
20787
21402
|
// packages/dbx-components-mcp/src/tools/model-store-scaffold.tool.ts
|
|
20788
21403
|
import { type as type41 } from "arktype";
|
|
@@ -21343,7 +21958,7 @@ function paramsHasFixtureDependencies(params) {
|
|
|
21343
21958
|
}
|
|
21344
21959
|
|
|
21345
21960
|
// packages/dbx-components-mcp/src/tools/model-fixture-shared/extract.ts
|
|
21346
|
-
import { Node as
|
|
21961
|
+
import { Node as Node16, Project as Project9, SyntaxKind as SyntaxKind7 } from "ts-morph";
|
|
21347
21962
|
|
|
21348
21963
|
// packages/dbx-components-mcp/src/tools/model-fixture-shared/framework-fixtures.ts
|
|
21349
21964
|
var KNOWN_NON_MODEL_FIXTURE_FAMILIES = [
|
|
@@ -21455,7 +22070,7 @@ function extendsByName(cls, baseName) {
|
|
|
21455
22070
|
const extendsClause = cls.getExtends();
|
|
21456
22071
|
if (!extendsClause) return false;
|
|
21457
22072
|
const expr = extendsClause.getExpression();
|
|
21458
|
-
if (
|
|
22073
|
+
if (Node16.isIdentifier(expr)) {
|
|
21459
22074
|
return expr.getText() === baseName;
|
|
21460
22075
|
}
|
|
21461
22076
|
return extendsClause.getText().startsWith(baseName);
|
|
@@ -21583,14 +22198,14 @@ function readContextFactory(decl) {
|
|
|
21583
22198
|
const initializer = decl.getInitializer();
|
|
21584
22199
|
if (!initializer) return void 0;
|
|
21585
22200
|
let bodyExpr;
|
|
21586
|
-
if (
|
|
22201
|
+
if (Node16.isArrowFunction(initializer) || Node16.isFunctionExpression(initializer)) {
|
|
21587
22202
|
bodyExpr = readSingleReturnExpression(initializer);
|
|
21588
22203
|
} else {
|
|
21589
22204
|
bodyExpr = initializer;
|
|
21590
22205
|
}
|
|
21591
|
-
if (!bodyExpr || !
|
|
22206
|
+
if (!bodyExpr || !Node16.isCallExpression(bodyExpr)) return void 0;
|
|
21592
22207
|
const callee = bodyExpr.getExpression();
|
|
21593
|
-
if (!
|
|
22208
|
+
if (!Node16.isIdentifier(callee)) return void 0;
|
|
21594
22209
|
const calleeName = callee.getText();
|
|
21595
22210
|
let family;
|
|
21596
22211
|
if (calleeName !== MODEL_FACTORY_NAME) {
|
|
@@ -21605,9 +22220,9 @@ function readContextFactory(decl) {
|
|
|
21605
22220
|
let parentFixtureFieldFromGetCollection;
|
|
21606
22221
|
if (args.length > 0) {
|
|
21607
22222
|
const first = args[0];
|
|
21608
|
-
if (
|
|
22223
|
+
if (Node16.isObjectLiteralExpression(first)) {
|
|
21609
22224
|
const getCollection = first.getProperty("getCollection");
|
|
21610
|
-
if (getCollection &&
|
|
22225
|
+
if (getCollection && Node16.isPropertyAssignment(getCollection)) {
|
|
21611
22226
|
const fnInit = getCollection.getInitializer();
|
|
21612
22227
|
const arity = readArrowParameterCount(fnInit);
|
|
21613
22228
|
if (arity >= 2) {
|
|
@@ -21658,7 +22273,7 @@ function familyFromExtends(cls) {
|
|
|
21658
22273
|
if (!ext) return void 0;
|
|
21659
22274
|
const expr = ext.getExpression();
|
|
21660
22275
|
let baseName;
|
|
21661
|
-
if (
|
|
22276
|
+
if (Node16.isIdentifier(expr)) {
|
|
21662
22277
|
baseName = expr.getText();
|
|
21663
22278
|
} else {
|
|
21664
22279
|
const text = ext.getText();
|
|
@@ -21671,9 +22286,9 @@ function familyFromExtends(cls) {
|
|
|
21671
22286
|
function readSingleReturnExpression(fn) {
|
|
21672
22287
|
const body = fn.getBody();
|
|
21673
22288
|
if (!body) return void 0;
|
|
21674
|
-
if (
|
|
22289
|
+
if (Node16.isBlock(body)) {
|
|
21675
22290
|
for (const stmt of body.getStatements()) {
|
|
21676
|
-
if (
|
|
22291
|
+
if (Node16.isReturnStatement(stmt)) {
|
|
21677
22292
|
return stmt.getExpression();
|
|
21678
22293
|
}
|
|
21679
22294
|
}
|
|
@@ -21683,19 +22298,19 @@ function readSingleReturnExpression(fn) {
|
|
|
21683
22298
|
}
|
|
21684
22299
|
function readArrowParameterCount(fn) {
|
|
21685
22300
|
if (!fn) return 0;
|
|
21686
|
-
if (
|
|
22301
|
+
if (Node16.isArrowFunction(fn) || Node16.isFunctionExpression(fn) || Node16.isFunctionDeclaration(fn) || Node16.isMethodDeclaration(fn)) {
|
|
21687
22302
|
return fn.getParameters().length;
|
|
21688
22303
|
}
|
|
21689
22304
|
return 0;
|
|
21690
22305
|
}
|
|
21691
22306
|
function readParamsAccessFieldName(fn) {
|
|
21692
22307
|
if (!fn) return void 0;
|
|
21693
|
-
if (!
|
|
22308
|
+
if (!Node16.isArrowFunction(fn) && !Node16.isFunctionExpression(fn)) return void 0;
|
|
21694
22309
|
const body = fn.getBody();
|
|
21695
22310
|
if (!body) return void 0;
|
|
21696
22311
|
for (const access2 of body.getDescendantsOfKind(SyntaxKind7.PropertyAccessExpression)) {
|
|
21697
22312
|
const expr = access2.getExpression();
|
|
21698
|
-
if (
|
|
22313
|
+
if (Node16.isIdentifier(expr) && expr.getText() === "params") {
|
|
21699
22314
|
return access2.getName();
|
|
21700
22315
|
}
|
|
21701
22316
|
}
|
|
@@ -21736,9 +22351,9 @@ function collectSingletonsByFactory(sourceFile) {
|
|
|
21736
22351
|
for (const stmt of sourceFile.getVariableStatements()) {
|
|
21737
22352
|
for (const decl of stmt.getDeclarations()) {
|
|
21738
22353
|
const init = decl.getInitializer();
|
|
21739
|
-
if (!init || !
|
|
22354
|
+
if (!init || !Node16.isCallExpression(init)) continue;
|
|
21740
22355
|
const callee = init.getExpression();
|
|
21741
|
-
if (!
|
|
22356
|
+
if (!Node16.isIdentifier(callee)) continue;
|
|
21742
22357
|
out.set(callee.getText(), decl.getName());
|
|
21743
22358
|
}
|
|
21744
22359
|
}
|
|
@@ -22986,7 +23601,7 @@ import { resolve as resolve32 } from "node:path";
|
|
|
22986
23601
|
import { type as type47 } from "arktype";
|
|
22987
23602
|
|
|
22988
23603
|
// packages/dbx-components-mcp/src/tools/model-test-shared/extract.ts
|
|
22989
|
-
import { Node as
|
|
23604
|
+
import { Node as Node17, Project as Project11, SyntaxKind as SyntaxKind8 } from "ts-morph";
|
|
22990
23605
|
var SPEC_FILE_VIRTUAL_PATH = "__spec__/spec.ts";
|
|
22991
23606
|
var FIXTURE_CONTEXT_SUFFIX = "Context";
|
|
22992
23607
|
var FIXTURE_FACTORY_SUFFIX = "ContextFactory";
|
|
@@ -23007,9 +23622,9 @@ function extractSpecTreeFromText(input) {
|
|
|
23007
23622
|
const counts = { describe: 0, it: 0, fixture: 0 };
|
|
23008
23623
|
const rootChildren = [];
|
|
23009
23624
|
for (const stmt of sourceFile.getStatements()) {
|
|
23010
|
-
if (!
|
|
23625
|
+
if (!Node17.isExpressionStatement(stmt)) continue;
|
|
23011
23626
|
const expr = stmt.getExpression();
|
|
23012
|
-
if (!
|
|
23627
|
+
if (!Node17.isCallExpression(expr)) continue;
|
|
23013
23628
|
const node = walkCall(expr, { prefix, knownFixtureNames, helperNames, counts });
|
|
23014
23629
|
if (node !== void 0) {
|
|
23015
23630
|
rootChildren.push(node);
|
|
@@ -23175,7 +23790,7 @@ function fixtureNode(input) {
|
|
|
23175
23790
|
const { call, calleeName, ctx, line, endLine } = input;
|
|
23176
23791
|
const args = call.getArguments();
|
|
23177
23792
|
const argsObject = args[0];
|
|
23178
|
-
const parentVars =
|
|
23793
|
+
const parentVars = Node17.isObjectLiteralExpression(argsObject) ? readParentVars(argsObject) : [];
|
|
23179
23794
|
const callback = findCallback(call);
|
|
23180
23795
|
const varName = callback === void 0 ? void 0 : readCallbackVarName(callback);
|
|
23181
23796
|
const model = stripFixturePrefix(calleeName, ctx.prefix);
|
|
@@ -23213,11 +23828,11 @@ function stripFixturePrefix(name, prefix) {
|
|
|
23213
23828
|
}
|
|
23214
23829
|
function readTitle(arg) {
|
|
23215
23830
|
if (arg === void 0) return { title: void 0, titleIsTemplate: false };
|
|
23216
|
-
if (
|
|
23831
|
+
if (Node17.isStringLiteral(arg) || Node17.isNoSubstitutionTemplateLiteral(arg)) {
|
|
23217
23832
|
const result2 = { title: arg.getLiteralText(), titleIsTemplate: false };
|
|
23218
23833
|
return result2;
|
|
23219
23834
|
}
|
|
23220
|
-
if (
|
|
23835
|
+
if (Node17.isTemplateExpression(arg)) {
|
|
23221
23836
|
const result2 = { title: arg.getText(), titleIsTemplate: true };
|
|
23222
23837
|
return result2;
|
|
23223
23838
|
}
|
|
@@ -23228,7 +23843,7 @@ function findCallback(call) {
|
|
|
23228
23843
|
const args = call.getArguments();
|
|
23229
23844
|
for (let i = args.length - 1; i >= 0; i--) {
|
|
23230
23845
|
const a = args[i];
|
|
23231
|
-
if (
|
|
23846
|
+
if (Node17.isArrowFunction(a) || Node17.isFunctionExpression(a)) {
|
|
23232
23847
|
return a;
|
|
23233
23848
|
}
|
|
23234
23849
|
}
|
|
@@ -23239,7 +23854,7 @@ function readCallbackVarName(callback) {
|
|
|
23239
23854
|
if (params.length === 0) return void 0;
|
|
23240
23855
|
const first = params[0];
|
|
23241
23856
|
const nameNode = first.getNameNode();
|
|
23242
|
-
if (
|
|
23857
|
+
if (Node17.isIdentifier(nameNode)) {
|
|
23243
23858
|
return nameNode.getText();
|
|
23244
23859
|
}
|
|
23245
23860
|
return void 0;
|
|
@@ -23247,13 +23862,13 @@ function readCallbackVarName(callback) {
|
|
|
23247
23862
|
function readParentVars(obj) {
|
|
23248
23863
|
const out = [];
|
|
23249
23864
|
for (const prop of obj.getProperties()) {
|
|
23250
|
-
if (
|
|
23865
|
+
if (Node17.isShorthandPropertyAssignment(prop)) {
|
|
23251
23866
|
out.push(prop.getName());
|
|
23252
23867
|
continue;
|
|
23253
23868
|
}
|
|
23254
|
-
if (
|
|
23869
|
+
if (Node17.isPropertyAssignment(prop)) {
|
|
23255
23870
|
const init = prop.getInitializer();
|
|
23256
|
-
if (init !== void 0 &&
|
|
23871
|
+
if (init !== void 0 && Node17.isIdentifier(init)) {
|
|
23257
23872
|
out.push(init.getText());
|
|
23258
23873
|
}
|
|
23259
23874
|
}
|
|
@@ -23262,10 +23877,10 @@ function readParentVars(obj) {
|
|
|
23262
23877
|
}
|
|
23263
23878
|
function walkCallbackBody(callback, ctx) {
|
|
23264
23879
|
const body = callback.getBody();
|
|
23265
|
-
if (
|
|
23880
|
+
if (Node17.isBlock(body)) {
|
|
23266
23881
|
return walkBlock(body, ctx);
|
|
23267
23882
|
}
|
|
23268
|
-
if (
|
|
23883
|
+
if (Node17.isCallExpression(body)) {
|
|
23269
23884
|
const child = walkCall(body, ctx);
|
|
23270
23885
|
return child === void 0 ? [] : [child];
|
|
23271
23886
|
}
|
|
@@ -23274,9 +23889,9 @@ function walkCallbackBody(callback, ctx) {
|
|
|
23274
23889
|
function walkBlock(block, ctx) {
|
|
23275
23890
|
const children = [];
|
|
23276
23891
|
for (const stmt of block.getStatements()) {
|
|
23277
|
-
if (!
|
|
23892
|
+
if (!Node17.isExpressionStatement(stmt)) continue;
|
|
23278
23893
|
const expr = stmt.getExpression();
|
|
23279
|
-
if (!
|
|
23894
|
+
if (!Node17.isCallExpression(expr)) continue;
|
|
23280
23895
|
const child = walkCall(expr, ctx);
|
|
23281
23896
|
if (child !== void 0) {
|
|
23282
23897
|
children.push(child);
|
|
@@ -23286,7 +23901,7 @@ function walkBlock(block, ctx) {
|
|
|
23286
23901
|
}
|
|
23287
23902
|
function getCalleeName(call) {
|
|
23288
23903
|
const expr = call.getExpression();
|
|
23289
|
-
if (
|
|
23904
|
+
if (Node17.isIdentifier(expr)) return expr.getText();
|
|
23290
23905
|
return void 0;
|
|
23291
23906
|
}
|
|
23292
23907
|
function collectHelpers(sourceFile) {
|
|
@@ -23302,7 +23917,7 @@ function collectHelpers(sourceFile) {
|
|
|
23302
23917
|
for (const decl of v.getDeclarations()) {
|
|
23303
23918
|
const init = decl.getInitializer();
|
|
23304
23919
|
if (init === void 0) continue;
|
|
23305
|
-
if (!
|
|
23920
|
+
if (!Node17.isArrowFunction(init) && !Node17.isFunctionExpression(init)) continue;
|
|
23306
23921
|
const usage = scanForDescribeOrIt(init);
|
|
23307
23922
|
if (!usage.emitsDescribe && !usage.emitsIt) continue;
|
|
23308
23923
|
out.push({ name: decl.getName(), line: decl.getStartLineNumber(), endLine: decl.getEndLineNumber(), emitsDescribe: usage.emitsDescribe, emitsIt: usage.emitsIt });
|
|
@@ -23316,7 +23931,7 @@ function scanForDescribeOrIt(node) {
|
|
|
23316
23931
|
if (descendant.getKind() !== SyntaxKind8.CallExpression) return;
|
|
23317
23932
|
const call = descendant;
|
|
23318
23933
|
const expr = call.getExpression();
|
|
23319
|
-
if (!
|
|
23934
|
+
if (!Node17.isIdentifier(expr)) return;
|
|
23320
23935
|
const name = expr.getText();
|
|
23321
23936
|
if (DESCRIBE_NAMES.has(name)) usage.emitsDescribe = true;
|
|
23322
23937
|
if (isItName(name)) usage.emitsIt = true;
|
|
@@ -24089,10 +24704,10 @@ var modelHierarchyTool = {
|
|
|
24089
24704
|
};
|
|
24090
24705
|
|
|
24091
24706
|
// packages/dbx-components-mcp/src/tools/storagefile-m-validate-app/extract.ts
|
|
24092
|
-
import { Node as
|
|
24707
|
+
import { Node as Node19, SyntaxKind as SyntaxKind10 } from "ts-morph";
|
|
24093
24708
|
|
|
24094
24709
|
// packages/dbx-components-mcp/src/tools/_validate/ast.ts
|
|
24095
|
-
import { Node as
|
|
24710
|
+
import { Node as Node18, Project as Project12, SyntaxKind as SyntaxKind9 } from "ts-morph";
|
|
24096
24711
|
var COMPONENT_VIRTUAL_PREFIX = "__component__/";
|
|
24097
24712
|
var API_VIRTUAL_PREFIX = "__api__/";
|
|
24098
24713
|
// @__NO_SIDE_EFFECTS__
|
|
@@ -24117,28 +24732,28 @@ function componentRelPath(sourceFile) {
|
|
|
24117
24732
|
}
|
|
24118
24733
|
function unwrapAsExpressions(node) {
|
|
24119
24734
|
let current = node;
|
|
24120
|
-
while (current &&
|
|
24735
|
+
while (current && Node18.isAsExpression(current)) {
|
|
24121
24736
|
current = current.getExpression();
|
|
24122
24737
|
}
|
|
24123
24738
|
return current;
|
|
24124
24739
|
}
|
|
24125
24740
|
function asObjectLiteral(node) {
|
|
24126
24741
|
const inner = unwrapAsExpressions(node);
|
|
24127
|
-
if (inner &&
|
|
24742
|
+
if (inner && Node18.isObjectLiteralExpression(inner)) {
|
|
24128
24743
|
return inner;
|
|
24129
24744
|
}
|
|
24130
24745
|
return void 0;
|
|
24131
24746
|
}
|
|
24132
24747
|
function asArrayLiteral(node) {
|
|
24133
24748
|
const inner = unwrapAsExpressions(node);
|
|
24134
|
-
if (inner &&
|
|
24749
|
+
if (inner && Node18.isArrayLiteralExpression(inner)) {
|
|
24135
24750
|
return inner;
|
|
24136
24751
|
}
|
|
24137
24752
|
return void 0;
|
|
24138
24753
|
}
|
|
24139
24754
|
function readStringLiteralInitializer(decl) {
|
|
24140
24755
|
const initializer = unwrapAsExpressions(decl.getInitializer());
|
|
24141
|
-
if (initializer &&
|
|
24756
|
+
if (initializer && Node18.isStringLiteral(initializer)) {
|
|
24142
24757
|
return initializer.getLiteralText();
|
|
24143
24758
|
}
|
|
24144
24759
|
return void 0;
|
|
@@ -24150,37 +24765,37 @@ function typeAnnotationText(node) {
|
|
|
24150
24765
|
function getPropertyInitializer(obj, name) {
|
|
24151
24766
|
const prop = obj.getProperty(name);
|
|
24152
24767
|
if (!prop) return void 0;
|
|
24153
|
-
if (
|
|
24768
|
+
if (Node18.isPropertyAssignment(prop)) {
|
|
24154
24769
|
return prop.getInitializer();
|
|
24155
24770
|
}
|
|
24156
|
-
if (
|
|
24771
|
+
if (Node18.isShorthandPropertyAssignment(prop)) {
|
|
24157
24772
|
return prop.getNameNode();
|
|
24158
24773
|
}
|
|
24159
24774
|
return void 0;
|
|
24160
24775
|
}
|
|
24161
24776
|
function readStringProperty2(obj, name) {
|
|
24162
24777
|
const init = unwrapAsExpressions(getPropertyInitializer(obj, name));
|
|
24163
|
-
if (init &&
|
|
24778
|
+
if (init && Node18.isStringLiteral(init)) {
|
|
24164
24779
|
return init.getLiteralText();
|
|
24165
24780
|
}
|
|
24166
24781
|
return void 0;
|
|
24167
24782
|
}
|
|
24168
24783
|
function readIdentifierProperty(obj, name) {
|
|
24169
24784
|
const init = unwrapAsExpressions(getPropertyInitializer(obj, name));
|
|
24170
|
-
if (init &&
|
|
24785
|
+
if (init && Node18.isIdentifier(init)) {
|
|
24171
24786
|
return init.getText();
|
|
24172
24787
|
}
|
|
24173
24788
|
return void 0;
|
|
24174
24789
|
}
|
|
24175
24790
|
function findReturnExpression(fn) {
|
|
24176
|
-
if (!
|
|
24791
|
+
if (!Node18.isArrowFunction(fn) && !Node18.isFunctionDeclaration(fn) && !Node18.isFunctionExpression(fn)) {
|
|
24177
24792
|
return void 0;
|
|
24178
24793
|
}
|
|
24179
24794
|
const body = fn.getBody();
|
|
24180
24795
|
if (!body) return void 0;
|
|
24181
|
-
if (
|
|
24796
|
+
if (Node18.isBlock(body)) {
|
|
24182
24797
|
for (const stmt of body.getStatements()) {
|
|
24183
|
-
if (
|
|
24798
|
+
if (Node18.isReturnStatement(stmt)) {
|
|
24184
24799
|
return stmt.getExpression();
|
|
24185
24800
|
}
|
|
24186
24801
|
}
|
|
@@ -24200,7 +24815,7 @@ function findLocalVariable(sf, name) {
|
|
|
24200
24815
|
return void 0;
|
|
24201
24816
|
}
|
|
24202
24817
|
function collectTypeofReferences(node, out) {
|
|
24203
|
-
if (
|
|
24818
|
+
if (Node18.isTypeQuery(node)) {
|
|
24204
24819
|
out.push(node.getExprName().getText());
|
|
24205
24820
|
return;
|
|
24206
24821
|
}
|
|
@@ -24402,7 +25017,7 @@ function buildApiFunctionIndex(sources) {
|
|
|
24402
25017
|
for (const decl of stmt.getDeclarations()) {
|
|
24403
25018
|
const initializer = unwrapAsExpressions(decl.getInitializer());
|
|
24404
25019
|
if (!initializer) continue;
|
|
24405
|
-
if (
|
|
25020
|
+
if (Node19.isArrowFunction(initializer) || Node19.isFunctionExpression(initializer)) {
|
|
24406
25021
|
map.set(decl.getName(), { node: initializer, sourceFile: sf });
|
|
24407
25022
|
}
|
|
24408
25023
|
}
|
|
@@ -24415,34 +25030,34 @@ function collectInitializerBindingsFromValue(options) {
|
|
|
24415
25030
|
const { node, sf, index, resolved, unresolved, visited } = options;
|
|
24416
25031
|
const inner = unwrapAsExpressions(node);
|
|
24417
25032
|
if (!inner) return false;
|
|
24418
|
-
if (
|
|
25033
|
+
if (Node19.isArrayLiteralExpression(inner)) {
|
|
24419
25034
|
for (const el of inner.getElements()) {
|
|
24420
|
-
if (
|
|
25035
|
+
if (Node19.isSpreadElement(el)) {
|
|
24421
25036
|
const spreadInner = el.getExpression();
|
|
24422
|
-
if (
|
|
25037
|
+
if (Node19.isIdentifier(spreadInner)) {
|
|
24423
25038
|
collectInitializerBindingsFromIdentifier({ name: spreadInner.getText(), sf, index, resolved, unresolved, visited });
|
|
24424
|
-
} else if (
|
|
25039
|
+
} else if (Node19.isCallExpression(spreadInner)) {
|
|
24425
25040
|
const callee = spreadInner.getExpression();
|
|
24426
|
-
if (
|
|
25041
|
+
if (Node19.isIdentifier(callee)) {
|
|
24427
25042
|
collectInitializerBindingsFromIdentifier({ name: callee.getText(), sf, index, resolved, unresolved, visited });
|
|
24428
25043
|
}
|
|
24429
25044
|
}
|
|
24430
25045
|
continue;
|
|
24431
25046
|
}
|
|
24432
25047
|
const elementInner = unwrapAsExpressions(el);
|
|
24433
|
-
if (elementInner &&
|
|
25048
|
+
if (elementInner && Node19.isIdentifier(elementInner)) {
|
|
24434
25049
|
resolved.push(elementInner.getText());
|
|
24435
25050
|
}
|
|
24436
25051
|
}
|
|
24437
25052
|
return true;
|
|
24438
25053
|
}
|
|
24439
|
-
if (
|
|
25054
|
+
if (Node19.isCallExpression(inner)) {
|
|
24440
25055
|
const callee = inner.getExpression();
|
|
24441
|
-
if (
|
|
25056
|
+
if (Node19.isIdentifier(callee)) {
|
|
24442
25057
|
return collectInitializerBindingsFromIdentifier({ name: callee.getText(), sf, index, resolved, unresolved, visited });
|
|
24443
25058
|
}
|
|
24444
25059
|
}
|
|
24445
|
-
if (
|
|
25060
|
+
if (Node19.isIdentifier(inner)) {
|
|
24446
25061
|
return collectInitializerBindingsFromIdentifier({ name: inner.getText(), sf, index, resolved, unresolved, visited });
|
|
24447
25062
|
}
|
|
24448
25063
|
return false;
|
|
@@ -24481,15 +25096,15 @@ function collectUploadInitializerArray(options) {
|
|
|
24481
25096
|
const { initializerArr, sf, index, direct, spreads, resolved, unresolved } = options;
|
|
24482
25097
|
const visited = /* @__PURE__ */ new Set();
|
|
24483
25098
|
for (const el of initializerArr.getElements()) {
|
|
24484
|
-
if (
|
|
25099
|
+
if (Node19.isSpreadElement(el)) {
|
|
24485
25100
|
const inner2 = el.getExpression();
|
|
24486
|
-
if (
|
|
25101
|
+
if (Node19.isIdentifier(inner2)) {
|
|
24487
25102
|
const name = inner2.getText();
|
|
24488
25103
|
spreads.push(name);
|
|
24489
25104
|
collectInitializerBindingsFromIdentifier({ name, sf, index, resolved, unresolved, visited });
|
|
24490
|
-
} else if (
|
|
25105
|
+
} else if (Node19.isCallExpression(inner2)) {
|
|
24491
25106
|
const callee = inner2.getExpression();
|
|
24492
|
-
if (
|
|
25107
|
+
if (Node19.isIdentifier(callee)) {
|
|
24493
25108
|
const name = callee.getText();
|
|
24494
25109
|
spreads.push(name);
|
|
24495
25110
|
collectInitializerBindingsFromIdentifier({ name, sf, index, resolved, unresolved, visited });
|
|
@@ -24498,7 +25113,7 @@ function collectUploadInitializerArray(options) {
|
|
|
24498
25113
|
continue;
|
|
24499
25114
|
}
|
|
24500
25115
|
const inner = unwrapAsExpressions(el);
|
|
24501
|
-
if (inner &&
|
|
25116
|
+
if (inner && Node19.isIdentifier(inner)) {
|
|
24502
25117
|
const name = inner.getText();
|
|
24503
25118
|
direct.push(name);
|
|
24504
25119
|
resolved.push(name);
|
|
@@ -24511,7 +25126,7 @@ function extractUploadServiceCalls(sources, index) {
|
|
|
24511
25126
|
const rel = apiRelPath(sf);
|
|
24512
25127
|
for (const call of sf.getDescendantsOfKind(SyntaxKind10.CallExpression)) {
|
|
24513
25128
|
const expr = call.getExpression();
|
|
24514
|
-
if (!
|
|
25129
|
+
if (!Node19.isIdentifier(expr) || expr.getText() !== UPLOAD_SERVICE_FACTORY) continue;
|
|
24515
25130
|
const args = call.getArguments();
|
|
24516
25131
|
const first = args[0] ? resolveObjectArg(args[0], sf) : void 0;
|
|
24517
25132
|
if (!first) continue;
|
|
@@ -24539,12 +25154,12 @@ function extractUploadServiceCalls(sources, index) {
|
|
|
24539
25154
|
function resolveObjectArg(node, sf) {
|
|
24540
25155
|
const inner = unwrapAsExpressions(node);
|
|
24541
25156
|
if (!inner) return void 0;
|
|
24542
|
-
if (
|
|
24543
|
-
if (
|
|
25157
|
+
if (Node19.isObjectLiteralExpression(inner)) return inner;
|
|
25158
|
+
if (Node19.isIdentifier(inner)) {
|
|
24544
25159
|
const declNode = findLocalVariable(sf, inner.getText());
|
|
24545
25160
|
if (declNode) {
|
|
24546
25161
|
const declInit = unwrapAsExpressions(declNode.getInitializer());
|
|
24547
|
-
if (declInit &&
|
|
25162
|
+
if (declInit && Node19.isObjectLiteralExpression(declInit)) {
|
|
24548
25163
|
return declInit;
|
|
24549
25164
|
}
|
|
24550
25165
|
}
|
|
@@ -24554,12 +25169,12 @@ function resolveObjectArg(node, sf) {
|
|
|
24554
25169
|
function resolveArrayFromProperty(obj, name, sf) {
|
|
24555
25170
|
const init = unwrapAsExpressions(getPropertyInitializer(obj, name));
|
|
24556
25171
|
if (!init) return void 0;
|
|
24557
|
-
if (
|
|
24558
|
-
if (
|
|
25172
|
+
if (Node19.isArrayLiteralExpression(init)) return init;
|
|
25173
|
+
if (Node19.isIdentifier(init)) {
|
|
24559
25174
|
const declNode = findLocalVariable(sf, init.getText());
|
|
24560
25175
|
if (declNode) {
|
|
24561
25176
|
const declInit = unwrapAsExpressions(declNode.getInitializer());
|
|
24562
|
-
if (declInit &&
|
|
25177
|
+
if (declInit && Node19.isArrayLiteralExpression(declInit)) {
|
|
24563
25178
|
return declInit;
|
|
24564
25179
|
}
|
|
24565
25180
|
}
|
|
@@ -24570,11 +25185,11 @@ function enclosingFactoryName(node) {
|
|
|
24570
25185
|
let current = node.getParent();
|
|
24571
25186
|
let result;
|
|
24572
25187
|
while (current) {
|
|
24573
|
-
if (
|
|
25188
|
+
if (Node19.isFunctionDeclaration(current)) {
|
|
24574
25189
|
result = current.getName() ?? void 0;
|
|
24575
25190
|
break;
|
|
24576
25191
|
}
|
|
24577
|
-
if (
|
|
25192
|
+
if (Node19.isVariableDeclaration(current)) {
|
|
24578
25193
|
result = current.getName();
|
|
24579
25194
|
break;
|
|
24580
25195
|
}
|
|
@@ -24628,10 +25243,10 @@ function extractProcessingConfigFromDecl(decl, rel) {
|
|
|
24628
25243
|
function readFlowSubtasks(obj) {
|
|
24629
25244
|
const flowInit = unwrapAsExpressions(getPropertyInitializer(obj, "flow"));
|
|
24630
25245
|
const subtasks = [];
|
|
24631
|
-
if (flowInit &&
|
|
25246
|
+
if (flowInit && Node19.isArrayLiteralExpression(flowInit)) {
|
|
24632
25247
|
for (const el of flowInit.getElements()) {
|
|
24633
25248
|
const inner = unwrapAsExpressions(el);
|
|
24634
|
-
if (inner &&
|
|
25249
|
+
if (inner && Node19.isObjectLiteralExpression(inner)) {
|
|
24635
25250
|
const subtask = readIdentifierProperty(inner, "subtask");
|
|
24636
25251
|
if (subtask) subtasks.push(subtask);
|
|
24637
25252
|
}
|
|
@@ -24645,7 +25260,7 @@ function extractProcessingHandlerCalls(sources) {
|
|
|
24645
25260
|
const rel = apiRelPath(sf);
|
|
24646
25261
|
for (const call of sf.getDescendantsOfKind(SyntaxKind10.CallExpression)) {
|
|
24647
25262
|
const expr = call.getExpression();
|
|
24648
|
-
if (!
|
|
25263
|
+
if (!Node19.isIdentifier(expr) || expr.getText() !== PROCESSING_HANDLER_FACTORY) continue;
|
|
24649
25264
|
const args = call.getArguments();
|
|
24650
25265
|
const first = args[0] ? resolveObjectArg(args[0], sf) : void 0;
|
|
24651
25266
|
if (!first) continue;
|
|
@@ -24666,7 +25281,7 @@ function extractProcessingHandlerCalls(sources) {
|
|
|
24666
25281
|
}
|
|
24667
25282
|
function collectProcessorArrayElements(processorsArr, direct, spreads) {
|
|
24668
25283
|
for (const el of processorsArr.getElements()) {
|
|
24669
|
-
if (
|
|
25284
|
+
if (Node19.isSpreadElement(el)) {
|
|
24670
25285
|
addProcessorIdentifierName(el.getExpression(), spreads);
|
|
24671
25286
|
continue;
|
|
24672
25287
|
}
|
|
@@ -24678,13 +25293,13 @@ function collectProcessorArrayElements(processorsArr, direct, spreads) {
|
|
|
24678
25293
|
}
|
|
24679
25294
|
function addProcessorIdentifierName(node, bucket) {
|
|
24680
25295
|
if (!node) return;
|
|
24681
|
-
if (
|
|
25296
|
+
if (Node19.isIdentifier(node)) {
|
|
24682
25297
|
bucket.push(node.getText());
|
|
24683
25298
|
return;
|
|
24684
25299
|
}
|
|
24685
|
-
if (
|
|
25300
|
+
if (Node19.isCallExpression(node)) {
|
|
24686
25301
|
const callee = node.getExpression();
|
|
24687
|
-
if (
|
|
25302
|
+
if (Node19.isIdentifier(callee)) {
|
|
24688
25303
|
bucket.push(callee.getText());
|
|
24689
25304
|
}
|
|
24690
25305
|
}
|
|
@@ -25676,7 +26291,7 @@ var storageFileMValidateFolderTool = createTwoSideValidateTool({
|
|
|
25676
26291
|
});
|
|
25677
26292
|
|
|
25678
26293
|
// packages/dbx-components-mcp/src/tools/notification-m-validate-app/extract.ts
|
|
25679
|
-
import { Node as
|
|
26294
|
+
import { Node as Node20, SyntaxKind as SyntaxKind11 } from "ts-morph";
|
|
25680
26295
|
var NOTIF_TEMPLATE_TYPE = "NotificationTemplateType";
|
|
25681
26296
|
var NOTIF_TEMPLATE_TYPE_INFO = "NotificationTemplateTypeInfo";
|
|
25682
26297
|
var NOTIF_TEMPLATE_TYPE_INFO_ARRAY = "NotificationTemplateTypeInfo[]";
|
|
@@ -25797,7 +26412,7 @@ function extractTemplateInfoAggregates(sources) {
|
|
|
25797
26412
|
const ids = [];
|
|
25798
26413
|
for (const el of arr.getElements()) {
|
|
25799
26414
|
const inner = unwrapAsExpressions(el);
|
|
25800
|
-
if (inner &&
|
|
26415
|
+
if (inner && Node20.isIdentifier(inner)) {
|
|
25801
26416
|
ids.push(inner.getText());
|
|
25802
26417
|
}
|
|
25803
26418
|
}
|
|
@@ -25843,7 +26458,7 @@ function extractTaskAllTypesAggregates(sources) {
|
|
|
25843
26458
|
const ids = [];
|
|
25844
26459
|
for (const el of arr.getElements()) {
|
|
25845
26460
|
const inner = unwrapAsExpressions(el);
|
|
25846
|
-
if (inner &&
|
|
26461
|
+
if (inner && Node20.isIdentifier(inner)) {
|
|
25847
26462
|
ids.push(inner.getText());
|
|
25848
26463
|
}
|
|
25849
26464
|
}
|
|
@@ -25871,14 +26486,14 @@ function extractTaskCheckpointAliases(sources) {
|
|
|
25871
26486
|
return out;
|
|
25872
26487
|
}
|
|
25873
26488
|
function collectStringLiterals(node, out) {
|
|
25874
|
-
if (
|
|
26489
|
+
if (Node20.isLiteralTypeNode(node)) {
|
|
25875
26490
|
const lit = node.getLiteral();
|
|
25876
|
-
if (
|
|
26491
|
+
if (Node20.isStringLiteral(lit)) {
|
|
25877
26492
|
out.push(lit.getLiteralText());
|
|
25878
26493
|
}
|
|
25879
26494
|
return;
|
|
25880
26495
|
}
|
|
25881
|
-
if (
|
|
26496
|
+
if (Node20.isUnionTypeNode(node)) {
|
|
25882
26497
|
for (const child of node.getTypeNodes()) {
|
|
25883
26498
|
collectStringLiterals(child, out);
|
|
25884
26499
|
}
|
|
@@ -25932,7 +26547,7 @@ function extractTemplateInfoRecord(sources, index) {
|
|
|
25932
26547
|
}
|
|
25933
26548
|
function tryReadTemplateInfoRecord(decl, rel, index) {
|
|
25934
26549
|
const initializer = unwrapAsExpressions(decl.getInitializer());
|
|
25935
|
-
if (!initializer || !
|
|
26550
|
+
if (!initializer || !Node20.isCallExpression(initializer)) return void 0;
|
|
25936
26551
|
if (initializer.getExpression().getText() !== TEMPLATE_INFO_RECORD_FACTORY) return void 0;
|
|
25937
26552
|
const args = initializer.getArguments();
|
|
25938
26553
|
const arr = args[0] ? asArrayLiteral(args[0]) : void 0;
|
|
@@ -25953,15 +26568,15 @@ function tryReadTemplateInfoRecord(decl, rel, index) {
|
|
|
25953
26568
|
}
|
|
25954
26569
|
function classifyInfoRecordArrayElements(arr, direct, spreads) {
|
|
25955
26570
|
for (const el of arr.getElements()) {
|
|
25956
|
-
if (
|
|
26571
|
+
if (Node20.isSpreadElement(el)) {
|
|
25957
26572
|
const inner2 = el.getExpression();
|
|
25958
|
-
if (
|
|
26573
|
+
if (Node20.isIdentifier(inner2)) {
|
|
25959
26574
|
spreads.push(inner2.getText());
|
|
25960
26575
|
}
|
|
25961
26576
|
continue;
|
|
25962
26577
|
}
|
|
25963
26578
|
const inner = unwrapAsExpressions(el);
|
|
25964
|
-
if (inner &&
|
|
26579
|
+
if (inner && Node20.isIdentifier(inner)) {
|
|
25965
26580
|
direct.push(inner.getText());
|
|
25966
26581
|
}
|
|
25967
26582
|
}
|
|
@@ -25988,7 +26603,7 @@ function extractTemplateInfoRecordWiring(sources) {
|
|
|
25988
26603
|
const first = args[0];
|
|
25989
26604
|
if (!first) continue;
|
|
25990
26605
|
const inner = unwrapAsExpressions(first);
|
|
25991
|
-
if (!inner || !
|
|
26606
|
+
if (!inner || !Node20.isIdentifier(inner)) continue;
|
|
25992
26607
|
const result = {
|
|
25993
26608
|
recordIdentifier: inner.getText(),
|
|
25994
26609
|
sourceFile: rel
|
|
@@ -26026,7 +26641,7 @@ function buildApiFunctionIndex2(sources) {
|
|
|
26026
26641
|
for (const decl of stmt.getDeclarations()) {
|
|
26027
26642
|
const initializer = unwrapAsExpressions(decl.getInitializer());
|
|
26028
26643
|
if (!initializer) continue;
|
|
26029
|
-
if (
|
|
26644
|
+
if (Node20.isArrowFunction(initializer) || Node20.isFunctionExpression(initializer)) {
|
|
26030
26645
|
map.set(decl.getName(), { node: initializer, sourceFile: sf, relPath: rel });
|
|
26031
26646
|
}
|
|
26032
26647
|
}
|
|
@@ -26062,11 +26677,11 @@ function extractTemplateConfigsArrayFactory(sources, index) {
|
|
|
26062
26677
|
const arr = returnExpr ? asArrayLiteral(returnExpr) : void 0;
|
|
26063
26678
|
if (arr) {
|
|
26064
26679
|
for (const el of arr.getElements()) {
|
|
26065
|
-
if (
|
|
26680
|
+
if (Node20.isSpreadElement(el)) {
|
|
26066
26681
|
const inner2 = el.getExpression();
|
|
26067
|
-
if (
|
|
26682
|
+
if (Node20.isCallExpression(inner2)) {
|
|
26068
26683
|
const callee = inner2.getExpression();
|
|
26069
|
-
if (
|
|
26684
|
+
if (Node20.isIdentifier(callee)) {
|
|
26070
26685
|
const callName = callee.getText();
|
|
26071
26686
|
spreadCalls.push(callName);
|
|
26072
26687
|
if (!index.functionsByName.has(callName)) {
|
|
@@ -26077,9 +26692,9 @@ function extractTemplateConfigsArrayFactory(sources, index) {
|
|
|
26077
26692
|
continue;
|
|
26078
26693
|
}
|
|
26079
26694
|
const inner = unwrapAsExpressions(el);
|
|
26080
|
-
if (inner &&
|
|
26695
|
+
if (inner && Node20.isCallExpression(inner)) {
|
|
26081
26696
|
const callee = inner.getExpression();
|
|
26082
|
-
if (
|
|
26697
|
+
if (Node20.isIdentifier(callee)) {
|
|
26083
26698
|
directCalls.push(callee.getText());
|
|
26084
26699
|
}
|
|
26085
26700
|
}
|
|
@@ -26128,11 +26743,11 @@ function collectFromArrayEntryFactory(name, out, index) {
|
|
|
26128
26743
|
const arr = asArrayLiteral(returnExpr);
|
|
26129
26744
|
if (!arr) return;
|
|
26130
26745
|
for (const el of arr.getElements()) {
|
|
26131
|
-
if (
|
|
26746
|
+
if (Node20.isSpreadElement(el)) {
|
|
26132
26747
|
const inner2 = el.getExpression();
|
|
26133
|
-
if (
|
|
26748
|
+
if (Node20.isCallExpression(inner2)) {
|
|
26134
26749
|
const callee = inner2.getExpression();
|
|
26135
|
-
if (
|
|
26750
|
+
if (Node20.isIdentifier(callee)) {
|
|
26136
26751
|
collectFromArrayEntryFactory(callee.getText(), out, index);
|
|
26137
26752
|
}
|
|
26138
26753
|
}
|
|
@@ -26140,9 +26755,9 @@ function collectFromArrayEntryFactory(name, out, index) {
|
|
|
26140
26755
|
}
|
|
26141
26756
|
const inner = unwrapAsExpressions(el);
|
|
26142
26757
|
if (!inner) continue;
|
|
26143
|
-
if (
|
|
26758
|
+
if (Node20.isCallExpression(inner)) {
|
|
26144
26759
|
const callee = inner.getExpression();
|
|
26145
|
-
if (
|
|
26760
|
+
if (Node20.isIdentifier(callee)) {
|
|
26146
26761
|
collectFromSingleEntryFactory(callee.getText(), out, index);
|
|
26147
26762
|
}
|
|
26148
26763
|
continue;
|
|
@@ -26162,7 +26777,7 @@ function extractTaskServiceCalls(sources, index, trustedExternal) {
|
|
|
26162
26777
|
const rel = apiRelPath(sf);
|
|
26163
26778
|
for (const call of sf.getDescendantsOfKind(SyntaxKind11.CallExpression)) {
|
|
26164
26779
|
const expr = call.getExpression();
|
|
26165
|
-
if (!
|
|
26780
|
+
if (!Node20.isIdentifier(expr) || expr.getText() !== TASK_SERVICE_FACTORY) continue;
|
|
26166
26781
|
const args = call.getArguments();
|
|
26167
26782
|
const first = args[0] ? asObjectLiteral(args[0]) : void 0;
|
|
26168
26783
|
if (!first) continue;
|
|
@@ -26188,13 +26803,13 @@ function collectValidateArrayElements(validateArr) {
|
|
|
26188
26803
|
const spreads = [];
|
|
26189
26804
|
if (!validateArr) return { ids, spreads };
|
|
26190
26805
|
for (const el of validateArr.getElements()) {
|
|
26191
|
-
if (
|
|
26806
|
+
if (Node20.isSpreadElement(el)) {
|
|
26192
26807
|
const inner2 = el.getExpression();
|
|
26193
|
-
if (
|
|
26808
|
+
if (Node20.isIdentifier(inner2)) spreads.push(inner2.getText());
|
|
26194
26809
|
continue;
|
|
26195
26810
|
}
|
|
26196
26811
|
const inner = unwrapAsExpressions(el);
|
|
26197
|
-
if (inner &&
|
|
26812
|
+
if (inner && Node20.isIdentifier(inner)) ids.push(inner.getText());
|
|
26198
26813
|
}
|
|
26199
26814
|
return { ids, spreads };
|
|
26200
26815
|
}
|
|
@@ -26207,17 +26822,17 @@ function collectHandlersArrayElements(options) {
|
|
|
26207
26822
|
if (!handlersArr) return { handlerTypes, unresolvedSpreads, resolvedBindings, unresolvedBindings };
|
|
26208
26823
|
const visited = /* @__PURE__ */ new Set();
|
|
26209
26824
|
for (const el of handlersArr.getElements()) {
|
|
26210
|
-
if (
|
|
26825
|
+
if (Node20.isSpreadElement(el)) {
|
|
26211
26826
|
consumeHandlersSpreadElement({ inner: el.getExpression(), sf, index, trustedExternal, unresolvedSpreads, resolvedBindings, unresolvedBindings, visited });
|
|
26212
26827
|
continue;
|
|
26213
26828
|
}
|
|
26214
26829
|
const inner = unwrapAsExpressions(el);
|
|
26215
26830
|
if (!inner) continue;
|
|
26216
|
-
if (
|
|
26831
|
+
if (Node20.isIdentifier(inner)) {
|
|
26217
26832
|
collectHandlerBindingsFromIdentifier({ name: inner.getText(), sf, index, trustedExternal, resolved: resolvedBindings, unresolved: unresolvedBindings, visited });
|
|
26218
|
-
} else if (
|
|
26833
|
+
} else if (Node20.isCallExpression(inner)) {
|
|
26219
26834
|
const callee = inner.getExpression();
|
|
26220
|
-
if (
|
|
26835
|
+
if (Node20.isIdentifier(callee)) {
|
|
26221
26836
|
collectHandlerBindingsFromIdentifier({ name: callee.getText(), sf, index, trustedExternal, resolved: resolvedBindings, unresolved: unresolvedBindings, visited });
|
|
26222
26837
|
}
|
|
26223
26838
|
}
|
|
@@ -26226,16 +26841,16 @@ function collectHandlersArrayElements(options) {
|
|
|
26226
26841
|
}
|
|
26227
26842
|
function consumeHandlersSpreadElement(options) {
|
|
26228
26843
|
const { inner, sf, index, trustedExternal, unresolvedSpreads, resolvedBindings, unresolvedBindings, visited } = options;
|
|
26229
|
-
if (
|
|
26844
|
+
if (Node20.isCallExpression(inner)) {
|
|
26230
26845
|
const callee = inner.getExpression();
|
|
26231
|
-
if (
|
|
26846
|
+
if (Node20.isIdentifier(callee)) {
|
|
26232
26847
|
const name = callee.getText();
|
|
26233
26848
|
unresolvedSpreads.push(name);
|
|
26234
26849
|
collectHandlerBindingsFromIdentifier({ name, sf, index, trustedExternal, resolved: resolvedBindings, unresolved: unresolvedBindings, visited });
|
|
26235
26850
|
}
|
|
26236
26851
|
return;
|
|
26237
26852
|
}
|
|
26238
|
-
if (
|
|
26853
|
+
if (Node20.isIdentifier(inner)) {
|
|
26239
26854
|
const name = inner.getText();
|
|
26240
26855
|
unresolvedSpreads.push(name);
|
|
26241
26856
|
collectHandlerBindingsFromIdentifier({ name, sf, index, trustedExternal, resolved: resolvedBindings, unresolved: unresolvedBindings, visited });
|
|
@@ -26245,15 +26860,31 @@ function collectHandlerBindingsFromValue(options) {
|
|
|
26245
26860
|
const { node, sf, index, trustedExternal, resolved, unresolved, visited } = options;
|
|
26246
26861
|
const inner = unwrapAsExpressions(node);
|
|
26247
26862
|
if (!inner) return false;
|
|
26248
|
-
if (
|
|
26863
|
+
if (Node20.isCallExpression(inner)) {
|
|
26249
26864
|
const callee = inner.getExpression();
|
|
26250
|
-
if (
|
|
26865
|
+
if (Node20.isIdentifier(callee)) {
|
|
26251
26866
|
return collectHandlerBindingsFromIdentifier({ name: callee.getText(), sf, index, trustedExternal, resolved, unresolved, visited });
|
|
26252
26867
|
}
|
|
26868
|
+
return false;
|
|
26253
26869
|
}
|
|
26254
|
-
if (
|
|
26870
|
+
if (Node20.isIdentifier(inner)) {
|
|
26255
26871
|
return collectHandlerBindingsFromIdentifier({ name: inner.getText(), sf, index, trustedExternal, resolved, unresolved, visited });
|
|
26256
26872
|
}
|
|
26873
|
+
if (Node20.isArrayLiteralExpression(inner)) {
|
|
26874
|
+
let any = false;
|
|
26875
|
+
for (const el of inner.getElements()) {
|
|
26876
|
+
if (Node20.isSpreadElement(el)) {
|
|
26877
|
+
if (collectHandlerBindingsFromValue({ node: el.getExpression(), sf, index, trustedExternal, resolved, unresolved, visited })) {
|
|
26878
|
+
any = true;
|
|
26879
|
+
}
|
|
26880
|
+
continue;
|
|
26881
|
+
}
|
|
26882
|
+
if (collectHandlerBindingsFromValue({ node: el, sf, index, trustedExternal, resolved, unresolved, visited })) {
|
|
26883
|
+
any = true;
|
|
26884
|
+
}
|
|
26885
|
+
}
|
|
26886
|
+
return any;
|
|
26887
|
+
}
|
|
26257
26888
|
return false;
|
|
26258
26889
|
}
|
|
26259
26890
|
function collectHandlerBindingsFromIdentifier(options) {
|
|
@@ -26268,7 +26899,7 @@ function collectHandlerBindingsFromIdentifier(options) {
|
|
|
26268
26899
|
const typeText = typeNode ? typeNode.getText() : void 0;
|
|
26269
26900
|
const isHandlerConfigTyped = typeText !== void 0 && (typeText === NOTIF_TASK_SERVICE_HANDLER_CONFIG || typeText.startsWith(`${NOTIF_TASK_SERVICE_HANDLER_CONFIG}<`));
|
|
26270
26901
|
const declInit = unwrapAsExpressions(localDecl.getInitializer());
|
|
26271
|
-
const isObjectLit = declInit !== void 0 &&
|
|
26902
|
+
const isObjectLit = declInit !== void 0 && Node20.isObjectLiteralExpression(declInit);
|
|
26272
26903
|
if (isHandlerConfigTyped && isObjectLit) {
|
|
26273
26904
|
resolved.push(localDecl.getName());
|
|
26274
26905
|
resolvedAny = true;
|
|
@@ -26299,12 +26930,12 @@ function collectHandlerBindingsFromIdentifier(options) {
|
|
|
26299
26930
|
function resolveArrayFromProperty2(obj, name, sf) {
|
|
26300
26931
|
const init = unwrapAsExpressions(getPropertyInitializer(obj, name));
|
|
26301
26932
|
if (!init) return void 0;
|
|
26302
|
-
if (
|
|
26303
|
-
if (
|
|
26933
|
+
if (Node20.isArrayLiteralExpression(init)) return init;
|
|
26934
|
+
if (Node20.isIdentifier(init)) {
|
|
26304
26935
|
const declNode = findLocalVariable(sf, init.getText());
|
|
26305
26936
|
if (declNode) {
|
|
26306
26937
|
const declInit = unwrapAsExpressions(declNode.getInitializer());
|
|
26307
|
-
if (declInit &&
|
|
26938
|
+
if (declInit && Node20.isArrayLiteralExpression(declInit)) {
|
|
26308
26939
|
return declInit;
|
|
26309
26940
|
}
|
|
26310
26941
|
}
|
|
@@ -26325,12 +26956,12 @@ function extractTaskHandlerEntries(sources) {
|
|
|
26325
26956
|
if (!typeIdent) return;
|
|
26326
26957
|
const flowInit = unwrapAsExpressions(getPropertyInitializer(obj, "flow"));
|
|
26327
26958
|
let flowCount;
|
|
26328
|
-
if (flowInit &&
|
|
26959
|
+
if (flowInit && Node20.isArrayLiteralExpression(flowInit)) {
|
|
26329
26960
|
flowCount = flowInit.getElements().length;
|
|
26330
26961
|
}
|
|
26331
26962
|
let dataArg;
|
|
26332
26963
|
let checkpointArg;
|
|
26333
|
-
if (
|
|
26964
|
+
if (Node20.isTypeReference(typeNode)) {
|
|
26334
26965
|
const tArgs = typeNode.getTypeArguments();
|
|
26335
26966
|
if (tArgs.length >= 1) dataArg = tArgs[0].getText();
|
|
26336
26967
|
if (tArgs.length >= 2) checkpointArg = tArgs[1].getText();
|
|
@@ -27223,7 +27854,7 @@ async function inspectFolder2(path) {
|
|
|
27223
27854
|
}
|
|
27224
27855
|
|
|
27225
27856
|
// packages/dbx-components-mcp/src/tools/system-m-validate-folder/extract.ts
|
|
27226
|
-
import { Node as
|
|
27857
|
+
import { Node as Node21, Project as Project13 } from "ts-morph";
|
|
27227
27858
|
var TYPE_CONSTANT_SUFFIX = "_SYSTEM_STATE_TYPE";
|
|
27228
27859
|
var DATA_INTERFACE_SUFFIX = "SystemData";
|
|
27229
27860
|
var CONVERTER_CONFIG_TYPE = "SystemStateStoredDataFieldConverterConfig";
|
|
@@ -27287,7 +27918,7 @@ function findDataInterfaces2(sourceFile) {
|
|
|
27287
27918
|
return out;
|
|
27288
27919
|
}
|
|
27289
27920
|
function extendsSystemStateStoredData(iface) {
|
|
27290
|
-
if (!
|
|
27921
|
+
if (!Node21.isInterfaceDeclaration(iface)) return false;
|
|
27291
27922
|
for (const clause of iface.getExtends()) {
|
|
27292
27923
|
const text = clause.getText();
|
|
27293
27924
|
if (text === SYSTEM_STATE_STORED_DATA || text.startsWith(`${SYSTEM_STATE_STORED_DATA}<`) || text === `${SYSTEM_STATE_STORED_DATA}, ${SYSTEM_STATE_STORED_DATA}`) {
|
|
@@ -27307,14 +27938,14 @@ function findConverters2(sourceFile) {
|
|
|
27307
27938
|
for (const decl of stmt.getDeclarations()) {
|
|
27308
27939
|
const typeNode = decl.getTypeNode();
|
|
27309
27940
|
if (!typeNode) continue;
|
|
27310
|
-
if (!
|
|
27941
|
+
if (!Node21.isTypeReference(typeNode)) continue;
|
|
27311
27942
|
const typeName = typeNode.getTypeName().getText();
|
|
27312
27943
|
if (typeName !== CONVERTER_CONFIG_TYPE) continue;
|
|
27313
27944
|
const typeArgs = typeNode.getTypeArguments();
|
|
27314
27945
|
let dataTypeArgument;
|
|
27315
27946
|
if (typeArgs.length >= 1) {
|
|
27316
27947
|
const arg = typeArgs[0];
|
|
27317
|
-
if (
|
|
27948
|
+
if (Node21.isTypeReference(arg)) {
|
|
27318
27949
|
dataTypeArgument = arg.getTypeName().getText();
|
|
27319
27950
|
} else {
|
|
27320
27951
|
dataTypeArgument = arg.getText();
|
|
@@ -27361,19 +27992,19 @@ function collectConverterMapKeys(decl) {
|
|
|
27361
27992
|
if (!obj) return [];
|
|
27362
27993
|
const out = [];
|
|
27363
27994
|
for (const prop of obj.getProperties()) {
|
|
27364
|
-
if (!
|
|
27995
|
+
if (!Node21.isPropertyAssignment(prop)) continue;
|
|
27365
27996
|
const nameNode = prop.getNameNode();
|
|
27366
27997
|
const line = prop.getStartLineNumber();
|
|
27367
|
-
if (
|
|
27998
|
+
if (Node21.isComputedPropertyName(nameNode)) {
|
|
27368
27999
|
const inner = nameNode.getExpression();
|
|
27369
28000
|
out.push({ raw: inner.getText(), kind: "identifier", line });
|
|
27370
28001
|
continue;
|
|
27371
28002
|
}
|
|
27372
|
-
if (
|
|
28003
|
+
if (Node21.isStringLiteral(nameNode)) {
|
|
27373
28004
|
out.push({ raw: nameNode.getLiteralText(), kind: "string", line });
|
|
27374
28005
|
continue;
|
|
27375
28006
|
}
|
|
27376
|
-
if (
|
|
28007
|
+
if (Node21.isIdentifier(nameNode)) {
|
|
27377
28008
|
out.push({ raw: nameNode.getText(), kind: "string", line });
|
|
27378
28009
|
continue;
|
|
27379
28010
|
}
|
|
@@ -27383,10 +28014,10 @@ function collectConverterMapKeys(decl) {
|
|
|
27383
28014
|
}
|
|
27384
28015
|
function unwrapObjectLiteral(node) {
|
|
27385
28016
|
let current = node;
|
|
27386
|
-
while (current &&
|
|
28017
|
+
while (current && Node21.isAsExpression(current)) {
|
|
27387
28018
|
current = current.getExpression();
|
|
27388
28019
|
}
|
|
27389
|
-
if (current &&
|
|
28020
|
+
if (current && Node21.isObjectLiteralExpression(current)) {
|
|
27390
28021
|
return current;
|
|
27391
28022
|
}
|
|
27392
28023
|
return void 0;
|
|
@@ -27420,10 +28051,10 @@ function findLastTopLevelExportLine(sourceFile) {
|
|
|
27420
28051
|
return last;
|
|
27421
28052
|
}
|
|
27422
28053
|
function isExportedTopLevel(stmt) {
|
|
27423
|
-
if (
|
|
28054
|
+
if (Node21.isVariableStatement(stmt) || Node21.isFunctionDeclaration(stmt) || Node21.isClassDeclaration(stmt) || Node21.isInterfaceDeclaration(stmt) || Node21.isTypeAliasDeclaration(stmt) || Node21.isEnumDeclaration(stmt)) {
|
|
27424
28055
|
return stmt.isExported();
|
|
27425
28056
|
}
|
|
27426
|
-
if (
|
|
28057
|
+
if (Node21.isExportDeclaration(stmt) || Node21.isExportAssignment(stmt)) {
|
|
27427
28058
|
return true;
|
|
27428
28059
|
}
|
|
27429
28060
|
return false;
|
|
@@ -27734,7 +28365,7 @@ var systemMValidateFolderTool = createFolderValidateTool({
|
|
|
27734
28365
|
});
|
|
27735
28366
|
|
|
27736
28367
|
// packages/dbx-components-mcp/src/tools/dbx-asset-validate-app/extract.ts
|
|
27737
|
-
import { Node as
|
|
28368
|
+
import { Node as Node22, Project as Project14 } from "ts-morph";
|
|
27738
28369
|
var ASSETS_RELPATH = "src/lib/assets.ts";
|
|
27739
28370
|
var BARREL_RELPATH = "src/lib/index.ts";
|
|
27740
28371
|
var LOCAL_ASSET_BUILDER = "localAsset";
|
|
@@ -27793,11 +28424,11 @@ function extractFromAssetsSource(ctx, sf) {
|
|
|
27793
28424
|
const initializer = unwrapAsExpressions(decl.getInitializer());
|
|
27794
28425
|
if (!initializer) continue;
|
|
27795
28426
|
const site = { isExported, symbolName: decl.getName(), line: decl.getStartLineNumber(), rel };
|
|
27796
|
-
if (
|
|
28427
|
+
if (Node22.isCallExpression(initializer)) {
|
|
27797
28428
|
handleCallInitializer(ctx, site, initializer);
|
|
27798
28429
|
continue;
|
|
27799
28430
|
}
|
|
27800
|
-
if (isExported &&
|
|
28431
|
+
if (isExported && Node22.isArrayLiteralExpression(initializer) && typeAnnotationText(decl) === ASSET_PATH_REF_ARRAY_TYPE) {
|
|
27801
28432
|
ctx.aggregatorExports.push({ symbolName: site.symbolName, memberNames: collectArrayIdentifiers(initializer.getElements()), sourceFile: rel, line: site.line });
|
|
27802
28433
|
}
|
|
27803
28434
|
}
|
|
@@ -27807,7 +28438,7 @@ function collectArrayIdentifiers(elements) {
|
|
|
27807
28438
|
const out = [];
|
|
27808
28439
|
for (const el of elements) {
|
|
27809
28440
|
const inner = unwrapAsExpressions(el);
|
|
27810
|
-
if (inner &&
|
|
28441
|
+
if (inner && Node22.isIdentifier(inner)) {
|
|
27811
28442
|
out.push(inner.getText());
|
|
27812
28443
|
}
|
|
27813
28444
|
}
|
|
@@ -27815,11 +28446,11 @@ function collectArrayIdentifiers(elements) {
|
|
|
27815
28446
|
}
|
|
27816
28447
|
function handleCallInitializer(ctx, site, callExpr) {
|
|
27817
28448
|
const callee = callExpr.getExpression();
|
|
27818
|
-
if (
|
|
28449
|
+
if (Node22.isIdentifier(callee)) {
|
|
27819
28450
|
handleIdentifierCallee({ ctx, site, callExpr, calleeName: callee.getText() });
|
|
27820
28451
|
return;
|
|
27821
28452
|
}
|
|
27822
|
-
if (
|
|
28453
|
+
if (Node22.isPropertyAccessExpression(callee)) {
|
|
27823
28454
|
handleFluentMemberCall(ctx, site, callExpr);
|
|
27824
28455
|
}
|
|
27825
28456
|
}
|
|
@@ -27877,9 +28508,9 @@ function handleRemoteBaseBuilder(ctx, site, arg) {
|
|
|
27877
28508
|
}
|
|
27878
28509
|
function handleFluentMemberCall(ctx, site, callExpr) {
|
|
27879
28510
|
const callee = callExpr.getExpression();
|
|
27880
|
-
if (!
|
|
28511
|
+
if (!Node22.isPropertyAccessExpression(callee)) return;
|
|
27881
28512
|
const identNode = callee.getExpression();
|
|
27882
|
-
if (!
|
|
28513
|
+
if (!Node22.isIdentifier(identNode)) return;
|
|
27883
28514
|
const memberName = callee.getName();
|
|
27884
28515
|
if (memberName !== "asset" && memberName !== "assets") return;
|
|
27885
28516
|
const builderName = identNode.getText();
|
|
@@ -27904,7 +28535,7 @@ function handleFluentMemberCall(ctx, site, callExpr) {
|
|
|
27904
28535
|
return;
|
|
27905
28536
|
}
|
|
27906
28537
|
const arrayInner = unwrapAsExpressions(args[0]);
|
|
27907
|
-
if (!arrayInner || !
|
|
28538
|
+
if (!arrayInner || !Node22.isArrayLiteralExpression(arrayInner)) return;
|
|
27908
28539
|
const resolvedPaths = collectFluentArrayPaths({ elements: arrayInner.getElements(), sourceType, folder, remoteBase });
|
|
27909
28540
|
if (site.isExported) {
|
|
27910
28541
|
ctx.assetConstants.push({ symbolName: site.symbolName, sourceType, helper, resolved: void 0, resolvedPaths, sourceFile: site.rel, line: site.line });
|
|
@@ -27914,7 +28545,7 @@ function collectFluentArrayPaths(input) {
|
|
|
27914
28545
|
const out = [];
|
|
27915
28546
|
for (const el of input.elements) {
|
|
27916
28547
|
const inner = unwrapAsExpressions(el);
|
|
27917
|
-
if (inner &&
|
|
28548
|
+
if (inner && Node22.isStringLiteral(inner)) {
|
|
27918
28549
|
const joined = joinFluent({ sourceType: input.sourceType, folder: input.folder, remoteBase: input.remoteBase, child: inner.getLiteralText() });
|
|
27919
28550
|
if (joined !== void 0) out.push(joined);
|
|
27920
28551
|
}
|
|
@@ -27947,7 +28578,7 @@ function readStringArg(call) {
|
|
|
27947
28578
|
}
|
|
27948
28579
|
function readStringArgFromNode(node) {
|
|
27949
28580
|
const inner = unwrapAsExpressions(node);
|
|
27950
|
-
if (inner &&
|
|
28581
|
+
if (inner && Node22.isStringLiteral(inner)) {
|
|
27951
28582
|
return inner.getLiteralText();
|
|
27952
28583
|
}
|
|
27953
28584
|
return void 0;
|
|
@@ -29862,7 +30493,7 @@ import { glob as fsGlob5, readFile as readFile18, stat as stat14 } from "node:fs
|
|
|
29862
30493
|
import { dirname as dirname13, isAbsolute as isAbsolute13, resolve as resolve34, sep as sep12 } from "node:path";
|
|
29863
30494
|
|
|
29864
30495
|
// packages/dbx-components-mcp/src/tools/route/extract.ts
|
|
29865
|
-
import { Node as
|
|
30496
|
+
import { Node as Node23, Project as Project15, SyntaxKind as SyntaxKind12 } from "ts-morph";
|
|
29866
30497
|
function extractFile3(source) {
|
|
29867
30498
|
const project = new Project15({ useInMemoryFileSystem: true, skipAddingFilesFromTsConfig: true });
|
|
29868
30499
|
const sourceFile = project.createSourceFile(source.name, source.text, { overwrite: true });
|
|
@@ -29877,9 +30508,9 @@ function extractFile3(source) {
|
|
|
29877
30508
|
}
|
|
29878
30509
|
for (const arrayLit of findStateArrayLiterals(sourceFile)) {
|
|
29879
30510
|
for (const element of arrayLit.getElements()) {
|
|
29880
|
-
if (
|
|
30511
|
+
if (Node23.isObjectLiteralExpression(element)) {
|
|
29881
30512
|
addNode({ file: source.name, literal: element, fallbackConstName: void 0, into: collected, issues });
|
|
29882
|
-
} else if (
|
|
30513
|
+
} else if (Node23.isIdentifier(element)) {
|
|
29883
30514
|
const ref = objectLiteralsByName.get(element.getText());
|
|
29884
30515
|
if (ref) {
|
|
29885
30516
|
addNode({ file: source.name, literal: ref.literal, fallbackConstName: element.getText(), into: collected, issues });
|
|
@@ -29900,7 +30531,7 @@ function collectStateConsts(sourceFile) {
|
|
|
29900
30531
|
for (const stmt of sourceFile.getVariableStatements()) {
|
|
29901
30532
|
for (const decl of stmt.getDeclarations()) {
|
|
29902
30533
|
const initializer = decl.getInitializer();
|
|
29903
|
-
if (!initializer || !
|
|
30534
|
+
if (!initializer || !Node23.isObjectLiteralExpression(initializer)) {
|
|
29904
30535
|
continue;
|
|
29905
30536
|
}
|
|
29906
30537
|
const typeNode = decl.getTypeNode();
|
|
@@ -29915,7 +30546,7 @@ function findStateArrayLiterals(sourceFile) {
|
|
|
29915
30546
|
for (const stmt of sourceFile.getVariableStatements()) {
|
|
29916
30547
|
for (const decl of stmt.getDeclarations()) {
|
|
29917
30548
|
const initializer = decl.getInitializer();
|
|
29918
|
-
if (initializer &&
|
|
30549
|
+
if (initializer && Node23.isArrayLiteralExpression(initializer) && declarationIsStateArray(stmt, decl.getName(), initializer)) {
|
|
29919
30550
|
arrays.push(initializer);
|
|
29920
30551
|
}
|
|
29921
30552
|
}
|
|
@@ -29929,17 +30560,17 @@ function findStateArrayLiterals(sourceFile) {
|
|
|
29929
30560
|
continue;
|
|
29930
30561
|
}
|
|
29931
30562
|
for (const arg of call.getArguments()) {
|
|
29932
|
-
if (!
|
|
30563
|
+
if (!Node23.isObjectLiteralExpression(arg)) {
|
|
29933
30564
|
continue;
|
|
29934
30565
|
}
|
|
29935
30566
|
const statesProp = arg.getProperty("states");
|
|
29936
|
-
if (!statesProp || !
|
|
30567
|
+
if (!statesProp || !Node23.isPropertyAssignment(statesProp)) {
|
|
29937
30568
|
continue;
|
|
29938
30569
|
}
|
|
29939
30570
|
const initializer = statesProp.getInitializer();
|
|
29940
|
-
if (initializer &&
|
|
30571
|
+
if (initializer && Node23.isArrayLiteralExpression(initializer)) {
|
|
29941
30572
|
arrays.push(initializer);
|
|
29942
|
-
} else if (initializer &&
|
|
30573
|
+
} else if (initializer && Node23.isIdentifier(initializer)) {
|
|
29943
30574
|
const referenced = findArrayLiteralForIdentifier(sourceFile, initializer);
|
|
29944
30575
|
if (referenced) {
|
|
29945
30576
|
arrays.push(referenced);
|
|
@@ -29956,10 +30587,10 @@ function declarationIsStateArray(stmt, name, literal) {
|
|
|
29956
30587
|
return true;
|
|
29957
30588
|
}
|
|
29958
30589
|
for (const element of literal.getElements()) {
|
|
29959
|
-
if (
|
|
30590
|
+
if (Node23.isObjectLiteralExpression(element) && hasStringNameProperty(element)) {
|
|
29960
30591
|
return true;
|
|
29961
30592
|
}
|
|
29962
|
-
if (
|
|
30593
|
+
if (Node23.isIdentifier(element)) {
|
|
29963
30594
|
return true;
|
|
29964
30595
|
}
|
|
29965
30596
|
}
|
|
@@ -29967,11 +30598,11 @@ function declarationIsStateArray(stmt, name, literal) {
|
|
|
29967
30598
|
}
|
|
29968
30599
|
function hasStringNameProperty(literal) {
|
|
29969
30600
|
const nameProp = literal.getProperty("name");
|
|
29970
|
-
if (!nameProp || !
|
|
30601
|
+
if (!nameProp || !Node23.isPropertyAssignment(nameProp)) {
|
|
29971
30602
|
return false;
|
|
29972
30603
|
}
|
|
29973
30604
|
const initializer = nameProp.getInitializer();
|
|
29974
|
-
return initializer !== void 0 && (
|
|
30605
|
+
return initializer !== void 0 && (Node23.isStringLiteral(initializer) || Node23.isNoSubstitutionTemplateLiteral(initializer));
|
|
29975
30606
|
}
|
|
29976
30607
|
function findArrayLiteralForIdentifier(sourceFile, identifier) {
|
|
29977
30608
|
const name = identifier.getText();
|
|
@@ -29981,7 +30612,7 @@ function findArrayLiteralForIdentifier(sourceFile, identifier) {
|
|
|
29981
30612
|
continue;
|
|
29982
30613
|
}
|
|
29983
30614
|
const initializer = decl.getInitializer();
|
|
29984
|
-
if (initializer &&
|
|
30615
|
+
if (initializer && Node23.isArrayLiteralExpression(initializer)) {
|
|
29985
30616
|
return initializer;
|
|
29986
30617
|
}
|
|
29987
30618
|
}
|
|
@@ -30031,21 +30662,21 @@ function addNode(options) {
|
|
|
30031
30662
|
}
|
|
30032
30663
|
function stringPropertyValue(literal, key) {
|
|
30033
30664
|
const prop = literal.getProperty(key);
|
|
30034
|
-
if (!prop || !
|
|
30665
|
+
if (!prop || !Node23.isPropertyAssignment(prop)) {
|
|
30035
30666
|
return void 0;
|
|
30036
30667
|
}
|
|
30037
30668
|
const initializer = prop.getInitializer();
|
|
30038
30669
|
if (!initializer) {
|
|
30039
30670
|
return void 0;
|
|
30040
30671
|
}
|
|
30041
|
-
if (
|
|
30672
|
+
if (Node23.isStringLiteral(initializer) || Node23.isNoSubstitutionTemplateLiteral(initializer)) {
|
|
30042
30673
|
return initializer.getLiteralText();
|
|
30043
30674
|
}
|
|
30044
30675
|
return void 0;
|
|
30045
30676
|
}
|
|
30046
30677
|
function booleanPropertyValue(literal, key) {
|
|
30047
30678
|
const prop = literal.getProperty(key);
|
|
30048
|
-
if (!prop || !
|
|
30679
|
+
if (!prop || !Node23.isPropertyAssignment(prop)) {
|
|
30049
30680
|
return void 0;
|
|
30050
30681
|
}
|
|
30051
30682
|
const initializer = prop.getInitializer();
|
|
@@ -30062,33 +30693,33 @@ function booleanPropertyValue(literal, key) {
|
|
|
30062
30693
|
}
|
|
30063
30694
|
function identifierPropertyValue(literal, key) {
|
|
30064
30695
|
const prop = literal.getProperty(key);
|
|
30065
|
-
if (!prop || !
|
|
30696
|
+
if (!prop || !Node23.isPropertyAssignment(prop)) {
|
|
30066
30697
|
return void 0;
|
|
30067
30698
|
}
|
|
30068
30699
|
const initializer = prop.getInitializer();
|
|
30069
30700
|
if (!initializer) {
|
|
30070
30701
|
return void 0;
|
|
30071
30702
|
}
|
|
30072
|
-
if (
|
|
30703
|
+
if (Node23.isIdentifier(initializer)) {
|
|
30073
30704
|
return initializer.getText();
|
|
30074
30705
|
}
|
|
30075
|
-
if (
|
|
30706
|
+
if (Node23.isPropertyAccessExpression(initializer)) {
|
|
30076
30707
|
return initializer.getText();
|
|
30077
30708
|
}
|
|
30078
30709
|
return void 0;
|
|
30079
30710
|
}
|
|
30080
30711
|
function objectPropertyKeys(literal, key) {
|
|
30081
30712
|
const prop = literal.getProperty(key);
|
|
30082
|
-
if (!prop || !
|
|
30713
|
+
if (!prop || !Node23.isPropertyAssignment(prop)) {
|
|
30083
30714
|
return [];
|
|
30084
30715
|
}
|
|
30085
30716
|
const initializer = prop.getInitializer();
|
|
30086
|
-
if (!initializer || !
|
|
30717
|
+
if (!initializer || !Node23.isObjectLiteralExpression(initializer)) {
|
|
30087
30718
|
return [];
|
|
30088
30719
|
}
|
|
30089
30720
|
const out = [];
|
|
30090
30721
|
for (const member of initializer.getProperties()) {
|
|
30091
|
-
if (
|
|
30722
|
+
if (Node23.isPropertyAssignment(member) || Node23.isShorthandPropertyAssignment(member) || Node23.isMethodDeclaration(member)) {
|
|
30092
30723
|
const nameNode = member.getNameNode();
|
|
30093
30724
|
if (nameNode) {
|
|
30094
30725
|
out.push(nameNode.getText());
|
|
@@ -30099,7 +30730,7 @@ function objectPropertyKeys(literal, key) {
|
|
|
30099
30730
|
}
|
|
30100
30731
|
function collectResolveKeys(literal) {
|
|
30101
30732
|
const prop = literal.getProperty("resolve");
|
|
30102
|
-
if (!prop || !
|
|
30733
|
+
if (!prop || !Node23.isPropertyAssignment(prop)) {
|
|
30103
30734
|
return [];
|
|
30104
30735
|
}
|
|
30105
30736
|
const initializer = prop.getInitializer();
|
|
@@ -30107,22 +30738,22 @@ function collectResolveKeys(literal) {
|
|
|
30107
30738
|
return [];
|
|
30108
30739
|
}
|
|
30109
30740
|
const keys = [];
|
|
30110
|
-
if (
|
|
30741
|
+
if (Node23.isObjectLiteralExpression(initializer)) {
|
|
30111
30742
|
for (const member of initializer.getProperties()) {
|
|
30112
|
-
if (
|
|
30743
|
+
if (Node23.isPropertyAssignment(member) || Node23.isShorthandPropertyAssignment(member) || Node23.isMethodDeclaration(member)) {
|
|
30113
30744
|
const nameNode = member.getNameNode();
|
|
30114
30745
|
if (nameNode) {
|
|
30115
30746
|
keys.push(nameNode.getText());
|
|
30116
30747
|
}
|
|
30117
30748
|
}
|
|
30118
30749
|
}
|
|
30119
|
-
} else if (
|
|
30750
|
+
} else if (Node23.isArrayLiteralExpression(initializer)) {
|
|
30120
30751
|
for (const element of initializer.getElements()) {
|
|
30121
|
-
if (
|
|
30752
|
+
if (Node23.isObjectLiteralExpression(element)) {
|
|
30122
30753
|
const tokenProp = element.getProperty("token");
|
|
30123
|
-
if (tokenProp &&
|
|
30754
|
+
if (tokenProp && Node23.isPropertyAssignment(tokenProp)) {
|
|
30124
30755
|
const tokenInit = tokenProp.getInitializer();
|
|
30125
|
-
if (tokenInit && (
|
|
30756
|
+
if (tokenInit && (Node23.isStringLiteral(tokenInit) || Node23.isIdentifier(tokenInit))) {
|
|
30126
30757
|
keys.push(tokenInit.getText().replaceAll(/^['"]|['"]$/g, ""));
|
|
30127
30758
|
}
|
|
30128
30759
|
}
|
|
@@ -32191,7 +32822,7 @@ function createSearchModelSnapshotFieldTool(config) {
|
|
|
32191
32822
|
import { glob, readFile as readFile19 } from "node:fs/promises";
|
|
32192
32823
|
import { resolve as resolve35 } from "node:path";
|
|
32193
32824
|
import { type as type59 } from "arktype";
|
|
32194
|
-
import { Node as
|
|
32825
|
+
import { Node as Node24, Project as Project16 } from "ts-morph";
|
|
32195
32826
|
var ListAppArgsType2 = type59({
|
|
32196
32827
|
componentDir: "string",
|
|
32197
32828
|
"apiDir?": "string",
|
|
@@ -32321,7 +32952,7 @@ async function buildListAppReport(input) {
|
|
|
32321
32952
|
function findSnapshotConverterCalls(sourceFile) {
|
|
32322
32953
|
const out = [];
|
|
32323
32954
|
sourceFile.forEachDescendant((node) => {
|
|
32324
|
-
if (
|
|
32955
|
+
if (Node24.isCallExpression(node)) {
|
|
32325
32956
|
const expr = node.getExpression();
|
|
32326
32957
|
const exprText = expr.getText();
|
|
32327
32958
|
const head = exprText.replace(/<[^>]*>$/, "");
|
|
@@ -32336,17 +32967,17 @@ function readFieldsObject(call) {
|
|
|
32336
32967
|
const args = call.getArguments();
|
|
32337
32968
|
if (args.length === 0) return void 0;
|
|
32338
32969
|
const first = args[0];
|
|
32339
|
-
if (!
|
|
32970
|
+
if (!Node24.isObjectLiteralExpression(first)) return void 0;
|
|
32340
32971
|
const fieldsProp = first.getProperty("fields");
|
|
32341
|
-
if (fieldsProp === void 0 || !
|
|
32972
|
+
if (fieldsProp === void 0 || !Node24.isPropertyAssignment(fieldsProp)) return void 0;
|
|
32342
32973
|
const initializer = fieldsProp.getInitializer();
|
|
32343
|
-
if (initializer === void 0 || !
|
|
32974
|
+
if (initializer === void 0 || !Node24.isObjectLiteralExpression(initializer)) return void 0;
|
|
32344
32975
|
return initializer;
|
|
32345
32976
|
}
|
|
32346
32977
|
function resolveModelName(call) {
|
|
32347
32978
|
let current = call.getParent();
|
|
32348
32979
|
while (current !== void 0) {
|
|
32349
|
-
if (
|
|
32980
|
+
if (Node24.isVariableDeclaration(current)) {
|
|
32350
32981
|
return current.getName();
|
|
32351
32982
|
}
|
|
32352
32983
|
current = current.getParent();
|
|
@@ -32356,10 +32987,10 @@ function resolveModelName(call) {
|
|
|
32356
32987
|
function extractFieldUsages(fieldsObj, registry) {
|
|
32357
32988
|
const out = [];
|
|
32358
32989
|
for (const property of fieldsObj.getProperties()) {
|
|
32359
|
-
if (!
|
|
32990
|
+
if (!Node24.isPropertyAssignment(property) && !Node24.isShorthandPropertyAssignment(property)) continue;
|
|
32360
32991
|
const fieldName = property.getName();
|
|
32361
32992
|
let converterText;
|
|
32362
|
-
if (
|
|
32993
|
+
if (Node24.isShorthandPropertyAssignment(property)) {
|
|
32363
32994
|
converterText = property.getName();
|
|
32364
32995
|
} else {
|
|
32365
32996
|
const initializer = property.getInitializer();
|
|
@@ -33882,7 +34513,7 @@ import { type as type64 } from "arktype";
|
|
|
33882
34513
|
// packages/dbx-components-mcp/src/tools/model-list-component/extract.ts
|
|
33883
34514
|
import { readdir as readdir12, readFile as readFile20, stat as stat16 } from "node:fs/promises";
|
|
33884
34515
|
import { basename as basename6, join as join19, relative as relative11, sep as sep14 } from "node:path";
|
|
33885
|
-
import { Node as
|
|
34516
|
+
import { Node as Node25, Project as Project17 } from "ts-morph";
|
|
33886
34517
|
var MODEL_SUBPATH3 = "src/lib/model";
|
|
33887
34518
|
var MODEL_SUBPATH_PREFIX = `${MODEL_SUBPATH3}/`;
|
|
33888
34519
|
var RESERVED_NAMES = RESERVED_MODEL_FOLDERS.map((r) => r.name);
|
|
@@ -34023,7 +34654,7 @@ function findIdentityIn(sourceFile) {
|
|
|
34023
34654
|
for (const statement of sourceFile.getVariableStatements()) {
|
|
34024
34655
|
for (const decl of statement.getDeclarations()) {
|
|
34025
34656
|
const initializer = decl.getInitializer();
|
|
34026
|
-
if (!initializer || !
|
|
34657
|
+
if (!initializer || !Node25.isCallExpression(initializer)) continue;
|
|
34027
34658
|
if (initializer.getExpression().getText() !== "firestoreModelIdentity") continue;
|
|
34028
34659
|
const args = initializer.getArguments();
|
|
34029
34660
|
const parsed = parseIdentityArgs2(args);
|
|
@@ -34043,13 +34674,13 @@ function parseIdentityArgs2(args) {
|
|
|
34043
34674
|
return { parentIdentityRef: void 0, collectionName: stringLiteralValue3(args[0]), collectionPrefix: stringLiteralValue3(args[1]) };
|
|
34044
34675
|
}
|
|
34045
34676
|
if (args.length === 3) {
|
|
34046
|
-
const parent =
|
|
34677
|
+
const parent = Node25.isIdentifier(args[0]) ? args[0].getText() : void 0;
|
|
34047
34678
|
return { parentIdentityRef: parent, collectionName: stringLiteralValue3(args[1]), collectionPrefix: stringLiteralValue3(args[2]) };
|
|
34048
34679
|
}
|
|
34049
34680
|
return { parentIdentityRef: void 0, collectionName: void 0, collectionPrefix: void 0 };
|
|
34050
34681
|
}
|
|
34051
34682
|
function stringLiteralValue3(node) {
|
|
34052
|
-
if (
|
|
34683
|
+
if (Node25.isStringLiteral(node) || Node25.isNoSubstitutionTemplateLiteral(node)) {
|
|
34053
34684
|
return node.getLiteralText();
|
|
34054
34685
|
}
|
|
34055
34686
|
return void 0;
|
|
@@ -34211,7 +34842,7 @@ import { type as type65 } from "arktype";
|
|
|
34211
34842
|
// packages/dbx-components-mcp/src/tools/server-actions-list-app/extract.ts
|
|
34212
34843
|
import { readdir as readdir13, readFile as readFile21, stat as stat17 } from "node:fs/promises";
|
|
34213
34844
|
import { dirname as dirname14, join as join20, relative as relative12, sep as sep15 } from "node:path";
|
|
34214
|
-
import { Node as
|
|
34845
|
+
import { Node as Node26, Project as Project18 } from "ts-morph";
|
|
34215
34846
|
var ACTION_SERVER_SUFFIX2 = ".action.server.ts";
|
|
34216
34847
|
var COMMON_MODEL_SUBPATH2 = "src/app/common/model";
|
|
34217
34848
|
var COMMON_BARREL_REL = "src/app/common/index.ts";
|
|
@@ -34348,7 +34979,7 @@ function inspectModuleText(input) {
|
|
|
34348
34979
|
for (const decorator of cls.getDecorators()) {
|
|
34349
34980
|
if (decorator.getName() !== "Module") continue;
|
|
34350
34981
|
const args = decorator.getArguments();
|
|
34351
|
-
if (args.length === 0 || !
|
|
34982
|
+
if (args.length === 0 || !Node26.isObjectLiteralExpression(args[0])) continue;
|
|
34352
34983
|
const obj = args[0];
|
|
34353
34984
|
providedByModule ||= moduleArrayContains(obj, "providers", input.className);
|
|
34354
34985
|
exportedByModule ||= moduleArrayContains(obj, "exports", input.className);
|
|
@@ -34358,16 +34989,16 @@ function inspectModuleText(input) {
|
|
|
34358
34989
|
}
|
|
34359
34990
|
function moduleArrayContains(decoratorArg, propertyName, className) {
|
|
34360
34991
|
const property = decoratorArg.getProperty(propertyName);
|
|
34361
|
-
if (!property || !
|
|
34992
|
+
if (!property || !Node26.isPropertyAssignment(property)) return false;
|
|
34362
34993
|
const initializer = property.getInitializer();
|
|
34363
|
-
if (!initializer || !
|
|
34994
|
+
if (!initializer || !Node26.isArrayLiteralExpression(initializer)) return false;
|
|
34364
34995
|
for (const element of initializer.getElements()) {
|
|
34365
|
-
if (
|
|
34366
|
-
if (
|
|
34996
|
+
if (Node26.isIdentifier(element) && element.getText() === className) return true;
|
|
34997
|
+
if (Node26.isObjectLiteralExpression(element)) {
|
|
34367
34998
|
const provideProp = element.getProperty("provide");
|
|
34368
|
-
if (provideProp &&
|
|
34999
|
+
if (provideProp && Node26.isPropertyAssignment(provideProp)) {
|
|
34369
35000
|
const provideInit = provideProp.getInitializer();
|
|
34370
|
-
if (provideInit &&
|
|
35001
|
+
if (provideInit && Node26.isIdentifier(provideInit) && provideInit.getText() === className) return true;
|
|
34371
35002
|
}
|
|
34372
35003
|
}
|
|
34373
35004
|
}
|
|
@@ -34421,11 +35052,11 @@ function collectInterfaceGetters(sourceFile) {
|
|
|
34421
35052
|
const out = /* @__PURE__ */ new Map();
|
|
34422
35053
|
for (const iface of sourceFile.getInterfaces()) {
|
|
34423
35054
|
for (const member of iface.getMembers()) {
|
|
34424
|
-
if (
|
|
35055
|
+
if (Node26.isGetAccessorDeclaration(member)) {
|
|
34425
35056
|
out.set(member.getName(), iface.getName());
|
|
34426
35057
|
continue;
|
|
34427
35058
|
}
|
|
34428
|
-
if (
|
|
35059
|
+
if (Node26.isPropertySignature(member)) {
|
|
34429
35060
|
out.set(member.getName(), iface.getName());
|
|
34430
35061
|
}
|
|
34431
35062
|
}
|
|
@@ -34437,7 +35068,7 @@ function collectClassGetters(sourceFile) {
|
|
|
34437
35068
|
for (const cls of sourceFile.getClasses()) {
|
|
34438
35069
|
const set = /* @__PURE__ */ new Set();
|
|
34439
35070
|
for (const member of cls.getMembers()) {
|
|
34440
|
-
if (
|
|
35071
|
+
if (Node26.isGetAccessorDeclaration(member)) set.add(member.getName());
|
|
34441
35072
|
}
|
|
34442
35073
|
if (set.size > 0) out.set(cls.getName() ?? "<anon>", set);
|
|
34443
35074
|
}
|
|
@@ -34949,7 +35580,7 @@ import { resolve as resolve43 } from "node:path";
|
|
|
34949
35580
|
import { type as type67 } from "arktype";
|
|
34950
35581
|
|
|
34951
35582
|
// packages/dbx-components-mcp/src/scan/actions-extract.ts
|
|
34952
|
-
import { Node as
|
|
35583
|
+
import { Node as Node27, SyntaxKind as SyntaxKind13 } from "ts-morph";
|
|
34953
35584
|
var ACTION_MARKER = "dbxAction";
|
|
34954
35585
|
var ACTION_SLUG_TAG = "dbxActionSlug";
|
|
34955
35586
|
var ACTION_ROLE_TAG = "dbxActionRole";
|
|
@@ -35157,7 +35788,7 @@ function filterStates(values) {
|
|
|
35157
35788
|
}
|
|
35158
35789
|
function readEnumMemberLiteral(member) {
|
|
35159
35790
|
const initializer = member.getInitializer();
|
|
35160
|
-
if (initializer !== void 0 && (
|
|
35791
|
+
if (initializer !== void 0 && (Node27.isStringLiteral(initializer) || Node27.isNoSubstitutionTemplateLiteral(initializer))) {
|
|
35161
35792
|
return initializer.getLiteralText();
|
|
35162
35793
|
}
|
|
35163
35794
|
return member.getName();
|
|
@@ -35777,7 +36408,7 @@ import { resolve as resolve45 } from "node:path";
|
|
|
35777
36408
|
import { type as type71 } from "arktype";
|
|
35778
36409
|
|
|
35779
36410
|
// packages/dbx-components-mcp/src/scan/pipes-extract.ts
|
|
35780
|
-
import { Node as
|
|
36411
|
+
import { Node as Node28 } from "ts-morph";
|
|
35781
36412
|
var PIPE_MARKER = "dbxPipe";
|
|
35782
36413
|
var PIPE_SLUG_TAG = "dbxPipeSlug";
|
|
35783
36414
|
var PIPE_CATEGORY_TAG = "dbxPipeCategory";
|
|
@@ -35970,7 +36601,7 @@ function readPipeDecoratorConfig(decorator) {
|
|
|
35970
36601
|
return void 0;
|
|
35971
36602
|
}
|
|
35972
36603
|
const firstArg = args[0];
|
|
35973
|
-
if (!
|
|
36604
|
+
if (!Node28.isObjectLiteralExpression(firstArg)) {
|
|
35974
36605
|
return void 0;
|
|
35975
36606
|
}
|
|
35976
36607
|
const name = readStringProperty(firstArg, "name");
|
|
@@ -35983,7 +36614,7 @@ function readPipeDecoratorConfig(decorator) {
|
|
|
35983
36614
|
}
|
|
35984
36615
|
function readBooleanProperty(obj, propName) {
|
|
35985
36616
|
const prop = obj.getProperty(propName);
|
|
35986
|
-
if (prop === void 0 || !
|
|
36617
|
+
if (prop === void 0 || !Node28.isPropertyAssignment(prop)) {
|
|
35987
36618
|
return void 0;
|
|
35988
36619
|
}
|
|
35989
36620
|
const initializer = prop.getInitializer();
|
|
@@ -39781,7 +40412,6 @@ var DBX_TOOLS = [
|
|
|
39781
40412
|
modelApiListAppTool,
|
|
39782
40413
|
modelApiLookupTool,
|
|
39783
40414
|
modelApiValidateAppTool,
|
|
39784
|
-
modelValidateFolderTool,
|
|
39785
40415
|
modelStoreScaffoldTool,
|
|
39786
40416
|
modelFixtureListAppTool,
|
|
39787
40417
|
modelFixtureLookupTool,
|
|
@@ -39833,7 +40463,7 @@ function createAuthClusterTools(registry) {
|
|
|
39833
40463
|
function registerTools(server, options = {}) {
|
|
39834
40464
|
const underlyingServer = server.server;
|
|
39835
40465
|
const tools = [...DBX_TOOLS];
|
|
39836
|
-
tools.push(createUiExamplesTool({ examplesRegistry: options.dbxDocsUiExamplesRegistry }), createModelValidateTool({ ruleOptions: options.modelValidateRuleOptions }), createModelFixtureValidateAppTool({ getRegistry: () => options.fixtureModelRegistry }));
|
|
40466
|
+
tools.push(createUiExamplesTool({ examplesRegistry: options.dbxDocsUiExamplesRegistry }), createModelValidateTool({ ruleOptions: options.modelValidateRuleOptions }), createModelValidateFolderTool({ ruleOptions: options.modelValidateRuleOptions }), createModelFixtureValidateAppTool({ getRegistry: () => options.fixtureModelRegistry }));
|
|
39837
40467
|
if (options.forgeFieldRegistry !== void 0) {
|
|
39838
40468
|
tools.push(createLookupFormTool({ registry: options.forgeFieldRegistry }), createSearchFormTool({ registry: options.forgeFieldRegistry }), createFormScaffoldTool({ registry: options.forgeFieldRegistry }));
|
|
39839
40469
|
}
|
|
@@ -40158,9 +40788,11 @@ function resolveModelValidateRuleOptions(config) {
|
|
|
40158
40788
|
return void 0;
|
|
40159
40789
|
}
|
|
40160
40790
|
const ignoredFieldNames = block.ignoredFieldNames === void 0 ? void 0 : new Set(block.ignoredFieldNames);
|
|
40791
|
+
const ignoredExternalParents = block.ignoredExternalParents === void 0 ? void 0 : new Set(block.ignoredExternalParents);
|
|
40161
40792
|
const result = {
|
|
40162
40793
|
maxFieldNameLength: block.maxFieldNameLength,
|
|
40163
|
-
ignoredFieldNames
|
|
40794
|
+
ignoredFieldNames,
|
|
40795
|
+
ignoredExternalParents
|
|
40164
40796
|
};
|
|
40165
40797
|
return result;
|
|
40166
40798
|
}
|