or run

npx @tessl/cli init
Log in

Version

Tile

Overview

Evals

Files

Files

docs

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

metadata-management.mddocs/

0

# Metadata Management

1

2

The metadata management system handles caching and retrieval of package information from npm registries, providing efficient storage and access to package metadata with support for offline operations.

3

4

## Capabilities

5

6

### Package Metadata Cache

7

8

The cache interface provides storage and retrieval of package metadata to minimize registry requests.

9

10

```typescript { .api }

11

/**

12

* Interface for caching package metadata

13

*/

14

interface PackageMetaCache {

15

/**

16

* Retrieve cached metadata for a package

17

* @param key - Cache key (typically package name)

18

* @returns Cached metadata or undefined if not found

19

*/

20

get(key: string): PackageMeta | undefined;

21

22

/**

23

* Store metadata in cache

24

* @param key - Cache key (typically package name)

25

* @param meta - Package metadata to cache

26

*/

27

set(key: string, meta: PackageMeta): void;

28

29

/**

30

* Check if metadata exists in cache

31

* @param key - Cache key to check

32

* @returns True if metadata is cached

33

*/

34

has(key: string): boolean;

35

}

36

```

37

38

### Package Metadata Structure

39

40

Complete package metadata retrieved from npm registries.

41

42

```typescript { .api }

43

/**

44

* Complete package metadata from npm registry

45

*/

46

interface PackageMeta {

47

/** Distribution tags mapping tag names to version numbers */

48

'dist-tags': { [name: string]: string };

49

/** All available versions with their manifests */

50

versions: { [name: string]: PackageInRegistry };

51

/** Timestamp when metadata was cached (optional) */

52

cachedAt?: number;

53

}

54

55

/**

56

* Package manifest with distribution information

57

*/

58

type PackageInRegistry = PackageManifest & {

59

/** Distribution metadata for downloading */

60

dist: {

61

/** Subresource integrity hash (optional) */

62

integrity?: string;

63

/** SHA-1 hash of the tarball */

64

shasum: string;

65

/** URL to download the package tarball */

66

tarball: string;

67

};

68

};

69

```

70

71

### Package Selection from Metadata

72

73

The resolver automatically selects appropriate package versions from cached metadata based on the specification requirements and any preferred version configuration. This selection process is handled internally when resolving packages.

74

75

## Usage Examples

76

77

### Basic Cache Implementation

78

79

```typescript

80

import createResolveFromNpm, { PackageMetaCache, PackageMeta } from '@pnpm/npm-resolver';

81

82

// Simple Map-based cache

83

const cache: PackageMetaCache = new Map();

84

85

const resolve = createResolveFromNpm({

86

metaCache: cache,

87

store: './pnpm-store',

88

rawNpmConfig: {

89

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

90

},

91

});

92

93

// First resolution will fetch from registry and cache

94

const result1 = await resolve(

95

{ alias: 'lodash', pref: '^4.0.0' },

96

{

97

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

98

prefix: process.cwd()

99

}

100

);

101

102

// Second resolution will use cached metadata

103

const result2 = await resolve(

104

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

105

{

106

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

107

prefix: process.cwd()

108

}

109

);

110

111

console.log(`Cache has lodash: ${cache.has('lodash')}`);

112

```

113

114

### Custom Cache Implementation

115

116

```typescript

117

class PersistentPackageCache implements PackageMetaCache {

118

private cache = new Map<string, PackageMeta>();

119

private cacheFile: string;

120

121

constructor(cacheFile: string) {

122

this.cacheFile = cacheFile;

123

this.loadFromDisk();

124

}

125

126

get(key: string): PackageMeta | undefined {

127

return this.cache.get(key);

128

}

129

130

set(key: string, meta: PackageMeta): void {

131

meta.cachedAt = Date.now();

132

this.cache.set(key, meta);

133

this.saveToDisk();

134

}

135

136

has(key: string): boolean {

137

return this.cache.has(key);

138

}

139

140

private loadFromDisk(): void {

141

try {

142

const data = fs.readFileSync(this.cacheFile, 'utf8');

143

const entries = JSON.parse(data);

144

this.cache = new Map(entries);

145

} catch {

146

// Cache file doesn't exist or is invalid

147

}

148

}

149

150

private saveToDisk(): void {

151

const entries = Array.from(this.cache.entries());

152

fs.writeFileSync(this.cacheFile, JSON.stringify(entries));

153

}

154

}

155

156

const persistentCache = new PersistentPackageCache('./package-cache.json');

157

```

