{"record":{"id":"7d531e1dfc7af753","repo":"jackwener/OpenCLI","slug":"invalid-xiaoe-url-raw","errorCode":null,"errorMessage":"invalid xiaoe URL: ${raw}","messagePattern":"invalid xiaoe URL: (.+?)","errorType":"validation","errorClass":"ArgumentError","httpStatus":null,"severity":"error","filePath":"clis/xiaoe/content.js","lineNumber":89,"sourceCode":"        const src = imgs[i].getAttribute('src') || imgs[i].src || '';\n        if (!src) continue;\n        if (src.startsWith('data:')) continue;\n        if (!src.includes('xiaoe')) continue;\n        count += 1;\n    }\n    return count;\n}\n\nexport function requireXiaoePageUrl(value, commandName) {\n    const raw = typeof value === 'string' ? value.trim() : '';\n    if (!raw) {\n        throw new ArgumentError('url is required (positional)');\n    }\n    let parsed;\n    try {\n        parsed = new URL(raw);\n    } catch {\n        throw new ArgumentError(\n            `invalid xiaoe URL: ${raw}`,\n            `Example: opencli xiaoe ${commandName} https://appxxxx.h5.xet.citv.cn/p/course/ecourse/v_xxxxx`,\n        );\n    }\n    if (parsed.protocol !== 'https:') {\n        throw new ArgumentError(\n            `xiaoe URL must use https (got ${parsed.protocol.replace(':', '')})`,\n            `Example: opencli xiaoe ${commandName} https://appxxxx.h5.xet.citv.cn/p/course/ecourse/v_xxxxx`,\n        );\n    }\n    const host = parsed.hostname.toLowerCase();\n    if (host !== 'h5.xet.citv.cn' && !host.endsWith('.h5.xet.citv.cn')) {\n        throw new ArgumentError(\n            `url must be on h5.xet.citv.cn or a shop subdomain (got ${parsed.hostname})`,\n            `Example: opencli xiaoe ${commandName} https://appxxxx.h5.xet.citv.cn/p/course/ecourse/v_xxxxx`,\n        );\n    }\n    return parsed.toString();","sourceCodeStart":71,"sourceCodeEnd":107,"githubUrl":"https://github.com/jackwener/OpenCLI/blob/49907e53dc3ade5c223ff0c4c2c2785687cec4e6/clis/xiaoe/content.js#L71-L107","documentation":"requireXiaoePageUrl attempts new URL(raw) on the supplied value; when the string is not a parseable absolute URL it throws ArgumentError('invalid xiaoe URL: <raw>') with an example of the expected format.","triggerScenarios":"Passing a relative path ('/p/course/...'), a bare host ('appxxxx.h5.xet.citv.cn'), a malformed URL, or a value with stray whitespace/characters so the URL constructor throws.","commonSituations":"Copy-paste truncated the 'https://' prefix; shell mangled query strings (?, &) that weren't quoted; pasted a URL with surrounding text or trailing punctuation; passed an app-specific deep link that isn't an HTTP URL.","solutions":["Include the full scheme: https://appxxxx.h5.xet.citv.cn/p/course/ecourse/v_xxxxx.","Quote the URL in the shell so ?, &, and # are not interpreted.","Trim whitespace and remove any surrounding text before pasting.","Validate in JS beforehand with new URL(value) inside try/catch."],"exampleFix":"// before\nopencli xiaoe content appxxxx.h5.xet.citv.cn/p/course/ecourse/v_xxxxx\n// after\nopencli xiaoe content 'https://appxxxx.h5.xet.citv.cn/p/course/ecourse/v_xxxxx'","handlingStrategy":"validation","validationCode":"function assertHttpsUrl(raw) {\n  let u;\n  try { u = new URL(String(raw).trim()); } catch { throw new Error(`invalid xiaoe URL: ${raw}`); }\n  if (u.protocol !== 'https:') throw new Error('must use https');\n  return u;\n}","typeGuard":"const isAbsoluteHttpUrl = v => { try { const u = new URL(v); return u.protocol === 'https:' || u.protocol === 'http:'; } catch { return false; } };","tryCatchPattern":"try {\n  await contentCommand(url);\n} catch (e) {\n  if (e instanceof ArgumentError && /invalid xiaoe URL/.test(e.message)) {\n    console.error('Include the full https:// scheme and quote the URL.');\n    process.exit(2);\n  } else throw e;\n}","preventionTips":["Always copy the complete URL including https://.","Quote URLs containing ?, &, # in the shell.","Pre-validate with new URL() in scripts that build the URL programmatically."],"tags":["argument-validation","url-parsing","cli","xiaoe"],"backgroundTag":"invalid-url-format","analyzedSha":"49907e53dc3ade5c223ff0c4c2c2785687cec4e6","analyzedAt":"2026-08-29T08:14:47.543Z","schemaVersion":2},"datasetVersion":"2026-08-29T12:17:43.993Z"}