Skip to content

Freeze the remaining exported constants that double as default arguments #18

Description

@Hugoer

lib/crux-client.js freezes CRUX_FORM_FACTORS and DEFAULT_CRUX_FORM_FACTORS (#17), but four more exported constants have exactly the same shape and were left mutable. Split out of #17 rather than folded in, because it is a sweep across three modules and one of them changes a published type.

The hazard

Each of these is exported from a public subpath and used as a default parameter value or a shared argument inside the library, so a consumer mutating one changes behaviour for every later call in the process:

Constant Module Exported via Used as
DEFAULT_PSI_STRATEGIES lib/psi.js:10 web-perf-cli/psi default strategies in runPsi, runPsiBatch, runPsiAuditBatch
PSI_STRATEGIES lib/psi.js:9 web-perf-cli/psi validation list
CHROME_FLAGS lib/lab.js:15 web-perf-cli/lab, root passed to every Chrome launch in lab.js and links.js
DEFAULT_SKIP_AUDITS lib/lab.js:13 web-perf-cli/lab, root default in buildLighthouseConfig
LAB_CATEGORIES lib/profiles.js:4 web-perf-cli/profiles category validation

The PSI one is the closest analogue to what was fixed:

const { DEFAULT_PSI_STRATEGIES } = require('@hugoer/web-perf-cli/psi');
DEFAULT_PSI_STRATEGIES.push('desktop');
// every later runPsi / runPsiBatch / runPsiAuditBatch now issues an extra request per URL,
// against the 25,000/day PSI quota

CHROME_FLAGS is the sharpest: a consumer could append a flag that silently changes how every subsequent audit launches Chrome, which would quietly invalidate scores rather than just cost quota.

Do the same thing #17 did, including the typing

Freezing alone is not enough — that was the lesson from #17. DEFAULT_CRUX_FORM_FACTORS was declared string[], so it could never be passed back into the option it was the default for, and freezing it only changed the error code. Each constant here needs both halves:

  1. Object.freeze, with an element type where the values are a closed set (DEFAULT_PSI_STRATEGIES should be readonly PsiStrategy[], not readonly string[]).
  2. The options that accept it widened to readonly T[], so the exported constant can be passed to the function it is the default for.
  3. A type-tests/ assertion that passes the constant into that option, plus a @ts-expect-error on a mutation. A spread compiles against both readonly and mutable shapes, so a spread alone asserts nothing.

Published type change to expect

CHROME_FLAGS is currently string[] and becomes readonly string[]. lib/links.js and lib/lab.js both pass it straight to chromeLauncher.launch({ chromeFlags }); check whether chrome-launcher's own types accept a readonly array before committing to the freeze, and spread at the call site if not.

Verification

npm run lint, npm test, npm run generate-types, npm run check-types, and the new type-tests assertions mutation-checked by reverting each freeze in turn.

Activity

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Metadata

Metadata

Assignees

No one assigned

    Labels

    bugSomething isn't working

    Projects

    No projects

      Milestone

      No milestone

      Relationships

      None yet

      Development

      No branches or pull requests

      Issue actions