feat: working CLI, vitest 4, modernized tooling, Changesets-driven releases#13
Open
adiled wants to merge 31 commits into
Open
feat: working CLI, vitest 4, modernized tooling, Changesets-driven releases#13adiled wants to merge 31 commits into
adiled wants to merge 31 commits into
Conversation
…T and error prefixing
BREAKING CHANGE: removes the default export from the package entrypoint. The legacy aliases `resample_ohlcv`, `array`, `json`, `trade_to_candle`, and `tick_chart` are gone. Use the named exports `resampleOhlcv`, `resampleTicksByTime`, and `resampleTicksByCount` directly. - `resampleOhlcv` now has overloaded signatures so the return type follows the input shape (`OHLCV[]` in → `OHLCV[]` out, same for `IOHLCV[]`). - Fixed inverted JSDoc on `resampleOhlcvArray`. - Re-export types from the package entrypoint. - Update existing library tests to use named imports.
BREAKING CHANGE: `parseCSV` (CLI helper, not part of the library API)
now returns `{ rows, skipped }` instead of `IOHLCV[]`.
Issue #8's "standardize to OHLCV (array-of-arrays)" bullet is now
addressed.
- `--input-format <csv|json|auto>` now actually honors the requested
format (was previously a no-op heuristic that ignored the flag).
- New `-s, --shape <object|array|auto>` flag for OHLCV tuple vs object
JSON output. Auto-detects shape on JSON input and preserves it
through the pipeline by default.
- `parseCSV` reports skipped rows; the CLI now warns to stderr and
errors out when a CSV produces zero valid rows (was silently exiting
0 with empty output).
- Collapsed triple-nested error wrapping in `readFileData` to a single
`prefixError` helper.
- Extracted `formatCSV`/`formatJSON` writer helpers.
- SIGINT/SIGTERM now use `process.exit()` so they preserve a
previously-set non-zero `exitCode`.
- Dropped redundant `stdoutStream` default in `writeOutput`.
- Tests now check the real `process.exitCode` (the previous
`getExitCode()` helper was a tautology). Added array-of-arrays
shape coverage.
- README CLI section updated for the new flags.
BREAKING CHANGE: `bin.ohlc` now points at `dist/cli.js` (was `src/cli.ts`). The previous binary was non-functional post-install because npm has no way to execute raw TypeScript; this fix changes the resolved path. Combined with the library and CLI breaking changes in the same release, this warrants a major bump. - `bin.ohlc` → `dist/cli.js`; `src/cli.ts` retains its shebang so the emitted JS is directly executable. - Drop unused `chalk` runtime dep; drop `coveralls` devDep. - Move `ts-node` to devDependencies (no longer needed at runtime now that the bin points at compiled JS). - `prebuild` now cleans `dist/` (was incorrectly cleaning `build/`). - `prepublishOnly` simplified: a single test+build pass without the coveralls step that always failed without a token. - Drop obsolete `.npmignore` — superseded by `package.json#files`. - Add `types: dist/index.d.ts` to advertise type declarations. - CI workflow now triggers on `main` (was `master`) and runs Node 20.x and 22.x (was EOL 10.x and 12.x). Switched to pnpm with frozen lockfile, bumped action versions. - Added CHANGELOG.md with the full 2.0.0 breaking-change list.
ed78cd7 to
88317d8
Compare
- Replace jest + ts-jest + @types/jest with vitest + @vitest/coverage-v8.
- New `vitest.config.ts`, drops `jest.config.js` (incl. its ts-jest
`ignoreCodes: [2345]` workaround).
- Add `test:watch` and `test:coverage` scripts. The default `test`
script no longer collects coverage on every run; use `test:coverage`
on demand.
- Add explicit `import { ... } from 'vitest'` at the top of each test
file. `jest.spyOn` → `vi.spyOn` in the one place it appears.
- Fix a pre-existing race in `withTimeout`: a slow `testFn` could log
after the test had torn down ("Cannot log after tests are done").
Tracked via a `settled` flag so post-rejection resolutions are
ignored.
Cold-start test time: ~9s → ~2.5s.
Re-target this PR as a backward-compatible 1.4.0 minor instead of 2.0.
- Restore the legacy `default` export with the `resample_ohlcv`,
`array`, `json`, `trade_to_candle`, `tick_chart` aliases. 1.x
consumers using `import lib from 'ohlc-resample'; lib.tick_chart(...)`
continue to work unchanged.
- Source the CLI's `--version` string from `package.json` at runtime
via `require('../package.json')`, so version bumps stay in one
place (matches the project's manual-bump convention).
- Bump to 1.4.0 in `package.json`.
- Rewrite CHANGELOG entry as 1.4.0; drop the breaking-changes section.
Note: prior commits on this branch carry stale `!` markers and
`BREAKING CHANGE:` trailers from when this was scoped as 2.0. The
final state of the branch is non-breaking; squash-merging the PR is
the cleanest path.
Pushing a `v*` tag now runs build + tests, then `pnpm publish` with npm provenance. Replaces the implicit "publish from a maintainer's laptop" model. One-time setup before the first release: 1. Generate an npm "Automation" granular access token scoped to this package (npmjs.com → Access Tokens). 2. Add it to the repo as the `NPM_TOKEN` secret. Release flow: - Bump `package.json#version`, commit, merge to `main`. - `git tag v1.4.0 && git push --tags`. - The workflow tests, builds, and publishes with provenance attesting the artifact came from this exact commit + workflow run.
Replaces the tag-triggered publish workflow with a Changesets-driven release pipeline. Every PR that affects users carries a markdown file in `.changeset/` describing the change and the semver bump. On merge to `main`, the action either opens a "Version Packages" PR (consuming pending changesets, bumping `package.json#version`, regenerating `CHANGELOG.md`) or, when no changesets are pending, publishes to npm with provenance and creates a matching GitHub Release. This delivers the trio (git tag + GitHub Release + npm publish) as a single consequence of merging the Version Packages PR, and tracks release notes in the file system instead of relying on commit message discipline. Changes - New `release.yml` workflow using `changesets/action@v1` with `contents: write`, `pull-requests: write`, and `id-token: write` (npm provenance). - Drop the prior `publish.yml`. - Add `@changesets/cli` and `@changesets/changelog-github` as devDependencies. Changelog entries are auto-linked to PRs/contributors. - New `release` script: `pnpm publish --no-git-checks --access public --provenance`. - New `changeset` script for ergonomics (`pnpm changeset`). - Reset `CHANGELOG.md` to a stub header so Changesets owns it going forward. - Roll `package.json#version` back to 1.3.0. The Version Packages PR produced after merging this one will propose the bump to 1.4.0 driven by the seed changeset. - Seed `.changeset/working-cli-and-modern-tooling.md` capturing the full body of work in this PR; this becomes the 1.4.0 release note. - README: new "Releasing" section documenting the flow. One-time setup before the first release - Add `NPM_TOKEN` (npm "Granular Access Token", read+write on this package) as a repo secret. - Settings → Actions → General → enable "Allow GitHub Actions to create and approve pull requests".
- README: drop coveralls badge + link (coveralls was removed earlier in this branch); fix master → main in remaining links; replace yarn → pnpm in the test instructions; drop reference to a CONTRIBUTING.md that doesn't exist; point license link to COPYING. - Remove deprecated.md — coveralls is gone and TypeScript is on 5.x, the file's contents no longer reflect reality. - prepublishOnly: switch to pnpm for consistency with the rest of the toolchain.
# Conflicts: # package.json # pnpm-lock.yaml
Make the layered architecture explicit: GitHub is the conveyer belt for our CI, not a dependency of the release model. The Changesets machinery (`.changeset/*.md`, CHANGELOG, version-bump logic, npm publish) is portable to any forge or to no forge at all. Only `.github/workflows/release.yml` is GitHub-specific. Adds two subsections under Releasing: - "Hosting independence" — table of layers and what to swap when moving off GitHub. - "Manual release" — the local laptop flow for when CI is unavailable, the project hasn't been pushed to a forge yet, or you simply prefer hand-driven publishes.
Replace the changesets/action PR-opening behaviour with raw CLI calls that bump, commit-to-main with [skip ci], publish, tag, and create the GitHub Release in a single workflow run from one push to main. Rationale: the release decision is driven entirely by what's in `.changeset/`. The "Version Packages" PR was just plumbing for repos with branch protection on `main`. This branch isn't protected, so collapse the two-PR dance into a single push-and-ship. Behaviour: - Push to main with a `.changeset/*.md` → CI bumps, commits the bump back to main with [skip ci], publishes, tags, creates GH Release. - Push to main without a changeset → CI exits cleanly, nothing released. Doc-only edits and refactors are silent. Order of operations is publish-then-tag so a phantom version on main without a published artifact is avoided. (Re-running after a publish failure is safe — npm rejects duplicate versions.) Permissions trimmed: dropped `pull-requests: write` since we no longer open PRs. Side effects: - Removed the "Allow GitHub Actions to create and approve pull requests" prerequisite from the README — direct-commit flow doesn't need it. - Local `main` will be one commit behind after each release; pull to catch up. - gitignore: AGENTS.md and CLAUDE.md added (local-only agent context).
The two-overload form added in this branch (`OHLCV[]` in → `OHLCV[]`
out, `IOHLCV[]` in → `IOHLCV[]` out) is strictly narrower than the
1.x signature. Callers with code like
function process(data: OHLCV[] | IOHLCV[]) {
return resampleOhlcv(data, opts); // 1.x: OK; 1.4.0-pre: type error
}
would fail to type-check, because TypeScript overload resolution
doesn't match a union argument against the individual overloads.
Add the original union signature as a third overload. The narrower
overloads remain preferred for new code; the union form keeps 1.x
TypeScript callers compiling.
Verified empirically against `origin/main`'s build:
- declarations diff is now strictly additive
- runtime smoke test (each public function + every default export
alias on identical inputs) shows no behavioural drift
Earlier commits on this branch bumped `package.json#version` from 1.2.1 (matches `main`) to 1.3.0 / 1.4.0 by hand. With Changesets adopted, the version is supposed to be a derived value — the workflow runs `pnpm changeset version` post-merge and computes the bump from the pending `.changeset/*.md` files. Reset the working tree's version to match `main` (1.2.1). The seed changeset declares "minor", so the next release will ship as 1.3.0 (previous published was 1.2.1 → minor → 1.3.0). Verified locally: `pnpm changeset version` against this state produces `"version": "1.3.0"` and a clean `## 1.3.0` CHANGELOG entry.
Major-bump worthy because of `engines.node` ≥22.12.0 (was ≥20.12.2). Runtime API surface is unchanged; smoke-tested against `origin/main`'s build with zero behavioural drift on every public function and every default-export alias. Deps - typescript: 5.8 → 6.x. tsconfig modernized: `target: es2022`, `lib: es2022`, explicit `types: ["node"]`, `ignoreDeprecations: "6.0"` for `moduleResolution: node` (will need a node16/nodenext migration before TS 7). - vitest: 2.x → 4.x. Requires Node ≥22.12 because vite 7 + std-env use require-of-ESM, which is only stable from Node 22.12+. - @vitest/coverage-v8 to match. - @types/node: 24 → 25. - rimraf, commander, lodash, @types/lodash, @changesets/changelog-github — all to latest within range. Engines / CI - engines.node: ≥20.12.2 → ≥22.12.0. - nodejs.yml matrix: [20.x, 22.x] → [22.x, 24.x]. - release.yml setup-node: 22.x → 24.x. CLI fix (caught by ad-hoc kicking-the-tires) - `-i` now always wins over stdin, regardless of `isTTY`. The previous `isTTY && options.input` gate broke any non-interactive caller (CI, scripted invocations) where stdin is not a TTY but `-i` is the intended source. Removed the unused `isTTY` parameter from `runCli` and added a regression test. Seed changeset relabelled `minor → major` so the next release ships as 2.0.0, matching the engine bump's semver impact.
The Releasing / Hosting independence / Manual release subsections were maintainer-facing and don't belong in a public-facing README. That content lives in the gitignored AGENTS.md.
Audience expansion: users no longer need Node.js to install or use the CLI. Two parallel distribution channels, both fed by the same release event: 1. npm — for JS/TS projects (existing) 2. install.sh + standalone binary — for everyone else (new) How it works - New `install.sh` at the repo root detects platform (Darwin/Linux, arm64/x64), resolves the latest release tag via GitHub's API, and downloads the matching binary to `$HOME/.local/bin/ohlc`. PATH hint emitted if the install dir isn't on PATH. Flags: `--version` to pin, `--bin-dir` to customize destination. - `release.yml` extended with a parallel `binaries` matrix job that runs only when an actual release fired. For each of darwin-arm64, darwin-x64, linux-x64, linux-arm64, and windows-x64, the matching runner installs Bun, runs `bun build --compile --target=...` against `src/cli.ts`, and uploads the resulting self-contained binary as a release asset. Each binary is ~60MB (Bun runtime is bundled). - README's Install section now leads with the curl-pipe-sh path; the npm path is listed below for library consumers. Locally verified - `bun build --compile --target=bun-darwin-arm64 src/cli.ts` produces a working 60MB binary that responds to `--version`, `--help`, pipe input, file input, all output shapes, and reads its version from the bundled package.json (single source of truth preserved). - `install.sh` syntax-checks clean (`bash -n`) and `--help` renders the documented usage.
Constraint clarified: Node is the runtime everywhere. No third-party JS
runtimes (Bun, Deno, …) at dev, in CI, in shipped artifacts, or in
installer-provided runtimes.
Distribution model
- Single source of truth: the npm package.
- `install.sh` runs client-side. If the host has Node ≥22.12, it uses
it. Otherwise it downloads the official Node binary distribution
from nodejs.org into `${INSTALL_DIR}/runtime/`, then `npm install`s
the package into `${INSTALL_DIR}/lib/` and writes a `${BIN_DIR}/ohlc`
launcher that exec's `<chosen-node> <installed-cli.js> "$@"`.
- No per-platform binaries are built or published.
Reverts
- The `binaries` matrix job in release.yml (was building per-platform
binaries via `bun build --compile`).
- The README copy that advertised "no Node.js required."
Bug fixed in install.sh during this rewrite
- The bundled `npm` script's `#!/usr/bin/env node` shebang failed when
`node` wasn't on `PATH`. Fix: prepend the chosen Node's bin dir to
`PATH` for the install call.
Verified end-to-end
- Host-Node path: install.sh detected my host Node 22.17.1, npm-installed
the package, launcher resolves to host Node.
- BYO-Node path: with `PATH=/usr/bin:/bin` (no Node on PATH), install.sh
downloaded Node 22.12.0 from nodejs.org, npm-installed the package
using the bundled Node, launcher exec's the bundled Node correctly.
AGENTS.md (gitignored) now documents this constraint up top so future
agents don't reach for Bun (or Deno, or anything else) to solve a
distribution problem.
Same script, two modes. Uninstall removes the launcher (`$BIN_DIR/ohlc`) and the install dir (`$INSTALL_DIR`, runtime + lib). Both removals are gated by sanity checks: the launcher must reference our package's `cli.js`; the install dir must contain `lib/node_modules/$PKG` or a `runtime/` we wrote. Pass `--force` to override the gate. Verified locally: - Real install + --uninstall round-trip cleanly removes everything. - A planted foreign launcher and unrelated dir are refused without --force; --force removes them. - Re-running --uninstall on an empty target is a clean no-op. README updated with the uninstall one-liner under the Install section. AGENTS.md notes the safety gate.
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
Closes #8.
Ships as 2.0.0 (1.2.1 → major). The major bump is driven by
engines.node ≥22.12.0for npm consumers — the runtime API itself is backward-compatible (smoke-verified). For non-Node users, a newinstall.shdistribution path means most people can install + use the CLI without ever touching npm or having Node pre-installed.Distribution
curl -fsSL .../install.sh | sh— the installer uses the host's Node ≥22.12 if present; otherwise it downloads the official Node binary from nodejs.org, installs the package into~/.ohlc/lib, and writes a~/.local/bin/ohlclauncher. No per-platform binaries published — the npm tarball is the single release artifact, the installer does the platform work client-side.npm install ohlc-resample— for JS/TS projects that want the library API. Requires Node ≥22.12.Breaking change
engines.node≥22.12.0 (was ≥20.12.2). Required by vite 7 / vitest 4 (require-of-ESM only stable from Node 22.12+). Runtime library API on supported Node versions is unchanged.CLI (was a non-functional stub in 1.x; now ships)
--input-format <csv|json|auto>honors the requested format (was a no-op heuristic).-s, --shape <object|array|auto>for OHLCV tuple vs object JSON output. Auto-detects shape on JSON input and preserves it through the pipeline by default.-ialways wins over stdin, even in non-TTY contexts (CI, scripts) — caught while ad-hoc kicking the tires.bin.ohlc→dist/cli.js(wassrc/cli.ts, non-functional post-install).Library (BC preserved)
resampleOhlcvhas overloaded signatures: return type follows input shape (OHLCV[]in →OHLCV[]out, same forIOHLCV[]). Original union signature kept as a third overload, so 1.x TS callers with union-typed data still compile.resampleOhlcvArray.defaultexport withresample_ohlcv/array/json/trade_to_candle/tick_chartaliases preserved unchanged.origin/mainis strictly additive; runtime smoke test on every public function and every default-export alias shows zero behavioural drift.Build & tooling
target: es2022,lib: es2022,types: ["node"]).mainwith a changeset triggers bump + commit + npm publish (with provenance) + tag + GitHub Release in a single CI run.chalk,coveralls,fast-csvdeps; movedts-nodeto devDependencies.main, runs Node 22.x and 24.x..npmignore, dropped staledeprecated.md, cleaned coveralls/yarn/master references from README.Release flow after this PR
main).pnpm changeset version, commits the bump (1.2.1 → 2.0.0) back tomainwith[skip ci], then publishes 2.0.0 to npm with provenance, tagsv2.0.0, creates the GitHub Release.One-time setup before merging
NPM_TOKEN(npm "Granular Access Token", read+write on this package) as a repo secret on GitHub.Test status
origin/main's build (zero drift).install.shverified end-to-end on both host-Node and BYO-Node paths.