create-ailk 0.10.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.
Files changed (103) hide show
  1. package/README.md +7 -2
  2. package/dist/cli.js +5 -1
  3. package/dist/content-payloads.d.ts +37 -0
  4. package/dist/content-payloads.js +64 -0
  5. package/dist/lib/fetch-module.d.ts +3 -3
  6. package/dist/lib/fetch-module.js +11 -5
  7. package/dist/lib/fetch-template.d.ts +1 -1
  8. package/dist/lib/fetch-template.js +7 -4
  9. package/dist/lib/scaffold-render-sweep.d.ts +16 -0
  10. package/dist/lib/scaffold-render-sweep.js +52 -3
  11. package/dist/module-architecture.d.ts +9 -0
  12. package/dist/module-architecture.js +43 -12
  13. package/dist/parse-args.d.ts +2 -0
  14. package/dist/parse-args.js +7 -0
  15. package/dist/surfaces.js +2 -3
  16. package/package.json +3 -3
  17. package/site-templates/marketing-site-free/TEMPLATE-MANIFEST.md +4 -3
  18. package/site-templates/shapes/local-business-service/content/en/Service/first-job.mdx +8 -8
  19. package/site-templates/shapes/local-business-service/content/en/Service/second-job.mdx +8 -8
  20. package/site-templates/shapes/local-business-service/content/en/Service/third-job.mdx +8 -8
  21. package/site-templates/shapes/organization-service/content/en/Service/first-engagement.mdx +8 -8
  22. package/site-templates/shapes/organization-service/content/en/Service/second-engagement.mdx +8 -8
  23. package/site-templates/shapes/organization-service/content/en/Service/third-engagement.mdx +8 -8
  24. package/site-templates/shapes/person-service/content/en/Service/first-engagement.mdx +8 -8
  25. package/site-templates/shapes/person-service/content/en/Service/second-engagement.mdx +8 -8
  26. package/site-templates/shapes/person-service/content/en/Service/third-engagement.mdx +8 -8
  27. package/templates/.claude/settings.json +12 -0
  28. package/templates/.env.example +1 -5
  29. package/templates/CLAUDE.md +38 -22
  30. package/templates/README.md +12 -12
  31. package/templates/apps/api/jest.config.cjs +1 -0
  32. package/templates/apps/api/package.json +2 -1
  33. package/templates/apps/mcp/CLAUDE.md +23 -10
  34. package/templates/apps/mcp/__tests__/catalog-drift.test.ts +160 -17
  35. package/templates/apps/mcp/__tests__/index.test.ts +118 -1
  36. package/templates/apps/mcp/__tests__/lib/commerce-claim.test.ts +425 -5
  37. package/templates/apps/mcp/__tests__/module-exclusion.test.ts +67 -3
  38. package/templates/apps/mcp/__tests__/server.test.ts +162 -1
  39. package/templates/apps/mcp/package.json +1 -1
  40. package/templates/apps/mcp/src/index.ts +35 -3
  41. package/templates/apps/mcp/src/lib/commerce-claim.ts +193 -24
  42. package/templates/apps/mcp/src/server.ts +77 -5
  43. package/templates/apps/mcp/src/tools/purchase.ts +1 -1
  44. package/templates/apps/web/CLAUDE.md +1 -1
  45. package/templates/apps/web/app/.well-known/mcp.json/build-manifest.ts +15 -0
  46. package/templates/apps/web/app/.well-known/mcp.json/route.ts +10 -3
  47. package/templates/apps/web/app/[locale]/careers/[slug]/page.tsx +0 -10
  48. package/templates/apps/web/app/[locale]/case-studies/[slug]/page.tsx +0 -10
  49. package/templates/apps/web/app/[locale]/courses/[slug]/page.tsx +1 -14
  50. package/templates/apps/web/app/[locale]/events/[slug]/page.tsx +0 -10
  51. package/templates/apps/web/app/[locale]/features/[slug]/page.tsx +0 -10
  52. package/templates/apps/web/app/[locale]/for/[slug]/page.tsx +0 -10
  53. package/templates/apps/web/app/[locale]/how-to/[slug]/page.tsx +1 -14
  54. package/templates/apps/web/app/[locale]/integrations/[slug]/page.tsx +0 -10
  55. package/templates/apps/web/app/[locale]/partners/[slug]/page.tsx +0 -10
  56. package/templates/apps/web/app/[locale]/portfolio/[slug]/page.tsx +0 -10
  57. package/templates/apps/web/app/[locale]/products/[slug]/page.tsx +0 -10
  58. package/templates/apps/web/app/[locale]/projects/[slug]/page.tsx +1 -15
  59. package/templates/apps/web/app/[locale]/resources/[slug]/page.tsx +0 -10
  60. package/templates/apps/web/app/[locale]/services/[slug]/page.tsx +0 -10
  61. package/templates/apps/web/app/[locale]/software/[slug]/page.tsx +0 -10
  62. package/templates/apps/web/app/[locale]/tech-articles/[slug]/page.tsx +1 -14
  63. package/templates/apps/web/app/[locale]/thanks/[slug]/page.tsx +4 -16
  64. package/templates/apps/web/app/[locale]/vs/[slug]/__tests__/page.test.tsx +3 -15
  65. package/templates/apps/web/app/[locale]/vs/[slug]/page.tsx +0 -10
  66. package/templates/apps/web/app/[locale]/whitepapers/[slug]/page.tsx +1 -14
  67. package/templates/apps/web/src/lib/commerce-claim.ts +15 -6
  68. package/templates/content/_site.mdx +23 -4
  69. package/templates/database/CHANGELOG.md +7 -0
  70. package/templates/database/package.json +1 -1
  71. package/versions.json +34 -34
  72. package/templates/.claude/skills/README.md +0 -37
  73. package/templates/.claude/skills/add-auth/SKILL.md +0 -313
  74. package/templates/.claude/skills/add-auth/references/env-vars.md +0 -106
  75. package/templates/.claude/skills/add-auth/references/oauth-app-setup.md +0 -180
  76. package/templates/.claude/skills/add-page/SKILL.md +0 -260
  77. package/templates/.claude/skills/add-page/references/page-types.md +0 -123
  78. package/templates/.claude/skills/add-page/scripts/site-pages.mjs +0 -87
  79. package/templates/.claude/skills/add-page/templates/comparison-page.mdx.tmpl +0 -99
  80. package/templates/.claude/skills/add-page/templates/content.mdx.tmpl +0 -18
  81. package/templates/.claude/skills/add-page/templates/flow-landing-page.mdx.tmpl +0 -43
  82. package/templates/.claude/skills/add-page/templates/report-page.mdx.tmpl +0 -52
  83. package/templates/.claude/skills/add-page/templates/reports-list.mdx.tmpl +0 -38
  84. package/templates/.claude/skills/check-site/SKILL.md +0 -245
  85. package/templates/.claude/skills/check-site/references/common-errors.md +0 -116
  86. package/templates/.claude/skills/check-site/references/findings-glossary.md +0 -140
  87. package/templates/.claude/skills/check-site/references/severity-levels.md +0 -114
  88. package/templates/.claude/skills/fill-page/SKILL.md +0 -199
  89. package/templates/.claude/skills/fill-page/profiles/plumbing.yaml +0 -80
  90. package/templates/.claude/skills/fill-page/references/section-sources.md +0 -41
  91. package/templates/.claude/skills/setup-database/SKILL.md +0 -156
  92. package/templates/.claude/skills/setup-database/scripts/verify-inbox.ts +0 -154
  93. package/templates/.claude/skills/setup-deploy/SKILL.md +0 -312
  94. package/templates/.claude/skills/setup-deploy/references/runbook-template.md +0 -204
  95. package/templates/.claude/skills/setup-deploy/references/vercel-json-template.md +0 -98
  96. package/templates/.claude/skills/update-page/SKILL.md +0 -117
  97. package/templates/.claude/skills/update-page/scripts/update-page.ts +0 -640
  98. package/templates/.claude/skills/update-page/templates/redirects.mjs.tmpl +0 -31
  99. package/templates/.claude/skills/upgrade-plan/SKILL.md +0 -121
  100. package/templates/.claude/skills/upgrade-plan/references/options.md +0 -24
  101. package/templates/.claude/skills/upgrade-plan/scripts/upgrade-plan.ts +0 -527
  102. package/templates/apps/mcp/__tests__/lib/require-paid-tier.test.ts +0 -75
  103. package/templates/apps/mcp/src/lib/require-paid-tier.ts +0 -44
