create-fate 0.0.0 → 1.0.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 (266) hide show
  1. package/LICENSE +21 -0
  2. package/README.md +24 -0
  3. package/bin/create-fate.mjs +412 -0
  4. package/package.json +26 -3
  5. package/templates/fate/README.md +8 -0
  6. package/templates/fate/drizzle/.github/workflows/push.yml +27 -0
  7. package/templates/fate/drizzle/.oxfmtrc.jsonc +23 -0
  8. package/templates/fate/drizzle/.vite-hooks/pre-commit +1 -0
  9. package/templates/fate/drizzle/.vscode/extensions.json +12 -0
  10. package/templates/fate/drizzle/.vscode/settings.json +25 -0
  11. package/templates/fate/drizzle/AGENTS.md +55 -0
  12. package/templates/fate/drizzle/README.md +112 -0
  13. package/templates/fate/drizzle/_gitignore +20 -0
  14. package/templates/fate/drizzle/client/package.json +58 -0
  15. package/templates/fate/drizzle/client/pages/index.tsx +96 -0
  16. package/templates/fate/drizzle/client/pages/layout.tsx +104 -0
  17. package/templates/fate/drizzle/client/pages/login.tsx +107 -0
  18. package/templates/fate/drizzle/client/pages/post/[id].tsx +22 -0
  19. package/templates/fate/drizzle/client/pages/search.tsx +95 -0
  20. package/templates/fate/drizzle/client/public/icon.svg +1 -0
  21. package/templates/fate/drizzle/client/src/App.css +153 -0
  22. package/templates/fate/drizzle/client/src/lib/AvailableLanguages.tsx +5 -0
  23. package/templates/fate/drizzle/client/src/lib/cx.tsx +6 -0
  24. package/templates/fate/drizzle/client/src/lib/env.tsx +5 -0
  25. package/templates/fate/drizzle/client/src/ui/Badge.tsx +31 -0
  26. package/templates/fate/drizzle/client/src/ui/Button.tsx +108 -0
  27. package/templates/fate/drizzle/client/src/ui/Card.tsx +32 -0
  28. package/templates/fate/drizzle/client/src/ui/CommentCard.tsx +75 -0
  29. package/templates/fate/drizzle/client/src/ui/Error.tsx +19 -0
  30. package/templates/fate/drizzle/client/src/ui/H2.tsx +15 -0
  31. package/templates/fate/drizzle/client/src/ui/H3.tsx +20 -0
  32. package/templates/fate/drizzle/client/src/ui/Header.tsx +96 -0
  33. package/templates/fate/drizzle/client/src/ui/Input.tsx +17 -0
  34. package/templates/fate/drizzle/client/src/ui/Link.tsx +11 -0
  35. package/templates/fate/drizzle/client/src/ui/LocaleSwitcher.tsx +52 -0
  36. package/templates/fate/drizzle/client/src/ui/PostCard.tsx +284 -0
  37. package/templates/fate/drizzle/client/src/ui/Section.tsx +23 -0
  38. package/templates/fate/drizzle/client/src/ui/UserCard.tsx +131 -0
  39. package/templates/fate/drizzle/client/src/user/AuthClient.tsx +6 -0
  40. package/templates/fate/drizzle/client/translations/de_DE.json +689 -0
  41. package/templates/fate/drizzle/client/translations/ja_JP.json +689 -0
  42. package/templates/fate/drizzle/client/vite.config.ts +39 -0
  43. package/templates/fate/drizzle/client/void.json +29 -0
  44. package/templates/fate/drizzle/docker-compose.yml +11 -0
  45. package/templates/fate/drizzle/package.json +46 -0
  46. package/templates/fate/drizzle/pnpm-workspace.yaml +36 -0
  47. package/templates/fate/drizzle/public/fate-logo-dark.svg +4 -0
  48. package/templates/fate/drizzle/public/fate-logo.svg +4 -0
  49. package/templates/fate/drizzle/server/.env.example +5 -0
  50. package/templates/fate/drizzle/server/drizzle.config.ts +11 -0
  51. package/templates/fate/drizzle/server/package.json +48 -0
  52. package/templates/fate/drizzle/server/src/__tests__/index.test.tsx +5 -0
  53. package/templates/fate/drizzle/server/src/app.tsx +44 -0
  54. package/templates/fate/drizzle/server/src/drizzle/db.ts +25 -0
  55. package/templates/fate/drizzle/server/src/drizzle/queries.ts +89 -0
  56. package/templates/fate/drizzle/server/src/drizzle/schema.ts +174 -0
  57. package/templates/fate/drizzle/server/src/drizzle/seed.tsx +77 -0
  58. package/templates/fate/drizzle/server/src/drizzle/seedData.ts +205 -0
  59. package/templates/fate/drizzle/server/src/index.tsx +24 -0
  60. package/templates/fate/drizzle/server/src/lib/auth.ts +33 -0
  61. package/templates/fate/drizzle/server/src/lib/env.ts +3 -0
  62. package/templates/fate/drizzle/server/src/lib/origins.ts +18 -0
  63. package/templates/fate/drizzle/server/src/router.ts +15 -0
  64. package/templates/fate/drizzle/server/src/trpc/connection.ts +4 -0
  65. package/templates/fate/drizzle/server/src/trpc/context.ts +22 -0
  66. package/templates/fate/drizzle/server/src/trpc/init.ts +51 -0
  67. package/templates/fate/drizzle/server/src/trpc/routers/comment.ts +146 -0
  68. package/templates/fate/drizzle/server/src/trpc/routers/post.ts +99 -0
  69. package/templates/fate/drizzle/server/src/trpc/routers/user.ts +68 -0
  70. package/templates/fate/drizzle/server/src/trpc/views.ts +75 -0
  71. package/templates/fate/drizzle/server/src/user/SessionUser.tsx +17 -0
  72. package/templates/fate/drizzle/server/vite.config.ts +8 -0
  73. package/templates/fate/drizzle/tsconfig.json +33 -0
  74. package/templates/fate/drizzle/vite.config.ts +71 -0
  75. package/templates/fate/http/.github/workflows/push.yml +27 -0
  76. package/templates/fate/http/.oxfmtrc.jsonc +23 -0
  77. package/templates/fate/http/.vite-hooks/pre-commit +1 -0
  78. package/templates/fate/http/.vscode/extensions.json +12 -0
  79. package/templates/fate/http/.vscode/settings.json +25 -0
  80. package/templates/fate/http/AGENTS.md +55 -0
  81. package/templates/fate/http/README.md +112 -0
  82. package/templates/fate/http/_gitignore +20 -0
  83. package/templates/fate/http/client/package.json +56 -0
  84. package/templates/fate/http/client/pages/index.tsx +96 -0
  85. package/templates/fate/http/client/pages/layout.tsx +89 -0
  86. package/templates/fate/http/client/pages/login.tsx +107 -0
  87. package/templates/fate/http/client/pages/post/[id].tsx +22 -0
  88. package/templates/fate/http/client/pages/search.tsx +95 -0
  89. package/templates/fate/http/client/public/icon.svg +1 -0
  90. package/templates/fate/http/client/src/App.css +153 -0
  91. package/templates/fate/http/client/src/lib/AvailableLanguages.tsx +5 -0
  92. package/templates/fate/http/client/src/lib/cx.tsx +6 -0
  93. package/templates/fate/http/client/src/lib/env.tsx +5 -0
  94. package/templates/fate/http/client/src/ui/Badge.tsx +31 -0
  95. package/templates/fate/http/client/src/ui/Button.tsx +108 -0
  96. package/templates/fate/http/client/src/ui/Card.tsx +32 -0
  97. package/templates/fate/http/client/src/ui/CommentCard.tsx +75 -0
  98. package/templates/fate/http/client/src/ui/Error.tsx +19 -0
  99. package/templates/fate/http/client/src/ui/H2.tsx +15 -0
  100. package/templates/fate/http/client/src/ui/H3.tsx +20 -0
  101. package/templates/fate/http/client/src/ui/Header.tsx +96 -0
  102. package/templates/fate/http/client/src/ui/Input.tsx +17 -0
  103. package/templates/fate/http/client/src/ui/Link.tsx +11 -0
  104. package/templates/fate/http/client/src/ui/LocaleSwitcher.tsx +52 -0
  105. package/templates/fate/http/client/src/ui/PostCard.tsx +281 -0
  106. package/templates/fate/http/client/src/ui/Section.tsx +23 -0
  107. package/templates/fate/http/client/src/ui/UserCard.tsx +131 -0
  108. package/templates/fate/http/client/src/user/AuthClient.tsx +6 -0
  109. package/templates/fate/http/client/translations/de_DE.json +689 -0
  110. package/templates/fate/http/client/translations/ja_JP.json +689 -0
  111. package/templates/fate/http/client/vite.config.ts +40 -0
  112. package/templates/fate/http/client/void.json +29 -0
  113. package/templates/fate/http/docker-compose.yml +11 -0
  114. package/templates/fate/http/package.json +46 -0
  115. package/templates/fate/http/pnpm-workspace.yaml +36 -0
  116. package/templates/fate/http/public/fate-logo-dark.svg +4 -0
  117. package/templates/fate/http/public/fate-logo.svg +4 -0
  118. package/templates/fate/http/server/.env.example +5 -0
  119. package/templates/fate/http/server/drizzle.config.ts +11 -0
  120. package/templates/fate/http/server/package.json +46 -0
  121. package/templates/fate/http/server/src/__tests__/index.test.tsx +5 -0
  122. package/templates/fate/http/server/src/app.tsx +36 -0
  123. package/templates/fate/http/server/src/drizzle/db.ts +25 -0
  124. package/templates/fate/http/server/src/drizzle/queries.ts +89 -0
  125. package/templates/fate/http/server/src/drizzle/schema.ts +174 -0
  126. package/templates/fate/http/server/src/drizzle/seed.tsx +77 -0
  127. package/templates/fate/http/server/src/drizzle/seedData.ts +205 -0
  128. package/templates/fate/http/server/src/fate/context.ts +20 -0
  129. package/templates/fate/http/server/src/fate/server.ts +285 -0
  130. package/templates/fate/http/server/src/fate/views.ts +75 -0
  131. package/templates/fate/http/server/src/index.tsx +24 -0
  132. package/templates/fate/http/server/src/lib/auth.ts +33 -0
  133. package/templates/fate/http/server/src/lib/env.ts +3 -0
  134. package/templates/fate/http/server/src/lib/origins.ts +18 -0
  135. package/templates/fate/http/server/src/router.ts +2 -0
  136. package/templates/fate/http/server/src/user/SessionUser.tsx +17 -0
  137. package/templates/fate/http/server/vite.config.ts +8 -0
  138. package/templates/fate/http/tsconfig.json +33 -0
  139. package/templates/fate/http/vite.config.ts +71 -0
  140. package/templates/fate/prisma/.github/workflows/push.yml +27 -0
  141. package/templates/fate/prisma/.oxfmtrc.jsonc +23 -0
  142. package/templates/fate/prisma/.vite-hooks/pre-commit +1 -0
  143. package/templates/fate/prisma/.vscode/extensions.json +12 -0
  144. package/templates/fate/prisma/.vscode/settings.json +25 -0
  145. package/templates/fate/prisma/AGENTS.md +55 -0
  146. package/templates/fate/prisma/README.md +126 -0
  147. package/templates/fate/prisma/_gitignore +20 -0
  148. package/templates/fate/prisma/client/package.json +58 -0
  149. package/templates/fate/prisma/client/pages/index.tsx +96 -0
  150. package/templates/fate/prisma/client/pages/layout.tsx +94 -0
  151. package/templates/fate/prisma/client/pages/login.tsx +107 -0
  152. package/templates/fate/prisma/client/pages/post/[id].tsx +22 -0
  153. package/templates/fate/prisma/client/pages/search.tsx +95 -0
  154. package/templates/fate/prisma/client/public/icon.svg +1 -0
  155. package/templates/fate/prisma/client/src/App.css +153 -0
  156. package/templates/fate/prisma/client/src/lib/AvailableLanguages.tsx +5 -0
  157. package/templates/fate/prisma/client/src/lib/cx.tsx +6 -0
  158. package/templates/fate/prisma/client/src/lib/env.tsx +5 -0
  159. package/templates/fate/prisma/client/src/ui/Badge.tsx +31 -0
  160. package/templates/fate/prisma/client/src/ui/Button.tsx +108 -0
  161. package/templates/fate/prisma/client/src/ui/Card.tsx +32 -0
  162. package/templates/fate/prisma/client/src/ui/CommentCard.tsx +75 -0
  163. package/templates/fate/prisma/client/src/ui/Error.tsx +19 -0
  164. package/templates/fate/prisma/client/src/ui/H2.tsx +15 -0
  165. package/templates/fate/prisma/client/src/ui/H3.tsx +20 -0
  166. package/templates/fate/prisma/client/src/ui/Header.tsx +96 -0
  167. package/templates/fate/prisma/client/src/ui/Input.tsx +17 -0
  168. package/templates/fate/prisma/client/src/ui/Link.tsx +11 -0
  169. package/templates/fate/prisma/client/src/ui/LocaleSwitcher.tsx +52 -0
  170. package/templates/fate/prisma/client/src/ui/PostCard.tsx +281 -0
  171. package/templates/fate/prisma/client/src/ui/Section.tsx +23 -0
  172. package/templates/fate/prisma/client/src/ui/UserCard.tsx +131 -0
  173. package/templates/fate/prisma/client/src/user/AuthClient.tsx +6 -0
  174. package/templates/fate/prisma/client/translations/de_DE.json +689 -0
  175. package/templates/fate/prisma/client/translations/ja_JP.json +689 -0
  176. package/templates/fate/prisma/client/vite.config.ts +39 -0
  177. package/templates/fate/prisma/client/void.json +29 -0
  178. package/templates/fate/prisma/docker-compose.yml +11 -0
  179. package/templates/fate/prisma/package.json +47 -0
  180. package/templates/fate/prisma/pnpm-workspace.yaml +39 -0
  181. package/templates/fate/prisma/public/fate-logo-dark.svg +4 -0
  182. package/templates/fate/prisma/public/fate-logo.svg +4 -0
  183. package/templates/fate/prisma/server/.env.example +5 -0
  184. package/templates/fate/prisma/server/package.json +42 -0
  185. package/templates/fate/prisma/server/prisma.config.ts +21 -0
  186. package/templates/fate/prisma/server/src/__tests__/index.test.tsx +5 -0
  187. package/templates/fate/prisma/server/src/app.tsx +40 -0
  188. package/templates/fate/prisma/server/src/index.tsx +24 -0
  189. package/templates/fate/prisma/server/src/lib/auth.ts +31 -0
  190. package/templates/fate/prisma/server/src/lib/env.ts +3 -0
  191. package/templates/fate/prisma/server/src/lib/origins.ts +18 -0
  192. package/templates/fate/prisma/server/src/prisma/prisma.ts +9 -0
  193. package/templates/fate/prisma/server/src/prisma/schema.prisma +98 -0
  194. package/templates/fate/prisma/server/src/prisma/seed.tsx +71 -0
  195. package/templates/fate/prisma/server/src/prisma/seedData.ts +205 -0
  196. package/templates/fate/prisma/server/src/router.ts +14 -0
  197. package/templates/fate/prisma/server/src/trpc/connection.ts +4 -0
  198. package/templates/fate/prisma/server/src/trpc/context.ts +22 -0
  199. package/templates/fate/prisma/server/src/trpc/init.ts +8 -0
  200. package/templates/fate/prisma/server/src/trpc/routers/comment.ts +176 -0
  201. package/templates/fate/prisma/server/src/trpc/routers/post.ts +154 -0
  202. package/templates/fate/prisma/server/src/trpc/routers/user.ts +71 -0
  203. package/templates/fate/prisma/server/src/trpc/views.ts +77 -0
  204. package/templates/fate/prisma/server/src/user/SessionUser.tsx +17 -0
  205. package/templates/fate/prisma/server/vite.config.ts +8 -0
  206. package/templates/fate/prisma/tsconfig.json +33 -0
  207. package/templates/fate/prisma/vite.config.ts +72 -0
  208. package/templates/fate/void/.oxfmtrc.jsonc +14 -0
  209. package/templates/fate/void/README.md +55 -0
  210. package/templates/fate/void/auth.ts +22 -0
  211. package/templates/fate/void/db/migrations/20260508073357_initial_schema.sql +133 -0
  212. package/templates/fate/void/db/migrations/20260508120500_seed_void_demo.sql +170 -0
  213. package/templates/fate/void/db/migrations/meta/20260508073357_snapshot.json +868 -0
  214. package/templates/fate/void/db/migrations/meta/_journal.json +20 -0
  215. package/templates/fate/void/db/queries.ts +211 -0
  216. package/templates/fate/void/db/schema.ts +325 -0
  217. package/templates/fate/void/db/seed-auth.ts +31 -0
  218. package/templates/fate/void/db/seed.ts +163 -0
  219. package/templates/fate/void/middleware/01.shared.ts +29 -0
  220. package/templates/fate/void/package.json +65 -0
  221. package/templates/fate/void/pages/category/[id].tsx +23 -0
  222. package/templates/fate/void/pages/index.tsx +153 -0
  223. package/templates/fate/void/pages/layout.tsx +46 -0
  224. package/templates/fate/void/pages/login.tsx +103 -0
  225. package/templates/fate/void/pages/post/[id].tsx +20 -0
  226. package/templates/fate/void/pages/search.tsx +81 -0
  227. package/templates/fate/void/pnpm-workspace.yaml +31 -0
  228. package/templates/fate/void/public/.assetsignore +1 -0
  229. package/templates/fate/void/public/icon.svg +1 -0
  230. package/templates/fate/void/routes/fate-live.ts +8 -0
  231. package/templates/fate/void/routes/fate.ts +8 -0
  232. package/templates/fate/void/seedData.ts +535 -0
  233. package/templates/fate/void/src/App.css +130 -0
  234. package/templates/fate/void/src/fate/__tests__/commentPermissions.test.ts +31 -0
  235. package/templates/fate/void/src/fate/commentPermissions.ts +17 -0
  236. package/templates/fate/void/src/fate/context.ts +15 -0
  237. package/templates/fate/void/src/fate/live.ts +6 -0
  238. package/templates/fate/void/src/fate/server.ts +401 -0
  239. package/templates/fate/void/src/fate/views.ts +164 -0
  240. package/templates/fate/void/src/lib/cx.tsx +6 -0
  241. package/templates/fate/void/src/lib/formatLabel.tsx +6 -0
  242. package/templates/fate/void/src/lib/shared.ts +10 -0
  243. package/templates/fate/void/src/ui/Badge.tsx +31 -0
  244. package/templates/fate/void/src/ui/Button.tsx +92 -0
  245. package/templates/fate/void/src/ui/Card.tsx +29 -0
  246. package/templates/fate/void/src/ui/CategoryCard.tsx +105 -0
  247. package/templates/fate/void/src/ui/CommentCard.tsx +80 -0
  248. package/templates/fate/void/src/ui/CreatePost.tsx +111 -0
  249. package/templates/fate/void/src/ui/Error.tsx +19 -0
  250. package/templates/fate/void/src/ui/EventCard.tsx +152 -0
  251. package/templates/fate/void/src/ui/H2.tsx +15 -0
  252. package/templates/fate/void/src/ui/H3.tsx +20 -0
  253. package/templates/fate/void/src/ui/Header.tsx +96 -0
  254. package/templates/fate/void/src/ui/Input.tsx +33 -0
  255. package/templates/fate/void/src/ui/Link.tsx +11 -0
  256. package/templates/fate/void/src/ui/Number.tsx +5 -0
  257. package/templates/fate/void/src/ui/PostCard.tsx +294 -0
  258. package/templates/fate/void/src/ui/Section.tsx +23 -0
  259. package/templates/fate/void/src/ui/TagBadge.tsx +23 -0
  260. package/templates/fate/void/src/ui/UserCard.tsx +129 -0
  261. package/templates/fate/void/src/user/AuthClient.tsx +1 -0
  262. package/templates/fate/void/src/user/SessionUser.tsx +17 -0
  263. package/templates/fate/void/tsconfig.json +34 -0
  264. package/templates/fate/void/vite.config.ts +83 -0
  265. package/templates/fate/void/void.json +32 -0
  266. package/templates/fate/void/wrangler.jsonc +16 -0
