epoch-2: Proxy-based API, hooks instrumentation, docs overhaul#33
Draft
nerdalytics wants to merge 184 commits intotrunkfrom
Draft
epoch-2: Proxy-based API, hooks instrumentation, docs overhaul#33nerdalytics wants to merge 184 commits intotrunkfrom
nerdalytics wants to merge 184 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.
…x type safety - Remove dead `if (silent)` branch in recordEffectRead (unreachable after early return) - Remove CONFIG wrapper, inline MUTATING_ARRAY_METHODS array directly into Set - Replace inline try-catch in addPendingEffect with callHookSafe for consistency - Deduplicate hookless get handler — delegate to getWrappedArrayMethod instead of pre-checking - Fix disposeChildEffects — snapshot children before iterating to avoid mutation-during-iteration - Add onDependencyChange to EffectHooks in types.ts, remove unsafe type cast in effect()/derive() - Deduplicate cleanupEffect — delegate subscriber removal to removeEffectFromSubscribers - Replace tempDepsMatchPrev with existing depsMatch (identical semantics) - Delete broken scripts/memory-benchmark.ts (uses removed epoch-1 API) - Update performance page with fresh benchmark numbers
Adds '100 subs disjoint props' benchmark: 100 effects on 1 state object, each reading a different property. Measures notification pipeline efficiency when only 1 of N subscribers cares about the changed property.
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
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