@@ -14,10 +14,13 @@
14
14
  * endpoint when MCP_PUBLIC_HTTP_URL is configured.
15
15
  * Issue #4157 — `name` resolves from SITE_NAME (env → _site.mdx → neutral
16
16
  * default) instead of a hardcoded vendor-brand literal.
17
+ * Issue #7136 — the five `feature`-tagged tools are gated on the licence
18
+ * features the route passes in (`heldFeatures`), as registration is.
17
19
  */
18
20
 
19
21
  import {
20
22
  getMcpToolCatalog,
23
+ type McpCatalogOptions,
21
24
  type McpToolCatalogEntry,
22
25
  } from "@working-theory/metadata";
23
26
 
@@ -88,6 +91,16 @@ export interface BuildMcpManifestOptions {
88
91
  * claim and passes it in. Omitted ⇒ false, the fail-closed default.
89
92
  */
90
93
  commerceClaimed?: boolean;
94
+ /**
95
+ * The licence features this deployment holds (#7136). In refuse mode a
96
+ * `feature`-tagged tool is advertised only when its feature is held — the
97
+ * same gate the MCP server's registration applies. Injected, like
98
+ * `commerceClaimed`. Omitted ⇒ none, the fail-closed default.
99
+ */
100
+ heldFeatures?: readonly string[];
101
+ /** Overrides `MCP_FEATURE_GATE_MODE` — a test seam; `route.ts` never
102
+ * passes it. */
103
+ featureGate?: McpCatalogOptions["featureGate"];
91
104
  }
