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
```