# Cycle.js Ships Eight Scoped Packages From One Private Monorepo

> Cycle.js is a functional and reactive JavaScript framework written in TypeScript and split across eight scoped npm packages in a single workspace, with a release history that stopped in 2017 while the branch kept moving into 2026.

**cyclejs/cyclejs** — A functional and reactive JavaScript framework for predictable code

- Repository: https://github.com/cyclejs/cyclejs
- Website: http://cycle.js.org
- Stars: 10,225 · Forks: 423
- Language: TypeScript
- License: MIT
- Published: 2026-09-21 · Updated: 2026-09-21 · Language: en
- Canonical page: https://hysenlabs.com/projects/cyclejs-cyclejs

## A directory per package, and two directories the table never lists

Cycle.js is not one npm package. The repository holds a directory per package and the npm name mirrors the directory, so @cycle/run lives in run/. The README table names eight of them: @cycle/dom, @cycle/history, @cycle/html, @cycle/http, @cycle/isolate, @cycle/most-run, @cycle/run, and @cycle/rxjs-run, each with its own changelog link, dependency page, and dev dependency page. The root manifest is marked private and pnpm-workspace.yaml with pnpm-lock.yaml sits beside it, so the repository publishes nothing as a single artifact. What that costs a reader is the missing umbrella install: you pick which @cycle scopes you need and wire them yourself. The tree also carries state/ and time/ directories that the package table never lists, so a folder in the checkout is not evidence of a published package.

## The last release tag is 2017 while the branch was pushed in 2026

Release history and commit history have drifted apart by years. The three most recent releases are unified-tag labeled Cycle Unified from 2017-02-09, v7.0.0 labeled Cycle Diversity from 2016-06-27, and v6.0.0 labeled Cycle Nested from 2015-12-21, and the last push to the default branch is dated 2026-06-09. The repository is not archived, yet no versioned release after 2017 appears in the listing. A commit on master is therefore not the same artifact as the code npm hands you, and a project pinning @cycle/run receives the published version rather than the head of the branch. The consequence for anyone judging maintenance is that branch activity and release activity answer different questions, and only the push date tells you how recent the source is.

## TypeScript is pinned exactly while the engine floor says node 8

The build toolchain is held far more tightly than the runtime requirement. devDependencies list typescript at exactly 3.2.4, written with an equals sign so no other release satisfies it, while prettier sits at ^1.15.3, tslint at ^5.11.0, rxjs at ^6.3.3, lint-staged at ^8.1.0, and husky at ^3.0.2. The engines field asks for nothing beyond node >=8, which is the lowest floor npm accepts. That gap has a direct effect on a current machine: the compiler resolves to one exact release and the formatter and linter to their older major lines, so contributor tooling runs against that era of code even when the host Node is far newer, and a type problem a current compiler would reject is not something these checks were in a position to catch.

## Build, test, and docs all fan out through pnpm recursive

There is no single build entry point. The scripts fan out across the workspace:

```sh
pnpm recursive run build
pnpm recursive test
```

Lint is pnpm recursive run lint and the continuous integration variant is pnpm recursive run test-ci. Documentation is a four stage chain of pnpm run docs-index, docs-documentation, docs-releases, and docs-api, where the first three are node scripts under docs/.scripts and the last one runs make-api-index.js before recursing into each package for its own docs step. The consequence is that the documentation is a build artifact assembled from per package output, so a fork that renames or removes a package directory has to keep that recursion intact before the docs regenerate at all. Changelogs come out of the same recursive pattern, one package at a time, and release work has its own scripts: check-release runs a script under .scripts, release-all runs release-whatever-needs-release.sh, and release is a shell function wrapping .scripts/release.sh. Because each package is versioned on its own, a fork that wants one coordinated version has to drive that machinery rather than edit a version field in a single manifest.

## Your flow runs on most, rxjs, or xstream, and none of them live here

A Cycle.js program runs on a stream runtime you choose, and the choice is a separate package. The README keeps most, rxjs, and xstream in their own table and says plainly that they are not under Cycle.js but are important dependencies, shown there for convenience. Two runners in the repository connect them: @cycle/most-run and @cycle/rxjs-run. That split is the framework's portability mechanism and also its runtime coupling, because the streams carrying every value in your program come from a package maintained outside this tree. Production behavior depends on which version your lockfile resolves, and moving between runtimes means a different import and a different set of external semantics rather than a configuration flag. The repository also keeps state/ and time/ directories beside the packages, and the README shows dependency and dev dependency pages per package rather than one resolved graph, so the exact stream version behind a flow is something you confirm in a lockfile, not something the framework declares for you.

