TimetableKit is a pnpm workspace. Its core package owns the timetable contract and deterministic behavior. Other packages add input, UI, transport, and provider boundaries around that core.
apps/web
browser boundary, playground, local OCR wiring, schema routes, security headers
packages/core
input schemas, deterministic parsing, normalization, validation, conflicts,
assessment, corrections, locale registry, JSON/CSV/iCalendar exporters
packages/agent
JSON tool definition, capabilities, bounded base64 input, JSONL transport
packages/cli
local file and stdin input, output files, command-line validation
packages/react
optional review, correction, agenda, and calendar components
packages/provider-pdfjs
PDF.js text extraction and scanned-page rendering
packages/provider-tesseract
local browser OCR for raster images
packages/provider-vercel-ai
opt-in structured recovery for unresolved fields
examples, fixtures, docs
consumer examples, synthetic test data, and project guidance
The dependency direction is inward. The core does not import browser, file system, OCR, PDF.js, React, Next.js, Vercel, or model SDK code. Provider and UI packages import the public core package.
flowchart LR
A[Text, CSV, image, or PDF] --> B[Input schema and limits]
B --> C[Configured extraction provider]
C --> D[Bounded text document]
D --> E[Core parser and locale rules]
E --> F[Normalized events]
F --> G[Validation and conflict detection]
G --> H[Versioned parse result]
H --> I[Review and typed correction]
I --> J[JSON, CSV, or iCalendar]
H -. optional unresolved fields .-> K[Consented recovery provider]
K --> G
The input boundary accepts only the supported discriminated union. It checks
the input schema before provider work. A provider returns an
ExtractionArtifact with a source descriptor, pages, text lines, locations,
and provider warnings.
The core converts that artifact into candidates, normalizes fields, validates
the complete result, detects conflicts, and returns a schema version of 1.0.
The result keeps warnings, source evidence, confidence, provider IDs, and stage
reports together so a caller can build a review flow without reading parser
internals.
Use this map before you change code.
| Change | Package or path |
|---|---|
| Input shapes, locale aliases, date and time recognition, normalization, warnings, validation, corrections, conflicts, or exports | packages/core |
| PDF loading, page rendering, OCR handoff | packages/provider-pdfjs |
| Image OCR worker lifecycle | packages/provider-tesseract |
| Remote recovery request and output validation | packages/provider-vercel-ai |
| Agent schemas, capabilities, JSONL framing, structured errors | packages/agent |
| Local paths, stdin, output files, and CLI messages | packages/cli |
| React review and correction views | packages/react |
| File selection, playground state, browser routes, and local asset wiring | apps/web |
| Public parser behavior | fixtures, packages/core/tests, and the generator definitions |
Keep parser rules in packages/core. Keep browser, filesystem, OCR, PDF, and
remote provider code outside the core package.
Do not import another workspace package through its src directory. Build the
core package before you typecheck a provider package. The lint command checks
import and re-export syntax in packages, apps, and examples. It permits a
package to use its own source, but requires other consumers to use public
exports. Type-only imports and literal dynamic imports follow the same rule.
TimetableInputSchema accepts UTF-8 text and CSV strings, or Uint8Array
bytes with an allowed image or PDF MIME type. The core applies byte, pixel,
page, timeout, and output limits. It treats all content as data.
The core does not follow URLs found in timetable content. It does not execute commands or instructions found in imported text, OCR output, or PDF text.
An extraction provider declares support for an input kind and returns a validated artifact. The parser tries configured providers in order. A provider can report progress and must honor the supplied abort signal and limits.
The parser maps provider failures to stable warnings when it can continue. It
returns an unusable result when no provider can produce an artifact. A caller
abort remains an ABORTED error.
Recovery is a separate provider interface. The core collects only unresolved
event fields linked to warnings. Recovery is disabled unless the host configures
a provider and the parse options set both enabled: true and consent: true.
The provider receives a bounded request and returns schema-checked patches. The core accepts patches only for requested fields, applies them as typed changes, and runs validation and conflict detection again. A failed recovery attempt keeps the deterministic result available.
The agent package turns JSON requests into core inputs and turns results or failures into JSON-safe responses. The CLI adds local path and stdin handling. Neither package duplicates parser rules. The agent checks cancellation before calling an injected parser and stops waiting when the signal aborts. It does not accept a successful result after cancellation. Custom parsers must still honor the signal to stop their own work.
The web app and React package consume the public result, warning, assessment, and correction APIs. They do not decide a second set of parser rules. The web playground keeps selected content in browser memory and wires local OCR and PDF providers when the user selects binary input. Each input tab owns its draft in memory. Switching tabs changes the active source without discarding other drafts. Reset clears every draft and the current result. The editor renders at most 50 event rows per page. Corrections and exports still use the full result. Paging moves keyboard focus to the table.
The core result is immutable by convention. applyEventCorrection returns a
new result when a field changes and recomputes warnings, conflicts, and the
deterministic confidence summary. Generated event IDs stay unchanged when
unique. If different events collide, normalization sorts their canonical keys
and adds suffixes to disambiguate them before attaching warnings. It returns the original result for an unknown
event or a no-op correction. React correction callbacks compose edits made
before the next parent render. A new result supplied by the parent replaces
that pending correction state.
Conflict detection prepares times and exact-date membership once per detector call. The cache is local to that call, so later calls read current input values. Pair traversal and resource limits remain the same.
Assessment is derived state. assessTimetableResult and
analyzeTimetableResult check events, warnings, event semantics, term bounds,
and bounded conflicts. Assessment is not stored in TimetableParseResult and
is not serialized into JSON, CSV, or iCalendar output. The core exports the
assessment functions and the ResultAssessment type for callers.
Stage reports keep the public pipeline vocabulary stable. The current
deterministic parser performs several row-recognition operations during
normalize, while the reports still expose the stages preflight, extract,
normalize, segment, recognize, assemble, locale, deduplicate,
validate, conflicts, confidence, recovery, and finalize.
The default path is local and in memory. Do not add logging, persistent upload storage, remote URL fetching, or real user schedules to fixtures. A remote provider must be optional, visible to the host, bounded, cancellation-aware, schema-validated, and covered by a data-retention policy.
See Privacy, Security, and Providers before adding an external service.
Run the smallest relevant check while developing. Run pnpm validate before a
release or after a cross-package change. The full command checks formatting,
package metadata, source boundaries, copy, types, tests, coverage, fixtures,
builds, browser behavior, local links, secret patterns, and dependencies.
The validation command is local evidence. It does not prove package publication, deployment, hosted CI, OCR accuracy with a real worker, or a third-party provider's retention behavior.