npm package discovery and stats viewer.

Discover Tips

  • General search

    [free text search, go nuts!]

  • Package details

    pkg:[package-name]

  • User packages

    @[username]

Sponsor

Optimize Toolset

I’ve always been into building performant and accessible sites, but lately I’ve been taking it extremely seriously. So much so that I’ve been building a tool to help me optimize and monitor the sites that I build to make sure that I’m making an attempt to offer the best experience to those who visit them. If you’re into performant, accessible and SEO friendly sites, you might like it too! You can check it out at Optimize Toolset.

About

Hi, 👋, I’m Ryan Hefner  and I built this site for me, and you! The goal of this site was to provide an easy way for me to check the stats on my npm packages, both for prioritizing issues and updates, and to give me a little kick in the pants to keep up on stuff.

As I was building it, I realized that I was actually using the tool to build the tool, and figured I might as well put this out there and hopefully others will find it to be a fast and useful way to search and browse npm packages as I have.

If you’re interested in other things I’m working on, follow me on Twitter or check out the open source projects I’ve been publishing on GitHub.

I am also working on a Twitter bot for this site to tweet the most popular, newest, random packages from npm. Please follow that account now and it will start sending out packages soon–ish.

Open Software & Tools

This site wouldn’t be possible without the immense generosity and tireless efforts from the people who make contributions to the world and share their work via open source initiatives. Thank you 🙏

© 2026 – Pkg Stats / Ryan Hefner

workspec

v1.2.5

Published

WorkSpec 2 runtime, validator and CLI.

Readme

WorkSpec (CLI + Validator)

This package provides:

  • A document validator (validate()) and project validation orchestrator (validateProject()) that emit RFC 7807-style Problem Details with validation provenance
  • The current Starting State JSON Schema (v2.2.schema.json)
  • Shared TypeScript declarations for authoring changes.workspec.js, constraints.workspec.js, and generator.workspec.js
  • Shared observable playback-state and State Library visual resolution helpers
  • A workspec CLI with validate, snapshot, constraints, migrate, and format commands

WorkSpec 2 project architecture

A project has four authoring files:

  • start.workspec.json is declarative Starting State: world, objects, layout, configuration, planned tasks, timing, and dependencies. It contains no executable effects.
  • changes.workspec.js contains explicit authored changes. Task handles retain onStart and onComplete; set, change, move, create, and remove are ambient inside handlers.
  • generator.workspec.js is optional simulation logic. Its output enters the same observable history as Changes.
  • constraints.workspec.js is optional executable domain intent. Constraints inspect resolved state/history and report structured violations; they do not inspect Changes or Generator source.

Changes example:

WorkSpec.task("mix_dough", task => {
    task.onStart(() => {
        set("mixer", "state", "in_use", { temporary: true });
    });

    task.onComplete(() => {
        change("flour", "quantity", -3);
        set("mixer", "state", "dirty");
    });
});

Generator example:

WorkSpec.onStart(({ set }) => set("mixer", "temperature", 20));
WorkSpec.onUpdate(({ get, set, random }) => {
    const drift = random() < 0.5 ? -1 : 1;
    set("mixer", "temperature", get("mixer", "temperature") + drift);
});

The deterministic tick rule is: onStart runs at execution start, then onUpdate runs once at each whole minute strictly after that start through the last resolved task completion (or the requested horizon), in ascending order. delta is one minute. Random values come from the seeded random() helper; do not use Math.random(). The seed is project/runtime metadata, not world state.

runProject() produces one authoritative resolved run. At a logical time the runtime completes active tasks and applies their onComplete Changes, applies Generator writes, evaluates task dependencies and guards, selects an actor only for work whose guards passed, checks reservations, then applies accepted tasks' onStart Changes. Generator owns a same-target/property conflict at that time: completion Changes are overwritten, conflicting start Changes are suppressed, and a non-blocking generator.changes.conflict warning is emitted. Numeric deltas from simultaneous Changes combine; other simultaneous Changes writes conflict transactionally.

Use runtime.runProject(startingState, changesSource, generatorSource, { seed, until }) to produce the single observable history consumed by playback, snapshots, and Constraints. runtime.snapshotRunAt(run, time) and runtime.runConstraintsOnResult(run, constraintsSource, { time }) inspect that existing execution without rerunning Generator or Changes. runtime.analyzeChanges(source, { taskIds }) provides lightweight Changes indexing. Runtime execution remains authoritative for dynamic JavaScript.

