import { RuntimeAdapter } from "../../runtime/adapter.mjs"; //#region src/core/config/package-json.d.ts /** * Object form of the package.json `repository` field * (e.g. `{"type":"git","url":"git+https://github.com/u/r.git"}`). */ interface PackageRepository { /** Version control system type (usually `'git'`). */ readonly type?: string; /** Repository URL or locator. */ readonly url?: string; /** Subdirectory within a monorepo where the package lives. */ readonly directory?: string; } /** * Subset of package.json fields relevant to CLI metadata. * * All fields are optional — a valid package.json may omit any of them. */ interface PackageJsonData { /** Package name (e.g. `@scope/mycli`). */ readonly name?: string; /** Semver version string from `package.json`. */ readonly version?: string; /** One-line package description. */ readonly description?: string; /** Binary entry point(s) — string for single-bin, object for multi-bin. */ readonly bin?: string | Readonly>; /** Project homepage URL. */ readonly homepage?: string; /** Repository locator — URL/shorthand string or `{type, url, directory}` object. */ readonly repository?: string | PackageRepository; } /** * The subset of {@link RuntimeAdapter} needed for package.json discovery. * * Using a narrow pick keeps the function easy to test and makes the * dependency explicit. */ type PackageJsonAdapter = Pick; /** Options for {@link discoverManifest}. */ interface ManifestDiscoveryOptions { /** * Explicit directory or file path to walk up from. Defaults to * `adapter.cwd` when omitted. Pass an absolute path inside your own package * (e.g. `fileURLToPath(import.meta.url)`) for installable CLIs whose version * should reflect the CLI's own package, not the consumer's working directory. * * This low-level helper takes a resolved path **string** only. The builder * sugar `CLIBuilder.manifest({ from })` exposes the same anchor under the name * `from` and additionally accepts a `file:` URL string or `URL` instance, * normalizing it to a path first. */ readonly startDir?: string; /** * Candidate manifest filenames, in per-directory priority order * (e.g. `['deno.json', 'jsr.json']`). At each directory the first existing * file that carries CLI metadata wins, so the nearest manifest directory * always takes precedence over file order. A file that parses but holds no * recognised metadata (e.g. a config-only `deno.json` with just * `tasks`/`imports`) is skipped, so a sibling `jsr.json` — or a manifest * higher up — can still be found. Defaults to `['package.json']`. * * An empty list is a deliberate no-op: with no candidates to probe, the * walk-up reads nothing and resolves to `null` (consistent with the * "returns `null` when no manifest is found" contract). */ readonly files?: readonly string[]; } /** * Discover the nearest manifest (`package.json`, `deno.json`, `jsr.json`, …) * by walking up from `startDir` (or `adapter.cwd` when omitted). * * Convenience helper behind `CLIBuilder.manifest()`. Most apps should let the * CLI runtime discover metadata automatically; call this directly when testing * metadata inference or embedding the behavior in custom tooling. * * Candidate files are parsed as JSON with a JSONC fallback — `package.json`, * `deno.json`, `jsr.json`, and `deno.jsonc` all qualify, including files that * carry `//` / block comments or trailing commas (common in `deno.json`). A * file that fails both parses is skipped, so discovery keeps probing. * * Returns the parsed metadata on success, `null` when no manifest is found * (not an error). Malformed JSON, non-object roots, and config-only manifests * that carry no recognised metadata (e.g. a `deno.json` with only `tasks` / * `imports`) are all skipped, so discovery keeps probing the remaining * candidate files and parent directories — the feature is a convenience, not a * hard requirement. * * @param adapter - Adapter providing `readFile` + `cwd`. * @param options - Optional anchor (`startDir`) and candidate filenames (`files`). * * @example * ```ts * import { discoverManifest } from '@kjanat/dreamcli'; * * const meta = await discoverManifest(adapter, { files: ['deno.json', 'jsr.json'] }); * if (meta !== null) { * console.log(meta.version); // '1.2.3' * } * ``` */ declare function discoverManifest(adapter: PackageJsonAdapter, options?: ManifestDiscoveryOptions): Promise; /** * Discover the nearest `package.json` by walking up from `startDir` (or * `adapter.cwd` when omitted). * * @deprecated Use {@link discoverManifest} with `{ files: ['package.json'] }` * (the default), which also supports `deno.json` / `jsr.json`. * * Behavior note (changed by the manifest generalization): a parseable but * metadata-less `package.json` — `{}`, or one carrying only non-metadata fields * such as `dependencies` / `scripts` / `type` — is no longer treated as a * discovery hit. Previously such a file halted the walk-up and resolved to `{}`; * now it is skipped and the walk-up CONTINUES to parent directories. In a * monorepo this means a metadata-less leaf `package.json` no longer shadows an * ancestor manifest, so an ancestor's `version` can surface where the old * behavior returned `{}`. Pass pre-loaded `data` (or use {@link discoverManifest} * with an explicit `startDir`) when you need to pin discovery to one directory. * * @param adapter - Adapter providing `readFile` + `cwd`. * @param startDir - Optional explicit directory or file path to walk up from. * * @example * ```ts * import { discoverPackageJson } from '@kjanat/dreamcli'; * * const pkg = await discoverPackageJson(adapter); * if (pkg !== null) { * console.log(pkg.version); // '1.2.3' * } * ``` */ declare function discoverPackageJson(adapter: PackageJsonAdapter, startDir?: string): Promise; /** Options for {@link packageRepositoryUrl}. */ interface PackageRepositoryUrlOptions { /** * Throw a `CLIError` (code `INVALID_REPOSITORY`) instead of returning * `undefined` when the `repository` field is absent or not a recognisable * locator. With `require: true` the return type narrows to `string`, so * callers that know their manifest carries a valid repository need no * assertion — and a bad manifest fails fast with a real message instead * of propagating `undefined`. * * @defaultValue `false` */ readonly require?: boolean; } /** * Resolve a package's `repository` field to a browsable `https://` URL. * * Handles the locator formats npm accepts: * - object form: `{ "type": "git", "url": "git+https://github.com/u/r.git" }` * - `https`/`git`/`ssh` URLs (`git+` prefix and `.git` suffix stripped) * - scp-style locators: `git@github.com:u/r.git` * - shorthands: `github:u/r`, `gitlab:u/r`, `bitbucket:u/r`, and bare `u/r` * (GitHub, per npm convention) * * Returns `undefined` when the field is absent or unrecognised — or, with * `{ require: true }`, throws instead and the return type is `string`. * (The narrowing cannot key off the input type alone: a present `repository` * may still be an empty string, a url-less object, or an unparseable * locator, so the `string` promise is backed by the runtime check.) * * @example * ```ts * packageRepositoryUrl({ repository: 'git+https://github.com/u/r.git' }); * // 'https://github.com/u/r' * * const url: string = packageRepositoryUrl(pkg, { require: true }); * // throws CLIError when pkg.repository is missing or unrecognised * ``` */ declare function packageRepositoryUrl(pkg: PackageJsonData, options: PackageRepositoryUrlOptions & { readonly require: true; }): string; /** Resolve a package repository URL, returning `undefined` for absent or invalid locators. */ declare function packageRepositoryUrl(pkg: PackageJsonData, options?: PackageRepositoryUrlOptions): string | undefined; /** * Infer the CLI binary name from manifest data. * * Resolution order: * 1. First key of `bin` object (e.g. `{"mycli": "./dist/cli.js"}` → `"mycli"`) * 2. Package `name`, scope stripped by default (e.g. `"@scope/mycli"` → `"mycli"`); * pass `{ stripScope: false }` to keep the full scoped name * 3. `undefined` if neither exists * * Note: `bin` keys are never scoped, so `stripScope` only affects the `name` * fallback (relevant for `deno.json` / `jsr.json`, which have no `bin` field). * * @param pkg - Parsed manifest metadata. * @param options - `stripScope` (default `true`): strip a leading `@scope/` * from the `name` fallback. * * @example * ```ts * import { inferCliName } from '@kjanat/dreamcli'; * * inferCliName({ bin: { mycli: './dist/cli.js' } }); // 'mycli' * inferCliName({ name: '@scope/mycli' }); // 'mycli' * inferCliName({ name: '@scope/mycli' }, { stripScope: false }); // '@scope/mycli' * inferCliName({ name: 'mycli' }); // 'mycli' * inferCliName({}); // undefined * ``` */ declare function inferCliName(pkg: PackageJsonData, options?: { readonly stripScope?: boolean; }): string | undefined; //#endregion export { type ManifestDiscoveryOptions, type PackageJsonAdapter, type PackageJsonData, type PackageRepository, type PackageRepositoryUrlOptions, discoverManifest, discoverPackageJson, inferCliName, packageRepositoryUrl };