@ethisyscore/eslint-plugin-coreconnect 1.77.0 → 1.79.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@ethisyscore/eslint-plugin-coreconnect",
3
- "version": "1.77.0",
3
+ "version": "1.79.0",
4
4
  "description": "ESLint rules enforcing EthisysCore plugin frontend conventions. Published so a new rule reaches every plugin on a version bump, rather than being copied into each scaffold and drifting.",
5
5
  "type": "module",
6
6
  "main": "src/index.js",
package/src/index.js CHANGED
@@ -1,6 +1,7 @@
1
1
  import noHardcodedColors from "./rules/no-hardcoded-colors.js";
2
2
  import noInlineObjectType from "./rules/no-inline-object-type.js";
3
3
  import noInlineStringUnion from "./rules/no-inline-string-union.js";
4
+ import noLocalCompletenessBanner from "./rules/no-local-completeness-banner.js";
4
5
  import noLocalDataGrid from "./rules/no-local-data-grid.js";
5
6
  import noLocalDateInput from "./rules/no-local-date-input.js";
6
7
  import noLocalEmptyState from "./rules/no-local-empty-state.js";
@@ -12,6 +13,7 @@ const rules = {
12
13
  "no-hardcoded-colors": noHardcodedColors,
13
14
  "no-inline-object-type": noInlineObjectType,
14
15
  "no-inline-string-union": noInlineStringUnion,
16
+ "no-local-completeness-banner": noLocalCompletenessBanner,
15
17
  "no-local-data-grid": noLocalDataGrid,
16
18
  "no-local-date-input": noLocalDateInput,
17
19
  "no-local-empty-state": noLocalEmptyState,
@@ -0,0 +1,92 @@
1
+ /**
2
+ * Disallow a plugin declaring its own connector mapping-completeness banner.
3
+ *
4
+ * The second backstop to the Tool Mappings promotion, and the one that shows why these rules report
5
+ * rather than warn. `MappingCompletenessBanner` moved into
6
+ * `@ethisyscore/plugin-ui/components/connectors` in 1.71.6, and `timesheets` adopted it. Measured on
7
+ * origin/develop months later, `self-healing` and `tech` still carried their own - 126 and 155 lines
8
+ * - and one of them had drifted on the string that matters most:
9
+ *
10
+ * - `self-healing`'s optional-gap alert is titled "Optional operations not mapped". The SDK's, and
11
+ * `tech`'s, read "Optional operations aren't fully mapped".
12
+ *
13
+ * That reads like a wording nit and is not. BOTH alerts select on
14
+ * `unmappedRequiredCanonicalFields`, i.e. on FIELD mapping, but "not mapped" describes TOOL mapping,
15
+ * which is a binary an operator can close with one button. So operators pressed auto-match - which
16
+ * picks the tool and cannot touch its fields - watched the count refuse to move, and raised the
17
+ * banner as broken. It was right; the sentence describing it was wrong, and the cost was paid in
18
+ * support round-trips rather than in a stack trace. Nothing was ever going to catch that in review,
19
+ * because reviewing a copy tells you nothing about the copy next door.
20
+ *
21
+ * Fires on a DECLARATION, never a use, so importing `MappingCompletenessBanner` from
22
+ * `@ethisyscore/plugin-ui/components/connectors` and rendering it is exactly what the rule wants -
23
+ * as is a local wrapper that composes it, which the shared helper exempts.
24
+ *
25
+ * ## Why there is no companion rule for the selectors
26
+ *
27
+ * `mappingCompleteness.ts` - `selectRequiredIncompleteOperations`,
28
+ * `selectOptionalIncompleteOperations`, `selectMappingReadiness` - is duplicated in the same two
29
+ * plugins, and it is where the field-vs-tool predicate actually lives, so it looks like the more
30
+ * dangerous copy. Measured, it is not:
31
+ *
32
+ * - Both plugin copies are BEHAVIOURALLY IDENTICAL to the promoted ones. Diffed against the SDK,
33
+ * they differ only by the type import's source and by selectors they never used being absent.
34
+ * Three copies, months, zero logic drift. What drifted was the prose explaining the predicate to
35
+ * an operator - which lives in the banner, and is what this rule guards.
36
+ * - The local selectors have exactly ONE consumer each, the local banner beside them. No page,
37
+ * hook or service imports them, and `tech`'s `selectIncompleteOperations` is referenced by
38
+ * nothing but its own docstring. So this rule already closes the duplication: `timesheets`,
39
+ * the one plugin that adopted the shared banner, deleted its whole `components/connectors`
40
+ * directory in the same move.
41
+ * - `createNoLocalComponentRule` could not express it anyway. It gates on `isComponentName`
42
+ * (`^[A-Z]`) and on a component-shaped initializer, and a selector is neither. A selectors rule
43
+ * would be new machinery for no coverage any measured file lacks.
44
+ *
45
+ * If a plugin ever imports those selectors somewhere other than its own banner, that reasoning stops
46
+ * holding and the rule becomes worth adding. Nothing in the estate does today.
47
+ *
48
+ * @type {import("eslint").Rule.RuleModule}
49
+ */
50
+ import { createNoLocalComponentRule } from "./componentDeclarations.js";
51
+
52
+ export default createNoLocalComponentRule({
53
+ // Both a MAPPING context AND the literal `Completeness` AND a banner-shaped suffix. All three are
54
+ // load-bearing, and each was chosen against a count rather than a guess.
55
+ //
56
+ // A bare `Banner$` is unusable: the estate declares 25 other banners, among them
57
+ // `ConnectorWarningBanner`, `AiPlatformStatusBanner`, `PluginRuntimeBanner`, `ErrorBanner`,
58
+ // `SuspiciousActivityBanner`, `PinnedMessagesBanner` and `InvoiceWarningsBanner` - the first of
59
+ // which sits on the very same Tool Mappings page. Requiring `Completeness` spares all of them.
60
+ //
61
+ // A bare `Completeness(Banner|Alert)$` is no better, because `Completeness` is NOT this feature's
62
+ // private word. `repo-monitoring` ships a `DataCompleteness` union ("Complete" |
63
+ // "PartialFallbackApplied" | "Degraded") in both the host contracts and `tech`, and a
64
+ // `DataCompletenessBanner` warning that commit data is degraded is exactly the component that
65
+ // would appear next. So the qualifier alternation is required, and it is the same shape
66
+ // `no-local-mapping-editor` uses: `Mapping` normally, `Connector` or `Tool` for a rename that
67
+ // drops it. A prefixed spelling needs no branch of its own - `ToolMappingCompletenessBanner` ends
68
+ // in the branch-one form already.
69
+ //
70
+ // The suffix set is likewise measured. `Alert` is the MUI component the banner literally renders,
71
+ // so it is the likeliest rename; `Notice` and `Callout` are estate vocabulary (`DeveloperCallout`).
72
+ // `(View|Panel)` is optional so the adapter-view spelling matches. What is deliberately EXCLUDED
73
+ // is the rest of the `MappingCompleteness*` family, which is legitimately duplicated everywhere as
74
+ // wire contracts: `MappingCompletenessItem` (112 references), `MappingCompletenessResult` (34),
75
+ // `ConnectorMappingCompletenessResult`, `GetToolMappingCompletenessResponse` and
76
+ // `MappingCompletenessBannerProps`. Anchoring on a banner-shaped suffix is what keeps them out.
77
+ //
78
+ // Hooks are excluded by the shared helper, which matters here: `useConnectorMappingCompleteness`
79
+ // is a real hook in three repos. Lowercase names are excluded too, which covers
80
+ // `getMappingCompleteness`, `mappingCompleteness`, `dataCompleteness` and
81
+ // `invalidateMappingsAndCompleteness`.
82
+ pattern: /(Mappings?|Connector|Tool)Completeness(Banner|Alert|Notice|Callout)(View|Panel)?$/,
83
+ canonical: {
84
+ source: "@ethisyscore/plugin-ui/components/connectors",
85
+ name: "MappingCompletenessBanner",
86
+ },
87
+ messageId: "noLocalCompletenessBanner",
88
+ description:
89
+ "Disallow a locally-declared mapping completeness banner - import MappingCompletenessBanner from @ethisyscore/plugin-ui/components/connectors",
90
+ message:
91
+ "'{{name}}' re-declares the mapping completeness banner. Import { MappingCompletenessBanner } from '@ethisyscore/plugin-ui/components/connectors' instead: a local copy had already drifted to \"Optional operations not mapped\", which describes TOOL mapping when the alert selects on unmapped FIELDS - so operators pressed auto-match, saw the count refuse to move, and reported the banner as broken. If this reports completeness for something other than connector mappings, rename it so it does not read as one.",
92
+ });