@proofkit/cli 1.0.0-beta.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 (317) hide show
  1. package/CHANGELOG.md +120 -0
  2. package/LICENSE.md +21 -0
  3. package/README.md +19 -0
  4. package/dist/acorn-AKFTBDM6.js +15 -0
  5. package/dist/angular-BOQ6FHSU.js +2 -0
  6. package/dist/babel-ZTOORN7K.js +15 -0
  7. package/dist/chunk-4LISTI44.js +1 -0
  8. package/dist/estree-KOJPX4S6.js +36 -0
  9. package/dist/flow-RCI44GYZ.js +19 -0
  10. package/dist/glimmer-GV5EF5E4.js +30 -0
  11. package/dist/graphql-YXQNPQWM.js +29 -0
  12. package/dist/html-ZAJTRROK.js +22 -0
  13. package/dist/index.js +300 -0
  14. package/dist/markdown-Q75DTQI7.js +63 -0
  15. package/dist/meriyah-32K7GBV5.js +4 -0
  16. package/dist/postcss-WWYO4PGL.js +54 -0
  17. package/dist/typescript-M6N7JDNQ.js +20 -0
  18. package/dist/yaml-LY7PNAYV.js +161 -0
  19. package/index.d.ts +19 -0
  20. package/package.json +122 -0
  21. package/template/extras/_cursor/conditional-rules/nextjs-framework.mdc +51 -0
  22. package/template/extras/_cursor/conditional-rules/npm.mdc +60 -0
  23. package/template/extras/_cursor/conditional-rules/pnpm.mdc +65 -0
  24. package/template/extras/_cursor/conditional-rules/yarn.mdc +60 -0
  25. package/template/extras/_cursor/rules/cursor-rules.mdc +88 -0
  26. package/template/extras/_cursor/rules/filemaker-api.mdc +176 -0
  27. package/template/extras/_cursor/rules/troubleshooting-patterns.mdc +240 -0
  28. package/template/extras/_cursor/rules/ui-components.mdc +57 -0
  29. package/template/extras/config/_eslint.js +27 -0
  30. package/template/extras/config/_prettier.config.js +6 -0
  31. package/template/extras/config/drizzle-config-mysql.ts +12 -0
  32. package/template/extras/config/drizzle-config-postgres.ts +12 -0
  33. package/template/extras/config/drizzle-config-sqlite.ts +12 -0
  34. package/template/extras/config/fmschema.config.mjs +9 -0
  35. package/template/extras/config/get-query-client.ts +6 -0
  36. package/template/extras/config/postcss.config.cjs +7 -0
  37. package/template/extras/config/query-provider-vite.tsx +19 -0
  38. package/template/extras/config/query-provider.tsx +21 -0
  39. package/template/extras/emailProviders/none/email.tsx +24 -0
  40. package/template/extras/emailProviders/plunk/email.tsx +26 -0
  41. package/template/extras/emailProviders/plunk/service.ts +4 -0
  42. package/template/extras/emailProviders/resend/email.tsx +23 -0
  43. package/template/extras/emailProviders/resend/service.ts +4 -0
  44. package/template/extras/fmaddon-auth/app/(main)/auth/profile/actions.ts +93 -0
  45. package/template/extras/fmaddon-auth/app/(main)/auth/profile/page.tsx +27 -0
  46. package/template/extras/fmaddon-auth/app/(main)/auth/profile/profile-form.tsx +56 -0
  47. package/template/extras/fmaddon-auth/app/(main)/auth/profile/reset-password-form.tsx +110 -0
  48. package/template/extras/fmaddon-auth/app/(main)/auth/profile/schema.ts +19 -0
  49. package/template/extras/fmaddon-auth/app/auth/forgot-password/actions.ts +37 -0
  50. package/template/extras/fmaddon-auth/app/auth/forgot-password/forgot-form.tsx +41 -0
  51. package/template/extras/fmaddon-auth/app/auth/forgot-password/page.tsx +21 -0
  52. package/template/extras/fmaddon-auth/app/auth/forgot-password/schema.ts +5 -0
  53. package/template/extras/fmaddon-auth/app/auth/login/actions.ts +34 -0
  54. package/template/extras/fmaddon-auth/app/auth/login/login-form.tsx +64 -0
  55. package/template/extras/fmaddon-auth/app/auth/login/page.tsx +26 -0
  56. package/template/extras/fmaddon-auth/app/auth/login/schema.ts +6 -0
  57. package/template/extras/fmaddon-auth/app/auth/reset-password/actions.ts +50 -0
  58. package/template/extras/fmaddon-auth/app/auth/reset-password/page.tsx +32 -0
  59. package/template/extras/fmaddon-auth/app/auth/reset-password/reset-password-form.tsx +59 -0
  60. package/template/extras/fmaddon-auth/app/auth/reset-password/schema.ts +14 -0
  61. package/template/extras/fmaddon-auth/app/auth/reset-password/verify-email/actions.ts +45 -0
  62. package/template/extras/fmaddon-auth/app/auth/reset-password/verify-email/page.tsx +32 -0
  63. package/template/extras/fmaddon-auth/app/auth/reset-password/verify-email/schema.ts +5 -0
  64. package/template/extras/fmaddon-auth/app/auth/reset-password/verify-email/verify-email-form.tsx +48 -0
  65. package/template/extras/fmaddon-auth/app/auth/signup/actions.ts +49 -0
  66. package/template/extras/fmaddon-auth/app/auth/signup/page.tsx +26 -0
  67. package/template/extras/fmaddon-auth/app/auth/signup/schema.ts +12 -0
  68. package/template/extras/fmaddon-auth/app/auth/signup/signup-form.tsx +67 -0
  69. package/template/extras/fmaddon-auth/app/auth/verify-email/actions.ts +110 -0
  70. package/template/extras/fmaddon-auth/app/auth/verify-email/email-verification-form.tsx +45 -0
  71. package/template/extras/fmaddon-auth/app/auth/verify-email/page.tsx +38 -0
  72. package/template/extras/fmaddon-auth/app/auth/verify-email/resend-button.tsx +35 -0
  73. package/template/extras/fmaddon-auth/app/auth/verify-email/schema.ts +5 -0
  74. package/template/extras/fmaddon-auth/components/auth/actions.ts +16 -0
  75. package/template/extras/fmaddon-auth/components/auth/protect.tsx +17 -0
  76. package/template/extras/fmaddon-auth/components/auth/redirect.tsx +26 -0
  77. package/template/extras/fmaddon-auth/components/auth/use-user.ts +59 -0
  78. package/template/extras/fmaddon-auth/components/auth/user-menu.tsx +51 -0
  79. package/template/extras/fmaddon-auth/emails/auth-code.tsx +156 -0
  80. package/template/extras/fmaddon-auth/middleware.ts +45 -0
  81. package/template/extras/fmaddon-auth/server/auth/utils/email-verification.ts +136 -0
  82. package/template/extras/fmaddon-auth/server/auth/utils/encryption.ts +51 -0
  83. package/template/extras/fmaddon-auth/server/auth/utils/index.ts +16 -0
  84. package/template/extras/fmaddon-auth/server/auth/utils/password-reset.ts +152 -0
  85. package/template/extras/fmaddon-auth/server/auth/utils/password.ts +67 -0
  86. package/template/extras/fmaddon-auth/server/auth/utils/redirect.ts +8 -0
  87. package/template/extras/fmaddon-auth/server/auth/utils/session.ts +192 -0
  88. package/template/extras/fmaddon-auth/server/auth/utils/user.ts +147 -0
  89. package/template/extras/prisma/schema/base-planetscale.prisma +24 -0
  90. package/template/extras/prisma/schema/base.prisma +20 -0
  91. package/template/extras/prisma/schema/with-auth-planetscale.prisma +77 -0
  92. package/template/extras/prisma/schema/with-auth.prisma +74 -0
  93. package/template/extras/src/app/_components/post-tw.tsx +50 -0
  94. package/template/extras/src/app/_components/post.tsx +54 -0
  95. package/template/extras/src/app/api/auth/[...nextauth]/route.ts +4 -0
  96. package/template/extras/src/app/api/trpc/[trpc]/route.ts +34 -0
  97. package/template/extras/src/app/clerk-auth/layout.tsx +10 -0
  98. package/template/extras/src/app/clerk-auth/signin/[[...sign-in]]/page.tsx +5 -0
  99. package/template/extras/src/app/clerk-auth/signup/[[...sign-up]]/page.tsx +5 -0
  100. package/template/extras/src/app/layout/base.tsx +34 -0
  101. package/template/extras/src/app/layout/main-shell.tsx +37 -0
  102. package/template/extras/src/app/layout/with-trpc-tw.tsx +24 -0
  103. package/template/extras/src/app/layout/with-trpc.tsx +24 -0
  104. package/template/extras/src/app/layout/with-tw.tsx +20 -0
  105. package/template/extras/src/app/next-auth/layout.tsx +22 -0
  106. package/template/extras/src/app/next-auth/signin/page.tsx +82 -0
  107. package/template/extras/src/app/next-auth/signup/action.ts +24 -0
  108. package/template/extras/src/app/next-auth/signup/page.tsx +40 -0
  109. package/template/extras/src/app/next-auth/signup/validation.ts +12 -0
  110. package/template/extras/src/app/page/base.tsx +6 -0
  111. package/template/extras/src/app/page/with-auth-trpc-tw.tsx +67 -0
  112. package/template/extras/src/app/page/with-auth-trpc.tsx +68 -0
  113. package/template/extras/src/app/page/with-trpc-tw.tsx +53 -0
  114. package/template/extras/src/app/page/with-trpc.tsx +54 -0
  115. package/template/extras/src/app/page/with-tw.tsx +37 -0
  116. package/template/extras/src/components/clerk-auth/clerk-provider.tsx +18 -0
  117. package/template/extras/src/components/clerk-auth/user-menu-mobile.tsx +36 -0
  118. package/template/extras/src/components/clerk-auth/user-menu.tsx +24 -0
  119. package/template/extras/src/components/next-auth/next-auth-provider.tsx +14 -0
  120. package/template/extras/src/components/next-auth/user-menu-mobile.tsx +31 -0
  121. package/template/extras/src/components/next-auth/user-menu.tsx +38 -0
  122. package/template/extras/src/env/with-auth.ts +31 -0
  123. package/template/extras/src/env/with-clerk.ts +20 -0
  124. package/template/extras/src/index.module.css +177 -0
  125. package/template/extras/src/middleware/clerk.ts +20 -0
  126. package/template/extras/src/middleware/next-auth.ts +5 -0
  127. package/template/extras/src/pages/_app/base.tsx +14 -0
  128. package/template/extras/src/pages/_app/with-auth-trpc-tw.tsx +23 -0
  129. package/template/extras/src/pages/_app/with-auth-trpc.tsx +23 -0
  130. package/template/extras/src/pages/_app/with-auth-tw.tsx +21 -0
  131. package/template/extras/src/pages/_app/with-auth.tsx +21 -0
  132. package/template/extras/src/pages/_app/with-trpc-tw.tsx +16 -0
  133. package/template/extras/src/pages/_app/with-trpc.tsx +16 -0
  134. package/template/extras/src/pages/_app/with-tw.tsx +14 -0
  135. package/template/extras/src/pages/api/auth/[...nextauth].ts +5 -0
  136. package/template/extras/src/pages/api/trpc/[trpc].ts +19 -0
  137. package/template/extras/src/pages/index/base.tsx +47 -0
  138. package/template/extras/src/pages/index/with-auth-trpc-tw.tsx +80 -0
  139. package/template/extras/src/pages/index/with-auth-trpc.tsx +81 -0
  140. package/template/extras/src/pages/index/with-trpc-tw.tsx +52 -0
  141. package/template/extras/src/pages/index/with-trpc.tsx +53 -0
  142. package/template/extras/src/pages/index/with-tw.tsx +45 -0
  143. package/template/extras/src/server/api/root.ts +23 -0
  144. package/template/extras/src/server/api/routers/post/base.ts +40 -0
  145. package/template/extras/src/server/api/routers/post/with-auth-drizzle.ts +39 -0
  146. package/template/extras/src/server/api/routers/post/with-auth-prisma.ts +41 -0
  147. package/template/extras/src/server/api/routers/post/with-auth.ts +37 -0
  148. package/template/extras/src/server/api/routers/post/with-drizzle.ts +30 -0
  149. package/template/extras/src/server/api/routers/post/with-prisma.ts +31 -0
  150. package/template/extras/src/server/api/trpc-app/base.ts +103 -0
  151. package/template/extras/src/server/api/trpc-app/with-auth-db.ts +133 -0
  152. package/template/extras/src/server/api/trpc-app/with-auth.ts +130 -0
  153. package/template/extras/src/server/api/trpc-app/with-db.ts +106 -0
  154. package/template/extras/src/server/api/trpc-pages/base.ts +122 -0
  155. package/template/extras/src/server/api/trpc-pages/with-auth-db.ts +160 -0
  156. package/template/extras/src/server/api/trpc-pages/with-auth.ts +158 -0
  157. package/template/extras/src/server/api/trpc-pages/with-db.ts +125 -0
  158. package/template/extras/src/server/data/users.ts +23 -0
  159. package/template/extras/src/server/db/db-prisma-planetscale.ts +22 -0
  160. package/template/extras/src/server/db/db-prisma.ts +17 -0
  161. package/template/extras/src/server/db/index-drizzle/with-mysql.ts +18 -0
  162. package/template/extras/src/server/db/index-drizzle/with-planetscale.ts +7 -0
  163. package/template/extras/src/server/db/index-drizzle/with-postgres.ts +18 -0
  164. package/template/extras/src/server/db/index-drizzle/with-sqlite.ts +19 -0
  165. package/template/extras/src/server/db/schema-drizzle/base-mysql.ts +34 -0
  166. package/template/extras/src/server/db/schema-drizzle/base-planetscale.ts +34 -0
  167. package/template/extras/src/server/db/schema-drizzle/base-postgres.ts +36 -0
  168. package/template/extras/src/server/db/schema-drizzle/base-sqlite.ts +30 -0
  169. package/template/extras/src/server/db/schema-drizzle/with-auth-mysql.ts +123 -0
  170. package/template/extras/src/server/db/schema-drizzle/with-auth-planetscale.ts +117 -0
  171. package/template/extras/src/server/db/schema-drizzle/with-auth-postgres.ts +130 -0
  172. package/template/extras/src/server/db/schema-drizzle/with-auth-sqlite.ts +116 -0
  173. package/template/extras/src/server/next-auth/base.ts +111 -0
  174. package/template/extras/src/server/next-auth/password.ts +13 -0
  175. package/template/extras/src/server/next-auth/with-drizzle.ts +83 -0
  176. package/template/extras/src/server/next-auth/with-prisma.ts +72 -0
  177. package/template/extras/src/trpc/query-client.ts +25 -0
  178. package/template/extras/src/trpc/react.tsx +76 -0
  179. package/template/extras/src/trpc/server.ts +30 -0
  180. package/template/extras/src/utils/api.ts +68 -0
  181. package/template/extras/start-database/mysql.sh +54 -0
  182. package/template/extras/start-database/postgres.sh +55 -0
  183. package/template/fm-addon/ProofKitAuth/de.xml +518 -0
  184. package/template/fm-addon/ProofKitAuth/en.xml +518 -0
  185. package/template/fm-addon/ProofKitAuth/es.xml +518 -0
  186. package/template/fm-addon/ProofKitAuth/fr.xml +518 -0
  187. package/template/fm-addon/ProofKitAuth/icon.png +0 -0
  188. package/template/fm-addon/ProofKitAuth/icon@2x.png +0 -0
  189. package/template/fm-addon/ProofKitAuth/info.json +11 -0
  190. package/template/fm-addon/ProofKitAuth/info_de.json +18 -0
  191. package/template/fm-addon/ProofKitAuth/info_en.json +8 -0
  192. package/template/fm-addon/ProofKitAuth/info_es.json +18 -0
  193. package/template/fm-addon/ProofKitAuth/info_fr.json +18 -0
  194. package/template/fm-addon/ProofKitAuth/info_it.json +18 -0
  195. package/template/fm-addon/ProofKitAuth/info_ja.json +18 -0
  196. package/template/fm-addon/ProofKitAuth/info_ko.json +18 -0
  197. package/template/fm-addon/ProofKitAuth/info_nl.json +18 -0
  198. package/template/fm-addon/ProofKitAuth/info_pt.json +18 -0
  199. package/template/fm-addon/ProofKitAuth/info_sv.json +18 -0
  200. package/template/fm-addon/ProofKitAuth/info_zh.json +18 -0
  201. package/template/fm-addon/ProofKitAuth/it.xml +518 -0
  202. package/template/fm-addon/ProofKitAuth/ja.xml +518 -0
  203. package/template/fm-addon/ProofKitAuth/ko.xml +518 -0
  204. package/template/fm-addon/ProofKitAuth/nl.xml +518 -0
  205. package/template/fm-addon/ProofKitAuth/preview.png +0 -0
  206. package/template/fm-addon/ProofKitAuth/pt.xml +518 -0
  207. package/template/fm-addon/ProofKitAuth/sv.xml +518 -0
  208. package/template/fm-addon/ProofKitAuth/template.xml +0 -0
  209. package/template/fm-addon/ProofKitAuth/zh.xml +518 -0
  210. package/template/fm-addon/ProofKitWV/de.xml +896 -0
  211. package/template/fm-addon/ProofKitWV/en.xml +896 -0
  212. package/template/fm-addon/ProofKitWV/es.xml +896 -0
  213. package/template/fm-addon/ProofKitWV/fr.xml +896 -0
  214. package/template/fm-addon/ProofKitWV/icon.png +0 -0
  215. package/template/fm-addon/ProofKitWV/icon@2x.png +0 -0
  216. package/template/fm-addon/ProofKitWV/info.json +11 -0
  217. package/template/fm-addon/ProofKitWV/info_de.json +18 -0
  218. package/template/fm-addon/ProofKitWV/info_en.json +11 -0
  219. package/template/fm-addon/ProofKitWV/info_es.json +18 -0
  220. package/template/fm-addon/ProofKitWV/info_fr.json +18 -0
  221. package/template/fm-addon/ProofKitWV/info_it.json +18 -0
  222. package/template/fm-addon/ProofKitWV/info_ja.json +18 -0
  223. package/template/fm-addon/ProofKitWV/info_ko.json +18 -0
  224. package/template/fm-addon/ProofKitWV/info_nl.json +18 -0
  225. package/template/fm-addon/ProofKitWV/info_pt.json +18 -0
  226. package/template/fm-addon/ProofKitWV/info_sv.json +18 -0
  227. package/template/fm-addon/ProofKitWV/info_zh.json +18 -0
  228. package/template/fm-addon/ProofKitWV/it.xml +896 -0
  229. package/template/fm-addon/ProofKitWV/ja.xml +896 -0
  230. package/template/fm-addon/ProofKitWV/ko.xml +896 -0
  231. package/template/fm-addon/ProofKitWV/nl.xml +896 -0
  232. package/template/fm-addon/ProofKitWV/preview.png +0 -0
  233. package/template/fm-addon/ProofKitWV/pt.xml +896 -0
  234. package/template/fm-addon/ProofKitWV/records_de.xml +0 -0
  235. package/template/fm-addon/ProofKitWV/records_en.xml +0 -0
  236. package/template/fm-addon/ProofKitWV/records_es.xml +0 -0
  237. package/template/fm-addon/ProofKitWV/records_fr.xml +0 -0
  238. package/template/fm-addon/ProofKitWV/records_it.xml +0 -0
  239. package/template/fm-addon/ProofKitWV/records_ja.xml +0 -0
  240. package/template/fm-addon/ProofKitWV/records_ko.xml +0 -0
  241. package/template/fm-addon/ProofKitWV/records_nl.xml +0 -0
  242. package/template/fm-addon/ProofKitWV/records_pt.xml +0 -0
  243. package/template/fm-addon/ProofKitWV/records_sv.xml +0 -0
  244. package/template/fm-addon/ProofKitWV/records_zh.xml +0 -0
  245. package/template/fm-addon/ProofKitWV/sv.xml +896 -0
  246. package/template/fm-addon/ProofKitWV/template.xml +0 -0
  247. package/template/fm-addon/ProofKitWV/zh.xml +896 -0
  248. package/template/nextjs/README.md +27 -0
  249. package/template/nextjs/_gitignore +37 -0
  250. package/template/nextjs/next.config.ts +12 -0
  251. package/template/nextjs/package.json +50 -0
  252. package/template/nextjs/postcss.config.cjs +14 -0
  253. package/template/nextjs/proofkit.json +1 -0
  254. package/template/nextjs/public/favicon.ico +0 -0
  255. package/template/nextjs/public/proofkit.png +0 -0
  256. package/template/nextjs/src/app/(main)/layout.tsx +6 -0
  257. package/template/nextjs/src/app/(main)/page.tsx +90 -0
  258. package/template/nextjs/src/app/layout.tsx +39 -0
  259. package/template/nextjs/src/app/navigation.tsx +12 -0
  260. package/template/nextjs/src/components/AppLogo.tsx +6 -0
  261. package/template/nextjs/src/components/AppShell/internal/AppShell.tsx +21 -0
  262. package/template/nextjs/src/components/AppShell/internal/Header.module.css +40 -0
  263. package/template/nextjs/src/components/AppShell/internal/Header.tsx +34 -0
  264. package/template/nextjs/src/components/AppShell/internal/HeaderMobileMenu.tsx +27 -0
  265. package/template/nextjs/src/components/AppShell/internal/HeaderNavLink.tsx +31 -0
  266. package/template/nextjs/src/components/AppShell/internal/config.ts +1 -0
  267. package/template/nextjs/src/components/AppShell/slot-header-center.tsx +13 -0
  268. package/template/nextjs/src/components/AppShell/slot-header-left.tsx +23 -0
  269. package/template/nextjs/src/components/AppShell/slot-header-mobile-content.tsx +43 -0
  270. package/template/nextjs/src/components/AppShell/slot-header-right.tsx +26 -0
  271. package/template/nextjs/src/config/env.ts +13 -0
  272. package/template/nextjs/src/config/theme/globals.css +1 -0
  273. package/template/nextjs/src/config/theme/mantine-theme.ts +22 -0
  274. package/template/nextjs/src/server/safe-action.ts +3 -0
  275. package/template/nextjs/src/utils/notification-helpers.ts +32 -0
  276. package/template/nextjs/tsconfig.json +40 -0
  277. package/template/pages/nextjs/blank/page.tsx +5 -0
  278. package/template/pages/nextjs/table/page.tsx +17 -0
  279. package/template/pages/nextjs/table/table.tsx +18 -0
  280. package/template/pages/nextjs/table-edit/actions.ts +23 -0
  281. package/template/pages/nextjs/table-edit/page.tsx +28 -0
  282. package/template/pages/nextjs/table-edit/schema.ts +4 -0
  283. package/template/pages/nextjs/table-edit/table.tsx +43 -0
  284. package/template/pages/nextjs/table-infinite/actions.ts +62 -0
  285. package/template/pages/nextjs/table-infinite/page.tsx +11 -0
  286. package/template/pages/nextjs/table-infinite/query.ts +44 -0
  287. package/template/pages/nextjs/table-infinite/table.tsx +107 -0
  288. package/template/pages/nextjs/table-infinite-edit/actions.ts +84 -0
  289. package/template/pages/nextjs/table-infinite-edit/page.tsx +23 -0
  290. package/template/pages/nextjs/table-infinite-edit/query.ts +81 -0
  291. package/template/pages/nextjs/table-infinite-edit/schema.ts +4 -0
  292. package/template/pages/nextjs/table-infinite-edit/table.tsx +130 -0
  293. package/template/pages/vite-wv/blank/index.tsx +0 -0
  294. package/template/pages/vite-wv/table/index.tsx +34 -0
  295. package/template/pages/vite-wv/table-edit/index.tsx +72 -0
  296. package/template/vite-wv/.vscode/settings.json +11 -0
  297. package/template/vite-wv/_gitignore +18 -0
  298. package/template/vite-wv/index.html +13 -0
  299. package/template/vite-wv/package.json +52 -0
  300. package/template/vite-wv/pnpm-lock.yaml +2294 -0
  301. package/template/vite-wv/postcss.config.cjs +14 -0
  302. package/template/vite-wv/proofkit.json +1 -0
  303. package/template/vite-wv/scripts/launch-fm.sh +3 -0
  304. package/template/vite-wv/scripts/upload.js +21 -0
  305. package/template/vite-wv/src/components/AppLogo.tsx +5 -0
  306. package/template/vite-wv/src/components/full-screen-loader.tsx +9 -0
  307. package/template/vite-wv/src/config/env.ts +16 -0
  308. package/template/vite-wv/src/config/theme/globals.css +1 -0
  309. package/template/vite-wv/src/config/theme/mantine-theme.ts +22 -0
  310. package/template/vite-wv/src/main.tsx +42 -0
  311. package/template/vite-wv/src/routeTree.gen.ts +111 -0
  312. package/template/vite-wv/src/routes/__root.tsx +21 -0
  313. package/template/vite-wv/src/routes/index.tsx +63 -0
  314. package/template/vite-wv/src/routes/secondary.tsx +28 -0
  315. package/template/vite-wv/src/utils/notification-helpers.ts +32 -0
  316. package/template/vite-wv/tsconfig.json +14 -0
  317. package/template/vite-wv/vite.config.ts +18 -0
