create-ailk 0.11.0 → 0.11.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/dist/lib/scaffold-render-sweep.d.ts +16 -0
- package/dist/lib/scaffold-render-sweep.js +52 -3
- package/package.json +1 -1
- package/site-templates/shapes/local-business-service/content/en/Service/first-job.mdx +8 -8
- package/site-templates/shapes/local-business-service/content/en/Service/second-job.mdx +8 -8
- package/site-templates/shapes/local-business-service/content/en/Service/third-job.mdx +8 -8
- package/site-templates/shapes/organization-service/content/en/Service/first-engagement.mdx +8 -8
- package/site-templates/shapes/organization-service/content/en/Service/second-engagement.mdx +8 -8
- package/site-templates/shapes/organization-service/content/en/Service/third-engagement.mdx +8 -8
- package/site-templates/shapes/person-service/content/en/Service/first-engagement.mdx +8 -8
- package/site-templates/shapes/person-service/content/en/Service/second-engagement.mdx +8 -8
- package/site-templates/shapes/person-service/content/en/Service/third-engagement.mdx +8 -8
- package/templates/.env.example +0 -4
- package/templates/apps/api/package.json +1 -1
- package/templates/apps/mcp/package.json +1 -1
- package/templates/apps/web/CLAUDE.md +1 -1
- package/templates/apps/web/app/[locale]/careers/[slug]/page.tsx +0 -10
- package/templates/apps/web/app/[locale]/case-studies/[slug]/page.tsx +0 -10
- package/templates/apps/web/app/[locale]/courses/[slug]/page.tsx +1 -14
- package/templates/apps/web/app/[locale]/events/[slug]/page.tsx +0 -10
- package/templates/apps/web/app/[locale]/features/[slug]/page.tsx +0 -10
- package/templates/apps/web/app/[locale]/for/[slug]/page.tsx +0 -10
- package/templates/apps/web/app/[locale]/how-to/[slug]/page.tsx +1 -14
- package/templates/apps/web/app/[locale]/integrations/[slug]/page.tsx +0 -10
- package/templates/apps/web/app/[locale]/partners/[slug]/page.tsx +0 -10
- package/templates/apps/web/app/[locale]/portfolio/[slug]/page.tsx +0 -10
- package/templates/apps/web/app/[locale]/products/[slug]/page.tsx +0 -10
- package/templates/apps/web/app/[locale]/projects/[slug]/page.tsx +1 -15
- package/templates/apps/web/app/[locale]/resources/[slug]/page.tsx +0 -10
- package/templates/apps/web/app/[locale]/services/[slug]/page.tsx +0 -10
- package/templates/apps/web/app/[locale]/software/[slug]/page.tsx +0 -10
- package/templates/apps/web/app/[locale]/tech-articles/[slug]/page.tsx +1 -14
- package/templates/apps/web/app/[locale]/thanks/[slug]/page.tsx +4 -16
- package/templates/apps/web/app/[locale]/vs/[slug]/__tests__/page.test.tsx +3 -15
- package/templates/apps/web/app/[locale]/vs/[slug]/page.tsx +0 -10
- package/templates/apps/web/app/[locale]/whitepapers/[slug]/page.tsx +1 -14
- package/versions.json +0 -1
|
@@ -103,3 +103,19 @@ export declare function assessConfigGatedRoute(input: {
|
|
|
103
103
|
* a socket) throws rather than being silently followed or skipped.
|
|
104
104
|
*/
|
|
105
105
|
export declare function listPublicFiles(dir: string): string[];
|
|
106
|
+
/** The slug the sweep requests to prove a detail route 404s an unknown slug. */
|
|
107
|
+
export declare const UNKNOWN_SLUG = "ailk-unknown-slug-7379";
|
|
108
|
+
/** `/features/[slug]` → `/features`; null unless `[slug]` is the one dynamic segment and the last. */
|
|
109
|
+
export declare function detailRoutePrefix(route: string): string | null;
|
|
110
|
+
/** The sorted basenames (sans `.mdx`) of the `*.mdx` files directly in `dir`; [] when `dir` is absent. */
|
|
111
|
+
export declare function listSeedSlugs(dir: string): string[];
|
|
112
|
+
/** An unknown slug must answer exactly 404, with no redirect. A 500 is the #7379 failure; a 200 means no 404 branch ran. */
|
|
113
|
+
export declare function assessUnknownSlug(input: {
|
|
114
|
+
route: string;
|
|
115
|
+
hops: RedirectHop[];
|
|
116
|
+
status: number | null;
|
|
117
|
+
}): SweepVerdict;
|
|
118
|
+
/** Fails, naming each route, when `.next/prerender-manifest.json`'s `dynamicRoutes` has a key under `/[locale]/`. */
|
|
119
|
+
export declare function assessPrerenderManifest(manifest: {
|
|
120
|
+
dynamicRoutes?: Record<string, unknown>;
|
|
121
|
+
}): SweepVerdict;
|
|
@@ -13,10 +13,17 @@
|
|
|
13
13
|
* not cost a templates build, an install and a `next build`. Each is a pure
|
|
14
14
|
* function of what the driver observed, so
|
|
15
15
|
* `__tests__/verify-scaffold-render-sweep.test.ts` drives them directly. The
|
|
16
|
-
* driver loads this module from `dist/`. Only `listPublicFiles`
|
|
17
|
-
* filesystem, and none of it touches the network.
|
|
16
|
+
* driver loads this module from `dist/`. Only `listPublicFiles` and
|
|
17
|
+
* `listSeedSlugs` touch the filesystem, and none of it touches the network.
|
|
18
|
+
*
|
|
19
|
+
* #7379 added two sweeps. A starter roster's detail row that names a seed
|
|
20
|
+
* directory (`/features/[slug]` → `content/en/Feature`) is probed: each seed
|
|
21
|
+
* must render, and `UNKNOWN_SLUG` must answer 404. And the build's
|
|
22
|
+
* `prerender-manifest.json` must register no `/[locale]/…` route as an
|
|
23
|
+
* on-demand prerender — under the cookie-reading locale layout, such a route
|
|
24
|
+
* answers 500 for every slug, seeded or not.
|
|
18
25
|
*/
|
|
19
|
-
import { readdirSync } from "node:fs";
|
|
26
|
+
import { existsSync, readdirSync } from "node:fs";
|
|
20
27
|
import { join } from "node:path";
|
|
21
28
|
/** More hops than this is a redirect chain, not a render. */
|
|
22
29
|
export const MAX_REDIRECTS = 5;
|
|
@@ -234,3 +241,45 @@ function listFilesUnder(dir, relPrefix) {
|
|
|
234
241
|
}
|
|
235
242
|
return out;
|
|
236
243
|
}
|
|
244
|
+
/** The slug the sweep requests to prove a detail route 404s an unknown slug. */
|
|
245
|
+
export const UNKNOWN_SLUG = "ailk-unknown-slug-7379";
|
|
246
|
+
/** `/features/[slug]` → `/features`; null unless `[slug]` is the one dynamic segment and the last. */
|
|
247
|
+
export function detailRoutePrefix(route) {
|
|
248
|
+
const match = /^((?:\/[^/[\]]+)+)\/\[slug\]$/.exec(route);
|
|
249
|
+
return match?.[1] ?? null;
|
|
250
|
+
}
|
|
251
|
+
/** The sorted basenames (sans `.mdx`) of the `*.mdx` files directly in `dir`; [] when `dir` is absent. */
|
|
252
|
+
export function listSeedSlugs(dir) {
|
|
253
|
+
if (!existsSync(dir))
|
|
254
|
+
return [];
|
|
255
|
+
return readdirSync(dir, { withFileTypes: true })
|
|
256
|
+
.filter((entry) => entry.isFile() && entry.name.endsWith(".mdx"))
|
|
257
|
+
.map((entry) => entry.name.slice(0, -".mdx".length))
|
|
258
|
+
.sort();
|
|
259
|
+
}
|
|
260
|
+
/** An unknown slug must answer exactly 404, with no redirect. A 500 is the #7379 failure; a 200 means no 404 branch ran. */
|
|
261
|
+
export function assessUnknownSlug(input) {
|
|
262
|
+
const { route, hops, status } = input;
|
|
263
|
+
const first = hops[0];
|
|
264
|
+
if (first !== undefined) {
|
|
265
|
+
return { ok: false, reason: `${route} -> ${first.status} redirect (an unknown slug must answer 404, not redirect)` };
|
|
266
|
+
}
|
|
267
|
+
if (status === 200)
|
|
268
|
+
return { ok: false, reason: `${route} -> 200 (no 404 branch ran for an unknown slug)` };
|
|
269
|
+
if (status !== 404)
|
|
270
|
+
return { ok: false, reason: `${route} -> status ${status} (expected 404 for an unknown slug)` };
|
|
271
|
+
return { ok: true, reason: "404" };
|
|
272
|
+
}
|
|
273
|
+
/** Fails, naming each route, when `.next/prerender-manifest.json`'s `dynamicRoutes` has a key under `/[locale]/`. */
|
|
274
|
+
export function assessPrerenderManifest(manifest) {
|
|
275
|
+
const locale = Object.keys(manifest.dynamicRoutes ?? {})
|
|
276
|
+
.filter((route) => route.startsWith("/[locale]/"))
|
|
277
|
+
.sort();
|
|
278
|
+
if (locale.length > 0) {
|
|
279
|
+
return {
|
|
280
|
+
ok: false,
|
|
281
|
+
reason: `on-demand prerender under the cookie-reading locale layout (500s every slug, #7379): ${locale.join(", ")}`,
|
|
282
|
+
};
|
|
283
|
+
}
|
|
284
|
+
return { ok: true, reason: "no /[locale] on-demand prerender route" };
|
|
285
|
+
}
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "create-ailk",
|
|
3
|
-
"version": "0.11.
|
|
3
|
+
"version": "0.11.1",
|
|
4
4
|
"description": "Scaffolder for AI Launch Kit — `pnpm create ailk@latest <dir>` produces a starter repo consuming @working-theory/* from npm (per ADR 0007).",
|
|
5
5
|
"type": "module",
|
|
6
6
|
"main": "./dist/index.js",
|
|
@@ -11,29 +11,29 @@ schema:
|
|
|
11
11
|
name: "Your Company"
|
|
12
12
|
---
|
|
13
13
|
|
|
14
|
-
# {First job}
|
|
14
|
+
# \{First job}
|
|
15
15
|
|
|
16
|
-
{Open with the situation a reader is in when they come looking for this. Two or
|
|
16
|
+
\{Open with the situation a reader is in when they come looking for this. Two or
|
|
17
17
|
three sentences, no more — the detail belongs under the headings below.}
|
|
18
18
|
|
|
19
19
|
## What it covers
|
|
20
20
|
|
|
21
|
-
- {The first thing you actually do}
|
|
22
|
-
- {The second thing you actually do}
|
|
23
|
-
- {The third thing you actually do}
|
|
21
|
+
- \{The first thing you actually do}
|
|
22
|
+
- \{The second thing you actually do}
|
|
23
|
+
- \{The third thing you actually do}
|
|
24
24
|
|
|
25
25
|
## How it runs
|
|
26
26
|
|
|
27
|
-
{One paragraph on the shape of the visit: how it is scheduled, how long it
|
|
27
|
+
\{One paragraph on the shape of the visit: how it is scheduled, how long it
|
|
28
28
|
takes, who turns up, and what you need from the customer beforehand.}
|
|
29
29
|
|
|
30
30
|
## What you end up with
|
|
31
31
|
|
|
32
|
-
{One paragraph on the result. Be concrete — describe the property when the
|
|
32
|
+
\{One paragraph on the result. Be concrete — describe the property when the
|
|
33
33
|
crew drives away.}
|
|
34
34
|
|
|
35
35
|
## What it costs
|
|
36
36
|
|
|
37
|
-
{One line on the pricing shape — flat rate, hourly, or by the job — and what it
|
|
37
|
+
\{One line on the pricing shape — flat rate, hourly, or by the job — and what it
|
|
38
38
|
excludes. Replace with your real numbers, or delete this section and let a
|
|
39
39
|
call do it.}
|
|
@@ -11,29 +11,29 @@ schema:
|
|
|
11
11
|
name: "Your Company"
|
|
12
12
|
---
|
|
13
13
|
|
|
14
|
-
# {Second job}
|
|
14
|
+
# \{Second job}
|
|
15
15
|
|
|
16
|
-
{Open with the situation a reader is in when they come looking for this. Two or
|
|
16
|
+
\{Open with the situation a reader is in when they come looking for this. Two or
|
|
17
17
|
three sentences, no more — the detail belongs under the headings below.}
|
|
18
18
|
|
|
19
19
|
## What it covers
|
|
20
20
|
|
|
21
|
-
- {The first thing you actually do}
|
|
22
|
-
- {The second thing you actually do}
|
|
23
|
-
- {The third thing you actually do}
|
|
21
|
+
- \{The first thing you actually do}
|
|
22
|
+
- \{The second thing you actually do}
|
|
23
|
+
- \{The third thing you actually do}
|
|
24
24
|
|
|
25
25
|
## How it runs
|
|
26
26
|
|
|
27
|
-
{One paragraph on the shape of the visit: how it is scheduled, how long it
|
|
27
|
+
\{One paragraph on the shape of the visit: how it is scheduled, how long it
|
|
28
28
|
takes, who turns up, and what you need from the customer beforehand.}
|
|
29
29
|
|
|
30
30
|
## What you end up with
|
|
31
31
|
|
|
32
|
-
{One paragraph on the result. Be concrete — describe the property when the
|
|
32
|
+
\{One paragraph on the result. Be concrete — describe the property when the
|
|
33
33
|
crew drives away.}
|
|
34
34
|
|
|
35
35
|
## What it costs
|
|
36
36
|
|
|
37
|
-
{One line on the pricing shape — flat rate, hourly, or by the job — and what it
|
|
37
|
+
\{One line on the pricing shape — flat rate, hourly, or by the job — and what it
|
|
38
38
|
excludes. Replace with your real numbers, or delete this section and let a
|
|
39
39
|
call do it.}
|
|
@@ -11,29 +11,29 @@ schema:
|
|
|
11
11
|
name: "Your Company"
|
|
12
12
|
---
|
|
13
13
|
|
|
14
|
-
# {Third job}
|
|
14
|
+
# \{Third job}
|
|
15
15
|
|
|
16
|
-
{Open with the situation a reader is in when they come looking for this. Two or
|
|
16
|
+
\{Open with the situation a reader is in when they come looking for this. Two or
|
|
17
17
|
three sentences, no more — the detail belongs under the headings below.}
|
|
18
18
|
|
|
19
19
|
## What it covers
|
|
20
20
|
|
|
21
|
-
- {The first thing you actually do}
|
|
22
|
-
- {The second thing you actually do}
|
|
23
|
-
- {The third thing you actually do}
|
|
21
|
+
- \{The first thing you actually do}
|
|
22
|
+
- \{The second thing you actually do}
|
|
23
|
+
- \{The third thing you actually do}
|
|
24
24
|
|
|
25
25
|
## How it runs
|
|
26
26
|
|
|
27
|
-
{One paragraph on the shape of the visit: how it is scheduled, how long it
|
|
27
|
+
\{One paragraph on the shape of the visit: how it is scheduled, how long it
|
|
28
28
|
takes, who turns up, and what you need from the customer beforehand.}
|
|
29
29
|
|
|
30
30
|
## What you end up with
|
|
31
31
|
|
|
32
|
-
{One paragraph on the result. Be concrete — describe the property when the
|
|
32
|
+
\{One paragraph on the result. Be concrete — describe the property when the
|
|
33
33
|
crew drives away.}
|
|
34
34
|
|
|
35
35
|
## What it costs
|
|
36
36
|
|
|
37
|
-
{One line on the pricing shape — flat rate, hourly, or by the job — and what it
|
|
37
|
+
\{One line on the pricing shape — flat rate, hourly, or by the job — and what it
|
|
38
38
|
excludes. Replace with your real numbers, or delete this section and let a
|
|
39
39
|
call do it.}
|
|
@@ -10,29 +10,29 @@ schema:
|
|
|
10
10
|
name: "Your Company"
|
|
11
11
|
---
|
|
12
12
|
|
|
13
|
-
# {First engagement}
|
|
13
|
+
# \{First engagement}
|
|
14
14
|
|
|
15
|
-
{Open with the situation a reader is in when they come looking for this. Two or
|
|
15
|
+
\{Open with the situation a reader is in when they come looking for this. Two or
|
|
16
16
|
three sentences, no more — the detail belongs under the headings below.}
|
|
17
17
|
|
|
18
18
|
## What it covers
|
|
19
19
|
|
|
20
|
-
- {The first thing you actually do}
|
|
21
|
-
- {The second thing you actually do}
|
|
22
|
-
- {The third thing you actually do}
|
|
20
|
+
- \{The first thing you actually do}
|
|
21
|
+
- \{The second thing you actually do}
|
|
22
|
+
- \{The third thing you actually do}
|
|
23
23
|
|
|
24
24
|
## How it runs
|
|
25
25
|
|
|
26
|
-
{One paragraph on the shape of the engagement: how it starts, how long it runs,
|
|
26
|
+
\{One paragraph on the shape of the engagement: how it starts, how long it runs,
|
|
27
27
|
who is involved on both sides, and what a week looks like.}
|
|
28
28
|
|
|
29
29
|
## What you end up with
|
|
30
30
|
|
|
31
|
-
{One paragraph on the deliverable. Be concrete — name the artefact, not the
|
|
31
|
+
\{One paragraph on the deliverable. Be concrete — name the artefact, not the
|
|
32
32
|
feeling.}
|
|
33
33
|
|
|
34
34
|
## What it costs
|
|
35
35
|
|
|
36
|
-
{One line on the pricing shape — fixed fee, retainer, day rate — and what it
|
|
36
|
+
\{One line on the pricing shape — fixed fee, retainer, day rate — and what it
|
|
37
37
|
excludes. Replace with your real numbers, or delete this section and let the
|
|
38
38
|
call do it.}
|
|
@@ -10,29 +10,29 @@ schema:
|
|
|
10
10
|
name: "Your Company"
|
|
11
11
|
---
|
|
12
12
|
|
|
13
|
-
# {Second engagement}
|
|
13
|
+
# \{Second engagement}
|
|
14
14
|
|
|
15
|
-
{Open with the situation a reader is in when they come looking for this. Two or
|
|
15
|
+
\{Open with the situation a reader is in when they come looking for this. Two or
|
|
16
16
|
three sentences, no more — the detail belongs under the headings below.}
|
|
17
17
|
|
|
18
18
|
## What it covers
|
|
19
19
|
|
|
20
|
-
- {The first thing you actually do}
|
|
21
|
-
- {The second thing you actually do}
|
|
22
|
-
- {The third thing you actually do}
|
|
20
|
+
- \{The first thing you actually do}
|
|
21
|
+
- \{The second thing you actually do}
|
|
22
|
+
- \{The third thing you actually do}
|
|
23
23
|
|
|
24
24
|
## How it runs
|
|
25
25
|
|
|
26
|
-
{One paragraph on the shape of the engagement: how it starts, how long it runs,
|
|
26
|
+
\{One paragraph on the shape of the engagement: how it starts, how long it runs,
|
|
27
27
|
who is involved on both sides, and what a week looks like.}
|
|
28
28
|
|
|
29
29
|
## What you end up with
|
|
30
30
|
|
|
31
|
-
{One paragraph on the deliverable. Be concrete — name the artefact, not the
|
|
31
|
+
\{One paragraph on the deliverable. Be concrete — name the artefact, not the
|
|
32
32
|
feeling.}
|
|
33
33
|
|
|
34
34
|
## What it costs
|
|
35
35
|
|
|
36
|
-
{One line on the pricing shape — fixed fee, retainer, day rate — and what it
|
|
36
|
+
\{One line on the pricing shape — fixed fee, retainer, day rate — and what it
|
|
37
37
|
excludes. Replace with your real numbers, or delete this section and let the
|
|
38
38
|
call do it.}
|
|
@@ -10,29 +10,29 @@ schema:
|
|
|
10
10
|
name: "Your Company"
|
|
11
11
|
---
|
|
12
12
|
|
|
13
|
-
# {Third engagement}
|
|
13
|
+
# \{Third engagement}
|
|
14
14
|
|
|
15
|
-
{Open with the situation a reader is in when they come looking for this. Two or
|
|
15
|
+
\{Open with the situation a reader is in when they come looking for this. Two or
|
|
16
16
|
three sentences, no more — the detail belongs under the headings below.}
|
|
17
17
|
|
|
18
18
|
## What it covers
|
|
19
19
|
|
|
20
|
-
- {The first thing you actually do}
|
|
21
|
-
- {The second thing you actually do}
|
|
22
|
-
- {The third thing you actually do}
|
|
20
|
+
- \{The first thing you actually do}
|
|
21
|
+
- \{The second thing you actually do}
|
|
22
|
+
- \{The third thing you actually do}
|
|
23
23
|
|
|
24
24
|
## How it runs
|
|
25
25
|
|
|
26
|
-
{One paragraph on the shape of the engagement: how it starts, how long it runs,
|
|
26
|
+
\{One paragraph on the shape of the engagement: how it starts, how long it runs,
|
|
27
27
|
who is involved on both sides, and what a week looks like.}
|
|
28
28
|
|
|
29
29
|
## What you end up with
|
|
30
30
|
|
|
31
|
-
{One paragraph on the deliverable. Be concrete — name the artefact, not the
|
|
31
|
+
\{One paragraph on the deliverable. Be concrete — name the artefact, not the
|
|
32
32
|
feeling.}
|
|
33
33
|
|
|
34
34
|
## What it costs
|
|
35
35
|
|
|
36
|
-
{One line on the pricing shape — fixed fee, retainer, day rate — and what it
|
|
36
|
+
\{One line on the pricing shape — fixed fee, retainer, day rate — and what it
|
|
37
37
|
excludes. Replace with your real numbers, or delete this section and let the
|
|
38
38
|
call do it.}
|
|
@@ -10,29 +10,29 @@ schema:
|
|
|
10
10
|
name: "Your Name"
|
|
11
11
|
---
|
|
12
12
|
|
|
13
|
-
# {First engagement}
|
|
13
|
+
# \{First engagement}
|
|
14
14
|
|
|
15
|
-
{Open with the situation a reader is in when they come looking for this. Two or
|
|
15
|
+
\{Open with the situation a reader is in when they come looking for this. Two or
|
|
16
16
|
three sentences, no more — the detail belongs under the headings below.}
|
|
17
17
|
|
|
18
18
|
## What it covers
|
|
19
19
|
|
|
20
|
-
- {The first thing you actually do}
|
|
21
|
-
- {The second thing you actually do}
|
|
22
|
-
- {The third thing you actually do}
|
|
20
|
+
- \{The first thing you actually do}
|
|
21
|
+
- \{The second thing you actually do}
|
|
22
|
+
- \{The third thing you actually do}
|
|
23
23
|
|
|
24
24
|
## How it runs
|
|
25
25
|
|
|
26
|
-
{One paragraph on the shape of the engagement: how it starts, how long it runs,
|
|
26
|
+
\{One paragraph on the shape of the engagement: how it starts, how long it runs,
|
|
27
27
|
who is involved, and what a week looks like.}
|
|
28
28
|
|
|
29
29
|
## What you end up with
|
|
30
30
|
|
|
31
|
-
{One paragraph on the deliverable. Be concrete — name the artefact, not the
|
|
31
|
+
\{One paragraph on the deliverable. Be concrete — name the artefact, not the
|
|
32
32
|
feeling.}
|
|
33
33
|
|
|
34
34
|
## What it costs
|
|
35
35
|
|
|
36
|
-
{One line on the pricing shape — fixed fee, retainer, day rate — and what it
|
|
36
|
+
\{One line on the pricing shape — fixed fee, retainer, day rate — and what it
|
|
37
37
|
excludes. Replace with your real numbers, or delete this section and let the
|
|
38
38
|
call do it.}
|
|
@@ -10,29 +10,29 @@ schema:
|
|
|
10
10
|
name: "Your Name"
|
|
11
11
|
---
|
|
12
12
|
|
|
13
|
-
# {Second engagement}
|
|
13
|
+
# \{Second engagement}
|
|
14
14
|
|
|
15
|
-
{Open with the situation a reader is in when they come looking for this. Two or
|
|
15
|
+
\{Open with the situation a reader is in when they come looking for this. Two or
|
|
16
16
|
three sentences, no more — the detail belongs under the headings below.}
|
|
17
17
|
|
|
18
18
|
## What it covers
|
|
19
19
|
|
|
20
|
-
- {The first thing you actually do}
|
|
21
|
-
- {The second thing you actually do}
|
|
22
|
-
- {The third thing you actually do}
|
|
20
|
+
- \{The first thing you actually do}
|
|
21
|
+
- \{The second thing you actually do}
|
|
22
|
+
- \{The third thing you actually do}
|
|
23
23
|
|
|
24
24
|
## How it runs
|
|
25
25
|
|
|
26
|
-
{One paragraph on the shape of the engagement: how it starts, how long it runs,
|
|
26
|
+
\{One paragraph on the shape of the engagement: how it starts, how long it runs,
|
|
27
27
|
who is involved, and what a week looks like.}
|
|
28
28
|
|
|
29
29
|
## What you end up with
|
|
30
30
|
|
|
31
|
-
{One paragraph on the deliverable. Be concrete — name the artefact, not the
|
|
31
|
+
\{One paragraph on the deliverable. Be concrete — name the artefact, not the
|
|
32
32
|
feeling.}
|
|
33
33
|
|
|
34
34
|
## What it costs
|
|
35
35
|
|
|
36
|
-
{One line on the pricing shape — fixed fee, retainer, day rate — and what it
|
|
36
|
+
\{One line on the pricing shape — fixed fee, retainer, day rate — and what it
|
|
37
37
|
excludes. Replace with your real numbers, or delete this section and let the
|
|
38
38
|
call do it.}
|
|
@@ -10,29 +10,29 @@ schema:
|
|
|
10
10
|
name: "Your Name"
|
|
11
11
|
---
|
|
12
12
|
|
|
13
|
-
# {Third engagement}
|
|
13
|
+
# \{Third engagement}
|
|
14
14
|
|
|
15
|
-
{Open with the situation a reader is in when they come looking for this. Two or
|
|
15
|
+
\{Open with the situation a reader is in when they come looking for this. Two or
|
|
16
16
|
three sentences, no more — the detail belongs under the headings below.}
|
|
17
17
|
|
|
18
18
|
## What it covers
|
|
19
19
|
|
|
20
|
-
- {The first thing you actually do}
|
|
21
|
-
- {The second thing you actually do}
|
|
22
|
-
- {The third thing you actually do}
|
|
20
|
+
- \{The first thing you actually do}
|
|
21
|
+
- \{The second thing you actually do}
|
|
22
|
+
- \{The third thing you actually do}
|
|
23
23
|
|
|
24
24
|
## How it runs
|
|
25
25
|
|
|
26
|
-
{One paragraph on the shape of the engagement: how it starts, how long it runs,
|
|
26
|
+
\{One paragraph on the shape of the engagement: how it starts, how long it runs,
|
|
27
27
|
who is involved, and what a week looks like.}
|
|
28
28
|
|
|
29
29
|
## What you end up with
|
|
30
30
|
|
|
31
|
-
{One paragraph on the deliverable. Be concrete — name the artefact, not the
|
|
31
|
+
\{One paragraph on the deliverable. Be concrete — name the artefact, not the
|
|
32
32
|
feeling.}
|
|
33
33
|
|
|
34
34
|
## What it costs
|
|
35
35
|
|
|
36
|
-
{One line on the pricing shape — fixed fee, retainer, day rate — and what it
|
|
36
|
+
\{One line on the pricing shape — fixed fee, retainer, day rate — and what it
|
|
37
37
|
excludes. Replace with your real numbers, or delete this section and let the
|
|
38
38
|
call do it.}
|
package/templates/.env.example
CHANGED
|
@@ -50,10 +50,6 @@ CONTENT_ADAPTER=mdx
|
|
|
50
50
|
# Developers → Webhooks → <your endpoint> → Signing secret.
|
|
51
51
|
# Format: whsec_*. Server-only — must NOT be prefixed with NEXT_PUBLIC_.
|
|
52
52
|
# STRIPE_WEBHOOK_SECRET=
|
|
53
|
-
# Internal URL of the billing service — used by marketing-api's webhook
|
|
54
|
-
# proxy (POST /v1/webhooks/stripe) to forward verified deliveries. Format:
|
|
55
|
-
# https://<vercel-deployment>.vercel.app (no trailing slash, no path).
|
|
56
|
-
# BILLING_SERVICE_URL=
|
|
57
53
|
|
|
58
54
|
# ── Auth (Better Auth — apps/web only) ───────────────────────────────────────
|
|
59
55
|
# BETTER_AUTH_URL=http://localhost:3000
|
|
@@ -14,7 +14,7 @@ Inherits root `CLAUDE.md`. This file adds web-specific rules.
|
|
|
14
14
|
|
|
15
15
|
## Always do first
|
|
16
16
|
|
|
17
|
-
- For any UI work:
|
|
17
|
+
- For any UI work: read § Theme & styling and § Iterate visually below before writing code. To add a whole page, run the `/ailk:add-page` skill.
|
|
18
18
|
- For any page-rendering or schema-emission work: read `packages/schema/README.md` (catalog) and `packages/schema/CLAUDE.md` (operational layer + component map) if you haven't this session.
|
|
19
19
|
- For content authoring questions: read `content/CLAUDE.md` first.
|
|
20
20
|
- For a full page build, follow the per-page loop (Content intent → Layout → Author content → Visual iterate). The visual-iterate step uses `screenshots/` as scratch (see § Iterate visually).
|
|
@@ -3,7 +3,6 @@
|
|
|
3
3
|
// Careers list at /careers links here; each job posting lives at /careers/<slug>.
|
|
4
4
|
import {
|
|
5
5
|
getMdxSource as defaultGetMdxSource,
|
|
6
|
-
getMdxSlugs,
|
|
7
6
|
renderPage,
|
|
8
7
|
renderPageMetadata,
|
|
9
8
|
} from "@working-theory/page-renderer";
|
|
@@ -23,15 +22,6 @@ export async function generateMetadata({
|
|
|
23
22
|
return renderPageMetadata({ type: "JobPosting", slug, locale });
|
|
24
23
|
}
|
|
25
24
|
|
|
26
|
-
export async function generateStaticParams({
|
|
27
|
-
params,
|
|
28
|
-
}: {
|
|
29
|
-
params: { locale: string };
|
|
30
|
-
}): Promise<{ slug: string }[]> {
|
|
31
|
-
const slugs = await getMdxSlugs("JobPosting", params.locale);
|
|
32
|
-
return slugs.map((slug: string) => ({ slug }));
|
|
33
|
-
}
|
|
34
|
-
|
|
35
25
|
export default async function Page({
|
|
36
26
|
params,
|
|
37
27
|
getMdxSource = defaultGetMdxSource,
|
|
@@ -2,7 +2,6 @@
|
|
|
2
2
|
// next-mdx-remote/rsc, and emits a JSON-LD <script>. Unknown slugs trigger 404.
|
|
3
3
|
import {
|
|
4
4
|
getMdxSource as defaultGetMdxSource,
|
|
5
|
-
getMdxSlugs,
|
|
6
5
|
renderPage,
|
|
7
6
|
renderPageMetadata,
|
|
8
7
|
} from "@working-theory/page-renderer";
|
|
@@ -22,15 +21,6 @@ export async function generateMetadata({
|
|
|
22
21
|
return renderPageMetadata({ type: "CaseStudy", slug, locale });
|
|
23
22
|
}
|
|
24
23
|
|
|
25
|
-
export async function generateStaticParams({
|
|
26
|
-
params,
|
|
27
|
-
}: {
|
|
28
|
-
params: { locale: string };
|
|
29
|
-
}): Promise<{ slug: string }[]> {
|
|
30
|
-
const slugs = await getMdxSlugs("CaseStudy", params.locale);
|
|
31
|
-
return slugs.map((slug: string) => ({ slug }));
|
|
32
|
-
}
|
|
33
|
-
|
|
34
24
|
export default async function Page({
|
|
35
25
|
params,
|
|
36
26
|
getMdxSource = defaultGetMdxSource,
|
|
@@ -1,11 +1,7 @@
|
|
|
1
1
|
// Course detail page. Reads the Course MDX for the given slug, renders via
|
|
2
2
|
// @working-theory/page-renderer, and emits a JSON-LD <script>. Unknown slugs trigger 404.
|
|
3
3
|
import type { getMdxSource as defaultGetMdxSource } from "@working-theory/page-renderer";
|
|
4
|
-
import {
|
|
5
|
-
getMdxSlugs,
|
|
6
|
-
renderPage,
|
|
7
|
-
renderPageMetadata,
|
|
8
|
-
} from "@working-theory/page-renderer";
|
|
4
|
+
import { renderPage, renderPageMetadata } from "@working-theory/page-renderer";
|
|
9
5
|
import type { Metadata } from "next";
|
|
10
6
|
import type { ReactElement } from "react";
|
|
11
7
|
|
|
@@ -21,15 +17,6 @@ export async function generateMetadata({
|
|
|
21
17
|
return renderPageMetadata({ type: "Course", slug, locale });
|
|
22
18
|
}
|
|
23
19
|
|
|
24
|
-
export async function generateStaticParams({
|
|
25
|
-
params,
|
|
26
|
-
}: {
|
|
27
|
-
params: { locale: string };
|
|
28
|
-
}): Promise<{ slug: string }[]> {
|
|
29
|
-
const slugs = await getMdxSlugs("Course", params.locale);
|
|
30
|
-
return slugs.map((slug) => ({ slug }));
|
|
31
|
-
}
|
|
32
|
-
|
|
33
20
|
export default async function Page({
|
|
34
21
|
params,
|
|
35
22
|
getMdxSource,
|
|
@@ -4,7 +4,6 @@ import {
|
|
|
4
4
|
renderPage,
|
|
5
5
|
renderPageMetadata,
|
|
6
6
|
getMdxSource as defaultGetMdxSource,
|
|
7
|
-
getMdxSlugs,
|
|
8
7
|
} from "@working-theory/page-renderer";
|
|
9
8
|
import type { Metadata } from "next";
|
|
10
9
|
import { notFound } from "next/navigation";
|
|
@@ -22,15 +21,6 @@ export async function generateMetadata({
|
|
|
22
21
|
return renderPageMetadata({ type: "Event", slug, locale });
|
|
23
22
|
}
|
|
24
23
|
|
|
25
|
-
export async function generateStaticParams({
|
|
26
|
-
params,
|
|
27
|
-
}: {
|
|
28
|
-
params: { locale: string };
|
|
29
|
-
}): Promise<{ slug: string }[]> {
|
|
30
|
-
const slugs = await getMdxSlugs("Event", params.locale);
|
|
31
|
-
return slugs.map((slug) => ({ slug }));
|
|
32
|
-
}
|
|
33
|
-
|
|
34
24
|
export default async function Page({
|
|
35
25
|
params,
|
|
36
26
|
getMdxSource = defaultGetMdxSource,
|
|
@@ -7,7 +7,6 @@
|
|
|
7
7
|
import type { getMdxSource as defaultGetMdxSource } from "@working-theory/page-renderer";
|
|
8
8
|
import {
|
|
9
9
|
getMdxSource as loadMdxSource,
|
|
10
|
-
getMdxSlugs,
|
|
11
10
|
renderPage,
|
|
12
11
|
renderPageMetadata,
|
|
13
12
|
} from "@working-theory/page-renderer";
|
|
@@ -26,15 +25,6 @@ export async function generateMetadata({
|
|
|
26
25
|
return renderPageMetadata({ type: "Feature", slug, locale });
|
|
27
26
|
}
|
|
28
27
|
|
|
29
|
-
export async function generateStaticParams({
|
|
30
|
-
params,
|
|
31
|
-
}: {
|
|
32
|
-
params: { locale: string };
|
|
33
|
-
}): Promise<{ slug: string }[]> {
|
|
34
|
-
const slugs = await getMdxSlugs("Feature", params.locale);
|
|
35
|
-
return slugs.map((slug) => ({ slug }));
|
|
36
|
-
}
|
|
37
|
-
|
|
38
28
|
export default async function Page({
|
|
39
29
|
params,
|
|
40
30
|
getMdxSource,
|
|
@@ -7,7 +7,6 @@
|
|
|
7
7
|
import type { getMdxSource as defaultGetMdxSource } from "@working-theory/page-renderer";
|
|
8
8
|
import {
|
|
9
9
|
getMdxSource as loadMdxSource,
|
|
10
|
-
getMdxSlugs,
|
|
11
10
|
renderPage,
|
|
12
11
|
renderPageMetadata,
|
|
13
12
|
} from "@working-theory/page-renderer";
|
|
@@ -26,15 +25,6 @@ export async function generateMetadata({
|
|
|
26
25
|
return renderPageMetadata({ type: "UseCase", slug, locale });
|
|
27
26
|
}
|
|
28
27
|
|
|
29
|
-
export async function generateStaticParams({
|
|
30
|
-
params,
|
|
31
|
-
}: {
|
|
32
|
-
params: { locale: string };
|
|
33
|
-
}): Promise<{ slug: string }[]> {
|
|
34
|
-
const slugs = await getMdxSlugs("UseCase", params.locale);
|
|
35
|
-
return slugs.map((slug) => ({ slug }));
|
|
36
|
-
}
|
|
37
|
-
|
|
38
28
|
export default async function Page({
|
|
39
29
|
params,
|
|
40
30
|
getMdxSource,
|
|
@@ -1,11 +1,7 @@
|
|
|
1
1
|
// HowTo detail page. Reads the HowTo MDX for the given slug, renders via
|
|
2
2
|
// @working-theory/page-renderer, and emits a JSON-LD <script>. Unknown slugs trigger 404.
|
|
3
3
|
import type { getMdxSource as defaultGetMdxSource } from "@working-theory/page-renderer";
|
|
4
|
-
import {
|
|
5
|
-
getMdxSlugs,
|
|
6
|
-
renderPage,
|
|
7
|
-
renderPageMetadata,
|
|
8
|
-
} from "@working-theory/page-renderer";
|
|
4
|
+
import { renderPage, renderPageMetadata } from "@working-theory/page-renderer";
|
|
9
5
|
import type { Metadata } from "next";
|
|
10
6
|
import type { ReactElement } from "react";
|
|
11
7
|
|
|
@@ -21,15 +17,6 @@ export async function generateMetadata({
|
|
|
21
17
|
return renderPageMetadata({ type: "HowTo", slug, locale });
|
|
22
18
|
}
|
|
23
19
|
|
|
24
|
-
export async function generateStaticParams({
|
|
25
|
-
params,
|
|
26
|
-
}: {
|
|
27
|
-
params: { locale: string };
|
|
28
|
-
}): Promise<{ slug: string }[]> {
|
|
29
|
-
const slugs = await getMdxSlugs("HowTo", params.locale);
|
|
30
|
-
return slugs.map((slug) => ({ slug }));
|
|
31
|
-
}
|
|
32
|
-
|
|
33
20
|
export default async function Page({
|
|
34
21
|
params,
|
|
35
22
|
getMdxSource,
|
|
@@ -4,7 +4,6 @@ import {
|
|
|
4
4
|
renderPage,
|
|
5
5
|
renderPageMetadata,
|
|
6
6
|
getMdxSource as defaultGetMdxSource,
|
|
7
|
-
getMdxSlugs,
|
|
8
7
|
} from "@working-theory/page-renderer";
|
|
9
8
|
import type { Metadata } from "next";
|
|
10
9
|
import { notFound } from "next/navigation";
|
|
@@ -22,15 +21,6 @@ export async function generateMetadata({
|
|
|
22
21
|
return renderPageMetadata({ type: "Integration", slug, locale });
|
|
23
22
|
}
|
|
24
23
|
|
|
25
|
-
export async function generateStaticParams({
|
|
26
|
-
params,
|
|
27
|
-
}: {
|
|
28
|
-
params: { locale: string };
|
|
29
|
-
}): Promise<{ slug: string }[]> {
|
|
30
|
-
const slugs = await getMdxSlugs("Integration", params.locale);
|
|
31
|
-
return slugs.map((slug) => ({ slug }));
|
|
32
|
-
}
|
|
33
|
-
|
|
34
24
|
export default async function Page({
|
|
35
25
|
params,
|
|
36
26
|
getMdxSource = defaultGetMdxSource,
|
|
@@ -2,7 +2,6 @@
|
|
|
2
2
|
// next-mdx-remote/rsc, and emits a JSON-LD <script>. Unknown slugs trigger 404.
|
|
3
3
|
import {
|
|
4
4
|
getMdxSource as defaultGetMdxSource,
|
|
5
|
-
getMdxSlugs,
|
|
6
5
|
renderPage,
|
|
7
6
|
renderPageMetadata,
|
|
8
7
|
} from "@working-theory/page-renderer";
|
|
@@ -22,15 +21,6 @@ export async function generateMetadata({
|
|
|
22
21
|
return renderPageMetadata({ type: "Partner", slug, locale });
|
|
23
22
|
}
|
|
24
23
|
|
|
25
|
-
export async function generateStaticParams({
|
|
26
|
-
params,
|
|
27
|
-
}: {
|
|
28
|
-
params: { locale: string };
|
|
29
|
-
}): Promise<{ slug: string }[]> {
|
|
30
|
-
const slugs = await getMdxSlugs("Partner", params.locale);
|
|
31
|
-
return slugs.map((slug: string) => ({ slug }));
|
|
32
|
-
}
|
|
33
|
-
|
|
34
24
|
export default async function Page({
|
|
35
25
|
params,
|
|
36
26
|
getMdxSource = defaultGetMdxSource,
|
|
@@ -4,7 +4,6 @@
|
|
|
4
4
|
// NO_HAND_IMPLEMENT_OK: /build-starter operator-directed live pass (#6687).
|
|
5
5
|
import {
|
|
6
6
|
getMdxSource as defaultGetMdxSource,
|
|
7
|
-
getMdxSlugs,
|
|
8
7
|
renderPage,
|
|
9
8
|
renderPageMetadata,
|
|
10
9
|
} from "@working-theory/page-renderer";
|
|
@@ -24,15 +23,6 @@ export async function generateMetadata({
|
|
|
24
23
|
return renderPageMetadata({ type: "PortfolioItem", slug, locale });
|
|
25
24
|
}
|
|
26
25
|
|
|
27
|
-
export async function generateStaticParams({
|
|
28
|
-
params,
|
|
29
|
-
}: {
|
|
30
|
-
params: { locale: string };
|
|
31
|
-
}): Promise<{ slug: string }[]> {
|
|
32
|
-
const slugs = await getMdxSlugs("PortfolioItem", params.locale);
|
|
33
|
-
return slugs.map((slug: string) => ({ slug }));
|
|
34
|
-
}
|
|
35
|
-
|
|
36
26
|
export default async function Page({
|
|
37
27
|
params,
|
|
38
28
|
getMdxSource = defaultGetMdxSource,
|
|
@@ -4,7 +4,6 @@ import {
|
|
|
4
4
|
renderPage,
|
|
5
5
|
renderPageMetadata,
|
|
6
6
|
getMdxSource as defaultGetMdxSource,
|
|
7
|
-
getMdxSlugs,
|
|
8
7
|
} from "@working-theory/page-renderer";
|
|
9
8
|
import type { Metadata } from "next";
|
|
10
9
|
import { notFound } from "next/navigation";
|
|
@@ -22,15 +21,6 @@ export async function generateMetadata({
|
|
|
22
21
|
return renderPageMetadata({ type: "Product", slug, locale });
|
|
23
22
|
}
|
|
24
23
|
|
|
25
|
-
export async function generateStaticParams({
|
|
26
|
-
params,
|
|
27
|
-
}: {
|
|
28
|
-
params: { locale: string };
|
|
29
|
-
}): Promise<{ slug: string }[]> {
|
|
30
|
-
const slugs = await getMdxSlugs("Product", params.locale);
|
|
31
|
-
return slugs.map((slug) => ({ slug }));
|
|
32
|
-
}
|
|
33
|
-
|
|
34
24
|
export default async function Page({
|
|
35
25
|
params,
|
|
36
26
|
getMdxSource = defaultGetMdxSource,
|
|
@@ -4,11 +4,7 @@ import type {
|
|
|
4
4
|
getMdxSource as defaultGetMdxSource,
|
|
5
5
|
getPageSections as defaultGetPageSections,
|
|
6
6
|
} from "@working-theory/page-renderer";
|
|
7
|
-
import {
|
|
8
|
-
getContentAdapter,
|
|
9
|
-
renderPage,
|
|
10
|
-
renderPageMetadata,
|
|
11
|
-
} from "@working-theory/page-renderer";
|
|
7
|
+
import { renderPage, renderPageMetadata } from "@working-theory/page-renderer";
|
|
12
8
|
import type { Metadata } from "next";
|
|
13
9
|
import type { ReactElement } from "react";
|
|
14
10
|
|
|
@@ -25,16 +21,6 @@ export async function generateMetadata({
|
|
|
25
21
|
return renderPageMetadata({ type: "Project", slug, locale });
|
|
26
22
|
}
|
|
27
23
|
|
|
28
|
-
export async function generateStaticParams({
|
|
29
|
-
params,
|
|
30
|
-
}: {
|
|
31
|
-
params: { locale: string };
|
|
32
|
-
}): Promise<{ slug: string }[]> {
|
|
33
|
-
const adapter = getContentAdapter();
|
|
34
|
-
const pages = await adapter.listDetail("Project", params.locale);
|
|
35
|
-
return pages.map((page) => ({ slug: page.slug }));
|
|
36
|
-
}
|
|
37
|
-
|
|
38
24
|
export default async function Page({
|
|
39
25
|
params,
|
|
40
26
|
getMdxSource,
|
|
@@ -4,7 +4,6 @@ import type {
|
|
|
4
4
|
getMdxSource as defaultGetMdxSource,
|
|
5
5
|
} from "@working-theory/page-renderer";
|
|
6
6
|
import {
|
|
7
|
-
getMdxSlugs,
|
|
8
7
|
getMdxSource as defaultGetMdxSourceImpl,
|
|
9
8
|
renderPage,
|
|
10
9
|
renderPageMetadata,
|
|
@@ -25,15 +24,6 @@ export async function generateMetadata({
|
|
|
25
24
|
return renderPageMetadata({ type: "ResourceDetail", slug, locale });
|
|
26
25
|
}
|
|
27
26
|
|
|
28
|
-
export async function generateStaticParams({
|
|
29
|
-
params,
|
|
30
|
-
}: {
|
|
31
|
-
params: { locale: string };
|
|
32
|
-
}): Promise<{ slug: string }[]> {
|
|
33
|
-
const slugs = await getMdxSlugs("ResourceDetail", params.locale);
|
|
34
|
-
return slugs.map((slug) => ({ slug }));
|
|
35
|
-
}
|
|
36
|
-
|
|
37
27
|
export default async function Page({
|
|
38
28
|
params,
|
|
39
29
|
getMdxSource = defaultGetMdxSourceImpl,
|
|
@@ -4,7 +4,6 @@ import {
|
|
|
4
4
|
renderPage,
|
|
5
5
|
renderPageMetadata,
|
|
6
6
|
getMdxSource as defaultGetMdxSource,
|
|
7
|
-
getMdxSlugs,
|
|
8
7
|
} from "@working-theory/page-renderer";
|
|
9
8
|
import type { Metadata } from "next";
|
|
10
9
|
import { notFound } from "next/navigation";
|
|
@@ -22,15 +21,6 @@ export async function generateMetadata({
|
|
|
22
21
|
return renderPageMetadata({ type: "Service", slug, locale });
|
|
23
22
|
}
|
|
24
23
|
|
|
25
|
-
export async function generateStaticParams({
|
|
26
|
-
params,
|
|
27
|
-
}: {
|
|
28
|
-
params: { locale: string };
|
|
29
|
-
}): Promise<{ slug: string }[]> {
|
|
30
|
-
const slugs = await getMdxSlugs("Service", params.locale);
|
|
31
|
-
return slugs.map((slug) => ({ slug }));
|
|
32
|
-
}
|
|
33
|
-
|
|
34
24
|
export default async function Page({
|
|
35
25
|
params,
|
|
36
26
|
getMdxSource = defaultGetMdxSource,
|
|
@@ -4,7 +4,6 @@
|
|
|
4
4
|
// renders them; a prose-only page keeps the MDX-body render.
|
|
5
5
|
import type { getMdxSource as defaultGetMdxSource } from "@working-theory/page-renderer";
|
|
6
6
|
import {
|
|
7
|
-
getMdxSlugs,
|
|
8
7
|
getMdxSource as loadMdxSource,
|
|
9
8
|
renderPage,
|
|
10
9
|
renderPageMetadata,
|
|
@@ -24,15 +23,6 @@ export async function generateMetadata({
|
|
|
24
23
|
return renderPageMetadata({ type: "SoftwareProduct", slug, locale });
|
|
25
24
|
}
|
|
26
25
|
|
|
27
|
-
export async function generateStaticParams({
|
|
28
|
-
params,
|
|
29
|
-
}: {
|
|
30
|
-
params: { locale: string };
|
|
31
|
-
}): Promise<{ slug: string }[]> {
|
|
32
|
-
const slugs = await getMdxSlugs("SoftwareProduct", params.locale);
|
|
33
|
-
return slugs.map((slug) => ({ slug }));
|
|
34
|
-
}
|
|
35
|
-
|
|
36
26
|
export default async function Page({
|
|
37
27
|
params,
|
|
38
28
|
getMdxSource,
|
|
@@ -1,11 +1,7 @@
|
|
|
1
1
|
// TechArticle detail page. Reads the TechArticle MDX for the given slug, renders via
|
|
2
2
|
// @working-theory/page-renderer, and emits a JSON-LD <script>. Unknown slugs trigger 404.
|
|
3
3
|
import type { getMdxSource as defaultGetMdxSource } from "@working-theory/page-renderer";
|
|
4
|
-
import {
|
|
5
|
-
getMdxSlugs,
|
|
6
|
-
renderPage,
|
|
7
|
-
renderPageMetadata,
|
|
8
|
-
} from "@working-theory/page-renderer";
|
|
4
|
+
import { renderPage, renderPageMetadata } from "@working-theory/page-renderer";
|
|
9
5
|
import type { Metadata } from "next";
|
|
10
6
|
import type { ReactElement } from "react";
|
|
11
7
|
|
|
@@ -21,15 +17,6 @@ export async function generateMetadata({
|
|
|
21
17
|
return renderPageMetadata({ type: "TechArticle", slug, locale });
|
|
22
18
|
}
|
|
23
19
|
|
|
24
|
-
export async function generateStaticParams({
|
|
25
|
-
params,
|
|
26
|
-
}: {
|
|
27
|
-
params: { locale: string };
|
|
28
|
-
}): Promise<{ slug: string }[]> {
|
|
29
|
-
const slugs = await getMdxSlugs("TechArticle", params.locale);
|
|
30
|
-
return slugs.map((slug) => ({ slug }));
|
|
31
|
-
}
|
|
32
|
-
|
|
33
20
|
export default async function Page({
|
|
34
21
|
params,
|
|
35
22
|
getMdxSource,
|
|
@@ -1,14 +1,11 @@
|
|
|
1
1
|
// ThankYouPage detail page (#3960 D6) — the segment-keyed flow-completion
|
|
2
|
-
// redirect target. Sections-mode renderPage (the ContactPage pattern)
|
|
3
|
-
//
|
|
2
|
+
// redirect target. Sections-mode renderPage (the ContactPage pattern),
|
|
3
|
+
// rendered on request under the cookie-reading locale layout; [slug]/page.tsx's
|
|
4
|
+
// header says why there is no static-params export (#7379). Unknown slugs 404
|
|
4
5
|
// via renderPage's own detail-route branch (no translationsNamespace passed
|
|
5
6
|
// — renderPage calls notFound() itself when getMdxSource resolves null).
|
|
6
7
|
import type { getMdxSource as defaultGetMdxSource } from "@working-theory/page-renderer";
|
|
7
|
-
import {
|
|
8
|
-
getMdxSlugs,
|
|
9
|
-
renderPage,
|
|
10
|
-
renderPageMetadata,
|
|
11
|
-
} from "@working-theory/page-renderer";
|
|
8
|
+
import { renderPage, renderPageMetadata } from "@working-theory/page-renderer";
|
|
12
9
|
import type { Metadata } from "next";
|
|
13
10
|
import type { ReactElement } from "react";
|
|
14
11
|
|
|
@@ -24,15 +21,6 @@ export async function generateMetadata({
|
|
|
24
21
|
return renderPageMetadata({ type: "ThankYouPage", slug, locale });
|
|
25
22
|
}
|
|
26
23
|
|
|
27
|
-
export async function generateStaticParams({
|
|
28
|
-
params,
|
|
29
|
-
}: {
|
|
30
|
-
params: { locale: string };
|
|
31
|
-
}): Promise<{ slug: string }[]> {
|
|
32
|
-
const slugs = await getMdxSlugs("ThankYouPage", params.locale);
|
|
33
|
-
return slugs.map((slug) => ({ slug }));
|
|
34
|
-
}
|
|
35
|
-
|
|
36
24
|
export default async function Page({
|
|
37
25
|
params,
|
|
38
26
|
getMdxSource,
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
/**
|
|
2
2
|
* Tests for /vs/[slug] (#6878 D9) — the ComparisonPage detail route.
|
|
3
|
-
* `renderPage`/`renderPageMetadata
|
|
3
|
+
* `renderPage`/`renderPageMetadata` are mocked at the
|
|
4
4
|
* `@working-theory/page-renderer` boundary; `getMdxSource` is supplied
|
|
5
5
|
* per-call through the route's DI prop.
|
|
6
6
|
*/
|
|
@@ -10,24 +10,19 @@
|
|
|
10
10
|
// production, unaffected by this mock's loose type here.
|
|
11
11
|
const mockRenderPage = jest.fn(async (..._args: unknown[]) => "<rendered/>");
|
|
12
12
|
const mockRenderPageMetadata = jest.fn((..._args: unknown[]) => undefined);
|
|
13
|
-
const mockGetMdxSlugs = jest.fn((..._args: unknown[]) => Promise.resolve<string[]>([]));
|
|
14
13
|
|
|
15
14
|
jest.mock("@working-theory/page-renderer", () => ({
|
|
16
15
|
__esModule: true,
|
|
17
|
-
getMdxSlugs: (...args: unknown[]) => mockGetMdxSlugs(...args),
|
|
18
16
|
renderPage: (...args: unknown[]) => mockRenderPage(...args),
|
|
19
17
|
renderPageMetadata: (...args: unknown[]) => mockRenderPageMetadata(...args),
|
|
20
18
|
getMdxSource: jest.fn(),
|
|
21
19
|
}));
|
|
22
20
|
|
|
23
|
-
import ComparisonPage, {
|
|
24
|
-
generateMetadata,
|
|
25
|
-
generateStaticParams,
|
|
26
|
-
} from "../page";
|
|
21
|
+
import ComparisonPage, { generateMetadata } from "../page";
|
|
27
22
|
|
|
28
23
|
beforeEach(() => jest.clearAllMocks());
|
|
29
24
|
|
|
30
|
-
describe("ComparisonPage detail — metadata
|
|
25
|
+
describe("ComparisonPage detail — metadata", () => {
|
|
31
26
|
it("resolves metadata for the given slug", async () => {
|
|
32
27
|
await generateMetadata({
|
|
33
28
|
params: Promise.resolve({ locale: "en", slug: "acme" }),
|
|
@@ -38,13 +33,6 @@ describe("ComparisonPage detail — metadata + static params", () => {
|
|
|
38
33
|
locale: "en",
|
|
39
34
|
});
|
|
40
35
|
});
|
|
41
|
-
|
|
42
|
-
it("enumerates every ComparisonPage slug for static generation", async () => {
|
|
43
|
-
mockGetMdxSlugs.mockResolvedValue(["acme", "globex"]);
|
|
44
|
-
const params = await generateStaticParams({ params: { locale: "en" } });
|
|
45
|
-
expect(mockGetMdxSlugs).toHaveBeenCalledWith("ComparisonPage", "en");
|
|
46
|
-
expect(params).toEqual([{ slug: "acme" }, { slug: "globex" }]);
|
|
47
|
-
});
|
|
48
36
|
});
|
|
49
37
|
|
|
50
38
|
describe("ComparisonPage detail — sections vs mdx-body", () => {
|
|
@@ -12,7 +12,6 @@
|
|
|
12
12
|
import type { getMdxSource as defaultGetMdxSource } from "@working-theory/page-renderer";
|
|
13
13
|
import {
|
|
14
14
|
getMdxSource as loadMdxSource,
|
|
15
|
-
getMdxSlugs,
|
|
16
15
|
renderPage,
|
|
17
16
|
renderPageMetadata,
|
|
18
17
|
} from "@working-theory/page-renderer";
|
|
@@ -31,15 +30,6 @@ export async function generateMetadata({
|
|
|
31
30
|
return renderPageMetadata({ type: "ComparisonPage", slug, locale });
|
|
32
31
|
}
|
|
33
32
|
|
|
34
|
-
export async function generateStaticParams({
|
|
35
|
-
params,
|
|
36
|
-
}: {
|
|
37
|
-
params: { locale: string };
|
|
38
|
-
}): Promise<{ slug: string }[]> {
|
|
39
|
-
const slugs = await getMdxSlugs("ComparisonPage", params.locale);
|
|
40
|
-
return slugs.map((slug) => ({ slug }));
|
|
41
|
-
}
|
|
42
|
-
|
|
43
33
|
export default async function Page({
|
|
44
34
|
params,
|
|
45
35
|
getMdxSource,
|
|
@@ -1,11 +1,7 @@
|
|
|
1
1
|
// Whitepaper detail page. Reads the Whitepaper MDX for the given slug, renders via
|
|
2
2
|
// @working-theory/page-renderer, and emits a JSON-LD <script>. Unknown slugs trigger 404.
|
|
3
3
|
import type { getMdxSource as defaultGetMdxSource } from "@working-theory/page-renderer";
|
|
4
|
-
import {
|
|
5
|
-
getMdxSlugs,
|
|
6
|
-
renderPage,
|
|
7
|
-
renderPageMetadata,
|
|
8
|
-
} from "@working-theory/page-renderer";
|
|
4
|
+
import { renderPage, renderPageMetadata } from "@working-theory/page-renderer";
|
|
9
5
|
import type { Metadata } from "next";
|
|
10
6
|
import type { ReactElement } from "react";
|
|
11
7
|
|
|
@@ -21,15 +17,6 @@ export async function generateMetadata({
|
|
|
21
17
|
return renderPageMetadata({ type: "Whitepaper", slug, locale });
|
|
22
18
|
}
|
|
23
19
|
|
|
24
|
-
export async function generateStaticParams({
|
|
25
|
-
params,
|
|
26
|
-
}: {
|
|
27
|
-
params: { locale: string };
|
|
28
|
-
}): Promise<{ slug: string }[]> {
|
|
29
|
-
const slugs = await getMdxSlugs("Whitepaper", params.locale);
|
|
30
|
-
return slugs.map((slug) => ({ slug }));
|
|
31
|
-
}
|
|
32
|
-
|
|
33
20
|
export default async function Page({
|
|
34
21
|
params,
|
|
35
22
|
getMdxSource,
|
package/versions.json
CHANGED
|
@@ -39,7 +39,6 @@
|
|
|
39
39
|
"@working-theory/branding": "0.0.0",
|
|
40
40
|
"@working-theory/license-client": "0.0.0",
|
|
41
41
|
"@working-theory/license-manifest": "0.0.0",
|
|
42
|
-
"@working-theory/marketing-api-client": "0.0.0",
|
|
43
42
|
"@working-theory/partner-session": "0.0.0",
|
|
44
43
|
"@working-theory/preview-protocol": "0.0.0",
|
|
45
44
|
"@working-theory/ui-commerce": "0.23.7",
|