create-better-t-stack 3.12.5 → 3.12.7

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 (37) hide show
  1. package/dist/cli.mjs +1 -1
  2. package/dist/index.mjs +1 -1
  3. package/dist/{src-D3EHRs6z.mjs → src-DNjxspj9.mjs} +32 -21
  4. package/package.json +2 -2
  5. package/templates/api/orpc/web/react/base/src/utils/orpc.ts.hbs +3 -3
  6. package/templates/auth/better-auth/convex/native/uniwind/components/sign-in.tsx.hbs +32 -50
  7. package/templates/auth/better-auth/convex/native/uniwind/components/sign-up.tsx.hbs +40 -57
  8. package/templates/auth/better-auth/native/uniwind/components/sign-in.tsx.hbs +32 -35
  9. package/templates/auth/better-auth/native/uniwind/components/sign-up.tsx.hbs +49 -37
  10. package/templates/auth/better-auth/web/nuxt/app/components/SignInForm.vue.hbs +40 -35
  11. package/templates/auth/better-auth/web/nuxt/app/components/SignUpForm.vue.hbs +47 -40
  12. package/templates/auth/better-auth/web/nuxt/app/middleware/auth.ts.hbs +9 -7
  13. package/templates/auth/better-auth/web/nuxt/app/pages/dashboard.vue.hbs +61 -29
  14. package/templates/auth/better-auth/web/nuxt/app/pages/login.vue.hbs +6 -3
  15. package/templates/backend/server/elysia/src/index.ts.hbs +8 -3
  16. package/templates/backend/server/express/src/index.ts.hbs +8 -3
  17. package/templates/backend/server/fastify/src/index.ts.hbs +8 -3
  18. package/templates/backend/server/hono/src/index.ts.hbs +16 -6
  19. package/templates/examples/ai/fullstack/next/src/app/api/ai/route.ts.hbs +8 -3
  20. package/templates/examples/ai/fullstack/tanstack-start/src/routes/api/ai/$.ts.hbs +8 -3
  21. package/templates/examples/ai/native/bare/polyfills.js +14 -11
  22. package/templates/examples/ai/native/uniwind/app/(drawer)/ai.tsx.hbs +104 -124
  23. package/templates/examples/ai/web/nuxt/app/pages/ai.vue.hbs +22 -22
  24. package/templates/examples/todo/native/uniwind/app/(drawer)/todos.tsx.hbs +67 -80
  25. package/templates/examples/todo/web/nuxt/app/pages/todos.vue.hbs +115 -90
  26. package/templates/frontend/native/uniwind/app/(drawer)/index.tsx.hbs +60 -54
  27. package/templates/frontend/native/uniwind/app/+not-found.tsx.hbs +16 -21
  28. package/templates/frontend/native/uniwind/app/modal.tsx.hbs +18 -34
  29. package/templates/frontend/native/uniwind/package.json.hbs +10 -10
  30. package/templates/frontend/nuxt/app/components/Header.vue.hbs +25 -29
  31. package/templates/frontend/nuxt/app/layouts/default.vue.hbs +7 -8
  32. package/templates/frontend/nuxt/app/pages/index.vue.hbs +58 -39
  33. package/templates/frontend/nuxt/package.json.hbs +1 -1
  34. package/templates/frontend/react/tanstack-router/package.json.hbs +1 -1
  35. package/templates/frontend/react/web-base/src/components/ui/skeleton.tsx.hbs +5 -5
  36. package/templates/frontend/nuxt/app/components/Loader.vue.hbs +0 -5
  37. package/templates/frontend/nuxt/app/components/ModeToggle.vue.hbs +0 -25
@@ -1,13 +1,31 @@
1
1
  <script setup lang="ts">
2
- import z from 'zod'
3
- const {$authClient} = useNuxtApp()
4
- import type { FormSubmitEvent } from '#ui/types'
2
+ import * as z from 'zod'
3
+ import type { FormSubmitEvent, AuthFormField } from '@nuxt/ui'
4
+
5
+ const { $authClient } = useNuxtApp()
5
6
 
