test(gates): the snapshot holds a config per scope, not a path per file
gates.snapshot.json listed ~800 file paths under their config, so every new or deleted frontend file regenerated it (31% of commits, E34 S2) and the regen diff buried the gate changes it exists to show. Now a scope is named by path (scopeOf: src, src tests, ui, lib tests, …), the snapshot holds one effective config per scope (2345 → 1456 lines), and a new test fails when a file's config is not its scope's. Not narrowed: a new file in a known place changes nothing (it gets its scope's config, checked), a glob that moves one file into another config fails (seen: an override on Feed.tsx → "src: src/components/Feed.tsx"), a scope whose rules change is a snapshot diff, and a file no config lints is still a register miss.
This commit is contained in:
@@ -20,7 +20,7 @@ green **before** any merge, release, or handing work to review.
|
||||
|
||||
- Full gate: `bash scripts/check.sh` (`lint` for the static lanes only); the private ops dispatcher's `siftlode check` runs the same script. Single lanes: [`docs/development.md`](docs/development.md) § The gate.
|
||||
- **There is no CI** (owner, 2026-10-04): this gate, run here, is the only one — green before every hand-over, and `release` refuses to merge without it.
|
||||
- **A gate with any real hit is never switched off or narrowed** — not a lint rule, a compiler flag, a lane or a release step. Fix the sites, or suppress each one inline naming its invariant (`-- why`, lint-enforced). What stays narrowed is in [`docs/gates.md`](docs/gates.md), and a contract test fails when a narrowing is missing from it. The effective ESLint config of every file and the gate/lane lines are pinned in `frontend/src/lib/gates.snapshot.json`: a new code file or any gate change fails until you regenerate it (`GATE_SNAPSHOT=update`, see gates.md) — then read that diff as a gate change. A comment saying "X is enforced by Y" is checked by running Y (E32: one such comment hid 95 functions the React Compiler skipped).
|
||||
- **A gate with any real hit is never switched off or narrowed** — not a lint rule, a compiler flag, a lane or a release step. Fix the sites, or suppress each one inline naming its invariant (`-- why`, lint-enforced). What stays narrowed is in [`docs/gates.md`](docs/gates.md), and a contract test fails when a narrowing is missing from it. The effective ESLint config of every scope and the gate/lane lines are pinned in `frontend/src/lib/gates.snapshot.json`: any gate change fails until you regenerate it (`GATE_SNAPSHOT=update`, see gates.md) — then read that diff as a gate change. A comment saying "X is enforced by Y" is checked by running Y (E32: one such comment hid 95 functions the React Compiler skipped).
|
||||
- **The backend type lane runs in the test image, not on the host** — `requirements.lock` cannot be installed on Windows (uvloop), and a type checker without the real packages reports nonsense. It fails on NEW errors only; the pre-existing ones sit in `backend/.basedpyright/baseline.json` and burn down epic by epic. Fixed some? Regenerate with `--writebaseline`.
|
||||
- New DB-backed test? It must actually run, not silently skip — assume `SIFTLODE_TEST_REQUIRE_DB=1`.
|
||||
|
||||
|
||||
+8
-6
@@ -13,16 +13,18 @@ name its invariant. Whatever is left narrowed is listed here, and
|
||||
**effective** configuration and fails when a narrowing is not on this page. A row here is a decision,
|
||||
not an oversight; a narrowing missing from here is a test failure.
|
||||
|
||||
**The snapshot.** ESLint's effective config for every code file `eslint .` reaches, and the lines
|
||||
that run the lanes (`scripts/check.sh`, `backend/docker-compose.test.yml`,
|
||||
**The snapshot.** ESLint's effective config for every scope `eslint .` reaches, and the lines that
|
||||
run the lanes (`scripts/check.sh`, `backend/docker-compose.test.yml`,
|
||||
`backend/Dockerfile.test`, the npm scripts, `vitest.config.ts`, `.prettierignore`), are committed in
|
||||
[`frontend/src/lib/gates.snapshot.json`](../frontend/src/lib/gates.snapshot.json). The test fails on
|
||||
ANY difference — a rule, an option, a glob that moves one file to another config, a new ignore, a gate
|
||||
flag. So a change to a gate is always a diff of that file in review, never a shape a detector failed
|
||||
ANY difference — a rule, an option, a new ignore, a gate flag. A scope is named by path (`scopeOf` in
|
||||
the test: `src`, `src tests`, `ui`, …) and every file in it must get the same config, so a glob that
|
||||
moves one file into another config fails too, while a new file in a known place changes nothing
|
||||
(until E35 the snapshot listed every file, and 31% of commits regenerated it). So a change to a gate is always a diff of that file in review, never a shape a detector failed
|
||||
to model (E32 review: three rounds of a cleverer detector each found a bypass of the last one's
|
||||
fixes). Regenerate it with `GATE_SNAPSHOT=update npx vitest run src/lib/gates.contract.test.ts`
|
||||
(from `frontend/`), and read its diff as a gate change: a new file only adds its path; anything else
|
||||
needs a reason. A new code file is part of that diff by design.
|
||||
(from `frontend/`), and read its diff as a gate change. A new kind of file (a new config, a new place)
|
||||
needs a line in `scopeOf`.
|
||||
|
||||
**How the register is matched:** each narrowing must appear in a table row below as its
|
||||
`code-quoted` id, in a row whose **Scope** cell names the scope exactly as the test reports it (a cell
|
||||
|
||||
@@ -101,20 +101,23 @@ function restrictedEntries(entry: Linter.RuleEntry | undefined): Set<string> {
|
||||
|
||||
// --- the snapshot ---------------------------------------------------------------------------------
|
||||
// The configs that can narrow a gate in more shapes than a check can enumerate are held to a
|
||||
// COMMITTED SNAPSHOT (E32 review): ESLint's effective config for every file `eslint .` reaches, and
|
||||
// the lines that run the lanes. Any change fails until the snapshot is regenerated, so a narrowing
|
||||
// arrives as a diff of `gates.snapshot.json` in review instead of slipping past a detector. (Three
|
||||
// review rounds kept finding shapes the detector this replaced did not model — a `basePath` global
|
||||
// ignore, a widened glob filed under a narrow row, a substring check on a gate line. A comparison
|
||||
// against everything has no shape to miss.)
|
||||
// COMMITTED SNAPSHOT (E32 review): ESLint's effective config per scope, and the lines that run the
|
||||
// lanes. Any change fails until the snapshot is regenerated, so a narrowing arrives as a diff of
|
||||
// `gates.snapshot.json` in review instead of slipping past a detector. (Three review rounds kept
|
||||
// finding shapes the detector this replaced did not model — a `basePath` global ignore, a widened glob
|
||||
// filed under a narrow row, a substring check on a gate line. A comparison against everything has no
|
||||
// shape to miss.)
|
||||
//
|
||||
// A scope is named by PATH (`scopeOf`), and every file of a scope must get the same effective config.
|
||||
// So a new file in a known place changes nothing (until E35 the snapshot listed every file, and 31% of
|
||||
// commits regenerated it), while a glob that moves a file into another config splits its scope and
|
||||
// fails below, and a scope whose rules change is a snapshot diff.
|
||||
//
|
||||
// Regenerate: GATE_SNAPSHOT=update npx vitest run src/lib/gates.contract.test.ts
|
||||
// then read the diff like any gate change. An ESLint config that changed keeps its scope name when
|
||||
// most of its files had it; one no name fits is written as `new config N` — rename it to the
|
||||
// register scope it is (the register test below then asks for its rows).
|
||||
// then read the diff like any gate change. A new kind of file (a new config or a new place) needs a
|
||||
// line in `scopeOf` and, when it narrows anything, its register rows.
|
||||
|
||||
const SNAPSHOT = "frontend/src/lib/gates.snapshot.json";
|
||||
const NOT_LINTED = "(not linted)";
|
||||
type EslintConfig = {
|
||||
rules: Record<string, unknown[]>;
|
||||
linterOptions: Record<string, unknown>;
|
||||
@@ -123,10 +126,24 @@ type EslintConfig = {
|
||||
settings: unknown;
|
||||
};
|
||||
type Snapshot = {
|
||||
eslint: { configs: Record<string, EslintConfig>; files: Record<string, string[]> };
|
||||
eslint: Record<string, EslintConfig>;
|
||||
lanes: Record<string, unknown>;
|
||||
};
|
||||
|
||||
/** The register scope a code file belongs to, by its path (relative to frontend/). */
|
||||
function scopeOf(file: string): string {
|
||||
if (!file.includes("/")) return /[.][mc]?js$/.test(file) ? "js configs" : "ts configs";
|
||||
if (file.startsWith("e2e/")) return "e2e";
|
||||
if (file.startsWith("src/design-lab/")) return "design-lab";
|
||||
// the network owners (NETWORK_OWNERS in eslint.config.js)
|
||||
if (file === "src/lib/http.ts") return "lib/http.ts";
|
||||
if (file === "src/lib/api/core.ts") return "lib/api/core.ts";
|
||||
const test = /[.]test[.]tsx?$/.test(file);
|
||||
if (file.startsWith("src/components/ui/")) return test ? "ui tests" : "ui";
|
||||
if (file.startsWith("src/lib/")) return test ? "lib tests" : "src/lib";
|
||||
return test ? "src tests" : "src";
|
||||
}
|
||||
|
||||
const sortKeys = (v: unknown): unknown =>
|
||||
Array.isArray(v)
|
||||
? v.map(sortKeys)
|
||||
@@ -173,7 +190,7 @@ function normalize(cfg: Linter.Config): EslintConfig {
|
||||
|
||||
// Every code file `eslint .` walks into. The skipped directories are the global ignores' generated
|
||||
// output (the register's `everywhere` row) — untracked, and present on one machine but not another.
|
||||
// Every other file is asked for its config, so a file a new ignore drops lands in `(not linted)`.
|
||||
// Every other file is asked for its config, so a file a new ignore drops is reported as not linted.
|
||||
const WALK_SKIP = new Set([
|
||||
"node_modules",
|
||||
"dist",
|
||||
@@ -192,7 +209,7 @@ function codeFiles(dir = ""): string[] {
|
||||
|
||||
// On disk every config but src is written as its difference from src — the rules it sets otherwise
|
||||
// and the src rules it does not apply — so a scope reads like its register rows, and one rule set
|
||||
// is not repeated fourteen times. In memory it is always whole.
|
||||
// is not repeated a dozen times. In memory it is always whole.
|
||||
type Stored = Omit<EslintConfig, "rules"> & {
|
||||
rules: Record<string, unknown[]>;
|
||||
notApplied?: string[];
|
||||
@@ -225,15 +242,22 @@ function fromStored(stored: Record<string, Stored>): Record<string, EslintConfig
|
||||
);
|
||||
}
|
||||
const committed = (): Snapshot => {
|
||||
const s = JSON.parse(read(SNAPSHOT)) as Snapshot & {
|
||||
eslint: { configs: Record<string, Stored> };
|
||||
const s = JSON.parse(read(SNAPSHOT)) as Omit<Snapshot, "eslint"> & {
|
||||
eslint: Record<string, Stored>;
|
||||
};
|
||||
return canonical({ ...s, eslint: { ...s.eslint, configs: fromStored(s.eslint.configs) } });
|
||||
return canonical({ ...s, eslint: fromStored(s.eslint) });
|
||||
};
|
||||
|
||||
async function eslintActual(names: Snapshot["eslint"]): Promise<Snapshot["eslint"]> {
|
||||
type EslintActual = {
|
||||
configs: Record<string, EslintConfig>;
|
||||
/** the files no config lints */
|
||||
notLinted: string[];
|
||||
/** `scope: file` for each file whose config is not its scope's (the scope's majority) */
|
||||
strays: string[];
|
||||
};
|
||||
async function eslintActual(): Promise<EslintActual> {
|
||||
const eslint = new ESLint({ cwd: FRONTEND });
|
||||
const groups = new Map<string, { config: EslintConfig; files: string[] }>();
|
||||
const byScope = new Map<string, Map<string, { config: EslintConfig; files: string[] }>>();
|
||||
const notLinted: string[] = [];
|
||||
for (const file of codeFiles().sort()) {
|
||||
const raw = (await eslint.calculateConfigForFile(file)) as Linter.Config | undefined;
|
||||
@@ -243,35 +267,22 @@ async function eslintActual(names: Snapshot["eslint"]): Promise<Snapshot["eslint
|
||||
}
|
||||
const config = normalize(raw);
|
||||
const key = JSON.stringify(config);
|
||||
const scope = scopeOf(file);
|
||||
const groups =
|
||||
byScope.get(scope) ?? new Map<string, { config: EslintConfig; files: string[] }>();
|
||||
const group = groups.get(key) ?? { config, files: [] };
|
||||
group.files.push(file);
|
||||
groups.set(key, group);
|
||||
byScope.set(scope, groups);
|
||||
}
|
||||
// a config is named by the snapshot entry it equals; a changed one by the old name most of its
|
||||
// files had (if still free), so the diff shows the rule that moved, not a renamed scope
|
||||
const byContent = new Map(Object.entries(names.configs).map(([n, c]) => [JSON.stringify(c), n]));
|
||||
const oldName = new Map(
|
||||
Object.entries(names.files).flatMap(([n, files]) => files.map((f) => [f, n] as const)),
|
||||
);
|
||||
const configs: Record<string, EslintConfig> = {};
|
||||
const files: Record<string, string[]> = { [NOT_LINTED]: notLinted };
|
||||
const unnamed: { config: EslintConfig; files: string[] }[] = [];
|
||||
for (const [key, group] of groups) {
|
||||
const name = byContent.get(key);
|
||||
if (name) [configs[name], files[name]] = [group.config, group.files];
|
||||
else unnamed.push(group);
|
||||
const strays: string[] = [];
|
||||
for (const [scope, groups] of byScope) {
|
||||
const [first, ...rest] = [...groups.values()].sort((a, b) => b.files.length - a.files.length);
|
||||
configs[scope] = first!.config;
|
||||
for (const g of rest) strays.push(...g.files.map((f) => `${scope}: ${f}`));
|
||||
}
|
||||
unnamed.forEach((group, i) => {
|
||||
const votes = new Map<string, number>();
|
||||
for (const f of group.files) {
|
||||
const n = oldName.get(f);
|
||||
if (n && n !== NOT_LINTED) votes.set(n, (votes.get(n) ?? 0) + 1);
|
||||
}
|
||||
const best = [...votes].sort((a, b) => b[1] - a[1])[0]?.[0];
|
||||
const name = best && !(best in configs) ? best : `new config ${i + 1}`;
|
||||
[configs[name], files[name]] = [group.config, group.files];
|
||||
});
|
||||
return canonical({ configs, files });
|
||||
return { configs: canonical(configs), notLinted, strays };
|
||||
}
|
||||
|
||||
// the lines that run each lane, comments and blank lines aside
|
||||
@@ -295,10 +306,10 @@ const lanes = () => ({
|
||||
"frontend/.prettierignore": meaningful("frontend/.prettierignore"),
|
||||
});
|
||||
|
||||
let actual: Promise<Snapshot> | undefined;
|
||||
const current = () =>
|
||||
(actual ??= (async () =>
|
||||
canonical({ eslint: await eslintActual(committed().eslint), lanes: lanes() }))());
|
||||
let actual: Promise<EslintActual> | undefined;
|
||||
const linted = () => (actual ??= eslintActual());
|
||||
const current = async (): Promise<Snapshot> =>
|
||||
canonical({ eslint: (await linted()).configs, lanes: lanes() });
|
||||
|
||||
// --- the register, read off the snapshot ------------------------------------------------------------
|
||||
|
||||
@@ -310,11 +321,8 @@ const ignorable = (rule: string, all: Record<string, unknown>) =>
|
||||
const entry = (v: unknown[] | undefined) => v as Linter.RuleEntry | undefined;
|
||||
|
||||
/** Every narrowing in the effective ESLint configs, as (scope, id). */
|
||||
function eslintNarrowings({ configs, files }: Snapshot["eslint"]): [string, string][] {
|
||||
const found: [string, string][] = files[NOT_LINTED]!.map((f) => [
|
||||
"everywhere",
|
||||
`${f} — not linted`,
|
||||
]);
|
||||
function eslintNarrowings({ configs, notLinted }: EslintActual): [string, string][] {
|
||||
const found: [string, string][] = notLinted.map((f) => ["everywhere", `${f} — not linted`]);
|
||||
const src = configs.src?.rules;
|
||||
if (!src) return [...found, ["src", "(no config named src)"]];
|
||||
for (const [scope, { rules, linterOptions }] of Object.entries(configs)) {
|
||||
@@ -347,22 +355,29 @@ function eslintNarrowings({ configs, files }: Snapshot["eslint"]): [string, stri
|
||||
}
|
||||
|
||||
describe("the gate snapshot (gates.snapshot.json)", () => {
|
||||
it("holds the effective ESLint config of every file and the lines that run each lane", async () => {
|
||||
it("holds the effective ESLint config of every scope and the lines that run each lane", async () => {
|
||||
const now = await current();
|
||||
if (process.env.GATE_SNAPSHOT === "update") {
|
||||
const path = REPO + SNAPSHOT;
|
||||
const options = { ...(await resolveConfig(path)), filepath: path };
|
||||
const stored = { ...now, eslint: { ...now.eslint, configs: toStored(now.eslint.configs) } };
|
||||
const stored = { ...now, eslint: toStored(now.eslint) };
|
||||
writeFileSync(path, await format(JSON.stringify(sortKeys(stored)), options));
|
||||
return;
|
||||
}
|
||||
expect(now).toEqual(committed());
|
||||
}, 120_000);
|
||||
|
||||
// A glob that moves one file into another config (a product file into the test config) leaves its
|
||||
// scope's config as it was, so the snapshot alone would not see it: the file now differs from the
|
||||
// rest of its scope, and that is what fails here.
|
||||
it("gives every file of a scope the same config", async () => {
|
||||
expect((await linted()).strays).toEqual([]);
|
||||
}, 120_000);
|
||||
});
|
||||
|
||||
describe("the gate register (docs/gates.md)", () => {
|
||||
it("lists every ESLint rule narrowed, by scope", async () => {
|
||||
expect(missingFrom(eslintNarrowings((await current()).eslint))).toEqual([]);
|
||||
expect(missingFrom(eslintNarrowings(await linted()))).toEqual([]);
|
||||
}, 120_000);
|
||||
|
||||
it("keeps --max-warnings 0, which is what makes a warn-level rule fail", () => {
|
||||
|
||||
@@ -1,6 +1,5 @@
|
||||
{
|
||||
"eslint": {
|
||||
"configs": {
|
||||
"design-lab": {
|
||||
"languageOptions": {
|
||||
"ecmaVersion": 2022,
|
||||
@@ -707,10 +706,7 @@
|
||||
"no-extra-boolean-cast": [2, {}],
|
||||
"no-extra-parens": [0],
|
||||
"no-extra-semi": [0],
|
||||
"no-fallthrough": [
|
||||
2,
|
||||
{ "allowEmptyCase": false, "reportUnusedFallthroughComment": false }
|
||||
],
|
||||
"no-fallthrough": [2, { "allowEmptyCase": false, "reportUnusedFallthroughComment": false }],
|
||||
"no-floating-decimal": [0],
|
||||
"no-func-assign": [0],
|
||||
"no-global-assign": [2, { "exceptions": [] }],
|
||||
@@ -1280,798 +1276,6 @@
|
||||
"settings": null
|
||||
}
|
||||
},
|
||||
"files": {
|
||||
"(not linted)": [],
|
||||
"design-lab": [
|
||||
"src/design-lab/DesignLab.tsx",
|
||||
"src/design-lab/batchA.tsx",
|
||||
"src/design-lab/batchB.tsx",
|
||||
"src/design-lab/batchC.tsx",
|
||||
"src/design-lab/batchCActions.tsx",
|
||||
"src/design-lab/batchD.tsx",
|
||||
"src/design-lab/labKit.tsx",
|
||||
"src/design-lab/labTokens.ts"
|
||||
],
|
||||
"e2e": [
|
||||
"e2e/channel.spec.ts",
|
||||
"e2e/feed.spec.ts",
|
||||
"e2e/global.setup.ts",
|
||||
"e2e/helpers.ts",
|
||||
"e2e/nav.spec.ts",
|
||||
"e2e/player.spec.ts",
|
||||
"e2e/settings.spec.ts",
|
||||
"e2e/speed.measure.ts"
|
||||
],
|
||||
"js configs": ["eslint.config.js", "postcss.config.js"],
|
||||
"lib tests": [
|
||||
"src/lib/accountPinnedStores.dom.test.ts",
|
||||
"src/lib/actionErrors.test.ts",
|
||||
"src/lib/activeAccount.dom.test.ts",
|
||||
"src/lib/adminUsers.test.ts",
|
||||
"src/lib/api/adminLogs.dom.test.ts",
|
||||
"src/lib/api/core.test.ts",
|
||||
"src/lib/api/diagnostics.dom.test.ts",
|
||||
"src/lib/api/discover.dom.test.ts",
|
||||
"src/lib/api/downloads.contract.test.ts",
|
||||
"src/lib/api/facade.test.ts",
|
||||
"src/lib/api/feed.test.ts",
|
||||
"src/lib/api/plex.dom.test.ts",
|
||||
"src/lib/api/plexFilters.dom.test.ts",
|
||||
"src/lib/api/plexLang.dom.test.ts",
|
||||
"src/lib/api/tagModes.test.ts",
|
||||
"src/lib/auditGroups.test.ts",
|
||||
"src/lib/browserBackgroundPlay.dom.test.ts",
|
||||
"src/lib/buttonPrimitive.contract.test.ts",
|
||||
"src/lib/captureTab.dom.test.ts",
|
||||
"src/lib/channelDetailPoll.backfill.dom.test.tsx",
|
||||
"src/lib/channelDetailPoll.test.ts",
|
||||
"src/lib/channelOrder.dom.test.ts",
|
||||
"src/lib/channelWriteFailures.dom.test.tsx",
|
||||
"src/lib/channelsUrlState.dom.test.ts",
|
||||
"src/lib/chipLane.contract.test.ts",
|
||||
"src/lib/chipOverflow.test.ts",
|
||||
"src/lib/columnSort.test.ts",
|
||||
"src/lib/controlRules.contract.test.ts",
|
||||
"src/lib/cropGeometry.test.ts",
|
||||
"src/lib/dateFilter.test.ts",
|
||||
"src/lib/detailLog.dom.test.ts",
|
||||
"src/lib/diagnostics.dom.test.ts",
|
||||
"src/lib/diagnostics.session.dom.test.ts",
|
||||
"src/lib/downloadActions.test.ts",
|
||||
"src/lib/downloadFailure.test.ts",
|
||||
"src/lib/downloads.test.ts",
|
||||
"src/lib/fadeEdge.contract.test.ts",
|
||||
"src/lib/feedSort.yt.test.ts",
|
||||
"src/lib/focusSafe.contract.test.ts",
|
||||
"src/lib/format.test.ts",
|
||||
"src/lib/gates.contract.test.ts",
|
||||
"src/lib/globalKeymap.dom.test.tsx",
|
||||
"src/lib/headings.contract.test.ts",
|
||||
"src/lib/history.dom.test.ts",
|
||||
"src/lib/history.nav.dom.test.ts",
|
||||
"src/lib/history.navPosition.dom.test.tsx",
|
||||
"src/lib/history.navRestore.reload.dom.test.tsx",
|
||||
"src/lib/history.subview.dom.test.ts",
|
||||
"src/lib/hoverAndLink.contract.test.ts",
|
||||
"src/lib/http.test.ts",
|
||||
"src/lib/inputPrimitive.contract.test.ts",
|
||||
"src/lib/issueDraft.test.ts",
|
||||
"src/lib/jobResult.dom.test.ts",
|
||||
"src/lib/jsxCommentText.contract.test.ts",
|
||||
"src/lib/keymap.sequence.test.ts",
|
||||
"src/lib/keymap.test.ts",
|
||||
"src/lib/layerStack.dom.test.ts",
|
||||
"src/lib/layerStack.integration.dom.test.tsx",
|
||||
"src/lib/lazyWithPreload.dom.test.tsx",
|
||||
"src/lib/linkify.test.ts",
|
||||
"src/lib/listNav.dom.test.tsx",
|
||||
"src/lib/listNav.scope.dom.test.tsx",
|
||||
"src/lib/listPaging.test.ts",
|
||||
"src/lib/literalA11yText.contract.test.ts",
|
||||
"src/lib/localeFormat.contract.test.ts",
|
||||
"src/lib/log.contract.test.ts",
|
||||
"src/lib/log.test.ts",
|
||||
"src/lib/logHooks.dom.test.ts",
|
||||
"src/lib/logsUrlState.test.ts",
|
||||
"src/lib/moveInList.test.ts",
|
||||
"src/lib/mutationFailures.contract.test.ts",
|
||||
"src/lib/navPosition.wiring.contract.test.ts",
|
||||
"src/lib/navRing.contract.test.ts",
|
||||
"src/lib/networkBoundary.contract.test.ts",
|
||||
"src/lib/nextPage.contract.test.ts",
|
||||
"src/lib/nextPage.dom.test.ts",
|
||||
"src/lib/nextPage.wiring.contract.test.ts",
|
||||
"src/lib/nodeEngine.contract.test.ts",
|
||||
"src/lib/notifications.actionTtl.dom.test.ts",
|
||||
"src/lib/pageKeys.dom.test.tsx",
|
||||
"src/lib/pageMeta.test.ts",
|
||||
"src/lib/panelLayout.dom.test.ts",
|
||||
"src/lib/parseId.test.ts",
|
||||
"src/lib/playerModalHooks.apiload.dom.test.tsx",
|
||||
"src/lib/playerModalHooks.progress.dom.test.tsx",
|
||||
"src/lib/playerQueue.test.ts",
|
||||
"src/lib/playerStage.contract.test.ts",
|
||||
"src/lib/playerVolume.test.ts",
|
||||
"src/lib/playlistView.test.ts",
|
||||
"src/lib/plexBackdrop.dom.test.tsx",
|
||||
"src/lib/plexFinishMargin.contract.test.ts",
|
||||
"src/lib/plexImage.contract.test.ts",
|
||||
"src/lib/plexImage.test.ts",
|
||||
"src/lib/plexPlayerHooks.controls.dom.test.tsx",
|
||||
"src/lib/plexPlayerHooks.subtitle.dom.test.tsx",
|
||||
"src/lib/plexQueue.test.ts",
|
||||
"src/lib/plexRating.test.ts",
|
||||
"src/lib/plexStats.test.ts",
|
||||
"src/lib/plexUrlState.dom.test.ts",
|
||||
"src/lib/pxBox.contract.test.ts",
|
||||
"src/lib/pythonEngine.contract.test.ts",
|
||||
"src/lib/queryClient.dom.test.ts",
|
||||
"src/lib/queryKeys.test.ts",
|
||||
"src/lib/reactEngine.contract.test.ts",
|
||||
"src/lib/reinsert.test.ts",
|
||||
"src/lib/releaseNotes.test.ts",
|
||||
"src/lib/removedFeatures.contract.test.ts",
|
||||
"src/lib/repoScripts.contract.test.ts",
|
||||
"src/lib/rovingHighlight.contract.test.ts",
|
||||
"src/lib/saveLanguage.dom.test.ts",
|
||||
"src/lib/savedViews.test.ts",
|
||||
"src/lib/sortableA11y.contract.test.ts",
|
||||
"src/lib/staleBuild.dom.test.ts",
|
||||
"src/lib/store.dom.test.tsx",
|
||||
"src/lib/subscriptionCache.test.ts",
|
||||
"src/lib/surfaceRole.contract.test.ts",
|
||||
"src/lib/tabState.dom.test.ts",
|
||||
"src/lib/theme.contract.test.ts",
|
||||
"src/lib/theme.test.ts",
|
||||
"src/lib/touchTarget.contract.test.ts",
|
||||
"src/lib/uiBoundary.contract.test.ts",
|
||||
"src/lib/urlState.dom.test.ts",
|
||||
"src/lib/useCardPager.dom.test.tsx",
|
||||
"src/lib/useChipOverflowFit.dom.test.tsx",
|
||||
"src/lib/useDebounced.dom.test.ts",
|
||||
"src/lib/useDismiss.dom.test.tsx",
|
||||
"src/lib/useFilterState.dom.test.ts",
|
||||
"src/lib/useFocusKeeper.dom.test.tsx",
|
||||
"src/lib/useFocusKeeper.restore.dom.test.tsx",
|
||||
"src/lib/useHoverFocusWithin.dom.test.tsx",
|
||||
"src/lib/usePlexCollectionTitles.dom.test.tsx",
|
||||
"src/lib/usePref.dom.test.ts",
|
||||
"src/lib/usePref.saveFailure.dom.test.ts",
|
||||
"src/lib/useSortState.dom.test.ts",
|
||||
"src/lib/useTags.dom.test.tsx",
|
||||
"src/lib/useUndoable.dom.test.ts",
|
||||
"src/lib/useUrlMirror.dom.test.ts",
|
||||
"src/lib/verifyEntry.dom.test.tsx",
|
||||
"src/lib/viewportUnits.contract.test.ts",
|
||||
"src/lib/vocab.dom.test.ts"
|
||||
],
|
||||
"lib/api/core.ts": ["src/lib/api/core.ts"],
|
||||
"lib/http.ts": ["src/lib/http.ts"],
|
||||
"src": [
|
||||
"src/App.tsx",
|
||||
"src/components/About.tsx",
|
||||
"src/components/AboutCell.tsx",
|
||||
"src/components/AccountPanel.tsx",
|
||||
"src/components/ActiveFilterChips.tsx",
|
||||
"src/components/AddToPlaylist.tsx",
|
||||
"src/components/AdminLogs.tsx",
|
||||
"src/components/AdminUsers.tsx",
|
||||
"src/components/AppCommandPalette.tsx",
|
||||
"src/components/AuditEarlierRuns.tsx",
|
||||
"src/components/AuditLog.tsx",
|
||||
"src/components/BootGate.tsx",
|
||||
"src/components/ChannelAbout.tsx",
|
||||
"src/components/ChannelDiscovery.tsx",
|
||||
"src/components/ChannelLayoutGrid.tsx",
|
||||
"src/components/ChannelLink.tsx",
|
||||
"src/components/ChannelNameLink.tsx",
|
||||
"src/components/ChannelPage.tsx",
|
||||
"src/components/ChannelPicker.tsx",
|
||||
"src/components/ChannelStatsPopover.tsx",
|
||||
"src/components/ChannelTags.tsx",
|
||||
"src/components/Channels.tsx",
|
||||
"src/components/ChannelsProvider.tsx",
|
||||
"src/components/ConfigPanel.tsx",
|
||||
"src/components/ConfirmProvider.tsx",
|
||||
"src/components/DateFilterSelect.tsx",
|
||||
"src/components/DebugRecorder.tsx",
|
||||
"src/components/DiagnosticsSettings.tsx",
|
||||
"src/components/DownloadButton.tsx",
|
||||
"src/components/DownloadCenter.tsx",
|
||||
"src/components/DownloadDialog.tsx",
|
||||
"src/components/ErrorBoundary.tsx",
|
||||
"src/components/ErrorDialog.tsx",
|
||||
"src/components/ExcludedChannelList.tsx",
|
||||
"src/components/Feed.tsx",
|
||||
"src/components/FeedFiltersProvider.tsx",
|
||||
"src/components/FeedScopeToggle.tsx",
|
||||
"src/components/FeedViewProvider.tsx",
|
||||
"src/components/FilterRail.tsx",
|
||||
"src/components/Header.tsx",
|
||||
"src/components/Home.tsx",
|
||||
"src/components/IssueComposer.tsx",
|
||||
"src/components/LanguageSwitcher.tsx",
|
||||
"src/components/LibraryPlayer.tsx",
|
||||
"src/components/LogLine.tsx",
|
||||
"src/components/MediaResumeButtons.tsx",
|
||||
"src/components/MyLogViewer.tsx",
|
||||
"src/components/NavSidebar.tsx",
|
||||
"src/components/NavigationProvider.tsx",
|
||||
"src/components/NotificationsPanel.tsx",
|
||||
"src/components/OnboardingWizard.tsx",
|
||||
"src/components/PaletteLauncher.tsx",
|
||||
"src/components/PersonalViewsModal.tsx",
|
||||
"src/components/PlayerModal.tsx",
|
||||
"src/components/PlaylistLimitsHint.tsx",
|
||||
"src/components/Playlists.tsx",
|
||||
"src/components/PlaylistsProvider.tsx",
|
||||
"src/components/PlaylistsRail.tsx",
|
||||
"src/components/PlexBrowse.tsx",
|
||||
"src/components/PlexCollectionEditor.tsx",
|
||||
"src/components/PlexCollectionView.tsx",
|
||||
"src/components/PlexDiscover.tsx",
|
||||
"src/components/PlexInfo.tsx",
|
||||
"src/components/PlexLibraryPicker.tsx",
|
||||
"src/components/PlexMenu.tsx",
|
||||
"src/components/PlexPlayer.tsx",
|
||||
"src/components/PlexPlayerSettings.tsx",
|
||||
"src/components/PlexPlaylistAdd.tsx",
|
||||
"src/components/PlexPlaylistView.tsx",
|
||||
"src/components/PlexProvider.tsx",
|
||||
"src/components/PlexScopeToggle.tsx",
|
||||
"src/components/PlexSearchBox.tsx",
|
||||
"src/components/PlexSidebar.tsx",
|
||||
"src/components/PlexStatsOverlay.tsx",
|
||||
"src/components/PlexViewBridge.tsx",
|
||||
"src/components/PlexViewToggle.tsx",
|
||||
"src/components/PosterTile.tsx",
|
||||
"src/components/PrefsProvider.tsx",
|
||||
"src/components/ProfileEditor.tsx",
|
||||
"src/components/ReleaseNotes.tsx",
|
||||
"src/components/SavedViewsPopover.tsx",
|
||||
"src/components/SavedViewsProvider.tsx",
|
||||
"src/components/Scheduler.tsx",
|
||||
"src/components/SearchBar.tsx",
|
||||
"src/components/SelectedViewChip.tsx",
|
||||
"src/components/SettingsPanel.tsx",
|
||||
"src/components/SetupWizard.tsx",
|
||||
"src/components/ShareDialog.tsx",
|
||||
"src/components/Sidebar.tsx",
|
||||
"src/components/Stats.tsx",
|
||||
"src/components/SyncStatus.tsx",
|
||||
"src/components/TagFilterChips.tsx",
|
||||
"src/components/TagManager.tsx",
|
||||
"src/components/UpNextCard.tsx",
|
||||
"src/components/UploadDialog.tsx",
|
||||
"src/components/UserPicker.tsx",
|
||||
"src/components/VerifyBanner.tsx",
|
||||
"src/components/VersionBanner.tsx",
|
||||
"src/components/VideoCard.tsx",
|
||||
"src/components/VideoEditor.tsx",
|
||||
"src/components/VideoFollowUp.tsx",
|
||||
"src/components/VideoInfoCard.tsx",
|
||||
"src/components/VideoMenu.tsx",
|
||||
"src/components/ViewSwitcher.tsx",
|
||||
"src/components/VirtualFeed.tsx",
|
||||
"src/components/WatchPage.tsx",
|
||||
"src/components/Welcome.tsx",
|
||||
"src/components/WizardProvider.tsx",
|
||||
"src/components/YearFilter.tsx",
|
||||
"src/components/auditColumns.tsx",
|
||||
"src/components/channelCells.tsx",
|
||||
"src/components/channelColumns.tsx",
|
||||
"src/components/channelSync.ts",
|
||||
"src/components/channelsContext.ts",
|
||||
"src/components/comfortPin.tsx",
|
||||
"src/components/compilerSkips.testkit.ts",
|
||||
"src/components/confirmContext.ts",
|
||||
"src/components/copyItems.tsx",
|
||||
"src/components/descriptionLinks.tsx",
|
||||
"src/components/downloadAdmin.tsx",
|
||||
"src/components/downloadModals.tsx",
|
||||
"src/components/downloadRow.tsx",
|
||||
"src/components/downloadTitle.ts",
|
||||
"src/components/facetOption.ts",
|
||||
"src/components/feedFiltersContext.ts",
|
||||
"src/components/feedFiltersInit.ts",
|
||||
"src/components/feedViewContext.ts",
|
||||
"src/components/legal/LegalLayout.tsx",
|
||||
"src/components/legal/PrivacyPolicy.tsx",
|
||||
"src/components/legal/Terms.tsx",
|
||||
"src/components/legal/useHungarian.ts",
|
||||
"src/components/logLineParts.ts",
|
||||
"src/components/modulePages.tsx",
|
||||
"src/components/moduleRegistry.tsx",
|
||||
"src/components/navigationContext.ts",
|
||||
"src/components/navigationInit.ts",
|
||||
"src/components/playerModalControls.tsx",
|
||||
"src/components/playlistColumns.tsx",
|
||||
"src/components/playlistsContext.ts",
|
||||
"src/components/playlistsInit.ts",
|
||||
"src/components/plexBrowseLists.tsx",
|
||||
"src/components/plexCards.tsx",
|
||||
"src/components/plexContext.ts",
|
||||
"src/components/plexDetailAside.tsx",
|
||||
"src/components/plexDetailUi.tsx",
|
||||
"src/components/plexPlayable.ts",
|
||||
"src/components/plexPlayerControls.tsx",
|
||||
"src/components/plexSubUrl.ts",
|
||||
"src/components/plexViewContext.ts",
|
||||
"src/components/plexViews.tsx",
|
||||
"src/components/prefsContext.ts",
|
||||
"src/components/savedViewsContext.ts",
|
||||
"src/components/useChannelActions.tsx",
|
||||
"src/components/useDebugRecording.ts",
|
||||
"src/components/useDownloadActions.tsx",
|
||||
"src/components/usePlaylistActions.tsx",
|
||||
"src/components/usePlexActiveFilters.ts",
|
||||
"src/components/usePlexCardActions.tsx",
|
||||
"src/components/usePlexPlaylistEdits.ts",
|
||||
"src/components/useTagSuggestions.ts",
|
||||
"src/components/useVideoActions.tsx",
|
||||
"src/components/welcomeMode.ts",
|
||||
"src/components/wizardContext.ts",
|
||||
"src/i18n/appCopy.ts",
|
||||
"src/i18n/index.ts",
|
||||
"src/main.tsx",
|
||||
"src/vite-env.d.ts"
|
||||
],
|
||||
"src tests": [
|
||||
"src/components/AdminLogs.dom.test.tsx",
|
||||
"src/components/AdminUsers.dom.test.tsx",
|
||||
"src/components/AppCommandPalette.dom.test.tsx",
|
||||
"src/components/BootGate.dom.test.tsx",
|
||||
"src/components/ChannelLayoutGrid.dom.test.tsx",
|
||||
"src/components/ChannelNameLink.dom.test.tsx",
|
||||
"src/components/ChannelPage.backfill.dom.test.tsx",
|
||||
"src/components/ChannelPage.banner.dom.test.tsx",
|
||||
"src/components/ChannelPage.busyFocus.dom.test.tsx",
|
||||
"src/components/ChannelPage.fold.dom.test.tsx",
|
||||
"src/components/ChannelPage.watchState.dom.test.tsx",
|
||||
"src/components/ChannelStatsPopover.dom.test.tsx",
|
||||
"src/components/ChannelTags.suggest.dom.test.tsx",
|
||||
"src/components/ChannelsProvider.dom.test.tsx",
|
||||
"src/components/ConfigPanel.choice.dom.test.tsx",
|
||||
"src/components/ConfigPanel.field.dom.test.tsx",
|
||||
"src/components/ConfigPanel.sections.dom.test.tsx",
|
||||
"src/components/DateFilterSelect.dom.test.tsx",
|
||||
"src/components/DebugRecorder.dom.test.tsx",
|
||||
"src/components/DiagnosticsSettings.dom.test.tsx",
|
||||
"src/components/DownloadCenter.confirm.dom.test.tsx",
|
||||
"src/components/DownloadCenter.menu.dom.test.tsx",
|
||||
"src/components/ErrorBoundary.message.dom.test.tsx",
|
||||
"src/components/ErrorBoundary.staleBuild.dom.test.tsx",
|
||||
"src/components/ExcludedChannelList.dom.test.tsx",
|
||||
"src/components/Feed.countStale.dom.test.tsx",
|
||||
"src/components/Feed.nextPageFailed.dom.test.tsx",
|
||||
"src/components/Feed.reset.dom.test.tsx",
|
||||
"src/components/Feed.ytsort.dom.test.tsx",
|
||||
"src/components/FeedFiltersProvider.dom.test.ts",
|
||||
"src/components/FeedFiltersProvider.seed.dom.test.tsx",
|
||||
"src/components/Header.searchTip.dom.test.tsx",
|
||||
"src/components/Home.dom.test.tsx",
|
||||
"src/components/Home.pin.dom.test.tsx",
|
||||
"src/components/IssueComposer.dom.test.tsx",
|
||||
"src/components/LibraryPlayer.dom.test.tsx",
|
||||
"src/components/MyLogViewer.dom.test.tsx",
|
||||
"src/components/NavigationProvider.dom.test.tsx",
|
||||
"src/components/NotificationsPanel.menu.dom.test.tsx",
|
||||
"src/components/OnboardingWizard.dialog.dom.test.tsx",
|
||||
"src/components/OnboardingWizard.escape.dom.test.tsx",
|
||||
"src/components/PaletteLauncher.dom.test.tsx",
|
||||
"src/components/PersonalViewsModal.dom.test.tsx",
|
||||
"src/components/PlayerModal.dom.test.tsx",
|
||||
"src/components/PlaylistEditFailures.dom.test.tsx",
|
||||
"src/components/PlaylistLimitsHint.dom.test.tsx",
|
||||
"src/components/Playlists.dom.test.tsx",
|
||||
"src/components/PlaylistsRail.rename.dom.test.tsx",
|
||||
"src/components/PlexCollectionView.dom.test.tsx",
|
||||
"src/components/PlexDiscover.dom.test.tsx",
|
||||
"src/components/PlexInfo.dom.test.tsx",
|
||||
"src/components/PlexLibraryPicker.dom.test.tsx",
|
||||
"src/components/PlexMenu.dom.test.tsx",
|
||||
"src/components/PlexPersonView.dom.test.tsx",
|
||||
"src/components/PlexPlayer.backspace.dom.test.tsx",
|
||||
"src/components/PlexPlayer.controls.dom.test.tsx",
|
||||
"src/components/PlexPlayer.hls.dom.test.tsx",
|
||||
"src/components/PlexPlayer.session.dom.test.tsx",
|
||||
"src/components/PlexPlayer.strictmode.dom.test.tsx",
|
||||
"src/components/PlexPlayer.tabtrap.dom.test.tsx",
|
||||
"src/components/PlexPlayerSettings.dom.test.tsx",
|
||||
"src/components/PlexPlaylistReorder.dom.test.tsx",
|
||||
"src/components/PlexPlaylistView.header.dom.test.tsx",
|
||||
"src/components/PlexPlaylistView.menu.dom.test.tsx",
|
||||
"src/components/PlexProvider.dom.test.tsx",
|
||||
"src/components/PlexScopeToggle.dom.test.tsx",
|
||||
"src/components/PlexSearchBox.dom.test.tsx",
|
||||
"src/components/PlexSidebar.picker.dom.test.tsx",
|
||||
"src/components/PlexViewToggle.dom.test.tsx",
|
||||
"src/components/PrefsProvider.dom.test.tsx",
|
||||
"src/components/SavedViewsPopover.dom.test.tsx",
|
||||
"src/components/SavedViewsProvider.dom.test.tsx",
|
||||
"src/components/Scheduler.dom.test.tsx",
|
||||
"src/components/Scheduler.menu.dom.test.tsx",
|
||||
"src/components/SearchBar.dom.test.tsx",
|
||||
"src/components/SearchBar.keys.dom.test.tsx",
|
||||
"src/components/SettingsPanel.account.dom.test.tsx",
|
||||
"src/components/SettingsPanel.downloadAuth.dom.test.tsx",
|
||||
"src/components/ShareDialog.dom.test.tsx",
|
||||
"src/components/TagFilterChips.dom.test.tsx",
|
||||
"src/components/UpNextCard.dom.test.tsx",
|
||||
"src/components/UploadDialog.dom.test.tsx",
|
||||
"src/components/VideoCard.actions.dom.test.tsx",
|
||||
"src/components/VideoCard.channel.dom.test.tsx",
|
||||
"src/components/VideoCard.nav.dom.test.tsx",
|
||||
"src/components/VideoEditor.partial.dom.test.tsx",
|
||||
"src/components/VideoInfoCard.dom.test.tsx",
|
||||
"src/components/VideoMenu.dom.test.tsx",
|
||||
"src/components/WatchPage.dom.test.tsx",
|
||||
"src/components/WriteFailureReporting.dom.test.tsx",
|
||||
"src/components/accountPanel.dom.test.tsx",
|
||||
"src/components/adminInvites.dom.test.tsx",
|
||||
"src/components/adminPendingAccess.dom.test.tsx",
|
||||
"src/components/auditColumns.dom.test.tsx",
|
||||
"src/components/authCardStates.dom.test.tsx",
|
||||
"src/components/channelChipNames.dom.test.tsx",
|
||||
"src/components/channelColumns.dom.test.tsx",
|
||||
"src/components/channelsTagRow.contract.test.ts",
|
||||
"src/components/clientLogWiring.dom.test.tsx",
|
||||
"src/components/comfortPin.dom.test.ts",
|
||||
"src/components/comfortPin.langSwitch.dom.test.tsx",
|
||||
"src/components/compilerCoverage.contract.test.ts",
|
||||
"src/components/descriptionLinks.dom.test.tsx",
|
||||
"src/components/downloadAdmin.dom.test.tsx",
|
||||
"src/components/downloadRow.dom.test.tsx",
|
||||
"src/components/facetOption.test.ts",
|
||||
"src/components/formAlerts.dom.test.tsx",
|
||||
"src/components/legal/legalBodies.contract.dom.test.tsx",
|
||||
"src/components/listNav.wiring.contract.test.ts",
|
||||
"src/components/mediaPicture.contract.test.ts",
|
||||
"src/components/navigation.dom.test.ts",
|
||||
"src/components/playlistsInit.test.ts",
|
||||
"src/components/plexCards.dom.test.tsx",
|
||||
"src/components/plexCards.lang.dom.test.tsx",
|
||||
"src/components/plexCards.langSwitch.dom.test.tsx",
|
||||
"src/components/plexShow.aside.dom.test.tsx",
|
||||
"src/components/plexSubUrl.test.ts",
|
||||
"src/components/plexViews.restart.dom.test.tsx",
|
||||
"src/components/tagChipNames.dom.test.tsx",
|
||||
"src/components/useChannelActions.dom.test.tsx",
|
||||
"src/components/usePlaylistActions.dom.test.tsx",
|
||||
"src/components/usePlexActiveFilters.dom.test.tsx",
|
||||
"src/components/usePlexPlaylistEdits.dom.test.tsx",
|
||||
"src/components/useTagSuggestions.dom.test.tsx",
|
||||
"src/components/useVideoActions.dom.test.tsx",
|
||||
"src/components/welcomeMode.dom.test.ts",
|
||||
"src/components/welcomeMode.render.dom.test.tsx",
|
||||
"src/i18n/appCopy.dom.test.ts",
|
||||
"src/i18n/backendIds.contract.test.ts",
|
||||
"src/i18n/copyRule.test.ts",
|
||||
"src/i18n/parity.test.ts",
|
||||
"src/i18n/plurals.test.ts",
|
||||
"src/i18n/unusedKeys.contract.test.ts",
|
||||
"src/jsxWhitespace.dom.test.tsx"
|
||||
],
|
||||
"src/lib": [
|
||||
"src/lib/actionErrors.ts",
|
||||
"src/lib/activeAccount.ts",
|
||||
"src/lib/adminUsers.ts",
|
||||
"src/lib/adminUsersTab.ts",
|
||||
"src/lib/api.ts",
|
||||
"src/lib/api/account.ts",
|
||||
"src/lib/api/admin.ts",
|
||||
"src/lib/api/auth.ts",
|
||||
"src/lib/api/channels.ts",
|
||||
"src/lib/api/diagnostics.ts",
|
||||
"src/lib/api/discover.ts",
|
||||
"src/lib/api/downloads.ts",
|
||||
"src/lib/api/feed.ts",
|
||||
"src/lib/api/notifications.ts",
|
||||
"src/lib/api/playlists.ts",
|
||||
"src/lib/api/plex.ts",
|
||||
"src/lib/api/quota.ts",
|
||||
"src/lib/api/savedViews.ts",
|
||||
"src/lib/api/setup.ts",
|
||||
"src/lib/api/sync.ts",
|
||||
"src/lib/api/tagModes.ts",
|
||||
"src/lib/api/tags.ts",
|
||||
"src/lib/auditGroups.ts",
|
||||
"src/lib/backgroundTarget.ts",
|
||||
"src/lib/brandIcons.ts",
|
||||
"src/lib/browserBackgroundPlay.ts",
|
||||
"src/lib/captureTab.ts",
|
||||
"src/lib/channelDetailPoll.ts",
|
||||
"src/lib/channelLayout.ts",
|
||||
"src/lib/channelOrder.ts",
|
||||
"src/lib/channelsUrlState.ts",
|
||||
"src/lib/chipOverflow.ts",
|
||||
"src/lib/clipboard.ts",
|
||||
"src/lib/collate.ts",
|
||||
"src/lib/colorMath.ts",
|
||||
"src/lib/columnSort.ts",
|
||||
"src/lib/cropGeometry.ts",
|
||||
"src/lib/dateFilter.ts",
|
||||
"src/lib/debugRecorder.ts",
|
||||
"src/lib/diagnostics.ts",
|
||||
"src/lib/documentTitle.ts",
|
||||
"src/lib/downloadActions.ts",
|
||||
"src/lib/downloadFailure.ts",
|
||||
"src/lib/downloads.ts",
|
||||
"src/lib/entryUrl.ts",
|
||||
"src/lib/errorDialog.ts",
|
||||
"src/lib/feedSort.ts",
|
||||
"src/lib/feedView.ts",
|
||||
"src/lib/filterLog.ts",
|
||||
"src/lib/format.ts",
|
||||
"src/lib/fullscreen.ts",
|
||||
"src/lib/hints.ts",
|
||||
"src/lib/history.ts",
|
||||
"src/lib/homeIntent.ts",
|
||||
"src/lib/hueTile.ts",
|
||||
"src/lib/issueDraft.ts",
|
||||
"src/lib/jobResult.ts",
|
||||
"src/lib/keymap.ts",
|
||||
"src/lib/layerStack.ts",
|
||||
"src/lib/lazyWithPreload.ts",
|
||||
"src/lib/linkify.tsx",
|
||||
"src/lib/lintProbe.testkit.ts",
|
||||
"src/lib/listNav.ts",
|
||||
"src/lib/listPaging.ts",
|
||||
"src/lib/log.ts",
|
||||
"src/lib/logHooks.ts",
|
||||
"src/lib/logsUrlState.ts",
|
||||
"src/lib/mediaLog.ts",
|
||||
"src/lib/mediaSource.ts",
|
||||
"src/lib/modules.ts",
|
||||
"src/lib/moveInList.ts",
|
||||
"src/lib/nav.ts",
|
||||
"src/lib/nextPage.ts",
|
||||
"src/lib/notifications.ts",
|
||||
"src/lib/onboarding.ts",
|
||||
"src/lib/pageMeta.ts",
|
||||
"src/lib/panelLayout.ts",
|
||||
"src/lib/parseId.ts",
|
||||
"src/lib/playerModalHooks.ts",
|
||||
"src/lib/playerModes.ts",
|
||||
"src/lib/playerQueue.ts",
|
||||
"src/lib/playerVolume.ts",
|
||||
"src/lib/playlistName.ts",
|
||||
"src/lib/playlistView.ts",
|
||||
"src/lib/plexDetail.ts",
|
||||
"src/lib/plexImage.ts",
|
||||
"src/lib/plexPlayerHooks.ts",
|
||||
"src/lib/plexPlayerPrefs.ts",
|
||||
"src/lib/plexQueue.ts",
|
||||
"src/lib/plexRating.ts",
|
||||
"src/lib/plexSearch.ts",
|
||||
"src/lib/plexSort.ts",
|
||||
"src/lib/plexStats.ts",
|
||||
"src/lib/plexUrlState.ts",
|
||||
"src/lib/plexWatch.ts",
|
||||
"src/lib/progressCache.ts",
|
||||
"src/lib/queryClient.ts",
|
||||
"src/lib/queryKeys.ts",
|
||||
"src/lib/reinsert.ts",
|
||||
"src/lib/releaseNotes.ts",
|
||||
"src/lib/saveLanguage.ts",
|
||||
"src/lib/savedViews.ts",
|
||||
"src/lib/seekableEnd.ts",
|
||||
"src/lib/sortOptions.ts",
|
||||
"src/lib/staleBuild.ts",
|
||||
"src/lib/storage.ts",
|
||||
"src/lib/store.ts",
|
||||
"src/lib/subscriptionCache.ts",
|
||||
"src/lib/tableColumns.ts",
|
||||
"src/lib/tagPicks.ts",
|
||||
"src/lib/textSelection.ts",
|
||||
"src/lib/theme.ts",
|
||||
"src/lib/toastDock.ts",
|
||||
"src/lib/urlState.ts",
|
||||
"src/lib/useActiveFilters.ts",
|
||||
"src/lib/useCardPager.ts",
|
||||
"src/lib/useChannelNames.ts",
|
||||
"src/lib/useChannelPriority.ts",
|
||||
"src/lib/useChannelTagActions.ts",
|
||||
"src/lib/useChipOverflowFit.ts",
|
||||
"src/lib/useDebounced.ts",
|
||||
"src/lib/useDismiss.ts",
|
||||
"src/lib/useFilterFold.ts",
|
||||
"src/lib/useFilterState.ts",
|
||||
"src/lib/useFocusKeeper.ts",
|
||||
"src/lib/useHoverFocusWithin.ts",
|
||||
"src/lib/useLatest.ts",
|
||||
"src/lib/useLiveQuery.ts",
|
||||
"src/lib/useMe.ts",
|
||||
"src/lib/useMediaQuery.ts",
|
||||
"src/lib/usePlexCollectionTitles.ts",
|
||||
"src/lib/usePref.ts",
|
||||
"src/lib/useScrollEdges.ts",
|
||||
"src/lib/useScrollFade.ts",
|
||||
"src/lib/useSortState.ts",
|
||||
"src/lib/useTags.ts",
|
||||
"src/lib/useUndoable.ts",
|
||||
"src/lib/useUrlMirror.ts",
|
||||
"src/lib/useVerifyEntry.ts",
|
||||
"src/lib/version.ts",
|
||||
"src/lib/vocab.ts",
|
||||
"src/lib/youtube.ts",
|
||||
"src/lib/youtubeErrors.ts"
|
||||
],
|
||||
"ts configs": [
|
||||
"devOnlyGuard.ts",
|
||||
"playwright.config.ts",
|
||||
"reactPlugin.ts",
|
||||
"vite.config.ts",
|
||||
"vitest.config.ts"
|
||||
],
|
||||
"ui": [
|
||||
"src/components/ui/ActionMenu.tsx",
|
||||
"src/components/ui/AnchoredPopover.tsx",
|
||||
"src/components/ui/Avatar.tsx",
|
||||
"src/components/ui/BackToTop.tsx",
|
||||
"src/components/ui/Badge.tsx",
|
||||
"src/components/ui/Banner.tsx",
|
||||
"src/components/ui/Button.tsx",
|
||||
"src/components/ui/Carousel.tsx",
|
||||
"src/components/ui/Chip.tsx",
|
||||
"src/components/ui/ClampedText.tsx",
|
||||
"src/components/ui/ColumnSortControl.tsx",
|
||||
"src/components/ui/Combobox.tsx",
|
||||
"src/components/ui/CommandPalette.tsx",
|
||||
"src/components/ui/ContextMenu.tsx",
|
||||
"src/components/ui/CountBadge.tsx",
|
||||
"src/components/ui/DataTable.tsx",
|
||||
"src/components/ui/DraftSaveBar.tsx",
|
||||
"src/components/ui/DragHandle.tsx",
|
||||
"src/components/ui/FilterChipGroup.tsx",
|
||||
"src/components/ui/FilterFold.tsx",
|
||||
"src/components/ui/FilterSelect.tsx",
|
||||
"src/components/ui/FocusSafeButton.tsx",
|
||||
"src/components/ui/Headings.tsx",
|
||||
"src/components/ui/HiddenFilterCue.tsx",
|
||||
"src/components/ui/IconButton.tsx",
|
||||
"src/components/ui/InlineNotice.tsx",
|
||||
"src/components/ui/Input.tsx",
|
||||
"src/components/ui/ItemCount.tsx",
|
||||
"src/components/ui/KeyboardHelp.tsx",
|
||||
"src/components/ui/LinkButton.tsx",
|
||||
"src/components/ui/MatchToggle.tsx",
|
||||
"src/components/ui/MediaCard.tsx",
|
||||
"src/components/ui/MediaPreview.tsx",
|
||||
"src/components/ui/MenuCheck.tsx",
|
||||
"src/components/ui/Modal.tsx",
|
||||
"src/components/ui/MoreButton.tsx",
|
||||
"src/components/ui/NativeControls.tsx",
|
||||
"src/components/ui/NavGrid.tsx",
|
||||
"src/components/ui/NavRail.tsx",
|
||||
"src/components/ui/OrderButtons.tsx",
|
||||
"src/components/ui/Overlay.tsx",
|
||||
"src/components/ui/PageMenu.tsx",
|
||||
"src/components/ui/PageScroller.tsx",
|
||||
"src/components/ui/PageShell.tsx",
|
||||
"src/components/ui/Pager.tsx",
|
||||
"src/components/ui/PanelGroup.tsx",
|
||||
"src/components/ui/PanelGroups.tsx",
|
||||
"src/components/ui/Pressable.tsx",
|
||||
"src/components/ui/PriorityStepper.tsx",
|
||||
"src/components/ui/QueryState.tsx",
|
||||
"src/components/ui/RelativeTime.tsx",
|
||||
"src/components/ui/SegmentedToggle.tsx",
|
||||
"src/components/ui/SelectTrigger.tsx",
|
||||
"src/components/ui/SidePanel.tsx",
|
||||
"src/components/ui/Skeleton.tsx",
|
||||
"src/components/ui/SkipLink.tsx",
|
||||
"src/components/ui/SortControl.tsx",
|
||||
"src/components/ui/StatTiles.tsx",
|
||||
"src/components/ui/Tabs.tsx",
|
||||
"src/components/ui/Toaster.tsx",
|
||||
"src/components/ui/Tooltip.tsx",
|
||||
"src/components/ui/UndoToolbar.tsx",
|
||||
"src/components/ui/VirtualGrid.tsx",
|
||||
"src/components/ui/anchoredPopoverHooks.ts",
|
||||
"src/components/ui/backToTopState.ts",
|
||||
"src/components/ui/buttonStyles.ts",
|
||||
"src/components/ui/chipStyles.ts",
|
||||
"src/components/ui/dialogFocus.ts",
|
||||
"src/components/ui/fieldStyles.ts",
|
||||
"src/components/ui/form.tsx",
|
||||
"src/components/ui/gridColumns.ts",
|
||||
"src/components/ui/landmarks.ts",
|
||||
"src/components/ui/mediaCardStyles.ts",
|
||||
"src/components/ui/menuGroups.ts",
|
||||
"src/components/ui/overlayOrder.ts",
|
||||
"src/components/ui/pageHeading.ts",
|
||||
"src/components/ui/pageMenuItems.ts",
|
||||
"src/components/ui/pageMenuSlot.ts",
|
||||
"src/components/ui/pageToolbarSlot.ts",
|
||||
"src/components/ui/pagerContent.ts",
|
||||
"src/components/ui/progressiveRows.ts",
|
||||
"src/components/ui/readAhead.ts",
|
||||
"src/components/ui/reveal.ts",
|
||||
"src/components/ui/scrollFold.ts",
|
||||
"src/components/ui/sortableA11y.ts",
|
||||
"src/components/ui/stickyTop.ts",
|
||||
"src/components/ui/surfaces.ts",
|
||||
"src/components/ui/tabPanel.ts",
|
||||
"src/components/ui/tableAlign.ts",
|
||||
"src/components/ui/tableHeader.tsx",
|
||||
"src/components/ui/toastLevels.ts",
|
||||
"src/components/ui/useContextMenu.ts",
|
||||
"src/components/ui/useMediaPreview.ts",
|
||||
"src/components/ui/useOrderItems.tsx",
|
||||
"src/components/ui/useReturnFocus.ts"
|
||||
],
|
||||
"ui tests": [
|
||||
"src/components/ui/ActionMenu.dom.test.tsx",
|
||||
"src/components/ui/AnchoredPopover.dom.test.tsx",
|
||||
"src/components/ui/AnchoredPopover.returnFocus.dom.test.tsx",
|
||||
"src/components/ui/Avatar.dom.test.tsx",
|
||||
"src/components/ui/BackToTop.dom.test.tsx",
|
||||
"src/components/ui/Banner.dom.test.tsx",
|
||||
"src/components/ui/Button.dom.test.tsx",
|
||||
"src/components/ui/Carousel.dom.test.tsx",
|
||||
"src/components/ui/Chip.dom.test.tsx",
|
||||
"src/components/ui/ClampedText.dom.test.tsx",
|
||||
"src/components/ui/Combobox.dom.test.tsx",
|
||||
"src/components/ui/CommandPalette.dom.test.tsx",
|
||||
"src/components/ui/CommandPalette.ordering.dom.test.tsx",
|
||||
"src/components/ui/ContextMenu.dom.test.tsx",
|
||||
"src/components/ui/DataTable.fixedLayout.dom.test.tsx",
|
||||
"src/components/ui/DataTable.keyboard.dom.test.tsx",
|
||||
"src/components/ui/DataTable.progressive.dom.test.tsx",
|
||||
"src/components/ui/FilterChipGroup.dom.test.tsx",
|
||||
"src/components/ui/FilterFold.dom.test.tsx",
|
||||
"src/components/ui/FilterSelect.dom.test.tsx",
|
||||
"src/components/ui/FocusSafeButton.select.dom.test.tsx",
|
||||
"src/components/ui/HiddenFilterCue.dom.test.tsx",
|
||||
"src/components/ui/IconButton.dom.test.tsx",
|
||||
"src/components/ui/Input.dom.test.tsx",
|
||||
"src/components/ui/ItemCount.dom.test.tsx",
|
||||
"src/components/ui/KeyboardHelp.dom.test.tsx",
|
||||
"src/components/ui/MatchToggle.dom.test.tsx",
|
||||
"src/components/ui/Modal.dom.test.tsx",
|
||||
"src/components/ui/Modal.fullscreen.dom.test.tsx",
|
||||
"src/components/ui/Modal.purpose.dom.test.tsx",
|
||||
"src/components/ui/Modal.strictmode.dom.test.tsx",
|
||||
"src/components/ui/NavRail.dom.test.tsx",
|
||||
"src/components/ui/OrderButtons.dom.test.tsx",
|
||||
"src/components/ui/PageMenu.dom.test.tsx",
|
||||
"src/components/ui/PageScroller.dom.test.tsx",
|
||||
"src/components/ui/PageShell.dom.test.tsx",
|
||||
"src/components/ui/Pager.dom.test.tsx",
|
||||
"src/components/ui/PanelGroups.dom.test.tsx",
|
||||
"src/components/ui/PriorityStepper.dom.test.tsx",
|
||||
"src/components/ui/QueryState.dom.test.tsx",
|
||||
"src/components/ui/RelativeTime.dom.test.tsx",
|
||||
"src/components/ui/SegmentedToggle.dom.test.tsx",
|
||||
"src/components/ui/SidePanel.drawer.dom.test.tsx",
|
||||
"src/components/ui/SkipLink.dom.test.tsx",
|
||||
"src/components/ui/SortControl.dom.test.tsx",
|
||||
"src/components/ui/StatTiles.dom.test.tsx",
|
||||
"src/components/ui/Tabs.dom.test.tsx",
|
||||
"src/components/ui/Toaster.dom.test.tsx",
|
||||
"src/components/ui/Toaster.hosts.test.ts",
|
||||
"src/components/ui/Toaster.strictmode.dom.test.tsx",
|
||||
"src/components/ui/Tooltip.layer.dom.test.tsx",
|
||||
"src/components/ui/dialogFocus.dom.test.tsx",
|
||||
"src/components/ui/form.dom.test.tsx",
|
||||
"src/components/ui/gridColumns.test.ts",
|
||||
"src/components/ui/hiddenFilterCue.contract.test.ts",
|
||||
"src/components/ui/mediaRail.contract.test.ts",
|
||||
"src/components/ui/menuCheck.contract.test.ts",
|
||||
"src/components/ui/pageMenuScope.contract.test.ts",
|
||||
"src/components/ui/pressableSelectable.contract.test.ts",
|
||||
"src/components/ui/readAhead.test.ts",
|
||||
"src/components/ui/scrollFold.test.ts",
|
||||
"src/components/ui/sortableA11y.dom.test.tsx",
|
||||
"src/components/ui/stickyTop.dom.test.tsx",
|
||||
"src/components/ui/tailwindScan.contract.test.ts",
|
||||
"src/components/ui/uiI18n.contract.test.ts",
|
||||
"src/components/ui/useItemTriggers.dom.test.ts",
|
||||
"src/components/ui/useReturnFocus.dom.test.tsx"
|
||||
]
|
||||
}
|
||||
},
|
||||
"lanes": {
|
||||
"backend/Dockerfile.test": [
|
||||
"FROM python:3.12-slim",
|
||||
|
||||
Reference in New Issue
Block a user