Skip to content

refactor(appkit): add defineManifest to remove manifest casts#485

Draft
MarioCadenas wants to merge 1 commit into
mainfrom
refactor/define-manifest
Draft

refactor(appkit): add defineManifest to remove manifest casts#485
MarioCadenas wants to merge 1 commit into
mainfrom
refactor/define-manifest

Conversation

@MarioCadenas

Copy link
Copy Markdown
Collaborator

What

Introduces defineManifest() in the registry so plugins stop hand-casting their JSON manifests. All 9 plugins now declare:

static manifest = defineManifest<"my-plugin">(manifest);

instead of static manifest = manifest as PluginManifest<...> (agents needed the uglier as unknown as PluginManifest).

Why

The cast was unavoidable with a raw .json import:

  • TS widens every JSON field to string, but PluginManifest.resources[].type is the nominal ResourceType enum — a structural string never assigns to it.
  • Multi-resource plugins (only agents today) infer a heterogeneous union for resources, which a plain as also rejects (hence agents' as unknown as).

So the escape hatch wasn't a plugin bug — it was the JSON→type boundary being crossed by assertion in 9 places, with no runtime validation.

How

defineManifest() parses the manifest through the canonical pluginManifestSchema (Zod) and returns the strict type via one audited internal assertion, after parse has confirmed the values are real ResourceType/permission strings. This:

  • removes every per-plugin cast (one bridge assertion, centralized),
  • adds real runtime validation at module load that the old cast never provided,
  • preserves the literal plugin name via <TName> (needed — toPlugin derives the typed plugin key from manifest.name; widening to string would collapse the typed registry).

pluginManifestSchema is now exported from shared for the parse.

Verification

  • pnpm -r typecheck — clean across all projects
  • Full plugin + registry suites — 1093 pass, 1 skipped
  • Directly parsed all 9 real manifest.json files through the schema — all OK
  • New unit tests: valid pass-through + rejection of unknown resource type, invalid permission, and missing required field
  • biome check clean

Split out of the MLflow-tracing PR (pr/agent-evals-1-tracing), which is where this cast friction first surfaced.

Every plugin declared `static manifest = manifest as PluginManifest`
(agents needed `as unknown as`). The cast is unavoidable with a raw JSON
import: TS widens JSON fields to `string`, but PluginManifest.resources[].type
is the nominal ResourceType enum, so the structural shape never assigns —
and multi-resource plugins infer a heterogeneous union that a plain `as`
also rejects.

Add defineManifest() in the registry: it parses the JSON through the
canonical pluginManifestSchema (real runtime validation, which the loader
did not do before) and returns the strict type via one audited internal
assertion. All 9 plugins now use `static manifest = defineManifest<"name">(manifest)`
with no local casts. Export pluginManifestSchema from shared for the parse.

Adds tests covering valid pass-through and rejection of unknown resource
type, invalid permission, and missing required fields.

Signed-off-by: MarioCadenas <MarioCadenas@users.noreply.github.com>
@MarioCadenas
MarioCadenas requested a review from a team as a code owner July 20, 2026 16:58
@github-actions

Copy link
Copy Markdown
Contributor

🔬  Run evals on this PR  ·  Go to Evals Monitor →

@github-actions

Copy link
Copy Markdown
Contributor

📦 Bundle size report

Compared against bundle-size-baseline.json (main).

@databricks/appkit

npm tarball (packed): 723 KB (+21 KB) — gzipped download (dist + bin; excludes release-only docs/NOTICE).

dist raw gzip
JS (runtime) 757 KB (+29 KB) 262 KB (+8.0 KB)
Type declarations 275 KB (+98 B) 94 KB (+12 B)
Source maps 1.5 MB (+57 KB) 488 KB (+14 KB)
Other 11 KB 3.7 KB
Total 2.5 MB (+86 KB) 848 KB (+22 KB)
Per-entry composition (own code — deps external (as shipped))
Entry Initial (gz) Lazy (gz) Total (gz) node_modules (min) Own code (min)
. 83 KB (+4.7 KB) 2.5 KB 86 KB (+4.7 KB) external 275 KB (+17 KB)
./beta 44 KB (+4.9 KB) 230 B 44 KB (+4.9 KB) external 135 KB (+18 KB)
./type-generator 19 KB 0 B 19 KB external 54 KB

Chunks:

Entry Chunk Load Size (gz)
. index.js initial 79 KB
. utils.js initial 4.0 KB
. remote-tunnel-manager.js lazy 2.5 KB
./beta beta.js initial 35 KB
./beta databricks.js initial 5.7 KB
./beta service-context.js initial 3.2 KB
./beta client-options.js initial 220 B
./beta databricks.js lazy 127 B
./beta index.js lazy 103 B
./type-generator index.js initial 19 KB

@databricks/appkit-ui

npm tarball (packed): 295 KB (+9 B) — gzipped download (dist + bin; excludes release-only docs/NOTICE).

dist raw gzip
JS (runtime) 350 KB 116 KB
Type declarations 201 KB (+32 B) 72 KB (+9 B)
Source maps 669 KB 218 KB
CSS 16 KB 3.3 KB
Total 1.2 MB (+32 B) 410 KB (+9 B)
Per-entry composition (consumer bundle — deps bundled, peerDeps external)
Entry Initial (gz) Lazy (gz) Total (gz) node_modules (min) Own code (min)
./js 4.3 KB 49 KB 54 KB 208 KB 12 KB
./js/beta 20 B 0 B 20 B 0 B 0 B
./react 428 KB 49 KB 476 KB 1.3 MB 163 KB
./react/beta 20 B 0 B 20 B 0 B 0 B

Chunks:

Entry Chunk Load Size (gz)
./js index.js initial 4.2 KB
./js chunk initial 120 B
./js apache-arrow lazy 49 KB
./js/beta beta.js initial 20 B
./react index.js initial 426 KB
./react tslib initial 2.1 KB
./react apache-arrow lazy 49 KB
./react/beta beta.js initial 20 B

@MarioCadenas
MarioCadenas marked this pull request as draft July 21, 2026 08:17
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant