@dereekb/dbx-components-mcp 14.0.0 → 14.0.1
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 +578 -73
- 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.0.
|
|
13
|
+
version: "14.0.1",
|
|
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.0.
|
|
25
|
-
"@dereekb/util": "14.0.
|
|
24
|
+
"@dereekb/dbx-cli": "14.0.1",
|
|
25
|
+
"@dereekb/util": "14.0.1"
|
|
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
|
}
|
|
@@ -10134,7 +10134,7 @@ function collectFromTypeReference(ref, context) {
|
|
|
10134
10134
|
result = collectFromTypeNode(args[0], { ...context, forceOptional: false }).map((p) => ({ ...p, required: true }));
|
|
10135
10135
|
} else if (refName === "Omit" && args.length === 2) {
|
|
10136
10136
|
const keys = parseKeyUnion(args[1]);
|
|
10137
|
-
const merged = /* @__PURE__ */ new Set([...context.omit, ...keys]);
|
|
10137
|
+
const merged = /* @__PURE__ */ new Set([...Array.from(context.omit), ...Array.from(keys)]);
|
|
10138
10138
|
result = collectFromTypeNode(args[0], { ...context, omit: merged });
|
|
10139
10139
|
} else if (refName === "Pick" && args.length === 2) {
|
|
10140
10140
|
const keys = parseKeyUnion(args[1]);
|
|
@@ -11476,7 +11476,7 @@ function tryTransitiveResolution(input) {
|
|
|
11476
11476
|
const calleeLine = resolved.decl.getStartLineNumber();
|
|
11477
11477
|
warnings.push({ kind: "unannotated-query-helper", severity: "warning", name: factoryName, callee: calleeName, calleeFilePath, calleeLine, filePath, line: call.getStartLineNumber() });
|
|
11478
11478
|
}
|
|
11479
|
-
const nextVisited =
|
|
11479
|
+
const nextVisited = new Set(visited).add(calleeKey);
|
|
11480
11480
|
walkBodyInto({
|
|
11481
11481
|
decl: resolved.decl,
|
|
11482
11482
|
factoryName,
|
|
@@ -15456,13 +15456,21 @@ function formatBuildFailure(outcome) {
|
|
|
15456
15456
|
}
|
|
15457
15457
|
function formatWriteSummary(input) {
|
|
15458
15458
|
const { outputPath, diff, generatedComposites, generatedFieldOverrides } = input;
|
|
15459
|
-
const lines = [
|
|
15459
|
+
const lines = [
|
|
15460
|
+
`generate-firestore-indexes: wrote ${outputPath}`,
|
|
15461
|
+
` composites: ${generatedComposites} (added ${diff.added.length}, removed ${diff.removed.length}, unchanged ${diff.unchanged.length})`,
|
|
15462
|
+
` fieldOverrides: ${generatedFieldOverrides} (added ${diff.fieldOverridesAdded.length}, removed ${diff.fieldOverridesRemoved.length}, unchanged ${diff.fieldOverridesUnchanged.length})`
|
|
15463
|
+
];
|
|
15460
15464
|
return appendDiffSection(lines, diff).join("\n");
|
|
15461
15465
|
}
|
|
15462
15466
|
function formatCheckSummary(input) {
|
|
15463
15467
|
const { drift, outputPath, diff, generatedComposites, generatedFieldOverrides } = input;
|
|
15464
15468
|
const headline = drift ? `generate-firestore-indexes: drift detected against ${outputPath}` : `generate-firestore-indexes: in sync (${outputPath})`;
|
|
15465
|
-
const lines = [
|
|
15469
|
+
const lines = [
|
|
15470
|
+
headline,
|
|
15471
|
+
` composites: ${generatedComposites} (added ${diff.added.length}, removed ${diff.removed.length}, unchanged ${diff.unchanged.length})`,
|
|
15472
|
+
` fieldOverrides: ${generatedFieldOverrides} (added ${diff.fieldOverridesAdded.length}, removed ${diff.fieldOverridesRemoved.length}, unchanged ${diff.fieldOverridesUnchanged.length})`
|
|
15473
|
+
];
|
|
15466
15474
|
return appendDiffSection(lines, diff).join("\n");
|
|
15467
15475
|
}
|
|
15468
15476
|
function appendDiffSection(lines, diff) {
|
|
@@ -19816,7 +19824,7 @@ function registerResources(server, options = {}) {
|
|
|
19816
19824
|
if (options.authRegistry !== void 0) {
|
|
19817
19825
|
registerAuthResource(server, { registry: options.authRegistry });
|
|
19818
19826
|
}
|
|
19819
|
-
registerFirestoreRulesResource(server,
|
|
19827
|
+
registerFirestoreRulesResource(server, options.cwd === void 0 ? {} : { cwd: options.cwd });
|
|
19820
19828
|
}
|
|
19821
19829
|
|
|
19822
19830
|
// packages/dbx-components-mcp/src/tools/lookup-form.tool.ts
|
|
@@ -19909,7 +19917,19 @@ function formatBrief(field) {
|
|
|
19909
19917
|
const tierBits = formatTierBits(field);
|
|
19910
19918
|
const arrayOptional = field.arrayOutput === "optional" ? " (single or array)" : "";
|
|
19911
19919
|
const array = field.arrayOutput === "yes" ? " (array)" : arrayOptional;
|
|
19912
|
-
return [
|
|
19920
|
+
return [
|
|
19921
|
+
`## ${field.factoryName}`,
|
|
19922
|
+
"",
|
|
19923
|
+
`**slug:** \`${field.slug}\` \xB7 **tier:** \`${field.tier}\` \xB7 **produces:** \`${field.produces}\`${array}${tierBits}`,
|
|
19924
|
+
"",
|
|
19925
|
+
field.description,
|
|
19926
|
+
"",
|
|
19927
|
+
"```ts",
|
|
19928
|
+
field.minimalExample,
|
|
19929
|
+
"```",
|
|
19930
|
+
"",
|
|
19931
|
+
`\u2192 Call \`dbx_form_lookup topic="${field.slug}" depth="full"\` for the config table and full example.`
|
|
19932
|
+
].join("\n");
|
|
19913
19933
|
}
|
|
19914
19934
|
function formatFull(field) {
|
|
19915
19935
|
const header = formatHeader(field);
|
|
@@ -20411,7 +20431,19 @@ function formatSearchResults(input) {
|
|
|
20411
20431
|
const field = hit.entry;
|
|
20412
20432
|
const optionalArray = field.arrayOutput === "optional" ? " *(single or array)*" : "";
|
|
20413
20433
|
const array = field.arrayOutput === "yes" ? " *(array)*" : optionalArray;
|
|
20414
|
-
lines.push(
|
|
20434
|
+
lines.push(
|
|
20435
|
+
`## \`${field.slug}\` \xB7 form \xB7 score ${hit.score}`,
|
|
20436
|
+
"",
|
|
20437
|
+
`- **factory:** \`${field.factoryName}\``,
|
|
20438
|
+
`- **tier:** \`${field.tier}\``,
|
|
20439
|
+
`- **produces:** \`${field.produces}\`${array}`,
|
|
20440
|
+
`- **matched:** \`${hit.matchedTokens.join(", ")}\``,
|
|
20441
|
+
"",
|
|
20442
|
+
field.description,
|
|
20443
|
+
"",
|
|
20444
|
+
`\u2192 \`dbx_form_lookup topic="${field.slug}"\` for full docs.`,
|
|
20445
|
+
""
|
|
20446
|
+
);
|
|
20415
20447
|
}
|
|
20416
20448
|
return lines.join("\n").trimEnd();
|
|
20417
20449
|
}
|
|
@@ -21463,7 +21495,19 @@ function formatSearchResults2(options) {
|
|
|
21463
21495
|
} else {
|
|
21464
21496
|
const lines = [`# Search: \`${query}\``, "", `Tokens: \`${tokenDisplay}\`${scopeLabel} \xB7 ${hits.length} result${hits.length === 1 ? "" : "s"}`, ""];
|
|
21465
21497
|
for (const hit of hits) {
|
|
21466
|
-
lines.push(
|
|
21498
|
+
lines.push(
|
|
21499
|
+
`## \`${hit.entry.slug}\` \xB7 ${hit.entry.category} \xB7 score ${hit.score}`,
|
|
21500
|
+
"",
|
|
21501
|
+
`- **class:** \`${hit.entry.className}\``,
|
|
21502
|
+
`- **kind:** \`${hit.entry.kind}\``,
|
|
21503
|
+
`- **selector:** \`${hit.entry.selector}\``,
|
|
21504
|
+
`- **matched:** \`${hit.matchedTokens.join(", ")}\``,
|
|
21505
|
+
"",
|
|
21506
|
+
hit.entry.description,
|
|
21507
|
+
"",
|
|
21508
|
+
`\u2192 \`dbx_ui_lookup topic="${hit.entry.slug}"\` for full docs.`,
|
|
21509
|
+
""
|
|
21510
|
+
);
|
|
21467
21511
|
}
|
|
21468
21512
|
appendRelatedExamples(lines, hits, examplesRegistry);
|
|
21469
21513
|
result = lines.join("\n").trimEnd();
|
|
@@ -25553,7 +25597,31 @@ function attachRemediation(code17) {
|
|
|
25553
25597
|
|
|
25554
25598
|
// packages/dbx-cli/validate/src/lib/_core/rule-catalog/format.ts
|
|
25555
25599
|
function formatRuleAsMarkdown(entry) {
|
|
25556
|
-
const lines = [
|
|
25600
|
+
const lines = [
|
|
25601
|
+
`# \`${entry.code}\``,
|
|
25602
|
+
"",
|
|
25603
|
+
`**${entry.title}**`,
|
|
25604
|
+
"",
|
|
25605
|
+
`- **Severity:** \`${entry.severity}\``,
|
|
25606
|
+
`- **Source tool:** \`${entry.source}\``,
|
|
25607
|
+
"",
|
|
25608
|
+
"## What it flags",
|
|
25609
|
+
"",
|
|
25610
|
+
entry.whatItFlags,
|
|
25611
|
+
"",
|
|
25612
|
+
"## When it applies",
|
|
25613
|
+
"",
|
|
25614
|
+
entry.whenItApplies,
|
|
25615
|
+
"",
|
|
25616
|
+
"## When it does NOT apply",
|
|
25617
|
+
"",
|
|
25618
|
+
entry.whenItDoesNotApply,
|
|
25619
|
+
"",
|
|
25620
|
+
"## Canonical fix",
|
|
25621
|
+
"",
|
|
25622
|
+
entry.canonicalFix,
|
|
25623
|
+
""
|
|
25624
|
+
];
|
|
25557
25625
|
if (entry.canonicalFixTemplate) {
|
|
25558
25626
|
lines.push("## Fix template", "", entry.canonicalFixTemplate, "");
|
|
25559
25627
|
}
|
|
@@ -35894,7 +35962,15 @@ function appendGroupSection(lines, apiRel, group) {
|
|
|
35894
35962
|
function appendFileTable(lines, group) {
|
|
35895
35963
|
lines.push("", "| File | Kind | Subgroups | Canonical | Rename suggestion |", "|---|---|---|---|---|");
|
|
35896
35964
|
for (const file of group.files) {
|
|
35897
|
-
lines.push(
|
|
35965
|
+
lines.push(
|
|
35966
|
+
"| " + [
|
|
35967
|
+
code3(file.filename),
|
|
35968
|
+
KIND_LABEL[file.classification.kind],
|
|
35969
|
+
file.classification.subgroups.length === 0 ? "\u2014" : code3(file.classification.subgroups.join(".")),
|
|
35970
|
+
file.classification.isCanonical ? "\u2713" : "\u2717",
|
|
35971
|
+
file.classification.recommendedRename === void 0 ? "\u2014" : code3(file.classification.recommendedRename)
|
|
35972
|
+
].join(" | ") + " |"
|
|
35973
|
+
);
|
|
35898
35974
|
}
|
|
35899
35975
|
}
|
|
35900
35976
|
function appendWhereToAddSection(lines, apiRel, group) {
|
|
@@ -35910,7 +35986,17 @@ function appendRecommendation(lines, rec, group) {
|
|
|
35910
35986
|
lines.push("", `- **${rec.label}** \u2192 \`${rec.canonicalPath}\` ${status}`, ` ${rec.summary}`);
|
|
35911
35987
|
}
|
|
35912
35988
|
function appendConventionLegend(lines) {
|
|
35913
|
-
lines.push(
|
|
35989
|
+
lines.push(
|
|
35990
|
+
"",
|
|
35991
|
+
"## Naming convention",
|
|
35992
|
+
"",
|
|
35993
|
+
"- `<group>.crud.spec.ts` \u2014 non-scenario CRUD tests.",
|
|
35994
|
+
"- `<group>.crud.<sub>[.<sub>...].spec.ts` \u2014 focused CRUD sub-test.",
|
|
35995
|
+
"- `<group>.scenario.spec.ts` \u2014 generic multi-step scenario tests.",
|
|
35996
|
+
"- `<group>.scenario.<sub>[.<sub>...].spec.ts` \u2014 focused scenario sub-bucket.",
|
|
35997
|
+
"",
|
|
35998
|
+
"Drift forms are still parsed but flagged \u2014 see the `Rename suggestion` column."
|
|
35999
|
+
);
|
|
35914
36000
|
}
|
|
35915
36001
|
function appendDriftSummary(lines, catalog) {
|
|
35916
36002
|
const driftFiles = [];
|
|
@@ -36056,7 +36142,14 @@ function stringify(value) {
|
|
|
36056
36142
|
|
|
36057
36143
|
// packages/dbx-cli/model-test/src/lib/fixture/format.markdown.ts
|
|
36058
36144
|
function formatListAsMarkdown(extraction) {
|
|
36059
|
-
const lines = [
|
|
36145
|
+
const lines = [
|
|
36146
|
+
`# App fixtures \u2014 ${extraction.fixturePath}`,
|
|
36147
|
+
"",
|
|
36148
|
+
`Detected workspace prefix: \`${extraction.prefix ?? "(none)"}\``,
|
|
36149
|
+
`Identity imports: ${extraction.identityImports.length === 0 ? "_None._" : extraction.identityImports.map((s) => "`" + s + "`").join(", ")}`,
|
|
36150
|
+
"",
|
|
36151
|
+
`## Fixtures (${extraction.entries.length})`
|
|
36152
|
+
];
|
|
36060
36153
|
if (extraction.entries.length === 0) {
|
|
36061
36154
|
lines.push("", "_No model fixtures found._");
|
|
36062
36155
|
} else {
|
|
@@ -36113,7 +36206,15 @@ function nonModelDescription(entry) {
|
|
|
36113
36206
|
return "Non-Firestore-model fixture.";
|
|
36114
36207
|
}
|
|
36115
36208
|
function appendLookupTriplet(lines, entry) {
|
|
36116
|
-
lines.push(
|
|
36209
|
+
lines.push(
|
|
36210
|
+
"## Triplet",
|
|
36211
|
+
"",
|
|
36212
|
+
`- Fixture: \`${entry.fixtureClassName}\` (lines ${entry.fixtureLine}-${entry.fixtureEndLine})`,
|
|
36213
|
+
`- Instance: \`${entry.instanceClassName}\` (lines ${entry.instanceLine}-${entry.instanceEndLine})`,
|
|
36214
|
+
`- Params: \`${entry.paramsTypeName}\``,
|
|
36215
|
+
`- Factory: \`${entry.factoryName ?? "\u2014"}\``,
|
|
36216
|
+
`- Singleton: \`${entry.singletonName ?? "\u2014"}\``
|
|
36217
|
+
);
|
|
36117
36218
|
}
|
|
36118
36219
|
function appendLookupGenerics(lines, entry) {
|
|
36119
36220
|
if (entry.fixtureExtendsGenerics.length > 0) {
|
|
@@ -36283,11 +36384,19 @@ function renderParamsBlock(input, paramsTypeName, todos) {
|
|
|
36283
36384
|
}
|
|
36284
36385
|
function renderInstanceBlock(input) {
|
|
36285
36386
|
input.todos.push(`Add instance methods to \`${input.instanceClassName}\`.`);
|
|
36286
|
-
return [
|
|
36387
|
+
return [
|
|
36388
|
+
`export class ${input.instanceClassName}<F extends FirebaseAdminFunctionTestContextInstance = FirebaseAdminFunctionTestContextInstance> extends ModelTestContextInstance<${input.model}, ${input.modelDocumentTypeName}, ${input.prefix}FunctionContextFixtureInstance<F>> {`,
|
|
36389
|
+
" // TODO: implement instance-side methods (these own the real logic).",
|
|
36390
|
+
"}"
|
|
36391
|
+
].join("\n");
|
|
36287
36392
|
}
|
|
36288
36393
|
function renderFixtureBlock(input) {
|
|
36289
36394
|
input.todos.push(`Add forwarder methods to \`${input.fixtureClassName}\` after defining instance methods.`);
|
|
36290
|
-
return [
|
|
36395
|
+
return [
|
|
36396
|
+
`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>> {`,
|
|
36397
|
+
" // TODO: forward instance methods. Run `dbx_model_fixture_forward` after adding instance methods.",
|
|
36398
|
+
"}"
|
|
36399
|
+
].join("\n");
|
|
36291
36400
|
}
|
|
36292
36401
|
function renderFactoryBlock(input) {
|
|
36293
36402
|
const generics = renderFactoryGenerics(input);
|
|
@@ -36307,7 +36416,15 @@ function renderFactoryBlock(input) {
|
|
|
36307
36416
|
return lines.join("\n");
|
|
36308
36417
|
}
|
|
36309
36418
|
function renderFactoryGenerics(input) {
|
|
36310
|
-
const baseGenerics = [
|
|
36419
|
+
const baseGenerics = [
|
|
36420
|
+
input.model,
|
|
36421
|
+
input.modelDocumentTypeName,
|
|
36422
|
+
input.paramsTypeName,
|
|
36423
|
+
`${input.prefix}FunctionContextFixtureInstance<FirebaseAdminFunctionTestContextInstance>`,
|
|
36424
|
+
`${input.prefix}FunctionContextFixture<FirebaseAdminFunctionTestContextInstance>`,
|
|
36425
|
+
`${input.instanceClassName}<FirebaseAdminFunctionTestContextInstance>`,
|
|
36426
|
+
`${input.fixtureClassName}<FirebaseAdminFunctionTestContextInstance>`
|
|
36427
|
+
];
|
|
36311
36428
|
if (input.collectionGenericArg) {
|
|
36312
36429
|
return [...baseGenerics, input.collectionGenericArg];
|
|
36313
36430
|
}
|
|
@@ -36329,10 +36446,13 @@ function renderFactoryCallbacks(input) {
|
|
|
36329
36446
|
} else {
|
|
36330
36447
|
callbacks.push(`getCollection: (fi) => fi.${defaultCollectionsAccessor(input.prefix)}.${camelInitial(input.model)}Collection`);
|
|
36331
36448
|
}
|
|
36332
|
-
callbacks.push(
|
|
36449
|
+
callbacks.push(
|
|
36450
|
+
`makeInstance: (delegate, ref, testInstance) => new ${input.instanceClassName}(delegate, ref, testInstance)`,
|
|
36451
|
+
`makeRef: async (collection, params, _p) => {
|
|
36333
36452
|
// TODO: choose the correct ref strategy (auto id vs deterministic id).
|
|
36334
36453
|
return collection.documentAccessor().newDocument().documentRef;
|
|
36335
|
-
}`
|
|
36454
|
+
}`
|
|
36455
|
+
);
|
|
36336
36456
|
if (input.withInitDocument) {
|
|
36337
36457
|
callbacks.push(`initDocument: async (instance, params) => {
|
|
36338
36458
|
// TODO: seed the new document with sensible defaults from \`params\`.
|
|
@@ -37532,7 +37652,18 @@ function formatReportAsJson4(report) {
|
|
|
37532
37652
|
// packages/dbx-cli/validate/src/lib/dbx-asset-list-app/format.markdown.ts
|
|
37533
37653
|
function formatReportAsMarkdown4(report) {
|
|
37534
37654
|
const basename12 = report.componentDir.split("/").pop() ?? report.componentDir;
|
|
37535
|
-
const lines = [
|
|
37655
|
+
const lines = [
|
|
37656
|
+
`# App assets \u2014 ${basename12}`,
|
|
37657
|
+
"",
|
|
37658
|
+
`Component: \`${report.componentDir}\``,
|
|
37659
|
+
`App: \`${report.apiDir}\``,
|
|
37660
|
+
"",
|
|
37661
|
+
`\`src/lib/assets.ts\`: ${formatBool(report.assetsFileExists)}`,
|
|
37662
|
+
`Barrel re-exports \`./assets\`: ${formatBool(report.barrelReExportsAssets)}`,
|
|
37663
|
+
`\`provideDbxAssetLoader()\` called in \`src/root.app.config.ts\`: ${formatBool(report.providerWiredInApp)}`,
|
|
37664
|
+
"",
|
|
37665
|
+
`## Assets (${report.assets.length})`
|
|
37666
|
+
];
|
|
37536
37667
|
if (report.assets.length === 0) {
|
|
37537
37668
|
lines.push("", "_None found._");
|
|
37538
37669
|
} else {
|
|
@@ -37923,7 +38054,18 @@ function formatReportAsJson6(report) {
|
|
|
37923
38054
|
function formatReportAsMarkdown6(report) {
|
|
37924
38055
|
const basename12 = report.componentDir.split("/").pop() ?? report.componentDir;
|
|
37925
38056
|
const uploadServiceFactoryText = report.uploadServiceFactoryName ? code7(report.uploadServiceFactoryName) : "_Not defined._";
|
|
37926
|
-
const lines = [
|
|
38057
|
+
const lines = [
|
|
38058
|
+
`# App storagefiles \u2014 ${basename12}`,
|
|
38059
|
+
"",
|
|
38060
|
+
`Component: \`${report.componentDir}\``,
|
|
38061
|
+
`API: \`${report.apiDir}\``,
|
|
38062
|
+
"",
|
|
38063
|
+
`Upload service factory: ${uploadServiceFactoryText}`,
|
|
38064
|
+
`Wired via \`StorageFileInitializeFromUploadService\` provider: ${formatBool3(report.uploadServiceWiredInApi)}`,
|
|
38065
|
+
`Processing handler call present: ${formatBool3(report.processingHandlerWiredInApi)}`,
|
|
38066
|
+
"",
|
|
38067
|
+
`## Purposes (${report.purposes.length})`
|
|
38068
|
+
];
|
|
37927
38069
|
if (report.purposes.length === 0) {
|
|
37928
38070
|
lines.push("", "_None found._");
|
|
37929
38071
|
} else {
|
|
@@ -41602,7 +41744,14 @@ function rootDocumentStoreBody(args) {
|
|
|
41602
41744
|
if (args.functionsClass !== void 0) {
|
|
41603
41745
|
packageImports.push(args.functionsClass);
|
|
41604
41746
|
}
|
|
41605
|
-
const lines = [
|
|
41747
|
+
const lines = [
|
|
41748
|
+
`import { Injectable, inject } from '@angular/core';`,
|
|
41749
|
+
`import { ${joinUnique(dbxFirebaseImports)} } from '@dereekb/dbx-firebase';`,
|
|
41750
|
+
`import { ${joinUnique(packageImports)} } from '${args.firebasePackage}';`,
|
|
41751
|
+
"",
|
|
41752
|
+
`@Injectable()`,
|
|
41753
|
+
`export class ${storeClass} extends ${baseClass}<${args.modelName}, ${docType}> {`
|
|
41754
|
+
];
|
|
41606
41755
|
if (args.functionsClass !== void 0) {
|
|
41607
41756
|
lines.push(` readonly ${functionsFieldName(args.functionsClass)} = inject(${args.functionsClass});`, "");
|
|
41608
41757
|
}
|
|
@@ -41645,11 +41794,29 @@ function subDocumentStoreBody(args) {
|
|
|
41645
41794
|
if (args.functionsClass !== void 0) {
|
|
41646
41795
|
packageImports.push(args.functionsClass);
|
|
41647
41796
|
}
|
|
41648
|
-
const lines = [
|
|
41797
|
+
const lines = [
|
|
41798
|
+
`import { Injectable, inject } from '@angular/core';`,
|
|
41799
|
+
`import { ${joinUnique(dbxFirebaseImports)} } from '@dereekb/dbx-firebase';`,
|
|
41800
|
+
`import { ${joinUnique(packageImports)} } from '${args.firebasePackage}';`,
|
|
41801
|
+
`import { ${parentStore} } from './${parentBaseFile}.document.store';`,
|
|
41802
|
+
"",
|
|
41803
|
+
`@Injectable()`,
|
|
41804
|
+
`export class ${storeClass} extends ${baseClass}<${args.modelName}, ${parent.name}, ${docType}, ${parent.documentType}> {`
|
|
41805
|
+
];
|
|
41649
41806
|
if (args.functionsClass !== void 0) {
|
|
41650
41807
|
lines.push(` readonly ${functionsFieldName(args.functionsClass)} = inject(${args.functionsClass});`, "");
|
|
41651
41808
|
}
|
|
41652
|
-
lines.push(
|
|
41809
|
+
lines.push(
|
|
41810
|
+
` constructor() {`,
|
|
41811
|
+
` const collections = inject(${collections});`,
|
|
41812
|
+
` super({ collectionFactory: collections.${args.factoryAccessor}, firestoreCollectionLike: collections.${args.groupAccessor} });`,
|
|
41813
|
+
` const parent = inject(${parentStore}, { optional: true });`,
|
|
41814
|
+
"",
|
|
41815
|
+
` if (parent) {`,
|
|
41816
|
+
` this.setParentStore(parent);`,
|
|
41817
|
+
` }`,
|
|
41818
|
+
` }`
|
|
41819
|
+
);
|
|
41653
41820
|
const crudLines = renderCrudLines(args, "this");
|
|
41654
41821
|
if (crudLines.length > 0) {
|
|
41655
41822
|
lines.push("", ...crudLines);
|
|
@@ -41694,7 +41861,14 @@ function systemStateAccessorBody(args) {
|
|
|
41694
41861
|
if (args.functionsClass !== void 0) {
|
|
41695
41862
|
packageImports.push(args.functionsClass);
|
|
41696
41863
|
}
|
|
41697
|
-
const lines = [
|
|
41864
|
+
const lines = [
|
|
41865
|
+
`import { Injectable, inject } from '@angular/core';`,
|
|
41866
|
+
`import { ${joinUnique(dbxFirebaseImports)} } from '@dereekb/dbx-firebase';`,
|
|
41867
|
+
`import { ${joinUnique(packageImports)} } from '${args.firebasePackage}';`,
|
|
41868
|
+
"",
|
|
41869
|
+
`@Injectable()`,
|
|
41870
|
+
`export class ${accessorClass} extends AbstractSystemStateDocumentStoreAccessor<${dataType}> {`
|
|
41871
|
+
];
|
|
41698
41872
|
if (args.functionsClass !== void 0) {
|
|
41699
41873
|
lines.push(` readonly ${functionsFieldName(args.functionsClass)} = inject(${args.functionsClass});`, "");
|
|
41700
41874
|
}
|
|
@@ -42277,7 +42451,17 @@ var ForwardArgsType = type62({
|
|
|
42277
42451
|
});
|
|
42278
42452
|
var TOOL8 = {
|
|
42279
42453
|
name: "dbx_model_fixture_forward",
|
|
42280
|
-
description: [
|
|
42454
|
+
description: [
|
|
42455
|
+
"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:",
|
|
42456
|
+
"",
|
|
42457
|
+
"```ts",
|
|
42458
|
+
"async <name>(<paramText>): <returnType> {",
|
|
42459
|
+
" return this.instance.<name>(<args>);",
|
|
42460
|
+
"}",
|
|
42461
|
+
"```",
|
|
42462
|
+
"",
|
|
42463
|
+
"Existing forwarders are left untouched (idempotent). Pass `methods` to limit the run to specific method names; omit to forward everything that's missing."
|
|
42464
|
+
].join("\n"),
|
|
42281
42465
|
inputSchema: {
|
|
42282
42466
|
type: "object",
|
|
42283
42467
|
properties: {
|
|
@@ -42756,7 +42940,11 @@ function bucketSummary(group, bucket) {
|
|
|
42756
42940
|
}
|
|
42757
42941
|
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
42942
|
}
|
|
42759
|
-
var DRIFT_RULES = [
|
|
42943
|
+
var DRIFT_RULES = [
|
|
42944
|
+
"`<group>.<sub>.crud.spec.ts` \u2192 rename to `<group>.crud.<sub>.spec.ts` (`crud` belongs directly after the group name).",
|
|
42945
|
+
"`<group>.<sub>.scenario.spec.ts` \u2192 rename to `<group>.scenario.<sub>.spec.ts` (`scenario` belongs directly after the group name).",
|
|
42946
|
+
"`<group>.<rest>.spec.ts` with no `crud` / `scenario` segment \u2192 add one (default to `scenario` when in doubt)."
|
|
42947
|
+
];
|
|
42760
42948
|
function formatMarkdown(report) {
|
|
42761
42949
|
const lines = [];
|
|
42762
42950
|
lines.push(`# Spec convention for \`${report.group}\``, "", `Tests live under \`${report.apiDir}/src/app/function/${report.group}/\`. Canonical name segments:`, "");
|
|
@@ -43536,7 +43724,14 @@ var ARCHETYPE_RECOMMEND_TOOL = {
|
|
|
43536
43724
|
import { type as type70 } from "arktype";
|
|
43537
43725
|
var DBX_MODEL_ARCHETYPE_LOOKUP_TOOL = {
|
|
43538
43726
|
name: "dbx_model_archetype_lookup",
|
|
43539
|
-
description: [
|
|
43727
|
+
description: [
|
|
43728
|
+
"Look up an archetype in the model-archetype catalog. Accepts:",
|
|
43729
|
+
' \u2022 a slug (`"root-entity"`, `"denormalised-aggregate"`, \u2026);',
|
|
43730
|
+
' \u2022 the literal `"list"` for the full catalog grouped by family.',
|
|
43731
|
+
"",
|
|
43732
|
+
"Optional inputs:",
|
|
43733
|
+
' \u2022 `axes`: optional axis filter (`{ "subPurpose": "private" }`) \u2014 when set, the description shifts to focus on the matched axis.'
|
|
43734
|
+
].join("\n"),
|
|
43540
43735
|
inputSchema: {
|
|
43541
43736
|
type: "object",
|
|
43542
43737
|
properties: {
|
|
@@ -44929,7 +45124,18 @@ function formatStateInteraction(entry) {
|
|
|
44929
45124
|
return result;
|
|
44930
45125
|
}
|
|
44931
45126
|
function formatDirectiveEntry(entry, depth) {
|
|
44932
|
-
const lines = [
|
|
45127
|
+
const lines = [
|
|
45128
|
+
`# ${entry.className}`,
|
|
45129
|
+
"",
|
|
45130
|
+
entry.description,
|
|
45131
|
+
"",
|
|
45132
|
+
bullet("selector", `\`${entry.selector}\``),
|
|
45133
|
+
bullet("module", `\`${entry.module}\``),
|
|
45134
|
+
bullet("slug", `\`${entry.slug}\``),
|
|
45135
|
+
bullet("produces context", entry.producesContext ? "yes (provides `ActionContextStore` via DI)" : "no"),
|
|
45136
|
+
bullet("consumes context", entry.consumesContext ? "yes (injects `DbxActionContextStoreSourceInstance`)" : "no"),
|
|
45137
|
+
""
|
|
45138
|
+
];
|
|
44933
45139
|
if (depth === "full") {
|
|
44934
45140
|
lines.push("## Inputs", "", entry.inputs.length > 0 ? formatInputsTable2(entry.inputs) : "_No inputs._", "");
|
|
44935
45141
|
if (entry.outputs.length > 0) {
|
|
@@ -45692,7 +45898,15 @@ export class TagEditorComponent {
|
|
|
45692
45898
|
};
|
|
45693
45899
|
|
|
45694
45900
|
// packages/dbx-components-mcp/src/tools/data/patterns/action-patterns.ts
|
|
45695
|
-
var ACTION_EXAMPLE_PATTERNS = [
|
|
45901
|
+
var ACTION_EXAMPLE_PATTERNS = [
|
|
45902
|
+
ACTION_PATTERN_BUTTON_CONFIRM_DELETE,
|
|
45903
|
+
ACTION_PATTERN_FORM_SUBMIT,
|
|
45904
|
+
ACTION_PATTERN_FORM_WITH_SECONDARY,
|
|
45905
|
+
ACTION_PATTERN_AUTO_TRIGGER_ON_MODIFY,
|
|
45906
|
+
ACTION_PATTERN_DISABLED_BY_KEY,
|
|
45907
|
+
ACTION_PATTERN_VALUE_GETTER_ON_TRIGGER,
|
|
45908
|
+
ACTION_PATTERN_PROVIDE_CONTEXT_UP
|
|
45909
|
+
];
|
|
45696
45910
|
function getActionExamplePattern(slug) {
|
|
45697
45911
|
const lowered = slug.trim().toLowerCase();
|
|
45698
45912
|
return ACTION_EXAMPLE_PATTERNS.find((p) => p.slug === lowered);
|
|
@@ -46016,7 +46230,27 @@ function renderScaffold3(args) {
|
|
|
46016
46230
|
const directivesUsed = buildDirectivesUsed(args);
|
|
46017
46231
|
const examplePattern = relatedExamplePattern(args);
|
|
46018
46232
|
const lines = [];
|
|
46019
|
-
lines.push(
|
|
46233
|
+
lines.push(
|
|
46234
|
+
`# Action scaffold \u2014 ${args.useCase}`,
|
|
46235
|
+
"",
|
|
46236
|
+
`Trigger: \`${args.trigger}\` \xB7 Confirm: \`${args.confirm}\` \xB7 Success feedback: \`${args.successFeedback}\` \xB7 Context: \`${args.contextProvider}\``,
|
|
46237
|
+
"",
|
|
46238
|
+
"## Template",
|
|
46239
|
+
"",
|
|
46240
|
+
"```html",
|
|
46241
|
+
template,
|
|
46242
|
+
"```",
|
|
46243
|
+
"",
|
|
46244
|
+
"## Component class",
|
|
46245
|
+
"",
|
|
46246
|
+
"```ts",
|
|
46247
|
+
handler,
|
|
46248
|
+
"```",
|
|
46249
|
+
"",
|
|
46250
|
+
"## Imports",
|
|
46251
|
+
"",
|
|
46252
|
+
"```ts"
|
|
46253
|
+
);
|
|
46020
46254
|
for (const line of imports) {
|
|
46021
46255
|
lines.push(line);
|
|
46022
46256
|
}
|
|
@@ -46460,7 +46694,13 @@ function formatMissingRouteModelLine(param) {
|
|
|
46460
46694
|
// packages/dbx-components-mcp/src/tools/route-lookup.tool.ts
|
|
46461
46695
|
var DBX_ROUTE_LOOKUP_TOOL = {
|
|
46462
46696
|
name: "dbx_route_lookup",
|
|
46463
|
-
description: [
|
|
46697
|
+
description: [
|
|
46698
|
+
"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.",
|
|
46699
|
+
"",
|
|
46700
|
+
"Provide at least one of `sources` / `paths` / `glob` to point the tool at the source set, plus a `topic`.",
|
|
46701
|
+
"",
|
|
46702
|
+
"Resolution order: exact state name \u2192 URL path \u2192 component class name \u2192 fuzzy substring (returns up to 5 candidates)."
|
|
46703
|
+
].join("\n"),
|
|
46464
46704
|
inputSchema: {
|
|
46465
46705
|
type: "object",
|
|
46466
46706
|
properties: {
|
|
@@ -46720,7 +46960,11 @@ var ROUTE_LOOKUP_TOOL = {
|
|
|
46720
46960
|
import { type as type82 } from "arktype";
|
|
46721
46961
|
var DBX_ROUTE_SEARCH_TOOL = {
|
|
46722
46962
|
name: "dbx_route_search",
|
|
46723
|
-
description: [
|
|
46963
|
+
description: [
|
|
46964
|
+
"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.",
|
|
46965
|
+
"",
|
|
46966
|
+
"Provide at least one of `sources` / `paths` / `glob` plus a `query`. `scope` narrows to a single axis (`name`, `url`, `component`, `resolve`)."
|
|
46967
|
+
].join("\n"),
|
|
46724
46968
|
inputSchema: {
|
|
46725
46969
|
type: "object",
|
|
46726
46970
|
properties: {
|
|
@@ -47812,7 +48056,25 @@ function formatScaffold2(args) {
|
|
|
47812
48056
|
if (args.presetKeys.length > 0) {
|
|
47813
48057
|
lines.push("## Presets", "", "```ts", renderPresetConstants(args), "```", "");
|
|
47814
48058
|
}
|
|
47815
|
-
lines.push(
|
|
48059
|
+
lines.push(
|
|
48060
|
+
"## Template",
|
|
48061
|
+
"",
|
|
48062
|
+
"```html",
|
|
48063
|
+
renderTemplate(args),
|
|
48064
|
+
"```",
|
|
48065
|
+
"",
|
|
48066
|
+
"## Component class",
|
|
48067
|
+
"",
|
|
48068
|
+
"```ts",
|
|
48069
|
+
...renderImports(args),
|
|
48070
|
+
"",
|
|
48071
|
+
renderClass(args),
|
|
48072
|
+
"```",
|
|
48073
|
+
"",
|
|
48074
|
+
"## Notes",
|
|
48075
|
+
"",
|
|
48076
|
+
"- The outer `[dbxFilterSourceConnector]` provides both `FilterSource` and `FilterSourceConnector`. Children that own the form use `[dbxFilterSource]` + `[dbxFilterConnectSource]` to wire up."
|
|
48077
|
+
);
|
|
47816
48078
|
if (args.presetKeys.length > 0) {
|
|
47817
48079
|
lines.push("- Each `ClickableFilterPreset` listed in the preset array becomes a chip; clicking sets `presetValue` on the filter source.");
|
|
47818
48080
|
}
|
|
@@ -47879,7 +48141,20 @@ function formatArgsTable(args) {
|
|
|
47879
48141
|
return lines.join("\n");
|
|
47880
48142
|
}
|
|
47881
48143
|
function formatEntry4(entry, depth) {
|
|
47882
|
-
const lines = [
|
|
48144
|
+
const lines = [
|
|
48145
|
+
`# ${entry.className}`,
|
|
48146
|
+
"",
|
|
48147
|
+
entry.description,
|
|
48148
|
+
"",
|
|
48149
|
+
bullet3("slug", `\`${entry.slug}\``),
|
|
48150
|
+
bullet3("pipe", `\`${entry.pipeName}\``),
|
|
48151
|
+
bullet3("category", `\`${entry.category}\``),
|
|
48152
|
+
bullet3("purity", `\`${entry.purity}\``),
|
|
48153
|
+
bullet3("input", `\`${entry.inputType}\``),
|
|
48154
|
+
bullet3("output", `\`${entry.outputType}\``),
|
|
48155
|
+
bullet3("module", `\`${entry.module}\``),
|
|
48156
|
+
""
|
|
48157
|
+
];
|
|
47883
48158
|
if (depth === "full") {
|
|
47884
48159
|
if (entry.args.length > 0) {
|
|
47885
48160
|
lines.push("## Args", "", formatArgsTable(entry.args), "");
|
|
@@ -48041,7 +48316,20 @@ function formatSearchResults5(input) {
|
|
|
48041
48316
|
const lines = [`# Search: \`${query}\``, "", `Tokens: \`${tokenDisplay}\` \xB7 ${hits.length} result${hits.length === 1 ? "" : "s"}`, ""];
|
|
48042
48317
|
for (const hit of hits) {
|
|
48043
48318
|
const entry = hit.entry;
|
|
48044
|
-
lines.push(
|
|
48319
|
+
lines.push(
|
|
48320
|
+
`## \`${entry.slug}\` \xB7 ${entry.category} \xB7 score ${hit.score}`,
|
|
48321
|
+
"",
|
|
48322
|
+
`- **pipe:** \`${entry.pipeName}\``,
|
|
48323
|
+
`- **class:** \`${entry.className}\``,
|
|
48324
|
+
`- **input:** \`${entry.inputType}\``,
|
|
48325
|
+
`- **output:** \`${entry.outputType}\``,
|
|
48326
|
+
`- **matched:** \`${hit.matchedTokens.join(", ")}\``,
|
|
48327
|
+
"",
|
|
48328
|
+
entry.description,
|
|
48329
|
+
"",
|
|
48330
|
+
`\u2192 \`dbx_pipe_lookup topic="${entry.slug}"\` for full docs.`,
|
|
48331
|
+
""
|
|
48332
|
+
);
|
|
48045
48333
|
}
|
|
48046
48334
|
return lines.join("\n").trimEnd();
|
|
48047
48335
|
}
|
|
@@ -48317,7 +48605,20 @@ function formatCatalogHit(input) {
|
|
|
48317
48605
|
const tagDisplay = entry.tags.length > 0 ? `
|
|
48318
48606
|
- **tags:** ${tagBadges}` : "";
|
|
48319
48607
|
const badge = options.headerBadge ?? "";
|
|
48320
|
-
return [
|
|
48608
|
+
return [
|
|
48609
|
+
`## \`${entry.slug}\` \xB7 \`${entry.name}\` \xB7 ${entry.kind}${badge} \xB7 score ${hit.score}`,
|
|
48610
|
+
"",
|
|
48611
|
+
`- **module:** \`${entry.module}\``,
|
|
48612
|
+
`- **category:** \`${entry.category}\``,
|
|
48613
|
+
`- **subpath:** \`${entry.subpath}\``,
|
|
48614
|
+
`- **signature:** \`${entry.signature}\``,
|
|
48615
|
+
`- **matched:** \`${hit.matchedTokens.join(", ")}\`${tagDisplay}`,
|
|
48616
|
+
"",
|
|
48617
|
+
entry.description.split("\n")[0],
|
|
48618
|
+
"",
|
|
48619
|
+
`\u2192 \`${options.lookupTool} topic="${entry.slug}"\` for full docs.`,
|
|
48620
|
+
""
|
|
48621
|
+
];
|
|
48321
48622
|
}
|
|
48322
48623
|
function formatCatalogSearchResults(input) {
|
|
48323
48624
|
const { query, tokens, hits, filters, entityLabel, emptyHint, hitOptions } = input;
|
|
@@ -48493,7 +48794,20 @@ function appendFullEntrySections2(lines, entry) {
|
|
|
48493
48794
|
}
|
|
48494
48795
|
function formatEntry6(entry, depth) {
|
|
48495
48796
|
const heading = `# ${entry.name}`;
|
|
48496
|
-
const lines = [
|
|
48797
|
+
const lines = [
|
|
48798
|
+
heading,
|
|
48799
|
+
"",
|
|
48800
|
+
entry.description,
|
|
48801
|
+
"",
|
|
48802
|
+
bullet5("slug", `\`${entry.slug}\``),
|
|
48803
|
+
bullet5("kind", `\`${entry.kind}\``),
|
|
48804
|
+
bullet5("category", `\`${entry.category}\``),
|
|
48805
|
+
bullet5("module", `\`${entry.module}\``),
|
|
48806
|
+
bullet5("subpath", `\`${entry.subpath}\``),
|
|
48807
|
+
bullet5("optional", entry.optional ? "yes (use this for `Maybe<T>` fields)" : "no (required)"),
|
|
48808
|
+
bullet5("signature", `\`${entry.signature}\``),
|
|
48809
|
+
""
|
|
48810
|
+
];
|
|
48497
48811
|
appendDeprecationNotice2(lines, entry);
|
|
48498
48812
|
if (depth === "full") {
|
|
48499
48813
|
appendFullEntrySections2(lines, entry);
|
|
@@ -49823,7 +50137,14 @@ function appendTaggedSections(lines, tagged) {
|
|
|
49823
50137
|
}
|
|
49824
50138
|
function appendUntaggedSection(lines, untagged) {
|
|
49825
50139
|
if (untagged.length === 0) return;
|
|
49826
|
-
lines.push(
|
|
50140
|
+
lines.push(
|
|
50141
|
+
"## Untagged candidates",
|
|
50142
|
+
"",
|
|
50143
|
+
"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.",
|
|
50144
|
+
"",
|
|
50145
|
+
"| Function | File |",
|
|
50146
|
+
"| --- | --- |"
|
|
50147
|
+
);
|
|
49827
50148
|
for (const candidate of untagged) {
|
|
49828
50149
|
lines.push(`| \`${candidate.name}\` | \`${candidate.subpath}:${candidate.line}\` |`);
|
|
49829
50150
|
}
|
|
@@ -50523,7 +50844,14 @@ function formatReportAsMarkdown17(report) {
|
|
|
50523
50844
|
} else {
|
|
50524
50845
|
lines.push("## \u2705 In sync", "");
|
|
50525
50846
|
}
|
|
50526
|
-
lines.push(
|
|
50847
|
+
lines.push(
|
|
50848
|
+
`- generated composites: ${report.generatedComposites}`,
|
|
50849
|
+
`- existing composites: ${report.existingComposites}`,
|
|
50850
|
+
`- generated fieldOverrides: ${report.generatedFieldOverrides}`,
|
|
50851
|
+
`- existing fieldOverrides: ${report.existingFieldOverrides}`,
|
|
50852
|
+
`- ${report.errorCount} error(s), ${report.warningCount} warning(s)`,
|
|
50853
|
+
""
|
|
50854
|
+
);
|
|
50527
50855
|
if (report.violations.length > 0) {
|
|
50528
50856
|
appendViolationSection(
|
|
50529
50857
|
lines,
|
|
@@ -50941,7 +51269,10 @@ function renderStorageFilePurpose(input, ctx) {
|
|
|
50941
51269
|
tokens: ctx.tokens,
|
|
50942
51270
|
files: [componentFile, handlerFile],
|
|
50943
51271
|
wiring,
|
|
50944
|
-
summary: applyTokens(
|
|
51272
|
+
summary: applyTokens(
|
|
51273
|
+
`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.`,
|
|
51274
|
+
ctx
|
|
51275
|
+
)
|
|
50945
51276
|
};
|
|
50946
51277
|
return result;
|
|
50947
51278
|
}
|
|
@@ -50949,19 +51280,28 @@ function renderNotificationTemplate(input, ctx) {
|
|
|
50949
51280
|
const componentFile = {
|
|
50950
51281
|
status: "append",
|
|
50951
51282
|
path: applyTokens("<<componentDir>>/src/lib/model/notification/notification.ts", ctx),
|
|
50952
|
-
description: applyTokens(
|
|
51283
|
+
description: applyTokens(
|
|
51284
|
+
`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.`,
|
|
51285
|
+
ctx
|
|
51286
|
+
),
|
|
50953
51287
|
content: applyTokens(NOTIFICATION_TEMPLATE_COMPONENT_TEMPLATE, ctx)
|
|
50954
51288
|
};
|
|
50955
51289
|
const factoryFile = {
|
|
50956
51290
|
status: "append",
|
|
50957
51291
|
path: applyTokens("<<apiDir>>/src/app/common/model/notification/notification.factory.ts", ctx),
|
|
50958
|
-
description: applyTokens(
|
|
51292
|
+
description: applyTokens(
|
|
51293
|
+
`Append the \`<<appCamel>><<Pascal>>NotificationFactory\` to \`notification.factory.ts\`. Reuses the file's existing imports for \`NotificationMessageFunctionFactoryConfig\`, \`NotificationMessageInputContext\`, \`NotificationMessageContent\`, \`NotificationMessage\`, \`notificationMessageFunction\`, and \`NotificationTemplateServiceTypeConfig\`.`,
|
|
51294
|
+
ctx
|
|
51295
|
+
),
|
|
50959
51296
|
content: applyTokens(NOTIFICATION_TEMPLATE_FACTORY_TEMPLATE, ctx)
|
|
50960
51297
|
};
|
|
50961
51298
|
const wiring = [
|
|
50962
51299
|
{
|
|
50963
51300
|
file: applyTokens("<<apiDir>>/src/app/common/model/notification/notification.factory.ts", ctx),
|
|
50964
|
-
description: applyTokens(
|
|
51301
|
+
description: applyTokens(
|
|
51302
|
+
`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.`,
|
|
51303
|
+
ctx
|
|
51304
|
+
),
|
|
50965
51305
|
snippet: applyTokens(NOTIFICATION_TEMPLATE_WIRING_SNIPPET, ctx)
|
|
50966
51306
|
}
|
|
50967
51307
|
];
|
|
@@ -50970,7 +51310,10 @@ function renderNotificationTemplate(input, ctx) {
|
|
|
50970
51310
|
tokens: ctx.tokens,
|
|
50971
51311
|
files: [componentFile, factoryFile],
|
|
50972
51312
|
wiring,
|
|
50973
|
-
summary: applyTokens(
|
|
51313
|
+
summary: applyTokens(
|
|
51314
|
+
`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.`,
|
|
51315
|
+
ctx
|
|
51316
|
+
)
|
|
50974
51317
|
};
|
|
50975
51318
|
return result;
|
|
50976
51319
|
}
|
|
@@ -50995,7 +51338,10 @@ function renderNotificationTask(input, ctx) {
|
|
|
50995
51338
|
const wiring = [
|
|
50996
51339
|
{
|
|
50997
51340
|
file: applyTokens("<<apiDir>>/src/app/common/model/notification/notification.task.service.ts", ctx),
|
|
50998
|
-
description: applyTokens(
|
|
51341
|
+
description: applyTokens(
|
|
51342
|
+
`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.`,
|
|
51343
|
+
ctx
|
|
51344
|
+
),
|
|
50999
51345
|
snippet: applyTokens(NOTIFICATION_TASK_WIRING_SNIPPET, ctx)
|
|
51000
51346
|
}
|
|
51001
51347
|
];
|
|
@@ -51004,7 +51350,10 @@ function renderNotificationTask(input, ctx) {
|
|
|
51004
51350
|
tokens: ctx.tokens,
|
|
51005
51351
|
files: [componentFile, handlerFile],
|
|
51006
51352
|
wiring,
|
|
51007
|
-
summary: applyTokens(
|
|
51353
|
+
summary: applyTokens(
|
|
51354
|
+
`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.`,
|
|
51355
|
+
ctx
|
|
51356
|
+
)
|
|
51008
51357
|
};
|
|
51009
51358
|
return result;
|
|
51010
51359
|
}
|
|
@@ -51269,7 +51618,11 @@ var STORAGEFILE_PURPOSE = {
|
|
|
51269
51618
|
heading: "API upload handler",
|
|
51270
51619
|
path: "<apiDir>/src/app/common/model/storagefile/handlers/upload.<name>.ts",
|
|
51271
51620
|
altPaths: ["Inline in `<apiDir>/src/app/common/model/storagefile/storagefile.upload.service.ts` for tiny workspaces."],
|
|
51272
|
-
body: [
|
|
51621
|
+
body: [
|
|
51622
|
+
"**Required export:** `<camelName>StorageFileUploadInitializers(context): StorageFileInitializeFromUploadServiceInitializer[]`.",
|
|
51623
|
+
"",
|
|
51624
|
+
"**Wire into:** `<apiDir>/src/app/common/model/storagefile/storagefile.upload.service.ts` \u2014 spread into the `initializer: [...]` array passed to `storageFileInitializeFromUploadService({ initializer })`."
|
|
51625
|
+
].join("\n")
|
|
51273
51626
|
},
|
|
51274
51627
|
{
|
|
51275
51628
|
heading: "API processor (only if subtasks declared)",
|
|
@@ -51331,12 +51684,27 @@ var STORAGEFILE_PROCESSOR = {
|
|
|
51331
51684
|
{
|
|
51332
51685
|
heading: "API processor handler",
|
|
51333
51686
|
path: "<apiDir>/src/app/common/model/notification/handlers/storagefile/task.handler.storagefile.<name>.ts",
|
|
51334
|
-
body: [
|
|
51687
|
+
body: [
|
|
51688
|
+
"**Required shape:**",
|
|
51689
|
+
"```ts",
|
|
51690
|
+
"export function <camelName>ProcessingNotificationTaskHandler(context): StorageFileProcessingPurposeSubtaskProcessorConfig<<Name>SubtaskMetadata, <Name>ProcessingSubtask> {",
|
|
51691
|
+
" const config: StorageFileProcessingPurposeSubtaskProcessorConfig<...> = {",
|
|
51692
|
+
" target: <NAME>_PURPOSE,",
|
|
51693
|
+
" flow: [",
|
|
51694
|
+
" { subtask: <NAME>_PURPOSE_<DESC>_SUBTASK, fn: async (input) => ... }",
|
|
51695
|
+
" ]",
|
|
51696
|
+
" };",
|
|
51697
|
+
" return config;",
|
|
51698
|
+
"}",
|
|
51699
|
+
"```"
|
|
51700
|
+
].join("\n")
|
|
51335
51701
|
},
|
|
51336
51702
|
{
|
|
51337
51703
|
heading: "Wire into processing handler",
|
|
51338
51704
|
path: "<apiDir>/src/app/common/model/notification/handlers/task.handler.storagefile.ts",
|
|
51339
|
-
body: [
|
|
51705
|
+
body: [
|
|
51706
|
+
"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."
|
|
51707
|
+
].join("\n")
|
|
51340
51708
|
}
|
|
51341
51709
|
],
|
|
51342
51710
|
seeAlso: ["storagefile-purpose", "storagefile-processor-subtask", "notification-task"],
|
|
@@ -51350,7 +51718,12 @@ var STORAGEFILE_PROCESSOR_SUBTASK = {
|
|
|
51350
51718
|
{
|
|
51351
51719
|
heading: "Component declarations",
|
|
51352
51720
|
path: "<componentDir>/src/lib/model/storagefile/storagefile.<name>.ts (the file that already declares the purpose)",
|
|
51353
|
-
body: [
|
|
51721
|
+
body: [
|
|
51722
|
+
"**Add:**",
|
|
51723
|
+
"- `<NAME>_PURPOSE_<DESC>_SUBTASK: StorageFileProcessingSubtask = '<descriptor>'` \u2014 the new subtask constant.",
|
|
51724
|
+
"- Extend the `<Name>ProcessingSubtask` union alias: `typeof <NAME>_PURPOSE_<DESC>_SUBTASK | ...existing`.",
|
|
51725
|
+
"- Optional: extend `<Name>ProcessingSubtaskMetadata` with any new fields the new subtask needs."
|
|
51726
|
+
].join("\n")
|
|
51354
51727
|
},
|
|
51355
51728
|
{
|
|
51356
51729
|
heading: "API processor flow",
|
|
@@ -51382,13 +51755,19 @@ var NOTIFICATION_TEMPLATE = {
|
|
|
51382
51755
|
{
|
|
51383
51756
|
heading: "Component aggregator",
|
|
51384
51757
|
path: "<componentDir>/src/lib/model/notification/notification.ts",
|
|
51385
|
-
body: [
|
|
51758
|
+
body: [
|
|
51759
|
+
"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`."
|
|
51760
|
+
].join("\n")
|
|
51386
51761
|
},
|
|
51387
51762
|
{
|
|
51388
51763
|
heading: "API handler factory",
|
|
51389
51764
|
path: "<apiDir>/src/app/common/model/notification/notification.factory.ts",
|
|
51390
51765
|
altPaths: ["<apiDir>/src/app/common/model/notification/notification.factory.<name>.ts (split per-template for larger workspaces)"],
|
|
51391
|
-
body: [
|
|
51766
|
+
body: [
|
|
51767
|
+
"**Required export:** A factory function returning a `NotificationTemplateServiceTypeConfig` with `{ type: <NAME>_NOTIFICATION_TEMPLATE_TYPE, factory: async (...) => ... }`.",
|
|
51768
|
+
"",
|
|
51769
|
+
"**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."
|
|
51770
|
+
].join("\n")
|
|
51392
51771
|
}
|
|
51393
51772
|
],
|
|
51394
51773
|
seeAlso: ["notification-task"],
|
|
@@ -51417,7 +51796,17 @@ var NOTIFICATION_TASK = {
|
|
|
51417
51796
|
heading: "API handler config",
|
|
51418
51797
|
path: "<apiDir>/src/app/common/model/notification/notification.task.service.ts",
|
|
51419
51798
|
altPaths: ["<apiDir>/src/app/common/model/notification/handlers/task.handler.<name>.ts (per-task split for larger workspaces)"],
|
|
51420
|
-
body: [
|
|
51799
|
+
body: [
|
|
51800
|
+
"**Required shape:**",
|
|
51801
|
+
"```ts",
|
|
51802
|
+
"const <camelName>NotificationTaskHandler: NotificationTaskServiceTaskHandlerConfig<<Name>NotificationTaskData, <Name>NotificationTaskCheckpoint> = {",
|
|
51803
|
+
" type: <NAME>_NOTIFICATION_TASK_TYPE,",
|
|
51804
|
+
" flow: [",
|
|
51805
|
+
" { checkpoint: 'part_a', fn: async (notificationTask) => ... }",
|
|
51806
|
+
" ]",
|
|
51807
|
+
"};",
|
|
51808
|
+
"```"
|
|
51809
|
+
].join("\n")
|
|
51421
51810
|
},
|
|
51422
51811
|
{
|
|
51423
51812
|
heading: "Wire into task service",
|
|
@@ -52340,7 +52729,12 @@ function formatFirestoreRulesScanReportAsMarkdown(report) {
|
|
|
52340
52729
|
}
|
|
52341
52730
|
lines.push("");
|
|
52342
52731
|
if (report.serverOnly.length > 0) {
|
|
52343
|
-
lines.push(
|
|
52732
|
+
lines.push(
|
|
52733
|
+
`**Server-only (${report.serverOnly.length}):** ${report.serverOnly.map((c) => `\`${c}\``).join(", ")}`,
|
|
52734
|
+
"",
|
|
52735
|
+
"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`.",
|
|
52736
|
+
""
|
|
52737
|
+
);
|
|
52344
52738
|
}
|
|
52345
52739
|
if (report.gettableNotListable.length > 0) {
|
|
52346
52740
|
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 +52972,15 @@ function validateModelServerOnly(input) {
|
|
|
52578
52972
|
}
|
|
52579
52973
|
for (const iface of input.interfaces) {
|
|
52580
52974
|
if (iface.serverOnly && !iface.hasModelTag) {
|
|
52581
|
-
violations.push(
|
|
52975
|
+
violations.push(
|
|
52976
|
+
buildViolation({
|
|
52977
|
+
code: "MODEL_SERVER_ONLY_TAG_WITHOUT_MODEL_TAG" /* MODEL_SERVER_ONLY_TAG_WITHOUT_MODEL_TAG */,
|
|
52978
|
+
severity: "warning",
|
|
52979
|
+
modelType: void 0,
|
|
52980
|
+
file: iface.file,
|
|
52981
|
+
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.`
|
|
52982
|
+
})
|
|
52983
|
+
);
|
|
52582
52984
|
}
|
|
52583
52985
|
}
|
|
52584
52986
|
const errorCount = violations.filter((v) => v.severity === "error").length;
|
|
@@ -52600,22 +53002,78 @@ function appendServiceViolations(input) {
|
|
|
52600
53002
|
const { modelType, collection, tag, flag, rules } = reconciliation;
|
|
52601
53003
|
const declared = tag === true || flag;
|
|
52602
53004
|
if (collection == null) {
|
|
52603
|
-
violations.push(
|
|
53005
|
+
violations.push(
|
|
53006
|
+
buildViolation({
|
|
53007
|
+
code: "MODEL_SERVER_ONLY_UNRESOLVED_IDENTITY" /* MODEL_SERVER_ONLY_UNRESOLVED_IDENTITY */,
|
|
53008
|
+
severity: "warning",
|
|
53009
|
+
modelType,
|
|
53010
|
+
file: service.exportName,
|
|
53011
|
+
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.`
|
|
53012
|
+
})
|
|
53013
|
+
);
|
|
52604
53014
|
} else if (rules === true && !flag) {
|
|
52605
|
-
violations.push(
|
|
53015
|
+
violations.push(
|
|
53016
|
+
buildViolation({
|
|
53017
|
+
code: "MODEL_SERVER_ONLY_MISSING_RUNTIME_FLAG" /* MODEL_SERVER_ONLY_MISSING_RUNTIME_FLAG */,
|
|
53018
|
+
severity: "error",
|
|
53019
|
+
modelType,
|
|
53020
|
+
file: service.exportName,
|
|
53021
|
+
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.`
|
|
53022
|
+
})
|
|
53023
|
+
);
|
|
52606
53024
|
} else if (rules === false && declared) {
|
|
52607
|
-
violations.push(
|
|
53025
|
+
violations.push(
|
|
53026
|
+
buildViolation({
|
|
53027
|
+
code: "MODEL_SERVER_ONLY_RULES_ALLOW_READ" /* MODEL_SERVER_ONLY_RULES_ALLOW_READ */,
|
|
53028
|
+
severity: "error",
|
|
53029
|
+
modelType,
|
|
53030
|
+
file: service.exportName,
|
|
53031
|
+
message: `\`${modelType}\` declares server-only (${describeDeclarations(tag, flag)}) but \`${collection}\` IS client-readable in the rules (get=${reconciliation.rulesGet}, list=${reconciliation.rulesList}).`
|
|
53032
|
+
})
|
|
53033
|
+
);
|
|
52608
53034
|
}
|
|
52609
53035
|
if (rules === true && iface != null && !iface.serverOnly) {
|
|
52610
|
-
violations.push(
|
|
53036
|
+
violations.push(
|
|
53037
|
+
buildViolation({
|
|
53038
|
+
code: "MODEL_SERVER_ONLY_MISSING_TAG" /* MODEL_SERVER_ONLY_MISSING_TAG */,
|
|
53039
|
+
severity: "warning",
|
|
53040
|
+
modelType,
|
|
53041
|
+
file: iface.file,
|
|
53042
|
+
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.`
|
|
53043
|
+
})
|
|
53044
|
+
);
|
|
52611
53045
|
}
|
|
52612
53046
|
if (iface == null && flag) {
|
|
52613
|
-
violations.push(
|
|
53047
|
+
violations.push(
|
|
53048
|
+
buildViolation({
|
|
53049
|
+
code: "MODEL_SERVER_ONLY_NO_INTERFACE" /* MODEL_SERVER_ONLY_NO_INTERFACE */,
|
|
53050
|
+
severity: "warning",
|
|
53051
|
+
modelType,
|
|
53052
|
+
file: service.exportName,
|
|
53053
|
+
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\`.`
|
|
53054
|
+
})
|
|
53055
|
+
);
|
|
52614
53056
|
} else if (iface != null && tag !== flag) {
|
|
52615
|
-
violations.push(
|
|
53057
|
+
violations.push(
|
|
53058
|
+
buildViolation({
|
|
53059
|
+
code: "MODEL_SERVER_ONLY_TAG_FLAG_MISMATCH" /* MODEL_SERVER_ONLY_TAG_FLAG_MISMATCH */,
|
|
53060
|
+
severity: "error",
|
|
53061
|
+
modelType,
|
|
53062
|
+
file: iface.file,
|
|
53063
|
+
message: `\`${modelType}\`: interface \`${iface.name}\` ${tag === true ? "carries" : "does NOT carry"} \`@dbxModelServerOnly\` while \`${service.exportName}\` ${flag ? "DOES" : "does not"} set \`serverOnly: true\`.`
|
|
53064
|
+
})
|
|
53065
|
+
);
|
|
52616
53066
|
}
|
|
52617
53067
|
if (manifestModelTypes != null && declared && !manifestModelTypes.has(modelType)) {
|
|
52618
|
-
violations.push(
|
|
53068
|
+
violations.push(
|
|
53069
|
+
buildViolation({
|
|
53070
|
+
code: "MODEL_SERVER_ONLY_NOT_IN_MANIFEST" /* MODEL_SERVER_ONLY_NOT_IN_MANIFEST */,
|
|
53071
|
+
severity: "warning",
|
|
53072
|
+
modelType,
|
|
53073
|
+
file: manifestFile,
|
|
53074
|
+
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.`
|
|
53075
|
+
})
|
|
53076
|
+
);
|
|
52619
53077
|
}
|
|
52620
53078
|
}
|
|
52621
53079
|
function legsAgree(input) {
|
|
@@ -52724,7 +53182,6 @@ async function walkTypeScriptFiles(dir) {
|
|
|
52724
53182
|
try {
|
|
52725
53183
|
entries = (await readdir12(dir)).sort();
|
|
52726
53184
|
} catch {
|
|
52727
|
-
return out;
|
|
52728
53185
|
}
|
|
52729
53186
|
for (const entry of entries) {
|
|
52730
53187
|
if (entry === "node_modules" || entry === "dist") continue;
|
|
@@ -55739,7 +56196,13 @@ async function tryReadBatchPath(path, cwd, reader) {
|
|
|
55739
56196
|
import { type as type104 } from "arktype";
|
|
55740
56197
|
var DBX_AUTH_CLAIM_LOOKUP_TOOL = {
|
|
55741
56198
|
name: "dbx_auth_claim_lookup",
|
|
55742
|
-
description: [
|
|
56199
|
+
description: [
|
|
56200
|
+
"Look up a Firebase Auth custom-claim entry by key (`a`, `o`, `fr`) or by `*ApiAuthClaims` interface name (`DemoApiAuthClaims`).",
|
|
56201
|
+
"",
|
|
56202
|
+
"Returns the claim meaning, value type, role mapping through `authRoleClaimsService`, owning app + interface, and the source path:line where the claim is declared.",
|
|
56203
|
+
"",
|
|
56204
|
+
"Use the optional `app` parameter (`demo-api`) to scope the lookup when the same claim key exists in multiple apps."
|
|
56205
|
+
].join("\n"),
|
|
55743
56206
|
inputSchema: {
|
|
55744
56207
|
type: "object",
|
|
55745
56208
|
properties: {
|
|
@@ -55962,7 +56425,14 @@ function sourceLink2(path, line) {
|
|
|
55962
56425
|
import { type as type106 } from "arktype";
|
|
55963
56426
|
var DBX_AUTH_ROLE_LOOKUP_TOOL = {
|
|
55964
56427
|
name: "dbx_auth_role_lookup",
|
|
55965
|
-
description: [
|
|
56428
|
+
description: [
|
|
56429
|
+
"Look up auth roles forward (by role string / const name), by tag, or in reverse (model + verb \u2192 required roles/scopes).",
|
|
56430
|
+
"",
|
|
56431
|
+
'- `topic="admin"` or `topic="AUTH_ADMIN_ROLE"` \u2192 role entry + claims that set it.',
|
|
56432
|
+
'- `tag="privileged"` \u2192 every role tagged with `privileged`.',
|
|
56433
|
+
'- `model="StorageFile" verb="read"` \u2192 reverse lookup of the roles/scopes the gate would expect.',
|
|
56434
|
+
'- `topic="list"` \u2192 catalog.'
|
|
56435
|
+
].join("\n"),
|
|
55966
56436
|
inputSchema: {
|
|
55967
56437
|
type: "object",
|
|
55968
56438
|
properties: {
|
|
@@ -56145,7 +56615,11 @@ function sourceLink3(path, line) {
|
|
|
56145
56615
|
import { type as type107 } from "arktype";
|
|
56146
56616
|
var DBX_AUTH_TOKEN_EXPLAIN_TOOL = {
|
|
56147
56617
|
name: "dbx_auth_token_explain",
|
|
56148
|
-
description: [
|
|
56618
|
+
description: [
|
|
56619
|
+
"Decode a JWT or claims object and annotate each claim with the catalogued meaning, role mapping, and source path:line.",
|
|
56620
|
+
"",
|
|
56621
|
+
"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."
|
|
56622
|
+
].join("\n"),
|
|
56149
56623
|
inputSchema: {
|
|
56150
56624
|
type: "object",
|
|
56151
56625
|
properties: {
|
|
@@ -56604,7 +57078,13 @@ function createAuthClusterTools(registry) {
|
|
|
56604
57078
|
function registerTools(server, options = {}) {
|
|
56605
57079
|
const underlyingServer = server.server;
|
|
56606
57080
|
const tools = [...DBX_TOOLS];
|
|
56607
|
-
tools.push(
|
|
57081
|
+
tools.push(
|
|
57082
|
+
createUiExamplesTool({ examplesRegistry: options.dbxDocsUiExamplesRegistry }),
|
|
57083
|
+
createModelValidateTool({ ruleOptions: options.modelValidateRuleOptions }),
|
|
57084
|
+
createModelValidateFolderTool({ ruleOptions: options.modelValidateRuleOptions }),
|
|
57085
|
+
createModelFixtureValidateAppTool({ getRegistry: () => options.fixtureModelRegistry }),
|
|
57086
|
+
createLogSearchTool(options.logSearchConfig)
|
|
57087
|
+
);
|
|
56608
57088
|
if (options.forgeFieldRegistry !== void 0) {
|
|
56609
57089
|
tools.push(createLookupFormTool({ registry: options.forgeFieldRegistry }), createSearchFormTool({ registry: options.forgeFieldRegistry }), createFormScaffoldTool({ registry: options.forgeFieldRegistry }));
|
|
56610
57090
|
}
|
|
@@ -56858,7 +57338,25 @@ async function loadServerContext(options = {}) {
|
|
|
56858
57338
|
await emitDownstreamHints({ cwd, externalCounts, onDownstreamHints: options.onDownstreamHints });
|
|
56859
57339
|
const context = {
|
|
56860
57340
|
resources: { semanticTypeRegistry: registry, forgeFieldRegistry: forgeRegistry, pipeRegistry, utilRegistry, modelSnapshotFieldRegistry, modelFirebaseIndexRegistry, uiComponentRegistry: uiRegistry, actionRegistry, filterRegistry, tokenRegistry, cssUtilityRegistry, authRegistry, cwd },
|
|
56861
|
-
tools: {
|
|
57341
|
+
tools: {
|
|
57342
|
+
semanticTypeRegistry: registry,
|
|
57343
|
+
forgeFieldRegistry: forgeRegistry,
|
|
57344
|
+
pipeRegistry,
|
|
57345
|
+
utilRegistry,
|
|
57346
|
+
modelSnapshotFieldRegistry,
|
|
57347
|
+
modelFirebaseIndexRegistry,
|
|
57348
|
+
uiComponentRegistry: uiRegistry,
|
|
57349
|
+
dbxDocsUiExamplesRegistry,
|
|
57350
|
+
actionRegistry,
|
|
57351
|
+
filterRegistry,
|
|
57352
|
+
tokenRegistry,
|
|
57353
|
+
cssUtilityRegistry,
|
|
57354
|
+
fixtureModelRegistry,
|
|
57355
|
+
modelValidateRuleOptions,
|
|
57356
|
+
authRegistry,
|
|
57357
|
+
cwd,
|
|
57358
|
+
logSearchConfig
|
|
57359
|
+
}
|
|
56862
57360
|
};
|
|
56863
57361
|
return context;
|
|
56864
57362
|
}
|
|
@@ -56962,7 +57460,14 @@ async function resolveAuthRegistry(input) {
|
|
|
56962
57460
|
}
|
|
56963
57461
|
function reportAuthLoaderResult(result) {
|
|
56964
57462
|
const { registry, scannedFiles, fileWarnings, extractWarnings, extractedAppCount, extractedClaimCount } = result;
|
|
56965
|
-
const summary = [
|
|
57463
|
+
const summary = [
|
|
57464
|
+
`[dbx-components-mcp] auth registry loaded`,
|
|
57465
|
+
` sources: ${registry.loadedSources.join(", ") || "(none)"}`,
|
|
57466
|
+
` scanned-files: ${scannedFiles.length}`,
|
|
57467
|
+
` apps: ${registry.apps.length} (extracted: ${extractedAppCount})`,
|
|
57468
|
+
` claims: ${registry.claims.length} (extracted: ${extractedClaimCount})`,
|
|
57469
|
+
` warnings: ${fileWarnings.length + extractWarnings.length}`
|
|
57470
|
+
].join("\n");
|
|
56966
57471
|
process.stderr.write(`${summary}
|
|
56967
57472
|
`);
|
|
56968
57473
|
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.0.
|
|
3
|
+
"version": "14.0.1",
|
|
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.0.
|
|
15
|
-
"@dereekb/util": "14.0.
|
|
14
|
+
"@dereekb/dbx-cli": "14.0.1",
|
|
15
|
+
"@dereekb/util": "14.0.1"
|
|
16
16
|
}
|
|
17
17
|
}
|