Predicate Types

A type that is a function you can run — and, because it is verified safe, one that can also validate, autocomplete, and travel as JSON Schema.

/#
## The gap this fills

JSON Schema describes STRUCTURE well and computation not at all. "A string"
it can say. "A valid CSS colour" it cannot — that is `#f00`, `rgb(1 2 3)`,
`color-mix(in oklch, red, blue)` and 148 named keywords, which is a grammar,
not a shape.

So every real schema ends up with a `pattern` that is a bad approximation,
or gives up and says `type: 'string'`.

A **predicate** is the missing half: an ordinary function returning true or
false. The interesting part is not that you can write one — it is what
becomes possible once the toolchain can PROVE the function is safe.

## Verified-safe means three things follow

A predicate cluster is checked to be pure, synchronous and composable: no IO,
no clock, no randomness, no unbounded regex backtracking. Given that proof:

1. **It compiles to native JavaScript** — validation costs a function call,
   not an interpreter.
2. **It can be MINED for completions.** The verifier already knows the
   keyword sets and `startsWith` guards inside the predicate, so autocomplete
   comes from the same source as validation and cannot disagree with it.
3. **It serialises into JSON Schema** as the `$predicate` keyword. Naive
   validators see ordinary structure and ignore it; aware ones run the
   predicate. Progressive enhancement, not a fork.

That last point is the strategic one: types stop being erased annotations and
become portable, executable artifacts. See `docs/type-system-north-star.md`.

## `tjs-lang/css` is this made real

Not a toy — the CSS validators are built from verified predicates, and
`suggestColor` mines its completions from the same predicate that validates.
The suggestions are guaranteed valid because they are run through the
compiled predicate before being offered.
#/

import { isColor, suggestColor } from 'tjs-lang/css'

// --- A predicate is just a function: run it ---
console.log('validation:')
console.log('  #ff0000              ->', isColor('#ff0000'))
console.log('  rebeccapurple        ->', isColor('rebeccapurple'))
console.log('  rgb(255 128 0)       ->', isColor('rgb(255 128 0)'))
console.log('  notacolor            ->', isColor('notacolor'))
console.log('  #gggggg              ->', isColor('#gggggg'))

// --- Completions are MINED from the same predicate that validates ---
// Not a separate hand-maintained list, so the two cannot drift apart.
console.log('\nautocomplete from the same source:')
const forRe = suggestColor('re')
console.log('  "re" ->', forRe.map((s) => s.value).join(', '))

// Every suggestion is run through the compiled predicate before being offered,
// so an invalid completion is not something this API can produce.
const allValid = forRe.every((s) => s.kind !== 'value' || isColor(s.value))
console.log('  all suggestions valid ->', allValid)

test 'the predicate accepts every CSS colour form' {
  expect(isColor('#ff0000')).toBe(true)
  expect(isColor('rebeccapurple')).toBe(true)
  expect(isColor('rgb(255 128 0)')).toBe(true)
}

test 'and rejects near-misses' {
  expect(isColor('notacolor')).toBe(false)
  expect(isColor('#gggggg')).toBe(false)
}

test 'completions come from the predicate, and are all valid' {
  const suggestions = suggestColor('re')
  expect(suggestions.length > 0).toBe(true)
  // The guarantee: mined values are run through the compiled predicate.
  expect(suggestions.every((s) => s.kind !== 'value' || isColor(s.value))).toBe(
    true
  )
}

test 'suggestions are relevant to the prefix' {
  const values = suggestColor('re').map((s) => s.value)
  expect(values).toContain('red')
}