92
105
 
93
106
  /** Path the MCP server is mounted at, relative to baseUrl. */
@@ -121,6 +134,8 @@ export function buildMcpManifest(opts: BuildMcpManifestOptions): McpManifest {
121
134
  mcpEndpoint: `${opts.baseUrl}${MCP_ENDPOINT_PATH}`,
122
135
  tools: getMcpToolCatalog({
123
136
  addons: { commerce: opts.commerceClaimed === true },
137
+ features: opts.heldFeatures ?? [],
138
+ ...(opts.featureGate ? { featureGate: opts.featureGate } : {}),
124
139
  }),
125
140
  transport: {
126
141
  type: "http",
@@ -12,7 +12,9 @@
12
12
  * the deployments whose MCP server registers it. Without that, a KEYED site
13
13
  * would register `start_purchase` while hiding it from its own manifest —
14
14
  * the surface disagreement `apps/mcp/__tests__/catalog-drift.test.ts` exists
15
- * to make impossible.
15
+ * to make impossible. The same single read also yields the licence features
16
+ * the deployment holds (#7136), which gate the five `feature`-tagged tools
17
+ * here exactly as they gate registration on the MCP server.
16
18
  *
17
19
  * Mirrors /.well-known/ai-plugin.json conventions: Response.json with a
18
20
  * conservative cache-control header.
@@ -23,7 +25,10 @@
23
25
  */
24
26
 
25
27
  import packageJson from "../../../package.json";
26
- import { resolveCommerceClaim } from "../../../src/lib/commerce-claim";
28
+ import {
29
+ COMMERCE_FEATURE,
30
+ resolveLicenceFeatures,
31
+ } from "../../../src/lib/commerce-claim";
27
32
  import { resolveBaseUrl } from "../../../src/lib/routes";
28
33
 
29
34
  import { buildMcpManifest } from "./build-manifest";
@@ -46,11 +51,13 @@ export async function GET(): Promise<Response> {
46
51
  // (apps/mcp running in HTTP mode). When set, the manifest includes it
47
52
  // as an additional transport descriptor for discovering agents.
48
53
  const mcpHttpUrlEnv = process.env["MCP_PUBLIC_HTTP_URL"];
54
+ const heldFeatures = await resolveLicenceFeatures();
49
55
 
50
56
  const manifest = buildMcpManifest({
51
57
  baseUrl: resolveBaseUrl(),
52
58
  ailkVersion: packageJson.version,
53
- commerceClaimed: await resolveCommerceClaim(),
59
+ commerceClaimed: heldFeatures.includes(COMMERCE_FEATURE),
60
+ heldFeatures,
54
61
  // Spread conditional to satisfy exactOptionalPropertyTypes: omit the key
55
62
  // entirely when the env var is not set rather than passing undefined.
56
63
  ...(mcpHttpUrlEnv ? { mcpHttpUrl: mcpHttpUrlEnv } : {}),
@@ -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) with
3
- // Whitepaper's dynamic-slug generateStaticParams pattern. Unknown slugs 404
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`/`getMdxSlugs` are mocked at the
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 + static params", () => {
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,
@@ -15,19 +15,28 @@
15
15
  * unlike `apps/mcp/src/lib/commerce-claim.ts`'s boot-time memoisation — so a
16
16
  * site that buys commerce after its last deploy starts serving immediately
17
17
  * rather than waiting on a redeploy.
18
+ *
19
+ * {@link resolveLicenceFeatures} (#7136) is the same single read, returning
20
+ * every licence feature the deployment holds: `/.well-known/mcp.json` gates
21
+ * the five `feature`-tagged MCP tools on it, from one call per request.
18
22
  */
19
23
  import { checkLicense } from "@working-theory/license";
20
24
 
21
25
  /** The licence `features[]` claim that turns the commerce add-on on. */
22
- const COMMERCE_FEATURE = "commerce";
26
+ export const COMMERCE_FEATURE = "commerce";
23
27
 
24
- export async function resolveCommerceClaim(): Promise<boolean> {
28
+ /** The licence features this deployment positively holds — `[]` when the
29
+ * licence is revoked, carries no `features` array, or cannot be read. */
30
+ export async function resolveLicenceFeatures(): Promise<readonly string[]> {
25
31
  try {
26
32
  const result = await checkLicense();
27
- return (
28
- result.revoked !== true && result.features.includes(COMMERCE_FEATURE)
29
- );
33
+ if (result.revoked === true) return [];
34
+ return Array.isArray(result.features) ? result.features : [];
30
35
  } catch {
31
- return false;
36
+ return [];
32
37
  }
33
38
  }
39
+
40
+ export async function resolveCommerceClaim(): Promise<boolean> {
41
+ return (await resolveLicenceFeatures()).includes(COMMERCE_FEATURE);
42
+ }