{"record":{"id":"e87de899f58d7904","repo":"toeverything/AFFiNE","slug":"a-should-be-smaller-than-b","errorCode":null,"errorMessage":"a should be smaller than b","messagePattern":"a should be smaller than b","errorType":"exception","errorClass":"Error","httpStatus":null,"severity":"error","filePath":"blocksuite/framework/std/src/utils/fractional-indexing.ts","lineNumber":28,"sourceCode":" * make sure a and b are generated by this function.\n *\n * @param customPostfix custom postfix for the key, only letters and numbers are allowed\n */\nexport function generateKeyBetweenV2(a: string | null, b: string | null) {\n  const randomSize = 32;\n  function postfix(length: number = randomSize) {\n    const chars =\n      '123456789ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz';\n    const values = new Uint8Array(length);\n    crypto.getRandomValues(values);\n    let result = '';\n    for (let i = 0; i < length; i++) {\n      result += chars.charAt(values[i] % chars.length);\n    }\n    return result;\n  }\n  if (a !== null && b !== null && a >= b) {\n    throw new Error('a should be smaller than b');\n  }\n  // get the subkey in full key\n  // e.g.\n  // a0xxxx -> a\n  // a0x0xxxx -> a0x\n  function subkey(key: string | null) {\n    if (key === null) {\n      return null;\n    }\n    if (key.length <= randomSize + 1) {\n      // no subkey\n      return key;\n    }\n    const splitAt = key.substring(0, key.length - randomSize - 1);\n    return splitAt;\n  }\n  const aSubkey = subkey(a);\n  const bSubkey = subkey(b);","sourceCodeStart":10,"sourceCodeEnd":46,"githubUrl":"https://github.com/toeverything/AFFiNE/blob/b4c8548c09da21b2898443559a5b846f0ccf5dd8/blocksuite/framework/std/src/utils/fractional-indexing.ts#L10-L46","documentation":"generateKeyBetweenV2() in blocksuite/framework/std/src/utils/fractional-indexing.ts creates a sort key strictly between a and b (used for ordering edgeless/surface blocks). It validates that when both neighbors are given, a >= b must fail-fast — fractional indexing only defines a key for an open interval (a, b). A plain Error (not BlockSuiteError) is thrown because this is a caller contract violation.","triggerScenarios":"Calling generateKeyBetweenV2(a, b) with a >= b: passing (nextKey, prevKey) in the wrong order when inserting/moving an element, passing the same key for both arguments, or feeding legacy/hand-built keys that don't compare as expected lexicographically.","commonSituations":"Implementing drag-to-reorder in edgeless mode and swapping the prev/next key arguments; duplicating a block and reusing its key for both bounds; mixed key formats (some keys from the older generateNKeysBetween or hand-written) where lexicographic order surprises the caller.","solutions":["Always call it as generateKeyBetweenV2(prevKey, nextKey) with prevKey strictly less than nextKey; use null for the open ends.","Guard before calling: if (a !== null && b !== null && a >= b) throw/swap or fall back to inserting at an end.","When keys come from stored data, re-validate ordering (sort neighbors before picking bounds).","Never mix keys produced by different generators in the same index space."],"exampleFix":"// before\nconst key = generateKeyBetweenV2(nextKey, prevKey);\n\n// after\nconst key = generateKeyBetweenV2(prevKey, nextKey);\n// or guard:\nconst key =\n  prevKey && nextKey && prevKey >= nextKey\n    ? generateKeyBetweenV2(null, nextKey)\n    : generateKeyBetweenV2(prevKey, nextKey);","handlingStrategy":"validation","validationCode":"function safeGenerate(a: string | null, b: string | null): string {\n  if (a !== null && b !== null && a >= b) {\n    // caller mixed up order or keys are equal — insert at an end instead of crashing\n    return generateKeyBetweenV2(null, b);\n  }\n  return generateKeyBetweenV2(a, b);\n}","typeGuard":null,"tryCatchPattern":null,"preventionTips":["Always pass (prevKey, nextKey) in strictly increasing order; null means unbounded.","Never reuse an existing key as both bounds (duplicate keys violate the invariant).","Use only keys produced by generateKeyBetweenV2 in the same index space."],"tags":["blocksuite","fractional-indexing","ordering","edgeless","argument-validation"],"backgroundTag":"fractional-index-order-violation","analyzedSha":"b4c8548c09da21b2898443559a5b846f0ccf5dd8","analyzedAt":"2026-08-18T21:16:52.546Z","contentChangedAt":"2026-08-18T21:16:52.546Z","schemaVersion":2},"datasetVersion":"2026-09-14T05:17:10.506Z"}