or run

npx @tessl/cli init
Log in

Version

Tile

Overview

Evals

Files

Files

docs

index.mdmetadata-management.mdpackage-resolution.mdversion-selection.md

version-selection.mddocs/

0

# Version Selection

1

2

The version selection system handles intelligent package version resolution based on preferences, constraints, and semantic versioning rules with support for tags, ranges, and exact versions.

3

4

## Capabilities

5

6

### Package Specification Types

7

8

Core types used for version resolution within the resolver.

9

10

```typescript { .api }

11

/**

12

* Structured package specification for version resolution

13

*/

14

interface RegistryPackageSpec {

15

/** Type of specification */

16

type: 'tag' | 'version' | 'range';

17

/** Package name */

18

name: string;

19

/** Specification string to match against */

20

fetchSpec: string;

21

/** Normalized preference for caching */

22

normalizedPref?: string;

23

}

24

```

25

26

### Version Preference Configuration

27

28

Configure preferred versions for specific packages during resolution.

29

30

```typescript { .api }

31

/**

32

* Version preference configuration for specific packages

33

*/

34

interface PreferredVersions {

35

[packageName: string]: {

36

/** Version selector (exact version, range, or tag) */

37

selector: string;

38

/** Type of version selector */

39

type: 'version' | 'range' | 'tag';

40

};

41

}

42

43

/**

44

* Options for package resolution

45

*/

46

interface ResolveOptions {

47

/** Default tag when no preference specified */

48

defaultTag?: string;

49

/** Registry URL to resolve from */

50

registry: string;

51

/** Preferred versions for specific packages */

52

preferredVersions?: PreferredVersions;

53

/** Project root prefix */

54

prefix: string;

55

/** Available local packages for fallback */

56

localPackages?: LocalPackages;

57

}

58

```

59

60

### Package Selection Logic

61

62

The resolver internally handles package version selection from metadata based on the specification type and preferred versions configuration. This process is handled automatically when calling the resolver function.

63

64

## Usage Examples

65

66

### Basic Version Selection

67

68

```typescript

69

import createResolveFromNpm from '@pnpm/npm-resolver';

70

71

const resolve = createResolveFromNpm({

72

metaCache: new Map(),

73

store: './pnpm-store',

74

rawNpmConfig: { registry: 'https://registry.npmjs.org/' },

75

});

76

77

// Exact version

78

const exactResult = await resolve(

79

{ alias: 'lodash', pref: '4.17.21' },

80

{

81

registry: 'https://registry.npmjs.org/',

82

prefix: process.cwd()

83

}

84

);

85

86

// Semantic version range

87

const rangeResult = await resolve(

88

{ alias: 'express', pref: '^4.18.0' },

89

{

90

registry: 'https://registry.npmjs.org/',

91

prefix: process.cwd()

92

}

93

);

94

95

// Tag-based selection

96

const tagResult = await resolve(

97

{ alias: 'react', pref: 'beta' },

98

{

99

registry: 'https://registry.npmjs.org/',

100

prefix: process.cwd()

101

}

102

);

103

104

console.log(`Exact: ${exactResult?.package.version}`);

105

console.log(`Range: ${rangeResult?.package.version}`);

106

console.log(`Tag: ${tagResult?.package.version}`);

107

```

108

109

### Advanced Range Selection

110

111

```typescript

112

// Complex version ranges

113

const complexRanges = [

114

{ alias: 'typescript', pref: '~4.9.0' }, // Patch-level changes

115

{ alias: 'webpack', pref: '^5.0.0' }, // Compatible changes

116

{ alias: 'eslint', pref: '>=8.0.0 <9.0.0' }, // Range with constraints

117

{ alias: 'prettier', pref: '2.x' }, // Major version wildcard

118

];

119

120

for (const dep of complexRanges) {

121

const result = await resolve(dep, {

122

registry: 'https://registry.npmjs.org/',

123

prefix: process.cwd()

124

});

125

126

if (result) {

127

console.log(`${dep.alias}@${dep.pref} -> ${result.package.version}`);

128

}

129

}

130

```

131

132

### Preferred Version Configuration

133

134

```typescript

135

// Configure preferred versions for specific packages

136

const preferredVersions = {

137

'react': {

138

selector: '17.0.2', // Prefer specific stable version

139

type: 'version' as const

140

},

141

'lodash': {

142

selector: '^4.17.0', // Prefer range within 4.17.x

143

type: 'range' as const

144

},

145

'typescript': {

146

selector: 'beta', // Prefer beta releases

147

type: 'tag' as const

148

}

149

};

150

151

const result = await resolve(

152

{ alias: 'react', pref: '^17.0.0' },

153

{

154

registry: 'https://registry.npmjs.org/',

155

prefix: process.cwd(),

156

preferredVersions

157

}

158

);

159

160

// Will prefer 17.0.2 even though range allows newer versions

161

console.log(`Selected version: ${result?.package.version}`);

162

```

