create-ailk 0.28.0 → 0.29.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/README.md +124 -1
- package/dist/cli.js +92 -2
- package/dist/lib/chrome-readers.d.ts +21 -5
- package/dist/lib/chrome-readers.js +128 -20
- package/dist/lib/declared-env.d.ts +6 -0
- package/dist/lib/declared-env.js +2 -0
- package/dist/lib/import-graph.d.ts +16 -0
- package/dist/lib/import-graph.js +53 -0
- package/dist/lib/import-mdx.d.ts +59 -0
- package/dist/lib/import-mdx.js +452 -0
- package/dist/migrate-content.d.ts +45 -0
- package/dist/migrate-content.js +215 -0
- package/dist/seed-workspace.d.ts +71 -0
- package/dist/seed-workspace.js +242 -0
- package/dist/template-record.d.ts +13 -4
- package/dist/template-record.js +8 -4
- package/dist/update.d.ts +32 -2
- package/dist/update.js +357 -44
- package/package.json +3 -3
- package/templates/apps/api/CLAUDE.md +1 -1
- package/templates/apps/mcp/CLAUDE.md +16 -12
- package/templates/apps/web/CLAUDE.md +11 -1
- package/templates/apps/web/app/[locale]/[slug]/page.tsx +4 -0
- package/templates/apps/web/app/[locale]/alternatives/page.tsx +9 -1
- package/templates/apps/web/app/[locale]/careers/page.tsx +23 -15
- package/templates/apps/web/app/[locale]/clients/page.tsx +13 -1
- package/templates/apps/web/app/[locale]/faq/page.tsx +9 -0
- package/templates/apps/web/app/[locale]/glossary/page.tsx +3 -0
- package/templates/apps/web/app/[locale]/services/__tests__/service-detail-sections.test.tsx +1 -0
- package/templates/apps/web/app/[locale]/templates/__tests__/page.test.tsx +1 -0
- package/templates/apps/web/app/[locale]/templates/page.tsx +6 -0
- package/templates/apps/web/app/[locale]/testimonials/page.tsx +13 -1
- package/templates/apps/web/lib/__tests__/hub-children.fixtures.ts +1 -0
- package/templates/apps/web/lib/hub-children.tsx +38 -9
- package/templates/apps/web/messages/en.json +22 -2
- package/templates/tools/declared-env.ts +7 -0
- package/versions.json +48 -48
|
@@ -594,6 +594,14 @@ A site serves its services at `/services` unless `content/_site.mdx` sets `servi
|
|
|
594
594
|
- **Surfaces.** The canonical, hreflang and `BreadcrumbList` (the base's title-cased last segment, then the category's title), `/sitemap.xml` and `/llms.txt` (`pagePathFor`), every nav and footer entry at `/services` (mapped in the locale layout and `components/signed-in-nav.tsx`), the hub's own section links, and route-family chrome (`lib/page-chrome.ts`) all follow it. A site that sets neither `services.path` nor any `parent` serves every URL exactly as before.
|
|
595
595
|
- **Moving it.** `/ailk:growth-ops page rename` moves the base (`--from <current base> --to <new base>`): it writes `services.path` and keeps a previous moved base redirecting.
|
|
596
596
|
|
|
597
|
+
## Nav-only lists (`navOnly`)
|
|
598
|
+
|
|
599
|
+
A list page whose frontmatter sets `navOnly: true` (valid on a registry list type only, #10200) has no page of its own: `honourNavOnly` (`lib/hub-children.tsx`) answers `permanentRedirect` (308) to the first child in the route's own child order (a hub's is slug order after `includeChild`, not the order of links its authored sections carry), at the card's public path with the locale prefix kept, and `notFound()` when it lists none. Only an exact `true` acts; anything else renders as before.
|
|
600
|
+
|
|
601
|
+
- **Where.** The thirteen hubs inside `hubPageInput` (slug order after `includeChild`; `/services` at `servicePublicPath`), `/alternatives` (`/vs/<slug>`, slug-read order), `/templates`, `/careers` (newest first) and `/reports` (newest first) with one call each, and the childless `/faq`, `/glossary`, `/clients`, `/testimonials` and the `[slug]` `BlogList` teaser, which 404.
|
|
602
|
+
- **Not honoured.** The docs-blog module's Fumadocs `/blog` and `/docs`; `DownloadsList` (`/downloads` already redirects to `/reports`); `LocationsList` (no route). `/sitemap.xml` and `/llms.txt` still list a Nav-only list's URL.
|
|
603
|
+
- **The nav is untouched.** `lib/nav-config.ts` is generated from route surfaces, not from list pages, so the heading and its children stay in the menu.
|
|
604
|
+
|
|
597
605
|
## AEO & performance
|
|
598
606
|
|
|
599
607
|
- Static rendering is the default. Don't introduce client rendering without a reason.
|
|
@@ -610,7 +618,9 @@ A site serves its services at `/services` unless `content/_site.mdx` sets `servi
|
|
|
610
618
|
- Don't reach into `apps/api` or `apps/mcp` from `apps/web`. They're peer apps — talk via HTTP/MCP.
|
|
611
619
|
- Don't commit images to `screenshots/` — only the `.gitkeep` is tracked.
|
|
612
620
|
|
|
613
|
-
Last updated: 2026-10-
|
|
621
|
+
Last updated: 2026-10-28 — § Nav-only lists (`navOnly`): a Nav-only list's route answers 308 to its first child and 404 with none (#10200) — verified against `lib/hub-children.tsx` and the nine list routes it is called from beside the hubs.
|
|
622
|
+
|
|
623
|
+
Prior: 2026-10-27 — § Where the services live (`services.path`): the services base, one category level, the 308 from `/services` and the `update-page` base move (#10043) — verified against `lib/services-path.ts`, `lib/service-listing.ts`, `middleware.ts`, `next.config.mjs` and `app/[locale]/services/`.
|
|
614
624
|
|
|
615
625
|
Prior: 2026-10-26 — § The same-origin relay: the bot check moves to `lib/bot-check.ts` and covers the relay's three lead routes and the seven public lead, flow and checkout Server Actions, with `BotIdClient` protecting every same-origin POST and the `AILK_BOT_CHECK` switch (#9255) — verified against `lib/bot-check.ts`, `lib/relay.ts`, `app/[locale]/layout.tsx` and the seven actions' files.
|
|
616
626
|
|
|
@@ -42,6 +42,8 @@ import {
|
|
|
42
42
|
import type { Metadata } from "next";
|
|
43
43
|
import type { ReactElement } from "react";
|
|
44
44
|
|
|
45
|
+
import { honourNavOnly } from "../../../lib/hub-children";
|
|
46
|
+
|
|
45
47
|
// The content type is written as a literal in both renderPage calls below
|
|
46
48
|
// (not a shared constant): `lib/__tests__/structured-data.test.tsx` and
|
|
47
49
|
// tools/lint-check-content-renders.ts read `renderPage({ type: "…" })` off
|
|
@@ -87,6 +89,8 @@ export default async function Page({
|
|
|
87
89
|
const load = getMdxSource ?? loadMdxSource;
|
|
88
90
|
const type = await resolveType(slug, locale, load);
|
|
89
91
|
if (type === "BlogList") {
|
|
92
|
+
const source = await load("BlogList", slug, locale);
|
|
93
|
+
honourNavOnly(source?.frontmatter, locale, []);
|
|
90
94
|
return renderPage({
|
|
91
95
|
type: "BlogList",
|
|
92
96
|
slug,
|
|
@@ -27,6 +27,8 @@ import type { Metadata } from "next";
|
|
|
27
27
|
import { notFound } from "next/navigation";
|
|
28
28
|
import type { ReactElement } from "react";
|
|
29
29
|
|
|
30
|
+
import { honourNavOnly } from "../../../lib/hub-children";
|
|
31
|
+
|
|
30
32
|
interface Props {
|
|
31
33
|
params: Promise<{ locale: string }>;
|
|
32
34
|
getMdxSource?: typeof defaultGetMdxSource;
|
|
@@ -60,6 +62,13 @@ export default async function Page({
|
|
|
60
62
|
notFound();
|
|
61
63
|
}
|
|
62
64
|
|
|
65
|
+
const slugs = await getMdxSlugs("ComparisonPage", locale);
|
|
66
|
+
honourNavOnly(
|
|
67
|
+
source.frontmatter,
|
|
68
|
+
locale,
|
|
69
|
+
slugs.map((slug) => `/vs/${slug}`),
|
|
70
|
+
);
|
|
71
|
+
|
|
63
72
|
/**
|
|
64
73
|
* Append one generated `items` section listing every ComparisonPage spoke
|
|
65
74
|
* — D9's "the visible list and the CollectionPage's ItemList always
|
|
@@ -69,7 +78,6 @@ export default async function Page({
|
|
|
69
78
|
*/
|
|
70
79
|
async function withSpokes(frontmatter: unknown): Promise<Section[]> {
|
|
71
80
|
const declared = await loadSections(frontmatter);
|
|
72
|
-
const slugs = await getMdxSlugs("ComparisonPage", locale);
|
|
73
81
|
if (slugs.length === 0) return declared;
|
|
74
82
|
|
|
75
83
|
const items = await Promise.all(
|
|
@@ -25,6 +25,8 @@ import type { Metadata } from "next";
|
|
|
25
25
|
import { getTranslations } from "next-intl/server";
|
|
26
26
|
import type { ReactElement } from "react";
|
|
27
27
|
|
|
28
|
+
import { honourNavOnly } from "../../../lib/hub-children";
|
|
29
|
+
|
|
28
30
|
interface Props {
|
|
29
31
|
params: Promise<{ locale: string }>;
|
|
30
32
|
getMdxSource?: typeof defaultGetMdxSource;
|
|
@@ -75,6 +77,26 @@ export default async function Page({
|
|
|
75
77
|
const loadSections = getPageSections ?? loadPageSections;
|
|
76
78
|
const t = await getTranslations({ locale, namespace: "careers" });
|
|
77
79
|
|
|
80
|
+
const slugs = await listSlugs("JobPosting", locale);
|
|
81
|
+
const postings = await Promise.all(
|
|
82
|
+
slugs.map(async (slug) => {
|
|
83
|
+
const source = await load("JobPosting", slug, locale);
|
|
84
|
+
return { slug, fm: (source?.frontmatter ?? {}) as PostingFields };
|
|
85
|
+
}),
|
|
86
|
+
);
|
|
87
|
+
// Newest `publishedAt` first, then by slug; no date sorts last.
|
|
88
|
+
postings.sort(
|
|
89
|
+
(a, b) =>
|
|
90
|
+
(b.fm.publishedAt ?? "").localeCompare(a.fm.publishedAt ?? "") ||
|
|
91
|
+
a.slug.localeCompare(b.slug),
|
|
92
|
+
);
|
|
93
|
+
const source = await load("CareersList", "careers", locale);
|
|
94
|
+
honourNavOnly(
|
|
95
|
+
source?.frontmatter,
|
|
96
|
+
locale,
|
|
97
|
+
postings.map(({ slug }) => `/careers/${slug}`),
|
|
98
|
+
);
|
|
99
|
+
|
|
78
100
|
/**
|
|
79
101
|
* Put one generated `items` section first — the postings are the page's
|
|
80
102
|
* main content — followed by the declared sections. Absent when there are
|
|
@@ -82,21 +104,7 @@ export default async function Page({
|
|
|
82
104
|
*/
|
|
83
105
|
async function withPostings(frontmatter: unknown): Promise<Section[]> {
|
|
84
106
|
const declared = await loadSections(frontmatter);
|
|
85
|
-
|
|
86
|
-
if (slugs.length === 0) return declared;
|
|
87
|
-
|
|
88
|
-
const postings = await Promise.all(
|
|
89
|
-
slugs.map(async (slug) => {
|
|
90
|
-
const source = await load("JobPosting", slug, locale);
|
|
91
|
-
return { slug, fm: (source?.frontmatter ?? {}) as PostingFields };
|
|
92
|
-
}),
|
|
93
|
-
);
|
|
94
|
-
// Newest `publishedAt` first, then by slug; no date sorts last.
|
|
95
|
-
postings.sort(
|
|
96
|
-
(a, b) =>
|
|
97
|
-
(b.fm.publishedAt ?? "").localeCompare(a.fm.publishedAt ?? "") ||
|
|
98
|
-
a.slug.localeCompare(b.slug),
|
|
99
|
-
);
|
|
107
|
+
if (postings.length === 0) return declared;
|
|
100
108
|
|
|
101
109
|
const items = postings.map(({ slug, fm }) => {
|
|
102
110
|
const type =
|
|
@@ -3,10 +3,16 @@
|
|
|
3
3
|
import type {
|
|
4
4
|
getMdxSource as defaultGetMdxSource,
|
|
5
5
|
} from "@working-theory/page-renderer";
|
|
6
|
-
import {
|
|
6
|
+
import {
|
|
7
|
+
getMdxSource as loadMdxSource,
|
|
8
|
+
renderPage,
|
|
9
|
+
renderPageMetadata,
|
|
10
|
+
} from "@working-theory/page-renderer";
|
|
7
11
|
import type { Metadata } from "next";
|
|
8
12
|
import type { ReactElement } from "react";
|
|
9
13
|
|
|
14
|
+
import { honourNavOnly } from "../../../lib/hub-children";
|
|
15
|
+
|
|
10
16
|
interface Props {
|
|
11
17
|
params: Promise<{ locale: string }>;
|
|
12
18
|
getMdxSource?: typeof defaultGetMdxSource;
|
|
@@ -24,6 +30,12 @@ export default async function Page({
|
|
|
24
30
|
getMdxSource,
|
|
25
31
|
}: Props): Promise<ReactElement> {
|
|
26
32
|
const { locale } = await params;
|
|
33
|
+
const source = await (getMdxSource ?? loadMdxSource)(
|
|
34
|
+
"ClientsList",
|
|
35
|
+
"clients",
|
|
36
|
+
locale,
|
|
37
|
+
);
|
|
38
|
+
honourNavOnly(source?.frontmatter, locale, []);
|
|
27
39
|
return renderPage({
|
|
28
40
|
type: "ClientsList",
|
|
29
41
|
slug: "clients",
|
|
@@ -5,12 +5,15 @@ import type {
|
|
|
5
5
|
getMdxSource as defaultGetMdxSource,
|
|
6
6
|
getPageSections as defaultGetPageSections} from "@working-theory/page-renderer";
|
|
7
7
|
import {
|
|
8
|
+
getMdxSource as loadMdxSource,
|
|
8
9
|
renderPage,
|
|
9
10
|
renderPageMetadata
|
|
10
11
|
} from "@working-theory/page-renderer";
|
|
11
12
|
import type { Metadata } from "next";
|
|
12
13
|
import type { ReactElement } from "react";
|
|
13
14
|
|
|
15
|
+
import { honourNavOnly } from "../../../lib/hub-children";
|
|
16
|
+
|
|
14
17
|
interface Props {
|
|
15
18
|
params: Promise<{ locale: string }>;
|
|
16
19
|
getMdxSource?: typeof defaultGetMdxSource;
|
|
@@ -30,6 +33,12 @@ export default async function Page({
|
|
|
30
33
|
getPageSections,
|
|
31
34
|
}: Props): Promise<ReactElement> {
|
|
32
35
|
const { locale } = await params;
|
|
36
|
+
const source = await (getMdxSource ?? loadMdxSource)(
|
|
37
|
+
"FAQPage",
|
|
38
|
+
"faq",
|
|
39
|
+
locale,
|
|
40
|
+
);
|
|
41
|
+
honourNavOnly(source?.frontmatter, locale, []);
|
|
33
42
|
return renderPage({
|
|
34
43
|
type: "FAQPage",
|
|
35
44
|
slug: "faq",
|
|
@@ -13,6 +13,8 @@ import {
|
|
|
13
13
|
import type { Metadata } from "next";
|
|
14
14
|
import type { ReactElement } from "react";
|
|
15
15
|
|
|
16
|
+
import { honourNavOnly } from "../../../lib/hub-children";
|
|
17
|
+
|
|
16
18
|
interface Props {
|
|
17
19
|
params: Promise<{ locale: string }>;
|
|
18
20
|
getMdxSource?: typeof defaultGetMdxSource;
|
|
@@ -35,6 +37,7 @@ export default async function Page({
|
|
|
35
37
|
}: Props): Promise<ReactElement> {
|
|
36
38
|
const { locale } = await params;
|
|
37
39
|
const source = await (getMdxSource ?? loadMdxSource)("GlossaryList", "glossary", locale);
|
|
40
|
+
honourNavOnly(source?.frontmatter, locale, []);
|
|
38
41
|
const composed =
|
|
39
42
|
source !== null &&
|
|
40
43
|
Array.isArray((source.frontmatter as { sections?: unknown[] }).sections) &&
|
|
@@ -25,6 +25,7 @@ jest.mock("@working-theory/page-renderer", () => ({
|
|
|
25
25
|
getMdxSource: jest.fn(),
|
|
26
26
|
}));
|
|
27
27
|
|
|
28
|
+
// eslint-disable-next-line no-restricted-syntax -- #507: the route passes no `listSlugs`, so `listDetailPages` would list the real content tree; the category's child list is this test's fixture.
|
|
28
29
|
jest.mock("../../../../lib/route-family", () => ({
|
|
29
30
|
__esModule: true,
|
|
30
31
|
listDetailPages: (...args: unknown[]) => mockListDetailPages(...args),
|
|
@@ -222,6 +222,7 @@ describe("TemplatesPage — the generated card grid (#10051 AC-4)", () => {
|
|
|
222
222
|
|
|
223
223
|
it("emits an ItemList with as many elements as cards", async () => {
|
|
224
224
|
const sections = await renderedSections(["a", "b", "c"]);
|
|
225
|
+
// eslint-disable-next-line no-restricted-syntax -- the builder returns a loose JSON-LD node; this test reads one nested key of it
|
|
225
226
|
const jsonLd = buildTemplatesList({
|
|
226
227
|
type: "TemplatesList",
|
|
227
228
|
slug: "templates",
|
|
@@ -27,6 +27,7 @@ import { notFound } from "next/navigation";
|
|
|
27
27
|
import { getTranslations } from "next-intl/server";
|
|
28
28
|
import type { ReactElement } from "react";
|
|
29
29
|
|
|
30
|
+
import { honourNavOnly } from "../../../lib/hub-children";
|
|
30
31
|
import {
|
|
31
32
|
insertBeforeClosingCtas,
|
|
32
33
|
listDetailPages,
|
|
@@ -67,6 +68,11 @@ export default async function Page({
|
|
|
67
68
|
}
|
|
68
69
|
|
|
69
70
|
const pages = await listDetailPages("TemplatePage", locale, load);
|
|
71
|
+
honourNavOnly(
|
|
72
|
+
source.frontmatter,
|
|
73
|
+
locale,
|
|
74
|
+
pages.map((page) => `/templates/${page.slug}`),
|
|
75
|
+
);
|
|
70
76
|
const t = await getTranslations({ namespace: "templates", locale });
|
|
71
77
|
const labels = templateLabels(t);
|
|
72
78
|
|
|
@@ -3,10 +3,16 @@
|
|
|
3
3
|
import type {
|
|
4
4
|
getMdxSource as defaultGetMdxSource,
|
|
5
5
|
} from "@working-theory/page-renderer";
|
|
6
|
-
import {
|
|
6
|
+
import {
|
|
7
|
+
getMdxSource as loadMdxSource,
|
|
8
|
+
renderPage,
|
|
9
|
+
renderPageMetadata,
|
|
10
|
+
} from "@working-theory/page-renderer";
|
|
7
11
|
import type { Metadata } from "next";
|
|
8
12
|
import type { ReactElement } from "react";
|
|
9
13
|
|
|
14
|
+
import { honourNavOnly } from "../../../lib/hub-children";
|
|
15
|
+
|
|
10
16
|
interface Props {
|
|
11
17
|
params: Promise<{ locale: string }>;
|
|
12
18
|
getMdxSource?: typeof defaultGetMdxSource;
|
|
@@ -28,6 +34,12 @@ export default async function Page({
|
|
|
28
34
|
getMdxSource,
|
|
29
35
|
}: Props): Promise<ReactElement> {
|
|
30
36
|
const { locale } = await params;
|
|
37
|
+
const source = await (getMdxSource ?? loadMdxSource)(
|
|
38
|
+
"TestimonialsList",
|
|
39
|
+
"testimonials",
|
|
40
|
+
locale,
|
|
41
|
+
);
|
|
42
|
+
honourNavOnly(source?.frontmatter, locale, []);
|
|
31
43
|
return renderPage({
|
|
32
44
|
type: "TestimonialsList",
|
|
33
45
|
slug: "testimonials",
|
|
@@ -162,6 +162,7 @@ export function describeHub(
|
|
|
162
162
|
loaders(listType, childType, children, [HERO, CLOSING]),
|
|
163
163
|
);
|
|
164
164
|
const sections = await input.getPageSections({});
|
|
165
|
+
// eslint-disable-next-line no-restricted-syntax -- buildJsonLd returns a loose JSON-LD node; this case reads one nested key of it
|
|
165
166
|
const jsonLd = buildJsonLd({
|
|
166
167
|
type: listType,
|
|
167
168
|
slug: route,
|
|
@@ -30,7 +30,9 @@ import {
|
|
|
30
30
|
getMdxSource as loadMdxSource,
|
|
31
31
|
getPageSections as loadPageSections,
|
|
32
32
|
} from "@working-theory/page-renderer";
|
|
33
|
+
import { localeHref } from "@working-theory/ui";
|
|
33
34
|
import { SectionRenderer } from "@working-theory/ui/sections";
|
|
35
|
+
import { notFound, permanentRedirect } from "next/navigation";
|
|
34
36
|
|
|
35
37
|
import {
|
|
36
38
|
type DetailPage,
|
|
@@ -242,6 +244,26 @@ function hasSections(frontmatter: unknown): boolean {
|
|
|
242
244
|
return Array.isArray(sections) && sections.length > 0;
|
|
243
245
|
}
|
|
244
246
|
|
|
247
|
+
/**
|
|
248
|
+
* Nav only (#10200): a list that is a heading in the site navigation with no
|
|
249
|
+
* page of its own answers a permanent redirect to its first child — in the
|
|
250
|
+
* route's own child order — and 404 when it lists none. Any other page
|
|
251
|
+
* returns, and renders as before.
|
|
252
|
+
*/
|
|
253
|
+
export function honourNavOnly(
|
|
254
|
+
frontmatter: unknown,
|
|
255
|
+
locale: string,
|
|
256
|
+
childHrefs: readonly string[],
|
|
257
|
+
): void {
|
|
258
|
+
if ((frontmatter as { navOnly?: unknown } | undefined)?.navOnly !== true)
|
|
259
|
+
return;
|
|
260
|
+
const first = childHrefs[0];
|
|
261
|
+
if (first === undefined) notFound();
|
|
262
|
+
permanentRedirect(
|
|
263
|
+
localeHref(locale, first, process.env.NEXT_PUBLIC_DEFAULT_LOCALE ?? "en"),
|
|
264
|
+
);
|
|
265
|
+
}
|
|
266
|
+
|
|
245
267
|
type RenderPageInput = Parameters<typeof renderPage>[0];
|
|
246
268
|
|
|
247
269
|
/**
|
|
@@ -279,25 +301,32 @@ export async function hubPageInput(
|
|
|
279
301
|
};
|
|
280
302
|
}
|
|
281
303
|
|
|
282
|
-
const
|
|
283
|
-
source.frontmatter,
|
|
284
|
-
);
|
|
285
|
-
const pages = await listDetailPages(
|
|
304
|
+
const listed = await listDetailPages(
|
|
286
305
|
config.childType,
|
|
287
306
|
locale,
|
|
288
307
|
load,
|
|
289
308
|
loaders.getMdxSlugs ?? loadMdxSlugs,
|
|
290
309
|
);
|
|
310
|
+
const pages = config.includeChild
|
|
311
|
+
? listed.filter(config.includeChild)
|
|
312
|
+
: listed;
|
|
313
|
+
honourNavOnly(
|
|
314
|
+
source.frontmatter,
|
|
315
|
+
locale,
|
|
316
|
+
pages.flatMap((page) => {
|
|
317
|
+
const card = config.card?.(page) ?? defaultCard(page, config);
|
|
318
|
+
return card.link === undefined ? [] : [card.link.href];
|
|
319
|
+
}),
|
|
320
|
+
);
|
|
321
|
+
const declared = await (loaders.getPageSections ?? loadPageSections)(
|
|
322
|
+
source.frontmatter,
|
|
323
|
+
);
|
|
291
324
|
// A prose hub renders its MDX body, never its declared sections, so its
|
|
292
325
|
// existing links are read from the body and its cards stand alone after it.
|
|
293
326
|
const linked = mdxBody
|
|
294
327
|
? bodyLinkPaths(source.body)
|
|
295
328
|
: linkedPaths(declared);
|
|
296
|
-
const generated = childCardsSection(
|
|
297
|
-
linked,
|
|
298
|
-
config.includeChild ? pages.filter(config.includeChild) : pages,
|
|
299
|
-
config,
|
|
300
|
-
);
|
|
329
|
+
const generated = childCardsSection(linked, pages, config);
|
|
301
330
|
const placed =
|
|
302
331
|
generated === null
|
|
303
332
|
? declared
|
|
@@ -1396,7 +1396,6 @@
|
|
|
1396
1396
|
"notFoundHeadline": "Page not found",
|
|
1397
1397
|
"notFoundDescription": "There is no page at this address. It may have been renamed or removed.",
|
|
1398
1398
|
"editor": {
|
|
1399
|
-
"tabPreview": "Preview",
|
|
1400
1399
|
"toolChanges": "Show unsaved changes",
|
|
1401
1400
|
"backLabel": "Pages",
|
|
1402
1401
|
"statusDraft": "Draft",
|
|
@@ -1427,6 +1426,26 @@
|
|
|
1427
1426
|
"toolNumberedList": "Numbered list",
|
|
1428
1427
|
"toolLink": "Link",
|
|
1429
1428
|
"toolImage": "Insert image",
|
|
1429
|
+
"toolView": "View",
|
|
1430
|
+
"toolRendered": "Rendered — how readers see it",
|
|
1431
|
+
"toolCode": "Markdown — the words with their marks",
|
|
1432
|
+
"toolInsert": "Insert from your library",
|
|
1433
|
+
"insertMeeting": "Add meeting",
|
|
1434
|
+
"insertMagnet": "Add magnet",
|
|
1435
|
+
"insertOffer": "Add offer",
|
|
1436
|
+
"insertAsset": "Add asset",
|
|
1437
|
+
"tips": {
|
|
1438
|
+
"h1": "Your page title is the H1. Use H2 for sections.",
|
|
1439
|
+
"h2": "Types ## before the line → a section heading.",
|
|
1440
|
+
"h3": "Types ### before the line → a smaller heading.",
|
|
1441
|
+
"bold": "Types **word** → bold.",
|
|
1442
|
+
"italic": "Types *word* → italic.",
|
|
1443
|
+
"bulletList": "Types - before each line → a bulleted list.",
|
|
1444
|
+
"numberedList": "Types 1. before each line → a numbered list.",
|
|
1445
|
+
"link": "Types [words](address) → a link.",
|
|
1446
|
+
"insert": "A meeting, magnet, offer or image from your library. AI uses these when it lays out the page.",
|
|
1447
|
+
"changes": "Compare your unsaved edits with the last save."
|
|
1448
|
+
},
|
|
1430
1449
|
"issueRequired": "{field} is required.",
|
|
1431
1450
|
"issueInvalid": "{field} isn't a valid choice.",
|
|
1432
1451
|
"forYou": {
|
|
@@ -1455,7 +1474,8 @@
|
|
|
1455
1474
|
"imageInsertError": "Could not upload that image. Try again.",
|
|
1456
1475
|
"diffLegendAdded": "Added",
|
|
1457
1476
|
"diffLegendRemoved": "Removed",
|
|
1458
|
-
"diffLegendUnchanged": "Unchanged"
|
|
1477
|
+
"diffLegendUnchanged": "Unchanged",
|
|
1478
|
+
"tagPlaceholder": "{kind}: {slug}"
|
|
1459
1479
|
},
|
|
1460
1480
|
"sections": {
|
|
1461
1481
|
"heading": "Sections",
|
|
@@ -1025,6 +1025,12 @@ export interface PlanEnvRowsOptions {
|
|
|
1025
1025
|
* with no reader and so no `scope`; a platform or refused name is skipped.
|
|
1026
1026
|
*/
|
|
1027
1027
|
declare?: readonly string[];
|
|
1028
|
+
/**
|
|
1029
|
+
* When set, only reads in these site-relative files get a row (#10173):
|
|
1030
|
+
* `update` declares what the files it wrote read, never a name only the
|
|
1031
|
+
* site's own code reads.
|
|
1032
|
+
*/
|
|
1033
|
+
onlyFiles?: ReadonlySet<string>;
|
|
1028
1034
|
}
|
|
1029
1035
|
|
|
1030
1036
|
/** The header the writer puts above a block it creates. */
|
|
@@ -1066,6 +1072,7 @@ export function planEnvRows(
|
|
|
1066
1072
|
const refused = new Set(opts.topology === "spoke" ? SPOKE_REFUSED_ENV : []);
|
|
1067
1073
|
const files = new Map<string, string[]>();
|
|
1068
1074
|
for (const read of collectSiteReads(root).reads) {
|
|
1075
|
+
if (opts.onlyFiles !== undefined && !opts.onlyFiles.has(read.file)) continue;
|
|
1069
1076
|
if (isPlatformEnvName(read.name) || refused.has(read.name)) continue;
|
|
1070
1077
|
if (declared.has(read.name)) continue;
|
|
1071
1078
|
const list = files.get(read.name) ?? [];
|
package/versions.json
CHANGED
|
@@ -1,66 +1,66 @@
|
|
|
1
1
|
{
|
|
2
|
-
"@working-theory/admin-ui": "0.
|
|
3
|
-
"@working-theory/aeo": "0.
|
|
4
|
-
"@working-theory/agent-context": "0.
|
|
2
|
+
"@working-theory/admin-ui": "0.51.0",
|
|
3
|
+
"@working-theory/aeo": "0.51.0",
|
|
4
|
+
"@working-theory/agent-context": "0.51.0",
|
|
5
5
|
"@working-theory/agent-contract": "0.1.0",
|
|
6
6
|
"@working-theory/agent-memory": "0.0.0",
|
|
7
7
|
"@working-theory/ai": "0.1.0",
|
|
8
|
-
"@working-theory/analytics": "0.
|
|
9
|
-
"@working-theory/api": "0.
|
|
10
|
-
"@working-theory/api-client": "0.
|
|
8
|
+
"@working-theory/analytics": "0.51.0",
|
|
9
|
+
"@working-theory/api": "0.51.0",
|
|
10
|
+
"@working-theory/api-client": "0.51.0",
|
|
11
11
|
"@working-theory/cda-filesystem": "0.0.0",
|
|
12
|
-
"@working-theory/cli": "0.
|
|
13
|
-
"@working-theory/common": "0.
|
|
12
|
+
"@working-theory/cli": "0.51.0",
|
|
13
|
+
"@working-theory/common": "0.51.0",
|
|
14
14
|
"@working-theory/consent": "0.1.0",
|
|
15
|
-
"@working-theory/content-adapters": "0.
|
|
16
|
-
"@working-theory/content-projection": "0.
|
|
17
|
-
"@working-theory/email": "0.
|
|
15
|
+
"@working-theory/content-adapters": "0.51.0",
|
|
16
|
+
"@working-theory/content-projection": "0.51.0",
|
|
17
|
+
"@working-theory/email": "0.51.0",
|
|
18
18
|
"@working-theory/env": "0.0.0",
|
|
19
|
-
"@working-theory/eslint-config": "0.
|
|
20
|
-
"@working-theory/fumadocs-schema": "0.
|
|
21
|
-
"@working-theory/ids": "0.
|
|
22
|
-
"@working-theory/jsonld": "0.
|
|
23
|
-
"@working-theory/license": "0.
|
|
24
|
-
"@working-theory/mcp-server": "0.
|
|
25
|
-
"@working-theory/metadata": "0.
|
|
26
|
-
"@working-theory/observability": "0.
|
|
27
|
-
"@working-theory/page-renderer": "0.
|
|
19
|
+
"@working-theory/eslint-config": "0.51.0",
|
|
20
|
+
"@working-theory/fumadocs-schema": "0.51.0",
|
|
21
|
+
"@working-theory/ids": "0.17.0",
|
|
22
|
+
"@working-theory/jsonld": "0.51.0",
|
|
23
|
+
"@working-theory/license": "0.51.0",
|
|
24
|
+
"@working-theory/mcp-server": "0.51.0",
|
|
25
|
+
"@working-theory/metadata": "0.51.0",
|
|
26
|
+
"@working-theory/observability": "0.51.0",
|
|
27
|
+
"@working-theory/page-renderer": "0.51.0",
|
|
28
28
|
"@working-theory/plugin-mcp": "0.1.3",
|
|
29
|
-
"@working-theory/schema": "0.
|
|
30
|
-
"@working-theory/schema-pack-registry": "0.
|
|
31
|
-
"@working-theory/schema-validator": "0.
|
|
32
|
-
"@working-theory/sections-registry": "0.
|
|
33
|
-
"@working-theory/site-config": "0.
|
|
34
|
-
"@working-theory/spoke-checkout": "0.7.
|
|
35
|
-
"@working-theory/stripe-client": "0.
|
|
36
|
-
"@working-theory/templates": "0.
|
|
37
|
-
"@working-theory/theme": "0.
|
|
38
|
-
"@working-theory/ui": "0.
|
|
39
|
-
"@working-theory/validation": "0.
|
|
40
|
-
"@working-theory/web-i18n": "0.
|
|
41
|
-
"@working-theory/admin": "0.
|
|
42
|
-
"@working-theory/api-commerce": "0.1.
|
|
43
|
-
"@working-theory/api-content-write": "0.
|
|
44
|
-
"@working-theory/api-hub": "0.3.
|
|
45
|
-
"@working-theory/api-multi-tenant": "0.
|
|
46
|
-
"@working-theory/api-scheduling": "0.4.
|
|
47
|
-
"@working-theory/api-signal-collectors": "0.2.
|
|
48
|
-
"@working-theory/api-workspace": "0.3.
|
|
29
|
+
"@working-theory/schema": "0.51.0",
|
|
30
|
+
"@working-theory/schema-pack-registry": "0.51.0",
|
|
31
|
+
"@working-theory/schema-validator": "0.51.0",
|
|
32
|
+
"@working-theory/sections-registry": "0.51.0",
|
|
33
|
+
"@working-theory/site-config": "0.51.0",
|
|
34
|
+
"@working-theory/spoke-checkout": "0.7.7",
|
|
35
|
+
"@working-theory/stripe-client": "0.51.0",
|
|
36
|
+
"@working-theory/templates": "0.51.0",
|
|
37
|
+
"@working-theory/theme": "0.51.0",
|
|
38
|
+
"@working-theory/ui": "0.51.0",
|
|
39
|
+
"@working-theory/validation": "0.51.0",
|
|
40
|
+
"@working-theory/web-i18n": "0.51.0",
|
|
41
|
+
"@working-theory/admin": "0.8.0",
|
|
42
|
+
"@working-theory/api-commerce": "0.1.5",
|
|
43
|
+
"@working-theory/api-content-write": "0.2.0",
|
|
44
|
+
"@working-theory/api-hub": "0.3.2",
|
|
45
|
+
"@working-theory/api-multi-tenant": "0.2.0",
|
|
46
|
+
"@working-theory/api-scheduling": "0.4.2",
|
|
47
|
+
"@working-theory/api-signal-collectors": "0.2.4",
|
|
48
|
+
"@working-theory/api-workspace": "0.3.1",
|
|
49
49
|
"@working-theory/billing-client": "0.0.0",
|
|
50
50
|
"@working-theory/branding": "0.0.0",
|
|
51
51
|
"@working-theory/license-client": "0.0.0",
|
|
52
52
|
"@working-theory/license-manifest": "0.0.0",
|
|
53
|
-
"@working-theory/mcp-scheduling": "0.3.
|
|
54
|
-
"@working-theory/mcp-signal-collectors": "0.2.
|
|
55
|
-
"@working-theory/mcp-workspace": "0.
|
|
53
|
+
"@working-theory/mcp-scheduling": "0.3.4",
|
|
54
|
+
"@working-theory/mcp-signal-collectors": "0.2.5",
|
|
55
|
+
"@working-theory/mcp-workspace": "0.4.0",
|
|
56
56
|
"@working-theory/partner-session": "0.0.0",
|
|
57
57
|
"@working-theory/preview-protocol": "0.0.0",
|
|
58
|
-
"@working-theory/ui-commerce": "0.23.
|
|
59
|
-
"@working-theory/ui-docs-blog": "0.23.
|
|
60
|
-
"@working-theory/ui-scheduling": "0.25.
|
|
61
|
-
"@working-theory/ui-signal-collectors": "0.27.
|
|
58
|
+
"@working-theory/ui-commerce": "0.23.29",
|
|
59
|
+
"@working-theory/ui-docs-blog": "0.23.29",
|
|
60
|
+
"@working-theory/ui-scheduling": "0.25.3",
|
|
61
|
+
"@working-theory/ui-signal-collectors": "0.27.7",
|
|
62
62
|
"@working-theory/web": "0.0.0",
|
|
63
63
|
"@working-theory/app-api": "1.0.0",
|
|
64
64
|
"@working-theory/mcp": "0.0.0",
|
|
65
|
-
"@working-theory/database": "0.
|
|
65
|
+
"@working-theory/database": "0.51.0"
|
|
66
66
|
}
|