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.
Files changed (342) hide show
  1. package/component-catalog.json +186 -8
  2. package/component-catalog.md +9 -7
  3. package/dist/add-module.js +20 -14
  4. package/dist/add-route.d.ts +55 -1
  5. package/dist/add-route.js +294 -12
  6. package/dist/cli.js +27 -10
  7. package/dist/derive-architecture.d.ts +12 -3
  8. package/dist/derive-architecture.js +20 -8
  9. package/dist/design-package.js +7 -2
  10. package/dist/lib/design-checks.js +4 -2
  11. package/dist/lib/env-catalog.js +5 -5
  12. package/dist/lib/npm-auth.d.ts +9 -0
  13. package/dist/lib/npm-auth.js +48 -0
  14. package/dist/lib/scaffold-render-sweep.d.ts +3 -2
  15. package/dist/lib/scaffold-render-sweep.js +5 -8
  16. package/dist/module-architecture.js +99 -9
  17. package/dist/paid-sections.d.ts +21 -2
  18. package/dist/paid-sections.js +69 -7
  19. package/dist/programmatic.js +19 -5
  20. package/dist/seed-flows.d.ts +4 -1
  21. package/dist/seed-flows.js +50 -4
  22. package/dist/site-chrome.d.ts +8 -0
  23. package/dist/site-chrome.js +17 -1
  24. package/dist/spoke-checkout.d.ts +65 -41
  25. package/dist/spoke-checkout.js +208 -430
  26. package/dist/starter-grid.d.ts +0 -3
  27. package/dist/starter-grid.js +24 -14
  28. package/dist/surfaces.js +52 -26
  29. package/dist/sync-routes.d.ts +38 -1
  30. package/dist/sync-routes.js +187 -13
  31. package/dist/template-record.d.ts +4 -3
  32. package/dist/template-record.js +8 -4
  33. package/dist/topology.js +25 -1
  34. package/dist/update.js +15 -0
  35. package/package.json +3 -3
  36. package/site-templates/_base/content/en/NewsletterSignup/newsletter.mdx +4 -49
  37. package/site-templates/_base/content/en/Pricing/pricing.mdx +3 -21
  38. package/site-templates/designs/minimalism/DESIGN.md +1 -1
  39. package/site-templates/marketing-site/TEMPLATE-MANIFEST.json +23 -20
  40. package/site-templates/marketing-site/TEMPLATE-MANIFEST.md +28 -14
  41. package/site-templates/marketing-site-free/TEMPLATE-MANIFEST.json +15 -39
  42. package/site-templates/marketing-site-free/TEMPLATE-MANIFEST.md +38 -34
  43. package/site-templates/marketing-site-free/content/en/NewsletterSignup/newsletter.mdx +6 -43
  44. package/site-templates/shapes/local-business-service/TEMPLATE-MANIFEST.json +14 -31
  45. package/site-templates/shapes/local-business-service/TEMPLATE-MANIFEST.md +26 -25
  46. package/site-templates/shapes/local-business-service/content/en/NewsletterSignup/newsletter.mdx +6 -43
  47. package/site-templates/shapes/organization-service/TEMPLATE-MANIFEST.json +14 -31
  48. package/site-templates/shapes/organization-service/TEMPLATE-MANIFEST.md +26 -25
  49. package/site-templates/shapes/organization-service/content/en/NewsletterSignup/newsletter.mdx +6 -43
  50. package/site-templates/shapes/person-service/TEMPLATE-MANIFEST.json +14 -31
  51. package/site-templates/shapes/person-service/TEMPLATE-MANIFEST.md +26 -25
  52. package/site-templates/shapes/person-service/content/en/NewsletterSignup/newsletter.mdx +6 -43
  53. package/templates/.env.example +9 -7
  54. package/templates/.github/workflows/ci.yml +13 -0
  55. package/templates/README.md +1 -0
  56. package/templates/_gitignore +4 -0
  57. package/templates/apps/api/.env.example +27 -0
  58. package/templates/apps/api/CLAUDE.md +87 -17
  59. package/templates/apps/api/package.json +1 -0
  60. package/templates/apps/api/src/__tests__/factories/in-memory-prisma.ts +2 -0
  61. package/templates/apps/api/src/bin/stripe-connections-rekey.ts +45 -0
  62. package/templates/apps/api/src/config/__tests__/stripe-connection-key.test.ts +185 -0
  63. package/templates/apps/api/src/config/index.ts +109 -0
  64. package/templates/apps/api/src/lib/__mocks__/prisma.ts +3 -0
  65. package/templates/apps/api/src/lib/__tests__/consent-store.test.ts +19 -0
  66. package/templates/apps/api/src/lib/__tests__/flow-completion-store.test.ts +149 -0
  67. package/templates/apps/api/src/lib/__tests__/flow-rename-race.db.test.ts +116 -0
  68. package/templates/apps/api/src/lib/consent-store.ts +28 -0
  69. package/templates/apps/api/src/lib/error-serializer.ts +74 -7
  70. package/templates/apps/api/src/lib/flow-completion-store.ts +136 -0
  71. package/templates/apps/api/src/lib/hub-sign-in-store.ts +38 -2
  72. package/templates/apps/api/src/lib/proposal-paid-store.ts +21 -4
  73. package/templates/apps/api/src/lib/stripe.ts +74 -4
  74. package/templates/apps/api/src/lib/webhook-idempotency.ts +66 -2
  75. package/templates/apps/api/src/middleware/__tests__/auth.hub-session-path.test.ts +14 -3
  76. package/templates/apps/api/src/middleware/__tests__/rate-limit.test.ts +218 -0
  77. package/templates/apps/api/src/middleware/__tests__/write-budget.test.ts +174 -0
  78. package/templates/apps/api/src/middleware/auth.ts +13 -6
  79. package/templates/apps/api/src/middleware/rate-limit.ts +226 -4
  80. package/templates/apps/api/src/openapi/spec.ts +33 -3
  81. package/templates/apps/api/src/routes/auth/__tests__/hub-sign-in.test.ts +49 -0
  82. package/templates/apps/api/src/routes/auth/hub-sign-in.ts +41 -0
  83. package/templates/apps/api/src/routes/billing/plan-checkout.ts +325 -0
  84. package/templates/apps/api/src/routes/checkout/__tests__/live-key.test.ts +11 -0
  85. package/templates/apps/api/src/routes/checkout/__tests__/offerings-authorization.test.ts +1300 -0
  86. package/templates/apps/api/src/routes/checkout/__tests__/session.test.ts +11 -0
  87. package/templates/apps/api/src/routes/checkout/__tests__/sessions.test.ts +49 -0
  88. package/templates/apps/api/src/routes/checkout/session.ts +57 -25
  89. package/templates/apps/api/src/routes/checkout/sessions.ts +232 -82
  90. package/templates/apps/api/src/routes/consent/__tests__/account.test.ts +386 -0
  91. package/templates/apps/api/src/routes/consent/__tests__/sign-up-rate-limit.test.ts +160 -0
  92. package/templates/apps/api/src/routes/consent/account.ts +268 -0
  93. package/templates/apps/api/src/routes/consent/index.ts +15 -7
  94. package/templates/apps/api/src/routes/content/__tests__/get-page.test.ts +36 -0
  95. package/templates/apps/api/src/routes/content/__tests__/offerings.test.ts +731 -0
  96. package/templates/apps/api/src/routes/content/__tests__/pricing.test.ts +753 -0
  97. package/templates/apps/api/src/routes/content/__tests__/shown-offerings.test.ts +762 -0
  98. package/templates/apps/api/src/routes/content/get-page.ts +4 -0
  99. package/templates/apps/api/src/routes/content/index.ts +16 -2
  100. package/templates/apps/api/src/routes/content/offerings.ts +184 -0
  101. package/templates/apps/api/src/routes/content/pricing.ts +176 -0
  102. package/templates/apps/api/src/routes/content/shown-offerings.ts +229 -0
  103. package/templates/apps/api/src/routes/leads/__tests__/magnet-completion.test.ts +515 -0
  104. package/templates/apps/api/src/routes/leads/index.ts +164 -7
  105. package/templates/apps/api/src/routes/webhooks/README.md +66 -0
  106. package/templates/apps/api/src/routes/webhooks/__tests__/stripe-checkout-consent.test.ts +649 -0
  107. package/templates/apps/api/src/routes/webhooks/__tests__/stripe-offerings.test.ts +814 -0
  108. package/templates/apps/api/src/routes/webhooks/__tests__/stripe-org-billing.test.ts +80 -68
  109. package/templates/apps/api/src/routes/webhooks/__tests__/stripe-site-filter.test.ts +5 -5
  110. package/templates/apps/api/src/routes/webhooks/__tests__/stripe.test.ts +8 -2
  111. package/templates/apps/api/src/routes/webhooks/index.ts +23 -0
  112. package/templates/apps/api/src/routes/webhooks/stripe-offerings.ts +384 -0
  113. package/templates/apps/api/src/routes/webhooks/stripe.ts +245 -36
  114. package/templates/apps/api/src/server.ts +163 -46
  115. package/templates/apps/api/src/services/__tests__/checkout-plan.test.ts +313 -0
  116. package/templates/apps/api/src/services/__tests__/deliverable-fulfillment.test.ts +51 -2
  117. package/templates/apps/api/src/services/__tests__/keyless-workspace-predicate.test.ts +234 -0
  118. package/templates/apps/api/src/services/__tests__/lead-routing.test.ts +1383 -133
  119. package/templates/apps/api/src/services/__tests__/magnet-completion.test.ts +389 -0
  120. package/templates/apps/api/src/services/__tests__/person.test.ts +232 -1
  121. package/templates/apps/api/src/services/__tests__/plan-offerings.test.ts +444 -0
  122. package/templates/apps/api/src/services/checkout-buyer.ts +100 -4
  123. package/templates/apps/api/src/services/consent.ts +95 -0
  124. package/templates/apps/api/src/services/deliverable-fulfillment.ts +13 -5
  125. package/templates/apps/api/src/services/entitlements.ts +157 -1
  126. package/templates/apps/api/src/services/lead-routing.ts +431 -47
  127. package/templates/apps/api/src/services/magnet-completion.ts +242 -0
  128. package/templates/apps/api/src/services/offerings/__tests__/catalog.test.ts +295 -0
  129. package/templates/apps/api/src/services/offerings/__tests__/checkout-session-record.test.ts +268 -0
  130. package/templates/apps/api/src/services/offerings/__tests__/connection.test.ts +868 -0
  131. package/templates/apps/api/src/services/offerings/__tests__/fixtures.ts +416 -0
  132. package/templates/apps/api/src/services/offerings/__tests__/org-products.test.ts +396 -0
  133. package/templates/apps/api/src/services/offerings/__tests__/readers.test.ts +681 -0
  134. package/templates/apps/api/src/services/offerings/__tests__/source.test.ts +231 -0
  135. package/templates/apps/api/src/services/offerings/catalog.ts +308 -0
  136. package/templates/apps/api/src/services/offerings/checkout-authorization.ts +385 -0
  137. package/templates/apps/api/src/services/offerings/checkout-session-record.ts +192 -0
  138. package/templates/apps/api/src/services/offerings/connection.ts +1003 -0
  139. package/templates/apps/api/src/services/offerings/index.ts +614 -0
  140. package/templates/apps/api/src/services/offerings/source.ts +140 -0
  141. package/templates/apps/api/src/services/offerings/store.ts +150 -0
  142. package/templates/apps/api/src/services/person.ts +164 -17
  143. package/templates/apps/api/src/services/plan-offerings.ts +125 -0
  144. package/templates/apps/api/src/services/public-site-resolver.ts +27 -15
  145. package/templates/apps/mcp/CLAUDE.md +22 -14
  146. package/templates/apps/mcp/__tests__/catalog-drift.test.ts +9 -4
  147. package/templates/apps/mcp/__tests__/get_offerings.test.ts +358 -0
  148. package/templates/apps/mcp/__tests__/get_pricing.test.ts +364 -0
  149. package/templates/apps/mcp/__tests__/http-transport.test.ts +4 -4
  150. package/templates/apps/mcp/__tests__/module-exclusion.test.ts +2 -2
  151. package/templates/apps/mcp/__tests__/server.test.ts +13 -11
  152. package/templates/apps/mcp/__tests__/start_purchase.test.ts +208 -12
  153. package/templates/apps/mcp/__tests__/tools/purchase.test.ts +11 -11
  154. package/templates/apps/mcp/src/lib/shown-offerings.ts +105 -0
  155. package/templates/apps/mcp/src/server.ts +5 -0
  156. package/templates/apps/mcp/src/tools/get_offerings.ts +47 -30
  157. package/templates/apps/mcp/src/tools/get_pricing.ts +40 -40
  158. package/templates/apps/mcp/src/tools/purchase.ts +30 -2
  159. package/templates/apps/mcp/src/workspace-server.ts +15 -0
  160. package/templates/apps/web/.env.example +24 -31
  161. package/templates/apps/web/CLAUDE.md +393 -9
  162. package/templates/apps/web/__tests__/__mocks__/next-intl-server.ts +6 -2
  163. package/templates/apps/web/app/[locale]/(auth)/__tests__/auth-pages.test.tsx +5 -4
  164. package/templates/apps/web/app/[locale]/(auth)/forgot-password/forgot-password-card.tsx +5 -1
  165. package/templates/apps/web/app/[locale]/(auth)/forgot-password/page.tsx +2 -0
  166. package/templates/apps/web/app/[locale]/(auth)/layout.tsx +3 -14
  167. package/templates/apps/web/app/[locale]/(auth)/reset-password/page.tsx +2 -0
  168. package/templates/apps/web/app/[locale]/(auth)/reset-password/reset-password-card.tsx +5 -1
  169. package/templates/apps/web/app/[locale]/(auth)/sign-in/__tests__/page.test.tsx +160 -8
  170. package/templates/apps/web/app/[locale]/(auth)/sign-in/__tests__/sign-in-card.test.tsx +12 -9
  171. package/templates/apps/web/app/[locale]/(auth)/sign-in/page.tsx +105 -2
  172. package/templates/apps/web/app/[locale]/(auth)/sign-in/sign-in-card.tsx +36 -3
  173. package/templates/apps/web/app/[locale]/(auth)/sign-in/sign-in-text.ts +47 -1
  174. package/templates/apps/web/app/[locale]/(auth)/sign-up/__tests__/sign-up-card.test.tsx +60 -22
  175. package/templates/apps/web/app/[locale]/(auth)/sign-up/page.tsx +16 -8
  176. package/templates/apps/web/app/[locale]/(auth)/sign-up/sign-up-card.tsx +85 -31
  177. package/templates/apps/web/app/[locale]/(auth)/use-auth-card-flow.ts +18 -4
  178. package/templates/apps/web/app/[locale]/(auth)/verify-email/page.tsx +3 -1
  179. package/templates/apps/web/app/[locale]/(authed)/__tests__/gate.test.tsx +7 -4
  180. package/templates/apps/web/app/[locale]/(authed)/account/__tests__/agreements-prompt.test.tsx +227 -207
  181. package/templates/apps/web/app/[locale]/(authed)/account/__tests__/layout-sign-out.test.tsx +17 -1
  182. package/templates/apps/web/app/[locale]/(authed)/account/__tests__/layout.test.tsx +143 -1
  183. package/templates/apps/web/app/[locale]/(authed)/account/__tests__/new-menu.test.tsx +4 -0
  184. package/templates/apps/web/app/[locale]/(authed)/account/__tests__/page.test.tsx +385 -89
  185. package/templates/apps/web/app/[locale]/(authed)/account/__tests__/popups.test.tsx +5 -0
  186. package/templates/apps/web/app/[locale]/(authed)/account/__tests__/privacy.test.tsx +44 -5
  187. package/templates/apps/web/app/[locale]/(authed)/account/account-frame.tsx +68 -34
  188. package/templates/apps/web/app/[locale]/(authed)/account/actions.ts +0 -8
  189. package/templates/apps/web/app/[locale]/(authed)/account/agreements-actions.ts +39 -151
  190. package/templates/apps/web/app/[locale]/(authed)/account/agreements-gate.ts +61 -0
  191. package/templates/apps/web/app/[locale]/(authed)/account/agreements-prompt.tsx +143 -140
  192. package/templates/apps/web/app/[locale]/(authed)/account/layout.tsx +40 -6
  193. package/templates/apps/web/app/[locale]/(authed)/account/page.tsx +68 -54
  194. package/templates/apps/web/app/[locale]/(authed)/account/privacy/page.tsx +5 -1
  195. package/templates/apps/web/app/[locale]/careers/[slug]/[action]/page.tsx +189 -0
  196. package/templates/apps/web/app/[locale]/careers/[slug]/apply.actions.ts +295 -0
  197. package/templates/apps/web/app/[locale]/careers/[slug]/job-application-form.ts +120 -0
  198. package/templates/apps/web/app/[locale]/careers/[slug]/page.tsx +23 -0
  199. package/templates/apps/web/app/[locale]/careers/__tests__/apply.actions.test.ts +319 -0
  200. package/templates/apps/web/app/[locale]/careers/__tests__/careers-hub.test.tsx +172 -0
  201. package/templates/apps/web/app/[locale]/careers/__tests__/careers-set.test.tsx +149 -0
  202. package/templates/apps/web/app/[locale]/careers/__tests__/job-application-form.test.ts +141 -0
  203. package/templates/apps/web/app/[locale]/careers/__tests__/job-apply-page.test.tsx +424 -0
  204. package/templates/apps/web/app/[locale]/careers/__tests__/page-renderer-migration.test.tsx +72 -11
  205. package/templates/apps/web/app/[locale]/careers/page.tsx +129 -5
  206. package/templates/apps/web/app/[locale]/case-studies/__tests__/page-renderer-migration.test.tsx +2 -2
  207. package/templates/apps/web/app/[locale]/checkout/__tests__/pages.test.tsx +139 -0
  208. package/templates/apps/web/app/[locale]/checkout/__tests__/thank-you-account.test.tsx +136 -0
  209. package/templates/apps/web/app/[locale]/checkout/thank-you/page.tsx +114 -0
  210. package/templates/apps/web/app/[locale]/checkout/unavailable/page.tsx +51 -0
  211. package/templates/apps/web/app/[locale]/clients/__tests__/page-renderer-migration.test.tsx +1 -1
  212. package/templates/apps/web/app/[locale]/contact/ConversationalIntakeWrapper.tsx +17 -1
  213. package/templates/apps/web/app/[locale]/contact/__tests__/ConversationalIntakeWrapper.test.tsx +79 -4
  214. package/templates/apps/web/app/[locale]/contact/__tests__/contact-magnet-page.test.tsx +160 -13
  215. package/templates/apps/web/app/[locale]/contact/__tests__/contact.actions.test.ts +466 -36
  216. package/templates/apps/web/app/[locale]/contact/__tests__/page-renderer-migration.test.tsx +7 -0
  217. package/templates/apps/web/app/[locale]/contact/contact.actions.ts +172 -57
  218. package/templates/apps/web/app/[locale]/contact/intake-script.ts +26 -0
  219. package/templates/apps/web/app/[locale]/contact/page.tsx +99 -60
  220. package/templates/apps/web/app/[locale]/courses/__tests__/page-renderer-migration.test.tsx +2 -2
  221. package/templates/apps/web/app/[locale]/events/__tests__/page-renderer-migration.test.tsx +2 -2
  222. package/templates/apps/web/app/[locale]/flows/[slug]/FlowStepperClient.tsx +341 -4
  223. package/templates/apps/web/app/[locale]/flows/[slug]/__tests__/FlowStepperClient.count.test.tsx +43 -0
  224. package/templates/apps/web/app/[locale]/flows/[slug]/__tests__/FlowStepperClient.deliverable.test.tsx +200 -0
  225. package/templates/apps/web/app/[locale]/flows/[slug]/__tests__/FlowStepperClient.ending.test.tsx +197 -0
  226. package/templates/apps/web/app/[locale]/flows/[slug]/__tests__/FlowStepperClient.invite.test.tsx +257 -0
  227. package/templates/apps/web/app/[locale]/flows/[slug]/__tests__/account-invite.actions.test.ts +69 -0
  228. package/templates/apps/web/app/[locale]/flows/[slug]/__tests__/magnet-page.test.tsx +430 -0
  229. package/templates/apps/web/app/[locale]/flows/[slug]/__tests__/two-route-tool.test.tsx +21 -9
  230. package/templates/apps/web/app/[locale]/flows/[slug]/account-invite.actions.ts +35 -0
  231. package/templates/apps/web/app/[locale]/flows/[slug]/magnet-page.tsx +352 -0
  232. package/templates/apps/web/app/[locale]/flows/[slug]/page.tsx +12 -73
  233. package/templates/apps/web/app/[locale]/flows/[slug]/start/page.tsx +10 -31
  234. package/templates/apps/web/app/[locale]/for/__tests__/page-renderer-migration.test.tsx +1 -1
  235. package/templates/apps/web/app/[locale]/how-to/__tests__/page-renderer-migration.test.tsx +1 -1
  236. package/templates/apps/web/app/[locale]/integrations/__tests__/page-renderer-migration.test.tsx +2 -2
  237. package/templates/apps/web/app/[locale]/layout.tsx +55 -4
  238. package/templates/apps/web/app/[locale]/newsletter/__tests__/page.test.tsx +174 -104
  239. package/templates/apps/web/app/[locale]/newsletter/page.tsx +95 -58
  240. package/templates/apps/web/app/[locale]/page.tsx +13 -1
  241. package/templates/apps/web/app/[locale]/partners/__tests__/page-renderer-migration.test.tsx +2 -2
  242. package/templates/apps/web/app/[locale]/pricing/__tests__/__snapshots__/unconnected.test.tsx.snap +7 -0
  243. package/templates/apps/web/app/[locale]/pricing/__tests__/offerings.test.tsx +599 -0
  244. package/templates/apps/web/app/[locale]/pricing/__tests__/page.test.tsx +216 -0
  245. package/templates/apps/web/app/[locale]/pricing/__tests__/unconnected.test.tsx +236 -0
  246. package/templates/apps/web/app/[locale]/pricing/page.tsx +367 -6
  247. package/templates/apps/web/app/[locale]/products/__tests__/page-renderer-migration.test.tsx +2 -2
  248. package/templates/apps/web/app/[locale]/resources/__tests__/page-renderer-migration.test.tsx +2 -2
  249. package/templates/apps/web/app/[locale]/services/__tests__/page-renderer-migration.test.tsx +2 -2
  250. package/templates/apps/web/app/[locale]/software/__tests__/page-renderer-migration.test.tsx +1 -1
  251. package/templates/apps/web/app/[locale]/team/__tests__/team-routes.test.tsx +4 -1
  252. package/templates/apps/web/app/[locale]/tech-articles/__tests__/page-renderer-migration.test.tsx +1 -1
  253. package/templates/apps/web/app/[locale]/testimonials/__tests__/page-renderer-migration.test.tsx +1 -1
  254. package/templates/apps/web/app/[locale]/thanks/[slug]/AccountInviteLink.tsx +89 -0
  255. package/templates/apps/web/app/[locale]/thanks/[slug]/__tests__/AccountInviteLink.test.tsx +104 -0
  256. package/templates/apps/web/app/[locale]/thanks/[slug]/__tests__/page.test.tsx +38 -0
  257. package/templates/apps/web/app/[locale]/thanks/[slug]/page.tsx +9 -0
  258. package/templates/apps/web/app/[locale]/unsubscribe/__tests__/unsubscribe-rsc-boundary.test.tsx +17 -0
  259. package/templates/apps/web/app/[locale]/unsubscribe/__tests__/unsubscribe.test.tsx +62 -3
  260. package/templates/apps/web/app/[locale]/unsubscribe/page.tsx +70 -18
  261. package/templates/apps/web/app/[locale]/unsubscribe/unsubscribe-button.tsx +30 -9
  262. package/templates/apps/web/app/[locale]/whitepapers/__tests__/page-renderer-migration.test.tsx +1 -1
  263. package/templates/apps/web/app/api/auth/hub/authorize/__tests__/route.test.ts +114 -0
  264. package/templates/apps/web/app/api/auth/hub/authorize/route.ts +20 -3
  265. package/templates/apps/web/app/api/hub-session/__tests__/routes.test.ts +44 -1
  266. package/templates/apps/web/app/api/hub-session/sign-in/route.ts +22 -3
  267. package/templates/apps/web/app/api/offerings/checkout/[lookupKey]/route.ts +316 -0
  268. package/templates/apps/web/app/api/offerings/checkout/__tests__/route.test.ts +810 -0
  269. package/templates/apps/web/auth.ts +28 -2
  270. package/templates/apps/web/lib/__tests__/account-invite.test.ts +63 -0
  271. package/templates/apps/web/lib/__tests__/auth-routes.test.ts +27 -1
  272. package/templates/apps/web/lib/__tests__/consent-policies.test.ts +23 -0
  273. package/templates/apps/web/lib/__tests__/deck-page.test.ts +7 -1
  274. package/templates/apps/web/lib/__tests__/in-page-magnet.test.ts +265 -0
  275. package/templates/apps/web/lib/__tests__/magnet-routes.test.ts +57 -0
  276. package/templates/apps/web/lib/__tests__/newsletter.actions.test.ts +293 -0
  277. package/templates/apps/web/lib/__tests__/offerings-pricing.test.ts +601 -0
  278. package/templates/apps/web/lib/__tests__/structured-data.test.tsx +101 -25
  279. package/templates/apps/web/lib/account-invite.ts +37 -0
  280. package/templates/apps/web/lib/auth-routes.ts +36 -2
  281. package/templates/apps/web/lib/checkout-config.ts +16 -0
  282. package/templates/apps/web/lib/consent-policies.ts +22 -0
  283. package/templates/apps/web/lib/deck-page.ts +7 -0
  284. package/templates/apps/web/lib/deliverable-grant.ts +18 -0
  285. package/templates/apps/web/lib/hub-session/__tests__/sign-in-gate.test.tsx +111 -0
  286. package/templates/apps/web/lib/hub-session/sign-in-gate.tsx +88 -32
  287. package/templates/apps/web/lib/in-page-magnet.ts +416 -0
  288. package/templates/apps/web/lib/magnet-routes.ts +42 -0
  289. package/templates/apps/web/lib/modules.generated.ts +9 -0
  290. package/templates/apps/web/lib/newsletter.actions.ts +202 -0
  291. package/templates/apps/web/lib/offerings-pricing.ts +326 -0
  292. package/templates/apps/web/lib/posting-title.ts +17 -0
  293. package/templates/apps/web/lib/prisma-for-auth.ts +8 -0
  294. package/templates/apps/web/lib/sign-up-agreements.ts +449 -0
  295. package/templates/apps/web/lib/site-chrome.ts +2 -0
  296. package/templates/apps/web/lib/telemetry-scrub.ts +5 -1
  297. package/templates/apps/web/messages/en.json +71 -20
  298. package/templates/apps/web/middleware.ts +12 -2
  299. package/templates/apps/web/package.json +1 -0
  300. package/templates/apps/web/security-headers.config.ts +17 -0
  301. package/templates/apps/web/src/lib/__tests__/lead-write.test.ts +187 -0
  302. package/templates/apps/web/src/lib/__tests__/rate-limit.test.ts +67 -0
  303. package/templates/apps/web/src/lib/lead-write.ts +90 -0
  304. package/templates/apps/web/src/lib/rate-limit.ts +79 -0
  305. package/templates/content/en/BlogList/blog.mdx +4 -71
  306. package/templates/content/en/BookCall/book-a-call.mdx +1 -41
  307. package/templates/content/en/BookCall/onsite-estimate.mdx +1 -17
  308. package/templates/content/en/BookCall/onsite-service.mdx +1 -17
  309. package/templates/content/en/BookCall/start-project.mdx +1 -17
  310. package/templates/content/en/ContactPage/contact.mdx +6 -63
  311. package/templates/content/en/HomePage/home.mdx +3 -21
  312. package/templates/database/CHANGELOG.md +69 -0
  313. package/templates/database/__tests__/consent-source-checkout-migration.test.ts +79 -0
  314. package/templates/database/__tests__/flow-completion-migration.test.ts +115 -0
  315. package/templates/database/__tests__/flow-completion.db.test.ts +136 -0
  316. package/templates/database/__tests__/in-page-magnets-migration.test.ts +161 -0
  317. package/templates/database/__tests__/offerings-checkout-session.db.test.ts +270 -0
  318. package/templates/database/__tests__/offerings-migration.test.ts +197 -0
  319. package/templates/database/__tests__/offerings-shown-price-ids-migration.test.ts +86 -0
  320. package/templates/database/__tests__/offerings.db.test.ts +303 -0
  321. package/templates/database/__tests__/seed.test.ts +64 -1
  322. package/templates/database/auth/schema.prisma +4 -0
  323. package/templates/database/config/schema.prisma +91 -0
  324. package/templates/database/inbox/schema.prisma +17 -2
  325. package/templates/database/migrations/20261018120000_flow_completion/migration.sql +52 -0
  326. package/templates/database/migrations/20261019120000_consent_source_checkout/migration.sql +27 -0
  327. package/templates/database/migrations/20261020120000_offerings_data_model/migration.sql +146 -0
  328. package/templates/database/migrations/20261021120000_offerings_checkout_session/migration.sql +77 -0
  329. package/templates/database/migrations/20261022120000_in_page_magnets/migration.sql +45 -0
  330. package/templates/database/migrations/20261023120000_offerings_shown_price_ids/migration.sql +47 -0
  331. package/templates/database/migrations/20261024120000_organization_plan_checkout_session/migration.sql +16 -0
  332. package/templates/database/package.json +1 -1
  333. package/templates/database/seed.ts +62 -3
  334. package/versions.json +35 -35
  335. package/site-templates/marketing-site-free/content/en/Pricing/pricing.mdx +0 -199
  336. package/templates/apps/web/app/[locale]/(auth)/sign-up/sign-up.actions.ts +0 -125
  337. package/templates/apps/web/app/[locale]/downloads/__tests__/page-renderer-migration.test.tsx +0 -93
  338. package/templates/apps/web/app/[locale]/downloads/page.tsx +0 -37
  339. package/templates/apps/web/app/[locale]/newsletter/__tests__/newsletter.actions.test.ts +0 -139
  340. package/templates/apps/web/app/[locale]/newsletter/newsletter.actions.ts +0 -93
  341. package/templates/apps/web/lib/__tests__/meeting-slugs.test.ts +0 -120
  342. package/templates/apps/web/lib/meeting-slugs.ts +0 -63