## The browser test path runs through BrowserStack, not your machine

The browser coverage is a hosted one. karma.conf.js and browserstack-karma.js sit at the top of the repository and .travis.yml carries the continuous integration configuration, while the scripts keep a plain test and a separate CI test as different commands. That split means the run you trigger locally and the run that gates a merge are not the same thing over the same browsers, and reproducing the CI run for a DOM package depends on the settings in browserstack-karma.js plus whatever credentials feed it. The README does not document how to point those tests at a local browser, so a contributor without access to that grid has no documented offline substitute for the same coverage, and a change to @cycle/dom cannot be validated the same way on a laptop.

## Pre-commit hooks rewrite your files instead of only checking them

Every staged change passes through tooling before it lands. husky wires a pre-commit hook to lint-staged, and lint-staged modifies staged files rather than only reporting on them: TypeScript and TSX files are formatted, linted against tsconfig.lint.json, and restaged, using

```sh
prettier --write
tslint --project tsconfig.lint.json
git add
```

Staged JavaScript files get the prettier pass and are restaged too, and the format script widens that same glob to src and test directories for ts, tsx, and js files across the workspace. Style is decided for you as well: singleQuote true, trailingComma es5, and bracketSpacing false, with a .prettierignore at the root for the exceptions. Commit messages run through commitizen and git-cz with conventional-changelog and a .cz-config.js on top, which is also the shape the recursive changelog step later parses. For anyone cloning the repository this means the first commit changes files you wrote, and a Node setup that cannot run the pinned tooling stops the commit instead of landing it.

## Discussion issues are labeled and closed on arrival

The project routes questions away from its own tracker by rule. The welcome table sends questions to a StackOverflow tag named cyclejs, to the Gitter chat room, or to a new issue, and then states that all discussion-like issues are labeled discussion and immediately closed, with only actual issues left open. Bugs go straight to a new issue. Contributors are pointed at CONTRIBUTING.md and at issues carrying a help wanted label, and funding runs through OpenCollective sponsors. The consequence for a reader with a question is that filing it as an issue gets it closed by design, which leaves a question tag and a chat room as the only help surfaces named in the README.

## Conclusion

Use Cycle.js if you want streams as the center of a frontend and you are willing to depend on individual @cycle packages plus an external stream library. Before committing, check the published version of each @cycle package against your build tooling, since the toolchain pins typescript 3.2.4 and node 8, and decide your runner early because most, rxjs, and xstream are third party. Skip it if you need a framework whose latest npm release tracks its repository, or if your team cannot support the pinned toolchain.

## FAQ

### What is js mostly used for?

Inside the Cycle.js repository, JavaScript carries the tooling while the libraries are TypeScript: the root scripts run under node, with karma.conf.js, browserstack-karma.js, and the docs scripts under docs/.scripts, and the primary language of the project is TypeScript.

### Is JavaScript still relevant in 2026?

Cycle.js is still built and published as npm packages under the @cycle scope, with eight of them listed in the README. The toolchain behind that work pins typescript at 3.2.4 and declares node >=8 as the engine floor.

### Is JavaScript just C++?

Cycle.js itself contains no native sources in its repository listing. It is TypeScript compiled for JavaScript consumers, with each package in its own directory and the build driven by pnpm recursive scripts.

### Is js like Python?

Cycle.js is organized around stream libraries, most, rxjs, and xstream, all maintained outside this repository, with @cycle/most-run and @cycle/rxjs-run choosing which runtime a flow uses. No Python implementation appears in the repository listing.

## Sources

- [cyclejs/cyclejs on GitHub](https://github.com/cyclejs/cyclejs)
- [License: MIT](https://github.com/cyclejs/cyclejs/blob/master/LICENSE)
- [Project website](http://cycle.js.org)
- [README](https://github.com/cyclejs/cyclejs/blob/master/README.md)
- [Releases](https://github.com/cyclejs/cyclejs/releases)

---

Hysen Labs editorial analysis, written from the project's own repository and release notes. Cite the canonical page: https://hysenlabs.com/projects/cyclejs-cyclejs
