You signed in with another tab or window. Reload to refresh your session.You signed out in another tab or window. Reload to refresh your session.You switched accounts on another tab or window. Reload to refresh your session.Dismiss alert
{{ message }}
This repository was archived by the owner on May 14, 2026. It is now read-only.
pacquet_store_dir::add_files_from_dir — directory walker that re-CAFSes a post-build package directory. Mirrors pnpm/pnpm@7e3145f9fc:store/cafs/src/addFilesFromDir.ts:16-194 + worker/src/start.ts:312-383. Symlinks read/recurse via the resolved canonical path (TOCTOU-safe), top-level node_modules skipped, cycle-safe via recursion-stack visited set.
pacquet_store_dir::upload + calculate_diff — upload(store_dir, built_pkg_location, files_index_file, cache_key, writer) walks the directory and queues a WriteMsg::SideEffectsUpload. The actual R/M/W (load existing row → algo == HASH_ALGORITHM check → diff → side_effects[cache_key] = diff) runs inside the writer task's transaction so concurrent uploads to the same row stay commutative.
StoreIndexWriter driving a WriteMsg enum (Replace vs SideEffectsUpload). The batch loop coalesces per-key updates into one PackageFilesIndex before flushing.
BuildModules WRITE call site — after run_postinstall_hooks returns Ok and the gate fires, calls pacquet_store_dir::upload(...). Upload errors are swallowed with tracing::warn!, matching upstream's try { upload } catch { logger.warn } at building/during-install/src/index.ts:208-215.
build_deps_subgraph — the cache-hashing dep graph is bounded to the forward closure of requires_build snapshots, so pure-JS installs skip the walk entirely. Upstream's lockfileToDepGraph builds the full graph because its consumers extend beyond cache hashing; pacquet's graph is consumed only by calc_dep_state today, so the closure-bounded walk produces byte-identical cache keys with strictly less work.
StoreIndexWriter hoisted to InstallFrozenLockfile::run so the writer spans both the prefetch/download phase (existing Replace queues) and the build phase (new SideEffectsUpload queues). The final batch flushes once BuildModules::run returns.
Byte-stable msgpack — encode_package_files_index now iterates files, side_effects, and SideEffectsDiff.added in sorted-key order so two pacquet writes of the same logical row produce identical bytes on disk.
workspace_yaml::tests: sideEffectsCacheReadonly round-trip + 4-state truth table.
build_modules::tests:
write_path_populates_side_effects_row — pre-seed a base row, run a postinstall that creates generated.txt, drain the writer, assert side_effects[cache_key].added carries generated.txt and NOT pristine index.js. Mirrors 'a postinstall script does not modify the original sources added to the store' at pnpm/pnpm@7e3145f9fc:sideEffects.ts:189-223.
write_path_disabled_skips_upload — gate off → row's side_effects stays None.
upload_error_does_not_interrupt_install — postinstall produces a 0-permission file → walker fails on EACCES → BuildModules swallows with warn! → install completes, base row untouched. Mirrors 'uploading errors do not interrupt installation' at sideEffects.ts:166-187.
Benchmark at merge: cold install on par with main (~2.4s), hot cache ~7% faster than main (690ms vs 738ms), pacquet beats pnpm by 2.5× cold / 3.5× hot.
Closed in
55f6f7684f(PR #424) — slice (B) WRITE path landed.PR #424 wired:
pacquet_store_dir::add_files_from_dir— directory walker that re-CAFSes a post-build package directory. Mirrorspnpm/pnpm@7e3145f9fc:store/cafs/src/addFilesFromDir.ts:16-194+worker/src/start.ts:312-383. Symlinks read/recurse via the resolved canonical path (TOCTOU-safe), top-levelnode_modulesskipped, cycle-safe via recursion-stackvisitedset.pacquet_store_dir::upload+calculate_diff—upload(store_dir, built_pkg_location, files_index_file, cache_key, writer)walks the directory and queues aWriteMsg::SideEffectsUpload. The actual R/M/W (load existing row →algo == HASH_ALGORITHMcheck → diff →side_effects[cache_key] = diff) runs inside the writer task's transaction so concurrent uploads to the same row stay commutative.StoreIndexWriterdriving aWriteMsgenum (ReplacevsSideEffectsUpload). The batch loop coalesces per-key updates into onePackageFilesIndexbefore flushing.Config.side_effects_cache_readonly: bool(defaultfalse) — mirrors pnpm'ssideEffectsCacheReadonly. HelpersConfig::side_effects_cache_read()/side_effects_cache_write()consolidate the precedence.BuildModulesWRITE call site — afterrun_postinstall_hooksreturnsOkand the gate fires, callspacquet_store_dir::upload(...). Upload errors are swallowed withtracing::warn!, matching upstream'stry { upload } catch { logger.warn }atbuilding/during-install/src/index.ts:208-215.build_deps_subgraph— the cache-hashing dep graph is bounded to the forward closure ofrequires_buildsnapshots, so pure-JS installs skip the walk entirely. Upstream'slockfileToDepGraphbuilds the full graph because its consumers extend beyond cache hashing; pacquet's graph is consumed only bycalc_dep_statetoday, so the closure-bounded walk produces byte-identical cache keys with strictly less work.StoreIndexWriterhoisted toInstallFrozenLockfile::runso the writer spans both the prefetch/download phase (existingReplacequeues) and the build phase (newSideEffectsUploadqueues). The final batch flushes onceBuildModules::runreturns.encode_package_files_indexnow iteratesfiles,side_effects, andSideEffectsDiff.addedin sorted-key order so two pacquet writes of the same logical row produce identical bytes on disk.Tests ported / added:
add_files_from_dir::tests(9 cases): top-level files, nested forward-slash paths, exec-bit propagation, top-level-vs-nestednode_modules, symlink-out-of-root drop, symlink-in-root follow, directory-cycle termination, missing-root error.upload::tests(6 cases): identical / added-only / deleted-only / digest change / mode change / mixed — everycalculateDiffbranch.deps_graph::testsforbuild_deps_subgraph: empty-roots → empty graph, forward-closure inclusion/exclusion, cycle termination.workspace_yaml::tests:sideEffectsCacheReadonlyround-trip + 4-state truth table.build_modules::tests:write_path_populates_side_effects_row— pre-seed a base row, run a postinstall that createsgenerated.txt, drain the writer, assertside_effects[cache_key].addedcarriesgenerated.txtand NOT pristineindex.js. Mirrors'a postinstall script does not modify the original sources added to the store'atpnpm/pnpm@7e3145f9fc:sideEffects.ts:189-223.write_path_disabled_skips_upload— gate off → row'sside_effectsstaysNone.upload_error_does_not_interrupt_install— postinstall produces a 0-permission file → walker fails onEACCES→BuildModulesswallows withwarn!→ install completes, base row untouched. Mirrors'uploading errors do not interrupt installation'atsideEffects.ts:166-187.Benchmark at merge: cold install on par with
main(~2.4s), hot cache ~7% faster thanmain(690ms vs 738ms), pacquet beats pnpm by 2.5× cold / 3.5× hot.Both sub-tasks of this issue are now complete:
97d74398e1(PR feat(package-manager): is_built gate for the side-effects cache READ path (#421 slice A) #423)55f6f7684f(PR feat: side-effects cache WRITE path (#421 slice B) #424)#397 item #10 fully closed.
Written by an agent (Claude Code, claude-opus-4-7).