Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
52 commits
Select commit Hold shift + click to select a range
e58fb9d
chore: budget each bundle entry by what it adds to the measured core
jo16oh Oct 15, 2026
f65a5c0
docs(notes): record why bundle increments are measured from the core
jo16oh Oct 15, 2026
433d162
refactor: write bundle-size with valof
jo16oh Oct 15, 2026
4e9900d
refactor: register each bundle in bundle-size as a strategy, flagged …
jo16oh Oct 15, 2026
3bf404a
chore: check every bundle in bundle-size, and label each by its base …
jo16oh Oct 15, 2026
8efcd58
chore: share column alignment in bundle-size, and name the budget's unit
jo16oh Oct 15, 2026
d9def50
refactor: replace the bundle strategies with one Bundle value
jo16oh Oct 15, 2026
e1c8754
refactor: list the bundles in a registry value, each flagging its own…
jo16oh Oct 15, 2026
dad4b0d
refactor: split bundle-size into schema, measuring and formatting mod…
jo16oh Oct 15, 2026
5c6d793
refactor: measure the bundles through a Suite, split from Bundle
jo16oh Oct 15, 2026
0fed8b6
refactor: report bundle-size failures as better-result Results
jo16oh Oct 15, 2026
18cc495
refactor: move the bundle-size run into Cli.run
jo16oh Oct 15, 2026
a8353db
refactor: measure the whole suite in Suite.measure
jo16oh Oct 15, 2026
d71ffe4
refactor: branch Cli.run on the measured result with match
jo16oh Oct 15, 2026
ba01a2a
refactor: build the bundle-size output in report
jo16oh Oct 15, 2026
55ec20f
refactor: carry each bundle's capped bytes on MeasuredBundle
jo16oh Oct 15, 2026
10d90e6
refactor: model bundle-size measurements as Build, Measured and Measu…
jo16oh Oct 15, 2026
6fa0e0c
refactor: make Measurement a value
jo16oh Oct 15, 2026
92c287c
chore: check bundle-size builds by comparing their sizes
jo16oh Oct 15, 2026
79dc26d
refactor: split the bundle-size toolchain out of measure, declaring f…
jo16oh Oct 15, 2026
1f05c1c
docs(notes): record why bundle-size checks compare sizes
jo16oh Oct 15, 2026
b7e854f
refactor: put the exported functions first in format and toolchain
jo16oh Oct 15, 2026
a79b2ee
refactor: put the ok branch first in bundle-size results
jo16oh Oct 15, 2026
9ff3495
refactor: drop bundle-size comments the code already states
jo16oh Oct 15, 2026
f956217
refactor: build the CompileFailed and MinifyFailed messages in their …
jo16oh Oct 15, 2026
bad167e
refactor: declare Compiled as a Val
jo16oh Oct 15, 2026
81ad63e
refactor: ask Modules whether they read NODE_ENV
jo16oh Oct 15, 2026
1931ac9
refactor: declare Code as a Val that minifies and sizes itself
jo16oh Oct 15, 2026
eda1bde
refactor: ask each Module whether it reads NODE_ENV
jo16oh Oct 15, 2026
db802e8
refactor: suffix the bundle-size error names with Error
jo16oh Oct 15, 2026
b08d01b
docs(notes): use the renamed bundle-size errors in §16.2
jo16oh Oct 15, 2026
572aef1
refactor: cast the build promise instead of wrapping it in async
jo16oh Oct 15, 2026
9381b03
refactor: rename the bundle-size results to BuildResult, BundleSize a…
jo16oh Oct 15, 2026
80dc42d
refactor: chain the bundle-size promises instead of nesting them
jo16oh Oct 15, 2026
b535402
feat: add pipe, flow and tap to the bundle-size scripts
jo16oh Oct 15, 2026
72c0332
feat: let tap wait for a promise its function returns
jo16oh Oct 15, 2026
cb2edd9
feat: extend pipe and flow to 20 steps, with generated type tests
jo16oh Oct 15, 2026
611a0a2
refactor: chain the bundle-size steps with flow
jo16oh Oct 15, 2026
b4b0217
feat: let flow end in a sink that takes the promise, and add GenAwait
jo16oh Oct 15, 2026
079b9b4
refactor: await the suite's measurements with GenAwait
jo16oh Oct 15, 2026
b6e73c4
refactor: await the toolchain's measurements with GenAwait
jo16oh Oct 15, 2026
9c6f565
refactor: build the bundle-size error messages in their constructors
jo16oh Oct 15, 2026
362a480
docs(notes): record the DevCodeKeptError rename in §16.2
jo16oh Oct 15, 2026
b507e07
style: break the bundle-size functions into paragraphs
jo16oh Oct 15, 2026
3bb2ebf
refactor: stop at a missing chunk with yield* instead of an early return
jo16oh Oct 15, 2026
399a887
refactor: fold single-use bundle-size variables into pipe and flow
jo16oh Oct 15, 2026
17a85ef
refactor: name the entry's content before writing it
jo16oh Oct 15, 2026
b38def6
test: rename the sink fixture away from the reserved Box
jo16oh Oct 15, 2026
6ab80ec
refactor: wrap the compile steps in attempt and measure sizes on Comp…
jo16oh Oct 15, 2026
d0a3e2c
refactor: move the files several scripts share into scripts/shared
jo16oh Oct 15, 2026
cd6a528
chore: pin the scripts to the released valof through #valof
jo16oh Oct 16, 2026
a2939bc
docs: check the scripts against src before a release, and bump valof-…
jo16oh Oct 16, 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
14 changes: 10 additions & 4 deletions AGENTS.md
Original file line number Diff line number Diff line change
Expand Up @@ -46,17 +46,23 @@ title and body as a draft and stop there.
## Releasing

