{"record":{"id":"086d6af4ae68289b","repo":"nexu-io/open-design","slug":"field-must-be-a-dot-separated-json-path","errorCode":null,"errorMessage":"${field} must be a dot-separated JSON path","messagePattern":"(.+?) must be a dot-separated JSON path","errorType":"validation","errorClass":null,"httpStatus":null,"severity":"error","filePath":"apps/daemon/src/live-artifacts/refresh.ts","lineNumber":282,"sourceCode":"    || value === 'public_github_repository_metric';\n}\n\nfunction asBoundedRefreshOutput(value: BoundedJsonObject): BoundedJsonObject {\n  const result = validateBoundedJsonObject(value, 'localRefreshOutput');\n  if (!result.ok) {\n    const firstIssue = result.issues[0];\n    throw new Error(firstIssue === undefined ? result.error : `${firstIssue.path}: ${firstIssue.message}`);\n  }\n  return result.value;\n}\n\nconst SAFE_MAPPING_SEGMENT = /^[A-Za-z_][A-Za-z0-9_-]*$|^(?:0|[1-9][0-9]*)$/;\nconst UNSAFE_MAPPING_SEGMENTS = new Set(['__proto__', 'prototype', 'constructor']);\n\nfunction parseMappingPath(path: string, field: string): string[] {\n  const normalized = path.startsWith('$.') ? path.slice(2) : path;\n  if (normalized.length === 0 || normalized.startsWith('.') || normalized.endsWith('.') || normalized.includes('..')) {\n    throw new Error(`${field} must be a dot-separated JSON path`);\n  }\n  const segments = normalized.split('.');\n  for (const segment of segments) {\n    if (!SAFE_MAPPING_SEGMENT.test(segment) || UNSAFE_MAPPING_SEGMENTS.has(segment)) {\n      throw new Error(`${field} contains unsupported JSON path segment: ${segment}`);\n    }\n  }\n  return segments;\n}\n\nfunction isJsonObject(value: BoundedJsonValue | undefined): value is BoundedJsonObject {\n  return value !== null && typeof value === 'object' && !Array.isArray(value);\n}\n\nfunction readMappedValue(root: BoundedJsonObject, path: string): BoundedJsonValue | undefined {\n  let current: BoundedJsonValue | undefined = root;\n  for (const segment of parseMappingPath(path, 'outputMapping.dataPaths.from')) {\n    if (Array.isArray(current)) {","sourceCodeStart":264,"sourceCodeEnd":300,"githubUrl":"https://github.com/nexu-io/open-design/blob/5be4028344c2eb4c667c5a97bda8f750c5597ef7/apps/daemon/src/live-artifacts/refresh.ts#L264-L300","documentation":"parseMappingPath (refresh.ts:279-283) normalizes an outputMapping dataPath (stripping an optional '$.' prefix) and requires it to be a clean dot-separated path: non-empty, not starting or ending with '.', and containing no '..'. This is used for both dataPaths.from (read) and dataPaths.to (write).","triggerScenarios":"An outputMapping.dataPaths entry has a `from` or `to` value that is '', '.', '.foo', 'foo.', or 'foo..bar'.","commonSituations":"Hand-edited or programmatically-built outputMapping that leaves an empty path, includes a trailing dot, or joins segments with a missing middle (producing '..'); copying a JSONPath like '$.foo.' verbatim.","solutions":["Fix the dataPath to be a clean dot-separated path with no leading/trailing/double dots, e.g. 'items.0.name'.","Strip empty segments when building paths dynamically.","Validate dataPaths with a regex/unit test before persisting the source."],"exampleFix":"// before\noutputMapping: { dataPaths: [{ from: 'items.', to: '.result' }] }\n// throws `outputMapping.dataPaths.from must be a dot-separated JSON path`\n\n// after\noutputMapping: { dataPaths: [{ from: 'items', to: 'result' }] }","handlingStrategy":"validation","validationCode":"function isValidDataPath(p: string): boolean {\n  const norm = p.startsWith('$.') ? p.slice(2) : p;\n  return norm.length > 0 && !norm.startsWith('.') && !norm.endsWith('.') && !norm.includes('..');\n}\nfor (const dp of source.outputMapping?.dataPaths ?? []) {\n  if (!isValidDataPath(dp.from) || !isValidDataPath(dp.to)) throw new Error('invalid dataPath');\n}","typeGuard":"function isDotSeparatedPath(p: unknown): p is string {\n  if (typeof p !== 'string') return false;\n  const n = p.startsWith('$.') ? p.slice(2) : p;\n  return n.length > 0 && !n.startsWith('.') && !n.endsWith('.') && !n.includes('..');\n}","tryCatchPattern":null,"preventionTips":["Build dataPaths from typed segment arrays joined with '.', never hand-concatenated strings.","Reject empty/leading/trailing-dot paths at the input boundary.","Unit-test outputMapping paths against parseMappingPath before persisting the source."],"tags":["live-artifacts","output-mapping","json-path","validation"],"backgroundTag":null,"analyzedSha":"5be4028344c2eb4c667c5a97bda8f750c5597ef7","analyzedAt":"2026-08-12T12:03:58.812Z","schemaVersion":2},"datasetVersion":"2026-08-12T18:17:37.767Z"}