create-ailk 0.1.0 → 0.2.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +124 -3
- package/component-catalog.md +456 -0
- package/dist/cli.js +92 -16
- package/dist/component-catalog.d.ts +11 -0
- package/dist/component-catalog.js +39 -0
- package/dist/copy.d.ts +7 -0
- package/dist/copy.js +24 -0
- package/dist/derive-architecture.d.ts +13 -0
- package/dist/derive-architecture.js +238 -0
- package/dist/index.d.ts +7 -2
- package/dist/index.js +19 -2
- package/dist/lib/extract-module-bundle.d.ts +16 -0
- package/dist/lib/extract-module-bundle.js +184 -0
- package/dist/lib/fetch-module.d.ts +55 -0
- package/dist/lib/fetch-module.js +125 -0
- package/dist/lib/module-license-gate.d.ts +48 -0
- package/dist/lib/module-license-gate.js +67 -0
- package/dist/lib/module-tier.d.ts +35 -0
- package/dist/lib/module-tier.js +37 -0
- package/dist/module-architecture.d.ts +175 -0
- package/dist/module-architecture.js +663 -0
- package/dist/parse-args.d.ts +47 -1
- package/dist/parse-args.js +143 -2
- package/dist/programmatic.d.ts +160 -3
- package/dist/programmatic.js +196 -7
- package/dist/surfaces.d.ts +211 -0
- package/dist/surfaces.js +532 -0
- package/dist/sync-routes.d.ts +92 -0
- package/dist/sync-routes.js +350 -0
- package/package.json +12 -5
- package/templates/.claude/rules/architecture.md +6 -2
- package/templates/.claude/skills/README.md +9 -9
- package/templates/.claude/skills/add-schema/SKILL.md +14 -7
- package/templates/.claude/skills/add-schema/references/schema-types.md +109 -6
- package/templates/.claude/skills/provision-config/SKILL.md +1 -1
- package/templates/.claude/skills/scaffold-commerce/SKILL.md +498 -1
- package/templates/.claude/skills/scaffold-commerce/templates/checkout-route.template.ts +370 -0
- package/templates/.claude/skills/scaffold-commerce/templates/invoice-route.template.ts +376 -0
- package/templates/.claude/skills/scaffold-commerce/templates/payment-link-route.template.ts +471 -0
- package/templates/.claude/skills/scaffold-commerce/templates/portal-route.template.ts +365 -0
- package/templates/.claude/skills/scaffold-commerce/templates/stripe-config.template.ts +60 -0
- package/templates/.claude/skills/scaffold-commerce/templates/subscription-route.template.ts +446 -0
- package/templates/.claude/skills/suggest-site-pages/references/categories.md +4 -4
- package/templates/.env.example +126 -2
- package/templates/CLAUDE.md +11 -5
- package/templates/README.md +1 -1
- package/templates/apps/api/.env.example +38 -0
- package/templates/apps/api/CLAUDE.md +78 -1
- package/templates/apps/api/LICENSE +201 -0
- package/templates/apps/api/api/index.ts +6 -141
- package/templates/apps/api/jest.config.cjs +16 -0
- package/templates/apps/api/package.json +22 -6
- package/templates/apps/api/src/__tests__/module-exclusion.test.ts +141 -0
- package/templates/apps/api/src/__tests__/server.test.ts +46 -0
- package/templates/apps/api/src/__tests__/vercel-handler.test.ts +211 -0
- package/templates/apps/api/src/bin/abandon-sweep.ts +42 -0
- package/templates/apps/api/src/bin/deliverable-resend.ts +144 -0
- package/templates/apps/api/src/bin/deliverable-upload.ts +114 -0
- package/templates/apps/api/src/bin/followup-sweep.ts +44 -0
- package/templates/apps/api/src/bin/listing-csv-sweep.ts +46 -0
- package/templates/apps/api/src/bin/seed-presets.ts +58 -0
- package/templates/apps/api/src/bin/seed-waitlist-experiments.ts +104 -0
- package/templates/apps/api/src/bin/session-retention-sweep.ts +46 -0
- package/templates/apps/api/src/cli.ts +5 -11
- package/templates/apps/api/src/config/index.ts +35 -0
- package/templates/apps/api/src/config/modules.ts +43 -0
- package/templates/apps/api/src/lib/__mocks__/prisma-client.js +11 -0
- package/templates/apps/api/src/lib/__mocks__/prisma.ts +241 -0
- package/templates/apps/api/src/lib/__tests__/lead-capture-store.test.ts +2 -2
- package/templates/apps/api/src/lib/site.ts +16 -0
- package/templates/apps/api/src/lib/stripe.ts +22 -0
- package/templates/apps/api/src/lib/tenant-db.ts +225 -0
- package/templates/apps/api/src/lib/ws-token.ts +91 -0
- package/templates/apps/api/src/middleware/__tests__/adversarial/session-fixation.test.ts +7 -1
- package/templates/apps/api/src/middleware/__tests__/auth.cookie-path.test.ts +7 -1
- package/templates/apps/api/src/middleware/__tests__/rate-limit.test.ts +91 -4
- package/templates/apps/api/src/middleware/auth.ts +43 -8
- package/templates/apps/api/src/middleware/rate-limit.ts +78 -3
- package/templates/apps/api/src/middleware/tenant.ts +99 -0
- package/templates/apps/api/src/openapi/__tests__/openapi.test.ts +92 -0
- package/templates/apps/api/src/openapi/spec.ts +312 -3
- package/templates/apps/api/src/routes/aeo/__tests__/index.test.ts +175 -0
- package/templates/apps/api/src/routes/aeo/index.ts +107 -0
- package/templates/apps/api/src/routes/auth/__tests__/auth-tokens.functional.test.ts +5 -2
- package/templates/apps/api/src/routes/auth/__tests__/tokens.test.ts +22 -3
- package/templates/apps/api/src/routes/auth/tokens.ts +13 -1
- package/templates/apps/api/src/routes/billing/__tests__/portal.test.ts +450 -0
- package/templates/apps/api/src/routes/billing/__tests__/read.test.ts +262 -0
- package/templates/apps/api/src/routes/billing/__tests__/usage.test.ts +436 -0
- package/templates/apps/api/src/routes/billing/index.ts +36 -0
- package/templates/apps/api/src/routes/billing/portal.ts +182 -0
- package/templates/apps/api/src/routes/billing/read.ts +75 -0
- package/templates/apps/api/src/routes/billing/usage.ts +153 -0
- package/templates/apps/api/src/routes/checkout/__tests__/sessions.test.ts +99 -0
- package/templates/apps/api/src/routes/checkout/sessions.ts +20 -8
- package/templates/apps/api/src/routes/content/create.ts +1 -0
- package/templates/apps/api/src/routes/content/delete.ts +1 -0
- package/templates/apps/api/src/routes/content/index.ts +42 -6
- package/templates/apps/api/src/routes/content/update.ts +1 -0
- package/templates/apps/api/src/routes/deliverables/__tests__/index.test.ts +393 -0
- package/templates/apps/api/src/routes/deliverables/index.ts +200 -0
- package/templates/apps/api/src/routes/flow-checkouts/__tests__/index.test.ts +443 -0
- package/templates/apps/api/src/routes/flow-checkouts/index.ts +82 -0
- package/templates/apps/api/src/routes/flows/README.md +147 -0
- package/templates/apps/api/src/routes/flows/__tests__/index.test.ts +752 -0
- package/templates/apps/api/src/routes/flows/__tests__/recommender.test.ts +671 -0
- package/templates/apps/api/src/routes/flows/index.ts +202 -0
- package/templates/apps/api/src/routes/flows/recommender.ts +443 -0
- package/templates/apps/api/src/routes/leads/__tests__/index.test.ts +438 -31
- package/templates/apps/api/src/routes/leads/__tests__/track.test.ts +10 -10
- package/templates/apps/api/src/routes/leads/index.ts +43 -9
- package/templates/apps/api/src/routes/project-listings/__tests__/configured-application.test.ts +558 -0
- package/templates/apps/api/src/routes/project-listings/__tests__/copy-edit.test.ts +815 -0
- package/templates/apps/api/src/routes/project-listings/__tests__/drafts.test.ts +869 -0
- package/templates/apps/api/src/routes/project-listings/__tests__/me-route-precedence.test.ts +221 -0
- package/templates/apps/api/src/routes/project-listings/__tests__/me.test.ts +468 -0
- package/templates/apps/api/src/routes/project-listings/__tests__/public.test.ts +424 -0
- package/templates/apps/api/src/routes/project-listings/__tests__/site-answers.test.ts +707 -0
- package/templates/apps/api/src/routes/project-listings/__tests__/site-key.test.ts +375 -0
- package/templates/apps/api/src/routes/project-listings/__tests__/structured-address.test.ts +467 -0
- package/templates/apps/api/src/routes/project-listings/__tests__/tenant-isolation.test.ts +566 -0
- package/templates/apps/api/src/routes/project-listings/get.ts +70 -0
- package/templates/apps/api/src/routes/project-listings/index.ts +120 -0
- package/templates/apps/api/src/routes/project-listings/list.ts +47 -0
- package/templates/apps/api/src/routes/project-listings/me.ts +76 -0
- package/templates/apps/api/src/routes/project-listings/patch-copy.ts +222 -0
- package/templates/apps/api/src/routes/project-listings/patch-draft.ts +97 -0
- package/templates/apps/api/src/routes/project-listings/patch.ts +136 -0
- package/templates/apps/api/src/routes/project-listings/public.ts +52 -0
- package/templates/apps/api/src/routes/project-listings/respond.ts +37 -0
- package/templates/apps/api/src/routes/project-listings/resume-token.ts +22 -0
- package/templates/apps/api/src/routes/project-listings/start.ts +106 -0
- package/templates/apps/api/src/routes/project-listings/submit.ts +122 -0
- package/templates/apps/api/src/routes/schedule/__tests__/index.test.ts +490 -0
- package/templates/apps/api/src/routes/schedule/index.ts +249 -0
- package/templates/apps/api/src/routes/slack/__tests__/actions.test.ts +385 -0
- package/templates/apps/api/src/routes/slack/actions.ts +177 -0
- package/templates/apps/api/src/routes/slack/index.ts +38 -0
- package/templates/apps/api/src/routes/test/auth/__tests__/session.test.ts +16 -6
- package/templates/apps/api/src/routes/test/auth/session.ts +8 -6
- package/templates/apps/api/src/routes/waitlist-experiments/__tests__/tenant-isolation.test.ts +577 -0
- package/templates/apps/api/src/routes/waitlist-experiments/comparison.ts +78 -0
- package/templates/apps/api/src/routes/waitlist-experiments/index.ts +41 -0
- package/templates/apps/api/src/routes/waitlist-experiments/list.ts +64 -0
- package/templates/apps/api/src/routes/waitlist-experiments/respond.ts +42 -0
- package/templates/apps/api/src/routes/waitlist-experiments/signup.ts +84 -0
- package/templates/apps/api/src/routes/waitlist-experiments/signups.ts +119 -0
- package/templates/apps/api/src/routes/waitlist-signups/__tests__/index.test.ts +238 -0
- package/templates/apps/api/src/routes/waitlist-signups/index.ts +101 -0
- package/templates/apps/api/src/routes/webhooks/README.md +35 -12
- package/templates/apps/api/src/routes/webhooks/__tests__/stripe-flow-checkout.test.ts +315 -0
- package/templates/apps/api/src/routes/webhooks/__tests__/stripe-org-billing.test.ts +270 -0
- package/templates/apps/api/src/routes/webhooks/stripe.ts +248 -20
- package/templates/apps/api/src/routes/workspaces/__tests__/config-tenant-isolation.test.ts +356 -0
- package/templates/apps/api/src/routes/workspaces/__tests__/tenant-isolation.test.ts +671 -0
- package/templates/apps/api/src/routes/workspaces/config.ts +125 -0
- package/templates/apps/api/src/routes/workspaces/index.ts +194 -0
- package/templates/apps/api/src/routes/workspaces/sessions.ts +218 -0
- package/templates/apps/api/src/server.ts +307 -7
- package/templates/apps/api/src/services/__tests__/abandon-email.test.ts +489 -0
- package/templates/apps/api/src/services/__tests__/aeo-score.test.ts +475 -0
- package/templates/apps/api/src/services/__tests__/answer-distributions.test.ts +132 -0
- package/templates/apps/api/src/services/__tests__/consent-migration.test.ts +65 -0
- package/templates/apps/api/src/services/__tests__/consent.test.ts +282 -0
- package/templates/apps/api/src/services/__tests__/deliverable-fulfillment.test.ts +523 -0
- package/templates/apps/api/src/services/__tests__/deliverable-resend.test.ts +359 -0
- package/templates/apps/api/src/services/__tests__/entitlements.test.ts +142 -0
- package/templates/apps/api/src/services/__tests__/flow-aggregate.test.ts +425 -0
- package/templates/apps/api/src/services/__tests__/flow-engine.test.ts +2840 -0
- package/templates/apps/api/src/services/__tests__/lead-promotion.test.ts +393 -0
- package/templates/apps/api/src/services/__tests__/lead-routing.test.ts +8 -8
- package/templates/apps/api/src/services/__tests__/listing-csv-sweep.test.ts +560 -0
- package/templates/apps/api/src/services/__tests__/listing-promotion.test.ts +684 -0
- package/templates/apps/api/src/services/__tests__/playbook-compile.test.ts +406 -0
- package/templates/apps/api/src/services/__tests__/playbook-render.test.ts +290 -0
- package/templates/apps/api/src/services/__tests__/project-listing-decision.test.ts +736 -0
- package/templates/apps/api/src/services/__tests__/project-listing-flow.test.ts +475 -0
- package/templates/apps/api/src/services/__tests__/project-listing-issue.test.ts +340 -0
- package/templates/apps/api/src/services/__tests__/recommender-capture.test.ts +983 -0
- package/templates/apps/api/src/services/__tests__/slack-notify.test.ts +278 -0
- package/templates/apps/api/src/services/__tests__/tenant-context-cascade.test.ts +71 -0
- package/templates/apps/api/src/services/__tests__/tenant-context.test.ts +379 -0
- package/templates/apps/api/src/services/__tests__/usage-aggregation.test.ts +941 -0
- package/templates/apps/api/src/services/__tests__/usage-credits.test.ts +588 -0
- package/templates/apps/api/src/services/__tests__/usage-metering.test.ts +768 -0
- package/templates/apps/api/src/services/__tests__/waitlist-dashboard.test.ts +1314 -0
- package/templates/apps/api/src/services/__tests__/waitlist-experiments.test.ts +341 -0
- package/templates/apps/api/src/services/__tests__/waitlist-followup.test.ts +567 -0
- package/templates/apps/api/src/services/__tests__/waitlist-scoring.test.ts +474 -0
- package/templates/apps/api/src/services/__tests__/waitlist-signups.test.ts +354 -0
- package/templates/apps/api/src/services/abandon-email.ts +307 -0
- package/templates/apps/api/src/services/aeo-score.ts +288 -0
- package/templates/apps/api/src/services/answer-distributions.ts +138 -0
- package/templates/apps/api/src/services/consent.ts +236 -0
- package/templates/apps/api/src/services/deliverable-fulfillment.ts +523 -0
- package/templates/apps/api/src/services/entitlements.ts +102 -0
- package/templates/apps/api/src/services/flow-aggregate.ts +232 -0
- package/templates/apps/api/src/services/flow-checkouts.ts +123 -0
- package/templates/apps/api/src/services/flow-engine.ts +1296 -0
- package/templates/apps/api/src/services/lead-promotion.ts +176 -0
- package/templates/apps/api/src/services/lead-routing.ts +3 -3
- package/templates/apps/api/src/services/listing-config.ts +102 -0
- package/templates/apps/api/src/services/listing-csv-sweep.ts +455 -0
- package/templates/apps/api/src/services/listing-promotion.ts +342 -0
- package/templates/apps/api/src/services/playbook-compile.ts +398 -0
- package/templates/apps/api/src/services/playbook-render.ts +263 -0
- package/templates/apps/api/src/services/project-listing-decision.ts +510 -0
- package/templates/apps/api/src/services/project-listing-flow.ts +426 -0
- package/templates/apps/api/src/services/project-listing-issue.ts +251 -0
- package/templates/apps/api/src/services/project-listings.ts +1472 -0
- package/templates/apps/api/src/services/recommender-capture.ts +835 -0
- package/templates/apps/api/src/services/scheduling/cal-provider.ts +392 -0
- package/templates/apps/api/src/services/scheduling/index.ts +63 -0
- package/templates/apps/api/src/services/scheduling/types.ts +88 -0
- package/templates/apps/api/src/services/slack-notify.ts +240 -0
- package/templates/apps/api/src/services/tenant-context.ts +451 -0
- package/templates/apps/api/src/services/usage-aggregation.ts +516 -0
- package/templates/apps/api/src/services/usage-credits.ts +340 -0
- package/templates/apps/api/src/services/usage-metering.ts +699 -0
- package/templates/apps/api/src/services/waitlist-dashboard.ts +947 -0
- package/templates/apps/api/src/services/waitlist-experiments.ts +213 -0
- package/templates/apps/api/src/services/waitlist-followup.ts +486 -0
- package/templates/apps/api/src/services/waitlist-scoring.ts +166 -0
- package/templates/apps/api/src/services/waitlist-signups.ts +165 -0
- package/templates/apps/api/src/vercel-handler.ts +165 -0
- package/templates/apps/api/tsconfig.json +0 -12
- package/templates/apps/mcp/CLAUDE.md +19 -9
- package/templates/apps/mcp/LICENSE +201 -0
- package/templates/apps/mcp/__tests__/create_page.test.ts +1 -0
- package/templates/apps/mcp/__tests__/delete_page.test.ts +1 -0
- package/templates/apps/mcp/__tests__/module-exclusion.test.ts +105 -0
- package/templates/apps/mcp/__tests__/parity.test.ts +70 -6
- package/templates/apps/mcp/__tests__/request_callback.test.ts +4 -4
- package/templates/apps/mcp/__tests__/schedule_tools.test.ts +242 -0
- package/templates/apps/mcp/__tests__/submit_lead.test.ts +4 -4
- package/templates/apps/mcp/__tests__/subscribe_newsletter.test.ts +4 -4
- package/templates/apps/mcp/__tests__/tools/purchase.test.ts +47 -0
- package/templates/apps/mcp/__tests__/update_page.test.ts +1 -0
- package/templates/apps/mcp/package.json +6 -4
- package/templates/apps/mcp/src/config/modules.ts +37 -0
- package/templates/apps/mcp/src/server.ts +109 -18
- package/templates/apps/mcp/src/tools/capture_lead.ts +1 -1
- package/templates/apps/mcp/src/tools/create_booking.ts +58 -0
- package/templates/apps/mcp/src/tools/create_page.ts +1 -0
- package/templates/apps/mcp/src/tools/delete_page.ts +1 -0
- package/templates/apps/mcp/src/tools/get_event_meta.ts +55 -0
- package/templates/apps/mcp/src/tools/get_flow.ts +61 -0
- package/templates/apps/mcp/src/tools/index.ts +25 -0
- package/templates/apps/mcp/src/tools/list_availability.ts +67 -0
- package/templates/apps/mcp/src/tools/purchase.ts +3 -0
- package/templates/apps/mcp/src/tools/submit_flow_step.ts +83 -0
- package/templates/apps/mcp/src/tools/update_page.ts +1 -0
- package/templates/apps/mcp/tsconfig.json +1 -9
- package/templates/apps/web/.env.example +44 -0
- package/templates/apps/web/CLAUDE.md +28 -0
- package/templates/apps/web/LICENSE +201 -0
- package/templates/apps/web/__tests__/__mocks__/code-highlight.jest-stub.ts +24 -0
- package/templates/apps/web/__tests__/__mocks__/next-intl-server.ts +33 -0
- package/templates/apps/web/__tests__/__mocks__/next-intl.ts +18 -0
- package/templates/apps/web/__tests__/__mocks__/server-only.ts +13 -0
- package/templates/apps/web/__tests__/__mocks__/style-mock.js +4 -0
- package/templates/apps/web/__tests__/__mocks__/vercel-analytics-server.ts +9 -0
- package/templates/apps/web/__tests__/jest-support/import-meta-transformer.cjs +90 -0
- package/templates/apps/web/__tests__/setup.ts +22 -32
- package/templates/apps/web/app/.well-known/ai-plugin.json/route.ts +6 -3
- package/templates/apps/web/app/.well-known/mcp.json/build-manifest.ts +8 -2
- package/templates/apps/web/app/[locale]/(authed)/account/__tests__/page.test.tsx +260 -0
- package/templates/apps/web/app/[locale]/(authed)/account/page.tsx +263 -0
- package/templates/apps/web/app/[locale]/(authed)/account/sign-out-button.tsx +39 -0
- package/templates/apps/web/app/[locale]/(authed)/layout.tsx +54 -0
- package/templates/apps/web/app/[locale]/(authed)/sign-in-gate.tsx +96 -0
- package/templates/apps/web/app/[locale]/(authed)/waitlist/[experimentId]/page.tsx +309 -0
- package/templates/apps/web/app/[locale]/(authed)/waitlist/[experimentId]/signups/[leadId]/page.tsx +149 -0
- package/templates/apps/web/app/[locale]/(authed)/waitlist/__tests__/comparison.test.tsx +273 -0
- package/templates/apps/web/app/[locale]/(authed)/waitlist/__tests__/gate.test.tsx +172 -0
- package/templates/apps/web/app/[locale]/(authed)/waitlist/__tests__/list.test.tsx +171 -0
- package/templates/apps/web/app/[locale]/(authed)/waitlist/__tests__/signup.test.tsx +179 -0
- package/templates/apps/web/app/[locale]/(authed)/waitlist/components/ExperimentsTable.tsx +116 -0
- package/templates/apps/web/app/[locale]/(authed)/waitlist/components/ForbiddenState.tsx +24 -0
- package/templates/apps/web/app/[locale]/(authed)/waitlist/components/OfferFunnelTable.tsx +66 -0
- package/templates/apps/web/app/[locale]/(authed)/waitlist/components/RollupTicker.tsx +61 -0
- package/templates/apps/web/app/[locale]/(authed)/waitlist/components/ScoreTrace.tsx +114 -0
- package/templates/apps/web/app/[locale]/(authed)/waitlist/components/SignupsTable.tsx +139 -0
- package/templates/apps/web/app/[locale]/(authed)/waitlist/components/WaitlistComparisonTabs.tsx +85 -0
- package/templates/apps/web/app/[locale]/(authed)/waitlist/components/status-badges.tsx +67 -0
- package/templates/apps/web/app/[locale]/(authed)/waitlist/format.ts +111 -0
- package/templates/apps/web/app/[locale]/(authed)/waitlist/page.tsx +179 -0
- package/templates/apps/web/app/[locale]/about/page.tsx +10 -1
- package/templates/apps/web/app/[locale]/blog/[[...slug]]/page.tsx +6 -1
- package/templates/apps/web/app/[locale]/careers/[slug]/page.tsx +9 -0
- package/templates/apps/web/app/[locale]/careers/__tests__/page-renderer-migration.test.tsx +3 -3
- package/templates/apps/web/app/[locale]/careers/page.tsx +9 -1
- package/templates/apps/web/app/[locale]/case-studies/[slug]/page.tsx +9 -0
- package/templates/apps/web/app/[locale]/case-studies/__tests__/page-renderer-migration.test.tsx +3 -3
- package/templates/apps/web/app/[locale]/case-studies/page.tsx +13 -1
- package/templates/apps/web/app/[locale]/clients/__tests__/page-renderer-migration.test.tsx +11 -1
- package/templates/apps/web/app/[locale]/clients/page.tsx +9 -1
- package/templates/apps/web/app/[locale]/contact/ContactForm.tsx +21 -1
- package/templates/apps/web/app/[locale]/contact/ConversationalIntakeWrapper.tsx +188 -0
- package/templates/apps/web/app/[locale]/contact/__tests__/ConversationalIntakeWrapper.test.tsx +242 -0
- package/templates/apps/web/app/[locale]/contact/__tests__/contact.actions.test.ts +29 -0
- package/templates/apps/web/app/[locale]/contact/__tests__/intake-analytics.test.ts +51 -0
- package/templates/apps/web/app/[locale]/contact/__tests__/page-renderer-migration.test.tsx +39 -15
- package/templates/apps/web/app/[locale]/contact/contact.actions.ts +21 -3
- package/templates/apps/web/app/[locale]/contact/intake-analytics.ts +49 -0
- package/templates/apps/web/app/[locale]/contact/intake-script.ts +66 -0
- package/templates/apps/web/app/[locale]/contact/page.tsx +14 -4
- package/templates/apps/web/app/[locale]/courses/[slug]/page.tsx +13 -1
- package/templates/apps/web/app/[locale]/courses/__tests__/page-renderer-migration.test.tsx +3 -3
- package/templates/apps/web/app/[locale]/courses/page.tsx +9 -1
- package/templates/apps/web/app/[locale]/docs/[[...slug]]/page.tsx +1 -0
- package/templates/apps/web/app/[locale]/downloads/__tests__/page-renderer-migration.test.tsx +11 -1
- package/templates/apps/web/app/[locale]/downloads/page.tsx +13 -1
- package/templates/apps/web/app/[locale]/events/[slug]/page.tsx +9 -0
- package/templates/apps/web/app/[locale]/events/__tests__/page-renderer-migration.test.tsx +12 -2
- package/templates/apps/web/app/[locale]/events/page.tsx +9 -1
- package/templates/apps/web/app/[locale]/faq/page.tsx +10 -1
- package/templates/apps/web/app/[locale]/flows/[slug]/FlowStepperClient.tsx +265 -0
- package/templates/apps/web/app/[locale]/flows/[slug]/__tests__/FlowStepperClient.permalink.test.tsx +445 -0
- package/templates/apps/web/app/[locale]/flows/[slug]/__tests__/FlowStepperClient.test.tsx +341 -0
- package/templates/apps/web/app/[locale]/flows/[slug]/__tests__/checkout.actions.test.ts +287 -0
- package/templates/apps/web/app/[locale]/flows/[slug]/__tests__/flow.actions.test.ts +302 -0
- package/templates/apps/web/app/[locale]/flows/[slug]/__tests__/two-route-tool.test.tsx +128 -0
- package/templates/apps/web/app/[locale]/flows/[slug]/checkout.actions.ts +254 -0
- package/templates/apps/web/app/[locale]/flows/[slug]/flow.actions.ts +144 -0
- package/templates/apps/web/app/[locale]/flows/[slug]/page.tsx +108 -0
- package/templates/apps/web/app/[locale]/flows/[slug]/results/[payload]/ResultsClient.tsx +532 -0
- package/templates/apps/web/app/[locale]/flows/[slug]/results/[payload]/ResultsEmailStep.tsx +121 -0
- package/templates/apps/web/app/[locale]/flows/[slug]/results/[payload]/__tests__/ResultsClient.checkout.test.tsx +457 -0
- package/templates/apps/web/app/[locale]/flows/[slug]/results/[payload]/__tests__/page.test.tsx +396 -0
- package/templates/apps/web/app/[locale]/flows/[slug]/results/[payload]/page.tsx +114 -0
- package/templates/apps/web/app/[locale]/flows/[slug]/start/page.tsx +52 -0
- package/templates/apps/web/app/[locale]/how-to/[slug]/page.tsx +13 -1
- package/templates/apps/web/app/[locale]/how-to/__tests__/page-renderer-migration.test.tsx +1 -1
- package/templates/apps/web/app/[locale]/integrations/[slug]/page.tsx +9 -0
- package/templates/apps/web/app/[locale]/integrations/__tests__/page-renderer-migration.test.tsx +3 -3
- package/templates/apps/web/app/[locale]/integrations/page.tsx +13 -1
- package/templates/apps/web/app/[locale]/launch-service/page.tsx +14 -1
- package/templates/apps/web/app/[locale]/layout.tsx +127 -16
- package/templates/apps/web/app/[locale]/page.tsx +10 -1
- package/templates/apps/web/app/[locale]/partners/[slug]/page.tsx +9 -0
- package/templates/apps/web/app/[locale]/partners/__tests__/page-renderer-migration.test.tsx +3 -3
- package/templates/apps/web/app/[locale]/partners/page.tsx +9 -1
- package/templates/apps/web/app/[locale]/products/[slug]/page.tsx +9 -0
- package/templates/apps/web/app/[locale]/products/__tests__/page-renderer-migration.test.tsx +3 -3
- package/templates/apps/web/app/[locale]/products/page.tsx +9 -1
- package/templates/apps/web/app/[locale]/projects/[slug]/page.tsx +13 -1
- package/templates/apps/web/app/[locale]/projects/__tests__/page-renderer-migration.test.tsx +10 -0
- package/templates/apps/web/app/[locale]/projects/apply/ListingApplicationClient.tsx +110 -0
- package/templates/apps/web/app/[locale]/projects/apply/__tests__/ListingApplicationClient.test.tsx +96 -0
- package/templates/apps/web/app/[locale]/projects/page.tsx +13 -1
- package/templates/apps/web/app/[locale]/resources/[slug]/page.tsx +9 -0
- package/templates/apps/web/app/[locale]/resources/__tests__/page-renderer-migration.test.tsx +12 -2
- package/templates/apps/web/app/[locale]/resources/page.tsx +13 -1
- package/templates/apps/web/app/[locale]/schedule/__tests__/page.test.tsx +117 -0
- package/templates/apps/web/app/[locale]/schedule/confirmed/__tests__/page.test.tsx +96 -0
- package/templates/apps/web/app/[locale]/schedule/confirmed/page.tsx +92 -0
- package/templates/apps/web/app/[locale]/schedule/page.tsx +82 -0
- package/templates/apps/web/app/[locale]/services/[slug]/page.tsx +9 -0
- package/templates/apps/web/app/[locale]/services/__tests__/page-renderer-migration.test.tsx +3 -3
- package/templates/apps/web/app/[locale]/services/page.tsx +9 -1
- package/templates/apps/web/app/[locale]/sign-in/__tests__/next-path.test.ts +31 -0
- package/templates/apps/web/app/[locale]/sign-in/__tests__/page.test.tsx +152 -0
- package/templates/apps/web/app/[locale]/sign-in/next-path.ts +0 -0
- package/templates/apps/web/app/[locale]/sign-in/page.tsx +83 -0
- package/templates/apps/web/app/[locale]/sign-in/sign-in-card.tsx +123 -0
- package/templates/apps/web/app/[locale]/software/[slug]/page.tsx +13 -1
- package/templates/apps/web/app/[locale]/software/__tests__/page-renderer-migration.test.tsx +1 -1
- package/templates/apps/web/app/[locale]/tech-articles/[slug]/page.tsx +13 -1
- package/templates/apps/web/app/[locale]/tech-articles/__tests__/page-renderer-migration.test.tsx +1 -1
- package/templates/apps/web/app/[locale]/testimonials/__tests__/page-renderer-migration.test.tsx +11 -1
- package/templates/apps/web/app/[locale]/testimonials/page.tsx +13 -1
- package/templates/apps/web/app/[locale]/thanks/[slug]/__tests__/page.test.tsx +93 -0
- package/templates/apps/web/app/[locale]/thanks/[slug]/page.tsx +48 -0
- package/templates/apps/web/app/[locale]/use-cases/__tests__/page-renderer-migration.test.tsx +11 -1
- package/templates/apps/web/app/[locale]/use-cases/page.tsx +13 -1
- package/templates/apps/web/app/[locale]/whitepapers/[slug]/page.tsx +13 -1
- package/templates/apps/web/app/[locale]/whitepapers/__tests__/page-renderer-migration.test.tsx +1 -1
- package/templates/apps/web/app/globals.css +35 -0
- package/templates/apps/web/app/llms.txt/route.ts +2 -1
- package/templates/apps/web/app/og/route.tsx +10 -2
- package/templates/apps/web/auth.ts +8 -0
- package/templates/apps/web/components/sign-out-trigger.tsx +42 -0
- package/templates/apps/web/content/en/blog/README.txt +25 -0
- package/templates/apps/web/content/en/docs/index.mdx +28 -0
- package/templates/apps/web/jest.config.cjs +90 -27
- package/templates/apps/web/lib/__tests__/site-brand.test.ts +119 -0
- package/templates/apps/web/lib/__tests__/site-theme.test.ts +112 -0
- package/templates/apps/web/lib/__tests__/structured-data.test.tsx +499 -0
- package/templates/apps/web/lib/account-config.ts +13 -0
- package/templates/apps/web/lib/server-api.ts +85 -0
- package/templates/apps/web/lib/site-brand.tsx +121 -0
- package/templates/apps/web/lib/site-theme.ts +74 -0
- package/templates/apps/web/messages/en.json +275 -7
- package/templates/apps/web/middleware.ts +11 -3
- package/templates/apps/web/next.config.mjs +8 -2
- package/templates/apps/web/package.json +17 -7
- package/templates/apps/web/postcss.config.mjs +23 -0
- package/templates/apps/web/public/.well-known/security.txt +3 -3
- package/templates/apps/web/src/lib/routes.ts +15 -2
- package/templates/apps/web/tsconfig.json +1 -16
- package/templates/content/_site.mdx +36 -25
- package/templates/content/en/AboutPage/about.mdx +24 -14
- package/templates/content/en/ContactPage/contact.mdx +23 -14
- package/templates/content/en/FAQPage/faq.mdx +10 -392
- package/templates/content/en/HomePage/home.mdx +14 -285
- package/templates/database/CHANGELOG.md +443 -0
- package/templates/database/LICENSE +201 -0
- package/templates/database/README.md +3 -1
- package/templates/database/__tests__/flows-listing-diagnostic.test.ts +392 -0
- package/templates/database/__tests__/flows-presets.test.ts +173 -0
- package/templates/database/__tests__/prefixed-ids.db.test.ts +146 -0
- package/templates/database/__tests__/seed.test.ts +88 -0
- package/templates/database/__tests__/user-cascade.test.ts +19 -4
- package/templates/database/auth/schema.prisma +521 -0
- package/templates/database/config/schema.prisma +234 -0
- package/templates/database/content/schema.prisma +263 -0
- package/templates/database/flows/listing-diagnostic.ts +182 -0
- package/templates/database/flows/presets.ts +315 -0
- package/templates/database/inbox/__tests__/schema.test.ts +4 -0
- package/templates/database/inbox/schema.prisma +574 -3
- package/templates/database/jest.config.cjs +8 -0
- package/templates/database/migrations/20260712215647_add_multi_tenant_tenancy/migration.sql +201 -0
- package/templates/database/migrations/20260713143320_reanchor_site_config_to_workspace/migration.sql +67 -0
- package/templates/database/migrations/20260713174000_add_audit_logs/migration.sql +46 -0
- package/templates/database/migrations/20260713190000_drop_telemetry_records/migration.sql +25 -0
- package/templates/database/migrations/20260713192000_add_uuidv7_function/migration.sql +49 -0
- package/templates/database/migrations/20260713192429_prefixed_uuid_entity_ids/migration.sql +197 -0
- package/templates/database/migrations/20260713200000_add_org_billing/migration.sql +32 -0
- package/templates/database/migrations/20260714000000_add_lead_promotion_fields/migration.sql +22 -0
- package/templates/database/migrations/20260714010000_add_usage_billing_content_models/migration.sql +151 -0
- package/templates/database/migrations/20260714020000_add_usage_billing_auth_config_models/migration.sql +223 -0
- package/templates/database/migrations/20260714030000_credit_balance_non_negative/migration.sql +22 -0
- package/templates/database/migrations/20260714030000_widen_usage_billing_report_aggregates/migration.sql +32 -0
- package/templates/database/migrations/20260810233516_add_flow_engine_models/migration.sql +40 -0
- package/templates/database/migrations/20260811020000_add_flow_session_abandon_email_columns/migration.sql +4 -0
- package/templates/database/migrations/20260811030000_add_flow_session_abandon_email_index/migration.sql +2 -0
- package/templates/database/migrations/20260811040000_add_flow_session_segment/migration.sql +2 -0
- package/templates/database/migrations/20260811050000_add_deliverable_models/migration.sql +29 -0
- package/templates/database/migrations/20260812000000_add_deliverable_grant_send_columns/migration.sql +6 -0
- package/templates/database/migrations/20260902222948_add_waitlist_experiment_models/migration.sql +39 -0
- package/templates/database/migrations/20260902224500_add_waitlist_id_format_checks/migration.sql +24 -0
- package/templates/database/migrations/20260903004112_add_flow_session_flags_aeo_score/migration.sql +3 -0
- package/templates/database/migrations/20260903010000_add_playbook_instance/migration.sql +33 -0
- package/templates/database/migrations/20260904000000_add_waitlist_followup_send/migration.sql +30 -0
- package/templates/database/migrations/20260905230000_add_flow_checkout/migration.sql +47 -0
- package/templates/database/migrations/20260906000000_add_flow_checkout_idempotency/migration.sql +16 -0
- package/templates/database/migrations/20260907120000_add_project_listing/migration.sql +47 -0
- package/templates/database/migrations/20260907130000_add_project_listing_slack_columns/migration.sql +10 -0
- package/templates/database/migrations/20260907180000_add_listing_csv_send/migration.sql +26 -0
- package/templates/database/migrations/20260909120000_project_listings_platform/migration.sql +90 -0
- package/templates/database/migrations/20260909180000_listing_draft_and_fields/migration.sql +174 -0
- package/templates/database/migrations/20260910120000_listing_public_site_key/migration.sql +14 -0
- package/templates/database/migrations/20260911120000_listing_copy_edit_audit/migration.sql +25 -0
- package/templates/database/migrations/20260911140000_listing_structured_address/migration.sql +32 -0
- package/templates/database/migrations/20260911180000_consent_grants/migration.sql +71 -0
- package/templates/database/migrations/20260911200000_listing_site_answers/migration.sql +30 -0
- package/templates/database/migrations/20260912120000_listing_owner_link/migration.sql +49 -0
- package/templates/database/ops/schema.prisma +27 -0
- package/templates/database/package.json +42 -4
- package/templates/database/prisma.config.ts +1 -1
- package/templates/database/schema.prisma +19 -435
- package/templates/database/seed-test.ts +14 -4
- package/templates/database/seed.ts +148 -0
- package/templates/database/tenancy.ts +45 -0
- package/templates/database/tsconfig.flows.json +15 -0
- package/templates/database/tsconfig.json +7 -1
- package/templates/database/tsconfig.tenancy.json +14 -0
- package/templates/package.json +30 -55
- package/templates/pnpm-workspace.yaml +0 -3
- package/templates/project.yaml +1 -1
- package/templates/tsconfig.json +1 -37
- package/templates/apps/web/__tests__/auth/adversarial/cookie-attributes.test.ts +0 -94
- package/templates/apps/web/__tests__/auth/adversarial/oauth-state-parameter.test.ts +0 -75
- package/templates/apps/web/__tests__/auth/adversarial/redirect-uri-allowlist.test.ts +0 -82
- package/templates/apps/web/__tests__/auth/env-helpers.test.ts +0 -168
- package/templates/apps/web/__tests__/authority-list-routes.test.tsx +0 -208
- package/templates/apps/web/__tests__/better-stack.test.ts +0 -66
- package/templates/apps/web/__tests__/blog-dir-resolution.test.ts +0 -87
- package/templates/apps/web/__tests__/blog-source-populated.test.ts +0 -130
- package/templates/apps/web/__tests__/data-testid-coverage.test.tsx +0 -60
- package/templates/apps/web/__tests__/google-analytics.test.tsx +0 -104
- package/templates/apps/web/__tests__/i18n/middleware.test.ts +0 -119
- package/templates/apps/web/__tests__/i18n/routing.test.ts +0 -114
- package/templates/apps/web/__tests__/i18n/translations.test.ts +0 -191
- package/templates/apps/web/__tests__/json-ld.test.ts +0 -98
- package/templates/apps/web/__tests__/knowledge-events-routes.test.tsx +0 -580
- package/templates/apps/web/__tests__/layout-stealth.test.ts +0 -39
- package/templates/apps/web/__tests__/lighthouse-fixtures/improvement-image-shrink.tsx +0 -103
- package/templates/apps/web/__tests__/lighthouse-fixtures/override-fixture.md +0 -114
- package/templates/apps/web/__tests__/lighthouse-fixtures/regression-bloat.tsx +0 -94
- package/templates/apps/web/__tests__/page.test.tsx +0 -230
- package/templates/apps/web/__tests__/products-services-phase1.test.tsx +0 -321
- package/templates/apps/web/__tests__/projects-routes.test.tsx +0 -392
- package/templates/apps/web/__tests__/resources-downloads-integrations-phase2.test.tsx +0 -475
- package/templates/apps/web/__tests__/routes/ai-plugin-json.test.ts +0 -53
- package/templates/apps/web/__tests__/routes/blog-route.test.tsx +0 -191
- package/templates/apps/web/__tests__/routes/llms-txt.test.ts +0 -123
- package/templates/apps/web/__tests__/routes/robots-txt.test.ts +0 -52
- package/templates/apps/web/__tests__/routes/sitemap-xml.test.ts +0 -78
- package/templates/apps/web/__tests__/routes/well-known-mcp.test.ts +0 -203
- package/templates/apps/web/__tests__/section-routes.test.tsx +0 -693
- package/templates/apps/web/__tests__/security-headers.test.ts +0 -201
- package/templates/apps/web/__tests__/sentry-config.test.ts +0 -116
- package/templates/apps/web/__tests__/sitemap-locales.test.ts +0 -415
- package/templates/apps/web/__tests__/standalone-informational-routes.test.tsx +0 -844
- package/templates/apps/web/app/[locale]/__tests__/layout.test.tsx +0 -120
- package/templates/apps/web/app/[locale]/__tests__/page-renderer-migration.test.tsx +0 -202
- package/templates/apps/web/app/__tests__/error-boundaries.test.tsx +0 -152
- package/templates/apps/web/content/en/blog/index.mdx +0 -15
- package/templates/apps/web/content/en/blog/launching-ailk.mdx +0 -20
- package/templates/apps/web/content/en/docs/getting-started.mdx +0 -30
- package/templates/content/.gitkeep +0 -0
- package/templates/content/CLAUDE.md +0 -85
- package/templates/content/ar/HomePage/home.mdx +0 -123
- package/templates/content/ar/_site.mdx +0 -35
- package/templates/content/de/_site.mdx +0 -29
- package/templates/content/en/.gitkeep +0 -0
- package/templates/content/en/AboutPage/about.schema.json +0 -82
- package/templates/content/en/BookCall/book-call.mdx +0 -19
- package/templates/content/en/BookCall/book-call.schema.json +0 -121
- package/templates/content/en/BreadcrumbList/site-breadcrumbs.mdx +0 -20
- package/templates/content/en/CareersList/careers.mdx +0 -24
- package/templates/content/en/CaseStudiesList/case-studies.mdx +0 -22
- package/templates/content/en/CaseStudiesList/showcase.mdx +0 -8
- package/templates/content/en/CaseStudy/ailk-aeo-rollout.mdx +0 -32
- package/templates/content/en/CaseStudy/schema-builder-migration.mdx +0 -41
- package/templates/content/en/ClientsList/clients.mdx +0 -38
- package/templates/content/en/ContactPage/contact.schema.json +0 -100
- package/templates/content/en/ContactPoint/primary.mdx +0 -14
- package/templates/content/en/Course/aeo-fundamentals.mdx +0 -37
- package/templates/content/en/Course/schema-builder-workshop.mdx +0 -62
- package/templates/content/en/CoursesList/courses.mdx +0 -22
- package/templates/content/en/DownloadsList/downloads.mdx +0 -22
- package/templates/content/en/Event/aeo-office-hours-may-2026.mdx +0 -39
- package/templates/content/en/Event/ailk-launch-day-2026.mdx +0 -35
- package/templates/content/en/EventsList/events.mdx +0 -26
- package/templates/content/en/FAQPage/faq.schema.json +0 -231
- package/templates/content/en/GlossaryList/glossary.mdx +0 -19
- package/templates/content/en/GlossaryList/glossary.schema.json +0 -300
- package/templates/content/en/HomePage/home.schema.json +0 -88
- package/templates/content/en/HomePage/launch-service.mdx +0 -475
- package/templates/content/en/HomePage/launch-service.schema.json +0 -138
- package/templates/content/en/HowTo/add-mdx-content.mdx +0 -38
- package/templates/content/en/HowTo/configure-aeo-scoring.mdx +0 -35
- package/templates/content/en/HowTo/set-up-json-ld.mdx +0 -36
- package/templates/content/en/Integration/github-actions.mdx +0 -31
- package/templates/content/en/Integration/openai-api.mdx +0 -26
- package/templates/content/en/Integration/vercel.mdx +0 -32
- package/templates/content/en/IntegrationsList/integrations.mdx +0 -22
- package/templates/content/en/JobPosting/content-strategist.mdx +0 -46
- package/templates/content/en/JobPosting/developer-advocate.mdx +0 -47
- package/templates/content/en/JobPosting/senior-fullstack-engineer.mdx +0 -57
- package/templates/content/en/LandingPage/aeo-audit.mdx +0 -19
- package/templates/content/en/LandingPage/aeo-audit.schema.json +0 -92
- package/templates/content/en/LandingPage/agency-waitlist.mdx +0 -340
- package/templates/content/en/LandingPage/agency-waitlist.schema.json +0 -152
- package/templates/content/en/LandingPage/developers.mdx +0 -19
- package/templates/content/en/LandingPage/developers.schema.json +0 -109
- package/templates/content/en/LandingPage/for-agencies.mdx +0 -390
- package/templates/content/en/LandingPage/for-agencies.schema.json +0 -107
- package/templates/content/en/LandingPage/for-ai-search-agencies.mdx +0 -417
- package/templates/content/en/LandingPage/for-ai-search-agencies.schema.json +0 -105
- package/templates/content/en/LandingPage/for-businesses.mdx +0 -19
- package/templates/content/en/LandingPage/for-businesses.schema.json +0 -96
- package/templates/content/en/LandingPage/platform.mdx +0 -369
- package/templates/content/en/LandingPage/platform.schema.json +0 -166
- package/templates/content/en/LandingPage/vs-ai-website-builders.mdx +0 -19
- package/templates/content/en/LandingPage/vs-ai-website-builders.schema.json +0 -102
- package/templates/content/en/LandingPage/vs-wordpress.mdx +0 -19
- package/templates/content/en/LandingPage/vs-wordpress.schema.json +0 -53
- package/templates/content/en/NewsletterSignup/newsletter.mdx +0 -236
- package/templates/content/en/NewsletterSignup/newsletter.schema.json +0 -91
- package/templates/content/en/Organization/site-owner.mdx +0 -16
- package/templates/content/en/Partner/prisma.mdx +0 -31
- package/templates/content/en/Partner/supabase.mdx +0 -30
- package/templates/content/en/Partner/vercel.mdx +0 -28
- package/templates/content/en/PartnersList/partners.mdx +0 -22
- package/templates/content/en/Person/founder.mdx +0 -18
- package/templates/content/en/Pricing/pricing.mdx +0 -538
- package/templates/content/en/Pricing/pricing.schema.json +0 -209
- package/templates/content/en/PrivacyPage/privacy.mdx +0 -165
- package/templates/content/en/PrivacyPage/privacy.schema.json +0 -64
- package/templates/content/en/Product/ailk.mdx +0 -19
- package/templates/content/en/Product/ailk.schema.json +0 -94
- package/templates/content/en/Product/api-launch-kit.mdx +0 -26
- package/templates/content/en/Product/launch-kit.mdx +0 -28
- package/templates/content/en/ProductsList/products.mdx +0 -26
- package/templates/content/en/Project/ailk-phase-2.mdx +0 -54
- package/templates/content/en/ProjectsList/projects.mdx +0 -25
- package/templates/content/en/ResourceDetail/aeo-scoring-guide.mdx +0 -27
- package/templates/content/en/ResourceDetail/content-model-whitepaper.mdx +0 -28
- package/templates/content/en/ResourceDetail/schema-org-quick-reference.mdx +0 -39
- package/templates/content/en/ResourcesHub/resources.mdx +0 -22
- package/templates/content/en/Service/consulting.mdx +0 -22
- package/templates/content/en/Service/implementation.mdx +0 -30
- package/templates/content/en/Service/launch-service.mdx +0 -232
- package/templates/content/en/Service/launch.mdx +0 -228
- package/templates/content/en/ServicesList/services.mdx +0 -84
- package/templates/content/en/SoftwareProduct/ailk-aeo.mdx +0 -44
- package/templates/content/en/SoftwareProduct/ailk-content-adapters.mdx +0 -47
- package/templates/content/en/SoftwareProduct/ailk-schema.mdx +0 -49
- package/templates/content/en/TeamList/team.mdx +0 -63
- package/templates/content/en/TeamMember/founder.mdx +0 -72
- package/templates/content/en/TermsPage/terms.mdx +0 -19
- package/templates/content/en/TermsPage/terms.schema.json +0 -99
- package/templates/content/en/TestimonialsList/testimonials.mdx +0 -44
- package/templates/content/en/UseCasesList/use-cases.mdx +0 -30
- package/templates/content/en/Whitepaper/ax-first-product-development.mdx +0 -27
- package/templates/content/en/Whitepaper/open-core-commercial-strategy.mdx +0 -27
- package/templates/content/en/Whitepaper/structured-data-for-answer-engines.mdx +0 -31
- package/templates/content/en-XA/AboutPage/about.mdx +0 -8
- package/templates/content/en-XA/FAQPage/faq.mdx +0 -8
- package/templates/content/en-XA/HomePage/home.mdx +0 -8
- package/templates/content/es/_site.mdx +0 -30
- package/templates/content/fr/_site.mdx +0 -30
- package/templates/content/ja/_site.mdx +0 -30
- package/templates/content/pt-BR/AboutPage/about.mdx +0 -8
- package/templates/content/pt-BR/FAQPage/faq.mdx +0 -8
- package/templates/content/pt-BR/HomePage/home.mdx +0 -23
|
@@ -1,120 +0,0 @@
|
|
|
1
|
-
/**
|
|
2
|
-
* Tests for LocaleLayout — verifies the <html dir> attribute is set
|
|
3
|
-
* correctly based on the active locale.
|
|
4
|
-
*
|
|
5
|
-
* The layout is an async server component; jest renders it by awaiting
|
|
6
|
-
* the returned ReactElement promise. Heavy Next.js/next-intl deps are
|
|
7
|
-
* mocked so the test focuses on the dir attribute wiring only.
|
|
8
|
-
*/
|
|
9
|
-
import React from "react";
|
|
10
|
-
import { render } from "@testing-library/react";
|
|
11
|
-
|
|
12
|
-
// ── Mock heavy/Next.js-specific dependencies ──────────────────────────
|
|
13
|
-
jest.mock("next/navigation", () => ({
|
|
14
|
-
usePathname: jest.fn().mockReturnValue("/en/"),
|
|
15
|
-
}));
|
|
16
|
-
|
|
17
|
-
jest.mock("next/headers", () => ({
|
|
18
|
-
headers: jest.fn().mockResolvedValue({
|
|
19
|
-
get: jest.fn().mockReturnValue("/"),
|
|
20
|
-
}),
|
|
21
|
-
}));
|
|
22
|
-
|
|
23
|
-
jest.mock("next-intl/server", () => ({
|
|
24
|
-
getMessages: jest.fn().mockResolvedValue({}),
|
|
25
|
-
getTranslations: jest.fn().mockImplementation(({ namespace }: { namespace: string }) => {
|
|
26
|
-
if (namespace === "nav") {
|
|
27
|
-
const t = (key: string) => key;
|
|
28
|
-
return Promise.resolve(t);
|
|
29
|
-
}
|
|
30
|
-
const t = (key: string) => key;
|
|
31
|
-
return Promise.resolve(t);
|
|
32
|
-
}),
|
|
33
|
-
}));
|
|
34
|
-
|
|
35
|
-
jest.mock("next-intl", () => ({
|
|
36
|
-
NextIntlClientProvider: ({ children }: { children: React.ReactNode }) => (
|
|
37
|
-
<>{children}</>
|
|
38
|
-
),
|
|
39
|
-
}));
|
|
40
|
-
|
|
41
|
-
jest.mock("@working-theory/web-i18n", () => ({
|
|
42
|
-
readLocales: jest.fn().mockReturnValue(["en"]),
|
|
43
|
-
buildHreflangAlternates: jest.fn().mockReturnValue({}),
|
|
44
|
-
getStealthRobots: jest.fn().mockReturnValue({}),
|
|
45
|
-
}));
|
|
46
|
-
|
|
47
|
-
jest.mock("@working-theory/analytics", () => ({
|
|
48
|
-
Telemetry: () => null,
|
|
49
|
-
VerificationMeta: () => null,
|
|
50
|
-
}));
|
|
51
|
-
|
|
52
|
-
// ── Import subject under test ──────────────────────────────────────────
|
|
53
|
-
import LocaleLayout, { generateMetadata } from "../layout";
|
|
54
|
-
|
|
55
|
-
async function renderLayout(locale: string): Promise<void> {
|
|
56
|
-
const element = LocaleLayout({
|
|
57
|
-
children: <span data-testid="child">content</span>,
|
|
58
|
-
params: Promise.resolve({ locale }),
|
|
59
|
-
});
|
|
60
|
-
const resolved = await element;
|
|
61
|
-
render(resolved);
|
|
62
|
-
}
|
|
63
|
-
|
|
64
|
-
describe("generateMetadata()", () => {
|
|
65
|
-
it("returns a title and description", async () => {
|
|
66
|
-
const metadata = await generateMetadata();
|
|
67
|
-
expect(metadata.title).toBe("AI Launch Kit");
|
|
68
|
-
expect(metadata.description).toMatch(/OSS/);
|
|
69
|
-
});
|
|
70
|
-
|
|
71
|
-
it("includes alternates.languages from buildHreflangAlternates", async () => {
|
|
72
|
-
const metadata = await generateMetadata();
|
|
73
|
-
expect(metadata.alternates).toBeDefined();
|
|
74
|
-
expect(metadata.alternates?.languages).toBeDefined();
|
|
75
|
-
});
|
|
76
|
-
});
|
|
77
|
-
|
|
78
|
-
describe("LocaleLayout — chrome smoke (#1451)", () => {
|
|
79
|
-
it("renders Nav with aria-label=Primary", async () => {
|
|
80
|
-
await renderLayout("en");
|
|
81
|
-
expect(document.querySelector("nav[aria-label='Primary']")).toBeInTheDocument();
|
|
82
|
-
});
|
|
83
|
-
|
|
84
|
-
it("renders Footer element", async () => {
|
|
85
|
-
await renderLayout("en");
|
|
86
|
-
expect(document.querySelector("footer")).toBeInTheDocument();
|
|
87
|
-
});
|
|
88
|
-
|
|
89
|
-
it("renders nav logo link pointing to locale home", async () => {
|
|
90
|
-
await renderLayout("en");
|
|
91
|
-
const logoLink = document.querySelector("[data-testid='brand-logo-link']");
|
|
92
|
-
expect(logoLink).toBeInTheDocument();
|
|
93
|
-
// as-needed dialect (#3450): bare for the default locale.
|
|
94
|
-
expect(logoLink?.getAttribute("href")).toBe("/");
|
|
95
|
-
});
|
|
96
|
-
});
|
|
97
|
-
|
|
98
|
-
describe("LocaleLayout — <html dir> attribute", () => {
|
|
99
|
-
// React 19 + jsdom: <html> is a singleton (document.documentElement);
|
|
100
|
-
// RTL merges the rendered <html> attributes onto the live document root.
|
|
101
|
-
it('renders dir="ltr" for locale "en"', async () => {
|
|
102
|
-
await renderLayout("en");
|
|
103
|
-
expect(document.documentElement.getAttribute("dir")).toBe("ltr");
|
|
104
|
-
});
|
|
105
|
-
|
|
106
|
-
it('renders dir="rtl" for locale "ar"', async () => {
|
|
107
|
-
await renderLayout("ar");
|
|
108
|
-
expect(document.documentElement.getAttribute("dir")).toBe("rtl");
|
|
109
|
-
});
|
|
110
|
-
|
|
111
|
-
it('preserves lang attribute for locale "en"', async () => {
|
|
112
|
-
await renderLayout("en");
|
|
113
|
-
expect(document.documentElement.getAttribute("lang")).toBe("en");
|
|
114
|
-
});
|
|
115
|
-
|
|
116
|
-
it('preserves lang attribute for locale "ar"', async () => {
|
|
117
|
-
await renderLayout("ar");
|
|
118
|
-
expect(document.documentElement.getAttribute("lang")).toBe("ar");
|
|
119
|
-
});
|
|
120
|
-
});
|
|
@@ -1,202 +0,0 @@
|
|
|
1
|
-
/**
|
|
2
|
-
* HTML snapshot regression tests for the apps/web → @working-theory/page-renderer migration.
|
|
3
|
-
*
|
|
4
|
-
* Node version: captured against Node 22.x (same as CI).
|
|
5
|
-
* MDX seed content: DI-injected stub — no fs reads, deterministic across environments.
|
|
6
|
-
*
|
|
7
|
-
* Two-commit strategy (per spec §8 Open Questions):
|
|
8
|
-
* Commit 1: this file added; snapshots captured against pre-migration routes (RED-passing).
|
|
9
|
-
* Commit 2: routes migrated; same snapshots verify diff=0 (GREEN).
|
|
10
|
-
*
|
|
11
|
-
* Each of the four routes is tested in two states:
|
|
12
|
-
* - empty: getMdxSource returns null → empty-state branch
|
|
13
|
-
* - populated: getMdxSource returns a minimal MdxPageSource → full render
|
|
14
|
-
*
|
|
15
|
-
* Failure = snapshot diff > 0 bytes. DO NOT regenerate snapshots to pass.
|
|
16
|
-
* A non-zero diff indicates a behavior regression in the migration. Escalate.
|
|
17
|
-
*/
|
|
18
|
-
import React from "react";
|
|
19
|
-
import { render } from "@testing-library/react";
|
|
20
|
-
import { stripJsonLd } from "@working-theory/page-renderer/testing";
|
|
21
|
-
|
|
22
|
-
// ── @working-theory/ui mock ─────────────────────────────────────────────────────────────
|
|
23
|
-
// SectionRenderer renders a predictable deterministic div so the snapshot is
|
|
24
|
-
// stable across section-component implementations.
|
|
25
|
-
jest.mock("@working-theory/ui", () => ({
|
|
26
|
-
SectionRenderer: ({ sections }: { sections: unknown[] }) =>
|
|
27
|
-
React.createElement("div", {
|
|
28
|
-
"data-testid": "section-renderer",
|
|
29
|
-
"data-section-count": String(sections.length),
|
|
30
|
-
}),
|
|
31
|
-
}));
|
|
32
|
-
|
|
33
|
-
// ── Import page components ─────────────────────────────────────────────────────
|
|
34
|
-
import HomePage from "../page";
|
|
35
|
-
import AboutPage from "../about/page";
|
|
36
|
-
import FAQPage from "../faq/page";
|
|
37
|
-
import LaunchServicePage from "../launch-service/page";
|
|
38
|
-
|
|
39
|
-
// ── Shared stubs ───────────────────────────────────────────────────────────────
|
|
40
|
-
|
|
41
|
-
function makeSource(
|
|
42
|
-
type: string,
|
|
43
|
-
slug: string,
|
|
44
|
-
title: string,
|
|
45
|
-
description: string,
|
|
46
|
-
) {
|
|
47
|
-
return {
|
|
48
|
-
frontmatter: {
|
|
49
|
-
type: type as "HomePage",
|
|
50
|
-
slug,
|
|
51
|
-
title,
|
|
52
|
-
description,
|
|
53
|
-
},
|
|
54
|
-
body: "",
|
|
55
|
-
};
|
|
56
|
-
}
|
|
57
|
-
|
|
58
|
-
const getMdxSourceEmpty = jest.fn().mockResolvedValue(null);
|
|
59
|
-
const getPageSectionsEmpty = jest.fn().mockResolvedValue([]);
|
|
60
|
-
|
|
61
|
-
// ── HomePage ───────────────────────────────────────────────────────────────────
|
|
62
|
-
|
|
63
|
-
describe("HomePage — snapshot regression", () => {
|
|
64
|
-
it("empty state renders deterministic HTML", async () => {
|
|
65
|
-
const element = await HomePage({
|
|
66
|
-
params: Promise.resolve({ locale: "en" }),
|
|
67
|
-
getMdxSource: getMdxSourceEmpty,
|
|
68
|
-
getPageSections: getPageSectionsEmpty,
|
|
69
|
-
});
|
|
70
|
-
const { container } = render(element);
|
|
71
|
-
expect(stripJsonLd(container.innerHTML)).toMatchInlineSnapshot(
|
|
72
|
-
`"<main><h1>AI Launch Kit</h1><p data-testid="home-description">AX-first OSS template for shipping software with API + MCP as canonical surfaces.</p></main>"`,
|
|
73
|
-
);
|
|
74
|
-
});
|
|
75
|
-
|
|
76
|
-
it("populated state renders deterministic HTML", async () => {
|
|
77
|
-
const source = makeSource(
|
|
78
|
-
"HomePage",
|
|
79
|
-
"home",
|
|
80
|
-
"AI Launch Kit",
|
|
81
|
-
"AX-first OSS template for shipping software with API + MCP as canonical surfaces.",
|
|
82
|
-
);
|
|
83
|
-
const getMdxSourcePopulated = jest.fn().mockResolvedValue(source);
|
|
84
|
-
const getPageSectionsPopulated = jest.fn().mockResolvedValue([]);
|
|
85
|
-
|
|
86
|
-
const element = await HomePage({
|
|
87
|
-
params: Promise.resolve({ locale: "en" }),
|
|
88
|
-
getMdxSource: getMdxSourcePopulated,
|
|
89
|
-
getPageSections: getPageSectionsPopulated,
|
|
90
|
-
});
|
|
91
|
-
const { container } = render(element);
|
|
92
|
-
expect(stripJsonLd(container.innerHTML)).toMatchInlineSnapshot(
|
|
93
|
-
`"<main><h1>AI Launch Kit</h1><p data-testid="home-description">AX-first OSS template for shipping software with API + MCP as canonical surfaces.</p><div data-testid="section-renderer" data-section-count="0"></div></main>"`,
|
|
94
|
-
);
|
|
95
|
-
});
|
|
96
|
-
});
|
|
97
|
-
|
|
98
|
-
// ── AboutPage ──────────────────────────────────────────────────────────────────
|
|
99
|
-
|
|
100
|
-
describe("AboutPage — snapshot regression", () => {
|
|
101
|
-
it("empty state renders deterministic HTML", async () => {
|
|
102
|
-
const element = await AboutPage({
|
|
103
|
-
params: Promise.resolve({ locale: "en" }),
|
|
104
|
-
getMdxSource: getMdxSourceEmpty,
|
|
105
|
-
getPageSections: getPageSectionsEmpty,
|
|
106
|
-
});
|
|
107
|
-
const { container } = render(element);
|
|
108
|
-
expect(stripJsonLd(container.innerHTML)).toMatchInlineSnapshot(
|
|
109
|
-
`"<main><h1>About</h1><p data-testid="section-empty-state">About page content coming soon.</p></main>"`,
|
|
110
|
-
);
|
|
111
|
-
});
|
|
112
|
-
|
|
113
|
-
it("populated state renders deterministic HTML", async () => {
|
|
114
|
-
const source = makeSource("AboutPage", "about", "About Us", "Who we are.");
|
|
115
|
-
const getMdxSourcePopulated = jest.fn().mockResolvedValue(source);
|
|
116
|
-
const getPageSectionsPopulated = jest.fn().mockResolvedValue([]);
|
|
117
|
-
|
|
118
|
-
const element = await AboutPage({
|
|
119
|
-
params: Promise.resolve({ locale: "en" }),
|
|
120
|
-
getMdxSource: getMdxSourcePopulated,
|
|
121
|
-
getPageSections: getPageSectionsPopulated,
|
|
122
|
-
});
|
|
123
|
-
const { container } = render(element);
|
|
124
|
-
expect(stripJsonLd(container.innerHTML)).toMatchInlineSnapshot(
|
|
125
|
-
`"<main><h1>About Us</h1><p data-testid="about-description">Who we are.</p><div data-testid="section-renderer" data-section-count="0"></div></main>"`,
|
|
126
|
-
);
|
|
127
|
-
});
|
|
128
|
-
});
|
|
129
|
-
|
|
130
|
-
// ── FAQPage ────────────────────────────────────────────────────────────────────
|
|
131
|
-
|
|
132
|
-
describe("FAQPage — snapshot regression", () => {
|
|
133
|
-
it("empty state renders deterministic HTML (with scaffold JSON-LD)", async () => {
|
|
134
|
-
const element = await FAQPage({
|
|
135
|
-
params: Promise.resolve({ locale: "en" }),
|
|
136
|
-
getMdxSource: getMdxSourceEmpty,
|
|
137
|
-
getPageSections: getPageSectionsEmpty,
|
|
138
|
-
});
|
|
139
|
-
const { container } = render(element);
|
|
140
|
-
expect(stripJsonLd(container.innerHTML)).toMatchInlineSnapshot(
|
|
141
|
-
`"<main><h1>FAQ</h1><p data-testid="section-empty-state">Frequently asked questions content coming soon.</p></main>"`,
|
|
142
|
-
);
|
|
143
|
-
});
|
|
144
|
-
|
|
145
|
-
it("populated state renders deterministic HTML", async () => {
|
|
146
|
-
const source = makeSource(
|
|
147
|
-
"FAQPage",
|
|
148
|
-
"faq",
|
|
149
|
-
"FAQ",
|
|
150
|
-
"Frequently asked questions.",
|
|
151
|
-
);
|
|
152
|
-
const getMdxSourcePopulated = jest.fn().mockResolvedValue(source);
|
|
153
|
-
const getPageSectionsPopulated = jest.fn().mockResolvedValue([]);
|
|
154
|
-
|
|
155
|
-
const element = await FAQPage({
|
|
156
|
-
params: Promise.resolve({ locale: "en" }),
|
|
157
|
-
getMdxSource: getMdxSourcePopulated,
|
|
158
|
-
getPageSections: getPageSectionsPopulated,
|
|
159
|
-
});
|
|
160
|
-
const { container } = render(element);
|
|
161
|
-
expect(stripJsonLd(container.innerHTML)).toMatchInlineSnapshot(
|
|
162
|
-
`"<main><h1>FAQ</h1><p data-testid="faq-description">Frequently asked questions.</p><div data-testid="section-renderer" data-section-count="0"></div></main>"`,
|
|
163
|
-
);
|
|
164
|
-
});
|
|
165
|
-
});
|
|
166
|
-
|
|
167
|
-
// ── LaunchServicePage ──────────────────────────────────────────────────────────
|
|
168
|
-
|
|
169
|
-
describe("LaunchServicePage — snapshot regression", () => {
|
|
170
|
-
it("empty state renders deterministic HTML", async () => {
|
|
171
|
-
const element = await LaunchServicePage({
|
|
172
|
-
params: Promise.resolve({ locale: "en" }),
|
|
173
|
-
getMdxSource: getMdxSourceEmpty,
|
|
174
|
-
getPageSections: getPageSectionsEmpty,
|
|
175
|
-
});
|
|
176
|
-
const { container } = render(element);
|
|
177
|
-
expect(stripJsonLd(container.innerHTML)).toMatchInlineSnapshot(
|
|
178
|
-
`"<main><h1>Launch Service</h1><p data-testid="section-empty-state">Launch Service page content coming soon.</p></main>"`,
|
|
179
|
-
);
|
|
180
|
-
});
|
|
181
|
-
|
|
182
|
-
it("populated state renders deterministic HTML", async () => {
|
|
183
|
-
const source = makeSource(
|
|
184
|
-
"Service",
|
|
185
|
-
"launch-service",
|
|
186
|
-
"Launch Service",
|
|
187
|
-
"Our launch service offering.",
|
|
188
|
-
);
|
|
189
|
-
const getMdxSourcePopulated = jest.fn().mockResolvedValue(source);
|
|
190
|
-
const getPageSectionsPopulated = jest.fn().mockResolvedValue([]);
|
|
191
|
-
|
|
192
|
-
const element = await LaunchServicePage({
|
|
193
|
-
params: Promise.resolve({ locale: "en" }),
|
|
194
|
-
getMdxSource: getMdxSourcePopulated,
|
|
195
|
-
getPageSections: getPageSectionsPopulated,
|
|
196
|
-
});
|
|
197
|
-
const { container } = render(element);
|
|
198
|
-
expect(stripJsonLd(container.innerHTML)).toMatchInlineSnapshot(
|
|
199
|
-
`"<main><h1>Launch Service</h1><p data-testid="launch-service-description">Our launch service offering.</p><div data-testid="section-renderer" data-section-count="0"></div></main>"`,
|
|
200
|
-
);
|
|
201
|
-
});
|
|
202
|
-
});
|
|
@@ -1,152 +0,0 @@
|
|
|
1
|
-
import React from "react";
|
|
2
|
-
import { render, screen, fireEvent } from "@testing-library/react";
|
|
3
|
-
import "@testing-library/jest-dom";
|
|
4
|
-
|
|
5
|
-
import RootError from "../error";
|
|
6
|
-
import NotFound from "../not-found";
|
|
7
|
-
import ContactError from "../[locale]/contact/error";
|
|
8
|
-
import PurchaseError from "../[locale]/dev/purchase/error";
|
|
9
|
-
|
|
10
|
-
function buildError(message = "Test error"): Error & { digest?: string } {
|
|
11
|
-
const err = new Error(message) as Error & { digest?: string };
|
|
12
|
-
err.digest = "test-digest-1";
|
|
13
|
-
return err;
|
|
14
|
-
}
|
|
15
|
-
|
|
16
|
-
describe("RootError", () => {
|
|
17
|
-
beforeEach(() => {
|
|
18
|
-
jest.spyOn(console, "error").mockImplementation(() => {});
|
|
19
|
-
});
|
|
20
|
-
|
|
21
|
-
afterEach(() => {
|
|
22
|
-
(console.error as jest.Mock).mockRestore();
|
|
23
|
-
});
|
|
24
|
-
|
|
25
|
-
it("renders a fallback heading when an error is provided", () => {
|
|
26
|
-
const err = buildError("Something bad");
|
|
27
|
-
const reset = jest.fn();
|
|
28
|
-
render(<RootError error={err} reset={reset} />);
|
|
29
|
-
expect(screen.getByRole("heading", { level: 1 })).toBeInTheDocument();
|
|
30
|
-
});
|
|
31
|
-
|
|
32
|
-
it("renders a Try again button", () => {
|
|
33
|
-
const err = buildError();
|
|
34
|
-
const reset = jest.fn();
|
|
35
|
-
render(<RootError error={err} reset={reset} />);
|
|
36
|
-
expect(
|
|
37
|
-
screen.getByRole("button", { name: /try again/i }),
|
|
38
|
-
).toBeInTheDocument();
|
|
39
|
-
});
|
|
40
|
-
|
|
41
|
-
it("calls reset when Try again is clicked", () => {
|
|
42
|
-
const err = buildError();
|
|
43
|
-
const reset = jest.fn();
|
|
44
|
-
render(<RootError error={err} reset={reset} />);
|
|
45
|
-
fireEvent.click(screen.getByRole("button", { name: /try again/i }));
|
|
46
|
-
expect(reset).toHaveBeenCalledTimes(1);
|
|
47
|
-
});
|
|
48
|
-
|
|
49
|
-
it("calls console.error with the error on mount", () => {
|
|
50
|
-
const err = buildError("logged error");
|
|
51
|
-
const reset = jest.fn();
|
|
52
|
-
render(<RootError error={err} reset={reset} />);
|
|
53
|
-
expect(console.error).toHaveBeenCalledWith(err);
|
|
54
|
-
});
|
|
55
|
-
});
|
|
56
|
-
|
|
57
|
-
describe("NotFound", () => {
|
|
58
|
-
it("renders a 404 heading", () => {
|
|
59
|
-
render(<NotFound />);
|
|
60
|
-
expect(screen.getByRole("heading", { level: 1 })).toBeInTheDocument();
|
|
61
|
-
});
|
|
62
|
-
|
|
63
|
-
it("renders a link back to the home page", () => {
|
|
64
|
-
render(<NotFound />);
|
|
65
|
-
const link = screen.getByRole("link");
|
|
66
|
-
expect(link).toHaveAttribute("href", "/");
|
|
67
|
-
});
|
|
68
|
-
});
|
|
69
|
-
|
|
70
|
-
describe("ContactError", () => {
|
|
71
|
-
beforeEach(() => {
|
|
72
|
-
jest.spyOn(console, "error").mockImplementation(() => {});
|
|
73
|
-
});
|
|
74
|
-
|
|
75
|
-
afterEach(() => {
|
|
76
|
-
(console.error as jest.Mock).mockRestore();
|
|
77
|
-
});
|
|
78
|
-
|
|
79
|
-
it("renders a heading specific to the lead-capture flow (message/contact)", () => {
|
|
80
|
-
const err = buildError("Contact error");
|
|
81
|
-
const reset = jest.fn();
|
|
82
|
-
render(<ContactError error={err} reset={reset} />);
|
|
83
|
-
const heading = screen.getByRole("heading", { level: 1 });
|
|
84
|
-
expect(heading.textContent?.toLowerCase()).toMatch(/message|contact/);
|
|
85
|
-
});
|
|
86
|
-
|
|
87
|
-
it("does NOT render generic 'something went wrong' as the heading", () => {
|
|
88
|
-
const err = buildError();
|
|
89
|
-
const reset = jest.fn();
|
|
90
|
-
render(<ContactError error={err} reset={reset} />);
|
|
91
|
-
const heading = screen.getByRole("heading", { level: 1 });
|
|
92
|
-
expect(heading.textContent?.toLowerCase()).not.toBe("something went wrong");
|
|
93
|
-
});
|
|
94
|
-
|
|
95
|
-
it("renders a Try again button that calls reset", () => {
|
|
96
|
-
const err = buildError();
|
|
97
|
-
const reset = jest.fn();
|
|
98
|
-
render(<ContactError error={err} reset={reset} />);
|
|
99
|
-
fireEvent.click(screen.getByRole("button", { name: /try again/i }));
|
|
100
|
-
expect(reset).toHaveBeenCalledTimes(1);
|
|
101
|
-
});
|
|
102
|
-
|
|
103
|
-
it("calls console.error with the error on mount", () => {
|
|
104
|
-
const err = buildError("contact logged");
|
|
105
|
-
const reset = jest.fn();
|
|
106
|
-
render(<ContactError error={err} reset={reset} />);
|
|
107
|
-
expect(console.error).toHaveBeenCalledWith(err);
|
|
108
|
-
});
|
|
109
|
-
});
|
|
110
|
-
|
|
111
|
-
describe("PurchaseError", () => {
|
|
112
|
-
beforeEach(() => {
|
|
113
|
-
jest.spyOn(console, "error").mockImplementation(() => {});
|
|
114
|
-
});
|
|
115
|
-
|
|
116
|
-
afterEach(() => {
|
|
117
|
-
(console.error as jest.Mock).mockRestore();
|
|
118
|
-
});
|
|
119
|
-
|
|
120
|
-
it("renders a heading specific to the checkout flow (checkout/purchase)", () => {
|
|
121
|
-
const err = buildError("Purchase error");
|
|
122
|
-
const reset = jest.fn();
|
|
123
|
-
render(<PurchaseError error={err} reset={reset} />);
|
|
124
|
-
const heading = screen.getByRole("heading", { level: 1 });
|
|
125
|
-
expect(heading.textContent?.toLowerCase()).toMatch(/checkout|purchase/);
|
|
126
|
-
});
|
|
127
|
-
|
|
128
|
-
it("does NOT claim payment was or was not processed in the heading", () => {
|
|
129
|
-
const err = buildError();
|
|
130
|
-
const reset = jest.fn();
|
|
131
|
-
render(<PurchaseError error={err} reset={reset} />);
|
|
132
|
-
const heading = screen.getByRole("heading", { level: 1 });
|
|
133
|
-
expect(heading.textContent?.toLowerCase()).not.toMatch(
|
|
134
|
-
/payment (was|not|failed|charged)/,
|
|
135
|
-
);
|
|
136
|
-
});
|
|
137
|
-
|
|
138
|
-
it("renders a Try again button that calls reset", () => {
|
|
139
|
-
const err = buildError();
|
|
140
|
-
const reset = jest.fn();
|
|
141
|
-
render(<PurchaseError error={err} reset={reset} />);
|
|
142
|
-
fireEvent.click(screen.getByRole("button", { name: /try again/i }));
|
|
143
|
-
expect(reset).toHaveBeenCalledTimes(1);
|
|
144
|
-
});
|
|
145
|
-
|
|
146
|
-
it("calls console.error with the error on mount", () => {
|
|
147
|
-
const err = buildError("purchase logged");
|
|
148
|
-
const reset = jest.fn();
|
|
149
|
-
render(<PurchaseError error={err} reset={reset} />);
|
|
150
|
-
expect(console.error).toHaveBeenCalledWith(err);
|
|
151
|
-
});
|
|
152
|
-
});
|
|
@@ -1,15 +0,0 @@
|
|
|
1
|
-
---
|
|
2
|
-
title: Blog
|
|
3
|
-
description: Writing on AEO, answer-engine citation readiness, and what it means to ship software with three peers — UX, API, and MCP.
|
|
4
|
-
author:
|
|
5
|
-
name: The AI Launch Kit Team
|
|
6
|
-
tags:
|
|
7
|
-
- AEO
|
|
8
|
-
- AX
|
|
9
|
-
---
|
|
10
|
-
|
|
11
|
-
# Blog
|
|
12
|
-
|
|
13
|
-
This is where we think out loud. The topics stay close to the work: schema.org implementation details, how LLMs decide what to cite, and what AX-first looks like in practice.
|
|
14
|
-
|
|
15
|
-
The posts are opinionated because vague advice isn't useful. If something here turns out to be wrong, we'll update it and say so.
|
|
@@ -1,20 +0,0 @@
|
|
|
1
|
-
---
|
|
2
|
-
title: Launching AI Launch Kit
|
|
3
|
-
description: A short note on why we built an AX-first OSS template and what ships in v1.
|
|
4
|
-
date: "2026-05-06"
|
|
5
|
-
author:
|
|
6
|
-
name: The AI Launch Kit Team
|
|
7
|
-
tags:
|
|
8
|
-
- Announcements
|
|
9
|
-
- AX
|
|
10
|
-
---
|
|
11
|
-
|
|
12
|
-
# Launching AI Launch Kit
|
|
13
|
-
|
|
14
|
-
We're shipping the first public version today. The template gives you three
|
|
15
|
-
peer apps (web, api, mcp), a content-adapter contract with MDX as the default
|
|
16
|
-
backend, and a JSON-LD pipeline for SEO that doesn't require manual schema.org
|
|
17
|
-
authoring.
|
|
18
|
-
|
|
19
|
-
The goal is clone-to-shipping in under an hour. If you hit friction, the
|
|
20
|
-
README's standalone test should catch it before you do.
|
|
@@ -1,30 +0,0 @@
|
|
|
1
|
-
---
|
|
2
|
-
title: Getting Started
|
|
3
|
-
description: Everything you need to know to get your AI Launch Kit project running.
|
|
4
|
-
---
|
|
5
|
-
|
|
6
|
-
# Getting Started
|
|
7
|
-
|
|
8
|
-
Welcome to AI Launch Kit. This guide walks you through the first steps after cloning the template.
|
|
9
|
-
|
|
10
|
-
## Prerequisites
|
|
11
|
-
|
|
12
|
-
- Node.js 20+
|
|
13
|
-
- pnpm 9+
|
|
14
|
-
|
|
15
|
-
## Quick start
|
|
16
|
-
|
|
17
|
-
```bash
|
|
18
|
-
pnpm install
|
|
19
|
-
pnpm dev
|
|
20
|
-
```
|
|
21
|
-
|
|
22
|
-
The dev server starts three apps in parallel:
|
|
23
|
-
|
|
24
|
-
- **web** (`localhost:3000`) — the Next.js UX surface
|
|
25
|
-
- **api** (`localhost:3001`) — the Fastify REST API
|
|
26
|
-
- **mcp** — the MCP server (stdio transport)
|
|
27
|
-
|
|
28
|
-
## Next steps
|
|
29
|
-
|
|
30
|
-
Replace the seed content in `apps/web/content/en/` with your own pages, blog posts, and docs. Each MDX file maps to a route automatically via Fumadocs.
|
|
File without changes
|
|
@@ -1,85 +0,0 @@
|
|
|
1
|
-
# content/ — MDX Content Authoring
|
|
2
|
-
|
|
3
|
-
Inherits root `CLAUDE.md`. This file owns content-authoring rules.
|
|
4
|
-
|
|
5
|
-
## What lives here
|
|
6
|
-
|
|
7
|
-
File-based MDX content, one file per page, organized by page type:
|
|
8
|
-
|
|
9
|
-
content/
|
|
10
|
-
├── HomePage/home.mdx
|
|
11
|
-
├── ServicesList/services.mdx
|
|
12
|
-
├── Service/<slug>.mdx
|
|
13
|
-
├── BlogList/blog.mdx
|
|
14
|
-
├── BlogPosting/<slug>.mdx
|
|
15
|
-
└── ...
|
|
16
|
-
|
|
17
|
-
For the full list of supported page types and what each emits, see `packages/schema/README.md`. It's the source of truth. For per-page-type component guidance (which `@working-theory/ui` components are idiomatic for each family), see `packages/schema/CLAUDE.md` § `@working-theory/ui` component map.
|
|
18
|
-
|
|
19
|
-
## Frontmatter contract (hard rule)
|
|
20
|
-
|
|
21
|
-
Every MDX file has frontmatter validated at build time against `@working-theory/schema`. Required:
|
|
22
|
-
|
|
23
|
-
- `type` — must match a `SchemaOrgType` key from the schema README
|
|
24
|
-
- `slug` — URL slug, no leading slash
|
|
25
|
-
- `title` — visible page title
|
|
26
|
-
- `description` — meta description and JSON-LD `description`
|
|
27
|
-
|
|
28
|
-
Optional:
|
|
29
|
-
|
|
30
|
-
- `locale` — defaults to `'en'`. Emitted as `inLanguage`.
|
|
31
|
-
- `publishedAt`, `updatedAt`, `image`
|
|
32
|
-
- `schema` — escape hatch (see below)
|
|
33
|
-
|
|
34
|
-
Build fails on missing/invalid frontmatter. Fix the frontmatter, not the validator.
|
|
35
|
-
|
|
36
|
-
## The `schema` field (escape hatch)
|
|
37
|
-
|
|
38
|
-
`schema` is a shallow-merged record. Frontmatter wins on conflicts with the builder. Use it to:
|
|
39
|
-
|
|
40
|
-
- Add structured sub-entities (`author`, `mainEntity`, `step`)
|
|
41
|
-
- Enrich page types whose schema.org default is generic (e.g. Integration → `mainEntity: SoftwareApplication`)
|
|
42
|
-
- Override a derived field (rare)
|
|
43
|
-
|
|
44
|
-
Some types (JobPosting in particular) REQUIRE programmatic overrides for full coverage — frontmatter alone can't express `baseSalary`, `employmentType`. See `packages/schema/README.md` for which types need code-level overrides.
|
|
45
|
-
|
|
46
|
-
After using `schema`, run `ailk audit` — a malformed override produces invalid JSON-LD and tanks AEO scoring.
|
|
47
|
-
|
|
48
|
-
## Component vocabulary (universal — page type doesn't change this)
|
|
49
|
-
|
|
50
|
-
These components are available on any page. Choice depends on content, not page type. An FAQ block belongs in `<FAQ>` whether it's on a HomePage, ServicePage, or FAQPage.
|
|
51
|
-
|
|
52
|
-
- `<Hero>` — top-of-page hero. One per page max.
|
|
53
|
-
- `<FAQ>` — FAQ entry. **One question per block** (see AEO rules below).
|
|
54
|
-
- `<CTA>` — call-to-action card. Use sparingly.
|
|
55
|
-
- `<Testimonial>` — single testimonial. Multiple = multiple blocks.
|
|
56
|
-
- `<PriceTable>` — pricing display.
|
|
57
|
-
- `<FeatureGrid>` — feature/benefit grid.
|
|
58
|
-
- `<Locations>` — physical locations.
|
|
59
|
-
- `<BusinessHours>` — hours of operation.
|
|
60
|
-
|
|
61
|
-
For the per-page-type editorial map (which family of pages typically uses which components), see `packages/schema/CLAUDE.md` § `@working-theory/ui` component map.
|
|
62
|
-
|
|
63
|
-
Don't use raw HTML where a component exists. Components carry schema and styling guarantees; `<div>` soup throws both away.
|
|
64
|
-
|
|
65
|
-
## AEO editorial craft (universal — this is the wedge)
|
|
66
|
-
|
|
67
|
-
**Lead with the answer.** First 60 words of body content state the answer to the page's implicit question. LLM citation extractors read top-down and bail early. Backstory comes after.
|
|
68
|
-
|
|
69
|
-
**One question per `<FAQ>` block.** Don't cram multiple Q&As into one block. Each block becomes one `Question` entity in JSON-LD — separation drives citability.
|
|
70
|
-
|
|
71
|
-
**Entity clarity in the first paragraph.** Name the company, product, location, or person explicitly. "We" and "our service" are entity-opaque. AEO rewards unambiguous attribution.
|
|
72
|
-
|
|
73
|
-
**Concrete claims over adjectives.** "Ships in 48 hours" beats "fast turnaround." Specifics get cited; vagueness gets skipped.
|
|
74
|
-
|
|
75
|
-
**Headings as questions when natural.** H2/H3 phrased as questions (when the content actually answers one) align with passage extraction.
|
|
76
|
-
|
|
77
|
-
**Don't pad for length.** A 400-word page that answers cleanly outperforms a 1,500-word page that buries the answer.
|
|
78
|
-
|
|
79
|
-
## What NOT to do
|
|
80
|
-
|
|
81
|
-
- Don't hand-roll JSON-LD in MDX body. Ever.
|
|
82
|
-
- Don't use `<script type="application/ld+json">` inside content.
|
|
83
|
-
- Don't bypass the frontmatter validator.
|
|
84
|
-
- Don't write `type: WebPage` to silence a validator error — pick the right type.
|
|
85
|
-
- Don't use raw `<h1>` in body. The page template emits the H1 from `title`.
|