A finite until is a requested horizon: resolve the project through T. It is not a demand that all planned work finish by T. Later tasks remain pending, tasks crossing T remain active, and dependencies waiting only on future completion are not failures. A run separately reports requestedHorizon, resolvedThrough, and complete. If the work budget stops execution early, snapshots and Constraints refuse to inspect times after resolvedThrough.

Execution is incremental and defaults to a 10,000-work-unit safety limit; trusted callers can set maxEvents/--max-events (up to 1,000,000). Work units count task starts/completions, Generator callbacks and writes, Changes interactions, reservation evaluation, active guards, and dynamic-collection work instead of counting only timestamps. Invalid or clamped limits produce configuration diagnostics. This prevents a large same-minute workload or minute-driven Generator run from silently escaping the budget. Because callbacks are synchronous JavaScript, the package runtime cannot pre-empt a callback that never returns. The CLI therefore requires executable project sources to be trusted; hosts that accept untrusted sources must use worker/process isolation with a wall-clock timeout.

Validation is deliberately layered:

  • validate(startingState) checks only facts decidable from start.workspec.json.
  • validateProject(startingState, { changesSource, generatorSource, constraintsSource, seed, until }) composes document, source, runtime/history, and optional Constraint results.
  • Every built-in problem includes scope and provenance; the scope is document, source, runtime, or constraint.

For WorkSpec 2.2, document validation does not infer resource utilization, profitability, actor execution overlap, lifecycle, or resource flow from Starting State. A bounded “unused during this run” diagnostic requires an explicit finite until; other domain conclusions such as profitability belong in Constraints unless their runtime semantics and horizon are explicitly defined. “Used through T” means read by an expression or Generator get(), written, created, removed, or named by a resolved reservation attempt through T. Boolean all/any expressions short-circuit, so unreachable branches do not count. Constraint reads are evidence queries over the completed execution and deliberately do not change execution-usage accounting.

State-driven visuals

WorkSpec v2 simulations may define reusable simulation.state_libraries. Objects opt in with object-level state_library and optional appearance fields; properties.state remains the simulation source of truth. Appearance mappings contain project asset IDs only—asset bytes remain in the Studio project asset store.

{
  "simulation": {
    "state_libraries": {
      "actor_basic": {
        "states": ["available", "working"],
        "appearances": {
          "female": {
            "available": "asset_female_idle",
            "working": "asset_female_working"
          }
        }
      }
    },
    "world": {
      "objects": [{
        "id": "worker_1",
        "type": "actor",
        "name": "Worker 1",
        "state_library": "actor_basic",
        "appearance": "female",
        "properties": { "state": "available" }
      }]
    }
  }
}

createPlaybackModel(document) prepares a reusable pure playback model. resolveWorldStateAtTime(model, time), getObjectAtTime(...), getObjectStateAtTime(...), and getObjectLocationAtTime(...) resolve the observable WorkSpec v2 world without requiring renderers to replay interactions. resolveStateVisualAssetId(document, object, state) maps the resulting semantic state to an asset ID. The compatibility helper resolveObjectStateAtTime(object, tasks, time) delegates to the playback-state layer.

JSON Schema coverage

v2.2.schema.json identifies the current declarative Starting State surface. The dependency-free validator is the canonical semantic source of truth and rejects executable task behaviour in WorkSpec 2 Starting State. Earlier schemas remain only as historical versioned specifications.

Install

Install globally to use workspec from any terminal/command prompt path:

npm install -g workspec

After install, run:

workspec --help

This follows npm's standard cross-platform CLI pattern via package.json#bin:

  • macOS/Linux: npm links an executable on your PATH
  • Windows: npm creates command shims (workspec.cmd/workspec.ps1)

CLI

Validate:

workspec validate path/to/start.workspec.json
workspec validate path/to/start.workspec.json --json
workspec validate path/to/start.workspec.json --changes path/to/changes.workspec.js --generator path/to/generator.workspec.js --seed 17 --time 09:42 --json
workspec validate path/to/start.workspec.json --changes path/to/changes.workspec.js --constraints path/to/constraints.workspec.js --time 09:42 --json --yes
workspec validate -custom path/to/simulation-validator-custom.js path/to/start.workspec.json -y
workspec validate path/to/start.workspec.json --custom path/to/custom-validator.js --custom-catalog path/to/metrics-catalog-custom.json -y

