@study-lenses/tracing
v0.1.2
Published
API wrapper infrastructure for @study-lenses tracer packages
Readme
@study-lenses/tracing
API wrapper infrastructure for
@study-lensestracer packages. ValidatesTracerModuleobjects and provides four pre-bound API wrappers:trace,tracify,embody,embodify.
Pedagogical Purpose
Neutral infrastructure. This package makes no pedagogical decisions — those belong in tracer implementations and the tools that consume them.
The same infrastructure pattern appears across technology stacks:
- Git provides version control infrastructure → GitHub builds collaboration workflows
- Docker provides container infrastructure → Kubernetes builds orchestration intelligence
- SQL provides data storage infrastructure → ORMs build application logic
@study-lenses/tracingprovides execution-tracing API infrastructure →@study-lenses/trace-*packages build language-specific tracing logic → educational tools build learning experiences on top
Why Neutral?
Pedagogical approaches vary: constructivist vs. instructivist, individual vs. collaborative, formative vs. summative, visual vs. textual. Staying neutral at this layer enables multiple approaches to coexist. Domain experts (CS educators, learning scientists) can specialize their tools without reinventing the tracing infrastructure.
Config/Trace Symmetry
Config structure mirrors trace output structure. The limits you configure correspond to the events you observe:
| Config (meta.max.*) | What it limits | Trace output |
| --------------------- | ------------------- | --------------------------- |
| steps | Execution steps | step field per StepCore |
| iterations | Loop iterations | Tracer-specific step fields |
| callstack | Stack depth | Tracer-specific step fields |
| time | Execution time (ms) | timestamps option |
Only Log Learner-Visible Behavior
Tracer implementations should log what a professional debugger shows — explicit operations
that a learner would see and reason about. Implicit operations (e.g., implicit toString
coercions, internal VM bookkeeping) are not logged. This keeps traces meaningful.
Who Is This For
Primary — Tracer package developers building @study-lenses/trace-* packages:
- CS Instructors building custom assessment tools for large classes
- EdTech Developers building debugging platforms for coding bootcamps
- Documentation Authors building interactive examples for libraries
- Research Tool Builders collecting data for CS education studies
- IDE Extension Developers building educational debugging visualizations
This package gives you the four standard API wrappers so you only write the record()
function. Everything else is handled.
Secondary — Educational tool developers consuming tracer packages:
- Building Study Lenses integrations, LMS plugins, or custom learning environments
- Consume tracer packages (which depend on this) rather than using this package directly
Tertiary — CER researchers using tracer packages for data collection or measurement.
Install
npm install @study-lenses/tracingQuick Start
// In a tracer package (typical use)
import tracing from '@study-lenses/tracing';
import tracerModule from './tracer-module.js';
// Validates tracerModule once upfront; returns 4 pre-bound wrappers
const { trace, tracify, embody, embodify } = tracing(tracerModule);
// trace: positional args, throws on error
const steps = await trace(code, config);
// tracify: chainable, throws on error
const steps = await tracify.code(code).config(config).steps;
// embody: keyed args, returns { ok, steps } or { ok: false, error }
const result = await embody({ code, config });
if (result.ok) console.log(result.steps);
// embodify: keyed + chainable, .trace() is async
const chain = await embodify({ code }).set({ config }).trace();
if (chain.ok) console.log(chain.steps);API — The 2x2 Matrix
| | Simple | Chainable |
| ---------- | -------- | ---------- |
| Throws | trace | tracify |
| Safe | embody | embodify |
Throws = errors propagate as exceptions. Use in scripts and REPLs.
Safe = errors returned as { ok: false, error }. Use in production UIs.
Simple = all args at once. Chainable = build state across multiple calls.
Full API reference: run npm run docs locally, or see the hosted API docs.
TracerModule Contract
Every tracer package exports one object matching this contract:
| Field | Type | Required | Purpose |
| --------------- | ------------------- | -------- | ---------------------------------------------------------------- |
| id | string | Yes | Unique tracer ID, e.g. 'js:klve' — used for cache invalidation |
| langs | readonly string[] | Yes | Supported file extensions; [] = universal tracer |
| record | RecordFunction | Yes | The tracing function |
| optionsSchema | JSON Schema object | No | Tracer-specific options schema |
| verifyOptions | (opts) => void | No | Cross-field semantic validation |
import type { TracerModule } from '@study-lenses/tracing';
const tracerModule: TracerModule = {
id: 'js:klve',
langs: Object.freeze(['js', 'mjs', 'cjs']),
record: async (code, { meta, options }) => {
// ... language-specific tracing logic
return steps;
},
optionsSchema: {
/* JSON Schema */
},
verifyOptions: (options) => {
/* cross-field checks */
},
};
export default tracerModule;langs: [] = universal tracer — accepts any language. The chainable APIs use langs to
decide whether to keep or clear code when switching tracers.
What We Provide / What Your Tool Does
We provide:
TracerModulevalidation (TracerInvalidErrorwith aggregate violations)- Config pipeline: shorthand expansion, default-filling, JSON Schema validation
- Four pre-bound API wrappers with immutable state, caching, and error handling
- All error classes for
instanceofdiscrimination - TypeScript types:
TracerModule,RecordFunction,StepCore,MetaConfig,ResolvedConfig
Your tracer does:
- Language-specific parsing and execution tracing
options.schema.jsondefining tracer-specific options- Optional
verifyOptionsfor cross-field constraints
Your tool does:
- Educational intelligence (what to show, when, how)
- Student-facing interfaces and visualizations
- Assessment logic and progress tracking
- Pedagogical sequencing and scaffolding
Architecture
tracing(tracerModule)
→ validateTracerModule() // TracerInvalidError if contract violated
→ Object.freeze({ trace, tracify, embody, embodify })
trace(code, config?) [and other wrappers]
→ prepareConfig(meta, metaSchema) // /configuring — expand + fill + validate
→ prepareConfig(options, optionsSchema) // /configuring — same pipeline
→ tracerModule.verifyOptions?(options) // tracer's semantic validator
→ tracerModule.record(code, { meta, options }) // tracer-specific execution
→ readonly StepCore[]Layer stack (bottom → top, each layer imports only from below):
src/utils/ ← deep-clone, deep-freeze, deep-freeze-in-place, deep-merge, deep-equal
src/errors/ ← EmbodyError base + 8 specific error classes
src/configuring/ ← pure config pipeline (schema-agnostic, tracer-agnostic)
src/api/ ← trace, tracify, embody, embodify
src/tracing.ts ← tracing() sugar (validates TracerModule, returns pre-bound wrappers)
src/index.ts ← entry point (re-exports everything public)See DEV.md for full architecture, conventions, and TDD workflow. See src/DOCS.md for cross-cutting architectural decisions.
Contributing
See CONTRIBUTING.md and DEV.md.
License
MIT © 2026 CodeSchool in a Box
