@feniix/bridgekit 0.8.0 → 0.8.1

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/CHANGELOG.md CHANGED
@@ -4,7 +4,19 @@ All notable changes to `@feniix/bridgekit` are documented here. The format
4
4
  follows [Keep a Changelog](https://keepachangelog.com/en/1.1.0/) and the project
5
5
  adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0.html).
6
6
 
7
- ## [0.8.0] - Unreleased
7
+ ## [0.8.1] - Unreleased
8
+
9
+ ### Fixed
10
+
11
+ - Union-of-objects validation no longer surfaces phantom missing-required-
12
+ property entries for every branch. When TypeBox emits an `anyOf`/`oneOf`
13
+ error at a path, sibling `required`/`additionalProperties` entries at the
14
+ same path are now suppressed so consumers see only the union summary.
15
+ `const`/`enum` discriminator errors at deeper paths are kept (they're
16
+ signal about which branch was intended).
17
+ Resolves [#35](https://github.com/feniix/bridgekit/issues/35).
18
+
19
+ ## [0.8.0] - 2026-05-26
8
20
 
9
21
  ### Breaking
10
22
 
@@ -1,5 +1,32 @@
1
1
  import { Check, Errors } from "typebox/value";
2
2
  const ROOT_FIELD = "(root)";
3
+ function suppressSiblingErrorsUnderUnion(errors) {
4
+ // For unions of objects, TypeBox emits per-branch `required` /
5
+ // `additionalProperties` errors at the union's path. The user only needs
6
+ // to satisfy ONE branch, so those entries are phantoms; the `anyOf` /
7
+ // `oneOf` summary at the same path is the real signal.
8
+ //
9
+ // `const` / `enum` errors at the same path survive — they carry real
10
+ // discriminator info about which branch was intended.
11
+ //
12
+ // Known trade-off (#38): discriminated unions where the user picks a
13
+ // branch correctly but forgets that branch's required props also lose
14
+ // the "you picked tag=X and forgot Y" hint. Keyword-aware branch
15
+ // matching is the follow-up.
16
+ const unionPaths = new Set();
17
+ for (const error of errors) {
18
+ if (error.keyword === "anyOf" || error.keyword === "oneOf") {
19
+ unionPaths.add(error.instancePath);
20
+ }
21
+ }
22
+ if (unionPaths.size === 0)
23
+ return errors;
24
+ return errors.filter((error) => {
25
+ if (!unionPaths.has(error.instancePath))
26
+ return true;
27
+ return error.keyword !== "required" && error.keyword !== "additionalProperties";
28
+ });
29
+ }
3
30
  function expandTypeBoxError(error) {
4
31
  // Read offending property names from TypeBox's structured `params` rather
5
32
  // than parsing the message string. Two payoffs: locale independence (the
@@ -32,11 +59,15 @@ export function validatePortableToolArgs(tool, args) {
32
59
  return { ok: true };
33
60
  }
34
61
  // TypeBox can emit multiple errors per offending field (e.g. union/anyOf
35
- // mismatches fire one error per failed branch). Dedupe by (field, message)
36
- // using JSON.stringify so the key can't collide regardless of what
37
- // characters field or message contain.
62
+ // mismatches fire one error per failed branch). First, when an anyOf/oneOf
63
+ // error fires at a path, drop sibling `required`/`additionalProperties`
64
+ // errors at the same path — they're phantoms (the consumer only needs to
65
+ // satisfy ONE branch, not all of them). Then dedupe the survivors by
66
+ // (field, message) using JSON.stringify so the key can't collide regardless
67
+ // of what characters field or message contain.
68
+ const rawErrors = suppressSiblingErrorsUnderUnion([...Errors(tool.parameters, args)]);
38
69
  const seen = new Set();
39
- const errors = [...Errors(tool.parameters, args)]
70
+ const errors = rawErrors
40
71
  .flatMap((error) => expandTypeBoxError(error))
41
72
  .filter(({ field, message }) => {
42
73
  const key = JSON.stringify([field, message]);
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@feniix/bridgekit",
3
- "version": "0.8.0",
3
+ "version": "0.8.1",
4
4
  "description": "BridgeKit defines TypeBox-backed tools once and adapts them to pi, MCP, and other hosts.",
5
5
  "keywords": [
6
6
  "pi",