create-croissant 0.1.44 → 0.1.46

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 (170) hide show
  1. package/dist/add.js +8 -8
  2. package/dist/index.js +8 -8
  3. package/package.json +10 -10
  4. package/template/.env.example +2 -2
  5. package/template/.github/dependabot.yml +15 -0
  6. package/template/.github/workflows/ci.yml +143 -0
  7. package/template/.github/workflows/dependabot-automerge.yml +23 -0
  8. package/template/.husky/pre-push +0 -0
  9. package/template/README.md +23 -23
  10. package/template/apps/desktop/.eslintcache +1 -0
  11. package/template/apps/desktop/README.md +5 -5
  12. package/template/apps/desktop/package.json +5 -5
  13. package/template/apps/mobile/README.md +3 -3
  14. package/template/apps/mobile/app/(tabs)/_layout.tsx +21 -14
  15. package/template/apps/mobile/app/(tabs)/account.tsx +147 -0
  16. package/template/apps/mobile/app/(tabs)/explore.tsx +335 -104
  17. package/template/apps/mobile/app/(tabs)/index.tsx +94 -96
  18. package/template/apps/mobile/app/_layout.tsx +26 -7
  19. package/template/apps/mobile/app/index.tsx +129 -0
  20. package/template/apps/mobile/app/login.tsx +135 -0
  21. package/template/apps/mobile/app/signup.tsx +144 -0
  22. package/template/apps/mobile/app.json +9 -4
  23. package/template/apps/mobile/components/ui/button.tsx +86 -0
  24. package/template/apps/mobile/components/ui/input.tsx +56 -0
  25. package/template/apps/mobile/lib/auth-client.ts +1 -1
  26. package/template/apps/mobile/lib/orpc.ts +28 -0
  27. package/template/apps/mobile/package.json +14 -1
  28. package/template/apps/mobile/scripts/reset-project.js +0 -0
  29. package/template/apps/mobile/tsconfig.json +4 -1
  30. package/template/apps/platform/.env.example +1 -1
  31. package/template/apps/platform/package.json +10 -6
  32. package/template/apps/platform/portless.json +3 -0
  33. package/template/apps/platform/src/components/login-form.tsx +5 -4
  34. package/template/apps/platform/src/components/signup-form.tsx +12 -16
  35. package/template/apps/platform/src/router.tsx +0 -1
  36. package/template/apps/platform/src/routes/__root.tsx +6 -2
  37. package/template/apps/platform/src/routes/_auth/account.tsx +13 -17
  38. package/template/apps/platform/src/routes/_auth/examples/client-orpc-auth.tsx +2 -6
  39. package/template/apps/platform/src/routes/_public/examples/client-orpc.tsx +16 -29
  40. package/template/apps/platform/src/routes/_public/examples/ssr-orpc.tsx +10 -14
  41. package/template/apps/platform/src/routes/api/auth/$.ts +23 -2
  42. package/template/apps/platform/src/routes/api/rpc.$.ts +18 -0
  43. package/template/package.json +23 -18
  44. package/template/packages/auth/package.json +2 -2
  45. package/template/packages/db/package.json +1 -1
  46. package/template/packages/orpc/package.json +11 -4
  47. package/template/packages/orpc/src/lib/planets.ts +18 -18
  48. package/template/packages/orpc/src/lib/router.ts +3 -3
  49. package/template/packages/orpc/src/react/context.tsx +23 -0
  50. package/template/packages/orpc/src/react/general.ts +29 -0
  51. package/template/packages/orpc/src/react/index.ts +3 -0
  52. package/template/packages/orpc/src/react/planets.ts +90 -0
  53. package/template/packages/ui/package.json +3 -2
  54. package/template/pnpm-workspace.yaml +3 -0
  55. package/template/tsconfig.json +2 -1
  56. package/template/apps/mobile/app/modal.tsx +0 -29
  57. package/template/packages/orpc/node_modules/@types/node/LICENSE +0 -21
  58. package/template/packages/orpc/node_modules/@types/node/README.md +0 -15
  59. package/template/packages/orpc/node_modules/@types/node/assert/strict.d.ts +0 -111
  60. package/template/packages/orpc/node_modules/@types/node/assert.d.ts +0 -1078
  61. package/template/packages/orpc/node_modules/@types/node/async_hooks.d.ts +0 -603
  62. package/template/packages/orpc/node_modules/@types/node/buffer.buffer.d.ts +0 -472
  63. package/template/packages/orpc/node_modules/@types/node/buffer.d.ts +0 -1934
  64. package/template/packages/orpc/node_modules/@types/node/child_process.d.ts +0 -1476
  65. package/template/packages/orpc/node_modules/@types/node/cluster.d.ts +0 -578
  66. package/template/packages/orpc/node_modules/@types/node/compatibility/disposable.d.ts +0 -14
  67. package/template/packages/orpc/node_modules/@types/node/compatibility/index.d.ts +0 -9
  68. package/template/packages/orpc/node_modules/@types/node/compatibility/indexable.d.ts +0 -20
  69. package/template/packages/orpc/node_modules/@types/node/compatibility/iterators.d.ts +0 -20
  70. package/template/packages/orpc/node_modules/@types/node/console.d.ts +0 -452
  71. package/template/packages/orpc/node_modules/@types/node/constants.d.ts +0 -21
  72. package/template/packages/orpc/node_modules/@types/node/crypto.d.ts +0 -4545
  73. package/template/packages/orpc/node_modules/@types/node/dgram.d.ts +0 -600
  74. package/template/packages/orpc/node_modules/@types/node/diagnostics_channel.d.ts +0 -578
  75. package/template/packages/orpc/node_modules/@types/node/dns/promises.d.ts +0 -503
  76. package/template/packages/orpc/node_modules/@types/node/dns.d.ts +0 -923
  77. package/template/packages/orpc/node_modules/@types/node/domain.d.ts +0 -170
  78. package/template/packages/orpc/node_modules/@types/node/events.d.ts +0 -976
  79. package/template/packages/orpc/node_modules/@types/node/fs/promises.d.ts +0 -1295
  80. package/template/packages/orpc/node_modules/@types/node/fs.d.ts +0 -4461
  81. package/template/packages/orpc/node_modules/@types/node/globals.d.ts +0 -172
  82. package/template/packages/orpc/node_modules/@types/node/globals.typedarray.d.ts +0 -38
  83. package/template/packages/orpc/node_modules/@types/node/http.d.ts +0 -2089
  84. package/template/packages/orpc/node_modules/@types/node/http2.d.ts +0 -2644
  85. package/template/packages/orpc/node_modules/@types/node/https.d.ts +0 -579
  86. package/template/packages/orpc/node_modules/@types/node/index.d.ts +0 -97
  87. package/template/packages/orpc/node_modules/@types/node/inspector.d.ts +0 -253
  88. package/template/packages/orpc/node_modules/@types/node/inspector.generated.d.ts +0 -4052
  89. package/template/packages/orpc/node_modules/@types/node/module.d.ts +0 -891
  90. package/template/packages/orpc/node_modules/@types/node/net.d.ts +0 -1076
  91. package/template/packages/orpc/node_modules/@types/node/os.d.ts +0 -506
  92. package/template/packages/orpc/node_modules/@types/node/package.json +0 -145
  93. package/template/packages/orpc/node_modules/@types/node/path.d.ts +0 -200
  94. package/template/packages/orpc/node_modules/@types/node/perf_hooks.d.ts +0 -968
  95. package/template/packages/orpc/node_modules/@types/node/process.d.ts +0 -2084
  96. package/template/packages/orpc/node_modules/@types/node/punycode.d.ts +0 -117
  97. package/template/packages/orpc/node_modules/@types/node/querystring.d.ts +0 -152
  98. package/template/packages/orpc/node_modules/@types/node/readline/promises.d.ts +0 -161
  99. package/template/packages/orpc/node_modules/@types/node/readline.d.ts +0 -594
  100. package/template/packages/orpc/node_modules/@types/node/repl.d.ts +0 -428
  101. package/template/packages/orpc/node_modules/@types/node/sea.d.ts +0 -153
  102. package/template/packages/orpc/node_modules/@types/node/sqlite.d.ts +0 -721
  103. package/template/packages/orpc/node_modules/@types/node/stream/consumers.d.ts +0 -38
  104. package/template/packages/orpc/node_modules/@types/node/stream/promises.d.ts +0 -90
  105. package/template/packages/orpc/node_modules/@types/node/stream/web.d.ts +0 -622
  106. package/template/packages/orpc/node_modules/@types/node/stream.d.ts +0 -1687
  107. package/template/packages/orpc/node_modules/@types/node/string_decoder.d.ts +0 -67
  108. package/template/packages/orpc/node_modules/@types/node/test.d.ts +0 -2163
  109. package/template/packages/orpc/node_modules/@types/node/timers/promises.d.ts +0 -108
  110. package/template/packages/orpc/node_modules/@types/node/timers.d.ts +0 -287
  111. package/template/packages/orpc/node_modules/@types/node/tls.d.ts +0 -1319
  112. package/template/packages/orpc/node_modules/@types/node/trace_events.d.ts +0 -197
  113. package/template/packages/orpc/node_modules/@types/node/ts5.6/buffer.buffer.d.ts +0 -468
  114. package/template/packages/orpc/node_modules/@types/node/ts5.6/globals.typedarray.d.ts +0 -34
  115. package/template/packages/orpc/node_modules/@types/node/ts5.6/index.d.ts +0 -97
  116. package/template/packages/orpc/node_modules/@types/node/tty.d.ts +0 -208
  117. package/template/packages/orpc/node_modules/@types/node/url.d.ts +0 -984
  118. package/template/packages/orpc/node_modules/@types/node/util.d.ts +0 -2606
  119. package/template/packages/orpc/node_modules/@types/node/v8.d.ts +0 -920
  120. package/template/packages/orpc/node_modules/@types/node/vm.d.ts +0 -1000
  121. package/template/packages/orpc/node_modules/@types/node/wasi.d.ts +0 -181
  122. package/template/packages/orpc/node_modules/@types/node/web-globals/abortcontroller.d.ts +0 -34
  123. package/template/packages/orpc/node_modules/@types/node/web-globals/domexception.d.ts +0 -68
  124. package/template/packages/orpc/node_modules/@types/node/web-globals/events.d.ts +0 -97
  125. package/template/packages/orpc/node_modules/@types/node/web-globals/fetch.d.ts +0 -55
  126. package/template/packages/orpc/node_modules/@types/node/web-globals/navigator.d.ts +0 -22
  127. package/template/packages/orpc/node_modules/@types/node/web-globals/storage.d.ts +0 -24
  128. package/template/packages/orpc/node_modules/@types/node/worker_threads.d.ts +0 -784
  129. package/template/packages/orpc/node_modules/@types/node/zlib.d.ts +0 -747
  130. package/template/packages/orpc/node_modules/undici-types/LICENSE +0 -21
  131. package/template/packages/orpc/node_modules/undici-types/README.md +0 -6
  132. package/template/packages/orpc/node_modules/undici-types/agent.d.ts +0 -31
  133. package/template/packages/orpc/node_modules/undici-types/api.d.ts +0 -43
  134. package/template/packages/orpc/node_modules/undici-types/balanced-pool.d.ts +0 -29
  135. package/template/packages/orpc/node_modules/undici-types/cache.d.ts +0 -36
  136. package/template/packages/orpc/node_modules/undici-types/client.d.ts +0 -108
  137. package/template/packages/orpc/node_modules/undici-types/connector.d.ts +0 -34
  138. package/template/packages/orpc/node_modules/undici-types/content-type.d.ts +0 -21
  139. package/template/packages/orpc/node_modules/undici-types/cookies.d.ts +0 -28
  140. package/template/packages/orpc/node_modules/undici-types/diagnostics-channel.d.ts +0 -66
  141. package/template/packages/orpc/node_modules/undici-types/dispatcher.d.ts +0 -256
  142. package/template/packages/orpc/node_modules/undici-types/env-http-proxy-agent.d.ts +0 -21
  143. package/template/packages/orpc/node_modules/undici-types/errors.d.ts +0 -149
  144. package/template/packages/orpc/node_modules/undici-types/eventsource.d.ts +0 -61
  145. package/template/packages/orpc/node_modules/undici-types/fetch.d.ts +0 -209
  146. package/template/packages/orpc/node_modules/undici-types/file.d.ts +0 -39
  147. package/template/packages/orpc/node_modules/undici-types/filereader.d.ts +0 -54
  148. package/template/packages/orpc/node_modules/undici-types/formdata.d.ts +0 -108
  149. package/template/packages/orpc/node_modules/undici-types/global-dispatcher.d.ts +0 -9
  150. package/template/packages/orpc/node_modules/undici-types/global-origin.d.ts +0 -7
  151. package/template/packages/orpc/node_modules/undici-types/handlers.d.ts +0 -15
  152. package/template/packages/orpc/node_modules/undici-types/header.d.ts +0 -4
  153. package/template/packages/orpc/node_modules/undici-types/index.d.ts +0 -71
  154. package/template/packages/orpc/node_modules/undici-types/interceptors.d.ts +0 -17
  155. package/template/packages/orpc/node_modules/undici-types/mock-agent.d.ts +0 -50
  156. package/template/packages/orpc/node_modules/undici-types/mock-client.d.ts +0 -25
  157. package/template/packages/orpc/node_modules/undici-types/mock-errors.d.ts +0 -12
  158. package/template/packages/orpc/node_modules/undici-types/mock-interceptor.d.ts +0 -93
  159. package/template/packages/orpc/node_modules/undici-types/mock-pool.d.ts +0 -25
  160. package/template/packages/orpc/node_modules/undici-types/package.json +0 -55
  161. package/template/packages/orpc/node_modules/undici-types/patch.d.ts +0 -33
  162. package/template/packages/orpc/node_modules/undici-types/pool-stats.d.ts +0 -19
  163. package/template/packages/orpc/node_modules/undici-types/pool.d.ts +0 -39
  164. package/template/packages/orpc/node_modules/undici-types/proxy-agent.d.ts +0 -28
  165. package/template/packages/orpc/node_modules/undici-types/readable.d.ts +0 -65
  166. package/template/packages/orpc/node_modules/undici-types/retry-agent.d.ts +0 -8
  167. package/template/packages/orpc/node_modules/undici-types/retry-handler.d.ts +0 -116
  168. package/template/packages/orpc/node_modules/undici-types/util.d.ts +0 -18
  169. package/template/packages/orpc/node_modules/undici-types/webidl.d.ts +0 -228
  170. package/template/packages/orpc/node_modules/undici-types/websocket.d.ts +0 -150
