@vltpkg/types 0.0.0-3 → 0.0.0-31
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +2 -1
- package/dist/esm/index.d.ts +340 -2
- package/dist/esm/index.d.ts.map +1 -1
- package/dist/esm/index.js +623 -14
- package/dist/esm/index.js.map +1 -1
- package/package.json +13 -9
package/dist/esm/index.js
CHANGED
|
@@ -1,5 +1,428 @@
|
|
|
1
1
|
import { error } from '@vltpkg/error-cause';
|
|
2
|
-
|
|
2
|
+
import { Version } from '@vltpkg/semver';
|
|
3
|
+
/**
|
|
4
|
+
* Normalize a single funding entry to a consistent format.
|
|
5
|
+
*/
|
|
6
|
+
const normalizeFundingEntry = (item) => {
|
|
7
|
+
const getTypeFromUrl = (url) => {
|
|
8
|
+
try {
|
|
9
|
+
const { hostname } = new URL(url);
|
|
10
|
+
const domain = hostname.startsWith('www.') ? hostname.slice(4) : hostname;
|
|
11
|
+
if (domain === 'github.com')
|
|
12
|
+
return 'github';
|
|
13
|
+
if (domain === 'patreon.com')
|
|
14
|
+
return 'patreon';
|
|
15
|
+
if (domain === 'opencollective.com')
|
|
16
|
+
return 'opencollective';
|
|
17
|
+
return 'individual';
|
|
18
|
+
}
|
|
19
|
+
catch {
|
|
20
|
+
return 'invalid';
|
|
21
|
+
}
|
|
22
|
+
};
|
|
23
|
+
const validateType = (url, type) => {
|
|
24
|
+
const urlType = getTypeFromUrl(url);
|
|
25
|
+
if (!type ||
|
|
26
|
+
['github', 'patreon', 'opencollective'].includes(urlType))
|
|
27
|
+
return urlType;
|
|
28
|
+
if (urlType === 'invalid')
|
|
29
|
+
return undefined;
|
|
30
|
+
return type;
|
|
31
|
+
};
|
|
32
|
+
if (typeof item === 'string') {
|
|
33
|
+
return { url: item, type: getTypeFromUrl(item) };
|
|
34
|
+
}
|
|
35
|
+
if (isObject(item) &&
|
|
36
|
+
'url' in item &&
|
|
37
|
+
typeof item.url === 'string') {
|
|
38
|
+
// If the item is already normalized, return it directly
|
|
39
|
+
if (isNormalizedFundingEntry(item)) {
|
|
40
|
+
return item;
|
|
41
|
+
}
|
|
42
|
+
const obj = item;
|
|
43
|
+
const url = obj.url;
|
|
44
|
+
const validatedType = validateType(url, obj.type);
|
|
45
|
+
const result = { ...obj, url };
|
|
46
|
+
if (validatedType) {
|
|
47
|
+
result.type = validatedType;
|
|
48
|
+
}
|
|
49
|
+
else {
|
|
50
|
+
delete result.type;
|
|
51
|
+
}
|
|
52
|
+
return result;
|
|
53
|
+
}
|
|
54
|
+
return { url: '', type: 'individual' };
|
|
55
|
+
};
|
|
56
|
+
/**
|
|
57
|
+
* Normalize funding information to a consistent format.
|
|
58
|
+
*/
|
|
59
|
+
export const normalizeFunding = (funding) => {
|
|
60
|
+
if (!funding)
|
|
61
|
+
return;
|
|
62
|
+
const fundingArray = Array.isArray(funding) ? funding : [funding];
|
|
63
|
+
const sources = fundingArray.map(normalizeFundingEntry);
|
|
64
|
+
return sources.length > 0 ? sources : undefined;
|
|
65
|
+
};
|
|
66
|
+
/**
|
|
67
|
+
* Type guard to check if a value is a {@link NormalizedFundingEntry}.
|
|
68
|
+
*/
|
|
69
|
+
export const isNormalizedFundingEntry = (o) => {
|
|
70
|
+
return (isObject(o) &&
|
|
71
|
+
'url' in o &&
|
|
72
|
+
typeof o.url === 'string' &&
|
|
73
|
+
!!o.url &&
|
|
74
|
+
'type' in o &&
|
|
75
|
+
typeof o.type === 'string' &&
|
|
76
|
+
['github', 'patreon', 'opencollective', 'individual'].includes(o.type));
|
|
77
|
+
};
|
|
78
|
+
/**
|
|
79
|
+
* Type guard to check if a value is a {@link NormalizedFunding}.
|
|
80
|
+
*/
|
|
81
|
+
export const isNormalizedFunding = (o) => {
|
|
82
|
+
return (Array.isArray(o) &&
|
|
83
|
+
o.length > 0 &&
|
|
84
|
+
o.every(isNormalizedFundingEntry));
|
|
85
|
+
};
|
|
86
|
+
/**
|
|
87
|
+
* Given a version Normalize the version field in a manifest.
|
|
88
|
+
*/
|
|
89
|
+
export const fixManifestVersion = (manifest) => {
|
|
90
|
+
if (!Object.hasOwn(manifest, 'version')) {
|
|
91
|
+
return manifest;
|
|
92
|
+
}
|
|
93
|
+
if (!manifest.version) {
|
|
94
|
+
throw error('version is empty', {
|
|
95
|
+
manifest,
|
|
96
|
+
});
|
|
97
|
+
}
|
|
98
|
+
const version = Version.parse(manifest.version);
|
|
99
|
+
manifest.version = version.toString();
|
|
100
|
+
return manifest;
|
|
101
|
+
};
|
|
102
|
+
const kWriteAccess = Symbol.for('writeAccess');
|
|
103
|
+
const kIsPublisher = Symbol.for('isPublisher');
|
|
104
|
+
/**
|
|
105
|
+
* Parse a string or object into a normalized contributor.
|
|
106
|
+
*/
|
|
107
|
+
export const parsePerson = (person, writeAccess, isPublisher) => {
|
|
108
|
+
if (!person)
|
|
109
|
+
return;
|
|
110
|
+
if (isObject(person)) {
|
|
111
|
+
// this is an already parsed object person, just return its value
|
|
112
|
+
if (isNormalizedContributorEntry(person)) {
|
|
113
|
+
return person;
|
|
114
|
+
}
|
|
115
|
+
const name = typeof person.name === 'string' ? person.name : undefined;
|
|
116
|
+
const email = typeof person.email === 'string' ? person.email
|
|
117
|
+
: typeof person.mail === 'string' ? person.mail
|
|
118
|
+
: undefined;
|
|
119
|
+
if (!name && !email)
|
|
120
|
+
return undefined;
|
|
121
|
+
return {
|
|
122
|
+
name,
|
|
123
|
+
email,
|
|
124
|
+
[kWriteAccess]: writeAccess ?? false,
|
|
125
|
+
[kIsPublisher]: isPublisher ?? false,
|
|
126
|
+
};
|
|
127
|
+
}
|
|
128
|
+
else if (typeof person === 'string') {
|
|
129
|
+
const NAME_PATTERN = /^([^(<]+)/;
|
|
130
|
+
const EMAIL_PATTERN = /<([^<>]+)>/;
|
|
131
|
+
const name = NAME_PATTERN.exec(person)?.[0].trim() || '';
|
|
132
|
+
const email = EMAIL_PATTERN.exec(person)?.[1] || '';
|
|
133
|
+
if (!name && !email)
|
|
134
|
+
return undefined;
|
|
135
|
+
return {
|
|
136
|
+
name: name || undefined,
|
|
137
|
+
email: email || undefined,
|
|
138
|
+
[kWriteAccess]: writeAccess ?? false,
|
|
139
|
+
[kIsPublisher]: isPublisher ?? false,
|
|
140
|
+
};
|
|
141
|
+
}
|
|
142
|
+
return;
|
|
143
|
+
};
|
|
144
|
+
/**
|
|
145
|
+
* Type guard to check if a value is a normalized contributor entry.
|
|
146
|
+
*/
|
|
147
|
+
export const isNormalizedContributorEntry = (o) => {
|
|
148
|
+
return (isObject(o) &&
|
|
149
|
+
typeof o.name === 'string' &&
|
|
150
|
+
!!o.name &&
|
|
151
|
+
typeof o.email === 'string' &&
|
|
152
|
+
!!o.email &&
|
|
153
|
+
(isBoolean(o[kWriteAccess]) ||
|
|
154
|
+
isBoolean(o.writeAccess)) &&
|
|
155
|
+
(isBoolean(o[kIsPublisher]) ||
|
|
156
|
+
isBoolean(o.isPublisher)));
|
|
157
|
+
};
|
|
158
|
+
/**
|
|
159
|
+
* Type guard to check if a value is a {@link NormalizedContributors}.
|
|
160
|
+
*/
|
|
161
|
+
export const isNormalizedContributors = (o) => {
|
|
162
|
+
return (Array.isArray(o) &&
|
|
163
|
+
o.length > 0 &&
|
|
164
|
+
o.every(isNormalizedContributorEntry));
|
|
165
|
+
};
|
|
166
|
+
/**
|
|
167
|
+
* Normalize contributors and maintainers from various formats
|
|
168
|
+
*/
|
|
169
|
+
export const normalizeContributors = (contributors, maintainers) => {
|
|
170
|
+
if (!contributors && !maintainers)
|
|
171
|
+
return;
|
|
172
|
+
const result = [];
|
|
173
|
+
// Parse regular contributors (if any)
|
|
174
|
+
if (contributors) {
|
|
175
|
+
const contributorsArray = Array.isArray(contributors) ? contributors : [contributors];
|
|
176
|
+
const normalizedArray = contributorsArray.every(isNormalizedContributorEntry);
|
|
177
|
+
const noMaintainers = !maintainers ||
|
|
178
|
+
(Array.isArray(maintainers) && maintainers.length === 0);
|
|
179
|
+
// If all contributors are already normalized, and there are
|
|
180
|
+
// no maintainers, return the contributors directly
|
|
181
|
+
if (normalizedArray) {
|
|
182
|
+
if (noMaintainers) {
|
|
183
|
+
return contributorsArray.length > 0 ?
|
|
184
|
+
contributorsArray
|
|
185
|
+
: undefined;
|
|
186
|
+
}
|
|
187
|
+
else {
|
|
188
|
+
result.push(...contributorsArray);
|
|
189
|
+
}
|
|
190
|
+
}
|
|
191
|
+
// Parse each contributor and filter out undefined values
|
|
192
|
+
const parsedContributors = contributorsArray
|
|
193
|
+
.map(person => parsePerson(person))
|
|
194
|
+
.filter((c) => c !== undefined);
|
|
195
|
+
result.push(...parsedContributors);
|
|
196
|
+
}
|
|
197
|
+
// Parse maintainers with special flags
|
|
198
|
+
if (maintainers) {
|
|
199
|
+
const maintainersArray = Array.isArray(maintainers) ? maintainers : [maintainers];
|
|
200
|
+
const parsedMaintainers = maintainersArray
|
|
201
|
+
.map(person => parsePerson(person, true, true))
|
|
202
|
+
.filter((c) => c !== undefined);
|
|
203
|
+
result.push(...parsedMaintainers);
|
|
204
|
+
}
|
|
205
|
+
return result.length > 0 ? result : undefined;
|
|
206
|
+
};
|
|
207
|
+
/**
|
|
208
|
+
* Helper function to normalize a single {@link Bugs} entry.
|
|
209
|
+
*/
|
|
210
|
+
const normalizeSingleBug = (bug) => {
|
|
211
|
+
const res = [];
|
|
212
|
+
if (typeof bug === 'string') {
|
|
213
|
+
// Try to parse as URL first - if it succeeds, treat as link
|
|
214
|
+
try {
|
|
215
|
+
new URL(bug);
|
|
216
|
+
res.push({ type: 'link', url: bug });
|
|
217
|
+
}
|
|
218
|
+
catch {
|
|
219
|
+
// TODO: need a more robust email validation, likely
|
|
220
|
+
// to be replaced with valibot / zod
|
|
221
|
+
// If URL parsing fails, check if it's a valid email
|
|
222
|
+
const emailRegex = /^[^\s@]+@[^\s@]+\.[^\s@]+$/;
|
|
223
|
+
if (emailRegex.test(bug)) {
|
|
224
|
+
res.push({ type: 'email', email: bug });
|
|
225
|
+
}
|
|
226
|
+
else {
|
|
227
|
+
// Default to link for plain strings like 'example.com'
|
|
228
|
+
res.push({ type: 'link', url: bug });
|
|
229
|
+
}
|
|
230
|
+
}
|
|
231
|
+
}
|
|
232
|
+
else if (isObject(bug)) {
|
|
233
|
+
if (isNormalizedBugsEntry(bug)) {
|
|
234
|
+
res.push(bug);
|
|
235
|
+
}
|
|
236
|
+
const obj = bug;
|
|
237
|
+
if (obj.url) {
|
|
238
|
+
res.push({ type: 'link', url: obj.url });
|
|
239
|
+
}
|
|
240
|
+
if (obj.email) {
|
|
241
|
+
res.push({ type: 'email', email: obj.email });
|
|
242
|
+
}
|
|
243
|
+
}
|
|
244
|
+
return res.length > 0 ? res : [];
|
|
245
|
+
};
|
|
246
|
+
/**
|
|
247
|
+
* Normalize bugs information to a {@link NormalizedBugs} consistent format.
|
|
248
|
+
*/
|
|
249
|
+
export const normalizeBugs = (bugs) => {
|
|
250
|
+
if (!bugs)
|
|
251
|
+
return;
|
|
252
|
+
const result = [];
|
|
253
|
+
// Handle array of bugs entries
|
|
254
|
+
if (Array.isArray(bugs)) {
|
|
255
|
+
for (const bug of bugs) {
|
|
256
|
+
result.push(...normalizeSingleBug(bug));
|
|
257
|
+
}
|
|
258
|
+
}
|
|
259
|
+
else {
|
|
260
|
+
// Handle single bugs entry
|
|
261
|
+
result.push(...normalizeSingleBug(bugs));
|
|
262
|
+
}
|
|
263
|
+
return result.length > 0 ? result : undefined;
|
|
264
|
+
};
|
|
265
|
+
/**
|
|
266
|
+
* Type guard to check if a value is a {@link NormalizedBugsEntry}.
|
|
267
|
+
*/
|
|
268
|
+
export const isNormalizedBugsEntry = (o) => {
|
|
269
|
+
return (isObject(o) &&
|
|
270
|
+
'type' in o &&
|
|
271
|
+
((o.type === 'email' &&
|
|
272
|
+
typeof o.email === 'string' &&
|
|
273
|
+
!!o.email) ||
|
|
274
|
+
(o.type === 'link' && typeof o.url === 'string' && !!o.url)));
|
|
275
|
+
};
|
|
276
|
+
/**
|
|
277
|
+
* Type guard to check if a value is a {@link NormalizedBugs}.
|
|
278
|
+
*/
|
|
279
|
+
export const isNormalizedBugs = (o) => {
|
|
280
|
+
return (Array.isArray(o) && o.length > 0 && o.every(isNormalizedBugsEntry));
|
|
281
|
+
};
|
|
282
|
+
/**
|
|
283
|
+
* Normalize keywords information to a {@link NormalizedKeywords} consistent format.
|
|
284
|
+
*/
|
|
285
|
+
export const normalizeKeywords = (keywords) => {
|
|
286
|
+
if (!keywords)
|
|
287
|
+
return;
|
|
288
|
+
let keywordArray = [];
|
|
289
|
+
if (typeof keywords === 'string') {
|
|
290
|
+
// Handle comma-separated string values
|
|
291
|
+
keywordArray = keywords
|
|
292
|
+
.split(',')
|
|
293
|
+
.map(keyword => keyword.trim())
|
|
294
|
+
.filter(keyword => keyword.length > 0);
|
|
295
|
+
}
|
|
296
|
+
else if (Array.isArray(keywords)) {
|
|
297
|
+
// If all keywords are already normalized, return them directly
|
|
298
|
+
if (isNormalizedKeywords(keywords)) {
|
|
299
|
+
return keywords;
|
|
300
|
+
}
|
|
301
|
+
// Handle array of strings, filter out empty/invalid entries
|
|
302
|
+
keywordArray = keywords
|
|
303
|
+
.filter((keyword) => typeof keyword === 'string')
|
|
304
|
+
.map(keyword => keyword.trim())
|
|
305
|
+
.filter(keyword => keyword.length > 0);
|
|
306
|
+
}
|
|
307
|
+
else {
|
|
308
|
+
// Invalid format
|
|
309
|
+
return;
|
|
310
|
+
}
|
|
311
|
+
return keywordArray.length > 0 ? keywordArray : undefined;
|
|
312
|
+
};
|
|
313
|
+
/**
|
|
314
|
+
* Type guard to check if a value is a {@link NormalizedKeywords}.
|
|
315
|
+
*/
|
|
316
|
+
export const isNormalizedKeywords = (o) => {
|
|
317
|
+
return (Array.isArray(o) &&
|
|
318
|
+
o.length > 0 &&
|
|
319
|
+
o.every(keyword => typeof keyword === 'string' &&
|
|
320
|
+
!!keyword &&
|
|
321
|
+
!keyword.startsWith(' ') &&
|
|
322
|
+
!keyword.endsWith(' ')));
|
|
323
|
+
};
|
|
324
|
+
/**
|
|
325
|
+
* Normalize engines information to a {@link NormalizedEngines} consistent format.
|
|
326
|
+
*/
|
|
327
|
+
export const normalizeEngines = (engines) => {
|
|
328
|
+
if (!engines)
|
|
329
|
+
return;
|
|
330
|
+
if (isNormalizedEngines(engines)) {
|
|
331
|
+
// Return undefined if empty object
|
|
332
|
+
return Object.keys(engines).length === 0 ? undefined : engines;
|
|
333
|
+
}
|
|
334
|
+
// Invalid format
|
|
335
|
+
return;
|
|
336
|
+
};
|
|
337
|
+
/**
|
|
338
|
+
* Normalize OS information to a {@link NormalizedOs} consistent format.
|
|
339
|
+
*/
|
|
340
|
+
export const normalizeOs = (os) => {
|
|
341
|
+
if (!os)
|
|
342
|
+
return;
|
|
343
|
+
let osArray = [];
|
|
344
|
+
if (typeof os === 'string') {
|
|
345
|
+
// Handle single OS string
|
|
346
|
+
osArray = [os.trim()].filter(item => item.length > 0);
|
|
347
|
+
}
|
|
348
|
+
else if (Array.isArray(os)) {
|
|
349
|
+
// If all OS entries are already normalized, return them directly
|
|
350
|
+
if (isNormalizedOs(os)) {
|
|
351
|
+
return os;
|
|
352
|
+
}
|
|
353
|
+
// Handle array of strings, filter out empty/invalid entries
|
|
354
|
+
osArray = os
|
|
355
|
+
.filter((item) => typeof item === 'string')
|
|
356
|
+
.map(item => item.trim())
|
|
357
|
+
.filter(item => item.length > 0);
|
|
358
|
+
}
|
|
359
|
+
else {
|
|
360
|
+
// Invalid format
|
|
361
|
+
return;
|
|
362
|
+
}
|
|
363
|
+
return osArray.length > 0 ? osArray : undefined;
|
|
364
|
+
};
|
|
365
|
+
/**
|
|
366
|
+
* Normalize CPU information to a {@link NormalizedCpu} consistent format.
|
|
367
|
+
*/
|
|
368
|
+
export const normalizeCpu = (cpu) => {
|
|
369
|
+
if (!cpu)
|
|
370
|
+
return;
|
|
371
|
+
let cpuArray = [];
|
|
372
|
+
if (typeof cpu === 'string') {
|
|
373
|
+
// Handle single CPU string
|
|
374
|
+
cpuArray = [cpu.trim()].filter(item => item.length > 0);
|
|
375
|
+
}
|
|
376
|
+
else if (Array.isArray(cpu)) {
|
|
377
|
+
// If all CPU entries are already normalized, return them directly
|
|
378
|
+
if (isNormalizedCpu(cpu)) {
|
|
379
|
+
return cpu;
|
|
380
|
+
}
|
|
381
|
+
// Handle array of strings, filter out empty/invalid entries
|
|
382
|
+
cpuArray = cpu
|
|
383
|
+
.filter((item) => typeof item === 'string')
|
|
384
|
+
.map(item => item.trim())
|
|
385
|
+
.filter(item => item.length > 0);
|
|
386
|
+
}
|
|
387
|
+
else {
|
|
388
|
+
// Invalid format
|
|
389
|
+
return;
|
|
390
|
+
}
|
|
391
|
+
return cpuArray.length > 0 ? cpuArray : undefined;
|
|
392
|
+
};
|
|
393
|
+
/**
|
|
394
|
+
* Type guard to check if a value is a {@link NormalizedEngines}.
|
|
395
|
+
*/
|
|
396
|
+
export const isNormalizedEngines = (o) => {
|
|
397
|
+
return isRecordStringString(o);
|
|
398
|
+
};
|
|
399
|
+
/**
|
|
400
|
+
* Type guard to check if a value is a {@link NormalizedOs}.
|
|
401
|
+
*/
|
|
402
|
+
export const isNormalizedOs = (o) => {
|
|
403
|
+
return (Array.isArray(o) &&
|
|
404
|
+
o.length > 0 &&
|
|
405
|
+
o.every(item => typeof item === 'string' &&
|
|
406
|
+
!!item &&
|
|
407
|
+
!item.startsWith(' ') &&
|
|
408
|
+
!item.endsWith(' ')));
|
|
409
|
+
};
|
|
410
|
+
/**
|
|
411
|
+
* Type guard to check if a value is a {@link NormalizedCpu}.
|
|
412
|
+
*/
|
|
413
|
+
export const isNormalizedCpu = (o) => {
|
|
414
|
+
return (Array.isArray(o) &&
|
|
415
|
+
o.length > 0 &&
|
|
416
|
+
o.every(item => typeof item === 'string' &&
|
|
417
|
+
!!item &&
|
|
418
|
+
!item.startsWith(' ') &&
|
|
419
|
+
!item.endsWith(' ')));
|
|
420
|
+
};
|
|
421
|
+
/**
|
|
422
|
+
* A type guard to check if a value is a boolean.
|
|
423
|
+
*/
|
|
424
|
+
export const isBoolean = (value) => typeof value === 'boolean';
|
|
425
|
+
export const integrityRE = /^sha512-[a-zA-Z0-9/+]{86}==$/;
|
|
3
426
|
export const isIntegrity = (i) => typeof i === 'string' && integrityRE.test(i);
|
|
4
427
|
export const asIntegrity = (i) => {
|
|
5
428
|
if (!isIntegrity(i)) {
|
|
@@ -13,7 +436,7 @@ export const asIntegrity = (i) => {
|
|
|
13
436
|
export const assertIntegrity = i => {
|
|
14
437
|
asIntegrity(i);
|
|
15
438
|
};
|
|
16
|
-
const keyIDRE = /^SHA256:[a-zA-Z0-9/+]{43}$/;
|
|
439
|
+
export const keyIDRE = /^SHA256:[a-zA-Z0-9/+]{43}$/;
|
|
17
440
|
export const isKeyID = (k) => typeof k === 'string' && keyIDRE.test(k);
|
|
18
441
|
export const asKeyID = (k) => {
|
|
19
442
|
if (!isKeyID(k)) {
|
|
@@ -27,19 +450,52 @@ export const asKeyID = (k) => {
|
|
|
27
450
|
export const assertKeyID = k => {
|
|
28
451
|
asKeyID(k);
|
|
29
452
|
};
|
|
30
|
-
|
|
31
|
-
|
|
32
|
-
|
|
33
|
-
const
|
|
453
|
+
/**
|
|
454
|
+
* Convert an unknown value to an error.
|
|
455
|
+
*/
|
|
456
|
+
export const asError = (er, fallbackMessage = 'Unknown error') => er instanceof Error ? er : new Error(String(er) || fallbackMessage);
|
|
457
|
+
/**
|
|
458
|
+
* Check if a value is an error.
|
|
459
|
+
*/
|
|
460
|
+
export const isError = (er) => er instanceof Error;
|
|
461
|
+
/**
|
|
462
|
+
* Check if an error has a cause property.
|
|
463
|
+
*/
|
|
464
|
+
export const isErrorWithCause = (er) => isError(er) && 'cause' in er;
|
|
465
|
+
/**
|
|
466
|
+
* Check if an unknown value is a plain object.
|
|
467
|
+
*/
|
|
468
|
+
export const isObject = (v) => !!v &&
|
|
469
|
+
typeof v === 'object' &&
|
|
470
|
+
(v.constructor === Object ||
|
|
471
|
+
v.constructor === undefined);
|
|
472
|
+
export const maybeRecordStringString = (o) => o === undefined || isRecordStringString(o);
|
|
473
|
+
export const isRecordStringString = (o) => isRecordStringT(o, s => typeof s === 'string');
|
|
474
|
+
export const assertRecordStringString = (o) => assertRecordStringT(o, s => typeof s === 'string', 'Record<string, string>');
|
|
475
|
+
export const isRecordStringT = (o, check) => isObject(o) &&
|
|
34
476
|
Object.entries(o).every(([k, v]) => typeof k === 'string' && check(v));
|
|
35
|
-
const
|
|
36
|
-
|
|
477
|
+
export const assertRecordStringT = (o, check,
|
|
478
|
+
/** a type description, like 'Record<string, Record<string, string>>' */
|
|
479
|
+
wanted) => {
|
|
480
|
+
if (!isRecordStringT(o, check)) {
|
|
481
|
+
throw error('Invalid record', {
|
|
482
|
+
found: o,
|
|
483
|
+
wanted,
|
|
484
|
+
});
|
|
485
|
+
}
|
|
486
|
+
};
|
|
487
|
+
export const isRecordStringManifest = (o) => isRecordStringT(o, v => isManifest(v));
|
|
488
|
+
export const maybePeerDependenciesMetaSet = (o) => o === undefined ||
|
|
37
489
|
isRecordStringT(o, v => isPeerDependenciesMetaValue(v));
|
|
38
|
-
const maybeBoolean = (o) => o === undefined || typeof o === 'boolean';
|
|
39
|
-
const isPeerDependenciesMetaValue = (o) =>
|
|
40
|
-
const maybeString = (a) => a === undefined || typeof a === 'string';
|
|
41
|
-
const maybeDist = (a) => a === undefined || (
|
|
42
|
-
|
|
490
|
+
export const maybeBoolean = (o) => o === undefined || typeof o === 'boolean';
|
|
491
|
+
export const isPeerDependenciesMetaValue = (o) => isObject(o) && maybeBoolean(o.optional);
|
|
492
|
+
export const maybeString = (a) => a === undefined || typeof a === 'string';
|
|
493
|
+
export const maybeDist = (a) => a === undefined || (isObject(a) && maybeString(a.tarball));
|
|
494
|
+
/**
|
|
495
|
+
* Is a given unknown value a valid {@link Manifest} object?
|
|
496
|
+
* Returns `true` if so.
|
|
497
|
+
*/
|
|
498
|
+
export const isManifest = (m) => isObject(m) &&
|
|
43
499
|
!Array.isArray(m) &&
|
|
44
500
|
maybeString(m.name) &&
|
|
45
501
|
maybeString(m.version) &&
|
|
@@ -50,19 +506,172 @@ export const isManifest = (m) => isObj(m) &&
|
|
|
50
506
|
maybeRecordStringString(m.acceptDependencies) &&
|
|
51
507
|
maybePeerDependenciesMetaSet(m.peerDependenciesMeta) &&
|
|
52
508
|
maybeDist(m.dist);
|
|
509
|
+
/**
|
|
510
|
+
* A specific {@link Manifest} that is retrieved uniquely from reading
|
|
511
|
+
* registry packument and manifest endpoints, it has `dist`, `name` and
|
|
512
|
+
* `version` fields defined.
|
|
513
|
+
*/
|
|
53
514
|
export const isManifestRegistry = (m) => isManifest(m) && !!m.dist && !!m.name && !!m.version;
|
|
515
|
+
/**
|
|
516
|
+
* Given an unknown value, convert it to a {@link Manifest}.
|
|
517
|
+
*/
|
|
54
518
|
export const asManifest = (m, from) => {
|
|
55
519
|
if (!isManifest(m)) {
|
|
56
520
|
throw error('invalid manifest', { found: m }, from ?? asManifest);
|
|
57
521
|
}
|
|
58
522
|
return m;
|
|
59
523
|
};
|
|
524
|
+
/**
|
|
525
|
+
* Given a {@link Manifest} returns a {@link NormalizedManifest} that
|
|
526
|
+
* contains normalized author, bugs, funding, contributors, keywords and
|
|
527
|
+
* version fields.
|
|
528
|
+
*/
|
|
529
|
+
export const normalizeManifest = (manifest) => {
|
|
530
|
+
manifest = fixManifestVersion(manifest);
|
|
531
|
+
const normalizedAuthor = parsePerson(manifest.author);
|
|
532
|
+
const normalizedFunding = normalizeFunding(manifest.funding);
|
|
533
|
+
const normalizedContributors = normalizeContributors(manifest.contributors, manifest.maintainers);
|
|
534
|
+
const normalizedBugs = normalizeBugs(manifest.bugs);
|
|
535
|
+
const normalizedKeywords = normalizeKeywords(manifest.keywords);
|
|
536
|
+
const normalizedEngines = normalizeEngines(manifest.engines);
|
|
537
|
+
const normalizedOs = normalizeOs(manifest.os);
|
|
538
|
+
const normalizedCpu = normalizeCpu(manifest.cpu);
|
|
539
|
+
// holds the same object reference but renames the variable here
|
|
540
|
+
// so that it's simpler to cast it to the normalized type
|
|
541
|
+
const normalizedManifest = manifest;
|
|
542
|
+
if (normalizedAuthor) {
|
|
543
|
+
normalizedManifest.author = normalizedAuthor;
|
|
544
|
+
}
|
|
545
|
+
else {
|
|
546
|
+
delete normalizedManifest.author;
|
|
547
|
+
}
|
|
548
|
+
if (normalizedFunding) {
|
|
549
|
+
normalizedManifest.funding = normalizedFunding;
|
|
550
|
+
}
|
|
551
|
+
else {
|
|
552
|
+
delete normalizedManifest.funding;
|
|
553
|
+
}
|
|
554
|
+
if (normalizedContributors) {
|
|
555
|
+
normalizedManifest.contributors = normalizedContributors;
|
|
556
|
+
}
|
|
557
|
+
else {
|
|
558
|
+
delete normalizedManifest.contributors;
|
|
559
|
+
}
|
|
560
|
+
if (normalizedBugs) {
|
|
561
|
+
normalizedManifest.bugs = normalizedBugs;
|
|
562
|
+
}
|
|
563
|
+
else {
|
|
564
|
+
delete normalizedManifest.bugs;
|
|
565
|
+
}
|
|
566
|
+
if (normalizedKeywords) {
|
|
567
|
+
normalizedManifest.keywords = normalizedKeywords;
|
|
568
|
+
}
|
|
569
|
+
else {
|
|
570
|
+
delete normalizedManifest.keywords;
|
|
571
|
+
}
|
|
572
|
+
if (normalizedEngines) {
|
|
573
|
+
normalizedManifest.engines = normalizedEngines;
|
|
574
|
+
}
|
|
575
|
+
else {
|
|
576
|
+
delete normalizedManifest.engines;
|
|
577
|
+
}
|
|
578
|
+
if (normalizedOs) {
|
|
579
|
+
normalizedManifest.os = normalizedOs;
|
|
580
|
+
}
|
|
581
|
+
else {
|
|
582
|
+
delete normalizedManifest.os;
|
|
583
|
+
}
|
|
584
|
+
if (normalizedCpu) {
|
|
585
|
+
normalizedManifest.cpu = normalizedCpu;
|
|
586
|
+
}
|
|
587
|
+
else {
|
|
588
|
+
delete normalizedManifest.cpu;
|
|
589
|
+
}
|
|
590
|
+
// Remove maintainers field if it exists in the raw manifest
|
|
591
|
+
// this can only happen if the manifest is of ManifestRegistry type
|
|
592
|
+
if ('maintainers' in normalizedManifest &&
|
|
593
|
+
normalizedManifest.maintainers) {
|
|
594
|
+
delete normalizedManifest.maintainers;
|
|
595
|
+
return normalizedManifest;
|
|
596
|
+
}
|
|
597
|
+
return normalizedManifest;
|
|
598
|
+
};
|
|
599
|
+
/**
|
|
600
|
+
* Type guard to check if a value is a {@link NormalizedManifest}.
|
|
601
|
+
*/
|
|
602
|
+
export const isNormalizedManifest = (o) => {
|
|
603
|
+
return (isManifest(o) &&
|
|
604
|
+
// given that all these values are optional and potentially undefined
|
|
605
|
+
// we only check their value content if they are present
|
|
606
|
+
('author' in o ? isNormalizedContributorEntry(o.author) : true) &&
|
|
607
|
+
('contributors' in o ?
|
|
608
|
+
isNormalizedContributors(o.contributors)
|
|
609
|
+
: true) &&
|
|
610
|
+
('funding' in o ? isNormalizedFunding(o.funding) : true) &&
|
|
611
|
+
('bugs' in o ? isNormalizedBugs(o.bugs) : true) &&
|
|
612
|
+
('keywords' in o ? isNormalizedKeywords(o.keywords) : true) &&
|
|
613
|
+
('engines' in o ? isNormalizedEngines(o.engines) : true) &&
|
|
614
|
+
('os' in o ? isNormalizedOs(o.os) : true) &&
|
|
615
|
+
('cpu' in o ? isNormalizedCpu(o.cpu) : true));
|
|
616
|
+
};
|
|
617
|
+
/**
|
|
618
|
+
* Given an unknown value, convert it to a {@link NormalizedManifest}.
|
|
619
|
+
*/
|
|
620
|
+
export const asNormalizedManifest = (m, from) => {
|
|
621
|
+
if (!isNormalizedManifest(m)) {
|
|
622
|
+
throw error('invalid normalized manifest', { found: m }, from ?? asNormalizedManifest);
|
|
623
|
+
}
|
|
624
|
+
return m;
|
|
625
|
+
};
|
|
626
|
+
/**
|
|
627
|
+
* Given an unknown value, convert it to a {@link ManifestRegistry}.
|
|
628
|
+
*/
|
|
60
629
|
export const asManifestRegistry = (m, from) => {
|
|
61
630
|
if (!isManifestRegistry(m)) {
|
|
62
631
|
throw error('invalid registry manifest', { found: m }, from ?? asManifestRegistry);
|
|
63
632
|
}
|
|
64
633
|
return m;
|
|
65
634
|
};
|
|
635
|
+
/**
|
|
636
|
+
* Type guard to check if a value is a {@link NormalizedManifestRegistry}.
|
|
637
|
+
*/
|
|
638
|
+
export const isNormalizedManifestRegistry = (o) => {
|
|
639
|
+
return isNormalizedManifest(o) && isManifestRegistry(o);
|
|
640
|
+
};
|
|
641
|
+
/**
|
|
642
|
+
* Given an unknown value, convert it to a {@link NormalizedManifestRegistry}.
|
|
643
|
+
*/
|
|
644
|
+
export const asNormalizedManifestRegistry = (m, from) => {
|
|
645
|
+
if (!isNormalizedManifestRegistry(m)) {
|
|
646
|
+
throw error('invalid normalized manifest registry', { found: m }, from ?? asNormalizedManifestRegistry);
|
|
647
|
+
}
|
|
648
|
+
return m;
|
|
649
|
+
};
|
|
650
|
+
/**
|
|
651
|
+
* Expands a normalized contributor entry by converting the
|
|
652
|
+
* in-memory symbols to their plain values.
|
|
653
|
+
*/
|
|
654
|
+
const expandNormalizedContributorEntrySymbols = (c) => {
|
|
655
|
+
return {
|
|
656
|
+
...c,
|
|
657
|
+
writeAccess: c[kWriteAccess],
|
|
658
|
+
isPublisher: c[kIsPublisher],
|
|
659
|
+
};
|
|
660
|
+
};
|
|
661
|
+
/**
|
|
662
|
+
* Walks a normalized manifest and expands any symbols found
|
|
663
|
+
* in the `author` and `contributors` fields.
|
|
664
|
+
*/
|
|
665
|
+
export const expandNormalizedManifestSymbols = (m) => {
|
|
666
|
+
const res = { ...m };
|
|
667
|
+
if (isNormalizedContributorEntry(m.author)) {
|
|
668
|
+
res.author = expandNormalizedContributorEntrySymbols(m.author);
|
|
669
|
+
}
|
|
670
|
+
if (isNormalizedContributors(m.contributors)) {
|
|
671
|
+
res.contributors = m.contributors.map(expandNormalizedContributorEntrySymbols);
|
|
672
|
+
}
|
|
673
|
+
return res;
|
|
674
|
+
};
|
|
66
675
|
export const assertManifest = m => {
|
|
67
676
|
asManifest(m, assertManifest);
|
|
68
677
|
};
|
|
@@ -70,7 +679,7 @@ export const assertManifestRegistry = m => {
|
|
|
70
679
|
asManifestRegistry(m, assertManifestRegistry);
|
|
71
680
|
};
|
|
72
681
|
export const isPackument = (p) => {
|
|
73
|
-
if (!
|
|
682
|
+
if (!isObject(p) || typeof p.name !== 'string')
|
|
74
683
|
return false;
|
|
75
684
|
const { versions, 'dist-tags': distTags, time } = p;
|
|
76
685
|
return (isRecordStringString(distTags) &&
|