{"record":{"id":"053309b020174c28","repo":"tobi/qmd","slug":"handelize-path-cannot-be-empty","errorCode":null,"errorMessage":"handelize: path cannot be empty","messagePattern":"handelize: path cannot be empty","errorType":"validation","errorClass":"Error","httpStatus":null,"severity":"error","filePath":"src/store.ts","lineNumber":2365,"sourceCode":" * - Convert triple underscore `___` to `/` (folder separator)\n * - Replace sequences of non-word chars (except /) with single dash\n * - Remove leading/trailing dashes from path segments\n * - Preserve folder structure (a/b/c/d.md stays structured)\n * - Preserve file extension\n * - Preserve original case (important for case-sensitive filesystems)\n */\n/** Replace emoji/symbol codepoints with their hex representation (e.g. 🐘 → 1f418) */\nfunction emojiToHex(str: string): string {\n  return str.replace(/(?:\\p{So}\\p{Mn}?|\\p{Sk})+/gu, (run) => {\n    // Split the run into individual emoji and convert each to hex, dash-separated\n    return [...run].filter(c => /\\p{So}|\\p{Sk}/u.test(c))\n      .map(c => c.codePointAt(0)!.toString(16)).join('-');\n  });\n}\n\nexport function handelize(path: string): string {\n  if (!path || path.trim() === '') {\n    throw new Error('handelize: path cannot be empty');\n  }\n\n  // Allow route-style \"$\" filenames while still rejecting paths with no usable content.\n  // Emoji (\\p{So}) counts as valid content — they get converted to hex codepoints below.\n  const segments = path.split('/').filter(Boolean);\n  const lastSegment = segments[segments.length - 1] || '';\n  const filenameWithoutExt = lastSegment.replace(/\\.[^.]+$/, '');\n  const hasValidContent = /[\\p{L}\\p{N}\\p{So}\\p{Sk}$]/u.test(filenameWithoutExt);\n  if (!hasValidContent) {\n    throw new Error(`handelize: path \"${path}\" has no valid filename content`);\n  }\n\n  const result = path\n    .replace(/___/g, '/')       // Triple underscore becomes folder separator\n    .split('/')\n    .map((segment, idx, arr) => {\n      const isLastSegment = idx === arr.length - 1;\n","sourceCodeStart":2347,"sourceCodeEnd":2383,"githubUrl":"https://github.com/tobi/qmd/blob/dbfd0b4736aeaf761d1a16ca8e424f071df8feb9/src/store.ts#L2347-L2383","documentation":"handelize() converts a path into a handle (safe filename) and rejects empty or whitespace-only input up front, since there is no filename content to build a handle from.","triggerScenarios":"Calling handelize('') or handelize('   ') (or a variable that trims to empty) — e.g. a computed path whose basename was stripped earlier.","commonSituations":"Passing undefined/null coerced to 'undefined' is fine but empty string from a failed lookup or .replace() is common; default parameters producing '' when config is missing.","solutions":["Check the path is non-empty before calling handelize","Fix the upstream code producing the empty string","Provide a fallback handle name"],"exampleFix":"// before\nconst h = handelize(pathVar); // pathVar === ''\n// after\nconst h = handelize(pathVar || 'untitled');","handlingStrategy":"validation","validationCode":"if (!path || !path.trim()) throw new TypeError('path required');","typeGuard":"const isNonEmptyPath = (p: unknown): p is string => typeof p === 'string' && p.trim().length > 0;","tryCatchPattern":null,"preventionTips":["Guard computed paths before handelize","Provide fallback names for empty lookups","Log empty-path occurrences to find upstream bugs"],"tags":["path","validation","filename"],"backgroundTag":"empty-argument","analyzedSha":"dbfd0b4736aeaf761d1a16ca8e424f071df8feb9","analyzedAt":"2026-08-28T18:07:46.628Z","schemaVersion":2},"datasetVersion":"2026-08-28T21:17:43.275Z"}