Fractional indexing — generate ordered keys that admit insertion between
any two existing positions without rebalancing.
Used by cell_item.position to give parent → child membership a stable,
lex-sortable order. Two clients editing the same parent each compute keys
locally; the server applies them with (parent_id, position) as the
primary key. Concurrent inserts that hit the same key surface as a
cell_item_position_taken error; helper-side jitter (a small random
suffix on every generated key) keeps the collision probability negligible
at realistic UX concurrency.
Alphabet: base62 (0-9, A-Z, a-z). '0' is the least
digit; 'z' the greatest. Keys are read as base62 fractions: lex order
over keys equals numeric order over fractions.
Invariants emitted by this generator:
- No emitted key ends in
'0'. Without this rule, key + '0' would
lex-equal key for ordering purposes, losing the room-to-insert that
the trailing-'0' extension would otherwise provide. - Every emitted key is non-empty and matches FRACTIONAL_INDEX_REGEX.
- Every emitted key is
≤ FRACTIONAL_INDEX_LENGTH_MAX. The cap is checked
on both inputs and output: bounds within a few digits of the cap can
push a generated key over it, and the generator throws in that case
rather than emitting an oversized key the wire would reject. In
realistic use keys stay far under 100 chars, so this never fires.
Wire vs. emitted invariants: FRACTIONAL_INDEX_REGEX enforces the
alphabet + non-empty contract for any client-supplied position. The
no-trailing-'0' rule is stricter and lives only in this generator;
the wire admits the looser form so a future encoder can sit alongside
this one without a wire-schema bump.
MVP scope — unsupported bracket shapes:
- Unbounded prepend below
'0…': a long sequence of front-inserts
eventually requires a key lex-less than every '0'-only key. - Brackets shaped
(a, a + '0…0') for any non-empty zero tail: the
no-trailing-'0' invariant means no valid key fits the gap.
Both surface as explicit throws. Realistic UX bounds keep emitted
keys well under 100 chars after thousands of consecutive front- or
back-inserts; consumers that genuinely need the prepend extension
should request a negative-prefix scheme.