Repository navigation
feat: unified config package with typed tool sections - #2
Conversation
A descriptor whose name type is string (a bare Section annotation) gave the section map a string index signature, which switched off the excess-property check for every key of the config. withSections now rejects such a descriptor, one named after an envelope key, two that share a name, and an empty list, each with a message stating the reason. defineSection rejects an envelope key name in the tool's own build.
cosmiconfig-extends hands back the whole module namespace when a module has no default export, and null for a null default. Both then read as a config that defines nothing, so loadSection returned undefined and doctor reported a clean directory for a configuration that was never applied. The explorer's TypeScript loaders now refuse both; an empty file or a default export of undefined remains an empty config.
loadSection returned undefined for a nonexistent directory or one that is a file, and doctor and the exadev-config command reported success for a mistyped --cwd, so the check could never go red for that input. Both now throw, and the command exits with the failure status.
The standalone file <name>.config.ts is the native config file of many tools, so a section called eslint or vitest read the tool's own config as its section, or failed as defined in both files. The standalone file is now exadev.<name>.config.ts, which no tool owns. The .mts and .cts extensions that cosmiconfig-extends loads were silently ignored. Both file kinds now accept .ts, .mts and .cts, and a file present under more than one extension throws. The defined-in-both error says the unified side may come through extends, since the preset is not known at that point.
…ere is no config The usage text advertises --help for the doctor command, but parseArgs rejected it as an unknown option. Running the command with no arguments printed the usage and exited 0. It now exits with the failure status. A directory with no unified config exited 0 in silence, which made a clean exit weak evidence; the command now says nothing was checked.
… manifest keys doctor ignored the exadevConfig field of the project's own package.json, so a tool repository dogfooding its section saw it reported as unowned. The manifest check also rejected any key besides sections, which would make every consumer on an older version fail as soon as a tool adds one; it now checks sections and ignores the rest.
loadSection throws ConfigValidationError, but the class was reachable only by importing cosmiconfig-extends, which a consumer under strict node_modules cannot do without adding it as a direct dependency.
Exact pins on the published package's runtime dependencies stop a consumer taking a cosmiconfig-extends patch without a new release here, and install a second copy beside any other 1.x version, where ConfigValidationError differs between the copies and instanceof fails. The release age exclusion now names the package rather than one version.
…tory's own The check installed whatever zod was latest at run time, so the job was not reproducible.
The authoring type is the schema's input type, so rules the schema enforces only at runtime pass the type checker, and the excess-property check applies only to an object literal passed directly. The packaged-install sentence now says which check runs the command. The authoring test's option list is described as the subset it is.
…its decisions The coherence messages quoted group names, patterns and pair members from the config, against the loader design that error messages do not echo input. They now name the path and the rule. The layout schema doc and README state the two other deliberate behaviours: a key written as undefined is rejected where the ESLint reader accepts it, and the relational rules wait for a valid shape. The Check contract says a relational guard may report fewer problems.
…erage of the files it loads The alias shim re-exported this package's source, so jiti evaluated a second copy that coverage attributed to the same files, reporting covered functions as uncovered. The shim is a stand-in now, since the test is about where the import resolves. Hooks get the same timeout as tests, because the authoring type test compiles its fixtures in one.
A project package.json whose dependency fields were not objects, and an installed package.json that was not an object, were skipped, so doctor reported a clean result for input it could not read. Both are now validated and named in the error, with every problem listed. The dependency fields of installed packages are still not read.
Descriptors in the section unit tests are built in a hook, because a module level call that throws fails the suite at load with no failing test, which a mutation run counts as a survivor. The layout schema vendor and the manifest field name are asserted. The explorer no longer sets a cache option that a fresh explorer per call made unobservable, and takes its module name from the unified file base, so the two cannot drift. The command resolves the working directory through process.cwd, whose fallback was indistinguishable from resolving an empty path.
The mutation job no longer runs on pull requests or pushes and no longer gates required checks or the release job. The script and its threshold are unchanged for manual runs.
Codex Review SummaryThis comment shows the latest Codex review activity on this pull request.
Security findingsAdvisory findings (1)ℹ️ About Codex in GitHubYour team has set up Codex to review pull requests in this repo. Reviews are triggered when you
Codex reacts with 👀 while any review is running, comments if it has suggestions, and reacts with 👍 once all reviews finish with no findings. |
|
🎉 This PR is included in version 1.0.0 🎉 The release is available on: Your semantic-release bot 📦🚀 |
There was a problem hiding this comment.
🛡️ Codex Security Review · Automatically triggered
Here are some automated security review suggestions for this pull request.
Reviewed commit: 7c57cbef98
ℹ️ About Codex security reviews in GitHub
This is an experimental Codex feature. Security reviews are triggered when:
- You comment "@codex security review"
- A regular code review gets triggered (for example, "@codex review" or when a PR is opened), and you’re opted in so security review runs alongside code review
Once complete, Codex will leave suggestions, or a comment if no findings are found.
| commitlint: | ||
| name: Commitlint | ||
| needs: determine-runner | ||
| runs-on: ${{ fromJson(needs.determine-runner.outputs.selected-runner) }} |
There was a problem hiding this comment.
🛡️ Codex Security Review · Automatically triggered
Security: Keep pull requests off the self-hosted runner fleet
When fork PR workflows are approved or auto-run and the shared fleet is available, keep every pull-request job on a hosted runner. pull_request evaluates the PR-controlled workflow revision, so an attacker can change the repository-local determine-runner.yml or this runs-on expression to select the self-hosted fleet; the fork-aware action is mutable before scheduling. CI then checks out the PR and executes pnpm install and repository scripts, yielding arbitrary code execution on that host. The read-only token and hosted release job limit direct publication but do not protect the runner. Enforce hosted runners for PRs or an immutable gate plus runner-group restrictions.
Useful? React with 👍 / 👎.
Builds
@exadev/configoncosmiconfig-extends: tool-owned typed sections composed in an authoring file withwithSections(toolA, toolB)({ ... }),loadSectionresolving a section fromexadev.config.tsor a standalone<name>.config.ts(defined in both is an error naming both files), the sharedlayoutsection matching the options of the workspace architecture rules in@exadev/eslint-config, and adoctorfunction with anexadev-config doctorcommand for sections no installed or listed tool owns.Refs #1