epoch-2: Proxy-based API, hooks instrumentation, docs overhaul#33
Draft
nerdalytics wants to merge 187 commits intotrunkfrom
Draft
epoch-2: Proxy-based API, hooks instrumentation, docs overhaul#33nerdalytics wants to merge 187 commits intotrunkfrom
nerdalytics wants to merge 187 commits intotrunkfrom
Conversation
Replace function-based state with Proxy-based objects for more natural
property access syntax. Removes select, lens, readonlyState, and
protectedState APIs in favor of direct property mutation tracking.
BREAKING CHANGE: v2000.0.0 API overhaul
- state() now returns reactive Proxy object
- derive() returns {value, dispose, [Symbol.dispose]}
- Removed: select, lens, readonlyState, protectedState
Delete tests for removed APIs (select, lens, readonlyState, protectedState) and legacy function-based state tests. Replaced by new reorganized test suite in follow-up commit.
Introduce new test organization with clear separation of concerns: - Core tests for each primitive (state, derive, effect, batch) - Integration tests (state-derive, state-effect, batch-integration) - Updated cleanup, cyclic-dependency, and infinite-loop tests Includes test style guide and organization documentation.
Remove beacon-logo.png and beacon-logo@2.png in favor of new beacon-logo-v2.svg for better scalability and smaller file size.
- Update README with new usage examples and API reference - Refresh TECHNICAL_DETAILS with Proxy implementation details - Add docs/ folder with modular documentation per feature - Remove references to deprecated APIs (select, lens, etc.)
- Simplify GitHub Actions workflows - Update mise.toml configuration - Remove benchmark.ts, strip-comments.ts, update-performance-docs.ts - Enhance naiv-benchmark.ts with consolidated functionality
Add documentation for Beacon's hooks system: - HOOKS.md: Overview and usage guide - HOOKS_API.md: API reference - HOOKS_CATALOG.md: Available hooks catalog - HOOKS_TODO.md: Future development roadmap
… not derive chain consistency Derive chains propagate consistently for a single source mutation without batch — effects run in Set insertion order, which matches creation order, which matches dependency order. Batch collapses multiple source mutations into one notification cycle. Updated docs that implied batch was needed for derive chain consistency.
Whitelist .md files in docs/, src/, tests/, scripts/ in .gitignore to allow progressive disclosure documentation. Add AGENTS.md and CLAUDE.md index files at root and per-directory level for codebase navigation and domain-specific instructions.
…ncyAdd, onSchedule
…Error, onDependencyChange
Remove unused HooksObject type, replace non-null assertions with optional chaining, fix import ordering, add explicit parameter types to hook callbacks, apply Biome formatting.
Add src/hooks/AGENTS.md documenting the hooks public API, composition utility, interfaces, and design constraints. Update tests/AGENTS.md with hooks test category and per-file coverage breakdown. Update root and src indexes to reflect hooks infrastructure now implemented.
Local validation results: - Node 25.8.1: 193/193 tests pass - Bun 1.3.10: 192/193 tests pass (deepStrictEqual compat difference) - Deno 2.7.5: 185/193 tests pass (afterEach not implemented in node:test compat) CI jobs run with continue-on-error: true (informational, not gating).
- Bun: use ./tests/ path prefix (bun test treats bare globs as filters) - Deno: run deno install before tests to resolve npm dependencies
…fix small screen overflow
…() from $app/paths
…s, simplify handbook internals - Add thin, transparent-track scrollbars with progressive enhancement (@supports scrollbar-color for Firefox/Chrome/Safari, webkit fallback) - Use --color-text-muted for thumb contrast (~4.5:1 vs background) - Hide TOC scrollbar to eliminate double-scrollbar on long pages - Add resolveHref() wrapper to fix 9 svelte-check type errors from SvelteKit's typed resolve() rejecting dynamic strings - Hoist static allPages to navigation module (avoid per-mount allocation) - Guard sidebar close effect to skip no-op writes
…date benchmarks - Fix bug: add clearRerunState() to error path in runEffectSafely — previously, if an effect threw during re-run, the 6 module-level rerun tracking variables retained stale data - Extract resetEffectTracking() from cleanupEffect/cleanupEffectOnError to prevent field-reset drift - Add currentEffect guard in hookless get handler — eliminates a function call on every property read outside effects (~6% faster) - Eliminate spread allocation in disposeChildEffects by severing __children before iterating - Replace Array.from in runDeferredEffects with index-based iteration - Update performance page with 10-cycle v1000 vs v2000 benchmarks
Print per-sample mean (was computed for sd but never shown) and relabel aggregate columns to match the summary line style: total, avg/cycle, total mem, avg mem/cycle.
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.
Summary
state.valueinstead ofstate())state,effect,derive,batch)sideEffects: false, CI OIDC publishing, multi-cycle benchmark)exactOptionalPropertyTypesandnoUncheckedIndexedAccesstype errors for strict LTS buildChanges
Core (
src/index.ts)state()with five handler traps (get, set, deleteProperty, has, ownKeys)derive()returns{ value, reactive }— disposal viareactive = falsetoggle, notdispose()methodsrc/types.ts, composition utility insrc/hooks/NonNullable<>forexactOptionalPropertyTypescompliancePerformance Optimizations
getSubscriberseffectDependencies,effectStateReads,parentEffect,childEffects,subscriberCache) with direct EffectFunction properties (__deps,__reads,__parent,__children); change inner reads tracking fromWeakMaptoMap; simplifypromoteTempToGlobalto 2 assignments; removesubscriberCachelayerperformWrite/checkInfiniteLoopwhen!currentEffect(~28% improvement on subscriberless writes)HOOKLESS_HANDLERat module level avoids 5 factory calls + 1 object allocation per state (~33% improvement on state creation)Benchmark Results
Worktree-based comparison, 1M iterations, 10 cycles x 7 samples = 70 total per benchmark. Medians shown.
Interpretation: Epoch-2's Proxy-based architecture pays a fixed cost on bare reads/writes/creation (no subscribers involved). Where it matters — effect re-runs, subscriber notification, derive chains — epoch-2 is 15-64% faster due to per-property tracking, stable-dep skip, readList fast path, and inlined hot paths.
Documentation (
docs/README*.md,README.md,.github/README.md)dispose()/Symbol.dispose/usingreferences withreactivetoggle inREADME.derive.mdbatchDirtyTargetswith actualpendingEffects+deferredEffectCreationsmechanism inREADME.batch.mdREADME.core.mdREADME.debugging.md— remove fictional env-var debug system (BEACON_DEBUG,NODE_ENV,devLogRead/Write/Assert), replace with hooks-based debuggingREADME.hooks.mdcovering all 16 hook callbacks, composition, error isolationv2000.0.0version references from proseRefactoring
composeHookfrom hooks module instead of inliningTests
{primitive}-core.test.ts,{primitive}-hooks.test.ts, integration, behaviornoUncheckedIndexedAccesstype errors across all property-based test filesstate: array mutations, same-value optimization, proxy identity, deep reactivity, frozen/sealed objects, frozen children of reactive stateeffect: cleanup completeness, infinite loop detection boundary, dynamic dependency trackingbatch: effect deduplication, error recoveryderive: consistency invariantsInfrastructure
sideEffects: false, CI OIDC npm publishing, multi-cycle benchmarkcheck:fixnow runsbiome check --write(wasbiome format --fix).github/README.md, keep root README as install + quick start onlyTest plan
npm test— 193/193 tests pass (129 unit + 64 property-based, 27 suites)npm run test:coverage— meets coverage targets (100% branches, 100% functions, 90% lines)npm run build— builds successfully (esbuild minification)tsc -p tsconfig.lts.json— zero type errors under strict modenpm run check— Biome lint + format + assists cleannpm run benchmark— confirms performance improvementstate-only bundle eliminatesderiveandbatchcodeid-token: write, noNODE_AUTH_TOKENgrep -r "dispose()" docs/shows only effect disposalsgrep -rE "BEACON_DEBUG|devLog|batchDirtyTargets|v2000\.0\.0" docs/returns empty