@@ -0,0 +1,86 @@
1
+ import React from "react";
2
+ import { TouchableOpacity, Text, StyleSheet, ActivityIndicator, ViewStyle, StyleProp } from "react-native";
3
+
4
+ interface ButtonProps {
5
+ onPress: () => void;
6
+ children: React.ReactNode;
7
+ disabled?: boolean;
8
+ loading?: boolean;
9
+ variant?: "primary" | "secondary" | "outline" | "destructive";
10
+ style?: StyleProp<ViewStyle>;
11
+ }
12
+
13
+ export function Button({
14
+ onPress,
15
+ children,
16
+ disabled,
17
+ loading,
18
+ variant = "primary",
19
+ style
20
+ }: ButtonProps) {
21
+ const containerStyle = [
22
+ styles.button,
23
+ styles[variant],
24
+ disabled && styles.disabled,
25
+ style,
26
+ ];
27
+
28
+ const textStyle = [
29
+ styles.text,
30
+ variant === "outline" && styles.outlineText,
31
+ disabled && styles.disabledText,
32
+ ];
33
+
34
+ return (
35
+ <TouchableOpacity
36
+ style={containerStyle}
37
+ onPress={onPress}
38
+ disabled={disabled || loading}
39
+ >
40
+ {loading ? (
41
+ <ActivityIndicator color={variant === "outline" ? "#000" : "#fff"} />
42
+ ) : (
43
+ <Text style={textStyle}>{children}</Text>
44
+ )}
45
+ </TouchableOpacity>
46
+ );
47
+ }
48
+
49
+ const styles = StyleSheet.create({
50
+ button: {
51
+ height: 48,
52
+ borderRadius: 8,
53
+ justifyContent: "center",
54
+ alignItems: "center",
55
+ paddingHorizontal: 16,
56
+ },
57
+ primary: {
58
+ backgroundColor: "#000",
59
+ },
60
+ secondary: {
61
+ backgroundColor: "#f0f0f0",
62
+ },
63
+ outline: {
64
+ backgroundColor: "transparent",
65
+ borderWidth: 1,
66
+ borderColor: "#e0e0e0",
67
+ },
68
+ destructive: {
69
+ backgroundColor: "#ef4444",
70
+ },
71
+ disabled: {
72
+ backgroundColor: "#ccc",
73
+ borderColor: "#ccc",
74
+ },
75
+ text: {
76
+ color: "#fff",
77
+ fontSize: 16,
78
+ fontWeight: "600",
79
+ },
80
+ outlineText: {
81
+ color: "#000",
82
+ },
83
+ disabledText: {
84
+ color: "#999",
85
+ },
86
+ });
@@ -0,0 +1,56 @@
1
+ import React from "react";
2
+ import { TextInput, StyleSheet, TextInputProps, View, Text } from "react-native";
3
+
4
+ interface InputProps extends TextInputProps {
5
+ label?: string;
6
+ error?: string;
7
+ }
8
+
9
+ export function Input({ label, error, style, ...props }: InputProps) {
10
+ return (
11
+ <View style={styles.container}>
12
+ {label && <Text style={styles.label}>{label}</Text>}
13
+ <TextInput
14
+ style={[
15
+ styles.input,
16
+ error && styles.inputError,
17
+ style,
18
+ ]}
19
+ placeholderTextColor="#999"
20
+ {...props}
21
+ />
22
+ {error && <Text style={styles.errorText}>{error}</Text>}
23
+ </View>
24
+ );
25
+ }
26
+
27
+ const styles = StyleSheet.create({
28
+ container: {
29
+ width: "100%",
30
+ marginBottom: 16,
31
+ },
32
+ label: {
33
+ fontSize: 14,
34
+ fontWeight: "500",
35
+ marginBottom: 8,
36
+ color: "#333",
37
+ },
38
+ input: {
39
+ height: 48,
40
+ borderWidth: 1,
41
+ borderColor: "#e0e0e0",
42
+ borderRadius: 8,
43
+ paddingHorizontal: 12,
44
+ fontSize: 16,
45
+ color: "#000",
46
+ backgroundColor: "#fff",
47
+ },
48
+ inputError: {
49
+ borderColor: "#ff4444",
50
+ },
51
+ errorText: {
52
+ color: "#ff4444",
53
+ fontSize: 12,
54
+ marginTop: 4,
55
+ },
56
+ });
@@ -3,7 +3,7 @@ import { expoClient } from "@better-auth/expo/client";
3
3
  import * as SecureStore from "expo-secure-store";
