Skip to content

refactor: derive platform allow-lists from the canonical device tuples#895

Merged
thymikee merged 1 commit into
mainfrom
refactor/derive-platform-allowlists
Jun 27, 2026
Merged

refactor: derive platform allow-lists from the canonical device tuples#895
thymikee merged 1 commit into
mainfrom
refactor/derive-platform-allowlists

Conversation

@thymikee

Copy link
Copy Markdown
Member

What

The set of supported platforms was hand-restated in three places that must agree:

  1. The canonical tuples in src/utils/device.ts (PLATFORMS / PLATFORM_SELECTORS).
  2. The --platform flag's enumValues in src/utils/cli-flags.ts.
  3. The leaf-platform validation in normalizeOpenDevice in src/client-normalizers.ts.

This PR makes copies (2) and (3) derive from the canonical source so they cannot drift.

  • Export PLATFORMS from device.ts and add an isPlatform() leaf-platform type guard derived from it. isPlatform accepts exactly PLATFORMS and rejects the apple selector (which is a routing selector, not a concrete device platform).
  • cli-flags.ts --platform: enumValues and usageLabel now derive from PLATFORM_SELECTORS (the CLI accepts the leaf platforms plus the apple selector).
  • client-normalizers.ts normalizeOpenDevice: the leaf-platform gate now uses isPlatform(). Per-platform udid/serial identifier shaping is unchanged.

Why

Three independent restatements of the same allow-list invite silent drift when a platform is added or renamed. Deriving the two non-canonical copies from device.ts makes the canonical tuples the single source of truth.

Behaviorless

The derived sets are identical to the previous hardcoded sets:

  • --platform enumValues stay ['ios','macos','android','linux','web','apple'].
  • normalizeOpenDevice still accepts exactly ['ios','macos','android','linux','web'] and rejects apple/unknown platforms.

New tests assert these memberships explicitly so the derivation is pinned.

Validation

  • node_modules/.bin/tsc -p tsconfig.json --noEmit (exit 0)
  • node ./node_modules/oxfmt/bin/oxfmt --write <changed .ts files>
  • node_modules/.bin/oxlint <changed .ts files> --deny-warnings (exit 0)
  • node_modules/.bin/vitest run cli-flags client-normalizers args device (12 files, 197 tests passed)

The set of platforms was hand-restated in three places that must agree:
the canonical tuples in src/utils/device.ts, the --platform flag's
enumValues in cli-flags.ts, and the leaf-platform validation in
client-normalizers.ts. The two non-canonical copies could drift.

- Export PLATFORMS from device.ts and add an isPlatform() leaf-platform
  type guard derived from it (excludes the `apple` selector).
- Derive the --platform enumValues and usageLabel from PLATFORM_SELECTORS.
- Derive normalizeOpenDevice's leaf-platform check from isPlatform();
  per-platform udid/serial identifier shaping is unchanged.

Behaviorless: the derived sets equal the previous hardcoded sets.
@github-actions

Copy link
Copy Markdown

Size Report

Metric Base Current Diff
JS raw 1.4 MB 1.4 MB -43 B
JS gzip 445.2 kB 445.2 kB -19 B
npm tarball 583.3 kB 583.3 kB -33 B
npm unpacked 2.0 MB 2.0 MB -43 B

Startup median (7 runs, lower is better):

Scenario Base Current Diff
CLI --version 27.4 ms 27.5 ms +0.2 ms
CLI --help 48.1 ms 47.8 ms -0.3 ms

Top changed chunks:

Chunk Raw diff Gzip diff
dist/src/2948.js -103 B -42 B

@thymikee

Copy link
Copy Markdown
Member Author

Reviewed — merge-ready. The derivation is genuinely behaviorless: PLATFORM_SELECTORS equals the old ['ios','macos','android','linux','web','apple'] byte-for-byte, and isPlatform() matches the old !== chain on every probe (incl. undefined/null/''/numbers/apple/windows). The new tests pin both memberships explicitly, and this lines up with the pattern already in use (contracts.ts, remote-config-schema.ts, command-input.ts all consume PLATFORM_SELECTORS). CI is green across typecheck/unit/integration.

One non-blocking follow-up — same drift you're killing, just out of scope here: there's a fourth restatement of the leaf-platform allow-list at src/replay/script.ts:19, REPLAY_METADATA_PLATFORMS. It's typed Exclude<PlatformSelector, 'apple'> (i.e. Platform) but hardcoded as a Set of only ['ios','android','macos','linux'] — it already omits 'web', so a context platform=web line is silently dropped (script.ts:81) even though the type permits it. Worth deriving it from PLATFORMS too, or narrowing the type if web replay is intentionally unsupported.

Residual risk: ~none. Co-edits src/utils/device.ts with #896 (AppleOS) but in independent regions — no correctness coupling and no required merge order; at most a trivial rebase.

@thymikee

Copy link
Copy Markdown
Member Author

Thanks @thymikee — opened the follow-up in #899. It narrows ReplayScriptPlatform to Exclude<PlatformSelector, 'apple' | 'web'> and derives REPLAY_METADATA_PLATFORMS from the canonical PLATFORM_SELECTORS instead of restating it a 4th time. Behaviorless: context platform=web was already dropped and stays dropped — now intentionally, type-safe, and from a single source. web support remains a separate feature decision. Doesn't depend on this PR.

@thymikee thymikee merged commit d16f01c into main Jun 27, 2026
20 checks passed
@thymikee thymikee deleted the refactor/derive-platform-allowlists branch June 27, 2026 11:48
@github-actions

Copy link
Copy Markdown
PR Preview Action v1.8.1
Preview removed because the pull request was closed.
2026-06-27 11:49 UTC

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

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant