create-ailk 0.8.0 → 0.9.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.
Files changed (211) hide show
  1. package/README.md +68 -0
  2. package/component-catalog.json +279 -45
  3. package/dist/add-module.d.ts +20 -0
  4. package/dist/add-module.js +43 -7
  5. package/dist/add-route.d.ts +56 -0
  6. package/dist/add-route.js +286 -0
  7. package/dist/apply-design-md.d.ts +7 -0
  8. package/dist/apply-design-md.js +50 -0
  9. package/dist/apply-design.d.ts +64 -0
  10. package/dist/apply-design.js +273 -0
  11. package/dist/claude-md.d.ts +111 -0
  12. package/dist/claude-md.js +334 -0
  13. package/dist/cli.js +92 -4
  14. package/dist/derive-architecture.js +41 -36
  15. package/dist/design-compile.d.ts +71 -0
  16. package/dist/design-compile.js +98 -0
  17. package/dist/design-package.d.ts +83 -0
  18. package/dist/design-package.js +468 -0
  19. package/dist/design-presets.d.ts +86 -0
  20. package/dist/design-presets.js +642 -0
  21. package/dist/design-scaffold.d.ts +76 -0
  22. package/dist/design-scaffold.js +220 -0
  23. package/dist/index.d.ts +2 -1
  24. package/dist/index.js +4 -1
  25. package/dist/legacy-nav-renderers.d.ts +45 -0
  26. package/dist/legacy-nav-renderers.js +545 -0
  27. package/dist/lib/auth-secret-file.d.ts +19 -0
  28. package/dist/lib/auth-secret-file.js +112 -0
  29. package/dist/lib/design-checks.d.ts +78 -0
  30. package/dist/lib/design-checks.js +174 -0
  31. package/dist/lib/license-key-files.d.ts +9 -0
  32. package/dist/lib/license-key-files.js +2 -2
  33. package/dist/lib/module-license-gate.d.ts +19 -9
  34. package/dist/lib/module-license-gate.js +19 -8
  35. package/dist/lib/render-verdict.d.ts +52 -0
  36. package/dist/lib/render-verdict.js +77 -0
  37. package/dist/lib/scaffold-render-sweep.d.ts +105 -0
  38. package/dist/lib/scaffold-render-sweep.js +236 -0
  39. package/dist/module-architecture.d.ts +19 -16
  40. package/dist/module-architecture.js +64 -21
  41. package/dist/parse-args.d.ts +12 -2
  42. package/dist/parse-args.js +74 -13
  43. package/dist/programmatic.d.ts +30 -6
  44. package/dist/programmatic.js +206 -17
  45. package/dist/site-chrome.d.ts +19 -9
  46. package/dist/site-chrome.js +23 -13
  47. package/dist/site-manifest.d.ts +25 -0
  48. package/dist/site-manifest.js +254 -0
  49. package/dist/site-roster.d.ts +41 -0
  50. package/dist/site-roster.js +141 -8
  51. package/dist/starter-grid.d.ts +32 -16
  52. package/dist/starter-grid.js +129 -33
  53. package/dist/surfaces.d.ts +41 -9
  54. package/dist/surfaces.js +200 -83
  55. package/dist/template-manifest.d.ts +30 -13
  56. package/dist/template-manifest.js +49 -24
  57. package/package.json +5 -4
  58. package/site-templates/_base/content/en/UseCase/persona-1.mdx +7 -6
  59. package/site-templates/_base/content/en/UseCase/persona-2.mdx +7 -6
  60. package/site-templates/_base/content/en/UseCase/persona-3.mdx +7 -6
  61. package/site-templates/{minimalism-site/TEMPLATE-MANIFEST.json → aliases/minimalism-site.TEMPLATE-MANIFEST.json} +72 -40
  62. package/site-templates/designs/minimalism/DESIGN.md +98 -6
  63. package/site-templates/designs/minimalism/sample/README.md +48 -0
  64. package/site-templates/designs/standard/DESIGN.md +98 -6
  65. package/site-templates/marketing-site/DESIGN.md +98 -6
  66. package/site-templates/marketing-site/TEMPLATE-MANIFEST.json +60 -27
  67. package/site-templates/marketing-site/TEMPLATE-MANIFEST.md +50 -30
  68. package/site-templates/marketing-site-free/DESIGN.md +98 -6
  69. package/site-templates/marketing-site-free/TEMPLATE-MANIFEST.json +60 -24
  70. package/site-templates/marketing-site-free/TEMPLATE-MANIFEST.md +44 -27
  71. package/site-templates/marketing-site-free/ailk-template.json +1 -1
  72. package/site-templates/saas-product/DESIGN.md +98 -6
  73. package/site-templates/shapes/local-business-service/TEMPLATE-MANIFEST.json +127 -18
  74. package/site-templates/shapes/local-business-service/TEMPLATE-MANIFEST.md +74 -20
  75. package/site-templates/shapes/local-business-service/ailk-template.json +1 -1
  76. package/site-templates/shapes/organization-service/TEMPLATE-MANIFEST.json +127 -18
  77. package/site-templates/shapes/organization-service/TEMPLATE-MANIFEST.md +74 -20
  78. package/site-templates/shapes/organization-service/ailk-template.json +1 -1
  79. package/site-templates/shapes/person-service/TEMPLATE-MANIFEST.json +127 -18
  80. package/site-templates/shapes/person-service/TEMPLATE-MANIFEST.md +75 -21
  81. package/site-templates/shapes/person-service/ailk-template.json +1 -1
  82. package/site-templates/shapes/person-service/content/en/AboutPage/about.mdx +1 -1
  83. package/site-templates/shapes/person-service/content/en/HomePage/home.mdx +2 -2
  84. package/site-templates/shapes/person-service/content/en/PortfolioList/portfolio.mdx +4 -2
  85. package/site-templates/shapes/person-service/content/en/ServicesList/services.mdx +1 -1
  86. package/templates/.claude/rules/architecture.md +7 -0
  87. package/templates/.claude/skills/README.md +3 -2
  88. package/templates/.claude/skills/add-auth/references/env-vars.md +22 -15
  89. package/templates/.claude/skills/add-auth/references/oauth-app-setup.md +3 -2
  90. package/templates/.claude/skills/add-page/SKILL.md +241 -238
  91. package/templates/.claude/skills/add-page/references/page-types.md +85 -76
  92. package/templates/.claude/skills/add-page/scripts/site-pages.mjs +87 -0
  93. package/templates/.claude/skills/add-page/templates/comparison-page.mdx.tmpl +99 -0
  94. package/templates/.claude/skills/add-page/templates/content.mdx.tmpl +8 -18
  95. package/templates/.claude/skills/add-page/templates/flow-landing-page.mdx.tmpl +43 -0
  96. package/templates/.claude/skills/add-page/templates/report-page.mdx.tmpl +52 -0
  97. package/templates/.claude/skills/add-page/templates/reports-list.mdx.tmpl +38 -0
  98. package/templates/.claude/skills/check-site/SKILL.md +23 -0
  99. package/templates/.claude/skills/fill-page/SKILL.md +26 -7
  100. package/templates/.claude/skills/fill-page/profiles/plumbing.yaml +1 -1
  101. package/templates/.claude/skills/setup-deploy/references/env-vars-template.md +28 -11
  102. package/templates/.claude/skills/update-page/SKILL.md +117 -0
  103. package/templates/.claude/skills/update-page/scripts/update-page.ts +640 -0
  104. package/templates/.claude/skills/update-page/templates/redirects.mjs.tmpl +31 -0
  105. package/templates/.claude/skills/upgrade-plan/SKILL.md +12 -8
  106. package/templates/.github/workflows/ci.yml +4 -0
  107. package/templates/.prettierignore +1 -0
  108. package/templates/CLAUDE.md +146 -103
  109. package/templates/DESIGN.md +98 -6
  110. package/templates/README.md +50 -38
  111. package/templates/apps/api/.env.example +2 -1
  112. package/templates/apps/api/src/__tests__/commerce-claim-registration.test.ts +2 -0
  113. package/templates/apps/api/src/__tests__/server.test.ts +116 -0
  114. package/templates/apps/api/src/lib/stripe.ts +9 -0
  115. package/templates/apps/api/src/middleware/__tests__/auth.optional-cookie.test.ts +140 -0
  116. package/templates/apps/api/src/middleware/auth.ts +54 -0
  117. package/templates/apps/api/src/routes/checkout/__tests__/session.test.ts +5 -2
  118. package/templates/apps/api/src/routes/checkout/__tests__/sessions.test.ts +628 -0
  119. package/templates/apps/api/src/routes/checkout/sessions.ts +222 -24
  120. package/templates/apps/api/src/routes/webhooks/__tests__/stripe-flow-checkout.test.ts +1 -1
  121. package/templates/apps/api/src/routes/webhooks/__tests__/stripe-org-billing.test.ts +5 -1
  122. package/templates/apps/api/src/routes/webhooks/__tests__/stripe.test.ts +521 -20
  123. package/templates/apps/api/src/routes/webhooks/stripe.ts +124 -32
  124. package/templates/apps/api/src/server.ts +28 -0
  125. package/templates/apps/api/src/services/__tests__/revalidation.test.ts +90 -15
  126. package/templates/apps/api/src/services/checkout-buyer.ts +72 -0
  127. package/templates/apps/api/src/services/revalidation.ts +60 -15
  128. package/templates/apps/mcp/__tests__/tools/create_project.test.ts +30 -24
  129. package/templates/apps/mcp/src/tools/purchase.ts +4 -2
  130. package/templates/apps/web/.env.example +13 -0
  131. package/templates/apps/web/CLAUDE.md +22 -28
  132. package/templates/apps/web/app/[locale]/(auth)/layout.tsx +1 -1
  133. package/templates/apps/web/app/[locale]/(authed)/leads/__tests__/page.test.tsx +24 -0
  134. package/templates/apps/web/app/[locale]/(authed)/leads/page.tsx +17 -6
  135. package/templates/apps/web/app/[locale]/[slug]/[detail]/__tests__/page.test.tsx +110 -0
  136. package/templates/apps/web/app/[locale]/[slug]/[detail]/page.tsx +60 -0
  137. package/templates/apps/web/app/[locale]/[slug]/page.tsx +1 -1
  138. package/templates/apps/web/app/[locale]/alternatives/__tests__/page.test.tsx +161 -0
  139. package/templates/apps/web/app/[locale]/alternatives/page.tsx +104 -0
  140. package/templates/apps/web/app/[locale]/{use-cases → for}/[slug]/page.tsx +4 -3
  141. package/templates/apps/web/app/[locale]/for/__tests__/page-renderer-migration.test.tsx +73 -0
  142. package/templates/apps/web/app/[locale]/{use-cases → for}/page.tsx +8 -7
  143. package/templates/apps/web/app/[locale]/glossary/page.tsx +1 -1
  144. package/templates/apps/web/app/[locale]/layout.tsx +35 -19
  145. package/templates/apps/web/app/[locale]/newsletter/page.tsx +1 -1
  146. package/templates/apps/web/app/[locale]/portfolio/[slug]/page.tsx +1 -1
  147. package/templates/apps/web/app/[locale]/portfolio/page.tsx +1 -1
  148. package/templates/apps/web/app/[locale]/vs/[slug]/__tests__/page.test.tsx +112 -0
  149. package/templates/apps/web/app/[locale]/vs/[slug]/page.tsx +70 -0
  150. package/templates/apps/web/app/api/revalidate/__tests__/route.test.ts +236 -44
  151. package/templates/apps/web/app/api/revalidate/route.ts +78 -36
  152. package/templates/apps/web/lib/__tests__/site-brand.test.ts +79 -0
  153. package/templates/apps/web/lib/__tests__/site-theme.test.ts +104 -11
  154. package/templates/apps/web/lib/__tests__/structured-data.test.tsx +21 -4
  155. package/templates/apps/web/lib/locale-guard.ts +18 -0
  156. package/templates/apps/web/lib/meet-placeholder.ts +5 -0
  157. package/templates/apps/web/lib/nav-config.ts +14 -13
  158. package/templates/apps/web/lib/site-brand.tsx +13 -9
  159. package/templates/apps/web/lib/site-logo.tsx +36 -0
  160. package/templates/apps/web/lib/site-theme.ts +46 -26
  161. package/templates/apps/web/messages/en.json +23 -9
  162. package/templates/apps/web/middleware.ts +28 -4
  163. package/templates/content/en/BookCall/book-a-call.mdx +80 -0
  164. package/templates/content/en/BookCall/onsite-estimate.mdx +56 -0
  165. package/templates/content/en/BookCall/onsite-service.mdx +56 -0
  166. package/templates/content/en/BookCall/start-project.mdx +56 -0
  167. package/templates/content/en/ContactPage/contact.mdx +40 -1
  168. package/templates/content/en/HomePage/home.mdx +2 -6
  169. package/templates/content/en/LandingPage/meet.mdx +59 -0
  170. package/templates/content/en/UseCasesList/{use-cases.mdx → for.mdx} +19 -18
  171. package/templates/database/CHANGELOG.md +7 -0
  172. package/templates/database/package.json +1 -1
  173. package/templates/package.json +2 -1
  174. package/templates/tools/check-claude-md.ts +128 -0
  175. package/versions.json +33 -33
  176. package/site-templates/_base/content/en/BookCall/book-call.mdx +0 -52
  177. package/site-templates/minimalism-site/DESIGN.md +0 -162
  178. package/site-templates/minimalism-site/TEMPLATE-MANIFEST.md +0 -458
  179. package/site-templates/minimalism-site/ailk-template.json +0 -23
  180. package/site-templates/minimalism-site/content/en/AboutPage/about.mdx +0 -110
  181. package/site-templates/minimalism-site/content/en/BookCall/book-call.mdx +0 -52
  182. package/site-templates/minimalism-site/content/en/ContactPage/contact.mdx +0 -34
  183. package/site-templates/minimalism-site/content/en/FAQPage/faq.mdx +0 -57
  184. package/site-templates/minimalism-site/content/en/LandingPage/referral.mdx +0 -77
  185. package/site-templates/minimalism-site/content/en/LandingPage/reports.mdx +0 -73
  186. package/site-templates/minimalism-site/content/en/PurchasePolicyPage/purchase-policy.mdx +0 -123
  187. package/site-templates/minimalism-site/content/en/ReportPage/annual-letter-2024.mdx +0 -52
  188. package/site-templates/minimalism-site/content/en/ReportPage/field-notes-2023.mdx +0 -52
  189. package/site-templates/minimalism-site/content/en/ReportPage/platform-shift-2025.mdx +0 -52
  190. package/site-templates/minimalism-site/content/en/ReportPage/state-of-the-market-2026.mdx +0 -52
  191. package/site-templates/minimalism-site/content/en/Service/first-engagement.mdx +0 -38
  192. package/site-templates/minimalism-site/content/en/Service/second-engagement.mdx +0 -38
  193. package/site-templates/minimalism-site/content/en/Service/third-engagement.mdx +0 -38
  194. package/site-templates/minimalism-site/content/en/ServicesList/services.mdx +0 -73
  195. package/site-templates/minimalism-site/flows/referral.flow.json +0 -71
  196. package/site-templates/minimalism-site/provenance.json +0 -4
  197. package/templates/.claude/skills/add-page/templates/route.tsx.tmpl +0 -28
  198. package/templates/apps/web/app/[locale]/book-a-call/page.tsx +0 -50
  199. package/templates/apps/web/app/[locale]/use-cases/__tests__/page-renderer-migration.test.tsx +0 -72
  200. /package/site-templates/{minimalism-site → designs/minimalism/sample}/content/en/HomePage/home.mdx +0 -0
  201. /package/site-templates/{minimalism-site/assets → designs/minimalism/sample/public}/avatar-placeholder-1.svg +0 -0
  202. /package/site-templates/{minimalism-site/assets → designs/minimalism/sample/public}/avatar-placeholder-2.svg +0 -0
  203. /package/site-templates/{minimalism-site/assets → designs/minimalism/sample/public}/avatar-placeholder-3.svg +0 -0
  204. /package/site-templates/{minimalism-site/assets → designs/minimalism/sample/public}/avatar-placeholder-4.svg +0 -0
  205. /package/site-templates/{minimalism-site/assets → designs/minimalism/sample/public}/avatar-placeholder-5.svg +0 -0
  206. /package/site-templates/{minimalism-site/assets → designs/minimalism/sample/public}/avatar-placeholder-6.svg +0 -0
  207. /package/site-templates/{minimalism-site/assets → designs/minimalism/sample/public}/hero-backdrop.svg +0 -0
  208. /package/site-templates/{minimalism-site/assets → designs/minimalism/sample/public}/logo-placeholder-1.svg +0 -0
  209. /package/site-templates/{minimalism-site/assets → designs/minimalism/sample/public}/logo-placeholder-2.svg +0 -0
  210. /package/site-templates/{minimalism-site/assets → designs/minimalism/sample/public}/logo-placeholder-3.svg +0 -0
  211. /package/site-templates/{minimalism-site/assets → designs/minimalism/sample/public}/wordmark-placeholder.svg +0 -0
