# Migration guide ## Before changing the dependency 1. Confirm that every deployed runtime is Node.js 14.15.1 or newer. 2. Inventory root and deep `fstream` imports. 3. Record filesystem integration coverage for symlinks, hard links, filters, event order, permissions, timestamps, and destination replacement. 4. Decide whether to preserve the old dependency key or adopt the scoped name. ## Lowest-change alias migration Keep source imports unchanged by aliasing the dependency: ```json { "dependencies": { "fstream": "npm:@stackline/fstream@1.0.0" } } ``` Then `require('fstream')` and historical `require('fstream/lib/reader')` calls continue to resolve. Commit the manifest and lockfile together, perform a clean production install, and run the consumer's ordinary test suite plus focused filesystem cases. ## Scoped-name migration Change root imports directly: ```diff -const fstream = require('fstream') +const fstream = require('@stackline/fstream') ``` Update deep imports the same way. All 14 modules historically shipped beneath `lib/` are export-mapped with and without `.js`. ## ESM Use the default namespace, named exports, or both: ```js import fstream, { Reader, Writer, collect } from '@stackline/fstream' Reader('source').pipe(Writer('destination')) console.log(collect === fstream.collect) ``` Historical deep modules use default ESM exports: ```js import Reader from '@stackline/fstream/lib/reader.js' ``` There is no browser build or browser export condition. ## TypeScript CommonJS projects may use `import = require`: ```ts import fstream = require('@stackline/fstream') const options: fstream.ReaderOptions = { path: 'input', sort: 'alpha' } const reader = fstream.Reader(options) ``` Modern ESM projects receive the ESM declaration facade and named type exports. Upstream shipped no first-party declarations, so remove or review local ambient shims that could shadow the package types. ## Intentional corrections to exercise 1. Nested `follow: true` follows descendant links. An ancestor cycle emits one `ELOOP` warning and ends only that branch. 2. Independent sibling aliases of one directory may each traverse it. 3. Hard-link identity is local to one root traversal; `Reader.hardLinks` is present but inert. 4. Typed and read-only Readers expose complete metadata before `ready`. 5. A successful Writer close is terminal; valid pre-close errors still emit. 6. ProxyWriter backpressure emits `drain` when the selected delegate can accept more work. 7. Destinationless `collect().pipe()` replay terminates in event order. 8. Native private helpers replace the deprecated mkdirp/rimraf production tree. Do not deep-import implementation helpers. ## Recommended acceptance checks - clean install with production dependencies only; - unchanged root key order and constructor identities; - CJS and ESM root and deep resolution, with and without `.js`; - TypeScript compile using the consumer's oldest and current compilers; - directory and file copy, filtering, ordering, permissions, and timestamps; - external symlink targets, sibling aliases, and back-to-ancestor cycles; - multiply-linked files across concurrent and later root traversals; - malformed paths, expected-size mismatch, and immediate cleanup after close; - each supported operating system and filesystem. ## Consumers older than 1.0.12 `1.0.12` is the first upstream release outside the affected range of [GHSA-xf7w-r453-m56c](https://github.com/advisories/GHSA-xf7w-r453-m56c). Consumers of an earlier version should first compare their relied-on behavior with `fstream@1.0.12`; this package does not emulate older affected releases. ## Rollback Restore the prior dependency specification and lockfile, install cleanly, and run the same tests. The package owns no migration state. Writer operations are not transactional, so dependency rollback does not undo files, links, ownership, modes, or timestamps already changed.