1. Write the release notes for users, not reviewers, in a file outside the repo.
2. Run `vp run bump-version --body-file notes.md --release <type> --yes`, where `<type>` is the
2. Set `#valof` in `package.json` to `./src/index.ts`, run `vp check` and `vp run bundle-size`, and
set it back to `valof-release`. Report what breaks, and where the new API is awkward to use in
the scripts. A user encounters both when upgrading. If a problem is serious, stop the release and
rework the design.
3. Run `vp run bump-version --body-file notes.md --release <type> --yes`, where `<type>` is the
release type I give, such as `patch`. It pushes a release branch and opens its PR with the file
as the body. Options other than `--body-file` go to bumpp.
3. If CI reports stale bundle-size claims, run
4. If CI reports stale bundle-size claims, run
`vp run replace-bundle-size-claims --write README.md package.json` on that branch, commit, and
push.
4. I merge the PR. The release workflow tags the merge commit, publishes to npm, creates the GitHub
5. I merge the PR. The release workflow tags the merge commit, publishes to npm, creates the GitHub
release, and publishes the book. The notes start with the PR body, followed by the PRs merged
since the last tag.
6. Once npm has the new version, set `valof-release` in `package.json` to it, and fix the scripts
where step 2 found a break.

Steps 2 and 3 push, and step 2 opens a PR. They are the only exception to "Never push, and never
Steps 3 and 4 push, and step 3 opens a PR. They are the only exception to "Never push, and never
open a PR" above. Run them only on my explicit go-ahead.

