@connextar/house 0.15.2 → 0.16.1
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 +64 -0
- package/dist/changelog/changelog.d.ts +10 -1
- package/dist/changelog/changelog.d.ts.map +1 -1
- package/dist/changelog/changelog.js +15 -5
- package/dist/changelog/changelog.js.map +1 -1
- package/dist/guides/guide-article.d.ts +27 -0
- package/dist/guides/guide-article.d.ts.map +1 -0
- package/dist/guides/guide-article.js +19 -0
- package/dist/guides/guide-article.js.map +1 -0
- package/dist/guides/guide-toc.d.ts +22 -0
- package/dist/guides/guide-toc.d.ts.map +1 -0
- package/dist/guides/guide-toc.js +66 -0
- package/dist/guides/guide-toc.js.map +1 -0
- package/dist/guides/guides-landing.d.ts +14 -0
- package/dist/guides/guides-landing.d.ts.map +1 -0
- package/dist/guides/guides-landing.js +12 -0
- package/dist/guides/guides-landing.js.map +1 -0
- package/dist/guides/guides-search.d.ts +13 -0
- package/dist/guides/guides-search.d.ts.map +1 -0
- package/dist/guides/guides-search.js +68 -0
- package/dist/guides/guides-search.js.map +1 -0
- package/dist/guides/guides-shell.d.ts +15 -0
- package/dist/guides/guides-shell.d.ts.map +1 -0
- package/dist/guides/guides-shell.js +19 -0
- package/dist/guides/guides-shell.js.map +1 -0
- package/dist/guides/guides-sidebar.d.ts +10 -0
- package/dist/guides/guides-sidebar.d.ts.map +1 -0
- package/dist/guides/guides-sidebar.js +29 -0
- package/dist/guides/guides-sidebar.js.map +1 -0
- package/dist/guides/index.d.ts +8 -0
- package/dist/guides/index.d.ts.map +1 -0
- package/dist/guides/index.js +8 -0
- package/dist/guides/index.js.map +1 -0
- package/dist/guides/library.d.ts +85 -0
- package/dist/guides/library.d.ts.map +1 -0
- package/dist/guides/library.js +63 -0
- package/dist/guides/library.js.map +1 -0
- package/dist/release/changelog.d.ts +84 -0
- package/dist/release/changelog.d.ts.map +1 -1
- package/dist/release/changelog.js +117 -0
- package/dist/release/changelog.js.map +1 -1
- package/dist/release/index.d.ts +1 -1
- package/dist/release/index.d.ts.map +1 -1
- package/dist/release/index.js +1 -1
- package/dist/release/index.js.map +1 -1
- package/docs/ReleaseProcess.md +27 -4
- package/package.json +6 -1
|
@@ -0,0 +1,10 @@
|
|
|
1
|
+
import { type GuideNavCategory, type GuidesLabels } from "./library.js";
|
|
2
|
+
export interface GuidesSidebarProps {
|
|
3
|
+
nav: readonly GuideNavCategory[];
|
|
4
|
+
labels?: Partial<GuidesLabels>;
|
|
5
|
+
/** Called when a guide is chosen — the shell closes its phone menu with it. */
|
|
6
|
+
onNavigate?: () => void;
|
|
7
|
+
}
|
|
8
|
+
/** The guides grouped by category, with a filter that narrows the list as you type. */
|
|
9
|
+
export declare function GuidesSidebar({ nav, labels, onNavigate }: GuidesSidebarProps): import("react").JSX.Element;
|
|
10
|
+
//# sourceMappingURL=guides-sidebar.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"guides-sidebar.d.ts","sourceRoot":"","sources":["../../src/guides/guides-sidebar.tsx"],"names":[],"mappings":"AAOA,OAAO,EAA4B,KAAK,gBAAgB,EAAE,KAAK,YAAY,EAAE,MAAM,cAAc,CAAC;AAElG,MAAM,WAAW,kBAAkB;IACjC,GAAG,EAAE,SAAS,gBAAgB,EAAE,CAAC;IACjC,MAAM,CAAC,EAAE,OAAO,CAAC,YAAY,CAAC,CAAC;IAC/B,+EAA+E;IAC/E,UAAU,CAAC,EAAE,MAAM,IAAI,CAAC;CACzB;AAED,uFAAuF;AACvF,wBAAgB,aAAa,CAAC,EAAE,GAAG,EAAE,MAAM,EAAE,UAAU,EAAE,EAAE,kBAAkB,+BA2E5E"}
|
|
@@ -0,0 +1,29 @@
|
|
|
1
|
+
"use client";
|
|
2
|
+
import { jsx as _jsx, jsxs as _jsxs } from "react/jsx-runtime";
|
|
3
|
+
import { usePathname } from "next/navigation.js";
|
|
4
|
+
import { useMemo, useState } from "react";
|
|
5
|
+
import { cx } from "../internal/cx.js";
|
|
6
|
+
import { Link } from "../internal/next-link.js";
|
|
7
|
+
import { GUIDES_LABELS, withQuery } from "./library.js";
|
|
8
|
+
/** The guides grouped by category, with a filter that narrows the list as you type. */
|
|
9
|
+
export function GuidesSidebar({ nav, labels, onNavigate }) {
|
|
10
|
+
const text = { ...GUIDES_LABELS, ...labels };
|
|
11
|
+
const pathname = usePathname();
|
|
12
|
+
const [filter, setFilter] = useState("");
|
|
13
|
+
const query = filter.trim().toLowerCase();
|
|
14
|
+
const visible = useMemo(() => nav
|
|
15
|
+
.map((category) => ({
|
|
16
|
+
...category,
|
|
17
|
+
items: query
|
|
18
|
+
? category.items.filter((item) => `${item.title} ${item.summary} ${category.title}`.toLowerCase().includes(query))
|
|
19
|
+
: category.items,
|
|
20
|
+
}))
|
|
21
|
+
.filter((category) => category.items.length > 0), [nav, query]);
|
|
22
|
+
return (_jsxs("div", { className: "space-y-6", children: [_jsxs("div", { children: [_jsx("label", { className: "sr-only", htmlFor: "house-guides-filter", children: text.filterLabel }), _jsx("input", { id: "house-guides-filter", type: "search", value: filter, onChange: (event) => setFilter(event.target.value), placeholder: text.filterPlaceholder, className: "h-9 w-full rounded-lg border border-border bg-background px-3 text-sm outline-none transition focus-visible:border-primary focus-visible:ring-2 focus-visible:ring-primary/20" })] }), _jsx("nav", { "aria-label": text.menu, children: visible.length === 0 ? (_jsx("p", { className: "px-1 text-sm text-muted-foreground", children: withQuery(text.filterEmpty, filter) })) : (_jsx("ul", { className: "space-y-6", children: visible.map((category) => (_jsxs("li", { children: [_jsx("p", { className: "mb-2 px-2.5 text-[11px] font-bold tracking-[0.14em] text-muted-foreground uppercase", children: category.title }), _jsx("ul", { className: "space-y-0.5", children: category.items.map((item) => {
|
|
23
|
+
const active = pathname === item.href;
|
|
24
|
+
return (_jsx("li", { children: _jsx(Link, { href: item.href, onClick: onNavigate, "aria-current": active ? "page" : undefined, className: cx("block rounded-lg px-2.5 py-1.5 text-sm transition-colors", active
|
|
25
|
+
? "bg-primary/10 font-semibold text-primary"
|
|
26
|
+
: "text-muted-foreground hover:bg-accent hover:text-foreground"), children: item.title }) }, item.href));
|
|
27
|
+
}) })] }, category.id))) })) })] }));
|
|
28
|
+
}
|
|
29
|
+
//# sourceMappingURL=guides-sidebar.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"guides-sidebar.js","sourceRoot":"","sources":["../../src/guides/guides-sidebar.tsx"],"names":[],"mappings":"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"}
|
|
@@ -0,0 +1,8 @@
|
|
|
1
|
+
export { BOILERPLATE_HEADINGS, categoryOf, GUIDES_LABELS, meaningfulSections, searchGuides, type GuideLibrary, type GuideNavCategory, type GuideNavItem, type GuideSearchEntry, type GuideSearchResult, type GuidesLabels, } from "./library.js";
|
|
2
|
+
export { GuideArticle, type GuideArticleProps } from "./guide-article.js";
|
|
3
|
+
export { GuidesLanding, type GuidesLandingProps } from "./guides-landing.js";
|
|
4
|
+
export { GuidesSearch, type GuidesSearchProps } from "./guides-search.js";
|
|
5
|
+
export { GuidesShell, type GuidesShellProps } from "./guides-shell.js";
|
|
6
|
+
export { GuidesSidebar, type GuidesSidebarProps } from "./guides-sidebar.js";
|
|
7
|
+
export { GuideToc, useActiveHeading, type GuideTocProps } from "./guide-toc.js";
|
|
8
|
+
//# sourceMappingURL=index.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../../src/guides/index.ts"],"names":[],"mappings":"AAAA,OAAO,EACL,oBAAoB,EACpB,UAAU,EACV,aAAa,EACb,kBAAkB,EAClB,YAAY,EACZ,KAAK,YAAY,EACjB,KAAK,gBAAgB,EACrB,KAAK,YAAY,EACjB,KAAK,gBAAgB,EACrB,KAAK,iBAAiB,EACtB,KAAK,YAAY,GAClB,MAAM,cAAc,CAAC;AACtB,OAAO,EAAE,YAAY,EAAE,KAAK,iBAAiB,EAAE,MAAM,oBAAoB,CAAC;AAC1E,OAAO,EAAE,aAAa,EAAE,KAAK,kBAAkB,EAAE,MAAM,qBAAqB,CAAC;AAC7E,OAAO,EAAE,YAAY,EAAE,KAAK,iBAAiB,EAAE,MAAM,oBAAoB,CAAC;AAC1E,OAAO,EAAE,WAAW,EAAE,KAAK,gBAAgB,EAAE,MAAM,mBAAmB,CAAC;AACvE,OAAO,EAAE,aAAa,EAAE,KAAK,kBAAkB,EAAE,MAAM,qBAAqB,CAAC;AAC7E,OAAO,EAAE,QAAQ,EAAE,gBAAgB,EAAE,KAAK,aAAa,EAAE,MAAM,gBAAgB,CAAC"}
|
|
@@ -0,0 +1,8 @@
|
|
|
1
|
+
export { BOILERPLATE_HEADINGS, categoryOf, GUIDES_LABELS, meaningfulSections, searchGuides, } from "./library.js";
|
|
2
|
+
export { GuideArticle } from "./guide-article.js";
|
|
3
|
+
export { GuidesLanding } from "./guides-landing.js";
|
|
4
|
+
export { GuidesSearch } from "./guides-search.js";
|
|
5
|
+
export { GuidesShell } from "./guides-shell.js";
|
|
6
|
+
export { GuidesSidebar } from "./guides-sidebar.js";
|
|
7
|
+
export { GuideToc, useActiveHeading } from "./guide-toc.js";
|
|
8
|
+
//# sourceMappingURL=index.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"index.js","sourceRoot":"","sources":["../../src/guides/index.ts"],"names":[],"mappings":"AAAA,OAAO,EACL,oBAAoB,EACpB,UAAU,EACV,aAAa,EACb,kBAAkB,EAClB,YAAY,GAOb,MAAM,cAAc,CAAC;AACtB,OAAO,EAAE,YAAY,EAA0B,MAAM,oBAAoB,CAAC;AAC1E,OAAO,EAAE,aAAa,EAA2B,MAAM,qBAAqB,CAAC;AAC7E,OAAO,EAAE,YAAY,EAA0B,MAAM,oBAAoB,CAAC;AAC1E,OAAO,EAAE,WAAW,EAAyB,MAAM,mBAAmB,CAAC;AACvE,OAAO,EAAE,aAAa,EAA2B,MAAM,qBAAqB,CAAC;AAC7E,OAAO,EAAE,QAAQ,EAAE,gBAAgB,EAAsB,MAAM,gBAAgB,CAAC"}
|
|
@@ -0,0 +1,85 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* The shape a help centre is drawn from: the guides one reader may open, grouped into
|
|
3
|
+
* categories, and a small search index of their titles, summaries and section headings.
|
|
4
|
+
*
|
|
5
|
+
* Each app builds it on the server for one reader — the public, or someone signed in — so a
|
|
6
|
+
* guide that reader can't open is never sent to the browser. It is plain data, so it crosses
|
|
7
|
+
* into the client components (`GuidesShell`, `GuidesSearch`, `GuidesSidebar`) as props.
|
|
8
|
+
*/
|
|
9
|
+
export interface GuideNavItem {
|
|
10
|
+
slug: string;
|
|
11
|
+
title: string;
|
|
12
|
+
/** One or two sentences, shown on landing cards and searched. */
|
|
13
|
+
summary: string;
|
|
14
|
+
/** Where the guide opens — `/guides/<slug>`, `/dashboard/guides/<slug>`, or anywhere else. */
|
|
15
|
+
href: string;
|
|
16
|
+
}
|
|
17
|
+
export interface GuideNavCategory {
|
|
18
|
+
id: string;
|
|
19
|
+
title: string;
|
|
20
|
+
/** One line under the category's heading on a landing page. */
|
|
21
|
+
summary: string;
|
|
22
|
+
items: GuideNavItem[];
|
|
23
|
+
}
|
|
24
|
+
export interface GuideSearchEntry {
|
|
25
|
+
href: string;
|
|
26
|
+
title: string;
|
|
27
|
+
summary: string;
|
|
28
|
+
category: string;
|
|
29
|
+
/** The guide's meaningful `##`/`###` headings, so a search can land on a section. */
|
|
30
|
+
sections: {
|
|
31
|
+
id: string;
|
|
32
|
+
text: string;
|
|
33
|
+
}[];
|
|
34
|
+
}
|
|
35
|
+
export interface GuideLibrary {
|
|
36
|
+
nav: GuideNavCategory[];
|
|
37
|
+
search: GuideSearchEntry[];
|
|
38
|
+
}
|
|
39
|
+
export interface GuideSearchResult {
|
|
40
|
+
href: string;
|
|
41
|
+
title: string;
|
|
42
|
+
detail: string;
|
|
43
|
+
/** A section within a guide rather than the guide itself. */
|
|
44
|
+
section: boolean;
|
|
45
|
+
}
|
|
46
|
+
/** The category a guide sits in, for the eyebrow above its title. */
|
|
47
|
+
export declare function categoryOf(library: GuideLibrary, slug: string): GuideNavCategory | undefined;
|
|
48
|
+
/**
|
|
49
|
+
* Headings nearly every guide has, which would only crowd a search. An app building its index
|
|
50
|
+
* can drop them with `meaningfulSections`.
|
|
51
|
+
*/
|
|
52
|
+
export declare const BOILERPLATE_HEADINGS: RegExp;
|
|
53
|
+
/** A guide's headings without the boilerplate ones, ready for `GuideSearchEntry.sections`. */
|
|
54
|
+
export declare function meaningfulSections(headings: readonly {
|
|
55
|
+
id: string;
|
|
56
|
+
text: string;
|
|
57
|
+
}[]): {
|
|
58
|
+
id: string;
|
|
59
|
+
text: string;
|
|
60
|
+
}[];
|
|
61
|
+
/**
|
|
62
|
+
* Search a library's index. Every word of the query must appear in a result's title or detail;
|
|
63
|
+
* guides come before sections. An empty query lists every guide.
|
|
64
|
+
*/
|
|
65
|
+
export declare function searchGuides(index: readonly GuideSearchEntry[], rawQuery: string, limit?: number): GuideSearchResult[];
|
|
66
|
+
/** Every word the guides frame shows, in UK English by default. */
|
|
67
|
+
export interface GuidesLabels {
|
|
68
|
+
searchTrigger: string;
|
|
69
|
+
searchPlaceholder: string;
|
|
70
|
+
searchTitle: string;
|
|
71
|
+
searchAll: string;
|
|
72
|
+
searchMatches: string;
|
|
73
|
+
/** Said when nothing matched; `{query}` is replaced with what was typed. */
|
|
74
|
+
searchEmpty: string;
|
|
75
|
+
filterLabel: string;
|
|
76
|
+
filterPlaceholder: string;
|
|
77
|
+
/** Said when the filter hides every guide; `{query}` is replaced. */
|
|
78
|
+
filterEmpty: string;
|
|
79
|
+
menu: string;
|
|
80
|
+
onThisPage: string;
|
|
81
|
+
allGuides: string;
|
|
82
|
+
}
|
|
83
|
+
export declare const GUIDES_LABELS: GuidesLabels;
|
|
84
|
+
export declare function withQuery(label: string, query: string): string;
|
|
85
|
+
//# sourceMappingURL=library.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"library.d.ts","sourceRoot":"","sources":["../../src/guides/library.ts"],"names":[],"mappings":"AAAA;;;;;;;GAOG;AAEH,MAAM,WAAW,YAAY;IAC3B,IAAI,EAAE,MAAM,CAAC;IACb,KAAK,EAAE,MAAM,CAAC;IACd,iEAAiE;IACjE,OAAO,EAAE,MAAM,CAAC;IAChB,8FAA8F;IAC9F,IAAI,EAAE,MAAM,CAAC;CACd;AAED,MAAM,WAAW,gBAAgB;IAC/B,EAAE,EAAE,MAAM,CAAC;IACX,KAAK,EAAE,MAAM,CAAC;IACd,+DAA+D;IAC/D,OAAO,EAAE,MAAM,CAAC;IAChB,KAAK,EAAE,YAAY,EAAE,CAAC;CACvB;AAED,MAAM,WAAW,gBAAgB;IAC/B,IAAI,EAAE,MAAM,CAAC;IACb,KAAK,EAAE,MAAM,CAAC;IACd,OAAO,EAAE,MAAM,CAAC;IAChB,QAAQ,EAAE,MAAM,CAAC;IACjB,qFAAqF;IACrF,QAAQ,EAAE;QAAE,EAAE,EAAE,MAAM,CAAC;QAAC,IAAI,EAAE,MAAM,CAAA;KAAE,EAAE,CAAC;CAC1C;AAED,MAAM,WAAW,YAAY;IAC3B,GAAG,EAAE,gBAAgB,EAAE,CAAC;IACxB,MAAM,EAAE,gBAAgB,EAAE,CAAC;CAC5B;AAED,MAAM,WAAW,iBAAiB;IAChC,IAAI,EAAE,MAAM,CAAC;IACb,KAAK,EAAE,MAAM,CAAC;IACd,MAAM,EAAE,MAAM,CAAC;IACf,6DAA6D;IAC7D,OAAO,EAAE,OAAO,CAAC;CAClB;AAED,qEAAqE;AACrE,wBAAgB,UAAU,CAAC,OAAO,EAAE,YAAY,EAAE,IAAI,EAAE,MAAM,GAAG,gBAAgB,GAAG,SAAS,CAE5F;AAED;;;GAGG;AACH,eAAO,MAAM,oBAAoB,QAC6G,CAAC;AAE/I,8FAA8F;AAC9F,wBAAgB,kBAAkB,CAAC,QAAQ,EAAE,SAAS;IAAE,EAAE,EAAE,MAAM,CAAC;IAAC,IAAI,EAAE,MAAM,CAAA;CAAE,EAAE,GAAG;IAAE,EAAE,EAAE,MAAM,CAAC;IAAC,IAAI,EAAE,MAAM,CAAA;CAAE,EAAE,CAEpH;AAED;;;GAGG;AACH,wBAAgB,YAAY,CAAC,KAAK,EAAE,SAAS,gBAAgB,EAAE,EAAE,QAAQ,EAAE,MAAM,EAAE,KAAK,SAAK,GAAG,iBAAiB,EAAE,CAoBlH;AAED,mEAAmE;AACnE,MAAM,WAAW,YAAY;IAC3B,aAAa,EAAE,MAAM,CAAC;IACtB,iBAAiB,EAAE,MAAM,CAAC;IAC1B,WAAW,EAAE,MAAM,CAAC;IACpB,SAAS,EAAE,MAAM,CAAC;IAClB,aAAa,EAAE,MAAM,CAAC;IACtB,4EAA4E;IAC5E,WAAW,EAAE,MAAM,CAAC;IACpB,WAAW,EAAE,MAAM,CAAC;IACpB,iBAAiB,EAAE,MAAM,CAAC;IAC1B,qEAAqE;IACrE,WAAW,EAAE,MAAM,CAAC;IACpB,IAAI,EAAE,MAAM,CAAC;IACb,UAAU,EAAE,MAAM,CAAC;IACnB,SAAS,EAAE,MAAM,CAAC;CACnB;AAED,eAAO,MAAM,aAAa,EAAE,YAa3B,CAAC;AAEF,wBAAgB,SAAS,CAAC,KAAK,EAAE,MAAM,EAAE,KAAK,EAAE,MAAM,GAAG,MAAM,CAE9D"}
|
|
@@ -0,0 +1,63 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* The shape a help centre is drawn from: the guides one reader may open, grouped into
|
|
3
|
+
* categories, and a small search index of their titles, summaries and section headings.
|
|
4
|
+
*
|
|
5
|
+
* Each app builds it on the server for one reader — the public, or someone signed in — so a
|
|
6
|
+
* guide that reader can't open is never sent to the browser. It is plain data, so it crosses
|
|
7
|
+
* into the client components (`GuidesShell`, `GuidesSearch`, `GuidesSidebar`) as props.
|
|
8
|
+
*/
|
|
9
|
+
/** The category a guide sits in, for the eyebrow above its title. */
|
|
10
|
+
export function categoryOf(library, slug) {
|
|
11
|
+
return library.nav.find((category) => category.items.some((item) => item.slug === slug));
|
|
12
|
+
}
|
|
13
|
+
/**
|
|
14
|
+
* Headings nearly every guide has, which would only crowd a search. An app building its index
|
|
15
|
+
* can drop them with `meaningfulSections`.
|
|
16
|
+
*/
|
|
17
|
+
export const BOILERPLATE_HEADINGS = /^(what it does|where to find it|step by step|what each screen shows|tips|tips\s*(?:&|and)\s*good to know|troubleshooting|related guides)$/i;
|
|
18
|
+
/** A guide's headings without the boilerplate ones, ready for `GuideSearchEntry.sections`. */
|
|
19
|
+
export function meaningfulSections(headings) {
|
|
20
|
+
return headings.filter((h) => !BOILERPLATE_HEADINGS.test(h.text)).map(({ id, text }) => ({ id, text }));
|
|
21
|
+
}
|
|
22
|
+
/**
|
|
23
|
+
* Search a library's index. Every word of the query must appear in a result's title or detail;
|
|
24
|
+
* guides come before sections. An empty query lists every guide.
|
|
25
|
+
*/
|
|
26
|
+
export function searchGuides(index, rawQuery, limit = 20) {
|
|
27
|
+
const query = rawQuery.trim().toLowerCase();
|
|
28
|
+
const guides = index.map((guide) => ({
|
|
29
|
+
href: guide.href,
|
|
30
|
+
title: guide.title,
|
|
31
|
+
detail: `${guide.category} · ${guide.summary}`,
|
|
32
|
+
section: false,
|
|
33
|
+
}));
|
|
34
|
+
if (!query)
|
|
35
|
+
return guides;
|
|
36
|
+
const terms = query.split(/\s+/);
|
|
37
|
+
const matches = (value) => terms.every((term) => value.toLowerCase().includes(term));
|
|
38
|
+
const sections = index.flatMap((guide) => guide.sections.map((section) => ({
|
|
39
|
+
href: `${guide.href}#${section.id}`,
|
|
40
|
+
title: section.text,
|
|
41
|
+
detail: guide.title,
|
|
42
|
+
section: true,
|
|
43
|
+
})));
|
|
44
|
+
return [...guides, ...sections].filter((entry) => matches(`${entry.title} ${entry.detail}`)).slice(0, limit);
|
|
45
|
+
}
|
|
46
|
+
export const GUIDES_LABELS = {
|
|
47
|
+
searchTrigger: "Search the guides…",
|
|
48
|
+
searchPlaceholder: "Search guides and sections…",
|
|
49
|
+
searchTitle: "Search the guides",
|
|
50
|
+
searchAll: "All guides",
|
|
51
|
+
searchMatches: "Matches",
|
|
52
|
+
searchEmpty: "Nothing matched “{query}”. Try another word.",
|
|
53
|
+
filterLabel: "Filter guides",
|
|
54
|
+
filterPlaceholder: "Filter guides…",
|
|
55
|
+
filterEmpty: "No guide matches “{query}”.",
|
|
56
|
+
menu: "Guides",
|
|
57
|
+
onThisPage: "On this page",
|
|
58
|
+
allGuides: "All guides",
|
|
59
|
+
};
|
|
60
|
+
export function withQuery(label, query) {
|
|
61
|
+
return label.replace("{query}", query.trim());
|
|
62
|
+
}
|
|
63
|
+
//# sourceMappingURL=library.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"library.js","sourceRoot":"","sources":["../../src/guides/library.ts"],"names":[],"mappings":"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"}
|
|
@@ -10,6 +10,14 @@
|
|
|
10
10
|
/** The kind of change — drives the coloured tag. `changed` means someone may need to act. */
|
|
11
11
|
export type ChangeType = "new" | "improved" | "changed" | "fixed";
|
|
12
12
|
export declare const CHANGE_TYPES: readonly ChangeType[];
|
|
13
|
+
/**
|
|
14
|
+
* Who may read a change. The full changelog is the team's: every change, in its
|
|
15
|
+
* own words. The public changelog (`publicChangelog`) leaves `internal` changes
|
|
16
|
+
* out — operations, infrastructure, limits and security mechanics a visitor has
|
|
17
|
+
* no use for and an attacker would — and says only that work happened behind
|
|
18
|
+
* the scenes. Omitted means `public`.
|
|
19
|
+
*/
|
|
20
|
+
export type ChangeAudience = "public" | "internal";
|
|
13
21
|
/** One change within a release section. */
|
|
14
22
|
export interface ChangelogChange {
|
|
15
23
|
type: ChangeType;
|
|
@@ -17,11 +25,24 @@ export interface ChangelogChange {
|
|
|
17
25
|
title: string;
|
|
18
26
|
/** One to three sentences: what a person can now do, and where. May use `**bold**` for UI labels. */
|
|
19
27
|
text: string;
|
|
28
|
+
/** `internal` keeps the change to the team's changelog. */
|
|
29
|
+
audience?: ChangeAudience;
|
|
30
|
+
/** The public changelog's wording, where `title` or `text` say more than the public should read. */
|
|
31
|
+
public?: {
|
|
32
|
+
title?: string;
|
|
33
|
+
text?: string;
|
|
34
|
+
};
|
|
20
35
|
}
|
|
21
36
|
/** Changes grouped under a product area, as people name it ("Invoices", "Fixes"). */
|
|
22
37
|
export interface ChangelogSection {
|
|
23
38
|
heading: string;
|
|
24
39
|
changes: ChangelogChange[];
|
|
40
|
+
/** `internal` keeps the whole section to the team's changelog. */
|
|
41
|
+
audience?: ChangeAudience;
|
|
42
|
+
/** The public changelog's heading, where the team's names something the public shouldn't see. */
|
|
43
|
+
public?: {
|
|
44
|
+
heading?: string;
|
|
45
|
+
};
|
|
25
46
|
}
|
|
26
47
|
/** A numbered, dated release. */
|
|
27
48
|
export interface ChangelogRelease {
|
|
@@ -35,6 +56,24 @@ export interface ChangelogRelease {
|
|
|
35
56
|
summary: string;
|
|
36
57
|
/** Most important first; "Fixes" and "Behind the scenes" last. */
|
|
37
58
|
sections: ChangelogSection[];
|
|
59
|
+
/**
|
|
60
|
+
* The release's public review. A release reaches the public changelog only
|
|
61
|
+
* once it has one: present — even `{}` — means someone has read the release
|
|
62
|
+
* for the public, marked what is internal and reworded what says too much.
|
|
63
|
+
* `title` and `summary` replace the team's where those do.
|
|
64
|
+
*/
|
|
65
|
+
public?: {
|
|
66
|
+
title?: string;
|
|
67
|
+
summary?: string;
|
|
68
|
+
};
|
|
69
|
+
/**
|
|
70
|
+
* The version released just before this one in the whole history. Only
|
|
71
|
+
* `publicChangelog` sets it: the public list is cut to the newest few and skips
|
|
72
|
+
* unreviewed releases, so the release under each one in that list isn't
|
|
73
|
+
* necessarily the one it followed. `<Changelog>` uses it to label the release
|
|
74
|
+
* major, minor or patch; never write it by hand.
|
|
75
|
+
*/
|
|
76
|
+
previousVersion?: string;
|
|
38
77
|
}
|
|
39
78
|
/** What a release guard compares the changelog against. Parsed JSON, so this stays free of `node:fs`. */
|
|
40
79
|
export interface ReleaseManifest {
|
|
@@ -69,4 +108,49 @@ export declare function releaseAnchor(version: string): string;
|
|
|
69
108
|
/** Format an ISO date as "29 August 2026" (or the locale's equivalent), in UTC so it never shifts a day. */
|
|
70
109
|
export declare function formatReleaseDate(iso: string, locale?: string): string;
|
|
71
110
|
export declare function countChanges(release: ChangelogRelease): number;
|
|
111
|
+
/** Whether a change stays out of the public changelog, by its own audience or its section's. */
|
|
112
|
+
export declare function isInternalChange(change: ChangelogChange, section?: ChangelogSection): boolean;
|
|
113
|
+
/** What the public changelog says in place of the changes it leaves out — one line a release. */
|
|
114
|
+
export interface BehindTheScenesNote {
|
|
115
|
+
heading: string;
|
|
116
|
+
title: string;
|
|
117
|
+
text: string;
|
|
118
|
+
}
|
|
119
|
+
export declare const DEFAULT_BEHIND_THE_SCENES: BehindTheScenesNote;
|
|
120
|
+
export interface PublicChangelogOptions {
|
|
121
|
+
/** How many of the newest reviewed releases to show. Defaults to 5. */
|
|
122
|
+
limit?: number;
|
|
123
|
+
/** The line that stands in for internal changes. */
|
|
124
|
+
behindTheScenes?: Partial<BehindTheScenesNote>;
|
|
125
|
+
}
|
|
126
|
+
/** How many releases a public changelog shows unless told otherwise. */
|
|
127
|
+
export declare const PUBLIC_RELEASE_LIMIT = 5;
|
|
128
|
+
/**
|
|
129
|
+
* One release as the public may read it, or `null` when it hasn't had its
|
|
130
|
+
* public review. Internal sections and changes are dropped; the reworded
|
|
131
|
+
* headline, summary, headings and changes replace the team's; and when
|
|
132
|
+
* anything was dropped, one generic line under "Behind the scenes" says so.
|
|
133
|
+
* The result is a fresh object with no audience or public fields, so the
|
|
134
|
+
* team's wording of a redacted change cannot reach the page by accident.
|
|
135
|
+
*/
|
|
136
|
+
export declare function publicRelease(release: ChangelogRelease, behindTheScenes?: Partial<BehindTheScenesNote>): ChangelogRelease | null;
|
|
137
|
+
/**
|
|
138
|
+
* The public changelog: the newest reviewed releases (5 by default), each as
|
|
139
|
+
* `publicRelease` draws it. A release not yet reviewed is left out rather than
|
|
140
|
+
* shown in the team's words, so forgetting the review hides a release instead
|
|
141
|
+
* of leaking one; `publicChangelogProblems` is the test that catches the
|
|
142
|
+
* forgetting.
|
|
143
|
+
*/
|
|
144
|
+
export declare function publicChangelog(releases: readonly ChangelogRelease[], { limit, behindTheScenes }?: PublicChangelogOptions): ChangelogRelease[];
|
|
145
|
+
/**
|
|
146
|
+
* Everything wrong with the public side of a release history, as sentences.
|
|
147
|
+
* The newest `reviewed` releases (5 by default — the public window) must each
|
|
148
|
+
* have their public review; a reworded field can't be blank; and a release
|
|
149
|
+
* whose every change is internal must give its own public headline and
|
|
150
|
+
* summary, because its team ones are about the work being hidden. An app's
|
|
151
|
+
* release guard asserts this is empty beside `releaseHistoryProblems`.
|
|
152
|
+
*/
|
|
153
|
+
export declare function publicChangelogProblems(changelog: readonly ChangelogRelease[], { reviewed }?: {
|
|
154
|
+
reviewed?: number;
|
|
155
|
+
}): string[];
|
|
72
156
|
//# sourceMappingURL=changelog.d.ts.map
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"changelog.d.ts","sourceRoot":"","sources":["../../src/release/changelog.ts"],"names":[],"mappings":"AAEA;;;;;;;;GAQG;AAEH,6FAA6F;AAC7F,MAAM,MAAM,UAAU,GAAG,KAAK,GAAG,UAAU,GAAG,SAAS,GAAG,OAAO,CAAC;AAElE,eAAO,MAAM,YAAY,EAAE,SAAS,UAAU,EAA4C,CAAC;AAE3F,2CAA2C;AAC3C,MAAM,WAAW,eAAe;IAC9B,IAAI,EAAE,UAAU,CAAC;IACjB,uGAAuG;IACvG,KAAK,EAAE,MAAM,CAAC;IACd,qGAAqG;IACrG,IAAI,EAAE,MAAM,CAAC;
|
|
1
|
+
{"version":3,"file":"changelog.d.ts","sourceRoot":"","sources":["../../src/release/changelog.ts"],"names":[],"mappings":"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"}
|
|
@@ -89,4 +89,121 @@ export function formatReleaseDate(iso, locale = "en-GB") {
|
|
|
89
89
|
export function countChanges(release) {
|
|
90
90
|
return release.sections.reduce((n, section) => n + section.changes.length, 0);
|
|
91
91
|
}
|
|
92
|
+
/** Whether a change stays out of the public changelog, by its own audience or its section's. */
|
|
93
|
+
export function isInternalChange(change, section) {
|
|
94
|
+
return change.audience === "internal" || section?.audience === "internal";
|
|
95
|
+
}
|
|
96
|
+
export const DEFAULT_BEHIND_THE_SCENES = {
|
|
97
|
+
heading: "Behind the scenes",
|
|
98
|
+
title: "Reliability and security",
|
|
99
|
+
text: "Work behind the scenes to keep everything running smoothly and securely.",
|
|
100
|
+
};
|
|
101
|
+
/** How many releases a public changelog shows unless told otherwise. */
|
|
102
|
+
export const PUBLIC_RELEASE_LIMIT = 5;
|
|
103
|
+
/**
|
|
104
|
+
* One release as the public may read it, or `null` when it hasn't had its
|
|
105
|
+
* public review. Internal sections and changes are dropped; the reworded
|
|
106
|
+
* headline, summary, headings and changes replace the team's; and when
|
|
107
|
+
* anything was dropped, one generic line under "Behind the scenes" says so.
|
|
108
|
+
* The result is a fresh object with no audience or public fields, so the
|
|
109
|
+
* team's wording of a redacted change cannot reach the page by accident.
|
|
110
|
+
*/
|
|
111
|
+
export function publicRelease(release, behindTheScenes = {}) {
|
|
112
|
+
if (!release.public)
|
|
113
|
+
return null;
|
|
114
|
+
const note = { ...DEFAULT_BEHIND_THE_SCENES, ...behindTheScenes };
|
|
115
|
+
const byHeading = new Map();
|
|
116
|
+
let redacted = false;
|
|
117
|
+
for (const section of release.sections) {
|
|
118
|
+
const changes = [];
|
|
119
|
+
for (const change of section.changes) {
|
|
120
|
+
if (isInternalChange(change, section)) {
|
|
121
|
+
redacted = true;
|
|
122
|
+
continue;
|
|
123
|
+
}
|
|
124
|
+
changes.push({
|
|
125
|
+
type: change.type,
|
|
126
|
+
title: change.public?.title ?? change.title,
|
|
127
|
+
text: change.public?.text ?? change.text,
|
|
128
|
+
});
|
|
129
|
+
}
|
|
130
|
+
if (changes.length === 0)
|
|
131
|
+
continue;
|
|
132
|
+
const heading = section.public?.heading ?? section.heading;
|
|
133
|
+
byHeading.set(heading, [...(byHeading.get(heading) ?? []), ...changes]);
|
|
134
|
+
}
|
|
135
|
+
if (redacted) {
|
|
136
|
+
const existing = byHeading.get(note.heading) ?? [];
|
|
137
|
+
byHeading.delete(note.heading);
|
|
138
|
+
const generic = { type: "improved", title: note.title, text: note.text };
|
|
139
|
+
byHeading.set(note.heading, existing.some((c) => c.title === note.title) ? existing : [...existing, generic]);
|
|
140
|
+
}
|
|
141
|
+
return {
|
|
142
|
+
version: release.version,
|
|
143
|
+
date: release.date,
|
|
144
|
+
title: release.public.title ?? release.title,
|
|
145
|
+
summary: release.public.summary ?? release.summary,
|
|
146
|
+
sections: [...byHeading].map(([heading, changes]) => ({ heading, changes })),
|
|
147
|
+
};
|
|
148
|
+
}
|
|
149
|
+
/**
|
|
150
|
+
* The public changelog: the newest reviewed releases (5 by default), each as
|
|
151
|
+
* `publicRelease` draws it. A release not yet reviewed is left out rather than
|
|
152
|
+
* shown in the team's words, so forgetting the review hides a release instead
|
|
153
|
+
* of leaking one; `publicChangelogProblems` is the test that catches the
|
|
154
|
+
* forgetting.
|
|
155
|
+
*/
|
|
156
|
+
export function publicChangelog(releases, { limit = PUBLIC_RELEASE_LIMIT, behindTheScenes } = {}) {
|
|
157
|
+
const shown = [];
|
|
158
|
+
for (const [i, release] of releases.entries()) {
|
|
159
|
+
if (shown.length >= limit)
|
|
160
|
+
break;
|
|
161
|
+
const visible = publicRelease(release, behindTheScenes);
|
|
162
|
+
if (!visible)
|
|
163
|
+
continue;
|
|
164
|
+
// Carry the true predecessor, so the oldest release shown still gets its kind.
|
|
165
|
+
const previous = releases[i + 1];
|
|
166
|
+
shown.push(previous ? { ...visible, previousVersion: previous.version } : visible);
|
|
167
|
+
}
|
|
168
|
+
return shown;
|
|
169
|
+
}
|
|
170
|
+
/**
|
|
171
|
+
* Everything wrong with the public side of a release history, as sentences.
|
|
172
|
+
* The newest `reviewed` releases (5 by default — the public window) must each
|
|
173
|
+
* have their public review; a reworded field can't be blank; and a release
|
|
174
|
+
* whose every change is internal must give its own public headline and
|
|
175
|
+
* summary, because its team ones are about the work being hidden. An app's
|
|
176
|
+
* release guard asserts this is empty beside `releaseHistoryProblems`.
|
|
177
|
+
*/
|
|
178
|
+
export function publicChangelogProblems(changelog, { reviewed = PUBLIC_RELEASE_LIMIT } = {}) {
|
|
179
|
+
const problems = [];
|
|
180
|
+
const blank = (value) => value !== undefined && !value.trim();
|
|
181
|
+
changelog.forEach((release, i) => {
|
|
182
|
+
const label = release.version || `release #${i + 1}`;
|
|
183
|
+
if (!release.public) {
|
|
184
|
+
if (i < reviewed)
|
|
185
|
+
problems.push(`${label} has not been reviewed for the public changelog`);
|
|
186
|
+
return;
|
|
187
|
+
}
|
|
188
|
+
if (blank(release.public.title))
|
|
189
|
+
problems.push(`${label} has a blank public title`);
|
|
190
|
+
if (blank(release.public.summary))
|
|
191
|
+
problems.push(`${label} has a blank public summary`);
|
|
192
|
+
const changes = release.sections.flatMap((section) => section.changes.map((change) => ({ section, change })));
|
|
193
|
+
const allInternal = changes.length > 0 && changes.every(({ section, change }) => isInternalChange(change, section));
|
|
194
|
+
if (allInternal && (!release.public.title || !release.public.summary)) {
|
|
195
|
+
problems.push(`${label} is all internal, so it needs its own public title and summary`);
|
|
196
|
+
}
|
|
197
|
+
for (const section of release.sections) {
|
|
198
|
+
if (blank(section.public?.heading))
|
|
199
|
+
problems.push(`${label} · ${section.heading} has a blank public heading`);
|
|
200
|
+
for (const change of section.changes) {
|
|
201
|
+
if (blank(change.public?.title) || blank(change.public?.text)) {
|
|
202
|
+
problems.push(`${label} · ${section.heading} · "${change.title}" has blank public wording`);
|
|
203
|
+
}
|
|
204
|
+
}
|
|
205
|
+
}
|
|
206
|
+
});
|
|
207
|
+
return problems;
|
|
208
|
+
}
|
|
92
209
|
//# sourceMappingURL=changelog.js.map
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"changelog.js","sourceRoot":"","sources":["../../src/release/changelog.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,gBAAgB,EAAE,WAAW,EAAE,MAAM,cAAc,CAAC;AAe7D,MAAM,CAAC,MAAM,YAAY,GAA0B,CAAC,KAAK,EAAE,UAAU,EAAE,SAAS,EAAE,OAAO,CAAC,CAAC;
|
|
1
|
+
{"version":3,"file":"changelog.js","sourceRoot":"","sources":["../../src/release/changelog.ts"],"names":[],"mappings":"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"}
|
package/dist/release/index.d.ts
CHANGED
|
@@ -1,3 +1,3 @@
|
|
|
1
1
|
export { compareVersions, isReleaseVersion, nextVersion, parseVersion, releaseKind, type ReleaseKind, type Version, } from "./version.js";
|
|
2
|
-
export { CHANGE_TYPES, countChanges, formatReleaseDate, releaseAnchor, releaseHistoryProblems, type ChangeType, type ChangelogChange, type ChangelogRelease, type ChangelogSection, type ReleaseManifest, } from "./changelog.js";
|
|
2
|
+
export { CHANGE_TYPES, countChanges, DEFAULT_BEHIND_THE_SCENES, formatReleaseDate, isInternalChange, PUBLIC_RELEASE_LIMIT, publicChangelog, publicChangelogProblems, publicRelease, releaseAnchor, releaseHistoryProblems, type BehindTheScenesNote, type ChangeAudience, type ChangeType, type ChangelogChange, type ChangelogRelease, type ChangelogSection, type PublicChangelogOptions, type ReleaseManifest, } from "./changelog.js";
|
|
3
3
|
//# sourceMappingURL=index.d.ts.map
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../../src/release/index.ts"],"names":[],"mappings":"AAAA,OAAO,EACL,eAAe,EACf,gBAAgB,EAChB,WAAW,EACX,YAAY,EACZ,WAAW,EACX,KAAK,WAAW,EAChB,KAAK,OAAO,GACb,MAAM,cAAc,CAAC;AACtB,OAAO,EACL,YAAY,EACZ,YAAY,EACZ,iBAAiB,EACjB,aAAa,EACb,sBAAsB,EACtB,KAAK,UAAU,EACf,KAAK,eAAe,EACpB,KAAK,gBAAgB,EACrB,KAAK,gBAAgB,EACrB,KAAK,eAAe,GACrB,MAAM,gBAAgB,CAAC"}
|
|
1
|
+
{"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../../src/release/index.ts"],"names":[],"mappings":"AAAA,OAAO,EACL,eAAe,EACf,gBAAgB,EAChB,WAAW,EACX,YAAY,EACZ,WAAW,EACX,KAAK,WAAW,EAChB,KAAK,OAAO,GACb,MAAM,cAAc,CAAC;AACtB,OAAO,EACL,YAAY,EACZ,YAAY,EACZ,yBAAyB,EACzB,iBAAiB,EACjB,gBAAgB,EAChB,oBAAoB,EACpB,eAAe,EACf,uBAAuB,EACvB,aAAa,EACb,aAAa,EACb,sBAAsB,EACtB,KAAK,mBAAmB,EACxB,KAAK,cAAc,EACnB,KAAK,UAAU,EACf,KAAK,eAAe,EACpB,KAAK,gBAAgB,EACrB,KAAK,gBAAgB,EACrB,KAAK,sBAAsB,EAC3B,KAAK,eAAe,GACrB,MAAM,gBAAgB,CAAC"}
|
package/dist/release/index.js
CHANGED
|
@@ -1,3 +1,3 @@
|
|
|
1
1
|
export { compareVersions, isReleaseVersion, nextVersion, parseVersion, releaseKind, } from "./version.js";
|
|
2
|
-
export { CHANGE_TYPES, countChanges, formatReleaseDate, releaseAnchor, releaseHistoryProblems, } from "./changelog.js";
|
|
2
|
+
export { CHANGE_TYPES, countChanges, DEFAULT_BEHIND_THE_SCENES, formatReleaseDate, isInternalChange, PUBLIC_RELEASE_LIMIT, publicChangelog, publicChangelogProblems, publicRelease, releaseAnchor, releaseHistoryProblems, } from "./changelog.js";
|
|
3
3
|
//# sourceMappingURL=index.js.map
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"index.js","sourceRoot":"","sources":["../../src/release/index.ts"],"names":[],"mappings":"AAAA,OAAO,EACL,eAAe,EACf,gBAAgB,EAChB,WAAW,EACX,YAAY,EACZ,WAAW,GAGZ,MAAM,cAAc,CAAC;AACtB,OAAO,EACL,YAAY,EACZ,YAAY,EACZ,iBAAiB,EACjB,aAAa,EACb,sBAAsB,
|
|
1
|
+
{"version":3,"file":"index.js","sourceRoot":"","sources":["../../src/release/index.ts"],"names":[],"mappings":"AAAA,OAAO,EACL,eAAe,EACf,gBAAgB,EAChB,WAAW,EACX,YAAY,EACZ,WAAW,GAGZ,MAAM,cAAc,CAAC;AACtB,OAAO,EACL,YAAY,EACZ,YAAY,EACZ,yBAAyB,EACzB,iBAAiB,EACjB,gBAAgB,EAChB,oBAAoB,EACpB,eAAe,EACf,uBAAuB,EACvB,aAAa,EACb,aAAa,EACb,sBAAsB,GASvB,MAAM,gBAAgB,CAAC"}
|
package/docs/ReleaseProcess.md
CHANGED
|
@@ -148,6 +148,22 @@ Writing rules:
|
|
|
148
148
|
- **Behind-the-scenes releases get one short line.** A pruning round is "Behind the scenes · Tidier documentation", not an essay.
|
|
149
149
|
- **Never renumber a released version.** Edit an old release only to correct a factual error.
|
|
150
150
|
|
|
151
|
+
### The public review
|
|
152
|
+
|
|
153
|
+
The changelog has two readers. The **team's changelog** — every change in full, for the people who run the app — is `CHANGELOG` as written. The **public changelog** is `publicChangelog(CHANGELOG)` from `@connextar/house/release`: the newest five reviewed releases, redacted. Every release gets its public review before it is cut:
|
|
154
|
+
|
|
155
|
+
| Field | Use it for |
|
|
156
|
+
| --------------------------------------- | -------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- |
|
|
157
|
+
| `public` on the release | **Required.** Marks the release reviewed — `public: {}` when its headline and summary are fine for anyone; `public: { title, summary }` when they name internal work. Unreviewed = not shown |
|
|
158
|
+
| `audience: "internal"` on a change | Operations and infrastructure, deploys and dependencies, security mechanics and limits (counts, thresholds, caps, token rules), admin-only tooling, documentation and process work |
|
|
159
|
+
| `audience: "internal"` on a section | A whole section the public has no use for |
|
|
160
|
+
| `public: { title?, text? }` on a change | A change the public should hear about, in plainer or vaguer words — what it means for them, not how it works |
|
|
161
|
+
| `public: { heading }` on a section | A heading that itself names something internal |
|
|
162
|
+
|
|
163
|
+
The public page adds one line — **Behind the scenes · Reliability and security** — to any release it removed something from, so a release never looks emptier than it was. A release whose every change is internal needs its own `public.title` and `public.summary`. Write the public wording for a customer, not an engineer: no numbers that would help someone game a limit, no names of providers or systems, no description of how a protection works — say what it does for them ("someone guessing at a patient's details is stopped sooner").
|
|
164
|
+
|
|
165
|
+
The release guard checks it: `publicChangelogProblems(CHANGELOG)` must be empty, and it fails until the newest five releases each have their review.
|
|
166
|
+
|
|
151
167
|
---
|
|
152
168
|
|
|
153
169
|
## 4. The pieces in code
|
|
@@ -179,7 +195,7 @@ export const CHANGELOG: ChangelogRelease[] = [
|
|
|
179
195
|
**The guard test** — `lib/release.test.ts`:
|
|
180
196
|
|
|
181
197
|
```ts
|
|
182
|
-
import { releaseHistoryProblems } from "@connextar/house/release";
|
|
198
|
+
import { publicChangelogProblems, releaseHistoryProblems } from "@connextar/house/release";
|
|
183
199
|
import { describe, expect, it } from "vitest";
|
|
184
200
|
|
|
185
201
|
import lock from "../package-lock.json";
|
|
@@ -190,20 +206,26 @@ describe("release history", () => {
|
|
|
190
206
|
it("agrees with package.json, moves one step at a time, and is dated in order", () => {
|
|
191
207
|
expect(releaseHistoryProblems(CHANGELOG, { pkg, lock })).toEqual([]);
|
|
192
208
|
});
|
|
209
|
+
|
|
210
|
+
it("has had its public review for every release the public page can show", () => {
|
|
211
|
+
expect(publicChangelogProblems(CHANGELOG)).toEqual([]);
|
|
212
|
+
});
|
|
193
213
|
});
|
|
194
214
|
```
|
|
195
215
|
|
|
196
216
|
Each problem is a sentence that says what to fix ("package.json declares 0.17.0 but the newest release is 0.16.2").
|
|
197
217
|
|
|
198
|
-
**The page** — the app's `/changelog` route draws its own hero and calls to action around the shared body:
|
|
218
|
+
**The page** — the app's `/changelog` route draws its own hero and calls to action around the shared body. One URL serves both readers; the app decides who may see the team's changelog:
|
|
199
219
|
|
|
200
220
|
```tsx
|
|
201
221
|
import { Changelog } from "@connextar/house/changelog";
|
|
222
|
+
import { publicChangelog } from "@connextar/house/release";
|
|
202
223
|
|
|
203
224
|
import { CHANGELOG } from "@lib/changelog";
|
|
204
225
|
|
|
205
|
-
export default function ChangelogPage() {
|
|
206
|
-
|
|
226
|
+
export default async function ChangelogPage() {
|
|
227
|
+
const team = await canReadTeamChangelog(); // the app's own check, e.g. platform admins
|
|
228
|
+
return team ? <Changelog releases={CHANGELOG} showAudience /> : <Changelog releases={publicChangelog(CHANGELOG)} />;
|
|
207
229
|
}
|
|
208
230
|
```
|
|
209
231
|
|
|
@@ -296,6 +318,7 @@ Copy into the work-log entry and tick off:
|
|
|
296
318
|
- [ ] `npm version <x.y.z> --no-git-tag-version` run
|
|
297
319
|
- [ ] Release added to the top of `CHANGELOG` — title, summary, sections, typed changes
|
|
298
320
|
- [ ] No internal names; nothing dated leaked into marketing copy
|
|
321
|
+
- [ ] Public review done (§3): `public` on the release, internal changes marked, public wording where the team's says too much
|
|
299
322
|
- [ ] Status document _Last updated_ line and work-log entry name the version
|
|
300
323
|
- [ ] Release guard test, `npm run check`, `npm test`, `npm run build` exit 0
|
|
301
324
|
- [ ] Committed with `Release: vX.Y.Z` and tagged — or the owed tag noted in the final report
|