@absolutejs/changelog 0.1.2 → 0.3.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.
@@ -1,11 +1,13 @@
1
- import { type SurfaceDiff } from "./surface";
1
+ import type { SurfaceDiff } from "./surface";
2
2
  import { type Change } from "./types";
3
3
  export type Unannounced = {
4
- /** Why it matters: gone entirely, or the call shape moved. */
5
- kind: "removed" | "parameters" | "members";
4
+ /** Gone entirely, or what about its declaration moved. */
5
+ kind: "removed" | "parameters" | "members" | "types";
6
6
  name: string;
7
7
  };
8
8
  export type Reconciliation = {
9
+ /** Entries whose migration does not describe what the package did. */
10
+ wrongMigrations: string[];
9
11
  /** Exports that moved with nothing written about them. */
10
12
  unannounced: Unannounced[];
11
13
  /** Names entries claim that this package has never exported. A typo, or
@@ -15,9 +17,18 @@ export type Reconciliation = {
15
17
  /** What to print when it fails, in the order somebody fixes them. */
16
18
  export declare const describeReconciliation: (found: Reconciliation) => string[];
17
19
  export declare const reconcile: (input: {
20
+ /** Every entry that speaks for the versions being compared: the ones
21
+ * waiting for a release, and the ones a release already consumed but the
22
+ * registry has not seen yet. */
18
23
  changes: readonly Change[];
19
24
  diff: SurfaceDiff;
20
25
  /** Every name the package exported before or after, for catching entries
21
26
  * that name something that was never there. */
22
27
  known: ReadonlySet<string>;
28
+ /** What the package exports now, for checking that a migration describes
29
+ * what it says it describes. */
30
+ after?: ReadonlySet<string>;
31
+ /** The package's entry points, so a migration cannot point at one that is
32
+ * not there. Empty means "not known", and the check is skipped. */
33
+ subpaths?: ReadonlySet<string>;
23
34
  }) => Reconciliation;
@@ -1,3 +1,4 @@
1
+ export declare const isPackageName: (name: string) => boolean;
1
2
  export type RegistryOptions = {
2
3
  fetch?: typeof fetch;
3
4
  /** For a private registry, or a test. */
package/dist/surface.d.ts CHANGED
@@ -1,24 +1,17 @@
1
- /**
2
- * The exported names of one declaration file, each with the declaration it
3
- * stands for.
4
- *
5
- * Overloads and merged declarations share a name; they are joined, so adding
6
- * an overload reads as a change to that name rather than as a replacement.
7
- */
8
- export declare const exportedDeclarations: (source: string) => Map<string, string>;
9
1
  export type PackageSurface = Map<string, string>;
2
+ /** Whether a manifest claims to ship types at all. A package that ships none
3
+ * has no promise of this kind to check; one that claims some and has none is
4
+ * a package about to publish types it did not build. */
5
+ export declare const declaresTypes: (manifest: unknown) => boolean;
10
6
  /** The surface of a package tarball, already gunzipped. */
11
- export declare const surfaceOfArchive: (archive: Uint8Array) => PackageSurface;
7
+ export declare const surfaceOfArchive: (archive: Uint8Array) => PackageSurface | undefined;
12
8
  /**
13
- * Every exported name in a package's declaration files.
14
- *
15
- * A name usually appears twice: where it is declared, and in the index that
16
- * passes it along. The declaration wins wherever both are found, whichever
17
- * file the archive happened to put first -- otherwise the same package reads
18
- * as `export { syncSocket }` in one version and as its full signature in the
19
- * next, and the two compare as a change nobody made.
9
+ * The public surface of a package: what its entry points export, and what
10
+ * each of those names is declared as.
20
11
  */
21
- export declare const surfaceOfFiles: (files: Iterable<string>) => PackageSurface;
12
+ export declare const surfaceOfFiles: (files: ReadonlyMap<string, string>, entries: readonly string[]) => PackageSurface;
13
+ /** The declaration files a manifest points at, as paths inside the package. */
14
+ export declare const typeEntriesOf: (manifest: unknown, files: ReadonlyMap<string, string>) => string[];
22
15
  declare const SHAPE: {
23
16
  readonly members: "members";
24
17
  readonly parameters: "parameters";
@@ -44,12 +37,18 @@ export type SurfaceDiff = {
44
37
  /**
45
38
  * What moved between two surfaces.
46
39
  *
47
- * Removed is the one that breaks a build outright. Changed is the one that may:
48
- * a widened parameter is not a break and a narrowed one is, and telling them
49
- * apart is a job for whoever reads the two declarations -- so both are carried
50
- * rather than judged here.
40
+ * Removed is the one that breaks a build outright. Changed is the one that
41
+ * may: a widened parameter is not a break and a narrowed one is, and telling
42
+ * them apart is a job for whoever reads the two declarations -- so both are
43
+ * carried rather than judged here.
51
44
  */
52
45
  export declare const diffSurface: (before: PackageSurface, after: PackageSurface) => SurfaceDiff;
53
- /** The changes in a diff that a caller would notice. */
46
+ /**
47
+ * The changes a consumer would notice, for an advisor deciding what to put in
48
+ * front of somebody.
49
+ *
50
+ * Not for the gate: `types` changes are still changes, and the package that
51
+ * made one is the party that can say in one line whether it matters.
52
+ */
54
53
  export declare const notable: (diff: SurfaceDiff) => SurfaceChange[];
55
54
  export {};
package/dist/types.d.ts CHANGED
@@ -19,12 +19,12 @@ export type DisruptiveKind = (typeof DISRUPTIVE_KINDS)[number];
19
19
  * `manual` is the honest escape hatch. A changed meaning is not a rewrite
20
20
  * anybody should automate, and pretending otherwise is worse than saying so.
21
21
  */
22
- export type Migration =
22
+ export type Migration<TSymbol extends string = string> =
23
23
  /** An export kept its meaning and changed its name. */
24
24
  {
25
25
  readonly instruction: string;
26
26
  readonly rename: {
27
- readonly from: string;
27
+ readonly from: TSymbol;
28
28
  readonly to: string;
29
29
  };
30
30
  }
@@ -32,7 +32,7 @@ export type Migration =
32
32
  | {
33
33
  readonly instruction: string;
34
34
  readonly moved: {
35
- readonly symbol: string;
35
+ readonly symbol: TSymbol;
36
36
  readonly from: string;
37
37
  readonly to: string;
38
38
  };
@@ -50,6 +50,38 @@ export type Migration =
50
50
  readonly instruction: string;
51
51
  readonly manual: true;
52
52
  };
53
+ /**
54
+ * The names a package exports, for an entry that wants them checked.
55
+ *
56
+ * An entry can be written against the package's own public API:
57
+ *
58
+ * ```ts
59
+ * import type * as Api from "../../src/index";
60
+ * import type { Change } from "@absolutejs/changelog";
61
+ *
62
+ * export const change: Change<typeof Api> = {
63
+ * kind: "fixed",
64
+ * summary: "stops throwing on an empty list",
65
+ * symbols: ["listen"] // an export, or the compiler says so
66
+ * };
67
+ * ```
68
+ *
69
+ * Suggested rather than required, for two reasons that both matter. A
70
+ * disruptive change is usually about something that has just stopped
71
+ * existing -- the release that removes `start` is the release that writes the
72
+ * entry about it -- and `typeof Api` cannot see type-only exports at all, so
73
+ * insisting would reject an entry about `Change` or `Release` for being
74
+ * something this package definitely exports.
75
+ *
76
+ * What that leaves is an editor that completes the name and a gate that is
77
+ * certain: the release check reads the published `.d.ts`, which does carry
78
+ * the type exports, and refuses an entry naming something the package has
79
+ * never exported.
80
+ */
81
+ type Exported<TApi> = TApi extends object ? Extract<keyof TApi, string> : string;
82
+ /** Autocompleted, not restricted: an entry may name an export that has just
83
+ * stopped being one. */
84
+ type Suggested<TApi> = Exported<TApi> | (string & {});
53
85
  type ChangeBase = {
54
86
  /** One line, imperative, in the reader's terms rather than the
55
87
  * implementation's. */
@@ -66,19 +98,19 @@ type ChangeBase = {
66
98
  * the package's published types, and what lets a project be told whether the
67
99
  * change reaches any of its files. An entry without it can only ever be read.
68
100
  */
69
- export type DisruptiveChange = ChangeBase & {
101
+ export type DisruptiveChange<TApi = unknown> = ChangeBase & {
70
102
  readonly kind: DisruptiveKind;
71
- readonly symbols: NonEmpty<string>;
72
- readonly migration: Migration;
103
+ readonly symbols: NonEmpty<Suggested<TApi>>;
104
+ readonly migration: Migration<Suggested<TApi>>;
73
105
  };
74
- export type OrdinaryChange = ChangeBase & {
106
+ export type OrdinaryChange<TApi = unknown> = ChangeBase & {
75
107
  readonly kind: Exclude<ChangeKind, DisruptiveKind>;
76
108
  /** Optional here, and still worth filling in: it is what connects a fix
77
109
  * to the export somebody is looking at. */
78
- readonly symbols?: readonly string[];
110
+ readonly symbols?: readonly Suggested<TApi>[];
79
111
  readonly migration?: never;
80
112
  };
81
- export type Change = DisruptiveChange | OrdinaryChange;
113
+ export type Change<TApi = unknown> = DisruptiveChange<TApi> | OrdinaryChange<TApi>;
82
114
  export type Release = {
83
115
  readonly version: string;
84
116
  /** ISO date, the day it was published. */
package/dist/write.d.ts CHANGED
@@ -1,4 +1,13 @@
1
1
  import { type Changelog } from "./types";
2
+ /**
3
+ * The line that says this file was generated.
4
+ *
5
+ * Load-bearing: adopting a package reads whatever `CHANGELOG.md` said before
6
+ * and keeps it as history, and on a second adoption the file it generated the
7
+ * first time is what it finds. Without a way to recognise its own output it
8
+ * files that as history and prints the header underneath itself.
9
+ */
10
+ export declare const GENERATED_MARKER = "This file is generated by `absolute-changelog`";
2
11
  export type MarkdownOptions = {
3
12
  /** Whatever the package's changelog said before it was generated, kept
4
13
  * under the releases that came after it. History is not noise. */
package/package.json CHANGED
@@ -1,72 +1,76 @@
1
1
  {
2
- "name": "@absolutejs/changelog",
3
- "version": "0.1.2",
4
- "description": "The changelog contract for the AbsoluteJS packages. Changes are written as typed entries the compiler checks, the release gate reconciles them against the package's own published types so a change nobody wrote down cannot ship, and migrations are data rather than prose so an upgrade can be applied instead of read.",
5
- "repository": {
6
- "type": "git",
7
- "url": "git+https://github.com/absolutejs/changelog.git"
8
- },
9
- "homepage": "https://github.com/absolutejs/changelog",
10
- "bugs": {
11
- "url": "https://github.com/absolutejs/changelog/issues"
12
- },
13
- "main": "./dist/index.js",
14
- "module": "./dist/index.js",
15
- "types": "./dist/index.d.ts",
16
- "type": "module",
17
- "license": "MIT",
18
- "author": "Alex Kahn",
19
- "exports": {
20
- ".": {
21
- "types": "./dist/index.d.ts",
22
- "import": "./dist/index.js",
23
- "default": "./dist/index.js"
24
- }
25
- },
26
- "bin": {
27
- "absolute-changelog": "dist/cli.js"
28
- },
29
- "publishConfig": {
30
- "access": "public"
31
- },
32
- "files": [
33
- "CHANGELOG.md",
34
- "LICENSE",
35
- "README.md",
36
- "changelog.json",
37
- "dist"
38
- ],
39
- "scripts": {
40
- "build": "rm -rf dist && bun build src/index.ts src/cli.ts --outdir dist --root src --sourcemap --target=bun && tsc --project tsconfig.build.json",
41
- "test": "bun test tests/",
42
- "typecheck": "tsc --noEmit",
43
- "format": "prettier --write \"./**/*.{ts,json,md}\"",
44
- "lint": "eslint . --max-warnings 0",
45
- "changelog": "bun src/cli.ts",
46
- "check:package": "bun run typecheck && bun run lint && bun run test && bun run build && bun src/cli.ts check",
47
- "release": "bun run format && bun run check:package && bun publish"
48
- },
49
- "keywords": [
50
- "absolutejs",
51
- "changelog",
52
- "codemod",
53
- "migration",
54
- "release",
55
- "semver",
56
- "upgrade"
57
- ],
58
- "devDependencies": {
59
- "@eslint/js": "^10.0.1",
60
- "@stylistic/eslint-plugin": "^5.10.0",
61
- "@types/bun": "^1.3.14",
62
- "@typescript-eslint/parser": "^8.68.0",
63
- "eslint": "^10.9.1",
64
- "eslint-plugin-absolute": "^0.11.34",
65
- "eslint-plugin-promise": "^7.3.0",
66
- "eslint-plugin-security": "^4.0.1",
67
- "globals": "^17.11.0",
68
- "prettier": "^3.5.3",
69
- "typescript": "^5.8.3",
70
- "typescript-eslint": "^8.56.0"
71
- }
2
+ "name": "@absolutejs/changelog",
3
+ "version": "0.3.0",
4
+ "description": "The changelog contract for the AbsoluteJS packages. Changes are written as typed entries the compiler checks, the release gate reconciles them against the package's own published types so a change nobody wrote down cannot ship, and migrations are data rather than prose so an upgrade can be applied instead of read.",
5
+ "repository": {
6
+ "type": "git",
7
+ "url": "git+https://github.com/absolutejs/changelog.git"
8
+ },
9
+ "homepage": "https://github.com/absolutejs/changelog",
10
+ "bugs": {
11
+ "url": "https://github.com/absolutejs/changelog/issues"
12
+ },
13
+ "main": "./dist/index.js",
14
+ "module": "./dist/index.js",
15
+ "types": "./dist/index.d.ts",
16
+ "type": "module",
17
+ "license": "MIT",
18
+ "author": "Alex Kahn",
19
+ "exports": {
20
+ ".": {
21
+ "types": "./dist/index.d.ts",
22
+ "import": "./dist/index.js",
23
+ "default": "./dist/index.js"
24
+ }
25
+ },
26
+ "bin": {
27
+ "absolute-changelog": "dist/cli.js"
28
+ },
29
+ "publishConfig": {
30
+ "access": "public"
31
+ },
32
+ "files": [
33
+ "CHANGELOG.md",
34
+ "LICENSE",
35
+ "README.md",
36
+ "changelog.json",
37
+ "dist"
38
+ ],
39
+ "scripts": {
40
+ "build": "rm -rf dist && bun build src/index.ts src/cli.ts --outdir dist --root src --sourcemap --target=bun --external typescript && tsc --project tsconfig.build.json",
41
+ "test": "bun test tests/",
42
+ "typecheck": "tsc --noEmit",
43
+ "format": "prettier --write \"./**/*.{ts,json,md}\"",
44
+ "lint": "eslint . --max-warnings 0",
45
+ "changelog": "bun src/cli.ts",
46
+ "check:package": "bun run typecheck && bun run lint && bun run test && bun run build && bun src/cli.ts check",
47
+ "release": "bun run format && bun run check:package && bun publish",
48
+ "prepublishOnly": "bun src/cli.ts check"
49
+ },
50
+ "keywords": [
51
+ "absolutejs",
52
+ "changelog",
53
+ "codemod",
54
+ "migration",
55
+ "release",
56
+ "semver",
57
+ "upgrade"
58
+ ],
59
+ "devDependencies": {
60
+ "@eslint/js": "^10.0.1",
61
+ "@stylistic/eslint-plugin": "^5.10.0",
62
+ "@types/bun": "^1.3.14",
63
+ "@typescript-eslint/parser": "^8.68.0",
64
+ "eslint": "^10.9.1",
65
+ "eslint-plugin-absolute": "^0.11.34",
66
+ "eslint-plugin-promise": "^7.3.0",
67
+ "eslint-plugin-security": "^4.0.1",
68
+ "globals": "^17.11.0",
69
+ "prettier": "^3.5.3",
70
+ "typescript": "^5.8.3",
71
+ "typescript-eslint": "^8.56.0"
72
+ },
73
+ "dependencies": {
74
+ "typescript": "^5.8.3"
75
+ }
72
76
  }