@@ -133,10 +133,9 @@ _Detail route over `content/en/UseCase/` — 3 seed pages: `persona-1.mdx`, `per
133
133
 
134
134
  <!-- generated:begin page=/contact -->
135
135
 
136
- | # | Section | Component | Copy (first line) |
137
- | --- | --------------------------------------------------------- | --------- | ----------------------------------------- |
138
- | 1 | `hero` (split · eyebrow "Contact" · checklist ×3 · 1 CTA) | `Hero` | Get every message in your email, free |
139
- | 2 | `items` (tiles · columns=2 · items ×4) | `Items` | {Add appointment scheduling to your site} |
136
+ | # | Section | Component | Copy (first line) |
137
+ | --- | ----------------------------------- | ------------- | ----------------- |
138
+ | 1 | `contact-form` (column · fields ×0) | `ContactForm` | |
140
139
 
141
140
  <!-- generated:end -->
142
141
 
@@ -165,13 +164,17 @@ _Detail route over `content/en/UseCase/` — 3 seed pages: `persona-1.mdx`, `per
165
164
 
166
165
  <!-- generated:begin page=/newsletter -->
167
166
 
168
- | # | Section | Component | Copy (first line) |
169
- | --- | -------------------------------------- | --------- | --------------------------------------- |
170
- | 1 | `hero` (eyebrow "Newsletter" · 2 CTAs) | `Hero` | {Start a free newsletter with Substack} |
171
- | 2 | `faq` (expanded · items ×3) | `FAQ` | FAQs |
167
+ | # | Section | Component | Copy (first line) |
168
+ | --- | ----------------------------------- | ------------- | ----------------- |
169
+ | 1 | `contact-form` (column · fields ×0) | `ContactForm` | |
172
170
 
