create-ailk 0.4.0 → 0.6.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 +55 -8
- package/component-catalog.json +12248 -0
- package/component-catalog.md +193 -68
- package/dist/cli.js +29 -1
- package/dist/derive-architecture.js +5 -2
- package/dist/lib/api-boot-assertion.d.ts +46 -0
- package/dist/lib/api-boot-assertion.js +70 -0
- package/dist/lib/extract-module-bundle.d.ts +50 -2
- package/dist/lib/extract-module-bundle.js +223 -15
- package/dist/lib/fetch-guard.d.ts +28 -0
- package/dist/lib/fetch-guard.js +101 -0
- package/dist/lib/fetch-module.js +4 -0
- package/dist/lib/fetch-template.js +4 -0
- package/dist/lib/import-graph.d.ts +29 -0
- package/dist/lib/import-graph.js +140 -0
- package/dist/module-architecture.d.ts +60 -8
- package/dist/module-architecture.js +587 -242
- package/dist/parse-args.js +6 -2
- package/dist/programmatic.d.ts +10 -0
- package/dist/programmatic.js +134 -19
- package/dist/site-chrome.d.ts +80 -0
- package/dist/site-chrome.js +135 -0
- package/dist/surfaces.d.ts +21 -6
- package/dist/surfaces.js +209 -36
- package/dist/template-manifest.d.ts +88 -3
- package/dist/template-manifest.js +281 -29
- package/dist/template-names.d.ts +1 -1
- package/dist/template-names.js +2 -0
- package/package.json +10 -2
- package/site-templates/_base/ailk-template.json +6 -0
- package/site-templates/_base/content/en/Pricing/pricing.mdx +169 -0
- package/site-templates/marketing-site/TEMPLATE-MANIFEST.json +351 -0
- package/site-templates/marketing-site/TEMPLATE-MANIFEST.md +400 -0
- package/site-templates/marketing-site/ailk-template.json +11 -0
- package/site-templates/marketing-site/content/en/blog/05-this-vc-morning-routine-will-make-you-super-annoying.mdx +36 -0
- package/site-templates/marketing-site/provenance.json +4 -0
- package/site-templates/marketing-site-free/TEMPLATE-MANIFEST.json +288 -0
- package/site-templates/marketing-site-free/TEMPLATE-MANIFEST.md +286 -0
- package/site-templates/marketing-site-free/ailk-template.json +2 -1
- package/site-templates/minimalism-site/TEMPLATE-MANIFEST.json +279 -0
- package/site-templates/minimalism-site/TEMPLATE-MANIFEST.md +336 -0
- package/site-templates/minimalism-site/ailk-template.json +22 -0
- package/site-templates/minimalism-site/assets/avatar-placeholder-1.svg +7 -0
- package/site-templates/minimalism-site/assets/avatar-placeholder-2.svg +7 -0
- package/site-templates/minimalism-site/assets/avatar-placeholder-3.svg +7 -0
- package/site-templates/minimalism-site/assets/avatar-placeholder-4.svg +7 -0
- package/site-templates/minimalism-site/assets/avatar-placeholder-5.svg +7 -0
- package/site-templates/minimalism-site/assets/avatar-placeholder-6.svg +7 -0
- package/site-templates/minimalism-site/assets/hero-backdrop.svg +17 -0
- package/site-templates/minimalism-site/assets/logo-placeholder-1.svg +6 -0
- package/site-templates/minimalism-site/assets/logo-placeholder-2.svg +6 -0
- package/site-templates/minimalism-site/assets/logo-placeholder-3.svg +6 -0
- package/site-templates/minimalism-site/assets/wordmark-placeholder.svg +28 -0
- package/site-templates/minimalism-site/content/en/AboutPage/about.mdx +110 -0
- package/site-templates/minimalism-site/content/en/ContactPage/contact.mdx +68 -0
- package/site-templates/minimalism-site/content/en/FAQPage/faq.mdx +57 -0
- package/site-templates/minimalism-site/content/en/HomePage/home.mdx +244 -0
- package/site-templates/minimalism-site/content/en/LandingPage/referral.mdx +77 -0
- package/site-templates/minimalism-site/content/en/LandingPage/reports.mdx +73 -0
- package/site-templates/minimalism-site/content/en/ReportPage/annual-letter-2024.mdx +52 -0
- package/site-templates/minimalism-site/content/en/ReportPage/field-notes-2023.mdx +52 -0
- package/site-templates/minimalism-site/content/en/ReportPage/platform-shift-2025.mdx +52 -0
- package/site-templates/minimalism-site/content/en/ReportPage/state-of-the-market-2026.mdx +52 -0
- package/site-templates/minimalism-site/content/en/Service/first-engagement.mdx +38 -0
- package/site-templates/minimalism-site/content/en/Service/second-engagement.mdx +38 -0
- package/site-templates/minimalism-site/content/en/Service/third-engagement.mdx +38 -0
- package/site-templates/minimalism-site/content/en/ServicesList/services.mdx +73 -0
- package/site-templates/minimalism-site/flows/referral.flow.json +71 -0
- package/site-templates/minimalism-site/provenance.json +4 -0
- package/site-templates/saas-product/TEMPLATE-MANIFEST.md +61 -0
- package/site-templates/saas-product/ailk-template.json +16 -0
- package/site-templates/saas-product/provenance.json +10 -0
- package/site-templates/simple-service/TEMPLATE-MANIFEST.json +240 -0
- package/site-templates/simple-service/TEMPLATE-MANIFEST.md +278 -0
- package/site-templates/simple-service/ailk-template.json +10 -0
- package/site-templates/simple-service/content/en/AboutPage/about.mdx +38 -0
- package/site-templates/simple-service/content/en/ContactPage/contact.mdx +67 -0
- package/site-templates/simple-service/content/en/FAQPage/faq.mdx +56 -0
- package/site-templates/simple-service/content/en/HomePage/home.mdx +85 -0
- package/site-templates/simple-service/content/en/Service/landscape-design.mdx +40 -0
- package/site-templates/simple-service/content/en/Service/lawn-maintenance.mdx +40 -0
- package/site-templates/simple-service/content/en/Service/seasonal-cleanup.mdx +40 -0
- package/site-templates/simple-service/content/en/ServicesList/services.mdx +72 -0
- package/site-templates/simple-service/provenance.json +4 -0
- package/templates/.claude/skills/README.md +16 -25
- package/templates/.env.example +102 -12
- package/templates/.github/workflows/ci.yml +48 -0
- package/templates/README.md +3 -1
- package/templates/apps/api/.ailk/inbox/2026-09-20T14-57-31.552Z-fallback-1789916251590-037e9005.json +8 -0
- package/templates/apps/api/.ailk/inbox/2026-09-20T14-57-33.840Z-fallback-1789916253842-b52659e9.json +12 -0
- package/templates/apps/api/.ailk/inbox/2026-09-20T14-57-33.844Z-fallback-1789916253845-6b266f8c.json +12 -0
- package/templates/apps/api/.env.example +13 -0
- package/templates/apps/api/CLAUDE.md +29 -2
- package/templates/apps/api/eslint.config.mjs +69 -0
- package/templates/apps/api/jest.config.cjs +3 -0
- package/templates/apps/api/package.json +7 -2
- package/templates/apps/api/src/__tests__/commerce-claim-registration.test.ts +407 -0
- package/templates/apps/api/src/__tests__/module-exclusion.test.ts +89 -9
- package/templates/apps/api/src/__tests__/review-surface-registration.test.ts +167 -0
- package/templates/apps/api/src/__tests__/server.test.ts +471 -0
- package/templates/apps/api/src/__tests__/vercel-handler.test.ts +45 -0
- package/templates/apps/api/src/__tests__/workspace-module-registration.test.ts +10 -1
- package/templates/apps/api/src/config/index.ts +62 -0
- package/templates/apps/api/src/config/modules.ts +7 -3
- package/templates/apps/api/src/lib/__mocks__/prisma.ts +241 -2
- package/templates/apps/api/src/lib/__tests__/auth-email-budget.test.ts +143 -0
- package/templates/apps/api/src/lib/__tests__/content-adapter.test.ts +23 -0
- package/templates/apps/api/src/lib/__tests__/deliverable-asset-store.test.ts +125 -0
- package/templates/apps/api/src/lib/__tests__/error-serializer.test.ts +20 -0
- package/templates/apps/api/src/lib/__tests__/require-commerce-claim.test.ts +317 -0
- package/templates/apps/api/src/lib/__tests__/site.test.ts +238 -0
- package/templates/apps/api/src/lib/__tests__/stripe.test.ts +75 -0
- package/templates/apps/api/src/lib/auth-email-budget.ts +160 -0
- package/templates/apps/api/src/lib/content-adapter.ts +57 -31
- package/templates/apps/api/src/lib/deliverable-asset-store.ts +76 -0
- package/templates/apps/api/src/lib/error-serializer.ts +23 -3
- package/templates/apps/api/src/lib/outbound-http.ts +7 -2
- package/templates/apps/api/src/lib/require-commerce-claim.ts +250 -0
- package/templates/apps/api/src/lib/site.ts +208 -9
- package/templates/apps/api/src/lib/stripe.ts +66 -0
- package/templates/apps/api/src/middleware/__tests__/default-tenant.test.ts +521 -0
- package/templates/apps/api/src/middleware/__tests__/rate-limit.test.ts +150 -0
- package/templates/apps/api/src/middleware/default-tenant.ts +136 -0
- package/templates/apps/api/src/middleware/rate-limit.ts +7 -1
- package/templates/apps/api/src/openapi/spec.ts +543 -2
- package/templates/apps/api/src/routes/auth/__tests__/auth-user.functional.test.ts +48 -0
- package/templates/apps/api/src/routes/auth/__tests__/transactional-email.test.ts +309 -0
- package/templates/apps/api/src/routes/auth/__tests__/user.test.ts +123 -0
- package/templates/apps/api/src/routes/auth/transactional-email.ts +273 -0
- package/templates/apps/api/src/routes/auth/user.ts +61 -0
- package/templates/apps/api/src/routes/checkout/__tests__/session.test.ts +434 -0
- package/templates/apps/api/src/routes/checkout/__tests__/sessions.test.ts +888 -0
- package/templates/apps/api/src/routes/checkout/index.ts +34 -0
- package/templates/apps/api/src/routes/checkout/session.ts +139 -0
- package/templates/apps/api/src/routes/checkout/sessions.ts +197 -0
- package/templates/apps/api/src/routes/content/__tests__/get-page.test.ts +554 -0
- package/templates/apps/api/src/routes/content/__tests__/list-pages.test.ts +108 -0
- package/templates/apps/api/src/routes/content/__tests__/search.test.ts +450 -0
- package/templates/apps/api/src/routes/content/get-page.ts +94 -12
- package/templates/apps/api/src/routes/content/index.ts +13 -73
- package/templates/apps/api/src/routes/content/list-pages.ts +15 -3
- package/templates/apps/api/src/routes/content/rate-limit.ts +15 -6
- package/templates/apps/api/src/routes/content/search.ts +129 -0
- package/templates/apps/api/src/routes/leads/__tests__/index.test.ts +447 -31
- package/templates/apps/api/src/routes/leads/__tests__/list.test.ts +171 -0
- package/templates/apps/api/src/routes/leads/index.ts +100 -13
- package/templates/apps/api/src/routes/leads/list.ts +104 -0
- package/templates/apps/api/src/routes/project-listings/__tests__/configured-application.test.ts +51 -0
- package/templates/apps/api/src/routes/project-listings/__tests__/drafts.test.ts +351 -0
- package/templates/apps/api/src/routes/project-listings/__tests__/founder-identity.test.ts +51 -0
- package/templates/apps/api/src/routes/project-listings/__tests__/me.test.ts +5 -2
- package/templates/apps/api/src/routes/project-listings/__tests__/public.test.ts +4 -1
- package/templates/apps/api/src/routes/project-listings/__tests__/resume-email.test.ts +58 -0
- package/templates/apps/api/src/routes/project-listings/__tests__/site-answers.test.ts +107 -6
- package/templates/apps/api/src/routes/project-listings/__tests__/site-key.test.ts +87 -2
- package/templates/apps/api/src/routes/project-listings/__tests__/structured-address.test.ts +51 -0
- package/templates/apps/api/src/routes/project-listings/start.ts +5 -1
- package/templates/apps/api/src/routes/requests/__tests__/confirm.test.ts +686 -0
- package/templates/apps/api/src/routes/requests/confirm.ts +601 -0
- package/templates/apps/api/src/routes/requests/index.ts +54 -0
- package/templates/apps/api/src/routes/webhooks/README.md +265 -0
- package/templates/apps/api/src/routes/webhooks/__tests__/stripe-dunning.test.ts +749 -0
- package/templates/apps/api/src/routes/webhooks/__tests__/stripe-flow-checkout.test.ts +330 -0
- package/templates/apps/api/src/routes/webhooks/__tests__/stripe-idempotency.test.ts +262 -0
- package/templates/apps/api/src/routes/webhooks/__tests__/stripe-org-billing.test.ts +285 -0
- package/templates/apps/api/src/routes/webhooks/__tests__/stripe-rate-limit.test.ts +213 -0
- package/templates/apps/api/src/routes/webhooks/__tests__/stripe-request-paid.test.ts +301 -0
- package/templates/apps/api/src/routes/webhooks/__tests__/stripe.test.ts +648 -0
- package/templates/apps/api/src/routes/webhooks/index.ts +89 -0
- package/templates/apps/api/src/routes/webhooks/stripe.ts +1399 -0
- package/templates/apps/api/src/server.ts +521 -55
- package/templates/apps/api/src/services/__tests__/abandon-email.test.ts +39 -0
- package/templates/apps/api/src/services/__tests__/aeo-score.test.ts +217 -40
- package/templates/apps/api/src/services/__tests__/audit-log.test.ts +620 -0
- package/templates/apps/api/src/services/__tests__/consent.test.ts +2 -0
- package/templates/apps/api/src/services/__tests__/deliverable-fulfillment.test.ts +300 -5
- package/templates/apps/api/src/services/__tests__/deliverable-resend.test.ts +20 -0
- package/templates/apps/api/src/services/__tests__/dunning-policy.test.ts +368 -0
- package/templates/apps/api/src/services/__tests__/lead-promotion.test.ts +408 -0
- package/templates/apps/api/src/services/__tests__/lead-routing.test.ts +160 -0
- package/templates/apps/api/src/services/__tests__/leads-inbox.test.ts +329 -0
- package/templates/apps/api/src/services/__tests__/listing-config.test.ts +166 -0
- package/templates/apps/api/src/services/__tests__/project-listings.fence.test.ts +154 -0
- package/templates/apps/api/src/services/__tests__/public-site-resolver.test.ts +137 -0
- package/templates/apps/api/src/services/__tests__/request-confirm.test.ts +444 -0
- package/templates/apps/api/src/services/__tests__/revalidation.test.ts +238 -0
- package/templates/apps/api/src/services/__tests__/verified-email.test.ts +78 -0
- package/templates/apps/api/src/services/abandon-email.ts +8 -2
- package/templates/apps/api/src/services/aeo-score.ts +49 -12
- package/templates/apps/api/src/services/audit-log.ts +668 -0
- package/templates/apps/api/src/services/consent.ts +18 -0
- package/templates/apps/api/src/services/default-tenant.ts +300 -0
- package/templates/apps/api/src/services/deliverable-fulfillment.ts +141 -13
- package/templates/apps/api/src/services/dunning-policy.ts +294 -0
- package/templates/apps/api/src/services/entitlements.ts +80 -0
- package/templates/apps/api/src/services/lead-promotion.ts +160 -0
- package/templates/apps/api/src/services/lead-routing.ts +161 -2
- package/templates/apps/api/src/services/leads-inbox.ts +284 -0
- package/templates/apps/api/src/services/listing-config.ts +27 -9
- package/templates/apps/api/src/services/owner-workspaces.ts +132 -0
- package/templates/apps/api/src/services/project-listings.ts +201 -6
- package/templates/apps/api/src/services/public-site-resolver.ts +106 -0
- package/templates/apps/api/src/services/request-confirm.ts +646 -0
- package/templates/apps/api/src/services/revalidation.ts +173 -0
- package/templates/apps/api/src/services/verified-email.ts +71 -0
- package/templates/apps/api/src/vercel-handler.ts +75 -35
- package/templates/apps/mcp/.env.example +17 -0
- package/templates/apps/mcp/CLAUDE.md +58 -25
- package/templates/apps/mcp/__tests__/aeo-cli-parity.test.ts +6 -0
- package/templates/apps/mcp/__tests__/audit.test.ts +137 -8
- package/templates/apps/mcp/__tests__/catalog-drift.test.ts +79 -36
- package/templates/apps/mcp/__tests__/get_offerings.test.ts +111 -2
- package/templates/apps/mcp/__tests__/get_pricing.test.ts +82 -2
- package/templates/apps/mcp/__tests__/http-transport.test.ts +541 -2
- package/templates/apps/mcp/__tests__/index.test.ts +39 -0
- package/templates/apps/mcp/__tests__/lib/audit-single-page-readiness-scan.test.ts +143 -0
- package/templates/apps/mcp/__tests__/lib/commerce-claim.test.ts +111 -0
- package/templates/apps/mcp/__tests__/lib/content-adapter-parity.test.ts +65 -0
- package/templates/apps/mcp/__tests__/lib/content-adapter.test.ts +188 -0
- package/templates/apps/mcp/__tests__/lib/fail-closed-tools.test.ts +165 -0
- package/templates/apps/mcp/__tests__/lib/public-workspace.test.ts +124 -0
- package/templates/apps/mcp/__tests__/list_pages.test.ts +116 -0
- package/templates/apps/mcp/__tests__/module-exclusion.test.ts +7 -1
- package/templates/apps/mcp/__tests__/parity.test.ts +12 -9
- package/templates/apps/mcp/__tests__/purchase-disabled.test.ts +89 -22
- package/templates/apps/mcp/__tests__/read_content.test.ts +322 -1
- package/templates/apps/mcp/__tests__/search_content.test.ts +105 -2
- package/templates/apps/mcp/__tests__/server.test.ts +67 -14
- package/templates/apps/mcp/__tests__/start_purchase.test.ts +252 -0
- package/templates/apps/mcp/__tests__/tools/{scaffold.test.ts → create_project.test.ts} +23 -23
- package/templates/apps/mcp/__tests__/tools/purchase.test.ts +300 -123
- package/templates/apps/mcp/eslint.config.mjs +69 -0
- package/templates/apps/mcp/jest.config.cjs +13 -0
- package/templates/apps/mcp/package.json +8 -2
- package/templates/apps/mcp/src/abuse/index.ts +159 -24
- package/templates/apps/mcp/src/config/modules.ts +10 -5
- package/templates/apps/mcp/src/http-transport.ts +102 -29
- package/templates/apps/mcp/src/index.ts +73 -11
- package/templates/apps/mcp/src/lib/audit-single-page.ts +90 -3
- package/templates/apps/mcp/src/lib/commerce-claim.ts +94 -0
- package/templates/apps/mcp/src/lib/content-adapter.ts +113 -0
- package/templates/apps/mcp/src/lib/empty-content-adapter.ts +139 -0
- package/templates/apps/mcp/src/lib/prisma.ts +39 -0
- package/templates/apps/mcp/src/lib/public-workspace.ts +133 -0
- package/templates/apps/mcp/src/lib/stripe-tax-signal.ts +59 -0
- package/templates/apps/mcp/src/server.ts +61 -37
- package/templates/apps/mcp/src/tools/audit.ts +26 -2
- package/templates/apps/mcp/src/tools/{scaffold.ts → create_project.ts} +3 -3
- package/templates/apps/mcp/src/tools/get_business_hours.ts +4 -3
- package/templates/apps/mcp/src/tools/get_faq.ts +4 -3
- package/templates/apps/mcp/src/tools/get_locations.ts +5 -3
- package/templates/apps/mcp/src/tools/get_offerings.ts +16 -4
- package/templates/apps/mcp/src/tools/get_pricing.ts +25 -6
- package/templates/apps/mcp/src/tools/get_site_info.ts +4 -3
- package/templates/apps/mcp/src/tools/index.ts +9 -7
- package/templates/apps/mcp/src/tools/list_pages.ts +4 -3
- package/templates/apps/mcp/src/tools/purchase.ts +128 -45
- package/templates/apps/mcp/src/tools/read_content.ts +20 -6
- package/templates/apps/mcp/src/tools/search_content.ts +11 -4
- package/templates/apps/web/.env.example +74 -8
- package/templates/apps/web/CLAUDE.md +46 -0
- package/templates/apps/web/app/.well-known/mcp.json/build-manifest.ts +18 -2
- package/templates/apps/web/app/.well-known/mcp.json/route.ts +11 -3
- package/templates/apps/web/app/[locale]/(auth)/__tests__/auth-pages.test.tsx +282 -0
- package/templates/apps/web/app/[locale]/(auth)/__tests__/password-reset.test.tsx +515 -0
- package/templates/apps/web/app/[locale]/(auth)/forgot-password/forgot-password-card.tsx +89 -0
- package/templates/apps/web/app/[locale]/(auth)/forgot-password/page.tsx +109 -0
- package/templates/apps/web/app/[locale]/(auth)/layout.tsx +92 -0
- package/templates/apps/web/app/[locale]/(auth)/reset-password/page.tsx +135 -0
- package/templates/apps/web/app/[locale]/(auth)/reset-password/reset-password-card.tsx +109 -0
- package/templates/apps/web/app/[locale]/{sign-in → (auth)/sign-in}/__tests__/page.test.tsx +48 -2
- package/templates/apps/web/app/[locale]/{sign-in → (auth)/sign-in}/page.tsx +20 -6
- package/templates/apps/web/app/[locale]/(auth)/sign-in/sign-in-card.tsx +125 -0
- package/templates/apps/web/app/[locale]/(auth)/sign-up/page.tsx +96 -0
- package/templates/apps/web/app/[locale]/(auth)/sign-up/sign-up-card.tsx +101 -0
- package/templates/apps/web/app/[locale]/(auth)/use-auth-card-flow.ts +102 -0
- package/templates/apps/web/app/[locale]/(auth)/verify-email/page.tsx +87 -0
- package/templates/apps/web/app/[locale]/(authed)/__tests__/gate.test.tsx +20 -10
- package/templates/apps/web/app/[locale]/(authed)/account/__tests__/page.test.tsx +84 -0
- package/templates/apps/web/app/[locale]/(authed)/account/actions.ts +58 -0
- package/templates/apps/web/app/[locale]/(authed)/account/page.tsx +67 -14
- package/templates/apps/web/app/[locale]/(authed)/layout.tsx +19 -2
- package/templates/apps/web/app/[locale]/(authed)/leads/__tests__/page.test.tsx +181 -0
- package/templates/apps/web/app/[locale]/(authed)/leads/components/LeadsTable.tsx +137 -0
- package/templates/apps/web/app/[locale]/(authed)/leads/page.tsx +111 -0
- package/templates/apps/web/app/[locale]/(authed)/sign-in-gate.tsx +1 -1
- package/templates/apps/web/app/[locale]/[slug]/__tests__/page.test.tsx +111 -0
- package/templates/apps/web/app/[locale]/[slug]/page.tsx +65 -0
- package/templates/apps/web/app/[locale]/cancellation/page.tsx +44 -0
- package/templates/apps/web/app/[locale]/flows/[slug]/FlowStepperClient.tsx +1 -1
- package/templates/apps/web/app/[locale]/flows/[slug]/__tests__/FlowStepperClient.permalink.test.tsx +1 -1
- package/templates/apps/web/app/[locale]/flows/[slug]/__tests__/FlowStepperClient.test.tsx +6 -5
- package/templates/apps/web/app/[locale]/flows/[slug]/flow.actions.ts +1 -1
- package/templates/apps/web/app/[locale]/layout.tsx +308 -65
- package/templates/apps/web/app/[locale]/purchase/__tests__/cancel.test.tsx +162 -0
- package/templates/apps/web/app/[locale]/purchase/__tests__/module-scope.test.ts +68 -0
- package/templates/apps/web/app/[locale]/purchase/__tests__/success.test.tsx +361 -0
- package/templates/apps/web/app/[locale]/purchase/cancel/page.tsx +115 -0
- package/templates/apps/web/app/[locale]/purchase/success/page.tsx +307 -0
- package/templates/apps/web/app/[locale]/returns/page.tsx +44 -0
- package/templates/apps/web/app/[locale]/shipping/page.tsx +44 -0
- package/templates/apps/web/app/api/revalidate/__tests__/route.test.ts +670 -0
- package/templates/apps/web/app/api/revalidate/route.ts +194 -0
- package/templates/apps/web/auth.ts +407 -8
- package/templates/apps/web/components/google-analytics.tsx +18 -1
- package/templates/apps/web/eslint.config.mjs +69 -0
- package/templates/apps/web/jest.config.cjs +15 -19
- package/templates/apps/web/lib/__tests__/auth-email.test.ts +214 -0
- package/templates/apps/web/lib/__tests__/auth-routes.test.ts +74 -0
- package/templates/apps/web/lib/__tests__/meeting-slugs.test.ts +72 -0
- package/templates/apps/web/lib/__tests__/structured-data.test.tsx +50 -6
- package/templates/apps/web/lib/auth-email.ts +318 -0
- package/templates/apps/web/lib/auth-env.ts +85 -0
- package/templates/apps/web/lib/auth-routes.ts +55 -0
- package/templates/apps/web/lib/meeting-slugs.ts +53 -0
- package/templates/apps/web/lib/nav-config.ts +27 -7
- package/templates/apps/web/lib/server-api.ts +1 -1
- package/templates/apps/web/lib/site-chrome.ts +29 -0
- package/templates/apps/web/lib/telemetry-scrub.ts +50 -0
- package/templates/apps/web/messages/en.json +216 -2
- package/templates/apps/web/next.config.mjs +20 -0
- package/templates/apps/web/package.json +8 -3
- package/templates/apps/web/sentry.client.config.ts +131 -0
- package/templates/apps/web/sentry.edge.config.ts +72 -0
- package/templates/apps/web/sentry.server.config.ts +72 -0
- package/templates/apps/web/src/lib/commerce-claim.ts +33 -0
- package/templates/apps/web/src/lib/commerce-sections.ts +61 -0
- package/templates/apps/web/src/lib/routes.ts +13 -18
- package/templates/brand_assets/README.md +24 -0
- package/templates/content/en/AboutPage/about.mdx +160 -15
- package/templates/content/en/CancellationPolicyPage/cancellation.mdx +108 -0
- package/templates/content/en/ContactPage/contact.mdx +90 -13
- package/templates/content/en/FAQPage/faq.mdx +62 -22
- package/templates/content/en/HomePage/home.mdx +244 -20
- package/templates/content/en/LandingPage/blog.mdx +83 -0
- package/templates/content/en/LandingPage/features.mdx +103 -0
- package/templates/content/en/LandingPage/how-it-works.mdx +143 -0
- package/templates/content/en/ReturnsPolicyPage/returns.mdx +108 -0
- package/templates/content/en/ShippingPolicyPage/shipping.mdx +108 -0
- package/templates/database/CHANGELOG.md +149 -0
- package/templates/database/README.md +10 -3
- package/templates/database/__tests__/appointment.db.test.ts +106 -0
- package/templates/database/__tests__/artifact-content-store.db.test.ts +316 -0
- package/templates/database/__tests__/composite-fence.db.test.ts +206 -0
- package/templates/database/__tests__/fence-fan-out.db.test.ts +711 -0
- package/templates/database/__tests__/fixtures/appointment-create-probe.ts +79 -0
- package/templates/database/__tests__/fixtures/project-listing-consent-probe.ts +100 -0
- package/templates/database/__tests__/flow-workspace-key.db.test.ts +263 -0
- package/templates/database/__tests__/prefixed-ids.db.test.ts +85 -9
- package/templates/database/__tests__/project-listing-consent-at.db.test.ts +111 -0
- package/templates/database/__tests__/seed.test.ts +1 -1
- package/templates/database/__tests__/tenancy.test.ts +141 -0
- package/templates/database/config/schema.prisma +79 -2
- package/templates/database/content/schema.prisma +87 -11
- package/templates/database/eslint.config.mjs +69 -0
- package/templates/database/inbox/schema.prisma +452 -9
- package/templates/database/jest.config.cjs +5 -3
- package/templates/database/migrations/20260915120000_project_listing_solution/migration.sql +15 -0
- package/templates/database/migrations/20260916120000_artifact_content_store/migration.sql +158 -0
- package/templates/database/migrations/20260917120000_callback_request_confirm/migration.sql +40 -0
- package/templates/database/migrations/20260918120000_add_appointment/migration.sql +67 -0
- package/templates/database/migrations/20260918120000_asset_metadata_columns/migration.sql +70 -0
- package/templates/database/migrations/20260919120000_flow_workspace_key/migration.sql +181 -0
- package/templates/database/migrations/20260919130000_flow_definition_admin_columns/migration.sql +172 -0
- package/templates/database/migrations/20260919130000_workspace_meetings/migration.sql +41 -0
- package/templates/database/migrations/20260919140000_inbox_workspace_key/migration.sql +185 -0
- package/templates/database/migrations/20260920120000_lead_handled_at/migration.sql +70 -0
- package/templates/database/migrations/20260920130000_lead_workspace_source_index/migration.sql +57 -0
- package/templates/database/migrations/20260921120000_deliverable_grant_workspace_key/migration.sql +68 -0
- package/templates/database/migrations/20260922120000_tenancy_fence/migration.sql +1067 -0
- package/templates/database/migrations/20260923120000_listing_capturing_magnet/migration.sql +210 -0
- package/templates/database/package.json +7 -3
- package/templates/database/seed.ts +1 -1
- package/templates/database/tenancy.ts +86 -1
- package/templates/database/tsconfig.json +1 -0
- package/templates/package.json +1 -14
- package/versions.json +52 -0
- package/site-templates/marketing-site-free/content/en/Pricing/pricing.mdx +0 -103
- package/templates/apps/api/src/routes/content/__tests__/create.test.ts +0 -253
- package/templates/apps/api/src/routes/content/__tests__/delete.test.ts +0 -215
- package/templates/apps/api/src/routes/content/__tests__/update.test.ts +0 -266
- package/templates/apps/api/src/routes/content/create.ts +0 -115
- package/templates/apps/api/src/routes/content/delete.ts +0 -82
- package/templates/apps/api/src/routes/content/update.ts +0 -112
- package/templates/apps/mcp/__tests__/create_page.test.ts +0 -202
- package/templates/apps/mcp/__tests__/delete_page.test.ts +0 -177
- package/templates/apps/mcp/__tests__/update_page.test.ts +0 -196
- package/templates/apps/mcp/src/tools/create_page.ts +0 -96
- package/templates/apps/mcp/src/tools/delete_page.ts +0 -74
- package/templates/apps/mcp/src/tools/update_page.ts +0 -92
- package/templates/apps/web/app/[locale]/sign-in/sign-in-card.tsx +0 -123
- /package/site-templates/{marketing-site-free → _base}/content/en/PrivacyPage/privacy.mdx +0 -0
- /package/site-templates/{marketing-site-free → _base}/content/en/TermsPage/terms.mdx +0 -0
- /package/templates/apps/web/app/[locale]/{sign-in → (auth)/sign-in}/__tests__/next-path.test.ts +0 -0
- /package/templates/apps/web/app/[locale]/{sign-in → (auth)/sign-in}/next-path.ts +0 -0
- /package/templates/apps/web/{src/components → components}/SiteNav.tsx +0 -0
package/dist/surfaces.js
CHANGED
|
@@ -49,6 +49,12 @@ export const CORE_SURFACE_PATHS = [
|
|
|
49
49
|
"/contact",
|
|
50
50
|
"/faq",
|
|
51
51
|
"/thanks/{slug}",
|
|
52
|
+
// #5469 D2 — the one generic content route: `app/[locale]/[slug]/page.tsx`
|
|
53
|
+
// renders `content/<locale>/LandingPage/<slug>.mdx` and 404s otherwise.
|
|
54
|
+
// Next.js resolves a static segment before a dynamic one, so it never
|
|
55
|
+
// shadows a scaffolded route directory — which is exactly what lets a
|
|
56
|
+
// module's own route (the docs-blog `blog/`) win over a same-named MDX.
|
|
57
|
+
"/{slug}",
|
|
52
58
|
];
|
|
53
59
|
/** Template-relative route directories that are always copied. */
|
|
54
60
|
export const CORE_ROUTE_DIRS = [
|
|
@@ -60,7 +66,32 @@ export const CORE_ROUTE_DIRS = [
|
|
|
60
66
|
// copies all of apps/web verbatim, no `--surfaces` gating on this route),
|
|
61
67
|
// so joining CORE_ROUTE_DIRS only makes the REGISTRY match what already
|
|
62
68
|
// ships — it does not change what a fresh scaffold carries.
|
|
63
|
-
|
|
69
|
+
//
|
|
70
|
+
// #5963 moved `sign-in` INTO the `(auth)` route group and added four
|
|
71
|
+
// siblings, so the group is what is listed now. It stays CORE rather than
|
|
72
|
+
// taking its own surface id the way `(authed)` did: `(authed)` is gated
|
|
73
|
+
// authed-app surface reported under its own identity, whereas these five
|
|
74
|
+
// are the public auth entry points that have always shipped ungated —
|
|
75
|
+
// registering the group keeps `sign-in`'s existing `core` identity exactly.
|
|
76
|
+
`${ROUTE_ROOT}/(auth)`,
|
|
77
|
+
// #5469 D2 — always copied: it is what serves the Free template's
|
|
78
|
+
// /how-it-works, /features and /blog pages, and it is inert for every slug
|
|
79
|
+
// that has no LandingPage MDX (notFound), so a scaffold carrying it and no
|
|
80
|
+
// content is byte-for-byte a scaffold with three more 404s, nothing else.
|
|
81
|
+
`${ROUTE_ROOT}/[slug]`,
|
|
82
|
+
// #6084 D12 (the operator's shape-2 disposition) — the checkout return
|
|
83
|
+
// pair. The launchers pointing at them (`apps/mcp/src/tools/purchase.ts`,
|
|
84
|
+
// `apps/web/.../dev/purchase/purchase.actions.ts`) are ALREADY
|
|
85
|
+
// unconditional base-tree code under D-64, so gating the return pages
|
|
86
|
+
// behind a scaffold-time surface would leave a licence-added commerce
|
|
87
|
+
// site with a working checkout and a 404 return page — the exact defect
|
|
88
|
+
// this issue exists to close. `CORE_ROUTE_DIRS` is the right home for the
|
|
89
|
+
// same reason `[slug]` immediately above is: inert (a request-time
|
|
90
|
+
// `notFound()`, `src/lib/commerce-claim.ts`) for every scaffold that never
|
|
91
|
+
// buys the `commerce` claim, so carrying it costs nothing beyond two more
|
|
92
|
+
// 404s. Neither `GATEABLE_SURFACES` nor `MODULE_SURFACES.commerce` gains
|
|
93
|
+
// an entry — the claim decides whether the pages serve, not the scaffold.
|
|
94
|
+
`${ROUTE_ROOT}/purchase`,
|
|
64
95
|
];
|
|
65
96
|
/**
|
|
66
97
|
* Directory names skipped by `listTemplateRouteDirs`'s walker even though they
|
|
@@ -159,6 +190,22 @@ export const GATEABLE_SURFACES = [
|
|
|
159
190
|
routes: [`${ROUTE_ROOT}/launch-service`],
|
|
160
191
|
content: [],
|
|
161
192
|
},
|
|
193
|
+
{
|
|
194
|
+
// The `scheduling` MODULE's presentation tier — the booking page, the
|
|
195
|
+
// per-booking-type page and the confirmation page the module OWNS
|
|
196
|
+
// (`/meet`, `/meet/:slug`, `/meet/confirmed`, one directory, so one entry
|
|
197
|
+
// gates all three; renamed from the schedule/ directory by #5563).
|
|
198
|
+
// API-backed like `flows`: the booker reads its slots from apps/api's
|
|
199
|
+
// `/v1/schedule/*` routes and the event name from `SCHEDULING_CAL_LINK`,
|
|
200
|
+
// so there is no MDX of its own.
|
|
201
|
+
id: "meet",
|
|
202
|
+
routes: [`${ROUTE_ROOT}/meet`],
|
|
203
|
+
content: [],
|
|
204
|
+
// `navLabelKey` names COPY, not a route — D1 R2 (#5563): renaming it would
|
|
205
|
+
// orphan an operator's customised `messages/<locale>.json` for zero
|
|
206
|
+
// functional gain, since no route derives from a message key.
|
|
207
|
+
navLabelKey: "schedule",
|
|
208
|
+
},
|
|
162
209
|
{
|
|
163
210
|
id: "partners",
|
|
164
211
|
routes: [`${ROUTE_ROOT}/partners`],
|
|
@@ -195,17 +242,6 @@ export const GATEABLE_SURFACES = [
|
|
|
195
242
|
"content/<locale>/ResourceDetail",
|
|
196
243
|
],
|
|
197
244
|
},
|
|
198
|
-
{
|
|
199
|
-
// The `scheduling` MODULE's presentation tier — the booking page and the
|
|
200
|
-
// confirmation page the module OWNS (`/schedule` + `/schedule/confirmed`,
|
|
201
|
-
// one directory, so one entry gates both). API-backed like `flows`: the
|
|
202
|
-
// booker reads its slots from apps/api's `/v1/schedule/*` routes and the
|
|
203
|
-
// event name from `SCHEDULING_CAL_LINK`, so there is no MDX of its own.
|
|
204
|
-
id: "schedule",
|
|
205
|
-
routes: [`${ROUTE_ROOT}/schedule`],
|
|
206
|
-
content: [],
|
|
207
|
-
navLabelKey: "schedule",
|
|
208
|
-
},
|
|
209
245
|
{
|
|
210
246
|
id: "services",
|
|
211
247
|
routes: [`${ROUTE_ROOT}/services`],
|
|
@@ -244,6 +280,18 @@ export const GATEABLE_SURFACES = [
|
|
|
244
280
|
];
|
|
245
281
|
/** The recognized `--surfaces` vocabulary, sorted. */
|
|
246
282
|
export const RECOGNIZED_SURFACES = GATEABLE_SURFACES.map((s) => s.id);
|
|
283
|
+
/**
|
|
284
|
+
* Retired surface ids, kept for ONE release (#5563). Removal rides the same
|
|
285
|
+
* trigger as the `/schedule` legacyRoutes entries in routes-map.ts — see the
|
|
286
|
+
* comment on that row. Resolving through an alias prints a deprecation notice.
|
|
287
|
+
*/
|
|
288
|
+
export const SURFACE_ALIASES = {
|
|
289
|
+
schedule: "meet",
|
|
290
|
+
};
|
|
291
|
+
/** Canonical id for a `--surfaces` value, resolving a retired alias. */
|
|
292
|
+
function canonicalizeSurface(s) {
|
|
293
|
+
return SURFACE_ALIASES[s] ?? s;
|
|
294
|
+
}
|
|
247
295
|
/**
|
|
248
296
|
* Surfaces each recognized module contributes.
|
|
249
297
|
*
|
|
@@ -271,7 +319,7 @@ export const MODULE_SURFACES = {
|
|
|
271
319
|
// carries an empty list. The entry itself is still required: surface-gating's
|
|
272
320
|
// vocabulary assertion covers exactly the recognized module set.
|
|
273
321
|
workspace: [],
|
|
274
|
-
scheduling: ["
|
|
322
|
+
scheduling: ["meet"],
|
|
275
323
|
"signal-collectors": ["flows"],
|
|
276
324
|
commerce: [],
|
|
277
325
|
};
|
|
@@ -297,7 +345,7 @@ export function parseSurfaces(raw) {
|
|
|
297
345
|
return [];
|
|
298
346
|
const requested = raw
|
|
299
347
|
.split(",")
|
|
300
|
-
.map((s) => s.trim().toLowerCase())
|
|
348
|
+
.map((s) => canonicalizeSurface(s.trim().toLowerCase()))
|
|
301
349
|
.filter(Boolean);
|
|
302
350
|
for (const s of requested) {
|
|
303
351
|
if (!RECOGNIZED_SURFACES.includes(s)) {
|
|
@@ -326,7 +374,8 @@ export function resolveSurfaces(modules = ["marketing"], explicit = []) {
|
|
|
326
374
|
set.add(s);
|
|
327
375
|
}
|
|
328
376
|
const selected = new Set(modules.map((m) => m.trim().toLowerCase()));
|
|
329
|
-
for (const
|
|
377
|
+
for (const raw of explicit) {
|
|
378
|
+
const s = canonicalizeSurface(raw);
|
|
330
379
|
if (!RECOGNIZED_SURFACES.includes(s)) {
|
|
331
380
|
throw new SurfaceError(`unknown surface "${s}". Recognized: ${RECOGNIZED_SURFACES.join(", ")}`);
|
|
332
381
|
}
|
|
@@ -431,6 +480,12 @@ export const ROUTE_SINCE = {
|
|
|
431
480
|
integrations: "0.0.0",
|
|
432
481
|
"integrations/[slug]": "0.0.0",
|
|
433
482
|
"launch-service": "0.0.0",
|
|
483
|
+
meet: "0.0.0",
|
|
484
|
+
// #5563 — the genuinely new row: a per-booking-type page with no
|
|
485
|
+
// predecessor, so it attaches at the create-ailk minor this rename ships
|
|
486
|
+
// in rather than the "predates the manifest" 0.0.0 above.
|
|
487
|
+
"meet/[slug]": "0.5.0",
|
|
488
|
+
"meet/confirmed": "0.0.0",
|
|
434
489
|
partners: "0.0.0",
|
|
435
490
|
"partners/[slug]": "0.0.0",
|
|
436
491
|
// #5479 — three genuinely new route directories, so they attach at today's
|
|
@@ -447,10 +502,13 @@ export const ROUTE_SINCE = {
|
|
|
447
502
|
// question set is the SITE's content. A scaffolded site adds its own route
|
|
448
503
|
// beside this file and passes its own config.
|
|
449
504
|
"projects/apply": "0.18.0",
|
|
505
|
+
// #6084 — two genuinely new route directories, attaching at today's
|
|
506
|
+
// fixed-group version, the same "attach at today's version" rule #5977,
|
|
507
|
+
// #5678, #5479, #5371 and #5048 applied.
|
|
508
|
+
"purchase/success": "0.24.0",
|
|
509
|
+
"purchase/cancel": "0.24.0",
|
|
450
510
|
resources: "0.0.0",
|
|
451
511
|
"resources/[slug]": "0.0.0",
|
|
452
|
-
schedule: "0.0.0",
|
|
453
|
-
"schedule/confirmed": "0.0.0",
|
|
454
512
|
services: "0.0.0",
|
|
455
513
|
"services/[slug]": "0.0.0",
|
|
456
514
|
software: "0.0.0",
|
|
@@ -474,16 +532,57 @@ export const ROUTE_SINCE = {
|
|
|
474
532
|
// choice #5048's "projects/apply" entry already made for the same reason.
|
|
475
533
|
"(authed)": "0.18.0",
|
|
476
534
|
"(authed)/account": "0.18.0",
|
|
535
|
+
// The leads inbox (#5678). The ROUTE ships in every scaffold, Free
|
|
536
|
+
// included — lead capture is a never-gate surface, so reading a lead back
|
|
537
|
+
// is one too. Whether it shows a ROW is a separate question with two
|
|
538
|
+
// deployment preconditions (a resolvable workspace, and a `siteId` that
|
|
539
|
+
// equals a workspace id); both fail empty, never wide. See
|
|
540
|
+
// `apps/api/src/services/leads-inbox.ts` § "What it takes for this to
|
|
541
|
+
// return anything".
|
|
542
|
+
"(authed)/leads": "0.24.0",
|
|
477
543
|
// #5371 — the workspace module's three settings routes. Genuinely new route
|
|
478
544
|
// directories, so they attach at today's fixed-group version rather than the
|
|
479
545
|
// "predates the manifest" 0.0.0 above, the same rule #5479 and #5048 applied.
|
|
480
|
-
|
|
481
|
-
|
|
482
|
-
|
|
546
|
+
// Directory renamed apps/web/.../(authed)/workspace -> (authed)/admin by
|
|
547
|
+
// #5637 (Admin-vocabulary rename); the ROUTE_SINCE version is unchanged —
|
|
548
|
+
// it tracks when the route was introduced, not when it was renamed.
|
|
549
|
+
"(authed)/admin": "0.22.1",
|
|
550
|
+
"(authed)/admin/site": "0.22.1",
|
|
551
|
+
"(authed)/admin/team": "0.22.1",
|
|
552
|
+
// #5977 — the Pro content host's editor routes (spec 5872 FR1/FR2), added
|
|
553
|
+
// to apps/web by #5957. Genuinely new route directories, so they attach at
|
|
554
|
+
// today's fixed-group version, the same "attach at today's version" rule
|
|
555
|
+
// #5678, #5479, #5371 and #5048 applied.
|
|
556
|
+
//
|
|
557
|
+
// They are registered here but do NOT ship in a scaffold: the pair is
|
|
558
|
+
// excluded from the template snapshot by `EXCLUDE_TEMPLATE_DIRS` in
|
|
559
|
+
// `scripts/build-templates.mjs` (see that entry for the two spec-5872-D11
|
|
560
|
+
// constraints behind the choice). The entries are kept because ROUTE_SINCE
|
|
561
|
+
// is the route registry for `apps/web`, not a mirror of what the snapshot
|
|
562
|
+
// happens to carry — sync-routes' AC-5 coverage test knows the exclusion
|
|
563
|
+
// and treats the absence as justified.
|
|
564
|
+
"(authed)/edit": "0.24.0",
|
|
565
|
+
"(authed)/edit/[type]/[slug]": "0.24.0",
|
|
483
566
|
"(authed)/waitlist": "0.18.0",
|
|
484
567
|
"(authed)/waitlist/[experimentId]": "0.18.0",
|
|
485
568
|
"(authed)/waitlist/[experimentId]/signups/[leadId]": "0.18.0",
|
|
486
|
-
|
|
569
|
+
// #5963 — the `(auth)` route group. `sign-in` keeps its ORIGINAL version
|
|
570
|
+
// under its new key: ROUTE_SINCE tracks when a route was introduced, not
|
|
571
|
+
// when it was moved, exactly as the #5637 workspace -> admin rename above
|
|
572
|
+
// left `(authed)/admin` at 0.22.1. The group's own layout and the four new
|
|
573
|
+
// pages are genuinely new route directories, so they attach at today's
|
|
574
|
+
// fixed-group version — the same rule #5977, #5678, #5479, #5371 and #5048
|
|
575
|
+
// applied.
|
|
576
|
+
"(auth)": "0.24.0",
|
|
577
|
+
"(auth)/sign-in": "0.18.0",
|
|
578
|
+
"(auth)/sign-up": "0.24.0",
|
|
579
|
+
"(auth)/forgot-password": "0.24.0",
|
|
580
|
+
"(auth)/reset-password": "0.24.0",
|
|
581
|
+
"(auth)/verify-email": "0.24.0",
|
|
582
|
+
// #5469 D2 — the generic `[slug]` content route. A genuinely new route
|
|
583
|
+
// directory, so it attaches at today's fixed-group version, the same rule
|
|
584
|
+
// #5479, #5371 and #5048 applied.
|
|
585
|
+
"[slug]": "0.23.0",
|
|
487
586
|
};
|
|
488
587
|
/**
|
|
489
588
|
* Surface id of the registry that owns `routeDir` (ROUTE_ROOT-relative): the
|
|
@@ -518,7 +617,17 @@ export function surfaceForRouteDir(routeDir) {
|
|
|
518
617
|
* that the waitlist dashboard also uses, rather than duplicating them.
|
|
519
618
|
*/
|
|
520
619
|
export const ROUTE_DEPENDENCIES = {
|
|
521
|
-
|
|
620
|
+
// #5963 — the (auth) group's layout renders the brand mark through
|
|
621
|
+
// `siteLogo()`, and the LOCALE layout above it branches on
|
|
622
|
+
// `isAuthPath()` to drop the marketing chrome for these five paths: without
|
|
623
|
+
// `auth-routes.ts` a synced group renders inside the Nav + Footer it exists
|
|
624
|
+
// to replace. The sign-in page no longer imports `site-brand` — the mark is
|
|
625
|
+
// the shell's since #5963 — so its own list is just the env reader.
|
|
626
|
+
"(auth)": ["apps/web/lib/site-brand.tsx", "apps/web/lib/auth-routes.ts"],
|
|
627
|
+
"(auth)/sign-in": ["apps/web/lib/auth-env.ts"],
|
|
628
|
+
"(auth)/sign-up": ["apps/web/lib/auth-env.ts"],
|
|
629
|
+
"(auth)/forgot-password": ["apps/web/lib/auth-env.ts"],
|
|
630
|
+
"(auth)/reset-password": ["apps/web/lib/auth-env.ts"],
|
|
522
631
|
"(authed)": ["apps/web/auth.ts", "apps/web/lib/auth-env.ts"],
|
|
523
632
|
"(authed)/account": [
|
|
524
633
|
"apps/web/lib/account-config.ts",
|
|
@@ -526,10 +635,37 @@ export const ROUTE_DEPENDENCIES = {
|
|
|
526
635
|
`${ROUTE_ROOT}/(authed)/waitlist/components/ForbiddenState.tsx`,
|
|
527
636
|
`${ROUTE_ROOT}/(authed)/waitlist/format.ts`,
|
|
528
637
|
],
|
|
529
|
-
"(authed)/
|
|
530
|
-
|
|
531
|
-
|
|
638
|
+
"(authed)/admin": [
|
|
639
|
+
"apps/web/lib/server-api.ts",
|
|
640
|
+
`${ROUTE_ROOT}/(authed)/waitlist/components/ForbiddenState.tsx`,
|
|
641
|
+
],
|
|
642
|
+
"(authed)/leads": ["apps/web/lib/server-api.ts"],
|
|
643
|
+
// #5977 — the editor routes' own imports, read from their sources: both
|
|
644
|
+
// pages call `forwardedCookieInit`/`unwrapApiResult` from `lib/server-api`
|
|
645
|
+
// and render the shared `ForbiddenState` on the tenant guard's 403, the
|
|
646
|
+
// same pair `(authed)/admin` already declares. Declared for registry
|
|
647
|
+
// completeness; inert in practice while the routes are excluded from the
|
|
648
|
+
// snapshot (see ROUTE_SINCE above).
|
|
649
|
+
"(authed)/edit": [
|
|
650
|
+
"apps/web/lib/server-api.ts",
|
|
651
|
+
`${ROUTE_ROOT}/(authed)/waitlist/components/ForbiddenState.tsx`,
|
|
652
|
+
],
|
|
653
|
+
"(authed)/edit/[type]/[slug]": [
|
|
654
|
+
"apps/web/lib/server-api.ts",
|
|
655
|
+
`${ROUTE_ROOT}/(authed)/waitlist/components/ForbiddenState.tsx`,
|
|
656
|
+
],
|
|
657
|
+
"(authed)/admin/site": [
|
|
658
|
+
"apps/web/lib/server-api.ts",
|
|
659
|
+
`${ROUTE_ROOT}/(authed)/waitlist/components/ForbiddenState.tsx`,
|
|
660
|
+
],
|
|
661
|
+
"(authed)/admin/team": [
|
|
662
|
+
"apps/web/lib/server-api.ts",
|
|
663
|
+
`${ROUTE_ROOT}/(authed)/waitlist/components/ForbiddenState.tsx`,
|
|
664
|
+
],
|
|
532
665
|
"(authed)/waitlist": ["apps/web/lib/server-api.ts"],
|
|
666
|
+
// #6183 S9b D5 — both /meet pages call the shared enabled-slug read.
|
|
667
|
+
meet: ["apps/web/lib/meeting-slugs.ts"],
|
|
668
|
+
"meet/[slug]": ["apps/web/lib/meeting-slugs.ts"],
|
|
533
669
|
};
|
|
534
670
|
/**
|
|
535
671
|
* i18n message namespaces (dot-path into `apps/web/messages/<locale>.json`) a
|
|
@@ -541,11 +677,23 @@ export const ROUTE_DEPENDENCIES = {
|
|
|
541
677
|
* to the site owner, same as any other reported gap.
|
|
542
678
|
*/
|
|
543
679
|
export const ROUTE_MESSAGE_NAMESPACES = {
|
|
544
|
-
|
|
680
|
+
// #5963 — `signIn` is untouched and still the sign-in page's namespace; the
|
|
681
|
+
// group's layout and the four new pages read the new `auth` namespace.
|
|
682
|
+
"(auth)": ["auth"],
|
|
683
|
+
"(auth)/sign-in": ["signIn"],
|
|
684
|
+
"(auth)/sign-up": ["auth"],
|
|
685
|
+
"(auth)/forgot-password": ["auth"],
|
|
686
|
+
"(auth)/reset-password": ["auth"],
|
|
687
|
+
"(auth)/verify-email": ["auth"],
|
|
545
688
|
"(authed)/account": ["account"],
|
|
546
|
-
"(authed)/
|
|
547
|
-
"(authed)/
|
|
548
|
-
|
|
689
|
+
"(authed)/admin": ["admin"],
|
|
690
|
+
"(authed)/leads": ["leadsInbox"],
|
|
691
|
+
// #5977 — every string on both editor routes comes from the `edit`
|
|
692
|
+
// namespace (spec 5872 D10).
|
|
693
|
+
"(authed)/edit": ["edit"],
|
|
694
|
+
"(authed)/edit/[type]/[slug]": ["edit"],
|
|
695
|
+
"(authed)/admin/site": ["admin"],
|
|
696
|
+
"(authed)/admin/team": ["admin"],
|
|
549
697
|
"(authed)/waitlist": ["waitlistDashboard.gate"],
|
|
550
698
|
};
|
|
551
699
|
/** Template-relative path of the generated site-nav registry. */
|
|
@@ -559,19 +707,43 @@ export const NAV_CONFIG_PATH = "apps/web/lib/nav-config.ts";
|
|
|
559
707
|
* exists: two dead links, sitewide, by default. So the nav follows the declared
|
|
560
708
|
* surface set for the same reason the route tree does.
|
|
561
709
|
*
|
|
562
|
-
*
|
|
563
|
-
*
|
|
564
|
-
*
|
|
565
|
-
*
|
|
710
|
+
* The base items are the Free marketing-site chrome (#5469 D5, the approved
|
|
711
|
+
* chrome diff recorded on that card):
|
|
712
|
+
* a Product flyout (How it works · Features · FAQ), then How it works, Pricing
|
|
713
|
+
* and About. Every one of those pages is in EVERY scaffold — the four core
|
|
714
|
+
* routes plus the `[slug]` route over the overlay's LandingPage MDX — except
|
|
715
|
+
* Pricing, which is a gateable surface: its item is emitted only when the
|
|
716
|
+
* `pricing` surface is enabled (a template roster enables it, #5469 D10), so
|
|
717
|
+
* the nav still never links a page the scaffold does not carry.
|
|
718
|
+
*
|
|
719
|
+
* Only gateable surfaces carrying a `navLabelKey` are linked after the base
|
|
720
|
+
* items, because a nav entry needs a translated label. Emitting an entry whose
|
|
721
|
+
* `labelKey` has no translation would trade a dead link for a missing-message
|
|
722
|
+
* error.
|
|
566
723
|
*
|
|
567
|
-
* Deterministic: entries follow GATEABLE_SURFACES order (alphabetical
|
|
568
|
-
* after the
|
|
724
|
+
* Deterministic: gateable entries follow GATEABLE_SURFACES order (alphabetical
|
|
725
|
+
* by id), after the fixed base items.
|
|
569
726
|
*/
|
|
570
727
|
export function renderNavConfig(enabled) {
|
|
571
728
|
const on = new Set(enabled);
|
|
572
729
|
const linked = GATEABLE_SURFACES.filter((s) => s.navLabelKey !== undefined && on.has(s.id));
|
|
573
730
|
const entries = [
|
|
574
|
-
|
|
731
|
+
" {",
|
|
732
|
+
' labelKey: "product",',
|
|
733
|
+
" flyout: {",
|
|
734
|
+
' presentation: "list",',
|
|
735
|
+
' width: "trigger",',
|
|
736
|
+
" features: [",
|
|
737
|
+
' { labelKey: "howItWorks", descKey: "howItWorksDesc", icon: Layers, path: "/how-it-works" },',
|
|
738
|
+
' { labelKey: "features", descKey: "featuresDesc", icon: Sparkles, path: "/features" },',
|
|
739
|
+
' { labelKey: "faq", descKey: "faqDesc", icon: HelpCircle, path: "/faq" },',
|
|
740
|
+
" ],",
|
|
741
|
+
' actions: [{ labelKey: "docs", icon: BookOpen, path: "/how-it-works" }],',
|
|
742
|
+
" },",
|
|
743
|
+
" },",
|
|
744
|
+
' { labelKey: "howItWorks", path: "/how-it-works" },',
|
|
745
|
+
...(on.has("pricing") ? [' { labelKey: "pricing", path: "/pricing" },'] : []),
|
|
746
|
+
' { labelKey: "about", path: "/about" },',
|
|
575
747
|
...linked.map((s) => ` { labelKey: "${s.navLabelKey}", path: "/${s.id}" },`),
|
|
576
748
|
];
|
|
577
749
|
// The emitted file imports the consumer's `NavItem` type from the UI package.
|
|
@@ -596,6 +768,7 @@ export function renderNavConfig(enabled) {
|
|
|
596
768
|
// \`apps/web/messages/<locale>.json\`. Paths are locale-agnostic — Nav prefixes
|
|
597
769
|
// them with the active locale at render.
|
|
598
770
|
${navItemImport}
|
|
771
|
+
import { BookOpen, HelpCircle, Layers, Sparkles } from "lucide-react";
|
|
599
772
|
|
|
600
773
|
export const navItems: NavItem[] = [
|
|
601
774
|
${entries.join("\n")}
|
|
@@ -1,5 +1,23 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Site-template registry, and the roster merge that composes a template with
|
|
3
|
+
* the modules it takes (spec 5478, unit A of the site-templates mechanism;
|
|
4
|
+
* design record docs/specs/5462-site-templates-module-bundles-spec.md).
|
|
5
|
+
*
|
|
6
|
+
* A site template is DATA AND CONTENT, NOT CODE: a page roster, seed content
|
|
7
|
+
* and config — never a `page.tsx` (parent § D16). This file declares what a
|
|
8
|
+
* template IS. It declares no delivery channel, reads no licence, and touches
|
|
9
|
+
* no disk: the `--template` flag, the bundle fetch and the landing order are
|
|
10
|
+
* #5481's, and `GET /v1/templates/:name/:version` is #5480's.
|
|
11
|
+
*
|
|
12
|
+
* A CODE registry, not per-template JSON (parent § D2): in TypeScript,
|
|
13
|
+
* `requires` is checked against `RECOGNIZED_MODULES` at compile time, and this
|
|
14
|
+
* package already twice records that a fixed template FILE is the drift a
|
|
15
|
+
* registry removes.
|
|
16
|
+
*/
|
|
17
|
+
import type { CategorySlug } from "@working-theory/schema/categories";
|
|
1
18
|
import { type PageRosterRow } from "./module-architecture.js";
|
|
2
19
|
import type { ModuleName } from "./parse-args.js";
|
|
20
|
+
import type { TemplateLayout } from "./site-chrome.js";
|
|
3
21
|
/**
|
|
4
22
|
* One site template: its fixed module list and the page rows it brings itself.
|
|
5
23
|
*
|
|
@@ -23,11 +41,59 @@ export interface TemplateManifest {
|
|
|
23
41
|
* unrecognized name fails type-check rather than a runtime validation.
|
|
24
42
|
*/
|
|
25
43
|
requires: readonly ModuleName[];
|
|
44
|
+
/**
|
|
45
|
+
* The business categories this template is FOR — one or more LEAF slugs from
|
|
46
|
+
* the site-category registry (`@working-theory/schema/categories`, #5830),
|
|
47
|
+
* shared with content's frontmatter `categories`. Required, and typed against
|
|
48
|
+
* `CategorySlug`, for the same reason `requires` is typed against
|
|
49
|
+
* `ModuleName`: an unrecognized name fails type-check rather than a runtime
|
|
50
|
+
* validation, and every template must be findable under at least one
|
|
51
|
+
* category on the hub's "Browse by business" band.
|
|
52
|
+
*
|
|
53
|
+
* LEAVES ONLY. A group slug (`professional-services`, `home-and-trades`, …)
|
|
54
|
+
* is never a valid value: the group is DERIVED through `groupOf`, so a
|
|
55
|
+
* template that fits "professional services" names `accounting` and `legal`.
|
|
56
|
+
* Where a leaf is genuinely missing, the operator adds a leaf to the
|
|
57
|
+
* registry — never a group here.
|
|
58
|
+
*/
|
|
59
|
+
categories: readonly CategorySlug[];
|
|
60
|
+
/**
|
|
61
|
+
* The template this one EXTENDS — its lineage, never its level (#5618 D2).
|
|
62
|
+
* A single parent, not a list: "later layers win file by file" needs a
|
|
63
|
+
* total order, and a list would make the winner depend on the list's order
|
|
64
|
+
* (the failure `normalizeModules` exists to prevent for modules). The
|
|
65
|
+
* chain terminates at `BASE_LAYER_DIR`, the one layer every template
|
|
66
|
+
* reaches; `resolveTemplateLineage` walks and validates it. The delivery
|
|
67
|
+
* level stays derived from `requires` — `saas-product` extends `_base`, not
|
|
68
|
+
* `marketing-site`, precisely because "Pro plus multi-tenant" is a level
|
|
69
|
+
* fact `requires` already states.
|
|
70
|
+
*/
|
|
71
|
+
extends: string;
|
|
26
72
|
/** Base rows, before any module's contribution is merged. */
|
|
27
73
|
roster: readonly PageRosterRow[];
|
|
28
74
|
/** Bundle-relative source → scaffold-relative destination. */
|
|
29
75
|
installMap?: Record<string, string>;
|
|
76
|
+
/**
|
|
77
|
+
* Site-level chrome knobs (#5612 D1) — nav/footer/banner/home. Every field
|
|
78
|
+
* optional; a template declaring none (all four settled entries below)
|
|
79
|
+
* renders byte-identically to before this field existed. See
|
|
80
|
+
* `site-chrome.ts` for the vocabulary, defaults and the generated
|
|
81
|
+
* `apps/web/lib/site-chrome.ts` this resolves into.
|
|
82
|
+
*/
|
|
83
|
+
layout?: TemplateLayout;
|
|
30
84
|
}
|
|
85
|
+
/**
|
|
86
|
+
* The base layer's directory under `site-templates/` (#5618 D1) — the seed
|
|
87
|
+
* files every template reaches by declaring `extends`, installed by
|
|
88
|
+
* `installBundleTree` before any module bundle and only when `--template`
|
|
89
|
+
* is named, so a scaffold naming no template is byte-identical to before it
|
|
90
|
+
* existed. It is NOT a template: it has no `TEMPLATE_MANIFESTS` entry (so
|
|
91
|
+
* `--template _base` stays unrecognized — `RECOGNIZED_TEMPLATES` is exactly
|
|
92
|
+
* that object's key set) and is never published as a registry bundle. The
|
|
93
|
+
* leading underscore is what `build-module-bundles.mjs`'s template loop and
|
|
94
|
+
* the directory-scanning tests key their skip on.
|
|
95
|
+
*/
|
|
96
|
+
export declare const BASE_LAYER_DIR = "_base";
|
|
31
97
|
/** Thrown on an undeclared template name or a route claimed twice. Mirrors `SurfaceError`'s shape. */
|
|
32
98
|
export declare class TemplateError extends Error {
|
|
33
99
|
constructor(message: string);
|
|
@@ -40,17 +106,36 @@ export declare class TemplateError extends Error {
|
|
|
40
106
|
* `workspace` appears in none of them. It is a Pro module, but an authed app
|
|
41
107
|
* surface contributing no PUBLIC page to a marketing site, and `requires`
|
|
42
108
|
* names what the roster uses (parent § D15). A Pro key unlocks it regardless
|
|
43
|
-
* of any template. `ModuleName` could
|
|
44
|
-
*
|
|
109
|
+
* of any template. `ModuleName` could express it either way — `workspace` is
|
|
110
|
+
* one of the seven `RECOGNIZED_MODULES` names; it is simply not one any
|
|
111
|
+
* template's fixed `requires` list names.
|
|
45
112
|
*
|
|
46
113
|
* `commerce` is likewise in none of them: it is a per-site add-on available at
|
|
47
114
|
* any level, so a template for it would contradict the offering model and
|
|
48
115
|
* multiply the set combinatorially (parent § D13). It still contributes rows
|
|
49
116
|
* to whichever template takes it, through `mergeRoster` below.
|
|
117
|
+
*
|
|
118
|
+
* Frozen (#5507): this registry is also the issuer's authorization allowlist
|
|
119
|
+
* (spec 5480 § C7) — a mutable global there could be monkey-patched at
|
|
120
|
+
* runtime. A caller needing a different registry for a test injects its own
|
|
121
|
+
* fixture rather than mutating this one.
|
|
50
122
|
*/
|
|
51
|
-
export declare const TEMPLATE_MANIFESTS: Record<string, TemplateManifest
|
|
123
|
+
export declare const TEMPLATE_MANIFESTS: Readonly<Record<string, TemplateManifest>>;
|
|
52
124
|
/** Look up a declared template. @throws TemplateError on an undeclared name. */
|
|
53
125
|
export declare function resolveTemplate(name: string): TemplateManifest;
|
|
126
|
+
/**
|
|
127
|
+
* Resolve a template's `extends` chain — `[name, …ancestors, "_base"]` — in
|
|
128
|
+
* the client registry, before any fetch (#5618 D2). `_base` is the only
|
|
129
|
+
* valid terminus: an `extends` naming neither it nor a declared template is
|
|
130
|
+
* refused naming the field, the value and the recognized set, in
|
|
131
|
+
* `resolveTemplate`'s message shape; a cycle is refused naming the whole
|
|
132
|
+
* chain. The registry is a parameter for the same reason `TEMPLATE_MANIFESTS`
|
|
133
|
+
* is frozen (#5507): a test needing a cyclic or dangling fixture injects its
|
|
134
|
+
* own rather than mutating the shipped one.
|
|
135
|
+
*
|
|
136
|
+
* @throws TemplateError on an undeclared name, an unknown parent or a cycle.
|
|
137
|
+
*/
|
|
138
|
+
export declare function resolveTemplateLineage(name: string, registry?: Readonly<Record<string, TemplateManifest>>): string[];
|
|
54
139
|
/**
|
|
55
140
|
* Compose a template's roster: its base rows, then each taken module's
|
|
56
141
|
* contributed rows in `normalizeModules` order (parent § D7).
|