4
4
 
5
5
  export const authClient = createAuthClient({
6
- baseURL: process.env.EXPO_PUBLIC_BETTER_AUTH_URL || "http://localhost:3000",
6
+ baseURL: process.env.EXPO_PUBLIC_BETTER_AUTH_URL || "https://platform.local",
7
7
  plugins: [
8
8
  expoClient({
9
9
  scheme: "mobile",
@@ -0,0 +1,28 @@
1
+ import { createORPCClient } from '@orpc/client'
2
+ import { RPCLink } from '@orpc/client/fetch'
3
+ import { RouterClient } from '@orpc/server'
4
+ import { router } from '@workspace/orpc/router'
5
+ import { authClient } from './auth-client'
6
+
7
+ export const link = new RPCLink({
8
+ url: `https://platform.local/api/rpc`,
9
+ async fetch(request, init) {
10
+ const { fetch } = await import('expo/fetch')
11
+ const headers = new Map<string, string>();
12
+ const cookies = authClient.getCookie();
13
+ if (cookies) {
14
+ headers.set("Cookie", cookies);
15
+ }
16
+
17
+ const resp = await fetch(request.url, {
18
+ body: request.body,
19
+ headers: Object.fromEntries(headers),
20
+ method: request.method,
21
+ ...init,
22
+ })
23
+
24
+ return resp
25
+ },
26
+ })
27
+
28
+ export const orpc: RouterClient<typeof router> = createORPCClient(link)
@@ -15,9 +15,16 @@
15
15
  "dependencies": {
16
16
  "@better-auth/expo": "^1.6.9",
17
17
  "@expo/vector-icons": "^15.0.3",
18
+ "@orpc/client": "^1.14.0",
19
+ "@orpc/server": "^1.14.0",
20
+ "@orpc/tanstack-query": "^1.14.0",
18
21
  "@react-navigation/bottom-tabs": "^7.4.0",
19
22
  "@react-navigation/elements": "^2.6.3",
20
23
  "@react-navigation/native": "^7.1.8",
24
+ "@tanstack/react-form": "^1.29.1",
25
+ "@tanstack/react-query": "^5.100.5",
26
+ "@workspace/orpc": "workspace:*",
27
+ "@workspace/ui": "workspace:*",
21
28
  "better-auth": "^1.6.9",
22
29
  "expo": "^55.0.17",
23
30
  "expo-constants": "~55.0.15",
@@ -33,12 +40,18 @@
33
40
  "expo-symbols": "~55.0.7",
34
41
  "expo-system-ui": "~55.0.16",
35
42
  "expo-web-browser": "~55.0.14",
36
- "react-native": "^0.84.0",
43
+ "react": "19.2.5",
44
+ "react-dom": "19.2.5",
45
+ "react-native": "0.83.6",
37
46
  "react-native-gesture-handler": "~2.30.0",
38
47
  "react-native-reanimated": "4.2.1",
39
48
  "react-native-safe-area-context": "~5.6.0",
40
49
  "react-native-screens": "~4.23.0",
41
50
  "react-native-web": "~0.21.0",
42
51
  "react-native-worklets": "0.7.4"
52
+ },
53
+ "devDependencies": {
54
+ "babel-plugin-transform-import-meta": "^2.3.3",
55
+ "metro-react-native-babel-transformer": "^0.77.0"
43
56
  }
44
57
  }
File without changes
@@ -1,5 +1,8 @@
1
1
  {
2
- "extends": "expo/tsconfig.base",
2
+ "extends": [
3
+ "expo/tsconfig.base",
4
+ "@workspace/config-typescript/react.json"
5
+ ],
3
6
  "compilerOptions": {
4
7
  "strict": true,
5
8
  "paths": {
@@ -1,4 +1,4 @@
1
1
  BETTER_AUTH_SECRET=your-super-secret-key-change-this-in-production
2
- BETTER_AUTH_URL=http://localhost:3000
2
+ BETTER_AUTH_URL=https://platform.local
3
3
 
4
4
  DATABASE_URL=postgresql://postgres:postgres@localhost:5432/auth
@@ -4,7 +4,7 @@
4
4
  "private": true,
5
5
  "type": "module",
6
6
  "scripts": {
7
- "dev": "vite dev --port 3000",
7
+ "dev": "portless platform vite dev",
8
8
  "build": "vite build",
9
9
  "preview": "vite preview",
10
10
  "typecheck": "tsc --noEmit",
@@ -22,19 +22,23 @@
22
22
  "@tanstack/react-router": "^1.168.24",
23
23
  "@tanstack/react-start": "^1.167.49",
24
24
  "@tanstack/router-plugin": "^1.167.27",
25
- "@workspace/auth": "*",
26
- "@workspace/orpc": "*",
27
- "@workspace/ui": "*",
25
+ "@workspace/auth": "workspace:*",
26
+ "@workspace/orpc": "workspace:*",
27
+ "@workspace/ui": "workspace:*",
28
28
  "better-auth": "^1.6.9",
29
29
  "lucide-react": "^1.11.0",
30
30
  "nitro": "latest",
31
+ "sonner": "^2.0.7",
31
32
  "tailwindcss": "^4.2.4",
32
- "vite-tsconfig-paths": "^6.1.1"
33
+ "vite-tsconfig-paths": "^6.1.1",
34
+ "zod": "4.3.6"
33
35
  },
34
36
  "devDependencies": {
35
37
  "@types/node": "^25.6.0",
36
38
  "@vitejs/plugin-react": "^6.0.1",
37
- "@workspace/config-typescript": "*",
39
+ "@workspace/config-typescript": "workspace:*",
40
+ "dotenv": "^17.4.2",
41
+ "drizzle-kit": "^0.31.10",
38
42
  "vite": "^8.0.10"
39
43
  }
40
44
  }
@@ -0,0 +1,3 @@
1
+ {
2
+ "name": "platform"
3
+ }
@@ -18,12 +18,13 @@ import { Input } from "@workspace/ui/components/input";
18
18
  import { useState } from "react";
19
19
  import { Link } from "@tanstack/react-router";
20
20
  import { useForm } from "@tanstack/react-form";
21
- import { type } from "arktype";
21
+ import { z } from "zod";
22
+
22
23
  import { authClient } from "@/lib/auth-client";
23
24
 
24
- const loginSchema = type({
25
- email: "string.email",
26
- password: "string>0",
25
+ const loginSchema = z.object({
26
+ email: z.string().email("Invalid email address"),
27
+ password: z.string().min(8, "Password must be at least 8 characters"),
27
28
  });
28
29
 
29
30
  export function LoginForm({ className, ...props }: React.ComponentProps<"div">) {
@@ -17,24 +17,20 @@ import { Input } from "@workspace/ui/components/input";
17
17
  import { useState } from "react";
18
18
  import { Link } from "@tanstack/react-router";
19
19
  import { useForm } from "@tanstack/react-form";
20
- import { type } from "arktype";
20
+ import { z } from "zod";
21
21
  import { authClient } from "@/lib/auth-client";
22
22
 
23
- const signupSchema = type({
24
- name: "string>0",
25
- email: "string.email",
26
- password: "string>=8",
27
- confirmPassword: "string>0",
28
- }).narrow((data, ctx) => {
29
- if (data.password !== data.confirmPassword) {
30
- ctx.error({
31
- message: "Passwords do not match",
32
- path: ["confirmPassword"],
33
- });
34
- return false;
35
- }
36
- return true;
37
- });
23
+ const signupSchema = z
24
+ .object({
25
+ name: z.string().min(1, "Name is required"),
26
+ email: z.string().email("Invalid email address"),
27
+ password: z.string().min(8, "Password must be at least 8 characters"),
28
+ confirmPassword: z.string().min(1, "Confirm password is required"),
29
+ })
30
+ .refine((data) => data.password === data.confirmPassword, {
31
+ message: "Passwords do not match",
32
+ path: ["confirmPassword"],
33
+ });
38
34
 
39
35
  export function SignupForm({ ...props }: React.ComponentProps<typeof Card>) {
40
36
  const [loading, setLoading] = useState(false);
@@ -4,7 +4,6 @@ import { routeTree } from "./routeTree.gen";
4
4
  export function getRouter() {
5
5
  const router = createTanStackRouter({
6
6
  routeTree,
7
-
8
7
  scrollRestoration: true,
9
8
  defaultPreload: "intent",
10
9
  defaultPreloadStaleTime: 0,
@@ -2,6 +2,8 @@ import { HeadContent, Scripts, createRootRoute } from "@tanstack/react-router";
2
2
  import { QueryClient, QueryClientProvider } from "@tanstack/react-query";
3
3
  import { Toaster } from "@workspace/ui/components/sonner";
4
4
  import { ThemeProvider } from "@workspace/ui/components/theme-provider";
5
+ import { ORPCProvider } from "@workspace/orpc/react";
6
+ import { orpc } from "@/lib/orpc";
5
7
 
6
8
  import appCss from "@workspace/ui/globals.css?url";
7
9
 
@@ -40,8 +42,10 @@ function RootDocument({ children }: { children: React.ReactNode }) {
40
42
  <body>
41
43
  <ThemeProvider defaultTheme="system" storageKey="theme">
42
44
  <QueryClientProvider client={queryClient}>
43
- {children}
44
- <Toaster />
45
+ <ORPCProvider client={orpc}>
46
+ {children}
47
+ <Toaster />
48
+ </ORPCProvider>
45
49
  </QueryClientProvider>
46
50
  </ThemeProvider>
47
51
  <Scripts />
@@ -1,9 +1,9 @@
1
1
  import * as React from "react";
2
2
  import { createFileRoute, redirect } from "@tanstack/react-router";
3
3
  import { useForm } from "@tanstack/react-form";
4
+ import { z } from "zod";
4
5
  import { toast } from "sonner";
5
6
  import { Loader2, User } from "lucide-react";
6
- import { type } from "arktype";
7
7
 
8
8
  import { Button } from "@workspace/ui/components/button";
9
9
  import { Input } from "@workspace/ui/components/input";
@@ -22,24 +22,20 @@ import { Separator } from "@workspace/ui/components/separator";
22
22
  import { authClient } from "@/lib/auth-client";
23
23
  import { getSessionFn } from "@/lib/auth-utils";
24
24
 
25
- const profileSchema = type({
26
- name: "string>0",
25
+ const profileSchema = z.object({
26
+ name: z.string().min(1, "Name is required"),
27
27
  });
28
28
 
29
- const passwordSchema = type({
30
- currentPassword: "string>0",
31
- newPassword: "string>=8",
32
- confirmPassword: "string>0",
33
- }).narrow((data, ctx) => {
34
- if (data.newPassword !== data.confirmPassword) {
35
- ctx.error({
36
- message: "Passwords do not match",
37
- path: ["confirmPassword"],
38
- });
39
- return false;
40
- }
41
- return true;
42
- });
29
+ const passwordSchema = z
30
+ .object({
31
+ currentPassword: z.string().min(1, "Current password is required"),
32
+ newPassword: z.string().min(8, "New password must be at least 8 characters"),
33
+ confirmPassword: z.string().min(1, "Confirm password is required"),
34
+ })
35
+ .refine((data) => data.newPassword === data.confirmPassword, {
36
+ message: "Passwords do not match",
37
+ path: ["confirmPassword"],
38
+ });
43
39
 
44
40
  export const Route = createFileRoute("/_auth/account")({
45
41
  beforeLoad: async () => {
@@ -1,7 +1,6 @@
1
1
  import { createFileRoute, redirect } from "@tanstack/react-router";
2
- import { useQuery } from "@tanstack/react-query";
3
2
  import { getSessionFn } from "@/lib/auth-utils";
4
- import { orpc } from "@/lib/orpc";
3
+ import { useSecretData } from "@workspace/orpc/react";
5
4
 
6
5
  export const Route = createFileRoute("/_auth/examples/client-orpc-auth")({
7
6
  beforeLoad: async () => {
@@ -22,10 +21,7 @@ export const Route = createFileRoute("/_auth/examples/client-orpc-auth")({
22
21
  function ClientORPCAuth() {
23
22
  const { session } = Route.useRouteContext();
24
23
 
25
- const { data, isLoading } = useQuery({
26
- queryKey: ["secret-data"],
27
- queryFn: () => orpc.getSecretData(),
28
- });
24
+ const { data, isLoading } = useSecretData();
29
25
 
30
26
  return (
31
27
  <div className="flex flex-col gap-4">
@@ -2,9 +2,9 @@ import * as React from "react";
2
2
  import { createFileRoute } from "@tanstack/react-router";
3
3
  import { Check, Pencil, Plus, Trash2 } from "lucide-react";
4
4
  import { toast } from "sonner";
5
- import { useMutation, useQuery, useQueryClient } from "@tanstack/react-query";
5
+ import { useQueryClient } from "@tanstack/react-query";
6
6
  import { useForm } from "@tanstack/react-form";
7
- import { type } from "arktype";
7
+ import { z } from "zod";
8
8
 
9
9
  import { Button } from "@workspace/ui/components/button";
10
10
  import { Input } from "@workspace/ui/components/input";
@@ -12,25 +12,21 @@ import { Field, FieldError, FieldLabel } from "@workspace/ui/components/field";
12
12
 
13
13
  import type { router } from "@workspace/orpc/router";
14
14
  import type { InferRouterInputs, InferRouterOutputs } from "@orpc/server";
15
- import { orpc } from "@/lib/orpc";
15
+ import { usePlanets, useCreatePlanet, useUpdatePlanet, useDeletePlanet } from "@workspace/orpc/react";
16
16
 
17
17
  type Inputs = InferRouterInputs<typeof router>;
18
18
  type Outputs = InferRouterOutputs<typeof router>;
19
19
  type Planet = Outputs["planets"]["getPlanets"][number];
20
20
 
21
- const planetSchema = type({
22
- name: "string>0",
23
- description: "string",
24
- distance: "string",
25
- diameter: "string",
26
- }).narrow((data, ctx) => {
27
- if (isNaN(parseFloat(data.distance))) {
28
- ctx.error({ message: "Must be a number", path: ["distance"] });
29
- }
30
- if (isNaN(parseFloat(data.diameter))) {
31
- ctx.error({ message: "Must be a number", path: ["diameter"] });
32
- }
33
- return true;
21
+ const planetSchema = z.object({
22
+ name: z.string().min(1),
23
+ description: z.string(),
24
+ distance: z.string().refine((val) => !isNaN(parseFloat(val)), {
25
+ message: "Must be a number",
26
+ }),
27
+ diameter: z.string().refine((val) => !isNaN(parseFloat(val)), {
28
+ message: "Must be a number",
29
+ }),
34
30
  });
35
31
 
36
32
  export const Route = createFileRoute("/_public/examples/client-orpc")({
@@ -52,10 +48,7 @@ function ClientORPC() {
52
48
  const queryClient = useQueryClient();
53
49
  const [editingId, setEditingId] = React.useState<number | null>(null);
54
50
 
55
- const { data: planets = [], isLoading } = useQuery({
56
- queryKey: ["planets"],
57
- queryFn: () => orpc.planets.getPlanets(),
58
- });
51
+ const { data: planets = [], isLoading } = usePlanets();
59
52
 
60
53
  const form = useForm({
61
54
  defaultValues: {
@@ -94,10 +87,8 @@ function ClientORPC() {
94
87
  setEditingId(null);
95
88
  };
96
89
 
97
- const createMutation = useMutation({
98
- mutationFn: (input: Inputs["planets"]["createPlanet"]) => orpc.planets.createPlanet(input),
90
+ const createMutation = useCreatePlanet({
99
91
  onSuccess: () => {
100
- queryClient.invalidateQueries({ queryKey: ["planets"] });
101
92
  resetForm();
102
93
  toast.success("Planet added successfully");
103
94
  },
@@ -106,10 +97,8 @@ function ClientORPC() {
106
97
  },
107
98
  });
108
99
 
109
- const updateMutation = useMutation({
110
- mutationFn: (input: Inputs["planets"]["updatePlanet"]) => orpc.planets.updatePlanet(input),
100
+ const updateMutation = useUpdatePlanet({
111
101
  onSuccess: () => {
112
- queryClient.invalidateQueries({ queryKey: ["planets"] });
113
102
  resetForm();
114
103
  toast.success("Planet updated successfully");
115
104
  },
@@ -118,10 +107,8 @@ function ClientORPC() {
118
107
  },
119
108
  });
120
109
 
121
- const deleteMutation = useMutation({
122
- mutationFn: (input: Inputs["planets"]["deletePlanet"]) => orpc.planets.deletePlanet(input),
110
+ const deleteMutation = useDeletePlanet({
123
111
  onSuccess: () => {
124
- queryClient.invalidateQueries({ queryKey: ["planets"] });
125
112
  toast.success("Planet deleted successfully");
126
113
  },
127
114
  onError: (err) => {
@@ -4,7 +4,7 @@ import { createServerFn } from "@tanstack/react-start";
4
4
  import { Check, Pencil, Plus, Trash2 } from "lucide-react";
5
5
  import { toast } from "sonner";
6
6
  import { useForm } from "@tanstack/react-form";
7
- import { type } from "arktype";
7
+ import { z } from "zod";
8
8
 
9
9
  import { Button } from "@workspace/ui/components/button";
10
10
  import { Input } from "@workspace/ui/components/input";
@@ -17,19 +17,15 @@ import { orpc } from "@/lib/orpc";
17
17
  type Outputs = InferRouterOutputs<typeof router>;
18
18
  type Planet = Outputs["planets"]["getPlanets"][number];
19
19
 
20
- const planetSchema = type({
21
- name: "string>0",
22
- description: "string",
23
- distance: "string",
24
- diameter: "string",
25
- }).narrow((data, ctx) => {
26
- if (isNaN(parseFloat(data.distance))) {
27
- ctx.error({ message: "Must be a number", path: ["distance"] });
28
- }
29
- if (isNaN(parseFloat(data.diameter))) {
30
- ctx.error({ message: "Must be a number", path: ["diameter"] });
31
- }
32
- return true;
20
+ const planetSchema = z.object({
21
+ name: z.string().min(1),
22
+ description: z.string(),
23
+ distance: z.string().refine((val) => !isNaN(parseFloat(val)), {
24
+ message: "Must be a number",
25
+ }),
26
+ diameter: z.string().refine((val) => !isNaN(parseFloat(val)), {
27
+ message: "Must be a number",
28
+ }),
33
29
  });
34
30
 
35
31
  const getPlanets = createServerFn({ method: "GET" }).handler(async () => {
@@ -1,14 +1,35 @@
1
1
  import { auth } from "@workspace/auth/lib/auth";
2
2
  import { createFileRoute } from "@tanstack/react-router";
3
3
 
4
+ const CORS_HEADERS = {
5
+ "Access-Control-Allow-Origin": "http://localhost:8081",
6
+ "Access-Control-Allow-Methods": "GET, POST, PUT, DELETE, OPTIONS",
7
+ "Access-Control-Allow-Headers": "Content-Type, Authorization",
8
+ "Access-Control-Allow-Credentials": "true",
9
+ };
10
+
4
11
  export const Route = createFileRoute("/api/auth/$")({
5
12
  server: {
6
13
  handlers: {
7
14
  GET: async ({ request }: { request: Request }) => {
8
- return await auth.handler(request);
15
+ const response = await auth.handler(request);
16
+ Object.entries(CORS_HEADERS).forEach(([key, value]) => {
17
+ response.headers.set(key, value);
18
+ });
19
+ return response;
9
20
  },
10
21
  POST: async ({ request }: { request: Request }) => {
11
- return await auth.handler(request);
22
+ const response = await auth.handler(request);
23
+ Object.entries(CORS_HEADERS).forEach(([key, value]) => {
24
+ response.headers.set(key, value);
25
+ });
26
+ return response;
27
+ },
28
+ OPTIONS: async () => {
29
+ return new Response(null, {
30
+ status: 204,
31
+ headers: CORS_HEADERS,
32
+ });
12
33
  },
13
34
  },
14
35
  },
@@ -27,8 +27,26 @@ export const Route = createFileRoute("/api/rpc/$")({
27
27
  },
28
28
  });
29
29
 
30
+ if (response) {
31
+ response.headers.set("Access-Control-Allow-Origin", "http://localhost:8081");
32
+ response.headers.set("Access-Control-Allow-Methods", "GET, POST, PUT, DELETE, OPTIONS");
33
+ response.headers.set("Access-Control-Allow-Headers", "Content-Type, Authorization");
34
+ response.headers.set("Access-Control-Allow-Credentials", "true");
35
+ }
36
+
30
37
  return response ?? new Response("Not Found", { status: 404 });
31
38
  },
39
+ OPTIONS: async () => {
40
+ return new Response(null, {
41
+ status: 204,
42
+ headers: {
43
+ "Access-Control-Allow-Origin": "http://localhost:8081",
44
+ "Access-Control-Allow-Methods": "GET, POST, PUT, DELETE, OPTIONS",
45
+ "Access-Control-Allow-Headers": "Content-Type, Authorization",
46
+ "Access-Control-Allow-Credentials": "true",
47
+ },
48
+ });
49
+ },
32
50
  },
33
51
  },
34
52
  });