173
171
  <!-- generated:end -->
174
172
 
173
+ <!-- generated:begin page=/unsubscribe -->
174
+
175
+ _Code route — no content, no sections._
176
+ <!-- generated:end -->
177
+
175
178
  <!-- generated:begin page=/meet -->
176
179
 
177
180
  | # | Section | Component | Copy (first line) |
@@ -183,12 +186,10 @@ _Detail route over `content/en/UseCase/` — 3 seed pages: `persona-1.mdx`, `per
183
186
 
184
187
  <!-- generated:begin page=/meet/[slug] -->
185
188
 
186
- | # | Section | Component | Copy (first line) |
187
- | --- | -------------------------------------- | -------------- | ------------------------------------------------ |
188
- | 1 | `hero` (eyebrow "Book a Call" · 1 CTA) | `Hero` | Book a Call |
189
- | 2 | `cta` (box · 1 CTA) | `CTA` | {What booking a call would do for your visitors} |
190
- | 3 | `items` (columns=3 · items ×3) | `Items` | What happens on the call? |
191
- | 4 | `content-block` (1 CTA) | `ContentBlock` | |
189
+ | # | Section | Component | Copy (first line) |
190
+ | --- | -------------------------------------- | -------------- | ----------------- |
191
+ | 1 | `hero` (eyebrow "Book a Call" · 1 CTA) | `Hero` | Book a Call |
192
+ | 2 | `content-block` (1 CTA) | `ContentBlock` | |
192
193
 
193
194
  <!-- generated:end -->
194
195
 
@@ -244,12 +245,11 @@ _Code route — no content, no sections._
244
245
 
245
246
  <!-- generated:begin page=/blog -->
246
247
 
247
- | # | Section | Component | Copy (first line) |
248
- | --- | ------------------------------------------------------------ | -------------- | ----------------------------------------------------------------- |
249
- | 1 | `hero` (centered · gapBelow=tight · eyebrow "Blog" · 2 CTAs) | `Hero` | Get free blog/docs from Fumadocs |
250
- | 2 | `blog-list` (grid) | `BlogList` | |
251
- | 3 | `logoCloud` (layout=inline · logos ×6) | `LogoCloud` | Don’t just take our word for it |
252
- | 4 | `testimonials` (quote · items ×1) | `Testimonials` | {A customer quote, in their own words, about the result they got} |
248
+ | # | Section | Component | Copy (first line) |
249
+ | --- | --------------------------------------------------- | -------------- | ----------------------------------------------------------------- |
250
+ | 1 | `hero` (centered · gapBelow=tight · eyebrow "Blog") | `Hero` | {Blog} |
251
+ | 2 | `logoCloud` (layout=inline · logos ×6) | `LogoCloud` | Don’t just take our word for it |
252
+ | 3 | `testimonials` (quote · items ×1) | `Testimonials` | {A customer quote, in their own words, about the result they got} |
253
253
 
254
254
  <!-- generated:end -->
255
255
 
@@ -324,6 +324,7 @@ person-service:
324
324
  - { route: "/faq", kind: page, content: ["content/en/FAQPage/faq.mdx"] }
325
325
  - { route: "/glossary", kind: page, content: ["content/en/GlossaryList/glossary.mdx"] }
326
326
  - { route: "/newsletter", kind: page, content: ["content/en/NewsletterSignup/newsletter.mdx"] }
327
+ - { route: "/unsubscribe", kind: route }
327
328
  - { route: "/sign-in", kind: route }
328
329
  - { route: "/sign-up", kind: route }
329
330
  - { route: "/purchase-policy", kind: page, content: ["content/en/PurchasePolicyPage/purchase-policy.mdx"] }
@@ -354,10 +355,10 @@ Every row is module-less: `requires` is `[marketing]` alone, so `mergeRoster` co
354
355
 
355
356
  <!-- generated:begin inventory -->
356
357
 
357
- | Tier | Used by this template |
358
- | -------- | -------------------------------------------------------------------------------------------------------------------------- |
359
- | Shell | `MarketingShell` |
360
- | Chrome | `Nav`, `Footer`, `Banner` (+ `BannerConsentIsland`, `BannerDismissIsland`) |
361
- | Sections | `BlogList`, `ContentBlock`, `CTA`, `DocumentChassis`, `FAQ`, `GlossaryIndex`, `Hero`, `Items`, `LogoCloud`, `Testimonials` |
358
+ | Tier | Used by this template |
359
+ | -------- | ----------------------------------------------------------------------------------------------------------------------------- |
360
+ | Shell | `MarketingShell` |
361
+ | Chrome | `Nav`, `Footer`, `Banner` (+ `BannerConsentIsland`, `BannerDismissIsland`) |
362
+ | Sections | `ContactForm`, `ContentBlock`, `CTA`, `DocumentChassis`, `FAQ`, `GlossaryIndex`, `Hero`, `Items`, `LogoCloud`, `Testimonials` |
362
363
 
363
364
  <!-- generated:end -->
@@ -1,8 +1,8 @@
1
1
  ---
2
2
  type: "NewsletterSignup"
3
3
  slug: "newsletter"
4
- title: "{Start a free newsletter with Substack}"
5
- description: "{Or upgrade to Pro to capture every subscriber and build your own marketing database.}"
4
+ title: "Get the newsletter"
5
+ description: "{One sentence on what this newsletter covers and who it is for}"
6
6
  schema:
7
7
  mainEntity:
8
8
  "@type": "Offer"
@@ -12,48 +12,11 @@ schema:
12
12
  sections:
13
13
  [
14
14
  {
15
- "id": "hero",
16
- "type": "hero",
17
- "data":
18
- {
19
- "eyebrow": "Newsletter",
20
- "headline": "{Start a free newsletter with Substack}",
21
- "subheadline": "{Or upgrade to Pro to capture every subscriber and build your own marketing database.}",
22
- "primaryCta":
23
- { "label": "{Connect Substack}", "href": "https://substack.com" },
24
- "secondaryCta":
25
- {
26
- "label": "{Upgrade to Pro}",
27
- "href": "https://ailaunchkit.ai/pricing",
28
- "variant": "outline",
29
- },
30
- },
31
- },
32
- {
33
- "id": "faq",
34
- "type": "faq",
35
- "data":
36
- {
37
- "headline": "FAQs",
38
- "presentation": "expanded",
39
- "items":
40
- [
41
- {
42
- "question": "{How often will I hear from you?}",
43
- "answer": "{Answer in one or two plain sentences}",
44
- },
45
- {
46
- "question": "{What will I get?}",
47
- "answer": "{Answer in one or two plain sentences}",
48
- },
49
- {
50
- "question": "{Can I unsubscribe?}",
51
- "answer": "{Answer in one or two plain sentences}",
52
- },
53
- ],
54
- },
15
+ "id": "signup",
16
+ "type": "contact-form",
17
+ "data": { "variant": "column", "fields": [] },
55
18
  },
56
19
  ]
57
20
  ---
58
21
 
59
- {/* Rendered from frontmatter `sections[]` by the /newsletter route. The Free template has no signup form: the hero points the owner to Substack, or to Pro for subscriber capture. The `schema:` Offer is the page's JSON-LD main entity — a free subscription. Placeholder copy — replace every {brace} with your own words before you launch. */}
22
+ {/* Rendered from frontmatter `sections[]` by the /newsletter route. The page's one signup surface is the workspace's Newsletter magnet: its heading, sentence, field, consent, button and ending all come from the magnet, mounted by the route in this section's slot. To change the signup, change the magnet in Admin → Magnets. The `schema:` Offer is the page's JSON-LD main entity — a free subscription. Replace every {brace} with your own words before you launch. */}
@@ -437,19 +437,21 @@ CONTENT_ADAPTER=mdx
437
437
  # random bytes, `openssl rand -base64 32`. Unset = connections cannot be
438
438
  # written; any other shape stops the API. Losing it unreads every connection.
439
439
  # SCHEDULING_CREDENTIAL_KEY=
440
+ # The key each Organization's Stripe connection is encrypted under (#8907),
441
+ # and the key it replaced during a rotation: 32 random bytes each, `openssl
442
+ # rand -base64 32`, distinct from each other and from SCHEDULING_CREDENTIAL_KEY.
443
+ # Unset = a connection can be read and removed but not written; any other
444
+ # shape stops the API. ONE KEY PER DATABASE: a database holding live
445
+ # connections gets its key on production only — never on a preview sharing
446
+ # that database. Rotation and the rekey bin: apps/api/.env.example.
447
+ # STRIPE_CONNECTION_KEY=
448
+ # STRIPE_CONNECTION_KEY_PREVIOUS=
440
449
  # HMAC-SHA256 key POST /v1/webhooks/calcom verifies the x-cal-signature-256
441
450
  # header against (#6085). Unset makes the receiver answer 503 and accept
442
451
  # nothing — the same fail-closed posture an unset STRIPE_WEBHOOK_SECRET has.
443
452
  # CAL_WEBHOOK_SECRET=
444
453
  # SCHEDULING_PUBLIC_BASE_URL=https://cal.com
445
454
  # SCHEDULING_PROVIDER=cal
446
- #
447
- # Which event the /meet page books, as "<owner>/<event-slug>" (the path of
448
- # the provider's own public booking page). Read server-side by apps/web; unset
449
- # means the /meet route 404s, exactly as if the scheduling module were not
450
- # installed. Deliberately NOT a NEXT_PUBLIC_ variable — the whole scheduling
451
- # config surface is server-read.
452
- # SCHEDULING_CAL_LINK=owner/event-slug
453
455
 
454
456
  # ── Recommender mode — the purchase hand-off (REL-013, #4762 / #4879) ────────
455
457
  # A `recommending` flow's email + checkout moment spans two deployments:
@@ -26,6 +26,19 @@ jobs:
26
26
  cache: pnpm
27
27
  cache-dependency-path: pnpm-lock.yaml
28
28
 
29
+ # The private @working-theory/* scope needs NPM_TOKEN (#9092) — add it
30
+ # as a repository secret. It goes into the runner's user-level npmrc,
31
+ # never a committed project .npmrc (pnpm >= 11 ignores credentials
32
+ # there), and stays there for the rest of the job — fine on a hosted
33
+ # runner, which is discarded after it. With no secret the step is a
34
+ # no-op; public packages still install.
35
+ - env:
36
+ NPM_TOKEN: ${{ secrets.NPM_TOKEN }}
37
+ run: |
38
+ if [ -n "$NPM_TOKEN" ]; then
39
+ pnpm config set "//registry.npmjs.org/:_authToken" "$NPM_TOKEN"
40
+ fi
41
+
29
42
  # Not --frozen-lockfile: the scaffold ships no pnpm-lock.yaml (it is
