Skip to content
Draft
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
38 commits
Select commit Hold shift + click to select a range
ae15575
v3.0.0: Modular architecture, internal MT19937, cleanup
abrisene Feb 13, 2026
0003be8
Enforce true immutability for MarkovChain and Distribution instance m…
abrisene Feb 13, 2026
15e7962
Regenerate typedoc for v3.0.0 with immutable return types
abrisene Feb 13, 2026
688165c
Remove stale CI, Jest config, and npm lockfile; add .nvmrc
abrisene Feb 13, 2026
c35d361
Migrate from Jest to Vitest; consolidate on pnpm
abrisene Feb 13, 2026
eaa47dd
Add docs/ to .gitignore; CI deploys docs on release
abrisene Feb 13, 2026
e0d40f7
Add GitHub Actions for CI, docs deployment, and releases
abrisene Feb 13, 2026
71639b6
Update tsup dts config and CLAUDE.md for new toolchain
abrisene Feb 13, 2026
d13f4da
Merge origin/master into v3.0.0
abrisene Feb 13, 2026
566a447
Add packageManager field for pnpm/action-setup@v4
abrisene Feb 13, 2026
d703f78
Fix CI lint: hoist eslint plugins, rename .prettierrc.js to .cjs
abrisene Feb 13, 2026
99e7b42
Add RandomSampler with statistical distribution sampling
abrisene Mar 11, 2026
0ec36e2
Add immutable variants for Distribution, MarkovChain, and MultiDimMar…
abrisene Mar 11, 2026
c824f57
Fix lint: empty interface, prettier formatting, unused variable
abrisene Mar 11, 2026
b51eb88
Bump version to 3.0.0-alpha.0
abrisene Mar 11, 2026
2515c99
Fix P0-P2 correctness bugs and add input validation
abrisene Mar 11, 2026
61a3bbc
Add ISSUES.md tracker and PLAN.md implementation plan
abrisene Mar 11, 2026
8bd457b
Fix remaining P1-P3 issues: validation, docs, rename, optimization
abrisene Mar 11, 2026
bb1edcb
Add DX improvements: backward(), freeze/toMutable, fix stale docs
abrisene Mar 11, 2026
56935f6
API surface improvements for v3.0.0
abrisene Mar 11, 2026
f321c8b
Add statistical validation tests for distributions, blending, and mas…
abrisene Mar 11, 2026
efce879
Bump version to 3.0.0-alpha.1
abrisene Mar 11, 2026
b0ca778
Remove commented-out dead code from tests and production files
abrisene Mar 12, 2026
1aa8823
Extract validateWeights/arithmeticMean helpers, fix bool() consistency
abrisene Mar 12, 2026
8a9c297
Achieve full coverage for foundational services
abrisene Mar 12, 2026
f256bb4
Move ISSUES.md/PLAN.md to gitignored docs/.local, clean up stale TODO
abrisene Mar 12, 2026
2a3fac7
Remove unreachable geometric/harmonic fallback paths in blend.ts
abrisene Mar 12, 2026
c97ad6f
Restructure docs: docs/ for documentation, .site/ for typedoc output
abrisene Mar 12, 2026
6bb6ff2
Add Starlight documentation site
abrisene Mar 12, 2026
5cafbf5
Fix CI: trigger on all version branches and their PRs
abrisene Mar 12, 2026
b7ef359
Fix lint errors and consolidate CI into single job matrix
abrisene Mar 12, 2026
73bb6f2
Merge pull request #41 from abrisene/v3.0.0-fixes
abrisene Mar 12, 2026
76dcaa7
Add comprehensive guides and Mermaid diagrams for all major features
abrisene Mar 12, 2026
b6fccac
Fix Mermaid diagram rendering via Starlight head config
abrisene Mar 12, 2026
236c219
Add interactive playground section with 5 React components
abrisene Mar 12, 2026
f3b2c9f
Polish playground layout: CSS grid controls, tighter spacing, inline …
abrisene Mar 12, 2026
705c074
Fix lint: apply prettier formatting to playground components
abrisene Mar 12, 2026
10762a2
Merge pull request #40 from abrisene/docs/starlight
abrisene Mar 12, 2026
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
26 changes: 26 additions & 0 deletions .github/workflows/ci.yml
Original file line number Diff line number Diff line change
@@ -0,0 +1,26 @@
name: CI

on:
push:
branches: [master, 'v[0-9]+.*']
pull_request:
branches: [master, 'v[0-9]+.*']

