create-ailk 0.21.0 → 0.23.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/component-catalog.json +186 -8
- package/component-catalog.md +9 -7
- package/dist/add-module.js +20 -14
- package/dist/add-route.d.ts +55 -1
- package/dist/add-route.js +294 -12
- package/dist/cli.js +27 -10
- package/dist/derive-architecture.d.ts +12 -3
- package/dist/derive-architecture.js +20 -8
- package/dist/design-package.js +7 -2
- package/dist/lib/design-checks.js +4 -2
- package/dist/lib/env-catalog.js +5 -5
- package/dist/lib/npm-auth.d.ts +9 -0
- package/dist/lib/npm-auth.js +48 -0
- package/dist/lib/scaffold-render-sweep.d.ts +3 -2
- package/dist/lib/scaffold-render-sweep.js +5 -8
- package/dist/module-architecture.js +99 -9
- package/dist/paid-sections.d.ts +21 -2
- package/dist/paid-sections.js +69 -7
- package/dist/programmatic.js +19 -5
- package/dist/seed-flows.d.ts +4 -1
- package/dist/seed-flows.js +50 -4
- package/dist/site-chrome.d.ts +8 -0
- package/dist/site-chrome.js +17 -1
- package/dist/spoke-checkout.d.ts +65 -41
- package/dist/spoke-checkout.js +208 -430
- package/dist/starter-grid.d.ts +0 -3
- package/dist/starter-grid.js +24 -14
- package/dist/surfaces.js +52 -26
- package/dist/sync-routes.d.ts +38 -1
- package/dist/sync-routes.js +187 -13
- package/dist/template-record.d.ts +4 -3
- package/dist/template-record.js +8 -4
- package/dist/topology.js +25 -1
- package/dist/update.js +15 -0
- package/package.json +3 -3
- package/site-templates/_base/content/en/NewsletterSignup/newsletter.mdx +4 -49
- package/site-templates/_base/content/en/Pricing/pricing.mdx +3 -21
- package/site-templates/designs/minimalism/DESIGN.md +1 -1
- package/site-templates/marketing-site/TEMPLATE-MANIFEST.json +23 -20
- package/site-templates/marketing-site/TEMPLATE-MANIFEST.md +28 -14
- package/site-templates/marketing-site-free/TEMPLATE-MANIFEST.json +15 -39
- package/site-templates/marketing-site-free/TEMPLATE-MANIFEST.md +38 -34
- package/site-templates/marketing-site-free/content/en/NewsletterSignup/newsletter.mdx +6 -43
- package/site-templates/shapes/local-business-service/TEMPLATE-MANIFEST.json +14 -31
- package/site-templates/shapes/local-business-service/TEMPLATE-MANIFEST.md +26 -25
- package/site-templates/shapes/local-business-service/content/en/NewsletterSignup/newsletter.mdx +6 -43
- package/site-templates/shapes/organization-service/TEMPLATE-MANIFEST.json +14 -31
- package/site-templates/shapes/organization-service/TEMPLATE-MANIFEST.md +26 -25
- package/site-templates/shapes/organization-service/content/en/NewsletterSignup/newsletter.mdx +6 -43
- package/site-templates/shapes/person-service/TEMPLATE-MANIFEST.json +14 -31
- package/site-templates/shapes/person-service/TEMPLATE-MANIFEST.md +26 -25
- package/site-templates/shapes/person-service/content/en/NewsletterSignup/newsletter.mdx +6 -43
- package/templates/.env.example +9 -7
- package/templates/.github/workflows/ci.yml +13 -0
- package/templates/README.md +1 -0
- package/templates/_gitignore +4 -0
- package/templates/apps/api/.env.example +27 -0
- package/templates/apps/api/CLAUDE.md +87 -17
- package/templates/apps/api/package.json +1 -0
- package/templates/apps/api/src/__tests__/factories/in-memory-prisma.ts +2 -0
- package/templates/apps/api/src/bin/stripe-connections-rekey.ts +45 -0
- package/templates/apps/api/src/config/__tests__/stripe-connection-key.test.ts +185 -0
- package/templates/apps/api/src/config/index.ts +109 -0
- package/templates/apps/api/src/lib/__mocks__/prisma.ts +3 -0
- package/templates/apps/api/src/lib/__tests__/consent-store.test.ts +19 -0
- package/templates/apps/api/src/lib/__tests__/flow-completion-store.test.ts +149 -0
- package/templates/apps/api/src/lib/__tests__/flow-rename-race.db.test.ts +116 -0
- package/templates/apps/api/src/lib/consent-store.ts +28 -0
- package/templates/apps/api/src/lib/error-serializer.ts +74 -7
- package/templates/apps/api/src/lib/flow-completion-store.ts +136 -0
- package/templates/apps/api/src/lib/hub-sign-in-store.ts +38 -2
- package/templates/apps/api/src/lib/proposal-paid-store.ts +21 -4
- package/templates/apps/api/src/lib/stripe.ts +74 -4
- package/templates/apps/api/src/lib/webhook-idempotency.ts +66 -2
- package/templates/apps/api/src/middleware/__tests__/auth.hub-session-path.test.ts +14 -3
- package/templates/apps/api/src/middleware/__tests__/rate-limit.test.ts +218 -0
- package/templates/apps/api/src/middleware/__tests__/write-budget.test.ts +174 -0
- package/templates/apps/api/src/middleware/auth.ts +13 -6
- package/templates/apps/api/src/middleware/rate-limit.ts +226 -4
- package/templates/apps/api/src/openapi/spec.ts +33 -3
- package/templates/apps/api/src/routes/auth/__tests__/hub-sign-in.test.ts +49 -0
- package/templates/apps/api/src/routes/auth/hub-sign-in.ts +41 -0
- package/templates/apps/api/src/routes/billing/plan-checkout.ts +325 -0
- package/templates/apps/api/src/routes/checkout/__tests__/live-key.test.ts +11 -0
- package/templates/apps/api/src/routes/checkout/__tests__/offerings-authorization.test.ts +1300 -0
- package/templates/apps/api/src/routes/checkout/__tests__/session.test.ts +11 -0
- package/templates/apps/api/src/routes/checkout/__tests__/sessions.test.ts +49 -0
- package/templates/apps/api/src/routes/checkout/session.ts +57 -25
- package/templates/apps/api/src/routes/checkout/sessions.ts +232 -82
- package/templates/apps/api/src/routes/consent/__tests__/account.test.ts +386 -0
- package/templates/apps/api/src/routes/consent/__tests__/sign-up-rate-limit.test.ts +160 -0
- package/templates/apps/api/src/routes/consent/account.ts +268 -0
- package/templates/apps/api/src/routes/consent/index.ts +15 -7
- package/templates/apps/api/src/routes/content/__tests__/get-page.test.ts +36 -0
- package/templates/apps/api/src/routes/content/__tests__/offerings.test.ts +731 -0
- package/templates/apps/api/src/routes/content/__tests__/pricing.test.ts +753 -0
- package/templates/apps/api/src/routes/content/__tests__/shown-offerings.test.ts +762 -0
- package/templates/apps/api/src/routes/content/get-page.ts +4 -0
- package/templates/apps/api/src/routes/content/index.ts +16 -2
- package/templates/apps/api/src/routes/content/offerings.ts +184 -0
- package/templates/apps/api/src/routes/content/pricing.ts +176 -0
- package/templates/apps/api/src/routes/content/shown-offerings.ts +229 -0
- package/templates/apps/api/src/routes/leads/__tests__/magnet-completion.test.ts +515 -0
- package/templates/apps/api/src/routes/leads/index.ts +164 -7
- package/templates/apps/api/src/routes/webhooks/README.md +66 -0
- package/templates/apps/api/src/routes/webhooks/__tests__/stripe-checkout-consent.test.ts +649 -0
- package/templates/apps/api/src/routes/webhooks/__tests__/stripe-offerings.test.ts +814 -0
- package/templates/apps/api/src/routes/webhooks/__tests__/stripe-org-billing.test.ts +80 -68
- package/templates/apps/api/src/routes/webhooks/__tests__/stripe-site-filter.test.ts +5 -5
- package/templates/apps/api/src/routes/webhooks/__tests__/stripe.test.ts +8 -2
- package/templates/apps/api/src/routes/webhooks/index.ts +23 -0
- package/templates/apps/api/src/routes/webhooks/stripe-offerings.ts +384 -0
- package/templates/apps/api/src/routes/webhooks/stripe.ts +245 -36
- package/templates/apps/api/src/server.ts +163 -46
- package/templates/apps/api/src/services/__tests__/checkout-plan.test.ts +313 -0
- package/templates/apps/api/src/services/__tests__/deliverable-fulfillment.test.ts +51 -2
- package/templates/apps/api/src/services/__tests__/keyless-workspace-predicate.test.ts +234 -0
- package/templates/apps/api/src/services/__tests__/lead-routing.test.ts +1383 -133
- package/templates/apps/api/src/services/__tests__/magnet-completion.test.ts +389 -0
- package/templates/apps/api/src/services/__tests__/person.test.ts +232 -1
- package/templates/apps/api/src/services/__tests__/plan-offerings.test.ts +444 -0
- package/templates/apps/api/src/services/checkout-buyer.ts +100 -4
- package/templates/apps/api/src/services/consent.ts +95 -0
- package/templates/apps/api/src/services/deliverable-fulfillment.ts +13 -5
- package/templates/apps/api/src/services/entitlements.ts +157 -1
- package/templates/apps/api/src/services/lead-routing.ts +431 -47
- package/templates/apps/api/src/services/magnet-completion.ts +242 -0
- package/templates/apps/api/src/services/offerings/__tests__/catalog.test.ts +295 -0
- package/templates/apps/api/src/services/offerings/__tests__/checkout-session-record.test.ts +268 -0
- package/templates/apps/api/src/services/offerings/__tests__/connection.test.ts +868 -0
- package/templates/apps/api/src/services/offerings/__tests__/fixtures.ts +416 -0
- package/templates/apps/api/src/services/offerings/__tests__/org-products.test.ts +396 -0
- package/templates/apps/api/src/services/offerings/__tests__/readers.test.ts +681 -0
- package/templates/apps/api/src/services/offerings/__tests__/source.test.ts +231 -0
- package/templates/apps/api/src/services/offerings/catalog.ts +308 -0
- package/templates/apps/api/src/services/offerings/checkout-authorization.ts +385 -0
- package/templates/apps/api/src/services/offerings/checkout-session-record.ts +192 -0
- package/templates/apps/api/src/services/offerings/connection.ts +1003 -0
- package/templates/apps/api/src/services/offerings/index.ts +614 -0
- package/templates/apps/api/src/services/offerings/source.ts +140 -0
- package/templates/apps/api/src/services/offerings/store.ts +150 -0
- package/templates/apps/api/src/services/person.ts +164 -17
- package/templates/apps/api/src/services/plan-offerings.ts +125 -0
- package/templates/apps/api/src/services/public-site-resolver.ts +27 -15
- package/templates/apps/mcp/CLAUDE.md +22 -14
- package/templates/apps/mcp/__tests__/catalog-drift.test.ts +9 -4
- package/templates/apps/mcp/__tests__/get_offerings.test.ts +358 -0
- package/templates/apps/mcp/__tests__/get_pricing.test.ts +364 -0
- package/templates/apps/mcp/__tests__/http-transport.test.ts +4 -4
- package/templates/apps/mcp/__tests__/module-exclusion.test.ts +2 -2
- package/templates/apps/mcp/__tests__/server.test.ts +13 -11
- package/templates/apps/mcp/__tests__/start_purchase.test.ts +208 -12
- package/templates/apps/mcp/__tests__/tools/purchase.test.ts +11 -11
- package/templates/apps/mcp/src/lib/shown-offerings.ts +105 -0
- package/templates/apps/mcp/src/server.ts +5 -0
- package/templates/apps/mcp/src/tools/get_offerings.ts +47 -30
- package/templates/apps/mcp/src/tools/get_pricing.ts +40 -40
- package/templates/apps/mcp/src/tools/purchase.ts +30 -2
- package/templates/apps/mcp/src/workspace-server.ts +15 -0
- package/templates/apps/web/.env.example +24 -31
- package/templates/apps/web/CLAUDE.md +393 -9
- package/templates/apps/web/__tests__/__mocks__/next-intl-server.ts +6 -2
- package/templates/apps/web/app/[locale]/(auth)/__tests__/auth-pages.test.tsx +5 -4
- package/templates/apps/web/app/[locale]/(auth)/forgot-password/forgot-password-card.tsx +5 -1
- package/templates/apps/web/app/[locale]/(auth)/forgot-password/page.tsx +2 -0
- package/templates/apps/web/app/[locale]/(auth)/layout.tsx +3 -14
- package/templates/apps/web/app/[locale]/(auth)/reset-password/page.tsx +2 -0
- package/templates/apps/web/app/[locale]/(auth)/reset-password/reset-password-card.tsx +5 -1
- package/templates/apps/web/app/[locale]/(auth)/sign-in/__tests__/page.test.tsx +160 -8
- package/templates/apps/web/app/[locale]/(auth)/sign-in/__tests__/sign-in-card.test.tsx +12 -9
- package/templates/apps/web/app/[locale]/(auth)/sign-in/page.tsx +105 -2
- package/templates/apps/web/app/[locale]/(auth)/sign-in/sign-in-card.tsx +36 -3
- package/templates/apps/web/app/[locale]/(auth)/sign-in/sign-in-text.ts +47 -1
- package/templates/apps/web/app/[locale]/(auth)/sign-up/__tests__/sign-up-card.test.tsx +60 -22
- package/templates/apps/web/app/[locale]/(auth)/sign-up/page.tsx +16 -8
- package/templates/apps/web/app/[locale]/(auth)/sign-up/sign-up-card.tsx +85 -31
- package/templates/apps/web/app/[locale]/(auth)/use-auth-card-flow.ts +18 -4
- package/templates/apps/web/app/[locale]/(auth)/verify-email/page.tsx +3 -1
- package/templates/apps/web/app/[locale]/(authed)/__tests__/gate.test.tsx +7 -4
- package/templates/apps/web/app/[locale]/(authed)/account/__tests__/agreements-prompt.test.tsx +227 -207
- package/templates/apps/web/app/[locale]/(authed)/account/__tests__/layout-sign-out.test.tsx +17 -1
- package/templates/apps/web/app/[locale]/(authed)/account/__tests__/layout.test.tsx +143 -1
- package/templates/apps/web/app/[locale]/(authed)/account/__tests__/new-menu.test.tsx +4 -0
- package/templates/apps/web/app/[locale]/(authed)/account/__tests__/page.test.tsx +385 -89
- package/templates/apps/web/app/[locale]/(authed)/account/__tests__/popups.test.tsx +5 -0
- package/templates/apps/web/app/[locale]/(authed)/account/__tests__/privacy.test.tsx +44 -5
- package/templates/apps/web/app/[locale]/(authed)/account/account-frame.tsx +68 -34
- package/templates/apps/web/app/[locale]/(authed)/account/actions.ts +0 -8
- package/templates/apps/web/app/[locale]/(authed)/account/agreements-actions.ts +39 -151
- package/templates/apps/web/app/[locale]/(authed)/account/agreements-gate.ts +61 -0
- package/templates/apps/web/app/[locale]/(authed)/account/agreements-prompt.tsx +143 -140
- package/templates/apps/web/app/[locale]/(authed)/account/layout.tsx +40 -6
- package/templates/apps/web/app/[locale]/(authed)/account/page.tsx +68 -54
- package/templates/apps/web/app/[locale]/(authed)/account/privacy/page.tsx +5 -1
- package/templates/apps/web/app/[locale]/careers/[slug]/[action]/page.tsx +189 -0
- package/templates/apps/web/app/[locale]/careers/[slug]/apply.actions.ts +295 -0
- package/templates/apps/web/app/[locale]/careers/[slug]/job-application-form.ts +120 -0
- package/templates/apps/web/app/[locale]/careers/[slug]/page.tsx +23 -0
- package/templates/apps/web/app/[locale]/careers/__tests__/apply.actions.test.ts +319 -0
- package/templates/apps/web/app/[locale]/careers/__tests__/careers-hub.test.tsx +172 -0
- package/templates/apps/web/app/[locale]/careers/__tests__/careers-set.test.tsx +149 -0
- package/templates/apps/web/app/[locale]/careers/__tests__/job-application-form.test.ts +141 -0
- package/templates/apps/web/app/[locale]/careers/__tests__/job-apply-page.test.tsx +424 -0
- package/templates/apps/web/app/[locale]/careers/__tests__/page-renderer-migration.test.tsx +72 -11
- package/templates/apps/web/app/[locale]/careers/page.tsx +129 -5
- package/templates/apps/web/app/[locale]/case-studies/__tests__/page-renderer-migration.test.tsx +2 -2
- package/templates/apps/web/app/[locale]/checkout/__tests__/pages.test.tsx +139 -0
- package/templates/apps/web/app/[locale]/checkout/__tests__/thank-you-account.test.tsx +136 -0
- package/templates/apps/web/app/[locale]/checkout/thank-you/page.tsx +114 -0
- package/templates/apps/web/app/[locale]/checkout/unavailable/page.tsx +51 -0
- package/templates/apps/web/app/[locale]/clients/__tests__/page-renderer-migration.test.tsx +1 -1
- package/templates/apps/web/app/[locale]/contact/ConversationalIntakeWrapper.tsx +17 -1
- package/templates/apps/web/app/[locale]/contact/__tests__/ConversationalIntakeWrapper.test.tsx +79 -4
- package/templates/apps/web/app/[locale]/contact/__tests__/contact-magnet-page.test.tsx +160 -13
- package/templates/apps/web/app/[locale]/contact/__tests__/contact.actions.test.ts +466 -36
- package/templates/apps/web/app/[locale]/contact/__tests__/page-renderer-migration.test.tsx +7 -0
- package/templates/apps/web/app/[locale]/contact/contact.actions.ts +172 -57
- package/templates/apps/web/app/[locale]/contact/intake-script.ts +26 -0
- package/templates/apps/web/app/[locale]/contact/page.tsx +99 -60
- package/templates/apps/web/app/[locale]/courses/__tests__/page-renderer-migration.test.tsx +2 -2
- package/templates/apps/web/app/[locale]/events/__tests__/page-renderer-migration.test.tsx +2 -2
- package/templates/apps/web/app/[locale]/flows/[slug]/FlowStepperClient.tsx +341 -4
- package/templates/apps/web/app/[locale]/flows/[slug]/__tests__/FlowStepperClient.count.test.tsx +43 -0
- package/templates/apps/web/app/[locale]/flows/[slug]/__tests__/FlowStepperClient.deliverable.test.tsx +200 -0
- package/templates/apps/web/app/[locale]/flows/[slug]/__tests__/FlowStepperClient.ending.test.tsx +197 -0
- package/templates/apps/web/app/[locale]/flows/[slug]/__tests__/FlowStepperClient.invite.test.tsx +257 -0
- package/templates/apps/web/app/[locale]/flows/[slug]/__tests__/account-invite.actions.test.ts +69 -0
- package/templates/apps/web/app/[locale]/flows/[slug]/__tests__/magnet-page.test.tsx +430 -0
- package/templates/apps/web/app/[locale]/flows/[slug]/__tests__/two-route-tool.test.tsx +21 -9
- package/templates/apps/web/app/[locale]/flows/[slug]/account-invite.actions.ts +35 -0
- package/templates/apps/web/app/[locale]/flows/[slug]/magnet-page.tsx +352 -0
- package/templates/apps/web/app/[locale]/flows/[slug]/page.tsx +12 -73
- package/templates/apps/web/app/[locale]/flows/[slug]/start/page.tsx +10 -31
- package/templates/apps/web/app/[locale]/for/__tests__/page-renderer-migration.test.tsx +1 -1
- package/templates/apps/web/app/[locale]/how-to/__tests__/page-renderer-migration.test.tsx +1 -1
- package/templates/apps/web/app/[locale]/integrations/__tests__/page-renderer-migration.test.tsx +2 -2
- package/templates/apps/web/app/[locale]/layout.tsx +55 -4
- package/templates/apps/web/app/[locale]/newsletter/__tests__/page.test.tsx +174 -104
- package/templates/apps/web/app/[locale]/newsletter/page.tsx +95 -58
- package/templates/apps/web/app/[locale]/page.tsx +13 -1
- package/templates/apps/web/app/[locale]/partners/__tests__/page-renderer-migration.test.tsx +2 -2
- package/templates/apps/web/app/[locale]/pricing/__tests__/__snapshots__/unconnected.test.tsx.snap +7 -0
- package/templates/apps/web/app/[locale]/pricing/__tests__/offerings.test.tsx +599 -0
- package/templates/apps/web/app/[locale]/pricing/__tests__/page.test.tsx +216 -0
- package/templates/apps/web/app/[locale]/pricing/__tests__/unconnected.test.tsx +236 -0
- package/templates/apps/web/app/[locale]/pricing/page.tsx +367 -6
- package/templates/apps/web/app/[locale]/products/__tests__/page-renderer-migration.test.tsx +2 -2
- package/templates/apps/web/app/[locale]/resources/__tests__/page-renderer-migration.test.tsx +2 -2
- package/templates/apps/web/app/[locale]/services/__tests__/page-renderer-migration.test.tsx +2 -2
- package/templates/apps/web/app/[locale]/software/__tests__/page-renderer-migration.test.tsx +1 -1
- package/templates/apps/web/app/[locale]/team/__tests__/team-routes.test.tsx +4 -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 +1 -1
- package/templates/apps/web/app/[locale]/thanks/[slug]/AccountInviteLink.tsx +89 -0
- package/templates/apps/web/app/[locale]/thanks/[slug]/__tests__/AccountInviteLink.test.tsx +104 -0
- package/templates/apps/web/app/[locale]/thanks/[slug]/__tests__/page.test.tsx +38 -0
- package/templates/apps/web/app/[locale]/thanks/[slug]/page.tsx +9 -0
- package/templates/apps/web/app/[locale]/unsubscribe/__tests__/unsubscribe-rsc-boundary.test.tsx +17 -0
- package/templates/apps/web/app/[locale]/unsubscribe/__tests__/unsubscribe.test.tsx +62 -3
- package/templates/apps/web/app/[locale]/unsubscribe/page.tsx +70 -18
- package/templates/apps/web/app/[locale]/unsubscribe/unsubscribe-button.tsx +30 -9
- package/templates/apps/web/app/[locale]/whitepapers/__tests__/page-renderer-migration.test.tsx +1 -1
- package/templates/apps/web/app/api/auth/hub/authorize/__tests__/route.test.ts +114 -0
- package/templates/apps/web/app/api/auth/hub/authorize/route.ts +20 -3
- package/templates/apps/web/app/api/hub-session/__tests__/routes.test.ts +44 -1
- package/templates/apps/web/app/api/hub-session/sign-in/route.ts +22 -3
- package/templates/apps/web/app/api/offerings/checkout/[lookupKey]/route.ts +316 -0
- package/templates/apps/web/app/api/offerings/checkout/__tests__/route.test.ts +810 -0
- package/templates/apps/web/auth.ts +28 -2
- package/templates/apps/web/lib/__tests__/account-invite.test.ts +63 -0
- package/templates/apps/web/lib/__tests__/auth-routes.test.ts +27 -1
- package/templates/apps/web/lib/__tests__/consent-policies.test.ts +23 -0
- package/templates/apps/web/lib/__tests__/deck-page.test.ts +7 -1
- package/templates/apps/web/lib/__tests__/in-page-magnet.test.ts +265 -0
- package/templates/apps/web/lib/__tests__/magnet-routes.test.ts +57 -0
- package/templates/apps/web/lib/__tests__/newsletter.actions.test.ts +293 -0
- package/templates/apps/web/lib/__tests__/offerings-pricing.test.ts +601 -0
- package/templates/apps/web/lib/__tests__/structured-data.test.tsx +101 -25
- package/templates/apps/web/lib/account-invite.ts +37 -0
- package/templates/apps/web/lib/auth-routes.ts +36 -2
- package/templates/apps/web/lib/checkout-config.ts +16 -0
- package/templates/apps/web/lib/consent-policies.ts +22 -0
- package/templates/apps/web/lib/deck-page.ts +7 -0
- package/templates/apps/web/lib/deliverable-grant.ts +18 -0
- package/templates/apps/web/lib/hub-session/__tests__/sign-in-gate.test.tsx +111 -0
- package/templates/apps/web/lib/hub-session/sign-in-gate.tsx +88 -32
- package/templates/apps/web/lib/in-page-magnet.ts +416 -0
- package/templates/apps/web/lib/magnet-routes.ts +42 -0
- package/templates/apps/web/lib/modules.generated.ts +9 -0
- package/templates/apps/web/lib/newsletter.actions.ts +202 -0
- package/templates/apps/web/lib/offerings-pricing.ts +326 -0
- package/templates/apps/web/lib/posting-title.ts +17 -0
- package/templates/apps/web/lib/prisma-for-auth.ts +8 -0
- package/templates/apps/web/lib/sign-up-agreements.ts +449 -0
- package/templates/apps/web/lib/site-chrome.ts +2 -0
- package/templates/apps/web/lib/telemetry-scrub.ts +5 -1
- package/templates/apps/web/messages/en.json +71 -20
- package/templates/apps/web/middleware.ts +12 -2
- package/templates/apps/web/package.json +1 -0
- package/templates/apps/web/security-headers.config.ts +17 -0
- package/templates/apps/web/src/lib/__tests__/lead-write.test.ts +187 -0
- package/templates/apps/web/src/lib/__tests__/rate-limit.test.ts +67 -0
- package/templates/apps/web/src/lib/lead-write.ts +90 -0
- package/templates/apps/web/src/lib/rate-limit.ts +79 -0
- package/templates/content/en/BlogList/blog.mdx +4 -71
- package/templates/content/en/BookCall/book-a-call.mdx +1 -41
- package/templates/content/en/BookCall/onsite-estimate.mdx +1 -17
- package/templates/content/en/BookCall/onsite-service.mdx +1 -17
- package/templates/content/en/BookCall/start-project.mdx +1 -17
- package/templates/content/en/ContactPage/contact.mdx +6 -63
- package/templates/content/en/HomePage/home.mdx +3 -21
- package/templates/database/CHANGELOG.md +69 -0
- package/templates/database/__tests__/consent-source-checkout-migration.test.ts +79 -0
- package/templates/database/__tests__/flow-completion-migration.test.ts +115 -0
- package/templates/database/__tests__/flow-completion.db.test.ts +136 -0
- package/templates/database/__tests__/in-page-magnets-migration.test.ts +161 -0
- package/templates/database/__tests__/offerings-checkout-session.db.test.ts +270 -0
- package/templates/database/__tests__/offerings-migration.test.ts +197 -0
- package/templates/database/__tests__/offerings-shown-price-ids-migration.test.ts +86 -0
- package/templates/database/__tests__/offerings.db.test.ts +303 -0
- package/templates/database/__tests__/seed.test.ts +64 -1
- package/templates/database/auth/schema.prisma +4 -0
- package/templates/database/config/schema.prisma +91 -0
- package/templates/database/inbox/schema.prisma +17 -2
- package/templates/database/migrations/20261018120000_flow_completion/migration.sql +52 -0
- package/templates/database/migrations/20261019120000_consent_source_checkout/migration.sql +27 -0
- package/templates/database/migrations/20261020120000_offerings_data_model/migration.sql +146 -0
- package/templates/database/migrations/20261021120000_offerings_checkout_session/migration.sql +77 -0
- package/templates/database/migrations/20261022120000_in_page_magnets/migration.sql +45 -0
- package/templates/database/migrations/20261023120000_offerings_shown_price_ids/migration.sql +47 -0
- package/templates/database/migrations/20261024120000_organization_plan_checkout_session/migration.sql +16 -0
- package/templates/database/package.json +1 -1
- package/templates/database/seed.ts +62 -3
- package/versions.json +35 -35
- package/site-templates/marketing-site-free/content/en/Pricing/pricing.mdx +0 -199
- package/templates/apps/web/app/[locale]/(auth)/sign-up/sign-up.actions.ts +0 -125
- package/templates/apps/web/app/[locale]/downloads/__tests__/page-renderer-migration.test.tsx +0 -93
- package/templates/apps/web/app/[locale]/downloads/page.tsx +0 -37
- package/templates/apps/web/app/[locale]/newsletter/__tests__/newsletter.actions.test.ts +0 -139
- package/templates/apps/web/app/[locale]/newsletter/newsletter.actions.ts +0 -93
- package/templates/apps/web/lib/__tests__/meeting-slugs.test.ts +0 -120
- package/templates/apps/web/lib/meeting-slugs.ts +0 -63
|
@@ -14,13 +14,35 @@
|
|
|
14
14
|
* offering fields on its own frontmatter answer instead — a listing that
|
|
15
15
|
* declares `price` + `priceCurrency` yields one tier named for the page. A
|
|
16
16
|
* listing with neither an authored offer nor a price still returns `null`: no
|
|
17
|
-
* price means no tier, never a fabricated zero.
|
|
17
|
+
* price means no tier, never a fabricated zero. `getPricingTiers`
|
|
18
|
+
* (`@working-theory/content-adapters`) documents the mapping.
|
|
19
|
+
*
|
|
20
|
+
* HTTP twin (#7178): `GET /v1/content/pricing` in `apps/api` takes the same
|
|
21
|
+
* arguments and answers through the same `getPricingTiers` — the shared seam
|
|
22
|
+
* the pair proves parity at. The tool still reads content locally, so it
|
|
23
|
+
* needs no API to answer.
|
|
24
|
+
*
|
|
25
|
+
* Offerings (#8933, spec D8): when the site sells through its connected
|
|
26
|
+
* Stripe catalog and `slug` is omitted or `pricing`, the tool answers the
|
|
27
|
+
* workspace's Offerings shown set instead — read over HTTP by
|
|
28
|
+
* `lib/shown-offerings.ts` (D6: only on a `claimed` host, at most
|
|
29
|
+
* `MCP_SHOWN_SET_BUDGET_MS`) and mapped by `pricingTiersFromShownSet`, the
|
|
30
|
+
* function the twin route answers through too, so each tier also carries
|
|
31
|
+
* `interval` and the full `lookupKey`. Names, descriptions and prices are
|
|
32
|
+
* seller-provided data returned as plain JSON values (T5). Any other slug, an
|
|
33
|
+
* unclaimed host, a workspace with no current set, a failed read or a mapping
|
|
34
|
+
* that throws answers the page content below, unchanged.
|
|
18
35
|
*
|
|
19
36
|
* Input: { slug?: string, locale?: string }
|
|
20
|
-
* Output: { tiers: Array<{ name, price, priceCurrency, description? }> } | null
|
|
37
|
+
* Output: { tiers: Array<{ name, price, priceCurrency, description?, interval?, lookupKey? }> } | null
|
|
21
38
|
* Session/scope: none — read-only public content.
|
|
22
39
|
*/
|
|
23
40
|
|
|
41
|
+
import {
|
|
42
|
+
getPricingTiers,
|
|
43
|
+
pricingTiersFromShownSet,
|
|
44
|
+
shownSetAnswersPricing,
|
|
45
|
+
} from "@working-theory/content-adapters";
|
|
24
46
|
import {
|
|
25
47
|
getPricingInputSchema,
|
|
26
48
|
getPricingOutputSchema,
|
|
@@ -33,12 +55,14 @@ import {
|
|
|
33
55
|
mcpContentRoot,
|
|
34
56
|
} from "../lib/content-adapter.js";
|
|
35
57
|
import { resolveLocale } from "../lib/resolve-locale.js";
|
|
58
|
+
import {
|
|
59
|
+
mcpCurrentShownSet,
|
|
60
|
+
reportMappingFailure,
|
|
61
|
+
} from "../lib/shown-offerings.js";
|
|
36
62
|
|
|
37
63
|
export { getPricingInputSchema, getPricingOutputSchema };
|
|
38
64
|
export type { GetPricingInput, GetPricingOutput };
|
|
39
65
|
|
|
40
|
-
const DEFAULT_SLUG = "pricing";
|
|
41
|
-
|
|
42
66
|
/**
|
|
43
67
|
* get_pricing handler.
|
|
44
68
|
*
|
|
@@ -51,44 +75,20 @@ export async function ailkGetPricing(
|
|
|
51
75
|
): Promise<GetPricingOutput> {
|
|
52
76
|
getPricingInputSchema.parse(input);
|
|
53
77
|
|
|
78
|
+
if (shownSetAnswersPricing(input.slug)) {
|
|
79
|
+
const shownSet = await mcpCurrentShownSet();
|
|
80
|
+
if (shownSet !== null) {
|
|
81
|
+
try {
|
|
82
|
+
return pricingTiersFromShownSet(shownSet);
|
|
83
|
+
} catch (err) {
|
|
84
|
+
reportMappingFailure(err);
|
|
85
|
+
}
|
|
86
|
+
}
|
|
87
|
+
}
|
|
88
|
+
|
|
54
89
|
const root = contentRoot ?? mcpContentRoot();
|
|
55
90
|
const locale = resolveLocale(input.locale);
|
|
56
91
|
const adapter = getMcpContentAdapter({ contentRoot: root, locale });
|
|
57
92
|
|
|
58
|
-
|
|
59
|
-
const data = await adapter.extractStructuredData("Product", slug, locale);
|
|
60
|
-
|
|
61
|
-
const schema = (data ?? {}) as Record<string, unknown>;
|
|
62
|
-
|
|
63
|
-
// Support both a top-level `offers` array and a single offer object.
|
|
64
|
-
const offersRaw = schema["offers"];
|
|
65
|
-
const offersArr: Record<string, unknown>[] = Array.isArray(offersRaw)
|
|
66
|
-
? (offersRaw as Record<string, unknown>[])
|
|
67
|
-
: offersRaw && typeof offersRaw === "object"
|
|
68
|
-
? [offersRaw as Record<string, unknown>]
|
|
69
|
-
: [];
|
|
70
|
-
|
|
71
|
-
if (offersArr.length === 0) {
|
|
72
|
-
const page = await adapter.getPageBySlug("Product", slug, locale);
|
|
73
|
-
if (!page || page.price === undefined) return null;
|
|
74
|
-
return getPricingOutputSchema.parse({
|
|
75
|
-
tiers: [
|
|
76
|
-
{
|
|
77
|
-
name: page.title,
|
|
78
|
-
price: page.price,
|
|
79
|
-
priceCurrency: page.priceCurrency ?? "USD",
|
|
80
|
-
...(page.summary !== undefined && { description: page.summary }),
|
|
81
|
-
},
|
|
82
|
-
],
|
|
83
|
-
});
|
|
84
|
-
}
|
|
85
|
-
|
|
86
|
-
const tiers = offersArr.map((offer) => ({
|
|
87
|
-
name: (offer["name"] as string) ?? "",
|
|
88
|
-
price: (offer["price"] as string | number) ?? "",
|
|
89
|
-
priceCurrency: (offer["priceCurrency"] as string) ?? "USD",
|
|
90
|
-
description: offer["description"] as string | undefined,
|
|
91
|
-
}));
|
|
92
|
-
|
|
93
|
-
return getPricingOutputSchema.parse({ tiers });
|
|
93
|
+
return getPricingTiers(adapter, { slug: input.slug, locale });
|
|
94
94
|
}
|
|
@@ -57,11 +57,24 @@
|
|
|
57
57
|
* through `@working-theory/api-client` per the architecture invariants (apps
|
|
58
58
|
* may not import each other; the shared HTTP surface lives in packages/).
|
|
59
59
|
*
|
|
60
|
-
*
|
|
60
|
+
* THE KEY — the full Offerings lookup_key (#8935). `productCode` is a price's
|
|
61
|
+
* full lookup_key (contract §3.7), the `lookupKey` `get_pricing` and
|
|
62
|
+
* `get_offerings` return. After the claim and the strict schema, the one
|
|
63
|
+
* §3.7 parser, `parseLookupKey`, refuses an ungrammatical key before any HTTP
|
|
64
|
+
* call, with the same not-found body every route 404 maps to (A3, A5). The
|
|
65
|
+
* workspace stays the server's (A1): the api-client attaches the
|
|
66
|
+
* deployment's own spoke key, and the route alone decides whether the key is
|
|
67
|
+
* shown and org-enabled. A spoke whose Organization has no Stripe connection
|
|
68
|
+
* gets the route's same 404, never a session on the hub's own env key, so
|
|
69
|
+
* this tool refuses it as the web buy route does (#9126).
|
|
70
|
+
*
|
|
71
|
+
* Spec: `docs/specs/5971-start-purchase-hosted-checkout-spec.md`,
|
|
72
|
+
* `docs/specs/8935-start-purchase-sells-a-named-spec.md`.
|
|
61
73
|
*/
|
|
62
74
|
|
|
63
75
|
import { checkout } from "@working-theory/api-client";
|
|
64
76
|
import {
|
|
77
|
+
parseLookupKey,
|
|
65
78
|
purchaseMcpInputSchema,
|
|
66
79
|
type PurchaseMcpInput,
|
|
67
80
|
type PurchaseOutput,
|
|
@@ -83,6 +96,15 @@ function commerceDisabledBody(): ErrorBody {
|
|
|
83
96
|
return notFoundError("Not found");
|
|
84
97
|
}
|
|
85
98
|
|
|
99
|
+
/**
|
|
100
|
+
* The one not-found body for an offering (#8935 D2, A5): an ungrammatical key
|
|
101
|
+
* refused before the call and every route 404 answer this same value, and
|
|
102
|
+
* neither echoes the caller's key.
|
|
103
|
+
*/
|
|
104
|
+
function offeringNotFoundBody(): ErrorBody {
|
|
105
|
+
return notFoundError("No offering matches that product code.");
|
|
106
|
+
}
|
|
107
|
+
|
|
86
108
|
/**
|
|
87
109
|
* Export name `ailkPurchase` satisfies the parity-test naming convention
|
|
88
110
|
* (ailk<PascalVerb>) so the harness can discover it.
|
|
@@ -127,6 +149,12 @@ export async function ailkPurchase(
|
|
|
127
149
|
|
|
128
150
|
const { productCode, quantity } = parsed.data;
|
|
129
151
|
|
|
152
|
+
// #8935 D2 — a key outside the §3.7 grammar can never sell, so it is
|
|
153
|
+
// refused here, before the route spends a rate-limit token on it.
|
|
154
|
+
if (parseLookupKey(productCode) === null) {
|
|
155
|
+
return offeringNotFoundBody();
|
|
156
|
+
}
|
|
157
|
+
|
|
130
158
|
// successUrl/cancelUrl are ALWAYS derived from baseUrl (#6001) — never
|
|
131
159
|
// accepted from the caller. allowPromotionCodes is never forwarded: it is
|
|
132
160
|
// not part of purchaseMcpInputSchema, so it can never be present here.
|
|
@@ -152,7 +180,7 @@ export async function ailkPurchase(
|
|
|
152
180
|
// states and tests that invariant ("Error responses never echo user
|
|
153
181
|
// input", apps/api/src/routes/checkout/sessions.ts), and parity means
|
|
154
182
|
// the pair answers alike.
|
|
155
|
-
return
|
|
183
|
+
return offeringNotFoundBody();
|
|
156
184
|
}
|
|
157
185
|
return validationError(
|
|
158
186
|
`checkout-unavailable: POST /v1/checkout/sessions returned ${result.status}`,
|
|
@@ -83,6 +83,21 @@ const WORKSPACE_TOOL_ENTRIES: Record<
|
|
|
83
83
|
specifier: "./tools/inbox_submissions.js",
|
|
84
84
|
exportName: "ailkMarkInboxSubmissionDone",
|
|
85
85
|
},
|
|
86
|
+
list_listing_applications: {
|
|
87
|
+
specifier: "./tools/inbox_applications.js",
|
|
88
|
+
exportName: "ailkListListingApplications",
|
|
89
|
+
},
|
|
90
|
+
get_listing_application: {
|
|
91
|
+
specifier: "./tools/inbox_applications.js",
|
|
92
|
+
exportName: "ailkGetListingApplication",
|
|
93
|
+
},
|
|
94
|
+
decide_listing_application: {
|
|
95
|
+
specifier: "./tools/inbox_applications.js",
|
|
96
|
+
exportName: "ailkDecideListingApplication",
|
|
97
|
+
},
|
|
98
|
+
// #9137 — the pre-rename names, deprecated aliases of the three above for
|
|
99
|
+
// one release: each export is the same function, so the same arguments and
|
|
100
|
+
// result.
|
|
86
101
|
list_inbox_applications: {
|
|
87
102
|
specifier: "./tools/inbox_applications.js",
|
|
88
103
|
exportName: "ailkListInboxApplications",
|
|
@@ -92,6 +92,20 @@ CONTENT_ADAPTER=mdx
|
|
|
92
92
|
# origin the browser never sees.
|
|
93
93
|
# NEXT_PUBLIC_AILK_API_URL=http://localhost:3002
|
|
94
94
|
|
|
95
|
+
# =============================================================================
|
|
96
|
+
# The /contact lead's site marker (server-side only) — #8747
|
|
97
|
+
# =============================================================================
|
|
98
|
+
# This site's slug. The /contact Server Action writes it into every lead as
|
|
99
|
+
# payload.site (with payload.page = "contact"), so a hub serving several sites
|
|
100
|
+
# — or one site routing leads to two workspaces — can tell where a lead came
|
|
101
|
+
# from. Use the site's short name, lowercase letters, digits and hyphens (for
|
|
102
|
+
# example "wendihenry"); apps/mcp's capture_lead reads the same variable. Unset
|
|
103
|
+
# or blank leaves the lead with no site marker — nothing is invented, and a
|
|
104
|
+
# visitor-sent "site" field is dropped. The marker is attribution only: the
|
|
105
|
+
# lead route is public, so never gate access on it. The action sends no
|
|
106
|
+
# siteId; the API names the workspace itself (by AILK_SPOKE_KEY on a hub).
|
|
107
|
+
# AILK_LEAD_SITE=
|
|
108
|
+
|
|
95
109
|
# =============================================================================
|
|
96
110
|
# Listicle directory (#7521) — switched on by the site's listing key (#7872)
|
|
97
111
|
# =============================================================================
|
|
@@ -247,37 +261,16 @@ CONTENT_ADAPTER=mdx
|
|
|
247
261
|
# BETTER_STACK_HEARTBEAT_URL=
|
|
248
262
|
|
|
249
263
|
# =============================================================================
|
|
250
|
-
# Scheduling — the scheduling module's booking
|
|
264
|
+
# Scheduling — the scheduling module's booking pages (#4633, #8843)
|
|
251
265
|
# =============================================================================
|
|
252
|
-
#
|
|
253
|
-
#
|
|
254
|
-
#
|
|
255
|
-
#
|
|
256
|
-
#
|
|
257
|
-
#
|
|
258
|
-
#
|
|
259
|
-
#
|
|
260
|
-
# NOT a credential, and deliberately NOT a NEXT_PUBLIC_ variable — the API key
|
|
261
|
-
# it pairs with (CAL_API_KEY) lives in apps/api and this app never reads it, so
|
|
262
|
-
# keeping the whole scheduling config surface server-read is what makes that
|
|
263
|
-
# boundary checkable rather than merely intended.
|
|
264
|
-
# SCHEDULING_CAL_LINK=owner/event-slug
|
|
265
|
-
#
|
|
266
|
-
# The booker's /v1/schedule/* calls go through this site's same-origin relay
|
|
267
|
-
# (see the relay block above, #7644), so the baseline CSP's
|
|
268
|
-
# `connect-src 'self'` already covers them — no CSP extension is needed for
|
|
269
|
-
# them, whatever NEXT_PUBLIC_AILK_API_URL is set to.
|
|
266
|
+
# No variable here. /meet, /private and /meet/<slug> read every meeting fact
|
|
267
|
+
# — which meetings are Live, public or private, their titles, lengths and
|
|
268
|
+
# Cal.com links — from apps/api's GET /v1/schedule/meetings, which resolves
|
|
269
|
+
# the workspace from this site (the site alias keyless, a spoke by its key)
|
|
270
|
+
# and composes each Cal.com link from that workspace's own account. The
|
|
271
|
+
# one-meeting "<owner>/<event-slug>" variable this block used to set is
|
|
272
|
+
# retired, and nothing reads it.
|
|
270
273
|
#
|
|
271
274
|
# On a SPOKE (create-ailk --topology spoke, #7872) this site has no apps/api:
|
|
272
|
-
#
|
|
273
|
-
#
|
|
274
|
-
# - the FIRST segment (the owner) must name the provider account the hub
|
|
275
|
-
# uses for this spoke's workspace (its scheduling connection). It composes
|
|
276
|
-
# each booker's calLink; it is not an access control — the hub checks a
|
|
277
|
-
# spoke booking's slug, never its owner.
|
|
278
|
-
# - the SECOND segment is ignored: a workspace with no stored meeting-type
|
|
279
|
-
# list shows no meetings, because the hub books only a slug it stores.
|
|
280
|
-
# - the booker's two calls count against the relay's per-visitor budget,
|
|
281
|
-
# which holds per visitor only behind an edge that overwrites
|
|
282
|
-
# x-forwarded-for and x-real-ip (Vercel does); elsewhere every visitor
|
|
283
|
-
# shares one budget.
|
|
275
|
+
# the pages read the hub's meetings for this spoke's workspace at AILK_API_URL
|
|
276
|
+
# with AILK_SPOKE_KEY, so a workspace with no stored meeting list shows none.
|