Skip to content

fix(api): keep the config and artifact types stable for consumers - #48

Merged
steve-calvert-glean merged 6 commits into
mainfrom
fix/config-contract
Oct 21, 2026
Merged

steve-calvert-glean merged 6 commits into
mainfrom
fix/config-contract

Conversation

@steve-calvert-glean

Copy link
Copy Markdown
Contributor

Problem

The public config and type contract was not safe to keep stable:

  • defineConfig and the config types used the schema's output type. The first field with a .default() would have made that field required, and existing defineConfig({...}) calls would have stopped compiling.
  • dist/index.d.ts referenced zod internals (z.core), so the published types depended on zod's type layout.
  • FileValue used Buffer without importing it. A consumer with types: [] and skipLibCheck: false failed with TS2591.
  • Unknown config keys such as component: or a target named codxe were dropped without a word.
  • files and managedPaths in an Artifact used different sort orders.
  • ResolvedProject exposed internal source state with no stability note.
  • No test pinned the runtime export list or compiled every exported type.

Change

  • Authoring types use the schema input (PluginpackConfig). The parsed config is a new ResolvedPluginpackConfig. Both still derive from the zod schemas, and REVIEW.md says so.
  • A build step (scripts/materialize-config-types.mjs) asks the TypeScript checker for the evaluated schema types and writes them into dist/index.d.ts as plain multi-line types. It replaces only aliases whose right-hand side is a schema-derived type, fails on any name collision, and typechecks the result with types: [] and skipLibCheck: false.
  • FileValue is string | Uint8Array. Runtime values are still Buffers, and the README explains how to decode them.
  • Unknown config and manifest keys print one warning per field, with the file and dotted path. Parsing stays non-strict, and manifest and entry passthroughs are not reported.
  • Both artifact fields use code-unit ordering.
  • ResolvedProject's internal fields and SourceProvider are marked @unstable.
  • Tests pin the runtime exports for src and dist, and compile a consumer that imports every exported type, both without zod and with zod 3 hoisted.

Breaking changes

A TypeScript consumer that calls .toString("utf8") on an artifact file value now needs Buffer.from(value).toString("utf8").

Testing

npm run check passes. Tests cover a virtual schema that gains a default, an alias name collision, and an unresolved $brand type.

Keep authoring config types schema-input-derived and resolved config schema-output-derived. Materialize their declarations with the TypeScript checker so consumers do not inherit Zod internals or ambient Node types.

Warn about stripped config keys, mark exposed source state unstable, and give artifact files and managed paths one code-unit ordering. Pin runtime exports and compile all public types in isolated consumers, including hoisted Zod 3 definitions.

The Uint8Array file-value change needs the matching explicit UTF-8 conversions in the companion partials change. That owned file is deliberately left for its implementer.
FileValue is now string | Uint8Array, and Uint8Array.toString() joins byte values with commas instead of decoding them. Decode through Buffer.from so a Uint8Array value is read as UTF-8 text.
Match schema-derived alias expressions before replacing declarations, so a public type with the same name cannot be silently overwritten. Reuse existing structural aliases and emit multiline literals for readable output.

Typecheck the publishable declaration with no ambient types and full library checking. This catches unqualified Zod helpers and missing dependencies during the build.
Track diagnostics by file and field instead of loader identity, so prune and diff do not warn again when they reload a project. Keep new keys visible without relying on the config module cache.

Use manifest wording for legacy and canonical source manifests and MCP shipping manifests. Keep intentional record passthroughs silent.
Document string or Uint8Array artifact values and explicit UTF-8 decoding, including Node buffers. State which authored manifests receive stripped-key warnings and that repeated loads do not duplicate them.
@steve-calvert-glean steve-calvert-glean added the breaking Considered a breaking change label Oct 21, 2026
# ------------------------ >8 ------------------------
# Do not modify or remove the line above.
# Everything below it will be ignored.
#
# Conflicts:
#	src/config.ts
@steve-calvert-glean
steve-calvert-glean marked this pull request as ready for review October 21, 2026 23:59
@steve-calvert-glean
steve-calvert-glean merged commit 6858bf1 into main Oct 21, 2026
@steve-calvert-glean
steve-calvert-glean deleted the fix/config-contract branch October 21, 2026 23:59
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

breaking Considered a breaking change

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant