diff --git a/packages/boot/app-boot/src/profile-resolution/legacy-links.ts b/packages/boot/app-boot/src/profile-resolution/legacy-links.ts index b7d01a5639..075f1a9d1c 100644 --- a/packages/boot/app-boot/src/profile-resolution/legacy-links.ts +++ b/packages/boot/app-boot/src/profile-resolution/legacy-links.ts @@ -1,10 +1,6 @@ -/** Package directory canonicalization and stale profile-projection recognition. */ +/** Package directory canonicalization through the active runtime carrier's filesystem. */ -import { lstatSync, readlinkSync, realpathSync } from 'node:fs' -import { basename, dirname, join, resolve } from 'node:path' - -/** Profile-private package links projected into its pnpm-managed node_modules. */ -export const PROFILE_MODULE_FALLBACK_DIR = '.dsh-module-fallback' +import { realpathSync } from 'node:fs' /** * Return whether the process reads application modules from pkg's virtual filesystem. @@ -22,52 +18,3 @@ export function isPackagedExecutable(): boolean { export function realModuleDirectory(path: string): string { return isPackagedExecutable() ? realpathSync(path) : realpathSync.native(path) } - -/** - * Resolve a link target without following the final path component. - * @param path - candidate path whose parent is canonicalized. - * @returns the canonical candidate, or undefined when its parent is absent. - */ -export function canonicalLinkPath(path: string): string | undefined { - try { - return join(realModuleDirectory(dirname(path)), basename(path)) - } catch (error) { - // A missing parent means the candidate cannot identify an existing owned link. - /* v8 ignore next 2 -- a non-ENOENT realpath failure requires a host filesystem fault */ - if ((error as NodeJS.ErrnoException).code === 'ENOENT') return undefined - /* v8 ignore next -- see the host-filesystem exception above */ - throw error - } -} - -/** - * Return whether a symlink or junction points at the same path as `target`. - * @param link - symlink or junction to inspect. - * @param target - expected target path. - * @returns whether both paths identify the same entry. - */ -export function symlinkPointsTo(link: string, target: string): boolean { - const actual = resolve(dirname(link), readlinkSync(link)) - const canonicalActual = canonicalLinkPath(actual) - const canonicalTarget = canonicalLinkPath(resolve(target)) - return canonicalActual !== undefined && canonicalActual === canonicalTarget -} - -/** - * Return whether an observed profile package must not claim local precedence. - * @param profileDir - profile directory containing the package projection. - * @param packageName - bare package name to inspect. - * @returns whether the entry is a managed fallback link or disappeared during inspection. - */ -export function isProfileModuleFallbackLink(profileDir: string, packageName: string): boolean { - const link = join(profileDir, 'node_modules', packageName) - const target = join(profileDir, PROFILE_MODULE_FALLBACK_DIR, 'node_modules', packageName) - try { - return lstatSync(link).isSymbolicLink() && symlinkPointsTo(link, target) - } catch (error) { - /* v8 ignore next 2 -- a vanished candidate cannot claim local precedence */ - if ((error as NodeJS.ErrnoException).code === 'ENOENT') return true - /* v8 ignore next -- non-ENOENT lstat/readlink failures require a host filesystem fault */ - throw error - } -} diff --git a/packages/boot/app-boot/src/profile-resolution/resolver.ts b/packages/boot/app-boot/src/profile-resolution/resolver.ts index e9774e917d..11533aba18 100644 --- a/packages/boot/app-boot/src/profile-resolution/resolver.ts +++ b/packages/boot/app-boot/src/profile-resolution/resolver.ts @@ -7,7 +7,6 @@ import { fileURLToPath, pathToFileURL } from 'node:url' import { getEnvironmentData, setEnvironmentData } from 'node:worker_threads' import type { ModuleLoaderV1, ModuleLoaderV2, ResolveResult } from '@deepseek-ai/cordis-plugin-loader' import { imports as resolvePackageImports, type Package as ResolvePackageManifest } from 'resolve.exports' -import { isProfileModuleFallbackLink } from './legacy-links.ts' import type { ProfileResolutionEntry, ProfileResolutionGeneration } from '../profile.ts' const WORKER_RESOLUTION_KEY = '@deepseek-ai/dsh-app-boot/profile-resolution' @@ -48,6 +47,12 @@ type EsmResolve = ( request: string, parent: string | undefined, attributes: ImportAttributes, ) => ResolveResult | Promise +/** + * Where a scoped bare request resolves. A `fallback` entry occupies the package + * directory at the interception layer; `after` anchors the ancestors above that + * layer for a CommonJS subpath the entry lacks. An `after-fallback` route anchors + * the interception layer itself for a name the generation does not carry. + */ type ResolutionRoute = | { readonly kind: 'fallback'; readonly entry: ProfileResolutionEntry; readonly after: string } | { readonly kind: 'after-fallback'; readonly parent: string } @@ -62,7 +67,8 @@ interface ResolutionRouteState { interface ParentRoutes { readonly parent: string - readonly profilesDir: string + /** Profile directory owning the importer; its node_modules are local, the first ancestor above it is the interception layer. */ + readonly localRoot: string readonly activeProfile: boolean readonly requests: Map selfReferenceName?: string | false | null @@ -79,7 +85,6 @@ interface CompiledGeneration { readonly profile: readonly string[] readonly activeProfileUrls: readonly string[] readonly localPackageNames: ReadonlySet - readonly shared: ReadonlySet readonly esmRoutes: ResolutionRoutes readonly cjsRoutes: ResolutionRoutes } @@ -145,7 +150,6 @@ function compileGeneration(generation: ProfileResolutionGeneration): CompiledGen profile, activeProfileUrls: profile.map(path => pathToFileURL(path).href), localPackageNames: new Set(generation.localPackageNames), - shared: new Set(profilePaths.map(prefix => join(prefix, 'node_modules'))), esmRoutes: new Map(), cjsRoutes: new Map(), } @@ -158,6 +162,18 @@ function startsWithin(path: string, roots: readonly string[]): boolean { return false } +/** + * The profile directory owning a module inside the profiles tree: the first path segment below the tree root. + * @param path - module path below `treePrefix`. + * @param treePrefix - profiles directory with a trailing separator. + * @returns the profile directory without a trailing separator. + */ +function profileChild(path: string, treePrefix: string): string { + const rest = path.slice(treePrefix.length) + const end = rest.indexOf(sep) + return treePrefix + (end < 0 ? rest : rest.slice(0, end)) +} + function nativePackageDir(parent: string, name: string): string | undefined { for (const searchPath of createRequire(parent).resolve.paths(name) as string[]) { const candidate = join(searchPath, name) @@ -166,16 +182,14 @@ function nativePackageDir(parent: string, name: string): string | undefined { return undefined } -function localPackageCandidate( - searchPath: string, name: string, flavor: 'esm' | 'cjs', -): { packageDir: string; canBeManagedLink: boolean } | undefined { +function localPackageCandidate(searchPath: string, name: string, flavor: 'esm' | 'cjs'): string | undefined { const candidate = join(searchPath, name) const stat = statSync(candidate, { throwIfNoEntry: false }) const found = flavor === 'esm' ? stat?.isDirectory() === true : stat !== undefined || ['.js', '.json', '.node'].some(extension => existsSync(candidate + extension)) - return found ? { packageDir: candidate, canBeManagedLink: stat !== undefined } : undefined + return found ? candidate : undefined } function selfReferenceName(parent: string): string | false | null { @@ -326,7 +340,7 @@ class ResolutionRouter { nativeResolve?: (searchPaths: readonly string[]) => string, cacheable = false, ): ResolutionRouteState | undefined { - const { parent, profilesDir, requests } = parentRoutes + const { parent, localRoot, requests } = parentRoutes const name = barePackageName(request) if (name === undefined) return undefined @@ -340,31 +354,26 @@ class ResolutionRouter { } const target = generation.entries.get(name) - const candidates: Array<{ packageDir: string; canBeManagedLink: boolean }> = [] + const candidates: string[] = [] const localSearchPaths: string[] = [] + const localPrefix = localRoot + sep for (const searchPath of createRequire(parent).resolve.paths(name) as string[]) { - if (generation.shared.has(resolve(searchPath))) break + if (!searchPath.startsWith(localPrefix)) break localSearchPaths.push(searchPath) const candidate = localPackageCandidate(searchPath, name, flavor) - if (candidate !== undefined) { - const legacy = candidate.canBeManagedLink && generation.profile.some(prefix => ( - candidate.packageDir === join(prefix, 'node_modules', name) - && isProfileModuleFallbackLink(prefix.slice(0, -1), name) - )) - if (!legacy) candidates.push(candidate) - } + if (candidate !== undefined) candidates.push(candidate) } if (candidates.length > 0) { if (flavor === 'cjs' && nativeResolve !== undefined) { try { const resolved = nativeResolve(localSearchPaths) const selected = candidates.find(candidate => ( - localCandidateOwnsResolution(candidate.packageDir, resolved, request, name) + localCandidateOwnsResolution(candidate, resolved, request, name) )) if (selected !== undefined) { const state = { - route: { kind: 'native' as const, packageDir: selected.packageDir }, - packageDir: selected.packageDir, + route: { kind: 'native' as const, packageDir: selected }, + packageDir: selected, ...(cacheable ? { cjs: resolved } : {}), } requests.set(request, state) @@ -374,22 +383,23 @@ class ResolutionRouter { if (!isUnselectedPackageMiss(error)) throw error } } else { - const selected = candidates[0] as { packageDir: string } + const selected = candidates[0] as string const state = { - route: { kind: 'native' as const, packageDir: selected.packageDir }, - packageDir: selected.packageDir, + route: { kind: 'native' as const, packageDir: selected }, + packageDir: selected, } requests.set(request, state) return state } } + // The interception layer is the node_modules directory of the profile's parent. + const layer = dirname(localRoot) const eligible = target?.scope === 'installation' || (target?.scope === 'profile' && parentRoutes.activeProfile) - const after = join(dirname(profilesDir), 'package.json') const route: ResolutionRoute = eligible - ? { kind: 'fallback', entry: target, after } - : { kind: 'after-fallback', parent: after } + ? { kind: 'fallback', entry: target, after: join(dirname(layer), 'package.json') } + : { kind: 'after-fallback', parent: join(layer, 'package.json') } const state: ResolutionRouteState = { route } if (route.kind === 'fallback') requests.set(request, state) return state @@ -425,17 +435,13 @@ class ResolutionRouter { generation.esmRoutes.set(parentURL, false) return undefined } - const profileRoot = profileIndex >= 0 - ? generation.profilePaths[profileIndex] - : generation.profile[activeProfileIndex] + const treeRoot = generation.profilePaths[profileIndex] + const localRoot = treeRoot !== undefined + ? profileChild(parent, treeRoot) + : generation.profile[activeProfileIndex]?.slice(0, -1) /* v8 ignore next -- one matching index was established above */ - if (profileRoot === undefined) return undefined - parentRoutes = { - parent, - profilesDir: profileRoot.slice(0, -1), - activeProfile: activeProfileIndex >= 0, - requests: new Map(), - } + if (localRoot === undefined) return undefined + parentRoutes = { parent, localRoot, activeProfile: activeProfileIndex >= 0, requests: new Map() } generation.esmRoutes.set(parentURL, parentRoutes) } const local = this.routeLocalPackage(request, parentRoutes, generation) @@ -452,18 +458,11 @@ class ResolutionRouter { const cached = parentRoutes?.requests.get(request) if (cached !== undefined) return cached if (parentRoutes === undefined) { - const profilesDir = generation.profilePaths.find(prefix => parent.startsWith(prefix)) + const treeRoot = generation.profilePaths.find(prefix => parent.startsWith(prefix)) const activeProfile = generation.profile.find(prefix => parent.startsWith(prefix)) - if (profilesDir !== undefined || activeProfile !== undefined) { - const profileRoot = profilesDir ?? activeProfile - /* v8 ignore next -- one matching root was established above */ - if (profileRoot === undefined) return undefined - parentRoutes = { - parent, - profilesDir: profileRoot.slice(0, -1), - activeProfile: activeProfile !== undefined, - requests: new Map(), - } + const localRoot = treeRoot !== undefined ? profileChild(parent, treeRoot) : activeProfile?.slice(0, -1) + if (localRoot !== undefined) { + parentRoutes = { parent, localRoot, activeProfile: activeProfile !== undefined, requests: new Map() } generation.cjsRoutes.set(parent, parentRoutes) } } diff --git a/packages/boot/app-boot/src/profile.ts b/packages/boot/app-boot/src/profile.ts index 0528ce189c..5d4ba04b19 100644 --- a/packages/boot/app-boot/src/profile.ts +++ b/packages/boot/app-boot/src/profile.ts @@ -21,16 +21,14 @@ */ import { createRequire } from 'node:module' -import { existsSync, lstatSync, mkdirSync, readFileSync, writeFileSync } from 'node:fs' +import { existsSync, mkdirSync, readFileSync, writeFileSync } from 'node:fs' import { basename, dirname, join } from 'node:path' import type { EntryOptions } from '@deepseek-ai/cordis-plugin-loader' import { applyEntryPatches, type PatchOptions } from '@deepseek-ai/cordis-plugin-include' import { resolveDshHome } from '@deepseek-ai/dsh-home-paths' import type { DshPackageManifest } from '@deepseek-ai/dsh-package-manifest' import { loadOverlayPatches } from './index.ts' -import { - canonicalLinkPath, isProfileModuleFallbackLink, PROFILE_MODULE_FALLBACK_DIR, realModuleDirectory, symlinkPointsTo, -} from './profile-resolution/legacy-links.ts' +import { realModuleDirectory } from './profile-resolution/legacy-links.ts' /** Directory under the Harness home holding every profile. */ export const PROFILES_DIR = 'profiles' @@ -326,17 +324,13 @@ function skippedProfileBundles(profile: Profile | undefined, manifest: ProfileMa /** Return installed direct dependencies that Node resolves before profile fallback. */ function installedProfilePackageNames(profile: Profile, manifest: ProfileManifest | undefined): string[] { if (manifest === undefined) return [] - return profileDependencyNames(manifest).filter((name) => { - const candidate = join(profile.dir, 'node_modules', name) - if (!existsSync(join(candidate, 'package.json'))) return false - return !isProfileModuleFallbackLink(profile.dir, name) - }) + return profileDependencyNames(manifest) + .filter(name => existsSync(join(profile.dir, 'node_modules', name, 'package.json'))) } /** Collect the first resolvable package directory for each dependency name. */ function dependencyClosure( anchors: readonly string[], reserved: ReadonlySet, - exclude: (candidate: string, packageName: string) => boolean, declarers?: Map, versions?: Map, ): Map { @@ -359,7 +353,7 @@ function dependencyClosure( /* v8 ignore next -- an installable package manifest always declares dependencies or peers */ for (const dep of profileDependencyNames(next.manifest)) { if (visited.has(dep)) continue - const dir = packageDirFromAnchor(next.anchor, dep, exclude) + const dir = packageDirFromAnchor(next.anchor, dep) // A declared-but-uninstalled dependency cannot be loader-visible. if (dir === undefined) continue visited.add(dep) @@ -381,25 +375,10 @@ function resolveProfileModuleFallback( declarers?: Map, versions?: Map, ): Map { - const profileModulesDir = join(profile.dir, 'node_modules') - const ownedModulesDir = join(profile.dir, PROFILE_MODULE_FALLBACK_DIR, 'node_modules') const bundleAnchors = profile.layers .filter(layer => !installationPackageNames.has(layer.packageName)) .map(layer => join(layer.packageDir, 'package.json')) - const bundleLinks = dependencyClosure(bundleAnchors, installationPackageNames, (candidate, packageName) => { - const profileLink = join(profileModulesDir, packageName) - if (canonicalLinkPath(candidate) !== canonicalLinkPath(profileLink)) return false - try { - return lstatSync(profileLink).isSymbolicLink() - && symlinkPointsTo(profileLink, join(ownedModulesDir, packageName)) - } catch (error) { - // A concurrent cleanup may remove the projection after package discovery. - /* v8 ignore next 2 -- a non-ENOENT lstat failure requires a host filesystem fault */ - if ((error as NodeJS.ErrnoException).code === 'ENOENT') return true - /* v8 ignore next -- see the host-filesystem exception above */ - throw error - } - }, declarers, versions) + const bundleLinks = dependencyClosure(bundleAnchors, installationPackageNames, declarers, versions) for (const layer of profile.layers) bundleLinks.delete(layer.packageName) return bundleLinks } @@ -473,15 +452,12 @@ function normalizeShippedProfile(name: string, dir: string, manifest: ProfileMan * matches what the Loader would import from the same anchor, and * `existsSync` follows the symlinks pnpm's isolated layout uses. */ -function packageDirFromAnchor( - anchor: string, packageName: string, - exclude: (candidate: string, packageName: string) => boolean = () => false, -): string | undefined { +function packageDirFromAnchor(anchor: string, packageName: string): string | undefined { // resolve.paths returns null only for builtins, which no bundle name is. /* v8 ignore next */ for (const searchPath of createRequire(anchor).resolve.paths(packageName) ?? []) { const candidate = join(searchPath, packageName) - if (existsSync(join(candidate, 'package.json')) && !exclude(candidate, packageName)) return candidate + if (existsSync(join(candidate, 'package.json'))) return candidate } return undefined } diff --git a/packages/boot/app-boot/tests/profile-resolution.spec.ts b/packages/boot/app-boot/tests/profile-resolution.spec.ts index 60dba4a1b1..198b874407 100644 --- a/packages/boot/app-boot/tests/profile-resolution.spec.ts +++ b/packages/boot/app-boot/tests/profile-resolution.spec.ts @@ -132,29 +132,30 @@ function thrownError(callback: () => unknown): Error & { throw new Error('expected callback to throw') } -function fixture(name = 'resolution-lib'): { +function fixture(name = '@deepseek-ai/dsh-core'): { root: string installAnchor: string installed: string profile: Profile } { + // root plays $DSH_HOME; the running dsh lives in a global install outside the profiles tree. // macOS exposes tmpdir through /var while Node returns resolved module paths through /private/var. const root = realpathSync(mkdtempSync(join(tmpdir(), 'dsh-profile-generation-'))) roots.push(root) - const installDir = join(root, 'install') + const installDir = join(root, 'global', 'node_modules', '@deepseek-ai', 'dsh') const installed = join(installDir, 'node_modules', name) - const installAnchor = pkg(installDir, 'test-app', 0, { [name]: '*' }) + const installAnchor = pkg(installDir, '@deepseek-ai/dsh', 0, { [name]: '*' }) pkg(installed, name, 1) - const profileDir = join(root, 'profiles', 'test') + const profileDir = join(root, 'profiles', 'web') file(join(profileDir, 'package.json'), JSON.stringify({ - name: 'test-profile', private: true, dependencies: { 'missing-local': '*' }, + name: 'dsh-profile-web', private: true, dependencies: { 'missing-local': '*' }, })) return { root, installAnchor, installed, profile: { - name: 'test', + name: 'web', dir: profileDir, layers: [], patchPath: join(profileDir, 'cordis.patch.yml'), @@ -171,11 +172,61 @@ async function generationOf(f: ReturnType): Promise = { private: 11, profile: 12, shared: 13, home: 14 } + +interface LookupCase { + title: string + importer: 'profile' | 'plugin' + kind: 'installation' | 'bundle' | 'unlisted' + layers: readonly LookupLayer[] + form: 'directory' | 'symlink' +} + +/** Every combination of importer, package kind, present layers, and on-disk form of the profile and shared layers. */ +function lookupMatrix(): LookupCase[] { + const cases: LookupCase[] = [] + for (const importer of ['profile', 'plugin'] as const) { + const available = importer === 'plugin' ? LOOKUP_LAYERS : LOOKUP_LAYERS.filter(layer => layer !== 'private') + for (let mask = 0; mask < 1 << available.length; mask++) { + const layers = available.filter((_, index) => (mask & (1 << index)) !== 0) + const forms = layers.some(layer => layer === 'profile' || layer === 'shared') + ? ['directory', 'symlink'] as const + : ['directory'] as const + for (const kind of ['installation', 'bundle', 'unlisted'] as const) { + for (const form of forms) { + cases.push({ + title: `resolves ${kind} package from ${importer} importer with [${layers.join(', ')}] as ${form}`, + importer, kind, layers, form, + }) + } + } + } + } + return cases +} + +/** The lookup order: profile-internal layers, then the generation at the interception layer, then the remaining ancestors. */ +function lookupWinner(matrixCase: LookupCase): LookupLayer | 'generation' | 'missing' { + if (matrixCase.importer === 'plugin' && matrixCase.layers.includes('private')) return 'private' + if (matrixCase.layers.includes('profile')) return 'profile' + if (matrixCase.kind !== 'unlisted') return 'generation' + if (matrixCase.layers.includes('shared')) return 'shared' + if (matrixCase.layers.includes('home')) return 'home' + return 'missing' +} + describe('profile resolution generation', { concurrent: false }, () => { it('computes an immutable runtime generation without writing profile packages', async () => { const f = fixture() const generation = await generationOf(f) - expect(generation.entries.find(entry => entry.name === 'resolution-lib')).toMatchObject({ + expect(generation.entries.find(entry => entry.name === '@deepseek-ai/dsh-core')).toMatchObject({ packageDir: f.installed, version: '1.0.0', declarer: f.installAnchor, @@ -195,7 +246,7 @@ describe('profile resolution generation', { concurrent: false }, () => { expect(installationOnly.localPackageNames).toEqual([]) const registration = installProfileResolution(installationOnly) registrations.push(registration) - expect(createRequire(join(installationOnly.profilesDir, 'entry.cjs'))('resolution-lib')) + expect(createRequire(join(installationOnly.profilesDir, 'entry.cjs'))('@deepseek-ai/dsh-core')) .toEqual({ marker: 1 }) }) @@ -231,7 +282,7 @@ describe('profile resolution generation', { concurrent: false }, () => { symlinkSync(dirname(f.installAnchor), dirname(installAnchor), 'junction') } if (origin === 'bundle') { - pkg(dirname(f.installAnchor), 'test-app', 0) + pkg(dirname(f.installAnchor), '@deepseek-ai/dsh', 0) f.profile.layers.push({ packageName: 'bridge', packageDir: f.installed, patchPath: join(f.installed, 'cordis.patch.yml'), patches: [], @@ -241,7 +292,7 @@ describe('profile resolution generation', { concurrent: false }, () => { const generation = await createProfileResolutionGeneration({ installAnchor, profile: f.profile, home: f.root, }) - expect(generation.entries.find(entry => entry.name === 'test-app')?.declarer).toBe(f.installAnchor) + expect(generation.entries.find(entry => entry.name === '@deepseek-ai/dsh')?.declarer).toBe(f.installAnchor) expect(generation.entries.find(entry => entry.name === 'middle')?.declarer) .toBe(join(bridge, 'package.json')) expect(generation.entries.find(entry => entry.name === 'leaf')).toMatchObject({ @@ -301,25 +352,25 @@ describe('profile resolution generation', { concurrent: false }, () => { const registration = installProfileResolution(await generationOf(f)) registrations.push(registration) const require = createRequire(join(f.profile.dir, 'entry.cjs')) - expect(require('resolution-lib')).toEqual({ marker: 1 }) - expect(require.resolve('resolution-lib')).toBe(join(f.installed, 'index.cjs')) + expect(require('@deepseek-ai/dsh-core')).toEqual({ marker: 1 }) + expect(require.resolve('@deepseek-ai/dsh-core')).toBe(join(f.installed, 'index.cjs')) const parent = pathToFileURL(join(f.profile.dir, 'entry.mjs')).href - expect(resolveFrom('resolution-lib', parent)).toBe(pathToFileURL(join(f.installed, 'index.js')).href) - expect(resolveFrom('resolution-lib', parent, { type: 'javascript' })) + expect(resolveFrom('@deepseek-ai/dsh-core', parent)).toBe(pathToFileURL(join(f.installed, 'index.js')).href) + expect(resolveFrom('@deepseek-ai/dsh-core', parent, { type: 'javascript' })) .toBe(pathToFileURL(join(f.installed, 'index.js')).href) - expect(resolveFrom('resolution-lib', parent)).toBe(pathToFileURL(join(f.installed, 'index.js')).href) - expect(import.meta.resolve('resolution-lib', parent)).toBe(pathToFileURL(join(f.installed, 'index.js')).href) - expect(await importFrom('resolution-lib', parent)).toMatchObject({ marker: 1 }) + expect(resolveFrom('@deepseek-ai/dsh-core', parent)).toBe(pathToFileURL(join(f.installed, 'index.js')).href) + expect(import.meta.resolve('@deepseek-ai/dsh-core', parent)).toBe(pathToFileURL(join(f.installed, 'index.js')).href) + expect(await importFrom('@deepseek-ai/dsh-core', parent)).toMatchObject({ marker: 1 }) }) it('routes a scoped CommonJS package through its containing node_modules directory', async () => { - const f = fixture('@scope/resolution-lib') + const f = fixture('@scope/tools') const registration = installProfileResolution(await generationOf(f)) registrations.push(registration) const require = createRequire(join(f.profile.dir, 'entry.cjs')) - expect(require('@scope/resolution-lib')).toEqual({ marker: 1 }) - expect(require.resolve('@scope/resolution-lib')).toBe(join(f.installed, 'index.cjs')) + expect(require('@scope/tools')).toEqual({ marker: 1 }) + expect(require.resolve('@scope/tools')).toBe(join(f.installed, 'index.cjs')) }) it('routes a CommonJS npm alias through its declaring package', async () => { @@ -334,10 +385,12 @@ describe('profile resolution generation', { concurrent: false }, () => { expect(createRequire(join(f.profile.dir, 'entry.cjs'))('aliased-lib')).toEqual({ marker: 5 }) }) - it('routes an application-owned profile outside the shared profiles directory', async () => { + it('intercepts directly above an application-owned profile outside the shared profiles directory', async () => { const f = fixture() const profileDir = join(f.root, 'application-profile') file(join(profileDir, 'package.json'), JSON.stringify({ name: 'application-profile', private: true })) + // The first ancestor node_modules above the profile directory is the interception layer. + pkg(join(f.root, 'node_modules', '@deepseek-ai/dsh-core'), '@deepseek-ai/dsh-core', 2) const profile = { ...f.profile, dir: profileDir, @@ -351,32 +404,34 @@ describe('profile resolution generation', { concurrent: false }, () => { const registration = installProfileResolution(generation) registrations.push(registration) const require = createRequire(join(profileDir, 'entry.cjs')) - expect(require('resolution-lib')).toEqual({ marker: 1 }) + expect(require('@deepseek-ai/dsh-core')).toEqual({ marker: 1 }) + expect(require.resolve('@deepseek-ai/dsh-core', { paths: [profileDir] })).toBe(join(f.installed, 'index.cjs')) const parent = pathToFileURL(join(profileDir, 'entry.mjs')).href - expect(resolveFrom('resolution-lib', parent)).toBe(pathToFileURL(join(f.installed, 'index.js')).href) - expect(await importFrom('resolution-lib', parent)).toMatchObject({ marker: 1 }) - expect(() => { createRequire(join(f.root, 'outside.cjs'))('resolution-lib') }).toThrow(/Cannot find module/u) + expect(resolveFrom('@deepseek-ai/dsh-core', parent)).toBe(pathToFileURL(join(f.installed, 'index.js')).href) + expect(await importFrom('@deepseek-ai/dsh-core', parent)).toMatchObject({ marker: 1 }) + expect(registration.packageDir('@deepseek-ai/dsh-core', parent)).toBe(f.installed) + expect(createRequire(join(f.root, 'outside.cjs'))('@deepseek-ai/dsh-core')).toEqual({ marker: 2 }) }) it('does not reuse a default route for explicit CommonJS paths', async () => { const f = fixture() const alternative = join(f.root, 'alternative') - const alternativePackage = join(alternative, 'node_modules', 'resolution-lib') - pkg(alternativePackage, 'resolution-lib', 2) + const alternativePackage = join(alternative, 'node_modules', '@deepseek-ai/dsh-core') + pkg(alternativePackage, '@deepseek-ai/dsh-core', 2) const registration = installProfileResolution(await generationOf(f)) registrations.push(registration) const require = createRequire(join(f.profile.dir, 'entry.cjs')) - expect(require.resolve('resolution-lib')).toBe(join(f.installed, 'index.cjs')) - expect(require.resolve('resolution-lib', { paths: [alternative] })).toBe(join(alternativePackage, 'index.cjs')) - expect(require.resolve('resolution-lib', { paths: [f.profile.dir] })).toBe(join(f.installed, 'index.cjs')) - expect(require.resolve('resolution-lib', { paths: [join(f.root, 'missing'), f.profile.dir] })) + expect(require.resolve('@deepseek-ai/dsh-core')).toBe(join(f.installed, 'index.cjs')) + expect(require.resolve('@deepseek-ai/dsh-core', { paths: [alternative] })).toBe(join(alternativePackage, 'index.cjs')) + expect(require.resolve('@deepseek-ai/dsh-core', { paths: [f.profile.dir] })).toBe(join(f.installed, 'index.cjs')) + expect(require.resolve('@deepseek-ai/dsh-core', { paths: [join(f.root, 'missing'), f.profile.dir] })) .toBe(join(f.installed, 'index.cjs')) const relative = join(f.profile.dir, 'relative.cjs') file(relative, '') expect(require.resolve('./relative.cjs', { paths: [f.profile.dir] })).toBe(relative) const invalid = join(f.root, 'invalid') - file(join(invalid, 'node_modules', 'resolution-lib', 'package.json'), '{') - expect(() => { require.resolve('resolution-lib', { paths: [invalid, f.profile.dir] }) }) + file(join(invalid, 'node_modules', '@deepseek-ai/dsh-core', 'package.json'), '{') + expect(() => { require.resolve('@deepseek-ai/dsh-core', { paths: [invalid, f.profile.dir] }) }) .toThrow(/Invalid package config/u) }) @@ -387,33 +442,33 @@ describe('profile resolution generation', { concurrent: false }, () => { profile: f.profile, home: f.root, }) - const local = join(f.profile.dir, 'node_modules', 'resolution-lib') + const local = join(f.profile.dir, 'node_modules', '@deepseek-ai/dsh-core') file(join(local, 'package.json'), JSON.stringify({ - name: 'resolution-lib', version: '2.0.0', exports: './missing.cjs', + name: '@deepseek-ai/dsh-core', version: '2.0.0', exports: './missing.cjs', })) const alternative = join(f.root, 'alternative') - const selected = join(alternative, 'node_modules', 'resolution-lib', 'index.cjs') - pkg(dirname(selected), 'resolution-lib', 3) + const selected = join(alternative, 'node_modules', '@deepseek-ai/dsh-core', 'index.cjs') + pkg(dirname(selected), '@deepseek-ai/dsh-core', 3) const require = createRequire(join(f.profile.dir, 'entry.cjs')) const paths = [alternative, f.profile.dir] - expect(require.resolve('resolution-lib', { paths })).toBe(selected) + expect(require.resolve('@deepseek-ai/dsh-core', { paths })).toBe(selected) const registration = installProfileResolution(generation) registrations.push(registration) - expect(require.resolve('resolution-lib', { paths })).toBe(selected) + expect(require.resolve('@deepseek-ai/dsh-core', { paths })).toBe(selected) }) it('keeps a missing legacy main before a managed explicit CommonJS path', async () => { const f = fixture() const invalid = join(f.root, 'invalid') - const selected = join(invalid, 'node_modules', 'resolution-lib') - file(join(selected, 'package.json'), JSON.stringify({ name: 'resolution-lib', main: './missing.cjs' })) + const selected = join(invalid, 'node_modules', '@deepseek-ai/dsh-core') + file(join(selected, 'package.json'), JSON.stringify({ name: '@deepseek-ai/dsh-core', main: './missing.cjs' })) const require = createRequire(join(f.profile.dir, 'entry.cjs')) const paths = [invalid, f.profile.dir] - const nativeError = thrownError(() => require.resolve('resolution-lib', { paths })) + const nativeError = thrownError(() => require.resolve('@deepseek-ai/dsh-core', { paths })) const registration = installProfileResolution(await generationOf(f)) registrations.push(registration) - const runtimeError = thrownError(() => require.resolve('resolution-lib', { paths })) + const runtimeError = thrownError(() => require.resolve('@deepseek-ai/dsh-core', { paths })) expect(runtimeError).toMatchObject({ code: nativeError.code, @@ -447,18 +502,18 @@ describe('profile resolution generation', { concurrent: false }, () => { it('keeps a missing fallback legacy main after a generation miss', async () => { const f = fixture() - const selected = join(f.root, 'node_modules', 'resolution-lib') - file(join(selected, 'package.json'), JSON.stringify({ name: 'resolution-lib', main: './missing.cjs' })) + const selected = join(f.root, 'node_modules', '@deepseek-ai/dsh-core') + file(join(selected, 'package.json'), JSON.stringify({ name: '@deepseek-ai/dsh-core', main: './missing.cjs' })) const alternative = join(f.root, 'alternative') - pkg(join(alternative, 'node_modules', 'resolution-lib'), 'resolution-lib', 2) + pkg(join(alternative, 'node_modules', '@deepseek-ai/dsh-core'), '@deepseek-ai/dsh-core', 2) const generation = await generationOf(f) rmSync(f.installed, { recursive: true }) const require = createRequire(join(f.profile.dir, 'entry.cjs')) const paths = [f.profile.dir, alternative] - const nativeError = thrownError(() => require.resolve('resolution-lib', { paths })) + const nativeError = thrownError(() => require.resolve('@deepseek-ai/dsh-core', { paths })) const registration = installProfileResolution(generation) registrations.push(registration) - const runtimeError = thrownError(() => require.resolve('resolution-lib', { paths })) + const runtimeError = thrownError(() => require.resolve('@deepseek-ai/dsh-core', { paths })) expect(runtimeError).toMatchObject({ code: nativeError.code, @@ -471,12 +526,12 @@ describe('profile resolution generation', { concurrent: false }, () => { it('keeps a profile-local package ahead of the generation', async () => { const f = fixture() file(join(f.profile.dir, 'package.json'), JSON.stringify({ - name: 'test-profile', + name: 'dsh-profile-web', private: true, - dependencies: { 'resolution-lib': '*', 'linked-local': '*' }, + dependencies: { '@deepseek-ai/dsh-core': '*', 'linked-local': '*' }, })) - const localResolution = join(f.profile.dir, 'node_modules', 'resolution-lib') - pkg(localResolution, 'resolution-lib', 2) + const localResolution = join(f.profile.dir, 'node_modules', '@deepseek-ai/dsh-core') + pkg(localResolution, '@deepseek-ai/dsh-core', 2) const linkedLocal = join(f.root, 'linked-local') pkg(linkedLocal, 'linked-local', 3) symlinkSync( @@ -485,32 +540,32 @@ describe('profile resolution generation', { concurrent: false }, () => { process.platform === 'win32' ? 'junction' : 'dir', ) const generation = await generationOf(f) - expect(generation.localPackageNames).toEqual(['resolution-lib', 'linked-local']) + expect(generation.localPackageNames).toEqual(['@deepseek-ai/dsh-core', 'linked-local']) const registration = installProfileResolution(generation) registrations.push(registration) const require = createRequire(join(f.profile.dir, 'entry.cjs')) - expect(require('resolution-lib')).toEqual({ marker: 2 }) - expect(require.resolve('resolution-lib', { paths: [f.profile.dir] })) + expect(require('@deepseek-ai/dsh-core')).toEqual({ marker: 2 }) + expect(require.resolve('@deepseek-ai/dsh-core', { paths: [f.profile.dir] })) .toBe(join(localResolution, 'index.cjs')) const parent = pathToFileURL(join(f.profile.dir, 'entry.mjs')).href - expect(resolveFrom('resolution-lib', parent)).toBe( - pathToFileURL(join(f.profile.dir, 'node_modules', 'resolution-lib', 'index.js')).href, + expect(resolveFrom('@deepseek-ai/dsh-core', parent)).toBe( + pathToFileURL(join(f.profile.dir, 'node_modules', '@deepseek-ai/dsh-core', 'index.js')).href, ) }) it('preserves an npm alias package self-reference', async () => { const f = fixture() file(f.installAnchor, JSON.stringify({ - name: 'test-app', + name: '@deepseek-ai/dsh', version: '0.0.0', type: 'module', exports: { import: './index.js', require: './index.cjs' }, - dependencies: { 'resolution-lib': '*', 'real-name': '*' }, + dependencies: { '@deepseek-ai/dsh-core': '*', 'real-name': '*' }, })) const installedRealName = join(dirname(f.installAnchor), 'node_modules', 'real-name') pkg(installedRealName, 'real-name', 7) file(join(f.profile.dir, 'package.json'), JSON.stringify({ - name: 'test-profile', + name: 'dsh-profile-web', private: true, dependencies: { alias: 'npm:real-name' }, })) @@ -530,9 +585,9 @@ describe('profile resolution generation', { concurrent: false }, () => { ) const invalidScope = join(f.profile.dir, 'node_modules', 'invalid-scope') file(join(invalidScope, 'package.json'), '{') - expect(() => createRequire(join(invalidScope, 'inside.cjs')).resolve('resolution-lib')) + expect(() => createRequire(join(invalidScope, 'inside.cjs')).resolve('@deepseek-ai/dsh-core')) .toThrow(/Invalid package config/u) - expect(() => resolveFrom('resolution-lib', pathToFileURL(join(invalidScope, 'inside.mjs')).href)) + expect(() => resolveFrom('@deepseek-ai/dsh-core', pathToFileURL(join(invalidScope, 'inside.mjs')).href)) .toThrow(/Invalid package config/u) const registration = installProfileResolution(generation) @@ -542,18 +597,18 @@ describe('profile resolution generation', { concurrent: false }, () => { expect(resolveFrom('real-name', pathToFileURL(join(alias, 'inside-runtime.mjs')).href)).toBe( pathToFileURL(join(alias, 'index.js')).href, ) - expect(() => createRequire(join(invalidScope, 'inside.cjs')).resolve('resolution-lib')) + expect(() => createRequire(join(invalidScope, 'inside.cjs')).resolve('@deepseek-ai/dsh-core')) .toThrow(/Invalid package config/u) - expect(() => resolveFrom('resolution-lib', pathToFileURL(join(invalidScope, 'inside.mjs')).href)) + expect(() => resolveFrom('@deepseek-ai/dsh-core', pathToFileURL(join(invalidScope, 'inside.mjs')).href)) .toThrow(/Invalid package config/u) }) it('keeps package imports aliases on the generation route', async () => { const f = fixture() file(join(f.profile.dir, 'package.json'), JSON.stringify({ - name: 'test-profile', + name: 'dsh-profile-web', private: true, - imports: { '#resolution-lib': 'resolution-lib' }, + imports: { '#@deepseek-ai/dsh-core': '@deepseek-ai/dsh-core' }, })) const generation = await createProfileResolutionGeneration({ installAnchor: f.installAnchor, @@ -563,22 +618,22 @@ describe('profile resolution generation', { concurrent: false }, () => { const nested = join(f.profile.dir, 'nested') const require = createRequire(join(nested, 'entry.cjs')) const nativeParent = pathToFileURL(join(nested, 'entry-native.mjs')).href - expect(() => require.resolve('#resolution-lib')).toThrow(/Cannot find module/u) - expect(() => resolveFrom('#resolution-lib', nativeParent)).toThrow(/Cannot find package/u) + expect(() => require.resolve('#@deepseek-ai/dsh-core')).toThrow(/Cannot find module/u) + expect(() => resolveFrom('#@deepseek-ai/dsh-core', nativeParent)).toThrow(/Cannot find package/u) const registration = installProfileResolution(generation) registrations.push(registration) const runtimeParent = pathToFileURL(join(nested, 'entry-runtime.mjs')).href - expect(require.resolve('#resolution-lib')).toBe(join(f.installed, 'index.cjs')) - expect(resolveFrom('#resolution-lib', runtimeParent)).toBe(pathToFileURL(join(f.installed, 'index.js')).href) + expect(require.resolve('#@deepseek-ai/dsh-core')).toBe(join(f.installed, 'index.cjs')) + expect(resolveFrom('#@deepseek-ai/dsh-core', runtimeParent)).toBe(pathToFileURL(join(f.installed, 'index.js')).href) }) it('imports package aliases through the generation and reports the original importer', async () => { const f = fixture() file(join(f.profile.dir, 'package.json'), JSON.stringify({ - name: 'test-profile', + name: 'dsh-profile-web', private: true, - imports: { '#library': 'resolution-lib', '#missing': 'resolution-lib/missing' }, + imports: { '#library': '@deepseek-ai/dsh-core', '#missing': '@deepseek-ai/dsh-core/missing' }, })) const registration = installProfileResolution(await generationOf(f)) registrations.push(registration) @@ -593,7 +648,7 @@ describe('profile resolution generation', { concurrent: false }, () => { it('leaves relative package imports targets and their diagnostics to Node', async () => { const f = fixture() file(join(f.profile.dir, 'package.json'), JSON.stringify({ - name: 'test-profile', + name: 'dsh-profile-web', private: true, imports: { '#missing-relative': './missing.cjs' }, })) @@ -616,7 +671,7 @@ describe('profile resolution generation', { concurrent: false }, () => { it('keeps local and after-fallback package imports targets in native order', async () => { const f = fixture() file(join(f.profile.dir, 'package.json'), JSON.stringify({ - name: 'test-profile', + name: 'dsh-profile-web', private: true, imports: { '#local': 'local-import', '#ancestor': 'ancestor-import' }, })) @@ -651,62 +706,62 @@ describe('profile resolution generation', { concurrent: false }, () => { it('does not inherit package imports across node_modules', async () => { const f = fixture() file(join(f.profile.dir, 'package.json'), JSON.stringify({ - name: 'test-profile', private: true, imports: { '#resolution-lib': 'resolution-lib' }, + name: 'dsh-profile-web', private: true, imports: { '#@deepseek-ai/dsh-core': '@deepseek-ai/dsh-core' }, })) const nested = join(f.profile.dir, 'node_modules', 'manifestless', 'entry.cjs') file(nested, '') const require = createRequire(nested) - const nativeMessage = thrownMessage(() => require.resolve('#resolution-lib')) + const nativeMessage = thrownMessage(() => require.resolve('#@deepseek-ai/dsh-core')) const registration = installProfileResolution(await generationOf(f)) registrations.push(registration) - expect(thrownMessage(() => require.resolve('#resolution-lib'))).toBe(nativeMessage) + expect(thrownMessage(() => require.resolve('#@deepseek-ai/dsh-core'))).toBe(nativeMessage) }) it('falls through a missing local CommonJS subpath to the generation', async () => { const f = fixture() file(join(f.profile.dir, 'package.json'), JSON.stringify({ - name: 'test-profile', + name: 'dsh-profile-web', private: true, - dependencies: { 'resolution-lib': '*' }, + dependencies: { '@deepseek-ai/dsh-core': '*' }, })) file(join(f.installed, 'package.json'), JSON.stringify({ - name: 'resolution-lib', version: '1.0.0', type: 'module', main: './index.cjs', + name: '@deepseek-ai/dsh-core', version: '1.0.0', type: 'module', main: './index.cjs', })) file(join(f.installed, 'only-install.cjs'), 'module.exports = { marker: 8 }\n') file(join(f.installed, 'legacy-install.cjs'), 'module.exports = { marker: 9 }\n') - const local = join(f.profile.dir, 'node_modules', 'resolution-lib') + const local = join(f.profile.dir, 'node_modules', '@deepseek-ai/dsh-core') file(join(local, 'package.json'), JSON.stringify({ - name: 'resolution-lib', version: '2.0.0', type: 'module', main: './index.cjs', + name: '@deepseek-ai/dsh-core', version: '2.0.0', type: 'module', main: './index.cjs', })) file(join(local, 'index.cjs'), 'module.exports = { marker: 2 }\n') const generation = await generationOf(f) - expect(generation.localPackageNames).toEqual(['resolution-lib']) + expect(generation.localPackageNames).toEqual(['@deepseek-ai/dsh-core']) const registration = installProfileResolution(generation) registrations.push(registration) - expect(createRequire(join(f.profile.dir, 'entry.cjs')).resolve('resolution-lib/only-install.cjs')) + expect(createRequire(join(f.profile.dir, 'entry.cjs')).resolve('@deepseek-ai/dsh-core/only-install.cjs')) .toBe(join(f.installed, 'only-install.cjs')) rmSync(join(local, 'package.json')) - expect(createRequire(join(f.profile.dir, 'entry.cjs')).resolve('resolution-lib/legacy-install.cjs')) + expect(createRequire(join(f.profile.dir, 'entry.cjs')).resolve('@deepseek-ai/dsh-core/legacy-install.cjs')) .toBe(join(f.installed, 'legacy-install.cjs')) }) it('treats null exports as legacy CommonJS package resolution', async () => { const f = fixture() file(join(f.profile.dir, 'package.json'), JSON.stringify({ - name: 'test-profile', + name: 'dsh-profile-web', private: true, - dependencies: { 'resolution-lib': '*' }, + dependencies: { '@deepseek-ai/dsh-core': '*' }, })) file(join(f.installed, 'package.json'), JSON.stringify({ - name: 'resolution-lib', version: '1.0.0', type: 'module', exports: null, + name: '@deepseek-ai/dsh-core', version: '1.0.0', type: 'module', exports: null, })) const installedSubpath = join(f.installed, 'only-install.cjs') file(installedSubpath, 'module.exports = { marker: 8 }\n') - const local = join(f.profile.dir, 'node_modules', 'resolution-lib') + const local = join(f.profile.dir, 'node_modules', '@deepseek-ai/dsh-core') file(join(local, 'package.json'), JSON.stringify({ - name: 'resolution-lib', version: '2.0.0', type: 'module', exports: null, + name: '@deepseek-ai/dsh-core', version: '2.0.0', type: 'module', exports: null, })) const generation = await createProfileResolutionGeneration({ installAnchor: f.installAnchor, @@ -714,17 +769,17 @@ describe('profile resolution generation', { concurrent: false }, () => { home: f.root, }) const require = createRequire(join(local, 'entry.cjs')) - expect(() => require.resolve('resolution-lib/only-install.cjs')).toThrow(/Cannot find module/u) + expect(() => require.resolve('@deepseek-ai/dsh-core/only-install.cjs')).toThrow(/Cannot find module/u) const registration = installProfileResolution(generation) registrations.push(registration) - expect(require.resolve('resolution-lib/only-install.cjs')).toBe(installedSubpath) + expect(require.resolve('@deepseek-ai/dsh-core/only-install.cjs')).toBe(installedSubpath) }) it('stops a local CommonJS probe before the generation fallback position', async () => { const f = fixture() file(join(f.installed, 'package.json'), JSON.stringify({ - name: 'resolution-lib', version: '1.0.0', type: 'module', main: './index.cjs', + name: '@deepseek-ai/dsh-core', version: '1.0.0', type: 'module', main: './index.cjs', })) const installedSubpath = join(f.installed, 'only-install.cjs') file(installedSubpath, 'module.exports = { marker: 8 }\n') @@ -733,21 +788,21 @@ describe('profile resolution generation', { concurrent: false }, () => { profile: f.profile, home: f.root, }) - file(join(f.profile.dir, 'node_modules', 'resolution-lib', 'package.json'), JSON.stringify({ - name: 'resolution-lib', version: '2.0.0', main: './index.cjs', + file(join(f.profile.dir, 'node_modules', '@deepseek-ai/dsh-core', 'package.json'), JSON.stringify({ + name: '@deepseek-ai/dsh-core', version: '2.0.0', main: './index.cjs', })) - file(join(f.profile.dir, 'node_modules', 'resolution-lib', 'index.cjs'), 'module.exports = {}\n') - file(join(f.root, 'node_modules', 'resolution-lib', 'package.json'), JSON.stringify({ - name: 'resolution-lib', version: '3.0.0', exports: './index.cjs', + file(join(f.profile.dir, 'node_modules', '@deepseek-ai/dsh-core', 'index.cjs'), 'module.exports = {}\n') + file(join(f.root, 'node_modules', '@deepseek-ai/dsh-core', 'package.json'), JSON.stringify({ + name: '@deepseek-ai/dsh-core', version: '3.0.0', exports: './index.cjs', })) - file(join(f.root, 'node_modules', 'resolution-lib', 'index.cjs'), 'module.exports = {}\n') + file(join(f.root, 'node_modules', '@deepseek-ai/dsh-core', 'index.cjs'), 'module.exports = {}\n') const require = createRequire(join(f.profile.dir, 'entry.cjs')) - expect(thrownError(() => require.resolve('resolution-lib/only-install.cjs'))) + expect(thrownError(() => require.resolve('@deepseek-ai/dsh-core/only-install.cjs'))) .toMatchObject({ code: 'ERR_PACKAGE_PATH_NOT_EXPORTED' }) const registration = installProfileResolution(generation) registrations.push(registration) - expect(require.resolve('resolution-lib/only-install.cjs')).toBe(installedSubpath) + expect(require.resolve('@deepseek-ai/dsh-core/only-install.cjs')).toBe(installedSubpath) }) it('observes a profile-local package installed after an earlier miss', async () => { @@ -804,59 +859,59 @@ describe('profile resolution generation', { concurrent: false }, () => { it('keeps a legacy CommonJS package without a manifest ahead of the generation', async () => { const f = fixture() - file(join(f.profile.dir, 'node_modules', 'resolution-lib', 'index.js'), 'module.exports = { marker: 2 }\n') + file(join(f.profile.dir, 'node_modules', '@deepseek-ai/dsh-core', 'index.js'), 'module.exports = { marker: 2 }\n') const registration = installProfileResolution(await generationOf(f)) registrations.push(registration) - expect(createRequire(join(f.profile.dir, 'entry.cjs'))('resolution-lib')).toEqual({ marker: 2 }) + expect(createRequire(join(f.profile.dir, 'entry.cjs'))('@deepseek-ai/dsh-core')).toEqual({ marker: 2 }) }) it('keeps a manifestless local CommonJS subpath ahead of the generation', async () => { const f = fixture() - const localSubpath = join(f.profile.dir, 'node_modules', 'resolution-lib', 'sub.cjs') + const localSubpath = join(f.profile.dir, 'node_modules', '@deepseek-ai/dsh-core', 'sub.cjs') file(localSubpath, 'module.exports = { marker: 2 }\n') const require = createRequire(join(f.profile.dir, 'entry.cjs')) - expect(require.resolve('resolution-lib/sub.cjs')).toBe(localSubpath) + expect(require.resolve('@deepseek-ai/dsh-core/sub.cjs')).toBe(localSubpath) const registration = installProfileResolution(await generationOf(f)) registrations.push(registration) - expect(require.resolve('resolution-lib/sub.cjs')).toBe(localSubpath) + expect(require.resolve('@deepseek-ai/dsh-core/sub.cjs')).toBe(localSubpath) }) it('keeps a local extensionless CommonJS package ahead of the generation', async () => { const f = fixture() - const local = join(f.profile.dir, 'node_modules', 'resolution-lib') + const local = join(f.profile.dir, 'node_modules', '@deepseek-ai/dsh-core') file(local, 'module.exports = { marker: 2 }\n') const require = createRequire(join(f.profile.dir, 'entry.cjs')) - expect(require.resolve('resolution-lib')).toBe(local) + expect(require.resolve('@deepseek-ai/dsh-core')).toBe(local) const registration = installProfileResolution(await generationOf(f)) registrations.push(registration) - expect(require.resolve('resolution-lib')).toBe(local) + expect(require.resolve('@deepseek-ai/dsh-core')).toBe(local) }) it('keeps a local legacy main outside its package directory ahead of the generation', async () => { const f = fixture() - const local = join(f.profile.dir, 'node_modules', 'resolution-lib') - const outside = join(f.profile.dir, 'node_modules', 'outside.cjs') - file(join(local, 'package.json'), JSON.stringify({ name: 'resolution-lib', main: '../outside.cjs' })) + const local = join(f.profile.dir, 'node_modules', '@deepseek-ai/dsh-core') + const outside = join(f.profile.dir, 'node_modules', '@deepseek-ai', 'outside.cjs') + file(join(local, 'package.json'), JSON.stringify({ name: '@deepseek-ai/dsh-core', main: '../outside.cjs' })) file(outside, 'module.exports = { marker: 2 }\n') const require = createRequire(join(f.profile.dir, 'entry.cjs')) - expect(require.resolve('resolution-lib')).toBe(outside) + expect(require.resolve('@deepseek-ai/dsh-core')).toBe(outside) const registration = installProfileResolution(await generationOf(f)) registrations.push(registration) - expect(require.resolve('resolution-lib')).toBe(outside) + expect(require.resolve('@deepseek-ai/dsh-core')).toBe(outside) }) it('keeps a missing local legacy main error ahead of the generation', async () => { const f = fixture() - const local = join(f.profile.dir, 'node_modules', 'resolution-lib') - file(join(local, 'package.json'), JSON.stringify({ name: 'resolution-lib', main: './missing.cjs' })) + const local = join(f.profile.dir, 'node_modules', '@deepseek-ai/dsh-core') + file(join(local, 'package.json'), JSON.stringify({ name: '@deepseek-ai/dsh-core', main: './missing.cjs' })) const require = createRequire(join(f.profile.dir, 'entry.cjs')) - const nativeError = thrownError(() => require.resolve('resolution-lib')) + const nativeError = thrownError(() => require.resolve('@deepseek-ai/dsh-core')) const registration = installProfileResolution(await generationOf(f)) registrations.push(registration) - const runtimeError = thrownError(() => require.resolve('resolution-lib')) + const runtimeError = thrownError(() => require.resolve('@deepseek-ai/dsh-core')) expect(runtimeError).toMatchObject({ code: nativeError.code, @@ -868,23 +923,23 @@ describe('profile resolution generation', { concurrent: false }, () => { it('keeps a profile-local CommonJS package file ahead of the generation', async () => { const f = fixture() - file(join(f.profile.dir, 'node_modules', 'resolution-lib.js'), 'module.exports = { marker: 2 }\n') + file(join(f.profile.dir, 'node_modules', '@deepseek-ai/dsh-core.js'), 'module.exports = { marker: 2 }\n') const registration = installProfileResolution(await generationOf(f)) registrations.push(registration) - expect(createRequire(join(f.profile.dir, 'entry.cjs'))('resolution-lib')).toEqual({ marker: 2 }) - expect(resolveFrom('resolution-lib', pathToFileURL(join(f.profile.dir, 'entry.mjs')).href)).toBe( + expect(createRequire(join(f.profile.dir, 'entry.cjs'))('@deepseek-ai/dsh-core')).toEqual({ marker: 2 }) + expect(resolveFrom('@deepseek-ai/dsh-core', pathToFileURL(join(f.profile.dir, 'entry.mjs')).href)).toBe( pathToFileURL(join(f.installed, 'index.js')).href, ) }) it('preserves the ESM and CommonJS behavior of an empty local package directory', async () => { const f = fixture() - mkdirSync(join(f.profile.dir, 'node_modules', 'resolution-lib'), { recursive: true }) + mkdirSync(join(f.profile.dir, 'node_modules', '@deepseek-ai/dsh-core'), { recursive: true }) const registration = installProfileResolution(await generationOf(f)) registrations.push(registration) - expect(createRequire(join(f.profile.dir, 'entry.cjs'))('resolution-lib')).toEqual({ marker: 1 }) + expect(createRequire(join(f.profile.dir, 'entry.cjs'))('@deepseek-ai/dsh-core')).toEqual({ marker: 1 }) expect(() => resolveFrom( - 'resolution-lib', pathToFileURL(join(f.profile.dir, 'entry.mjs')).href, + '@deepseek-ai/dsh-core', pathToFileURL(join(f.profile.dir, 'entry.mjs')).href, )).toThrow(/Cannot find/u) }) @@ -925,32 +980,32 @@ describe('profile resolution generation', { concurrent: false }, () => { it('keeps the generation ahead of packages above the shared fallback position', async () => { const f = fixture() - pkg(join(f.root, 'node_modules', 'resolution-lib'), 'resolution-lib', 2) + pkg(join(f.root, 'node_modules', '@deepseek-ai/dsh-core'), '@deepseek-ai/dsh-core', 2) const registration = installProfileResolution(await generationOf(f)) registrations.push(registration) const require = createRequire(join(f.profile.dir, 'entry.cjs')) - expect(require('resolution-lib')).toEqual({ marker: 1 }) - expect(await importFrom('resolution-lib', pathToFileURL(join(f.profile.dir, 'entry.mjs')).href)) + expect(require('@deepseek-ai/dsh-core')).toEqual({ marker: 1 }) + expect(await importFrom('@deepseek-ai/dsh-core', pathToFileURL(join(f.profile.dir, 'entry.mjs')).href)) .toMatchObject({ marker: 1 }) }) it('continues the original ancestor lookup after a generation subpath miss', async () => { const f = fixture() const home = join(f.root, 'home') - const profileDir = join(home, 'profiles', 'test') + const profileDir = join(home, 'profiles', 'web') const profile = { ...f.profile, dir: profileDir, patchPath: join(profileDir, 'cordis.patch.yml'), } - file(join(profileDir, 'package.json'), JSON.stringify({ name: 'test-profile', private: true })) - file(join(profileDir, 'node_modules', 'resolution-lib', 'package.json'), JSON.stringify({ - name: 'resolution-lib', version: '2.0.0', + file(join(profileDir, 'package.json'), JSON.stringify({ name: 'dsh-profile-web', private: true })) + file(join(profileDir, 'node_modules', '@deepseek-ai/dsh-core', 'package.json'), JSON.stringify({ + name: '@deepseek-ai/dsh-core', version: '2.0.0', })) file(join(f.installed, 'package.json'), JSON.stringify({ - name: 'resolution-lib', version: '1.0.0', type: 'module', main: './index.cjs', + name: '@deepseek-ai/dsh-core', version: '1.0.0', type: 'module', main: './index.cjs', })) - const ancestorSubpath = join(home, 'node_modules', 'resolution-lib', 'sub.cjs') + const ancestorSubpath = join(home, 'node_modules', '@deepseek-ai/dsh-core', 'sub.cjs') file(ancestorSubpath, 'module.exports = { marker: 3 }\n') const generation = await createProfileResolutionGeneration({ installAnchor: f.installAnchor, @@ -958,135 +1013,293 @@ describe('profile resolution generation', { concurrent: false }, () => { home, }) const require = createRequire(join(profileDir, 'entry.cjs')) - expect(() => require.resolve('resolution-lib')).toThrow(/Cannot find module/u) - expect(require.resolve('resolution-lib/sub.cjs')).toBe(ancestorSubpath) + expect(() => require.resolve('@deepseek-ai/dsh-core')).toThrow(/Cannot find module/u) + expect(require.resolve('@deepseek-ai/dsh-core/sub.cjs')).toBe(ancestorSubpath) const registration = installProfileResolution(generation) registrations.push(registration) - expect(require.resolve('resolution-lib')).toBe(join(f.installed, 'index.cjs')) - expect(require.resolve('resolution-lib/sub.cjs')).toBe(ancestorSubpath) + expect(require.resolve('@deepseek-ai/dsh-core')).toBe(join(f.installed, 'index.cjs')) + expect(require.resolve('@deepseek-ai/dsh-core/sub.cjs')).toBe(ancestorSubpath) }) it('continues explicit CommonJS paths after a generation subpath miss', async () => { const f = fixture() file(join(f.installed, 'package.json'), JSON.stringify({ - name: 'resolution-lib', version: '1.0.0', type: 'module', main: './index.cjs', + name: '@deepseek-ai/dsh-core', version: '1.0.0', type: 'module', main: './index.cjs', })) const alternative = join(f.root, 'alternative') - const alternativeSubpath = join(alternative, 'node_modules', 'resolution-lib', 'sub.cjs') + const alternativeSubpath = join(alternative, 'node_modules', '@deepseek-ai/dsh-core', 'sub.cjs') file(alternativeSubpath, 'module.exports = { marker: 4 }\n') const registration = installProfileResolution(await generationOf(f)) registrations.push(registration) - expect(createRequire(join(f.profile.dir, 'entry.cjs')).resolve('resolution-lib/sub.cjs', { + expect(createRequire(join(f.profile.dir, 'entry.cjs')).resolve('@deepseek-ai/dsh-core/sub.cjs', { paths: [f.profile.dir, alternative], })).toBe(alternativeSubpath) }) - it('skips stale shared and profile-owned fallback entries when the generation misses', async () => { + it('occupies the interception layer package directory for a generation hit and continues above it on a CommonJS subpath miss', async () => { const f = fixture() - pkg(join(f.root, 'profiles', 'node_modules', 'stale-shared'), 'stale-shared', 9) - pkg(join(f.root, 'node_modules', 'stale-shared'), 'stale-shared', 3) - const target = join(f.root, 'stale-private-target') - const owned = join(f.profile.dir, '.dsh-module-fallback', 'node_modules', 'stale-private') - const projected = join(f.profile.dir, 'node_modules', 'stale-private') - pkg(target, 'stale-private', 9) - mkdirSync(dirname(owned), { recursive: true }) - symlinkSync(target, owned, process.platform === 'win32' ? 'junction' : 'dir') - mkdirSync(dirname(projected), { recursive: true }) - symlinkSync(owned, projected, process.platform === 'win32' ? 'junction' : 'dir') - pkg(join(f.root, 'node_modules', 'stale-private'), 'stale-private', 3) + file(join(f.installed, 'package.json'), JSON.stringify({ + name: '@deepseek-ai/dsh-core', version: '1.0.0', main: './index.cjs', + })) + // The generation entry stands in for profiles/node_modules/@deepseek-ai/dsh-core, so this stale copy is never read. + const shared = join(f.root, 'profiles', 'node_modules', '@deepseek-ai/dsh-core') + file(join(shared, 'package.json'), JSON.stringify({ name: '@deepseek-ai/dsh-core', version: '9.0.0' })) + file(join(shared, 'sub.cjs'), 'module.exports = { marker: 9 }\n') + file(join(shared, 'sub.js'), 'module.exports = { marker: 9 }\n') + const home = join(f.root, 'node_modules', '@deepseek-ai/dsh-core') + file(join(home, 'package.json'), JSON.stringify({ name: '@deepseek-ai/dsh-core', version: '3.0.0' })) + file(join(home, 'sub.cjs'), 'module.exports = { marker: 3 }\n') + // A name without an entry sees the physical directory at the same layer. + pkg(join(f.root, 'profiles', 'node_modules', 'left-pad'), 'left-pad', 9) + pkg(join(f.root, 'node_modules', 'left-pad'), 'left-pad', 3) const registration = installProfileResolution(await generationOf(f)) registrations.push(registration) const require = createRequire(join(f.profile.dir, 'entry.cjs')) - for (const name of ['stale-shared', 'stale-private']) { - expect(require(name)).toEqual({ marker: 3 }) - expect(require.resolve(name, { paths: [f.profile.dir] })).toBe(join(f.root, 'node_modules', name, 'index.cjs')) - expect(await importFrom(name, pathToFileURL(join(f.profile.dir, `${name}.mjs`)).href)) - .toMatchObject({ marker: 3 }) - } + expect(require.resolve('@deepseek-ai/dsh-core')).toBe(join(f.installed, 'index.cjs')) + expect(require.resolve('@deepseek-ai/dsh-core/sub.cjs')).toBe(join(home, 'sub.cjs')) + expect(require('@deepseek-ai/dsh-core/sub.cjs')).toEqual({ marker: 3 }) + expect(require.resolve('@deepseek-ai/dsh-core/sub.cjs', { paths: [f.profile.dir] })).toBe(join(home, 'sub.cjs')) + expect(require('left-pad')).toEqual({ marker: 9 }) + // ESM resolves the subpath inside the selected package and never consults another copy. + const parent = pathToFileURL(join(f.profile.dir, 'entry.mjs')).href + expect(resolveFrom('@deepseek-ai/dsh-core', parent)).toBe(pathToFileURL(join(f.installed, 'index.cjs')).href) + await expect(importFrom('@deepseek-ai/dsh-core/sub.js', parent)).rejects.toMatchObject({ code: 'ERR_MODULE_NOT_FOUND' }) + expect(await importFrom('left-pad', parent)).toMatchObject({ marker: 9 }) }) - it('skips stale shared and declared profile projections when the generation supplies the package', async () => { - const f = fixture() + it.each(['profile', 'plugin'] as const)('uses the installation DSH package from a %s while a stale shared link remains', async (importer) => { + const name = '@deepseek-ai/dsh-core' + const f = fixture(name) file(join(f.profile.dir, 'package.json'), JSON.stringify({ - name: 'test-profile', private: true, dependencies: { 'resolution-lib': '*' }, - imports: { '#library': 'resolution-lib' }, + name: 'dsh-profile-web', private: true, dependencies: { [name]: '*' }, + imports: { '#library': name }, })) - const target = join(f.root, 'stale-target') - pkg(target, 'resolution-lib', 9) - const owned = join(f.profile.dir, '.dsh-module-fallback', 'node_modules', 'resolution-lib') - const projected = join(f.profile.dir, 'node_modules', 'resolution-lib') - const shared = join(f.root, 'profiles', 'node_modules', 'resolution-lib') - mkdirSync(dirname(owned), { recursive: true }) - mkdirSync(dirname(projected), { recursive: true }) + const parentDir = importer === 'profile' ? f.profile.dir : join(f.profile.dir, 'node_modules', 'my-plugin') + if (importer === 'plugin') { + pkg(parentDir, 'my-plugin', 1, { [name]: '*' }) + file(join(parentDir, 'package.json'), JSON.stringify({ + name: 'my-plugin', type: 'module', dependencies: { [name]: '*' }, imports: { '#library': name }, + })) + } + const target = join(f.root, 'old-dsh', 'node_modules', name) + pkg(target, name, 9) + const shared = join(f.root, 'profiles', 'node_modules', name) mkdirSync(dirname(shared), { recursive: true }) - symlinkSync(target, owned, process.platform === 'win32' ? 'junction' : 'dir') - symlinkSync(owned, projected, process.platform === 'win32' ? 'junction' : 'dir') symlinkSync(target, shared, process.platform === 'win32' ? 'junction' : 'dir') const generation = await generationOf(f) expect(generation.localPackageNames).toEqual([]) const registration = installProfileResolution(generation) registrations.push(registration) - const require = createRequire(join(f.profile.dir, 'entry.cjs')) - for (const name of ['resolution-lib', '#library']) { - expect(require(name)).toEqual({ marker: 1 }) - expect(await importFrom(name, pathToFileURL(join(f.profile.dir, 'entry.mjs')).href)) + const require = createRequire(join(parentDir, 'entry.cjs')) + const parent = pathToFileURL(join(parentDir, 'entry.mjs')).href + for (const specifier of [name, '#library']) { + expect(require(specifier)).toEqual({ marker: 1 }) + expect(require.resolve(specifier)).toBe(join(f.installed, 'index.cjs')) + expect(resolveFrom(specifier, parent)).toBe(pathToFileURL(join(f.installed, 'index.js')).href) + expect(await importFrom(specifier, parent)) .toMatchObject({ marker: 1 }) } - expect(realpathSync(projected)).toBe(target) + expect(registration.packageDir(name, parent)).toBe(f.installed) expect(realpathSync(shared)).toBe(target) }) - it('excludes profile-owned projections from selected bundle dependency traversal', async () => { + it('treats a profile symlink into .dsh-module-fallback as an ordinary local package', async () => { const f = fixture() - const bundle = join(f.profile.dir, 'node_modules', 'selected-bundle') - pkg(bundle, 'selected-bundle', 1, { 'bundle-leaf': '*' }) - f.profile.layers.push({ - packageName: 'selected-bundle', packageDir: bundle, - patchPath: join(bundle, 'cordis.patch.yml'), patches: [], - }) - const target = join(f.root, 'stale-leaf') - const owned = join(f.profile.dir, '.dsh-module-fallback', 'node_modules', 'bundle-leaf') - const projected = join(f.profile.dir, 'node_modules', 'bundle-leaf') - pkg(target, 'bundle-leaf', 9) + file(join(f.profile.dir, 'package.json'), JSON.stringify({ + name: 'dsh-profile-web', private: true, dependencies: { '@deepseek-ai/dsh-core': '*' }, + })) + const target = join(f.root, 'old-dsh', 'node_modules', '@deepseek-ai', 'dsh-core') + pkg(target, '@deepseek-ai/dsh-core', 9) + const owned = join(f.profile.dir, '.dsh-module-fallback', 'node_modules', '@deepseek-ai/dsh-core') + const projected = join(f.profile.dir, 'node_modules', '@deepseek-ai/dsh-core') mkdirSync(dirname(owned), { recursive: true }) + mkdirSync(dirname(projected), { recursive: true }) symlinkSync(target, owned, process.platform === 'win32' ? 'junction' : 'dir') symlinkSync(owned, projected, process.platform === 'win32' ? 'junction' : 'dir') - const ancestor = join(f.root, 'node_modules', 'bundle-leaf') - pkg(ancestor, 'bundle-leaf', 3) const generation = await generationOf(f) - expect(generation.entries.find(entry => entry.name === 'bundle-leaf')) - .toMatchObject({ packageDir: ancestor, version: '3.0.0', scope: 'profile' }) + expect(generation.localPackageNames).toEqual(['@deepseek-ai/dsh-core']) + const registration = installProfileResolution(generation) + registrations.push(registration) + const require = createRequire(join(f.profile.dir, 'entry.cjs')) + const parent = pathToFileURL(join(f.profile.dir, 'entry.mjs')).href + expect(require('@deepseek-ai/dsh-core')).toEqual({ marker: 9 }) + expect(require.resolve('@deepseek-ai/dsh-core')).toBe(join(target, 'index.cjs')) + expect(resolveFrom('@deepseek-ai/dsh-core', parent)).toBe(pathToFileURL(join(target, 'index.js')).href) + expect(await importFrom('@deepseek-ai/dsh-core', parent)).toMatchObject({ marker: 9 }) + expect(registration.packageDir('@deepseek-ai/dsh-core', parent)).toBe(projected) expect(realpathSync(projected)).toBe(target) }) - it('continues after a canonicalized profiles directory from the matching parent tree', async () => { + it.each(['directory', 'symlink'] as const)('resolves an unlisted shared %s and its nested dependencies without changing profile files', async (layout) => { + const f = fixture() + const plugin = 'my-plugin' + const dependency = 'external-dependency' + const middle = 'external-middle' + const leaf = 'external-leaf' + const pluginDir = join(f.profile.dir, 'node_modules', plugin) + const shared = join(f.root, 'profiles', 'node_modules', dependency) + const selected = layout === 'directory' ? shared : join(f.root, 'workspace', dependency) + const middleDir = join(selected, 'node_modules', middle) + const leafDir = join(middleDir, 'node_modules', leaf) + file(join(f.profile.dir, 'package.json'), JSON.stringify({ + name: 'dsh-profile-web', private: true, dependencies: { [plugin]: '*' }, + })) + pkg(pluginDir, plugin, 1, { [dependency]: '*' }) + pkg(selected, dependency, 2, { [middle]: '*' }) + pkg(middleDir, middle, 3, { [leaf]: '*' }) + pkg(leafDir, leaf, 4) + pkg(join(selected, 'node_modules', leaf), leaf, 8) + pkg(join(f.root, 'node_modules', dependency), dependency, 9) + file(join(pluginDir, 'index.js'), `import * as external from '${dependency}'\nexport { external }\n`) + file(join(pluginDir, 'index.cjs'), `module.exports = { external: require('${dependency}') }\n`) + for (const [dir, child] of [[selected, middle], [middleDir, leaf]] as const) { + file(join(dir, 'index.js'), `import * as child from '${child}'\nexport { child }\nexport const url = import.meta.url\n`) + file(join(dir, 'index.cjs'), `module.exports = { filename: __filename, child: require('${child}') }\n`) + } + file(join(leafDir, 'index.js'), 'export const marker = 4\nexport const url = import.meta.url\n') + file(join(leafDir, 'index.cjs'), 'module.exports = { marker: 4, filename: __filename }\n') + if (layout === 'symlink') { + mkdirSync(dirname(shared), { recursive: true }) + symlinkSync(selected, shared, process.platform === 'win32' ? 'junction' : 'dir') + } + const generation = await generationOf(f) + expect(generation.entries.some(entry => [dependency, middle, leaf].includes(entry.name))).toBe(false) + const registration = installProfileResolution(generation) + registrations.push(registration) + const require = createRequire(join(f.profile.dir, 'entry.cjs')) + const parent = pathToFileURL(join(f.profile.dir, 'entry.mjs')).href + expect(require(plugin)).toEqual({ + external: { filename: join(selected, 'index.cjs'), child: { + filename: join(middleDir, 'index.cjs'), child: { marker: 4, filename: join(leafDir, 'index.cjs') }, + } }, + }) + expect(await importFrom(plugin, parent)).toMatchObject({ + external: { url: pathToFileURL(join(selected, 'index.js')).href, child: { + url: pathToFileURL(join(middleDir, 'index.js')).href, + child: { marker: 4, url: pathToFileURL(join(leafDir, 'index.js')).href }, + } }, + }) + expect(require.resolve(dependency, { paths: [pluginDir] })).toBe(join(selected, 'index.cjs')) + expect(registration.packageDir(dependency, pathToFileURL(join(pluginDir, 'index.js')).href)).toBe(shared) + expect(realpathSync(shared)).toBe(selected) + expect(existsSync(join(pluginDir, 'package.json'))).toBe(true) + expect(existsSync(join(leafDir, 'package.json'))).toBe(true) + }) + + it('follows profile node_modules symlinks during selected bundle dependency traversal', async () => { + const f = fixture() + const bundle = join(f.profile.dir, 'node_modules', 'my-bundle') + pkg(bundle, 'my-bundle', 1, { 'bridge': '*' }) + f.profile.layers.push({ + packageName: 'my-bundle', packageDir: bundle, + patchPath: join(bundle, 'cordis.patch.yml'), patches: [], + }) + const target = join(f.root, 'old-dsh', 'node_modules', 'bridge') + const owned = join(f.profile.dir, '.dsh-module-fallback', 'node_modules', 'bridge') + const projected = join(f.profile.dir, 'node_modules', 'bridge') + pkg(target, 'bridge', 9) + mkdirSync(dirname(owned), { recursive: true }) + symlinkSync(target, owned, process.platform === 'win32' ? 'junction' : 'dir') + symlinkSync(owned, projected, process.platform === 'win32' ? 'junction' : 'dir') + const ancestor = join(f.root, 'node_modules', 'bridge') + pkg(ancestor, 'bridge', 3) + const generation = await generationOf(f) + expect(generation.entries.find(entry => entry.name === 'bridge')) + .toMatchObject({ packageDir: projected, version: '9.0.0', scope: 'profile' }) + expect(realpathSync(projected)).toBe(target) + }) + + it.each(lookupMatrix())('$title', async (matrixCase) => { + const { importer, kind, layers, form } = matrixCase + const name = { installation: '@deepseek-ai/dsh-core', bundle: 'bridge', unlisted: 'left-pad' }[kind] + const f = fixture() + const pluginDir = join(f.profile.dir, 'node_modules', 'my-plugin') + pkg(pluginDir, 'my-plugin', 0) + const bundleDir = join(f.profile.dir, 'node_modules', 'my-bundle') + pkg(bundleDir, 'my-bundle', 0, { 'bridge': '*' }) + const bundleLib = join(bundleDir, 'node_modules', 'bridge') + pkg(bundleLib, 'bridge', 2) + f.profile.layers.push({ + packageName: 'my-bundle', packageDir: bundleDir, + patchPath: join(bundleDir, 'cordis.patch.yml'), patches: [], + }) + file(join(f.profile.dir, 'package.json'), JSON.stringify({ + name: 'dsh-profile-web', private: true, dependencies: { 'my-plugin': '*', 'my-bundle': '*' }, + })) + const logical: Record = { + private: join(pluginDir, 'node_modules', name), + profile: join(f.profile.dir, 'node_modules', name), + shared: join(f.root, 'profiles', 'node_modules', name), + home: join(f.root, 'node_modules', name), + } + const real = { ...logical } + for (const layer of layers) { + if (form === 'symlink' && (layer === 'profile' || layer === 'shared')) { + real[layer] = join(f.root, 'linked', layer, name) + pkg(real[layer], name, LOOKUP_MARKERS[layer]) + mkdirSync(dirname(logical[layer]), { recursive: true }) + symlinkSync(real[layer], logical[layer], process.platform === 'win32' ? 'junction' : 'dir') + } else { + pkg(logical[layer], name, LOOKUP_MARKERS[layer]) + } + } + const generation = await generationOf(f) + expect(generation.entries.find(entry => entry.name === name)?.scope) + .toBe(kind === 'installation' ? 'installation' : kind === 'bundle' ? 'profile' : undefined) + const registration = installProfileResolution(generation) + registrations.push(registration) + const importerDir = importer === 'plugin' ? pluginDir : f.profile.dir + const require = createRequire(join(importerDir, 'entry.cjs')) + const parent = pathToFileURL(join(importerDir, 'entry.mjs')).href + const winner = lookupWinner(matrixCase) + if (winner === 'missing') { + expect(thrownError(() => require(name))).toMatchObject({ code: 'MODULE_NOT_FOUND' }) + expect(thrownError(() => require.resolve(name, { paths: [importerDir] }))).toMatchObject({ code: 'MODULE_NOT_FOUND' }) + await expect(importFrom(name, parent)).rejects.toMatchObject({ code: 'ERR_MODULE_NOT_FOUND' }) + expect(registration.packageDir(name, parent)).toBeUndefined() + return + } + const packageDir = winner === 'generation' + ? (kind === 'installation' ? f.installed : bundleLib) + : logical[winner] + const realDir = winner === 'generation' ? packageDir : real[winner] + const marker = winner === 'generation' ? (kind === 'installation' ? 1 : 2) : LOOKUP_MARKERS[winner] + expect(require(name)).toEqual({ marker }) + expect(require.resolve(name)).toBe(join(realDir, 'index.cjs')) + expect(require.resolve(name, { paths: [importerDir] })).toBe(join(realDir, 'index.cjs')) + expect(resolveFrom(name, parent)).toBe(pathToFileURL(join(realDir, 'index.js')).href) + expect(await importFrom(name, parent)).toMatchObject({ marker }) + expect(registration.packageDir(name, parent)).toBe(packageDir) + }) + + it('uses shared packages in the canonicalized profiles tree before its ancestors', async () => { const root = realpathSync(mkdtempSync(join(tmpdir(), 'dsh-profile-generation-symlink-'))) roots.push(root) const carrier = join(root, 'carrier') const profilesDir = join(root, 'home', 'profiles') const realProfilesDir = join(carrier, 'profiles') - const realProfileDir = join(realProfilesDir, 'test') + const realProfileDir = join(realProfilesDir, 'web') mkdirSync(realProfileDir, { recursive: true }) mkdirSync(dirname(profilesDir), { recursive: true }) symlinkSync(realProfilesDir, profilesDir, process.platform === 'win32' ? 'junction' : 'dir') - pkg(join(realProfilesDir, 'node_modules', 'stale-only'), 'stale-only', 9) - const ancestor = join(carrier, 'node_modules', 'stale-only') - pkg(ancestor, 'stale-only', 3) - pkg(join(root, 'home', 'node_modules', 'stale-only'), 'stale-only', 4) + pkg(join(realProfilesDir, 'node_modules', 'left-pad'), 'left-pad', 9) + const ancestor = join(carrier, 'node_modules', 'left-pad') + pkg(ancestor, 'left-pad', 3) + pkg(join(root, 'home', 'node_modules', 'left-pad'), 'left-pad', 4) const registration = installProfileResolution({ profilesDir, - profileDir: join(profilesDir, 'test'), + profileDir: join(profilesDir, 'web'), localPackageNames: [], entries: [], }) registrations.push(registration) const require = createRequire(join(realProfileDir, 'entry.cjs')) - expect(require('stale-only')).toEqual({ marker: 3 }) + expect(require('left-pad')).toEqual({ marker: 9 }) const parent = pathToFileURL(join(realProfileDir, 'entry.mjs')).href - expect(await importFrom('stale-only', parent)).toMatchObject({ marker: 3 }) - expect(registration.packageDir('stale-only', parent)).toBe(ancestor) + expect(await importFrom('left-pad', parent)).toMatchObject({ marker: 9 }) + expect(registration.packageDir('left-pad', parent)).toBe(join(realProfilesDir, 'node_modules', 'left-pad')) }) it('leaves conditional exports to Node', async () => { @@ -1173,14 +1386,14 @@ describe('profile resolution generation', { concurrent: false }, () => { parent.paths = internal.Module._nodeModulePaths(f.profile.dir) const originalChildren = [...parent.children] - expect(internal.Module._resolveFilename('resolution-lib', parent, false)).toBe(join(f.installed, 'index.cjs')) + expect(internal.Module._resolveFilename('@deepseek-ai/dsh-core', parent, false)).toBe(join(f.installed, 'index.cjs')) expect(parent.children).toEqual(originalChildren) }) it('reports routed CommonJS failures with the native require stack', async () => { const f = fixture() file(join(f.installed, 'package.json'), JSON.stringify({ - name: 'resolution-lib', version: '1.0.0', type: 'module', main: './index.cjs', + name: '@deepseek-ai/dsh-core', version: '1.0.0', type: 'module', main: './index.cjs', })) const generation = await createProfileResolutionGeneration({ installAnchor: f.installAnchor, @@ -1188,11 +1401,11 @@ describe('profile resolution generation', { concurrent: false }, () => { home: f.root, }) const nativeRequire = createRequire(join(f.profile.dir, 'entry-native.cjs')) - const nativeError = thrownError(() => nativeRequire.resolve('resolution-lib/missing.cjs')) + const nativeError = thrownError(() => nativeRequire.resolve('@deepseek-ai/dsh-core/missing.cjs')) const registration = installProfileResolution(generation) registrations.push(registration) const runtimeRequire = createRequire(join(f.profile.dir, 'entry-runtime.cjs')) - const runtimeError = thrownError(() => runtimeRequire.resolve('resolution-lib/missing.cjs')) + const runtimeError = thrownError(() => runtimeRequire.resolve('@deepseek-ai/dsh-core/missing.cjs')) expect(runtimeError.requireStack).toEqual([ join(f.profile.dir, 'entry-runtime.cjs'), @@ -1214,7 +1427,7 @@ describe('profile resolution generation', { concurrent: false }, () => { registrations.push(registration) expect(thrownMessage(() => resolveFrom('unavailable-lib', parent))).toBe(nativeMessage) - expect(thrownMessage(() => resolveFrom('resolution-lib/private', parent))) + expect(thrownMessage(() => resolveFrom('@deepseek-ai/dsh-core/private', parent))) .toContain(` imported from ${fileURLToPath(parent)}`) }) @@ -1227,12 +1440,12 @@ describe('profile resolution generation', { concurrent: false }, () => { }) file(join(f.installed, 'package.json'), '{') const parent = pathToFileURL(join(f.profile.dir, 'entry.mjs')).href - expect(() => resolveFrom('resolution-lib', pathToFileURL(f.installAnchor).href)) + expect(() => resolveFrom('@deepseek-ai/dsh-core', pathToFileURL(f.installAnchor).href)) .toThrow(/Invalid package config/u) const registration = installProfileResolution(generation) registrations.push(registration) - expect(() => resolveFrom('resolution-lib', parent)).toThrow(/Invalid package config/u) + expect(() => resolveFrom('@deepseek-ai/dsh-core', parent)).toThrow(/Invalid package config/u) }) it('does not fall back after Node selects a broken profile-local package', async () => { @@ -1253,28 +1466,28 @@ describe('profile resolution generation', { concurrent: false }, () => { it('does not fall back after the generation selects a missing exports target', async () => { const f = fixture() file(join(f.installed, 'package.json'), JSON.stringify({ - name: 'resolution-lib', version: '1.0.0', type: 'module', exports: './missing.cjs', + name: '@deepseek-ai/dsh-core', version: '1.0.0', type: 'module', exports: './missing.cjs', })) - pkg(join(f.root, 'node_modules', 'resolution-lib'), 'resolution-lib', 2) + pkg(join(f.root, 'node_modules', '@deepseek-ai/dsh-core'), '@deepseek-ai/dsh-core', 2) const registration = installProfileResolution(await generationOf(f)) registrations.push(registration) - expect(() => createRequire(join(f.profile.dir, 'entry.cjs')).resolve('resolution-lib')) + expect(() => createRequire(join(f.profile.dir, 'entry.cjs')).resolve('@deepseek-ai/dsh-core')) .toThrow(/Cannot find module/u) }) it('does not fall back after the generation selects a missing legacy main', async () => { const f = fixture() file(join(f.installed, 'package.json'), JSON.stringify({ - name: 'resolution-lib', version: '1.0.0', type: 'module', main: './missing.cjs', + name: '@deepseek-ai/dsh-core', version: '1.0.0', type: 'module', main: './missing.cjs', })) unlinkSync(join(f.installed, 'index.cjs')) unlinkSync(join(f.installed, 'index.js')) - pkg(join(f.root, 'node_modules', 'resolution-lib'), 'resolution-lib', 2) + pkg(join(f.root, 'node_modules', '@deepseek-ai/dsh-core'), '@deepseek-ai/dsh-core', 2) const registration = installProfileResolution(await generationOf(f)) registrations.push(registration) - expect(() => createRequire(join(f.profile.dir, 'entry.cjs')).resolve('resolution-lib')) + expect(() => createRequire(join(f.profile.dir, 'entry.cjs')).resolve('@deepseek-ai/dsh-core')) .toThrow(/valid "main" entry/u) }) @@ -1303,21 +1516,21 @@ describe('profile resolution generation', { concurrent: false }, () => { it('rejects changing an existing package mapping without publishing it', async () => { const f = fixture() const second = join(f.root, 'second') - pkg(second, 'resolution-lib', 2) + pkg(second, '@deepseek-ai/dsh-core', 2) const first = await generationOf(f) const registration = installProfileResolution(first) registrations.push(registration) - const alias = join(f.root, 'resolution-lib-alias') + const alias = join(f.root, 'dsh-core-alias') symlinkSync(f.installed, alias, process.platform === 'win32' ? 'junction' : 'dir') registration.replace({ ...first, - entries: first.entries.map(entry => entry.name === 'resolution-lib' + entries: first.entries.map(entry => entry.name === '@deepseek-ai/dsh-core' ? { ...entry, packageDir: alias } : entry), }) const changed = { ...first, - entries: first.entries.map(entry => entry.name === 'resolution-lib' + entries: first.entries.map(entry => entry.name === '@deepseek-ai/dsh-core' ? { ...entry, packageDir: second, version: '2.0.0', declarer: join(second, 'package.json') } : entry), } @@ -1325,16 +1538,16 @@ describe('profile resolution generation', { concurrent: false }, () => { expect(() => { registration.replace({ ...first, - entries: first.entries.map(entry => entry.name === 'resolution-lib' + entries: first.entries.map(entry => entry.name === '@deepseek-ai/dsh-core' ? { ...entry, version: '9.0.0' } : entry), }) }).toThrow(/requires a process restart/u) expect(() => { - registration.replace({ ...first, localPackageNames: ['resolution-lib'] }) + registration.replace({ ...first, localPackageNames: ['@deepseek-ai/dsh-core'] }) }).toThrow(/requires a process restart/u) expect(() => { - registration.replace({ ...first, entries: first.entries.filter(entry => entry.name !== 'resolution-lib') }) + registration.replace({ ...first, entries: first.entries.filter(entry => entry.name !== '@deepseek-ai/dsh-core') }) }).toThrow(/requires a process restart/u) expect(() => { registration.replace({ ...first, profilesDir: join(f.root, 'other-profiles') }) @@ -1343,7 +1556,7 @@ describe('profile resolution generation', { concurrent: false }, () => { registration.replace({ ...first, localPackageNames: ['new-local'] }) expect(() => { registration.replace(first) }).toThrow(/removing local package/u) expect(registration.packageDir( - 'resolution-lib', pathToFileURL(join(f.profile.dir, 'entry.mjs')).href, + '@deepseek-ai/dsh-core', pathToFileURL(join(f.profile.dir, 'entry.mjs')).href, )).toBe(f.installed) }) @@ -1367,7 +1580,7 @@ describe('profile resolution generation', { concurrent: false }, () => { expect(registration.packageDir('#internal', profileParent)).toBeUndefined() expect(registration.packageDir('@scope', profileParent)).toBeUndefined() expect(registration.packageDir('node:fs', profileParent)).toBeUndefined() - expect(registration.packageDir('resolution-lib/private', profileParent)).toBe(f.installed) + expect(registration.packageDir('@deepseek-ai/dsh-core/private', profileParent)).toBe(f.installed) expect(registration.packageDir('outside-lib', outsideParent)).toBe(outsidePackage) expect(registration.packageDir('outside-lib', outsideParent)).toBe(outsidePackage) expect(registration.packageDir('@scope/outside', outsideParent)).toBe(scopedPackage) @@ -1390,7 +1603,7 @@ describe('profile resolution generation', { concurrent: false }, () => { home: f.root, })).rejects.toThrow() expect(registration.packageDir( - 'resolution-lib', pathToFileURL(join(f.profile.dir, 'entry.mjs')).href, + '@deepseek-ai/dsh-core', pathToFileURL(join(f.profile.dir, 'entry.mjs')).href, )).toBe(f.installed) }) @@ -1413,9 +1626,9 @@ describe('profile resolution generation', { concurrent: false }, () => { const registration = installProfileResolution(await generationOf(f)) registrations.push(registration) const require = createRequire(join(f.profile.dir, 'entry.cjs')) - expect(require.resolve('resolution-lib')).toBe(join(f.installed, 'index.cjs')) + expect(require.resolve('@deepseek-ai/dsh-core')).toBe(join(f.installed, 'index.cjs')) registration.dispose() registrations.pop() - expect(() => { require.resolve('resolution-lib') }).toThrow(/Cannot find module/u) + expect(() => { require.resolve('@deepseek-ai/dsh-core') }).toThrow(/Cannot find module/u) }) })