@@ -1,38 +0,0 @@
1
- ---
2
- type: "Service"
3
- slug: "second-engagement"
4
- title: "{Second engagement}"
5
- description: "{One line on what this engagement is and who it suits. Replace before you launch.}"
6
- schema:
7
- serviceType: "{Second engagement}"
8
- provider:
9
- "@type": Organization
10
- name: "Your Company"
11
- ---
12
-
13
- # {Second engagement}
14
-
15
- {Open with the situation a reader is in when they come looking for this. Two or
16
- three sentences, no more — the detail belongs under the headings below.}
17
-
18
- ## What it covers
19
-
20
- - {The first thing you actually do}
21
- - {The second thing you actually do}
22
- - {The third thing you actually do}
23
-
24
- ## How it runs
25
-
26
- {One paragraph on the shape of the engagement: how it starts, how long it runs,
27
- who is involved on both sides, and what a week looks like.}
28
-
29
- ## What you end up with
30
-
31
- {One paragraph on the deliverable. Be concrete — name the artefact, not the
32
- feeling.}
33
-
34
- ## What it costs
35
-
36
- {One line on the pricing shape — fixed fee, retainer, day rate — and what it
37
- excludes. Replace with your real numbers, or delete this section and let the
38
- call do it.}
@@ -1,38 +0,0 @@
1
- ---
2
- type: "Service"
3
- slug: "third-engagement"
4
- title: "{Third engagement}"
5
- description: "{One line on what this engagement is and who it suits. Replace before you launch.}"
6
- schema:
7
- serviceType: "{Third engagement}"
8
- provider:
9
- "@type": Organization
10
- name: "Your Company"
11
- ---
12
-
13
- # {Third engagement}
14
-
15
- {Open with the situation a reader is in when they come looking for this. Two or
16
- three sentences, no more — the detail belongs under the headings below.}
17
-
18
- ## What it covers
19
-
20
- - {The first thing you actually do}
21
- - {The second thing you actually do}
22
- - {The third thing you actually do}
23
-
24
- ## How it runs
25
-
26
- {One paragraph on the shape of the engagement: how it starts, how long it runs,
27
- who is involved on both sides, and what a week looks like.}
28
-
29
- ## What you end up with
30
-
31
- {One paragraph on the deliverable. Be concrete — name the artefact, not the
32
- feeling.}
33
-
34
- ## What it costs
35
-
36
- {One line on the pricing shape — fixed fee, retainer, day rate — and what it
37
- excludes. Replace with your real numbers, or delete this section and let the
38
- call do it.}
@@ -1,73 +0,0 @@
1
- ---
2
- type: "ServicesList"
3
- slug: "services"
4
- title: "Services"
5
- description: "The three engagements {your company} offers. Placeholder copy — replace before you launch."
6
- sections:
7
- [
8
- {
9
- "id": "hero",
10
- "type": "hero",
11
- "data":
12
- {
13
- "variant": "centered",
14
- "eyebrow": "Services",
15
- "headline": "Three ways to work together",
16
- "subheadline": "{One line on how to choose between them. Name the question a reader is actually asking.}",
17
- },
18
- },
19
- {
20
- "id": "engagements",
21
- "type": "items",
22
- "data":
23
- {
24
- "headline": "Engagements",
25
- "columns": 3,
26
- "items":
27
- [
28
- {
29
- "title": "{First engagement}",
30
- "description": "{One line on what it is and who it suits.}",
31
- "link":
32
- {
33
- "label": "Read more",
34
- "href": "/services/first-engagement",
35
- },
36
- },
37
- {
38
- "title": "{Second engagement}",
39
- "description": "{One line on what it is and who it suits.}",
40
- "link":
41
- {
42
- "label": "Read more",
43
- "href": "/services/second-engagement",
44
- },
45
- },
46
- {
47
- "title": "{Third engagement}",
48
- "description": "{One line on what it is and who it suits.}",
49
- "link":
50
- {
51
- "label": "Read more",
52
- "href": "/services/third-engagement",
53
- },
54
- },
55
- ],
56
- },
57
- },
58
- {
59
- "id": "closing",
60
- "type": "cta",
61
- "data":
62
- {
63
- "variant": "minimal",
64
- "headline": "Not sure which one?",
65
- "description": "{One line inviting the reader to book the call instead of guessing.}",
66
- "primaryCta": { "label": "Hire me →", "href": "/meet" },
67
- },
68
- },
69
- ]
70
- ---
71
-
72
- {/_ Placeholder copy. The three detail pages under `content/en/Service/` carry
73
- the long form; this page is the chooser. _/}
@@ -1,71 +0,0 @@
1
- {
2
- "slug": "referral",
3
- "defaultLocale": "en",
4
- "steps": [
5
- {
6
- "key": "introduction",
7
- "type": "field",
8
- "copy": {
9
- "title": {
10
- "en": "Who should we meet?"
11
- }
12
- },
13
- "fields": [
14
- {
15
- "name": "referrer_name",
16
- "inputType": "text",
17
- "required": true,
18
- "mapsTo": "name",
19
- "copy": {
20
- "label": {
21
- "en": "Your name"
22
- },
23
- "help": {
24
- "en": "So we can tell them who sent us."
25
- }
26
- }
27
- },
28
- {
29
- "name": "referrer_email",
30
- "inputType": "email",
31
- "required": true,
32
- "mapsTo": "email",
33
- "copy": {
34
- "label": {
35
- "en": "Your email"
36
- },
37
- "help": {
38
- "en": "Where we tell you how it went."
39
- }
40
- }
41
- },
42
- {
43
- "name": "referral_details",
44
- "inputType": "textarea",
45
- "required": true,
46
- "minLength": 20,
47
- "copy": {
48
- "label": {
49
- "en": "Who are they, and why them?"
50
- },
51
- "placeholder": {
52
- "en": "Their name, a way to reach them, and one line on why you thought of them."
53
- }
54
- }
55
- }
56
- ]
57
- },
58
- {
59
- "key": "sent",
60
- "type": "message",
61
- "copy": {
62
- "title": {
63
- "en": "Thank you — the introduction is in"
64
- },
65
- "body": {
66
- "en": "We will reach out within a few days and let you know either way. Placeholder copy: replace it with what you actually do."
67
- }
68
- }
69
- }
70
- ]
71
- }
@@ -1,4 +0,0 @@
1
- {
2
- "template": "minimalism-site",
3
- "requires": ["marketing", "scheduling", "signal-collectors"]
4
- }
@@ -1,28 +0,0 @@
1
- // Emitted by `add-page` at `app/{{SLUG}}/page.tsx`. Substitutions:
2
- // {{SLUG}} — page slug (e.g., "pricing")
3
- // {{TYPE}} — schema.org @type (e.g., "Service")
4
- // {{BUILDER}} — typed builder name (e.g., "buildService")
5
- // {{TITLE}} — page title (e.g., "Pricing")
6
- import { notFound } from "next/navigation";
7
-
8
- import { JsonLd } from "@working-theory/jsonld";
9
- import { {{BUILDER}} } from "@working-theory/schema";
10
- import { mdxAdapter } from "@/lib/content";
11
-
12
- export default async function {{TYPE}}Page() {
13
- const page = await mdxAdapter.getPage("{{SLUG}}");
14
- if (!page) return notFound();
15
-
16
- const schema = {{BUILDER}}(page);
17
-
18
- return (
19
- <main className="mx-auto max-w-3xl px-6 py-12">
20
- <JsonLd schema={schema} />
21
- <article className="prose dark:prose-invert">
22
- <h1>{page.title}</h1>
23
- {page.description ? <p className="lead">{page.description}</p> : null}
24
- {/* Render MDX body via your project's MDX renderer of choice. */}
25
- </article>
26
- </main>
27
- );
28
- }
@@ -1,50 +0,0 @@
1
- // Book-a-call page (#6658 D5) — a BookCall at /book-a-call, reading the
2
- // fixed slug `book-call` (the monorepo's own content already uses it). Its
3
- // CTA points at /contact, or at /meet where a template carries scheduling. A
4
- // module-less surface a template enables by naming it in its roster; with no
5
- // content the route 404s. Mirrors use-cases/page.tsx: a sections-composed page
6
- // renders its sections, a prose-only page its MDX body.
7
- import type { getMdxSource as defaultGetMdxSource } from "@working-theory/page-renderer";
8
- import {
9
- getMdxSource as loadMdxSource,
10
- renderPage,
11
- renderPageMetadata,
12
- } from "@working-theory/page-renderer";
13
- import type { Metadata } from "next";
14
- import type { ReactElement } from "react";
15
-
16
- interface Props {
17
- params: Promise<{ locale: string }>;
18
- getMdxSource?: typeof defaultGetMdxSource;
19
- }
20
-
21
- export async function generateMetadata({
22
- params,
23
- }: Pick<Props, "params">): Promise<Metadata> {
24
- const { locale } = await params;
25
- return renderPageMetadata({
26
- type: "BookCall",
27
- slug: "book-call",
28
- locale,
29
- });
30
- }
31
-
32
- export default async function Page({
33
- params,
34
- getMdxSource,
35
- }: Props): Promise<ReactElement> {
36
- const { locale } = await params;
37
- const source = await (getMdxSource ?? loadMdxSource)("BookCall", "book-call", locale);
38
- const composed =
39
- source !== null &&
40
- Array.isArray((source.frontmatter as { sections?: unknown[] }).sections) &&
41
- ((source.frontmatter as { sections?: unknown[] }).sections?.length ?? 0) >
42
- 0;
43
- return renderPage({
44
- type: "BookCall",
45
- slug: "book-call",
46
- locale,
47
- ...(composed ? {} : { bodyMode: "mdx-body" as const }),
48
- ...(getMdxSource !== undefined && { getMdxSource }),
49
- });
50
- }
@@ -1,72 +0,0 @@
1
- /**
2
- * HTML snapshot regression tests for use-cases/page.tsx → @working-theory/page-renderer migration.
3
- *
4
- * Two-commit strategy:
5
- * Commit 1: this file added; snapshots captured against pre-migration route (RED-passing).
6
- * Commit 2: route migrated; same snapshots verify diff=0 (GREEN).
7
- *
8
- * Two states tested:
9
- * - empty: getMdxSource returns null → empty-state branch
10
- * - populated: getMdxSource returns a minimal MdxPageSource → full render
11
- *
12
- * Failure = snapshot diff > 0 bytes. DO NOT regenerate snapshots to pass.
13
- */
14
- import React from "react";
15
- import { render } from "@testing-library/react";
16
-
17
- // ── @working-theory/ui mock ─────────────────────────────────────────────────────────────
18
- jest.mock("@working-theory/ui", () => ({
19
- // #4446/#4447: renderPage's own fallback/empty-state title now composes
20
- // Heading (mobile type-budget classes, #4417) instead of a bare <h1>.
21
- Heading: ({
22
- children,
23
- level,
24
- }: {
25
- children: React.ReactNode;
26
- level: number;
27
- }): React.ReactElement => React.createElement(`h${level}`, null, children),
28
- SectionRenderer: ({ sections }: { sections: unknown[] }) =>
29
- React.createElement("div", {
30
- "data-testid": "section-renderer",
31
- "data-section-count": String(sections.length),
32
- }),
33
- }));
34
-
35
- import UseCasesPage from "../page";
36
-
37
- const getMdxSourceEmpty = jest.fn().mockResolvedValue(null);
38
-
39
- describe("UseCasesPage — snapshot regression", () => {
40
- it("empty state renders deterministic HTML", async () => {
41
- const element = await UseCasesPage({
42
- params: Promise.resolve({ locale: "en" }),
43
- getMdxSource: getMdxSourceEmpty,
44
- });
45
- const { container } = render(element);
46
- expect(container.innerHTML).toMatchInlineSnapshot(
47
- `"<main id="main"><h1>Use Cases</h1><p data-testid="section-empty-state">Use cases listing coming soon.</p></main>"`,
48
- );
49
- });
50
-
51
- it("populated state renders deterministic HTML", async () => {
52
- const source = {
53
- frontmatter: {
54
- type: "UseCasesList" as const,
55
- slug: "use-cases",
56
- title: "Use Cases",
57
- description: "How teams use our platform.",
58
- },
59
- body: "",
60
- };
61
- const getMdxSourcePopulated = jest.fn().mockResolvedValue(source);
62
-
63
- const element = await UseCasesPage({
64
- params: Promise.resolve({ locale: "en" }),
65
- getMdxSource: getMdxSourcePopulated,
66
- });
67
- const { container } = render(element);
68
- expect(container.innerHTML).toMatchInlineSnapshot(
69
- `"<script type="application/ld+json" data-testid="json-ld">{"@context":"https://schema.org","@type":"CollectionPage","inLanguage":"en","name":"Use Cases","description":"How teams use our platform.","mainEntity":{"@type":"ItemList","itemListElement":[{"@type":"Article"}]}}</script><main id="main"><h1>Use Cases</h1><p data-testid="use-cases-description">How teams use our platform.</p><div data-testid="markdown-view-toggle" class="ailk-markdown-view-toggle"><div class="flex justify-end mb-3"><div role="radiogroup" aria-label="Page view" data-testid="markdown-view-toggle" class="inline-flex items-center gap-0.5 rounded-lg p-1 bg-(--color-surface-secondary)"><button class="inline-flex items-center justify-center gap-2 font-medium transition-colors focus-visible:outline-none focus-visible:ring-2 focus-visible:ring-(--color-accent) focus-visible:ring-offset-1 disabled:cursor-not-allowed disabled:opacity-50 [&amp;>svg]:shrink-0 text-(--color-fg) hover:bg-(--color-surface-secondary) active:bg-gray-200 rounded-control px-control-x h-(--control-height-thin) text-caption leading-none rounded-md px-3 text-body-sm font-medium transition-colors focus-visible:outline-none focus-visible:ring-2 focus-visible:ring-(--color-accent) focus-visible:ring-offset-1 min-w-(--control-height-thin) min-h-(--control-height-big) bg-(--color-bg) text-(--color-fg) shadow-sm" type="button" role="radio" aria-checked="true" aria-label="Rendered" tabindex="0" data-testid="markdown-view-toggle-rendered">Rendered</button><button class="inline-flex items-center justify-center gap-2 font-medium transition-colors focus-visible:outline-none focus-visible:ring-2 focus-visible:ring-(--color-accent) focus-visible:ring-offset-1 disabled:cursor-not-allowed disabled:opacity-50 [&amp;>svg]:shrink-0 text-(--color-fg) hover:bg-(--color-surface-secondary) active:bg-gray-200 rounded-control px-control-x h-(--control-height-thin) text-caption leading-none rounded-md px-3 text-body-sm font-medium transition-colors focus-visible:outline-none focus-visible:ring-2 focus-visible:ring-(--color-accent) focus-visible:ring-offset-1 min-w-(--control-height-thin) min-h-(--control-height-big) text-(--color-text-muted) hover:text-(--color-fg)" type="button" role="radio" aria-checked="false" aria-label="Markdown" tabindex="-1" data-testid="markdown-view-toggle-markdown">Markdown</button></div></div><div role="status" aria-live="polite" aria-atomic="true" class="sr-only" data-testid="markdown-view-toggle-status">Showing Rendered view</div><div data-testid="markdown-view-rendered"><article data-testid="mdx-body"><div data-testid="mdx-remote" data-source=""></div></article></div></div></main>"`,
70
- );
71
- });
72
- });