All files / src/state devFeatures.ts

93.93% Statements 31/33
93.33% Branches 14/15
100% Functions 5/5
93.93% Lines 31/33

Press n or j to go to the next uncovered block, b, p or k for the previous block.

1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106                                                      2x     2x         2x   2x   2x 2x       2x               2x 3356x 130x 130x 130x                 2x 3350x 3350x                 2x 115x 115x 115x 115x     115x 115x 115x   130x         130x 105x 104x 104x             2x 225x 225x  
/**
 * Dev features: on `main`, invisible unless the page asked for them (§29).
 *
 * A dev feature is built, specified and tested like any other, and a reader
 * who has not asked for it sees **nothing** of it — no row, no task, no
 * command, no example, no clause in a sentence. Not greyed, not listed with an
 * instruction: §8.5's corrected form would list a control the reader could make
 * work, and this is the one case where there is something they could do and
 * they are deliberately not told.
 *
 * **The registry is the list below**, one line each with the reason it is not
 * public yet. Making a feature public is deleting its line — and then the
 * compiler names every gate it leaves behind, because {@link DevFeature} is the
 * registry's keys: a `devFeature('metacool')` whose key no longer exists is a
 * type error rather than a gate that quietly always opens.
 *
 * **The switch is the page's address**, read once per page, so nothing about
 * it is kept in the browser. The answer must not change under a page that was
 * built with the other one: every surface is built once with it, so no card,
 * task list, worker or cached command list can disagree with it.
 *
 * **Never in an embed** (§21): a host page is not a developer's browser.
 * **Always on where there is no page** — the command line has no switch and
 * shows everything (§29.5), and it shares the task registry and the examples
 * that ask this.
 */
 
import { embedded } from '../embed'
 
/** What is dev today, and why. Delete a line to make that feature public. */
export const DEV_FEATURES = {
  /**
   * Both containers, the genome layer, its views and tasks, and every sentence naming them —
   * and the DNA Bacterial Zoo's store, which is published with them (§24.3).
   */
  metacool: 'The multi-genome format, and the bacterial zoo made beside it, are not published yet.',
  /** The reference editor (§27): the stage, its task, its keys and the lens. */
  edit: 'Editing the reference is not validated yet.',
  /** Two sequences on two axes (§28). */
  dotplot: 'The dot plot is not validated yet.',
} as const
 
export type DevFeature = keyof typeof DEV_FEATURES
 
let cached: boolean | null = null
 
/**
 * Whether the dev features are on in this page.
 *
 * Read once and remembered: the answer must not change under a page that was
 * built with the other one. `false` always in an embed.
 */
export function devFeaturesOn(): boolean {
  if (cached !== null) return cached
  cached = read()
  return cached
}
 
/**
 * Whether one dev feature is to be shown.
 *
 * Every registered feature answers to the one switch; the name is taken so that
 * each gate says which feature it is, and so that deleting a registry line turns
 * that feature's gates into compile errors (see the header).
 */
export function devFeature(feature: DevFeature): boolean {
  return DEV_FEATURES[feature] !== undefined && devFeaturesOn()
}
 
/**
 * Whether a query string asks for the dev features.
 *
 * Pure, so the whole contract is testable without a `location`. On for a key
 * that is present, off for the three spellings of "no" — the same reading as
 * `embed` (`embed.ts`).
 */
export function readDevFeatures(search: string): boolean {
  let params: URLSearchParams
  try {
    params = new URLSearchParams(search)
  } catch {
    return false
  }
  const flag = params.get('dev')
  return flag !== null && flag !== '0' && flag !== 'false' && flag !== 'off'
}
 
function read(): boolean {
  // No page, no switch: the command line — which imports the same task
  // registry and the same examples — sees everything, as §29.5 says it does. A
  // worker has no `window` either, and is told the page's answer on each open
  // instead of asking this.
  if (typeof window === 'undefined') return true
  if (embedded()) return false
  return readDevFeatures(window.location.search)
}
 
/**
 * Forget the remembered answer, or state one. For tests, and for the files the
 * site build writes for language models (`llms/render.ts`): a page reads it
 * once, and a test or a build under `node` has no address to put an answer in.
 */
export function resetDevFeatures(on: boolean | null = null): void {
  cached = on
}