diff --git a/API-FRICTION.md b/API-FRICTION.md index 575de2b0..60c6700a 100644 --- a/API-FRICTION.md +++ b/API-FRICTION.md @@ -7680,3 +7680,14 @@ Each entry records: retention, and passes bare React Native and Expo Metro. The full 1,756-test unit matrix, root TypeScript, package artifact, documentation, and bundle-size gates pass. +- Release evidence: release run `31340548562` built every `0.9.0` artifact but + the new core-only consumer performed a second unlocked resolution. Its + `@types/d3-geo` range selected `3.1.1` while the frozen workspace install had + populated `3.1.0`, so the offline gate failed before npm publishing. +- Release decision: preserve public dependency ranges and the offline boundary. + Derive fixture overrides from every packed core dependency, link them to the + setup-populated core workspace, and give the fixture an isolated empty pnpm + store so a missing override cannot pass from a developer cache. +- Release verification: the focused contract covers ranged and unscoped + dependencies. All 12 `0.9.0` release artifacts pass with the unified fixture + installing from its isolated store. diff --git a/scripts/ci-workflow.test.mjs b/scripts/ci-workflow.test.mjs index 3d157f96..8c3921a1 100644 --- a/scripts/ci-workflow.test.mjs +++ b/scripts/ci-workflow.test.mjs @@ -26,6 +26,10 @@ const packedConsumer = await readFile( resolve(import.meta.dirname, './check-packed-consumers.mjs'), 'utf8', ) +const unifiedArtifact = await readFile( + resolve(import.meta.dirname, './unified-package-artifact.mjs'), + 'utf8', +) const nxDistribution = await readFile( resolve(import.meta.dirname, '../.nx/workflows/distribution.yaml'), 'utf8', @@ -86,6 +90,20 @@ describe('CI workflow contract', () => { ).length, 1, ) + assert.match(unifiedArtifact, /'autoInstallPeers: false'/) + assert.match(unifiedArtifact, /linkedUnifiedConsumerDependencies\(/) + assert.match( + unifiedArtifact, + /\[\s*'install',\s*'--offline',\s*'--ignore-scripts',\s*'--frozen-lockfile=false',\s*'--store-dir'/, + ) + for (const directory of [ + 'XDG_CACHE_HOME', + 'XDG_DATA_HOME', + 'XDG_STATE_HOME', + ]) { + assert.match(unifiedArtifact, new RegExp(`${directory}: resolve\\(`)) + } + assert.doesNotMatch(unifiedArtifact, /--prefer-offline/) }) test('starts static checks immediately and gates expensive pull request partitions', () => { diff --git a/scripts/unified-package-artifact.mjs b/scripts/unified-package-artifact.mjs index 07fc109b..416e6bd6 100644 --- a/scripts/unified-package-artifact.mjs +++ b/scripts/unified-package-artifact.mjs @@ -118,6 +118,43 @@ export function mappedUnifiedExportConditions(namespace, conditions) { ) } +export function linkedUnifiedConsumerDependencies({ + repositoryRoot, + packageDirectory, + dependencies, +}) { + return Object.fromEntries( + Object.keys(dependencies ?? {}) + .sort() + .map((packageName) => [ + packageName, + `link:${resolve( + repositoryRoot, + 'packages', + packageDirectory, + 'node_modules', + ...packageName.split('/'), + )}`, + ]), + ) +} + +export function unifiedConsumerWorkspace(linkedDependencies) { + const overrides = Object.entries(linkedDependencies).map( + ([packageName, target]) => + ` ${JSON.stringify(packageName)}: ${JSON.stringify(target)}`, + ) + assert.ok(overrides.length > 0, '@tanstack/charts must declare dependencies') + return [ + 'packages:', + " - '.'", + 'autoInstallPeers: false', + 'overrides:', + ...overrides, + '', + ].join('\n') +} + export function validateUnifiedCoreExports(coreExports, sourceManifests) { assert.ok(coreExports && typeof coreExports === 'object') @@ -252,6 +289,18 @@ export async function verifyUnifiedCoreArtifact({ await validateNoNestedPackageManifests(resolve(packedRoot, 'dist')) await validateNoLegacyRuntimeImports(resolve(packedRoot, 'dist')) + const linkedDependencies = linkedUnifiedConsumerDependencies({ + repositoryRoot, + packageDirectory: coreInfo.directory, + dependencies: packedManifest.dependencies, + }) + for (const [packageName, target] of Object.entries(linkedDependencies)) { + assert.ok( + (await stat(target.slice('link:'.length))).isDirectory(), + `Workspace install omitted ${packageName}`, + ) + } + await mkdir(fixtureRoot, { recursive: true }) await writeFile( resolve(fixtureRoot, 'package.json'), @@ -270,13 +319,25 @@ export async function verifyUnifiedCoreArtifact({ ) await writeFile( resolve(fixtureRoot, 'pnpm-workspace.yaml'), - "packages:\n - '.'\nautoInstallPeers: false\n", + unifiedConsumerWorkspace(linkedDependencies), ) await run( 'pnpm', - ['install', '--offline', '--ignore-scripts', '--frozen-lockfile=false'], + [ + 'install', + '--offline', + '--ignore-scripts', + '--frozen-lockfile=false', + '--store-dir', + resolve(temporaryRoot, 'store'), + ], fixtureRoot, - { npm_config_offline: 'true' }, + { + npm_config_offline: 'true', + XDG_CACHE_HOME: resolve(temporaryRoot, 'cache'), + XDG_DATA_HOME: resolve(temporaryRoot, 'data'), + XDG_STATE_HOME: resolve(temporaryRoot, 'state'), + }, ) const installedScope = resolve(fixtureRoot, 'node_modules', '@tanstack') diff --git a/scripts/unified-package-artifact.test.mjs b/scripts/unified-package-artifact.test.mjs index c5df689d..d9c82956 100644 --- a/scripts/unified-package-artifact.test.mjs +++ b/scripts/unified-package-artifact.test.mjs @@ -1,8 +1,10 @@ import { describe, expect, it } from 'vitest' import { isUnifiedCoreExport, + linkedUnifiedConsumerDependencies, mappedUnifiedExportConditions, mappedUnifiedExportKey, + unifiedConsumerWorkspace, unifiedPackageSources, validateUnifiedCoreExports, } from './unified-package-artifact.mjs' @@ -75,4 +77,24 @@ describe('unified package export mapping', () => { '@tanstack/charts/react exports drifted from @tanstack/react-charts', ) }) + + it('links every packed dependency without resolving its published range', () => { + const linkedDependencies = linkedUnifiedConsumerDependencies({ + repositoryRoot: '/workspace', + packageDirectory: 'charts-core', + dependencies: { + tslib: '^2.8.1', + '@types/d3-geo': '^3.1.0', + }, + }) + + expect(linkedDependencies).toEqual({ + '@types/d3-geo': + 'link:/workspace/packages/charts-core/node_modules/@types/d3-geo', + tslib: 'link:/workspace/packages/charts-core/node_modules/tslib', + }) + expect(unifiedConsumerWorkspace(linkedDependencies)).toBe( + `packages:\n - '.'\nautoInstallPeers: false\noverrides:\n "@types/d3-geo": "link:/workspace/packages/charts-core/node_modules/@types/d3-geo"\n "tslib": "link:/workspace/packages/charts-core/node_modules/tslib"\n`, + ) + }) })