@starci/eslint-canon-fe 9.9.10 → 9.9.11
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
package/dist/hooks-folder.js
CHANGED
|
@@ -28,6 +28,7 @@
|
|
|
28
28
|
import { defineRule } from "./lib/rule.js";
|
|
29
29
|
import { importedReduxHook } from "./lib/redux-hook-origin.js";
|
|
30
30
|
import { importedOwnHook } from "./lib/own-hook-origin.js";
|
|
31
|
+
import { builtinHook, isLibrarySource, namedOrigin, OWN_HOOK } from "./lib/hook-library-origin.js";
|
|
31
32
|
import { hfsOf } from "./lib/hfs.js";
|
|
32
33
|
import { walk } from "./lib/ast.js";
|
|
33
34
|
import { functionOf, isFn, unwrap, variableOf } from "./lib/bindings.js";
|
|
@@ -75,55 +76,11 @@ const exportedEntries = (node) => {
|
|
|
75
76
|
}
|
|
76
77
|
return [];
|
|
77
78
|
};
|
|
78
|
-
/** Actual React hook APIs and SWR public entry points, rather than an arbitrary `use*` spelling. */
|
|
79
|
-
const REACT_HOOKS = new Set([
|
|
80
|
-
"use", "useState", "useReducer", "useEffect", "useLayoutEffect", "useInsertionEffect", "useRef", "useImperativeHandle",
|
|
81
|
-
"useMemo", "useCallback", "useContext", "useDebugValue", "useDeferredValue", "useTransition", "useId",
|
|
82
|
-
"useSyncExternalStore", "useActionState", "useOptimistic",
|
|
83
|
-
]);
|
|
84
|
-
const SWR_HOOKS = new Map([
|
|
85
|
-
["swr", new Set(["useSWR", "useSWRConfig"])],
|
|
86
|
-
["swr/immutable", new Set(["useSWRImmutable"])],
|
|
87
|
-
["swr/infinite", new Set(["useSWRInfinite"])],
|
|
88
|
-
["swr/mutation", new Set(["useSWRMutation"])],
|
|
89
|
-
["swr/subscription", new Set(["useSWRSubscription"])],
|
|
90
|
-
]);
|
|
91
|
-
const SWR_DEFAULTS = new Map([["swr", "useSWR"], ["swr/immutable", "useSWRImmutable"], ["swr/infinite", "useSWRInfinite"], ["swr/mutation", "useSWRMutation"], ["swr/subscription", "useSWRSubscription"]]);
|
|
92
|
-
/**
|
|
93
|
-
* The canonical library hooks a composite hook may call (R56, FE-THEME): next-intl, next/navigation,
|
|
94
|
-
* next-themes, react-hook-form and zustand's React entry points. A name outside the list is not a hook by its spelling.
|
|
95
|
-
*/
|
|
96
|
-
const LIBRARY_HOOKS = new Map([
|
|
97
|
-
["next-intl", new Set(["useTranslations", "useLocale", "useFormatter", "useNow", "useTimeZone", "useMessages"])],
|
|
98
|
-
["next/navigation", new Set(["useRouter", "usePathname", "useSearchParams", "useParams", "useSelectedLayoutSegment", "useSelectedLayoutSegments"])],
|
|
99
|
-
["next-themes", new Set(["useTheme"])],
|
|
100
|
-
["react-hook-form", new Set(["useForm", "useFormContext", "useWatch", "useController", "useFieldArray", "useFormState"])],
|
|
101
|
-
["zustand", new Set(["useStore"])],
|
|
102
|
-
["zustand/react/shallow", new Set(["useShallow"])],
|
|
103
|
-
]);
|
|
104
|
-
const OWN_HOOK = "own-hooks";
|
|
105
|
-
/** True when `set` holds `name`. */
|
|
106
|
-
const hasName = (set, name) => set !== undefined && typeof name === "string" && set.has(name);
|
|
107
|
-
const builtinHook = (origin) => {
|
|
108
|
-
if (origin === null)
|
|
109
|
-
return false;
|
|
110
|
-
return origin.source === OWN_HOOK || hasName(LIBRARY_HOOKS.get(origin.source), origin.name) || origin.source === "react-redux" || (origin.source === "react" ? hasName(REACT_HOOKS, origin.name) : hasName(SWR_HOOKS.get(origin.source), origin.name));
|
|
111
|
-
};
|
|
112
79
|
const propertyName = (node) => {
|
|
113
80
|
if (!node.computed && node.property.type === "Identifier")
|
|
114
81
|
return node.property.name;
|
|
115
82
|
return node.property.type === "Literal" ? node.property.value : null;
|
|
116
83
|
};
|
|
117
|
-
/** SWR request hooks are default exports; its root also names useSWRConfig. */
|
|
118
|
-
const namedOrigin = (source, name) => {
|
|
119
|
-
if (source === "react")
|
|
120
|
-
return { source, name };
|
|
121
|
-
if (hasName(LIBRARY_HOOKS.get(source), name))
|
|
122
|
-
return { source, name };
|
|
123
|
-
if (name === "default" && SWR_DEFAULTS.has(source))
|
|
124
|
-
return { source, name: SWR_DEFAULTS.get(source) };
|
|
125
|
-
return source === "swr" && name === "useSWRConfig" ? { source, name } : null;
|
|
126
|
-
};
|
|
127
84
|
/** A runtime import records whether its value is a hook or its module namespace. */
|
|
128
85
|
const importOrigin = (definition) => {
|
|
129
86
|
const specifier = definition.node;
|
|
@@ -132,14 +89,14 @@ const importOrigin = (definition) => {
|
|
|
132
89
|
const source = String(definition.parent.source.value);
|
|
133
90
|
if (definition.parent.importKind === "type" || ("importKind" in specifier && specifier.importKind === "type"))
|
|
134
91
|
return null;
|
|
135
|
-
if (
|
|
92
|
+
if (!isLibrarySource(source))
|
|
136
93
|
return null;
|
|
137
94
|
if (source === "next-themes" && specifier.type === "ImportDefaultSpecifier")
|
|
138
95
|
return null;
|
|
139
96
|
if (specifier.type === "ImportSpecifier")
|
|
140
97
|
return namedOrigin(source, spelled(specifier.imported));
|
|
141
|
-
if (specifier.type === "ImportDefaultSpecifier"
|
|
142
|
-
return { source, name:
|
|
98
|
+
if (specifier.type === "ImportDefaultSpecifier")
|
|
99
|
+
return source === "react" ? { source, name: null } : namedOrigin(source, "default") ?? { source, name: null };
|
|
143
100
|
return { source, name: null };
|
|
144
101
|
};
|
|
145
102
|
/** Resolve a constant alias, including destructuring a React/SWR namespace. */
|
|
@@ -0,0 +1,13 @@
|
|
|
1
|
+
/** The marker for an app hook proven by symbol provenance. */
|
|
2
|
+
export declare const OWN_HOOK = "own-hooks";
|
|
3
|
+
/** Where a hook comes from: the module and the export it names (null for the module object itself; any key a member reads). */
|
|
4
|
+
export interface HookOrigin {
|
|
5
|
+
readonly source: string;
|
|
6
|
+
readonly name: unknown;
|
|
7
|
+
}
|
|
8
|
+
/** Judge an origin against the canonical library hook catalog. */
|
|
9
|
+
export declare const builtinHook: (origin: HookOrigin | null) => boolean;
|
|
10
|
+
/** SWR request hooks are default exports; its root also names useSWRConfig. */
|
|
11
|
+
export declare const namedOrigin: (source: string, name: unknown) => HookOrigin | null;
|
|
12
|
+
/** A library with a known hook entry. */
|
|
13
|
+
export declare const isLibrarySource: (source: string) => boolean;
|
|
@@ -0,0 +1,48 @@
|
|
|
1
|
+
/** Actual React hook APIs and SWR public entry points, rather than an arbitrary `use*` spelling. */
|
|
2
|
+
const REACT_HOOKS = new Set([
|
|
3
|
+
"use", "useState", "useReducer", "useEffect", "useLayoutEffect", "useInsertionEffect", "useRef", "useImperativeHandle",
|
|
4
|
+
"useMemo", "useCallback", "useContext", "useDebugValue", "useDeferredValue", "useTransition", "useId",
|
|
5
|
+
"useSyncExternalStore", "useActionState", "useOptimistic",
|
|
6
|
+
]);
|
|
7
|
+
const SWR_HOOKS = new Map([
|
|
8
|
+
["swr", new Set(["useSWR", "useSWRConfig"])],
|
|
9
|
+
["swr/immutable", new Set(["useSWRImmutable"])],
|
|
10
|
+
["swr/infinite", new Set(["useSWRInfinite"])],
|
|
11
|
+
["swr/mutation", new Set(["useSWRMutation"])],
|
|
12
|
+
["swr/subscription", new Set(["useSWRSubscription"])],
|
|
13
|
+
]);
|
|
14
|
+
const SWR_DEFAULTS = new Map([["swr", "useSWR"], ["swr/immutable", "useSWRImmutable"], ["swr/infinite", "useSWRInfinite"], ["swr/mutation", "useSWRMutation"], ["swr/subscription", "useSWRSubscription"]]);
|
|
15
|
+
/**
|
|
16
|
+
* The canonical library hooks a composite hook may call (R56, FE-THEME): next-intl, next/navigation,
|
|
17
|
+
* next-themes, react-hook-form and zustand's React entry points. A name outside the list is not a hook by its spelling.
|
|
18
|
+
*/
|
|
19
|
+
const LIBRARY_HOOKS = new Map([
|
|
20
|
+
["next-intl", new Set(["useTranslations", "useLocale", "useFormatter", "useNow", "useTimeZone", "useMessages"])],
|
|
21
|
+
["next/navigation", new Set(["useRouter", "usePathname", "useSearchParams", "useParams", "useSelectedLayoutSegment", "useSelectedLayoutSegments"])],
|
|
22
|
+
["next-themes", new Set(["useTheme"])],
|
|
23
|
+
["react-hook-form", new Set(["useForm", "useFormContext", "useWatch", "useController", "useFieldArray", "useFormState"])],
|
|
24
|
+
["zustand", new Set(["useStore"])],
|
|
25
|
+
["zustand/react/shallow", new Set(["useShallow"])],
|
|
26
|
+
]);
|
|
27
|
+
/** The marker for an app hook proven by symbol provenance. */
|
|
28
|
+
export const OWN_HOOK = "own-hooks";
|
|
29
|
+
/** True when `set` holds `name`. */
|
|
30
|
+
const hasName = (set, name) => set !== undefined && typeof name === "string" && set.has(name);
|
|
31
|
+
/** Judge an origin against the canonical library hook catalog. */
|
|
32
|
+
export const builtinHook = (origin) => {
|
|
33
|
+
if (origin === null)
|
|
34
|
+
return false;
|
|
35
|
+
return origin.source === OWN_HOOK || hasName(LIBRARY_HOOKS.get(origin.source), origin.name) || origin.source === "react-redux" || (origin.source === "react" ? hasName(REACT_HOOKS, origin.name) : hasName(SWR_HOOKS.get(origin.source), origin.name));
|
|
36
|
+
};
|
|
37
|
+
/** SWR request hooks are default exports; its root also names useSWRConfig. */
|
|
38
|
+
export const namedOrigin = (source, name) => {
|
|
39
|
+
if (source === "react")
|
|
40
|
+
return { source, name };
|
|
41
|
+
if (hasName(LIBRARY_HOOKS.get(source), name))
|
|
42
|
+
return { source, name };
|
|
43
|
+
if (name === "default" && SWR_DEFAULTS.has(source))
|
|
44
|
+
return { source, name: SWR_DEFAULTS.get(source) };
|
|
45
|
+
return source === "swr" && name === "useSWRConfig" ? { source, name } : null;
|
|
46
|
+
};
|
|
47
|
+
/** A library with a known hook entry. */
|
|
48
|
+
export const isLibrarySource = (source) => source === "react" || SWR_HOOKS.has(source) || LIBRARY_HOOKS.has(source);
|
|
@@ -1,5 +1,7 @@
|
|
|
1
1
|
import ts from "typescript";
|
|
2
2
|
import { programFor } from "./program-for.js";
|
|
3
|
+
import { hfsOf } from "./hfs.js";
|
|
4
|
+
import { builtinHook, namedOrigin } from "./hook-library-origin.js";
|
|
3
5
|
const HOOKS_FOLDER = /(^|\/)hooks\//;
|
|
4
6
|
const isHookName = (name) => /^use[A-Z0-9]/.test(name);
|
|
5
7
|
/** The program that holds the import target: the project program has it; a program rooted at the linted file alone (its disk text may differ from the linted text) is widened with it. */
|
|
@@ -23,6 +25,133 @@ const inAppHooksFolder = (file) => {
|
|
|
23
25
|
const fileName = file.fileName.replaceAll("\\", "/");
|
|
24
26
|
return !fileName.includes("/node_modules/") && !file.isDeclarationFile && HOOKS_FOLDER.test(fileName);
|
|
25
27
|
};
|
|
28
|
+
/** A type-only alias never supplies a runtime hook. */
|
|
29
|
+
const runtimeAlias = (declaration) => {
|
|
30
|
+
if (ts.isImportSpecifier(declaration))
|
|
31
|
+
return !declaration.isTypeOnly && !declaration.parent.parent.isTypeOnly;
|
|
32
|
+
if (ts.isImportClause(declaration))
|
|
33
|
+
return !declaration.isTypeOnly;
|
|
34
|
+
if (ts.isExportSpecifier(declaration))
|
|
35
|
+
return !declaration.isTypeOnly && !declaration.parent.parent.isTypeOnly;
|
|
36
|
+
if (ts.isNamespaceExport(declaration))
|
|
37
|
+
return !declaration.parent.isTypeOnly;
|
|
38
|
+
return false;
|
|
39
|
+
};
|
|
40
|
+
/** Follow every alias separately, so a type-only link or cycle never becomes a runtime binding. */
|
|
41
|
+
function runtimeSymbol(checker, start) {
|
|
42
|
+
let symbol = start;
|
|
43
|
+
const seen = new Set();
|
|
44
|
+
while ((symbol.flags & ts.SymbolFlags.Alias) !== 0) {
|
|
45
|
+
if (seen.has(symbol))
|
|
46
|
+
return null;
|
|
47
|
+
seen.add(symbol);
|
|
48
|
+
const declarations = symbol.declarations ?? [];
|
|
49
|
+
if (declarations.length !== 1 || !declarations.every(runtimeAlias))
|
|
50
|
+
return null;
|
|
51
|
+
const next = checker.getImmediateAliasedSymbol(symbol);
|
|
52
|
+
if (!next)
|
|
53
|
+
return null;
|
|
54
|
+
symbol = next;
|
|
55
|
+
}
|
|
56
|
+
return symbol;
|
|
57
|
+
}
|
|
58
|
+
/** The module hook role is slot-defined and belongs to the same app as the caller. */
|
|
59
|
+
function moduleHookRole(context, file) {
|
|
60
|
+
const hfs = hfsOf(context);
|
|
61
|
+
const found = hfs.classify(file.fileName);
|
|
62
|
+
const caller = hfs.classify(context.filename);
|
|
63
|
+
return !file.isDeclarationFile && !file.fileName.replaceAll("\\", "/").includes("/node_modules/") &&
|
|
64
|
+
found.status === "owned" && found.slot?.startsWith("fe.modules.") === true && found.role === "hooks" &&
|
|
65
|
+
found.bindings?.app !== undefined && found.bindings.app === caller.bindings?.app && hfs.allows(file.fileName)?.allowed === true;
|
|
66
|
+
}
|
|
67
|
+
/** A known library import is proven by its own runtime import declaration and canonical exported name. */
|
|
68
|
+
function libraryImport(declaration) {
|
|
69
|
+
if (!runtimeAlias(declaration))
|
|
70
|
+
return false;
|
|
71
|
+
if (ts.isImportSpecifier(declaration)) {
|
|
72
|
+
const module = declaration.parent.parent.parent.moduleSpecifier;
|
|
73
|
+
return ts.isStringLiteral(module) && builtinHook(namedOrigin(module.text, (declaration.propertyName ?? declaration.name).text));
|
|
74
|
+
}
|
|
75
|
+
if (ts.isExportSpecifier(declaration)) {
|
|
76
|
+
const module = declaration.parent.parent.moduleSpecifier;
|
|
77
|
+
return module !== undefined && ts.isStringLiteral(module) && builtinHook(namedOrigin(module.text, (declaration.propertyName ?? declaration.name).text));
|
|
78
|
+
}
|
|
79
|
+
return false;
|
|
80
|
+
}
|
|
81
|
+
/** Follow runtime re-exports until a known library import itself proves the binding. */
|
|
82
|
+
function librarySymbol(checker, start) {
|
|
83
|
+
let symbol = start;
|
|
84
|
+
const visited = new Set();
|
|
85
|
+
while (!visited.has(symbol)) {
|
|
86
|
+
visited.add(symbol);
|
|
87
|
+
const declarations = symbol.declarations ?? [];
|
|
88
|
+
if (declarations.length !== 1)
|
|
89
|
+
return false;
|
|
90
|
+
const declaration = declarations[0];
|
|
91
|
+
if (!declaration)
|
|
92
|
+
return false;
|
|
93
|
+
if (libraryImport(declaration))
|
|
94
|
+
return true;
|
|
95
|
+
if ((symbol.flags & ts.SymbolFlags.Alias) === 0 || !runtimeAlias(declaration))
|
|
96
|
+
return false;
|
|
97
|
+
const next = checker.getImmediateAliasedSymbol(symbol);
|
|
98
|
+
if (!next)
|
|
99
|
+
return false;
|
|
100
|
+
symbol = next;
|
|
101
|
+
}
|
|
102
|
+
return false;
|
|
103
|
+
}
|
|
104
|
+
/** Resolve calls through lexical symbols, constant aliases and runtime import/re-export chains. */
|
|
105
|
+
function moduleHookBody(context, checker, declaration, seen) {
|
|
106
|
+
if (!moduleHookRole(context, declaration.getSourceFile()))
|
|
107
|
+
return false;
|
|
108
|
+
if (ts.isVariableDeclaration(declaration) && (declaration.parent.flags & ts.NodeFlags.Const) === 0)
|
|
109
|
+
return false;
|
|
110
|
+
const body = ts.isFunctionDeclaration(declaration) ? declaration.body : ts.isVariableDeclaration(declaration) && declaration.initializer &&
|
|
111
|
+
(ts.isArrowFunction(declaration.initializer) || ts.isFunctionExpression(declaration.initializer)) ? declaration.initializer.body : undefined;
|
|
112
|
+
if (!body)
|
|
113
|
+
return false;
|
|
114
|
+
const proves = (expression, visited) => {
|
|
115
|
+
if (!ts.isIdentifier(expression))
|
|
116
|
+
return false;
|
|
117
|
+
const symbol = checker.getSymbolAtLocation(expression);
|
|
118
|
+
if (!symbol || visited.has(symbol))
|
|
119
|
+
return false;
|
|
120
|
+
const nextSeen = new Set([...visited, symbol]);
|
|
121
|
+
if (librarySymbol(checker, symbol))
|
|
122
|
+
return true;
|
|
123
|
+
const resolved = runtimeSymbol(checker, symbol);
|
|
124
|
+
if (!resolved || nextSeen.has(resolved) && resolved !== symbol)
|
|
125
|
+
return false;
|
|
126
|
+
const actual = resolved.declarations ?? [];
|
|
127
|
+
if (actual.length !== 1)
|
|
128
|
+
return false;
|
|
129
|
+
const target = actual[0];
|
|
130
|
+
if (!target)
|
|
131
|
+
return false;
|
|
132
|
+
if (hookValue(target) && inAppHooksFolder(target.getSourceFile()) && isHookName(resolved.name)) {
|
|
133
|
+
const hfs = hfsOf(context);
|
|
134
|
+
const app = hfs.classify(context.filename).bindings?.app;
|
|
135
|
+
const found = hfs.classify(target.getSourceFile().fileName);
|
|
136
|
+
return app !== undefined && found.status === "owned" && found.slot?.startsWith("fe.hooks") === true && found.bindings?.app === app;
|
|
137
|
+
}
|
|
138
|
+
if (ts.isVariableDeclaration(target) && (target.parent.flags & ts.NodeFlags.Const) !== 0 && target.initializer && ts.isIdentifier(target.initializer))
|
|
139
|
+
return proves(target.initializer, nextSeen);
|
|
140
|
+
return moduleHookBody(context, checker, target, nextSeen);
|
|
141
|
+
};
|
|
142
|
+
let found = false;
|
|
143
|
+
const visit = (node) => {
|
|
144
|
+
if (found || node !== body && ts.isFunctionLike(node))
|
|
145
|
+
return;
|
|
146
|
+
if (ts.isCallExpression(node) && proves(node.expression, seen)) {
|
|
147
|
+
found = true;
|
|
148
|
+
return;
|
|
149
|
+
}
|
|
150
|
+
ts.forEachChild(node, visit);
|
|
151
|
+
};
|
|
152
|
+
visit(body);
|
|
153
|
+
return found;
|
|
154
|
+
}
|
|
26
155
|
/** True when the import binding is a runtime `use*` import that resolves to a hook declared in a `hooks/` folder of the app. */
|
|
27
156
|
export function importedOwnHook(context, definition) {
|
|
28
157
|
const specifier = definition.node;
|
|
@@ -47,6 +176,11 @@ export function importedOwnHook(context, definition) {
|
|
|
47
176
|
const exported = moduleSymbol ? checker.getExportsOfModule(moduleSymbol).find((symbol) => symbol.name === name) : undefined;
|
|
48
177
|
if (!exported)
|
|
49
178
|
return false;
|
|
50
|
-
const
|
|
51
|
-
|
|
179
|
+
const original = (exported.flags & ts.SymbolFlags.Alias) !== 0 ? checker.getAliasedSymbol(exported) : exported;
|
|
180
|
+
if ((original.declarations ?? []).some((declaration) => hookValue(declaration) && inAppHooksFolder(declaration.getSourceFile())))
|
|
181
|
+
return true;
|
|
182
|
+
const symbol = runtimeSymbol(checker, exported);
|
|
183
|
+
if (!symbol)
|
|
184
|
+
return false;
|
|
185
|
+
return (symbol.declarations ?? []).some((declaration) => hookValue(declaration) && moduleHookBody(context, checker, declaration, new Set([symbol])));
|
|
52
186
|
}
|
package/package.json
CHANGED
|
@@ -31,35 +31,35 @@ pins:
|
|
|
31
31
|
source: packages/event-stream/package.json
|
|
32
32
|
why: 'the one server-sent-events client of every front end (fe-stack event-stream, owner:event-stream-package): the transport client layer (modules/api/sse/clients) creates one instance for the app with the bearer token source and the stream operations follow on it; it carries Last-Event-ID resume, the capped doubling backoff, the heartbeat timeout, the single 401 renewal and the useSWRSubscription adapter, so no app keeps a hand-written reader.'
|
|
33
33
|
'@starci/grammar':
|
|
34
|
-
version: 0.21.
|
|
34
|
+
version: 0.21.11
|
|
35
35
|
group: starci
|
|
36
36
|
install: registry
|
|
37
37
|
side: fe
|
|
38
38
|
source: packages/grammar/package.json
|
|
39
39
|
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.'
|
|
40
40
|
'@starci/eslint-canon-be':
|
|
41
|
-
version: 3.11.
|
|
41
|
+
version: 3.11.18
|
|
42
42
|
group: starci
|
|
43
43
|
install: registry
|
|
44
44
|
side: be
|
|
45
45
|
source: packages/eslint-canon-be/package.json
|
|
46
46
|
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.'
|
|
47
47
|
'@starci/eslint-canon-fe':
|
|
48
|
-
version: 9.9.
|
|
48
|
+
version: 9.9.11
|
|
49
49
|
group: starci
|
|
50
50
|
install: registry
|
|
51
51
|
side: fe
|
|
52
52
|
source: packages/eslint-canon-fe/package.json
|
|
53
53
|
why: 'the front-end ESLint canon: `loadHfs(import.meta.url)` of fe/eslint.config.mjs finds the app-root hfs.json (kind app) and gives every linted file the view of the fe 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; the front end has no tests (FE_NO_TESTS R97 in hfs) and no-vietnamese-in-source (R91) applies.'
|
|
54
54
|
'@starci/eslint-grammar-core':
|
|
55
|
-
version: 0.21.
|
|
55
|
+
version: 0.21.11
|
|
56
56
|
group: starci
|
|
57
57
|
install: registry
|
|
58
58
|
side: fe
|
|
59
59
|
source: packages/eslint-grammar-core/package.json
|
|
60
60
|
why: 'the StarCi Grammar component API lint, versioned with @starci/grammar; front ends compose this family package with the universal FE canon.'
|
|
61
61
|
'@starci/eslint-grammar-offset-pop':
|
|
62
|
-
version: 0.21.
|
|
62
|
+
version: 0.21.11
|
|
63
63
|
group: starci
|
|
64
64
|
install: registry
|
|
65
65
|
side: fe
|
|
@@ -106,7 +106,7 @@ pins:
|
|
|
106
106
|
source: packages/ai-router/package.json
|
|
107
107
|
why: 'the AI pool router of a back end: a NestJS module that routes OpenAI-compatible chat, stream and embeddings calls across keys, models and providers, with @starci/ai-router/testing for its specs; @starci/jest-preset compiles it as an ESM-only dependency.'
|
|
108
108
|
'@starci/hfs':
|
|
109
|
-
version: 5.0.
|
|
109
|
+
version: 5.0.7
|
|
110
110
|
group: starci
|
|
111
111
|
install: registry
|
|
112
112
|
side: both
|