jobs:
ci:
runs-on: ubuntu-latest
strategy:
matrix:
node-version: [18, 20, 22]
steps:
- uses: actions/checkout@v4
- uses: pnpm/action-setup@v4
- uses: actions/setup-node@v4
with:
node-version: ${{ matrix.node-version }}
cache: pnpm
- run: pnpm install --frozen-lockfile
- run: pnpm run lint
- run: pnpm run typecheck
- run: pnpm run test:coverage
- run: pnpm run build
44 changes: 44 additions & 0 deletions .github/workflows/docs.yml
Original file line number Diff line number Diff line change
@@ -0,0 +1,44 @@
name: Docs

on:
push:
tags: ['v*']
workflow_dispatch:

permissions:
contents: read
pages: write
id-token: write

concurrency:
group: pages
cancel-in-progress: false

jobs:
build:
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v4
- uses: pnpm/action-setup@v4
- uses: actions/setup-node@v4
with:
node-version-file: .nvmrc
cache: pnpm
- run: pnpm install --frozen-lockfile
- name: Install website dependencies
run: pnpm --dir website install --frozen-lockfile
- name: Build docs site
run: pnpm run docs
- uses: actions/upload-pages-artifact@v3
with:
path: website/dist

deploy:
needs: build
runs-on: ubuntu-latest
environment:
name: github-pages
url: ${{ steps.deployment.outputs.page_url }}
steps:
- id: deployment
uses: actions/deploy-pages@v4
32 changes: 32 additions & 0 deletions .github/workflows/release.yml
Original file line number Diff line number Diff line change
@@ -0,0 +1,32 @@
name: Release

on:
push:
tags: ['v*']

permissions:
contents: write
id-token: write

jobs:
release:
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v4
- uses: pnpm/action-setup@v4
- uses: actions/setup-node@v4
with:
node-version-file: .nvmrc
cache: pnpm
registry-url: https://registry.npmjs.org
- run: pnpm install --frozen-lockfile
- run: pnpm run typecheck
- run: pnpm run lint
- run: pnpm test
- run: pnpm run build
- run: pnpm publish --provenance --no-git-checks
env:
NODE_AUTH_TOKEN: ${{ secrets.NPM_TOKEN }}
- uses: softprops/action-gh-release@v2
with:
generate_release_notes: true
10 changes: 10 additions & 0 deletions .gitignore
Original file line number Diff line number Diff line change
Expand Up @@ -3,6 +3,16 @@
dist/
lib/

# Generated site (typedoc / starlight)
.site/
website/dist/
website/node_modules/
website/.astro/
website/src/content/docs/api/

# Local planning docs
docs/.local/

