Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
42 commits
Select commit Hold shift + click to select a range
6d5ff71
docs: design spec for production-grade concerns
danfry1 May 22, 2026
2e87b97
docs: implementation plan for production-grade concerns
danfry1 May 22, 2026
3d0d7d3
docs: lead with 'incremental' in tagline
danfry1 May 22, 2026
40b4c58
docs: add 'How faultline differs' section
danfry1 May 22, 2026
f92712f
docs: document explicit error-code escape hatch
danfry1 May 22, 2026
3074895
test: cover explicit-code escape hatch in defineErrors
danfry1 May 22, 2026
0efda35
test: assert explicit-code literal type preservation
danfry1 May 22, 2026
bc051f2
test: failing cancellation propagation tests for TaskResult
danfry1 May 22, 2026
516b4ee
feat(task-result): propagate cancellation through chain steps
danfry1 May 22, 2026
9ac0eb9
test: assert TaskResult chain widens error union with System.Cancelled
danfry1 May 22, 2026
18502a0
chore(task-result): document mapErr+Cancelled, widen andThenTask type…
danfry1 May 22, 2026
54c1179
refactor(task-result): remove toPromise alias (use run)
danfry1 May 22, 2026
6d8fc47
fix(task-result): fromPromise catches rejections as System.Unexpected
danfry1 May 22, 2026
a17427a
test(task-result): cover non-Error throws through fromPromise
danfry1 May 22, 2026
a6f7727
docs: explain when to use TaskResult vs Promise<Result>
danfry1 May 22, 2026
dc6cd92
docs: changelog entries for Phase 3 task-result changes
danfry1 May 22, 2026
6850549
test(eslint-plugin): scaffold torture fixture directory
danfry1 May 22, 2026
c13543b
test(eslint-plugin): scaffold torture harness
danfry1 May 22, 2026
58afe8a
test(eslint-plugin): baseline fixture (no-report negative controls)
danfry1 May 22, 2026
99105aa
chore(eslint-plugin): polish torture harness ergonomics
danfry1 May 22, 2026
8952909
test(eslint-plugin): re-throws torture fixture
danfry1 May 22, 2026
cb1fa44
test(eslint-plugin): generics torture fixture (triage candidates)
danfry1 May 22, 2026
d4bf96a
test(eslint-plugin): third-party throws torture fixture
danfry1 May 22, 2026
da37197
test(eslint-plugin): nested-try torture fixture
danfry1 May 22, 2026
ab1eefa
test(eslint-plugin): await-shapes torture fixture
danfry1 May 22, 2026
c2b9a77
test(eslint-plugin): overloads torture fixture (triage candidate)
danfry1 May 22, 2026
ccb8980
test(eslint-plugin): re-exports, class-methods, boundaries torture fi…
danfry1 May 22, 2026
0866b05
docs: ESLint torture triage findings
danfry1 May 22, 2026
236e3a5
test(eslint-plugin): align failing fixtures to design constraint
danfry1 May 22, 2026
8284a7d
fix(eslint-plugin): inspect all overload signatures, not just first
danfry1 May 22, 2026
0c3e722
fix(eslint-plugin): report throw of untyped variable in TypedPromise …
danfry1 May 22, 2026
3e29d55
fix(eslint-plugin): don't descend into async closures when collecting…
danfry1 May 22, 2026
d13a54b
docs(eslint-plugin): publish known limitations
danfry1 May 22, 2026
8807a49
docs: changelog entries for Phase 4 ESLint improvements
danfry1 May 22, 2026
e1fe7d1
feat(cli): make faultline-cli npm-publishable
danfry1 Jun 13, 2026
9725c09
fix(eslint-plugin): publish readiness
danfry1 Jun 13, 2026
b1b495e
test(core): cover Result methods, attempt, fromUnknown; add coverage …
danfry1 Jun 13, 2026
86492a6
build: adopt Changesets for versioning and releases
danfry1 Jun 13, 2026
40485ad
ci: add Node 18/20/22 compat matrix and build all publishable packages
danfry1 Jun 13, 2026
cbe987c
docs: add repo hygiene and README trust signals
danfry1 Jun 13, 2026
25d2ac4
fix(core): redaction globstar + clarify cancellation/boundary footguns
danfry1 Jun 13, 2026
9fdcc42
feat(core): non-throwing boundary.try() + raise task-result coverage …
danfry1 Jun 13, 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
8 changes: 8 additions & 0 deletions .changeset/README.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,8 @@
# Changesets

