You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
svelte-kit-vice/audit/components/table.md

4.2 KiB

Audit: table

audit-version: 1 audited-at: 2026-06-26 scope: method: adversarially-verified workflow (analyze → refute); HIGH lead-verified by direct read of the cited code. B5 ground-truth: the A31 O(N²) isSelected/isExpanded (.includes from a per-item $derived) is confirmed across listbox/grid-list/tree-view/tree-grid/tag-group (SYS-7); rovingTargetEl is correctly LIFTED everywhere (not A31); virtual-* use SvelteMap (A33-clean). provider: src/uix/soma/components/table/table-provider.svelte.ts reactivity (A31/A33/A35): ">ALERT A31 DUAL HAZARD: (1) Line 296: per-row $derived.by reading global selection state via getIsRowSelected() — O(N) re-runs when selection changes. (2) Line 518: per-row $derived.by calling getVisibleColumns().length — O(N*M) filter ops for N rows and M columns. Both are HIGH severity and should be lifted to provider-level caches with O(1) per-item lookups."

Summary

Counts (post-verification): CRITICAL 0 · HIGH 0 · MEDIUM 1 · LOW 0.

Findings

MEDIUM: A31 (per-item derived calling filtering operation) — table-002

  • dimension: B (Behavior / Reactivity)
  • rule: A31 (per-item derived calling filtering operation)
  • location: src/uix/soma/components/table/table-provider.svelte.ts:518
  • evidence: readonly colSpan = $derived.by(() => this.provider.table.getVisibleColumns().length);

In TableRowDetailProvider (per-row), calling getVisibleColumns() which internally filters resolvedColumns array. This runs on every row.

  • impact: For a table with N rows and M columns, showing a row detail calls getVisibleColumns() which is O(M) filter operation. Total complexity becomes O(N*M). On a 50-row table with 10 columns, that's 500 filter operations just to compute colSpan.
  • repro: Create a table with visibility toggling. Show row details and toggle column visibility. Monitor performance and observe the getVisibleColumns() is called per row on every visibility toggle.
  • proposed-fix: Move visibleColumns to a table-level $derived and cache it: const visibleColumns = $derived.by(() => resolvedColumns.filter(col => getIsColumnVisible(col.id))). Each RowDetail then accesses a single cached array and reads .length in O(1).
  • verify: [downgraded] Confirmed the A31 pattern but HIGH overstated → MEDIUM. table-provider.svelte.ts:518 readonly colSpan = $derived.by(() => this.provider.table.getVisibleColumns().length) is a per-RowDetail derived calling a provider method (table-core.svelte.ts:532-534) function getVisibleColumns() { return resolvedColumns.filter((col) => getIsColumnVisible(col.id)); } — O(M) filter reading the global columnVisibility $state. It re-runs whenever columnVisibility is reassigned (toggleColumnVisibility line 537 columnVisibility = { ...columnVisibility, [columnId]: ... }). So the pattern IS real. But the candidate's O(N*M) and 'hang at 30+ rows' framing is wrong on two counts: (1) RowDetailProviders are mounted one-per-rendered-row, and the rendered set is PAGE-BOUNDED (default pageSize=10, table-core.svelte.ts:446) — not total row count N; (2) the trigger is a column-visibility toggle, a rare user action, not a per-keystroke / per-selection mutation. Real cost is O(pageSize × M) per infrequent toggle, with M (column count) small and the filter cheap. That is a legitimate micro-optimization (lift visibleColumns to a single table-level $derived so each RowDetail reads .length in O(1)) but not a behavior/scaling hazard users hit — MEDIUM, not HIGH.
  • fix-status: open

No-findings dimensions

A (Contract/Morfo), C (DOM-selector safety), D (Frontier / Eidos imports), E (TSC / Theming), A34 (require() topology)

Theming facts (E-bis)

  • magic z-index: Acceptable local z-index: 2 within sticky header subtree (line 190 of table.css) — local stacking context, not global
  • magic literals: none
  • undeclared parts: none
  • roles clean: true · variants clean: true

Tests (F)

  • exists: true · env: jsdom
  • covers: provider props mapping; row selection toggling; row detail open/close; sort state; pinned column styling
  • untested: A31 quadratic hazard with large row counts; column visibility changes with row details; multi-row bulk operations (toggleAllSelection); sorting + detail interaction

Powered by TurnKey Linux.