@@ -0,0 +1,112 @@
1
+ <p align="center">
2
+ <picture>
3
+ <source media="(prefers-color-scheme: dark)" srcset="/public/fate-logo-dark.svg">
4
+ <source media="(prefers-color-scheme: light)" srcset="/public/fate-logo.svg">
5
+ <img alt="Logo" src="/public/fate-logo.svg" width="50%">
6
+ </picture>
7
+ </p>
8
+
9
+ **_fate_** is a modern data client for React inspired by [Relay](https://relay.dev/) and [GraphQL](https://graphql.org/). It combines view composition, normalized caching, data masking, Async React features, and type-safe data fetching.
10
+
11
+ Check out [fate.technology](https://fate.technology) for documentation, examples, and guides.
12
+
13
+ # _fate_ Quick Start Template
14
+
15
+ Create a new fate app with Vite+:
16
+
17
+ ```bash
18
+ vp create fate my-app --template drizzle
19
+ ```
20
+
21
+ ## Getting Started with _fate_
22
+
23
+ Read the [Getting Started Guide](https://fate.technology/guide/core-concepts) to learn how to use _fate_ in your React applications.
24
+
25
+ ## Technologies
26
+
27
+ This template combines the Nakazawa Tech's [Web App Template](https://github.com/nkzw-tech/web-app-template) and [Server Template](https://github.com/nkzw-tech/server-template) into a monorepo, with unified tooling and _fate_ as the data client. It uses tRPC with Drizzle.
28
+
29
+ _This template lives on the edge._ It's a turbocharged starting point on top of an already optimized stack. It uses TypeScript Go, Vite+, and ships with sensible defaults to unlock an incredibly fast development experience. It follows the principles laid out in [Building Scalable Applications](https://www.youtube.com/watch?v=rxPTEko8J7c&t=36s).
30
+
31
+ Next to [_fate_](https://fate.technology), it comes with the following technologies:
32
+
33
+ ### Client Technologies
34
+
35
+ - [Vite 8](https://vitejs.dev/)
36
+ - [React](https://reactjs.org/) with [React Compiler](https://react.dev/learn/react-compiler) enabled
37
+ - [Tailwind](https://tailwindcss.com/)
38
+ - [fbtee](https://github.com/nkzw-tech/fbtee) for i18n
39
+ - [Better Auth](https://www.better-auth.com/) for authentication
40
+ - [Void Pages Router](https://void.cloud/docs)
41
+ - [TypeScript](https://www.typescriptlang.org)
42
+ - [pnpm](https://pnpm.io/)
43
+
44
+ ### Server Technologies
45
+
46
+ - [Drizzle](https://orm.drizzle.team/) as the ORM.
47
+ - [tRPC](https://trpc.io/) for type-safe APIs.
48
+ - [Hono](https://hono.dev/)
49
+ - [Better Auth](https://better-auth.com/) for Authentication.
50
+
51
+ ### Folder Structure
52
+
53
+ - `client/` - The React client application using _fate_.
54
+ - `server/` - The fate server using tRPC with Drizzle.
55
+
56
+ ## Initial Setup
57
+
58
+ You'll need Node.js 24+ and [Vite+](https://viteplus.dev/guide/).
59
+
60
+ Install dependencies:
61
+
62
+ ```bash
63
+ vp install
64
+ ```
65
+
66
+ Review `server/.env`, which is copied from `server/.env.example` when the app is created. The default local values expect:
67
+
68
+ - Postgres at `postgresql://fate:echo@localhost:5432/fate`.
69
+ - The server at `http://localhost:9000`.
70
+ - The client at `http://localhost:5173`.
71
+
72
+ Start Postgres with Docker:
73
+
74
+ ```bash
75
+ docker-compose up -d
76
+ ```
77
+
78
+ Alternatively, create the database manually:
79
+
80
+ ```SQL
81
+ CREATE ROLE fate WITH LOGIN PASSWORD 'echo';
82
+ CREATE DATABASE fate;
83
+ ALTER DATABASE fate OWNER TO fate;
84
+ ```
85
+
86
+ Then set up the schema, seed data, translations, and generated fate client:
87
+
88
+ ```bash
89
+ vp run dev:setup
90
+ ```
91
+
92
+ Start the app:
93
+
94
+ ```bash
95
+ vp run dev
96
+ ```
97
+
98
+ The client runs at `http://localhost:5173` and the server runs at `http://localhost:9000`. tRPC requests go to `/trpc`; fate live updates use the SSE endpoint under `/fate/live`.
99
+
100
+ ## Development
101
+
102
+ Common commands from the project root:
103
+
104
+ - `vp run dev` starts the client and server together.
105
+ - `vp run dev:client` starts only the client.
106
+ - `vp run dev:server` starts only the server.
107
+ - `vp run dev:setup` pushes the Drizzle schema, seeds the database, runs fbtee setup, and regenerates the fate client.
108
+ - `vp run fate:generate` regenerates `client/.fate/client.generated.ts` after changing server views, roots, or routers.
109
+ - `vp run drizzle` opens Drizzle Kit commands for the server package.
110
+ - `vp check --fix` formats, lints, and type-checks the workspace.
111
+ - `vp test` runs the test suite.
112
+ - `vp run build` builds the client and server.
@@ -0,0 +1,20 @@
1
+ .fate/
2
+ **/.fate/
3
+ .void/
4
+ **/.void/
5
+ .eslintcache
6
+ .pnpm-debug.log
7
+ **/node_modules/
8
+ /node_modules/
9
+ client/.enum_manifest.json
10
+ client/dist/
11
+ client/source_strings.json
12
+ client/src/translations/
13
+ coverage/
14
+ dist/
15
+ server/.env
16
+ server/.prod.env
17
+ server/dist/
18
+ server/src/prisma/prisma-client/
19
+ tsconfig.tsbuildinfo
20
+ vite.config.ts.timestamp-*
@@ -0,0 +1,58 @@
1
+ {
2
+ "name": "@app/client",
3
+ "version": "0.0.1",
4
+ "private": true,
5
+ "author": "Christoph Nakazawa <christoph.pojer@gmail.com>",
6
+ "repository": {
7
+ "type": "git",
8
+ "url": "git://github.com/nkzw-tech/fate.git"
9
+ },
10
+ "type": "module",
11
+ "scripts": {
12
+ "build": "vp build",
13
+ "dev": "vp dev",
14
+ "dev:setup": "vp run fbtee:all",
15
+ "fate:generate": "vp exec fate generate",
16
+ "fbtee:all": "vpx fbtee collect && vpx fbtee translate"
17
+ },
18
+ "dependencies": {
19
+ "@app/server": "workspace:*",
20
+ "@hono/node-server": "^2.0.1",
21
+ "@nkzw/core": "^1.3.1",
22
+ "@nkzw/define-env": "^1.1.0",
23
+ "@nkzw/stack": "^2.3.2",
24
+ "@radix-ui/react-slot": "^1.2.4",
25
+ "@trpc/client": "^11.17.0",
26
+ "@void/react": "^0.7.5",
27
+ "better-auth": "^1.6.9",
28
+ "class-variance-authority": "^0.7.1",
29
+ "clsx": "^2.1.1",
30
+ "fbtee": "^1.7.4",
31
+ "lucide-react": "^1.14.0",
32
+ "react": "^19.2.5",
33
+ "react-dom": "^19.2.5",
34
+ "react-error-boundary": "^6.1.1",
35
+ "react-fate": "latest",
36
+ "tailwind-merge": "^3.5.0",
37
+ "void": "^0.7.5"
38
+ },
39
+ "devDependencies": {
40
+ "@nkzw/babel-preset-fbtee": "^1.7.4",
41
+ "@rolldown/plugin-babel": "^0.2.3",
42
+ "@tailwindcss/vite": "^4.2.4",
43
+ "@trpc/server": "^11.17.0",
44
+ "@types/babel__core": "^7.20.5",
45
+ "@types/node": "^25.6.0",
46
+ "@types/react": "^19.2.14",
47
+ "@types/react-dom": "^19.2.3",
48
+ "@vitejs/plugin-react": "^6.0.1",
49
+ "tailwindcss": "^4.2.4",
50
+ "vite": "catalog:",
51
+ "vite-plus": "catalog:"
52
+ },
53
+ "browserslist": [
54
+ ">0.2%",
55
+ "not dead",
56
+ "not op_mini all"
57
+ ]
58
+ }
@@ -0,0 +1,96 @@
1
+ import Stack, { VStack } from '@nkzw/stack';
2
+ import { ConnectionRef, useListView, useRequest } from 'react-fate';
3
+ import cx from '../src/lib/cx.tsx';
4
+ import { Button } from '../src/ui/Button.tsx';
5
+ import Card from '../src/ui/Card.tsx';
6
+ import H2 from '../src/ui/H2.tsx';
7
+ import Link from '../src/ui/Link.tsx';
8
+ import { PostCard, PostView } from '../src/ui/PostCard.tsx';
9
+ import Section from '../src/ui/Section.tsx';
10
+ import UserCard, { UserView } from '../src/ui/UserCard.tsx';
11
+
12
+ const PostConnectionView = {
13
+ args: { first: 3 },
14
+ items: {
15
+ node: PostView,
16
+ },
17
+ pagination: {
18
+ hasNext: true,
19
+ },
20
+ };
21
+
22
+ const PostFeed = ({ posts: postsRef }: { posts: ConnectionRef<'Post'> }) => {
23
+ const [posts, loadNext] = useListView(PostConnectionView, postsRef);
24
+
25
+ return posts.length ? (
26
+ <VStack gap={16}>
27
+ <H2 className="pl-5">
28
+ <fbt desc="Latest posts header">Latest posts</fbt>
29
+ </H2>
30
+ <VStack gap={32}>
31
+ {posts.map(({ node }) => (
32
+ <PostCard key={node.id} post={node} />
33
+ ))}
34
+ {loadNext ? (
35
+ <Stack center>
36
+ <Button onClick={loadNext} variant="ghost">
37
+ <fbt desc="Load more posts button">Load more posts</fbt>
38
+ </Button>
39
+ </Stack>
40
+ ) : null}
41
+ </VStack>
42
+ </VStack>
43
+ ) : null;
44
+ };
45
+
46
+ export default function HomePage() {
47
+ const { posts, viewer } = useRequest({
48
+ posts: { list: PostConnectionView },
49
+ viewer: { view: UserView },
50
+ });
51
+
52
+ return (
53
+ <Section gap={32}>
54
+ <div
55
+ className={cx('grid gap-8 lg:items-stretch', viewer?.id ? 'lg:grid-cols-[1.6fr_1fr]' : '')}
56
+ >
57
+ <Card className="border border-white/20 bg-linear-to-r from-blue-500 to-sky-500 text-white dark:from-blue-600 dark:to-sky-600">
58
+ <Stack alignCenter gap={12} wrap>
59
+ <span className="squircle bg-white/20 px-2 py-1 text-xs font-semibold tracking-widest uppercase">
60
+ <span className="lowercase italic">fate</span> demo
61
+ </span>
62
+ </Stack>
63
+ <div className="space-y-3">
64
+ <h1 className="text-3xl leading-tight font-semibold text-balance lg:text-4xl">
65
+ <fbt desc="Description of fate">
66
+ fate is a modern data client for React inspired by Relay and GraphQL.
67
+ </fbt>
68
+ </h1>
69
+ <p className="text-sm text-white/80 lg:text-base">
70
+ <fbt desc="Tagline for fate">
71
+ fate combines view composition, normalized caching, data masking, Async React
72
+ features, and type-safe data fetching.
73
+ </fbt>
74
+ </p>
75
+ </div>
76
+ <Stack alignCenter gap={12} wrap>
77
+ {!viewer?.id && (
78
+ <>
79
+ <Button asChild size="sm" variant="secondary">
80
+ <Link className="squircle px-4 py-2 text-sm font-semibold" to="/login">
81
+ <fbt desc="Login button">Login</fbt>
82
+ </Link>
83
+ </Button>
84
+ <span className="text-sm text-white/80">
85
+ <fbt desc="Login button label">Sign in to post comments.</fbt>
86
+ </span>
87
+ </>
88
+ )}
89
+ </Stack>
90
+ </Card>
91
+ {viewer?.id && <UserCard user={viewer} />}
92
+ </div>
93
+ <PostFeed posts={posts} />
94
+ </Section>
95
+ );
96
+ }
@@ -0,0 +1,104 @@
1
+ /// <reference types="fbtee/ReactTypes.d.ts" />
2
+
3
+ import '../src/App.css';
4
+ import Stack from '@nkzw/stack';
5
+ import { httpBatchLink } from '@trpc/client';
6
+ import { createLocaleContext } from 'fbtee';
7
+ import { ReactNode, Suspense, useMemo } from 'react';
8
+ import { ErrorBoundary } from 'react-error-boundary';
9
+ import { FateClient } from 'react-fate';
10
+ import { createFateClient } from 'react-fate/client';
11
+ import AvailableLanguages from '../src/lib/AvailableLanguages.tsx';
12
+ import env from '../src/lib/env.tsx';
13
+ import Card from '../src/ui/Card.tsx';
14
+ import Error from '../src/ui/Error.tsx';
15
+ import Header from '../src/ui/Header.tsx';
16
+ import Section from '../src/ui/Section.tsx';
17
+ import AuthClient from '../src/user/AuthClient.tsx';
18
+
19
+ const Thinking = () => (
20
+ <Section>
21
+ <Stack center className="animate-pulse text-gray-500 italic" verticalPadding={48}>
22
+ <fbt desc="Text for thinking/loading screen">Thinking...</fbt>
23
+ </Stack>
24
+ </Section>
25
+ );
26
+
27
+ const LocaleContext = createLocaleContext({
28
+ availableLanguages: AvailableLanguages,
29
+ clientLocales: [navigator.language, ...navigator.languages],
30
+ loadLocale: async (locale: string) => {
31
+ if (locale !== 'en_US' && AvailableLanguages.has(locale)) {
32
+ return (await import(`../src/translations/${locale}.json`)).default[locale];
33
+ }
34
+
35
+ return {};
36
+ },
37
+ });
38
+
39
+ export default function Layout({ children }: { children: ReactNode }) {
40
+ const { data: session, isPending } = AuthClient.useSession();
41
+ const userId = session?.user.id;
42
+
43
+ const fate = useMemo(
44
+ () =>
45
+ createFateClient({
46
+ links: [
47
+ httpBatchLink({
48
+ fetch: (input, init) =>
49
+ fetch(input, {
50
+ ...init,
51
+ credentials: userId ? 'include' : undefined,
52
+ }),
53
+ url: `${env('SERVER_URL')}/trpc`,
54
+ }),
55
+ ],
56
+ liveUrl: `${env('SERVER_URL')}/fate`,
57
+ ...(userId
58
+ ? {
59
+ fetch: (input: RequestInfo | URL, init?: RequestInit) =>
60
+ fetch(input, {
61
+ ...init,
62
+ credentials: 'include',
63
+ }),
64
+ }
65
+ : null),
66
+ }),
67
+ [userId],
68
+ );
69
+
70
+ if (isPending) {
71
+ return (
72
+ <LocaleContext>
73
+ <div className="min-h-screen bg-background text-foreground">
74
+ <div className="min-h-screen bg-[radial-gradient(circle_at_20%_20%,rgba(59,130,246,0.08),transparent_35%),radial-gradient(circle_at_80%_0,rgba(99,102,241,0.08),transparent_28%)]">
75
+ <Thinking />
76
+ </div>
77
+ </div>
78
+ </LocaleContext>
79
+ );
80
+ }
81
+
82
+ return (
83
+ <LocaleContext>
84
+ <FateClient client={fate} key={userId}>
85
+ <div className="min-h-screen bg-background text-foreground">
86
+ <div className="min-h-screen bg-[radial-gradient(circle_at_20%_20%,rgba(59,130,246,0.08),transparent_35%),radial-gradient(circle_at_80%_0,rgba(99,102,241,0.08),transparent_28%)]">
87
+ <Header />
88
+ <ErrorBoundary
89
+ fallbackRender={({ error }) => (
90
+ <Section>
91
+ <Card>
92
+ <Error error={error} />
93
+ </Card>
94
+ </Section>
95
+ )}
96
+ >
97
+ <Suspense fallback={<Thinking />}>{children}</Suspense>
98
+ </ErrorBoundary>
99
+ </div>
100
+ </div>
101
+ </FateClient>
102
+ </LocaleContext>
103
+ );
104
+ }
@@ -0,0 +1,107 @@
1
+ import Stack, { VStack } from '@nkzw/stack';
2
+ import { useRouter } from '@void/react';
3
+ import { fbs } from 'fbtee';
4
+ import { ExternalLinkIcon } from 'lucide-react';
5
+ import { useActionState, useEffect, useState } from 'react';
6
+ import { AsyncButton } from '../src/ui/Button.tsx';
7
+ import Card from '../src/ui/Card.tsx';
8
+ import H2 from '../src/ui/H2.tsx';
9
+ import Input from '../src/ui/Input.tsx';
10
+ import Section from '../src/ui/Section.tsx';
11
+ import AuthClient from '../src/user/AuthClient.tsx';
12
+
13
+ export default function LoginPage() {
14
+ const [email, setEmail] = useState('');
15
+ const [password, setPassword] = useState('');
16
+ const router = useRouter();
17
+ const { data: session } = AuthClient.useSession();
18
+
19
+ const [, signInAction] = useActionState(async () => {
20
+ await AuthClient.signIn.email(
21
+ {
22
+ email,
23
+ password,
24
+ },
25
+ {
26
+ onError: () => {},
27
+ onRequest: () => {},
28
+ onSuccess: () => {
29
+ router.flushAll();
30
+ void router.visit('/', { replace: true });
31
+ },
32
+ },
33
+ );
34
+
35
+ return null;
36
+ }, null);
37
+
38
+ useEffect(() => {
39
+ if (session) {
40
+ router.flushAll();
41
+ void router.visit('/', { replace: true });
42
+ }
43
+ }, [router, session]);
44
+
45
+ if (session) {
46
+ return null;
47
+ }
48
+
49
+ return (
50
+ <Section>
51
+ <VStack center gap={16}>
52
+ <H2 className="pl-5">
53
+ <fbt desc="Sign In headline">Sign In</fbt>
54
+ </H2>
55
+ <Stack gap={32} wrap>
56
+ <Card className="w-84">
57
+ <Stack gap vertical>
58
+ <VStack action={signInAction} as="form" gap={12}>
59
+ <Input
60
+ className="w-48"
61
+ name="email"
62
+ onChange={(e) => setEmail(e.target.value)}
63
+ placeholder={fbs('email', 'Email placeholder')}
64
+ type="email"
65
+ value={email}
66
+ />
67
+ <Input
68
+ className="w-48"
69
+ name="password"
70
+ onChange={(e) => setPassword(e.target.value)}
71
+ placeholder={fbs('password', 'Password placeholder')}
72
+ type="password"
73
+ value={password}
74
+ />
75
+ <div>
76
+ <AsyncButton type="submit" variant="outline">
77
+ <fbt desc="Sign in button">Sign In</fbt>
78
+ </AsyncButton>
79
+ </div>
80
+ </VStack>
81
+ </Stack>
82
+ </Card>
83
+ <Card className="w-84">
84
+ <p>
85
+ <fbt desc="Login details">
86
+ Try one of the
87
+ <Stack
88
+ alignCenter
89
+ as="a"
90
+ className="inline-flex! px-1 underline hover:no-underline"
91
+ gap={4}
92
+ href="https://github.com/nkzw-tech/fate/blob/main/packages/create-fate/templates/fate/drizzle/server/src/drizzle/seedData.ts#L1"
93
+ rel="noreferrer"
94
+ target="_blank"
95
+ >
96
+ Example Accounts
97
+ <ExternalLinkIcon className="h-4 w-4" />
98
+ </Stack>{' '}
99
+ in the seed data.
100
+ </fbt>
101
+ </p>
102
+ </Card>
103
+ </Stack>
104
+ </VStack>
105
+ </Section>
106
+ );
107
+ }
@@ -0,0 +1,22 @@
1
+ import { useParams } from '@void/react';
2
+ import { useRequest } from 'react-fate';
3
+ import { PostCard, PostView } from '../../src/ui/PostCard.tsx';
4
+ import Section from '../../src/ui/Section.tsx';
5
+
6
+ export default function PostPage() {
7
+ const { id } = useParams<{ id: string }>();
8
+
9
+ if (!id) {
10
+ throw new Error('fate: Post ID is required.');
11
+ }
12
+
13
+ const { post } = useRequest({
14
+ post: { id, view: PostView },
15
+ });
16
+
17
+ return (
18
+ <Section>
19
+ <PostCard detail post={post} />
20
+ </Section>
21
+ );
22
+ }
@@ -0,0 +1,95 @@
1
+ import type { Comment, Post } from '@app/server/src/trpc/views.ts';
2
+ import Stack, { VStack } from '@nkzw/stack';
3
+ import { fbs } from 'fbtee';
4
+ import { Suspense, useDeferredValue, useState } from 'react';
5
+ import { ErrorBoundary } from 'react-error-boundary';
6
+ import { useRequest, useView, view, ViewRef } from 'react-fate';
7
+ import cx from '../src/lib/cx.tsx';
8
+ import Card from '../src/ui/Card.tsx';
9
+ import CommentCard, { CommentView } from '../src/ui/CommentCard.tsx';
10
+ import Error from '../src/ui/Error.tsx';
11
+ import Input from '../src/ui/Input.tsx';
12
+ import Section from '../src/ui/Section.tsx';
13
+
14
+ const CommentPostView = view<Post>()({
15
+ commentCount: true,
16
+ id: true,
17
+ title: true,
18
+ });
19
+
20
+ const CommentSearchView = view<Comment>()({
21
+ ...CommentView,
22
+ id: true,
23
+ post: CommentPostView,
24
+ });
25
+
26
+ const CommentResult = ({ comment: commentRef }: { comment: ViewRef<'Comment'> }) => {
27
+ const comment = useView(CommentSearchView, commentRef);
28
+ const post = useView(CommentPostView, comment.post);
29
+
30
+ return <CommentCard comment={comment} link post={post} />;
31
+ };
32
+
33
+ const SearchResults = ({ isStale, query }: { isStale: boolean; query: string }) => {
34
+ const { commentSearch } = useRequest({
35
+ commentSearch: { args: { query }, list: CommentSearchView },
36
+ });
37
+
38
+ if (commentSearch.length === 0) {
39
+ return (
40
+ <p>
41
+ <fbt desc="No matches text">
42
+ No matches for{' '}
43
+ <i>
44
+ &quot;<fbt:param name="query">{query}</fbt:param>&quot;
45
+ </i>
46
+ </fbt>
47
+ </p>
48
+ );
49
+ }
50
+
51
+ return (
52
+ <VStack className={cx(isStale && 'opacity-50')} gap={12}>
53
+ {commentSearch.map((comment) => (
54
+ <CommentResult comment={comment} key={comment.id} />
55
+ ))}
56
+ </VStack>
57
+ );
58
+ };
59
+
60
+ export default function SearchPage() {
61
+ const [query, setQuery] = useState('');
62
+ const deferredQuery = useDeferredValue(query);
63
+ const isStale = query !== deferredQuery;
64
+
65
+ return (
66
+ <Section>
67
+ <Card>
68
+ <Stack alignCenter between gap={16}>
69
+ <Input
70
+ className="w-64"
71
+ onChange={(e) => setQuery(e.target.value)}
72
+ placeholder={fbs('Search comments...', 'Search input placeholder')}
73
+ ref={(ref) => ref?.focus()}
74
+ value={query}
75
+ />
76
+ <div className="text-xs text-muted-foreground">
77
+ <fbt desc="Slowdown label explanation">500ms artificial slowdown</fbt>
78
+ </div>
79
+ </Stack>
80
+
81
+ <ErrorBoundary FallbackComponent={Error}>
82
+ <Suspense
83
+ fallback={
84
+ <h2>
85
+ <fbt desc="Text for thinking/loading screen">Thinking…</fbt>
86
+ </h2>
87
+ }
88
+ >
89
+ {query.trim().length > 0 ? <SearchResults isStale={isStale} query={query} /> : null}
90
+ </Suspense>
91
+ </ErrorBoundary>
92
+ </Card>
93
+ </Section>
94
+ );
95
+ }
@@ -0,0 +1 @@
1
+ <svg xmlns="http://www.w3.org/2000/svg" preserveAspectRatio="xMidYMax" viewBox="0 0 160 160"><path d="M108.301 17.465h.272c1.797-.005 3.563.177 5.331.496l.349.063c5.588 1.02 10.731 3.447 15.201 6.933l.347.267c1.579 1.26 3.083 2.684 4.386 4.227l.161.186c4.018 4.719 6.924 10.686 7.767 16.852l.033.239c.262 1.848.351 3.688.352 5.551l.001.307c-.001 1.623-.092 3.188-.386 4.788l-.05.292c-1.003 5.874-3.476 11.447-7.286 16.034l-.29.358c-1.422 1.721-3.011 3.382-4.797 4.729l-.444.354c-3.046 2.405-6.436 4.147-10.084 5.442l-.497.186c-1.766.627-3.615.974-5.465 1.266l-.364.058c-1.591.238-3.185.286-4.791.281h-.499l-1.339-.002h-1.408c-.886-.001-1.772-.001-2.656-.003-3.085-.001-6.163-.005-9.247-.008v-12.78l3.606-.006 2.282-.007c1.205-.006 2.407-.01 3.611-.011.878 0 1.754-.003 2.63-.007.462-.002.928-.005 1.389-.002 6.302.01 12.003-1.72 16.65-6.117l.291-.272c3.951-3.807 6.097-9.302 6.241-14.736.04-3.078-.476-6.048-1.68-8.892l-.104-.244c-2.382-5.559-6.98-9.439-12.504-11.675-2.204-.816-4.522-1.16-6.863-1.134-.301.004-.606.004-.909.001-1.677.003-3.304.127-4.924.591l-.331.098c-2.625.758-5.093 1.928-7.254 3.607l-.323.249c-1.046.817-2.031 1.682-2.873 2.71l-.154.179c-2.354 2.782-3.9 5.988-4.727 9.529l-.054.229c-.391 1.8-.423 3.633-.42 5.471l-.001.74c.002 1.4.001 2.801-.004 4.201l-.004 3.779c0 1.824-.001 3.649-.003 5.473l-.006 2.813-.005 6.076-.001.267-.008 8.614c-.001 2.023-.004 4.045-.006 6.066v.259l-.004 2.55-.006 5.46c0 1.331-.001 2.658-.003 3.985-.002.72-.002 1.438-.002 2.156 0 .658 0 1.317-.002 1.978v.708c.004 2.098-.07 4.214-.545 6.263l-.077.359-.161.716c-.071.302-.136.605-.199.909-.55 2.538-1.6 5.067-2.832 7.348l-.214.42c-.965 1.921-2.22 3.714-3.574 5.38l-.152.183c-.78.973-1.639 1.866-2.519 2.751l-.308.313a25.81 25.81 0 0 1-2.226 1.955l-.22.179c-3.698 2.953-7.975 5.044-12.538 6.262-.212.056-.422.115-.631.176-8.366 2.287-17.8.738-25.278-3.512-3.639-2.122-7.171-4.945-9.721-8.307-.121-.15-.241-.298-.356-.448a36.247 36.247 0 0 1-2.605-3.814l-.144-.234c-.889-1.466-1.562-3.034-2.222-4.617l-.11-.265c-3.253-7.791-2.911-17.367.184-25.133.865-2.069 1.869-4.089 3.123-5.951l.172-.261a31.91 31.91 0 0 1 2.547-3.29l.229-.263c.13-.152.266-.304.396-.452l.425-.493c1.216-1.389 2.593-2.615 4.068-3.723.208-.157.414-.315.62-.476 1.184-.901 2.437-1.665 3.729-2.4l.272-.159c1.586-.893 3.235-1.573 4.934-2.21l.253-.097c4.232-1.567 8.615-1.836 13.079-1.82 1.359 0 2.732.002 4.093.007 1.209.005 2.417.006 3.621.006 1.899.002 3.794.007 5.688.012v12.78l-3.612.013-2.282.011c-1.201.005-2.401.012-3.61.016-.876.003-1.751.005-2.629.013-.463.002-.924.005-1.387.006-4.322.005-8.193.592-12.033 2.662l-.217.114c-1.458.773-2.697 1.731-3.908 2.849-.206.185-.413.362-.63.535-.435.376-.794.788-1.16 1.232l-.153.18a21.18 21.18 0 0 0-4.474 8.651l-.094.342c-.42 1.682-.54 3.395-.536 5.127v.272c.019 5.672 2.392 10.671 6.206 14.769l.215.237c3.296 3.453 8.483 5.917 13.247 6.046.448.008.896.01 1.345.01.276 0 .553.003.835.005 4.917.019 9.864-1.73 13.577-4.995l.303-.267c.299-.263.592-.527.881-.799l.262-.247c.787-.756 1.451-1.599 2.104-2.474l.151-.201c1.929-2.606 3.117-5.723 3.635-8.909l.059-.362c.194-1.296.21-2.573.207-3.883l.003-.74v-2.02c0-.73 0-1.459.005-2.188v-3.782c0-1.827.004-3.654.007-5.481 0-.939 0-1.877.004-2.816l.003-6.082v-.268c.005-2.876.008-5.75.008-8.627l.013-8.883c.004-1.823.004-3.646.004-5.469 0-1.33.004-2.659.004-3.989.003-.721.003-1.442.003-2.159v-1.98c.005-.237.005-.473 0-.711-.011-8.523 3.072-17.023 8.941-23.268.244-.263.483-.531.717-.805.416-.48.861-.918 1.324-1.352l.252-.232a26.259 26.259 0 0 1 2.184-1.812l.46-.349c1.39-1.058 2.837-1.951 4.375-2.775l.318-.172c5.005-2.666 10.5-3.877 16.15-3.889Z" style="transform-box:fill-box;transform-origin:50% 50%" transform="rotate(-31 0 0)"/></svg>