6
7
  const emit = defineEmits(['switchToSignUp'])
7
8
 
8
9
  const toast = useToast()
9
10
  const loading = ref(false)
10
11
 
12
+ const fields: AuthFormField[] = [
13
+ {
14
+ name: 'email',
15
+ type: 'email',
16
+ label: 'Email',
17
+ placeholder: 'Enter your email',
18
+ required: true
19
+ },
20
+ {
21
+ name: 'password',
22
+ type: 'password',
23
+ label: 'Password',
24
+ placeholder: 'Enter your password',
25
+ required: true
26
+ }
27
+ ]
28
+
11
29
  const schema = z.object({
12
30
  email: z.email('Invalid email address'),
13
31
  password: z.string().min(8, 'Password must be at least 8 characters'),
@@ -15,12 +33,7 @@ const schema = z.object({
15
33
 
16
34
  type Schema = z.output<typeof schema>
17
35
 
18
- const state = reactive({
19
- email: '',
20
- password: '',
21
- })
22
-
23
- async function onSubmit (event: FormSubmitEvent<Schema>) {
36
+ async function onSubmit(event: FormSubmitEvent<Schema>) {
24
37
  loading.value = true
25
38
  try {
26
39
  await $authClient.signIn.email(
@@ -39,7 +52,7 @@ async function onSubmit (event: FormSubmitEvent<Schema>) {
39
52
  },
40
53
  )
41
54
  } catch (error: any) {
42
- toast.add({ title: 'An unexpected error occurred', description: error.message || 'Please try again.' })
55
+ toast.add({ title: 'An unexpected error occurred', description: error.message || 'Please try again.' })
43
56
  } finally {
44
57
  loading.value = false
45
58
  }
@@ -47,31 +60,23 @@ async function onSubmit (event: FormSubmitEvent<Schema>) {
47
60
  </script>
48
61
 
49
62
  <template>
50
- <div class="mx-auto w-full mt-10 max-w-md p-6">
51
- <h1 class="mb-6 text-center text-3xl font-bold">Welcome Back</h1>
52
-
53
- <UForm :schema="schema" :state="state" class="space-y-4" @submit="onSubmit">
54
- <UFormField label="Email" name="email">
55
- <UInput v-model="state.email" type="email" class="w-full" />
56
- </UFormField>
57
-
58
- <UFormField label="Password" name="password">
59
- <UInput v-model="state.password" type="password" class="w-full" />
60
- </UFormField>
61
-
62
- <UButton type="submit" block :loading="loading">
63
- Sign In
64
- </UButton>
65
- </UForm>
66
-
67
- <div class="mt-4 text-center">
68
- <UButton
69
- variant="link"
70
- @click="$emit('switchToSignUp')"
71
- class="text-primary hover:text-primary-dark"
63
+ <div class="flex flex-col items-center justify-center gap-4 p-4">
64
+ <UPageCard class="w-full max-w-md">
65
+ <UAuthForm
66
+ :schema="schema"
67
+ :fields="fields"
68
+ title="Welcome Back"
69
+ icon="i-lucide-log-in"
70
+ :submit="{ label: 'Sign In', loading }"
71
+ @submit="onSubmit"
72
72
  >
73
- Need an account? Sign Up
74
- </UButton>
75
- </div>
73
+ <template #description>
74
+ Need an account?
75
+ <ULink class="text-primary font-medium" @click="$emit('switchToSignUp')">
76
+ Sign Up
77
+ </ULink>
78
+ </template>
79
+ </UAuthForm>
80
+ </UPageCard>
76
81
  </div>
77
82
  </template>
@@ -1,13 +1,38 @@
1
1
  <script setup lang="ts">
2
- import z from 'zod'
3
- import type { FormSubmitEvent } from '#ui/types'
4
- const {$authClient} = useNuxtApp()
2
+ import * as z from 'zod'
3
+ import type { FormSubmitEvent, AuthFormField } from '@nuxt/ui'
4
+
5
+ const { $authClient } = useNuxtApp()
5
6
 
6
7
  const emit = defineEmits(['switchToSignIn'])
7
8
 
8
9
  const toast = useToast()
9
10
  const loading = ref(false)
10
11
 
12
+ const fields: AuthFormField[] = [
13
+ {
14
+ name: 'name',
15
+ type: 'text',
16
+ label: 'Name',
17
+ placeholder: 'Enter your name',
18
+ required: true
19
+ },
20
+ {
21
+ name: 'email',
22
+ type: 'email',
23
+ label: 'Email',
24
+ placeholder: 'Enter your email',
25
+ required: true
26
+ },
27
+ {
28
+ name: 'password',
29
+ type: 'password',
30
+ label: 'Password',
31
+ placeholder: 'Enter your password',
32
+ required: true
33
+ }
34
+ ]
35
+
11
36
  const schema = z.object({
12
37
  name: z.string().min(2, 'Name must be at least 2 characters'),
13
38
  email: z.email('Invalid email address'),
@@ -16,13 +41,7 @@ const schema = z.object({
16
41
 
17
42
  type Schema = z.output<typeof schema>
18
43
 
19
- const state = reactive({
20
- name: '',
21
- email: '',
22
- password: '',
23
- })
24
-
25
- async function onSubmit (event: FormSubmitEvent<Schema>) {
44
+ async function onSubmit(event: FormSubmitEvent<Schema>) {
26
45
  loading.value = true
27
46
  try {
28
47
  await $authClient.signUp.email(
@@ -42,7 +61,7 @@ async function onSubmit (event: FormSubmitEvent<Schema>) {
42
61
  },
43
62
  )
44
63
  } catch (error: any) {
45
- toast.add({ title: 'An unexpected error occurred', description: error.message || 'Please try again.' })
64
+ toast.add({ title: 'An unexpected error occurred', description: error.message || 'Please try again.' })
46
65
  } finally {
47
66
  loading.value = false
48
67
  }
@@ -50,35 +69,23 @@ async function onSubmit (event: FormSubmitEvent<Schema>) {
50
69
  </script>
51
70
 
52
71
  <template>
53
- <div class="mx-auto w-full mt-10 max-w-md p-6">
54
- <h1 class="mb-6 text-center text-3xl font-bold">Create Account</h1>
55
-
56
- <UForm :schema="schema" :state="state" class="space-y-4" @submit="onSubmit">
57
- <UFormField label="Name" name="name">
58
- <UInput v-model="state.name" class="w-full" />
59
- </UFormField>
60
-
61
- <UFormField label="Email" name="email">
62
- <UInput v-model="state.email" type="email" class="w-full" />
63
- </UFormField>
64
-
65
- <UFormField label="Password" name="password">
66
- <UInput v-model="state.password" type="password" class="w-full" />
67
- </UFormField>
68
-
69
- <UButton type="submit" block :loading="loading">
70
- Sign Up
71
- </UButton>
72
- </UForm>
73
-
74
- <div class="mt-4 text-center">
75
- <UButton
76
- variant="link"
77
- @click="$emit('switchToSignIn')"
78
- class="text-primary hover:text-primary-dark"
72
+ <div class="flex flex-col items-center justify-center gap-4 p-4">
73
+ <UPageCard class="w-full max-w-md">
74
+ <UAuthForm
75
+ :schema="schema"
76
+ :fields="fields"
77
+ title="Create Account"
78
+ icon="i-lucide-user-plus"
79
+ :submit="{ label: 'Sign Up', loading }"
80
+ @submit="onSubmit"
79
81
  >
80
- Already have an account? Sign In
81
- </UButton>
82
- </div>
82
+ <template #description>
83
+ Already have an account?
84
+ <ULink class="text-primary font-medium" @click="$emit('switchToSignIn')">
85
+ Sign In
86
+ </ULink>
87
+ </template>
88
+ </UAuthForm>
89
+ </UPageCard>
83
90
  </div>
84
91
  </template>
@@ -1,12 +1,14 @@
1
1
  export default defineNuxtRouteMiddleware(async (to, from) => {
2
- if (import.meta.server) return
2
+ if (import.meta.server) return;
3
3
 
4
- const { $authClient } = useNuxtApp()
5
- const session = $authClient.useSession()
4
+ const { $authClient } = useNuxtApp();
5
+ const session = $authClient.useSession();
6
6
 
7
- if (session.value.isPending || !session.value) {
8
- if (to.path === "/dashboard") {
9
- return navigateTo("/login");
10
- }
7
+ if (session.value.isPending) {
8
+ return;
9
+ }
10
+
11
+ if (!session.value.data) {
12
+ return navigateTo("/login");
11
13
  }
12
14
  });
@@ -2,15 +2,15 @@
2
2
  {{#if (eq api "orpc")}}
3
3
  import { useQuery } from '@tanstack/vue-query'
4
4
  {{/if}}
5
- const {$authClient} = useNuxtApp()
5
+
6
+ const { $authClient, $orpc } = useNuxtApp()
6
7
 
7
8
  definePageMeta({
8
9
  middleware: ['auth']
9
10
  })
10
11
 
11
- const { $orpc } = useNuxtApp()
12
-
13
12
  const session = $authClient.useSession()
13
+
14
14
  {{#if (eq payments "polar")}}
15
15
  const customerState = ref<any>(null)
16
16
  {{/if}}
@@ -34,34 +34,66 @@ const hasProSubscription = computed(() =>
34
34
  customerState.value?.activeSubscriptions?.length! > 0
35
35
  )
36
36
  {{/if}}
37
-
38
37
  </script>
39
38
 
40
39
  <template>
41
- <div class="container mx-auto p-4">
42
- <h1 class="text-2xl font-bold mb-4">Dashboard</h1>
43
- <div v-if="session?.data?.user">
44
- <p class="mb-2">Welcome \{{ session.data.user.name }}</p>
40
+ <UContainer class="py-8">
41
+ <UPageHeader
42
+ title="Dashboard"
43
+ :description="session?.data?.user ? `Welcome back, ${session.data.user.name}!` : 'Loading...'"
44
+ />
45
+
46
+ <div class="mt-6 space-y-4">
47
+ {{#if (eq api "orpc")}}
48
+ <UCard>
49
+ <template #header>
50
+ <div class="font-medium">Private Data</div>
51
+ </template>
52
+
53
+ <USkeleton v-if="privateData.status.value === 'pending'" class="h-6 w-48" />
54
+
55
+ <UAlert
56
+ v-else-if="privateData.status.value === 'error'"
57
+ color="error"
58
+ icon="i-lucide-alert-circle"
59
+ title="Error loading data"
60
+ :description="privateData.error.value?.message || 'Failed to load private data'"
61
+ />
62
+
63
+ <div v-else-if="privateData.data.value" class="flex items-center gap-2">
64
+ <UIcon name="i-lucide-check-circle" class="text-success" />
65
+ <span>\{{ privateData.data.value.message }}</span>
66
+ </div>
67
+ </UCard>
68
+ {{/if}}
69
+
70
+ {{#if (eq payments "polar")}}
71
+ <UCard>
72
+ <template #header>
73
+ <div class="font-medium">Subscription</div>
74
+ </template>
75
+
76
+ <div class="flex items-center justify-between">
77
+ <div class="flex items-center gap-2">
78
+ <UIcon :name="hasProSubscription ? 'i-lucide-crown' : 'i-lucide-user'" :class="hasProSubscription ? 'text-warning' : 'text-muted'" />
79
+ <span>Plan: \{{ hasProSubscription ? "Pro" : "Free" }}</span>
80
+ </div>
81
+ <UButton
82
+ v-if="hasProSubscription"
83
+ variant="outline"
84
+ @click="() => { $authClient.customer.portal() }"
85
+ >
86
+ Manage Subscription
87
+ </UButton>
88
+ <UButton
89
+ v-else
90
+ @click="() => { $authClient.checkout({ slug: 'pro' }) }"
91
+ >
92
+ Upgrade to Pro
93
+ </UButton>
94
+ </div>
95
+ </UCard>
96
+ {{/if}}
45
97
  </div>
46
- {{#if (eq api "orpc")}}
47
- <div v-if="privateData.status.value === 'pending'">Loading private data...</div>
48
- <div v-else-if="privateData.status.value === 'error'">Error loading private data: \{{ privateData.error.value?.message }}</div>
49
- <p v-else-if="privateData.data.value">API: \{{ privateData.data.value.message }}</p>
50
- {{/if}}
51
- {{#if (eq payments "polar")}}
52
- <p class="mb-2">Plan: \{{ hasProSubscription ? "Pro" : "Free" }}</p>
53
- <UButton
54
- v-if="hasProSubscription"
55
- @click="() => { $authClient.customer.portal() }"
56
- >
57
- Manage Subscription
58
- </UButton>
59
- <UButton
60
- v-else
61
- @click="() => { $authClient.checkout({ slug: 'pro' }) }"
62
- >
63
- Upgrade to Pro
64
- </UButton>
65
- {{/if}}
66
- </div>
98
+ </UContainer>
67
99
  </template>
@@ -14,11 +14,14 @@ watchEffect(() => {
14
14
  </script>
15
15
 
16
16
  <template>
17
- <div>
18
- <Loader v-if="session.isPending" />
17
+ <UContainer class="py-8">
18
+ <div v-if="session.isPending" class="flex flex-col items-center justify-center gap-4 py-12">
19
+ <UIcon name="i-lucide-loader-2" class="animate-spin text-4xl text-primary" />
20
+ <span class="text-muted">Loading...</span>
21
+ </div>
19
22
  <div v-else-if="!session.data">
20
23
  <SignInForm v-if="showSignIn" @switch-to-sign-up="showSignIn = false" />
21
24
  <SignUpForm v-else @switch-to-sign-in="showSignIn = true" />
22
25
  </div>
23
- </div>
26
+ </UContainer>
24
27
  </template>
@@ -6,7 +6,8 @@ import { Elysia } from "elysia";
6
6
  import { cors } from "@elysiajs/cors";
7
7
  {{#if (includes examples "ai")}}
8
8
  import { google } from "@ai-sdk/google";
9
- import { convertToModelMessages, streamText } from "ai";
9
+ import { convertToModelMessages, streamText, wrapLanguageModel } from "ai";
10
+ import { devToolsMiddleware } from "@ai-sdk/devtools";
10
11
  {{/if}}
11
12
  {{#if (eq api "trpc")}}
12
13
  import { createContext } from "@{{projectName}}/api/context";
@@ -103,9 +104,13 @@ const app = new Elysia()
103
104
  .post("/ai", async (context) => {
104
105
  const body = await context.request.json();
105
106
  const uiMessages = body.messages || [];
106
- const result = streamText({
107
+ const model = wrapLanguageModel({
107
108
  model: google("gemini-2.5-flash"),
108
- messages: convertToModelMessages(uiMessages)
109
+ middleware: devToolsMiddleware(),
110
+ });
111
+ const result = streamText({
112
+ model,
113
+ messages: await convertToModelMessages(uiMessages)
109
114
  });
110
115
 
111
116
  return result.toUIMessageStreamResponse();
@@ -18,8 +18,9 @@ import { createContext } from "@{{projectName}}/api/context";
18
18
  import cors from "cors";
19
19
  import express from "express";
20
20
  {{#if (includes examples "ai")}}
21
- import { streamText, type UIMessage, convertToModelMessages } from "ai";
21
+ import { streamText, type UIMessage, convertToModelMessages, wrapLanguageModel } from "ai";
22
22
  import { google } from "@ai-sdk/google";
23
+ import { devToolsMiddleware } from "@ai-sdk/devtools";
23
24
  {{/if}}
24
25
  {{#if (eq auth "better-auth")}}
25
26
  import { auth } from "@{{projectName}}/auth";
@@ -104,9 +105,13 @@ app.use(express.json());
104
105
  {{#if (includes examples "ai")}}
105
106
  app.post("/ai", async (req, res) => {
106
107
  const { messages = [] } = (req.body || {}) as { messages: UIMessage[] };
107
- const result = streamText({
108
+ const model = wrapLanguageModel({
108
109
  model: google("gemini-2.5-flash"),
109
- messages: convertToModelMessages(messages),
110
+ middleware: devToolsMiddleware(),
111
+ });
112
+ const result = streamText({
113
+ model,
114
+ messages: await convertToModelMessages(messages),
110
115
  });
111
116
  result.pipeUIMessageStreamToResponse(res);
112
117
  });
@@ -23,8 +23,9 @@ import { createContext } from "@{{projectName}}/api/context";
23
23
  {{/if}}
24
24
 
25
25
  {{#if (includes examples "ai")}}
26
- import { streamText, type UIMessage, convertToModelMessages } from "ai";
26
+ import { streamText, type UIMessage, convertToModelMessages, wrapLanguageModel } from "ai";
27
27
  import { google } from "@ai-sdk/google";
28
+ import { devToolsMiddleware } from "@ai-sdk/devtools";
28
29
  {{/if}}
29
30
 
30
31
  {{#if (eq auth "better-auth")}}
@@ -160,9 +161,13 @@ interface AiRequestBody {
160
161
 
161
162
  fastify.post('/ai', async function (request) {
162
163
  const { messages } = request.body as AiRequestBody;
163
- const result = streamText({
164
+ const model = wrapLanguageModel({
164
165
  model: google('gemini-2.5-flash'),
165
- messages: convertToModelMessages(messages),
166
+ middleware: devToolsMiddleware(),
167
+ });
168
+ const result = streamText({
169
+ model,
170
+ messages: await convertToModelMessages(messages),
166
171
  });
167
172
 
168
173
  return result.toUIMessageStreamResponse();
@@ -20,12 +20,14 @@ import { Hono } from "hono";
20
20
  import { cors } from "hono/cors";
21
21
  import { logger } from "hono/logger";
22
22
  {{#if (and (includes examples "ai") (or (eq runtime "bun") (eq runtime "node")))}}
23
- import { streamText, convertToModelMessages } from "ai";
23
+ import { streamText, convertToModelMessages, wrapLanguageModel } from "ai";
24
24
  import { google } from "@ai-sdk/google";
25
+ import { devToolsMiddleware } from "@ai-sdk/devtools";
25
26
  {{/if}}
26
27
  {{#if (and (includes examples "ai") (eq runtime "workers"))}}
27
- import { streamText, convertToModelMessages } from "ai";
28
+ import { streamText, convertToModelMessages, wrapLanguageModel } from "ai";
28
29
  import { createGoogleGenerativeAI } from "@ai-sdk/google";
30
+ import { devToolsMiddleware } from "@ai-sdk/devtools";
29
31
  {{/if}}
30
32
 
31
33
  const app = new Hono();
@@ -110,9 +112,13 @@ app.use(
110
112
  app.post("/ai", async (c) => {
111
113
  const body = await c.req.json();
112
114
  const uiMessages = body.messages || [];
113
- const result = streamText({
115
+ const model = wrapLanguageModel({
114
116
  model: google("gemini-2.5-flash"),
115
- messages: convertToModelMessages(uiMessages),
117
+ middleware: devToolsMiddleware(),
118
+ });
119
+ const result = streamText({
120
+ model,
121
+ messages: await convertToModelMessages(uiMessages),
116
122
  });
117
123
 
118
124
  return result.toUIMessageStreamResponse();
@@ -126,9 +132,13 @@ app.post("/ai", async (c) => {
126
132
  const google = createGoogleGenerativeAI({
127
133
  apiKey: env.GOOGLE_GENERATIVE_AI_API_KEY,
128
134
  });
129
- const result = streamText({
135
+ const model = wrapLanguageModel({
130
136
  model: google("gemini-2.5-flash"),
131
- messages: convertToModelMessages(uiMessages),
137
+ middleware: devToolsMiddleware(),
138
+ });
139
+ const result = streamText({
140
+ model,
141
+ messages: await convertToModelMessages(uiMessages),
132
142
  });
133
143
 
134
144
  return result.toUIMessageStreamResponse();
@@ -1,14 +1,19 @@
1
1
  import { google } from "@ai-sdk/google";
2
- import { streamText, type UIMessage, convertToModelMessages } from "ai";
2
+ import { streamText, type UIMessage, convertToModelMessages, wrapLanguageModel } from "ai";
3
+ import { devToolsMiddleware } from "@ai-sdk/devtools";
3
4
 
4
5
  export const maxDuration = 30;
5
6
 
6
7
  export async function POST(req: Request) {
7
8
  const { messages }: { messages: UIMessage[] } = await req.json();
8
9
 
9
- const result = streamText({
10
+ const model = wrapLanguageModel({
10
11
  model: google("gemini-2.5-flash"),
11
- messages: convertToModelMessages(messages),
12
+ middleware: devToolsMiddleware(),
13
+ });
14
+ const result = streamText({
15
+ model,
16
+ messages: await convertToModelMessages(messages),
12
17
  });
13
18
 
14
19
  return result.toUIMessageStreamResponse();
@@ -1,6 +1,7 @@
1
1
  import { createFileRoute } from "@tanstack/react-router";
2
2
  import { google } from "@ai-sdk/google";
3
- import { streamText, type UIMessage, convertToModelMessages } from "ai";
3
+ import { streamText, type UIMessage, convertToModelMessages, wrapLanguageModel } from "ai";
4
+ import { devToolsMiddleware } from "@ai-sdk/devtools";
4
5
 
5
6
  export const Route = createFileRoute("/api/ai/$")({
6
7
  server: {
@@ -9,9 +10,13 @@ export const Route = createFileRoute("/api/ai/$")({
9
10
  try {
10
11
  const { messages }: { messages: UIMessage[] } = await request.json();
11
12
 
12
- const result = streamText({
13
+ const model = wrapLanguageModel({
13
14
  model: google("gemini-2.5-flash"),
14
- messages: convertToModelMessages(messages),
15
+ middleware: devToolsMiddleware(),
16
+ });
17
+ const result = streamText({
18
+ model,
19
+ messages: await convertToModelMessages(messages),
15
20
  });
16
21
 
17
22
  return result.toUIMessageStreamResponse();
@@ -1,22 +1,25 @@
1
- import structuredClone from "@ungap/structured-clone";
2
- import { Platform } from "react-native";
1
+ import { Platform } from 'react-native';
2
+ import structuredClone from '@ungap/structured-clone';
3
3
 
4
- if (Platform.OS !== "web") {
4
+ if (Platform.OS !== 'web') {
5
5
  const setupPolyfills = async () => {
6
- const { polyfillGlobal } = await import("react-native/Libraries/Utilities/PolyfillFunctions");
6
+ const { polyfillGlobal } = await import(
7
+ 'react-native/Libraries/Utilities/PolyfillFunctions'
8
+ );
7
9
 
8
- const { TextEncoderStream, TextDecoderStream } =
9
- await import("@stardazed/streams-text-encoding");
10
+ const { TextEncoderStream, TextDecoderStream } = await import(
11
+ '@stardazed/streams-text-encoding'
12
+ );
10
13
 
11
- if (!("structuredClone" in global)) {
12
- polyfillGlobal("structuredClone", () => structuredClone);
14
+ if (!('structuredClone' in global)) {
15
+ polyfillGlobal('structuredClone', () => structuredClone);
13
16
  }
14
17
 
15
- polyfillGlobal("TextEncoderStream", () => TextEncoderStream);
16
- polyfillGlobal("TextDecoderStream", () => TextDecoderStream);
18
+ polyfillGlobal('TextEncoderStream', () => TextEncoderStream);
19
+ polyfillGlobal('TextDecoderStream', () => TextDecoderStream);
17
20
  };
18
21
 
19
22
  setupPolyfills();
20
23
  }
21
24
 
22
- export {};
25
+ export {};