{"record":{"id":"4608199c87afc178","repo":"jackwener/OpenCLI","slug":"packagist-package-value-must-be-vendor-pa","errorCode":null,"errorMessage":"packagist package \"${value}\" must be \"<vendor>/<package>\"","messagePattern":"packagist package \"(.+?)\" must be \"<vendor>/<package>\"","errorType":"validation","errorClass":"ArgumentError","httpStatus":null,"severity":"error","filePath":"clis/packagist/utils.js","lineNumber":38,"sourceCode":"export function requireBoundedInt(value, defaultValue, maxValue, label = 'limit') {\n    const raw = value ?? defaultValue;\n    const n = typeof raw === 'number' ? raw : Number(raw);\n    if (!Number.isInteger(n) || n <= 0) {\n        throw new ArgumentError(`packagist ${label} must be a positive integer`);\n    }\n    if (n > maxValue) {\n        throw new ArgumentError(`packagist ${label} must be <= ${maxValue}`);\n    }\n    return n;\n}\n\nexport function requirePackageName(value) {\n    const raw = String(value ?? '').trim().toLowerCase();\n    if (!raw) {\n        throw new ArgumentError('packagist package name is required (e.g. \"symfony/console\", \"monolog/monolog\")');\n    }\n    const slash = raw.indexOf('/');\n    if (slash <= 0 || slash === raw.length - 1) {\n        throw new ArgumentError(\n            `packagist package \"${value}\" must be \"<vendor>/<package>\"`,\n            'Both segments are required (Composer convention).',\n        );\n    }\n    const vendor = raw.slice(0, slash);\n    const pkg = raw.slice(slash + 1);\n    if (vendor.length > 100 || pkg.length > 100 || !SEGMENT.test(vendor) || !SEGMENT.test(pkg)) {\n        throw new ArgumentError(\n            `packagist package \"${value}\" is not a valid Composer name`,\n            'Use lowercase letters / digits / \"_-.\", segments separated by single \"_-.\" chars (max 100 chars each).',\n        );\n    }\n    return { vendor, package: pkg, full: `${vendor}/${pkg}` };\n}\n\nexport async function packagistFetch(url, label) {\n    let resp;","sourceCodeStart":20,"sourceCodeEnd":56,"githubUrl":"https://github.com/jackwener/OpenCLI/blob/49907e53dc3ade5c223ff0c4c2c2785687cec4e6/clis/packagist/utils.js#L20-L56","documentation":"requirePackageName throws this ArgumentError when the input is non-empty but not shaped like a Composer name: there is no '/', or the '/' is the first or last character (slash <= 0 || slash === raw.length - 1). Both `<vendor>` and `<package>` segments are required by Composer convention. The offending value is echoed in the message.","triggerScenarios":"Calling full with 'symfony' (no slash), '/console' (leading slash), 'symfony/' (trailing slash), or 'a/b/c' still passes this check (first slash used) — the failing cases are missing or edge-position slashes.","commonSituations":"User types just the package name 'monolog' out of npm habit; copying a URL fragment like '/packages/monolog/monolog' leaves stray slashes; autocomplete produced a trailing slash.","solutions":["Use the full vendor/package form, e.g. \"monolog/monolog\" instead of \"monolog\".","Strip surrounding slashes from copied URL fragments before calling.","If you only have a package name, resolve the vendor first (e.g. via search) then call full.","Catch ArgumentError and hint the user with the `<vendor>/<package>` format."],"exampleFix":"// before\nawait full('symfony'); // no vendor segment\n\n// after\nawait full('symfony/console');","handlingStrategy":"validation","validationCode":"function isVendorPackage(v) {\n  if (typeof v !== 'string') return false;\n  const s = v.trim().toLowerCase();\n  const i = s.indexOf('/');\n  return i > 0 && i < s.length - 1;\n}\nif (!isVendorPackage(name)) throw new Error(`'${name}' must be <vendor>/<package>`);","typeGuard":"const hasBothSegments = (v) => {\n  const s = String(v ?? '').trim();\n  const i = s.indexOf('/');\n  return i > 0 && i < s.length - 1;\n};","tryCatchPattern":"try {\n  await full(name);\n} catch (e) {\n  if (e instanceof ArgumentError && /<vendor>\\/<package>/.test(e.message)) {\n    console.error(`'${name}' is not <vendor>/<package>. Try 'packagist search ${name}'.`);\n    process.exitCode = 2;\n  } else throw e;\n}","preventionTips":["Always pass both vendor and package — npm-style bare names are rejected.","Strip URL fragments: extract vendor/package from pasted packagist.org URLs.","Run search first when you only know part of the name.","Trim trailing slashes from tab-completed or pasted input."],"tags":["argument-validation","composer","naming","format","packagist"],"backgroundTag":"invalid-package-name-format","analyzedSha":"49907e53dc3ade5c223ff0c4c2c2785687cec4e6","analyzedAt":"2026-08-29T08:14:47.543Z","schemaVersion":2},"datasetVersion":"2026-08-29T17:17:51.833Z"}