@starci/eslint-canon-fe 8.2.16 → 8.2.18

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/CHANGELOG.md CHANGED
@@ -1,5 +1,13 @@
1
1
  # Changelog
2
2
 
3
+ ## 8.2.18 - 2026-10-14
4
+
5
+ - Changed: carry the canon pins of eslint-canon-be 3.2.19, HFS 4.2.17 and CLI 1.2.17 in the runtime mirror. ESLint rule implementations are unchanged.
6
+
7
+ ## 8.2.17 - 2026-10-14
8
+
9
+ - Fixed: `dead-exports` (`HFS_UNUSED_EXPORT`) counts an import by another public entry the slot declares in `entries` as a use: a `graphql/{queries,mutations}/types/<x>.ts` export its operation file `query-<x>.ts`/`mutation-<x>.ts` imports is part of that operation's signature and is no longer reported. A barrel or any other non-entry file of the owner still does not count, so an operation reached only through a re-export of `modules/api/index.ts` stays dead.
10
+
3
11
  ## 8.2.16 - 2026-10-13
4
12
 
5
13
  - Changed: the heroicons family is the one application icon set again (owner ruling 2026-10-13): `heroicons-is-the-glyph-vendor` admits exactly `@heroicons/react/24/outline`, `@heroicons/react/16/solid` and the matching `@starci/heroicons` subpaths (read from the fe-stack `icons` concern), and refuses Phosphor and every other glyph catalogue, a package the catalog forbids included; `one-icon-set` (8.2.15) is gone. Only the Icon leaf and the toast module import a glyph. The bundled runtime mirror carries the fe-stack catalog and the canon pins with `@heroicons/react` 2.2.0 and `@starci/heroicons` 0.3.3.
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@starci/eslint-canon-fe",
3
- "version": "8.2.16",
3
+ "version": "8.2.18",
4
4
  "description": "The StarCi front-end lint canon: every rule its laws publish, gathered as one flat-config plugin and attached by one factory.",
5
5
  "type": "module",
6
6
  "license": "MIT",
@@ -29,14 +29,14 @@ pins:
29
29
  source: packages/grammar/package.json
30
30
  why: 'the grammar front ends read: the brand layer sets `--font-sans` and `--font-mono` and the grammar reads them; it carries the Input tel kind, the copyable and read-only Input, the IconButton disclosure props and count badge, ChatBubble, FillViewport, Textarea mentions and Enter-to-send, the leading PrimaryRailLayout rail, the plate-less Image, ListRow, the soft Alert, Progress steps, the floating primary IconButton, FlowGraph, the STATE-8 loading views and the bordered selectable TagGroup chips. The runtime source version wins for a @starci package.'
31
31
  '@starci/eslint-canon-be':
32
- version: 3.2.17
32
+ version: 3.2.19
33
33
  group: starci
34
34
  install: registry
35
35
  side: be
36
36
  source: packages/eslint/be/package.json
37
37
  why: 'the back-end ESLint canon: `loadHfs(import.meta.url)` of be/eslint.config.mjs finds the app-root hfs.json (kind app) and gives every linted file the view of the be side, and the project graph is built per side; its bundled runtime copies (slots, canon-pins, failure codes, the architecture machine) put .starcistacks and .sops.yaml at the app root; `starciBeConfig({ hfs: loadHfs(import.meta.url) })` is the typed factory and the BE-CONVENTION laws are its rules.'
38
38
  '@starci/eslint-canon-fe':
39
- version: 8.2.16
39
+ version: 8.2.18
40
40
  group: starci
41
41
  install: registry
42
42
  side: fe
@@ -76,14 +76,14 @@ pins:
76
76
  source: packages/test-world/package.json
77
77
  why: 'the shared e2e library of every back end (R47, R48), a devDependency of every back end: the warm stack behind toxiproxy, the network-edge fakes, the Nest boot, the typed useTestWorld handle, useSandbox for contract specs and the outage lock. Kafka is real own infra (the one digest-pinned apache/kafka KRaft image, a broker listener and proxy per slot, slot-prefixed topics, consumer groups and client ids) and Postgres is schema-per-context (a schema and a login role per context in a shared database). Each worker has its own data slot (<ns>_w<k>) with its own databases, Keycloak realm, leased Redis DB, bucket/collection/topic prefixes, fakes host, toxiproxy proxies and outage lock; a run provisions min(jest workers, workers cap, default 2) slots, and state file protocol 2 pairs exactly with @starci/jest-preset (TEST_WORLD_PAIR_MISMATCH otherwise). A modules world boots real peer apps beside its modules, world.apps.<name>.during(fn) is the outage of a peer app, w.keycloak.clientSecret(client) answers the run-generated secret of a confidential realm client, world.keycloak.events/sessions read the realm user events and live sessions, world.infra.postgresql.connection(name) takes one database connection down while the others serve, and world.resolve and scope.resolve take any Nest token. Every path a declaration names (`stack`, seeds, the realm, Dockerfiles) resolves from the app root (the directory of hfs.json); the globalSetup registers the path aliases as TypeScript resolves them. Its CLI main is exported at ./cli for starci app stack.'
