Skip to content

feat: parse with the TypeScript compiler, share one validated API handler, add CI - #4

Merged
Devrax merged 8 commits into
masterfrom
feature/ast-mock-engine
Oct 12, 2026
Merged

Devrax merged 8 commits into
masterfrom
feature/ast-mock-engine

Conversation

@Devrax

@Devrax Devrax commented Oct 12, 2026 •

Copy link
Copy Markdown
Owner

Summary

Replaces the regex-based mocking engine with a real parser built on the TypeScript compiler API, consolidates the two API routes behind one validated handler, adds CI, and restructures the README. Includes the CLAUDE.md from #3 (this branch was cut from it).

Engine (core/)

  • Parser (core/parser/): ts.createSourceFile + AST walk (syntax only, no type checker) into a small type model. Syntax errors now throw with line and column instead of being silently mis-parsed. The old \; / \: escapes are still accepted.
  • Generator (core/generator/): turns the model into Faker values. Interface2Mock keeps its constructor, buildMock() and error messages, and gains an optional { seed, maxDepth } argument for repeatable output.
  • Newly supported: inline object literals, Array<T> / ReadonlyArray<T>, nested arrays and tuples, type unions and intersections, multiple extends, generics with defaults, index and method signatures (methods are skipped instead of aborting the block), enum, Partial / Required / Readonly / Pick / Omit / Record, mapped types, keyof, indexed access, template literal types, bigint and the remaining scalar keywords.
  • Recursive types (interface Node { children: Node[] }) are cut at maxDepth (3) instead of looping forever.
  • Removed typeValidation.ts, ValidTypes.ts and randNumber.ts; all randomness goes through a per-instance Faker.

HTTP layer

  • routes/api/mock and routes/api/typemocker are one-line wrappers around core/http/mockHandler.ts, which is unit-tested with a plain Request.
  • Bad JSON, non-string or > 100 KB value, unparsable valueForAny, quantity outside 1-10, non-integer seed and TypeScript syntax errors answer 400 with { error }; unexpected failures stay 500. quantity may be omitted or null. Bigints are serialized as strings.

Tooling and docs

  • Faker and assertions moved to npm: / jsr: specifiers; typescript added via npm:.
  • .github/workflows/ci.yml runs deno fmt --check, deno lint, deno test core/ on a pinned Deno version; deno task test / deno task check mirror it locally.
  • README restructured: quick start (web, API, library), request/response reference, a "Supported TypeScript" table with a worked example, limitations, FAQ, development section. CLAUDE.md updated for the new layout.

Behavior changes worth a look

  • 'tel' | string now picks any member (the old engine always returned the literal); string | null yields null about half the time.
  • Numbers come from Faker's full safe-integer range.
  • quantity of 0 or negative now returns 400 instead of being treated as 1.
  • Arrays have 1-5 independently generated items instead of 1-4 copies of the same object.
  • Loading the TypeScript compiler adds roughly a second to cold start. core/parser/ts_sys_guard.ts sets process.browser while it loads so deno test does not need --allow-env.

Test plan

  • deno task check (fmt, lint, 57 type-checked tests) passes
  • All 24 pre-existing tests still pass unchanged in their assertions
  • Stress corpus of 30 realistic/adversarial inputs through the engine and the handler (GitHub user payload, order graph with enums and generics, utility types, deep recursion, index/method signatures, namespaces, 200-member interface, seeds, every 400 path). Found and fixed one bug: keyof over an interface returned a random string.
  • Real Fresh server (main.ts) under Deno 1.46.3 and Deno 2.9.7: landing page SSR, island bundles, /api/mock with CORS preflight and headers, /api/typemocker without CORS, 405 on GET, the deno.dev 301 redirect, 404, static assets, a 10 × 200-field batch (< 100 ms)
  • dev.ts under Deno 2.9.7: regenerates fresh.gen.ts identical to the committed one and serves the app
  • Headless Chromium against the running app: Monaco loads, play/copy/clear buttons, name filter + quantity, any selector, datalist suggestions, syntax error shown with line and column, paste auto-mock, no unexpected console errors