Hello and welcome! This folder has been automatically generated by `@changesets/cli`, a build tool that works
with multi-package repos, or single-package repos to help you version and publish your code. You can
find the full documentation for it [in our repository](https://github.com/changesets/changesets).

We have a quick list of common questions to get you started engaging with this project in
[our documentation](https://github.com/changesets/changesets/blob/main/docs/common-questions.md).
5 changes: 5 additions & 0 deletions .changeset/boundary-try.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,5 @@
---
"faultline": minor
---

Add `boundary.try(error)` — a non-throwing variant of a boundary that returns `Result<To, System.BoundaryViolation>` instead of throwing on an unmapped tag. Boundaries usually sit at a transport edge where an uncaught throw is costly; `.try()` lets you handle an unexpected (untyped/foreign) error tag as a value. Exposes the `BoundaryViolationError` type.
5 changes: 5 additions & 0 deletions .changeset/cli-node-publishable.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,5 @@
---
"faultline-cli": minor
---

The CLI is now publishable to npm and runs on plain Node.js (>=18). The `bin` points at a built `dist/index.js` with a Node shebang (previously a raw `.ts` file with a `bun` shebang), `typescript` is declared as a runtime dependency, and the package ships a `files` allowlist, README, LICENSE, and full repository metadata.
11 changes: 11 additions & 0 deletions .changeset/config.json
Original file line number Diff line number Diff line change
@@ -0,0 +1,11 @@
{
"$schema": "https://unpkg.com/@changesets/config@3.1.4/schema.json",
"changelog": "@changesets/cli/changelog",
"commit": false,
"fixed": [],
"linked": [],
"access": "public",
"baseBranch": "main",
"updateInternalDependencies": "patch",
"ignore": ["faultline-vscode"]
}
16 changes: 16 additions & 0 deletions .changeset/eslint-plugin-publish-fixes.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,16 @@
---
"eslint-plugin-faultline": minor
---

Rule improvements and publish-readiness fixes.

Rules:
- `throw-type-mismatch` now inspects all overload signatures, not just the first — overloaded functions are fully checked for transitive error propagation.
- `throw-type-mismatch` reports `throw <identifier>` when the surrounding function declares a `TypedPromise` error type and the thrown variable's type has no resolvable `_tag`.
- `uncovered-catch` no longer misattributes throws inside async closures (e.g. `setTimeout(async () => ...)`) to the outer try's catch.
- Published `LIMITATIONS.md` documenting patterns the rules can't detect, plus an 11-pattern torture-fixture corpus guarding the rules.

Packaging:
- Ship the previously-missing `README.md` (it was already listed in `files`).
- Derive `meta.version` from `package.json` at build time so it can no longer drift from the published version (was hardcoded `0.1.0`).
- Run tests as part of `prepublishOnly`.
7 changes: 7 additions & 0 deletions .changeset/redaction-globstar.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,7 @@
---
"faultline": minor
---

Redaction: support a `**` globstar segment in `redactPaths` to redact a key at any depth (e.g. `**.password`). Redaction paths are matched against the serialized error structure and a non-matching path is a silent no-op (it fails open), so `**.<key>` is now the recommended form for sensitive keys — it can't silently miss a nested occurrence the way a brittle exact path can. Documented this behavior in the README and SECURITY.md.

Also clarified (docs only, no behavior change): `attemptAsync`/`TaskResult` cancellation is cooperative — aborting short-circuits *subsequent* steps and stops the caller waiting, but does not preempt in-flight work unless your function threads the `AbortSignal` through. And `defineBoundary` throws `System.BoundaryViolation` (preserving the original as cause) when an unmapped error tag reaches it.
13 changes: 13 additions & 0 deletions .changeset/taskresult-cancellation.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,13 @@
---
"faultline": minor
---

TaskResult cancellation, rejection safety, and API pruning.

Breaking changes:
- `TaskResult.toPromise()` removed. Use `.run()` instead — same signature.
- TaskResult chain methods (`.map`, `.andThen`, `.catchTag`, etc.) now widen their error union to include `System.Cancelled`. Exhaustive `match()` over a TaskResult needs a `'System.Cancelled'` handler or a `_` branch.
- `TaskResult.fromPromise` return type now widens to include `System.Unexpected`. Factory rejections are no longer unhandled — they are caught and wrapped as `System.Unexpected` with the original throw preserved as `.cause`.

Added:
- TaskResult chain steps check `context.signal?.aborted` between every step. Aborting the signal mid-pipeline short-circuits the remaining steps to `System.Cancelled` instead of running them to completion.
54 changes: 54 additions & 0 deletions .github/ISSUE_TEMPLATE/bug_report.yml
Original file line number Diff line number Diff line change
@@ -0,0 +1,54 @@
name: Bug report
description: Report a bug in a faultline package
labels: [bug]
body:
- type: dropdown
id: package
attributes:
label: Package
description: Which package is affected?
options:
- faultline
- eslint-plugin-faultline
- faultline-cli
- faultline-vscode
- not sure
validations:
required: true
- type: input
id: version
attributes:
label: Package version
placeholder: e.g. 0.2.0
validations:
required: true
- type: textarea
id: what-happened
attributes:
label: What happened?
description: A clear description of the bug and what you expected instead.
validations:
required: true
- type: textarea
id: repro
attributes:
label: Minimal reproduction
description: The smallest code snippet (or repo link) that reproduces the issue. For ESLint rules, include the source and your ESLint config.
render: ts
validations:
required: true
- type: input
id: env
attributes:
label: Environment
description: Node/Bun version, OS, TypeScript version, ESLint version (if relevant).
placeholder: e.g. Node 20.11, macOS 14, TypeScript 5.9
validations:
required: false
- type: checkboxes
id: checks
attributes:
label: Checks
options:
- label: For an ESLint false negative/positive, I checked LIMITATIONS.md first.
required: false
8 changes: 8 additions & 0 deletions .github/ISSUE_TEMPLATE/config.yml
Original file line number Diff line number Diff line change
@@ -0,0 +1,8 @@
blank_issues_enabled: false
contact_links:
- name: Security vulnerability
url: https://github.com/danfry1/faultline/security/advisories/new
about: Please report security issues privately, not as public issues. See SECURITY.md.
- name: Question / discussion
url: https://github.com/danfry1/faultline/discussions
about: Ask questions and discuss ideas here.
25 changes: 25 additions & 0 deletions .github/ISSUE_TEMPLATE/feature_request.yml
Original file line number Diff line number Diff line change
@@ -0,0 +1,25 @@
name: Feature request
description: Suggest an idea or improvement
labels: [enhancement]
body:
- type: textarea
id: problem
attributes:
label: Problem
description: What problem are you trying to solve? What's the use case?
validations:
required: true
- type: textarea
id: proposal
attributes:
label: Proposed solution
description: What would you like to happen? API sketches welcome.
validations:
required: true
- type: textarea
id: alternatives
attributes:
label: Alternatives considered
description: Other approaches you've thought about, including existing workarounds.
validations:
required: false
27 changes: 27 additions & 0 deletions .github/pull_request_template.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,27 @@
<!-- Thanks for contributing to faultline! -->

## Summary

<!-- What does this PR change and why? -->

## Type of change

- [ ] Bug fix
- [ ] New feature
- [ ] Breaking change
- [ ] Docs / internal only (no changeset needed)

## Affected packages

- [ ] `faultline`
- [ ] `eslint-plugin-faultline`
- [ ] `faultline-cli`
- [ ] `faultline-vscode`

## Checklist

- [ ] `bun run typecheck` passes
- [ ] `bun run test` passes (new code paths are covered)
- [ ] `bun run lint` passes
- [ ] Added a changeset (`bun run changeset`) for user-facing changes
- [ ] Updated docs / `LIMITATIONS.md` / type assertions where relevant
32 changes: 30 additions & 2 deletions .github/workflows/ci.yml
Original file line number Diff line number Diff line change
Expand Up @@ -15,5 +15,33 @@ jobs:
- run: bun install --frozen-lockfile
- run: bun run lint
- run: bun run typecheck
- run: bun run test
- run: bun --filter 'faultline' build
- run: bun run test # enforces the coverage gate in packages/faultline/bunfig.toml
- name: Build publishable packages
run: bun --filter 'faultline' --filter 'eslint-plugin-faultline' --filter 'faultline-cli' build
- name: Validate faultline package exports
working-directory: packages/faultline
run: npm pack && npx @arethetypeswrong/cli ./faultline-*.tgz --ignore-rules no-resolution

node-compat:
needs: quality
runs-on: ubuntu-latest
strategy:
fail-fast: false
matrix:
node-version: [18, 20, 22]
steps:
- uses: actions/checkout@v4
- uses: oven-sh/setup-bun@v2
- run: bun install --frozen-lockfile
- name: Build publishable packages
run: bun --filter 'faultline' --filter 'eslint-plugin-faultline' --filter 'faultline-cli' build
- uses: actions/setup-node@v4
with:
node-version: ${{ matrix.node-version }}
- name: Smoke-test built artifacts on Node ${{ matrix.node-version }}
run: |
set -euo pipefail
node -e "const f=require('./packages/faultline/dist/index.cjs'); if(typeof f.defineErrors!=='function') throw new Error('CJS load failed'); console.log('faultline CJS ok')"
node --input-type=module -e "const f=await import('./packages/faultline/dist/index.js'); if(typeof f.defineErrors!=='function') throw new Error('ESM load failed'); console.log('faultline ESM ok')"
node -e "const p=require('./packages/eslint-plugin-faultline/dist/index.js'); if(!p.rules||!p.rules['no-raw-throw']) throw new Error('plugin load failed'); console.log('eslint-plugin ok')"
node packages/faultline-cli/dist/index.js catalog test/fixtures/sample-app | grep -q 'User.NotFound' && echo 'faultline-cli ok'
62 changes: 22 additions & 40 deletions .github/workflows/release.yml
Original file line number Diff line number Diff line change
@@ -1,16 +1,21 @@
name: Release

# Changesets flow:
# - A push to main with pending changesets opens (or updates) a "Version Packages" PR.
# - Merging that PR bumps versions + changelogs, then this workflow publishes to npm.
on:
push:
tags: ['v*', 'eslint-plugin-v*']
branches: [main]

concurrency: ${{ github.workflow }}-${{ github.ref }}

permissions:
contents: write
pull-requests: write
id-token: write

jobs:
publish-core:
if: startsWith(github.ref, 'refs/tags/v') && !startsWith(github.ref, 'refs/tags/eslint-plugin-v')
release:
runs-on: ubuntu-latest
environment: npm
steps:
Expand All @@ -24,43 +29,20 @@ jobs:
- run: bun install --frozen-lockfile
- run: bun run typecheck
- run: bun run test
- name: Build
run: bun --filter 'faultline' build
- name: Validate package exports
working-directory: packages/faultline
run: npm pack && npx @arethetypeswrong/cli ./faultline-*.tgz --ignore-rules no-resolution
- name: Publish to npm
working-directory: packages/faultline
run: npm publish --provenance --access public
env:
NODE_AUTH_TOKEN: ${{ secrets.NPM_TOKEN }}
- name: Create GitHub Release
uses: softprops/action-gh-release@v2
- name: Validate package exports (faultline)
run: |
bun --filter 'faultline' build
cd packages/faultline
npm pack
npx @arethetypeswrong/cli ./faultline-*.tgz --ignore-rules no-resolution
- name: Create Release PR or Publish
uses: changesets/action@v1
with:
generate_release_notes: true

publish-eslint-plugin:
if: startsWith(github.ref, 'refs/tags/eslint-plugin-v')
runs-on: ubuntu-latest
environment: npm
steps:
- uses: actions/checkout@v4
- uses: oven-sh/setup-bun@v2
- uses: actions/setup-node@v4
with:
node-version: 22
registry-url: https://registry.npmjs.org
- run: npm i -g npm@latest
- run: bun install --frozen-lockfile
- run: bun run typecheck
- name: Build
run: bun --filter 'eslint-plugin-faultline' build
- name: Publish to npm
working-directory: packages/eslint-plugin-faultline
run: npm publish --provenance --access public
version: bun run version-packages
publish: bun run release
commit: 'chore: version packages'
title: 'chore: version packages'
env:
GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}
NODE_AUTH_TOKEN: ${{ secrets.NPM_TOKEN }}
- name: Create GitHub Release
uses: softprops/action-gh-release@v2
with:
generate_release_notes: true
NPM_CONFIG_PROVENANCE: 'true'
1 change: 0 additions & 1 deletion .gitignore
Original file line number Diff line number Diff line change
Expand Up @@ -2,5 +2,4 @@ node_modules
dist
out
*.tsbuildinfo
bun.lock
.claude/
4 changes: 4 additions & 0 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
@@ -1,5 +1,9 @@
# Changelog

> Versioning and per-package changelogs are managed with [Changesets](https://github.com/changesets/changesets).
> Each published package keeps its own `CHANGELOG.md` (`packages/*/CHANGELOG.md`), generated at release time.
> Unreleased changes live as files under [`.changeset/`](./.changeset). This file retains the pre-Changesets history.

## 0.1.0

Initial release.
Expand Down
41 changes: 41 additions & 0 deletions CODE_OF_CONDUCT.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,41 @@
# Contributor Covenant Code of Conduct

## Our Pledge

We as members, contributors, and leaders pledge to make participation in our community a harassment-free experience for everyone, regardless of age, body size, visible or invisible disability, ethnicity, sex characteristics, gender identity and expression, level of experience, education, socio-economic status, nationality, personal appearance, race, religion, or sexual identity and orientation.

We pledge to act and interact in ways that contribute to an open, welcoming, diverse, inclusive, and healthy community.

## Our Standards

Examples of behavior that contributes to a positive environment:

- Demonstrating empathy and kindness toward other people
- Being respectful of differing opinions, viewpoints, and experiences
- Giving and gracefully accepting constructive feedback
- Accepting responsibility and apologizing to those affected by our mistakes
- Focusing on what is best for the overall community

Examples of unacceptable behavior:

- The use of sexualized language or imagery, and sexual attention or advances of any kind
- Trolling, insulting or derogatory comments, and personal or political attacks
- Public or private harassment
- Publishing others' private information without their explicit permission
- Other conduct which could reasonably be considered inappropriate in a professional setting

## Enforcement Responsibilities

Community leaders are responsible for clarifying and enforcing our standards and will take appropriate and fair corrective action in response to any behavior that they deem inappropriate, threatening, offensive, or harmful.

## Scope

This Code of Conduct applies within all community spaces, and also applies when an individual is officially representing the community in public spaces.

## Enforcement

Instances of abusive, harassing, or otherwise unacceptable behavior may be reported to the maintainers responsible for enforcement via the contact on the maintainer's GitHub profile. All complaints will be reviewed and investigated promptly and fairly.

## Attribution

This Code of Conduct is adapted from the [Contributor Covenant](https://www.contributor-covenant.org), version 2.1, available at https://www.contributor-covenant.org/version/2/1/code_of_conduct.html.
Loading
Loading