78
78
  '@starci/cli':
79
- version: 1.2.15
79
+ version: 1.2.17
80
80
  group: starci
81
81
  install: registry
82
82
  side: both
83
83
  source: packages/cli/package.json
84
84
  why: 'the only StarCi command package; product repositories install it for starci app, while @starci/hfs remains its pinned transitive implementation.'
85
85
  '@starci/hfs':
86
- version: 4.2.15
86
+ version: 4.2.17
87
87
  group: starci
88
88
  install: registry
89
89
  side: both
@@ -9,7 +9,10 @@
9
9
  *
10
10
  * Owner = every graph.ownerRoots unit whose tier is not `app` and whose public entry file is in the graph (index.ts or
11
11
  * index.tsx, a package's src/index.ts). The `entries` its slot declares (slot data: a second public entry file of the owner) are further
12
- * public entries of the same owner, judged exactly like the entry. The exported names of an entry are its named exports, `export { a as b }`,
12
+ * public entries of the same owner, judged like the entry, with one addition: a name another declared entry of the owner
13
+ * imports (not re-exports) is used, because it is part of that entry's public signature (an operation file
14
+ * `graphql/queries/query-<x>.ts` typing its request and answer with `graphql/queries/types/<x>.ts`). A barrel or any other
15
+ * non-entry file of the owner still does not count. The exported names of an entry are its named exports, `export { a as b }`,
13
16
  * `export { x } from`, declarations with `export`, and `default` for `export default`. `export *` is refused by owners.mjs
14
17
  * and contributes no name here.
15
18
  *
@@ -330,11 +333,22 @@ const unusedExportViolation = (publicEntry, name, line, owner) => ({
330
333
  message: `${publicEntry} exports ${name}, but no production file outside ${owner.root || 'the repository root'} imports it; remove the export (only a unit role spec, a fixture builder, a test world file, or an integration spec of this integration's own provider folder counts besides production files).`,
331
334
  });
332
335
 
333
- /** The unused-export violations and the export count of one public entry of an owner. */
334
- const publicEntryExports = (context, key, owner, publicEntry) => {
336
+ /** Adds to `used` what another declared entry of the same owner imports from `publicEntry`: that entry's signature makes it public. */
337
+ const addSiblingEntryUses = (context, publicEntry, declared, used) => {
338
+ for (const edge of context.edgesTo.get(publicEntry) ?? []) {
339
+ if (edge.reexport || edge.from === publicEntry || !declared.has(edge.from)) continue;
340
+ const bindings = bindingsOf(context.ts, edge.edge.declaration);
341
+ if (bindings.all) used.all = true;
342
+ for (const pair of bindings.pairs) used.names.add(pair.imported);
343
+ }
344
+ };
345
+
346
+ /** The unused-export violations and the export count of one public entry of an owner (`declared`: its slot's extra entries). */
347
+ const publicEntryExports = (context, key, owner, publicEntry, declared) => {
335
348
  const names = exportedNames(context.ts, context.graph.files.get(publicEntry).sourceFile);
336
349
  const used = consumedNames(context, publicEntry, key, 0, new Set([publicEntry]));
337
350
  addReexportedUses(context, key, publicEntry, used);
351
+ if (declared.has(publicEntry)) addSiblingEntryUses(context, publicEntry, declared, used);
338
352
  const violations = [...names]
339
353
  .filter(([name]) => !(used.all || used.names.has(name)))
340
354
  .map(([name, line]) => unusedExportViolation(publicEntry, name, line, owner));
@@ -351,8 +365,9 @@ const ownerExportFindings = (context, config) => {
351
365
  const entry = entryOf(graph, config, key, owner);
352
366
  if (!entry) continue;
353
367
  owners += 1;
354
- for (const publicEntry of [entry, ...extraEntries(graph, entry)]) {
355
- const found = publicEntryExports(context, key, owner, publicEntry);
368
+ const declared = new Set(extraEntries(graph, entry));
369
+ for (const publicEntry of [entry, ...declared]) {
370
+ const found = publicEntryExports(context, key, owner, publicEntry, declared);
356
371
  exports += found.exports;
357
372
  violations.push(...found.violations);
358
373
  }