30
43
  # generated fresh by the customer's own first `pnpm install`, and the
31
44
  # placeholder step above is a cache-key stand-in, not a real lockfile),
@@ -56,6 +56,7 @@ The pages of the Free `organization-product` shape, and where each one's copy li
56
56
  | `/contact` | `content/en/ContactPage/contact.mdx` | every scaffold |
57
57
  | `/glossary` | `content/en/GlossaryList/glossary.mdx` | `--shape organization-product` |
58
58
  | `/newsletter` | `content/en/NewsletterSignup/newsletter.mdx` | `--shape organization-product` |
59
+ | `/unsubscribe` | — (a code route) | every scaffold |
59
60
  | `/sign-in` | — (a code route) | every scaffold |
60
61
  | `/sign-up` | — (a code route) | every scaffold |
61
62
  | `/privacy` | `content/en/PrivacyPage/privacy.mdx` | `--shape organization-product` |
@@ -158,5 +158,9 @@ __pycache__/
158
158
  tests/e2e/.stack/
159
159
  tests/e2e/.results/
160
160
 
161
+ # session scratch swept into the 0.43.0 release commit by git add -A; never tracked
162
+ undefined/
163
+ .impeccable/
164
+
161
165
  # create-ailk licence key file (`--license`, `ailk license set`) — owner-only, never committed