163

164

### Custom Tag Handling

165

166

```typescript

167

// Working with distribution tags

168

const tagExamples = [

169

{ alias: 'next', pref: 'canary' }, // Canary releases

170

{ alias: 'vue', pref: 'beta' }, // Beta releases

171

{ alias: 'angular', pref: 'next' }, // Next major version

172

{ alias: 'react', pref: 'experimental' }, // Experimental features

173

];

174

175

for (const dep of tagExamples) {

176

try {

177

const result = await resolve(dep, {

178

registry: 'https://registry.npmjs.org/',

179

prefix: process.cwd()

180

});

181

182

if (result) {

183

console.log(`${dep.alias}@${dep.pref}: ${result.package.version}`);

184

console.log(`Latest: ${result.latest}`);

185

}

186

} catch (error) {

187

console.error(`Tag ${dep.pref} not found for ${dep.alias}`);

188

}

189

}

190

```

191

192

### Fallback Strategy

193

194

```typescript

195

// Try multiple version preferences with fallback

196

async function resolveWithFallback(packageName: string, preferences: string[]) {

197

for (const pref of preferences) {

198

try {

199

const result = await resolve(

200

{ alias: packageName, pref },

201

{

202

registry: 'https://registry.npmjs.org/',

203

prefix: process.cwd()

204

}

205

);

206

207

if (result) {

208

console.log(`Resolved ${packageName}@${pref} -> ${result.package.version}`);

209

return result;

210

}

211

} catch (error) {

212

console.log(`Failed to resolve ${packageName}@${pref}: ${error.message}`);

213

}

214

}

215

216

throw new Error(`Could not resolve ${packageName} with any preference`);

217

}

218

219

// Try beta first, fallback to latest

220

const result = await resolveWithFallback('typescript', ['beta', 'rc', 'latest']);

221

```

222

223

### Version Format Support

224

225

The resolver supports various version specification formats:

226

227

```typescript

228

// Different preference formats supported

229

const supportedFormats = [

230

'1.2.3', // Exact version

231

'^1.2.0', // Caret range - compatible within major version

232

'~1.2.0', // Tilde range - compatible within minor version

233

'>=1.0.0 <2.0.0', // Range expression with constraints

234

'latest', // Distribution tag

235

'beta', // Beta distribution tag

236

'next', // Next distribution tag

237

];

238

239

// Example resolution with different formats

240

for (const pref of supportedFormats) {

241

const result = await resolve(

242

{ alias: 'example-package', pref },

243

{

244

registry: 'https://registry.npmjs.org/',

245

prefix: process.cwd()

246

}

247

);

248

249

if (result) {

250

console.log(`"${pref}" resolved to: ${result.package.version}`);

251

}

252

}

253

```

254

255

### Local Package Integration

256

257

```typescript

258

// Define local packages for development

259

const localPackages = {

260

'my-lib': {

261

'1.0.0': {

262

directory: '/workspace/my-lib',

263

package: { name: 'my-lib', version: '1.0.0' }

264

},

265

'1.1.0-dev': {

266

directory: '/workspace/my-lib',

267

package: { name: 'my-lib', version: '1.1.0-dev' }

268

}

269

}

270

};

271

272

// Prefer local development version over registry

273

const devResult = await resolve(

274

{ alias: 'my-lib', pref: '^1.0.0' },

275

{

276

registry: 'https://registry.npmjs.org/',

277

prefix: process.cwd(),

278

localPackages

279

}

280

);

281

282

if (devResult?.resolvedVia === 'local-filesystem') {

283

console.log(`Using local development version: ${devResult.package.version}`);

284

console.log(`Location: ${devResult.resolution.directory}`);

285

} else {

286

console.log(`Using registry version: ${devResult?.package.version}`);

287

}

288

```

289

290

### Version Preference Validation

291

292

```typescript

293

// Validate version preferences through resolution attempt

294

async function validateVersionPreference(alias: string, pref: string): Promise<boolean> {

295

try {

296

const result = await resolve(

297

{ alias, pref },

298

{

299

registry: 'https://registry.npmjs.org/',

300

prefix: process.cwd()

301

}

302

);

303

304

return result !== null;

305

} catch (error) {

306

// Invalid preference or resolution failed

307

return false;

308

}

309

}

310

311

// Test various preferences

312

const testPreferences = [

313

{ alias: 'lodash', pref: '4.17.21' }, // Valid exact version

314

{ alias: 'lodash', pref: '^4.0.0' }, // Valid range

315

{ alias: 'lodash', pref: 'latest' }, // Valid tag

316

{ alias: 'lodash', pref: 'invalid' }, // Invalid tag

317

];

318

319

for (const { alias, pref } of testPreferences) {

320

const isValid = await validateVersionPreference(alias, pref);

321

console.log(`${alias}@${pref}: ${isValid ? 'valid' : 'invalid'}`);

322

}

323

```