# Work in Progress
wip/
bak/
Expand Down
3 changes: 3 additions & 0 deletions .npmignore
Original file line number Diff line number Diff line change
@@ -1 +1,4 @@
.github
.site
docs
website
3 changes: 3 additions & 0 deletions .npmrc
Original file line number Diff line number Diff line change
@@ -0,0 +1,3 @@
public-hoist-pattern[]=*eslint*
public-hoist-pattern[]=*prettier*
public-hoist-pattern[]=@typescript-eslint/*
1 change: 1 addition & 0 deletions .nvmrc
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
22
File renamed without changes.
5 changes: 0 additions & 5 deletions .travis.yml

This file was deleted.

44 changes: 43 additions & 1 deletion CHANGELOG.md
Original file line number Diff line number Diff line change
@@ -1,5 +1,47 @@
# Change Log

<a name="3.0.0"></a>
# [3.0.0](https://github.com/abrisene/acausal/compare/v3.0.0...v2.0.1) (2025-11-08)

### Breaking Changes

* **Build Output**: Changed from `lib/` to `dist/` directory
* **Modular Structure**: Markov chain source split into `src/structures/markov/` with separate files for chain, batch, blend, multi-dim, types, utils, defaults
* **Removed `ScaledMarkovChain`**: Needs full rewrite; use `MultiDimMarkovChain` for structured states
* **Removed analysis/export methods**: `extractPatterns()`, `findSimilar()`, `exportAsGraph()`, `diff()`, `toJSON()`, `rankByLikelihood()`, `isAnomaly()`, `withSelector()` removed from `MarkovChain`
* **Removed `IMarkovChain` interface** and `StateId` type
* **`MarkovChainConstructor` is no longer generic** (was `MarkovChainConstructor<T>`)
* **`interpolate()` restricted to same type parameter** (was `<U extends string>` with unsafe cast)
* **`MultiDimMarkovChain` constructor changed**: `stateKey` now accepts `string | StateKeyFunction<T>`; string keys are looked up in a named-function registry
* **Node.js**: Minimum version now 18+ (was 14+)
* **TypeScript**: Upgraded to 5.6.3 with stricter type checking

### Features

* **Generic Types**: `MarkovChain<T extends string = string>`, `Distribution<T extends string = string>`
* **Batch Operations**: `MarkovChainBatch` class for efficient bulk `addSequence`/`addEdge`
* **Chain Blending**: `MarkovChain.blend()` and `interpolate()` with 5 strategies (arithmetic, geometric, harmonic, max, min)
* **Static Dual-API**: `MarkovChain.blendDTOs()`, `MarkovChain.score()`, `MarkovChain.getStats()` operate on DTOs without requiring instances
* **Sequence Scoring**: `score()` returns log probability, perplexity, and validity
* **Constraint-Based Generation**: `MCConstraints` with minLength, maxLength, mustContain, mustNotContain, pattern, validator, maxRetries
* **MultiDimMarkovChain**: Structured state spaces with named-function registry for serializable state keys
* **MultiDimMarkovChain Serialization**: `serialize()` / `fromDTO()` round-trip, `registerStateKey()` / `getStateKey()` for portable state key functions
* **Utility Methods**: `hasGram()`, `getGramsByOrder()`, `getStats()`

### Bug Fixes

* **Gram frequency**: `addEdge` now correctly increments gram frequency
* **`clone(0)`**: `Random.clone(0)` preserves zero use count
* **Constraint retry**: Failed constraint retries return last attempt, not empty array
* **Null safety**: Proper null checks throughout with `noUncheckedIndexedAccess`

### Internal

* Replaced `tsc` with `tsup` for ESM/CJS dual output
* Modern TypeScript 5.6.3 with strict mode
* Vitest with native ESM support
* 95% test coverage threshold enforced

<a name="2.0.1"></a>
## [2.0.1](https://github.com/abrisene/acausal/compare/v2.0.1...v2.0.0) (2021-09-08)
* Added "Analyze" function to Markov Chains.
Expand All @@ -15,7 +57,7 @@
* Full conversion to Typescript.
* Rewrote Markov Chain class.
* Rewrote "Transition Matrix" class, now named "Distribution".
* Wrote "Random" class, which wraps `random-js`.
* Wrote "Random" class, backed by an internal MT19937 PRNG implementation.
* Rewrote Unit Tests.
* Test Coverage at > 99%
* Removed file loading utilities - these should be a separate module, or written ad hoc as needed with implementations.
Expand Down
96 changes: 96 additions & 0 deletions CLAUDE.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,96 @@
# acausal

TypeScript library for weighted random distributions, Markov chains, and seeded PRNG. Used for procedural generation, name generators, game systems, and stochastic modeling.

## Design Philosophy

These are non-negotiable architectural constraints:

- **Static clone-on-write / Mutable instances**: Static methods clone the DTO, mutate the clone, and return the new DTO. Instance methods (e.g. `addSequence`, `add`, `remove`) delegate to statics, reassign internal state, and return `this` for chaining — the instance is mutated in place. Use `.clone()` before mutating if you need to preserve the original. `Immutable*` variants (`ImmutableMarkovChain`, `ImmutableDistribution`, `ImmutableMultiDimMarkovChain`) return new instances from mutating methods for functional patterns and safe sharing. `MarkovChainBatch` remains valuable for avoiding N static-level clones in bulk operations.
- **Dual API**: Every feature has both an instance method and a static method that operates on raw DTOs. Instance methods delegate to statics. Example: `chain.generate(opts)` wraps `MarkovChain.generate({ model: chain.dto, ...opts })`.
- **Portable DTOs**: All classes serialize to/from plain JSON objects (`serialize()` / constructor from DTO). Models can be stored, transferred over the network, and rebuilt without the original training data.
- **Minimal dependencies**: Only `scalr` (weight normalization). The PRNG is an internal MT19937 implementation.

## Architecture

```
src/
index.ts # Barrel: constants, services, structures, types
types.ts # WeightedDistribution (shared base type)
constants/index.ts # MT_PREWARM, delimiters, max order default
services/
mersenne-twister.ts # Internal MT19937 PRNG engine
random.ts # Random class (wraps MT engine)
sampler.ts # RandomSampler (statistical distributions)
structures/
distribution.ts # Distribution<T> + ImmutableDistribution<T>
markov/
markov-chain.ts # MarkovChain<T> core class (mutable instances)
immutable-markov-chain.ts # ImmutableMarkovChain<T> (returns new instances)
batch.ts # MarkovChainBatch<T> (bulk add sequences/edges)
blend.ts # blendMultipleDistributions() (5 strategies)
multi-dim-chain.ts # MultiDimMarkovChain<T> (mutable instances)
immutable-multi-dim-chain.ts # ImmutableMultiDimMarkovChain<T>
utils.ts # Module-level gram dictionary mutation functions
types.ts # All Markov type definitions
defaults.ts # Default options and empty DTOs
index.ts # Controlled barrel export
__tests__/
distribution.spec.ts
markov.spec.ts
random.spec.ts
sampler.spec.ts
```

## Key Patterns

### Gram Structure
An n-gram state with bidirectional transitions. Grams have an `id` (states joined by delimiter `⏐`), `order`, `frequency`, and two `DistributionSourceDTO` objects (`last`, `next`) for backward/forward transitions.

### Delimiters
- Start: `○` (U+25CB)
- Gram separator: `⏐` (U+23F0)
- End: `◍` (U+25CD)

Defined in `constants/index.ts`. These mark sequence boundaries in gram IDs.

### MultiDimMarkovChain Registry
Uses a named-function registry (`registerStateKey` / `getStateKey`) so that state key functions can be serialized by name and looked up at deserialization time. The DTO stores `stateKeyName: string`, not the function itself.

### Blend Strategies
`blend.ts` supports 5 strategies: `arithmetic`, `geometric`, `harmonic`, `max`, `min`. Geometric/harmonic fall back to arithmetic when values are zero (mathematically correct — can't compute geometric mean of zero).

## Build & Test

```bash
pnpm run build # tsup → dist/ (ESM + CJS + .d.ts)
pnpm run typecheck # tsc --noEmit
pnpm test # vitest
pnpm run test:coverage # vitest --coverage (thresholds: 85% branch, 95% lines/funcs/stmts)
pnpm run lint # gts lint
pnpm run docs # typedoc (deployed via CI to GitHub Pages)
```

**Build** uses tsup (see `tsup.config.ts`). Output is ESM + CJS dual format targeting ES2022. The `onSuccess` hook runs `tsc --noEmit` after build.

**Test** uses Vitest with native ESM support. No special Node flags needed.

**CI** runs on GitHub Actions: lint, typecheck, test (Node 18/20/22 matrix), and build. Docs deploy to GitHub Pages on version tags. Releases publish to npm with provenance.

**TypeScript** extends `gts/tsconfig-google.json` with strict mode, `noUncheckedIndexedAccess`, and bundler module resolution.

## Important Implementation Details

- **MT19937 engine** (`mersenne-twister.ts`): Full reference implementation with `seed()`, `seedWithArray()`, `next()`, `discard()`. Helper functions: `integer()` (rejection sampling to avoid modulo bias), `real()` (53-bit resolution using two 32-bit draws), `pick()`, `bool()`, `createEntropy()`.
- **Random class** tracks a `uses` counter that increments with each draw. Serializing `{ seed, uses }` and replaying `discard(uses)` reproduces exact engine state.
- **Distribution** maintains dual representation: raw `source` weights (human-readable) and `normal` weights (probabilities summing to 1.0). Normalization uses `scalr`.
- **MarkovChain generation** uses dynamic order adjustment when `strict: false` — if no gram exists at the target order, it decrements until a match is found.
- **MarkovChainBatch** accumulates sequences/edges, then `commit()` applies them all at once to produce a new `MarkovChain<T>`.
- **RandomSampler** (`sampler.ts`): Statistical distribution sampling built on `Random`. Provides `normal()` (Box-Muller), `truncatedNormal()` (clamped), `exponential()`, `poisson()`, `binomial()`, `geometric()`, `beta()`, `gamma()` (Marsaglia-Tsang), `weibull()`, `cauchy()`, `logistic()`, `logNormal()`. Also provides `sampleDistribution(params)` for data-driven sampling from typed distribution parameter objects (`DistributionParams`). All methods are deterministic given the same seed.

## Common Gotchas

- Don't share `Random` engine instances between cloned chains — use `engine.clone()` to create independent copies.
- `MCAnalyzeOptions` extends `Omit<MCGeneratorOptions, 'constraints'>` — constraints are intentionally excluded from analysis.
- The `GramDictionary` and `MCDelimitersShort` types are internal but exported for advanced use cases. They are not part of the stable public API.
- `scalr` is the only runtime dependency. Do not add new dependencies without strong justification.
1 change: 0 additions & 1 deletion docs/.nojekyll

This file was deleted.

85 changes: 0 additions & 85 deletions docs/assets/highlight.css

This file was deleted.

Loading
Loading