## Writing Style
Expand Down
75 changes: 74 additions & 1 deletion notes/design.md
Original file line number Diff line number Diff line change
Expand Up @@ -39,7 +39,7 @@ import { Val } from "valof";
- **15.2 `Enum`** §7.4 の見直し。Variant をレコードに宣言して union を導出、ブランドの導出、タグ名のカスタムと `tag-mismatch`、companion に置く `match`、ts-pattern との線引き、型を確かめた記録(共通フィールド、`match` の型引数、`VariantOf` の表示、却下した戻り値の型引数・自由関数の `match`・Val のレコード、Trait の実装、タグ名を `Tag<…>` で渡すこと、トップレベルの条件型が宣言出力を壊すこと)、実装して分かったこと(Fault の置き場所、Variant 1 個の禁止、`then` を 3 箇所で落とす、宣言出力の CI、variance 測定と型コスト)、Variant ごとの seal と union の `seal`(入口を 2 つに分ける、builder を callback で渡す、`impl` で鎖を閉じる、却下した値の形)、`seal` の引数を排他的にしたこと、タグだけで分ける `[Tag].match` と `[Tag].key`(キーを文字列にする理由、却下した名前)、`implVariant` を Variant ごとの鎖にしたこと、steps を枠そのものにしたこと、タグ名の渡し方を変える 4 案の却下、companion を渡さない形(val.ts の門を共有する、`PayloadKeys` の分配、入口 2 つぶんのテスト)
- **15.3 `path`** seal をまたぐ patch の合成。`abort` を合成側に置く判断、ハンドラが最終段である理由(HKT)、`glue` の `open` / `close`、`each` / `where`、却下した `deepPatch`
- **15.4 `.impl` のコールバック形** 自分の companion を参照すると推論が回らない(TS7022)。contextual typing がコールバック越しでも効くことの実測、`implTrait` も callback、予算、却下したメンバのカリー化と、下流の `.impl` を型で塞ぐ案。**鎖は 2026-09-18 に閉じた**(1 回だけ、兄弟は注釈で呼ぶ、Trait の実装の受け手を `Tr` に、却下した型レベルの案内と lint 規則)
- **§16 予算の責務** バンドルと型を別のスクリプトに割る。宣言のバイト数を type-perf へ、予算を 64 kB に上げた理由
- **§16 予算の責務** バンドルと型を別のスクリプトに割る。宣言のバイト数を type-perf へ、予算を 64 kB に上げた理由、16.1 バンドルの増分を実測の core から測る、16.2 production の検査はサイズを比べる
- **§17 Effect v4 調査** 落ちた typeclass と HKT の層(§15.3 の根拠)、値の中に等価性を置く形、クラスの構造比較に開く穴、向こうにあってこちらに無いもの

---
Expand Down Expand Up @@ -6532,6 +6532,79 @@ type-perf に移せる理由でもある。
`index.d.mts` は entry を分けた後も同じ読み方をする。entry ファイル自体は再 export 2 行で 323 B しかなく、
中身は共有チャンクにあるので、type-perf は import を辿って合算する。

### 16.1 増分は実測の core から測る、2026-10-15

**`1024 + 256` の書き方は、core の実測が増えたときの関係を保たなかった。**比べていたのは各エントリの実測と
「core の予算 + 増分」で、各エントリは core の予算の余りを借りて通っていた。

| | 実測 | 予算 | core からの増分 | 名目上の増分 |
| ---------- | ------- | ---------- | --------------- | ------------ |
| `Val` | 896 B | 1024 | | |
| + `equals` | 1,083 B | 1024 + 128 | **+187** | +128 |
| + `Trait` | 978 B | 1024 + 256 | +82 | +256 |
| + `Enum` | 1,370 B | 1024 + 384 | **+474** | +384 |
| 全部 | 1,633 B | 1024 + 640 | **+737** | +640 |

