Skip to content

Split entry points, generate types from schema, humanize errors - #11

Merged
zhawtof merged 4 commits into
mainfrom
claude/brave-diffie-16dd3c
May 16, 2026
Merged

Split entry points, generate types from schema, humanize errors#11
zhawtof merged 4 commits into
mainfrom
claude/brave-diffie-16dd3c

Conversation

@zhawtof

@zhawtof zhawtof commented May 16, 2026

Copy link
Copy Markdown
Collaborator

Summary

Adds five new entry points so consumers only pay for what they use, generates TypeScript types directly from the JSON Schema, and collapses AJV's oneOf-branch cascade into focused per-block errors.

New entry points

Entry Pulls Ajv? Use when
@tightknitai/slack-block-kit-validator yes Full wrapper (existing behavior).
/helpers no Pure helpers, composable with Zod/TypeBox/your validator.
/schema no Just slackBlockKitSchema as a JS module.
/schema.json n/a Raw JSON Schema (existing).
/standalone no — precompiled Self-contained validateBlocks / validateModal / validateHome for Workers/Deno deploy. No runtime deps — Ajv usage is fully inlined at build time via esbuild.
/types no — types only Block, SectionBlock, ModalView, etc. generated from the schema.

Error humanization

  • /0/text paths now render as blocks[0].text, matching the helper-emitted format.
  • A malformed section block went from ~80 errors (expected "actions", expected "alert", … one per block kind) to 2 errors (missing required property 'text', missing required property 'fields').
  • Achieved by compiling one focus validator per block kind in BLOCK_TYPE_TO_DEF and re-validating each offending block against just its discriminator-matched branch. Umbrella oneOf/anyOf/not messages are suppressed when underlying errors say the same thing.
  • Single-line diagnostics for unrecognized / missing / wrong-shape type.

Tests

  • Property-based tests via fast-check for the recursive walkers and stripUndefined. Caught a real bug: check-response-url-enabled-context.ts crashed on null block slots (which appear after normalization). Fixed.
  • Type-level test (test/types.test-d.ts) exercises the most easily-broken generated shapes so the types can't silently regress.
  • Tests now live under tsconfig.test.json so tsc --noEmit actually checks them (the original config excluded test/).
  • 428 tests passing, was 404.

Benchmarks

bench/index.ts via tinybench. Baseline numbers (M-class laptop, Node 22):

Task ops/sec
validateBlockKit / 5-block valid ~64K
validateBlockKit / 50-block valid ~4.3K
findDuplicateBlockIds / 50 blocks ~890K
checkSurfaceCompatibility / 50 blocks ~4M

CI

  • Node matrix expanded to 20/22/24.
  • Bun job runs the full vitest suite under bun --bun.
  • Deno smoke (scripts/smoke-deno.ts) imports every entry point and runs basic round-trip checks.
  • Cloudflare Workers smoke bundles scripts/smoke-worker.ts via esbuild in worker mode; any unbundled require() or Node-only API surfaces as a build failure.
  • Generated types in sync check fails if src/types.generated.ts drifts from the schema.

Notes for the reviewer

  • src/types.generated.ts is committed (so types are greppable on GitHub). The new CI job + pnpm run generate:types chained into build keeps it honest.
  • Standalone bundle is 783 KB unpacked but only ~91 KB gzipped. The full tarball ships at 91.5 KB / 1 MB unpacked.
  • The README now points at the new entry points; old pnpm dlx ajv compile ... snippet removed.

Test plan

  • pnpm typecheck — src + test + bench + scripts all type-checked
  • pnpm lint — biome clean (42 files)
  • pnpm test — 428 passing
  • pnpm run build:clean — full pipeline (generate:typestsccompile:standalone) green
  • npm pack --dry-run — 66 files, 91.5 KB
  • Cross-entry-point smoke (/, /helpers, /schema, /standalone) — all 11 assertions pass
  • Consumer-style typecheck of dist/types.js — exports usable in strict TS
  • Bun job (verified on CI — no local Bun)
  • Deno smoke (verified on CI — no local Deno)
  • Workers smoke build (verified locally — esbuild produces 766 KB ESM bundle)

🤖 Generated with Claude Code

zhawtof and others added 4 commits May 15, 2026 16:15
The emitted ESM in 0.1.0-alpha.0 worked under bundler resolution but
broke under Node's native ESM resolver (used by vitest in consumer
repos). Three fixes:

- Append .js extensions to all relative imports in src/. tsc preserves
  source paths verbatim, so the emitted .js needs the extensions to
  match Node ESM's strict resolution rules.
- Add `with { type: "json" }` import attributes to the schema import
  and re-export so Node accepts JSON modules under ESM.
- Add .js to the ajv subpath import (`ajv/dist/2020.js`).
- Bump tsconfig `module` from ES2022 to ESNext so TypeScript accepts
  import attributes.
Adds five new entry points so consumers only pay for what they use:
- `/helpers` and `/schema` import no Ajv at runtime
- `/standalone` ships precompiled validators (Workers-friendly)
- `/types` re-exports TypeScript types generated from the JSON schema

Other changes:
- AJV errors now use `blocks[N].field` paths and collapse the oneOf
  cascade by re-validating each block against its discriminator-matched
  branch (a malformed section block drops from ~80 errors to 2)
- Property-based tests via fast-check caught a null-pointer crash in
  check-response-url-enabled-context.ts (fixed)
- Benchmarks via tinybench in bench/
- CI matrix expanded: Node 20/22/24, Bun, Deno smoke, Cloudflare Workers
  build smoke, plus a check that the committed types stay in sync with
  the schema
- tsconfig.test.json so tests/scripts/bench are type-checked too

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
Resolved:
- ci.yml: kept new Bun/Deno/Workers smoke jobs and the types-in-sync
  check alongside main's Codecov upload on the test job
- package.json: took main's 0.1.1-alpha.1 release-please bump
- src/index.ts: kept the new /helpers barrel (drops the now-redundant
  per-file re-exports main added)
- src/validate-block-kit.ts: kept the FocusValidators wiring
- pnpm-lock.yaml: regenerated against the merged package.json

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
scripts/smoke-deno.ts and scripts/smoke-worker.ts validate the *built*
artifacts, so they import from ../dist/*.js. The Typecheck CI job runs
without first building, which made tsc fail to resolve those imports.
Their own CI jobs (Smoke Deno, Smoke Workers) run after a build, so the
exclusion doesn't reduce coverage — it just stops Typecheck from
needing a build step that's irrelevant to it.

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
@zhawtof
zhawtof merged commit 429da51 into main May 16, 2026
11 checks passed
@zhawtof
zhawtof deleted the claude/brave-diffie-16dd3c branch May 16, 2026 01:52
zhawtof added a commit that referenced this pull request May 16, 2026
Resolve conflicts in package.json, src/index.ts, and src/validate-block-kit.ts
by taking the main branch versions — this branch's only intended change is
the new .github/pull_request_template.md, and the conflicts were purely from
the branch being based on an older commit before #11 landed.

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
zhawtof added a commit that referenced this pull request May 16, 2026
Resolve conflicts by taking main's version of package.json, src/index.ts,
and src/validate-block-kit.ts (all changed by PR #11's entry-point split
and version bump; this branch only touches README + MAINTAINING.md).

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
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