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.
- package/dist/add.js +8 -8
- package/dist/index.js +8 -8
- package/package.json +10 -10
- package/template/.env.example +2 -2
- package/template/.github/dependabot.yml +15 -0
- package/template/.github/workflows/ci.yml +143 -0
- package/template/.github/workflows/dependabot-automerge.yml +23 -0
- package/template/.husky/pre-push +0 -0
- package/template/README.md +23 -23
- package/template/apps/desktop/.eslintcache +1 -0
- package/template/apps/desktop/README.md +5 -5
- package/template/apps/desktop/package.json +5 -5
- package/template/apps/mobile/README.md +3 -3
- package/template/apps/mobile/app/(tabs)/_layout.tsx +21 -14
- package/template/apps/mobile/app/(tabs)/account.tsx +147 -0
- package/template/apps/mobile/app/(tabs)/explore.tsx +335 -104
- package/template/apps/mobile/app/(tabs)/index.tsx +94 -96
- package/template/apps/mobile/app/_layout.tsx +26 -7
- package/template/apps/mobile/app/index.tsx +129 -0
- package/template/apps/mobile/app/login.tsx +135 -0
- package/template/apps/mobile/app/signup.tsx +144 -0
- package/template/apps/mobile/app.json +9 -4
- package/template/apps/mobile/components/ui/button.tsx +86 -0
- package/template/apps/mobile/components/ui/input.tsx +56 -0
- package/template/apps/mobile/lib/auth-client.ts +1 -1
- package/template/apps/mobile/lib/orpc.ts +28 -0
- package/template/apps/mobile/package.json +14 -1
- package/template/apps/mobile/scripts/reset-project.js +0 -0
- package/template/apps/mobile/tsconfig.json +4 -1
- package/template/apps/platform/.env.example +1 -1
- package/template/apps/platform/package.json +10 -6
- package/template/apps/platform/portless.json +3 -0
- package/template/apps/platform/src/components/login-form.tsx +5 -4
- package/template/apps/platform/src/components/signup-form.tsx +12 -16
- package/template/apps/platform/src/router.tsx +0 -1
- package/template/apps/platform/src/routes/__root.tsx +6 -2
- package/template/apps/platform/src/routes/_auth/account.tsx +13 -17
- package/template/apps/platform/src/routes/_auth/examples/client-orpc-auth.tsx +2 -6
- package/template/apps/platform/src/routes/_public/examples/client-orpc.tsx +16 -29
- package/template/apps/platform/src/routes/_public/examples/ssr-orpc.tsx +10 -14
- package/template/apps/platform/src/routes/api/auth/$.ts +23 -2
- package/template/apps/platform/src/routes/api/rpc.$.ts +18 -0
- package/template/package.json +23 -18
- package/template/packages/auth/package.json +2 -2
- package/template/packages/db/package.json +1 -1
- package/template/packages/orpc/package.json +11 -4
- package/template/packages/orpc/src/lib/planets.ts +18 -18
- package/template/packages/orpc/src/lib/router.ts +3 -3
- package/template/packages/orpc/src/react/context.tsx +23 -0
- package/template/packages/orpc/src/react/general.ts +29 -0
- package/template/packages/orpc/src/react/index.ts +3 -0
- package/template/packages/orpc/src/react/planets.ts +90 -0
- package/template/packages/ui/package.json +3 -2
- package/template/pnpm-workspace.yaml +3 -0
- package/template/tsconfig.json +2 -1
- package/template/apps/mobile/app/modal.tsx +0 -29
- package/template/packages/orpc/node_modules/@types/node/LICENSE +0 -21
- package/template/packages/orpc/node_modules/@types/node/README.md +0 -15
- package/template/packages/orpc/node_modules/@types/node/assert/strict.d.ts +0 -111
- package/template/packages/orpc/node_modules/@types/node/assert.d.ts +0 -1078
- package/template/packages/orpc/node_modules/@types/node/async_hooks.d.ts +0 -603
- package/template/packages/orpc/node_modules/@types/node/buffer.buffer.d.ts +0 -472
- package/template/packages/orpc/node_modules/@types/node/buffer.d.ts +0 -1934
- package/template/packages/orpc/node_modules/@types/node/child_process.d.ts +0 -1476
- package/template/packages/orpc/node_modules/@types/node/cluster.d.ts +0 -578
- package/template/packages/orpc/node_modules/@types/node/compatibility/disposable.d.ts +0 -14
- package/template/packages/orpc/node_modules/@types/node/compatibility/index.d.ts +0 -9
- package/template/packages/orpc/node_modules/@types/node/compatibility/indexable.d.ts +0 -20
- package/template/packages/orpc/node_modules/@types/node/compatibility/iterators.d.ts +0 -20
- package/template/packages/orpc/node_modules/@types/node/console.d.ts +0 -452
- package/template/packages/orpc/node_modules/@types/node/constants.d.ts +0 -21
- package/template/packages/orpc/node_modules/@types/node/crypto.d.ts +0 -4545
- package/template/packages/orpc/node_modules/@types/node/dgram.d.ts +0 -600
- package/template/packages/orpc/node_modules/@types/node/diagnostics_channel.d.ts +0 -578
- package/template/packages/orpc/node_modules/@types/node/dns/promises.d.ts +0 -503
- package/template/packages/orpc/node_modules/@types/node/dns.d.ts +0 -923
- package/template/packages/orpc/node_modules/@types/node/domain.d.ts +0 -170
- package/template/packages/orpc/node_modules/@types/node/events.d.ts +0 -976
- package/template/packages/orpc/node_modules/@types/node/fs/promises.d.ts +0 -1295
- package/template/packages/orpc/node_modules/@types/node/fs.d.ts +0 -4461
- package/template/packages/orpc/node_modules/@types/node/globals.d.ts +0 -172
- package/template/packages/orpc/node_modules/@types/node/globals.typedarray.d.ts +0 -38
- package/template/packages/orpc/node_modules/@types/node/http.d.ts +0 -2089
- package/template/packages/orpc/node_modules/@types/node/http2.d.ts +0 -2644
- package/template/packages/orpc/node_modules/@types/node/https.d.ts +0 -579
- package/template/packages/orpc/node_modules/@types/node/index.d.ts +0 -97
- package/template/packages/orpc/node_modules/@types/node/inspector.d.ts +0 -253
- package/template/packages/orpc/node_modules/@types/node/inspector.generated.d.ts +0 -4052
- package/template/packages/orpc/node_modules/@types/node/module.d.ts +0 -891
- package/template/packages/orpc/node_modules/@types/node/net.d.ts +0 -1076
- package/template/packages/orpc/node_modules/@types/node/os.d.ts +0 -506
- package/template/packages/orpc/node_modules/@types/node/package.json +0 -145
- package/template/packages/orpc/node_modules/@types/node/path.d.ts +0 -200
- package/template/packages/orpc/node_modules/@types/node/perf_hooks.d.ts +0 -968
- package/template/packages/orpc/node_modules/@types/node/process.d.ts +0 -2084
- package/template/packages/orpc/node_modules/@types/node/punycode.d.ts +0 -117
- package/template/packages/orpc/node_modules/@types/node/querystring.d.ts +0 -152
- package/template/packages/orpc/node_modules/@types/node/readline/promises.d.ts +0 -161
- package/template/packages/orpc/node_modules/@types/node/readline.d.ts +0 -594
- package/template/packages/orpc/node_modules/@types/node/repl.d.ts +0 -428
- package/template/packages/orpc/node_modules/@types/node/sea.d.ts +0 -153
- package/template/packages/orpc/node_modules/@types/node/sqlite.d.ts +0 -721
- package/template/packages/orpc/node_modules/@types/node/stream/consumers.d.ts +0 -38
- package/template/packages/orpc/node_modules/@types/node/stream/promises.d.ts +0 -90
- package/template/packages/orpc/node_modules/@types/node/stream/web.d.ts +0 -622
- package/template/packages/orpc/node_modules/@types/node/stream.d.ts +0 -1687
- package/template/packages/orpc/node_modules/@types/node/string_decoder.d.ts +0 -67
- package/template/packages/orpc/node_modules/@types/node/test.d.ts +0 -2163
- package/template/packages/orpc/node_modules/@types/node/timers/promises.d.ts +0 -108
- package/template/packages/orpc/node_modules/@types/node/timers.d.ts +0 -287
- package/template/packages/orpc/node_modules/@types/node/tls.d.ts +0 -1319
- package/template/packages/orpc/node_modules/@types/node/trace_events.d.ts +0 -197
- package/template/packages/orpc/node_modules/@types/node/ts5.6/buffer.buffer.d.ts +0 -468
- package/template/packages/orpc/node_modules/@types/node/ts5.6/globals.typedarray.d.ts +0 -34
- package/template/packages/orpc/node_modules/@types/node/ts5.6/index.d.ts +0 -97
- package/template/packages/orpc/node_modules/@types/node/tty.d.ts +0 -208
- package/template/packages/orpc/node_modules/@types/node/url.d.ts +0 -984
- package/template/packages/orpc/node_modules/@types/node/util.d.ts +0 -2606
- package/template/packages/orpc/node_modules/@types/node/v8.d.ts +0 -920
- package/template/packages/orpc/node_modules/@types/node/vm.d.ts +0 -1000
- package/template/packages/orpc/node_modules/@types/node/wasi.d.ts +0 -181
- package/template/packages/orpc/node_modules/@types/node/web-globals/abortcontroller.d.ts +0 -34
- package/template/packages/orpc/node_modules/@types/node/web-globals/domexception.d.ts +0 -68
- package/template/packages/orpc/node_modules/@types/node/web-globals/events.d.ts +0 -97
- package/template/packages/orpc/node_modules/@types/node/web-globals/fetch.d.ts +0 -55
- package/template/packages/orpc/node_modules/@types/node/web-globals/navigator.d.ts +0 -22
- package/template/packages/orpc/node_modules/@types/node/web-globals/storage.d.ts +0 -24
- package/template/packages/orpc/node_modules/@types/node/worker_threads.d.ts +0 -784
- package/template/packages/orpc/node_modules/@types/node/zlib.d.ts +0 -747
- package/template/packages/orpc/node_modules/undici-types/LICENSE +0 -21
- package/template/packages/orpc/node_modules/undici-types/README.md +0 -6
- package/template/packages/orpc/node_modules/undici-types/agent.d.ts +0 -31
- package/template/packages/orpc/node_modules/undici-types/api.d.ts +0 -43
- package/template/packages/orpc/node_modules/undici-types/balanced-pool.d.ts +0 -29
- package/template/packages/orpc/node_modules/undici-types/cache.d.ts +0 -36
- package/template/packages/orpc/node_modules/undici-types/client.d.ts +0 -108
- package/template/packages/orpc/node_modules/undici-types/connector.d.ts +0 -34
- package/template/packages/orpc/node_modules/undici-types/content-type.d.ts +0 -21
- package/template/packages/orpc/node_modules/undici-types/cookies.d.ts +0 -28
- package/template/packages/orpc/node_modules/undici-types/diagnostics-channel.d.ts +0 -66
- package/template/packages/orpc/node_modules/undici-types/dispatcher.d.ts +0 -256
- package/template/packages/orpc/node_modules/undici-types/env-http-proxy-agent.d.ts +0 -21
- package/template/packages/orpc/node_modules/undici-types/errors.d.ts +0 -149
- package/template/packages/orpc/node_modules/undici-types/eventsource.d.ts +0 -61
- package/template/packages/orpc/node_modules/undici-types/fetch.d.ts +0 -209
- package/template/packages/orpc/node_modules/undici-types/file.d.ts +0 -39
- package/template/packages/orpc/node_modules/undici-types/filereader.d.ts +0 -54
- package/template/packages/orpc/node_modules/undici-types/formdata.d.ts +0 -108
- package/template/packages/orpc/node_modules/undici-types/global-dispatcher.d.ts +0 -9
- package/template/packages/orpc/node_modules/undici-types/global-origin.d.ts +0 -7
- package/template/packages/orpc/node_modules/undici-types/handlers.d.ts +0 -15
- package/template/packages/orpc/node_modules/undici-types/header.d.ts +0 -4
- package/template/packages/orpc/node_modules/undici-types/index.d.ts +0 -71
- package/template/packages/orpc/node_modules/undici-types/interceptors.d.ts +0 -17
- package/template/packages/orpc/node_modules/undici-types/mock-agent.d.ts +0 -50
- package/template/packages/orpc/node_modules/undici-types/mock-client.d.ts +0 -25
- package/template/packages/orpc/node_modules/undici-types/mock-errors.d.ts +0 -12
- package/template/packages/orpc/node_modules/undici-types/mock-interceptor.d.ts +0 -93
- package/template/packages/orpc/node_modules/undici-types/mock-pool.d.ts +0 -25
- package/template/packages/orpc/node_modules/undici-types/package.json +0 -55
- package/template/packages/orpc/node_modules/undici-types/patch.d.ts +0 -33
- package/template/packages/orpc/node_modules/undici-types/pool-stats.d.ts +0 -19
- package/template/packages/orpc/node_modules/undici-types/pool.d.ts +0 -39
- package/template/packages/orpc/node_modules/undici-types/proxy-agent.d.ts +0 -28
- package/template/packages/orpc/node_modules/undici-types/readable.d.ts +0 -65
- package/template/packages/orpc/node_modules/undici-types/retry-agent.d.ts +0 -8
- package/template/packages/orpc/node_modules/undici-types/retry-handler.d.ts +0 -116
- package/template/packages/orpc/node_modules/undici-types/util.d.ts +0 -18
- package/template/packages/orpc/node_modules/undici-types/webidl.d.ts +0 -228
- 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 || "
|
|
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
|
|
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
|
|
@@ -4,7 +4,7 @@
|
|
|
4
4
|
"private": true,
|
|
5
5
|
"type": "module",
|
|
6
6
|
"scripts": {
|
|
7
|
-
"dev": "vite dev
|
|
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
|
}
|
|
@@ -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 {
|
|
21
|
+
import { z } from "zod";
|
|
22
|
+
|
|
22
23
|
import { authClient } from "@/lib/auth-client";
|
|
23
24
|
|
|
24
|
-
const loginSchema =
|
|
25
|
-
email:
|
|
26
|
-
password:
|
|
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 {
|
|
20
|
+
import { z } from "zod";
|
|
21
21
|
import { authClient } from "@/lib/auth-client";
|
|
22
22
|
|
|
23
|
-
const signupSchema =
|
|
24
|
-
|
|
25
|
-
|
|
26
|
-
|
|
27
|
-
|
|
28
|
-
|
|
29
|
-
|
|
30
|
-
|
|
31
|
-
|
|
32
|
-
|
|
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);
|
|
@@ -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
|
-
{
|
|
44
|
-
|
|
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 =
|
|
26
|
-
name:
|
|
25
|
+
const profileSchema = z.object({
|
|
26
|
+
name: z.string().min(1, "Name is required"),
|
|
27
27
|
});
|
|
28
28
|
|
|
29
|
-
const passwordSchema =
|
|
30
|
-
|
|
31
|
-
|
|
32
|
-
|
|
33
|
-
|
|
34
|
-
|
|
35
|
-
|
|
36
|
-
|
|
37
|
-
|
|
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 {
|
|
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 } =
|
|
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 {
|
|
5
|
+
import { useQueryClient } from "@tanstack/react-query";
|
|
6
6
|
import { useForm } from "@tanstack/react-form";
|
|
7
|
-
import {
|
|
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 {
|
|
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 =
|
|
22
|
-
name:
|
|
23
|
-
description:
|
|
24
|
-
distance:
|
|
25
|
-
|
|
26
|
-
})
|
|
27
|
-
|
|
28
|
-
|
|
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 } =
|
|
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 =
|
|
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 =
|
|
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 =
|
|
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 {
|
|
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 =
|
|
21
|
-
name:
|
|
22
|
-
description:
|
|
23
|
-
distance:
|
|
24
|
-
|
|
25
|
-
})
|
|
26
|
-
|
|
27
|
-
|
|
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
|
-
|
|
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
|
-
|
|
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
|
});
|