@dereekb/dbx-components-mcp 13.43.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 +744 -118
- package/generated/dereekb-date.utils.mcp.generated.json +11 -11
- package/generated/dereekb-dbx-form.forge-fields.mcp.generated.json +2 -2
- package/generated/dereekb-dbx-web.ui-components.mcp.generated.json +0 -6
- package/generated/dereekb-demo.dbx-docs-ui-examples.mcp.generated.json +20 -20
- package/generated/dereekb-firebase-server.utils.mcp.generated.json +4 -4
- package/generated/dereekb-firebase.utils.mcp.generated.json +6 -6
- package/generated/dereekb-util.utils.mcp.generated.json +13 -13
- package/package.json +3 -3
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: "
|
|
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": "
|
|
25
|
-
"@dereekb/util": "
|
|
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
|
}
|
|
@@ -2139,7 +2139,15 @@ var FIREBASE_MODELS = [
|
|
|
2139
2139
|
converter: "optionalFirestoreDate()",
|
|
2140
2140
|
tsType: "Maybe<Date>",
|
|
2141
2141
|
optional: true,
|
|
2142
|
-
description: "The date the
|
|
2142
|
+
description: "The date the CURRENT submission was made, if the space is submitted. Its presence IS the lock."
|
|
2143
|
+
},
|
|
2144
|
+
{
|
|
2145
|
+
name: "fsat",
|
|
2146
|
+
longName: "firstSubmittedAt",
|
|
2147
|
+
converter: "optionalFirestoreDate()",
|
|
2148
|
+
tsType: "Maybe<Date>",
|
|
2149
|
+
optional: true,
|
|
2150
|
+
description: "The date the space was FIRST submitted, if it ever was."
|
|
2143
2151
|
},
|
|
2144
2152
|
{
|
|
2145
2153
|
name: "cpat",
|
|
@@ -2156,6 +2164,46 @@ var FIREBASE_MODELS = [
|
|
|
2156
2164
|
tsType: "Maybe<Date>",
|
|
2157
2165
|
optional: true,
|
|
2158
2166
|
description: "The date this space becomes eligible for the expiration sweep, if it expires at all."
|
|
2167
|
+
},
|
|
2168
|
+
{
|
|
2169
|
+
name: "lat",
|
|
2170
|
+
longName: "locksAt",
|
|
2171
|
+
converter: "optionalFirestoreDate()",
|
|
2172
|
+
tsType: "Maybe<Date>",
|
|
2173
|
+
optional: true,
|
|
2174
|
+
description: "The date reopening stops being possible \u2014 the instant the submission becomes FULLY LOCKED."
|
|
2175
|
+
},
|
|
2176
|
+
{
|
|
2177
|
+
name: "lby",
|
|
2178
|
+
longName: "lockedBy",
|
|
2179
|
+
converter: "optionalFirestoreUID()",
|
|
2180
|
+
tsType: "Maybe<FirebaseAuthUserId>",
|
|
2181
|
+
optional: true,
|
|
2182
|
+
description: "The user who locked the submission early, when a caller did rather than the deadline passing."
|
|
2183
|
+
},
|
|
2184
|
+
{
|
|
2185
|
+
name: "rc",
|
|
2186
|
+
longName: "reopenCount",
|
|
2187
|
+
converter: "firestoreNumber({ default: 0 })",
|
|
2188
|
+
tsType: "number",
|
|
2189
|
+
optional: false,
|
|
2190
|
+
description: "Monotonic count of times this space has been REOPENED after a submission."
|
|
2191
|
+
},
|
|
2192
|
+
{
|
|
2193
|
+
name: "rat",
|
|
2194
|
+
longName: "reopenedAt",
|
|
2195
|
+
converter: "optionalFirestoreDate()",
|
|
2196
|
+
tsType: "Maybe<Date>",
|
|
2197
|
+
optional: true,
|
|
2198
|
+
description: "The date the space was last reopened, if it ever was."
|
|
2199
|
+
},
|
|
2200
|
+
{
|
|
2201
|
+
name: "rby",
|
|
2202
|
+
longName: "reopenedBy",
|
|
2203
|
+
converter: "optionalFirestoreUID()",
|
|
2204
|
+
tsType: "Maybe<FirebaseAuthUserId>",
|
|
2205
|
+
optional: true,
|
|
2206
|
+
description: "The user who last reopened the space."
|
|
2159
2207
|
}
|
|
2160
2208
|
],
|
|
2161
2209
|
enums: [
|
|
@@ -2212,7 +2260,7 @@ var FIREBASE_MODELS = [
|
|
|
2212
2260
|
description: "Processing state of a submitted {@link FormSpace}."
|
|
2213
2261
|
}
|
|
2214
2262
|
],
|
|
2215
|
-
detectionHints: ["t", "n", "ps", "m", "uc", "f", "pn", "pat", "uat", "sat", "cpat", "eat"],
|
|
2263
|
+
detectionHints: ["t", "n", "ps", "m", "uc", "f", "pn", "pat", "uat", "sat", "fsat", "cpat", "eat", "lat", "lby", "rc", "rat", "rby"],
|
|
2216
2264
|
description: "A type-registered container for a client-side form: its in-progress JSON, its uploads, and its submission state.",
|
|
2217
2265
|
modelGroup: "FormSpace",
|
|
2218
2266
|
collectionKind: "root",
|
|
@@ -10086,7 +10134,7 @@ function collectFromTypeReference(ref, context) {
|
|
|
10086
10134
|
result = collectFromTypeNode(args[0], { ...context, forceOptional: false }).map((p) => ({ ...p, required: true }));
|
|
10087
10135
|
} else if (refName === "Omit" && args.length === 2) {
|
|
10088
10136
|
const keys = parseKeyUnion(args[1]);
|
|
10089
|
-
const merged = /* @__PURE__ */ new Set([...context.omit, ...keys]);
|
|
10137
|
+
const merged = /* @__PURE__ */ new Set([...Array.from(context.omit), ...Array.from(keys)]);
|
|
10090
10138
|
result = collectFromTypeNode(args[0], { ...context, omit: merged });
|
|
10091
10139
|
} else if (refName === "Pick" && args.length === 2) {
|
|
10092
10140
|
const keys = parseKeyUnion(args[1]);
|
|
@@ -10121,6 +10169,7 @@ var PropertyAccumulator = class {
|
|
|
10121
10169
|
constructor(context) {
|
|
10122
10170
|
this.context = context;
|
|
10123
10171
|
}
|
|
10172
|
+
context;
|
|
10124
10173
|
entries = [];
|
|
10125
10174
|
index = /* @__PURE__ */ new Map();
|
|
10126
10175
|
add(entry) {
|
|
@@ -11427,7 +11476,7 @@ function tryTransitiveResolution(input) {
|
|
|
11427
11476
|
const calleeLine = resolved.decl.getStartLineNumber();
|
|
11428
11477
|
warnings.push({ kind: "unannotated-query-helper", severity: "warning", name: factoryName, callee: calleeName, calleeFilePath, calleeLine, filePath, line: call.getStartLineNumber() });
|
|
11429
11478
|
}
|
|
11430
|
-
const nextVisited =
|
|
11479
|
+
const nextVisited = new Set(visited).add(calleeKey);
|
|
11431
11480
|
walkBodyInto({
|
|
11432
11481
|
decl: resolved.decl,
|
|
11433
11482
|
factoryName,
|
|
@@ -15407,13 +15456,21 @@ function formatBuildFailure(outcome) {
|
|
|
15407
15456
|
}
|
|
15408
15457
|
function formatWriteSummary(input) {
|
|
15409
15458
|
const { outputPath, diff, generatedComposites, generatedFieldOverrides } = input;
|
|
15410
|
-
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
|
+
];
|
|
15411
15464
|
return appendDiffSection(lines, diff).join("\n");
|
|
15412
15465
|
}
|
|
15413
15466
|
function formatCheckSummary(input) {
|
|
15414
15467
|
const { drift, outputPath, diff, generatedComposites, generatedFieldOverrides } = input;
|
|
15415
15468
|
const headline = drift ? `generate-firestore-indexes: drift detected against ${outputPath}` : `generate-firestore-indexes: in sync (${outputPath})`;
|
|
15416
|
-
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
|
+
];
|
|
15417
15474
|
return appendDiffSection(lines, diff).join("\n");
|
|
15418
15475
|
}
|
|
15419
15476
|
function appendDiffSection(lines, diff) {
|
|
@@ -19767,7 +19824,7 @@ function registerResources(server, options = {}) {
|
|
|
19767
19824
|
if (options.authRegistry !== void 0) {
|
|
19768
19825
|
registerAuthResource(server, { registry: options.authRegistry });
|
|
19769
19826
|
}
|
|
19770
|
-
registerFirestoreRulesResource(server,
|
|
19827
|
+
registerFirestoreRulesResource(server, options.cwd === void 0 ? {} : { cwd: options.cwd });
|
|
19771
19828
|
}
|
|
19772
19829
|
|
|
19773
19830
|
// packages/dbx-components-mcp/src/tools/lookup-form.tool.ts
|
|
@@ -19860,7 +19917,19 @@ function formatBrief(field) {
|
|
|
19860
19917
|
const tierBits = formatTierBits(field);
|
|
19861
19918
|
const arrayOptional = field.arrayOutput === "optional" ? " (single or array)" : "";
|
|
19862
19919
|
const array = field.arrayOutput === "yes" ? " (array)" : arrayOptional;
|
|
19863
|
-
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");
|
|
19864
19933
|
}
|
|
19865
19934
|
function formatFull(field) {
|
|
19866
19935
|
const header = formatHeader(field);
|
|
@@ -20362,7 +20431,19 @@ function formatSearchResults(input) {
|
|
|
20362
20431
|
const field = hit.entry;
|
|
20363
20432
|
const optionalArray = field.arrayOutput === "optional" ? " *(single or array)*" : "";
|
|
20364
20433
|
const array = field.arrayOutput === "yes" ? " *(array)*" : optionalArray;
|
|
20365
|
-
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
|
+
);
|
|
20366
20447
|
}
|
|
20367
20448
|
return lines.join("\n").trimEnd();
|
|
20368
20449
|
}
|
|
@@ -21414,7 +21495,19 @@ function formatSearchResults2(options) {
|
|
|
21414
21495
|
} else {
|
|
21415
21496
|
const lines = [`# Search: \`${query}\``, "", `Tokens: \`${tokenDisplay}\`${scopeLabel} \xB7 ${hits.length} result${hits.length === 1 ? "" : "s"}`, ""];
|
|
21416
21497
|
for (const hit of hits) {
|
|
21417
|
-
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
|
+
);
|
|
21418
21511
|
}
|
|
21419
21512
|
appendRelatedExamples(lines, hits, examplesRegistry);
|
|
21420
21513
|
result = lines.join("\n").trimEnd();
|
|
@@ -21510,7 +21603,6 @@ import { DbxActionConfirmConfig } from '@dereekb/dbx-web';
|
|
|
21510
21603
|
|
|
21511
21604
|
@Component({
|
|
21512
21605
|
selector: 'app-danger-zone',
|
|
21513
|
-
standalone: true,
|
|
21514
21606
|
imports: [DbxCardBoxComponent, DbxBarDirective, DbxButtonComponent, DbxActionDirective, DbxActionConfirmDirective],
|
|
21515
21607
|
template: \`
|
|
21516
21608
|
<dbx-card-box header="Danger zone" icon="warning">
|
|
@@ -21527,8 +21619,7 @@ import { DbxActionConfirmConfig } from '@dereekb/dbx-web';
|
|
|
21527
21619
|
</dbx-button>
|
|
21528
21620
|
</dbx-bar>
|
|
21529
21621
|
</dbx-card-box>
|
|
21530
|
-
|
|
21531
|
-
changeDetection: ChangeDetectionStrategy.OnPush
|
|
21622
|
+
\`
|
|
21532
21623
|
})
|
|
21533
21624
|
export class DangerZoneComponent {
|
|
21534
21625
|
readonly deleteAccountAction = /* DbxActionContextStoreSourceInstance<...> */ null;
|
|
@@ -21579,7 +21670,6 @@ interface Item {
|
|
|
21579
21670
|
|
|
21580
21671
|
@Component({
|
|
21581
21672
|
selector: 'app-items-page',
|
|
21582
|
-
standalone: true,
|
|
21583
21673
|
imports: [DbxSectionPageComponent, DbxListComponent, DbxListEmptyContentComponent, DbxPagebarComponent, DbxButtonComponent, DbxActionDirective],
|
|
21584
21674
|
template: \`
|
|
21585
21675
|
<dbx-section-page header="Items" icon="list" scroll="body">
|
|
@@ -21594,8 +21684,7 @@ interface Item {
|
|
|
21594
21684
|
</dbx-list-empty-content>
|
|
21595
21685
|
</dbx-list>
|
|
21596
21686
|
</dbx-section-page>
|
|
21597
|
-
|
|
21598
|
-
changeDetection: ChangeDetectionStrategy.OnPush
|
|
21687
|
+
\`
|
|
21599
21688
|
})
|
|
21600
21689
|
export class ItemsPageComponent {
|
|
21601
21690
|
readonly items$: Observable<ListLoadingState<Item>> = /* ... */ null!;
|
|
@@ -21641,7 +21730,6 @@ interface Item {
|
|
|
21641
21730
|
|
|
21642
21731
|
@Component({
|
|
21643
21732
|
selector: 'app-items-loaded',
|
|
21644
|
-
standalone: true,
|
|
21645
21733
|
imports: [CommonModule, DbxLoadingComponent, DbxListComponent, DbxListEmptyContentComponent, DbxIntroActionSectionComponent],
|
|
21646
21734
|
template: \`
|
|
21647
21735
|
<dbx-loading [state]="state$ | async" text="Loading items...">
|
|
@@ -21657,8 +21745,7 @@ interface Item {
|
|
|
21657
21745
|
</dbx-list>
|
|
21658
21746
|
</dbx-intro-action-section>
|
|
21659
21747
|
</dbx-loading>
|
|
21660
|
-
|
|
21661
|
-
changeDetection: ChangeDetectionStrategy.OnPush
|
|
21748
|
+
\`
|
|
21662
21749
|
})
|
|
21663
21750
|
export class ItemsLoadedComponent {
|
|
21664
21751
|
readonly state$: Observable<ListLoadingState<Item>> = /* ... */ null!;
|
|
@@ -21707,7 +21794,6 @@ import { MatInputModule } from '@angular/material/input';
|
|
|
21707
21794
|
|
|
21708
21795
|
@Component({
|
|
21709
21796
|
selector: 'app-account-settings',
|
|
21710
|
-
standalone: true,
|
|
21711
21797
|
imports: [DbxSectionComponent, DbxBarDirective, DbxButtonComponent, DbxButtonSpacerDirective, DbxActionDirective, ReactiveFormsModule, MatFormFieldModule, MatInputModule],
|
|
21712
21798
|
template: \`
|
|
21713
21799
|
<dbx-section header="Account" icon="person" hint="Update your profile">
|
|
@@ -21724,8 +21810,7 @@ import { MatInputModule } from '@angular/material/input';
|
|
|
21724
21810
|
<dbx-button text="Cancel" stroked (btnClick)="cancel()"></dbx-button>
|
|
21725
21811
|
</dbx-bar>
|
|
21726
21812
|
</dbx-section>
|
|
21727
|
-
|
|
21728
|
-
changeDetection: ChangeDetectionStrategy.OnPush
|
|
21813
|
+
\`
|
|
21729
21814
|
})
|
|
21730
21815
|
export class AccountSettingsComponent {
|
|
21731
21816
|
private readonly fb = inject(FormBuilder);
|
|
@@ -21762,7 +21847,6 @@ import { UIRouterModule } from '@uirouter/angular';
|
|
|
21762
21847
|
|
|
21763
21848
|
@Component({
|
|
21764
21849
|
selector: 'app-shell',
|
|
21765
|
-
standalone: true,
|
|
21766
21850
|
imports: [DbxSidenavPageComponent, DbxSidenavComponent, DbxContentPageDirective, UIRouterModule],
|
|
21767
21851
|
template: \`
|
|
21768
21852
|
<dbx-sidenav-page>
|
|
@@ -21771,8 +21855,7 @@ import { UIRouterModule } from '@uirouter/angular';
|
|
|
21771
21855
|
<ui-view></ui-view>
|
|
21772
21856
|
</div>
|
|
21773
21857
|
</dbx-sidenav-page>
|
|
21774
|
-
|
|
21775
|
-
changeDetection: ChangeDetectionStrategy.OnPush
|
|
21858
|
+
\`
|
|
21776
21859
|
})
|
|
21777
21860
|
export class AppShellComponent {
|
|
21778
21861
|
readonly navAnchors: ClickableAnchor[] = [
|
|
@@ -21823,7 +21906,6 @@ interface Member {
|
|
|
21823
21906
|
|
|
21824
21907
|
@Component({
|
|
21825
21908
|
selector: 'app-members-page',
|
|
21826
|
-
standalone: true,
|
|
21827
21909
|
imports: [CommonModule, MatButtonModule, MatIconModule, DbxSectionPageComponent, DbxTwoColumnComponent, DbxTwoColumnRightComponent, DbxListComponent],
|
|
21828
21910
|
template: \`
|
|
21829
21911
|
<dbx-section-page header="Members" icon="group" scroll="body">
|
|
@@ -21835,8 +21917,7 @@ interface Member {
|
|
|
21835
21917
|
</dbx-two-column-right>
|
|
21836
21918
|
</dbx-two-column>
|
|
21837
21919
|
</dbx-section-page>
|
|
21838
|
-
|
|
21839
|
-
changeDetection: ChangeDetectionStrategy.OnPush
|
|
21920
|
+
\`
|
|
21840
21921
|
})
|
|
21841
21922
|
export class MembersPageComponent {
|
|
21842
21923
|
readonly members$: Observable<ListLoadingState<Member>> = /* ... */ null!;
|
|
@@ -25365,6 +25446,96 @@ var RULE_CATALOG = [
|
|
|
25365
25446
|
whenItApplies: "When a key on `<app>SystemStateStoredDataConverterMap` is neither a local `<NAME>_SYSTEM_STATE_TYPE` constant nor an imported identifier.",
|
|
25366
25447
|
whenItDoesNotApply: "Keys imported from upstream packages \u2014 those are detected via `importedIdentifiers` and skipped.",
|
|
25367
25448
|
canonicalFix: "Either declare the missing constant locally or import it from the upstream package that owns it."
|
|
25449
|
+
},
|
|
25450
|
+
{
|
|
25451
|
+
code: "WORKSPACE_DEPLOY_ENVIRONMENT_FILE_MISSING",
|
|
25452
|
+
source: "dbx_workspace_validate",
|
|
25453
|
+
severity: "error",
|
|
25454
|
+
title: "A configuration's file-replacement or esbuild-config path does not exist on disk",
|
|
25455
|
+
whatItFlags: "A configuration's file-replacement or esbuild-config path does not exist on disk.",
|
|
25456
|
+
whenItApplies: "When a `fileReplacements` `replace` / `with` path, or a configuration's `esbuildConfig` path, names a file that is absent from the working tree.",
|
|
25457
|
+
whenItDoesNotApply: "Generated files produced by an earlier target in the same pipeline \u2014 declare them as that target's `outputs` so the dependency is explicit, or the reference is genuinely broken.",
|
|
25458
|
+
canonicalFix: "Create the missing file or correct the path. An Angular build fails loudly on a missing replacement, but an esbuild `additionalProperties: true` schema accepts a bad path silently."
|
|
25459
|
+
},
|
|
25460
|
+
{
|
|
25461
|
+
code: "WORKSPACE_DEPLOY_ENVIRONMENT_FILE_UNREFERENCED",
|
|
25462
|
+
source: "dbx_workspace_validate",
|
|
25463
|
+
severity: "warning",
|
|
25464
|
+
title: "An `environment.<name>.ts` file exists but no build configuration references it",
|
|
25465
|
+
whatItFlags: "An `environment.<name>.ts` file exists but no build configuration references it.",
|
|
25466
|
+
whenItApplies: "When a file matching `environment.*.ts` sits in a project's `src/environments/` directory and appears in no configuration's `fileReplacements`, and no `esbuildConfig` file references it.",
|
|
25467
|
+
whenItDoesNotApply: "The base `environment.ts` (always the replacement source, never a target), and environment files imported directly by application code rather than swapped in by the build.",
|
|
25468
|
+
canonicalFix: "Add the configuration that consumes it, or delete the file. An unreachable environment file reads as a working deploy lane that does not exist \u2014 the file is edited, reviewed, and never compiled."
|
|
25469
|
+
},
|
|
25470
|
+
{
|
|
25471
|
+
code: "WORKSPACE_DEPLOY_HOSTING_TARGET_MISSING",
|
|
25472
|
+
source: "dbx_workspace_validate",
|
|
25473
|
+
severity: "error",
|
|
25474
|
+
title: "A deploy command targets a firebase hosting target that `firebase.json` does not declare",
|
|
25475
|
+
whatItFlags: "A deploy command targets a firebase hosting target that `firebase.json` does not declare.",
|
|
25476
|
+
whenItApplies: "When a deploy command runs `firebase deploy --only hosting:<target>` and `<target>` matches no `target` (or `site`) entry under `hosting` in `firebase.json`.",
|
|
25477
|
+
whenItDoesNotApply: "Workspaces that resolve hosting targets through `.firebaserc` target aliases the scanner does not read \u2014 verify manually if this misfires.",
|
|
25478
|
+
canonicalFix: "Add the hosting entry to `firebase.json`, or point the command at a declared target. `firebase deploy` fails on an unknown hosting target, so this breaks the lane outright."
|
|
25479
|
+
},
|
|
25480
|
+
{
|
|
25481
|
+
code: "WORKSPACE_DEPLOY_LANE_ASYMMETRIC",
|
|
25482
|
+
source: "dbx_workspace_validate",
|
|
25483
|
+
severity: "warning",
|
|
25484
|
+
title: "A deploy lane is wired on one side of the app/api pair but not the other",
|
|
25485
|
+
whatItFlags: "A deploy lane is wired on one side of the app/api pair but not the other.",
|
|
25486
|
+
whenItApplies: "When one project in the workspace declares `ci-deploy-<lane>` and a sibling project that declares at least one other `ci-deploy-*` lane does not declare this one.",
|
|
25487
|
+
whenItDoesNotApply: "Projects deliberately deployed on their own cadence, and workspaces with a single deployable project. Suppress by giving the sibling the lane, or accept the warning.",
|
|
25488
|
+
canonicalFix: "Add the missing `ci-deploy-<lane>` target to the sibling project. A lane present on the API but not the web app (or vice versa) deploys half an application and leaves the two halves on different builds."
|
|
25489
|
+
},
|
|
25490
|
+
{
|
|
25491
|
+
code: "WORKSPACE_DEPLOY_LANE_NO_ENVIRONMENT_SELECTION",
|
|
25492
|
+
source: "dbx_workspace_validate",
|
|
25493
|
+
severity: "error",
|
|
25494
|
+
title: "A deploy lane builds a configuration that selects no environment file",
|
|
25495
|
+
whatItFlags: "A deploy lane builds a configuration that selects no environment file.",
|
|
25496
|
+
whenItApplies: "When a `ci-deploy-<lane>` target builds a configuration that provides no environment-selection mechanism for its executor \u2014 no `fileReplacements` for an Angular `application` build, or no lane-specific `esbuildConfig` for an `@nx/esbuild:esbuild` build.",
|
|
25497
|
+
whenItDoesNotApply: "Non-deploy configurations such as `development`, which are expected to compile the base `environment.ts`. Only configurations reachable from a `ci-deploy-*` target are checked.",
|
|
25498
|
+
canonicalFix: "Add the `fileReplacements` entry (or the lane's `esbuildConfig`) to the configuration. Without it the lane deploys the unreplaced `environment.ts` \u2014 localhost URLs, dev credentials, and any developer-only function map \u2014 to a live project."
|
|
25499
|
+
},
|
|
25500
|
+
{
|
|
25501
|
+
code: "WORKSPACE_TARGET_OUTPUT_ESCAPES_ROOT",
|
|
25502
|
+
source: "dbx_workspace_validate",
|
|
25503
|
+
severity: "error",
|
|
25504
|
+
title: "A target's `outputs` entry resolves outside the workspace root",
|
|
25505
|
+
whatItFlags: "A target's `outputs` entry resolves outside the workspace root.",
|
|
25506
|
+
whenItApplies: 'When an `outputs` entry \u2014 after `{options.<name>}` substitution from the target\'s own options \u2014 resolves to a path above the workspace root. The common shape is `outputs: ["{options.reportsDirectory}"]` with a `../../`-relative option value.',
|
|
25507
|
+
whenItDoesNotApply: "Entries anchored by `{workspaceRoot}` or `{projectRoot}`, and entries whose `{options.<name>}` token has no resolvable value (skipped rather than guessed).",
|
|
25508
|
+
canonicalFix: 'Anchor the entry explicitly, e.g. `"{workspaceRoot}/coverage/{projectRoot}"`. Nx refuses to cache an output it cannot place inside the workspace and will report the task as flaky instead of failing loudly.'
|
|
25509
|
+
},
|
|
25510
|
+
{
|
|
25511
|
+
code: "WORKSPACE_TARGET_REF_CONFIGURATION_MISSING",
|
|
25512
|
+
source: "dbx_workspace_validate",
|
|
25513
|
+
severity: "error",
|
|
25514
|
+
title: "An `nx run <project>:<target>:<configuration>` reference names a configuration the target does not declare",
|
|
25515
|
+
whatItFlags: "An `nx run <project>:<target>:<configuration>` reference names a configuration the target does not declare.",
|
|
25516
|
+
whenItApplies: "Whenever a reference carries a configuration segment and the target's `configurations` map has no such key.",
|
|
25517
|
+
whenItDoesNotApply: "Targets that declare no `configurations` at all \u2014 Nx tolerates a configuration argument there, so it is not reported.",
|
|
25518
|
+
canonicalFix: "Rename the reference to a declared configuration (a `prod` / `production` mismatch is the usual cause), or add the configuration to the target. Nx does NOT error on an unknown configuration: `create-task-graph.js` silently substitutes `defaultConfiguration`, so a mismatch here ships whatever that default builds \u2014 and ships the base options with no configuration merged at all when no default is set."
|
|
25519
|
+
},
|
|
25520
|
+
{
|
|
25521
|
+
code: "WORKSPACE_TARGET_REF_PROJECT_MISSING",
|
|
25522
|
+
source: "dbx_workspace_validate",
|
|
25523
|
+
severity: "error",
|
|
25524
|
+
title: "An `nx run <project>:...` reference names a project that does not exist in the workspace",
|
|
25525
|
+
whatItFlags: "An `nx run <project>:...` reference names a project that does not exist in the workspace.",
|
|
25526
|
+
whenItApplies: "When a target reference in a `project.json` command, CI config, shell script, or npm script names a project with no matching entry in the workspace project graph.",
|
|
25527
|
+
whenItDoesNotApply: "References built from shell variables or CI interpolation the scanner cannot resolve \u2014 those are skipped rather than reported.",
|
|
25528
|
+
canonicalFix: "Create the project, or update the reference to the project's real name. A renamed or deleted project leaves these references behind and they only fail at deploy time."
|
|
25529
|
+
},
|
|
25530
|
+
{
|
|
25531
|
+
code: "WORKSPACE_TARGET_REF_TARGET_MISSING",
|
|
25532
|
+
source: "dbx_workspace_validate",
|
|
25533
|
+
severity: "error",
|
|
25534
|
+
title: "An `nx run <project>:<target>` reference names a target the project does not declare",
|
|
25535
|
+
whatItFlags: "An `nx run <project>:<target>` reference names a target the project does not declare.",
|
|
25536
|
+
whenItApplies: "When the referenced project exists but has no target by that name (after `nx.json` `targetDefaults` are accounted for).",
|
|
25537
|
+
whenItDoesNotApply: "Targets contributed by an inferred-target plugin the scanner does not evaluate \u2014 declare the plugin's targets explicitly if this misfires.",
|
|
25538
|
+
canonicalFix: "Add the target to the project, or point the reference at an existing one. This is the single most common way a CI lane silently stops doing anything."
|
|
25368
25539
|
}
|
|
25369
25540
|
];
|
|
25370
25541
|
|
|
@@ -25426,7 +25597,31 @@ function attachRemediation(code17) {
|
|
|
25426
25597
|
|
|
25427
25598
|
// packages/dbx-cli/validate/src/lib/_core/rule-catalog/format.ts
|
|
25428
25599
|
function formatRuleAsMarkdown(entry) {
|
|
25429
|
-
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
|
+
];
|
|
25430
25625
|
if (entry.canonicalFixTemplate) {
|
|
25431
25626
|
lines.push("## Fix template", "", entry.canonicalFixTemplate, "");
|
|
25432
25627
|
}
|
|
@@ -35767,7 +35962,15 @@ function appendGroupSection(lines, apiRel, group) {
|
|
|
35767
35962
|
function appendFileTable(lines, group) {
|
|
35768
35963
|
lines.push("", "| File | Kind | Subgroups | Canonical | Rename suggestion |", "|---|---|---|---|---|");
|
|
35769
35964
|
for (const file of group.files) {
|
|
35770
|
-
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
|
+
);
|
|
35771
35974
|
}
|
|
35772
35975
|
}
|
|
35773
35976
|
function appendWhereToAddSection(lines, apiRel, group) {
|
|
@@ -35783,7 +35986,17 @@ function appendRecommendation(lines, rec, group) {
|
|
|
35783
35986
|
lines.push("", `- **${rec.label}** \u2192 \`${rec.canonicalPath}\` ${status}`, ` ${rec.summary}`);
|
|
35784
35987
|
}
|
|
35785
35988
|
function appendConventionLegend(lines) {
|
|
35786
|
-
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
|
+
);
|
|
35787
36000
|
}
|
|
35788
36001
|
function appendDriftSummary(lines, catalog) {
|
|
35789
36002
|
const driftFiles = [];
|
|
@@ -35929,7 +36142,14 @@ function stringify(value) {
|
|
|
35929
36142
|
|
|
35930
36143
|
// packages/dbx-cli/model-test/src/lib/fixture/format.markdown.ts
|
|
35931
36144
|
function formatListAsMarkdown(extraction) {
|
|
35932
|
-
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
|
+
];
|
|
35933
36153
|
if (extraction.entries.length === 0) {
|
|
35934
36154
|
lines.push("", "_No model fixtures found._");
|
|
35935
36155
|
} else {
|
|
@@ -35986,7 +36206,15 @@ function nonModelDescription(entry) {
|
|
|
35986
36206
|
return "Non-Firestore-model fixture.";
|
|
35987
36207
|
}
|
|
35988
36208
|
function appendLookupTriplet(lines, entry) {
|
|
35989
|
-
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
|
+
);
|
|
35990
36218
|
}
|
|
35991
36219
|
function appendLookupGenerics(lines, entry) {
|
|
35992
36220
|
if (entry.fixtureExtendsGenerics.length > 0) {
|
|
@@ -36156,11 +36384,19 @@ function renderParamsBlock(input, paramsTypeName, todos) {
|
|
|
36156
36384
|
}
|
|
36157
36385
|
function renderInstanceBlock(input) {
|
|
36158
36386
|
input.todos.push(`Add instance methods to \`${input.instanceClassName}\`.`);
|
|
36159
|
-
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");
|
|
36160
36392
|
}
|
|
36161
36393
|
function renderFixtureBlock(input) {
|
|
36162
36394
|
input.todos.push(`Add forwarder methods to \`${input.fixtureClassName}\` after defining instance methods.`);
|
|
36163
|
-
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");
|
|
36164
36400
|
}
|
|
36165
36401
|
function renderFactoryBlock(input) {
|
|
36166
36402
|
const generics = renderFactoryGenerics(input);
|
|
@@ -36180,7 +36416,15 @@ function renderFactoryBlock(input) {
|
|
|
36180
36416
|
return lines.join("\n");
|
|
36181
36417
|
}
|
|
36182
36418
|
function renderFactoryGenerics(input) {
|
|
36183
|
-
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
|
+
];
|
|
36184
36428
|
if (input.collectionGenericArg) {
|
|
36185
36429
|
return [...baseGenerics, input.collectionGenericArg];
|
|
36186
36430
|
}
|
|
@@ -36202,10 +36446,13 @@ function renderFactoryCallbacks(input) {
|
|
|
36202
36446
|
} else {
|
|
36203
36447
|
callbacks.push(`getCollection: (fi) => fi.${defaultCollectionsAccessor(input.prefix)}.${camelInitial(input.model)}Collection`);
|
|
36204
36448
|
}
|
|
36205
|
-
callbacks.push(
|
|
36449
|
+
callbacks.push(
|
|
36450
|
+
`makeInstance: (delegate, ref, testInstance) => new ${input.instanceClassName}(delegate, ref, testInstance)`,
|
|
36451
|
+
`makeRef: async (collection, params, _p) => {
|
|
36206
36452
|
// TODO: choose the correct ref strategy (auto id vs deterministic id).
|
|
36207
36453
|
return collection.documentAccessor().newDocument().documentRef;
|
|
36208
|
-
}`
|
|
36454
|
+
}`
|
|
36455
|
+
);
|
|
36209
36456
|
if (input.withInitDocument) {
|
|
36210
36457
|
callbacks.push(`initDocument: async (instance, params) => {
|
|
36211
36458
|
// TODO: seed the new document with sensible defaults from \`params\`.
|
|
@@ -37405,7 +37652,18 @@ function formatReportAsJson4(report) {
|
|
|
37405
37652
|
// packages/dbx-cli/validate/src/lib/dbx-asset-list-app/format.markdown.ts
|
|
37406
37653
|
function formatReportAsMarkdown4(report) {
|
|
37407
37654
|
const basename12 = report.componentDir.split("/").pop() ?? report.componentDir;
|
|
37408
|
-
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
|
+
];
|
|
37409
37667
|
if (report.assets.length === 0) {
|
|
37410
37668
|
lines.push("", "_None found._");
|
|
37411
37669
|
} else {
|
|
@@ -37796,7 +38054,18 @@ function formatReportAsJson6(report) {
|
|
|
37796
38054
|
function formatReportAsMarkdown6(report) {
|
|
37797
38055
|
const basename12 = report.componentDir.split("/").pop() ?? report.componentDir;
|
|
37798
38056
|
const uploadServiceFactoryText = report.uploadServiceFactoryName ? code7(report.uploadServiceFactoryName) : "_Not defined._";
|
|
37799
|
-
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
|
+
];
|
|
37800
38069
|
if (report.purposes.length === 0) {
|
|
37801
38070
|
lines.push("", "_None found._");
|
|
37802
38071
|
} else {
|
|
@@ -38745,11 +39014,12 @@ function matchKindForRadius(exact, raw) {
|
|
|
38745
39014
|
if (exact.defaults.light === raw || exact.defaults.dark === raw) return "(exact-match design-system corner token).";
|
|
38746
39015
|
return "(idiomatic full / pill radius \u2014 design system token).";
|
|
38747
39016
|
}
|
|
38748
|
-
function findExactRoleToken(
|
|
39017
|
+
function findExactRoleToken(params) {
|
|
39018
|
+
const { registry, raw, role, isAllowed } = params;
|
|
38749
39019
|
const candidates = registry.byRole.get(role) ?? [];
|
|
38750
39020
|
let result;
|
|
38751
39021
|
for (const entry of candidates) {
|
|
38752
|
-
if (entry.defaults.light === raw || entry.defaults.dark === raw) {
|
|
39022
|
+
if ((entry.defaults.light === raw || entry.defaults.dark === raw) && (isAllowed === void 0 || isAllowed(entry))) {
|
|
38753
39023
|
result = entry;
|
|
38754
39024
|
break;
|
|
38755
39025
|
}
|
|
@@ -38894,7 +39164,7 @@ var hardcodedRadius = (input) => {
|
|
|
38894
39164
|
const colonIndex = input.scss.indexOf(":", match.index);
|
|
38895
39165
|
if (colonIndex !== -1 && isInsideScssVarDeclaration(input.scss, colonIndex)) continue;
|
|
38896
39166
|
const raw = match[1];
|
|
38897
|
-
let exact = findExactRoleToken(input.tokenRegistry, raw, "radius");
|
|
39167
|
+
let exact = findExactRoleToken({ registry: input.tokenRegistry, raw, role: "radius" });
|
|
38898
39168
|
if (exact === void 0 && FULL_RADIUS_SYNONYMS.has(raw)) {
|
|
38899
39169
|
exact = input.tokenRegistry.findByCssVariable("--mat-sys-corner-full");
|
|
38900
39170
|
}
|
|
@@ -38925,7 +39195,7 @@ var hardcodedShadow = (input) => {
|
|
|
38925
39195
|
if (match.index === void 0) continue;
|
|
38926
39196
|
const raw = match[1].trim();
|
|
38927
39197
|
if (raw.startsWith("var(") || raw === "none") continue;
|
|
38928
|
-
const exact = findExactRoleToken(input.tokenRegistry, raw, "elevation");
|
|
39198
|
+
const exact = findExactRoleToken({ registry: input.tokenRegistry, raw, role: "elevation" });
|
|
38929
39199
|
const fix = exact === void 0 ? 'Hardcoded `box-shadow:` declarations should map to one of the `--mat-sys-level0..5` elevation tokens, or wrap the surface in `<dbx-content-box [elevate]="true">` so the shadow comes from the design system.' : `Replace this hardcoded shadow with \`box-shadow: var(${exact.cssVariable})\` or use \`<dbx-content-box [elevate]="true">\` to apply the elevation through the wrapper component.`;
|
|
38930
39200
|
matches.push({
|
|
38931
39201
|
id: "hardcoded-shadow",
|
|
@@ -38970,12 +39240,13 @@ var hardcodedHintColor = (input) => {
|
|
|
38970
39240
|
return matches;
|
|
38971
39241
|
};
|
|
38972
39242
|
var HARDCODED_PADDING_RE = /(?:padding|margin)\s*(?:-(?:top|right|bottom|left))?\s*:\s*(\d+(?:\.\d+)?px)(?!\s*\))/gi;
|
|
39243
|
+
var DBX_PADDING_SCALE_RE = /^--dbx-padding-\d+$/;
|
|
38973
39244
|
var hardcodedPadding = (input) => {
|
|
38974
39245
|
const matches = [];
|
|
38975
39246
|
for (const match of input.scss.matchAll(HARDCODED_PADDING_RE)) {
|
|
38976
39247
|
if (match.index === void 0) continue;
|
|
38977
39248
|
const raw = match[1];
|
|
38978
|
-
const exact = findExactRoleToken(input.tokenRegistry, raw, "spacing");
|
|
39249
|
+
const exact = findExactRoleToken({ registry: input.tokenRegistry, raw, role: "spacing", isAllowed: (entry) => DBX_PADDING_SCALE_RE.test(entry.cssVariable) });
|
|
38979
39250
|
if (exact !== void 0) {
|
|
38980
39251
|
const utility = exact.utilityClasses?.[0];
|
|
38981
39252
|
const fix = ["Replace `" + match[0].trim() + "` with `var(" + exact.cssVariable + ")`" + (utility === void 0 ? "" : ` (or use the \`${utility}\` utility class on the host element)`) + "."].join("\n");
|
|
@@ -41473,7 +41744,14 @@ function rootDocumentStoreBody(args) {
|
|
|
41473
41744
|
if (args.functionsClass !== void 0) {
|
|
41474
41745
|
packageImports.push(args.functionsClass);
|
|
41475
41746
|
}
|
|
41476
|
-
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
|
+
];
|
|
41477
41755
|
if (args.functionsClass !== void 0) {
|
|
41478
41756
|
lines.push(` readonly ${functionsFieldName(args.functionsClass)} = inject(${args.functionsClass});`, "");
|
|
41479
41757
|
}
|
|
@@ -41516,11 +41794,29 @@ function subDocumentStoreBody(args) {
|
|
|
41516
41794
|
if (args.functionsClass !== void 0) {
|
|
41517
41795
|
packageImports.push(args.functionsClass);
|
|
41518
41796
|
}
|
|
41519
|
-
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
|
+
];
|
|
41520
41806
|
if (args.functionsClass !== void 0) {
|
|
41521
41807
|
lines.push(` readonly ${functionsFieldName(args.functionsClass)} = inject(${args.functionsClass});`, "");
|
|
41522
41808
|
}
|
|
41523
|
-
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
|
+
);
|
|
41524
41820
|
const crudLines = renderCrudLines(args, "this");
|
|
41525
41821
|
if (crudLines.length > 0) {
|
|
41526
41822
|
lines.push("", ...crudLines);
|
|
@@ -41565,7 +41861,14 @@ function systemStateAccessorBody(args) {
|
|
|
41565
41861
|
if (args.functionsClass !== void 0) {
|
|
41566
41862
|
packageImports.push(args.functionsClass);
|
|
41567
41863
|
}
|
|
41568
|
-
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
|
+
];
|
|
41569
41872
|
if (args.functionsClass !== void 0) {
|
|
41570
41873
|
lines.push(` readonly ${functionsFieldName(args.functionsClass)} = inject(${args.functionsClass});`, "");
|
|
41571
41874
|
}
|
|
@@ -42148,7 +42451,17 @@ var ForwardArgsType = type62({
|
|
|
42148
42451
|
});
|
|
42149
42452
|
var TOOL8 = {
|
|
42150
42453
|
name: "dbx_model_fixture_forward",
|
|
42151
|
-
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"),
|
|
42152
42465
|
inputSchema: {
|
|
42153
42466
|
type: "object",
|
|
42154
42467
|
properties: {
|
|
@@ -42627,7 +42940,11 @@ function bucketSummary(group, bucket) {
|
|
|
42627
42940
|
}
|
|
42628
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`).";
|
|
42629
42942
|
}
|
|
42630
|
-
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
|
+
];
|
|
42631
42948
|
function formatMarkdown(report) {
|
|
42632
42949
|
const lines = [];
|
|
42633
42950
|
lines.push(`# Spec convention for \`${report.group}\``, "", `Tests live under \`${report.apiDir}/src/app/function/${report.group}/\`. Canonical name segments:`, "");
|
|
@@ -43407,7 +43724,14 @@ var ARCHETYPE_RECOMMEND_TOOL = {
|
|
|
43407
43724
|
import { type as type70 } from "arktype";
|
|
43408
43725
|
var DBX_MODEL_ARCHETYPE_LOOKUP_TOOL = {
|
|
43409
43726
|
name: "dbx_model_archetype_lookup",
|
|
43410
|
-
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"),
|
|
43411
43735
|
inputSchema: {
|
|
43412
43736
|
type: "object",
|
|
43413
43737
|
properties: {
|
|
@@ -44800,7 +45124,18 @@ function formatStateInteraction(entry) {
|
|
|
44800
45124
|
return result;
|
|
44801
45125
|
}
|
|
44802
45126
|
function formatDirectiveEntry(entry, depth) {
|
|
44803
|
-
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
|
+
];
|
|
44804
45139
|
if (depth === "full") {
|
|
44805
45140
|
lines.push("## Inputs", "", entry.inputs.length > 0 ? formatInputsTable2(entry.inputs) : "_No inputs._", "");
|
|
44806
45141
|
if (entry.outputs.length > 0) {
|
|
@@ -45205,8 +45540,7 @@ import { type Work } from '@dereekb/rxjs';
|
|
|
45205
45540
|
dbxActionForm
|
|
45206
45541
|
[dbxFormSource]="settings$"></my-settings-form>
|
|
45207
45542
|
</div>
|
|
45208
|
-
|
|
45209
|
-
standalone: true
|
|
45543
|
+
\`
|
|
45210
45544
|
})
|
|
45211
45545
|
export class SettingsAutosaveComponent {
|
|
45212
45546
|
private readonly store = inject(SettingsDocumentStore);
|
|
@@ -45250,8 +45584,7 @@ import { type Work } from '@dereekb/rxjs';
|
|
|
45250
45584
|
icon="delete"
|
|
45251
45585
|
dbxActionButton></dbx-button>
|
|
45252
45586
|
</ng-container>
|
|
45253
|
-
|
|
45254
|
-
standalone: true
|
|
45587
|
+
\`
|
|
45255
45588
|
})
|
|
45256
45589
|
export class DeleteAccountButtonComponent {
|
|
45257
45590
|
private readonly accountStore = inject(AccountDocumentStore);
|
|
@@ -45315,8 +45648,7 @@ import { type Work } from '@dereekb/rxjs';
|
|
|
45315
45648
|
<dbx-button text="Archive" dbxActionButton></dbx-button>
|
|
45316
45649
|
</ng-container>
|
|
45317
45650
|
</div>
|
|
45318
|
-
|
|
45319
|
-
standalone: true
|
|
45651
|
+
\`
|
|
45320
45652
|
})
|
|
45321
45653
|
export class ContentToolbarComponent {
|
|
45322
45654
|
private readonly store = inject(ArticleDocumentStore);
|
|
@@ -45370,8 +45702,7 @@ import { type WorkUsingContext } from '@dereekb/rxjs';
|
|
|
45370
45702
|
<dbx-button [raised]="true" text="Save" dbxActionButton></dbx-button>
|
|
45371
45703
|
<dbx-error dbxActionError></dbx-error>
|
|
45372
45704
|
</form>
|
|
45373
|
-
|
|
45374
|
-
standalone: true
|
|
45705
|
+
\`
|
|
45375
45706
|
})
|
|
45376
45707
|
export class EditProfilePageComponent {
|
|
45377
45708
|
private readonly profileStore = inject(ProfileDocumentStore);
|
|
@@ -45441,8 +45772,7 @@ type ConsentDecision = { readonly outcome: 'approved'; readonly value: ConsentFo
|
|
|
45441
45772
|
}
|
|
45442
45773
|
</div>
|
|
45443
45774
|
}
|
|
45444
|
-
|
|
45445
|
-
standalone: true
|
|
45775
|
+
\`
|
|
45446
45776
|
})
|
|
45447
45777
|
export class ConsentPageComponent {
|
|
45448
45778
|
readonly defaults$ = this.store.defaults$;
|
|
@@ -45489,8 +45819,7 @@ import { DbxActionContextMachine } from '@dereekb/dbx-core';
|
|
|
45489
45819
|
<div [dbxActionSource]="actionSource()">
|
|
45490
45820
|
<child-component></child-component>
|
|
45491
45821
|
</div>
|
|
45492
|
-
|
|
45493
|
-
standalone: true
|
|
45822
|
+
\`
|
|
45494
45823
|
})
|
|
45495
45824
|
export class ParentComponent {
|
|
45496
45825
|
readonly machine = new DbxActionContextMachine<MyValue, MyResult>();
|
|
@@ -45510,8 +45839,7 @@ export class ParentComponent {
|
|
|
45510
45839
|
<ng-container dbxAction dbxActionValue="hello">
|
|
45511
45840
|
<dbx-button [raised]="true" text="Submit" dbxActionButton></dbx-button>
|
|
45512
45841
|
</ng-container>
|
|
45513
|
-
|
|
45514
|
-
standalone: true
|
|
45842
|
+
\`
|
|
45515
45843
|
})
|
|
45516
45844
|
export class ChildComponent {}`
|
|
45517
45845
|
},
|
|
@@ -45554,8 +45882,7 @@ import { type IsEqualFunction } from '@dereekb/rxjs';
|
|
|
45554
45882
|
<my-tag-form #form></my-tag-form>
|
|
45555
45883
|
<dbx-button text="Save" dbxActionButton></dbx-button>
|
|
45556
45884
|
</div>
|
|
45557
|
-
|
|
45558
|
-
standalone: true
|
|
45885
|
+
\`
|
|
45559
45886
|
})
|
|
45560
45887
|
export class TagEditorComponent {
|
|
45561
45888
|
private readonly store = inject(TagCollectionStore);
|
|
@@ -45571,7 +45898,15 @@ export class TagEditorComponent {
|
|
|
45571
45898
|
};
|
|
45572
45899
|
|
|
45573
45900
|
// packages/dbx-components-mcp/src/tools/data/patterns/action-patterns.ts
|
|
45574
|
-
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
|
+
];
|
|
45575
45910
|
function getActionExamplePattern(slug) {
|
|
45576
45911
|
const lowered = slug.trim().toLowerCase();
|
|
45577
45912
|
return ACTION_EXAMPLE_PATTERNS.find((p) => p.slug === lowered);
|
|
@@ -45895,7 +46230,27 @@ function renderScaffold3(args) {
|
|
|
45895
46230
|
const directivesUsed = buildDirectivesUsed(args);
|
|
45896
46231
|
const examplePattern = relatedExamplePattern(args);
|
|
45897
46232
|
const lines = [];
|
|
45898
|
-
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
|
+
);
|
|
45899
46254
|
for (const line of imports) {
|
|
45900
46255
|
lines.push(line);
|
|
45901
46256
|
}
|
|
@@ -46339,7 +46694,13 @@ function formatMissingRouteModelLine(param) {
|
|
|
46339
46694
|
// packages/dbx-components-mcp/src/tools/route-lookup.tool.ts
|
|
46340
46695
|
var DBX_ROUTE_LOOKUP_TOOL = {
|
|
46341
46696
|
name: "dbx_route_lookup",
|
|
46342
|
-
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"),
|
|
46343
46704
|
inputSchema: {
|
|
46344
46705
|
type: "object",
|
|
46345
46706
|
properties: {
|
|
@@ -46599,7 +46960,11 @@ var ROUTE_LOOKUP_TOOL = {
|
|
|
46599
46960
|
import { type as type82 } from "arktype";
|
|
46600
46961
|
var DBX_ROUTE_SEARCH_TOOL = {
|
|
46601
46962
|
name: "dbx_route_search",
|
|
46602
|
-
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"),
|
|
46603
46968
|
inputSchema: {
|
|
46604
46969
|
type: "object",
|
|
46605
46970
|
properties: {
|
|
@@ -47691,7 +48056,25 @@ function formatScaffold2(args) {
|
|
|
47691
48056
|
if (args.presetKeys.length > 0) {
|
|
47692
48057
|
lines.push("## Presets", "", "```ts", renderPresetConstants(args), "```", "");
|
|
47693
48058
|
}
|
|
47694
|
-
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
|
+
);
|
|
47695
48078
|
if (args.presetKeys.length > 0) {
|
|
47696
48079
|
lines.push("- Each `ClickableFilterPreset` listed in the preset array becomes a chip; clicking sets `presetValue` on the filter source.");
|
|
47697
48080
|
}
|
|
@@ -47758,7 +48141,20 @@ function formatArgsTable(args) {
|
|
|
47758
48141
|
return lines.join("\n");
|
|
47759
48142
|
}
|
|
47760
48143
|
function formatEntry4(entry, depth) {
|
|
47761
|
-
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
|
+
];
|
|
47762
48158
|
if (depth === "full") {
|
|
47763
48159
|
if (entry.args.length > 0) {
|
|
47764
48160
|
lines.push("## Args", "", formatArgsTable(entry.args), "");
|
|
@@ -47920,7 +48316,20 @@ function formatSearchResults5(input) {
|
|
|
47920
48316
|
const lines = [`# Search: \`${query}\``, "", `Tokens: \`${tokenDisplay}\` \xB7 ${hits.length} result${hits.length === 1 ? "" : "s"}`, ""];
|
|
47921
48317
|
for (const hit of hits) {
|
|
47922
48318
|
const entry = hit.entry;
|
|
47923
|
-
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
|
+
);
|
|
47924
48333
|
}
|
|
47925
48334
|
return lines.join("\n").trimEnd();
|
|
47926
48335
|
}
|
|
@@ -48196,7 +48605,20 @@ function formatCatalogHit(input) {
|
|
|
48196
48605
|
const tagDisplay = entry.tags.length > 0 ? `
|
|
48197
48606
|
- **tags:** ${tagBadges}` : "";
|
|
48198
48607
|
const badge = options.headerBadge ?? "";
|
|
48199
|
-
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
|
+
];
|
|
48200
48622
|
}
|
|
48201
48623
|
function formatCatalogSearchResults(input) {
|
|
48202
48624
|
const { query, tokens, hits, filters, entityLabel, emptyHint, hitOptions } = input;
|
|
@@ -48372,7 +48794,20 @@ function appendFullEntrySections2(lines, entry) {
|
|
|
48372
48794
|
}
|
|
48373
48795
|
function formatEntry6(entry, depth) {
|
|
48374
48796
|
const heading = `# ${entry.name}`;
|
|
48375
|
-
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
|
+
];
|
|
48376
48811
|
appendDeprecationNotice2(lines, entry);
|
|
48377
48812
|
if (depth === "full") {
|
|
48378
48813
|
appendFullEntrySections2(lines, entry);
|
|
@@ -49702,7 +50137,14 @@ function appendTaggedSections(lines, tagged) {
|
|
|
49702
50137
|
}
|
|
49703
50138
|
function appendUntaggedSection(lines, untagged) {
|
|
49704
50139
|
if (untagged.length === 0) return;
|
|
49705
|
-
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
|
+
);
|
|
49706
50148
|
for (const candidate of untagged) {
|
|
49707
50149
|
lines.push(`| \`${candidate.name}\` | \`${candidate.subpath}:${candidate.line}\` |`);
|
|
49708
50150
|
}
|
|
@@ -50402,7 +50844,14 @@ function formatReportAsMarkdown17(report) {
|
|
|
50402
50844
|
} else {
|
|
50403
50845
|
lines.push("## \u2705 In sync", "");
|
|
50404
50846
|
}
|
|
50405
|
-
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
|
+
);
|
|
50406
50855
|
if (report.violations.length > 0) {
|
|
50407
50856
|
appendViolationSection(
|
|
50408
50857
|
lines,
|
|
@@ -50820,7 +51269,10 @@ function renderStorageFilePurpose(input, ctx) {
|
|
|
50820
51269
|
tokens: ctx.tokens,
|
|
50821
51270
|
files: [componentFile, handlerFile],
|
|
50822
51271
|
wiring,
|
|
50823
|
-
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
|
+
)
|
|
50824
51276
|
};
|
|
50825
51277
|
return result;
|
|
50826
51278
|
}
|
|
@@ -50828,19 +51280,28 @@ function renderNotificationTemplate(input, ctx) {
|
|
|
50828
51280
|
const componentFile = {
|
|
50829
51281
|
status: "append",
|
|
50830
51282
|
path: applyTokens("<<componentDir>>/src/lib/model/notification/notification.ts", ctx),
|
|
50831
|
-
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
|
+
),
|
|
50832
51287
|
content: applyTokens(NOTIFICATION_TEMPLATE_COMPONENT_TEMPLATE, ctx)
|
|
50833
51288
|
};
|
|
50834
51289
|
const factoryFile = {
|
|
50835
51290
|
status: "append",
|
|
50836
51291
|
path: applyTokens("<<apiDir>>/src/app/common/model/notification/notification.factory.ts", ctx),
|
|
50837
|
-
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
|
+
),
|
|
50838
51296
|
content: applyTokens(NOTIFICATION_TEMPLATE_FACTORY_TEMPLATE, ctx)
|
|
50839
51297
|
};
|
|
50840
51298
|
const wiring = [
|
|
50841
51299
|
{
|
|
50842
51300
|
file: applyTokens("<<apiDir>>/src/app/common/model/notification/notification.factory.ts", ctx),
|
|
50843
|
-
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
|
+
),
|
|
50844
51305
|
snippet: applyTokens(NOTIFICATION_TEMPLATE_WIRING_SNIPPET, ctx)
|
|
50845
51306
|
}
|
|
50846
51307
|
];
|
|
@@ -50849,7 +51310,10 @@ function renderNotificationTemplate(input, ctx) {
|
|
|
50849
51310
|
tokens: ctx.tokens,
|
|
50850
51311
|
files: [componentFile, factoryFile],
|
|
50851
51312
|
wiring,
|
|
50852
|
-
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
|
+
)
|
|
50853
51317
|
};
|
|
50854
51318
|
return result;
|
|
50855
51319
|
}
|
|
@@ -50874,7 +51338,10 @@ function renderNotificationTask(input, ctx) {
|
|
|
50874
51338
|
const wiring = [
|
|
50875
51339
|
{
|
|
50876
51340
|
file: applyTokens("<<apiDir>>/src/app/common/model/notification/notification.task.service.ts", ctx),
|
|
50877
|
-
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
|
+
),
|
|
50878
51345
|
snippet: applyTokens(NOTIFICATION_TASK_WIRING_SNIPPET, ctx)
|
|
50879
51346
|
}
|
|
50880
51347
|
];
|
|
@@ -50883,7 +51350,10 @@ function renderNotificationTask(input, ctx) {
|
|
|
50883
51350
|
tokens: ctx.tokens,
|
|
50884
51351
|
files: [componentFile, handlerFile],
|
|
50885
51352
|
wiring,
|
|
50886
|
-
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
|
+
)
|
|
50887
51357
|
};
|
|
50888
51358
|
return result;
|
|
50889
51359
|
}
|
|
@@ -51148,7 +51618,11 @@ var STORAGEFILE_PURPOSE = {
|
|
|
51148
51618
|
heading: "API upload handler",
|
|
51149
51619
|
path: "<apiDir>/src/app/common/model/storagefile/handlers/upload.<name>.ts",
|
|
51150
51620
|
altPaths: ["Inline in `<apiDir>/src/app/common/model/storagefile/storagefile.upload.service.ts` for tiny workspaces."],
|
|
51151
|
-
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")
|
|
51152
51626
|
},
|
|
51153
51627
|
{
|
|
51154
51628
|
heading: "API processor (only if subtasks declared)",
|
|
@@ -51210,12 +51684,27 @@ var STORAGEFILE_PROCESSOR = {
|
|
|
51210
51684
|
{
|
|
51211
51685
|
heading: "API processor handler",
|
|
51212
51686
|
path: "<apiDir>/src/app/common/model/notification/handlers/storagefile/task.handler.storagefile.<name>.ts",
|
|
51213
|
-
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")
|
|
51214
51701
|
},
|
|
51215
51702
|
{
|
|
51216
51703
|
heading: "Wire into processing handler",
|
|
51217
51704
|
path: "<apiDir>/src/app/common/model/notification/handlers/task.handler.storagefile.ts",
|
|
51218
|
-
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")
|
|
51219
51708
|
}
|
|
51220
51709
|
],
|
|
51221
51710
|
seeAlso: ["storagefile-purpose", "storagefile-processor-subtask", "notification-task"],
|
|
@@ -51229,7 +51718,12 @@ var STORAGEFILE_PROCESSOR_SUBTASK = {
|
|
|
51229
51718
|
{
|
|
51230
51719
|
heading: "Component declarations",
|
|
51231
51720
|
path: "<componentDir>/src/lib/model/storagefile/storagefile.<name>.ts (the file that already declares the purpose)",
|
|
51232
|
-
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")
|
|
51233
51727
|
},
|
|
51234
51728
|
{
|
|
51235
51729
|
heading: "API processor flow",
|
|
@@ -51261,13 +51755,19 @@ var NOTIFICATION_TEMPLATE = {
|
|
|
51261
51755
|
{
|
|
51262
51756
|
heading: "Component aggregator",
|
|
51263
51757
|
path: "<componentDir>/src/lib/model/notification/notification.ts",
|
|
51264
|
-
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")
|
|
51265
51761
|
},
|
|
51266
51762
|
{
|
|
51267
51763
|
heading: "API handler factory",
|
|
51268
51764
|
path: "<apiDir>/src/app/common/model/notification/notification.factory.ts",
|
|
51269
51765
|
altPaths: ["<apiDir>/src/app/common/model/notification/notification.factory.<name>.ts (split per-template for larger workspaces)"],
|
|
51270
|
-
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")
|
|
51271
51771
|
}
|
|
51272
51772
|
],
|
|
51273
51773
|
seeAlso: ["notification-task"],
|
|
@@ -51296,7 +51796,17 @@ var NOTIFICATION_TASK = {
|
|
|
51296
51796
|
heading: "API handler config",
|
|
51297
51797
|
path: "<apiDir>/src/app/common/model/notification/notification.task.service.ts",
|
|
51298
51798
|
altPaths: ["<apiDir>/src/app/common/model/notification/handlers/task.handler.<name>.ts (per-task split for larger workspaces)"],
|
|
51299
|
-
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")
|
|
51300
51810
|
},
|
|
51301
51811
|
{
|
|
51302
51812
|
heading: "Wire into task service",
|
|
@@ -52219,7 +52729,12 @@ function formatFirestoreRulesScanReportAsMarkdown(report) {
|
|
|
52219
52729
|
}
|
|
52220
52730
|
lines.push("");
|
|
52221
52731
|
if (report.serverOnly.length > 0) {
|
|
52222
|
-
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
|
+
);
|
|
52223
52738
|
}
|
|
52224
52739
|
if (report.gettableNotListable.length > 0) {
|
|
52225
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.", "");
|
|
@@ -52457,7 +52972,15 @@ function validateModelServerOnly(input) {
|
|
|
52457
52972
|
}
|
|
52458
52973
|
for (const iface of input.interfaces) {
|
|
52459
52974
|
if (iface.serverOnly && !iface.hasModelTag) {
|
|
52460
|
-
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
|
+
);
|
|
52461
52984
|
}
|
|
52462
52985
|
}
|
|
52463
52986
|
const errorCount = violations.filter((v) => v.severity === "error").length;
|
|
@@ -52479,22 +53002,78 @@ function appendServiceViolations(input) {
|
|
|
52479
53002
|
const { modelType, collection, tag, flag, rules } = reconciliation;
|
|
52480
53003
|
const declared = tag === true || flag;
|
|
52481
53004
|
if (collection == null) {
|
|
52482
|
-
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
|
+
);
|
|
52483
53014
|
} else if (rules === true && !flag) {
|
|
52484
|
-
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
|
+
);
|
|
52485
53024
|
} else if (rules === false && declared) {
|
|
52486
|
-
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
|
+
);
|
|
52487
53034
|
}
|
|
52488
53035
|
if (rules === true && iface != null && !iface.serverOnly) {
|
|
52489
|
-
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
|
+
);
|
|
52490
53045
|
}
|
|
52491
53046
|
if (iface == null && flag) {
|
|
52492
|
-
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
|
+
);
|
|
52493
53056
|
} else if (iface != null && tag !== flag) {
|
|
52494
|
-
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
|
+
);
|
|
52495
53066
|
}
|
|
52496
53067
|
if (manifestModelTypes != null && declared && !manifestModelTypes.has(modelType)) {
|
|
52497
|
-
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
|
+
);
|
|
52498
53077
|
}
|
|
52499
53078
|
}
|
|
52500
53079
|
function legsAgree(input) {
|
|
@@ -52603,7 +53182,6 @@ async function walkTypeScriptFiles(dir) {
|
|
|
52603
53182
|
try {
|
|
52604
53183
|
entries = (await readdir12(dir)).sort();
|
|
52605
53184
|
} catch {
|
|
52606
|
-
return out;
|
|
52607
53185
|
}
|
|
52608
53186
|
for (const entry of entries) {
|
|
52609
53187
|
if (entry === "node_modules" || entry === "dist") continue;
|
|
@@ -52884,7 +53462,7 @@ function relativeOrAbsolute(from, target) {
|
|
|
52884
53462
|
const rel = relative20(from, target);
|
|
52885
53463
|
if (rel.length === 0) return ".";
|
|
52886
53464
|
if (rel.startsWith("..")) return target;
|
|
52887
|
-
return rel.
|
|
53465
|
+
return rel.replaceAll("\\", "/");
|
|
52888
53466
|
}
|
|
52889
53467
|
|
|
52890
53468
|
// packages/dbx-components-mcp/src/tools/mcp-config/init.ts
|
|
@@ -53013,19 +53591,19 @@ async function planRootConfig(input) {
|
|
|
53013
53591
|
}
|
|
53014
53592
|
function defaultOutFor(input) {
|
|
53015
53593
|
const targetAbs = resolve66(input.snapshot.workspaceRoot, ".tmp/dbx-mcp", `${input.slug}.${clusterFilenameSegment(input.cluster)}.json`);
|
|
53016
|
-
return relative21(dirname22(input.scanConfigPath), targetAbs).
|
|
53594
|
+
return relative21(dirname22(input.scanConfigPath), targetAbs).replaceAll("\\", "/");
|
|
53017
53595
|
}
|
|
53018
53596
|
function resolveScanOut(scanConfigPath, out) {
|
|
53019
53597
|
return resolve66(dirname22(scanConfigPath), out);
|
|
53020
53598
|
}
|
|
53021
53599
|
function relativeFromConfig(snapshot, absolute) {
|
|
53022
53600
|
const configDir = snapshot.configPath === null ? snapshot.workspaceRoot : dirname22(snapshot.configPath);
|
|
53023
|
-
return relative21(configDir, absolute).
|
|
53601
|
+
return relative21(configDir, absolute).replaceAll("\\", "/");
|
|
53024
53602
|
}
|
|
53025
53603
|
function workspaceRel(workspaceRoot, absolute) {
|
|
53026
53604
|
const rel = relative21(workspaceRoot, absolute);
|
|
53027
53605
|
if (rel.length === 0) return ".";
|
|
53028
|
-
return rel.
|
|
53606
|
+
return rel.replaceAll("\\", "/");
|
|
53029
53607
|
}
|
|
53030
53608
|
function serializeJson(value) {
|
|
53031
53609
|
return `${JSON.stringify(value, null, 2)}
|
|
@@ -55618,7 +56196,13 @@ async function tryReadBatchPath(path, cwd, reader) {
|
|
|
55618
56196
|
import { type as type104 } from "arktype";
|
|
55619
56197
|
var DBX_AUTH_CLAIM_LOOKUP_TOOL = {
|
|
55620
56198
|
name: "dbx_auth_claim_lookup",
|
|
55621
|
-
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"),
|
|
55622
56206
|
inputSchema: {
|
|
55623
56207
|
type: "object",
|
|
55624
56208
|
properties: {
|
|
@@ -55841,7 +56425,14 @@ function sourceLink2(path, line) {
|
|
|
55841
56425
|
import { type as type106 } from "arktype";
|
|
55842
56426
|
var DBX_AUTH_ROLE_LOOKUP_TOOL = {
|
|
55843
56427
|
name: "dbx_auth_role_lookup",
|
|
55844
|
-
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"),
|
|
55845
56436
|
inputSchema: {
|
|
55846
56437
|
type: "object",
|
|
55847
56438
|
properties: {
|
|
@@ -56024,7 +56615,11 @@ function sourceLink3(path, line) {
|
|
|
56024
56615
|
import { type as type107 } from "arktype";
|
|
56025
56616
|
var DBX_AUTH_TOKEN_EXPLAIN_TOOL = {
|
|
56026
56617
|
name: "dbx_auth_token_explain",
|
|
56027
|
-
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"),
|
|
56028
56623
|
inputSchema: {
|
|
56029
56624
|
type: "object",
|
|
56030
56625
|
properties: {
|
|
@@ -56483,7 +57078,13 @@ function createAuthClusterTools(registry) {
|
|
|
56483
57078
|
function registerTools(server, options = {}) {
|
|
56484
57079
|
const underlyingServer = server.server;
|
|
56485
57080
|
const tools = [...DBX_TOOLS];
|
|
56486
|
-
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
|
+
);
|
|
56487
57088
|
if (options.forgeFieldRegistry !== void 0) {
|
|
56488
57089
|
tools.push(createLookupFormTool({ registry: options.forgeFieldRegistry }), createSearchFormTool({ registry: options.forgeFieldRegistry }), createFormScaffoldTool({ registry: options.forgeFieldRegistry }));
|
|
56489
57090
|
}
|
|
@@ -56737,7 +57338,25 @@ async function loadServerContext(options = {}) {
|
|
|
56737
57338
|
await emitDownstreamHints({ cwd, externalCounts, onDownstreamHints: options.onDownstreamHints });
|
|
56738
57339
|
const context = {
|
|
56739
57340
|
resources: { semanticTypeRegistry: registry, forgeFieldRegistry: forgeRegistry, pipeRegistry, utilRegistry, modelSnapshotFieldRegistry, modelFirebaseIndexRegistry, uiComponentRegistry: uiRegistry, actionRegistry, filterRegistry, tokenRegistry, cssUtilityRegistry, authRegistry, cwd },
|
|
56740
|
-
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
|
+
}
|
|
56741
57360
|
};
|
|
56742
57361
|
return context;
|
|
56743
57362
|
}
|
|
@@ -56841,7 +57460,14 @@ async function resolveAuthRegistry(input) {
|
|
|
56841
57460
|
}
|
|
56842
57461
|
function reportAuthLoaderResult(result) {
|
|
56843
57462
|
const { registry, scannedFiles, fileWarnings, extractWarnings, extractedAppCount, extractedClaimCount } = result;
|
|
56844
|
-
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");
|
|
56845
57471
|
process.stderr.write(`${summary}
|
|
56846
57472
|
`);
|
|
56847
57473
|
for (const warning of fileWarnings) {
|