@absolutejs/changelog 0.1.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 +13 -0
- package/LICENSE +21 -0
- package/README.md +158 -0
- package/changelog.json +16 -0
- package/dist/apply.d.ts +34 -0
- package/dist/authoring.d.ts +79 -0
- package/dist/cli.d.ts +2 -0
- package/dist/cli.js +1007 -0
- package/dist/cli.js.map +19 -0
- package/dist/entries.d.ts +23 -0
- package/dist/index.d.ts +11 -0
- package/dist/index.js +946 -0
- package/dist/index.js.map +20 -0
- package/dist/read.d.ts +63 -0
- package/dist/reconcile.d.ts +23 -0
- package/dist/registry.d.ts +32 -0
- package/dist/surface.d.ts +55 -0
- package/dist/tarball.d.ts +17 -0
- package/dist/types.d.ts +117 -0
- package/dist/versioning.d.ts +25 -0
- package/dist/write.d.ts +10 -0
- package/package.json +72 -0
package/CHANGELOG.md
ADDED
|
@@ -0,0 +1,13 @@
|
|
|
1
|
+
# Changelog
|
|
2
|
+
|
|
3
|
+
All notable changes to `@absolutejs/changelog`.
|
|
4
|
+
|
|
5
|
+
This file is generated by `absolute-changelog` from the entries in
|
|
6
|
+
`changelog/`. Edit an entry, not this file — and add new ones under
|
|
7
|
+
`changelog/unreleased/`.
|
|
8
|
+
|
|
9
|
+
## 0.1.0 — 2026-09-10
|
|
10
|
+
|
|
11
|
+
### Added
|
|
12
|
+
|
|
13
|
+
- **The changelog contract: typed entries, a gate that checks them against the package's own types, and migrations as data**
|
package/LICENSE
ADDED
|
@@ -0,0 +1,21 @@
|
|
|
1
|
+
MIT License
|
|
2
|
+
|
|
3
|
+
Copyright (c) 2026 Alex Kahn
|
|
4
|
+
|
|
5
|
+
Permission is hereby granted, free of charge, to any person obtaining a copy
|
|
6
|
+
of this software and associated documentation files (the "Software"), to deal
|
|
7
|
+
in the Software without restriction, including without limitation the rights
|
|
8
|
+
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
|
9
|
+
copies of the Software, and to permit persons to whom the Software is
|
|
10
|
+
furnished to do so, subject to the following conditions:
|
|
11
|
+
|
|
12
|
+
The above copyright notice and this permission notice shall be included in all
|
|
13
|
+
copies or substantial portions of the Software.
|
|
14
|
+
|
|
15
|
+
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
|
16
|
+
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
|
17
|
+
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
|
18
|
+
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
|
19
|
+
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
|
20
|
+
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
|
21
|
+
SOFTWARE.
|
package/README.md
ADDED
|
@@ -0,0 +1,158 @@
|
|
|
1
|
+
# @absolutejs/changelog
|
|
2
|
+
|
|
3
|
+
The changelog contract for the AbsoluteJS packages.
|
|
4
|
+
|
|
5
|
+
A changelog is worth having when it can be trusted and acted on. Prose
|
|
6
|
+
changelogs manage neither: they rot because writing them is a discipline, and
|
|
7
|
+
they cannot be acted on because "refactored the server options" is a true
|
|
8
|
+
sentence that does not say which export moved or what to write instead.
|
|
9
|
+
|
|
10
|
+
This package makes both possible.
|
|
11
|
+
|
|
12
|
+
- **Entries are typed.** A change is written as TypeScript, so the compiler
|
|
13
|
+
insists that a breaking change names the exports it breaks and says how to
|
|
14
|
+
migrate. The entries that cost somebody an afternoon are the ones that
|
|
15
|
+
cannot be filed empty.
|
|
16
|
+
- **The release gate checks them against the package.** Before a release goes
|
|
17
|
+
out, the exports that vanished or changed shape are compared with the
|
|
18
|
+
exports the entries name. Forget an entry and the release stops. Nobody has
|
|
19
|
+
to remember.
|
|
20
|
+
- **Migrations are data.** A rename and a move are the overwhelming majority
|
|
21
|
+
of real breaking changes, and both are mechanical. Written as data they can
|
|
22
|
+
be applied — by a CLI, an editor, or a hosted upgrade button — without
|
|
23
|
+
running a line of code that came from a registry.
|
|
24
|
+
|
|
25
|
+
## Adopting it
|
|
26
|
+
|
|
27
|
+
```sh
|
|
28
|
+
bun add -d @absolutejs/changelog
|
|
29
|
+
bunx absolute-changelog adopt
|
|
30
|
+
```
|
|
31
|
+
|
|
32
|
+
`adopt` creates `changelog/unreleased/`, keeps whatever `CHANGELOG.md` already
|
|
33
|
+
said as `changelog/history.md`, adds `changelog.json` and `CHANGELOG.md` to the
|
|
34
|
+
package's `files`, and makes sure your tsconfig compiles the entries — that
|
|
35
|
+
last one matters, because an entry nothing compiles is an entry nobody checks.
|
|
36
|
+
|
|
37
|
+
Then add the gate to the release chain:
|
|
38
|
+
|
|
39
|
+
```json
|
|
40
|
+
"check:package": "bun run typecheck && bun run build && bun run test && absolute-changelog check"
|
|
41
|
+
```
|
|
42
|
+
|
|
43
|
+
Put it after `build`: the check compares the types you are about to publish
|
|
44
|
+
against the ones you published last time, and it needs `dist` to exist.
|
|
45
|
+
|
|
46
|
+
## Writing a change
|
|
47
|
+
|
|
48
|
+
One file per change, so two branches adding one do not meet over the same
|
|
49
|
+
line:
|
|
50
|
+
|
|
51
|
+
```sh
|
|
52
|
+
bunx absolute-changelog add --kind added --summary "listen accepts a unix socket path"
|
|
53
|
+
|
|
54
|
+
bunx absolute-changelog add \
|
|
55
|
+
--kind breaking \
|
|
56
|
+
--summary "start is now listen" \
|
|
57
|
+
--symbol start \
|
|
58
|
+
--instruction "Import listen instead of start." \
|
|
59
|
+
--rename-to listen
|
|
60
|
+
```
|
|
61
|
+
|
|
62
|
+
Which writes `changelog/unreleased/start-is-now-listen.ts`:
|
|
63
|
+
|
|
64
|
+
```ts
|
|
65
|
+
import type { Change } from "@absolutejs/changelog";
|
|
66
|
+
|
|
67
|
+
const entry: Change = {
|
|
68
|
+
kind: "breaking",
|
|
69
|
+
migration: {
|
|
70
|
+
instruction: "Import listen instead of start.",
|
|
71
|
+
rename: { from: "start", to: "listen" },
|
|
72
|
+
},
|
|
73
|
+
summary: "start is now listen",
|
|
74
|
+
symbols: ["start"],
|
|
75
|
+
};
|
|
76
|
+
|
|
77
|
+
export default entry;
|
|
78
|
+
```
|
|
79
|
+
|
|
80
|
+
The import is type-only, so an entry has nothing to resolve at run time and
|
|
81
|
+
the gate works in a checkout with no dependencies installed.
|
|
82
|
+
|
|
83
|
+
### The kinds
|
|
84
|
+
|
|
85
|
+
`breaking` and `removed` cost a consumer work, and the type refuses them
|
|
86
|
+
without `symbols` and a `migration`. `added`, `changed`, `deprecated`,
|
|
87
|
+
`fixed`, `security` and `internal` do not.
|
|
88
|
+
|
|
89
|
+
### The migrations
|
|
90
|
+
|
|
91
|
+
| Shape | What it means | Applied |
|
|
92
|
+
| ----------------------------- | ----------------------------------------------- | ------- |
|
|
93
|
+
| `rename: { from, to }` | An export kept its meaning and changed its name | yes |
|
|
94
|
+
| `moved: { symbol, from, to }` | An export moved to another entry point | yes |
|
|
95
|
+
| `resubpath: { from, to }` | A whole entry point moved | yes |
|
|
96
|
+
| `manual: true` | Somebody has to read the call | no |
|
|
97
|
+
|
|
98
|
+
`manual` is not a failure. A changed meaning is not a rewrite anybody should
|
|
99
|
+
automate, and pretending otherwise is worse than saying so.
|
|
100
|
+
|
|
101
|
+
## Releasing
|
|
102
|
+
|
|
103
|
+
```sh
|
|
104
|
+
bunx absolute-changelog release
|
|
105
|
+
```
|
|
106
|
+
|
|
107
|
+
Works out the version from the entries — a breaking change moves the minor
|
|
108
|
+
below `1.0.0` and the major above it, and a version already on a prerelease
|
|
109
|
+
line moves along that line — then writes `changelog.json` and `CHANGELOG.md`,
|
|
110
|
+
bumps `package.json`, and deletes the entries it consumed.
|
|
111
|
+
|
|
112
|
+
`--as major|minor|patch|prerelease` overrides the inference, `--version x.y.z`
|
|
113
|
+
overrides it entirely, and `--dry` shows the release without writing anything.
|
|
114
|
+
|
|
115
|
+
## The gate
|
|
116
|
+
|
|
117
|
+
```sh
|
|
118
|
+
bunx absolute-changelog check
|
|
119
|
+
```
|
|
120
|
+
|
|
121
|
+
- every entry parses, and the disruptive ones carry what they must;
|
|
122
|
+
- `CHANGELOG.md` is what the entries say it should be — it is generated, and
|
|
123
|
+
editing it is how the two copies drift;
|
|
124
|
+
- the version in `package.json` and the newest release agree;
|
|
125
|
+
- the entries name every export that moved since the last published version.
|
|
126
|
+
|
|
127
|
+
`--offline` skips the last one, which is the only one that talks to the
|
|
128
|
+
registry.
|
|
129
|
+
|
|
130
|
+
## Reading somebody else's
|
|
131
|
+
|
|
132
|
+
Anything deciding whether an upgrade is safe reads the published document:
|
|
133
|
+
|
|
134
|
+
```ts
|
|
135
|
+
import {
|
|
136
|
+
applyMigrations,
|
|
137
|
+
migrationsBetween,
|
|
138
|
+
publishedChangelog,
|
|
139
|
+
} from "@absolutejs/changelog";
|
|
140
|
+
|
|
141
|
+
const changelog = await publishedChangelog("@absolutejs/example", "2.1.0");
|
|
142
|
+
const migrations = changelog
|
|
143
|
+
? migrationsBetween(changelog, "1.4.0", "2.1.0")
|
|
144
|
+
: [];
|
|
145
|
+
|
|
146
|
+
const upgraded = applyMigrations(source, {
|
|
147
|
+
migrations: migrations.map((at) => at.migration),
|
|
148
|
+
packageName: "@absolutejs/example",
|
|
149
|
+
});
|
|
150
|
+
```
|
|
151
|
+
|
|
152
|
+
For a package that has not adopted this yet, `publishedSurface` and
|
|
153
|
+
`diffSurface` compare the published types of two versions instead — less than
|
|
154
|
+
a changelog, and much more than nothing.
|
|
155
|
+
|
|
156
|
+
## Licence
|
|
157
|
+
|
|
158
|
+
MIT.
|
package/changelog.json
ADDED
|
@@ -0,0 +1,16 @@
|
|
|
1
|
+
{
|
|
2
|
+
"contract": 1,
|
|
3
|
+
"name": "@absolutejs/changelog",
|
|
4
|
+
"releases": [
|
|
5
|
+
{
|
|
6
|
+
"changes": [
|
|
7
|
+
{
|
|
8
|
+
"kind": "added",
|
|
9
|
+
"summary": "The changelog contract: typed entries, a gate that checks them against the package's own types, and migrations as data"
|
|
10
|
+
}
|
|
11
|
+
],
|
|
12
|
+
"date": "2026-09-10",
|
|
13
|
+
"version": "0.1.0"
|
|
14
|
+
}
|
|
15
|
+
]
|
|
16
|
+
}
|
package/dist/apply.d.ts
ADDED
|
@@ -0,0 +1,34 @@
|
|
|
1
|
+
import { type Migration } from "./types";
|
|
2
|
+
export type MigrationResult = {
|
|
3
|
+
/** Whether anything in this file changed. */
|
|
4
|
+
changed: boolean;
|
|
5
|
+
/** Anything the reader of the diff should know. */
|
|
6
|
+
notes: string[];
|
|
7
|
+
source: string;
|
|
8
|
+
};
|
|
9
|
+
/**
|
|
10
|
+
* Apply one migration to one file.
|
|
11
|
+
*
|
|
12
|
+
* A file the migration does not touch comes back unchanged, which is the
|
|
13
|
+
* ordinary case: a project imports a dozen names from a package that moved a
|
|
14
|
+
* hundred.
|
|
15
|
+
*/
|
|
16
|
+
export declare const applyMigration: (source: string, input: {
|
|
17
|
+
migration: Migration;
|
|
18
|
+
packageName: string;
|
|
19
|
+
}) => {
|
|
20
|
+
changed: boolean;
|
|
21
|
+
notes: string[];
|
|
22
|
+
source: string;
|
|
23
|
+
};
|
|
24
|
+
/**
|
|
25
|
+
* Apply migrations in order.
|
|
26
|
+
*
|
|
27
|
+
* Order matters: a symbol renamed in one release and moved in the next has to
|
|
28
|
+
* be renamed first, or the move goes looking for a name that is no longer
|
|
29
|
+
* there.
|
|
30
|
+
*/
|
|
31
|
+
export declare const applyMigrations: (source: string, input: {
|
|
32
|
+
migrations: readonly Migration[];
|
|
33
|
+
packageName: string;
|
|
34
|
+
}) => MigrationResult;
|
|
@@ -0,0 +1,79 @@
|
|
|
1
|
+
import type { Change, Migration } from "./types";
|
|
2
|
+
/**
|
|
3
|
+
* Record one change.
|
|
4
|
+
*
|
|
5
|
+
* Written as its own file under `changelog/unreleased/`, so two branches each
|
|
6
|
+
* adding a change do not meet over the same line of the same file. The file
|
|
7
|
+
* name is yours; something short and about the change reads best in a diff.
|
|
8
|
+
*
|
|
9
|
+
* ```ts
|
|
10
|
+
* import { change } from '@absolutejs/changelog';
|
|
11
|
+
*
|
|
12
|
+
* export default change({
|
|
13
|
+
* kind: 'breaking',
|
|
14
|
+
* summary: 'serve() takes an options object',
|
|
15
|
+
* symbols: ['serve'],
|
|
16
|
+
* migration: {
|
|
17
|
+
* instruction: 'Pass { port } instead of a bare port.',
|
|
18
|
+
* manual: true
|
|
19
|
+
* }
|
|
20
|
+
* });
|
|
21
|
+
* ```
|
|
22
|
+
*/
|
|
23
|
+
export declare const change: <const T extends Change>(entry: T) => T;
|
|
24
|
+
/** A migration nobody should automate, said as such rather than left off. */
|
|
25
|
+
export declare const manual: (instruction: string) => Migration;
|
|
26
|
+
/**
|
|
27
|
+
* Record an export moving between entry points.
|
|
28
|
+
*
|
|
29
|
+
* The second most common, and the one people most often discover by way of a
|
|
30
|
+
* failed build with no explanation in it.
|
|
31
|
+
*/
|
|
32
|
+
export declare const moved: (input: {
|
|
33
|
+
symbol: string;
|
|
34
|
+
from: string;
|
|
35
|
+
to: string;
|
|
36
|
+
detail?: string;
|
|
37
|
+
reference?: string;
|
|
38
|
+
}) => {
|
|
39
|
+
readonly summary: `\`${string}\` moved to \`${string}\``;
|
|
40
|
+
readonly symbols: readonly [string];
|
|
41
|
+
readonly reference?: string | undefined;
|
|
42
|
+
readonly kind: "breaking";
|
|
43
|
+
readonly migration: {
|
|
44
|
+
readonly instruction: `Import \`${string}\` from \`${string}\`.`;
|
|
45
|
+
readonly moved: {
|
|
46
|
+
readonly from: string;
|
|
47
|
+
readonly symbol: string;
|
|
48
|
+
readonly to: string;
|
|
49
|
+
};
|
|
50
|
+
};
|
|
51
|
+
readonly detail?: string | undefined;
|
|
52
|
+
};
|
|
53
|
+
/**
|
|
54
|
+
* Record a rename.
|
|
55
|
+
*
|
|
56
|
+
* The most common breaking change there is, and the one worth having a
|
|
57
|
+
* shorthand for -- both because it is written often and because writing it
|
|
58
|
+
* this way is what makes it applicable rather than readable.
|
|
59
|
+
*/
|
|
60
|
+
export declare const renamed: (input: {
|
|
61
|
+
from: string;
|
|
62
|
+
to: string;
|
|
63
|
+
/** Anything a reader needs beyond "it is called this now". */
|
|
64
|
+
detail?: string;
|
|
65
|
+
reference?: string;
|
|
66
|
+
}) => {
|
|
67
|
+
readonly summary: `\`${string}\` is now \`${string}\``;
|
|
68
|
+
readonly symbols: readonly [string];
|
|
69
|
+
readonly reference?: string | undefined;
|
|
70
|
+
readonly kind: "breaking";
|
|
71
|
+
readonly migration: {
|
|
72
|
+
readonly instruction: `Import \`${string}\` instead of \`${string}\`.`;
|
|
73
|
+
readonly rename: {
|
|
74
|
+
readonly from: string;
|
|
75
|
+
readonly to: string;
|
|
76
|
+
};
|
|
77
|
+
};
|
|
78
|
+
readonly detail?: string | undefined;
|
|
79
|
+
};
|
package/dist/cli.d.ts
ADDED