Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
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
5 changes: 5 additions & 0 deletions .changeset/calm-machines-organize.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,5 @@
---
"@typeonce/effect-machine": patch
---

Organize public, internal, testing, and unstable modules into Effect-shaped directories without changing package entrypoints. Add a TypeScript-resolved architecture check that enforces dependency direction, test boundaries, acyclic runtime imports, and internal naming conventions.
52 changes: 52 additions & 0 deletions CONTRIBUTING.md
Original file line number Diff line number Diff line change
Expand Up @@ -3,3 +3,55 @@
This project generally does not accept unsolicited pull requests. Open an issue first describing the problem or use case and, for API changes, the public API you want to add or change.

Wait for the proposal to be discussed and accepted before starting an implementation or opening a pull request. Pull requests without prior agreement may be closed.

## Repository architecture

The source tree follows Effect's public-module/internal-implementation split:

```text
src/
├── Machine.ts
├── index.ts
├── testing/
├── unstable/
└── internal/
├── machine/
└── testing/machine/
```

Public entrypoints and public modules use Effect-style names. Private files sit
under the domain they implement and use responsibility names such as
`planner.ts`, `process.ts`, and `runtime.ts`; they do not repeat `machine` in
every filename. Runtime tests mirror the same domains. Tests below
`test/internal/` are the only white-box suites allowed to import `src/internal`.

The core dependency direction is:

```text
public entrypoint -> public module -> process -> planner
| |
v v
runtime model
| |
└-> errors <-┘
```

Internal machine modules may refer back to the public `Machine` types through
type-only imports. The runtime is intentionally unaware of the model, planner,
and process layers. Testing implementations are isolated under
`src/internal/testing` and may only be consumed by the public testing module or
other testing internals.

`pnpm check:architecture` builds a TypeScript dependency graph using the
project's NodeNext resolver. It distinguishes type-only and runtime edges,
understands imports, re-exports, and dynamic imports, and enforces:

- public entrypoints do not leak internals;
- internal back-edges and layer dependencies keep their intended direction;
- production code does not depend on testing internals;
- black-box tests do not depend on implementation internals;
- production runtime imports are acyclic;
- private directories have no barrels or legacy `machine*` filenames.

The checker has executable fixture tests and runs as part of `pnpm check`. Add
new rules only with a failing fixture that demonstrates the boundary.
2 changes: 1 addition & 1 deletion examples/platformer/src/machine.test.ts
Original file line number Diff line number Diff line change
@@ -1,7 +1,7 @@
import { Machine } from "@typeonce/effect-machine"
import { Effect } from "effect"
import { describe, expect, it } from "vitest"
import { makeTextRenderer } from "../../../test/visualization/text.ts"
import { makeTextRenderer } from "../../../test/machine/visualization/text.ts"
import { CharacterMachine, type CharacterSnapshot, Event } from "./machine.ts"

const renderMachine = makeTextRenderer<typeof CharacterMachine, CharacterSnapshot>(Machine)
Expand Down
15 changes: 8 additions & 7 deletions package.json
Original file line number Diff line number Diff line change
Expand Up @@ -27,16 +27,16 @@
"import": "./dist/index.js"
},
"./reactivity": {
"types": "./dist/reactivity.d.ts",
"import": "./dist/reactivity.js"
"types": "./dist/unstable/reactivity/index.d.ts",
"import": "./dist/unstable/reactivity/index.js"
},
"./cluster": {
"types": "./dist/cluster.d.ts",
"import": "./dist/cluster.js"
"types": "./dist/unstable/cluster/index.d.ts",
"import": "./dist/unstable/cluster/index.js"
},
"./testing": {
"types": "./dist/testing.d.ts",
"import": "./dist/testing.js"
"types": "./dist/testing/index.d.ts",
"import": "./dist/testing/index.js"
},
"./package.json": "./package.json"
},
Expand All @@ -48,14 +48,15 @@
"build": "tsc -p tsconfig.build.json",
"test": "vitest run",
"test:types": "tstyche",
"check:architecture": "node --test scripts/check-architecture.test.mjs && node scripts/check-architecture.mjs",
"typecheck": "tsc -p tsconfig.json --noEmit",
"perf:types": "pnpm build && node scripts/type-performance.mjs",
"perf:runtime": "pnpm build && node --expose-gc scripts/runtime-performance.mjs",
"format": "dprint fmt",
"format:check": "dprint check",
"test:consumer": "node scripts/test-consumer.mjs",
"pack:check": "node scripts/pack-check.mjs",
"check": "pnpm format:check && pnpm typecheck && pnpm build && pnpm test && pnpm test:types && pnpm test:consumer && pnpm pack:check",
"check": "pnpm format:check && pnpm check:architecture && pnpm typecheck && pnpm build && pnpm test && pnpm test:types && pnpm test:consumer && pnpm pack:check",
"changeset": "changeset",
"version-packages": "changeset version",
"release": "pnpm build && changeset publish"
Expand Down
Loading