Note for reviewers: the sandbox cannot reach deno.land or esm.sh, so the server runs above used a throwaway import map that mirrors Fresh's dependencies from GitHub and npm. The repository's own deno.json is unchanged in that respect.

🤖 Generated with Claude Code

https://claude.ai/code/session_01JgwVZtwAHHXJu5neejhnYR

Switch Faker to npm:@faker-js/faker@8.4.1 and the test assertions to jsr:@std/assert, replacing three different deno.land std versions. Cast the two buildMock() results the tests index into so the suite passes with type checking.
Replace the regex engine with a parser (core/parser) that walks the
TypeScript AST into a small type model, and a generator (core/generator)
that turns that model into Faker values. Interface2Mock keeps its
constructor and buildMock() contract and error messages, and takes an
optional { seed, maxDepth } third argument.

Newly supported: inline object literals, Array<T>/ReadonlyArray<T>,
nested arrays and tuples, unions of types, intersections, multiple
extends, generics with defaults, index and method signatures, enums,
Partial/Required/Readonly/Pick/Omit/Record, mapped types, keyof and
indexed access, template literal types, bigint and the remaining scalar
keywords. Recursive types are cut after maxDepth (3) expansions instead
of looping forever. The \; and \: escapes are still accepted.

Remove typeValidation.ts, ValidTypes.ts and randNumber.ts, which only
served the regex engine; all randomness now goes through a per-instance
Faker so a seed gives repeatable output.
Move the duplicated POST bodies of /api/mock and /api/typemocker into
core/http/mockHandler.ts. Malformed JSON, a non-string or oversized
(> 100 KB) source, unparsable valueForAny, quantity outside 1-10, a
non-integer seed and TypeScript syntax errors now answer 400 with
{ error }; unexpected failures still answer 500. The body accepts an
optional seed, quantity may be omitted or null, and bigint values are
serialized as strings. The handler no longer logs the submitted source.
Add a GitHub Actions workflow running deno fmt --check, deno lint and
deno test core/ on a pinned Deno version, plus "test" and "check" tasks.
Format the remaining files with proseWrap "preserve" so Markdown prose
keeps its line breaks. islands/, components/ and static/ are excluded
from fmt and lint: they hold the Monaco glue and its pre-existing
globals, which this change leaves untouched. Give the logo button in
routes/index.tsx an explicit type to satisfy jsx-button-has-type.
README: describe the request fields (including seed) and 400 responses,
replace the limitations table and status list with what the TypeScript
parser now supports (inline objects, generics, Array<T>, index and
method signatures, enums, utility types, recursion limit), and note
that the \; and \: escapes are accepted but no longer required.
CLAUDE.md: describe the parser/generator/facade layout, the shared HTTP
handler and the new test/check tasks.
The generator passed the operand of a keyof node to #keysOf instead of
the node itself, so `type Keys = keyof User` with User declared as an
interface fell through to a random string. Aliases and enums were
unaffected because #keysOf resolves them directly. Add the case to the
utility-types test.
…ed syntax

Replace the status checklist and scattered notes with a quick start for
the web UI, the HTTP API and library use, a request/response reference,
a table of supported TypeScript constructs with one worked example, a
limitations table, a short FAQ and a development section (commands,
layout, how routes and islands are verified). Drop the bad-formatting
screenshot, which documented a parsing quirk of the old engine. Point
CLAUDE.md at the README tables as the capability contract and note the
Deno versions the app was verified on.
@Devrax
Devrax force-pushed the feature/ast-mock-engine branch from d4d3b25 to 4c25b09 Compare October 12, 2026 15:41
@Devrax
Devrax merged commit 8dfff98 into master Oct 12, 2026
1 check passed
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.

2 participants