@absolutejs/changelog 0.4.0 → 0.5.0
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/CHANGELOG.md +15 -0
- package/changelog.json +39 -0
- package/dist/cli.js +86 -32
- package/dist/cli.js.map +6 -6
- package/dist/index.d.ts +2 -2
- package/dist/index.js +83 -30
- package/dist/index.js.map +5 -5
- package/dist/reconcile.d.ts +5 -2
- package/dist/registry.d.ts +14 -11
- package/dist/surface.d.ts +28 -4
- package/package.json +1 -1
package/CHANGELOG.md
CHANGED
|
@@ -6,6 +6,21 @@ This file is generated by `absolute-changelog` from the entries in
|
|
|
6
6
|
`changelog/`. Edit an entry, not this file — and add new ones under
|
|
7
7
|
`changelog/unreleased/`.
|
|
8
8
|
|
|
9
|
+
## 0.5.0 — 2026-09-10
|
|
10
|
+
|
|
11
|
+
### Breaking
|
|
12
|
+
|
|
13
|
+
- **A surface records which entry points offer each name, so a name leaving one is a change** (`PackageSurface`, `SurfaceName`, `surfaceOfFiles`, `typeEntriesOf`, `TypeEntry`, `SurfaceDiff`, `Withdrawal`, `Unannounced`)
|
|
14
|
+
_Migration:_ surfaceOfFiles takes { path, specifier } entries and its values are { declaration, entries }; diffSurface reports withdrawn alongside removed.
|
|
15
|
+
|
|
16
|
+
### Added
|
|
17
|
+
|
|
18
|
+
- **Cache what the registry said for five minutes, so a page asking about twenty packages asks once each** (`forgetRegistry`, `packageVersions`)
|
|
19
|
+
|
|
20
|
+
### Fixed
|
|
21
|
+
|
|
22
|
+
- **Tell a package that has never been published from a registry that will not answer, so a first release is not refused forever** (`packageVersions`)
|
|
23
|
+
|
|
9
24
|
## 0.4.0 — 2026-09-10
|
|
10
25
|
|
|
11
26
|
### Added
|
package/changelog.json
CHANGED
|
@@ -2,6 +2,45 @@
|
|
|
2
2
|
"contract": 1,
|
|
3
3
|
"name": "@absolutejs/changelog",
|
|
4
4
|
"releases": [
|
|
5
|
+
{
|
|
6
|
+
"changes": [
|
|
7
|
+
{
|
|
8
|
+
"kind": "breaking",
|
|
9
|
+
"migration": {
|
|
10
|
+
"instruction": "surfaceOfFiles takes { path, specifier } entries and its values are { declaration, entries }; diffSurface reports withdrawn alongside removed.",
|
|
11
|
+
"manual": true
|
|
12
|
+
},
|
|
13
|
+
"summary": "A surface records which entry points offer each name, so a name leaving one is a change",
|
|
14
|
+
"symbols": [
|
|
15
|
+
"PackageSurface",
|
|
16
|
+
"SurfaceName",
|
|
17
|
+
"surfaceOfFiles",
|
|
18
|
+
"typeEntriesOf",
|
|
19
|
+
"TypeEntry",
|
|
20
|
+
"SurfaceDiff",
|
|
21
|
+
"Withdrawal",
|
|
22
|
+
"Unannounced"
|
|
23
|
+
]
|
|
24
|
+
},
|
|
25
|
+
{
|
|
26
|
+
"kind": "added",
|
|
27
|
+
"summary": "Cache what the registry said for five minutes, so a page asking about twenty packages asks once each",
|
|
28
|
+
"symbols": [
|
|
29
|
+
"forgetRegistry",
|
|
30
|
+
"packageVersions"
|
|
31
|
+
]
|
|
32
|
+
},
|
|
33
|
+
{
|
|
34
|
+
"kind": "fixed",
|
|
35
|
+
"summary": "Tell a package that has never been published from a registry that will not answer, so a first release is not refused forever",
|
|
36
|
+
"symbols": [
|
|
37
|
+
"packageVersions"
|
|
38
|
+
]
|
|
39
|
+
}
|
|
40
|
+
],
|
|
41
|
+
"date": "2026-09-10",
|
|
42
|
+
"version": "0.5.0"
|
|
43
|
+
},
|
|
5
44
|
{
|
|
6
45
|
"changes": [
|
|
7
46
|
{
|
package/dist/cli.js
CHANGED
|
@@ -438,33 +438,55 @@ var surfaceOfArchive = (archive) => {
|
|
|
438
438
|
};
|
|
439
439
|
var surfaceOfFiles = (files, entries) => {
|
|
440
440
|
const surface = new Map;
|
|
441
|
-
|
|
442
|
-
|
|
443
|
-
|
|
444
|
-
|
|
441
|
+
const offered = entries.flatMap((entry) => [...exportsOf(files, entry.path, new Set)].map(([name, declaration]) => ({ declaration, name, specifier: entry.specifier })));
|
|
442
|
+
for (const { declaration, name, specifier } of offered) {
|
|
443
|
+
const known = surface.get(name);
|
|
444
|
+
if (known === undefined)
|
|
445
|
+
surface.set(name, { declaration, entries: [specifier] });
|
|
446
|
+
else if (!known.entries.includes(specifier))
|
|
447
|
+
known.entries.push(specifier);
|
|
448
|
+
}
|
|
449
|
+
for (const value of surface.values())
|
|
450
|
+
value.entries.sort();
|
|
445
451
|
return surface;
|
|
446
452
|
};
|
|
447
|
-
var
|
|
453
|
+
var exportEntries = (value, packageName, specifier) => {
|
|
454
|
+
const here = specifier ?? packageName;
|
|
448
455
|
if (typeof value === "string")
|
|
449
|
-
return [value];
|
|
456
|
+
return [{ specifier: here, value }];
|
|
450
457
|
if (typeof value !== "object" || value === null)
|
|
451
458
|
return [];
|
|
452
|
-
return Object.entries(value).flatMap(([key, nested]) => key
|
|
459
|
+
return Object.entries(value).flatMap(([key, nested]) => key.startsWith(".") ? exportEntries(nested, packageName, key === "." ? packageName : `${packageName}${key.slice(1)}`) : exportEntries(nested, packageName, here));
|
|
453
460
|
};
|
|
454
461
|
var typeEntriesOf = (manifest, files) => {
|
|
455
462
|
const record = typeof manifest === "object" && manifest !== null ? new Map(Object.entries(manifest)) : new Map;
|
|
456
|
-
const
|
|
457
|
-
|
|
458
|
-
|
|
459
|
-
|
|
463
|
+
const named = record.get("name");
|
|
464
|
+
const packageName = typeof named === "string" ? named : "";
|
|
465
|
+
const resolved = (value, specifier) => {
|
|
466
|
+
if (typeof value !== "string")
|
|
467
|
+
return [];
|
|
468
|
+
const cleaned = value.replace(/^\.\//u, "");
|
|
469
|
+
const candidates = DECLARATION.test(cleaned) ? [cleaned] : [
|
|
470
|
+
cleaned.replace(/\.[cm]?js$/u, ".d.ts"),
|
|
471
|
+
cleaned.replace(/\.[cm]?js$/u, ".d.mts"),
|
|
472
|
+
cleaned.replace(/\.[cm]?js$/u, ".d.cts")
|
|
473
|
+
];
|
|
474
|
+
return candidates.filter((candidate) => files.has(candidate)).map((resolvedPath) => ({ path: resolvedPath, specifier }));
|
|
475
|
+
};
|
|
476
|
+
const found = [
|
|
477
|
+
...resolved(record.get("types"), packageName),
|
|
478
|
+
...resolved(record.get("typings"), packageName),
|
|
479
|
+
...exportEntries(record.get("exports"), packageName).flatMap((entry) => resolved(entry.value, entry.specifier))
|
|
460
480
|
];
|
|
461
|
-
const
|
|
462
|
-
|
|
463
|
-
|
|
464
|
-
|
|
465
|
-
|
|
466
|
-
|
|
467
|
-
|
|
481
|
+
const fallback = found.length === 0 && files.has("dist/index.d.ts") ? [{ path: "dist/index.d.ts", specifier: packageName }] : [];
|
|
482
|
+
const seen = new Set;
|
|
483
|
+
return [...found, ...fallback].filter((entry) => {
|
|
484
|
+
const key = `${entry.specifier}\x00${entry.path}`;
|
|
485
|
+
if (seen.has(key))
|
|
486
|
+
return false;
|
|
487
|
+
seen.add(key);
|
|
488
|
+
return true;
|
|
489
|
+
});
|
|
468
490
|
};
|
|
469
491
|
var SHAPE = {
|
|
470
492
|
members: "members",
|
|
@@ -519,23 +541,33 @@ var changeShape = (change) => {
|
|
|
519
541
|
};
|
|
520
542
|
var diffSurface = (before, after) => {
|
|
521
543
|
const removed = [];
|
|
544
|
+
const withdrawn = [];
|
|
522
545
|
const changed = [];
|
|
523
|
-
for (const [name,
|
|
546
|
+
for (const [name, was] of before) {
|
|
524
547
|
const now = after.get(name);
|
|
525
548
|
if (now === undefined)
|
|
526
549
|
removed.push(name);
|
|
527
|
-
|
|
550
|
+
if (now === undefined)
|
|
551
|
+
continue;
|
|
552
|
+
const lost = was.entries.filter((entry) => !now.entries.includes(entry));
|
|
553
|
+
if (lost.length > 0)
|
|
554
|
+
withdrawn.push({ from: lost, name });
|
|
555
|
+
if (now.declaration !== was.declaration)
|
|
528
556
|
changed.push({
|
|
529
|
-
after: now,
|
|
530
|
-
before: declaration,
|
|
557
|
+
after: now.declaration,
|
|
558
|
+
before: was.declaration,
|
|
531
559
|
name,
|
|
532
|
-
shape: changeShape({
|
|
560
|
+
shape: changeShape({
|
|
561
|
+
after: now.declaration,
|
|
562
|
+
before: was.declaration
|
|
563
|
+
})
|
|
533
564
|
});
|
|
534
565
|
}
|
|
535
566
|
return {
|
|
536
567
|
added: [...after.keys()].filter((name) => !before.has(name)),
|
|
537
568
|
changed,
|
|
538
|
-
removed
|
|
569
|
+
removed,
|
|
570
|
+
withdrawn
|
|
539
571
|
};
|
|
540
572
|
};
|
|
541
573
|
var notable = (diff) => diff.changed.filter((change) => change.shape === "parameters" || change.shape === "members");
|
|
@@ -671,10 +703,11 @@ var describeReconciliation = (found) => {
|
|
|
671
703
|
members: "changed the members of its type",
|
|
672
704
|
parameters: "changed what it is called with",
|
|
673
705
|
removed: "is no longer exported",
|
|
674
|
-
types: "changed its declaration"
|
|
706
|
+
types: "changed its declaration",
|
|
707
|
+
withdrawn: "is no longer exported from"
|
|
675
708
|
};
|
|
676
709
|
for (const entry of found.unannounced)
|
|
677
|
-
lines.push(`\`${entry.name}\` ${moved[entry.kind]} and no entry says so`);
|
|
710
|
+
lines.push(entry.kind === "withdrawn" ? `\`${entry.name}\` ${moved.withdrawn} ${(entry.from ?? []).join(", ")} and no entry says so` : `\`${entry.name}\` ${moved[entry.kind]} and no entry says so`);
|
|
678
711
|
for (const name of found.unknownSymbols)
|
|
679
712
|
lines.push(`an entry names \`${name}\`, which this package does not export`);
|
|
680
713
|
lines.push(...found.wrongMigrations);
|
|
@@ -685,6 +718,7 @@ var reconcile = (input) => {
|
|
|
685
718
|
const mentioned = new Set(input.changes.flatMap((change) => [...change.symbols ?? []]));
|
|
686
719
|
const unannounced = [
|
|
687
720
|
...input.diff.removed.filter((name) => !disruptive.has(name)).map((name) => ({ kind: "removed", name })),
|
|
721
|
+
...input.diff.withdrawn.filter((entry) => !disruptive.has(entry.name)).map((entry) => ({ from: entry.from, kind: "withdrawn", name: entry.name })),
|
|
688
722
|
...input.diff.changed.filter((change) => !mentioned.has(change.name) && change.shape !== "unknown").map((change) => ({ kind: change.shape, name: change.name }))
|
|
689
723
|
];
|
|
690
724
|
const unknownSymbols = [
|
|
@@ -710,6 +744,8 @@ var MAX_ARCHIVE_BYTES = MAX_ARCHIVE_MEBIBYTES * MEBIBYTE2;
|
|
|
710
744
|
var MAX_DOWNLOAD_MEBIBYTES = 32;
|
|
711
745
|
var MAX_DOWNLOAD_BYTES = MAX_DOWNLOAD_MEBIBYTES * MEBIBYTE2;
|
|
712
746
|
var TIMEOUT_MS = 20000;
|
|
747
|
+
var NOT_FOUND = 404;
|
|
748
|
+
var NOTHING_PUBLISHED = { published: [], tarballs: {} };
|
|
713
749
|
var PACKAGE_NAME = /^(?:@[a-z0-9][a-z0-9._-]*\/)?[a-z0-9][a-z0-9._-]*$/u;
|
|
714
750
|
var MAX_NAME_LENGTH = 214;
|
|
715
751
|
var isPackageName = (name) => name.length <= MAX_NAME_LENGTH && PACKAGE_NAME.test(name);
|
|
@@ -753,9 +789,25 @@ var packageArchive = async (name, version, options = {}) => {
|
|
|
753
789
|
return;
|
|
754
790
|
}
|
|
755
791
|
};
|
|
792
|
+
var packuments = new Map;
|
|
793
|
+
var SECONDS_PER_MINUTE = 60;
|
|
794
|
+
var MILLISECONDS_PER_SECOND = 1000;
|
|
795
|
+
var PACKUMENT_MINUTES = 5;
|
|
796
|
+
var PACKUMENT_TTL_MS = PACKUMENT_MINUTES * SECONDS_PER_MINUTE * MILLISECONDS_PER_SECOND;
|
|
797
|
+
var forgetRegistry = () => packuments.clear();
|
|
756
798
|
var packageVersions = async (name, options = {}) => {
|
|
757
799
|
if (!isPackageName(name))
|
|
758
800
|
return;
|
|
801
|
+
const cacheable = options.fetch === undefined && options.registry === undefined;
|
|
802
|
+
const known = cacheable ? packuments.get(name) : undefined;
|
|
803
|
+
if (known !== undefined && Date.now() - known.asked < PACKUMENT_TTL_MS)
|
|
804
|
+
return known.value;
|
|
805
|
+
const value = askRegistry(name, options);
|
|
806
|
+
if (cacheable)
|
|
807
|
+
packuments.set(name, { asked: Date.now(), value });
|
|
808
|
+
return value;
|
|
809
|
+
};
|
|
810
|
+
var askRegistry = async (name, options) => {
|
|
759
811
|
const call = options.fetch ?? fetch;
|
|
760
812
|
const url = `${options.registry ?? DEFAULT_REGISTRY}/${name.replace("/", "%2f")}`;
|
|
761
813
|
try {
|
|
@@ -764,9 +816,9 @@ var packageVersions = async (name, options = {}) => {
|
|
|
764
816
|
signal: AbortSignal.timeout(TIMEOUT_MS)
|
|
765
817
|
});
|
|
766
818
|
const response = abbreviated.ok ? abbreviated : await call(url, { signal: AbortSignal.timeout(TIMEOUT_MS) });
|
|
767
|
-
if (
|
|
768
|
-
return;
|
|
769
|
-
return
|
|
819
|
+
if (response.ok)
|
|
820
|
+
return readPackument(await response.json());
|
|
821
|
+
return response.status === NOT_FOUND ? NOTHING_PUBLISHED : undefined;
|
|
770
822
|
} catch {
|
|
771
823
|
return;
|
|
772
824
|
}
|
|
@@ -1111,9 +1163,11 @@ var check = async (root, flags) => {
|
|
|
1111
1163
|
const versions = offline ? undefined : await packageVersions(manifest.name ?? "");
|
|
1112
1164
|
if (!offline && versions === undefined)
|
|
1113
1165
|
failures.push(`the registry did not answer for ${manifest.name}, so nothing was compared \u2014 pass --offline to skip the comparison deliberately`);
|
|
1166
|
+
if (versions !== undefined && versions.published.length === 0)
|
|
1167
|
+
console.warn(` (${manifest.name} has never been published, so there was nothing to compare)`);
|
|
1114
1168
|
const baseline = (versions?.published ?? []).filter((version) => compareVersions(version, manifest.version) <= 0).sort(compareVersions).at(LAST2);
|
|
1115
|
-
if (versions !== undefined && baseline === undefined && !offline)
|
|
1116
|
-
console.warn(` (nothing published at or below ${manifest.version}
|
|
1169
|
+
if (versions !== undefined && versions.published.length > 0 && baseline === undefined && !offline)
|
|
1170
|
+
console.warn(` (nothing published at or below ${manifest.version}, so there was nothing to compare)`);
|
|
1117
1171
|
if (built !== undefined && baseline !== undefined) {
|
|
1118
1172
|
const published = await publishedSurface(manifest.name ?? "", baseline);
|
|
1119
1173
|
if (published === undefined)
|
|
@@ -1249,5 +1303,5 @@ await main().catch((error) => {
|
|
|
1249
1303
|
process.exit(1);
|
|
1250
1304
|
});
|
|
1251
1305
|
|
|
1252
|
-
//# debugId=
|
|
1306
|
+
//# debugId=220FF2C6ABB0241464756E2164756E21
|
|
1253
1307
|
//# sourceMappingURL=cli.js.map
|