@@ -0,0 +1,48 @@
1
+ "use client";
2
+
3
+ import { useHookFormAction } from "@next-safe-action/adapter-react-hook-form/hooks";
4
+ import { zodResolver } from "@hookform/resolvers/zod";
5
+ import { Button, Stack, Paper, Text, PinInput } from "@mantine/core";
6
+ import { verifyEmailAction } from "./actions";
7
+ import { verifyEmailSchema } from "./schema";
8
+
9
+ export default function VerifyEmailForm() {
10
+ const { form, handleSubmitWithAction, action } = useHookFormAction(
11
+ verifyEmailAction,
12
+ zodResolver(verifyEmailSchema),
13
+ {}
14
+ );
15
+
16
+ return (
17
+ <form onSubmit={handleSubmitWithAction}>
18
+ <Paper withBorder shadow="md" p={30} mt={30} radius="md">
19
+ <Stack>
20
+ <PinInput
21
+ oneTimeCode
22
+ length={8}
23
+ {...form.register("code")}
24
+ onChange={(value) => {
25
+ form.setValue("code", value);
26
+ if (value.length === 8) {
27
+ handleSubmitWithAction();
28
+ }
29
+ }}
30
+ error={!!form.formState.errors.code?.message}
31
+ autoFocus
32
+ />
33
+ {form.formState.errors.code?.message && (
34
+ <Text>{form.formState.errors.code.message}</Text>
35
+ )}
36
+ {action.result.data?.error ? (
37
+ <Text c="red">{action.result.data.error}</Text>
38
+ ) : action.hasErrored ? (
39
+ <Text c="red">An error occured</Text>
40
+ ) : null}
41
+ <Button fullWidth type="submit" loading={action.isPending}>
42
+ Continue
43
+ </Button>
44
+ </Stack>
45
+ </Paper>
46
+ </form>
47
+ );
48
+ }
@@ -0,0 +1,49 @@
1
+ "use server";
2
+
3
+ import { actionClient } from "@/server/safe-action";
4
+ import { signupSchema } from "./schema";
5
+ import { checkEmailAvailability, createUser } from "@/server/auth/utils/user";
6
+ import { verifyPasswordStrength } from "@/server/auth/utils/password";
7
+ import {
8
+ createSession,
9
+ setSessionTokenCookie,
10
+ } from "@/server/auth/utils/session";
11
+ import { generateSessionToken } from "@/server/auth/utils/session";
12
+ import { redirect } from "next/navigation";
13
+ import {
14
+ createEmailVerificationRequest,
15
+ sendVerificationEmail,
16
+ setEmailVerificationRequestCookie,
17
+ } from "@/server/auth/utils/email-verification";
18
+
19
+ export const signupAction = actionClient
20
+ .schema(signupSchema)
21
+ .action(async ({ parsedInput }) => {
22
+ const { email, password } = parsedInput;
23
+ const emailAvailable = await checkEmailAvailability(email);
24
+ if (!emailAvailable) {
25
+ return { error: "Email already in use" };
26
+ }
27
+
28
+ const passwordStrong = await verifyPasswordStrength(password);
29
+ if (!passwordStrong) {
30
+ return { error: "Password is too weak" };
31
+ }
32
+
33
+ const user = await createUser(email, password);
34
+ const emailVerificationRequest = await createEmailVerificationRequest(
35
+ user.id,
36
+ user.email
37
+ );
38
+ await sendVerificationEmail(
39
+ emailVerificationRequest.email,
40
+ emailVerificationRequest.code
41
+ );
42
+ await setEmailVerificationRequestCookie(emailVerificationRequest);
43
+
44
+ const sessionToken = generateSessionToken();
45
+ const session = await createSession(sessionToken, user.id);
46
+ setSessionTokenCookie(sessionToken, session.expiresAt);
47
+
48
+ return redirect("/auth/verify-email");
49
+ });
@@ -0,0 +1,26 @@
1
+ import { getCurrentSession } from "@/server/auth/utils/session";
2
+ import { Anchor, Container, Text, Title } from "@mantine/core";
3
+ import { redirect } from "next/navigation";
4
+ import SignupForm from "./signup-form";
5
+
6
+ export default async function Page() {
7
+ const { session } = await getCurrentSession();
8
+
9
+ if (session !== null) {
10
+ return redirect("/");
11
+ }
12
+
13
+ return (
14
+ <Container size={420} my={40}>
15
+ <Title ta="center">Create account</Title>
16
+ <Text c="dimmed" size="sm" ta="center" mt={5}>
17
+ Already have an account?{" "}
18
+ <Anchor size="sm" component="a" href="/auth/login">
19
+ Sign in
20
+ </Anchor>
21
+ </Text>
22
+
23
+ <SignupForm />
24
+ </Container>
25
+ );
26
+ }
@@ -0,0 +1,12 @@
1
+ import { z } from "zod/v4";
2
+
3
+ export const signupSchema = z
4
+ .object({
5
+ email: z.string().email(),
6
+ password: z.string().min(8),
7
+ confirmPassword: z.string(),
8
+ })
9
+ .refine((data) => data.password === data.confirmPassword, {
10
+ path: ["confirmPassword"],
11
+ message: "Passwords do not match",
12
+ });
@@ -0,0 +1,67 @@
1
+ "use client";
2
+
3
+ import { useHookFormAction } from "@next-safe-action/adapter-react-hook-form/hooks";
4
+ import { signupAction } from "./actions";
5
+ import { zodResolver } from "@hookform/resolvers/zod";
6
+ import { signupSchema } from "./schema";
7
+ import {
8
+ Anchor,
9
+ PasswordInput,
10
+ TextInput,
11
+ Button,
12
+ Group,
13
+ Stack,
14
+ Paper,
15
+ Text,
16
+ } from "@mantine/core";
17
+
18
+ export default function SignupForm() {
19
+ const { form, handleSubmitWithAction, action } = useHookFormAction(
20
+ signupAction,
21
+ zodResolver(signupSchema),
22
+ {}
23
+ );
24
+
25
+ return (
26
+ <form onSubmit={handleSubmitWithAction}>
27
+ <Paper withBorder shadow="md" p={30} mt={30} radius="md">
28
+ <Stack>
29
+ <TextInput
30
+ label="Email"
31
+ placeholder="you@proofkit.dev"
32
+ required
33
+ withAsterisk={false}
34
+ {...form.register("email")}
35
+ error={form.formState.errors.email?.message}
36
+ />
37
+ <PasswordInput
38
+ label="Password"
39
+ placeholder="Your password"
40
+ autoComplete="new-password"
41
+ required
42
+ withAsterisk={false}
43
+ {...form.register("password")}
44
+ error={form.formState.errors.password?.message}
45
+ />
46
+ <PasswordInput
47
+ label="Confirm Password"
48
+ placeholder="Your password (again)"
49
+ autoComplete="new-password"
50
+ required
51
+ withAsterisk={false}
52
+ {...form.register("confirmPassword")}
53
+ error={form.formState.errors.confirmPassword?.message}
54
+ />
55
+ {action.result.data?.error ? (
56
+ <Text c="red">{action.result.data.error}</Text>
57
+ ) : action.hasErrored ? (
58
+ <Text c="red">An error occured</Text>
59
+ ) : null}
60
+ <Button fullWidth type="submit" loading={action.isPending}>
61
+ Create Account
62
+ </Button>
63
+ </Stack>
64
+ </Paper>
65
+ </form>
66
+ );
67
+ }
@@ -0,0 +1,110 @@
1
+ "use server";
2
+
3
+ import { getCurrentSession } from "@/server/auth/utils/session";
4
+ import { actionClient } from "@/server/safe-action";
5
+ import { emailVerificationSchema } from "./schema";
6
+ import {
7
+ deleteEmailVerificationRequestCookie,
8
+ deleteUserEmailVerificationRequest,
9
+ sendVerificationEmail,
10
+ setEmailVerificationRequestCookie,
11
+ } from "@/server/auth/utils/email-verification";
12
+ import {
13
+ createEmailVerificationRequest,
14
+ getUserEmailVerificationRequestFromRequest,
15
+ } from "@/server/auth/utils/email-verification";
16
+ import { invalidateUserPasswordResetSessions } from "@/server/auth/utils/password-reset";
17
+ import { updateUserEmailAndSetEmailAsVerified } from "@/server/auth/utils/user";
18
+ import { redirect } from "next/navigation";
19
+ import { getRedirectCookie } from "@/server/auth/utils/redirect";
20
+
21
+ export const verifyEmailAction = actionClient
22
+ .schema(emailVerificationSchema)
23
+ .action(async ({ parsedInput, ctx }) => {
24
+ const { session, user } = await getCurrentSession();
25
+ if (session === null) {
26
+ return {
27
+ error: "Not authenticated",
28
+ };
29
+ }
30
+
31
+ let verificationRequest =
32
+ await getUserEmailVerificationRequestFromRequest();
33
+ if (verificationRequest === null) {
34
+ return {
35
+ error: "Not authenticated",
36
+ };
37
+ }
38
+ const { code } = parsedInput;
39
+ if (verificationRequest.expires_at === null) {
40
+ return {
41
+ error: "Verification code expired",
42
+ };
43
+ }
44
+
45
+ if (Date.now() >= verificationRequest.expires_at * 1000) {
46
+ verificationRequest = await createEmailVerificationRequest(
47
+ verificationRequest.id_user,
48
+ verificationRequest.email
49
+ );
50
+ await sendVerificationEmail(
51
+ verificationRequest.email,
52
+ verificationRequest.code
53
+ );
54
+ return {
55
+ error:
56
+ "The verification code was expired. We sent another code to your inbox.",
57
+ };
58
+ }
59
+ if (verificationRequest.code !== code) {
60
+ return {
61
+ error: "Incorrect code.",
62
+ };
63
+ }
64
+ await deleteUserEmailVerificationRequest(user.id);
65
+ await invalidateUserPasswordResetSessions(user.id);
66
+ await updateUserEmailAndSetEmailAsVerified(
67
+ user.id,
68
+ verificationRequest.email
69
+ );
70
+ await deleteEmailVerificationRequestCookie();
71
+
72
+ const redirectTo = await getRedirectCookie();
73
+ return redirect(redirectTo);
74
+ });
75
+
76
+ export const resendEmailVerificationAction = actionClient.action(async () => {
77
+ const { session, user } = await getCurrentSession();
78
+ if (session === null) {
79
+ return {
80
+ error: "Not authenticated",
81
+ };
82
+ }
83
+
84
+ let verificationRequest = await getUserEmailVerificationRequestFromRequest();
85
+ if (verificationRequest === null) {
86
+ if (user.emailVerified) {
87
+ return {
88
+ error: "Forbidden",
89
+ };
90
+ }
91
+
92
+ verificationRequest = await createEmailVerificationRequest(
93
+ user.id,
94
+ user.email
95
+ );
96
+ } else {
97
+ verificationRequest = await createEmailVerificationRequest(
98
+ user.id,
99
+ verificationRequest.email
100
+ );
101
+ }
102
+ await sendVerificationEmail(
103
+ verificationRequest.email,
104
+ verificationRequest.code
105
+ );
106
+ await setEmailVerificationRequestCookie(verificationRequest);
107
+ return {
108
+ message: "A new code was sent to your inbox.",
109
+ };
110
+ });
@@ -0,0 +1,45 @@
1
+ "use client";
2
+ import { useHookFormAction } from "@next-safe-action/adapter-react-hook-form/hooks";
3
+ import { emailVerificationSchema } from "./schema";
4
+ import { verifyEmailAction } from "./actions";
5
+ import { zodResolver } from "@hookform/resolvers/zod";
6
+ import { Button, Paper, PinInput, Text } from "@mantine/core";
7
+ import { Stack } from "@mantine/core";
8
+
9
+ export default function LoginForm() {
10
+ const { form, handleSubmitWithAction, action } = useHookFormAction(
11
+ verifyEmailAction,
12
+ zodResolver(emailVerificationSchema),
13
+ {}
14
+ );
15
+
16
+ return (
17
+ <form onSubmit={handleSubmitWithAction}>
18
+ <Paper withBorder shadow="md" p={30} mt={30} radius="md">
19
+ <Stack>
20
+ <PinInput
21
+ length={8}
22
+ autoFocus
23
+ oneTimeCode
24
+ {...form.register("code")}
25
+ onChange={(value) => {
26
+ form.setValue("code", value);
27
+ if (value.length === 8) {
28
+ handleSubmitWithAction();
29
+ }
30
+ }}
31
+ />
32
+
33
+ {action.result.data?.error ? (
34
+ <Text c="red">{action.result.data.error}</Text>
35
+ ) : action.hasErrored ? (
36
+ <Text c="red">An error occured</Text>
37
+ ) : null}
38
+ <Button fullWidth type="submit" loading={action.isPending}>
39
+ Verify email
40
+ </Button>
41
+ </Stack>
42
+ </Paper>
43
+ </form>
44
+ );
45
+ }
@@ -0,0 +1,38 @@
1
+ import { getCurrentSession } from "@/server/auth/utils/session";
2
+ import { Anchor, Container, Text, Title } from "@mantine/core";
3
+ import { redirect } from "next/navigation";
4
+ import EmailVerificationForm from "./email-verification-form";
5
+ import ResendButton from "./resend-button";
6
+ import { getUserEmailVerificationRequestFromRequest } from "@/server/auth/utils/email-verification";
7
+ import { getRedirectCookie } from "@/server/auth/utils/redirect";
8
+
9
+ export default async function Page() {
10
+ const { user } = await getCurrentSession();
11
+
12
+ if (user === null) {
13
+ return redirect("/auth/login");
14
+ }
15
+
16
+ // TODO: Ideally we'd sent a new verification email automatically if the previous one is expired,
17
+ // but we can't set cookies inside server components.
18
+ const verificationRequest = await getUserEmailVerificationRequestFromRequest();
19
+ if (verificationRequest === null && user.emailVerified) {
20
+ const redirectTo = await getRedirectCookie();
21
+ return redirect(redirectTo);
22
+ }
23
+
24
+ return (
25
+ <Container size={420} my={40}>
26
+ <Title ta="center">Verify your email</Title>
27
+ <Text c="dimmed" size="sm" ta="center" mt={5}>
28
+ Enter the code sent to {verificationRequest?.email ?? user.email}
29
+ </Text>
30
+ <Text c="dimmed" size="sm" ta="center">
31
+ <Anchor href="/auth/profile">Change email</Anchor>
32
+ </Text>
33
+
34
+ <EmailVerificationForm />
35
+ <ResendButton />
36
+ </Container>
37
+ );
38
+ }
@@ -0,0 +1,35 @@
1
+ "use client";
2
+ import { Alert, Anchor, Button, Group, Stack, Text } from "@mantine/core";
3
+ import { resendEmailVerificationAction } from "./actions";
4
+ import { useAction } from "next-safe-action/hooks";
5
+
6
+ export default function ResendButton() {
7
+ const action = useAction(resendEmailVerificationAction);
8
+ return (
9
+ <Stack>
10
+ <Group gap={4} justify="center" mt={5}>
11
+ <Text c="dimmed" size="sm">
12
+ {"Didn't receive the email?"}
13
+ </Text>
14
+ <Button
15
+ size="compact-sm"
16
+ variant="subtle"
17
+ disabled={action.isPending}
18
+ onClick={() => action.execute()}
19
+ >
20
+ Resend
21
+ </Button>
22
+ </Group>
23
+
24
+ {action.result.data?.message && (
25
+ <Alert ta="center">{action.result.data.message}</Alert>
26
+ )}
27
+
28
+ {action.result.data?.error && (
29
+ <Alert ta="center" color="red">
30
+ {action.result.data.error}
31
+ </Alert>
32
+ )}
33
+ </Stack>
34
+ );
35
+ }
@@ -0,0 +1,5 @@
1
+ import { z } from "zod/v4";
2
+
3
+ export const emailVerificationSchema = z.object({
4
+ code: z.string().length(8),
5
+ });
@@ -0,0 +1,16 @@
1
+ "use server";
2
+
3
+ import { getCurrentSession, invalidateSession } from "@/server/auth/utils/session";
4
+ import { redirect } from "next/navigation";
5
+
6
+ export async function currentSessionAction() {
7
+ return await getCurrentSession();
8
+ }
9
+
10
+ export async function logoutAction() {
11
+ const { session } = await currentSessionAction();
12
+ if (session) {
13
+ await invalidateSession(session.id);
14
+ }
15
+ redirect("/");
16
+ }
@@ -0,0 +1,17 @@
1
+ import { getCurrentSession } from "@/server/auth/utils/session";
2
+ import AuthRedirect from "./redirect";
3
+
4
+ /**
5
+ * This server component will protect the contents of it's children from users who aren't logged in
6
+ * It will redirect to the login page if the user is not logged in, or the verify email page if the user is logged in but hasn't verified their email
7
+ */
8
+ export default async function Protect({
9
+ children,
10
+ }: {
11
+ children: React.ReactNode;
12
+ }) {
13
+ const { session, user } = await getCurrentSession();
14
+ if (!session) return <AuthRedirect path="/auth/login" />;
15
+ if (!user.emailVerified) return <AuthRedirect path="/auth/verify-email" />;
16
+ return <>{children}</>;
17
+ }
@@ -0,0 +1,26 @@
1
+ "use client";
2
+
3
+ import { redirect } from "next/navigation";
4
+ import { Center, Loader } from "@mantine/core";
5
+ import { useEffect } from "react";
6
+ import Cookies from "js-cookie";
7
+
8
+ /**
9
+ * A client-side component that redirects to the given path, but saves the current path in the redirectTo cookie.
10
+ */
11
+ export default function AuthRedirect({ path }: { path: string }) {
12
+ useEffect(() => {
13
+ if (typeof window !== "undefined") {
14
+ Cookies.set("redirectTo", window.location.pathname, {
15
+ expires: 1 / 24 / 60, // 1 hour
16
+ });
17
+ redirect(path);
18
+ }
19
+ }, []);
20
+
21
+ return (
22
+ <Center h="100vh">
23
+ <Loader />
24
+ </Center>
25
+ );
26
+ }
@@ -0,0 +1,59 @@
1
+ import { useMutation, useQuery, useQueryClient } from "@tanstack/react-query";
2
+ import { currentSessionAction, logoutAction } from "./actions";
3
+ import { Session } from "@/server/auth/utils/session";
4
+ import { User } from "@/server/auth/utils/user";
5
+
6
+ type LogoutAction = () => Promise<void>;
7
+ type UseUserResult =
8
+ | {
9
+ state: "authenticated";
10
+ session: Session;
11
+ user: User;
12
+ logout: LogoutAction;
13
+ }
14
+ | {
15
+ state: "unauthenticated";
16
+ session: null;
17
+ user: null;
18
+ logout: LogoutAction;
19
+ }
20
+ | { state: "loading"; session: null; user: null; logout: LogoutAction };
21
+
22
+ export function useUser(): UseUserResult {
23
+ const query = useQuery({
24
+ queryKey: ["current-user"],
25
+ queryFn: () => currentSessionAction(),
26
+ retry: false,
27
+ });
28
+ const queryClient = useQueryClient();
29
+
30
+ const { mutateAsync } = useMutation({
31
+ mutationFn: logoutAction,
32
+ onMutate: async () => {
33
+ await queryClient.cancelQueries({ queryKey: ["current-user"] });
34
+ queryClient.setQueryData(["current-user"], { session: null, user: null });
35
+ },
36
+ onSettled: () =>
37
+ queryClient.invalidateQueries({ queryKey: ["current-user"] }),
38
+ });
39
+
40
+ const defaultResult: UseUserResult = {
41
+ state: "unauthenticated",
42
+ session: null,
43
+ user: null,
44
+ logout: mutateAsync,
45
+ };
46
+
47
+ if (query.isLoading) {
48
+ return { ...defaultResult, state: "loading" };
49
+ }
50
+ if (query.data?.session) {
51
+ return {
52
+ ...defaultResult,
53
+ state: "authenticated",
54
+ session: query.data.session,
55
+ user: query.data.user,
56
+ };
57
+ }
58
+ return defaultResult;
59
+ }
@@ -0,0 +1,51 @@
1
+ "use client";
2
+
3
+ import { Button, Menu, px, Skeleton } from "@mantine/core";
4
+ import { useUser } from "./use-user";
5
+ import Link from "next/link";
6
+ import { IconChevronDown, IconLogout, IconUser } from "@tabler/icons-react";
7
+
8
+ export default function UserMenu() {
9
+ const { state, session, user, logout } = useUser();
10
+
11
+ if (state === "loading") {
12
+ return <Skeleton w={100} h={20} />;
13
+ }
14
+ if (state === "unauthenticated") {
15
+ return (
16
+ <Button component="a" href="/auth/login" variant="subtle" size="sm">
17
+ Sign in
18
+ </Button>
19
+ );
20
+ }
21
+ return (
22
+ <Menu position="bottom-end">
23
+ <Menu.Target>
24
+ <Button
25
+ variant="subtle"
26
+ size="sm"
27
+ color="gray"
28
+ rightSection={<IconChevronDown size={px("1rem")} />}
29
+ >
30
+ {user.email}
31
+ </Button>
32
+ </Menu.Target>
33
+ <Menu.Dropdown>
34
+ <Menu.Item
35
+ component={Link}
36
+ href="/auth/profile"
37
+ leftSection={<IconUser size={px("1rem")} />}
38
+ >
39
+ My Profile
40
+ </Menu.Item>
41
+ <Menu.Divider />
42
+ <Menu.Item
43
+ leftSection={<IconLogout size={px("1rem")} />}
44
+ onClick={logout}
45
+ >
46
+ Sign out
47
+ </Menu.Item>
48
+ </Menu.Dropdown>
49
+ </Menu>
50
+ );
51
+ }