The CLI discovers changes.workspec.js, generator.workspec.js, and constraints.workspec.js beside the Starting State. Explicit source paths override those files. Use --no-changes, --no-generator, or --no-constraints to exclude an input. A loaded Constraints source requires --yes. --time is optional, but it is required for claims whose truth depends on a finite horizon, such as a resource being unused during the run. JSON validation output is always an envelope with validation, run, and problems; run is null for document-only validation and contains seed, requested horizon, actual resolved-through time, completion status, and work-budget accounting for project validation.

Run a project and inspect its resolved observable state:

workspec snapshot path/to/start.workspec.json \
  --time 09:42 \
  --seed 1 \
  --json

--changes and --generator override discovered sibling sources. --time accepts elapsed minutes, HH:MM, a strict ISO date-time, or a JSON day/time value such as '{"day":2,"time":"09:42"}'. --seed defaults to 1 and controls the Generator's deterministic random() helper. --max-events sets the work-unit budget. With --json, stdout is a stable envelope containing time, time_minutes, seed, run, the resolved state, and runtime problems. When execution does not reach the requested time, state is null, run.resolved_through reports the safe boundary, and snapshot.time.unresolved is returned. The command exits 1 when runtime problems contain an error and 2 for CLI usage or file-reading errors.

The snapshot command creates one runProject(...) result and passes that same object to snapshotRunAt(...); it does not replay authored behavior independently and does not expose runtime history. Validate Starting State separately with workspec validate when using the edit → validate → snapshot loop.

Authoring language declarations

The package publishes ambient TypeScript declarations for each executable WorkSpec 2 authoring file. Editors and agents can load the declaration matching the file they are writing:

  • workspec/workspec-changes.d.ts
  • workspec/workspec-constraints.d.ts
  • workspec/workspec-generator.d.ts

For example, a checked JavaScript Changes file can opt in explicitly:

// @ts-check
/// <reference types="workspec/workspec-changes" />

Run ordinary JavaScript runtime constraints over the resolved project:

workspec constraints path/to/start.workspec.json \
  --changes path/to/changes.workspec.js \
  --generator path/to/generator.workspec.js \
  --constraints path/to/constraints.workspec.js \
  --time 09:42 \
  --seed 1 \
  --json --yes

--time is optional and defaults to the resolved run's final time. The command validates Starting State before execution, emits run, violations, and runtime problems, and exits 1 for an error violation or runtime problem. A requested time beyond run.resolved_through returns runtime.execution.unresolved_time without invoking Constraint callbacks. --yes acknowledges that the constraint file is ordinary JavaScript.

A constraint may be registered directly or exported with CommonJS:

WorkSpec.constraint('inventory.non_negative', (ctx) => {
    const quantity = ctx.get('inventory_stock', 'quantity');
    if (quantity >= 0) return null;
    return {
        severity: 'error',
        time: ctx.time,
        objects: ['inventory_stock'],
        property: 'quantity',
        observed: quantity,
        expected: { min: 0 },
        message: 'Inventory cannot be negative.'
    };
});

The read-only query context exposes time, state(), stateAt(time), get(objectId, property), getAt(time, objectId, property), and times(). times() returns the resolved event/update times from the authoritative run so a constraint can inspect a meaningful period without exposing Changes or Generator internals. stateAt() and getAt() may query any time already covered by that run; they never start another execution. Resolve through a later horizon first if a Constraint needs later state.

-custom/--custom supports:

  • Metrics Editor-style validate* functions in a plain .js file
  • Node-style exports (module.exports = function (...) { ... } or module.exports.validate = ...)

Custom validators run user-provided JavaScript and may be dangerous/malicious. The CLI now requires an interactive Y confirmation before custom validation executes. Use -y / --yes to skip the confirmation (required in non-interactive runs like CI). Custom validation execution is isolated in a subprocess with a hard timeout, and catalog/discovered entry points are restricted to validate* function names.

If --custom-catalog is omitted, the CLI auto-loads metrics-catalog-custom.json from the custom validator file's folder when present.

Migrate (Previous UAW Syntax -> WorkSpec 2.1):

workspec migrate legacy.json --out migrated.workspec.json --schema

Format JSON:

workspec format file.json --write