162
166
  ailk.key
@@ -98,6 +98,33 @@ STRIPE_WEBHOOK_SECRET=whsec_xxx
98
98
  # unreadable: each workspace must then reconnect. Never log or share it.
99
99
  # SCHEDULING_CREDENTIAL_KEY=
100
100
  #
101
+ # An Organization's Stripe connection (#8907) — the AES-256-GCM key each
102
+ # Organization's restricted Stripe key and webhook signing secret are
103
+ # encrypted under. The same shape rule: exactly 32 random bytes in canonical
104
+ # standard base64 (`openssl rand -base64 32`), and never the same bytes as
105
+ # SCHEDULING_CREDENTIAL_KEY. Any other value stops the API at startup. Unset
106
+ # means a connection can be read, reset and removed, but connect, go-live and
107
+ # the webhook secret answer 503 credential_key_not_configured.
108
+ #
109
+ # ONE KEY PER DATABASE. A deployment whose database holds live connections
110
+ # sets this key on production only; a preview or development deployment that
111
+ # shares that database must never be given it. A preview with its own
112
+ # database takes its own key. (A build that is not production also refuses to
113
+ # read or change a live connection.)
114
+ #
115
+ # ROTATION. Set STRIPE_CONNECTION_KEY_PREVIOUS to the old key and
116
+ # STRIPE_CONNECTION_KEY to a new one, redeploy, run
117
+ # `node dist/bin/stripe-connections-rekey.js` (dev:
118
+ # `pnpm --filter @working-theory/api stripe-connections:rekey`) with the
119
+ # production environment (VERCEL_ENV=production, or NODE_ENV=production off
120
+ # Vercel) — anywhere else it prints `not_production` and touches nothing —
121
+ # and unset _PREVIOUS once it prints `remaining=0`. A row it counts `unreadable`
122
+ # decrypts under neither key and stays so after _PREVIOUS is unset: its owner
123
+ # reconnects or disconnects. _PREVIOUS without a current key, or equal to it,
124
+ # stops the API.
125
+ # STRIPE_CONNECTION_KEY=
126
+ # STRIPE_CONNECTION_KEY_PREVIOUS=
127
+ #
101
128
  # The booking webhook receiver (#6085) — a SEPARATE credential from the
102
129
  # three above: it verifies POST /v1/webhooks/calcom's x-cal-signature-256
103
130
  # header, it does not call the provider. Unset makes the receiver answer 503