158

159

### Cache Inspection and Management

160

161

```typescript

162

// Inspect cached metadata

163

const cachedMeta = cache.get('express');

164

if (cachedMeta) {

165

console.log('Available versions:', Object.keys(cachedMeta.versions));

166

console.log('Dist tags:', cachedMeta['dist-tags']);

167

console.log('Latest version:', cachedMeta['dist-tags'].latest);

168

169

// Check if cached recently (within 1 hour)

170

const oneHourAgo = Date.now() - (60 * 60 * 1000);

171

const isFresh = cachedMeta.cachedAt && cachedMeta.cachedAt > oneHourAgo;

172

console.log(`Cache is fresh: ${isFresh}`);

173

}

174

```

175

176

### Working with Package Metadata

177

178

```typescript

179

// Inspect cached metadata

180

const expressMeta = cache.get('express');

181

if (expressMeta) {

182

console.log('Available versions:', Object.keys(expressMeta.versions));

183

console.log('Dist tags:', expressMeta['dist-tags']);

184

console.log('Latest version:', expressMeta['dist-tags'].latest);

185

186

// Get specific version information

187

const latestVersion = expressMeta['dist-tags'].latest;

188

const packageInfo = expressMeta.versions[latestVersion];

189

190

if (packageInfo) {

191

console.log(`Latest: ${packageInfo.name}@${packageInfo.version}`);

192

console.log(`Tarball: ${packageInfo.dist.tarball}`);

193

console.log(`Integrity: ${packageInfo.dist.integrity || packageInfo.dist.shasum}`);

194

}

195

}

196

```

197

198

### Offline-First Approach

199

200

```typescript

201

// Create resolver that prefers cached data

202

const offlineFirstResolve = createResolveFromNpm({

203

metaCache: cache,

204

store: './pnpm-store',

205

preferOffline: true, // Use cache when available

206

rawNpmConfig: {

207

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

208

},

209

});

210

211

// This will use cached metadata if available

212

const result = await offlineFirstResolve(

213

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

214

{

215

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

216

prefix: process.cwd()

217

}

218

);

219

```

220

221

### Cache Warming

222

223

```typescript

224

// Pre-populate cache with commonly used packages

225

const commonPackages = ['lodash', 'express', 'react', 'axios'];

226

227

for (const packageName of commonPackages) {

228

try {

229

await resolve(

230

{ alias: packageName, pref: 'latest' },

231

{

232

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

233

prefix: process.cwd()

234

}

235

);

236

console.log(`Cached metadata for ${packageName}`);

237

} catch (error) {

238

console.error(`Failed to cache ${packageName}:`, error.message);

239

}

240

}

241

```

242

243

### Cache Statistics

244

245

```typescript

246

class StatisticalPackageCache implements PackageMetaCache {

247

private cache = new Map<string, PackageMeta>();

248

private hits = 0;

249

private misses = 0;

250

251

get(key: string): PackageMeta | undefined {

252

const result = this.cache.get(key);

253

if (result) {

254

this.hits++;

255

} else {

256

this.misses++;

257

}

258

return result;

259

}

260

261

set(key: string, meta: PackageMeta): void {

262

this.cache.set(key, meta);

263

}

264

265

has(key: string): boolean {

266

return this.cache.has(key);

267

}

268

269

getStats() {

270

const total = this.hits + this.misses;

271

return {

272

hits: this.hits,

273

misses: this.misses,

274

hitRate: total > 0 ? this.hits / total : 0,

275

cacheSize: this.cache.size,

276

};

277

}

278

}

279

```