@dereekb/dbx-components-mcp 14.0.0 → 14.1.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +1 -1
- package/dbx-components-mcp.js +595 -74
- package/package.json +3 -3
package/README.md
CHANGED
|
@@ -78,4 +78,4 @@ The Firebase model registry (`src/registry/firebase-models.generated.ts`) is pro
|
|
|
78
78
|
npx nx run dbx-components-mcp:generate-firebase-models
|
|
79
79
|
```
|
|
80
80
|
|
|
81
|
-
The script formats output through
|
|
81
|
+
The script formats output through oxfmt so regenerating produces a byte-equal file when source models haven't changed.
|
package/dbx-components-mcp.js
CHANGED
|
@@ -10,7 +10,7 @@ var __export = (target, all) => {
|
|
|
10
10
|
// packages/dbx-components-mcp/package.json
|
|
11
11
|
var package_default = {
|
|
12
12
|
name: "@dereekb/dbx-components-mcp",
|
|
13
|
-
version: "14.
|
|
13
|
+
version: "14.1.0",
|
|
14
14
|
type: "module",
|
|
15
15
|
bin: {
|
|
16
16
|
"dbx-components-mcp": "dbx-components-mcp.js"
|
|
@@ -21,8 +21,8 @@ var package_default = {
|
|
|
21
21
|
"ts-morph": "^21.0.0"
|
|
22
22
|
},
|
|
23
23
|
peerDependencies: {
|
|
24
|
-
"@dereekb/dbx-cli": "14.
|
|
25
|
-
"@dereekb/util": "14.
|
|
24
|
+
"@dereekb/dbx-cli": "14.1.0",
|
|
25
|
+
"@dereekb/util": "14.1.0"
|
|
26
26
|
}
|
|
27
27
|
};
|
|
28
28
|
|
|
@@ -706,7 +706,7 @@ function tallyProp(input) {
|
|
|
706
706
|
}
|
|
707
707
|
function scoreAgainstEntry(input, entry) {
|
|
708
708
|
const tally = { matched: [], extraEntry: [], missingInput: [], weightedIntersect: 0, weightedUnion: 0 };
|
|
709
|
-
const allProps = /* @__PURE__ */ new Set([...input.keys(), ...entry.keys()]);
|
|
709
|
+
const allProps = /* @__PURE__ */ new Set([...Array.from(input.keys()), ...Array.from(entry.keys())]);
|
|
710
710
|
for (const prop of allProps) {
|
|
711
711
|
tallyProp({ tally, prop, inputValue: input.get(prop), entryValue: entry.get(prop) });
|
|
712
712
|
}
|
|
@@ -3003,6 +3003,14 @@ var FIREBASE_MODELS = [
|
|
|
3003
3003
|
optional: false,
|
|
3004
3004
|
description: "Per-provider connection state, keyed by provider type."
|
|
3005
3005
|
},
|
|
3006
|
+
{
|
|
3007
|
+
name: "li",
|
|
3008
|
+
longName: "logins",
|
|
3009
|
+
converter: "firestoreObjectMap<UserExternalConnectionLogin, FirestoreModelData<UserExternalConnectionLogin>, UserExternalConnectionProviderType>({ objectField: { fields: userExternalConnectionLoginFields } })",
|
|
3010
|
+
tsType: "UserExternalConnectionLoginMap",
|
|
3011
|
+
optional: false,
|
|
3012
|
+
description: "Per-provider LOGIN LINKS, keyed by provider type."
|
|
3013
|
+
},
|
|
3006
3014
|
{
|
|
3007
3015
|
name: "c",
|
|
3008
3016
|
longName: "connectedProviderTypes",
|
|
@@ -3011,6 +3019,14 @@ var FIREBASE_MODELS = [
|
|
|
3011
3019
|
optional: false,
|
|
3012
3020
|
description: "DERIVED from `e`: every provider type whose entry status is `connected`."
|
|
3013
3021
|
},
|
|
3022
|
+
{
|
|
3023
|
+
name: "ec",
|
|
3024
|
+
longName: "externalAccountKeys",
|
|
3025
|
+
converter: "optionalFirestoreArray<UserExternalConnectionExternalAccountKey>()",
|
|
3026
|
+
tsType: "Maybe<UserExternalConnectionExternalAccountKey[]>",
|
|
3027
|
+
optional: true,
|
|
3028
|
+
description: "DERIVED from `e` UNION `li`: the `<providerType>:<externalAccountId>` key of every entry that names an external account, plus every login link's."
|
|
3029
|
+
},
|
|
3014
3030
|
{
|
|
3015
3031
|
name: "uat",
|
|
3016
3032
|
longName: "updatedAt",
|
|
@@ -3021,7 +3037,7 @@ var FIREBASE_MODELS = [
|
|
|
3021
3037
|
}
|
|
3022
3038
|
],
|
|
3023
3039
|
enums: [],
|
|
3024
|
-
detectionHints: ["e", "c", "uat"],
|
|
3040
|
+
detectionHints: ["e", "li", "c", "ec", "uat"],
|
|
3025
3041
|
description: "The client-readable half of a user's third-party OAuth connection state.",
|
|
3026
3042
|
modelGroup: "UserExternalConnection",
|
|
3027
3043
|
collectionKind: "root",
|
|
@@ -10134,7 +10150,7 @@ function collectFromTypeReference(ref, context) {
|
|
|
10134
10150
|
result = collectFromTypeNode(args[0], { ...context, forceOptional: false }).map((p) => ({ ...p, required: true }));
|
|
10135
10151
|
} else if (refName === "Omit" && args.length === 2) {
|
|
10136
10152
|
const keys = parseKeyUnion(args[1]);
|
|
10137
|
-
const merged = /* @__PURE__ */ new Set([...context.omit, ...keys]);
|
|
10153
|
+
const merged = /* @__PURE__ */ new Set([...Array.from(context.omit), ...Array.from(keys)]);
|
|
10138
10154
|
result = collectFromTypeNode(args[0], { ...context, omit: merged });
|
|
10139
10155
|
} else if (refName === "Pick" && args.length === 2) {
|
|
10140
10156
|
const keys = parseKeyUnion(args[1]);
|
|
@@ -11476,7 +11492,7 @@ function tryTransitiveResolution(input) {
|
|
|
11476
11492
|
const calleeLine = resolved.decl.getStartLineNumber();
|
|
11477
11493
|
warnings.push({ kind: "unannotated-query-helper", severity: "warning", name: factoryName, callee: calleeName, calleeFilePath, calleeLine, filePath, line: call.getStartLineNumber() });
|
|
11478
11494
|
}
|
|
11479
|
-
const nextVisited =
|
|
11495
|
+
const nextVisited = new Set(visited).add(calleeKey);
|
|
11480
11496
|
walkBodyInto({
|
|
11481
11497
|
decl: resolved.decl,
|
|
11482
11498
|
factoryName,
|
|
@@ -15456,13 +15472,21 @@ function formatBuildFailure(outcome) {
|
|
|
15456
15472
|
}
|
|
15457
15473
|
function formatWriteSummary(input) {
|
|
15458
15474
|
const { outputPath, diff, generatedComposites, generatedFieldOverrides } = input;
|
|
15459
|
-
const lines = [
|
|
15475
|
+
const lines = [
|
|
15476
|
+
`generate-firestore-indexes: wrote ${outputPath}`,
|
|
15477
|
+
` composites: ${generatedComposites} (added ${diff.added.length}, removed ${diff.removed.length}, unchanged ${diff.unchanged.length})`,
|
|
15478
|
+
` fieldOverrides: ${generatedFieldOverrides} (added ${diff.fieldOverridesAdded.length}, removed ${diff.fieldOverridesRemoved.length}, unchanged ${diff.fieldOverridesUnchanged.length})`
|
|
15479
|
+
];
|
|
15460
15480
|
return appendDiffSection(lines, diff).join("\n");
|
|
15461
15481
|
}
|
|
15462
15482
|
function formatCheckSummary(input) {
|
|
15463
15483
|
const { drift, outputPath, diff, generatedComposites, generatedFieldOverrides } = input;
|
|
15464
15484
|
const headline = drift ? `generate-firestore-indexes: drift detected against ${outputPath}` : `generate-firestore-indexes: in sync (${outputPath})`;
|
|
15465
|
-
const lines = [
|
|
15485
|
+
const lines = [
|
|
15486
|
+
headline,
|
|
15487
|
+
` composites: ${generatedComposites} (added ${diff.added.length}, removed ${diff.removed.length}, unchanged ${diff.unchanged.length})`,
|
|
15488
|
+
` fieldOverrides: ${generatedFieldOverrides} (added ${diff.fieldOverridesAdded.length}, removed ${diff.fieldOverridesRemoved.length}, unchanged ${diff.fieldOverridesUnchanged.length})`
|
|
15489
|
+
];
|
|
15466
15490
|
return appendDiffSection(lines, diff).join("\n");
|
|
15467
15491
|
}
|
|
15468
15492
|
function appendDiffSection(lines, diff) {
|
|
@@ -19816,7 +19840,7 @@ function registerResources(server, options = {}) {
|
|
|
19816
19840
|
if (options.authRegistry !== void 0) {
|
|
19817
19841
|
registerAuthResource(server, { registry: options.authRegistry });
|
|
19818
19842
|
}
|
|
19819
|
-
registerFirestoreRulesResource(server,
|
|
19843
|
+
registerFirestoreRulesResource(server, options.cwd === void 0 ? {} : { cwd: options.cwd });
|
|
19820
19844
|
}
|
|
19821
19845
|
|
|
19822
19846
|
// packages/dbx-components-mcp/src/tools/lookup-form.tool.ts
|
|
@@ -19909,7 +19933,19 @@ function formatBrief(field) {
|
|
|
19909
19933
|
const tierBits = formatTierBits(field);
|
|
19910
19934
|
const arrayOptional = field.arrayOutput === "optional" ? " (single or array)" : "";
|
|
19911
19935
|
const array = field.arrayOutput === "yes" ? " (array)" : arrayOptional;
|
|
19912
|
-
return [
|
|
19936
|
+
return [
|
|
19937
|
+
`## ${field.factoryName}`,
|
|
19938
|
+
"",
|
|
19939
|
+
`**slug:** \`${field.slug}\` \xB7 **tier:** \`${field.tier}\` \xB7 **produces:** \`${field.produces}\`${array}${tierBits}`,
|
|
19940
|
+
"",
|
|
19941
|
+
field.description,
|
|
19942
|
+
"",
|
|
19943
|
+
"```ts",
|
|
19944
|
+
field.minimalExample,
|
|
19945
|
+
"```",
|
|
19946
|
+
"",
|
|
19947
|
+
`\u2192 Call \`dbx_form_lookup topic="${field.slug}" depth="full"\` for the config table and full example.`
|
|
19948
|
+
].join("\n");
|
|
19913
19949
|
}
|
|
19914
19950
|
function formatFull(field) {
|
|
19915
19951
|
const header = formatHeader(field);
|
|
@@ -20411,7 +20447,19 @@ function formatSearchResults(input) {
|
|
|
20411
20447
|
const field = hit.entry;
|
|
20412
20448
|
const optionalArray = field.arrayOutput === "optional" ? " *(single or array)*" : "";
|
|
20413
20449
|
const array = field.arrayOutput === "yes" ? " *(array)*" : optionalArray;
|
|
20414
|
-
lines.push(
|
|
20450
|
+
lines.push(
|
|
20451
|
+
`## \`${field.slug}\` \xB7 form \xB7 score ${hit.score}`,
|
|
20452
|
+
"",
|
|
20453
|
+
`- **factory:** \`${field.factoryName}\``,
|
|
20454
|
+
`- **tier:** \`${field.tier}\``,
|
|
20455
|
+
`- **produces:** \`${field.produces}\`${array}`,
|
|
20456
|
+
`- **matched:** \`${hit.matchedTokens.join(", ")}\``,
|
|
20457
|
+
"",
|
|
20458
|
+
field.description,
|
|
20459
|
+
"",
|
|
20460
|
+
`\u2192 \`dbx_form_lookup topic="${field.slug}"\` for full docs.`,
|
|
20461
|
+
""
|
|
20462
|
+
);
|
|
20415
20463
|
}
|
|
20416
20464
|
return lines.join("\n").trimEnd();
|
|
20417
20465
|
}
|
|
@@ -21463,7 +21511,19 @@ function formatSearchResults2(options) {
|
|
|
21463
21511
|
} else {
|
|
21464
21512
|
const lines = [`# Search: \`${query}\``, "", `Tokens: \`${tokenDisplay}\`${scopeLabel} \xB7 ${hits.length} result${hits.length === 1 ? "" : "s"}`, ""];
|
|
21465
21513
|
for (const hit of hits) {
|
|
21466
|
-
lines.push(
|
|
21514
|
+
lines.push(
|
|
21515
|
+
`## \`${hit.entry.slug}\` \xB7 ${hit.entry.category} \xB7 score ${hit.score}`,
|
|
21516
|
+
"",
|
|
21517
|
+
`- **class:** \`${hit.entry.className}\``,
|
|
21518
|
+
`- **kind:** \`${hit.entry.kind}\``,
|
|
21519
|
+
`- **selector:** \`${hit.entry.selector}\``,
|
|
21520
|
+
`- **matched:** \`${hit.matchedTokens.join(", ")}\``,
|
|
21521
|
+
"",
|
|
21522
|
+
hit.entry.description,
|
|
21523
|
+
"",
|
|
21524
|
+
`\u2192 \`dbx_ui_lookup topic="${hit.entry.slug}"\` for full docs.`,
|
|
21525
|
+
""
|
|
21526
|
+
);
|
|
21467
21527
|
}
|
|
21468
21528
|
appendRelatedExamples(lines, hits, examplesRegistry);
|
|
21469
21529
|
result = lines.join("\n").trimEnd();
|
|
@@ -25553,7 +25613,31 @@ function attachRemediation(code17) {
|
|
|
25553
25613
|
|
|
25554
25614
|
// packages/dbx-cli/validate/src/lib/_core/rule-catalog/format.ts
|
|
25555
25615
|
function formatRuleAsMarkdown(entry) {
|
|
25556
|
-
const lines = [
|
|
25616
|
+
const lines = [
|
|
25617
|
+
`# \`${entry.code}\``,
|
|
25618
|
+
"",
|
|
25619
|
+
`**${entry.title}**`,
|
|
25620
|
+
"",
|
|
25621
|
+
`- **Severity:** \`${entry.severity}\``,
|
|
25622
|
+
`- **Source tool:** \`${entry.source}\``,
|
|
25623
|
+
"",
|
|
25624
|
+
"## What it flags",
|
|
25625
|
+
"",
|
|
25626
|
+
entry.whatItFlags,
|
|
25627
|
+
"",
|
|
25628
|
+
"## When it applies",
|
|
25629
|
+
"",
|
|
25630
|
+
entry.whenItApplies,
|
|
25631
|
+
"",
|
|
25632
|
+
"## When it does NOT apply",
|
|
25633
|
+
"",
|
|
25634
|
+
entry.whenItDoesNotApply,
|
|
25635
|
+
"",
|
|
25636
|
+
"## Canonical fix",
|
|
25637
|
+
"",
|
|
25638
|
+
entry.canonicalFix,
|
|
25639
|
+
""
|
|
25640
|
+
];
|
|
25557
25641
|
if (entry.canonicalFixTemplate) {
|
|
25558
25642
|
lines.push("## Fix template", "", entry.canonicalFixTemplate, "");
|
|
25559
25643
|
}
|
|
@@ -35894,7 +35978,15 @@ function appendGroupSection(lines, apiRel, group) {
|
|
|
35894
35978
|
function appendFileTable(lines, group) {
|
|
35895
35979
|
lines.push("", "| File | Kind | Subgroups | Canonical | Rename suggestion |", "|---|---|---|---|---|");
|
|
35896
35980
|
for (const file of group.files) {
|
|
35897
|
-
lines.push(
|
|
35981
|
+
lines.push(
|
|
35982
|
+
"| " + [
|
|
35983
|
+
code3(file.filename),
|
|
35984
|
+
KIND_LABEL[file.classification.kind],
|
|
35985
|
+
file.classification.subgroups.length === 0 ? "\u2014" : code3(file.classification.subgroups.join(".")),
|
|
35986
|
+
file.classification.isCanonical ? "\u2713" : "\u2717",
|
|
35987
|
+
file.classification.recommendedRename === void 0 ? "\u2014" : code3(file.classification.recommendedRename)
|
|
35988
|
+
].join(" | ") + " |"
|
|
35989
|
+
);
|
|
35898
35990
|
}
|
|
35899
35991
|
}
|
|
35900
35992
|
function appendWhereToAddSection(lines, apiRel, group) {
|
|
@@ -35910,7 +36002,17 @@ function appendRecommendation(lines, rec, group) {
|
|
|
35910
36002
|
lines.push("", `- **${rec.label}** \u2192 \`${rec.canonicalPath}\` ${status}`, ` ${rec.summary}`);
|
|
35911
36003
|
}
|
|
35912
36004
|
function appendConventionLegend(lines) {
|
|
35913
|
-
lines.push(
|
|
36005
|
+
lines.push(
|
|
36006
|
+
"",
|
|
36007
|
+
"## Naming convention",
|
|
36008
|
+
"",
|
|
36009
|
+
"- `<group>.crud.spec.ts` \u2014 non-scenario CRUD tests.",
|
|
36010
|
+
"- `<group>.crud.<sub>[.<sub>...].spec.ts` \u2014 focused CRUD sub-test.",
|
|
36011
|
+
"- `<group>.scenario.spec.ts` \u2014 generic multi-step scenario tests.",
|
|
36012
|
+
"- `<group>.scenario.<sub>[.<sub>...].spec.ts` \u2014 focused scenario sub-bucket.",
|
|
36013
|
+
"",
|
|
36014
|
+
"Drift forms are still parsed but flagged \u2014 see the `Rename suggestion` column."
|
|
36015
|
+
);
|
|
35914
36016
|
}
|
|
35915
36017
|
function appendDriftSummary(lines, catalog) {
|
|
35916
36018
|
const driftFiles = [];
|
|
@@ -36056,7 +36158,14 @@ function stringify(value) {
|
|
|
36056
36158
|
|
|
36057
36159
|
// packages/dbx-cli/model-test/src/lib/fixture/format.markdown.ts
|
|
36058
36160
|
function formatListAsMarkdown(extraction) {
|
|
36059
|
-
const lines = [
|
|
36161
|
+
const lines = [
|
|
36162
|
+
`# App fixtures \u2014 ${extraction.fixturePath}`,
|
|
36163
|
+
"",
|
|
36164
|
+
`Detected workspace prefix: \`${extraction.prefix ?? "(none)"}\``,
|
|
36165
|
+
`Identity imports: ${extraction.identityImports.length === 0 ? "_None._" : extraction.identityImports.map((s) => "`" + s + "`").join(", ")}`,
|
|
36166
|
+
"",
|
|
36167
|
+
`## Fixtures (${extraction.entries.length})`
|
|
36168
|
+
];
|
|
36060
36169
|
if (extraction.entries.length === 0) {
|
|
36061
36170
|
lines.push("", "_No model fixtures found._");
|
|
36062
36171
|
} else {
|
|
@@ -36113,7 +36222,15 @@ function nonModelDescription(entry) {
|
|
|
36113
36222
|
return "Non-Firestore-model fixture.";
|
|
36114
36223
|
}
|
|
36115
36224
|
function appendLookupTriplet(lines, entry) {
|
|
36116
|
-
lines.push(
|
|
36225
|
+
lines.push(
|
|
36226
|
+
"## Triplet",
|
|
36227
|
+
"",
|
|
36228
|
+
`- Fixture: \`${entry.fixtureClassName}\` (lines ${entry.fixtureLine}-${entry.fixtureEndLine})`,
|
|
36229
|
+
`- Instance: \`${entry.instanceClassName}\` (lines ${entry.instanceLine}-${entry.instanceEndLine})`,
|
|
36230
|
+
`- Params: \`${entry.paramsTypeName}\``,
|
|
36231
|
+
`- Factory: \`${entry.factoryName ?? "\u2014"}\``,
|
|
36232
|
+
`- Singleton: \`${entry.singletonName ?? "\u2014"}\``
|
|
36233
|
+
);
|
|
36117
36234
|
}
|
|
36118
36235
|
function appendLookupGenerics(lines, entry) {
|
|
36119
36236
|
if (entry.fixtureExtendsGenerics.length > 0) {
|
|
@@ -36283,11 +36400,19 @@ function renderParamsBlock(input, paramsTypeName, todos) {
|
|
|
36283
36400
|
}
|
|
36284
36401
|
function renderInstanceBlock(input) {
|
|
36285
36402
|
input.todos.push(`Add instance methods to \`${input.instanceClassName}\`.`);
|
|
36286
|
-
return [
|
|
36403
|
+
return [
|
|
36404
|
+
`export class ${input.instanceClassName}<F extends FirebaseAdminFunctionTestContextInstance = FirebaseAdminFunctionTestContextInstance> extends ModelTestContextInstance<${input.model}, ${input.modelDocumentTypeName}, ${input.prefix}FunctionContextFixtureInstance<F>> {`,
|
|
36405
|
+
" // TODO: implement instance-side methods (these own the real logic).",
|
|
36406
|
+
"}"
|
|
36407
|
+
].join("\n");
|
|
36287
36408
|
}
|
|
36288
36409
|
function renderFixtureBlock(input) {
|
|
36289
36410
|
input.todos.push(`Add forwarder methods to \`${input.fixtureClassName}\` after defining instance methods.`);
|
|
36290
|
-
return [
|
|
36411
|
+
return [
|
|
36412
|
+
`export class ${input.fixtureClassName}<F extends FirebaseAdminFunctionTestContextInstance = FirebaseAdminFunctionTestContextInstance> extends ModelTestContextFixture<${input.model}, ${input.modelDocumentTypeName}, ${input.prefix}FunctionContextFixtureInstance<F>, ${input.prefix}FunctionContextFixture<F>, ${input.instanceClassName}<F>> {`,
|
|
36413
|
+
" // TODO: forward instance methods. Run `dbx_model_fixture_forward` after adding instance methods.",
|
|
36414
|
+
"}"
|
|
36415
|
+
].join("\n");
|
|
36291
36416
|
}
|
|
36292
36417
|
function renderFactoryBlock(input) {
|
|
36293
36418
|
const generics = renderFactoryGenerics(input);
|
|
@@ -36307,7 +36432,15 @@ function renderFactoryBlock(input) {
|
|
|
36307
36432
|
return lines.join("\n");
|
|
36308
36433
|
}
|
|
36309
36434
|
function renderFactoryGenerics(input) {
|
|
36310
|
-
const baseGenerics = [
|
|
36435
|
+
const baseGenerics = [
|
|
36436
|
+
input.model,
|
|
36437
|
+
input.modelDocumentTypeName,
|
|
36438
|
+
input.paramsTypeName,
|
|
36439
|
+
`${input.prefix}FunctionContextFixtureInstance<FirebaseAdminFunctionTestContextInstance>`,
|
|
36440
|
+
`${input.prefix}FunctionContextFixture<FirebaseAdminFunctionTestContextInstance>`,
|
|
36441
|
+
`${input.instanceClassName}<FirebaseAdminFunctionTestContextInstance>`,
|
|
36442
|
+
`${input.fixtureClassName}<FirebaseAdminFunctionTestContextInstance>`
|
|
36443
|
+
];
|
|
36311
36444
|
if (input.collectionGenericArg) {
|
|
36312
36445
|
return [...baseGenerics, input.collectionGenericArg];
|
|
36313
36446
|
}
|
|
@@ -36329,10 +36462,13 @@ function renderFactoryCallbacks(input) {
|
|
|
36329
36462
|
} else {
|
|
36330
36463
|
callbacks.push(`getCollection: (fi) => fi.${defaultCollectionsAccessor(input.prefix)}.${camelInitial(input.model)}Collection`);
|
|
36331
36464
|
}
|
|
36332
|
-
callbacks.push(
|
|
36465
|
+
callbacks.push(
|
|
36466
|
+
`makeInstance: (delegate, ref, testInstance) => new ${input.instanceClassName}(delegate, ref, testInstance)`,
|
|
36467
|
+
`makeRef: async (collection, params, _p) => {
|
|
36333
36468
|
// TODO: choose the correct ref strategy (auto id vs deterministic id).
|
|
36334
36469
|
return collection.documentAccessor().newDocument().documentRef;
|
|
36335
|
-
}`
|
|
36470
|
+
}`
|
|
36471
|
+
);
|
|
36336
36472
|
if (input.withInitDocument) {
|
|
36337
36473
|
callbacks.push(`initDocument: async (instance, params) => {
|
|
36338
36474
|
// TODO: seed the new document with sensible defaults from \`params\`.
|
|
@@ -37532,7 +37668,18 @@ function formatReportAsJson4(report) {
|
|
|
37532
37668
|
// packages/dbx-cli/validate/src/lib/dbx-asset-list-app/format.markdown.ts
|
|
37533
37669
|
function formatReportAsMarkdown4(report) {
|
|
37534
37670
|
const basename12 = report.componentDir.split("/").pop() ?? report.componentDir;
|
|
37535
|
-
const lines = [
|
|
37671
|
+
const lines = [
|
|
37672
|
+
`# App assets \u2014 ${basename12}`,
|
|
37673
|
+
"",
|
|
37674
|
+
`Component: \`${report.componentDir}\``,
|
|
37675
|
+
`App: \`${report.apiDir}\``,
|
|
37676
|
+
"",
|
|
37677
|
+
`\`src/lib/assets.ts\`: ${formatBool(report.assetsFileExists)}`,
|
|
37678
|
+
`Barrel re-exports \`./assets\`: ${formatBool(report.barrelReExportsAssets)}`,
|
|
37679
|
+
`\`provideDbxAssetLoader()\` called in \`src/root.app.config.ts\`: ${formatBool(report.providerWiredInApp)}`,
|
|
37680
|
+
"",
|
|
37681
|
+
`## Assets (${report.assets.length})`
|
|
37682
|
+
];
|
|
37536
37683
|
if (report.assets.length === 0) {
|
|
37537
37684
|
lines.push("", "_None found._");
|
|
37538
37685
|
} else {
|
|
@@ -37923,7 +38070,18 @@ function formatReportAsJson6(report) {
|
|
|
37923
38070
|
function formatReportAsMarkdown6(report) {
|
|
37924
38071
|
const basename12 = report.componentDir.split("/").pop() ?? report.componentDir;
|
|
37925
38072
|
const uploadServiceFactoryText = report.uploadServiceFactoryName ? code7(report.uploadServiceFactoryName) : "_Not defined._";
|
|
37926
|
-
const lines = [
|
|
38073
|
+
const lines = [
|
|
38074
|
+
`# App storagefiles \u2014 ${basename12}`,
|
|
38075
|
+
"",
|
|
38076
|
+
`Component: \`${report.componentDir}\``,
|
|
38077
|
+
`API: \`${report.apiDir}\``,
|
|
38078
|
+
"",
|
|
38079
|
+
`Upload service factory: ${uploadServiceFactoryText}`,
|
|
38080
|
+
`Wired via \`StorageFileInitializeFromUploadService\` provider: ${formatBool3(report.uploadServiceWiredInApi)}`,
|
|
38081
|
+
`Processing handler call present: ${formatBool3(report.processingHandlerWiredInApi)}`,
|
|
38082
|
+
"",
|
|
38083
|
+
`## Purposes (${report.purposes.length})`
|
|
38084
|
+
];
|
|
37927
38085
|
if (report.purposes.length === 0) {
|
|
37928
38086
|
lines.push("", "_None found._");
|
|
37929
38087
|
} else {
|
|
@@ -41602,7 +41760,14 @@ function rootDocumentStoreBody(args) {
|
|
|
41602
41760
|
if (args.functionsClass !== void 0) {
|
|
41603
41761
|
packageImports.push(args.functionsClass);
|
|
41604
41762
|
}
|
|
41605
|
-
const lines = [
|
|
41763
|
+
const lines = [
|
|
41764
|
+
`import { Injectable, inject } from '@angular/core';`,
|
|
41765
|
+
`import { ${joinUnique(dbxFirebaseImports)} } from '@dereekb/dbx-firebase';`,
|
|
41766
|
+
`import { ${joinUnique(packageImports)} } from '${args.firebasePackage}';`,
|
|
41767
|
+
"",
|
|
41768
|
+
`@Injectable()`,
|
|
41769
|
+
`export class ${storeClass} extends ${baseClass}<${args.modelName}, ${docType}> {`
|
|
41770
|
+
];
|
|
41606
41771
|
if (args.functionsClass !== void 0) {
|
|
41607
41772
|
lines.push(` readonly ${functionsFieldName(args.functionsClass)} = inject(${args.functionsClass});`, "");
|
|
41608
41773
|
}
|
|
@@ -41645,11 +41810,29 @@ function subDocumentStoreBody(args) {
|
|
|
41645
41810
|
if (args.functionsClass !== void 0) {
|
|
41646
41811
|
packageImports.push(args.functionsClass);
|
|
41647
41812
|
}
|
|
41648
|
-
const lines = [
|
|
41813
|
+
const lines = [
|
|
41814
|
+
`import { Injectable, inject } from '@angular/core';`,
|
|
41815
|
+
`import { ${joinUnique(dbxFirebaseImports)} } from '@dereekb/dbx-firebase';`,
|
|
41816
|
+
`import { ${joinUnique(packageImports)} } from '${args.firebasePackage}';`,
|
|
41817
|
+
`import { ${parentStore} } from './${parentBaseFile}.document.store';`,
|
|
41818
|
+
"",
|
|
41819
|
+
`@Injectable()`,
|
|
41820
|
+
`export class ${storeClass} extends ${baseClass}<${args.modelName}, ${parent.name}, ${docType}, ${parent.documentType}> {`
|
|
41821
|
+
];
|
|
41649
41822
|
if (args.functionsClass !== void 0) {
|
|
41650
41823
|
lines.push(` readonly ${functionsFieldName(args.functionsClass)} = inject(${args.functionsClass});`, "");
|
|
41651
41824
|
}
|
|
41652
|
-
lines.push(
|
|
41825
|
+
lines.push(
|
|
41826
|
+
` constructor() {`,
|
|
41827
|
+
` const collections = inject(${collections});`,
|
|
41828
|
+
` super({ collectionFactory: collections.${args.factoryAccessor}, firestoreCollectionLike: collections.${args.groupAccessor} });`,
|
|
41829
|
+
` const parent = inject(${parentStore}, { optional: true });`,
|
|
41830
|
+
"",
|
|
41831
|
+
` if (parent) {`,
|
|
41832
|
+
` this.setParentStore(parent);`,
|
|
41833
|
+
` }`,
|
|
41834
|
+
` }`
|
|
41835
|
+
);
|
|
41653
41836
|
const crudLines = renderCrudLines(args, "this");
|
|
41654
41837
|
if (crudLines.length > 0) {
|
|
41655
41838
|
lines.push("", ...crudLines);
|
|
@@ -41694,7 +41877,14 @@ function systemStateAccessorBody(args) {
|
|
|
41694
41877
|
if (args.functionsClass !== void 0) {
|
|
41695
41878
|
packageImports.push(args.functionsClass);
|
|
41696
41879
|
}
|
|
41697
|
-
const lines = [
|
|
41880
|
+
const lines = [
|
|
41881
|
+
`import { Injectable, inject } from '@angular/core';`,
|
|
41882
|
+
`import { ${joinUnique(dbxFirebaseImports)} } from '@dereekb/dbx-firebase';`,
|
|
41883
|
+
`import { ${joinUnique(packageImports)} } from '${args.firebasePackage}';`,
|
|
41884
|
+
"",
|
|
41885
|
+
`@Injectable()`,
|
|
41886
|
+
`export class ${accessorClass} extends AbstractSystemStateDocumentStoreAccessor<${dataType}> {`
|
|
41887
|
+
];
|
|
41698
41888
|
if (args.functionsClass !== void 0) {
|
|
41699
41889
|
lines.push(` readonly ${functionsFieldName(args.functionsClass)} = inject(${args.functionsClass});`, "");
|
|
41700
41890
|
}
|
|
@@ -42277,7 +42467,17 @@ var ForwardArgsType = type62({
|
|
|
42277
42467
|
});
|
|
42278
42468
|
var TOOL8 = {
|
|
42279
42469
|
name: "dbx_model_fixture_forward",
|
|
42280
|
-
description: [
|
|
42470
|
+
description: [
|
|
42471
|
+
"Insert the missing Fixture forwarder methods for one model in `<apiDir>/src/test/fixture.ts`. Every public Instance method that lacks a Fixture method gains a thin wrapper:",
|
|
42472
|
+
"",
|
|
42473
|
+
"```ts",
|
|
42474
|
+
"async <name>(<paramText>): <returnType> {",
|
|
42475
|
+
" return this.instance.<name>(<args>);",
|
|
42476
|
+
"}",
|
|
42477
|
+
"```",
|
|
42478
|
+
"",
|
|
42479
|
+
"Existing forwarders are left untouched (idempotent). Pass `methods` to limit the run to specific method names; omit to forward everything that's missing."
|
|
42480
|
+
].join("\n"),
|
|
42281
42481
|
inputSchema: {
|
|
42282
42482
|
type: "object",
|
|
42283
42483
|
properties: {
|
|
@@ -42756,7 +42956,11 @@ function bucketSummary(group, bucket) {
|
|
|
42756
42956
|
}
|
|
42757
42957
|
return "Multi-step scenarios using fixture chains (mirrors real workflows). Add focused buckets as `" + group + ".scenario.<sub>.spec.ts` (chain subgroups freely, e.g. `.scenario.requirement.worker.spec.ts`).";
|
|
42758
42958
|
}
|
|
42759
|
-
var DRIFT_RULES = [
|
|
42959
|
+
var DRIFT_RULES = [
|
|
42960
|
+
"`<group>.<sub>.crud.spec.ts` \u2192 rename to `<group>.crud.<sub>.spec.ts` (`crud` belongs directly after the group name).",
|
|
42961
|
+
"`<group>.<sub>.scenario.spec.ts` \u2192 rename to `<group>.scenario.<sub>.spec.ts` (`scenario` belongs directly after the group name).",
|
|
42962
|
+
"`<group>.<rest>.spec.ts` with no `crud` / `scenario` segment \u2192 add one (default to `scenario` when in doubt)."
|
|
42963
|
+
];
|
|
42760
42964
|
function formatMarkdown(report) {
|
|
42761
42965
|
const lines = [];
|
|
42762
42966
|
lines.push(`# Spec convention for \`${report.group}\``, "", `Tests live under \`${report.apiDir}/src/app/function/${report.group}/\`. Canonical name segments:`, "");
|
|
@@ -43536,7 +43740,14 @@ var ARCHETYPE_RECOMMEND_TOOL = {
|
|
|
43536
43740
|
import { type as type70 } from "arktype";
|
|
43537
43741
|
var DBX_MODEL_ARCHETYPE_LOOKUP_TOOL = {
|
|
43538
43742
|
name: "dbx_model_archetype_lookup",
|
|
43539
|
-
description: [
|
|
43743
|
+
description: [
|
|
43744
|
+
"Look up an archetype in the model-archetype catalog. Accepts:",
|
|
43745
|
+
' \u2022 a slug (`"root-entity"`, `"denormalised-aggregate"`, \u2026);',
|
|
43746
|
+
' \u2022 the literal `"list"` for the full catalog grouped by family.',
|
|
43747
|
+
"",
|
|
43748
|
+
"Optional inputs:",
|
|
43749
|
+
' \u2022 `axes`: optional axis filter (`{ "subPurpose": "private" }`) \u2014 when set, the description shifts to focus on the matched axis.'
|
|
43750
|
+
].join("\n"),
|
|
43540
43751
|
inputSchema: {
|
|
43541
43752
|
type: "object",
|
|
43542
43753
|
properties: {
|
|
@@ -44929,7 +45140,18 @@ function formatStateInteraction(entry) {
|
|
|
44929
45140
|
return result;
|
|
44930
45141
|
}
|
|
44931
45142
|
function formatDirectiveEntry(entry, depth) {
|
|
44932
|
-
const lines = [
|
|
45143
|
+
const lines = [
|
|
45144
|
+
`# ${entry.className}`,
|
|
45145
|
+
"",
|
|
45146
|
+
entry.description,
|
|
45147
|
+
"",
|
|
45148
|
+
bullet("selector", `\`${entry.selector}\``),
|
|
45149
|
+
bullet("module", `\`${entry.module}\``),
|
|
45150
|
+
bullet("slug", `\`${entry.slug}\``),
|
|
45151
|
+
bullet("produces context", entry.producesContext ? "yes (provides `ActionContextStore` via DI)" : "no"),
|
|
45152
|
+
bullet("consumes context", entry.consumesContext ? "yes (injects `DbxActionContextStoreSourceInstance`)" : "no"),
|
|
45153
|
+
""
|
|
45154
|
+
];
|
|
44933
45155
|
if (depth === "full") {
|
|
44934
45156
|
lines.push("## Inputs", "", entry.inputs.length > 0 ? formatInputsTable2(entry.inputs) : "_No inputs._", "");
|
|
44935
45157
|
if (entry.outputs.length > 0) {
|
|
@@ -45692,7 +45914,15 @@ export class TagEditorComponent {
|
|
|
45692
45914
|
};
|
|
45693
45915
|
|
|
45694
45916
|
// packages/dbx-components-mcp/src/tools/data/patterns/action-patterns.ts
|
|
45695
|
-
var ACTION_EXAMPLE_PATTERNS = [
|
|
45917
|
+
var ACTION_EXAMPLE_PATTERNS = [
|
|
45918
|
+
ACTION_PATTERN_BUTTON_CONFIRM_DELETE,
|
|
45919
|
+
ACTION_PATTERN_FORM_SUBMIT,
|
|
45920
|
+
ACTION_PATTERN_FORM_WITH_SECONDARY,
|
|
45921
|
+
ACTION_PATTERN_AUTO_TRIGGER_ON_MODIFY,
|
|
45922
|
+
ACTION_PATTERN_DISABLED_BY_KEY,
|
|
45923
|
+
ACTION_PATTERN_VALUE_GETTER_ON_TRIGGER,
|
|
45924
|
+
ACTION_PATTERN_PROVIDE_CONTEXT_UP
|
|
45925
|
+
];
|
|
45696
45926
|
function getActionExamplePattern(slug) {
|
|
45697
45927
|
const lowered = slug.trim().toLowerCase();
|
|
45698
45928
|
return ACTION_EXAMPLE_PATTERNS.find((p) => p.slug === lowered);
|
|
@@ -46016,7 +46246,27 @@ function renderScaffold3(args) {
|
|
|
46016
46246
|
const directivesUsed = buildDirectivesUsed(args);
|
|
46017
46247
|
const examplePattern = relatedExamplePattern(args);
|
|
46018
46248
|
const lines = [];
|
|
46019
|
-
lines.push(
|
|
46249
|
+
lines.push(
|
|
46250
|
+
`# Action scaffold \u2014 ${args.useCase}`,
|
|
46251
|
+
"",
|
|
46252
|
+
`Trigger: \`${args.trigger}\` \xB7 Confirm: \`${args.confirm}\` \xB7 Success feedback: \`${args.successFeedback}\` \xB7 Context: \`${args.contextProvider}\``,
|
|
46253
|
+
"",
|
|
46254
|
+
"## Template",
|
|
46255
|
+
"",
|
|
46256
|
+
"```html",
|
|
46257
|
+
template,
|
|
46258
|
+
"```",
|
|
46259
|
+
"",
|
|
46260
|
+
"## Component class",
|
|
46261
|
+
"",
|
|
46262
|
+
"```ts",
|
|
46263
|
+
handler,
|
|
46264
|
+
"```",
|
|
46265
|
+
"",
|
|
46266
|
+
"## Imports",
|
|
46267
|
+
"",
|
|
46268
|
+
"```ts"
|
|
46269
|
+
);
|
|
46020
46270
|
for (const line of imports) {
|
|
46021
46271
|
lines.push(line);
|
|
46022
46272
|
}
|
|
@@ -46460,7 +46710,13 @@ function formatMissingRouteModelLine(param) {
|
|
|
46460
46710
|
// packages/dbx-components-mcp/src/tools/route-lookup.tool.ts
|
|
46461
46711
|
var DBX_ROUTE_LOOKUP_TOOL = {
|
|
46462
46712
|
name: "dbx_route_lookup",
|
|
46463
|
-
description: [
|
|
46713
|
+
description: [
|
|
46714
|
+
"Look up a single UIRouter state by name, full URL, or component class name. Returns the state's URL, component, parent chain, params, resolves, siblings, and children.",
|
|
46715
|
+
"",
|
|
46716
|
+
"Provide at least one of `sources` / `paths` / `glob` to point the tool at the source set, plus a `topic`.",
|
|
46717
|
+
"",
|
|
46718
|
+
"Resolution order: exact state name \u2192 URL path \u2192 component class name \u2192 fuzzy substring (returns up to 5 candidates)."
|
|
46719
|
+
].join("\n"),
|
|
46464
46720
|
inputSchema: {
|
|
46465
46721
|
type: "object",
|
|
46466
46722
|
properties: {
|
|
@@ -46720,7 +46976,11 @@ var ROUTE_LOOKUP_TOOL = {
|
|
|
46720
46976
|
import { type as type82 } from "arktype";
|
|
46721
46977
|
var DBX_ROUTE_SEARCH_TOOL = {
|
|
46722
46978
|
name: "dbx_route_search",
|
|
46723
|
-
description: [
|
|
46979
|
+
description: [
|
|
46980
|
+
"Search the UIRouter state tree of a dbx-components app. Matches against state names, full URLs, component class names, resolve keys, and source-file paths. Returns the top 10 results with state name, URL, component, and source location.",
|
|
46981
|
+
"",
|
|
46982
|
+
"Provide at least one of `sources` / `paths` / `glob` plus a `query`. `scope` narrows to a single axis (`name`, `url`, `component`, `resolve`)."
|
|
46983
|
+
].join("\n"),
|
|
46724
46984
|
inputSchema: {
|
|
46725
46985
|
type: "object",
|
|
46726
46986
|
properties: {
|
|
@@ -47812,7 +48072,25 @@ function formatScaffold2(args) {
|
|
|
47812
48072
|
if (args.presetKeys.length > 0) {
|
|
47813
48073
|
lines.push("## Presets", "", "```ts", renderPresetConstants(args), "```", "");
|
|
47814
48074
|
}
|
|
47815
|
-
lines.push(
|
|
48075
|
+
lines.push(
|
|
48076
|
+
"## Template",
|
|
48077
|
+
"",
|
|
48078
|
+
"```html",
|
|
48079
|
+
renderTemplate(args),
|
|
48080
|
+
"```",
|
|
48081
|
+
"",
|
|
48082
|
+
"## Component class",
|
|
48083
|
+
"",
|
|
48084
|
+
"```ts",
|
|
48085
|
+
...renderImports(args),
|
|
48086
|
+
"",
|
|
48087
|
+
renderClass(args),
|
|
48088
|
+
"```",
|
|
48089
|
+
"",
|
|
48090
|
+
"## Notes",
|
|
48091
|
+
"",
|
|
48092
|
+
"- The outer `[dbxFilterSourceConnector]` provides both `FilterSource` and `FilterSourceConnector`. Children that own the form use `[dbxFilterSource]` + `[dbxFilterConnectSource]` to wire up."
|
|
48093
|
+
);
|
|
47816
48094
|
if (args.presetKeys.length > 0) {
|
|
47817
48095
|
lines.push("- Each `ClickableFilterPreset` listed in the preset array becomes a chip; clicking sets `presetValue` on the filter source.");
|
|
47818
48096
|
}
|
|
@@ -47879,7 +48157,20 @@ function formatArgsTable(args) {
|
|
|
47879
48157
|
return lines.join("\n");
|
|
47880
48158
|
}
|
|
47881
48159
|
function formatEntry4(entry, depth) {
|
|
47882
|
-
const lines = [
|
|
48160
|
+
const lines = [
|
|
48161
|
+
`# ${entry.className}`,
|
|
48162
|
+
"",
|
|
48163
|
+
entry.description,
|
|
48164
|
+
"",
|
|
48165
|
+
bullet3("slug", `\`${entry.slug}\``),
|
|
48166
|
+
bullet3("pipe", `\`${entry.pipeName}\``),
|
|
48167
|
+
bullet3("category", `\`${entry.category}\``),
|
|
48168
|
+
bullet3("purity", `\`${entry.purity}\``),
|
|
48169
|
+
bullet3("input", `\`${entry.inputType}\``),
|
|
48170
|
+
bullet3("output", `\`${entry.outputType}\``),
|
|
48171
|
+
bullet3("module", `\`${entry.module}\``),
|
|
48172
|
+
""
|
|
48173
|
+
];
|
|
47883
48174
|
if (depth === "full") {
|
|
47884
48175
|
if (entry.args.length > 0) {
|
|
47885
48176
|
lines.push("## Args", "", formatArgsTable(entry.args), "");
|
|
@@ -48041,7 +48332,20 @@ function formatSearchResults5(input) {
|
|
|
48041
48332
|
const lines = [`# Search: \`${query}\``, "", `Tokens: \`${tokenDisplay}\` \xB7 ${hits.length} result${hits.length === 1 ? "" : "s"}`, ""];
|
|
48042
48333
|
for (const hit of hits) {
|
|
48043
48334
|
const entry = hit.entry;
|
|
48044
|
-
lines.push(
|
|
48335
|
+
lines.push(
|
|
48336
|
+
`## \`${entry.slug}\` \xB7 ${entry.category} \xB7 score ${hit.score}`,
|
|
48337
|
+
"",
|
|
48338
|
+
`- **pipe:** \`${entry.pipeName}\``,
|
|
48339
|
+
`- **class:** \`${entry.className}\``,
|
|
48340
|
+
`- **input:** \`${entry.inputType}\``,
|
|
48341
|
+
`- **output:** \`${entry.outputType}\``,
|
|
48342
|
+
`- **matched:** \`${hit.matchedTokens.join(", ")}\``,
|
|
48343
|
+
"",
|
|
48344
|
+
entry.description,
|
|
48345
|
+
"",
|
|
48346
|
+
`\u2192 \`dbx_pipe_lookup topic="${entry.slug}"\` for full docs.`,
|
|
48347
|
+
""
|
|
48348
|
+
);
|
|
48045
48349
|
}
|
|
48046
48350
|
return lines.join("\n").trimEnd();
|
|
48047
48351
|
}
|
|
@@ -48317,7 +48621,20 @@ function formatCatalogHit(input) {
|
|
|
48317
48621
|
const tagDisplay = entry.tags.length > 0 ? `
|
|
48318
48622
|
- **tags:** ${tagBadges}` : "";
|
|
48319
48623
|
const badge = options.headerBadge ?? "";
|
|
48320
|
-
return [
|
|
48624
|
+
return [
|
|
48625
|
+
`## \`${entry.slug}\` \xB7 \`${entry.name}\` \xB7 ${entry.kind}${badge} \xB7 score ${hit.score}`,
|
|
48626
|
+
"",
|
|
48627
|
+
`- **module:** \`${entry.module}\``,
|
|
48628
|
+
`- **category:** \`${entry.category}\``,
|
|
48629
|
+
`- **subpath:** \`${entry.subpath}\``,
|
|
48630
|
+
`- **signature:** \`${entry.signature}\``,
|
|
48631
|
+
`- **matched:** \`${hit.matchedTokens.join(", ")}\`${tagDisplay}`,
|
|
48632
|
+
"",
|
|
48633
|
+
entry.description.split("\n")[0],
|
|
48634
|
+
"",
|
|
48635
|
+
`\u2192 \`${options.lookupTool} topic="${entry.slug}"\` for full docs.`,
|
|
48636
|
+
""
|
|
48637
|
+
];
|
|
48321
48638
|
}
|
|
48322
48639
|
function formatCatalogSearchResults(input) {
|
|
48323
48640
|
const { query, tokens, hits, filters, entityLabel, emptyHint, hitOptions } = input;
|
|
@@ -48493,7 +48810,20 @@ function appendFullEntrySections2(lines, entry) {
|
|
|
48493
48810
|
}
|
|
48494
48811
|
function formatEntry6(entry, depth) {
|
|
48495
48812
|
const heading = `# ${entry.name}`;
|
|
48496
|
-
const lines = [
|
|
48813
|
+
const lines = [
|
|
48814
|
+
heading,
|
|
48815
|
+
"",
|
|
48816
|
+
entry.description,
|
|
48817
|
+
"",
|
|
48818
|
+
bullet5("slug", `\`${entry.slug}\``),
|
|
48819
|
+
bullet5("kind", `\`${entry.kind}\``),
|
|
48820
|
+
bullet5("category", `\`${entry.category}\``),
|
|
48821
|
+
bullet5("module", `\`${entry.module}\``),
|
|
48822
|
+
bullet5("subpath", `\`${entry.subpath}\``),
|
|
48823
|
+
bullet5("optional", entry.optional ? "yes (use this for `Maybe<T>` fields)" : "no (required)"),
|
|
48824
|
+
bullet5("signature", `\`${entry.signature}\``),
|
|
48825
|
+
""
|
|
48826
|
+
];
|
|
48497
48827
|
appendDeprecationNotice2(lines, entry);
|
|
48498
48828
|
if (depth === "full") {
|
|
48499
48829
|
appendFullEntrySections2(lines, entry);
|
|
@@ -49823,7 +50153,14 @@ function appendTaggedSections(lines, tagged) {
|
|
|
49823
50153
|
}
|
|
49824
50154
|
function appendUntaggedSection(lines, untagged) {
|
|
49825
50155
|
if (untagged.length === 0) return;
|
|
49826
|
-
lines.push(
|
|
50156
|
+
lines.push(
|
|
50157
|
+
"## Untagged candidates",
|
|
50158
|
+
"",
|
|
50159
|
+
"Exported functions returning `FirestoreQueryConstraint[]` without an `@dbxModelFirebaseIndex` block. Tag them so the validator can verify their indexes \u2014 or annotate with `@dbxModelFirebaseIndexSkip` to record that they were considered.",
|
|
50160
|
+
"",
|
|
50161
|
+
"| Function | File |",
|
|
50162
|
+
"| --- | --- |"
|
|
50163
|
+
);
|
|
49827
50164
|
for (const candidate of untagged) {
|
|
49828
50165
|
lines.push(`| \`${candidate.name}\` | \`${candidate.subpath}:${candidate.line}\` |`);
|
|
49829
50166
|
}
|
|
@@ -50523,7 +50860,14 @@ function formatReportAsMarkdown17(report) {
|
|
|
50523
50860
|
} else {
|
|
50524
50861
|
lines.push("## \u2705 In sync", "");
|
|
50525
50862
|
}
|
|
50526
|
-
lines.push(
|
|
50863
|
+
lines.push(
|
|
50864
|
+
`- generated composites: ${report.generatedComposites}`,
|
|
50865
|
+
`- existing composites: ${report.existingComposites}`,
|
|
50866
|
+
`- generated fieldOverrides: ${report.generatedFieldOverrides}`,
|
|
50867
|
+
`- existing fieldOverrides: ${report.existingFieldOverrides}`,
|
|
50868
|
+
`- ${report.errorCount} error(s), ${report.warningCount} warning(s)`,
|
|
50869
|
+
""
|
|
50870
|
+
);
|
|
50527
50871
|
if (report.violations.length > 0) {
|
|
50528
50872
|
appendViolationSection(
|
|
50529
50873
|
lines,
|
|
@@ -50941,7 +51285,10 @@ function renderStorageFilePurpose(input, ctx) {
|
|
|
50941
51285
|
tokens: ctx.tokens,
|
|
50942
51286
|
files: [componentFile, handlerFile],
|
|
50943
51287
|
wiring,
|
|
50944
|
-
summary: applyTokens(
|
|
51288
|
+
summary: applyTokens(
|
|
51289
|
+
`Scaffolds a new \`<<SCREAMING>>_PURPOSE\` storage-file purpose end-to-end: component-side constants + path helpers (append to \`storagefile.ts\`) and an API-side upload-initializer factory in \`handlers/\`. After applying, run \`dbx_validate_app_storagefiles\` and \`dbx_validate_storagefile_folder\` against your project \u2014 both should PASS.`,
|
|
51290
|
+
ctx
|
|
51291
|
+
)
|
|
50945
51292
|
};
|
|
50946
51293
|
return result;
|
|
50947
51294
|
}
|
|
@@ -50949,19 +51296,28 @@ function renderNotificationTemplate(input, ctx) {
|
|
|
50949
51296
|
const componentFile = {
|
|
50950
51297
|
status: "append",
|
|
50951
51298
|
path: applyTokens("<<componentDir>>/src/lib/model/notification/notification.ts", ctx),
|
|
50952
|
-
description: applyTokens(
|
|
51299
|
+
description: applyTokens(
|
|
51300
|
+
`Append the \`<<Pascal>>\` notification block to \`notification.ts\`: type constant + info + data interface + template factory. Reuses the file's existing imports \u2014 no new top-level imports required as long as \`NotificationTemplateType\`, \`NotificationTemplateTypeInfo\`, \`CreateNotificationTemplate\`, \`createNotificationTemplate\`, \`FirebaseAuthUserId\`, \`profileIdentity\`, and \`ProfileDocument\` are already in scope.`,
|
|
51301
|
+
ctx
|
|
51302
|
+
),
|
|
50953
51303
|
content: applyTokens(NOTIFICATION_TEMPLATE_COMPONENT_TEMPLATE, ctx)
|
|
50954
51304
|
};
|
|
50955
51305
|
const factoryFile = {
|
|
50956
51306
|
status: "append",
|
|
50957
51307
|
path: applyTokens("<<apiDir>>/src/app/common/model/notification/notification.factory.ts", ctx),
|
|
50958
|
-
description: applyTokens(
|
|
51308
|
+
description: applyTokens(
|
|
51309
|
+
`Append the \`<<appCamel>><<Pascal>>NotificationFactory\` to \`notification.factory.ts\`. Reuses the file's existing imports for \`NotificationMessageFunctionFactoryConfig\`, \`NotificationMessageInputContext\`, \`NotificationMessageContent\`, \`NotificationMessage\`, \`notificationMessageFunction\`, and \`NotificationTemplateServiceTypeConfig\`.`,
|
|
51310
|
+
ctx
|
|
51311
|
+
),
|
|
50959
51312
|
content: applyTokens(NOTIFICATION_TEMPLATE_FACTORY_TEMPLATE, ctx)
|
|
50960
51313
|
};
|
|
50961
51314
|
const wiring = [
|
|
50962
51315
|
{
|
|
50963
51316
|
file: applyTokens("<<apiDir>>/src/app/common/model/notification/notification.factory.ts", ctx),
|
|
50964
|
-
description: applyTokens(
|
|
51317
|
+
description: applyTokens(
|
|
51318
|
+
`Two registrations are required: (1) add a call to \`<<appCamel>><<Pascal>>NotificationFactory(context)\` in the existing configs-array factory return list (so the handler is reachable through the template service); (2) add \`<<SCREAMING>>_NOTIFICATION_TEMPLATE_TYPE_INFO\` to the \`*_NOTIFICATION_TEMPLATE_TYPE_INFO_RECORD\` aggregator in \`notification.ts\` so the metadata path is wired.`,
|
|
51319
|
+
ctx
|
|
51320
|
+
),
|
|
50965
51321
|
snippet: applyTokens(NOTIFICATION_TEMPLATE_WIRING_SNIPPET, ctx)
|
|
50966
51322
|
}
|
|
50967
51323
|
];
|
|
@@ -50970,7 +51326,10 @@ function renderNotificationTemplate(input, ctx) {
|
|
|
50970
51326
|
tokens: ctx.tokens,
|
|
50971
51327
|
files: [componentFile, factoryFile],
|
|
50972
51328
|
wiring,
|
|
50973
|
-
summary: applyTokens(
|
|
51329
|
+
summary: applyTokens(
|
|
51330
|
+
`Scaffolds a new \`<<SCREAMING>>_NOTIFICATION_TEMPLATE_TYPE\` end-to-end: component-side type + info + data + template factory (append to \`notification.ts\`) and an API-side template-service factory (append to \`notification.factory.ts\`). After applying the wiring, run \`dbx_notification_m_validate_app\` and \`dbx_notification_m_validate_folder\` against your project \u2014 both should PASS.`,
|
|
51331
|
+
ctx
|
|
51332
|
+
)
|
|
50974
51333
|
};
|
|
50975
51334
|
return result;
|
|
50976
51335
|
}
|
|
@@ -50995,7 +51354,10 @@ function renderNotificationTask(input, ctx) {
|
|
|
50995
51354
|
const wiring = [
|
|
50996
51355
|
{
|
|
50997
51356
|
file: applyTokens("<<apiDir>>/src/app/common/model/notification/notification.task.service.ts", ctx),
|
|
50998
|
-
description: applyTokens(
|
|
51357
|
+
description: applyTokens(
|
|
51358
|
+
`Import the new factory, instantiate it bound to \`<<camel>>Handler\`, push that name into the existing \`handlers\` array, and add \`<<SCREAMING>>_NOTIFICATION_TASK_TYPE\` to the \`ALL_NOTIFICATION_TASK_TYPES\` aggregate in the component package's \`notification.task.ts\` so the validate-vs-handler parity check stays consistent.`,
|
|
51359
|
+
ctx
|
|
51360
|
+
),
|
|
50999
51361
|
snippet: applyTokens(NOTIFICATION_TASK_WIRING_SNIPPET, ctx)
|
|
51000
51362
|
}
|
|
51001
51363
|
];
|
|
@@ -51004,7 +51366,10 @@ function renderNotificationTask(input, ctx) {
|
|
|
51004
51366
|
tokens: ctx.tokens,
|
|
51005
51367
|
files: [componentFile, handlerFile],
|
|
51006
51368
|
wiring,
|
|
51007
|
-
summary: applyTokens(
|
|
51369
|
+
summary: applyTokens(
|
|
51370
|
+
`Scaffolds a new \`<<SCREAMING>>_NOTIFICATION_TASK_TYPE\` end-to-end: component-side type + checkpoint alias + data + template factory (append to \`notification.task.ts\`) and an API-side handler factory in \`handlers/\`. After applying the wiring (handlers array + ALL_NOTIFICATION_TASK_TYPES), run \`dbx_notification_m_validate_app\` and \`dbx_notification_m_validate_folder\` against your project \u2014 both should PASS.`,
|
|
51371
|
+
ctx
|
|
51372
|
+
)
|
|
51008
51373
|
};
|
|
51009
51374
|
return result;
|
|
51010
51375
|
}
|
|
@@ -51269,7 +51634,11 @@ var STORAGEFILE_PURPOSE = {
|
|
|
51269
51634
|
heading: "API upload handler",
|
|
51270
51635
|
path: "<apiDir>/src/app/common/model/storagefile/handlers/upload.<name>.ts",
|
|
51271
51636
|
altPaths: ["Inline in `<apiDir>/src/app/common/model/storagefile/storagefile.upload.service.ts` for tiny workspaces."],
|
|
51272
|
-
body: [
|
|
51637
|
+
body: [
|
|
51638
|
+
"**Required export:** `<camelName>StorageFileUploadInitializers(context): StorageFileInitializeFromUploadServiceInitializer[]`.",
|
|
51639
|
+
"",
|
|
51640
|
+
"**Wire into:** `<apiDir>/src/app/common/model/storagefile/storagefile.upload.service.ts` \u2014 spread into the `initializer: [...]` array passed to `storageFileInitializeFromUploadService({ initializer })`."
|
|
51641
|
+
].join("\n")
|
|
51273
51642
|
},
|
|
51274
51643
|
{
|
|
51275
51644
|
heading: "API processor (only if subtasks declared)",
|
|
@@ -51331,12 +51700,27 @@ var STORAGEFILE_PROCESSOR = {
|
|
|
51331
51700
|
{
|
|
51332
51701
|
heading: "API processor handler",
|
|
51333
51702
|
path: "<apiDir>/src/app/common/model/notification/handlers/storagefile/task.handler.storagefile.<name>.ts",
|
|
51334
|
-
body: [
|
|
51703
|
+
body: [
|
|
51704
|
+
"**Required shape:**",
|
|
51705
|
+
"```ts",
|
|
51706
|
+
"export function <camelName>ProcessingNotificationTaskHandler(context): StorageFileProcessingPurposeSubtaskProcessorConfig<<Name>SubtaskMetadata, <Name>ProcessingSubtask> {",
|
|
51707
|
+
" const config: StorageFileProcessingPurposeSubtaskProcessorConfig<...> = {",
|
|
51708
|
+
" target: <NAME>_PURPOSE,",
|
|
51709
|
+
" flow: [",
|
|
51710
|
+
" { subtask: <NAME>_PURPOSE_<DESC>_SUBTASK, fn: async (input) => ... }",
|
|
51711
|
+
" ]",
|
|
51712
|
+
" };",
|
|
51713
|
+
" return config;",
|
|
51714
|
+
"}",
|
|
51715
|
+
"```"
|
|
51716
|
+
].join("\n")
|
|
51335
51717
|
},
|
|
51336
51718
|
{
|
|
51337
51719
|
heading: "Wire into processing handler",
|
|
51338
51720
|
path: "<apiDir>/src/app/common/model/notification/handlers/task.handler.storagefile.ts",
|
|
51339
|
-
body: [
|
|
51721
|
+
body: [
|
|
51722
|
+
"Push the new processor config into the `processors: [...]` array passed to `storageFileProcessingNotificationTaskHandler({ processors })`. The surrounding handler is itself wired into `notificationTaskService({ handlers })` \u2014 already covered by the notification-task plumbing, so no further wiring needed here."
|
|
51723
|
+
].join("\n")
|
|
51340
51724
|
}
|
|
51341
51725
|
],
|
|
51342
51726
|
seeAlso: ["storagefile-purpose", "storagefile-processor-subtask", "notification-task"],
|
|
@@ -51350,7 +51734,12 @@ var STORAGEFILE_PROCESSOR_SUBTASK = {
|
|
|
51350
51734
|
{
|
|
51351
51735
|
heading: "Component declarations",
|
|
51352
51736
|
path: "<componentDir>/src/lib/model/storagefile/storagefile.<name>.ts (the file that already declares the purpose)",
|
|
51353
|
-
body: [
|
|
51737
|
+
body: [
|
|
51738
|
+
"**Add:**",
|
|
51739
|
+
"- `<NAME>_PURPOSE_<DESC>_SUBTASK: StorageFileProcessingSubtask = '<descriptor>'` \u2014 the new subtask constant.",
|
|
51740
|
+
"- Extend the `<Name>ProcessingSubtask` union alias: `typeof <NAME>_PURPOSE_<DESC>_SUBTASK | ...existing`.",
|
|
51741
|
+
"- Optional: extend `<Name>ProcessingSubtaskMetadata` with any new fields the new subtask needs."
|
|
51742
|
+
].join("\n")
|
|
51354
51743
|
},
|
|
51355
51744
|
{
|
|
51356
51745
|
heading: "API processor flow",
|
|
@@ -51382,13 +51771,19 @@ var NOTIFICATION_TEMPLATE = {
|
|
|
51382
51771
|
{
|
|
51383
51772
|
heading: "Component aggregator",
|
|
51384
51773
|
path: "<componentDir>/src/lib/model/notification/notification.ts",
|
|
51385
|
-
body: [
|
|
51774
|
+
body: [
|
|
51775
|
+
"Add the new info to the call to `notificationTemplateTypeInfoRecord([...])` \u2014 either as a direct entry (`<NAME>_NOTIFICATION_TEMPLATE_TYPE_INFO`) or via a spread of the module aggregate (`...ALL_<MODULE>_NOTIFICATION_TEMPLATE_TYPE_INFOS`). The result constant is typically `<APP>_FIREBASE_NOTIFICATION_TEMPLATE_TYPE_INFO_RECORD`."
|
|
51776
|
+
].join("\n")
|
|
51386
51777
|
},
|
|
51387
51778
|
{
|
|
51388
51779
|
heading: "API handler factory",
|
|
51389
51780
|
path: "<apiDir>/src/app/common/model/notification/notification.factory.ts",
|
|
51390
51781
|
altPaths: ["<apiDir>/src/app/common/model/notification/notification.factory.<name>.ts (split per-template for larger workspaces)"],
|
|
51391
|
-
body: [
|
|
51782
|
+
body: [
|
|
51783
|
+
"**Required export:** A factory function returning a `NotificationTemplateServiceTypeConfig` with `{ type: <NAME>_NOTIFICATION_TEMPLATE_TYPE, factory: async (...) => ... }`.",
|
|
51784
|
+
"",
|
|
51785
|
+
"**Wire into:** the top-level `<app>NotificationTemplateServiceConfigsArrayFactory(context)` \u2014 call the new factory inside its returned array. The configs-array factory is itself bound via `NOTIFICATION_TEMPLATE_SERVICE_CONFIGS_ARRAY_TOKEN` in the notification module."
|
|
51786
|
+
].join("\n")
|
|
51392
51787
|
}
|
|
51393
51788
|
],
|
|
51394
51789
|
seeAlso: ["notification-task"],
|
|
@@ -51417,7 +51812,17 @@ var NOTIFICATION_TASK = {
|
|
|
51417
51812
|
heading: "API handler config",
|
|
51418
51813
|
path: "<apiDir>/src/app/common/model/notification/notification.task.service.ts",
|
|
51419
51814
|
altPaths: ["<apiDir>/src/app/common/model/notification/handlers/task.handler.<name>.ts (per-task split for larger workspaces)"],
|
|
51420
|
-
body: [
|
|
51815
|
+
body: [
|
|
51816
|
+
"**Required shape:**",
|
|
51817
|
+
"```ts",
|
|
51818
|
+
"const <camelName>NotificationTaskHandler: NotificationTaskServiceTaskHandlerConfig<<Name>NotificationTaskData, <Name>NotificationTaskCheckpoint> = {",
|
|
51819
|
+
" type: <NAME>_NOTIFICATION_TASK_TYPE,",
|
|
51820
|
+
" flow: [",
|
|
51821
|
+
" { checkpoint: 'part_a', fn: async (notificationTask) => ... }",
|
|
51822
|
+
" ]",
|
|
51823
|
+
"};",
|
|
51824
|
+
"```"
|
|
51825
|
+
].join("\n")
|
|
51421
51826
|
},
|
|
51422
51827
|
{
|
|
51423
51828
|
heading: "Wire into task service",
|
|
@@ -52340,7 +52745,12 @@ function formatFirestoreRulesScanReportAsMarkdown(report) {
|
|
|
52340
52745
|
}
|
|
52341
52746
|
lines.push("");
|
|
52342
52747
|
if (report.serverOnly.length > 0) {
|
|
52343
|
-
lines.push(
|
|
52748
|
+
lines.push(
|
|
52749
|
+
`**Server-only (${report.serverOnly.length}):** ${report.serverOnly.map((c) => `\`${c}\``).join(", ")}`,
|
|
52750
|
+
"",
|
|
52751
|
+
"Each of these must carry `@dbxModelServerOnly` on its model interface AND `serverOnly: true` on its `firebaseModelServiceFactory` config, so the model API refuses the read instead of authorizing it via `roleMapForModel`.",
|
|
52752
|
+
""
|
|
52753
|
+
);
|
|
52344
52754
|
}
|
|
52345
52755
|
if (report.gettableNotListable.length > 0) {
|
|
52346
52756
|
lines.push(`**Gettable but not listable (${report.gettableNotListable.length}):** ${report.gettableNotListable.map((c) => `\`${c}\``).join(", ")}`, "", "`firestore-get` works on these; `firestore-query` does not \u2014 the rules grant reads by id only.", "");
|
|
@@ -52578,7 +52988,15 @@ function validateModelServerOnly(input) {
|
|
|
52578
52988
|
}
|
|
52579
52989
|
for (const iface of input.interfaces) {
|
|
52580
52990
|
if (iface.serverOnly && !iface.hasModelTag) {
|
|
52581
|
-
violations.push(
|
|
52991
|
+
violations.push(
|
|
52992
|
+
buildViolation({
|
|
52993
|
+
code: "MODEL_SERVER_ONLY_TAG_WITHOUT_MODEL_TAG" /* MODEL_SERVER_ONLY_TAG_WITHOUT_MODEL_TAG */,
|
|
52994
|
+
severity: "warning",
|
|
52995
|
+
modelType: void 0,
|
|
52996
|
+
file: iface.file,
|
|
52997
|
+
message: `Interface \`${iface.name}\` carries \`@dbxModelServerOnly\` but no \`@dbxModel\` tag, so the model extractor skips it and \`serverOnly\` never lands on the generated model manifest.`
|
|
52998
|
+
})
|
|
52999
|
+
);
|
|
52582
53000
|
}
|
|
52583
53001
|
}
|
|
52584
53002
|
const errorCount = violations.filter((v) => v.severity === "error").length;
|
|
@@ -52600,22 +53018,78 @@ function appendServiceViolations(input) {
|
|
|
52600
53018
|
const { modelType, collection, tag, flag, rules } = reconciliation;
|
|
52601
53019
|
const declared = tag === true || flag;
|
|
52602
53020
|
if (collection == null) {
|
|
52603
|
-
violations.push(
|
|
53021
|
+
violations.push(
|
|
53022
|
+
buildViolation({
|
|
53023
|
+
code: "MODEL_SERVER_ONLY_UNRESOLVED_IDENTITY" /* MODEL_SERVER_ONLY_UNRESOLVED_IDENTITY */,
|
|
53024
|
+
severity: "warning",
|
|
53025
|
+
modelType,
|
|
53026
|
+
file: service.exportName,
|
|
53027
|
+
message: `No \`firestoreModelIdentity(...)\` for model type \`${modelType}\` was found in the scanned dirs, so its collection name \u2014 and therefore the rules leg of the reconciliation \u2014 could not be resolved.`
|
|
53028
|
+
})
|
|
53029
|
+
);
|
|
52604
53030
|
} else if (rules === true && !flag) {
|
|
52605
|
-
violations.push(
|
|
53031
|
+
violations.push(
|
|
53032
|
+
buildViolation({
|
|
53033
|
+
code: "MODEL_SERVER_ONLY_MISSING_RUNTIME_FLAG" /* MODEL_SERVER_ONLY_MISSING_RUNTIME_FLAG */,
|
|
53034
|
+
severity: "error",
|
|
53035
|
+
modelType,
|
|
53036
|
+
file: service.exportName,
|
|
53037
|
+
message: `\`${collection}\` has no client read grant in the rules (get=${reconciliation.rulesGet}, list=${reconciliation.rulesList}) but \`${service.exportName}\` does not set \`serverOnly: true\`, so the model API will hand a client a document the rules would refuse.`
|
|
53038
|
+
})
|
|
53039
|
+
);
|
|
52606
53040
|
} else if (rules === false && declared) {
|
|
52607
|
-
violations.push(
|
|
53041
|
+
violations.push(
|
|
53042
|
+
buildViolation({
|
|
53043
|
+
code: "MODEL_SERVER_ONLY_RULES_ALLOW_READ" /* MODEL_SERVER_ONLY_RULES_ALLOW_READ */,
|
|
53044
|
+
severity: "error",
|
|
53045
|
+
modelType,
|
|
53046
|
+
file: service.exportName,
|
|
53047
|
+
message: `\`${modelType}\` declares server-only (${describeDeclarations(tag, flag)}) but \`${collection}\` IS client-readable in the rules (get=${reconciliation.rulesGet}, list=${reconciliation.rulesList}).`
|
|
53048
|
+
})
|
|
53049
|
+
);
|
|
52608
53050
|
}
|
|
52609
53051
|
if (rules === true && iface != null && !iface.serverOnly) {
|
|
52610
|
-
violations.push(
|
|
53052
|
+
violations.push(
|
|
53053
|
+
buildViolation({
|
|
53054
|
+
code: "MODEL_SERVER_ONLY_MISSING_TAG" /* MODEL_SERVER_ONLY_MISSING_TAG */,
|
|
53055
|
+
severity: "warning",
|
|
53056
|
+
modelType,
|
|
53057
|
+
file: iface.file,
|
|
53058
|
+
message: `\`${collection}\` has no client read grant in the rules but interface \`${iface.name}\` has no \`@dbxModelServerOnly\` tag, so the CLI cannot refuse the read locally.`
|
|
53059
|
+
})
|
|
53060
|
+
);
|
|
52611
53061
|
}
|
|
52612
53062
|
if (iface == null && flag) {
|
|
52613
|
-
violations.push(
|
|
53063
|
+
violations.push(
|
|
53064
|
+
buildViolation({
|
|
53065
|
+
code: "MODEL_SERVER_ONLY_NO_INTERFACE" /* MODEL_SERVER_ONLY_NO_INTERFACE */,
|
|
53066
|
+
severity: "warning",
|
|
53067
|
+
modelType,
|
|
53068
|
+
file: service.exportName,
|
|
53069
|
+
message: `\`${modelType}\` sets \`serverOnly: true\` but its data type \`${service.modelName ?? "(unknown)"}\` resolves to no interface in the scanned dirs, so there is nothing to carry \`@dbxModelServerOnly\`.`
|
|
53070
|
+
})
|
|
53071
|
+
);
|
|
52614
53072
|
} else if (iface != null && tag !== flag) {
|
|
52615
|
-
violations.push(
|
|
53073
|
+
violations.push(
|
|
53074
|
+
buildViolation({
|
|
53075
|
+
code: "MODEL_SERVER_ONLY_TAG_FLAG_MISMATCH" /* MODEL_SERVER_ONLY_TAG_FLAG_MISMATCH */,
|
|
53076
|
+
severity: "error",
|
|
53077
|
+
modelType,
|
|
53078
|
+
file: iface.file,
|
|
53079
|
+
message: `\`${modelType}\`: interface \`${iface.name}\` ${tag === true ? "carries" : "does NOT carry"} \`@dbxModelServerOnly\` while \`${service.exportName}\` ${flag ? "DOES" : "does not"} set \`serverOnly: true\`.`
|
|
53080
|
+
})
|
|
53081
|
+
);
|
|
52616
53082
|
}
|
|
52617
53083
|
if (manifestModelTypes != null && declared && !manifestModelTypes.has(modelType)) {
|
|
52618
|
-
violations.push(
|
|
53084
|
+
violations.push(
|
|
53085
|
+
buildViolation({
|
|
53086
|
+
code: "MODEL_SERVER_ONLY_NOT_IN_MANIFEST" /* MODEL_SERVER_ONLY_NOT_IN_MANIFEST */,
|
|
53087
|
+
severity: "warning",
|
|
53088
|
+
modelType,
|
|
53089
|
+
file: manifestFile,
|
|
53090
|
+
message: `\`${modelType}\` is server-only but has no entry in the generated model manifest, so the CLI's local pre-transport refusal cannot fire for it \u2014 the read costs a round-trip to an API that refuses it.`
|
|
53091
|
+
})
|
|
53092
|
+
);
|
|
52619
53093
|
}
|
|
52620
53094
|
}
|
|
52621
53095
|
function legsAgree(input) {
|
|
@@ -52724,7 +53198,6 @@ async function walkTypeScriptFiles(dir) {
|
|
|
52724
53198
|
try {
|
|
52725
53199
|
entries = (await readdir12(dir)).sort();
|
|
52726
53200
|
} catch {
|
|
52727
|
-
return out;
|
|
52728
53201
|
}
|
|
52729
53202
|
for (const entry of entries) {
|
|
52730
53203
|
if (entry === "node_modules" || entry === "dist") continue;
|
|
@@ -55739,7 +56212,13 @@ async function tryReadBatchPath(path, cwd, reader) {
|
|
|
55739
56212
|
import { type as type104 } from "arktype";
|
|
55740
56213
|
var DBX_AUTH_CLAIM_LOOKUP_TOOL = {
|
|
55741
56214
|
name: "dbx_auth_claim_lookup",
|
|
55742
|
-
description: [
|
|
56215
|
+
description: [
|
|
56216
|
+
"Look up a Firebase Auth custom-claim entry by key (`a`, `o`, `fr`) or by `*ApiAuthClaims` interface name (`DemoApiAuthClaims`).",
|
|
56217
|
+
"",
|
|
56218
|
+
"Returns the claim meaning, value type, role mapping through `authRoleClaimsService`, owning app + interface, and the source path:line where the claim is declared.",
|
|
56219
|
+
"",
|
|
56220
|
+
"Use the optional `app` parameter (`demo-api`) to scope the lookup when the same claim key exists in multiple apps."
|
|
56221
|
+
].join("\n"),
|
|
55743
56222
|
inputSchema: {
|
|
55744
56223
|
type: "object",
|
|
55745
56224
|
properties: {
|
|
@@ -55962,7 +56441,14 @@ function sourceLink2(path, line) {
|
|
|
55962
56441
|
import { type as type106 } from "arktype";
|
|
55963
56442
|
var DBX_AUTH_ROLE_LOOKUP_TOOL = {
|
|
55964
56443
|
name: "dbx_auth_role_lookup",
|
|
55965
|
-
description: [
|
|
56444
|
+
description: [
|
|
56445
|
+
"Look up auth roles forward (by role string / const name), by tag, or in reverse (model + verb \u2192 required roles/scopes).",
|
|
56446
|
+
"",
|
|
56447
|
+
'- `topic="admin"` or `topic="AUTH_ADMIN_ROLE"` \u2192 role entry + claims that set it.',
|
|
56448
|
+
'- `tag="privileged"` \u2192 every role tagged with `privileged`.',
|
|
56449
|
+
'- `model="StorageFile" verb="read"` \u2192 reverse lookup of the roles/scopes the gate would expect.',
|
|
56450
|
+
'- `topic="list"` \u2192 catalog.'
|
|
56451
|
+
].join("\n"),
|
|
55966
56452
|
inputSchema: {
|
|
55967
56453
|
type: "object",
|
|
55968
56454
|
properties: {
|
|
@@ -56145,7 +56631,11 @@ function sourceLink3(path, line) {
|
|
|
56145
56631
|
import { type as type107 } from "arktype";
|
|
56146
56632
|
var DBX_AUTH_TOKEN_EXPLAIN_TOOL = {
|
|
56147
56633
|
name: "dbx_auth_token_explain",
|
|
56148
|
-
description: [
|
|
56634
|
+
description: [
|
|
56635
|
+
"Decode a JWT or claims object and annotate each claim with the catalogued meaning, role mapping, and source path:line.",
|
|
56636
|
+
"",
|
|
56637
|
+
"Pass `token` (raw JWT string) **or** `claims` (decoded JSON object). Reserved JWT claims (`iss`/`aud`/`exp`/`iat`/`sub`/`scope`) are listed separately. Flags expired tokens and unknown wrong-issuer claims."
|
|
56638
|
+
].join("\n"),
|
|
56149
56639
|
inputSchema: {
|
|
56150
56640
|
type: "object",
|
|
56151
56641
|
properties: {
|
|
@@ -56604,7 +57094,13 @@ function createAuthClusterTools(registry) {
|
|
|
56604
57094
|
function registerTools(server, options = {}) {
|
|
56605
57095
|
const underlyingServer = server.server;
|
|
56606
57096
|
const tools = [...DBX_TOOLS];
|
|
56607
|
-
tools.push(
|
|
57097
|
+
tools.push(
|
|
57098
|
+
createUiExamplesTool({ examplesRegistry: options.dbxDocsUiExamplesRegistry }),
|
|
57099
|
+
createModelValidateTool({ ruleOptions: options.modelValidateRuleOptions }),
|
|
57100
|
+
createModelValidateFolderTool({ ruleOptions: options.modelValidateRuleOptions }),
|
|
57101
|
+
createModelFixtureValidateAppTool({ getRegistry: () => options.fixtureModelRegistry }),
|
|
57102
|
+
createLogSearchTool(options.logSearchConfig)
|
|
57103
|
+
);
|
|
56608
57104
|
if (options.forgeFieldRegistry !== void 0) {
|
|
56609
57105
|
tools.push(createLookupFormTool({ registry: options.forgeFieldRegistry }), createSearchFormTool({ registry: options.forgeFieldRegistry }), createFormScaffoldTool({ registry: options.forgeFieldRegistry }));
|
|
56610
57106
|
}
|
|
@@ -56858,7 +57354,25 @@ async function loadServerContext(options = {}) {
|
|
|
56858
57354
|
await emitDownstreamHints({ cwd, externalCounts, onDownstreamHints: options.onDownstreamHints });
|
|
56859
57355
|
const context = {
|
|
56860
57356
|
resources: { semanticTypeRegistry: registry, forgeFieldRegistry: forgeRegistry, pipeRegistry, utilRegistry, modelSnapshotFieldRegistry, modelFirebaseIndexRegistry, uiComponentRegistry: uiRegistry, actionRegistry, filterRegistry, tokenRegistry, cssUtilityRegistry, authRegistry, cwd },
|
|
56861
|
-
tools: {
|
|
57357
|
+
tools: {
|
|
57358
|
+
semanticTypeRegistry: registry,
|
|
57359
|
+
forgeFieldRegistry: forgeRegistry,
|
|
57360
|
+
pipeRegistry,
|
|
57361
|
+
utilRegistry,
|
|
57362
|
+
modelSnapshotFieldRegistry,
|
|
57363
|
+
modelFirebaseIndexRegistry,
|
|
57364
|
+
uiComponentRegistry: uiRegistry,
|
|
57365
|
+
dbxDocsUiExamplesRegistry,
|
|
57366
|
+
actionRegistry,
|
|
57367
|
+
filterRegistry,
|
|
57368
|
+
tokenRegistry,
|
|
57369
|
+
cssUtilityRegistry,
|
|
57370
|
+
fixtureModelRegistry,
|
|
57371
|
+
modelValidateRuleOptions,
|
|
57372
|
+
authRegistry,
|
|
57373
|
+
cwd,
|
|
57374
|
+
logSearchConfig
|
|
57375
|
+
}
|
|
56862
57376
|
};
|
|
56863
57377
|
return context;
|
|
56864
57378
|
}
|
|
@@ -56962,7 +57476,14 @@ async function resolveAuthRegistry(input) {
|
|
|
56962
57476
|
}
|
|
56963
57477
|
function reportAuthLoaderResult(result) {
|
|
56964
57478
|
const { registry, scannedFiles, fileWarnings, extractWarnings, extractedAppCount, extractedClaimCount } = result;
|
|
56965
|
-
const summary = [
|
|
57479
|
+
const summary = [
|
|
57480
|
+
`[dbx-components-mcp] auth registry loaded`,
|
|
57481
|
+
` sources: ${registry.loadedSources.join(", ") || "(none)"}`,
|
|
57482
|
+
` scanned-files: ${scannedFiles.length}`,
|
|
57483
|
+
` apps: ${registry.apps.length} (extracted: ${extractedAppCount})`,
|
|
57484
|
+
` claims: ${registry.claims.length} (extracted: ${extractedClaimCount})`,
|
|
57485
|
+
` warnings: ${fileWarnings.length + extractWarnings.length}`
|
|
57486
|
+
].join("\n");
|
|
56966
57487
|
process.stderr.write(`${summary}
|
|
56967
57488
|
`);
|
|
56968
57489
|
for (const warning of fileWarnings) {
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@dereekb/dbx-components-mcp",
|
|
3
|
-
"version": "14.
|
|
3
|
+
"version": "14.1.0",
|
|
4
4
|
"type": "module",
|
|
5
5
|
"bin": {
|
|
6
6
|
"dbx-components-mcp": "dbx-components-mcp.js"
|
|
@@ -11,7 +11,7 @@
|
|
|
11
11
|
"ts-morph": "^21.0.0"
|
|
12
12
|
},
|
|
13
13
|
"peerDependencies": {
|
|
14
|
-
"@dereekb/dbx-cli": "14.
|
|
15
|
-
"@dereekb/util": "14.
|
|
14
|
+
"@dereekb/dbx-cli": "14.1.0",
|
|
15
|
+
"@dereekb/util": "14.1.0"
|
|
16
16
|
}
|
|
17
17
|
}
|