`.implCreate.noseal`(#65)で core が 44 B 増え、全部の余りが 31 B、`Enum` の余りが 38 B になった。core が
1 B 増えれば、全部の余りも 1 B 減る。§16 の「core の予算を動かしたときも関係が保たれる」と合わない。

- **core だけを実測と比べる。**ほかは `エントリの実測 - core の実測` を増分の予算と比べる。core の増加は
core の予算でだけ止まる
- **増分を決め直した。**`equals` +256、`Trait` +256(据え置き)、`Enum` +640、全部 +1024。どれも 26% 以上の
余りがある
- **予算の表は増分を印字する。**`added by Enum +474 B / +640 B`

**却下: 仕組みはそのままで、数値だけ上げる。**`Enum` を +512、全部を +768 にする案。core が増えるたびに、
また狭くなる。

**却下: `Enum` を +512 にする。**区切りはよいが、余りが 38 B(7%)で、直す前と変わらない。`match` の `_` が
実行時のコードを足すので、その PR で上げ直すことになる。

### 16.2 production の検査はサイズを比べる、2026-10-15

**前の検査は文字列を探していた。**production の core に `snapshotSealInput` など development 用の
文字列 3 つが残っていないか、`equals` を import しないバンドルに `Number.isNaN` が残っていないかを見ていた。
関数名とメッセージが変わると黙って効かなくなり、新しい development 用のコードは書き足さないと見ない。

**`Number.isNaN` は `equals` の目印になっていなかった。**`development` を `true` に固定すると、`equals` を
import しない core で「equals が残っている」と出た。development 用のコードも `Number.isNaN` を使う。

今は 2 つの検査とも、特定の名前や文字列に頼らず、サイズで比べる。

| 検査 | 失敗する条件 |
| ------------------ | ---------------------------------------------------------------------------------------------------- |
| `DevCodeKeptError` | バンドルに入ったソースが `NODE_ENV` を読むのに、production と development の minify 後のサイズが同じ |
| `AddsNothingError` | ベースの上に積んだバンドルのライブラリ部分が、ベースと同じ長さ。足した import はベースに入っている |

- **ライブラリ部分は「チャンクの長さ − エントリの `renderedLength`」で測る。**エントリは import した名前を
`console.log` に渡すので、全体を比べると名前の分だけ必ず違う。各モジュールの `renderedLength` は
tree-shaking の前の長さで、`equals` の有無で同じ 13,923 だった。使えない
- **`NODE_ENV` を読むかどうかは、`define` が置き換える前のソースで見る。**ビルド後のコードには残らない

壊して確かめた結果。

| 壊し方 | 結果 |
| -------------------------------------------------------------- | ------------------------------------------ |
| `src/val.ts` のトップレベルで `equals` を参照し、core に漏らす | `AddsNothingError` |
| `define` を効かなくし、両方のモードを同じコードにする | `DevCodeKeptError` |
| `development` を `true` に固定する | 検査は通る。ソースが `NODE_ENV` を読まない |

**拾えないもの: development 用の分岐の一部だけが production に残る場合。**`development` で囲み忘れた
検査が 1 つあっても、ほかの分岐は消えるのでサイズは違ったまま。予算が拾うのは、core の余りより大きい漏れだけ。
`development` を `true` に固定したときは、core が 896 B から 1.65 kB になり、予算を 670 B 超えた。

**却下: 検査をやめて予算に任せる。**core の余り 128 B より大きい漏れは予算が拾う。実測では development 用の
コードが約 +790 B、`equals` が +187 B。ただし `equals` は余りより 59 B 大きいだけで、core が増えないまま
core の予算を 60 B 上げれば通る。サイズの比較は余りに左右されない。

**名前は検査の範囲に合わせる、2026-10-16。**`BranchIgnoredError` を `DevCodeKeptError` に改めた。前の名前は、
何の分岐を何が無視したのかを言っていなかった。

- **却下: `TreeShakingError`。**tree-shaking は使われない export やモジュールを落とすこと。`if` の分岐を消すのは
dead code elimination で、別の工程
- **却下: `DeadCodeEliminationError`。**この検査が見るのは development 用の分岐だけで、dead code 全般ではない。
名前が検査の保証より広い

---

## 17. Effect v4 調査、2026-09-10
Expand Down
7 changes: 6 additions & 1 deletion package.json
Original file line number Diff line number Diff line change
Expand Up @@ -31,6 +31,9 @@
],
"type": "module",
"sideEffects": false,
"imports": {
"#valof": "valof-release"
},
"exports": {
".": "./dist/index.mjs",
"./eslint-plugin": "./dist/eslint-plugin.mjs",
Expand All @@ -48,7 +51,7 @@
"docs:serve": "mdbook serve docs",
"test": "vp test",
"check": "vp check",
"bundle-size": "node scripts/bundle-size.ts",
"bundle-size": "node scripts/bundle-size/index.ts",
"replace-bundle-size-claims": "node scripts/replace-bundle-size-claims.ts",
"ts-compatibility": "node scripts/ts-compatibility/index.ts",
"type-perf": "node scripts/type-perf/index.ts",
Expand All @@ -58,6 +61,7 @@
},
"devDependencies": {
"@types/node": "^26.5.0",
"better-result": "^3.0.1",
"bumpp": "^11.1.0",
"eslint": "^10.10.0",
"handlebars": "^4.7.9",
Expand All @@ -69,6 +73,7 @@
"typescript": "^7.0.2",
"typescript-5": "npm:typescript@^5.9.3",
"typescript-6": "npm:typescript@^6.0.3",
"valof-release": "npm:[email protected]",
"vite": "catalog:",
"vite-plus": "catalog:"
},
Expand Down
24 changes: 24 additions & 0 deletions pnpm-lock.yaml

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

Loading
Loading