refactor(core): apply /simplify findings on validation PR

- core.types.ts: export COMPOSITION_VARIABLE_TYPES, a runtime tuple of
  every CompositionVariableType variant guarded by `as const satisfies
  readonly CompositionVariableType[]`. Adding a new variant to the union
  without also adding it to the tuple becomes a compile error rather
  than silent drift in callers that maintain their own list.
- composition.ts (lint rule): the local
  `new Set(["string","number","color","boolean","enum"])` now derives
  from COMPOSITION_VARIABLE_TYPES instead of duplicating the list.
- index.ts: export COMPOSITION_VARIABLE_TYPES alongside the rest of the
  variable type guards.

Reuse + efficiency reviews otherwise clean. The other reuse finding
(loadProjectHtml helper to dedupe readFileSync + ensureDOMParser across
3 callers) is real but reaches files outside this PR's scope; it's a
better fit as a follow-up cleanup once the variable-feature stack lands.

All 48 composition lint tests + 49 core suite tests still green.

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
This commit is contained in:
James
2026-05-04 20:06:10 +00:00
committed by James Russo
co-authored by Claude Opus 4.7
parent c1b6efd9c5
commit 09da5db436
3 changed files with 17 additions and 1 deletions
+14
View File
@@ -88,6 +88,20 @@ export interface TimelineCompositionElement extends TimelineElementBase {
// Composition Variable Types // Composition Variable Types
export type CompositionVariableType = "string" | "number" | "color" | "boolean" | "enum"; export type CompositionVariableType = "string" | "number" | "color" | "boolean" | "enum";
/**
* Runtime list of every valid `CompositionVariableType`. Use this anywhere
* a Set/array of valid type strings is needed (lint rules, validators).
* The `satisfies` guard turns adding a new variant to the union without
* also adding it here into a compile error.
*/
export const COMPOSITION_VARIABLE_TYPES = [
"string",
"number",
"color",
"boolean",
"enum",
] as const satisfies readonly CompositionVariableType[];
export interface CompositionVariableBase { export interface CompositionVariableBase {
id: string; id: string;
type: CompositionVariableType; type: CompositionVariableType;
+1
View File
@@ -38,6 +38,7 @@ export {
CANVAS_DIMENSIONS, CANVAS_DIMENSIONS,
TIMELINE_COLORS, TIMELINE_COLORS,
DEFAULT_DURATIONS, DEFAULT_DURATIONS,
COMPOSITION_VARIABLE_TYPES,
isTextElement, isTextElement,
isMediaElement, isMediaElement,
isCompositionElement, isCompositionElement,
+2 -1
View File
@@ -1,5 +1,6 @@
import type { LintContext, HyperframeLintFinding } from "../context"; import type { LintContext, HyperframeLintFinding } from "../context";
import { findHtmlTag, readAttr, readJsonAttr, truncateSnippet } from "../utils"; import { findHtmlTag, readAttr, readJsonAttr, truncateSnippet } from "../utils";
import { COMPOSITION_VARIABLE_TYPES } from "../../core.types";
// Agent guidance thresholds: warning-only nudges for files/tracks that become hard // Agent guidance thresholds: warning-only nudges for files/tracks that become hard
// to inspect and revise reliably in a single composition. // to inspect and revise reliably in a single composition.
@@ -475,7 +476,7 @@ export const compositionRules: Array<(ctx: LintContext) => HyperframeLintFinding
} }
const findings: HyperframeLintFinding[] = []; const findings: HyperframeLintFinding[] = [];
const knownTypes = new Set(["string", "number", "color", "boolean", "enum"]); const knownTypes = new Set<string>(COMPOSITION_VARIABLE_TYPES);
for (let i = 0; i < parsed.length; i += 1) { for (let i = 0; i < parsed.length; i += 1) {
const entry = parsed[i]; const entry = parsed[i];
if (!entry || typeof entry !== "object" || Array.isArray(entry)) { if (!entry || typeof entry !== "object" || Array.isArray(entry)) {